Implements time-based behavior transitions for NPCs: - GameTime converts real time to game time (1 real min = 1 game hour) - ScheduleEntry defines hour/state/location/data transitions - NpcSchedule manages multiple entries with midnight wrapping - process_schedules() applies transitions when game hour changes - TOML support for schedule data in mob templates - Integrated into game loop with hourly checks Tests cover schedule transitions, game time calculation, TOML loading, and preventing duplicate processing.
90 lines
2.7 KiB
Python
90 lines
2.7 KiB
Python
"""Game time system for converting real time to in-game time."""
|
|
|
|
import time
|
|
|
|
|
|
class GameTime:
|
|
"""Tracks game time based on real time with configurable ratio.
|
|
|
|
By default: 1 real minute = 1 game hour (24 real minutes = 1 game day).
|
|
"""
|
|
|
|
def __init__(self, epoch: float, real_minutes_per_game_hour: float = 1.0):
|
|
"""Initialize game time.
|
|
|
|
Args:
|
|
epoch: Unix timestamp for game time hour 0
|
|
real_minutes_per_game_hour: Real minutes per game hour (default 1.0)
|
|
"""
|
|
self.epoch = epoch
|
|
self.real_minutes_per_game_hour = real_minutes_per_game_hour
|
|
|
|
def get_game_hour(self) -> int:
|
|
"""Get current game hour (0-23)."""
|
|
elapsed_real_seconds = time.time() - self.epoch
|
|
elapsed_real_minutes = elapsed_real_seconds / 60
|
|
elapsed_game_hours = elapsed_real_minutes / self.real_minutes_per_game_hour
|
|
return int(elapsed_game_hours) % 24
|
|
|
|
def get_game_time(self) -> tuple[int, int]:
|
|
"""Get current game time as (hour, minute).
|
|
|
|
Returns:
|
|
Tuple of (hour 0-23, minute 0-59)
|
|
"""
|
|
elapsed_real_seconds = time.time() - self.epoch
|
|
elapsed_real_minutes = elapsed_real_seconds / 60
|
|
elapsed_game_hours = elapsed_real_minutes / self.real_minutes_per_game_hour
|
|
|
|
hour = int(elapsed_game_hours) % 24
|
|
minute_fraction = elapsed_game_hours - int(elapsed_game_hours)
|
|
minute = int(minute_fraction * 60) % 60
|
|
|
|
return hour, minute
|
|
|
|
|
|
# Global game time instance (initialized at server startup)
|
|
_game_time: GameTime | None = None
|
|
|
|
|
|
def init_game_time(
|
|
epoch: float | None = None, real_minutes_per_game_hour: float = 1.0
|
|
) -> None:
|
|
"""Initialize the global game time instance.
|
|
|
|
Args:
|
|
epoch: Unix timestamp for game time hour 0 (default: current time)
|
|
real_minutes_per_game_hour: Real minutes per game hour (default 1.0)
|
|
"""
|
|
global _game_time
|
|
if epoch is None:
|
|
epoch = time.time()
|
|
_game_time = GameTime(epoch, real_minutes_per_game_hour)
|
|
|
|
|
|
def get_game_hour() -> int:
|
|
"""Get current game hour from global instance.
|
|
|
|
Returns:
|
|
Current game hour (0-23)
|
|
|
|
Raises:
|
|
RuntimeError: If game time not initialized
|
|
"""
|
|
if _game_time is None:
|
|
raise RuntimeError("Game time not initialized. Call init_game_time() first.")
|
|
return _game_time.get_game_hour()
|
|
|
|
|
|
def get_game_time() -> tuple[int, int]:
|
|
"""Get current game time from global instance.
|
|
|
|
Returns:
|
|
Tuple of (hour 0-23, minute 0-59)
|
|
|
|
Raises:
|
|
RuntimeError: If game time not initialized
|
|
"""
|
|
if _game_time is None:
|
|
raise RuntimeError("Game time not initialized. Call init_game_time() first.")
|
|
return _game_time.get_game_time()
|