mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-04-15 00:53:30 -07:00
Merge branch 'ArchipelagoMW:main' into main
This commit is contained in:
5
.coveragerc
Normal file
5
.coveragerc
Normal file
@@ -0,0 +1,5 @@
|
||||
[report]
|
||||
exclude_lines =
|
||||
pragma: no cover
|
||||
if TYPE_CHECKING:
|
||||
if typing.TYPE_CHECKING:
|
||||
2
.github/workflows/analyze-modified-files.yml
vendored
2
.github/workflows/analyze-modified-files.yml
vendored
@@ -71,7 +71,7 @@ jobs:
|
||||
continue-on-error: true
|
||||
if: env.diff != '' && matrix.task == 'flake8'
|
||||
run: |
|
||||
flake8 --count --max-complexity=10 --max-doc-length=120 --max-line-length=120 --statistics ${{ env.diff }}
|
||||
flake8 --count --max-complexity=14 --max-doc-length=120 --max-line-length=120 --statistics ${{ env.diff }}
|
||||
|
||||
- name: "mypy: Type check modified files"
|
||||
continue-on-error: true
|
||||
|
||||
@@ -1056,9 +1056,6 @@ class Location:
|
||||
|
||||
@property
|
||||
def hint_text(self) -> str:
|
||||
hint_text = getattr(self, "_hint_text", None)
|
||||
if hint_text:
|
||||
return hint_text
|
||||
return "at " + self.name.replace("_", " ").replace("-", " ")
|
||||
|
||||
|
||||
|
||||
@@ -26,6 +26,13 @@ class ALttPLocation(Location):
|
||||
self.player_address = player_address
|
||||
self._hint_text = hint_text
|
||||
|
||||
@property
|
||||
def hint_text(self) -> str:
|
||||
hint_text = getattr(self, "_hint_text", None)
|
||||
if hint_text:
|
||||
return hint_text
|
||||
return "at " + self.name.replace("_", " ").replace("-", " ")
|
||||
|
||||
|
||||
class ALttPItem(Item):
|
||||
game: str = "A Link to the Past"
|
||||
|
||||
@@ -7,16 +7,25 @@ from ..AutoWorld import WebWorld, World
|
||||
class Bk_SudokuWebWorld(WebWorld):
|
||||
options_page = "games/Sudoku/info/en"
|
||||
theme = 'partyTime'
|
||||
tutorials = [
|
||||
Tutorial(
|
||||
tutorial_name='Setup Guide',
|
||||
description='A guide to playing BK Sudoku',
|
||||
language='English',
|
||||
file_name='setup_en.md',
|
||||
link='setup/en',
|
||||
authors=['Jarno']
|
||||
)
|
||||
]
|
||||
|
||||
setup_en = Tutorial(
|
||||
tutorial_name='Setup Guide',
|
||||
description='A guide to playing BK Sudoku',
|
||||
language='English',
|
||||
file_name='setup_en.md',
|
||||
link='setup/en',
|
||||
authors=['Jarno']
|
||||
)
|
||||
setup_de = Tutorial(
|
||||
tutorial_name='Setup Anleitung',
|
||||
description='Eine Anleitung um BK-Sudoku zu spielen',
|
||||
language='Deutsch',
|
||||
file_name='setup_de.md',
|
||||
link='setup/de',
|
||||
authors=['Held_der_Zeit']
|
||||
)
|
||||
|
||||
tutorials = [setup_en, setup_de]
|
||||
|
||||
|
||||
class Bk_SudokuWorld(World):
|
||||
|
||||
21
worlds/bk_sudoku/docs/de_Sudoku.md
Normal file
21
worlds/bk_sudoku/docs/de_Sudoku.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# BK-Sudoku
|
||||
|
||||
## Was ist das für ein Spiel?
|
||||
|
||||
BK-Sudoku ist kein typisches Archipelago-Spiel; stattdessen ist es ein gewöhnlicher Sudoku-Client der sich zu jeder
|
||||
beliebigen Multiworld verbinden kann. Einmal verbunden kannst du ein 9x9 Sudoku spielen um einen zufälligen Hinweis
|
||||
für dein Spiel zu erhalten. Es ist zwar langsam, aber es gibt dir etwas zu tun, solltest du mal nicht in der Lage sein
|
||||
weitere „Checks” zu erreichen.
|
||||
(Wer mag kann auch einfach so Sudoku spielen. Man muss nicht mit einer Multiworld verbunden sein, um ein Sudoku zu
|
||||
spielen/generieren.)
|
||||
|
||||
## Wie werden Hinweise freigeschalten?
|
||||
|
||||
Nach dem Lösen eines Sudokus wird für den verbundenen Slot ein zufällig ausgewählter Hinweis freigegeben, für einen
|
||||
Gegenstand der noch nicht gefunden wurde.
|
||||
|
||||
## Wo ist die Seite für die Einstellungen?
|
||||
|
||||
Es gibt keine Seite für die Einstellungen. Dieses Spiel kann nicht in deinen YAML-Dateien benutzt werden. Stattdessen
|
||||
kann sich der Client mit einem beliebigen Slot einer Multiworld verbinden. In dem Client selbst kann aber der
|
||||
Schwierigkeitsgrad des Sudoku ausgewählt werden.
|
||||
27
worlds/bk_sudoku/docs/setup_de.md
Normal file
27
worlds/bk_sudoku/docs/setup_de.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# BK-Sudoku Setup Anleitung
|
||||
|
||||
## Benötigte Software
|
||||
- [Bk-Sudoku](https://github.com/Jarno458/sudoku)
|
||||
- Windows 8 oder höher
|
||||
|
||||
## Generelles Konzept
|
||||
|
||||
Dies ist ein Client, der sich mit jedem beliebigen Slot einer Multiworld verbinden kann. Er lässt dich ein (9x9) Sudoku
|
||||
spielen, um zufällige Hinweise für den verbundenen Slot freizuschalten.
|
||||
|
||||
Aufgrund des Fakts, dass der Sudoku-Client sich zu jedem beliebigen Slot verbinden kann, ist es daher nicht notwendig
|
||||
eine YAML für dieses Spiel zu generieren, da es keinen neuen Slot zur Multiworld-Session hinzufügt.
|
||||
|
||||
## Installationsprozess
|
||||
|
||||
Gehe zu der aktuellsten (latest) Veröffentlichung der [BK-Sudoku Releases](https://github.com/Jarno458/sudoku/releases).
|
||||
Downloade und extrahiere/entpacke die `Bk_Sudoku.zip`-Datei.
|
||||
|
||||
## Verbinden mit einer Multiworld
|
||||
|
||||
1. Starte `Bk_Sudoku.exe`
|
||||
2. Trage den Namen des Slots ein, mit dem du dich verbinden möchtest
|
||||
3. Trage die Server-URL und den Port ein
|
||||
4. Drücke auf Verbinden (connect)
|
||||
5. Wähle deinen Schwierigkeitsgrad
|
||||
6. Versuche das Sudoku zu Lösen
|
||||
@@ -11,16 +11,26 @@ from .Rules import get_button_rule
|
||||
|
||||
class CliqueWebWorld(WebWorld):
|
||||
theme = "partyTime"
|
||||
tutorials = [
|
||||
Tutorial(
|
||||
tutorial_name="Start Guide",
|
||||
description="A guide to playing Clique.",
|
||||
language="English",
|
||||
file_name="guide_en.md",
|
||||
link="guide/en",
|
||||
authors=["Phar"]
|
||||
)
|
||||
]
|
||||
|
||||
setup_en = Tutorial(
|
||||
tutorial_name="Start Guide",
|
||||
description="A guide to playing Clique.",
|
||||
language="English",
|
||||
file_name="guide_en.md",
|
||||
link="guide/en",
|
||||
authors=["Phar"]
|
||||
)
|
||||
|
||||
setup_de = Tutorial(
|
||||
tutorial_name="Anleitung zum Anfangen",
|
||||
description="Eine Anleitung um Clique zu spielen.",
|
||||
language="Deutsch",
|
||||
file_name="guide_de.md",
|
||||
link="guide/de",
|
||||
authors=["Held_der_Zeit"]
|
||||
)
|
||||
|
||||
tutorials = [setup_en, setup_de]
|
||||
|
||||
|
||||
class CliqueWorld(World):
|
||||
|
||||
18
worlds/clique/docs/de_Clique.md
Normal file
18
worlds/clique/docs/de_Clique.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# Clique
|
||||
|
||||
## Was ist das für ein Spiel?
|
||||
|
||||
~~Clique ist ein psychologisches Überlebens-Horror Spiel, in dem der Spieler der Versuchung wiederstehen muss große~~
|
||||
~~(rote) Knöpfe zu drücken.~~
|
||||
|
||||
Clique ist ein scherzhaftes Spiel, welches für Archipelago im März 2023 entwickelt wurde, um zu zeigen, wie einfach
|
||||
es sein kann eine Welt für Archipelago zu entwicklen. Das Ziel des Spiels ist es den großen (standardmäßig) roten
|
||||
Knopf zu drücken. Wenn ein Spieler auf dem `hard_mode` (schwieriger Modus) spielt, muss dieser warten bis jemand
|
||||
anderes in der Multiworld den Knopf aktiviert, damit er gedrückt werden kann.
|
||||
|
||||
Clique kann auf den meisten modernen, HTML5-fähigen Browsern gespielt werden.
|
||||
|
||||
## Wo ist die Seite für die Einstellungen?
|
||||
|
||||
Die [Seite für die Spielereinstellungen dieses Spiels](../player-options) enthält alle Optionen die man benötigt um
|
||||
eine YAML-Datei zu konfigurieren und zu exportieren.
|
||||
25
worlds/clique/docs/guide_de.md
Normal file
25
worlds/clique/docs/guide_de.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# Clique Anleitung
|
||||
|
||||
Nachdem dein Seed generiert wurde, gehe auf die Website von [Clique dem Spiel](http://clique.pharware.com/) und gib
|
||||
Server-Daten, deinen Slot-Namen und ein Passwort (falls vorhanden) ein. Klicke dann auf "Connect" (Verbinden).
|
||||
|
||||
Wenn du auf "Einfach" spielst, kannst du unbedenklich den Knopf drücken und deine "Befriedigung" erhalten.
|
||||
|
||||
Wenn du auf "Schwer" spielst, ist es sehr wahrscheinlich, dass du warten musst bevor du dein Ziel erreichen kannst.
|
||||
Glücklicherweise läuft Click auf den meißten großen Browsern, die HTML5 unterstützen. Das heißt du kannst Clique auf
|
||||
deinem Handy starten und produktiv sein während du wartest!
|
||||
|
||||
Falls du einige Ideen brauchst was du tun kannst, während du wartest bis der Knopf aktiviert wurde, versuche
|
||||
(mindestens) eins der Folgenden:
|
||||
|
||||
- Dein Zimmer aufräumen.
|
||||
- Die Wäsche machen.
|
||||
- Etwas Essen von einem X-Belieben Fast Food Restaruant holen.
|
||||
- Das tägliche Wordle machen.
|
||||
- ~~Deine Seele an **Phar** verkaufen.~~
|
||||
- Deine Hausaufgaben erledigen.
|
||||
- Deine Post abholen.
|
||||
|
||||
|
||||
~~Solltest du auf irgendwelche Probleme in diesem Spiel stoßen, solltest du keinesfalls nicht **thephar** auf~~
|
||||
~~Discord kontaktieren. *zwinker* *zwinker*~~
|
||||
@@ -821,7 +821,8 @@ class KH2Context(CommonContext):
|
||||
|
||||
def finishedGame(ctx: KH2Context, message):
|
||||
if ctx.kh2slotdata['FinalXemnas'] == 1:
|
||||
if not ctx.final_xemnas and ctx.kh2_loc_name_to_id[LocationName.FinalXemnas] in ctx.locations_checked:
|
||||
if not ctx.final_xemnas and ctx.kh2_read_byte(ctx.Save + all_world_locations[LocationName.FinalXemnas].addrObtained) \
|
||||
& 0x1 << all_world_locations[LocationName.FinalXemnas].bitIndex > 0:
|
||||
ctx.final_xemnas = True
|
||||
# three proofs
|
||||
if ctx.kh2slotdata['Goal'] == 0:
|
||||
|
||||
@@ -2,22 +2,7 @@ import typing
|
||||
|
||||
from BaseClasses import Item
|
||||
from .Names import ItemName
|
||||
|
||||
|
||||
class KH2Item(Item):
|
||||
game: str = "Kingdom Hearts 2"
|
||||
|
||||
|
||||
class ItemData(typing.NamedTuple):
|
||||
quantity: int = 0
|
||||
kh2id: int = 0
|
||||
# Save+ mem addr
|
||||
memaddr: int = 0
|
||||
# some items have bitmasks. if bitmask>0 bitor to give item else
|
||||
bitmask: int = 0
|
||||
# if ability then
|
||||
ability: bool = False
|
||||
|
||||
from .Subclasses import ItemData
|
||||
|
||||
# 0x130000
|
||||
Reports_Table = {
|
||||
@@ -209,7 +194,7 @@ Armor_Table = {
|
||||
ItemName.GrandRibbon: ItemData(1, 157, 0x35D4),
|
||||
}
|
||||
Usefull_Table = {
|
||||
ItemName.MickeyMunnyPouch: ItemData(1, 535, 0x3695), # 5000 munny per
|
||||
ItemName.MickeyMunnyPouch: ItemData(1, 535, 0x3695), # 5000 munny per
|
||||
ItemName.OletteMunnyPouch: ItemData(2, 362, 0x363C), # 2500 munny per
|
||||
ItemName.HadesCupTrophy: ItemData(1, 537, 0x3696),
|
||||
ItemName.UnknownDisk: ItemData(1, 462, 0x365F),
|
||||
@@ -349,7 +334,7 @@ GoofyAbility_Table = {
|
||||
|
||||
Wincon_Table = {
|
||||
ItemName.LuckyEmblem: ItemData(kh2id=367, memaddr=0x3641), # letter item
|
||||
ItemName.Victory: ItemData(kh2id=263, memaddr=0x111),
|
||||
# ItemName.Victory: ItemData(kh2id=263, memaddr=0x111),
|
||||
ItemName.Bounty: ItemData(kh2id=461, memaddr=0x365E), # Dummy 14
|
||||
# ItemName.UniversalKey:ItemData(,365,0x363F,0)#Tournament Poster
|
||||
}
|
||||
|
||||
@@ -1,19 +1,9 @@
|
||||
import typing
|
||||
|
||||
from BaseClasses import Location
|
||||
from .Names import LocationName, ItemName
|
||||
|
||||
|
||||
class KH2Location(Location):
|
||||
game: str = "Kingdom Hearts 2"
|
||||
|
||||
|
||||
class LocationData(typing.NamedTuple):
|
||||
locid: int
|
||||
yml: str
|
||||
charName: str = "Sora"
|
||||
charNumber: int = 1
|
||||
|
||||
from .Names import LocationName, ItemName, RegionName
|
||||
from .Subclasses import LocationData
|
||||
from .Regions import KH2REGIONS
|
||||
|
||||
# data's addrcheck sys3 addr obtained roomid bit index is eventid
|
||||
LoD_Checks = {
|
||||
@@ -541,7 +531,7 @@ TWTNW_Checks = {
|
||||
LocationName.Xemnas1: LocationData(26, "Double Get Bonus"),
|
||||
LocationName.Xemnas1GetBonus: LocationData(26, "Second Get Bonus"),
|
||||
LocationName.Xemnas1SecretAnsemReport13: LocationData(537, "Chest"),
|
||||
LocationName.FinalXemnas: LocationData(71, "Get Bonus"),
|
||||
# LocationName.FinalXemnas: LocationData(71, "Get Bonus"),
|
||||
LocationName.XemnasDataPowerBoost: LocationData(554, "Chest"),
|
||||
}
|
||||
|
||||
@@ -806,74 +796,75 @@ Atlantica_Checks = {
|
||||
}
|
||||
|
||||
event_location_to_item = {
|
||||
LocationName.HostileProgramEventLocation: ItemName.HostileProgramEvent,
|
||||
LocationName.McpEventLocation: ItemName.McpEvent,
|
||||
LocationName.HostileProgramEventLocation: ItemName.HostileProgramEvent,
|
||||
LocationName.McpEventLocation: ItemName.McpEvent,
|
||||
# LocationName.ASLarxeneEventLocation: ItemName.ASLarxeneEvent,
|
||||
LocationName.DataLarxeneEventLocation: ItemName.DataLarxeneEvent,
|
||||
LocationName.BarbosaEventLocation: ItemName.BarbosaEvent,
|
||||
LocationName.GrimReaper1EventLocation: ItemName.GrimReaper1Event,
|
||||
LocationName.GrimReaper2EventLocation: ItemName.GrimReaper2Event,
|
||||
LocationName.DataLuxordEventLocation: ItemName.DataLuxordEvent,
|
||||
LocationName.DataAxelEventLocation: ItemName.DataAxelEvent,
|
||||
LocationName.CerberusEventLocation: ItemName.CerberusEvent,
|
||||
LocationName.OlympusPeteEventLocation: ItemName.OlympusPeteEvent,
|
||||
LocationName.HydraEventLocation: ItemName.HydraEvent,
|
||||
LocationName.DataLarxeneEventLocation: ItemName.DataLarxeneEvent,
|
||||
LocationName.BarbosaEventLocation: ItemName.BarbosaEvent,
|
||||
LocationName.GrimReaper1EventLocation: ItemName.GrimReaper1Event,
|
||||
LocationName.GrimReaper2EventLocation: ItemName.GrimReaper2Event,
|
||||
LocationName.DataLuxordEventLocation: ItemName.DataLuxordEvent,
|
||||
LocationName.DataAxelEventLocation: ItemName.DataAxelEvent,
|
||||
LocationName.CerberusEventLocation: ItemName.CerberusEvent,
|
||||
LocationName.OlympusPeteEventLocation: ItemName.OlympusPeteEvent,
|
||||
LocationName.HydraEventLocation: ItemName.HydraEvent,
|
||||
LocationName.OcPainAndPanicCupEventLocation: ItemName.OcPainAndPanicCupEvent,
|
||||
LocationName.OcCerberusCupEventLocation: ItemName.OcCerberusCupEvent,
|
||||
LocationName.HadesEventLocation: ItemName.HadesEvent,
|
||||
LocationName.OcCerberusCupEventLocation: ItemName.OcCerberusCupEvent,
|
||||
LocationName.HadesEventLocation: ItemName.HadesEvent,
|
||||
# LocationName.ASZexionEventLocation: ItemName.ASZexionEvent,
|
||||
LocationName.DataZexionEventLocation: ItemName.DataZexionEvent,
|
||||
LocationName.Oc2TitanCupEventLocation: ItemName.Oc2TitanCupEvent,
|
||||
LocationName.Oc2GofCupEventLocation: ItemName.Oc2GofCupEvent,
|
||||
LocationName.DataZexionEventLocation: ItemName.DataZexionEvent,
|
||||
LocationName.Oc2TitanCupEventLocation: ItemName.Oc2TitanCupEvent,
|
||||
LocationName.Oc2GofCupEventLocation: ItemName.Oc2GofCupEvent,
|
||||
# LocationName.Oc2CupsEventLocation: ItemName.Oc2CupsEventLocation,
|
||||
LocationName.HadesCupEventLocations: ItemName.HadesCupEvents,
|
||||
LocationName.PrisonKeeperEventLocation: ItemName.PrisonKeeperEvent,
|
||||
LocationName.OogieBoogieEventLocation: ItemName.OogieBoogieEvent,
|
||||
LocationName.ExperimentEventLocation: ItemName.ExperimentEvent,
|
||||
LocationName.HadesCupEventLocations: ItemName.HadesCupEvents,
|
||||
LocationName.PrisonKeeperEventLocation: ItemName.PrisonKeeperEvent,
|
||||
LocationName.OogieBoogieEventLocation: ItemName.OogieBoogieEvent,
|
||||
LocationName.ExperimentEventLocation: ItemName.ExperimentEvent,
|
||||
# LocationName.ASVexenEventLocation: ItemName.ASVexenEvent,
|
||||
LocationName.DataVexenEventLocation: ItemName.DataVexenEvent,
|
||||
LocationName.ShanYuEventLocation: ItemName.ShanYuEvent,
|
||||
LocationName.AnsemRikuEventLocation: ItemName.AnsemRikuEvent,
|
||||
LocationName.StormRiderEventLocation: ItemName.StormRiderEvent,
|
||||
LocationName.DataXigbarEventLocation: ItemName.DataXigbarEvent,
|
||||
LocationName.RoxasEventLocation: ItemName.RoxasEvent,
|
||||
LocationName.XigbarEventLocation: ItemName.XigbarEvent,
|
||||
LocationName.LuxordEventLocation: ItemName.LuxordEvent,
|
||||
LocationName.SaixEventLocation: ItemName.SaixEvent,
|
||||
LocationName.XemnasEventLocation: ItemName.XemnasEvent,
|
||||
LocationName.ArmoredXemnasEventLocation: ItemName.ArmoredXemnasEvent,
|
||||
LocationName.ArmoredXemnas2EventLocation: ItemName.ArmoredXemnas2Event,
|
||||
LocationName.DataVexenEventLocation: ItemName.DataVexenEvent,
|
||||
LocationName.ShanYuEventLocation: ItemName.ShanYuEvent,
|
||||
LocationName.AnsemRikuEventLocation: ItemName.AnsemRikuEvent,
|
||||
LocationName.StormRiderEventLocation: ItemName.StormRiderEvent,
|
||||
LocationName.DataXigbarEventLocation: ItemName.DataXigbarEvent,
|
||||
LocationName.RoxasEventLocation: ItemName.RoxasEvent,
|
||||
LocationName.XigbarEventLocation: ItemName.XigbarEvent,
|
||||
LocationName.LuxordEventLocation: ItemName.LuxordEvent,
|
||||
LocationName.SaixEventLocation: ItemName.SaixEvent,
|
||||
LocationName.XemnasEventLocation: ItemName.XemnasEvent,
|
||||
LocationName.ArmoredXemnasEventLocation: ItemName.ArmoredXemnasEvent,
|
||||
LocationName.ArmoredXemnas2EventLocation: ItemName.ArmoredXemnas2Event,
|
||||
# LocationName.FinalXemnasEventLocation: ItemName.FinalXemnasEvent,
|
||||
LocationName.DataXemnasEventLocation: ItemName.DataXemnasEvent,
|
||||
LocationName.ThresholderEventLocation: ItemName.ThresholderEvent,
|
||||
LocationName.BeastEventLocation: ItemName.BeastEvent,
|
||||
LocationName.DarkThornEventLocation: ItemName.DarkThornEvent,
|
||||
LocationName.XaldinEventLocation: ItemName.XaldinEvent,
|
||||
LocationName.DataXaldinEventLocation: ItemName.DataXaldinEvent,
|
||||
LocationName.TwinLordsEventLocation: ItemName.TwinLordsEvent,
|
||||
LocationName.GenieJafarEventLocation: ItemName.GenieJafarEvent,
|
||||
LocationName.DataXemnasEventLocation: ItemName.DataXemnasEvent,
|
||||
LocationName.ThresholderEventLocation: ItemName.ThresholderEvent,
|
||||
LocationName.BeastEventLocation: ItemName.BeastEvent,
|
||||
LocationName.DarkThornEventLocation: ItemName.DarkThornEvent,
|
||||
LocationName.XaldinEventLocation: ItemName.XaldinEvent,
|
||||
LocationName.DataXaldinEventLocation: ItemName.DataXaldinEvent,
|
||||
LocationName.TwinLordsEventLocation: ItemName.TwinLordsEvent,
|
||||
LocationName.GenieJafarEventLocation: ItemName.GenieJafarEvent,
|
||||
# LocationName.ASLexaeusEventLocation: ItemName.ASLexaeusEvent,
|
||||
LocationName.DataLexaeusEventLocation: ItemName.DataLexaeusEvent,
|
||||
LocationName.ScarEventLocation: ItemName.ScarEvent,
|
||||
LocationName.GroundShakerEventLocation: ItemName.GroundShakerEvent,
|
||||
LocationName.DataSaixEventLocation: ItemName.DataSaixEvent,
|
||||
LocationName.HBDemyxEventLocation: ItemName.HBDemyxEvent,
|
||||
LocationName.ThousandHeartlessEventLocation: ItemName.ThousandHeartlessEvent,
|
||||
LocationName.Mushroom13EventLocation: ItemName.Mushroom13Event,
|
||||
LocationName.SephiEventLocation: ItemName.SephiEvent,
|
||||
LocationName.DataDemyxEventLocation: ItemName.DataDemyxEvent,
|
||||
LocationName.CorFirstFightEventLocation: ItemName.CorFirstFightEvent,
|
||||
LocationName.CorSecondFightEventLocation: ItemName.CorSecondFightEvent,
|
||||
LocationName.TransportEventLocation: ItemName.TransportEvent,
|
||||
LocationName.OldPeteEventLocation: ItemName.OldPeteEvent,
|
||||
LocationName.FuturePeteEventLocation: ItemName.FuturePeteEvent,
|
||||
LocationName.DataLexaeusEventLocation: ItemName.DataLexaeusEvent,
|
||||
LocationName.ScarEventLocation: ItemName.ScarEvent,
|
||||
LocationName.GroundShakerEventLocation: ItemName.GroundShakerEvent,
|
||||
LocationName.DataSaixEventLocation: ItemName.DataSaixEvent,
|
||||
LocationName.HBDemyxEventLocation: ItemName.HBDemyxEvent,
|
||||
LocationName.ThousandHeartlessEventLocation: ItemName.ThousandHeartlessEvent,
|
||||
LocationName.Mushroom13EventLocation: ItemName.Mushroom13Event,
|
||||
LocationName.SephiEventLocation: ItemName.SephiEvent,
|
||||
LocationName.DataDemyxEventLocation: ItemName.DataDemyxEvent,
|
||||
LocationName.CorFirstFightEventLocation: ItemName.CorFirstFightEvent,
|
||||
LocationName.CorSecondFightEventLocation: ItemName.CorSecondFightEvent,
|
||||
LocationName.TransportEventLocation: ItemName.TransportEvent,
|
||||
LocationName.OldPeteEventLocation: ItemName.OldPeteEvent,
|
||||
LocationName.FuturePeteEventLocation: ItemName.FuturePeteEvent,
|
||||
# LocationName.ASMarluxiaEventLocation: ItemName.ASMarluxiaEvent,
|
||||
LocationName.DataMarluxiaEventLocation: ItemName.DataMarluxiaEvent,
|
||||
LocationName.TerraEventLocation: ItemName.TerraEvent,
|
||||
LocationName.TwilightThornEventLocation: ItemName.TwilightThornEvent,
|
||||
LocationName.Axel1EventLocation: ItemName.Axel1Event,
|
||||
LocationName.Axel2EventLocation: ItemName.Axel2Event,
|
||||
LocationName.DataRoxasEventLocation: ItemName.DataRoxasEvent,
|
||||
LocationName.DataMarluxiaEventLocation: ItemName.DataMarluxiaEvent,
|
||||
LocationName.TerraEventLocation: ItemName.TerraEvent,
|
||||
LocationName.TwilightThornEventLocation: ItemName.TwilightThornEvent,
|
||||
LocationName.Axel1EventLocation: ItemName.Axel1Event,
|
||||
LocationName.Axel2EventLocation: ItemName.Axel2Event,
|
||||
LocationName.DataRoxasEventLocation: ItemName.DataRoxasEvent,
|
||||
LocationName.FinalXemnasEventLocation: ItemName.Victory,
|
||||
}
|
||||
all_weapon_slot = {
|
||||
LocationName.FAKESlot,
|
||||
@@ -1361,3 +1352,9 @@ exclusion_table = {
|
||||
location for location, data in all_locations.items() if location not in event_location_to_item.keys() and location not in popups_set and location != LocationName.StationofSerenityPotion and data.yml == "Chest"
|
||||
}
|
||||
}
|
||||
|
||||
location_groups: typing.Dict[str, list]
|
||||
location_groups = {
|
||||
Region_Name: [loc for loc in Region_Locs if "Event" not in loc]
|
||||
for Region_Name, Region_Locs in KH2REGIONS.items() if Region_Locs
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import typing
|
||||
|
||||
from BaseClasses import MultiWorld, Region
|
||||
from . import Locations
|
||||
|
||||
from .Locations import KH2Location, event_location_to_item
|
||||
from . import LocationName, RegionName, Events_Table
|
||||
from .Subclasses import KH2Location
|
||||
from .Names import LocationName, RegionName
|
||||
from .Items import Events_Table
|
||||
|
||||
KH2REGIONS: typing.Dict[str, typing.List[str]] = {
|
||||
"Menu": [],
|
||||
@@ -788,7 +790,7 @@ KH2REGIONS: typing.Dict[str, typing.List[str]] = {
|
||||
LocationName.ArmoredXemnas2EventLocation
|
||||
],
|
||||
RegionName.FinalXemnas: [
|
||||
LocationName.FinalXemnas
|
||||
LocationName.FinalXemnasEventLocation
|
||||
],
|
||||
RegionName.DataXemnas: [
|
||||
LocationName.XemnasDataPowerBoost,
|
||||
@@ -1020,7 +1022,8 @@ def create_regions(self):
|
||||
multiworld.regions += [create_region(multiworld, player, active_locations, region, locations) for region, locations in
|
||||
KH2REGIONS.items()]
|
||||
# fill the event locations with events
|
||||
for location, item in event_location_to_item.items():
|
||||
|
||||
for location, item in Locations.event_location_to_item.items():
|
||||
multiworld.get_location(location, player).place_locked_item(
|
||||
multiworld.worlds[player].create_event_item(item))
|
||||
|
||||
|
||||
@@ -83,6 +83,8 @@ class KH2Rules:
|
||||
return state.has(ItemName.TornPages, self.player, amount)
|
||||
|
||||
def level_locking_unlock(self, state: CollectionState, amount):
|
||||
if self.world.options.Promise_Charm and state.has(ItemName.PromiseCharm, self.player):
|
||||
return True
|
||||
return amount <= sum([state.count(item_name, self.player) for item_name in visit_locking_dict["2VisitLocking"]])
|
||||
|
||||
def summon_levels_unlocked(self, state: CollectionState, amount) -> bool:
|
||||
@@ -270,7 +272,7 @@ class KH2WorldRules(KH2Rules):
|
||||
add_item_rule(location, lambda item: item.player == self.player and item.name in SupportAbility_Table.keys())
|
||||
|
||||
def set_kh2_goal(self):
|
||||
final_xemnas_location = self.multiworld.get_location(LocationName.FinalXemnas, self.player)
|
||||
final_xemnas_location = self.multiworld.get_location(LocationName.FinalXemnasEventLocation, self.player)
|
||||
if self.multiworld.Goal[self.player] == "three_proofs":
|
||||
final_xemnas_location.access_rule = lambda state: self.kh2_has_all(three_proofs, state)
|
||||
if self.multiworld.FinalXemnas[self.player]:
|
||||
|
||||
29
worlds/kh2/Subclasses.py
Normal file
29
worlds/kh2/Subclasses.py
Normal file
@@ -0,0 +1,29 @@
|
||||
import typing
|
||||
|
||||
from BaseClasses import Location, Item
|
||||
|
||||
|
||||
class KH2Location(Location):
|
||||
game: str = "Kingdom Hearts 2"
|
||||
|
||||
|
||||
class LocationData(typing.NamedTuple):
|
||||
locid: int
|
||||
yml: str
|
||||
charName: str = "Sora"
|
||||
charNumber: int = 1
|
||||
|
||||
|
||||
class KH2Item(Item):
|
||||
game: str = "Kingdom Hearts 2"
|
||||
|
||||
|
||||
class ItemData(typing.NamedTuple):
|
||||
quantity: int = 0
|
||||
kh2id: int = 0
|
||||
# Save+ mem addr
|
||||
memaddr: int = 0
|
||||
# some items have bitmasks. if bitmask>0 bitor to give item else
|
||||
bitmask: int = 0
|
||||
# if ability then
|
||||
ability: bool = False
|
||||
@@ -12,6 +12,7 @@ from .OpenKH import patch_kh2
|
||||
from .Options import KingdomHearts2Options
|
||||
from .Regions import create_regions, connect_regions
|
||||
from .Rules import *
|
||||
from .Subclasses import KH2Item
|
||||
|
||||
|
||||
def launch_client():
|
||||
@@ -49,7 +50,9 @@ class KH2World(World):
|
||||
for item_id, item in enumerate(item_dictionary_table.keys(), 0x130000)}
|
||||
location_name_to_id = {item: location
|
||||
for location, item in enumerate(all_locations.keys(), 0x130000)}
|
||||
|
||||
item_name_groups = item_groups
|
||||
location_name_groups = location_groups
|
||||
|
||||
visitlocking_dict: Dict[str, int]
|
||||
plando_locations: Dict[str, str]
|
||||
@@ -253,11 +256,8 @@ class KH2World(World):
|
||||
self.goofy_gen_early()
|
||||
self.keyblade_gen_early()
|
||||
|
||||
if self.multiworld.FinalXemnas[self.player]:
|
||||
self.plando_locations[LocationName.FinalXemnas] = ItemName.Victory
|
||||
else:
|
||||
self.plando_locations[LocationName.FinalXemnas] = self.create_filler().name
|
||||
self.total_locations -= 1
|
||||
# final xemnas isn't a location anymore
|
||||
# self.total_locations -= 1
|
||||
|
||||
if self.options.WeaponSlotStartHint:
|
||||
for location in all_weapon_slot:
|
||||
|
||||
@@ -2635,12 +2635,6 @@
|
||||
panels:
|
||||
- OBSTACLE
|
||||
The Colorful:
|
||||
# The set of required_doors in the achievement panel should prevent
|
||||
# generation from asking you to solve The Colorful before opening all of the
|
||||
# doors. Access from the roof is included so that the painting here could be
|
||||
# an entrance. The client will have to be hardcoded to not open the door to
|
||||
# the achievement until all of the doors are open, whether by solving the
|
||||
# panels or through receiving items.
|
||||
entrances:
|
||||
The Colorful (Gray):
|
||||
room: The Colorful (Gray)
|
||||
@@ -2651,31 +2645,53 @@
|
||||
id: Countdown Panels/Panel_colorful_colorful
|
||||
check: True
|
||||
tag: forbid
|
||||
required_door:
|
||||
required_panel:
|
||||
- room: The Colorful (White)
|
||||
door: Progress Door
|
||||
panel: BEGIN
|
||||
- room: The Colorful (Black)
|
||||
door: Progress Door
|
||||
panel: FOUND
|
||||
- room: The Colorful (Red)
|
||||
door: Progress Door
|
||||
panel: LOAF
|
||||
- room: The Colorful (Yellow)
|
||||
door: Progress Door
|
||||
panel: CREAM
|
||||
- room: The Colorful (Blue)
|
||||
door: Progress Door
|
||||
panel: SUN
|
||||
- room: The Colorful (Purple)
|
||||
door: Progress Door
|
||||
panel: SPOON
|
||||
- room: The Colorful (Orange)
|
||||
door: Progress Door
|
||||
panel: LETTERS
|
||||
- room: The Colorful (Green)
|
||||
door: Progress Door
|
||||
panel: WALLS
|
||||
- room: The Colorful (Brown)
|
||||
door: Progress Door
|
||||
panel: IRON
|
||||
- room: The Colorful (Gray)
|
||||
door: Progress Door
|
||||
panel: OBSTACLE
|
||||
achievement: The Colorful
|
||||
paintings:
|
||||
- id: arrows_painting_12
|
||||
orientation: north
|
||||
progression:
|
||||
Progressive Colorful:
|
||||
- room: The Colorful (White)
|
||||
door: Progress Door
|
||||
- room: The Colorful (Black)
|
||||
door: Progress Door
|
||||
- room: The Colorful (Red)
|
||||
door: Progress Door
|
||||
- room: The Colorful (Yellow)
|
||||
door: Progress Door
|
||||
- room: The Colorful (Blue)
|
||||
door: Progress Door
|
||||
- room: The Colorful (Purple)
|
||||
door: Progress Door
|
||||
- room: The Colorful (Orange)
|
||||
door: Progress Door
|
||||
- room: The Colorful (Green)
|
||||
door: Progress Door
|
||||
- room: The Colorful (Brown)
|
||||
door: Progress Door
|
||||
- room: The Colorful (Gray)
|
||||
door: Progress Door
|
||||
Welcome Back Area:
|
||||
entrances:
|
||||
Starting Room:
|
||||
@@ -4202,9 +4218,6 @@
|
||||
SIX:
|
||||
id: Backside Room/Panel_six_six_5
|
||||
tag: midwhite
|
||||
colors:
|
||||
- red
|
||||
- yellow
|
||||
hunt: True
|
||||
required_door:
|
||||
room: Number Hunt
|
||||
@@ -4280,9 +4293,6 @@
|
||||
SIX:
|
||||
id: Backside Room/Panel_six_six_6
|
||||
tag: midwhite
|
||||
colors:
|
||||
- red
|
||||
- yellow
|
||||
hunt: True
|
||||
required_door:
|
||||
room: Number Hunt
|
||||
|
||||
@@ -1452,3 +1452,4 @@ progression:
|
||||
Progressive Fearless: 444470
|
||||
Progressive Orange Tower: 444482
|
||||
Progressive Art Gallery: 444563
|
||||
Progressive Colorful: 444580
|
||||
|
||||
@@ -28,6 +28,10 @@ class ItemData(NamedTuple):
|
||||
# door shuffle is on and tower isn't progressive
|
||||
return world.options.shuffle_doors != ShuffleDoors.option_none \
|
||||
and not world.options.progressive_orange_tower
|
||||
elif self.mode == "the colorful":
|
||||
# complex door shuffle is on and colorful isn't progressive
|
||||
return world.options.shuffle_doors == ShuffleDoors.option_complex \
|
||||
and not world.options.progressive_colorful
|
||||
elif self.mode == "complex door":
|
||||
return world.options.shuffle_doors == ShuffleDoors.option_complex
|
||||
elif self.mode == "door group":
|
||||
@@ -70,6 +74,8 @@ def load_item_data():
|
||||
if room_name in PROGRESSION_BY_ROOM and door_name in PROGRESSION_BY_ROOM[room_name]:
|
||||
if room_name == "Orange Tower":
|
||||
door_mode = "orange tower"
|
||||
elif room_name == "The Colorful":
|
||||
door_mode = "the colorful"
|
||||
else:
|
||||
door_mode = "special"
|
||||
|
||||
|
||||
@@ -21,6 +21,13 @@ class ProgressiveOrangeTower(DefaultOnToggle):
|
||||
display_name = "Progressive Orange Tower"
|
||||
|
||||
|
||||
class ProgressiveColorful(DefaultOnToggle):
|
||||
"""When "Shuffle Doors" is on "complex", this setting governs the manner in which The Colorful opens up.
|
||||
If off, there is an item for each room of The Colorful, meaning that random rooms in the middle of the sequence can open up without giving you access to them.
|
||||
If on, there are ten progressive items, which open up the sequence from White forward."""
|
||||
display_name = "Progressive Colorful"
|
||||
|
||||
|
||||
class LocationChecks(Choice):
|
||||
"""On "normal", there will be a location check for each panel set that would ordinarily open a door, as well as for
|
||||
achievement panels and a small handful of other panels.
|
||||
@@ -117,6 +124,7 @@ class DeathLink(Toggle):
|
||||
class LingoOptions(PerGameCommonOptions):
|
||||
shuffle_doors: ShuffleDoors
|
||||
progressive_orange_tower: ProgressiveOrangeTower
|
||||
progressive_colorful: ProgressiveColorful
|
||||
location_checks: LocationChecks
|
||||
shuffle_colors: ShuffleColors
|
||||
shuffle_panels: ShufflePanels
|
||||
|
||||
@@ -83,7 +83,8 @@ class LingoPlayerLogic:
|
||||
|
||||
def handle_non_grouped_door(self, room_name: str, door_data: Door, world: "LingoWorld"):
|
||||
if room_name in PROGRESSION_BY_ROOM and door_data.name in PROGRESSION_BY_ROOM[room_name]:
|
||||
if room_name == "Orange Tower" and not world.options.progressive_orange_tower:
|
||||
if (room_name == "Orange Tower" and not world.options.progressive_orange_tower)\
|
||||
or (room_name == "The Colorful" and not world.options.progressive_colorful):
|
||||
self.set_door_item(room_name, door_data.name, door_data.item_name)
|
||||
else:
|
||||
progressive_item_name = PROGRESSION_BY_ROOM[room_name][door_data.name].item_name
|
||||
@@ -223,7 +224,7 @@ class LingoPlayerLogic:
|
||||
"kind of logic error.")
|
||||
|
||||
if door_shuffle != ShuffleDoors.option_none and location_classification != LocationClassification.insanity \
|
||||
and not early_color_hallways is False:
|
||||
and not early_color_hallways:
|
||||
# If shuffle doors is on, force a useful item onto the HI panel. This may not necessarily get you out of BK,
|
||||
# but the goal is to allow you to reach at least one more check. The non-painting ones are hardcoded right
|
||||
# now. We only allow the entrance to the Pilgrim Room if color shuffle is off, because otherwise there are
|
||||
|
||||
@@ -118,7 +118,16 @@ class OOTWeb(WebWorld):
|
||||
["TheLynk"]
|
||||
)
|
||||
|
||||
tutorials = [setup, setup_es, setup_fr]
|
||||
setup_de = Tutorial(
|
||||
setup.tutorial_name,
|
||||
setup.description,
|
||||
"Deutsch",
|
||||
"setup_de.md",
|
||||
"setup/de",
|
||||
["Held_der_Zeit"]
|
||||
)
|
||||
|
||||
tutorials = [setup, setup_es, setup_fr, setup_de]
|
||||
|
||||
|
||||
class OOTWorld(World):
|
||||
|
||||
BIN
worlds/oot/docs/MultiWorld-Room_oot.png
Normal file
BIN
worlds/oot/docs/MultiWorld-Room_oot.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 56 KiB |
41
worlds/oot/docs/de_Ocarina of Time.md
Normal file
41
worlds/oot/docs/de_Ocarina of Time.md
Normal file
@@ -0,0 +1,41 @@
|
||||
# The Legend of Zelda: Ocarina of Time
|
||||
|
||||
## Wo ist die Seite für die Einstellungen?
|
||||
|
||||
Die [Seite für die Spielereinstellungen dieses Spiels](../player-options) enthält alle Optionen die man benötigt um
|
||||
eine YAML-Datei zu konfigurieren und zu exportieren.
|
||||
|
||||
## Was macht der Randomizer in diesem Spiel?
|
||||
|
||||
Items, welche der Spieler für gewöhnlich im Verlauf des Spiels erhalten würde, wurden umhergemischt. Die Logik bleit
|
||||
bestehen, damit ist das Spiel immer durchspielbar. Doch weil die Items durch das ganze Spiel gemischt wurden, müssen
|
||||
manche Bereiche früher bescuht werden, als man es in Vanilla tun würde.
|
||||
Eine Liste von implementierter Logik, die unoffensichtlich erscheinen kann, kann
|
||||
[hier (Englisch)](https://wiki.ootrandomizer.com/index.php?title=Logic) gefunden werden.
|
||||
|
||||
## Welche Items und Bereiche werden gemischt?
|
||||
|
||||
Alle ausrüstbare und sammelbare Gegenstände, sowie Munition können gemischt werden. Und alle Bereiche, die einen
|
||||
dieser Items enthalten könnten, haben (sehr wahrscheinlich) ihren Inhalt verändert. Goldene Skulltulas können ebenfalls
|
||||
dazugezählt werden, je nach Wunsch des Spielers.
|
||||
|
||||
## Welche Items können in sich in der Welt eines anderen Spielers befinden?
|
||||
|
||||
Jedes dieser Items, die gemicht werden können, können in einer Multiworld auch in der Welt eines anderen Spielers
|
||||
fallen. Es ist jedoch möglich ausgewählte Items auf deine eigene Welt zu beschränken.
|
||||
|
||||
## Wie sieht ein Item einer anderen Welt in OoT aus?
|
||||
|
||||
Items, die zu einer anderen Welt gehören, werden repräsentiert durch Zelda's Brief.
|
||||
|
||||
## Was passiert, wenn der Spieler ein Item erhält?
|
||||
|
||||
Sobald der Spieler ein Item erhält, wird Link das Item über seinen Kopf halten und der ganzen Welt präsentieren.
|
||||
Gut für's Geschäft!
|
||||
|
||||
## Einzigartige Lokale Befehle
|
||||
|
||||
Die folgenden Befehle stehen nur im OoTClient, um mit Archipelago zu spielen, zur Verfügung:
|
||||
|
||||
- `/n64` Überprüffe den Verbindungsstatus deiner N64
|
||||
- `/deathlink` Schalte den "Deathlink" des Clients um. Überschreibt die zuvor konfigurierten Einstellungen.
|
||||
108
worlds/oot/docs/setup_de.md
Normal file
108
worlds/oot/docs/setup_de.md
Normal file
@@ -0,0 +1,108 @@
|
||||
# Setup Anleitung für Ocarina of Time: Archipelago Edition
|
||||
|
||||
## WICHTIG
|
||||
|
||||
Da wir BizHawk benutzen, gilt diese Anleitung nur für Windows und Linux.
|
||||
|
||||
## Benötigte Software
|
||||
|
||||
- BizHawk: [BizHawk Veröffentlichungen von TASVideos](https://tasvideos.org/BizHawk/ReleaseHistory)
|
||||
- Version 2.3.1 und später werden unterstützt. Version 2.9 ist empfohlen.
|
||||
- Detailierte Installtionsanweisungen für BizHawk können über den obrigen Link gefunden werden.
|
||||
- Windows-Benutzer müssen die Prerequisiten installiert haben. Diese können ebenfalls über
|
||||
den obrigen Link gefunden werden.
|
||||
- Der integrierte Archipelago-Client, welcher [hier](https://github.com/ArchipelagoMW/Archipelago/releases) installiert
|
||||
werden kann.
|
||||
- Eine `Ocarina of Time v1.0 US(?) ROM`. (Nicht aus Europa und keine Master-Quest oder Debug-Rom!)
|
||||
|
||||
## Konfigurieren von BizHawk
|
||||
|
||||
Sobald Bizhawk einmal installiert wurde, öffne **EmuHawk** und ändere die folgenen Einsteluungen:
|
||||
|
||||
- (≤ 2.8) Gehe zu `Config > Customize`. Wechlse zu dem `Advanced`-Reiter, wechsle dann den `Lua Core` von "NLua+KopiLua" zu
|
||||
`"Lua+LuaInterface"`. Starte danach EmuHawk neu. Dies ist zwingend notwendig, damit die Lua-Scripts, mit denen man sich mit dem Client verbindet, ordnungsgemäß funktionieren.
|
||||
**ANMERKUNG: Selbst wenn "Lua+LuaInterface" bereits ausgewählt ist, wechsle zwischen den beiden Optionen umher und**
|
||||
**wähle es erneut aus. Neue Installationen oder Versionen von EmuHawk neigen dazu "Lua+LuaInterface" als die**
|
||||
**Standard-Option anzuzeigen, aber laden dennoch "NLua+KopiLua", bis dieser Schritt getan ist.**
|
||||
- Unter `Config > Customize > Advanced`, gehe sicher dass der Haken bei `AutoSaveRAM` ausgeählt ist, und klicke dann
|
||||
den 5s-Knopf. Dies verringert die Wahrscheinlichkeit den Speicherfrotschritt zu verlieren, sollte der Emulator mal
|
||||
abstürzen.
|
||||
- **(Optional)** Unter `Config > Customize` kannst du die Haken in den "Run in background"
|
||||
(Laufe weiter im Hintergrund) und "Accept background input" (akzeptiere Tastendruck im Hintergrund) Kästchen setzen.
|
||||
Dies erlaubt dir das Spiel im Hintergrund weiter zu spielen, selbst wenn ein anderes Fenster aktiv ist. (Nützlich bei
|
||||
mehreren oder eher großen Bildschrimen/Monitoren.)
|
||||
- Unter `Config > Hotkeys` sind viele Hotkeys, die mit oft genuten Tasten belegt worden sind. Es wird empfohlen die
|
||||
meisten (oder alle) Hotkeys zu deaktivieren. Dies kann schnell mit `Esc` erledigt werden.
|
||||
- Wird mit einem Kontroller gespielt, bei der Tastenbelegung (bei einem Laufendem Spiel, unter
|
||||
`Config > Controllers...`), deaktiviere "P1 A Up", "P1 A Down", "P1 A Left", and "P1 A Right" und gehe stattdessen in
|
||||
den Reiter `Analog Controls` um den Stick zu belegen, da sonst Probleme beim Zielen auftreten (mit dem Bogen oder
|
||||
ähnliches). Y-Axis ist für Oben und Unten, und die X-Axis ist für Links und Rechts.
|
||||
- Unter `N64` setze einen Haken bei "Use Expansion Slot" (Benutze Erweiterungs-Slot). Dies wird benötigt damit
|
||||
savestates/schnellspeichern funktioniert. (Das N64-Menü taucht nur **nach** dem laden einer N64-ROM auf.)
|
||||
|
||||
Es wird sehr empfohlen N64 Rom-Erweiterungen (\*.n64, \*.z64) mit dem Emuhawk - welcher zuvor installiert wurde - zu
|
||||
verknüpfen.
|
||||
Um dies zu tun, muss eine beliebige N64 Rom aufgefunden werden, welche in deinem Besitz ist, diese Rechtsklicken und
|
||||
dann auf "Öffnen mit..." gehen. Gehe dann auf "Andere App auswählen" und suche nach deinen BizHawk-Ordner, in der
|
||||
sich der Emulator befindet, und wähle dann `EmuHawk.exe` **(NICHT "DiscoHawk.exe"!)** aus.
|
||||
|
||||
Eine Alternative BizHawk Setup Anleitung (auf Englisch), sowie weitere Hilfe bei Problemen kann
|
||||
[hier](https://wiki.ootrandomizer.com/index.php?title=Bizhawk) gefunden werden.
|
||||
|
||||
## Erstelle eine YAML-Datei
|
||||
|
||||
### Was ist eine YAML-Datei und Warum brauch ich eine?
|
||||
|
||||
Eine YAML-Datie enthält einen Satz an einstellbaren Optionen, die dem Generator mitteilen, wie
|
||||
dein Spiel generiert werden soll. In einer Multiworld stellt jeder Spieler eine eigene YAML-Datei zur Verfügung. Dies
|
||||
erlaubt jeden Spieler eine personalisierte Erfahrung nach derem Geschmack. Damit kann auch jeder Spieler in einer
|
||||
Multiworld (des gleichen Spiels) völlig unterschiedliche Einstellungen haben.
|
||||
|
||||
Für weitere Informationen, besuche die allgemeine Anleitung zum Erstellen einer
|
||||
YAML-Datei: [Archipelago Setup Anleitung](/tutorial/Archipelago/setup/en)
|
||||
|
||||
### Woher bekomme ich eine YAML-Datei?
|
||||
|
||||
Die Seite für die Spielereinstellungen auf dieser Website erlaubt es dir deine persönlichen Einstellungen nach
|
||||
vorlieben zu konfigurieren und eine YAML-Datei zu exportieren.
|
||||
Seite für die Spielereinstellungen:
|
||||
[Seite für die Spielereinstellungen von Ocarina of Time](/games/Ocarina%20of%20Time/player-options)
|
||||
|
||||
### Überprüfen deiner YAML-Datei
|
||||
|
||||
Wenn du deine YAML-Datei überprüfen möchtest, um sicher zu gehen, dass sie funktioniert, kannst du dies auf der
|
||||
YAML-Überprüfungsseite tun.
|
||||
YAML-Überprüfungsseite: [YAML-Überprüfungsseite](/check)
|
||||
|
||||
## Beitreten einer Multiworld
|
||||
|
||||
### Erhalte deinen OoT-Patch
|
||||
|
||||
(Der folgende Prozess ist bei den meisten ROM-basierenden Spielen sehr ähnlich.)
|
||||
|
||||
Wenn du einer Multiworld beitrittst, wirst du gefordert eine YAML-Datei bei dem Host abzugeben. Ist dies getan,
|
||||
erhälst du (in der Regel) einen Link vom Host der Multiworld. Dieser führt dich zu einem Raum, in dem alle
|
||||
teilnehmenden Spieler (bzw. Welten) aufgelistet sind. Du solltest dich dann auf **deine** Welt konzentrieren
|
||||
und klicke dann auf `Download APZ5 File...`.
|
||||

|
||||
|
||||
Führe die `.apz5`-Datei mit einem Doppelklick aus, um deinen Ocarina Of Time-Client zu starten, sowie das patchen
|
||||
deiner ROM. Ist dieser Prozess fertig (kann etwas dauern), startet sich der Client und der Emulator automatisch
|
||||
(sofern das "Öffnen mit..." ausgewählt wurde).
|
||||
|
||||
### Verbinde zum Multiserver
|
||||
|
||||
Sind einmal der Client und der Emulator gestartet, müssen sie nur noch miteinander verbunden werden. Gehe dazu in
|
||||
deinen Archipelago-Ordner, dann zu `data/lua`, und füge das `connector_oot.lua` Script per Drag&Drop (ziehen und
|
||||
fallen lassen) auf das EmuHawk-Fenster. (Alternativ kannst du die Lua-Konsole manuell öffnen, gehe dazu auf
|
||||
`Script > Open Script` und durchsuche die Ordner nach `data/lua/connector_oot.lua`.)
|
||||
|
||||
Um den Client mit dem Multiserver zu verbinden, füge einfach `<Adresse>:<Port>` in das Textfeld ganz oben im
|
||||
Client ein und drücke Enter oder "Connect" (verbinden). Wird ein Passwort benötigt, musst du es danach unten in das
|
||||
Textfeld (für den Chat und Befehle) eingeben.
|
||||
Alternativ kannst du auch in dem unterem Textfeld den folgenden Befehl schreiben:
|
||||
`/connect <Adresse>:<Port> [Passwort]` (wie die Adresse und der Port lautet steht in dem Raum, oder wird von deinem
|
||||
Host an dich weitergegeben.)
|
||||
Beispiel: `/connect archipelago.gg:12345 Passw123`
|
||||
|
||||
Du bist nun bereit für dein Zeitreise-Abenteuer in Hyrule.
|
||||
@@ -7,7 +7,7 @@ import logging
|
||||
import os
|
||||
from typing import Any, Set, List, Dict, Optional, Tuple, ClassVar
|
||||
|
||||
from BaseClasses import ItemClassification, MultiWorld, Tutorial
|
||||
from BaseClasses import ItemClassification, MultiWorld, Tutorial, LocationProgressType
|
||||
from Fill import FillError, fill_restrictive
|
||||
from Options import Toggle
|
||||
import settings
|
||||
@@ -20,7 +20,7 @@ from .items import (ITEM_GROUPS, PokemonEmeraldItem, create_item_label_to_code_m
|
||||
offset_item_value)
|
||||
from .locations import (LOCATION_GROUPS, PokemonEmeraldLocation, create_location_label_to_id_map,
|
||||
create_locations_with_tags)
|
||||
from .options import (ItemPoolType, RandomizeWildPokemon, RandomizeBadges, RandomizeTrainerParties, RandomizeHms,
|
||||
from .options import (Goal, ItemPoolType, RandomizeWildPokemon, RandomizeBadges, RandomizeTrainerParties, RandomizeHms,
|
||||
RandomizeStarters, LevelUpMoves, RandomizeAbilities, RandomizeTypes, TmCompatibility,
|
||||
HmCompatibility, RandomizeStaticEncounters, NormanRequirement, PokemonEmeraldOptions)
|
||||
from .pokemon import get_random_species, get_random_move, get_random_damaging_move, get_random_type
|
||||
@@ -146,6 +146,60 @@ class PokemonEmeraldWorld(World):
|
||||
|
||||
self.multiworld.regions.extend(regions.values())
|
||||
|
||||
# Exclude locations which are always locked behind the player's goal
|
||||
def exclude_locations(location_names: List[str]):
|
||||
for location_name in location_names:
|
||||
try:
|
||||
self.multiworld.get_location(location_name,
|
||||
self.player).progress_type = LocationProgressType.EXCLUDED
|
||||
except KeyError:
|
||||
continue # Location not in multiworld
|
||||
|
||||
if self.options.goal == Goal.option_champion:
|
||||
# Always required to beat champion before receiving this
|
||||
exclude_locations([
|
||||
"Littleroot Town - S.S. Ticket from Norman"
|
||||
])
|
||||
|
||||
# S.S. Ticket requires beating champion, so ferry is not accessible until after goal
|
||||
if not self.options.enable_ferry:
|
||||
exclude_locations([
|
||||
"SS Tidal - Hidden Item in Lower Deck Trash Can",
|
||||
"SS Tidal - TM49 from Thief"
|
||||
])
|
||||
|
||||
# Construction workers don't move until champion is defeated
|
||||
if "Safari Zone Construction Workers" not in self.options.remove_roadblocks.value:
|
||||
exclude_locations([
|
||||
"Safari Zone NE - Hidden Item North",
|
||||
"Safari Zone NE - Hidden Item East",
|
||||
"Safari Zone NE - Item on Ledge",
|
||||
"Safari Zone SE - Hidden Item in South Grass 1",
|
||||
"Safari Zone SE - Hidden Item in South Grass 2",
|
||||
"Safari Zone SE - Item in Grass"
|
||||
])
|
||||
elif self.options.goal == Goal.option_norman:
|
||||
# If the player sets their options such that Surf or the Balance
|
||||
# Badge is vanilla, a very large number of locations become
|
||||
# "post-Norman". Similarly, access to the E4 may require you to
|
||||
# defeat Norman as an event or to get his badge, making postgame
|
||||
# locations inaccessible. Detecting these situations isn't trivial
|
||||
# and excluding all locations requiring Surf would be a bad idea.
|
||||
# So for now we just won't touch it and blame the user for
|
||||
# constructing their options in this way. Players usually expect
|
||||
# to only partially complete their world when playing this goal
|
||||
# anyway.
|
||||
|
||||
# Locations which are directly unlocked by defeating Norman.
|
||||
exclude_locations([
|
||||
"Petalburg Gym - Balance Badge",
|
||||
"Petalburg Gym - TM42 from Norman",
|
||||
"Petalburg City - HM03 from Wally's Uncle",
|
||||
"Dewford Town - TM36 from Sludge Bomb Man",
|
||||
"Mauville City - Basement Key from Wattson",
|
||||
"Mauville City - TM24 from Wattson"
|
||||
])
|
||||
|
||||
def create_items(self) -> None:
|
||||
item_locations: List[PokemonEmeraldLocation] = [
|
||||
location
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import typing
|
||||
from Options import Option, DeathLink, Range, Toggle
|
||||
from dataclasses import dataclass
|
||||
from Options import Option, DeathLink, Range, Toggle, PerGameCommonOptions
|
||||
|
||||
class DoorCost(Range):
|
||||
"""Amount of Trinkets required to enter Areas. Set to 0 to disable artificial locks."""
|
||||
display_name = "Door Cost"
|
||||
range_start = 0
|
||||
range_end = 3
|
||||
default = 3
|
||||
@@ -13,6 +15,7 @@ class AreaCostRandomizer(Toggle):
|
||||
|
||||
class DeathLinkAmnesty(Range):
|
||||
"""Amount of Deaths to take before sending a DeathLink signal, for balancing difficulty"""
|
||||
display_name = "Death Link Amnesty"
|
||||
range_start = 0
|
||||
range_end = 30
|
||||
default = 15
|
||||
@@ -25,11 +28,11 @@ class MusicRandomizer(Toggle):
|
||||
"""Randomize Music"""
|
||||
display_name = "Music Randomizer"
|
||||
|
||||
v6_options: typing.Dict[str,type(Option)] = {
|
||||
"MusicRandomizer": MusicRandomizer,
|
||||
"AreaRandomizer": AreaRandomizer,
|
||||
"DoorCost": DoorCost,
|
||||
"AreaCostRandomizer": AreaCostRandomizer,
|
||||
"death_link": DeathLink,
|
||||
"DeathLinkAmnesty": DeathLinkAmnesty
|
||||
}
|
||||
@dataclass
|
||||
class V6Options(PerGameCommonOptions):
|
||||
music_rando: MusicRandomizer
|
||||
area_rando: AreaRandomizer
|
||||
door_cost: DoorCost
|
||||
area_cost: AreaCostRandomizer
|
||||
death_link: DeathLink
|
||||
death_link_amnesty: DeathLinkAmnesty
|
||||
|
||||
@@ -31,14 +31,3 @@ def create_regions(world: MultiWorld, player: int):
|
||||
locWrp_names = ["Edge Games"]
|
||||
regWrp.locations += [V6Location(player, loc_name, location_table[loc_name], regWrp) for loc_name in locWrp_names]
|
||||
world.regions.append(regWrp)
|
||||
|
||||
|
||||
def connect_regions(world: MultiWorld, player: int, source: str, target: str, rule):
|
||||
sourceRegion = world.get_region(source, player)
|
||||
targetRegion = world.get_region(target, player)
|
||||
|
||||
connection = Entrance(player,'', sourceRegion)
|
||||
connection.access_rule = rule
|
||||
|
||||
sourceRegion.exits.append(connection)
|
||||
connection.connect(targetRegion)
|
||||
@@ -1,6 +1,6 @@
|
||||
import typing
|
||||
from ..generic.Rules import add_rule
|
||||
from .Regions import connect_regions, v6areas
|
||||
from .Regions import v6areas
|
||||
|
||||
|
||||
def _has_trinket_range(state, player, start, end) -> bool:
|
||||
@@ -10,34 +10,36 @@ def _has_trinket_range(state, player, start, end) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
def set_rules(world, player, area_connections: typing.Dict[int, int], area_cost_map: typing.Dict[int, int]):
|
||||
def set_rules(multiworld, options, player, area_connections: typing.Dict[int, int], area_cost_map: typing.Dict[int, int]):
|
||||
areashuffle = list(range(len(v6areas)))
|
||||
if world.AreaRandomizer[player].value:
|
||||
world.random.shuffle(areashuffle)
|
||||
if options.area_rando:
|
||||
multiworld.random.shuffle(areashuffle)
|
||||
area_connections.update({(index + 1): (value + 1) for index, value in enumerate(areashuffle)})
|
||||
area_connections.update({0: 0})
|
||||
if world.AreaCostRandomizer[player].value:
|
||||
world.random.shuffle(areashuffle)
|
||||
if options.area_cost:
|
||||
multiworld.random.shuffle(areashuffle)
|
||||
area_cost_map.update({(index + 1): (value + 1) for index, value in enumerate(areashuffle)})
|
||||
area_cost_map.update({0: 0})
|
||||
|
||||
menu_region = multiworld.get_region("Menu", player)
|
||||
for i in range(1, 5):
|
||||
connect_regions(world, player, "Menu", v6areas[area_connections[i] - 1],
|
||||
lambda state, i=i: _has_trinket_range(state, player,
|
||||
world.DoorCost[player].value * (area_cost_map[i] - 1),
|
||||
world.DoorCost[player].value * area_cost_map[i]))
|
||||
target_region = multiworld.get_region(v6areas[area_connections[i] - 1], player)
|
||||
menu_region.connect(connecting_region=target_region,
|
||||
rule=lambda state, i=i: _has_trinket_range(state, player,
|
||||
options.door_cost * (area_cost_map[i] - 1),
|
||||
options.door_cost * area_cost_map[i]))
|
||||
|
||||
# Special Rule for V
|
||||
add_rule(world.get_location("V", player), lambda state: state.can_reach("Laboratory", 'Region', player) and
|
||||
add_rule(multiworld.get_location("V", player), lambda state: state.can_reach("Laboratory", 'Region', player) and
|
||||
state.can_reach("The Tower", 'Region', player) and
|
||||
state.can_reach("Space Station 2", 'Region', player) and
|
||||
state.can_reach("Warp Zone", 'Region', player))
|
||||
|
||||
# Special Rule for NPC Trinket
|
||||
add_rule(world.get_location("NPC Trinket", player),
|
||||
add_rule(multiworld.get_location("NPC Trinket", player),
|
||||
lambda state: state.can_reach("Laboratory", 'Region', player) or
|
||||
(state.can_reach("The Tower", 'Region', player) and
|
||||
state.can_reach("Space Station 2", 'Region', player) and
|
||||
state.can_reach("Warp Zone", 'Region', player)))
|
||||
|
||||
world.completion_condition[player] = lambda state: state.can_reach("V", 'Location', player)
|
||||
multiworld.completion_condition[player] = lambda state: state.can_reach("V", 'Location', player)
|
||||
|
||||
@@ -2,7 +2,7 @@ import typing
|
||||
import os, json
|
||||
from .Items import item_table, V6Item
|
||||
from .Locations import location_table, V6Location
|
||||
from .Options import v6_options
|
||||
from .Options import V6Options
|
||||
from .Rules import set_rules
|
||||
from .Regions import create_regions
|
||||
from BaseClasses import Item, ItemClassification, Tutorial
|
||||
@@ -41,7 +41,7 @@ class V6World(World):
|
||||
|
||||
music_map: typing.Dict[int,int]
|
||||
|
||||
option_definitions = v6_options
|
||||
options_dataclass = V6Options
|
||||
|
||||
def create_regions(self):
|
||||
create_regions(self.multiworld, self.player)
|
||||
@@ -49,7 +49,7 @@ class V6World(World):
|
||||
def set_rules(self):
|
||||
self.area_connections = {}
|
||||
self.area_cost_map = {}
|
||||
set_rules(self.multiworld, self.player, self.area_connections, self.area_cost_map)
|
||||
set_rules(self.multiworld, self.options, self.player, self.area_connections, self.area_cost_map)
|
||||
|
||||
def create_item(self, name: str) -> Item:
|
||||
return V6Item(name, ItemClassification.progression, item_table[name], self.player)
|
||||
@@ -61,7 +61,7 @@ class V6World(World):
|
||||
def generate_basic(self):
|
||||
musiclist_o = [1,2,3,4,9,12]
|
||||
musiclist_s = musiclist_o.copy()
|
||||
if self.multiworld.MusicRandomizer[self.player].value:
|
||||
if self.options.music_rando:
|
||||
self.multiworld.random.shuffle(musiclist_s)
|
||||
self.music_map = dict(zip(musiclist_o, musiclist_s))
|
||||
|
||||
@@ -69,10 +69,10 @@ class V6World(World):
|
||||
return {
|
||||
"MusicRando": self.music_map,
|
||||
"AreaRando": self.area_connections,
|
||||
"DoorCost": self.multiworld.DoorCost[self.player].value,
|
||||
"DoorCost": self.options.door_cost.value,
|
||||
"AreaCostRando": self.area_cost_map,
|
||||
"DeathLink": self.multiworld.death_link[self.player].value,
|
||||
"DeathLink_Amnesty": self.multiworld.DeathLinkAmnesty[self.player].value
|
||||
"DeathLink": self.options.death_link.value,
|
||||
"DeathLink_Amnesty": self.options.death_link_amnesty.value
|
||||
}
|
||||
|
||||
def generate_output(self, output_directory: str):
|
||||
|
||||
@@ -16,6 +16,7 @@ Symbols:
|
||||
72 - Colored Squares
|
||||
80 - Arrows
|
||||
200 - Progressive Dots - Dots,Full Dots
|
||||
210 - Progressive Symmetry - Symmetry,Colored Dots
|
||||
260 - Progressive Stars - Stars,Stars + Same Colored Symbol
|
||||
|
||||
Useful:
|
||||
@@ -29,8 +30,9 @@ Filler:
|
||||
#503 - Energy Fill (Max) - 1
|
||||
|
||||
Traps:
|
||||
600 - Slowness - 8
|
||||
600 - Slowness - 6
|
||||
610 - Power Surge - 2
|
||||
615 - Bonk - 1
|
||||
|
||||
Jokes:
|
||||
650 - Functioning Brain
|
||||
@@ -41,10 +43,13 @@ Doors:
|
||||
1102 - Tutorial Outpost Exit (Panel) - 0x04CA4
|
||||
1105 - Symmetry Island Lower (Panel) - 0x000B0
|
||||
1107 - Symmetry Island Upper (Panel) - 0x1C349
|
||||
1108 - Desert Surface 3 Control (Panel) - 0x09FA0
|
||||
1109 - Desert Surface 8 Control (Panel) - 0x09F86
|
||||
1110 - Desert Light Room Entry (Panel) - 0x0C339
|
||||
1111 - Desert Flood Controls (Panel) - 0x1C2DF,0x1831E,0x1C260,0x1831C,0x1C2F3,0x1831D,0x1C2B1,0x1831B
|
||||
1112 - Desert Light Control (Panel) - 0x09FAA
|
||||
1113 - Desert Flood Room Entry (Panel) - 0x0A249
|
||||
1114 - Desert Elevator Room Hexagonal Control (Panel) - 0x0A015
|
||||
1115 - Quarry Elevator Control (Panel) - 0x17CC4
|
||||
1117 - Quarry Entry 1 (Panel) - 0x09E57
|
||||
1118 - Quarry Entry 2 (Panel) - 0x17C09
|
||||
@@ -69,6 +74,7 @@ Doors:
|
||||
1167 - Town Maze Rooftop Bridge (Panel) - 0x2896A
|
||||
1169 - Town Windmill Entry (Panel) - 0x17F5F
|
||||
1172 - Town Cargo Box Entry (Panel) - 0x0A0C8
|
||||
1173 - Town Desert Laser Redirect Control (Panel) - 0x09F98
|
||||
1182 - Windmill Turn Control (Panel) - 0x17D02
|
||||
1184 - Theater Entry (Panel) - 0x17F89
|
||||
1185 - Theater Video Input (Panel) - 0x00815
|
||||
@@ -231,10 +237,10 @@ Doors:
|
||||
1984 - Caves Shortcuts - 0x2D859,0x2D73F
|
||||
1987 - Tunnels Doors - 0x27739,0x27263,0x09E87,0x0348A
|
||||
|
||||
2000 - Desert Control Panels - 0x09FAA,0x1C2DF,0x1831E,0x1C260,0x1831C,0x1C2F3,0x1831D,0x1C2B1,0x1831B
|
||||
2000 - Desert Control Panels - 0x09FAA,0x1C2DF,0x1831E,0x1C260,0x1831C,0x1C2F3,0x1831D,0x1C2B1,0x1831B,0x0A015,0x09FA0,0x09F86
|
||||
2005 - Quarry Stoneworks Control Panels - 0x03678,0x03676,0x03679,0x03675
|
||||
2010 - Quarry Boathouse Control Panels - 0x03852,0x03858,0x275FA
|
||||
2015 - Town Control Panels - 0x2896A,0x334D8
|
||||
2015 - Town Control Panels - 0x2896A,0x334D8,0x09F98
|
||||
2020 - Windmill & Theater Control Panels - 0x17D02,0x00815
|
||||
2025 - Bunker Control Panels - 0x34BC5,0x34BC6,0x0A079
|
||||
2030 - Swamp Control Panels - 0x00609,0x18488,0x181F5,0x17E2B,0x17C0A,0x17E07
|
||||
@@ -242,7 +248,7 @@ Doors:
|
||||
|
||||
2100 - Symmetry Island Panels - 0x1C349,0x000B0
|
||||
2101 - Tutorial Outpost Panels - 0x0A171,0x04CA4
|
||||
2105 - Desert Panels - 0x09FAA,0x1C2DF,0x1831E,0x1C260,0x1831C,0x1C2F3,0x1831D,0x1C2B1,0x1831B,0x0C339,0x0A249
|
||||
2105 - Desert Panels - 0x09FAA,0x1C2DF,0x1831E,0x1C260,0x1831C,0x1C2F3,0x1831D,0x1C2B1,0x1831B,0x0C339,0x0A249,0x0A015,0x09FA0,0x09F86
|
||||
2110 - Quarry Outside Panels - 0x17C09,0x09E57,0x17CC4
|
||||
2115 - Quarry Stoneworks Panels - 0x01E5A,0x01E59,0x03678,0x03676,0x03679,0x03675
|
||||
2120 - Quarry Boathouse Panels - 0x03852,0x03858,0x275FA
|
||||
@@ -250,6 +256,7 @@ Doors:
|
||||
2125 - Monastery Panels - 0x09D9B,0x00C92,0x00B10
|
||||
2130 - Town Church & RGB House Panels - 0x28998,0x28A0D,0x334D8
|
||||
2135 - Town Maze Panels - 0x2896A,0x28A79
|
||||
2137 - Town Dockside House Panels - 0x0A0C8,0x09F98
|
||||
2140 - Windmill & Theater Panels - 0x17D02,0x00815,0x17F5F,0x17F89,0x0A168,0x33AB2
|
||||
2145 - Treehouse Panels - 0x0A182,0x0288C,0x02886,0x2700B,0x17CBC,0x037FF
|
||||
2150 - Bunker Panels - 0x34BC5,0x34BC6,0x0A079,0x0A099,0x17C2E
|
||||
|
||||
@@ -209,12 +209,12 @@ Door - 0x0C316 (Elevator Room Entry) - 0x18076
|
||||
159034 - 0x337F8 (Flood Room EP) - 0x1C2DF - True
|
||||
|
||||
Desert Elevator Room (Desert) - Desert Lowest Level Inbetween Shortcuts - 0x01317:
|
||||
158111 - 0x17C31 (Final Transparent) - True - True
|
||||
158113 - 0x012D7 (Final Hexagonal) - 0x17C31 & 0x0A015 - True
|
||||
158114 - 0x0A015 (Final Hexagonal Control) - 0x17C31 - True
|
||||
158115 - 0x0A15C (Final Bent 1) - True - True
|
||||
158116 - 0x09FFF (Final Bent 2) - 0x0A15C - True
|
||||
158117 - 0x0A15F (Final Bent 3) - 0x09FFF - True
|
||||
158111 - 0x17C31 (Elevator Room Transparent) - True - True
|
||||
158113 - 0x012D7 (Elevator Room Hexagonal) - 0x17C31 & 0x0A015 - True
|
||||
158114 - 0x0A015 (Elevator Room Hexagonal Control) - 0x17C31 - True
|
||||
158115 - 0x0A15C (Elevator Room Bent 1) - True - True
|
||||
158116 - 0x09FFF (Elevator Room Bent 2) - 0x0A15C - True
|
||||
158117 - 0x0A15F (Elevator Room Bent 3) - 0x09FFF - True
|
||||
159035 - 0x037BB (Elevator EP) - 0x01317 - True
|
||||
Door - 0x01317 (Elevator) - 0x03608
|
||||
|
||||
@@ -474,7 +474,7 @@ Town (Town) - Main Island - True - The Ocean - 0x0A054 - Town Maze Rooftop - 0x2
|
||||
158218 - 0x0A054 (Boat Spawn) - 0x17CA6 | 0x17CDF | 0x09DB8 | 0x17C95 - Boat
|
||||
158219 - 0x0A0C8 (Cargo Box Entry Panel) - True - Black/White Squares & Shapers
|
||||
Door - 0x0A0C9 (Cargo Box Entry) - 0x0A0C8
|
||||
158707 - 0x09F98 (Desert Laser Redirect) - True - True
|
||||
158707 - 0x09F98 (Desert Laser Redirect Control) - True - True
|
||||
158220 - 0x18590 (Transparent) - True - Symmetry
|
||||
158221 - 0x28AE3 (Vines) - 0x18590 - True
|
||||
158222 - 0x28938 (Apple Tree) - 0x28AE3 - True
|
||||
@@ -895,9 +895,9 @@ Mountainside Vault (Mountainside):
|
||||
|
||||
Mountaintop (Mountaintop) - Mountain Top Layer - 0x17C34:
|
||||
158405 - 0x0042D (River Shape) - True - True
|
||||
158406 - 0x09F7F (Box Short) - 7 Lasers - True
|
||||
158406 - 0x09F7F (Box Short) - 7 Lasers + Redirect - True
|
||||
158407 - 0x17C34 (Mountain Entry Panel) - 0x09F7F - Stars & Black/White Squares & Stars + Same Colored Symbol
|
||||
158800 - 0xFFF00 (Box Long) - 11 Lasers & 0x17C34 - True
|
||||
158800 - 0xFFF00 (Box Long) - 11 Lasers + Redirect & 0x17C34 - True
|
||||
159300 - 0x001A3 (River Shape EP) - True - True
|
||||
159320 - 0x3370E (Arch Black EP) - True - True
|
||||
159324 - 0x336C8 (Arch White Right EP) - True - True
|
||||
|
||||
@@ -209,12 +209,12 @@ Door - 0x0C316 (Elevator Room Entry) - 0x18076
|
||||
159034 - 0x337F8 (Flood Room EP) - 0x1C2DF - True
|
||||
|
||||
Desert Elevator Room (Desert) - Desert Lowest Level Inbetween Shortcuts - 0x01317:
|
||||
158111 - 0x17C31 (Final Transparent) - True - True
|
||||
158113 - 0x012D7 (Final Hexagonal) - 0x17C31 & 0x0A015 - True
|
||||
158114 - 0x0A015 (Final Hexagonal Control) - 0x17C31 - True
|
||||
158115 - 0x0A15C (Final Bent 1) - True - True
|
||||
158116 - 0x09FFF (Final Bent 2) - 0x0A15C - True
|
||||
158117 - 0x0A15F (Final Bent 3) - 0x09FFF - True
|
||||
158111 - 0x17C31 (Elevator Room Transparent) - True - True
|
||||
158113 - 0x012D7 (Elevator Room Hexagonal) - 0x17C31 & 0x0A015 - True
|
||||
158114 - 0x0A015 (Elevator Room Hexagonal Control) - 0x17C31 - True
|
||||
158115 - 0x0A15C (Elevator Room Bent 1) - True - True
|
||||
158116 - 0x09FFF (Elevator Room Bent 2) - 0x0A15C - True
|
||||
158117 - 0x0A15F (Elevator Room Bent 3) - 0x09FFF - True
|
||||
159035 - 0x037BB (Elevator EP) - 0x01317 - True
|
||||
Door - 0x01317 (Elevator) - 0x03608
|
||||
|
||||
@@ -474,7 +474,7 @@ Town (Town) - Main Island - True - The Ocean - 0x0A054 - Town Maze Rooftop - 0x2
|
||||
158218 - 0x0A054 (Boat Spawn) - 0x17CA6 | 0x17CDF | 0x09DB8 | 0x17C95 - Boat
|
||||
158219 - 0x0A0C8 (Cargo Box Entry Panel) - True - Squares & Black/White Squares & Shapers & Triangles
|
||||
Door - 0x0A0C9 (Cargo Box Entry) - 0x0A0C8
|
||||
158707 - 0x09F98 (Desert Laser Redirect) - True - True
|
||||
158707 - 0x09F98 (Desert Laser Redirect Control) - True - True
|
||||
158220 - 0x18590 (Transparent) - True - Symmetry
|
||||
158221 - 0x28AE3 (Vines) - 0x18590 - True
|
||||
158222 - 0x28938 (Apple Tree) - 0x28AE3 - True
|
||||
@@ -895,9 +895,9 @@ Mountainside Vault (Mountainside):
|
||||
|
||||
Mountaintop (Mountaintop) - Mountain Top Layer - 0x17C34:
|
||||
158405 - 0x0042D (River Shape) - True - True
|
||||
158406 - 0x09F7F (Box Short) - 7 Lasers - True
|
||||
158406 - 0x09F7F (Box Short) - 7 Lasers + Redirect - True
|
||||
158407 - 0x17C34 (Mountain Entry Panel) - 0x09F7F - Stars & Black/White Squares & Stars + Same Colored Symbol & Triangles
|
||||
158800 - 0xFFF00 (Box Long) - 11 Lasers & 0x17C34 - True
|
||||
158800 - 0xFFF00 (Box Long) - 11 Lasers + Redirect & 0x17C34 - True
|
||||
159300 - 0x001A3 (River Shape EP) - True - True
|
||||
159320 - 0x3370E (Arch Black EP) - True - True
|
||||
159324 - 0x336C8 (Arch White Right EP) - True - True
|
||||
|
||||
@@ -209,12 +209,12 @@ Door - 0x0C316 (Elevator Room Entry) - 0x18076
|
||||
159034 - 0x337F8 (Flood Room EP) - 0x1C2DF - True
|
||||
|
||||
Desert Elevator Room (Desert) - Desert Lowest Level Inbetween Shortcuts - 0x01317:
|
||||
158111 - 0x17C31 (Final Transparent) - True - True
|
||||
158113 - 0x012D7 (Final Hexagonal) - 0x17C31 & 0x0A015 - True
|
||||
158114 - 0x0A015 (Final Hexagonal Control) - 0x17C31 - True
|
||||
158115 - 0x0A15C (Final Bent 1) - True - True
|
||||
158116 - 0x09FFF (Final Bent 2) - 0x0A15C - True
|
||||
158117 - 0x0A15F (Final Bent 3) - 0x09FFF - True
|
||||
158111 - 0x17C31 (Elevator Room Transparent) - True - True
|
||||
158113 - 0x012D7 (Elevator Room Hexagonal) - 0x17C31 & 0x0A015 - True
|
||||
158114 - 0x0A015 (Elevator Room Hexagonal Control) - 0x17C31 - True
|
||||
158115 - 0x0A15C (Elevator Room Bent 1) - True - True
|
||||
158116 - 0x09FFF (Elevator Room Bent 2) - 0x0A15C - True
|
||||
158117 - 0x0A15F (Elevator Room Bent 3) - 0x09FFF - True
|
||||
159035 - 0x037BB (Elevator EP) - 0x01317 - True
|
||||
Door - 0x01317 (Elevator) - 0x03608
|
||||
|
||||
@@ -474,7 +474,7 @@ Town (Town) - Main Island - True - The Ocean - 0x0A054 - Town Maze Rooftop - 0x2
|
||||
158218 - 0x0A054 (Boat Spawn) - 0x17CA6 | 0x17CDF | 0x09DB8 | 0x17C95 - Boat
|
||||
158219 - 0x0A0C8 (Cargo Box Entry Panel) - True - Black/White Squares & Shapers
|
||||
Door - 0x0A0C9 (Cargo Box Entry) - 0x0A0C8
|
||||
158707 - 0x09F98 (Desert Laser Redirect) - True - True
|
||||
158707 - 0x09F98 (Desert Laser Redirect Control) - True - True
|
||||
158220 - 0x18590 (Transparent) - True - Symmetry
|
||||
158221 - 0x28AE3 (Vines) - 0x18590 - True
|
||||
158222 - 0x28938 (Apple Tree) - 0x28AE3 - True
|
||||
@@ -895,9 +895,9 @@ Mountainside Vault (Mountainside):
|
||||
|
||||
Mountaintop (Mountaintop) - Mountain Top Layer - 0x17C34:
|
||||
158405 - 0x0042D (River Shape) - True - True
|
||||
158406 - 0x09F7F (Box Short) - 7 Lasers - True
|
||||
158406 - 0x09F7F (Box Short) - 7 Lasers + Redirect - True
|
||||
158407 - 0x17C34 (Mountain Entry Panel) - 0x09F7F - Black/White Squares
|
||||
158800 - 0xFFF00 (Box Long) - 11 Lasers & 0x17C34 - True
|
||||
158800 - 0xFFF00 (Box Long) - 11 Lasers + Redirect & 0x17C34 - True
|
||||
159300 - 0x001A3 (River Shape EP) - True - True
|
||||
159320 - 0x3370E (Arch Black EP) - True - True
|
||||
159324 - 0x336C8 (Arch White Right EP) - True - True
|
||||
|
||||
@@ -6,6 +6,7 @@ from typing import Dict, Optional
|
||||
|
||||
from BaseClasses import Region, Location, MultiWorld, Item, Entrance, Tutorial, CollectionState
|
||||
from Options import PerGameCommonOptions, Toggle
|
||||
from .presets import witness_option_presets
|
||||
from .hints import get_always_hint_locations, get_always_hint_items, get_priority_hint_locations, \
|
||||
get_priority_hint_items, make_hints, generate_joke_hints
|
||||
from worlds.AutoWorld import World, WebWorld
|
||||
@@ -15,7 +16,7 @@ from .locations import WitnessPlayerLocations, StaticWitnessLocations
|
||||
from .items import WitnessItem, StaticWitnessItems, WitnessPlayerItems, ItemData
|
||||
from .regions import WitnessRegions
|
||||
from .rules import set_rules
|
||||
from .Options import TheWitnessOptions
|
||||
from .options import TheWitnessOptions
|
||||
from .utils import get_audio_logs
|
||||
from logging import warning, error
|
||||
|
||||
@@ -31,6 +32,8 @@ class WitnessWebWorld(WebWorld):
|
||||
["NewSoupVi", "Jarno"]
|
||||
)]
|
||||
|
||||
options_presets = witness_option_presets
|
||||
|
||||
|
||||
class WitnessWorld(World):
|
||||
"""
|
||||
@@ -40,7 +43,6 @@ class WitnessWorld(World):
|
||||
"""
|
||||
game = "The Witness"
|
||||
topology_present = False
|
||||
data_version = 14
|
||||
|
||||
StaticWitnessLogic()
|
||||
StaticWitnessLocations()
|
||||
@@ -88,10 +90,10 @@ class WitnessWorld(World):
|
||||
}
|
||||
|
||||
def generate_early(self):
|
||||
disabled_locations = self.multiworld.exclude_locations[self.player].value
|
||||
disabled_locations = self.options.exclude_locations.value
|
||||
|
||||
self.player_logic = WitnessPlayerLogic(
|
||||
self, disabled_locations, self.multiworld.start_inventory[self.player].value
|
||||
self, disabled_locations, self.options.start_inventory.value
|
||||
)
|
||||
|
||||
self.locat: WitnessPlayerLocations = WitnessPlayerLocations(self, self.player_logic)
|
||||
@@ -102,14 +104,29 @@ class WitnessWorld(World):
|
||||
|
||||
self.log_ids_to_hints = dict()
|
||||
|
||||
if not (self.options.shuffle_symbols or self.options.shuffle_doors or self.options.shuffle_lasers):
|
||||
if self.multiworld.players == 1:
|
||||
warning(f"{self.multiworld.get_player_name(self.player)}'s Witness world doesn't have any progression"
|
||||
f" items. Please turn on Symbol Shuffle, Door Shuffle or Laser Shuffle if that doesn't"
|
||||
f" seem right.")
|
||||
else:
|
||||
raise Exception(f"{self.multiworld.get_player_name(self.player)}'s Witness world doesn't have any"
|
||||
f" progression items. Please turn on Symbol Shuffle, Door Shuffle or Laser Shuffle.")
|
||||
interacts_with_multiworld = (
|
||||
self.options.shuffle_symbols or
|
||||
self.options.shuffle_doors or
|
||||
self.options.shuffle_lasers == "anywhere"
|
||||
)
|
||||
|
||||
has_progression = (
|
||||
interacts_with_multiworld
|
||||
or self.options.shuffle_lasers == "local"
|
||||
or self.options.shuffle_boat
|
||||
or self.options.early_caves == "add_to_pool"
|
||||
)
|
||||
|
||||
if not has_progression and self.multiworld.players == 1:
|
||||
warning(f"{self.multiworld.get_player_name(self.player)}'s Witness world doesn't have any progression"
|
||||
f" items. Please turn on Symbol Shuffle, Door Shuffle or Laser Shuffle if that doesn't seem right.")
|
||||
elif not interacts_with_multiworld and self.multiworld.players > 1:
|
||||
raise Exception(f"{self.multiworld.get_player_name(self.player)}'s Witness world doesn't have enough"
|
||||
f" progression items that can be placed in other players' worlds. Please turn on Symbol"
|
||||
f" Shuffle, Door Shuffle or non-local Laser Shuffle.")
|
||||
|
||||
if self.options.shuffle_lasers == "local":
|
||||
self.options.local_items.value |= self.item_name_groups["Lasers"]
|
||||
|
||||
def create_regions(self):
|
||||
self.regio.create_regions(self, self.player_logic)
|
||||
@@ -176,7 +193,8 @@ class WitnessWorld(World):
|
||||
extra_checks = [
|
||||
("First Hallway Room", "First Hallway Bend"),
|
||||
("First Hallway", "First Hallway Straight"),
|
||||
("Desert Outside", "Desert Surface 3"),
|
||||
("Desert Outside", "Desert Surface 1"),
|
||||
("Desert Outside", "Desert Surface 2"),
|
||||
]
|
||||
|
||||
for i in range(num_early_locs, needed_size):
|
||||
@@ -253,7 +271,7 @@ class WitnessWorld(World):
|
||||
self.own_itempool += new_items
|
||||
self.multiworld.itempool += new_items
|
||||
if self.items.item_data[item_name].local_only:
|
||||
self.multiworld.local_items[self.player].value.add(item_name)
|
||||
self.options.local_items.value.add(item_name)
|
||||
|
||||
def fill_slot_data(self) -> dict:
|
||||
hint_amount = self.options.hint_amount.value
|
||||
|
||||
@@ -187,8 +187,8 @@ def get_always_hint_items(world: "WitnessWorld") -> List[str]:
|
||||
return always
|
||||
|
||||
|
||||
def get_always_hint_locations(_: "WitnessWorld") -> List[str]:
|
||||
return [
|
||||
def get_always_hint_locations(world: "WitnessWorld") -> List[str]:
|
||||
always = [
|
||||
"Challenge Vault Box",
|
||||
"Mountain Bottom Floor Discard",
|
||||
"Theater Eclipse EP",
|
||||
@@ -196,6 +196,16 @@ def get_always_hint_locations(_: "WitnessWorld") -> List[str]:
|
||||
"Mountainside Cloud Cycle EP",
|
||||
]
|
||||
|
||||
# Add Obelisk Sides that contain EPs that are meant to be hinted, if they are necessary to complete the Obelisk Side
|
||||
if world.options.EP_difficulty == "eclipse":
|
||||
always.append("Town Obelisk Side 6") # Eclipse EP
|
||||
|
||||
if world.options.EP_difficulty != "normal":
|
||||
always.append("Treehouse Obelisk Side 4") # Couch EP
|
||||
always.append("River Obelisk Side 1") # Cloud Cycle EP. Needs to be changed to "Mountainside Obelisk" soon
|
||||
|
||||
return always
|
||||
|
||||
|
||||
def get_priority_hint_items(world: "WitnessWorld") -> List[str]:
|
||||
priority = {
|
||||
@@ -217,9 +227,8 @@ def get_priority_hint_items(world: "WitnessWorld") -> List[str]:
|
||||
"Eraser",
|
||||
"Black/White Squares",
|
||||
"Colored Squares",
|
||||
"Colored Dots",
|
||||
"Sound Dots",
|
||||
"Symmetry"
|
||||
"Progressive Symmetry"
|
||||
]
|
||||
|
||||
priority.update(world.random.sample(symbols, 5))
|
||||
@@ -249,8 +258,8 @@ def get_priority_hint_items(world: "WitnessWorld") -> List[str]:
|
||||
return sorted(priority)
|
||||
|
||||
|
||||
def get_priority_hint_locations(_: "WitnessWorld") -> List[str]:
|
||||
return [
|
||||
def get_priority_hint_locations(world: "WitnessWorld") -> List[str]:
|
||||
priority = [
|
||||
"Swamp Purple Underwater",
|
||||
"Shipwreck Vault Box",
|
||||
"Town RGB Room Left",
|
||||
@@ -265,6 +274,13 @@ def get_priority_hint_locations(_: "WitnessWorld") -> List[str]:
|
||||
"Boat Shipwreck Green EP",
|
||||
"Quarry Stoneworks Control Room Left",
|
||||
]
|
||||
|
||||
# Add Obelisk Sides that contain EPs that are meant to be hinted, if they are necessary to complete the Obelisk Side
|
||||
if world.options.EP_difficulty != "normal":
|
||||
priority.append("Town Obelisk Side 6") # Theater Flowers EP
|
||||
priority.append("Treehouse Obelisk Side 4") # Shipwreck Green EP
|
||||
|
||||
return priority
|
||||
|
||||
|
||||
def make_hint_from_item(world: "WitnessWorld", item_name: str, own_itempool: List[Item]):
|
||||
|
||||
@@ -55,8 +55,8 @@ class StaticWitnessLocations:
|
||||
"Desert Light Room 3",
|
||||
"Desert Pond Room 5",
|
||||
"Desert Flood Room 6",
|
||||
"Desert Final Hexagonal",
|
||||
"Desert Final Bent 3",
|
||||
"Desert Elevator Room Hexagonal",
|
||||
"Desert Elevator Room Bent 3",
|
||||
"Desert Laser Panel",
|
||||
|
||||
"Quarry Entry 1 Panel",
|
||||
|
||||
@@ -28,11 +28,14 @@ class ShuffleSymbols(DefaultOnToggle):
|
||||
display_name = "Shuffle Symbols"
|
||||
|
||||
|
||||
class ShuffleLasers(Toggle):
|
||||
class ShuffleLasers(Choice):
|
||||
"""If on, the 11 lasers are turned into items and will activate on their own upon receiving them.
|
||||
Note: There is a visual bug that can occur with the Desert Laser. It does not affect gameplay - The Laser can still
|
||||
be redirected as normal, for both applications of redirection."""
|
||||
display_name = "Shuffle Lasers"
|
||||
option_off = 0
|
||||
option_local = 1
|
||||
option_anywhere = 2
|
||||
|
||||
|
||||
class ShuffleDoors(Choice):
|
||||
@@ -114,9 +117,13 @@ class ShufflePostgame(Toggle):
|
||||
|
||||
|
||||
class VictoryCondition(Choice):
|
||||
"""Change the victory condition from the original game's ending (elevator) to beating the Challenge
|
||||
or solving the mountaintop box, either using the short solution
|
||||
(7 lasers or whatever you've changed it to) or the long solution (11 lasers or whatever you've changed it to)."""
|
||||
"""Set the victory condition for this world.
|
||||
Elevator: Start the elevator at the bottom of the mountain (requires Mountain Lasers).
|
||||
Challenge: Beat the secret Challenge (requires Challenge Lasers).
|
||||
Mountain Box Short: Input the short solution to the Mountaintop Box (requires Mountain Lasers).
|
||||
Mountain Box Long: Input the long solution to the Mountaintop Box (requires Challenge Lasers).
|
||||
It is important to note that while the Mountain Box requires Desert Laser to be redirected in Town for that laser
|
||||
to count, the laser locks on the Elevator and Challenge Timer panels do not."""
|
||||
display_name = "Victory Condition"
|
||||
option_elevator = 0
|
||||
option_challenge = 1
|
||||
@@ -133,10 +140,13 @@ class PuzzleRandomization(Choice):
|
||||
|
||||
|
||||
class MountainLasers(Range):
|
||||
"""Sets the amount of beams required to enter the final area."""
|
||||
"""Sets the amount of lasers required to enter the Mountain.
|
||||
If set to a higher amount than 7, the mountaintop box will be slightly rotated to make it possible to solve without
|
||||
the hatch being opened.
|
||||
This change will also be applied logically to the long solution ("Challenge Lasers" setting)."""
|
||||
display_name = "Required Lasers for Mountain Entry"
|
||||
range_start = 1
|
||||
range_end = 7
|
||||
range_end = 11
|
||||
default = 7
|
||||
|
||||
|
||||
@@ -182,10 +192,19 @@ class HintAmount(Range):
|
||||
|
||||
class DeathLink(Toggle):
|
||||
"""If on: Whenever you fail a puzzle (with some exceptions), everyone who is also on Death Link dies.
|
||||
The effect of a "death" in The Witness is a Power Surge."""
|
||||
The effect of a "death" in The Witness is a Bonk Trap."""
|
||||
display_name = "Death Link"
|
||||
|
||||
|
||||
class DeathLinkAmnesty(Range):
|
||||
"""Number of panel fails to allow before sending a death through Death Link.
|
||||
0 means every panel fail will send a death, 1 means every other panel fail will send a death, etc."""
|
||||
display_name = "Death Link Amnesty"
|
||||
range_start = 0
|
||||
range_end = 5
|
||||
default = 1
|
||||
|
||||
|
||||
@dataclass
|
||||
class TheWitnessOptions(PerGameCommonOptions):
|
||||
puzzle_randomization: PuzzleRandomization
|
||||
@@ -209,3 +228,4 @@ class TheWitnessOptions(PerGameCommonOptions):
|
||||
puzzle_skip_amount: PuzzleSkipAmount
|
||||
hint_amount: HintAmount
|
||||
death_link: DeathLink
|
||||
death_link_amnesty: DeathLinkAmnesty
|
||||
@@ -103,7 +103,8 @@ class WitnessPlayerLogic:
|
||||
|
||||
if option_entity in self.EVENT_NAMES_BY_HEX:
|
||||
new_items = frozenset({frozenset([option_entity])})
|
||||
elif option_entity in {"7 Lasers", "11 Lasers", "PP2 Weirdness", "Theater to Tunnels"}:
|
||||
elif option_entity in {"7 Lasers", "11 Lasers", "7 Lasers + Redirect", "11 Lasers + Redirect",
|
||||
"PP2 Weirdness", "Theater to Tunnels"}:
|
||||
new_items = frozenset({frozenset([option_entity])})
|
||||
else:
|
||||
new_items = self.reduce_req_within_region(option_entity)
|
||||
@@ -322,7 +323,10 @@ class WitnessPlayerLogic:
|
||||
elif victory == 3:
|
||||
self.VICTORY_LOCATION = "0xFFF00"
|
||||
|
||||
if chal_lasers <= 7:
|
||||
# Long box can usually only be solved by opening Mountain Entry. However, if it requires 7 lasers or less
|
||||
# (challenge_lasers <= 7), you can now solve it without opening Mountain Entry first.
|
||||
# Furthermore, if the user sets mountain_lasers > 7, the box is rotated to not require Mountain Entry either.
|
||||
if chal_lasers <= 7 or mnt_lasers > 7:
|
||||
adjustment_linesets_in_order.append([
|
||||
"Requirement Changes:",
|
||||
"0xFFF00 - 11 Lasers - True",
|
||||
|
||||
101
worlds/witness/presets.py
Normal file
101
worlds/witness/presets.py
Normal file
@@ -0,0 +1,101 @@
|
||||
from typing import Any, Dict
|
||||
|
||||
from .options import *
|
||||
|
||||
witness_option_presets: Dict[str, Dict[str, Any]] = {
|
||||
# Great for short syncs & scratching that "speedrun with light routing elements" itch.
|
||||
"Short & Dense": {
|
||||
"progression_balancing": 30,
|
||||
|
||||
"puzzle_randomization": PuzzleRandomization.option_sigma_normal,
|
||||
|
||||
"shuffle_symbols": False,
|
||||
"shuffle_doors": ShuffleDoors.option_panels,
|
||||
"door_groupings": DoorGroupings.option_off,
|
||||
"shuffle_boat": True,
|
||||
"shuffle_lasers": ShuffleLasers.option_local,
|
||||
|
||||
"disable_non_randomized_puzzles": True,
|
||||
"shuffle_discarded_panels": False,
|
||||
"shuffle_vault_boxes": False,
|
||||
"shuffle_EPs": ShuffleEnvironmentalPuzzles.option_off,
|
||||
"EP_difficulty": EnvironmentalPuzzlesDifficulty.option_normal,
|
||||
"shuffle_postgame": False,
|
||||
|
||||
"victory_condition": VictoryCondition.option_mountain_box_short,
|
||||
"mountain_lasers": 7,
|
||||
"challenge_lasers": 11,
|
||||
|
||||
"early_caves": EarlyCaves.option_off,
|
||||
"elevators_come_to_you": False,
|
||||
|
||||
"trap_percentage": TrapPercentage.default,
|
||||
"puzzle_skip_amount": PuzzleSkipAmount.default,
|
||||
"hint_amount": HintAmount.default,
|
||||
"death_link": DeathLink.default,
|
||||
},
|
||||
|
||||
# For relative beginners who want to move to the next step.
|
||||
"Advanced, But Chill": {
|
||||
"progression_balancing": 30,
|
||||
|
||||
"puzzle_randomization": PuzzleRandomization.option_sigma_normal,
|
||||
|
||||
"shuffle_symbols": True,
|
||||
"shuffle_doors": ShuffleDoors.option_doors,
|
||||
"door_groupings": DoorGroupings.option_regional,
|
||||
"shuffle_boat": True,
|
||||
"shuffle_lasers": ShuffleLasers.option_off,
|
||||
|
||||
"disable_non_randomized_puzzles": False,
|
||||
"shuffle_discarded_panels": True,
|
||||
"shuffle_vault_boxes": True,
|
||||
"shuffle_EPs": ShuffleEnvironmentalPuzzles.option_obelisk_sides,
|
||||
"EP_difficulty": EnvironmentalPuzzlesDifficulty.option_normal,
|
||||
"shuffle_postgame": False,
|
||||
|
||||
"victory_condition": VictoryCondition.option_mountain_box_long,
|
||||
"mountain_lasers": 6,
|
||||
"challenge_lasers": 9,
|
||||
|
||||
"early_caves": EarlyCaves.option_off,
|
||||
"elevators_come_to_you": False,
|
||||
|
||||
"trap_percentage": TrapPercentage.default,
|
||||
"puzzle_skip_amount": 15,
|
||||
"hint_amount": HintAmount.default,
|
||||
"death_link": DeathLink.default,
|
||||
},
|
||||
|
||||
# Allsanity but without the BS (no expert, no tedious EPs).
|
||||
"Nice Allsanity": {
|
||||
"progression_balancing": 50,
|
||||
|
||||
"puzzle_randomization": PuzzleRandomization.option_sigma_normal,
|
||||
|
||||
"shuffle_symbols": True,
|
||||
"shuffle_doors": ShuffleDoors.option_mixed,
|
||||
"door_groupings": DoorGroupings.option_off,
|
||||
"shuffle_boat": True,
|
||||
"shuffle_lasers": ShuffleLasers.option_anywhere,
|
||||
|
||||
"disable_non_randomized_puzzles": False,
|
||||
"shuffle_discarded_panels": True,
|
||||
"shuffle_vault_boxes": True,
|
||||
"shuffle_EPs": ShuffleEnvironmentalPuzzles.option_individual,
|
||||
"EP_difficulty": EnvironmentalPuzzlesDifficulty.option_normal,
|
||||
"shuffle_postgame": False,
|
||||
|
||||
"victory_condition": VictoryCondition.option_challenge,
|
||||
"mountain_lasers": 6,
|
||||
"challenge_lasers": 9,
|
||||
|
||||
"early_caves": EarlyCaves.option_off,
|
||||
"elevators_come_to_you": True,
|
||||
|
||||
"trap_percentage": TrapPercentage.default,
|
||||
"puzzle_skip_amount": 15,
|
||||
"hint_amount": HintAmount.default,
|
||||
"death_link": DeathLink.default,
|
||||
},
|
||||
}
|
||||
@@ -29,8 +29,9 @@ laser_hexes = [
|
||||
]
|
||||
|
||||
|
||||
def _has_laser(laser_hex: str, world: "WitnessWorld", player: int) -> Callable[[CollectionState], bool]:
|
||||
if laser_hex == "0x012FB":
|
||||
def _has_laser(laser_hex: str, world: "WitnessWorld", player: int,
|
||||
redirect_required: bool) -> Callable[[CollectionState], bool]:
|
||||
if laser_hex == "0x012FB" and redirect_required:
|
||||
return lambda state: (
|
||||
_can_solve_panel(laser_hex, world, world.player, world.player_logic, world.locat)(state)
|
||||
and state.has("Desert Laser Redirection", player)
|
||||
@@ -39,11 +40,11 @@ def _has_laser(laser_hex: str, world: "WitnessWorld", player: int) -> Callable[[
|
||||
return _can_solve_panel(laser_hex, world, world.player, world.player_logic, world.locat)
|
||||
|
||||
|
||||
def _has_lasers(amount: int, world: "WitnessWorld") -> Callable[[CollectionState], bool]:
|
||||
def _has_lasers(amount: int, world: "WitnessWorld", redirect_required: bool) -> Callable[[CollectionState], bool]:
|
||||
laser_lambdas = []
|
||||
|
||||
for laser_hex in laser_hexes:
|
||||
has_laser_lambda = _has_laser(laser_hex, world, world.player)
|
||||
has_laser_lambda = _has_laser(laser_hex, world, world.player, redirect_required)
|
||||
|
||||
laser_lambdas.append(has_laser_lambda)
|
||||
|
||||
@@ -155,10 +156,16 @@ def _has_item(item: str, world: "WitnessWorld", player: int,
|
||||
return lambda state: state.can_reach(item, "Region", player)
|
||||
if item == "7 Lasers":
|
||||
laser_req = world.options.mountain_lasers.value
|
||||
return _has_lasers(laser_req, world)
|
||||
return _has_lasers(laser_req, world, False)
|
||||
if item == "7 Lasers + Redirect":
|
||||
laser_req = world.options.mountain_lasers.value
|
||||
return _has_lasers(laser_req, world, True)
|
||||
if item == "11 Lasers":
|
||||
laser_req = world.options.challenge_lasers.value
|
||||
return _has_lasers(laser_req, world)
|
||||
return _has_lasers(laser_req, world, False)
|
||||
if item == "11 Lasers + Redirect":
|
||||
laser_req = world.options.challenge_lasers.value
|
||||
return _has_lasers(laser_req, world, True)
|
||||
elif item == "PP2 Weirdness":
|
||||
return lambda state: _can_do_expert_pp2(state, world)
|
||||
elif item == "Theater to Tunnels":
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
Items:
|
||||
Desert Surface 3 Control (Panel)
|
||||
Desert Surface 8 Control (Panel)
|
||||
Desert Elevator Room Hexagonal Control (Panel)
|
||||
Desert Flood Controls (Panel)
|
||||
Desert Light Control (Panel)
|
||||
Quarry Elevator Control (Panel)
|
||||
@@ -10,6 +13,7 @@ Quarry Boathouse Hook Control (Panel)
|
||||
Monastery Shutters Control (Panel)
|
||||
Town Maze Rooftop Bridge (Panel)
|
||||
Town RGB Control (Panel)
|
||||
Town Desert Laser Redirect Control (Panel)
|
||||
Windmill Turn Control (Panel)
|
||||
Theater Video Input (Panel)
|
||||
Bunker Drop-Down Door Controls (Panel)
|
||||
|
||||
@@ -10,7 +10,7 @@ Monastery Panels
|
||||
Town Church & RGB House Panels
|
||||
Town Maze Panels
|
||||
Windmill & Theater Panels
|
||||
Town Cargo Box Entry (Panel)
|
||||
Town Dockside House Panels
|
||||
Treehouse Panels
|
||||
Bunker Panels
|
||||
Swamp Panels
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
Items:
|
||||
Arrows
|
||||
Progressive Dots
|
||||
Colored Dots
|
||||
Sound Dots
|
||||
Symmetry
|
||||
Progressive Symmetry
|
||||
Triangles
|
||||
Eraser
|
||||
Shapers
|
||||
|
||||
@@ -109,7 +109,6 @@ class StaticWitnessLogicObj:
|
||||
"Laser",
|
||||
"Laser Hedges",
|
||||
"Laser Pressure Plates",
|
||||
"Desert Laser Redirect"
|
||||
}
|
||||
is_vault_or_video = "Vault" in entity_name or "Video" in entity_name
|
||||
|
||||
|
||||
Reference in New Issue
Block a user