When interacting with NPCs or shops, Faxanadu runs through a series of scripted actions based on the player's state, menu choices, and button inputs. As there's no public name given to Faxanadu's implementation, I've opted to call these "Interaction Scripts," or "IScripts" for short. IScripts are all stored in Bank 12. They're stored as a series of bytes, each beginning with an Action ID followed by 0 or more parameters. Action IDs map to registered Action functions, which may handle showing or hiding messages, displaying menus, handling gold/experience/items, or jumping to new IScripts. # Memory Locations There are a few memory locations relevant to IScripts, all in Bank 12: | Address Range | Description | | ------------- | ------------------------------------------------------------------------- | | $827b - $8292 | **Actions Lookup Table:** Lower bytes for the action functions | | $8293 - $82a9 | **Actions Lookup Table:** Upper bytes for the action functions | | $9f6b - $a002 | **Entrypoint Lookup Table:** Lower bytes for IScript entrypoint addresses | | $a003 - $a09a | **Entrypoint Lookup Table:** Upper bytes for IScript entrypoint addresses | | $a09b - $a6c7 | **Script Data:** IScripts and store data | # IScripts Structure IScripts have the following structure: | Script Offset | Size (bytes) | Description | | ------------- | ------------ | ---------------------- | | 0 | 1 | Entity ID | | 1+ | N | Actions and parameters | Actions have the following structure: | Action Offset | Size (bytes) | Description | | ------------- | ------------ | ----------------------------- | | 0 | 1 | Action ID (values 0-23) | | 1+ | 0...N | Any parameters for the action | A script may have any number of actions. Actions may have any number of parameters, as specified by the action handler. Actions in scripts may jump to addresses within the script or to entirely different scripts (allowing sharing of common parts of a script). All action parameters are in Big Endian. ## Example Here's an example of a script for the game, featuring the King (address $A350 in Bank 12). ``` 80 ENTITY_KING 0D IF_PLAYER_HAS_ANY_GOLD 5A A3 Jump to $A35A (_HAS_GOLD) 01 SHOW_UNSKIPPABLE_MESSAGE 35 Message 53 (Exposition) 09 ADD_PLAYER_GOLD DC 05 1500G 00 END_SCRIPT _HAS_GOLD: 03 SHOW_MESSAGE 36 Message 54 (Nothing more I can do to help) 00 END_SCRIPT ``` # Entity IDs IScripts begin with an **Entity ID**. This is the entity conveying any dialogue in the script. Depending on the ID, it may be accompanied by a portrait showing an NPC. The following values are supported | Entity ID | Description | | --------- | ------------------------------- | | $00 | No portrait (generic textboxes) | | $80 | King | | $81 | Guru | | $82 | Martial Artist | | $83 | Magician | | $84 | Doctor | | $85 | Nurse | | $86 | Pink Shirt Guy | | $87 | Smoking Guy | | $88 | Meat Salesman | | $89 | Tools Salesman | | $8A | Key Salesman | # Actions Actions are identified by a number between 0 and 23, an index into the Actions Lookup Table Here's a quick summary of the actions: | Action ID | Name | Parameters | Implementation Address | | --------- | ------------------------------------------------ | ----------- | ---------------------- | | $00 | End Script | 0 | $82B4 | | $01 | Show Unskippable Message | 1 (1 byte) | $82CF | | $02 | Show Question Message | 1 (1 byte) | $82EF | | $03 | Show Message | 1 (1 byte) | $82D9 | | $04 | Check and Update Player Title | 1 (2 bytes) | $8726 | | $05 | Spend Gold | 1 (2 bytes) | $835B | | $06 | Set Spawn Point | 1 (1 byte) | $8391 | | $07 | Add Player Item | 1 (1 byte) | $839F | | $08 | Open Shop | 1 (2 bytes) | $83D8 | | $09 | Add Player Gold | 1 (2 bytes) | $8525 | | $0A | Add MP | 1 (1 byte) | $8580 | | $0B | If Quest Completed | 2 (3 bytes) | $85D1 | | $0C | If Player Has Title | 2 (3 bytes) | $85F6 | | $0D | If Player Has Any Gold | 1 (2 bytes) | $861F | | $0E | Set Quest Complete | 1 (1 byte) | $85E6 | | $0F | Show Buy/Sell Menu | 1 (1 byte) | $8630 | | $10 | Take Player Item | 1 (1 byte) | $8657 | | $11 | Show Sell Menu | 1 (2 bytes) | $8660 | | $12 | If Player Has Item | 2 (3 bytes) | $8718 | | $13 | Add HP | 1 (1 byte) | $85AF | | $14 | Show Password | 0 | $8737 | | $15 | Finish Game | 0 | $02AE | | $16 | Unused <br>(Show Message and Jump if Dismissed?) | 2 (2 bytes) | $8308 | | $17 | Jump to Address | 1 (2 bytes) | $82AB | Let's look at these in detail. ## Action $00: End Script **Function Address:** $82B4 **Arguments:** None Ends execution of the script and closes any open dialogs. **Example:** ``` 00 END_SCRIPT ``` ## Action $01: Show Unskippable Message **Function Address:** $82C5 **Arguments:** 1. [[Strings|Message ID]] (1 byte) Displays a message in a text box, with an optional portrait depending on the script's Entity ID. This message cannot be skipped by pressing the B button. It's used for important messages, like the game's intro. **Example:** ``` 01 SHOW_UNSKIPPABLE_MESSAGE 42 MESSAGE_66 ``` ## Action $02: Show Question Message **Function Address:** $82EF **Arguments:** 1. [[Strings|Message ID]] (1 byte) Displays a message in a text box, with an optional portrait depending on the script's Entity ID. The message will terminate with a flashing "?" at the bottom-center of the dialogue, indicating a question for the player. This message can be skipped at any point by pressing the B button. **Example:** ``` 02 SHOW_QUESTION_MESSAGE 42 MESSAGE_66 ``` ## Action $03: Show Message **Function Address:** $82D9 **Arguments:** 1. [[Strings|Message ID]] (1 byte) Displays a message in a text box, with an optional portrait depending on the script's Entity ID. This message can be skipped at any point by pressing the B button. **Example:** ``` 01 SHOW_MESSAGE 42 MESSAGE_66 ``` ## Action $04: Check and Update Player Title **Function Address:** $8726 **Arguments:** 1. IScript address (2 bytes) Checks if the player is eligible for a new title. If so, the title will be updated, and the script will jump to the address specified as the first parameter. Else, the next action will be invoked instead. **Example:** ``` 04 CHECK_UPDATE_PLAYER_TITLE 02 a2 Jump to $A202 if updated ``` ## Action $05: Spend Gold **Function Address:** $835B **Arguments:** 1. Gold amount (2 bytes) Takes the specified amount of gold away from the player. **Example:** ``` 05 SPEND_GOLD dc 05 1500G ``` ## Action $06: Set Spawn Point **Function Address:** $8391 **Arguments:** 1. Temple ID (1 byte) Sets the spawn point to the specified temple. Only the furthest temple will be set. If backtracking, to an earlier spawn point, then ew spawn point will not be set. The temple ID must be one of the following: | Tempe ID | Description | | -------- | --------------- | | $00 | Eolis Temple | | $01 | Apolune Temple | | $02 | Forepaw Temple | | $03 | Mascon Temple | | $04 | Victim Temple | | $05 | Conflate Temple | | $06 | Daybreak Temple | | $07 | Final Temple | **Example:** ``` 06 SET_SPAWN_POINT 04 Temple 4 (Victim) ``` ## Action $07: Add Player Item **Function Address:** $839F **Arguments:** 1. Item ID (1 byte) Adds an item with the specified ID to the player's inventory. **Example:** ``` 07 ADD_PLAYER_ITEM 43 00 Item 67 (Battle Helmet) ``` ## Action $08: Open Shop **Function Address:** $83D8 **Arguments:** 1. Shop address (2 bytes) Open a shop window with the [[Faxanadu Shop Data|shop contents]] at the specified address. **Example:** ``` 08 OPEN_SHOP 89 a5 At address $A589 ``` ## Action $09: Add Player Gold **Function Address:** $8525 **Arguments:** 1. Gold amount (2 bytes) Adds the specified amount of gold to the player. **Example:** ``` 09 ADD_PLAYER_GOLD dc 05 1500G ``` ## Action $0A: Add MP **Function Address:** $8580 **Arguments:** 1. MP amount (1 byte) Adds the specified amount of Magic Points to the player. **Example:** ``` 0A ADD_PLAYER_MP 40 64MP ``` ## Action $0B: If Quest Completed **Function Address:** $85D1 **Arguments:** 1. Quest ID (1 byte) 2. Jump address (2 bytes) Check if a quest has been completed. If so, jump to the specified address. Else, invoke the next action. The quest ID must be one of the following: | Quest ID | Description | | -------- | --------------------------------- | | $01 | Spring of Trunk Flowing | | $02 | Spring of Sky Flowing | | $03 | Spring of Trunk _and_ Sky Flowing | **Example:** ``` 0B IF_QUEST_COMPLETED 01 Quest 1 (Spring oF Trunk) 44 a5 Then jump to $A544 ``` ## Action $0C: If Player Has Title **Function Address:** $85F6 **Arguments:** 1. Title ID (1 byte) 2. Jump address (2 bytes) Check if the player has a specified title. If so, jump to the specified address. Else, invoke the next action. The title ID must be one of the following: | Title ID | Name | | -------- | --------- | | $00 | Novice | | $01 | Aspirant | | $02 | Battler | | $03 | Fighter | | $04 | Adept | | $05 | Chevalier | | $06 | Veteran | | $07 | Warrior | | $08 | Swordman | | $09 | Hero | | $0A | Soldier | | $0B | Myrmidon | | $0C | Champion | | $0D | Superhero | | $0E | Paladin | | $0F | Lord | **Example:** ``` 0C IF_PLAYER_HAS_TITLE 06 Veteran 02 a2 Then jump to $A202 ``` ## Action $0D: If Player Has Any Gold **Function Address:** $861F **Arguments:** 1. Jump address (2 bytes) Check if the player has any gold. If so, jump to the specified address. Else, invoke the next action. **Example:** ``` 0D IF_PLAYER_HAS_ANY_GOLD 02 a2 Then jump to $A202 ``` ## Action $0E: Set Quest Completed **Function Address:** $85E6 **Arguments:** 1. Quest ID (1 byte) Sets a quest flag for the user based on the given quest ID. **Example:** ``` 0E SET_QUEST_COMPLETED 01 Sky of Trunk ``` ## Action $0F: Show Buy/Sell Menu **Function Address:** $8630 **Arguments:** 1. Buy jump address (2 bytes) Asks the user if they want to buy or sell. If the user chooses Buy, this will jump to the provided address. Else, it will invoke the next action. **Example:** ``` 0F SHOW_BUY_SELL_MENU 02 a2 For store at $A202 ``` ## Action $10: Take Player Item **Function Address:** $8657 **Arguments:** 1. Item ID (1 byte) If the player has the item in the inventory, it will be removed. **Example:** ``` 10 TAKE_PLAYER_ITEM 84 "A" Key ``` ## Action $11: Show Sell Menu **Function Address:** $8660 **Arguments:** 1. Shop address (2 bytes) Open a shop window capable of selling items found in the [[Faxanadu Shop Data|shop contents]] at the specified address. **Example:** ``` 11 SHOW_SELL_MENU 89 a5 For store at $A589 ``` ## Action $12: If Player Has Item **Function Address:** $8718 **Arguments:** 1. Item ID (1 byte) 2. Jump address (2 bytes) Checks if the player has the specified item. If so, it will jump to the script at the specified address. Else, it will invoke the next action. **Example:** ``` 12 IF_PLAYER_HAS_ITEM 92 Elixir 02 a2 Then jump to $A202 ``` ## Action $13: Add HP **Function Address:** $85AF **Arguments:** 1. HP amount (1 byte) Adds the specified amount of Hit Points to the player. **Example:** ``` 13 ADD_PLAYER_HP 40 64HP ``` ## Action $14: Show Password **Function Address:** $8737 **Arguments:** None Shows a password for the player's current game state. **Example:** ``` 14 SHOW_PASSWORD ``` ## Action $15: Finish Game **Function Address:** $02AE **Arguments:** None This will finish the game, showing the outro screen and victory music. **Example:** ``` 15 FINISH_GAME ``` ## Action $16: Unused (Show Message and Check If Dismissed?) **Function Address:** $8308 **Arguments:** 1. [[Strings|Message ID]] (1 byte) 2. Jump address (2 bytes) This is unused in the game, but appears to be another way of showing a message. Based on an initial read of the code, it appears it will show a message and then jump if dismissed early, or invoke the next action if not. **Example:** ``` 16 SHOW_MESSAGE_AND_JUMP 42 66 02 a2 $A202 ``` ## Action $17: Jump to Address **Function Address:** $82AB **Arguments:** 1. Jump address (2 bytes) This will immediately jump to the specified address. **Example:** ``` 17 JUMP 02 a2 To $A202 ```