Changes
From Final Fantasy Inside
no edit summary
=== Instructions & Stack ===
The Worldmap scripting engine for FF7 is very different from the field scripting format. It is a stack based language and for the most part instructions are the same size (16 bits), instead of having their parameters encoded into the instruction they take a predefined number of items off the stack and operate on that data. There are a few instructions that incorporate another word (16 bits) of immediate data, such instructions are clearly marked in the [[FF7/WorldMap_Module/Script/Opcodes|opcode list]].
The stack itself is global, there is only one stack which is shared between all scripts that are currently running. It is 8 levels deep, that is to say a maximum of 8 different items can be on the stack at any given time. A given item on the stack is not evaluated until it is popped off the stack, which can be a little unintuitive. For example, pushing the current X position of the player does not actually read the players position at that time, only when the value is actually used will it be fetched from the player entity. In practice this is not an issue because the script itself would have to change the position in between pushing the value and using it. Refer to the section below for an explanation of why that is the case.
=== Contexts ===
The script engine uses cooperative multitasking to run different scripts in parallel, the state of each script is contained in a separate context. On each frame, the game will loop over every active context and run it until it either returns or enters a waiting state. This means that scripts cannot run too long or they will slow down the game, an infinite loop will lock up the game completely. This also means that from the scripts point of view, nothing will happen until it returns or enters a wait state, the game is not running and no other script can run during this time.
=== Entities & Models ===
The Worldmap module operates on a fixed set of models, each having a specific model ID.
Each model that is currently loaded into the map also has an entity associated with it, this is the state of the model and holds information such as its position, rotation, current animation etc. Most instructions operate on the current active entity, which can be changed with the [[FF7/WorldMap_Module/Script/Opcodes/330|330]] opcode. The current active entity can also change as a side effect of certain instructions, all known cases are documented in the opcode descriptions but the list is not complete. The variable holding the current active entity is a global variable that resets every frame, if a script enters a wait state the current active entity is undefined when executing resumes.
Each entity is also a context (see the above section for more information about contexts) and in fact, there is no difference whatsoever between an entity and a context. The distinction is made because of the way they are treated by the scripting engine, the script state of the active entity (or any entity for that matter) cannot be modified (other than asking it to execute a function by means of the [[FF7/WorldMap_Module/Script/Opcodes/204|204]] opcode) and conversely, the state of the model corresponding to the context cannot be modified unless it is also the current active entity. This is the default state, whenever a function begins execution the context and current active entity are always equal.
In addition to the contexts associated with the models there is also a system context, this is where execution begins when the worldmap is first loaded and it also handles events which are not specific to any model on the map. The system context is technically an entity because, again, contexts and entities are the same thing but since it does not correspond to a model, manipulating the state of the system "entity" is an error.
=== Functions ===
As alluded to earlier, the script engine is driven by executing functions, each model has a set of functions that are executed by the game in response to certain events;
{| border="1" cellspacing="1" cellpadding="3" style="border: 1px solid black; border-collapse: collapse"
|-
! style="background:rgb(204,204,204)" align="center" | #
! style="background:rgb(204,204,204)" align="center" | Name
! style="background:rgb(204,204,204)" align="center" | Description
|-
| 0
| Enter
| Called when the model is loaded
|-
| 1
| Exit
| Called when the model is unloaded
|-
| 2
| Tick
| Called each frame if the model is set to recieve ticks?
|-
| 3
| Movement?
| Seems to be called for certain models that move around the map
|-
| 4
| Unknown
|
|-
| 5
| Unknown
|
|}
{| border="1" cellspacing="1" cellpadding="3" style="border: 1px solid black; border-collapse: collapse"
|-
! style="background:rgb(204,204,204)" align="center" | #
! style="background:rgb(204,204,204)" align="center" | Name
! style="background:rgb(204,204,204)" align="center" | Description
|-
| 0
| Enter
| Called when the worldmap is loaded
|-
| 1
| Exit
| Called when the worldmap is unloaded
|-
| 2
| Tick
| Called each frame
|-
| 3
| Unknown
|
|-
| 4
| Unknown
|
|-
| 5
| Unknown
|
|-
| 6
| Unknown
| Enter highwind interior?
|-
| 7
| Midgar Zolom
| Called when the player touches the midgar zolom in the swamp.
|}
And finally there is a set of functions which are called when the player enters an area of the walkmesh that is designated to trigger a script (it is not clear exactly which walkmesh types can trigger this event). Which function is executed depends on the mesh coordinates of the player (0-35, 0-27) as well as the exact walkmesh type that triggered the event. In theory there can be more than 2000 unique combinations so no list will be given for these functions :) Fortunately, not all functions need to be implemented, as will become apparent in the next section, functions that do nothing do not need to be implemented.
== .ev Format ==
=== Call Table ===
The first 0x400 bytes of an .ev file is the call table, a mapping between functions and entry points. Each entry is 4 bytes, 2 bytes of function identifier and 2 bytes instruction pointer. Instruction pointers are in 2-byte increments from the start of the code section. The last two bits of the function identifier defines the format of the remaining 14 bits;
{| border="1" cellspacing="1" cellpadding="3" style="border: 1px solid black; border-collapse: collapse"
|+ Type = 0, System function
|-
! style="background:rgb(204,204,204)" align="center" | size
! style="background:rgb(204,204,204)" align="center" | description
|-
| 8 bits
| Function #
|-
| 6 bits
| Padding
|}
{| border="1" cellspacing="1" cellpadding="3" style="border: 1px solid black; border-collapse: collapse"
|+ Type = 1, Model function
|-
! style="background:rgb(204,204,204)" align="center" | size
! style="background:rgb(204,204,204)" align="center" | description
|-
| 8 bits
| Function #
|-
| 6 bits
| Model ID
|}
{| border="1" cellspacing="1" cellpadding="3" style="border: 1px solid black; border-collapse: collapse"
|+ Type = 2, Mesh function
|-
! style="background:rgb(204,204,204)" align="center" | size
! style="background:rgb(204,204,204)" align="center" | description
|-
| 4 bits
| Walkmesh type
|-
| 10 bits
| MeshX + MeshY * 36
|}
=== Code ===