mirror of
https://github.com/valentderah/totk-pc-recomp.git
synced 2026-11-11 01:41:02 +00:00
Merge #900: lay the action prompt's diamond out as an Xbox pad with the xbox-label prompts
This commit is contained in:
commit
71d8b0cc61
2 files changed
+94
-4
No files matched your search
+1
-1
@@ -46,7 +46,7 @@ Esc is unbound in the game; it opens the settings menu and releases the mouse cu
|
||||
| Plus, Minus | Menu, View | Menu, View |
|
||||
| D-pad | cross | cross |
|
||||
|
||||
The action prompt (`DoCommand_00`) shows its button in a diamond whose shape gives the face buttons' positions as on a Switch, with the other three letters around it. With `nintendo` its letters are swapped with the rest and every one names the button in that place. With `xbox` and `auto` the letters are right but the diamond keeps the Switch positions: A shows on its right, while the button labelled A is at the bottom. The sticks keep the Switch icons, and so do prompts drawn as fixed images (some tutorial animations).
|
||||
The action prompt (`DoCommand_00`) shows its button in a diamond whose shape gives the face buttons' positions as on a Switch, with the other three letters around it. With `nintendo` its letters are swapped with the rest and every one names the button in that place. With `xbox` and `auto` the letters stay and the diamond is laid out as an Xbox pad: A at the bottom, B on the right, X on the left and Y at the top, the button the prompt names lit in its place, its bar reaching left from X and right from the others (`tools/make_romfs_patches.py`, `bflyt_xbox_diamond`: the panes of `blyt/DoCommand_00.bflyt` take their partner's place, A's of B and X's of Y). The sticks keep the Switch icons, and so do prompts drawn as fixed images (some tutorial animations).
|
||||
|
||||
The prompts are part of the game's fonts, textures and messages, so they follow the layout and not the bindings. A button remapped with a `bind_` line or on the Controls tab keeps the prompt the game draws for it, which then names the wrong button. Use the Xbox prompts with the default bindings.
|
||||
|
||||
|
||||
@@ -35,8 +35,9 @@ from the game's own files on the player's machine, nothing added (tools/button_i
|
||||
stays right.
|
||||
- every other prompt: the glyphs U+E040 A, U+E041 B, U+E042 X, U+E043 Y of the button font trade places.
|
||||
- xbox-label, for pad_layout = xbox and auto. Those layouts put Switch A on the button labelled A (auto picks xbox for
|
||||
every pad not labelled the Nintendo way), so the face buttons keep their letters; the action prompt's diamond keeps
|
||||
the Switch positions (A on the right).
|
||||
every pad not labelled the Nintendo way), so the face buttons keep their letters. The action prompt's diamond is
|
||||
laid out as an Xbox pad's (bflyt_xbox_diamond): A at the bottom, B on the right, X on the left, Y at the top, the
|
||||
button the prompt names lit in its place with its bar.
|
||||
--xbox-prompts is the older name of --prompts xbox.
|
||||
--prompts pc: keyboard and mouse prompts, for prompts = pc (romfs-patches-pc, or romfs-patches-pc-<w>x<h> with
|
||||
--aspect): every button of the button font is drawn as the key or mouse button the keyboard defaults put on it
|
||||
@@ -170,6 +171,92 @@ def bflyt_letters(data, base, end, swap):
|
||||
return changed
|
||||
|
||||
|
||||
def bflyt_panes(data, base, end):
|
||||
"""name -> absolute offset of each pane section (pan1, pic1, txt1, wnd1, ...) of the BFLYT at data[base:end], and
|
||||
name -> absolute offset of each material of its mat1, and its texture names (txl1)."""
|
||||
assert data[base:base + 4] == b'FLYT'
|
||||
pos, panes, mats, textures = base + struct.unpack_from('<H', data, base + 6)[0], {}, {}, []
|
||||
while pos + 8 <= end:
|
||||
magic, size = data[pos:pos + 4], struct.unpack_from('<I', data, pos + 4)[0]
|
||||
if magic in (b'pan1', b'pic1', b'txt1', b'wnd1', b'bnd1', b'prt1', b'ali1', b'scr1'):
|
||||
panes[data[pos + 12:pos + 36].split(b'\0')[0].decode()] = pos
|
||||
elif magic == b'mat1':
|
||||
for i in range(struct.unpack_from('<H', data, pos + 8)[0]):
|
||||
at = pos + struct.unpack_from('<I', data, pos + 12 + 4 * i)[0]
|
||||
mats[data[at:at + 28].split(b'\0')[0].decode()] = at
|
||||
elif magic == b'txl1':
|
||||
for i in range(struct.unpack_from('<H', data, pos + 8)[0]):
|
||||
at = pos + 12 + struct.unpack_from('<I', data, pos + 12 + 4 * i)[0]
|
||||
textures.append(data[at:data.index(0, at)].decode())
|
||||
if not size:
|
||||
break
|
||||
pos += size
|
||||
return panes, mats, textures
|
||||
|
||||
|
||||
# The action prompt's diamond (blyt/DoCommand_00.bflyt) for pad_layout = xbox and auto, where Switch A, B, X and Y are
|
||||
# the buttons labelled so on an Xbox pad: A at the bottom, B on the right, X on the left, Y at the top. The layout has
|
||||
# one diamond per button the prompt can name (N_A_00, N_B_00, N_X_00, N_Y_00; N_StopA_00 is A's while held), each
|
||||
# with its button and bar (N_Btn<K>_00), the three other letters (T_<letter>_<n>) and a base image (P_Base<K>_00)
|
||||
# that lights the button's side. A Switch button's place on an Xbox pad is that of its partner on a Switch (A of B,
|
||||
# X of Y), so each piece takes the place and look its partner's piece has: the bar of a button on the left reaches
|
||||
# left (Y's on a Switch), the others reach right.
|
||||
DIAMOND_SLOTS = {'A': (39.0, 1.0), 'B': (2.0, -39.0), 'X': (1.0, 39.0), 'Y': (-38.0, 0.0)} # the letters' places
|
||||
DIAMOND_PARTNER = {'A': 'B', 'B': 'A', 'X': 'Y', 'Y': 'X'}
|
||||
# (pane, the pane whose place it takes): the buttons with their bars, and the flashes of a repeated press (Rush)
|
||||
DIAMOND_MOVES = [('N_BtnA_00', 'N_BtnB_00'), ('N_BtnA_01', 'N_BtnB_00'), ('N_CompanionBtnA_00', 'N_BtnB_00'),
|
||||
('N_BtnB_00', 'N_BtnA_00'), ('N_BtnX_00', 'N_BtnY_00'), ('N_BtnY_00', 'N_BtnX_00')]
|
||||
DIAMOND_MOVES += [(f'P_Capture{k}_{n:02}', f'P_Capture{DIAMOND_PARTNER[k]}_{n:02}') for k in 'ABXY' for n in (1, 2, 3)]
|
||||
# X and Y trade the side their bar reaches to: the flash, the bar (Y's mirrored) and the text (Y's right-aligned)
|
||||
DIAMOND_MIRROR = [('P_CaptureX_00', 'P_CaptureY_00'), ('W_BaseX_00', 'W_BaseY_00'), ('T_GuideX_00', 'T_GuideY_00'),
|
||||
('T_GuideX_TW', 'T_GuideY_TW')]
|
||||
# the base image: its texture (by its material) and its rotation and scale
|
||||
DIAMOND_BASES = [('P_BaseA_00', 'P_BaseB_00'), ('P_BaseA_01', 'P_BaseB_00'), ('P_BaseB_00', 'P_BaseA_00'),
|
||||
('P_BaseX_00', 'P_BaseY_00'), ('P_BaseY_00', 'P_BaseX_00')]
|
||||
|
||||
|
||||
def bflyt_xbox_diamond(data, base, end):
|
||||
"""Lay the action prompt's diamond in the BFLYT at data[base:end] (blyt/DoCommand_00.bflyt) out as an Xbox pad's
|
||||
face buttons (DIAMOND_* above); returns the panes changed. A pane section holds its origin at +0x09, translation
|
||||
(3 floats) at +0x2C, rotation (3) at +0x38 and scale (2) at +0x44; a text pane its text position at +0x5C; a
|
||||
picture pane its material index at +0x64, and a material its first texture's index at +0x2C."""
|
||||
panes, mats, textures = bflyt_panes(data, base, end)
|
||||
old = bytes(data) # every piece takes its partner's place as the game has it
|
||||
changed = []
|
||||
for name, at in panes.items():
|
||||
m = re.fullmatch(r'T_([ABXY])_\d+', name)
|
||||
if m: # a letter around a diamond: its partner's place
|
||||
struct.pack_into('<2f', data, at + 0x2C, *DIAMOND_SLOTS[DIAMOND_PARTNER[m[1]]])
|
||||
changed.append(name)
|
||||
for name, like in DIAMOND_MOVES:
|
||||
data[panes[name] + 0x2C:panes[name] + 0x34] = old[panes[like] + 0x2C:panes[like] + 0x34]
|
||||
changed.append(name)
|
||||
# A's hold flashes (N_LoopA_03) sit 129 right of the diamond, as A's repeated-press ones: the same move as those
|
||||
dx, dy = (a - b for a, b in zip(struct.unpack_from('<2f', old, panes['P_CaptureB_01'] + 0x2C),
|
||||
struct.unpack_from('<2f', old, panes['P_CaptureA_01'] + 0x2C)))
|
||||
x, y = struct.unpack_from('<2f', old, panes['N_LoopA_03'] + 0x2C)
|
||||
struct.pack_into('<2f', data, panes['N_LoopA_03'] + 0x2C, x + dx, y + dy)
|
||||
changed.append('N_LoopA_03')
|
||||
for a, b in DIAMOND_MIRROR:
|
||||
pa, pb = panes[a], panes[b]
|
||||
for lo, hi in ((0x09, 0x0A), (0x2C, 0x4C)) + (((0x5C, 0x5D),) if old[pa:pa + 4] == b'txt1' else ()):
|
||||
data[pa + lo:pa + hi], data[pb + lo:pb + hi] = old[pb + lo:pb + hi], old[pa + lo:pa + hi]
|
||||
changed += [a, b]
|
||||
in_order = list(mats.values())
|
||||
|
||||
def material(name): # the offset of a picture pane's material
|
||||
return in_order[struct.unpack_from('<H', old, panes[name] + 0x64)[0]]
|
||||
|
||||
for name, like in DIAMOND_BASES:
|
||||
assert old[panes[name]:panes[name] + 4] == b'pic1', name
|
||||
data[panes[name] + 0x38:panes[name] + 0x4C] = old[panes[like] + 0x38:panes[like] + 0x4C]
|
||||
mine, theirs = material(name), material(like)
|
||||
data[mine + 0x2C:mine + 0x2E] = old[theirs + 0x2C:theirs + 0x2E]
|
||||
assert textures[struct.unpack_from('<H', data, mine + 0x2C)[0]].startswith('Nt_KeyBtnSubBase'), name
|
||||
changed.append(name)
|
||||
return changed
|
||||
|
||||
|
||||
def msbt_letters(data, swap):
|
||||
"""Swap the one-letter messages labelled T_<letter>_<n> (A, B, X or Y) in the UTF-16 MSBT data (a bytearray) by
|
||||
swap; the labels changed. The game sets a layout's text panes from the messages of the same name in
|
||||
@@ -319,6 +406,9 @@ if args.prompts:
|
||||
letters = {}
|
||||
if by_position:
|
||||
bntx_swap(blarc, bntx, KEY_TEXTURES)
|
||||
diamond = []
|
||||
if args.prompts == 'xbox-label':
|
||||
diamond = bflyt_xbox_diamond(blarc, *members['blyt/DoCommand_00.bflyt'])
|
||||
if letter_panes:
|
||||
for member, (lo, hi) in members.items():
|
||||
if member.endswith('.bflyt'):
|
||||
@@ -350,7 +440,7 @@ if args.prompts:
|
||||
if grown:
|
||||
outputs[RESTBL] = icons.grow_restbl(unpack(RESTBL), grown)
|
||||
prompts = dict(style=args.prompts, textures=KEY_TEXTURES if by_position else [], letters=letters,
|
||||
messages=messages, hud=hud, icons=len(drawn), fonts=FONTS, grown=grown)
|
||||
diamond=len(diamond), messages=messages, hud=hud, icons=len(drawn), fonts=FONTS, grown=grown)
|
||||
|
||||
lines = []
|
||||
for path, contents in outputs.items():
|
||||
|
||||
Reference in new issue
Block a user