mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 20:21:32 -06:00

If a NamedRange has a `special_range_names` entry outside the `range_start` and `range_end`, the HTML5 range input will clamp the submitted value to the closest value in the range. These means that, for example, Pokemon RB's "HM Compatibility" option's "Vanilla (-1)" option would instead get posted as "0" rather than "-1". This change updates NamedRange to behave like TextChoice, where the select element has a `name` attribute matching the option, and there is an additional element to be able to provide an option other than the select element's choices. This uses a different suffix of `-range` rather than `-custom` that TextChoice uses. The reason is we need some way to decide whether to use the custom value or the select value, and that method needs to work without JavaScript. For TextChoice this is easy, if the custom field is empty use the select element. For NamedRange this is more difficult as the browser will always submit *something*. My choice was to only use the value from the range if the select box is set to "custom". Since this only happens with JS as "custom' is hidden, I made the range hidden under no-JS. If it's preferred, I could make the select box hidden instead. Let me know. This PR also makes the `js-required` class set `display: none` with `!important` as otherwise the class wouldn't work on any rule that had `display: flex` with more specificity than a single class.
167 lines
8.8 KiB
HTML
167 lines
8.8 KiB
HTML
{% extends 'pageWrapper.html' %}
|
|
{% import 'playerOptions/macros.html' as inputs with context %}
|
|
|
|
{% block head %}
|
|
<title>{{ world_name }} Options</title>
|
|
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename="styles/playerOptions/playerOptions.css") }}" />
|
|
<script type="application/ecmascript" src="{{ url_for('static', filename="assets/md5.min.js") }}"></script>
|
|
<script type="application/ecmascript" src="{{ url_for('static', filename="assets/js-yaml.min.js") }}"></script>
|
|
<script type="application/ecmascript" src="{{ url_for('static', filename="assets/playerOptions.js") }}"></script>
|
|
|
|
<noscript>
|
|
<style>
|
|
.js-required{
|
|
display: none !important;
|
|
}
|
|
</style>
|
|
</noscript>
|
|
{% endblock %}
|
|
|
|
{% block body %}
|
|
{% include 'header/'+theme+'Header.html' %}
|
|
<div id="player-options" class="markdown" data-game="{{ world_name }}" data-presets="{{ presets }}">
|
|
<noscript>
|
|
<div class="js-warning-banner">
|
|
This page has reduced functionality without JavaScript.
|
|
</div>
|
|
</noscript>
|
|
|
|
<div id="user-message">{{ message }}</div>
|
|
|
|
<div id="player-options-header">
|
|
<h1>{{ world_name }}</h1>
|
|
<h1>Player Options</h1>
|
|
</div>
|
|
<p>Choose the options you would like to play with! You may generate a single-player game from this page,
|
|
or download an options file you can use to participate in a MultiWorld.</p>
|
|
|
|
<p>
|
|
A more advanced options configuration for all games can be found on the
|
|
<a href="weighted-options">Weighted options</a> page.
|
|
<br />
|
|
A list of all games you have generated can be found on the <a href="/user-content">User Content Page</a>.
|
|
<br />
|
|
You may also download the
|
|
<a href="/static/generated/configs/{{ world_name }}.yaml">template file for this game</a>.
|
|
</p>
|
|
|
|
<form id="options-form" method="post" enctype="application/x-www-form-urlencoded" action="generate-yaml">
|
|
<div id="meta-options">
|
|
<div>
|
|
<label for="player-name">
|
|
Player Name: <span class="interactive" data-tooltip="This is the name you use to connect with your game. This is also known as your 'slot name'.">(?)</span>
|
|
</label>
|
|
<input id="player-name" placeholder="Player" name="name" maxlength="16" />
|
|
</div>
|
|
<div class="js-required">
|
|
<label for="game-options-preset">
|
|
Options Preset: <span class="interactive" data-tooltip="Select from a list of developer-curated presets (if any) or reset all options to their defaults.">(?)</span>
|
|
</label>
|
|
<select id="game-options-preset" name="game-options-preset" disabled>
|
|
<option value="default">Default</option>
|
|
{% for preset_name in world.web.options_presets %}
|
|
<option value="{{ preset_name }}">{{ preset_name }}</option>
|
|
{% endfor %}
|
|
<option value="custom" hidden>Custom</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="option-groups">
|
|
{% for group_name, group_options in option_groups.items() %}
|
|
<details class="group-container" {% if not start_collapsed[group_name] %}open{% endif %}>
|
|
<summary class="h2">{{ group_name }}</summary>
|
|
<div class="game-options">
|
|
<div class="left">
|
|
{% for option_name, option in group_options.items() %}
|
|
{% if loop.index <= (loop.length / 2)|round(0,"ceil") %}
|
|
{% if issubclass(option, Options.Toggle) %}
|
|
{{ inputs.Toggle(option_name, option) }}
|
|
|
|
{% elif issubclass(option, Options.TextChoice) %}
|
|
{{ inputs.TextChoice(option_name, option) }}
|
|
|
|
{% elif issubclass(option, Options.Choice) %}
|
|
{{ inputs.Choice(option_name, option) }}
|
|
|
|
{% elif issubclass(option, Options.NamedRange) %}
|
|
{{ inputs.NamedRange(option_name, option) }}
|
|
|
|
{% elif issubclass(option, Options.Range) %}
|
|
{{ inputs.Range(option_name, option) }}
|
|
|
|
{% elif issubclass(option, Options.FreeText) %}
|
|
{{ inputs.FreeText(option_name, option) }}
|
|
|
|
{% elif issubclass(option, Options.ItemDict) and option.verify_item_name %}
|
|
{{ inputs.ItemDict(option_name, option) }}
|
|
|
|
{% elif issubclass(option, Options.OptionList) and option.valid_keys %}
|
|
{{ inputs.OptionList(option_name, option) }}
|
|
|
|
{% elif issubclass(option, Options.LocationSet) and option.verify_location_name %}
|
|
{{ inputs.LocationSet(option_name, option) }}
|
|
|
|
{% elif issubclass(option, Options.ItemSet) and option.verify_item_name %}
|
|
{{ inputs.ItemSet(option_name, option) }}
|
|
|
|
{% elif issubclass(option, Options.OptionSet) and option.valid_keys %}
|
|
{{ inputs.OptionSet(option_name, option) }}
|
|
|
|
{% endif %}
|
|
{% endif %}
|
|
{% endfor %}
|
|
</div>
|
|
<div class="right">
|
|
{% for option_name, option in group_options.items() %}
|
|
{% if loop.index > (loop.length / 2)|round(0,"ceil") %}
|
|
{% if issubclass(option, Options.Toggle) %}
|
|
{{ inputs.Toggle(option_name, option) }}
|
|
|
|
{% elif issubclass(option, Options.TextChoice) %}
|
|
{{ inputs.TextChoice(option_name, option) }}
|
|
|
|
{% elif issubclass(option, Options.Choice) %}
|
|
{{ inputs.Choice(option_name, option) }}
|
|
|
|
{% elif issubclass(option, Options.NamedRange) %}
|
|
{{ inputs.NamedRange(option_name, option) }}
|
|
|
|
{% elif issubclass(option, Options.Range) %}
|
|
{{ inputs.Range(option_name, option) }}
|
|
|
|
{% elif issubclass(option, Options.FreeText) %}
|
|
{{ inputs.FreeText(option_name, option) }}
|
|
|
|
{% elif issubclass(option, Options.ItemDict) and option.verify_item_name %}
|
|
{{ inputs.ItemDict(option_name, option) }}
|
|
|
|
{% elif issubclass(option, Options.OptionList) and option.valid_keys %}
|
|
{{ inputs.OptionList(option_name, option) }}
|
|
|
|
{% elif issubclass(option, Options.LocationSet) and option.verify_location_name %}
|
|
{{ inputs.LocationSet(option_name, option) }}
|
|
|
|
{% elif issubclass(option, Options.ItemSet) and option.verify_item_name %}
|
|
{{ inputs.ItemSet(option_name, option) }}
|
|
|
|
{% elif issubclass(option, Options.OptionSet) and option.valid_keys %}
|
|
{{ inputs.OptionSet(option_name, option) }}
|
|
|
|
{% endif %}
|
|
{% endif %}
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</details>
|
|
{% endfor %}
|
|
</div>
|
|
|
|
<div id="player-options-button-row">
|
|
<input type="submit" name="intent-export" value="Export Options" />
|
|
<input type="submit" name="intent-generate" value="Generate Single-Player Game">
|
|
</div>
|
|
</form>
|
|
</div>
|
|
{% endblock %}
|