Initial commit

This commit is contained in:
Hash Borgir
2024-04-16 21:45:38 -06:00
commit 3159020c38
533 changed files with 135446 additions and 0 deletions

View File

@@ -0,0 +1,188 @@
#include <d2tweaks/ui/controls/button.h>
#include <d2tweaks/ui/controls/image.h>
#include <diablo2/d2client.h>
#include <diablo2/d2win.h>
#include <diablo2/d2gfx.h>
#include <spdlog/spdlog.h>
#include <d2tweaks/common/asset_manager.h>
#include <common/string_utils.h>
d2_tweaks::ui::controls::button::button(menu* menu,
const rect& rect, const std::function<void()>& onClick,
common::asset* image,
int32_t frameDown, int32_t frameUp, int32_t clickSound) : control(menu,
rect.get_x(),
rect.get_y(),
rect.get_width(),
rect.get_height()) {
control::set_enabled(true);
control::set_visible(true);
set_x(rect.get_x());
set_y(rect.get_y());
m_rect = rect;
m_image = new controls::image(menu, image, m_rect.get_x(), m_rect.get_y(), frameUp);
m_frame_down = frameDown;
m_frame_up = frameUp;
m_click_sound = clickSound;
m_is_down = false;
m_current_frame = m_frame_up;
m_on_click = onClick;
}
//struct respos {
// uint32_t res_x;
// uint32_t res_y;
// uint32_t pos_x;
// uint32_t pos_y;
//};
d2_tweaks::ui::controls::button::button(menu* menu, const pugi::xml_node& node) : control(menu, 0, 0, 0, 0) {
char buf[32] = { 0 };
auto cx = node.attribute("default_pos_x").as_int(0);
auto cy = node.attribute("default_pos_y").as_int(0);
m_res_count = node.attribute("resolution_count").as_int(1);
respos temp;
for (uint32_t i = 1; i <= m_res_count; i++) {
sprintf_s(buf, "res%d_x", i);
temp.res_x = node.attribute(buf).as_int(0);
sprintf_s(buf, "res%d_y", i);
temp.res_y = node.attribute(buf).as_int(0);
sprintf_s(buf, "pos%d_x", i);
temp.pos_x = node.attribute(buf).as_int(0);
sprintf_s(buf, "pos%d_y", i);
temp.pos_y = node.attribute(buf).as_int(0);
if (temp.pos_x == diablo2::d2_client::screen_width() && temp.pos_y == diablo2::d2_client::screen_height()) {
cx = temp.pos_x;
cy = temp.pos_y;
}
m_respos.push_back(temp);
}
const auto cname = node.attribute("name").as_string();
const auto cw = node.attribute("width").as_int(0);
const auto ch = node.attribute("height").as_int(0);
const auto cImgPath = node.attribute("image").as_string(nullptr);
const auto cimg = singleton<common::asset_manager>::instance().get_mpq_file(const_cast<char*>(cImgPath), common::MPQ_FILE_TYPE_DC6);
if (!cimg) {
spdlog::critical("Cannot load {0} image for {1} control!", cImgPath, cname);
exit(-1);
}
const auto frameDown = node.attribute("frameDown").as_int();
const auto frameUp = node.attribute("frameUp").as_int();
const auto clickSound = node.attribute("clickSound").as_int(-1);
const auto popup = node.attribute("popup").as_string();
control::set_enabled(true);
control::set_visible(true);
m_rect = rect(cx, cy, cw, ch);
m_image = new image(menu, cimg, m_rect.get_x(), m_rect.get_y(), frameUp);
m_frame_down = frameDown;
m_frame_up = frameUp;
m_click_sound = clickSound;
m_is_down = false;
m_current_frame = m_frame_up;
set_x(cx);
set_y(cy);
control::set_width(cw);
control::set_height(ch);
set_name(cname);
set_popup(string_utils::string_to_wstring(popup));
}
d2_tweaks::ui::controls::button::~button() {
delete m_image;
}
void d2_tweaks::ui::controls::button::set_x(int32_t value) {
m_rect.set_x(value);
m_image->set_x(value);
control::set_x(value);
}
void d2_tweaks::ui::controls::button::set_y(int32_t value) {
m_rect.set_y(value);
m_image->set_y(value);
control::set_y(value);
}
void d2_tweaks::ui::controls::button::draw() {
draw(0, 0);
}
void d2_tweaks::ui::controls::button::draw(int32_t offsetX, int32_t offsetY) {
for (auto it = m_respos.begin(); it != m_respos.end(); it++) {
if (it->res_x == diablo2::d2_client::screen_width() && it->res_y == diablo2::d2_client::screen_height()) {
set_x(it->pos_x);
set_y(it->pos_y);
break;
}
}
if (m_rect.contains(diablo2::d2_client::get_mouse_x(), diablo2::d2_client::get_mouse_y(), offsetX, offsetY) &&
!m_popup.empty()) {
diablo2::d2_win::set_current_font(diablo2::UI_FONT_16);
diablo2::d2_win::set_popup_properties(const_cast<wchar_t*>(m_popup.c_str()),
get_x() + offsetX + m_rect.get_width() / 2,
get_y() + offsetY - m_rect.get_height(),
diablo2::UI_COLOR_WHITE, TRUE);
diablo2::d2_win::draw_popup();
}
m_image->set_frame(m_current_frame);
m_image->draw(offsetX, offsetY);
}
void d2_tweaks::ui::controls::button::left_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) {
if (up && m_is_down) {
block = true;
m_is_down = false;
m_current_frame = m_frame_up;
if (!m_rect.contains(diablo2::d2_client::get_mouse_x(), diablo2::d2_client::get_mouse_y(), offsetX, offsetY))
return;
if (m_on_click)
m_on_click();
return;
}
if (!m_rect.contains(diablo2::d2_client::get_mouse_x(), diablo2::d2_client::get_mouse_y(), offsetX, offsetY))
return;
block = true;
m_is_down = true;
m_current_frame = m_frame_down;
if (m_click_sound < 0)
return;
diablo2::d2_client::play_sound(m_click_sound, nullptr, 0, FALSE, 0);
}
void d2_tweaks::ui::controls::button::right_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) {}
void d2_tweaks::ui::controls::button::key_event(int32_t offsetX, int32_t offsetY, uint32_t key, bool up, bool& block) {}

View File

@@ -0,0 +1,161 @@
#include <d2tweaks/ui/controls/button.h>
#include <d2tweaks/ui/controls/image.h>
#include <diablo2/d2client.h>
#include <diablo2/d2win.h>
#include <diablo2/d2gfx.h>
#include <spdlog/spdlog.h>
#include <d2tweaks/common/asset_manager.h>
#include <common/string_utils.h>
d2_tweaks::ui::controls::button::button(menu* menu,
const rect& rect, const std::function<void()>& onClick,
common::asset* image,
int32_t frameDown, int32_t frameUp, int32_t clickSound) : control(menu,
rect.get_x(),
rect.get_y(),
rect.get_width(),
rect.get_height()) {
control::set_enabled(true);
control::set_visible(true);
set_x(rect.get_x());
set_y(rect.get_y());
m_rect = rect;
m_image = new controls::image(menu, image, m_rect.get_x(), m_rect.get_y(), frameUp);
m_frame_down = frameDown;
m_frame_up = frameUp;
m_click_sound = clickSound;
m_is_down = false;
m_current_frame = m_frame_up;
m_on_click = onClick;
}
d2_tweaks::ui::controls::button::button(menu* menu, const pugi::xml_node& node) : control(menu, 0, 0, 0, 0) {
const auto cname = node.attribute("name").as_string();
const auto cx_lo = node.attribute("x_lowres").as_int(0);
const auto cy_lo = node.attribute("y_lowres").as_int(0);
const auto cx_hi = node.attribute("x_hires").as_int(0);
const auto cy_hi = node.attribute("y_hires").as_int(0);
const auto cw = node.attribute("width").as_int(0);
const auto ch = node.attribute("height").as_int(0);
const auto cImgPath = node.attribute("image").as_string(nullptr);
const auto cimg = singleton<common::asset_manager>::instance().get_mpq_file(
const_cast<char*>(cImgPath), common::MPQ_FILE_TYPE_DC6);
if (!cimg) {
spdlog::critical("Cannot load {0} image for {1} control!", cImgPath, cname);
exit(-1);
}
const auto frameDown = node.attribute("frameDown").as_int();
const auto frameUp = node.attribute("frameUp").as_int();
const auto clickSound = node.attribute("clickSound").as_int(-1);
const auto popup = node.attribute("popup").as_string();
control::set_enabled(true);
control::set_visible(true);
if (diablo2::d2_gfx::get_resolution_mode() == 0) {
m_rect = rect(cx_hi, cy_hi, cw, ch);
}
else {
m_rect = rect(cx_lo, cy_lo, cw, ch);
}
m_image = new image(menu, cimg, m_rect.get_x(), m_rect.get_y(), frameUp);
m_frame_down = frameDown;
m_frame_up = frameUp;
m_click_sound = clickSound;
m_is_down = false;
m_current_frame = m_frame_up;
if (diablo2::d2_gfx::get_resolution_mode() == 0) {
set_x(cx_hi);
set_y(cy_hi);
}
else if (diablo2::d2_gfx::get_resolution_mode() == 2) {
set_x(cx_lo);
set_y(cy_lo);
}
control::set_width(cw);
control::set_height(ch);
set_name(cname);
set_popup(string_utils::string_to_wstring(popup));
}
d2_tweaks::ui::controls::button::~button() {
delete m_image;
}
void d2_tweaks::ui::controls::button::set_x(int32_t value) {
m_rect.set_x(value);
m_image->set_x(value);
control::set_x(value);
}
void d2_tweaks::ui::controls::button::set_y(int32_t value) {
m_rect.set_y(value);
m_image->set_y(value);
control::set_y(value);
}
void d2_tweaks::ui::controls::button::draw() {
draw(0, 0);
}
void d2_tweaks::ui::controls::button::draw(int32_t offsetX, int32_t offsetY) {
if (m_rect.contains(diablo2::d2_client::get_mouse_x(), diablo2::d2_client::get_mouse_y(), offsetX, offsetY) &&
!m_popup.empty()) {
diablo2::d2_win::set_current_font(diablo2::UI_FONT_16);
diablo2::d2_win::set_popup_properties(const_cast<wchar_t*>(m_popup.c_str()),
get_x() + offsetX + m_rect.get_width() / 2,
get_y() + offsetY - m_rect.get_height(),
diablo2::UI_COLOR_WHITE, TRUE);
diablo2::d2_win::draw_popup();
}
m_image->set_frame(m_current_frame);
m_image->draw(offsetX, offsetY);
}
void d2_tweaks::ui::controls::button::left_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) {
if (up && m_is_down) {
block = true;
m_is_down = false;
m_current_frame = m_frame_up;
if (!m_rect.contains(diablo2::d2_client::get_mouse_x(), diablo2::d2_client::get_mouse_y(), offsetX, offsetY))
return;
if (m_on_click)
m_on_click();
return;
}
if (!m_rect.contains(diablo2::d2_client::get_mouse_x(), diablo2::d2_client::get_mouse_y(), offsetX, offsetY))
return;
block = true;
m_is_down = true;
m_current_frame = m_frame_down;
if (m_click_sound < 0)
return;
diablo2::d2_client::play_sound(m_click_sound, nullptr, 0, FALSE, 0);
}
void d2_tweaks::ui::controls::button::right_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) {}
void d2_tweaks::ui::controls::button::key_event(int32_t offsetX, int32_t offsetY, uint32_t key, bool up, bool& block) {}

View File

@@ -0,0 +1,155 @@
#include <d2tweaks/ui/controls/checkbox.h>
#include <d2tweaks/ui/controls/image.h>
#include <d2tweaks/ui/controls/label.h>
#include <common/string_utils.h>
#include <d2tweaks/common/asset_manager.h>
#include <spdlog/spdlog.h>
#include <diablo2/d2client.h>
const int32_t PADDING = 3;
d2_tweaks::ui::controls::checkbox::checkbox(menu* menu, const std::wstring& text, const rect& rect,
const std::function<void()>& onClick,
common::asset* image, int32_t frameChecked, int32_t frameUnchecked, int32_t clickSound)
: control(menu,
rect.get_x(),
rect.get_y(),
rect.get_width(),
rect.get_height()) {
control::set_enabled(true);
control::set_visible(true);
checkbox::set_x(rect.get_x());
checkbox::set_y(rect.get_y());
m_rect = rect;
m_image = new controls::image(menu, image, rect.get_x(), rect.get_y(), frameUnchecked);
m_label = new label(menu, text, rect.get_x() + m_image->get_width() + PADDING, m_rect.get_y());
m_frame_checked = frameChecked;
m_frame_unchecked = frameUnchecked;
m_click_sound = clickSound;
m_is_down = false;
m_state = false;
}
d2_tweaks::ui::controls::checkbox::checkbox(menu* menu, const pugi::xml_node& node) : control(menu, 0, 0, 0, 0) {
control::set_enabled(true);
control::set_visible(true);
const auto cname = node.attribute("name").as_string();
const auto cx = node.attribute("x").as_int(0);
const auto cy = node.attribute("y").as_int(0);
const auto frameChecked = node.attribute("frameChecked").as_int();
const auto frameUnchecked = node.attribute("frameUnchecked").as_int();
const auto clickSound = node.attribute("clickSound").as_int(-1);
const auto popup = node.attribute("popup").as_string();
const auto ctext = node.attribute("text").as_string();
const auto ccolor = node.attribute("color").as_int();
const auto cfont = node.attribute("font").as_int(1);
const auto cImgPath = node.attribute("image").as_string(nullptr);
const auto cimg = singleton<common::asset_manager>::instance().get_mpq_file(
const_cast<char*>(cImgPath), common::MPQ_FILE_TYPE_DC6);
if (!cimg) {
spdlog::critical("Cannot load {0} image for {1} control!", cImgPath, cname);
exit(-1);
}
m_frame_checked = frameChecked;
m_frame_unchecked = frameUnchecked;
m_click_sound = clickSound;
m_popup = string_utils::string_to_wstring(popup);
m_is_down = false;
m_state = false;
m_image = new image(menu, cimg, cx, cy, m_frame_unchecked);
m_label = new label(menu, string_utils::string_to_wstring(ctext), cx + m_image->get_width() + PADDING, cy,
static_cast<diablo2::ui_color_t>(ccolor), static_cast<diablo2::ui_font_t>(cfont));
checkbox::set_x(cx);
checkbox::set_y(cy);
set_name(cname);
}
void d2_tweaks::ui::controls::checkbox::set_x(int32_t value) {
m_rect.set_x(value);
m_image->set_x(value);
m_label->set_x(value + m_image->get_width() + PADDING);
control::set_x(value);
}
void d2_tweaks::ui::controls::checkbox::set_y(int32_t value) {
m_rect.set_y(value);
m_image->set_y(value);
m_label->set_y(value);
control::set_y(value);
}
void d2_tweaks::ui::controls::checkbox::draw() {
draw(0, 0);
}
void d2_tweaks::ui::controls::checkbox::draw(int32_t offsetX, int32_t offsetY) {
if (m_rect.contains(diablo2::d2_client::get_mouse_x(), diablo2::d2_client::get_mouse_y(), offsetX, offsetY) &&
!m_popup.empty()) {
diablo2::d2_win::set_current_font(diablo2::UI_FONT_16);
diablo2::d2_win::set_popup_properties(const_cast<wchar_t*>(m_popup.c_str()),
get_x() + offsetX + m_rect.get_width() / 2,
get_y() + offsetY - m_rect.get_height(),
diablo2::UI_COLOR_WHITE, TRUE);
diablo2::d2_win::draw_popup();
}
m_image->set_frame(m_state ? m_frame_checked : m_frame_unchecked);
m_image->draw(offsetX, offsetY);
m_label->draw(offsetX, offsetY);
}
void d2_tweaks::ui::controls::checkbox::left_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) {
if (m_rect.get_width() == 0)
m_rect.set_width(m_image->get_width() + m_label->get_width() + PADDING);
if (m_rect.get_height() == 0)
m_rect.set_height(m_image->get_height());
if (up && m_is_down) {
block = true;
m_is_down = false;
if (!m_rect.contains(diablo2::d2_client::get_mouse_x(), diablo2::d2_client::get_mouse_y(), offsetX, offsetY))
return;
m_state = !m_state;
if (m_on_click)
m_on_click(m_state);
return;
}
if (!m_rect.contains(diablo2::d2_client::get_mouse_x(), diablo2::d2_client::get_mouse_y(), offsetX, offsetY))
return;
block = true;
m_is_down = true;
if (m_click_sound < 0)
return;
diablo2::d2_client::play_sound(m_click_sound, nullptr, 0, FALSE, 0);
}
void d2_tweaks::ui::controls::checkbox::right_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) {}
void d2_tweaks::ui::controls::checkbox::key_event(int32_t offsetX, int32_t offsetY, uint32_t key, bool up, bool& block) {}

View File

@@ -0,0 +1,108 @@
#include <d2tweaks/ui/controls/group.h>
#include <d2tweaks/ui/controls/button.h>
#include <d2tweaks/ui/controls/label.h>
#include <d2tweaks/ui/controls/checkbox.h>
#include <d2tweaks/ui/controls/image.h>
#include <d2tweaks/ui/menu.h>
d2_tweaks::ui::controls::group::group(menu* menu, int32_t x, int32_t y) : control(menu, x, y, 0, 0) {
control::set_enabled(true);
control::set_visible(true);
}
d2_tweaks::ui::controls::group::group(menu* menu, const pugi::xml_node& node) : control(menu, 0, 0, 0, 0) {
control::set_enabled(true);
control::set_visible(true);
const auto cname = node.attribute("name").as_string();
const auto cx = node.attribute("x").as_int(0);
const auto cy = node.attribute("y").as_int(0);
const auto cw = node.attribute("width").as_int(0);
const auto ch = node.attribute("height").as_int(0);
set_name(cname);
control::set_x(cx);
control::set_y(cy);
control::set_width(cw);
control::set_height(ch);
for (auto child : node.children()) {
const auto name = child.name();
if (strcmp(name, "button") == 0) {
add_control(new button(menu, child));
continue;
}
if (strcmp(name, "checkbox") == 0) {
add_control(new checkbox(menu, child));
continue;
}
if (strcmp(name, "group") == 0) {
add_control(new group(menu, child));
continue;
}
if (strcmp(name, "image") == 0) {
add_control(new image(menu, child));
continue;
}
if (strcmp(name, "label") == 0) {
add_control(new label(menu, child));
}
}
}
void d2_tweaks::ui::controls::group::draw() {
draw(0, 0);
}
void d2_tweaks::ui::controls::group::draw(int32_t offsetX, int32_t offsetY) {
for (auto control : m_controls) {
if (!control->get_visible())
continue;
control->draw(get_x() + offsetX, get_y() + offsetY);
}
}
void d2_tweaks::ui::controls::group::left_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) {
for (auto control : m_controls) {
if (!control->get_enabled())
continue;
control->left_mouse(get_x() + offsetX, get_y() + offsetY, up, block);
}
}
void d2_tweaks::ui::controls::group::right_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) {
for (auto control : m_controls) {
if (!control->get_enabled())
continue;
control->right_mouse(get_x() + offsetX, get_y() + offsetY, up, block);
}
}
void d2_tweaks::ui::controls::group::key_event(int32_t offsetX, int32_t offsetY,
uint32_t key, bool up, bool& block) {
for (auto control : m_controls) {
if (!control->get_enabled())
continue;
control->key_event(get_x() + offsetX, get_y() + offsetY, key, up, block);
}
}
void d2_tweaks::ui::controls::group::add_control(control* control) {
control->set_parent(this);
m_controls.push_back(control);
if (get_menu() == nullptr)
return;
get_menu()->add_control(control);
}

View File

@@ -0,0 +1,95 @@
#include <d2tweaks/ui/controls/image.h>
#include <cstring>
#include <diablo2/d2gfx.h>
#include <d2tweaks/common/asset.h>
#include <d2tweaks/common/asset_manager.h>
#include <spdlog/spdlog.h>
#include <diablo2/structures/cellfile.h>
#include <diablo2/structures/gfxcell.h>
#include <diablo2/d2client.h>
d2_tweaks::ui::controls::image::image(menu* menu, common::asset* image, int32_t x, int32_t y, int32_t frame)
: control(menu, x, y, 0, 0) {
control::set_enabled(true);
control::set_visible(true);
m_image = image;
m_frame = frame;
auto cellFile = static_cast<diablo2::structures::cellfile*>(m_image->get());
const auto cell = cellFile->cells[m_frame];
m_rect = rect(x, y, cell->width + cell->offset_x, cell->height - cell->offset_y);
m_block_click = false;
control::set_width(m_rect.get_width());
control::set_height(m_rect.get_height());
}
d2_tweaks::ui::controls::image::image(menu* menu, const pugi::xml_node& node) : control(menu, 0, 0, 0, 0) {
const auto cname = node.attribute("name").as_string();
const auto cx = node.attribute("x").as_int(0);
const auto cy = node.attribute("y").as_int(0);
const auto cImgPath = node.attribute("image").as_string(nullptr);
const auto cimg = singleton<common::asset_manager>::instance().get_mpq_file(
const_cast<char*>(cImgPath), common::MPQ_FILE_TYPE_DC6);
if (!cimg) {
spdlog::critical("Cannot load {0} image for {1} control!", cImgPath, cname);
exit(-1);
}
const auto frame = node.attribute("frame").as_int();
const auto blockClick = node.attribute("blockClick").as_bool(false);
control::set_enabled(true);
control::set_visible(true);
m_image = cimg;
m_frame = frame;
auto cellFile = static_cast<diablo2::structures::cellfile*>(m_image->get());
const auto cell = cellFile->cells[m_frame];
m_rect = rect(cx, cy, cell->width + cell->offset_x, cell->height - cell->offset_y);
m_block_click = blockClick;
control::set_x(cx);
control::set_y(cy);
control::set_width(m_rect.get_width());
control::set_height(m_rect.get_height());
set_name(cname);
}
void d2_tweaks::ui::controls::image::draw() {
draw(0, 0);
}
void d2_tweaks::ui::controls::image::draw(int32_t offsetX, int32_t offsetY) {
if (m_image == nullptr || !m_image->get())
return;
//I don't like to call memset for each draw_info every frame, but if we wouldn't clear it - we'll get access violation inside draw_image
memset(&m_draw_info, 0x00, sizeof m_draw_info);
m_draw_info.cell_file = static_cast<diablo2::structures::cellfile*>(m_image->get());
m_draw_info.frame = m_frame;
diablo2::d2_gfx::draw_image(&m_draw_info, get_x() + offsetX, get_y() + offsetY, -1, 5, nullptr);
}
void d2_tweaks::ui::controls::image::left_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) {
if (!m_block_click)
return;
block = m_rect.contains(diablo2::d2_client::get_mouse_x(), diablo2::d2_client::get_mouse_y(), offsetX, offsetY);
}
void d2_tweaks::ui::controls::image::right_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) {
if (!m_block_click)
return;
block = m_rect.contains(diablo2::d2_client::get_mouse_x(), diablo2::d2_client::get_mouse_y(), offsetX, offsetY);
}
void d2_tweaks::ui::controls::image::key_event(int32_t offsetX, int32_t offsetY, uint32_t key, bool up, bool& block) {}

View File

@@ -0,0 +1,100 @@
#include <d2tweaks/ui/controls/label.h>
#include <common/string_utils.h>
#include <diablo2/d2client.h>
d2_tweaks::ui::controls::label::label(menu* menu, const std::wstring& text, int32_t x, int32_t y, diablo2::ui_color_t color,
diablo2::ui_font_t font) : control(menu, x, y, 0, 0) {
control::set_enabled(true);
control::set_visible(true);
m_text = text;
m_text_owned = false;
m_color = color;
m_font = font;
control::set_height(0);
control::set_width(0);
}
d2_tweaks::ui::controls::label::label(menu* menu, const pugi::xml_node& node) : control(menu, 0, 0, 0, 0) {
const auto cname = node.attribute("name").as_string();
char buf[32] = { 0 };
auto cx = node.attribute("default_pos_x").as_int(0);
auto cy = node.attribute("default_pos_y").as_int(0);
m_res_count = node.attribute("resolution_count").as_int(1);
respos temp;
for (uint32_t i = 1; i <= m_res_count; i++) {
sprintf_s(buf, "res%d_x", i);
temp.res_x = node.attribute(buf).as_int(0);
sprintf_s(buf, "res%d_y", i);
temp.res_y = node.attribute(buf).as_int(0);
sprintf_s(buf, "pos%d_x", i);
temp.pos_x = node.attribute(buf).as_int(0);
sprintf_s(buf, "pos%d_y", i);
temp.pos_y = node.attribute(buf).as_int(0);
if (temp.pos_x == diablo2::d2_client::screen_width() && temp.pos_y == diablo2::d2_client::screen_height()) {
cx = temp.pos_x;
cy = temp.pos_y;
}
m_respos.push_back(temp);
}
const auto ctext = node.attribute("text").as_string();
const auto ccolor = node.attribute("color").as_int();
const auto cfont = node.attribute("font").as_int(1);
control::set_enabled(true);
control::set_visible(true);
m_text_owned = true;
m_text = string_utils::string_to_wstring(ctext);
m_color = static_cast<diablo2::ui_color_t>(ccolor);
m_font = static_cast<diablo2::ui_font_t>(cfont);
control::set_x(cx);
control::set_y(cy);
control::set_height(0);
control::set_width(0);
set_name(cname);
}
void d2_tweaks::ui::controls::label::draw() {
draw(0, 0);
}
void d2_tweaks::ui::controls::label::draw(int32_t offsetX, int32_t offsetY) {
for (auto it = m_respos.begin(); it != m_respos.end(); it++) {
if (it->res_x == diablo2::d2_client::screen_width() && it->res_y == diablo2::d2_client::screen_height()) {
set_x(it->pos_x);
set_y(it->pos_y);
break;
}
}
const auto font = diablo2::d2_win::get_current_font();
diablo2::d2_win::set_current_font(m_font);
if (get_width() == 0) {
set_width(diablo2::d2_win::get_text_pixel_width(const_cast<wchar_t*>(m_text.c_str())));
}
if (get_height() == 0) {
set_height(diablo2::d2_win::get_current_font_height());
}
diablo2::d2_win::draw_text(const_cast<wchar_t*>(m_text.c_str()), get_x() + offsetX, get_y() + offsetY, m_color, 0);
diablo2::d2_win::set_current_font(font);
}
void d2_tweaks::ui::controls::label::left_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) {}
void d2_tweaks::ui::controls::label::right_mouse(int32_t offsetX, int32_t offsetY, bool up, bool& block) {}
void d2_tweaks::ui::controls::label::key_event(int32_t offsetX, int32_t offsetY, uint32_t key, bool up, bool& block) {}

206
src/d2tweaks/ui/menu.cpp Normal file
View File

@@ -0,0 +1,206 @@
#include <d2tweaks/ui/menu.h>
#include <algorithm>
#include <pugixml.hpp>
#include <stdio.h>
#include <diablo2/utils/mpq_ifstream.h>
#include <diablo2/d2client.h>
#include <spdlog/spdlog.h>
#include <d2tweaks/ui/controls/control.h>
#include <d2tweaks/ui/controls/button.h>
#include <d2tweaks/ui/controls/image.h>
#include <d2tweaks/ui/controls/label.h>
#include <d2tweaks/ui/controls/checkbox.h>
#include <d2tweaks/ui/controls/group.h>
d2_tweaks::ui::menu::menu() : m_x(0), m_y(0), m_width(0), m_height(0) {}
bool d2_tweaks::ui::menu::load_xml(const char* path) {
//diablo2::utils::mpq_ifstream file(path);
//auto file = fopen((char*)path, "r+");
//if (!file)
// return false;
//pugi::xml_document doc;
//const auto result = doc.load_file(path);
diablo2::utils::mpq_ifstream file(path);
if (!file.good())
return false;
pugi::xml_document doc;
const auto result = doc.load(file);
if (!result)
return false;
const auto root = doc.select_nodes("/interface/menu");
if (root.size() == 0)
return false;
const auto menuNode = root.first();
m_x = menuNode.node().attribute("default_pos_x").as_int(0);
m_y = menuNode.node().attribute("default_pos_y").as_int(0);
m_res_count = menuNode.node().attribute("resolution_count").as_int(1);
char buf[32] = { 0 };
respos temp;
for (uint32_t i = 1; i <= m_res_count; i++) {
sprintf_s(buf, "res%d_x", i);
temp.res_x = menuNode.node().attribute(buf).as_int(0);
sprintf_s(buf, "res%d_y", i);
temp.res_y = menuNode.node().attribute(buf).as_int(0);
sprintf_s(buf, "pos%d_x", i);
temp.pos_x = menuNode.node().attribute(buf).as_int(0);
sprintf_s(buf, "pos%d_y", i);
temp.pos_y = menuNode.node().attribute(buf).as_int(0);
if (temp.pos_x == diablo2::d2_client::screen_width() && temp.pos_y == diablo2::d2_client::screen_height()) {
m_x = temp.pos_x;
m_y = temp.pos_y;
}
m_respos.push_back(temp);
}
m_name = menuNode.node().attribute("name").as_string();
m_width = menuNode.node().attribute("width").as_int(-1);
m_height = menuNode.node().attribute("height").as_int(-1);
for (auto child : menuNode.node().children()) {
const auto name = child.name();
if (strcmp(name, "button") == 0) {
add_control(new controls::button(this, child));
continue;
}
if (strcmp(name, "checkbox") == 0) {
add_control(new controls::checkbox(this, child));
continue;
}
if (strcmp(name, "group") == 0) {
add_control(new controls::group(this, child));
continue;
}
if (strcmp(name, "image") == 0) {
add_control(new controls::image(this, child));
continue;
}
if (strcmp(name, "label") == 0) {
add_control(new controls::label(this, child));
}
}
if (m_width == -1) {
for (auto control : m_controls)
if (control->get_width() > m_width)
m_width = control->get_width();
}
if (m_height == -1) {
for (auto control : m_controls)
if (control->get_height() > m_height)
m_height = control->get_height();
}
return true;
}
void d2_tweaks::ui::menu::add_control(controls::control* control) {
if (control == nullptr)
return;
if (control->get_name().empty()) {
spdlog::error("Cannot add control: empty name!");
return;
}
const auto it = m_named_controls.find(control->get_name());
if (it != m_named_controls.end())
return;
m_named_controls[control->get_name()] = control;
m_controls.push_back(control);
}
void d2_tweaks::ui::menu::remove_control(controls::control* control) {
if (control == nullptr)
return;
m_controls.erase(std::remove(m_controls.begin(), m_controls.end(), control), m_controls.end());
}
void d2_tweaks::ui::menu::draw() {
for (auto control : m_controls) {
if (!control->get_visible() || control->get_parent() != nullptr)
continue;
for (auto it = m_respos.begin(); it != m_respos.end(); it++) {
if (it->res_x == diablo2::d2_client::screen_width() && it->res_y == diablo2::d2_client::screen_height()) {
m_x = it->pos_x;
m_y = it->pos_y;
break;
}
}
control->draw(m_x, m_y);
}
}
bool d2_tweaks::ui::menu::left_mouse(bool up) {
auto block = false;
for (auto control : m_controls) {
if (!control->get_enabled() || control->get_parent() != nullptr)
continue;
auto tblock = false;
control->left_mouse(m_x, m_y, up, tblock);
block |= tblock;
}
return block;
}
bool d2_tweaks::ui::menu::right_mouse(bool up) {
auto block = false;
for (auto control : m_controls) {
if (!control->get_enabled() || control->get_parent() != nullptr)
continue;
auto tblock = false;
control->right_mouse(m_x, m_y, up, tblock);
block |= tblock;
}
return block;
}
bool d2_tweaks::ui::menu::key_event(uint32_t key, bool up) {
auto block = false;
for (auto control : m_controls) {
if (!control->get_enabled() || control->get_parent() != nullptr)
continue;
auto tblock = false;
control->key_event(m_x, m_y, key, up, tblock);
block |= tblock;
}
return block;
}

View File

@@ -0,0 +1,181 @@
#include <d2tweaks/ui/ui_manager.h>
#include <Windows.h>
#include <algorithm>
#include <common/hooking.h>
#include <d2tweaks/ui/menu.h>
#include <diablo2/d2win.h>
#include <spdlog/spdlog.h>
static LRESULT(__stdcall* g_wnd_proc_original)(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
d2_tweaks::ui::ui_manager::ui_manager(token) {
hooking::hook(diablo2::d2_win::get_base() + 0xD9B0, wnd_proc, reinterpret_cast<void**>(&g_wnd_proc_original));
}
void d2_tweaks::ui::ui_manager::add_menu(menu* m) {
if (m == nullptr)
return;
const auto it = std::find(m_menus.begin(), m_menus.end(), m);
if (it != m_menus.end())
return;
m_menus.push_back(m);
}
d2_tweaks::ui::menu* d2_tweaks::ui::ui_manager::get_menu(const std::string& name) {
if (name.empty())
return nullptr;
//TODO: optimize somehow
for (auto menu : m_menus) {
if (menu->get_name() == name)
return menu;
}
return nullptr;
}
void d2_tweaks::ui::ui_manager::remove_menu(menu* m) {
if (m == nullptr)
return;
m_menus.erase(std::remove(m_menus.begin(), m_menus.end(), m), m_menus.end());
}
void d2_tweaks::ui::ui_manager::draw() {
//process_inputs();
for (auto menu : m_menus) {
if (!menu->get_visible())
continue;
menu->draw();
}
}
LRESULT d2_tweaks::ui::ui_manager::wnd_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
static auto& instance = singleton<ui_manager>::instance();
bool block;
switch (msg) {
case WM_LBUTTONDOWN:
{
block = instance.process_left_mouse(false);
break;
}
case WM_LBUTTONUP:
{
block = instance.process_left_mouse(true);
break;
}
case WM_RBUTTONDOWN:
{
block = instance.process_right_mouse(false);
break;
}
case WM_RBUTTONUP:
{
block = instance.process_right_mouse(true);
break;
}
case WM_SYSKEYDOWN:
case WM_KEYDOWN:
{
block = instance.process_key_event(wParam, false);
break;
}
case WM_SYSKEYUP:
case WM_KEYUP:
{
block = instance.process_key_event(wParam, true);
break;
}
default: return g_wnd_proc_original(hWnd, msg, wParam, lParam);
}
if (block)
return 0;
return g_wnd_proc_original(hWnd, msg, wParam, lParam);
}
void d2_tweaks::ui::ui_manager::process_inputs() {
if (GetAsyncKeyState(VK_LBUTTON)) {
if (!m_was_down_before_left) {
m_was_down_before_left = true;
m_mouse_state_left = true;
process_left_mouse(false);
}
} else if (m_was_down_before_left) {
m_was_down_before_left = false;
m_mouse_state_left = false;
process_left_mouse(true);
}
if (GetAsyncKeyState(VK_RBUTTON)) {
if (!m_was_down_before_right) {
m_was_down_before_right = true;
m_mouse_state_right = true;
process_right_mouse(false);
}
} else if (m_was_down_before_right) {
m_was_down_before_right = false;
m_mouse_state_right = false;
process_right_mouse(true);
}
}
bool d2_tweaks::ui::ui_manager::process_left_mouse(bool up) {
auto block = false;
for (auto menu : m_menus) {
if (!menu->get_enabled())
continue;
block |= menu->left_mouse(up);
}
return block;
}
bool d2_tweaks::ui::ui_manager::process_right_mouse(bool up) {
auto block = false;
for (auto menu : m_menus) {
if (!menu->get_enabled())
continue;
block |= menu->right_mouse(up);
}
return block;
}
bool d2_tweaks::ui::ui_manager::process_key_event(uint32_t key, bool up) {
auto block = false;
for (auto menu : m_menus) {
if (!menu->get_enabled())
continue;
block |= menu->key_event(key, up);
}
return block;
}