Selaa lähdekoodia

Battle entities can be loaded into the BattleManager and presented in 3D space.

Iñigo Valentin 3 vuotta sitten
vanhempi
sitoutus
aa937130f1

+ 3 - 0
data/resources.cfg

@@ -9,6 +9,9 @@ FileSystem=data
 
 [FFVIITextures]
 FileSystem=data/models/field/entities/
+FileSystem=data/models/battle/enemy/
+FileSystem=data/models/battle/char/
+FileSystem=data/models/battle/scene/
 
 [Images]
 FileSystem=data/images/

+ 68 - 3
src/core/BattleManager.cpp

@@ -13,12 +13,14 @@
  * GNU General Public License for more details.
  */
 
+#include <algorithm>
 #include <iostream>
 #include <cmath>
 #include <OgreEntity.h>
 #include <OgreRoot.h>
 #include <OgreViewport.h>
 #include "core/BattleManager.h"
+#include "core/CameraManager.h"
 #include "core/Enemy.h"
 #include "core/EntityManager.h"
 #include "core/ConfigVar.h"
@@ -33,6 +35,8 @@ template<>BattleManager *Ogre::Singleton<BattleManager>::msSingleton = nullptr;
 ConfigVar cv_debug_battle_grid("debug_battle_grid", "Draw debug battle grid", "false");
 ConfigVar cv_debug_battle_axis("debug_battle_axis", "Draw debug battle axis", "false");
 
+const float BattleManager::MODEL_SCALE = 0.0015f;
+
 BattleManager::BattleManager(): paused_(false){
     LOG_TRIVIAL("BattleManager created.");
     scene_node_ = Ogre::Root::getSingleton().getSceneManager("Scene")
@@ -56,14 +60,30 @@ void BattleManager::StartBattle(const unsigned int id){
     filename = "./data/game/formation/" + filename + ".xml";
     // This sets the data in the battle manager singleton.
     XmlFormationFile(filename).LoadFormation();
-    // TODO: Read formation file, enemy files...
     // TODO: Music
-    // TODO: Camera
+    if (camera_.size() == 0)
+        LOG_ERROR("Unable to start battle camera. No cameras defined in the BattleManager.");
+    else if (initial_camera_ > camera_.size()){
+        LOG_ERROR(
+          "Unable to set initial battle camera. Default camera is set to "
+          + std::to_string(initial_camera_) + " but only " + std::to_string(camera_.size())
+          + " cameras are defined in the CameraManager. Defaulting to first camera.");
+        CameraManager::getSingleton().StartBattleCamera(
+          camera_.at(0).location, camera_.at(0).orientation
+        );
+    }
+    else
+        CameraManager::getSingleton().StartBattleCamera(
+          camera_.at(initial_camera_).location, camera_.at(initial_camera_).orientation
+        );
+    LoadParty();
+    //EndBattle();// TODO: Debug
 }
 
 void BattleManager::EndBattle(){
     formation_id_ = -1;
     EntityManager::getSingleton().SetPreviousModule();
+    CameraManager::getSingleton().EndBattleCamera();
 }
 
 std::vector<Enemy> BattleManager::GetEnemies() const{return enemies_;}
@@ -74,12 +94,29 @@ void BattleManager::AddEnemy(
 ){
     Enemy* enemy = new Enemy(id, pos, front, visible, targeteable, active, cover);
     enemies_.push_back(*enemy);
+    EntityManager::getSingleton().AddBattleEntity(
+      enemy->GetName() + "_" + std::to_string(enemies_.size() - 1),
+      "models/battle/enemy/" + enemy->GetModel() + ".mesh", enemy->GetPos(), Ogre::Degree(0),
+      Ogre::Vector3(MODEL_SCALE, MODEL_SCALE, MODEL_SCALE), id, visible
+    );
+
+    /*EntityManager::getSingleton().AddBattleEntity(
+      enemy->GetName() + "_" + std::to_string(enemies_.size() - 1),
+      "models/battle/enemy/" + enemy->GetModel() + ".mesh",
+      Ogre::Vector3(
+        28.554688 + 5 * (enemies_.size() - 1), 214.312500 + 10 * (enemies_.size() - 1), 2.421875
+      ), Ogre::Degree(0), Ogre::Vector3(0.0015, 0.0015, 0.0015), id, visible
+    );*/
 }
 
 void BattleManager::AddCamera(
   const unsigned int id, const Ogre::Vector3 pos, const Ogre::Vector3 dir
 ){
-    // TODO implrement
+    BattleCamera camera;
+    camera.id = id;
+    camera.location = pos;
+    camera.orientation = dir;
+    camera_.push_back(camera);
 }
 
 void BattleManager::Input(const VGears::Event& event){
@@ -161,3 +198,31 @@ void BattleManager::SetLocation(const int id, const Ogre::String name){
 void BattleManager::SetArenaBattle(const bool arena){arena_battle_ = arena;}
 
 void BattleManager::SetInitialCamera(const unsigned int id){initial_camera_ = id;}
+
+void BattleManager::LoadParty(){
+    std::vector<int> positions {0, 1, 2};
+    Ogre::Vector3 position = Ogre::Vector3(0, 5, 0);
+    // Randomize party member positions.
+    std::random_shuffle(positions.begin(), positions.end());
+    for (int i = 0; i < positions.size(); i ++){
+        EntityManager::getSingleton().AddBattleEntity(
+          "party_" + std::to_string(i),
+          "models/fields/entities/avfe.mesh", position, Ogre::Degree(0),
+          //Ogre::Vector3(MODEL_SCALE, MODEL_SCALE, MODEL_SCALE), 100 + i, true
+          Ogre::Vector3(0.1, 0.1, 0.1), 100 + i, true
+        );
+
+        // Next position in Y axis
+        position.x += ((i + 1) * (i % 2 == 0 ? 5 : -5));
+
+
+    }
+
+    EntityManager::getSingleton().AddBattleEntity(
+          "center",
+          "models/fields/entities/avfe.mesh", position, Ogre::Degree(0),
+          //Ogre::Vector3(MODEL_SCALE, MODEL_SCALE, MODEL_SCALE), 100 + i, true
+          Ogre::Vector3(0.01, 0.01, 0.01), 999, true
+        );
+
+}

+ 35 - 2
src/core/BattleManager.h

@@ -123,6 +123,29 @@ class BattleManager : public Ogre::Singleton<BattleManager>{
             unsigned int qty;
         };
 
+        /**
+         * A battle camera settings.
+         */
+        struct BattleCamera{
+
+            /**
+             * Camera ID.
+             */
+            unsigned int id;
+
+            /**
+             * Camera location.
+             */
+            Ogre::Vector3 location;
+
+            /**
+             * Camera orientation.
+             *
+             * It's not an angle, or a rotation. but the coordinates the camera points at.
+             */
+            Ogre::Vector3 orientation;
+        };
+
         /**
          * Constructor.
          */
@@ -278,7 +301,7 @@ class BattleManager : public Ogre::Singleton<BattleManager>{
          *
          * @param[in] id Camera ID.
          * @param[in] pos Camera position (x, y, z).
-         * @param[in] dir Camera orientation (x, y, z).
+         * @param[in] dir Camera orientation (a point the camera will point to).
          */
         void AddCamera(
           const unsigned int id, const Ogre::Vector3 pos, const Ogre::Vector3 dir
@@ -286,6 +309,16 @@ class BattleManager : public Ogre::Singleton<BattleManager>{
 
     private:
 
+        /**
+         * Loads the party members into the battle.
+         */
+        void LoadParty();
+
+        /**
+         * Scale factor for all battle models.
+         */
+        static const float MODEL_SCALE;
+
         /**
          * The scene node.
          */
@@ -310,7 +343,7 @@ class BattleManager : public Ogre::Singleton<BattleManager>{
         //BattleLocation location_;
 
         // TODO: Camera list
-        //std::vector<BattleCamera> camera_;
+        std::vector<BattleCamera> camera_;
 
         /**
          * Default camera ID

+ 75 - 0
src/core/CameraManager.cpp

@@ -13,6 +13,7 @@
  * GNU General Public License for more details.
  */
 
+#include <iostream>
 #include <OgreRenderWindow.h>
 #include <OgreRoot.h>
 #include <OgreViewport.h>
@@ -32,6 +33,7 @@ ConfigVar cv_cam_speed("camera_speed", "Camera speed", "0.02");
 template<>CameraManager* Ogre::Singleton<CameraManager>::msSingleton = nullptr;
 
 CameraManager::CameraManager():
+  battle_(false),
   camera_free_(false),
   camera_free_rotate_(false),
   d2_position_(Ogre::Vector3::ZERO),
@@ -43,8 +45,15 @@ CameraManager::CameraManager():
     InitCommands();
     camera_ = Ogre::Root::getSingleton().getSceneManager("Scene")
       ->createCamera("Camera");
+    position_initial_
+      = Ogre::Vector3(camera_->getPosition().x, camera_->getPosition().y, camera_->getPosition().z);
+    orientation_initial_ = Ogre::Quaternion(
+      camera_->getOrientation().w, camera_->getOrientation().x,
+      camera_->getOrientation().z, camera_->getOrientation().z
+    );
     camera_->setNearClipDistance(0.001f);
     camera_->setFarClipDistance(1000.0f);
+    //camera_->setFixedYawAxis(true, Ogre::Vector3::UNIT_Y);
     //camera_->setPosition(Ogre::Vector3(0, 0, 0));
     Ogre::Root::getSingleton().getSceneManager("Scene")->getRootSceneNode()
       ->setPosition(Ogre::Vector3(0, 0, 0));
@@ -193,6 +202,56 @@ void CameraManager::Set2DCamera(
     Set2DScroll(Ogre::Vector2::ZERO);
 }
 
+void CameraManager::StartBattleCamera(
+  const Ogre::Vector3 position, const Ogre::Vector3 orientation
+){
+    if (battle_){
+        LOG_ERROR("Tried to start battle camera but the CameraManager is already in battle mode");
+        return;
+    }
+    battle_ = true;
+    std::cout << "CAMERA BATTLE START: " << camera_->getPosition().x << ", " << camera_->getPosition().y << ", " << camera_->getPosition().z << ", "
+        << camera_->getOrientation().w << ", "<< camera_->getOrientation().x << ", " << camera_->getOrientation().y << ", "
+        << camera_->getOrientation().z << std::endl;
+    position_backup_ = Ogre::Vector3(
+      camera_->getPosition().x, camera_->getPosition().y,
+      camera_->getPosition().z
+    );
+    orientation_backup_ = Ogre::Quaternion(
+      camera_->getOrientation().w, camera_->getOrientation().x,
+      camera_->getOrientation().y, camera_->getOrientation().z
+    );
+    camera_->setPosition(position);
+    //CameraManager::getSingleton().GetCurrentCamera()->setFixedYawAxis(true, Ogre::Vector3::UNIT_Y);
+    camera_->lookAt(Ogre::Vector3(orientation.x, orientation.y, camera_->getPosition().z));
+    camera_->lookAt(orientation);
+
+
+    std::cout << "CAMERA BATTLE START FINISH: " << camera_->getPosition().x << ", " << camera_->getPosition().y << ", " << camera_->getPosition().z << ", "
+            << camera_->getOrientation().w << ", "<< camera_->getOrientation().x << ", " << camera_->getOrientation().y << ", "
+            << camera_->getOrientation().z << std::endl;
+}
+
+void CameraManager::EndBattleCamera(){
+    if (!battle_){
+        LOG_ERROR("Tried to end battle camera but the CameraManager is not in battle mode");
+        return;
+    }
+    battle_ = false;
+        std::cout << "CAMERA BATTLE END: " << camera_->getPosition().x << ", " << camera_->getPosition().y << ", " << camera_->getPosition().z << ", "
+            << camera_->getOrientation().w << ", "<< camera_->getOrientation().x << ", " << camera_->getOrientation().y << ", "
+            << camera_->getOrientation().z << std::endl;
+    CameraManager::getSingleton().GetCurrentCamera()->setPosition(
+      Ogre::Vector3(position_backup_.x, position_backup_.y, position_backup_.z)
+    );
+    CameraManager::getSingleton().GetCurrentCamera()->setOrientation(Ogre::Quaternion(
+      orientation_backup_.w, orientation_backup_.x, orientation_backup_.y, orientation_backup_.z
+    ));
+    std::cout << "CAMERA BATTLE END FINISH: " << camera_->getPosition().x << ", " << camera_->getPosition().y << ", " << camera_->getPosition().z << ", "
+                << camera_->getOrientation().w << ", "<< camera_->getOrientation().x << ", " << camera_->getOrientation().y << ", "
+                << camera_->getOrientation().z << std::endl;
+}
+
 void CameraManager::Set2DScroll(const Ogre::Vector2& position){
     d2_scroll_ = position;
     if(camera_free_ == true) return;
@@ -242,3 +301,19 @@ void CameraManager::EnableWireFrame(bool enable){
         camera_->setFarClipDistance(900000000);
     }
 }
+
+void CameraManager::ScriptSetCamera(
+  const int x, const int y, const int z, const int d_x, const int d_y, const int d_z
+){
+    Ogre::Vector3 position = Ogre::Vector3(x, y, z);
+    camera_->setPosition(position);
+    //Ogre::Quaternion orientation = position.getRotationTo(Ogre::Vector3(d_x, d_y, d_z));
+    //camera_->setOrientation(Ogre::Quaternion(orientation_initial_.w, orientation_initial_.x, orientation_initial_.y, orientation_initial_.z));
+    //CameraManager::getSingleton().GetCurrentCamera()->setFixedYawAxis(false);
+    //camera_->setOrientation(orientation);
+    //camera_->roll(orientation.getRoll());
+    //camera_->yaw(orientation.getYaw());
+    //camera_->pitch(orientation.getPitch());
+    camera_->lookAt(Ogre::Vector3(d_x, d_y, camera_->getPosition().z));
+    camera_->lookAt(Ogre::Vector3(d_x, d_y, d_z));
+}

+ 61 - 0
src/core/CameraManager.h

@@ -87,6 +87,25 @@ class CameraManager : public Ogre::Singleton<CameraManager>{
           const Ogre::Radian fov
         );
 
+        /**
+         * Starts the battle camera
+         *
+         * Saves the position and orientation of the current camera to return once the battle is
+         * over.
+         *
+         * @param[in] position Initial position of the battle camera.
+         * @param[in] orientation Initial orientation of the battle camera. It indicates the point
+         * the camera will look at.
+         */
+        void StartBattleCamera(const Ogre::Vector3 position, const Ogre::Vector3 orientation);
+
+        /**
+         * Ends the battle camera
+         *
+         * Returns the camera to the state it was when {@see StartBattleCamera} was called.
+         */
+        void EndBattleCamera();
+
         /**
          * Sets the camera scroll.
          *
@@ -138,6 +157,23 @@ class CameraManager : public Ogre::Singleton<CameraManager>{
          */
         void EnableWireFrame(bool enable);
 
+        /**
+         * Sets the camera position and orientation.
+         *
+         * It's only meand to be used for 3D cameras (battle, world map...), and using it while on
+         * a field can have unexpected results.
+         *
+         * @param[in] x X coordinate for the camera position.
+         * @param[in] y Y coordinate for the camera position.
+         * @param[in] z Z coordinate for the camera position.
+         * @param[in] d_x X coordinate of the point the camera looks at.
+         * @param[in] d_y Y coordinate of the point the camera looks at.
+         * @param[in] d_y Z coordinate of the point the camera looks at.
+         */
+        void ScriptSetCamera(
+          const int x, const int y, const int z, const int d_x, const int d_y, const int d_z
+        );
+
     private:
 
         /**
@@ -147,6 +183,11 @@ class CameraManager : public Ogre::Singleton<CameraManager>{
          */
         void InitCommands();
 
+        /**
+         * Indicates if the camera is in battle mode.
+         */
+        bool battle_;
+
         /**
          * The camera.
          */
@@ -157,6 +198,26 @@ class CameraManager : public Ogre::Singleton<CameraManager>{
          */
         Ogre::Viewport *viewport_;
 
+        /**
+         * The initial position of the camera, saved when created.
+         */
+        Ogre::Vector3 position_initial_;
+
+        /**
+         * The initial orientation of the camera, saved when created.
+         */
+        Ogre::Quaternion orientation_initial_;
+
+        /**
+         * A backup of the field or world camera position for when the battle camera is activated.
+         */
+        Ogre::Vector3 position_backup_;
+
+        /**
+         * A backup of the field or world camera orientation for when the battle camera is active.
+         */
+        Ogre::Quaternion orientation_backup_;
+
         /**
          * Flag to indicate a free camera.
          *

+ 4 - 0
src/core/Enemy.cpp

@@ -95,6 +95,10 @@ const int Enemy::GetId() const{return id_;}
 
 void Enemy::SetId(const int id){id_ = id;}
 
+const std::string Enemy::GetModel() const{return model_;}
+
+void Enemy::SetModel(const std::string model){model_ = model;}
+
 const Ogre::String& Enemy::GetName() const{return name_;}
 
 void Enemy::SetName(const Ogre::String& name){name_ = name;}

+ 23 - 0
src/core/Enemy.h

@@ -141,6 +141,24 @@ class Enemy{
          */
         void SetId(const int id);
 
+        /**
+         * Retrieves the enemy model.
+         *
+         * The model name is the filename of the .mesh file, without extension.
+         *
+         * @return The enemy model.
+         */
+        const std::string GetModel() const;
+
+        /**
+         * Sets the enemy model.
+         *
+         * The model name must be the filename of the .mesh file, without extension.
+         *
+         * @param[in] The enemy model name.
+         */
+        void SetModel(const std::string model);
+
         /**
          * Retrieves the enemy name.
          *
@@ -622,6 +640,11 @@ class Enemy{
          */
         int id_;
 
+        /**
+         * NAme of the enemy model file.
+         */
+        std::string model_;
+
         /**
          * The name of the enemy.
          */

+ 39 - 5
src/core/EntityManager.cpp

@@ -148,7 +148,9 @@ void EntityManager::SetModule(EntityManager::MODULE module){
         ClearBattle();
         ClearField();
     }
-    else if (module == MODULE::BATTLE) prev_module_ = module_;
+    else if (module == MODULE::BATTLE){
+        prev_module_ = module_;
+    }
     module_ = module;
 }
 
@@ -352,7 +354,11 @@ void EntityManager::UpdateField(){
 }
 
 void EntityManager::UpdateBattle(){
-    // TODO: Implement
+    for (unsigned int i = 0; i < battle_entity_.size(); ++ i){
+        battle_entity_[i]->Update();
+        battle_entity_[i]->PlayAnimationContinue(battle_entity_[i]->GetDefaultAnimationName());
+    }
+    //CameraManager::getSingleton().GetCurrentCamera()->roll(Ogre::Radian(0.01));
 }
 
 void EntityManager::UpdateWorld(){
@@ -419,7 +425,13 @@ void EntityManager::ClearField(){
 }
 
 void EntityManager::ClearBattle(){
-    // TODO implement
+    for (unsigned int i = 0; i < battle_entity_.size(); ++ i){
+        ScriptManager::getSingleton().RemoveEntity(
+          ScriptManager::ENTITY, battle_entity_[i]->GetName()
+        );
+        delete battle_entity_[i];
+    }
+    battle_entity_.clear();
 }
 
 void EntityManager::ClearWorld(){
@@ -453,6 +465,10 @@ void EntityManager::AddEntity(
   const Ogre::Degree& rotation, const Ogre::Vector3& scale,
   const Ogre::Quaternion& root_orientation, int index
 ){
+    if (module_ != MODULE::FIELD){
+        LOG_ERROR("Tried to add field Entity but the EntityManager is not in field mode.");
+        return;
+    }
     Ogre::SceneNode* node = scene_node_->createChildSceneNode("Model_" + name);
     EntityModel* entity = new EntityModel(name, file_name, node);
     entity->SetPosition(position);
@@ -460,8 +476,26 @@ void EntityManager::AddEntity(
     entity->setScale(scale);
     entity->SetIndex(index);
     entity->setRootOrientation(root_orientation);
-    if (module_ == MODULE::BATTLE) battle_entity_.push_back(entity);
-    else entity_.push_back(entity);
+    entity_.push_back(entity);
+    ScriptManager::getSingleton().AddEntity(ScriptManager::ENTITY, entity->GetName(), entity);
+}
+
+void EntityManager::AddBattleEntity(
+  const Ogre::String& name, const Ogre::String& file_name, const Ogre::Vector3& position,
+  const Ogre::Degree& rotation, const Ogre::Vector3& scale, const int index, const int visible
+){
+    if (module_ != MODULE::BATTLE){
+        LOG_ERROR("Tried to add battle Entity but the EntityManager is not in battle mode.");
+        return;
+    }
+    Ogre::SceneNode* node = scene_node_->createChildSceneNode("Model_" + name);
+    EntityModel* entity = new EntityModel(name, file_name, node);
+    entity->SetPosition(position);
+    entity->SetRotation(rotation);
+    entity->setScale(scale);
+    entity->SetIndex(index);
+    entity->SetVisible(visible);
+    battle_entity_.push_back(entity);
     ScriptManager::getSingleton().AddEntity(ScriptManager::ENTITY, entity->GetName(), entity);
 }
 

+ 20 - 1
src/core/EntityManager.h

@@ -262,7 +262,7 @@ class EntityManager : public Ogre::Singleton<EntityManager>{
         );
 
         /**
-         * Adds an entity to the manager.
+         * Adds a field entity to the manager.
          *
          * @param[in] name Entity name.
          * @param[in] file_name Path to the entity model file.
@@ -278,6 +278,25 @@ class EntityManager : public Ogre::Singleton<EntityManager>{
           const Ogre::Vector3& scale, const Ogre::Quaternion& root_orientation, int index
         );
 
+        /**
+         * Adds a battle entity to the manager.
+         *
+         * If the manager is not in the battle module, it will do nothing.
+         *
+         * @param[in] name Entity name.
+         * @param[in] file_name Path to the entity model file.
+         * @param[in] position Entity position.
+         * @param[in] rotation Entity face direction.
+         * @param[in] scale Entity scale.
+         * @param[in] index Index of the entity.
+         * @param[in] visible True to make the entity visible, false otherwise.
+         */
+        void AddBattleEntity(
+          const Ogre::String& name, const Ogre::String& file_name,
+          const Ogre::Vector3& position, const Ogre::Degree& rotation,
+          const Ogre::Vector3& scale, const int index, const int visible
+        );
+
         /**
          * Adds an entity to the manager.
          *

+ 27 - 0
src/core/ScriptManager.h

@@ -165,6 +165,33 @@ class ScriptManager : public Ogre::Singleton<ScriptManager>{
             FIELD
         };
 
+        /**
+         * The modules the script manager can handle.
+         */
+        enum MODULE{
+
+            /**
+             * Field module.
+             *
+             * Used in field maps. It has background, walkmesh, entities...
+             */
+            FIELD = 0,
+
+            /**
+             * Battle module.
+             *
+             * Used in battles. During battles there is no walkmesh.
+             */
+            BATTLE = 1,
+
+            /**
+             * World map module.
+             *
+             * Used in the world map. It has background, walkmesh, entities...
+             */
+            WORLD = 2
+        };
+
         /**
          * Constructor.
          */

+ 12 - 1
src/core/ScriptManagerBinds.h

@@ -18,6 +18,7 @@
 #include "../modules/worldmap/WorldmapModule.h"
 #include "Console.h"
 #include "Logger.h"
+#include "CameraManager.h"
 #include "Entity.h"
 #include "EntityManager.h"
 #include "BattleManager.h"
@@ -294,7 +295,15 @@ void ScriptManager::InitBinds(){
           )
     ];
 
-
+    // Commands for the audio manager.
+    luabind::module(lua_state_)[
+        luabind::class_<CameraManager>("CameraManager")
+          .def("set_camera",
+             (void(CameraManager::*)(
+               const int, const int, const int, const int, const int, const int
+             )) &CameraManager::ScriptSetCamera
+           )
+    ];
 
     // Commands for the savemap manager.
     luabind::module(lua_state_)[
@@ -893,6 +902,8 @@ void ScriptManager::InitBinds(){
     auto a = boost::ref(*(EntityManager::getSingletonPtr()));
     luabind::globals(lua_state_);
     //auto b = luabind::globals(lua_state_)["entity_manager"];
+    luabind::globals(lua_state_)["camera_manager"]
+      = boost::ref(*(CameraManager::getSingletonPtr()));
     luabind::globals(lua_state_)["entity_manager"]
       = boost::ref(*(EntityManager::getSingletonPtr()));
     luabind::globals(lua_state_)["battle_manager"]

+ 1 - 0
src/core/XmlEnemyFile.cpp

@@ -28,6 +28,7 @@ void XmlEnemyFile::LoadEnemy(Enemy& enemy){
         return;
     }
     enemy.SetId(GetInt(node, "id"));
+    enemy.SetModel(GetString(node, "model"));
     enemy.SetName(GetString(node, "name"));
     enemy.SetLevel(GetInt(node, "level"));
     enemy.SetExp(GetInt(node, "exp"));

+ 1 - 0
src/core/XmlFormationFile.cpp

@@ -69,6 +69,7 @@ void XmlFormationFile::LoadFormation(){
             TiXmlNode* enemy_node = node->FirstChild();
             while (enemy_node != nullptr){
                 int id = GetInt(enemy_node, "id");
+                std::cout << "LOADING ENEMY " << id << std::endl;
                 Ogre::Vector3 pos = Ogre::Vector3(
                   GetInt(enemy_node, "x"), GetInt(enemy_node, "y"), GetInt(enemy_node, "z")
                 );

+ 5 - 2
src/installer/BattleDataInstaller.cpp

@@ -146,7 +146,7 @@ unsigned int BattleDataInstaller::ProcessModel(){
     //  - am - cz: Polygon files (.p)
     //  - da: Animations (.anim)
     std::string id = battle_lgp_file_names_[next_model_to_process_].substr(0, 2);
-    if (id != "at"){next_model_to_process_ ++; return next_model_to_process_;} // TODO: DEBUG
+    //if (id != "at"){next_model_to_process_ ++; return next_model_to_process_;} // TODO: DEBUG
     std::string type = battle_lgp_file_names_[next_model_to_process_].substr(2, 2);
     FF7Data::BattleModelInfo info = FF7Data::GetBattleModelInfo(id);
     if (type == "aa"){
@@ -329,6 +329,7 @@ void BattleDataInstaller::WriteEnemies(){
         TiXmlDocument xml;
         std::unique_ptr<TiXmlElement> container(new TiXmlElement("enemy"));
         container->SetAttribute("id", enemy.id);
+        container->SetAttribute("model", enemy.model);
         container->SetAttribute("name", enemy.name);
         container->SetAttribute("level", enemy.level);
         container->SetAttribute("exp", enemy.exp);
@@ -769,8 +770,10 @@ void BattleDataInstaller::ExportMesh(const std::string outdir, const Ogre::MeshP
     std::set<std::string> textures;
     Ogre::Mesh::SubMeshList sub_meshes = mesh->getSubMeshes();
     for (Ogre::SubMesh* sub_mesh : sub_meshes){
+        // Change material name to avoid conflicts with field model materials.
+        sub_mesh->setMaterialName(sub_mesh->getMaterialName() + "_btl");
         Ogre::MaterialPtr mat(
-          Ogre::MaterialManager::getSingleton().getByName(sub_mesh->getMaterialName())
+            Ogre::MaterialManager::getSingleton().getByName(sub_mesh->getMaterialName())
         );
         if (mat == nullptr) continue;
         for (size_t techs = 0; techs < mat->getNumTechniques(); techs ++){

+ 40 - 23
src/installer/data/BattleSceneFile.cpp

@@ -19,6 +19,7 @@
 #include <iostream>
 #include "BattleSceneFile.h"
 #include "VGearsUtility.h"
+#include "FF7Data.h"
 
 BattleSceneFile::BattleSceneFile(const unsigned int id, File file): id_(id){Read(file);}
 
@@ -48,39 +49,54 @@ void BattleSceneFile::Read(File file){
     }
     for (int c = 0; c < 4; c ++){
         for (int p = 0; p < 3; p ++){
-            // Camera positions are stored as 16 bit floats.
-            // Here, ideally, 16 bits would be read, and then turned to float values, but...
-            // since the rounding is not really important, this is just reading the most
-            // significative byte as an integer, and skipping the other byte. Also, it's being
-            // multiplied by 10 to use bigger numbers in the XML.
-            scene_.camera[c].camera[p].x = file.readU8();
-            file.readU8();
+            // Camera positions are stored as 16 bit floats. Here, 16 bit are being read, then, the
+            // sign is checked to fit it into -32766 and 32767 (0x7F9b). Then, the number is
+            // divided by 255 (0xFF) to keep just the integer part.
+            //
+            scene_.camera[c].camera[p].x = file.readU16LE();
+            if (scene_.camera[c].camera[p].x > 0x7F9B) scene_.camera[c].camera[p].x -= 0xFFFF;
+            scene_.camera[c].camera[p].x /= 0xFF;
             // Y and Z are inverted in original data.
-            scene_.camera[c].camera[p].z = file.readU8();
-            file.readU8();
-            scene_.camera[c].camera[p].y = file.readU8();
-            file.readU8();
+            scene_.camera[c].camera[p].y = file.readU16LE();
+            if (scene_.camera[c].camera[p].y > 0x7F9B) scene_.camera[c].camera[p].y -= 0xFFFF;
+            scene_.camera[c].camera[p].y /= 0xFF;
+            scene_.camera[c].camera[p].z = file.readU16LE();
+            if (scene_.camera[c].camera[p].z > 0x7F9B) scene_.camera[c].camera[p].z -= 0xFFFF;
+            scene_.camera[c].camera[p].z /= 0xFF;
             scene_.camera[c].camera[p].d_x = file.readU16LE();
+            if (scene_.camera[c].camera[p].d_x > 0x7F9B) scene_.camera[c].camera[p].d_x -= 0xFFFF;
+            scene_.camera[c].camera[p].d_x /= 0xFF;
             scene_.camera[c].camera[p].d_y = file.readU16LE();
-            scene_.camera[c].camera[p].d_x = file.readU16LE();
+            if (scene_.camera[c].camera[p].d_y > 0x7F9B) scene_.camera[c].camera[p].d_y -= 0xFFFF;
+            scene_.camera[c].camera[p].d_y /= 0xFF;
+            scene_.camera[c].camera[p].d_z = file.readU16LE();
+            if (scene_.camera[c].camera[p].d_z > 0x7F9B) scene_.camera[c].camera[p].d_z -= 0xFFFF;
+            scene_.camera[c].camera[p].d_z /= 0xFF;
         }
         for (int p = 0; p < 12; p ++) scene_.camera[c].unused[p] = file.readU8();
     }
     for (int f = 0; f < 4; f ++){
         for (int e = 0; e < 6; e ++){
             scene_.formation[f][e].id = file.readU16LE();
-            // Enemy positions are stored as 16 bit floats.
-            // Here, ideally, 16 bits would be read, and then turned to float values, but...
-            // since the rounding is not really important, this is just reading the most
-            // significative byte as an integer, and skipping the other byte. Also, it's being
-            // multiplied by 10 to use bigger numbers in the XML.
-            scene_.formation[f][e].x = file.readU8();
-            file.readU8();
+            // Enemy positions are stored as 16 bit floats. Here, 16 bit are being read, then, the
+            // sign is checked to fit it into -32766 and 32767 (0x7F9b). Then, the number is
+            // divided by 255 (0xFF) to keep just the integer part.
+            scene_.formation[f][e].x = file.readU16LE();
+            if (scene_.formation[f][e].id == 347) std::cout << "TAILVAULT: x1 = " << std::to_string(scene_.formation[f][e].x) << std::endl;
+            if (scene_.formation[f][e].x > 0x7F9B) scene_.formation[f][e].x -= 0xFFFF;
+            scene_.formation[f][e].x /= 0xFF;
+            if (scene_.formation[f][e].id == 347) std::cout << "         : x2 = " << std::to_string(scene_.formation[f][e].x) << std::endl;
             // Y and Z are inverted in original data.
-            scene_.formation[f][e].z = file.readU8();
-            file.readU8();
-            scene_.formation[f][e].y = file.readU8();
-            file.readU8();
+            scene_.formation[f][e].y = file.readU16LE();
+            if (scene_.formation[f][e].id == 347) std::cout << "         : y1 = " << std::to_string(scene_.formation[f][e].y) << std::endl;
+            if (scene_.formation[f][e].y > 0x7F9B) scene_.formation[f][e].y -= 0xFFFF;
+            scene_.formation[f][e].y /= 0xFF;
+            if (scene_.formation[f][e].id == 347) std::cout << "         : y2 = " << std::to_string(scene_.formation[f][e].y) << std::endl;
+            scene_.formation[f][e].z = file.readU16LE();
+            if (scene_.formation[f][e].id == 347) std::cout << "         : z1 = " << std::to_string(scene_.formation[f][e].z) << std::endl;
+            if (scene_.formation[f][e].z > 0x7F9B) scene_.formation[f][e].z -= 0xFFFF;
+            scene_.formation[f][e].z /= 0xFF;
+            if (scene_.formation[f][e].id == 347) std::cout << "         : z2 = " << std::to_string(scene_.formation[f][e].z) << std::endl;
             scene_.formation[f][e].row = file.readU16LE();
             scene_.formation[f][e].cover_flags = file.readU16LE();
             scene_.formation[f][e].flags = file.readU32LE();
@@ -159,6 +175,7 @@ void BattleSceneFile::Read(File file){
         // Create a new enemy
         Enemy enemy;
         enemy.id = scene_.enemy[e];
+        enemy.model = FF7Data::GetEnemyModelFromEnemyId(enemy.id);
         enemy.name = VGears::Utility::DecodeString(scene_.enemy_data[e].name, 32);
         enemy.level = scene_.enemy_data[e].level;
         enemy.str = scene_.enemy_data[e].str;

+ 10 - 10
src/installer/data/BattleSceneFile.h

@@ -143,32 +143,32 @@ class BattleSceneFile{
                     /**
                      * X position.
                      */
-                    u16 x;
+                    int x;
 
                     /**
                      * Y position.
                      */
-                    u16 y;
+                    int y;
 
                     /**
                      * z position.
                      */
-                    u16 z;
+                    int z;
 
                     /**
                      * X direction.
                      */
-                    u16 d_x;
+                    int d_x;
 
                     /**
                      * Y direction.
                      */
-                    u16 d_y;
+                    int d_y;
 
                     /**
                      * z direction.
                      */
-                    u16 d_z;
+                    int d_z;
                 };
 
                 /**
@@ -190,24 +190,24 @@ class BattleSceneFile{
             struct Formation{
 
                 /**
-                 * Enemy ID,
+                 * Enemy ID.
                  */
                 u16 id;
 
                 /**
                  * Enemy X position.
                  */
-                u16 x;
+                int x;
 
                 /**
                  * Enemy Y position.
                  */
-                u16 y;
+                int y;
 
                 /**
                  * Enemy Z position.
                  */
-                u16 z;
+                int z;
 
                 /**
                  * Enemy row.

+ 5 - 0
src/installer/data/Enemy.h

@@ -214,6 +214,11 @@ struct Enemy{
      */
     std::vector<Item> drop;
 
+    /**
+     * Two-letter model code.
+     */
+    std::string model;
+
     /**
      * ID of the item the enemy can be morphed into.
      *

+ 387 - 5
src/installer/data/FF7Data.h

@@ -803,7 +803,7 @@ class FF7Data{
                 info.is_enemy = true;
             }
             else if ("dw" == alphanumeric_id){
-                info.numeric_id = 101;
+                info.numeric_id = 100;
                 info.name = "Search Crown";
                 info.name_normal = "search_crown";
                 info.is_enemy = true;
@@ -881,7 +881,7 @@ class FF7Data{
                 info.is_enemy = true;
             }
             else if ("ej" == alphanumeric_id){
-                info.numeric_id = 112;
+                info.numeric_id = 113;
                 info.name = "Spencer";
                 info.name_normal = "spencer";
                 info.is_enemy = true;
@@ -1019,19 +1019,19 @@ class FF7Data{
                 info.is_enemy = true;
             }
             else if ("fg" == alphanumeric_id){
-                info.numeric_id = 139;
+                info.numeric_id = 136;
                 info.name = "Heg";
                 info.name_normal = "heg";
                 info.is_enemy = true;
             }
             else if ("fh" == alphanumeric_id){
-                info.numeric_id = 139;
+                info.numeric_id = 137;
                 info.name = "Stinger";
                 info.name_normal = "stinger";
                 info.is_enemy = true;
             }
             else if ("fi" == alphanumeric_id){
-                info.numeric_id = 139;
+                info.numeric_id = 138;
                 info.name = "Soul Fire";
                 info.name_normal = "soul_fire";
                 info.is_enemy = true;
@@ -3063,4 +3063,386 @@ class FF7Data{
             }
             return info;
         };
+
+        /**
+         * Retrieves a battle model code from an enemy ID.
+         *
+         * @param[in] Enemy ID.
+         * @return The code for the model for the enemy, as in the filename in the .mesh file.
+         */
+        static std::string GetEnemyModelFromEnemyId(const unsigned int id){
+            switch (id){
+                case 0: return "aa_unused_pyramid";
+                case 1: return "ab_unused_pyramid";
+                case 2: return "ac_unused_pyramid";
+                case 3: return "ad_unused_pyramid";
+                case 4: return "ae_unused_pyramid";
+                case 5: return "af_unused_pyramid";
+                case 6: return "ag_unused_pyramid";
+                case 7: return "ah_unused_pyramid";
+                case 8: return "ai_unused_pyramid";
+                case 9: return "aj_unused_pyramid";
+                case 10: return "ak_diamond_weapon";
+                case 11: return "al_ruby_weapon";
+                case 12: return "am_rubys_tentacle";
+                case 13: return "an_emerald_weapon_upper_battle_skeleton";
+                case 14: return "ao_emerald_weapon_lower_battle_skeleton";
+                case 15: return "ap_unknown";
+                case 16: return "aq_mp";
+                case 17: return "ar_guard_hound";
+                case 18: return "as_mono_dive";
+                case 19: return "at_grunt";
+                case 20: return "au_1st_ray";
+                case 21: return "av_sweeper_unidentified_part";
+                case 22: return "aw_guard_scorpion_battle_skeleton";
+                case 23: return "ax_garshtrike";
+                case 24: return "ay_rocket_launcher";
+                case 25: return "az_whole_eater";
+                case 26: return "ba_chuse_tank";
+                case 27: return "bb_blugu";
+                case 28: return "bc_hedgehog_pie";
+                case 29: return "bd_smogger";
+                case 30: return "be_special_combatant";
+                case 31: return "bf_blood_taste";
+                case 32: return "bg_proto_machinegun";
+                case 33: return "bh_air_buster";
+                case 34: return "bi_vice_battle_model";
+                case 35: return "bj_corneos_lackey";
+                case 36: return "bk_scotch";
+                case 37: return "bl_aps_unidentified_part";
+                case 38: return "bm_sahagin";
+                case 39: return "bn_ceasar";
+                case 40: return "bo_eligor";
+                case 41: return "bp_ghost";
+                case 42: return "bq_cripshay";
+                case 43: return "br_deenglow";
+                case 44: return "bs_hell_house_calm_";
+                case 45: return "bt_hell_house_angry";
+                case 46: return "bu_aero_combatant_flying";
+                case 47: return "bv_aero_combatant_downed";
+                case 48: return "bw_turks_reno";
+                case 49: return "bx_renos_pyramid";
+                case 50: return "by_warning_board";
+                case 51: return "bz_machine_gun";
+                case 52: return "ca_laser_cannon";
+                case 53: return "cb_hammer_blaster_upper";
+                case 54: return "cc_hammer_blaster_base";
+                case 55: return "cd_sword_dance";
+                case 56: return "ce_soldier_3rd";
+                case 57: return "cf_mighty_grunt_outer_shell";
+                case 58: return "cg_mighty_grunt_inner";
+                case 59: return "ch_moth_slasher";
+                case 60: return "ci_grenade_combatant";
+                case 61: return "cj_brain_pod";
+                case 62: return "ck_vargid_police";
+                case 63: return "cl_zenene";
+                case 64: return "cm_sample_h0512";
+                case 65: return "cn_sample_h0512opt_";
+                case 66: return "co_hundred_gunner";
+                case 67: return "cp_heli_gunner";
+                case 68: return "cq_rufus";
+                case 69: return "cr_dark_nation";
+                case 70: return "cs_helicopter";
+                case 71: return "ct_motor_ball";
+                case 72: return "cu_devil_ride";
+                case 73: return "cv_custom_sweeper";
+                case 74: return "cw_kalm_fang";
+                case 75: return "cx_prowler";
+                case 76: return "cy_elfadunk";
+                case 77: return "cz_mu";
+                case 78: return "da_mu_rock_only";
+                case 79: return "db_mandragora";
+                case 80: return "dc_levrikon_unidentified_part";
+                case 81: return "dd_midgar_zolom";
+                case 82: return "de_madouge";
+                case 83: return "df_crawler";
+                case 84: return "dg_ark_dragon";
+                case 85: return "dh_castanets";
+                case 86: return "di_zemzelett";
+                case 87: return "dj_nerosuferoth";
+                case 88: return "dk_hell_rider_vr2";
+                case 89: return "dl_formula";
+                case 90: return "dm_capparwire";
+                case 91: return "dn_bottomswell";
+                case 92: return "do_waterpolo";
+                case 93: return "dp_scrutin_eye";
+                case 94: return "dq_marine";
+                case 95: return "dr_jenova_birth";
+                case 96: return "ds_grangalan";
+                case 97: return "dt_grangalan_jr";
+                case 98: return "du_grangalan_jr_jr";
+                case 99: return "dv_beach_plug";
+                case 100: return "dw_search_crown";
+                case 101: return "dx_needle_kiss";
+                case 102: return "dy_bloatfloat";
+                case 103: return "dz_bagnadrana";
+                case 104: return "ea_cokatolis";
+                case 105: return "eb_bomb";
+                case 106: return "ec_death_claw";
+                case 107: return "ed_2faced";
+                case 108: return "ee_bandit";
+                case 109: return "ef_bullmotor";
+                case 110: return "eg_land_worm";
+                case 111: return "eh_dyne";
+                case 112: return "ei_bullmotor";
+                case 113: return "ej_spencer";
+                case 114: return "ek_joker";
+                case 115: return "el_flapbeat";
+                case 116: return "em_harpy";
+                case 117: return "en_grand_horn";
+                case 118: return "eo_gagighandi";
+                case 119: return "ep_touch_me";
+                case 120: return "eq_crown_lance";
+                case 121: return "er_flower_prong_small";
+                case 122: return "es_flower_prong_medium";
+                case 123: return "et_flower_prong_large";
+                case 124: return "eu_slaps";
+                case 125: return "ev_kimara_bug";
+                case 126: return "ew_heavy_tank";
+                case 127: return "ex_turks_reno";
+                case 128: return "ey_turks_rude";
+                case 129: return "ez_skeeskee";
+                case 130: return "fa_griffin";
+                case 131: return "fb_golem";
+                case 132: return "fc_bagrisk";
+                case 133: return "fd_desert_sahagin";
+                case 134: return "fe_gi_spector";
+                case 135: return "ff_sneaky_step";
+                case 136: return "fg_heg";
+                case 137: return "fh_stinger";
+                case 138: return "fi_soul_fire";
+                case 139: return "fj_gi_nattak";
+                case 140: return "fk_nibel_wolf";
+                case 141: return "fl_velcher_task";
+                case 142: return "fm_bahba_velamyu";
+                case 143: return "fn_valron";
+                case 144: return "fo_battery_cap";
+                case 145: return "fp_mirage";
+                case 146: return "fq_dorkey_face";
+                case 147: return "fr_jersey";
+                case 148: return "fs_black_bat";
+                case 149: return "ft_ghirofelgo_wo_chain";
+                case 150: return "fu_ghirofelgos_chain";
+                case 151: return "fv_ying";
+                case 152: return "fw_yang";
+                case 153: return "fx_yingyang_body";
+                case 154: return "fy_lost_number";
+                case 155: return "fz_lost_number_psychic";
+                case 156: return "ga_lost_number_psysic_";
+                case 157: return "gb_dragon";
+                case 158: return "gc_sonic_speed";
+                case 159: return "gd_twin_brain";
+                case 160: return "ge_zuu";
+                case 161: return "gf_kyuvilduns";
+                case 162: return "gg_screamer";
+                case 163: return "gh_materia_keeper";
+                case 164: return "gi_palmer";
+                case 165: return "gj_tiny_bronco";
+                case 166: return "gk_shinra_truck";
+                case 167: return "gl_thunderbird";
+                case 168: return "gm_razor_weed";
+                case 169: return "gn_edgehead";
+                case 170: return "go_bizarre_bug";
+                case 171: return "gp_tail_vault";
+                case 172: return "gq_adamantaimai";
+                case 173: return "gr_attack_squad";
+                case 174: return "gs_foulander";
+                case 175: return "gt_garuda";
+                case 176: return "gu_jayjujayme";
+                case 177: return "gv_rapps";
+                case 178: return "gw_gorkii";
+                case 179: return "gx_shake";
+                case 180: return "gy_chekhov";
+                case 181: return "gz_staniv";
+                case 182: return "ha_godo";
+                case 183: return "hb_toxic_frog";
+                case 184: return "hc_toxic_frog";
+                case 185: return "hd_under_lizard";
+                case 186: return "he_kelzmelzer";
+                case 187: return "hf_dual_horn";
+                case 188: return "hg_tonadu";
+                case 189: return "hh_toxic_frog";
+                case 190: return "hi_jemnezmy";
+                case 191: return "hj_doorbull";
+                case 192: return "hk_ancient_dragon";
+                case 193: return "hl_red_dragon";
+                case 194: return "hm_8_eye";
+                case 195: return "hn_demons_gate";
+                case 196: return "ho_jenova_life";
+                case 197: return "hp_vlakorados";
+                case 198: return "hq_trickplay";
+                case 199: return "hr_trick_plays_attack";
+                case 200: return "hs_boundfat";
+                case 201: return "ht_malldancer";
+                case 202: return "hu_grimguard";
+                case 203: return "hv_hungry";
+                case 204: return "hw_acrophies";
+                case 205: return "hx_ice_golem";
+                case 206: return "hy_shred";
+                case 207: return "hz_lessaloploth";
+                case 208: return "ia_frozen_nail";
+                case 209: return "ib_jumping";
+                case 210: return "ic_snow";
+                case 211: return "id_bandersnatch";
+                case 212: return "ie_magnade";
+                case 213: return "if_magnades_shield";
+                case 214: return "ig_magnades_shield";
+                case 215: return "ih_malboro";
+                case 216: return "ii_blue_dragon";
+                case 217: return "ij_icicle";
+                case 218: return "ik_headbomber";
+                case 219: return "il_stilva";
+                case 220: return "im_zolokalter";
+                case 221: return "in_evilhead";
+                case 222: return "io_cuahl";
+                case 223: return "ip_gigas";
+                case 224: return "iq_grenade";
+                case 225: return "ir_gremlin";
+                case 226: return "is_ironite";
+                case 227: return "it_sculpture";
+                case 228: return "iu_schizo";
+                case 229: return "iv_schizos_head";
+                case 230: return "iw_wind_wing";
+                case 231: return "ix_dragon_rider";
+                case 232: return "iy_killbin";
+                case 233: return "iz_tonberry";
+                case 234: return "ja_jenova_death";
+                case 235: return "jb_roulette_cannon";
+                case 236: return "jc_pedestal";
+                case 237: return "jd_soldier2nd";
+                case 238: return "je_death_machine";
+                case 239: return "jf_slalom";
+                case 240: return "jg_scissors";
+                case 241: return "jh_scissors_upper";
+                case 242: return "ji_scissors_lower";
+                case 243: return "jj_guard_system";
+                case 244: return "jk_quick_machine_gun";
+                case 245: return "jl_rocket_launcher";
+                case 246: return "jm_ghost_ship";
+                case 247: return "jn_corvette";
+                case 248: return "jo_diver_nest";
+                case 249: return "jp_submarine_crew";
+                case 250: return "jq_captain";
+                case 251: return "jr_underwater_mp";
+                case 252: return "js_senior_grunt";
+                case 253: return "jt_hard_attacker";
+                case 254: return "ju_guardian";
+                case 255: return "jv_guardians_hand_right";
+                case 256: return "jw_guardians_hand_left";
+                case 257: return "jx_gun_carrier";
+                case 258: return "jy_carry_armor";
+                case 259: return "jz_carrys_left_arm";
+                case 260: return "ka_carrys_right_arm";
+                case 261: return "kb_rilfsak";
+                case 262: return "kc_diablo";
+                case 263: return "kd_epiolnis";
+                case 264: return "ke_hochu";
+                case 265: return "kf_gas_ducter";
+                case 266: return "kg_wolfmeister";
+                case 267: return "kh_eagle_gun";
+                case 268: return "ki_serpent";
+                case 269: return "kj_poodler";
+                case 270: return "kk_bad_rap";
+                case 271: return "kl_unknown_tongue";
+                case 272: return "km_unknown3_creep";
+                case 273: return "kn_unknown2_needle";
+                case 274: return "ko_turks_reno";
+                case 275: return "kp_turks_rude";
+                case 276: return "kq_hippogriff";
+                case 277: return "kr_head_hunter";
+                case 278: return "ks_spiral";
+                case 279: return "kt_crysales";
+                case 280: return "ku_sea_worm";
+                case 281: return "kv_turks_rude";
+                case 282: return "kw_cmd_grand_horn";
+                case 283: return "kx_cmd_grand_horn";
+                case 284: return "ky_cmd_grand_horn";
+                case 285: return "kz_behemoth";
+                case 286: return "la_cromwell";
+                case 287: return "lb_manhole";
+                case 288: return "lc_manhole_lid";
+                case 289: return "ld_crazy_saw";
+                case 290: return "le_shadow_maker";
+                case 291: return "lf_grosspanzer_big";
+                case 292: return "lg_grosspanzer_small";
+                case 293: return "lh_grosspanzer_mobile";
+                case 294: return "li_gargoyle_stoned";
+                case 295: return "lj_gargoyle";
+                case 296: return "lk_turks_elena";
+                case 297: return "ll_turks_reno";
+                case 298: return "lm_turks_rude";
+                case 299: return "ln_proud_clod";
+                case 300: return "lo_jamar_armor";
+                case 301: return "lp_soldier_1st";
+                case 302: return "lq_xcannon";
+                case 303: return "lr_bubble";
+                case 304: return "ls_maximum_kimaira";
+                case 305: return "lt_hojo";
+                case 306: return "lu_heletic_hojo";
+                case 307: return "lv_hojos_left_arm";
+                case 308: return "lw_hojos_right_arm";
+                case 309: return "lx_lifeform_hojo";
+                case 310: return "ly_magic_pot";
+                case 311: return "lz_christopher";
+                case 312: return "ma_gighee";
+                case 313: return "mb_king_behemoth";
+                case 314: return "mc_allemagne";
+                case 315: return "md_dragon_zombie";
+                case 316: return "me_armored_golem";
+                case 317: return "mf_master_tonberry";
+                case 318: return "mg_pollensalta";
+                case 319: return "mh_mover";
+                case 320: return "mi_iron_man";
+                case 321: return "mj_parasite";
+                case 322: return "mk_dark_dragon";
+                case 323: return "ml_death_dealer";
+                case 324: return "mm_jenova_synthesis";
+                case 325: return "mn_bizarro_sephiroth";
+                case 326: return "mo_bizarro_sephiroth";
+                case 327: return "mp_bizarro_sephiroth";
+                case 328: return "mq_bizarro_sephiroth";
+                case 329: return "mr_bizarro_sephiroth";
+                case 330: return "ms_bizarro_sephiroth";
+                case 331: return "mt_safer_sephiroth";
+                case 332: return "mu_sephiroth";
+                case 333: return "mv_ultima_weapon";
+                case 334: return "mw_ultima_weapon";
+                case 335: return "mx_ultima_weapon_aerial_fights";
+                case 336: return "my_ultima_weapon_aerial_fights";
+                case 337: return "mz_cactuar";
+                case 338: return "na_goblin";
+                case 339: return "nb_chocobo";
+                case 340: return "nc_chocobo";
+                case 341: return "nd_chocobo";
+                case 342: return "ne_chocobo";
+                case 343: return "nf_chocobo";
+                case 344: return "ng_chocobo";
+                case 345: return "nh_chocobo";
+                case 346: return "ni_chocobo";
+                case 347: return "nj_chocobo";
+                case 348: return "nk_chocobo";
+                case 349: return "nl_chocobo";
+                case 350: return "nm_chocobo";
+                case 351: return "nn_chocobo";
+                case 352: return "no_chocobo";
+                case 353: return "np_mystery_ninjayuf";
+                case 354: return "nq_yuffie_as_enemy";
+                case 355: return "nr_yuffie_as_enemy";
+                case 356: return "ns_yuffie_as_enemy";
+                case 357: return "nt_yuffie_as_enemy";
+                case 358: return "nu_yuffie_as_enemy";
+                case 359: return "nv_corneos_lacky";
+                case 360: return "nw_corneos_lacky";
+                case 361: return "nx_corneos_lacky";
+                case 362: return "ny_bad_rap_sample";
+                case 363: return "nz_poodler_sample";
+                case 364: return "oa_cactuer";
+                case 365: return "ob_shinra_trooper";
+                case 366: return "oc_shinra_trooper";
+                case 367: return "od_shinra_trooper";
+                case 368: return "oe_shinra_trooper";
+                case 369: return "of_shinra_trooper";
+            }
+            return "";
+        }
 };