Skip to content
Snippets Groups Projects
Commit 889364ac authored by Florian Schröder's avatar Florian Schröder
Browse files

Add waste progress attribute and burnt item visualization

A `waste_progress` attribute has been added to track the progress of burnt items. Changes also include the visualization of burnt items in greyscale and additional definitions for various types of burnt meals in Overcooked Simulator. This update enhances both the gameplay and its realism, indicating when an item is overcooked or burnt.
parent 9fc975d5
No related branches found
No related tags found
1 merge request!47Resolve "Burnt Meals and Ingredients"
Pipeline #45402 passed
......@@ -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
......@@ -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
......@@ -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 = []
......
......@@ -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()
......
......@@ -20,6 +20,7 @@ class ItemState(TypedDict):
category: Literal["Item"] | Literal["ItemCookingEquipment"]
type: str
progress_percentage: float | int
waste_progress: bool
# add ItemType Meal ?
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment