Bläddra i källkod

Saving functionality implemented.

It has some caveats:
 - The save menu doesn't exist yet, so the game can also be saved from the console.
 - Some of the saved data is not actual data, but hardcoded values.
 - Loading is not yet implemented.

Also:
 - Fixed dialog width issues.
 - Some simplifications in Lua scripts.
 - Fixed some compiler warnings, mostly Ogre-related.
Iñigo Valentin 3 år sedan
förälder
incheckning
84c31bdbb7
35 ändrade filer med 2389 tillägg och 204 borttagningar
  1. 6 1
      V-Gears-Installer/include/VGearsUtility.h
  2. 14 6
      V-Gears-Installer/include/common/OgreBase.h
  3. 2 2
      V-Gears-Installer/include/decompiler/field/FieldCodeGenerator.h
  4. 1 1
      V-Gears-Installer/src/FieldDataInstaller.cpp
  5. 18 16
      V-Gears-Installer/src/KernelDataInstaller.cpp
  6. 2 2
      V-Gears-Installer/src/common/DrawSkeleton.cpp
  7. 3 3
      V-Gears-Installer/src/common/OgreBase.cpp
  8. 2 2
      V-Gears-Installer/src/common/Surface.cpp
  9. 1 1
      V-Gears-Installer/src/decompiler/field/instruction/FieldModuleInstruction.cpp
  10. 1 1
      V-Gears-Installer/src/decompiler/field/instruction/FieldPartyInstruction.cpp
  11. 4 0
      V-Gears/CMakeLists.txt
  12. 879 0
      V-Gears/include/core/Savemap.h
  13. 391 0
      V-Gears/include/core/SavemapManager.h
  14. 134 1
      V-Gears/include/core/ScriptManagerBinds.h
  15. 4 3
      V-Gears/src/Main.cpp
  16. 6 4
      V-Gears/src/core/DialogsManager.cpp
  17. 0 1
      V-Gears/src/core/EntityManager.cpp
  18. 439 0
      V-Gears/src/core/Savemap.cpp
  19. 246 0
      V-Gears/src/core/SavemapManager.cpp
  20. 2 0
      V-Gears/src/core/UiTextArea.cpp
  21. 3 3
      V-Gears/src/core/XmlTextFile.cpp
  22. 6 6
      data/data/fonts/ffvii_en.xml
  23. 1 0
      data/data/scripts.xml
  24. 43 43
      data/data/scripts/battle.lua
  25. 7 7
      data/data/scripts/battle_ai.lua
  26. 12 30
      data/data/scripts/data.lua
  27. 7 6
      data/data/scripts/field.lua
  28. 10 9
      data/data/scripts/menu/begin_menu.lua
  29. 4 4
      data/data/scripts/menu/item_menu.lua
  30. 9 9
      data/data/scripts/menu/main_menu.lua
  31. 2 2
      data/data/scripts/menu/name_menu.lua
  32. 5 5
      data/data/scripts/menu/pause_menu.lua
  33. 86 0
      data/data/scripts/save.lua
  34. 38 35
      data/data/scripts/system.lua
  35. 1 1
      doc/STYLE-LUA.md

+ 6 - 1
V-Gears-Installer/include/VGearsUtility.h

@@ -40,7 +40,12 @@ namespace VGears{
              */
             virtual ~Utility();
 
-            virtual Ogre::Camera* GetCamera( void );
+            /**
+             * Retrieves the camera.
+             *
+             * @return The camera.
+             */
+            virtual Ogre::Camera* GetCamera(void);
 
         protected:
 

+ 14 - 6
V-Gears-Installer/include/common/OgreBase.h

@@ -158,24 +158,32 @@ class DisplayFrameListener :
             }
             if (keyboard_->isKeyDown(OIS::KC_A)){
                 assert(camera_);
-                camera_->moveRelative(Ogre::Vector3(-0.005f, 0.0f, 0.0f));
+                camera_->getParentSceneNode()->translate(
+                  Ogre::Vector3(-0.005f, 0.0f, 0.0f), Ogre::Node::TS_LOCAL
+                );
             }
             if (keyboard_->isKeyDown(OIS::KC_D)){
                 assert(camera_);
-                camera_->moveRelative(Ogre::Vector3(0.005f, 0.0f, 0.0f));
+                camera_->getParentSceneNode()->translate(
+                  Ogre::Vector3(0.005f, 0.0f, 0.0f), Ogre::Node::TS_LOCAL
+                );
             }
             if (keyboard_->isKeyDown(OIS::KC_W)){
                 assert(camera_);
-                camera_->moveRelative(Ogre::Vector3(0.0f, 0.0f, -0.005f));
+                camera_->getParentSceneNode()->translate(
+                  Ogre::Vector3(0.0f, 0.0f, -0.005f), Ogre::Node::TS_LOCAL
+                );
             }
             if (keyboard_->isKeyDown(OIS::KC_S)){
                 assert(camera_);
-                camera_->moveRelative(Ogre::Vector3(0.0f, 0.0f,  0.005f));
+                camera_->getParentSceneNode()->translate(
+                  Ogre::Vector3(0.0f, 0.0f,  0.005f), Ogre::Node::TS_LOCAL
+                );
             }
             if (mouse_rotate_ == true){
                 assert(camera_);
-                camera_->yaw(Ogre::Degree(-mouse_move_x_ * 0.13f));
-                camera_->pitch(Ogre::Degree(-mouse_move_y_ * 0.13f));
+                camera_->getParentSceneNode()->yaw(Ogre::Degree(-mouse_move_x_ * 0.13f));
+                camera_->getParentSceneNode()->pitch(Ogre::Degree(-mouse_move_y_ * 0.13f));
                 mouse_move_x_ = 0;
                 mouse_move_y_ = 0;
             }

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

@@ -273,7 +273,7 @@ class FieldCodeGenerator : public CodeGenerator{
                         );
                         if (friendly_name.empty())
                             return (boost::format("Banks[%1%][%2%]") % bank % address).str();
-                        return (boost::format("FFVII.Data.%1%") % friendly_name).str();
+                        return (boost::format("Data.%1%") % friendly_name).str();
                     }
                 case 5:
                 case 6:
@@ -282,7 +282,7 @@ class FieldCodeGenerator : public CodeGenerator{
                         const  auto friendly_name = formatter.GetFriendlyVarName(bank, address);
                         if (friendly_name.empty())
                             return (boost::format("Banks[%1%][%2%]") % bank % address).str();
-                        return "FFVII.Data." + friendly_name;
+                        return "Data." + friendly_name;
                     }
                 default:
                     return (

+ 1 - 1
V-Gears-Installer/src/FieldDataInstaller.cpp

@@ -50,7 +50,7 @@ std::string FieldDataInstaller::CreateGateWayScript(
       "    end,\n\n"
       "    on_near = function(self, entity)\n"
       "        if entity == \"Cloud\" then\n"
-      "            if not FFVII.Data.DisableGateways then\n"
+      "            if not Data.DisableGateways then\n"
       "                load_field_map_request(\""
       + target_map_name + "\", \"" + source_spawn_point_name + "\")\n"
       "            end\n"

+ 18 - 16
V-Gears-Installer/src/KernelDataInstaller.cpp

@@ -2260,9 +2260,11 @@ void KernelDataInstaller::WriteInitialSaveMap(std::string file_name){
           << "Characters[" << c << "] = {\n"
           << "    char_id = " << static_cast<int>(character.identifier) << ",\n"
           << "    name = \"" << character.name << "\",\n"
+          << "    enabled = false,\n"
+          << "    locked = false,\n"
           << "    level = " << static_cast<int>(character.level) << ",\n"
           << "    kills = " << character.kills << ",\n"
-          << "    back_row = " << character.back_row << ",\n"
+          << "    back_row = " << (character.back_row ? "true" : "false") << ",\n"
           << "    exp = " << character.exp << ",\n"
           << "    exp_to_next = " << character.exp_to_next << ",\n"
           << "    stats = {\n"
@@ -2288,26 +2290,26 @@ void KernelDataInstaller::WriteInitialSaveMap(std::string file_name){
           << ", max = " << character.max_hp << "},\n"
           << "        mp = {current = " << character.mp  << ", base = " << character.base_mp
           << ", max = " << character.max_mp << "},\n"
-          << "        atk = 0,"
-          << "        acc = 0,"
-          << "        def = 0,"
-          << "        eva = 0,"
-          << "        matk = 0,"
-          << "        mdef = 0,"
-          << "        meva = 0"
+          << "        atk = 0,\n"
+          << "        acc = 0,\n"
+          << "        def = 0,\n"
+          << "        eva = 0,\n"
+          << "        matk = 0,\n"
+          << "        mdef = 0,\n"
+          << "        meva = 0\n"
           << "    },\n"
           << "    limit = {\n"
           << "        current = " << static_cast<int>(character.limit_level) << ",\n"
           << "        bar = " << static_cast<int>(character.limit_bar) << ",\n"
           << "        learned = {\n"
-          << "            [1] = {[1] = " << character.limits_learned[0][0] << ", [2] = "
-          << character.limits_learned[0][1] << "},\n"
-          << "            [2] = {[1] = " << character.limits_learned[1][0] << ", [2] = "
-          << character.limits_learned[1][1] << "},\n"
-          << "            [3] = {[1] = " << character.limits_learned[2][0] << ", [2] = "
-          << character.limits_learned[2][1] << "},\n"
-          << "            [4] = {[1] = " << character.limits_learned[3][0] << ", [2] = "
-          << character.limits_learned[3][1] << "}\n"
+          << "            [1] = {[1] = " << (character.limits_learned[0][0] == 1 ? "true" : "false")
+          << ", [2] = " << (character.limits_learned[0][1] == 1 ? "true" : "false") << "},\n"
+          << "            [2] = {[1] = " << (character.limits_learned[1][0] == 1 ? "true" : "false")
+          << ", [2] = " << (character.limits_learned[1][1] == 1 ? "true" : "false") << "},\n"
+          << "            [3] = {[1] = " << (character.limits_learned[2][0] == 1 ? "true" : "false")
+          << ", [2] = " << (character.limits_learned[2][1] == 1 ? "true" : "false") << "},\n"
+          << "            [4] = {[1] = " << (character.limits_learned[3][0] == 1 ? "true" : "false")
+          << ", [2] = " << (character.limits_learned[3][1] == 1 ? "true" : "false") << "}\n"
           << "        },\n"
           << "        uses = {[1] = " << character.limit_uses[0] << ", [2] = "
           << character.limit_uses[1] << ", [3] = " << character.limit_uses[2] << ", [4] = 0}\n"

+ 2 - 2
V-Gears-Installer/src/common/DrawSkeleton.cpp

@@ -118,8 +118,8 @@ void DrawBone(const Skeleton& skeleton, int parent_index, Ogre::RGBA* colours){
             Ogre::ColourValue colour2 = Ogre::ColourValue(1, 1, 0);
             Ogre::RenderSystem* rs
               = Ogre::Root::getSingleton().getRenderSystem();
-            rs->convertColourValue(colour1, colours + cur_index + 0);
-            rs->convertColourValue(colour2, colours + cur_index + 1);
+            colours[cur_index] = colour1.getAsBYTE();
+            colours[cur_index + 1] = colour2.getAsBYTE();
             index_data[cur_index + 0] = cur_index + 0;
             index_data[cur_index + 1] = cur_index + 1;
             Ogre::VertexBoneAssignment vba;

+ 3 - 3
V-Gears-Installer/src/common/OgreBase.cpp

@@ -107,11 +107,11 @@ void InitializeOgreBase(const Ogre::String& name){
     directional_light->setType(Ogre::Light::LT_DIRECTIONAL);
     directional_light->setDiffuseColour(Ogre::ColourValue(0.5, 0.5, 0.5));
     directional_light->setSpecularColour(Ogre::ColourValue(0.5, 0.5, 0.5));
-    directional_light->setDirection(Ogre::Vector3(0, 0, -1));
+    directional_light->getParentSceneNode()->setDirection(Ogre::Vector3(0, 0, -1));
     camera = scene_manager->createCamera("Camera");
     camera->setNearClipDistance(0.01f);
-    camera->setPosition(0, 5, 10);
-    camera->lookAt(0, 0, 0);
+    camera->getParentSceneNode()->setPosition(0, 5, 10);
+    camera->getParentSceneNode()->lookAt(Ogre::Vector3(0, 0, 0), Ogre::Node::TS_LOCAL);
     viewport = window->addViewport(camera);
     viewport->setBackgroundColour(Ogre::ColourValue(0.0f, 0.4f, 0.0f));
     camera->setAspectRatio(

+ 2 - 2
V-Gears-Installer/src/common/Surface.cpp

@@ -19,9 +19,9 @@
 #include <windows.h>
 #endif
 
-Surface::Surface(): pixels(NULL), width(0), height(0){}
+Surface::Surface(): width(0), height(0){}
 
-Surface::Surface(const Surface &copy):pixels(NULL), width(copy.width), height(copy.height){
+Surface::Surface(const Surface &copy): width(copy.width), height(copy.height){
   if (width && height) pixels = copy.pixels;
 }
 

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

@@ -84,7 +84,7 @@ void FieldModuleInstruction::ProcessInst(
         case OPCODES::MPJPO:
             // Gateway function will do nothing if this is set to true
             code_gen->AddOutputLine(
-              std::string("FFVII.Data.DisableGateways=")
+              std::string("Data.DisableGateways=")
               + (params_[0]->GetUnsigned() ? "true" : "false")
             );
             break;

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

@@ -136,7 +136,7 @@ void FieldPartyInstruction::ProcessPRTYE(CodeGenerator* code_gen){
         else chars[i] = "nil";
     }
     code_gen->AddOutputLine((
-      boost::format("FFVII.set_party(%1%, %2%, %3%)") % chars[0] % chars[1] % chars[2]
+      boost::format("set_party(%1%, %2%, %3%)") % chars[0] % chars[1] % chars[2]
     ).str());
 }
 

+ 4 - 0
V-Gears/CMakeLists.txt

@@ -73,6 +73,8 @@ set(HEADER_FILES
     include/core/InputManagerCommands.h
     include/core/Logger.h
     include/core/Module.h
+    include/core/Savemap.h
+    include/core/SavemapManager.h
     include/core/ScriptManager.h
     include/core/ScriptManagerBinds.h
     include/core/ScriptManagerCommands.h
@@ -262,6 +264,8 @@ set(SOURCE_FILES
     src/core/EntityTrigger.cpp
     src/core/GameFrameListener.cpp
     src/core/InputManager.cpp
+    src/core/Savemap.cpp
+    src/core/SavemapManager.cpp
     src/core/ScriptManager.cpp
     src/core/Timer.cpp
     src/core/UiAnimation.cpp

+ 879 - 0
V-Gears/include/core/Savemap.h

@@ -0,0 +1,879 @@
+/*
+ * Copyright (C) 2022 The V-Gears Team
+ *
+ * This file is part of V-Gears
+ *
+ * V-Gears is free software: you can redistribute it and/or modify it under
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, version 3.0 (GPLv3) of the License.
+ *
+ * V-Gears is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#pragma once
+
+#include <string>
+#include "common/TypeDefine.h"
+
+/**
+ * A savemap.
+ */
+class Savemap{
+
+    public:
+
+        /**
+         * Constructor. Initializes an empty savemap.
+         */
+        Savemap();
+
+        /**
+         * Destructor.
+         */
+        ~Savemap();
+
+        void operator = (const Savemap &to_copy);
+
+        /**
+         * Indicates if the savemap is empty or contains actual data.
+         *
+         * @return True if the savemap is empty, false otherwise.
+         */
+        bool IsEmpty();
+
+        /**
+         * Reads the savemap data from a file.
+         */
+        void Read(std::string file_name);
+
+        /**
+         * Writes the savemap to a file.
+         */
+        void Write(std::string file_name);
+
+        /**
+         * Retrieves a control string for a savemap.
+         *
+         * Not a checksum, it does not depend on the actual data. It never mutates, is used to
+         * differenciate savemaps when asking to override.
+         */
+        std::string GetControlKey() const;
+
+        /**
+         * Sets the control string of the savemap.
+         *
+         * @param[in] control The control string.
+         */
+        void SetControlKey(const std::string control);
+
+        /**
+         * Sets the window colours in the current savemap.
+         *
+         * @param[in] t_l_r Top-left corner, red component.
+         * @param[in] t_l_r Top-left corner, green component.
+         * @param[in] t_l_r Top-left corner, blue component.
+         * @param[in] t_r_r Top-right corner, red component.
+         * @param[in] t_r_r Top-right corner, green component.
+         * @param[in] t_r_r Top-right corner, blue component.
+         * @param[in] b_r_r Bottom-right corner, red component.
+         * @param[in] b_r_r Bottom-right corner, green component.
+         * @param[in] b_r_r Bottom-right corner, blue component.
+         * @param[in] b_l_r Bottom-left corner, red component.
+         * @param[in] b_l_r Bottom-left corner, green component.
+         * @param[in] b_l_r Bottom-left corner, blue component.
+         */
+        void SetWindowColours(
+          const unsigned int t_l_r, const unsigned int t_l_g, const unsigned int t_l_b,
+          const unsigned int t_r_r, const unsigned int t_r_g, const unsigned int t_r_b,
+          const unsigned int b_r_r, const unsigned int b_r_g, const unsigned int b_r_b,
+          const unsigned int b_l_r, const unsigned int b_l_g, const unsigned int b_l_b
+        );
+
+        /**
+         * Sets the money of the savemap.
+         *
+         * @param[in] money The current money.
+         */
+        void SetMoney(const unsigned int money);
+
+        /**
+         * Sets the total playtime of the savemap.
+         *
+         * @param[in] seconds The total playtime, in seconds.
+         */
+        void SetGameTime(const unsigned int seconds);
+
+        /**
+         * Sets the time of the curent timer.
+         *
+         * @param[in] seconds The current countdown timer time, in seconds.
+         */
+        void SetCountdownTime(const unsigned int seconds);
+
+        /**
+         * Sets the current field of the savemap.
+         *
+         * If set to an empty string, it will be assumed that it was saved in the world map.
+         *
+         * @param[in] field Field map ID, or empty for the world map.
+         */
+        void SetField(const std::string field);
+
+        /**
+         * Marks a key item as owned or non-owned in the savemap.
+         *
+         * @param[in] item Key item id.
+         * @param[in] owned True to mark it as owned, false to mark is as non-owned.
+         */
+        void SetKeyItem(const unsigned int item, const bool owned);
+
+        /**
+         * Sets the current party in the savemap.
+         *
+         * @param[in] member_1 The first party member ID. -1 to leave empty.
+         * @param[in] member_2 The second party member ID. -1 to leave empty.
+         * @param[in] member_3 The third party member ID. -1 to leave empty.
+         */
+        void SetParty(const int member_1, const int member_2, const int member_3);
+
+        /**
+         * Sets an item in a inventory slot in the savemap.
+         *
+         * @param[in] slot Inventory slot.
+         * @param[in] id Item ID.
+         * @param[in] quantity Item quantity. Set to 0 to mark as no item in the specified slot.
+         */
+        void SetItem(const unsigned int slot, const unsigned int id, const unsigned int quantity);
+
+        /**
+         * Sets a materia in a materia inventory slot in the savemap.
+         *
+         * For Enemy Skill materia, don't use this, use {@see SetESkillMateria}.
+         *
+         * @param[in] slot Materia inventory slot.
+         * @param[in] id Materia ID. Set to -1 to mark it as an empty slot.
+         * @param[in] ap Total AP of the materia.
+         */
+        void SetMateria(const unsigned int slot, const int id, const unsigned int ap);
+
+        /**
+         * Sets a skill as learned in an Enemy Skill materia inventory slot in the savemap.
+         *
+         * Calling this will make the materia in the slot to be an Enemy Skill materia and set the
+         * AP to 0. To delete it, call {@see SetMateria} with the same slot and id -1.
+         *
+         * @param[in] slot Materia inventory slot.
+         * @param[in] skill Enemy Skill ID (sequential, from 0).
+         * @param[in] learned TRue to mark as learned, false to mark as not learned.
+         */
+        void SetESkillMateria(const unsigned slot, const unsigned int skill, const bool learned);
+
+        /**
+         * Sets a materia in a materia inventory slot in the savemap.
+         *
+         * For Enemy Skill materia, don't use this, use {@see SetEnemySkillMateriaStash}.
+         *
+         * @param[in] slot Materia inventory slot.
+         * @param[in] id Materia ID. Set to -1 to mark it as an empty slot.
+         * @param[in] ap Total AP of the materia.
+         */
+        void SetMateriaStash(const unsigned int slot, const int id, const unsigned int ap);
+
+        /**
+         * Sets a skill as learned in an Enemy Skill materia stash slot in the savemap.
+         *
+         * Calling this will make the materia in the slot to be an Enemy Skill materia and set the
+         * AP to 0. To delete it, call {@see SetMateriaStash} with the same slot and id -1.
+         *
+         * @param[in] slot Materia inventory slot.
+         * @param[in] skill Enemy Skill ID (sequential, from 0)
+         * @param[in] learned TRue to mark as learned, false to mark as not learned.
+         */
+        void SetESkillMateriaStash(
+          const unsigned slot, const unsigned int skill, const bool learned
+        );
+
+        /**
+         * Sets the current location in the savemap.
+         *
+         * @param[in] x X coordinate.
+         * @param[in] y Y coordinate.
+         * @param[in] z Z coordinate. It's optional, set it to -1 to ignore it.
+         * @param[in] triangle Walkmesh triangle ID.
+         * @param[in] angle Facing direction.
+         */
+        void SetLocation(
+          const unsigned int x, const unsigned int y, const int z,
+          const unsigned int triangle, const int angle
+        );
+
+        /**
+         * Sets a setting value in the savemap.
+         *
+         * @param[in] key Setting key.
+         * @param[in] value Setting value.
+         * @todo Does nothing, implement when settings are working.
+         */
+        void SetSetting(const unsigned int key, const unsigned int value);
+
+        /**
+         * Sets a character basic information in the savemap.
+         *
+         * @param[in] id Character ID.
+         * @param[in] char_id Character identifier.
+         * @param[in] name Character name.
+         * @param[in] enabled If enabled in the PHS.
+         * @param[in] locked If locked in or out the current party.
+         * @param[in] level Character level.
+         * @param[in] kills Total kills.
+         * @param[in] back_row If the character is in the back row.
+         * @param[in] exp Total experience.
+         * @param[in] exp_to_next Experience to reach next level.
+         * @param[in] limit_level Currently selected limit level.
+         * @param[in] limit_bar Current limit bar fill status
+         * @param[in] weapon Equipped weapon ID.
+         * @param[in] armor Equipped armor ID.
+         * @param[in] accessory Equipped accessory ID. -1 if none equipped.
+         */
+        void SetCharacterInfo(
+          const unsigned int id, const int char_id, const std::string name,
+          const bool enabled, const bool locked,
+          const unsigned int level, const unsigned int kills,
+          const bool back_row, const unsigned int exp, const unsigned int exp_to_next,
+          const unsigned int limit_level, const unsigned int limit_bar,
+          const unsigned int weapon, const unsigned int armor, const int accessory
+        );
+
+        /**
+         * Sets a character stat values in the savemap.
+         *
+         * @param[in] id Character ID.
+         * @param[in] stat Stat ID. See {@see STAT}.
+         * @param[in] base Base value of the stat.
+         * @param[in] extra Extra value of the stat. For STR, VIT, MAG, SPR, DEX and LCK, it means
+         * the increment by means of sources. For HP and MP, the current value.
+         */
+        void SetCharacterStat(
+          const unsigned int id, const unsigned int stat,
+          const unsigned int base, const unsigned int extra
+        );
+
+        /**
+         * Sets a character limit learned status in the savemap.
+         *
+         * @param[in] id Character ID.
+         * @param[in] level Limit level.
+         * @param[in] technique Limit level technique ID.
+         * @param[in] learned TRue to mark as learned, false to mark as non-learned.
+         * @param[in] uses Times the technique has been used.
+         */
+        void SetCharacterLimitLearned(
+          const unsigned int id, const unsigned int level,
+          const unsigned int technique, const bool learned, const unsigned int uses
+        );
+
+        /**
+         * Sets a materia in a character weapon or armor slot in the savemap.
+         *
+         * For Enemy Skill materia, don't use this, use {@see SetCharacterESkillMateria}.
+         *
+         * @param[in] id Character ID.
+         * @param[in] weapon True to set the materia in the weapon, false to set it in the armor.
+         * @param[in] slot Weapon or armor slot.
+         * @param[in] materia Materia ID. Set to -1 to mark it as an empty slot.
+         * @param[in] ap Total AP of the materia.
+         */
+        void SetCharacterMateria(
+          const unsigned int id, const bool weapon, const unsigned int slot,
+          const int materia, const unsigned int ap
+        );
+
+        /**
+         * Sets a skill as learned in a character's Enemy Skill materia in the savemap.
+         *
+         * Calling this will make the materia to be an Enemy Skill materia and set the AP to 0. To
+         * delete it, call {@see SetCharacterMateria} with the same character ID, weapon/armor and
+         * slot and id -1.
+         *
+         * @param[in] id Character ID.
+         * @param[in] weapon True to set the materia in the weapon, false to set it in the armor.
+         * @param[in] slot Weapon or armor slot.
+         * @param[in] skill Enemy Skill ID (sequential, from 0).
+         * @param[in] learned TRue to mark as learned, false to mark as not learned.
+         */
+        void SetCharacterESkillMateria(
+          const unsigned int id, const bool weapon, const unsigned int slot,
+          const unsigned int skill, const bool learned
+        );
+
+        /**
+         * Adds or removes a status to a character in the savemap.
+         *
+         * @param[in] id Character ID.
+         * @param[in] status ID of the status.
+         * @param[in] inflicted True to add the status, false to remove it.
+         */
+        void SetCharacterStatus(
+          const unsigned int id, const unsigned int status, const bool inflicted
+        );
+
+        /**
+         * Sets data in a data bank.
+         *
+         * @param[in] bank Data bank index.
+         * @param[in] address Address in the data bank.
+         * @param[in] value The value to store.
+         */
+        void SetData(const unsigned int bank, const unsigned int address, const int value);
+
+        /**
+         * Character basic stats.
+         */
+        enum class STAT{
+
+            /**
+             * Strength stat.
+             */
+            STR,
+
+            /**
+             * Vtatlity stat.
+             */
+            VIT,
+
+            /**
+             * Magic stat.
+             */
+            MAG,
+
+            /**
+             * Spirit stat.
+             */
+            SPR,
+
+            /**
+             * Dexterity stat.
+             */
+            DEX,
+
+            /**
+             * Luck stat.
+             */
+            LCK,
+
+            /**
+             * HP stat.
+             */
+            HP,
+
+            /**
+             * MP stat.
+             */
+            MP
+        };
+
+    private:
+
+        /**
+         * Maximum number of characters.
+         */
+        static const unsigned int MAX_CHARACTERS = 11;
+
+        /**
+         * Maximum number of inventory slots.
+         */
+        static const unsigned int MAX_ITEM_SLOTS = 500;
+
+        /**
+         * Maximum number of inventory slots for key items.
+         */
+        static const unsigned int MAX_KEY_ITEM_SLOTS = 100;
+
+        /**
+         * Maximum number of materia slots.
+         */
+        static const unsigned int MAX_MATERIA_SLOTS = 500;
+
+        /**
+         * Maximum number of materia slots in the stash.
+         */
+        static const unsigned int MAX_STASH_SLOTS = 500;
+
+        /**
+         * Maximum number of materia slots in a weapon or armor.
+         */
+        static const unsigned int MAX_EQUIP_SLOTS = 10;
+
+        /**
+         * Maximum number of skills in an Enemy Skill materia.
+         */
+        static const unsigned int MAX_ENEMY_SKILLS = 32;
+
+        /**
+         * Maximum limit level.
+         */
+        static const unsigned int MAX_LIMIT_LEVELS = 4;
+
+        /**
+         * Maximum limit techniques per level.
+         */
+        static const unsigned int MAX_LIMIT_TECHNIQUES = 4;
+
+        /**
+         * Level at which the limit level is full.
+         */
+        static const unsigned int MAX_LIMIT_BAR = 255;
+
+        /**
+         * Maximum number of party member.
+         */
+        static const unsigned int MAX_PARTY_MEMBERS = 3;
+
+        /**
+         * Number of data banks.
+         */
+        static const unsigned int BANK_COUNT = 15;
+
+        /**
+         * Number of addresses in each data bank.
+         */
+        static const unsigned int BANK_ADDRESS_COUNT = 255;
+
+        /**
+         * MAx colour component value.
+         */
+        static const unsigned int MAX_COLOUR = 254;
+
+
+        /**
+         * Game settings.
+         */
+        struct Settings{
+
+            /**
+             * Magic sorting methods.
+             */
+            enum class MagicOrder{
+
+                /**
+                 * Restore-Attack-Indirect.
+                 */
+                RAI,
+
+                /**
+                 * Restore-Indirect-Attack.
+                 */
+                RIA,
+
+                /**
+                 * Attack-Indirect-Restore.
+                 */
+                AIR,
+
+                /**
+                 * Attack-Restore-Indirect.
+                 */
+                ARI,
+
+                /**
+                 * Indirect-Restore-Attack.
+                 */
+                IRA,
+
+                /**
+                 * Indirect-Attack-Restore.
+                 */
+                IAR,
+            };
+
+            /**
+             * ATB modes.
+             */
+            enum class AtbMode{
+
+                /**
+                 * Active battle.
+                 */
+                ACTIVE,
+
+                /**
+                 * Mixed mode.
+                 */
+                RECOMMENDED,
+
+                /**
+                 * Paused mode.
+                 */
+                WAIT
+            };
+
+            /**
+             * Battle speed.
+             */
+            u8 battle_speed;
+
+            /**
+             * Battle message speed.
+             */
+            u8 battle_msg_speed;
+
+            /**
+             * Field message speed
+             */
+            u8 msg_speed;
+
+            /**
+             * Magic order.
+             */
+            MagicOrder order;
+
+            /**
+             * ATB mode.
+             */
+            AtbMode atb_mode;
+
+            /**
+             * Indicates if the battle help window is to be shown.
+             */
+            bool batle_help;
+
+            // TODO: Key mappings, when implemented.
+        };
+
+        /**
+         * Location information.
+         *
+         * Used for fields and world map.
+         */
+        struct Location{
+
+            /**
+             * X coordinate.
+             */
+            u16 x;
+
+            /**
+             * Y coordinate.
+             */
+            u16 y;
+
+            /**
+             * Z coordinate.
+             *
+             * Usually unused.
+             */
+            u16 z;
+
+            /**
+             * Triangle in the walkmesh;
+             */
+            u16 triangle;
+
+            /**
+             * Facing direction.
+             */
+            u16 angle;
+        };
+
+        /**
+         * A materia structure.
+         */
+        struct Materia{
+
+            /**
+             * Materia ID.
+             */
+            int id;
+
+            /**
+             * Total AP in the materia.
+             */
+            u32 ap;
+
+            /**
+             * Learned enemy skills (only for Enemy Skill materia).
+             */
+            bool enemy_skill_learned[MAX_ENEMY_SKILLS];
+        };
+
+        /**
+         * Item structure.
+         */
+        struct Item{
+
+            /**
+             * Item ID.
+             */
+            int id;
+
+            /**
+             * Item stock.
+             */
+            u16 quantity;
+        };
+
+        /**
+         * A character entri in the savemap.
+         */
+        struct Character{
+
+            /**
+             * A character stat.
+             */
+            struct Stat{
+
+                /**
+                 * Stat base value.
+                 */
+                u32 base;
+
+                /**
+                 * For STR, VIT, DEX and LCK, it means the source bonus. For HP and MP, it means
+                 * the current values.
+                 */
+                u32 extra;
+            };
+
+            /**
+             * A weapon or armor.
+             */
+            struct Equipment{
+
+                /**
+                 * Item ID of the weapon or armor.
+                 */
+                u16 id;
+
+                /**
+                 * Materia in the weapon/armor.
+                 */
+                Materia materia[MAX_EQUIP_SLOTS];
+            };
+
+            /**
+             * Character ID, sequential.
+             */
+            u8 id;
+
+            /**
+             * Character ID, can change with the history.
+             */
+            u8 char_id;
+
+            /**
+             * Indicates if the character has been unlocked for the PHS.
+             */
+            bool enabled;
+
+            /**
+             * Indicates if the character is locked in or out of the PHS.
+             */
+            bool locked;
+
+            /**
+             * Character name.
+             */
+            std::string name;
+
+            /**
+             * Character level.
+             */
+            u16 level;
+
+            /**
+             * Enemies killed by the character.
+             */
+            u32 kills;
+
+            /**
+             * Indicates if the character is in the back_row;
+             */
+            bool back_row;
+
+            /**
+             * Total experience.
+             */
+            u32 exp;
+
+            /**
+             * Experience required to reach next level.
+             */
+            u32 exp_to_next;
+
+            /**
+             * STR stat.
+             */
+            Stat str;
+
+            /**
+             * VIT stat.
+             */
+            Stat vit;
+
+            /**
+             * MAG stat.
+             */
+            Stat mag;
+
+            /**
+             * SPR stat.
+             */
+            Stat spr;
+
+            /**
+             * DEX stat.
+             */
+            Stat dex;
+
+            /**
+             * LCK stat.
+             */
+            Stat lck;
+
+            /**
+             * HP stat.
+             */
+            Stat hp;
+
+            /**
+             * MP stat.
+             */
+            Stat mp;
+
+            /**
+             * Current limit level.
+             */
+            u8 limit_level;
+
+            /**
+             * Current limit bar.
+             */
+            u8 limit_bar;
+
+            /**
+             * Learned limits.
+             *
+             * Grouped by level and technique.
+             */
+            bool limits_learned[MAX_LIMIT_LEVELS][MAX_LIMIT_TECHNIQUES];
+
+            /**
+             * Uses of techniques in each limit level.
+             */
+            u32 limit_uses[MAX_LIMIT_LEVELS];
+
+            /**
+             * Equipped weapon;
+             */
+            Equipment weapon;
+
+            /**
+             * Equipped weapon;
+             */
+            Equipment armor;
+
+            /**
+             * Equipped accessory ID. 0 if none equipped.
+             */
+            u16 accessory;
+
+            /**
+             * Current status list.
+             */
+            std::vector<u8> status;
+        };
+
+        /**
+         * Indicates if the savemap is empty or contains actual data.
+         */
+        bool empty_;
+
+        /**
+         * Control string.
+         *
+         * Not a checksum, it does not depend on the actual data. It never mutates, is used to
+         * differenciate savemaps when asking to override.
+         */
+        std::string control_;
+
+        /**
+         * Window colours.
+         */
+        u8 window_colours_[4][3];
+
+        /**
+         * List of characters.
+         */
+        Character characters_[MAX_CHARACTERS];
+
+        /**
+         * IDs of characters in the party.
+         */
+        int party_[MAX_PARTY_MEMBERS];
+
+        /**
+         * Owned items.
+         */
+        Item items_[MAX_ITEM_SLOTS];
+
+        /**
+         * Owned key items.
+         */
+        bool key_items_[MAX_KEY_ITEM_SLOTS];
+
+        /**
+         * Owned materia.
+         */
+        Materia materia_[MAX_MATERIA_SLOTS];
+
+        /**
+         * Alternative materia stash.
+         *
+         * Used for the Yuffie materia event.
+         */
+        Materia materia_stash_[MAX_STASH_SLOTS];
+
+        /**
+         * Current party money.
+         */
+        u32 money_;
+
+        /**
+         * Seconds played
+         */
+        u32 seconds_;
+
+        /**
+         * Current countdown remaining seconds.
+         */
+        u32 countdown_;
+
+        /**
+         * Current field map.
+         *
+         * If null or empty, it means the player is in in the world map.
+         */
+        std::string field_map_;
+
+        /**
+         * Current location.
+         */
+        Location location_;
+
+        /**
+         * Game settings.
+         */
+        Settings settings_;
+
+        /**
+         * Memory banks.
+         */
+        int data_[BANK_COUNT][BANK_ADDRESS_COUNT];
+};
+
+
+
+

+ 391 - 0
V-Gears/include/core/SavemapManager.h

@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2022 The V-Gears Team
+ *
+ * This file is part of V-Gears
+ *
+ * V-Gears is free software: you can redistribute it and/or modify it under
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, version 3.0 (GPLv3) of the License.
+ *
+ * V-Gears is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#pragma once
+
+#include <vector>
+#include <OgreSingleton.h>
+#include <OgreColourValue.h>
+#include "core/Savemap.h"
+
+
+class SavemapManager : public Ogre::Singleton<SavemapManager>{
+
+    public:
+
+        /**
+         * The maximum number of save slots.
+         */
+        static int MAX_SAVE_SLOTS;
+
+        /**
+         * Constructor.
+         */
+        SavemapManager();
+
+        /**
+         * Destructor.
+         */
+        virtual ~SavemapManager();
+
+        /**
+         * Retrieves the current savemap.
+         *
+         * @return The current Savemap.
+         */
+        Savemap GetCurrentSavemap();
+
+        /**
+         * Retrieves a savemap saved in a slot.
+         *
+         * @param[in] slot Slot of the savemap to retrieve.
+         * @return The savemap in the slot. NULL if there is no such savemap or an invalid slot is
+         * supplied.
+         */
+        Savemap* GetSavemap(unsigned int slot);
+
+        /**
+         * Retrieves a list of the saved maps.
+         */
+        std::vector<Savemap*> GetSavemaps();
+
+        /**
+         * Copies the current savemap to a slot and writes the file.
+         *
+         * @param slot[in] Slot at which to save.
+         * @param force[in] If false, it will check if the control data is the save (i.e. if its
+         * trying to overwrite a savemap of the same playthrought), and if they do not match, it
+         * will do nothing and return false. If true, it will not be checked.
+         * @return True if the data was saved, false ifforce was false and control data strings did
+         * not match, or if there was an I/O error (which will be logged).
+         */
+        bool Save(unsigned int slot, bool force);
+
+        /**
+         * Saves a savemap to a slot and writes the file.
+         *
+         * @param slot[in] Slot at which to save.
+         * @param force[in] If false, it will check if the control data is the save (i.e. if its
+         * trying to overwrite a savemap of the same playthrought), and if they do not match, it
+         * will do nothing and return false. If true, it will not be checked.
+         * @return True if the data was saved, false ifforce was false and control data strings did
+         * not match, or if there was an I/O error (which will be logged).
+         */
+        bool Save(Savemap savemap, unsigned int slot, bool force);
+
+        /**
+         * Releases savemaps from memory.
+         *
+         * The content of the current savemap which are not yet saved to a slot will be lost.
+         */
+        void Release();
+
+        /**
+         * Pushes data to the current savemap memory banks.
+         *
+         * @param[in] bank The memory bank.
+         * @param[in] address The address in the bank.
+         * @param[in] value The value to save.
+         */
+        void SetData(const unsigned int bank, const unsigned int address, const int value);
+
+        /**
+         * Sets the control string of the current savemap.
+         *
+         * @param[in] control The control string.
+         */
+        void SetControlKey(const char* control);
+
+        /**
+         * Sets the window colours in the current savemap.
+         *
+         * @param[in] t_l_r Top-left corner, red component.
+         * @param[in] t_l_r Top-left corner, green component.
+         * @param[in] t_l_r Top-left corner, blue component.
+         * @param[in] t_r_r Top-right corner, red component.
+         * @param[in] t_r_r Top-right corner, green component.
+         * @param[in] t_r_r Top-right corner, blue component.
+         * @param[in] b_r_r Bottom-right corner, red component.
+         * @param[in] b_r_r Bottom-right corner, green component.
+         * @param[in] b_r_r Bottom-right corner, blue component.
+         * @param[in] b_l_r Bottom-left corner, red component.
+         * @param[in] b_l_r Bottom-left corner, green component.
+         * @param[in] b_l_r Bottom-left corner, blue component.
+         */
+        void SetWindowColours(
+          const unsigned int t_l_r, const unsigned int t_l_g, const unsigned int t_l_b,
+          const unsigned int t_r_r, const unsigned int t_r_g, const unsigned int t_r_b,
+          const unsigned int b_r_r, const unsigned int b_r_g, const unsigned int b_r_b,
+          const unsigned int b_l_r, const unsigned int b_l_g, const unsigned int b_l_b
+        );
+
+        /**
+         * Sets the money of the current savemap.
+         *
+         * @param[in] money The current money.
+         */
+        void SetMoney(const unsigned int money);
+
+        /**
+         * Sets the total playtime of the current savemap.
+         *
+         * @param[in] seconds The total playtime, in seconds.
+         */
+        void SetGameTime(const unsigned int seconds);
+
+        /**
+         * Sets the time of the curent timer in the default savemap.
+         *
+         * @param[in] seconds The current countdown timer time, in seconds.
+         */
+        void SetCountdownTime(const unsigned int seconds);
+
+        /**
+         * Sets the current field of the current savemap.
+         *
+         * If set to an empty string, it will be assumed that it was saved in the world map.
+         *
+         * @param[in] field Field map ID, or empty for the world map.
+         */
+        void SetField(const char* field);
+
+        /**
+         * Marks a key item as owned or non-owned in the current savemap.
+         *
+         * @param[in] item Key item id.
+         * @param[in] owned True to mark it as owned, false to mark is as non-owned.
+         */
+        void SetKeyItem(const unsigned int item, const bool owned);
+
+        /**
+         * Sets the current party in the current savemap.
+         *
+         * @param[in] member_1 The first party member ID. -1 to leave empty.
+         * @param[in] member_2 The second party member ID. -1 to leave empty.
+         * @param[in] member_3 The third party member ID. -1 to leave empty.
+         */
+        void SetParty(const int member_1, const int member_2, const int member_3);
+
+        /**
+         * Sets an item in a inventory slot in the current savemap.
+         *
+         * @param[in] slot Inventory slot.
+         * @param[in] id Item ID.
+         * @param[in] quantity Item quantity. Set to 0 to mark as no item in the specified slot.
+         */
+        void SetItem(const unsigned int slot, const unsigned int id, const unsigned int quantity);
+
+        /**
+         * Sets a materia in a materia inventory slot in the current savemap.
+         *
+         * For Enemy Skill materia, don't use this, use {@see SetESkillMateria}.
+         *
+         * @param[in] slot Materia inventory slot.
+         * @param[in] id Materia ID. Set to -1 to mark it as an empty slot.
+         * @param[in] ap Total AP of the materia.
+         */
+        void SetMateria(const unsigned int slot, const int id, const unsigned int ap);
+
+        /**
+         * Sets a skill as learned in an Enemy Skill materia inventory slot in the current savemap.
+         *
+         * Calling this will make the materia in the slot to be an Enemy Skill materia and set the
+         * AP to 0. To delete it, call {@see SetMateria} with the same slot and id -1.
+         *
+         * @param[in] slot Materia inventory slot.
+         * @param[in] skill Enemy Skill ID (sequential, from 0).
+         * @param[in] learned TRue to mark as learned, false to mark as not learned.
+         */
+        void SetESkillMateria(const unsigned slot, const unsigned int skill, const bool learned);
+
+        /**
+         * Sets a materia in a materia inventory slot in the current savemap.
+         *
+         * For Enemy Skill materia, don't use this, use {@see SetEnemySkillMateriaStash}.
+         *
+         * @param[in] slot Materia inventory slot.
+         * @param[in] id Materia ID. Set to -1 to mark it as an empty slot.
+         * @param[in] ap Total AP of the materia.
+         */
+        void SetMateriaStash(const unsigned int slot, const int id, const unsigned int ap);
+
+        /**
+         * Sets a skill as learned in an Enemy Skill materia stash slot in the current savemap.
+         *
+         * Calling this will make the materia in the slot to be an Enemy Skill materia and set the
+         * AP to 0. To delete it, call {@see SetMateriaStash} with the same slot and id -1.
+         *
+         * @param[in] slot Materia inventory slot.
+         * @param[in] skill Enemy Skill ID (sequential, from 0)
+         * @param[in] learned TRue to mark as learned, false to mark as not learned.
+         */
+        void SetESkillMateriaStash(
+          const unsigned slot, const unsigned int skill, const bool learned
+        );
+
+        /**
+         * Sets the current location in the current savemap.
+         *
+         * @param[in] x X coordinate.
+         * @param[in] y Y coordinate.
+         * @param[in] z Z coordinate. It's optional, set it to -1 to ignore it.
+         * @param[in] triangle Walkmesh triangle ID.
+         * @param[in] angle Facing direction.
+         */
+        void SetLocation(
+          const unsigned int x, const unsigned int y, const int z,
+          const unsigned int triangle, const int angle
+        );
+
+        /**
+         * Sets a setting value in the current savemap.
+         *
+         * @param[in] key Setting key.
+         * @param[in] value Setting value.
+         * @todo Does nothing, implement when settings are working.
+         */
+        void SetSetting(const unsigned int key, const unsigned int value);
+
+        /**
+         * Sets a character basic information in the current savemap.
+         *
+         * @param[in] id Character ID.
+         * @param[in] char_id Character identifier.
+         * @param[in] name Character name.
+         * @param[in] enabled If enabled in the PHS.
+         * @param[in] locked If locked in or out the current party.
+         * @param[in] level Character level.
+         * @param[in] kills Total kills.
+         * @param[in] back_row If the character is in the back row.
+         * @param[in] exp Total experience.
+         * @param[in] exp_to_next Experience to reach next level.
+         * @param[in] limit_level Currently selected limit level.
+         * @param[in] limit_bar Current limit bar fill status
+         * @param[in] weapon Equipped weapon ID.
+         * @param[in] armor Equipped armor ID.
+         * @param[in] accessory Equipped accessory ID. -1 if none equipped.
+         */
+        void SetCharacterInfo(
+          const unsigned int id, const int char_id, const char* name,
+          const bool enabled, const bool locked,
+          const unsigned int level, const unsigned int kills,
+          const bool back_row, const unsigned int exp, const unsigned int exp_to_next,
+          const unsigned int limit_level, const unsigned int limit_bar,
+          const unsigned int weapon, const unsigned int armor, const int accessory
+        );
+
+        /**
+         * Sets a character stat values in the current savemap.
+         *
+         * @param[in] id Character ID.
+         * @param[in] stat Stat ID. See {@see STAT}.
+         * @param[in] base Base value of the stat.
+         * @param[in] extra Extra value of the stat. For STR, VIT, MAG, SPR, DEX and LCK, it means
+         * the increment by means of sources. For HP and MP, the current value.
+         */
+        void SetCharacterStat(
+          const unsigned int id, const unsigned int stat,
+          const unsigned int base, const unsigned int extra
+        );
+
+        /**
+         * Sets a character limit learned status in the current savemap.
+         *
+         * @param[in] id Character ID.
+         * @param[in] level Limit level.
+         * @param[in] technique Limit level technique ID.
+         * @param[in] learned TRue to mark as learned, false to mark as non-learned.
+         * @param[in] uses Times the technique has been used.
+         */
+        void SetCharacterLimitLearned(
+          const unsigned int id, const unsigned int level,
+          const unsigned int technique, const bool learned, const unsigned int uses
+        );
+
+        /**
+         * Sets a materia in a character weapon or armor slot in the current savemap.
+         *
+         * For Enemy Skill materia, don't use this, use {@see SetCharacterESkillMateria}.
+         *
+         * @param[in] id Character ID.
+         * @param[in] weapon True to set the materia in the weapon, false to set it in the armor.
+         * @param[in] slot Weapon or armor slot.
+         * @param[in] materia Materia ID. Set to -1 to mark it as an empty slot.
+         * @param[in] ap Total AP of the materia.
+         */
+        void SetCharacterMateria(
+          const unsigned int id, const bool weapon, const unsigned int slot,
+          const int materia, const unsigned int ap
+        );
+
+        /**
+         * Sets a skill as learned in a character's Enemy Skill materia in the current savemap.
+         *
+         * Calling this will make the materia to be an Enemy Skill materia and set the AP to 0. To
+         * delete it, call {@see SetCharacterMateria} with the same character ID, weapon/armor and
+         * slot and id -1.
+         *
+         * @param[in] id Character ID.
+         * @param[in] weapon True to set the materia in the weapon, false to set it in the armor.
+         * @param[in] slot Weapon or armor slot.
+         * @param[in] skill Enemy Skill ID (sequential, from 0).
+         * @param[in] learned TRue to mark as learned, false to mark as not learned.
+         */
+        void SetCharacterESkillMateria(
+          const unsigned int id, const bool weapon, const unsigned int slot,
+          const unsigned int skill, const bool learned
+        );
+
+        /**
+         * Adds or removes a status to a character in the current savemap.
+         *
+         * @param[in] id Character ID.
+         * @param[in] status ID of the status.
+         * @param[in] inflicted True to add the status, false to remove it.
+         */
+        void SetCharacterStatus(
+          const unsigned int id, const unsigned int status, const bool inflicted
+        );
+
+    private:
+
+        /**
+         * Path for the save files.
+         */
+        static std::string SAVE_PATH;
+
+        /**
+         * Reads every saved savemap from it's file.
+         */
+        void ReadSavemaps();
+
+        /**
+         * The current savemap.
+         */
+        Savemap* current_savemap_;
+
+        /**
+         * List of saved savemaps.
+         */
+        std::vector<Savemap*> saved_savemaps_;
+
+        /**
+         * Indicates if the saved savemaps have been read from files.
+         */
+        bool savemaps_read_;
+
+};
+
+

+ 134 - 1
V-Gears/include/core/ScriptManagerBinds.h

@@ -19,6 +19,7 @@
 #include "Entity.h"
 #include "EntityManager.h"
 #include "AudioManager.h"
+#include "SavemapManager.h"
 #include "Timer.h"
 #include "UiManager.h"
 #include "UiWidget.h"
@@ -257,7 +258,7 @@ void ScriptManager::InitBinds(){
           .def("get_track_id", (int(EntityManager::*)(int)) &EntityManager::GetTrack)
     ];
 
-    // Commands for the entity manager, not related to any particular entity.
+    // Commands for the audio manager.
     luabind::module(lua_state_)[
         luabind::class_<AudioManager>("AudioManager")
           .def("play_music", (void(AudioManager::*)(const char*)) &AudioManager::ScriptPlayMusic)
@@ -273,6 +274,136 @@ void ScriptManager::InitBinds(){
           )
     ];
 
+
+
+    // Commands for the savemap manager.
+    luabind::module(lua_state_)[
+        luabind::class_<SavemapManager>("SavemapManager")
+        .def(
+          "get_current_savemap", (Savemap*(SavemapManager::*)()) &SavemapManager::GetCurrentSavemap
+        )
+        .def(
+          "get_savemap",
+          (Savemap*(SavemapManager::*)(const unsigned int)) &SavemapManager::GetSavemap
+        )
+        .def(
+          "set_data",
+          (void(SavemapManager::*)(const unsigned int, const unsigned int, const int))
+            &SavemapManager::SetData
+        )
+        .def(
+          "set_control_key", (void(SavemapManager::*)(const char*)) &SavemapManager::SetControlKey
+        )
+        .def(
+          "set_window_colours",
+          (void(SavemapManager::*)(
+            const unsigned int, const unsigned int, const unsigned int,
+            const unsigned int, const unsigned int, const unsigned int,
+            const unsigned int, const unsigned int, const unsigned int,
+            const unsigned int, const unsigned int, const unsigned int
+          )) &SavemapManager::SetWindowColours
+        )
+        .def("set_money", (void(SavemapManager::*)(const unsigned int)) &SavemapManager::SetMoney)
+        .def(
+          "set_game_time",
+          (void(SavemapManager::*)(const unsigned int)) &SavemapManager::SetGameTime
+        )
+        .def(
+          "set_countdown_time",
+          (void(SavemapManager::*)(const unsigned int)) &SavemapManager::SetCountdownTime
+        )
+        .def(
+          "set_field", (void(SavemapManager::*)(const char*)) &SavemapManager::SetField
+        )
+        .def(
+          "set_key_item",
+          (void(SavemapManager::*)(const unsigned int, const bool)) &SavemapManager::SetKeyItem
+        )
+        .def(
+          "set_party",
+          (void(SavemapManager::*)(const int, const int, const int)) &SavemapManager::SetParty
+        )
+        .def(
+          "set_item",
+          (void(SavemapManager::*)(const unsigned int, const unsigned int, const unsigned int))
+            &SavemapManager::SetItem
+        )
+        .def(
+          "set_materia",
+          (void(SavemapManager::*)(const unsigned int, const unsigned int, const unsigned int))
+            &SavemapManager::SetMateria
+        )
+        .def(
+          "set_e_skill_materia",
+          (void(SavemapManager::*)(const unsigned int, const unsigned int, const bool))
+            &SavemapManager::SetESkillMateria
+        )
+        .def(
+          "set_materia_stash",
+          (void(SavemapManager::*)(const unsigned int, const unsigned int, const unsigned int))
+            &SavemapManager::SetMateriaStash
+        )
+        .def(
+          "set_e_skill_materia_stash",
+          (void(SavemapManager::*)(const unsigned int, const unsigned int, const bool))
+            &SavemapManager::SetESkillMateriaStash
+        )
+        .def(
+          "set_location",
+          (void(SavemapManager::*)(
+            const unsigned int, const unsigned int, const int, const unsigned int, const int)
+          ) &SavemapManager::SetLocation
+        )
+        .def(
+          "set_setting",
+          (void(SavemapManager::*)(const unsigned int, const unsigned int))
+            &SavemapManager::SetSetting
+        )
+        .def(
+          "set_character_info",
+          (void(SavemapManager::*)(
+            const unsigned int, const int, const char*, const bool, const bool, const unsigned int,
+            const unsigned int, const bool, const unsigned int, const unsigned int,
+            const unsigned int, const unsigned int, const unsigned int, const unsigned int,
+            const int
+          )) &SavemapManager::SetCharacterInfo
+        )
+        .def(
+          "set_character_stat",
+          (void(SavemapManager::*)(
+            const unsigned int, const unsigned int, const unsigned int, const unsigned int
+          )) &SavemapManager::SetCharacterStat
+        )
+        .def(
+          "set_character_limit_learned",
+          (void(SavemapManager::*)(
+            const unsigned int, const unsigned int, const unsigned int,
+            const bool, const unsigned int
+          )) &SavemapManager::SetCharacterLimitLearned
+        )
+        .def(
+          "set_character_materia",
+          (void(SavemapManager::*)(
+            const unsigned int, const bool, const unsigned int,
+            const unsigned int, const unsigned int
+          )) &SavemapManager::SetCharacterMateria
+        )
+        .def(
+          "set_character_e_skill_materia",
+          (void(SavemapManager::*)(
+            const unsigned int, const bool, const unsigned int, const unsigned int, const bool
+          )) &SavemapManager::SetCharacterESkillMateria
+        )
+        .def(
+          "set_character_status",
+          (void(SavemapManager::*)(const unsigned int, const unsigned int, const bool))
+            &SavemapManager::SetCharacterStatus
+        )
+        .def(
+          "save", (bool(SavemapManager::*)(const unsigned int, const bool)) &SavemapManager::Save
+        )
+    ];
+
     // 2D background and camera commands
     luabind::module(lua_state_)[
         luabind::class_<Background2D>("Background2D")
@@ -467,6 +598,8 @@ void ScriptManager::InitBinds(){
     luabind::globals(lua_state_)["entity_manager"]
       = boost::ref(*(EntityManager::getSingletonPtr()));
     luabind::globals(lua_state_)["audio_manager"] = boost::ref(*(AudioManager::getSingletonPtr()));
+    luabind::globals(lua_state_)["savemap_manager"]
+      = boost::ref(*(SavemapManager::getSingletonPtr()));
     luabind::globals(lua_state_)["background2d"]
       = boost::ref(*(EntityManager::getSingletonPtr()->GetBackground2D()));
     luabind::globals(lua_state_)["walkmesh"]

+ 4 - 3
V-Gears/src/Main.cpp

@@ -21,8 +21,8 @@
 #include <OgreOverlayManager.h>
 #include <OgreOverlaySystem.h>
 #include "VGearsGameState.h"
-#include "core/AudioManager.h"
 #include "common/VGearsApplication.h"
+#include "core/AudioManager.h"
 #include "core/CameraManager.h"
 #include "core/ConfigCmdManager.h"
 #include "core/ConfigFile.h"
@@ -33,6 +33,7 @@
 #include "core/GameFrameListener.h"
 #include "core/InputManager.h"
 #include "core/Logger.h"
+#include "core/SavemapManager.h"
 #include "core/ScriptManager.h"
 #include "core/Timer.h"
 #include "core/UiManager.h"
@@ -103,9 +104,9 @@ int main(int argc, char *argv[]){
         // Initialize before GameFrameListener, but after ConfigCmdManager
         auto input_manager = std::make_unique<InputManager>();
 
-
         auto audio_manager = std::make_unique<AudioManager>();
-        //audio_manager->MusicPlay( "loop1" );
+
+        auto savemap_manager = std::make_unique<SavemapManager>();
 
         // Create this earlier than DisplayFrameListener cause it can fire
         // events there

+ 6 - 4
V-Gears/src/core/DialogsManager.cpp

@@ -247,16 +247,16 @@ void DialogsManager::SetText(const char* d_name, const char* text){
     float height = 0.0f;
     TiXmlNode* xmlText = TextManager::getSingleton().GetDialog(text, width, height);
     if (xmlText != NULL){
-        messages_[id]->text_area->SetText(xmlText);
         ShowMessage(
           id, data->x, data->y,
           width == 0.0f ? data->w : width,
           height == 0.0f ? data->h : height
         );
+        messages_[id]->text_area->SetText(xmlText);
     }
     else{
-        messages_[id]->text_area->SetText(std::string("[ERROR string not found:] ") + text);
         ShowMessage(id, data->x, data->y, data->w, data->h);
+        messages_[id]->text_area->SetText(std::string("[ERROR string not found:] ") + text);
     }
 }
 
@@ -333,18 +333,20 @@ int DialogsManager::GetCursor(const char* d_name) const{
 void DialogsManager::ShowMessage(
   const int id, const int x, const int y, const int width, const int height
 ){
+    // Add 5 pixels, to compensate character width operation roundowns.
+    int extra_width = width + 5;
     // Handle error when dialog window cross border of limit area
     float la_percent_width, la_width;
     limit_area_->GetWidth(la_percent_width, la_width);
     float move_back_x = 0;
-    if (x + width > la_width) move_back_x = x + width - la_width;
+    if (x + extra_width > la_width) move_back_x = x + extra_width - la_width;
     float la_percent_height, la_height;
     limit_area_->GetWidth(la_percent_height, la_height);
     float move_back_y = 0;
     if (y + height > la_height) move_back_y = y + height - la_height;
     messages_[id]->widget->SetX(0, x - move_back_x);
     messages_[id]->widget->SetY(0, y - move_back_y);
-    messages_[id]->widget->SetWidth(0, width);
+    messages_[id]->widget->SetWidth(0, extra_width);
     messages_[id]->widget->SetHeight(0, height);
     messages_[id]->widget->SetVisible(true);
     messages_[id]->auto_close = false;

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

@@ -31,7 +31,6 @@
 
 /**
  * Entity manager singleton.
- * @private
  */
 template<>EntityManager *Ogre::Singleton<EntityManager>::msSingleton = nullptr;
 

+ 439 - 0
V-Gears/src/core/Savemap.cpp

@@ -0,0 +1,439 @@
+/*
+ * Copyright (C) 2022 The V-Gears Team
+ *
+ * This file is part of V-Gears
+ *
+ * V-Gears is free software: you can redistribute it and/or modify it under
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, version 3.0 (GPLv3) of the License.
+ *
+ * V-Gears is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <string>
+#include <iostream>
+#include <algorithm>
+#include "core/Savemap.h"
+#include "common/TypeDefine.h"
+
+Savemap::Savemap():
+  empty_(true), control_(""), money_(0), seconds_(0), countdown_(0), field_map_("")
+{
+    for (int i = 0; i < 4; i ++){
+        window_colours_[i][0] = 0;
+        window_colours_[i][1] = 0;
+        window_colours_[i][2] = 0;
+    }
+    // TODO: Characters? Needed to initialize?
+    party_[0] = -1;
+    party_[1] = -1;
+    party_[2] = -1;
+    for (int i = 0; i < 500; i ++){
+        items_[i].id = -1;
+        items_[i].quantity = 0;
+        materia_[i].id = -1;
+        materia_[i].ap = 0;
+        materia_stash_[i].id = -1;
+        materia_stash_[i].ap = 0;
+    }
+    for (int i = 0; i < 50; i ++){
+        key_items_[i] = false;
+    }
+    for (int b = 0; b < 15; b ++){
+        for (int a = 0; a < 15; a ++){
+            data_[b][a] = 0;
+        }
+    }
+}
+
+Savemap::~Savemap(){}
+
+void Savemap::operator = (const Savemap &to_copy){
+    empty_ = to_copy.empty_;
+    control_ = to_copy.control_;
+    for (int i = 0; i < 4; i ++){
+        window_colours_[i][0] = to_copy.window_colours_[i][0];
+        window_colours_[i][1] = to_copy.window_colours_[i][0];
+        window_colours_[i][2] = to_copy.window_colours_[i][0];
+    }
+    for (int i = 0; i < 11; i ++) characters_[i] = to_copy.characters_[i];
+    for (int i = 0; i < 3; i ++) party_[i] = to_copy.party_[i];
+}
+
+bool Savemap::IsEmpty(){return empty_;}
+
+void Savemap::Read(std::string file_name){
+    // TODO
+}
+
+void Savemap::SetData(const unsigned int bank, const unsigned int address, const int value){
+    if (bank < BANK_COUNT && address < BANK_ADDRESS_COUNT) data_[bank][address] = value;
+}
+
+void Savemap::SetControlKey(const std::string control){control_ = control;}
+
+/**
+ * Sets the window colours in the current savemap.
+ *
+ * @param[in] t_l_r Top-left corner, red component.
+ * @param[in] t_l_r Top-left corner, green component.
+ * @param[in] t_l_r Top-left corner, blue component.
+ * @param[in] t_r_r Top-right corner, red component.
+ * @param[in] t_r_r Top-right corner, green component.
+ * @param[in] t_r_r Top-right corner, blue component.
+ * @param[in] b_r_r Bottom-right corner, red component.
+ * @param[in] b_r_r Bottom-right corner, green component.
+ * @param[in] b_r_r Bottom-right corner, blue component.
+ * @param[in] b_l_r Bottom-left corner, red component.
+ * @param[in] b_l_r Bottom-left corner, green component.
+ * @param[in] b_l_r Bottom-left corner, blue component.
+ */
+void Savemap::SetWindowColours(
+  const unsigned int t_l_r, const unsigned int t_l_g, const unsigned int t_l_b,
+  const unsigned int t_r_r, const unsigned int t_r_g, const unsigned int t_r_b,
+  const unsigned int b_r_r, const unsigned int b_r_g, const unsigned int b_r_b,
+  const unsigned int b_l_r, const unsigned int b_l_g, const unsigned int b_l_b
+){
+    window_colours_[0][0] = std::min(MAX_COLOUR, t_l_r);
+    window_colours_[0][1] = std::min(MAX_COLOUR, t_l_g);
+    window_colours_[0][2] = std::min(MAX_COLOUR, t_l_b);
+    window_colours_[1][0] = std::min(MAX_COLOUR, t_r_r);
+    window_colours_[1][1] = std::min(MAX_COLOUR, t_r_g);
+    window_colours_[1][2] = std::min(MAX_COLOUR, t_r_b);
+    window_colours_[2][0] = std::min(MAX_COLOUR, b_r_r);
+    window_colours_[2][1] = std::min(MAX_COLOUR, b_r_g);
+    window_colours_[2][2] = std::min(MAX_COLOUR, b_r_b);
+    window_colours_[3][0] = std::min(MAX_COLOUR, b_l_r);
+    window_colours_[3][1] = std::min(MAX_COLOUR, b_l_g);
+    window_colours_[3][2] = std::min(MAX_COLOUR, b_l_b);
+}
+
+std::string Savemap::GetControlKey() const {return control_;}
+
+void Savemap::SetMoney(const unsigned int money){money_ = money;}
+
+void Savemap::SetGameTime(const unsigned int seconds){seconds_ = seconds;}
+
+void Savemap::SetCountdownTime(const unsigned int seconds){countdown_ = seconds;}
+
+void Savemap::SetField(const std::string field){field_map_ = field;}
+
+void Savemap::SetKeyItem(const unsigned int item, const bool owned){
+    if (item < MAX_KEY_ITEM_SLOTS) key_items_[item] = owned;
+}
+
+void Savemap::SetParty(const int member_1, const int member_2, const int member_3){
+    if (member_1 == -1 || (member_1 >= 0 && member_1 < MAX_CHARACTERS)) party_[0] = member_1;
+    if (member_2 == -1 || (member_2 >= 0 && member_2 < MAX_CHARACTERS)) party_[1] = member_2;
+    if (member_3 == -1 || (member_3 >= 0 && member_3 < MAX_CHARACTERS)) party_[2] = member_3;
+}
+
+void Savemap::SetItem(const unsigned int slot, const unsigned int id, const unsigned int quantity){
+    if (slot < MAX_ITEM_SLOTS){
+        items_[slot].id = id;
+        items_[slot].quantity = quantity;
+    }
+}
+
+void Savemap::SetMateria(const unsigned int slot, const int id, const unsigned int ap){
+    if (slot < MAX_MATERIA_SLOTS){
+        materia_[slot].id = id;
+        materia_[slot].ap = ap;
+        for (int i = 0; i < MAX_ENEMY_SKILLS; i ++) materia_[slot].enemy_skill_learned[i] = false;
+    }
+}
+
+void Savemap::SetESkillMateria(const unsigned slot, const unsigned int skill, const bool learned){
+    if (slot < MAX_MATERIA_SLOTS && skill < MAX_ENEMY_SKILLS){
+        // TODO: Maybe set enemy skill materia ID here?
+        materia_[slot].ap = 0;
+        materia_[slot].enemy_skill_learned[skill] = learned;
+    }
+}
+
+void Savemap::SetMateriaStash(const unsigned int slot, const int id, const unsigned int ap){
+    if (slot < MAX_STASH_SLOTS){
+        materia_stash_[slot].id = id;
+        materia_stash_[slot].ap = ap;
+        for (int i = 0; i < MAX_ENEMY_SKILLS; i ++)
+            materia_stash_[slot].enemy_skill_learned[i] = false;
+    }
+}
+
+void Savemap::SetESkillMateriaStash(
+  const unsigned slot, const unsigned int skill, const bool learned
+){
+    if (slot < MAX_STASH_SLOTS && skill < MAX_ENEMY_SKILLS){
+        // TODO: Maybe set enemy skill materia ID here?
+        materia_stash_[slot].ap = 0;
+        materia_stash_[slot].enemy_skill_learned[skill] = learned;
+    }
+}
+
+void Savemap::SetLocation(
+  const unsigned int x, const unsigned int y, const int z,
+  const unsigned int triangle, const int angle
+){
+    if (z >= -1 && angle >= -360 && angle <= 360){
+        location_.x = x;
+        location_.y = y;
+        location_.z = z;
+        location_.triangle = triangle;
+        location_.angle = angle;
+    }
+}
+
+void Savemap::SetSetting(const unsigned int key, const unsigned int value){
+    // TODO : Implements when settings are working.
+}
+
+void Savemap::SetCharacterInfo(
+  const unsigned int id, const int char_id, const std::string name,
+  const bool enabled, const bool locked,
+  const unsigned int level, const unsigned int kills,
+  const bool back_row, const unsigned int exp, const unsigned int exp_to_next,
+  const unsigned int limit_level, const unsigned int limit_bar,
+  const unsigned int weapon, const unsigned int armor, const int accessory
+){
+    if (id < MAX_CHARACTERS){
+        characters_[id].id = id;
+        characters_[id].char_id = char_id;
+        characters_[id].name = name;
+        characters_[id].enabled = enabled;
+        characters_[id].locked = locked;
+        characters_[id].level = level;
+        characters_[id].kills = kills;
+        characters_[id].back_row = back_row;
+        characters_[id].exp = exp;
+        characters_[id].exp_to_next = exp_to_next;
+        characters_[id].limit_level = std::min(limit_level, MAX_LIMIT_LEVELS);
+        characters_[id].limit_bar = std::min(limit_bar, MAX_LIMIT_BAR);
+        characters_[id].weapon.id = weapon;
+        characters_[id].armor.id = armor;
+        characters_[id].accessory = std::max(accessory, -1);
+    }
+}
+
+void Savemap::SetCharacterStat(
+  const unsigned int id, const unsigned int stat,
+  const unsigned int base, const unsigned int extra
+){
+    if (id < MAX_CHARACTERS){
+        if (stat == 0){
+            characters_[id].str.base = base;
+            characters_[id].str.extra = extra;
+        }
+        else if (stat == 1){
+            characters_[id].vit.base = base;
+            characters_[id].vit.extra = extra;
+        }
+        else if (stat == 2){
+            characters_[id].mag.base = base;
+            characters_[id].mag.extra = extra;
+        }
+        else if (stat == 3){
+            characters_[id].spr.base = base;
+            characters_[id].spr.extra = extra;
+        }
+        else if (stat == 4){
+            characters_[id].dex.base = base;
+            characters_[id].dex.extra = extra;
+        }
+        else if (stat == 5){
+            characters_[id].lck.base = base;
+            characters_[id].lck.extra = extra;
+        }
+        else if (stat == 6){
+            characters_[id].hp.base = base;
+            characters_[id].hp.extra = extra;
+        }
+        else if (stat == 7){
+            characters_[id].mp.base = base;
+            characters_[id].mp.extra = extra;
+        }
+    }
+}
+void Savemap::SetCharacterLimitLearned(
+  const unsigned int id, const unsigned int level,
+  const unsigned int technique, const bool learned, const unsigned int uses
+){
+    if (id < MAX_CHARACTERS && level < MAX_LIMIT_LEVELS && technique < MAX_LIMIT_TECHNIQUES){
+        characters_[id].limits_learned[level][technique] = learned;
+        characters_[id].limit_uses[level] = uses;
+    }
+}
+
+void Savemap::SetCharacterMateria(
+  const unsigned int id, const bool weapon, const unsigned int slot,
+  const int materia, const unsigned int ap
+){
+    if (id < MAX_CHARACTERS && slot < MAX_EQUIP_SLOTS){
+        if (weapon){
+            characters_[id].weapon.materia[slot].id = id;
+            characters_[id].weapon.materia[slot].ap = ap;
+            for (int i = 0; i < MAX_ENEMY_SKILLS; i ++)
+                characters_[id].weapon.materia[slot].enemy_skill_learned[i] = false;
+        }
+        else{
+            characters_[id].armor.materia[slot].id = id;
+            characters_[id].armor.materia[slot].ap = ap;
+            for (int i = 0; i < MAX_ENEMY_SKILLS; i ++)
+                characters_[id].armor.materia[slot].enemy_skill_learned[i] = false;
+        }
+    }
+}
+
+void Savemap::SetCharacterESkillMateria(
+  const unsigned int id, const bool weapon, const unsigned int slot,
+  const unsigned int skill, const bool learned
+){
+    if (id < MAX_CHARACTERS && slot < MAX_EQUIP_SLOTS && skill < MAX_ENEMY_SKILLS){
+        if (weapon){
+            // TODO: Maybe set enemy skill materia ID here?
+            characters_[id].weapon.materia[slot].ap = 0;
+            characters_[id].weapon.materia[slot].enemy_skill_learned[skill] = learned;
+        }
+        else{
+            // TODO: Maybe set enemy skill materia ID here?
+            characters_[id].armor.materia[slot].ap = 0;
+            characters_[id].armor.materia[slot].enemy_skill_learned[skill] = learned;
+        }
+    }
+}
+
+void Savemap::SetCharacterStatus(
+  const unsigned int id, const unsigned int status, const bool inflicted
+){
+    //TODO
+}
+
+void Savemap::Write(std::string file_name){
+    std::fstream file;
+    file.open(file_name, std::ios::out);
+    if (!file) return;
+    file
+      << "Savemap = {\n"
+      << "    control = \"" << control_ << "\",\n"
+      << "    window_colours = {},\n"
+      << "    party = {" << party_[0] << ", " << party_[1] << ", " << party_[2] << "},\n"
+      << "    characters = {\n";
+    for (int c = 0; c < 9; c ++){
+        int total_count; // Utility to count in loops.
+        Character character = characters_[c];
+        file
+          << "        [" << c << "] = {\n"
+          << "            char_id = " << static_cast<int>(character.char_id) << ",\n"
+          << "            name = \"" << character.name << "\",\n"
+          << "            enabled = " << (character.enabled ? "true" : "false") << ",\n"
+          << "            locked = " << (character.locked ? "true" : "false") << ",\n"
+          << "            level = " << static_cast<int>(character.level) << ",\n"
+          << "            kills = " << character.kills << ",\n"
+          << "            back_row = " << (character.back_row ? "true" : "false") << ",\n"
+          << "            exp = " << character.exp << ",\n"
+          << "            exp_to_next = " << character.exp_to_next << " ,\n"
+          << "            stats = {\n"
+          << "                str = {base = " << character.str.base
+          << ", bonus = " << character.str.extra
+          << ", total = " << (character.str.base + character.str.extra) << "},\n"
+          << "                vit = {base = " << character.vit.base
+          << ", bonus = " << character.vit.extra
+          << ", total = " << (character.vit.base + character.vit.extra) << "},\n"
+          << "                mag = {base = " << character.mag.base
+          << ", bonus = " << character.mag.extra
+          << ", total = " << (character.mag.base + character.mag.extra) << "},\n"
+          << "                spr = {base = " << character.spr.base
+          << ", bonus = " << character.spr.extra
+          << ", total = " << (character.spr.base + character.spr.extra) << "},\n"
+          << "                dex = {base = " << character.dex.base
+          << ", bonus = " << character.dex.extra
+          << ", total = " << (character.dex.base + character.dex.extra) << "},\n"
+          << "                lck = {base = " << character.lck.base
+          << ", bonus = " << character.lck.extra
+          << ", total = " << (character.lck.base + character.lck.extra) << "},\n"
+          << "                hp = {current = " << character.hp.extra
+          << ", base = " << character.hp.base << ", max = 0},\n"
+          << "                mp = {current = " << character.mp.extra
+          << ", base = " << character.mp.base << ", max = 0},\n"
+          << "                atk = 0,\n"
+          << "                acc = 0,\n"
+          << "                def = 0,\n"
+          << "                eva = 0,\n"
+          << "                matk = 0,\n"
+          << "                mdef = 0,\n"
+          << "                meva = 0\n"
+          << "            },\n"
+          << "            limit = {\n"
+          << "                current = " << static_cast<int>(character.limit_level) << ",\n"
+          << "                bar = " << static_cast<int>(character.limit_bar) << ",\n"
+          << "                learned = {\n"
+          << "                    [1] = {[1] = "
+          << (character.limits_learned[0][0] == 1 ? "true" : "false")
+          << ", [2] = " << (character.limits_learned[0][1] == 1 ? "true" : "false") << "},\n"
+          << "                [2] = {[1] = "
+          << (character.limits_learned[1][0] == 1 ? "true" : "false")
+          << ", [2] = " << (character.limits_learned[1][1] == 1 ? "true" : "false") << "},\n"
+          << "                [3] = {[1] = "
+          << (character.limits_learned[2][0] == 1 ? "true" : "false")
+          << ", [2] = " << (character.limits_learned[2][1] == 1 ? "true" : "false") << "},\n"
+          << "                [4] = {[1] = "
+          << (character.limits_learned[3][0] == 1 ? "true" : "false")
+          << ", [2] = " << (character.limits_learned[3][1] == 1 ? "true" : "false") << "}\n"
+          << "                },\n"
+          << "                uses = {[1] = " << character.limit_uses[0] << ", [2] = "
+          << character.limit_uses[1] << ", [3] = " << character.limit_uses[2] << ", [4] = 0}\n"
+          << "            },\n"
+          << "            weapon = {\n"
+          << "                id = " << static_cast<int>(character.weapon.id) << ",\n"
+          << "                materia = {";
+        total_count = 0;
+        for (int m = 0; m < 8; m ++){
+            if (character.weapon.materia[m].id < 255){
+                total_count ++;
+                file << "\n                    [" << m << "] = {id = "
+                  << static_cast<int>(character.weapon.materia[m].id) << ", ap = "
+                  << character.weapon.materia[m].ap << "},";
+                // TODO: Enemy skills
+            }
+        }
+        if (total_count > 0) file << "\n                }\n        },\n";
+        else file << "        }\n        },\n";
+        file
+          << "            armor = {\n"
+          << "                id = " << static_cast<int>(character.armor.id) << ",\n"
+          << "                materia = {";
+        total_count = 0;
+        for (int m = 0; m < 8; m ++){
+            if (character.armor.materia[m].id < 255){
+                total_count ++;
+                file << "\n                    [" << m << "] = {id = "
+                  << static_cast<int>(character.armor.materia[m].id) << ", ap = "
+                  << character.armor.materia[m].ap << "},";
+                // TODO: Enemy skills
+            }
+        }
+        if (total_count > 0) file << "\n                }\n        },\n";
+        else file << "        }\n        },\n";
+        file << "            accessory = ";
+        if (character.accessory < 0xFF) file << static_cast<int>(character.accessory);
+        else file << "nil";
+        file << ",\n            status = {";
+        // TODO: Loop statuses
+        //if (character.fury) file << FURY;
+        //if (character.sadness) file << SADNESS;
+        file << "}\n        },\n";
+
+    }
+    file << "    },\n    data = {\n";
+    for (int b = 0; b < BANK_COUNT; b ++){
+        file << "        [" << b << "] = {\n";
+        for (int a = 0; a < BANK_ADDRESS_COUNT; a ++){
+            if (data_[b][a] != 0) file << "            [" << a << "] = " << data_[b][a] << ",\n";
+        }
+        file << "        },\n";
+    }
+    file << "    }\n}\n";
+    file.close();
+}

+ 246 - 0
V-Gears/src/core/SavemapManager.cpp

@@ -0,0 +1,246 @@
+/*
+ * Copyright (C) 2022 The V-Gears Team
+ *
+ * This file is part of V-Gears
+ *
+ * V-Gears is free software: you can redistribute it and/or modify it under
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, version 3.0 (GPLv3) of the License.
+ *
+ * V-Gears is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <iostream>
+#include <string>
+#include <vector>
+#include "core/SavemapManager.h"
+#include "core/Logger.h"
+
+/**
+ * Savemap manager singleton.
+ */
+template<>SavemapManager *Ogre::Singleton<SavemapManager>::msSingleton = nullptr;
+
+int SavemapManager::MAX_SAVE_SLOTS(15);
+
+std::string SavemapManager::SAVE_PATH("save/");
+
+SavemapManager::SavemapManager(): current_savemap_(nullptr), savemaps_read_(false){}
+
+SavemapManager::~SavemapManager(){
+    //TODO
+    LOG_TRIVIAL("SavemapManager destroyed.");
+}
+
+Savemap SavemapManager::GetCurrentSavemap(){return *current_savemap_;}
+
+Savemap* SavemapManager::GetSavemap(unsigned int slot){
+    if (slot >= MAX_SAVE_SLOTS) return nullptr;
+    if (!savemaps_read_) ReadSavemaps();
+    return saved_savemaps_[slot];
+}
+
+std::vector<Savemap*> SavemapManager::GetSavemaps(){
+    if (!savemaps_read_) ReadSavemaps();
+    return saved_savemaps_;
+}
+
+bool SavemapManager::Save(unsigned int slot, bool force){
+    if (slot >= MAX_SAVE_SLOTS) return false;
+    if (!savemaps_read_) ReadSavemaps();
+    if (
+      !force && !saved_savemaps_[slot]->IsEmpty()
+      && current_savemap_->GetControlKey() != saved_savemaps_[slot]->GetControlKey()
+    ){
+        return false;
+    }
+    else{
+        saved_savemaps_[slot] = current_savemap_;
+        saved_savemaps_[slot]->Write(SAVE_PATH + std::to_string(slot) + ".lua");
+        return true;
+    }
+}
+
+bool SavemapManager::Save(Savemap savemap, unsigned int slot, bool force){
+    if (slot >= MAX_SAVE_SLOTS) return false;
+    if (!savemaps_read_) ReadSavemaps();
+    if (
+      !force && !saved_savemaps_[slot]->IsEmpty()
+      && savemap.GetControlKey() != saved_savemaps_[slot]->GetControlKey()
+    ){
+        return false;
+    }
+    else{
+        *current_savemap_ = savemap;
+        saved_savemaps_[slot] = current_savemap_;
+        saved_savemaps_[slot]->Write(SAVE_PATH + std::to_string(slot) + ".lua");
+        return true;
+    }
+}
+
+void SavemapManager::Release(){
+    current_savemap_ = nullptr;
+    saved_savemaps_.clear();
+    savemaps_read_ = false;
+}
+
+void SavemapManager::ReadSavemaps(){
+    saved_savemaps_.clear();
+    for (int i = 0; i < MAX_SAVE_SLOTS; i ++){
+        Savemap* savemap;
+        savemap->Read(SAVE_PATH + std::to_string(i) + ".save");
+        saved_savemaps_.push_back(savemap);
+    }
+    savemaps_read_ = true;
+}
+
+void SavemapManager::SetData(const unsigned int bank, const unsigned int address, const int value){
+    if (current_savemap_ == nullptr) current_savemap_ = new Savemap();
+    current_savemap_->SetData(bank, address, value);
+}
+
+void SavemapManager::SetControlKey(const char* control){
+    if (current_savemap_ == nullptr) current_savemap_ = new Savemap();
+    current_savemap_->SetControlKey(std::string(control));
+}
+
+void SavemapManager::SetWindowColours(
+  const unsigned int t_l_r, const unsigned int t_l_g, const unsigned int t_l_b,
+  const unsigned int t_r_r, const unsigned int t_r_g, const unsigned int t_r_b,
+  const unsigned int b_r_r, const unsigned int b_r_g, const unsigned int b_r_b,
+  const unsigned int b_l_r, const unsigned int b_l_g, const unsigned int b_l_b
+){
+    if (current_savemap_ == nullptr) current_savemap_ = new Savemap();
+    current_savemap_->SetWindowColours(
+      t_l_r, t_l_g, t_l_b, t_r_r, t_r_g, t_r_b, b_r_r, b_r_g, b_r_b, b_l_r, b_l_g, b_l_b
+    );
+}
+
+void SavemapManager::SetMoney(const unsigned int money){
+    if (current_savemap_ == nullptr) current_savemap_ = new Savemap();
+    current_savemap_->SetMoney(money);
+}
+
+void SavemapManager::SetGameTime(const unsigned int seconds){
+    if (current_savemap_ == nullptr) current_savemap_ = new Savemap();
+    current_savemap_->SetGameTime(seconds);
+}
+void SavemapManager::SetCountdownTime(const unsigned int seconds){
+    if (current_savemap_ == nullptr) current_savemap_ = new Savemap();
+    current_savemap_->SetCountdownTime(seconds);
+}
+
+void SavemapManager::SetField(const char* field){
+    if (current_savemap_ == nullptr) current_savemap_ = new Savemap();
+    current_savemap_->SetField(std::string(field));
+}
+
+void SavemapManager::SetKeyItem(const unsigned int item, const bool owned){
+    if (current_savemap_ == nullptr) current_savemap_ = new Savemap();
+    current_savemap_->SetKeyItem(item, owned);
+}
+
+void SavemapManager::SetParty(const int member_1, const int member_2, const int member_3){
+    if (current_savemap_ == nullptr) current_savemap_ = new Savemap();
+    current_savemap_->SetParty(member_1, member_2, member_3);
+}
+
+void SavemapManager::SetItem(
+  const unsigned int slot, const unsigned int id, const unsigned int quantity
+){
+    if (current_savemap_ == nullptr) current_savemap_ = new Savemap();
+    current_savemap_->SetItem(slot, id, quantity);
+}
+
+void SavemapManager::SetMateria(const unsigned int slot, const int id, const unsigned int ap){
+    if (current_savemap_ == nullptr) current_savemap_ = new Savemap();
+    current_savemap_->SetMateria(slot, id, ap);
+}
+
+void SavemapManager::SetESkillMateria(
+  const unsigned slot, const unsigned int skill, const bool learned
+){
+    if (current_savemap_ == nullptr) current_savemap_ = new Savemap();
+    current_savemap_->SetESkillMateria(slot, skill, learned);
+}
+
+void SavemapManager::SetMateriaStash(const unsigned int slot, const int id, const unsigned int ap){
+    if (current_savemap_ == nullptr) current_savemap_ = new Savemap();
+    current_savemap_->SetMateriaStash(slot, id, ap);
+}
+
+void SavemapManager::SetESkillMateriaStash(
+  const unsigned slot, const unsigned int skill, const bool learned
+){
+    if (current_savemap_ == nullptr) current_savemap_ = new Savemap();
+    current_savemap_->SetESkillMateriaStash(slot, skill, learned);
+}
+
+void SavemapManager::SetLocation(
+  const unsigned int x, const unsigned int y, const int z,
+  const unsigned int triangle, const int angle
+){
+    if (current_savemap_ == nullptr) current_savemap_ = new Savemap();
+    current_savemap_->SetLocation(x, y, z, triangle, angle);
+}
+
+void SavemapManager::SetSetting(const unsigned int key, const unsigned int value){
+    if (current_savemap_ == nullptr) current_savemap_ = new Savemap();
+    current_savemap_->SetSetting(key, value);
+}
+
+void SavemapManager::SetCharacterInfo(
+  const unsigned int id, const int char_id, const char* name,
+  const bool enabled, const bool locked,
+  const unsigned int level, const unsigned int kills,
+  const bool back_row, const unsigned int exp, const unsigned int exp_to_next,
+  const unsigned int limit_level, const unsigned int limit_bar,
+  const unsigned int weapon, const unsigned int armor, const int accessory
+){
+    if (current_savemap_ == nullptr) current_savemap_ = new Savemap();
+    current_savemap_->SetCharacterInfo(
+      id, char_id, std::string(name), enabled, locked, level, kills, back_row, exp, exp_to_next,
+      limit_level, limit_bar, weapon, armor, accessory
+    );
+}
+
+void SavemapManager::SetCharacterStat(
+  const unsigned int id, const unsigned int stat, const unsigned int base, const unsigned int extra
+){
+    if (current_savemap_ == nullptr) current_savemap_ = new Savemap();
+    current_savemap_->SetCharacterStat(id, stat, base, extra);
+}
+
+void SavemapManager::SetCharacterLimitLearned(
+  const unsigned int id, const unsigned int level,
+  const unsigned int technique, const bool learned, const unsigned int uses
+){
+    if (current_savemap_ == nullptr) current_savemap_ = new Savemap();
+    current_savemap_->SetCharacterLimitLearned(id, level, technique, learned, uses);
+}
+
+void SavemapManager::SetCharacterMateria(
+  const unsigned int id, const bool weapon, const unsigned int slot,
+  const int materia, const unsigned int ap
+){
+    if (current_savemap_ == nullptr) current_savemap_ = new Savemap();
+    current_savemap_->SetCharacterMateria(id, weapon, slot, materia, ap);
+}
+
+void SavemapManager::SetCharacterESkillMateria(
+  const unsigned int id, const bool weapon, const unsigned int slot,
+  const unsigned int skill, const bool learned
+){
+    if (current_savemap_ == nullptr) current_savemap_ = new Savemap();
+    current_savemap_->SetCharacterESkillMateria(id, weapon, slot, skill, learned);
+}
+
+void SavemapManager::SetCharacterStatus(
+  const unsigned int id, const unsigned int status, const bool inflicted
+){
+    if (current_savemap_ == nullptr) current_savemap_ = new Savemap();
+    current_savemap_->SetCharacterStatus(id, status, inflicted);
+}

+ 2 - 0
V-Gears/src/core/UiTextArea.cpp

@@ -252,6 +252,7 @@ void UiTextArea::SetText(TiXmlNode* text){
         if (language != "")
             if (TextManager::getSingleton().GetLanguage() != language) SetFont(font_->GetName());
     }
+
     TextClear();
     PrepareTextFromNode(text, colour_1_);
     text_state_ = TS_SHOW_TEXT;
@@ -582,6 +583,7 @@ float UiTextArea::GetTextWidth() const{
 }
 
 void UiTextArea::PrepareTextFromNode(TiXmlNode* node, const Ogre::ColourValue& colour){
+
     while (node != NULL){
         switch(node->Type()){
             case TiXmlNode::TINYXML_TEXT:

+ 3 - 3
V-Gears/src/core/XmlTextFile.cpp

@@ -37,9 +37,9 @@ void XmlTextFile::LoadTexts(){
             TextManager::getSingleton().AddText(name, node->Clone());
         }
         else if (node->Type() == TiXmlNode::TINYXML_ELEMENT && node->ValueStr() == "dialog"){
-            Ogre::String name = GetString( node, "name" );
-            float width = GetFloat( node, "width", 0.0f );
-            float height = GetFloat( node, "height", 0.0f );
+            Ogre::String name = GetString(node, "name");
+            float width = GetFloat(node, "width", 0.0f);
+            float height = GetFloat(node, "height", 0.0f);
             TextManager::getSingleton().AddDialog(name, node->Clone(), width, height);
         }
         node = node->NextSibling();

+ 6 - 6
data/data/fonts/ffvii_en.xml

@@ -1,7 +1,7 @@
 <font name="FFVIIFont" language="English" image="fonts/ffvii_en.png" image_size="256 256" height="16">
     <char name=" " x="0" y="0" width="12" height="12" pre="0" post="-9" />
-    <char name="!" x="12" y="0" width="12" height="12" pre="2" post="-7" />
-    <char name="&quot;" x="24" y="0" width="12" height="12" pre="2" post="-4" />
+    <char name="!" x="12" y="0" width="12" height="12" pre="0" post="-9" />
+    <char name="&quot;" x="24" y="0" width="12" height="12" pre="0" post="-6" />
     <char name="#" x="36" y="0" width="12" height="12" pre="0" post="-2" />
     <char name="$" x="48" y="0" width="12" height="12" pre="0" post="-5" />
     <char name="%" x="60" y="0" width="12" height="12" pre="0" post="-2" />
@@ -11,9 +11,9 @@
     <char name=")" x="108" y="0" width="12" height="12" pre="2" post="-4" />
     <char name="*" x="120" y="0" width="12" height="12" pre="0" post="-5" />
     <char name="+" x="132" y="0" width="12" height="12" pre="0" post="-5" />
-    <char name="," x="144" y="0" width="12" height="12" pre="1" post="-5" />
+    <char name="," x="144" y="0" width="12" height="12" pre="0" post="-8" />
     <char name="-" x="156" y="0" width="12" height="12" pre="0" post="-7" />
-    <char name="." x="168" y="0" width="12" height="12" pre="1" post="-6" />
+    <char name="." x="168" y="0" width="12" height="12" pre="0" post="-9" />
     <char name="/" x="180" y="0" width="12" height="12" pre="0" post="-6" />
     <char name="0" x="192" y="0" width="12" height="12" pre="0" post="-4" />
     <char name="1" x="204" y="0" width="12" height="12" pre="2" post="-5" />
@@ -25,12 +25,12 @@
     <char name="7" x="24" y="12" width="12" height="12" pre="0" post="-4" />
     <char name="8" x="36" y="12" width="12" height="12" pre="0" post="-4" />
     <char name="9" x="48" y="12" width="12" height="12" pre="0" post="-4" />
-    <char name=":" x="60" y="12" width="12" height="12" pre="2" post="-7" />
+    <char name=":" x="60" y="12" width="12" height="12" pre="2" post="-9" />
     <char name=";" x="72" y="12" width="12" height="12" pre="0" post="-8" />
     <char name="<" x="84" y="12" width="12" height="12" pre="0" post="-5" />
     <char name="=" x="96" y="12" width="12" height="12" pre="0" post="-4" />
     <char name=">" x="108" y="12" width="12" height="12" pre="0" post="-5" />
-    <char name="?" x="120" y="12" width="12" height="12" pre="1" post="-5" />
+    <char name="?" x="120" y="12" width="12" height="12" pre="0" post="-6" />
     <char name="@" x="132" y="12" width="12" height="12" pre="0" post="-2" />
     <char name="A" x="144" y="12" width="12" height="12" pre="0" post="-3" />
     <char name="B" x="156" y="12" width="12" height="12" pre="0" post="-5" />

+ 1 - 0
data/data/scripts.xml

@@ -13,6 +13,7 @@
     <script file="game/weapons.lua" />
 
     <script file="scripts/system.lua" />
+    <script file="scripts/save.lua" />
 
     <!-- TODO: Don't always parse initial savemap when loading is implemented. -->
     <script file="game/initial_savemap.lua" />

+ 43 - 43
data/data/scripts/battle.lua

@@ -1,36 +1,36 @@
-FFVII.Battle = {}
+Battle = {}
 
-FFVII.Battle.Action = {
+Battle.Action = {
     MONSTER_ACTION = 32,
 }
 
-FFVII.Battle.Row = {
+Battle.Row = {
     BACK = 0,
     FRONT = 1,
 }
 
-FFVII.Battle.Type = {
+Battle.Type = {
     ENEMY = 0,
     ALLY = 1,
     LOGIC = 2,
 }
 
-FFVII.Battle.ActionDefaults = {
-    [ FFVII.Battle.Action.MONSTER_ACTION ] = { animation_script = "0" }
+Battle.ActionDefaults = {
+    [ Battle.Action.MONSTER_ACTION ] = { animation_script = "0" }
 }
 
-FFVII.Battle.AttackHitChance = {
+Battle.AttackHitChance = {
     PhysicalCritical = 1, -- 0x01 upper 4 bits of formula
 }
 
-FFVII.Battle.Damage = {
+Battle.Damage = {
     Physical = 1, -- 0x01 lower 4 bits of formula
 }
 
 
 
-FFVII.Battle.logic_script = {
-    unit_type = FFVII.Battle.Type.LOGIC,
+Battle.logic_script = {
+    unit_type = Battle.Type.LOGIC,
 
     battle_speed = 0,
     game_timer = 0,
@@ -62,7 +62,7 @@ FFVII.Battle.logic_script = {
 
         -- update timers
         for key, value in pairs( EntityContainer ) do
-            if value.unit_type ~= FFVII.Battle.Type.LOGIC then
+            if value.unit_type ~= Battle.Type.LOGIC then
                 if value.timer < 65535 then
                     value.timer = value.timer + delta * 30 * value.battle_speed
                     --print( tostring( key ) .. ".timer " .. tostring( value.timer ) )
@@ -82,7 +82,7 @@ FFVII.Battle.logic_script = {
         -- update command queue
         for key_p, value_p in pairs( self.command_queue ) do
             for key_c, value_c in pairs( value_p ) do
-                FFVII.Battle.run_command( self, value_c )
+                Battle.run_command( self, value_c )
 
                 value_c.self.timer = 0
 
@@ -98,14 +98,14 @@ FFVII.Battle.logic_script = {
 
 
 
-FFVII.Battle.init = function()
-    EntityContainer.BattleLogic = FFVII.Battle.logic_script
+Battle.init = function()
+    EntityContainer.BattleLogic = Battle.logic_script
     entity_manager:add_entity_script( "BattleLogic" )
 
 
 
     -- load players
-    EntityContainer.Cloud = FFVII.Characters.Cloud
+    EntityContainer.Cloud = Characters.Cloud
     entity_manager:add_entity( "Cloud", "models/ffvii/battle/units/first_ray.mesh", 0, 2, 0, 0 -1)
 
 
@@ -116,18 +116,18 @@ FFVII.Battle.init = function()
 
 
     -- init battle speed calculated from menu settings
-    EntityContainer.BattleLogic.battle_speed = 65536 / ( ( ( FFVII.MenuSettings.battle_speed * 480 / 256 ) + 120 ) * 2 )
+    EntityContainer.BattleLogic.battle_speed = 65536 / ( ( ( MenuSettings.battle_speed * 480 / 256 ) + 120 ) * 2 )
 
 
 
     local party_speed = 0
     for key, value in pairs( EntityContainer ) do
-        if value.unit_type == FFVII.Battle.Type.ALLY then
+        if value.unit_type == Battle.Type.ALLY then
             EntityContainer.BattleLogic.ally_number = EntityContainer.BattleLogic.ally_number + 1
 
             party_speed = party_speed + value.dexterity
             value.speed = value.dexterity + 50
-        elseif value.unit_type == FFVII.Battle.Type.ENEMY then
+        elseif value.unit_type == Battle.Type.ENEMY then
             EntityContainer.BattleLogic.enemy_number = EntityContainer.BattleLogic.enemy_number + 1
         end
     end
@@ -139,7 +139,7 @@ FFVII.Battle.init = function()
         party_speed = ( party_speed - 1 + EntityContainer.BattleLogic.ally_number ) / EntityContainer.BattleLogic.ally_number + 50;
 
         for key, value in pairs( EntityContainer ) do
-            if value.unit_type ~= FFVII.Battle.Type.LOGIC then
+            if value.unit_type ~= Battle.Type.LOGIC then
                 value.battle_speed = EntityContainer.BattleLogic.battle_speed * value.speed / party_speed
                 value.battle_speed = value.battle_speed * 2 -- normal speed (initial value calculated for slow)
             end
@@ -149,7 +149,7 @@ end
 
 
 
-FFVII.Battle.run_command = function( self, command )
+Battle.run_command = function( self, command )
     print( "Command: attack = \"" .. command.attack.name .. "\", target = \"" .. tostring( command.target[ 1 ] ) .. "\"" )
 
 
@@ -168,23 +168,23 @@ FFVII.Battle.run_command = function( self, command )
 
 
 
-    if command.action == FFVII.Battle.Action.MONSTER_ACTION then
+    if command.action == Battle.Action.MONSTER_ACTION then
         -- todo action_type_07
         -- todo action_type_0C
-        FFVII.Battle.calculate_and_apply_damage()   -- action_type_09
+        Battle.calculate_and_apply_damage()   -- action_type_09
     end
 end
 
 
 
 -- action_type_09
-FFVII.Battle.calculate_and_apply_damage = function()
+Battle.calculate_and_apply_damage = function()
     -- todo mp check
     -- todo a lot of fuctions
 
 
 
-    FFVII.Battle.set_base_damage()
+    Battle.set_base_damage()
 
 
 
@@ -194,7 +194,7 @@ FFVII.Battle.calculate_and_apply_damage = function()
 
     -- attack every unit in target
     for key, value in pairs( EntityContainer.BattleLogic.temp_data.target ) do
-        FFVII.Battle.main_damage_calculation( value )
+        Battle.main_damage_calculation( value )
     end
 
 
@@ -204,7 +204,7 @@ end
 
 
 
-FFVII.Battle.set_base_damage = function()
+Battle.set_base_damage = function()
     -- todo force physical attack settings in physical formula
     -- todo use special settings for special attacks
     -- todo use another base damage for magic attack
@@ -220,12 +220,12 @@ end
 
 
 
-FFVII.Battle.main_damage_calculation = function( target )
+Battle.main_damage_calculation = function( target )
     -- todo a lot of fuctions
 
 
 
-    FFVII.Battle.calculate_target_stats( target )
+    Battle.calculate_target_stats( target )
 
 
 
@@ -233,7 +233,7 @@ FFVII.Battle.main_damage_calculation = function( target )
 
 
 
-    FFVII.Battle.damage_formula_run()
+    Battle.damage_formula_run()
 
 
 
@@ -242,7 +242,7 @@ end
 
 
 
-FFVII.Battle.calculate_target_stats = function( target )
+Battle.calculate_target_stats = function( target )
     -- todo a lot of initialization
     -- todo a lot of initialization
     -- todo defense check and modification
@@ -268,11 +268,11 @@ end
 
 
 
-FFVII.Battle.damage_formula_run = function()
+Battle.damage_formula_run = function()
     -- run high formula
-    if EntityContainer.BattleLogic.temp_data.attack.hit_chance_formula == FFVII.Battle.AttackHitChance.PhysicalCritical then
-        FFVII.Battle.HighFormula.Physical()
-        FFVII.Battle.HighFormula.Critical()
+    if EntityContainer.BattleLogic.temp_data.attack.hit_chance_formula == Battle.AttackHitChance.PhysicalCritical then
+        Battle.HighFormula.Physical()
+        Battle.HighFormula.Critical()
     end
 
 
@@ -286,8 +286,8 @@ FFVII.Battle.damage_formula_run = function()
 
 
 
-        if EntityContainer.BattleLogic.temp_data.attack.damage_formula == FFVII.Battle.Damage.Physical then
-            FFVII.Battle.LowFormula.Physical()
+        if EntityContainer.BattleLogic.temp_data.attack.damage_formula == Battle.Damage.Physical then
+            Battle.LowFormula.Physical()
         end
 
 
@@ -298,27 +298,27 @@ end
 
 
 
-FFVII.Battle.HighFormula = {}
+Battle.HighFormula = {}
 
 
 
-FFVII.Battle.HighFormula.Physical = function()
+Battle.HighFormula.Physical = function()
     -- todo add physical hit
 end
 
 
 
-FFVII.Battle.HighFormula.Critical = function()
+Battle.HighFormula.Critical = function()
     -- todo add critical hit
 end
 
 
 
-FFVII.Battle.LowFormula = {}
+Battle.LowFormula = {}
 
 
 
-FFVII.Battle.LowFormula.Physical = function()
+Battle.LowFormula.Physical = function()
     local damage = EntityContainer.BattleLogic.temp_data.base_damage
     -- calculate base damage
     damage = damage + ( ( EntityContainer.BattleLogic.temp_data.attacker.level + damage ) / 32 ) * ( ( EntityContainer.BattleLogic.temp_data.attacker.level * damage ) / 32 )
@@ -339,7 +339,7 @@ FFVII.Battle.LowFormula.Physical = function()
 
 
 
-    damage = FFVII.Battle.add_random_modifier_and_zero_check( damage )
+    damage = Battle.add_random_modifier_and_zero_check( damage )
 
     EntityContainer.BattleLogic.temp_data.calculated_damage = damage
 
@@ -348,7 +348,7 @@ end
 
 
 
-FFVII.Battle.add_random_modifier_and_zero_check = function( damage )
+Battle.add_random_modifier_and_zero_check = function( damage )
     local random_damage = damage * ( 15 + math.random() ) / 16;
 
     if random_damage == 0 then

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

@@ -1,17 +1,17 @@
 -- helper function for ai
-FFVII.Battle.in_front_row = function( self )
-    return self.row == FFVII.Battle.Row.FRONT
+Battle.in_front_row = function( self )
+    return self.row == Battle.Row.FRONT
 end
 
 
 
-FFVII.Battle.get_random_enemy_as_target = function( self )
+Battle.get_random_enemy_as_target = function( self )
     local target = {}
 
-    local enemy = FFVII.Battle.Type.ENEMY
+    local enemy = Battle.Type.ENEMY
     local number = EntityContainer.BattleLogic.enemy_number
-    if self.unit_type == FFVII.Battle.Type.ENEMY then
-        enemy = FFVII.Battle.Type.ALLY
+    if self.unit_type == Battle.Type.ENEMY then
+        enemy = Battle.Type.ALLY
         number = EntityContainer.BattleLogic.ally_number
     end
 
@@ -33,7 +33,7 @@ end;
 
 
 
-FFVII.Battle.add_command = function( self, target, action, attack )
+Battle.add_command = function( self, target, action, attack )
     local command = {
         self = self,
         target = target,

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

@@ -173,19 +173,17 @@ Materia.LINK = {
     QUADRA_MAGIC = 99
 }
 
-
-FFVII = {}
-
-
-
-FFVII.BattleType = {
+--- Battle ATB modes.
+BattleType = {
     ATB = 0,
     WAIT = 1,
 }
 
 
-
-FFVII.Data = {
+--- Game data
+--
+-- TODO: Remove, use banks.
+Data = {
     progress_game = 0,
     moving_linear = false,
     moving_jump = false,
@@ -217,37 +215,21 @@ FFVII.Data = {
 
 
 
-FFVII.Script = {
+ScriptState = {
     is_executing = false,
 }
 
 
 
-FFVII.MenuSettings = {
+MenuSettings = {
     available = false,
     pause_available = false,
 
-    battle_type = FFVII.BattleType.ATB,
+    battle_type = BattleType.ATB,
     battle_speed = 10,
 }
 
-
-
-FFVII.ItemStorage = {
-}
-
 --- Current party.
---
--- @todo: Change to Party, don't include it in FFVII, which is to be deleted.
-FFVII.Party = {
-    [1] = nil,
-    [2] = nil,
-    [3] = nil
-}
-
---- Current party.
---
--- @todo: Change from FFVII.Party, which is to be deleted.
 Party = {
     [1] = nil,
     [2] = nil,
@@ -262,7 +244,7 @@ Party = {
 Banks = {}
 for i = 0, 14 do
     Banks[i] = {}
-    for j = 0, 255 do
+    for j = 0, 254 do
         Banks[i][j] = 0
     end
 end
@@ -272,9 +254,9 @@ end
 --
 -- @todo Delete this, and implement bit operations over Banks.
 BitBanks = {}
-for i = 0, 15 do
+for i = 0, 14 do
     BitBanks[i] = {}
-    for j = 0, 255 do
+    for j = 0, 254 do
         BitBanks[i][j] = {}
         for k = 0, 7 do
             BitBanks[i][j][k] = 0

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

@@ -7,6 +7,7 @@ export_character_names()
 -- @param point_name Spawn point name in the target map for the PC.
 load_field_map_request = function(map_name, point_name)
     if type(map_name) == "string" and map_name ~= "" then
+
         System.MapChanger.map_name = map_name
         System.MapChanger.point_name = point_name
         script:request(Script.SYSTEM, "MapChanger", "ffvii_field", 0)
@@ -18,7 +19,7 @@ end
 -- @param entity_name The entity name.
 -- @param character_id Id of the character to assign.
 set_entity_to_character = function(entity_name, character_id)
-    if character_id == FFVII.Party[1] and entity_name ~= "" then
+    if character_id == Party[1] and entity_name ~= "" then
         if System.MapChanger.point_name ~= "" then
             local point = entity_manager:get_entity_point(System.MapChanger.point_name)
             if point ~= nil then
@@ -47,8 +48,8 @@ join_party = function(speed)
     end
     local x, y, z = player:get_position()
     for c = 2, 3 do
-        if FFVII.Party[c] ~= nil then
-            local character = entity_manager:get_entity_from_character_id(FFVII.Party[c])
+        if Party[c] ~= nil then
+            local character = entity_manager:get_entity_from_character_id(Party[c])
             --print("    Name: " .. character:get_name())
             if character ~= nil then
                 local cur_speed = character:get_move_auto_speed()
@@ -85,8 +86,8 @@ split_party = function(ax, ay, ar, bx, by, br, speed)
     local player = entity_manager:get_player_entity()
     local x, y, z = player:get_position()
     for c = 2, 3 do
-        if FFVII.Party[c] ~= nil then
-            local character = entity_manager:get_entity_from_character_id(FFVII.Party[c])
+        if Party[c] ~= nil then
+            local character = entity_manager:get_entity_from_character_id(Party[c])
             if character ~= nil then
                 local cur_speed = character:get_move_auto_speed()
                 -- Approximated speed, good enough for now.
@@ -123,7 +124,7 @@ end
 -- @param lock True to lock, false to unlock.
 player_lock = function(lock)
     entity_manager:player_lock(lock)
-    FFVII.MenuSettings.available = (lock == false)
+    MenuSettings.available = (lock == false)
 end
 
 --- Sets a bit variable on (1).

+ 10 - 9
data/data/scripts/menu/begin_menu.lua

@@ -17,6 +17,7 @@ UiContainer.BeginMenu = {
 
 
     on_button = function( self, button, event )
+
         if ui_manager:get_widget( "BeginMenu" ):is_visible() ~= false then
             local cursor = ui_manager:get_widget( "BeginMenu.Container.Cursor" )
 
@@ -27,16 +28,16 @@ UiContainer.BeginMenu = {
                     console( "camera_free false" )
                     --console( "debug_walkmesh true" )
                     script:request_end_sync( Script.UI, "BeginMenu", "hide", 0 )
-                    FFVII.MenuSettings.pause_available = true
+                    MenuSettings.pause_available = true
                 elseif self.position == 2 then
                     script:request_end_sync( Script.UI, "BeginMenu", "hide", 0 )
                     console( "camera_free true" )
                     console( "debug_walkmesh true" )
                     map( "test_3" )
-                    FFVII.MenuSettings.pause_available = true
+                    MenuSettings.pause_available = true
                 elseif self.position == 3 then
                     -- NMKIN_3 START
-                    --[[FFVII.Data.progress_game = 23
+                    Data.progress_game = 27
                     load_field_map_request("nmkin_3", "")
                     console( "camera_free false" )
                     console( "debug_walkmesh true" )
@@ -49,10 +50,10 @@ UiContainer.BeginMenu = {
                     entity_manager:get_entity("Cloud"):jump_to_position(0.46875, 14.9922, -1, 1, 42) -- Triangle 42, 2 steps.
                     entity_manager:get_entity("Cloud"):jump_sync()
                     entity_manager:player_lock(false)
-                    FFVII.MenuSettings.pause_available = true]]
+                    MenuSettings.pause_available = true
                     -- NMKIN_3 END
                     -- NMKIN_4 START
-                    FFVII.Data.progress_game = 14
+                    --[[Data.progress_game = 14
                     --load_field_map_request("nmkin_4", "")
                     map("nmkin_4")
                     console("camera_free false")
@@ -61,21 +62,21 @@ UiContainer.BeginMenu = {
                     script:request_end_sync( Script.UI, "BeginMenu", "hide", 0 )
                     entity_manager:get_entity("Cloud"):set_position(-0.820312, 0.500000, 17.281244)
                     entity_manager:set_player_entity("Cloud")
-                    FFVII.set_party(0, 1, 4)
+                    set_party(0, 1, 4)
                     script:wait(0.5)
                     entity_manager:get_player_entity():set_position(-0.820312, 0.500000, 17.281244)
                     entity_manager:get_entity("Cloud"):jump_to_position(-0.800312, 0.500000, -1, 1) -- Triangle 42, 2 steps.
                     entity_manager:get_entity("Cloud"):jump_sync()
                     entity_manager:player_lock(false)
                     print("TRIANGLE " .. entity_manager:get_entity("Cloud"):get_move_triangle_id())
-                    FFVII.MenuSettings.pause_available = true
+                    MenuSettings.pause_available = true]]
                     -- NMKIN_4 END
                 elseif self.position == 4 then
                     script:request_end_sync( Script.UI, "BeginMenu", "hide", 0 )
                     console( "camera_free true" )
                     console( "debug_walkmesh true" )
                     map( "test_2" )
-                    FFVII.MenuSettings.pause_available = true
+                    MenuSettings.pause_available = true
                 elseif self.position == 5 then
                     script:request_end_sync( Script.UI, "BeginMenu", "hide", 0 )
                     console( "camera_free true" )
@@ -85,7 +86,7 @@ UiContainer.BeginMenu = {
                     console( "camera_free false" )
                     console( "debug_walkmesh false" )
                     script:request_end_sync( Script.UI, "BeginMenu", "hide", 0 )
-                    FFVII.MenuSettings.pause_available = true
+                    MenuSettings.pause_available = true
                 end
             elseif button == "Down" then
                 audio_manager:play_sound("Cursor")

+ 4 - 4
data/data/scripts/menu/item_menu.lua

@@ -463,10 +463,10 @@ UiContainer.ItemMenu = {
         UiContainer.current_menu = "item"
         self.char_position_total = 0
         for c = 1, 3 do
-            if FFVII.Party[c] ~= nil then
+            if Party[c] ~= nil then
                 self.char_position_total = self.char_position_total + 1
-                UiContainer.populate_character_data("ItemMenu.Container.ItemMenuCharacters.Character" .. tostring(c), Characters[FFVII.Party[c]])
-                ui_manager:get_widget("ItemMenu.Container.ItemMenuCharacters.Character" .. tostring(c) .. ".Portrait"):set_image("images/characters/" .. tostring(FFVII.Party[c]) .. ".png")
+                UiContainer.populate_character_data("ItemMenu.Container.ItemMenuCharacters.Character" .. tostring(c), Characters[Party[c]])
+                ui_manager:get_widget("ItemMenu.Container.ItemMenuCharacters.Character" .. tostring(c) .. ".Portrait"):set_image("images/characters/" .. tostring(Party[c]) .. ".png")
             else
                 UiContainer.populate_character_data("ItemMenu.Container.ItemMenuCharacters.Character" .. tostring(c), nil)
             end
@@ -581,7 +581,7 @@ UiContainer.ItemMenu = {
         char_id = -1
         if character_pos then
             -- Normal item, use in one character.
-            char_id = FFVII.Party[character_pos]
+            char_id = Party[character_pos]
         end
 
         -- TODO: When HP MAX is fixed, use it instead of base.

+ 9 - 9
data/data/scripts/menu/main_menu.lua

@@ -104,10 +104,10 @@ UiContainer.MainMenu = {
                     if self.select_character_for_menu == 2 then -- Magic menu
                         print("Open magic menu for char in slot " .. self.character_position)
                     elseif self.select_character_for_menu == 3 then -- Materia menu
-                        UiContainer.current_character_id = FFVII.Party[self.character_position]
+                        UiContainer.current_character_id = Party[self.character_position]
                         script:request_end_sync(Script.UI, "MateriaMenu", "show", 0)
                     elseif self.select_character_for_menu == 4 then -- Equip menu
-                        UiContainer.current_character_id = FFVII.Party[self.character_position]
+                        UiContainer.current_character_id = Party[self.character_position]
                         script:request_end_sync(Script.UI, "EquipMenu", "show", 0)
                     elseif self.select_character_for_menu == 5 then -- Status menu
                         print("Open status menu for char in slot " .. self.character_position)
@@ -118,7 +118,7 @@ UiContainer.MainMenu = {
                     end
                 end
             end
-        elseif ui_manager:get_widget("MainMenu"):is_visible() == false and FFVII.MenuSettings.available == true then
+        elseif ui_manager:get_widget("MainMenu"):is_visible() == false and MenuSettings.available == true then
             if button == "Escape" and event == "Press" then
                 audio_manager:play_sound("Back")
                 script:request_end_sync(Script.UI, "MainMenu", "show", 0)
@@ -137,7 +137,7 @@ UiContainer.MainMenu = {
         UiContainer.current_menu = "main"
         UiContainer.current_submenu = ""
         entity_manager:set_paused(true)
-        FFVII.MenuSettings.pause_available = false
+        MenuSettings.pause_available = false
         ui_manager:get_widget("MainMenu"):set_visible(true)
         local characters  = ui_manager:get_widget("MainMenu.Container.Characters")
         local menu = ui_manager:get_widget("MainMenu.Container.Menu")
@@ -147,10 +147,10 @@ UiContainer.MainMenu = {
 
 
         for c = 1, 3 do
-            if FFVII.Party[c] ~= nil then
-                UiContainer.populate_character_data("MainMenu.Container.Characters.Character" .. tostring(c), Characters[FFVII.Party[c]])
-                ui_manager:get_widget("MainMenu.Container.Characters.Character" .. tostring(c) .. ".Portrait"):set_image("images/characters/" .. tostring(FFVII.Party[c]) .. ".png")
-                if Characters[FFVII.Party[c]].back_row == 1 then
+            if Party[c] ~= nil then
+                UiContainer.populate_character_data("MainMenu.Container.Characters.Character" .. tostring(c), Characters[Party[c]])
+                ui_manager:get_widget("MainMenu.Container.Characters.Character" .. tostring(c) .. ".Portrait"):set_image("images/characters/" .. tostring(Party[c]) .. ".png")
+                if Characters[Party[c]].back_row == 1 then
                     ui_manager:get_widget("MainMenu.Container.Characters.Character" .. tostring(c) .. ".Portrait"):set_default_animation("RowBack")
                 else
                     ui_manager:get_widget("MainMenu.Container.Characters.Character" .. tostring(c) .. ".Portrait"):set_default_animation("RowFront")
@@ -200,7 +200,7 @@ UiContainer.MainMenu = {
 
         ui_manager:get_widget("MainMenu"):set_visible(false)
 
-        FFVII.MenuSettings.pause_available = true
+        MenuSettings.pause_available = true
         entity_manager:set_paused(false)
 
         return 0;

+ 2 - 2
data/data/scripts/menu/name_menu.lua

@@ -167,7 +167,7 @@ UiContainer.NameMenu = {
     -- effect.
     show = function(self)
         entity_manager:set_paused(true)
-        FFVII.MenuSettings.pause_available = false
+        MenuSettings.pause_available = false
         script:request_end_sync(Script.UI, "Fade", "fade_out", 0)
         ui_manager:get_widget("NameMenu"):set_visible(true)
         UiContainer.current_menu = "name"
@@ -278,7 +278,7 @@ UiContainer.NameMenu = {
         ui_manager:get_widget("NameMenu"):set_visible(false)
         UiContainer.current_menu = ""
         UiContainer.current_submenu = ""
-        FFVII.MenuSettings.pause_available = true
+        MenuSettings.pause_available = true
         entity_manager:set_paused(false)
         script:request_end_sync(Script.UI, "Fade", "fade_in", 0)
         return 0;

+ 5 - 5
data/data/scripts/menu/pause_menu.lua

@@ -24,14 +24,14 @@ UiContainer.PauseMenu = {
                 if self.position == 1 then
                     script:request_end_sync( Script.UI, "BeginMenu", "show", 0 )
                     map( "empty" )
-                    FFVII.MenuSettings.pause_available = false
+                    MenuSettings.pause_available = false
                 else
-                    FFVII.MenuSettings.available = true
+                    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 )
-                FFVII.MenuSettings.available = true
+                MenuSettings.available = true
             elseif button == "Right" then
                 audio_manager:play_sound("Cursor")
                 self.position = self.position + 1
@@ -47,10 +47,10 @@ UiContainer.PauseMenu = {
                 end
                 menu_cursor:set_default_animation( "Position" .. self.position )
             end
-        elseif FFVII.MenuSettings.pause_available == true then
+        elseif MenuSettings.pause_available == true then
             if button == "Enter" and event == "Press" then
                 audio_manager:play_sound("Cursor")
-                FFVII.MenuSettings.available = false
+                MenuSettings.available = false
                 script:request_end_sync( Script.UI, "PauseMenu", "show", 0 )
             end
         end

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

@@ -0,0 +1,86 @@
+save_game = function(slot)
+    savemap_manager:set_control_key("CONTROL_EXAMPLE") -- TODO Actual data
+    savemap_manager:set_window_colours(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) -- TODO Actual data
+    savemap_manager:set_money(Inventory.money)
+    savemap_manager:set_game_time(123456) -- TODO Actual data
+    savemap_manager:set_countdown_time(0) -- TODO Actual data
+    savemap_manager:set_field("FIELD_ID") -- TODO Actual data
+    for i = 1, 50 do
+       if Inventory.key[i] == true then
+           savemap_manager:set_key_item(i - 1, true)
+       end
+    end
+    savemap_manager:set_party(Party[1] or -1, Party[2] or -1, Party[3] or -1)
+    for i = 1, MAX_INVENTORY_SLOTS do
+        if Inventory[i] ~= nil then
+            savemap_manager:set_item(i - 1, Inventory[i].item, Inventory[i].quantity)
+        end
+    end
+    for i = 1, MAX_MATERIA_SLOTS do
+        if Materia[i] ~= nil then
+            savemap_manager:set_materia(i - 1, Materia[i].id, Materia[i].ap)
+            if Materia[i].id == Materia.ENEMY_SKILL_ID then
+                for s = 1, 24 do
+                    if Materia[i].skills[s] == true then
+                        savemap_manager:set_e_skill_materia(i - 1, s, true)
+                    end
+                end
+            end
+        end
+    end
+    -- TODO: Materia stash, when implemented.
+    savemap_manager:set_location(10, 20, -1, 3, 120) -- TODO Actual data
+    -- TODO Settings, when implemented
+    for i = 0, 11 do
+        if Characters[i] ~= nil and Characters[i].char_id ~= nil then
+            local c = Characters[i]
+            savemap_manager:set_character_info(i, c.char_id, c.name, c.enabled, c.locked, c.level, c.kills, c.back_row, c.exp, c.exp_to_next, c.limit.current, c.limit.bar, c.weapon.id, c.armor.id, c.accessory or -1)
+            savemap_manager:set_character_stat(i, 0, c.stats.str.base, c.stats.str.bonus)
+            savemap_manager:set_character_stat(i, 1, c.stats.vit.base, c.stats.vit.bonus)
+            savemap_manager:set_character_stat(i, 2, c.stats.mag.base, c.stats.mag.bonus)
+            savemap_manager:set_character_stat(i, 3, c.stats.spr.base, c.stats.spr.bonus)
+            savemap_manager:set_character_stat(i, 4, c.stats.dex.base, c.stats.dex.bonus)
+            savemap_manager:set_character_stat(i, 5, c.stats.lck.base, c.stats.lck.bonus)
+            savemap_manager:set_character_stat(i, 6, c.stats.hp.base, c.stats.hp.current)
+            savemap_manager:set_character_stat(i, 7, c.stats.mp.base, c.stats.mp.current)
+            for l = 1, 4 do
+                savemap_manager:set_character_limit_learned(i, l - 1, 0, c.limit.learned[l][1], c.limit.uses[l])
+                savemap_manager:set_character_limit_learned(i, l - 1, 1, c.limit.learned[l][2], c.limit.uses[l])
+            end
+            for m = 0, 8 do
+                if c.weapon.materia[m] ~= nil then
+                    savemap_manager:set_character_materia(i, true, m, c.weapon.materia[m].id, c.weapon.materia[m].ap)
+                    if c.weapon.materia[m].id == Materia.ENEMY_SKILL_ID then
+                        for s = 1, 24 do
+                            if c.weapon.materia[m].skills[s] == true then
+                                savemap_manager:set_character_e_skill_materie(i, true, m, s, true)
+                            end
+                        end
+                    end
+                end
+                if c.armor[m] ~= nil then
+                    savemap_manager:set_character_materia(i, false, m, c.armor.materia[m].id, c.weapon.materia[m].ap)
+                    if c.weapon.materia[m].id == Materia.ENEMY_SKILL_ID then
+                        for s = 1, 24 do
+                            if c.armor.materia[m].skills[s] == true then
+                                savemap_manager:set_character_e_skill_materie(i, true, m, s, true)
+                            end
+                        end
+                    end
+                end
+            end
+            -- TODO: Character statuses
+        end
+    end
+    print("TO_DATA")
+    for b = 0, 14 do
+        for a = 0, 254 do
+            if (Banks[b] ~= nil and Banks[b][a] ~= nil and Banks[b][a] ~= 0) then
+                print("DATA " .. tostring(b) .. "." .. tostring(a))
+                savemap_manager:set_data(b, a, tonumber(Banks[b][a]))
+            end
+
+        end
+    end
+    savemap_manager:save(slot, false)
+end

+ 38 - 35
data/data/scripts/system.lua

@@ -18,20 +18,20 @@ end
 -- Used to compose dialog with party character names. Must be called when the party formation is
 -- changed in any way.
 export_party_members = function()
-    if FFVII.Party[1] == nil then
+    if Party[1] == nil then
         c1 = -1
     else
-        c1 = FFVII.Party[1]
+        c1 = Party[1]
     end
-    if FFVII.Party[2] == nil then
+    if Party[2] == nil then
         c2 = -1
     else
-        c2 = FFVII.Party[2]
+        c2 = Party[2]
     end
-    if FFVII.Party[3] == nil then
+    if Party[3] == nil then
         c3 = -1
     else
-        c3 = FFVII.Party[3]
+        c3 = Party[3]
     end
     text_manager:set_party(c1, c2, c3)
 end
@@ -41,10 +41,13 @@ end
 -- @param character1 The first member character ID, the party leader.
 -- @param character2 The second member character ID, can be nil.
 -- @param character3 The third member character ID, can be nil.
-FFVII.set_party = function(character1, character2, character3)
-    FFVII.Party[1] = character1
-    FFVII.Party[2] = character2
-    FFVII.Party[3] = character3
+set_party = function(character1, character2, character3)
+    Party[1] = character1
+    Party[2] = character2
+    Party[3] = character3
+    Party[1] = character1
+    Party[2] = character2
+    Party[3] = character3
     export_party_members()
 end
 
@@ -52,8 +55,8 @@ end
 --
 -- @param character The ID to check.
 -- @return True if the ID is a valid character ID, false otherwise.
-FFVII.is_character = function(character)
-    if FFVII.Characters[character] == nil then
+is_character = function(character)
+    if Characters[character] == nil then
         return false
     end
     return true
@@ -62,26 +65,26 @@ end
 --- Adds a character to the curent party.
 --
 -- @param character ID of the character to add.
-FFVII.add_party_character = function(character)
-    if FFVII.Characters[character] == nil then
+add_party_character = function(character)
+    if Characters[character] == nil then
         print("Tried add to party invalid character \"" .. character .. "\".");
         return
     end
-    if FFVII.Characters[character].party_lock == true then
+    if Characters[character].party_lock == true then
         print("Tried add to party locked character \"" .. character .. "\".");
         return
     end
     -- Check if character already in party
     for x = 1, 3 do
-        if FFVII.Party[i] == character then
+        if Party[i] == character then
             print("Character \"" .. character .. "\" already in party.");
             return
         end
     end
     -- Try to add in first free slot.
     for x = 1, 3 do
-        if FFVII.Party[i] == nil then
-            FFVII.Party[i] = character
+        if Party[i] == nil then
+            Party[i] = character
             export_party_members()
             return
         end
@@ -91,18 +94,18 @@ end
 --- Removes a character from the curent party.
 --
 -- @param character ID of the character to remove.
-FFVII.remove_party_character = function(character)
-    if FFVII.Characters[character] == nil then
+remove_party_character = function(character)
+    if Characters[character] == nil then
         print("Tried remove from party invalid character \"" .. character .. "\".");
         return
     end
-    if FFVII.Characters[character].party_lock == true then
+    if Characters[character].party_lock == true then
         print("Tried remove from party locked character \"" .. character .. "\".");
         return
     end
     for x = 1, 3 do
-        if FFVII.Party[i] == character then
-            FFVII.Party[i] = nil
+        if Party[i] == character then
+            Party[i] = nil
             export_party_members()
             return
         end
@@ -114,12 +117,12 @@ end
 -- Locked characters can't be manually  swapped in or out of the party.
 --
 -- @param character ID of the character to lock.
-FFVII.set_party_lock = function(character, lock)
-    if FFVII.Characters[character] == nil then
+set_party_lock = function(character, lock)
+    if Characters[character] == nil then
         print("Tried set/unset party lock to invalid character \"" .. character .. "\".");
         return
     end
-    FFVII.Characters[character].party_lock = lock;
+    Characters[character].party_lock = lock;
 end
 
 --- Checks if a character is locked in the party.
@@ -127,12 +130,12 @@ end
 -- Locked characters can't be manually  swapped in or out of the party.
 --
 -- @param character ID of the character to check for locks.
-FFVII.get_party_lock = function(character)
-    if FFVII.Characters[character] == nil then
+get_party_lock = function(character)
+    if Characters[character] == nil then
         print("Tried get party lock from invalid character \"" .. character .. "\".");
         return false
     end
-    return FFVII.Characters[character].party_lock
+    return Characters[character].party_lock
 end
 
 --- Enables or disables a character is the PHS.
@@ -141,12 +144,12 @@ end
 --
 -- @param character ID of the character to enable or disable.
 -- @param enable True to enable, false to disable.
-FFVII.set_character_enable = function(character, enable)
-    if FFVII.Characters[character] == nil then
+set_character_enable = function(character, enable)
+    if Characters[character] == nil then
         print("Tried enable/disable invalid character \"" .. character .. "\".");
         return
     end
-    FFVII.Characters[character].enable = enable;
+    Characters[character].enable = enable;
 end
 
 --- Checks if a character is enabled in the PHS.
@@ -155,12 +158,12 @@ end
 --
 -- @param character ID of the character to check.
 -- @return True if enabled, false if disables.
-FFVII.get_character_enable = function(character)
-    if FFVII.Characters[character] == nil then
+get_character_enable = function(character)
+    if Characters[character] == nil then
         print("Tried get enabled from invalid character \"" .. character .. "\".");
         return false
     end
-    return FFVII.Characters[character].enable;
+    return Characters[character].enable;
 end
 
 --- Adds items to the inventory.

+ 1 - 1
doc/STYLE-LUA.md

@@ -104,7 +104,7 @@ Document every block of code with JavaDoc-style docstrings, for doxygen, like th
  @return 1 or 0
 ]]
 bit = function(bank, variable, bit)
-    return FFVII.BitBanks[bank + 1][variable + 1][bit + 1]
+    return BitBanks[bank + 1][variable + 1][bit + 1]
 end
 ```