Skip to content

Commit 6f243bf

Browse files
author
alchav
committed
Universal Tracker support
1 parent af513da commit 6f243bf

4 files changed

Lines changed: 70 additions & 28 deletions

File tree

worlds/ffmq/Client.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ async def validate_rom(self, ctx):
7373
ctx.rom = rom_name
7474
ctx.game = self.game
7575
ctx.items_handling = 0b001
76+
ctx.want_slot_data = False
77+
7678
return True
7779

7880
async def game_watcher(self, ctx):

worlds/ffmq/Regions.py

Lines changed: 33 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,18 @@
2525
crest_warps = [51, 52, 53, 76, 96, 108, 158, 171, 175, 191, 275, 276, 277, 308, 334, 336, 396, 397]
2626

2727

28-
def process_rules(spot, access):
28+
def process_rules(spot, access, ut_glitch=False):
2929
for weapon in weapons:
3030
if weapon in access:
31-
add_rule(spot, lambda state, w=weapon: state.has_any(item_groups[w + "s"], spot.player))
31+
items = item_groups[weapon + "s"].copy()
32+
if ut_glitch:
33+
items.append("ut_glitch")
34+
add_rule(spot, lambda state, i=items: state.has_any(i, spot.player))
3235
access = [yaml_item(rule) for rule in access if rule not in weapons]
33-
add_rule(spot, lambda state: state.has_all(access, spot.player))
36+
if ut_glitch:
37+
add_rule(spot, lambda state: state.has_all(access, spot.player) or state.has("ut_glitch", spot.player))
38+
else:
39+
add_rule(spot, lambda state: state.has_all(access, spot.player))
3440

3541

3642
def create_region(world: MultiWorld, player: int, name: str, room_id=None, locations=None, links=None):
@@ -129,7 +135,7 @@ def set_rules(self) -> None:
129135
self.multiworld.completion_condition[self.player] = lambda state: state.has("Dark King", self.player)
130136

131137
def hard_boss_logic(state):
132-
return state.has_all(["River Coin", "Sand Coin"], self.player)
138+
return state.has_all(["River Coin", "Sand Coin"], self.player) or state.has("ut_glitch", self.player)
133139

134140
add_rule(self.multiworld.get_location("Pazuzu 1F", self.player), hard_boss_logic)
135141
add_rule(self.multiworld.get_location("Gidrah", self.player), hard_boss_logic)
@@ -158,43 +164,45 @@ def check_foresta(region):
158164

159165
if self.options.logic == "friendly":
160166
process_rules(self.multiworld.get_entrance("Overworld - Ice Pyramid", self.player),
161-
["MagicMirror"])
167+
["MagicMirror"], ut_glitch=True)
162168
process_rules(self.multiworld.get_entrance("Overworld - Volcano", self.player),
163-
["Mask"])
164-
if self.options.map_shuffle:
169+
["Mask"], ut_glitch=True)
170+
if self.options.map_shuffle in ("none", "dungeons_internal"):
165171
process_rules(self.multiworld.get_entrance("Overworld - Bone Dungeon", self.player),
166-
["Bomb"])
172+
["Bomb"], ut_glitch=True)
167173
process_rules(self.multiworld.get_entrance("Overworld - Wintry Cave", self.player),
168-
["Bomb", "Claw"])
174+
["Bomb", "Claw"], ut_glitch=True)
169175
process_rules(self.multiworld.get_entrance("Overworld - Ice Pyramid", self.player),
170-
["Bomb", "Claw"])
176+
["Bomb", "Claw"], ut_glitch=True)
171177
process_rules(self.multiworld.get_entrance("Overworld - Mine", self.player),
172-
["MegaGrenade", "Claw"])
178+
["MegaGrenade", "Claw"], ut_glitch=True)
173179
process_rules(self.multiworld.get_entrance("Overworld - Lava Dome", self.player),
174-
["MegaGrenade"])
180+
["MegaGrenade"], ut_glitch=True)
175181
process_rules(self.multiworld.get_entrance("Overworld - Giant Tree", self.player),
176-
["DragonClaw", "Axe"])
182+
["DragonClaw", "Axe"], ut_glitch=True)
177183
process_rules(self.multiworld.get_entrance("Overworld - Mount Gale", self.player),
178-
["DragonClaw"])
184+
["DragonClaw"], ut_glitch=True)
179185
process_rules(self.multiworld.get_entrance("Overworld - Pazuzu Tower", self.player),
180-
["DragonClaw", "Bomb"])
186+
["DragonClaw", "Bomb"], ut_glitch=True)
181187
process_rules(self.multiworld.get_entrance("Overworld - Mac Ship", self.player),
182-
["DragonClaw", "CaptainCap"])
188+
["DragonClaw", "CaptainCap"], ut_glitch=True)
183189
process_rules(self.multiworld.get_entrance("Overworld - Mac Ship Doom", self.player),
184-
["DragonClaw", "CaptainCap"])
190+
["DragonClaw", "CaptainCap"], ut_glitch=True)
185191

186-
if self.options.logic == "expert":
187-
if self.options.map_shuffle == "none" and not self.options.crest_shuffle:
188-
inner_room = self.multiworld.get_region("Wintry Temple Inner Room", self.player)
189-
connection = Entrance(self.player, "Sealed Temple Exit Trick", inner_room)
190-
connection.connect(self.multiworld.get_region("Wintry Temple Outer Room", self.player))
192+
if self.options.map_shuffle == "none" and not self.options.crest_shuffle:
193+
inner_room = self.multiworld.get_region("Wintry Temple Inner Room", self.player)
194+
connection = Entrance(self.player, "Sealed Temple Exit Trick", inner_room)
195+
connection.connect(self.multiworld.get_region("Wintry Temple Outer Room", self.player))
196+
if self.options.logic == "expert":
191197
connection.access_rule = lambda state: state.has("Exit Book", self.player)
192-
inner_room.exits.append(connection)
193-
else:
198+
else:
199+
connection.access_rule = lambda state: state.has_all(["Exit Book", "ut_glitch"], self.player)
200+
inner_room.exits.append(connection)
201+
if self.options.logic != "expert":
194202
for crest_warp in non_dead_end_crest_warps:
195203
entrance = self.multiworld.get_entrance(crest_warp, self.player)
196204
if entrance.connected_region.name in non_dead_end_crest_rooms:
197-
entrance.access_rule = lambda state: False
205+
add_rule(entrance, lambda state: state.has("ut_glitch", self.player))
198206

199207
if self.options.sky_coin_mode == "shattered_sky_coin":
200208
logic_coins = [16, 24, 32, 32, 38][self.options.shattered_sky_coin_quantity.value]

worlds/ffmq/__init__.py

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from typing import Mapping, Any
2+
13
import Utils
24
import settings
35
import base64
@@ -12,7 +14,8 @@
1214
from .Output import generate_output
1315
from .Options import FFMQOptions
1416
from .Client import FFMQClient
15-
17+
import zlib
18+
import msgpack
1619

1720
# removed until lists are supported
1821
# class FFMQSettings(settings.Group):
@@ -72,11 +75,15 @@ class FFMQWorld(World):
7275
web = FFMQWebWorld()
7376
# settings: FFMQSettings
7477

78+
ut_can_gen_without_yaml = True
79+
glitches_item_name = "ut_glitch"
80+
7581
def __init__(self, world, player: int):
7682
self.rom_name_available_event = threading.Event()
7783
self.rom_name = None
7884
self.rooms = None
7985
self.hint_data = []
86+
self.ut = False
8087
self.finished_hint_data_collection = threading.Event()
8188
super().__init__(world, player)
8289

@@ -87,6 +94,13 @@ def generate_early(self):
8794
if self.options.bosses_scaling_lower.value > self.options.bosses_scaling_upper.value:
8895
self.options.bosses_scaling_lower.value, self.options.bosses_scaling_upper.value = \
8996
self.options.bosses_scaling_upper.value, self.options.bosses_scaling_lower.value
97+
if hasattr(self.multiworld, "re_gen_passthrough") and self.game in self.multiworld.re_gen_passthrough:
98+
self.ut = True
99+
for key, value in self.multiworld.re_gen_passthrough[self.game].items():
100+
if hasattr(self.options, key):
101+
getattr(self.options, key).value = value
102+
elif key == "rooms":
103+
self.rooms = msgpack.unpackb(zlib.decompress(base64.b64decode(value)), raw=False)
90104

91105
@classmethod
92106
def stage_generate_early(cls, multiworld):
@@ -105,6 +119,10 @@ def stage_generate_early(cls, multiworld):
105119
rooms_data = {}
106120

107121
for world in multiworld.get_game_worlds("Final Fantasy Mystic Quest"):
122+
if world.ut:
123+
if not world.rooms:
124+
world.rooms = rooms
125+
continue
108126
if (world.options.map_shuffle or world.options.crest_shuffle or world.options.shuffle_battlefield_rewards
109127
or world.options.companions_locations or world.options.overworld_shuffle):
110128
if world.options.map_shuffle_seed.value.isdigit():
@@ -121,7 +139,7 @@ def stage_generate_early(cls, multiworld):
121139
kaeli_mom = world.options.kaelis_mom_fight_minotaur.current_key
122140
overworld_shuffle = world.options.overworld_shuffle.current_key
123141

124-
query = f"s={seed}&m={map_shuffle}&c={crest_shuffle}&b={battlefield_shuffle}&cs={companion_shuffle}&km={kaeli_mom}&os={overworld_shuffle}"
142+
query = f"s={seed}&m={map_shuffle}&c={crest_shuffle}&b={battlefield_shuffle}&cs={companion_shuffle}&km={kaeli_mom}&os={overworld_shuffle}&version=1.7"
125143

126144
if query in rooms_data:
127145
world.rooms = rooms_data[query]
@@ -250,4 +268,17 @@ def extend_hint_information(self, hint_data):
250268
if location.address in hint_data[self.player]:
251269
hint_data[self.player][location.address] += f"/{hint}"
252270
else:
253-
hint_data[self.player][location.address] = hint
271+
hint_data[self.player][location.address] = hint
272+
273+
def fill_slot_data(self):
274+
ret = self.options.as_dict("logic", "sky_coin_mode", "shattered_sky_coin_quantity", "map_shuffle")
275+
if self.rooms != rooms:
276+
compressed_rooms = base64.b64encode(zlib.compress(msgpack.packb(self.rooms, use_bin_type=True))).decode("ascii")
277+
ret["rooms"] = compressed_rooms
278+
else:
279+
ret["rooms"] = None
280+
return ret
281+
282+
@staticmethod
283+
def interpret_slot_data(slot_data):
284+
return slot_data

worlds/ffmq/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
msgpack

0 commit comments

Comments
 (0)