Эх сурвалжийг харах

Field map improvements:

 - NPCs arre allowed out of the walkmesh without throwing errors.
 - Implemenred opcodes: WSIZW, LINON.
Iñigo Valentin 3 жил өмнө
parent
commit
d5bd6f7583

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

@@ -259,6 +259,7 @@ load_game = function(slot)
         end
 
     end
+    export_character_names()
     -- TODO: Settings
     -- TODO: Location
     -- TODO: Update character stats

+ 7 - 2
src/core/EntityManager.cpp

@@ -518,7 +518,7 @@ bool EntityManager::SetEntityOnWalkmesh(Entity* entity){
         }
     }
     // If the coordinates doesn't match any triangle, exit.
-    if (triangles.size() == 0){
+    if (triangles.size() == 0 && entity == player_entity_){
         LOG_ERROR(
           "Can't find any triangle to place entity '" + entity->GetName() + "' on walkmesh."
         );
@@ -534,7 +534,7 @@ bool EntityManager::SetEntityOnWalkmesh(Entity* entity){
             closest_z_distance = std::abs(pos_z - triangles[i].second);
         }
     }
-    if (closest_z_triangle == -1){
+    if (closest_z_triangle == -1 && entity == player_entity_){
         LOG_ERROR(
           "Can't find nearby triangle to place entity '" + entity->GetName() + "' on walkmesh."
         );
@@ -596,6 +596,11 @@ bool EntityManager::PerformWalkmeshMove(Entity* entity, const float speed){
         entity->SetRotation(Ogre::Degree(angle));
     }
     float solid = (entity->IsSolid() == true) ? entity->GetSolidRadius() : 0.01f;
+
+    // For not playable entities, be a bit more lenient with solid collisions.
+    // Don't use the solid radius.
+    if (entity != player_entity_) solid /= 10;
+
     // Get ending point.
     end_point.z = start_point.z;
     for (

+ 9 - 0
src/core/ScriptManager.cpp

@@ -604,6 +604,15 @@ bool ScriptManager::ScriptRequest(
         if (start_sync == true)
             script.paused_script_start = GetCurrentScriptId();
         if (end_sync == true) script.paused_script_end = GetCurrentScriptId();
+
+        // If the script is already running, don't queue it.
+        for (int i = 0; i < script_entity->queue.size(); i ++){
+             if (script_entity->queue[i].function == function){
+                 script_entity->resort = true;
+                 return true;
+            }
+        }
+
         script_entity->queue.push_back(script);
         script_entity->resort = true;
         return true;

+ 9 - 0
src/installer/decompiler/field/FieldCodeGenerator.cpp

@@ -240,6 +240,15 @@ void FieldCodeGenerator::OnStartFunction(const Function& func){
         AddOutputLine("entity_manager:set_player_entity(\"Cloud\")");
         AddOutputLine("background2d:autoscroll_to_entity(entity_manager:get_entity(\"Cloud\"))\n");
     }
+    // For lines, exit if they are off.
+    if (
+      func.name == "on_approach" || func.name == "on_cross"
+      || func.name == "on_near" || func.name == "on_leave"
+    ){
+        AddOutputLine("if self.on ~= nill and self.on == false then", false, true);
+        AddOutputLine("return 0");
+        AddOutputLine("end", true, false);
+    }
     AddOutputLine("--[[");
     for (const auto& inst : insts_){
         if (inst->GetAddress() >= func.start_addr && inst->GetAddress() <= func.end_addr){

+ 1 - 0
src/installer/decompiler/field/FieldCodeGenerator.h

@@ -159,6 +159,7 @@ class FunctionMetaData{
          * The character ID.
          */
         int character_id_ = -1;
+
 };
 
 /**

+ 1 - 0
src/installer/decompiler/field/instruction/FieldCondJumpInstruction.cpp

@@ -69,6 +69,7 @@ void FieldCondJumpInstruction::ProcessInst(
         case 3: op = "<"; break;
         case 4: op = ">="; break;
         case 5: op = "<="; break;
+        // TODO: FIX those, use the new binary functions.
         case 6: op = "&"; break;
         case 7: op = "^"; break;
         case 8: op = "|"; break;

+ 6 - 1
src/installer/decompiler/field/instruction/FieldWalkmeshInstruction.cpp

@@ -40,7 +40,7 @@ void FieldWalkmeshInstruction::ProcessInst(
                 % FieldCodeGenerator::FormatBool(params_[1]->GetUnsigned())).str());
             break;
         case OPCODES::LINE: ProcessLINE(code_gen, md.GetEntityName()); break;
-        case OPCODES::LINON: code_gen->WriteTodo(md.GetEntityName(), "LINON"); break;
+        case OPCODES::LINON: ProcessLINON(code_gen); break;
         case OPCODES::SLINE: code_gen->WriteTodo(md.GetEntityName(), "SLINE"); break;
         default:
             code_gen->AddOutputLine(FieldCodeGenerator::FormatInstructionNotImplemented(
@@ -76,3 +76,8 @@ void FieldWalkmeshInstruction::ProcessLINE(CodeGenerator* code_gen, const std::s
       + ")-(" + std::to_string(xb) + ", " +std::to_string(yb) + ", " + std::to_string(zb) + ")"
     );
 }
+
+void FieldWalkmeshInstruction::ProcessLINON(CodeGenerator* code_gen){
+    std::string on = ((params_[0]->GetSigned() == 1) ? "true" : "false");
+    code_gen->AddOutputLine("self.on = " + on);
+}

+ 19 - 0
src/installer/decompiler/field/instruction/FieldWalkmeshInstruction.h

@@ -79,4 +79,23 @@ class FieldWalkmeshInstruction : public KernelCallInstruction{
          * @param[in] entity The entity name.
          */
         void ProcessLINE(CodeGenerator* code_gen, const std::string& entity);
+
+        /**
+         * Processes a LINON opcode.
+         *
+         * Opcode: 0xD1
+         * Short name: LINON
+         * Long name: Line Switch
+         *
+         * Memory layout (2 bytes):
+         * |0xD1|S|
+         *
+         * const UByte S: Switch on/off (1/0, respectively).
+         *
+         * Turns on or off the LINE that was registered by this entity in the current field. If set
+         * to off, the line will not be triggered by the character walking through them.
+         *
+         * @param[in,out] code_gen Code generator to append lines.
+         */
+        void ProcessLINON(CodeGenerator* code_gen);
 };

+ 14 - 1
src/installer/decompiler/field/instruction/FieldWindowInstruction.cpp

@@ -33,7 +33,7 @@ void FieldWindowInstruction::ProcessInst(
     switch (opcode_){
         case OPCODES::TUTOR: code_gen->WriteTodo(md.GetEntityName(), "TUTOR"); break;
         case OPCODES::WCLS: code_gen->WriteTodo(md.GetEntityName(), "WCLS"); break;
-        case OPCODES::WSIZW: code_gen->WriteTodo(md.GetEntityName(), "WSIZW"); break;
+        case OPCODES::WSIZW: ProcessWSIZW(code_gen); break;
         case OPCODES::WSPCL: ProcessWSPCL(code_gen); break;
         case OPCODES::WNUMB: code_gen->WriteTodo(md.GetEntityName(), "WNUMB"); break;
         case OPCODES::STTIM: ProcessSTTIM(code_gen); break;
@@ -59,6 +59,19 @@ void FieldWindowInstruction::ProcessInst(
     }
 }
 
+void FieldWindowInstruction::ProcessWSIZW(CodeGenerator* code_gen){
+    // Do the same as window.
+    auto windowId = params_[0]->GetUnsigned();
+    auto x = params_[1]->GetUnsigned();
+    auto y = params_[2]->GetUnsigned();
+    auto width = params_[3]->GetUnsigned();
+    auto height = params_[4]->GetUnsigned();
+    code_gen->AddOutputLine((
+      boost::format("dialog:dialog_open(\"%1%\", %2%, %3%, %4%, %5%) -- WSIZW")
+      % windowId % x % y % width % height
+    ).str());
+}
+
 void FieldWindowInstruction::ProcessWSPCL(CodeGenerator* code_gen){
     auto window_id = params_[0]->GetUnsigned();
     std::string numeric = "false";

+ 26 - 0
src/installer/decompiler/field/instruction/FieldWindowInstruction.h

@@ -156,6 +156,32 @@ class FieldWindowInstruction : public KernelCallInstruction{
 
         void ProcessWCLSE(CodeGenerator* code_gen);
 
+        /**
+         * Processes a WSIZW opcode.
+         *
+         * Opcode: 0x2F
+         * Short name: WSIZW
+         * Long name: Window Resize
+         *
+         * Memory layout (6 bytes).
+         * |0x2F|I|X|Y|W|H|
+         *
+         * Arguments:
+         *
+         * - const UByte I: WINDOW ID to resize.
+         * - const UShort X: X-coordinate of the window.
+         * - const UShort Y: Y-coordinate of the window.
+         * - const UShort W: Width of the window.
+         * - const UShort H: Height of the window.
+         *
+         * Resizes and/or repositions the window, after it has been created with the WINDOW opcode.
+         * On the next MESSAGE or ASK, the window will be positioned and sized with the new
+         * properties.
+         *
+         * @param[in,out] code_gen Code generator to append lines.
+         */
+        void ProcessWSIZW(CodeGenerator* code_gen);
+
         /**
          * Processes a WSPCL opcode.
          *

+ 157 - 0
src/main.cpp

@@ -0,0 +1,157 @@
+/*
+ * 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 <OgreRoot.h>
+#include <OgreConfigFile.h>
+#include <OgreArchiveManager.h>
+#include <Overlay/OgreOverlaySystem.h>
+#include <Overlay/OgreOverlayManager.h>
+#include <Overlay/OgreFontManager.h>
+#include <OIS/OIS.h>
+#include "VGearsGameState.h"
+#include "common/VGearsApplication.h"
+#include "core/AudioManager.h"
+#include "core/CameraManager.h"
+#include "core/ConfigCmdManager.h"
+#include "core/ConfigFile.h"
+#include "core/ConfigVarManager.h"
+#include "core/Console.h"
+#include "core/DebugDraw.h"
+#include "core/EntityManager.h"
+#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"
+#include "core/particles/ParticleSystemManager.h"
+#include "core/TextManager.h"
+#include "core/DialogsManager.h"
+#include "data/VGearsLZSFLevelFileManager.h"
+#include "data/VGearsLGPArchiveFactory.h"
+#include "data/worldmap/WorldmapFileManager.h"
+#include "modules/worldmap/WorldmapModule.h"
+
+/**
+ * Main application function
+ *
+ * @param[in] argc Number of command line arguments.
+ * @param[in] argv List of command line arguments.
+ * @return 0 on sucess, an error code on error.
+ */
+int main(int argc, char *argv[]){
+    try{
+        std::cout << "V-Gears Init" << std::endl;
+        VGears::Application app(argc, argv);
+        if (!app.initOgre()) return 0;
+        Ogre::Root *root(app.getRoot());
+        Ogre::RenderWindow *window(app.getRenderWindow());
+        Ogre::SceneManager *scene_manager(nullptr);
+
+        auto timer = std::make_unique<Timer>();
+        auto particle_system_manager =
+          std::make_unique<ParticleSystemManager>();
+
+        // Set scene camera and viewport for other modules.
+        // Create this before initialize particle because some of them use
+        // scene to create themselves.
+        scene_manager = root->createSceneManager(Ogre::ST_GENERIC, "Scene");
+        // TODO: Why is this used twice?
+        scene_manager->setAmbientLight(Ogre::ColourValue(1, 1, 1));
+        scene_manager->setAmbientLight(Ogre::ColourValue(0.5, 0.5, 0.5));
+        Ogre::Light *directionalLight(
+          scene_manager->createLight("directionalLight")
+        );
+        directionalLight->setType(Ogre::Light::LT_DIRECTIONAL);
+        directionalLight->setDiffuseColour(Ogre::ColourValue(0.5, 0.5, 0.5));
+        directionalLight->setSpecularColour(Ogre::ColourValue(0.0, 0.0, 0.0));
+        directionalLight->setDirection(Ogre::Vector3(0, 1, 0));
+        // auto fontManager = std::make_unique<Ogre::FontManager>();
+        //VGears::MapFileManager* worldManager = new VGears::MapFileManager();
+
+        // Initialize resources.
+        // TODO: Use correct file location in the end, now is OK for testing
+        Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
+          ".", "FileSystem"
+        );
+        Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
+          "./data/wm", "FileSystem", "TEST"
+        );
+        Ogre::ResourceGroupManager::getSingleton().addResourceLocation(
+          "./data/wm/world_us.lgp",
+          VGears::LGPArchiveFactory::ARCHIVE_TYPE, "TEST"
+        );
+        Ogre::ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
+
+        // Initialize it before console because it may use it
+        auto config_var_manager = std::make_unique<ConfigVarManager>();
+        auto config_cmd_manager = std::make_unique<ConfigCmdManager>();
+        auto debug_draw = std::make_unique<DebugDraw>();
+
+        // Initialize before GameFrameListener, but after ConfigCmdManager
+        auto input_manager = std::make_unique<InputManager>();
+
+        auto audio_manager = std::make_unique<AudioManager>();
+
+        auto savemap_manager = std::make_unique<SavemapManager>();
+
+        // Create this earlier than DisplayFrameListener cause it can fire
+        // events there
+        auto camera_manager = std::make_unique<CameraManager>();
+        auto text_manager = std::make_unique<TextManager>();
+        auto ui_manager = std::make_unique<UiManager>();
+        auto dialogs_manager = std::make_unique<DialogsManager>();
+        auto entity_manager = std::make_unique<EntityManager>();
+        auto console = std::make_unique<Console>();
+        auto worldMapModule = std::make_unique<VGears::WorldmapModule>();
+
+        // Initialize after game managers because it attaches them to script.
+        auto script_manager = std::make_unique<ScriptManager>();
+
+        // Set base listener for usual game modules.
+        auto frame_listener = std::make_unique<GameFrameListener>(window);
+        root->addFrameListener(frame_listener.get());
+
+        // Execute the configuration file to locad values.
+        ConfigFile config;
+        config.Execute("./data/config.cfg");
+
+
+        // Initialize UID and run it's scripts.
+        ui_manager->Initialise();
+        dialogs_manager->Initialise();
+
+        // Run application loop
+        VGears::g_ApplicationState = VGears::G_GAME;
+        root->startRendering();
+
+        // System modules
+        // Thes must be removed first cause this can fire events to console.
+        root->removeFrameListener(frame_listener.get());
+
+        // Must be destroyed before the script manager.
+        entity_manager.reset();
+        ui_manager.reset();
+        script_manager.reset();
+    }
+    catch (const std::runtime_error& ex){
+        std::cout << "std::runtime_error thrown: " << ex.what() << std::endl;
+    }
+    catch (const Ogre::Exception& ex){
+        std::cout << "Ogre::Exception thrown: " << ex.what() << std::endl;
+    }
+    return 0;
+}