Przeglądaj źródła

[INSTALLER] Changes in the installer to use the new TEX file handler: non-square images are now OK.
[INSTALLER] More consistent system to add 'end's and 'return's in field scripts.
[ENGINE] Minor improvements on trigger event detection (ladders still do funny thinsgs, but they are useable).
[ENGINE] Always a default playable character.

Iñigo Valentin 3 lat temu
rodzic
commit
69d27d462b

+ 57 - 6
V-Gears-Installer/src/DataInstaller.cpp

@@ -94,11 +94,11 @@ int DataInstaller::Progress(){
             progress_step_num_elements_ = 1;
             iterator_counter_ = 0;
             fields_lgp_ = std::make_unique<ScopedLgp>(
-              application_.getRoot(), input_dir_ + "field/flevel.lgp", "LGP", "FFVIIFields"
+              application_.getRoot(), input_dir_ + "data/field/flevel.lgp", "LGP", "FFVIIFields"
             );
             set_progress_label_("Installing kernel data...");
             kernel_installer_ = std::make_unique<KernelDataInstaller>(
-              input_dir_ + "kernel/KERNEL.BIN"
+              input_dir_ + "data/kernel/KERNEL.BIN"
             );
             kernel_installer_->ReadPrices(exe_path_);
             CreateDir("game");
@@ -254,7 +254,10 @@ void DataInstaller::ExportMesh(const std::string outdir, const Ogre::MeshPtr &me
                                   = pass->getTextureUnitState(texture_unit_num);
                                 if (unit && unit->getTextureName().empty() == false){
                                     // Convert the texture from .tex to .png.
-                                    TexToPng(unit->getTextureName());
+                                    std::cout << "[TEX2PNG] Calling with " << unit->getTextureName() << "\n";
+                                    //TexToPng(unit->getTextureName());
+                                    TexFile tex(output_dir_ + "temp/char/" + unit->getTextureName());
+
                                     // Ensure the output material script references png files
                                     // rather than tex
                                     // files.
@@ -266,6 +269,9 @@ void DataInstaller::ExportMesh(const std::string outdir, const Ogre::MeshPtr &me
                                       FieldModelDir() + "/" + base_mesh_name
                                       + "_" + base_name + ".png"
                                     );
+
+                                    std::cout << "SAVING PNG: " << output_dir_ << FieldModelDir() << "/" << base_name << ".png\n";
+                                    tex.SavePng(output_dir_ + FieldModelDir() + "/" + base_name + ".png", 0);
                                     // Copy subtexture (xxxx.png) to
                                     // model_xxxx.png
                                     // TODO: obtain the "data" folder
@@ -1379,11 +1385,54 @@ void DataInstaller::EndWriteMapsXml(){
 
 void DataInstaller::ConvertFieldModelsBegin(){
     // TODO: Convert models and animations in model_animation_db.
-    progress_step_num_elements_ = 1;
+    /*progress_step_num_elements_ = 1;
     field_models_lgp_ = std::make_unique<ScopedLgp>(
-      application_.getRoot(), input_dir_ + "field/char.lgp", "LGP", "FFVII"
+      application_.getRoot(), input_dir_ + "data/field/char.lgp", "LGP", "FFVII"
     );
     field_model_file_list_ = application_.ResMgr()->listResourceNames("FFVII", "*");
+    // Save file
+    //Ogre::StringVectorPtr file_list = field_models_lgp_->list(true, true);
+    VGears::LGPArchive::FileList files = field_models_lgp_.GetFiles();
+    CreateDir("temp/char");
+    for (int i = 0; i < file_list->size(); i ++){
+        VGears::LGPArchive::FileEntry f = files.at(i);
+        // Save the TEX File
+        std::fstream out;
+        out.open(output_dir_ + "temp/char/" + f.file_name, std::ios::out);
+        menu.SetOffset(f.data_offset);
+        for (int j = 0; j < f.data_size; j ++) out << menu.readU8();
+        out.close();
+    }*/
+
+    field_model_file_list_;// = application_.ResMgr()->listResourceNames("FFVII", "*");
+
+    // Open char_lgp as a lgp archive
+    VGears::LGPArchive char_lgp(input_dir_ + "data/field/char.lgp", "LGP");
+    char_lgp.open(input_dir_ + "data/field/char.lgp", true);
+    char_lgp.load();
+    // Also, open it as a file for reading
+    File char_file(input_dir_ + "data/field/char.lgp");
+
+    //Ogre::StringVectorPtr file_list = char_lgp.list(true, true);
+    field_model_file_list_ = char_lgp.list(true, true);
+    VGears::LGPArchive::FileList files = char_lgp.GetFiles();
+    CreateDir("temp/char");
+    std::cout << "[CHAR_LGP] START \n" ;
+    for (int i = 0; i < field_model_file_list_->size(); i ++){
+        VGears::LGPArchive::FileEntry f = files.at(i);
+        //std::cout << "[CHAR_LGP] File : " << f.file_name << "\n";
+        // Save the TEX File
+        std::fstream out;
+        out.open(output_dir_ + "temp/char/" + f.file_name, std::ios::out);
+        char_file.SetOffset(f.data_offset);
+        for (int j = 0; j < f.data_size; j ++) out << char_file.readU8();
+        out.close();
+
+        //field_model_file_list_->push_back(f.file_name);
+    }
+
+    application_.ResMgr()->addResourceLocation("data/temp/char/", "FileSystem", "FFVII", true, true);
+
     iterator_counter_ = 0;
     installation_state_ = CONVERT_FIELD_MODELS;
     iterator_counter_ = 0;
@@ -1400,9 +1449,11 @@ void DataInstaller::ConvertFieldModelsIteration(){
         }
         else{
             try{
+                std::cout << "OPEN HRC: " << model_animation_map_iterator_->first << std::endl;
                 Ogre::ResourcePtr hrc = VGears::HRCFileManager::GetSingleton().load(
                     model_animation_map_iterator_->first, "FFVII"
-                  );
+                );
+                //Ogre::ResourcePtr hrc = VGears::HRCFile();
                 Ogre::String base_name;
                 VGears::StringUtil::splitBase(model_animation_map_iterator_->first, base_name);
                 auto mesh_name = VGears::NameLookup::model(base_name) + ".mesh";

+ 7 - 3
V-Gears-Installer/src/MainWindow.cpp

@@ -40,8 +40,8 @@ MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), main_window_(new U
     main_window_->line_data_dst->setText(settings_->value("DataDir").toString());
     main_window_->line_vgears_exe->setText(settings_->value("VGearsEXE").toString());
     // TODO" Hard coded paths for debugging to save time. Remove them.
-    main_window_->line_data_src->setText("/home/ivalentin/data/");
-    main_window_->line_exe_src->setText("/home/ivalentin/data/ff7.exe");
+    main_window_->line_data_src->setText("/home/ivalentin/FF7InstallDisk/");
+    main_window_->line_exe_src->setText("/home/ivalentin/FF7InstallDisk/ff7.exe");
     //main_window_->line_data_dst->setText("/home/ivalentin/.v-gears/data/");
     timer_ = new QTimer(this);
     connect(timer_, SIGNAL(timeout()), this, SLOT(DoProgress()));
@@ -181,7 +181,11 @@ void MainWindow::on_btn_data_run_clicked(){
         if (!output.endsWith("/")) output += "/";
         // TODO: Enumerate files or find some better way to do this.
         const std::vector<std::string> required_files = {
-          "field/char.lgp", "field/flevel.lgp", "kernel/KERNEL.BIN"
+          "data/field/char.lgp",
+          "data/field/flevel.lgp",
+          "data/kernel/KERNEL.BIN",
+          "data/menu/menu_us.lgp",
+          "ff7.exe"
         };
         // Ensure required files are in the input dir
         for (auto& file : required_files){

+ 3 - 3
V-Gears-Installer/src/MediaDataInstaller.cpp

@@ -28,17 +28,17 @@
 
 MediaDataInstaller::MediaDataInstaller(const std::string input_dir, const std::string output_dir):
   input_dir_(input_dir), output_dir_(output_dir),
-  menu_(input_dir + "menu/menu_us.lgp", "LGP"), window_(input_dir + "kernel/WINDOW.BIN")
+  menu_(input_dir + "data/menu/menu_us.lgp", "LGP"), window_(input_dir + "data/kernel/WINDOW.BIN")
 {}
 
 MediaDataInstaller::~MediaDataInstaller(){}
 
 void MediaDataInstaller::InstallSprites(){
     // Actually open the lgp as a file for reading
-    File menu(input_dir_ + "menu/menu_us.lgp");
+    File menu(input_dir_ + "data/menu/menu_us.lgp");
 
     // Also, open it as a LGP archive.
-    menu_.open(input_dir_ + "menu/menu_us.lgp", true);
+    menu_.open(input_dir_ + "data/menu/menu_us.lgp", true);
     menu_.load();
 
 

+ 26 - 19
V-Gears-Installer/src/decompiler/field/FieldCodeGenerator.cpp

@@ -78,8 +78,7 @@ void FunctionMetaData::ParseCharId(const std::string& item, std::deque<std::stri
 
 void FunctionMetaData::ParseEntity(const std::string& item, std::deque<std::string>& strs){
     entity_name_ = item;
-    for (auto& part : strs)
-        if (!part.empty()) entity_name_ += "_" + part;
+    for (auto& part : strs) if (!part.empty()) entity_name_ += "_" + part;
 }
 
 void FieldCodeGenerator::Generate(InstVec& insts, const Graph& graph){
@@ -88,9 +87,7 @@ void FieldCodeGenerator::Generate(InstVec& insts, const Graph& graph){
     
     std::vector<std::pair<Function&, InstVec>> functions_with_bodies;
     for (
-      auto function = engine_->functions.begin();
-      function != engine_->functions.end();
-      ++ function
+      auto function = engine_->functions.begin(); function != engine_->functions.end(); ++ function
     ){
         InstVec body;
         for (size_t i = 0; i < function->second.num_instructions; ++ i, ++ instruction)
@@ -109,7 +106,7 @@ void FieldCodeGenerator::Generate(InstVec& insts, const Graph& graph){
         AddOutputLine(signature, false, true);
         OnStartFunction(function->first);
         
-        // Comment with original instructions.
+        // Generate labels for jump instruction targets.
         std::unordered_map<uint32, InstVec> labels;
         for (
           auto instruction = function->second.begin();
@@ -125,11 +122,15 @@ void FieldCodeGenerator::Generate(InstVec& insts, const Graph& graph){
         }
 
         // Implemented instructions.
-        bool end_needed = false;
+
+        // Some conditional jumps point to an address outside the function (i.e the starting
+        // address of the next function) and the 'end' are not written to the Lua script. Keep
+        // track of how many are needed and add them at the end of the function.
+        int ends_needed = 0;
         for (
           auto instruction = function->second.begin();
           instruction != function->second.end();
-          ++instruction
+          ++ instruction
         ){
             auto label = labels.find((*instruction)->GetAddress());
             if (label != labels.end()){
@@ -137,6 +138,7 @@ void FieldCodeGenerator::Generate(InstVec& insts, const Graph& graph){
                 bool needs_new_line = false;
                 for (auto origin = label->second.begin(); origin != label->second.end(); ++ origin){
                     if ((*origin)->IsCondJump()){
+                        ends_needed --;
                         AddOutputLine("end", true, false);
                         needs_new_line = true;
                     }
@@ -148,18 +150,19 @@ void FieldCodeGenerator::Generate(InstVec& insts, const Graph& graph){
             }
             ValueStack stack;
             (*instruction)->ProcessInst(function->first, stack, engine_, this);
-            if (end_needed){
-                AddOutputLine("end -- end if", true, false);
-                end_needed = false;
-            }
+            //if (end_needed){
+            //    AddOutputLine("end -- end if", true, false);
+            //    end_needed = false;
+            //}
             if ((*instruction)->IsCondJump()){
+                ends_needed ++;
                 AddOutputLine(
                   (boost::format("if (%s) then") % stack.Pop()->GetString()).str(), false, true
                 );
                 // If the next instruction is the last in the function, mark the next pass to
                 // add an 'end' after the instruction to close the if.
-                if ((*(instruction + 1))->GetAddress() == (*(function->second.back())).GetAddress())
-                    end_needed = true;
+                //if ((*(instruction + 1))->GetAddress() == (*(function->second.back())).GetAddress())
+                //    end_needed = true;
             }
             else if ((*instruction)->IsUncondJump()){
                 // If destination address is outside the functions, turn goto into a return.
@@ -190,6 +193,7 @@ void FieldCodeGenerator::Generate(InstVec& insts, const Graph& graph){
             }
             // Else, already output'd.
         }
+        for(; ends_needed > 0; ends_needed --) AddOutputLine("end -- clean-up", true, false);
         // Add missing return:
         if (
           "return 0" != lines_.at(lines_.size() - 1).line
@@ -228,6 +232,14 @@ void FieldCodeGenerator::OnBeforeStartFunction(const Function& function){
 }
 
 void FieldCodeGenerator::OnStartFunction(const Function& func){
+    // Add some hacks for Director entity before the comments
+    FunctionMetaData meta_data(func.metadata);
+    if (meta_data.GetEntityName() == "Director" && func.name == "on_start"){
+        // TODO: Don't hardcode Cloud, Use the one from the previous level,
+        AddOutputLine("-- HACK: Set default playable entity an ensure the camera follows it.");
+        AddOutputLine("entity_manager:set_player_entity(\"Cloud\")");
+        AddOutputLine("background2d:autoscroll_to_entity(entity_manager:get_entity(\"Cloud\"))\n");
+    }
     AddOutputLine("--[[");
     for (const auto& inst : insts_){
         if (inst->GetAddress() >= func.start_addr && inst->GetAddress() <= func.end_addr){
@@ -237,11 +249,6 @@ void FieldCodeGenerator::OnStartFunction(const Function& func){
         }
     }
     AddOutputLine("]]\n");
-    // TODO: If this hack is needed, maybe it can just be added to the "Director" entity.
-    if (func.name == "on_start" || func.name == "init"){
-        AddOutputLine("-- HACK ensure camera follows cloud, fix in engine.");
-        AddOutputLine("background2d:autoscroll_to_entity(entity_manager:get_entity(\"Cloud\"))");
-    }
 }
 
 void FieldCodeGenerator::OnEndFunction(const Function& function){

+ 1 - 1
V-Gears/include/core/EntityManager.h

@@ -203,7 +203,7 @@ class EntityManager : public Ogre::Singleton<EntityManager>{
          * @return The entity assigned to the character, or nullptr if there
          * is no one.
          */
-        Entity* GetEntityFromCharacterId(const char* id) const;
+        Entity* GetEntityFromCharacterId(const int id) const;
 
         /**
          * Retrieves an entity by name.

+ 3 - 2
V-Gears/include/core/ScriptManager.h

@@ -322,6 +322,8 @@ class ScriptManager : public Ogre::Singleton<ScriptManager>{
         /**
          * Request an script execution.
          *
+         * The script will execute only if it's not already being executed.
+         *
          * @param[in] type Script type.
          * @param[in] entity Entity the scripts belong to.
          * @param[in] function Name of the function of the selected entity to
@@ -329,8 +331,7 @@ class ScriptManager : public Ogre::Singleton<ScriptManager>{
          * @param[in] priority Execution priority.
          */
         void ScriptRequest(
-          const Type type, const char* entity,
-          const char* function, const int priority
+          const Type type, const char* entity, const char* function, const int priority
         );
 
         /**

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

@@ -219,7 +219,7 @@ void ScriptManager::InitBinds(){
            )
           .def(
              "get_entity_from_character_id",
-             (Entity*(EntityManager::*)(const char*)) &EntityManager::GetEntityFromCharacterId
+             (Entity*(EntityManager::*)(const int)) &EntityManager::GetEntityFromCharacterId
           )
           .def(
              "get_entity_point",

+ 1 - 1
V-Gears/src/core/Entity.cpp

@@ -453,7 +453,7 @@ void Entity::SetLinear(
 }
 
 void Entity::UnsetLinear(){
-    state_ = Entity::NEEDS_TO_REATTACH;
+    state_ = Entity::NONE;
     move_triangle_id_ = linear_dest_triangle_; // Set for NPCs. Playable can end up in two places.
     animation_auto_play_ = true;
     PlayAnimation(animation_default_, Entity::AUTO_ANIMATION, Entity::PLAY_LOOPED, 0, -1);

+ 9 - 3
V-Gears/src/core/EntityManager.cpp

@@ -13,6 +13,7 @@
  * GNU General Public License for more details.
  */
 
+#include <iostream>
 #include <cmath>
 #include <OgreEntity.h>
 #include <OgreRoot.h>
@@ -511,9 +512,9 @@ Entity* EntityManager::GetEntityFromIndex(const int index) const{
     return nullptr;
 }
 
-Entity* EntityManager::GetEntityFromCharacterId(const char* id) const{
+Entity* EntityManager::GetEntityFromCharacterId(const int id) const{
     for (unsigned int i = 0; i < entity_.size(); ++ i){
-        if (entity_[i]->IsCharacter() && entity_[i]->GetCharacterId() == atoi(id))
+        if (entity_[i]->IsCharacter() && entity_[i]->GetCharacterId() == id)
             return entity_[i];
     }
     return nullptr;
@@ -572,6 +573,10 @@ void EntityManager::StartBattle(unsigned int formation){
 }
 
 bool EntityManager::IsKeyOn(unsigned int key_code){
+    // If the player is locked, always false.
+    // TODO: Maybe this will interfere with something down the road?
+    // Some scene where the player can't move but muts press a button?
+    if (player_lock_) return false;
     unsigned int code = key_code;
     // Translate keycodes to game key codes.
     // For example 32 is standard for "Enter", but in game is "Circle/Action".
@@ -1067,7 +1072,8 @@ void EntityManager::CheckTriggers(Entity* entity, const Ogre::Vector3& position)
                     //std::cout << "    [TRIGGER] " << entity->GetName()
                     //  << " on_cross_line " << entity_triggers_[i]->GetName() << std::endl;
                     ScriptManager::getSingleton().ScriptRequest(
-                      trigger, "on_cross_line", 1, entity->GetName(), "", false, false
+                      //trigger, "on_cross_line", 1, entity->GetName(), "", false, false
+                      trigger, "on_cross_line", 1, entity->GetName(), "", true, true
                     );
                     // Test on_cross_line_once.
                     // Same conditions that on_cross_line, but triggered only once (i.e. if the entity

+ 3 - 2
data/data/config.cfg

@@ -1,11 +1,12 @@
 set camera_speed 10.05
 camera_free true
 set_log_level 2
-set console_notification true
-set debug_fps true
+set console_notification false
+set debug_fps false
 set debug_entity 1
 set debug_trigger true
 set debug_walkmesh true
+set debug_script 1
 
 bind F11 "toggle timer_scale_game 0.0 1.0"
 bind NumAdd "increment timer_scale_game 0.0 1.0 0.1"

+ 8 - 2
data/data/scripts/field.lua

@@ -49,6 +49,7 @@ join_party = function(speed)
     for c = 2, 3 do
         if FFVII.Party[c] ~= nil then
             local character = entity_manager:get_entity_from_character_id(FFVII.Party[c])
+            --print("    Name: " .. character:get_name())
             if character ~= nil then
                 local cur_speed = character:get_move_auto_speed()
                 character:set_solid(false)
@@ -91,8 +92,13 @@ split_party = function(ax, ay, ar, bx, by, br, speed)
                 -- Approximated speed, good enough for now.
                 -- TODO: Calculate speed based on time, as the orignal opcode.
                 character:set_move_auto_speed(speed / 16)
-                character:set_position(x, y, z)
-                character:set_visible(true)
+                if charater:is_visible() == false then
+                    -- In theory, a member is never visible before a split, but some maps
+                    -- are wrong. In these cases, don't change position and move the entity
+                    -- from it's current position.
+                    character:set_position(x, y, z)
+                    character:set_visible(true)
+                end
                 character:set_solid(false)
                 if c == 2 then
                     character:move_to_position(ax, ay)

+ 0 - 1
data/data/scripts/menu/item_menu.lua

@@ -900,7 +900,6 @@ UiContainer.ItemMenu = {
                 elseif type == Inventory.ITEM_TYPE.WEAPON then
                     local users = Game.Items[Inventory[inventory_index].item].users
                     for _, v in ipairs(users) do
-                        print(Game.Items[Inventory[inventory_index].item].name .. " Users " .. tostring(v))
                         if v >= 0 and v <= 8 then
                             w_icon:set_image("images/icons/item_weapon_" .. v .. ".png")
                             break;