diff --git a/overcooked_simulator/counter_factory.py b/overcooked_simulator/counter_factory.py
index 02abcf47a6e4f1ce408bd673528934a96f127585..757cddfe27582ba50880a26f72fb8a624561e40c 100644
--- a/overcooked_simulator/counter_factory.py
+++ b/overcooked_simulator/counter_factory.py
@@ -50,7 +50,14 @@ from overcooked_simulator.counters import (
     SinkAddon,
     Trashcan,
 )
-from overcooked_simulator.game_items import ItemInfo, ItemType, CookingEquipment, Plate
+from overcooked_simulator.effect_manager import EffectManager
+from overcooked_simulator.game_items import (
+    ItemInfo,
+    ItemType,
+    CookingEquipment,
+    Plate,
+    Item,
+)
 from overcooked_simulator.hooks import Hooks
 from overcooked_simulator.order import OrderAndScoreManager
 from overcooked_simulator.utils import get_closest
@@ -112,6 +119,7 @@ class CounterFactory:
         serving_window_additional_kwargs: dict[str, Any],
         plate_config: PlateConfig,
         order_and_score: OrderAndScoreManager,
+        effect_manager_config: dict,
         hook: Hooks,
     ) -> None:
         """Constructor for the `CounterFactory` class. Set up the attributes necessary to instantiate the counters.
@@ -142,6 +150,8 @@ class CounterFactory:
         """The plate config from the `environment_config`"""
         self.order_and_score: OrderAndScoreManager = order_and_score
         """The order and score manager to pass to `ServingWindow` and the `Tashcan` which can affect the scores."""
+        self.effect_manager_config = effect_manager_config
+        """The effect manager config to setup the effect manager based on the defined effects in the item info."""
 
         self.no_counter_chars: set[str] = set(
             c
@@ -190,13 +200,20 @@ class CounterFactory:
                             name=item_info.name,
                             item_info=item_info,
                             transitions=self.filter_item_info(
-                                by_equipment_name=item_info.name
+                                by_equipment_name=item_info.name,
+                                add_effects=True,
                             ),
                         ),
                         hook=self.hook,
                     )
             elif item_info.type == ItemType.Ingredient:
                 return Dispenser(pos=pos, hook=self.hook, dispensing=item_info)
+            elif item_info.type == ItemType.Tool:
+                return Counter(
+                    pos=pos,
+                    hook=self.hook,
+                    occupied_by=Item(name=item_info.name, item_info=item_info),
+                )
 
         if counter_class is None:
             counter_class = self.counter_classes[self.layout_chars_config[c]]
@@ -206,13 +223,14 @@ class CounterFactory:
         }
         if issubclass(counter_class, (CuttingBoard, Sink)):
             kwargs["transitions"] = self.filter_item_info(
-                by_equipment_name=counter_class.__name__
+                by_equipment_name=counter_class.__name__,
+                add_effects=True,
             )
         elif issubclass(counter_class, PlateDispenser):
             kwargs.update(
                 {
                     "plate_transitions": self.filter_item_info(
-                        by_item_type=ItemType.Meal
+                        by_item_type=ItemType.Meal, add_effects=True
                     ),
                     "plate_config": self.plate_config,
                     "dispensing": self.item_info[Plate.__name__],
@@ -249,21 +267,32 @@ class CounterFactory:
         self,
         by_item_type: ItemType = None,
         by_equipment_name: str = None,
+        add_effects: bool = False,
     ) -> dict[str, ItemInfo]:
         """Filter the item info dict by item type or equipment name"""
+        filtered = {}
         if by_item_type is not None:
-            return {
+            filtered = {
                 name: info
                 for name, info in self.item_info.items()
                 if info.type == by_item_type
             }
         if by_equipment_name is not None:
-            return {
+            filtered = {
                 name: info
                 for name, info in self.item_info.items()
                 if info.equipment is not None
                 and info.equipment.name == by_equipment_name
             }
+        if add_effects:
+            for name, effect in self.filter_item_info(
+                by_item_type=ItemType.Effect
+            ).items():
+                for need in effect.needs:
+                    if need in filtered:
+                        filtered.update({name: effect})
+        if by_item_type or by_equipment_name:
+            return filtered
         return self.item_info
 
     def post_counter_setup(self, counters: list[Counter]):
@@ -289,11 +318,33 @@ class CounterFactory:
                     counter: Sink  # Pycharm type checker does now work for match statements?
                     assert len(sink_addons) > 0, "No SinkAddon but normal Sink"
                     closest_addon = get_closest(pos, sink_addons)
-                    assert 1 - (1 * 0.05) <= np.linalg.norm(
+                    assert 1.0 == np.linalg.norm(
                         closest_addon.pos - pos
                     ), f"No SinkAddon connected to Sink at pos {pos}"
                     counter.set_addon(closest_addon)
 
+    def setup_effect_manger(self, counters: list[Counter]) -> dict[str, EffectManager]:
+        effect_manager = {}
+        for name, effect in self.filter_item_info(by_item_type=ItemType.Effect).items():
+            assert (
+                effect.manager in self.effect_manager_config
+            ), f"Manager for effect not found: {name} -> {effect.manager} not in {list(self.effect_manager_config.keys())}"
+            if effect.manager in effect_manager:
+                manager = effect_manager[effect.manager]
+            else:
+                manager = self.effect_manager_config[effect.manager]["class"](
+                    hook=self.hook,
+                    **self.effect_manager_config[effect.manager]["kwargs"],
+                )
+                manager.set_counters(counters)
+                effect_manager[effect.manager] = manager
+
+            manager.add_effect(effect)
+
+            effect.manager = manager
+
+        return effect_manager
+
     @staticmethod
     def get_counter_of_type(counter_type: Type[T], counters: list[Counter]) -> list[T]:
         """Filter all counters in the environment for a counter type."""
diff --git a/overcooked_simulator/counters.py b/overcooked_simulator/counters.py
index d4b72eec6029dd96ca6c8d5604deb64b12b0c970..697a3394385a2019c8b49233df334f20fdc3300b 100644
--- a/overcooked_simulator/counters.py
+++ b/overcooked_simulator/counters.py
@@ -48,8 +48,6 @@ from overcooked_simulator.hooks import (
     PRE_DISPENSER_PICK_UP,
     CUTTING_BOARD_PROGRESS,
     CUTTING_BOARD_100,
-    CUTTING_BOARD_START_INTERACT,
-    CUTTING_BOARD_END_INTERACT,
     PRE_COUNTER_PICK_UP,
     POST_COUNTER_PICK_UP,
     PRE_SERVING,
@@ -58,14 +56,13 @@ from overcooked_simulator.hooks import (
     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,
 )
 
 if TYPE_CHECKING:
+    from overcooked_simulator.effect_manager import Effect
     from overcooked_simulator.overcooked_environment import (
         OrderAndScoreManager,
     )
@@ -78,6 +75,7 @@ from overcooked_simulator.game_items import (
     CookingEquipment,
     Plate,
     ItemInfo,
+    EffectType,
 )
 
 
@@ -114,6 +112,8 @@ class Counter:
         """The position of the counter."""
         self.occupied_by: Optional[Item] = occupied_by
         """What is on top of the counter, e.g., `Item`s."""
+        self.active_effects: list[Effect] = []
+        """The effects that currently affect the usage of the counter."""
         self.hook = hook
         """Reference to the hook manager."""
 
@@ -190,19 +190,45 @@ class Counter:
             return self.occupied_by.combine(item)
         return None
 
-    def interact_start(self):
-        """Starts an interaction by the player. Nothing happens for the standard counter."""
-        pass
-
-    def interact_stop(self):
-        """Stops an interaction by the player. Nothing happens for the standard counter."""
-        pass
-
     def __repr__(self):
         return (
             f"{self.__class__.__name__}(pos={self.pos},occupied_by={self.occupied_by})"
         )
 
+    def do_tool_interaction(self, passed_time: timedelta, tool: Item):
+        successful = False
+        if self.occupied_by:
+            if isinstance(self.occupied_by, deque):
+                for item in self.occupied_by:
+                    successful |= self._do_single_tool_interaction(
+                        passed_time, tool, item
+                    )
+            else:
+                successful = self._do_single_tool_interaction(
+                    passed_time, tool, self.occupied_by
+                )
+        if not successful:
+            self._do_single_tool_interaction(passed_time, tool, self)
+
+    def _do_single_tool_interaction(
+        self, passed_time: timedelta, tool: Item, target: Item | Counter
+    ) -> bool:
+        suitable_effects = [
+            e for e in target.active_effects if e.name in tool.item_info.needs
+        ]
+        if suitable_effects:
+            effect = suitable_effects[0]
+            percent = passed_time.total_seconds() / tool.item_info.seconds
+            effect.progres_percentage += percent
+            if effect.progres_percentage > 1.0:
+                effect.item_info.manager.remove_active_effect(effect, target)
+                target.active_effects.remove(effect)
+            return True
+        return False
+
+    def do_hand_free_interaction(self, passed_time: timedelta, now: datetime):
+        ...
+
     def to_dict(self) -> dict:
         """For the state representation. Only the relevant attributes are put into the dict."""
         return {
@@ -217,6 +243,7 @@ class Counter:
                 if isinstance(self.occupied_by, Iterable)
                 else self.occupied_by.to_dict()
             ),
+            "active_effects": [e.to_dict() for e in self.active_effects],
         }
 
 
@@ -233,8 +260,6 @@ class CuttingBoard(Counter):
     """
 
     def __init__(self, transitions: dict[str, ItemInfo], **kwargs):
-        self.progressing: bool = False
-        """Is a player progressing/cutting on the board."""
         self.transitions: dict[str, ItemInfo] = transitions
         """The allowed transitions to a new item. Keys are the resulting items and the `ItemInfo` (value) contains 
         the needed items in the `need` attribute."""
@@ -245,7 +270,7 @@ class CuttingBoard(Counter):
         board."""
         super().__init__(**kwargs)
 
-    def progress(self, passed_time: timedelta, now: datetime):
+    def do_hand_free_interaction(self, passed_time: timedelta, now: datetime):
         """Called by environment step function for time progression.
 
         Args:
@@ -258,8 +283,15 @@ class CuttingBoard(Counter):
         """
         if (
             self.occupied
-            and self.progressing
             and self.occupied_by.name in self.inverted_transition_dict
+            and not any(
+                e.item_info.effect_type == EffectType.Unusable
+                for e in self.occupied_by.active_effects
+            )
+            and not any(
+                e.item_info.effect_type == EffectType.Unusable
+                for e in self.active_effects
+            )
         ):
             percent = (
                 passed_time.total_seconds()
@@ -281,29 +313,6 @@ class CuttingBoard(Counter):
                 ].name
                 self.hook(CUTTING_BOARD_100, counter=self)
 
-    def start_progress(self):
-        """Starts the cutting process."""
-        self.progressing = True
-
-    def pause_progress(self):
-        """Pauses the cutting process"""
-        self.progressing = False
-
-    def interact_start(self):
-        """Handles player interaction, starting to hold key down."""
-        self.start_progress()
-        self.hook(CUTTING_BOARD_START_INTERACT, counter=self)
-
-    def interact_stop(self):
-        """Handles player interaction, stopping to hold key down."""
-        self.pause_progress()
-        self.hook(CUTTING_BOARD_END_INTERACT, counter=self)
-
-    def to_dict(self) -> dict:
-        d = super().to_dict()
-        d.update((("progressing", self.progressing),))
-        return d
-
 
 class ServingWindow(Counter):
     """The orders and scores are updated based on completed and dropped off meals. The plate dispenser is pinged for
@@ -349,6 +358,14 @@ class ServingWindow(Counter):
         return item
 
     def can_drop_off(self, item: Item) -> bool:
+        if any(
+            e.item_info.effect_type == EffectType.Unusable for e in self.active_effects
+        ):
+            return False
+        if any(
+            e.item_info.effect_type == EffectType.Unusable for e in item.active_effects
+        ):
+            return False
         return isinstance(item, CookingEquipment) and (
             (item.content_ready is not None and item.content_ready.name in self.meals)
             or (len(item.content_list) == 1 and item.content_list[0].name in self.meals)
@@ -573,6 +590,12 @@ class Trashcan(Counter):
         pass
 
     def drop_off(self, item: Item) -> Item | None:
+        if any(
+            e.item_info.effect_type == EffectType.Unusable for e in item.active_effects
+        ) or any(
+            e.item_info.effect_type == EffectType.Unusable for e in self.active_effects
+        ):
+            return item
         if isinstance(item, CookingEquipment):
             penalty = self.order_and_score.apply_penalty_for_using_trash(
                 item.content_list
@@ -625,6 +648,10 @@ class CookingCounter(Counter):
             and isinstance(self.occupied_by, CookingEquipment)
             and self.occupied_by.name in self.equipments
             and self.occupied_by.can_progress()
+            and not any(
+                e.item_info.effect_type == EffectType.Unusable
+                for e in self.active_effects
+            )
         ):
             self.occupied_by.progress(passed_time, now)
 
@@ -657,8 +684,6 @@ class Sink(Counter):
         **kwargs,
     ):
         super().__init__(**kwargs)
-        self.progressing: bool = False
-        """If a player currently cleans a plate."""
         self.sink_addon: SinkAddon = sink_addon
         """The connected sink addon which will receive the clean plates"""
         self.occupied_by: deque[Plate] = deque()
@@ -679,12 +704,30 @@ class Sink(Counter):
         """If there is a plate in the sink."""
         return len(self.occupied_by) != 0
 
-    def progress(self, passed_time: timedelta, now: datetime):
+    def do_hand_free_interaction(self, passed_time: timedelta, now: datetime):
         """Called by environment step function for time progression"""
         if (
             self.occupied
-            and self.progressing
             and self.occupied_by[-1].name in self.transition_needs
+            and not any(
+                e.item_info.effect_type == EffectType.Unusable
+                for e in self.active_effects
+            )
+            and not any(
+                e.item_info.effect_type == EffectType.Unusable
+                for e in self.sink_addon.active_effects
+            )
+            and not any(
+                e.item_info.effect_type == EffectType.Unusable
+                for e in self.occupied_by[-1].active_effects
+            )
+            and (
+                not self.sink_addon.occupied_by
+                or not any(
+                    e.item_info.effect_type == EffectType.Unusable
+                    for e in self.sink_addon.occupied_by[-1].active_effects
+                )
+            )
         ):
             for name, info in self.transitions.items():
                 if info.needs[0] == self.occupied_by[-1].name:
@@ -701,24 +744,6 @@ class Sink(Counter):
                         self.sink_addon.add_clean_plate(plate)
                     break
 
-    def start_progress(self):
-        """Starts the cutting process."""
-        self.progressing = True
-
-    def pause_progress(self):
-        """Pauses the cutting process"""
-        self.progressing = False
-
-    def interact_start(self):
-        """Handles player interaction, starting to hold key down."""
-        self.start_progress()
-        self.hook(SINK_START_INTERACT, counter=self)
-
-    def interact_stop(self):
-        """Handles player interaction, stopping to hold key down."""
-        self.pause_progress()
-        self.hook(SINK_END_INTERACT, counter=self)
-
     def can_drop_off(self, item: Item) -> bool:
         return isinstance(item, Plate) and not item.clean
 
@@ -734,11 +759,6 @@ class Sink(Counter):
         """Set the closest addon in post_setup."""
         self.sink_addon = sink_addon
 
-    def to_dict(self) -> dict:
-        d = super().to_dict()
-        d.update((("progressing", self.progressing),))
-        return d
-
 
 class SinkAddon(Counter):
     """The counter on which the clean plates appear after cleaning them in the `Sink`
diff --git a/overcooked_simulator/effect_manager.py b/overcooked_simulator/effect_manager.py
new file mode 100644
index 0000000000000000000000000000000000000000..905fef957bf5f200b84ab84c141681838d0a9241
--- /dev/null
+++ b/overcooked_simulator/effect_manager.py
@@ -0,0 +1,133 @@
+from __future__ import annotations
+
+import random
+from collections import deque
+from datetime import timedelta, datetime
+from typing import TYPE_CHECKING, Tuple
+
+from overcooked_simulator.game_items import (
+    ItemInfo,
+    Item,
+    ItemType,
+    Effect,
+    CookingEquipment,
+)
+from overcooked_simulator.hooks import Hooks
+from overcooked_simulator.utils import get_touching_counters, find_item_on_counters
+
+if TYPE_CHECKING:
+    from overcooked_simulator.counters import Counter
+
+
+class EffectManager:
+    def __init__(self, hook: Hooks):
+        self.effects = []
+        self.counters = []
+        self.hook = hook
+        self.new_effects: list[Tuple[Effect, Item | Counter]] = []
+
+    def add_effect(self, effect: ItemInfo):
+        self.effects.append(effect)
+
+    def set_counters(self, counters: list[Counter]):
+        self.counters.extend(counters)
+
+    def register_active_effect(self, effect: Effect, target: Item | Counter):
+        target.active_effects.append(effect)
+        self.new_effects.append((effect, target))
+
+    def progress(self, passed_time: timedelta, now: datetime):
+        ...
+
+    def can_start_effect_transition(
+        self, effect: ItemInfo, target: Item | Counter
+    ) -> bool:
+        return effect.name not in [e.name for e in target.active_effects]
+
+    def remove_active_effect(self, effect: Effect, target: Item | Counter):
+        ...
+
+
+class FireEffectManager(EffectManager):
+    # TODO add Random object
+
+    def __init__(
+        self,
+        spreading_duration: list[float],
+        fire_burns_ingredients_and_meals: bool,
+        **kwargs,
+    ):
+        super().__init__(**kwargs)
+        self.spreading_duration = spreading_duration
+        self.fire_burns_ingredients_and_meals = fire_burns_ingredients_and_meals
+        self.effect_to_timer: dict[str:datetime] = {}
+        self.next_finished_timer = datetime.max
+        self.active_effects: list[Tuple[Effect, Item | Counter]] = []
+
+    def progress(self, passed_time: timedelta, now: datetime):
+        if self.new_effects:
+            for effect, target in self.new_effects:
+                self.effect_to_timer[effect.uuid] = now + timedelta(
+                    seconds=random.uniform(*self.spreading_duration)
+                )
+                self.next_finished_timer = min(
+                    self.next_finished_timer, self.effect_to_timer[effect.uuid]
+                )
+                self.active_effects.append((effect, target))
+            self.new_effects = []
+        if self.next_finished_timer < now:
+            for effect, target in self.active_effects:
+                if self.effect_to_timer[effect.uuid] < now:
+                    if isinstance(target, Item):
+                        target = find_item_on_counters(target.uuid, self.counters)
+                    if target:
+                        touching = get_touching_counters(target, self.counters)
+                        for counter in touching:
+                            if counter.occupied_by:
+                                if isinstance(counter.occupied_by, deque):
+                                    self.apply_effect(effect, counter.occupied_by[-1])
+                                else:
+                                    self.apply_effect(effect, counter.occupied_by)
+                            else:
+                                self.apply_effect(effect, counter)
+                    self.effect_to_timer[effect.uuid] = now + timedelta(
+                        seconds=random.uniform(*self.spreading_duration)
+                    )
+            if self.effect_to_timer:
+                self.next_finished_timer = min(self.effect_to_timer.values())
+            else:
+                self.next_finished_timer = datetime.max
+
+    def apply_effect(self, effect: Effect, target: Item | Counter):
+        if (
+            isinstance(target, Item)
+            and target.item_info.type == ItemType.Tool
+            and effect.name in target.item_info.needs
+        ):
+            # Tools that reduce fire can not burn
+            return
+        if effect.name not in target.active_effects and target.uuid not in [
+            t.uuid for _, t in self.active_effects
+        ]:
+            if isinstance(target, CookingEquipment):
+                if target.content_list:
+                    for content in target.content_list:
+                        self.burn_content(content)
+                    if self.fire_burns_ingredients_and_meals:
+                        self.burn_content(target.content_ready)
+            elif isinstance(target, Item):
+                self.burn_content(target)
+            self.register_active_effect(
+                Effect(effect.name, item_info=effect.item_info), target
+            )
+
+    def burn_content(self, content: Item):
+        if self.fire_burns_ingredients_and_meals and content:
+            if not content.name.startswith("Burnt"):
+                content.name = "Burnt" + content.name
+
+    def remove_active_effect(self, effect: Effect, target: Item | Counter):
+        if (effect, target) in self.active_effects:
+            self.active_effects.remove((effect, target))
+        if effect.uuid in self.effect_to_timer:
+            del self.effect_to_timer[effect.uuid]
diff --git a/overcooked_simulator/game_content/environment_config.yaml b/overcooked_simulator/game_content/environment_config.yaml
index c0de86d2afc35fad41a5c206b448a0f1eecdad6e..3cf44456a89edc420129101ff912b5ba4203c7d4 100644
--- a/overcooked_simulator/game_content/environment_config.yaml
+++ b/overcooked_simulator/game_content/environment_config.yaml
@@ -20,6 +20,7 @@ layout_chars:
   _: Free
   hash: Counter
   A: Agent
+  pipe: Extinguisher
   P: PlateDispenser
   C: CuttingBoard
   X: Trashcan
@@ -41,6 +42,7 @@ layout_chars:
   B: Bun
   M: Meat
 
+
 orders:
   order_gen_class: !!python/name:overcooked_simulator.order.RandomOrderGeneration ''
   # the class to that receives the kwargs. Should be a child class of OrderGeneration in order.py
@@ -85,3 +87,11 @@ player_config:
   radius: 0.4
   player_speed_units_per_seconds: 8
   interaction_range: 1.6
+
+
+effect_manager:
+  FireManager:
+    class: !!python/name:overcooked_simulator.effect_manager.FireEffectManager ''
+    kwargs:
+      spreading_duration: [ 5, 10 ]
+      fire_burns_ingredients_and_meals: true
\ No newline at end of file
diff --git a/overcooked_simulator/game_content/item_info.yaml b/overcooked_simulator/game_content/item_info.yaml
index 639f855132b57b7ccef78b6b865b9942b00f5820..1266f61ebd611cd5c2a9097b1be9dd7eff65b7f7 100644
--- a/overcooked_simulator/game_content/item_info.yaml
+++ b/overcooked_simulator/game_content/item_info.yaml
@@ -213,4 +213,20 @@ BurntPizza:
   type: Waste
   needs: [ Pizza ]
   seconds: 7.0
-  equipment: Peel
\ No newline at end of file
+  equipment: Peel
+
+# --------------------------------------------------------------------------------
+
+Fire:
+  type: Effect
+  seconds: 5.0
+  needs: [ BurntCookedPatty, BurntChips, BurntFriedFish, BurntTomatoSoup, BurntOnionSoup, BurntPizza ]
+  manager: FireManager
+  effect_type: Unusable
+
+# --------------------------------------------------------------------------------
+
+Extinguisher:
+  type: Tool
+  seconds: 1.0
+  needs: [ Fire ]
diff --git a/overcooked_simulator/game_content/item_info_debug.yaml b/overcooked_simulator/game_content/item_info_debug.yaml
index 6340f564820daec1316060bc2d549b8aeca389c4..15d3b606480a232ffce7de3ef1ba61c930686cf4 100644
--- a/overcooked_simulator/game_content/item_info_debug.yaml
+++ b/overcooked_simulator/game_content/item_info_debug.yaml
@@ -216,3 +216,18 @@ BurntPizza:
   seconds: 7.0
   equipment: Peel
 
+# --------------------------------------------------------------------------------
+
+Fire:
+  type: Effect
+  seconds: 10.0
+  needs: [ BurntCookedPatty, BurntChips, BurntFriedFish, BurntTomatoSoup, BurntOnionSoup, BurntPizza ]
+  manager: FireManager
+  effect_type: Unusable
+
+# --------------------------------------------------------------------------------
+
+Extinguisher:
+  type: Tool
+  seconds: 0.1
+  needs: [ Fire ]
\ No newline at end of file
diff --git a/overcooked_simulator/game_content/layouts/basic.layout b/overcooked_simulator/game_content/layouts/basic.layout
index ccc4076303e985a8b60c9f2dd091f323b5d6e7a6..94e5fb1dc055cefacfa6ef562ad1fe04424e726b 100644
--- a/overcooked_simulator/game_content/layouts/basic.layout
+++ b/overcooked_simulator/game_content/layouts/basic.layout
@@ -1,6 +1,6 @@
 #QU#FO#TNLB#
 #__________M
-#__________K
+|__________K
 W__________I
 #__A_____A_D
 C__________E
diff --git a/overcooked_simulator/game_items.py b/overcooked_simulator/game_items.py
index 163a2d136ad8f54d1e314ac9ff72a53abe3adce8..0b7c6d6db4f6a687dd587bed7a0bdfb733ecc9c7 100644
--- a/overcooked_simulator/game_items.py
+++ b/overcooked_simulator/game_items.py
@@ -26,7 +26,10 @@ import datetime
 import logging
 import uuid
 from enum import Enum
-from typing import Optional, TypedDict
+from typing import Optional, TypedDict, TYPE_CHECKING
+
+if TYPE_CHECKING:
+    from overcooked_simulator.effect_manager import EffectManager
 
 log = logging.getLogger(__name__)
 """The logger for this module."""
@@ -38,6 +41,10 @@ COOKING_EQUIPMENT_ITEM_CATEGORY = "ItemCookingEquipment"
 """The string for the `category` value in the json state representation for all cooking equipments."""
 
 
+class EffectType(Enum):
+    Unusable = "Unusable"
+
+
 class ItemType(Enum):
     Ingredient = "Ingredient"
     """All ingredients and process ingredients."""
@@ -47,6 +54,10 @@ class ItemType(Enum):
     """All counters and cooking equipments."""
     Waste = "Waste"
     """Burnt ingredients and meals."""
+    Effect = "Effect"
+    """Does not change the item but the object attributes, like adding fire."""
+    Tool = "Tool"
+    """Item that remains in hands in extends the interactive abilities of the player."""
 
 
 @dataclasses.dataclass
@@ -89,16 +100,24 @@ class ItemInfo:
     """The name of the item, is set automatically by the "group" name of the item."""
     seconds: float = dataclasses.field(compare=False, default=0)
     """If progress is needed this argument defines how long it takes to complete the process in seconds."""
+
+    # TODO maybe as a lambda/based on Prefix?
     needs: list[str] = dataclasses.field(compare=False, default_factory=list)
     """The ingredients/items which are needed to create the item/start the progress."""
     equipment: ItemInfo | None = dataclasses.field(compare=False, default=None)
     """On which the item can be created. `null`, `~` (None) converts to Plate."""
+    manager: str | None | EffectManager = None
+    """The manager for the effect."""
+    effect_type: None | EffectType = None
+    """How does the effect effect interaction, combine actions etc."""
 
     recipe: collections.Counter | None = None
     """Internally set in CookingEquipment"""
 
     def __post_init__(self):
         self.type = ItemType(self.type)
+        if self.effect_type:
+            self.effect_type = EffectType(self.effect_type)
 
 
 class ActiveTransitionTypedDict(TypedDict):
@@ -106,7 +125,7 @@ class ActiveTransitionTypedDict(TypedDict):
 
     seconds: int | float
     """The needed seconds to progress for the transition."""
-    result: str
+    result: str | Item | Effect
     """The new name of the item after the transition."""
 
 
@@ -127,10 +146,12 @@ class Item:
         """The equipment with which the item was last progressed."""
         self.progress_percentage: float = 0.0
         """The current progress percentage of the item if it is progress-able."""
-        self.waste_progress: bool = False
+        self.inverse_progress: bool = False
         """Whether the progress will produce waste."""
         self.uuid: str = uuid.uuid4().hex if uid is None else uid
         """A unique identifier for the item. Useful for GUIs that handles specific asset instances."""
+        self.active_effects: list[Effect] = []
+        """The effects that affect the item."""
 
     def __repr__(self):
         if self.progress_equipment is None:
@@ -171,7 +192,7 @@ class Item:
         """Reset the progress."""
         self.progress_equipment = None
         self.progress_percentage = 0.0
-        self.waste_progress = False
+        self.inverse_progress = False
 
     def to_dict(self) -> dict:
         """For the state representation. Only the relevant attributes are put into the dict."""
@@ -180,7 +201,8 @@ class Item:
             "category": self.item_category,
             "type": self.name,
             "progress_percentage": self.progress_percentage,
-            "waste_progress": self.waste_progress,
+            "inverse_progress": self.inverse_progress,
+            "active_effects": [e.to_dict() for e in self.active_effects],
         }
 
 
@@ -215,6 +237,12 @@ class CookingEquipment(Item):
         if other is None:
             return False
 
+        if any(
+            e.item_info.effect_type == EffectType.Unusable for e in other.active_effects
+        ) or any(
+            e.item_info.effect_type == EffectType.Unusable for e in self.active_effects
+        ):
+            return False
         if isinstance(other, CookingEquipment):
             other = other.content_list
         else:
@@ -242,13 +270,22 @@ class CookingEquipment(Item):
 
     def can_progress(self) -> bool:
         """Check if the cooking equipment can progress items at all."""
-        return self.active_transition is not None
+        return self.active_transition is not None and not any(
+            e.item_info.effect_type == EffectType.Unusable for e in self.active_effects
+        )
 
     def progress(self, passed_time: datetime.timedelta, now: datetime.datetime):
         percent = passed_time.total_seconds() / self.active_transition["seconds"]
         super().progress(equipment=self.name, percent=percent)
         if self.progress_percentage == 1.0:
-            self.content_list = [self.active_transition["result"]]
+            if isinstance(self.active_transition["result"], Effect):
+                self.active_transition[
+                    "result"
+                ].item_info.manager.register_active_effect(
+                    self.active_transition["result"], self
+                )
+            else:
+                self.content_list = [self.active_transition["result"]]
             self.reset()
             self.check_active_transition()
 
@@ -257,16 +294,34 @@ class CookingEquipment(Item):
     def check_active_transition(self):
         ingredients = collections.Counter(item.name for item in self.content_list)
         for result, transition in self.transitions.items():
-            if ingredients == transition.recipe:
-                if transition.seconds == 0:
-                    self.content_ready = Item(name=result, item_info=transition)
-                else:
-                    self.active_transition = {
-                        "seconds": transition.seconds,
-                        "result": Item(name=result, item_info=transition),
-                    }
-                    self.waste_progress = transition.type == ItemType.Waste
-                break
+            if transition.type == ItemType.Effect:
+                if set(ingredients.keys()).issubset(
+                    transition.needs
+                ) and transition.manager.can_start_effect_transition(transition, self):
+                    if transition.seconds == 0:
+                        transition.manager.register_active_effect(
+                            Effect(name=transition.name, item_info=transition), self
+                        )
+                    else:
+                        self.active_transition = {
+                            "seconds": transition.seconds,
+                            "result": Effect(
+                                name=transition.name, item_info=transition
+                            ),
+                        }
+                        self.inverse_progress = True
+                    break  # ?
+            else:
+                if ingredients == transition.recipe:
+                    if transition.seconds == 0:
+                        self.content_ready = Item(name=result, item_info=transition)
+                    else:
+                        self.active_transition = {
+                            "seconds": transition.seconds,
+                            "result": Item(name=result, item_info=transition),
+                        }
+                        self.inverse_progress = transition.type == ItemType.Waste
+                    break
         else:
             self.content_ready = None
 
@@ -351,3 +406,19 @@ class Plate(CookingEquipment):
         elif self.clean:
             return True
         return False
+
+
+class Effect:
+    def __init__(self, name: str, item_info: ItemInfo, uid: str = None):
+        self.uuid: str = uuid.uuid4().hex if uid is None else uid
+        self.name = name
+        self.item_info = item_info
+        self.progres_percentage = 0.0
+
+    def to_dict(self) -> dict:
+        return {
+            "id": self.uuid,
+            "type": self.name,
+            "progress_percentage": self.progres_percentage,
+            "inverse_progress": True,
+        }
diff --git a/overcooked_simulator/gui_2d_vis/drawing.py b/overcooked_simulator/gui_2d_vis/drawing.py
index 00d4e32f6cc76b03179af72192482ca5b5407dd4..7515b212dd0c45b0e6c84d0228c86cea6df53ebd 100644
--- a/overcooked_simulator/gui_2d_vis/drawing.py
+++ b/overcooked_simulator/gui_2d_vis/drawing.py
@@ -17,6 +17,7 @@ from overcooked_simulator.state_representation import (
     PlayerState,
     CookingEquipmentState,
     ItemState,
+    EffectState,
 )
 
 USE_PLAYER_COOK_SPRITES = True
@@ -303,7 +304,7 @@ class Visualizer:
         self,
         pos: npt.NDArray[float] | list[float],
         grid_size: float,
-        item: ItemState | CookingEquipmentState,
+        item: ItemState | CookingEquipmentState | EffectState,
         scale: float = 1.0,
         plate=False,
         screen=None,
@@ -341,7 +342,7 @@ class Visualizer:
                 )
                 #
         if "progress_percentage" in item and item["progress_percentage"] > 0.0:
-            if item["waste_progress"]:
+            if item["inverse_progress"]:
                 percentage = 1 - item["progress_percentage"]
             else:
                 percentage = item["progress_percentage"]
@@ -350,7 +351,7 @@ class Visualizer:
                 pos,
                 percentage,
                 grid_size=grid_size,
-                attention=item["waste_progress"],
+                attention=item["inverse_progress"],
             )
 
         if (
@@ -386,6 +387,9 @@ class Visualizer:
                     screen=screen,
                     grid_size=grid_size,
                 )
+        if "active_effects" in item and item["active_effects"]:
+            for effect in item["active_effects"]:
+                self.draw_item(pos=pos, item=effect, screen=screen, grid_size=grid_size)
 
     @staticmethod
     def draw_progress_bar(
@@ -479,6 +483,14 @@ class Visualizer:
                     grid_size,
                     np.array(counter["pos"]) * grid_size + (grid_size / 2),
                 )
+            if counter["active_effects"]:
+                for effect in counter["active_effects"]:
+                    self.draw_item(
+                        pos=np.array(counter["pos"]) * grid_size + (grid_size / 2),
+                        grid_size=grid_size,
+                        screen=screen,
+                        item=effect,
+                    )
             if SHOW_COUNTER_CENTERS:
                 pygame.draw.circle(
                     screen,
diff --git a/overcooked_simulator/gui_2d_vis/visualization.yaml b/overcooked_simulator/gui_2d_vis/visualization.yaml
index 87415a0ed41fa01c46c5fb8e830d370e4acd776b..00310805584e8bb56fc5b4413399f269308b249c 100644
--- a/overcooked_simulator/gui_2d_vis/visualization.yaml
+++ b/overcooked_simulator/gui_2d_vis/visualization.yaml
@@ -125,6 +125,26 @@ SinkAddon:
       size: 0.85
       center_offset: [ 0, 0.03 ]
 
+# Tools
+Extinguisher:
+  parts:
+    - type: rect
+      color: red
+      height: 0.2
+      width: 0.6
+    - type: rect
+      color: black
+      width: 0.1
+      height: 0.2
+      center_offset: [ 0.1, -0.3 ]
+
+# Effects
+Fire:
+  parts:
+    - type: circle
+      color: red
+      radius: 0.3
+
 # Items
 Tomato:
   parts:
diff --git a/overcooked_simulator/overcooked_environment.py b/overcooked_simulator/overcooked_environment.py
index 53d8559f0be0642f3fa376789bea66e495eafb14..afbe8160a618c92400d82db9d2195e5d776e0c56 100644
--- a/overcooked_simulator/overcooked_environment.py
+++ b/overcooked_simulator/overcooked_environment.py
@@ -9,7 +9,7 @@ import sys
 from datetime import timedelta, datetime
 from enum import Enum
 from pathlib import Path
-from typing import Literal, TypedDict, Callable
+from typing import Literal, TypedDict, Callable, Tuple
 
 import numpy as np
 import numpy.typing as npt
@@ -20,6 +20,7 @@ from overcooked_simulator.counters import (
     Counter,
     PlateConfig,
 )
+from overcooked_simulator.effect_manager import EffectManager
 from overcooked_simulator.game_items import (
     ItemInfo,
     ItemType,
@@ -107,6 +108,7 @@ class EnvironmentConfig(TypedDict):
     orders: OrderConfig
     player_config: PlayerConfig
     layout_chars: dict[str, str]
+    effect_manager: dict
 
 
 class Environment:
@@ -197,6 +199,7 @@ class Environment:
                 )
             ),
             order_and_score=self.order_and_score,
+            effect_manager_config=self.environment_config["effect_manager"],
             hook=self.hook,
         )
 
@@ -240,6 +243,10 @@ class Environment:
         """The relative env time when it will stop/end"""
         log.debug(f"End time: {self.env_time_end}")
 
+        self.effect_manager: dict[
+            str, EffectManager
+        ] = self.counter_factory.setup_effect_manger(self.counters)
+
         self.hook(ENV_INITIALIZED)
 
     @property
@@ -325,7 +332,9 @@ class Environment:
         # TODO add colors for ingredients, equipment and meals
         # plt.show()
 
-    def parse_layout_file(self):
+    def parse_layout_file(
+        self,
+    ) -> Tuple[list[Counter], list[npt.NDArray], list[npt.NDArray]]:
         """Creates layout of kitchen counters in the environment based on layout file.
         Counters are arranged in a fixed size grid starting at [0,0]. The center of the first counter is at
         [counter_size/2, counter_size/2], counters are directly next to each other (of no empty space is specified
@@ -398,16 +407,14 @@ class Environment:
                     self.hook(ACTION_PUT, action=action, counter=counter)
                 elif action.action_type == ActionType.INTERACT:
                     if action.action_data == InterActionData.START:
-                        player.perform_interact_hold_start(counter)
-                        player.last_interacted_counter = counter
+                        player.perform_interact_start(counter)
                         self.hook(ACTION_INTERACT_START, action=action, counter=counter)
             else:
                 self.hook(
                     ACTION_ON_NOT_REACHABLE_COUNTER, action=action, counter=counter
                 )
             if action.action_data == InterActionData.STOP:
-                if player.last_interacted_counter:
-                    player.perform_interact_hold_stop(player.last_interacted_counter)
+                player.perform_interact_stop()
 
         self.hook(POST_PERFORM_ACTION, action=action)
 
@@ -642,13 +649,15 @@ class Environment:
             self.hook(GAME_ENDED_STEP)
         else:
             for player in self.players.values():
+                player.progress(passed_time, self.env_time)
                 if self.env_time <= player.movement_until:
                     self.perform_movement(player, passed_time)
 
             for counter in self.progressing_counters:
                 counter.progress(passed_time=passed_time, now=self.env_time)
             self.order_and_score.progress(passed_time=passed_time, now=self.env_time)
-
+            for effect_manager in self.effect_manager.values():
+                effect_manager.progress(passed_time=passed_time, now=self.env_time)
         # self.hook(POST_STEP, passed_time=passed_time)
 
     def get_state(self):
diff --git a/overcooked_simulator/player.py b/overcooked_simulator/player.py
index 0507e279ab80b765ead610623cd29991e37b0332..e2284ae0658f82064cfb690dc6bb0629fcb6ae21 100644
--- a/overcooked_simulator/player.py
+++ b/overcooked_simulator/player.py
@@ -7,16 +7,16 @@ holding object**. If so, it picks up the content and combines it on its hands.
 """
 
 import dataclasses
-import datetime
 import logging
 from collections import deque
+from datetime import datetime, timedelta
 from typing import Optional
 
 import numpy as np
 import numpy.typing as npt
 
 from overcooked_simulator.counters import Counter
-from overcooked_simulator.game_items import Item, Plate
+from overcooked_simulator.game_items import Item, Plate, ItemType
 from overcooked_simulator.state_representation import PlayerState
 
 log = logging.getLogger(__name__)
@@ -81,9 +81,11 @@ class Player:
 
         self.current_movement: npt.NDArray[float] = np.zeros(2, float)
         """The movement vector that will be used to calculate the movement in the next step call."""
-        self.movement_until: datetime.datetime = datetime.datetime.min
+        self.movement_until: datetime = datetime.min
         """The env time until the player wants to move."""
 
+        self.interacting: bool = False
+
     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"""
@@ -97,6 +99,8 @@ class Player:
         Args:
             movement: 2D-Vector of length 1
         """
+        if self.interacting and np.any(movement):
+            self.perform_interact_stop()
         self.pos += movement
         if np.linalg.norm(movement) != 0:
             self.turn(movement)
@@ -164,25 +168,36 @@ class Player:
         if isinstance(self.holding, Plate):
             log.debug(self.holding.clean)
 
-    @staticmethod
-    def perform_interact_hold_start(counter: Counter):
+    def perform_interact_start(self, counter: Counter):
         """Starts an interaction with the counter. Should be called for a
         keydown event, for holding down a key on the keyboard.
 
         Args:
             counter: The counter to start the interaction with.
         """
-        counter.interact_start()
+        self.interacting = True
+        self.last_interacted_counter = counter
 
-    @staticmethod
-    def perform_interact_hold_stop(counter: Counter):
+    def perform_interact_stop(self):
         """Stops an interaction with the counter. Should be called for a
         keyup event, for letting go of a keyboard key.
 
         Args:
             counter: The counter to stop the interaction with.
         """
-        counter.interact_stop()
+        self.interacting = False
+        self.last_interacted_counter = None
+
+    def progress(self, passed_time: timedelta, now: datetime):
+        if self.interacting and self.last_interacted_counter:
+            # TODO only interact on counter (Sink/CuttingBoard) if hands are free configure in config?
+            if self.holding:
+                if self.holding.item_info.type == ItemType.Tool:
+                    self.last_interacted_counter.do_tool_interaction(
+                        passed_time, self.holding
+                    )
+            else:
+                self.last_interacted_counter.do_hand_free_interaction(passed_time, now)
 
     def __repr__(self):
         return f"Player(name:{self.name},pos:{str(self.pos)},holds:{self.holding})"
diff --git a/overcooked_simulator/state_representation.py b/overcooked_simulator/state_representation.py
index 1949e6248832d6e66ba10895f8ec1a1816c65eae..6f61be46d54e5a941cc31fb96ea4f5b322b1b8ad 100644
--- a/overcooked_simulator/state_representation.py
+++ b/overcooked_simulator/state_representation.py
@@ -15,12 +15,20 @@ class OrderState(TypedDict):
     max_duration: float
 
 
+class EffectState(TypedDict):
+    id: str
+    type: str
+    progress_percentage: float | int
+    inverse_progress: bool
+
+
 class ItemState(TypedDict):
     id: str
     category: Literal["Item"] | Literal["ItemCookingEquipment"]
     type: str
     progress_percentage: float | int
-    waste_progress: bool
+    inverse_progress: bool
+    active_effects: list[EffectState]
     # add ItemType Meal ?
 
 
@@ -37,14 +45,10 @@ class CounterState(TypedDict):
     occupied_by: None | list[
         ItemState | CookingEquipmentState
     ] | ItemState | CookingEquipmentState
+    active_effects: list[EffectState]
     # list[ItemState] -> type in ["Sink", "PlateDispenser"]
 
 
-class CuttingBoardAndSinkState(TypedDict):
-    type: Literal["CuttingBoard"] | Literal["Sink"]
-    progressing: bool
-
-
 class PlayerState(TypedDict):
     id: str
     pos: list[float]
diff --git a/overcooked_simulator/utils.py b/overcooked_simulator/utils.py
index 2754f08912d31ff513ed891646d29571800e8960..8f22d82daee96902a56799aad905d8996a0ea15f 100644
--- a/overcooked_simulator/utils.py
+++ b/overcooked_simulator/utils.py
@@ -1,20 +1,25 @@
 """
 Some utility functions.
 """
+from __future__ import annotations
 
 import logging
 import os
 import sys
 import uuid
+from collections import deque
 from datetime import datetime
 from enum import Enum
+from typing import TYPE_CHECKING
 
 import numpy as np
 import numpy.typing as npt
 from scipy.spatial import distance_matrix
 
 from overcooked_simulator import ROOT_DIR
-from overcooked_simulator.counters import Counter
+
+if TYPE_CHECKING:
+    from overcooked_simulator.counters import Counter
 
 
 def create_init_env_time():
@@ -39,6 +44,26 @@ def get_closest(point: npt.NDArray[float], counters: list[Counter]):
     ]
 
 
+def get_touching_counters(target: Counter, counters: list[Counter]) -> list[Counter]:
+    return list(
+        filter(
+            lambda counter: np.linalg.norm(counter.pos - target.pos) == 1.0, counters
+        )
+    )
+
+
+def find_item_on_counters(item_uuid: str, counters: list[Counter]) -> Counter | None:
+    for counter in counters:
+        if counter.occupied_by:
+            if isinstance(counter.occupied_by, deque):
+                for item in counter.occupied_by:
+                    if item.uuid == item_uuid:
+                        return counter
+            else:
+                if item_uuid == counter.occupied_by.uuid:
+                    return counter
+
+
 def custom_asdict_factory(data):
     """Convert enums to their value."""