Add mod loading menu
This commit is contained in:
parent
f8e689397b
commit
b67012622d
@ -15,6 +15,7 @@
|
|||||||
#include <utils/hook.hpp>
|
#include <utils/hook.hpp>
|
||||||
#include <utils/string.hpp>
|
#include <utils/string.hpp>
|
||||||
#include <utils/memory.hpp>
|
#include <utils/memory.hpp>
|
||||||
|
#include <utils/io.hpp>
|
||||||
|
|
||||||
namespace command
|
namespace command
|
||||||
{
|
{
|
||||||
@ -447,6 +448,62 @@ namespace command
|
|||||||
}
|
}
|
||||||
}, scheduler::pipeline::server);
|
}, scheduler::pipeline::server);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
add("loadmod", [](const params& params)
|
||||||
|
{
|
||||||
|
if (params.size() < 2)
|
||||||
|
{
|
||||||
|
game_console::print(game_console::con_type_info, "Usage: loadmod mods/<modname>");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (::game::SV_Loaded())
|
||||||
|
{
|
||||||
|
game_console::print(game_console::con_type_error, "Cannot load mod while in-game!\n");
|
||||||
|
game::CG_GameMessage(0, "^1Cannot unload mod while in-game!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto path = params.get(1);
|
||||||
|
game_console::print(game_console::con_type_info, "Loading mod %s\n", path);
|
||||||
|
|
||||||
|
if (!utils::io::directory_exists(path))
|
||||||
|
{
|
||||||
|
game_console::print(game_console::con_type_error, "Mod %s not found!\n", path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
game::mod_folder = path;
|
||||||
|
|
||||||
|
::scheduler::once([]()
|
||||||
|
{
|
||||||
|
command::execute("lui_restart", true);
|
||||||
|
}, ::scheduler::pipeline::renderer);
|
||||||
|
});
|
||||||
|
|
||||||
|
add("unloadmod", [](const params& params)
|
||||||
|
{
|
||||||
|
if (game::mod_folder.empty())
|
||||||
|
{
|
||||||
|
game_console::print(game_console::con_type_info, "No mod loaded\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (::game::SV_Loaded())
|
||||||
|
{
|
||||||
|
game_console::print(game_console::con_type_error, "Cannot unload mod while in-game!\n");
|
||||||
|
game::CG_GameMessage(0, "^1Cannot unload mod while in-game!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
game_console::print(game_console::con_type_info, "Unloading mod %s\n", game::mod_folder.data());
|
||||||
|
game::mod_folder.clear();
|
||||||
|
|
||||||
|
::scheduler::once([]()
|
||||||
|
{
|
||||||
|
command::execute("lui_restart", true);
|
||||||
|
}, ::scheduler::pipeline::renderer);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,8 @@ namespace game
|
|||||||
base_address = uint64_t(module);
|
base_address = uint64_t(module);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string mod_folder{};
|
||||||
|
|
||||||
namespace environment
|
namespace environment
|
||||||
{
|
{
|
||||||
launcher::mode mode = launcher::mode::none;
|
launcher::mode mode = launcher::mode::none;
|
||||||
|
@ -6,9 +6,10 @@
|
|||||||
namespace game
|
namespace game
|
||||||
{
|
{
|
||||||
extern uint64_t base_address;
|
extern uint64_t base_address;
|
||||||
|
|
||||||
void load_base_address();
|
void load_base_address();
|
||||||
|
|
||||||
|
extern std::string mod_folder;
|
||||||
|
|
||||||
namespace environment
|
namespace environment
|
||||||
{
|
{
|
||||||
launcher::mode get_mode();
|
launcher::mode get_mode();
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include "../execution.hpp"
|
#include "../execution.hpp"
|
||||||
|
|
||||||
#include <utils/io.hpp>
|
#include <utils/io.hpp>
|
||||||
|
#include <utils/string.hpp>
|
||||||
|
|
||||||
namespace scripting::lua::engine
|
namespace scripting::lua::engine
|
||||||
{
|
{
|
||||||
@ -17,12 +18,13 @@ namespace scripting::lua::engine
|
|||||||
return scripts;
|
return scripts;
|
||||||
}
|
}
|
||||||
|
|
||||||
void load_scripts()
|
void load_generic_script()
|
||||||
{
|
{
|
||||||
get_scripts().push_back(std::make_unique<context>());
|
get_scripts().push_back(std::make_unique<context>());
|
||||||
|
}
|
||||||
|
|
||||||
const auto script_dir = "scripts/"s;
|
void load_scripts(const std::string& script_dir)
|
||||||
|
{
|
||||||
if (!utils::io::directory_exists(script_dir))
|
if (!utils::io::directory_exists(script_dir))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@ -44,7 +46,17 @@ namespace scripting::lua::engine
|
|||||||
{
|
{
|
||||||
clear_custom_fields();
|
clear_custom_fields();
|
||||||
get_scripts().clear();
|
get_scripts().clear();
|
||||||
load_scripts();
|
|
||||||
|
load_generic_script();
|
||||||
|
|
||||||
|
load_scripts("scripts/");
|
||||||
|
load_scripts("h2-mod/scripts/");
|
||||||
|
load_scripts("data/scripts/");
|
||||||
|
|
||||||
|
if (!game::mod_folder.empty())
|
||||||
|
{
|
||||||
|
load_scripts(utils::string::va("%s/scripts/", game::mod_folder.data()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void stop()
|
void stop()
|
||||||
|
@ -1087,6 +1087,11 @@ namespace ui_scripting::lua
|
|||||||
return std::string(buffer);
|
return std::string(buffer);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
game_type["getloadedmod"] = [](const game&)
|
||||||
|
{
|
||||||
|
return ::game::mod_folder;
|
||||||
|
};
|
||||||
|
|
||||||
struct player
|
struct player
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
@ -1242,9 +1247,8 @@ namespace ui_scripting::lua
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context::context(std::string folder)
|
context::context(std::string data, script_type type)
|
||||||
: folder_(std::move(folder))
|
: scheduler_(state_)
|
||||||
, scheduler_(state_)
|
|
||||||
, event_handler_(state_)
|
, event_handler_(state_)
|
||||||
|
|
||||||
{
|
{
|
||||||
@ -1256,23 +1260,6 @@ namespace ui_scripting::lua
|
|||||||
sol::lib::math,
|
sol::lib::math,
|
||||||
sol::lib::table);
|
sol::lib::table);
|
||||||
|
|
||||||
this->state_["include"] = [this](const std::string& file)
|
|
||||||
{
|
|
||||||
this->load_script(file);
|
|
||||||
};
|
|
||||||
|
|
||||||
sol::function old_require = this->state_["require"];
|
|
||||||
auto base_path = utils::string::replace(this->folder_, "/", ".") + ".";
|
|
||||||
this->state_["require"] = [base_path, old_require](const std::string& path)
|
|
||||||
{
|
|
||||||
return old_require(base_path + path);
|
|
||||||
};
|
|
||||||
|
|
||||||
this->state_["scriptdir"] = [this]()
|
|
||||||
{
|
|
||||||
return this->folder_;
|
|
||||||
};
|
|
||||||
|
|
||||||
setup_io(this->state_);
|
setup_io(this->state_);
|
||||||
setup_vector_type(this->state_);
|
setup_vector_type(this->state_);
|
||||||
setup_element_type(this->state_, this->event_handler_, this->scheduler_);
|
setup_element_type(this->state_, this->event_handler_, this->scheduler_);
|
||||||
@ -1280,8 +1267,35 @@ namespace ui_scripting::lua
|
|||||||
setup_game_type(this->state_, this->event_handler_, this->scheduler_);
|
setup_game_type(this->state_, this->event_handler_, this->scheduler_);
|
||||||
setup_lui_types(this->state_, this->event_handler_, this->scheduler_);
|
setup_lui_types(this->state_, this->event_handler_, this->scheduler_);
|
||||||
|
|
||||||
printf("Loading ui script '%s'\n", this->folder_.data());
|
if (type == script_type::file)
|
||||||
this->load_script("__init__");
|
{
|
||||||
|
this->folder_ = data;
|
||||||
|
|
||||||
|
this->state_["include"] = [this](const std::string& file)
|
||||||
|
{
|
||||||
|
this->load_script(file);
|
||||||
|
};
|
||||||
|
|
||||||
|
sol::function old_require = this->state_["require"];
|
||||||
|
auto base_path = utils::string::replace(this->folder_, "/", ".") + ".";
|
||||||
|
this->state_["require"] = [base_path, old_require](const std::string& path)
|
||||||
|
{
|
||||||
|
return old_require(base_path + path);
|
||||||
|
};
|
||||||
|
|
||||||
|
this->state_["scriptdir"] = [this]()
|
||||||
|
{
|
||||||
|
return this->folder_;
|
||||||
|
};
|
||||||
|
|
||||||
|
printf("Loading ui script '%s'\n", this->folder_.data());
|
||||||
|
this->load_script("__init__");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == script_type::code)
|
||||||
|
{
|
||||||
|
handle_error(this->state_.safe_script(data, &sol::script_pass_on_error));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context::~context()
|
context::~context()
|
||||||
|
@ -15,6 +15,12 @@
|
|||||||
|
|
||||||
namespace ui_scripting::lua
|
namespace ui_scripting::lua
|
||||||
{
|
{
|
||||||
|
enum script_type
|
||||||
|
{
|
||||||
|
file,
|
||||||
|
code
|
||||||
|
};
|
||||||
|
|
||||||
extern std::unordered_map<std::string, menu> menus;
|
extern std::unordered_map<std::string, menu> menus;
|
||||||
extern std::vector<element*> elements;
|
extern std::vector<element*> elements;
|
||||||
extern element ui_element;
|
extern element ui_element;
|
||||||
@ -23,7 +29,7 @@ namespace ui_scripting::lua
|
|||||||
class context
|
class context
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
context(std::string folder);
|
context(std::string data, script_type);
|
||||||
~context();
|
~context();
|
||||||
|
|
||||||
context(context&&) noexcept = delete;
|
context(context&&) noexcept = delete;
|
||||||
|
@ -7,11 +7,14 @@
|
|||||||
|
|
||||||
#include <utils/io.hpp>
|
#include <utils/io.hpp>
|
||||||
#include <utils/string.hpp>
|
#include <utils/string.hpp>
|
||||||
|
#include <utils/nt.hpp>
|
||||||
|
|
||||||
namespace ui_scripting::lua::engine
|
namespace ui_scripting::lua::engine
|
||||||
{
|
{
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
const auto mods_menu_script = utils::nt::load_resource(LUI_MODS_MENU);
|
||||||
|
|
||||||
float screen_max[2];
|
float screen_max[2];
|
||||||
|
|
||||||
void check_resize()
|
void check_resize()
|
||||||
@ -302,10 +305,8 @@ namespace ui_scripting::lua::engine
|
|||||||
return scripts;
|
return scripts;
|
||||||
}
|
}
|
||||||
|
|
||||||
void load_scripts()
|
void load_scripts(const std::string& script_dir)
|
||||||
{
|
{
|
||||||
const auto script_dir = "ui_scripts/"s;
|
|
||||||
|
|
||||||
if (!utils::io::directory_exists(script_dir))
|
if (!utils::io::directory_exists(script_dir))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
@ -317,11 +318,16 @@ namespace ui_scripting::lua::engine
|
|||||||
{
|
{
|
||||||
if (std::filesystem::is_directory(script) && utils::io::file_exists(script + "/__init__.lua"))
|
if (std::filesystem::is_directory(script) && utils::io::file_exists(script + "/__init__.lua"))
|
||||||
{
|
{
|
||||||
get_scripts().push_back(std::make_unique<context>(script));
|
get_scripts().push_back(std::make_unique<context>(script, script_type::file));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void load_code(const std::string& code)
|
||||||
|
{
|
||||||
|
get_scripts().push_back(std::make_unique<context>(code, script_type::code));
|
||||||
|
}
|
||||||
|
|
||||||
void render_menus()
|
void render_menus()
|
||||||
{
|
{
|
||||||
check_resize();
|
check_resize();
|
||||||
@ -406,7 +412,17 @@ namespace ui_scripting::lua::engine
|
|||||||
close_all_menus();
|
close_all_menus();
|
||||||
get_scripts().clear();
|
get_scripts().clear();
|
||||||
clear_menus();
|
clear_menus();
|
||||||
load_scripts();
|
|
||||||
|
load_code(mods_menu_script);
|
||||||
|
|
||||||
|
load_scripts("ui_scripts/");
|
||||||
|
load_scripts("h2-mod/ui_scripts/");
|
||||||
|
load_scripts("data/ui_scripts/");
|
||||||
|
|
||||||
|
if (!game::mod_folder.empty())
|
||||||
|
{
|
||||||
|
load_scripts(utils::string::va("%s/ui_scripts/", game::mod_folder.data()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void stop()
|
void stop()
|
||||||
|
@ -22,3 +22,4 @@
|
|||||||
#define ICON_IMAGE 313
|
#define ICON_IMAGE 313
|
||||||
|
|
||||||
#define LUA_ANIMATION_SCRIPT 314
|
#define LUA_ANIMATION_SCRIPT 314
|
||||||
|
#define LUI_MODS_MENU 315
|
||||||
|
@ -98,6 +98,7 @@ ID_ICON ICON "resources/icon.ico"
|
|||||||
MENU_MAIN RCDATA "resources/main.html"
|
MENU_MAIN RCDATA "resources/main.html"
|
||||||
|
|
||||||
LUA_ANIMATION_SCRIPT RCDATA "resources/animation.lua"
|
LUA_ANIMATION_SCRIPT RCDATA "resources/animation.lua"
|
||||||
|
LUI_MODS_MENU RCDATA "resources/mods_menu.lua"
|
||||||
|
|
||||||
#ifdef _DEBUG
|
#ifdef _DEBUG
|
||||||
TLS_DLL RCDATA "../../build/bin/x64/Debug/tlsdll.dll"
|
TLS_DLL RCDATA "../../build/bin/x64/Debug/tlsdll.dll"
|
||||||
|
118
src/client/resources/mods_menu.lua
Normal file
118
src/client/resources/mods_menu.lua
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
function createdivider(menu, text)
|
||||||
|
local element = LUI.UIElement.new( {
|
||||||
|
leftAnchor = true,
|
||||||
|
rightAnchor = true,
|
||||||
|
left = 0,
|
||||||
|
right = 0,
|
||||||
|
topAnchor = true,
|
||||||
|
bottomAnchor = false,
|
||||||
|
top = 0,
|
||||||
|
bottom = 33.33
|
||||||
|
})
|
||||||
|
|
||||||
|
element.scrollingToNext = true
|
||||||
|
element:addElement(LUI.MenuBuilder.BuildRegisteredType("h1_option_menu_titlebar", {
|
||||||
|
title_bar_text = Engine.ToUpperCase(Engine.Localize(text))
|
||||||
|
}))
|
||||||
|
|
||||||
|
menu.list:addElement(element)
|
||||||
|
end
|
||||||
|
|
||||||
|
local maincampaign = LUI.MenuBuilder.m_types_build["main_campaign"]
|
||||||
|
LUI.MenuBuilder.m_types_build["main_campaign"] = function(a1, a2)
|
||||||
|
local menu = maincampaign(a1, a2)
|
||||||
|
local buttonlist = menu:getChildById("main_campaign_list")
|
||||||
|
|
||||||
|
local button = menu:AddButton("$_MODS", function()
|
||||||
|
LUI.FlowManager.RequestAddMenu(nil, "mods_menu")
|
||||||
|
end, nil, true, nil, {
|
||||||
|
desc_text = "$_Open mods menu"
|
||||||
|
})
|
||||||
|
|
||||||
|
buttonlist:removeElement(button)
|
||||||
|
buttonlist:insertElement(button, 6)
|
||||||
|
button.id = "mods_menu-button"
|
||||||
|
|
||||||
|
local hintbox = menu.optionTextInfo
|
||||||
|
local firstbutton = buttonlist:getFirstChild()
|
||||||
|
hintbox:dispatchEventToRoot({
|
||||||
|
name = "set_button_info_text",
|
||||||
|
text = firstbutton.properties.desc_text,
|
||||||
|
immediate = true
|
||||||
|
})
|
||||||
|
|
||||||
|
menu:CreateBottomDivider()
|
||||||
|
menu:AddBottomDividerToList(buttonlist:getLastChild())
|
||||||
|
menu:removeElement(menu.optionTextInfo)
|
||||||
|
|
||||||
|
LUI.Options.InitScrollingList(menu.list, nil)
|
||||||
|
menu:CreateBottomDivider()
|
||||||
|
menu.optionTextInfo = LUI.Options.AddOptionTextInfo(menu)
|
||||||
|
|
||||||
|
return menu
|
||||||
|
end
|
||||||
|
|
||||||
|
function modsmenu(a1)
|
||||||
|
local menu = LUI.MenuTemplate.new(a1, {
|
||||||
|
menu_title = "$_MODS",
|
||||||
|
exclusiveController = 0,
|
||||||
|
menu_width = 400,
|
||||||
|
menu_top_indent = LUI.MenuTemplate.spMenuOffset,
|
||||||
|
showTopRightSmallBar = true
|
||||||
|
})
|
||||||
|
|
||||||
|
local modfolder = game:getloadedmod()
|
||||||
|
if (modfolder ~= "") then
|
||||||
|
createdivider(menu, "$_Loaded mod: " .. modfolder)
|
||||||
|
|
||||||
|
menu:AddButton("$_UNLOAD", function()
|
||||||
|
game:executecommand("unloadmod")
|
||||||
|
end, nil, true, nil, {
|
||||||
|
desc_text = "Unload the currently loaded mod"
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
createdivider(menu, "$_Available mods")
|
||||||
|
|
||||||
|
local mods = io.listfiles("mods/")
|
||||||
|
for i = 1, #mods do
|
||||||
|
local desc = "Load " .. mods[i]
|
||||||
|
local infofile = mods[i] .. "/mod.txt"
|
||||||
|
local exists = io.fileexists(infofile)
|
||||||
|
|
||||||
|
if (exists) then
|
||||||
|
desc = io.readfile(infofile)
|
||||||
|
end
|
||||||
|
|
||||||
|
if (mods[i] ~= modfolder) then
|
||||||
|
menu:AddButton("$_" .. mods[i], function()
|
||||||
|
game:executecommand("loadmod " .. mods[i])
|
||||||
|
end, nil, true, nil, {
|
||||||
|
desc_text = desc
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
menu:AddBackButton(function(a1)
|
||||||
|
Engine.PlaySound(CoD.SFX.MenuBack)
|
||||||
|
LUI.FlowManager.RequestLeaveMenu(a1)
|
||||||
|
end)
|
||||||
|
|
||||||
|
LUI.Options.InitScrollingList(menu.list, nil)
|
||||||
|
menu:CreateBottomDivider()
|
||||||
|
menu.optionTextInfo = LUI.Options.AddOptionTextInfo(menu)
|
||||||
|
|
||||||
|
return menu
|
||||||
|
end
|
||||||
|
|
||||||
|
LUI.MenuBuilder.m_types_build["mods_menu"] = modsmenu
|
||||||
|
|
||||||
|
local localize = Engine.Localize
|
||||||
|
Engine.Localize = function(...)
|
||||||
|
local args = {...}
|
||||||
|
if (args[1]:sub(1, 2) == "$_") then
|
||||||
|
return args[1]:sub(3, -1)
|
||||||
|
end
|
||||||
|
|
||||||
|
return localize(table.unpack(args))
|
||||||
|
end
|
Loading…
Reference in New Issue
Block a user