Files
Grinch-AP/worlds/adventure/Options.py
Chris Wilson 5e3c5dedf3 WebHost: Massive overhaul of options pages (#2614)
* Implement support for option groups. WebHost options pages still need to be updated.

* Remove debug output

* In-progress conversion of player-options to Jinja rendering

* Support "Randomize" button without JS, transpile SCSS to CSS, include map file for later editors

* Un-alphabetize options, add default group name for item/location Option classes, implement more option types

* Re-flow UI generation to avoid printing rows with unsupported or invalid option types, add support for TextChoice options

* Support all remaining option types

* Rendering improvements and CSS fixes for prettiness

* Wrap options in a form, update button styles, fix labels, disable inputs where the default is random, nuke the JS

* Minor CSS tweaks, as recommended by the designer

* Hide JS-required elements in noscript tag. Add JS reactivity to range, named-range, and randomize buttons.

* Fix labels, add JS handling for TextChoice

* Make option groups collapsable

* PEP8 current option_groups progress (#2604)

* Make the python more PEP8 and remove unneeded imports

* remove LocationSet from `Item & Location Options` group

* It's ugly, but YAML generation is working

* Stop generating JSON files for player-options pages

* Do not include ItemDict entries whose values are zero

* Properly format yaml output

* Save options when form is submitted, load options on page load

* Fix options being omitted from the page if a group has an even number of options

* Implement generate-game, escape option descriptions

* Fix "randomize" checkboxes not properly setting YAML options to "random"

* Add a separator between item/location groups and items/locations in their respective lists

* Implement option presets

* Fix docs to detail what actually ended up happening

* implement option groups on webworld to allow dev sorting (#2616)

* Force extremely long item/location/option names with no spaces to text-wrap

* Fix "randomize" button being too wide in single-column display, change page header to include game name

* Update preset select to read "custom" when updating form inputs. Show error message if the user doesn't input a name

* Un-break weighted-options, add option group names to weighted options

* Nuke weighted-options. Set up framework to rebuild it in Jinja.

* Generate styles with scss, remove styles which will be replaced, add placeholders for worlds

* Support Toggle, DefaultOnToggle, and Choice options in weighted-options

* Implement expand/collapse without JS for worlds and option groups

* Properly style set options

* Implement Range and NamedRange. Also, CSS is hard.

* Add support for remaining option types. JS and backend still forthcoming.

* Add JS functionality for collapsing game divs, populating span values on range updates. Add <noscript> tag to warn users with JS disabled.

* Support showing/hiding game divs based on range value for game

* Add support for adding/deleting range rows

* Save settings to localStorage on form submission

* Save deleted options on form submission

* Break weighted-options into a per-game page.

- Break weighted-options into a per-game page
- Add "advanced options" links to supported games page
- Use details/summary tags on supported games, player-options, and weighted-options
- Fix bug preventing previously deleted rows from being removed on page load if JS is enabled
- Move route handling for options pages to options.py
- Remove world handling from weighted-options

* Implement loading previous settings from localStorage on page load if JS is enabled

* Weighted options can now generate YAML files and single-player games

* options pages now respect option visibility settings for simple and complex pages

* Remove `/weighted-settings` redirect, fix weighted-options link on player-options page

* Fix instance of AutoWorld not having access to proper `random`

* Catch instances of frozenset along with set

* Restore word-wrap in tooltips

* Fix word wrap in player-options labels

* Add `dedent` filter to help with formatting tooltips in player-options

* Do not change the ordering of keys when printing yaml files

* Move necessary import out of conditional statement

* Expand only the first option group by default on both options pages

* Respect option visibility when generating yaml template files

* Swap to double quotes

* Replace instances of `/weighted-settings` with `/weighted-options`, swap out incomplete links

* Strip newlines and spaces after applying dedent filter

* Fix documentation for option groups

* Update site map

* Update various docs

* Sort OptionSet lists alphabetically

* Minor style tweak

* Fix extremely long text overflowing tooltips

* Convert player-options to use CSS grid instead of tables

* Do not display link to weighted-options page on supported games if the options page is an external link

* Update worlds/AutoWorld.py

Bugfix by @alwaysintreble

Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com>

* Fix NamedRange options not being properly set if a preset it loaded

* Move option-presets route into options.py

* Include preset name in YAML if not "default" and not "custom"

* Removed macros for PlandoBosses and DefaultOnToggle, as they were handled by their parent classes

* Fix not disabling custom inputs when the randomize button is clicked

* Only sort OptionList and OptionSet valid_keys if they are unordered

* Quick style fixes for player-settings to give `select` elements `text-overflow: ellipsis` and increase base size of left-column

* Prevent showing a horizontal scroll bar on player-options if the browser width was beneath a certain threshold

* Fix a bug in weighted-options which prevented inputting a negative value for new range inputs

---------

Co-authored-by: Aaron Wagener <mmmcheese158@gmail.com>
2024-05-18 00:11:57 -04:00

245 lines
7.4 KiB
Python

from __future__ import annotations
from typing import Dict
from Options import Choice, Option, DefaultOnToggle, DeathLink, Range, Toggle
class FreeincarnateMax(Range):
"""How many maximum freeincarnate items to allow
When done generating items, any remaining item slots will be filled
with freeincarnates, up to this maximum amount. Any remaining item
slots after that will be 'nothing' items placed locally, so in multigame
multiworlds, keeping this value high will allow more items from other games
into Adventure.
"""
display_name = "Freeincarnate Maximum"
range_start = 0
range_end = 17
default = 17
class ItemRandoType(Choice):
"""Choose how items are placed in the game
Not yet implemented. Currently only traditional supported
Traditional: Adventure items are not in the map until
they are collected (except local items) and are dropped
on the player when collected. Adventure items are not checks.
Inactive: Every item is placed, but is inactive until collected.
Each item touched is a check. The bat ignores inactive items.
Supported values: traditional, inactive
Default value: traditional
"""
display_name = "Item type"
option_traditional = 0x00
option_inactive = 0x01
default = option_traditional
class DragonSlayCheck(DefaultOnToggle):
"""If true, slaying each dragon for the first time is a check
"""
display_name = "Slay Dragon Checks"
class TrapBatCheck(Choice):
"""
Locking the bat inside a castle may be a check
Not yet implemented
If set to yes, the bat will not start inside a castle.
Setting with_key requires the matching castle key to also be
in the castle with the bat, achieved by dropping the key in the
path of the portcullis as it falls. This setting is not recommended with the bat use_logic setting
Supported values: no, yes, with_key
Default value: yes
"""
display_name = "Trap bat check"
option_no_check = 0x0
option_yes_key_optional = 0x1
option_with_key = 0x2
default = option_yes_key_optional
class DragonRandoType(Choice):
"""
How to randomize the dragon starting locations
normal: Grundle is in the overworld, Yorgle in the white castle, and Rhindle in the black castle
shuffle: A random dragon is placed in the overworld, one in the white castle, and one in the black castle
overworldplus: Dragons can be placed anywhere, but at least one will be in the overworld
randomized: Dragons can be anywhere except the credits room
Supported values: normal, shuffle, overworldplus, randomized
Default value: shuffle
"""
display_name = "Dragon Randomization"
option_normal = 0x0
option_shuffle = 0x1
option_overworldplus = 0x2
option_randomized = 0x3
default = option_shuffle
class BatLogic(Choice):
"""How the bat is considered for logic
With cannot_break, the bat cannot pick up an item that starts out-of-logic until the player touches it
With can_break, the bat is free to pick up any items, even if they are out-of-logic
With use_logic, the bat can pick up anything just like can_break, and locations are no longer considered to require
the magnet or bridge to collect, since the bat can retrieve these.
A future option may allow the bat itself to be placed as an item.
Supported values: cannot_break, can_break, use_logic
Default value: can_break
"""
display_name = "Bat Logic"
option_cannot_break = 0x0
option_can_break = 0x1
option_use_logic = 0x2
default = option_can_break
class YorgleStartingSpeed(Range):
"""
Sets Yorgle's initial speed. Yorgle has a speed of 2 in the original game
Default value: 2
"""
display_name = "Yorgle MaxSpeed"
range_start = 1
range_end = 9
default = 2
class YorgleMinimumSpeed(Range):
"""
Sets Yorgle's speed when all speed reducers are found. Yorgle has a speed of 2 in the original game
Default value: 2
"""
display_name = "Yorgle Min Speed"
range_start = 1
range_end = 9
default = 1
class GrundleStartingSpeed(Range):
"""
Sets Grundle's initial speed. Grundle has a speed of 2 in the original game
Default value: 2
"""
display_name = "Grundle MaxSpeed"
range_start = 1
range_end = 9
default = 2
class GrundleMinimumSpeed(Range):
"""
Sets Grundle's speed when all speed reducers are found. Grundle has a speed of 2 in the original game
Default value: 2
"""
display_name = "Grundle Min Speed"
range_start = 1
range_end = 9
default = 1
class RhindleStartingSpeed(Range):
"""
Sets Rhindle's initial speed. Rhindle has a speed of 3 in the original game
Default value: 3
"""
display_name = "Rhindle MaxSpeed"
range_start = 1
range_end = 9
default = 3
class RhindleMinimumSpeed(Range):
"""
Sets Rhindle's speed when all speed reducers are found. Rhindle has a speed of 3 in the original game
Default value: 2
"""
display_name = "Rhindle Min Speed"
range_start = 1
range_end = 9
default = 2
class ConnectorMultiSlot(Toggle):
"""If true, the client and lua connector will add lowest 8 bits of the player slot
to the port number used to connect to each other, to simplify connecting multiple local
clients to local EmuHawk instances.
Set in the yaml, since the connector has to read this out of the rom file before connecting.
"""
display_name = "Connector Multi-Slot"
class DifficultySwitchA(Choice):
"""Set availability of left difficulty switch
This controls the speed of the dragons' bite animation
"""
display_name = "Left Difficulty Switch"
option_normal = 0x0
option_locked_hard = 0x1
option_hard_with_unlock_item = 0x2
default = option_hard_with_unlock_item
class DifficultySwitchB(Choice):
"""Set availability of right difficulty switch
On hard, dragons will run away from the sword
"""
display_name = "Right Difficulty Switch"
option_normal = 0x0
option_locked_hard = 0x1
option_hard_with_unlock_item = 0x2
default = option_hard_with_unlock_item
class StartCastle(Choice):
"""Choose or randomize which castle to start in front of.
This affects both normal start and reincarnation. Starting
at the black castle may give easy dot runs, while starting
at the white castle may make them more dangerous! Also, not
starting at the yellow castle can make delivering the chalice
with a full inventory slightly less trivial.
This doesn't affect logic since all the castles are reachable
from each other.
"""
display_name = "Start Castle"
option_yellow = 0
option_black = 1
option_white = 2
default = option_yellow
adventure_option_definitions: Dict[str, type(Option)] = {
"dragon_slay_check": DragonSlayCheck,
"death_link": DeathLink,
"bat_logic": BatLogic,
"freeincarnate_max": FreeincarnateMax,
"dragon_rando_type": DragonRandoType,
"connector_multi_slot": ConnectorMultiSlot,
"yorgle_speed": YorgleStartingSpeed,
"yorgle_min_speed": YorgleMinimumSpeed,
"grundle_speed": GrundleStartingSpeed,
"grundle_min_speed": GrundleMinimumSpeed,
"rhindle_speed": RhindleStartingSpeed,
"rhindle_min_speed": RhindleMinimumSpeed,
"difficulty_switch_a": DifficultySwitchA,
"difficulty_switch_b": DifficultySwitchB,
"start_castle": StartCastle,
}