Skip to content

Add starting items to ManualWorld to allow interacting with in hooks#210

Open
fraxker wants to merge 1 commit intoManualForArchipelago:mainfrom
fraxker:frax-starting-items
Open

Add starting items to ManualWorld to allow interacting with in hooks#210
fraxker wants to merge 1 commit intoManualForArchipelago:mainfrom
fraxker:frax-starting-items

Conversation

@fraxker
Copy link
Copy Markdown

@fraxker fraxker commented Mar 25, 2026

This PR adds the ability to set starting items from within hooks. Useful when there are items that the player could start with or could be placed into generation depending on settings.

def before_create_items_starting(item_pool: list, world: World, multiworld: MultiWorld, player: int) -> list:

    ### Handle Modifications to the Starting Inventory
    # for x_location, a value of 0 means starting inventory, hence the "not"
    option_item_pairs = [
        (not(get_option_value(multiworld, player, "mark_location")),"Teleport-Mark"),
        (not(get_option_value(multiworld, player, "mount_location")),"Slot-Mount")
    ]

    for option, item in option_item_pairs:
        starting_item_block = format_starting_item_block(item)
        if option: # add to starting item if option enabled
            world.starting_items.append(starting_item_block)
        elif starting_item_block in world.starting_items: # remove if option not enabled (prevents issues with multiple worlds overlapping)
            world.starting_items.remove(starting_item_block)

Copy link
Copy Markdown
Contributor

@silasary silasary left a comment

Choose a reason for hiding this comment

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

I think you're missing some commits?

@fraxker
Copy link
Copy Markdown
Author

fraxker commented Mar 30, 2026

I think you're missing some commits?

No, as of the current stable the starting_items are already imported on line 11.

from .Game import game_name, filler_item_name, starting_items

And then referenced on lines 207 & 208

if starting_items:
    for starting_item_block in starting_items:

In the current version, lines 207 & 208 reference the imported (global) version but after adding the starting_items reference to the ManualWorld object, since these existing references occur in the create_items instance function, python's tiered scope automatically updates to use the locally referenced object instead of the global one (See python docs for more information on scoping)

This one line change has been battle tested in an unreleased Manual archipelago of Wizard101 I and the other speedrunners have been developing over the last couple months. The hook I shared is a real example that is in use today.

@silasary
Copy link
Copy Markdown
Contributor

I know how scoping works. But starting_items is a dict, which is a mutable type.

You're not actually changing anything by changing the scope of a reference variable. It's still the same globally defined dict, and modifying it when you have multiple slots with different yaml settings will not do what you want it to do.

At the bare minimum, you need to be using copy.deepcopy to make starting_items safe to modify.

But I'd much rather have it as either a new hook, or a parameter on before_create_items_starting, rather than adding an undocumented field on the world object.

@fraxker
Copy link
Copy Markdown
Author

fraxker commented Mar 31, 2026

Wdym undocumented field? This seemed like a documented feature of the game.json schema.

Apologies if I am being difficult. I assumed this was a straight forward change that honestly seemed like an oversight as it is the only one of the three imports from game.py that is not saved on the world object (but is referenced inside it). From my experience, while the global dict may be a mutable type, any changes to it do not get saved to the reference of the dict that is imported for when create_items so it is effectively immutable after data validation is run.

The main feature here that we are using this for mapping choice options to if an item is included in starting_items or put into the generation pool as the yaml_option parameter for them only support booleans. Please let me know what the preferred way to solve this problem is so I can implement that to your vision. In the meantime we will keep using this solution as it has caused zero unintended behavior across hundreds of multiworlds.

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.

2 participants