Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add .adventure command #1653

Open
wants to merge 13 commits into
base: main
Choose a base branch
from

Conversation

Strengthless
Copy link

@Strengthless Strengthless commented Mar 1, 2025

Relevant Issues

This adds a text-based RPG adventure game. Closes #238.

Description

In this PR, we add a new adventure.py file, along with several JSON files as game assets.

The architecture of adventure.py is pretty similar is that of help.py:

  • the setup function loads the Adventure cog;
  • the Adventure cog contains all the commands that can be invoked;
  • when .adventure [game_code_or_index] is run, we instantiate a GameSession which contains all the states, business logic, event handlers, and helper functions for the actual game to run.
  • when .adventures or .adventure is run, we instantiate a GameSession without game_data, raising a GameCodeNotFoundError error, and is subsequently handled by sending a message to the channel.

Note: we are currently working on a few feature additions, e.g. support display of images in the rooms, and a retry button for restarting the game after it has ended. However, we would still like to get some early feedback from the dev team. Thanks!

Scope

  • Write a playable prototype of your game as a bot command.
    • Use .adventure [game_code] or .adventure [index] to play the game.
    • Use .adventures or .adventure to view a list of available games.
  • Make all player interactions reactions instead of having the player type commands.
  • Make the entire game happen in a single message that the bot edits, instead of having the bot post new messages.
  • Make a system that is possible to easily extend with new campaigns.
    • Define your own rooms, choices, collectibles (i.e., effects) and endings in a JSON format!
    • Customize game settings such as embed color, timeout seconds, etc.
    • Display a list of available games, or an error message if the game does not exist.
  • Support multiple concurrent games.
    • One player can instantiate multiple games at once.
    • More than one game can be played at the same time, and players can only react to their own game.

How to use

As a player

To test out the command, simply run .adventure or .adventures to see available games. You can then run .adventure [index] or .adventure [code] to start a game.

As a game developer

You need to include your game info in the available_games.json file. Then, design your rooms, collectibles and endings in [game_code].json.

You will need to include a starting room with "start" as key, and choices that lead to other rooms. Repeat this until your storyline reaches an ending (of course, you can have multiple endings). Trust us, the JSON format is very intuitive. Take a look at the three sample JSON files we've provided, and you shall understand it quickly. :)

The only interesting caveat here, is effect, requires_effect and effect_restricts (optional fields in OptionData). Basically, they're "items" or "collectibles" that you can get by choosing a specific option.

Once you have obtained the item, options with requires_effect set to that item will become unlocked, and is now a valid option for the player; at the same time, options with effect_restricts set to that item will become locked.

This "memory" mechanism allows you to design your game much more efficiently, and opens up a lot more interesting possibilities. Just as an example, you can introduce hidden paths, collectible items, ever-changing scenes, and much more!

Showcase video

Screen.Recording.2025-03-01.at.1.22.44.AM.mp4

Future considerations

  • Allow aborting of games
  • Allow display of images in the rooms
  • Allow the user to see a report of their choices/ collectibles at the end of the game
  • Add a retry button to restart the game after it has ended
  • Annotate type of endings with "neural", "good" or "bad"
  • Use LinePaginator for the list of available games

Did you:

Strengthless and others added 7 commits February 26, 2025 16:05
* feat: added scary haunted mansion story and choices now affect the game later

* refactor: refactored and cherry picked according to comments

* refactor: refactored and cherry picked according to comments

* feat: added effect_restricts

* docs: changed comments describing effect_restrics

---------

Co-authored-by: Johan Nilsson <[email protected]>
Co-authored-by: Strengthless <[email protected]>
Copy link
Contributor

@wookie184 wookie184 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR - this looks really good! The code is very neat and I love the addition of the requires_effect/effect_restricts options. Also, thanks for the detailed PR description, it makes it much easier to review.

I've played around with it and the game works nicely - I have no suggestions from a gameplay perspective.

I've commented on a few minor points, let me know your thoughts :)

@Strengthless
Copy link
Author

Strengthless commented Mar 2, 2025

Thanks @wookie184, most of the comments have been addressed, except for one about GameData, which I've left some comments on. Could you please advise?

edit: following an offline discussion over Discord, I have decided to refactor it via commit 8cb5f97. All comments are now addressed.

I've also included an extra commit 75d4044 that fixes an echo vulnerability, along with a QoL update that allows backticked game codes to be processed as normal:
image

@Strengthless Strengthless force-pushed the experimental/adventure branch from e3dc086 to cabb427 Compare March 2, 2025 15:36
@Strengthless Strengthless requested a review from wookie184 March 2, 2025 17:21
Copy link
Contributor

@wookie184 wookie184 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your patience on this. This all looks good other than two minor bugs I have noticed.

  1. When you type just .adventure, you get this error in the console after 30 seconds
Traceback (most recent call last):
  File "C:\Users\wookie\AppData\Local\pypoetry\Cache\virtualenvs\sir-lancebot-XnEUY10r-py3.12\Lib\site-packages\pydis_core\utils\scheduling.py", line 257, in _coro_wrapper
    await coro
  File "D:\sir-lancebot\bot\exts\fun\adventure.py", line 181, in timeout
    await self.notify_timeout()
  File "D:\sir-lancebot\bot\exts\fun\adventure.py", line 176, in notify_timeout
    await self.message.edit(content="⏰ You took too long to make a choice! The game has ended. :(")
          ^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'edit'
  1. If you click two reactions for the same room before it has a change to clear (you have to be very fast) you get a race condition.
Traceback (most recent call last):
  File "C:\Users\wookie\AppData\Local\pypoetry\Cache\virtualenvs\sir-lancebot-XnEUY10r-py3.12\Lib\site-packages\discord\client.py", line 449, in _run_event
    await coro(*args, **kwargs)
  File "D:\sir-lancebot\bot\exts\fun\adventure.py", line 240, in on_reaction_add
    await self.pick_option(all_emojis.index(emoji))
                           ^^^^^^^^^^^^^^^^^^^^^^^
ValueError: '💨' is not in list

Neither of them have any effects visible to the user, but it's good to avoid creating unnecessary error logs so it would be ideal if they could be fixed.

Thanks again for working on this feature.

@Strengthless Strengthless requested a review from wookie184 April 1, 2025 20:19
@Strengthless
Copy link
Author

Strengthless commented Apr 1, 2025

@wookie184 Sorry for the delayed response - I've been quite busy recently.

That being said, I've just pushed a quick fix for this, though a little ugly.

I timeboxed half an hour for this, and unfortunately I couldn't come up with a clean solution for the race condition you mentioned (without refactoring a bunch of code) 🤔 Do we have any utility functions for mutex/ critical sections/ something like that?

Let me know what you think about this patch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Text Based Adventure
3 participants