-
Notifications
You must be signed in to change notification settings - Fork 638
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
Saving Princess: implement new game #3238
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a quick skim, looks pretty great so far. Will review it in more depth later.
Co-authored-by: Scipio Wright <scipiowright@gmail.com>
Co-authored-by: Scipio Wright <scipiowright@gmail.com>
RegionData was only wrapping a List[str], so we can directly use List[str]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small game, the apworld is written very cleanly. Unit tests passed fine, no errors when genning lots of random seeds. I did not test the client at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nature of Review
- Only
Options.py
and the documentation was reviewed - Documentation was checked through an editor and verified through WebHost
Options
Looking over the options, I did not see anything of concern, the tooltips were self-explanatory and the types made sense.
Documentation
Documentation, particularly in the game page is fairly good. I left some comments that I think will make the pages a bit more readable and condense information.
Client
Looking over the set-up guide, I see that a .bs4diff
file is used to patch the data.win
file of a Gamemaker game. I see that there are concerns about assets and code being included, but you may want to look at the Undertale implementation for patching or AP Procedure Patch (APPP) to see if you can integrate the patching process into either a launcher for patching or a standalone program. I don't consider this a requirement for merging, but it may make the game more approachable for randomizing.
Additional Housekeeping
The following things should also get done before merging
- Add game to
README.md
- Add your implementation to
CODEOWNERS
Co-authored-by: Scipio Wright <scipiowright@gmail.com>
Co-authored-by: Nicholas Saylor <79181893+nicholassaylor@users.noreply.github.com>
Co-authored-by: Nicholas Saylor <79181893+nicholassaylor@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nature of Review
- All aspects of previous review were re-reviewed
- Client was briefly looked over
Documentation
Changes look good, very minor changes suggested, otherwise very good
Client
Client now has an automated patch feature while still retaining the option to patch manually. I only took a cursory look over the patching process and didn't see any glaring issues. I was not able to test patching as I do not own the game.
Options
No changes since last review, however with #2614 being merged after my last review, it may be a good idea to take a look at Option Groups and Presets before merging. Not a requirement, but looking at the dataclass for your options, it seems like Option Groups would serve you well.
Housekeeping
Game was added to README.md
and CODEOWNERS
but a merge conflict on README.md
needs to be resolved. Minor inconvenience at most.
Overall, the concerns that I had previously were addressed and any remaining reservations are extremely minor and/or optional.
README.md
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has a merge conflict and needs to be resolved
SavingPrincessClient.py
Outdated
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe that in general, core is trying to shift away from clients in the root AP directory and instead having them open through the launcher.
3. Launch `ArchipelagoLauncher` or `ArchipelagoSavingPrincessClient` from your Archipelago installation folder | ||
4. If you launched `ArchipelagoLauncher`, click on "Saving Princess Client" | ||
* You will probably need to scroll down on the Clients column to see it |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you end up moving the client to launcher only, this would need to be changed
|
||
### Where do I get a YAML file? | ||
|
||
You can customize your options by visiting the [Saving Princess Player Options Page](/games/Saving Princess/player-options). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can customize your options by visiting the [Saving Princess Player Options Page](/games/Saving Princess/player-options). | |
You can customize your options by visiting the [Saving Princess Player Options Page](/games/Saving%20Princess/player-options). |
Fix broken link
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bump (broken link)
worlds/saving_princess/Client.py
Outdated
valid_install = is_install_valid() | ||
if not valid_install: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
valid_install = is_install_valid() | |
if not valid_install: | |
if not is_install_valid(): |
No need to hold onto this value since it's not used elsewhere
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A lot of minor things and mostly docs. Overall this all looks very good, though some of the helper functions, like get_location
confuse me. Only major thing was that you're putting events in the name_to_id
lookups. I looked at everything except Client.py
and have not done any generations yet.
Something I forgot to comment on: The trap change is simply a flat chance, so if there are 10 filler items and 50% traps, there are not assuredly 5 traps, but there could be any number 0-10. Is this intended?
|
||
Some locations, such as boss kills, have no visual representation, but those that do will have the Archipelago icon. | ||
|
||
Once the item is picked up a textbox will inform you of the item that was found as well as the player that will be receiving it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing comma
Once the item is picked up a textbox will inform you of the item that was found as well as the player that will be receiving it. | |
Once the item is picked up, a textbox will inform you of the item that was found as well as the player that will be receiving it. |
This textbox shows both which item you got and which player sent it to you. | ||
|
||
If you send an item to yourself, however, the sending player will be omitted. | ||
If the items are cheated in, no textbox will be displayed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a reason for this? What counts as "cheated in" here? Does start_inventory count?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By cheated in I meant anything that has the server as the player that found it. So, yeah, start_inventory would count for that. I found it annoying to see all the pop-ups when using start_inventory, but perhaps this would just be confusing instead?
I don't feel strongly one way or the other, so if this is outside of the norm of what other worlds do then I'm happy to change it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would probably be good to show the popup always, at least so the case of /send
and /get_item
give immediate feedback to the player. The games I've played do show start inventory items as well, but I'm not sure if that's typical across AP.
|
||
### Where do I get a YAML file? | ||
|
||
You can customize your options by visiting the [Saving Princess Player Options Page](/games/Saving Princess/player-options). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bump (broken link)
### Set up the connection details | ||
|
||
After launching the game, enter the Archipelago options menu through the in-game button with the Archipelago icon. | ||
From here, enter the different menus and type in the **server:port**, **slot**, as well as the **password**, if one is required. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
An example of the exact connection details required could be quite helpful. Are all three of these in their own boxes and password is left empty if one doesn't exist?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is what I came up with, I'm not sure if this is what you had in mind, so I'm asking before committing anything:
After launching the game, enter the Archipelago options menu through the in-game button with the Archipelago icon.
From here, enter the different menus and type in the following details in their respective fields:
- server:port (e.g.
archipelago.gg:38281
)
- If hosting on the website, the server and port will be shown in your created room.
- slot name (e.g.
Player
)
- This is your player name, which you chose along with your player options.
- password (e.g.
123456
)
- If the room does not have a password, it should be left empty.
This configuration persists through launches and even updates.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That looks great to me!
If the game is unable to reconnect, save and restart. | ||
|
||
Although you can keep playing while disconnected, you won't get any items until you reconnect, not even items found in your own game. | ||
Once reconnected, however, all of your progress should sync up. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see the use of "should" here, if there's some kind of thing to do if it doesn't sync, it would be good to list that here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By "should sync up" I was trying to say that gm-apclientpp would take care of it.
I guess I should just say "will sync up"? Because if we assume the mod, the client and the protocol all work, then it will sync up, and if doesn't I don't think there's anything the user could really do to help it anyway?
The only way I've managed to make it not sync properly in my testing has been by wiping all of the local server's data and then starting it back up, but if it comes to that I'm not sure there's much a user can do about it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changing to "will" makes sense to me then
worlds/saving_princess/__init__.py
Outdated
item_name_to_id = {key: value.code for key, value in Items.item_dict.items()} | ||
location_name_to_id = {key: value.code for key, value in Locations.location_dict.items()} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These include events, which should be removed
worlds/saving_princess/__init__.py
Outdated
settings: ClassVar[SavingPrincessSettings] | ||
|
||
is_pool_expanded: bool = False | ||
music_table: List[int] = [i for i in range(16)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is faster
music_table: List[int] = [i for i in range(16)] | |
music_table: List[int] = list(range(16)) |
worlds/saving_princess/__init__.py
Outdated
# and return one of the names at random | ||
return self.random.choice(filler_list) | ||
|
||
def fill_slot_data(self) -> Dict[str, Any]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could use self.options.as_dict("death_link", "expanded_pool" "instant_saving", "sprint_availability", "cliff_weapon_upgrade", "ace_weapon_upgrade", "shake_intensity", "iframes_duration")
instead here.
def set_rules(self): | ||
from .Rules import set_rules | ||
set_rules(self) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should probably be moved to line up with when it occurs in the call order, namely after create_items
/ before fill_slot_data
from .Constants import * | ||
|
||
|
||
def set_rules(world: "SavingPrincessWorld"): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you have to import SavingPrincessWorld
then?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if you mean I'm missing the import (I think this is what other worlds do/what I'm missing):
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from . import SavingPrincessWorld
Or if you are asking why I'm using it in the first place?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I'm saying you'd want to include that exact code here
I've now done one thousand generations (wow this game gens fast) and no errors occurred across 250 solo gens and 750 gens of increasing player size. Went over the WebHost display and everything looks fine there as well, you could potentially consider adding option groups or option presets, but it's small enough that it seems alright without. |
Having now played the game, it connects and seems to run just fine, however you're connecting with the from Options import OptionError, PerGameCommonOptions
def generate_early(self) -> None:
if not self.player_name.isascii():
raise OptionError(f"{self.player_name}'s name must be only ASCII.") |
self.count = count | ||
self.count_extra = count_extra | ||
|
||
def create_item(self, player: int): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of creating these every single time you create an item, why not just make lookup tables directly beforehand that are shared and then use those?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I'm not sure I follow.
I think you mean count
and count_extra
? But I'm pretty sure those are only touched once per entry in my item dictionary, which is the only place ItemData
instances are made (unless I made a mistake somewhere).
Or maybe it's my class naming that is confusing? ItemData
holds data used to create SavingPrincessItem
instances, but the SavingPrincessItem
class doesn't have ItemData
in it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm saying that every time you call create_item
the index
and name
variables get totally rebuilt. But since the item dict is static, you could just create these once, right after making the item dict itself instead of creating them many times
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand now. Maybe something like this would make more sense:
def create_item(self, player: int):
return SavingPrincessItem(item_data_names[self], self.item_class, self.code, player)
[... all of the ItemData dict stuff ...]
item_data_names: Dict[ItemData, str] = {value: key for key, value in item_dict.items()}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me
This is intentional, and I believe the docstring ("Likelihood of a filler item becoming a trap.") for the option is clear enough. Is this design choice a big deal? I figured traps should be a surprise, and knowing the exact number of them kinda goes against that in my mind. |
I don't think it's a big deal, just a design choice that different devs do wildly differently so i figured it was worth asking about just in case you hadn't considered it. |
as suggested by @Exempt-Medic this includes the removal of events from the item/location name to id, as well as checking for the player name being ASCII
the LaunchCommand option is filled to either the executable or wine with the necessary arguments based on Utils.is_windows
now deletes possible existing files before installing the mod
items sent directly by the server (such as with starting inventory) now have pop-ups just like any other item
Unless I missed something, I believe I tackled all of the concerns. I also removed the standalone launcher, leaving only the ArchipelagoLauncher one, and added option groups and presets. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Recent changes LGTM, did 100 more singleplayer generations and 100 more 25-player generations. Tested playing through one game. All prior comments were addressed. The Webhost changes look good as well and the presets and option groups make sense to me.
What is this fixing or adding?
Adds support for Saving Princess to Archipelago.
How was this tested?
The world was tested with an async that had 3 players run the game, together with another 6 games. Only two major issues were found during this, which have since been fixed.
Over the past month and a half I have also played seeds by myself, testing every option by running the game on its own, with other slots of the game and also with other supported and unsupported games.
If this makes graphical changes, please attach screenshots.