Selaa lähdekoodia

Item menu fully implemented.

Iñigo Valentin 3 vuotta sitten
vanhempi
sitoutus
e08cf7b3da

+ 14 - 7
V-Gears-Installer/src/KernelDataInstaller.cpp

@@ -779,7 +779,8 @@ void KernelDataInstaller::WriteItems(std::string file_name){
     if (!file) return;
     for (ItemData item : items_){
         file << "Game.Items[" << item.id << "] = {\n"
-          << "    inventory_id = " << item.id << ",\n"
+          << "    type = Inventory.ITEM_TYPE.ITEM,\n"
+          << "    id = " << item.id << ",\n"
           << "    price = " << prices_[item.id] << ",\n"
           << "    name = \"" << item.name << "\",\n"
           << "    description = \"" << item.description << "\",\n"
@@ -787,6 +788,7 @@ void KernelDataInstaller::WriteItems(std::string file_name){
           << "    sell = " << item.sellable << ",\n"
           << "    battle = " << item.useable_battle << ",\n"
           << "    menu = " << item.useable_menu << ",\n"
+          << "    throw = 0,\n"
           << "    effect = " << static_cast<int>(item.effect) << ",\n"
           << "    dmg_formula = " << static_cast<int>(item.damage_formula) << ",\n"
           << "    dmg_modifier = " << static_cast<int>(item.damage_modifier) << ",\n"
@@ -998,8 +1000,9 @@ void KernelDataInstaller::WriteWeapons(std::string file_name){
     file.open(file_name, std::ios::out);
     if (!file) return;
     for (WeaponData weapon : weapons_){
-        file << "Game.Weapons[" << weapon.id << "] = {\n"
-          << "    inventory_id = " << weapon.id + 128 << ",\n"
+        file << "Game.Items[" << weapon.id + 128 << "] = {\n"
+          << "    type = Inventory.ITEM_TYPE.WEAPON,\n"
+          << "    id = " << weapon.id + 128 << ",\n"
           << "    price = " << prices_[weapon.id + 128] << ",\n"
           << "    name = \"" << weapon.name << "\",\n"
           << "    description = \"" << weapon.description << "\",\n"
@@ -1201,14 +1204,16 @@ void KernelDataInstaller::WriteArmors(std::string file_name){
     file.open(file_name, std::ios::out);
     if (!file) return;
     for (ArmorData armor : armors_){
-        file << "Game.Armors[" << armor.id << "] = {\n"
-          << "    inventory_id = " << armor.id + 256 << ",\n"
+        file << "Game.Items[" << armor.id + 256 << "] = {\n"
+          << "    type = Inventory.ITEM_TYPE.ARMOR,\n"
+          << "    id = " << armor.id + 256 << ",\n"
           << "    price = " << prices_[armor.id + 256] << ",\n"
           << "    name = \"" << armor.name << "\",\n"
           << "    description = \"" << armor.description << "\",\n"
           << "    sell = " << armor.sellable << ",\n"
           << "    battle = " << armor.useable_battle << ",\n"
           << "    menu = " << armor.useable_menu << ",\n"
+          << "    throw = 0,\n"
           << "    defense = {physical = " << static_cast<int>(armor.defense) << ", magical = "
           << static_cast<int>(armor.m_defense) << "},\n"
           << "    evasion = {physical = " << static_cast<int>(armor.evasion) << ", magical = "
@@ -1409,14 +1414,16 @@ void KernelDataInstaller::WriteAccessories(std::string file_name){
     file.open(file_name, std::ios::out);
     if (!file) return;
     for (AccessoryData accessory : accessories_){
-        file << "Game.Accessories[" << accessory.id << "] = {\n"
-          << "    inventory_id = " << accessory.id + 287 << ",\n"
+        file << "Game.Items[" << accessory.id + 287 << "] = {\n"
+          << "    type = Inventory.ITEM_TYPE.ACCESSORY,\n"
+          << "    id = " << accessory.id + 287 << ",\n"
           << "    price = " << prices_[accessory.id + 287] << ",\n"
           << "    name = \"" << accessory.name << "\",\n"
           << "    description = \"" << accessory.description << "\",\n"
           << "    sell = " << accessory.sellable << ",\n"
           << "    battle = " << accessory.useable_battle << ",\n"
           << "    menu = " << accessory.useable_menu << ",\n"
+          << "    throw = 0,\n"
           << "    special_effect = {";
         if (accessory.effect != 0XFF) file << static_cast<int>(accessory.effect);
         file

+ 289 - 42
data/data/screens/item_menu/item_menu.lua

@@ -1,26 +1,76 @@
 if UiContainer == nil then UiContainer = {} end
 
---- The main menu.
+--- The item menu.
 UiContainer.ItemMenu = {
+
+    --- Current cursor position in the top bar (1-3).
     bar_position = 1,
+
+    --- Max cursor position in the top bar.
     bar_position_total = 3,
+
+    --- Current cursor position in the item list (1-10).
+    --
+    -- It doesn't represent the item id or the inventory position, but the highlighted row among
+    -- the ten displayed.
     item_cursor_position = 1,
+
+    --- Max cursor position in the item list (1-10).
     item_cursor_position_total = 10,
-    item_cursor_item_selected = 0,
+
+    --- Highlighted inventory position (1-320).
+    item_cursor_item_selected = 1,
+
+    --- Current cursor position in the character section (1-3).
     char_position = 1,
+
+    --- Max cursor position in the character section.
     char_position_total = 3,
+
+    --- First inventory entry currently visible in the item list.
     first_item_in_window = 0,
 
-    -- Item ID of the currently selected (not highlighted) item.
+    --- Key items cursor vertical position (0-9)
+    --
+    -- It doesn't represent the item id or the inventory position, but the highlighted row among
+    -- the ten displayed.
+    key_items_cursor_x_position = 0,
+
+    --- Key items cursor horizontal position (0-1)
+    --
+    -- It doesn't represent the item id or the inventory position, but the highlighted row among
+    -- the ten displayed. 0 if the cursor is in the left column, 1 if it's on the right.
+    key_items_cursor_y_position = 0,
+
+    --- First visible row of key items (0-16)
+    --
+    -- Changes as the key item list scrols vertically.
+    first_key_item_row_in_window = 0,
+
+    --- Current cursor position in the order section (1-8).
+    order_cursor_position = 1,
+
+    --- Max cursor position in the order section.
+    order_cursor_position_total = 8,
+
+    --- Item ID of the currently selected (not highlighted) item.
     item_in_use = nil,
 
-    -- Indicates it the selected item affect the entire party.
+    --- Indicates it the selected item affect the entire party.
     item_in_use_party = false,
 
+    --- Run when the menu is creaded.
+    --
+    -- It does nothing.
     on_start = function(self)
         return 0
     end,
 
+    --- Handles button events.
+    --
+    -- For the current submenu, handles directional keys, enter and escape events.
+    -- @param button Pressed button string key. "Up", "Left", "Enter" and "Escape" are handled.
+    -- @param event Trigger event. Normally, "Press".
     on_button = function(self, button, event)
         if UiContainer.current_menu == "item" then
             if UiContainer.current_submenu == "bar_selection" then
@@ -33,25 +83,73 @@ UiContainer.ItemMenu = {
                         self.bar_position = 1
                     end
                     ui_manager:get_widget("ItemMenu.Container.ItemMenuTopBar.Cursor"):set_default_animation("Position" .. self.bar_position)
+                    -- If highlighted, show key items.
+                    if self.bar_position == 3 then
+                        ui_manager:get_widget("ItemMenu.Container.KeyItems"):set_visible(true)
+                    else
+                        ui_manager:get_widget("ItemMenu.Container.KeyItems"):set_visible(false)
+                    end
                 elseif button == "Left" then
                     self.bar_position = self.bar_position - 1
                     if self.bar_position < 1 then
                         self.bar_position = self.bar_position_total
                     end
                     ui_manager:get_widget("ItemMenu.Container.ItemMenuTopBar.Cursor"):set_default_animation("Position" .. self.bar_position)
+                    -- If highlighted, show key items.
+                    if self.bar_position == 3 then
+                        ui_manager:get_widget("ItemMenu.Container.KeyItems"):set_visible(true)
+                    else
+                        ui_manager:get_widget("ItemMenu.Container.KeyItems"):set_visible(false)
+                    end
                 elseif button == "Enter" then
                     if self.bar_position == 1 then
                         self.submenu_items(self)
                     elseif self.bar_position == 2 then
-                        print("Unimplemented: Order")
+                        self.submenu_order(self)
                     elseif self.bar_position == 3 then
-                        print("Unimplemented: Key Items")
+                        self.submenu_key_items(self)
                     else
                         return 0
                     end
                 else
                     return 0
                 end
+            elseif UiContainer.current_submenu == "order_selection" then
+                if button == "Escape" and event == "Press" then
+                    self.submenu_bar(self)
+                elseif button == "Down" then
+                    self.order_cursor_position = self.order_cursor_position + 1
+                    if self.order_cursor_position > self.order_cursor_position_total then
+                        self.order_cursor_position = 1
+                    end
+                    ui_manager:get_widget("ItemMenu.Container.ItemOrder.Cursor"):set_default_animation("Position" .. self.order_cursor_position)
+                elseif button == "Up" then
+                    self.order_cursor_position = self.order_cursor_position - 1
+                    if self.order_cursor_position <= 0 then
+                        self.order_cursor_position = self.order_cursor_position_total
+                    end
+                    ui_manager:get_widget("ItemMenu.Container.ItemOrder.Cursor"):set_default_animation("Position" .. self.order_cursor_position)
+                elseif button == "Enter" then
+                    if self.order_cursor_position == 1 then
+                        Inventory.sort(Inventory.ORDER.CUSTOM)
+                    elseif self.order_cursor_position == 2 then
+                        Inventory.sort(Inventory.ORDER.FIELD)
+                    elseif self.order_cursor_position == 3 then
+                        Inventory.sort(Inventory.ORDER.BATTLE)
+                    elseif self.order_cursor_position == 4 then
+                        Inventory.sort(Inventory.ORDER.THROW)
+                    elseif self.order_cursor_position == 5 then
+                        Inventory.sort(Inventory.ORDER.TYPE)
+                    elseif self.order_cursor_position == 6 then
+                        Inventory.sort(Inventory.ORDER.NAME)
+                    elseif self.order_cursor_position == 7 then
+                        Inventory.sort(Inventory.ORDER.MOST)
+                    elseif self.order_cursor_position == 8 then
+                        Inventory.sort(Inventory.ORDER.LEAST)
+                    end
+                    self.populate_items(self)
+                    self.submenu_bar(self)
+                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")
@@ -114,9 +212,6 @@ UiContainer.ItemMenu = {
                     else
                         print("BEEP")
                     end
-                    --self.submenu_bar(self)
-                    --UiContainer.current_submenu = "bar_selection"
-                    --ui_manager:get_widget("ItemMenu.Container.ItemList.Cursor"):set_visible(false)
                 end
             elseif UiContainer.current_submenu == "char_selection" then
                 if button == "Down" then
@@ -152,7 +247,109 @@ UiContainer.ItemMenu = {
                         print("USE BEEP")
                     end
                 end
-            elseif UiContainer.current_submenu == "key_items" then
+            elseif UiContainer.current_submenu == "key_item_selection" then
+                if button == "Down" then
+                    if self.key_items_cursor_y_position == 9 and self.first_key_item_row_in_window == 16 then
+                        -- Last key item selected, do nothing
+                        return 0
+                    end
+                    if self.key_items_cursor_y_position == 9 then
+                        -- Scroll down by 1, don't move cursor
+                        self.first_key_item_row_in_window = self.first_key_item_row_in_window + 1
+                        self.populate_key_items(self)
+                    else
+                        -- Move cursor down by 1.
+                        self.key_items_cursor_y_position = self.key_items_cursor_y_position + 1
+                    end
+                    ui_manager:get_widget("ItemMenu.Container.KeyItems.Cursor"):set_default_animation("Position" .. self.key_items_cursor_y_position .. "-" .. self.key_items_cursor_x_position)
+                    -- Show description of the selected item, if any.
+                    local k_selected_id = (self.first_key_item_row_in_window + self.key_items_cursor_y_position) * 2 + self.key_items_cursor_x_position
+                    if (Inventory.key[k_selected_id] == true) then
+                        ui_manager:get_widget("ItemMenu.Container.ItemMenuSecondBar.ItemText"):set_text(Game.Key_Items[k_selected_id].description)
+                    else
+                        ui_manager:get_widget("ItemMenu.Container.ItemMenuSecondBar.ItemText"):set_text("")
+                    end
+                elseif button == "Up" then
+                    if self.key_items_cursor_y_position == 0 and self.first_key_item_row_in_window == 0 then -- TODO: Don't hardcode.
+                        -- First item, do nothing.
+                        return 0
+                    end
+                    if self.key_items_cursor_y_position == 0 then
+                        -- Scroll down by 1, don't move cursor
+                        self.first_key_item_row_in_window = self.first_key_item_row_in_window - 1
+                        self.populate_key_items(self)
+                    else
+                        -- Move cursor up by 1.
+                        self.key_items_cursor_y_position = self.key_items_cursor_y_position - 1
+                    end
+                    ui_manager:get_widget("ItemMenu.Container.KeyItems.Cursor"):set_default_animation("Position" .. self.key_items_cursor_y_position .. "-" .. self.key_items_cursor_x_position)
+                    -- Show description of the selected item, if any.
+                    local k_selected_id = (self.first_key_item_row_in_window + self.key_items_cursor_y_position) * 2 + self.key_items_cursor_x_position
+                    if (Inventory.key[k_selected_id] == true) then
+                        ui_manager:get_widget("ItemMenu.Container.ItemMenuSecondBar.ItemText"):set_text(Game.Key_Items[k_selected_id].description)
+                    else
+                        ui_manager:get_widget("ItemMenu.Container.ItemMenuSecondBar.ItemText"):set_text("")
+                    end
+                elseif button == "Right" then
+                    local next_selected_index = (self.first_key_item_row_in_window + self.key_items_cursor_y_position) * 2 + self.key_items_cursor_x_position + 1
+                    if next_selected_index >= 50 then
+                        -- Already in last item, do nothing
+                        return 0
+                    end
+                    if self.key_items_cursor_x_position == 0 then
+                        -- If on the left, move to the right. No scrolling.
+                        self.key_items_cursor_x_position = 1
+                    else
+                        -- If on the right, move to left and one row down...
+                        self.key_items_cursor_x_position = 0
+                        if self.key_items_cursor_y_position == 9 then
+                            -- ... by scrolling down by 1, not moving cursor.
+                            self.first_key_item_row_in_window = self.first_key_item_row_in_window + 1
+                            self.populate_key_items(self)
+                        else
+                            --  ... by moving the cursor down.
+                            self.key_items_cursor_y_position = self.key_items_cursor_y_position + 1
+                        end
+                    end
+                    ui_manager:get_widget("ItemMenu.Container.KeyItems.Cursor"):set_default_animation("Position" .. self.key_items_cursor_y_position .. "-" .. self.key_items_cursor_x_position)
+                    -- Show description of the selected item, if any.
+                    local k_selected_id = (self.first_key_item_row_in_window + self.key_items_cursor_y_position) * 2 + self.key_items_cursor_x_position
+                    if (Inventory.key[k_selected_id] == true) then
+                        ui_manager:get_widget("ItemMenu.Container.ItemMenuSecondBar.ItemText"):set_text(Game.Key_Items[k_selected_id].description)
+                    else
+                        ui_manager:get_widget("ItemMenu.Container.ItemMenuSecondBar.ItemText"):set_text("")
+                    end
+                elseif button == "Left" then
+                    local next_selected_index = (self.first_key_item_row_in_window + self.key_items_cursor_y_position) * 2 + self.key_items_cursor_x_position - 1
+                    if next_selected_index < 0 then
+                        -- Already in first item, do nothing
+                        return 0
+                    end
+                    if self.key_items_cursor_x_position == 1 then
+                        -- If on the right, move to the left. No scrolling.
+                        self.key_items_cursor_x_position = 0
+                    else
+                        -- If on the left, move to right and one row up...
+                        self.key_items_cursor_x_position = 1
+                        if self.key_items_cursor_y_position == 0 then
+                            -- ... by scrolling up by 1, not moving cursor.
+                            self.first_key_item_row_in_window = self.first_key_item_row_in_window - 1
+                            self.populate_key_items(self)
+                        else
+                            --  ... by moving the cursor up.
+                            self.key_items_cursor_y_position = self.key_items_cursor_y_position - 1
+                        end
+                    end
+                    ui_manager:get_widget("ItemMenu.Container.KeyItems.Cursor"):set_default_animation("Position" .. self.key_items_cursor_y_position .. "-" .. self.key_items_cursor_x_position)
+                    -- Show description of the selected item, if any.
+                    local k_selected_id = (self.first_key_item_row_in_window + self.key_items_cursor_y_position) * 2 + self.key_items_cursor_x_position
+                    if (Inventory.key[k_selected_id] == true) then
+                        ui_manager:get_widget("ItemMenu.Container.ItemMenuSecondBar.ItemText"):set_text(Game.Key_Items[k_selected_id].description)
+                    else
+                        ui_manager:get_widget("ItemMenu.Container.ItemMenuSecondBar.ItemText"):set_text("")
+                    end
+                end
+
             end
         end
         return 0
@@ -164,14 +361,6 @@ UiContainer.ItemMenu = {
     show = function(self)
         ui_manager:get_widget("ItemMenu"):set_visible(true)
         UiContainer.current_menu = "item"
-        Inventory.add_item(0, 2)
-        Inventory.add_item(1, 4)
-        Inventory.add_item(42, 3)
-        Inventory.add_item(1, 4)
-        Inventory.add_item(6, 5)
-        Inventory.add_item(13, 2)
-        Inventory.add_item(14, 4)
-        Characters[0].stats.hp.current = 10
         self.char_position_total = 0
         for c = 1, 3 do
             if FFVII.Party[c] ~= nil then
@@ -182,10 +371,12 @@ UiContainer.ItemMenu = {
             end
         end
         self.populate_items(self)
+        self.populate_key_items(self)
         self.submenu_items(self)
         return 0;
     end,
 
+    --- Initializes the selection of the menu section in the top bar.
     submenu_bar = function(self)
         UiContainer.current_submenu = "bar_selection"
         ui_manager:get_widget("ItemMenu.Container.ItemList.Cursor"):set_visible(false)
@@ -194,16 +385,47 @@ UiContainer.ItemMenu = {
         ui_manager:get_widget("ItemMenu.Container.ItemMenuTopBar.Cursor"):set_visible(true)
         ui_manager:get_widget("ItemMenu.Container.ItemMenuTopBar.Cursor"):set_default_animation("Position" .. self.bar_position)
         ui_manager:get_widget("ItemMenu.Container.ItemMenuSecondBar.ItemText"):set_text("")
+        ui_manager:get_widget("ItemMenu.Container.KeyItems.Cursor"):set_visible(false)
+        ui_manager:get_widget("ItemMenu.Container.ItemOrder.Cursor"):set_visible(false)
+        ui_manager:get_widget("ItemMenu.Container.ItemOrder"):set_visible(false)
+    end,
+
+    --- Initializes the sorting mode window.
+    submenu_order = function(self)
+        UiContainer.current_submenu = "order_selection"
+        self.order_cursor_position = 1
+        ui_manager:get_widget("ItemMenu.Container.ItemList.Cursor"):set_visible(false)
+        ui_manager:get_widget("ItemMenu.Container.ItemMenuCharacters.Cursor"):set_visible(false)
+        ui_manager:get_widget("ItemMenu.Container.ItemMenuCharacters.CursorMultiple"):set_visible(false)
+        ui_manager:get_widget("ItemMenu.Container.ItemMenuTopBar.Cursor"):set_visible(false)
+        ui_manager:get_widget("ItemMenu.Container.ItemMenuSecondBar.ItemText"):set_text("")
+        ui_manager:get_widget("ItemMenu.Container.KeyItems.Cursor"):set_visible(false)
+        ui_manager:get_widget("ItemMenu.Container.ItemOrder"):set_visible(true)
+        ui_manager:get_widget("ItemMenu.Container.ItemOrder.Cursor"):set_visible(true)
+        ui_manager:get_widget("ItemMenu.Container.ItemOrder.Cursor"):set_default_animation("Position" .. self.order_cursor_position)
     end,
 
+    --- Initializes the item selection selection mode.
     submenu_items = function(self)
         UiContainer.current_submenu = "item_selection"
         ui_manager:get_widget("ItemMenu.Container.ItemList.Cursor"):set_visible(true)
         ui_manager:get_widget("ItemMenu.Container.ItemList.Cursor"):set_default_animation("Position" .. self.item_cursor_position)
         ui_manager:get_widget("ItemMenu.Container.ItemMenuCharacters.Cursor"):set_visible(false)
         ui_manager:get_widget("ItemMenu.Container.ItemMenuCharacters.CursorMultiple"):set_visible(false)
+        ui_manager:get_widget("ItemMenu.Container.KeyItems.Cursor"):set_visible(false)
+        ui_manager:get_widget("ItemMenu.Container.ItemOrder.Cursor"):set_visible(false)
         --ui_manager:get_widget("ItemMenu.Container.ItemMenuTopBar.Cursor"):set_visible(false)
         -- Show description of the very first item, if any.
+        -- TODO: Selected, not first
+        -- Show description of the selected item, if any.
+        local k_selected_id = (self.first_key_item_row_in_window + self.key_items_cursor_y_position) * 2 + self.key_items_cursor_x_position
+        if (Inventory.key[k_selected_id] == true) then
+            ui_manager:get_widget("ItemMenu.Container.ItemMenuSecondBar.ItemText"):set_text(Game.Key_Items[k_selected_id].description)
+        else
+            ui_manager:get_widget("ItemMenu.Container.ItemMenuSecondBar.ItemText"):set_text("")
+        end
+
+
         if (Inventory[0] ~= nil) then
             ui_manager:get_widget("ItemMenu.Container.ItemMenuSecondBar.ItemText"):set_text(Game.Items[Inventory[0].item].description)
         else
@@ -211,6 +433,7 @@ UiContainer.ItemMenu = {
         end
     end,
 
+    --- Initializes the character selection mode.
     submenu_chars = function(self, multiple)
         UiContainer.current_submenu = "char_selection"
         --ui_manager:get_widget("ItemMenu.Container.ItemList.Cursor"):set_visible(false)
@@ -223,28 +446,43 @@ UiContainer.ItemMenu = {
             ui_manager:get_widget("ItemMenu.Container.ItemMenuCharacters.Cursor"):set_visible(true)
             ui_manager:get_widget("ItemMenu.Container.ItemMenuCharacters.Cursor"):set_default_animation("Position" .. self.char_position)
         end
+        ui_manager:get_widget("ItemMenu.Container.KeyItems.Cursor"):set_visible(false)
+        ui_manager:get_widget("ItemMenu.Container.ItemOrder.Cursor"):set_visible(false)
+    end,
+
+    --- Initializes the key item selection mode.
+    submenu_key_items = function(self)
+        UiContainer.current_submenu = "key_item_selection"
+        ui_manager:get_widget("ItemMenu.Container.ItemList.Cursor"):set_visible(true)
+        ui_manager:get_widget("ItemMenu.Container.ItemMenuCharacters.Cursor"):set_visible(false)
+        ui_manager:get_widget("ItemMenu.Container.ItemMenuCharacters.CursorMultiple"):set_visible(false)
+        ui_manager:get_widget("ItemMenu.Container.ItemOrder.Cursor"):set_visible(false)
+        ui_manager:get_widget("ItemMenu.Container.KeyItems.Cursor"):set_visible(true)
+        ui_manager:get_widget("ItemMenu.Container.KeyItems.Cursor"):set_default_animation("Position" .. self.key_items_cursor_y_position .. "-" .. self.key_items_cursor_x_position)
+        -- Show description of the selected item, if any.
+        local k_selected_id = (self.first_key_item_row_in_window + self.key_items_cursor_y_position) * 2 + self.key_items_cursor_x_position
+        if (Inventory.key[k_selected_id] == true) then
+            ui_manager:get_widget("ItemMenu.Container.ItemMenuSecondBar.ItemText"):set_text(Game.Key_Items[k_selected_id].description)
+        else
+            ui_manager:get_widget("ItemMenu.Container.ItemMenuSecondBar.ItemText"):set_text("")
+        end
     end,
 
     --- Uses an item from the menu.
     --
     -- Checks the item conditions. If they are valid, uses the item and discounts 1 from the
     -- inventory.
-    -- TODO: If after using a item, the quantity becames 0, go back to the item selection menu.
     --
     -- @param item The ID of the item being used.
-    -- @param character_pos Position in the party of the character on which to use the item (1-3). If
-    -- nil, the item affects the whole party.
+    -- @param character_pos Position in the party of the character on which to use the item (1-3).
+    -- If nil, the item affects the whole party.
     use_item = function(self, item, character_pos)
         char_id = -1
         if character_pos then
-            char_id = FFVII.Party[character_pos]
             -- Normal item, use in one character.
-        else
-            -- Party item, use in all characters.
+            char_id = FFVII.Party[character_pos]
         end
 
-        -- TODO: Refactor this. Use damage formulas, power and restore types to make it less
-        -- hardcoded
         -- TODO: When HP MAX is fixed, use it instead of base.
         if Game.Items[item].dmg_formula == 2 then
             -- HP or MP heal: Potion, Hi-Potion, X-Potion, Ether, Turbo Ether, Phoenix Down
@@ -627,7 +865,7 @@ UiContainer.ItemMenu = {
             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
+            local inventory_index = i + first_item_in_window + 1
             if Inventory[inventory_index] == nil then
                 w_icon:set_visible(false)
                 w_name:set_visible(false)
@@ -658,26 +896,35 @@ UiContainer.ItemMenu = {
         return
     end,
 
+    --- Populates the visible key items entries.
+    --
+    -- 20 entries visible at a time (10 rows).
+    populate_key_items = function(self)
+        local first_key_item_row_in_window = self.first_key_item_row_in_window
+        for i = 0, 9 do
+            local label_left = ui_manager:get_widget("ItemMenu.Container.KeyItems.KeyItem" .. tostring(i) .. "-0")
+            local label_right = ui_manager:get_widget("ItemMenu.Container.KeyItems.KeyItem" .. tostring(i) .. "-1")
+            local left_item_index = (first_key_item_row_in_window + i) * 2
+            local right_item_index = left_item_index + 1
+            if Inventory.has_key_item(left_item_index) == true then -- Left column
+                label_left:set_text(Game.Key_Items[left_item_index].name)
+            else
+                label_left:set_text("")
+            end
+            if Inventory.has_key_item(right_item_index) == true then -- Right column
+                label_right:set_text(Game.Key_Items[right_item_index].name)
+            else
+                label_right:set_text("")
+            end
+        end
+        return
+    end,
 
+    --- Hides the item menu and goes back to the main menu.
     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,
 }

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

@@ -117,6 +117,73 @@
             </sprite>
 
 
+        </widget>
+        <widget name="ItemOrder" x="140" y="25" width="62" height="108" visible="false">
+            <prototype name="Window" />
+            <text_area name="Order1" x="12" y="6" text_name="ItemMenuOrder1" font="FFVIIFont" visible="true" />
+            <text_area name="Order2" x="12" y="18" text_name="ItemMenuOrder2" font="FFVIIFont" visible="true" />
+            <text_area name="Order3" x="12" y="30" text_name="ItemMenuOrder3" font="FFVIIFont" visible="true" />
+            <text_area name="Order4" x="12" y="42" text_name="ItemMenuOrder4" font="FFVIIFont" visible="true" />
+            <text_area name="Order5" x="12" y="54" text_name="ItemMenuOrder5" font="FFVIIFont" visible="true" />
+            <text_area name="Order6" x="12" y="66" text_name="ItemMenuOrder6" font="FFVIIFont" visible="true" />
+            <text_area name="Order7" x="12" y="78" text_name="ItemMenuOrder7" font="FFVIIFont" visible="true" />
+            <text_area name="Order8" x="12" y="90" text_name="ItemMenuOrder8" font="FFVIIFont" visible="true" />
+            <sprite name="Cursor" image="screens/images/cursor.png" x="-12" 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" />
+            </sprite>
+
+        </widget>
+        <widget name="KeyItems" x="30" y="53" width="310" height="182" visible="false">
+            <prototype name="Window" />
+            <text_area name="KeyItem0-0" x="30" y="6" text_name="Placeholder" font="FFVIIFont" visible="true" />
+            <text_area name="KeyItem0-1" x="185" y="6" text_name="Placeholder" font="FFVIIFont" visible="true" />
+            <text_area name="KeyItem1-0" x="30" y="6" text_name="Placeholder" font="FFVIIFont" visible="true" />
+            <text_area name="KeyItem1-1" x="185" y="6" text_name="Placeholder" font="FFVIIFont" visible="true" />
+            <text_area name="KeyItem2-0" x="30" y="40" text_name="Placeholder" font="FFVIIFont" visible="true" />
+            <text_area name="KeyItem2-1" x="185" y="40" text_name="Placeholder" font="FFVIIFont" visible="true" />
+            <text_area name="KeyItem3-0" x="30" y="57" text_name="Placeholder" font="FFVIIFont" visible="true" />
+            <text_area name="KeyItem3-1" x="185" y="57" text_name="Placeholder" font="FFVIIFont" visible="true" />
+            <text_area name="KeyItem4-0" x="30" y="74" text_name="Placeholder" font="FFVIIFont" visible="true" />
+            <text_area name="KeyItem4-1" x="185" y="74" text_name="Placeholder" font="FFVIIFont" visible="true" />
+            <text_area name="KeyItem5-0" x="30" y="91" text_name="Placeholder" font="FFVIIFont" visible="true" />
+            <text_area name="KeyItem5-1" x="185" y="91" text_name="Placeholder" font="FFVIIFont" visible="true" />
+            <text_area name="KeyItem6-0" x="30" y="108" text_name="Placeholder" font="FFVIIFont" visible="true" />
+            <text_area name="KeyItem6-1" x="185" y="108" text_name="Placeholder" font="FFVIIFont" visible="true" />
+            <text_area name="KeyItem7-0" x="30" y="125" text_name="Placeholder" font="FFVIIFont" visible="true" />
+            <text_area name="KeyItem7-1" x="185" y="125" text_name="Placeholder" font="FFVIIFont" visible="true" />
+            <text_area name="KeyItem8-0" x="30" y="142" text_name="Placeholder" font="FFVIIFont" visible="true" />
+            <text_area name="KeyItem8-1" x="185" y="142" text_name="Placeholder" font="FFVIIFont" visible="true" />
+            <text_area name="KeyItem9-0" x="30" y="159" text_name="Placeholder" font="FFVIIFont" visible="true" />
+            <text_area name="KeyItem9-1" x="185" 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="Position0-0" length="0" x="0:0" y="0:6" />
+                <animation name="Position0-1" length="0" x="0:150" y="0:6" />
+                <animation name="Position1-0" length="0" x="0:0" y="0:23" />
+                <animation name="Position1-1" length="0" x="0:150" y="0:23" />
+                <animation name="Position2-0" length="0" x="0:0" y="0:40" />
+                <animation name="Position2-1" length="0" x="0:150" y="0:40" />
+                <animation name="Position3-0" length="0" x="0:0" y="0:57" />
+                <animation name="Position3-1" length="0" x="0:150" y="0:57" />
+                <animation name="Position4-0" length="0" x="0:0" y="0:78" />
+                <animation name="Position4-1" length="0" x="0:150" y="0:78" />
+                <animation name="Position5-0" length="0" x="0:0" y="0:91" />
+                <animation name="Position5-1" length="0" x="0:150" y="0:91" />
+                <animation name="Position6-0" length="0" x="0:0" y="0:108" />
+                <animation name="Position6-1" length="0" x="0:150" y="0:108" />
+                <animation name="Position7-0" length="0" x="0:0" y="0:125" />
+                <animation name="Position7-1" length="0" x="0:150" y="0:125" />
+                <animation name="Position8-0" length="0" x="0:0" y="0:142" />
+                <animation name="Position8-1" length="0" x="0:150" y="0:142" />
+                <animation name="Position9-0" length="0" x="0:0" y="0:159" />
+                <animation name="Position9-1" length="0" x="0:150" y="0:159" />
+            </sprite>
         </widget>
         <!--<widget name="Characters" x="0" y="17" width="300" height="190" visible="true">
             <prototype name="Window" />

+ 15 - 50
data/data/screens/main_menu/main_menu.lua

@@ -2,62 +2,26 @@ if UiContainer == nil then UiContainer = {} end
 
 --- The main menu.
 UiContainer.MainMenu = {
+
+    --- Current cursor position in the main menu (1-10).
     position = 1,
+
+    -- Max cursor position in the main menu.
     position_total = 10,
 
+    --- Run when the menu is creaded.
+    --
+    -- It does nothing.
     on_start = function(self)
         return 0
     end,
 
-    --- Populates a character data.
+    --- Handles button events.
     --
-    -- @param row The row the character is in (1-3).
-    -- @param character The character to get the data from. If nill, the character details will be
-    -- hidden.
-    populate_character = function(row, character)
-        local widget = "MainMenu.Container.Characters.Character" .. tostring(row)
-        if character == nil then
-            ui_manager:get_widget(widget):set_visible(false)
-            return 0
-        end
-
-        -- TODO: When MAX HP and MP values are fixed, use them instead of base.
-
-        local char_id = character.char_id
-        ui_manager:get_widget(widget):set_visible(true)
-
-        -- Critical?
-        if character.stats.hp.current == 0 then
-            ui_manager:get_widget(widget .. ".Data.HpCurrent"):set_colour(1.0, 0, 0)
-        elseif character.stats.hp.current <= character.stats.hp.base / 4 then
-            ui_manager:get_widget(widget .. ".Data.HpCurrent"):set_colour(1.0, 1.0, 0)
-        else
-            ui_manager:get_widget(widget .. ".Data.HpCurrent"):set_colour(0.9, 0.9, 0.9)
-        end
-
-        -- Text data
-        ui_manager:get_widget(widget .. ".Data.Name"):set_text(character.name)
-        ui_manager:get_widget(widget .. ".Data.LvNumber"):set_text(tostring(character.level))
-        ui_manager:get_widget(widget .. ".Data.HpCurrent"):set_text(tostring(character.stats.hp.current))
-        ui_manager:get_widget(widget .. ".Data.HpMax"):set_text(tostring(character.stats.hp.base))
-        ui_manager:get_widget(widget .. ".Data.MpCurrent"):set_text(tostring(character.stats.mp.current))
-        ui_manager:get_widget(widget .. ".Data.MpMax"):set_text(tostring(character.stats.mp.base))
-
-        -- Calculate the HP and MP bar.
-        local max_hp_width= ui_manager:get_widget(widget .. ".Data.HpLine"):get_width()
-        local hp_width = character.stats.hp.current * max_hp_width / character.stats.hp.base
-        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
-        ui_manager:get_widget(widget .. ".Data.MpLineCurrent"):set_width(mp_width)
-
-        return 0
-    end,
-
-
-
+    -- For the current submenu, handles directional keys, enter and escape events.
+    -- @param button Pressed button string key. "Up", "Left", "Enter" and "Escape" are handled.
+    -- @param event Trigger event. Normally, "Press".
     on_button = function(self, button, event)
-
         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")
@@ -96,7 +60,7 @@ UiContainer.MainMenu = {
 
     --- Opens the main menu.
     --
-    -- Populates and updates displayed data before opening.
+    -- Populates and updates displayed data before opening. It also pauses the game.
     show = function(self)
         UiContainer.current_menu = "main"
         UiContainer.current_submenu = ""
@@ -129,8 +93,9 @@ UiContainer.MainMenu = {
         return 0;
     end,
 
-
-
+    --- Hides the item menu and goes back to the game.
+    --
+    -- It also unpauses the game.
     hide = function(self)
         UiContainer.current_menu = ""
         UiContainer.current_submenu = ""

+ 3 - 0
data/data/scripts.xml

@@ -18,5 +18,8 @@
     <script file="scripts/battle_ai.lua" />
     <script file="screens/menu_helpers.lua" />
 
+    <!-- TODO: Debugging pourposes only -->
+    <script file="scripts/debug_data.lua" />
+
     <system_script name="MapChanger" />
 </scripts>

+ 33 - 12
data/data/scripts/data.lua

@@ -53,19 +53,9 @@ Game = {
 
     --- Item definitions.
     --
-    -- Only items: weapons, armors, accessories, key items and materia are not
-    -- stored here.
+    -- Include items, weapons, armors, and accessories. Key items and materia are not stored here.
     Items = {},
 
-    --- Weapon definitions.
-    Weapons = {},
-
-    --- Weapon definitions.
-    Armors = {},
-
-    --- Accessory definitions.
-    Accessories = {},
-
     --- Materia definitions.
     Materia = {},
 
@@ -85,15 +75,46 @@ 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:
+-- index "money". Key items are under the index "key", with indexes 0-50, true or false if the key
+-- item is in the inventory. Inventory has the following functions:
 --   add_item(item, quantity)
 --   remove_item(item, quantity)
+--   add_key_item(item)
+--   remove_key_item(item)
+--   has_key_item(item)
 --   get_item_quantity(item)
 --   add_money(amount)
 --   remove_money(amount)
 --   money_to_2_banks(b1, b2, a1, a2)
+--   sort(criteria)
 Inventory = {}
 Inventory.money = 0
+Inventory.key = {}
+for i = 0, 50  do
+    Inventory.key[i] = false
+end
+Inventory.ITEM_TYPE = {
+    ITEM = 0,
+    WEAPON = 1,
+    ARMOR = 2,
+    ACCESSORY = 3
+}
+Inventory.RESTORE_TYPE = {
+    HP = 0,
+    MP = 1,
+    STATUS = 2,
+    NONE = 3
+}
+Inventory.ORDER = {
+    CUSTOM = 0,
+    FIELD = 1,
+    BATTLE = 2,
+    THROW = 3,
+    TYPE = 4,
+    NAME = 5,
+    MOST = 6,
+    LEAST = 7
+}
 
 
 

+ 26 - 0
data/data/scripts/debug_data.lua

@@ -0,0 +1,26 @@
+-- Debug data used for testing, remove or don't include this script for actual gameplay.
+
+Inventory.add_item(6, 5)
+Inventory.add_item(73, 4)
+Inventory.add_item(56, 3)
+Inventory.add_item(0, 2)
+Inventory.add_item(1, 4)
+Inventory.add_item(42, 3)
+Inventory.add_item(1, 4)
+Inventory.add_item(71, 4)
+Inventory.add_item(13, 2)
+Inventory.add_item(14, 4)
+Inventory.add_item(58, 3)
+Inventory.add_item(240, 4)
+Inventory.add_key_item(0)
+Inventory.add_key_item(1)
+Inventory.add_key_item(5)
+Inventory.add_key_item(18)
+Inventory.add_key_item(19)
+Inventory.add_key_item(20)
+Inventory.add_key_item(21)
+Inventory.add_key_item(22)
+Inventory.add_key_item(33)
+Inventory.add_key_item(38)
+Inventory.sort(Inventory.ORDER.NAME)
+Characters[0].stats.hp.current = 10

+ 90 - 4
data/data/scripts/system.lua

@@ -174,7 +174,7 @@ Inventory.add_item = function(item, quantity)
         print("Tried add invalid item \"" .. item .. "\".");
         return
     end
-    for i = 0, MAX_INVENTORY_SLOTS do
+    for i = 1, MAX_INVENTORY_SLOTS do
         if Inventory[i] ~= nil and Inventory[i].item == item then
             -- Item already in inventory, add quantity
             Inventory[i].quantity = Inventory[i].quantity + quantity
@@ -185,7 +185,7 @@ Inventory.add_item = function(item, quantity)
         end
     end
     -- Item not in inventory, search for the first available position.
-    for i = 0, MAX_INVENTORY_SLOTS do
+    for i = 1, MAX_INVENTORY_SLOTS do
         if Inventory[i] == nil then
             Inventory[i] = {item = item, quantity = quantity}
             if Inventory[i].quantity > 99 then
@@ -205,7 +205,7 @@ end
 -- @return Number of the items in the inventory.
 Inventory.get_item_quantity = function(item)
     local qty = 0
-    for i = 0, MAX_INVENTORY_SLOTS do
+    for i = 1, MAX_INVENTORY_SLOTS do
         if Inventory[i] ~= nil and Inventory[i].item == item then
             qty = qty + Inventory[i].quantity
         end
@@ -221,7 +221,7 @@ end
 -- @param item Item ID.
 -- @param quantity Quantity to remove.
 Inventory.remove_item = function(item, quantity)
-    for i = 0, MAX_INVENTORY_SLOTS do
+    for i = 1, MAX_INVENTORY_SLOTS do
         if Inventory[i] ~= nil and Inventory[i].item == item then
             -- Item already in inventory, add quantity
             Inventory[i].quantity = Inventory[i].quantity - quantity
@@ -233,6 +233,30 @@ Inventory.remove_item = function(item, quantity)
     end
 end
 
+--- Adds a key item.
+--
+-- @param item ID of the key item (0-50).
+Inventory.add_key_item = function(item)
+    Inventory.key[item] = true
+    return
+end
+
+--- Removes a key item.
+--
+-- @param item ID of the key item (0-50).
+Inventory.remove_key_item = function(item)
+    Inventory.key[item] = false
+    return
+end
+
+--- Checks if the party has a key item.
+--
+-- @param item ID of the key item (0-50).
+-- @return True if the party has the key item, false otherwise.
+Inventory.has_key_item = function(item)
+    return Inventory.key[item]
+end
+
 --- Adds money.
 --
 -- @param amount The money to add.
@@ -270,3 +294,65 @@ 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
+
+--- Sorts the items in the inventory.
+--
+-- @param criteria {@see Inventory.ORDER.CUSTOM} (unimplemented), {@see Inventory.ORDER.FIELD.},
+-- {@see Inventory.ORDER.BATTLE}, {@see Inventory.ORDER.THROW}, {@see Inventory.ORDER.TYPE},
+-- {@see Inventory.ORDER.NAME}, {@see Inventory.ORDER.MOST} or {@see Inventory.ORDER.LEAST}. Any
+-- othe rvalue and the items will not be sorted.
+Inventory.sort = function(criteria)
+    if criteria == Inventory.ORDER.CUSTOM then
+        print("Custom order unimplemented")
+    elseif criteria == Inventory.ORDER.FIELD then
+        table.sort(Inventory, function (left, right)
+                                  if Game.Items[left.item].menu ~= Game.Items[right.item].menu then
+                                      return Game.Items[left.item].menu > Game.Items[right.item].menu
+                                  end
+                                  return left.item < right.item
+                              end
+        )
+    elseif criteria == Inventory.ORDER.BATTLE then
+        table.sort(Inventory, function (left, right)
+                                  if Game.Items[left.item].battle ~= Game.Items[right.item].battle then
+                                      return Game.Items[left.item].battle > Game.Items[right.item].battle
+                                  end
+                                  return left.item < right.item
+                              end
+        )
+    elseif criteria == Inventory.ORDER.THROW then
+        table.sort(Inventory, function (left, right)
+                                  if Game.Items[left.item].throw ~= Game.Items[right.item].throw then
+                                      return Game.Items[left.item].throw > Game.Items[right.item].throw
+                                  end
+                                  return left.item < right.item
+                              end
+        )
+    elseif criteria == Inventory.ORDER.TYPE then
+        table.sort(Inventory, function (left, right)
+                                  return left.item < right.item
+                              end
+        )
+    elseif criteria == Inventory.ORDER.NAME then
+        table.sort(Inventory, function (left, right)
+                                  return Game.Items[left.item].name < Game.Items[right.item].name
+                              end
+        )
+    elseif criteria == Inventory.ORDER.MOST then
+        table.sort(Inventory, function (left, right)
+                                  if left.quantity ~= right.quantity then
+                                      return left.quantity > right.quantity
+                                  end
+                                  return left.item < right.item
+                              end
+        )
+    elseif criteria == Inventory.ORDER.LEAST then
+        table.sort(Inventory, function (left, right)
+                                  if left.quantity ~= right.quantity then
+                                      return left.quantity < right.quantity
+                                  end
+                                  return left.item < right.item
+                              end
+        )
+    end
+end