aboutsummaryrefslogtreecommitdiffstats
path: root/slop_drive.sh
blob: 3ca019d70f40b75938c1b2e77ead152a50a4529e (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#!/usr/bin/env bash
# Robust driver for the SL0P menu: verify each state transition via WRAM
# sidechannels before sending more input, instead of racing the menu poll.
#   sidechannels:
#     0xCFCA wUpdateSpritesEnabled : 01 = overworld, ff = SL0P menu / EXPLORE / saver
#     0xCC26 wCurrentMenuItem      : menu cursor index (valid while a menu is open)
#     0xD35D wCurMap  0xD360 wYCoord  0xD361 wXCoord
SOCK=$(./gbctl list | grep -o 'sock=[^ ]*' | cut -d= -f2)
rd(){ ./gbctl read "$1" "${2:-1}" | cut -d' ' -f5- | tr -d ' '; }
# reliable sidechannels: player sprite present in OAM => overworld; the "SL0P
# MENU" title glyph at wTileMap(5,0)=0xC3A5 == 0x92 ('S') => the SL0P menu is up.
# (wUpdateSpritesEnabled is NOT reliable: the top menu leaves it at 01.)
in_ow(){ [ "$(rd 0xC300 1)" != "00" ]; }
menu_up(){ [ "$(rd 0xC3A5 1)" = "92" ]; }
cursor(){ printf '%d' "0x$(rd 0xCC26 1)"; }

# back out to the overworld (press B until the player sprite is drawn again)
reset_ow(){
  for _ in $(seq 1 10); do
    in_ow && return 0
    ./gbctl press b:6 >/dev/null; sleep 0.5
  done
  echo "reset_ow: FAILED" >&2; return 1
}

# confirm we're actually in the overworld and idle (coords stable across a beat)
wait_ow(){
  for _ in $(seq 1 40); do
    if in_ow; then
      a=$(rd 0xD360); sleep 0.25; b=$(rd 0xD360)
      [ "$a" = "$b" ] && { echo "overworld ready: map=$(rd 0xD35D) Y=$(rd 0xD360) X=$(rd 0xD361)"; return 0; }
    fi
    sleep 0.25
  done
  echo "wait_ow: FAILED" >&2; return 1
}

# open the SL0P menu (hold SELECT, verify via the title glyph, retry if not)
open_menu(){
  for _ in $(seq 1 12); do
    menu_up && { echo "menu open"; return 0; }
    ./gbctl press select:6 >/dev/null; sleep 0.6
  done
  echo "open_menu: FAILED" >&2; return 1
}

# move the menu cursor to index $1, one press at a time, verifying each step
menu_to(){
  local target=$1 c
  for _ in $(seq 1 50); do
    c=$(cursor)
    [ "$c" -eq "$target" ] && { echo "cursor=$target"; return 0; }
    if [ "$c" -lt "$target" ]; then ./gbctl press down:4 >/dev/null; else ./gbctl press up:4 >/dev/null; fi
    sleep 0.4
  done
  echo "menu_to: FAILED (cursor=$c want $target)" >&2; return 1
}

# press A on the current menu item
menu_select(){ ./gbctl press a:5 >/dev/null; sleep 0.9; }