Fixed some bugs + added documentation + added a few features (#87)
* Refactorings + minor logic fix * Fixed unnececerly recalculation of item_name_groups * Enabled other itemId's so that they can be send to client when desired * Marked the loss of location 1337158 * Updated network graph * First draft tinmespinner documentation * Moved personal items to slot_data rather than location scouts * Disabled Remote Items * Updated docs * Fixed port override
This commit is contained in:
parent
858d4c74ce
commit
cff5db446d
|
@ -10,6 +10,7 @@ Currently, the following games are supported:
|
||||||
* Slay the Spire
|
* Slay the Spire
|
||||||
* Risk of Rain 2
|
* Risk of Rain 2
|
||||||
* The Legend of Zelda: Ocarina of Time
|
* The Legend of Zelda: Ocarina of Time
|
||||||
|
* Timespinner
|
||||||
|
|
||||||
For setup and instructions check out our [tutorials page](http://archipelago.gg:48484/tutorial).
|
For setup and instructions check out our [tutorials page](http://archipelago.gg:48484/tutorial).
|
||||||
Downloads can be found at [Releases](https://github.com/ArchipelagoMW/Archipelago/releases), including compiled
|
Downloads can be found at [Releases](https://github.com/ArchipelagoMW/Archipelago/releases), including compiled
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Timespinner
|
||||||
|
|
||||||
|
## Where is the settings page?
|
||||||
|
The player settings page for this game is located <a href="../player-settings">here</a>. It contains all the options
|
||||||
|
you need to configure and export a config file.
|
||||||
|
|
||||||
|
## What does randomization do to this game?
|
||||||
|
Items which the player would normally acquire throughout the game have been moved around. Logic remains, so the game
|
||||||
|
is always able to be completed, but because of the item shuffle the player may need to access certain areas before
|
||||||
|
they would in the vanilla game. All rings and spells are also randomized into those item locations, therefor you can nolonger craft them at the alchemist
|
||||||
|
|
||||||
|
## What is the goal of Timespinner when randomized?
|
||||||
|
The goal remains unchanged. Kill the Sandman\Nightmare!
|
||||||
|
|
||||||
|
## What items and locations get shuffled?
|
||||||
|
All main inventory items, orbs, collectables, and familiers can be shuffled, and all locations in the game which could
|
||||||
|
contain any of those items may have their contents changed.
|
||||||
|
|
||||||
|
## Which items can be in another player's world?
|
||||||
|
Any of the items which can be shuffled may also be placed into another player's world. It is possible to choose to
|
||||||
|
limit certain items to your own world.
|
||||||
|
|
||||||
|
## What does another world's item look like in Subnautica?
|
||||||
|
Items belonging to other worlds are represented by the vanilla item [Elemental Beads](https://timespinnerwiki.com/Use_Items), Elemental Beads have no use in the randomizer
|
||||||
|
|
||||||
|
## When the player receives an item, what happens?
|
||||||
|
When the player receives an item, the same items popup will be displayed as when you would normally obtain the item
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
# Timespinner Randomizer Setup Guide
|
||||||
|
|
||||||
|
## Required Software
|
||||||
|
|
||||||
|
- [Timespinner (steam)](https://store.steampowered.com/app/368620/Timespinner/) or [Timespinner (drm free)](https://www.humblebundle.com/store/timespinner)
|
||||||
|
- [Timespinner Randomizer](https://github.com/JarnoWesthof/TsRandomizer)
|
||||||
|
|
||||||
|
## General Concept
|
||||||
|
|
||||||
|
The timespinner Randomizer loads Timespinner.exe from the same folder, and alters its state in memory to allow for randomization of the items
|
||||||
|
|
||||||
|
## Installation Procedures
|
||||||
|
|
||||||
|
Download latest version of [Timespinner Randomizer](https://github.com/JarnoWesthof/TsRandomizer) you can find the .zip files on the releases page, download the zip for your current platform. Then extract the zip to the folder where your Timespinner game is installed. Then just run TsRandomizer.exe instead of Timespinner.exe to start the game in randomized mode, for more info see the [readme](https://github.com/JarnoWesthof/TsRandomizer)
|
||||||
|
|
||||||
|
## Joining a MultiWorld Game
|
||||||
|
|
||||||
|
1. Run TsRandomizer.exe
|
||||||
|
2. Select "New Game"
|
||||||
|
3. Switch "<< Select Seed >>" to "<< Archiplago >>" by pressing left on the controller or keyboard
|
||||||
|
4. Select "<< Archiplago >>" to open a new menu where you can enter your Archipelago login credentails
|
||||||
|
* NOTE: the input fields support Ctrl + V pasting of values
|
||||||
|
5. Select "Connect"
|
||||||
|
6. If all went well you will be taken back the difficulty selection menu and the game will start as soon as you select a difficulty
|
||||||
|
|
||||||
|
## YAML Settings
|
||||||
|
An example YAML would look like this:
|
||||||
|
```yaml
|
||||||
|
description: Default Timespinner Template
|
||||||
|
name: Lunais{number} # Your name in-game. Spaces will be replaced with underscores and there is a 16 character limit
|
||||||
|
game:
|
||||||
|
Timespinner: 1
|
||||||
|
requires:
|
||||||
|
version: 0.1.8
|
||||||
|
Timespinner:
|
||||||
|
StartWithJewelryBox: # Start with Jewelry Box unlocked
|
||||||
|
false: 50
|
||||||
|
true: 0
|
||||||
|
DownloadableItems: # With the tablet you will be able to download items at terminals
|
||||||
|
false: 50
|
||||||
|
true: 50
|
||||||
|
FacebookMode: # Requires Oculus Rift(ng) to spot the weakspots in walls and floors
|
||||||
|
false: 50
|
||||||
|
true: 0
|
||||||
|
StartWithMeyef: # Start with Meyef, ideal for when you want to play multiplayer
|
||||||
|
false: 50
|
||||||
|
true: 50
|
||||||
|
QuickSeed: # Start with Talaria Attachment, Nyoom!
|
||||||
|
false: 50
|
||||||
|
true: 0
|
||||||
|
SpecificKeycards: # Keycards can only open corresponding doors
|
||||||
|
false: 0
|
||||||
|
true: 50
|
||||||
|
Inverted: # Start in the past
|
||||||
|
false: 50
|
||||||
|
true: 50
|
||||||
|
```
|
||||||
|
* All Options are either enabled or not, if values are specified for both true & false the generator will select one based on weight
|
||||||
|
* The Timespinner Randomizer option "StinkyMaw" is currently always enabled for Archipelago generated seeds
|
||||||
|
* The Timespinner Randomizer options "ProgressiveVerticalMovement" & "ProgressiveKeycards" are currently not supported on Archipelago generated seeds
|
|
@ -158,5 +158,24 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"gameTitle": "Timespinner",
|
||||||
|
"tutorials": [
|
||||||
|
{
|
||||||
|
"name": "Multiworld Setup Guide",
|
||||||
|
"description": "A guide to setting up the Timespinner randomizer connected to an Archipelago Multiworld",
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"language": "English",
|
||||||
|
"filename": "timespinner/setup_en.md",
|
||||||
|
"link": "timespinner/setup/en",
|
||||||
|
"authors": [
|
||||||
|
"Jarno"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
BIN
docs/network.png
BIN
docs/network.png
Binary file not shown.
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 109 KiB |
|
@ -81,7 +81,7 @@ class World(metaclass=AutoWorldRegister):
|
||||||
# increment this every time something in your world's names/id mappings changes.
|
# increment this every time something in your world's names/id mappings changes.
|
||||||
# While this is set to 0 in *any* AutoWorld, the entire DataPackage is considered in testing mode and will be
|
# While this is set to 0 in *any* AutoWorld, the entire DataPackage is considered in testing mode and will be
|
||||||
# retrieved by clients on every connection.
|
# retrieved by clients on every connection.
|
||||||
data_version = 1
|
data_version: int = 1
|
||||||
|
|
||||||
hint_blacklist: Set[str] = frozenset() # any names that should not be hintable
|
hint_blacklist: Set[str] = frozenset() # any names that should not be hintable
|
||||||
|
|
||||||
|
@ -100,7 +100,7 @@ class World(metaclass=AutoWorldRegister):
|
||||||
forced_auto_forfeit: bool = False
|
forced_auto_forfeit: bool = False
|
||||||
|
|
||||||
# Hide World Type from various views. Does not remove functionality.
|
# Hide World Type from various views. Does not remove functionality.
|
||||||
hidden = False
|
hidden: bool = False
|
||||||
|
|
||||||
# autoset on creation:
|
# autoset on creation:
|
||||||
world: MultiWorld
|
world: MultiWorld
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
from typing import Dict, Tuple, NamedTuple
|
from typing import Dict, Set, Tuple, NamedTuple
|
||||||
|
|
||||||
class ItemData(NamedTuple):
|
class ItemData(NamedTuple):
|
||||||
category: str
|
category: str
|
||||||
|
@ -9,50 +9,50 @@ class ItemData(NamedTuple):
|
||||||
# A lot of items arent normally dropped by the randomizer as they are mostly enemy drops, but they can be enabled if desired
|
# A lot of items arent normally dropped by the randomizer as they are mostly enemy drops, but they can be enabled if desired
|
||||||
item_table: Dict[str, ItemData] = {
|
item_table: Dict[str, ItemData] = {
|
||||||
'Eternal Crown': ItemData('Equipment', 1337000),
|
'Eternal Crown': ItemData('Equipment', 1337000),
|
||||||
#'Security Visor': ItemData('Equipment', 1337001),
|
'Security Visor': ItemData('Equipment', 1337001, 0),
|
||||||
#'Engineer Goggles': ItemData('Equipment', 1337002),
|
'Engineer Goggles': ItemData('Equipment', 1337002, 0),
|
||||||
#'Leather Helmet': ItemData('Equipment', 1337003),
|
'Leather Helmet': ItemData('Equipment', 1337003, 0),
|
||||||
#'Copper Helmet': ItemData('Equipment', 1337004),
|
'Copper Helmet': ItemData('Equipment', 1337004, 0),
|
||||||
'Pointy Hat': ItemData('Equipment', 1337005),
|
'Pointy Hat': ItemData('Equipment', 1337005),
|
||||||
#'Dragoon Helmet': ItemData('Equipment', 1337006),
|
'Dragoon Helmet': ItemData('Equipment', 1337006, 0),
|
||||||
'Buckle Hat': ItemData('Equipment', 1337007),
|
'Buckle Hat': ItemData('Equipment', 1337007),
|
||||||
#'Advisor Hat': ItemData('Equipment', 1337008),
|
'Advisor Hat': ItemData('Equipment', 1337008, 0),
|
||||||
'Librarian Hat': ItemData('Equipment', 1337009),
|
'Librarian Hat': ItemData('Equipment', 1337009),
|
||||||
#'Combat Helmet': ItemData('Equipment', 1337010),
|
'Combat Helmet': ItemData('Equipment', 1337010, 0),
|
||||||
'Captain\'s Cap': ItemData('Equipment', 1337011),
|
'Captain\'s Cap': ItemData('Equipment', 1337011),
|
||||||
'Lab Glasses': ItemData('Equipment', 1337012),
|
'Lab Glasses': ItemData('Equipment', 1337012),
|
||||||
'Empire Crown': ItemData('Equipment', 1337013),
|
'Empire Crown': ItemData('Equipment', 1337013),
|
||||||
'Viletian Crown': ItemData('Equipment', 1337014),
|
'Viletian Crown': ItemData('Equipment', 1337014),
|
||||||
#'Sunglasses': ItemData('Equipment', 1337015),
|
'Sunglasses': ItemData('Equipment', 1337015, 0),
|
||||||
'Old Coat': ItemData('Equipment', 1337016),
|
'Old Coat': ItemData('Equipment', 1337016),
|
||||||
#'Trendy Jacket': ItemData('Equipment', 1337017),
|
'Trendy Jacket': ItemData('Equipment', 1337017, 0),
|
||||||
#'Security Vest': ItemData('Equipment', 1337018),
|
'Security Vest': ItemData('Equipment', 1337018, 0),
|
||||||
#'Leather Jerkin': ItemData('Equipment', 1337019),
|
'Leather Jerkin': ItemData('Equipment', 1337019, 0),
|
||||||
#'Copper Breastplate': ItemData('Equipment', 1337020),
|
'Copper Breastplate': ItemData('Equipment', 1337020, 0),
|
||||||
'Traveler\'s Cloak': ItemData('Equipment', 1337021),
|
'Traveler\'s Cloak': ItemData('Equipment', 1337021),
|
||||||
#'Dragoon Armor': ItemData('Equipment', 1337022),
|
'Dragoon Armor': ItemData('Equipment', 1337022, 0),
|
||||||
'Midnight Cloak': ItemData('Equipment', 1337023),
|
'Midnight Cloak': ItemData('Equipment', 1337023),
|
||||||
#'Advisor Robe': ItemData('Equipment', 1337024),
|
'Advisor Robe': ItemData('Equipment', 1337024, 0),
|
||||||
'Librarian Robe': ItemData('Equipment', 1337025),
|
'Librarian Robe': ItemData('Equipment', 1337025),
|
||||||
#'Military Armor': ItemData('Equipment', 1337026),
|
'Military Armor': ItemData('Equipment', 1337026, 0),
|
||||||
'Captain\'s Uniform': ItemData('Equipment', 1337027),
|
'Captain\'s Uniform': ItemData('Equipment', 1337027),
|
||||||
'Lab Coat': ItemData('Equipment', 1337028),
|
'Lab Coat': ItemData('Equipment', 1337028),
|
||||||
'Empress Robe': ItemData('Equipment', 1337029),
|
'Empress Robe': ItemData('Equipment', 1337029),
|
||||||
'Princess Dress': ItemData('Equipment', 1337030),
|
'Princess Dress': ItemData('Equipment', 1337030),
|
||||||
'Eternal Coat': ItemData('Equipment', 1337031),
|
'Eternal Coat': ItemData('Equipment', 1337031),
|
||||||
#'Synthetic Plume': ItemData('Equipment', 1337032),
|
'Synthetic Plume': ItemData('Equipment', 1337032, 0),
|
||||||
#'Cheveur Plume': ItemData('Equipment', 1337033),
|
'Cheveur Plume': ItemData('Equipment', 1337033, 0),
|
||||||
'Metal Wristband': ItemData('Equipment', 1337034),
|
'Metal Wristband': ItemData('Equipment', 1337034),
|
||||||
#'Nymph Hairband': ItemData('Equipment', 1337035),
|
'Nymph Hairband': ItemData('Equipment', 1337035, 0),
|
||||||
#'Mother o\' Pearl': ItemData('Equipment', 1337036),
|
'Mother o\' Pearl': ItemData('Equipment', 1337036, 0),
|
||||||
'Bird Statue': ItemData('Equipment', 1337037),
|
'Bird Statue': ItemData('Equipment', 1337037),
|
||||||
#'Chaos Stole': ItemData('Equipment', 1337038),
|
'Chaos Stole': ItemData('Equipment', 1337038, 0),
|
||||||
'Pendulum': ItemData('Equipment', 1337039),
|
'Pendulum': ItemData('Equipment', 1337039),
|
||||||
#'Chaos Horn': ItemData('Equipment', 1337040),
|
'Chaos Horn': ItemData('Equipment', 1337040, 0),
|
||||||
'Filigree Clasp': ItemData('Equipment', 1337041),
|
'Filigree Clasp': ItemData('Equipment', 1337041),
|
||||||
#'Azure Stole': ItemData('Equipment', 1337042),
|
'Azure Stole': ItemData('Equipment', 1337042, 0),
|
||||||
'Ancient Coin': ItemData('Equipment', 1337043),
|
'Ancient Coin': ItemData('Equipment', 1337043),
|
||||||
#'Shiny Rock': ItemData('Equipment', 1337044),
|
'Shiny Rock': ItemData('Equipment', 1337044, 0),
|
||||||
'Galaxy Earrings': ItemData('Equipment', 1337045),
|
'Galaxy Earrings': ItemData('Equipment', 1337045),
|
||||||
'Selen\'s Bangle': ItemData('Equipment', 1337046),
|
'Selen\'s Bangle': ItemData('Equipment', 1337046),
|
||||||
'Glass Pumpkin': ItemData('Equipment', 1337047),
|
'Glass Pumpkin': ItemData('Equipment', 1337047),
|
||||||
|
@ -76,45 +76,45 @@ item_table: Dict[str, ItemData] = {
|
||||||
'Antidote': ItemData('UseItem', 1337065, 0),
|
'Antidote': ItemData('UseItem', 1337065, 0),
|
||||||
'Chaos Rose': ItemData('UseItem', 1337066, 0),
|
'Chaos Rose': ItemData('UseItem', 1337066, 0),
|
||||||
'Warp Shard': ItemData('UseItem', 1337067),
|
'Warp Shard': ItemData('UseItem', 1337067),
|
||||||
#'Dream Wisp': ItemData('UseItem', 1337068),
|
'Dream Wisp': ItemData('UseItem', 1337068, 0),
|
||||||
#'PlaceHolderItem1': ItemData('UseItem', 1337069),
|
'PlaceHolderItem1': ItemData('UseItem', 1337069, 0),
|
||||||
#'Lachiemi Sun': ItemData('UseItem', 1337070),
|
'Lachiemi Sun': ItemData('UseItem', 1337070, 0),
|
||||||
'Jerky': ItemData('UseItem', 1337071),
|
'Jerky': ItemData('UseItem', 1337071),
|
||||||
#'Biscuit': ItemData('UseItem', 1337072),
|
'Biscuit': ItemData('UseItem', 1337072, 0),
|
||||||
#'Fried Cheveur': ItemData('UseItem', 1337073),
|
'Fried Cheveur': ItemData('UseItem', 1337073, 0),
|
||||||
#'Sautéed Wyvern Tail': ItemData('UseItem', 1337074),
|
'Sautéed Wyvern Tail': ItemData('UseItem', 1337074, 0),
|
||||||
#'Unagi Roll': ItemData('UseItem', 1337075),
|
'Unagi Roll': ItemData('UseItem', 1337075, 0),
|
||||||
#'Cheveur au Vin': ItemData('UseItem', 1337076),
|
'Cheveur au Vin': ItemData('UseItem', 1337076, 0),
|
||||||
#'Royal Casserole': ItemData('UseItem', 1337077),
|
'Royal Casserole': ItemData('UseItem', 1337077, 0),
|
||||||
'Spaghetti': ItemData('UseItem', 1337078),
|
'Spaghetti': ItemData('UseItem', 1337078),
|
||||||
#'Plump Maggot': ItemData('UseItem', 1337079),
|
'Plump Maggot': ItemData('UseItem', 1337079, 0),
|
||||||
#'Orange Juice': ItemData('UseItem', 1337080),
|
'Orange Juice': ItemData('UseItem', 1337080, 0),
|
||||||
'Filigree Tea': ItemData('UseItem', 1337081),
|
'Filigree Tea': ItemData('UseItem', 1337081),
|
||||||
#'Empress Cake': ItemData('UseItem', 1337082),
|
'Empress Cake': ItemData('UseItem', 1337082, 0),
|
||||||
#'Rotten Tail': ItemData('UseItem', 1337083),
|
'Rotten Tail': ItemData('UseItem', 1337083, 0),
|
||||||
#'Alchemy Tools': ItemData('UseItem', 1337084),
|
'Alchemy Tools': ItemData('UseItem', 1337084, 0),
|
||||||
'Galaxy Stone': ItemData('UseItem', 1337085),
|
'Galaxy Stone': ItemData('UseItem', 1337085),
|
||||||
#1337086 Used interally
|
# 1337086 Used interally
|
||||||
#'Essence Crystal': ItemData('UseItem', 1337087),
|
'Essence Crystal': ItemData('UseItem', 1337087, 0),
|
||||||
#'Gold Ring': ItemData('UseItem', 1337088),
|
'Gold Ring': ItemData('UseItem', 1337088, 0),
|
||||||
#'Gold Necklace': ItemData('UseItem', 1337089),
|
'Gold Necklace': ItemData('UseItem', 1337089, 0),
|
||||||
'Herb': ItemData('UseItem', 1337090),
|
'Herb': ItemData('UseItem', 1337090),
|
||||||
#'Mushroom': ItemData('UseItem', 1337091),
|
'Mushroom': ItemData('UseItem', 1337091, 0),
|
||||||
#'Plasma Crystal': ItemData('UseItem', 1337092),
|
'Plasma Crystal': ItemData('UseItem', 1337092, 0),
|
||||||
'Plasma IV Bag': ItemData('UseItem', 1337093),
|
'Plasma IV Bag': ItemData('UseItem', 1337093),
|
||||||
#'Cheveur Drumstick': ItemData('UseItem', 1337094),
|
'Cheveur Drumstick': ItemData('UseItem', 1337094, 0),
|
||||||
#'Wyvern Tail': ItemData('UseItem', 1337095),
|
'Wyvern Tail': ItemData('UseItem', 1337095, 0),
|
||||||
#'Eel Meat': ItemData('UseItem', 1337096),
|
'Eel Meat': ItemData('UseItem', 1337096, 0),
|
||||||
#'Cheveux Breast': ItemData('UseItem', 1337097),
|
'Cheveux Breast': ItemData('UseItem', 1337097, 0),
|
||||||
'Food Synthesizer': ItemData('UseItem', 1337098),
|
'Food Synthesizer': ItemData('UseItem', 1337098),
|
||||||
#'Cheveux Feather': ItemData('UseItem', 1337099),
|
'Cheveux Feather': ItemData('UseItem', 1337099, 0),
|
||||||
#'Siren Ink': ItemData('UseItem', 1337100),
|
'Siren Ink': ItemData('UseItem', 1337100, 0),
|
||||||
#'Plasma Core': ItemData('UseItem', 1337101),
|
'Plasma Core': ItemData('UseItem', 1337101, 0),
|
||||||
#'Silver Ore': ItemData('UseItem', 1337102),
|
'Silver Ore': ItemData('UseItem', 1337102, 0),
|
||||||
#'Historical Documents': ItemData('UseItem', 1337103),
|
'Historical Documents': ItemData('UseItem', 1337103, 0),
|
||||||
#'MapReveal 0': ItemData('UseItem', 1337104),
|
'MapReveal 0': ItemData('UseItem', 1337104, 0),
|
||||||
#'MapReveal 1': ItemData('UseItem', 1337105),
|
'MapReveal 1': ItemData('UseItem', 1337105, 0),
|
||||||
#'MapReveal 2': ItemData('UseItem', 1337106),
|
'MapReveal 2': ItemData('UseItem', 1337106, 0),
|
||||||
'Timespinner Wheel': ItemData('Relic', 1337107, progression=True),
|
'Timespinner Wheel': ItemData('Relic', 1337107, progression=True),
|
||||||
'Timespinner Spindle': ItemData('Relic', 1337108, progression=True),
|
'Timespinner Spindle': ItemData('Relic', 1337108, progression=True),
|
||||||
'Timespinner Gear 1': ItemData('Relic', 1337109, progression=True),
|
'Timespinner Gear 1': ItemData('Relic', 1337109, progression=True),
|
||||||
|
@ -193,7 +193,7 @@ item_table: Dict[str, ItemData] = {
|
||||||
'Max Sand': ItemData('Stat', 1337249, 14)
|
'Max Sand': ItemData('Stat', 1337249, 14)
|
||||||
}
|
}
|
||||||
|
|
||||||
starter_melee_weapons: Tuple[str] = (
|
starter_melee_weapons: Tuple[str, ...] = (
|
||||||
'Blue Orb',
|
'Blue Orb',
|
||||||
'Blade Orb',
|
'Blade Orb',
|
||||||
'Fire Orb',
|
'Fire Orb',
|
||||||
|
@ -211,7 +211,7 @@ starter_melee_weapons: Tuple[str] = (
|
||||||
'Radiant Orb'
|
'Radiant Orb'
|
||||||
)
|
)
|
||||||
|
|
||||||
starter_spells: Tuple[str] = (
|
starter_spells: Tuple[str, ...] = (
|
||||||
'Colossal Blade',
|
'Colossal Blade',
|
||||||
'Infernal Flames',
|
'Infernal Flames',
|
||||||
'Plasma Geyser',
|
'Plasma Geyser',
|
||||||
|
@ -229,7 +229,7 @@ starter_spells: Tuple[str] = (
|
||||||
)
|
)
|
||||||
|
|
||||||
# weighted
|
# weighted
|
||||||
starter_progression_items: Tuple[str] = (
|
starter_progression_items: Tuple[str, ...] = (
|
||||||
'Talaria Attachment',
|
'Talaria Attachment',
|
||||||
'Talaria Attachment',
|
'Talaria Attachment',
|
||||||
'Succubus Hairpin',
|
'Succubus Hairpin',
|
||||||
|
@ -241,7 +241,7 @@ starter_progression_items: Tuple[str] = (
|
||||||
'Lightwall'
|
'Lightwall'
|
||||||
)
|
)
|
||||||
|
|
||||||
filler_items: Tuple[str] = (
|
filler_items: Tuple[str, ...] = (
|
||||||
'Potion',
|
'Potion',
|
||||||
'Ether',
|
'Ether',
|
||||||
'Hi-Potion',
|
'Hi-Potion',
|
||||||
|
@ -254,4 +254,12 @@ filler_items: Tuple[str] = (
|
||||||
'Mind Refresh ULTRA',
|
'Mind Refresh ULTRA',
|
||||||
'Antidote',
|
'Antidote',
|
||||||
'Chaos Rose'
|
'Chaos Rose'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def get_item_names_per_category() -> Dict[str, Set[str]]:
|
||||||
|
categories: Dict[str, Set[str]] = {}
|
||||||
|
|
||||||
|
for name, data in item_table.items():
|
||||||
|
categories.setdefault(data.category, set()).add(name)
|
||||||
|
|
||||||
|
return categories
|
|
@ -4,14 +4,12 @@ from .Options import is_option_enabled
|
||||||
|
|
||||||
EventId: Optional[int] = None
|
EventId: Optional[int] = None
|
||||||
|
|
||||||
|
|
||||||
class LocationData(NamedTuple):
|
class LocationData(NamedTuple):
|
||||||
region: str
|
region: str
|
||||||
name: str
|
name: str
|
||||||
code: Optional[int]
|
code: Optional[int]
|
||||||
rule: Callable = lambda state: True
|
rule: Callable = lambda state: True
|
||||||
|
|
||||||
|
|
||||||
def get_locations(world: Optional[MultiWorld], player: Optional[int]):
|
def get_locations(world: Optional[MultiWorld], player: Optional[int]):
|
||||||
location_table: Tuple[LocationData, ...] = (
|
location_table: Tuple[LocationData, ...] = (
|
||||||
# PresentItemLocations
|
# PresentItemLocations
|
||||||
|
@ -200,6 +198,7 @@ def get_locations(world: Optional[MultiWorld], player: Optional[int]):
|
||||||
# DownloadTerminals
|
# DownloadTerminals
|
||||||
LocationData('Libary', 'Library terminal 1', 1337157, lambda state: state.has('Tablet', player)),
|
LocationData('Libary', 'Library terminal 1', 1337157, lambda state: state.has('Tablet', player)),
|
||||||
LocationData('Libary', 'Library terminal 2', 1337156, lambda state: state.has('Tablet', player)),
|
LocationData('Libary', 'Library terminal 2', 1337156, lambda state: state.has('Tablet', player)),
|
||||||
|
# 1337158 Is Lost in time
|
||||||
LocationData('Libary', 'Library terminal 3', 1337159, lambda state: state.has('Tablet', player)),
|
LocationData('Libary', 'Library terminal 3', 1337159, lambda state: state.has('Tablet', player)),
|
||||||
LocationData('Libary', 'V terminal 1', 1337160, lambda state: state.has_all(['Tablet', 'Library Keycard V'], player)),
|
LocationData('Libary', 'V terminal 1', 1337160, lambda state: state.has_all(['Tablet', 'Library Keycard V'], player)),
|
||||||
LocationData('Libary', 'V terminal 2', 1337161, lambda state: state.has_all(['Tablet', 'Library Keycard V'], player)),
|
LocationData('Libary', 'V terminal 2', 1337161, lambda state: state.has_all(['Tablet', 'Library Keycard V'], player)),
|
||||||
|
@ -218,6 +217,7 @@ def get_locations(world: Optional[MultiWorld], player: Optional[int]):
|
||||||
return ( *location_table, *downloadable_items )
|
return ( *location_table, *downloadable_items )
|
||||||
else:
|
else:
|
||||||
return location_table
|
return location_table
|
||||||
|
|
||||||
|
|
||||||
starter_progression_locations: Tuple[str, ...] = (
|
starter_progression_locations: Tuple[str, ...] = (
|
||||||
'Starter chest 2',
|
'Starter chest 2',
|
||||||
|
|
|
@ -19,7 +19,7 @@ class DownloadableItems(Toggle):
|
||||||
display_name = "Downloadable items"
|
display_name = "Downloadable items"
|
||||||
|
|
||||||
class FacebookMode(Toggle):
|
class FacebookMode(Toggle):
|
||||||
"With the tablet you will be able to download items at terminals"
|
"Requires Oculus Rift(ng) to spot the weakspots in walls and floors"
|
||||||
display_name = "Facebook mode"
|
display_name = "Facebook mode"
|
||||||
|
|
||||||
class StartWithMeyef(Toggle):
|
class StartWithMeyef(Toggle):
|
||||||
|
|
|
@ -3,7 +3,7 @@ from BaseClasses import MultiWorld
|
||||||
from .Options import is_option_enabled
|
from .Options import is_option_enabled
|
||||||
|
|
||||||
def get_pyramid_keys_unlock(world: MultiWorld, player: int) -> str:
|
def get_pyramid_keys_unlock(world: MultiWorld, player: int) -> str:
|
||||||
present_teleportation_gates: Tuple[str] = (
|
present_teleportation_gates: Tuple[str, ...] = (
|
||||||
"GateKittyBoss",
|
"GateKittyBoss",
|
||||||
"GateLeftLibrary",
|
"GateLeftLibrary",
|
||||||
"GateMilitairyGate",
|
"GateMilitairyGate",
|
||||||
|
@ -12,7 +12,7 @@ def get_pyramid_keys_unlock(world: MultiWorld, player: int) -> str:
|
||||||
"GateLakeDesolation"
|
"GateLakeDesolation"
|
||||||
)
|
)
|
||||||
|
|
||||||
past_teleportation_gates: Tuple[str] = (
|
past_teleportation_gates: Tuple[str, ...] = (
|
||||||
"GateLakeSirineRight",
|
"GateLakeSirineRight",
|
||||||
"GateAccessToPast",
|
"GateAccessToPast",
|
||||||
"GateCastleRamparts",
|
"GateCastleRamparts",
|
||||||
|
|
|
@ -3,46 +3,46 @@ from BaseClasses import MultiWorld, Region, Entrance, Location, RegionType
|
||||||
from .Options import is_option_enabled
|
from .Options import is_option_enabled
|
||||||
from .Locations import LocationData
|
from .Locations import LocationData
|
||||||
|
|
||||||
def create_regions(world: MultiWorld, player: int, locations: Tuple[LocationData], pyramid_keys_unlock: str):
|
def create_regions(world: MultiWorld, player: int, locations: Tuple[LocationData, ...], location_cache: List[Location], pyramid_keys_unlock: str):
|
||||||
locations_per_region = get_locations_per_region(locations)
|
locations_per_region = get_locations_per_region(locations)
|
||||||
|
|
||||||
world.regions += [
|
world.regions += [
|
||||||
create_region(world, player, locations_per_region, 'Menu'),
|
create_region(world, player, locations_per_region, location_cache, 'Menu'),
|
||||||
create_region(world, player, locations_per_region, 'Tutorial'),
|
create_region(world, player, locations_per_region, location_cache, 'Tutorial'),
|
||||||
create_region(world, player, locations_per_region, 'Lake desolation'),
|
create_region(world, player, locations_per_region, location_cache, 'Lake desolation'),
|
||||||
create_region(world, player, locations_per_region, 'Upper lake desolation'),
|
create_region(world, player, locations_per_region, location_cache, 'Upper lake desolation'),
|
||||||
create_region(world, player, locations_per_region, 'Lower lake desolation'),
|
create_region(world, player, locations_per_region, location_cache, 'Lower lake desolation'),
|
||||||
create_region(world, player, locations_per_region, 'Libary'),
|
create_region(world, player, locations_per_region, location_cache, 'Libary'),
|
||||||
create_region(world, player, locations_per_region, 'Libary top'),
|
create_region(world, player, locations_per_region, location_cache, 'Libary top'),
|
||||||
create_region(world, player, locations_per_region, 'Varndagroth tower left'),
|
create_region(world, player, locations_per_region, location_cache, 'Varndagroth tower left'),
|
||||||
create_region(world, player, locations_per_region, 'Varndagroth tower right (upper)'),
|
create_region(world, player, locations_per_region, location_cache, 'Varndagroth tower right (upper)'),
|
||||||
create_region(world, player, locations_per_region, 'Varndagroth tower right (lower)'),
|
create_region(world, player, locations_per_region, location_cache, 'Varndagroth tower right (lower)'),
|
||||||
create_region(world, player, locations_per_region, 'Varndagroth tower right (elevator)'),
|
create_region(world, player, locations_per_region, location_cache, 'Varndagroth tower right (elevator)'),
|
||||||
create_region(world, player, locations_per_region, 'Sealed Caves (Sirens)'),
|
create_region(world, player, locations_per_region, location_cache, 'Sealed Caves (Sirens)'),
|
||||||
create_region(world, player, locations_per_region, 'Militairy Fortress'),
|
create_region(world, player, locations_per_region, location_cache, 'Militairy Fortress'),
|
||||||
create_region(world, player, locations_per_region, 'The lab'),
|
create_region(world, player, locations_per_region, location_cache, 'The lab'),
|
||||||
create_region(world, player, locations_per_region, 'The lab (power off)'),
|
create_region(world, player, locations_per_region, location_cache, 'The lab (power off)'),
|
||||||
create_region(world, player, locations_per_region, 'The lab (upper)'),
|
create_region(world, player, locations_per_region, location_cache, 'The lab (upper)'),
|
||||||
create_region(world, player, locations_per_region, 'Emperors tower'),
|
create_region(world, player, locations_per_region, location_cache, 'Emperors tower'),
|
||||||
create_region(world, player, locations_per_region, 'Skeleton Shaft'),
|
create_region(world, player, locations_per_region, location_cache, 'Skeleton Shaft'),
|
||||||
create_region(world, player, locations_per_region, 'Sealed Caves (upper)'),
|
create_region(world, player, locations_per_region, location_cache, 'Sealed Caves (upper)'),
|
||||||
create_region(world, player, locations_per_region, 'Sealed Caves (Xarion)'),
|
create_region(world, player, locations_per_region, location_cache, 'Sealed Caves (Xarion)'),
|
||||||
create_region(world, player, locations_per_region, 'Refugee Camp'),
|
create_region(world, player, locations_per_region, location_cache, 'Refugee Camp'),
|
||||||
create_region(world, player, locations_per_region, 'Forest'),
|
create_region(world, player, locations_per_region, location_cache, 'Forest'),
|
||||||
create_region(world, player, locations_per_region, 'Left Side forest Caves'),
|
create_region(world, player, locations_per_region, location_cache, 'Left Side forest Caves'),
|
||||||
create_region(world, player, locations_per_region, 'Upper Lake Sirine'),
|
create_region(world, player, locations_per_region, location_cache, 'Upper Lake Sirine'),
|
||||||
create_region(world, player, locations_per_region, 'Lower Lake Sirine'),
|
create_region(world, player, locations_per_region, location_cache, 'Lower Lake Sirine'),
|
||||||
create_region(world, player, locations_per_region, 'Caves of Banishment (upper)'),
|
create_region(world, player, locations_per_region, location_cache, 'Caves of Banishment (upper)'),
|
||||||
create_region(world, player, locations_per_region, 'Caves of Banishment (Maw)'),
|
create_region(world, player, locations_per_region, location_cache, 'Caves of Banishment (Maw)'),
|
||||||
create_region(world, player, locations_per_region, 'Caves of Banishment (Sirens)'),
|
create_region(world, player, locations_per_region, location_cache, 'Caves of Banishment (Sirens)'),
|
||||||
create_region(world, player, locations_per_region, 'Caste Ramparts'),
|
create_region(world, player, locations_per_region, location_cache, 'Caste Ramparts'),
|
||||||
create_region(world, player, locations_per_region, 'Caste Keep'),
|
create_region(world, player, locations_per_region, location_cache, 'Caste Keep'),
|
||||||
create_region(world, player, locations_per_region, 'Royal towers (lower)'),
|
create_region(world, player, locations_per_region, location_cache, 'Royal towers (lower)'),
|
||||||
create_region(world, player, locations_per_region, 'Royal towers'),
|
create_region(world, player, locations_per_region, location_cache, 'Royal towers'),
|
||||||
create_region(world, player, locations_per_region, 'Royal towers (upper)'),
|
create_region(world, player, locations_per_region, location_cache, 'Royal towers (upper)'),
|
||||||
create_region(world, player, locations_per_region, 'Ancient Pyramid (left)'),
|
create_region(world, player, locations_per_region, location_cache, 'Ancient Pyramid (left)'),
|
||||||
create_region(world, player, locations_per_region, 'Ancient Pyramid (right)'),
|
create_region(world, player, locations_per_region, location_cache, 'Ancient Pyramid (right)'),
|
||||||
create_region(world, player, locations_per_region, 'Space time continuum')
|
create_region(world, player, locations_per_region, location_cache, 'Space time continuum')
|
||||||
]
|
]
|
||||||
|
|
||||||
connectStartingRegion(world, player)
|
connectStartingRegion(world, player)
|
||||||
|
@ -149,7 +149,7 @@ def create_regions(world: MultiWorld, player: int, locations: Tuple[LocationData
|
||||||
connect(world, player, names, 'Space time continuum', 'Caves of Banishment (Maw)', lambda state: pyramid_keys_unlock == "GateMaw")
|
connect(world, player, names, 'Space time continuum', 'Caves of Banishment (Maw)', lambda state: pyramid_keys_unlock == "GateMaw")
|
||||||
connect(world, player, names, 'Space time continuum', 'Caves of Banishment (upper)', lambda state: pyramid_keys_unlock == "GateCavesOfBanishment")
|
connect(world, player, names, 'Space time continuum', 'Caves of Banishment (upper)', lambda state: pyramid_keys_unlock == "GateCavesOfBanishment")
|
||||||
|
|
||||||
def create_location(player: int, name: str, id: Optional[int], region: Region, rule: Callable) -> Location:
|
def create_location(player: int, name: str, id: Optional[int], region: Region, rule: Callable, location_cache: List[Location]) -> Location:
|
||||||
location = Location(player, name, id, region)
|
location = Location(player, name, id, region)
|
||||||
location.access_rule = rule
|
location.access_rule = rule
|
||||||
|
|
||||||
|
@ -157,19 +157,23 @@ def create_location(player: int, name: str, id: Optional[int], region: Region, r
|
||||||
location.event = True
|
location.event = True
|
||||||
location.locked = True
|
location.locked = True
|
||||||
|
|
||||||
|
location_cache.append(location)
|
||||||
|
|
||||||
return location
|
return location
|
||||||
|
|
||||||
def create_region(world: MultiWorld, player: int, locations_per_region: Dict[str, List[LocationData]], name: str) -> Region:
|
|
||||||
|
def create_region(world: MultiWorld, player: int, locations_per_region: Dict[str, List[LocationData]], location_cache: List[Location], name: str) -> Region:
|
||||||
region = Region(name, RegionType.Generic, name, player)
|
region = Region(name, RegionType.Generic, name, player)
|
||||||
region.world = world
|
region.world = world
|
||||||
|
|
||||||
if name in locations_per_region:
|
if name in locations_per_region:
|
||||||
for location_data in locations_per_region[name]:
|
for location_data in locations_per_region[name]:
|
||||||
location = create_location(player, location_data.name, location_data.code, region, location_data.rule)
|
location = create_location(player, location_data.name, location_data.code, region, location_data.rule, location_cache)
|
||||||
region.locations.append(location)
|
region.locations.append(location)
|
||||||
|
|
||||||
return region
|
return region
|
||||||
|
|
||||||
|
|
||||||
def connectStartingRegion(world: MultiWorld, player: int):
|
def connectStartingRegion(world: MultiWorld, player: int):
|
||||||
menu = world.get_region('Menu', player)
|
menu = world.get_region('Menu', player)
|
||||||
tutorial = world.get_region('Tutorial', player)
|
tutorial = world.get_region('Tutorial', player)
|
||||||
|
@ -192,6 +196,7 @@ def connectStartingRegion(world: MultiWorld, player: int):
|
||||||
teleport_back_to_start.connect(starting_region)
|
teleport_back_to_start.connect(starting_region)
|
||||||
space_time_continuum.exits.append(teleport_back_to_start)
|
space_time_continuum.exits.append(teleport_back_to_start)
|
||||||
|
|
||||||
|
|
||||||
def connect(world: MultiWorld, player: int, used_names : Dict[str, int], source: str, target: str, rule: Optional[Callable] = None):
|
def connect(world: MultiWorld, player: int, used_names : Dict[str, int], source: str, target: str, rule: Optional[Callable] = None):
|
||||||
sourceRegion = world.get_region(source, player)
|
sourceRegion = world.get_region(source, player)
|
||||||
targetRegion = world.get_region(target, player)
|
targetRegion = world.get_region(target, player)
|
||||||
|
@ -211,10 +216,11 @@ def connect(world: MultiWorld, player: int, used_names : Dict[str, int], source:
|
||||||
sourceRegion.exits.append(connection)
|
sourceRegion.exits.append(connection)
|
||||||
connection.connect(targetRegion)
|
connection.connect(targetRegion)
|
||||||
|
|
||||||
def get_locations_per_region(locations: Tuple[LocationData]) -> Dict[str, List[LocationData]]:
|
|
||||||
|
def get_locations_per_region(locations: Tuple[LocationData, ...]) -> Dict[str, List[LocationData]]:
|
||||||
per_region: Dict[str, List[LocationData]] = {}
|
per_region: Dict[str, List[LocationData]] = {}
|
||||||
|
|
||||||
for location in locations:
|
for location in locations:
|
||||||
per_region[location.region] = [ location ] if location.region not in per_region else per_region[location.region] + [ location ]
|
per_region.setdefault(location.region, []).append(location)
|
||||||
|
|
||||||
return per_region
|
return per_region
|
|
@ -1,52 +1,60 @@
|
||||||
from typing import Dict, List, Set
|
from typing import Dict, List, Set
|
||||||
from BaseClasses import Item, MultiWorld
|
from BaseClasses import Item, MultiWorld, Location
|
||||||
from ..AutoWorld import World
|
from ..AutoWorld import World
|
||||||
from .LogicMixin import TimespinnerLogic
|
from .LogicMixin import TimespinnerLogic
|
||||||
from .Items import item_table, starter_melee_weapons, starter_spells, starter_progression_items, filler_items
|
from .Items import get_item_names_per_category, item_table, starter_melee_weapons, starter_spells, starter_progression_items, filler_items
|
||||||
from .Locations import get_locations, starter_progression_locations, EventId
|
from .Locations import get_locations, starter_progression_locations, EventId
|
||||||
from .Regions import create_regions
|
from .Regions import create_regions
|
||||||
from .Options import is_option_enabled, timespinner_options
|
from .Options import is_option_enabled, timespinner_options
|
||||||
from .PyramidKeys import get_pyramid_keys_unlock
|
from .PyramidKeys import get_pyramid_keys_unlock
|
||||||
|
|
||||||
|
|
||||||
class TimespinnerWorld(World):
|
class TimespinnerWorld(World):
|
||||||
|
"""
|
||||||
|
Timespinner is a beautiful metroidvania inspired by classic 90s action-platformers.
|
||||||
|
Travel back in time to change fate itself. Join timekeeper Lunais on her quest for revenge against the empire that killed her family.
|
||||||
|
"""
|
||||||
|
|
||||||
options = timespinner_options
|
options = timespinner_options
|
||||||
game = "Timespinner"
|
game = "Timespinner"
|
||||||
topology_present = True
|
topology_present = True
|
||||||
data_version = 1
|
remote_items = False
|
||||||
hidden = True
|
data_version = 2
|
||||||
|
|
||||||
item_name_to_id = {name: data.code for name, data in item_table.items()}
|
item_name_to_id = {name: data.code for name, data in item_table.items()}
|
||||||
location_name_to_id = {location.name: location.code for location in get_locations(None, None)}
|
location_name_to_id = {location.name: location.code for location in get_locations(None, None)}
|
||||||
|
item_name_groups = get_item_names_per_category()
|
||||||
|
|
||||||
locked_locations: Dict[int, List[str]] = {}
|
locked_locations: Dict[int, List[str]] = {}
|
||||||
pyramid_keys_unlock: Dict[int, str] = {}
|
pyramid_keys_unlock: Dict[int, str] = {}
|
||||||
|
location_cache: Dict[int, List[Location]] = {}
|
||||||
|
|
||||||
def generate_early(self):
|
def generate_early(self):
|
||||||
self.locked_locations[self.player] = []
|
self.locked_locations[self.player] = []
|
||||||
|
self.location_cache[self.player] = []
|
||||||
self.pyramid_keys_unlock[self.player] = get_pyramid_keys_unlock(self.world, self.player)
|
self.pyramid_keys_unlock[self.player] = get_pyramid_keys_unlock(self.world, self.player)
|
||||||
|
|
||||||
self.item_name_groups = get_item_name_groups()
|
|
||||||
|
|
||||||
def create_regions(self):
|
def create_regions(self):
|
||||||
create_regions(self.world, self.player, get_locations(self.world, self.player),
|
create_regions(self.world, self.player, get_locations(self.world, self.player),
|
||||||
self.pyramid_keys_unlock[self.player])
|
self.location_cache[self.player], self.pyramid_keys_unlock[self.player])
|
||||||
|
|
||||||
|
|
||||||
def create_item(self, name: str) -> Item:
|
def create_item(self, name: str) -> Item:
|
||||||
return create_item(name, self.player)
|
return create_item(name, self.player)
|
||||||
|
|
||||||
|
|
||||||
def set_rules(self):
|
def set_rules(self):
|
||||||
setup_events(self.world, self.player, self.locked_locations[self.player])
|
setup_events(self.world, self.player, self.locked_locations[self.player])
|
||||||
|
|
||||||
self.world.completion_condition[self.player] = lambda state: state.has('Killed Nightmare', self.player)
|
self.world.completion_condition[self.player] = lambda state: state.has('Killed Nightmare', self.player)
|
||||||
|
|
||||||
|
|
||||||
def generate_basic(self):
|
def generate_basic(self):
|
||||||
excluded_items = get_excluded_items_based_on_options(self.world, self.player)
|
excluded_items = get_excluded_items_based_on_options(self.world, self.player)
|
||||||
|
|
||||||
assign_starter_items(self.world, self.player, excluded_items, self.locked_locations[self.player])
|
assign_starter_items(self.world, self.player, excluded_items, self.locked_locations[self.player])
|
||||||
|
|
||||||
if not is_option_enabled(self.world, self.player, "QuickSeed") or \
|
if not is_option_enabled(self.world, self.player, "QuickSeed") and not is_option_enabled(self.world, self.player, "Inverted"):
|
||||||
not is_option_enabled(self.world, self.player, "Inverted"):
|
|
||||||
place_first_progression_item(self.world, self.player, excluded_items, self.locked_locations[self.player])
|
place_first_progression_item(self.world, self.player, excluded_items, self.locked_locations[self.player])
|
||||||
|
|
||||||
pool = get_item_pool(self.world, self.player, excluded_items)
|
pool = get_item_pool(self.world, self.player, excluded_items)
|
||||||
|
@ -55,17 +63,18 @@ class TimespinnerWorld(World):
|
||||||
|
|
||||||
self.world.itempool += pool
|
self.world.itempool += pool
|
||||||
|
|
||||||
|
|
||||||
def fill_slot_data(self) -> Dict:
|
def fill_slot_data(self) -> Dict:
|
||||||
slot_data = {}
|
slot_data = {}
|
||||||
|
|
||||||
for option_name in timespinner_options:
|
for option_name in timespinner_options:
|
||||||
option = getattr(self.world, option_name)[self.player]
|
slot_data[option_name] = is_option_enabled(self.world, self.player, option_name)
|
||||||
slot_data[option_name] = int(option.value)
|
|
||||||
|
|
||||||
slot_data["StinkyMaw"] = 1
|
slot_data["StinkyMaw"] = True
|
||||||
slot_data["ProgressiveVerticalMovement"] = 0
|
slot_data["ProgressiveVerticalMovement"] = False
|
||||||
slot_data["ProgressiveKeycards"] = 0
|
slot_data["ProgressiveKeycards"] = False
|
||||||
slot_data["PyramidKeysGate"] = self.pyramid_keys_unlock[self.player]
|
slot_data["PyramidKeysGate"] = self.pyramid_keys_unlock[self.player]
|
||||||
|
slot_data["PersonalItems"] = get_personal_items(self.player, self.location_cache[self.player])
|
||||||
|
|
||||||
return slot_data
|
return slot_data
|
||||||
|
|
||||||
|
@ -106,7 +115,7 @@ def assign_starter_items(world: MultiWorld, player: int, excluded_items: List[st
|
||||||
|
|
||||||
|
|
||||||
def get_item_pool(world: MultiWorld, player: int, excluded_items: List[str]) -> List[Item]:
|
def get_item_pool(world: MultiWorld, player: int, excluded_items: List[str]) -> List[Item]:
|
||||||
pool = []
|
pool: List[Item] = []
|
||||||
|
|
||||||
for name, data in item_table.items():
|
for name, data in item_table.items():
|
||||||
if not name in excluded_items:
|
if not name in excluded_items:
|
||||||
|
@ -159,10 +168,11 @@ def setup_events(world: MultiWorld, player: int, locked_locations: List[str]):
|
||||||
location.place_locked_item(item)
|
location.place_locked_item(item)
|
||||||
|
|
||||||
|
|
||||||
def get_item_name_groups() -> Dict[str, Set[str]]:
|
def get_personal_items(player: int, locations: List[Location]) -> Dict[int, int]:
|
||||||
groups: Dict[str, Set[str]] = {}
|
personal_items: Dict[int, int] = {}
|
||||||
|
|
||||||
for name, data in item_table.items():
|
for location in locations:
|
||||||
groups.setdefault(data.category, set()).add(name)
|
if location.address and location.item and location.item.code and location.item.player == player:
|
||||||
|
personal_items[location.address] = location.item.code
|
||||||
return groups
|
|
||||||
|
return personal_items
|
Loading…
Reference in New Issue