diff --git a/overcooked_simulator/game_content/environment_config.yaml b/overcooked_simulator/game_content/environment_config.yaml index 6e58cec45fa0d7c81701afe42a427e8475102447..fdbe1db23264984fa1521d2641c1b49dc2915b41 100644 --- a/overcooked_simulator/game_content/environment_config.yaml +++ b/overcooked_simulator/game_content/environment_config.yaml @@ -87,7 +87,8 @@ player_config: radius: 0.4 player_speed_units_per_seconds: 6 interaction_range: 1.6 - + restricted_view: False + view_angle: 95 effect_manager: FireManager: diff --git a/overcooked_simulator/game_server.py b/overcooked_simulator/game_server.py index b6c2675bb1c74c8a750d12c65cb7717f5f70f8d6..c5bb5d2577dd0dde98a76f54474893fd0a838b56 100644 --- a/overcooked_simulator/game_server.py +++ b/overcooked_simulator/game_server.py @@ -238,7 +238,7 @@ class EnvironmentHandler: ): return self.envs[ self.player_data[player_hash].env_id - ].environment.get_json_state() + ].environment.get_json_state(self.player_data[player_hash].player_id) if player_hash not in self.player_data: return 1 if self.player_data[player_hash].env_id not in self.envs: diff --git a/overcooked_simulator/gui_2d_vis/drawing.py b/overcooked_simulator/gui_2d_vis/drawing.py index 4c6bd198404743e6d3a2179d803015e57a582b94..d9e5e8e22b0c3ad16df665e496c759178cd26262 100644 --- a/overcooked_simulator/gui_2d_vis/drawing.py +++ b/overcooked_simulator/gui_2d_vis/drawing.py @@ -146,6 +146,39 @@ class Visualizer: grid_size, ) + if "view_restriction" in state and state["view_restriction"]: + # rotate direction vector in both direction with the angel + # draw 2 large rect which are rotated so that one edge is the viewing border + + direction = pygame.math.Vector2(state["view_restriction"]["direction"]) + pos = pygame.math.Vector2(state["view_restriction"]["position"]) + angle = state["view_restriction"]["angle"] / 2 + + pos = pos * grid_size + pygame.math.Vector2([grid_size / 2, grid_size / 2]) + + rect_scale = max(width, height) + + left_beam = pos + (direction.rotate(angle) * rect_scale * 2) + right_beam = pos + (direction.rotate(-angle) * rect_scale * 2) + + pygame.draw.polygon( + screen, + colors["black"], + ( + pos - (direction * grid_size * 0.6), + left_beam - (direction * grid_size * 0.6), + left_beam + (direction.rotate(90) * rect_scale), + pos + - (direction * rect_scale * 2) + + (direction.rotate(90) * rect_scale), + pos + - (direction * rect_scale * 2) + + (direction.rotate(-90) * rect_scale), + right_beam + (direction.rotate(-90) * rect_scale), + right_beam - (direction * grid_size * 0.6), + ), + ) + def draw_background( self, surface: pygame.Surface, width: int, height: int, grid_size: int ): diff --git a/overcooked_simulator/gui_2d_vis/overcooked_gui.py b/overcooked_simulator/gui_2d_vis/overcooked_gui.py index 9f66332533000c02de2a640d8e55e4884e684588..a4d28c5cb9fe468683db502766b2f2f0c37a7371 100644 --- a/overcooked_simulator/gui_2d_vis/overcooked_gui.py +++ b/overcooked_simulator/gui_2d_vis/overcooked_gui.py @@ -28,7 +28,6 @@ from overcooked_simulator.utils import ( url_and_port_arguments, disable_websocket_logging_arguments, add_list_of_manager_ids_arguments, - setup_logging, ) @@ -936,7 +935,7 @@ class PyGameGUI: json.dumps( { "type": "get_state", - "player_hash": self.player_info[self.state_player_id][ + "player_hash": self.player_info[str(self.key_sets[0].current_idx)][ "player_hash" ], } diff --git a/overcooked_simulator/overcooked_environment.py b/overcooked_simulator/overcooked_environment.py index d11aae3e77b2295a37d9ba943ee437f50398a2c0..3db6ca2f6a610f1c30cb7415bf3f5a83b094abf0 100644 --- a/overcooked_simulator/overcooked_environment.py +++ b/overcooked_simulator/overcooked_environment.py @@ -55,6 +55,8 @@ from overcooked_simulator.utils import create_init_env_time, get_closest log = logging.getLogger(__name__) +FOG_OF_WAR = True + PREVENT_SQUEEZING_INTO_OTHER_PLAYERS = False @@ -158,6 +160,14 @@ class Environment: ) """The config of the environment. All environment specific attributes is configured here.""" + self.player_view_restricted = self.environment_config["player_config"][ + "restricted_view" + ] + if self.player_view_restricted: + self.player_view_angle = self.environment_config["player_config"][ + "view_angle" + ] + self.extra_setup_functions() self.layout_config = layout_config @@ -750,25 +760,35 @@ class Environment: "remaining_time": max(self.env_time_end - self.env_time, timedelta(0)), } - def get_json_state(self, player_id: str = None): - self.hook(PRE_STATE, player_id=player_id) - state = { - "players": [p.to_dict() for p in self.players.values()], - "counters": [c.to_dict() for c in self.counters], - "kitchen": {"width": self.kitchen_width, "height": self.kitchen_height}, - "score": self.order_and_score.score, - "orders": self.order_and_score.order_state(), - "ended": self.game_ended, - "env_time": self.env_time.isoformat(), - "remaining_time": max( - self.env_time_end - self.env_time, timedelta(0) - ).total_seconds(), - } - self.hook(STATE_DICT, state=state, player_id=player_id) - json_data = json.dumps(state) - self.hook(JSON_STATE, json_data=json_data, player_id=player_id) - assert StateRepresentation.model_validate_json(json_data=json_data) - return json_data + def get_json_state(self, player_id: str = None) -> str: + if player_id in self.players: + self.hook(PRE_STATE, player_id=player_id) + state = { + "players": [p.to_dict() for p in self.players.values()], + "counters": [c.to_dict() for c in self.counters], + "kitchen": {"width": self.kitchen_width, "height": self.kitchen_height}, + "score": self.order_and_score.score, + "orders": self.order_and_score.order_state(), + "ended": self.game_ended, + "env_time": self.env_time.isoformat(), + "remaining_time": max( + self.env_time_end - self.env_time, timedelta(0) + ).total_seconds(), + "view_restriction": { + "direction": self.players[player_id].facing_direction.tolist(), + "position": self.players[player_id].pos.tolist(), + "angle": self.player_view_angle, + "counter_mask": None, + } + if self.player_view_restricted + else None, + } + self.hook(STATE_DICT, state=state, player_id=player_id) + json_data = json.dumps(state) + self.hook(JSON_STATE, json_data=json_data, player_id=player_id) + assert StateRepresentation.model_validate_json(json_data=json_data) + return json_data + raise ValueError(f"No valid {player_id=}") def reset_env_time(self): """Reset the env time to the initial time, defined by `create_init_env_time`.""" diff --git a/overcooked_simulator/player.py b/overcooked_simulator/player.py index 5633a491248ae8b9448d457607b86b8e74121c08..c367561e98862425ecc1590087ef7deb982e608e 100644 --- a/overcooked_simulator/player.py +++ b/overcooked_simulator/player.py @@ -33,7 +33,10 @@ class PlayerConfig: """The move distance/speed of the player per action call.""" interaction_range: float = 1.6 """How far player can interact with counters.""" - + restricted_view: bool = False + """Whether or not the player can see the entire map at once or just a view frustrum.""" + view_angle: int | None = None + """Angle of the players view if restricted.""" class Player: """Class representing a player in the game environment. A player consists of a name, their position and what diff --git a/overcooked_simulator/state_representation.py b/overcooked_simulator/state_representation.py index a35f8cd0577e0c231d63e55bcec080c46d39385b..9d4e7427f092fb77769873dd570988e1e3c94f4d 100644 --- a/overcooked_simulator/state_representation.py +++ b/overcooked_simulator/state_representation.py @@ -66,6 +66,13 @@ class KitchenInfo(BaseModel): height: float +class ViewRestriction(BaseModel): + direction: list[float] + position: list[float] + angle: int # degrees + counter_mask: None | list[bool] + + class StateRepresentation(BaseModel): """The format of the returned state representation.""" @@ -77,6 +84,7 @@ class StateRepresentation(BaseModel): ended: bool env_time: datetime # isoformat str remaining_time: float + view_restriction: None | ViewRestriction def create_json_schema():