diff --git a/overcooked_simulator/game_content/item_info.yaml b/overcooked_simulator/game_content/item_info.yaml index a6458c6329cbf0323e6fb59338f34e0739c9786e..639f855132b57b7ccef78b6b865b9942b00f5820 100644 --- a/overcooked_simulator/game_content/item_info.yaml +++ b/overcooked_simulator/game_content/item_info.yaml @@ -176,3 +176,41 @@ Pizza: needs: [ PizzaBase, ChoppedTomato, GratedCheese, ChoppedSausage ] seconds: 7.0 equipment: Peel + +# -------------------------------------------------------------------------------- + +BurntCookedPatty: + type: Waste + seconds: 5.0 + needs: [ CookedPatty ] + equipment: Pan + +BurntChips: + type: Waste + seconds: 5.0 + needs: [ Chips ] + equipment: Basket + +BurntFriedFish: + type: Waste + seconds: 5.0 + needs: [ FriedFish ] + equipment: Basket + +BurntTomatoSoup: + type: Waste + needs: [ TomatoSoup ] + seconds: 6.0 + equipment: Pot + +BurntOnionSoup: + type: Waste + needs: [ OnionSoup ] + seconds: 6.0 + equipment: Pot + +BurntPizza: + type: Waste + needs: [ Pizza ] + seconds: 7.0 + equipment: Peel \ No newline at end of file diff --git a/overcooked_simulator/game_content/item_info_debug.yaml b/overcooked_simulator/game_content/item_info_debug.yaml index c2282253e9539c9686cd6746f536e0316c3b21ec..6340f564820daec1316060bc2d549b8aeca389c4 100644 --- a/overcooked_simulator/game_content/item_info_debug.yaml +++ b/overcooked_simulator/game_content/item_info_debug.yaml @@ -177,3 +177,42 @@ Pizza: needs: [ PizzaBase, ChoppedTomato, GratedCheese, ChoppedSausage ] seconds: 0.1 equipment: Peel + +# -------------------------------------------------------------------------------- + +BurntCookedPatty: + type: Waste + seconds: 5.0 + needs: [ CookedPatty ] + equipment: Pan + +BurntChips: + type: Waste + seconds: 5.0 + needs: [ Chips ] + equipment: Basket + +BurntFriedFish: + type: Waste + seconds: 5.0 + needs: [ FriedFish ] + equipment: Basket + +BurntTomatoSoup: + type: Waste + needs: [ TomatoSoup ] + seconds: 6.0 + equipment: Pot + +BurntOnionSoup: + type: Waste + needs: [ OnionSoup ] + seconds: 6.0 + equipment: Pot + +BurntPizza: + type: Waste + needs: [ Pizza ] + seconds: 7.0 + equipment: Peel + diff --git a/overcooked_simulator/game_items.py b/overcooked_simulator/game_items.py index 9a406367ff71e802b5ba31e6d7bf2cd95304f062..163a2d136ad8f54d1e314ac9ff72a53abe3adce8 100644 --- a/overcooked_simulator/game_items.py +++ b/overcooked_simulator/game_items.py @@ -45,6 +45,8 @@ class ItemType(Enum): """All combined ingredients that can be served.""" Equipment = "Equipment" """All counters and cooking equipments.""" + Waste = "Waste" + """Burnt ingredients and meals.""" @dataclasses.dataclass @@ -125,6 +127,8 @@ 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 + """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.""" @@ -167,6 +171,7 @@ class Item: """Reset the progress.""" self.progress_equipment = None self.progress_percentage = 0.0 + self.waste_progress = False def to_dict(self) -> dict: """For the state representation. Only the relevant attributes are put into the dict.""" @@ -175,6 +180,7 @@ class Item: "category": self.item_category, "type": self.name, "progress_percentage": self.progress_percentage, + "waste_progress": self.waste_progress, } @@ -231,19 +237,7 @@ class CookingEquipment(Item): else: self.content_list.append(other) - 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), - } - break - else: - self.content_ready = None + self.check_active_transition() return return_value def can_progress(self) -> bool: @@ -256,9 +250,26 @@ class CookingEquipment(Item): if self.progress_percentage == 1.0: self.content_list = [self.active_transition["result"]] self.reset() + self.check_active_transition() # todo set active transition for fire/burnt? + 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 + else: + self.content_ready = None + def reset_content(self): """Reset the content attributes after the content was picked up from the equipment.""" self.content_list = [] diff --git a/overcooked_simulator/gui_2d_vis/drawing.py b/overcooked_simulator/gui_2d_vis/drawing.py index 0690e22d09e82ca5deb589238cd4c6404b4535d6..00d4e32f6cc76b03179af72192482ca5b5407dd4 100644 --- a/overcooked_simulator/gui_2d_vis/drawing.py +++ b/overcooked_simulator/gui_2d_vis/drawing.py @@ -24,6 +24,21 @@ SHOW_INTERACTION_RANGE = False SHOW_COUNTER_CENTERS = False +def grayscale(img): + arr = pygame.surfarray.pixels3d(img) + mean_arr = np.dot(arr[:, :, :], [0.216, 0.587, 0.144]) + mean_arr3d = mean_arr[..., np.newaxis] + new_arr = np.repeat(mean_arr3d[:, :, :], 3, axis=2) + new_arr = new_arr.astype(np.int8) + surface = pygame.Surface(new_arr.shape[0:2], pygame.SRCALPHA, 32) + + # Copy the rgb part of array to the new surface. + pygame.pixelcopy.array_to_surface(surface, new_arr) + surface_alpha = np.array(surface.get_view("A"), copy=False) + surface_alpha[:, :] = pygame.surfarray.pixels_alpha(img) + return surface + + def create_polygon(n, length): if n == 1: return np.array([0, 0]) @@ -112,16 +127,27 @@ class Visualizer: size: float, pos: npt.NDArray, rot_angle=0, + burnt: bool = False, ): cache_entry = f"{img_path}" - if cache_entry in self.image_cache_dict.keys(): - image = self.image_cache_dict[cache_entry] + if cache_entry + ("-burnt" if burnt else "") in self.image_cache_dict: + image = self.image_cache_dict[cache_entry + ("-burnt" if burnt else "")] else: - image = pygame.image.load( - ROOT_DIR / "gui_2d_vis" / img_path - ).convert_alpha() - self.image_cache_dict[cache_entry] = image - + if burnt: + if cache_entry in self.image_cache_dict: + normal_image = self.image_cache_dict[cache_entry] + else: + normal_image = pygame.image.load( + ROOT_DIR / "gui_2d_vis" / img_path + ).convert_alpha() + self.image_cache_dict[cache_entry] = normal_image + image = grayscale(normal_image) + self.image_cache_dict[cache_entry + "-burnt"] = image + else: + image = pygame.image.load( + ROOT_DIR / "gui_2d_vis" / img_path + ).convert_alpha() + self.image_cache_dict[cache_entry] = image image = pygame.transform.scale(image, (size, size)) if rot_angle != 0: image = pygame.transform.rotate(image, rot_angle) @@ -230,6 +256,7 @@ class Visualizer: grid_size: float, parts: list[dict[str]], scale: float = 1.0, + burnt: bool = False, ): """Draws an item, based on its visual parts specified in the visualization config. @@ -254,6 +281,7 @@ class Visualizer: part["path"], part["size"] * scale * grid_size, draw_pos, + burnt=burnt, ) case "rect": height = part["height"] * grid_size @@ -293,34 +321,58 @@ class Visualizer: plate: item is on a plate (soup are is different on a plate and pot) """ - if not isinstance(item, list): # can we remove this check? - if item["type"] in self.config: + if not isinstance(item, list): # can we remove this check?w + if item["type"] in self.config or ( + item["type"].startswith("Burnt") + and item["type"].replace("Burnt", "") in self.config + ): item_key = item["type"] if "Soup" in item_key and plate: item_key += "Plate" + if item_key.startswith("Burnt"): + item_key = item_key.replace("Burnt", "") self.draw_thing( pos=pos, parts=self.config[item_key]["parts"], scale=scale, screen=screen, grid_size=grid_size, + burnt=item["type"].startswith("Burnt"), ) # if "progress_percentage" in item and item["progress_percentage"] > 0.0: + if item["waste_progress"]: + percentage = 1 - item["progress_percentage"] + else: + percentage = item["progress_percentage"] self.draw_progress_bar( - screen, pos, item["progress_percentage"], grid_size=grid_size + screen, + pos, + percentage, + grid_size=grid_size, + attention=item["waste_progress"], ) if ( "content_ready" in item and item["content_ready"] - and item["content_ready"]["type"] in self.config + and ( + item["content_ready"]["type"] in self.config + or ( + item["content_ready"]["type"].startswith("Burnt") + and item["content_ready"]["type"].replace("Burnt", "") + in self.config + ) + ) ): self.draw_thing( pos=pos, - parts=self.config[item["content_ready"]["type"]]["parts"], + parts=self.config[item["content_ready"]["type"].replace("Burnt", "")][ + "parts" + ], screen=screen, grid_size=grid_size, + burnt=item["type"].startswith("Burnt"), ) elif "content_list" in item and item["content_list"]: triangle_offsets = create_polygon(len(item["content_list"]), length=10) @@ -341,6 +393,7 @@ class Visualizer: pos: npt.NDArray[float], percent: float, grid_size: float, + attention: bool = False, ): """Visualize progress of progressing item as a green bar under the item.""" bar_pos = pos - (grid_size / 2) @@ -353,7 +406,7 @@ class Visualizer: progress_width, bar_height, ) - pygame.draw.rect(screen, colors["green1"], progress_bar) + pygame.draw.rect(screen, colors["red" if attention else "green1"], progress_bar) def draw_counter( self, screen: pygame.Surface, counter_dict: dict, grid_size: float @@ -491,6 +544,7 @@ class Visualizer: percent=percentage, screen=order_screen, grid_size=grid_size, + attention=percentage < 0.25, ) orders_rect = order_screen.get_rect() diff --git a/overcooked_simulator/state_representation.py b/overcooked_simulator/state_representation.py index 47ddf2878977b394b35855732bcdd52a4bc0d0da..1949e6248832d6e66ba10895f8ec1a1816c65eae 100644 --- a/overcooked_simulator/state_representation.py +++ b/overcooked_simulator/state_representation.py @@ -20,6 +20,7 @@ class ItemState(TypedDict): category: Literal["Item"] | Literal["ItemCookingEquipment"] type: str progress_percentage: float | int + waste_progress: bool # add ItemType Meal ?