@@ -31,6 +31,8 @@ class GameInfo(TypedDict):
3131 id : str
3232 name : str
3333 description : str
34+ color : str
35+ time : int
3436
3537
3638BASE_PATH = "bot/resources/fun/adventures"
@@ -72,7 +74,7 @@ class EndRoomData(TypedDict):
7274 emoji : str
7375
7476
75- class AdventureData (TypedDict ):
77+ class GameData (TypedDict ):
7678 """
7779 A dictionary containing the game data, serialized from a JSON file in `resources/fun/adventures`.
7880
@@ -102,17 +104,20 @@ class GameSession:
102104 def __init__ (
103105 self ,
104106 ctx : Context ,
105- game_code : str | None = None ,
107+ game_code_or_index : str | None = None ,
106108 ):
107109 """Creates an instance of the GameSession class."""
108110 self ._ctx = ctx
109111 self ._bot = ctx .bot
110112
111113 # set the game details/ game codes required for the session
112- self .game_code = game_code
114+ self .game_code = game_code_or_index
113115 self .game_data = None
114- if game_code :
115- self .game_data = self ._get_game_data (game_code )
116+ self .game_info = None
117+ if game_code_or_index :
118+ self .game_code = self ._parse_game_code (game_code_or_index )
119+ self .game_data = self ._get_game_data ()
120+ self .game_info = self ._get_game_info ()
116121
117122 # store relevant discord info
118123 self .author = ctx .author
@@ -125,25 +130,31 @@ def __init__(
125130 self ._effects : list [str ] = []
126131
127132 # session settings
133+ self ._timeout_seconds = 30 if self .game_info is None else self .game_info ["time" ]
128134 self .timeout_message = (
129- "⏳ Hint: time is running out! You must make a choice within 60 seconds."
135+ f "⏳ Hint: time is running out! You must make a choice within { self . _timeout_seconds } seconds."
130136 )
131137 self ._timeout_task = None
132138 self .reset_timeout ()
133139
134- def _get_game_data (self , game_code : str ) -> AdventureData | None :
135- """Returns the game data for the given game code."""
140+ def _parse_game_code (self , game_code_or_index : str ) -> str :
141+ """Returns the actual game code for the given index/ game code."""
136142 # sanitize the game code to prevent directory traversal attacks.
137- game_code = Path (game_code ).name
143+ game_code = Path (game_code_or_index ).name
138144
139- # Convert index to game code if it's a number
145+ # convert index to game code if it's a number
140146 try :
141- index = int (game_code )
147+ index = int (game_code_or_index )
142148 game_code = AVAILABLE_GAMES [index - 1 ]["id" ]
143- self .game_code = game_code
144149 except (ValueError , IndexError ):
145150 pass
146151
152+ return game_code
153+
154+ def _get_game_data (self ) -> GameData | None :
155+ """Returns the game data for the given game code."""
156+ game_code = self .game_code
157+
147158 # load the game data from the JSON file
148159 try :
149160 game_data = json .loads (
@@ -153,14 +164,27 @@ def _get_game_data(self, game_code: str) -> AdventureData | None:
153164 except FileNotFoundError :
154165 raise GameCodeNotFoundError (f'Game code "{ game_code } " not found.' )
155166
167+ def _get_game_info (self ) -> GameInfo :
168+ """Returns the game info for the given game code."""
169+ game_code = self .game_code
170+
171+ try :
172+ return AVAILABLE_GAMES_DICT [game_code ]
173+ except KeyError :
174+ log .error (
175+ "Game data retrieved, but game info not found. Did you forget to add it to `available_games.json`?"
176+ )
177+ raise GameCodeNotFoundError (f'Game code "{ game_code } " not found.' )
178+
156179 async def notify_timeout (self ) -> None :
157180 """Notifies the user that the session has timed out."""
158181 await self .message .edit (content = "⏰ You took too long to make a choice! The game has ended. :(" )
159182
160- async def timeout (self , seconds : int = 60 ) -> None :
183+ async def timeout (self ) -> None :
161184 """Waits for a set number of seconds, then stops the game session."""
162- await asyncio .sleep (seconds )
185+ await asyncio .sleep (self . _timeout_seconds )
163186 await self .notify_timeout ()
187+ await self .message .clear_reactions ()
164188 await self .stop ()
165189
166190 def cancel_timeout (self ) -> None :
@@ -259,7 +283,7 @@ def _format_room_data(self, room_data: RoomData) -> str:
259283 def embed_message (self , room_data : RoomData | EndRoomData ) -> Embed :
260284 """Returns an Embed with the requested room data formatted within."""
261285 embed = Embed ()
262- embed .color = constants . Colours . soft_orange
286+ embed .color = int ( self . game_info [ "color" ], base = 16 )
263287
264288 current_game_name = AVAILABLE_GAMES_DICT [self .game_code ]["name" ]
265289
@@ -291,9 +315,9 @@ async def update_message(self, room_id: str) -> None:
291315 self .add_reactions ()
292316
293317 @classmethod
294- async def start (cls , ctx : Context , game_code : str | None = None ) -> "GameSession" :
318+ async def start (cls , ctx : Context , game_code_or_index : str | None = None ) -> "GameSession" :
295319 """Create and begin a game session based on the given game code."""
296- session = cls (ctx , game_code )
320+ session = cls (ctx , game_code_or_index )
297321 await session .prepare ()
298322
299323 return session
@@ -355,10 +379,10 @@ class Adventure(DiscordCog):
355379 """Custom Embed for Adventure RPG games."""
356380
357381 @commands .command (name = "adventure" )
358- async def new_adventure (self , ctx : Context , game_code : str | None = None ) -> None :
382+ async def new_adventure (self , ctx : Context , game_code_or_index : str | None = None ) -> None :
359383 """Wanted to slay a dragon? Embark on an exciting journey through text-based RPG adventure."""
360384 try :
361- await GameSession .start (ctx , game_code )
385+ await GameSession .start (ctx , game_code_or_index )
362386 except GameCodeNotFoundError as error :
363387 await ctx .send (str (error ))
364388
0 commit comments