mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-03-23 05:33:19 -07:00
Compare commits
65 Commits
NewSoupVi-
...
0-6-0-rc1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
992841a951 | ||
|
|
eb3c3d6bf2 | ||
|
|
39847c5502 | ||
|
|
130232b457 | ||
|
|
ca8ffe583d | ||
|
|
563794ab83 | ||
|
|
9443861849 | ||
|
|
cbf4bbbca8 | ||
|
|
9e353ebb8e | ||
|
|
9183e8f9c9 | ||
|
|
0bb657d2c8 | ||
|
|
992f192529 | ||
|
|
1c9409cac9 | ||
|
|
005a143e3e | ||
|
|
8732974857 | ||
|
|
1ac8349bd4 | ||
|
|
2b9fa89050 | ||
|
|
23ea3c0efc | ||
|
|
698d27aada | ||
|
|
3a46c9fd3e | ||
|
|
9507300939 | ||
|
|
0d6db291de | ||
|
|
d218dec826 | ||
|
|
3d5c277c31 | ||
|
|
a9435dc6bb | ||
|
|
8f307c226b | ||
|
|
4b8f990960 | ||
|
|
3a5a4b89ee | ||
|
|
1485882642 | ||
|
|
2e4f5a64b3 | ||
|
|
90f80ce1c1 | ||
|
|
78904151b0 | ||
|
|
9d4bd6eebd | ||
|
|
5c56dc0357 | ||
|
|
c7810823e8 | ||
|
|
902d03d447 | ||
|
|
b7621a0923 | ||
|
|
b7baaed391 | ||
|
|
9dac7d9cc3 | ||
|
|
1eefe23f11 | ||
|
|
207a76d1b5 | ||
|
|
01df35f215 | ||
|
|
bedf746f1d | ||
|
|
b91a7ac6fb | ||
|
|
79e6beeec3 | ||
|
|
dae9d4c575 | ||
|
|
04928bd83d | ||
|
|
0f3818e711 | ||
|
|
0f1dc6e19c | ||
|
|
ffd0c8b341 | ||
|
|
6220963195 | ||
|
|
20119e3162 | ||
|
|
4cb8fa3cdd | ||
|
|
93e8613da7 | ||
|
|
f9cc19e150 | ||
|
|
0f1c119c76 | ||
|
|
4c734b467f | ||
|
|
1f966ee705 | ||
|
|
172ad4e57d | ||
|
|
3f935aac13 | ||
|
|
9928639ce2 | ||
|
|
0fc722cb28 | ||
|
|
4edca0ce54 | ||
|
|
70942eda8c | ||
|
|
adcb2f59ca |
16
.github/pyright-config.json
vendored
16
.github/pyright-config.json
vendored
@@ -1,8 +1,20 @@
|
||||
{
|
||||
"include": [
|
||||
"type_check.py",
|
||||
"../BizHawkClient.py",
|
||||
"../Patch.py",
|
||||
"../test/general/test_groups.py",
|
||||
"../test/general/test_helpers.py",
|
||||
"../test/general/test_memory.py",
|
||||
"../test/general/test_names.py",
|
||||
"../test/multiworld/__init__.py",
|
||||
"../test/multiworld/test_multiworlds.py",
|
||||
"../test/netutils/__init__.py",
|
||||
"../test/programs/__init__.py",
|
||||
"../test/programs/test_multi_server.py",
|
||||
"../test/utils/__init__.py",
|
||||
"../test/webhost/test_descriptions.py",
|
||||
"../worlds/AutoSNIClient.py",
|
||||
"../Patch.py"
|
||||
"type_check.py"
|
||||
],
|
||||
|
||||
"exclude": [
|
||||
|
||||
2
.github/workflows/strict-type-check.yml
vendored
2
.github/workflows/strict-type-check.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
|
||||
- name: "Install dependencies"
|
||||
run: |
|
||||
python -m pip install --upgrade pip pyright==1.1.358
|
||||
python -m pip install --upgrade pip pyright==1.1.392.post0
|
||||
python ModuleUpdate.py --append "WebHostLib/requirements.txt" --force --yes
|
||||
|
||||
- name: "pyright: strict check on specific files"
|
||||
|
||||
@@ -31,6 +31,7 @@ import ssl
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
import kvui
|
||||
import argparse
|
||||
|
||||
logger = logging.getLogger("Client")
|
||||
|
||||
@@ -459,6 +460,13 @@ class CommonContext:
|
||||
await self.send_msgs([payload])
|
||||
await self.send_msgs([{"cmd": "Get", "keys": ["_read_race_mode"]}])
|
||||
|
||||
async def check_locations(self, locations: typing.Collection[int]) -> set[int]:
|
||||
"""Send new location checks to the server. Returns the set of actually new locations that were sent."""
|
||||
locations = set(locations) & self.missing_locations
|
||||
if locations:
|
||||
await self.send_msgs([{"cmd": 'LocationChecks', "locations": tuple(locations)}])
|
||||
return locations
|
||||
|
||||
async def console_input(self) -> str:
|
||||
if self.ui:
|
||||
self.ui.focus_textinput()
|
||||
@@ -1041,6 +1049,32 @@ def get_base_parser(description: typing.Optional[str] = None):
|
||||
return parser
|
||||
|
||||
|
||||
def handle_url_arg(args: "argparse.Namespace",
|
||||
parser: "typing.Optional[argparse.ArgumentParser]" = None) -> "argparse.Namespace":
|
||||
"""
|
||||
Parse the url arg "archipelago://name:pass@host:port" from launcher into correct launch args for CommonClient
|
||||
If alternate data is required the urlparse response is saved back to args.url if valid
|
||||
"""
|
||||
if not args.url:
|
||||
return args
|
||||
|
||||
url = urllib.parse.urlparse(args.url)
|
||||
if url.scheme != "archipelago":
|
||||
if not parser:
|
||||
parser = get_base_parser()
|
||||
parser.error(f"bad url, found {args.url}, expected url in form of archipelago://archipelago.gg:38281")
|
||||
return args
|
||||
|
||||
args.url = url
|
||||
args.connect = url.netloc
|
||||
if url.username:
|
||||
args.name = urllib.parse.unquote(url.username)
|
||||
if url.password:
|
||||
args.password = urllib.parse.unquote(url.password)
|
||||
|
||||
return args
|
||||
|
||||
|
||||
def run_as_textclient(*args):
|
||||
class TextContext(CommonContext):
|
||||
# Text Mode to use !hint and such with games that have no text entry
|
||||
@@ -1082,17 +1116,7 @@ def run_as_textclient(*args):
|
||||
parser.add_argument("url", nargs="?", help="Archipelago connection url")
|
||||
args = parser.parse_args(args)
|
||||
|
||||
# handle if text client is launched using the "archipelago://name:pass@host:port" url from webhost
|
||||
if args.url:
|
||||
url = urllib.parse.urlparse(args.url)
|
||||
if url.scheme == "archipelago":
|
||||
args.connect = url.netloc
|
||||
if url.username:
|
||||
args.name = urllib.parse.unquote(url.username)
|
||||
if url.password:
|
||||
args.password = urllib.parse.unquote(url.password)
|
||||
else:
|
||||
parser.error(f"bad url, found {args.url}, expected url in form of archipelago://archipelago.gg:38281")
|
||||
args = handle_url_arg(args, parser=parser)
|
||||
|
||||
# use colorama to display colored text highlighting on windows
|
||||
colorama.init()
|
||||
|
||||
20
Fill.py
20
Fill.py
@@ -571,6 +571,26 @@ def distribute_items_restrictive(multiworld: MultiWorld,
|
||||
print_data = {"items": items_counter, "locations": locations_counter}
|
||||
logging.info(f"Per-Player counts: {print_data})")
|
||||
|
||||
more_locations = locations_counter - items_counter
|
||||
more_items = items_counter - locations_counter
|
||||
for player in multiworld.player_ids:
|
||||
if more_locations[player]:
|
||||
logging.error(
|
||||
f"Player {multiworld.get_player_name(player)} had {more_locations[player]} more locations than items.")
|
||||
elif more_items[player]:
|
||||
logging.warning(
|
||||
f"Player {multiworld.get_player_name(player)} had {more_items[player]} more items than locations.")
|
||||
if unfilled:
|
||||
raise FillError(
|
||||
f"Unable to fill all locations.\n" +
|
||||
f"Unfilled locations({len(unfilled)}): {unfilled}"
|
||||
)
|
||||
else:
|
||||
logging.warning(
|
||||
f"Unable to place all items.\n" +
|
||||
f"Unplaced items({len(unplaced)}): {unplaced}"
|
||||
)
|
||||
|
||||
|
||||
def flood_items(multiworld: MultiWorld) -> None:
|
||||
# get items to distribute
|
||||
|
||||
25
Generate.py
25
Generate.py
@@ -42,7 +42,9 @@ def mystery_argparse():
|
||||
help="Path to output folder. Absolute or relative to cwd.") # absolute or relative to cwd
|
||||
parser.add_argument('--race', action='store_true', default=defaults.race)
|
||||
parser.add_argument('--meta_file_path', default=defaults.meta_file_path)
|
||||
parser.add_argument('--log_level', default='info', help='Sets log level')
|
||||
parser.add_argument('--log_level', default=defaults.loglevel, help='Sets log level')
|
||||
parser.add_argument('--log_time', help="Add timestamps to STDOUT",
|
||||
default=defaults.logtime, action='store_true')
|
||||
parser.add_argument("--csv_output", action="store_true",
|
||||
help="Output rolled player options to csv (made for async multiworld).")
|
||||
parser.add_argument("--plando", default=defaults.plando_options,
|
||||
@@ -75,7 +77,7 @@ def main(args=None) -> Tuple[argparse.Namespace, int]:
|
||||
|
||||
seed = get_seed(args.seed)
|
||||
|
||||
Utils.init_logging(f"Generate_{seed}", loglevel=args.log_level)
|
||||
Utils.init_logging(f"Generate_{seed}", loglevel=args.log_level, add_timestamp=args.log_time)
|
||||
random.seed(seed)
|
||||
seed_name = get_seed_name(random)
|
||||
|
||||
@@ -438,7 +440,7 @@ def roll_settings(weights: dict, plando_options: PlandoOptions = PlandoOptions.b
|
||||
if "linked_options" in weights:
|
||||
weights = roll_linked_options(weights)
|
||||
|
||||
valid_keys = set()
|
||||
valid_keys = {"triggers"}
|
||||
if "triggers" in weights:
|
||||
weights = roll_triggers(weights, weights["triggers"], valid_keys)
|
||||
|
||||
@@ -497,16 +499,23 @@ def roll_settings(weights: dict, plando_options: PlandoOptions = PlandoOptions.b
|
||||
for option_key, option in world_type.options_dataclass.type_hints.items():
|
||||
handle_option(ret, game_weights, option_key, option, plando_options)
|
||||
valid_keys.add(option_key)
|
||||
for option_key in game_weights:
|
||||
if option_key in {"triggers", *valid_keys}:
|
||||
continue
|
||||
logging.warning(f"{option_key} is not a valid option name for {ret.game} and is not present in triggers "
|
||||
f"for player {ret.name}.")
|
||||
|
||||
# TODO remove plando_items after moving it to the options system
|
||||
valid_keys.add("plando_items")
|
||||
if PlandoOptions.items in plando_options:
|
||||
ret.plando_items = copy.deepcopy(game_weights.get("plando_items", []))
|
||||
if ret.game == "A Link to the Past":
|
||||
# TODO there are still more LTTP options not on the options system
|
||||
valid_keys |= {"sprite_pool", "sprite", "random_sprite_on_event"}
|
||||
roll_alttp_settings(ret, game_weights)
|
||||
|
||||
# log a warning for options within a game section that aren't determined as valid
|
||||
for option_key in game_weights:
|
||||
if option_key in valid_keys:
|
||||
continue
|
||||
logging.warning(f"{option_key} is not a valid option name for {ret.game} and is not present in triggers "
|
||||
f"for player {ret.name}.")
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
|
||||
@@ -560,6 +560,10 @@ class LinksAwakeningContext(CommonContext):
|
||||
|
||||
while self.client.auth == None:
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
# Just return if we're closing
|
||||
if self.exit_event.is_set():
|
||||
return
|
||||
self.auth = self.client.auth
|
||||
await self.send_connect()
|
||||
|
||||
|
||||
@@ -444,7 +444,7 @@ class Context:
|
||||
|
||||
self.slot_info = decoded_obj["slot_info"]
|
||||
self.games = {slot: slot_info.game for slot, slot_info in self.slot_info.items()}
|
||||
self.groups = {slot: slot_info.group_members for slot, slot_info in self.slot_info.items()
|
||||
self.groups = {slot: set(slot_info.group_members) for slot, slot_info in self.slot_info.items()
|
||||
if slot_info.type == SlotType.group}
|
||||
|
||||
self.clients = {0: {}}
|
||||
@@ -743,16 +743,17 @@ class Context:
|
||||
concerns[player].append(data)
|
||||
if not hint.local and data not in concerns[hint.finding_player]:
|
||||
concerns[hint.finding_player].append(data)
|
||||
# remember hints in all cases
|
||||
|
||||
# since hints are bidirectional, finding player and receiving player,
|
||||
# we can check once if hint already exists
|
||||
if hint not in self.hints[team, hint.finding_player]:
|
||||
self.hints[team, hint.finding_player].add(hint)
|
||||
new_hint_events.add(hint.finding_player)
|
||||
for player in self.slot_set(hint.receiving_player):
|
||||
self.hints[team, player].add(hint)
|
||||
new_hint_events.add(player)
|
||||
# only remember hints that were not already found at the time of creation
|
||||
if not hint.found:
|
||||
# since hints are bidirectional, finding player and receiving player,
|
||||
# we can check once if hint already exists
|
||||
if hint not in self.hints[team, hint.finding_player]:
|
||||
self.hints[team, hint.finding_player].add(hint)
|
||||
new_hint_events.add(hint.finding_player)
|
||||
for player in self.slot_set(hint.receiving_player):
|
||||
self.hints[team, player].add(hint)
|
||||
new_hint_events.add(player)
|
||||
|
||||
self.logger.info("Notice (Team #%d): %s" % (team + 1, format_hint(self, team, hint)))
|
||||
for slot in new_hint_events:
|
||||
@@ -1887,7 +1888,8 @@ async def process_client_cmd(ctx: Context, client: Client, args: dict):
|
||||
for location in args["locations"]:
|
||||
if type(location) is not int:
|
||||
await ctx.send_msgs(client,
|
||||
[{'cmd': 'InvalidPacket', "type": "arguments", "text": 'LocationScouts',
|
||||
[{'cmd': 'InvalidPacket', "type": "arguments",
|
||||
"text": 'Locations has to be a list of integers',
|
||||
"original_cmd": cmd}])
|
||||
return
|
||||
|
||||
@@ -1990,6 +1992,7 @@ async def process_client_cmd(ctx: Context, client: Client, args: dict):
|
||||
args["cmd"] = "SetReply"
|
||||
value = ctx.stored_data.get(args["key"], args.get("default", 0))
|
||||
args["original_value"] = copy.copy(value)
|
||||
args["slot"] = client.slot
|
||||
for operation in args["operations"]:
|
||||
func = modify_functions[operation["operation"]]
|
||||
value = func(value, operation["value"])
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import tkinter as tk
|
||||
import argparse
|
||||
import logging
|
||||
import random
|
||||
import os
|
||||
import zipfile
|
||||
from itertools import chain
|
||||
@@ -197,7 +196,6 @@ def set_icon(window):
|
||||
def adjust(args):
|
||||
# Create a fake multiworld and OOTWorld to use as a base
|
||||
multiworld = MultiWorld(1)
|
||||
multiworld.per_slot_randoms = {1: random}
|
||||
ootworld = OOTWorld(multiworld, 1)
|
||||
# Set options in the fake OOTWorld
|
||||
for name, option in chain(cosmetic_options.items(), sfx_options.items()):
|
||||
|
||||
22
Options.py
22
Options.py
@@ -137,7 +137,7 @@ class Option(typing.Generic[T], metaclass=AssembleOptions):
|
||||
If this is False, the docstring is instead interpreted as plain text, and
|
||||
displayed as-is on the WebHost with whitespace preserved.
|
||||
|
||||
If this is None, it inherits the value of `World.rich_text_options_doc`. For
|
||||
If this is None, it inherits the value of `WebWorld.rich_text_options_doc`. For
|
||||
backwards compatibility, this defaults to False, but worlds are encouraged to
|
||||
set it to True and use reStructuredText for their Option documentation.
|
||||
|
||||
@@ -689,9 +689,9 @@ class Range(NumericOption):
|
||||
@classmethod
|
||||
def weighted_range(cls, text) -> Range:
|
||||
if text == "random-low":
|
||||
return cls(cls.triangular(cls.range_start, cls.range_end, cls.range_start))
|
||||
return cls(cls.triangular(cls.range_start, cls.range_end, 0.0))
|
||||
elif text == "random-high":
|
||||
return cls(cls.triangular(cls.range_start, cls.range_end, cls.range_end))
|
||||
return cls(cls.triangular(cls.range_start, cls.range_end, 1.0))
|
||||
elif text == "random-middle":
|
||||
return cls(cls.triangular(cls.range_start, cls.range_end))
|
||||
elif text.startswith("random-range-"):
|
||||
@@ -717,11 +717,11 @@ class Range(NumericOption):
|
||||
f"{random_range[0]}-{random_range[1]} is outside allowed range "
|
||||
f"{cls.range_start}-{cls.range_end} for option {cls.__name__}")
|
||||
if text.startswith("random-range-low"):
|
||||
return cls(cls.triangular(random_range[0], random_range[1], random_range[0]))
|
||||
return cls(cls.triangular(random_range[0], random_range[1], 0.0))
|
||||
elif text.startswith("random-range-middle"):
|
||||
return cls(cls.triangular(random_range[0], random_range[1]))
|
||||
elif text.startswith("random-range-high"):
|
||||
return cls(cls.triangular(random_range[0], random_range[1], random_range[1]))
|
||||
return cls(cls.triangular(random_range[0], random_range[1], 1.0))
|
||||
else:
|
||||
return cls(random.randint(random_range[0], random_range[1]))
|
||||
|
||||
@@ -739,8 +739,16 @@ class Range(NumericOption):
|
||||
return str(self.value)
|
||||
|
||||
@staticmethod
|
||||
def triangular(lower: int, end: int, tri: typing.Optional[int] = None) -> int:
|
||||
return int(round(random.triangular(lower, end, tri), 0))
|
||||
def triangular(lower: int, end: int, tri: float = 0.5) -> int:
|
||||
"""
|
||||
Integer triangular distribution for `lower` inclusive to `end` inclusive.
|
||||
|
||||
Expects `lower <= end` and `0.0 <= tri <= 1.0`. The result of other inputs is undefined.
|
||||
"""
|
||||
# Use the continuous range [lower, end + 1) to produce an integer result in [lower, end].
|
||||
# random.triangular is actually [a, b] and not [a, b), so there is a very small chance of getting exactly b even
|
||||
# when a != b, so ensure the result is never more than `end`.
|
||||
return min(end, math.floor(random.triangular(0.0, 1.0, tri) * (end - lower + 1) + lower))
|
||||
|
||||
|
||||
class NamedRange(Range):
|
||||
|
||||
16
Utils.py
16
Utils.py
@@ -521,8 +521,8 @@ def init_logging(name: str, loglevel: typing.Union[str, int] = logging.INFO,
|
||||
def filter(self, record: logging.LogRecord) -> bool:
|
||||
return self.condition(record)
|
||||
|
||||
file_handler.addFilter(Filter("NoStream", lambda record: not getattr(record, "NoFile", False)))
|
||||
file_handler.addFilter(Filter("NoCarriageReturn", lambda record: '\r' not in record.msg))
|
||||
file_handler.addFilter(Filter("NoStream", lambda record: not getattr(record, "NoFile", False)))
|
||||
file_handler.addFilter(Filter("NoCarriageReturn", lambda record: '\r' not in record.getMessage()))
|
||||
root_logger.addHandler(file_handler)
|
||||
if sys.stdout:
|
||||
formatter = logging.Formatter(fmt='[%(asctime)s] %(message)s', datefmt='%Y-%m-%d %H:%M:%S')
|
||||
@@ -940,7 +940,7 @@ def freeze_support() -> None:
|
||||
|
||||
def visualize_regions(root_region: Region, file_name: str, *,
|
||||
show_entrance_names: bool = False, show_locations: bool = True, show_other_regions: bool = True,
|
||||
linetype_ortho: bool = True) -> None:
|
||||
linetype_ortho: bool = True, regions_to_highlight: set[Region] | None = None) -> None:
|
||||
"""Visualize the layout of a world as a PlantUML diagram.
|
||||
|
||||
:param root_region: The region from which to start the diagram from. (Usually the "Menu" region of your world.)
|
||||
@@ -956,16 +956,22 @@ def visualize_regions(root_region: Region, file_name: str, *,
|
||||
Items without ID will be shown in italics.
|
||||
:param show_other_regions: (default True) If enabled, regions that can't be reached by traversing exits are shown.
|
||||
:param linetype_ortho: (default True) If enabled, orthogonal straight line parts will be used; otherwise polylines.
|
||||
:param regions_to_highlight: Regions that will be highlighted in green if they are reachable.
|
||||
|
||||
Example usage in World code:
|
||||
from Utils import visualize_regions
|
||||
visualize_regions(self.multiworld.get_region("Menu", self.player), "my_world.puml")
|
||||
state = self.multiworld.get_all_state(False)
|
||||
state.update_reachable_regions(self.player)
|
||||
visualize_regions(self.get_region("Menu"), "my_world.puml", show_entrance_names=True,
|
||||
regions_to_highlight=state.reachable_regions[self.player])
|
||||
|
||||
Example usage in Main code:
|
||||
from Utils import visualize_regions
|
||||
for player in multiworld.player_ids:
|
||||
visualize_regions(multiworld.get_region("Menu", player), f"{multiworld.get_out_file_name_base(player)}.puml")
|
||||
"""
|
||||
if regions_to_highlight is None:
|
||||
regions_to_highlight = set()
|
||||
assert root_region.multiworld, "The multiworld attribute of root_region has to be filled"
|
||||
from BaseClasses import Entrance, Item, Location, LocationProgressType, MultiWorld, Region
|
||||
from collections import deque
|
||||
@@ -1018,7 +1024,7 @@ def visualize_regions(root_region: Region, file_name: str, *,
|
||||
uml.append(f"\"{fmt(region)}\" : {{field}} {lock}{fmt(location)}")
|
||||
|
||||
def visualize_region(region: Region) -> None:
|
||||
uml.append(f"class \"{fmt(region)}\"")
|
||||
uml.append(f"class \"{fmt(region)}\" {'#00FF00' if region in regions_to_highlight else ''}")
|
||||
if show_locations:
|
||||
visualize_locations(region)
|
||||
visualize_exits(region)
|
||||
|
||||
@@ -3,13 +3,13 @@ from typing import List, Tuple
|
||||
|
||||
from flask import Blueprint
|
||||
|
||||
from ..models import Seed
|
||||
from ..models import Seed, Slot
|
||||
|
||||
api_endpoints = Blueprint('api', __name__, url_prefix="/api")
|
||||
|
||||
|
||||
def get_players(seed: Seed) -> List[Tuple[str, str]]:
|
||||
return [(slot.player_name, slot.game) for slot in seed.slots]
|
||||
return [(slot.player_name, slot.game) for slot in seed.slots.order_by(Slot.player_id)]
|
||||
|
||||
|
||||
from . import datapackage, generate, room, user # trigger registration
|
||||
|
||||
@@ -30,4 +30,4 @@ def get_seeds():
|
||||
"creation_time": seed.creation_time,
|
||||
"players": get_players(seed.slots),
|
||||
})
|
||||
return jsonify(response)
|
||||
return jsonify(response)
|
||||
|
||||
@@ -121,6 +121,14 @@ Response:
|
||||
|
||||
Expected Response Type: `HASH_RESPONSE`
|
||||
|
||||
- `MEMORY_SIZE`
|
||||
Returns the size in bytes of the specified memory domain.
|
||||
|
||||
Expected Response Type: `MEMORY_SIZE_RESPONSE`
|
||||
|
||||
Additional Fields:
|
||||
- `domain` (`string`): The name of the memory domain to check
|
||||
|
||||
- `GUARD`
|
||||
Checks a section of memory against `expected_data`. If the bytes starting
|
||||
at `address` do not match `expected_data`, the response will have `value`
|
||||
@@ -216,6 +224,12 @@ Response:
|
||||
Additional Fields:
|
||||
- `value` (`string`): The returned hash
|
||||
|
||||
- `MEMORY_SIZE_RESPONSE`
|
||||
Contains the size in bytes of the specified memory domain.
|
||||
|
||||
Additional Fields:
|
||||
- `value` (`number`): The size of the domain in bytes
|
||||
|
||||
- `GUARD_RESPONSE`
|
||||
The result of an attempted `GUARD` request.
|
||||
|
||||
@@ -376,6 +390,15 @@ request_handlers = {
|
||||
return res
|
||||
end,
|
||||
|
||||
["MEMORY_SIZE"] = function (req)
|
||||
local res = {}
|
||||
|
||||
res["type"] = "MEMORY_SIZE_RESPONSE"
|
||||
res["value"] = memory.getmemorydomainsize(req["domain"])
|
||||
|
||||
return res
|
||||
end,
|
||||
|
||||
["GUARD"] = function (req)
|
||||
local res = {}
|
||||
local expected_data = base64.decode(req["expected_data"])
|
||||
@@ -613,9 +636,11 @@ end)
|
||||
|
||||
if bizhawk_major < 2 or (bizhawk_major == 2 and bizhawk_minor < 7) then
|
||||
print("Must use BizHawk 2.7.0 or newer")
|
||||
elseif bizhawk_major > 2 or (bizhawk_major == 2 and bizhawk_minor > 9) then
|
||||
print("Warning: This version of BizHawk is newer than this script. If it doesn't work, consider downgrading to 2.9.")
|
||||
else
|
||||
if bizhawk_major > 2 or (bizhawk_major == 2 and bizhawk_minor > 10) then
|
||||
print("Warning: This version of BizHawk is newer than this script. If it doesn't work, consider downgrading to 2.10.")
|
||||
end
|
||||
|
||||
if emu.getsystemid() == "NULL" then
|
||||
print("No ROM is loaded. Please load a ROM.")
|
||||
while emu.getsystemid() == "NULL" do
|
||||
|
||||
@@ -1816,7 +1816,7 @@ end
|
||||
|
||||
-- Main control handling: main loop and socket receive
|
||||
|
||||
function receive()
|
||||
function APreceive()
|
||||
l, e = ootSocket:receive()
|
||||
-- Handle incoming message
|
||||
if e == 'closed' then
|
||||
@@ -1874,7 +1874,7 @@ function main()
|
||||
end
|
||||
if (curstate == STATE_OK) or (curstate == STATE_INITIAL_CONNECTION_MADE) or (curstate == STATE_TENTATIVELY_CONNECTED) then
|
||||
if (frame % 30 == 0) then
|
||||
receive()
|
||||
APreceive()
|
||||
end
|
||||
elseif (curstate == STATE_UNINITIALIZED) then
|
||||
if (frame % 60 == 0) then
|
||||
|
||||
@@ -99,6 +99,9 @@
|
||||
# Lingo
|
||||
/worlds/lingo/ @hatkirby
|
||||
|
||||
# Links Awakening DX
|
||||
/worlds/ladx/ @threeandthreee
|
||||
|
||||
# Lufia II Ancient Cave
|
||||
/worlds/lufia2ac/ @el-u
|
||||
/worlds/lufia2ac/docs/ @wordfcuk @el-u
|
||||
@@ -236,9 +239,6 @@
|
||||
# Final Fantasy (1)
|
||||
# /worlds/ff1/
|
||||
|
||||
# Links Awakening DX
|
||||
# /worlds/ladx/
|
||||
|
||||
# Ocarina of Time
|
||||
# /worlds/oot/
|
||||
|
||||
|
||||
@@ -261,6 +261,7 @@ Sent to clients in response to a [Set](#Set) package if want_reply was set to tr
|
||||
| key | str | The key that was updated. |
|
||||
| value | any | The new value for the key. |
|
||||
| original_value | any | The value the key had before it was updated. Not present on "_read" prefixed special keys. |
|
||||
| slot | int | The slot that originally sent the Set package causing this change. |
|
||||
|
||||
Additional arguments added to the [Set](#Set) package that triggered this [SetReply](#SetReply) will also be passed along.
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ user hovers over the yellow "(?)" icon, and included in the YAML templates gener
|
||||
The WebHost can display Option documentation either as plain text with all whitespace preserved (other than the base
|
||||
indentation), or as HTML generated from the standard Python [reStructuredText] format. Although plain text is the
|
||||
default for backwards compatibility, world authors are encouraged to write their Option documentation as
|
||||
reStructuredText and enable rich text rendering by setting `World.rich_text_options_doc = True`.
|
||||
reStructuredText and enable rich text rendering by setting `WebWorld.rich_text_options_doc = True`.
|
||||
|
||||
[reStructuredText]: https://docutils.sourceforge.io/rst.html
|
||||
|
||||
|
||||
@@ -2,3 +2,6 @@
|
||||
python_files = test_*.py Test*.py # TODO: remove Test* once all worlds have been ported
|
||||
python_classes = Test
|
||||
python_functions = test
|
||||
testpaths =
|
||||
test
|
||||
worlds
|
||||
|
||||
@@ -678,6 +678,8 @@ class GeneratorOptions(Group):
|
||||
race: Race = Race(0)
|
||||
plando_options: PlandoOptions = PlandoOptions("bosses, connections, texts")
|
||||
panic_method: PanicMethod = PanicMethod("swap")
|
||||
loglevel: str = "info"
|
||||
logtime: bool = False
|
||||
|
||||
|
||||
class SNIOptions(Group):
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import unittest
|
||||
from typing import Callable, Dict, Optional
|
||||
|
||||
from typing_extensions import override
|
||||
|
||||
from BaseClasses import CollectionState, MultiWorld, Region
|
||||
|
||||
|
||||
@@ -8,6 +10,7 @@ class TestHelpers(unittest.TestCase):
|
||||
multiworld: MultiWorld
|
||||
player: int = 1
|
||||
|
||||
@override
|
||||
def setUp(self) -> None:
|
||||
self.multiworld = MultiWorld(self.player)
|
||||
self.multiworld.game[self.player] = "helper_test_game"
|
||||
@@ -38,15 +41,15 @@ class TestHelpers(unittest.TestCase):
|
||||
"TestRegion1": {"TestRegion2": "connection"},
|
||||
"TestRegion2": {"TestRegion1": None},
|
||||
}
|
||||
|
||||
|
||||
reg_exit_set: Dict[str, set[str]] = {
|
||||
"TestRegion1": {"TestRegion3"}
|
||||
}
|
||||
|
||||
|
||||
exit_rules: Dict[str, Callable[[CollectionState], bool]] = {
|
||||
"TestRegion1": lambda state: state.has("test_item", self.player)
|
||||
}
|
||||
|
||||
|
||||
self.multiworld.regions += [Region(region, self.player, self.multiworld, regions[region]) for region in regions]
|
||||
|
||||
with self.subTest("Test Location Creation Helper"):
|
||||
@@ -73,7 +76,7 @@ class TestHelpers(unittest.TestCase):
|
||||
entrance_name = exit_name if exit_name else f"{parent} -> {exit_reg}"
|
||||
self.assertEqual(exit_rules[exit_reg],
|
||||
self.multiworld.get_entrance(entrance_name, self.player).access_rule)
|
||||
|
||||
|
||||
for region in reg_exit_set:
|
||||
current_region = self.multiworld.get_region(region, self.player)
|
||||
current_region.add_exits(reg_exit_set[region])
|
||||
|
||||
@@ -39,7 +39,7 @@ class TestImplemented(unittest.TestCase):
|
||||
"""Tests that if a world creates slot data, it's json serializable."""
|
||||
for game_name, world_type in AutoWorldRegister.world_types.items():
|
||||
# has an await for generate_output which isn't being called
|
||||
if game_name in {"Ocarina of Time", "Zillion"}:
|
||||
if game_name in {"Ocarina of Time"}:
|
||||
continue
|
||||
multiworld = setup_solo_multiworld(world_type)
|
||||
with self.subTest(game=game_name, seed=multiworld.seed):
|
||||
@@ -117,3 +117,12 @@ class TestImplemented(unittest.TestCase):
|
||||
f"\nUnexpectedly reachable locations in sphere {sphere_num}:"
|
||||
f"\n{reachable_only_with_explicit}")
|
||||
self.fail("Unreachable")
|
||||
|
||||
def test_no_items_or_locations_or_regions_submitted_in_init(self):
|
||||
"""Test that worlds don't submit items/locations/regions to the multiworld in __init__"""
|
||||
for game_name, world_type in AutoWorldRegister.world_types.items():
|
||||
with self.subTest("Game", game=game_name):
|
||||
multiworld = setup_solo_multiworld(world_type, ())
|
||||
self.assertEqual(len(multiworld.itempool), 0)
|
||||
self.assertEqual(len(multiworld.get_locations()), 0)
|
||||
self.assertEqual(len(multiworld.get_regions()), 0)
|
||||
|
||||
@@ -5,7 +5,7 @@ from . import setup_solo_multiworld
|
||||
|
||||
|
||||
class TestWorldMemory(unittest.TestCase):
|
||||
def test_leak(self):
|
||||
def test_leak(self) -> None:
|
||||
"""Tests that worlds don't leak references to MultiWorld or themselves with default options."""
|
||||
import gc
|
||||
import weakref
|
||||
|
||||
@@ -3,7 +3,7 @@ from worlds.AutoWorld import AutoWorldRegister
|
||||
|
||||
|
||||
class TestNames(unittest.TestCase):
|
||||
def test_item_names_format(self):
|
||||
def test_item_names_format(self) -> None:
|
||||
"""Item names must not be all numeric in order to differentiate between ID and name in !hint"""
|
||||
for gamename, world_type in AutoWorldRegister.world_types.items():
|
||||
with self.subTest(game=gamename):
|
||||
@@ -11,7 +11,7 @@ class TestNames(unittest.TestCase):
|
||||
self.assertFalse(item_name.isnumeric(),
|
||||
f"Item name \"{item_name}\" is invalid. It must not be numeric.")
|
||||
|
||||
def test_location_name_format(self):
|
||||
def test_location_name_format(self) -> None:
|
||||
"""Location names must not be all numeric in order to differentiate between ID and name in !hint_location"""
|
||||
for gamename, world_type in AutoWorldRegister.world_types.items():
|
||||
with self.subTest(game=gamename):
|
||||
|
||||
@@ -10,7 +10,7 @@ import base64
|
||||
import enum
|
||||
import json
|
||||
import sys
|
||||
import typing
|
||||
from typing import Any, Sequence
|
||||
|
||||
|
||||
BIZHAWK_SOCKET_PORT_RANGE_START = 43055
|
||||
@@ -44,10 +44,10 @@ class SyncError(Exception):
|
||||
|
||||
|
||||
class BizHawkContext:
|
||||
streams: typing.Optional[typing.Tuple[asyncio.StreamReader, asyncio.StreamWriter]]
|
||||
streams: tuple[asyncio.StreamReader, asyncio.StreamWriter] | None
|
||||
connection_status: ConnectionStatus
|
||||
_lock: asyncio.Lock
|
||||
_port: typing.Optional[int]
|
||||
_port: int | None
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.streams = None
|
||||
@@ -122,12 +122,12 @@ async def get_script_version(ctx: BizHawkContext) -> int:
|
||||
return int(await ctx._send_message("VERSION"))
|
||||
|
||||
|
||||
async def send_requests(ctx: BizHawkContext, req_list: typing.List[typing.Dict[str, typing.Any]]) -> typing.List[typing.Dict[str, typing.Any]]:
|
||||
async def send_requests(ctx: BizHawkContext, req_list: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
"""Sends a list of requests to the BizHawk connector and returns their responses.
|
||||
|
||||
It's likely you want to use the wrapper functions instead of this."""
|
||||
responses = json.loads(await ctx._send_message(json.dumps(req_list)))
|
||||
errors: typing.List[ConnectorError] = []
|
||||
errors: list[ConnectorError] = []
|
||||
|
||||
for response in responses:
|
||||
if response["type"] == "ERROR":
|
||||
@@ -151,7 +151,7 @@ async def ping(ctx: BizHawkContext) -> None:
|
||||
|
||||
|
||||
async def get_hash(ctx: BizHawkContext) -> str:
|
||||
"""Gets the system name for the currently loaded ROM"""
|
||||
"""Gets the hash value of the currently loaded ROM"""
|
||||
res = (await send_requests(ctx, [{"type": "HASH"}]))[0]
|
||||
|
||||
if res["type"] != "HASH_RESPONSE":
|
||||
@@ -160,6 +160,16 @@ async def get_hash(ctx: BizHawkContext) -> str:
|
||||
return res["value"]
|
||||
|
||||
|
||||
async def get_memory_size(ctx: BizHawkContext, domain: str) -> int:
|
||||
"""Gets the size in bytes of the specified memory domain"""
|
||||
res = (await send_requests(ctx, [{"type": "MEMORY_SIZE", "domain": domain}]))[0]
|
||||
|
||||
if res["type"] != "MEMORY_SIZE_RESPONSE":
|
||||
raise SyncError(f"Expected response of type MEMORY_SIZE_RESPONSE but got {res['type']}")
|
||||
|
||||
return res["value"]
|
||||
|
||||
|
||||
async def get_system(ctx: BizHawkContext) -> str:
|
||||
"""Gets the system name for the currently loaded ROM"""
|
||||
res = (await send_requests(ctx, [{"type": "SYSTEM"}]))[0]
|
||||
@@ -170,7 +180,7 @@ async def get_system(ctx: BizHawkContext) -> str:
|
||||
return res["value"]
|
||||
|
||||
|
||||
async def get_cores(ctx: BizHawkContext) -> typing.Dict[str, str]:
|
||||
async def get_cores(ctx: BizHawkContext) -> dict[str, str]:
|
||||
"""Gets the preferred cores for systems with multiple cores. Only systems with multiple available cores have
|
||||
entries."""
|
||||
res = (await send_requests(ctx, [{"type": "PREFERRED_CORES"}]))[0]
|
||||
@@ -223,8 +233,8 @@ async def set_message_interval(ctx: BizHawkContext, value: float) -> None:
|
||||
raise SyncError(f"Expected response of type SET_MESSAGE_INTERVAL_RESPONSE but got {res['type']}")
|
||||
|
||||
|
||||
async def guarded_read(ctx: BizHawkContext, read_list: typing.Sequence[typing.Tuple[int, int, str]],
|
||||
guard_list: typing.Sequence[typing.Tuple[int, typing.Sequence[int], str]]) -> typing.Optional[typing.List[bytes]]:
|
||||
async def guarded_read(ctx: BizHawkContext, read_list: Sequence[tuple[int, int, str]],
|
||||
guard_list: Sequence[tuple[int, Sequence[int], str]]) -> list[bytes] | None:
|
||||
"""Reads an array of bytes at 1 or more addresses if and only if every byte in guard_list matches its expected
|
||||
value.
|
||||
|
||||
@@ -252,7 +262,7 @@ async def guarded_read(ctx: BizHawkContext, read_list: typing.Sequence[typing.Tu
|
||||
"domain": domain
|
||||
} for address, size, domain in read_list])
|
||||
|
||||
ret: typing.List[bytes] = []
|
||||
ret: list[bytes] = []
|
||||
for item in res:
|
||||
if item["type"] == "GUARD_RESPONSE":
|
||||
if not item["value"]:
|
||||
@@ -266,7 +276,7 @@ async def guarded_read(ctx: BizHawkContext, read_list: typing.Sequence[typing.Tu
|
||||
return ret
|
||||
|
||||
|
||||
async def read(ctx: BizHawkContext, read_list: typing.Sequence[typing.Tuple[int, int, str]]) -> typing.List[bytes]:
|
||||
async def read(ctx: BizHawkContext, read_list: Sequence[tuple[int, int, str]]) -> list[bytes]:
|
||||
"""Reads data at 1 or more addresses.
|
||||
|
||||
Items in `read_list` should be organized `(address, size, domain)` where
|
||||
@@ -278,8 +288,8 @@ async def read(ctx: BizHawkContext, read_list: typing.Sequence[typing.Tuple[int,
|
||||
return await guarded_read(ctx, read_list, [])
|
||||
|
||||
|
||||
async def guarded_write(ctx: BizHawkContext, write_list: typing.Sequence[typing.Tuple[int, typing.Sequence[int], str]],
|
||||
guard_list: typing.Sequence[typing.Tuple[int, typing.Sequence[int], str]]) -> bool:
|
||||
async def guarded_write(ctx: BizHawkContext, write_list: Sequence[tuple[int, Sequence[int], str]],
|
||||
guard_list: Sequence[tuple[int, Sequence[int], str]]) -> bool:
|
||||
"""Writes data to 1 or more addresses if and only if every byte in guard_list matches its expected value.
|
||||
|
||||
Items in `write_list` should be organized `(address, value, domain)` where
|
||||
@@ -316,7 +326,7 @@ async def guarded_write(ctx: BizHawkContext, write_list: typing.Sequence[typing.
|
||||
return True
|
||||
|
||||
|
||||
async def write(ctx: BizHawkContext, write_list: typing.Sequence[typing.Tuple[int, typing.Sequence[int], str]]) -> None:
|
||||
async def write(ctx: BizHawkContext, write_list: Sequence[tuple[int, Sequence[int], str]]) -> None:
|
||||
"""Writes data to 1 or more addresses.
|
||||
|
||||
Items in write_list should be organized `(address, value, domain)` where
|
||||
|
||||
@@ -5,7 +5,7 @@ A module containing the BizHawkClient base class and metaclass
|
||||
from __future__ import annotations
|
||||
|
||||
import abc
|
||||
from typing import TYPE_CHECKING, Any, ClassVar, Dict, Optional, Tuple, Union
|
||||
from typing import TYPE_CHECKING, Any, ClassVar
|
||||
|
||||
from worlds.LauncherComponents import Component, SuffixIdentifier, Type, components, launch_subprocess
|
||||
|
||||
@@ -24,9 +24,9 @@ components.append(component)
|
||||
|
||||
|
||||
class AutoBizHawkClientRegister(abc.ABCMeta):
|
||||
game_handlers: ClassVar[Dict[Tuple[str, ...], Dict[str, BizHawkClient]]] = {}
|
||||
game_handlers: ClassVar[dict[tuple[str, ...], dict[str, BizHawkClient]]] = {}
|
||||
|
||||
def __new__(cls, name: str, bases: Tuple[type, ...], namespace: Dict[str, Any]) -> AutoBizHawkClientRegister:
|
||||
def __new__(cls, name: str, bases: tuple[type, ...], namespace: dict[str, Any]) -> AutoBizHawkClientRegister:
|
||||
new_class = super().__new__(cls, name, bases, namespace)
|
||||
|
||||
# Register handler
|
||||
@@ -54,7 +54,7 @@ class AutoBizHawkClientRegister(abc.ABCMeta):
|
||||
return new_class
|
||||
|
||||
@staticmethod
|
||||
async def get_handler(ctx: "BizHawkClientContext", system: str) -> Optional[BizHawkClient]:
|
||||
async def get_handler(ctx: "BizHawkClientContext", system: str) -> BizHawkClient | None:
|
||||
for systems, handlers in AutoBizHawkClientRegister.game_handlers.items():
|
||||
if system in systems:
|
||||
for handler in handlers.values():
|
||||
@@ -65,13 +65,13 @@ class AutoBizHawkClientRegister(abc.ABCMeta):
|
||||
|
||||
|
||||
class BizHawkClient(abc.ABC, metaclass=AutoBizHawkClientRegister):
|
||||
system: ClassVar[Union[str, Tuple[str, ...]]]
|
||||
system: ClassVar[str | tuple[str, ...]]
|
||||
"""The system(s) that the game this client is for runs on"""
|
||||
|
||||
game: ClassVar[str]
|
||||
"""The game this client is for"""
|
||||
|
||||
patch_suffix: ClassVar[Optional[Union[str, Tuple[str, ...]]]]
|
||||
patch_suffix: ClassVar[str | tuple[str, ...] | None]
|
||||
"""The file extension(s) this client is meant to open and patch (e.g. ".apz3")"""
|
||||
|
||||
@abc.abstractmethod
|
||||
|
||||
@@ -6,7 +6,7 @@ checking or launching the client, otherwise it will probably cause circular impo
|
||||
import asyncio
|
||||
import enum
|
||||
import subprocess
|
||||
from typing import Any, Dict, Optional
|
||||
from typing import Any
|
||||
|
||||
from CommonClient import CommonContext, ClientCommandProcessor, get_base_parser, server_loop, logger, gui_enabled
|
||||
import Patch
|
||||
@@ -43,15 +43,15 @@ class BizHawkClientContext(CommonContext):
|
||||
command_processor = BizHawkClientCommandProcessor
|
||||
auth_status: AuthStatus
|
||||
password_requested: bool
|
||||
client_handler: Optional[BizHawkClient]
|
||||
slot_data: Optional[Dict[str, Any]] = None
|
||||
rom_hash: Optional[str] = None
|
||||
client_handler: BizHawkClient | None
|
||||
slot_data: dict[str, Any] | None = None
|
||||
rom_hash: str | None = None
|
||||
bizhawk_ctx: BizHawkContext
|
||||
|
||||
watcher_timeout: float
|
||||
"""The maximum amount of time the game watcher loop will wait for an update from the server before executing"""
|
||||
|
||||
def __init__(self, server_address: Optional[str], password: Optional[str]):
|
||||
def __init__(self, server_address: str | None, password: str | None):
|
||||
super().__init__(server_address, password)
|
||||
self.auth_status = AuthStatus.NOT_AUTHENTICATED
|
||||
self.password_requested = False
|
||||
@@ -231,20 +231,27 @@ async def _run_game(rom: str):
|
||||
)
|
||||
|
||||
|
||||
async def _patch_and_run_game(patch_file: str):
|
||||
def _patch_and_run_game(patch_file: str):
|
||||
try:
|
||||
metadata, output_file = Patch.create_rom_file(patch_file)
|
||||
Utils.async_start(_run_game(output_file))
|
||||
return metadata
|
||||
except Exception as exc:
|
||||
logger.exception(exc)
|
||||
return {}
|
||||
|
||||
|
||||
def launch(*launch_args) -> None:
|
||||
def launch(*launch_args: str) -> None:
|
||||
async def main():
|
||||
parser = get_base_parser()
|
||||
parser.add_argument("patch_file", default="", type=str, nargs="?", help="Path to an Archipelago patch file")
|
||||
args = parser.parse_args(launch_args)
|
||||
|
||||
if args.patch_file != "":
|
||||
metadata = _patch_and_run_game(args.patch_file)
|
||||
if "server" in metadata:
|
||||
args.connect = metadata["server"]
|
||||
|
||||
ctx = BizHawkClientContext(args.connect, args.password)
|
||||
ctx.server_task = asyncio.create_task(server_loop(ctx), name="ServerLoop")
|
||||
|
||||
@@ -252,9 +259,6 @@ def launch(*launch_args) -> None:
|
||||
ctx.run_gui()
|
||||
ctx.run_cli()
|
||||
|
||||
if args.patch_file != "":
|
||||
Utils.async_start(_patch_and_run_game(args.patch_file))
|
||||
|
||||
watcher_task = asyncio.create_task(_game_watcher(ctx), name="GameWatcher")
|
||||
|
||||
try:
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Dict
|
||||
|
||||
from dataclasses import dataclass
|
||||
from Options import Choice, Option, DefaultOnToggle, DeathLink, Range, Toggle, PerGameCommonOptions
|
||||
|
||||
from Options import Choice, DefaultOnToggle, DeathLink, Range, Toggle, PerGameCommonOptions
|
||||
|
||||
|
||||
class FreeincarnateMax(Range):
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from BaseClasses import MultiWorld, Region, Entrance, LocationProgressType
|
||||
from Options import PerGameCommonOptions
|
||||
from .Locations import location_table, LocationData, AdventureLocation, dragon_room_to_region
|
||||
from .Locations import location_table, AdventureLocation, dragon_room_to_region
|
||||
|
||||
|
||||
def connect(world: MultiWorld, player: int, source: str, target: str, rule: callable = lambda state: True,
|
||||
|
||||
@@ -2,15 +2,15 @@ import hashlib
|
||||
import json
|
||||
import os
|
||||
import zipfile
|
||||
from typing import Optional, Any
|
||||
|
||||
import Utils
|
||||
from .Locations import AdventureLocation, LocationData
|
||||
from settings import get_settings
|
||||
from worlds.Files import APPatch, AutoPatchRegister
|
||||
from typing import Any
|
||||
|
||||
import bsdiff4
|
||||
|
||||
import Utils
|
||||
from settings import get_settings
|
||||
from worlds.Files import APPatch, AutoPatchRegister
|
||||
from .Locations import LocationData
|
||||
|
||||
ADVENTUREHASH: str = "157bddb7192754a45372be196797f284"
|
||||
|
||||
|
||||
|
||||
@@ -1,35 +1,24 @@
|
||||
import base64
|
||||
import copy
|
||||
import itertools
|
||||
import math
|
||||
import os
|
||||
import settings
|
||||
import typing
|
||||
from enum import IntFlag
|
||||
from typing import Any, ClassVar, Dict, List, Optional, Set, Tuple
|
||||
from typing import ClassVar, Dict, Optional, Tuple
|
||||
|
||||
from BaseClasses import Entrance, Item, ItemClassification, MultiWorld, Region, Tutorial, \
|
||||
LocationProgressType
|
||||
import settings
|
||||
from BaseClasses import Item, ItemClassification, MultiWorld, Tutorial, LocationProgressType
|
||||
from Utils import __version__
|
||||
from Options import AssembleOptions
|
||||
from worlds.AutoWorld import WebWorld, World
|
||||
from Fill import fill_restrictive
|
||||
from worlds.generic.Rules import add_rule, set_rule
|
||||
from .Options import DragonRandoType, DifficultySwitchA, DifficultySwitchB, \
|
||||
AdventureOptions
|
||||
from .Rom import get_base_rom_bytes, get_base_rom_path, AdventureDeltaPatch, apply_basepatch, \
|
||||
AdventureAutoCollectLocation
|
||||
from worlds.LauncherComponents import Component, components, SuffixIdentifier
|
||||
from .Items import item_table, ItemData, nothing_item_id, event_table, AdventureItem, standard_item_max
|
||||
from .Locations import location_table, base_location_id, LocationData, get_random_room_in_regions
|
||||
from .Offsets import static_item_data_location, items_ram_start, static_item_element_size, item_position_table, \
|
||||
static_first_dragon_index, connector_port_offset, yorgle_speed_data_location, grundle_speed_data_location, \
|
||||
rhindle_speed_data_location, item_ram_addresses, start_castle_values, start_castle_offset
|
||||
from .Options import DragonRandoType, DifficultySwitchA, DifficultySwitchB, AdventureOptions
|
||||
from .Regions import create_regions
|
||||
from .Rom import get_base_rom_bytes, get_base_rom_path, AdventureDeltaPatch, apply_basepatch, AdventureAutoCollectLocation
|
||||
from .Rules import set_rules
|
||||
|
||||
|
||||
from worlds.LauncherComponents import Component, components, SuffixIdentifier
|
||||
|
||||
# Adventure
|
||||
components.append(Component('Adventure Client', 'AdventureClient', file_identifier=SuffixIdentifier('.apadvn')))
|
||||
|
||||
|
||||
@@ -141,9 +141,12 @@ def set_dw_rules(world: "HatInTimeWorld"):
|
||||
add_dw_rules(world, all_clear)
|
||||
add_rule(main_stamp, main_objective.access_rule)
|
||||
add_rule(all_clear, main_objective.access_rule)
|
||||
# Only set bonus stamp rules if we don't auto complete bonuses
|
||||
# Only set bonus stamp rules to require All Clear if we don't auto complete bonuses
|
||||
if not world.options.DWAutoCompleteBonuses and not world.is_bonus_excluded(all_clear.name):
|
||||
add_rule(bonus_stamps, all_clear.access_rule)
|
||||
else:
|
||||
# As soon as the Main Objective is completed, the bonuses auto-complete.
|
||||
add_rule(bonus_stamps, main_objective.access_rule)
|
||||
|
||||
if world.options.DWShuffle:
|
||||
for i in range(len(world.dw_shuffle)-1):
|
||||
@@ -343,6 +346,7 @@ def create_enemy_events(world: "HatInTimeWorld"):
|
||||
|
||||
def set_enemy_rules(world: "HatInTimeWorld"):
|
||||
no_tourist = "Camera Tourist" in world.excluded_dws or "Camera Tourist" in world.excluded_bonuses
|
||||
difficulty = get_difficulty(world)
|
||||
|
||||
for enemy, regions in hit_list.items():
|
||||
if no_tourist and enemy in bosses:
|
||||
@@ -372,6 +376,14 @@ def set_enemy_rules(world: "HatInTimeWorld"):
|
||||
or state.has("Zipline Unlock - The Lava Cake Path", world.player)
|
||||
or state.has("Zipline Unlock - The Windmill Path", world.player))
|
||||
|
||||
elif enemy == "Toilet":
|
||||
if area == "Toilet of Doom":
|
||||
# The boss firewall is in the way and can only be skipped on Expert logic using a cherry hover.
|
||||
add_rule(event, lambda state: has_paintings(state, world, 1, allow_skip=difficulty == Difficulty.EXPERT))
|
||||
if difficulty < Difficulty.HARD:
|
||||
# Hard logic and above can cross the boss arena gap with a cherry bridge.
|
||||
add_rule(event, lambda state: can_use_hookshot(state, world))
|
||||
|
||||
elif enemy == "Director":
|
||||
if area == "Dead Bird Studio Basement":
|
||||
add_rule(event, lambda state: can_use_hookshot(state, world))
|
||||
@@ -430,7 +442,7 @@ hit_list = {
|
||||
# Bosses
|
||||
"Mafia Boss": ["Down with the Mafia!", "Encore! Encore!", "Boss Rush"],
|
||||
|
||||
"Conductor": ["Dead Bird Studio Basement", "Killing Two Birds", "Boss Rush"],
|
||||
"Director": ["Dead Bird Studio Basement", "Killing Two Birds", "Boss Rush"],
|
||||
"Toilet": ["Toilet of Doom", "Boss Rush"],
|
||||
|
||||
"Snatcher": ["Your Contract has Expired", "Breaching the Contract", "Boss Rush",
|
||||
@@ -454,7 +466,7 @@ triple_enemy_locations = [
|
||||
|
||||
bosses = [
|
||||
"Mafia Boss",
|
||||
"Conductor",
|
||||
"Director",
|
||||
"Toilet",
|
||||
"Snatcher",
|
||||
"Toxic Flower",
|
||||
|
||||
@@ -264,7 +264,6 @@ ahit_locations = {
|
||||
required_hats=[HatType.DWELLER], paintings=3),
|
||||
|
||||
"Subcon Forest - Tall Tree Hookshot Swing": LocData(2000324766, "Subcon Forest Area",
|
||||
required_hats=[HatType.DWELLER],
|
||||
hookshot=True,
|
||||
paintings=3),
|
||||
|
||||
@@ -323,7 +322,7 @@ ahit_locations = {
|
||||
"Alpine Skyline - The Twilight Path": LocData(2000334434, "Alpine Skyline Area", required_hats=[HatType.DWELLER]),
|
||||
"Alpine Skyline - The Twilight Bell: Wide Purple Platform": LocData(2000336478, "The Twilight Bell"),
|
||||
"Alpine Skyline - The Twilight Bell: Ice Platform": LocData(2000335826, "The Twilight Bell"),
|
||||
"Alpine Skyline - Goat Outpost Horn": LocData(2000334760, "Alpine Skyline Area"),
|
||||
"Alpine Skyline - Goat Outpost Horn": LocData(2000334760, "Alpine Skyline Area (TIHS)", hookshot=True),
|
||||
"Alpine Skyline - Windy Passage": LocData(2000334776, "Alpine Skyline Area (TIHS)", hookshot=True),
|
||||
"Alpine Skyline - The Windmill: Inside Pon Cluster": LocData(2000336395, "The Windmill"),
|
||||
"Alpine Skyline - The Windmill: Entrance": LocData(2000335783, "The Windmill"),
|
||||
@@ -407,7 +406,7 @@ act_completions = {
|
||||
hit_type=HitType.umbrella_or_brewing, hookshot=True, paintings=1),
|
||||
|
||||
"Act Completion (Queen Vanessa's Manor)": LocData(2000312017, "Queen Vanessa's Manor",
|
||||
hit_type=HitType.umbrella, paintings=1),
|
||||
hit_type=HitType.dweller_bell, paintings=1),
|
||||
|
||||
"Act Completion (Mail Delivery Service)": LocData(2000312032, "Mail Delivery Service",
|
||||
required_hats=[HatType.SPRINT]),
|
||||
@@ -878,7 +877,7 @@ snatcher_coins = {
|
||||
dlc_flags=HatDLC.death_wish),
|
||||
|
||||
"Snatcher Coin - Top of HQ (DW: BTH)": LocData(0, "Beat the Heat", snatcher_coin="Snatcher Coin - Top of HQ",
|
||||
dlc_flags=HatDLC.death_wish),
|
||||
hit_type=HitType.umbrella, dlc_flags=HatDLC.death_wish),
|
||||
|
||||
"Snatcher Coin - Top of Tower": LocData(0, "Mafia Town Area (HUMT)", snatcher_coin="Snatcher Coin - Top of Tower",
|
||||
dlc_flags=HatDLC.death_wish),
|
||||
|
||||
@@ -414,7 +414,7 @@ def set_moderate_rules(world: "HatInTimeWorld"):
|
||||
|
||||
# Moderate: Mystifying Time Mesa time trial without hats
|
||||
set_rule(world.multiworld.get_location("Alpine Skyline - Mystifying Time Mesa: Zipline", world.player),
|
||||
lambda state: can_use_hookshot(state, world))
|
||||
lambda state: True)
|
||||
|
||||
# Moderate: Goat Refinery from TIHS with Sprint only
|
||||
add_rule(world.multiworld.get_location("Alpine Skyline - Goat Refinery", world.player),
|
||||
@@ -493,9 +493,6 @@ def set_hard_rules(world: "HatInTimeWorld"):
|
||||
lambda state: has_paintings(state, world, 3, True))
|
||||
|
||||
# SDJ
|
||||
add_rule(world.multiworld.get_location("Subcon Forest - Long Tree Climb Chest", world.player),
|
||||
lambda state: can_use_hat(state, world, HatType.SPRINT) and has_paintings(state, world, 2), "or")
|
||||
|
||||
add_rule(world.multiworld.get_location("Act Completion (Time Rift - Curly Tail Trail)", world.player),
|
||||
lambda state: can_use_hat(state, world, HatType.SPRINT), "or")
|
||||
|
||||
@@ -533,7 +530,10 @@ def set_expert_rules(world: "HatInTimeWorld"):
|
||||
# Expert: Mafia Town - Above Boats, Top of Lighthouse, and Hot Air Balloon with nothing
|
||||
set_rule(world.multiworld.get_location("Mafia Town - Above Boats", world.player), lambda state: True)
|
||||
set_rule(world.multiworld.get_location("Mafia Town - Top of Lighthouse", world.player), lambda state: True)
|
||||
set_rule(world.multiworld.get_location("Mafia Town - Hot Air Balloon", world.player), lambda state: True)
|
||||
# There are not enough buckets/beach balls to bucket/ball hover in Heating Up Mafia Town, so any other Mafia Town
|
||||
# act is required.
|
||||
add_rule(world.multiworld.get_location("Mafia Town - Hot Air Balloon", world.player),
|
||||
lambda state: state.can_reach_region("Mafia Town Area", world.player), "or")
|
||||
|
||||
# Expert: Clear Dead Bird Studio with nothing
|
||||
for loc in world.multiworld.get_region("Dead Bird Studio - Post Elevator Area", world.player).locations:
|
||||
@@ -590,7 +590,7 @@ def set_expert_rules(world: "HatInTimeWorld"):
|
||||
|
||||
if world.is_dlc2():
|
||||
# Expert: clear Rush Hour with nothing
|
||||
if not world.options.NoTicketSkips:
|
||||
if world.options.NoTicketSkips != NoTicketSkips.option_true:
|
||||
set_rule(world.multiworld.get_location("Act Completion (Rush Hour)", world.player), lambda state: True)
|
||||
else:
|
||||
set_rule(world.multiworld.get_location("Act Completion (Rush Hour)", world.player),
|
||||
@@ -739,7 +739,7 @@ def set_dlc1_rules(world: "HatInTimeWorld"):
|
||||
|
||||
# This particular item isn't present in Act 3 for some reason, yes in vanilla too
|
||||
add_rule(world.multiworld.get_location("The Arctic Cruise - Toilet", world.player),
|
||||
lambda state: state.can_reach("Bon Voyage!", "Region", world.player)
|
||||
lambda state: (state.can_reach("Bon Voyage!", "Region", world.player) and can_use_hookshot(state, world))
|
||||
or state.can_reach("Ship Shape", "Region", world.player))
|
||||
|
||||
|
||||
|
||||
@@ -119,7 +119,9 @@ def KholdstareDefeatRule(state, player: int) -> bool:
|
||||
|
||||
|
||||
def VitreousDefeatRule(state, player: int) -> bool:
|
||||
return can_shoot_arrows(state, player) or has_melee_weapon(state, player)
|
||||
return ((can_shoot_arrows(state, player) and can_use_bombs(state, player, 10))
|
||||
or can_shoot_arrows(state, player, 35) or state.has("Silver Bow", player)
|
||||
or has_melee_weapon(state, player))
|
||||
|
||||
|
||||
def TrinexxDefeatRule(state, player: int) -> bool:
|
||||
|
||||
@@ -464,7 +464,7 @@ async def track_locations(ctx, roomid, roomdata) -> bool:
|
||||
snes_logger.info(f"Discarding recent {len(new_locations)} checks as ROM Status has changed.")
|
||||
return False
|
||||
else:
|
||||
await ctx.send_msgs([{"cmd": 'LocationChecks', "locations": new_locations}])
|
||||
await ctx.check_locations(new_locations)
|
||||
await snes_flush_writes(ctx)
|
||||
return True
|
||||
|
||||
|
||||
@@ -484,8 +484,7 @@ def generate_itempool(world):
|
||||
if multiworld.randomize_cost_types[player]:
|
||||
# Heart and Arrow costs require all Heart Container/Pieces and Arrow Upgrades to be advancement items for logic
|
||||
for item in items:
|
||||
if (item.name in ("Boss Heart Container", "Sanctuary Heart Container", "Piece of Heart")
|
||||
or "Arrow Upgrade" in item.name):
|
||||
if item.name in ("Boss Heart Container", "Sanctuary Heart Container", "Piece of Heart"):
|
||||
item.classification = ItemClassification.progression
|
||||
else:
|
||||
# Otherwise, logic has some branches where having 4 hearts is one possible requirement (of several alternatives)
|
||||
@@ -713,7 +712,7 @@ def get_pool_core(world, player: int):
|
||||
pool.remove("Rupees (20)")
|
||||
|
||||
if retro_bow:
|
||||
replace = {'Single Arrow', 'Arrows (10)', 'Arrow Upgrade (+5)', 'Arrow Upgrade (+10)', 'Arrow Upgrade (50)'}
|
||||
replace = {'Single Arrow', 'Arrows (10)', 'Arrow Upgrade (+5)', 'Arrow Upgrade (+10)', 'Arrow Upgrade (70)'}
|
||||
pool = ['Rupees (5)' if item in replace else item for item in pool]
|
||||
if world.small_key_shuffle[player] == small_key_shuffle.option_universal:
|
||||
pool.extend(diff.universal_keys)
|
||||
|
||||
@@ -7,7 +7,7 @@ from worlds.AutoWorld import World
|
||||
def GetBeemizerItem(world, player: int, item):
|
||||
item_name = item if isinstance(item, str) else item.name
|
||||
|
||||
if item_name not in trap_replaceable:
|
||||
if item_name not in trap_replaceable or player in world.groups:
|
||||
return item
|
||||
|
||||
# first roll - replaceable item should be replaced, within beemizer_total_chance
|
||||
@@ -110,9 +110,9 @@ item_table = {'Bow': ItemData(IC.progression, None, 0x0B, 'You have\nchosen the\
|
||||
'Crystal 7': ItemData(IC.progression, 'Crystal', (0x08, 0x34, 0x64, 0x40, 0x7C, 0x06), None, None, None, None, None, None, "a blue crystal"),
|
||||
'Single Arrow': ItemData(IC.filler, None, 0x43, 'a lonely arrow\nsits here.', 'and the arrow', 'stick-collecting kid', 'sewing needle for sale', 'fungus for arrow', 'archer boy sews again', 'an arrow'),
|
||||
'Arrows (10)': ItemData(IC.filler, None, 0x44, 'This will give\nyou ten shots\nwith your bow!', 'and the arrow pack','stick-collecting kid', 'sewing kit for sale', 'fungus for arrows', 'archer boy sews again','ten arrows'),
|
||||
'Arrow Upgrade (+10)': ItemData(IC.useful, None, 0x54, 'increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'),
|
||||
'Arrow Upgrade (+5)': ItemData(IC.useful, None, 0x53, 'increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'),
|
||||
'Arrow Upgrade (70)': ItemData(IC.useful, None, 0x4D, 'increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'),
|
||||
'Arrow Upgrade (+10)': ItemData(IC.progression_skip_balancing, None, 0x54, 'increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'),
|
||||
'Arrow Upgrade (+5)': ItemData(IC.progression_skip_balancing, None, 0x53, 'increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'),
|
||||
'Arrow Upgrade (70)': ItemData(IC.progression_skip_balancing, None, 0x4D, 'increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'),
|
||||
'Single Bomb': ItemData(IC.filler, None, 0x27, 'I make things\ngo BOOM! But\njust once.', 'and the explosion', 'the bomb-holding kid', 'firecracker for sale', 'blend fungus into bomb', '\'splosion boy explodes again', 'a bomb'),
|
||||
'Bombs (3)': ItemData(IC.filler, None, 0x28, 'I make things\ngo triple\nBOOM!!!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'three bombs'),
|
||||
'Bombs (10)': ItemData(IC.filler, None, 0x31, 'I make things\ngo BOOM! Ten\ntimes!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'ten bombs'),
|
||||
|
||||
@@ -170,7 +170,8 @@ def push_shop_inventories(multiworld):
|
||||
# Retro Bow arrows will already have been pushed
|
||||
if (not multiworld.retro_bow[location.player]) or ((item_name, location.item.player)
|
||||
!= ("Single Arrow", location.player)):
|
||||
location.shop.push_inventory(location.shop_slot, item_name, location.shop_price,
|
||||
location.shop.push_inventory(location.shop_slot, item_name,
|
||||
round(location.shop_price * get_price_modifier(location.item)),
|
||||
1, location.item.player if location.item.player != location.player else 0,
|
||||
location.shop_price_type)
|
||||
location.shop_price = location.shop.inventory[location.shop_slot]["price"] = min(location.shop_price,
|
||||
|
||||
@@ -15,18 +15,18 @@ def can_bomb_clip(state: CollectionState, region: LTTPRegion, player: int) -> bo
|
||||
|
||||
def can_buy_unlimited(state: CollectionState, item: str, player: int) -> bool:
|
||||
return any(shop.region.player == player and shop.has_unlimited(item) and shop.region.can_reach(state) for
|
||||
shop in state.multiworld.shops)
|
||||
shop in state.multiworld.shops)
|
||||
|
||||
|
||||
def can_buy(state: CollectionState, item: str, player: int) -> bool:
|
||||
return any(shop.region.player == player and shop.has(item) and shop.region.can_reach(state) for
|
||||
shop in state.multiworld.shops)
|
||||
shop in state.multiworld.shops)
|
||||
|
||||
|
||||
def can_shoot_arrows(state: CollectionState, player: int) -> bool:
|
||||
def can_shoot_arrows(state: CollectionState, player: int, count: int = 0) -> bool:
|
||||
if state.multiworld.retro_bow[player]:
|
||||
return (state.has('Bow', player) or state.has('Silver Bow', player)) and can_buy(state, 'Single Arrow', player)
|
||||
return state.has('Bow', player) or state.has('Silver Bow', player)
|
||||
return (state.has('Bow', player) or state.has('Silver Bow', player)) and can_hold_arrows(state, player, count)
|
||||
|
||||
|
||||
def has_triforce_pieces(state: CollectionState, player: int) -> bool:
|
||||
@@ -61,13 +61,13 @@ def heart_count(state: CollectionState, player: int) -> int:
|
||||
# Warning: This only considers items that are marked as advancement items
|
||||
diff = state.multiworld.worlds[player].difficulty_requirements
|
||||
return min(state.count('Boss Heart Container', player), diff.boss_heart_container_limit) \
|
||||
+ state.count('Sanctuary Heart Container', player) \
|
||||
+ state.count('Sanctuary Heart Container', player) \
|
||||
+ min(state.count('Piece of Heart', player), diff.heart_piece_limit) // 4 \
|
||||
+ 3 # starting hearts
|
||||
+ 3 # starting hearts
|
||||
|
||||
|
||||
def can_extend_magic(state: CollectionState, player: int, smallmagic: int = 16,
|
||||
fullrefill: bool = False): # This reflects the total magic Link has, not the total extra he has.
|
||||
fullrefill: bool = False): # This reflects the total magic Link has, not the total extra he has.
|
||||
basemagic = 8
|
||||
if state.has('Magic Upgrade (1/4)', player):
|
||||
basemagic = 32
|
||||
@@ -84,11 +84,18 @@ def can_extend_magic(state: CollectionState, player: int, smallmagic: int = 16,
|
||||
|
||||
|
||||
def can_hold_arrows(state: CollectionState, player: int, quantity: int):
|
||||
arrows = 30 + ((state.count("Arrow Upgrade (+5)", player) * 5) + (state.count("Arrow Upgrade (+10)", player) * 10)
|
||||
+ (state.count("Bomb Upgrade (50)", player) * 50))
|
||||
# Arrow Upgrade (+5) beyond the 6th gives +10
|
||||
arrows += max(0, ((state.count("Arrow Upgrade (+5)", player) - 6) * 10))
|
||||
return min(70, arrows) >= quantity
|
||||
if state.multiworld.worlds[player].options.shuffle_capacity_upgrades:
|
||||
if quantity == 0:
|
||||
return True
|
||||
if state.has("Arrow Upgrade (70)", player):
|
||||
arrows = 70
|
||||
else:
|
||||
arrows = (30 + (state.count("Arrow Upgrade (+5)", player) * 5)
|
||||
+ (state.count("Arrow Upgrade (+10)", player) * 10))
|
||||
# Arrow Upgrade (+5) beyond the 6th gives +10
|
||||
arrows += max(0, ((state.count("Arrow Upgrade (+5)", player) - 6) * 10))
|
||||
return min(70, arrows) >= quantity
|
||||
return quantity <= 30 or state.has("Capacity Upgrade Shop", player)
|
||||
|
||||
|
||||
def can_use_bombs(state: CollectionState, player: int, quantity: int = 1) -> bool:
|
||||
@@ -146,19 +153,19 @@ def can_get_good_bee(state: CollectionState, player: int) -> bool:
|
||||
def can_retrieve_tablet(state: CollectionState, player: int) -> bool:
|
||||
return state.has('Book of Mudora', player) and (has_beam_sword(state, player) or
|
||||
(state.multiworld.swordless[player] and
|
||||
state.has("Hammer", player)))
|
||||
state.has("Hammer", player)))
|
||||
|
||||
|
||||
def has_sword(state: CollectionState, player: int) -> bool:
|
||||
return state.has('Fighter Sword', player) \
|
||||
or state.has('Master Sword', player) \
|
||||
or state.has('Tempered Sword', player) \
|
||||
or state.has('Golden Sword', player)
|
||||
or state.has('Master Sword', player) \
|
||||
or state.has('Tempered Sword', player) \
|
||||
or state.has('Golden Sword', player)
|
||||
|
||||
|
||||
def has_beam_sword(state: CollectionState, player: int) -> bool:
|
||||
return state.has('Master Sword', player) or state.has('Tempered Sword', player) or state.has('Golden Sword',
|
||||
player)
|
||||
player)
|
||||
|
||||
|
||||
def has_melee_weapon(state: CollectionState, player: int) -> bool:
|
||||
@@ -171,9 +178,9 @@ def has_fire_source(state: CollectionState, player: int) -> bool:
|
||||
|
||||
def can_melt_things(state: CollectionState, player: int) -> bool:
|
||||
return state.has('Fire Rod', player) or \
|
||||
(state.has('Bombos', player) and
|
||||
(state.multiworld.swordless[player] or
|
||||
has_sword(state, player)))
|
||||
(state.has('Bombos', player) and
|
||||
(state.multiworld.swordless[player] or
|
||||
has_sword(state, player)))
|
||||
|
||||
|
||||
def has_misery_mire_medallion(state: CollectionState, player: int) -> bool:
|
||||
|
||||
@@ -1,224 +1,123 @@
|
||||
# Guía de instalación para A Link to the Past Randomizer Multiworld
|
||||
|
||||
<div id="tutorial-video-container">
|
||||
<iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/mJKEHaiyR_Y" frameborder="0"
|
||||
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen>
|
||||
</iframe>
|
||||
</div>
|
||||
|
||||
## Software requerido
|
||||
|
||||
- [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases)
|
||||
- [QUsb2Snes](https://github.com/Skarsnik/QUsb2snes/releases) (Incluido en Multiworld Utilities)
|
||||
- Hardware o software capaz de cargar y ejecutar archivos de ROM de SNES
|
||||
- Un emulador capaz de ejecutar scripts Lua
|
||||
([snes9x rr](https://github.com/gocha/snes9x-rr/releases),
|
||||
- [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases).
|
||||
- [SNI](https://github.com/alttpo/sni/releases). Esto está incluido automáticamente en la instalación de Archipelago.
|
||||
- SNI no es compatible con (Q)Usb2Snes.
|
||||
- Hardware o software capaz de cargar y ejecutar archivos de ROM de SNES, por ejemplo:
|
||||
- Un emulador capaz de conectarse a SNI
|
||||
([snes9x-nwa](https://github.com/Skarsnik/snes9x-emunwa/releases), [snes9x-rr](https://github.com/gocha/snes9x-rr/releases),
|
||||
[BSNES-plus](https://github.com/black-sliver/bsnes-plus),
|
||||
[BizHawk](https://tasvideos.org/BizHawk), o
|
||||
[RetroArch](https://retroarch.com?page=platforms) 1.10.1 o más nuevo). O,
|
||||
- Un flashcart SD2SNES, [FXPak Pro](https://krikzz.com/store/home/54-fxpak-pro.html), o otro hardware compatible
|
||||
[RetroArch](https://retroarch.com?page=platforms) 1.10.1 o más nuevo).
|
||||
- Un SD2SNES, [FXPak Pro](https://krikzz.com/store/home/54-fxpak-pro.html), u otro hardware compatible. **nota:
|
||||
Las SNES minis modificadas no tienen soporte de SNI. Algunos usuarios dicen haber tenido éxito con Qusb2Snes para esta consola,
|
||||
pero no tiene soporte.**
|
||||
- Tu archivo ROM japones v1.0, probablemente se llame `Zelda no Densetsu - Kamigami no Triforce (Japan).sfc`
|
||||
|
||||
## Procedimiento de instalación
|
||||
|
||||
### Instalación en Windows
|
||||
|
||||
1. Descarga e instala MultiWorld Utilities desde el enlace anterior, asegurando que instalamos la versión más reciente.
|
||||
**El archivo esta localizado en la sección "assets" en la parte inferior de la información de versión**. Si tu
|
||||
intención es jugar la versión normal de multiworld, necesitarás el archivo `Setup.Archipelago.exe`
|
||||
- Si estas interesado en jugar la variante que aleatoriza las puertas internas de las mazmorras, necesitaras bajar '
|
||||
Setup.BerserkerMultiWorld.Doors.exe'
|
||||
- Durante el proceso de instalación, se te pedirá donde esta situado tu archivo ROM japonés v1.0. Si ya habías
|
||||
instalado este software con anterioridad y simplemente estas actualizando, no se te pedirá la localización del
|
||||
archivo una segunda vez.
|
||||
- Puede ser que el programa pida la instalación de Microsoft Visual C++. Si ya lo tienes en tu ordenador (
|
||||
posiblemente por que un juego de Steam ya lo haya instalado), el instalador no te pedirá su instalación.
|
||||
|
||||
2. Si estas usando un emulador, deberías asignar la versión capaz de ejecutar scripts Lua como programa por defecto para
|
||||
lanzar ficheros de ROM de SNES.
|
||||
1. Extrae tu emulador al escritorio, o cualquier sitio que después recuerdes.
|
||||
2. Haz click derecho en un fichero de ROM (ha de tener la extensión sfc) y selecciona **Abrir con...**
|
||||
3. Marca la opción **Usar siempre esta aplicación para abrir los archivos .sfc**
|
||||
4. Baja hasta el final de la lista y haz click en la opción **Buscar otra aplicación en el equipo** (Si usas Windows
|
||||
10 es posible que debas hacer click en **Más aplicaciones**)
|
||||
5. Busca el archivo .exe de tu emulador y haz click en **Abrir**. Este archivo debe estar en el directorio donde
|
||||
extrajiste en el paso 1.
|
||||
|
||||
### Instalación en Macintosh
|
||||
|
||||
- ¡Necesitamos voluntarios para rellenar esta seccion! Contactad con **Farrak Kilhn** (en inglés) en Discord si queréis
|
||||
ayudar.
|
||||
|
||||
## Configurar tu archivo YAML
|
||||
|
||||
### Que es un archivo YAML y por qué necesito uno?
|
||||
|
||||
Tu archivo YAML contiene un conjunto de opciones de configuración que proveen al generador con información sobre como
|
||||
debe generar tu juego. Cada jugador en una partida de multiworld proveerá su propio fichero YAML. Esta configuración
|
||||
permite que cada jugador disfrute de una experiencia personalizada a su gusto, y cada jugador dentro de la misma partida
|
||||
de multiworld puede tener diferentes opciones.
|
||||
|
||||
### Donde puedo obtener un fichero YAML?
|
||||
|
||||
La página "[Generate Game](/games/A%20Link%20to%20the%20Past/player-options)" en el sitio web te permite configurar tu
|
||||
configuración personal y descargar un fichero "YAML".
|
||||
|
||||
### Configuración YAML avanzada
|
||||
|
||||
Una version mas avanzada del fichero Yaml puede ser creada usando la pagina
|
||||
["Weighted settings"](/games/A Link to the Past/weighted-options),
|
||||
la cual te permite tener almacenadas hasta 3 preajustes. La pagina "Weighted Settings" tiene muchas opciones
|
||||
representadas con controles deslizantes. Esto permite elegir cuan probable los valores de una categoría pueden ser
|
||||
elegidos sobre otros de la misma.
|
||||
|
||||
Por ejemplo, imagina que el generador crea un cubo llamado "map_shuffle", y pone trozos de papel doblado en él por cada
|
||||
sub-opción. Ademas imaginemos que tu valor elegido para "on" es 20 y el elegido para "off" es 40.
|
||||
|
||||
Por tanto, en este ejemplo, habrán 60 trozos de papel. 20 para "on" y 40 para "off". Cuando el generador esta decidiendo
|
||||
si activar o no "map shuffle" para tu partida, meterá la mano en el cubo y sacara un trozo de papel al azar. En este
|
||||
ejemplo, es mucho mas probable (2 de cada 3 veces (40/60)) que "map shuffle" esté desactivado.
|
||||
|
||||
Si quieres que una opción no pueda ser escogida, simplemente asigna el valor 0 a dicha opción. Recuerda que cada opción
|
||||
debe tener al menos un valor mayor que cero, si no la generación fallará.
|
||||
|
||||
### Verificando tu archivo YAML
|
||||
|
||||
Si quieres validar que tu fichero YAML para asegurarte que funciona correctamente, puedes hacerlo en la pagina
|
||||
[YAML Validator](/check).
|
||||
|
||||
## Generar una partida para un jugador
|
||||
|
||||
1. Navega a [la pagina Generate game](/games/A%20Link%20to%20the%20Past/player-options), configura tus opciones, haz
|
||||
click en el boton "Generate game".
|
||||
2. Se te redigirá a una pagina "Seed Info", donde puedes descargar tu archivo de parche.
|
||||
3. Haz doble click en tu fichero de parche, y el emulador debería ejecutar tu juego automáticamente. Como el Cliente no
|
||||
es necesario para partidas de un jugador, puedes cerrarlo junto a la pagina web (que tiene como titulo "Multiworld
|
||||
WebUI") que se ha abierto automáticamente.
|
||||
|
||||
## Unirse a una partida MultiWorld
|
||||
1. Descarga e instala [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases/latest).
|
||||
**El archivo del instalador se encuentra en la sección de assets al final de la información de version**.
|
||||
2. La primera vez que realices una generación local o parchees tu juego, se te pedirá que ubiques tu archivo ROM base.
|
||||
Este es tu archivo ROM de Link to the Past japonés. Esto sólo debe hacerse una vez.
|
||||
|
||||
4. Si estás usando un emulador, deberías de asignar tu emulador con compatibilidad con Lua como el programa por defecto para abrir archivos
|
||||
ROM.
|
||||
1. Extrae la carpeta de tu emulador al Escritorio, o algún otro sitio que vayas a recordar.
|
||||
2. Haz click derecho en un archivo ROM y selecciona **Abrir con...**
|
||||
3. Marca la casilla junto a **Usar siempre este programa para abrir archivos .sfc**
|
||||
4. Baja al final de la lista y haz click en el texto gris **Buscar otro programa en este PC**
|
||||
5. Busca el archivo `.exe` de tu emulador y haz click en **Abrir**. Este archivo debería de encontrarse dentro de la carpeta que
|
||||
extrajiste en el paso uno.
|
||||
|
||||
### Obtener el fichero de parche y crea tu ROM
|
||||
|
||||
Cuando te unes a una partida multiworld, debes proveer tu fichero YAML a quien sea el creador de la partida. Una vez
|
||||
Cuando te unas a una partida multiworld, se te pedirá enviarle tu archivo de configuración a quien quiera que esté creando. Una vez eso
|
||||
este hecho, el creador te devolverá un enlace para descargar el parche o un fichero zip conteniendo todos los ficheros
|
||||
de parche de la partida Tu fichero de parche debe tener la extensión `.aplttp`.
|
||||
de parche de la partida. Tu fichero de parche debe de tener la extensión `.aplttp`.
|
||||
|
||||
Pon tu fichero de parche en el escritorio o en algún sitio conveniente, y haz doble click. Esto debería ejecutar
|
||||
automáticamente el cliente, y ademas creara la rom en el mismo directorio donde este el fichero de parche.
|
||||
Pon tu fichero de parche en el escritorio o en algún sitio conveniente, y hazle doble click. Esto debería ejecutar
|
||||
automáticamente el cliente, y además creará la rom en el mismo directorio donde este el fichero de parche.
|
||||
|
||||
### Conectar al cliente
|
||||
|
||||
#### Con emulador
|
||||
|
||||
Cuando el cliente se lance automáticamente, QUsb2Snes debería haberse ejecutado también. Si es la primera vez que lo
|
||||
ejecutas, puedes ser que el firewall de Windows te pregunte si le permites la comunicación.
|
||||
Cuando el cliente se lance automáticamente, SNI debería de ejecutarse en segundo plano. Si es la
|
||||
primera vez que se ejecuta, tal vez se te pida permitir que se comunique a través del firewall de Windows
|
||||
|
||||
#### snes9x-nwa
|
||||
|
||||
1. Haz click en el menu Network y marca 'Enable Emu Network Control
|
||||
2. Carga tu archivo ROM si no lo habías hecho antes
|
||||
|
||||
##### snes9x-rr
|
||||
|
||||
1. Carga tu fichero de ROM, si no lo has hecho ya
|
||||
1. Carga tu fichero ROM, si no lo has hecho ya
|
||||
2. Abre el menu "File" y situa el raton en **Lua Scripting**
|
||||
3. Haz click en **New Lua Script Window...**
|
||||
4. En la nueva ventana, haz click en **Browse...**
|
||||
5. Navega hacia el directorio donde este situado snes9x-rr, entra en el directorio `lua`, y
|
||||
escoge `multibridge.lua`
|
||||
6. Observa que se ha asignado un nombre al dispositivo, y el cliente muestra "SNES Device: Connected", con el mismo
|
||||
nombre en la esquina superior izquierda.
|
||||
5. Selecciona el archivo lua conector incluido con tu cliente
|
||||
- Busca en la carpeta de Archipelago `/SNI/lua/`.
|
||||
6. Si ves un error mientras carga el script que dice `socket.dll missing` o algo similar, ve a la carpeta de
|
||||
el lua que estas usando en tu gestor de archivos y copia el `socket.dll` a la raíz de tu instalación de snes9x.
|
||||
|
||||
##### BNES-Plus
|
||||
|
||||
1. Cargue su archivo ROM si aún no se ha cargado.
|
||||
2. El emulador debería conectarse automáticamente mientras SNI se está ejecutando.
|
||||
|
||||
##### BizHawk
|
||||
|
||||
1. Asegurate que se ha cargado el nucleo BSNES. Debes hacer esto en el menu Tools y siguiento estas opciones:
|
||||
`Config --> Cores --> SNES --> BSNES`
|
||||
Una vez cambiado el nucleo cargado, BizHawk ha de ser reiniciado.
|
||||
1. Asegurate que se ha cargado el núcleo BSNES. Se hace en la barra de menú principal, bajo:
|
||||
- (≤ 2.8) `Config` 〉 `Cores` 〉 `SNES` 〉 `BSNES`
|
||||
- (≥ 2.9) `Config` 〉 `Preferred Cores` 〉 `SNES` 〉 `BSNESv115+`
|
||||
2. Carga tu fichero de ROM, si no lo has hecho ya.
|
||||
3. Haz click en el menu Tools y en la opción **Lua Console**
|
||||
4. Haz click en el botón para abrir un nuevo script Lua.
|
||||
5. Navega al directorio de instalación de MultiWorld Utilities, y en los siguiente directorios:
|
||||
`QUsb2Snes/Qusb2Snes/LuaBridge`
|
||||
6. Selecciona `luabridge.lua` y haz click en Abrir.
|
||||
7. Observa que se ha asignado un nombre al dispositivo, y el cliente muestra "SNES Device: Connected", con el mismo
|
||||
nombre en la esquina superior izquierda.
|
||||
Si has cambiado tu preferencia de núcleo tras haber cargado la ROM, no te olvides de volverlo a cargar (atajo por defecto: Ctrl+R).
|
||||
3. Arrastra el archivo `Connector.lua` que has descargado a la ventana principal de EmuHawk.
|
||||
- Busca en la carpeta de Archipelago `/SNI/lua/`.
|
||||
- También podrías abrir la consola de Lua manualmente, hacer click en `Script` 〉 `Open Script`, e ir a `Connector.lua`
|
||||
con el selector de archivos.
|
||||
|
||||
##### RetroArch 1.10.1 o más nuevo
|
||||
|
||||
Sólo hay que segiur estos pasos una vez.
|
||||
Sólo hay que seguir estos pasos una vez.
|
||||
|
||||
1. Comienza en la pantalla del menú principal de RetroArch.
|
||||
2. Ve a Ajustes --> Interfaz de usario. Configura "Mostrar ajustes avanzados" en ON.
|
||||
3. Ve a Ajustes --> Red. Configura "Comandos de red" en ON. (Se encuentra bajo Request Device 16.) Deja en 55355 (el
|
||||
default) el Puerto de comandos de red.
|
||||
3. Ve a Ajustes --> Red. Pon "Comandos de red" en ON. (Se encuentra bajo Request Device 16.) Deja en 55355 el valor por defecto,
|
||||
el Puerto de comandos de red.
|
||||
|
||||

|
||||
4. Ve a Menú principal --> Actualizador en línea --> Descargador de núcleos. Desplázate y selecciona "Nintendo - SNES /
|
||||
SFC (bsnes-mercury Performance)".
|
||||
|
||||
Cuando cargas un ROM, asegúrate de seleccionar un núcleo **bsnes-mercury**. Estos son los sólos núcleos que permiten
|
||||
Cuando cargas un ROM, asegúrate de seleccionar un núcleo **bsnes-mercury**. Estos son los únicos núcleos que permiten
|
||||
que herramientas externas lean datos del ROM.
|
||||
|
||||
#### Con Hardware
|
||||
|
||||
Esta guía asume que ya has descargado el firmware correcto para tu dispositivo. Si no lo has hecho ya, hazlo ahora. Los
|
||||
Esta guía asume que ya has descargado el firmware correcto para tu dispositivo. Si no lo has hecho ya, por favor hazlo ahora. Los
|
||||
usuarios de SD2SNES y FXPak Pro pueden descargar el firmware apropiado
|
||||
[aqui](https://github.com/RedGuyyyy/sd2snes/releases). Los usuarios de otros dispositivos pueden encontrar información
|
||||
[aqui](https://github.com/RedGuyyyy/sd2snes/releases). Puede que los usuarios de otros dispositivos encuentren informacion útil
|
||||
[en esta página](http://usb2snes.com/#supported-platforms).
|
||||
|
||||
1. Cierra tu emulador, el cual debe haberse autoejecutado.
|
||||
2. Cierra QUsb2Snes, el cual fue ejecutado junto al cliente.
|
||||
3. Ejecuta la version correcta de QUsb2Snes (v0.7.16).
|
||||
4. Enciende tu dispositivo y carga la ROM.
|
||||
5. Observa en el cliente que ahora muestra "SNES Device: Connected", y aparece el nombre del dispositivo.
|
||||
2. Enciende tu dispositivo y carga la ROM.
|
||||
|
||||
### Conecta al MultiServer
|
||||
### Conecta al Servidor Archipelago
|
||||
|
||||
El fichero de parche que ha lanzado el cliente debe haberte conectado automaticamente al MultiServer. Hay algunas
|
||||
razonas por las que esto puede que no pase, incluyendo que el juego este hospedado en el sitio web pero se genero en
|
||||
algún otro sitio. Si el cliente muestra "Server Status: Not Connected", preguntale al creador de la partida la dirección
|
||||
del servidor, copiala en el campo "Server" y presiona Enter.
|
||||
El fichero de parche que ha lanzado el cliente debería de haberte conectado automaticamente al MultiServer. Sin embargo hay algunas
|
||||
razones por las que puede que esto no suceda, como que la partida este hospedada en la página web pero generada en otra parte. Si la
|
||||
ventana del cliente muestra "Server Status: Not Connected", simplemente preguntale al creador de la partida la dirección
|
||||
del servidor, cópiala en el campo "Server" y presiona Enter.
|
||||
|
||||
El cliente intentara conectarse a esta nueva dirección, y debería mostrar "Server Status: Connected" en algún momento.
|
||||
Si el cliente no se conecta al cabo de un rato, puede ser que necesites refrescar la pagina web.
|
||||
El cliente intentará conectarse a esta nueva dirección, y debería mostrar "Server Status: Connected" momentáneamente.
|
||||
|
||||
### Jugando
|
||||
### Jugar al juego
|
||||
|
||||
Cuando ambos SNES Device and Server aparezcan como "connected", estas listo para empezar a jugar. Felicidades por unirte
|
||||
satisfactoriamente a una partida de multiworld!
|
||||
|
||||
## Hospedando una partida de multiworld
|
||||
|
||||
La manera recomendad para hospedar una partida es usar el servicio proveído en
|
||||
[el sitio web](/generate). El proceso es relativamente sencillo:
|
||||
|
||||
1. Recolecta los ficheros YAML de todos los jugadores que participen.
|
||||
2. Crea un fichero ZIP conteniendo esos ficheros.
|
||||
3. Carga el fichero zip en el sitio web enlazado anteriormente.
|
||||
4. Espera a que la seed sea generada.
|
||||
5. Cuando esto acabe, se te redigirá a una pagina titulada "Seed Info".
|
||||
6. Haz click en "Create New Room". Esto te llevara a la pagina del servidor. Pasa el enlace a esta pagina a los
|
||||
jugadores para que puedan descargar los ficheros de parche de ahi.
|
||||
**Nota:** Los ficheros de parche de esta pagina permiten a los jugadores conectarse al servidor automaticamente,
|
||||
mientras que los de la pagina "Seed info" no.
|
||||
7. Hay un enlace a un MultiWorld Tracker en la parte superior de la pagina de la sala. Deberías pasar también este
|
||||
enlace a los jugadores para que puedan ver el progreso de la partida. A los observadores también se les puede pasar
|
||||
este enlace.
|
||||
8. Una vez todos los jugadores se han unido, podeis empezar a jugar.
|
||||
|
||||
## Auto-Tracking
|
||||
|
||||
Si deseas usar auto-tracking para tu partida, varios programas ofrecen esta funcionalidad.
|
||||
El programa recomentdado actualmente es:
|
||||
[OpenTracker](https://github.com/trippsc2/OpenTracker/releases).
|
||||
|
||||
### Instalación
|
||||
|
||||
1. Descarga el fichero de instalacion apropiado para tu ordenador (Usuarios de windows quieren el fichero ".msi").
|
||||
2. Durante el proceso de insatalación, puede que se te pida instalar Microsoft Visual Studio Build Tools. Un enlace este
|
||||
programa se muestra durante la proceso, y debe ser ejecutado manualmente.
|
||||
|
||||
### Activar auto-tracking
|
||||
|
||||
1. Con OpenTracker ejecutado, haz click en el menu Tracking en la parte superior de la ventana, y elige **
|
||||
AutoTracker...**
|
||||
2. Click the **Get Devices** button
|
||||
3. Selecciona tu "SNES device" de la lista
|
||||
4. Si quieres que las llaves y los objetos de mazmorra tambien sean marcados, activa la caja con nombre **Race Illegal
|
||||
Tracking**
|
||||
5. Haz click en el boton **Start Autotracking**
|
||||
6. Cierra la ventana AutoTracker, ya que deja de ser necesaria
|
||||
Cuando el cliente muestre tanto el dispositivo SNES como el servidor como conectados, estas listo para empezar a jugar. Felicidades por
|
||||
haberte unido a una partida multiworld con exito! Puedes ejecutar varios comandos en tu cliente. Para mas informacion
|
||||
acerca de estos comando puedes usar `/help` para comandos locales del cliente y `!help` para comandos de servidor.
|
||||
|
||||
@@ -77,5 +77,5 @@ class TestMiseryMire(TestDungeon):
|
||||
["Misery Mire - Boss", False, [], ['Bomb Upgrade (+5)', 'Bomb Upgrade (+10)', 'Bomb Upgrade (50)']],
|
||||
["Misery Mire - Boss", True, ['Bomb Upgrade (+5)', 'Big Key (Misery Mire)', 'Lamp', 'Cane of Somaria', 'Progressive Sword', 'Pegasus Boots']],
|
||||
["Misery Mire - Boss", True, ['Bomb Upgrade (+5)', 'Big Key (Misery Mire)', 'Lamp', 'Cane of Somaria', 'Hammer', 'Pegasus Boots']],
|
||||
["Misery Mire - Boss", True, ['Bomb Upgrade (+5)', 'Big Key (Misery Mire)', 'Lamp', 'Cane of Somaria', 'Progressive Bow', 'Pegasus Boots']],
|
||||
["Misery Mire - Boss", True, ['Bomb Upgrade (+5)', 'Big Key (Misery Mire)', 'Lamp', 'Cane of Somaria', 'Progressive Bow', 'Arrow Upgrade (+5)', 'Pegasus Boots']],
|
||||
])
|
||||
@@ -93,7 +93,7 @@ class AquariaWorld(World):
|
||||
options: AquariaOptions
|
||||
"Every options of the world"
|
||||
|
||||
regions: AquariaRegions
|
||||
regions: AquariaRegions | None
|
||||
"Used to manage Regions"
|
||||
|
||||
exclude: List[str]
|
||||
@@ -101,10 +101,17 @@ class AquariaWorld(World):
|
||||
def __init__(self, multiworld: MultiWorld, player: int):
|
||||
"""Initialisation of the Aquaria World"""
|
||||
super(AquariaWorld, self).__init__(multiworld, player)
|
||||
self.regions = AquariaRegions(multiworld, player)
|
||||
self.regions = None
|
||||
self.ingredients_substitution = []
|
||||
self.exclude = []
|
||||
|
||||
def generate_early(self) -> None:
|
||||
"""
|
||||
Run before any general steps of the MultiWorld other than options. Useful for getting and adjusting option
|
||||
results and determining layouts for entrance rando etc. start inventory gets pushed after this step.
|
||||
"""
|
||||
self.regions = AquariaRegions(self.multiworld, self.player)
|
||||
|
||||
def create_regions(self) -> None:
|
||||
"""
|
||||
Create every Region in `regions`
|
||||
|
||||
@@ -103,6 +103,9 @@ class BlasphemousWorld(World):
|
||||
if not self.options.wall_climb_shuffle:
|
||||
self.multiworld.push_precollected(self.create_item("Wall Climb Ability"))
|
||||
|
||||
if self.options.thorn_shuffle == "local_only":
|
||||
self.options.local_items.value.add("Thorn Upgrade")
|
||||
|
||||
if not self.options.boots_of_pleading:
|
||||
self.disabled_locations.append("RE401")
|
||||
|
||||
@@ -200,9 +203,6 @@ class BlasphemousWorld(World):
|
||||
|
||||
if not self.options.skill_randomizer:
|
||||
self.place_items_from_dict(skill_dict)
|
||||
|
||||
if self.options.thorn_shuffle == "local_only":
|
||||
self.options.local_items.value.add("Thorn Upgrade")
|
||||
|
||||
|
||||
def place_items_from_set(self, location_set: Set[str], name: str):
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import logging
|
||||
import asyncio
|
||||
|
||||
from NetUtils import ClientStatus, color
|
||||
from worlds.AutoSNIClient import SNIClient
|
||||
@@ -32,7 +31,7 @@ class DKC3SNIClient(SNIClient):
|
||||
|
||||
|
||||
async def validate_rom(self, ctx):
|
||||
from SNIClient import snes_buffered_write, snes_flush_writes, snes_read
|
||||
from SNIClient import snes_read
|
||||
|
||||
rom_name = await snes_read(ctx, DKC3_ROMHASH_START, ROMHASH_SIZE)
|
||||
if rom_name is None or rom_name == bytes([0] * ROMHASH_SIZE) or rom_name[:2] != b"D3":
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import typing
|
||||
|
||||
from BaseClasses import Item, ItemClassification
|
||||
from BaseClasses import Item
|
||||
from .Names import ItemName
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from dataclasses import dataclass
|
||||
import typing
|
||||
|
||||
from Options import Choice, Range, Toggle, DeathLink, DefaultOnToggle, OptionGroup, PerGameCommonOptions
|
||||
from Options import Choice, Range, Toggle, DefaultOnToggle, OptionGroup, PerGameCommonOptions
|
||||
|
||||
|
||||
class Goal(Choice):
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
import typing
|
||||
|
||||
from BaseClasses import MultiWorld, Region, Entrance
|
||||
from .Items import DKC3Item
|
||||
from BaseClasses import Region, Entrance
|
||||
from worlds.AutoWorld import World
|
||||
from .Locations import DKC3Location
|
||||
from .Names import LocationName, ItemName
|
||||
from worlds.AutoWorld import World
|
||||
|
||||
|
||||
def create_regions(world: World, active_locations):
|
||||
|
||||
@@ -2,7 +2,6 @@ import Utils
|
||||
from Utils import read_snes_rom
|
||||
from worlds.AutoWorld import World
|
||||
from worlds.Files import APDeltaPatch
|
||||
from .Locations import lookup_id_to_name, all_locations
|
||||
from .Levels import level_list, level_dict
|
||||
|
||||
USHASH = '120abf304f0c40fe059f6a192ed4f947'
|
||||
@@ -436,7 +435,7 @@ level_music_ids = [
|
||||
|
||||
class LocalRom:
|
||||
|
||||
def __init__(self, file, patch=True, vanillaRom=None, name=None, hash=None):
|
||||
def __init__(self, file, name=None, hash=None):
|
||||
self.name = name
|
||||
self.hash = hash
|
||||
self.orig_buffer = None
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import math
|
||||
|
||||
from worlds.AutoWorld import World
|
||||
from worlds.generic.Rules import add_rule
|
||||
from .Names import LocationName, ItemName
|
||||
from worlds.AutoWorld import LogicMixin, World
|
||||
from worlds.generic.Rules import add_rule, set_rule
|
||||
|
||||
|
||||
def set_rules(world: World):
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
import dataclasses
|
||||
import os
|
||||
import typing
|
||||
import math
|
||||
import os
|
||||
import threading
|
||||
import typing
|
||||
|
||||
import settings
|
||||
from BaseClasses import Item, MultiWorld, Tutorial, ItemClassification
|
||||
from Options import PerGameCommonOptions
|
||||
import Patch
|
||||
import settings
|
||||
from worlds.AutoWorld import WebWorld, World
|
||||
|
||||
from .Client import DKC3SNIClient
|
||||
from .Items import DKC3Item, ItemData, item_table, inventory_table, junk_table
|
||||
from .Levels import level_list
|
||||
|
||||
@@ -234,8 +234,7 @@ async def game_watcher(ctx: FactorioContext):
|
||||
f"Connected Multiworld is not the expected one {data['seed_name']} != {ctx.seed_name}")
|
||||
else:
|
||||
data = data["info"]
|
||||
research_data = data["research_done"]
|
||||
research_data = {int(tech_name.split("-")[1]) for tech_name in research_data}
|
||||
research_data: set[int] = {int(tech_name.split("-")[1]) for tech_name in data["research_done"]}
|
||||
victory = data["victory"]
|
||||
await ctx.update_death_link(data["death_link"])
|
||||
ctx.multiplayer = data.get("multiplayer", False)
|
||||
@@ -249,7 +248,7 @@ async def game_watcher(ctx: FactorioContext):
|
||||
f"New researches done: "
|
||||
f"{[ctx.location_names.lookup_in_game(rid) for rid in research_data - ctx.locations_checked]}")
|
||||
ctx.locations_checked = research_data
|
||||
await ctx.send_msgs([{"cmd": 'LocationChecks', "locations": tuple(research_data)}])
|
||||
await ctx.check_locations(research_data)
|
||||
death_link_tick = data.get("death_link_tick", 0)
|
||||
if death_link_tick != ctx.death_link_tick:
|
||||
ctx.death_link_tick = death_link_tick
|
||||
|
||||
@@ -3,13 +3,23 @@ from __future__ import annotations
|
||||
from dataclasses import dataclass
|
||||
import typing
|
||||
|
||||
from schema import Schema, Optional, And, Or
|
||||
from schema import Schema, Optional, And, Or, SchemaError
|
||||
|
||||
from Options import Choice, OptionDict, OptionSet, DefaultOnToggle, Range, DeathLink, Toggle, \
|
||||
StartInventoryPool, PerGameCommonOptions, OptionGroup
|
||||
|
||||
# schema helpers
|
||||
FloatRange = lambda low, high: And(Or(int, float), lambda f: low <= f <= high)
|
||||
class FloatRange:
|
||||
def __init__(self, low, high):
|
||||
self._low = low
|
||||
self._high = high
|
||||
|
||||
def validate(self, value):
|
||||
if not isinstance(value, (float, int)):
|
||||
raise SchemaError(f"should be instance of float or int, but was {value!r}")
|
||||
if not self._low <= value <= self._high:
|
||||
raise SchemaError(f"{value} is not between {self._low} and {self._high}")
|
||||
|
||||
LuaBool = Or(bool, And(int, lambda n: n in (0, 1)))
|
||||
|
||||
|
||||
|
||||
@@ -717,8 +717,10 @@ TRAP_TABLE = {
|
||||
game.surfaces["nauvis"].build_enemy_base(game.forces["player"].get_spawn_position(game.get_surface(1)), 25)
|
||||
end,
|
||||
["Evolution Trap"] = function ()
|
||||
game.forces["enemy"].evolution_factor = game.forces["enemy"].evolution_factor + (TRAP_EVO_FACTOR * (1 - game.forces["enemy"].evolution_factor))
|
||||
game.print({"", "New evolution factor:", game.forces["enemy"].evolution_factor})
|
||||
local new_factor = game.forces["enemy"].get_evolution_factor("nauvis") +
|
||||
(TRAP_EVO_FACTOR * (1 - game.forces["enemy"].get_evolution_factor("nauvis")))
|
||||
game.forces["enemy"].set_evolution_factor(new_factor, "nauvis")
|
||||
game.print({"", "New evolution factor:", new_factor})
|
||||
end,
|
||||
["Teleport Trap"] = function ()
|
||||
for _, player in ipairs(game.forces["player"].players) do
|
||||
|
||||
39
worlds/factorio/test_file_validation.py
Normal file
39
worlds/factorio/test_file_validation.py
Normal file
@@ -0,0 +1,39 @@
|
||||
"""Tests for error messages from YAML validation."""
|
||||
|
||||
import os
|
||||
import unittest
|
||||
|
||||
import WebHostLib.check
|
||||
|
||||
FACTORIO_YAML="""
|
||||
game: Factorio
|
||||
Factorio:
|
||||
world_gen:
|
||||
autoplace_controls:
|
||||
coal:
|
||||
richness: 1
|
||||
frequency: {}
|
||||
size: 1
|
||||
"""
|
||||
|
||||
def yamlWithFrequency(f):
|
||||
return FACTORIO_YAML.format(f)
|
||||
|
||||
|
||||
class TestFileValidation(unittest.TestCase):
|
||||
def test_out_of_range(self):
|
||||
results, _ = WebHostLib.check.roll_options({"bob.yaml": yamlWithFrequency(1000)})
|
||||
self.assertIn("between 0 and 6", results["bob.yaml"])
|
||||
|
||||
def test_bad_non_numeric(self):
|
||||
results, _ = WebHostLib.check.roll_options({"bob.yaml": yamlWithFrequency("not numeric")})
|
||||
self.assertIn("float", results["bob.yaml"])
|
||||
self.assertIn("int", results["bob.yaml"])
|
||||
|
||||
def test_good_float(self):
|
||||
results, _ = WebHostLib.check.roll_options({"bob.yaml": yamlWithFrequency(1.0)})
|
||||
self.assertIs(results["bob.yaml"], True)
|
||||
|
||||
def test_good_int(self):
|
||||
results, _ = WebHostLib.check.roll_options({"bob.yaml": yamlWithFrequency(1)})
|
||||
self.assertIs(results["bob.yaml"], True)
|
||||
@@ -44,8 +44,13 @@ class FaxanaduWorld(World):
|
||||
location_name_to_id = {loc.name: loc.id for loc in Locations.locations if loc.id is not None}
|
||||
|
||||
def __init__(self, world: MultiWorld, player: int):
|
||||
self.filler_ratios: Dict[str, int] = {}
|
||||
|
||||
self.filler_ratios: Dict[str, int] = {
|
||||
item.name: item.count
|
||||
for item in Items.items
|
||||
if item.classification in [ItemClassification.filler, ItemClassification.trap]
|
||||
}
|
||||
# Remove poison by default to respect itemlinking
|
||||
self.filler_ratios["Poison"] = 0
|
||||
super().__init__(world, player)
|
||||
|
||||
def create_regions(self):
|
||||
@@ -160,19 +165,13 @@ class FaxanaduWorld(World):
|
||||
for i in range(item.progression_count):
|
||||
itempool.append(FaxanaduItem(item.name, ItemClassification.progression, item.id, self.player))
|
||||
|
||||
# Set up filler ratios
|
||||
self.filler_ratios = {
|
||||
item.name: item.count
|
||||
for item in Items.items
|
||||
if item.classification in [ItemClassification.filler, ItemClassification.trap]
|
||||
}
|
||||
|
||||
# Adjust filler ratios
|
||||
# If red potions are locked in shops, remove the count from the ratio.
|
||||
self.filler_ratios["Red Potion"] -= red_potion_in_shop_count
|
||||
|
||||
# Remove poisons if not desired
|
||||
if not self.options.include_poisons:
|
||||
self.filler_ratios["Poison"] = 0
|
||||
# Add poisons if desired
|
||||
if self.options.include_poisons:
|
||||
self.filler_ratios["Poison"] = self.item_name_to_item["Poison"].count
|
||||
|
||||
# Randomly add fillers to the pool with ratios based on og game occurrence counts.
|
||||
filler_count = len(Locations.locations) - len(itempool) - prefilled_count
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from Options import Choice, FreeText, Toggle, Range, PerGameCommonOptions
|
||||
from Options import Choice, FreeText, ItemsAccessibility, Toggle, Range, PerGameCommonOptions
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
@@ -324,6 +324,7 @@ class KaelisMomFightsMinotaur(Toggle):
|
||||
|
||||
@dataclass
|
||||
class FFMQOptions(PerGameCommonOptions):
|
||||
accessibility: ItemsAccessibility
|
||||
logic: Logic
|
||||
brown_boxes: BrownBoxes
|
||||
sky_coin_mode: SkyCoinMode
|
||||
|
||||
@@ -181,6 +181,7 @@ class HKWorld(World):
|
||||
charm_costs: typing.List[int]
|
||||
cached_filler_items = {}
|
||||
grub_count: int
|
||||
grub_player_count: typing.Dict[int, int]
|
||||
|
||||
def __init__(self, multiworld, player):
|
||||
super(HKWorld, self).__init__(multiworld, player)
|
||||
@@ -190,7 +191,6 @@ class HKWorld(World):
|
||||
self.ranges = {}
|
||||
self.created_shop_items = 0
|
||||
self.vanilla_shop_costs = deepcopy(vanilla_shop_costs)
|
||||
self.grub_count = 0
|
||||
|
||||
def generate_early(self):
|
||||
options = self.options
|
||||
@@ -204,7 +204,14 @@ class HKWorld(World):
|
||||
mini.value = min(mini.value, maxi.value)
|
||||
self.ranges[term] = mini.value, maxi.value
|
||||
self.multiworld.push_precollected(HKItem(starts[options.StartLocation.current_key],
|
||||
True, None, "Event", self.player))
|
||||
True, None, "Event", self.player))
|
||||
|
||||
# defaulting so completion condition isn't incorrect before pre_fill
|
||||
self.grub_count = (
|
||||
46 if options.GrubHuntGoal == GrubHuntGoal.special_range_names["all"]
|
||||
else options.GrubHuntGoal
|
||||
)
|
||||
self.grub_player_count = {self.player: self.grub_count}
|
||||
|
||||
def white_palace_exclusions(self):
|
||||
exclusions = set()
|
||||
@@ -469,25 +476,20 @@ class HKWorld(World):
|
||||
elif goal == Goal.option_godhome_flower:
|
||||
multiworld.completion_condition[player] = lambda state: state.count("Godhome_Flower_Quest", player)
|
||||
elif goal == Goal.option_grub_hunt:
|
||||
pass # will set in stage_pre_fill()
|
||||
multiworld.completion_condition[player] = lambda state: self.can_grub_goal(state)
|
||||
else:
|
||||
# Any goal
|
||||
multiworld.completion_condition[player] = lambda state: _hk_siblings_ending(state, player) and \
|
||||
_hk_can_beat_radiance(state, player) and state.count("Godhome_Flower_Quest", player)
|
||||
_hk_can_beat_radiance(state, player) and state.count("Godhome_Flower_Quest", player) and \
|
||||
self.can_grub_goal(state)
|
||||
|
||||
set_rules(self)
|
||||
|
||||
def can_grub_goal(self, state: CollectionState) -> bool:
|
||||
return all(state.has("Grub", owner, count) for owner, count in self.grub_player_count.items())
|
||||
|
||||
@classmethod
|
||||
def stage_pre_fill(cls, multiworld: "MultiWorld"):
|
||||
def set_goal(player, grub_rule: typing.Callable[[CollectionState], bool]):
|
||||
world = multiworld.worlds[player]
|
||||
|
||||
if world.options.Goal == "grub_hunt":
|
||||
multiworld.completion_condition[player] = grub_rule
|
||||
else:
|
||||
old_rule = multiworld.completion_condition[player]
|
||||
multiworld.completion_condition[player] = lambda state: old_rule(state) and grub_rule(state)
|
||||
|
||||
worlds = [world for world in multiworld.get_game_worlds(cls.game) if world.options.Goal in ["any", "grub_hunt"]]
|
||||
if worlds:
|
||||
grubs = [item for item in multiworld.get_items() if item.name == "Grub"]
|
||||
@@ -525,13 +527,13 @@ class HKWorld(World):
|
||||
|
||||
for player, grub_player_count in per_player_grubs_per_player.items():
|
||||
if player in all_grub_players:
|
||||
set_goal(player, lambda state, g=grub_player_count: all(state.has("Grub", owner, count) for owner, count in g.items()))
|
||||
multiworld.worlds[player].grub_player_count = grub_player_count
|
||||
|
||||
for world in worlds:
|
||||
if world.player not in all_grub_players:
|
||||
world.grub_count = world.options.GrubHuntGoal.value
|
||||
player = world.player
|
||||
set_goal(player, lambda state, p=player, c=world.grub_count: state.has("Grub", p, c))
|
||||
world.grub_player_count = {player: world.grub_count}
|
||||
|
||||
def fill_slot_data(self):
|
||||
slot_data = {}
|
||||
|
||||
@@ -110,6 +110,7 @@ class KH2Context(CommonContext):
|
||||
18: TWTNW_Checks,
|
||||
# 255: {}, # starting screen
|
||||
}
|
||||
self.last_world_int = -1
|
||||
# 0x2A09C00+0x40 is the sve anchor. +1 is the last saved room
|
||||
# self.sveroom = 0x2A09C00 + 0x41
|
||||
# 0 not in battle 1 in yellow battle 2 red battle #short
|
||||
@@ -345,33 +346,12 @@ class KH2Context(CommonContext):
|
||||
self.lookup_id_to_item = {v: k for k, v in self.kh2_item_name_to_id.items()}
|
||||
self.ability_code_list = [self.kh2_item_name_to_id[item] for item in exclusion_item_table["Ability"]]
|
||||
|
||||
if "keyblade_abilities" in self.kh2slotdata.keys():
|
||||
sora_ability_dict = self.kh2slotdata["KeybladeAbilities"]
|
||||
if "KeybladeAbilities" in self.kh2slotdata.keys():
|
||||
# sora ability to slot
|
||||
self.AbilityQuantityDict.update(self.kh2slotdata["KeybladeAbilities"])
|
||||
# itemid:[slots that are available for that item]
|
||||
for k, v in sora_ability_dict.items():
|
||||
if v >= 1:
|
||||
if k not in self.sora_ability_to_slot.keys():
|
||||
self.sora_ability_to_slot[k] = []
|
||||
for _ in range(sora_ability_dict[k]):
|
||||
self.sora_ability_to_slot[k].append(self.kh2_seed_save_cache["SoraInvo"][0])
|
||||
self.kh2_seed_save_cache["SoraInvo"][0] -= 2
|
||||
donald_ability_dict = self.kh2slotdata["StaffAbilities"]
|
||||
for k, v in donald_ability_dict.items():
|
||||
if v >= 1:
|
||||
if k not in self.donald_ability_to_slot.keys():
|
||||
self.donald_ability_to_slot[k] = []
|
||||
for _ in range(donald_ability_dict[k]):
|
||||
self.donald_ability_to_slot[k].append(self.kh2_seed_save_cache["DonaldInvo"][0])
|
||||
self.kh2_seed_save_cache["DonaldInvo"][0] -= 2
|
||||
goofy_ability_dict = self.kh2slotdata["ShieldAbilities"]
|
||||
for k, v in goofy_ability_dict.items():
|
||||
if v >= 1:
|
||||
if k not in self.goofy_ability_to_slot.keys():
|
||||
self.goofy_ability_to_slot[k] = []
|
||||
for _ in range(goofy_ability_dict[k]):
|
||||
self.goofy_ability_to_slot[k].append(self.kh2_seed_save_cache["GoofyInvo"][0])
|
||||
self.kh2_seed_save_cache["GoofyInvo"][0] -= 2
|
||||
self.AbilityQuantityDict.update(self.kh2slotdata["StaffAbilities"])
|
||||
self.AbilityQuantityDict.update(self.kh2slotdata["ShieldAbilities"])
|
||||
|
||||
all_weapon_location_id = []
|
||||
for weapon_location in all_weapon_slot:
|
||||
@@ -408,13 +388,15 @@ class KH2Context(CommonContext):
|
||||
async def checkWorldLocations(self):
|
||||
try:
|
||||
currentworldint = self.kh2_read_byte(self.Now)
|
||||
await self.send_msgs([{
|
||||
"cmd": "Set", "key": "Slot: " + str(self.slot) + " :CurrentWorld",
|
||||
"default": 0, "want_reply": True, "operations": [{
|
||||
"operation": "replace",
|
||||
"value": currentworldint
|
||||
}]
|
||||
}])
|
||||
if self.last_world_int != currentworldint:
|
||||
self.last_world_int = currentworldint
|
||||
await self.send_msgs([{
|
||||
"cmd": "Set", "key": "Slot: " + str(self.slot) + " :CurrentWorld",
|
||||
"default": 0, "want_reply": False, "operations": [{
|
||||
"operation": "replace",
|
||||
"value": currentworldint
|
||||
}]
|
||||
}])
|
||||
if currentworldint in self.worldid_to_locations:
|
||||
curworldid = self.worldid_to_locations[currentworldint]
|
||||
for location, data in curworldid.items():
|
||||
@@ -525,27 +507,7 @@ class KH2Context(CommonContext):
|
||||
if itemname not in self.kh2_seed_save_cache["AmountInvo"]["Ability"]:
|
||||
self.kh2_seed_save_cache["AmountInvo"]["Ability"][itemname] = []
|
||||
# appending the slot that the ability should be in
|
||||
# for non beta. remove after 4.3
|
||||
if "PoptrackerVersion" in self.kh2slotdata:
|
||||
if self.kh2slotdata["PoptrackerVersionCheck"] < 4.3:
|
||||
if (itemname in self.sora_ability_set
|
||||
and len(self.kh2_seed_save_cache["AmountInvo"]["Ability"][itemname]) < self.item_name_to_data[itemname].quantity) \
|
||||
and self.kh2_seed_save_cache["SoraInvo"][1] > 0x254C:
|
||||
ability_slot = self.kh2_seed_save_cache["SoraInvo"][1]
|
||||
self.kh2_seed_save_cache["AmountInvo"]["Ability"][itemname].append(ability_slot)
|
||||
self.kh2_seed_save_cache["SoraInvo"][1] -= 2
|
||||
elif itemname in self.donald_ability_set:
|
||||
ability_slot = self.kh2_seed_save_cache["DonaldInvo"][1]
|
||||
self.kh2_seed_save_cache["AmountInvo"]["Ability"][itemname].append(ability_slot)
|
||||
self.kh2_seed_save_cache["DonaldInvo"][1] -= 2
|
||||
else:
|
||||
ability_slot = self.kh2_seed_save_cache["GoofyInvo"][1]
|
||||
self.kh2_seed_save_cache["AmountInvo"]["Ability"][itemname].append(ability_slot)
|
||||
self.kh2_seed_save_cache["GoofyInvo"][1] -= 2
|
||||
if ability_slot in self.front_ability_slots:
|
||||
self.front_ability_slots.remove(ability_slot)
|
||||
|
||||
elif len(self.kh2_seed_save_cache["AmountInvo"]["Ability"][itemname]) < \
|
||||
if len(self.kh2_seed_save_cache["AmountInvo"]["Ability"][itemname]) < \
|
||||
self.AbilityQuantityDict[itemname]:
|
||||
if itemname in self.sora_ability_set:
|
||||
ability_slot = self.kh2_seed_save_cache["SoraInvo"][0]
|
||||
@@ -845,7 +807,7 @@ class KH2Context(CommonContext):
|
||||
logger.info("line 840")
|
||||
|
||||
|
||||
def finishedGame(ctx: KH2Context, message):
|
||||
def finishedGame(ctx: KH2Context):
|
||||
if ctx.kh2slotdata['FinalXemnas'] == 1:
|
||||
if not ctx.final_xemnas and ctx.kh2_read_byte(ctx.Save + all_world_locations[LocationName.FinalXemnas].addrObtained) \
|
||||
& 0x1 << all_world_locations[LocationName.FinalXemnas].bitIndex > 0:
|
||||
@@ -877,8 +839,9 @@ def finishedGame(ctx: KH2Context, message):
|
||||
elif ctx.kh2slotdata['Goal'] == 2:
|
||||
# for backwards compat
|
||||
if "hitlist" in ctx.kh2slotdata:
|
||||
locations = ctx.sending
|
||||
for boss in ctx.kh2slotdata["hitlist"]:
|
||||
if boss in message[0]["locations"]:
|
||||
if boss in locations:
|
||||
ctx.hitlist_bounties += 1
|
||||
if ctx.hitlist_bounties >= ctx.kh2slotdata["BountyRequired"] or ctx.kh2_seed_save_cache["AmountInvo"]["Amount"]["Bounty"] >= ctx.kh2slotdata["BountyRequired"]:
|
||||
if ctx.kh2_read_byte(ctx.Save + 0x36B3) < 1:
|
||||
@@ -919,11 +882,12 @@ async def kh2_watcher(ctx: KH2Context):
|
||||
await asyncio.create_task(ctx.verifyChests())
|
||||
await asyncio.create_task(ctx.verifyItems())
|
||||
await asyncio.create_task(ctx.verifyLevel())
|
||||
message = [{"cmd": 'LocationChecks', "locations": ctx.sending}]
|
||||
if finishedGame(ctx, message) and not ctx.kh2_finished_game:
|
||||
if finishedGame(ctx) and not ctx.kh2_finished_game:
|
||||
await ctx.send_msgs([{"cmd": "StatusUpdate", "status": ClientStatus.CLIENT_GOAL}])
|
||||
ctx.kh2_finished_game = True
|
||||
await ctx.send_msgs(message)
|
||||
if ctx.sending:
|
||||
message = [{"cmd": 'LocationChecks', "locations": ctx.sending}]
|
||||
await ctx.send_msgs(message)
|
||||
elif not ctx.kh2connected and ctx.serverconneced:
|
||||
logger.info("Game Connection lost. waiting 15 seconds until trying to reconnect.")
|
||||
ctx.kh2 = None
|
||||
|
||||
@@ -103,6 +103,7 @@ def generateRom(args, world: "LinksAwakeningWorld"):
|
||||
assembler.const("wGoldenLeaves", 0xDB42) # New memory location where to store the golden leaf counter
|
||||
assembler.const("wCollectedTunics", 0xDB6D) # Memory location where to store which tunic options are available (and boots)
|
||||
assembler.const("wCustomMessage", 0xC0A0)
|
||||
assembler.const("wOverworldRoomStatus", 0xD800)
|
||||
|
||||
# We store the link info in unused color dungeon flags, so it gets preserved in the savegame.
|
||||
assembler.const("wLinkSyncSequenceNumber", 0xDDF6)
|
||||
|
||||
@@ -68,10 +68,12 @@ DEFAULT_ITEM_POOL = {
|
||||
|
||||
|
||||
class ItemPool:
|
||||
def __init__(self, logic, settings, rnd):
|
||||
def __init__(self, logic, settings, rnd, stabilize_item_pool: bool):
|
||||
self.__pool = {}
|
||||
self.__setup(logic, settings)
|
||||
self.__randomizeRupees(settings, rnd)
|
||||
|
||||
if not stabilize_item_pool:
|
||||
self.__randomizeRupees(settings, rnd)
|
||||
|
||||
def add(self, item, count=1):
|
||||
self.__pool[item] = self.__pool.get(item, 0) + count
|
||||
|
||||
@@ -2,6 +2,10 @@ import typing
|
||||
from ..checkMetadata import checkMetadataTable
|
||||
from .constants import *
|
||||
|
||||
custom_name_replacements = {
|
||||
'"':"'",
|
||||
'_':' ',
|
||||
}
|
||||
|
||||
class ItemInfo:
|
||||
MULTIWORLD = True
|
||||
@@ -23,6 +27,11 @@ class ItemInfo:
|
||||
def setLocation(self, location):
|
||||
self._location = location
|
||||
|
||||
def setCustomItemName(self, name):
|
||||
for key, val in custom_name_replacements.items():
|
||||
name = name.replace(key, val)
|
||||
self.custom_item_name = name
|
||||
|
||||
def getOptions(self):
|
||||
return self.OPTIONS
|
||||
|
||||
|
||||
@@ -716,9 +716,7 @@ def addWarpImprovements(rom, extra_warps):
|
||||
|
||||
# Allow cursor to move over black squares
|
||||
# This allows warping to undiscovered areas - a fine cheat, but needs a check for wOverworldRoomStatus in the warp code
|
||||
CHEAT_WARP_ANYWHERE = False
|
||||
if CHEAT_WARP_ANYWHERE:
|
||||
rom.patch(0x01, 0x1AE8, None, ASM("jp $5AF5"))
|
||||
rom.patch(0x01, 0x1AE8, None, ASM("jp $5AF5"))
|
||||
|
||||
# This disables the arrows around the selection bubble
|
||||
#rom.patch(0x01, 0x1B6F, None, ASM("ret"), fill_nop=True)
|
||||
@@ -797,8 +795,14 @@ def addWarpImprovements(rom, extra_warps):
|
||||
TeleportHandler:
|
||||
|
||||
ld a, [$DBB4] ; Load the current selected tile
|
||||
; TODO: check if actually revealed so we can have free movement
|
||||
; Check cursor against different tiles to see if we are selecting a warp
|
||||
ld hl, wOverworldRoomStatus
|
||||
ld e, a ; $5D38: $5F
|
||||
ld d, $00 ; $5D39: $16 $00
|
||||
add hl, de ; $5D3B: $19
|
||||
ld a, [hl]
|
||||
and $80
|
||||
jr z, exit
|
||||
ld a, [$DBB4] ; Load the current selected tile
|
||||
{warp_jump}
|
||||
jr exit
|
||||
|
||||
|
||||
@@ -527,6 +527,13 @@ class InGameHints(DefaultOnToggle):
|
||||
display_name = "In-game Hints"
|
||||
|
||||
|
||||
class StabilizeItemPool(DefaultOffToggle):
|
||||
"""
|
||||
By default, rupees in the item pool may be randomly swapped with bombs, arrows, powders, or capacity upgrades. This option disables that swapping, which is useful for plando.
|
||||
"""
|
||||
display_name = "Stabilize Item Pool"
|
||||
|
||||
|
||||
class ForeignItemIcons(Choice):
|
||||
"""
|
||||
Choose how to display foreign items.
|
||||
@@ -562,6 +569,7 @@ ladx_option_groups = [
|
||||
TrendyGame,
|
||||
InGameHints,
|
||||
NagMessages,
|
||||
StabilizeItemPool,
|
||||
Quickswap,
|
||||
HardMode,
|
||||
BootsControls
|
||||
@@ -631,6 +639,7 @@ class LinksAwakeningOptions(PerGameCommonOptions):
|
||||
no_flash: NoFlash
|
||||
in_game_hints: InGameHints
|
||||
overworld: Overworld
|
||||
stabilize_item_pool: StabilizeItemPool
|
||||
|
||||
warp_improvements: Removed
|
||||
additional_warp_points: Removed
|
||||
|
||||
@@ -138,7 +138,30 @@ class LinksAwakeningWorld(World):
|
||||
world_setup = LADXRWorldSetup()
|
||||
world_setup.randomize(self.ladxr_settings, self.random)
|
||||
self.ladxr_logic = LADXRLogic(configuration_options=self.ladxr_settings, world_setup=world_setup)
|
||||
self.ladxr_itempool = LADXRItemPool(self.ladxr_logic, self.ladxr_settings, self.random).toDict()
|
||||
self.ladxr_itempool = LADXRItemPool(self.ladxr_logic, self.ladxr_settings, self.random, bool(self.options.stabilize_item_pool)).toDict()
|
||||
|
||||
|
||||
def generate_early(self) -> None:
|
||||
self.dungeon_item_types = {
|
||||
}
|
||||
for dungeon_item_type in ["maps", "compasses", "small_keys", "nightmare_keys", "stone_beaks", "instruments"]:
|
||||
option_name = "shuffle_" + dungeon_item_type
|
||||
option: DungeonItemShuffle = getattr(self.options, option_name)
|
||||
|
||||
self.dungeon_item_types[option.ladxr_item] = option.value
|
||||
|
||||
# The color dungeon does not contain an instrument
|
||||
num_items = 8 if dungeon_item_type == "instruments" else 9
|
||||
|
||||
# For any and different world, set item rule instead
|
||||
if option.value == DungeonItemShuffle.option_own_world:
|
||||
self.options.local_items.value |= {
|
||||
ladxr_item_to_la_item_name[f"{option.ladxr_item}{i}"] for i in range(1, num_items + 1)
|
||||
}
|
||||
elif option.value == DungeonItemShuffle.option_different_world:
|
||||
self.options.non_local_items.value |= {
|
||||
ladxr_item_to_la_item_name[f"{option.ladxr_item}{i}"] for i in range(1, num_items + 1)
|
||||
}
|
||||
|
||||
def create_regions(self) -> None:
|
||||
# Initialize
|
||||
@@ -185,32 +208,9 @@ class LinksAwakeningWorld(World):
|
||||
def create_items(self) -> None:
|
||||
exclude = [item.name for item in self.multiworld.precollected_items[self.player]]
|
||||
|
||||
dungeon_item_types = {
|
||||
|
||||
}
|
||||
|
||||
self.prefill_original_dungeon = [ [], [], [], [], [], [], [], [], [] ]
|
||||
self.prefill_own_dungeons = []
|
||||
self.pre_fill_items = []
|
||||
# For any and different world, set item rule instead
|
||||
|
||||
for dungeon_item_type in ["maps", "compasses", "small_keys", "nightmare_keys", "stone_beaks", "instruments"]:
|
||||
option_name = "shuffle_" + dungeon_item_type
|
||||
option: DungeonItemShuffle = getattr(self.options, option_name)
|
||||
|
||||
dungeon_item_types[option.ladxr_item] = option.value
|
||||
|
||||
# The color dungeon does not contain an instrument
|
||||
num_items = 8 if dungeon_item_type == "instruments" else 9
|
||||
|
||||
if option.value == DungeonItemShuffle.option_own_world:
|
||||
self.options.local_items.value |= {
|
||||
ladxr_item_to_la_item_name[f"{option.ladxr_item}{i}"] for i in range(1, num_items + 1)
|
||||
}
|
||||
elif option.value == DungeonItemShuffle.option_different_world:
|
||||
self.options.non_local_items.value |= {
|
||||
ladxr_item_to_la_item_name[f"{option.ladxr_item}{i}"] for i in range(1, num_items + 1)
|
||||
}
|
||||
# option_original_dungeon = 0
|
||||
# option_own_dungeons = 1
|
||||
# option_own_world = 2
|
||||
@@ -226,7 +226,7 @@ class LinksAwakeningWorld(World):
|
||||
for _ in range(count):
|
||||
if item_name in exclude:
|
||||
exclude.remove(item_name) # this is destructive. create unique list above
|
||||
self.multiworld.itempool.append(self.create_item("Nothing"))
|
||||
self.multiworld.itempool.append(self.create_item(self.get_filler_item_name()))
|
||||
else:
|
||||
item = self.create_item(item_name)
|
||||
|
||||
@@ -238,7 +238,7 @@ class LinksAwakeningWorld(World):
|
||||
|
||||
if isinstance(item.item_data, DungeonItemData):
|
||||
item_type = item.item_data.ladxr_id[:-1]
|
||||
shuffle_type = dungeon_item_types[item_type]
|
||||
shuffle_type = self.dungeon_item_types[item_type]
|
||||
|
||||
if item.item_data.dungeon_item_type == DungeonItemType.INSTRUMENT and shuffle_type == ShuffleInstruments.option_vanilla:
|
||||
# Find instrument, lock
|
||||
@@ -439,7 +439,7 @@ class LinksAwakeningWorld(World):
|
||||
# Otherwise, use a cute letter as the icon
|
||||
elif self.options.foreign_item_icons == 'guess_by_name':
|
||||
loc.ladxr_item.item = self.guess_icon_for_other_world(loc.item)
|
||||
loc.ladxr_item.custom_item_name = loc.item.name
|
||||
loc.ladxr_item.setCustomItemName(loc.item.name)
|
||||
|
||||
else:
|
||||
if loc.item.advancement:
|
||||
@@ -500,8 +500,14 @@ class LinksAwakeningWorld(World):
|
||||
state.prog_items[self.player]["RUPEES"] -= self.rupees[item.name]
|
||||
return change
|
||||
|
||||
# Same fill choices and weights used in LADXR.itempool.__randomizeRupees
|
||||
filler_choices = ("Bomb", "Single Arrow", "10 Arrows", "Magic Powder", "Medicine")
|
||||
filler_weights = ( 10, 5, 10, 10, 1)
|
||||
|
||||
def get_filler_item_name(self) -> str:
|
||||
return "Nothing"
|
||||
if self.options.stabilize_item_pool:
|
||||
return "Nothing"
|
||||
return self.random.choices(self.filler_choices, self.filler_weights)[0]
|
||||
|
||||
def fill_slot_data(self):
|
||||
slot_data = {}
|
||||
|
||||
@@ -128,6 +128,9 @@ class LingoWorld(World):
|
||||
pool.append(self.create_item("Puzzle Skip"))
|
||||
|
||||
if traps:
|
||||
if self.options.speed_boost_mode:
|
||||
self.options.trap_weights.value["Slowness Trap"] = 0
|
||||
|
||||
total_weight = sum(self.options.trap_weights.values())
|
||||
|
||||
if total_weight == 0:
|
||||
@@ -171,7 +174,7 @@ class LingoWorld(World):
|
||||
"death_link", "victory_condition", "shuffle_colors", "shuffle_doors", "shuffle_paintings", "shuffle_panels",
|
||||
"enable_pilgrimage", "sunwarp_access", "mastery_achievements", "level_2_requirement", "location_checks",
|
||||
"early_color_hallways", "pilgrimage_allows_roof_access", "pilgrimage_allows_paintings", "shuffle_sunwarps",
|
||||
"group_doors"
|
||||
"group_doors", "speed_boost_mode"
|
||||
]
|
||||
|
||||
slot_data = {
|
||||
@@ -188,5 +191,8 @@ class LingoWorld(World):
|
||||
return slot_data
|
||||
|
||||
def get_filler_item_name(self) -> str:
|
||||
filler_list = [":)", "The Feeling of Being Lost", "Wanderlust", "Empty White Hallways"]
|
||||
return self.random.choice(filler_list)
|
||||
if self.options.speed_boost_mode:
|
||||
return "Speed Boost"
|
||||
else:
|
||||
filler_list = [":)", "The Feeling of Being Lost", "Wanderlust", "Empty White Hallways"]
|
||||
return self.random.choice(filler_list)
|
||||
|
||||
Binary file not shown.
@@ -17,6 +17,7 @@ special_items:
|
||||
Iceland Trap: 444411
|
||||
Atbash Trap: 444412
|
||||
Puzzle Skip: 444413
|
||||
Speed Boost: 444680
|
||||
panels:
|
||||
Starting Room:
|
||||
HI: 444400
|
||||
|
||||
@@ -85,6 +85,7 @@ def load_item_data():
|
||||
"The Feeling of Being Lost": ItemClassification.filler,
|
||||
"Wanderlust": ItemClassification.filler,
|
||||
"Empty White Hallways": ItemClassification.filler,
|
||||
"Speed Boost": ItemClassification.filler,
|
||||
**{trap_name: ItemClassification.trap for trap_name in TRAP_ITEMS},
|
||||
"Puzzle Skip": ItemClassification.useful,
|
||||
}
|
||||
|
||||
@@ -232,6 +232,14 @@ class TrapWeights(OptionDict):
|
||||
default = {trap_name: 1 for trap_name in TRAP_ITEMS}
|
||||
|
||||
|
||||
class SpeedBoostMode(Toggle):
|
||||
"""
|
||||
If on, the player's default speed is halved, as if affected by a Slowness Trap. Speed Boosts are added to
|
||||
the item pool, which temporarily return the player to normal speed. Slowness Traps are removed from the pool.
|
||||
"""
|
||||
display_name = "Speed Boost Mode"
|
||||
|
||||
|
||||
class PuzzleSkipPercentage(Range):
|
||||
"""Replaces junk items with puzzle skips, at the specified rate."""
|
||||
display_name = "Puzzle Skip Percentage"
|
||||
@@ -260,6 +268,7 @@ lingo_option_groups = [
|
||||
Level2Requirement,
|
||||
TrapPercentage,
|
||||
TrapWeights,
|
||||
SpeedBoostMode,
|
||||
PuzzleSkipPercentage,
|
||||
])
|
||||
]
|
||||
@@ -287,6 +296,7 @@ class LingoOptions(PerGameCommonOptions):
|
||||
shuffle_postgame: ShufflePostgame
|
||||
trap_percentage: TrapPercentage
|
||||
trap_weights: TrapWeights
|
||||
speed_boost_mode: SpeedBoostMode
|
||||
puzzle_skip_percentage: PuzzleSkipPercentage
|
||||
death_link: DeathLink
|
||||
start_inventory_from_pool: StartInventoryPool
|
||||
|
||||
@@ -59,4 +59,11 @@ class TestShuffleSunwarpsAccess(LingoTestBase):
|
||||
"victory_condition": "pilgrimage",
|
||||
"shuffle_sunwarps": "true",
|
||||
"sunwarp_access": "individual"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class TestSpeedBoostMode(LingoTestBase):
|
||||
options = {
|
||||
"location_checks": "insanity",
|
||||
"speed_boost_mode": "true",
|
||||
}
|
||||
|
||||
@@ -216,3 +216,6 @@ config.each do |room_name, room_data|
|
||||
end
|
||||
|
||||
File.write(outputpath, old_generated.to_yaml)
|
||||
|
||||
puts "Next item ID: #{next_item_id}"
|
||||
puts "Next location ID: #{next_location_id}"
|
||||
|
||||
@@ -214,10 +214,19 @@ class MegaMan2Client(BizHawkClient):
|
||||
last_wily: Optional[int] = None # default to wily 1
|
||||
|
||||
async def validate_rom(self, ctx: "BizHawkClientContext") -> bool:
|
||||
from worlds._bizhawk import RequestFailedError, read
|
||||
from worlds._bizhawk import RequestFailedError, read, get_memory_size
|
||||
from . import MM2World
|
||||
|
||||
try:
|
||||
if (await get_memory_size(ctx.bizhawk_ctx, "PRG ROM")) < 0x3FFB0:
|
||||
if "pool" in ctx.command_processor.commands:
|
||||
ctx.command_processor.commands.pop("pool")
|
||||
if "request" in ctx.command_processor.commands:
|
||||
ctx.command_processor.commands.pop("request")
|
||||
if "autoheal" in ctx.command_processor.commands:
|
||||
ctx.command_processor.commands.pop("autoheal")
|
||||
return False
|
||||
|
||||
game_name, version = (await read(ctx.bizhawk_ctx, [(0x3FFB0, 21, "PRG ROM"),
|
||||
(0x3FFC8, 3, "PRG ROM")]))
|
||||
if game_name[:3] != b"MM2" or version != bytes(MM2World.world_version):
|
||||
|
||||
@@ -85,7 +85,7 @@ keyItemList: typing.List[ItemData] = [
|
||||
]
|
||||
|
||||
subChipList: typing.List[ItemData] = [
|
||||
ItemData(0xB31018, ItemName.Unlocker, ItemClassification.useful, ItemType.SubChip, 117),
|
||||
ItemData(0xB31018, ItemName.Unlocker, ItemClassification.progression, ItemType.SubChip, 117),
|
||||
ItemData(0xB31019, ItemName.Untrap, ItemClassification.filler, ItemType.SubChip, 115),
|
||||
ItemData(0xB3101A, ItemName.LockEnmy, ItemClassification.filler, ItemType.SubChip, 116),
|
||||
ItemData(0xB3101B, ItemName.MiniEnrg, ItemClassification.filler, ItemType.SubChip, 112),
|
||||
@@ -290,7 +290,9 @@ programList: typing.List[ItemData] = [
|
||||
ItemData(0xB31099, ItemName.WpnLV_plus_Yellow, ItemClassification.filler, ItemType.Program, 35, ProgramColor.Yellow),
|
||||
ItemData(0xB3109A, ItemName.Press, ItemClassification.progression, ItemType.Program, 20, ProgramColor.White),
|
||||
|
||||
ItemData(0xB310B7, ItemName.UnderSht, ItemClassification.useful, ItemType.Program, 30, ProgramColor.White)
|
||||
ItemData(0xB310B7, ItemName.UnderSht, ItemClassification.useful, ItemType.Program, 30, ProgramColor.White),
|
||||
ItemData(0xB310E0, ItemName.Humor, ItemClassification.progression, ItemType.Program, 45, ProgramColor.Pink),
|
||||
ItemData(0xB310E1, ItemName.BlckMnd, ItemClassification.progression, ItemType.Program, 46, ProgramColor.White)
|
||||
]
|
||||
|
||||
zennyList: typing.List[ItemData] = [
|
||||
@@ -338,8 +340,29 @@ item_frequencies: typing.Dict[str, int] = {
|
||||
ItemName.zenny_800z: 2,
|
||||
ItemName.zenny_1000z: 2,
|
||||
ItemName.zenny_1200z: 2,
|
||||
ItemName.bugfrag_01: 5,
|
||||
ItemName.bugfrag_01: 10,
|
||||
ItemName.bugfrag_10: 5
|
||||
}
|
||||
|
||||
item_groups: typing.Dict[str, typing.Set[str]] = {
|
||||
"Key Items": {loc.itemName for loc in keyItemList},
|
||||
"Subchips": {loc.itemName for loc in subChipList},
|
||||
"Programs": {loc.itemName for loc in programList},
|
||||
"BattleChips": {loc.itemName for loc in chipList},
|
||||
"Zenny": {loc.itemName for loc in zennyList},
|
||||
"BugFrags": {loc.itemName for loc in bugFragList},
|
||||
"Navi Chips": {
|
||||
ItemName.Roll_R, ItemName.RollV2_R, ItemName.RollV3_R, ItemName.GutsMan_G, ItemName.GutsManV2_G,
|
||||
ItemName.GutsManV3_G, ItemName.ProtoMan_B, ItemName.ProtoManV2_B, ItemName.ProtoManV3_B, ItemName.FlashMan_F,
|
||||
ItemName.FlashManV2_F, ItemName.FlashManV3_F, ItemName.BeastMan_B, ItemName.BeastManV2_B, ItemName.BeastManV3_B,
|
||||
ItemName.BubblMan_B, ItemName.BubblManV2_B, ItemName.BubblManV3_B, ItemName.DesertMan_D, ItemName.DesertManV2_D,
|
||||
ItemName.DesertManV3_D, ItemName.PlantMan_P, ItemName.PlantManV2_P, ItemName.PlantManV3_P, ItemName.FlamMan_F,
|
||||
ItemName.FlamManV2_F, ItemName.FlamManV3_F, ItemName.DrillMan_D, ItemName.DrillManV2_D, ItemName.DrillManV3_D,
|
||||
ItemName.MetalMan_M, ItemName.MetalManV2_M, ItemName.MetalManV3_M, ItemName.KingMan_K, ItemName.KingManV2_K,
|
||||
ItemName.KingManV3_K, ItemName.BowlMan_B, ItemName.BowlManV2_B, ItemName.BowlManV3_B
|
||||
}
|
||||
}
|
||||
|
||||
all_items: typing.List[ItemData] = keyItemList + subChipList + chipList + programList + zennyList + bugFragList
|
||||
item_table: typing.Dict[str, ItemData] = {item.itemName: item for item in all_items}
|
||||
items_by_id: typing.Dict[int, ItemData] = {item.code: item for item in all_items}
|
||||
|
||||
@@ -221,7 +221,8 @@ overworlds = [
|
||||
LocationData(LocationName.Hades_Boat_Dock, 0xb310ab, 0x200024c, 0x10, 0x7519B0, 223, [3]),
|
||||
LocationData(LocationName.WWW_Control_Room_1_Screen, 0xb310ac, 0x200024d, 0x40, 0x7596C4, 222, [3, 4]),
|
||||
LocationData(LocationName.WWW_Wilys_Desk, 0xb310ad, 0x200024d, 0x2, 0x759384, 229, [3]),
|
||||
LocationData(LocationName.Undernet_4_Pillar_Prog, 0xb310ae, 0x2000161, 0x1, 0x7746C8, 191, [0, 1])
|
||||
LocationData(LocationName.Undernet_4_Pillar_Prog, 0xb310ae, 0x2000161, 0x1, 0x7746C8, 191, [0, 1]),
|
||||
LocationData(LocationName.Serenade, 0xb3110f, 0x2000178, 0x40, 0x7B3C74, 1, [0])
|
||||
]
|
||||
|
||||
jobs = [
|
||||
@@ -240,7 +241,8 @@ jobs = [
|
||||
# LocationData(LocationName.Gathering_Data, 0xb310bb, 0x2000300, 0x10, 0x739580, 193, [0]),
|
||||
LocationData(LocationName.Somebody_please_help, 0xb310bc, 0x2000301, 0x4, 0x73A14C, 193, [0]),
|
||||
LocationData(LocationName.Looking_for_condor, 0xb310bd, 0x2000301, 0x2, 0x749444, 203, [0]),
|
||||
LocationData(LocationName.Help_with_rehab, 0xb310be, 0x2000301, 0x1, 0x762CF0, 192, [3]),
|
||||
LocationData(LocationName.Help_with_rehab, 0xb310be, 0x2000301, 0x1, 0x762CF0, 192, [0]),
|
||||
LocationData(LocationName.Help_with_rehab_bonus, 0xb3110e, 0x2000301, 0x1, 0x762CF0, 192, [3]),
|
||||
LocationData(LocationName.Old_Master, 0xb310bf, 0x2000302, 0x80, 0x760E80, 193, [0]),
|
||||
LocationData(LocationName.Catching_gang_members, 0xb310c0, 0x2000302, 0x40, 0x76EAE4, 193, [0]),
|
||||
LocationData(LocationName.Please_adopt_a_virus, 0xb310c1, 0x2000302, 0x20, 0x76A4F4, 193, [0]),
|
||||
@@ -250,7 +252,7 @@ jobs = [
|
||||
LocationData(LocationName.Hide_and_seek_Second_Child, 0xb310c5, 0x2000188, 0x2, 0x75ADA8, 191, [0]),
|
||||
LocationData(LocationName.Hide_and_seek_Third_Child, 0xb310c6, 0x2000188, 0x1, 0x75B5EC, 191, [0]),
|
||||
LocationData(LocationName.Hide_and_seek_Fourth_Child, 0xb310c7, 0x2000189, 0x80, 0x75BEB0, 191, [0]),
|
||||
LocationData(LocationName.Hide_and_seek_Completion, 0xb310c8, 0x2000302, 0x8, 0x7406A0, 193, [0]),
|
||||
LocationData(LocationName.Hide_and_seek_Completion, 0xb310c8, 0x2000302, 0x8, 0x742D40, 193, [0]),
|
||||
LocationData(LocationName.Finding_the_blue_Navi, 0xb310c9, 0x2000302, 0x4, 0x773700, 192, [0]),
|
||||
LocationData(LocationName.Give_your_support, 0xb310ca, 0x2000302, 0x2, 0x752D80, 192, [0]),
|
||||
LocationData(LocationName.Stamp_collecting, 0xb310cb, 0x2000302, 0x1, 0x756074, 193, [0]),
|
||||
@@ -329,10 +331,7 @@ chocolate_shop = [
|
||||
LocationData(LocationName.Chocolate_Shop_32, 0xb3110d, 0x20001c3, 0x01, 0x73F8FC, 181, [0]),
|
||||
]
|
||||
|
||||
always_excluded_locations = [
|
||||
LocationName.Undernet_7_PMD,
|
||||
LocationName.Undernet_7_Northeast_BMD,
|
||||
LocationName.Undernet_7_Northwest_BMD,
|
||||
secret_locations = {
|
||||
LocationName.Secret_1_Northwest_BMD,
|
||||
LocationName.Secret_1_Northeast_BMD,
|
||||
LocationName.Secret_1_South_BMD,
|
||||
@@ -341,19 +340,23 @@ always_excluded_locations = [
|
||||
LocationName.Secret_2_Island_BMD,
|
||||
LocationName.Secret_3_Island_BMD,
|
||||
LocationName.Secret_3_BugFrag_BMD,
|
||||
LocationName.Secret_3_South_BMD
|
||||
]
|
||||
LocationName.Secret_3_South_BMD,
|
||||
LocationName.Serenade
|
||||
}
|
||||
|
||||
location_groups: typing.Dict[str, typing.Set[str]] = {
|
||||
"BMDs": {loc.name for loc in bmds},
|
||||
"PMDs": {loc.name for loc in pmds},
|
||||
"Jobs": {loc.name for loc in jobs},
|
||||
"Number Trader": {loc.name for loc in number_traders},
|
||||
"Bugfrag Trader": {loc.name for loc in chocolate_shop},
|
||||
"Secret Area": {LocationName.Secret_1_Northwest_BMD, LocationName.Secret_1_Northeast_BMD,
|
||||
LocationName.Secret_1_South_BMD, LocationName.Secret_2_Upper_BMD, LocationName.Secret_2_Lower_BMD,
|
||||
LocationName.Secret_2_Island_BMD, LocationName.Secret_3_Island_BMD,
|
||||
LocationName.Secret_3_BugFrag_BMD, LocationName.Secret_3_South_BMD, LocationName.Serenade},
|
||||
}
|
||||
|
||||
all_locations: typing.List[LocationData] = bmds + pmds + overworlds + jobs + number_traders + chocolate_shop
|
||||
scoutable_locations: typing.List[LocationData] = [loc for loc in all_locations if loc.hint_flag is not None]
|
||||
location_table: typing.Dict[str, int] = {locData.name: locData.id for locData in all_locations}
|
||||
location_data_table: typing.Dict[str, LocationData] = {locData.name: locData for locData in all_locations}
|
||||
|
||||
|
||||
"""
|
||||
def setup_locations(world, player: int):
|
||||
# If we later include options to change what gets added to the random pool,
|
||||
# this is where they would be changed
|
||||
return {locData.name: locData.id for locData in all_locations}
|
||||
"""
|
||||
|
||||
@@ -173,6 +173,8 @@ class ItemName():
|
||||
WpnLV_plus_White = "WpnLV+1 (White)"
|
||||
Press = "Press"
|
||||
UnderSht = "UnderSht"
|
||||
Humor = "Humor"
|
||||
BlckMnd = "BlckMnd"
|
||||
|
||||
## Currency
|
||||
zenny_200z = "200z"
|
||||
|
||||
@@ -210,6 +210,7 @@ class LocationName():
|
||||
WWW_Control_Room_1_Screen = "WWW Control Room 1 Screen"
|
||||
WWW_Wilys_Desk = "WWW Wily's Desk"
|
||||
Undernet_4_Pillar_Prog = "Undernet 4 Pillar Prog"
|
||||
Serenade = "Serenade"
|
||||
|
||||
## Numberman Codes
|
||||
Numberman_Code_01 = "Numberman Code 01"
|
||||
@@ -261,6 +262,7 @@ class LocationName():
|
||||
Somebody_please_help = "Job: Somebody, please help!"
|
||||
Looking_for_condor = "Job: Looking for condor"
|
||||
Help_with_rehab = "Job: Help with rehab"
|
||||
Help_with_rehab_bonus = "Job: Help with rehab bonus"
|
||||
Old_Master = "Job: Old Master"
|
||||
Catching_gang_members = "Job: Catching gang members"
|
||||
Please_adopt_a_virus = "Job: Please adopt a virus!"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from dataclasses import dataclass
|
||||
from Options import Choice, Range, DefaultOnToggle, PerGameCommonOptions
|
||||
from Options import Choice, Range, DefaultOnToggle, Toggle, PerGameCommonOptions
|
||||
|
||||
|
||||
class ExtraRanks(Range):
|
||||
@@ -17,10 +17,17 @@ class ExtraRanks(Range):
|
||||
|
||||
class IncludeJobs(DefaultOnToggle):
|
||||
"""
|
||||
Whether Jobs can be included in logic.
|
||||
Whether Jobs can contain progression or useful items.
|
||||
"""
|
||||
display_name = "Include Jobs"
|
||||
|
||||
|
||||
class IncludeSecretArea(Toggle):
|
||||
"""
|
||||
Whether the Secret Area (including Serenade) can contain progression or useful items.
|
||||
"""
|
||||
display_name = "Include Secret Area"
|
||||
|
||||
# Possible logic options:
|
||||
# - Include Number Trader
|
||||
# - Include Secret Area
|
||||
@@ -46,5 +53,6 @@ class TradeQuestHinting(Choice):
|
||||
class MMBN3Options(PerGameCommonOptions):
|
||||
extra_ranks: ExtraRanks
|
||||
include_jobs: IncludeJobs
|
||||
include_secret: IncludeSecretArea
|
||||
trade_quest_hinting: TradeQuestHinting
|
||||
|
||||
@@ -135,6 +135,7 @@ regions = [
|
||||
LocationName.Somebody_please_help,
|
||||
LocationName.Looking_for_condor,
|
||||
LocationName.Help_with_rehab,
|
||||
LocationName.Help_with_rehab_bonus,
|
||||
LocationName.Old_Master,
|
||||
LocationName.Catching_gang_members,
|
||||
LocationName.Please_adopt_a_virus,
|
||||
@@ -349,6 +350,7 @@ regions = [
|
||||
LocationName.Secret_2_Upper_BMD,
|
||||
LocationName.Secret_3_Island_BMD,
|
||||
LocationName.Secret_3_South_BMD,
|
||||
LocationName.Secret_3_BugFrag_BMD
|
||||
LocationName.Secret_3_BugFrag_BMD,
|
||||
LocationName.Serenade
|
||||
])
|
||||
]
|
||||
|
||||
@@ -9,14 +9,14 @@ from BaseClasses import Item, MultiWorld, Tutorial, ItemClassification, Region,
|
||||
from worlds.AutoWorld import WebWorld, World
|
||||
|
||||
from .Rom import MMBN3DeltaPatch, LocalRom, get_base_rom_path
|
||||
from .Items import MMBN3Item, ItemData, item_table, all_items, item_frequencies, items_by_id, ItemType
|
||||
from .Items import MMBN3Item, ItemData, item_table, all_items, item_frequencies, items_by_id, ItemType, item_groups
|
||||
from .Locations import Location, MMBN3Location, all_locations, location_table, location_data_table, \
|
||||
always_excluded_locations, jobs
|
||||
secret_locations, jobs, location_groups
|
||||
from .Options import MMBN3Options
|
||||
from .Regions import regions, RegionName
|
||||
from .Names.ItemName import ItemName
|
||||
from .Names.LocationName import LocationName
|
||||
from worlds.generic.Rules import add_item_rule
|
||||
from worlds.generic.Rules import add_item_rule, add_rule
|
||||
|
||||
|
||||
class MMBN3Settings(settings.Group):
|
||||
@@ -57,12 +57,16 @@ class MMBN3World(World):
|
||||
settings: typing.ClassVar[MMBN3Settings]
|
||||
topology_present = False
|
||||
|
||||
|
||||
item_name_to_id = {name: data.code for name, data in item_table.items()}
|
||||
location_name_to_id = {loc_data.name: loc_data.id for loc_data in all_locations}
|
||||
|
||||
excluded_locations: typing.List[str]
|
||||
excluded_locations: typing.Set[str]
|
||||
item_frequencies: typing.Dict[str, int]
|
||||
|
||||
location_name_groups = location_groups
|
||||
item_name_groups = item_groups
|
||||
|
||||
web = MMBN3Web()
|
||||
|
||||
def generate_early(self) -> None:
|
||||
@@ -74,10 +78,11 @@ class MMBN3World(World):
|
||||
if self.options.extra_ranks > 0:
|
||||
self.item_frequencies[ItemName.Progressive_Undernet_Rank] = 8 + self.options.extra_ranks
|
||||
|
||||
self.excluded_locations = set()
|
||||
if not self.options.include_secret:
|
||||
self.excluded_locations |= secret_locations
|
||||
if not self.options.include_jobs:
|
||||
self.excluded_locations = always_excluded_locations + [job.name for job in jobs]
|
||||
else:
|
||||
self.excluded_locations = always_excluded_locations
|
||||
self.excluded_locations |= {job.name for job in jobs}
|
||||
|
||||
def create_regions(self) -> None:
|
||||
"""
|
||||
@@ -140,19 +145,19 @@ class MMBN3World(World):
|
||||
if connection == RegionName.SciLab_Cyberworld:
|
||||
entrance.access_rule = lambda state: \
|
||||
state.has(ItemName.CSciPas, self.player) or \
|
||||
state.can_reach(RegionName.SciLab_Overworld, "Region", self.player)
|
||||
state.can_reach_region(RegionName.SciLab_Overworld, self.player)
|
||||
self.multiworld.register_indirect_condition(self.get_region(RegionName.SciLab_Overworld), entrance)
|
||||
if connection == RegionName.Yoka_Cyberworld:
|
||||
entrance.access_rule = lambda state: \
|
||||
state.has(ItemName.CYokaPas, self.player) or \
|
||||
(
|
||||
state.can_reach(RegionName.SciLab_Overworld, "Region", self.player) and
|
||||
state.can_reach_region(RegionName.SciLab_Overworld, self.player) and
|
||||
state.has(ItemName.Press, self.player)
|
||||
)
|
||||
self.multiworld.register_indirect_condition(self.get_region(RegionName.SciLab_Overworld), entrance)
|
||||
if connection == RegionName.Beach_Cyberworld:
|
||||
entrance.access_rule = lambda state: state.has(ItemName.CBeacPas, self.player) and\
|
||||
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player)
|
||||
state.can_reach_region(RegionName.Yoka_Overworld, self.player)
|
||||
self.multiworld.register_indirect_condition(self.get_region(RegionName.Yoka_Overworld), entrance)
|
||||
if connection == RegionName.Undernet:
|
||||
entrance.access_rule = lambda state: self.explore_score(state) > 8 and\
|
||||
@@ -198,122 +203,138 @@ class MMBN3World(World):
|
||||
|
||||
# Set WWW ID requirements
|
||||
def has_www_id(state): return state.has(ItemName.WWW_ID, self.player)
|
||||
self.multiworld.get_location(LocationName.ACDC_1_PMD, self.player).access_rule = has_www_id
|
||||
self.multiworld.get_location(LocationName.SciLab_1_WWW_BMD, self.player).access_rule = has_www_id
|
||||
self.multiworld.get_location(LocationName.Yoka_1_WWW_BMD, self.player).access_rule = has_www_id
|
||||
self.multiworld.get_location(LocationName.Undernet_1_WWW_BMD, self.player).access_rule = has_www_id
|
||||
add_rule(self.multiworld.get_location(LocationName.ACDC_1_PMD, self.player), has_www_id)
|
||||
add_rule(self.multiworld.get_location(LocationName.SciLab_1_WWW_BMD, self.player), has_www_id)
|
||||
add_rule(self.multiworld.get_location(LocationName.Yoka_1_WWW_BMD, self.player), has_www_id)
|
||||
add_rule(self.multiworld.get_location(LocationName.Undernet_1_WWW_BMD, self.player), has_www_id)
|
||||
|
||||
# Set Press Program requirements
|
||||
def has_press(state): return state.has(ItemName.Press, self.player)
|
||||
self.multiworld.get_location(LocationName.Yoka_1_PMD, self.player).access_rule = has_press
|
||||
self.multiworld.get_location(LocationName.Yoka_2_Upper_BMD, self.player).access_rule = has_press
|
||||
self.multiworld.get_location(LocationName.Beach_2_East_BMD, self.player).access_rule = has_press
|
||||
self.multiworld.get_location(LocationName.Hades_South_BMD, self.player).access_rule = has_press
|
||||
self.multiworld.get_location(LocationName.Secret_3_BugFrag_BMD, self.player).access_rule = has_press
|
||||
self.multiworld.get_location(LocationName.Secret_3_Island_BMD, self.player).access_rule = has_press
|
||||
add_rule(self.multiworld.get_location(LocationName.Yoka_1_PMD, self.player), has_press)
|
||||
add_rule(self.multiworld.get_location(LocationName.Yoka_2_Upper_BMD, self.player), has_press)
|
||||
add_rule(self.multiworld.get_location(LocationName.Beach_2_East_BMD, self.player), has_press)
|
||||
add_rule(self.multiworld.get_location(LocationName.Hades_South_BMD, self.player), has_press)
|
||||
add_rule(self.multiworld.get_location(LocationName.Secret_3_BugFrag_BMD, self.player), has_press)
|
||||
add_rule(self.multiworld.get_location(LocationName.Secret_3_Island_BMD, self.player), has_press)
|
||||
|
||||
# Set Purple Mystery Data Unlocker access
|
||||
def can_unlock(state): return state.can_reach_region(RegionName.SciLab_Overworld, self.player) or \
|
||||
state.can_reach_region(RegionName.SciLab_Cyberworld, self.player) or \
|
||||
state.can_reach_region(RegionName.Yoka_Cyberworld, self.player) or \
|
||||
state.has(ItemName.Unlocker, self.player, 8) # There are 8 PMDs that aren't in one of the above areas
|
||||
add_rule(self.multiworld.get_location(LocationName.ACDC_1_PMD, self.player), can_unlock)
|
||||
add_rule(self.multiworld.get_location(LocationName.Yoka_1_PMD, self.player), can_unlock)
|
||||
add_rule(self.multiworld.get_location(LocationName.Beach_1_PMD, self.player), can_unlock)
|
||||
add_rule(self.multiworld.get_location(LocationName.Undernet_7_PMD, self.player), can_unlock)
|
||||
add_rule(self.multiworld.get_location(LocationName.Mayls_HP_PMD, self.player), can_unlock)
|
||||
add_rule(self.multiworld.get_location(LocationName.SciLab_Dads_Computer_PMD, self.player), can_unlock)
|
||||
add_rule(self.multiworld.get_location(LocationName.Zoo_Panda_PMD, self.player), can_unlock)
|
||||
add_rule(self.multiworld.get_location(LocationName.Beach_DNN_Security_Panel_PMD, self.player), can_unlock)
|
||||
add_rule(self.multiworld.get_location(LocationName.Beach_DNN_Main_Console_PMD, self.player), can_unlock)
|
||||
add_rule(self.multiworld.get_location(LocationName.Tamakos_HP_PMD, self.player), can_unlock)
|
||||
|
||||
# Set Job additional area access
|
||||
self.multiworld.get_location(LocationName.Please_deliver_this, self.player).access_rule = \
|
||||
lambda state: \
|
||||
state.can_reach(RegionName.ACDC_Overworld, "Region", self.player) and \
|
||||
state.can_reach(RegionName.ACDC_Cyberworld, "Region", self.player)
|
||||
state.can_reach_region(RegionName.ACDC_Overworld, self.player) and \
|
||||
state.can_reach_region(RegionName.ACDC_Cyberworld, self.player)
|
||||
self.multiworld.get_location(LocationName.My_Navi_is_sick, self.player).access_rule =\
|
||||
lambda state: \
|
||||
state.has(ItemName.Recov30_star, self.player)
|
||||
self.multiworld.get_location(LocationName.Help_me_with_my_son, self.player).access_rule =\
|
||||
lambda state:\
|
||||
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player) and \
|
||||
state.can_reach(RegionName.ACDC_Cyberworld, "Region", self.player)
|
||||
state.can_reach_region(RegionName.Yoka_Overworld, self.player) and \
|
||||
state.can_reach_region(RegionName.ACDC_Cyberworld, self.player)
|
||||
self.multiworld.get_location(LocationName.Transmission_error, self.player).access_rule = \
|
||||
lambda state: \
|
||||
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player)
|
||||
state.can_reach_region(RegionName.Yoka_Overworld, self.player)
|
||||
self.multiworld.get_location(LocationName.Chip_Prices, self.player).access_rule = \
|
||||
lambda state: \
|
||||
state.can_reach(RegionName.ACDC_Cyberworld, "Region", self.player) and \
|
||||
state.can_reach(RegionName.SciLab_Cyberworld, "Region", self.player)
|
||||
state.can_reach_region(RegionName.ACDC_Cyberworld, self.player) and \
|
||||
state.can_reach_region(RegionName.SciLab_Cyberworld, self.player)
|
||||
self.multiworld.get_location(LocationName.Im_broke, self.player).access_rule = \
|
||||
lambda state: \
|
||||
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player) and \
|
||||
state.can_reach(RegionName.Yoka_Cyberworld, "Region", self.player)
|
||||
state.can_reach_region(RegionName.Yoka_Overworld, self.player) and \
|
||||
state.can_reach_region(RegionName.Yoka_Cyberworld, self.player)
|
||||
self.multiworld.get_location(LocationName.Rare_chips_for_cheap, self.player).access_rule = \
|
||||
lambda state: \
|
||||
state.can_reach(RegionName.ACDC_Overworld, "Region", self.player)
|
||||
state.can_reach_region(RegionName.ACDC_Overworld, self.player)
|
||||
self.multiworld.get_location(LocationName.Be_my_boyfriend, self.player).access_rule =\
|
||||
lambda state: \
|
||||
state.can_reach(RegionName.Beach_Cyberworld, "Region", self.player)
|
||||
state.can_reach_region(RegionName.Beach_Cyberworld, self.player)
|
||||
self.multiworld.get_location(LocationName.Will_you_deliver, self.player).access_rule=\
|
||||
lambda state: \
|
||||
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player) and \
|
||||
state.can_reach(RegionName.Beach_Overworld, "Region", self.player) and \
|
||||
state.can_reach(RegionName.ACDC_Cyberworld, "Region", self.player)
|
||||
state.can_reach_region(RegionName.Yoka_Overworld, self.player) and \
|
||||
state.can_reach_region(RegionName.Beach_Overworld, self.player) and \
|
||||
state.can_reach_region(RegionName.ACDC_Cyberworld, self.player)
|
||||
self.multiworld.get_location(LocationName.Somebody_please_help, self.player).access_rule = \
|
||||
lambda state: \
|
||||
state.can_reach(RegionName.ACDC_Overworld, "Region", self.player)
|
||||
state.can_reach_region(RegionName.ACDC_Overworld, self.player)
|
||||
self.multiworld.get_location(LocationName.Looking_for_condor, self.player).access_rule = \
|
||||
lambda state: \
|
||||
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player) and \
|
||||
state.can_reach(RegionName.Beach_Overworld, "Region", self.player) and \
|
||||
state.can_reach(RegionName.ACDC_Overworld, "Region", self.player)
|
||||
state.can_reach_region(RegionName.Yoka_Overworld, self.player) and \
|
||||
state.can_reach_region(RegionName.Beach_Overworld, self.player) and \
|
||||
state.can_reach_region(RegionName.ACDC_Overworld, self.player)
|
||||
self.multiworld.get_location(LocationName.Help_with_rehab, self.player).access_rule = \
|
||||
lambda state: \
|
||||
state.can_reach(RegionName.Beach_Overworld, "Region", self.player)
|
||||
state.can_reach_region(RegionName.Beach_Overworld, self.player)
|
||||
self.multiworld.get_location(LocationName.Old_Master, self.player).access_rule = \
|
||||
lambda state: \
|
||||
state.can_reach(RegionName.ACDC_Overworld, "Region", self.player) and \
|
||||
state.can_reach(RegionName.Beach_Overworld, "Region", self.player)
|
||||
state.can_reach_region(RegionName.ACDC_Overworld, self.player) and \
|
||||
state.can_reach_region(RegionName.Beach_Overworld, self.player)
|
||||
self.multiworld.get_location(LocationName.Catching_gang_members, self.player).access_rule = \
|
||||
lambda state: \
|
||||
state.can_reach(RegionName.Yoka_Cyberworld, "Region", self.player) and \
|
||||
state.can_reach_region(RegionName.Yoka_Cyberworld, self.player) and \
|
||||
state.has(ItemName.Press, self.player)
|
||||
self.multiworld.get_location(LocationName.Please_adopt_a_virus, self.player).access_rule = \
|
||||
lambda state: \
|
||||
state.can_reach(RegionName.SciLab_Cyberworld, "Region", self.player)
|
||||
state.can_reach_region(RegionName.SciLab_Cyberworld, self.player)
|
||||
self.multiworld.get_location(LocationName.Legendary_Tomes, self.player).access_rule = \
|
||||
lambda state: \
|
||||
state.can_reach(RegionName.Beach_Overworld, "Region", self.player) and \
|
||||
state.can_reach(RegionName.Undernet, "Region", self.player) and \
|
||||
state.can_reach(RegionName.Deep_Undernet, "Region", self.player) and \
|
||||
state.can_reach_region(RegionName.Beach_Overworld, self.player) and \
|
||||
state.can_reach_region(RegionName.Undernet, self.player) and \
|
||||
state.can_reach_region(RegionName.Deep_Undernet, self.player) and \
|
||||
state.has_all({ItemName.Press, ItemName.Magnum1_A}, self.player)
|
||||
self.multiworld.get_location(LocationName.Legendary_Tomes_Treasure, self.player).access_rule = \
|
||||
lambda state: \
|
||||
state.can_reach(RegionName.ACDC_Overworld, "Region", self.player) and \
|
||||
state.can_reach(LocationName.Legendary_Tomes, "Location", self.player)
|
||||
state.can_reach_region(RegionName.ACDC_Overworld, self.player) and \
|
||||
state.can_reach_location(LocationName.Legendary_Tomes, self.player)
|
||||
self.multiworld.get_location(LocationName.Hide_and_seek_First_Child, self.player).access_rule = \
|
||||
lambda state: \
|
||||
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player)
|
||||
state.can_reach_region(RegionName.Yoka_Overworld, self.player)
|
||||
self.multiworld.get_location(LocationName.Hide_and_seek_Second_Child, self.player).access_rule = \
|
||||
lambda state: \
|
||||
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player)
|
||||
state.can_reach_region(RegionName.Yoka_Overworld, self.player)
|
||||
self.multiworld.get_location(LocationName.Hide_and_seek_Third_Child, self.player).access_rule = \
|
||||
lambda state: \
|
||||
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player)
|
||||
state.can_reach_region(RegionName.Yoka_Overworld, self.player)
|
||||
self.multiworld.get_location(LocationName.Hide_and_seek_Fourth_Child, self.player).access_rule = \
|
||||
lambda state: \
|
||||
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player)
|
||||
state.can_reach_region(RegionName.Yoka_Overworld, self.player)
|
||||
self.multiworld.get_location(LocationName.Hide_and_seek_Completion, self.player).access_rule = \
|
||||
lambda state: \
|
||||
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player)
|
||||
state.can_reach_region(RegionName.Yoka_Overworld, self.player)
|
||||
self.multiworld.get_location(LocationName.Finding_the_blue_Navi, self.player).access_rule = \
|
||||
lambda state: \
|
||||
state.can_reach(RegionName.Undernet, "Region", self.player)
|
||||
state.can_reach_region(RegionName.Undernet, self.player)
|
||||
self.multiworld.get_location(LocationName.Give_your_support, self.player).access_rule = \
|
||||
lambda state: \
|
||||
state.can_reach(RegionName.Beach_Overworld, "Region", self.player)
|
||||
state.can_reach_region(RegionName.Beach_Overworld, self.player)
|
||||
self.multiworld.get_location(LocationName.Stamp_collecting, self.player).access_rule = \
|
||||
lambda state: \
|
||||
state.can_reach(RegionName.Beach_Overworld, "Region", self.player) and \
|
||||
state.can_reach(RegionName.ACDC_Cyberworld, "Region", self.player) and \
|
||||
state.can_reach(RegionName.SciLab_Cyberworld, "Region", self.player) and \
|
||||
state.can_reach(RegionName.Yoka_Cyberworld, "Region", self.player) and \
|
||||
state.can_reach(RegionName.Beach_Cyberworld, "Region", self.player)
|
||||
state.can_reach_region(RegionName.Beach_Overworld, self.player) and \
|
||||
state.can_reach_region(RegionName.ACDC_Cyberworld, self.player) and \
|
||||
state.can_reach_region(RegionName.SciLab_Cyberworld, self.player) and \
|
||||
state.can_reach_region(RegionName.Yoka_Cyberworld, self.player) and \
|
||||
state.can_reach_region(RegionName.Beach_Cyberworld, self.player)
|
||||
self.multiworld.get_location(LocationName.Help_with_a_will, self.player).access_rule = \
|
||||
lambda state: \
|
||||
state.can_reach(RegionName.ACDC_Overworld, "Region", self.player) and \
|
||||
state.can_reach(RegionName.ACDC_Cyberworld, "Region", self.player) and \
|
||||
state.can_reach(RegionName.Yoka_Overworld, "Region", self.player) and \
|
||||
state.can_reach(RegionName.Yoka_Cyberworld, "Region", self.player) and \
|
||||
state.can_reach(RegionName.Beach_Overworld, "Region", self.player) and \
|
||||
state.can_reach(RegionName.Undernet, "Region", self.player)
|
||||
state.can_reach_region(RegionName.ACDC_Overworld, self.player) and \
|
||||
state.can_reach_region(RegionName.ACDC_Cyberworld, self.player) and \
|
||||
state.can_reach_region(RegionName.Yoka_Overworld, self.player) and \
|
||||
state.can_reach_region(RegionName.Yoka_Cyberworld, self.player) and \
|
||||
state.can_reach_region(RegionName.Beach_Overworld, self.player) and \
|
||||
state.can_reach_region(RegionName.Undernet, self.player)
|
||||
|
||||
# Set Trade quests
|
||||
self.multiworld.get_location(LocationName.ACDC_SonicWav_W_Trade, self.player).access_rule =\
|
||||
@@ -390,6 +411,11 @@ class MMBN3World(World):
|
||||
self.multiworld.get_location(LocationName.Numberman_Code_31, self.player).access_rule =\
|
||||
lambda state: self.explore_score(state) > 10
|
||||
|
||||
#miscellaneous locations with extra requirements
|
||||
add_rule(self.multiworld.get_location(LocationName.Comedian, self.player),
|
||||
lambda state: state.has(ItemName.Humor, self.player))
|
||||
add_rule(self.multiworld.get_location(LocationName.Villain, self.player),
|
||||
lambda state: state.has(ItemName.BlckMnd, self.player))
|
||||
def not_undernet(item): return item.code != item_table[ItemName.Progressive_Undernet_Rank].code or item.player != self.player
|
||||
self.multiworld.get_location(LocationName.WWW_1_Central_BMD, self.player).item_rule = not_undernet
|
||||
self.multiworld.get_location(LocationName.WWW_1_East_BMD, self.player).item_rule = not_undernet
|
||||
@@ -500,24 +526,24 @@ class MMBN3World(World):
|
||||
Determine roughly how much of the game you can explore to make certain checks not restrict much movement
|
||||
"""
|
||||
score = 0
|
||||
if state.can_reach(RegionName.WWW_Island, "Region", self.player):
|
||||
if state.can_reach_region(RegionName.WWW_Island, self.player):
|
||||
return 999
|
||||
if state.can_reach(RegionName.SciLab_Overworld, "Region", self.player):
|
||||
if state.can_reach_region(RegionName.SciLab_Overworld, self.player):
|
||||
score += 3
|
||||
if state.can_reach(RegionName.SciLab_Cyberworld, "Region", self.player):
|
||||
if state.can_reach_region(RegionName.SciLab_Cyberworld, self.player):
|
||||
score += 1
|
||||
if state.can_reach(RegionName.Yoka_Overworld, "Region", self.player):
|
||||
if state.can_reach_region(RegionName.Yoka_Overworld, self.player):
|
||||
score += 2
|
||||
if state.can_reach(RegionName.Yoka_Cyberworld, "Region", self.player):
|
||||
if state.can_reach_region(RegionName.Yoka_Cyberworld, self.player):
|
||||
score += 1
|
||||
if state.can_reach(RegionName.Beach_Overworld, "Region", self.player):
|
||||
if state.can_reach_region(RegionName.Beach_Overworld, self.player):
|
||||
score += 3
|
||||
if state.can_reach(RegionName.Beach_Cyberworld, "Region", self.player):
|
||||
if state.can_reach_region(RegionName.Beach_Cyberworld, self.player):
|
||||
score += 1
|
||||
if state.can_reach(RegionName.Undernet, "Region", self.player):
|
||||
if state.can_reach_region(RegionName.Undernet, self.player):
|
||||
score += 2
|
||||
if state.can_reach(RegionName.Deep_Undernet, "Region", self.player):
|
||||
if state.can_reach_region(RegionName.Deep_Undernet, self.player):
|
||||
score += 1
|
||||
if state.can_reach(RegionName.Secret_Area, "Region", self.player):
|
||||
if state.can_reach_region(RegionName.Secret_Area, self.player):
|
||||
score += 1
|
||||
return score
|
||||
|
||||
Binary file not shown.
@@ -6,6 +6,7 @@ class SongData(NamedTuple):
|
||||
"""Special data container to contain the metadata of each song to make filtering work."""
|
||||
|
||||
code: Optional[int]
|
||||
uid: str
|
||||
album: str
|
||||
streamer_mode: bool
|
||||
easy: Optional[int]
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
from .Items import SongData, AlbumData
|
||||
from typing import Dict, List, Set, Optional
|
||||
from .Items import SongData
|
||||
from .MuseDashData import SONG_DATA
|
||||
from typing import Dict, List, Set
|
||||
from collections import ChainMap
|
||||
|
||||
|
||||
def load_text_file(name: str) -> str:
|
||||
import pkgutil
|
||||
return pkgutil.get_data(__name__, name).decode()
|
||||
|
||||
|
||||
class MuseDashCollections:
|
||||
"""Contains all the data of Muse Dash, loaded from MuseDashData.txt."""
|
||||
STARTING_CODE = 2900000
|
||||
@@ -33,15 +29,6 @@ class MuseDashCollections:
|
||||
"Rin Len's Mirrorland", # Paid DLC not included in Muse Plus
|
||||
]
|
||||
|
||||
DIFF_OVERRIDES: List[str] = [
|
||||
"MuseDash ka nanika hi",
|
||||
"Rush-Hour",
|
||||
"Find this Month's Featured Playlist",
|
||||
"PeroPero in the Universe",
|
||||
"umpopoff",
|
||||
"P E R O P E R O Brother Dance",
|
||||
]
|
||||
|
||||
REMOVED_SONGS = [
|
||||
"CHAOS Glitch",
|
||||
"FM 17314 SUGAR RADIO",
|
||||
@@ -50,9 +37,7 @@ class MuseDashCollections:
|
||||
"Tsukuyomi Ni Naru Replaced",
|
||||
]
|
||||
|
||||
album_items: Dict[str, AlbumData] = {}
|
||||
album_locations: Dict[str, int] = {}
|
||||
song_items: Dict[str, SongData] = {}
|
||||
song_items = SONG_DATA
|
||||
song_locations: Dict[str, int] = {}
|
||||
|
||||
trap_items: Dict[str, int] = {
|
||||
@@ -65,7 +50,7 @@ class MuseDashCollections:
|
||||
"Gray Scale Trap": STARTING_CODE + 7,
|
||||
"Nyaa SFX Trap": STARTING_CODE + 8,
|
||||
"Error SFX Trap": STARTING_CODE + 9,
|
||||
"Focus Line Trap": STARTING_CODE + 10,
|
||||
"Focus Line Trap": STARTING_CODE + 10,
|
||||
}
|
||||
|
||||
sfx_trap_items: List[str] = [
|
||||
@@ -85,65 +70,13 @@ class MuseDashCollections:
|
||||
"Extra Life": 1,
|
||||
}
|
||||
|
||||
item_names_to_id: ChainMap = ChainMap({}, filler_items, trap_items)
|
||||
location_names_to_id: ChainMap = ChainMap(song_locations, album_locations)
|
||||
item_names_to_id: ChainMap = ChainMap({k: v.code for k, v in SONG_DATA.items()}, filler_items, trap_items)
|
||||
location_names_to_id: ChainMap = ChainMap(song_locations)
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.item_names_to_id[self.MUSIC_SHEET_NAME] = self.MUSIC_SHEET_CODE
|
||||
|
||||
item_id_index = self.STARTING_CODE + 50
|
||||
full_file = load_text_file("MuseDashData.txt")
|
||||
seen_albums = set()
|
||||
for line in full_file.splitlines():
|
||||
line = line.strip()
|
||||
sections = line.split("|")
|
||||
|
||||
album = sections[2]
|
||||
if album not in seen_albums:
|
||||
seen_albums.add(album)
|
||||
self.album_items[album] = AlbumData(item_id_index)
|
||||
item_id_index += 1
|
||||
|
||||
# Data is in the format 'Song|UID|Album|StreamerMode|EasyDiff|HardDiff|MasterDiff|SecretDiff'
|
||||
song_name = sections[0]
|
||||
# [1] is used in the client copy to make sure item id's match.
|
||||
steamer_mode = sections[3] == "True"
|
||||
|
||||
if song_name in self.DIFF_OVERRIDES:
|
||||
# These songs use non-standard difficulty values. Which are being overriden with standard values.
|
||||
# But also avoid filling any missing difficulties (i.e. 0s) with a difficulty value.
|
||||
if sections[4] != '0':
|
||||
diff_of_easy = 4
|
||||
else:
|
||||
diff_of_easy = None
|
||||
|
||||
if sections[5] != '0':
|
||||
diff_of_hard = 7
|
||||
else:
|
||||
diff_of_hard = None
|
||||
|
||||
if sections[6] != '0':
|
||||
diff_of_master = 10
|
||||
else:
|
||||
diff_of_master = None
|
||||
else:
|
||||
diff_of_easy = self.parse_song_difficulty(sections[4])
|
||||
diff_of_hard = self.parse_song_difficulty(sections[5])
|
||||
diff_of_master = self.parse_song_difficulty(sections[6])
|
||||
|
||||
self.song_items[song_name] = SongData(item_id_index, album, steamer_mode,
|
||||
diff_of_easy, diff_of_hard, diff_of_master)
|
||||
item_id_index += 1
|
||||
|
||||
self.item_names_to_id.update({name: data.code for name, data in self.song_items.items()})
|
||||
self.item_names_to_id.update({name: data.code for name, data in self.album_items.items()})
|
||||
|
||||
location_id_index = self.STARTING_CODE
|
||||
for name in self.album_items.keys():
|
||||
self.album_locations[f"{name}-0"] = location_id_index
|
||||
self.album_locations[f"{name}-1"] = location_id_index + 1
|
||||
location_id_index += 2
|
||||
|
||||
for name in self.song_items.keys():
|
||||
self.song_locations[f"{name}-0"] = location_id_index
|
||||
self.song_locations[f"{name}-1"] = location_id_index + 1
|
||||
@@ -157,7 +90,7 @@ class MuseDashCollections:
|
||||
for songKey, songData in self.song_items.items():
|
||||
if not self.song_matches_dlc_filter(songData, dlc_songs):
|
||||
continue
|
||||
|
||||
|
||||
if songKey in self.REMOVED_SONGS:
|
||||
continue
|
||||
|
||||
@@ -193,18 +126,3 @@ class MuseDashCollections:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def parse_song_difficulty(self, difficulty: str) -> Optional[int]:
|
||||
"""Attempts to parse the song difficulty."""
|
||||
if len(difficulty) <= 0 or difficulty == "?" or difficulty == "¿":
|
||||
return None
|
||||
|
||||
# 0 is used as a filler and no songs actually have a 0 difficulty song.
|
||||
if difficulty == "0":
|
||||
return None
|
||||
|
||||
# Curse the 2023 april fools update. Used on 3rd Avenue.
|
||||
if difficulty == "〇":
|
||||
return 10
|
||||
|
||||
return int(difficulty)
|
||||
|
||||
615
worlds/musedash/MuseDashData.py
Normal file
615
worlds/musedash/MuseDashData.py
Normal file
@@ -0,0 +1,615 @@
|
||||
from .Items import SongData
|
||||
from typing import Dict
|
||||
|
||||
|
||||
# Auto Generated
|
||||
SONG_DATA: Dict[str, SongData] = {
|
||||
"Magical Wonderland": SongData(2900051, "0-48", "Default Music", True, 1, 3, None),
|
||||
"Iyaiya": SongData(2900052, "0-0", "Default Music", True, 1, 4, None),
|
||||
"Wonderful Pain": SongData(2900053, "0-2", "Default Music", False, 1, 3, None),
|
||||
"Breaking Dawn": SongData(2900054, "0-3", "Default Music", True, 2, 4, None),
|
||||
"One-Way Subway": SongData(2900055, "0-4", "Default Music", True, 1, 4, None),
|
||||
"Frost Land": SongData(2900056, "0-1", "Default Music", False, 1, 3, 6),
|
||||
"Heart-Pounding Flight": SongData(2900057, "0-5", "Default Music", True, 2, 5, None),
|
||||
"Pancake is Love": SongData(2900058, "0-29", "Default Music", True, 2, 4, 7),
|
||||
"Shiguang Tuya": SongData(2900059, "0-6", "Default Music", True, 2, 5, None),
|
||||
"Evolution": SongData(2900060, "0-37", "Default Music", False, 2, 4, 7),
|
||||
"Dolphin and Broadcast": SongData(2900061, "0-7", "Default Music", True, 2, 5, None),
|
||||
"Yuki no Shizuku Ame no Oto": SongData(2900062, "0-8", "Default Music", True, 2, 4, 6),
|
||||
"Best One feat.tooko": SongData(2900063, "0-43", "Default Music", False, 3, 5, None),
|
||||
"Candy-coloured Love Theory": SongData(2900064, "0-31", "Default Music", False, 2, 4, 6),
|
||||
"Night Wander": SongData(2900065, "0-38", "Default Music", False, 3, 5, 7),
|
||||
"Dohna Dohna no Uta": SongData(2900066, "0-46", "Default Music", False, 2, 4, 6),
|
||||
"Spring Carnival": SongData(2900067, "0-9", "Default Music", False, 2, 4, 7),
|
||||
"DISCO NIGHT": SongData(2900068, "0-30", "Default Music", True, 2, 4, 7),
|
||||
"Koi no Moonlight": SongData(2900069, "0-49", "Default Music", False, 2, 5, 8),
|
||||
"Lian Ai Audio Navigation": SongData(2900070, "0-10", "Default Music", False, 3, 5, 7),
|
||||
"Lights of Muse": SongData(2900071, "0-11", "Default Music", True, 4, 6, 8),
|
||||
"midstream jam": SongData(2900072, "0-12", "Default Music", False, 2, 5, 8),
|
||||
"Nihao": SongData(2900073, "0-40", "Default Music", False, 3, 5, 7),
|
||||
"Confession": SongData(2900074, "0-13", "Default Music", False, 3, 5, 8),
|
||||
"Galaxy Striker": SongData(2900075, "0-32", "Default Music", False, 4, 7, 9),
|
||||
"Departure Road": SongData(2900076, "0-14", "Default Music", True, 2, 5, 8),
|
||||
"Bass Telekinesis": SongData(2900077, "0-15", "Default Music", False, 2, 5, 8),
|
||||
"Cage of Almeria": SongData(2900078, "0-16", "Default Music", True, 3, 5, 7),
|
||||
"Ira": SongData(2900079, "0-17", "Default Music", True, 4, 6, 8),
|
||||
"Blackest Luxury Car": SongData(2900080, "0-18", "Default Music", True, 3, 6, 8),
|
||||
"Medicine of Sing": SongData(2900081, "0-19", "Default Music", False, 3, 6, 8),
|
||||
"irregulyze": SongData(2900082, "0-20", "Default Music", True, 3, 6, 8),
|
||||
"I don't care about Christmas though": SongData(2900083, "0-47", "Default Music", False, 4, 6, 8),
|
||||
"Imaginary World": SongData(2900084, "0-21", "Default Music", True, 4, 6, 8),
|
||||
"Dysthymia": SongData(2900085, "0-22", "Default Music", True, 4, 7, 9),
|
||||
"From the New World": SongData(2900086, "0-42", "Default Music", False, 2, 5, 7),
|
||||
"NISEGAO": SongData(2900087, "0-33", "Default Music", True, 4, 7, 9),
|
||||
"Say! Fanfare!": SongData(2900088, "0-44", "Default Music", False, 4, 6, 9),
|
||||
"Star Driver": SongData(2900089, "0-34", "Default Music", True, 5, 7, 9),
|
||||
"Formation": SongData(2900090, "0-23", "Default Music", True, 4, 6, 9),
|
||||
"Shinsou Masui": SongData(2900091, "0-24", "Default Music", True, 4, 6, 10),
|
||||
"Mezame Eurythmics": SongData(2900092, "0-50", "Default Music", False, 4, 6, 9),
|
||||
"Shenri Kuaira -repeat-": SongData(2900093, "0-51", "Default Music", False, 5, 7, 9),
|
||||
"Latitude": SongData(2900094, "0-25", "Default Music", True, 3, 6, 9),
|
||||
"Aqua Stars": SongData(2900095, "0-39", "Default Music", False, 5, 7, 10),
|
||||
"Funkotsu Saishin Casino": SongData(2900096, "0-26", "Default Music", False, 5, 7, 10),
|
||||
"Clock Room & Spiritual World": SongData(2900097, "0-27", "Default Music", True, 4, 6, 9),
|
||||
"INTERNET OVERDOSE": SongData(2900098, "0-52", "Default Music", False, 3, 6, 9),
|
||||
"Tu Hua": SongData(2900099, "0-35", "Default Music", True, 4, 7, 9),
|
||||
"Mujinku-Vacuum": SongData(2900100, "0-28", "Default Music", False, 5, 7, 11),
|
||||
"MilK": SongData(2900101, "0-36", "Default Music", False, 5, 7, 9),
|
||||
"umpopoff": SongData(2900102, "0-41", "Default Music", False, None, 7, None),
|
||||
"Mopemope": SongData(2900103, "0-45", "Default Music", False, 4, 7, 9),
|
||||
"The Happycore Idol": SongData(2900105, "43-0", "MD Plus Project", True, 2, 5, 7),
|
||||
"Amatsumikaboshi": SongData(2900106, "43-1", "MD Plus Project", True, 4, 6, 8),
|
||||
"ARIGA THESIS": SongData(2900107, "43-2", "MD Plus Project", True, 3, 6, 10),
|
||||
"Night of Nights": SongData(2900108, "43-3", "MD Plus Project", False, 4, 7, 10),
|
||||
"#Psychedelic_Meguro_River": SongData(2900109, "43-4", "MD Plus Project", False, 3, 6, 8),
|
||||
"can you feel it": SongData(2900110, "43-5", "MD Plus Project", False, 4, 6, 8),
|
||||
"Midnight O'clock": SongData(2900111, "43-6", "MD Plus Project", True, 3, 6, 8),
|
||||
"Rin": SongData(2900112, "43-7", "MD Plus Project", True, 5, 7, 10),
|
||||
"Smile-mileS": SongData(2900113, "43-8", "MD Plus Project", False, 6, 8, 10),
|
||||
"Believing and Being": SongData(2900114, "43-9", "MD Plus Project", True, 4, 6, 9),
|
||||
"Catalyst": SongData(2900115, "43-10", "MD Plus Project", False, 5, 7, 9),
|
||||
"don't!stop!eroero!": SongData(2900116, "43-11", "MD Plus Project", True, 5, 7, 9),
|
||||
"pa pi pu pi pu pi pa": SongData(2900117, "43-12", "MD Plus Project", False, 6, 8, 10),
|
||||
"Sand Maze": SongData(2900118, "43-13", "MD Plus Project", True, 6, 8, 10),
|
||||
"Diffraction": SongData(2900119, "43-14", "MD Plus Project", True, 5, 8, 10),
|
||||
"AKUMU": SongData(2900120, "43-15", "MD Plus Project", False, 4, 6, 8),
|
||||
"Queen Aluett": SongData(2900121, "43-16", "MD Plus Project", True, 7, 9, 11),
|
||||
"DROPS": SongData(2900122, "43-17", "MD Plus Project", False, 2, 5, 8),
|
||||
"Frightfully-insane Flan-chan's frightful song": SongData(2900123, "43-18", "MD Plus Project", False, 5, 7, 10),
|
||||
"snooze": SongData(2900124, "43-19", "MD Plus Project", False, 5, 7, 10),
|
||||
"Kuishinbo Hacker feat.Kuishinbo Akachan": SongData(2900125, "43-20", "MD Plus Project", True, 5, 7, 9),
|
||||
"Inu no outa": SongData(2900126, "43-21", "MD Plus Project", True, 3, 5, 7),
|
||||
"Prism Fountain": SongData(2900127, "43-22", "MD Plus Project", True, 7, 9, 11),
|
||||
"Gospel": SongData(2900128, "43-23", "MD Plus Project", False, 4, 6, 9),
|
||||
"East Ai Li Lovely": SongData(2900130, "62-0", "Happy Otaku Pack Vol.17", False, 2, 4, 7),
|
||||
"Mori Umi no Fune": SongData(2900131, "62-1", "Happy Otaku Pack Vol.17", True, 5, 7, 9),
|
||||
"Ooi": SongData(2900132, "62-2", "Happy Otaku Pack Vol.17", True, 5, 7, 10),
|
||||
"Numatta!!": SongData(2900133, "62-3", "Happy Otaku Pack Vol.17", True, 5, 7, 9),
|
||||
"SATELLITE": SongData(2900134, "62-4", "Happy Otaku Pack Vol.17", False, 5, 7, 9),
|
||||
"Fantasia Sonata Colorful feat. V!C": SongData(2900135, "62-5", "Happy Otaku Pack Vol.17", True, 6, 8, 11),
|
||||
"MuseDash ka nanika hi": SongData(2900137, "61-0", "Ola Dash", True, 4, 7, 10),
|
||||
"Aleph-0": SongData(2900138, "61-1", "Ola Dash", True, 7, 9, 11),
|
||||
"Buttoba Supernova": SongData(2900139, "61-2", "Ola Dash", False, 5, 7, 10),
|
||||
"Rush-Hour": SongData(2900140, "61-3", "Ola Dash", False, 4, 7, 10),
|
||||
"3rd Avenue": SongData(2900141, "61-4", "Ola Dash", False, 3, 5, 10),
|
||||
"WORLDINVADER": SongData(2900142, "61-5", "Ola Dash", True, 5, 8, 10),
|
||||
"N3V3R G3T OV3R": SongData(2900144, "60-0", "maimai DX Limited-time Suite", True, 4, 7, 10),
|
||||
"Oshama Scramble!": SongData(2900145, "60-1", "maimai DX Limited-time Suite", True, 5, 7, 10),
|
||||
"Valsqotch": SongData(2900146, "60-2", "maimai DX Limited-time Suite", True, 5, 9, 11),
|
||||
"Paranormal My Mind": SongData(2900147, "60-3", "maimai DX Limited-time Suite", True, 5, 7, 9),
|
||||
"Flower, snow and Drum'n'bass.": SongData(2900148, "60-4", "maimai DX Limited-time Suite", True, 5, 8, 10),
|
||||
"Amenohoakari": SongData(2900149, "60-5", "maimai DX Limited-time Suite", True, 6, 8, 10),
|
||||
"Boiling Blood": SongData(2900151, "59-0", "MSR Anthology", True, 5, 8, 10),
|
||||
"ManiFesto": SongData(2900152, "59-1", "MSR Anthology", True, 4, 6, 9),
|
||||
"Operation Blade": SongData(2900153, "59-2", "MSR Anthology", True, 3, 5, 7),
|
||||
"Radiant": SongData(2900154, "59-3", "MSR Anthology", True, 3, 5, 8),
|
||||
"Renegade": SongData(2900155, "59-4", "MSR Anthology", True, 3, 5, 8),
|
||||
"Speed of Light": SongData(2900156, "59-5", "MSR Anthology", False, 1, 4, 7),
|
||||
"Dossoles Holiday": SongData(2900157, "59-6", "MSR Anthology", True, 5, 7, 9),
|
||||
"Autumn Moods": SongData(2900158, "59-7", "MSR Anthology", True, 3, 5, 7),
|
||||
"People People": SongData(2900160, "58-0", "Nanahira Paradise", True, 5, 7, 9),
|
||||
"Endless Error Loop": SongData(2900161, "58-1", "Nanahira Paradise", True, 4, 7, 9),
|
||||
"Forbidden Pizza!": SongData(2900162, "58-2", "Nanahira Paradise", True, 5, 7, 9),
|
||||
"Don't Make the Vocalist do Anything Insane": SongData(2900163, "58-3", "Nanahira Paradise", True, 5, 8, 9),
|
||||
"Tokimeki*Meteostrike": SongData(2900165, "57-0", "Happy Otaku Pack Vol.16", True, 3, 6, 8),
|
||||
"Down Low": SongData(2900166, "57-1", "Happy Otaku Pack Vol.16", True, 4, 6, 8),
|
||||
"LOUDER MACHINE": SongData(2900167, "57-2", "Happy Otaku Pack Vol.16", True, 5, 7, 9),
|
||||
"Sorewa mo Lovechu": SongData(2900168, "57-3", "Happy Otaku Pack Vol.16", True, 5, 7, 10),
|
||||
"Rave_Tech": SongData(2900169, "57-4", "Happy Otaku Pack Vol.16", True, 5, 8, 10),
|
||||
"Brilliant & Shining!": SongData(2900170, "57-5", "Happy Otaku Pack Vol.16", False, 5, 8, 10),
|
||||
"Psyched Fevereiro": SongData(2900172, "56-0", "Give Up TREATMENT Vol.11", False, 5, 8, 10),
|
||||
"Inferno City": SongData(2900173, "56-1", "Give Up TREATMENT Vol.11", False, 6, 8, 10),
|
||||
"Paradigm Shift": SongData(2900174, "56-2", "Give Up TREATMENT Vol.11", False, 4, 7, 10),
|
||||
"Snapdragon": SongData(2900175, "56-3", "Give Up TREATMENT Vol.11", False, 5, 7, 10),
|
||||
"Prestige and Vestige": SongData(2900176, "56-4", "Give Up TREATMENT Vol.11", True, 6, 8, 11),
|
||||
"Tiny Fate": SongData(2900177, "56-5", "Give Up TREATMENT Vol.11", False, 7, 9, 11),
|
||||
"Tsuki ni Murakumo Hana ni Kaze": SongData(2900179, "55-0", "Touhou Mugakudan -II-", False, 3, 5, 7),
|
||||
"Patchouli's - Best Hit GSK": SongData(2900180, "55-1", "Touhou Mugakudan -II-", False, 3, 5, 8),
|
||||
"Monosugoi Space Shuttle de Koishi ga Monosugoi uta": SongData(2900181, "55-2", "Touhou Mugakudan -II-", False, 3, 5, 7),
|
||||
"Kakoinaki Yo wa Ichigo no Tsukikage": SongData(2900182, "55-3", "Touhou Mugakudan -II-", False, 3, 6, 8),
|
||||
"Psychedelic Kizakura Doumei": SongData(2900183, "55-4", "Touhou Mugakudan -II-", False, 4, 7, 10),
|
||||
"Mischievous Sensation": SongData(2900184, "55-5", "Touhou Mugakudan -II-", False, 5, 7, 9),
|
||||
"White Canvas": SongData(2900186, "54-0", "MEGAREX THE FUTURE", False, 3, 6, 8),
|
||||
"Gloomy Flash": SongData(2900187, "54-1", "MEGAREX THE FUTURE", False, 5, 8, 10),
|
||||
"Find this Month's Featured Playlist": SongData(2900188, "54-2", "MEGAREX THE FUTURE", False, 4, 7, 10),
|
||||
"Sunday Night": SongData(2900189, "54-3", "MEGAREX THE FUTURE", False, 3, 6, 9),
|
||||
"Goodbye Goodnight": SongData(2900190, "54-4", "MEGAREX THE FUTURE", False, 4, 6, 9),
|
||||
"ENDLESS CIDER": SongData(2900191, "54-5", "MEGAREX THE FUTURE", False, 4, 6, 8),
|
||||
"On And On!!": SongData(2900193, "53-0", "Happy Otaku Pack Vol.15", True, 4, 7, 9),
|
||||
"Trip!": SongData(2900194, "53-1", "Happy Otaku Pack Vol.15", True, 3, 5, 7),
|
||||
"Hoshi no otoshimono": SongData(2900195, "53-2", "Happy Otaku Pack Vol.15", False, 5, 7, 9),
|
||||
"Plucky Race": SongData(2900196, "53-3", "Happy Otaku Pack Vol.15", True, 5, 8, 10),
|
||||
"Fantasia Sonata Destiny": SongData(2900197, "53-4", "Happy Otaku Pack Vol.15", True, 3, 7, 10),
|
||||
"Run through": SongData(2900198, "53-5", "Happy Otaku Pack Vol.15", False, 5, 8, 10),
|
||||
"marooned night": SongData(2900200, "52-0", "MUSE RADIO FM103", False, 2, 4, 6),
|
||||
"daydream girl": SongData(2900201, "52-1", "MUSE RADIO FM103", False, 3, 6, 8),
|
||||
"Not Ornament": SongData(2900202, "52-2", "MUSE RADIO FM103", True, 3, 5, 8),
|
||||
"Baby Pink": SongData(2900203, "52-3", "MUSE RADIO FM103", False, 3, 5, 8),
|
||||
"I'm Here": SongData(2900204, "52-4", "MUSE RADIO FM103", False, 4, 6, 8),
|
||||
"Masquerade Diary": SongData(2900206, "51-0", "Virtual Idol Production", True, 2, 5, 8),
|
||||
"Reminiscence": SongData(2900207, "51-1", "Virtual Idol Production", True, 5, 7, 9),
|
||||
"DarakuDatenshi": SongData(2900208, "51-2", "Virtual Idol Production", True, 3, 6, 9),
|
||||
"D.I.Y.": SongData(2900209, "51-3", "Virtual Idol Production", False, 4, 6, 9),
|
||||
"Boys in Virtual Land": SongData(2900210, "51-4", "Virtual Idol Production", False, 4, 7, 9),
|
||||
"kui": SongData(2900211, "51-5", "Virtual Idol Production", True, 5, 7, 9),
|
||||
"Nyan Cat": SongData(2900213, "50-0", "Nyanya Universe!", False, 4, 7, 9),
|
||||
"PeroPero in the Universe": SongData(2900214, "50-1", "Nyanya Universe!", True, 4, 7, 10),
|
||||
"In-kya Yo-kya Onmyoji": SongData(2900215, "50-2", "Nyanya Universe!", False, 6, 8, 10),
|
||||
"KABOOOOOM!!!!": SongData(2900216, "50-3", "Nyanya Universe!", True, 4, 6, 8),
|
||||
"Doppelganger": SongData(2900217, "50-4", "Nyanya Universe!", True, 5, 7, 9),
|
||||
"Pray a LOVE": SongData(2900219, "49-0", "DokiDoki! Valentine!", False, 2, 5, 8),
|
||||
"Love-Avoidance Addiction": SongData(2900220, "49-1", "DokiDoki! Valentine!", False, 3, 5, 7),
|
||||
"Daisuki Dayo feat.Wotoha": SongData(2900221, "49-2", "DokiDoki! Valentine!", False, 5, 7, 10),
|
||||
"glory day": SongData(2900223, "48-0", "DJMAX Reflect", False, 2, 5, 7),
|
||||
"Bright Dream": SongData(2900224, "48-1", "DJMAX Reflect", False, 2, 4, 7),
|
||||
"Groovin Up": SongData(2900225, "48-2", "DJMAX Reflect", False, 4, 6, 8),
|
||||
"I Want You": SongData(2900226, "48-3", "DJMAX Reflect", False, 3, 6, 8),
|
||||
"OBLIVION": SongData(2900227, "48-4", "DJMAX Reflect", False, 3, 6, 9),
|
||||
"Elastic STAR": SongData(2900228, "48-5", "DJMAX Reflect", False, 4, 6, 8),
|
||||
"U.A.D": SongData(2900229, "48-6", "DJMAX Reflect", False, 4, 6, 8),
|
||||
"Jealousy": SongData(2900230, "48-7", "DJMAX Reflect", False, 3, 5, 7),
|
||||
"Memory of Beach": SongData(2900231, "48-8", "DJMAX Reflect", False, 3, 6, 8),
|
||||
"Don't Die": SongData(2900232, "48-9", "DJMAX Reflect", False, 6, 8, 10),
|
||||
"Y CE Ver.": SongData(2900233, "48-10", "DJMAX Reflect", False, 4, 6, 9),
|
||||
"Fancy Night": SongData(2900234, "48-11", "DJMAX Reflect", False, 4, 6, 8),
|
||||
"Can We Talk": SongData(2900235, "48-12", "DJMAX Reflect", False, 4, 6, 8),
|
||||
"Give Me 5": SongData(2900236, "48-13", "DJMAX Reflect", False, 2, 6, 8),
|
||||
"Nightmare": SongData(2900237, "48-14", "DJMAX Reflect", False, 7, 9, 11),
|
||||
"Haze of Autumn": SongData(2900239, "47-0", "Arcaea", True, 3, 6, 9),
|
||||
"GIMME DA BLOOD": SongData(2900240, "47-1", "Arcaea", False, 3, 6, 9),
|
||||
"Libertas": SongData(2900241, "47-2", "Arcaea", False, 4, 7, 10),
|
||||
"Cyaegha": SongData(2900242, "47-3", "Arcaea", False, 5, 7, 9),
|
||||
"Bang!!": SongData(2900244, "46-0", "Happy Otaku Pack Vol.14", False, 4, 6, 8),
|
||||
"Paradise 2": SongData(2900245, "46-1", "Happy Otaku Pack Vol.14", False, 4, 6, 8),
|
||||
"Symbol": SongData(2900246, "46-2", "Happy Otaku Pack Vol.14", False, 5, 7, 9),
|
||||
"Nekojarashi": SongData(2900247, "46-3", "Happy Otaku Pack Vol.14", False, 5, 8, 10),
|
||||
"A Philosophical Wanderer": SongData(2900248, "46-4", "Happy Otaku Pack Vol.14", False, 4, 6, 10),
|
||||
"Isouten": SongData(2900249, "46-5", "Happy Otaku Pack Vol.14", True, 6, 8, 10),
|
||||
"ONOMATO Pairing!!!": SongData(2900251, "45-0", "WACCA Horizon", False, 4, 6, 9),
|
||||
"with U": SongData(2900252, "45-1", "WACCA Horizon", False, 6, 8, 10),
|
||||
"Chariot": SongData(2900253, "45-2", "WACCA Horizon", False, 3, 6, 9),
|
||||
"GASHATT": SongData(2900254, "45-3", "WACCA Horizon", False, 5, 7, 10),
|
||||
"LIN NE KRO NE feat. lasah": SongData(2900255, "45-4", "WACCA Horizon", False, 6, 8, 10),
|
||||
"ANGEL HALO": SongData(2900256, "45-5", "WACCA Horizon", False, 5, 8, 11),
|
||||
"Party in the HOLLOWood": SongData(2900258, "44-0", "Happy Otaku Pack Vol.13", False, 3, 6, 8),
|
||||
"Ying Ying da Zuozhan": SongData(2900259, "44-1", "Happy Otaku Pack Vol.13", True, 5, 7, 9),
|
||||
"Howlin' Pumpkin": SongData(2900260, "44-2", "Happy Otaku Pack Vol.13", True, 4, 6, 8),
|
||||
"Bad Apple!! feat. Nomico": SongData(2900262, "42-0", "Touhou Mugakudan -I-", False, 1, 3, 6),
|
||||
"Iro wa Nioedo, Chirinuru wo": SongData(2900263, "42-1", "Touhou Mugakudan -I-", False, 2, 4, 7),
|
||||
"Cirno's Perfect Math Class": SongData(2900264, "42-2", "Touhou Mugakudan -I-", False, 4, 7, 9),
|
||||
"Hiiro Gekka Kyousai no Zetsu": SongData(2900265, "42-3", "Touhou Mugakudan -I-", False, 4, 6, 8),
|
||||
"Flowery Moonlit Night": SongData(2900266, "42-4", "Touhou Mugakudan -I-", False, 3, 6, 8),
|
||||
"Unconscious Requiem": SongData(2900267, "42-5", "Touhou Mugakudan -I-", False, 3, 6, 8),
|
||||
"Super Battleworn Insomniac": SongData(2900269, "41-0", "7th Beat Games", True, 4, 7, 9),
|
||||
"Bomb-Sniffing Pomeranian": SongData(2900270, "41-1", "7th Beat Games", True, 4, 6, 8),
|
||||
"Rollerdisco Rumble": SongData(2900271, "41-2", "7th Beat Games", True, 4, 6, 9),
|
||||
"Rose Garden": SongData(2900272, "41-3", "7th Beat Games", False, 5, 8, 9),
|
||||
"EMOMOMO": SongData(2900273, "41-4", "7th Beat Games", True, 4, 7, 10),
|
||||
"Heracles": SongData(2900274, "41-5", "7th Beat Games", False, 6, 8, 10),
|
||||
"Rush-More": SongData(2900276, "40-0", "Happy Otaku Pack Vol.12", False, 4, 7, 9),
|
||||
"Kill My Fortune": SongData(2900277, "40-1", "Happy Otaku Pack Vol.12", False, 5, 7, 10),
|
||||
"Yosari Tsukibotaru Suminoborite": SongData(2900278, "40-2", "Happy Otaku Pack Vol.12", False, 5, 7, 9),
|
||||
"JUMP! HardCandy": SongData(2900279, "40-3", "Happy Otaku Pack Vol.12", False, 3, 6, 8),
|
||||
"Hibari": SongData(2900280, "40-4", "Happy Otaku Pack Vol.12", False, 3, 5, 8),
|
||||
"OCCHOCO-REST-LESS": SongData(2900281, "40-5", "Happy Otaku Pack Vol.12", True, 4, 7, 9),
|
||||
"See-Saw Day": SongData(2900283, "39-0", "MUSE RADIO FM102", True, 1, 3, 6),
|
||||
"happy hour": SongData(2900284, "39-1", "MUSE RADIO FM102", True, 2, 4, 7),
|
||||
"Seikimatsu no Natsu": SongData(2900285, "39-2", "MUSE RADIO FM102", True, 4, 6, 8),
|
||||
"twinkle night": SongData(2900286, "39-3", "MUSE RADIO FM102", False, 3, 6, 8),
|
||||
"ARUYA HARERUYA": SongData(2900287, "39-4", "MUSE RADIO FM102", False, 2, 5, 7),
|
||||
"Blush": SongData(2900288, "39-5", "MUSE RADIO FM102", False, 2, 4, 7),
|
||||
"Naked Summer": SongData(2900289, "39-6", "MUSE RADIO FM102", True, 4, 6, 8),
|
||||
"BLESS ME": SongData(2900290, "39-7", "MUSE RADIO FM102", True, 2, 5, 7),
|
||||
"FM 17314 SUGAR RADIO": SongData(2900291, "39-8", "MUSE RADIO FM102", True, None, None, None),
|
||||
"NO ONE YES MAN": SongData(2900293, "38-0", "Phigros", False, 5, 7, 9),
|
||||
"Snowfall, Merry Christmas": SongData(2900294, "38-1", "Phigros", False, 5, 8, 10),
|
||||
"Igallta": SongData(2900295, "38-2", "Phigros", False, 6, 8, 10),
|
||||
"Colored Glass": SongData(2900297, "37-0", "Cute Is Everything Vol.7", False, 1, 4, 7),
|
||||
"Neonlights": SongData(2900298, "37-1", "Cute Is Everything Vol.7", False, 4, 7, 9),
|
||||
"Hope for the flowers": SongData(2900299, "37-2", "Cute Is Everything Vol.7", False, 4, 7, 9),
|
||||
"Seaside Cycling on May 30": SongData(2900300, "37-3", "Cute Is Everything Vol.7", False, 3, 6, 8),
|
||||
"SKY HIGH": SongData(2900301, "37-4", "Cute Is Everything Vol.7", False, 2, 4, 6),
|
||||
"Mousou Chu!!": SongData(2900302, "37-5", "Cute Is Everything Vol.7", False, 4, 7, 8),
|
||||
"NightTheater": SongData(2900304, "36-0", "Give Up TREATMENT Vol.10", True, 6, 8, 11),
|
||||
"Cutter": SongData(2900305, "36-1", "Give Up TREATMENT Vol.10", False, 4, 7, 10),
|
||||
"bamboo": SongData(2900306, "36-2", "Give Up TREATMENT Vol.10", False, 6, 8, 10),
|
||||
"enchanted love": SongData(2900307, "36-3", "Give Up TREATMENT Vol.10", False, 2, 6, 9),
|
||||
"c.s.q.n.": SongData(2900308, "36-4", "Give Up TREATMENT Vol.10", False, 5, 8, 11),
|
||||
"Booouncing!!": SongData(2900309, "36-5", "Give Up TREATMENT Vol.10", False, 5, 7, 10),
|
||||
"PeroPeroGames goes Bankrupt": SongData(2900311, "35-0", "Happy Otaku Pack SP", True, 6, 8, 10),
|
||||
"MARENOL": SongData(2900312, "35-1", "Happy Otaku Pack SP", False, 4, 7, 10),
|
||||
"I am really good at Japanese style": SongData(2900313, "35-2", "Happy Otaku Pack SP", True, 6, 8, 10),
|
||||
"Rush B": SongData(2900314, "35-3", "Happy Otaku Pack SP", True, 4, 7, 9),
|
||||
"DataErr0r": SongData(2900315, "35-4", "Happy Otaku Pack SP", False, 5, 7, 9),
|
||||
"Burn": SongData(2900316, "35-5", "Happy Otaku Pack SP", True, 4, 7, 9),
|
||||
"ALiVE": SongData(2900318, "34-0", "HARDCORE TANO*C", False, 5, 7, 10),
|
||||
"BATTLE NO.1": SongData(2900319, "34-1", "HARDCORE TANO*C", False, 5, 8, 10),
|
||||
"Cthugha": SongData(2900320, "34-2", "HARDCORE TANO*C", False, 6, 8, 10),
|
||||
"TWINKLE*MAGIC": SongData(2900321, "34-3", "HARDCORE TANO*C", False, 4, 7, 10),
|
||||
"Comet Coaster": SongData(2900322, "34-4", "HARDCORE TANO*C", False, 6, 8, 10),
|
||||
"XODUS": SongData(2900323, "34-5", "HARDCORE TANO*C", False, 7, 9, 11),
|
||||
"Fireflies": SongData(2900325, "33-0", "cyTus", True, 1, 4, 7),
|
||||
"Light up my love!!": SongData(2900326, "33-1", "cyTus", True, 3, 5, 7),
|
||||
"Happiness Breeze": SongData(2900327, "33-2", "cyTus", True, 4, 6, 8),
|
||||
"Chrome VOX": SongData(2900328, "33-3", "cyTus", True, 6, 8, 10),
|
||||
"CHAOS": SongData(2900329, "33-4", "cyTus", True, 3, 6, 9),
|
||||
"Saika": SongData(2900330, "33-5", "cyTus", True, 3, 5, 8),
|
||||
"Standby for Action": SongData(2900331, "33-6", "cyTus", True, 4, 6, 8),
|
||||
"Hydrangea": SongData(2900332, "33-7", "cyTus", True, 5, 7, 9),
|
||||
"Amenemhat": SongData(2900333, "33-8", "cyTus", True, 6, 8, 10),
|
||||
"Santouka": SongData(2900334, "33-9", "cyTus", True, 2, 5, 8),
|
||||
"HEXENNACHTROCK-katashihaya-": SongData(2900335, "33-10", "cyTus", True, 4, 8, 10),
|
||||
"Blah!!": SongData(2900336, "33-11", "cyTus", True, 5, 8, 11),
|
||||
"CHAOS Glitch": SongData(2900337, "33-12", "cyTus", True, None, None, None),
|
||||
"Preparara": SongData(2900339, "32-0", "Let's Do Bad Things Together", False, 1, 4, 6),
|
||||
"Whatcha;Whatcha Doin'": SongData(2900340, "32-1", "Let's Do Bad Things Together", False, 3, 6, 9),
|
||||
"Madara": SongData(2900341, "32-2", "Let's Do Bad Things Together", False, 4, 6, 9),
|
||||
"pICARESq": SongData(2900342, "32-3", "Let's Do Bad Things Together", False, 4, 6, 8),
|
||||
"Desastre": SongData(2900343, "32-4", "Let's Do Bad Things Together", False, 4, 6, 8),
|
||||
"Shoot for the Moon": SongData(2900344, "32-5", "Let's Do Bad Things Together", False, 2, 5, 8),
|
||||
"The 90's Decision": SongData(2900346, "31-0", "Happy Otaku Pack Vol.11", True, 5, 7, 9),
|
||||
"Medusa": SongData(2900347, "31-1", "Happy Otaku Pack Vol.11", False, 4, 6, 8),
|
||||
"Final Step!": SongData(2900348, "31-2", "Happy Otaku Pack Vol.11", False, 5, 7, 10),
|
||||
"MAGENTA POTION": SongData(2900349, "31-3", "Happy Otaku Pack Vol.11", False, 4, 7, 9),
|
||||
"Cross Ray": SongData(2900350, "31-4", "Happy Otaku Pack Vol.11", False, 3, 6, 9),
|
||||
"Square Lake": SongData(2900351, "31-5", "Happy Otaku Pack Vol.11", False, 6, 8, 9),
|
||||
"Girly Cupid": SongData(2900353, "30-0", "Cute Is Everything Vol.6", False, 3, 6, 8),
|
||||
"sheep in the light": SongData(2900354, "30-1", "Cute Is Everything Vol.6", False, 2, 5, 8),
|
||||
"Breaker city": SongData(2900355, "30-2", "Cute Is Everything Vol.6", False, 4, 6, 9),
|
||||
"heterodoxy": SongData(2900356, "30-3", "Cute Is Everything Vol.6", False, 4, 6, 8),
|
||||
"Computer Music Girl": SongData(2900357, "30-4", "Cute Is Everything Vol.6", False, 3, 5, 7),
|
||||
"Focus Point": SongData(2900358, "30-5", "Cute Is Everything Vol.6", True, 2, 5, 7),
|
||||
"Groove Prayer": SongData(2900360, "29-0", "Let' s GROOVE!", True, 3, 5, 7),
|
||||
"FUJIN Rumble": SongData(2900361, "29-1", "Let' s GROOVE!", True, 5, 7, 10),
|
||||
"Marry me, Nightmare": SongData(2900362, "29-2", "Let' s GROOVE!", False, 6, 8, 11),
|
||||
"HG Makaizou Polyvinyl Shounen": SongData(2900363, "29-3", "Let' s GROOVE!", True, 4, 7, 9),
|
||||
"Seizya no Ibuki": SongData(2900364, "29-4", "Let' s GROOVE!", True, 6, 8, 10),
|
||||
"ouroboros -twin stroke of the end-": SongData(2900365, "29-5", "Let' s GROOVE!", True, 4, 6, 9),
|
||||
"Heisha Onsha": SongData(2900367, "28-0", "Happy Otaku Pack Vol.10", False, 4, 6, 8),
|
||||
"Ginevra": SongData(2900368, "28-1", "Happy Otaku Pack Vol.10", True, 5, 7, 10),
|
||||
"Paracelestia": SongData(2900369, "28-2", "Happy Otaku Pack Vol.10", False, 5, 8, 10),
|
||||
"un secret": SongData(2900370, "28-3", "Happy Otaku Pack Vol.10", False, 2, 4, 6),
|
||||
"Good Life": SongData(2900371, "28-4", "Happy Otaku Pack Vol.10", False, 4, 6, 8),
|
||||
"nini-nini-": SongData(2900372, "28-5", "Happy Otaku Pack Vol.10", False, 4, 7, 9),
|
||||
"Can I friend you on Bassbook? lol": SongData(2900374, "27-0", "Nanahira Festival", False, 3, 6, 8),
|
||||
"Gaming*Everything": SongData(2900375, "27-1", "Nanahira Festival", False, 5, 8, 11),
|
||||
"Renji de haochi": SongData(2900376, "27-2", "Nanahira Festival", False, 5, 7, 9),
|
||||
"You Make My Life 1UP": SongData(2900377, "27-3", "Nanahira Festival", False, 4, 6, 8),
|
||||
"Newbies, Geeks, Internets": SongData(2900378, "27-4", "Nanahira Festival", False, 6, 8, 10),
|
||||
"Onegai!Kon kon Oinarisama": SongData(2900379, "27-5", "Nanahira Festival", False, 3, 6, 9),
|
||||
"Legend of Eastern Rabbit -SKY DEFENDER-": SongData(2900381, "26-0", "Give Up TREATMENT Vol.9", False, 4, 6, 9),
|
||||
"ENERGY SYNERGY MATRIX": SongData(2900382, "26-1", "Give Up TREATMENT Vol.9", False, 6, 8, 10),
|
||||
"Punai Punai Genso": SongData(2900383, "26-2", "Give Up TREATMENT Vol.9", False, 2, 7, 11),
|
||||
"Better Graphic Animation": SongData(2900384, "26-3", "Give Up TREATMENT Vol.9", False, 5, 8, 11),
|
||||
"Variant Cross": SongData(2900385, "26-4", "Give Up TREATMENT Vol.9", False, 4, 7, 10),
|
||||
"Ultra Happy Miracle Bazoooooka!!": SongData(2900386, "26-5", "Give Up TREATMENT Vol.9", False, 7, 9, 11),
|
||||
"tape/stop/night": SongData(2900388, "25-0", "MUSE RADIO FM101", True, 3, 5, 7),
|
||||
"Pixel Galaxy": SongData(2900389, "25-1", "MUSE RADIO FM101", False, 2, 5, 8),
|
||||
"Notice": SongData(2900390, "25-2", "MUSE RADIO FM101", False, 4, 7, 10),
|
||||
"Strawberry Godzilla": SongData(2900391, "25-3", "MUSE RADIO FM101", True, 2, 5, 7),
|
||||
"OKIMOCHI EXPRESSION": SongData(2900392, "25-4", "MUSE RADIO FM101", False, 4, 6, 10),
|
||||
"Kimi to pool disco": SongData(2900393, "25-5", "MUSE RADIO FM101", False, 4, 6, 8),
|
||||
"The Last Page": SongData(2900395, "24-0", "Happy Otaku Pack Vol.9", False, 3, 5, 7),
|
||||
"IKAROS": SongData(2900396, "24-1", "Happy Otaku Pack Vol.9", False, 4, 7, 10),
|
||||
"Tsukuyomi": SongData(2900397, "24-2", "Happy Otaku Pack Vol.9", False, 3, 6, 9),
|
||||
"Future Stream": SongData(2900398, "24-3", "Happy Otaku Pack Vol.9", False, 4, 6, 8),
|
||||
"FULi AUTO SHOOTER": SongData(2900399, "24-4", "Happy Otaku Pack Vol.9", True, 4, 7, 9),
|
||||
"GOODFORTUNE": SongData(2900400, "24-5", "Happy Otaku Pack Vol.9", False, 5, 7, 9),
|
||||
"The Dessert After Rain": SongData(2900402, "23-0", "Cute Is Everything Vol.5", True, 2, 4, 6),
|
||||
"Confession Support Formula": SongData(2900403, "23-1", "Cute Is Everything Vol.5", False, 3, 5, 7),
|
||||
"Omatsuri": SongData(2900404, "23-2", "Cute Is Everything Vol.5", False, 1, 3, 6),
|
||||
"FUTUREPOP": SongData(2900405, "23-3", "Cute Is Everything Vol.5", True, 2, 5, 7),
|
||||
"The Breeze": SongData(2900406, "23-4", "Cute Is Everything Vol.5", False, 1, 4, 6),
|
||||
"I LOVE LETTUCE FRIED RICE!!": SongData(2900407, "23-5", "Cute Is Everything Vol.5", False, 3, 7, 9),
|
||||
"The NightScape": SongData(2900409, "22-0", "Give Up TREATMENT Vol.8", False, 4, 7, 9),
|
||||
"FREEDOM DiVE": SongData(2900410, "22-1", "Give Up TREATMENT Vol.8", False, 6, 8, 10),
|
||||
"Phi": SongData(2900411, "22-2", "Give Up TREATMENT Vol.8", False, 5, 8, 10),
|
||||
"Lueur de la nuit": SongData(2900412, "22-3", "Give Up TREATMENT Vol.8", False, 6, 8, 11),
|
||||
"Creamy Sugary OVERDRIVE!!!": SongData(2900413, "22-4", "Give Up TREATMENT Vol.8", True, 4, 7, 10),
|
||||
"Disorder": SongData(2900414, "22-5", "Give Up TREATMENT Vol.8", False, 5, 7, 11),
|
||||
"Glimmer": SongData(2900416, "21-0", "Budget Is Burning: Nano Core", False, 2, 5, 8),
|
||||
"EXIST": SongData(2900417, "21-1", "Budget Is Burning: Nano Core", False, 3, 5, 8),
|
||||
"Irreplaceable": SongData(2900418, "21-2", "Budget Is Burning: Nano Core", False, 4, 6, 8),
|
||||
"Moonlight Banquet": SongData(2900420, "20-0", "Happy Otaku Pack Vol.8", True, 2, 5, 8),
|
||||
"Flashdance": SongData(2900421, "20-1", "Happy Otaku Pack Vol.8", False, 3, 6, 9),
|
||||
"INFiNiTE ENERZY -Overdoze-": SongData(2900422, "20-2", "Happy Otaku Pack Vol.8", False, 4, 7, 9),
|
||||
"One Way Street": SongData(2900423, "20-3", "Happy Otaku Pack Vol.8", False, 3, 6, 10),
|
||||
"This Club is Not 4 U": SongData(2900424, "20-4", "Happy Otaku Pack Vol.8", False, 4, 7, 9),
|
||||
"ULTRA MEGA HAPPY PARTY!!!": SongData(2900425, "20-5", "Happy Otaku Pack Vol.8", False, 5, 7, 10),
|
||||
"INFINITY": SongData(2900427, "19-0", "Give Up TREATMENT Vol.7", True, 5, 8, 10),
|
||||
"Punai Punai Senso": SongData(2900428, "19-1", "Give Up TREATMENT Vol.7", False, 2, 7, 11),
|
||||
"Maxi": SongData(2900429, "19-2", "Give Up TREATMENT Vol.7", False, 5, 8, 10),
|
||||
"YInMn Blue": SongData(2900430, "19-3", "Give Up TREATMENT Vol.7", False, 6, 8, 10),
|
||||
"Plumage": SongData(2900431, "19-4", "Give Up TREATMENT Vol.7", False, 4, 7, 10),
|
||||
"Dr.Techro": SongData(2900432, "19-5", "Give Up TREATMENT Vol.7", False, 7, 9, 11),
|
||||
"SWEETSWEETSWEET": SongData(2900434, "18-0", "Cute Is Everything Vol.4", True, 2, 5, 7),
|
||||
"Deep Blue and the Breaths of the Night": SongData(2900435, "18-1", "Cute Is Everything Vol.4", True, 2, 4, 6),
|
||||
"Joy Connection": SongData(2900436, "18-2", "Cute Is Everything Vol.4", False, 3, 6, 8),
|
||||
"Self Willed Girl Ver.B": SongData(2900437, "18-3", "Cute Is Everything Vol.4", True, 4, 6, 8),
|
||||
"Just Disobedient": SongData(2900438, "18-4", "Cute Is Everything Vol.4", False, 3, 6, 8),
|
||||
"Holy Sh*t Grass Snake": SongData(2900439, "18-5", "Cute Is Everything Vol.4", False, 2, 6, 9),
|
||||
"Cotton Candy Wonderland": SongData(2900441, "17-0", "Happy Otaku Pack Vol.7", False, 2, 5, 8),
|
||||
"Punai Punai Taiso": SongData(2900442, "17-1", "Happy Otaku Pack Vol.7", False, 2, 7, 10),
|
||||
"Fly High": SongData(2900443, "17-2", "Happy Otaku Pack Vol.7", False, 3, 5, 7),
|
||||
"prejudice": SongData(2900444, "17-3", "Happy Otaku Pack Vol.7", True, 4, 6, 9),
|
||||
"The 89's Momentum": SongData(2900445, "17-4", "Happy Otaku Pack Vol.7", True, 5, 7, 9),
|
||||
"energy night": SongData(2900446, "17-5", "Happy Otaku Pack Vol.7", True, 5, 7, 10),
|
||||
"Future Dive": SongData(2900448, "16-0", "Give Up TREATMENT Vol.6", True, 4, 6, 9),
|
||||
"Re End of a Dream": SongData(2900449, "16-1", "Give Up TREATMENT Vol.6", False, 5, 8, 11),
|
||||
"Etude -Storm-": SongData(2900450, "16-2", "Give Up TREATMENT Vol.6", True, 6, 8, 10),
|
||||
"Unlimited Katharsis": SongData(2900451, "16-3", "Give Up TREATMENT Vol.6", False, 4, 6, 10),
|
||||
"Magic Knight Girl": SongData(2900452, "16-4", "Give Up TREATMENT Vol.6", False, 4, 7, 9),
|
||||
"Eeliaas": SongData(2900453, "16-5", "Give Up TREATMENT Vol.6", False, 6, 9, 11),
|
||||
"Magic Spell": SongData(2900455, "15-0", "Cute Is Everything Vol.3", True, 2, 5, 7),
|
||||
"Colorful Star, Colored Drawing, Travel Poem": SongData(2900456, "15-1", "Cute Is Everything Vol.3", False, 3, 4, 6),
|
||||
"Satell Knight": SongData(2900457, "15-2", "Cute Is Everything Vol.3", False, 3, 6, 8),
|
||||
"Black River Feat.Mes": SongData(2900458, "15-3", "Cute Is Everything Vol.3", True, 1, 4, 6),
|
||||
"I am sorry": SongData(2900459, "15-4", "Cute Is Everything Vol.3", False, 2, 5, 8),
|
||||
"Ueta Tori Tachi": SongData(2900460, "15-5", "Cute Is Everything Vol.3", False, 3, 6, 8),
|
||||
"Elysion's Old Mans": SongData(2900462, "14-0", "Happy Otaku Pack Vol.6", False, 3, 5, 8),
|
||||
"AXION": SongData(2900463, "14-1", "Happy Otaku Pack Vol.6", False, 4, 5, 8),
|
||||
"Amnesia": SongData(2900464, "14-2", "Happy Otaku Pack Vol.6", True, 3, 6, 9),
|
||||
"Onsen Dai Sakusen": SongData(2900465, "14-3", "Happy Otaku Pack Vol.6", True, 4, 6, 8),
|
||||
"Gleam stone": SongData(2900466, "14-4", "Happy Otaku Pack Vol.6", False, 4, 7, 9),
|
||||
"GOODWORLD": SongData(2900467, "14-5", "Happy Otaku Pack Vol.6", False, 4, 7, 10),
|
||||
"Instant Soluble Neon": SongData(2900469, "13-0", "Cute Is Everything Vol.2", True, 2, 4, 7),
|
||||
"Retrospective Poem on the Planet": SongData(2900470, "13-1", "Cute Is Everything Vol.2", False, 3, 5, 7),
|
||||
"I'm Gonna Buy! Buy! Buy!": SongData(2900471, "13-2", "Cute Is Everything Vol.2", True, 4, 6, 8),
|
||||
"Dating Manifesto": SongData(2900472, "13-3", "Cute Is Everything Vol.2", True, 2, 4, 6),
|
||||
"First Snow": SongData(2900473, "13-4", "Cute Is Everything Vol.2", True, 2, 3, 6),
|
||||
"Xin Shang Huahai": SongData(2900474, "13-5", "Cute Is Everything Vol.2", False, 3, 6, 8),
|
||||
"Gaikan Chrysalis": SongData(2900476, "12-0", "Give Up TREATMENT Vol.5", False, 4, 6, 8),
|
||||
"Sterelogue": SongData(2900477, "12-1", "Give Up TREATMENT Vol.5", True, 5, 7, 10),
|
||||
"Cheshire's Dance": SongData(2900478, "12-2", "Give Up TREATMENT Vol.5", True, 4, 7, 10),
|
||||
"Skrik": SongData(2900479, "12-3", "Give Up TREATMENT Vol.5", True, 5, 7, 11),
|
||||
"Soda Pop Canva5!": SongData(2900480, "12-4", "Give Up TREATMENT Vol.5", False, 5, 8, 10),
|
||||
"RUBY LINTe": SongData(2900481, "12-5", "Give Up TREATMENT Vol.5", False, 5, 8, 11),
|
||||
"Brave My Heart": SongData(2900483, "11-0", "Happy Otaku Pack Vol.5", True, 3, 5, 7),
|
||||
"Sakura Fubuki": SongData(2900484, "11-1", "Happy Otaku Pack Vol.5", False, 4, 7, 10),
|
||||
"8bit Adventurer": SongData(2900485, "11-2", "Happy Otaku Pack Vol.5", False, 6, 8, 10),
|
||||
"Suffering of screw": SongData(2900486, "11-3", "Happy Otaku Pack Vol.5", False, 3, 5, 8),
|
||||
"tiny lady": SongData(2900487, "11-4", "Happy Otaku Pack Vol.5", True, 4, 6, 9),
|
||||
"Power Attack": SongData(2900488, "11-5", "Happy Otaku Pack Vol.5", False, 5, 7, 10),
|
||||
"Destr0yer": SongData(2900490, "10-0", "Give Up TREATMENT Vol.4", False, 4, 7, 9),
|
||||
"Noel": SongData(2900491, "10-1", "Give Up TREATMENT Vol.4", False, 5, 8, 10),
|
||||
"Kyoukiranbu": SongData(2900492, "10-2", "Give Up TREATMENT Vol.4", False, 7, 9, 11),
|
||||
"Two Phace": SongData(2900493, "10-3", "Give Up TREATMENT Vol.4", True, 4, 7, 10),
|
||||
"Fly Again": SongData(2900494, "10-4", "Give Up TREATMENT Vol.4", False, 5, 7, 10),
|
||||
"ouroVoros": SongData(2900495, "10-5", "Give Up TREATMENT Vol.4", False, 7, 9, 11),
|
||||
"Leave It Alone": SongData(2900497, "9-0", "Happy Otaku Pack Vol.4", True, 2, 5, 8),
|
||||
"Tsubasa no Oreta Tenshitachi no Requiem": SongData(2900498, "9-1", "Happy Otaku Pack Vol.4", False, 4, 7, 9),
|
||||
"Chronomia": SongData(2900499, "9-2", "Happy Otaku Pack Vol.4", False, 5, 7, 10),
|
||||
"Dandelion's Daydream": SongData(2900500, "9-3", "Happy Otaku Pack Vol.4", True, 5, 7, 8),
|
||||
"Lorikeet Flat design": SongData(2900501, "9-4", "Happy Otaku Pack Vol.4", True, 5, 7, 10),
|
||||
"GOODRAGE": SongData(2900502, "9-5", "Happy Otaku Pack Vol.4", False, 6, 9, 11),
|
||||
"Altale": SongData(2900504, "8-0", "Give Up TREATMENT Vol.3", False, 3, 5, 7),
|
||||
"Brain Power": SongData(2900505, "8-1", "Give Up TREATMENT Vol.3", False, 4, 7, 10),
|
||||
"Berry Go!!": SongData(2900506, "8-2", "Give Up TREATMENT Vol.3", False, 3, 6, 9),
|
||||
"Sweet* Witch* Girl*": SongData(2900507, "8-3", "Give Up TREATMENT Vol.3", False, 6, 8, 10),
|
||||
"trippers feeling!": SongData(2900508, "8-4", "Give Up TREATMENT Vol.3", True, 5, 7, 9),
|
||||
"Lilith ambivalence lovers": SongData(2900509, "8-5", "Give Up TREATMENT Vol.3", False, 5, 8, 10),
|
||||
"Brave My Soul": SongData(2900511, "7-0", "Give Up TREATMENT Vol.2", False, 4, 6, 8),
|
||||
"Halcyon": SongData(2900512, "7-1", "Give Up TREATMENT Vol.2", False, 4, 7, 10),
|
||||
"Crimson Nightingale": SongData(2900513, "7-2", "Give Up TREATMENT Vol.2", True, 4, 7, 10),
|
||||
"Invader": SongData(2900514, "7-3", "Give Up TREATMENT Vol.2", True, 3, 7, 11),
|
||||
"Lyrith": SongData(2900515, "7-4", "Give Up TREATMENT Vol.2", False, 5, 7, 10),
|
||||
"GOODBOUNCE": SongData(2900516, "7-5", "Give Up TREATMENT Vol.2", False, 4, 6, 9),
|
||||
"Out of Sense": SongData(2900518, "6-0", "Budget Is Burning Vol.1", False, 3, 5, 8),
|
||||
"My Life Is For You": SongData(2900519, "6-1", "Budget Is Burning Vol.1", False, 2, 4, 7),
|
||||
"Etude -Sunset-": SongData(2900520, "6-2", "Budget Is Burning Vol.1", True, 5, 7, 9),
|
||||
"Goodbye Boss": SongData(2900521, "6-3", "Budget Is Burning Vol.1", False, 4, 6, 8),
|
||||
"Stargazer": SongData(2900522, "6-4", "Budget Is Burning Vol.1", True, 2, 5, 8),
|
||||
"Lys Tourbillon": SongData(2900523, "6-5", "Budget Is Burning Vol.1", True, 4, 6, 8),
|
||||
"Thirty Million Persona": SongData(2900525, "5-0", "Happy Otaku Pack Vol.3", False, 2, 4, 6),
|
||||
"conflict": SongData(2900526, "5-1", "Happy Otaku Pack Vol.3", False, 2, 6, 9),
|
||||
"Enka Dance Music": SongData(2900527, "5-2", "Happy Otaku Pack Vol.3", False, 3, 5, 7),
|
||||
"XING": SongData(2900528, "5-3", "Happy Otaku Pack Vol.3", True, 4, 6, 8),
|
||||
"Amakakeru Soukyuu no Serenade": SongData(2900529, "5-4", "Happy Otaku Pack Vol.3", False, 3, 6, 9),
|
||||
"Gift box": SongData(2900530, "5-5", "Happy Otaku Pack Vol.3", False, 5, 7, 10),
|
||||
"MUSEDASH!!!!": SongData(2900532, "4-0", "Happy Otaku Pack Vol.2", False, 2, 6, 9),
|
||||
"Imprinting": SongData(2900533, "4-1", "Happy Otaku Pack Vol.2", False, 3, 6, 9),
|
||||
"Skyward": SongData(2900534, "4-2", "Happy Otaku Pack Vol.2", True, 4, 7, 10),
|
||||
"La nuit de vif": SongData(2900535, "4-3", "Happy Otaku Pack Vol.2", True, 2, 5, 8),
|
||||
"Bit-alize": SongData(2900536, "4-4", "Happy Otaku Pack Vol.2", False, 3, 6, 8),
|
||||
"GOODTEK": SongData(2900537, "4-5", "Happy Otaku Pack Vol.2", False, 4, 6, 9),
|
||||
"Maharajah": SongData(2900539, "3-0", "Happy Otaku Pack Vol.1", False, 1, 3, 6),
|
||||
"keep on running": SongData(2900540, "3-1", "Happy Otaku Pack Vol.1", False, 5, 7, 9),
|
||||
"Kafig": SongData(2900541, "3-2", "Happy Otaku Pack Vol.1", True, 4, 6, 8),
|
||||
"-+": SongData(2900542, "3-3", "Happy Otaku Pack Vol.1", True, 4, 6, 8),
|
||||
"Tenri Kaku Jou": SongData(2900543, "3-4", "Happy Otaku Pack Vol.1", True, 3, 6, 9),
|
||||
"Adjudicatorz-DanZai-": SongData(2900544, "3-5", "Happy Otaku Pack Vol.1", False, 3, 7, 10),
|
||||
"Oriens": SongData(2900546, "2-0", "Give Up TREATMENT Vol.1", True, 3, 7, 9),
|
||||
"PUPA": SongData(2900547, "2-1", "Give Up TREATMENT Vol.1", False, 6, 8, 11),
|
||||
"Luna Express 2032": SongData(2900548, "2-2", "Give Up TREATMENT Vol.1", False, 4, 6, 8),
|
||||
"Ukiyoe Yokochou": SongData(2900549, "2-3", "Give Up TREATMENT Vol.1", False, 6, 7, 9),
|
||||
"Alice in Misanthrope": SongData(2900550, "2-4", "Give Up TREATMENT Vol.1", False, 5, 7, 10),
|
||||
"GOODMEN": SongData(2900551, "2-5", "Give Up TREATMENT Vol.1", False, 5, 7, 10),
|
||||
"Sunshine and Rainbow after August Rain": SongData(2900553, "1-0", "Cute Is Everything Vol.1", False, 2, 5, 8),
|
||||
"Magical Number": SongData(2900554, "1-1", "Cute Is Everything Vol.1", False, 2, 5, 8),
|
||||
"Dreaming Girl": SongData(2900555, "1-2", "Cute Is Everything Vol.1", False, 2, 5, 6),
|
||||
"Daruma-san Fell Over": SongData(2900556, "1-3", "Cute Is Everything Vol.1", False, 3, 4, 6),
|
||||
"Different": SongData(2900557, "1-4", "Cute Is Everything Vol.1", False, 1, 3, 6),
|
||||
"The Future of the Phantom": SongData(2900558, "1-5", "Cute Is Everything Vol.1", False, 1, 3, 5),
|
||||
"Doki Doki Jump!": SongData(2900560, "63-0", "MUSE RADIO FM104", True, 3, 5, 7),
|
||||
"Centennial Streamers High": SongData(2900561, "63-1", "MUSE RADIO FM104", False, 4, 7, 9),
|
||||
"Love Patrol": SongData(2900562, "63-2", "MUSE RADIO FM104", True, 3, 5, 7),
|
||||
"Mahorova": SongData(2900563, "63-3", "MUSE RADIO FM104", True, 3, 5, 8),
|
||||
"Yoru no machi": SongData(2900564, "63-4", "MUSE RADIO FM104", True, 1, 4, 7),
|
||||
"INTERNET YAMERO": SongData(2900565, "63-5", "MUSE RADIO FM104", True, 6, 8, 10),
|
||||
"Abracadabra": SongData(2900566, "43-24", "MD Plus Project", False, 6, 8, 10),
|
||||
"Squalldecimator feat. EZ-Ven": SongData(2900567, "43-25", "MD Plus Project", True, 5, 7, 9),
|
||||
"Amateras Rhythm": SongData(2900568, "43-26", "MD Plus Project", True, 6, 8, 11),
|
||||
"Record one's Dream": SongData(2900569, "43-27", "MD Plus Project", False, 4, 7, 10),
|
||||
"Lunatic": SongData(2900570, "43-28", "MD Plus Project", True, 5, 8, 10),
|
||||
"Jiumeng": SongData(2900571, "43-29", "MD Plus Project", True, 3, 6, 8),
|
||||
"The Day We Become Family": SongData(2900572, "43-30", "MD Plus Project", True, 3, 5, 8),
|
||||
"Sutori ma FIRE!?!?": SongData(2900574, "64-0", "COSMIC RADIO PEROLIST", True, 3, 5, 8),
|
||||
"Tanuki Step": SongData(2900575, "64-1", "COSMIC RADIO PEROLIST", True, 5, 7, 10),
|
||||
"Space Stationery": SongData(2900576, "64-2", "COSMIC RADIO PEROLIST", True, 5, 7, 10),
|
||||
"Songs Are Judged 90% by Chorus feat. Mameko": SongData(2900577, "64-3", "COSMIC RADIO PEROLIST", True, 6, 8, 10),
|
||||
"Kawai Splendid Space Thief": SongData(2900578, "64-4", "COSMIC RADIO PEROLIST", False, 6, 8, 10),
|
||||
"Night City Runway": SongData(2900579, "64-5", "COSMIC RADIO PEROLIST", True, 4, 6, 8),
|
||||
"Chaos Shotgun feat. ChumuNote": SongData(2900580, "64-6", "COSMIC RADIO PEROLIST", True, 6, 8, 10),
|
||||
"mew mew magical summer": SongData(2900581, "64-7", "COSMIC RADIO PEROLIST", False, 5, 8, 10),
|
||||
"BrainDance": SongData(2900583, "65-0", "NeonAbyss", True, 3, 6, 9),
|
||||
"My Focus!": SongData(2900584, "65-1", "NeonAbyss", True, 5, 7, 10),
|
||||
"ABABABA BURST": SongData(2900585, "65-2", "NeonAbyss", True, 5, 7, 9),
|
||||
"ULTRA HIGHER": SongData(2900586, "65-3", "NeonAbyss", True, 4, 7, 10),
|
||||
"Silver Bullet": SongData(2900587, "43-31", "MD Plus Project", True, 5, 7, 10),
|
||||
"Random": SongData(2900588, "43-32", "MD Plus Project", True, 4, 7, 9),
|
||||
"OTOGE-BOSS-KYOKU-CHAN": SongData(2900589, "43-33", "MD Plus Project", False, 6, 8, 10),
|
||||
"Crow Rabbit": SongData(2900590, "43-34", "MD Plus Project", True, 7, 9, 11),
|
||||
"SyZyGy": SongData(2900591, "43-35", "MD Plus Project", True, 6, 8, 10),
|
||||
"Mermaid Radio": SongData(2900592, "43-36", "MD Plus Project", True, 3, 5, 7),
|
||||
"Helixir": SongData(2900593, "43-37", "MD Plus Project", False, 6, 8, 10),
|
||||
"Highway Cruisin'": SongData(2900594, "43-38", "MD Plus Project", False, 3, 5, 8),
|
||||
"JACK PT BOSS": SongData(2900595, "43-39", "MD Plus Project", False, 6, 8, 10),
|
||||
"Time Capsule": SongData(2900596, "43-40", "MD Plus Project", False, 7, 9, 11),
|
||||
"39 Music!": SongData(2900598, "66-0", "Miku in Museland", False, 3, 5, 8),
|
||||
"Hand in Hand": SongData(2900599, "66-1", "Miku in Museland", False, 1, 3, 6),
|
||||
"Cynical Night Plan": SongData(2900600, "66-2", "Miku in Museland", False, 4, 6, 8),
|
||||
"God-ish": SongData(2900601, "66-3", "Miku in Museland", False, 4, 7, 10),
|
||||
"Darling Dance": SongData(2900602, "66-4", "Miku in Museland", False, 4, 7, 9),
|
||||
"Hatsune Creation Myth": SongData(2900603, "66-5", "Miku in Museland", False, 6, 8, 10),
|
||||
"The Vampire": SongData(2900604, "66-6", "Miku in Museland", False, 4, 6, 9),
|
||||
"Future Eve": SongData(2900605, "66-7", "Miku in Museland", False, 4, 8, 11),
|
||||
"Unknown Mother Goose": SongData(2900606, "66-8", "Miku in Museland", False, 4, 8, 10),
|
||||
"Shun-ran": SongData(2900607, "66-9", "Miku in Museland", False, 4, 7, 9),
|
||||
"NICE TYPE feat. monii": SongData(2900608, "43-41", "MD Plus Project", True, 3, 6, 8),
|
||||
"Rainy Angel": SongData(2900610, "67-0", "Happy Otaku Pack Vol.18", True, 4, 6, 9),
|
||||
"Gullinkambi": SongData(2900611, "67-1", "Happy Otaku Pack Vol.18", True, 4, 7, 10),
|
||||
"RakiRaki Rebuilders!!!": SongData(2900612, "67-2", "Happy Otaku Pack Vol.18", True, 5, 7, 10),
|
||||
"Laniakea": SongData(2900613, "67-3", "Happy Otaku Pack Vol.18", False, 5, 8, 10),
|
||||
"OTTAMA GAZER": SongData(2900614, "67-4", "Happy Otaku Pack Vol.18", True, 5, 8, 10),
|
||||
"Sleep Tight feat.Macoto": SongData(2900615, "67-5", "Happy Otaku Pack Vol.18", True, 3, 5, 8),
|
||||
"New York Back Raise": SongData(2900617, "68-0", "Gambler's Tricks", True, 6, 8, 10),
|
||||
"slic.hertz": SongData(2900618, "68-1", "Gambler's Tricks", True, 5, 7, 9),
|
||||
"Fuzzy-Navel": SongData(2900619, "68-2", "Gambler's Tricks", True, 6, 8, 10),
|
||||
"Swing Edge": SongData(2900620, "68-3", "Gambler's Tricks", True, 4, 8, 10),
|
||||
"Twisted Escape": SongData(2900621, "68-4", "Gambler's Tricks", True, 5, 8, 10),
|
||||
"Swing Sweet Twee Dance": SongData(2900622, "68-5", "Gambler's Tricks", False, 4, 7, 10),
|
||||
"Sanyousei SAY YA!!!": SongData(2900623, "43-42", "MD Plus Project", False, 4, 6, 8),
|
||||
"YUKEMURI TAMAONSEN II": SongData(2900624, "43-43", "MD Plus Project", False, 3, 6, 9),
|
||||
"Samayoi no mei Amatsu": SongData(2900626, "69-0", "Touhou Mugakudan -III-", False, 4, 6, 9),
|
||||
"INTERNET SURVIVOR": SongData(2900627, "69-1", "Touhou Mugakudan -III-", False, 5, 8, 10),
|
||||
"Shuki*RaiRai": SongData(2900628, "69-2", "Touhou Mugakudan -III-", False, 5, 7, 9),
|
||||
"HELLOHELL": SongData(2900629, "69-3", "Touhou Mugakudan -III-", False, 4, 7, 10),
|
||||
"Calamity Fortune": SongData(2900630, "69-4", "Touhou Mugakudan -III-", True, 6, 8, 10),
|
||||
"Tsurupettan": SongData(2900631, "69-5", "Touhou Mugakudan -III-", True, 2, 5, 8),
|
||||
"Twilight Poems": SongData(2900632, "43-44", "MD Plus Project", True, 3, 6, 8),
|
||||
"All My Friends feat. RANASOL": SongData(2900633, "43-45", "MD Plus Project", True, 4, 7, 9),
|
||||
"Heartache": SongData(2900634, "43-46", "MD Plus Project", True, 5, 7, 10),
|
||||
"Blue Lemonade": SongData(2900635, "43-47", "MD Plus Project", True, 3, 6, 8),
|
||||
"Haunted Dance": SongData(2900636, "43-48", "MD Plus Project", False, 6, 9, 11),
|
||||
"Hey Vincent.": SongData(2900637, "43-49", "MD Plus Project", True, 6, 8, 10),
|
||||
"Meteor feat. TEA": SongData(2900638, "43-50", "MD Plus Project", True, 3, 6, 9),
|
||||
"Narcissism Angel": SongData(2900639, "43-51", "MD Plus Project", True, 1, 3, 6),
|
||||
"AlterLuna": SongData(2900640, "43-52", "MD Plus Project", True, 6, 8, 11),
|
||||
"Niki Tousen": SongData(2900641, "43-53", "MD Plus Project", True, 6, 8, 10),
|
||||
"Rettou Joutou": SongData(2900643, "70-0", "Rin Len's Mirrorland", False, 4, 7, 9),
|
||||
"Telecaster B-Boy": SongData(2900644, "70-1", "Rin Len's Mirrorland", False, 5, 7, 10),
|
||||
"Iya Iya Iya": SongData(2900645, "70-2", "Rin Len's Mirrorland", False, 2, 4, 7),
|
||||
"Nee Nee Nee": SongData(2900646, "70-3", "Rin Len's Mirrorland", False, 4, 6, 8),
|
||||
"Chaotic Love Revolution": SongData(2900647, "70-4", "Rin Len's Mirrorland", False, 4, 6, 8),
|
||||
"Dance of the Corpses": SongData(2900648, "70-5", "Rin Len's Mirrorland", False, 2, 5, 8),
|
||||
"Bitter Choco Decoration": SongData(2900649, "70-6", "Rin Len's Mirrorland", False, 3, 6, 9),
|
||||
"Dance Robot Dance": SongData(2900650, "70-7", "Rin Len's Mirrorland", False, 4, 7, 10),
|
||||
"Sweet Devil": SongData(2900651, "70-8", "Rin Len's Mirrorland", False, 5, 7, 9),
|
||||
"Someday'z Coming": SongData(2900652, "70-9", "Rin Len's Mirrorland", False, 5, 7, 9),
|
||||
"Yume Ou Mono Yo Secret": SongData(2900653, "0-53", "Default Music", True, 6, 8, 10),
|
||||
"Yume Ou Mono Yo": SongData(2900654, "0-54", "Default Music", True, 1, 4, None),
|
||||
"Sweet Dream VIVINOS": SongData(2900656, "71-0", "Valentine Stage", False, 1, 4, 7),
|
||||
"Ruler Of My Heart VIVINOS": SongData(2900657, "71-1", "Valentine Stage", False, 2, 4, 6),
|
||||
"Reality Show": SongData(2900658, "71-2", "Valentine Stage", False, 5, 7, 10),
|
||||
"SIG feat.Tobokegao": SongData(2900659, "71-3", "Valentine Stage", True, 3, 6, 8),
|
||||
"Rose Love": SongData(2900660, "71-4", "Valentine Stage", True, 2, 4, 7),
|
||||
"Euphoria": SongData(2900661, "71-5", "Valentine Stage", True, 1, 3, 6),
|
||||
"P E R O P E R O Brother Dance": SongData(2900663, "72-0", "Legends of Muse Warriors", True, None, 7, None),
|
||||
"PA PPA PANIC": SongData(2900664, "72-1", "Legends of Muse Warriors", False, 4, 8, 10),
|
||||
"How To Make Music Game Song!": SongData(2900665, "72-2", "Legends of Muse Warriors", True, 6, 8, 10),
|
||||
"Re Re": SongData(2900666, "72-3", "Legends of Muse Warriors", True, 7, 9, 11),
|
||||
"Marmalade Twins": SongData(2900667, "72-4", "Legends of Muse Warriors", True, 5, 8, 10),
|
||||
"DOMINATOR": SongData(2900668, "72-5", "Legends of Muse Warriors", True, 7, 9, 11),
|
||||
"Teshikani TESHiKANi": SongData(2900669, "72-6", "Legends of Muse Warriors", True, 5, 7, 9),
|
||||
"Urban Magic": SongData(2900671, "73-0", "Happy Otaku Pack Vol.19", True, 3, 5, 7),
|
||||
"Maid's Prank": SongData(2900672, "73-1", "Happy Otaku Pack Vol.19", True, 5, 7, 10),
|
||||
"Dance Dance Good Night Dance": SongData(2900673, "73-2", "Happy Otaku Pack Vol.19", True, 2, 4, 7),
|
||||
"Ops Limone": SongData(2900674, "73-3", "Happy Otaku Pack Vol.19", True, 5, 8, 11),
|
||||
"NOVA": SongData(2900675, "73-4", "Happy Otaku Pack Vol.19", True, 6, 8, 10),
|
||||
"Heaven's Gradius": SongData(2900676, "73-5", "Happy Otaku Pack Vol.19", True, 6, 8, 10),
|
||||
"Ray Tuning": SongData(2900678, "74-0", "CHUNITHM COURSE MUSE", True, 6, 8, 10),
|
||||
"World Vanquisher": SongData(2900679, "74-1", "CHUNITHM COURSE MUSE", True, 6, 8, 10),
|
||||
"Tsukuyomi Ni Naru Replaced": SongData(2900680, "74-2", "CHUNITHM COURSE MUSE", True, 5, 7, 9),
|
||||
"The wheel to the right": SongData(2900681, "74-3", "CHUNITHM COURSE MUSE", True, 5, 7, 9),
|
||||
"Climax": SongData(2900682, "74-4", "CHUNITHM COURSE MUSE", True, 4, 8, 11),
|
||||
"Spider's Thread": SongData(2900683, "74-5", "CHUNITHM COURSE MUSE", True, 5, 8, 10),
|
||||
"HIT ME UP": SongData(2900684, "43-54", "MD Plus Project", True, 4, 6, 8),
|
||||
"Test Me feat. Uyeon": SongData(2900685, "43-55", "MD Plus Project", True, 3, 5, 9),
|
||||
"Assault TAXI": SongData(2900686, "43-56", "MD Plus Project", True, 4, 7, 10),
|
||||
"No": SongData(2900687, "43-57", "MD Plus Project", False, 4, 6, 9),
|
||||
"Pop it": SongData(2900688, "43-58", "MD Plus Project", True, 1, 3, 6),
|
||||
"HEARTBEAT! KyunKyun!": SongData(2900689, "43-59", "MD Plus Project", True, 4, 6, 9),
|
||||
"SUPERHERO": SongData(2900691, "75-0", "Novice Rider Pack", False, 2, 4, 7),
|
||||
"Highway_Summer": SongData(2900692, "75-1", "Novice Rider Pack", True, 2, 4, 6),
|
||||
"Mx. Black Box": SongData(2900693, "75-2", "Novice Rider Pack", True, 5, 7, 9),
|
||||
"Sweet Encounter": SongData(2900694, "75-3", "Novice Rider Pack", True, 2, 4, 7),
|
||||
"Echo over you... Secret": SongData(2900695, "0-55", "Default Music", False, 6, 8, 10),
|
||||
"Echo over you...": SongData(2900696, "0-56", "Default Music", False, 1, 4, None),
|
||||
"Tsukuyomi Ni Naru": SongData(2900697, "74-6", "CHUNITHM COURSE MUSE", True, 5, 8, 10),
|
||||
"disco light": SongData(2900699, "76-0", "MUSE RADIO FM105", True, 5, 7, 9),
|
||||
"room light feat.chancylemon": SongData(2900700, "76-1", "MUSE RADIO FM105", True, 3, 5, 7),
|
||||
"Invisible": SongData(2900701, "76-2", "MUSE RADIO FM105", True, 3, 5, 8),
|
||||
"Christmas Season-LLABB": SongData(2900702, "76-3", "MUSE RADIO FM105", True, 1, 4, 7),
|
||||
"Hyouryu": SongData(2900704, "77-0", "Let's Rhythm Jam!", False, 6, 8, 10),
|
||||
"The Whole Rest": SongData(2900705, "77-1", "Let's Rhythm Jam!", False, 5, 8, 10),
|
||||
"Hydra": SongData(2900706, "77-2", "Let's Rhythm Jam!", False, 4, 7, 11),
|
||||
"Pastel Lines": SongData(2900707, "77-3", "Let's Rhythm Jam!", False, 3, 6, 9),
|
||||
"LINK x LIN#S": SongData(2900708, "77-4", "Let's Rhythm Jam!", False, 3, 6, 9),
|
||||
"Arcade ViruZ": SongData(2900709, "77-5", "Let's Rhythm Jam!", False, 6, 8, 11),
|
||||
"Eve Avenir": SongData(2900711, "78-0", "Endless Pirouette", True, 6, 8, 10),
|
||||
"Silverstring": SongData(2900712, "78-1", "Endless Pirouette", True, 5, 7, 10),
|
||||
"Melusia": SongData(2900713, "78-2", "Endless Pirouette", False, 5, 7, 10),
|
||||
"Devil's Castle": SongData(2900714, "78-3", "Endless Pirouette", True, 4, 7, 10),
|
||||
"Abatement": SongData(2900715, "78-4", "Endless Pirouette", True, 6, 8, 10),
|
||||
"Azalea": SongData(2900716, "78-5", "Endless Pirouette", False, 4, 8, 10),
|
||||
"Brightly World": SongData(2900717, "78-6", "Endless Pirouette", True, 6, 8, 10),
|
||||
"We'll meet in every world ***": SongData(2900718, "78-7", "Endless Pirouette", True, 7, 9, 11),
|
||||
"Collapsar": SongData(2900719, "78-8", "Endless Pirouette", True, 7, 9, 10),
|
||||
"Parousia": SongData(2900720, "78-9", "Endless Pirouette", False, 6, 8, 10),
|
||||
"Gunners in the Rain": SongData(2900722, "79-0", "Ensemble Arcanum", False, 5, 8, 10),
|
||||
"Halzion": SongData(2900723, "79-1", "Ensemble Arcanum", False, 2, 5, 8),
|
||||
"SHOWTIME!!": SongData(2900724, "79-2", "Ensemble Arcanum", False, 6, 8, 10),
|
||||
"Achromic Riddle": SongData(2900725, "79-3", "Ensemble Arcanum", False, 6, 8, 10),
|
||||
"karanosu": SongData(2900726, "79-4", "Ensemble Arcanum", False, 3, 6, 8),
|
||||
"Necromantic": SongData(2900727, "43-60", "MD Plus Project", False, 6, 8, 10),
|
||||
"Saishuu kichiku imouto Flandre-S": SongData(2900729, "80-0", "Touhou Mugakudan -IV-", False, 6, 8, 10),
|
||||
"Kachoufuugetsu": SongData(2900730, "80-1", "Touhou Mugakudan -IV-", False, 2, 6, 8),
|
||||
"Maid heart is a puppet": SongData(2900731, "80-2", "Touhou Mugakudan -IV-", False, 5, 7, 9),
|
||||
"Trance dance anarchy": SongData(2900732, "80-3", "Touhou Mugakudan -IV-", False, 4, 7, 10),
|
||||
"fairy stage": SongData(2900733, "80-4", "Touhou Mugakudan -IV-", False, 4, 6, 9),
|
||||
"Scarlet Police on Ghetto Patrol": SongData(2900734, "80-5", "Touhou Mugakudan -IV-", False, 5, 7, 10),
|
||||
"Unwelcome School": SongData(2900735, "81-0", "MD-level Tactical Training Blu-ray", False, 6, 8, 10),
|
||||
"Usagi Flap": SongData(2900736, "81-1", "MD-level Tactical Training Blu-ray", False, 3, 6, 8),
|
||||
"RE Aoharu": SongData(2900737, "81-2", "MD-level Tactical Training Blu-ray", False, 3, 5, 8),
|
||||
"Operation*DOTABATA!": SongData(2900738, "81-3", "MD-level Tactical Training Blu-ray", False, 5, 7, 10),
|
||||
}
|
||||
@@ -1,597 +0,0 @@
|
||||
Magical Wonderland|0-48|Default Music|True|1|3|0|
|
||||
Iyaiya|0-0|Default Music|True|1|4|0|
|
||||
Wonderful Pain|0-2|Default Music|False|1|3|0|
|
||||
Breaking Dawn|0-3|Default Music|True|2|4|0|
|
||||
One-Way Subway|0-4|Default Music|True|1|4|0|
|
||||
Frost Land|0-1|Default Music|False|1|3|6|
|
||||
Heart-Pounding Flight|0-5|Default Music|True|2|5|0|
|
||||
Pancake is Love|0-29|Default Music|True|2|4|7|
|
||||
Shiguang Tuya|0-6|Default Music|True|2|5|0|
|
||||
Evolution|0-37|Default Music|False|2|4|7|
|
||||
Dolphin and Broadcast|0-7|Default Music|True|2|5|0|
|
||||
Yuki no Shizuku Ame no Oto|0-8|Default Music|True|2|4|6|
|
||||
Best One feat.tooko|0-43|Default Music|False|3|5|0|
|
||||
Candy-coloured Love Theory|0-31|Default Music|False|2|4|6|
|
||||
Night Wander|0-38|Default Music|False|3|5|7|
|
||||
Dohna Dohna no Uta|0-46|Default Music|False|2|4|6|
|
||||
Spring Carnival|0-9|Default Music|False|2|4|7|
|
||||
DISCO NIGHT|0-30|Default Music|True|2|4|7|
|
||||
Koi no Moonlight|0-49|Default Music|False|2|5|8|
|
||||
Lian Ai Audio Navigation|0-10|Default Music|False|3|5|7|
|
||||
Lights of Muse|0-11|Default Music|True|4|6|8|10
|
||||
midstream jam|0-12|Default Music|False|2|5|8|
|
||||
Nihao|0-40|Default Music|False|3|5|7|
|
||||
Confession|0-13|Default Music|False|3|5|8|
|
||||
Galaxy Striker|0-32|Default Music|False|4|7|9|
|
||||
Departure Road|0-14|Default Music|True|2|5|8|
|
||||
Bass Telekinesis|0-15|Default Music|False|2|5|8|
|
||||
Cage of Almeria|0-16|Default Music|True|3|5|7|
|
||||
Ira|0-17|Default Music|True|4|6|8|
|
||||
Blackest Luxury Car|0-18|Default Music|True|3|6|8|
|
||||
Medicine of Sing|0-19|Default Music|False|3|6|8|
|
||||
irregulyze|0-20|Default Music|True|3|6|8|
|
||||
I don't care about Christmas though|0-47|Default Music|False|4|6|8|
|
||||
Imaginary World|0-21|Default Music|True|4|6|8|10
|
||||
Dysthymia|0-22|Default Music|True|4|7|9|
|
||||
From the New World|0-42|Default Music|False|2|5|7|
|
||||
NISEGAO|0-33|Default Music|True|4|7|9|
|
||||
Say! Fanfare!|0-44|Default Music|False|4|6|9|
|
||||
Star Driver|0-34|Default Music|True|5|7|9|
|
||||
Formation|0-23|Default Music|True|4|6|9|
|
||||
Shinsou Masui|0-24|Default Music|True|4|6|10|
|
||||
Mezame Eurythmics|0-50|Default Music|False|4|6|9|
|
||||
Shenri Kuaira -repeat-|0-51|Default Music|False|5|7|9|
|
||||
Latitude|0-25|Default Music|True|3|6|9|
|
||||
Aqua Stars|0-39|Default Music|False|5|7|10|
|
||||
Funkotsu Saishin Casino|0-26|Default Music|False|5|7|10|
|
||||
Clock Room & Spiritual World|0-27|Default Music|True|4|6|9|
|
||||
INTERNET OVERDOSE|0-52|Default Music|False|3|6|9|
|
||||
Tu Hua|0-35|Default Music|True|4|7|9|
|
||||
Mujinku-Vacuum|0-28|Default Music|False|5|7|11|
|
||||
MilK|0-36|Default Music|False|5|7|9|
|
||||
umpopoff|0-41|Default Music|False|0|?|0|
|
||||
Mopemope|0-45|Default Music|False|4|7|9|11
|
||||
The Happycore Idol|43-0|MD Plus Project|True|2|5|7|
|
||||
Amatsumikaboshi|43-1|MD Plus Project|True|4|6|8|10
|
||||
ARIGA THESIS|43-2|MD Plus Project|True|3|6|10|
|
||||
Night of Nights|43-3|MD Plus Project|False|4|7|10|
|
||||
#Psychedelic_Meguro_River|43-4|MD Plus Project|False|3|6|8|
|
||||
can you feel it|43-5|MD Plus Project|False|4|6|8|9
|
||||
Midnight O'clock|43-6|MD Plus Project|True|3|6|8|
|
||||
Rin|43-7|MD Plus Project|True|5|7|10|
|
||||
Smile-mileS|43-8|MD Plus Project|False|6|8|10|
|
||||
Believing and Being|43-9|MD Plus Project|True|4|6|9|
|
||||
Catalyst|43-10|MD Plus Project|False|5|7|9|
|
||||
don't!stop!eroero!|43-11|MD Plus Project|True|5|7|9|
|
||||
pa pi pu pi pu pi pa|43-12|MD Plus Project|False|6|8|10|
|
||||
Sand Maze|43-13|MD Plus Project|True|6|8|10|11
|
||||
Diffraction|43-14|MD Plus Project|True|5|8|10|
|
||||
AKUMU|43-15|MD Plus Project|False|4|6|8|
|
||||
Queen Aluett|43-16|MD Plus Project|True|7|9|11|
|
||||
DROPS|43-17|MD Plus Project|False|2|5|8|
|
||||
Frightfully-insane Flan-chan's frightful song|43-18|MD Plus Project|False|5|7|10|
|
||||
snooze|43-19|MD Plus Project|False|5|7|10|
|
||||
Kuishinbo Hacker feat.Kuishinbo Akachan|43-20|MD Plus Project|True|5|7|9|
|
||||
Inu no outa|43-21|MD Plus Project|True|3|5|7|
|
||||
Prism Fountain|43-22|MD Plus Project|True|7|9|11|
|
||||
Gospel|43-23|MD Plus Project|False|4|6|9|
|
||||
East Ai Li Lovely|62-0|Happy Otaku Pack Vol.17|False|2|4|7|
|
||||
Mori Umi no Fune|62-1|Happy Otaku Pack Vol.17|True|5|7|9|
|
||||
Ooi|62-2|Happy Otaku Pack Vol.17|True|5|7|10|
|
||||
Numatta!!|62-3|Happy Otaku Pack Vol.17|True|5|7|9|
|
||||
SATELLITE|62-4|Happy Otaku Pack Vol.17|False|5|7|9|10
|
||||
Fantasia Sonata Colorful feat. V!C|62-5|Happy Otaku Pack Vol.17|True|6|8|11|
|
||||
MuseDash ka nanika hi|61-0|Ola Dash|True|?|?|¿|
|
||||
Aleph-0|61-1|Ola Dash|True|7|9|11|
|
||||
Buttoba Supernova|61-2|Ola Dash|False|5|7|10|11
|
||||
Rush-Hour|61-3|Ola Dash|False|IG|Jh|a2|Eh
|
||||
3rd Avenue|61-4|Ola Dash|False|3|5|〇|
|
||||
WORLDINVADER|61-5|Ola Dash|True|5|8|10|11
|
||||
N3V3R G3T OV3R|60-0|maimai DX Limited-time Suite|True|4|7|10|
|
||||
Oshama Scramble!|60-1|maimai DX Limited-time Suite|True|5|7|10|
|
||||
Valsqotch|60-2|maimai DX Limited-time Suite|True|5|9|11|
|
||||
Paranormal My Mind|60-3|maimai DX Limited-time Suite|True|5|7|9|
|
||||
Flower, snow and Drum'n'bass.|60-4|maimai DX Limited-time Suite|True|5|8|10|?
|
||||
Amenohoakari|60-5|maimai DX Limited-time Suite|True|6|8|10|
|
||||
Boiling Blood|59-0|MSR Anthology|True|5|8|10|
|
||||
ManiFesto|59-1|MSR Anthology|True|4|6|9|
|
||||
Operation Blade|59-2|MSR Anthology|True|3|5|7|
|
||||
Radiant|59-3|MSR Anthology|True|3|5|8|
|
||||
Renegade|59-4|MSR Anthology|True|3|5|8|
|
||||
Speed of Light|59-5|MSR Anthology|False|1|4|7|
|
||||
Dossoles Holiday|59-6|MSR Anthology|True|5|7|9|
|
||||
Autumn Moods|59-7|MSR Anthology|True|3|5|7|
|
||||
People People|58-0|Nanahira Paradise|True|5|7|9|11
|
||||
Endless Error Loop|58-1|Nanahira Paradise|True|4|7|9|
|
||||
Forbidden Pizza!|58-2|Nanahira Paradise|True|5|7|9|
|
||||
Don't Make the Vocalist do Anything Insane|58-3|Nanahira Paradise|True|5|8|9|
|
||||
Tokimeki*Meteostrike|57-0|Happy Otaku Pack Vol.16|True|3|6|8|
|
||||
Down Low|57-1|Happy Otaku Pack Vol.16|True|4|6|8|
|
||||
LOUDER MACHINE|57-2|Happy Otaku Pack Vol.16|True|5|7|9|
|
||||
Sorewa mo Lovechu|57-3|Happy Otaku Pack Vol.16|True|5|7|10|
|
||||
Rave_Tech|57-4|Happy Otaku Pack Vol.16|True|5|8|10|
|
||||
Brilliant & Shining!|57-5|Happy Otaku Pack Vol.16|False|5|8|10|
|
||||
Psyched Fevereiro|56-0|Give Up TREATMENT Vol.11|False|5|8|10|
|
||||
Inferno City|56-1|Give Up TREATMENT Vol.11|False|6|8|10|
|
||||
Paradigm Shift|56-2|Give Up TREATMENT Vol.11|False|4|7|10|
|
||||
Snapdragon|56-3|Give Up TREATMENT Vol.11|False|5|7|10|
|
||||
Prestige and Vestige|56-4|Give Up TREATMENT Vol.11|True|6|8|11|
|
||||
Tiny Fate|56-5|Give Up TREATMENT Vol.11|False|7|9|11|
|
||||
Tsuki ni Murakumo Hana ni Kaze|55-0|Touhou Mugakudan -2-|False|3|5|7|
|
||||
Patchouli's - Best Hit GSK|55-1|Touhou Mugakudan -2-|False|3|5|8|
|
||||
Monosugoi Space Shuttle de Koishi ga Monosugoi uta|55-2|Touhou Mugakudan -2-|False|3|5|7|11
|
||||
Kakoinaki Yo wa Ichigo no Tsukikage|55-3|Touhou Mugakudan -2-|False|3|6|8|
|
||||
Psychedelic Kizakura Doumei|55-4|Touhou Mugakudan -2-|False|4|7|10|
|
||||
Mischievous Sensation|55-5|Touhou Mugakudan -2-|False|5|7|9|
|
||||
White Canvas|54-0|MEGAREX THE FUTURE|False|3|6|8|
|
||||
Gloomy Flash|54-1|MEGAREX THE FUTURE|False|5|8|10|
|
||||
Find this Month's Featured Playlist|54-2|MEGAREX THE FUTURE|False|?|?|¿|
|
||||
Sunday Night|54-3|MEGAREX THE FUTURE|False|3|6|9|
|
||||
Goodbye Goodnight|54-4|MEGAREX THE FUTURE|False|4|6|9|
|
||||
ENDLESS CIDER|54-5|MEGAREX THE FUTURE|False|4|6|8|
|
||||
On And On!!|53-0|Happy Otaku Pack Vol.15|True|4|7|9|11
|
||||
Trip!|53-1|Happy Otaku Pack Vol.15|True|3|5|7|
|
||||
Hoshi no otoshimono|53-2|Happy Otaku Pack Vol.15|False|5|7|9|
|
||||
Plucky Race|53-3|Happy Otaku Pack Vol.15|True|5|8|10|11
|
||||
Fantasia Sonata Destiny|53-4|Happy Otaku Pack Vol.15|True|3|7|10|
|
||||
Run through|53-5|Happy Otaku Pack Vol.15|False|5|8|10|
|
||||
marooned night|52-0|MUSE RADIO FM103|False|2|4|6|
|
||||
daydream girl|52-1|MUSE RADIO FM103|False|3|6|8|
|
||||
Not Ornament|52-2|MUSE RADIO FM103|True|3|5|8|
|
||||
Baby Pink|52-3|MUSE RADIO FM103|False|3|5|8|
|
||||
I'm Here|52-4|MUSE RADIO FM103|False|4|6|8|
|
||||
Masquerade Diary|51-0|Virtual Idol Production|True|2|5|8|
|
||||
Reminiscence|51-1|Virtual Idol Production|True|5|7|9|
|
||||
DarakuDatenshi|51-2|Virtual Idol Production|True|3|6|9|
|
||||
D.I.Y.|51-3|Virtual Idol Production|False|4|6|9|
|
||||
Boys in Virtual Land|51-4|Virtual Idol Production|False|4|7|9|
|
||||
kui|51-5|Virtual Idol Production|True|5|7|9|11
|
||||
Nyan Cat|50-0|Nyanya Universe!|False|4|7|9|
|
||||
PeroPero in the Universe|50-1|Nyanya Universe!|True|?|?|¿|
|
||||
In-kya Yo-kya Onmyoji|50-2|Nyanya Universe!|False|6|8|10|
|
||||
KABOOOOOM!!!!|50-3|Nyanya Universe!|True|4|6|8|
|
||||
Doppelganger|50-4|Nyanya Universe!|True|5|7|9|12
|
||||
Pray a LOVE|49-0|DokiDoki! Valentine!|False|2|5|8|
|
||||
Love-Avoidance Addiction|49-1|DokiDoki! Valentine!|False|3|5|7|
|
||||
Daisuki Dayo feat.Wotoha|49-2|DokiDoki! Valentine!|False|5|7|10|
|
||||
glory day|48-0|DJMAX Reflect|False|2|5|7|
|
||||
Bright Dream|48-1|DJMAX Reflect|False|2|4|7|
|
||||
Groovin Up|48-2|DJMAX Reflect|False|4|6|8|
|
||||
I Want You|48-3|DJMAX Reflect|False|3|6|8|
|
||||
OBLIVION|48-4|DJMAX Reflect|False|3|6|9|
|
||||
Elastic STAR|48-5|DJMAX Reflect|False|4|6|8|
|
||||
U.A.D|48-6|DJMAX Reflect|False|4|6|8|10
|
||||
Jealousy|48-7|DJMAX Reflect|False|3|5|7|
|
||||
Memory of Beach|48-8|DJMAX Reflect|False|3|6|8|
|
||||
Don't Die|48-9|DJMAX Reflect|False|6|8|10|
|
||||
Y CE Ver.|48-10|DJMAX Reflect|False|4|6|9|
|
||||
Fancy Night|48-11|DJMAX Reflect|False|4|6|8|
|
||||
Can We Talk|48-12|DJMAX Reflect|False|4|6|8|
|
||||
Give Me 5|48-13|DJMAX Reflect|False|2|6|8|
|
||||
Nightmare|48-14|DJMAX Reflect|False|7|9|11|
|
||||
Haze of Autumn|47-0|Arcaea|True|3|6|9|
|
||||
GIMME DA BLOOD|47-1|Arcaea|False|3|6|9|
|
||||
Libertas|47-2|Arcaea|False|4|7|10|
|
||||
Cyaegha|47-3|Arcaea|False|5|7|9|11
|
||||
Bang!!|46-0|Happy Otaku Pack Vol.14|False|4|6|8|
|
||||
Paradise 2|46-1|Happy Otaku Pack Vol.14|False|4|6|8|
|
||||
Symbol|46-2|Happy Otaku Pack Vol.14|False|5|7|9|
|
||||
Nekojarashi|46-3|Happy Otaku Pack Vol.14|False|5|8|10|11
|
||||
A Philosophical Wanderer|46-4|Happy Otaku Pack Vol.14|False|4|6|10|
|
||||
Isouten|46-5|Happy Otaku Pack Vol.14|True|6|8|10|11
|
||||
ONOMATO Pairing!!!|45-0|WACCA Horizon|False|4|6|9|
|
||||
with U|45-1|WACCA Horizon|False|6|8|10|11
|
||||
Chariot|45-2|WACCA Horizon|False|3|6|9|
|
||||
GASHATT|45-3|WACCA Horizon|False|5|7|10|
|
||||
LIN NE KRO NE feat. lasah|45-4|WACCA Horizon|False|6|8|10|
|
||||
ANGEL HALO|45-5|WACCA Horizon|False|5|8|11|
|
||||
Party in the HOLLOWood|44-0|Happy Otaku Pack Vol.13|False|3|6|8|
|
||||
Ying Ying da Zuozhan|44-1|Happy Otaku Pack Vol.13|True|5|7|9|
|
||||
Howlin' Pumpkin|44-2|Happy Otaku Pack Vol.13|True|4|6|8|
|
||||
Bad Apple!! feat. Nomico|42-0|Touhou Mugakudan -1-|False|1|3|6|8
|
||||
Iro wa Nioedo, Chirinuru wo|42-1|Touhou Mugakudan -1-|False|2|4|7|
|
||||
Cirno's Perfect Math Class|42-2|Touhou Mugakudan -1-|False|4|7|9|
|
||||
Hiiro Gekka Kyousai no Zetsu|42-3|Touhou Mugakudan -1-|False|4|6|8|
|
||||
Flowery Moonlit Night|42-4|Touhou Mugakudan -1-|False|3|6|8|
|
||||
Unconscious Requiem|42-5|Touhou Mugakudan -1-|False|3|6|8|
|
||||
Super Battleworn Insomniac|41-0|7th Beat Games|True|4|7|9|?
|
||||
Bomb-Sniffing Pomeranian|41-1|7th Beat Games|True|4|6|8|
|
||||
Rollerdisco Rumble|41-2|7th Beat Games|True|4|6|9|
|
||||
Rose Garden|41-3|7th Beat Games|False|5|8|9|
|
||||
EMOMOMO|41-4|7th Beat Games|True|4|7|10|
|
||||
Heracles|41-5|7th Beat Games|False|6|8|10|?
|
||||
Rush-More|40-0|Happy Otaku Pack Vol.12|False|4|7|9|
|
||||
Kill My Fortune|40-1|Happy Otaku Pack Vol.12|False|5|7|10|
|
||||
Yosari Tsukibotaru Suminoborite|40-2|Happy Otaku Pack Vol.12|False|5|7|9|
|
||||
JUMP! HardCandy|40-3|Happy Otaku Pack Vol.12|False|3|6|8|
|
||||
Hibari|40-4|Happy Otaku Pack Vol.12|False|3|5|8|
|
||||
OCCHOCO-REST-LESS|40-5|Happy Otaku Pack Vol.12|True|4|7|9|
|
||||
See-Saw Day|39-0|MUSE RADIO FM102|True|1|3|6|
|
||||
happy hour|39-1|MUSE RADIO FM102|True|2|4|7|
|
||||
Seikimatsu no Natsu|39-2|MUSE RADIO FM102|True|4|6|8|
|
||||
twinkle night|39-3|MUSE RADIO FM102|False|3|6|8|
|
||||
ARUYA HARERUYA|39-4|MUSE RADIO FM102|False|2|5|7|
|
||||
Blush|39-5|MUSE RADIO FM102|False|2|4|7|
|
||||
Naked Summer|39-6|MUSE RADIO FM102|True|4|6|8|
|
||||
BLESS ME|39-7|MUSE RADIO FM102|True|2|5|7|
|
||||
FM 17314 SUGAR RADIO|39-8|MUSE RADIO FM102|True|?|?|?|
|
||||
NO ONE YES MAN|38-0|Phigros|False|5|7|9|
|
||||
Snowfall, Merry Christmas|38-1|Phigros|False|5|8|10|
|
||||
Igallta|38-2|Phigros|False|6|8|10|11
|
||||
Colored Glass|37-0|Cute Is Everything Vol.7|False|1|4|7|
|
||||
Neonlights|37-1|Cute Is Everything Vol.7|False|4|7|9|
|
||||
Hope for the flowers|37-2|Cute Is Everything Vol.7|False|4|7|9|
|
||||
Seaside Cycling on May 30|37-3|Cute Is Everything Vol.7|False|3|6|8|
|
||||
SKY HIGH|37-4|Cute Is Everything Vol.7|False|2|4|6|
|
||||
Mousou Chu!!|37-5|Cute Is Everything Vol.7|False|4|7|8|
|
||||
NightTheater|36-0|Give Up TREATMENT Vol.10|True|6|8|11|
|
||||
Cutter|36-1|Give Up TREATMENT Vol.10|False|4|7|10|
|
||||
bamboo|36-2|Give Up TREATMENT Vol.10|False|6|8|10|11
|
||||
enchanted love|36-3|Give Up TREATMENT Vol.10|False|2|6|9|
|
||||
c.s.q.n.|36-4|Give Up TREATMENT Vol.10|False|5|8|11|
|
||||
Booouncing!!|36-5|Give Up TREATMENT Vol.10|False|5|7|10|
|
||||
PeroPeroGames goes Bankrupt|35-0|Happy Otaku Pack SP|True|6|8|10|
|
||||
MARENOL|35-1|Happy Otaku Pack SP|False|4|7|10|
|
||||
I am really good at Japanese style|35-2|Happy Otaku Pack SP|True|6|8|10|
|
||||
Rush B|35-3|Happy Otaku Pack SP|True|4|7|9|
|
||||
DataErr0r|35-4|Happy Otaku Pack SP|False|5|7|9|?
|
||||
Burn|35-5|Happy Otaku Pack SP|True|4|7|9|
|
||||
ALiVE|34-0|HARDCORE TANO*C|False|5|7|10|
|
||||
BATTLE NO.1|34-1|HARDCORE TANO*C|False|5|8|10|11
|
||||
Cthugha|34-2|HARDCORE TANO*C|False|6|8|10|11
|
||||
TWINKLE*MAGIC|34-3|HARDCORE TANO*C|False|4|7|10|11
|
||||
Comet Coaster|34-4|HARDCORE TANO*C|False|6|8|10|11
|
||||
XODUS|34-5|HARDCORE TANO*C|False|7|9|11|12
|
||||
Fireflies|33-0|cyTus|True|1|4|7|
|
||||
Light up my love!!|33-1|cyTus|True|3|5|7|
|
||||
Happiness Breeze|33-2|cyTus|True|4|6|8|9
|
||||
Chrome VOX|33-3|cyTus|True|6|8|10|11
|
||||
CHAOS|33-4|cyTus|True|3|6|9|
|
||||
Saika|33-5|cyTus|True|3|5|8|
|
||||
Standby for Action|33-6|cyTus|True|4|6|8|
|
||||
Hydrangea|33-7|cyTus|True|5|7|9|
|
||||
Amenemhat|33-8|cyTus|True|6|8|10|
|
||||
Santouka|33-9|cyTus|True|2|5|8|
|
||||
HEXENNACHTROCK-katashihaya-|33-10|cyTus|True|4|8|10|
|
||||
Blah!!|33-11|cyTus|True|5|8|11|
|
||||
CHAOS Glitch|33-12|cyTus|True|0|?|0|
|
||||
Preparara|32-0|Let's Do Bad Things Together|False|1|4|6|
|
||||
Whatcha;Whatcha Doin'|32-1|Let's Do Bad Things Together|False|3|6|9|
|
||||
Madara|32-2|Let's Do Bad Things Together|False|4|6|9|
|
||||
pICARESq|32-3|Let's Do Bad Things Together|False|4|6|8|
|
||||
Desastre|32-4|Let's Do Bad Things Together|False|4|6|8|
|
||||
Shoot for the Moon|32-5|Let's Do Bad Things Together|False|2|5|8|
|
||||
The 90's Decision|31-0|Happy Otaku Pack Vol.11|True|5|7|9|
|
||||
Medusa|31-1|Happy Otaku Pack Vol.11|False|4|6|8|10
|
||||
Final Step!|31-2|Happy Otaku Pack Vol.11|False|5|7|10|
|
||||
MAGENTA POTION|31-3|Happy Otaku Pack Vol.11|False|4|7|9|
|
||||
Cross Ray|31-4|Happy Otaku Pack Vol.11|False|3|6|9|
|
||||
Square Lake|31-5|Happy Otaku Pack Vol.11|False|6|8|9|11
|
||||
Girly Cupid|30-0|Cute Is Everything Vol.6|False|3|6|8|
|
||||
sheep in the light|30-1|Cute Is Everything Vol.6|False|2|5|8|
|
||||
Breaker city|30-2|Cute Is Everything Vol.6|False|4|6|9|
|
||||
heterodoxy|30-3|Cute Is Everything Vol.6|False|4|6|8|
|
||||
Computer Music Girl|30-4|Cute Is Everything Vol.6|False|3|5|7|
|
||||
Focus Point|30-5|Cute Is Everything Vol.6|True|2|5|7|
|
||||
Groove Prayer|29-0|Let' s GROOVE!|True|3|5|7|
|
||||
FUJIN Rumble|29-1|Let' s GROOVE!|True|5|7|10|11
|
||||
Marry me, Nightmare|29-2|Let' s GROOVE!|False|6|8|11|
|
||||
HG Makaizou Polyvinyl Shounen|29-3|Let' s GROOVE!|True|4|7|9|10
|
||||
Seizya no Ibuki|29-4|Let' s GROOVE!|True|6|8|10|
|
||||
ouroboros -twin stroke of the end-|29-5|Let' s GROOVE!|True|4|6|9|12
|
||||
Heisha Onsha|28-0|Happy Otaku Pack Vol.10|False|4|6|8|
|
||||
Ginevra|28-1|Happy Otaku Pack Vol.10|True|5|7|10|10
|
||||
Paracelestia|28-2|Happy Otaku Pack Vol.10|False|5|8|10|
|
||||
un secret|28-3|Happy Otaku Pack Vol.10|False|2|4|6|
|
||||
Good Life|28-4|Happy Otaku Pack Vol.10|False|4|6|8|
|
||||
nini-nini-|28-5|Happy Otaku Pack Vol.10|False|4|7|9|
|
||||
Can I friend you on Bassbook? lol|27-0|Nanahira Festival|False|3|6|8|
|
||||
Gaming*Everything|27-1|Nanahira Festival|False|5|8|11|
|
||||
Renji de haochi|27-2|Nanahira Festival|False|5|7|9|
|
||||
You Make My Life 1UP|27-3|Nanahira Festival|False|4|6|8|
|
||||
Newbies, Geeks, Internets|27-4|Nanahira Festival|False|6|8|10|
|
||||
Onegai!Kon kon Oinarisama|27-5|Nanahira Festival|False|3|6|9|
|
||||
Legend of Eastern Rabbit -SKY DEFENDER-|26-0|Give Up TREATMENT Vol.9|False|4|6|9|
|
||||
ENERGY SYNERGY MATRIX|26-1|Give Up TREATMENT Vol.9|False|6|8|10|
|
||||
Punai Punai Genso|26-2|Give Up TREATMENT Vol.9|False|2|7|11|
|
||||
Better Graphic Animation|26-3|Give Up TREATMENT Vol.9|False|5|8|11|
|
||||
Variant Cross|26-4|Give Up TREATMENT Vol.9|False|4|7|10|
|
||||
Ultra Happy Miracle Bazoooooka!!|26-5|Give Up TREATMENT Vol.9|False|7|9|11|
|
||||
tape/stop/night|25-0|MUSE RADIO FM101|True|3|5|7|
|
||||
Pixel Galaxy|25-1|MUSE RADIO FM101|False|2|5|8|
|
||||
Notice|25-2|MUSE RADIO FM101|False|4|7|10|
|
||||
Strawberry Godzilla|25-3|MUSE RADIO FM101|True|2|5|7|
|
||||
OKIMOCHI EXPRESSION|25-4|MUSE RADIO FM101|False|4|6|10|
|
||||
Kimi to pool disco|25-5|MUSE RADIO FM101|False|4|6|8|
|
||||
The Last Page|24-0|Happy Otaku Pack Vol.9|False|3|5|7|
|
||||
IKAROS|24-1|Happy Otaku Pack Vol.9|False|4|7|10|
|
||||
Tsukuyomi|24-2|Happy Otaku Pack Vol.9|False|3|6|9|
|
||||
Future Stream|24-3|Happy Otaku Pack Vol.9|False|4|6|8|
|
||||
FULi AUTO SHOOTER|24-4|Happy Otaku Pack Vol.9|True|4|7|9|
|
||||
GOODFORTUNE|24-5|Happy Otaku Pack Vol.9|False|5|7|9|
|
||||
The Dessert After Rain|23-0|Cute Is Everything Vol.5|True|2|4|6|
|
||||
Confession Support Formula|23-1|Cute Is Everything Vol.5|False|3|5|7|
|
||||
Omatsuri|23-2|Cute Is Everything Vol.5|False|1|3|6|
|
||||
FUTUREPOP|23-3|Cute Is Everything Vol.5|True|2|5|7|
|
||||
The Breeze|23-4|Cute Is Everything Vol.5|False|1|4|6|
|
||||
I LOVE LETTUCE FRIED RICE!!|23-5|Cute Is Everything Vol.5|False|3|7|9|
|
||||
The NightScape|22-0|Give Up TREATMENT Vol.8|False|4|7|9|
|
||||
FREEDOM DiVE|22-1|Give Up TREATMENT Vol.8|False|6|8|10|12
|
||||
Phi|22-2|Give Up TREATMENT Vol.8|False|5|8|10|
|
||||
Lueur de la nuit|22-3|Give Up TREATMENT Vol.8|False|6|8|11|
|
||||
Creamy Sugary OVERDRIVE!!!|22-4|Give Up TREATMENT Vol.8|True|4|7|10|
|
||||
Disorder|22-5|Give Up TREATMENT Vol.8|False|5|7|11|
|
||||
Glimmer|21-0|Budget Is Burning: Nano Core|False|2|5|8|
|
||||
EXIST|21-1|Budget Is Burning: Nano Core|False|3|5|8|
|
||||
Irreplaceable|21-2|Budget Is Burning: Nano Core|False|4|6|8|
|
||||
Moonlight Banquet|20-0|Happy Otaku Pack Vol.8|True|2|5|8|
|
||||
Flashdance|20-1|Happy Otaku Pack Vol.8|False|3|6|9|
|
||||
INFiNiTE ENERZY -Overdoze-|20-2|Happy Otaku Pack Vol.8|False|4|7|9|10
|
||||
One Way Street|20-3|Happy Otaku Pack Vol.8|False|3|6|10|
|
||||
This Club is Not 4 U|20-4|Happy Otaku Pack Vol.8|False|4|7|9|
|
||||
ULTRA MEGA HAPPY PARTY!!!|20-5|Happy Otaku Pack Vol.8|False|5|7|10|
|
||||
INFINITY|19-0|Give Up TREATMENT Vol.7|True|5|8|10|
|
||||
Punai Punai Senso|19-1|Give Up TREATMENT Vol.7|False|2|7|11|
|
||||
Maxi|19-2|Give Up TREATMENT Vol.7|False|5|8|10|
|
||||
YInMn Blue|19-3|Give Up TREATMENT Vol.7|False|6|8|10|
|
||||
Plumage|19-4|Give Up TREATMENT Vol.7|False|4|7|10|
|
||||
Dr.Techro|19-5|Give Up TREATMENT Vol.7|False|7|9|11|
|
||||
SWEETSWEETSWEET|18-0|Cute Is Everything Vol.4|True|2|5|7|
|
||||
Deep Blue and the Breaths of the Night|18-1|Cute Is Everything Vol.4|True|2|4|6|
|
||||
Joy Connection|18-2|Cute Is Everything Vol.4|False|3|6|8|
|
||||
Self Willed Girl Ver.B|18-3|Cute Is Everything Vol.4|True|4|6|8|
|
||||
Just Disobedient|18-4|Cute Is Everything Vol.4|False|3|6|8|
|
||||
Holy Sh*t Grass Snake|18-5|Cute Is Everything Vol.4|False|2|6|9|
|
||||
Cotton Candy Wonderland|17-0|Happy Otaku Pack Vol.7|False|2|5|8|
|
||||
Punai Punai Taiso|17-1|Happy Otaku Pack Vol.7|False|2|7|10|
|
||||
Fly High|17-2|Happy Otaku Pack Vol.7|False|3|5|7|
|
||||
prejudice|17-3|Happy Otaku Pack Vol.7|True|4|6|9|
|
||||
The 89's Momentum|17-4|Happy Otaku Pack Vol.7|True|5|7|9|
|
||||
energy night|17-5|Happy Otaku Pack Vol.7|True|5|7|10|
|
||||
Future Dive|16-0|Give Up TREATMENT Vol.6|True|4|6|9|
|
||||
Re End of a Dream|16-1|Give Up TREATMENT Vol.6|False|5|8|11|
|
||||
Etude -Storm-|16-2|Give Up TREATMENT Vol.6|True|6|8|10|
|
||||
Unlimited Katharsis|16-3|Give Up TREATMENT Vol.6|False|4|6|10|
|
||||
Magic Knight Girl|16-4|Give Up TREATMENT Vol.6|False|4|7|9|
|
||||
Eeliaas|16-5|Give Up TREATMENT Vol.6|False|6|9|11|
|
||||
Magic Spell|15-0|Cute Is Everything Vol.3|True|2|5|7|
|
||||
Colorful Star, Colored Drawing, Travel Poem|15-1|Cute Is Everything Vol.3|False|3|4|6|
|
||||
Satell Knight|15-2|Cute Is Everything Vol.3|False|3|6|8|
|
||||
Black River Feat.Mes|15-3|Cute Is Everything Vol.3|True|1|4|6|
|
||||
I am sorry|15-4|Cute Is Everything Vol.3|False|2|5|8|
|
||||
Ueta Tori Tachi|15-5|Cute Is Everything Vol.3|False|3|6|8|
|
||||
Elysion's Old Mans|14-0|Happy Otaku Pack Vol.6|False|3|5|8|
|
||||
AXION|14-1|Happy Otaku Pack Vol.6|False|4|5|8|
|
||||
Amnesia|14-2|Happy Otaku Pack Vol.6|True|3|6|9|
|
||||
Onsen Dai Sakusen|14-3|Happy Otaku Pack Vol.6|True|4|6|8|
|
||||
Gleam stone|14-4|Happy Otaku Pack Vol.6|False|4|7|9|
|
||||
GOODWORLD|14-5|Happy Otaku Pack Vol.6|False|4|7|10|
|
||||
Instant Soluble Neon|13-0|Cute Is Everything Vol.2|True|2|4|7|
|
||||
Retrospective Poem on the Planet|13-1|Cute Is Everything Vol.2|False|3|5|7|
|
||||
I'm Gonna Buy! Buy! Buy!|13-2|Cute Is Everything Vol.2|True|4|6|8|
|
||||
Dating Manifesto|13-3|Cute Is Everything Vol.2|True|2|4|6|
|
||||
First Snow|13-4|Cute Is Everything Vol.2|True|2|3|6|
|
||||
Xin Shang Huahai|13-5|Cute Is Everything Vol.2|False|3|6|8|
|
||||
Gaikan Chrysalis|12-0|Give Up TREATMENT Vol.5|False|4|6|8|
|
||||
Sterelogue|12-1|Give Up TREATMENT Vol.5|True|5|7|10|
|
||||
Cheshire's Dance|12-2|Give Up TREATMENT Vol.5|True|4|7|10|
|
||||
Skrik|12-3|Give Up TREATMENT Vol.5|True|5|7|11|
|
||||
Soda Pop Canva5!|12-4|Give Up TREATMENT Vol.5|False|5|8|10|
|
||||
RUBY LINTe|12-5|Give Up TREATMENT Vol.5|False|5|8|11|
|
||||
Brave My Heart|11-0|Happy Otaku Pack Vol.5|True|3|5|7|
|
||||
Sakura Fubuki|11-1|Happy Otaku Pack Vol.5|False|4|7|10|
|
||||
8bit Adventurer|11-2|Happy Otaku Pack Vol.5|False|6|8|10|
|
||||
Suffering of screw|11-3|Happy Otaku Pack Vol.5|False|3|5|8|
|
||||
tiny lady|11-4|Happy Otaku Pack Vol.5|True|4|6|9|
|
||||
Power Attack|11-5|Happy Otaku Pack Vol.5|False|5|7|10|
|
||||
Destr0yer|10-0|Give Up TREATMENT Vol.4|False|4|7|9|
|
||||
Noel|10-1|Give Up TREATMENT Vol.4|False|5|8|10|
|
||||
Kyoukiranbu|10-2|Give Up TREATMENT Vol.4|False|7|9|11|
|
||||
Two Phace|10-3|Give Up TREATMENT Vol.4|True|4|7|10|
|
||||
Fly Again|10-4|Give Up TREATMENT Vol.4|False|5|7|10|
|
||||
ouroVoros|10-5|Give Up TREATMENT Vol.4|False|7|9|11|
|
||||
Leave It Alone|9-0|Happy Otaku Pack Vol.4|True|2|5|8|
|
||||
Tsubasa no Oreta Tenshitachi no Requiem|9-1|Happy Otaku Pack Vol.4|False|4|7|9|
|
||||
Chronomia|9-2|Happy Otaku Pack Vol.4|False|5|7|10|
|
||||
Dandelion's Daydream|9-3|Happy Otaku Pack Vol.4|True|5|7|8|
|
||||
Lorikeet Flat design|9-4|Happy Otaku Pack Vol.4|True|5|7|10|
|
||||
GOODRAGE|9-5|Happy Otaku Pack Vol.4|False|6|9|11|
|
||||
Altale|8-0|Give Up TREATMENT Vol.3|False|3|5|7|10
|
||||
Brain Power|8-1|Give Up TREATMENT Vol.3|False|4|7|10|
|
||||
Berry Go!!|8-2|Give Up TREATMENT Vol.3|False|3|6|9|
|
||||
Sweet* Witch* Girl*|8-3|Give Up TREATMENT Vol.3|False|6|8|10|?
|
||||
trippers feeling!|8-4|Give Up TREATMENT Vol.3|True|5|7|9|11
|
||||
Lilith ambivalence lovers|8-5|Give Up TREATMENT Vol.3|False|5|8|10|
|
||||
Brave My Soul|7-0|Give Up TREATMENT Vol.2|False|4|6|8|
|
||||
Halcyon|7-1|Give Up TREATMENT Vol.2|False|4|7|10|
|
||||
Crimson Nightingale|7-2|Give Up TREATMENT Vol.2|True|4|7|10|
|
||||
Invader|7-3|Give Up TREATMENT Vol.2|True|3|7|11|
|
||||
Lyrith|7-4|Give Up TREATMENT Vol.2|False|5|7|10|
|
||||
GOODBOUNCE|7-5|Give Up TREATMENT Vol.2|False|4|6|9|
|
||||
Out of Sense|6-0|Budget Is Burning Vol.1|False|3|5|8|
|
||||
My Life Is For You|6-1|Budget Is Burning Vol.1|False|2|4|7|
|
||||
Etude -Sunset-|6-2|Budget Is Burning Vol.1|True|5|7|9|
|
||||
Goodbye Boss|6-3|Budget Is Burning Vol.1|False|4|6|8|
|
||||
Stargazer|6-4|Budget Is Burning Vol.1|True|2|5|8|9
|
||||
Lys Tourbillon|6-5|Budget Is Burning Vol.1|True|4|6|8|
|
||||
Thirty Million Persona|5-0|Happy Otaku Pack Vol.3|False|2|4|6|
|
||||
conflict|5-1|Happy Otaku Pack Vol.3|False|2|6|9|10
|
||||
Enka Dance Music|5-2|Happy Otaku Pack Vol.3|False|3|5|7|
|
||||
XING|5-3|Happy Otaku Pack Vol.3|True|4|6|8|9
|
||||
Amakakeru Soukyuu no Serenade|5-4|Happy Otaku Pack Vol.3|False|3|6|9|
|
||||
Gift box|5-5|Happy Otaku Pack Vol.3|False|5|7|10|
|
||||
MUSEDASH!!!!|4-0|Happy Otaku Pack Vol.2|False|2|6|9|0
|
||||
Imprinting|4-1|Happy Otaku Pack Vol.2|False|3|6|9|0
|
||||
Skyward|4-2|Happy Otaku Pack Vol.2|True|4|7|10|0
|
||||
La nuit de vif|4-3|Happy Otaku Pack Vol.2|True|2|5|8|0
|
||||
Bit-alize|4-4|Happy Otaku Pack Vol.2|False|3|6|8|0
|
||||
GOODTEK|4-5|Happy Otaku Pack Vol.2|False|4|6|9|?
|
||||
Maharajah|3-0|Happy Otaku Pack Vol.1|False|1|3|6|
|
||||
keep on running|3-1|Happy Otaku Pack Vol.1|False|5|7|9|
|
||||
Kafig|3-2|Happy Otaku Pack Vol.1|True|4|6|8|
|
||||
-+|3-3|Happy Otaku Pack Vol.1|True|4|6|8|
|
||||
Tenri Kaku Jou|3-4|Happy Otaku Pack Vol.1|True|3|6|9|
|
||||
Adjudicatorz-DanZai-|3-5|Happy Otaku Pack Vol.1|False|3|7|10|
|
||||
Oriens|2-0|Give Up TREATMENT Vol.1|True|3|7|9|
|
||||
PUPA|2-1|Give Up TREATMENT Vol.1|False|6|8|11|
|
||||
Luna Express 2032|2-2|Give Up TREATMENT Vol.1|False|4|6|8|
|
||||
Ukiyoe Yokochou|2-3|Give Up TREATMENT Vol.1|False|6|7|9|
|
||||
Alice in Misanthrope|2-4|Give Up TREATMENT Vol.1|False|5|7|10|
|
||||
GOODMEN|2-5|Give Up TREATMENT Vol.1|False|5|7|10|
|
||||
Sunshine and Rainbow after August Rain|1-0|Cute Is Everything Vol.1|False|2|5|8|
|
||||
Magical Number|1-1|Cute Is Everything Vol.1|False|2|5|8|
|
||||
Dreaming Girl|1-2|Cute Is Everything Vol.1|False|2|5|6|
|
||||
Daruma-san Fell Over|1-3|Cute Is Everything Vol.1|False|3|4|6|
|
||||
Different|1-4|Cute Is Everything Vol.1|False|1|3|6|
|
||||
The Future of the Phantom|1-5|Cute Is Everything Vol.1|False|1|3|5|
|
||||
Doki Doki Jump!|63-0|MUSE RADIO FM104|True|3|5|7|
|
||||
Centennial Streamers High|63-1|MUSE RADIO FM104|False|4|7|9|
|
||||
Love Patrol|63-2|MUSE RADIO FM104|True|3|5|7|
|
||||
Mahorova|63-3|MUSE RADIO FM104|True|3|5|8|
|
||||
Yoru no machi|63-4|MUSE RADIO FM104|True|1|4|7|
|
||||
INTERNET YAMERO|63-5|MUSE RADIO FM104|True|6|8|10|
|
||||
Abracadabra|43-24|MD Plus Project|False|6|8|10|
|
||||
Squalldecimator feat. EZ-Ven|43-25|MD Plus Project|True|5|7|9|
|
||||
Amateras Rhythm|43-26|MD Plus Project|True|6|8|11|
|
||||
Record one's Dream|43-27|MD Plus Project|False|4|7|10|
|
||||
Lunatic|43-28|MD Plus Project|True|5|8|10|
|
||||
Jiumeng|43-29|MD Plus Project|True|3|6|8|
|
||||
The Day We Become Family|43-30|MD Plus Project|True|3|5|8|
|
||||
Sutori ma FIRE!?!?|64-0|COSMIC RADIO PEROLIST|True|3|5|8|
|
||||
Tanuki Step|64-1|COSMIC RADIO PEROLIST|True|5|7|10|11
|
||||
Space Stationery|64-2|COSMIC RADIO PEROLIST|True|5|7|10|
|
||||
Songs Are Judged 90% by Chorus feat. Mameko|64-3|COSMIC RADIO PEROLIST|True|6|8|10|
|
||||
Kawai Splendid Space Thief|64-4|COSMIC RADIO PEROLIST|False|6|8|10|11
|
||||
Night City Runway|64-5|COSMIC RADIO PEROLIST|True|4|6|8|
|
||||
Chaos Shotgun feat. ChumuNote|64-6|COSMIC RADIO PEROLIST|True|6|8|10|
|
||||
mew mew magical summer|64-7|COSMIC RADIO PEROLIST|False|5|8|10|11
|
||||
BrainDance|65-0|NeonAbyss|True|3|6|9|
|
||||
My Focus!|65-1|NeonAbyss|True|5|7|10|
|
||||
ABABABA BURST|65-2|NeonAbyss|True|5|7|9|
|
||||
ULTRA HIGHER|65-3|NeonAbyss|True|4|7|10|
|
||||
Silver Bullet|43-31|MD Plus Project|True|5|7|10|
|
||||
Random|43-32|MD Plus Project|True|4|7|9|
|
||||
OTOGE-BOSS-KYOKU-CHAN|43-33|MD Plus Project|False|6|8|10|11
|
||||
Crow Rabbit|43-34|MD Plus Project|True|7|9|11|
|
||||
SyZyGy|43-35|MD Plus Project|True|6|8|10|11
|
||||
Mermaid Radio|43-36|MD Plus Project|True|3|5|7|
|
||||
Helixir|43-37|MD Plus Project|False|6|8|10|
|
||||
Highway Cruisin'|43-38|MD Plus Project|False|3|5|8|
|
||||
JACK PT BOSS|43-39|MD Plus Project|False|6|8|10|
|
||||
Time Capsule|43-40|MD Plus Project|False|7|9|11|
|
||||
39 Music!|66-0|Miku in Museland|False|3|5|8|
|
||||
Hand in Hand|66-1|Miku in Museland|False|1|3|6|
|
||||
Cynical Night Plan|66-2|Miku in Museland|False|4|6|8|
|
||||
God-ish|66-3|Miku in Museland|False|4|7|10|
|
||||
Darling Dance|66-4|Miku in Museland|False|4|7|9|
|
||||
Hatsune Creation Myth|66-5|Miku in Museland|False|6|8|10|11
|
||||
The Vampire|66-6|Miku in Museland|False|4|6|9|
|
||||
Future Eve|66-7|Miku in Museland|False|4|8|11|
|
||||
Unknown Mother Goose|66-8|Miku in Museland|False|4|8|10|
|
||||
Shun-ran|66-9|Miku in Museland|False|4|7|9|
|
||||
NICE TYPE feat. monii|43-41|MD Plus Project|True|3|6|8|
|
||||
Rainy Angel|67-0|Happy Otaku Pack Vol.18|True|4|6|9|11
|
||||
Gullinkambi|67-1|Happy Otaku Pack Vol.18|True|4|7|10|
|
||||
RakiRaki Rebuilders!!!|67-2|Happy Otaku Pack Vol.18|True|5|7|10|
|
||||
Laniakea|67-3|Happy Otaku Pack Vol.18|False|5|8|10|
|
||||
OTTAMA GAZER|67-4|Happy Otaku Pack Vol.18|True|5|8|10|
|
||||
Sleep Tight feat.Macoto|67-5|Happy Otaku Pack Vol.18|True|3|5|8|
|
||||
New York Back Raise|68-0|Gambler's Tricks|True|6|8|10|
|
||||
slic.hertz|68-1|Gambler's Tricks|True|5|7|9|
|
||||
Fuzzy-Navel|68-2|Gambler's Tricks|True|6|8|10|11
|
||||
Swing Edge|68-3|Gambler's Tricks|True|4|8|10|
|
||||
Twisted Escape|68-4|Gambler's Tricks|True|5|8|10|11
|
||||
Swing Sweet Twee Dance|68-5|Gambler's Tricks|False|4|7|10|
|
||||
Sanyousei SAY YA!!!|43-42|MD Plus Project|False|4|6|8|
|
||||
YUKEMURI TAMAONSEN II|43-43|MD Plus Project|False|3|6|9|
|
||||
Samayoi no mei Amatsu|69-0|Touhou Mugakudan -3-|False|4|6|9|
|
||||
INTERNET SURVIVOR|69-1|Touhou Mugakudan -3-|False|5|8|10|
|
||||
Shuki*RaiRai|69-2|Touhou Mugakudan -3-|False|5|7|9|
|
||||
HELLOHELL|69-3|Touhou Mugakudan -3-|False|4|7|10|
|
||||
Calamity Fortune|69-4|Touhou Mugakudan -3-|True|6|8|10|11
|
||||
Tsurupettan|69-5|Touhou Mugakudan -3-|True|2|5|8|
|
||||
Twilight Poems|43-44|MD Plus Project|True|3|6|8|
|
||||
All My Friends feat. RANASOL|43-45|MD Plus Project|True|4|7|9|
|
||||
Heartache|43-46|MD Plus Project|True|5|7|10|
|
||||
Blue Lemonade|43-47|MD Plus Project|True|3|6|8|
|
||||
Haunted Dance|43-48|MD Plus Project|False|6|9|11|
|
||||
Hey Vincent.|43-49|MD Plus Project|True|6|8|10|
|
||||
Meteor feat. TEA|43-50|MD Plus Project|True|3|6|9|
|
||||
Narcissism Angel|43-51|MD Plus Project|True|1|3|6|
|
||||
AlterLuna|43-52|MD Plus Project|True|6|8|11|12
|
||||
Niki Tousen|43-53|MD Plus Project|True|6|8|10|12
|
||||
Rettou Joutou|70-0|Rin Len's Mirrorland|False|4|7|9|
|
||||
Telecaster B-Boy|70-1|Rin Len's Mirrorland|False|5|7|10|
|
||||
Iya Iya Iya|70-2|Rin Len's Mirrorland|False|2|4|7|
|
||||
Nee Nee Nee|70-3|Rin Len's Mirrorland|False|4|6|8|
|
||||
Chaotic Love Revolution|70-4|Rin Len's Mirrorland|False|4|6|8|
|
||||
Dance of the Corpses|70-5|Rin Len's Mirrorland|False|2|5|8|
|
||||
Bitter Choco Decoration|70-6|Rin Len's Mirrorland|False|3|6|9|
|
||||
Dance Robot Dance|70-7|Rin Len's Mirrorland|False|4|7|10|
|
||||
Sweet Devil|70-8|Rin Len's Mirrorland|False|5|7|9|
|
||||
Someday'z Coming|70-9|Rin Len's Mirrorland|False|5|7|9|
|
||||
Yume Ou Mono Yo Secret|0-53|Default Music|True|6|8|10|
|
||||
Yume Ou Mono Yo|0-54|Default Music|True|1|4|0|
|
||||
Sweet Dream VIVINOS|71-0|Valentine Stage|False|1|4|7|
|
||||
Ruler Of My Heart VIVINOS|71-1|Valentine Stage|False|2|4|6|
|
||||
Reality Show|71-2|Valentine Stage|False|5|7|10|
|
||||
SIG feat.Tobokegao|71-3|Valentine Stage|True|3|6|8|
|
||||
Rose Love|71-4|Valentine Stage|True|2|4|7|
|
||||
Euphoria|71-5|Valentine Stage|True|1|3|6|
|
||||
P E R O P E R O Brother Dance|72-0|Legends of Muse Warriors|True|0|?|0|
|
||||
PA PPA PANIC|72-1|Legends of Muse Warriors|False|4|8|10|
|
||||
How To Make Music Game Song!|72-2|Legends of Muse Warriors|True|6|8|10|11
|
||||
Re Re|72-3|Legends of Muse Warriors|True|7|9|11|12
|
||||
Marmalade Twins|72-4|Legends of Muse Warriors|True|5|8|10|
|
||||
DOMINATOR|72-5|Legends of Muse Warriors|True|7|9|11|
|
||||
Teshikani TESHiKANi|72-6|Legends of Muse Warriors|True|5|7|9|
|
||||
Urban Magic|73-0|Happy Otaku Pack Vol.19|True|3|5|7|
|
||||
Maid's Prank|73-1|Happy Otaku Pack Vol.19|True|5|7|10|
|
||||
Dance Dance Good Night Dance|73-2|Happy Otaku Pack Vol.19|True|2|4|7|
|
||||
Ops Limone|73-3|Happy Otaku Pack Vol.19|True|5|8|11|
|
||||
NOVA|73-4|Happy Otaku Pack Vol.19|True|6|8|10|
|
||||
Heaven's Gradius|73-5|Happy Otaku Pack Vol.19|True|6|8|10|
|
||||
Ray Tuning|74-0|CHUNITHM COURSE MUSE|True|6|8|10|
|
||||
World Vanquisher|74-1|CHUNITHM COURSE MUSE|True|6|8|10|11
|
||||
Tsukuyomi Ni Naru Replaced|74-2|CHUNITHM COURSE MUSE|True|5|7|9|
|
||||
The wheel to the right|74-3|CHUNITHM COURSE MUSE|True|5|7|9|11
|
||||
Climax|74-4|CHUNITHM COURSE MUSE|True|4|8|11|11
|
||||
Spider's Thread|74-5|CHUNITHM COURSE MUSE|True|5|8|10|12
|
||||
HIT ME UP|43-54|MD Plus Project|True|4|6|8|
|
||||
Test Me feat. Uyeon|43-55|MD Plus Project|True|3|5|9|
|
||||
Assault TAXI|43-56|MD Plus Project|True|4|7|10|
|
||||
No|43-57|MD Plus Project|False|4|6|9|
|
||||
Pop it|43-58|MD Plus Project|True|1|3|6|
|
||||
HEARTBEAT! KyunKyun!|43-59|MD Plus Project|True|4|6|9|
|
||||
SUPERHERO|75-0|Novice Rider Pack|False|2|4|7|
|
||||
Highway_Summer|75-1|Novice Rider Pack|True|2|4|6|
|
||||
Mx. Black Box|75-2|Novice Rider Pack|True|5|7|9|
|
||||
Sweet Encounter|75-3|Novice Rider Pack|True|2|4|7|
|
||||
Echo over you... Secret|0-55|Default Music|False|6|8|10|
|
||||
Echo over you...|0-56|Default Music|False|1|4|0|
|
||||
Tsukuyomi Ni Naru|74-6|CHUNITHM COURSE MUSE|True|5|8|10|
|
||||
disco light|76-0|MUSE RADIO FM105|True|5|7|9|
|
||||
room light feat.chancylemon|76-1|MUSE RADIO FM105|True|3|5|7|
|
||||
Invisible|76-2|MUSE RADIO FM105|True|3|5|8|
|
||||
Christmas Season-LLABB|76-3|MUSE RADIO FM105|True|1|4|7|
|
||||
Hyouryu|77-0|Let's Rhythm Jam!|False|6|8|10|
|
||||
The Whole Rest|77-1|Let's Rhythm Jam!|False|5|8|10|11
|
||||
Hydra|77-2|Let's Rhythm Jam!|False|4|7|11|
|
||||
Pastel Lines|77-3|Let's Rhythm Jam!|False|3|6|9|
|
||||
LINK x LIN#S|77-4|Let's Rhythm Jam!|False|3|6|9|
|
||||
Arcade ViruZ|77-5|Let's Rhythm Jam!|False|6|8|11|
|
||||
Eve Avenir|78-0|Endless Pirouette|True|6|8|10|
|
||||
Silverstring|78-1|Endless Pirouette|True|5|7|10|
|
||||
Melusia|78-2|Endless Pirouette|False|5|7|10|11
|
||||
Devil's Castle|78-3|Endless Pirouette|True|4|7|10|
|
||||
Abatement|78-4|Endless Pirouette|True|6|8|10|11
|
||||
Azalea|78-5|Endless Pirouette|False|4|8|10|
|
||||
Brightly World|78-6|Endless Pirouette|True|6|8|10|
|
||||
We'll meet in every world ***|78-7|Endless Pirouette|True|7|9|11|
|
||||
Collapsar|78-8|Endless Pirouette|True|7|9|10|11
|
||||
Parousia|78-9|Endless Pirouette|False|6|8|10|
|
||||
Gunners in the Rain|79-0|Ensemble Arcanum|False|5|8|10|
|
||||
Halzion|79-1|Ensemble Arcanum|False|2|5|8|
|
||||
SHOWTIME!!|79-2|Ensemble Arcanum|False|6|8|10|
|
||||
Achromic Riddle|79-3|Ensemble Arcanum|False|6|8|10|11
|
||||
karanosu|79-4|Ensemble Arcanum|False|3|6|8|
|
||||
@@ -1,13 +1,14 @@
|
||||
from Options import Toggle, Range, Choice, DeathLink, ItemSet, OptionSet, PerGameCommonOptions, OptionGroup, Removed
|
||||
from Options import Toggle, Range, Choice, DeathLink, OptionSet, PerGameCommonOptions, OptionGroup, Removed
|
||||
from dataclasses import dataclass
|
||||
|
||||
from .MuseDashCollection import MuseDashCollections
|
||||
from .MuseDashData import SONG_DATA
|
||||
|
||||
|
||||
class DLCMusicPacks(OptionSet):
|
||||
"""
|
||||
Choose which DLC Packs will be included in the pool of chooseable songs.
|
||||
|
||||
|
||||
Note: The [Just As Planned] DLC contains all [Muse Plus] songs.
|
||||
"""
|
||||
display_name = "DLC Packs"
|
||||
@@ -17,7 +18,7 @@ class DLCMusicPacks(OptionSet):
|
||||
class StreamerModeEnabled(Toggle):
|
||||
"""
|
||||
In Muse Dash, an option named 'Streamer Mode' removes songs which may trigger copyright issues when streaming.
|
||||
|
||||
|
||||
If this is enabled, only songs available under Streamer Mode will be available for randomization.
|
||||
"""
|
||||
display_name = "Streamer Mode Only Songs"
|
||||
@@ -69,7 +70,7 @@ class DifficultyMode(Choice):
|
||||
class DifficultyModeOverrideMin(Range):
|
||||
"""
|
||||
Ensures that 1 difficulty has at least 1 this value or higher per song.
|
||||
|
||||
|
||||
Note: Difficulty Mode must be set to Manual.
|
||||
"""
|
||||
display_name = "Manual Difficulty Min"
|
||||
@@ -82,7 +83,7 @@ class DifficultyModeOverrideMin(Range):
|
||||
class DifficultyModeOverrideMax(Range):
|
||||
"""
|
||||
Ensures that 1 difficulty has at least 1 this value or lower per song.
|
||||
|
||||
|
||||
Note: Difficulty Mode must be set to Manual.
|
||||
"""
|
||||
display_name = "Manual Difficulty Max"
|
||||
@@ -114,7 +115,7 @@ class GradeNeeded(Choice):
|
||||
class MusicSheetCountPercentage(Range):
|
||||
"""
|
||||
Controls how many music sheets are added to the pool based on the number of songs, including starting songs.
|
||||
|
||||
|
||||
Higher numbers leads to more consistent game lengths, but will cause individual music sheets to be less important.
|
||||
"""
|
||||
range_start = 10
|
||||
@@ -137,7 +138,7 @@ class ChosenTraps(OptionSet):
|
||||
- Traps last the length of a song, or until you die.
|
||||
- VFX Traps consist of visual effects that play over the song. (i.e. Grayscale.)
|
||||
- SFX Traps consist of changing your sfx setting to one possibly more annoying sfx.
|
||||
|
||||
|
||||
Note: SFX traps are only available if [Just as Planned] DLC songs are enabled.
|
||||
"""
|
||||
display_name = "Chosen Traps"
|
||||
@@ -152,24 +153,26 @@ class TrapCountPercentage(Range):
|
||||
display_name = "Trap Percentage"
|
||||
|
||||
|
||||
class IncludeSongs(ItemSet):
|
||||
class SongSet(OptionSet):
|
||||
valid_keys = SONG_DATA.keys()
|
||||
|
||||
|
||||
class IncludeSongs(SongSet):
|
||||
"""
|
||||
These songs will be guaranteed to show up within the seed.
|
||||
- You must have the DLC enabled to play these songs.
|
||||
- Difficulty options will not affect these songs.
|
||||
- If there are too many included songs, this will act as a whitelist ignoring song difficulty.
|
||||
"""
|
||||
verify_item_name = True
|
||||
display_name = "Include Songs"
|
||||
|
||||
|
||||
class ExcludeSongs(ItemSet):
|
||||
class ExcludeSongs(SongSet):
|
||||
"""
|
||||
These songs will be guaranteed to not show up within the seed.
|
||||
|
||||
|
||||
Note: Does not affect songs within the "Include Songs" list.
|
||||
"""
|
||||
verify_item_name = True
|
||||
display_name = "Exclude Songs"
|
||||
|
||||
|
||||
@@ -211,7 +214,7 @@ class MuseDashOptions(PerGameCommonOptions):
|
||||
death_link: DeathLink
|
||||
include_songs: IncludeSongs
|
||||
exclude_songs: ExcludeSongs
|
||||
|
||||
|
||||
# Removed
|
||||
allow_just_as_planned_dlc_songs: Removed
|
||||
available_trap_types: Removed
|
||||
|
||||
@@ -63,6 +63,11 @@ class MuseDashWorld(World):
|
||||
|
||||
item_name_to_id = {name: code for name, code in md_collection.item_names_to_id.items()}
|
||||
location_name_to_id = {name: code for name, code in md_collection.location_names_to_id.items()}
|
||||
item_name_groups = {
|
||||
"Songs": {name for name in md_collection.song_items.keys()},
|
||||
"Filler Items": {name for name in md_collection.filler_items.keys()},
|
||||
"Traps": {name for name in md_collection.trap_items.keys()}
|
||||
}
|
||||
|
||||
# Working Data
|
||||
victory_song_name: str = ""
|
||||
@@ -179,10 +184,6 @@ class MuseDashWorld(World):
|
||||
if trap:
|
||||
return MuseDashFixedItem(name, ItemClassification.trap, trap, self.player)
|
||||
|
||||
album = self.md_collection.album_items.get(name)
|
||||
if album:
|
||||
return MuseDashSongItem(name, self.player, album)
|
||||
|
||||
song = self.md_collection.song_items[name]
|
||||
return MuseDashSongItem(name, self.player, song)
|
||||
|
||||
|
||||
@@ -1,7 +1,17 @@
|
||||
from . import MuseDashTestBase
|
||||
from typing import List
|
||||
|
||||
|
||||
class DifficultyRanges(MuseDashTestBase):
|
||||
DIFF_OVERRIDES: List[str] = [
|
||||
"MuseDash ka nanika hi",
|
||||
"Rush-Hour",
|
||||
"Find this Month's Featured Playlist",
|
||||
"PeroPero in the Universe",
|
||||
"umpopoff",
|
||||
"P E R O P E R O Brother Dance",
|
||||
]
|
||||
|
||||
def test_all_difficulty_ranges(self) -> None:
|
||||
muse_dash_world = self.get_world()
|
||||
dlc_set = {x for x in muse_dash_world.md_collection.DLC}
|
||||
@@ -63,7 +73,7 @@ class DifficultyRanges(MuseDashTestBase):
|
||||
def test_songs_have_difficulty(self) -> None:
|
||||
muse_dash_world = self.get_world()
|
||||
|
||||
for song_name in muse_dash_world.md_collection.DIFF_OVERRIDES:
|
||||
for song_name in self.DIFF_OVERRIDES:
|
||||
song = muse_dash_world.md_collection.song_items[song_name]
|
||||
|
||||
# Some songs are weird and have less than the usual 3 difficulties.
|
||||
|
||||
@@ -2200,7 +2200,7 @@ def patch_rom(world, rom):
|
||||
elif world.shuffle_bosses != 'off':
|
||||
vanilla_reward = world.get_location(boss_name).vanilla_item
|
||||
vanilla_reward_location = world.multiworld.find_item(vanilla_reward, world.player) # hinted_dungeon_reward_locations[vanilla_reward.name]
|
||||
area = HintArea.at(vanilla_reward_location).text(world.clearer_hints, preposition=True)
|
||||
area = HintArea.at(vanilla_reward_location).text(world.hint_rng, world.clearer_hints, preposition=True)
|
||||
compass_message = "\x13\x75\x08You found the \x05\x41Compass\x05\x40\x01for %s\x05\x40!\x01The %s can be found\x01%s!\x09" % (dungeon_name, vanilla_reward, area)
|
||||
else:
|
||||
boss_location = next(filter(lambda loc: loc.type == 'Boss', world.get_entrance(f'{dungeon} Boss Door -> {boss_name} Boss Room').connected_region.locations))
|
||||
|
||||
@@ -582,8 +582,7 @@ class OOTWorld(World):
|
||||
new_exit = OOTEntrance(self.player, self.multiworld, '%s -> %s' % (new_region.name, exit), new_region)
|
||||
new_exit.vanilla_connected_region = exit
|
||||
new_exit.rule_string = rule
|
||||
if self.options.logic_rules != 'no_logic':
|
||||
self.parser.parse_spot_rule(new_exit)
|
||||
self.parser.parse_spot_rule(new_exit)
|
||||
if new_exit.never:
|
||||
logger.debug('Dropping unreachable exit: %s', new_exit.name)
|
||||
else:
|
||||
|
||||
@@ -287,7 +287,7 @@ class PokemonEmeraldClient(BizHawkClient):
|
||||
pokedex_caught_bytes = read_result[0]
|
||||
|
||||
game_clear = False
|
||||
local_checked_locations = set()
|
||||
local_checked_locations: set[int] = set()
|
||||
local_set_events = {flag_name: False for flag_name in TRACKER_EVENT_FLAGS}
|
||||
local_found_key_items = {location_name: False for location_name in KEY_LOCATION_FLAGS}
|
||||
defeated_legendaries = {legendary_name: False for legendary_name in LEGENDARY_NAMES.values()}
|
||||
@@ -350,10 +350,7 @@ class PokemonEmeraldClient(BizHawkClient):
|
||||
self.local_checked_locations = local_checked_locations
|
||||
|
||||
if local_checked_locations is not None:
|
||||
await ctx.send_msgs([{
|
||||
"cmd": "LocationChecks",
|
||||
"locations": list(local_checked_locations),
|
||||
}])
|
||||
await ctx.check_locations(local_checked_locations)
|
||||
|
||||
# Send game clear
|
||||
if not ctx.finished_game and game_clear:
|
||||
|
||||
@@ -87,6 +87,21 @@ class SMZ3World(World):
|
||||
self.rom_name_available_event = threading.Event()
|
||||
self.locations: Dict[str, Location] = {}
|
||||
self.unreachable = []
|
||||
self.junkItemsNames = [item.name for item in [
|
||||
ItemType.Arrow,
|
||||
ItemType.OneHundredRupees,
|
||||
ItemType.TenArrows,
|
||||
ItemType.ThreeBombs,
|
||||
ItemType.OneRupee,
|
||||
ItemType.FiveRupees,
|
||||
ItemType.TwentyRupees,
|
||||
ItemType.FiftyRupees,
|
||||
ItemType.ThreeHundredRupees,
|
||||
ItemType.ETank,
|
||||
ItemType.Missile,
|
||||
ItemType.Super,
|
||||
ItemType.PowerBomb
|
||||
]]
|
||||
super().__init__(world, player)
|
||||
|
||||
@classmethod
|
||||
|
||||
@@ -309,7 +309,7 @@ class SoEOptions(PerGameCommonOptions):
|
||||
|
||||
@property
|
||||
def flags(self) -> str:
|
||||
flags = ''
|
||||
flags = 'AGBo' # configures auto-tracker to AP's fill
|
||||
for field in fields(self):
|
||||
option = getattr(self, field.name)
|
||||
if isinstance(option, (EvermizerFlag, EvermizerFlags)):
|
||||
|
||||
@@ -1,36 +1,37 @@
|
||||
pyevermizer==0.48.0 \
|
||||
--hash=sha256:069ce348e480e04fd6208cfd0f789c600b18d7c34b5272375b95823be191ed57 \
|
||||
--hash=sha256:58164dddaba2f340b0a8b4f39605e9dac46d8b0ffb16120e2e57bef2bfc1d683 \
|
||||
--hash=sha256:115dd09d38a10f11d4629b340dfd75e2ba4089a1ff9e9748a11619829e02c876 \
|
||||
--hash=sha256:b5e79cfe721e75cd7dec306b5eecd6385ce059e31ef7523ba7f677e22161ec6f \
|
||||
--hash=sha256:382882fa9d641b9969a6c3ed89449a814bdabcb6b17b558872d95008a6cc908b \
|
||||
--hash=sha256:92f67700e9132064a90858d391dd0b8fb111aff6dfd472befed57772d89ae567 \
|
||||
--hash=sha256:fe4c453b7dbd5aa834b81f9a7aedb949a605455650b938b8b304d8e5a7edcbf7 \
|
||||
--hash=sha256:c6bdbc45daf73818f763ed59ad079f16494593395d806f772dd62605c722b3e9 \
|
||||
--hash=sha256:bb09f45448fdfd28566ae6fcc38c35a6632f4c31a9de2483848f6ce17b2359b5 \
|
||||
--hash=sha256:00a8b9014744bd1528d0d39c33ede7c0d1713ad797a331cebb33d377a5bc1064 \
|
||||
--hash=sha256:64ee69edc0a7d3b3caded78f2e46975f9beaff1ff8feaf29b87da44c45f38d7d \
|
||||
--hash=sha256:9211bdb1313e9f4869ed5bdc61f3831d39679bd08bb4087f1c1e5475d9e3018b \
|
||||
--hash=sha256:4a57821e422a1d75fe3307931a78db7a65e76955f8e401c4b347db6570390d09 \
|
||||
--hash=sha256:04670cee0a0b913f24d2b9a1e771781560e2485bda31e6cd372a08421cf85cfa \
|
||||
--hash=sha256:971fe77d0a20a1db984020ad253b613d0983f5e23ff22cba60ee5ac00d8128de \
|
||||
--hash=sha256:127265fdb49f718f54706bf15604af1cec23590afd00d423089dea4331dcfc61 \
|
||||
--hash=sha256:d47576360337c1a23f424cd49944a8d68fc4f3338e00719c9f89972c84604bef \
|
||||
--hash=sha256:879659603e51130a0de8d9885d815a2fa1df8bd6cebe6d520d1c6002302adfdb \
|
||||
--hash=sha256:6a91bfc53dd130db6424adf8ac97a1133e97b4157ed00f889d8cbd26a2a4b340 \
|
||||
--hash=sha256:f3bf35fc5eef4cda49d2de77339fc201dd3206660a3dc15db005625b15bb806c \
|
||||
--hash=sha256:e7c8d5bf59a3c16db20411bc5d8e9c9087a30b6b4edf1b5ed9f4c013291427e4 \
|
||||
--hash=sha256:054a4d84ffe75448d41e88e1e0642ef719eb6111be5fe608e71e27a558c59069 \
|
||||
--hash=sha256:e6f141ca367469c69ba7fbf65836c479ec6672c598cfcb6b39e8098c60d346bc \
|
||||
--hash=sha256:6e65eb88f0c1ff4acde1c13b24ce649b0fe3d1d3916d02d96836c781a5022571 \
|
||||
--hash=sha256:e61e8f476b6da809cf38912755ed8bb009665f589e913eb8df877e9fa763024b \
|
||||
--hash=sha256:7e7c5484c0a2e3da6064de3f73d8d988d6703db58ab0be4730cbbf1a82319237 \
|
||||
--hash=sha256:9033b954e5f4878fd94af6d2056c78e3316115521fb1c24a4416d5cbf2ad66ad \
|
||||
--hash=sha256:824c623fff8ae4da176306c458ad63ad16a06a495a16db700665eca3c115924f \
|
||||
--hash=sha256:8e31031409a8386c6a63b79d480393481badb3ba29f32ff7a0db2b4abed20ac8 \
|
||||
--hash=sha256:7dbb7bb13e1e94f69f7ccdbcf4d35776424555fce5af1ca29d0256f91fdf087a \
|
||||
--hash=sha256:3a24e331b259407b6912d6e0738aa8a675831db3b7493fcf54dc17cb0cb80d37 \
|
||||
--hash=sha256:fdda06662a994271e96633cba100dd92b2fcd524acef8b2f664d1aaa14503cbd \
|
||||
--hash=sha256:0f0fc81bef3dbb78ba6a7622dd4296f23c59825968a0bb0448beb16eb3397cc2 \
|
||||
--hash=sha256:e07cbef776a7468669211546887357cc88e9afcf1578b23a4a4f2480517b15d9 \
|
||||
--hash=sha256:e442212695bdf60e455673b7b9dd83a5d4b830d714376477093d2c9054d92832
|
||||
pyevermizer==0.50.1 \
|
||||
--hash=sha256:4d1f43d5f8016e7bfcb5cd80b447a4f278b60b1b250a6153e66150230bf280e8 \
|
||||
--hash=sha256:06af4f66ae1f21932a936bf741a0547bbb8ff92eea8fb8efece6bc1760a8a999 \
|
||||
--hash=sha256:1ddbc36860704385a767d24364eac6504acc74f185c98b50cf52219c6e0148c6 \
|
||||
--hash=sha256:61f0adc4f615867e51bfcd7d7c90f19779a61391a995c721e7393005e8413950 \
|
||||
--hash=sha256:d84761ee03ebdaf011befe01638db1fff128b1c37405088868f0025e064977f3 \
|
||||
--hash=sha256:0433507dd8ad96375f3b64534faefdf9d325b69a19e108db1414fc75d6e72160 \
|
||||
--hash=sha256:e8857f719da9eaaa54f564886ff1b36cb89b8ccf08aa6ccca2d5d3c41da0b067 \
|
||||
--hash=sha256:40e76a30968b1fce3d727b47b2693d4151a9ad29b053a33bf06cde8fa63c3d15 \
|
||||
--hash=sha256:09ced5349a183656c1f8dcb85e41bdd496d1c5f2bb8f712d12a055d6efa7b917 \
|
||||
--hash=sha256:162806e7b0156e25612e60d25af68772cf553b3352a5cf31866d838295ccb591 \
|
||||
--hash=sha256:79750965bc63ffa351c167672b51c32f2a8d3242e07e769f925d1f306564a18d \
|
||||
--hash=sha256:b1875eb79c8800352f30180db296036d8b512082d6609e2368aa7032c1cf7e27 \
|
||||
--hash=sha256:7989e6f06c1ea38687a6b14416b179f459282ea81edbb86086d426fe0d63bf7a \
|
||||
--hash=sha256:8a4c5c62997e7378457624a88c12b27b52d345b365c3cfae7fee77ee46eb7cd0 \
|
||||
--hash=sha256:a22557f56ada1ace61b781e731e06466c22b6cc605c1aa9dec10e3697b10f5e6 \
|
||||
--hash=sha256:d1057e70be839e9c3a91f0f173bc795fc0014cf560767d699cc26eba5f5cfc6f \
|
||||
--hash=sha256:8540bd8e8ec49422b494beece1f6bf4cca61aa452a4c0f85c3a8b77283b24753 \
|
||||
--hash=sha256:569b98352fc6e1fae85a8c2ee3f2e61276762bc158ac5b7e07a476ee0f9e2617 \
|
||||
--hash=sha256:1b21eed21eb9338a6e7024b015d0107eaecf78c61f8ece8e6553d77f7f0ba726 \
|
||||
--hash=sha256:51ff863e92c7b608d464da10c775b5df5ad3651a05c2d316c1d60a46572fdef9 \
|
||||
--hash=sha256:0f920d745df15e3171412cbda05fc21c9354323d0e8dfc066ed6051fa7df9879 \
|
||||
--hash=sha256:d78970415fb03c1dd22aef8da7526e5b33eaf4c9848f5cbad843ad159254f526 \
|
||||
--hash=sha256:50536924bbf702d310b92d307d7c5060f6a4307bf99b61f79571ba2675ebb1ff \
|
||||
--hash=sha256:1123f8f87ce6415183126842eca1fff98362ff545204adfd4c7b6cf1c396b738 \
|
||||
--hash=sha256:1b248af5aa7321e46ae05675b15a5993e28311dfabc68cee2e398ce571f28eb2 \
|
||||
--hash=sha256:a76e9d17ec3af9317b3a9d5e9f9f04aea80a5902c33f6fe82d02381f2fd2cb69 \
|
||||
--hash=sha256:081ed52f8e1693ca48262cb5a9687ee62c4f9a50c667a487192c72be4c1b7fac \
|
||||
--hash=sha256:2978aa13826337d59799f41dda41fa4cecd9f59fae8843613230cf298b06fa6e \
|
||||
--hash=sha256:d377c2fd68c3d529d89ba40a762b6424c3b04c0d58593c02f06adbdf236f72ad \
|
||||
--hash=sha256:800d6c30eab6ca3ee39a6c297d08cb74cfa5a4bce498aa3f05a612596f8c513b \
|
||||
--hash=sha256:0cf40413f4b7ae5d561e47706f446b91440a1b74abe33b8fabc995d92c3325ca \
|
||||
--hash=sha256:97791b8695aa215ef407824d1e6c0582a2a2f89f3a0f254f5d791a5a84a0ad00 \
|
||||
--hash=sha256:2174db5e4550f94cb63e17584973c9f9afdc23e5230cb556de8bf87bd72145ff \
|
||||
--hash=sha256:f3a4cd6a9b292e7385722d8200e834a936886136ddaef2069035f7ec5eb50d34 \
|
||||
--hash=sha256:7646efdf7e091c75dac9aebb6c9faf215de4f6b8567c049944790e43cbe63d51 \
|
||||
--hash=sha256:cd56cca26ed9675790154dd70402ad28a381fc3c9031bd02eb9b1dad8c317398 \
|
||||
|
||||
@@ -12,13 +12,13 @@ class OoBTest(SoETestBase):
|
||||
# some locations that just need a weapon + OoB
|
||||
oob_reachable = [
|
||||
"Aquagoth", "Sons of Sth.", "Mad Monk", "Magmar", # OoB can use volcano shop to skip rock skip
|
||||
"Levitate", "Fireball", "Drain", "Speed",
|
||||
"Levitate", "Fireball", "Speed",
|
||||
"E. Crustacia #107", "Energy Core #285", "Vanilla Gauge #57",
|
||||
]
|
||||
# some locations that should still be unreachable
|
||||
oob_unreachable = [
|
||||
"Tiny", "Rimsala",
|
||||
"Barrier", "Call Up", "Reflect", "Force Field", "Stop", # Stop guy doesn't spawn for the other entrances
|
||||
"Barrier", "Drain", "Call Up", "Reflect", "Force Field", "Stop", # Stop guy only spawns from one entrance
|
||||
"Pyramid bottom #118", "Tiny's hideout #160", "Tiny's hideout #161", "Greenhouse #275",
|
||||
]
|
||||
# OoB + Diamond Eyes
|
||||
@@ -31,11 +31,42 @@ class OoBTest(SoETestBase):
|
||||
"Tiny's hideout #161",
|
||||
]
|
||||
|
||||
self.assertLocationReachability(reachable=oob_reachable, unreachable=oob_unreachable, satisfied=False)
|
||||
self.collect_by_name("Gladiator Sword")
|
||||
self.assertLocationReachability(reachable=oob_reachable, unreachable=oob_unreachable, satisfied=in_logic)
|
||||
self.collect_by_name("Diamond Eye")
|
||||
self.assertLocationReachability(reachable=de_reachable, unreachable=de_unreachable, satisfied=in_logic)
|
||||
with self.subTest("No items", oob_logic=in_logic):
|
||||
self.assertLocationReachability(reachable=oob_reachable, unreachable=oob_unreachable, satisfied=False)
|
||||
with self.subTest("Cutting Weapon", oob_logic=in_logic):
|
||||
self.collect_by_name("Gladiator Sword")
|
||||
self.assertLocationReachability(reachable=oob_reachable, unreachable=oob_unreachable, satisfied=in_logic)
|
||||
with self.subTest("Cutting Weapon + DEs", oob_logic=in_logic):
|
||||
self.collect_by_name("Diamond Eye")
|
||||
self.assertLocationReachability(reachable=de_reachable, unreachable=de_unreachable, satisfied=in_logic)
|
||||
|
||||
def test_real_axe(self) -> None:
|
||||
in_logic = self.options["out_of_bounds"] == "logic"
|
||||
|
||||
# needs real Bronze Axe+, regardless of OoB
|
||||
real_axe_required = [
|
||||
"Drain",
|
||||
"Drain Cave #180",
|
||||
"Drain Cave #181",
|
||||
]
|
||||
also_des_required = [
|
||||
"Double Drain",
|
||||
]
|
||||
|
||||
with self.subTest("No Axe", oob_logic=in_logic):
|
||||
self.collect_by_name("Gladiator Sword")
|
||||
self.assertLocationReachability(reachable=real_axe_required, satisfied=False)
|
||||
with self.subTest("Bronze Axe", oob_logic=in_logic):
|
||||
self.collect_by_name("Bronze Axe")
|
||||
self.assertLocationReachability(reachable=real_axe_required, satisfied=True)
|
||||
with self.subTest("Knight Basher", oob_logic=in_logic):
|
||||
self.remove_by_name("Bronze Axe")
|
||||
self.collect_by_name("Knight Basher")
|
||||
self.assertLocationReachability(reachable=real_axe_required, satisfied=True)
|
||||
self.assertLocationReachability(reachable=also_des_required, satisfied=False)
|
||||
with self.subTest("Knight Basher + DEs", oob_logic=in_logic):
|
||||
self.collect_by_name("Diamond Eye")
|
||||
self.assertLocationReachability(reachable=also_des_required, satisfied=True)
|
||||
|
||||
def test_oob_goal(self) -> None:
|
||||
# still need Energy Core with OoB if sequence breaks are not in logic
|
||||
|
||||
@@ -1,33 +1,27 @@
|
||||
import logging
|
||||
from random import Random
|
||||
from typing import Dict, Any, Iterable, Optional, Union, List, TextIO
|
||||
from typing import Dict, Any, Iterable, Optional, List, TextIO
|
||||
|
||||
from BaseClasses import Region, Entrance, Location, Item, Tutorial, ItemClassification, MultiWorld, CollectionState
|
||||
from Options import PerGameCommonOptions
|
||||
from worlds.AutoWorld import World, WebWorld
|
||||
from . import rules
|
||||
from .bundles.bundle_room import BundleRoom
|
||||
from .bundles.bundles import get_all_bundles
|
||||
from .content import content_packs, StardewContent, unpack_content, create_content
|
||||
from .content import StardewContent, create_content
|
||||
from .early_items import setup_early_items
|
||||
from .items import item_table, create_items, ItemData, Group, items_by_group, get_all_filler_items, remove_limited_amount_packs
|
||||
from .locations import location_table, create_locations, LocationData, locations_by_tag
|
||||
from .logic.bundle_logic import BundleLogic
|
||||
from .logic.logic import StardewLogic
|
||||
from .logic.time_logic import MAX_MONTHS
|
||||
from .options import StardewValleyOptions, SeasonRandomization, Goal, BundleRandomization, EnabledFillerBuffs, NumberOfMovementBuffs, \
|
||||
BuildingProgression, ExcludeGingerIsland, TrapItems, EntranceRandomization, FarmType, Walnutsanity
|
||||
BuildingProgression, ExcludeGingerIsland, TrapItems, EntranceRandomization, FarmType
|
||||
from .options.forced_options import force_change_options_if_incompatible
|
||||
from .options.option_groups import sv_option_groups
|
||||
from .options.presets import sv_options_presets
|
||||
from .regions import create_regions
|
||||
from .rules import set_rules
|
||||
from .stardew_rule import True_, StardewRule, HasProgressionPercent, true_
|
||||
from .stardew_rule import True_, StardewRule, HasProgressionPercent
|
||||
from .strings.ap_names.event_names import Event
|
||||
from .strings.entrance_names import Entrance as EntranceName
|
||||
from .strings.goal_names import Goal as GoalName
|
||||
from .strings.metal_names import Ore
|
||||
from .strings.region_names import Region as RegionName, LogicRegion
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -94,7 +88,6 @@ class StardewValleyWorld(World):
|
||||
randomized_entrances: Dict[str, str]
|
||||
|
||||
total_progression_items: int
|
||||
excluded_from_total_progression_items: List[str] = [Event.received_walnuts]
|
||||
|
||||
def __init__(self, multiworld: MultiWorld, player: int):
|
||||
super().__init__(multiworld, player)
|
||||
@@ -159,7 +152,7 @@ class StardewValleyWorld(World):
|
||||
self.multiworld.itempool += created_items
|
||||
|
||||
setup_early_items(self.multiworld, self.options, self.content, self.player, self.random)
|
||||
self.setup_player_events()
|
||||
self.setup_logic_events()
|
||||
self.setup_victory()
|
||||
|
||||
# This is really a best-effort to get the total progression items count. It is mostly used to spread grinds across spheres are push back locations that
|
||||
@@ -182,7 +175,7 @@ class StardewValleyWorld(World):
|
||||
|
||||
if self.options.season_randomization == SeasonRandomization.option_disabled:
|
||||
for season in season_pool:
|
||||
self.multiworld.push_precollected(self.create_starting_item(season))
|
||||
self.multiworld.push_precollected(self.create_item(season))
|
||||
return
|
||||
|
||||
if [item for item in self.multiworld.precollected_items[self.player]
|
||||
@@ -192,26 +185,12 @@ class StardewValleyWorld(World):
|
||||
if self.options.season_randomization == SeasonRandomization.option_randomized_not_winter:
|
||||
season_pool = [season for season in season_pool if season.name != "Winter"]
|
||||
|
||||
starting_season = self.create_starting_item(self.random.choice(season_pool))
|
||||
starting_season = self.create_item(self.random.choice(season_pool))
|
||||
self.multiworld.push_precollected(starting_season)
|
||||
|
||||
def precollect_farm_type_items(self):
|
||||
if self.options.farm_type == FarmType.option_meadowlands and self.options.building_progression & BuildingProgression.option_progressive:
|
||||
self.multiworld.push_precollected(self.create_starting_item("Progressive Coop"))
|
||||
|
||||
def setup_player_events(self):
|
||||
self.setup_action_events()
|
||||
self.setup_logic_events()
|
||||
|
||||
def setup_action_events(self):
|
||||
spring_farming = LocationData(None, LogicRegion.spring_farming, Event.spring_farming)
|
||||
self.create_event_location(spring_farming, true_, Event.spring_farming)
|
||||
summer_farming = LocationData(None, LogicRegion.summer_farming, Event.summer_farming)
|
||||
self.create_event_location(summer_farming, true_, Event.summer_farming)
|
||||
fall_farming = LocationData(None, LogicRegion.fall_farming, Event.fall_farming)
|
||||
self.create_event_location(fall_farming, true_, Event.fall_farming)
|
||||
winter_farming = LocationData(None, LogicRegion.winter_farming, Event.winter_farming)
|
||||
self.create_event_location(winter_farming, true_, Event.winter_farming)
|
||||
self.multiworld.push_precollected(self.create_item("Progressive Coop"))
|
||||
|
||||
def setup_logic_events(self):
|
||||
def register_event(name: str, region: str, rule: StardewRule):
|
||||
@@ -291,7 +270,7 @@ class StardewValleyWorld(World):
|
||||
def get_all_location_names(self) -> List[str]:
|
||||
return list(location.name for location in self.multiworld.get_locations(self.player))
|
||||
|
||||
def create_item(self, item: Union[str, ItemData], override_classification: ItemClassification = None) -> StardewItem:
|
||||
def create_item(self, item: str | ItemData, override_classification: ItemClassification = None) -> StardewItem:
|
||||
if isinstance(item, str):
|
||||
item = item_table[item]
|
||||
|
||||
@@ -300,12 +279,6 @@ class StardewValleyWorld(World):
|
||||
|
||||
return StardewItem(item.name, override_classification, item.code, self.player)
|
||||
|
||||
def create_starting_item(self, item: Union[str, ItemData]) -> StardewItem:
|
||||
if isinstance(item, str):
|
||||
item = item_table[item]
|
||||
|
||||
return StardewItem(item.name, item.classification, item.code, self.player)
|
||||
|
||||
def create_event_location(self, location_data: LocationData, rule: StardewRule = None, item: Optional[str] = None):
|
||||
if rule is None:
|
||||
rule = True_()
|
||||
@@ -413,9 +386,19 @@ class StardewValleyWorld(World):
|
||||
if not change:
|
||||
return False
|
||||
|
||||
player_state = state.prog_items[self.player]
|
||||
|
||||
received_progression_count = player_state[Event.received_progression_item]
|
||||
received_progression_count += 1
|
||||
if self.total_progression_items:
|
||||
# Total progression items is not set until all items are created, but collect will be called during the item creation when an item is precollected.
|
||||
# We can't update the percentage if we don't know the total progression items, can't divide by 0.
|
||||
player_state[Event.received_progression_percent] = received_progression_count * 100 // self.total_progression_items
|
||||
player_state[Event.received_progression_item] = received_progression_count
|
||||
|
||||
walnut_amount = self.get_walnut_amount(item.name)
|
||||
if walnut_amount:
|
||||
state.prog_items[self.player][Event.received_walnuts] += walnut_amount
|
||||
player_state[Event.received_walnuts] += walnut_amount
|
||||
|
||||
return True
|
||||
|
||||
@@ -424,9 +407,18 @@ class StardewValleyWorld(World):
|
||||
if not change:
|
||||
return False
|
||||
|
||||
player_state = state.prog_items[self.player]
|
||||
|
||||
received_progression_count = player_state[Event.received_progression_item]
|
||||
received_progression_count -= 1
|
||||
if self.total_progression_items:
|
||||
# We can't update the percentage if we don't know the total progression items, can't divide by 0.
|
||||
player_state[Event.received_progression_percent] = received_progression_count * 100 // self.total_progression_items
|
||||
player_state[Event.received_progression_item] = received_progression_count
|
||||
|
||||
walnut_amount = self.get_walnut_amount(item.name)
|
||||
if walnut_amount:
|
||||
state.prog_items[self.player][Event.received_walnuts] -= walnut_amount
|
||||
player_state[Event.received_walnuts] -= walnut_amount
|
||||
|
||||
return True
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ from ..strings.craftable_names import Bomb, Fence, Sprinkler, WildSeeds, Floor,
|
||||
from ..strings.crop_names import Fruit, Vegetable
|
||||
from ..strings.currency_names import Currency
|
||||
from ..strings.fertilizer_names import Fertilizer, RetainingSoil, SpeedGro
|
||||
from ..strings.fish_names import Fish, WaterItem, ModTrash
|
||||
from ..strings.fish_names import Fish, WaterItem, ModTrash, Trash
|
||||
from ..strings.flower_names import Flower
|
||||
from ..strings.food_names import Meal
|
||||
from ..strings.forageable_names import Forageable, SVEForage, DistantLandsForageable, Mushroom
|
||||
@@ -378,4 +378,12 @@ recycling_bin = skill_recipe(ModMachine.recycling_bin, ModSkill.binning, 7, {Met
|
||||
advanced_recycling_machine = skill_recipe(ModMachine.advanced_recycling_machine, ModSkill.binning, 9,
|
||||
{MetalBar.iridium: 5, ArtisanGood.battery_pack: 2, MetalBar.quartz: 10}, ModNames.binning_skill)
|
||||
|
||||
coppper_slot_machine = skill_recipe(ModMachine.copper_slot_machine, ModSkill.luck, 2, {MetalBar.copper: 15, Material.stone: 1, Material.wood: 1,
|
||||
Material.fiber: 1, Material.sap: 1, Loot.slime: 1,
|
||||
Forageable.salmonberry: 1, Material.clay: 1, Trash.joja_cola: 1}, ModNames.luck_skill)
|
||||
|
||||
gold_slot_machine = skill_recipe(ModMachine.gold_slot_machine, ModSkill.luck, 4, {MetalBar.gold: 15, ModMachine.copper_slot_machine: 1}, ModNames.luck_skill)
|
||||
iridium_slot_machine = skill_recipe(ModMachine.iridium_slot_machine, ModSkill.luck, 4, {MetalBar.iridium: 15, ModMachine.gold_slot_machine: 1}, ModNames.luck_skill)
|
||||
radioactive_slot_machine = skill_recipe(ModMachine.radioactive_slot_machine, ModSkill.luck, 4, {MetalBar.radioactive: 15, ModMachine.iridium_slot_machine: 1}, ModNames.luck_skill)
|
||||
|
||||
all_crafting_recipes_by_name = {recipe.item: recipe for recipe in all_crafting_recipes}
|
||||
|
||||
@@ -2935,6 +2935,10 @@ id,region,name,tags,mod_name
|
||||
7433,Farm,Craft Composter,CRAFTSANITY,Binning Skill
|
||||
7434,Farm,Craft Recycling Bin,CRAFTSANITY,Binning Skill
|
||||
7435,Farm,Craft Advanced Recycling Machine,CRAFTSANITY,Binning Skill
|
||||
7440,Farm,Craft Copper Slot Machine,"CRAFTSANITY",Luck Skill
|
||||
7441,Farm,Craft Gold Slot Machine,"CRAFTSANITY",Luck Skill
|
||||
7442,Farm,Craft Iridium Slot Machine,"CRAFTSANITY",Luck Skill
|
||||
7443,Farm,Craft Radioactive Slot Machine,"CRAFTSANITY",Luck Skill
|
||||
7451,Adventurer's Guild,Magic Elixir Recipe,"CHEFSANITY,CHEFSANITY_PURCHASE",Magic
|
||||
7452,Adventurer's Guild,Travel Core Recipe,CRAFTSANITY,Magic
|
||||
7453,Alesia Shop,Haste Elixir Recipe,CRAFTSANITY,Stardew Valley Expanded
|
||||
@@ -3241,7 +3245,7 @@ id,region,name,tags,mod_name
|
||||
8199,Shipping,Shipsanity: Hardwood Display,SHIPSANITY,Archaeology
|
||||
8200,Shipping,Shipsanity: Wooden Display,SHIPSANITY,Archaeology
|
||||
8201,Shipping,Shipsanity: Dwarf Gadget: Infinite Volcano Simulation,"SHIPSANITY,GINGER_ISLAND",Archaeology
|
||||
8202,Shipping,Shipsanity: Water Shifter,SHIPSANITY,Archaeology
|
||||
8202,Shipping,Shipsanity: Water Shifter,"SHIPSANITY,DEPRECATED",Archaeology
|
||||
8203,Shipping,Shipsanity: Brown Amanita,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Distant Lands - Witch Swamp Overhaul
|
||||
8204,Shipping,Shipsanity: Swamp Herb,"SHIPSANITY,SHIPSANITY_FULL_SHIPMENT",Distant Lands - Witch Swamp Overhaul
|
||||
8205,Shipping,Shipsanity: Void Mint Seeds,SHIPSANITY,Distant Lands - Witch Swamp Overhaul
|
||||
|
||||
|
@@ -110,6 +110,8 @@ class LocationTags(enum.Enum):
|
||||
MAGIC_LEVEL = enum.auto()
|
||||
ARCHAEOLOGY_LEVEL = enum.auto()
|
||||
|
||||
DEPRECATED = enum.auto()
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class LocationData:
|
||||
@@ -519,6 +521,10 @@ def create_locations(location_collector: StardewLocationCollector,
|
||||
location_collector(location_data.name, location_data.code, location_data.region)
|
||||
|
||||
|
||||
def filter_deprecated_locations(locations: Iterable[LocationData]) -> Iterable[LocationData]:
|
||||
return [location for location in locations if LocationTags.DEPRECATED not in location.tags]
|
||||
|
||||
|
||||
def filter_farm_type(options: StardewValleyOptions, locations: Iterable[LocationData]) -> Iterable[LocationData]:
|
||||
# On Meadowlands, "Feeding Animals" replaces "Raising Animals"
|
||||
if options.farm_type == FarmType.option_meadowlands:
|
||||
@@ -549,7 +555,8 @@ def filter_modded_locations(options: StardewValleyOptions, locations: Iterable[L
|
||||
|
||||
|
||||
def filter_disabled_locations(options: StardewValleyOptions, content: StardewContent, locations: Iterable[LocationData]) -> Iterable[LocationData]:
|
||||
locations_farm_filter = filter_farm_type(options, locations)
|
||||
locations_deprecated_filter = filter_deprecated_locations(locations)
|
||||
locations_farm_filter = filter_farm_type(options, locations_deprecated_filter)
|
||||
locations_island_filter = filter_ginger_island(options, locations_farm_filter)
|
||||
locations_qi_filter = filter_qi_order_locations(options, locations_island_filter)
|
||||
locations_masteries_filter = filter_masteries_locations(content, locations_qi_filter)
|
||||
|
||||
@@ -10,17 +10,16 @@ from .season_logic import SeasonLogicMixin
|
||||
from .tool_logic import ToolLogicMixin
|
||||
from .. import options
|
||||
from ..stardew_rule import StardewRule, True_, false_
|
||||
from ..strings.ap_names.event_names import Event
|
||||
from ..strings.fertilizer_names import Fertilizer
|
||||
from ..strings.region_names import Region
|
||||
from ..strings.region_names import Region, LogicRegion
|
||||
from ..strings.season_names import Season
|
||||
from ..strings.tool_names import Tool
|
||||
|
||||
farming_event_by_season = {
|
||||
Season.spring: Event.spring_farming,
|
||||
Season.summer: Event.summer_farming,
|
||||
Season.fall: Event.fall_farming,
|
||||
Season.winter: Event.winter_farming,
|
||||
farming_region_by_season = {
|
||||
Season.spring: LogicRegion.spring_farming,
|
||||
Season.summer: LogicRegion.summer_farming,
|
||||
Season.fall: LogicRegion.fall_farming,
|
||||
Season.winter: LogicRegion.winter_farming,
|
||||
}
|
||||
|
||||
|
||||
@@ -54,7 +53,7 @@ class FarmingLogic(BaseLogic[Union[HasLogicMixin, ReceivedLogicMixin, RegionLogi
|
||||
if isinstance(seasons, str):
|
||||
seasons = (seasons,)
|
||||
|
||||
return self.logic.or_(*(self.logic.received(farming_event_by_season[season]) for season in seasons))
|
||||
return self.logic.or_(*(self.logic.region.can_reach(farming_region_by_season[season]) for season in seasons))
|
||||
|
||||
def has_island_farm(self) -> StardewRule:
|
||||
if self.options.exclude_ginger_island == options.ExcludeGingerIsland.option_false:
|
||||
|
||||
@@ -757,6 +757,14 @@ class Gifting(Toggle):
|
||||
default = 1
|
||||
|
||||
|
||||
all_mods = {ModNames.deepwoods, ModNames.tractor, ModNames.big_backpack,
|
||||
ModNames.luck_skill, ModNames.magic, ModNames.socializing_skill, ModNames.archaeology,
|
||||
ModNames.cooking_skill, ModNames.binning_skill, ModNames.juna,
|
||||
ModNames.jasper, ModNames.alec, ModNames.yoba, ModNames.eugene,
|
||||
ModNames.wellwick, ModNames.ginger, ModNames.shiko, ModNames.delores,
|
||||
ModNames.ayeisha, ModNames.riley, ModNames.skull_cavern_elevator, ModNames.sve, ModNames.distant_lands,
|
||||
ModNames.alecto, ModNames.lacey, ModNames.boarding_house}
|
||||
|
||||
# These mods have been disabled because either they are not updated for the current supported version of Stardew Valley,
|
||||
# or we didn't find the time to validate that they work or fix compatibility issues if they do.
|
||||
# Once a mod is validated to be functional, it can simply be removed from this list
|
||||
@@ -766,8 +774,7 @@ disabled_mods = {ModNames.deepwoods, ModNames.magic,
|
||||
ModNames.wellwick, ModNames.shiko, ModNames.delores, ModNames.riley,
|
||||
ModNames.boarding_house}
|
||||
|
||||
if 'unittest' in sys.modules.keys() or 'pytest' in sys.modules.keys():
|
||||
disabled_mods = {}
|
||||
enabled_mods = all_mods.difference(disabled_mods)
|
||||
|
||||
|
||||
class Mods(OptionSet):
|
||||
@@ -775,13 +782,11 @@ class Mods(OptionSet):
|
||||
visibility = Visibility.all & ~Visibility.simple_ui
|
||||
internal_name = "mods"
|
||||
display_name = "Mods"
|
||||
valid_keys = {ModNames.deepwoods, ModNames.tractor, ModNames.big_backpack,
|
||||
ModNames.luck_skill, ModNames.magic, ModNames.socializing_skill, ModNames.archaeology,
|
||||
ModNames.cooking_skill, ModNames.binning_skill, ModNames.juna,
|
||||
ModNames.jasper, ModNames.alec, ModNames.yoba, ModNames.eugene,
|
||||
ModNames.wellwick, ModNames.ginger, ModNames.shiko, ModNames.delores,
|
||||
ModNames.ayeisha, ModNames.riley, ModNames.skull_cavern_elevator, ModNames.sve, ModNames.distant_lands,
|
||||
ModNames.alecto, ModNames.lacey, ModNames.boarding_house}.difference(disabled_mods)
|
||||
valid_keys = enabled_mods
|
||||
# In tests, we keep even the disabled mods active, because we expect some of them to eventually get updated for SV 1.6
|
||||
# In that case, we want to maintain content and logic for them, and therefore keep testing them
|
||||
if 'unittest' in sys.modules.keys() or 'pytest' in sys.modules.keys():
|
||||
valid_keys = all_mods
|
||||
|
||||
|
||||
class BundlePlando(OptionSet):
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user