# Legacy Recon 05 — Hexsling Selection Rule & `dabypass` Semantics

Evidence build: **`extracted/client-5-8`** (BN Online Client 5-8) and
**`extracted/server-5-4`** (BN Online Master 5-4) — the only two builds present as
extracted plain-text GML. Everything else under `legacy/source-archive/` and
`legacy/servers/` is binary `.gmd`/`.exe`/`.gb1` (verified compressed binary, not
plain text) and cannot be examined without running the extraction pipeline.

---

## 1. HEXSLING SELECTION RULE

**Definitive answer:** In client-5-8 the hexsling does **NOT** "prefer the
direction the player was not travelling." The actual rule is:

- **0 or 1 enabled exit directions** (`up+down+left+right <= 1`): the sling
  auto-redirects the Navi to the single enabled direction (no choice, no
  "opposite of travel" preference).
- **2 or more enabled exit directions** (`> 1`): the sling **stops the Navi and
  waits for arrow-key input** (`keywait = 1`); the player then presses an arrow
  that matches an enabled direction to leave.

There is no direction-reversal / "anti-travel" logic anywhere in the hexsling.
The user's "prefer NOT travelling" description corresponds to the **HEXBRIDGE**
(`hexbridge`/`hxbtsideb`) bounce, which is a *different* object — that one DOES
reverse the current direction, but only when the bridge has **zero** enabled
arrows. (This matches the prior recon pass.) The "1–2 vs 3–4" thresholds the
user recalled do not exist; the only threshold in 5-8 is `<= 1` (auto) vs `> 1`
(wait for input).

### The hexsling redirect lives on object `0417-hxstsideb`

`hxstsideb` is the bottom-tile collision body of the hexsling
(`hexsling_any`, id 415, child of `hxtsideb` id 350). Its `up/down/left/right`
flags are copied from the parent sling instance in the Collision-415 event
(`if(!gotdirs){ up=other.up; … }`).

**Selection logic — `objects/0417-hxstsideb/events/Collision-0.gml` (collision
with the moving hexporting Navi = `server` while `sprite_index == Hexport`):**

```gml
// Collision-0.gml (hexsling collision with hexporting Navi)
if(server.sprite_index == Hexport && !server.hxdeny && server.fspeed != 0 && !keywait)
{
server.fspeed = 0;
server.x = x;
server.y = y-40;
if(up+down+left+right <= 1)          // 0 or 1 enabled exits → auto-pick
{
if(up) server.hxpdir = 0;
else if(down) server.hxpdir = 1;
else if(left) server.hxpdir = 2;
else if(right) server.hxpdir = 3;
server.hxdeny = 1;
server.alarm[2] = 10;
}
else keywait = 1;                    // 2+ exits → halt and wait for arrow key
}
```

**Wait-for-input path — the four KeyPress events on the same object** (37=Left,
38=Up, 39=Right, 40=Down). Each fires only when `keywait` is set AND that
direction is enabled, e.g.:

```gml
// KeyPress-38.gml (Up arrow)
if(up && keywait)
{
keywait = 0;
server.hxpdir = 0;
server.hxdeny = 1;
server.alarm[2] = 5;
}
```

(KeyPress-37 → `hxpdir=2` left, KeyPress-39 → `hxpdir=3` right, KeyPress-40 →
`hxpdir=1` down — all identical shape.)

`hxpdir` is then converted to motion by **`objects/0000-server/events/Alarm-2.gml`**:
`hxpdir 0→dir 90 (up)`, `1→270 (down)`, `2→180 (left)`, `3→0 (right)`, each with
`fspeed = 0.2`.

### Contrast: the HEXBRIDGE bounce DOES reverse (different object)

**`objects/0445-hxbtsideb/events/Collision-0.gml`:**

```gml
if(up+down+left+right == 0)          // bridge with NO arrows → reverse travel
{
if(server.hxpdir == 1) server.hxpdir = 0;
else if(server.hxpdir == 0) server.hxpdir = 1;
else if(server.hxpdir == 3) server.hxpdir = 2;
else if(server.hxpdir == 2) server.hxpdir = 3;
}
else if(up) server.hxpdir = 0;       // otherwise pick the enabled arrow
else if(down) server.hxpdir = 1;
else if(left) server.hxpdir = 2;
else if(right) server.hxpdir = 3;
```

This is the only "opposite direction" behavior in the hexport system, and it is
on the hexbridge, not the hexsling.

### Entry portal (for completeness — not the sling)

The hexporter ENTRY (`0414-hexporter_any` KeyRelease-35) picks `hxpdir` from the
Navi's facing (`server.direction`) when totdir>1, or the single arrow when
totdir==1 — this is initial entry, not mid-flight redirection.

### Older revisions

Cannot be checked. `legacy/source-archive/BNO/` contains client revisions
5-01, 5-1, 5-2, 5-3 … as binary `.gmd` files (confirmed compressed binary, e.g.
`BN Online Client 5-2.gmd` begins with non-printable compressed bytes). The
extraction pipeline (`decomp/wiki/15-extraction-pipeline.md`) would have to be
run on them to inspect their GML. **If the "prefer not-travelling, 1–2 vs 3–4"
behavior ever existed, it is in a pre-5-8 revision that has not been extracted.**
What's missing to settle that: an extracted GML tree for an earlier client
(≤ 5-3) — request extraction if this distinction matters for parity.

---

## 2. `dabypass` SEMANTICS

**Definitive answer:** `dabypass` is a **"trapped / sticky" flag, NOT an
exemption.** When set, the player respawns **in** the Digital Abyss on every
login until they leave room 49 (climb out). The prior pass that called it an
exemption is wrong; the "stick in the Abyss" reading is correct.

It is a **per-user, server-persisted** byte (one slot per uid), stored as the
first field of the user's `.bnu` inventory save.

### Server sets/clears it on room change — `server-5-4/scripts/0359-server_receive.gml:72-73`

```gml
global.p_room[pid] = tempint;
if(tempint == 49) global.dabypass[global.p_uid[pid]] = 1;   // entered Digital Abyss
else global.dabypass[global.p_uid[pid]] = 0;                 // any other room clears it
```

(Room index **49 = `Digital_Abyss`**, confirmed via
`extracted/client-5-8/rooms/0049-Digital_Abyss`.) So entering the Abyss arms the
flag; moving to any non-49 room disarms it (the "climb out" reset).

### Server sends it to the client at login — `0359-server_receive.gml:206`

```gml
writeint(global.playerudpport);
writebyte(global.dabypass[global.p_uid[pid]]);   // sent right after UDP port
writeint(global.latestclient);
```

### Server persists it to the `.bnu` save (first field)

`server-5-4/scripts/0376-uinv_backup.gml:4`
```gml
userfile = file_text_open_write("UserData\Inv\Inventory_"+string(argument0)+".bnu");
file_text_write_real(userfile,global.dabypass[argument0]);   // FIRST value written
```

`server-5-4/scripts/0377-uinv_restore.gml:8`
```gml
global.dabypass[argument0] = file_text_read_real(userfile);  // FIRST value read on load
```

### Client reads it at login — `client-5-8/scripts/0094-begin_client_receive.gml:65`

```gml
global.loc_udpsocket = udpconnect(global.loc_udpport, true);
global.dabypass = readbyte();      // matches server writebyte order at :206
global.latestclient = readint();
```

### Client acts on it at spawn — `client-5-8/objects/0045-commandob/events/Create.gml:25-46`

```gml
if(global.homedata != -1 && !global.gonehome && !global.dabypass)
{
// ... spawn at HOME (jid 100, home room/area) ...
}

if(global.dabypass)
{
global.gonehome = 1;
online_room(Digital_Abyss);        // dabypass set → spawn IN the Abyss
}
```

This is the deciding code: `!dabypass` is *required* to go home, and `dabypass`
**forces** `online_room(Digital_Abyss)`. The flag therefore **keeps the player
in the Abyss across logins**, the opposite of an exemption.

### Relationship to `dafalls` (the fall counter)

`dafalls` is **client-only and session-only** — the server never reads or writes
it. In `client-5-8/objects/0000-server/events/Step.gml` (the "Fall off edge"
block, lines 165-205):

```gml
global.dafalls += 1;
...
if(ceil(rand) > 9 || global.dafalls <= 5)   // first 5 falls (or 90% RNG) → respawn in place
{ ... global.p_room[99] = room; }
else                                          // 6th+ fall & 9% roll → into the Abyss
{
  if(global.duopid == -1 || !global.duorole)
  {
    global.dafalls = 0;                       // reset counter
    global.p_area[99] = "Abyssal Ruin";
    global.p_room[99] = Digital_Abyss;        // client moves itself to room 49
  }
}
```

So the chain is: repeated falls → `dafalls` exceeds 5 and a ~9% roll hits →
client warps itself into `Digital_Abyss` (room 49) → server's room-change handler
(`server_receive.gml:72`) sees room 49 and sets the persistent
`dabypass[uid] = 1` → it is saved to `.bnu` → next login the player is dropped
back into the Abyss until they walk out into another room (which clears it).

`dafalls` = transient "are you about to fall into the Abyss" accumulator;
`dabypass` = persistent "you are currently stuck in the Abyss" flag.

---

## Source-of-truth file:line index

| Claim | File:line |
|---|---|
| Hexsling auto vs wait threshold | `extracted/client-5-8/objects/0417-hxstsideb/events/Collision-0.gml:2-17` |
| Hexsling wait-for-arrow inputs | `…/0417-hxstsideb/events/KeyPress-{37,38,39,40}.gml` |
| Hexsling inherits dir flags | `…/0417-hxstsideb/events/Collision-415.gml:2-9` |
| hxpdir → motion | `…/0000-server/events/Alarm-2.gml:5-24` |
| Hexbridge reversal (contrast) | `…/0445-hxbtsideb/events/Collision-0.gml:8-18` |
| `dabypass` set on room 49 | `extracted/server-5-4/scripts/0359-server_receive.gml:72-73` |
| `dabypass` sent at login | `…/0359-server_receive.gml:206` |
| `dabypass` persisted (write) | `…/0376-uinv_backup.gml:4` |
| `dabypass` persisted (read) | `…/0377-uinv_restore.gml:8` |
| Client reads `dabypass` | `extracted/client-5-8/scripts/0094-begin_client_receive.gml:65` |
| Client spawns in Abyss if set | `…/0045-commandob/events/Create.gml:25-46` |
| `dafalls` fall logic (client-only) | `…/0000-server/events/Step.gml:165-205` |
| Room 49 = Digital_Abyss | `extracted/client-5-8/rooms/0049-Digital_Abyss/` |
