diff --git a/.opencode/skills/kq4-room-documenter/SKILL.md b/.opencode/skills/kq4-room-documenter/SKILL.md new file mode 100644 index 0000000..e7e77ad --- /dev/null +++ b/.opencode/skills/kq4-room-documenter/SKILL.md @@ -0,0 +1,326 @@ +--- +name: kq4-room-documenter +description: Generate comprehensive documentation for King's Quest IV rooms from decompiled SCI source code. Creates formatted markdown files following a standardized template with high-level summaries, look descriptions, interactions tables, scripts tables, and technical notes. +--- + +# KQ4 Room Documenter + +Generate comprehensive documentation for King's Quest IV rooms from decompiled SCI source code. + +## When to Use + +Use this skill when: +- Documenting a new room from the KQ4 decompiled source +- Converting rm##.sc files to human-readable documentation +- Extracting interactions, scripts, and game logic from Sierra Script files +- Creating standardized room documentation for the remake project + +## Default Configuration + +**Model Provider**: Local +**Model**: MiniMax-M2.5 + +## Documentation Format + +Generate documentation following this exact structure: + +### 1. Header +Format: `# Room ##: [Room Name]` + +**Filename**: `kq4-##-human-readable-name.md` + +### 2. High-Level Summary +A paragraph describing the room's purpose, key features, NPCs, and gameplay significance. + +### 3. Look Description Section +```markdown +## Look Description + +"[The room's primary description text when player looks around]" +``` + +### 4. Interactions Table +```markdown +## Interactions + +| Status | Behavior Type | Command | Response | +|--------|--------------|---------|----------| +| Documented | [Look/Get/Action/Talk/Inventory] | `[command]` | "[Response text]" | +``` + +**Status Options:** +- `Documented` - Interaction fully documented with response text +- `Partial` - Interaction exists but response text not extracted +- `Unverified` - Interaction referenced but not confirmed in code + +**Behavior Types:** +- `Look` - Visual examination commands (look, examine) +- `Get` - Item acquisition attempts (take, get, pick up) +- `Action` - Physical interactions (open, close, use, climb, kill, etc.) +- `Talk` - Dialogue commands (talk, ask, tell) +- `Inventory` - Item usage commands (use, give, deliver) + +**Command Format:** +- Use backticks around commands +- Show alternative phrasings with `/` separator +- Include optional words in brackets: `[]` +- Show synonyms where applicable +- Document unusual/unexpected command words (e.g., `dennis crown` instead of `use crown`) + +**Response Format:** +- Include full text in quotes +- Reference message file: `(Print ## #)` + +### 5. Scripts Table +```markdown +## Scripts + +| Status | Behavior Type | Name | Trigger | Behavior | +|--------|--------------|------|---------|----------| +| Documented | [Interaction/Background] | [ScriptName] | [Trigger condition] | [Behavior description] | +``` + +**Behavior Types:** +- `Interaction` - Player-triggered scripts (entry, exits, dialogue, cutscenes) +- `Background` - Automatic/continuous scripts (animations, AI, environmental effects) + +**Name:** The script class name from source (e.g., `SendOut`, `Tripped`, `Watch`) + +**Trigger:** When the script activates (e.g., "Entering room when `dwarf_state = DWARF_STATE.HOSTILE`") + +**Behavior:** What the script does, described clearly + +### 6. Technical Notes Section +```markdown +## Technical Notes + +- **Room Number**: ## +- **Picture**: ### +- **Region**: ### ([Region Name]) - if applicable +- **Exits**: [Direction→Room, ...] +- **Music**: [Sound number/name] - if applicable + +### State Variables + +| Variable | Values | Description | +|----------|--------|-------------| +| `[variable_name]` | `[values]` | [Description] | +``` + +**Variable Naming:** +- Convert `global###` to descriptive names +- Use enums where applicable: `dwarf_state = DWARF_STATE.FRIENDLY` +- Use boolean names: `diamonds_visible`, `room_visited` + +### Additional Technical Details + +- List synonyms defined in room: `[word] = [synonym]` +- List region synonyms if applicable +- Note special mechanics or behaviors +- Document entry points and conditions +- List all applied regions when multiple exist: `setRegions: ### ### ###` +- Document inventory item requirements: `(gEgo has: ##)` → map to item names +- Document view-based state detection (see Player State Patterns below) + +## Workflow + +### Step 1: Locate Source Files + +1. Find the room script: `KQ4_v1.006.004_int0.000.502_SRC_(6)/src/rm##.sc` +2. Find the text file: `strings/text_###.txt` +3. Find the region script if applicable: `KQ4_v1.006.004_int0.000.502_SRC_(6)/src/rg###.sc` + +### Step 2: Read Source Files + +1. Read the room script to understand: + - Room properties (picture, style, exits) + - Init method (objects, NPCs, initial state) + - HandleEvent method (interactions) + - Scripts (instances of Script class) + - State variables and conditions + +2. Read the text file to extract: + - Message text referenced by `Print` commands + - Index numbers correspond to second parameter of Print + +3. Read region script if room uses `setRegions:` to understand: + - Shared interactions + - Shared state + - Additional commands + +### Step 3: Extract Interactions + +From `handleEvent` method: + +1. Identify all `Said` patterns +2. Map them to corresponding `Print` commands +3. Look up message text in strings/text_###.txt +4. Categorize by behavior type (Look, Get, Action, Talk, Inventory) +5. Format commands with proper syntax showing alternatives + +### Step 4: Extract Scripts + +From Script instances: + +1. Identify each Script class instance +2. Determine if Interaction or Background type +3. Document trigger conditions +4. Describe behavior in detail +5. Note any state changes + +### Step 5: Identify State Variables + +From local variables and global references: + +1. Convert `global###` to descriptive names +2. Identify boolean flags +3. Identify enum states +4. Document their purpose and values + +### Step 6: Write Documentation + +Follow the format exactly: +1. Header with room name +2. High-level summary +3. Look description +4. Interactions table +5. Scripts table +6. Technical notes with state variables + +### Step 7: Save File + +Save to: `rooms/kq4-###-human-readable-name.md` + +### Step 8: Update README.md + +After saving the room documentation: +1. Read the current README.md to find the Progress section +2. Find the row for this room number +3. Update the Status to `[DONE](./rooms/kq4-###-human-readable-name.md)` +4. Fill in the Room Description with a human-readable name (e.g., "Beach", "Fountain Pool", "Cave Entrance") + +## Example Reference + +See `rooms/055-seven-dwarfs-diamond-mine.md` for a complete example following this format. + +## Command Reference + +### SCI Script Patterns + +**Room Definition:** +``` +(instance Room## of Rm + (properties + picture ## + style $0010 + east ## + ... + ) +``` + +**Edge Behavior:** +- `(gEgo edgeHit: 0)` - Reset edge detection +- `(= horizon ##)` - Set walkable area boundary +- Entry positioning uses `switch gPrevRoomNum` with cases for each direction + +**HandleEvent Pattern:** +``` +(method (handleEvent pEvent) + (cond + ((Said 'look>') + (cond + ((Said '/object') (Print ## #)) + ... + ) + ) + ) +) +``` + +**Script Pattern:** +``` +(instance ScriptName of Script + (method (changeState newState) + (switch (= state newState) + (0 ...) + (1 ...) + ) + ) +) +``` + +**State Checks:** +- `(if global### ...)` - Boolean flag check +- `(if (== global### #) ...)` - Equality check (use for enums) +- `(if (> (gEgo x?) ###) ...)` - Position check + +### Text File Format + +Text files use format: `[####] Message text` +- Index corresponds to second parameter of Print command +- Print 55 2 → Look for [0002] in strings/text_055.txt + +### Player State Patterns + +Player form/state is often determined by view number: +- `view: 2` - Normal form (on land) +- `view: 8` - Swimming form +- `view: 370, 371, 377` - Frog transformation forms +- `view: 44` - Falling/tripping +- Check current view: `(gEgo view?)` in conditions + +Document state variables like: +```markdown +| `is_frog_form` | `true`, `false` | Player is in frog transformation (views 370-377) | +| `in_water` | `true`, `false` | Player is swimming (view 8) or in water | +``` + +### Inventory Item Reference + +Items referenced by index in `gEgo has: ##` or `gInv has: ##`: +- `10` - Crown +- `17` - Fishing pole +- Map other numbers to item names when identified + +### Control Areas + +Walkable/control areas use hex bitmasks: +- `$0010` - Control area (e.g., waterfall zone) +- `$0001` - Terrain type (e.g., shallow water) +- `(gEgo onControl: 0)` - Check control under player +- `(gEgo onControl: 1)` - Check control at player's feet + +### Score Tracking + +Score changes use: `(gGame changeScore: ##)` +- Always paired with a flag to prevent re-awarding +- Example: `(if (== global200 0) (gGame changeScore: 5) (= global200 1))` +- Document as: `transformation_score_awarded` flag + +### Viewer System + +Background player scripts use the viewer system: +- Set viewer: `(gEgo viewer: ScriptName)` +- Remove viewer: `(gEgo viewer: 0)` +- Runs continuously (like a Background script on the player) +- Example: `frogViewer` monitors terrain and changes view accordingly +- Viewer doit method typically checks `(gEgo onControl: 1)` to determine terrain +- Switch views based on terrain: `(gEgo view: 371)` for shallow, `(gEgo view: 377)` for deep + +### Multiple Regions + +Rooms can apply multiple region scripts: +``` +(self setRegions: 501 512 511 508) +``` +Document all applied regions and check each for additional interactions. + +## Output Requirements + +- Use descriptive Status values: "Documented", "Partial", or "Unverified" (NOT "TODO") +- Use clear, descriptive variable names +- Include full message text in quotes +- Organize interactions logically by type +- Be thorough but concise in descriptions +- Filename must be: `kq4-##-human-readable-name.md` (e.g., `kq4-024-waterfall-and-pool.md`) +- After completing the documentation, update README.md with the room description and link diff --git a/README.md b/README.md new file mode 100644 index 0000000..dcf8509 --- /dev/null +++ b/README.md @@ -0,0 +1,345 @@ +# King's Quest IV: The Perils of Rosella - Interaction Documentation + +A comprehensive documentation of all game interactions extracted from the decompiled SCI (Sierra's Creative Interpreter) source code. This project documents the logic, conditions, state changes, and dependencies for recreating King's Quest IV as a modern point-and-click adventure. + +## Repository Structure + +``` +/ +├── rooms/ # Room-specific interaction documentation +│ ├── kq4-001.md # Beach (starting room) +│ ├── kq4-002.md # Beach continuation +│ ├── kq4-007.md # Fisherman's shack exterior +│ ├── kq4-042.md # Fisherman's cottage interior +│ └── ... # One file per room +├── KQ4_v1.006.004_int0.000.502_SRC_(6)/ +│ └── src/ # Decompiled SCI scripts +│ ├── Main.sc # Game engine & global handlers +│ ├── Game.sc # Core game classes +│ ├── Feature.sc # Object interaction framework +│ ├── InvI.sc # Inventory system +│ ├── rm1.sc - rm99.sc # Room scripts +│ └── *.sc # Various utility scripts +└── README.md # This file +``` + +## Source Code Overview + +### File Types + +- **`.sc` files**: Sierra Script source code (main logic) +- **`.sco` files**: Compiled script object files (not human-readable) +- **`game.sh`**: Header file with game constants and defines + +### Key Source Files + +| File | Purpose | +|------|---------| +| `Main.sc` | Global game state, inventory, death handlers, sound management | +| `Game.sc` | Core Game class, save/load system, event handling | +| `Feature.sc` | Base classes for interactive objects (Feature, View, Actor) | +| `InvI.sc` | Inventory item definitions and display logic | +| `Class_255_0.sc` | Low-level system procedures and kernel functions | +| `rm##.sc` | Individual room scripts (rooms 1-99) | +| `*Reg.sc` | Region scripts (shared logic for multiple rooms) | + +### Room Script Anatomy + +Each room script (`rm##.sc`) typically contains: + +``` +Room## of Rm # Room class definition +├── Properties +│ └── picture # Background image number +├── Methods +│ ├── init() # Room initialization +│ │ ├── north/south/east/west # Exit directions +│ │ ├── horizon # Walkable area boundary +│ │ ├── setRegions: # Apply shared region logic +│ │ ├── Props/Actors/Views # Interactive objects +│ │ └── gEgo positioning # Player start position +│ ├── doit() # Per-frame logic (rarely used) +│ ├── handleEvent() # Text parser input handling +│ └── dispose() # Cleanup when leaving room +└── Instances + ├── Scripts # Multi-step sequences + ├── Props # Animated objects + ├── Views # Static scenery + └── Sounds # Audio effects +``` + +## Game Systems + +### Parser Commands + +The original game uses a text parser with verb-noun structure: + +- **`Said 'look/grass'`** - Player typed "look at grass" +- **`Said 'use/key'`** - Player typed "use key" +- **`Said 'take/diamond'`** - Player typed "take diamond" +- **`Said '[ bytes: + """ + Decompress LZW compressed data (SCI0 method 1). + Uses 9-12 bit LZW encoding. + """ + dest = bytearray(length) + + bitlen = 9 # no. of bits to read (max. 12) + bitmask = 0x01ff + bitctr = 0 # current bit position + bytectr = 0 # current byte position + + tokenlist = [0] * 4096 # pointers to dest[] + tokenlengthlist = [0] * 4096 # char length of each token + tokenctr = 0x102 # no. of registered tokens (starts here) + maxtoken = 0x200 # The biggest token + + tokenlastlength = 0 + destctr = 0 + complength = len(src) + + while bytectr < complength: + # Read next token from bit stream + if bytectr >= complength: + break + + tokenmaker = src[bytectr] >> bitctr + if bytectr + 1 < complength: + tokenmaker |= (src[bytectr + 1] << (8 - bitctr)) + if bytectr + 2 < complength: + tokenmaker |= (src[bytectr + 2] << (16 - bitctr)) + + token = int(tokenmaker & bitmask) + + bitctr += bitlen - 8 + + while bitctr >= 8: + bitctr -= 8 + bytectr += 1 + + bytectr += 1 + + if token == 0x101: # terminator + break + + if token == 0x100: # reset command + maxtoken = 0x200 + bitlen = 9 + bitmask = 0x01ff + tokenctr = 0x0102 + else: + if token > 0xff: + if token < tokenctr: + tokenlastlength = tokenlengthlist[token] + 1 + if destctr + tokenlastlength > length: + # Overflow protection + i = 0 + while destctr < length and i < tokenlastlength: + dest[destctr] = dest[tokenlist[token] + i] + destctr += 1 + i += 1 + else: + for i in range(tokenlastlength): + dest[destctr] = dest[tokenlist[token] + i] + destctr += 1 + else: + tokenlastlength = 1 + if destctr < length: + dest[destctr] = token + destctr += 1 + + # Register new token + if tokenctr == maxtoken: + if bitlen < 12: + bitlen += 1 + bitmask = (bitmask << 1) | 1 + maxtoken <<= 1 + + if tokenctr < 4096: + tokenlist[tokenctr] = destctr - tokenlastlength + tokenlengthlist[tokenctr] = tokenlastlength + tokenctr += 1 + + return bytes(dest) + + +def decompress_huffman(src: bytes, length: int) -> bytes: + """ + Decompress Huffman compressed data (SCI0 method 2). + """ + if len(src) < 2: + return src + + dest = bytearray(length) + destctr = 0 + + numnodes = src[0] + terminator = src[1] + bytectr = 2 + (numnodes << 1) + bitctr = 0 + + nodes = src[2:2 + (numnodes << 1)] + + while bytectr < len(src) and destctr < length: + node_idx = 0 + + while nodes[node_idx * 2 + 1] != 0: + if bytectr >= len(src): + break + + value = (src[bytectr] << bitctr) & 0xFF + bitctr += 1 + + if bitctr == 8: + bitctr = 0 + bytectr += 1 + + if value & 0x80: + next_node = nodes[node_idx * 2 + 1] & 0x0f + if next_node == 0: + if bytectr >= len(src): + break + result = (src[bytectr] << bitctr) & 0xFF + bytectr += 1 + if bytectr < len(src): + result |= src[bytectr] >> (8 - bitctr) + result &= 0x0ff + + if result == terminator: + break + + if destctr < length: + dest[destctr] = result + destctr += 1 + break + else: + next_node = nodes[node_idx * 2 + 1] >> 4 + + node_idx += next_node + + if nodes[node_idx * 2 + 1] == 0: + value = nodes[node_idx * 2] | (nodes[node_idx * 2 + 1] << 8) + if value == (0x100 | terminator): + break + if destctr < length: + dest[destctr] = value & 0xFF + destctr += 1 + + return bytes(dest) + + +class SCIResourceExtractor: + """Extracts text resources from SCI game files.""" + + # Resource types + RESOURCE_TYPES = { + 0: "View", + 1: "Pic", + 2: "Script", + 3: "Text", + 4: "Sound", + 5: "Memory", + 6: "Vocab", + 7: "Font", + 8: "Cursor", + 9: "Patch" + } + + # Compression methods + COMPRESSION_METHODS = { + 0: "None", + 1: "LZW", + 2: "Huffman", + 3: "LZW+Huffman" + } + + def __init__(self, game_dir: str): + self.game_dir = Path(game_dir) + self.map_file = self.game_dir / "RESOURCE.MAP" + self.resource_files = sorted(self.game_dir.glob("RESOURCE.0*")) + self.resources = [] + + def read_resource_map(self) -> List[Dict]: + """Read the RESOURCE.MAP file and return list of resource entries.""" + resources = [] + + with open(self.map_file, 'rb') as f: + data = f.read() + + # SCI0 format: 6 bytes per entry + # - 2 bytes: resource number (11 bits) + type (5 bits) + # - 4 bytes: offset (26 bits) + package number (6 bits) + entry_size = 6 + + for i in range(0, len(data), entry_size): + if i + entry_size > len(data): + break + + entry = data[i:i+entry_size] + + # Unpack SCI0 entry + word = struct.unpack('> 11) & 0x1F # 5 bits + + # Check for terminator (all 1s) + if word == 0xFFFF: + dword = struct.unpack('> 26) & 0x3F # 6 bits + + resources.append({ + 'number': res_number, + 'type': res_type, + 'type_name': self.RESOURCE_TYPES.get(res_type, f"Unknown({res_type})"), + 'offset': offset, + 'package': package + }) + + return resources + + def read_resource_header(self, package: int, offset: int) -> Optional[Dict]: + """Read the header of a resource in a package file.""" + resource_file = self.game_dir / f"RESOURCE.{package:03d}" + + if not resource_file.exists(): + return None + + with open(resource_file, 'rb') as f: + f.seek(offset) + header_data = f.read(8) + + if len(header_data) < 8: + return None + + # SCI0 header format + word1 = struct.unpack('> 11) & 0x1F + + compressed_size = struct.unpack(' Optional[bytes]: + """Extract and decompress resource data.""" + resource_file = self.game_dir / f"RESOURCE.{package:03d}" + + if not resource_file.exists(): + return None + + with open(resource_file, 'rb') as f: + f.seek(offset + 8) # Skip header + # For SCI0, compressed_size includes the 4 bytes for cbDecompressed and iMethod + data = f.read(compressed_size - 4) + + if method == 0: + # No compression + return data[:decompressed_size] + elif method == 1: + # LZW compression + return decompress_lzw(data, decompressed_size) + elif method == 2: + # Huffman compression + return decompress_huffman(data, decompressed_size) + else: + print(f"Warning: Unsupported compression method {method}") + return None + + def parse_text_resource(self, data: bytes) -> List[str]: + """Parse a simple text resource (type 3).""" + strings = [] + i = 0 + + while i < len(data): + # Find null-terminated string + start = i + while i < len(data) and data[i] != 0: + i += 1 + + if i > start: + try: + text = data[start:i].decode('latin-1') + strings.append(text) + except: + pass + + i += 1 # Skip null terminator + + return strings + + def parse_message_resource(self, data: bytes) -> List[TextEntry]: + """Parse a message resource (type 10 or embedded text in scripts).""" + entries = [] + + if len(data) < 2: + return entries + + # Check for version indicator + msg_version = struct.unpack(' List[TextEntry]: + """Parse message resource version 2102 (SCI0/early SCI1).""" + entries = [] + + if len(data) < 4: + return entries + + msg_version = struct.unpack(' len(data): + break + + entry = TextEntry() + entry.noun = data[pos] + entry.verb = data[pos + 1] + pos += 2 + + text_offset = struct.unpack(' List[TextEntry]: + """Parse message resource version 3411.""" + entries = [] + + if len(data) < 6: + return entries + + # Skip first 2 bytes (ptr to end of text data) + pos = 2 + + message_count = struct.unpack(' len(data): + break + + entry = TextEntry() + entry.noun = data[pos] + entry.verb = data[pos + 1] + entry.condition = data[pos + 2] + entry.sequence = data[pos + 3] + entry.talker = data[pos + 4] + pos += 5 + + text_offset = struct.unpack(' List[TextEntry]: + """Parse message resource version 4000+ (SCI1.1+).""" + entries = [] + + if len(data) < 6: + return entries + + # Skip offset to end and mystery number + pos = 4 + + message_count = struct.unpack(' len(data): + break + + entry = TextEntry() + entry.noun = data[pos] + entry.verb = data[pos + 1] + entry.condition = data[pos + 2] + entry.sequence = data[pos + 3] + entry.talker = data[pos + 4] + pos += 5 + + text_offset = struct.unpack(' Package {r['package']}, Offset {r['offset']}\n") + + print(f"\nIndex written to {index_file}") + + def _extract_strings_from_binary(self, data: bytes, min_length: int = 5) -> List[str]: + """Extract readable strings from binary data.""" + strings = [] + i = 0 + + while i < len(data): + # Look for printable ASCII sequences + if 32 <= data[i] <= 126: + start = i + while i < len(data) and 32 <= data[i] <= 126: + i += 1 + + if i - start >= min_length: + try: + s = data[start:i].decode('ascii') + strings.append(s) + except: + pass + else: + i += 1 + + return strings + + +def main(): + """Main entry point.""" + # Find the game directory + game_dirs = [ + "King's Quest IV - The Perils of Rosella (1988)/KQ4", + "King's Quest IV - The Perils of Rosella (1988)", + ] + + game_dir = None + for d in game_dirs: + if os.path.exists(d): + game_dir = d + break + + if not game_dir: + print("Error: Could not find game directory") + sys.exit(1) + + print(f"Extracting text from: {game_dir}") + + extractor = SCIResourceExtractor(game_dir) + extractor.extract_all_text("strings") + + print("\nExtraction complete!") + + +if __name__ == "__main__": + main() diff --git a/rooms/001-beach.md b/rooms/001-beach.md new file mode 100644 index 0000000..2b8720c --- /dev/null +++ b/rooms/001-beach.md @@ -0,0 +1,239 @@ +# Room 001: Beach (Starting Area) + +## Overview +- **Room Number**: 1 +- **Script**: `rm1.sc` +- **Picture**: 1 (with overlay 101 if `global100` is set) +- **Region**: Beach +- **Regions Applied**: 503 (BeachReg), 501, 504, 506 + +### Exits +- **North**: Room 25 +- **South**: Room 7 (Fisherman's shack exterior) +- **East**: Room 2 +- **West**: Room 31 +- **Horizon**: 100 (player cannot walk above this Y coordinate) + +### State Dependencies +- `global105`: Controls dolphin riding state and player positioning + - `0`: Normal beach access from various rooms + - `1-4`: Different horizontal positions when entering from north/south + - `14`: Dolphin ride arrival (special entrance) +- `global100`: If set, overlays picture 101 (alternate weather/time?) +- `global205`: Cleared when dolphin ride completes + +## Visual Elements + +### Animated Waves (Background Effects) +Three animated wave Props cycle continuously using `waveActions` script. + +#### wave1 +- **Type**: Prop +- **Position**: (203, 75) +- **View**: 665 +- **Loop**: 0 +- **Animation**: Cycles through frames, then hides, cycles next wave +- **Priority**: 0 (background layer) +- **Behavior**: Creates continuous wave animation effect + +#### wave2 +- **Type**: Prop +- **Position**: (191, 115) +- **View**: 665 +- **Loop**: 1 +- **Animation**: Cycles through frames +- **Priority**: 0 + +#### wave3 +- **Type**: Prop +- **Position**: (191, 188) +- **View**: 665 +- **Loop**: 2 +- **Animation**: Cycles through frames +- **Priority**: 0 + +### Sea Spray / Mist +- **Type**: Prop (`newProp`) +- **Position**: (69, 61) +- **View**: 650 +- **Loop**: 1 +- **Cel**: 1 +- **Animation**: Forward cycle (continuous) +- **Purpose**: Ambient atmospheric effect + +## Player Positioning Logic + +The room uses complex positioning based on `global105` state and `gPrevRoomNum`: + +### Default State (global105 = 0) +- **From South (Room 7)**: + - If x > 128: Position at (300, 187) + - Else: Position at (225, 187) +- **From North (Room 25)**: Position at x=225, y=horizon+yStep+1 +- **New Game (Room 0)**: Position at (220, 135) +- **From East (Room 2)**: + - If y ≤ horizon: x=318, y=horizon+yStep+1 + - Else: x=318, keep current y + +### Dolphin State (global105 = 14) +- Special arrival from dolphin ride +- Sets `global205 = 0` +- Changes player view to 312 (dolphin riding animation) +- Initiates `rideDolphin` script + +## Interactions + +### Look Commands (Parser) + +#### Look at grass +- **Command**: `look grass` / `look at grass` +- **Text**: `Print 1 0` (message resource pending extraction) +- **Logic**: Direct text display, no conditions + +#### Look at brook +- **Command**: `look brook` / `look at brook` +- **Text**: `Print 1 1` (message resource pending extraction) +- **Logic**: Direct text display, no conditions + +#### Look around room +- **Command**: `look` / `look around` / `look room` +- **Text**: `Print 1 2` (message resource pending extraction) +- **Logic**: General room description + +### Region-Based Interactions (via BeachReg - Script 503) + +The following interactions are inherited from the BeachReg region: + +#### Look at ocean/water +- **Command**: `look ocean` / `look water` +- **Conditions**: + - If `gEgo view != 2` (not swimming): Shows one message + - If `gEgo view == 2` (swimming): Shows different message +- **Text**: `Print 503 2` or context-specific messages + +#### Look at fish +- **Command**: `look fish` +- **Conditions**: + - If player has item 24 (fish in inventory): Shows fish item + - Else: Shows default message +- **Text**: `Print 503 3` or item display + +#### Drink water +- **Command**: `drink` / `get drink` +- **Conditions**: + - Must be in swim view (view 2) + - Must be near water (control color check) +- **Effect**: Plays drinking animation (view 21) +- **Script**: `drinking` - 4-state animation sequence + +#### Bathe/Dive/Wade in ocean +- **Command**: `bathe ocean` / `dive ocean` / `wade ocean` +- **Conditions**: + - Only available when `global105 == 4` (dolphin accessible) +- **Response**: Context-aware messaging + +## Scripts + +### rideDolphin +**Purpose**: Handles player arrival via dolphin ride + +**Trigger**: `global105 == 14` on room entry + +**Sequence**: +1. State 1: Disable input (`proc0_8`), move player to (66, 136) +2. State 2: + - Create dolphin Actor + - Set `global105 = 4` + - Restore player viewer + - Position dolphin at player position minus 5 pixels Y + - Set dolphin view 311, loop 2 + - Wait 4 seconds +3. State 3: + - Accelerate dolphin (xStep 4, yStep 3) + - Change to loop 5 + - Move dolphin off-screen to (-10, 100) + - Re-enable input (`proc0_9`) + - Clear `global205` +4. State 4: + - Dispose dolphin Actor + - Clear player script + +**State Changes**: +- Sets `global105 = 4` +- Clears `global205 = 0` + +### waveActions +**Purpose**: Manages continuous wave animation cycle + +**Sequence**: +1. State 0: Initialize - Create static wave Views from wave Prop positions, add to picture +2. State 1: Show next wave Prop, animate to end +3. State 2: Hide wave, advance to next wave in list, loop back to State 1 + +**Behavior**: Infinite loop cycling through wave1 → wave2 → wave3 → wave1... + +## Point-and-Click Adaptation Notes + +### Automated Behaviors +- **Walking**: Player clicks anywhere on walkable area, character auto-walks +- **Horizon**: Enforced automatically; player cannot walk above y=100 +- **Exits**: Walking off screen edges transitions to connected rooms + +### Cursor Mappings + +| Original Command | Point-and-Click Equivalent | +|------------------|---------------------------| +| `look grass` | Eye cursor on grass patch | +| `look brook` | Eye cursor on water/brook area | +| `look around` | Eye cursor on background/scenery | +| `drink water` | Hand cursor on water (when near) | +| `bathe ocean` | Hand cursor on ocean (when dolphin available) | + +### Context-Sensitive Actions +- **Dolphin interaction**: Only show "ride" cursor when `global105 == 4` +- **Swim view**: Automatically switch to swimming animation (view 2) when entering water + +### Visual Feedback +- Waves animate continuously (no player interaction needed) +- Sea spray provides ambient movement +- Dolphin arrival is a scripted sequence (no player input during animation) + +## Unresolved Questions + +1. What is `global100`? (Appears to be weather or time of day flag) +2. What are regions 501, 504, 506? (Need to examine those scripts) +3. What do messages 1 0, 1 1, 1 2 say? (Need text resource extraction) +4. What triggers `global105 = 14` state? (Dolphin ride must be initiated elsewhere) + +## Dependencies + +### Global Variables Used +- `global100`: Weather/time flag +- `global101`: Room visited flag (set to 0) +- `global105`: Beach state / dolphin access +- `global205`: Cleared on dolphin arrival + +### Inventory Items Referenced +- Item 17: Fishing pole (via region) +- Item 24: Fish (via region) + +### Views Referenced +- View 311: Dolphin animation +- View 312: Player on dolphin +- View 665: Wave animations +- View 650: Sea spray + +### Rooms Connected +- North: 25 +- South: 7 +- East: 2 +- West: 31 + +## Critical Path Notes + +This room is part of the starting beach area. It serves as: +1. **Entry point** from various directions +2. **Dolphin transportation hub** (when `global105 == 4`) +3. **Transition zone** between beach, forest, and fisherman's shack + +The dolphin ride appears to be a significant transportation mechanic connecting distant areas of the game. diff --git a/rooms/024-waterfall-and-pool.md b/rooms/024-waterfall-and-pool.md new file mode 100644 index 0000000..3c85231 --- /dev/null +++ b/rooms/024-waterfall-and-pool.md @@ -0,0 +1,67 @@ +# Room 24: Waterfall and Pool + +This room features a magnificent waterfall cascading into a deep pool that flows westward as a rushing river. The area is surrounded by dense forest. The room contains the solution to the frog puzzle - players can use the crown here to transform into a frog and access the underwater cave (Room 70). The waterfall also creates a hazard that can sweep players away if they're not careful. + +## Look Description + +"You see a beautiful waterfall cascading down the mountain into a deep blue pool. From the pool, a river courses westward. Around you, a dense forest closes in." + +## Interactions + +| Status | Behavior Type | Command | Response | +|--------|--------------|---------|----------| +| TODO | Look | `look in falls` | "You can't see through the waterfall." | +| TODO | Look | `look under pool` / `look under water` / `look under falls` | "You can't make out many details from here." (if not in water) or "You look under the water, but can see nothing." (if in water) | +| TODO | Look | `look in water` / `look in pool` | "You look into the water, but see nothing of interest." | +| TODO | Look | `look behind falls` | "You think you see something behind the waterfall, but you're not sure what it is." | +| TODO | Look | `look falls` | "This is a magnificent waterfall! It tumbles into a deep pool, which then flows westward as a rushing river." | +| TODO | Look | `look in pool` | "You peer into the pool of water, but cannot see anything of importance." | +| TODO | Look | `look pool` | "You see a deep pool below the waterfall." | +| TODO | Look | `look around` / `look room` | "You see a beautiful waterfall cascading down the mountain into a deep blue pool. From the pool, a river courses westward. Around you, a dense forest closes in." | +| TODO | Action | `enter behind falls` | "The force of the water is too strong for you to be able to do that." | +| TODO | Action | `bathe under pool` / `bathe under water` / `bathe under falls` / `dive under pool` / `dive under water` / `dive under falls` | "You're not strong enough. The current forces you back!" (if in frog form), "The water isn't deep enough here." (if not in swimming/frog form), "You'd have to be in the water to do that." (otherwise) | +| TODO | Action | `bathe` / `dive` / `wade` | "Just enter the water." (if on land), "You are already swimming." (if swimming), "You are already in the water." (if in water) | +| TODO | Inventory | `dennis crown` / `place crown` | Transforms player into frog form if crown is in inventory and player is on land; "Not while you're IN the water." (if in water) | + +## Scripts + +| Status | Behavior Type | Name | Trigger | Behavior | +|--------|--------------|------|---------|----------| +| TODO | Interaction | egoFrogActions | Using `dennis crown` or `place crown` command while on land and having crown in inventory | Plays poof sound effect, creates transformation animation, changes player view to frog (370), enables frog viewer script, moves player to water's edge, hides player, sets `frog_transformation_used = true`, awards 5 points on first use, transports to Room 70 | +| TODO | Background | frogViewer | Continuous while player is in frog form (`viewer` is set to frogViewer) | Monitors player terrain control - switches view to 371 (shallow water) or 377 (deep water) based on terrain type | +| TODO | Interaction | swept | Walking near waterfall while in swimming form (view 8) and not already in a script | Player is pushed back by waterfall current, displays message "The force of the water pushes you back." | + +## Technical Notes + +- **Room Number**: 24 +- **Picture**: 24 +- **Region**: 501, 512, 511, 508 (Forest/Nature regions) +- **Exits**: North→18, South→30, West→23, East→70 (via frog transformation) +- **Horizon**: 85 (walkable area boundary) +- **Sound**: Poof sound effect (59) played during frog transformation + +### State Variables + +| Variable | Values | Description | +|----------|--------|-------------| +| `is_night` | `true`, `false` | Controls whether night overlay (picture 124) is displayed | +| `room_visited` | `true`, `false` | Tracks if player has entered this room before | +| `frog_transformation_used` | `true`, `false` | Set when player transforms into frog; controls access to Room 70 | +| `transformation_score_awarded` | `true`, `false` | Tracks if 5-point score bonus has been awarded for frog transformation | +| `firefly_phase` | `0`, `1`, `2` | Controls display of firefly/firefly-like props; phase 2 shows additional animated effects | +| `in_water` | `true`, `false` | Player state - determined by view (view 8 = swimming, view 2 = normal, view 370/371/377 = frog) | +| `is_frog_form` | `true`, `false` | Player is in frog transformation form | + +### Additional Technical Details + +- Room uses Regions 501, 512, 511, and 508 for shared forest/nature logic +- Synonyms defined: `pool` = `lake` +- Multiple animated props (views 651, 653) create environmental effects (fireflies/lights) +- Player entry positioning varies by previous room: + - From North: positioned at x=47, just below horizon + - From West: positioned at x=1, respects horizon boundary + - From South: positioned at y=188, x capped at 214 + - From East: positioned at (207, 159) with view 8 (swimming) +- Frog transformation requires crown (inventory item 10) +- Frog form uses different views for shallow water (371) vs deep water (377) +- Waterfall hazard triggers when player in swimming form (view 8) touches control area $0010 diff --git a/rooms/055-seven-dwarfs-diamond-mine.md b/rooms/055-seven-dwarfs-diamond-mine.md new file mode 100644 index 0000000..10378e9 --- /dev/null +++ b/rooms/055-seven-dwarfs-diamond-mine.md @@ -0,0 +1,73 @@ +# Room 55: Seven Dwarfs' Diamond Mine + +This is the interior of the Seven Dwarfs' diamond mine. The room contains glittering diamonds embedded in the walls, a narrow hazardous path, and the dwarfs themselves who may be either peacefully working or hostile depending on the game state. Players can look at various features but cannot take the diamonds. If the dwarfs are hostile, the player will be forcefully removed from the mine. + +## Look Description + +"Diamonds glitter and sparkle from the earthen walls of the Seven Dwarfs' diamond mine. Within it, you see the little men busily at work." + +## Interactions + +| Status | Behavior Type | Command | Response | +|--------|--------------|---------|----------| +| TODO | Look | `look door` | "It is a ramshackle wooden door." | +| TODO | Look | `look out` / `look out door` / `look out mine` | "You look out the open door of the diamond mine, but notice nothing." | +| TODO | Look | `look around` / `look room` / `look mine` | "Diamonds glitter and sparkle from the earthen walls of the Seven Dwarfs' diamond mine. Within it, you see the little men busily at work." | +| TODO | Look | `look dwarf` | "You see the Seven Dwarfs toiling away with their picks and shovels, extracting the plentiful diamonds from their underground mine." | +| TODO | Look | `look mine` / `look diamond mine` | "This underground diamond mine belongs to the Seven Dwarfs." | +| TODO | Look | `look boulder` / `look gray boulder` | "Chunks of rock litter the floor of the diamond mine." | +| TODO | Look | `look down dirt` / `look down mine` | "Numerous rock and diamond chunks lie scattered on the mine floor." | +| TODO | Look | `look passageway` / `look mine passageway` | "The passageway of the diamond mine is but a short distance underground." | +| TODO | Look | `look path` | "Be careful on these narrow paths." | +| TODO | Look | `look wall` | "Sparkling diamonds flash and gleam from the mine walls." | +| TODO | Look | `look bucket` | "The bucket is brimming with sparkling diamonds." | +| TODO | Look | `look diamond` | "Sparkling diamonds gleam from the walls and litter the mine floor." | +| TODO | Get | `get boulder` | "You are not interested in the rocks." | +| TODO | Get | `get diamond` | "These diamonds do not belong to you." | +| TODO | Get | `get dwarf` | "You couldn't get the Seven Dwarfs." | +| TODO | Action | `rob diamond` | "You were raised better than that!" | +| TODO | Action | `climb boulder` | "There are no rocks to climb here." | +| TODO | Talk | `talk dwarf` / `talk` | "You endeavor to engage in conversation with the small men, but they are too busy to reply." | +| TODO | Action | `kill dwarf` | "You are NOT a murderous girl, Rosella!" | +| TODO | Action | `kiss dwarf` / `kiss` | "They are working awfully hard. That might distract them." | +| TODO | Action | `hit dwarf` | "You don't want to do that!" | +| TODO | Action | `help dwarf` | "They don't need help." | +| TODO | Inventory | `deliver [item]` / `return [item]` (if owned) | "The dwarfs have everything they need." | + +## Scripts + +| Status | Behavior Type | Name | Trigger | Behavior | +|--------|--------------|------|---------|----------| +| TODO | Interaction | SendOut | Entering room when `dwarf_state = DWARF_STATE.HOSTILE` | Display hostile encounter message, dwarf approaches player, player is marched to door, sets `player_kicked_out = true`, transport to Room 28 | +| TODO | Interaction | Tripped | Walking on hazardous areas while `dwarf_state = DWARF_STATE.FRIENDLY` | Player falls animation sequence, lands and recovers, returns to normal walking state, sets `on_narrow_path = true` | +| TODO | Background | WalkPath | Continuous while player is on narrow paths | Adjusts player display priority while on path, monitors for tripping hazards, resets state when player leaves path | +| TODO | Background | Watch | Continuous while `dwarf_state = DWARF_STATE.FRIENDLY` | Dwarf plays "watching" animation, pauses between cycles, continuous loop | +| TODO | Background | sparkle | Continuous while `diamonds_visible = true` | Randomly selects one of 4 diamond locations, plays sparkle animation, repeats continuously | + +## Technical Notes + +- **Room Number**: 55 +- **Picture**: 55 +- **Region**: 600 (Diamond Mine) +- **Exits**: East→56, West→28 (via door/mine exit) +- **Music**: Dwarf theme (sound #30, looping) + +### State Variables + +| Variable | Values | Description | +|----------|--------|-------------| +| `dwarf_state` | `DWARF_STATE.FRIENDLY`, `DWARF_STATE.HOSTILE` | Controls dwarf behavior and presence | +| `diamonds_visible` | `true`, `false` | Whether diamond sparkles are visible on walls | +| `room_visited` | `true`, `false` | Set to true when player first enters | +| `player_kicked_out` | `true`, `false` | Set when dwarfs force player to leave | +| `dwarf_music_playing` | `true`, `false` | Background music state | +| `on_narrow_path` | `true`, `false` | Whether player is on hazardous path | + +### Additional Technical Details + +- Room uses Region 600 for shared diamond mine logic +- Synonyms defined: `path` = `ledge` +- Region synonyms: `dwarf` = `ass` = `dwarf` = `man` = `person` +- Entry from Room 28 uses different starting position than entry from Room 56 +- Player must navigate narrow paths with risk of tripping +- The `SendOut` script forces the player to leave if `dwarf_state = DWARF_STATE.HOSTILE` diff --git a/rooms/056-diamond-mine.md b/rooms/056-diamond-mine.md new file mode 100644 index 0000000..4c34383 --- /dev/null +++ b/rooms/056-diamond-mine.md @@ -0,0 +1,100 @@ +# Room 56: Diamond Mine + +Room 56 is the Seven Dwarfs' underground diamond mine, accessible from the cavern entrance in room 55. The room features multiple animated dwarves working at the mine, with one dwarf standing by a bucket full of diamonds who serves as the primary NPC interaction. The player can return lost diamonds to the dwarves in exchange for a lantern, which is required for exploring other dark areas of the game. + +## Look Description + +"You see the busy little dwarfs hard at work within the diamond mine. The diamonds flash and sparkle from the earthen walls." + +## Interactions + +| Status | Behavior Type | Command | Response | +|--------|--------------|---------|----------| +| TODO | Look | `look[