Skip to content

Commit b8de5e6

Browse files
committed
Passengers who are about to make the game end blink for the last 10 seconds of waiting
1 parent dfad96d commit b8de5e6

7 files changed

Lines changed: 80 additions & 6 deletions

File tree

PROGRESS.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ Tests:
4646
- Added mediator regression coverage for first-arriving eligible metro boarding and travel-plan reassignment to the arriving line.
4747
- Increased station cap from 10 to 20 by updating `num_stations` in `src/config.py` (unlock milestones now generate up to 20 stations).
4848
- Updated path unlock milestones to `[0, 90, 300, 650]` in `src/config.py` and adjusted affected unlock-threshold tests in env/gameplay/graph suites.
49+
- Added pre-timeout passenger warning blink: passengers in the last 10 seconds before `passenger_max_wait_time_ms` now blink on/off in station queues.
50+
- Threaded render-time wait thresholds through holder/station rendering so passenger warning blink is deterministic from mediator time.
51+
- Added station rendering regression coverage for warning passenger blink visibility phases.
4952

5053
Tests:
5154
- `python -m unittest -v`

src/config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@
3838
passenger_spawning_interval_step = 10 * framerate
3939
passenger_display_buffer = 3 * passenger_size
4040
passenger_max_wait_time_ms = 60_000
41+
passenger_blink_warning_time_ms = 10_000
42+
passenger_blink_interval_ms = 250
4143
max_waiting_passengers = 20
4244

4345
# metro

src/entity/holder.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,12 @@ def __init__(self, shape: Shape, capacity: int, id: str) -> None:
2323
def __repr__(self) -> str:
2424
return self.id
2525

26-
def draw(self, surface: pygame.surface.Surface):
26+
def draw(
27+
self,
28+
surface: pygame.surface.Surface,
29+
current_time_ms: int | None = None,
30+
passenger_max_wait_time_ms: int | None = None,
31+
):
2732
# draw self
2833
self.shape.draw(surface, self.position)
2934

@@ -42,7 +47,11 @@ def draw(self, surface: pygame.surface.Surface):
4247
)
4348
)
4449

45-
passenger.draw(surface)
50+
passenger.draw(
51+
surface,
52+
current_time_ms=current_time_ms,
53+
max_wait_time_ms=passenger_max_wait_time_ms,
54+
)
4655

4756
if col < (self.passengers_per_row - 1):
4857
col += 1

src/entity/passenger.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import pygame
2+
from config import passenger_blink_interval_ms, passenger_blink_warning_time_ms
23
from geometry.point import Point
34
from geometry.shape import Shape
45
from shortuuid import uuid # type: ignore
@@ -18,5 +19,27 @@ def __repr__(self) -> str:
1819
def __hash__(self) -> int:
1920
return hash(self.id)
2021

21-
def draw(self, surface: pygame.surface.Surface):
22+
def is_in_warning_window(self, max_wait_time_ms: int) -> bool:
23+
return (
24+
self.wait_ms < max_wait_time_ms
25+
and (max_wait_time_ms - self.wait_ms) <= passenger_blink_warning_time_ms
26+
)
27+
28+
def is_warning_blink_visible(self, current_time_ms: int) -> bool:
29+
phase_index = int(current_time_ms / passenger_blink_interval_ms)
30+
return phase_index % 2 == 0
31+
32+
def draw(
33+
self,
34+
surface: pygame.surface.Surface,
35+
current_time_ms: int | None = None,
36+
max_wait_time_ms: int | None = None,
37+
):
38+
if (
39+
current_time_ms is not None
40+
and max_wait_time_ms is not None
41+
and self.is_in_warning_window(max_wait_time_ms)
42+
and not self.is_warning_blink_visible(current_time_ms)
43+
):
44+
return
2245
self.destination_shape.draw(surface, self.position)

src/entity/station.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,15 @@ def draw(
5656
self,
5757
surface: pygame.surface.Surface,
5858
current_time_ms: int | None = None,
59+
passenger_max_wait_time_ms: int | None = None,
5960
) -> None:
6061
if (
6162
current_time_ms is not None
6263
and not self.is_unlock_blink_visible(current_time_ms)
6364
):
6465
return
65-
super().draw(surface)
66+
super().draw(
67+
surface,
68+
current_time_ms=current_time_ms,
69+
passenger_max_wait_time_ms=passenger_max_wait_time_ms,
70+
)

src/mediator.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,11 @@ def render(self, screen: pygame.surface.Surface) -> None:
188188
path_order = idx - (active_path_count // 2)
189189
path.draw(screen, path_order)
190190
for station in self.stations:
191-
station.draw(screen, self.time_ms)
191+
station.draw(
192+
screen,
193+
self.time_ms,
194+
passenger_max_wait_time_ms=self.passenger_max_wait_time_ms,
195+
)
192196
for metro in self.metros:
193197
metro.draw(screen)
194198
for button in self.buttons:

test/test_station.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@
66
sys.path.append(os.path.dirname(os.path.realpath(__file__)) + "/../src")
77

88
import pygame
9-
from config import passenger_display_buffer, passenger_size, station_color, station_size
9+
from config import (
10+
passenger_display_buffer,
11+
passenger_max_wait_time_ms,
12+
passenger_size,
13+
station_color,
14+
station_size,
15+
)
1016
from entity.get_entity import get_metros
1117
from entity.metro import Metro
1218
from entity.passenger import Passenger
@@ -96,6 +102,28 @@ def test_station_unlock_blink_hides_shape_during_off_phase(self):
96102
station.draw(self.screen, current_time_ms=200)
97103
station.shape.draw.assert_not_called()
98104

105+
def test_station_draw_hides_warning_passenger_during_off_phase(self):
106+
station = Station(Circle(station_color, station_size), Point(0, 0))
107+
passenger = Passenger(Circle((0, 0, 0), 3))
108+
passenger.wait_ms = passenger_max_wait_time_ms - 5_000
109+
station.add_passenger(passenger)
110+
passenger.destination_shape.draw = MagicMock()
111+
112+
station.draw(
113+
self.screen,
114+
current_time_ms=0,
115+
passenger_max_wait_time_ms=passenger_max_wait_time_ms,
116+
)
117+
passenger.destination_shape.draw.assert_called_once()
118+
119+
passenger.destination_shape.draw.reset_mock()
120+
station.draw(
121+
self.screen,
122+
current_time_ms=250,
123+
passenger_max_wait_time_ms=passenger_max_wait_time_ms,
124+
)
125+
passenger.destination_shape.draw.assert_not_called()
126+
99127

100128
if __name__ == "__main__":
101129
unittest.main()

0 commit comments

Comments
 (0)