Pārlūkot izejas kodu

Choice dialogs and new opcodes:

- Choice dialogs implemented.
- Implemented opcodes: SCRLC, SCRCC, SCRDL, TURA, SLIDR, ASK
- Data in the databanks is now properly loaded when loading a game.
- Fixed character stats when loading games.
Iñigo Valentin 3 gadi atpakaļ
vecāks
revīzija
2639ad25f8

+ 3 - 3
README.md

@@ -19,8 +19,8 @@ Note that, while these are actual screenshots of V-Gears, none of the images, sp
 |[<img src="https://v-gears.inigovalentin.com/img/screenshot/cloud.png" alt="Cloud model" width="250"/>](https://v-gears.inigovalentin.com/img/screenshot/cloud.png "Cloud model")|[<img src="https://v-gears.inigovalentin.com/img/screenshot/dialog.png" alt="dialog" width="250"/>](https://v-gears.inigovalentin.com/img/screenshot/dialog.png "Dialogs")|[<img src="https://v-gears.inigovalentin.com/img/screenshot/animation.png" alt="animation" width="250"/>](https://v-gears.inigovalentin.com/img/screenshot/animation.png "Animations")|
 |**Climbing**|**Mid-jump!**|**Materia**|
 |[<img src="https://v-gears.inigovalentin.com/img/screenshot/climb.png" alt="Climbing" width="250"/>](https://v-gears.inigovalentin.com/img/screenshot/climb.png "Climbing")|[<img src="https://v-gears.inigovalentin.com/img/screenshot/midjump.png" alt="Mid-jump" width="250"/>](https://v-gears.inigovalentin.com/img/screenshot/midjump.png "Mid-jump")|[<img src="https://v-gears.inigovalentin.com/img/screenshot/materia.png" alt="Materia" width="250"/>](https://v-gears.inigovalentin.com/img/screenshot/materia.png "Materia")|
-|**Transparencies**|**Timers**||
-|[<img src="https://v-gears.inigovalentin.com/img/screenshot/transparent_dialogs.png" alt="Transparency" width="250"/>](https://v-gears.inigovalentin.com/img/screenshot/transparent_dialogs.png "TRansparency")|[<img src="https://v-gears.inigovalentin.com/img/screenshot/timers.png" alt="Timers" width="250"/>](https://v-gears.inigovalentin.com/img/screenshot/timers.png "Timers")||
+|**Transparencies**|**Timers**|**Choices**|
+|[<img src="https://v-gears.inigovalentin.com/img/screenshot/transparent_dialogs.png" alt="Transparency" width="250"/>](https://v-gears.inigovalentin.com/img/screenshot/transparent_dialogs.png "Transparency")|[<img src="https://v-gears.inigovalentin.com/img/screenshot/timers.png" alt="Timers" width="250"/>](https://v-gears.inigovalentin.com/img/screenshot/timers.png "Timers")|[<img src="https://v-gears.inigovalentin.com/img/screenshot/choices.png" alt="Choices" width="250"/>](https://v-gears.inigovalentin.com/img/screenshot/choices.png "Choices")|
 
 ### Menus:
 |**Begin**|**Main**|**Name**|
@@ -62,7 +62,7 @@ If the question was "How deep into the game can I play?", then the answer is
 
 [<img src="https://v-gears.inigovalentin.com/img/screenshot/max_progress.png" alt="Max progress" width="450"/>](https://v-gears.inigovalentin.com/img/screenshot/max_progress.png "Max progress")
 
-Until there. Whitout cheats, and without using the developer console, the game hangs there.
+Until there. Whitout cheats, and without using the developer console, it's not possible to get to the next wagon. The dialogs start looping there.
 
 But don't worry! The engine is not fixed sequentially, or map by map. Once a fix is implememnted, it applies to all maps at once, anb big chunck of the game are unlocked after each fix.
 

+ 124 - 0
V-Gears-Installer/include/decompiler/field/instruction/FieldCameraInstruction.h

@@ -43,10 +43,134 @@ class FieldCameraInstruction : public KernelCallInstruction{
 
         void ProcessNFADE(CodeGenerator* code_gen);
 
+        /**
+         * Prodesses a SCR2D opcode.
+         *
+         * Opcode: 0x64
+         * Short name: SCR2D
+         * Long name: Scroll 2D
+         *
+         * Memory layout (4 bytes)
+         * |0x64|B1/B2|X|Y|
+         *
+         * Arguments
+         *
+         * - const Bit[4] B1: Source bank 1, or zero if X is set as a constant value.
+         * - const Bit[4] B2: Source bank 2, or zero if Y is set as a constant value.
+         * - const Short X: X-coordinate to scroll to, or address to find X if B1 is non-zero.
+         * - const Short Y: Y-coordinate to scroll to, or address to find Y if B2 is non-zero.
+         *
+         * Instantaneously scrolls the current view to the coordinates found in the arguments (or
+         * the values found at the addresses if memory banks and address are specified). The move
+         * to the new coordinates is instant; variants exist for linear and smooth scrolling.
+         *
+         * @param[in,out] code_gen Code generator to append lines.
+         */
         void ProcessSCR2D(CodeGenerator* code_gen);
 
+        /**
+         * Processes a SCRLC opcode.
+         *
+         * Opcode: 0x62
+         * Short name: SCRLC
+         * Long name: Scroll To Playable Character Stop Movement
+         *
+         * Memory layout (5 bytes)
+         * |0x63|B|S|U|T|
+         *
+         * Arguments
+         *
+         * - const UByte B: PRESUME -> Bank for the scroll speed, or zero if it is specified as a
+         * literal value. Always zero in game data
+         * - const UShort S: Speed of the scroll, in frames, or the address to find the speed if B
+         * is non-zero.
+         * - const UByte U: Unknown -> Always zero in game data. Seems to affect the speed above.
+         * - const UByte T: Type of scroll.
+         *
+         * Scrolls the current view so that the main playable character is in the center of the
+         * view. The script executes asynchronously and does not block whilst the camera movement
+         * is active but returns control to the next script operation. This is tweened over a
+         * period of S frames. If the main playable character also moves during this time period,
+         * the tween position is also updated to match the new playable character location. Once
+         * the camera has completed its movement, the camera NO LONGER follows the main playable
+         * character's movement. It stays in place.
+         *
+         * Scroll Types:
+         * 2 - Linear.
+         * 3 - Smooth (QuadraticInAndOut).
+         */
+        void ProcessSCRLC(CodeGenerator* code_gen);
+
+        /**
+         * Processes a SCRCC opcode.
+         *
+         * Opcode: 0x65
+         * Short name: SCRCC
+         * Long name: Scroll To Playable Character
+         *
+         * Memory layout (1 byte)
+         * |0x65|
+         *
+         * Instantaneously scrolls the current view so that it is centered on the current playable
+         * character.
+         */
+        void ProcessSCRCC(CodeGenerator* code_gen);
+
+        /**
+         * Processes a SCR2DC opcode.
+         *
+         * Opcode: 0x66
+         * Short name: SCR2DC
+         * Long name: Scroll to Coordinates (Smooth)
+         *
+         * Memory layout (6 bytes)
+         * |0x66|B1/B2|0/B3|X|Y|S|
+         *
+         * Arguments
+         *
+         * - const Bit[4] B1: Source bank 1, or zero if X is set as a constant value.
+         * - const Bit[4] B2: Source bank 2, or zero if Y is set as a constant value.
+         * - const Bit[4] 0: Zero.
+         * - const Bit[4] B4: Source bank 3, or zero if S is set as a constant value.
+         * - const Short X: X-coordinate to scroll to.
+         * - const Short Y: Y-coordinate to scroll to.
+         * - const UShort S: Speed to scroll; higher values scroll more slowly.
+         *
+         * Similar to SCR2D, except the scroll is not instantaneous and is performed smoothly, with
+         * a slower start and ending, with speed peaking in the center of the scroll. The overall
+         * speed can be set with S.
+         *
+         * @param[in,out] code_gen Code generator to append lines.
+         */
         void ProcessSCR2DC(CodeGenerator* code_gen);
 
+        /**
+         * Processes a SCR2DL opcode.
+         *
+         * Opcode: 0x68
+         * Short name: SCR2DC
+         * Long name: Scroll to Coordinates (Linear)
+         *
+         * Memory layout (6 bytes)
+         * |0x68|B1/B2|0/B3|X|Y|S|
+         *
+         * Arguments
+         *
+         * - const Bit[4] B1: Source bank 1, or zero if X is set as a constant value.
+         * - const Bit[4] B2: Source bank 2, or zero if Y is set as a constant value.
+         * - const Bit[4] 0: Zero.
+         * - const Bit[4] B4: Source bank 3, or zero if S is set as a constant value.
+         * - const Short X: X-coordinate to scroll to.
+         * - const Short Y: Y-coordinate to scroll to.
+         * - const UShort S: Speed to scroll; higher values scroll more slowly.
+         *
+         * Similar to SCR2D, except the scroll to the coordinates is linear; that is, the speed is
+         * constant throughout the duration of the screen scroll.
+         *
+         * @param[in,out] code_gen Code generator to append lines.
+         */
+        void ProcessSCR2DL(CodeGenerator* code_gen);
+
         /**
          * Processes a VWOFT opcode.
          *

+ 241 - 195
V-Gears-Installer/include/decompiler/field/instruction/FieldModelInstruction.h

@@ -53,22 +53,18 @@ class FieldModelInstruction : public KernelCallInstruction{
          * Arguments
          * - const UByte S: Speed that the characters join back together.
          *
-         * Causes seperated party characters that have previously been SPLIT
-         * onto the field, to be joined back together again; that is, only the
-         * party leader becomes visible on the field. This should be called if
-         * a previous SPLIT has completed (the party members have finished
-         * speaking, or performing their actions, for example). As with SPLIT,
-         * the speed of the join is specified, from a scale of 1 (almost
-         * instant) to FF (very slow walk), and must be non-zero. In contrast
-         * to most MOVE related op codes, the speed is this setting is
-         * actually the total number of frames required. Depending on the
-         * distance from the player character and the number of frames
-         * required, the entity plays a run or walk animation. Also, all
-         * characters take the same time irrespective of distance. Calling
-         * JOIN without having previously SPLIT the characters will cause the
-         * party members to appear at the walkmesh origin and attempt to JOIN
-         * from there. This is not normally the required behaviour and should
-         * be avoided.
+         * Causes seperated party characters that have previously been SPLIT onto the field, to be
+         * joined back together again; that is, only the party leader becomes visible on the field.
+         * This should be called if a previous SPLIT has completed (the party members have finished
+         * speaking, or performing their actions, for example). As with SPLIT, the speed of the
+         * join is specified, from a scale of 1 (almost instant) to FF (very slow walk), and must
+         * be non-zero. In contrast to most MOVE related op codes, the speed is this setting is
+         * actually the total number of frames required. Depending on the distance from the player
+         * character and the number of frames required, the entity plays a run or walk animation.
+         * Also, all characters take the same time irrespective of distance. Calling JOIN without
+         * having previously SPLIT the characters will cause the party members to appear at the
+         * walkmesh origin and attempt to JOIN from there. This is not normally the required
+         * behavior and should be avoided.
          *
          * @param[in,out] code_gen Code generator to append lines.
          */
@@ -85,55 +81,65 @@ class FieldModelInstruction : public KernelCallInstruction{
          * |0x20|B1/B2|B3/B4|B5/B6|XA|XA|YA|YA|DA|XB|XB|YB|YB|DB|S|
          *
          * Arguments
-         * - const Bit[4] B1: Bank for XA, or zero if XA is specified as a
-         * literal value.
-         * - const Bit[4] B2: Bank for YA, or zero if YA is specified as a
-         * literal value.
-         * - const Bit[4] B3: Bank for DA, or zero if DA is specified as a
-         * literal value.
-         * - const Bit[4] B4: Bank for XB, or zero if XB is specified as a
-         * literal value.
-         * - const Bit[4] B5: Bank for YB, or zero if YB is specified as a
-         * literal value.
-         * - const Bit[4] B6: Bank for DB, or zero if DB is specified as a
-         * literal value.
-         * - const Short XA: X-coordinate of the second character in the party
-         * after the split, or address for the value if B1 is non-zero.
-         * - const Short YA: Y-coordinate of the second character in the party
-         * after the split, or address for the value if B2 is non-zero.
-         * - const UByte DA: Direction the second character faces after the
-         * split, or address for the value if B3 is non-zero.
-         * - const Short XB: X-coordinate of the third character in the party
-         * after the split, or address for the value if B4 is non-zero.
-         * - const Short YB: Y-coordinate of the third character in the party
-         * after the split, or address for the value if B5 is non-zero.
-         * - const UByte DB: Direction the third character faces after the
-         * split, or address for the value if B6 is non-zero.
+         * - const Bit[4] B1: Bank for XA, or zero if XA is specified as a literal value.
+         * - const Bit[4] B2: Bank for YA, or zero if YA is specified as a literal value.
+         * - const Bit[4] B3: Bank for DA, or zero if DA is specified as a literal value.
+         * - const Bit[4] B4: Bank for XB, or zero if XB is specified as a literal value.
+         * - const Bit[4] B5: Bank for YB, or zero if YB is specified as a literal value.
+         * - const Bit[4] B6: Bank for DB, or zero if DB is specified as a literal value.
+         * - const Short XA: X-coordinate of the second character in the party after the split, or
+         * address for the value if B1 is non-zero.
+         * - const Short YA: Y-coordinate of the second character in the party after the split, or
+         * address for the value if B2 is non-zero.
+         * - const UByte DA: Direction the second character faces after the split, or address for
+         * the value if B3 is non-zero.
+         * - const Short XB: X-coordinate of the third character in the party after the split, or
+         * address for the value if B4 is non-zero.
+         * - const Short YB: Y-coordinate of the third character in the party after the split, or
+         * address for the value if B5 is non-zero.
+         * - const UByte DB: Direction the third character faces after the split, or address for
+         * the value if B6 is non-zero.
          * - const UByte S: Speed that the characters split.
          *
-         * Causes the common 'split effect' whereby the second and third
-         * characters in the current party 'come out' from the party leader.
-         * That is, they become visible in the field, starting from the center
-         * of the party leader, and move out to the coordinates specified in
-         * the argument list. This is commonly used when the other characters
-         * in the current party have an action or dialog to perform and must
-         * be individually visible in the field. As well as specifying final
-         * coordinates for the two other party characters, the directions each
-         * character faces after the split are specified as a byte, using the
-         * common direction values found throughout the game. Speed is also
-         * given and is used to specify the rate at which the characters leave
-         * the party leader, using a scale from 1 (almost instant) to FF
-         * (extremely slow walk); this must be non-zero. In contrast to most
-         * MOVE related op codes, the speed is this setting is actually the
-         * total number of frames required. Depending on the distance from the
-         * player character and the number of frames required, the entity
-         * plays a run or walk animation. Also, all characters take the same
+         * Causes the common 'split effect' whereby the second and third characters in the current
+         * party 'come out' from the party leader. That is, they become visible in the field,
+         * starting from the center of the party leader, and move out to the coordinates specified
+         * in the argument list. This is commonly used when the other characters in the current
+         * party have an action or dialog to perform and must be individually visible in the field.
+         * As well as specifying final coordinates for the two other party characters, the
+         * directions each character faces after the split are specified as a byte, using the
+         * common direction values found throughout the game. Speed is also given and is used to
+         * specify the rate at which the characters leave the party leader, using a scale from 1
+         * (almost instant) to FF (extremely slow walk); this must be non-zero. In contrast to most
+         * MOVE related op codes, the speed is this setting is actually the total number of frames
+         * required. Depending on the distance from the player character and the number of frames
+         * required, the entity plays a run or walk animation. Also, all characters take the same
          * time irrespective of distance.
          *
          * @param[in,out] code_gen Code generator to append lines.
          */
         void ProcessSPLIT(CodeGenerator* code_gen);
 
+        /**
+         * Processes a TLKON opcode.
+         *
+         * Opcode: 0x7E
+         * Short name: TLKON
+         * Long name: Talk Switch
+         *
+         * Memory layout (2 bytes)
+         * |0x7E|S
+         *
+         * Arguments:
+         *
+         * - const UByte B: Switch on/off (0/1, respectively).
+         *
+         * Turns on or off, for an entity, the ability for the playable character to interact with
+         * the entity by pressing the [X] button. More precisely, this enables or disables the On
+         * Press script (script 2); if set to off, script 2 will not execute when the button is
+         * pressed and the player is facing the entity's object. If set to on, the script will
+         * execute, as normal.
+         */
         void ProcessTLKON(CodeGenerator* code_gen, const std::string& entity);
 
         void ProcessPC(CodeGenerator* code_gen, const std::string& entity, int char_id);
@@ -150,6 +156,36 @@ class FieldModelInstruction : public KernelCallInstruction{
 
         void ProcessMOVE(CodeGenerator* code_gen, const std::string& entity);
 
+        /**
+         * Processes a TURA opcode.
+         *
+         * Opcode: 0xAB
+         * Short name: TURA
+         * Long name: Turn to entity
+         *
+         * Memory layout (4 bytes)
+         * |0xB4|Entity id|Rotate side type|Steps in rotation
+         *
+         * Arguments
+         *
+         * - const Bit[4] Entity id: Entity id to which model we calculate direction during first
+         * opcode call.
+         * - const Short Rotate side type: Specify how model will be rotated. (0 - clockwise/ 1 -
+         * anti-clockwise/ 2 - closest)
+         * - const Short Steps in rotation: Set number of steps in rotation.
+         *
+         * Rotation calculated like in TUGNGEN, except end direction calculated during first opcode
+         * call. Rotation always calculated smoothly. Like in TURNGEN(XX,XX,XX,XX,02); This opcode
+         * will be called until turn is over and then continue script execution.
+         *
+         * @param[in,out] code_gen Code generator to append lines.
+         * @param[in] entity The name of the entity.
+         * @param[in] engine The field engine.
+         */
+        void ProcessTURA(
+          CodeGenerator* code_gen, const std::string& entity, const FieldEngine& engine
+        );
+
         void ProcessMSPED(CodeGenerator* code_gen, const std::string& entity);
 
         void ProcessDIR(CodeGenerator* code_gen, const std::string& entity);
@@ -177,50 +213,39 @@ class FieldModelInstruction : public KernelCallInstruction{
          * |0xC2|B1/B2|B3/B4|X|Y|I|Steps|
          *
          * Arguments
-         * - const Bit[4] B1: Bank to retrieve X-coordinate, or zero if
-         * specifying X as a literal value.
-         * - const Bit[4] B2: Bank to retrieve Y-coordinate, or zero if
-         * specifying Y as a literal value.
-         * - const Bit[4] B3: Bank to retrieve triangle ID, or zero if
-         * specifying Z as a literal value.
-         * - const Bit[4] B4: Bank to retrieve jump height, or zero if
-         * specifying H as a literal value.
-         * - const Short X: X-coordinate of the target to jump to, or lower
-         * byte specifying address if B1 is non-zero.
-         * - const Short Y: Y-coordinate of the target to jump to, or lower
-         * byte specifying address if B2 is non-zero.
-         * - const Short I: Triangle ID of the target to jump to, or lower
-         * byte specifying address if B3 is non-zero.
-         * - const UShort Steps: Steps in jump. Must be non-zero if a literal
-         * value. Alternatively, lower byte specifies address if B4 is
-         * non-zero.
-         *
-         * Causes the character to jump to the specified point and triangle
-         * ID, with the jump curve peaking at a height which is increased by
-         * using a larger value for the H argument. In addition, the larger
-         * the number, the longer the jump will take to complete. A "normal"
-         * value is around 0x15, 0x01 is fast and instantaneous; the argument
-         * must not be zero or the game will crash. Whilst this is an unsigned
-         * two-byte number, a large value (beyond around 0x60) will not only
-         * cause a vast jump height, but also cause the screen to scroll
-         * erratically (the larger the number, the more erratic). Main update
-         * function go through all entity with JUMP state and if stage is 0 it
-         * calculates final Z point according to triangle id. It sets current
-         * coords as start coords. The main thing this function does is set B
-         * coefficient for later calculation. It defines as follows:
+         * - const Bit[4] B1: Bank to retrieve X-coordinate, or zero if specifying X as a literal.
+         * - const Bit[4] B2: Bank to retrieve Y-coordinate, or zero if specifying Y as a literal.
+         * - const Bit[4] B3: Bank to retrieve triangle ID, or zero if specifying Z as a literal.
+         * - const Bit[4] B4: Bank to retrieve jump height, or zero if specifying H as a literal.
+         * - const Short X: X-coordinate of the target to jump to, or lower byte specifying address
+         * if B1 is non-zero.
+         * - const Short Y: Y-coordinate of the target to jump to, or lower byte specifying address
+         * if B2 is non-zero.
+         * - const Short I: Triangle ID of the target to jump to, or lower byte specifying address
+         * if B3 is non-zero.
+         * - const UShort Steps: Steps in jump. Must be non-zero if a literal value. Alternatively,
+         * lower byte specifies address if B4 is non-zero.
+         *
+         * Causes the character to jump to the specified point and triangle ID, with the jump curve
+         * peaking at a height which is increased by using a larger value for the H argument. In
+         * addition, the larger the number, the longer the jump will take to complete. A "normal"
+         * value is around 0x15, 0x01 is fast and instantaneous; the argument must not be zero or
+         * the game will crash. Whilst this is an unsigned two-byte number, a large value (beyond
+         * around 0x60) will not only cause a vast jump height, but also cause the screen to scroll
+         * erratically (the larger the number, the more erratic). Main update function go through
+         * all entity with JUMP state and if stage is 0 it calculates final Z point according to
+         * triangle id. It sets current coords as start coords. The main thing this function does
+         * is set B coefficient for later calculation. It defines as follows:
          *   B = (Z_final - Z_start) / steps - steps * 1.45;
-         * Then it set current step to 0 and stage to 1. On next update other
-         * part of function works. It's calculate real position. First it
-         * increment current step number. Then it calculate X and Y. They
-         * change linear so nothing interesting here. The Z calculation is as
-         * follows:
+         * Then it set current step to 0 and stage to 1. On next update other part of function
+         * works. It's calculate real position. First it increment current step number. Then it
+         * calculate X and Y. They change linear so nothing interesting here. The Z calculation is
+         * as follows:
          *   Z_current = - step^2 * 1.45 + step * B + Z_start;
-         * If current substep equal number of steps then we set current
-         * triangle to final triangle and set stage to 2. Which finalizes the
-         * routine on next opcode call. Neither animation nor sound is
-         * specified in this opcode. An animation is played by using an
-         * animation opcode such as DFANM, and a SOUND played, before the
-         * jump.
+         * If current substep equal number of steps then we set current triangle to final triangle
+         * and set stage to 2. Which finalizes the routine on next opcode call. Neither animation
+         * nor sound is specified in this opcode. An animation is played by using an animation
+         * opcode such as DFANM, and a SOUND played, before the jump.
          *
          * @param[in,out] code_gen Code generator to append lines.
          * @param[in] entity The name of the entity.
@@ -242,19 +267,17 @@ class FieldModelInstruction : public KernelCallInstruction{
          * - const Bit[4] B2: Bank to store Y.
          * - const Bit[4] B3: Bank to store Z.
          * - const Bit[4] B4: Bank to store I.
-         * - const UByte A: Entity ID whose field object will have its
-         * position retrieved from.
+         * - const UByte A: Entity ID whose field object will have its position retrieved from.
          * - const UByte X: Address to store the X-coordinate.
          * - const UByte Y: Address to store the Y-coordinate.
          * - const UByte Z: Address to store the Z-coordinate.
-         * - const UByte I: Address to store the ID of the walkmesh triangle
-         * the object is standing on.
+         * - const UByte I: Address to store the ID of the walkmesh triangle the object is standing
+         * on.
          *
-         * Retrieves the coordinates of the field object that the entity,
-         * whose ID specified in A, is associated with. This opcode uses an
-         * entity ID, not a field object offset; as such, if an entity ID is
-         * given that does not have a field object, this opcode will store
-         * zero in each of the four address specified.
+         * Retrieves the coordinates of the field object that the entity, whose ID specified in A,
+         * is associated with. This opcode uses an entity ID, not a field object offset; as such,
+         * if an entity ID is given that does not have a field object, this opcode will store zero
+         * in each of the four address specified.
          *
          * @param[in,out] code_gen Code generator to append lines.
          */
@@ -271,58 +294,48 @@ class FieldModelInstruction : public KernelCallInstruction{
          * |0xC2|B1/B2|B3/B4|X|X|Y|Y|Z|Z|I|I|K|A|D|S|
          *
          * Arguments
-         * - const Bit[4] B1: Bank to retrieve X-coordinate, or zero if X is
-         * specified as a literal value.
-         * - const Bit[4] B2: Bank to retrieve Y-coordinate, or zero if Y is
-         * specified as a literal value.
-         * - const Bit[4] B3: Bank to retrieve Z-coordinate, or zero if Z is
-         * specified as a literal value.
-         * - const Bit[4] B4: Bank to retrieve ID, or zero if I is specified
-         * as a literal value.
-         * - const Short X: X-coordinate of the end of the ladder, or address
-         * to find X-coordinate if B1 is non-zero.
-         * - const Short Y: Y-coordinate of the end of the ladder, or address
-         * to find Y-coordinate if B2 is non-zero.
-         * - const Short Z: Z-coordinate of the end of the ladder, or address
-         * to find Z-coordinate if B3 is non-zero.
-         * - const UShort I: ID of the walkmesh triangle found at the end of
-         * the ladder, or address to find ID if B4 is non-zero.
+         * - const Bit[4] B1: Bank to retrieve X-coordinate, or zero if X is specified as a literal
+         * value.
+         * - const Bit[4] B2: Bank to retrieve Y-coordinate, or zero if Y is specified as a literal
+         * value.
+         * - const Bit[4] B3: Bank to retrieve Z-coordinate, or zero if Z is specified as a literal
+         * value.
+         * - const Bit[4] B4: Bank to retrieve ID, or zero if I is specifiedas a literal value.
+         * - const Short X: X-coordinate of the end of the ladder, or address to find X-coordinate
+         * if B1 is non-zero.
+         * - const Short Y: Y-coordinate of the end of the ladder, or address to find Y-coordinate
+         * if B2 is non-zero.
+         * - const Short Z: Z-coordinate of the end of the ladder, or address to find Z-coordinate
+         * if B3 is non-zero.
+         * - const UShort I: ID of the walkmesh triangle found at the end of the ladder, or address
+         * to find ID if B4 is non-zero.
          * - const UByte K: The keys used to move the character on the ladder.
-         * - const UByte A: Animation ID for the field object's movement
-         * animation.
-         * - const UByte D: Direction the character faces when climbing the
-         * ladder.
+         * - const UByte A: Animation ID for the field object's movement animation.
+         * - const UByte D: Direction the character faces when climbing the ladder.
          * - const UByte S: Speed of the animation whilst climbing the ladder.
          *
-         * Causes the character to climb a ladder; that is, switching from
-         * standard walkmesh movement, to climbing along a line connecting two
-         * points on the walkmesh. If B1, B2, B3 or B4 is non-zero, then the
-         * value for that particular component is taken from memory using the
-         * corresponding bank and address specified, rather than as a literal
-         * value. Both retrieved values and literals can be used for different
-         * components. If using X, Y, Z or I as addresses, the lower byte
-         * should hold the address whilst the higher byte should be zero. The
-         * coordinates specify the end-point of the ladder; the current
-         * position of the character is used as the start point. The ID of the
-         * walkmesh triangle must be specified; this is the triangle the
-         * character will step onto after reaching the end point of the ladder.
-         * The K value specifies the keys used to move the character across
-         * the ladder; keys outside the range found in the table will cause
-         * unpredictable behavior. The animation ID specifies an offset into
-         * the field object's animation list; this animation is played at the
-         * speed specified by S whilst the character climbs. Finally, the D
-         * argument is a direction value in the game's standard direction
-         * format, which orients the character on the ladder. This opcode is
-         * used as part of the character's entity, rather than in a separate
-         * entity, as with a LINE. A LINE is used to set the start point of
-         * the ladder on the walkmesh. When this LINE is crossed by the
-         * player, a script in the LINE then uses a PREQ (or one of its
-         * variants), calling the script in the party leader that defines the
-         * LADER, causing the character to switch to 'climbing mode'. To set
-         * up a two-way ladder, two LINEs are used at either end, with
-         * different values for the LADER arguments, such as differing end
-         * points. If this opcode is used as part of a non-playable character
-         * entity, the NPC object will automatically climb from the start to
+         * Causes the character to climb a ladder; that is, switching from standard walkmesh
+         * movement, to climbing along a line connecting two points on the walkmesh. If B1, B2, B3
+         * or B4 is non-zero, then the value for that particular component is taken from memory
+         * using the corresponding bank and address specified, rather than as a literal value. Both
+         * retrieved values and literals can be used for different components. If using X, Y, Z or
+         * I as addresses, the lower byte should hold the address whilst the higher byte should be
+         * zero. The coordinates specify the end-point of the ladder; the current position of the
+         * character is used as the start point. The ID of the walkmesh triangle must be specified;
+         * this is the triangle the character will step onto after reaching the end point of the
+         * ladder. The K value specifies the keys used to move the character across the ladder;
+         * keys outside the range found in the table will cause unpredictable behavior. The
+         * animation ID specifies an offset into the field object's animation list; this animation
+         * is played at the speed specified by S whilst the character climbs. Finally, the D
+         * argument is a direction value in the game's standard direction format, which orients the
+         * character on the ladder. This opcode is used as part of the character's entity, rather
+         * than in a separate entity, as with a LINE. A LINE is used to set the start point of
+         * the ladder on the walkmesh. When this LINE is crossed by the player, a script in the
+         * LINE then uses a PREQ (or one of its variants), calling the script in the party leader
+         * that defines the LADER, causing the character to switch to 'climbing mode'. To set up a
+         * two-way ladder, two LINEs are used at either end, with different values for the LADER
+         * arguments, such as differing end points. If this opcode is used as part of a
+         * non-playable character entity, the NPC object will automatically climb from the start to
          * the end point without need for player interaction.
          *
          * @param[in,out] code_gen Code generator to append lines.
@@ -330,6 +343,48 @@ class FieldModelInstruction : public KernelCallInstruction{
          */
         void ProcessLADER(CodeGenerator* code_gen, const std::string& entity);
 
+        /**
+         * Processes an SLIDR opcode.
+         *
+         * Opcode: 0xC6
+         * Short name: SLIDR
+         * Long name: Solid Range
+         *
+         * Memory layout (3 bytes)
+         * |0xC6|B|R|
+         *
+         * Arguments:
+         *
+         * - const UByte B: Bank to retrieve R, or zero if R is specified as a literal value.
+         * - const UByte R: Range value.
+         *
+         * Adjusts the range of the collision circle for the entity's field object, changing the
+         * distance threshold for collisions between the object, and both other objects and the
+         * walkmesh boundaries. Lower values produce a lower circle for the object; higher values
+         * increase the circle size.
+         */
+        void ProcessSLIDR(CodeGenerator* code_gen, const std::string& entity);
+
+        /**
+         * Processes a SOLID opcode.
+         *
+         * Opcode: 0xC7
+         * Short name: SOLID
+         * Long name: Solid object
+         *
+         * Memory layout (2 bytes)
+         * |0xC7|S|
+         *
+         * Arguments:
+         *
+         * const UByte S: Switch on/off (0/1, respectively).
+         *
+         * Switches the solidity of the field object associated with this entity; that is, turns
+         * collision detection on or off. When off, the playable character will be able to walk
+         * through the entity's object, as if it were not there. This may be used for such objects
+         * as save points, where the character must be able to walk through the object to be able
+         * to access the save menu item.
+         */
         void ProcessSOLID(CodeGenerator* code_gen, const std::string& entity);
 
         /**
@@ -343,45 +398,36 @@ class FieldModelInstruction : public KernelCallInstruction{
          * |0xC3|B1/B2|B3/B4|T|X|Y|Z|S|
          *
          * Arguments:
-         * - const Bit[4] B1: Bank to retrieve X offset, or zero if X is
-         * specified as a literal.
-         * - const Bit[4] B2: Bank to retrieve Y offset, or zero if Y is
-         * specified as a literal.
-         * - const Bit[4] B3: Bank to retrieve Z offset, or zero if Z is
-         * specified as a literal.
-         * - const Bit[4] B4: Bank to retrieve speed, or zero if S is
-         * specified as a literal.
+         * - const Bit[4] B1: Bank to retrieve X offset, or zero if X is specified as a literal.
+         * - const Bit[4] B2: Bank to retrieve Y offset, or zero if Y is specified as a literal.
+         * - const Bit[4] B3: Bank to retrieve Z offset, or zero if Z is specified as a literal.
+         * - const Bit[4] B4: Bank to retrieve speed, or zero if S is specified as a literal.
          * - const UByte T: Type of movement.
-         * - const Short X: X offset amount, relative to current position, or
-         * address to find X offset, if B1 is non-zero.
-         * - const Short Y: Y offset amount, relative to current position, or
-         * address to find Y offset, if B2 is non-zero.
-         * - const Short Z: Z offset amount, relative to current position, or
-         * address to find Z offset, if B3 is non-zero.
-         * - const UShort S: Speed of the offset movement, if type is
-         * non-zero, or address to find speed, if B4 is non-zero.
-         *
-         * Offsets the field object, belonging to the entity whose script this
-         * opcode resides in, by a certain amount. After being offset, the
-         * character continues to be constrained in movement as defined by the
-         * walkmesh's shape, but at a certain distance away from the normal
-         * walkmesh position. Other field objects are unaffected, and their
-         * position or movements are maintained on the walkmesh's original
-         * position. If B1, B2, B3 or B4 is non-zero, then the value for that
-         * particular component is taken from memory using the corresponding
-         * bank and address specified, rather than as a literal value. Both
-         * retrieved values and literals can be used for different components.
-         * If using T, X, Y or S as addresses, the lower byte should hold the
-         * address whilst the higher byte should be zero. The amount to offset
-         * is specified relative to the current position. If Type is
-         * specified, the object moves gradually from its current point to the
-         * offset position; this can be used to simulate movements such as
-         * elevators. Any type outside the range in the table will cause the
-         * offset not to occur. If the object is set to move gradually, then
-         * the speed of offset can be set; the greater the number, the slower
-         * the object moves to its target offset. Script execution may also be
-         * halted until the gradual offset has been completed. For this, see
-         * OFSTW.
+         * - const Short X: X offset amount, relative to current position, or address to find X
+         * offset, if B1 is non-zero.
+         * - const Short Y: Y offset amount, relative to current position, or address to find Y
+         * offset, if B2 is non-zero.
+         * - const Short Z: Z offset amount, relative to current position, or address to find Z
+         * offset, if B3 is non-zero.
+         * - const UShort S: Speed of the offset movement, if type is non-zero, or address to find
+         * speed, if B4 is non-zero.
+         *
+         * Offsets the field object, belonging to the entity whose script this opcode resides in,
+         * by a certain amount. After being offset, the character continues to be constrained in
+         * movement as defined by the walkmesh's shape, but at a certain distance away from the
+         * normal walkmesh position. Other field objects are unaffected, and their position or
+         * movements are maintained on the walkmesh's original position. If B1, B2, B3 or B4 is
+         * non-zero, then the value for that particular component is taken from memory using the
+         * corresponding bank and address specified, rather than as a literal value. Both retrieved
+         * values and literals can be used for different components. If using T, X, Y or S as
+         * addresses, the lower byte should hold the address whilst the higher byte should be zero.
+         * The amount to offset is specified relative to the current position. If Type is
+         * specified, the object moves gradually from its current point to the offset position;
+         * this can be used to simulate movements such as elevators. Any type outside the range in
+         * the table will cause the offset not to occur. If the object is set to move gradually,
+         * then the speed of offset can be set; the greater the number, the slower the object moves
+         * to its target offset. Script execution may also be halted until the gradual offset has
+         * been completed. For this, see OFSTW.
          *
          * @param[in,out] code_gen Code generator to append lines.
          * @param[in] entity The entity name.

+ 25 - 0
V-Gears-Installer/include/decompiler/field/instruction/FieldWindowInstruction.h

@@ -75,6 +75,31 @@ class FieldWindowInstruction : public KernelCallInstruction{
 
         void ProcessMESSAGE(CodeGenerator* code_gen, const std::string& script_name);
 
+        /**
+         * Processes an ASK opcode.
+         *
+         * Opcode: 0x48
+         * Short name: ASK
+         * Long name: Ask Question
+         *
+         * Memory layout (7 bytes)
+         * |0x48|Bank|Win|Mess|First|Last|Addr|
+         *
+         * Arguments
+         *
+         * const UByte Bank: Bank to put line number selected.
+         * const UByte Win: Window ID to place the question in. (Initialized with WINDOW)
+         * const UByte Mess: Which dialog to display from dialog table.
+         * const UByte First: Line from dialog where the first question is.
+         * const UByte Last: Line from dialog where the last question is.
+         * const UByte Addr: Address in bank where line selected is written.
+         *
+         * The ASK command opens a window with a set of choices to be picked with the "selector
+         * finger". If ASK is called on an open window ID, the window will shrink closed and
+         * re-open with the new data.
+         */
+        void ProcessASK(CodeGenerator* code_gen, const std::string& script_name);
+
         void ProcessMPNAM(CodeGenerator* code_gen, const std::string& script_name);
 
 

+ 32 - 3
V-Gears-Installer/src/decompiler/field/instruction/FieldCameraInstruction.cpp

@@ -33,12 +33,12 @@ void FieldCameraInstruction::ProcessInst(
     case OPCODES::NFADE: ProcessNFADE(code_gen); break;
     case OPCODES::SHAKE: code_gen->WriteTodo(md.GetEntityName(), "SHAKE"); break;
     case OPCODES::SCRLO: code_gen->WriteTodo(md.GetEntityName(), "SCRLO"); break;
-    case OPCODES::SCRLC: code_gen->WriteTodo(md.GetEntityName(), "SCRLC"); break;
+    case OPCODES::SCRLC: ProcessSCRLC(code_gen); break;
     case OPCODES::SCR2D: ProcessSCR2D(code_gen); break;
-    case OPCODES::SCRCC: code_gen->WriteTodo(md.GetEntityName(), "SCRCC"); break;
+    case OPCODES::SCRCC: ProcessSCRCC(code_gen); break;
     case OPCODES::SCR2DC: ProcessSCR2DC(code_gen); break;
     case OPCODES::SCRLW: code_gen->WriteTodo(md.GetEntityName(), "SCRLW"); break;
-    case OPCODES::SCR2DL: code_gen->WriteTodo(md.GetEntityName(), "SCR2DL"); break;
+    case OPCODES::SCR2DL: ProcessSCR2DL(code_gen); break;
     case OPCODES::VWOFT: ProcessVWOFT(code_gen); break;
     case OPCODES::FADE: ProcessFADE(code_gen); break;
     case OPCODES::FADEW: code_gen->WriteTodo(md.GetEntityName(), "FADEW"); break;
@@ -78,6 +78,13 @@ void FieldCameraInstruction::ProcessNFADE(CodeGenerator* code_gen){
     );
 }
 
+void FieldCameraInstruction::ProcessSCRLC(CodeGenerator* code_gen){
+    std::string mode = "Background2D.NONE";
+    if (params_[3]->GetUnsigned() == 2) mode = "Background2D.LINEAR";
+    else if (params_[3]->GetUnsigned() == 3) mode = "Background2D.SMOOTH";
+    code_gen->AddOutputLine("background2d:scroll_to_player(" + mode + ", 0)");
+}
+
 void FieldCameraInstruction::ProcessSCR2D(CodeGenerator* code_gen){
     // kUpScaler.
     FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
@@ -93,6 +100,28 @@ void FieldCameraInstruction::ProcessSCR2D(CodeGenerator* code_gen){
     ).str());
 }
 
+void FieldCameraInstruction::ProcessSCRCC(CodeGenerator* code_gen){
+    code_gen->AddOutputLine("background2d:scroll_to_player(Background2D.NONE, 0)");
+}
+
+void FieldCameraInstruction::ProcessSCR2DL(CodeGenerator* code_gen){
+    FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
+    const auto& x = FieldCodeGenerator::FormatValueOrVariable(
+      cg->GetFormatter(), params_[0]->GetUnsigned(), params_[4]->GetSigned()
+    );
+    const auto& y = FieldCodeGenerator::FormatValueOrVariable(
+      cg->GetFormatter(), params_[1]->GetUnsigned(), params_[5]->GetSigned()
+    );
+    const auto& speed = FieldCodeGenerator::FormatValueOrVariable(
+      cg->GetFormatter(), params_[3]->GetUnsigned(), params_[6]->GetUnsigned(),
+      FieldCodeGenerator::ValueType::Float, 30.0f
+    );
+    code_gen->AddOutputLine((
+      boost::format("background2d:scroll_to_position(%1% * 3, %2% * 3, Background2D.LINEAR, %3%)")
+      % x % y % speed
+    ).str());
+}
+
 void FieldCameraInstruction::ProcessSCR2DC(CodeGenerator* code_gen){
     FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
     const auto& x = FieldCodeGenerator::FormatValueOrVariable(

+ 37 - 2
V-Gears-Installer/src/decompiler/field/instruction/FieldModelInstruction.cpp

@@ -99,7 +99,7 @@ void FieldModelInstruction::ProcessInst(
         case OPCODES::MOVE: ProcessMOVE(code_gen, md.GetEntityName()); break;
         case OPCODES::CMOVE: code_gen->WriteTodo(md.GetEntityName(), "CMOVE"); break;
         case OPCODES::MOVA: code_gen->WriteTodo(md.GetEntityName(), "MOVA"); break;
-        case OPCODES::TURA: code_gen->WriteTodo(md.GetEntityName(), "TURA"); break;
+        case OPCODES::TURA: ProcessTURA(code_gen, md.GetEntityName(), eng); break;
         case OPCODES::ANIMW: code_gen->WriteTodo(md.GetEntityName(), "ANIMW"); break;
         case OPCODES::FMOVE: code_gen->WriteTodo(md.GetEntityName(), "FMOVE"); break;
         case OPCODES::ANIME2: code_gen->WriteTodo(md.GetEntityName(), "ANIME2"); break;
@@ -133,7 +133,7 @@ void FieldModelInstruction::ProcessInst(
             code_gen->AddOutputLine("self." + md.GetEntityName() + ":offset_sync()");
             break;
         case OPCODES::TALKR: code_gen->WriteTodo(md.GetEntityName(), "TALKR"); break;
-        case OPCODES::SLIDR: code_gen->WriteTodo(md.GetEntityName(), "SLIDR"); break;
+        case OPCODES::SLIDR: ProcessSLIDR(code_gen, md.GetEntityName()); break;
         case OPCODES::SOLID: ProcessSOLID(code_gen, md.GetEntityName()); break;
         case OPCODES::TLKR2: code_gen->WriteTodo(md.GetEntityName(), "TLKR2"); break;
         case OPCODES::SLDR2: code_gen->WriteTodo(md.GetEntityName(), "SLDR2"); break;
@@ -288,6 +288,28 @@ void FieldModelInstruction::ProcessMOVE(CodeGenerator* code_gen, const std::stri
     code_gen->AddOutputLine((boost::format("self.%1%:move_sync()") % entity).str());
 }
 
+void FieldModelInstruction::ProcessTURA(
+  CodeGenerator* code_gen, const std::string& entity, const FieldEngine& engine
+){
+    FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
+    const auto& dest_entity = engine.EntityByIndex(params_[0]->GetSigned());
+    std::string direction;
+    switch (params_[1]->GetUnsigned()){
+        case 0: direction = "Entity.CLOCKWISE"; break;
+        case 1: direction = "Entity.ANTICLOCKWISE"; break;
+        case 2: direction = "Entity.CLOSEST"; break;
+        // Default to closest:
+        default: direction = "Entity.CLOSEST"; break;
+    }
+    auto steps = params_[2]->GetUnsigned();
+    const float scaled_steps = static_cast<float>(steps) / 30.0f;
+    code_gen->AddOutputLine((
+      boost::format("self.%1%:turn_to_entity(entity_manager:get_entity(\"%2%\"), %3%, %4%)")
+      % entity % dest_entity.GetName() % direction % scaled_steps
+    ).str());
+    code_gen->AddOutputLine((boost::format("self.%1%:turn_sync()") % entity).str());
+}
+
 void FieldModelInstruction::ProcessMSPED(CodeGenerator* code_gen, const std::string& entity){
     FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
     const float scale = 128.0f * cg->GetScaleFactor();
@@ -490,6 +512,19 @@ void FieldModelInstruction::ProcessLADER(CodeGenerator* code_gen, const std::str
     code_gen->AddOutputLine((boost::format("self.%1%:linear_sync()") % entity).str());
 }
 
+void FieldModelInstruction::ProcessSLIDR(CodeGenerator* code_gen, const std::string& entity){
+    FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
+    const float scale = 128.0f * cg->GetScaleFactor();
+    float radius = std::stof(FieldCodeGenerator::FormatValueOrVariable(
+      cg->GetFormatter(), params_[0]->GetUnsigned(), params_[1]->GetSigned(),
+      FieldCodeGenerator::ValueType::Float, scale
+    ));
+    code_gen->AddOutputLine((
+      boost::format("self.%1%:set_solid_radius(%2%)")
+      % entity % radius
+    ).str());
+}
+
 void FieldModelInstruction::ProcessSOLID(CodeGenerator* code_gen, const std::string& entity){
     code_gen->AddOutputLine((
       boost::format("self.%1%:set_solid(%2%)")

+ 17 - 1
V-Gears-Installer/src/decompiler/field/instruction/FieldWindowInstruction.cpp

@@ -41,7 +41,7 @@ void FieldWindowInstruction::ProcessInst(
         case OPCODES::MPARA: code_gen->WriteTodo(md.GetEntityName(), "MPARA"); break;
         case OPCODES::MPRA2: code_gen->WriteTodo(md.GetEntityName(), "MPRA2"); break;
         case OPCODES::MPNAM: ProcessMPNAM(code_gen, eng.GetScriptName()); break;
-        case OPCODES::ASK: code_gen->WriteTodo(md.GetEntityName(), "ASK"); break;
+        case OPCODES::ASK: ProcessASK(code_gen, eng.GetScriptName()); break;
         case OPCODES::MENU: ProcessMENU(code_gen); break;
         case OPCODES::MENU2: ProcessMENU2(code_gen); break;
         case OPCODES::WINDOW: ProcessWINDOW(code_gen); break;
@@ -195,6 +195,22 @@ void FieldWindowInstruction::ProcessMESSAGE(
     );
 }
 
+void FieldWindowInstruction::ProcessASK(CodeGenerator* code_gen, const std::string& script_name){
+    auto bank = params_[0]->GetUnsigned() + params_[1]->GetUnsigned();
+    auto window_id = params_[2]->GetUnsigned();
+    auto dialog_id = params_[3]->GetUnsigned();
+    auto first = params_[4]->GetUnsigned();
+    auto last = params_[5]->GetUnsigned();
+    auto address = params_[6]->GetUnsigned();
+    code_gen->AddOutputLine((
+      boost::format("dialog_ask(\"%1%\", \"%2%_%3%\", %4%, %5%, %6%, %7%)")
+      % window_id % script_name % dialog_id % first % last % bank % address
+    ).str());
+    //code_gen->AddOutputLine(
+    //  (boost::format("dialog:ask_wait_for_close(\"%1%\")") % window_id).str()
+    //);
+}
+
 void FieldWindowInstruction::ProcessWCLSE(CodeGenerator* code_gen){
     // Close a dialog.
     auto windowId = params_[0]->GetUnsigned();

+ 8 - 0
V-Gears/include/core/Background2D.h

@@ -112,6 +112,14 @@ class Background2D : public Ogre::RenderQueueListener{
          */
         Entity* GetAutoScrollEntity() const;
 
+        /**
+         * Scrolls the background to the position of the playable character.
+         *
+         * @param[in] type Scroll type.
+         * @param[in] seconds Scroll duration.
+         */
+        void ScriptScrollToPlayer(const SCROLL_TYPE type, const unsigned int seconds);
+
         /**
          * Scrolls the background to a position.
          *

+ 5 - 0
V-Gears/include/core/ScriptManagerBinds.h

@@ -673,6 +673,11 @@ void ScriptManager::InitBinds(){
             "autoscroll_to_entity",
             (void(Background2D::*)(Entity*)) &Background2D::ScriptAutoScrollToEntity
           )
+          .def(
+            "scroll_to_player",
+            (void(Background2D::*)(const Background2D::SCROLL_TYPE, const unsigned int))
+              &Background2D::ScriptScrollToPlayer
+          )
           .def(
             "scroll_to_position",
             (void(Background2D::*)(

+ 14 - 0
V-Gears/include/data/VGearsBackgroundFile.h

@@ -230,6 +230,11 @@ namespace VGears{
 
             typedef std::vector<SpriteData*> SpritePtrList;
 
+            /**
+             * Sorts the sprites.
+             */
+            void SortSprites();
+
             /**
              * A layer of a background.
              */
@@ -408,6 +413,15 @@ namespace VGears{
              * The background pages.
              */
             std::array<Page, PAGE_COUNT> pages_;
+
+            /**
+             * Sorting algorithm for sprite sorting.
+             *
+             * @param[in] sprite1 First sprite to compare.
+             * @param[in] sprite2 Second sprite to compare.
+             * @return Ture if sprite 1 goes before sprite 2, false otherwise.
+             */
+            static bool SpriteSorter(SpriteData sprite1, SpriteData sprite2);
     };
 
     typedef Ogre::SharedPtr<BackgroundFile> BackgroundFilePtr;

+ 14 - 0
V-Gears/src/core/Background2D.cpp

@@ -21,6 +21,7 @@
 #include <OgreTechnique.h>
 #include "core/Background2D.h"
 #include "core/CameraManager.h"
+#include "core/EntityManager.h"
 #include "core/ConfigVar.h"
 #include "core/DebugDraw.h"
 #include "core/Logger.h"
@@ -261,6 +262,19 @@ void Background2D::ScriptAutoScrollToEntity(Entity* entity){scroll_entity_ = ent
 
 Entity* Background2D::GetAutoScrollEntity() const{return scroll_entity_;}
 
+void Background2D::ScriptScrollToPlayer(const SCROLL_TYPE type, const unsigned int seconds){
+    Entity* player = EntityManager::getSingleton().ScriptGetPlayerEntity();
+    if (player == nullptr) return;
+    Ogre::Vector2 position(player->GetPosition().x, player->GetPosition().y);
+    scroll_position_start_ = position_;
+    scroll_position_end_ = position;
+    scroll_type_ = type;
+    scroll_seconds_ = seconds;
+    scroll_current_seconds_ = 0;
+
+    return;
+}
+
 void Background2D::ScriptScrollToPosition(
   const float x, const float y, const SCROLL_TYPE type, const float seconds
 ){

+ 0 - 1
V-Gears/src/core/DialogsManager.cpp

@@ -349,7 +349,6 @@ int DialogsManager::Sync(const char* d_name){
         LOG_TRIVIAL("sync: dialog '" + Ogre::String(d_name) + "' doesn't exist.");
         return 1;
     }
-
     ScriptId script = ScriptManager::getSingleton().GetCurrentScriptId();
     messages_[id]->sync.push_back(script);
     return -1;

+ 33 - 2
V-Gears/src/core/Savemap.cpp

@@ -1292,8 +1292,8 @@ void Savemap::Read(std::string file_name){
                                 TiXmlNode* stat_node = char_node->FirstChild();
                                 while (stat_node != nullptr){
                                     if (
-                                      char_node->Type() == TiXmlNode::TINYXML_ELEMENT
-                                      && char_node->ValueStr() == "Stat"
+                                      stat_node->Type() == TiXmlNode::TINYXML_ELEMENT
+                                      && stat_node->ValueStr() == "Stat"
                                     ){
                                         std::string stat = "";
                                         int base = -1;
@@ -1528,6 +1528,37 @@ void Savemap::Read(std::string file_name){
                 character_node = character_node->NextSibling();
             }
         }
+        else if (node->Type() == TiXmlNode::TINYXML_ELEMENT && node->ValueStr() == "DataBanks"){
+            TiXmlNode* bank_node = node->FirstChild();
+            while (bank_node != nullptr){
+                if (
+                  bank_node->Type() == TiXmlNode::TINYXML_ELEMENT
+                  && bank_node->ValueStr() == "Bank"
+                ){
+                    int bank = -1;
+                    TiXmlElement* bank_elm = bank_node->ToElement();
+                    bank_elm->QueryIntAttribute("id", &bank);
+                    if (bank >= 0 && bank < BANK_COUNT){
+                        TiXmlNode* addr_node = bank_node->FirstChild();
+                        while (addr_node != nullptr){
+                            if (
+                                addr_node->Type() == TiXmlNode::TINYXML_ELEMENT
+                              && addr_node->ValueStr() == "Address"
+                            ){
+                                int addr = -1;
+                                int val = 0;
+                                TiXmlElement* addr_elm = addr_node->ToElement();
+                                addr_elm->QueryIntAttribute("address", &addr);
+                                addr_elm->QueryIntAttribute("value", &val);
+                                if (addr >= 0 && addr < BANK_ADDRESS_COUNT) data_[bank][addr] = val;
+                            }
+                            addr_node = addr_node->NextSibling();
+                        }
+                    }
+                }
+                bank_node = bank_node->NextSibling();
+            }
+        }
         node = node->NextSibling();
     }
 }

+ 14 - 1
V-Gears/src/data/VGearsBackgroundFile.cpp

@@ -86,6 +86,15 @@ namespace VGears{
                     SpriteData* ptr = &it;
                     sprites.push_back(ptr);
                 }
+                std::sort(layers_[i].sprites.begin(), layers_[i].sprites.end(), SpriteSorter);
+            }
+        }
+    }
+
+    void BackgroundFile::SortSprites(){
+        for (size_t i(0); i < LAYER_COUNT; ++ i){
+            if (layers_[i].enabled){
+                std::sort(layers_[i].sprites.begin(), layers_[i].sprites.end(), SpriteSorter);
             }
         }
     }
@@ -250,7 +259,6 @@ namespace VGears{
                         color[data_index] = colour.getAsARGB();
                     }
                 }
-                
             }
             // Source in the texture atlas is where it's just copied to.
             sprite.src.x = dst_x;
@@ -262,4 +270,9 @@ namespace VGears{
         return image;
     }
 
+    bool BackgroundFile::SpriteSorter(SpriteData sprite1, SpriteData sprite2){
+        if (sprite1.dst.y == sprite2.dst.y) return sprite1.dst.x < sprite2.dst.x;
+        else return sprite1.dst.y < sprite2.dst.y;
+    }
+
 }

+ 24 - 2
data/data/scripts/field.lua

@@ -8,8 +8,8 @@ export_character_names()
 load_field_map_request = function(map_name, point_name)
     if type(map_name) == "string" and map_name ~= "" then
 
-        System.MapChanger.map_name = map_name
-        System.MapChanger.point_name = point_name
+        System["MapChanger"].map_name = map_name
+        System["MapChanger"].point_name = point_name
         script:request(Script.SYSTEM, "MapChanger", "ffvii_field", 0)
     end
 end
@@ -236,11 +236,33 @@ play_map_music = function(id)
     end
 end
 
+--- Sets the map name
+--
+-- It's not set from a literal text, but from a dialog identifier.
+--
+-- @param text_id ID of the dialog text that holds the map name.
 set_map_name = function(text_id)
     dialog:set_map_name(text_id)
     System["MapChanger"].location_name = dialog:get_map_name()
 end
 
+--- Shows a choice dialog and stores the result in the selected bank address.
+--
+-- @param window_id ID of the window to show the dialog in.
+-- @param message_id ID of the text to show in the window.
+-- @param first First selectable line.
+-- @param last Last selectable line.
+-- @param bank Data bank to store the selected line in.
+-- @param address Addres of the bank at which to store the selected line in.
+
+dialog_ask = function(window_id, message_id, first, last, bank, address)
+    dialog:set_clickable(window_id, true)
+    dialog:set_cursor(window_id, first, last)
+    dialog:dialog_set_text(window_id, message_id)
+    dialog:dialog_wait_for_close(window_id)
+    Banks[bank][address] = dialog:get_cursor(window_id)
+end
+
 --- Utility to change fields and enter battles.
 System["MapChanger"] = {
 

+ 2 - 1
data/data/scripts/menu/main_menu.lua

@@ -156,6 +156,7 @@ UiContainer.MainMenu = {
                 else
                     ui_manager:get_widget("MainMenu.Container.Characters.Character" .. tostring(c) .. ".Portrait"):set_default_animation("RowFront")
                 end
+                ui_manager:get_widget("MainMenu.Container.Characters.Character" .. tostring(c) .. ".LimitLevel"):set_text(tostring(Characters[Party[c]].limit.current))
             else
                 UiContainer.populate_character_data("MainMenu.Container.Characters.Character" .. tostring(c), nil)
             end
@@ -167,7 +168,7 @@ UiContainer.MainMenu = {
         ui_manager:get_widget("MainMenu.Container.Menu.SaveText"):set_colour(0.4, 0.4, 0.4)
 
         -- Set location
-        ui_manager:get_widget("MainMenu.Container.Location.Text"):set_text((System["MapChanger"].location_name))
+        ui_manager:get_widget("MainMenu.Container.Location.Text"):set_text(System["MapChanger"].location_name)
 
         -- Set money
         local money_str = tostring(Inventory.money)

+ 0 - 1
data/data/scripts/menu/menu_helpers.lua

@@ -61,7 +61,6 @@ UiContainer.populate_character_data = function(widget, character)
     -- Calculate the HP and MP bar.
     local max_hp_width= ui_manager:get_widget(widget .. ".Data.HpLine"):get_width()
     local hp_width = math.floor(character.stats.hp.current * max_hp_width / character.stats.hp.base)
-    print ("HP width: " .. tostring(hp_width) .. " MAX: " .. tostring(max_hp_width))
     ui_manager:get_widget(widget .. ".Data.HpLineCurrent"):set_width(hp_width)
     local max_mp_width= ui_manager:get_widget(widget .. ".Data.MpLine"):get_width()
     local mp_width = character.stats.mp.current * max_mp_width / character.stats.mp.base

+ 3 - 0
data/data/scripts/save.lua

@@ -115,6 +115,9 @@ load_game = function(slot)
     -- TODO: Countdown
     for p = 1, 3 do
         Party[p] = savemap_manager:get_slot_party_member(slot, p - 1)
+        if Party[p] == -1 then
+            Party[p] = nil
+        end
     end
     for i = 0, MAX_INVENTORY_SLOTS do
         Inventory[i] = {item = 0, quantity = 0}