Timespinner: many new stuffs (#1433)
* Timespinner: added RisingTides and DadPercent flags * Implemented logic for DadPercent and RisingTides * Fixed TODO's * Logic fixes * Fixed + removed LogicMixins * Fixes * More Fixes * Added UnchainedKeys flag * Fixed available items in pool with UnchainedKeys * Fixed typing callable * Fixed generation failures * More refactorings * Implemented traps * Fixed more typo * Fixed copy paste bug * Fixed teleporter logic * Fixed traps from pool * Fixed pyramid gates bug that causes a crash on connecting * Fixed seed reproduceability * Fixed logic eye for eye spy Now consider warp beacons as starter progression items * Attempt to add tracker icons using table * Replaced table layout with css grid * Fixed tracker + added Timespinner was apworld capatible * Updated archipelago items description * updated URL * Cleared up text * Fixed based on self review of PR * Fixed unit tests * Fixed seed reproduceability when the traps yaml option is not provided * Fixed logic for flooded basement * Implemented Beserkers review result I am not sure why, i guess this is just to make adding future games less conflicting? Co-authored-by: Fabian Dill <Berserker66@users.noreply.github.com> * Added two new options (thanks to WeffJebster) * Apply suggestions from code review Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com> * Addition review results --------- Co-authored-by: Fabian Dill <Berserker66@users.noreply.github.com> Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com>
This commit is contained in:
parent
df020bb389
commit
fc2e555b4a
|
@ -9,19 +9,54 @@
|
|||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
padding: 3px 3px 10px;
|
||||
width: 384px;
|
||||
width: 374px;
|
||||
background-color: #8d60a7;
|
||||
}
|
||||
|
||||
#inventory-table td{
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
display: grid;
|
||||
grid-template-rows: repeat(5, 48px);
|
||||
}
|
||||
|
||||
#inventory-table img{
|
||||
display: block;
|
||||
}
|
||||
|
||||
#inventory-table div.table-row{
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
}
|
||||
|
||||
#inventory-table div.C1{
|
||||
grid-column: 1;
|
||||
place-content: center;
|
||||
place-items: center;
|
||||
display: flex;
|
||||
}
|
||||
#inventory-table div.C2{
|
||||
grid-column: 2;
|
||||
place-content: center;
|
||||
place-items: center;
|
||||
display: flex;
|
||||
}
|
||||
#inventory-table div.C3{
|
||||
grid-column: 3;
|
||||
place-content: center;
|
||||
place-items: center;
|
||||
display: flex;
|
||||
}
|
||||
#inventory-table div.C4{
|
||||
grid-column: 4;
|
||||
place-content: center;
|
||||
place-items: center;
|
||||
display: flex;
|
||||
}
|
||||
#inventory-table div.C5{
|
||||
grid-column: 5;
|
||||
place-content: center;
|
||||
place-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#inventory-table img{
|
||||
height: 100%;
|
||||
max-width: 40px;
|
||||
max-height: 40px;
|
||||
filter: grayscale(100%) contrast(75%) brightness(30%);
|
||||
|
@ -31,11 +66,70 @@
|
|||
filter: none;
|
||||
}
|
||||
|
||||
#inventory-table div.counted-item {
|
||||
#inventory-table img.acquired.purple{ /*00FFFF*/
|
||||
filter: hue-rotate(270deg) saturate(6) brightness(0.8);
|
||||
}
|
||||
#inventory-table img.acquired.cyan{ /*FF00FF*/
|
||||
filter: hue-rotate(138deg) saturate(10) brightness(0.8);
|
||||
}
|
||||
#inventory-table img.acquired.green{ /*32CD32*/
|
||||
filter: hue-rotate(84deg) saturate(10) brightness(0.7);
|
||||
}
|
||||
|
||||
#inventory-table div.image-stack{
|
||||
display: grid;
|
||||
position: relative;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr;
|
||||
}
|
||||
|
||||
#inventory-table div.image-stack div.stack-back{
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
#inventory-table div.image-stack div.stack-front{
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
display: grid;
|
||||
grid-template-columns: 20px 20px;
|
||||
grid-template-rows: 20px 20px;
|
||||
}
|
||||
|
||||
#inventory-table div.image-stack div.stack-top-left{
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#inventory-table div.image-stack div.stack-top-right{
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#inventory-table div.image-stack div.stack-bottum-left{
|
||||
grid-column: 1;
|
||||
grid-row: 2;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#inventory-table div.image-stack div.stack-bottum-right{
|
||||
grid-column: 2;
|
||||
grid-row: 2;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#inventory-table div.image-stack div.stack-front img{
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
#inventory-table div.counted-item{
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#inventory-table div.item-count {
|
||||
#inventory-table div.item-count{
|
||||
position: absolute;
|
||||
color: white;
|
||||
font-family: "Minecraftia", monospace;
|
||||
|
@ -69,16 +163,16 @@
|
|||
line-height: 20px;
|
||||
}
|
||||
|
||||
#location-table td.counter {
|
||||
#location-table td.counter{
|
||||
text-align: right;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#location-table td.toggle-arrow {
|
||||
#location-table td.toggle-arrow{
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#location-table tr#Total-header {
|
||||
#location-table tr#Total-header{
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
@ -88,14 +182,14 @@
|
|||
max-height: 30px;
|
||||
}
|
||||
|
||||
#location-table tbody.locations {
|
||||
#location-table tbody.locations{
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#location-table td.location-name {
|
||||
#location-table td.location-name{
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
.hide {
|
||||
.hide{
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -8,79 +8,94 @@
|
|||
|
||||
<body>
|
||||
<div id="player-tracker-wrapper" data-tracker="{{ room.tracker|suuid }}">
|
||||
<table id="inventory-table">
|
||||
<tr>
|
||||
<td><img src="{{ icons['Timespinner Wheel'] }}" class="{{ 'acquired' if 'Timespinner Wheel' in acquired_items }}" title="Timespinner Wheel" /></td>
|
||||
<td><img src="{{ icons['Timespinner Spindle'] }}" class="{{ 'acquired' if 'Timespinner Spindle' in acquired_items }}" title="Timespinner Spindle" /></td>
|
||||
<td><img src="{{ icons['Timespinner Gear 1'] }}" class="{{ 'acquired' if 'Timespinner Gear 1' in acquired_items }}" title="Timespinner Gear 1" /></td>
|
||||
<td><img src="{{ icons['Timespinner Gear 2'] }}" class="{{ 'acquired' if 'Timespinner Gear 2' in acquired_items }}" title="Timespinner Gear 2" /></td>
|
||||
<td><img src="{{ icons['Timespinner Gear 3'] }}" class="{{ 'acquired' if 'Timespinner Gear 3' in acquired_items }}" title="Timespinner Gear 3" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="{{ icons['Talaria Attachment'] }}" class="{{ 'acquired' if 'Talaria Attachment' in acquired_items or 'QuickSeed' in options }}" title="Talaria Attachment" /></td>
|
||||
<td><img src="{{ icons['Succubus Hairpin'] }}" class="{{ 'acquired' if 'Succubus Hairpin' in acquired_items }}" title="Succubus Hairpin" /></td>
|
||||
<td><img src="{{ icons['Lightwall'] }}" class="{{ 'acquired' if 'Lightwall' in acquired_items }}" title="Lightwall" /></td>
|
||||
<td><img src="{{ icons['Celestial Sash'] }}" class="{{ 'acquired' if 'Celestial Sash' in acquired_items }}" title="Celestial Sash" /></td>
|
||||
<td><img src="{{ icons['Twin Pyramid Key'] }}" class="{{ 'acquired' if 'Twin Pyramid Key' in acquired_items }}" title="Twin Pyramid Key" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="{{ icons['Security Keycard A'] }}" class="{{ 'acquired' if 'Security Keycard A' in acquired_items }}" title="Security Keycard A" /></td>
|
||||
<td><img src="{{ icons['Security Keycard B'] }}" class="{{ 'acquired' if 'Security Keycard B' in acquired_items }}" title="Security Keycard B" /></td>
|
||||
<td><img src="{{ icons['Security Keycard C'] }}" class="{{ 'acquired' if 'Security Keycard C' in acquired_items }}" title="Security Keycard C" /></td>
|
||||
<td><img src="{{ icons['Security Keycard D'] }}" class="{{ 'acquired' if 'Security Keycard D' in acquired_items }}" title="Security Keycard D" /></td>
|
||||
{% if 'DownloadableItems' in options %}
|
||||
<td><img src="{{ icons['Library Keycard V'] }}" class="{{ 'acquired' if 'Library Keycard V' in acquired_items }}" title="Library Keycard V" /></td>
|
||||
{% else %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
<tr>
|
||||
{% if 'DownloadableItems' in options %}
|
||||
<td><img src="{{ icons['Tablet'] }}" class="{{ 'acquired' if 'Tablet' in acquired_items }}" title="Tablet" /></td>
|
||||
{% else %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
<td><img src="{{ icons['Elevator Keycard'] }}" class="{{ 'acquired' if 'Elevator Keycard' in acquired_items }}" title="Elevator Keycard" /></td>
|
||||
{% if 'EyeSpy' in options %}
|
||||
<td><img src="{{ icons['Oculus Ring'] }}" class="{{ 'acquired' if 'Oculus Ring' in acquired_items }}" title="Oculus Ring" /></td>
|
||||
{% else %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
<td><img src="{{ icons['Water Mask'] }}" class="{{ 'acquired' if 'Water Mask' in acquired_items }}" title="Water Mask" /></td>
|
||||
<td><img src="{{ icons['Gas Mask'] }}" class="{{ 'acquired' if 'Gas Mask' in acquired_items }}" title="Gas Mask" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
{% if 'GyreArchives' in options %}
|
||||
<td><img src="{{ icons['Kobo'] }}" class="{{ 'acquired' if 'Kobo' in acquired_items }}" title="Kobo" /></td>
|
||||
<td><img src="{{ icons['Merchant Crow'] }}" class="{{ 'acquired' if 'Merchant Crow' in acquired_items }}" title="Merchant Crow" /></td>
|
||||
{% else %}
|
||||
<td></td>
|
||||
<td></td>
|
||||
{% endif %}
|
||||
|
||||
<div id="inventory-table">
|
||||
<div class="table-row">
|
||||
<div class="C1"><img src="{{ icons['Timespinner Wheel'] }}" class="{{ 'acquired' if 'Timespinner Wheel' in acquired_items }}" title="Timespinner Wheel" /></div>
|
||||
<div class="C2"><img src="{{ icons['Timespinner Spindle'] }}" class="{{ 'acquired' if 'Timespinner Spindle' in acquired_items }}" title="Timespinner Spindle" /></div>
|
||||
<div class="C3"><img src="{{ icons['Timespinner Gear 1'] }}" class="{{ 'acquired' if 'Timespinner Gear 1' in acquired_items }}" title="Timespinner Gear 1" /></div>
|
||||
<div class="C4"><img src="{{ icons['Timespinner Gear 2'] }}" class="{{ 'acquired' if 'Timespinner Gear 2' in acquired_items }}" title="Timespinner Gear 2" /></div>
|
||||
<div class="C5"><img src="{{ icons['Timespinner Gear 3'] }}" class="{{ 'acquired' if 'Timespinner Gear 3' in acquired_items }}" title="Timespinner Gear 3" /></div>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div class="C1"><img src="{{ icons['Talaria Attachment'] }}" class="{{ 'acquired' if 'Talaria Attachment' in acquired_items or 'QuickSeed' in options }}" title="Talaria Attachment" /></div>
|
||||
<div class="C2"><img src="{{ icons['Succubus Hairpin'] }}" class="{{ 'acquired' if 'Succubus Hairpin' in acquired_items }}" title="Succubus Hairpin" /></div>
|
||||
<div class="C3"><img src="{{ icons['Lightwall'] }}" class="{{ 'acquired' if 'Lightwall' in acquired_items }}" title="Lightwall" /></div>
|
||||
<div class="C4"><img src="{{ icons['Celestial Sash'] }}" class="{{ 'acquired' if 'Celestial Sash' in acquired_items }}" title="Celestial Sash" /></div>
|
||||
<div class="C5">
|
||||
<div class="image-stack">
|
||||
<div class="stack-back">
|
||||
<img src="{{ icons['Twin Pyramid Key'] }}" class="{{ 'acquired' if 'Twin Pyramid Key' in acquired_items or 'UnchainedKeys' in options }}" title="Twin Pyramid Key" />
|
||||
</div>
|
||||
<div class="stack-front">
|
||||
{% if 'UnchainedKeys' in options %}
|
||||
{% if 'EnterSandman' in options %}
|
||||
<div class="stack-top-right">
|
||||
<img src="{{ icons['Twin Pyramid Key'] }}" class="green {{ 'acquired' if 'Mysterious Warp Beacon' in acquired_items }}" title="Mysterious Warp Beacon" />
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="stack-bottum-left">
|
||||
<img src="{{ icons['Twin Pyramid Key'] }}" class="cyan {{ 'acquired' if 'Timeworn Warp Beacon' in acquired_items }}" title="Timeworn Warp Beacon" />
|
||||
</div>
|
||||
<div class="stack-bottum-right">
|
||||
<img src="{{ icons['Twin Pyramid Key'] }}" class="purple {{ 'acquired' if 'Modern Warp Beacon' in acquired_items }}" title="Modern Warp Beacon" />
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div class="C1"><img src="{{ icons['Security Keycard A'] }}" class="{{ 'acquired' if 'Security Keycard A' in acquired_items }}" title="Security Keycard A" /></div>
|
||||
<div class="C2"><img src="{{ icons['Security Keycard B'] }}" class="{{ 'acquired' if 'Security Keycard B' in acquired_items }}" title="Security Keycard B" /></div>
|
||||
<div class="C3"><img src="{{ icons['Security Keycard C'] }}" class="{{ 'acquired' if 'Security Keycard C' in acquired_items }}" title="Security Keycard C" /></div>
|
||||
<div class="C4"><img src="{{ icons['Security Keycard D'] }}" class="{{ 'acquired' if 'Security Keycard D' in acquired_items }}" title="Security Keycard D" /></div>
|
||||
{% if 'DownloadableItems' in options %}
|
||||
<div class="C5"><img src="{{ icons['Library Keycard V'] }}" class="{{ 'acquired' if 'Library Keycard V' in acquired_items }}" title="Library Keycard V" /></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="table-row">
|
||||
{% if 'DownloadableItems' in options %}
|
||||
<div class="C1"><img src="{{ icons['Tablet'] }}" class="{{ 'acquired' if 'Tablet' in acquired_items }}" title="Tablet" /></div>
|
||||
{% endif %}
|
||||
<div class="C2"><img src="{{ icons['Elevator Keycard'] }}" class="{{ 'acquired' if 'Elevator Keycard' in acquired_items }}" title="Elevator Keycard" /></div>
|
||||
{% if 'EyeSpy' in options %}
|
||||
<div class="C3"><img src="{{ icons['Oculus Ring'] }}" class="{{ 'acquired' if 'Oculus Ring' in acquired_items }}" title="Oculus Ring" /></div>
|
||||
{% endif %}
|
||||
<div class="C4"><img src="{{ icons['Water Mask'] }}" class="{{ 'acquired' if 'Water Mask' in acquired_items }}" title="Water Mask" /></div>
|
||||
<div class="C5"><img src="{{ icons['Gas Mask'] }}" class="{{ 'acquired' if 'Gas Mask' in acquired_items }}" title="Gas Mask" /></div>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
{% if 'GyreArchives' in options %}
|
||||
<div class="C1"><img src="{{ icons['Kobo'] }}" class="{{ 'acquired' if 'Kobo' in acquired_items }}" title="Kobo" /></div>
|
||||
<div class="C2"><img src="{{ icons['Merchant Crow'] }}" class="{{ 'acquired' if 'Merchant Crow' in acquired_items }}" title="Merchant Crow" /></div>
|
||||
{% endif %}
|
||||
<div class="C3">
|
||||
{% if 'Djinn Inferno' in acquired_items %}
|
||||
<td><img src="{{ icons['Djinn Inferno'] }}" class="acquired" title="Djinn Inferno" /></td>
|
||||
<img src="{{ icons['Djinn Inferno'] }}" class="acquired" title="Djinn Inferno" />
|
||||
{% elif 'Pyro Ring' in acquired_items %}
|
||||
<td><img src="{{ icons['Pyro Ring'] }}" class="acquired" title="Pyro Ring" /></td>
|
||||
<img src="{{ icons['Pyro Ring'] }}" class="acquired" title="Pyro Ring" />
|
||||
{% elif 'Fire Orb' in acquired_items %}
|
||||
<td><img src="{{ icons['Fire Orb'] }}" class="acquired" title="Fire Orb" /></td>
|
||||
<img src="{{ icons['Fire Orb'] }}" class="acquired" title="Fire Orb" />
|
||||
{% elif 'Infernal Flames' in acquired_items %}
|
||||
<td><img src="{{ icons['Infernal Flames'] }}" class="acquired" title="Infernal Flames" /></td>
|
||||
<img src="{{ icons['Infernal Flames'] }}" class="acquired" title="Infernal Flames" />
|
||||
{% else %}
|
||||
<td><img src="{{ icons['Djinn Inferno'] }}" title="Djinn Inferno" /></td>
|
||||
<img src="{{ icons['Djinn Inferno'] }}" title="Djinn Inferno" />
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
<div class="C4">
|
||||
{% if 'Royal Ring' in acquired_items %}
|
||||
<td><img src="{{ icons['Royal Ring'] }}" class="acquired" title="Royal Ring" /></td>
|
||||
<img src="{{ icons['Royal Ring'] }}" class="acquired" title="Royal Ring" />
|
||||
{% elif 'Plasma Geyser' in acquired_items %}
|
||||
<td><img src="{{ icons['Plasma Geyser'] }}" class="acquired" title="Plasma Geyser" /></td>
|
||||
<img src="{{ icons['Plasma Geyser'] }}" class="acquired" title="Plasma Geyser" />
|
||||
{% elif 'Plasma Orb' in acquired_items %}
|
||||
<td><img src="{{ icons['Plasma Orb'] }}" class="acquired" title="Plasma Orb" /></td>
|
||||
<img src="{{ icons['Plasma Orb'] }}" class="acquired" title="Plasma Orb" />
|
||||
{% else %}
|
||||
<td><img src="{{ icons['Royal Ring'] }}" title="Royal Ring" /></td>
|
||||
<img src="{{ icons['Royal Ring'] }}" title="Royal Ring" />
|
||||
{% endif %}
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table id="location-table">
|
||||
{% for area in checks_done %}
|
||||
<tr class="location-category" id="{{area}}-header">
|
||||
|
|
1
setup.py
1
setup.py
|
@ -45,6 +45,7 @@ apworlds: set = {
|
|||
"Rogue Legacy",
|
||||
"Donkey Kong Country 3",
|
||||
"Super Mario World",
|
||||
"Timespinner",
|
||||
}
|
||||
|
||||
if os.path.exists("X:/pw.txt"):
|
||||
|
|
|
@ -6,6 +6,7 @@ class ItemData(NamedTuple):
|
|||
count: int = 1
|
||||
progression: bool = False
|
||||
useful: bool = False
|
||||
trap: bool = False
|
||||
|
||||
# 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] = {
|
||||
|
@ -190,7 +191,15 @@ item_table: Dict[str, ItemData] = {
|
|||
'Hope Ring': ItemData('Orb Passive', 1337178),
|
||||
'Max HP': ItemData('Stat', 1337179, 12),
|
||||
'Max Aura': ItemData('Stat', 1337180, 13),
|
||||
# 1337181 - 1337248 Reserved
|
||||
'Timeworn Warp Beacon': ItemData('Relic', 1337181, progression=True),
|
||||
'Modern Warp Beacon': ItemData('Relic', 1337182, progression=True),
|
||||
'Mysterious Warp Beacon': ItemData('Relic', 1337183, progression=True),
|
||||
'Meteor Sparrow Trap': ItemData('Trap', 1337184, 0, trap=True),
|
||||
'Poison Trap': ItemData('Trap', 1337185, 0, trap=True),
|
||||
'Chaos Trap': ItemData('Trap', 1337186, 0, trap=True),
|
||||
'Neurotoxin Trap': ItemData('Trap', 1337187, 0, trap=True),
|
||||
'Bee Trap': ItemData('Trap', 1337188, 0, trap=True),
|
||||
# 1337189 - 1337248 Reserved
|
||||
'Max Sand': ItemData('Stat', 1337249, 14)
|
||||
}
|
||||
|
||||
|
@ -230,19 +239,6 @@ starter_spells: Tuple[str, ...] = (
|
|||
'Corruption'
|
||||
)
|
||||
|
||||
# weighted
|
||||
starter_progression_items: Tuple[str, ...] = (
|
||||
'Talaria Attachment',
|
||||
'Talaria Attachment',
|
||||
'Succubus Hairpin',
|
||||
'Succubus Hairpin',
|
||||
'Timespinner Wheel',
|
||||
'Timespinner Wheel',
|
||||
'Twin Pyramid Key',
|
||||
'Celestial Sash',
|
||||
'Lightwall'
|
||||
)
|
||||
|
||||
filler_items: Tuple[str, ...] = (
|
||||
'Potion',
|
||||
'Ether',
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
from typing import List, Tuple, Optional, Callable, NamedTuple
|
||||
from BaseClasses import MultiWorld
|
||||
from BaseClasses import MultiWorld, CollectionState
|
||||
from .Options import is_option_enabled
|
||||
from .PreCalculatedWeights import PreCalculatedWeights
|
||||
from .LogicExtensions import TimespinnerLogic
|
||||
|
||||
EventId: Optional[int] = None
|
||||
|
||||
|
@ -9,10 +11,15 @@ class LocationData(NamedTuple):
|
|||
region: str
|
||||
name: str
|
||||
code: Optional[int]
|
||||
rule: Callable = lambda state: True
|
||||
rule: Callable[[CollectionState], bool] = lambda state: True
|
||||
|
||||
|
||||
def get_locations(world: Optional[MultiWorld], player: Optional[int]) -> Tuple[LocationData, ...]:
|
||||
def get_locations(world: Optional[MultiWorld], player: Optional[int],
|
||||
precalculated_weights: PreCalculatedWeights) -> Tuple[LocationData, ...]:
|
||||
|
||||
flooded: PreCalculatedWeights = precalculated_weights
|
||||
logic = TimespinnerLogic(world, player, precalculated_weights)
|
||||
|
||||
# 1337000 - 1337155 Generic locations
|
||||
# 1337171 - 1337175 New Pickup checks
|
||||
# 1337246 - 1337249 Ancient Pyramid
|
||||
|
@ -24,13 +31,13 @@ def get_locations(world: Optional[MultiWorld], player: Optional[int]) -> Tuple[L
|
|||
LocationData('Lake desolation', 'Lake Desolation: Starter chest 3', 1337003),
|
||||
LocationData('Lake desolation', 'Lake Desolation: Starter chest 1', 1337004),
|
||||
LocationData('Lake desolation', 'Lake Desolation (Lower): Timespinner Wheel room', 1337005),
|
||||
LocationData('Lake desolation', 'Lake Desolation: Forget me not chest', 1337006, lambda state: state._timespinner_has_fire(world, player) and state.can_reach('Upper Lake Serene', 'Region', player)),
|
||||
LocationData('Lake desolation', 'Lake Desolation (Lower): Chicken chest', 1337007, lambda state: state._timespinner_has_timestop(world, player)),
|
||||
LocationData('Lower lake desolation', 'Lake Desolation (Lower): Not so secret room', 1337008, lambda state: state._timespinner_can_break_walls(world, player)),
|
||||
LocationData('Lower lake desolation', 'Lake Desolation (Upper): Tank chest', 1337009, lambda state: state._timespinner_has_timestop(world, player)),
|
||||
LocationData('Lake desolation', 'Lake Desolation: Forget me not chest', 1337006, lambda state: logic.has_fire(state) and state.can_reach('Upper Lake Serene', 'Region', player)),
|
||||
LocationData('Lake desolation', 'Lake Desolation (Lower): Chicken chest', 1337007, logic.has_timestop),
|
||||
LocationData('Lower lake desolation', 'Lake Desolation (Lower): Not so secret room', 1337008, logic.can_break_walls),
|
||||
LocationData('Lower lake desolation', 'Lake Desolation (Upper): Tank chest', 1337009, logic.has_timestop),
|
||||
LocationData('Upper lake desolation', 'Lake Desolation (Upper): Oxygen recovery room', 1337010),
|
||||
LocationData('Upper lake desolation', 'Lake Desolation (Upper): Secret room', 1337011, lambda state: state._timespinner_can_break_walls(world, player)),
|
||||
LocationData('Upper lake desolation', 'Lake Desolation (Upper): Double jump cave platform', 1337012, lambda state: state._timespinner_has_doublejump(world, player)),
|
||||
LocationData('Upper lake desolation', 'Lake Desolation (Upper): Secret room', 1337011, logic.can_break_walls),
|
||||
LocationData('Upper lake desolation', 'Lake Desolation (Upper): Double jump cave platform', 1337012, logic.has_doublejump),
|
||||
LocationData('Upper lake desolation', 'Lake Desolation (Upper): Double jump cave floor', 1337013),
|
||||
LocationData('Upper lake desolation', 'Lake Desolation (Upper): Sparrow chest', 1337014),
|
||||
LocationData('Upper lake desolation', 'Lake Desolation (Upper): Crash site pedestal', 1337015),
|
||||
|
@ -41,9 +48,9 @@ def get_locations(world: Optional[MultiWorld], player: Optional[int]) -> Tuple[L
|
|||
LocationData('Library', 'Library: Warp gate', 1337020),
|
||||
LocationData('Library', 'Library: Librarian', 1337021),
|
||||
LocationData('Library', 'Library: Reading nook chest', 1337022),
|
||||
LocationData('Library', 'Library: Storage room chest 1', 1337023, lambda state: state._timespinner_has_keycard_D(world, player)),
|
||||
LocationData('Library', 'Library: Storage room chest 2', 1337024, lambda state: state._timespinner_has_keycard_D(world, player)),
|
||||
LocationData('Library', 'Library: Storage room chest 3', 1337025, lambda state: state._timespinner_has_keycard_D(world, player)),
|
||||
LocationData('Library', 'Library: Storage room chest 1', 1337023, logic.has_keycard_D),
|
||||
LocationData('Library', 'Library: Storage room chest 2', 1337024, logic.has_keycard_D),
|
||||
LocationData('Library', 'Library: Storage room chest 3', 1337025, logic.has_keycard_D),
|
||||
LocationData('Library top', 'Library: Backer room chest 5', 1337026),
|
||||
LocationData('Library top', 'Library: Backer room chest 4', 1337027),
|
||||
LocationData('Library top', 'Library: Backer room chest 3', 1337028),
|
||||
|
@ -51,59 +58,60 @@ def get_locations(world: Optional[MultiWorld], player: Optional[int]) -> Tuple[L
|
|||
LocationData('Library top', 'Library: Backer room chest 1', 1337030),
|
||||
LocationData('Varndagroth tower left', 'Varndagroth Towers (Left): Elevator Key not required', 1337031),
|
||||
LocationData('Varndagroth tower left', 'Varndagroth Towers (Left): Ye olde Timespinner', 1337032),
|
||||
LocationData('Varndagroth tower left', 'Varndagroth Towers (Left): Bottom floor', 1337033, lambda state: state._timespinner_has_keycard_C(world, player)),
|
||||
LocationData('Varndagroth tower left', 'Varndagroth Towers (Left): Air vents secret', 1337034, lambda state: state._timespinner_can_break_walls(world, player)),
|
||||
LocationData('Varndagroth tower left', 'Varndagroth Towers (Left): Bottom floor', 1337033, logic.has_keycard_C),
|
||||
LocationData('Varndagroth tower left', 'Varndagroth Towers (Left): Air vents secret', 1337034, logic.can_break_walls),
|
||||
LocationData('Varndagroth tower left', 'Varndagroth Towers (Left): Elevator chest', 1337035, lambda state: state.has('Elevator Keycard', player)),
|
||||
LocationData('Varndagroth tower right (upper)', 'Varndagroth Towers: Bridge', 1337036),
|
||||
LocationData('Varndagroth tower right (elevator)', 'Varndagroth Towers (Right): Elevator chest', 1337037),
|
||||
LocationData('Varndagroth tower right (upper)', 'Varndagroth Towers (Right): Elevator card chest', 1337038, lambda state: state.has('Elevator Keycard', player) or state._timespinner_has_doublejump(world, player)),
|
||||
LocationData('Varndagroth tower right (upper)', 'Varndagroth Towers (Right): Air vents right chest', 1337039, lambda state: state.has('Elevator Keycard', player) or state._timespinner_has_doublejump(world, player)),
|
||||
LocationData('Varndagroth tower right (upper)', 'Varndagroth Towers (Right): Air vents left chest', 1337040, lambda state: state.has('Elevator Keycard', player) or state._timespinner_has_doublejump(world, player)),
|
||||
LocationData('Varndagroth tower right (upper)', 'Varndagroth Towers (Right): Elevator card chest', 1337038, lambda state: state.has('Elevator Keycard', player) or logic.has_doublejump(state)),
|
||||
LocationData('Varndagroth tower right (upper)', 'Varndagroth Towers (Right): Air vents right chest', 1337039, lambda state: state.has('Elevator Keycard', player) or logic.has_doublejump(state)),
|
||||
LocationData('Varndagroth tower right (upper)', 'Varndagroth Towers (Right): Air vents left chest', 1337040, lambda state: state.has('Elevator Keycard', player) or logic.has_doublejump(state)),
|
||||
LocationData('Varndagroth tower right (lower)', 'Varndagroth Towers (Right): Bottom floor', 1337041),
|
||||
LocationData('Varndagroth tower right (elevator)', 'Varndagroth Towers (Right): Varndagroth', 1337042, lambda state: state._timespinner_has_keycard_C(world, player)),
|
||||
LocationData('Varndagroth tower right (elevator)', 'Varndagroth Towers (Right): Spider Hell', 1337043, lambda state: state._timespinner_has_keycard_A(world, player)),
|
||||
LocationData('Varndagroth tower right (elevator)', 'Varndagroth Towers (Right): Varndagroth', 1337042, logic.has_keycard_C),
|
||||
LocationData('Varndagroth tower right (elevator)', 'Varndagroth Towers (Right): Spider Hell', 1337043, logic.has_keycard_A),
|
||||
LocationData('Skeleton Shaft', 'Sealed Caves (Xarion): Skeleton', 1337044),
|
||||
LocationData('Sealed Caves (Xarion)', 'Sealed Caves (Xarion): Shroom jump room', 1337045, lambda state: state._timespinner_has_timestop(world, player)),
|
||||
LocationData('Sealed Caves (Xarion)', 'Sealed Caves (Xarion): Shroom jump room', 1337045, logic.has_timestop),
|
||||
LocationData('Sealed Caves (Xarion)', 'Sealed Caves (Xarion): Double shroom room', 1337046),
|
||||
LocationData('Sealed Caves (Xarion)', 'Sealed Caves (Xarion): Mini jackpot room', 1337047, lambda state: state._timespinner_has_forwarddash_doublejump(world, player)),
|
||||
LocationData('Sealed Caves (Xarion)', 'Sealed Caves (Xarion): Mini jackpot room', 1337047, logic.has_forwarddash_doublejump),
|
||||
LocationData('Sealed Caves (Xarion)', 'Sealed Caves (Xarion): Below mini jackpot room', 1337048),
|
||||
LocationData('Sealed Caves (Xarion)', 'Sealed Caves (Xarion): Secret room', 1337049, lambda state: state._timespinner_can_break_walls(world, player)),
|
||||
LocationData('Sealed Caves (Xarion)', 'Sealed Caves (Xarion): Secret room', 1337049, logic.can_break_walls),
|
||||
LocationData('Sealed Caves (Xarion)', 'Sealed Caves (Xarion): Bottom left room', 1337050),
|
||||
LocationData('Sealed Caves (Xarion)', 'Sealed Caves (Xarion): Last chance before Xarion', 1337051, lambda state: state._timespinner_has_doublejump(world, player)),
|
||||
LocationData('Sealed Caves (Xarion)', 'Sealed Caves (Xarion): Xarion', 1337052),
|
||||
LocationData('Sealed Caves (Xarion)', 'Sealed Caves (Xarion): Last chance before Xarion', 1337051, logic.has_doublejump),
|
||||
LocationData('Sealed Caves (Xarion)', 'Sealed Caves (Xarion): Xarion', 1337052, lambda state: state.has('Water Mask', player) if flooded.flood_xarion else True),
|
||||
LocationData('Sealed Caves (Sirens)', 'Sealed Caves (Sirens): Water hook', 1337053, lambda state: state.has('Water Mask', player)),
|
||||
LocationData('Sealed Caves (Sirens)', 'Sealed Caves (Sirens): Siren room underwater right', 1337054, lambda state: state.has('Water Mask', player)),
|
||||
LocationData('Sealed Caves (Sirens)', 'Sealed Caves (Sirens): Siren room underwater left', 1337055, lambda state: state.has('Water Mask', player)),
|
||||
LocationData('Sealed Caves (Sirens)', 'Sealed Caves (Sirens): Cave after sirens chest 1', 1337056),
|
||||
LocationData('Sealed Caves (Sirens)', 'Sealed Caves (Sirens): Cave after sirens chest 2', 1337057),
|
||||
LocationData('Military Fortress', 'Military Fortress: Bomber chest', 1337058, lambda state: state.has('Timespinner Wheel', player) and state._timespinner_has_doublejump_of_npc(world, player)),
|
||||
LocationData('Military Fortress', 'Military Fortress: Bomber chest', 1337058, lambda state: state.has('Timespinner Wheel', player) and logic.has_doublejump_of_npc(state)),
|
||||
LocationData('Military Fortress', 'Military Fortress: Close combat room', 1337059),
|
||||
LocationData('Military Fortress (hangar)', 'Military Fortress: Soldiers bridge', 1337060),
|
||||
LocationData('Military Fortress (hangar)', 'Military Fortress: Giantess room', 1337061),
|
||||
LocationData('Military Fortress (hangar)', 'Military Fortress: Giantess bridge', 1337062),
|
||||
LocationData('Military Fortress (hangar)', 'Military Fortress: B door chest 2', 1337063, lambda state: state._timespinner_has_doublejump(world, player) and state._timespinner_has_keycard_B(world, player)),
|
||||
LocationData('Military Fortress (hangar)', 'Military Fortress: B door chest 1', 1337064, lambda state: state._timespinner_has_doublejump(world, player) and state._timespinner_has_keycard_B(world, player)),
|
||||
LocationData('Military Fortress (hangar)', 'Military Fortress: Pedestal', 1337065, lambda state: state._timespinner_has_doublejump_of_npc(world, player) or state._timespinner_has_forwarddash_doublejump(world, player)),
|
||||
LocationData('Military Fortress (hangar)', 'Military Fortress: B door chest 2', 1337063, lambda state: logic.has_doublejump(state) and logic.has_keycard_B(state)),
|
||||
LocationData('Military Fortress (hangar)', 'Military Fortress: B door chest 1', 1337064, lambda state: logic.has_doublejump(state) and logic.has_keycard_B(state)),
|
||||
LocationData('Military Fortress (hangar)', 'Military Fortress: Pedestal', 1337065, lambda state: logic.has_doublejump_of_npc(state) or logic.has_forwarddash_doublejump(state)),
|
||||
LocationData('The lab', 'Lab: Coffee break', 1337066),
|
||||
LocationData('The lab', 'Lab: Lower trash right', 1337067, lambda state: state._timespinner_has_doublejump(world, player)),
|
||||
LocationData('The lab', 'Lab: Lower trash left', 1337068, lambda state: state._timespinner_has_upwarddash(world, player)),
|
||||
LocationData('The lab', 'Lab: Below lab entrance', 1337069, lambda state: state._timespinner_has_doublejump(world, player)),
|
||||
LocationData('The lab', 'Lab: Lower trash right', 1337067, logic.has_doublejump),
|
||||
LocationData('The lab', 'Lab: Lower trash left', 1337068, logic.has_upwarddash),
|
||||
LocationData('The lab', 'Lab: Below lab entrance', 1337069, logic.has_doublejump),
|
||||
LocationData('The lab (power off)', 'Lab: Trash jump room', 1337070),
|
||||
LocationData('The lab (power off)', 'Lab: Dynamo Works', 1337071),
|
||||
LocationData('The lab (upper)', 'Lab: Genza (Blob Mom)', 1337072),
|
||||
LocationData('The lab (power off)', 'Lab: Experiment #13', 1337073),
|
||||
LocationData('The lab (upper)', 'Lab: Download and chest room chest', 1337074),
|
||||
LocationData('The lab (upper)', 'Lab: Lab secret', 1337075, lambda state: state._timespinner_can_break_walls(world, player)),
|
||||
LocationData('The lab (power off)', 'Lab: Spider Hell', 1337076, lambda state: state._timespinner_has_keycard_A(world, player)),
|
||||
LocationData('The lab (upper)', 'Lab: Lab secret', 1337075, logic.can_break_walls),
|
||||
LocationData('The lab (power off)', 'Lab: Spider Hell', 1337076, logic.has_keycard_A),
|
||||
LocationData('Emperors tower', 'Emperor\'s Tower: Courtyard bottom chest', 1337077),
|
||||
LocationData('Emperors tower', 'Emperor\'s Tower: Courtyard floor secret', 1337078, lambda state: state._timespinner_has_upwarddash(world, player) and state._timespinner_can_break_walls(world, player)),
|
||||
LocationData('Emperors tower', 'Emperor\'s Tower: Courtyard upper chest', 1337079, lambda state: state._timespinner_has_upwarddash(world, player)),
|
||||
LocationData('Emperors tower', 'Emperor\'s Tower: Courtyard floor secret', 1337078, lambda state: logic.has_upwarddash(state) and logic.can_break_walls(state)),
|
||||
LocationData('Emperors tower', 'Emperor\'s Tower: Courtyard upper chest', 1337079, lambda state: logic.has_upwarddash(state)),
|
||||
LocationData('Emperors tower', 'Emperor\'s Tower: Galactic sage room', 1337080),
|
||||
LocationData('Emperors tower', 'Emperor\'s Tower: Bottom right tower', 1337081),
|
||||
LocationData('Emperors tower', 'Emperor\'s Tower: Wayyyy up there', 1337082, lambda state: state._timespinner_has_doublejump_of_npc(world, player)),
|
||||
LocationData('Emperors tower', 'Emperor\'s Tower: Wayyyy up there', 1337082, logic.has_doublejump_of_npc),
|
||||
LocationData('Emperors tower', 'Emperor\'s Tower: Left tower balcony', 1337083),
|
||||
LocationData('Emperors tower', 'Emperor\'s Tower: Emperor\'s Chambers chest', 1337084),
|
||||
LocationData('Emperors tower', 'Emperor\'s Tower: Emperor\'s Chambers pedestal', 1337085),
|
||||
LocationData('Emperors tower', 'Killed Emperor', EventId),
|
||||
|
||||
# Past item locations
|
||||
LocationData('Refugee Camp', 'Refugee Camp: Neliste\'s Bra', 1337086),
|
||||
|
@ -111,18 +119,18 @@ def get_locations(world: Optional[MultiWorld], player: Optional[int]) -> Tuple[L
|
|||
LocationData('Refugee Camp', 'Refugee Camp: Storage chest 2', 1337088),
|
||||
LocationData('Refugee Camp', 'Refugee Camp: Storage chest 1', 1337089),
|
||||
LocationData('Forest', 'Forest: Refugee camp roof', 1337090),
|
||||
LocationData('Forest', 'Forest: Bat jump ledge', 1337091, lambda state: state._timespinner_has_doublejump_of_npc(world, player) or state._timespinner_has_forwarddash_doublejump(world, player) or state._timespinner_has_fastjump_on_npc(world, player)),
|
||||
LocationData('Forest', 'Forest: Green platform secret', 1337092, lambda state: state._timespinner_can_break_walls(world, player)),
|
||||
LocationData('Forest', 'Forest: Bat jump ledge', 1337091, lambda state: logic.has_doublejump_of_npc(state) or logic.has_forwarddash_doublejump(state) or logic.has_fastjump_on_npc(state)),
|
||||
LocationData('Forest', 'Forest: Green platform secret', 1337092, logic.can_break_walls),
|
||||
LocationData('Forest', 'Forest: Rats guarded chest', 1337093),
|
||||
LocationData('Forest', 'Forest: Waterfall chest 1', 1337094, lambda state: state.has('Water Mask', player)),
|
||||
LocationData('Forest', 'Forest: Waterfall chest 2', 1337095, lambda state: state.has('Water Mask', player)),
|
||||
LocationData('Forest', 'Forest: Batcave', 1337096),
|
||||
LocationData('Forest', 'Castle Ramparts: In the moat', 1337097),
|
||||
LocationData('Forest', 'Castle Ramparts: In the moat', 1337097, lambda state: state.has('Water Mask', player) if flooded.flood_moat else True),
|
||||
LocationData('Left Side forest Caves', 'Forest: Before Serene single bat cave', 1337098),
|
||||
LocationData('Upper Lake Serene', 'Lake Serene (Upper): Rat nest', 1337099),
|
||||
LocationData('Upper Lake Serene', 'Lake Serene (Upper): Double jump cave platform', 1337100, lambda state: state._timespinner_has_doublejump(world, player)),
|
||||
LocationData('Upper Lake Serene', 'Lake Serene (Upper): Double jump cave platform', 1337100, logic.has_doublejump),
|
||||
LocationData('Upper Lake Serene', 'Lake Serene (Upper): Double jump cave floor', 1337101),
|
||||
LocationData('Upper Lake Serene', 'Lake Serene (Upper): Cave secret', 1337102, lambda state: state._timespinner_can_break_walls(world, player)),
|
||||
LocationData('Upper Lake Serene', 'Lake Serene (Upper): Cave secret', 1337102, logic.can_break_walls),
|
||||
LocationData('Upper Lake Serene', 'Lake Serene: Before Big Bird', 1337175),
|
||||
LocationData('Upper Lake Serene', 'Lake Serene: Behind the vines', 1337103),
|
||||
LocationData('Upper Lake Serene', 'Lake Serene: Pyramid keys room', 1337104),
|
||||
|
@ -130,68 +138,68 @@ def get_locations(world: Optional[MultiWorld], player: Optional[int]) -> Tuple[L
|
|||
LocationData('Lower Lake Serene', 'Lake Serene (Lower): Deep dive', 1337105),
|
||||
LocationData('Lower Lake Serene', 'Lake Serene (Lower): Under the eels', 1337106),
|
||||
LocationData('Lower Lake Serene', 'Lake Serene (Lower): Water spikes room', 1337107),
|
||||
LocationData('Lower Lake Serene', 'Lake Serene (Lower): Underwater secret', 1337108, lambda state: state._timespinner_can_break_walls(world, player)),
|
||||
LocationData('Lower Lake Serene', 'Lake Serene (Lower): T chest', 1337109),
|
||||
LocationData('Lower Lake Serene', 'Lake Serene (Lower): Underwater secret', 1337108, logic.can_break_walls),
|
||||
LocationData('Lower Lake Serene', 'Lake Serene (Lower): T chest', 1337109, lambda state: logic.has_doublejump_of_npc(state) if flooded.dry_lake_serene else True),
|
||||
LocationData('Lower Lake Serene', 'Lake Serene (Lower): Past the eels', 1337110),
|
||||
LocationData('Lower Lake Serene', 'Lake Serene (Lower): Underwater pedestal', 1337111),
|
||||
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Shroom jump room', 1337112, lambda state: state._timespinner_has_doublejump(world, player)),
|
||||
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Secret room', 1337113),
|
||||
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Bottom left room', 1337114),
|
||||
LocationData('Lower Lake Serene', 'Lake Serene (Lower): Underwater pedestal', 1337111, lambda state: logic.has_doublejump(state) if flooded.dry_lake_serene else True),
|
||||
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Shroom jump room', 1337112, lambda state: logic.has_doublejump(state) if not flooded.flood_maw else True),
|
||||
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Secret room', 1337113, lambda state: logic.can_break_walls(state) and (state.has('Water Mask', player) if flooded.flood_maw else True)),
|
||||
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Bottom left room', 1337114, lambda state: state.has('Water Mask', player) if flooded.flood_maw else True),
|
||||
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Single shroom room', 1337115),
|
||||
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Jackpot room chest 1', 1337116, lambda state: state._timespinner_has_forwarddash_doublejump(world, player)),
|
||||
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Jackpot room chest 2', 1337117, lambda state: state._timespinner_has_forwarddash_doublejump(world, player)),
|
||||
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Jackpot room chest 3', 1337118, lambda state: state._timespinner_has_forwarddash_doublejump(world, player)),
|
||||
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Jackpot room chest 4', 1337119, lambda state: state._timespinner_has_forwarddash_doublejump(world, player)),
|
||||
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Pedestal', 1337120),
|
||||
LocationData('Caves of Banishment (Maw)', 'Caves of Banishment (Maw): Last chance before Maw', 1337121, lambda state: state._timespinner_has_doublejump(world, player)),
|
||||
LocationData('Caves of Banishment (Maw)', 'Caves of Banishment (Maw): Plasma Crystal', 1337173, lambda state: state.has_any({'Gas Mask', 'Talaria Attachment'}, player)),
|
||||
LocationData('Caves of Banishment (Maw)', 'Killed Maw', EventId, lambda state: state.has('Gas Mask', player)),
|
||||
LocationData('Caves of Banishment (Maw)', 'Caves of Banishment (Maw): Mineshaft', 1337122, lambda state: state.has('Gas Mask', player)),
|
||||
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Jackpot room chest 1', 1337116, lambda state: logic.has_forwarddash_doublejump(state) or flooded.flood_maw),
|
||||
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Jackpot room chest 2', 1337117, lambda state: logic.has_forwarddash_doublejump(state) or flooded.flood_maw),
|
||||
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Jackpot room chest 3', 1337118, lambda state: logic.has_forwarddash_doublejump(state) or flooded.flood_maw),
|
||||
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Jackpot room chest 4', 1337119, lambda state: logic.has_forwarddash_doublejump(state) or flooded.flood_maw),
|
||||
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Pedestal', 1337120, lambda state: state.has('Water Mask', player) if flooded.flood_maw else True),
|
||||
LocationData('Caves of Banishment (Maw)', 'Caves of Banishment (Maw): Last chance before Maw', 1337121, lambda state: state.has('Water Mask', player) if flooded.flood_maw else logic.has_doublejump(state)),
|
||||
LocationData('Caves of Banishment (Maw)', 'Caves of Banishment (Maw): Plasma Crystal', 1337173, lambda state: state.has_any({'Gas Mask', 'Talaria Attachment'}, player) and (state.has('Water Mask', player) if flooded.flood_maw else True)),
|
||||
LocationData('Caves of Banishment (Maw)', 'Killed Maw', EventId, lambda state: state.has('Gas Mask', player) and (state.has('Water Mask', player) if flooded.flood_maw else True)),
|
||||
LocationData('Caves of Banishment (Maw)', 'Caves of Banishment (Maw): Mineshaft', 1337122, lambda state: state.has_any({'Gas Mask', 'Talaria Attachment'}, player) and (state.has('Water Mask', player) if flooded.flood_maw else True)),
|
||||
LocationData('Caves of Banishment (Sirens)', 'Caves of Banishment (Sirens): Wyvern room', 1337123),
|
||||
LocationData('Caves of Banishment (Sirens)', 'Caves of Banishment (Sirens): Siren room above water chest', 1337124),
|
||||
LocationData('Caves of Banishment (Sirens)', 'Caves of Banishment (Sirens): Siren room underwater left chest', 1337125, lambda state: state.has('Water Mask', player)),
|
||||
LocationData('Caves of Banishment (Sirens)', 'Caves of Banishment (Sirens): Siren room underwater right chest', 1337126, lambda state: state.has('Water Mask', player)),
|
||||
LocationData('Caves of Banishment (Sirens)', 'Caves of Banishment (Sirens): Siren room underwater right ground', 1337172, lambda state: state.has('Water Mask', player)),
|
||||
LocationData('Caves of Banishment (Sirens)', 'Caves of Banishment (Sirens): Water hook', 1337127, lambda state: state.has('Water Mask', player)),
|
||||
LocationData('Castle Ramparts', 'Castle Ramparts: Bomber chest', 1337128, lambda state: state._timespinner_has_multiple_small_jumps_of_npc(world, player)),
|
||||
LocationData('Castle Ramparts', 'Castle Ramparts: Freeze the engineer', 1337129, lambda state: state.has('Talaria Attachment', player) or state._timespinner_has_timestop(world, player)),
|
||||
LocationData('Castle Ramparts', 'Castle Ramparts: Bomber chest', 1337128, logic.has_multiple_small_jumps_of_npc),
|
||||
LocationData('Castle Ramparts', 'Castle Ramparts: Freeze the engineer', 1337129, lambda state: state.has('Talaria Attachment', player) or logic.has_timestop(state)),
|
||||
LocationData('Castle Ramparts', 'Castle Ramparts: Giantess guarded room', 1337130),
|
||||
LocationData('Castle Ramparts', 'Castle Ramparts: Knight and archer guarded room', 1337131),
|
||||
LocationData('Castle Ramparts', 'Castle Ramparts: Pedestal', 1337132),
|
||||
LocationData('Castle Keep', 'Castle Keep: Basement secret pedestal', 1337133, lambda state: state._timespinner_can_break_walls(world, player)),
|
||||
LocationData('Castle Keep', 'Castle Keep: Clean the castle basement', 1337134),
|
||||
LocationData('Royal towers (lower)', 'Castle Keep: Yas queen room', 1337135, lambda state: state._timespinner_has_pink(world, player)),
|
||||
LocationData('Castle Keep', 'Castle Keep: Giantess guarded chest', 1337136),
|
||||
LocationData('Castle Keep', 'Castle Keep: Omelette chest', 1337137),
|
||||
LocationData('Castle Keep', 'Castle Keep: Just an egg', 1337138),
|
||||
LocationData('Castle Basement', 'Castle Basement: Secret pedestal', 1337133, logic.can_break_walls),
|
||||
LocationData('Castle Basement', 'Castle Basement: Clean the castle basement', 1337134),
|
||||
LocationData('Royal towers (lower)', 'Castle Keep: Yas queen room', 1337135, logic.has_pink),
|
||||
LocationData('Castle Basement', 'Castle Basement: Giantess guarded chest', 1337136),
|
||||
LocationData('Castle Basement', 'Castle Basement: Omelette chest', 1337137),
|
||||
LocationData('Castle Basement', 'Castle Basement: Just an egg', 1337138),
|
||||
LocationData('Castle Keep', 'Castle Keep: Under the twins', 1337139),
|
||||
LocationData('Castle Keep', 'Killed Twins', EventId, lambda state: state._timespinner_has_timestop(world, player)),
|
||||
LocationData('Castle Keep', 'Castle Keep: Advisor jump', 1337171, lambda state: state._timespinner_has_timestop(world, player)),
|
||||
LocationData('Castle Keep', 'Castle Keep: Twins', 1337140, lambda state: state._timespinner_has_timestop(world, player)),
|
||||
LocationData('Castle Keep', 'Castle Keep: Royal guard tiny room', 1337141, lambda state: state._timespinner_has_doublejump(world, player) or state._timespinner_has_fastjump_on_npc(world,player)),
|
||||
LocationData('Royal towers (lower)', 'Royal Towers: Floor secret', 1337142, lambda state: state._timespinner_has_doublejump(world, player) and state._timespinner_can_break_walls(world, player)),
|
||||
LocationData('Castle Keep', 'Killed Twins', EventId, logic.has_timestop),
|
||||
LocationData('Castle Keep', 'Castle Keep: Advisor jump', 1337171, logic.has_timestop),
|
||||
LocationData('Castle Keep', 'Castle Keep: Twins', 1337140, logic.has_timestop),
|
||||
LocationData('Castle Keep', 'Castle Keep: Royal guard tiny room', 1337141, lambda state: logic.has_doublejump(state) or logic.has_fastjump_on_npc(state)),
|
||||
LocationData('Royal towers (lower)', 'Royal Towers: Floor secret', 1337142, lambda state: logic.has_doublejump(state) and logic.can_break_walls(state)),
|
||||
LocationData('Royal towers', 'Royal Towers: Pre-climb gap', 1337143),
|
||||
LocationData('Royal towers', 'Royal Towers: Long balcony', 1337144),
|
||||
LocationData('Royal towers (upper)', 'Royal Towers: Past bottom struggle juggle', 1337145),
|
||||
LocationData('Royal towers (upper)', 'Royal Towers: Bottom struggle juggle', 1337146, lambda state: state._timespinner_has_doublejump_of_npc(world, player)),
|
||||
LocationData('Royal towers (upper)', 'Royal Towers: Top struggle juggle', 1337147, lambda state: state._timespinner_has_doublejump_of_npc(world, player)),
|
||||
LocationData('Royal towers (upper)', 'Royal Towers: No struggle required', 1337148, lambda state: state._timespinner_has_doublejump_of_npc(world, player)),
|
||||
LocationData('Royal towers', 'Royal Towers: Long balcony', 1337144, lambda state: state.has('Water Mask', player) if flooded.flood_courtyard else True),
|
||||
LocationData('Royal towers', 'Royal Towers: Past bottom struggle juggle', 1337145, lambda state: logic.has_doublejump_of_npc(state) if not flooded.flood_courtyard else True),
|
||||
LocationData('Royal towers', 'Royal Towers: Bottom struggle juggle', 1337146, logic.has_doublejump_of_npc),
|
||||
LocationData('Royal towers (upper)', 'Royal Towers: Top struggle juggle', 1337147, logic.has_doublejump_of_npc),
|
||||
LocationData('Royal towers (upper)', 'Royal Towers: No struggle required', 1337148, logic.has_doublejump_of_npc),
|
||||
LocationData('Royal towers', 'Royal Towers: Right tower freebie', 1337149),
|
||||
LocationData('Royal towers (upper)', 'Royal Towers: Left tower small balcony', 1337150),
|
||||
LocationData('Royal towers (upper)', 'Royal Towers: Left tower royal guard', 1337151),
|
||||
LocationData('Royal towers (upper)', 'Royal Towers: Before Aelana', 1337152),
|
||||
LocationData('Royal towers (upper)', 'Killed Aelana', EventId),
|
||||
LocationData('Royal towers (upper)', 'Royal Towers: Aelana\'s attic', 1337153, lambda state: state._timespinner_has_upwarddash(world, player)),
|
||||
LocationData('Royal towers (upper)', 'Royal Towers: Aelana\'s attic', 1337153, logic.has_upwarddash),
|
||||
LocationData('Royal towers (upper)', 'Royal Towers: Aelana\'s chest', 1337154),
|
||||
LocationData('Royal towers (upper)', 'Royal Towers: Aelana\'s pedestal', 1337155),
|
||||
|
||||
# Ancient pyramid locations
|
||||
LocationData('Ancient Pyramid (entrance)', 'Ancient Pyramid: Why not it\'s right there', 1337246),
|
||||
LocationData('Ancient Pyramid (left)', 'Ancient Pyramid: Conviction guarded room', 1337247),
|
||||
LocationData('Ancient Pyramid (left)', 'Ancient Pyramid: Pit secret room', 1337248, lambda state: state._timespinner_can_break_walls(world, player)),
|
||||
LocationData('Ancient Pyramid (left)', 'Ancient Pyramid: Regret chest', 1337249, lambda state: state._timespinner_can_break_walls(world, player)),
|
||||
LocationData('Ancient Pyramid (right)', 'Ancient Pyramid: Nightmare Door chest', 1337236),
|
||||
LocationData('Ancient Pyramid (right)', 'Killed Nightmare', EventId, lambda state: state.has_all({'Timespinner Wheel', 'Timespinner Spindle', 'Timespinner Gear 1', 'Timespinner Gear 2', 'Timespinner Gear 3'}, player))
|
||||
LocationData('Ancient Pyramid (left)', 'Ancient Pyramid: Pit secret room', 1337248, lambda state: logic.can_break_walls(state) and (state.has('Water Mask', player) if flooded.flood_pyramid_shaft else True)),
|
||||
LocationData('Ancient Pyramid (left)', 'Ancient Pyramid: Regret chest', 1337249, lambda state: logic.can_break_walls(state) and (state.has('Water Mask', player) if flooded.flood_pyramid_shaft else True)),
|
||||
LocationData('Ancient Pyramid (right)', 'Ancient Pyramid: Nightmare Door chest', 1337236, lambda state: state.has('Water Mask', player) if flooded.flood_pyramid_back else True),
|
||||
LocationData('Ancient Pyramid (right)', 'Killed Nightmare', EventId, lambda state: state.has_all({'Timespinner Wheel', 'Timespinner Spindle', 'Timespinner Gear 1', 'Timespinner Gear 2', 'Timespinner Gear 3'}, player) and (state.has('Water Mask', player) if flooded.flood_pyramid_back else True))
|
||||
]
|
||||
|
||||
# 1337156 - 1337170 Downloads
|
||||
|
@ -205,7 +213,7 @@ def get_locations(world: Optional[MultiWorld], player: Optional[int]) -> Tuple[L
|
|||
LocationData('Library', 'Library: V terminal 2 (Lake Desolation Map)', 1337161, lambda state: state.has_all({'Tablet', 'Library Keycard V'}, player)),
|
||||
LocationData('Library', 'Library: V terminal 3 (Vilete)', 1337162, lambda state: state.has_all({'Tablet', 'Library Keycard V'}, player)),
|
||||
LocationData('Library top', 'Library: Backer room terminal (Vandagray Metropolis Map)', 1337163, lambda state: state.has('Tablet', player)),
|
||||
LocationData('Varndagroth tower right (elevator)', 'Varndagroth Towers (Right): Medbay terminal (Bleakness Research)', 1337164, lambda state: state.has('Tablet', player) and state._timespinner_has_keycard_B(world, player)),
|
||||
LocationData('Varndagroth tower right (elevator)', 'Varndagroth Towers (Right): Medbay terminal (Bleakness Research)', 1337164, lambda state: state.has('Tablet', player) and logic.has_keycard_B(state)),
|
||||
LocationData('The lab (upper)', 'Lab: Download and chest room terminal (Experiment #13)', 1337165, lambda state: state.has('Tablet', player)),
|
||||
LocationData('The lab (power off)', 'Lab: Middle terminal (Amadeus Laboratory Map)', 1337166, lambda state: state.has('Tablet', player)),
|
||||
LocationData('The lab (power off)', 'Lab: Sentry platform terminal (Origins)', 1337167, lambda state: state.has('Tablet', player)),
|
||||
|
@ -228,23 +236,23 @@ def get_locations(world: Optional[MultiWorld], player: Optional[int]) -> Tuple[L
|
|||
LocationData('Library top', 'Library: Memory - Library Gap (Lachiemi Sun)', 1337179),
|
||||
LocationData('Library top', 'Library: Memory - Mr. Hat Portrait (Moonlit Night)', 1337180),
|
||||
LocationData('Varndagroth tower left', 'Varndagroth Towers (Left): Memory - Elevator (Nomads)', 1337181, lambda state: state.has('Elevator Keycard', player)),
|
||||
LocationData('Varndagroth tower right (lower)', 'Varndagroth Towers: Memory - Siren Elevator (Childhood)', 1337182, lambda state: state._timespinner_has_keycard_B(world, player)),
|
||||
LocationData('Varndagroth tower right (lower)', 'Varndagroth Towers: Memory - Siren Elevator (Childhood)', 1337182, logic.has_keycard_B),
|
||||
LocationData('Varndagroth tower right (lower)', 'Varndagroth Towers (Right): Memory - Bottom (Faron)', 1337183),
|
||||
LocationData('Military Fortress', 'Military Fortress: Memory - Bomber Climb (A Solution)', 1337184, lambda state: state.has('Timespinner Wheel', player) and state._timespinner_has_doublejump_of_npc(world, player)),
|
||||
LocationData('The lab', 'Lab: Memory - Genza\'s Secret Stash 1 (An Old Friend)', 1337185, lambda state: state._timespinner_can_break_walls(world, player)),
|
||||
LocationData('The lab', 'Lab: Memory - Genza\'s Secret Stash 2 (Twilight Dinner)', 1337186, lambda state: state._timespinner_can_break_walls(world, player)),
|
||||
LocationData('Emperors tower', 'Emperor\'s Tower: Memory - Way Up There (Final Circle)', 1337187, lambda state: state._timespinner_has_doublejump_of_npc(world, player)),
|
||||
LocationData('Military Fortress', 'Military Fortress: Memory - Bomber Climb (A Solution)', 1337184, lambda state: state.has('Timespinner Wheel', player) and logic.has_doublejump_of_npc(state)),
|
||||
LocationData('The lab', 'Lab: Memory - Genza\'s Secret Stash 1 (An Old Friend)', 1337185, logic.can_break_walls),
|
||||
LocationData('The lab', 'Lab: Memory - Genza\'s Secret Stash 2 (Twilight Dinner)', 1337186, logic.can_break_walls),
|
||||
LocationData('Emperors tower', 'Emperor\'s Tower: Memory - Way Up There (Final Circle)', 1337187, logic.has_doublejump_of_npc),
|
||||
LocationData('Forest', 'Forest: Journal - Rats (Lachiem Expedition)', 1337188),
|
||||
LocationData('Forest', 'Forest: Journal - Bat Jump Ledge (Peace Treaty)', 1337189, lambda state: state._timespinner_has_doublejump_of_npc(world, player) or state._timespinner_has_forwarddash_doublejump(world, player) or state._timespinner_has_fastjump_on_npc(world, player)),
|
||||
LocationData('Castle Ramparts', 'Castle Ramparts: Journal - Floating in Moat (Prime Edicts)', 1337190),
|
||||
LocationData('Forest', 'Forest: Journal - Bat Jump Ledge (Peace Treaty)', 1337189, lambda state: logic.has_doublejump_of_npc(state) or logic.has_forwarddash_doublejump(state) or logic.has_fastjump_on_npc(state)),
|
||||
LocationData('Forest', 'Forest: Journal - Floating in Moat (Prime Edicts)', 1337190, lambda state: state.has('Water Mask', player) if flooded.flood_moat else True),
|
||||
LocationData('Castle Ramparts', 'Castle Ramparts: Journal - Archer + Knight (Declaration of Independence)', 1337191),
|
||||
LocationData('Castle Keep', 'Castle Keep: Journal - Under the Twins (Letter of Reference)', 1337192),
|
||||
LocationData('Castle Keep', 'Castle Keep: Journal - Castle Loop Giantess (Political Advice)', 1337193),
|
||||
LocationData('Royal towers (lower)', 'Royal Towers: Journal - Aelana\'s Room (Diplomatic Missive)', 1337194, lambda state: state._timespinner_has_pink(world, player)),
|
||||
LocationData('Castle Basement', 'Castle Basement: Journal - Castle Loop Giantess (Political Advice)', 1337193),
|
||||
LocationData('Royal towers (lower)', 'Royal Towers: Journal - Aelana\'s Room (Diplomatic Missive)', 1337194, logic.has_pink),
|
||||
LocationData('Royal towers (upper)', 'Royal Towers: Journal - Top Struggle Juggle Base (War of the Sisters)', 1337195),
|
||||
LocationData('Royal towers (upper)', 'Royal Towers: Journal - Aelana Boss (Stained Letter)', 1337196),
|
||||
LocationData('Royal towers', 'Royal Towers: Journal - Near Bottom Struggle Juggle (Mission Findings)', 1337197, lambda state: state._timespinner_has_doublejump_of_npc(world, player)),
|
||||
LocationData('Caves of Banishment (Maw)', 'Caves of Banishment (Maw): Journal - Lower Left Caves (Naivety)', 1337198)
|
||||
LocationData('Royal towers', 'Royal Towers: Journal - Near Bottom Struggle Juggle (Mission Findings)', 1337197, lambda state: logic.has_doublejump_of_npc(state) if not flooded.flood_courtyard else True),
|
||||
LocationData('Caves of Banishment (Maw)', 'Caves of Banishment (Maw): Journal - Lower Left Caves (Naivety)', 1337198, lambda state: state.has('Water Mask', player) if flooded.flood_maw else True)
|
||||
)
|
||||
|
||||
# 1337199 - 1337236 Reserved for future use
|
||||
|
@ -264,11 +272,3 @@ def get_locations(world: Optional[MultiWorld], player: Optional[int]) -> Tuple[L
|
|||
)
|
||||
|
||||
return tuple(location_table)
|
||||
|
||||
|
||||
starter_progression_locations: Tuple[str, ...] = (
|
||||
'Lake Desolation: Starter chest 2',
|
||||
'Lake Desolation: Starter chest 3',
|
||||
'Lake Desolation: Starter chest 1',
|
||||
'Lake Desolation (Lower): Timespinner Wheel room'
|
||||
)
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
from typing import Union
|
||||
from BaseClasses import MultiWorld, CollectionState
|
||||
from .Options import is_option_enabled
|
||||
from .PreCalculatedWeights import PreCalculatedWeights
|
||||
|
||||
|
||||
class TimespinnerLogic:
|
||||
player: int
|
||||
|
||||
flag_unchained_keys: bool
|
||||
flag_eye_spy: bool
|
||||
flag_specific_keycards: bool
|
||||
pyramid_keys_unlock: Union[str, None]
|
||||
present_keys_unlock: Union[str, None]
|
||||
past_keys_unlock: Union[str, None]
|
||||
time_keys_unlock: Union[str, None]
|
||||
|
||||
def __init__(self, world: MultiWorld, player: int, precalculated_weights: PreCalculatedWeights):
|
||||
self.player = player
|
||||
|
||||
self.flag_specific_keycards = is_option_enabled(world, player, "SpecificKeycards")
|
||||
self.flag_eye_spy = is_option_enabled(world, player, "EyeSpy")
|
||||
self.flag_unchained_keys = is_option_enabled(world, player, "UnchainedKeys")
|
||||
|
||||
if precalculated_weights:
|
||||
if self.flag_unchained_keys:
|
||||
self.pyramid_keys_unlock = None
|
||||
self.present_keys_unlock = precalculated_weights.present_key_unlock
|
||||
self.past_keys_unlock = precalculated_weights.past_key_unlock
|
||||
self.time_keys_unlock = precalculated_weights.time_key_unlock
|
||||
else:
|
||||
self.pyramid_keys_unlock = precalculated_weights.pyramid_keys_unlock
|
||||
self.present_keys_unlock = None
|
||||
self.past_keys_unlock = None
|
||||
self.time_keys_unlock = None
|
||||
|
||||
def has_timestop(self, state: CollectionState) -> bool:
|
||||
return state.has_any({'Timespinner Wheel', 'Succubus Hairpin', 'Lightwall', 'Celestial Sash'}, self.player)
|
||||
|
||||
def has_doublejump(self, state: CollectionState) -> bool:
|
||||
return state.has_any({'Succubus Hairpin', 'Lightwall', 'Celestial Sash'}, self.player)
|
||||
|
||||
def has_forwarddash_doublejump(self, state: CollectionState) -> bool:
|
||||
return self.has_upwarddash(state) \
|
||||
or (state.has('Talaria Attachment', self.player) and self.has_doublejump(state))
|
||||
|
||||
def has_doublejump_of_npc(self, state: CollectionState) -> bool:
|
||||
return self.has_upwarddash(state) \
|
||||
or (state.has('Timespinner Wheel', self.player) and self.has_doublejump(state))
|
||||
|
||||
def has_fastjump_on_npc(self, state: CollectionState) -> bool:
|
||||
return state.has_all({'Timespinner Wheel', 'Talaria Attachment'}, self.player)
|
||||
|
||||
def has_multiple_small_jumps_of_npc(self, state: CollectionState) -> bool:
|
||||
return state.has('Timespinner Wheel', self.player) or self.has_upwarddash(state)
|
||||
|
||||
def has_upwarddash(self, state: CollectionState) -> bool:
|
||||
return state.has_any({'Lightwall', 'Celestial Sash'}, self.player)
|
||||
|
||||
def has_fire(self, state: CollectionState) -> bool:
|
||||
return state.has_any({'Fire Orb', 'Infernal Flames', 'Pyro Ring', 'Djinn Inferno'}, self.player)
|
||||
|
||||
def has_pink(self, state: CollectionState) -> bool:
|
||||
return state.has_any({'Plasma Orb', 'Plasma Geyser', 'Royal Ring'}, self.player)
|
||||
|
||||
def has_keycard_A(self, state: CollectionState) -> bool:
|
||||
return state.has('Security Keycard A', self.player)
|
||||
|
||||
def has_keycard_B(self, state: CollectionState) -> bool:
|
||||
if self.flag_specific_keycards:
|
||||
return state.has('Security Keycard B', self.player)
|
||||
else:
|
||||
return state.has_any({'Security Keycard A', 'Security Keycard B'}, self.player)
|
||||
|
||||
def has_keycard_C(self, state: CollectionState) -> bool:
|
||||
if self.flag_specific_keycards:
|
||||
return state.has('Security Keycard C', self.player)
|
||||
else:
|
||||
return state.has_any({'Security Keycard A', 'Security Keycard B', 'Security Keycard C'}, self.player)
|
||||
|
||||
def has_keycard_D(self, state: CollectionState) -> bool:
|
||||
if self.flag_specific_keycards:
|
||||
return state.has('Security Keycard D', self.player)
|
||||
else:
|
||||
return state.has_any({'Security Keycard A', 'Security Keycard B', 'Security Keycard C', 'Security Keycard D'}, self.player)
|
||||
|
||||
def can_break_walls(self, state: CollectionState) -> bool:
|
||||
if self.flag_eye_spy:
|
||||
return state.has('Oculus Ring', self.player)
|
||||
else:
|
||||
return True
|
||||
|
||||
def can_kill_all_3_bosses(self, state: CollectionState) -> bool:
|
||||
return state.has_all({'Killed Maw', 'Killed Twins', 'Killed Aelana'}, self.player)
|
||||
|
||||
def has_teleport(self, state: CollectionState) -> bool:
|
||||
return self.flag_unchained_keys or state.has('Twin Pyramid Key', self.player)
|
||||
|
||||
def can_teleport_to(self, state: CollectionState, era: str, gate: str) -> bool:
|
||||
if not self.flag_unchained_keys:
|
||||
return self.pyramid_keys_unlock == gate
|
||||
|
||||
if era == "Present":
|
||||
return self.present_keys_unlock == gate and state.has("Modern Warp Beacon", self.player)
|
||||
elif era == "Past":
|
||||
return self.past_keys_unlock == gate and state.has("Timeworn Warp Beacon", self.player)
|
||||
elif era == "Time":
|
||||
return self.time_keys_unlock == gate and state.has("Mysterious Warp Beacon", self.player)
|
||||
else:
|
||||
raise Exception("Invallid Era: {}".format(era))
|
|
@ -1,61 +0,0 @@
|
|||
from BaseClasses import MultiWorld
|
||||
from ..AutoWorld import LogicMixin
|
||||
from .Options import is_option_enabled
|
||||
|
||||
class TimespinnerLogic(LogicMixin):
|
||||
def _timespinner_has_timestop(self, world: MultiWorld, player: int) -> bool:
|
||||
return self.has_any({'Timespinner Wheel', 'Succubus Hairpin', 'Lightwall', 'Celestial Sash'}, player)
|
||||
|
||||
def _timespinner_has_doublejump(self, world: MultiWorld, player: int) -> bool:
|
||||
return self.has_any({'Succubus Hairpin', 'Lightwall', 'Celestial Sash'}, player)
|
||||
|
||||
def _timespinner_has_forwarddash_doublejump(self, world: MultiWorld, player: int) -> bool:
|
||||
return self._timespinner_has_upwarddash(world, player) or (self.has('Talaria Attachment', player) and self._timespinner_has_doublejump(world, player))
|
||||
|
||||
def _timespinner_has_doublejump_of_npc(self, world: MultiWorld, player: int) -> bool:
|
||||
return self._timespinner_has_upwarddash(world, player) or (self.has('Timespinner Wheel', player) and self._timespinner_has_doublejump(world, player))
|
||||
|
||||
def _timespinner_has_fastjump_on_npc(self, world: MultiWorld, player: int) -> bool:
|
||||
return self.has_all({'Timespinner Wheel', 'Talaria Attachment'}, player)
|
||||
|
||||
def _timespinner_has_multiple_small_jumps_of_npc(self, world: MultiWorld, player: int) -> bool:
|
||||
return self.has('Timespinner Wheel', player) or self._timespinner_has_upwarddash(world, player)
|
||||
|
||||
def _timespinner_has_upwarddash(self, world: MultiWorld, player: int) -> bool:
|
||||
return self.has_any({'Lightwall', 'Celestial Sash'}, player)
|
||||
|
||||
def _timespinner_has_fire(self, world: MultiWorld, player: int) -> bool:
|
||||
return self.has_any({'Fire Orb', 'Infernal Flames', 'Pyro Ring', 'Djinn Inferno'}, player)
|
||||
|
||||
def _timespinner_has_pink(self, world: MultiWorld, player: int) -> bool:
|
||||
return self.has_any({'Plasma Orb', 'Plasma Geyser', 'Royal Ring'}, player)
|
||||
|
||||
def _timespinner_has_keycard_A(self, world: MultiWorld, player: int) -> bool:
|
||||
return self.has('Security Keycard A', player)
|
||||
|
||||
def _timespinner_has_keycard_B(self, world: MultiWorld, player: int) -> bool:
|
||||
if is_option_enabled(world, player, "SpecificKeycards"):
|
||||
return self.has('Security Keycard B', player)
|
||||
else:
|
||||
return self.has_any({'Security Keycard A', 'Security Keycard B'}, player)
|
||||
|
||||
def _timespinner_has_keycard_C(self, world: MultiWorld, player: int) -> bool:
|
||||
if is_option_enabled(world, player, "SpecificKeycards"):
|
||||
return self.has('Security Keycard C', player)
|
||||
else:
|
||||
return self.has_any({'Security Keycard A', 'Security Keycard B', 'Security Keycard C'}, player)
|
||||
|
||||
def _timespinner_has_keycard_D(self, world: MultiWorld, player: int) -> bool:
|
||||
if is_option_enabled(world, player, "SpecificKeycards"):
|
||||
return self.has('Security Keycard D', player)
|
||||
else:
|
||||
return self.has_any({'Security Keycard A', 'Security Keycard B', 'Security Keycard C', 'Security Keycard D'}, player)
|
||||
|
||||
def _timespinner_can_break_walls(self, world: MultiWorld, player: int) -> bool:
|
||||
if is_option_enabled(world, player, "EyeSpy"):
|
||||
return self.has('Oculus Ring', player)
|
||||
else:
|
||||
return True
|
||||
|
||||
def _timespinner_can_kill_all_3_bosses(self, world: MultiWorld, player: int) -> bool:
|
||||
return self.has_all({'Killed Maw', 'Killed Twins', 'Killed Aelana'}, player)
|
|
@ -1,6 +1,6 @@
|
|||
from typing import Dict, Union
|
||||
from typing import Dict, Union, List
|
||||
from BaseClasses import MultiWorld
|
||||
from Options import Toggle, DefaultOnToggle, DeathLink, Choice, Range, Option, OptionDict
|
||||
from Options import Toggle, DefaultOnToggle, DeathLink, Choice, Range, Option, OptionDict, OptionList
|
||||
from schema import Schema, And, Optional
|
||||
|
||||
|
||||
|
@ -191,6 +191,22 @@ class HpCap(Range):
|
|||
default = 999
|
||||
|
||||
|
||||
class LevelCap(Range):
|
||||
"""Sets the max level Lunais can achieve."""
|
||||
display_name = "Level Cap"
|
||||
range_start = 1
|
||||
range_end = 99
|
||||
default = 99
|
||||
|
||||
|
||||
class ExtraEarringsXP(Range):
|
||||
"""Adds additional XP granted by Galaxy Earrings."""
|
||||
display_name = "Extra Earrings XP"
|
||||
range_start = 0
|
||||
range_end = 24
|
||||
default = 0
|
||||
|
||||
|
||||
class BossHealing(DefaultOnToggle):
|
||||
"Enables/disables healing after boss fights. NOTE: Currently only applicable when Boss Rando is enabled."
|
||||
display_name = "Heal After Bosses"
|
||||
|
@ -282,6 +298,94 @@ class EnterSandman(Toggle):
|
|||
display_name = "Enter Sandman"
|
||||
|
||||
|
||||
class DadPercent(Toggle):
|
||||
"""The win condition is beating the boss of Emperor's Tower"""
|
||||
display_name = "Dad Percent"
|
||||
|
||||
|
||||
class RisingTides(Toggle):
|
||||
"""Random areas are flooded or drained, can be further specified with RisingTidesOverrides"""
|
||||
display_name = "Rising Tides"
|
||||
|
||||
|
||||
class RisingTidesOverrides(OptionDict):
|
||||
"""Odds for specific areas to be flooded or drained, only has effect when RisingTides is on.
|
||||
Areas that are not specified will roll with the default 33% chance of getting flooded or drained"""
|
||||
schema = Schema({
|
||||
Optional("Xarion"): {
|
||||
"Dry": And(int, lambda n: n >= 0),
|
||||
"Flooded": And(int, lambda n: n >= 0)
|
||||
},
|
||||
Optional("Maw"): {
|
||||
"Dry": And(int, lambda n: n >= 0),
|
||||
"Flooded": And(int, lambda n: n >= 0)
|
||||
},
|
||||
Optional("AncientPyramidShaft"): {
|
||||
"Dry": And(int, lambda n: n >= 0),
|
||||
"Flooded": And(int, lambda n: n >= 0)
|
||||
},
|
||||
Optional("Sandman"): {
|
||||
"Dry": And(int, lambda n: n >= 0),
|
||||
"Flooded": And(int, lambda n: n >= 0)
|
||||
},
|
||||
Optional("CastleMoat"): {
|
||||
"Dry": And(int, lambda n: n >= 0),
|
||||
"Flooded": And(int, lambda n: n >= 0)
|
||||
},
|
||||
Optional("CastleBasement"): {
|
||||
"Dry": And(int, lambda n: n >= 0),
|
||||
"FloodedWithSavePointAvailable": And(int, lambda n: n >= 0),
|
||||
"Flooded": And(int, lambda n: n >= 0)
|
||||
},
|
||||
Optional("CastleCourtyard"): {
|
||||
"Dry": And(int, lambda n: n >= 0),
|
||||
"Flooded": And(int, lambda n: n >= 0)
|
||||
},
|
||||
Optional("LakeDesolation"): {
|
||||
"Dry": And(int, lambda n: n >= 0),
|
||||
"Flooded": And(int, lambda n: n >= 0)
|
||||
},
|
||||
Optional("LakeSerene"): {
|
||||
"Dry": And(int, lambda n: n >= 0),
|
||||
"Flooded": And(int, lambda n: n >= 0)
|
||||
}
|
||||
})
|
||||
display_name = "Rising Tides Overrides"
|
||||
default = {
|
||||
"Xarion": { "Dry": 67, "Flooded": 33 },
|
||||
"Maw": { "Dry": 67, "Flooded": 33 },
|
||||
"AncientPyramidShaft": { "Dry": 67, "Flooded": 33 },
|
||||
"Sandman": { "Dry": 67, "Flooded": 33 },
|
||||
"CastleMoat": { "Dry": 67, "Flooded": 33 },
|
||||
"CastleBasement": { "Dry": 66, "Flooded": 17, "FloodedWithSavePointAvailable": 17 },
|
||||
"CastleCourtyard": { "Dry": 67, "Flooded": 33 },
|
||||
"LakeDesolation": { "Dry": 67, "Flooded": 33 },
|
||||
"LakeSerene": { "Dry": 67, "Flooded": 33 },
|
||||
}
|
||||
|
||||
|
||||
class UnchainedKeys(Toggle):
|
||||
"""Start with Twin Pyramid Key, which does not give free warp;
|
||||
warp items for Past, Present, (and ??? with Enter Sandman) can be found."""
|
||||
display_name = "Unchained Keys"
|
||||
|
||||
|
||||
class TrapChance(Range):
|
||||
"""Chance of traps in the item pool.
|
||||
Traps will only replace filler items such as potions, vials and antidotes"""
|
||||
display_name = "Trap Chance"
|
||||
range_start = 0
|
||||
range_end = 100
|
||||
default = 10
|
||||
|
||||
|
||||
class Traps(OptionList):
|
||||
"""List of traps that may be in the item pool to find"""
|
||||
display_name = "Traps Types"
|
||||
valid_keys = { "Meteor Sparrow Trap", "Poison Trap", "Chaos Trap", "Neurotoxin Trap", "Bee Trap" }
|
||||
default = [ "Meteor Sparrow Trap", "Poison Trap", "Chaos Trap", "Neurotoxin Trap", "Bee Trap" ]
|
||||
|
||||
|
||||
# Some options that are available in the timespinner randomizer arent currently implemented
|
||||
timespinner_options: Dict[str, Option] = {
|
||||
"StartWithJewelryBox": StartWithJewelryBox,
|
||||
|
@ -299,6 +403,8 @@ timespinner_options: Dict[str, Option] = {
|
|||
"DamageRando": DamageRando,
|
||||
"DamageRandoOverrides": DamageRandoOverrides,
|
||||
"HpCap": HpCap,
|
||||
"LevelCap": LevelCap,
|
||||
"ExtraEarringsXP": ExtraEarringsXP,
|
||||
"BossHealing": BossHealing,
|
||||
"ShopFill": ShopFill,
|
||||
"ShopWarpShards": ShopWarpShards,
|
||||
|
@ -310,6 +416,12 @@ timespinner_options: Dict[str, Option] = {
|
|||
"ShowBestiary": ShowBestiary,
|
||||
"ShowDrops": ShowDrops,
|
||||
"EnterSandman": EnterSandman,
|
||||
"DadPercent": DadPercent,
|
||||
"RisingTides": RisingTides,
|
||||
"RisingTidesOverrides": RisingTidesOverrides,
|
||||
"UnchainedKeys": UnchainedKeys,
|
||||
"TrapChance": TrapChance,
|
||||
"Traps": Traps,
|
||||
"DeathLink": DeathLink,
|
||||
}
|
||||
|
||||
|
@ -318,7 +430,7 @@ def is_option_enabled(world: MultiWorld, player: int, name: str) -> bool:
|
|||
return get_option_value(world, player, name) > 0
|
||||
|
||||
|
||||
def get_option_value(world: MultiWorld, player: int, name: str) -> Union[int, dict]:
|
||||
def get_option_value(world: MultiWorld, player: int, name: str) -> Union[int, Dict, List]:
|
||||
option = getattr(world, name, None)
|
||||
if option == None:
|
||||
return 0
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
from typing import Tuple, Dict, Union
|
||||
from BaseClasses import MultiWorld
|
||||
from .Options import is_option_enabled, get_option_value
|
||||
|
||||
|
||||
class PreCalculatedWeights:
|
||||
pyramid_keys_unlock: str
|
||||
present_key_unlock: str
|
||||
past_key_unlock: str
|
||||
time_key_unlock: str
|
||||
|
||||
flood_basement: bool
|
||||
flood_basement_high: bool
|
||||
flood_xarion: bool
|
||||
flood_maw: bool
|
||||
flood_pyramid_shaft: bool
|
||||
flood_pyramid_back: bool
|
||||
flood_moat: bool
|
||||
flood_courtyard: bool
|
||||
flood_lake_desolation: bool
|
||||
dry_lake_serene: bool
|
||||
|
||||
def __init__(self, world: MultiWorld, player: int):
|
||||
weights_overrrides: Dict[str, Dict[str, int]] = self.get_flood_weights_overrides(world, player)
|
||||
|
||||
self.flood_basement, self.flood_basement_high = \
|
||||
self.roll_flood_setting_with_available_save(world, player, weights_overrrides, "CastleBasement")
|
||||
self.flood_xarion = self.roll_flood_setting(world, player, weights_overrrides, "Xarion")
|
||||
self.flood_maw = self.roll_flood_setting(world, player, weights_overrrides, "Maw")
|
||||
self.flood_pyramid_shaft = self.roll_flood_setting(world, player, weights_overrrides, "AncientPyramidShaft")
|
||||
self.flood_pyramid_back = self.roll_flood_setting(world, player, weights_overrrides, "Sandman")
|
||||
self.flood_moat = self.roll_flood_setting(world, player, weights_overrrides, "CastleMoat")
|
||||
self.flood_courtyard = self.roll_flood_setting(world, player, weights_overrrides, "CastleCourtyard")
|
||||
self.flood_lake_desolation = self.roll_flood_setting(world, player, weights_overrrides, "LakeDesolation")
|
||||
self.dry_lake_serene = self.roll_flood_setting(world, player, weights_overrrides, "LakeSerene")
|
||||
|
||||
self.pyramid_keys_unlock, self.present_key_unlock, self.past_key_unlock, self.time_key_unlock = \
|
||||
self.get_pyramid_keys_unlock(world, player, self.flood_maw)
|
||||
|
||||
|
||||
def get_pyramid_keys_unlock(self, world: MultiWorld, player: int, is_maw_flooded: bool) -> Tuple[str, str, str, str]:
|
||||
present_teleportation_gates: Tuple[str, ...] = (
|
||||
"GateKittyBoss",
|
||||
"GateLeftLibrary",
|
||||
"GateMilitaryGate",
|
||||
"GateSealedCaves",
|
||||
"GateSealedSirensCave",
|
||||
"GateLakeDesolation"
|
||||
)
|
||||
|
||||
past_teleportation_gates: Tuple[str, ...] = (
|
||||
"GateLakeSereneRight",
|
||||
"GateAccessToPast",
|
||||
"GateCastleRamparts",
|
||||
"GateCastleKeep",
|
||||
"GateRoyalTowers",
|
||||
"GateCavesOfBanishment"
|
||||
)
|
||||
|
||||
ancient_pyramid_teleportation_gates: Tuple[str, ...] = (
|
||||
"GateGyre",
|
||||
"GateLeftPyramid",
|
||||
"GateRightPyramid"
|
||||
)
|
||||
|
||||
if not world:
|
||||
return (
|
||||
present_teleportation_gates[0],
|
||||
present_teleportation_gates[0],
|
||||
past_teleportation_gates[0],
|
||||
ancient_pyramid_teleportation_gates[0]
|
||||
)
|
||||
|
||||
if not is_maw_flooded:
|
||||
past_teleportation_gates += ("GateMaw", )
|
||||
|
||||
if is_option_enabled(world, player, "Inverted"):
|
||||
all_gates: Tuple[str, ...] = present_teleportation_gates
|
||||
else:
|
||||
all_gates: Tuple[str, ...] = past_teleportation_gates + present_teleportation_gates
|
||||
|
||||
return (
|
||||
world.random.choice(all_gates),
|
||||
world.random.choice(present_teleportation_gates),
|
||||
world.random.choice(past_teleportation_gates),
|
||||
world.random.choice(ancient_pyramid_teleportation_gates)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def get_flood_weights_overrides( world: MultiWorld, player: int) -> Dict[str, int]:
|
||||
weights_overrides_option: Union[int, Dict[str, Dict[str, int]]] = \
|
||||
get_option_value(world, player, "RisingTidesOverrides")
|
||||
|
||||
if weights_overrides_option == 0:
|
||||
return {}
|
||||
else:
|
||||
return weights_overrides_option
|
||||
|
||||
@staticmethod
|
||||
def roll_flood_setting(world: MultiWorld, player: int, weights: Dict[str, Dict[str, int]], key: str) -> bool:
|
||||
if not world or not is_option_enabled(world, player, "RisingTides"):
|
||||
return False
|
||||
|
||||
weights = weights[key] if key in weights else { "Dry": 67, "Flooded": 33 }
|
||||
|
||||
result: str = world.random.choices(list(weights.keys()), weights=list(map(int, weights.values())))[0]
|
||||
|
||||
return result == "Flooded"
|
||||
|
||||
@staticmethod
|
||||
def roll_flood_setting_with_available_save(world: MultiWorld, player: int,
|
||||
weights: Dict[str, Dict[str, int]], key: str) -> Tuple[bool, bool]:
|
||||
|
||||
if not world or not is_option_enabled(world, player, "RisingTides"):
|
||||
return False, False
|
||||
|
||||
weights = weights[key] if key in weights else {"Dry": 66, "Flooded": 17, "FloodedWithSavePointAvailable": 17}
|
||||
|
||||
result: str = world.random.choices(list(weights.keys()), weights=list(map(int, weights.values())))[0]
|
||||
|
||||
if result == "Dry":
|
||||
return False, False
|
||||
elif result == "Flooded":
|
||||
return True, False
|
||||
elif result == "FloodedWithSavePointAvailable":
|
||||
return True, True
|
|
@ -1,33 +0,0 @@
|
|||
from typing import Tuple
|
||||
from BaseClasses import MultiWorld
|
||||
from .Options import is_option_enabled
|
||||
|
||||
def get_pyramid_keys_unlock(world: MultiWorld, player: int) -> str:
|
||||
present_teleportation_gates: Tuple[str, ...] = (
|
||||
"GateKittyBoss",
|
||||
"GateLeftLibrary",
|
||||
"GateMilitairyGate",
|
||||
"GateSealedCaves",
|
||||
"GateSealedSirensCave",
|
||||
"GateLakeDesolation"
|
||||
)
|
||||
|
||||
past_teleportation_gates: Tuple[str, ...] = (
|
||||
"GateLakeSirineRight",
|
||||
"GateAccessToPast",
|
||||
"GateCastleRamparts",
|
||||
"GateCastleKeep",
|
||||
"GateRoyalTowers",
|
||||
"GateMaw",
|
||||
"GateCavesOfBanishment"
|
||||
)
|
||||
|
||||
if is_option_enabled(world, player, "Inverted"):
|
||||
gates = present_teleportation_gates
|
||||
else:
|
||||
gates = (*past_teleportation_gates, *present_teleportation_gates)
|
||||
|
||||
if not world:
|
||||
return gates[0]
|
||||
|
||||
return world.random.choice(gates)
|
|
@ -1,10 +1,14 @@
|
|||
from typing import List, Set, Dict, Tuple, Optional, Callable
|
||||
from BaseClasses import MultiWorld, Region, Entrance, Location
|
||||
from BaseClasses import CollectionState, MultiWorld, Region, Entrance, Location
|
||||
from .Options import is_option_enabled
|
||||
from .Locations import LocationData
|
||||
from .PreCalculatedWeights import PreCalculatedWeights
|
||||
from .LogicExtensions import TimespinnerLogic
|
||||
|
||||
|
||||
def create_regions(world: MultiWorld, player: int, locations: Tuple[LocationData, ...], location_cache: List[Location], pyramid_keys_unlock: str):
|
||||
def create_regions(world: MultiWorld, player: int, locations: Tuple[LocationData, ...], location_cache: List[Location],
|
||||
precalculated_weights: PreCalculatedWeights):
|
||||
|
||||
locations_per_region = get_locations_per_region(locations)
|
||||
|
||||
regions = [
|
||||
|
@ -16,7 +20,6 @@ def create_regions(world: MultiWorld, player: int, locations: Tuple[LocationData
|
|||
create_region(world, player, locations_per_region, location_cache, 'Eastern lake desolation'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Library'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Library top'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Ifrit\'s Lair'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Varndagroth tower left'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Varndagroth tower right (upper)'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Varndagroth tower right (lower)'),
|
||||
|
@ -27,7 +30,6 @@ def create_regions(world: MultiWorld, player: int, locations: Tuple[LocationData
|
|||
create_region(world, player, locations_per_region, location_cache, 'The lab'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'The lab (power off)'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'The lab (upper)'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Ravenlord\'s Lair'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Emperors tower'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Skeleton Shaft'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Sealed Caves (upper)'),
|
||||
|
@ -42,6 +44,7 @@ def create_regions(world: MultiWorld, player: int, locations: Tuple[LocationData
|
|||
create_region(world, player, locations_per_region, location_cache, 'Caves of Banishment (Sirens)'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Castle Ramparts'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Castle Keep'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Castle Basement'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Royal towers (lower)'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Royal towers'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Royal towers (upper)'),
|
||||
|
@ -52,6 +55,12 @@ def create_regions(world: MultiWorld, player: int, locations: Tuple[LocationData
|
|||
create_region(world, player, locations_per_region, location_cache, 'Space time continuum')
|
||||
]
|
||||
|
||||
if is_option_enabled(world, player, "GyreArchives"):
|
||||
regions.extend([
|
||||
create_region(world, player, locations_per_region, location_cache, 'Ravenlord\'s Lair'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Ifrit\'s Lair'),
|
||||
])
|
||||
|
||||
if __debug__:
|
||||
throwIfAnyLocationIsNotAssignedToARegion(regions, locations_per_region.keys())
|
||||
|
||||
|
@ -59,125 +68,133 @@ def create_regions(world: MultiWorld, player: int, locations: Tuple[LocationData
|
|||
|
||||
connectStartingRegion(world, player)
|
||||
|
||||
flooded: PreCalculatedWeights = precalculated_weights
|
||||
logic = TimespinnerLogic(world, player, precalculated_weights)
|
||||
names: Dict[str, int] = {}
|
||||
|
||||
connect(world, player, names, 'Lake desolation', 'Lower lake desolation', lambda state: state._timespinner_has_timestop(world, player) or state.has('Talaria Attachment', player))
|
||||
connect(world, player, names, 'Lake desolation', 'Upper lake desolation', lambda state: state._timespinner_has_fire(world, player) and state.can_reach('Upper Lake Serene', 'Region', player))
|
||||
connect(world, player, names, 'Lake desolation', 'Skeleton Shaft', lambda state: state._timespinner_has_doublejump(world, player))
|
||||
connect(world, player, names, 'Lake desolation', 'Space time continuum', lambda state: state.has('Twin Pyramid Key', player))
|
||||
connect(world, player, names, 'Lake desolation', 'Lower lake desolation', lambda state: logic.has_timestop(state) or state.has('Talaria Attachment', player) or flooded.flood_lake_desolation)
|
||||
connect(world, player, names, 'Lake desolation', 'Upper lake desolation', lambda state: logic.has_fire(state) and state.can_reach('Upper Lake Serene', 'Region', player))
|
||||
connect(world, player, names, 'Lake desolation', 'Skeleton Shaft', lambda state: logic.has_doublejump(state) or flooded.flood_lake_desolation)
|
||||
connect(world, player, names, 'Lake desolation', 'Space time continuum', logic.has_teleport)
|
||||
connect(world, player, names, 'Upper lake desolation', 'Lake desolation')
|
||||
connect(world, player, names, 'Upper lake desolation', 'Eastern lake desolation')
|
||||
connect(world, player, names, 'Lower lake desolation', 'Lake desolation')
|
||||
connect(world, player, names, 'Lower lake desolation', 'Eastern lake desolation')
|
||||
connect(world, player, names, 'Eastern lake desolation', 'Space time continuum', lambda state: state.has('Twin Pyramid Key', player))
|
||||
connect(world, player, names, 'Eastern lake desolation', 'Space time continuum', logic.has_teleport)
|
||||
connect(world, player, names, 'Eastern lake desolation', 'Library')
|
||||
connect(world, player, names, 'Eastern lake desolation', 'Lower lake desolation')
|
||||
connect(world, player, names, 'Eastern lake desolation', 'Upper lake desolation', lambda state: state._timespinner_has_fire(world, player) and state.can_reach('Upper Lake Serene', 'Region', player))
|
||||
connect(world, player, names, 'Eastern lake desolation', 'Upper lake desolation', lambda state: logic.has_fire(state) and state.can_reach('Upper Lake Serene', 'Region', player))
|
||||
connect(world, player, names, 'Library', 'Eastern lake desolation')
|
||||
connect(world, player, names, 'Library', 'Library top', lambda state: state._timespinner_has_doublejump(world, player) or state.has('Talaria Attachment', player))
|
||||
connect(world, player, names, 'Library', 'Varndagroth tower left', lambda state: state._timespinner_has_keycard_D(world, player))
|
||||
connect(world, player, names, 'Library', 'Space time continuum', lambda state: state.has('Twin Pyramid Key', player))
|
||||
connect(world, player, names, 'Library', 'Library top', lambda state: logic.has_doublejump(state) or state.has('Talaria Attachment', player))
|
||||
connect(world, player, names, 'Library', 'Varndagroth tower left', logic.has_keycard_D)
|
||||
connect(world, player, names, 'Library', 'Space time continuum', logic.has_teleport)
|
||||
connect(world, player, names, 'Library top', 'Library')
|
||||
connect(world, player, names, 'Library top', 'Ifrit\'s Lair', lambda state: state.has('Kobo', player) and state.can_reach('Refugee Camp', 'Region', player))
|
||||
connect(world, player, names, 'Ifrit\'s Lair', 'Library top')
|
||||
connect(world, player, names, 'Varndagroth tower left', 'Library')
|
||||
connect(world, player, names, 'Varndagroth tower left', 'Varndagroth tower right (upper)', lambda state: state._timespinner_has_keycard_C(world, player))
|
||||
connect(world, player, names, 'Varndagroth tower left', 'Varndagroth tower right (lower)', lambda state: state._timespinner_has_keycard_B(world, player))
|
||||
connect(world, player, names, 'Varndagroth tower left', 'Sealed Caves (Sirens)', lambda state: state._timespinner_has_keycard_B(world, player) and state.has('Elevator Keycard', player))
|
||||
connect(world, player, names, 'Varndagroth tower left', 'Varndagroth tower right (upper)', logic.has_keycard_C)
|
||||
connect(world, player, names, 'Varndagroth tower left', 'Varndagroth tower right (lower)', logic.has_keycard_B)
|
||||
connect(world, player, names, 'Varndagroth tower left', 'Sealed Caves (Sirens)', lambda state: logic.has_keycard_B(state) and state.has('Elevator Keycard', player))
|
||||
connect(world, player, names, 'Varndagroth tower left', 'Refugee Camp', lambda state: state.has('Timespinner Wheel', player) and state.has('Timespinner Spindle', player))
|
||||
connect(world, player, names, 'Varndagroth tower right (upper)', 'Varndagroth tower left')
|
||||
connect(world, player, names, 'Varndagroth tower right (upper)', 'Varndagroth tower right (elevator)', lambda state: state.has('Elevator Keycard', player))
|
||||
connect(world, player, names, 'Varndagroth tower right (elevator)', 'Varndagroth tower right (upper)')
|
||||
connect(world, player, names, 'Varndagroth tower right (elevator)', 'Varndagroth tower right (lower)')
|
||||
connect(world, player, names, 'Varndagroth tower right (lower)', 'Varndagroth tower left', lambda state: state._timespinner_has_keycard_B(world, player))
|
||||
connect(world, player, names, 'Varndagroth tower right (lower)', 'Varndagroth tower left', logic.has_keycard_B)
|
||||
connect(world, player, names, 'Varndagroth tower right (lower)', 'Varndagroth tower right (elevator)', lambda state: state.has('Elevator Keycard', player))
|
||||
connect(world, player, names, 'Varndagroth tower right (lower)', 'Sealed Caves (Sirens)', lambda state: state._timespinner_has_keycard_B(world, player) and state.has('Elevator Keycard', player))
|
||||
connect(world, player, names, 'Varndagroth tower right (lower)', 'Military Fortress', lambda state: state._timespinner_can_kill_all_3_bosses(world, player))
|
||||
connect(world, player, names, 'Varndagroth tower right (lower)', 'Space time continuum', lambda state: state.has('Twin Pyramid Key', player))
|
||||
connect(world, player, names, 'Varndagroth tower right (lower)', 'Sealed Caves (Sirens)', lambda state: logic.has_keycard_B(state) and state.has('Elevator Keycard', player))
|
||||
connect(world, player, names, 'Varndagroth tower right (lower)', 'Military Fortress', logic.can_kill_all_3_bosses)
|
||||
connect(world, player, names, 'Varndagroth tower right (lower)', 'Space time continuum', logic.has_teleport)
|
||||
connect(world, player, names, 'Sealed Caves (Sirens)', 'Varndagroth tower left', lambda state: state.has('Elevator Keycard', player))
|
||||
connect(world, player, names, 'Sealed Caves (Sirens)', 'Varndagroth tower right (lower)', lambda state: state.has('Elevator Keycard', player))
|
||||
connect(world, player, names, 'Sealed Caves (Sirens)', 'Space time continuum', lambda state: state.has('Twin Pyramid Key', player))
|
||||
connect(world, player, names, 'Military Fortress', 'Varndagroth tower right (lower)', lambda state: state._timespinner_can_kill_all_3_bosses(world, player))
|
||||
connect(world, player, names, 'Sealed Caves (Sirens)', 'Space time continuum', logic.has_teleport)
|
||||
connect(world, player, names, 'Military Fortress', 'Varndagroth tower right (lower)', logic.can_kill_all_3_bosses)
|
||||
connect(world, player, names, 'Military Fortress', 'Temporal Gyre', lambda state: state.has('Timespinner Wheel', player))
|
||||
connect(world, player, names, 'Military Fortress', 'Military Fortress (hangar)', lambda state: state._timespinner_has_doublejump(world, player))
|
||||
connect(world, player, names, 'Military Fortress', 'Military Fortress (hangar)', logic.has_doublejump)
|
||||
connect(world, player, names, 'Military Fortress (hangar)', 'Military Fortress')
|
||||
connect(world, player, names, 'Military Fortress (hangar)', 'The lab', lambda state: state._timespinner_has_keycard_B(world, player) and state._timespinner_has_doublejump(world, player))
|
||||
connect(world, player, names, 'Military Fortress (hangar)', 'The lab', lambda state: logic.has_keycard_B(state) and logic.has_doublejump(state))
|
||||
connect(world, player, names, 'Temporal Gyre', 'Military Fortress')
|
||||
connect(world, player, names, 'The lab', 'Military Fortress')
|
||||
connect(world, player, names, 'The lab', 'The lab (power off)', lambda state: state._timespinner_has_doublejump_of_npc(world, player))
|
||||
connect(world, player, names, 'The lab', 'The lab (power off)', logic.has_doublejump_of_npc)
|
||||
connect(world, player, names, 'The lab (power off)', 'The lab')
|
||||
connect(world, player, names, 'The lab (power off)', 'The lab (upper)', lambda state: state._timespinner_has_forwarddash_doublejump(world, player))
|
||||
connect(world, player, names, 'The lab (power off)', 'The lab (upper)', logic.has_forwarddash_doublejump)
|
||||
connect(world, player, names, 'The lab (upper)', 'The lab (power off)')
|
||||
connect(world, player, names, 'The lab (upper)', 'Ravenlord\'s Lair', lambda state: state.has('Merchant Crow', player))
|
||||
connect(world, player, names, 'The lab (upper)', 'Emperors tower', lambda state: state._timespinner_has_forwarddash_doublejump(world, player))
|
||||
connect(world, player, names, 'The lab (upper)', 'Emperors tower', logic.has_forwarddash_doublejump)
|
||||
connect(world, player, names, 'The lab (upper)', 'Ancient Pyramid (entrance)', lambda state: state.has_all({'Timespinner Wheel', 'Timespinner Spindle', 'Timespinner Gear 1', 'Timespinner Gear 2', 'Timespinner Gear 3'}, player))
|
||||
connect(world, player, names, 'Ravenlord\'s Lair', 'The lab (upper)')
|
||||
connect(world, player, names, 'Emperors tower', 'The lab (upper)')
|
||||
connect(world, player, names, 'Skeleton Shaft', 'Lake desolation')
|
||||
connect(world, player, names, 'Skeleton Shaft', 'Sealed Caves (upper)', lambda state: state._timespinner_has_keycard_A(world, player))
|
||||
connect(world, player, names, 'Skeleton Shaft', 'Space time continuum', lambda state: state.has('Twin Pyramid Key', player))
|
||||
connect(world, player, names, 'Skeleton Shaft', 'Sealed Caves (upper)', logic.has_keycard_A)
|
||||
connect(world, player, names, 'Skeleton Shaft', 'Space time continuum', logic.has_teleport)
|
||||
connect(world, player, names, 'Sealed Caves (upper)', 'Skeleton Shaft')
|
||||
connect(world, player, names, 'Sealed Caves (upper)', 'Sealed Caves (Xarion)', lambda state: state.has('Twin Pyramid Key', player) or state._timespinner_has_doublejump(world, player))
|
||||
connect(world, player, names, 'Sealed Caves (Xarion)', 'Sealed Caves (upper)', lambda state: state._timespinner_has_doublejump(world, player))
|
||||
connect(world, player, names, 'Sealed Caves (Xarion)', 'Space time continuum', lambda state: state.has('Twin Pyramid Key', player))
|
||||
connect(world, player, names, 'Sealed Caves (upper)', 'Sealed Caves (Xarion)', lambda state: logic.has_teleport(state) or logic.has_doublejump(state))
|
||||
connect(world, player, names, 'Sealed Caves (Xarion)', 'Sealed Caves (upper)', logic.has_doublejump)
|
||||
connect(world, player, names, 'Sealed Caves (Xarion)', 'Space time continuum', logic.has_teleport)
|
||||
connect(world, player, names, 'Refugee Camp', 'Forest')
|
||||
#connect(world, player, names, 'Refugee Camp', 'Library', lambda state: not is_option_enabled(world, player, "Inverted"))
|
||||
connect(world, player, names, 'Refugee Camp', 'Space time continuum', lambda state: state.has('Twin Pyramid Key', player))
|
||||
connect(world, player, names, 'Refugee Camp', 'Space time continuum', logic.has_teleport)
|
||||
connect(world, player, names, 'Forest', 'Refugee Camp')
|
||||
connect(world, player, names, 'Forest', 'Left Side forest Caves', lambda state: state.has('Talaria Attachment', player) or state._timespinner_has_timestop(world, player))
|
||||
connect(world, player, names, 'Forest', 'Left Side forest Caves', lambda state: state.has('Talaria Attachment', player) or logic.has_timestop(state))
|
||||
connect(world, player, names, 'Forest', 'Caves of Banishment (Sirens)')
|
||||
connect(world, player, names, 'Forest', 'Castle Ramparts')
|
||||
connect(world, player, names, 'Left Side forest Caves', 'Forest')
|
||||
connect(world, player, names, 'Left Side forest Caves', 'Upper Lake Serene', lambda state: state._timespinner_has_timestop(world, player))
|
||||
connect(world, player, names, 'Left Side forest Caves', 'Lower Lake Serene', lambda state: state.has('Water Mask', player))
|
||||
connect(world, player, names, 'Left Side forest Caves', 'Space time continuum', lambda state: state.has('Twin Pyramid Key', player))
|
||||
connect(world, player, names, 'Left Side forest Caves', 'Upper Lake Serene', logic.has_timestop)
|
||||
connect(world, player, names, 'Left Side forest Caves', 'Lower Lake Serene', lambda state: state.has('Water Mask', player) or flooded.dry_lake_serene)
|
||||
connect(world, player, names, 'Left Side forest Caves', 'Space time continuum', logic.has_teleport)
|
||||
connect(world, player, names, 'Upper Lake Serene', 'Left Side forest Caves')
|
||||
connect(world, player, names, 'Upper Lake Serene', 'Lower Lake Serene', lambda state: state.has('Water Mask', player))
|
||||
connect(world, player, names, 'Lower Lake Serene', 'Upper Lake Serene')
|
||||
connect(world, player, names, 'Lower Lake Serene', 'Left Side forest Caves')
|
||||
connect(world, player, names, 'Lower Lake Serene', 'Caves of Banishment (upper)')
|
||||
connect(world, player, names, 'Caves of Banishment (upper)', 'Upper Lake Serene', lambda state: state.has('Water Mask', player))
|
||||
connect(world, player, names, 'Caves of Banishment (upper)', 'Caves of Banishment (Maw)', lambda state: state.has('Twin Pyramid Key', player) or state._timespinner_has_doublejump(world, player))
|
||||
connect(world, player, names, 'Caves of Banishment (upper)', 'Space time continuum', lambda state: state.has('Twin Pyramid Key', player))
|
||||
connect(world, player, names, 'Caves of Banishment (Maw)', 'Caves of Banishment (upper)', lambda state: state._timespinner_has_doublejump(world, player))
|
||||
connect(world, player, names, 'Caves of Banishment (upper)', 'Upper Lake Serene', lambda state: state.has('Water Mask', player) or flooded.dry_lake_serene)
|
||||
connect(world, player, names, 'Caves of Banishment (upper)', 'Caves of Banishment (Maw)', lambda state: logic.has_doublejump(state) or state.has_any({'Gas Mask', 'Twin Pyramid Key'}, player))
|
||||
connect(world, player, names, 'Caves of Banishment (upper)', 'Space time continuum', logic.has_teleport)
|
||||
connect(world, player, names, 'Caves of Banishment (Maw)', 'Caves of Banishment (upper)', lambda state: logic.has_doublejump(state) if not flooded.flood_maw else state.has('Water Mask', player))
|
||||
connect(world, player, names, 'Caves of Banishment (Maw)', 'Caves of Banishment (Sirens)', lambda state: state.has('Gas Mask', player))
|
||||
connect(world, player, names, 'Caves of Banishment (Maw)', 'Space time continuum', lambda state: state.has('Twin Pyramid Key', player))
|
||||
connect(world, player, names, 'Caves of Banishment (Maw)', 'Space time continuum', logic.has_teleport)
|
||||
connect(world, player, names, 'Caves of Banishment (Sirens)', 'Forest')
|
||||
connect(world, player, names, 'Castle Ramparts', 'Forest')
|
||||
connect(world, player, names, 'Castle Ramparts', 'Castle Keep')
|
||||
connect(world, player, names, 'Castle Ramparts', 'Space time continuum', lambda state: state.has('Twin Pyramid Key', player))
|
||||
connect(world, player, names, 'Castle Ramparts', 'Space time continuum', logic.has_teleport)
|
||||
connect(world, player, names, 'Castle Keep', 'Castle Ramparts')
|
||||
connect(world, player, names, 'Castle Keep', 'Royal towers (lower)', lambda state: state._timespinner_has_doublejump(world, player))
|
||||
connect(world, player, names, 'Castle Keep', 'Space time continuum', lambda state: state.has('Twin Pyramid Key', player))
|
||||
connect(world, player, names, 'Castle Keep', 'Castle Basement', lambda state: state.has('Water Mask', player) or not flooded.flood_basement)
|
||||
connect(world, player, names, 'Castle Keep', 'Royal towers (lower)', logic.has_doublejump)
|
||||
connect(world, player, names, 'Castle Keep', 'Space time continuum', logic.has_teleport)
|
||||
connect(world, player, names, 'Royal towers (lower)', 'Castle Keep')
|
||||
connect(world, player, names, 'Royal towers (lower)', 'Royal towers', lambda state: state.has('Timespinner Wheel', player) or state._timespinner_has_forwarddash_doublejump(world, player))
|
||||
connect(world, player, names, 'Royal towers (lower)', 'Space time continuum', lambda state: state.has('Twin Pyramid Key', player))
|
||||
connect(world, player, names, 'Royal towers (lower)', 'Royal towers', lambda state: state.has('Timespinner Wheel', player) or logic.has_forwarddash_doublejump(state))
|
||||
connect(world, player, names, 'Royal towers (lower)', 'Space time continuum', logic.has_teleport)
|
||||
connect(world, player, names, 'Royal towers', 'Royal towers (lower)')
|
||||
connect(world, player, names, 'Royal towers', 'Royal towers (upper)', lambda state: state._timespinner_has_doublejump(world, player))
|
||||
connect(world, player, names, 'Royal towers', 'Royal towers (upper)', logic.has_doublejump)
|
||||
connect(world, player, names, 'Royal towers (upper)', 'Royal towers')
|
||||
connect(world, player, names, 'Ancient Pyramid (entrance)', 'The lab (upper)', lambda state: not is_option_enabled(world, player, "EnterSandman"))
|
||||
connect(world, player, names, 'Ancient Pyramid (entrance)', 'Ancient Pyramid (left)', lambda state: state._timespinner_has_doublejump(world, player))
|
||||
#connect(world, player, names, 'Ancient Pyramid (entrance)', 'The lab (upper)', lambda state: not is_option_enabled(world, player, "EnterSandman"))
|
||||
connect(world, player, names, 'Ancient Pyramid (entrance)', 'Ancient Pyramid (left)', logic.has_doublejump)
|
||||
connect(world, player, names, 'Ancient Pyramid (left)', 'Ancient Pyramid (entrance)')
|
||||
connect(world, player, names, 'Ancient Pyramid (left)', 'Ancient Pyramid (right)', lambda state: state._timespinner_has_upwarddash(world, player))
|
||||
connect(world, player, names, 'Ancient Pyramid (right)', 'Ancient Pyramid (left)', lambda state: state._timespinner_has_upwarddash(world, player))
|
||||
connect(world, player, names, 'Space time continuum', 'Lake desolation', lambda state: pyramid_keys_unlock == "GateLakeDesolation")
|
||||
connect(world, player, names, 'Space time continuum', 'Lower lake desolation', lambda state: pyramid_keys_unlock == "GateKittyBoss")
|
||||
connect(world, player, names, 'Space time continuum', 'Library', lambda state: pyramid_keys_unlock == "GateLeftLibrary")
|
||||
connect(world, player, names, 'Space time continuum', 'Varndagroth tower right (lower)', lambda state: pyramid_keys_unlock == "GateMilitairyGate")
|
||||
connect(world, player, names, 'Space time continuum', 'Skeleton Shaft', lambda state: pyramid_keys_unlock == "GateSealedCaves")
|
||||
connect(world, player, names, 'Space time continuum', 'Sealed Caves (Sirens)', lambda state: pyramid_keys_unlock == "GateSealedSirensCave")
|
||||
connect(world, player, names, 'Space time continuum', 'Left Side forest Caves', lambda state: pyramid_keys_unlock == "GateLakeSirineRight")
|
||||
connect(world, player, names, 'Space time continuum', 'Refugee Camp', lambda state: pyramid_keys_unlock == "GateAccessToPast")
|
||||
connect(world, player, names, 'Space time continuum', 'Castle Ramparts', lambda state: pyramid_keys_unlock == "GateCastleRamparts")
|
||||
connect(world, player, names, 'Space time continuum', 'Castle Keep', lambda state: pyramid_keys_unlock == "GateCastleKeep")
|
||||
connect(world, player, names, 'Space time continuum', 'Royal towers (lower)', lambda state: pyramid_keys_unlock == "GateRoyalTowers")
|
||||
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', 'Ancient Pyramid (entrance)', lambda state: is_option_enabled(world, player, "EnterSandman"))
|
||||
connect(world, player, names, 'Ancient Pyramid (left)', 'Ancient Pyramid (right)', lambda state: logic.has_upwarddash(state) or flooded.flood_pyramid_shaft)
|
||||
connect(world, player, names, 'Ancient Pyramid (right)', 'Ancient Pyramid (left)', lambda state: logic.has_upwarddash(state) or flooded.flood_pyramid_shaft)
|
||||
connect(world, player, names, 'Space time continuum', 'Lake desolation', lambda state: logic.can_teleport_to(state, "Present", "GateLakeDesolation"))
|
||||
connect(world, player, names, 'Space time continuum', 'Lower lake desolation', lambda state: logic.can_teleport_to(state, "Present", "GateKittyBoss"))
|
||||
connect(world, player, names, 'Space time continuum', 'Library', lambda state: logic.can_teleport_to(state, "Present", "GateLeftLibrary"))
|
||||
connect(world, player, names, 'Space time continuum', 'Varndagroth tower right (lower)', lambda state: logic.can_teleport_to(state, "Present", "GateMilitaryGate"))
|
||||
connect(world, player, names, 'Space time continuum', 'Skeleton Shaft', lambda state: logic.can_teleport_to(state, "Present", "GateSealedCaves"))
|
||||
connect(world, player, names, 'Space time continuum', 'Sealed Caves (Sirens)', lambda state: logic.can_teleport_to(state, "Present", "GateSealedSirensCave"))
|
||||
connect(world, player, names, 'Space time continuum', 'Upper Lake Serene', lambda state: logic.can_teleport_to(state, "Past", "GateLakeSereneLeft"))
|
||||
connect(world, player, names, 'Space time continuum', 'Left Side forest Caves', lambda state: logic.can_teleport_to(state, "Past", "GateLakeSereneRight"))
|
||||
connect(world, player, names, 'Space time continuum', 'Refugee Camp', lambda state: logic.can_teleport_to(state, "Past", "GateAccessToPast"))
|
||||
connect(world, player, names, 'Space time continuum', 'Castle Ramparts', lambda state: logic.can_teleport_to(state, "Past", "GateCastleRamparts"))
|
||||
connect(world, player, names, 'Space time continuum', 'Castle Keep', lambda state: logic.can_teleport_to(state, "Past", "GateCastleKeep"))
|
||||
connect(world, player, names, 'Space time continuum', 'Royal towers (lower)', lambda state: logic.can_teleport_to(state, "Past", "GateRoyalTowers"))
|
||||
connect(world, player, names, 'Space time continuum', 'Caves of Banishment (Maw)', lambda state: logic.can_teleport_to(state, "Past", "GateMaw"))
|
||||
connect(world, player, names, 'Space time continuum', 'Caves of Banishment (upper)', lambda state: logic.can_teleport_to(state, "Past", "GateCavesOfBanishment"))
|
||||
connect(world, player, names, 'Space time continuum', 'Ancient Pyramid (entrance)', lambda state: logic.can_teleport_to(state, "Time", "GateGyre") or (not is_option_enabled(world, player, "UnchainedKeys") and is_option_enabled(world, player, "EnterSandman")))
|
||||
connect(world, player, names, 'Space time continuum', 'Ancient Pyramid (left)', lambda state: logic.can_teleport_to(state, "Time", "GateLeftPyramid"))
|
||||
connect(world, player, names, 'Space time continuum', 'Ancient Pyramid (right)', lambda state: logic.can_teleport_to(state, "Time", "GateRightPyramid"))
|
||||
|
||||
if is_option_enabled(world, player, "GyreArchives"):
|
||||
connect(world, player, names, 'The lab (upper)', 'Ravenlord\'s Lair', lambda state: state.has('Merchant Crow', player))
|
||||
connect(world, player, names, 'Ravenlord\'s Lair', 'The lab (upper)')
|
||||
connect(world, player, names, 'Library top', 'Ifrit\'s Lair', lambda state: state.has('Kobo', player) and state.can_reach('Refugee Camp', 'Region', player))
|
||||
connect(world, player, names, 'Ifrit\'s Lair', 'Library top')
|
||||
|
||||
|
||||
def throwIfAnyLocationIsNotAssignedToARegion(regions: List[Region], regionNames: Set[str]):
|
||||
existingRegions = set()
|
||||
existingRegions: Set[str] = set()
|
||||
|
||||
for region in regions:
|
||||
existingRegions.add(region.name)
|
||||
|
@ -233,7 +250,8 @@ def connectStartingRegion(world: MultiWorld, player: int):
|
|||
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[[CollectionState], bool]] = None):
|
||||
sourceRegion = world.get_region(source, player)
|
||||
targetRegion = world.get_region(target, player)
|
||||
|
||||
|
|
|
@ -1,15 +1,11 @@
|
|||
from typing import Dict, List, Set, Tuple, TextIO
|
||||
|
||||
from BaseClasses import Item, MultiWorld, Location, Tutorial, ItemClassification
|
||||
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 .LogicMixin import TimespinnerLogic
|
||||
from .Items import get_item_names_per_category, item_table, starter_melee_weapons, starter_spells, filler_items
|
||||
from .Locations import get_locations, EventId
|
||||
from .Options import is_option_enabled, get_option_value, timespinner_options
|
||||
from .PyramidKeys import get_pyramid_keys_unlock
|
||||
from .PreCalculatedWeights import PreCalculatedWeights
|
||||
from .Regions import create_regions
|
||||
from ..AutoWorld import World, WebWorld
|
||||
|
||||
from worlds.AutoWorld import World, WebWorld
|
||||
|
||||
class TimespinnerWebWorld(WebWorld):
|
||||
theme = "ice"
|
||||
|
@ -43,23 +39,24 @@ class TimespinnerWorld(World):
|
|||
option_definitions = timespinner_options
|
||||
game = "Timespinner"
|
||||
topology_present = True
|
||||
data_version = 10
|
||||
data_version = 11
|
||||
web = TimespinnerWebWorld()
|
||||
required_client_version = (0, 3, 7)
|
||||
|
||||
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, None)}
|
||||
item_name_groups = get_item_names_per_category()
|
||||
|
||||
locked_locations: List[str]
|
||||
pyramid_keys_unlock: str
|
||||
location_cache: List[Location]
|
||||
precalculated_weights: PreCalculatedWeights
|
||||
|
||||
def __init__(self, world: MultiWorld, player: int):
|
||||
super().__init__(world, player)
|
||||
|
||||
self.locked_locations = []
|
||||
self.location_cache = []
|
||||
self.pyramid_keys_unlock = get_pyramid_keys_unlock(world, player)
|
||||
self.precalculated_weights = PreCalculatedWeights(world, player)
|
||||
|
||||
def generate_early(self):
|
||||
# in generate_early the start_inventory isnt copied over to precollected_items yet, so we can still modify the options directly
|
||||
|
@ -71,28 +68,37 @@ class TimespinnerWorld(World):
|
|||
self.multiworld.StartWithJewelryBox[self.player].value = self.multiworld.StartWithJewelryBox[self.player].option_true
|
||||
|
||||
def create_regions(self):
|
||||
create_regions(self.multiworld, self.player, get_locations(self.multiworld, self.player),
|
||||
self.location_cache, self.pyramid_keys_unlock)
|
||||
locations = get_locations(self.multiworld, self.player, self.precalculated_weights)
|
||||
create_regions(self.multiworld, self.player, locations, self.location_cache, self.precalculated_weights)
|
||||
|
||||
def create_item(self, name: str) -> Item:
|
||||
return create_item_with_correct_settings(self.multiworld, self.player, name)
|
||||
|
||||
def get_filler_item_name(self) -> str:
|
||||
return self.multiworld.random.choice(filler_items)
|
||||
trap_chance: int = get_option_value(self.multiworld, self.player, "TrapChance")
|
||||
enabled_traps: List[str] = get_option_value(self.multiworld, self.player, "Traps")
|
||||
|
||||
if self.multiworld.random.random() < (trap_chance / 100) and enabled_traps:
|
||||
return self.multiworld.random.choice(enabled_traps)
|
||||
else:
|
||||
return self.multiworld.random.choice(filler_items)
|
||||
|
||||
def set_rules(self):
|
||||
setup_events(self.player, self.locked_locations, self.location_cache)
|
||||
|
||||
self.multiworld.completion_condition[self.player] = lambda state: state.has('Killed Nightmare', self.player)
|
||||
final_boss: str
|
||||
if is_option_enabled(self.multiworld, self.player, "DadPercent"):
|
||||
final_boss = "Killed Emperor"
|
||||
else:
|
||||
final_boss = "Killed Nightmare"
|
||||
|
||||
self.multiworld.completion_condition[self.player] = lambda state: state.has(final_boss, self.player)
|
||||
|
||||
def generate_basic(self):
|
||||
excluded_items = get_excluded_items(self, self.multiworld, self.player)
|
||||
excluded_items: Set[str] = get_excluded_items(self, self.multiworld, self.player)
|
||||
|
||||
assign_starter_items(self.multiworld, self.player, excluded_items, self.locked_locations)
|
||||
|
||||
if not is_option_enabled(self.multiworld, self.player, "QuickSeed") and not is_option_enabled(self.multiworld, self.player, "Inverted"):
|
||||
place_first_progression_item(self.multiworld, self.player, excluded_items, self.locked_locations)
|
||||
|
||||
pool = get_item_pool(self.multiworld, self.player, excluded_items)
|
||||
|
||||
fill_item_pool_with_dummy_items(self, self.multiworld, self.player, self.locked_locations, self.location_cache, pool)
|
||||
|
@ -102,19 +108,74 @@ class TimespinnerWorld(World):
|
|||
def fill_slot_data(self) -> Dict[str, object]:
|
||||
slot_data: Dict[str, object] = {}
|
||||
|
||||
ap_specific_settings: Set[str] = {"RisingTidesOverrides", "TrapChance"}
|
||||
|
||||
for option_name in timespinner_options:
|
||||
slot_data[option_name] = get_option_value(self.multiworld, self.player, option_name)
|
||||
if (option_name not in ap_specific_settings):
|
||||
slot_data[option_name] = get_option_value(self.multiworld, self.player, option_name)
|
||||
|
||||
slot_data["StinkyMaw"] = True
|
||||
slot_data["ProgressiveVerticalMovement"] = False
|
||||
slot_data["ProgressiveKeycards"] = False
|
||||
slot_data["PyramidKeysGate"] = self.pyramid_keys_unlock
|
||||
slot_data["PersonalItems"] = get_personal_items(self.player, self.location_cache)
|
||||
slot_data["PyramidKeysGate"] = self.precalculated_weights.pyramid_keys_unlock
|
||||
slot_data["PresentGate"] = self.precalculated_weights.present_key_unlock
|
||||
slot_data["PastGate"] = self.precalculated_weights.past_key_unlock
|
||||
slot_data["TimeGate"] = self.precalculated_weights.time_key_unlock
|
||||
slot_data["Basement"] = int(self.precalculated_weights.flood_basement) + \
|
||||
int(self.precalculated_weights.flood_basement_high)
|
||||
slot_data["Xarion"] = self.precalculated_weights.flood_xarion
|
||||
slot_data["Maw"] = self.precalculated_weights.flood_maw
|
||||
slot_data["PyramidShaft"] = self.precalculated_weights.flood_pyramid_shaft
|
||||
slot_data["BackPyramid"] = self.precalculated_weights.flood_pyramid_back
|
||||
slot_data["CastleMoat"] = self.precalculated_weights.flood_moat
|
||||
slot_data["CastleCourtyard"] = self.precalculated_weights.flood_courtyard
|
||||
slot_data["LakeDesolation"] = self.precalculated_weights.flood_lake_desolation
|
||||
slot_data["DryLakeSerene"] = self.precalculated_weights.dry_lake_serene
|
||||
|
||||
return slot_data
|
||||
|
||||
def write_spoiler_header(self, spoiler_handle: TextIO):
|
||||
spoiler_handle.write('Twin Pyramid Keys unlock: %s\n' % (self.pyramid_keys_unlock))
|
||||
if is_option_enabled(self.multiworld, self.player, "UnchainedKeys"):
|
||||
spoiler_handle.write(f'Modern Warp Beacon unlock: {self.precalculated_weights.present_key_unlock}\n')
|
||||
spoiler_handle.write(f'Timeworn Warp Beacon unlock: {self.precalculated_weights.past_key_unlock}\n')
|
||||
|
||||
if is_option_enabled(self.multiworld, self.player, "EnterSandman"):
|
||||
spoiler_handle.write(f'Mysterious Warp Beacon unlock: {self.precalculated_weights.time_key_unlock}\n')
|
||||
else:
|
||||
spoiler_handle.write(f'Twin Pyramid Keys unlock: {self.precalculated_weights.pyramid_keys_unlock}\n')
|
||||
|
||||
if is_option_enabled(self.multiworld, self.player, "RisingTides"):
|
||||
flooded_areas: List[str] = []
|
||||
|
||||
if self.precalculated_weights.flood_basement:
|
||||
if self.precalculated_weights.flood_basement_high:
|
||||
flooded_areas.append("Castle Basement")
|
||||
else:
|
||||
flooded_areas.append("Castle Basement (Savepoint available)")
|
||||
if self.precalculated_weights.flood_xarion:
|
||||
flooded_areas.append("Xarion (boss)")
|
||||
if self.precalculated_weights.flood_maw:
|
||||
flooded_areas.append("Maw (caves + boss)")
|
||||
if self.precalculated_weights.flood_pyramid_shaft:
|
||||
flooded_areas.append("Ancient Pyramid Shaft")
|
||||
if self.precalculated_weights.flood_pyramid_back:
|
||||
flooded_areas.append("Sandman\\Nightmare (boss)")
|
||||
if self.precalculated_weights.flood_moat:
|
||||
flooded_areas.append("Castle Ramparts Moat")
|
||||
if self.precalculated_weights.flood_courtyard:
|
||||
flooded_areas.append("Castle Courtyard")
|
||||
if self.precalculated_weights.flood_lake_desolation:
|
||||
flooded_areas.append("Lake Desolation")
|
||||
if self.precalculated_weights.dry_lake_serene:
|
||||
flooded_areas.append("Dry Lake Serene")
|
||||
|
||||
if len(flooded_areas) == 0:
|
||||
flooded_areas_string: str = "None"
|
||||
else:
|
||||
flooded_areas_string: str = ", ".join(flooded_areas)
|
||||
|
||||
spoiler_handle.write(f'Flooded Areas: {flooded_areas_string}\n')
|
||||
|
||||
|
||||
def get_excluded_items(self: TimespinnerWorld, world: MultiWorld, player: int) -> Set[str]:
|
||||
|
@ -127,6 +188,16 @@ def get_excluded_items(self: TimespinnerWorld, world: MultiWorld, player: int) -
|
|||
if is_option_enabled(world, player, "QuickSeed"):
|
||||
excluded_items.add('Talaria Attachment')
|
||||
|
||||
if is_option_enabled(world, player, "UnchainedKeys"):
|
||||
excluded_items.add('Twin Pyramid Key')
|
||||
|
||||
if not is_option_enabled(world, player, "EnterSandman"):
|
||||
excluded_items.add('Mysterious Warp Beacon')
|
||||
else:
|
||||
excluded_items.add('Timeworn Warp Beacon')
|
||||
excluded_items.add('Modern Warp Beacon')
|
||||
excluded_items.add('Mysterious Warp Beacon')
|
||||
|
||||
for item in world.precollected_items[player]:
|
||||
if item.name not in self.item_name_groups['UseItem']:
|
||||
excluded_items.add(item.name)
|
||||
|
@ -188,38 +259,18 @@ def fill_item_pool_with_dummy_items(self: TimespinnerWorld, world: MultiWorld, p
|
|||
pool.append(item)
|
||||
|
||||
|
||||
def place_first_progression_item(world: MultiWorld, player: int, excluded_items: Set[str], locked_locations: List[str]):
|
||||
for item in world.precollected_items[player]:
|
||||
if item.name in starter_progression_items:
|
||||
return
|
||||
|
||||
local_starter_progression_items = tuple(
|
||||
item for item in starter_progression_items if item not in world.non_local_items[player].value)
|
||||
non_excluded_starter_progression_locations = tuple(
|
||||
location for location in starter_progression_locations if location not in world.exclude_locations[player].value)
|
||||
|
||||
if not local_starter_progression_items or not non_excluded_starter_progression_locations:
|
||||
return
|
||||
|
||||
progression_item = world.random.choice(local_starter_progression_items)
|
||||
location = world.random.choice(non_excluded_starter_progression_locations)
|
||||
|
||||
excluded_items.add(progression_item)
|
||||
locked_locations.append(location)
|
||||
|
||||
item = create_item_with_correct_settings(world, player, progression_item)
|
||||
|
||||
world.get_location(location, player).place_locked_item(item)
|
||||
|
||||
|
||||
def create_item_with_correct_settings(world: MultiWorld, player: int, name: str) -> Item:
|
||||
data = item_table[name]
|
||||
|
||||
if data.useful:
|
||||
classification = ItemClassification.useful
|
||||
elif data.progression:
|
||||
classification = ItemClassification.progression
|
||||
elif data.trap:
|
||||
classification = ItemClassification.trap
|
||||
else:
|
||||
classification = ItemClassification.filler
|
||||
|
||||
item = Item(name, classification, data.code, player)
|
||||
|
||||
if not item.advancement:
|
||||
|
@ -231,6 +282,9 @@ def create_item_with_correct_settings(world: MultiWorld, player: int, name: str)
|
|||
item.classification = ItemClassification.filler
|
||||
elif (name == 'Kobo' or name == 'Merchant Crow') and not is_option_enabled(world, player, "GyreArchives"):
|
||||
item.classification = ItemClassification.filler
|
||||
elif name in {"Timeworn Warp Beacon", "Modern Warp Beacon", "Mysterious Warp Beacon"} \
|
||||
and not is_option_enabled(world, player, "UnchainedKeys"):
|
||||
item.classification = ItemClassification.filler
|
||||
|
||||
return item
|
||||
|
||||
|
|
|
@ -28,9 +28,7 @@ certain items to your own world.
|
|||
|
||||
## What does another world's item look like in Timespinner?
|
||||
|
||||
Items belonging to other worlds are represented by the vanilla item Elemental
|
||||
Beads ([Elemental Beads Wiki Page](https://timespinnerwiki.com/Use_Items)), Elemental Beads have no use in the
|
||||
randomizer.
|
||||
Items belonging to other worlds are represented by the four orbs icon from the [Orb Collectors (Wiki Page)](https://timespinnerwiki.com/Achievements) achievement.
|
||||
|
||||
## When the player receives an item, what happens?
|
||||
|
||||
|
|
Loading…
Reference in New Issue