Kaynağa Gözat

A bit of work in the battle system. Magic and summon list are now shown, commands are ordered.

Iñigo Valentin 3 yıl önce
ebeveyn
işleme
cceff4d5a4

+ 21 - 8
data/data/screens/battle/battle.xml

@@ -35,14 +35,14 @@
                 <text_area name="Cmd2" x="10" y="22%+5" width="100%-5" height="25%" font="FFVIIFont" text_name="BattleUiHp" visible="true" />
                 <text_area name="Cmd3" x="10" y="44%+5" width="100%-5" height="25%" font="FFVIIFont" text_name="BattleUiHp" visible="true" />
                 <text_area name="Cmd4" x="10" y="66%+5" width="100%-5" height="25%" font="FFVIIFont" text_name="BattleUiHp" visible="true" />
-                <text_area name="Cmd5" x="60" y="0%+5" width="100%-5" height="25%" font="FFVIIFont" text_name="BattleUiHp" visible="true" />
-                <text_area name="Cmd6" x="60" y="22%+5" width="100%-5" height="25%" font="FFVIIFont" text_name="BattleUiHp" visible="true" />
-                <text_area name="Cmd7" x="60" y="44%+5" width="100%-5" height="25%" font="FFVIIFont" text_name="BattleUiHp" visible="true" />
-                <text_area name="Cmd8" x="60" y="66%+5" width="100%-5" height="25%" font="FFVIIFont" text_name="BattleUiHp" visible="true" />
-                <text_area name="Cmd9" x="105" y="0%+5" width="100%-5" height="25%" font="FFVIIFont" text_name="BattleUiHp" visible="true" />
-                <text_area name="Cmd10" x="105" y="22%+5" width="100%-5" height="25%" font="FFVIIFont" text_name="BattleUiHp" visible="true" />
-                <text_area name="Cmd11" x="105" y="44%+5" width="100%-5" height="25%" font="FFVIIFont" text_name="BattleUiHp" visible="true" />
-                <text_area name="Cmd12" x="105" y="66%+5" width="100%-5" height="25%" font="FFVIIFont" text_name="BattleUiHp" visible="true" />
+                <text_area name="Cmd5" x="70" y="0%+5" width="100%-5" height="25%" font="FFVIIFont" text_name="BattleUiHp" visible="true" />
+                <text_area name="Cmd6" x="70" y="22%+5" width="100%-5" height="25%" font="FFVIIFont" text_name="BattleUiHp" visible="true" />
+                <text_area name="Cmd7" x="70" y="44%+5" width="100%-5" height="25%" font="FFVIIFont" text_name="BattleUiHp" visible="true" />
+                <text_area name="Cmd8" x="70" y="66%+5" width="100%-5" height="25%" font="FFVIIFont" text_name="BattleUiHp" visible="true" />
+                <text_area name="Cmd9" x="130" y="0%+5" width="100%-5" height="25%" font="FFVIIFont" text_name="BattleUiHp" visible="true" />
+                <text_area name="Cmd10" x="130" y="22%+5" width="100%-5" height="25%" font="FFVIIFont" text_name="BattleUiHp" visible="true" />
+                <text_area name="Cmd11" x="130" y="44%+5" width="100%-5" height="25%" font="FFVIIFont" text_name="BattleUiHp" visible="true" />
+                <text_area name="Cmd12" x="130" y="66%+5" width="100%-5" height="25%" font="FFVIIFont" text_name="BattleUiHp" visible="true" />
                 <sprite name="Cursor" image="icons/cursor.png" x="132" y="99" width="24" height="16" visible="true">
                     <animation name="Position1" length="0" x="0:-15" y="0:0%+7" />
                     <animation name="Position2" length="0" x="0:-15" y="0:22%+7" />
@@ -58,6 +58,19 @@
                     <animation name="Position12" length="0" x="0:80" y="0:66%+7" />
                 </sprite>
             </widget>
+            <widget name="List" x="42%" y="5%" width="120" height="95%" visible="false">
+                <prototype name="Window" />
+                <text_area name="List1" x="10" y="0%+5" width="100%-5" height="25%" font="FFVIIFont" visible="true" />
+                <text_area name="List2" x="10" y="22%+5" width="100%-5" height="25%" font="FFVIIFont" visible="true" />
+                <text_area name="List3" x="10" y="44%+5" width="100%-5" height="25%" font="FFVIIFont" visible="true" />
+                <text_area name="List4" x="10" y="66%+5" width="100%-5" height="25%" font="FFVIIFont" visible="true" />
+                <sprite name="Cursor" image="icons/cursor.png" x="132" y="99" width="24" height="16" visible="true">
+                    <animation name="Position1" length="0" x="0:-15" y="0:0%+7" />
+                    <animation name="Position2" length="0" x="0:-15" y="0:22%+7" />
+                    <animation name="Position3" length="0" x="0:-15" y="0:44%+7" />
+                    <animation name="Position4" length="0" x="0:-15" y="0:66%+7" />
+                </sprite>
+            </widget>
         </widget>
     </widget>
 </screen>

+ 7 - 8
data/data/screens/pause_menu/pause_menu.xml

@@ -1,13 +1,12 @@
 <screen name="PauseMenu" script="scripts/menu/pause_menu.lua" visible="false">
-    <widget name="Menu" x="50%-195" y="50%-75" width="150" height="50" scale="3 3" visible="true">
+    <widget name="Menu" x="50%-170" y="50%-75" width="100" height="75" scale="3 3" visible="true">
         <prototype name="Window" />
-        <text_area name="Header" x="50%" y="5" text_name="PauseMenuHeader" font="FFVIIFont" text_align="center" visible="true" />
-        <text_area name="Text" x="50%" y="20" text_name="PauseMenuText" font="FFVIIFont" text_align="center" visible="true" />
-        <text_area name="Yes" x="40" y="35" text_name="PauseMenuYes" font="FFVIIFont" visible="true" />
-        <text_area name="No" x="90" y="35" text_name="PauseMenuNo" font="FFVIIFont" visible="true" />
-        <sprite name="Cursor" image="icons/cursor.png" x="15" y="35" width="24" height="16" visible="true">
-            <animation name="Position1" length="0" x="0:15" />
-            <animation name="Position2" length="0" x="0:65" />
+        <text_area name="Title" x="50%" y="5" text_name="PauseMenuHeader" font="FFVIIFont" text_align="center" visible="true" />
+        <text_area name="Resume" x="25" y="35" text_name="PauseMenuResume" font="FFVIIFont" visible="true" />
+        <text_area name="Quit" x="25" y="55" text_name="PauseMenuQuit" font="FFVIIFont" visible="true" />
+        <sprite name="Cursor" image="icons/cursor.png" x="0" y="35" width="24" height="16" visible="true">
+            <animation name="Position1" length="0" y="0:35" />
+            <animation name="Position2" length="0" y="0:55" />
         </sprite>
     </widget>
 </screen>

+ 1 - 1
data/data/scripts/_scripts.xml

@@ -1,5 +1,6 @@
 <scripts>
     <script file="scripts/data.lua" />
+    <script file="scripts/battle.lua" />
     <script file="gamedata/accessories.lua" />
     <script file="gamedata/armors.lua" />
     <script file="gamedata/attacks.lua" />
@@ -19,7 +20,6 @@
     <script file="gamedata/initial_savemap.lua" />
 
     <script file="scripts/field.lua" />
-    <script file="scripts/battle.lua" />
     <script file="scripts/battle_ai.lua" />
     <script file="scripts/menu/menu_helpers.lua" />
 

+ 40 - 0
data/data/scripts/battle.lua

@@ -15,6 +15,46 @@ Battle.Type = {
     LOGIC = 2,
 }
 
+--- Menus that must be shown when selecting a command.
+Battle.Menu = {
+
+    --- No menu, execute command directly, without target selection.
+    NONE = 0,
+
+    --- Show magic menu.
+    MAGIC = 1,
+
+    --- Show summon menu.
+    SUMMON = 2,
+
+    --- Show item menu.
+    ITEM = 3,
+
+    --- Show enemy skill menu.
+    E_SKILL = 4,
+
+    --- Show throw menu.
+    THROW = 5,
+
+    --- Show limit menu.
+    LIMIT = 6,
+
+    --- No menu, skip to target selection.
+    TARGET = 7,
+
+    --- Show W-Magic menu
+    W_MAGIC = 8,
+
+    --- Show W-Summon menu
+    W_SUMMON = 9,
+
+    --- Show W-Item menu
+    W_ITEM = 10,
+
+    --- Show Coin menu
+    COIN = 11,
+}
+
 Battle.ActionDefaults = {
     [ Battle.Action.MONSTER_ACTION ] = { animation_script = "0" }
 }

+ 343 - 101
data/data/scripts/battle/battle_ui.lua

@@ -9,6 +9,19 @@ UiContainer.BattleUi = {
     --- Enemies
     enemies = {},
 
+    --- Battle action queue.
+    --
+    -- Every battle action gets added to this queue, sorted by priority, and the actions are
+    -- executed in order. Actions for enemies and allies are stored here.
+    action_queue = {},
+
+    --- Party command selection queue.
+    --
+    -- When a party member ATB is filled, the character is added to this queue. Command selection
+    -- windows are show in this order. If a character is dismissed (triangle in PSX), the character
+    -- gets moved to the end of the queue.
+    command_queue = {},
+
     --- Run when the menu is creaded.
     --
     -- It does nothing.
@@ -28,12 +41,22 @@ UiContainer.BattleUi = {
     --- Current position for the cursor in the list window.
     cursor_list = 1,
 
+    --- First visible item in the list window
+    first_list = 1,
+
     --- Current position for the cursor in the target window.
     cursor_target = 1,
 
     --- ID of the character the cursor is for.
     current_character_window = -1,
 
+    --- Indicates the currently opened menu
+    --
+    -- Possible values are none, magic, wmagic1, wmagic2, summon, wsummon1, wsummon2, item, witem1,
+    -- witem2, eskill, throw, limit, coin.
+    current_list = "none",
+
+
     --- Handles button events.
     --
     -- For the current submenu, handles directional keys, enter and escape events.
@@ -49,38 +72,40 @@ UiContainer.BattleUi = {
         -- Handle command cursors
         if self.window_state == "command" then
             local command_count = #self.party[self.current_character_window].command
+            local cursor = ui_manager:get_widget("BattleUi.Container.Bottom.Commands.Cursor")
             if button == "Down" then
                 if self.cursor_command < 4 then
                     -- If on the first three ones, always alow to go down to reach "Item"
                     self.cursor_command = self.cursor_command + 1
-                    ui_manager:get_widget("BattleUi.Container.Bottom.Commands.Cursor"):set_default_animation("Position" .. tostring(self.cursor_command))
+                    cursor:set_default_animation("Position" .. tostring(self.cursor_command))
                     audio_manager:play_sound("Cursor")
                 elseif self.cursor_command % 4 == 0 then
                     -- If on a multiple of 4, go to the top of the column
                     self.cursor_command = self.cursor_command - 3
-                    ui_manager:get_widget("BattleUi.Container.Bottom.Commands.Cursor"):set_default_animation("Position" .. tostring(self.cursor_command))
+                    cursor:set_default_animation("Position" .. tostring(self.cursor_command))
                     audio_manager:play_sound("Cursor")
                 elseif command_count > self.cursor_command then
                     -- If there is a command below, allow one position down.
                     self.cursor_command = self.cursor_command + 1
-                    ui_manager:get_widget("BattleUi.Container.Bottom.Commands.Cursor"):set_default_animation("Position" .. tostring(self.cursor_command))
+                    cursor:set_default_animation("Position" .. tostring(self.cursor_command))
                     audio_manager:play_sound("Cursor")
                 else
                     -- Back to the first row
                     self.cursor_command = 4 * math.floor(self.cursor_command / 4) + 1
-                    ui_manager:get_widget("BattleUi.Container.Bottom.Commands.Cursor"):set_default_animation("Position" .. tostring(self.cursor_command))
+                    cursor:set_default_animation("Position" .. tostring(self.cursor_command))
                     audio_manager:play_sound("Cursor")
                 end
             elseif button == "Up" then
                 if self.cursor_command == 1 then
                     -- If on "Attack, always go to item
                     self.cursor_command = 4
-                    ui_manager:get_widget("BattleUi.Container.Bottom.Commands.Cursor"):set_default_animation("Position" .. tostring(self.cursor_command))
+                    cursor:set_default_animation("Position" .. tostring(self.cursor_command))
                     audio_manager:play_sound("Cursor")
                 elseif self.cursor_command > 1 and self.cursor_command < 5 then
-                    -- If on the second, third, or fourth, always alwow to go down to reach "Attack" from "Item"
+                    -- If on the second, third, or fourth, always alwow to go down to
+                    -- reach "Attack" from "Item"
                     self.cursor_command = self.cursor_command - 1
-                    ui_manager:get_widget("BattleUi.Container.Bottom.Commands.Cursor"):set_default_animation("Position" .. tostring(self.cursor_command))
+                    cursor:set_default_animation("Position" .. tostring(self.cursor_command))
                     audio_manager:play_sound("Cursor")
                 elseif self.cursor_command % 4 == 1 and command_count > self.cursor_command then
                     -- If on the first row, go to the last where there is a command
@@ -88,27 +113,27 @@ UiContainer.BattleUi = {
                     while self.cursor_command > command_count do
                         self.cursor_command = self.cursor_command - 1
                     end
-                    ui_manager:get_widget("BattleUi.Container.Bottom.Commands.Cursor"):set_default_animation("Position" .. tostring(self.cursor_command))
+                    cursor:set_default_animation("Position" .. tostring(self.cursor_command))
                     audio_manager:play_sound("Cursor")
                 else
                     -- Else, always allow to go one up.
                     self.cursor_command = self.cursor_command - 1
-                    ui_manager:get_widget("BattleUi.Container.Bottom.Commands.Cursor"):set_default_animation("Position" .. tostring(self.cursor_command))
+                    cursor:set_default_animation("Position" .. tostring(self.cursor_command))
                     audio_manager:play_sound("Cursor")
                 end
             elseif button == "Left" then
                 if command_count > 4 then
                     if self.cursor_command > 4 then
                         self.cursor_command = self.cursor_command - 4
-                        ui_manager:get_widget("BattleUi.Container.Bottom.Commands.Cursor"):set_default_animation("Position" .. tostring(self.cursor_command))
+                        cursor:set_default_animation("Position" .. tostring(self.cursor_command))
                         audio_manager:play_sound("Cursor")
                     else
-                        local new_position = self.cursor_command + 4 * math.floor(command_count / 4)
-                        while new_position > command_count do
-                            new_position = new_position - 4
+                        local new_pos = self.cursor_command + 4 * math.floor(command_count / 4)
+                        while new_pos > command_count do
+                            new_pos = new_pos - 4
                         end
-                        self.cursor_command = new_position
-                        ui_manager:get_widget("BattleUi.Container.Bottom.Commands.Cursor"):set_default_animation("Position" .. tostring(self.cursor_command))
+                        self.cursor_command = new_pos
+                        cursor:set_default_animation("Position" .. tostring(self.cursor_command))
                         audio_manager:play_sound("Cursor")
                     end
                 end
@@ -116,50 +141,238 @@ UiContainer.BattleUi = {
                 if command_count > 4 then
                     self.cursor_command = self.cursor_command + 4
                     if self.cursor_command > command_count then
-                        self.cursor_command = self.cursor_command - 4 * math.floor(self.cursor_command / 4)
+                        self.cursor_command
+                          = self.cursor_command - 4 * math.floor(self.cursor_command / 4)
                         if self.cursor_command < 1 then
                             self.cursor_command = self.cursor_command + 4
                         end
                     end
-                    ui_manager:get_widget("BattleUi.Container.Bottom.Commands.Cursor"):set_default_animation("Position" .. tostring(self.cursor_command))
+                    cursor:set_default_animation("Position" .. tostring(self.cursor_command))
                     audio_manager:play_sound("Cursor")
                 end
-            -- TODO: elseif accept, skip buttons...
+            elseif button == "Escape" then
+                -- Push the current character to the end of the command queue, and show the first.
+                local new_queue = {}
+                for i = 1, #self.command_queue do
+                    if self.command_queue[i + 1] ~= nil then
+                        table.insert(new_queue, self.command_queue[i + 1])
+                    end
+                end
+                table.insert(new_queue, self.command_queue[1])
+                self.command_queue = new_queue
+                audio_manager:play_sound("Cursor")
+                self:show_commands(self.command_queue[1])
+            elseif button == "Enter" then
+                -- Check if a menu must be shown
+                local cmd = Game.Commands[
+                  self.party[self.current_character_window].command[self.cursor_command]
+                ]
+                if cmd.menu == Battle.Menu.NONE then
+                    print("DIRECT COMMAND")
+                elseif cmd.menu == Battle.Menu.TARGET then
+                    print("TARGET COMMAND")
+                else
+                    self:show_list(cmd.menu)
+                end
+            end
+        -- Handle menu list cursor
+        elseif self.window_state == "list" then
+            local total = 0
+            if
+              self.current_list == "magic"
+              or self.current_list == "wmagic1" or self.current_list == "wmagic2"
+            then
+                total = #self.party[self.current_character_window].magic
+            elseif
+              self.current_list == "summon"
+              or self.current_list == "wsummon1" or self.current_list == "wsummon2"
+            then
+                total = #self.party[self.current_character_window].summon
+            -- TODO: Rest of cases
             end
+            local cursor = ui_manager:get_widget("BattleUi.Container.Bottom.List.Cursor")
+                if button == "LCtrl" then
+                    self:close_list()
+                elseif button == "Down" then
+                    if total <=  self.cursor_list + self.first_list - 1 then
+                        do return 0 end
+                    end
+                    if self.cursor_list < 4 then
+                        -- Move cursor down
+                        audio_manager:play_sound("Cursor")
+                        self.cursor_list = self.cursor_list + 1
+                        cursor:set_default_animation("Position" .. tostring(self.cursor_list))
+                    else
+                        -- Scroll down
+                        audio_manager:play_sound("Cursor")
+                        self.first_list = self.first_list + 1
+                        self:repopulate_list()
+                    end
+                elseif button == "Up" then
+                    if self.cursor_list == 1 and self.first_list == 1 then
+                        do return 0 end
+                    end
+                    if self.cursor_list > 1 then
+                        -- Move cursor up
+                        audio_manager:play_sound("Cursor")
+                        self.cursor_list = self.cursor_list - 1
+                        cursor:set_default_animation("Position" .. tostring(self.cursor_list))
+                    else
+                        -- Scroll up
+                        audio_manager:play_sound("Cursor")
+                        self.first_list = self.first_list - 1
+                        self:repopulate_list()
+                    end
+                end
         end
         return 0
     end,
 
+    repopulate_list = function(self)
+        if self.current_list == "magic"
+          or self.current_list == "wmagic1" or self.current_list == "wmagic2"
+        then
+            self:populate_magic_list()
+        elseif self.current_list == "magic"
+          or self.current_list == "wmagic1" or self.current_list == "wmagic2"
+        then
+            self:populate_summon_list()
+        -- TODO: all other cases
+        end
+    end,
+
     tick = function(self)
-        --print("CALLED BattleUI:tick()")
-        if self.window_state ~= "wait" then
+        if System:is_paused() == true then
             do return end
         end
+        --print("CALLED BattleUI:tick()")
+        --if self.window_state ~= "wait" then
+        --    do return end
+        --end
+        -- Advance party ATBs
         for i = 1, #self.party do
             if self.party[i].dead == false then -- TODO: Or stopped
-                if self.party[i].atb < 100 then
+                if self.party[i].atb < 255 then
                     -- TODO: Consider haste/slow
-                    self.party[i].atb = self.party[i].atb + 0.01 * Characters[self.party[i].character].stats.dex.total -- TODO
-                    if self.party[i].atb >= 100 then
+                    self.party[i].atb
+                      = self.party[i].atb
+                      + 0.04 * Characters[self.party[i].character].stats.dex.total -- TODO
+                    if self.party[i].atb >= 255 then
                         print(Characters[self.party[i].character].name .. " ATB full")
-                        ui_manager:get_widget("BattleUi.Container.Bottom.Character" .. tostring(i) .. ".Right.AtbFill"):set_colour(0, 1, 0)
-                        self:show_commands(i)
+                        ui_manager:get_widget(
+                          "BattleUi.Container.Bottom.Character" .. tostring(i) .. ".Right.AtbFill"
+                        ):set_colour(0, 1, 0)
+                        --self:show_commands(i)
+                        table.insert(self.command_queue, i)
                     end
                     -- Fill ATB UI bar
                     -- 0   -> 0%
-                    -- 100 -> 18%-6
-                    local width_percent = math.min(18, 18 * self.party[i].atb / 100)
+                    -- 255 -> 18%-6
+                    local width_percent = math.min(18, 18 * self.party[i].atb / 255)
                     local width_fixed = math.max(-6, -6 * width_percent / 18)
-                    ui_manager:get_widget("BattleUi.Container.Bottom.Character" .. tostring(i) .. ".Right.AtbFill"):set_width(width_percent, width_fixed)
+                    ui_manager:get_widget(
+                      "BattleUi.Container.Bottom.Character" .. tostring(i) .. ".Right.AtbFill"
+                    ):set_width(width_percent, width_fixed)
                 end
             end
         end
+        -- TODO: Advance enemy ATBs
+        -- Show a command window if required
+        if self.window_state == "wait" and #self.command_queue > 0 then
+            self:show_commands(self.command_queue[1])
+        end
         --print("ENDED BattleUI:tick()")
     end,
 
+    show_list = function(self, menu)
+        local cursor = ui_manager:get_widget("BattleUi.Container.Bottom.List.Cursor")
+        if (menu == Battle.Menu.MAGIC) then
+            audio_manager:play_sound("Cursor")
+            self.cursor_list = 1
+            self.first_list = 1
+            self.current_list = "magic"
+            self:populate_magic_list()
+            self.window_state = "list"
+            ui_manager:get_widget("BattleUi.Container.Bottom.List"):set_visible(true)
+            cursor:set_default_animation("Position" .. tostring(self.cursor_list))
+            cursor:set_visible(true)
+        elseif (menu == Battle.Menu.SUMMON) then
+            audio_manager:play_sound("Cursor")
+            self.cursor_list = 1
+            self.first_list = 1
+            self.current_list = "summon"
+            self:populate_summon_list()
+            self.window_state = "list"
+            ui_manager:get_widget("BattleUi.Container.Bottom.List"):set_visible(true)
+            cursor:set_default_animation("Position" .. tostring(self.cursor_list))
+            cursor:set_visible("Position" .. tostring(self.cursor_list))
+        elseif (menu == Battle.Menu.ITEM) then
+            print("Show item menu: Not implemented")
+        elseif (menu == Battle.Menu.E_SKILL) then
+            print("Show e. skill menu: Not implemented")
+        elseif (menu == Battle.Menu.THROW) then
+            print("Show throw menu: Not implemented")
+        elseif (menu == Battle.Menu.LIMIT) then
+            print("Show magic menu: Not implemented")
+        elseif (menu == Battle.Menu.W_MAGIC) then
+            print("Show w-magic menu: Not implemented")
+        elseif (menu == Battle.Menu.W_SUMMON) then
+            print("Show w-summon menu: Not implemented")
+        elseif (menu == Battle.Menu.W_ITEM) then
+            print("Show w-magic menu: Not implemented")
+        elseif (menu == Battle.Menu.COIN) then
+            print("Show coin menu: Not implemented")
+        end
+    end,
+
+    close_list = function(self, menu)
+        self.current_list = "none"
+        self.window_state = "command"
+        ui_manager:get_widget("BattleUi.Container.Bottom.List"):set_visible(false)
+    end,
+
+    populate_magic_list = function(self)
+        local magic = self.party[self.current_character_window].magic
+        for pos = 1, 4 do
+            if magic[pos + self.first_list - 1] == nil then
+                ui_manager:get_widget(
+                  "BattleUi.Container.Bottom.List.List" .. tostring(pos)
+                ):set_visible(false)
+            else
+                print("ADDING MAGIC " .. tostring(magic[pos + self.first_list - 1].id))
+                ui_manager:get_widget(
+                  "BattleUi.Container.Bottom.List.List" .. tostring(pos)
+                ):set_text(Game.Attacks[magic[pos + self.first_list - 1].id].name)
+                ui_manager:get_widget(
+                  "BattleUi.Container.Bottom.List.List" .. tostring(pos)
+                ):set_visible(true)
+            end
+        end
+    end,
+
+    populate_summon_list = function(self)
+        local summon = self.party[self.current_character_window].summon
+        for pos = 1, 4 do
+            if summon[pos + self.first_list - 1] == nil then
+                ui_manager:get_widget(
+                  "BattleUi.Container.Bottom.List.List" .. tostring(pos)
+                ):set_visible(false)
+            else
+                print("ADDING SUMMON " .. tostring(summon[pos + self.first_list - 1].id))
+                ui_manager:get_widget(
+                  "BattleUi.Container.Bottom.List.List" .. tostring(pos)
+                ):set_text(Game.Attacks[summon[pos + self.first_list - 1].id].name)
+                ui_manager:get_widget(
+                  "BattleUi.Container.Bottom.List.List" .. tostring(pos)
+                ):set_visible(true)
+            end
+        end
+    end,
+
     --- Loads party character data
     --
     -- Reads materia, calculates stats, computes attacks...
+    -- TODO: Move most of this code to Characters.recalculate_stats()
     load_party_members = function(self)
         for i = 1, Party.MAX do
             if Party[i] ~= nil then
@@ -169,8 +382,10 @@ UiContainer.BattleUi = {
                 -- TODO: Consider battle layouts.
                 -- TODO: Check random seed
                 self.party[i].dead = false
-                self.party[i].atb = math.random(0, 100)
+                self.party[i].atb = math.random(0, 255)
                 self.party[i].command = {}
+                -- Set default "Attack" command.
+                self.party[i].command[1] = 1
                 self.party[i].magic = {}
                 self.party[i].summon = {}
                 self.party[i].cover = 0
@@ -181,9 +396,13 @@ UiContainer.BattleUi = {
                 -- Fill Limit UI bar
                 -- 0   -> 0%
                 -- 255 -> 18%-6
-                local width_percent = math.min(18, 18 * Characters[self.party[i].character].limit.bar / 255)
+                local width_percent = math.min(
+                  18, 18 * Characters[self.party[i].character].limit.bar / 255
+                )
                 local width_fixed = math.max(-6, -6 * width_percent / 18)
-                ui_manager:get_widget("BattleUi.Container.Bottom.Character" .. tostring(i) .. ".Right.LimitFill"):set_width(width_percent, width_fixed)
+                ui_manager:get_widget(
+                  "BattleUi.Container.Bottom.Character" .. tostring(i) .. ".Right.LimitFill"
+                ):set_width(width_percent, width_fixed)
                 -- Read materia
                 local w_magic = false
                 local w_item = false
@@ -195,6 +414,7 @@ UiContainer.BattleUi = {
                     else
                         equip = Characters[Party[i]].armor
                     end
+
                     for m = 1, 8 do -- lop for materia in weapon/armor
                         if equip.materia[m - 1] ~= nil then
                             local materia = Game.Materia[equip.materia[m - 1].id]
@@ -249,19 +469,21 @@ UiContainer.BattleUi = {
                                 end
                                 if command_id ~= nil then
                                     if
-                                      command_id ~= 1 and command_id ~= 20 and command_id ~= 24
-                                      and command_id ~= 25 and command_id ~= 26
-                                      and command_id ~= 26 and #self.party[i].command + 1 == 1
+                                      command_id == 1 or command_id == 20 or command_id == 24
+                                      or command_id == 25 or command_id == 26 or command_id == 26
                                     then
-                                        -- Leave command index 1 available for atgtack and substitutes
-                                        -- 1: Attack, 20: Limit, 24: Slash-all, 25: 2x-Cut, 26: Flash, 27: 4x-Cut
-                                        self.party[i].command[#self.party[i].command + 2] = command_id
-                                    elseif command_id ~= 23 and #self.party[i].command + 1 == 4 then
-                                        -- Leave command index 4 available for item
-                                        self.party[i].command[#self.party[i].command + 2] = command_id
+                                        -- Attack substitutes, index 1
+                                        -- TODO: Handle priorities
+                                        -- 1: Attack, 20: Limit, 24: Slash-all,
+                                        -- 25: 2x-Cut, 26: Flash, 27: 4x-Cut
+                                        self.party[i].command[1] = command_id
                                     else
-                                        self.party[i].command[#self.party[i].command + 1] = command_id
+                                        self.party[i].command[#self.party[i].command + 1]
+                                          = command_id
                                     end
+                                    if command_id == 21 then w_magic = true end
+                                    if command_id == 22 then w_summon = true end
+                                    if command_id == 23 then w_item = true end
                                 end
                             end
                             -- TODO: Independent materia
@@ -269,51 +491,40 @@ UiContainer.BattleUi = {
                         end
                     end
                 end
-                -- Set default "Attack" command.
-                if self.party[i].command[1] == nil then
-                    self.party[i].command[1] = 1
-                end
-                -- Command in slot 4 is reserved for "Item" or "W-Item"
-                if self.party[i].command[4] == nil then
-                    self.party[i].command[4] = 4
-                end
+
+                -- Enable Item command if W-Item is disabled
+                if w_item == false then self.party[i].command[#self.party[i].command + 1] = 4 end
+
                 -- Enable Magic command if any magic is available and W-Magic is disabled.
-                if #self.party[i].magic > 0 then
-                    local w = false
-                    for x = 1, #self.party[i].command do
-                        if self.party[i].command[x] == 21 then
-                            w = true
-                        end
-                    end
-                    if w == false then
-                        if #self.party[i].command + 1 == 1 or #self.party[i].command + 1 == 4 then
-                            -- Leave command index 1 available for attack or substitutes, and
-                            -- index 4 available for item
-                            self.party[i].command[#self.party[i].command + 2] = 2
-                        else
-                            self.party[i].command[#self.party[i].command + 1] = 2
-                        end
-                    end
+                if #self.party[i].magic > 0 and w_magic == false then
+                    self.party[i].command[#self.party[i].command + 1] = 2
                 end
                 -- Enable Summon command if any magic is available and W-Summon is disabled.
-                if #self.party[i].summon > 0 then
-                    local w = false
-                    for x = 1, #self.party[i].command do
-                        if self.party[i].command[x] == 22 then
-                            w = true
-                        end
-                    end
-                    if w == false then
-                        if #self.party[i].command + 1 == 1 or #self.party[i].command + 1 == 4 then
-                            -- Leave command index 1 available for attack or substitutes, and
-                            -- index 4 available for item
-                            self.party[i].command[#self.party[i].command + 2] = 3
-                        else
-                            self.party[i].command[#self.party[i].command + 1] = 3
-                        end
-                    end
+                if #self.party[i].summon > 0 and w_summon == false then
+                    self.party[i].command[#self.party[i].command + 1] = 3
                 end
                 -- TODO: Sort commands/magic/summons
+                table.sort(
+                  self.party[i].command,
+                  function(l, r)
+                      -- Attack substitutes always go first
+                      if l == 1 or l == 20 or l == 24 or l == 25 or l == 26 or l == 26 then
+                          return true
+                      else
+                          return l < r
+                      end
+                  end
+                )
+
+                if #self.party[i].magic > 1 then
+                    print("SORT MAGIC " .. tostring(i))
+                    table.sort(self.party[i].magic, function(l, r) return l.id < r.id end)
+                end
+                if #self.party[i].summon > 1 then
+                    print("SORT SUMMON " .. tostring(i))
+                    table.sort(self.party[i].summon, function(l, r) return l.id < r.id end)
+                end
+                print("ALL SORTED " .. tostring(i))
             end
         end
     end,
@@ -360,28 +571,47 @@ UiContainer.BattleUi = {
         -- Colour the character name
         for c = 1, #self.party do
             if c == party_id then
-                ui_manager:get_widget("BattleUi.Container.Bottom.Character" .. tostring(c) .. ".Left.Name"):set_colour(1, 1, 0)
+                ui_manager:get_widget(
+                  "BattleUi.Container.Bottom.Character" .. tostring(c) .. ".Left.Name"
+                ):set_colour(1, 1, 0)
             else
-                ui_manager:get_widget("BattleUi.Container.Bottom.Character" .. tostring(c) .. ".Left.Name"):set_colour(1, 1, 1)
+                ui_manager:get_widget(
+                  "BattleUi.Container.Bottom.Character" .. tostring(c) .. ".Left.Name"
+                ):set_colour(1, 1, 1)
             end
-            ui_manager:get_widget("BattleUi.Container.Bottom.Character" .. tostring(c) .. ".Left.Name"):set_text(Characters[Party[c]].name)
+            ui_manager:get_widget(
+              "BattleUi.Container.Bottom.Character" .. tostring(c) .. ".Left.Name"
+            ):set_text(Characters[Party[c]].name)
         end
-        ui_manager:get_widget("BattleUi.Container.Bottom.Commands"):set_visible(true)
-        -- Resize the window
-        ui_manager:get_widget("BattleUi.Container.Bottom.Commands"):set_width(
-          40 + 40 * (1 + math.floor(#self.party[party_id].command / 4))
-        )
         -- Show command names
+        ui_manager:get_widget("BattleUi.Container.Bottom.Commands"):set_visible(false)
+        local total_commands = 0
         for c = 1, 12 do
-            ui_manager:get_widget("BattleUi.Container.Bottom.Commands.Cmd" .. tostring(c)):set_visible(false)
-        end
-        for pos, cmd in ipairs(self.party[party_id].command) do
-            ui_manager:get_widget("BattleUi.Container.Bottom.Commands.Cmd" .. tostring(pos)):set_text(Game.Commands[cmd].name)
-            ui_manager:get_widget("BattleUi.Container.Bottom.Commands.Cmd" .. tostring(pos)):set_visible(true)
+            if self.party[party_id].command[c] == nil then
+                ui_manager:get_widget(
+                  "BattleUi.Container.Bottom.Commands.Cmd" .. tostring(c)
+                ):set_visible(false)
+            else
+                ui_manager:get_widget(
+                  "BattleUi.Container.Bottom.Commands.Cmd" .. tostring(c)
+                ):set_text(Game.Commands[self.party[party_id].command[c]].name)
+                ui_manager:get_widget(
+                  "BattleUi.Container.Bottom.Commands.Cmd" .. tostring(c)
+                ):set_visible(true)
+                total_commands = total_commands + 1
+            end
         end
+        -- Resize the window
+        ui_manager:get_widget("BattleUi.Container.Bottom.Commands"):set_visible(false)
+        ui_manager:get_widget("BattleUi.Container.Bottom.Commands"):set_width(
+          60 * (1 + math.floor(total_commands / 4))
+        )
+        ui_manager:get_widget("BattleUi.Container.Bottom.Commands"):set_visible(true)
         -- Set and show cursor
+        ui_manager:get_widget("BattleUi.Container.Bottom.Commands.Cursor"):set_default_animation(
+          "Position" .. tostring(self.cursor_command)
+        )
         ui_manager:get_widget("BattleUi.Container.Bottom.Commands.Cursor"):set_visible(true)
-        ui_manager:get_widget("BattleUi.Container.Bottom.Commands.Cursor"):set_default_animation("Position" .. tostring(self.cursor_command))
         self.window_state = "command"
 
     end,
@@ -413,13 +643,25 @@ UiContainer.BattleUi = {
 
         for c = 1, 3 do
             if Party[c] ~= nil then
-                ui_manager:get_widget("BattleUi.Container.Bottom.Character" .. tostring(c)):set_visible(true)
-                ui_manager:get_widget("BattleUi.Container.Bottom.Character" .. tostring(c) .. ".Left.Name"):set_text(Characters[Party[c]].name)
-                ui_manager:get_widget("BattleUi.Container.Bottom.Character" .. tostring(c) .. ".Right.Hp.HpCurrent"):set_text(space_pad(Characters[Party[c]].stats.hp.current, 5))
-                ui_manager:get_widget("BattleUi.Container.Bottom.Character" .. tostring(c) .. ".Right.Hp.HpMax"):set_text(space_pad(Characters[Party[c]].stats.hp.base, 5))
-                ui_manager:get_widget("BattleUi.Container.Bottom.Character" .. tostring(c) .. ".Right.Mp.MpCurrent"):set_text(space_pad(Characters[Party[c]].stats.mp.current, 5))
+                ui_manager:get_widget(
+                  "BattleUi.Container.Bottom.Character" .. tostring(c)
+                ):set_visible(true)
+                ui_manager:get_widget(
+                  "BattleUi.Container.Bottom.Character" .. tostring(c) .. ".Left.Name"
+                ):set_text(Characters[Party[c]].name)
+                ui_manager:get_widget(
+                  "BattleUi.Container.Bottom.Character" .. tostring(c) .. ".Right.Hp.HpCurrent"
+                ):set_text(space_pad(Characters[Party[c]].stats.hp.current, 5))
+                ui_manager:get_widget(
+                  "BattleUi.Container.Bottom.Character" .. tostring(c) .. ".Right.Hp.HpMax"
+                ):set_text(space_pad(Characters[Party[c]].stats.hp.base, 5))
+                ui_manager:get_widget(
+                  "BattleUi.Container.Bottom.Character" .. tostring(c) .. ".Right.Mp.MpCurrent"
+                ):set_text(space_pad(Characters[Party[c]].stats.mp.current, 5))
             else
-                ui_manager:get_widget("BattleUi.Container.Bottom.Character" .. tostring(c)):set_visible(false)
+                ui_manager:get_widget(
+                  "BattleUi.Container.Bottom.Character" .. tostring(c)
+                ):set_visible(false)
             end
         end
         return 0

+ 1 - 1
data/data/scripts/debug_data.lua

@@ -50,7 +50,7 @@ Characters[0].stats.dex.base = 11
 Characters[0].stats.dex.total = 11
 Characters[0].armor.id = 268
 Characters[0].armor.materia = {
-    [0] = {id = 62, ap = 5340},
+    [0] = {id = 62, ap = 15340},
     [2] = {id = 44, ap = 0, skills = {}}, -- E. Skill
     [3] = {id = 78, ap = 520},
     [4] = {id = 36, ap = 0},

+ 26 - 37
data/data/scripts/menu/pause_menu.lua

@@ -1,45 +1,46 @@
 if UiContainer == nil then UiContainer = {} end
 
-
-
+--- The pause menu
 UiContainer.PauseMenu = {
-    position = 1,
-    position_total = 2,
 
+    --- Cursor position.
+    position = 1,
 
+    --- Maximum cursor position
+    position_total = 2,
 
+    --- Run on creation, does nothing.
     on_start = function( self )
         return 0
     end,
 
-
-
+    --- Handles button events.
+    --
+    -- If the game is paused, it handles the navigation in the pause menu. If the game is not
+    -- paused, it only handles the pause event
     on_button = function( self, button, event )
-        if ui_manager:get_widget( "PauseMenu" ):is_visible() == true then
+        if System:is_paused() == true then
             local menu_cursor = ui_manager:get_widget( "PauseMenu.Menu.Cursor" )
-
-            if button == "Enter" and event == "Press" then
+            if button == "P" and event == "Press" then
+                System:unpause()
+            elseif button == "Enter" and event == "Press" then
                 audio_manager:play_sound("Cursor")
                 script:request_end_sync( Script.UI, "PauseMenu", "hide", 0 )
-                if self.position == 1 then
+                if self.position == 2 then
                     script:request_end_sync( Script.UI, "BeginMenu", "show", 0 )
-                    map( "empty" )
+                    map("empty")
                     MenuSettings.pause_available = false
                 else
                     MenuSettings.available = true
                 end
-            elseif button == "Escape" and event == "Press" then
-                audio_manager:play_sound("Back")
-                script:request_end_sync( Script.UI, "PauseMenu", "hide", 0 )
-                MenuSettings.available = true
-            elseif button == "Right" then
+            elseif button == "Down" then
                 audio_manager:play_sound("Cursor")
                 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 == "Left" then
+            elseif button == "Up" then
                 audio_manager:play_sound("Cursor")
                 self.position = self.position - 1
                 if self.position <= 0 then
@@ -47,34 +48,22 @@ UiContainer.PauseMenu = {
                 end
                 menu_cursor:set_default_animation( "Position" .. self.position )
             end
-        elseif MenuSettings.pause_available == true then
-            if button == "Enter" and event == "Press" then
-                audio_manager:play_sound("Cursor")
-                MenuSettings.available = false
-                script:request_end_sync( Script.UI, "PauseMenu", "show", 0 )
+        elseif System:is_paused() == false then
+            if button == "P" and event == "Press" then
+                System:pause()
             end
         end
 
         return 0
     end,
 
-
-
+    --- Shows the pause menu.
     show = function( self )
-        entity_manager:set_paused( true )
-
-        ui_manager:get_widget( "PauseMenu" ):set_visible( true )
-
-        return 0;
+        ui_manager:get_widget("PauseMenu"):set_visible(true)
     end,
 
-
-
-    hide = function( self )
-        ui_manager:get_widget( "PauseMenu" ):set_visible( false )
-
-        entity_manager:set_paused( false )
-
-        return 0;
+    --- Hides the pause menu
+    hide = function(self)
+        ui_manager:get_widget("PauseMenu"):set_visible(false)
     end,
 }

+ 43 - 0
data/data/scripts/system.lua

@@ -2,6 +2,49 @@ MAX_INVENTORY_SLOTS = 320
 MAX_KEY_SLOTS = 50
 MAX_MATERIA_SLOTS = 200
 
+--- System related parameters and utilities
+System = {
+
+    --- Indicates if the game is paused.
+    paused = false,
+
+    --- Indicates if the menu was available when paused.
+    paused_menu_settings_available = false,
+
+    --- Pauses the game.
+    pause = function(self)
+        if MenuSettings.pause_available == false then
+            do return end
+        end
+        if self.paused == false then
+            self.paused = true
+            entity_manager:set_paused(true)
+            self.paused_menu_settings_available = MenuSettings.available
+            MenuSettings.available = false
+            audio_manager:play_sound("Cursor")
+            UiContainer.PauseMenu:show()
+        end
+    end,
+
+    --- Unpauses the game.
+    unpause = function(self)
+        if self.paused == true then
+            self.paused = false
+            entity_manager:set_paused(false)
+            MenuSettings.available = self.paused_menu_settings_available
+            audio_manager:play_sound("Cursor")
+            UiContainer.PauseMenu:hide()
+        end
+    end,
+
+    --- Checks if the game is paused.
+    --
+    -- @return True if the game is paused, false if not.
+    is_paused = function(self)
+        return self.paused
+    end
+}
+
 Timer = {
     -- TODO When the game clock is implemented, dont use FPS, call this every second.
     FPS = 250,

+ 3 - 4
data/data/texts/english/menu.xml

@@ -29,10 +29,9 @@
     <text name="BeginMenu5"><colour value="0.5 0.5 0.5">DEBUG: Debug Room</colour></text>
     <text name="BeginMenu6"><colour value="0.5 0.5 0.5">DEBUG: Test Battle</colour></text>
 
-    <text name="PauseMenuHeader">Paused</text>
-    <text name="PauseMenuText">Are you want to quit?</text>
-    <text name="PauseMenuYes">Yes</text>
-    <text name="PauseMenuNo">No</text>
+    <text name="PauseMenuHeader"><colour value="0.7 0.7 0.7">PAUSE</colour></text>
+    <text name="PauseMenuResume">Resume</text>
+    <text name="PauseMenuQuit">Quit</text>
 
     <text name="MainMenuGil">Gil</text>
     <text name="MainMenuGilAmount">123456789</text>

+ 20 - 3
src/installer/KernelDataInstaller.cpp

@@ -38,7 +38,7 @@ void KernelDataInstaller::ReadPrices(){
 
 int KernelDataInstaller::ReadCommands(){
 
-    // Empty the item list
+    // Empty the command list
     commands_.clear();
 
     File command_file = kernel_.ExtractGZip(KERNEL_COMMAND_DATA);
@@ -98,9 +98,10 @@ int KernelDataInstaller::ReadCommands(){
         }
         boost::replace_all(data.description, "\"", "'");
 
-        // Read data from the item data section.
+        // Read data from the command       m, data section.
         data.cursor_action = command_file.readU8();
         data.target_raw = command_file.readU8();
+        data.unknown = command_file.readU16LE();
         data.camera_single = command_file.readU16LE();
         data.camera_multiple = command_file.readU16LE();
 
@@ -122,8 +123,23 @@ int KernelDataInstaller::ReadCommands(){
         else data.target.all_rows = false;
         if (data.target_raw == (data.target_raw | 0x80)) data.target.random = true;
         else data.target.random = false;
+        switch (data.cursor_action){
+            case NONE: data.menu = "Battle.Menu.NONE"; break;
+            case MAGIC: data.menu = "Battle.Menu.MAGIC"; break;
+            case SUMMON: data.menu = "Battle.Menu.SUMMON"; break;
+            case ITEM: data.menu = "Battle.Menu.ITEM"; break;
+            case E_SKILL: data.menu = "Battle.Menu.E_SKILL"; break;
+            case THROW: data.menu = "Battle.Menu.THROW"; break;
+            case LIMIT: data.menu = "Battle.Menu.LIMIT"; break;
+            case TARGET: data.menu = "Battle.Menu.TARGET"; break;
+            case W_MAGIC: data.menu = "Battle.Menu.W_MAGIC"; break;
+            case W_SUMMON: data.menu = "Battle.Menu.W_SUMMON"; break;
+            case W_ITEM: data.menu = "Battle.Menu.W_ITEM"; break;
+            case COIN: data.menu = "Battle.Menu.COIN"; break;
+            default: data.menu = "Battle.Menu.NONE";
+        }
 
-        // Add to the list of items.
+        // Add to the list of commands.
         if (data.name != ""){
             command_names_[i] = data.name;
             commands_.push_back(data);
@@ -142,6 +158,7 @@ void KernelDataInstaller::WriteCommands(std::string file_name){
         file << "Game.Commands[" << command.id << "] = {\n"
           << "    name = \"" << command.name << "\",\n"
           << "    description = \"" << command.description << "\",\n"
+          << "    menu = " << command.menu << ",\n"
           << "    camera = {single = " << command.camera_single << ", "
           << "multiple = " << command.camera_multiple << "}\n}\n";
     }

+ 75 - 0
src/installer/KernelDataInstaller.h

@@ -734,6 +734,74 @@ class KernelDataInstaller{
             TOGGLE = 2
         };
 
+        /**
+         * Menus that must be shown when selecting a command.
+         */
+        enum COMMAND_MENU{
+
+            /**
+             * No menu, execute command directly, without target selection.
+             */
+            NONE = 0,
+
+            /**
+             * Show magic menu.
+             */
+            MAGIC = 1,
+
+            /**
+             * Show summon menu.
+             */
+            SUMMON = 2,
+
+            /**
+             * Show item menu.
+             */
+            ITEM = 3,
+
+            /**
+             * Show enemy skill menu.
+             */
+            E_SKILL = 4,
+
+            /**
+             * Show throw menu.
+             */
+            THROW = 5,
+
+            /**
+             * Show limit menu.
+             */
+            LIMIT = 6,
+
+            /**
+             * No menu, skip to target selection.
+             */
+            TARGET = 7,
+
+            /**
+             * Show W-Magic menu
+             */
+            W_MAGIC = 8,
+
+            /**
+             * Show W-Summon menu
+             */
+            W_SUMMON = 9,
+
+            /**
+             * Show W-Item menu
+             */
+            W_ITEM = 10,
+
+            /**
+             * Show Coin menu
+             */
+            COIN = 11,
+
+
+        };
+
         struct StatusEffect{
 
             /**
@@ -821,6 +889,13 @@ class KernelDataInstaller{
              */
             Target target;
 
+            /**
+             * Menu name
+             *
+             * Derived from {@see cursor_action}.
+             */
+            std::string menu;
+
         };
 
         /**