Custom loadscreen dvars
This commit is contained in:
parent
899f8d27a6
commit
b476859f09
246
src/client/component/loadscreen.cpp
Normal file
246
src/client/component/loadscreen.cpp
Normal file
@ -0,0 +1,246 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "game/game.hpp"
|
||||
#include "game/dvars.hpp"
|
||||
|
||||
#include "scheduler.hpp"
|
||||
#include "loadscreen.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/io.hpp>
|
||||
|
||||
namespace loadscreen
|
||||
{
|
||||
namespace
|
||||
{
|
||||
game::dvar_t* cl_disable_map_movies = nullptr;
|
||||
game::dvar_t* cl_loadscreen_image = nullptr;
|
||||
game::dvar_t* cl_loadscreen_title = nullptr;
|
||||
game::dvar_t* cl_loadscreen_desc = nullptr;
|
||||
game::dvar_t* cl_loadscreen_obj = nullptr;
|
||||
game::dvar_t* cl_loadscreen_obj_icon = nullptr;
|
||||
|
||||
utils::hook::detour ui_draw_loadbar_hook;
|
||||
|
||||
float white_color[4] = {0.8f, 0.8f, 0.8f, 1.f};
|
||||
float text_color[4] = {0.65f, 0.65f, 0.65f, 1.f};
|
||||
float gray_color[4] = {0.2f, 0.2f, 0.2f, 1.f};
|
||||
float icon_yellow_color[4] = {0.86f, 0.81f, 0.34f, 1.f};
|
||||
float icon_grey_color[4] = {0.6f, 0.6f, 0.6f, 1.f};
|
||||
|
||||
void draw_loadscreen_image()
|
||||
{
|
||||
const auto material = game::Material_RegisterHandle(cl_loadscreen_image->current.string);
|
||||
const auto placement = game::ScrPlace_GetViewPlacement();
|
||||
|
||||
game::rectangle rect{};
|
||||
rect.p0.x = 0;
|
||||
rect.p0.y = 0;
|
||||
rect.p0.f2 = 0.f;
|
||||
rect.p0.f3 = 1.f;
|
||||
|
||||
rect.p1.x = 0 + placement->realViewportSize[0];
|
||||
rect.p1.y = 0;
|
||||
rect.p1.f2 = 0.f;
|
||||
rect.p1.f3 = 1.f;
|
||||
|
||||
rect.p2.x = 0 + placement->realViewportSize[0];
|
||||
rect.p2.y = 0 + placement->realViewportSize[1];
|
||||
rect.p2.f2 = 0.f;
|
||||
rect.p2.f3 = 1.f;
|
||||
|
||||
rect.p3.x = 0;
|
||||
rect.p3.y = 0 + placement->realViewportSize[1];
|
||||
rect.p3.f2 = 0.f;
|
||||
rect.p3.f3 = 1.f;
|
||||
|
||||
game::R_DrawRectangle(&rect, 0.f, 0.f, 1.f, 1.f,
|
||||
white_color, material);
|
||||
}
|
||||
|
||||
void draw_loadscreen_progress_bar()
|
||||
{
|
||||
const auto fraction = utils::hook::invoke<float>(0x140287E30);
|
||||
|
||||
const auto w = 290.f;
|
||||
const auto w_progress = w * fraction;
|
||||
|
||||
const auto material = game::Material_RegisterHandle("white");
|
||||
const auto placement = game::ScrPlace_GetViewPlacement();
|
||||
|
||||
game::CL_DrawStretchPic(placement, -20, 290, w, 3, 0, 0, 0.0f, 0.0f, 1.0f, 1.0f, gray_color, material);
|
||||
game::CL_DrawStretchPic(placement, -20, 290, w_progress, 3, 0, 0, 0.0f, 0.0f, 1.0f, 1.0f, white_color, material);
|
||||
}
|
||||
|
||||
void draw_loadscreen_title()
|
||||
{
|
||||
auto x = -20.f;
|
||||
auto y = 290.f;
|
||||
auto h = 24.f;
|
||||
auto w = 0.f;
|
||||
|
||||
const auto placement = game::ScrPlace_GetViewPlacement();
|
||||
game::ScrPlace_ApplyRect(placement, &x, &y, &w, &h, 0, 0);
|
||||
|
||||
const auto font = game::R_RegisterFont("fonts/default.otf", static_cast<int>(h));
|
||||
game::R_AddCmdDrawText(cl_loadscreen_title->current.string, 0x7FFFFFFF, font, x, y, 1.f, 1.f, 0.f, text_color, 0);
|
||||
}
|
||||
|
||||
void draw_loadscreen_desc()
|
||||
{
|
||||
const auto font = game::R_RegisterFont("fonts/default.otf", 20);
|
||||
const auto placement = game::ScrPlace_GetViewPlacement();
|
||||
const auto text = cl_loadscreen_desc->current.string;
|
||||
|
||||
game::rectDef_s rect{};
|
||||
rect.x = 0;
|
||||
rect.y = 0;
|
||||
rect.w = 290;
|
||||
rect.horzAlign = 0;
|
||||
rect.vertAlign = 0;
|
||||
|
||||
game::rectDef_s text_rect{};
|
||||
|
||||
game::UI_DrawWrappedText(placement, text, &rect, font, -20, 310, 0.25f, text_color, 0, 0, &text_rect, 0);
|
||||
}
|
||||
|
||||
void draw_loadscreen_objective_icons()
|
||||
{
|
||||
if (*cl_loadscreen_obj_icon->current.string == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto material = game::Material_RegisterHandle(cl_loadscreen_obj_icon->current.string);
|
||||
const auto placement = game::ScrPlace_GetViewPlacement();
|
||||
|
||||
const auto w = 15.f;
|
||||
const auto base_y = 365.f;
|
||||
const auto base_x = -20.f;
|
||||
|
||||
for (auto row = 0; row < 3; row++)
|
||||
{
|
||||
for (auto column = 0; column < 3; column++)
|
||||
{
|
||||
auto x = base_x + column * w;
|
||||
auto y = base_y + row * w + 2;
|
||||
|
||||
const auto color = column <= row ? icon_yellow_color : icon_grey_color;
|
||||
game::CL_DrawStretchPic(placement, x, y, w, w, 0, 0, 0.f, 0.f, 1.f, 1.f, color, material);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw_loadscreen_objective()
|
||||
{
|
||||
if (*cl_loadscreen_obj->current.string == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
draw_loadscreen_objective_icons();
|
||||
|
||||
const auto font = game::R_RegisterFont("fonts/default.otf", 20);
|
||||
const auto placement = game::ScrPlace_GetViewPlacement();
|
||||
const auto text = cl_loadscreen_obj->current.string;
|
||||
|
||||
game::rectDef_s rect{};
|
||||
rect.x = 0;
|
||||
rect.y = 0;
|
||||
rect.w = 290.f;
|
||||
rect.horzAlign = 0;
|
||||
rect.vertAlign = 0;
|
||||
|
||||
game::rectDef_s text_rect{};
|
||||
|
||||
game::UI_DrawWrappedText(placement, text, &rect, font, 30.f, 365.f + 17.5f, 0.25f, text_color, 0, 0, &text_rect, 0);
|
||||
}
|
||||
|
||||
void draw_loadscreen()
|
||||
{
|
||||
if (!cl_disable_map_movies->current.enabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (cl_loadscreen_image == nullptr || cl_loadscreen_title == nullptr ||
|
||||
cl_loadscreen_desc == nullptr || *cl_loadscreen_image->current.string == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
draw_loadscreen_image();
|
||||
draw_loadscreen_progress_bar();
|
||||
draw_loadscreen_title();
|
||||
draw_loadscreen_desc();
|
||||
draw_loadscreen_objective();
|
||||
}
|
||||
|
||||
bool in_loadscreen()
|
||||
{
|
||||
return *reinterpret_cast<int*>(0x14203F3C4) == 4;
|
||||
}
|
||||
|
||||
void ui_set_active_menu_stub(utils::hook::assembler& a)
|
||||
{
|
||||
const auto player_start = a.newLabel();
|
||||
|
||||
a.mov(rax, qword_ptr(reinterpret_cast<uint64_t>(&cl_disable_map_movies)));
|
||||
a.mov(al, byte_ptr(rax, 0x10));
|
||||
a.cmp(al, 1);
|
||||
a.jz(player_start);
|
||||
|
||||
a.mov(rax, qword_ptr(static_cast<uint64_t>(0x14BE6EA10)));
|
||||
a.mov(al, byte_ptr(rax, 0x10));
|
||||
a.cmp(al, 1);
|
||||
a.jz(player_start);
|
||||
|
||||
a.jmp(0x1405F4701);
|
||||
|
||||
a.bind(player_start);
|
||||
a.call(0x1405F1B00);
|
||||
a.jmp(0x1405F44F2);
|
||||
}
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
game::Dvar_Reset(cl_disable_map_movies, game::DVAR_SOURCE_INTERNAL);
|
||||
game::Dvar_Reset(cl_loadscreen_image, game::DVAR_SOURCE_INTERNAL);
|
||||
game::Dvar_Reset(cl_loadscreen_title, game::DVAR_SOURCE_INTERNAL);
|
||||
game::Dvar_Reset(cl_loadscreen_desc, game::DVAR_SOURCE_INTERNAL);
|
||||
}
|
||||
|
||||
class component final : public component_interface
|
||||
{
|
||||
public:
|
||||
void post_unpack() override
|
||||
{
|
||||
// not registered, used in CL_StartLoading
|
||||
cl_disable_map_movies = dvars::register_bool("cl_disableMapMovies", false, 0, "Disable map loading videos");
|
||||
|
||||
// auto start the game if cl_disableMapMovies is enabled
|
||||
utils::hook::jump(0x1405F46EA, utils::hook::assemble(ui_set_active_menu_stub), true);
|
||||
|
||||
scheduler::once([]()
|
||||
{
|
||||
cl_loadscreen_image = dvars::register_string("cl_loadscreenImage", "", 0, "Loadscreen background image");
|
||||
cl_loadscreen_title = dvars::register_string("cl_loadscreenTitle", "", 0, "Loadscreen mission title");
|
||||
cl_loadscreen_desc = dvars::register_string("cl_loadscreenDesc", "", 0, "Loadscreen mission description");
|
||||
cl_loadscreen_obj = dvars::register_string("cl_loadscreenObj", "", 0, "Loadscreen mission objective");
|
||||
cl_loadscreen_obj_icon = dvars::register_string("cl_loadscreenObjIcon", "", 0, "Loadscreen mission objective icon");
|
||||
}, scheduler::pipeline::main);
|
||||
|
||||
scheduler::loop([]()
|
||||
{
|
||||
if (in_loadscreen())
|
||||
{
|
||||
draw_loadscreen();
|
||||
}
|
||||
}, scheduler::pipeline::renderer);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(loadscreen::component)
|
6
src/client/component/loadscreen.hpp
Normal file
6
src/client/component/loadscreen.hpp
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace loadscreen
|
||||
{
|
||||
void clear();
|
||||
}
|
@ -1,8 +1,10 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
|
||||
#include "game/game.hpp"
|
||||
#include "console.hpp"
|
||||
#include "loadscreen.hpp"
|
||||
|
||||
#include "game/game.hpp"
|
||||
#include "game/dvars.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
@ -60,6 +62,8 @@ namespace logger
|
||||
console::error("Error: %s\n", buffer);
|
||||
}
|
||||
|
||||
loadscreen::clear();
|
||||
|
||||
com_error_hook.invoke<void>(error, "%s", buffer);
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include "materials.hpp"
|
||||
#include "console.hpp"
|
||||
#include "filesystem.hpp"
|
||||
#include "command.hpp"
|
||||
|
||||
#include "game/game.hpp"
|
||||
#include "game/dvars.hpp"
|
||||
@ -49,7 +50,7 @@ namespace materials
|
||||
return image;
|
||||
}
|
||||
|
||||
game::Material* create_material(const std::string& name, const std::string& data)
|
||||
game::Material* create_material(const std::string& name, const utils::image& raw_image)
|
||||
{
|
||||
const auto white = *reinterpret_cast<game::Material**>(0x141B09208);
|
||||
|
||||
@ -66,7 +67,7 @@ namespace materials
|
||||
image->name = material->name;
|
||||
|
||||
material->textureTable = texture_table;
|
||||
material->textureTable->u.image = setup_image(image, data);
|
||||
material->textureTable->u.image = setup_image(image, raw_image);
|
||||
|
||||
return material;
|
||||
}
|
||||
@ -96,16 +97,37 @@ namespace materials
|
||||
data = i->second;
|
||||
}
|
||||
|
||||
if (data.empty() && !filesystem::read_file(utils::string::va("materials/%s.png", name.data()), &data))
|
||||
if (!data.empty())
|
||||
{
|
||||
data_.materials[name] = nullptr;
|
||||
return nullptr;
|
||||
const auto material = create_material(name, data);
|
||||
data_.materials[name] = material;
|
||||
return material;
|
||||
}
|
||||
|
||||
const auto material = create_material(name, data);
|
||||
data_.materials[name] = material;
|
||||
if (filesystem::read_file(utils::string::va("materials/%s.stbi_img", name.data()), &data))
|
||||
{
|
||||
const auto buffer = data.data();
|
||||
const auto width = *reinterpret_cast<int*>(buffer);
|
||||
const auto height = *reinterpret_cast<int*>(buffer + 4);
|
||||
const auto image_data = std::string(reinterpret_cast<char*>(buffer + 8), data.size() - 8);
|
||||
|
||||
return material;
|
||||
const auto image = utils::image(image_data, width, height);
|
||||
|
||||
const auto material = create_material(name, image);
|
||||
data_.materials[name] = material;
|
||||
|
||||
return material;
|
||||
}
|
||||
|
||||
if (filesystem::read_file(utils::string::va("materials/%s.png", name.data()), &data))
|
||||
{
|
||||
const auto material = create_material(name, data);
|
||||
data_.materials[name] = material;
|
||||
return material;
|
||||
}
|
||||
|
||||
data_.materials[name] = nullptr;
|
||||
return nullptr;
|
||||
});
|
||||
}
|
||||
|
||||
@ -193,6 +215,54 @@ namespace materials
|
||||
material_register_handle_hook.create(game::Material_RegisterHandle.get(), material_register_handle_stub);
|
||||
db_material_streaming_fail_hook.create(0x14041D140, db_material_streaming_fail_stub);
|
||||
db_get_material_index_hook.create(0x140413BC0, db_get_material_index_stub);
|
||||
|
||||
command::add("preloadImage", [](const command::params& params)
|
||||
{
|
||||
if (params.size() < 2)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto image_name = params.join(1);
|
||||
if (!utils::io::file_exists(image_name))
|
||||
{
|
||||
console::error("Image file not found\n");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto data = utils::io::read_file(image_name);
|
||||
|
||||
try
|
||||
{
|
||||
const auto image = utils::image{ data };
|
||||
const auto last_of = image_name.find_last_of('.');
|
||||
const auto new_name = image_name.substr(0, last_of) + ".stbi_img";
|
||||
|
||||
auto width = image.get_width();
|
||||
auto height = image.get_height();
|
||||
auto size = image.get_size();
|
||||
|
||||
/*
|
||||
int width;
|
||||
int height;
|
||||
char* data;
|
||||
*/
|
||||
|
||||
std::string buffer{};
|
||||
buffer.append(reinterpret_cast<char*>(&width), 4);
|
||||
buffer.append(reinterpret_cast<char*>(&height), 4);
|
||||
buffer.append(image.get_data());
|
||||
|
||||
utils::io::write_file(new_name, buffer, false);
|
||||
|
||||
console::info("Image saved to %s\n", new_name.data());
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
console::error("Error processing image: %s\n", e.what());
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "mods.hpp"
|
||||
#include "mapents.hpp"
|
||||
#include "localized_strings.hpp"
|
||||
#include "loadscreen.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/io.hpp>
|
||||
@ -32,6 +33,7 @@ namespace mods
|
||||
materials::clear();
|
||||
fonts::clear();
|
||||
mapents::clear_dvars();
|
||||
loadscreen::clear();
|
||||
}
|
||||
|
||||
mapents::clear();
|
||||
|
@ -1211,6 +1211,16 @@ namespace game
|
||||
char __pad0[0x8];
|
||||
};
|
||||
|
||||
struct rectDef_s
|
||||
{
|
||||
float x;
|
||||
float y;
|
||||
float w;
|
||||
float h;
|
||||
int horzAlign;
|
||||
int vertAlign;
|
||||
};
|
||||
|
||||
namespace hks
|
||||
{
|
||||
struct lua_State;
|
||||
|
@ -23,6 +23,9 @@ namespace game
|
||||
WEAK symbol<char*(const unsigned int weapon,
|
||||
bool isAlternate, char* outputBuffer, int bufferLen)> CG_GetWeaponDisplayName{0x1403B9210};
|
||||
|
||||
WEAK symbol<void(ScreenPlacement* place, float x, float y, float w, float h, int horzAlign, int vertAlign,
|
||||
float t0, float s0, float t1, float s1, float* color, Material* material)> CL_DrawStretchPic{0x1403C9570};
|
||||
|
||||
WEAK symbol<void(const char* cmdName, void(), cmd_function_s* allocedCmd)> Cmd_AddCommandInternal{0x14059A5F0};
|
||||
WEAK symbol<void(int localClientNum, int controllerIndex, const char* text)> Cmd_ExecuteSingleCommand{0x14059ABA0};
|
||||
|
||||
@ -58,6 +61,7 @@ namespace game
|
||||
WEAK symbol<void(int hash, const char* name, const char* buffer)> Dvar_SetCommand{0x14061A5C0};
|
||||
WEAK symbol<void(const char* dvarName, const char* string, DvarSetSource source)> Dvar_SetFromStringFromSource{0x14061A910};
|
||||
WEAK symbol<void(const dvar_t* dvar, const char* value)> Dvar_SetString{0x14061ABF0};
|
||||
WEAK symbol<void(const dvar_t* dvar, DvarSetSource source)> Dvar_Reset{0x140619FE0};
|
||||
|
||||
WEAK symbol<int(const char* fname)> generateHashValue{0x140343D20};
|
||||
|
||||
@ -143,6 +147,8 @@ namespace game
|
||||
|
||||
WEAK symbol<ScreenPlacement*()> ScrPlace_GetViewPlacement{0x1403E16A0};
|
||||
WEAK symbol<ScreenPlacement*()> ScrPlace_GetView{0x1403E1660};
|
||||
WEAK symbol<void(ScreenPlacement* scrPlace, float* x, float* y, float* w, float* h,
|
||||
int horzAlign, int vertAlign)> ScrPlace_ApplyRect{0x1403E0BF0};
|
||||
|
||||
WEAK symbol<const char*(scr_string_t stringValue)> SL_ConvertToString{0x1405BFBB0};
|
||||
WEAK symbol<scr_string_t(const char* str, unsigned int user)> SL_GetString{0x1405C0170};
|
||||
@ -162,6 +168,8 @@ namespace game
|
||||
|
||||
WEAK symbol<const char*(const char* string)> UI_SafeTranslateString{0x1405A2930};
|
||||
WEAK symbol<int(int localClientNum, const char* sound)> UI_PlayLocalSoundAlias{0x140606080};
|
||||
WEAK symbol<void(ScreenPlacement* scrPlace, const char* text, rectDef_s* rect, Font_s* font, float x, float y,
|
||||
float scale, const float* color, int style, int textAlignMode, rectDef_s* textRect, char a12)> UI_DrawWrappedText{0x1406055E0};
|
||||
|
||||
WEAK symbol<void(pmove_t* move, trace_t*, const float*, const float*,
|
||||
const Bounds*, int, int)> PM_playerTrace{0x14068F0A0};
|
||||
|
@ -26,6 +26,13 @@ namespace utils
|
||||
std::memmove(this->data.data(), rgb_image, size);
|
||||
}
|
||||
|
||||
image::image(const std::string& data_, int width_, int height_)
|
||||
: data(data_)
|
||||
, width(width_)
|
||||
, height(height_)
|
||||
{
|
||||
}
|
||||
|
||||
int image::get_width() const
|
||||
{
|
||||
return this->width;
|
||||
|
@ -8,6 +8,7 @@ namespace utils
|
||||
{
|
||||
public:
|
||||
image(const std::string& data);
|
||||
image(const std::string& data_, int width_, int height_);
|
||||
|
||||
int get_width() const;
|
||||
int get_height() const;
|
||||
|
Loading…
Reference in New Issue
Block a user