浏览代码

In-game main menu is a bit useable. The main menu uses real data to populate character windows.
Items menu is partially implemented, and it also uses real data.
In the data installer, the following opcodes have been implemented: GOLDU, GOLDD, CHGLD, DLITM, CHITM.
Lua Scripts reformatted and documented. Inventory, money and bank access functions improved.

Iñigo Valentin 3 年之前
父节点
当前提交
991d32922e

+ 3 - 3
V-Gears-Installer/include/decompiler/field/FieldCodeGenerator.h

@@ -272,7 +272,7 @@ class FieldCodeGenerator : public CodeGenerator{
                           bank, value_or_address
                         );
                         if (friendly_name.empty())
-                            return (boost::format("FFVII.Banks[%1%][%2%]") % bank % address).str();
+                            return (boost::format("Banks[%1%][%2%]") % bank % address).str();
                         return (boost::format("FFVII.Data.%1%") % friendly_name).str();
                     }
                 case 5:
@@ -281,13 +281,13 @@ class FieldCodeGenerator : public CodeGenerator{
                         const auto address = static_cast<uint32>(value_or_address)& 0xFF;
                         const  auto friendly_name = formatter.GetFriendlyVarName(bank, address);
                         if (friendly_name.empty())
-                            return (boost::format("FFVII.Banks[%1%][%2%]") % bank % address).str();
+                            return (boost::format("Banks[%1%][%2%]") % bank % address).str();
                         return "FFVII.Data." + friendly_name;
                     }
                 default:
                     return (
                       boost::format(
-                        "FFVII.Banks[%1%][%2%]"
+                        "Banks[%1%][%2%]"
                       ) % bank % (static_cast<uint32>(value_or_address) & 0xFF)
                     ).str();
             }

+ 142 - 0
V-Gears-Installer/include/decompiler/field/instruction/FieldPartyInstruction.h

@@ -41,7 +41,149 @@ class FieldPartyInstruction : public KernelCallInstruction{
 
     private:
 
+        /**
+         * Processes a GOLDU opcode.
+         *
+         * Opcode: 0x39
+         * Short name: GOLDU
+         * Long name: Gil Up
+         * Memory layout (6 bytes)
+         * |0x39|0|A|A|A|A|     or    |0x39|B/0|A|0|0|0|
+         *
+         * Arguments
+         * - Increase by a constant amount:
+         *   - const UByte 0: Zero.
+         *   - const ULong A: Amount to increase.
+         * - Increase by an amount found in memory:
+         *   - const Bit[4] B: Source bank.
+         *   - const UByte A: Source address.
+         *   - const UByte[3] 0: Three zero bytes.
+         *
+         * Increases the amount of gil by a constant amount, or by an amount found in the source
+         * bank B and address A. The total gil is capped above by 0xFFFFFFFF; attempts to increment
+         * further will fail.
+         *
+         * @param[in,out] code_gen Code generator to append lines.
+         */
+        void ProcessGOLDU(CodeGenerator* code_gen);
+
+        /**
+         * Processes a GOLDD opcode.
+         *
+         * Opcode: 0x40
+         * Short name: GOLDD
+         * Long name: Gil Down
+         * Memory layout (6 bytes)
+         * |0x39|0|A|A|A|A|     or    |0x39|B/0|A|0|0|0|
+         *
+         * Arguments
+         * - Decrease by a constant amount:
+         *   - const UByte 0: Zero.
+         *   - const ULong A: Amount to increase.
+         * - Decrease by an amount found in memory:
+         *   - const Bit[4] B: Source bank.
+         *   - const UByte A: Source address.
+         *   - const UByte[3] 0: Three zero bytes.
+         *
+         * Decreases the amount of gil by a constant amount, or by an amount found in the source
+         * bank B and address A. The total gil can't get below 0.
+         *
+         * @param[in,out] code_gen Code generator to append lines.
+         */
+        void ProcessGOLDD(CodeGenerator* code_gen);
+
+        /**
+         * Processes a STITM opcode.
+         *
+         * Opcode: 0x58
+         * Short name: STITM
+         * Long name: Set item
+         * Memory layout (4 bytes)
+         * |0x58|B1/B2|T|T|A|
+         *
+         * Arguments
+         * - const Bit[4] B1: Source bank 1, or zero if T is set as a constant value.
+         * - const Bit[4] B2: Source bank 2, or zero if A is set as a constant value.
+         * - const UShort T: Type of item to add, or source address to retrieve item type from.
+         * - const UByte A: Amount of item to add, or source address to retrieve quantity from.
+         *
+         * Adds a new item to the inventory. Either the item is added explicitly with values, in
+         * which case B1 and B2 are zero, or the item type and quantity are retrieved from memory.
+         * In this case, bank B1 and address T retrieve the item type, whilst bank B2 and address A
+         * retrieve the quantity to add.
+         *
+         * @param[in,out] code_gen Code generator to append lines.
+         */
         void ProcessSTITM(CodeGenerator* code_gen);
 
+        /**
+         * Processes a DLITM opcode.
+         *
+         * Opcode: 0x59
+         * Short name: STITM
+         * Long name: Set item
+         * Memory layout (4 bytes)
+         * |0x59|B1/B2|T|T|A|
+         *
+         * Arguments
+         * - const Bit[4] B1: Source bank 1, or zero if T is set as a constant value.
+         * - const Bit[4] B2: Source bank 2, or zero if A is set as a constant value.
+         * - const UShort T: Type of item to remove, or source address to retrieve item type from.
+         * - const UByte A: Amount of item to remove, or source address to retrieve quantity from.
+         *
+         * Removes a quantity of an item from the inventory. Either the item is removed explicitly
+         * with values, in which case B1 and B2 are zero, or the item type and quantity are
+         * retrieved from memory. In this case, bank B1 and address T retrieve the item type,
+         * whilst bank B2 and address A retrieve the quantity to remove.
+         *
+         * @param[in,out] code_gen Code generator to append lines.
+         */
+        void ProcessDLITM(CodeGenerator* code_gen);
+
+        /**
+         * Processes a CKITM opcode.
+         *
+         * Opcode: 0x5A
+         * Short name: CKITM
+         * Long name: Check item
+         * Memory layout (3 bytes)
+         * |0x5A|B|I|A|
+         *
+         * Arguments
+         * - const UByte B: Bank to store result.
+         * - const UShort I: Item ID to check.
+         * - const UByte A: Address to store result.
+         *
+         * Copies the amount of item I the player has in their inventory, to the bank and address
+         * specified.
+         *
+         * @param[in,out] code_gen Code generator to append lines.
+         */
+        void ProcessCKITM(CodeGenerator* code_gen);
+
         void ProcessPRTYE(CodeGenerator* code_gen);
+
+        /**
+         * Processes a CHGLD opcode.
+         *
+         * Opcode: 0x3B
+         * Short name: CHGLD
+         * Long name: Check gil
+         * Memory layout (6 bytes)
+         * |0x39|B1/B2|A1|A2|
+         *
+         * Arguments
+         * - const Bit[4] B1: Destination bank 1.
+         * - const Bit[4] B2: Destination bank 2.
+         * - const UByte A1: Destination address 1.
+         * - const UByte A2: Destination address 2.
+         *
+         * Copies the amount of gil the party has into the destination addresses. As the gil amount
+         * is a four-byte value, the arguments require two destination addresses to place two
+         * two-byte values into. Address 1 takes the lower two bytes of the gil amount, while
+         * address 2 takes the higher two bytes.
+         *
+         * @param[in,out] code_gen Code generator to append lines.
+         */
+        void ProcessCHGLD(CodeGenerator* code_gen);
 };

+ 7 - 2
V-Gears-Installer/src/DataInstaller.cpp

@@ -1093,6 +1093,7 @@ static void CollectSpawnPoints(
   VGears::FLevelFilePtr& field, const std::vector<std::string>& field_id_to_name_lookup,
   FieldSpawnPointsMap& spawn_points
 ){
+    //std::cout << "Decompile FIELD " << field->getName() << "\n";
     const size_t this_field_id = FieldId(field->getName(), field_id_to_name_lookup);
     // Decompile the script just so the MAPJUMPs can be collected. This is needed to construct the
     // full list of entries to each field.
@@ -1182,6 +1183,10 @@ static bool WillCrash(const Ogre::String& resource_name){
       || resource_name == "lastmap" // Background image crash
       || resource_name == "md_e1" // Background image crash
       || resource_name == "trnad_3" // Background image crash
+      || resource_name == "bonevil2" // stof
+      || resource_name == "coloin1" // boost::bad_format_string: format-string is ill-formed
+      || resource_name == "del3" // Segmentation fault.
+      || resource_name == "elmin4_2" // boost::bad_format_string: format-string is ill-formed
     ){return true;}
     return false;
 }
@@ -1257,9 +1262,9 @@ void DataInstaller::CollectionFieldSpawnAndScaleFactors(){
     if (iterator_counter_ < flevel_file_list_->size()){
         auto resource_name = (*flevel_file_list_)[iterator_counter_];
         // Exclude things that are not fields.
-        //if (IsAFieldFile(resource_name) /*&& IsTestField(resource_name)*/){
+        //if (IsAFieldFile(resource_name) && !WillCrash(resource_name)){
         // TODO: DEBUG: Only testing fields.
-        if (IsAFieldFile(resource_name) && IsTestField(resource_name)){
+        if (IsAFieldFile(resource_name) && IsTestField(resource_name) && !WillCrash(resource_name)){
             conversion_step_ ++;
             if (conversion_step_ == 1){
                 field_ = VGears::LZSFLevelFileManager::GetSingleton().load(

+ 3 - 1
V-Gears-Installer/src/decompiler/field/FieldDisassembler.cpp

@@ -682,7 +682,9 @@ bool FieldDisassembler::ReadOpCodesToPositionOrReturn(
                 ParseOpcode(full_opcode, "GTPYE", new FieldPartyInstruction(), 0, "NNBBB");
                 break;
             case OPCODES::GOLDU:
-                ParseOpcode(full_opcode, "GOLDU", new FieldPartyInstruction(), 0, "Nww");
+                // Goldu can have two argument formats: (nybble, word) or (nybble, byte, 3 bytes)
+                // Read it as nybble, byte, byte, byte, byte and operate later
+                ParseOpcode(full_opcode, "GOLDU", new FieldPartyInstruction(), 0, "BBBBB");
                 break;
             case OPCODES::GOLDD:
                 ParseOpcode(full_opcode, "GOLDD", new FieldPartyInstruction(), 0, "Nww");

+ 70 - 6
V-Gears-Installer/src/decompiler/field/instruction/FieldPartyInstruction.cpp

@@ -31,8 +31,8 @@ void FieldPartyInstruction::ProcessInst(
     switch (opcode_){
         case OPCODES::SPTYE: code_gen->WriteTodo(md.GetEntityName(), "SPTYE"); break;
         case OPCODES::GTPYE: code_gen->WriteTodo(md.GetEntityName(), "GTPYE"); break;
-        case OPCODES::GOLDU: code_gen->WriteTodo(md.GetEntityName(), "GOLDU"); break;
-        case OPCODES::GOLDD: code_gen->WriteTodo(md.GetEntityName(), "GOLDD"); break;
+        case OPCODES::GOLDU: ProcessGOLDU(code_gen); break;
+        case OPCODES::GOLDD: ProcessGOLDD(code_gen); break;
         case OPCODES::HMPMAX1: code_gen->WriteTodo(md.GetEntityName(), "HMPMAX1"); break;
         case OPCODES::HMPMAX2: code_gen->WriteTodo(md.GetEntityName(), "HMPMAX2"); break;
         case OPCODES::MHMMX: code_gen->WriteTodo(md.GetEntityName(), "MHMMX"); break;
@@ -42,8 +42,8 @@ void FieldPartyInstruction::ProcessInst(
         case OPCODES::HPU: code_gen->WriteTodo(md.GetEntityName(), "HPU"); break;
         case OPCODES::HPD: code_gen->WriteTodo(md.GetEntityName(), "HPD"); break;
         case OPCODES::STITM: ProcessSTITM(code_gen); break;
-        case OPCODES::DLITM: code_gen->WriteTodo(md.GetEntityName(), "DLITM"); break;
-        case OPCODES::CKITM: code_gen->WriteTodo(md.GetEntityName(), "CKITM"); break;
+        case OPCODES::DLITM: ProcessDLITM(code_gen); break;
+        case OPCODES::CKITM: ProcessCKITM(code_gen); break;
         case OPCODES::SMTRA: code_gen->WriteTodo(md.GetEntityName(), "SMTRA"); break;
         case OPCODES::DMTRA: code_gen->WriteTodo(md.GetEntityName(), "DMTRA"); break;
         case OPCODES::CMTRA: code_gen->WriteTodo(md.GetEntityName(), "CMTRA"); break;
@@ -54,7 +54,7 @@ void FieldPartyInstruction::ProcessInst(
         case OPCODES::MMBUD: code_gen->WriteTodo(md.GetEntityName(), "MMBUD"); break;
         case OPCODES::MMBLK: code_gen->WriteTodo(md.GetEntityName(), "MMBLK"); break;
         case OPCODES::MMBUK: code_gen->WriteTodo(md.GetEntityName(), "MMBUK"); break;
-        case OPCODES::CHGLD: code_gen->WriteTodo(md.GetEntityName(), "CHGLD"); break;
+        case OPCODES::CHGLD: ProcessCHGLD(code_gen); break;
         default:
             code_gen->AddOutputLine(FieldCodeGenerator::FormatInstructionNotImplemented(
               md.GetEntityName(), address_, opcode_
@@ -62,6 +62,40 @@ void FieldPartyInstruction::ProcessInst(
     }
 }
 
+void FieldPartyInstruction::ProcessGOLDU(CodeGenerator* code_gen){
+    if (params_[0]->GetUnsigned() == 0){
+        // Add by constant amount, params 1-4 are a dword with the amount.
+        // Quick and dirty hex operation:
+        long amount = params_[1]->GetUnsigned() + params_[2]->GetUnsigned() * 256
+          + params_[3]->GetUnsigned() * 256 * 256 + params_[4]->GetUnsigned() * 256 * 256 * 256;
+        code_gen->AddOutputLine((boost::format("Inventory.add_money(%1%)") % amount).str());
+    }
+    else{
+        // Add by variable amount, param 0 is bank and 1 is address. 2-4 are zeroes, ignored.
+        code_gen->AddOutputLine((
+          boost::format("Inventory.add_money(Banks[%1%][%2%])")
+            % params_[0]->GetUnsigned() % params_[1]->GetUnsigned()
+        ).str());
+    }
+}
+
+void FieldPartyInstruction::ProcessGOLDD(CodeGenerator* code_gen){
+    if (params_[0]->GetUnsigned() == 0){
+        // Add by constant amount, params 1-4 are a dword with the amount.
+        // Quick and dirty hex operation:
+        long amount = params_[1]->GetUnsigned() + params_[2]->GetUnsigned() * 256
+          + params_[3]->GetUnsigned() * 256 * 256 + params_[4]->GetUnsigned() * 256 * 256 * 256;
+        code_gen->AddOutputLine((boost::format("Inventory.add_money(%1%)") % amount).str());
+    }
+    else{
+        // Add by variable amount, param 0 is bank and 1 is address. 2-4 are zeroes, ignored.
+        code_gen->AddOutputLine((
+          boost::format("Inventory.add_money(Banks[%1%][%2%])")
+            % params_[0]->GetUnsigned() % params_[1]->GetUnsigned()
+        ).str());
+    }
+}
+
 void FieldPartyInstruction::ProcessSTITM(CodeGenerator* code_gen){
     FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
     const auto& item_id = FieldCodeGenerator::FormatValueOrVariable(
@@ -70,7 +104,29 @@ void FieldPartyInstruction::ProcessSTITM(CodeGenerator* code_gen){
     const auto& amount = FieldCodeGenerator::FormatValueOrVariable(
       cg->GetFormatter(), params_[1]->GetUnsigned(), params_[3]->GetUnsigned()
     );
-    code_gen->AddOutputLine((boost::format("FFVII.add_item(%1%, %2%)") % item_id % amount).str());
+    code_gen->AddOutputLine(
+      (boost::format("Inventory.add_item(%1%, %2%)") % item_id % amount).str()
+    );
+}
+
+void FieldPartyInstruction::ProcessDLITM(CodeGenerator* code_gen){
+    FieldCodeGenerator* cg = static_cast<FieldCodeGenerator*>(code_gen);
+    const auto& item_id = FieldCodeGenerator::FormatValueOrVariable(
+      cg->GetFormatter(), params_[0]->GetUnsigned(), params_[2]->GetUnsigned()
+    );
+    const auto& amount = FieldCodeGenerator::FormatValueOrVariable(
+      cg->GetFormatter(), params_[1]->GetUnsigned(), params_[3]->GetUnsigned()
+    );
+    code_gen->AddOutputLine(
+      (boost::format("Inventory.remove_item(%1%, %2%)") % item_id % amount).str()
+    );
+}
+
+void FieldPartyInstruction::ProcessCKITM(CodeGenerator* code_gen){
+    code_gen->AddOutputLine((
+      boost::format("Banks[%1][%2] = Inventory.get_item_quantity(%3%)")
+      % params_[0]->GetUnsigned() % params_[2]->GetUnsigned() % params_[1]->GetUnsigned()
+    ).str());
 }
 
 void FieldPartyInstruction::ProcessPRTYE(CodeGenerator* code_gen){
@@ -83,3 +139,11 @@ void FieldPartyInstruction::ProcessPRTYE(CodeGenerator* code_gen){
       boost::format("FFVII.set_party(%1%, %2%, %3%)") % chars[0] % chars[1] % chars[2]
     ).str());
 }
+
+void FieldPartyInstruction::ProcessCHGLD(CodeGenerator* code_gen){
+    code_gen->AddOutputLine((
+      boost::format("Inventory.money_to_banks(%1%, %2%, %3, %4)")
+        % params_[0]->GetUnsigned() % params_[1]->GetUnsigned()
+        % params_[2]->GetUnsigned() % params_[3]->GetUnsigned()
+    ).str());
+}

+ 199 - 0
data/data/screens/item_menu/item_menu.lua

@@ -0,0 +1,199 @@
+if UiContainer == nil then UiContainer = {} end
+
+--- The main menu.
+UiContainer.ItemMenu = {
+    bar_position = 1,
+    bar_position_total = 3,
+    item_cursor_position = 1,
+    item_cursor_position_total = 10,
+    item_cursor_item_selected = 0,
+    char_position = 1,
+    char_position_total = 10,
+    first_item_in_window = 0,
+
+    on_start = function(self)
+        return 0
+    end,
+
+    on_button = function(self, button, event)
+        if UiContainer.current_menu == "item" then
+            if UiContainer.current_submenu == "" then
+                if button == "Escape" and event == "Press" then
+                    script:request_end_sync(Script.UI, "ItemMenu", "hide", 0)
+                elseif button == "Right" then
+
+                elseif button == "Left" then
+
+                elseif button == "Enter" then
+                    if bar_position == 1 then
+                        print("Select item")
+                    elseif bar_position == 2 then
+                        print("Unimplemented: Order")
+                    elseif bar_position == 3 then
+                        print("Unimplemented: Key Items")
+                    else
+                        return 0
+                    end
+                else
+                    return 0
+                end
+            elseif UiContainer.current_submenu == "item_selection" then
+                local cursor = ui_manager:get_widget("ItemMenu.Container.ItemList.Cursor")
+                local desc_label = ui_manager:get_widget("ItemMenu.Container.ItemMenuSecondBar.ItemText")
+                if button == "Down" then
+                    if self.item_cursor_item_selected >= 320 then
+                        -- At the very end, do nothing
+                        return 0
+                    end
+                    self.item_cursor_item_selected = self.item_cursor_item_selected + 1
+                    if self.item_cursor_position == self.item_cursor_position_total then
+                        -- The cursor is at the end. Scroll page, but dont change cursor.
+                        self.first_item_in_window = self.first_item_in_window + 1
+                        self.populate_items()
+                    else
+                        -- The cursor is not at the end, move cursor down.
+                        self.item_cursor_position = self.item_cursor_position + 1
+                    end
+                    cursor:set_default_animation("Position" .. self.item_cursor_position)
+                    -- If an item is selected, show description
+                    if (Inventory[self.item_cursor_item_selected] ~= nil) then
+                        desc_label:set_text(Game.Items[Inventory[self.item_cursor_item_selected].item].description)
+                    else
+                        desc_label:set_text("")
+                    end
+                elseif button == "Up" then
+                    if self.item_cursor_item_selected == 0 then
+                        -- At the very begining, do nothing
+                        return 0
+                    end
+                    self.item_cursor_item_selected = self.item_cursor_item_selected - 1
+                    if self.item_cursor_position == self.item_cursor_position_total then
+                        -- The cursor is at the end. Scroll page, but dont change cursor.
+                        self.first_item_in_window = self.first_item_in_window - 1
+                        self.populate_items()
+                    else
+                        -- The cursor is not at the end, move cursor down.
+                        self.item_cursor_position = self.item_cursor_position - 1
+                    end
+                    cursor:set_default_animation("Position" .. self.item_cursor_position)
+                    -- If an item is selected, show description
+                    if (Inventory[self.item_cursor_item_selected] ~= nil) then
+                        desc_label:set_text(Game.Items[Inventory[self.item_cursor_item_selected].item].description)
+                    else
+                        desc_label:set_text("")
+                    end
+                end
+            elseif UiContainer.current_submenu == "char_selection" then
+            elseif UiContainer.current_submenu == "key_items" then
+            end
+        end
+        --[[if ui_manager:get_widget("ItemMenu"):is_visible() == true then
+            local characters  = ui_manager:get_widget("MainMenu.Container.Characters")
+            local menu        = ui_manager:get_widget("MainMenu.Container.Menu")
+            local menu_cursor = ui_manager:get_widget("MainMenu.Container.Menu.Cursor")
+            local timegil     = ui_manager:get_widget("MainMenu.Container.TimeGil")
+            local location    = ui_manager:get_widget("MainMenu.Container.Location")
+
+            if button == "Escape" and event == "Press" then
+                script:request_end_sync(Script.UI, "ItemMenu", "hide", 0)
+            elseif button == "Down" then
+                self.position = self.position + 1
+                if self.position > self.position_total then
+                    self.position = 1;
+                end
+                menu_cursor:set_default_animation("Position" .. self.position)
+            elseif button == "Up" then
+                self.position = self.position - 1
+                if self.position <= 0 then
+                    self.position = self.position_total;
+                end
+                menu_cursor:set_default_animation("Position" .. self.position)
+            end
+        elseif FFVII.MenuSettings.available == true then
+            if button == "Escape" and event == "Press" then
+                script:request_end_sync(Script.UI, "ItemMenu", "show", 0)
+            end
+        end
+        ]]
+        return 0
+    end,
+
+    --- Opens the main menu.
+    --
+    -- Populates and updates displayed data before opening.
+    show = function(self)
+        ui_manager:get_widget("ItemMenu"):set_visible(true)
+        UiContainer.current_menu = "item"
+        UiContainer.current_submenu = "item_selection"
+        Inventory.add_item(0, 7)
+        Inventory.add_item(1, 4)
+        Inventory.add_item(42, 3)
+        Inventory.add_item(1, 4)
+        self.populate_items(self)
+        return 0;
+    end,
+
+    --- Populates the visible inventory entries.
+    --
+    -- 10 entries visible at a time.
+    populate_items = function(self)
+        local first_item_in_window = self.first_item_in_window
+        for i = 0, 9 do
+            local w_icon = ui_manager:get_widget("ItemMenu.Container.ItemList.Icon" .. tostring(i))
+            local w_name = ui_manager:get_widget("ItemMenu.Container.ItemList.Name" .. tostring(i))
+            local w_colon = ui_manager:get_widget("ItemMenu.Container.ItemList.Colon" .. tostring(i))
+            local w_qty = ui_manager:get_widget("ItemMenu.Container.ItemList.Qty" .. tostring(i))
+            local inventory_index = i + first_item_in_window
+            if Inventory[inventory_index] == nil then
+                w_icon:set_visible(false)
+                w_name:set_visible(false)
+                w_colon:set_visible(false)
+                w_qty:set_visible(false)
+            else
+                local useable = Game.Items[Inventory[inventory_index].item].menu
+                -- TODO: Get icon.
+                w_icon:set_visible(true)
+                w_name:set_visible(true)
+                w_colon:set_visible(true)
+                w_qty:set_visible(true)
+                -- TODO: Gray for unuseable
+                if (useable == 1) then
+                    w_name:set_colour(1.0, 1.0, 1.0)
+                    w_colon:set_colour(1.0, 1.0, 1.0)
+                    w_qty:set_colour(1.0, 1.0, 1.0)
+                else
+                    w_name:set_colour(0.6, 0.6, 0.6)
+                    w_colon:set_colour(0.6, 0.6, 0.6)
+                    w_qty:set_colour(0.6, 0.6, 0.6)
+                end
+                w_name:set_text(Game.Items[Inventory[inventory_index].item].name)
+                w_qty:set_text(tostring(Inventory[inventory_index].quantity))
+                w_colon:set_text(":") -- Refresh text to apply colour.
+            end
+        end
+        return
+    end,
+
+
+    hide = function(self)
+        --[[local characters  = ui_manager:get_widget("MainMenu.Container.Characters")
+        local menu = ui_manager:get_widget("MainMenu.Container.Menu")
+        local timegil = ui_manager:get_widget("MainMenu.Container.TimeGil")
+        local location = ui_manager:get_widget("MainMenu.Container.Location")
+
+        characters:play_animation_stop("Disappear")
+        menu:play_animation_stop("Disappear")
+        timegil:play_animation_stop("Disappear")
+        location:play_animation_stop("Disappear")
+        location:animation_sync()]]
+
+        ui_manager:get_widget("ItemMenu"):set_visible(false)
+        UiContainer.current_menu = "main"
+        UiContainer.current_submenu = ""
+
+        --FFVII.MenuSettings.pause_available = true
+        --entity_manager:set_paused(false)
+
+        return 0;
+    end,
+}

+ 207 - 0
data/data/screens/item_menu/item_menu.xml

@@ -0,0 +1,207 @@
+<screen name="ItemMenu" script="screens/item_menu/item_menu.lua" visible="false">
+    <widget name="Container" width="368" height="240" origin_x="50%" origin_y="50%" align="center" valign="middle" scale="3 3" visible="true">
+        <widget name="ItemMenuTopBar" x="30" y="5" width="310" height="24" visible="true">
+            <prototype name="Window" />
+            <text_area name="ItemText" x="36" y="6" text_name="ItemMenuUse" font="FFVIIFont" visible="true" />
+            <text_area name="ItemText" x="96" y="6" text_name="ItemMenuOrder" font="FFVIIFont" visible="true" />
+            <text_area name="ItemText" x="156" y="6" text_name="ItemMenuKey" font="FFVIIFont" visible="true" />
+        </widget>
+        <widget name="ItemMenuTitle" x="254" y="5" width="86" height="24" visible="true">
+            <prototype name="Window" />
+            <text_area name="ItemText" x="16" y="6" text_name="ItemMenuItem" font="FFVIIFont" visible="true" />
+        </widget>
+        <widget name="ItemMenuSecondBar" x="30" y="29" width="310" height="24" visible="true">
+            <prototype name="Window" />
+            <text_area name="ItemText" x="16" y="6" text_name="Placeholder" font="FFVIIFont" visible="true" />
+        </widget>
+        <widget name="ItemMenuCharacters" x="30" y="53" width="155" height="182" visible="true">
+            <prototype name="Window" />
+            <widget name="Character1" x="0" y="0" width="100%" height="55" visible="true">
+                <sprite name="Portrait" image="screens/images/cloud.png" x="10" y="12" width="48" height="48" visible="true" />
+                <widget name="Data" x="66" y="14" width="100%" height="100%" visible="true">
+                    <prototype name="MenuCharacterData" />
+                </widget>
+            </widget>
+            <widget name="Character2" x="0" y="55" width="100%" height="55" visible="true">
+                <sprite name="Portrait" image="screens/images/cloud.png" x="10" y="12" width="48" height="48" visible="true" />
+                <widget name="Data" x="66" y="14" width="100%" height="100%" visible="true">
+                    <prototype name="MenuCharacterData" />
+                </widget>
+            </widget>
+            <widget name="Character3" x="0" y="110" width="100%" height="55" visible="true">
+                <sprite name="Portrait" image="screens/images/cloud.png" x="10" y="12" width="48" height="48" visible="true" />
+                <widget name="Data" x="66" y="14" width="100%" height="100%" visible="true">
+                    <prototype name="MenuCharacterData" />
+                </widget>
+            </widget>
+        </widget>
+        <widget name="ItemList" x="185" y="53" width="155" height="182" visible="true">
+            <prototype name="Window" />
+
+            <sprite name="Icon0" image="screens/images/item_item.png" x="8" y="5" width="13" height="15" visible="true" />
+            <text_area name="Name0" x="30" y="6" text_name="Placeholder" font="FFVIIFont" visible="true" />
+            <text_area name="Colon0" x="130" y="6" text_name="Colon" font="FFVIIFont" visible="true" />
+            <text_area name="Qty0" x="135" y="6" text_name="Placeholder" font="FFVIIFont" visible="true" />
+
+            <sprite name="Icon1" image="screens/images/item_item.png" x="8" y="22" width="13" height="15" visible="true" />
+            <text_area name="Name1" x="30" y="23" text_name="Placeholder" font="FFVIIFont" visible="true" />
+            <text_area name="Colon1" x="130" y="23" text_name="Colon" font="FFVIIFont" visible="true" />
+            <text_area name="Qty1" x="135" y="23" text_name="Placeholder" font="FFVIIFont" visible="true" />
+
+            <sprite name="Icon2" image="screens/images/item_item.png" x="8" y="39" width="13" height="15" visible="true" />
+            <text_area name="Name2" x="30" y="40" text_name="Placeholder" font="FFVIIFont" visible="true" />
+            <text_area name="Colon2" x="130" y="40" text_name="Colon" font="FFVIIFont" visible="true" />
+            <text_area name="Qty2" x="135" y="40" text_name="Placeholder" font="FFVIIFont" visible="true" />
+
+            <sprite name="Icon3" image="screens/images/item_item.png" x="8" y="56" width="13" height="15" visible="true" />
+            <text_area name="Name3" x="30" y="57" text_name="Placeholder" font="FFVIIFont" visible="true" />
+            <text_area name="Colon3" x="130" y="57" text_name="Colon" font="FFVIIFont" visible="true" />
+            <text_area name="Qty3" x="135" y="57" text_name="Placeholder" font="FFVIIFont" visible="true" />
+
+            <sprite name="Icon4" image="screens/images/item_item.png" x="8" y="73" width="13" height="15" visible="true" />
+            <text_area name="Name4" x="30" y="78" text_name="Placeholder" font="FFVIIFont" visible="true" />
+            <text_area name="Colon4" x="130" y="78" text_name="Colon" font="FFVIIFont" visible="true" />
+            <text_area name="Qty4" x="135" y="78" text_name="Placeholder" font="FFVIIFont" visible="true" />
+
+            <sprite name="Icon5" image="screens/images/item_item.png" x="8" y="90" width="13" height="15" visible="true" />
+            <text_area name="Name5" x="30" y="91" text_name="Placeholder" font="FFVIIFont" visible="true" />
+            <text_area name="Colon5" x="130" y="91" text_name="Colon" font="FFVIIFont" visible="true" />
+            <text_area name="Qty5" x="135" y="91" text_name="Placeholder" font="FFVIIFont" visible="true" />
+
+            <sprite name="Icon6" image="screens/images/item_item.png" x="8" y="107" width="13" height="15" visible="true" />
+            <text_area name="Name6" x="30" y="108" text_name="Placeholder" font="FFVIIFont" visible="true" />
+            <text_area name="Colon6" x="130" y="108" text_name="Colon" font="FFVIIFont" visible="true" />
+            <text_area name="Qty6" x="135" y="108" text_name="Placeholder" font="FFVIIFont" visible="true" />
+
+            <sprite name="Icon7" image="screens/images/item_item.png" x="8" y="124" width="13" height="15" visible="true" />
+            <text_area name="Name7" x="30" y="125" text_name="Placeholder" font="FFVIIFont" visible="true" />
+            <text_area name="Colon7" x="130" y="125" text_name="Colon" font="FFVIIFont" visible="true" />
+            <text_area name="Qty7" x="135" y="125" text_name="Placeholder" font="FFVIIFont" visible="true" />
+
+            <sprite name="Icon8" image="screens/images/item_item.png" x="8" y="141" width="13" height="15" visible="true" />
+            <text_area name="Name8" x="30" y="142" text_name="Placeholder" font="FFVIIFont" visible="true" />
+            <text_area name="Colon8" x="130" y="142" text_name="Colon" font="FFVIIFont" visible="true" />
+            <text_area name="Qty8" x="135" y="142" text_name="Placeholder" font="FFVIIFont" visible="true" />
+
+            <sprite name="Icon9" image="screens/images/item_item.png" x="8" y="158" width="13" height="15" visible="true" />
+            <text_area name="Name9" x="30" y="159" text_name="Placeholder" font="FFVIIFont" visible="true" />
+            <text_area name="Colon9" x="130" y="159" text_name="Colon" font="FFVIIFont" visible="true" />
+            <text_area name="Qty9" x="135" y="159" text_name="Placeholder" font="FFVIIFont" visible="true" />
+
+            <sprite name="Cursor" image="screens/images/cursor.png" x="-11" y="6" width="24" height="17" visible="true">
+                <animation name="Position1" length="0" y="0:6" />
+                <animation name="Position2" length="0" y="0:23" />
+                <animation name="Position3" length="0" y="0:40" />
+                <animation name="Position4" length="0" y="0:57" />
+                <animation name="Position5" length="0" y="0:78" />
+                <animation name="Position6" length="0" y="0:91" />
+                <animation name="Position7" length="0" y="0:108" />
+                <animation name="Position8" length="0" y="0:125" />
+                <animation name="Position9" length="0" y="0:142" />
+                <animation name="Position10" length="0" y="0:159" />
+            </sprite>
+
+
+        </widget>
+        <!--<widget name="Characters" x="0" y="17" width="300" height="190" visible="true">
+            <prototype name="Window" />
+            <animation name="Appear" length="0.3" x="0:736,0.3:0" />
+            <animation name="Disappear" length="0.3" x="0:0,0.3:736" />
+
+            <widget name="Character1" x="0" y="0" width="100%" height="60" visible="true">
+                <sprite name="Portrait" image="screens/images/cloud.png" x="22" y="12" width="48" height="48" visible="true" />
+                <widget name="Data" x="102" y="14" width="100%" height="100%" visible="true">
+                    <prototype name="MenuCharacterData" />
+                </widget>
+                <text_area name="NextLevelText" x="186" y="17" text_name="CharacterDataNextLevel" font="FFVIIFont" visible="true" />
+                <sprite name="NextLevelBar" image="screens/images/menu_bar.png" x="202" y="28" width="64" height="8" visible="true" />
+                <sprite name="NextLevelLinePart1" x="203" y="29" width="20" height="3" colours="0.5 0.5 0.5, 0.5 0.5 0.5, 0.5 0.125 0.125, 0.5 0.125 0.125" alpha="0.8" visible="false" />
+                <sprite name="NextLevelLinePart2" x="203" y="32" width="20" height="3" colours="0.5 0.125 0.125, 0.5 0.125 0.125, 0 0 0, 0 0 0" alpha="0.8" visible="false" />
+                <text_area name="LimitLevelText" x="186" y="38" text_name="CharacterDataLimitLevel" font="FFVIIFont" visible="true" />
+                <sprite name="LimitLevelBar" image="screens/images/menu_bar.png" x="202" y="49" width="64" height="8" visible="true" />
+                <text_area name="LimitLevel" x="250" y="40" text_name="CharacterDataLimitLevelNumber" font="FFVIIMenuDigits" visible="true" />
+                <sprite name="LimitLinePart1" x="203" y="50" width="25" height="3" colours="0.5 0.5 0.5, 0.5 0.5 0.5, 0.5 0.125 0.31, 0.5 0.125 0.31" alpha="0.8" visible="false" />
+                <sprite name="LimitLinePart2" x="203" y="53" width="25" height="3" colours="0.5 0.125 0.31, 0.5 0.125 0.31, 0 0 0, 0 0 0" alpha="0.8" visible="false" />
+            </widget>
+            <widget name="Character2" x="0" y="65" width="100%" height="60" visible="true">
+                <sprite name="Portrait" image="screens/images/cloud.png" x="22" y="12" width="48" height="48" visible="true" />
+                <widget name="Data" x="102" y="14" width="100%" height="100%" visible="true">
+                    <prototype name="MenuCharacterData" />
+                </widget>
+                <text_area name="NextLevelText" x="186" y="17" text_name="CharacterDataNextLevel" font="FFVIIFont" visible="true" />
+                <sprite name="NextLevelBar" image="screens/images/menu_bar.png" x="202" y="28" width="64" height="8" visible="true" />
+                <sprite name="NextLevelLinePart1" x="203" y="29" width="20" height="3" colours="0.5 0.5 0.5, 0.5 0.5 0.5, 0.5 0.125 0.125, 0.5 0.125 0.125" alpha="0.8" visible="false" />
+                <sprite name="NextLevelLinePart2" x="203" y="32" width="20" height="3" colours="0.5 0.125 0.125, 0.5 0.125 0.125, 0 0 0, 0 0 0" alpha="0.8" visible="false" />
+                <text_area name="LimitLevelText" x="186" y="38" text_name="CharacterDataLimitLevel" font="FFVIIFont" visible="true" />
+                <sprite name="LimitLevelBar" image="screens/images/menu_bar.png" x="202" y="49" width="64" height="8" visible="true" />
+                <text_area name="LimitLevel" x="250" y="40" text_name="CharacterDataLimitLevelNumber" font="FFVIIMenuDigits" visible="true" />
+                <sprite name="LimitLinePart1" x="203" y="50" width="25" height="3" colours="0.5 0.5 0.5, 0.5 0.5 0.5, 0.5 0.125 0.31, 0.5 0.125 0.31" alpha="0.8" visible="false" />
+                <sprite name="LimitLinePart2" x="203" y="53" width="25" height="3" colours="0.5 0.125 0.31, 0.5 0.125 0.31, 0 0 0, 0 0 0" alpha="0.8" visible="false" />
+            </widget>
+            <widget name="Character3" x="0" y="130" width="100%" height="60" visible="true">
+                <sprite name="Portrait" image="screens/images/cloud.png" x="22" y="12" width="48" height="48" visible="true" />
+                <widget name="Data" x="102" y="14" width="100%" height="100%" visible="true">
+                    <prototype name="MenuCharacterData" />
+                </widget>
+                <text_area name="NextLevelText" x="186" y="17" text_name="CharacterDataNextLevel" font="FFVIIFont" visible="true" />
+                <sprite name="NextLevelBar" image="screens/images/menu_bar.png" x="202" y="28" width="64" height="8" visible="true" />
+                <sprite name="NextLevelLinePart1" x="203" y="29" width="20" height="3" colours="0.5 0.5 0.5, 0.5 0.5 0.5, 0.5 0.125 0.125, 0.5 0.125 0.125" alpha="0.8" visible="false" />
+                <sprite name="NextLevelLinePart2" x="203" y="32" width="20" height="3" colours="0.5 0.125 0.125, 0.5 0.125 0.125, 0 0 0, 0 0 0" alpha="0.8" visible="false" />
+                <text_area name="LimitLevelText" x="186" y="38" text_name="CharacterDataLimitLevel" font="FFVIIFont" visible="true" />
+                <sprite name="LimitLevelBar" image="screens/images/menu_bar.png" x="202" y="49" width="64" height="8" visible="true" />
+                <text_area name="LimitLevel" x="250" y="40" text_name="CharacterDataLimitLevelNumber" font="FFVIIMenuDigits" visible="true" />
+                <sprite name="LimitLinePart1" x="203" y="50" width="25" height="3" colours="0.5 0.5 0.5, 0.5 0.5 0.5, 0.5 0.125 0.31, 0.5 0.125 0.31" alpha="0.8" visible="false" />
+                <sprite name="LimitLinePart2" x="203" y="53" width="25" height="3" colours="0.5 0.125 0.31, 0.5 0.125 0.31, 0 0 0, 0 0 0" alpha="0.8" visible="false" />
+            </widget>
+        </widget>
+
+        <widget name="Menu" x="278" y="5" width="86" height="132" visible="true">
+            <prototype name="Window" />
+            <text_area name="ItemText" x="16" y="6" text_name="MainMenuItem" font="FFVIIFont" visible="true" />
+            <text_area name="MagicText" x="16" y="18" text_name="MainMenuMagic" font="FFVIIFont" visible="true" />
+            <text_area name="MateriaText" x="16" y="30" text_name="MainMenuMateria" font="FFVIIFont" visible="true" />
+            <text_area name="EquipText" x="16" y="42" text_name="MainMenuEquip" font="FFVIIFont" visible="true" />
+            <text_area name="StatusText" x="16" y="54" text_name="MainMenuStatus" font="FFVIIFont" visible="true" />
+            <text_area name="OrderText" x="16" y="66" text_name="MainMenuOrder" font="FFVIIFont" visible="true" />
+            <text_area name="LimitText" x="16" y="78" text_name="MainMenuLimit" font="FFVIIFont" visible="true" />
+            <text_area name="ConfigText" x="16" y="90" text_name="MainMenuConfig" font="FFVIIFont" visible="true" />
+            <text_area name="PHSText" x="16" y="102" text_name="MainMenuPHS" font="FFVIIFont" visible="true" />
+            <text_area name="SaveText" x="16" y="114" text_name="MainMenuSave" font="FFVIIFont" visible="true" />
+            <sprite name="Cursor" image="screens/images/cursor.png" x="-11" y="6" width="24" height="17" visible="true">
+                <animation name="Position1" length="0" y="0:6" />
+                <animation name="Position2" length="0" y="0:18" />
+                <animation name="Position3" length="0" y="0:30" />
+                <animation name="Position4" length="0" y="0:42" />
+                <animation name="Position5" length="0" y="0:54" />
+                <animation name="Position6" length="0" y="0:66" />
+                <animation name="Position7" length="0" y="0:78" />
+                <animation name="Position8" length="0" y="0:90" />
+                <animation name="Position9" length="0" y="0:102" />
+                <animation name="Position10" length="0" y="0:114" />
+            </sprite>
+            <animation name="Appear" length="0.3" y="0:405,0.3:5" />
+            <animation name="Disappear" length="0.3" y="0:5,0.3:405" />
+        </widget>
+
+        <widget name="TimeGil" x="278" y="164" width="86" height="36" visible="true">
+            <prototype name="Window" />
+            <text_area name="TimeText" x="4" y="6" text_name="MainMenuTime" font="FFVIIFont" visible="true" />
+            <text_area name="TimeHours" x="31" y="8" text_name="MainMenuHours" font="FFVIIMenuDigits" visible="true" />
+            <text_area name="TimeSemicolon1" x="44" y="1" text_name="MainMenuSemicolon" font="FFVIIMenuFont" visible="true" />
+            <text_area name="TimeMinutes" x="50" y="8" text_name="MainMenuMinutes" font="FFVIIMenuDigits" visible="true" />
+            <text_area name="TimeSemicolon2" x="62" y="1" text_name="MainMenuSemicolon" font="FFVIIMenuFont" visible="true" />
+            <text_area name="TimeSeconds" x="67" y="8" text_name="MainMenuSeconds" font="FFVIIMenuDigits" visible="true" />
+            <text_area name="GilText" x="5" y="20" text_name="MainMenuGil" font="FFVIIFont" visible="true" />
+            <text_area name="GilText" x="11" y="22" text_name="MainMenuGilAmount" font="FFVIIMenuDigits" visible="true" />
+            <animation name="Appear" length="0.3" x="0:-458,0.3:278" />
+            <animation name="Disappear" length="0.3" x="0:278,0.3:-458" />
+        </widget>
+
+        <widget name="Location" x="184" y="200" width="180" height="24" visible="true">
+            <prototype name="Window" />
+            <text_area name="Text" x="8" y="6" text_name="MainMenuLocation" font="FFVIIFont" visible="true" />
+            <animation name="Appear" length="0.3" y="0:-136,0.3:200" />
+            <animation name="Disappear" length="0.3" y="0:200,0.3:-136" />
+        </widget>-->
+    </widget>
+</screen>

+ 22 - 0
data/data/screens/item_menu/prototypes.xml

@@ -0,0 +1,22 @@
+<prototypes>
+    <prototype name="MenuCharacterData">
+        <text_area name="Name" x="0" y="0" text_name="CharacterDataName" font="FFVIIFont" visible="true" />
+        <text_area name="Lv" x="0" y="5" text_name="CharacterDataLv" colour="0 0.9 0.9" font="FFVIIMenuFont" visible="true" />
+        <text_area name="LvNumber" x="18" y="13" text_name="CharacterDataLvNumber" font="FFVIIMenuDigits" visible="true" />
+        <text_area name="Hp" x="0" y="16" text_name="CharacterDataHp" colour="0 0.9 0.9" font="FFVIIMenuFont" visible="true" />
+        <text_area name="HpCurrent" x="18" y="23" text_name="CharacterDataHpCurrent" colour="0.9 0.9 0.9" font="FFVIIMenuDigits" visible="true" />
+        <sprite name="HpSlash" image="screens/images/slash.png" x="46" y="23" width="4" height="8" visible="true" />
+        <text_area name="HpMax" x="50" y="23" text_name="CharacterDataHpMax" font="FFVIIMenuDigits" visible="true" />
+        <sprite name="HpLine" x="18" y="31" width="60" height="1" colour="0.3 0 0" visible="true" />
+        <sprite name="HpLineCurrent" x="18" y="31" width="5" height="1" colours="0 0.5 1, 0.78 0.78 0.78, 0.78 0.78 0.78, 0 0.5 1" visible="true" />
+        <sprite name="HpLineShadow" x="18" y="32" width="60" height="1" colour="0 0 0" visible="true" />
+        <text_area name="Mp" x="0" y="26" text_name="CharacterDataMp" colour="0 0.9 0.9" font="FFVIIMenuFont" visible="true" />
+        <text_area name="MpCurrent" x="18" y="33" text_name="CharacterDataMpCurrent" font="FFVIIMenuDigits" visible="true" />
+        <sprite name="MpSlash" image="screens/images/slash.png" x="46" y="33" width="4" height="8" visible="true" />
+        <text_area name="MpMax" x="50" y="33" text_name="CharacterDataMpMax" font="FFVIIMenuDigits" visible="true" />
+        <sprite name="MpLine" x="18" y="41" width="60" height="1" colour="0.3 0 0" visible="true" />
+        <sprite name="MpLineCurrent" x="18" y="41" width="60" height="1" colours="0 1 0.5, 0.78 0.78 0.78, 0.78 0.78 0.78, 0 1 0.5" visible="true" />
+        <sprite name="MpLineShadow" x="18" y="42" width="60" height="1" colour="0 0 0" visible="true" />
+        <text_area name="Status" x="36" y="11" text_name="CharacterDataFury" colour="0.9 0 0.9" font="FFVIIFont" visible="true" />
+    </prototype>
+</prototypes>

+ 30 - 21
data/data/screens/main_menu/main_menu.lua

@@ -57,32 +57,38 @@ UiContainer.MainMenu = {
 
 
     on_button = function(self, button, event)
-        if ui_manager:get_widget("MainMenu"):is_visible() == true then
-            local characters  = ui_manager:get_widget("MainMenu.Container.Characters")
-            local menu        = ui_manager:get_widget("MainMenu.Container.Menu")
-            local menu_cursor = ui_manager:get_widget("MainMenu.Container.Menu.Cursor")
-            local timegil     = ui_manager:get_widget("MainMenu.Container.TimeGil")
-            local location    = ui_manager:get_widget("MainMenu.Container.Location")
 
-            if button == "Escape" and event == "Press" then
-                script:request_end_sync(Script.UI, "MainMenu", "hide", 0)
-            elseif button == "Down" then
-                self.position = self.position + 1
-                if self.position > self.position_total then
-                    self.position = 1;
-                end
-                menu_cursor:set_default_animation("Position" .. self.position)
-            elseif button == "Up" then
-                self.position = self.position - 1
-                if self.position <= 0 then
-                    self.position = self.position_total;
+        local characters  = ui_manager:get_widget("MainMenu.Container.Characters")
+        local menu  = ui_manager:get_widget("MainMenu.Container.Menu")
+        local menu_cursor = ui_manager:get_widget("MainMenu.Container.Menu.Cursor")
+        local timegil = ui_manager:get_widget("MainMenu.Container.TimeGil")
+        local location = ui_manager:get_widget("MainMenu.Container.Location")
+        if UiContainer.current_menu == "main" then
+            if UiContainer.current_submenu == "" then
+                if button == "Escape" and event == "Press" then
+                    script:request_end_sync(Script.UI, "MainMenu", "hide", 0)
+                elseif button == "Enter" and event == "Press" and self.position == 1 then
+                    script:request_end_sync(Script.UI, "ItemMenu", "show", 0)
+                elseif button == "Down" then
+                    self.position = self.position + 1
+                    if self.position > self.position_total then
+                        self.position = 1;
+                    end
+                    menu_cursor:set_default_animation("Position" .. self.position)
+                elseif button == "Up" then
+                    self.position = self.position - 1
+                    if self.position <= 0 then
+                        self.position = self.position_total;
+                    end
+                    menu_cursor:set_default_animation("Position" .. self.position)
                 end
-                menu_cursor:set_default_animation("Position" .. self.position)
             end
-        elseif FFVII.MenuSettings.available == true then
+        elseif ui_manager:get_widget("MainMenu"):is_visible() == false and FFVII.MenuSettings.available == true then
             if button == "Escape" and event == "Press" then
                 script:request_end_sync(Script.UI, "MainMenu", "show", 0)
             end
+        else
+            return 0
         end
 
         return 0
@@ -92,9 +98,10 @@ UiContainer.MainMenu = {
     --
     -- Populates and updates displayed data before opening.
     show = function(self)
+        UiContainer.current_menu = "main"
+        UiContainer.current_submenu = ""
         entity_manager:set_paused(true)
         FFVII.MenuSettings.pause_available = false
-
         ui_manager:get_widget("MainMenu"):set_visible(true)
         local characters  = ui_manager:get_widget("MainMenu.Container.Characters")
         local menu = ui_manager:get_widget("MainMenu.Container.Menu")
@@ -125,6 +132,8 @@ UiContainer.MainMenu = {
 
 
     hide = function(self)
+        UiContainer.current_menu = ""
+        UiContainer.current_submenu = ""
         local characters  = ui_manager:get_widget("MainMenu.Container.Characters")
         local menu = ui_manager:get_widget("MainMenu.Container.Menu")
         local timegil = ui_manager:get_widget("MainMenu.Container.TimeGil")

+ 29 - 16
data/data/scripts/data.lua

@@ -82,6 +82,20 @@ for i = 1, 11 do
     Characters[i] = {}
 end
 
+--- The party inventory.
+--
+-- Items can be accessed with indexes 0 - MAX_INVENTORY_SLOTS. Money can be accessed with the
+-- index "money." It has the following functions:
+--   add_item(item, quantity)
+--   remove_item(item, quantity)
+--   get_item_quantity(item)
+--   add_money(amount)
+--   remove_money(amount)
+--   money_to_2_banks(b1, b2, a1, a2)
+Inventory = {}
+Inventory.money = 0
+
+
 
 FFVII = {}
 
@@ -156,29 +170,28 @@ FFVII.Party = {
 
 --- Memory banks.
 --
--- @todo: Give propper indices starting at 0.
--- @todo: Change to Banks, don't include it in FFVII, which is to be deleted.
-FFVII.Banks = {}
-for i = 1, 15 do
-    FFVII.Banks[i] = {}
-    for j = 1, 256 do
-        FFVII.Banks[i][j] = 0
+-- Used to store gama data.
+-- 15 banks (indexes 0-14) with 255 addresses (indexes 0-254) each. Memory banks 4, 5, 7, 8 and 9
+-- are temporary and are not saved to the savemap.
+Banks = {}
+for i = 0, 14 do
+    Banks[i] = {}
+    for j = 0, 255 do
+        Banks[i][j] = 0
     end
 end
 
 
 --- Memory banks for individual bits.
 --
--- @todo: Give propper indices starting at 0.
--- @todo: Change to Banks, don't include it in FFVII, which is to be deleted.
 -- @todo Delete this, and implement bit operations over Banks.
-FFVII.BitBanks = {}
-for i = 1, 16 do
-    FFVII.BitBanks[i] = {}
-    for j = 1, 256 do
-        FFVII.BitBanks[i][j] = {}
-        for k = 1, 8 do
-            FFVII.BitBanks[i][j][k] = 0
+BitBanks = {}
+for i = 0, 15 do
+    BitBanks[i] = {}
+    for j = 0, 255 do
+        BitBanks[i][j] = {}
+        for k = 0, 7 do
+            BitBanks[i][j][k] = 0
         end
     end
 end

+ 7 - 7
data/data/scripts/field.lua

@@ -126,7 +126,7 @@ end
 -- @param variable Bank address (0-index, as in game).
 -- @param bit Address bit (0-index, as in game).
 bit_on = function(bank, variable, bit)
-    FFVII.BitBanks[bank + 1][variable + 1][bit + 1] = 1
+    BitBanks[bank][variable][bit] = 1
 end
 
 --- Sets a bit variable off (0).
@@ -135,7 +135,7 @@ end
 -- @param variable Bank address (0-index, as in game).
 -- @param bit Address bit (0-index, as in game).
 bit_off = function(bank, variable, bit)
-    FFVII.BitBanks[bank + 1][variable + 1][bit + 1] = 0
+    BitBanks[bank][variable][bit] = 0
 end
 
 --- Retrieves a bit variable.
@@ -145,7 +145,7 @@ end
 -- @param bit Address bit (0-index, as in game).
 -- @return 1 or 0
 bit = function(bank, variable, bit)
-    return FFVII.BitBanks[bank + 1][variable + 1][bit + 1]
+    return BitBanks[bank][variable][bit]
 end
 
 --- Stores the coordinates and walkmesh triangle in memory.
@@ -168,10 +168,10 @@ axyzi = function(bx, by, bz, bt, id, ax, ay, az, at, scale)
     end
     local x, y, z = entity:get_position()
     local t = entity:get_move_triangle_id()
-    FFVII.Banks[bx + 1][ax + 1] = math.floor(x * scale)
-    FFVII.Banks[by + 1][ay + 1] = math.floor(y * scale)
-    FFVII.Banks[bz + 1][az + 1] = math.floor(z * scale)
-    FFVII.Banks[bt + 1][at + 1] = t
+    Banks[bx][ax] = math.floor(x * scale)
+    Banks[by][ay] = math.floor(y * scale)
+    Banks[bz][az] = math.floor(z * scale)
+    Banks[bt][at] = t
     return 0
 end
 

+ 54 - 26
data/data/scripts/system.lua

@@ -1,3 +1,5 @@
+MAX_INVENTORY_SLOTS = 320
+
 --- Export character names to the text manager.
 --
 -- Used to compose dialog with character names. Must be called on start and when a character is
@@ -166,26 +168,46 @@ end
 --
 -- @param item Item ID.
 -- @param quantity Quantity to add.
-FFVII.add_item = function(item, quantity)
-    if FFVII.Items[item] == nil then
+Inventory.add_item = function(item, quantity)
+    -- TODO: Also check weapons, armors and accessories.
+    if Game.Items[item] == nil then
         print("Tried add invalid item \"" .. item .. "\".");
         return
     end
     local old_quantity = 0
-    if FFVII.ItemStorage[item] ~= nil then
-        old_quantity = FFVII.ItemStorage[item]
+    local index = -1
+    for i = 0, MAX_INVENTORY_SLOTS do
+        if Inventory[i] ~= nil and Inventory[i].item == item then
+            -- Item already in inventory, add quantity
+            print("Add item " .. item .. " (" .. quantity .. "..) at a existing position " .. i)
+            Inventory[i].quantity = Inventory[i].quantity + quantity
+            if Inventory[i].quantity > 99 then
+                Inventory[i].quantity = 99
+            end
+            return
+        end
     end
-    FFVII.ItemStorage[item] = old_quantity + quantity
-    if FFVII.ItemStorage[item] > 99 then
-        FFVII.ItemStorage[item] = 99
+    -- Item not in inventory, search for the first available position.
+    for i = 0, MAX_INVENTORY_SLOTS do
+        if Inventory[i] == nil then
+            print("Add item " .. item .. " (" .. quantity .. "..) at a new position " .. i)
+            Inventory[i] = {item = item, quantity = quantity}
+            if Inventory[i].quantity > 99 then
+                Inventory[i].quantity = 99
+            end
+            return
+        end
     end
+    -- If reached this point, it means that the item is not in inventory, and all slots are full.
+    -- TODO: The orignal game does nothing, but in here?
+    return
 end
 
 --- Checks how many items of a type are in the inventory.
 --
 -- @param item The item ID.
 -- @return Number of the items in the inventory.
-FFVII.get_item_quantity = function(item)
+Inventory.get_item_quantity = function(item)
     if FFVII.Items[item] == nil then
         print("Tried get quontity for invalid item \"" .. item .. "\".");
         return
@@ -204,7 +226,7 @@ end
 --
 -- @param item Item ID.
 -- @param quantity Quantity to remove.
-FFVII.remove_item = function(item, quantity)
+Inventory.remove_item = function(item, quantity)
     if FFVII.Items[item] == nil then
         print("Tried remove invalid item \"" .. item .. "\".");
         return
@@ -222,31 +244,37 @@ end
 --- Adds money.
 --
 -- @param amount The money to add.
--- @todo Fix cap.
-FFVII.add_money = function(amount)
-    FFVII.Savemap.money = FFVII.Savemap.money + amount
-
-    if FFVII.Savemap.money > 999999 then
-        FFVII.ItemStorage[item] = 999999
+Inventory.add_money = function(amount)
+    Inventory.money = Inventory.money + amount
+    if Inventory.money > 99999999 then
+        Inventory.money = 99999999
     end
 end
 
---- Checks the curent money.
---
--- @return Current money.
-FFVII.get_money = function()
-    return FFVII.Savemap.money
-end
-
 --- Removes money.
 --
 -- Negative quantities can't remain as money. If there are less than requested to remove, all of
 -- it will be removed.
 --
 -- @param amount Money to remove.
-FFVII.remove_money = function(amount)
-    FFVII.Savemap.money = FFVII.Savemap.money - amount
-    if FFVII.Savemap.money < 0 then
-        FFVII.Savemap.money = 0
+Inventory.remove_money = function(amount)
+    Inventory.money = Inventory.money - amount
+    if Inventory.money < 0 then
+        Inventory.money = 0
     end
 end
+
+--- Copies the amount of gil the party has into data banks
+--
+-- As the gil amount is a four-byte value, in the original game the arguments require two
+-- destination addresses to place two two-byte values into. Address 1 takes the lower two bytes of
+-- the gil amount, while address 2 takes the higher two bytes.
+--
+-- @param b1 First destination bank.
+-- @param b2 Second destination address.
+-- @param a1 First destination bank.
+-- @param a2 Second destination address.
+Inventory.money_to_2_banks = function(b1, b2, a1, a2)
+    Banks[b1][a1] = math.floor(Inventory.money / 65535)
+    Banks[b2][a2] = math.floor(Inventory.money % 65535) - math.floor(Inventory.money / 65535)
+end