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,441 @@
#include <d2tweaks/client/client.h>
#include <common/hooking.h>
#include <spdlog/spdlog.h>
#include <d2tweaks/common/protocol.h>
#include <d2tweaks/client/modules/client_module.h>
#include <d2tweaks/ui/ui_manager.h>
#include <diablo2/structures/unit.h>
#include <diablo2/structures/client_unit_list.h>
#include <WinBase.h>
//debug junk
//#include <iostream>
#include <filesystem>
#include <fstream>
#include <string>
#include <Windows.h>
#include <sstream>
#include <vector>
#include <map>
#include <codecvt>
#include <iostream>
#include <regex>
#include <regex>
#include <d2tweaks/common/common.h>
#include <diablo2/d2common.h>
#include <diablo2/d2client.h>
#include <diablo2/d2cmp.h>
#include <diablo2/d2win.h>
#include <diablo2/d2gfx.h>
#include <diablo2/d2game.h>
#include <diablo2/structures/unit.h>
#include <diablo2/structures/inventory.h>
#include <diablo2/structures/item_data.h>
#include <diablo2/structures/player_data.h>
#define STB_IMAGE_IMPLEMENTATION
std::vector<StatEntry> globalStatsVector;
diablo2::structures::gfxdata g_gfxdata; // global gfxdata
int randStat;
int randStatRangeLow;
int randStatRangeHigh;
int randStatBool;
std::wstring ConvertCharToWString(const std::string& charString) {
std::wstring_convert<std::codecvt_utf8<wchar_t>> converter;
return converter.from_bytes(charString);
}
// Function to convert std::wstring to std::string
std::string WStringToString(const std::wstring& wstr) {
// Create a codecvt facet for UTF-8 conversion
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
return converter.to_bytes(wstr);
}
// Function to convert std::string to std::wstring
std::wstring StringToWString(const std::string& str) {
// Create a codecvt facet for UTF-8 conversion
std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
return converter.from_bytes(str);
}
// Define the mapColorToEnum function
diablo2::ui_color_t mapColorToEnum(const std::string& colorName) {
static const std::unordered_map<std::string, diablo2::ui_color_t> colorMap = {
{"RED", diablo2::ui_color_t::UI_COLOR_RED},
{"LIGHT_GREEN", diablo2::ui_color_t::UI_COLOR_LIGHT_GREEN},
{"BLUE", diablo2::ui_color_t::UI_COLOR_BLUE},
{"DARK_GOLD", diablo2::ui_color_t::UI_COLOR_DARK_GOLD},
{"GREY", diablo2::ui_color_t::UI_COLOR_GREY},
{"BLACK", diablo2::ui_color_t::UI_COLOR_BLACK},
{"GOLD", diablo2::ui_color_t::UI_COLOR_GOLD},
{"ORANGE", diablo2::ui_color_t::UI_COLOR_ORANGE},
{"YELLOW", diablo2::ui_color_t::UI_COLOR_YELLOW},
{"DARK_GREEN", diablo2::ui_color_t::UI_COLOR_DARK_GREEN},
{"PURPLE", diablo2::ui_color_t::UI_COLOR_PURPLE},
{"GREEN", diablo2::ui_color_t::UI_COLOR_GREEN},
{"WHITE", diablo2::ui_color_t::UI_COLOR_WHITE},
{"BLACK2", diablo2::ui_color_t::UI_COLOR_BLACK2},
{"DARK_WHITE", diablo2::ui_color_t::UI_COLOR_DARK_WHITE},
{"LIGHT_GREY", diablo2::ui_color_t::UI_COLOR_LIGHT_GREY}
};
auto it = colorMap.find(colorName);
if (it != colorMap.end()) {
return it->second;
}
// Default color if not found
return diablo2::ui_color_t::UI_COLOR_WHITE;
}
// Define a struct to hold key-value pairs within a section
struct Section {
std::map<std::string, std::string> assignments;
};
// Function to parse an INI file and extract sections starting with "Stat"
void ParseIniFile(const std::string& iniFilePath) {
std::ifstream inFile(iniFilePath);
if (!inFile) {
std::cerr << "Error opening file: " << iniFilePath << std::endl;
return;
}
std::string line;
std::string currentSection;
StatEntry entry;
while (std::getline(inFile, line)) {
// Trim leading and trailing whitespace from the line
line.erase(line.find_last_not_of(" \t\r\n") + 1);
// Check if the line contains a section header
if (line.size() > 2 && line.front() == '[' && line.back() == ']') {
currentSection = line.substr(1, line.size() - 2); // Extract section name
if (currentSection.find("Stat") == 0) {
entry = {}; // Reset entry for a new section
}
}
else if (!currentSection.empty() && line.find('=') != std::string::npos) {
// Parse key-value pairs within the section
std::istringstream iss(line);
std::string key, value;
if (std::getline(iss, key, '=') && std::getline(iss, value)) {
if (currentSection.find("Stat") == 0) {
// Process key-value pairs for "Stat" sections
if (key == "stat") {
entry.stat = std::stoi(value);
}
else if (key == "stat_display_string") {
entry.stat_display_string = ConvertCharToWString(value); // a conversion function
}
else if (key == "colorStat") {
entry.colorStat = mapColorToEnum(value); // a mapping function
}
else if (key == "colorStatValue") {
entry.colorStatValue = mapColorToEnum(value); // a mapping function
}
else if (key == "x1") {
entry.x1 = std::stoi(value);
}
else if (key == "y1") {
entry.y1 = std::stoi(value);
}
else if (key == "x2") {
entry.x2 = std::stoi(value);
}
else if (key == "y2") {
entry.y2 = std::stoi(value);
}
else if (key == "is_item_stat") {
entry.is_item_stat = std::stoi(value);
}
else if (key == "item_type_id") {
entry.item_type_id = std::stoi(value);
}
}
}
}
else if (!currentSection.empty() && currentSection.find("Stat") == 0) {
// End of section detected, add entry to global vector
globalStatsVector.push_back(entry);
}
}
inFile.close();
}
#include <D2Template.h>
static void(__fastcall* g_handle_packet)(d2_tweaks::common::packet_header* packet, size_t size);
static void(__fastcall* g_handle_packet_standart)(d2_tweaks::common::packet_header* packet, size_t size);
static void(__fastcall* g_handle_cs_packet)(d2_tweaks::common::packet_header* packet, size_t size);
static int32_t(__stdcall* g_draw_game_ui_original)();
static int32_t(__fastcall* g_game_tick_original)(int32_t a1);
//void(__fastcall* g_game_loop_start)();
//static uint32_t g_ret;
d2_tweaks::client::client::client(token) {
m_module_id_counter = 0;
m_tick_handler_id_counter = 0;
}
__declspec(naked) void __stdcall game_tick_wrapper()
{
// usercall, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> ecx = param1, esi = param2
__asm
{
pushad
pushfd
call[d2_tweaks::client::client::game_tick]
popfd
popad
push[g_game_tick_original]
ret
}
}
__declspec (naked) void handle_cs_packet_wrapper() {
__asm {
pushad;
pushfd;
call [d2_tweaks::client::client::handle_cs_packet]
popfd;
popad;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
sub esp, 0x200;
}
RET_TO_RVA(DLLBASE_D2CLIENT, 0xD856);
}
__declspec (naked) void handle_sc_standart_packet_wrapper() {
__asm {
pushad;
pushfd;
call[d2_tweaks::client::client::handle_standart_packet]
popfd;
popad;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
sub esp, 0x54;
push ebx;
push ebp;
}
RET_TO_RVA(DLLBASE_D2CLIENT, 0x150B5);
}
static const DLLPatchStrc gpt_handle_cs_packet[] =
{
{D2DLL_D2CLIENT, 0xD850 + 0, PATCH_JMP, FALSE, 0x1},
{D2DLL_D2CLIENT, 0xD850 + 1, (DWORD)handle_cs_packet_wrapper, TRUE, 0x0},
{D2DLL_D2CLIENT, 0xD850 + 5, (DWORD)PATCH_NOPBLOCK, FALSE, 0x1},
{D2DLL_INVALID}
};
static const DLLPatchStrc gpt_handle_sc_standart_packet[] =
{
{D2DLL_D2CLIENT, 0x150B0 + 0, PATCH_JMP, FALSE, 0x1},
{D2DLL_D2CLIENT, 0x150B0 + 1, (DWORD)handle_sc_standart_packet_wrapper, TRUE, 0x0},
{D2DLL_INVALID}
};
void d2_tweaks::client::client::init() {
// handle packet <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> GamePacketReceivedIntercept
hooking::hook(diablo2::d2_client::get_base() + 0x11CB0, handle_packet, reinterpret_cast<void**>(&g_handle_packet));
hooking::hook(diablo2::d2_client::get_base() + 0x9640, game_tick_wrapper, reinterpret_cast<void**>(&g_game_tick_original));
hooking::hook(diablo2::d2_client::get_base() + 0x5E650, draw_game_ui, reinterpret_cast<void**>(&g_draw_game_ui_original));
//hooking::hook(diablo2::d2_client::get_base() + 0x150B0, handle_standart_packet, reinterpret_cast<void**>(&g_handle_packet_standart));
// Get the path to the INI file
std::string iniFilePath = std::filesystem::current_path().generic_string() + "/d2tweaks.ini";
LPCSTR lpIniFilePath = iniFilePath.c_str();
for (auto& m_module : m_modules) {
if (m_module == nullptr)
break;
randStat = GetPrivateProfileIntA("RandStat", "stat", 0, lpIniFilePath);
randStatRangeLow = GetPrivateProfileIntA("RandStat", "statRangeLow", 0, lpIniFilePath);
randStatRangeHigh = GetPrivateProfileIntA("RandStat", "statRangeHigh", 0, lpIniFilePath);
randStatBool = GetPrivateProfileIntA("RandStat", "statBool", 0, lpIniFilePath);
spdlog::info("randStat = {0}", randStat);
spdlog::info("randStatRangeLow = {0}", randStatRangeLow);
spdlog::info("randStatRangeHigh = {0}", randStatRangeHigh);
// Load and parse the INI file
ParseIniFile(iniFilePath);
}
D2TEMPLATE_ApplyPatch(gpt_handle_cs_packet);
//D2TEMPLATE_ApplyPatch(gpt_handle_sc_standart_packet);
for (auto& m_module : m_modules) {
if (m_module == nullptr)
break;
m_module->init_early();
}
}
static int32_t g_ebp_send_to_client;
void d2_tweaks::client::client::handle_cs_packet(common::packet_header* packet, size_t size) {
#ifndef NDEBUG
__asm {
push [ebp + 0x2C];
pop [g_ebp_send_to_client];
}
spdlog::warn("[d2client SEND] Packet {} Message {} Size {} CallFrom {}", (void*)packet->d2_packet_type, (void*)packet->message_type, size, (void*)g_ebp_send_to_client);
#endif
static common::packet_header dummy;
static auto& instance = singleton<client>::instance();
if (size == -1)
return;
auto handler = instance.m_packet_cs_handlers[packet->d2_packet_type];
if (!handler)
return;
handler->handle_cs_packet(packet);
}
void d2_tweaks::client::client::handle_standart_packet(common::packet_header* packet, size_t size) {
if (size == -1)
return;
#ifndef NDEBUG
spdlog::info("[d2client RECV] Packet {} Message {} Size {}", (void*)packet->d2_packet_type, (void*)packet->message_type, size);
#endif
return;
}
void d2_tweaks::client::client::handle_packet(common::packet_header* packet, size_t size) {
static common::packet_header dummy;
static auto& instance = singleton<client>::instance();
if (size == -1)
return;
#ifndef NDEBUG
spdlog::info("[d2client RECV] Packet {} Message {} Size {}", (void*)packet->d2_packet_type, (void*)packet->message_type, size);
#endif
if (packet->d2_packet_type != dummy.d2_packet_type) {
g_handle_packet(packet, size);
return;
}
auto handler = instance.m_packet_handlers[packet->message_type];
if (!handler)
return;
handler->handle_packet(packet);
}
static bool g_is_init = false;
void d2_tweaks::client::client::game_tick() {
static auto& instance = singleton<client>::instance(); /// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> d2 gl
if (g_is_init == false) {
D2TEMPLATE_Init();
for (auto& m_module : instance.m_modules) {
if (m_module == nullptr)
break;
m_module->init();
}
g_is_init = true;
}
for (auto & tick_handler : instance.m_tick_handlers) {
if (tick_handler == nullptr)
break;
tick_handler->tick();
}
return;
}
int32_t d2_tweaks::client::client::draw_game_ui() {
static auto& ui = singleton<ui::ui_manager>::instance();
const auto result = g_draw_game_ui_original();
ui.draw();
return result;
}
void d2_tweaks::client::client::register_module(modules::client_module* module) {
m_modules[m_module_id_counter++] = module;
}
void d2_tweaks::client::client::register_tick_handler(modules::client_module* module) {
m_tick_handlers[m_tick_handler_id_counter++] = module;
}
void d2_tweaks::client::client::register_packet_cs_handler(common::packet_types_cs_t packet, common::message_types_t type, modules::client_module* module) {
if (m_packet_cs_handlers[packet] != nullptr) {
spdlog::warn("Clientside packet cs handler for {0} is already registered!", type);
}
m_packet_cs_handlers[packet] = module;
}
void d2_tweaks::client::client::register_packet_handler(common::message_types_t type, modules::client_module* module) {
if (m_packet_handlers[type] != nullptr) {
spdlog::warn("Clientside packet handler for {0} is already registered!", type);
}
m_packet_handlers[type] = module;
}
diablo2::structures::unit* d2_tweaks::client::client::get_client_unit(uint32_t type, uint32_t guid) {
static auto units = diablo2::d2_client::get_client_unit_list();
const auto index = guid & 127;
auto result = units->unit_list[type][index];
while (result != nullptr && result->guid != guid) {
result = result->prev_unit;
}
return result;
}

View File

@@ -0,0 +1,141 @@
#include <d2tweaks/client/modules/auto_gold_pickup/auto_gold_pickup_client.h>
#include <d2tweaks/client/client.h>
#include <d2tweaks/common/protocol.h>
#include <d2tweaks/ui/menu.h>
#include <d2tweaks/ui/ui_manager.h>
#include <d2tweaks/ui/controls/label.h>
#include <diablo2/structures/unit.h>
#include <diablo2/structures/item_data.h>
#include <diablo2/structures/data/items_line.h>
#include <diablo2/structures/data/item_types_line.h>
#include <diablo2/structures/inventory.h>
#include <diablo2/structures/room.h>
#include <diablo2/structures/player_data.h>
#include <diablo2/d2client.h>
#include <diablo2/d2common.h>
#include <diablo2/d2win.h>
#include <diablo2/d2gfx.h>
#include <DllNotify.h>
#include <D2Template.h>
MODULE_INIT(auto_gold_pickup)
static uint32_t m_nDisplayTime = 2500;
static uint32_t m_nLastUpdate = 0;
static uint32_t m_nGoldValue = 0;
static int32_t m_iDistance = 4;
class draw_gold_menu final : public d2_tweaks::ui::menu {
public:
d2_tweaks::ui::controls::label* m_label;
draw_gold_menu() {
//load_xml("d2tweaks\\interfaces\\autogoldpickup.xml");
if (DLLBASE_D2EXPRES != 0)
load_xml("d2tweaks\\interface_d2expres\\autogoldpickup.xml");
if (DLLBASE_SGD2FREERES != 0)
load_xml("d2tweaks\\interface_sgd2freeres\\autogoldpickup.xml");
if (DLLBASE_SGD2FREERES == 0 && DLLBASE_D2EXPRES == 0)
load_xml("d2tweaks\\interface_vanilla\\autogoldpickup.xml");
m_label = get_control<d2_tweaks::ui::controls::label>("m_gold_label");
menu::set_visible(true);
menu::set_enabled(true);
}
void draw() override {
static wchar_t buffer[1024];
if (m_nGoldValue == 0) {
return;
}
if (GetTickCount() - m_nLastUpdate >= m_nDisplayTime) {
m_nGoldValue = 0;
return;
}
swprintf_s(buffer, L"+%i", m_nGoldValue);
m_label->set_text(buffer);
m_label->draw();
}
};
void d2_tweaks::client::modules::auto_gold_pickup::init_early() {
}
void d2_tweaks::client::modules::auto_gold_pickup::init() {
char acPathToIni[MAX_PATH] = { 0 };
const char* pcIniFile = "\\d2tweaks.ini";
GetCurrentDirectory(MAX_PATH, acPathToIni);
lstrcat(acPathToIni, pcIniFile);
if (GetPrivateProfileInt("modules", "AutoGoldPickup", 1, acPathToIni) != FALSE) {
m_iDistance = GetPrivateProfileInt("AutoGoldPickup", "PickupDistance", 4, acPathToIni);
m_nDisplayTime = GetPrivateProfileInt("AutoGoldPickup", "DisplayTime", 2500, acPathToIni);
singleton<client>::instance().register_packet_handler(common::message_types_t::MESSAGE_TYPE_GOLD_PICKUP_INFO, this);
singleton<client>::instance().register_tick_handler(this);
singleton<ui::ui_manager>::instance().add_menu(new draw_gold_menu());
}
}
void d2_tweaks::client::modules::auto_gold_pickup::tick() {
const auto unit = diablo2::d2_client::get_local_player();
if (!unit)
return;
if (unit->type != diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER)
return;
const auto room = diablo2::d2_common::get_room_from_unit(unit);
if (!room)
return;
for (auto item = room->unit; item; item = item->prev_unit_in_room) {
if (!item)
continue;
if (item->type != diablo2::structures::unit_type_t::UNIT_TYPE_ITEM)
continue;
auto record = diablo2::d2_common::get_item_record(item->data_record_index);
if (!record)
continue;
const auto distance = diablo2::d2_common::get_distance_between_units(unit, item);
if (distance > m_iDistance)
continue;
if (record->string_code[0] == 'g' &&
record->string_code[1] == 'l' &&
record->string_code[2] == 'd')
{
static d2_tweaks::common::gold_pickup_info_cs request_packet_cs;
request_packet_cs.item_guid = item->guid;
diablo2::d2_client::send_to_server(&request_packet_cs, sizeof request_packet_cs);
}
continue;
}
}
void d2_tweaks::client::modules::auto_gold_pickup::handle_packet(common::packet_header* packet) {
const auto info = static_cast<common::gold_pickup_info_sc*>(packet);
m_nLastUpdate = GetTickCount();
m_nGoldValue += info->gold;
}

View File

@@ -0,0 +1,574 @@
#include <d2tweaks/client/modules/auto_item_pickup/auto_item_pickup_client.h>
#include <d2tweaks/client/client.h>
#include <common/hooking.h>
#include <d2tweaks/common/common.h>
#include <d2tweaks/common/protocol.h>
#include <d2tweaks/common/asset_manager.h>
#include <d2tweaks/ui/menu.h>
#include <d2tweaks/ui/ui_manager.h>
#include <d2tweaks/ui/controls/control.h>
#include <d2tweaks/ui/controls/button.h>
#include <diablo2/d2client.h>
#include <diablo2/d2win.h>
#include <diablo2/d2gfx.h>
#include <diablo2/d2common.h>
#include <diablo2/d2lang.h>
#include <diablo2/structures/unit.h>
#include <diablo2/structures/room.h>
#include <diablo2/structures/data/items_line.h>
#include <diablo2/structures/data/item_types_line.h>
#include <diablo2/structures/player_data.h>
#include <diablo2/structures/unit.h>
#include <diablo2/structures/inventory.h>
#include <diablo2/structures/item_data.h>
#include <common/autopickup_lootfilter.h>
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <common/ini.h>
#include <DllNotify.h>
#include <D2Template.h>
MODULE_INIT(auto_item_pickup)
static int32_t m_iDistance = 0;
static uint32_t m_bAutoItemPickupEnabled = 0;
static uint32_t m_nTick = 0;
static bool m_bToggleAutoItemPickup = false;
static bool m_bInventoryFull = false;
static item_code* m_stItemList;
static item_type* m_stItemTypes;
static uint32_t m_nCountItemListAll = 0;
static uint32_t m_nCountItemTypesAll = 0;
static const uint32_t m_nCountItemListKeys = 30;
static const uint32_t m_nCountItemTypesKeys = 10;
static char* m_apcItemList[m_nCountItemListKeys] = { 0 };
static char* m_apcItemTypes[m_nCountItemTypesKeys] = { 0 };
static char* m_pcItemListAll;
static char* m_pcItemListAllTemp;
static char* m_pcItemTypesAll;
static char* m_pcItemTypesAllTemp;
static char m_acBuffer[1024] = { 0 };
static char m_acPathToIni[MAX_PATH] = { 0 };
static const char* m_pcIniFile = "\\d2tweaks.ini";
void d2_tweaks::client::modules::auto_item_pickup::init_early() {
}
void ReloadFilters(char* szPathToIni) {
uint32_t dwLenght = 0;
m_nCountItemListAll = 0;
m_nCountItemTypesAll = 0;
free(m_stItemList);
free(m_stItemTypes);
free(m_pcItemListAllTemp);
free(m_pcItemListAll);
free(m_pcItemTypesAll);
free(m_pcItemTypesAllTemp);
m_pcItemListAll = (char*)malloc(MAX_STRING_LENGHT * m_nCountItemListKeys);
m_pcItemListAllTemp = (char*)malloc(MAX_STRING_LENGHT * m_nCountItemListKeys);
m_pcItemTypesAll = (char*)malloc(MAX_STRING_LENGHT * m_nCountItemTypesKeys);
m_pcItemTypesAllTemp = (char*)malloc(MAX_STRING_LENGHT * m_nCountItemTypesKeys);
memset(m_pcItemListAll, 0, MAX_STRING_LENGHT * m_nCountItemListKeys);
memset(m_pcItemListAllTemp, 0, MAX_STRING_LENGHT * m_nCountItemListKeys);
memset(m_pcItemTypesAll, 0, MAX_STRING_LENGHT * m_nCountItemTypesKeys);
memset(m_pcItemTypesAllTemp, 0, MAX_STRING_LENGHT * m_nCountItemTypesKeys);
for (uint32_t i = 0; i < m_nCountItemListKeys; i++) {
free(m_apcItemList[i]);
m_apcItemList[i] = (char*)malloc(MAX_STRING_LENGHT);
memset(m_apcItemList[i], 0, MAX_STRING_LENGHT);
sprintf_s(m_acBuffer, sizeof(m_acBuffer), "ItemList%d", i + 1);
dwLenght = GetPrivateProfileString("AutoItemPickup", m_acBuffer, 0, m_apcItemList[i], MAX_STRING_LENGHT - 1, szPathToIni);
if (dwLenght != 0) {
lstrcat(m_pcItemListAll, m_apcItemList[i]);
lstrcat(m_pcItemListAll, "|");
}
}
for (uint32_t i = 0; i < m_nCountItemTypesKeys; i++) {
free(m_apcItemTypes[i]);
m_apcItemTypes[i] = (char*)malloc(MAX_STRING_LENGHT);
memset(m_apcItemTypes[i], 0, MAX_STRING_LENGHT);
sprintf_s(m_acBuffer, sizeof(m_acBuffer), "ItemTypeList%d", i + 1);
dwLenght = GetPrivateProfileString("AutoItemPickup", m_acBuffer, 0, m_apcItemTypes[i], MAX_STRING_LENGHT - 1, szPathToIni);
if (dwLenght != 0) {
lstrcat(m_pcItemTypesAll, m_apcItemTypes[i]);
lstrcat(m_pcItemTypesAll, "|");
}
}
/// Parse ItemCode
dwLenght = lstrlen(m_pcItemListAll);
memcpy(m_pcItemListAllTemp, m_pcItemListAll, dwLenght + 1);
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
char* token_string_itemcode = strtok(m_pcItemListAllTemp, " ,|");
while (token_string_itemcode)
{
m_nCountItemListAll++;
token_string_itemcode = strtok(NULL, " ,|");
}
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
m_stItemList = (item_code*)malloc(m_nCountItemListAll * sizeof(item_code));
memset(m_stItemList, 0, m_nCountItemListAll * sizeof(item_code));
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
memcpy(m_pcItemListAllTemp, m_pcItemListAll, dwLenght + 1);
token_string_itemcode = strtok(m_pcItemListAllTemp, " ,|");
for (uint32_t i = 0; token_string_itemcode != 0; i++)
{
uint32_t cur_string_length = lstrlen(token_string_itemcode);
m_stItemList[i].code0 = token_string_itemcode[0];
m_stItemList[i].code1 = token_string_itemcode[1];
m_stItemList[i].code2 = token_string_itemcode[2];
if (token_string_itemcode[3] == ':') {
// first index quality = 1
for (uint32_t q = 1; q <= 9; q++) {
m_stItemList[i].qualityinclude[q] = FALSE;
}
if (cur_string_length <= 13) { //for example jew:123456789
// p = 4 --> this is first digit after ':'
for (uint32_t p = 4; p <= cur_string_length; p++) {
if (token_string_itemcode[p] == 0) {
break;
}
if (token_string_itemcode[p] >= '0' && token_string_itemcode[p] <= '9') {
m_stItemList[i].qualityinclude[token_string_itemcode[p] - 0x30] = TRUE;
}
}
}
}
if (token_string_itemcode[3] == '-') {
// first index quality = 1
for (uint32_t q = 1; q <= 9; q++) {
m_stItemList[i].qualityinclude[q] = TRUE;
}
if (cur_string_length <= 13) { //for example jew:123456789
// p = 4 --> this is first digit after '-'
for (uint32_t p = 4; p <= cur_string_length; p++) {
if (token_string_itemcode[p] == 0) {
break;
}
if (token_string_itemcode[p] >= '0' && token_string_itemcode[p] <= '9') {
m_stItemList[i].qualityinclude[token_string_itemcode[p] - 0x30] = FALSE;
}
}
}
}
if (token_string_itemcode[3] != '-' && token_string_itemcode[3] != ':')
{
// first index quality = 1
for (uint32_t q = 1; q <= 9; q++) {
m_stItemList[i].qualityinclude[q] = TRUE;
}
}
token_string_itemcode = strtok(NULL, " ,|");
}
/// Parse ItemType Code
dwLenght = lstrlen(m_pcItemTypesAll);
memcpy(m_pcItemTypesAllTemp, m_pcItemTypesAll, dwLenght + 1);
char* token_string_itemtype_code = strtok(m_pcItemTypesAllTemp, ",|");
while (token_string_itemtype_code)
{
m_nCountItemTypesAll++;
token_string_itemtype_code = strtok(NULL, ",|");
}
m_stItemTypes = (item_type*)malloc(m_nCountItemTypesAll * sizeof(item_type));
memset(m_stItemTypes, 0, m_nCountItemTypesAll * sizeof(item_type));
memcpy(m_pcItemTypesAllTemp, m_pcItemTypesAll, dwLenght + 1);
token_string_itemtype_code = strtok(m_pcItemTypesAllTemp, ",|");
for (uint32_t i = 0; token_string_itemtype_code != 0; i++)
{
uint32_t cur_itemtypes_string_length = lstrlen(token_string_itemtype_code);
//m_stItemTypes[i].type0 = token_string_itemtype_code[0];
//m_stItemTypes[i].type1 = token_string_itemtype_code[1];
//m_stItemTypes[i].type2 = token_string_itemtype_code[2];
//m_stItemTypes[i].type3 = token_string_itemtype_code[3];
m_stItemTypes[i].dwtype = *(DWORD*)token_string_itemtype_code;
if (token_string_itemtype_code[4] == ':') {
// first index quality = 1
for (uint32_t q = 1; q <= 9; q++) {
m_stItemTypes[i].qualityinclude[q] = FALSE;
}
if (cur_itemtypes_string_length <= 14) { //for example tors:123456789
// p = 5 --> this is first digit after ':'
for (uint32_t p = 5; p <= cur_itemtypes_string_length; p++) {
if (token_string_itemtype_code[p] == 0) {
break;
}
if (token_string_itemtype_code[p] >= '0' && token_string_itemtype_code[p] <= '9') {
m_stItemTypes[i].qualityinclude[token_string_itemtype_code[p] - 0x30] = TRUE;
}
}
}
}
if (token_string_itemtype_code[4] == '-') {
// first index quality = 1
for (uint32_t q = 1; q <= 9; q++) {
m_stItemTypes[i].qualityinclude[q] = TRUE;
}
if (cur_itemtypes_string_length <= 14) { //for example armo:123456789
// p = 5 --> this is first digit after '-'
for (uint32_t p = 5; p <= cur_itemtypes_string_length; p++) {
if (token_string_itemtype_code[p] == 0) {
break;
}
if (token_string_itemtype_code[p] >= '0' && token_string_itemtype_code[p] <= '9') {
m_stItemTypes[i].qualityinclude[token_string_itemtype_code[p] - 0x30] = FALSE;
}
}
}
}
if (token_string_itemtype_code[4] != '-' && token_string_itemtype_code[4] != ':')
{
// first index quality = 1
for (uint32_t q = 1; q <= 9; q++) {
m_stItemTypes[i].qualityinclude[q] = TRUE;
}
}
token_string_itemtype_code = strtok(NULL, ",|");
}
}
class auto_item_pickup_menu : public d2_tweaks::ui::menu {
d2_tweaks::common::asset* m_buttons_img;
d2_tweaks::ui::controls::button* m_auto_pickup_btn;
public:
auto_item_pickup_menu() {
menu::set_enabled(true);
menu::set_visible(true);
//load_xml("d2tweaks\\interfaces\\autopickup.xml");
if (DLLBASE_D2EXPRES != 0)
load_xml("d2tweaks\\interface_d2expres\\autopickup.xml");
if (DLLBASE_SGD2FREERES != 0)
load_xml("d2tweaks\\interface_sgd2freeres\\autopickup.xml");
if (DLLBASE_SGD2FREERES == 0 && DLLBASE_D2EXPRES == 0)
load_xml("d2tweaks\\interface_vanilla\\autopickup.xml");
m_buttons_img = singleton<d2_tweaks::common::asset_manager>::instance().get_mpq_file("d2tweaks\\assets\\buttons", d2_tweaks::common::MPQ_FILE_TYPE_DC6);
m_auto_pickup_btn = get_button("m_auto_pickup_btn", std::bind(&auto_item_pickup_menu::auto_item_pickup_click, this));
}
void draw() override {
if (m_bToggleAutoItemPickup) {
//diablo2::d2_client::print_chat(L"AUTO TRANSMUTE ON", 1);
m_auto_pickup_btn->set_current_frame(8); // index of frame in buttons.dc6
}
else if (!m_bToggleAutoItemPickup)
{
//diablo2::d2_client::print_chat(L"AUTO TRANSMUTE OFF", 2);
m_auto_pickup_btn->set_current_frame(6);
}
if (m_bToggleAutoItemPickup && m_bInventoryFull) {
m_auto_pickup_btn->set_current_frame(7);
}
//if (diablo2::d2_gfx::get_resolution_mode() == 0) {
// m_auto_pickup_btn->set_x(g_btn_hires_posx);
// m_auto_pickup_btn->set_y(g_btn_hires_posy);
//}
//else {
// m_auto_pickup_btn->set_x(g_btn_lowres_posx);
// m_auto_pickup_btn->set_y(g_btn_lowres_posy);
//}
if (!should_draw()) {
m_auto_pickup_btn->set_enabled(false);
m_auto_pickup_btn->set_visible(false);
return;
}
m_auto_pickup_btn->set_enabled(true);
m_auto_pickup_btn->set_visible(true);
menu::draw();
}
private:
static bool should_draw() {
return diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_INTERFACE);
}
d2_tweaks::ui::controls::button* get_button(const std::string& name, const std::function<void()>& onClick) {
auto result = static_cast<d2_tweaks::ui::controls::button*>(get_control(name));
result->set_on_click(onClick);
result->set_enabled(false);
result->set_visible(false);
return result;
}
void auto_item_pickup_click() {
m_bToggleAutoItemPickup ^= true;
if (m_bToggleAutoItemPickup) {
ReloadFilters(m_acPathToIni);
diablo2::d2_client::print_chat(L"AUTO PICKUP ON", 1);
}
else
{
diablo2::d2_client::print_chat(L"AUTO PICKUP OFF", 2);
}
}
};
void d2_tweaks::client::modules::auto_item_pickup::init() {
GetCurrentDirectory(MAX_PATH, m_acPathToIni);
lstrcat(m_acPathToIni, m_pcIniFile);
if (GetPrivateProfileInt("modules", "AutoItemPickup", 1, m_acPathToIni) != FALSE) {
m_iDistance = GetPrivateProfileInt("AutoItemPickup", "PickupDistance", 4, m_acPathToIni);
//ReloadFilters(m_acPathToIni);
singleton<ui::ui_manager>::instance().add_menu(new auto_item_pickup_menu());
singleton<client>::instance().register_tick_handler(this);
singleton<client>::instance().register_packet_handler(common::message_types_t::MESSAGE_TYPE_ITEM_PICKUP_INFO, this);
}
}
bool find_free_space(diablo2::structures::inventory* inv, diablo2::structures::unit* item, int32_t inventoryIndex, char page, uint32_t& x, uint32_t& y) {
char data[0x18];
diablo2::d2_common::get_inventory_data(inventoryIndex, 0, data);
const auto mx = static_cast<uint32_t>(data[0]);
const auto my = static_cast<uint32_t>(data[1]);
for (x = 0; x < mx; x++) {
for (y = 0; y < my; y++) {
diablo2::structures::unit* blockingUnit = nullptr;
uint32_t blockingUnitIndex = 0;
if (diablo2::d2_common::can_put_into_slot(inv, item, x, y, inventoryIndex, &blockingUnit, &blockingUnitIndex, page))
return true;
}
}
return false;
}
void d2_tweaks::client::modules::auto_item_pickup::tick() {
static common::item_pickup_info_sc packet;
const auto unit = diablo2::d2_client::get_local_player();
//spdlog::debug("g_tick_between_item_pickup {0}", g_tick_between_item_pickup);
m_nTick++;
if (!m_bToggleAutoItemPickup)
return;
if (!unit)
return;
if (unit->type != diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER)
return;
const auto room = diablo2::d2_common::get_room_from_unit(unit);
if (!room)
return;
for (auto item = room->unit; item; item = item->prev_unit_in_room) {
if (!item)
continue;
if (item->type != diablo2::structures::unit_type_t::UNIT_TYPE_ITEM)
continue;
const auto record = diablo2::d2_common::get_item_record(item->data_record_index);
if (!record)
continue;
const auto distance = diablo2::d2_common::get_distance_between_units(unit, item);
if (distance > m_iDistance)
continue;
//// <20><> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
//if (record->string_code[0] == 'g' &&
// record->string_code[1] == 'l' &&
// record->string_code[2] == 'd')
//{
// static d2_tweaks::common::gold_pickup_info_cs request_packet_cs;
// request_packet_cs.item_guid = item->guid;
// diablo2::d2_client::send_to_server(&request_packet_cs, sizeof request_packet_cs);
// continue;
//}
const auto itemtype_record = diablo2::d2_common::get_item_type_record(record->type);
auto itemtype_record_equiv1 = diablo2::d2_common::get_item_type_record(itemtype_record->equiv1);
auto itemtype_record_equiv2 = diablo2::d2_common::get_item_type_record(itemtype_record->equiv2);
uint32_t quality = diablo2::d2_common::get_item_quality(item);
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
char arr_itemtype_codestr_equivstr[20][5] = { 0 };
// itemtype code <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
*(DWORD*)arr_itemtype_codestr_equivstr[0] = *(DWORD*)itemtype_record->code;
// index second code in array
uint32_t index_arr_itemtype = 1;
if (itemtype_record_equiv1) {
if (*(DWORD*)itemtype_record_equiv1->code != 0x20202020) {
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> equiv1
*(DWORD*)arr_itemtype_codestr_equivstr[index_arr_itemtype] = *(DWORD*)itemtype_record_equiv1->code;
index_arr_itemtype++;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> eqiv1 <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
for (index_arr_itemtype; itemtype_record_equiv1->equiv1 != 0; index_arr_itemtype++) {
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
itemtype_record_equiv1 = diablo2::d2_common::get_item_type_record(itemtype_record_equiv1->equiv1);
if (*(DWORD*)itemtype_record_equiv1->code != 0x20202020) {
*(DWORD*)arr_itemtype_codestr_equivstr[index_arr_itemtype] = *(DWORD*)itemtype_record_equiv1->code;
}
else break;
}
}
}
if (itemtype_record_equiv2) {
if (*(DWORD*)itemtype_record_equiv2->code != 0x20202020) {
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> equiv1
*(DWORD*)arr_itemtype_codestr_equivstr[index_arr_itemtype] = *(DWORD*)itemtype_record_equiv2->code;
index_arr_itemtype++;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> eqiv1 <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
for (index_arr_itemtype; itemtype_record_equiv2->equiv2 != 0; index_arr_itemtype++) {
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
itemtype_record_equiv2 = diablo2::d2_common::get_item_type_record(itemtype_record_equiv2->equiv2);
if (*(DWORD*)itemtype_record_equiv2->code != 0x20202020) {
*(DWORD*)arr_itemtype_codestr_equivstr[index_arr_itemtype] = *(DWORD*)itemtype_record_equiv2->code;
}
else break;
}
}
}
static d2_tweaks::common::item_pickup_info_cs request_packet_cs;
request_packet_cs.item_guid = 0;
for (uint32_t i = 0; i < m_nCountItemTypesAll; i++)
{
for (uint32_t count = 0; count < index_arr_itemtype; count++)
{
if (*(DWORD*)arr_itemtype_codestr_equivstr[count] == (DWORD)m_stItemTypes[i].dwtype)
{
if (m_stItemTypes[i].qualityinclude[quality] == TRUE)
{
const auto inventoryIndex = diablo2::d2_common::get_inventory_index(unit, 0, 0x65);
uint32_t tx = 0, ty = 0;
if (!find_free_space(unit->inventory, item, inventoryIndex, 0, tx, ty)) {
m_bInventoryFull = true;
if (m_nTick >= 500) {
diablo2::d2_client::play_sound(0xB76, unit, 0, 0, 0);
m_nTick = 0;
}
goto L1;
}
else {
m_bInventoryFull = false;
}
request_packet_cs.item_guid = item->guid;
diablo2::d2_client::send_to_server(&request_packet_cs, sizeof request_packet_cs);
goto L1;
}
}
}
}
for (uint32_t i = 0; i < m_nCountItemListAll; i++)
{
if (record->string_code[0] == m_stItemList[i].code0 &&
record->string_code[1] == m_stItemList[i].code1 &&
record->string_code[2] == m_stItemList[i].code2)
{
if (m_stItemList[i].qualityinclude[quality] == TRUE)
{
const auto inventoryIndex = diablo2::d2_common::get_inventory_index(unit, 0, 0x65);
uint32_t tx = 0, ty = 0;
if (!find_free_space(unit->inventory, item, inventoryIndex, 0, tx, ty)) {
m_bInventoryFull = true;
if (m_nTick >= 500) {
diablo2::d2_client::play_sound(0xB76, unit, 0, 0, 0);
m_nTick = 0;
}
goto L1;
}
else {
m_bInventoryFull = false;
}
request_packet_cs.item_guid = item->guid;
diablo2::d2_client::send_to_server(&request_packet_cs, sizeof request_packet_cs);
goto L1;
}
}
}
L1:;
}
}
void d2_tweaks::client::modules::auto_item_pickup::handle_packet(common::packet_header* packet) {
const auto info = static_cast<common::item_pickup_info_sc*>(packet);
//if (info->inventory_full == true) {
// m_nTick = 0;
//}
//else {
// g_tick_between_item_pickup = g_delay_between_item_pickup;
//}
//g_value += info->gold;
}

View File

@@ -0,0 +1,391 @@
#include <d2tweaks/client/modules/autosort/autosort_client.h>
#include <d2tweaks/client/client.h>
#include <spdlog/spdlog.h>
#include <d2tweaks/common/common.h>
#include <d2tweaks/common/protocol.h>
#include <d2tweaks/common/asset_manager.h>
#include <d2tweaks/ui/menu.h>
#include <d2tweaks/ui/ui_manager.h>
#include <d2tweaks/ui/controls/control.h>
#include <d2tweaks/ui/controls/button.h>
#include <diablo2/d2common.h>
#include <diablo2/d2client.h>
#include <diablo2/d2win.h>
#include <diablo2/d2gfx.h>
#include <diablo2/d2cmp.h>
#include <diablo2/structures/unit.h>
#include <diablo2/structures/inventory.h>
#include <diablo2/structures/item_data.h>
#include <diablo2/structures/player_data.h>
#include <diablo2/structures/path.h>
#include <diablo2/structures/game.h>
#include <diablo2/structures/data/items_line.h>
#include <diablo2/structures/data/item_types_line.h>
#include <iostream>
#include <fstream>
#include <string>
#include <filesystem>
#include <unordered_map>
#include <time.h>
#include <cmath>
#include <random>
#include <algorithm>
#include <functional>
#include <vector>
#include <DllNotify.h>
#include <D2Template.h>
MODULE_INIT(autosort)
enum ColorEnum {
RED = diablo2::ui_color_t::UI_COLOR_RED,
LIGHT_GREEN = diablo2::ui_color_t::UI_COLOR_LIGHT_GREEN,
BLUE = diablo2::ui_color_t::UI_COLOR_BLUE,
DARK_GOLD = diablo2::ui_color_t::UI_COLOR_DARK_GOLD,
GREY = diablo2::ui_color_t::UI_COLOR_GREY,
BLACK = diablo2::ui_color_t::UI_COLOR_BLACK,
GOLD = diablo2::ui_color_t::UI_COLOR_GOLD,
ORANGE = diablo2::ui_color_t::UI_COLOR_ORANGE,
YELLOW = diablo2::ui_color_t::UI_COLOR_YELLOW,
DARK_GREEN = diablo2::ui_color_t::UI_COLOR_DARK_GREEN,
PURPLE = diablo2::ui_color_t::UI_COLOR_PURPLE,
GREEN = diablo2::ui_color_t::UI_COLOR_GREEN,
WHITE = diablo2::ui_color_t::UI_COLOR_WHITE,
BLACK2 = diablo2::ui_color_t::UI_COLOR_BLACK2,
DARK_WHITE = diablo2::ui_color_t::UI_COLOR_DARK_WHITE,
LIGHT_GREY = diablo2::ui_color_t::UI_COLOR_LIGHT_GREY
};
class inventory_sort_menu : public d2_tweaks::ui::menu {
d2_tweaks::common::asset* m_buttons_img;
d2_tweaks::ui::controls::button* m_sort_inventory_btn;
d2_tweaks::ui::controls::button* m_sort_stash_btn;
d2_tweaks::ui::controls::button* m_sort_cube_btn;
public:
inventory_sort_menu() {
menu::set_enabled(true);
menu::set_visible(true);
//load_xml("d2tweaks\\interfaces\\autosort.xml");
if (DLLBASE_D2EXPRES != 0)
load_xml("d2tweaks\\interface_d2expres\\autosort.xml");
if (DLLBASE_SGD2FREERES != 0)
load_xml("d2tweaks\\interface_sgd2freeres\\autosort.xml");
if (DLLBASE_SGD2FREERES == 0 && DLLBASE_D2EXPRES == 0)
load_xml("d2tweaks\\interface_vanilla\\autosort.xml");
m_buttons_img = singleton<d2_tweaks::common::asset_manager>::instance().get_mpq_file("d2tweaks\\assets\\buttons", d2_tweaks::common::MPQ_FILE_TYPE_DC6);
m_sort_inventory_btn = get_button("m_sort_inventory_btn", std::bind(&inventory_sort_menu::sort_inventory_click, this));
m_sort_stash_btn = get_button("m_sort_stash_btn", std::bind(&inventory_sort_menu::sort_stash_click, this));
m_sort_cube_btn = get_button("m_sort_cube_btn", std::bind(&inventory_sort_menu::sort_cube_click, this));
}
void draw() override {
auto stats = globalStatsVector;
int textOffset = 40; // Initial offset for the first line
const auto player = diablo2::d2_client::get_local_player();
// Add all items to vector
std::vector<diablo2::structures::unit*> items;
for (auto item = player->inventory->first_item; item != nullptr; item = item->item_data->pt_next_item) {
const auto record = diablo2::d2_common::get_item_record(item->data_record_index);
if (record->type == 104 || record->type == 123) {
items.push_back(item);
}
}
// Initialize statValue
int32_t statValue = 0;
for (const auto& stat : stats) {
double param = 6;
int32_t spirits = diablo2::d2_common::get_stat(player, static_cast<diablo2::unit_stats_t>(185), NULL);
int32_t soulscaptured = statValue = diablo2::d2_common::get_stat(player, static_cast<diablo2::unit_stats_t>(184), NULL);
switch (stat.stat) {
// 2. (statValue <- this is probably op stat1 ? * baseValue <- this is probably op base ) / 2 ^ param
// (op stat1 value * base stat value) / (2 ^ param)
// let's try this fucking thing
case 190: {
// str/spirits
statValue = static_cast<int32_t>((1 * spirits) / pow(2, param)); // what is the value of opStat_str
break;
}
case 191: {
// dex/spirits
statValue = static_cast<int32_t>((1 * spirits) / pow(2, param)); // what is the value of opStat_str
break;
}
case 192: {
// vit/spirits
statValue = static_cast<int32_t>((1 * spirits) / pow(2, param)); // what is the value of opStat_str
break;
}
case 193: {
// enr/spirits
statValue = static_cast<int32_t>((1 * spirits) / pow(2, param)); // what is the value of opStat_str
break;
}
case 200: {
// skills/souls
param = 8;
statValue = static_cast<int32_t>((1 * soulscaptured) / pow(2, param)); // what is the value of opStat_str
break;
}
case 301: {
for (auto item : items) {
const auto record = diablo2::d2_common::get_item_record(item->data_record_index);
if (record->type == 104) {
statValue = diablo2::d2_common::get_stat(item, static_cast<diablo2::unit_stats_t>(stat.stat), NULL);
}
}
break;
}
case 304: {
for (auto item : items) {
const auto record = diablo2::d2_common::get_item_record(item->data_record_index);
if (record->type == 104) {
statValue = diablo2::d2_common::get_stat(item, static_cast<diablo2::unit_stats_t>(stat.stat), NULL);
}
}
break;
}
default: {
// By default, get player stats
statValue = diablo2::d2_common::get_stat(player, static_cast<diablo2::unit_stats_t>(stat.stat), NULL);
break;
}
}
/*
int32_t diablo2::d2_common::set_stat(structures::unit* unit, unit_stats_t stat, uint32_t value, int16_t param) {
static wrap_func_std_import<int32_t(structures::unit*, int32_t, int32_t, int32_t)> set_stat(10517, get_base());
return set_stat(unit, stat, value, param);
}
std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<> dis(randStatRangeLow, randStatRangeHigh);
unsigned int randomNumber = dis(gen);
std::random_device rdb;
std::mt19937 genb(rdb());
std::uniform_int_distribution<> randBool(1, 2);
unsigned int randomBool = randBool(genb) - 1;
if (stat.is_item_stat == 1) {
for (auto item : items) {
const auto record = diablo2::d2_common::get_item_record(item->data_record_index);
int RandStatValue = diablo2::d2_common::get_stat(item, static_cast<diablo2::unit_stats_t>(randStat), NULL);
if (record->type == stat.item_type_id && RandStatValue != 0) {
// set randStat value to random number 1 and 2^(32) = 4294967296
diablo2::d2_common::set_stat(item, static_cast<diablo2::unit_stats_t>(randStat), randomNumber, 0);
diablo2::d2_common::set_stat(item, static_cast<diablo2::unit_stats_t>(randStatBool), randomBool, 0);
}
}
}
else {
// set randStat value to random number 1 and 2^(32) = 4294967296
//diablo2::d2_common::set_stat(player, static_cast<diablo2::unit_stats_t>(randStat), randomNumber, 0);
//diablo2::d2_common::set_stat(player, static_cast<diablo2::unit_stats_t>(randStatBool), randomBool, 0);
int statValue1 = diablo2::d2_common::get_stat(player, static_cast<diablo2::unit_stats_t>(randStat), NULL);
int statValue2 = diablo2::d2_common::get_stat(player, static_cast<diablo2::unit_stats_t>(randStatBool), NULL);
if (statValue1 > 0 ) {
diablo2::d2_common::set_stat(player, static_cast<diablo2::unit_stats_t>(randStat), 0, 0);
diablo2::d2_common::set_stat(player, static_cast<diablo2::unit_stats_t>(randStatBool), 0, 0);
}
}
*/
auto statValueStr = std::to_wstring(statValue);
std::wstring statText = std::wstring(stat.stat_display_string);// .append(L" " + statValueStr);
if (!diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_STASH)
&& !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_CHARACTER)
// && !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_INVENTORY)
// && !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_SKILL)
&& !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_CHAT)
&& !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_NPCMENU)
&& !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_MAINMENU)
&& !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_CONFIG)
&& !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_NPCSHOP)
&& !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_ANVIL)
&& !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_QUEST)
&& !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_QUESTLOG)
&& !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_STATUSAREA)
&& !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_WPMENU)
&& !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_PARTY)
&& !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_TRADE)
&& !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_MSGS)
&& !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_CUBE)
&& !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_BELT)
&& !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_HELP)
&& !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_MERC)
&& !diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_SCROLL)) {
// Draw stats
diablo2::d2_win::set_current_font(diablo2::UI_FONT_16); // Set font to FONT16
diablo2::d2_win::draw_text(const_cast<wchar_t*>(statText.c_str()), stat.x1, stat.y1 + textOffset, stat.colorStat, 0);
diablo2::d2_win::set_current_font(diablo2::UI_FONT_16); // Set font to FONT16
diablo2::d2_win::draw_text(const_cast<wchar_t*>(statValueStr.c_str()), stat.x2, stat.y2 + textOffset, stat.colorStatValue, 0);
//diablo2::d2_win::draw_boxed_text(const_cast<wchar_t*>(statText.c_str()), stat.x1, stat.y1 + textOffset, 1, 0, stat.colorStat);
//diablo2::d2_win::draw_boxed_text(const_cast<wchar_t*>(statValueStr.c_str()), stat.x2, stat.y2 + textOffset, 1, 1, statValueColor);
diablo2::d2_win::set_current_font(diablo2::UI_FONT_16); // Set font to FONT16
//diablo2::structures::d2_cmp::init_gfx_data(&g_gfxdata);
//diablo2::d2_gfx::draw_image(&g_gfxdata, 200, 200, 1, 5, 0);
// instead try to load direct jpg with gdi and insetad ofloading jpg file, specify it bb64 encoded and decode it.
diablo2::ui_color_t::UI_COLOR_WHITE;
}
}
if (!should_draw()) {
m_sort_inventory_btn->set_enabled(false);
m_sort_inventory_btn->set_visible(false);
m_sort_stash_btn->set_enabled(false);
m_sort_stash_btn->set_visible(false);
m_sort_cube_btn->set_enabled(false);
m_sort_cube_btn->set_visible(false);
return;
}
m_sort_inventory_btn->set_enabled(true);
m_sort_inventory_btn->set_visible(true);
m_sort_stash_btn->set_enabled(diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_STASH));
m_sort_stash_btn->set_visible(diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_STASH));
m_sort_cube_btn->set_enabled(diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_CUBE));
m_sort_cube_btn->set_visible(diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_CUBE));
menu::draw();
}
private:
static bool should_draw() {
return diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_INVENTORY) ||
diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_STASH) ||
diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_CUBE) ||
diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_NPCSHOP);
}
d2_tweaks::ui::controls::button* get_button(const std::string& name, const std::function<void()>& onClick) {
auto result = static_cast<d2_tweaks::ui::controls::button*>(get_control(name));
result->set_on_click(onClick);
result->set_enabled(false);
result->set_visible(false);
return result;
}
void sort_inventory_click() {
request_sort(0x00);
}
void sort_stash_click() {
request_sort(0x04);
}
void sort_cube_click() {
request_sort(0x03);
}
void request_sort(const uint8_t page) {
static d2_tweaks::common::inventory_sort_cs packet;
const auto player = diablo2::d2_client::get_local_player();
std::vector<diablo2::structures::unit*> items;
for (auto item = player->inventory->first_item; item != nullptr; item = item->item_data->pt_next_item) {
if (item->item_data->page == page)
items.push_back(item);
}
for (auto item : items)
diablo2::d2_common::inv_remove_item(player->inventory, item);
packet.page = page;
diablo2::d2_client::send_to_server(&packet, sizeof packet);
}
};
void d2_tweaks::client::modules::autosort::init_early() {
}
void d2_tweaks::client::modules::autosort::init() {
char acPathToIni[MAX_PATH] = { 0 };
const char* pcIniFile = "\\d2tweaks.ini";
GetCurrentDirectory(MAX_PATH, acPathToIni);
lstrcat(acPathToIni, pcIniFile);
if (GetPrivateProfileInt("modules", "Autosort", 1, acPathToIni) != FALSE) {
singleton<ui::ui_manager>::instance().add_menu(new inventory_sort_menu());
singleton<client>::instance().register_packet_handler(common::MESSAGE_TYPE_INVENTORY_SORT, this);
}
}
void d2_tweaks::client::modules::autosort::handle_packet(common::packet_header* packet) {
static auto& instance = singleton<client>::instance();
const auto inventorySort = static_cast<common::inventory_sort_sc*>(packet);
const auto item = instance.get_client_unit(0x04, inventorySort->guid); //0x03 -> 0x04 - item
if (item == nullptr)
return;;
const auto player = diablo2::d2_client::get_local_player();
//Last parameter is some boolean
const auto inventoryIndex = diablo2::d2_common::get_inventory_index(player, inventorySort->page, diablo2::d2_client::is_lod());
item->item_data->page = inventorySort->page;
diablo2::d2_common::inv_add_item(player->inventory, item, inventorySort->tx, inventorySort->ty, inventoryIndex, true, item->item_data->page);
diablo2::d2_common::inv_update_item(player->inventory, item, true);
}

View File

@@ -0,0 +1,15 @@
#include <d2tweaks/client/modules/client_module.h>
#include <d2tweaks/client/client.h>
d2_tweaks::client::modules::client_module::client_module() {
singleton<client>::instance().register_module(this);
}
void d2_tweaks::client::modules::client_module::draw_ui() {}
void d2_tweaks::client::modules::client_module::tick() {}
void d2_tweaks::client::modules::client_module::handle_packet(common::packet_header* packet) {}
void d2_tweaks::client::modules::client_module::handle_cs_packet(common::packet_header* packet) {}

View File

@@ -0,0 +1,410 @@
#include <d2tweaks/client/modules/damage_display/damage_display_client.h>
#include <d2tweaks/common/protocol.h>
#include <d2tweaks/client/client.h>
#include <spdlog/spdlog.h>
#include <diablo2/utils/screen.h>
#include <diablo2/d2common.h>
#include <diablo2/structures/unit.h>
#include <diablo2/d2client.h>
#include <d2tweaks/ui/ui_manager.h>
#include <diablo2/d2win.h>
#include <windows.h>
#include <iostream> // std::cout
#include <string> // std::string, std::to_string
#include <fw/pool.h>
#include <diablo2/structures/gfxdata.h>
#include <diablo2/structures/gfxcell.h>
#include <diablo2/d2cmp.h>
#include <common/hooking.h>
MODULE_INIT(damage_display)
struct damage_label {
bool screen_space;
diablo2::ui_color_t color;
uint32_t x;
uint32_t y;
uint32_t unit_width;
uint32_t unit_height;
uint64_t start;
int32_t damage;
int32_t currentHp; // New field for current hit points
int32_t maxHp; // New field for maximum hit points
int32_t text_width;
wchar_t text[64];
damage_label(uint32_t x, uint32_t y, uint32_t uw, uint32_t uh, int32_t damage, int32_t hp, int32_t maxHp)
: screen_space(false), color(diablo2::ui_color_t::UI_COLOR_WHITE), x(x), y(y),
unit_width(uw), unit_height(uh), damage(damage), currentHp(hp), maxHp(maxHp), text_width(42) {
start = GetTickCount64();
memset(text, 0x00, sizeof text);
}
damage_label()
: screen_space(false), color(diablo2::UI_COLOR_WHITE), x(0), y(0),
unit_width(0), unit_height(0), damage(0), currentHp(0), maxHp(0), text_width(0) {
start = GetTickCount64();
memset(text, 0x00, sizeof text);
}
};
static growing_object_pool<damage_label> g_label_pool([]() {
return new damage_label();
});
static const size_t DAMAGE_LABELS_SIZE = 1024;
static damage_label* g_labels[DAMAGE_LABELS_SIZE]{ nullptr };
static size_t g_labels_count = 0;
static uint32_t DISPLAY_TIME = 500;
static float lerp(const float v0, const float v1, const float t) {
return v0 + t * (v1 - v0);
}
static bool add_label(damage_label* label) {
for (size_t i = 0; i < DAMAGE_LABELS_SIZE; i++) {
if (g_labels[i] != nullptr)
continue;
g_labels[i] = label;
g_labels_count++;
return true;
}
return false;
}
static void remove_label(damage_label* label) {
for (size_t i = 0; i < DAMAGE_LABELS_SIZE; i++) {
if (g_labels[i] != label)
continue;
g_labels[i] = nullptr;
g_labels_count--;
return;
}
}
static void remove_label(size_t index) {
if (index < 0 || index >= DAMAGE_LABELS_SIZE)
return;
g_labels[index] = nullptr;
g_labels_count--;
}
static unsigned int g_font_enemy = 0;
static unsigned int g_font_player = 1;
static unsigned int g_player_label_posx = 70;
static unsigned int g_player_label_posy = 500;
static void draw_damage_labels() {
const auto player = diablo2::d2_client::get_local_player();
if (!player)
return;
if (g_labels_count == 0)
return;
const auto currentTime = GetTickCount64();
size_t updatedLabels = 0;
auto mx = 0, my = 0;
const auto font = diablo2::d2_win::get_current_font();
/*for (size_t i = 0; i < DAMAGE_LABELS_SIZE; i++) {
const auto label = g_labels[i];
if (label == nullptr) {
auto next = g_labels[i + 1];
if (updatedLabels == g_labels_count)
break;
continue;
}
const auto delta = currentTime - label->start;
if (delta >= DISPLAY_TIME) {
remove_label(i);
g_label_pool.put(label);
continue;
}
updatedLabels++;
if (label->screen_space) {
mx = label->x;
my = label->y;
diablo2::d2_win::set_current_font((diablo2::ui_font_t)g_font_player);
} else {
diablo2::utils::screen::world_to_screen(label->x, label->y, mx, my);
diablo2::d2_win::set_current_font((diablo2::ui_font_t)g_font_enemy);
}
const auto offset = static_cast<int32_t>(lerp(static_cast<float>(label->unit_height) + 5.f,
static_cast<float>(label->unit_height) + 30.f,
static_cast<float>(delta) / static_cast<float>(DISPLAY_TIME)));
mx -= label->text_width / 2;
mx -= label->unit_width / 2;
my -= offset;
diablo2::d2_win::draw_text(label->text,
mx,
my,
label->color, 0);
}*/
for (size_t i = 0; i < DAMAGE_LABELS_SIZE; i++) {
const auto label = g_labels[i];
if (label == nullptr) {
auto next = g_labels[i + 1];
if (updatedLabels == g_labels_count)
break;
continue;
}
const auto delta = currentTime - label->start;
if (delta >= DISPLAY_TIME) {
remove_label(i);
g_label_pool.put(label);
continue;
}
updatedLabels++;
int32_t mx = label->x;
int32_t my = label->y;
if (label->screen_space) {
diablo2::utils::screen::world_to_screen(label->x, label->y, mx, my);
}
else {
diablo2::utils::screen::world_to_screen(label->x, label->y, mx, my);
}
mx -= label->text_width / 2;
mx -= label->unit_width / 2;
my -= label->unit_height;
// Draw different labels based on screen_space flag
if (label->screen_space) {
const auto offset = static_cast<int32_t>(lerp(static_cast<float>(label->unit_height) + 5.f, static_cast<float>(label->unit_height) + 30.f, static_cast<float>(delta) / static_cast<float>(DISPLAY_TIME)));
mx -= 10;
my -= 50;
my -= offset;
// Draw player label
std::wstring dmgText = std::to_wstring(label->damage);
const wchar_t* dmgTextPtr = dmgText.c_str();
diablo2::d2_win::set_current_font((diablo2::ui_font_t)g_font_player);
diablo2::d2_win::draw_text(const_cast<wchar_t*>(dmgTextPtr), mx, my, label->color, 0);
}
else {
// Draw monster healthbar
if (label->currentHp > 0) {
// Calculate the percentage of currentHp relative to maxHp
float healthPercentage = (label->maxHp != 0) ? static_cast<float>(label->currentHp) / static_cast<float>(label->maxHp) : 0.0f;
// Determine the color based on healthPercentage
diablo2::ui_color_t textColor;
if (healthPercentage >= 0.67f) {
textColor = diablo2::UI_COLOR_GREEN;
}
else if (healthPercentage <= 0.33f) {
textColor = diablo2::UI_COLOR_RED;
}
else {
textColor = diablo2::UI_COLOR_YELLOW;
}
int bMaxWidth = 100;
// Calculate the width of the health bar based on the percentage (max width is 10 characters)
uint32_t barWidth = static_cast<uint32_t>(healthPercentage * 10.0f);
barWidth = (barWidth > bMaxWidth) ? bMaxWidth : barWidth; // Ensure barWidth doesn't exceed 10
barWidth = (label->currentHp > 0 && barWidth == 0) ? 1 : barWidth; // Ensure at least one '#' if currentHp is not 0
// Construct the health bar string representation
std::wstring barText;
for (uint32_t i = 0; i < barWidth; ++i) {
barText.append(L"!"); // Use '#' to represent filled portion of the bar
}
for (uint32_t i = barWidth; i < bMaxWidth; ++i) {
barText.append(L""); // Use '-' to represent empty portion of the bar
}
// Construct the health fraction string
std::wstring fractionStr = std::to_wstring(label->currentHp) + L"/" + std::to_wstring(label->maxHp);
// Combine the strings for health percentage and bar text
std::wstring combinedText = std::to_wstring(static_cast<int>(healthPercentage * 100.0f)) + L"% " + barText;
const WCHAR* combinedTextPtr = combinedText.c_str();
// Calculate text position for the combined text
uint32_t textX = mx;
uint32_t textY = my;
// Draw the combined text (health percentage and bar text)
//diablo2::d2_win::draw_text(const_cast<wchar_t*>(combinedText.c_str()), textX, textY, textColor, 0);
//diablo2::d2_win::draw_boxed_text(const_cast<wchar_t*>(fractionStr.c_str()), textX + label->unit_width/2, textY - 12, 0, 0, textColor);
diablo2::d2_win::set_current_font((diablo2::ui_font_t)g_font_enemy); // Set font to FONT16
diablo2::d2_win::draw_text(const_cast<wchar_t*>(fractionStr.c_str()), textX + diablo2::d2_win::get_text_pixel_width(const_cast<wchar_t*>(combinedTextPtr)) / 2, textY - 12, textColor, 0);
diablo2::d2_win::set_current_font((diablo2::ui_font_t)g_font_enemy); // Set font to FONT6
diablo2::d2_win::draw_text(const_cast<wchar_t*>(combinedText.c_str()), textX, textY, textColor, 0);
//diablo2::d2_win::draw_boxed_text(const_cast<wchar_t*>(combinedText.c_str()), textX, textY, 0, 0, textColor);
const auto offset = static_cast<int32_t>(lerp(static_cast<float>(label->unit_height) + 5.f, static_cast<float>(label->unit_height) + 30.f, static_cast<float>(delta) / static_cast<float>(DISPLAY_TIME)));
my -= offset;
// Draw damage label
std::wstring dmgText = L" " + std::to_wstring(label->damage) + L" ";
const wchar_t* dmgTextPtr = dmgText.c_str();
diablo2::d2_win::set_current_font((diablo2::ui_font_t)g_font_enemy);
diablo2::d2_win::draw_text(const_cast<wchar_t*>(dmgTextPtr), textX + diablo2::d2_win::get_text_pixel_width(const_cast<wchar_t*>(combinedTextPtr)) / 2, my + label->unit_height / 2, textColor, 0);
}
}
}
diablo2::d2_win::set_current_font(font);
}
static void(__fastcall* g_draw_game_ui)(int32_t a1);
static void __fastcall draw_game_ui(int32_t a1) {
draw_damage_labels();
g_draw_game_ui(a1);
}
static diablo2::ui_color_t damage_type_to_color(d2_tweaks::common::damage_type_t type) {
switch (type) {
case d2_tweaks::common::DAMAGE_TYPE_PHYSICAL: return diablo2::UI_COLOR_GREY;
case d2_tweaks::common::DAMAGE_TYPE_COLD: return diablo2::UI_COLOR_BLUE;
case d2_tweaks::common::DAMAGE_TYPE_FIRE: return diablo2::UI_COLOR_RED;
case d2_tweaks::common::DAMAGE_TYPE_LIGHTNING: return diablo2::UI_COLOR_YELLOW;
case d2_tweaks::common::DAMAGE_TYPE_POISON: return diablo2::UI_COLOR_GREEN;
case d2_tweaks::common::DAMAGE_TYPE_MAGIC: return diablo2::UI_COLOR_PURPLE;
default: return diablo2::UI_COLOR_BLACK;
}
}
void d2_tweaks::client::modules::damage_display::init_early() {
}
void d2_tweaks::client::modules::damage_display::init() {
char acPathToIni[MAX_PATH] = { 0 };
const char* pcIniFile = "\\d2tweaks.ini";
GetCurrentDirectory(MAX_PATH, acPathToIni);
lstrcat(acPathToIni, pcIniFile);
if (GetPrivateProfileInt("modules", "DamageDisplay", 1, acPathToIni) != FALSE) {
g_font_enemy = GetPrivateProfileInt("DamageDisplay", "EnemyDamageFont", 0, acPathToIni);
g_font_player = GetPrivateProfileInt("DamageDisplay", "PlayerDamageFont", 1, acPathToIni);
g_player_label_posx = GetPrivateProfileInt("DamageDisplay", "PlayerDamagePosx", 70, acPathToIni);
g_player_label_posy = GetPrivateProfileInt("DamageDisplay", "PlayerDamagePosy", 500, acPathToIni);
DISPLAY_TIME = GetPrivateProfileInt("DamageDisplay", "DisplayTime", 1000, acPathToIni);
singleton<client>::instance().register_packet_handler(common::MESSAGE_TYPE_DAMAGE_INFO, this);
hooking::hook(diablo2::d2_client::get_base() + 0x80A30, draw_game_ui, reinterpret_cast<void**>(&g_draw_game_ui));
}
}
void d2_tweaks::client::modules::damage_display::handle_packet(common::packet_header* packet) {
static auto& instance = singleton<client>::instance();
static diablo2::structures::gfxdata gfxdata;
const auto player = diablo2::d2_client::get_local_player();
const auto info = static_cast<common::damage_info_sc*>(packet);
if (player == nullptr)
return;
if (info->unit_type == 0x00 && info->guid == player->guid) {
const auto label = g_label_pool.get();
label->screen_space = true;
label->color = damage_type_to_color(info->damage_type);
label->unit_width = 0;
label->unit_height = 0;
label->x = g_player_label_posx;
label->y = g_player_label_posy;
label->damage = info->damage;
label->start = GetTickCount64();
swprintf_s(label->text, L"%u", label->damage);
label->text_width = diablo2::d2_win::get_text_pixel_width(label->text);
if (add_label(label))
return;
g_label_pool.put(label); //prevent memory leak if there's no room for another label
return;
}
if (info->unit_type != 0x01)
return;
const auto monsterUnit = instance.get_client_unit(0x01, info->guid);
if (monsterUnit == nullptr)
return;
const auto mx = diablo2::d2_common::get_unit_x(monsterUnit->path);
const auto my = diablo2::d2_common::get_unit_y(monsterUnit->path);
const auto label = g_label_pool.get();
label->color = damage_type_to_color(info->damage_type);
memset(&gfxdata, 0x00, sizeof gfxdata);
int32_t index;
if (diablo2::d2_client::cache_gfx_data(&gfxdata, monsterUnit, nullptr, 0, 0, &index, 1, -1) &&
diablo2::structures::d2_cmp::init_gfx_data(&gfxdata) &&
gfxdata.cell_init) {
label->unit_width = gfxdata.cell_init->width + gfxdata.cell_init->offset_x;
label->unit_height = gfxdata.cell_init->height - gfxdata.cell_init->offset_y;
} else {
label->unit_width = 0;
label->unit_height = 0;
}
label->screen_space = false;
label->x = mx;
label->y = my;
label->damage = info->damage;
label->start = GetTickCount64();
swprintf_s(label->text, L"%i", label->damage);
label->text_width = diablo2::d2_win::get_text_pixel_width(label->text);
if (add_label(label))
return;
g_label_pool.put(label); //prevent memory leak if there's no room for another label
}
void d2_tweaks::client::modules::damage_display::tick() {
}

View File

@@ -0,0 +1,267 @@
#include <d2tweaks/client/modules/item_drop_message/item_drop_message_client.h>
#include <d2tweaks/client/client.h>
#include <d2tweaks/common/protocol.h>
#include <common/hooking.h>
#include <d2tweaks/ui/menu.h>
#include <d2tweaks/ui/ui_manager.h>
#include <spdlog/spdlog.h>
#include <diablo2/d2client.h>
#include <diablo2/d2win.h>
#include <diablo2/d2gfx.h>
#include <diablo2/d2common.h>
#include <common/autopickup_lootfilter.h>
#include <diablo2/structures/data/items_line.h>
#include <diablo2/structures/data/item_types_line.h>
MODULE_INIT(item_drop_message)
struct message {
bool active;
uint64_t start;
int32_t text_width;
uint32_t quality;
wchar_t item_text[256];
char item_code[4];
// количество строк, длина каждой строки
char arr_itemtype_codestr_equivstr[20][5];
message(bool active, uint32_t hires_posx, uint32_t hires_posy, uint32_t lowres_posx, uint32_t lowres_posy, uint32_t quality)
: active(active),
text_width(0), quality(quality) {
start = 0;//GetTickCount();
memset(item_text, 0x00, sizeof item_text);
memset(item_code, 0x00, sizeof item_code);
memset(arr_itemtype_codestr_equivstr, 0x00, sizeof arr_itemtype_codestr_equivstr);
}
message() : active(0), text_width(0), quality(0) {
start = 0;//GetTickCount();
memset(item_text, 0x00, sizeof item_text);
memset(item_code, 0x00, sizeof item_code);
memset(arr_itemtype_codestr_equivstr, 0x00, sizeof arr_itemtype_codestr_equivstr);
}
};
static uint32_t m_nMsgCount = 0;
static message m_stMsg[32];
static wchar_t* m_apwcColorStr[17] = {L"ÿc0", L"ÿc1", L"ÿc2", L"ÿc3", L"ÿc4", L"ÿc5", L"ÿc6", L"ÿc7", L"ÿc8", L"ÿc9", L"ÿc:", L"ÿc;", L"ÿc0", L"ÿc0", L"ÿc0", L"ÿc0", L"ÿc0" };
static wchar_t* m_apwcQualityStr[10] = {L"", L"(Cracked)", L"(Normal)", L"(Superior)", L"(Magic)", L"(Set)", L"(Rare)", L"(Unique)", L"(Crafted)", L"(Tempered)"};
static char* m_apcQualityStr[10] = {"", "(Cracked)", "(Normal)", "(Superior)", "(Magic)", "(Set)", "(Rare)", "(Unique)", "(Crafted)", "(Tempered)"};
static uint32_t m_nHookMethod = 1;
static uint32_t m_anQualityColor[10] = { 0 };
static char m_acSecondString[1024] = { 0 };
static wchar_t m_awcSecondString[1024] = { 0 };
static wchar_t m_awcCode[4] = { 0 };
static wchar_t m_awcItemtypeCode[8] = { 0 };
static wchar_t m_aawcItemtypeEquiv[10][8] = { 0 };
static void(__fastcall* fn_GamePacketReceivedIntercept)(d2_tweaks::common::packet_header* packet, size_t size);
class draw_item_menu final : public d2_tweaks::ui::menu {
public:
draw_item_menu() {
menu::set_visible(true);
menu::set_enabled(true);
}
void draw() override {
for (uint32_t i = 0; i < m_nMsgCount; i++) {
m_stMsg[i].active = false;
}
}
};
void d2_tweaks::client::modules::item_drop_message::GamePacketReceivedIntercept(uint8_t* packet, size_t size) {
if (packet == 0 || size == 0)
return;
if ((packet[0] == 0x9C || (packet[0] == 0x9D)) && (packet[1] == 0x0 || packet[1] == 0x2 || packet[1] == 0x3)) {
static d2_tweaks::common::item_dropped_info_cs info;
info.item_id = *(WORD*)&packet[4];
spdlog::debug("[GamePacketReceived] ItemID {0} Pack {1} {2} {3} {4} Message {5} Size {6}", info.item_id, (void*)packet[4], (void*)packet[5], (void*)packet[6], (void*)packet[7], (void*)packet[1], size);
diablo2::d2_client::send_to_server(&info, sizeof common::item_dropped_info_cs);
}
return;
}
__declspec (naked) void d2_tweaks::client::modules::item_drop_message::GamePacketReceivedInterceptASM() {
__asm
{
// call our function (__fastcall)
pushad;
pushfd;
mov edx, ecx;
mov ecx, ebp;
call d2_tweaks::client::modules::item_drop_message::GamePacketReceivedIntercept;
popfd;
popad;
jmp [fn_GamePacketReceivedIntercept]
}
}
void d2_tweaks::client::modules::item_drop_message::init_early() {
}
void d2_tweaks::client::modules::item_drop_message::init() {
char acPathToIni[MAX_PATH] = { 0 };
const char* pcIniFile = "\\d2tweaks.ini";
GetCurrentDirectory(MAX_PATH, acPathToIni);
lstrcat(acPathToIni, pcIniFile);
if (GetPrivateProfileInt("modules", "ItemDropMessage", 1, acPathToIni) != FALSE) {
m_nMsgCount = GetPrivateProfileInt("ItemDropMessage", "MaxNumMessages", 8, acPathToIni);
m_nHookMethod = GetPrivateProfileInt("ItemDropMessage", "HookMethod", 1, acPathToIni);
m_anQualityColor[0] = GetPrivateProfileInt("ItemDropMessage", "DefaultColor", 0, acPathToIni);
m_anQualityColor[1] = GetPrivateProfileInt("ItemDropMessage", "Cracked", 0, acPathToIni);
m_anQualityColor[2] = GetPrivateProfileInt("ItemDropMessage", "Normal", 0, acPathToIni);
m_anQualityColor[3] = GetPrivateProfileInt("ItemDropMessage", "Superior", 0, acPathToIni);
m_anQualityColor[4] = GetPrivateProfileInt("ItemDropMessage", "Magic", 3, acPathToIni);
m_anQualityColor[5] = GetPrivateProfileInt("ItemDropMessage", "Set", 12, acPathToIni);
m_anQualityColor[6] = GetPrivateProfileInt("ItemDropMessage", "Rare", 9, acPathToIni);
m_anQualityColor[7] = GetPrivateProfileInt("ItemDropMessage", "Unique", 7, acPathToIni);
m_anQualityColor[8] = GetPrivateProfileInt("ItemDropMessage", "Crafted", 8, acPathToIni);
m_anQualityColor[9] = GetPrivateProfileInt("ItemDropMessage", "Tempered", 10, acPathToIni);
GetPrivateProfileString("ItemDropMessage", "SecondString", "", m_acSecondString, 1023, acPathToIni);
mbstowcs(m_awcSecondString, m_acSecondString, 1023);
// d2hackit hook d2client:$0x15123
if (m_nHookMethod == 1) {
hooking::hook(diablo2::d2_client::get_base() + 0x15116, GamePacketReceivedInterceptASM, reinterpret_cast<void**>(&fn_GamePacketReceivedIntercept));
}
if (m_nHookMethod == 2) {
hooking::hook(diablo2::d2_client::get_base() + 0x1511A, GamePacketReceivedInterceptASM, reinterpret_cast<void**>(&fn_GamePacketReceivedIntercept));
}
singleton<client>::instance().register_packet_handler(common::message_types_t::MESSAGE_TYPE_ITEM_DROPPED_INFO, this);
singleton<ui::ui_manager>::instance().add_menu(new draw_item_menu());
}
}
void d2_tweaks::client::modules::item_drop_message::handle_packet(common::packet_header* packet) {
const auto info = static_cast<common::item_pickup_info_sc*>(packet);
const auto item_dropped_packet = static_cast<common::item_dropped_info_sc*>(packet);
spdlog::debug("[MyPacketReceived] Message {} Size {}", item_dropped_packet->message_type, sizeof item_dropped_packet);
if (item_dropped_packet->message_type == common::message_types_t::MESSAGE_TYPE_ITEM_DROPPED_INFO) {
for (uint32_t i = 0; i < m_nMsgCount; i++) {
if (m_stMsg[i].active == false) {
static wchar_t buffer[512];
static char buffermb[512];
m_stMsg[i].active = true;
m_stMsg[i].item_code[0] = item_dropped_packet->code[0];
m_stMsg[i].item_code[1] = item_dropped_packet->code[1];
m_stMsg[i].item_code[2] = item_dropped_packet->code[2];
m_stMsg[i].quality = item_dropped_packet->quality;
m_stMsg[i].start = GetTickCount();
mbstowcs(buffer, (const char*)item_dropped_packet->namestr, 128);
memcpy(m_stMsg[i].item_text, buffer, 128);
memcpy(m_stMsg[i].arr_itemtype_codestr_equivstr, item_dropped_packet->arr_itemtype_codestr_equivstr, sizeof item_dropped_packet->arr_itemtype_codestr_equivstr);
//сделать в названии предмета замену всех переносов на пробелы
for (uint32_t c = 0; c <= 128; c++) {
if (m_stMsg[i].item_text[c] == '\n') {
m_stMsg[i].item_text[c] = ' ';
}
}
if (lstrlenW(m_awcSecondString) != 0) {
swprintf_s(buffer, L"%s%s%s", m_stMsg[i].item_text, m_apwcColorStr[m_anQualityColor[0]], m_awcSecondString);
}
else {
swprintf_s(buffer, L"%s", m_stMsg[i].item_text);
}
// вывести сообщение о любом предмете
if (GetKeyState(VK_SCROLL) != 0) {
mbstowcs(m_awcCode, m_stMsg[i].item_code, 3);
mbstowcs(m_awcItemtypeCode, m_stMsg[i].arr_itemtype_codestr_equivstr[0], 4);
mbstowcs(m_aawcItemtypeEquiv[0], m_stMsg[i].arr_itemtype_codestr_equivstr[1], 4);
mbstowcs(m_aawcItemtypeEquiv[1], m_stMsg[i].arr_itemtype_codestr_equivstr[2], 4);
mbstowcs(m_aawcItemtypeEquiv[2], m_stMsg[i].arr_itemtype_codestr_equivstr[3], 4);
mbstowcs(m_aawcItemtypeEquiv[3], m_stMsg[i].arr_itemtype_codestr_equivstr[4], 4);
mbstowcs(m_aawcItemtypeEquiv[4], m_stMsg[i].arr_itemtype_codestr_equivstr[5], 4);
mbstowcs(m_aawcItemtypeEquiv[5], m_stMsg[i].arr_itemtype_codestr_equivstr[6], 4);
mbstowcs(m_aawcItemtypeEquiv[6], m_stMsg[i].arr_itemtype_codestr_equivstr[7], 4);
mbstowcs(m_aawcItemtypeEquiv[7], m_stMsg[i].arr_itemtype_codestr_equivstr[8], 4);
mbstowcs(m_aawcItemtypeEquiv[8], m_stMsg[i].arr_itemtype_codestr_equivstr[9], 4);
mbstowcs(m_aawcItemtypeEquiv[9], m_stMsg[i].arr_itemtype_codestr_equivstr[10], 4);
swprintf_s(buffer, L"%s ÿc0Code: %s Quality: %i %s Type: %s Equiv: %s %s %s %s %s %s %s %s %s %s",
m_stMsg[i].item_text,
m_awcCode,
m_stMsg[i].quality,
m_apwcQualityStr[m_stMsg[i].quality],
m_awcItemtypeCode,
m_aawcItemtypeEquiv[0],
m_aawcItemtypeEquiv[1],
m_aawcItemtypeEquiv[2],
m_aawcItemtypeEquiv[3],
m_aawcItemtypeEquiv[4],
m_aawcItemtypeEquiv[5],
m_aawcItemtypeEquiv[6],
m_aawcItemtypeEquiv[7],
m_aawcItemtypeEquiv[8],
m_aawcItemtypeEquiv[9]);
}
diablo2::d2_client::print_chat(buffer, (diablo2::ui_color_t)m_anQualityColor[m_stMsg[i].quality]);
char* nul = "";
wcstombs(buffermb, m_stMsg[i].item_text, 256);
spdlog::info("{} {} ItemList |{}:{}| ItemTypeCode |{}:{}| ItemTypeEquiv |{}:{}| |{}:{}| |{}:{}| |{}:{}| |{}:{}| |{}:{}| |{}:{}| |{}:{}| |{}:{}| |{}:{}|",
buffermb,
m_apcQualityStr[m_stMsg[i].quality],
m_stMsg[i].item_code, m_stMsg[i].quality,
(*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[0] != 0) ? m_stMsg[i].arr_itemtype_codestr_equivstr[0] : nul,
(*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[0] != 0) ? m_stMsg[i].quality : 0,
(*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[1] != 0) ? m_stMsg[i].arr_itemtype_codestr_equivstr[1] : nul,
(*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[1] != 0) ? m_stMsg[i].quality : 0,
(*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[2] != 0) ? m_stMsg[i].arr_itemtype_codestr_equivstr[2] : nul,
(*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[2] != 0) ? m_stMsg[i].quality : 0,
(*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[3] != 0) ? m_stMsg[i].arr_itemtype_codestr_equivstr[3] : nul,
(*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[3] != 0) ? m_stMsg[i].quality : 0,
(*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[4] != 0) ? m_stMsg[i].arr_itemtype_codestr_equivstr[4] : nul,
(*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[4] != 0) ? m_stMsg[i].quality : 0,
(*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[5] != 0) ? m_stMsg[i].arr_itemtype_codestr_equivstr[5] : nul,
(*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[5] != 0) ? m_stMsg[i].quality : 0,
(*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[6] != 0) ? m_stMsg[i].arr_itemtype_codestr_equivstr[6] : nul,
(*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[6] != 0) ? m_stMsg[i].quality : 0,
(*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[7] != 0) ? m_stMsg[i].arr_itemtype_codestr_equivstr[7] : nul,
(*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[7] != 0) ? m_stMsg[i].quality : 0,
(*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[8] != 0) ? m_stMsg[i].arr_itemtype_codestr_equivstr[8] : nul,
(*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[8] != 0) ? m_stMsg[i].quality : 0,
(*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[9] != 0) ? m_stMsg[i].arr_itemtype_codestr_equivstr[9] : nul,
(*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[9] != 0) ? m_stMsg[i].quality : 0,
(*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[10] != 0) ? m_stMsg[i].arr_itemtype_codestr_equivstr[10] : nul,
(*(DWORD*)m_stMsg[i].arr_itemtype_codestr_equivstr[10] != 0) ? m_stMsg[i].quality : 0);
break;
}
}
}
}

View File

@@ -0,0 +1,166 @@
#include <d2tweaks/client/modules/item_move/item_move_client.h>
#include <d2tweaks/client/client.h>
#include <d2tweaks/common/protocol.h>
#include <common/ptr_wrapper.h>
#include <diablo2/d2client.h>
#include <diablo2/d2common.h>
#include <diablo2/structures/data_tables.h>
#include <diablo2/structures/unit.h>
#include <diablo2/structures/item_data.h>
#include <diablo2/structures/data/items_line.h>
#include <diablo2/structures/inventory.h>
#include <common/hooking.h>
#include <spdlog/spdlog.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
MODULE_INIT(item_move)
int32_t(__fastcall* g_item_click_original)(diablo2::structures::unit* playerUnit, diablo2::structures::inventory* inventory, int mouse_x, int mouse_y, uint8_t flag, void* a6, unsigned int page);
char get_target_page(char currentPage) {
if (currentPage == 0) { //item is in inventory
if (diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_STASH))
return 4;
if (diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_CUBE))
return 3;
}
return 0;
}
void request_item_move(diablo2::structures::unit* item, char targetPage) {
static d2_tweaks::common::item_move_cs packet;
packet.item_guid = item->guid;
packet.target_page = targetPage;
diablo2::d2_client::send_to_server(&packet, sizeof packet);
}
int32_t __fastcall item_click(diablo2::structures::unit* owner, diablo2::structures::inventory* inventory, int mouse_x, int mouse_y, uint8_t flag, char* a6, unsigned int page) {
const auto player = diablo2::d2_client::get_local_player();
if (owner->guid != player->guid)
return g_item_click_original(owner, inventory, mouse_x, mouse_y, flag, a6, page);
if ((static_cast<uint16_t>(GetAsyncKeyState(VK_CONTROL)) >> 8 & 0x80u) == 0)
return g_item_click_original(owner, inventory, mouse_x, mouse_y, flag, a6, page);
if (!diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_CUBE) &&
!diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_STASH)) {
return g_item_click_original(owner, inventory, mouse_x, mouse_y, flag, a6, page);
}
//code below taken from IDA directly, so that's why it is so ugly
const auto coefx1 = *(reinterpret_cast<uint32_t*>(a6) + 1);
const auto coefx2 = static_cast<unsigned int>(a6[20]);
const auto coefy1 = *(reinterpret_cast<uint32_t*>(a6) + 3);
const auto coefy2 = static_cast<unsigned int>(a6[21]);
const auto itemx = (mouse_x - coefx1) / coefx2;
const auto itemy = (mouse_y - coefy1) / coefy2;
diablo2::structures::unit* cubeItem = nullptr;
uint32_t px, py;
const auto currentInventoryIndex = diablo2::d2_common::get_inventory_index(player, page, diablo2::d2_client::is_lod());
const auto clickedItem = diablo2::d2_common::get_item_at_cell(player->inventory, itemx, itemy, &px, &py, currentInventoryIndex, page);
for (auto item = player->inventory->first_item; item != nullptr; item = item->item_data->pt_next_item) {
auto record = diablo2::d2_common::get_item_record(item->data_record_index);
//auto flags1 = diablo2::d2_common::get_item_record(item->item_data->flags1);
//auto flags2 = diablo2::d2_common::get_item_record(item->item_data->flags2);
if (record->string_code[0] == 'b' &&
record->string_code[1] == 'o' &&
record->string_code[2] == 'x') { //Cube
cubeItem = item;
break;
}
}
static wrap_value<diablo2::structures::data_tables*> dataTables(0x96A20, diablo2::d2_common::get_base());
auto sgptdataTables = *dataTables;
if (clickedItem != nullptr) {
const auto itemid = diablo2::d2_common::get_item_unique_index(clickedItem);
const auto itemRecord = diablo2::d2_common::get_item_record(clickedItem->data_record_index);
if (itemid != 0) {
if ((itemid >= 0) && (itemid < (int)sgptdataTables->nUniqueItems)) {
diablo2::structures::unique_items* uniqueItems = sgptdataTables->pUniqueItems + itemid;
if (uniqueItems && (uniqueItems->carry1 == 1)) {
//spdlog::warn("ItemID int: {0:d}; hex: {0:x}; oct: {0:o}; bin: {0:b}", itemid);
diablo2::d2_client::play_sound(itemRecord->drop_sound, nullptr, 0, 0, 0);
return g_item_click_original(owner, inventory, mouse_x, mouse_y, flag, a6, page);
}
}
}
}
if (clickedItem == nullptr) {
return g_item_click_original(owner, inventory, mouse_x, mouse_y, flag, a6, page);
}
const auto targetPage = get_target_page(page);
if (targetPage == 0x03 && clickedItem == cubeItem)
return g_item_click_original(owner, inventory, mouse_x, mouse_y, flag, a6, page);
request_item_move(clickedItem, targetPage);
return 0;
}
void d2_tweaks::client::modules::item_move::init_early() {
}
void d2_tweaks::client::modules::item_move::init() {
char acPathToIni[MAX_PATH] = { 0 };
const char* pcIniFile = "\\d2tweaks.ini";
GetCurrentDirectory(MAX_PATH, acPathToIni);
lstrcat(acPathToIni, pcIniFile);
if (GetPrivateProfileInt("modules", "ItemMover", 1, acPathToIni) != FALSE) {
hooking::hook(diablo2::d2_client::get_base() + 0x475C0, item_click, reinterpret_cast<void**>(&g_item_click_original));
singleton<client>::instance().register_packet_handler(common::MESSAGE_TYPE_ITEM_MOVE, this);
}
}
void d2_tweaks::client::modules::item_move::handle_packet(common::packet_header* packet) {
static auto& instance = singleton<client>::instance();
const auto itemMove = static_cast<common::item_move_sc*>(packet);
const auto item = instance.get_client_unit(0x04, itemMove->item_guid); //0x03 -> 0x04 - item
if (item == nullptr)
return;
const auto player = diablo2::d2_client::get_local_player();
//Last parameter is some boolean
const auto inventoryIndex = diablo2::d2_common::get_inventory_index(player, itemMove->target_page, diablo2::d2_client::is_lod());
item->item_data->page = itemMove->target_page;
diablo2::d2_common::inv_add_item(player->inventory, item, itemMove->tx, itemMove->ty, inventoryIndex, true, item->item_data->page);
diablo2::d2_common::inv_update_item(player->inventory, item, true);
const auto itemRecord = diablo2::d2_common::get_item_record(item->data_record_index);
if (itemRecord != nullptr)
diablo2::d2_client::play_sound(itemRecord->drop_sound, nullptr, 0, 0, 0);
else
diablo2::d2_client::play_sound(4, nullptr, 0, 0, 0);
}

View File

@@ -0,0 +1,57 @@
#include <d2tweaks/client/modules/loot_filter/loot_filter.h>
#include <d2tweaks/client/modules/loot_filter/loot_filter_settings.h>
#include <d2tweaks/client/modules/loot_filter/loot_filter_settings_menu.h>
#include <d2tweaks/client/modules/loot_filter/loot_filter_settings_toggle_menu.h>
#include <common/hooking.h>
#include <d2tweaks/ui/ui_manager.h>
#include <diablo2/d2client.h>
#include <diablo2/d2launch.h>
MODULE_INIT(loot_filter)
static char* (__fastcall* g_set_player_name_original)(void*, void*);
static char* __fastcall set_player_name(void* player, void* edx) {
const auto result = g_set_player_name_original(player, edx);
d2_tweaks::client::modules::loot_filter_settings::load(diablo2::d2_client::get_local_player_name());
singleton<d2_tweaks::client::modules::loot_filter_settings_menu>::instance().reload_settings();
return result;
}
static HANDLE(__fastcall* g_delete_save_file_original)(char*, char*);
static HANDLE __fastcall delete_save_file(char* name, char* a2) {
d2_tweaks::client::modules::loot_filter_settings::remove(name);
return g_delete_save_file_original(name, a2);
}
void d2_tweaks::client::modules::loot_filter::init_early() {
char acPathToIni[MAX_PATH] = { 0 };
const char* pcIniFile = "\\d2tweaks.ini";
GetCurrentDirectory(MAX_PATH, acPathToIni);
lstrcat(acPathToIni, pcIniFile);
if (GetPrivateProfileInt("modules", "LootFilter", 1, acPathToIni) != FALSE) {
hooking::hook(diablo2::d2_client::get_base() + 0xBDE0, set_player_name, &g_set_player_name_original);
hooking::hook(diablo2::d2_launch::get_base() + 0x17C00, delete_save_file, &g_delete_save_file_original);
}
}
void d2_tweaks::client::modules::loot_filter::init() {
char acPathToIni[MAX_PATH] = { 0 };
const char* pcIniFile = "\\d2tweaks.ini";
GetCurrentDirectory(MAX_PATH, acPathToIni);
lstrcat(acPathToIni, pcIniFile);
if (GetPrivateProfileInt("modules", "LootFilter", 1, acPathToIni) != FALSE) {
singleton<ui::ui_manager>::instance().add_menu(&singleton<loot_filter_settings_menu>::instance());
singleton<ui::ui_manager>::instance().add_menu(&singleton<loot_filter_settings_toggle_menu>::instance());
}
}

View File

@@ -0,0 +1,86 @@
#include <d2tweaks/client/modules/loot_filter/loot_filter_settings.h>
#include <filesystem>
#include <spdlog/spdlog.h>
#include <diablo2/fog.h>
using namespace d2_tweaks::client::modules;
static char g_buffer[sizeof(loot_filter_settings)]{ 0 };
static loot_filter_settings* g_settings = reinterpret_cast<loot_filter_settings*>(g_buffer);
loot_filter_settings& loot_filter_settings::get() {
return *g_settings;
}
void loot_filter_settings::save(const char* name) {
static char savePath[1024];
static char buffer[1024];
diablo2::fog::get_save_path(savePath, sizeof savePath);
sprintf_s(buffer, "%s%s_lf.bin", savePath, name);
spdlog::debug("Saving loot filter settings to {0}", buffer);
FILE* file = nullptr;
const auto err = fopen_s(&file, buffer, "wb");
if (err != 0) {
spdlog::critical("Cannot save loot filter settings to {0}", buffer);
exit(0);
}
fwrite(g_buffer, sizeof(loot_filter_settings), 1, file);
fclose(file);
}
void loot_filter_settings::load(const char* name) {
static char savePath[1024];
static char buffer[1024];
diablo2::fog::get_save_path(savePath, sizeof savePath);
sprintf_s(buffer, "%s%s_lf.bin", savePath, name);
spdlog::debug("Loading loot filter settings from {0}", buffer);
if (!std::filesystem::exists(buffer)) {
FILE* file = nullptr;
const auto err = fopen_s(&file, buffer, "wb");
if (err != 0) {
spdlog::critical("Cannot create loot filter settings at {0}", buffer);
exit(0);
}
loot_filter_settings dummy;
fwrite(&dummy, sizeof dummy, 1, file);
fclose(file);
}
FILE* file = nullptr;
const auto err = fopen_s(&file, buffer, "rb");
if (err != 0) {
spdlog::critical("Cannot read loot filter settings from {0}", buffer);
exit(0);
}
auto readed = fread(g_buffer, sizeof(loot_filter_settings), 1, file);
fclose(file);
}
void loot_filter_settings::remove(const char* name) {
static char savePath[1024];
static char buffer[1024];
diablo2::fog::get_save_path(savePath, sizeof savePath);
sprintf_s(buffer, "%s%s_lf.bin", savePath, name);
spdlog::debug("Deleting loot filter settings from {0}", buffer);
if (!std::filesystem::exists(buffer))
return;
std::filesystem::remove(buffer);
memset(g_buffer, 0x00, sizeof g_buffer);
}

View File

@@ -0,0 +1,347 @@
#include <d2tweaks/client/modules/loot_filter/loot_filter_settings_menu.h>
#include <d2tweaks/client/modules/loot_filter/loot_filter_settings.h>
#include <d2tweaks/client/client.h>
#include <common/hooking.h>
#include <common/asm_code.h>
#include <Windows.h>
#include <diablo2/d2client.h>
#include <diablo2/d2common.h>
#include <diablo2/structures/item_data.h>
#include <diablo2/structures/unit.h>
#include <diablo2/structures/data/items_line.h>
#include <diablo2/structures/data/item_types_line.h>
#include <d2tweaks/ui/controls/checkbox.h>
#include <DllNotify.h>
#include <D2Template.h>
d2_tweaks::client::modules::loot_filter_settings_menu::loot_filter_settings_menu(token) {
menu::set_enabled(false);
menu::set_visible(false);
//load_xml("d2tweaks\\interfaces\\loot_filter_settings_menu.xml");
if (DLLBASE_D2EXPRES != 0)
load_xml("d2tweaks\\interface_d2expres\\loot_filter_settings_menu.xml");
if (DLLBASE_SGD2FREERES != 0)
load_xml("d2tweaks\\interface_sgd2freeres\\loot_filter_settings_menu.xml");
if (DLLBASE_SGD2FREERES == 0 && DLLBASE_D2EXPRES == 0)
load_xml("d2tweaks\\interface_vanilla\\loot_filter_settings_menu.xml");
register_misc_checkboxes();
register_quality_checkboxes();
setup_hooks();
}
void d2_tweaks::client::modules::loot_filter_settings_menu::reload_settings() {
register_misc_checkboxes();
register_quality_checkboxes();
}
void d2_tweaks::client::modules::loot_filter_settings_menu::draw() {
if (diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_MAINMENU))
return;
menu::draw();
}
void d2_tweaks::client::modules::loot_filter_settings_menu::register_misc_checkboxes() {
m_altonly = get_control<ui::controls::checkbox>("m_altonly");
m_show_gold = get_control<ui::controls::checkbox>("m_show_gold");
m_show_runes = get_control<ui::controls::checkbox>("m_show_runes");
m_show_gems = get_control<ui::controls::checkbox>("m_show_gems");
if (m_altonly) {
m_altonly->set_state(loot_filter_settings::get().alt_only);
m_altonly->set_on_click(std::bind(&loot_filter_settings_menu::update_alt_only,
this, std::placeholders::_1));
}
if (m_show_gold) {
m_show_gold->set_state(loot_filter_settings::get().show_gold);
m_show_gold->set_on_click(std::bind(&loot_filter_settings_menu::update_show_gold,
this, std::placeholders::_1));
}
if (m_show_runes) {
m_show_runes->set_state(loot_filter_settings::get().show_runes);
m_show_runes->set_on_click(std::bind(&loot_filter_settings_menu::update_show_runes,
this, std::placeholders::_1));
}
if (m_show_gems) {
m_show_gems->set_state(loot_filter_settings::get().show_gems);
m_show_gems->set_on_click(std::bind(&loot_filter_settings_menu::update_show_gems,
this, std::placeholders::_1));
}
}
void d2_tweaks::client::modules::loot_filter_settings_menu::register_quality_checkboxes() {
using namespace diablo2::structures;
register_quality_checkbox("m_inferior", item_quality_t::ITEM_QUALITY_INFERIOR);
register_quality_checkbox("m_normal", item_quality_t::ITEM_QUALITY_NORMAL);
register_quality_checkbox("m_superior", item_quality_t::ITEM_QUALITY_SUPERIOR);
register_quality_checkbox("m_magic", item_quality_t::ITEM_QUALITY_MAGIC);
register_quality_checkbox("m_set", item_quality_t::ITEM_QUALITY_SET);
register_quality_checkbox("m_rare", item_quality_t::ITEM_QUALITY_RARE);
register_quality_checkbox("m_unique", item_quality_t::ITEM_QUALITY_UNIQUE);
register_quality_checkbox("m_crafted", item_quality_t::ITEM_QUALITY_CRAFTED);
register_quality_checkbox("m_tempered", item_quality_t::ITEM_QUALITY_TEMPERED);
}
void d2_tweaks::client::modules::loot_filter_settings_menu::update_alt_only(bool value) {
loot_filter_settings::get().alt_only = value;
loot_filter_settings::get().save(diablo2::d2_client::get_local_player_name());
}
void d2_tweaks::client::modules::loot_filter_settings_menu::update_show_gold(bool value) {
loot_filter_settings::get().show_gold = value;
loot_filter_settings::get().save(diablo2::d2_client::get_local_player_name());
}
void d2_tweaks::client::modules::loot_filter_settings_menu::update_show_runes(bool value) {
loot_filter_settings::get().show_runes = value;
loot_filter_settings::get().save(diablo2::d2_client::get_local_player_name());
}
void d2_tweaks::client::modules::loot_filter_settings_menu::update_show_gems(bool value) {
loot_filter_settings::get().show_gems = value;
loot_filter_settings::get().save(diablo2::d2_client::get_local_player_name());
}
void d2_tweaks::client::modules::loot_filter_settings_menu::update_quality_allowance(bool value,
diablo2::structures::item_quality_t quality) {
loot_filter_settings::get().quality_settings[static_cast<uint32_t>(quality)] = value;
loot_filter_settings::get().save(diablo2::d2_client::get_local_player_name());
}
void d2_tweaks::client::modules::loot_filter_settings_menu::register_quality_checkbox(const std::string& name,
diablo2::structures::item_quality_t quality) {
auto control = get_control<ui::controls::checkbox>(name);
if (!control)
return;
control->set_state(loot_filter_settings::get().quality_settings[static_cast<size_t>(quality)]);
control->set_on_click(std::bind(&loot_filter_settings_menu::update_quality_allowance,
this, std::placeholders::_1, quality));
}
void d2_tweaks::client::modules::loot_filter_settings_menu::setup_hooks() {
m_handle_dropped_items_original =
static_cast<decltype(m_handle_dropped_items_original)>(hooking::get_call(
diablo2::d2_client::get_base() + 0x1641B));
m_draw_dropped_items_names_original =
static_cast<decltype(m_draw_dropped_items_names_original)>(hooking::get_call(
diablo2::d2_client::get_base() + 0x81BF5));
hooking::set_call(diablo2::d2_client::get_base() + 0x81BF5, draw_dropped_items_names);
hooking::set_call(diablo2::d2_client::get_base() + 0x1641B, handle_dropped_items);
setup_alt_hook();
}
void d2_tweaks::client::modules::loot_filter_settings_menu::setup_alt_hook() const {
asm_code asmCode;
asmCode.add({ 0x83, 0x7D, 0x00, 0x04 }); //cmp dword ptr [ebp+0], 4
asmCode.add({ 0x0F, 0x85 },
new asm_address_relative(2, 6, diablo2::d2_client::get_base() + 0x6A399)); //jnz D2Client.dll+6A399
asmCode.add({ 0x56 }); //push esi
asmCode.add({ 0xB8 }, new asm_address_static(1, check_alt_item)); //mov eax, check_alt_item
asmCode.add({ 0x8B, 0xCD }); //mov ecx, ebp
asmCode.add({ 0xFF, 0xD0 }); //call eax
asmCode.add({ 0x5E }); //pop esi
asmCode.add({ 0x84, 0xC0 }); //test al, al
asmCode.add({ 0x0F, 0x84 },
new asm_address_relative(2, 6, diablo2::d2_client::get_base() + 0x6A399)); //je D2Client.dll+6A399
asmCode.add({ 0xE9 },
new asm_address_relative(1, 5, diablo2::d2_client::get_base() + 0x6A027)); //jmp D2Client.dll+6A027
auto addr = diablo2::d2_client::get_base() + 0x6A022;
DWORD old_protect;
VirtualProtect(addr, 5, PAGE_EXECUTE_READWRITE, &old_protect);
memset(addr, 0x90, 10);
*reinterpret_cast<unsigned char*>(addr) = 0xE9;
*reinterpret_cast<int32_t*>(static_cast<char*>(addr) + 1) =
reinterpret_cast<int32_t>(asmCode.get_code()) - reinterpret_cast<int32_t>(addr) - 5;
VirtualProtect(addr, 5, old_protect, &old_protect);
}
bool d2_tweaks::client::modules::loot_filter_settings_menu::is_gold(diablo2::structures::unit* item) {
static auto goldRecordIndex = -1;
if (item == nullptr)
return false;
if (item->type != diablo2::structures::unit_type_t::UNIT_TYPE_ITEM)
return false;
if (item->item_data == nullptr)
return false;
if (goldRecordIndex == -1) {
auto record = diablo2::d2_common::get_item_record(item->data_record_index);
if (record->string_code[0] == 'g' &&
record->string_code[1] == 'l' &&
record->string_code[2] == 'd') { //Gold pile
goldRecordIndex = item->data_record_index;
}
}
return item->data_record_index == goldRecordIndex;
}
bool d2_tweaks::client::modules::loot_filter_settings_menu::is_rune(diablo2::structures::unit* item) {
static auto runeTypeId = -1;
if (item == nullptr)
return false;
if (item->type != diablo2::structures::unit_type_t::UNIT_TYPE_ITEM)
return false;
if (item->item_data == nullptr)
return false;
const auto record = diablo2::d2_common::get_item_record(item->data_record_index);
if (runeTypeId == -1) {
const auto itemTypeRecord = diablo2::d2_common::get_item_type_record(record->type);
const auto code = itemTypeRecord->code;
if (code[0] == 'r' &&
code[1] == 'u' &&
code[2] == 'n' &&
code[3] == 'e') {
runeTypeId = record->type;
}
}
return record->type == runeTypeId;
}
bool d2_tweaks::client::modules::loot_filter_settings_menu::is_gem(diablo2::structures::unit* item) {
static auto gemTypeId = -1;
if (item == nullptr)
return false;
if (item->type != diablo2::structures::unit_type_t::UNIT_TYPE_ITEM)
return false;
if (item->item_data == nullptr)
return false;
const auto record = diablo2::d2_common::get_item_record(item->data_record_index);
if (gemTypeId == -1) {
const auto itemTypeRecord = diablo2::d2_common::get_item_type_record(record->type);
const auto code = itemTypeRecord->code;
if (code[0] == 'g' &&
code[1] == 'e' &&
code[2] == 'm') {
gemTypeId = record->type;
}
}
return record->type == gemTypeId;
}
bool d2_tweaks::client::modules::loot_filter_settings_menu::check_alt_item(diablo2::structures::unit* unit) {
static auto& instance = singleton<loot_filter_settings_menu>::instance();
if (!unit || unit->type != diablo2::structures::unit_type_t::UNIT_TYPE_ITEM) {
return true;
}
if (unit->item_data == nullptr) {
return true;
}
if (is_gold(unit))
return instance.m_show_gold && instance.m_show_gold->get_state();
if (is_rune(unit))
return instance.m_show_runes && instance.m_show_runes->get_state();
if (is_gem(unit))
return instance.m_show_gems && instance.m_show_gems->get_state();
return loot_filter_settings::get().quality_settings[static_cast<int32_t>(unit->item_data->quality)];
}
void d2_tweaks::client::modules::loot_filter_settings_menu::draw_dropped_items_names(diablo2::structures::unit* unit,
void* edx) {
static auto& instance = singleton<loot_filter_settings_menu>::instance();
if (!unit || unit->type != diablo2::structures::unit_type_t::UNIT_TYPE_ITEM) {
instance.m_draw_dropped_items_names_original(unit, edx);
return;
}
if (unit->item_data == nullptr) {
instance.m_draw_dropped_items_names_original(unit, edx);
return;
}
if (instance.m_altonly && instance.m_altonly->get_state())
return;
if (is_gold(unit) && instance.m_show_gold && !instance.m_show_gold->get_state())
return;
if (is_rune(unit) && instance.m_show_runes && !instance.m_show_runes->get_state())
return;
if (is_gem(unit) && instance.m_show_gems && !instance.m_show_gems->get_state())
return;
if (!loot_filter_settings::get().quality_settings[static_cast<int32_t>(unit->item_data->quality)])
return;
instance.m_draw_dropped_items_names_original(unit, edx);
}
void d2_tweaks::client::modules::loot_filter_settings_menu::handle_dropped_items(diablo2::structures::unit* unit,
void* edx) {
static auto& instance = singleton<loot_filter_settings_menu>::instance();
if (!unit || unit->type != diablo2::structures::unit_type_t::UNIT_TYPE_ITEM) {
instance.m_handle_dropped_items_original(unit, edx);
return;
}
if (unit->item_data == nullptr) {
instance.m_handle_dropped_items_original(unit, edx);
return;
}
if (instance.m_altonly && instance.m_altonly->get_state())
return;
if (is_gold(unit) && instance.m_show_gold && !instance.m_show_gold->get_state())
return;
if (is_rune(unit) && instance.m_show_runes && !instance.m_show_runes->get_state())
return;
if (is_gem(unit) && instance.m_show_gems && !instance.m_show_gems->get_state())
return;
if (!loot_filter_settings::get().quality_settings[static_cast<int32_t>(unit->item_data->quality)])
return;
instance.m_handle_dropped_items_original(unit, edx);
}

View File

@@ -0,0 +1,60 @@
#include <d2tweaks/client/modules/loot_filter/loot_filter_settings_toggle_menu.h>
#include <functional>
#include <d2tweaks/ui/controls/button.h>
#include <d2tweaks/client/client.h>
#include <diablo2/d2client.h>
#include <d2tweaks/ui/ui_manager.h>
#include <DllNotify.h>
#include <D2Template.h>
d2_tweaks::client::modules::loot_filter_settings_toggle_menu::loot_filter_settings_toggle_menu(token) {
m_show = false;
menu::set_enabled(true);
menu::set_visible(true);
//load_xml("d2tweaks\\interfaces\\loot_filter_settings_toggle_menu.xml");
if (DLLBASE_D2EXPRES != 0)
load_xml("d2tweaks\\interface_d2expres\\loot_filter_settings_toggle_menu.xml");
if (DLLBASE_SGD2FREERES != 0)
load_xml("d2tweaks\\interface_sgd2freeres\\loot_filter_settings_toggle_menu.xml");
if (DLLBASE_SGD2FREERES == 0 && DLLBASE_D2EXPRES == 0)
load_xml("d2tweaks\\interface_vanilla\\loot_filter_settings_toggle_menu.xml");
m_toggle_filter_settings_btn = static_cast<ui::controls::button*>(
get_control("m_toggle_filter_settings_btn"));
m_toggle_filter_settings_btn->set_enabled(false);
m_toggle_filter_settings_btn->set_visible(false);
m_toggle_filter_settings_btn->set_on_click(std::bind(&loot_filter_settings_toggle_menu::toggle_filter_settings_click, this));
m_filter_settings_menu = singleton<ui::ui_manager>::instance().get_menu("loot_filter_settings_menu");
}
void d2_tweaks::client::modules::loot_filter_settings_toggle_menu::toggle_filter_settings_click() {
m_show = !m_show;
m_filter_settings_menu->set_enabled(m_show);
m_filter_settings_menu->set_visible(m_show);
}
void d2_tweaks::client::modules::loot_filter_settings_toggle_menu::draw() {
m_toggle_filter_settings_btn->set_enabled(diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_INTERFACE));
m_toggle_filter_settings_btn->set_visible(diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_INTERFACE));
menu::draw();
}
bool d2_tweaks::client::modules::loot_filter_settings_toggle_menu::key_event(uint32_t key, bool up) {
if (key == VK_ESCAPE && m_show) {
m_show = false;
m_filter_settings_menu->set_enabled(m_show);
m_filter_settings_menu->set_visible(m_show);
return true; //block escape key stroke
}
return menu::key_event(key, up);
}

View File

@@ -0,0 +1,42 @@
#include <d2tweaks/client/modules/small_patches/small_patches.h>
#include <Windows.h>
#include <diablo2/d2client.h>
#include <diablo2/d2win.h>
#include <common/hooking.h>
#include <diablo2/d2gfx.h>
MODULE_INIT(small_patches)
static int(__cdecl* g_is_iconic_original)();
static int __cdecl is_iconic() {
return 0;
}
void d2_tweaks::client::modules::small_patches::init_early() {
}
void d2_tweaks::client::modules::small_patches::init() {
////Ingame FPS unlock
//DWORD oldProtect;
//char PathToIni[MAX_PATH] = { 0 };
//const char IniFile[] = "\\d2tweaks.ini";
//GetCurrentDirectory(MAX_PATH, PathToIni);
//lstrcat(PathToIni, IniFile);
//if (GetPrivateProfileInt("modules", "SmallPatches", 1, PathToIni) != FALSE) {
// if (VirtualProtect(diablo2::d2_client::get_base() + 0xA2C9, 0x04, PAGE_EXECUTE_READWRITE, &oldProtect))
// *reinterpret_cast<uint32_t*>(diablo2::d2_client::get_base() + 0xA2C9) = 0x90909090;
// //Main menu FPS unlock
// if (VirtualProtect(diablo2::d2_win::get_base() + 0xD029, 0x03, PAGE_EXECUTE_READWRITE, &oldProtect))
// memset(diablo2::d2_win::get_base() + 0xD029, 0x90, 0x03);
// return;
// //Window auto-hiding on focus loss
// hooking::hook<10026>(diablo2::d2_gfx::get_base(), is_iconic, &g_is_iconic_original);
//}
}

View File

@@ -0,0 +1,74 @@
#include <d2tweaks/client/modules/test/test.h>
#include <spdlog/spdlog.h>
#include <common/ptr_wrapper.h>
#include <d2tweaks/ui/menu.h>
#include <d2tweaks/ui/ui_manager.h>
#include <diablo2/structures/data_tables.h>
#include <diablo2/structures/unit.h>
#include <diablo2/d2common.h>
#include <diablo2/d2client.h>
#include <diablo2/d2win.h>
#include <diablo2/structures/data/bodylocs_line.h>
#include <diablo2/structures/data/elemtypes_line.h>
#include <diablo2/structures/data/hitclass_line.h>
#include <diablo2/structures/data/item_types_line.h>
#include <diablo2/structures/data/items_line.h>
#include <diablo2/structures/data/monmode_line.h>
#include <diablo2/structures/data/playerclass_line.h>
#include <diablo2/structures/data/storepage_line.h>
MODULE_INIT(test)
class test_menu : public d2_tweaks::ui::menu {
public:
test_menu() {
menu::set_enabled(true);
menu::set_visible(true);
}
void draw() override {
using namespace diablo2::structures;
static wrap_value<data_tables*> dataTables(0x96A20, diablo2::d2_common::get_base());
const auto player = diablo2::d2_client::get_local_player();
if (!player)
return;
auto v = *dataTables;
draw_cursor_pos();
menu::draw();
}
private:
static void draw_cursor_pos() {
static wchar_t buffer[512]{ 0 };
const auto mx = diablo2::d2_client::get_mouse_x();
const auto my = diablo2::d2_client::get_mouse_y();
swprintf_s(buffer, L"%i, %i", mx, my);
diablo2::d2_win::draw_text(buffer, mx, my, diablo2::UI_COLOR_WHITE, 0);
}
};
static test_menu* g_test_menu;
void d2_tweaks::client::modules::test::init_early() {
}
void d2_tweaks::client::modules::test::init() {
#ifndef NDEBUG
g_test_menu = new test_menu();
singleton<ui::ui_manager>::instance().add_menu(g_test_menu);
#endif
}
void d2_tweaks::client::modules::test::handle_packet(common::packet_header* packet) {}

View File

@@ -0,0 +1,204 @@
#include <d2tweaks/client/modules/trader_update/trader_update_client.h>
#include <d2tweaks/client/client.h>
#include <common/hooking.h>
#include <d2tweaks/common/common.h>
#include <d2tweaks/common/protocol.h>
#include <d2tweaks/common/asset_manager.h>
#include <d2tweaks/ui/menu.h>
#include <d2tweaks/ui/ui_manager.h>
#include <d2tweaks/ui/controls/control.h>
#include <d2tweaks/ui/controls/button.h>
#include <spdlog/spdlog.h>
#include <diablo2/d2common.h>
#include <diablo2/d2lang.h>
#include <diablo2/d2client.h>
#include <diablo2/d2win.h>
#include <diablo2/d2gfx.h>
#include <diablo2/d2common.h>
//#include <diablo2/structures/data_tables.h>
#include <diablo2/structures/unit.h>
#include <diablo2/structures/item_data.h>
#include <diablo2/structures/data/items_line.h>
#include <diablo2/structures/data/item_types_line.h>
#include <diablo2/structures/inventory.h>
#include <diablo2/structures/npc_record.h>
#include <common/autopickup_lootfilter.h>
#include <common/ini.h>
#include <DllNotify.h>
#include <D2Template.h>
MODULE_INIT(trader_update)
static uint8_t m_nMenuOpen;
static uint8_t m_nNpcId;
enum trader_command {
COMMAND_NULL,
COMMAND_FREE_NPC_INVENTORY,
COMMAND_FREE_NPC_GAMBLE,
COMMAND_FILL_NPC_INVENTORY,
COMMAND_FILL_NPC_GAMBLE,
COMMAND_FINISHED,
COMMAND_ABORT
};
class trader_update_menu : public d2_tweaks::ui::menu {
d2_tweaks::common::asset* m_buttons_img;
d2_tweaks::ui::controls::button* m_trader_update_btn;
d2_tweaks::ui::controls::button* m_gamble_update_btn;
public:
trader_update_menu() {
menu::set_enabled(true);
menu::set_visible(true);
//load_xml("d2tweaks\\interface_d2expres\\reloaditems.xml");
if (DLLBASE_D2EXPRES != 0)
load_xml("d2tweaks\\interface_d2expres\\reloaditems.xml");
if (DLLBASE_SGD2FREERES != 0)
load_xml("d2tweaks\\interface_sgd2freeres\\reloaditems.xml");
if (DLLBASE_SGD2FREERES == 0 && DLLBASE_D2EXPRES == 0)
load_xml("d2tweaks\\interface_vanilla\\reloaditems.xml");
m_buttons_img = singleton<d2_tweaks::common::asset_manager>::instance().get_mpq_file("d2tweaks\\assets\\buttons", d2_tweaks::common::MPQ_FILE_TYPE_DC6);
m_trader_update_btn = get_button("m_reload_items_btn", std::bind(&trader_update_menu::trader_update_click, this));
}
void draw() override {
if (!should_draw()) {
m_trader_update_btn->set_enabled(false);
m_trader_update_btn->set_visible(false);
return;
}
m_trader_update_btn->set_enabled(true);
m_trader_update_btn->set_visible(true);
menu::draw();
}
private:
static bool should_draw() {
return diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_NPCSHOP);
}
d2_tweaks::ui::controls::button* get_button(const std::string& name, const std::function<void()>& onClick) {
auto result = static_cast<d2_tweaks::ui::controls::button*>(get_control(name));
result->set_on_click(onClick);
result->set_enabled(false);
result->set_visible(false);
return result;
}
void trader_update_click() {
const auto unit = diablo2::d2_client::get_local_player();
static d2_tweaks::common::trader_update_cs request_packet_cs;
request_packet_cs.client_id = unit->guid;
request_packet_cs.npc_id = diablo2::d2_client::current_vendor_guid();
request_packet_cs.is_gamble_menu_open = diablo2::d2_client::is_gamble_open();
if (request_packet_cs.is_gamble_menu_open == false) {
request_packet_cs.command = COMMAND_FREE_NPC_INVENTORY;
diablo2::d2_client::send_to_server(&request_packet_cs, sizeof request_packet_cs);
}
if (request_packet_cs.is_gamble_menu_open == true) {
request_packet_cs.command = COMMAND_FREE_NPC_GAMBLE;
diablo2::d2_client::send_to_server(&request_packet_cs, sizeof request_packet_cs);
}
}
};
void d2_tweaks::client::modules::trader_update::init_early() {
}
void d2_tweaks::client::modules::trader_update::init() {
char szDir[MAX_PATH];
char szPath[MAX_PATH];
const char szConfig[] = "d2tweaks.ini";
GetCurrentDirectory(MAX_PATH, szDir);
snprintf(szPath, MAX_PATH, "%s\\%s", szDir, szConfig);
CIni config(szPath);
if (config.GetInt("modules", "ReloadTradeGamble", 1) != FALSE) {
singleton<ui::ui_manager>::instance().add_menu(new trader_update_menu());
singleton<client>::instance().register_packet_handler(common::message_types_t::MESSAGE_TYPE_TRADER_UPDATE, this);
//singleton<client>::instance().register_tick_handler(this);
//singleton<client>::instance().register_packet_cs_handler(common::packet_types_cs_t::PACKET_0x38, common::message_types_t::MESSAGE_TYPE_TRADER_UPDATE, this);
}
}
void d2_tweaks::client::modules::trader_update::tick() {
// const auto unit = diablo2::d2_client::get_local_player();
}
void d2_tweaks::client::modules::trader_update::handle_cs_packet(common::packet_header* packet) {
static auto& instance = singleton<client>::instance();
const auto income_packet_cs = static_cast<common::d2_entity_action_cs*>(packet);
#ifndef NDEBUG
spdlog::debug("[D2CLIENT C > S] ENTITY ACTION! message {} action {} entity_id {} complement {}", (uint8_t) income_packet_cs->message_type, (void*)(income_packet_cs->action >> 24), (void*)(income_packet_cs->entity_id >> 24), (void*)income_packet_cs->complement);
#endif
m_nMenuOpen = (uint8_t)income_packet_cs->message_type; // 1 = trade, 2 = gamble
m_nNpcId = (income_packet_cs->action >> 24);
}
void d2_tweaks::client::modules::trader_update::handle_packet(common::packet_header* packet) {
static auto& instance = singleton<client>::instance();
const auto income_packet_sc = static_cast<common::trader_update_sc*>(packet);
if (!diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_NPCSHOP))
return;
m_nNpcId = diablo2::d2_client::current_vendor_guid();
diablo2::structures::unit* ptNPC = diablo2::d2_client::get_unit_by_guid(m_nNpcId, (int32_t)diablo2::structures::unit_type_t::UNIT_TYPE_MONSTER);
if (ptNPC == 0)
return;
if (income_packet_sc->command == COMMAND_FREE_NPC_INVENTORY) {
// <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
if (income_packet_sc->npc_id == m_nNpcId && income_packet_sc->is_gamble_menu_open == diablo2::d2_client::is_gamble_open()) {
diablo2::d2_client::resync_vendor_inventory(ptNPC);
//diablo2::d2_common::empty_inventory_2(ptNPC->inventory);
static d2_tweaks::common::trader_update_cs request_packet_cs;
request_packet_cs.command = COMMAND_FILL_NPC_INVENTORY;
request_packet_cs.npc_id = m_nNpcId;
request_packet_cs.is_gamble_menu_open = diablo2::d2_client::is_gamble_open();
request_packet_cs.client_id = income_packet_sc->client_id;
diablo2::d2_client::send_to_server(&request_packet_cs, sizeof request_packet_cs);
}
}
if (income_packet_sc->command == COMMAND_FREE_NPC_GAMBLE) {
// <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
if (income_packet_sc->npc_id == m_nNpcId && income_packet_sc->is_gamble_menu_open == diablo2::d2_client::is_gamble_open()) {
diablo2::d2_client::resync_vendor_inventory(ptNPC);
//diablo2::d2_common::empty_inventory_2(ptNPC->inventory);
static d2_tweaks::common::trader_update_cs request_packet_cs;
request_packet_cs.command = COMMAND_FILL_NPC_GAMBLE;
request_packet_cs.npc_id = m_nNpcId;
request_packet_cs.is_gamble_menu_open = diablo2::d2_client::is_gamble_open();
request_packet_cs.client_id = income_packet_sc->client_id;
diablo2::d2_client::send_to_server(&request_packet_cs, sizeof request_packet_cs);
}
}
}

View File

@@ -0,0 +1,565 @@
#include <d2tweaks/client/modules/transmute/transmute_client.h>
#include <d2tweaks/client/client.h>
#include <common/hooking.h>
#include <d2tweaks/common/common.h>
#include <d2tweaks/common/protocol.h>
#include <d2tweaks/common/asset_manager.h>
#include <d2tweaks/ui/menu.h>
#include <d2tweaks/ui/ui_manager.h>
#include <d2tweaks/ui/controls/control.h>
#include <d2tweaks/ui/controls/button.h>
#include <spdlog/spdlog.h>
#include <diablo2/d2common.h>
#include <diablo2/d2lang.h>
#include <diablo2/d2client.h>
#include <diablo2/d2win.h>
#include <diablo2/d2gfx.h>
#include <diablo2/d2common.h>
//#include <diablo2/structures/data_tables.h>
#include <diablo2/structures/unit.h>
#include <diablo2/structures/item_data.h>
#include <diablo2/structures/data/items_line.h>
#include <diablo2/structures/data/item_types_line.h>
#include <diablo2/structures/inventory.h>
#include <common/autopickup_lootfilter.h>
#include <common/ini.h>
#include <DllNotify.h>
#include <D2Template.h>
MODULE_INIT(transmute)
static uint32_t m_nTransmuteSound = 0;
static bool m_bToggleTransmute = 0;
static uint32_t m_nDelayFrames = 10;
static uint32_t m_nCountFrames = 0;
static bool m_game_init_done = false;
static const uint8_t PAGE_CUBE = 3;
static const uint8_t PAGE_STASH = 4;
static const uint8_t PAGE_INVENTORY = 0;
static const uint32_t m_nCountKeys = 10;
static uint32_t m_nCountItemListAll = 0;
static char m_aacItemList[m_nCountKeys][MAX_STRING_LENGHT] = { 0 };
static char m_acItemListAll[MAX_STRING_LENGHT * m_nCountKeys] = { 0 };
static char m_acItemListAllTemp[MAX_STRING_LENGHT * m_nCountKeys] = { 0 };
static uint32_t m_nCountItemTypesAll = 0;
static char m_aacItemTypes[m_nCountKeys][MAX_STRING_LENGHT] = { 0 };
static char m_acItemTypesAll[MAX_STRING_LENGHT * m_nCountKeys] = { 0 };
static char m_acItemTypesAllTemp[MAX_STRING_LENGHT * m_nCountKeys] = { 0 };
static char m_acBuffer[1024] = { 0 };
static item_code* m_stItemList;
static item_type* m_stItemTypes;
static void(__fastcall* fn_hook_play_sound)(uint32_t soundId, uint32_t* unit, const uint32_t ticks, const BOOL prePick, const uint32_t cache);
static void(__fastcall* fn_hook_game_end)();
enum transmute_command {
COMMAND_NULL,
COMMAND_TRANSMUTE_START,
COMMAND_TRANSMUTE_END,
COMMAND_CALL_TRANSMUTE,
COMMAND_MOVE_ITEM,
COMMAND_ABORT
};
class auto_transmute_menu : public d2_tweaks::ui::menu {
d2_tweaks::common::asset* m_buttons_img;
d2_tweaks::ui::controls::button* m_auto_transmute_btn;
public:
auto_transmute_menu() {
menu::set_enabled(true);
menu::set_visible(true);
//load_xml("d2tweaks\\interfaces\\autotransmute.xml");
if (DLLBASE_D2EXPRES != 0)
load_xml("d2tweaks\\interface_d2expres\\autotransmute.xml");
if (DLLBASE_SGD2FREERES != 0)
load_xml("d2tweaks\\interface_sgd2freeres\\autotransmute.xml");
if (DLLBASE_SGD2FREERES == 0 && DLLBASE_D2EXPRES == 0)
load_xml("d2tweaks\\interface_vanilla\\autotransmute.xml");
m_buttons_img = singleton<d2_tweaks::common::asset_manager>::instance().get_mpq_file("d2tweaks\\assets\\buttons", d2_tweaks::common::MPQ_FILE_TYPE_DC6);
m_auto_transmute_btn = get_button("m_auto_transmute_btn", std::bind(&auto_transmute_menu::auto_transmute_click, this));
}
void draw() override {
if (m_bToggleTransmute) {
//diablo2::d2_client::print_chat(L"AUTO TRANSMUTE ON", 1);
m_auto_transmute_btn->set_current_frame(5); // index of frame in buttons.dc6
}
else
{
//diablo2::d2_client::print_chat(L"AUTO TRANSMUTE OFF", 2);
m_auto_transmute_btn->set_current_frame(4);
}
if (!should_draw()) {
m_auto_transmute_btn->set_enabled(false);
m_auto_transmute_btn->set_visible(false);
return;
}
m_auto_transmute_btn->set_enabled(true);
m_auto_transmute_btn->set_visible(true);
menu::draw();
}
private:
static bool should_draw() {
return diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_INVENTORY) ||
diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_STASH) ||
diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_CUBE) ||
diablo2::d2_client::get_ui_window_state(diablo2::UI_WINDOW_NPCSHOP);
}
d2_tweaks::ui::controls::button* get_button(const std::string& name, const std::function<void()>& onClick) {
auto result = static_cast<d2_tweaks::ui::controls::button*>(get_control(name));
result->set_on_click(onClick);
result->set_enabled(false);
result->set_visible(false);
return result;
}
void auto_transmute_click() {
send_request(0x00);
}
void send_request(const uint8_t page) {
static d2_tweaks::common::transmute_info_cs request_packet_cs;
m_bToggleTransmute ^= true;
//request_packet_cs.transmute_start_flag = m_bToggleTransmute;
//request_packet_cs.command = COMMAND_TRANSMUTE_START;
//diablo2::d2_client::send_to_server(&request_packet_cs, sizeof request_packet_cs);
if (m_bToggleTransmute) {
diablo2::d2_client::print_chat(L"AUTO TRANSMUTE ON", 1);
}
else
{
diablo2::d2_client::print_chat(L"AUTO TRANSMUTE OFF", 2);
}
}
};
void __fastcall hook_play_sound(uint32_t soundId, uint32_t* unit, const uint32_t ticks, const BOOL prePick, const uint32_t cache) {
if (m_bToggleTransmute) {
if (soundId == 0xB)
soundId = 0;
}
fn_hook_play_sound(soundId, unit, ticks, prePick, cache);
return;
}
void __stdcall hook_game_end () {
m_game_init_done = false;
m_bToggleTransmute = false;
return;
}
__declspec (naked) void hook_game_end_asm() {
__asm {
pushad;
pushfd;
call hook_game_end;
popfd;
popad;
jmp [fn_hook_game_end];
}
}
void d2_tweaks::client::modules::transmute::init_early() {
}
void d2_tweaks::client::modules::transmute::init() {
char szDir[MAX_PATH];
char szPath[MAX_PATH];
uint32_t dwLenght = 0;
const char szConfig[] = "d2tweaks.ini";
GetCurrentDirectory(MAX_PATH, szDir);
sprintf_s(szPath, MAX_PATH, "%s\\%s", szDir, szConfig);
CIni config(szPath);
//const char szRecipes[] = "d2tweaks.recipes.ini";
//snprintf(szPath, MAX_PATH, "%s\\%s", szDir, szRecipes);
//CIni recipes(szPath);
// key enlarging the buffer unless we make sure all section names
// are loaded.
//DWORD dwBufSize = 1024;
//char* pszBuffer = new char[dwBufSize];
//DWORD dwCopied = recipes.GetSectionNames(pszBuffer, dwBufSize - 1);
//while (dwCopied + 128 >= dwBufSize)
//{
// dwBufSize += 1024;
// delete[] pszBuffer;
// pszBuffer = new char[dwBufSize];
// dwCopied = recipes.GetSectionNames(pszBuffer, dwBufSize - 1);
//}
//for (char* pSection = pszBuffer; pSection[0]; pSection = &pSection[strlen(pSection) + 1])
//{
//}
if (config.GetInt("modules", "AutoTransmute", 1) != FALSE) {
m_nDelayFrames = config.GetUInt("AutoTransmute", "DelayInFrames", 10);
m_nTransmuteSound = config.GetUInt("AutoTransmute", "EnableTransmuteSound", 0);
for (uint32_t i = 0; i < m_nCountKeys; i++) {
sprintf_s(m_acBuffer, sizeof(m_acBuffer), "ItemList%d", i + 1);
dwLenght = config.GetString("AutoTransmute", m_acBuffer, m_aacItemList[i], MAX_STRING_LENGHT - 1);
if (dwLenght != 0) {
lstrcat(m_acItemListAll, m_aacItemList[i]);
lstrcat(m_acItemListAll, "|");
}
}
for (uint32_t i = 0; i < m_nCountKeys; i++) {
sprintf_s(m_acBuffer, sizeof(m_acBuffer), "ItemTypeList%d", i + 1);
dwLenght = config.GetString("AutoTransmute", m_acBuffer, m_aacItemTypes[i], MAX_STRING_LENGHT - 1);
if (dwLenght != 0) {
lstrcat(m_acItemTypesAll, m_aacItemTypes[i]);
lstrcat(m_acItemTypesAll, "|");
}
}
/////// Parse ItemCode
dwLenght = lstrlen(m_acItemListAll);
memcpy(m_acItemListAllTemp, m_acItemListAll, dwLenght + 1);
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
char* token_string_itemcode = strtok(m_acItemListAllTemp, " ,|");
while (token_string_itemcode)
{
m_nCountItemListAll++;
token_string_itemcode = strtok(NULL, " ,|");
}
m_stItemList = (item_code*)malloc(m_nCountItemListAll * sizeof(item_code));
memset(m_stItemList, 0, m_nCountItemListAll * sizeof(item_code));
memcpy(m_acItemListAllTemp, m_acItemListAll, dwLenght + 1);
token_string_itemcode = strtok(m_acItemListAllTemp, " ,|");
for (uint32_t i = 0; token_string_itemcode != 0; i++)
{
uint32_t cur_string_length = lstrlen(token_string_itemcode);
m_stItemList[i].code0 = token_string_itemcode[0];
m_stItemList[i].code1 = token_string_itemcode[1];
m_stItemList[i].code2 = token_string_itemcode[2];
if (token_string_itemcode[3] == ':') {
// first index quality = 1
for (uint32_t q = 1; q <= 9; q++) {
m_stItemList[i].qualityinclude[q] = FALSE;
}
if (cur_string_length <= 13) { //for example jew:123456789
// p = 4 is first digit after ':'
for (uint32_t p = 4; p <= cur_string_length; p++) {
if (token_string_itemcode[p] == 0) {
break;
}
if (token_string_itemcode[p] >= '0' && token_string_itemcode[p] <= '9') {
m_stItemList[i].qualityinclude[token_string_itemcode[p] - 0x30] = TRUE;
}
}
}
}
if (token_string_itemcode[3] == '-') {
// first index quality = 1
for (uint32_t q = 1; q <= 9; q++) {
m_stItemList[i].qualityinclude[q] = TRUE;
}
if (cur_string_length <= 13) { //for example jew:123456789
// p = 4 is first digit after '-'
for (uint32_t p = 4; p <= cur_string_length; p++) {
if (token_string_itemcode[p] == 0) {
break;
}
if (token_string_itemcode[p] >= '0' && token_string_itemcode[p] <= '9') {
m_stItemList[i].qualityinclude[token_string_itemcode[p] - 0x30] = FALSE;
}
}
}
}
if (token_string_itemcode[3] != '-' && token_string_itemcode[3] != ':')
{
// first index quality = 1
for (uint32_t q = 1; q <= 9; q++) {
m_stItemList[i].qualityinclude[q] = TRUE;
}
}
token_string_itemcode = strtok(NULL, " ,|");
}
/////// Parse ItemType
dwLenght = lstrlen(m_acItemTypesAll);
memcpy(m_acItemTypesAllTemp, m_acItemTypesAll, dwLenght + 1);
char* token_string_itemtype_code = strtok(m_acItemTypesAllTemp, ",|");
while (token_string_itemtype_code)
{
m_nCountItemTypesAll++;
token_string_itemtype_code = strtok(NULL, ",|");
}
m_stItemTypes = (item_type*)malloc(m_nCountItemTypesAll * sizeof(item_type));
memset(m_stItemTypes, 0, m_nCountItemTypesAll * sizeof(item_type));
memcpy(m_acItemTypesAllTemp, m_acItemTypesAll, dwLenght + 1);
token_string_itemtype_code = strtok(m_acItemTypesAllTemp, ",|");
for (uint32_t i = 0; token_string_itemtype_code != 0; i++)
{
uint32_t cur_itemtypes_string_length = lstrlen(token_string_itemtype_code);
//m_stItemTypes[i].type0 = token_string_itemtype_code[0];
//m_stItemTypes[i].type1 = token_string_itemtype_code[1];
//m_stItemTypes[i].type2 = token_string_itemtype_code[2];
//m_stItemTypes[i].type3 = token_string_itemtype_code[3];
m_stItemTypes[i].dwtype = *(DWORD*)token_string_itemtype_code;
if (token_string_itemtype_code[4] == ':') {
// first index quality = 1
for (uint32_t q = 1; q <= 9; q++) {
m_stItemTypes[i].qualityinclude[q] = FALSE;
}
if (cur_itemtypes_string_length <= 14) { //for example tors:123456789
// p = 5 is first digit after ':'
for (uint32_t p = 5; p <= cur_itemtypes_string_length; p++) {
if (token_string_itemtype_code[p] == 0) {
break;
}
if (token_string_itemtype_code[p] >= '0' && token_string_itemtype_code[p] <= '9') {
m_stItemTypes[i].qualityinclude[token_string_itemtype_code[p] - 0x30] = TRUE;
}
}
}
}
if (token_string_itemtype_code[4] == '-') {
// first index quality = 1
for (uint32_t q = 1; q <= 9; q++) {
m_stItemTypes[i].qualityinclude[q] = TRUE;
}
if (cur_itemtypes_string_length <= 14) { //for example armo:123456789
// p = 5 is first digit after '-'
for (uint32_t p = 5; p <= cur_itemtypes_string_length; p++) {
if (token_string_itemtype_code[p] == 0) {
break;
}
if (token_string_itemtype_code[p] >= '0' && token_string_itemtype_code[p] <= '9') {
m_stItemTypes[i].qualityinclude[token_string_itemtype_code[p] - 0x30] = FALSE;
}
}
}
}
if (token_string_itemtype_code[4] != '-' && token_string_itemtype_code[4] != ':')
{
// first index quality = 1
for (uint32_t q = 1; q <= 9; q++) {
m_stItemTypes[i].qualityinclude[q] = TRUE;
}
}
token_string_itemtype_code = strtok(NULL, ",|");
}
if (m_nTransmuteSound == false) {
hooking::hook(diablo2::d2_client::get_base() + 0xB5820, hook_play_sound, reinterpret_cast<void**>(&fn_hook_play_sound));
}
hooking::hook(diablo2::d2_client::get_base() + 0xB528, hook_game_end_asm, reinterpret_cast<void**>(&fn_hook_game_end));
singleton<ui::ui_manager>::instance().add_menu(new auto_transmute_menu());
singleton<client>::instance().register_tick_handler(this);
singleton<client>::instance().register_packet_handler(common::message_types_t::MESSAGE_TYPE_TRANSMUTE, this);
}
}
void d2_tweaks::client::modules::transmute::tick() {
const auto unit = diablo2::d2_client::get_local_player();
if (m_game_init_done == false) {
m_game_init_done = true;
m_bToggleTransmute = false;
}
if (!unit)
return;
if (unit->type != diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER)
return;
if (!m_bToggleTransmute)
return;
if (m_nCountFrames > m_nDelayFrames)
{
m_nCountFrames = 0;
auto item = diablo2::d2_common::get_first_inventory_item(unit->inventory);
for (item; item != 0; item = diablo2::d2_common::get_next_inventory_item(item)) {
auto currentpage = diablo2::d2_common::get_item_page(item);
if (currentpage == PAGE_INVENTORY) {
const auto record = diablo2::d2_common::get_item_record(item->data_record_index);
if (*(DWORD*)record->string_code == *(DWORD*)"box ") {
continue;
}
const auto itemtype_record = diablo2::d2_common::get_item_type_record(record->type);
auto itemtype_record_equiv1 = diablo2::d2_common::get_item_type_record(itemtype_record->equiv1);
auto itemtype_record_equiv2 = diablo2::d2_common::get_item_type_record(itemtype_record->equiv2);
uint32_t quality = diablo2::d2_common::get_item_quality(item);
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
char arr_itemtype_codestr_equivstr[20][5] = { 0 };
// itemtype code <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
*(DWORD*)arr_itemtype_codestr_equivstr[0] = *(DWORD*)itemtype_record->code;
// index second code in array
uint32_t index_arr_itemtype = 1;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> equiv1
if (itemtype_record_equiv1) {
if (*(DWORD*)itemtype_record_equiv1->code != 0x20202020) {
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> equiv1
*(DWORD*)arr_itemtype_codestr_equivstr[index_arr_itemtype] = *(DWORD*)itemtype_record_equiv1->code;
index_arr_itemtype++;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> eqiv1 <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
for (index_arr_itemtype; itemtype_record_equiv1->equiv1 != 0; index_arr_itemtype++) {
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
itemtype_record_equiv1 = diablo2::d2_common::get_item_type_record(itemtype_record_equiv1->equiv1);
if (*(DWORD*)itemtype_record_equiv1->code != 0x20202020) {
*(DWORD*)arr_itemtype_codestr_equivstr[index_arr_itemtype] = *(DWORD*)itemtype_record_equiv1->code;
}
else break;
}
}
}
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> equiv2
if (itemtype_record_equiv2) {
if (*(DWORD*)itemtype_record_equiv2->code != 0x20202020) {
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> equiv2
*(DWORD*)arr_itemtype_codestr_equivstr[index_arr_itemtype] = *(DWORD*)itemtype_record_equiv2->code;
index_arr_itemtype++;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> eqiv2 <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
for (index_arr_itemtype; itemtype_record_equiv2->equiv2 != 0; index_arr_itemtype++) {
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
itemtype_record_equiv2 = diablo2::d2_common::get_item_type_record(itemtype_record_equiv2->equiv2);
if (*(DWORD*)itemtype_record_equiv2->code != 0x20202020) {
*(DWORD*)arr_itemtype_codestr_equivstr[index_arr_itemtype] = *(DWORD*)itemtype_record_equiv2->code;
}
else break;
}
}
}
for (uint32_t i = 0; i < m_nCountItemTypesAll; i++)
{
for (uint32_t count = 0; count < index_arr_itemtype; count++)
{
if (*(DWORD*)arr_itemtype_codestr_equivstr[count] == (DWORD)m_stItemTypes[i].dwtype)
{
if (m_stItemTypes[i].qualityinclude[quality] == TRUE)
{
static d2_tweaks::common::transmute_info_cs request_packet_cs;
request_packet_cs.command = COMMAND_MOVE_ITEM;
request_packet_cs.item_guid = item->guid;
request_packet_cs.target_page = PAGE_CUBE;
diablo2::d2_client::send_to_server(&request_packet_cs, sizeof request_packet_cs);
goto L1;
}
}
}
}
for (uint32_t i = 0; i < m_nCountItemListAll; i++)
{
if (record->string_code[0] == m_stItemList[i].code0 &&
record->string_code[1] == m_stItemList[i].code1 &&
record->string_code[2] == m_stItemList[i].code2)
{
if (m_stItemList[i].qualityinclude[quality] == TRUE)
{
static d2_tweaks::common::transmute_info_cs request_packet_cs;
request_packet_cs.command = COMMAND_MOVE_ITEM;
request_packet_cs.item_guid = item->guid;
request_packet_cs.target_page = PAGE_CUBE;
diablo2::d2_client::send_to_server(&request_packet_cs, sizeof request_packet_cs);
goto L1;
}
}
}
}
}
}
L1:;
m_nCountFrames++;
}
void d2_tweaks::client::modules::transmute::handle_packet(common::packet_header* packet) {
static auto& instance = singleton<client>::instance();
const auto income_packet_sc = static_cast<common::transmute_info_sc*>(packet);
static d2_tweaks::common::transmute_info_cs request_packet_cs;
if (income_packet_sc->command == COMMAND_MOVE_ITEM) {
const auto item = instance.get_client_unit(0x04, income_packet_sc->item_guid); //0x03 -> 0x04 - item
if (item == nullptr)
return;
const auto player = diablo2::d2_client::get_local_player();
//Last parameter is some boolean
const auto inventoryIndex = diablo2::d2_common::get_inventory_index(player, income_packet_sc->target_page, diablo2::d2_client::is_lod());
item->item_data->page = income_packet_sc->target_page;
diablo2::d2_common::inv_add_item(player->inventory, item, income_packet_sc->tx, income_packet_sc->ty, inventoryIndex, true, item->item_data->page);
diablo2::d2_common::inv_update_item(player->inventory, item, true);
// send 'transmute' command
request_packet_cs.command = COMMAND_CALL_TRANSMUTE;
diablo2::d2_client::send_to_server(&request_packet_cs, sizeof request_packet_cs);
//diablo2::d2_client::send_to_server_7(0x4F, 0x18, 0, 0);
}
if (income_packet_sc->command == COMMAND_ABORT) {
m_bToggleTransmute = false;
diablo2::d2_client::print_chat(L"WRONG RECIPE, AUTO TRANSMUTE OFF", 2);
}
}

View File

@@ -0,0 +1,79 @@
#include <d2tweaks/common/asset_manager.h>
#include <spdlog/spdlog.h>
#include <d2tweaks/common/asset.h>
#include <diablo2/d2client.h>
#include <diablo2/d2win.h>
#include <common/hooking.h>
static void* g_d2_tweaks_mpq = nullptr;
static int32_t(__stdcall* g_reload_original)();
d2_tweaks::common::asset_manager::asset_manager(token) {}
void d2_tweaks::common::asset_manager::init() {
hooking::hook(diablo2::d2_client::get_base() + 0x5E370, reload, reinterpret_cast<void**>(&g_reload_original));
g_d2_tweaks_mpq = diablo2::d2_win::load_mpq("d2tweaks.dll", "d2tweaks.mpq", "D2TWEAKS", 6000);
//if (g_d2_tweaks_mpq == nullptr) {
// spdlog::critical("Cannot find d2tweaks.mpq!");
// exit(-1);
//}
spdlog::debug("d2tweaks.mpq: {0}", g_d2_tweaks_mpq);
}
int32_t d2_tweaks::common::asset_manager::reload() {
static auto& instance = singleton<asset_manager>::instance();
const auto result = g_reload_original();
spdlog::info("Reloading image assets!");
for (const auto& it : instance.m_assets) {
if (it.second->get_type() != MPQ_FILE_TYPE_DC6 &&
it.second->get_type() != MPQ_FILE_TYPE_DCC)
continue;
spdlog::info("Reloading {0}...", it.second->get_path());
if (it.second->get()) {
const auto ures = diablo2::d2_client::unload_gfx_resource(static_cast<diablo2::structures::cellfile*>(it.second->get()));
spdlog::info("Unloaded: {0}", ures);
}
const auto newData = instance.load_asset_data(it.second->get_path(), it.second->get_type());
it.second->update(newData);
spdlog::info("Done! {0}", newData);
}
return result;
}
d2_tweaks::common::asset* d2_tweaks::common::asset_manager::get_mpq_file(const std::string& path, mpq_file_type_t type) {
if (path.empty())
return nullptr;
const auto it = m_assets.find(path);
if (it != m_assets.end())
return it->second;
const auto asset = new common::asset(path, load_asset_data(path, type), type);
m_assets[path] = asset;
return asset;
}
void* d2_tweaks::common::asset_manager::load_asset_data(const std::string& path, mpq_file_type_t type) {
switch (type) {
case MPQ_FILE_TYPE_DC6:
return diablo2::d2_client::load_gfx_resource(const_cast<char*>(path.c_str()));
default:
return nullptr;
}
}

View File

@@ -0,0 +1,181 @@
#include <d2tweaks/common/common.h>
#include <spdlog/spdlog.h>
#include <d2tweaks/common/protocol.h>
#include <d2tweaks/common/asset_manager.h>
#include <diablo2/d2net.h>
#include <common/hooking.h>
static int32_t(__fastcall* g_get_packet_size_client_to_server)(d2_tweaks::common::packet_header* data, size_t size, size_t* sizeOut);
static char(__fastcall* g_get_packet_size_server_to_client)(d2_tweaks::common::packet_header* data, size_t size, size_t* outSize);
static int32_t(__stdcall* g_net_send_to_server)(int32_t queue, d2_tweaks::common::packet_header* packet, size_t size);
static int32_t(__stdcall* g_net_send_to_client)(int32_t queue, int32_t clientId, d2_tweaks::common::packet_header* packet, size_t size);
static int32_t g_ebp_send_to_server;
static int32_t g_ebp_send_to_client;
static int32_t __stdcall net_send_to_server(int32_t queue, d2_tweaks::common::packet_header* packet, size_t size) {
__asm {
push [ebp + 4]
pop [g_ebp_send_to_server]
}
spdlog::debug("[d2net SEND C >>> S] Queue {} Packet {} Size {} CallFrom {}", queue, (void*)packet->d2_packet_type, size, (void*)g_ebp_send_to_server);
//do something
return g_net_send_to_server(queue, packet, size);
}
static int32_t __stdcall net_send_to_client(int32_t queue, int32_t clientId, d2_tweaks::common::packet_header* packet, size_t size) {
__asm {
push [ebp+4]
pop [g_ebp_send_to_client]
}
spdlog::error("[d2net SEND S >>> C] Queue {} Packet {} Size {} ClientID {} CallFrom {}", queue, (void*)packet->d2_packet_type, size, clientId, (void*)g_ebp_send_to_client);
//do something
return g_net_send_to_client(queue, clientId, packet, size);
}
static int32_t __fastcall get_packet_size_client_to_server(d2_tweaks::common::packet_header* data, size_t size, size_t* sizeOut) {
static d2_tweaks::common::packet_header dummy;
static auto& instance = singleton<d2_tweaks::common::common>::instance();
if (data->d2_packet_type == dummy.d2_packet_type) {
size_t packetSize;
if (!instance.get_packet_size_cs(data, packetSize))
return g_get_packet_size_client_to_server(data, size, sizeOut);
*sizeOut = packetSize;
return packetSize;
}
return g_get_packet_size_client_to_server(data, size, sizeOut);
}
static char __fastcall get_packet_size_server_to_client(d2_tweaks::common::packet_header* data, size_t size, size_t* sizeOut) {
static d2_tweaks::common::packet_header dummy;
static auto& instance = singleton<d2_tweaks::common::common>::instance();
if (data->d2_packet_type == dummy.d2_packet_type) {
size_t packetSize;
if (!instance.get_packet_size_sc(data, packetSize))
return g_get_packet_size_server_to_client(data, size, sizeOut);
*sizeOut = packetSize;
return static_cast<char>(packetSize);
}
return g_get_packet_size_server_to_client(data, size, sizeOut);
}
d2_tweaks::common::common::common(token) {}
void d2_tweaks::common::common::init() {
singleton<asset_manager>::instance().init();
hooking::hook(reinterpret_cast<void*>(diablo2::d2_net::get_base() + 0x1B60), get_packet_size_server_to_client, reinterpret_cast<void**>(&g_get_packet_size_server_to_client));
hooking::hook(reinterpret_cast<void*>(diablo2::d2_net::get_base() + 0x1E60), get_packet_size_client_to_server, reinterpret_cast<void**>(&g_get_packet_size_client_to_server));
#ifndef NDEBUG
//hooking::hook(reinterpret_cast<void*>(diablo2::d2_net::get_base() + 0x1760), net_send_to_server, reinterpret_cast<void**>(&g_net_send_to_server));
//hooking::hook(reinterpret_cast<void*>(diablo2::d2_net::get_base() + 0x22B0), net_send_to_client, reinterpret_cast<void**>(&g_net_send_to_client));
#endif
}
bool d2_tweaks::common::common::get_packet_size_cs(packet_header* packet, size_t& size) {
switch (packet->message_type) {
case MESSAGE_TYPE_ITEM_MOVE:
{
size = sizeof item_move_cs;
return true;
}
case MESSAGE_TYPE_INVENTORY_SORT:
{
size = sizeof inventory_sort_cs;
return true;
}
case MESSAGE_TYPE_DAMAGE_INFO:
{
size = sizeof damage_info_cs;
return true;
}
case MESSAGE_TYPE_GOLD_PICKUP_INFO:
{
size = sizeof gold_pickup_info_cs;
return true;
}
case MESSAGE_TYPE_ITEM_PICKUP_INFO:
{
size = sizeof item_pickup_info_cs;
return true;
}
case MESSAGE_TYPE_ITEM_DROPPED_INFO:
{
size = sizeof item_dropped_info_cs;
return true;
}
case MESSAGE_TYPE_TRANSMUTE:
{
size = sizeof transmute_info_cs;
return true;
}
case MESSAGE_TYPE_TRADER_UPDATE:
{
size = sizeof trader_update_cs;
return true;
}
default:
return false;
}
}
bool d2_tweaks::common::common::get_packet_size_sc(packet_header* packet, size_t& size) {
switch (packet->message_type) {
case MESSAGE_TYPE_ITEM_MOVE:
{
size = sizeof item_move_sc;
return true;
}
case MESSAGE_TYPE_INVENTORY_SORT:
{
size = sizeof inventory_sort_sc;
return true;
}
case MESSAGE_TYPE_DAMAGE_INFO:
{
size = sizeof damage_info_sc;
return true;
}
case MESSAGE_TYPE_GOLD_PICKUP_INFO:
{
size = sizeof gold_pickup_info_sc;
return true;
}
case MESSAGE_TYPE_ITEM_PICKUP_INFO:
{
size = sizeof item_pickup_info_sc;
return true;
}
case MESSAGE_TYPE_ITEM_DROPPED_INFO:
{
size = sizeof item_dropped_info_sc;
return true;
}
case MESSAGE_TYPE_TRANSMUTE:
{
size = sizeof transmute_info_sc;
return true;
}
case MESSAGE_TYPE_TRADER_UPDATE:
{
size = sizeof trader_update_sc;
return true;
}
default:
return false;
}
}

View File

@@ -0,0 +1,105 @@
#include <d2tweaks/server/modules/auto_gold_pickup/auto_gold_pickup_server.h>
#include <d2tweaks/server/server.h>
#include <spdlog/spdlog.h>
#include <diablo2/d2game.h>
#include <diablo2/d2common.h>
#include <diablo2/structures/unit.h>
#include <diablo2/structures/room.h>
#include <diablo2/structures/data/items_line.h>
#include <diablo2/structures/player_data.h>
#include <diablo2/structures/unit.h>
MODULE_INIT(auto_gold_pickup)
void d2_tweaks::server::modules::auto_gold_pickup::init() {
char acPathToIni[MAX_PATH] = { 0 };
const char* pcIniFile = "\\d2tweaks.ini";
GetCurrentDirectory(MAX_PATH, acPathToIni);
lstrcat(acPathToIni, pcIniFile);
if (GetPrivateProfileInt("modules", "AutoGoldPickup", 1, acPathToIni) != FALSE) {
singleton<server>::instance().register_packet_handler(common::MESSAGE_TYPE_GOLD_PICKUP_INFO, this);
//singleton<server>::instance().register_tick_handler(this);
}
}
bool d2_tweaks::server::modules::auto_gold_pickup::handle_packet(diablo2::structures::game* game, diablo2::structures::unit* player, common::packet_header* packet) {
const auto income_packet_cs = static_cast<common::gold_pickup_info_cs*>(packet);
static auto& instance = singleton<server>::instance();
const auto item = instance.get_server_unit(game, income_packet_cs->item_guid, diablo2::structures::unit_type_t::UNIT_TYPE_ITEM); //0x4 = item
if (item == nullptr)
return true; //block further packet processing
d2_tweaks::server::modules::auto_gold_pickup::au_pickup_gold(game, player, item);
return true;
}
bool d2_tweaks::server::modules::auto_gold_pickup::au_pickup_gold(diablo2::structures::game* game, diablo2::structures::unit* unit, diablo2::structures::unit* item)
{
static common::gold_pickup_info_sc packet;
const auto currentGold = diablo2::d2_common::get_stat(unit, diablo2::UNIT_STAT_GOLD, 0);
const auto goldToPickup = diablo2::d2_common::get_stat(item, diablo2::UNIT_STAT_GOLD, 0);
const auto maxGold = diablo2::d2_common::get_maximum_character_gold(unit);
if (currentGold + goldToPickup > maxGold)
return FALSE;
diablo2::d2_game::pickup_gold_pile(game, unit, item);
packet.gold = goldToPickup;
singleton<server>::instance().send_packet(unit->player_data->net_client, &packet, sizeof packet);
return TRUE;
}
void d2_tweaks::server::modules::auto_gold_pickup::tick(diablo2::structures::game* game,
diablo2::structures::unit* unit) {
//static common::gold_pickup_info_sc packet;
//static auto& instance = singleton<d2_tweaks::server::server>::instance();
//if (!game || !unit)
// return;
//if (unit->type != diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER)
// return;
//const auto room = diablo2::d2_common::get_room_from_unit(unit);
//if (!room)
// return;
//for (auto item = room->unit; item; item = item->prev_unit_in_room) {
// if (!item)
// continue;
// if (item->type != diablo2::structures::unit_type_t::UNIT_TYPE_ITEM)
// continue;
// auto record = diablo2::d2_common::get_item_record(item->data_record_index);
// if (!record)
// continue;
// const auto distance = diablo2::d2_common::get_distance_between_units(unit, item);
// if (distance > g_distance)
// continue;
// // <20><> <20><><EFBFBD><EFBFBD> <20><><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
// if (record->string_code[0] == 'g' &&
// record->string_code[1] == 'l' &&
// record->string_code[2] == 'd')
// {
// d2_tweaks::server::modules::auto_gold_pickup::au_pickup_gold(game, unit, item);
// break;
// }
// continue;
//}
}

View File

@@ -0,0 +1,181 @@
#include <d2tweaks/server/modules/auto_item_pickup/auto_item_pickup_server.h>
#include <d2tweaks/server/server.h>
#include <spdlog/spdlog.h>
#include <diablo2/d2game.h>
#include <diablo2/d2common.h>
#include <diablo2/d2lang.h>
#include <diablo2/structures/unit.h>
#include <diablo2/structures/room.h>
#include <diablo2/structures/data/items_line.h>
#include <diablo2/structures/data/item_types_line.h>
#include <diablo2/structures/player_data.h>
#include <diablo2/structures/unit.h>
#include <common/autopickup_lootfilter.h>
MODULE_INIT(auto_item_pickup)
void d2_tweaks::server::modules::auto_item_pickup::init() {
char acPathToIni[MAX_PATH] = { 0 };
const char* pcIniFile = "\\d2tweaks.ini";
GetCurrentDirectory(MAX_PATH, acPathToIni);
lstrcat(acPathToIni, pcIniFile);
if (GetPrivateProfileInt("modules", "AutoItemPickup", 1, acPathToIni) != FALSE) {
singleton<server>::instance().register_packet_handler(common::MESSAGE_TYPE_ITEM_PICKUP_INFO, this);
singleton<server>::instance().register_tick_handler(this);
}
}
void d2_tweaks::server::modules::auto_item_pickup::tick(diablo2::structures::game* game,
diablo2::structures::unit* unit) {
//static common::item_pickup_info_sc packet;
//static auto& instance = singleton<d2_tweaks::server::server>::instance();
//if (!game || !unit)
// return;
//if (unit->type != diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER)
// return;
//const auto room = diablo2::d2_common::get_room_from_unit(unit);
//if (!room)
// return;
//for (auto item = room->unit; item; item = item->prev_unit_in_room) {
// if (!item)
// continue;
// if (item->type != diablo2::structures::unit_type_t::UNIT_TYPE_ITEM)
// continue;
// const auto record = diablo2::d2_common::get_item_record(item->data_record_index);
// if (!record)
// continue;
// const auto distance = diablo2::d2_common::get_distance_between_units(unit, item);
// if (distance > g_distance)
// continue;
// const auto itemtype_record = diablo2::d2_common::get_item_type_record(record->type);
// auto itemtype_record_equiv1 = diablo2::d2_common::get_item_type_record(itemtype_record->equiv1);
// auto itemtype_record_equiv2 = diablo2::d2_common::get_item_type_record(itemtype_record->equiv2);
// uint32_t quality = diablo2::d2_common::get_item_quality(item);
// // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
// char arr_itemtype_codestr_equivstr[20][5] = { 0 };
// // itemtype code <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
// *(DWORD*)arr_itemtype_codestr_equivstr[0] = *(DWORD*)itemtype_record->code;
// // index second code in array
// uint32_t index_arr_itemtype = 1;
// if (itemtype_record_equiv1) {
// if (*(DWORD*)itemtype_record_equiv1->code != 0x20202020) {
// // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> equiv1
// *(DWORD*)arr_itemtype_codestr_equivstr[index_arr_itemtype] = *(DWORD*)itemtype_record_equiv1->code;
// index_arr_itemtype++;
// // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> eqiv1 <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
// for (index_arr_itemtype; itemtype_record_equiv1->equiv1 != 0; index_arr_itemtype++) {
// // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
// itemtype_record_equiv1 = diablo2::d2_common::get_item_type_record(itemtype_record_equiv1->equiv1);
// if (*(DWORD*)itemtype_record_equiv1->code != 0x20202020) {
// *(DWORD*)arr_itemtype_codestr_equivstr[index_arr_itemtype] = *(DWORD*)itemtype_record_equiv1->code;
// }
// else break;
// }
// }
// }
// if (itemtype_record_equiv2) {
// if (*(DWORD*)itemtype_record_equiv2->code != 0x20202020) {
// // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> equiv1
// *(DWORD*)arr_itemtype_codestr_equivstr[index_arr_itemtype] = *(DWORD*)itemtype_record_equiv2->code;
// index_arr_itemtype++;
// // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> eqiv1 <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
// for (index_arr_itemtype; itemtype_record_equiv2->equiv2 != 0; index_arr_itemtype++) {
// // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
// itemtype_record_equiv2 = diablo2::d2_common::get_item_type_record(itemtype_record_equiv2->equiv2);
// if (*(DWORD*)itemtype_record_equiv2->code != 0x20202020) {
// *(DWORD*)arr_itemtype_codestr_equivstr[index_arr_itemtype] = *(DWORD*)itemtype_record_equiv2->code;
// }
// else break;
// }
// }
// }
// for (uint32_t i = 0; i < g_pickup_count_itemtype_code; i++)
// {
// for (uint32_t count = 0; count < index_arr_itemtype; count++)
// {
// if (*(DWORD*)arr_itemtype_codestr_equivstr[count] == (DWORD)g_pickup_itemtype_code_st[i].dwtype)
// {
// if (g_pickup_itemtype_code_st[i].qualityinclude[quality] == TRUE)
// {
// d2_tweaks::server::modules::auto_item_pickup::au_pickup_item(game, unit, item);
// goto L1;
// }
// }
// }
// }
// for (uint32_t i = 0; i < g_pickup_count_all_items; i++)
// {
// if (record->string_code[0] == g_pickup_itemcode_st[i].code0 &&
// record->string_code[1] == g_pickup_itemcode_st[i].code1 &&
// record->string_code[2] == g_pickup_itemcode_st[i].code2)
// {
// if (g_pickup_itemcode_st[i].qualityinclude[quality] == TRUE)
// {
// d2_tweaks::server::modules::auto_item_pickup::au_pickup_item(game, unit, item);
// }
// }
// }
// L1:
// continue;
//}
//g_tick_between_item_pickup++;
////spdlog::info("current {0}", g_tick_between_item_pickup);
}
bool d2_tweaks::server::modules::auto_item_pickup::au_pickup_item(diablo2::structures::game* game, diablo2::structures::unit* unit, uint32_t guid)
{
static common::item_pickup_info_sc packet;
uint32_t ptrNull = 0;
//if (g_tick_between_item_pickup >= 25) {
// true - if item picked up, false - if inventory full
if (diablo2::d2_game::pickup_item(game, unit, guid, &ptrNull) != true)
{
//packet.inventory_full = true;
//singleton<server>::instance().send_packet(unit->player_data->net_client, &packet, sizeof packet);
}
//}
return true;
}
bool d2_tweaks::server::modules::auto_item_pickup::handle_packet(diablo2::structures::game* game, diablo2::structures::unit* player, common::packet_header* packet) {
const auto income_packet_cs = static_cast<common::item_pickup_info_cs*>(packet);
static auto& instance = singleton<server>::instance();
//const auto item = instance.get_server_unit(game, income_packet_cs->item_guid, diablo2::structures::unit_type_t::UNIT_TYPE_ITEM); //0x4 = item
//if (item == nullptr)
// return true; //block further packet processing
d2_tweaks::server::modules::auto_item_pickup::au_pickup_item(game, player, income_packet_cs->item_guid);
return true;
}

View File

@@ -0,0 +1,360 @@
#include <d2tweaks/server/modules/autosort/autosort_server.h>
#include <d2tweaks/server/server.h>
#include <vector>
#include <spdlog/spdlog.h>
#include <d2tweaks/common/protocol.h>
#include <diablo2/d2common.h>
#include <diablo2/d2game.h>
#include <diablo2/structures/path.h>
#include <diablo2/structures/game.h>
#include <diablo2/structures/unit.h>
#include <diablo2/structures/inventory.h>
#include <diablo2/structures/item_data.h>
#include <diablo2/structures/data/items_line.h>
#include <diablo2/structures/data/item_types_line.h>
#include <diablo2/structures/player_data.h>
MODULE_INIT(autosort)
struct backup_item {
diablo2::structures::unit* item;
uint8_t x;
uint8_t y;
backup_item() : item(nullptr), x(0), y(0) {}
};
struct packed_area {
uint8_t x;
uint8_t y;
uint8_t w;
uint8_t h;
};
// Define the inventory zone
int iminValidX = 0;
int imaxValidX = 15;
int iminValidY = 0;
int imaxValidY = 12;
// Define the charm zone
int cminValidX = 0;
int cmaxValidX = 15;
int cminValidY = 12;
int cmaxValidY = 15;
void d2_tweaks::server::modules::autosort::init() {
char acPathToIni[MAX_PATH] = { 0 };
const char* pcIniFile = "\\d2tweaks.ini";
GetCurrentDirectory(MAX_PATH, acPathToIni);
lstrcat(acPathToIni, pcIniFile);
if (GetPrivateProfileInt("modules", "Autosort", 1, acPathToIni) != FALSE) {
singleton<server>::instance().register_packet_handler(common::MESSAGE_TYPE_INVENTORY_SORT, this);
}
}
bool d2_tweaks::server::modules::autosort::handle_packet(diablo2::structures::game* game,
diablo2::structures::unit* player, common::packet_header* packet) {
sort(game, player, static_cast<common::inventory_sort_cs*>(packet)->page);
return true;
}
bool d2_tweaks::server::modules::autosort::sort(diablo2::structures::game* game, diablo2::structures::unit* player, uint8_t page) {
static common::inventory_sort_sc packet;
static auto& instance = singleton<server>::instance();
if (player == nullptr)
return false;
const auto inventoryIndex = diablo2::d2_common::get_inventory_index(player, page, game->item_format == 101);
char data[0x18];
diablo2::d2_common::get_inventory_data(inventoryIndex, 0, data);
const auto inventoryWidth = static_cast<uint32_t>(data[0]);
const auto inventoryHeight = static_cast<uint32_t>(data[1]);
std::vector<packed_area> areas{ {0,0, static_cast<uint8_t>(inventoryWidth), static_cast<uint8_t>(inventoryHeight)} };
std::vector<diablo2::structures::unit*> items;
std::vector<diablo2::structures::unit*> charms;
std::vector<backup_item> backup_items;
std::unordered_map<uint32_t, std::vector<diablo2::structures::unit*>> items_typed;
std::unordered_map<uint32_t, std::vector<diablo2::structures::unit*>> charms_typed;
uint32_t occupied_cells = 0;
for (auto item = player->inventory->first_item; item != nullptr; item = item->item_data->pt_next_item) {
if (item->item_data->page != page)
continue;
const auto record = diablo2::d2_common::get_item_record(item->data_record_index);
const auto record_type = diablo2::d2_common::get_item_type_record(record->type);
bool is_charm = record_type->charm;
backup_item backup;
backup.item = item;
backup.x = item->path->x;
backup.y = item->path->y;
backup_items.push_back(backup);
// only check charmzone grid for inventory page 0
// Check if item's coordinates are within the valid range
if (is_charm) {
// Coordinates are within the valid range, add the item to items vector
charms_typed[item->data_record_index].push_back(item);
charms.push_back(item);
occupied_cells += record->inv_height * record->inv_width;
}
else {
// Coordinates are within the valid range, add the item to items vector
items_typed[item->data_record_index].push_back(item);
items.push_back(item);
occupied_cells += record->inv_height * record->inv_width;
}
spdlog::info("--------------------------------");
spdlog::info("Item Type: {0}", record->type);
spdlog::info("Item Type 2: {0}", record->type2);
spdlog::info("Occupied Cells: {0}", occupied_cells);
spdlog::info("x: {0}", backup.x);
spdlog::info("y: {0}", backup.y);
spdlog::info("is_charm: {0}\n\n", is_charm);
}
if (occupied_cells > inventoryHeight* inventoryWidth) {
//should never be happen in normal cases
spdlog::warn("occupied_cells > inventoryHeight* inventoryWidth");
return false;
}
// Remove all items from inventory
for (auto item : items)
diablo2::d2_common::inv_remove_item(player->inventory, item);
for (auto item : charms)
diablo2::d2_common::inv_remove_item(player->inventory, item);
const auto itemsCount = items.size();
const auto charmsCount = charms.size();
if (itemsCount == 0)
return true; //there's nothing to sort
auto success = NULL;
if (itemsCount > 0) {
//sort items by height*width
for (size_t i = 0; i < itemsCount - 1; i++) {
auto swapped = false;
for (size_t j = 0; j < itemsCount - i - 1; j++) {
const auto record1 = diablo2::d2_common::get_item_record(items[j]->data_record_index);
const auto record2 = diablo2::d2_common::get_item_record(items[j + 1]->data_record_index);
if (record1->inv_height * record1->inv_width > record2->inv_height * record2->inv_width)
continue;
const auto temp = items[j];
items[j] = items[j + 1];
items[j + 1] = temp;
swapped = true;
}
if (swapped == false)
break;
}
success = TRUE;
for (auto sorted_item : items) {
for (auto& item : items_typed[sorted_item->data_record_index]) { //iterate through all items of the same type for grouping
const auto record = diablo2::d2_common::get_item_record(item->data_record_index);
uint32_t tx, ty;
if (!find_free_space(player->inventory, item, inventoryIndex, page, tx, ty, false)) {
success = false;
break;
}
if (!diablo2::d2_common::inv_add_item(player->inventory, item, tx, ty, inventoryIndex, false, page)) {
success = false;
break;
}
if (diablo2::d2_common::inv_update_item(player->inventory, item, false))
continue;
success = false;
break;
}
if (!success)
break;
items_typed.erase(sorted_item->data_record_index);
}
}
if (charmsCount > 0) {
//sort charms by height*width
for (size_t i = 0; i < charmsCount - 1; i++) {
auto swapped = false;
for (size_t j = 0; j < charmsCount - i - 1; j++) {
const auto record1 = diablo2::d2_common::get_item_record(charms[j]->data_record_index);
const auto record2 = diablo2::d2_common::get_item_record(charms[j + 1]->data_record_index);
if (record1->inv_height * record1->inv_width > record2->inv_height * record2->inv_width)
continue;
const auto temp = charms[j];
charms[j] = charms[j + 1];
charms[j + 1] = temp;
swapped = true;
}
if (swapped == false)
break;
}
success = TRUE;
for (auto sorted_item : charms) {
for (auto& item : charms_typed[sorted_item->data_record_index]) { //iterate through all items of the same type for grouping
const auto record = diablo2::d2_common::get_item_record(item->data_record_index);
uint32_t tx, ty;
if (!find_free_space(player->inventory, item, inventoryIndex, page, tx, ty, true)) {
success = false;
break;
}
if (!diablo2::d2_common::inv_add_item(player->inventory, item, tx, ty, inventoryIndex, false, page)) {
success = false;
break;
}
if (diablo2::d2_common::inv_update_item(player->inventory, item, false))
continue;
success = false;
break;
}
if (!success)
break;
charms_typed.erase(sorted_item->data_record_index);
}
}
if (success) {
while (items.size() > 0) {
packet.page = page;
packet.tx = items.back()->path->x;
packet.ty = items.back()->path->y;
packet.guid = items.back()->guid;
items.pop_back();
instance.send_packet(player->player_data->net_client, &packet, sizeof packet);
}
while (charms.size() > 0) {
packet.page = page;
packet.tx = charms.back()->path->x;
packet.ty = charms.back()->path->y;
packet.guid = charms.back()->guid;
charms.pop_back();
instance.send_packet(player->player_data->net_client, &packet, sizeof packet);
}
diablo2::d2_game::update_inventory_items(game, player);
return true;
}
//sorting failed, remove all items and re-insert them at old coords
for (auto& backup_item : backup_items) {
const auto item = backup_item.item;
diablo2::d2_common::inv_remove_item(player->inventory, item);
}
while (backup_items.size() > 0) {
const auto backup = backup_items.back();
const auto item = backup.item;
const auto tx = backup.x;
const auto ty = backup.y;
packet.page = page;
packet.tx = tx;
packet.ty = ty;
packet.guid = item->guid;
diablo2::d2_common::inv_add_item(player->inventory, item, tx, ty, inventoryIndex, false, page);
diablo2::d2_common::inv_update_item(player->inventory, item, false);
backup_items.pop_back();
instance.send_packet(player->player_data->net_client, &packet, sizeof packet);
}
diablo2::d2_game::update_inventory_items(game, player);
return false;
}
bool d2_tweaks::server::modules::autosort::find_free_space(diablo2::structures::inventory* inv,
diablo2::structures::unit* item,
int32_t inventoryIndex, char page, uint32_t& x,
uint32_t& y, bool isCharmZone) {
char data[0x18];
diablo2::d2_common::get_inventory_data(inventoryIndex, 0, data);
// inventory coords
const auto mx = static_cast<uint32_t>(data[0]);
const auto my = static_cast<uint32_t>(data[1]);
const int minValidX = isCharmZone ? cminValidX : iminValidX;
const int maxValidX = isCharmZone ? cmaxValidX : imaxValidX;
const int minValidY = isCharmZone ? cminValidY : iminValidY;
const int maxValidY = isCharmZone ? cmaxValidY : imaxValidY;
// Get the item record to access the item size
const auto record = diablo2::d2_common::get_item_record(item->data_record_index);
// Update the maximum valid Y coordinate for the inventory zone
const int inventoryMaxValidY = isCharmZone ? maxValidY : 11;
if (page == 0x03 || page == 0x04) {
for (x = 0; x < mx; x++) {
for (y = 0; y < my; y++) {
diablo2::structures::unit* blockingUnit = nullptr;
uint32_t blockingUnitIndex = 0;
if (diablo2::d2_common::can_put_into_slot(inv, item, x, y, inventoryIndex, &blockingUnit, &blockingUnitIndex, page))
return true;
}
}
return false;
} else {
for (x = minValidX; x <= maxValidX - record->inv_width + 1; x++) {
for (y = minValidY; y <= inventoryMaxValidY - record->inv_height + 1; y++) {
diablo2::structures::unit* blockingUnit = nullptr;
uint32_t blockingUnitIndex = 0;
// Check if the item can be placed at the current position without overflowing
if (diablo2::d2_common::can_put_into_slot(inv, item, x, y, inventoryIndex, &blockingUnit, &blockingUnitIndex, page))
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,264 @@
#include <d2tweaks/server/modules/damage_display/damage_display_server.h>
#include <d2tweaks/server/server.h>
#include <diablo2/d2game.h>
#include <diablo2/structures/damage.h>
#include <diablo2/structures/unit.h>
#include <diablo2/structures/player_data.h>
#include <diablo2/structures/monster_data.h>
#include <diablo2/structures/net_client.h>
#include <common/hooking.h>
#include <diablo2/d2common.h>
#include <spdlog/spdlog.h>
#include <diablo2/structures/data/monstats_line.h>
MODULE_INIT(damage_display)
static char(__fastcall* g_apply_attack_results_origin)(diablo2::structures::game* game,
diablo2::structures::unit* attacker,
diablo2::structures::unit* defender,
BOOL recalculateDamage,
diablo2::structures::damage* dmg);
#define PRINT_DMG_DELIMITER(name, delimiter) if(dmg->name > 0) spdlog::debug(#name ": {0}", dmg->name / delimiter)
#define PRINT_DMG(name) PRINT_DMG_DELIMITER(name, 256)
d2_tweaks::common::damage_type_t get_damage_type(diablo2::structures::damage* dmg) {
uint32_t damage[d2_tweaks::common::DAMAGE_TYPE_COUNT]{ 0 };
if (dmg == nullptr || dmg->dmg_total == 0)
return d2_tweaks::common::DAMAGE_TYPE_UNKNOWN;
damage[d2_tweaks::common::DAMAGE_TYPE_PHYSICAL] = dmg->phys_damage;
damage[d2_tweaks::common::DAMAGE_TYPE_COLD] = dmg->cold_damage;
damage[d2_tweaks::common::DAMAGE_TYPE_FIRE] = dmg->fire_damage;
damage[d2_tweaks::common::DAMAGE_TYPE_LIGHTNING] = dmg->ltng_damage;
damage[d2_tweaks::common::DAMAGE_TYPE_POISON] = dmg->pois_damage;
damage[d2_tweaks::common::DAMAGE_TYPE_MAGIC] = dmg->mag_damage;
auto result = d2_tweaks::common::DAMAGE_TYPE_UNKNOWN;
uint32_t damage_temp = 0;
for (size_t i = 0; i < d2_tweaks::common::DAMAGE_TYPE_COUNT; i++) {
if (damage[i] <= damage_temp)
continue;
result = static_cast<d2_tweaks::common::damage_type_t>(i);
damage_temp = damage[i];
}
return result;
}
static void send_damage_data(diablo2::structures::unit* defender,
diablo2::structures::net_client* client,
diablo2::structures::damage* dmg) {
static auto& instance = singleton<d2_tweaks::server::server>::instance();
static d2_tweaks::common::damage_info_sc packet;
if (dmg->dmg_total <= 0)
return;
auto currentHp = diablo2::d2_common::get_stat(defender, diablo2::UNIT_STAT_HITPOINTS, 0);
auto maxHp = diablo2::d2_common::get_stat(defender, diablo2::UNIT_STAT_MAXHP, 0);
packet.unit_type = static_cast<uint8_t>(defender->type);
packet.guid = defender->guid;
packet.damage_type = get_damage_type(dmg);
packet.damage = dmg->dmg_total / 256;
packet.currentHp = currentHp / 256;
packet.maxHp = maxHp / 256;
spdlog::info("currentHp: {0}", packet.currentHp);
spdlog::info("maxHp: {0}", packet.maxHp);
spdlog::info("damage: {0}", packet.damage);
if (packet.damage_type == d2_tweaks::common::DAMAGE_TYPE_UNKNOWN)
return;
if (packet.damage <= 0)
return;
instance.send_packet(client, &packet, sizeof packet);
}
static bool has_players(diablo2::structures::unit* attacker,
diablo2::structures::unit* defender) {
return attacker->type == diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER ||
defender->type == diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER;
}
static bool has_hirelings(diablo2::structures::unit* attacker,
diablo2::structures::unit* defender) {
return attacker && attacker->is_hireling() || defender && defender->is_hireling();
}
static bool has_pets(diablo2::structures::unit* attacker,
diablo2::structures::unit* defender) {
return attacker && attacker->is_pet() || defender && defender->is_pet();
}
static diablo2::structures::unit* get_hireling_owner(diablo2::structures::game* game,
diablo2::structures::unit* unit) {
static auto& instance = singleton<d2_tweaks::server::server>::instance();
if (!unit)
return nullptr;
auto guid = diablo2::d2_common::get_stat(unit, diablo2::UNIT_STAT_UNUSED212, 0);
if (guid != 0)
return instance.get_server_unit(game, guid, diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER);
instance.iterate_server_units(game, diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER, [&](diablo2::structures::unit* player) {
const auto pet = diablo2::d2_game::get_player_pet(game, player, 7, 0);
if (pet == unit) {
guid = player->guid;
return false; //stop iterator
}
return true;
});
if (guid == 0)
return nullptr;
diablo2::d2_common::set_stat(unit, diablo2::UNIT_STAT_UNUSED212, guid, 0);
return instance.get_server_unit(game, guid, diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER);
}
static diablo2::structures::unit* get_pet_owner(diablo2::structures::game* game,
diablo2::structures::unit* unit) {
static auto& instance = singleton<d2_tweaks::server::server>::instance();
if (!unit)
return nullptr;
auto guid = diablo2::d2_common::get_stat(unit, diablo2::UNIT_STAT_UNUSED212, 0);
if (guid != 0)
return instance.get_server_unit(game, guid, diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER);
instance.iterate_server_units(game, diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER, [&](diablo2::structures::unit* player) {
diablo2::d2_game::iterate_unit_pets(
game, player, [&](diablo2::structures::game*, diablo2::structures::unit*, diablo2::structures::unit* u) {
if (u != unit)
return;
guid = player->guid;
});
return guid == 0;
});
if (guid == 0)
return nullptr;
diablo2::d2_common::set_stat(unit, diablo2::UNIT_STAT_UNUSED212, guid, 0);
return instance.get_server_unit(game, guid, diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER);
}
static void process_players_damage(diablo2::structures::unit* attacker,
diablo2::structures::unit* defender,
diablo2::structures::damage* dmg) {
diablo2::structures::net_client* client = nullptr;
if (attacker->type == diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER)
client = attacker->player_data->net_client;
else if (defender->type == diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER)
client = defender->player_data->net_client;
send_damage_data(defender, client, dmg);
}
static void process_hireling_damage(diablo2::structures::game* game,
diablo2::structures::unit* attacker,
diablo2::structures::unit* defender,
diablo2::structures::damage* dmg) {
diablo2::structures::unit* owner = nullptr;
if (attacker->is_hireling()) {
owner = get_hireling_owner(game, attacker);
} else if (defender->is_hireling()) {
owner = get_hireling_owner(game, defender);
}
if (!owner)
return;
send_damage_data(defender, owner->player_data->net_client, dmg);
}
static void process_pet_damage(diablo2::structures::game* game,
diablo2::structures::unit* attacker,
diablo2::structures::unit* defender,
diablo2::structures::damage* dmg) {
diablo2::structures::unit* owner = nullptr;
if (attacker->is_pet()) {
owner = get_pet_owner(game, attacker);
} else if (defender->is_pet()) {
owner = get_pet_owner(game, defender);
}
if (!owner)
return;
send_damage_data(defender, owner->player_data->net_client, dmg);
}
static char __fastcall apply_attack_results(diablo2::structures::game* game,
diablo2::structures::unit* attacker,
diablo2::structures::unit* defender,
BOOL recalculateDamage,
diablo2::structures::damage* dmg) {
static auto& instance = singleton<d2_tweaks::server::server>::instance();
static char result = 0;
result = g_apply_attack_results_origin(game, attacker, defender, recalculateDamage, dmg);
if (has_players(attacker, defender)) {
process_players_damage(attacker, defender, dmg);
return result;
}
if (has_hirelings(attacker, defender)) {
process_hireling_damage(game, attacker, defender, dmg);
return result;
}
if (has_pets(attacker, defender)) {
process_pet_damage(game, attacker, defender, dmg);
return result;
}
return result;
}
void d2_tweaks::server::modules::damage_display::init() {
char acPathToIni[MAX_PATH] = { 0 };
const char* pcIniFile = "\\d2tweaks.ini";
GetCurrentDirectory(MAX_PATH, acPathToIni);
lstrcat(acPathToIni, pcIniFile);
if (GetPrivateProfileInt("modules", "DamageDisplay", 1, acPathToIni) != FALSE) {
hooking::hook(diablo2::d2_game::get_base() + 0x8FE90, apply_attack_results, reinterpret_cast<void**>(&g_apply_attack_results_origin));
singleton<server>::instance().register_packet_handler(common::MESSAGE_TYPE_DAMAGE_INFO, this);
}
}
bool d2_tweaks::server::modules::damage_display::handle_packet(diablo2::structures::game* game,
diablo2::structures::unit* player, common::packet_header* packet) {
return true;
}
void d2_tweaks::server::modules::damage_display::tick(diablo2::structures::game* game, diablo2::structures::unit* unit) {}

View File

@@ -0,0 +1,136 @@
#include <d2tweaks/server/modules/identify_on_pickup/identify_on_pickup_server.h>
#include <d2tweaks/server/server.h>
#include <common/hooking.h>
#include <diablo2/d2game.h>
#include <diablo2/d2common.h>
#include <diablo2/structures/unit.h>
MODULE_INIT(identify_on_pickup)
static unsigned int g_item_Normal = 0;
static unsigned int g_item_Superior = 0;
static unsigned int g_item_Magic = 0;
static unsigned int g_item_Rare = 0;
static unsigned int g_item_Set = 0;
static unsigned int g_item_Unique = 0;
static unsigned int g_item_Crafted = 0;
static unsigned int g_item_Tempered = 0;
static uint32_t(__fastcall* g_pickup_item_original)(diablo2::structures::game*,
diablo2::structures::unit*,
uint32_t, uint32_t);
static uint32_t __fastcall pickup_item(diablo2::structures::game* game,
diablo2::structures::unit* player,
uint32_t guid,
uint32_t a4) {
static auto& instance = singleton<d2_tweaks::server::server>::instance();
if (!game || !player || player->type != diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER)
return g_pickup_item_original(game, player, guid, a4);
const auto item = instance.get_server_unit(game, guid, diablo2::structures::unit_type_t::UNIT_TYPE_ITEM);
if (!item)
return g_pickup_item_original(game, player, guid, a4);
auto quality = diablo2::d2_common::get_item_quality(item);
if (quality == 2 && g_item_Normal != 0) {
diablo2::d2_game::identify_item(game, player, item);
}
if (quality == 3 && g_item_Superior != 0) {
diablo2::d2_game::identify_item(game, player, item);
}
if (quality == 4 && g_item_Magic != 0) {
diablo2::d2_game::identify_item(game, player, item);
}
if (quality == 5 && g_item_Set != 0) {
diablo2::d2_game::identify_item(game, player, item);
}
if (quality == 6 && g_item_Rare != 0) {
diablo2::d2_game::identify_item(game, player, item);
}
if (quality == 7 && g_item_Unique != 0) {
diablo2::d2_game::identify_item(game, player, item);
}
if (quality == 8 && g_item_Crafted != 0) {
diablo2::d2_game::identify_item(game, player, item);
}
if (quality == 9 && g_item_Tempered != 0) {
diablo2::d2_game::identify_item(game, player, item);
}
return g_pickup_item_original(game, player, guid, a4);
}
static uint32_t(__fastcall* g_pickup_item_cursor_original)(diablo2::structures::game*,
diablo2::structures::unit*,
uint32_t, uint32_t);
static uint32_t __fastcall pickup_item_cursor(diablo2::structures::game* game,
diablo2::structures::unit* player,
uint32_t guid,
uint32_t a4) {
static auto& instance = singleton<d2_tweaks::server::server>::instance();
if (!game || !player || player->type != diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER)
return g_pickup_item_cursor_original(game, player, guid, a4);
const auto item = instance.get_server_unit(game, guid, diablo2::structures::unit_type_t::UNIT_TYPE_ITEM);
if (!item)
return g_pickup_item_cursor_original(game, player, guid, a4);
auto quality = diablo2::d2_common::get_item_quality(item);
if (quality == 2 && g_item_Normal != 0) {
diablo2::d2_game::identify_item(game, player, item);
}
if (quality == 3 && g_item_Superior != 0) {
diablo2::d2_game::identify_item(game, player, item);
}
if (quality == 4 && g_item_Magic != 0) {
diablo2::d2_game::identify_item(game, player, item);
}
if (quality == 5 && g_item_Set != 0) {
diablo2::d2_game::identify_item(game, player, item);
}
if (quality == 6 && g_item_Rare != 0) {
diablo2::d2_game::identify_item(game, player, item);
}
if (quality == 7 && g_item_Unique != 0) {
diablo2::d2_game::identify_item(game, player, item);
}
if (quality == 8 && g_item_Crafted != 0) {
diablo2::d2_game::identify_item(game, player, item);
}
if (quality == 9 && g_item_Tempered != 0) {
diablo2::d2_game::identify_item(game, player, item);
}
return g_pickup_item_cursor_original(game, player, guid, a4);
}
void d2_tweaks::server::modules::identify_on_pickup::init() {
char acPathToIni[MAX_PATH] = { 0 };
const char* pcIniFile = "\\d2tweaks.ini";
GetCurrentDirectory(MAX_PATH, acPathToIni);
lstrcat(acPathToIni, pcIniFile);
if (GetPrivateProfileInt("modules", "IdentifyOnPickup", 1, acPathToIni) != FALSE) {
hooking::hook(diablo2::d2_game::get_base() + 0x13340, pickup_item, &g_pickup_item_original);
hooking::hook(diablo2::d2_game::get_base() + 0x12B80, pickup_item_cursor, &g_pickup_item_cursor_original);
g_item_Normal = GetPrivateProfileInt("IdentifyOnPickup", "Normal", 1, acPathToIni);
g_item_Superior = GetPrivateProfileInt("IdentifyOnPickup", "Superior", 1, acPathToIni);
g_item_Magic = GetPrivateProfileInt("IdentifyOnPickup", "Magic", 1, acPathToIni);
g_item_Rare = GetPrivateProfileInt("IdentifyOnPickup", "Rare", 1, acPathToIni);
g_item_Set = GetPrivateProfileInt("IdentifyOnPickup", "Set", 1, acPathToIni);
g_item_Unique = GetPrivateProfileInt("IdentifyOnPickup", "Unique", 1, acPathToIni);
g_item_Crafted = GetPrivateProfileInt("IdentifyOnPickup", "Crafted", 1, acPathToIni);
g_item_Tempered = GetPrivateProfileInt("IdentifyOnPickup", "Tempered", 1, acPathToIni);
}
}

View File

@@ -0,0 +1,381 @@
#include <d2tweaks/server/modules/item_drop_message/item_drop_message_server.h>
#include <d2tweaks/server/server.h>
#include <spdlog/spdlog.h>
#include <diablo2/d2game.h>
#include <diablo2/d2common.h>
#include <diablo2/d2lang.h>
#include <diablo2/structures/unit.h>
#include <diablo2/structures/room.h>
#include <diablo2/structures/data/items_line.h>
#include <diablo2/structures/data/item_types_line.h>
#include <diablo2/structures/player_data.h>
#include <diablo2/structures/unit.h>
#include <common/autopickup_lootfilter.h>
MODULE_INIT(item_drop_message)
static const uint32_t m_nCountItemListKeys = 30;
static const uint32_t m_nCountItemTypesKeys = 10;
static uint32_t m_nCountItemListAll = 0;
static char m_aacItemList[m_nCountItemListKeys][MAX_STRING_LENGHT] = { 0 };
static char m_acItemListAll[MAX_STRING_LENGHT * m_nCountItemListKeys] = { 0 };
static char m_acItemListAllTemp[MAX_STRING_LENGHT * m_nCountItemListKeys] = { 0 };
static uint32_t m_nCountItemTypesAll = 0;
static char m_aacItemTypes[m_nCountItemTypesKeys][MAX_STRING_LENGHT] = { 0 };
static char m_acItemTypesAll[MAX_STRING_LENGHT * m_nCountItemTypesKeys] = { 0 };
static char m_acItemTypesAllTemp[MAX_STRING_LENGHT * m_nCountItemTypesKeys] = { 0 };
static char m_acBuffer[1024] = { 0 };
static item_code* m_stItemList;
static item_type* m_stItemTypes;
void d2_tweaks::server::modules::item_drop_message::init() {
uint32_t dwLenght = 0;
char acPathToIni[MAX_PATH] = { 0 };
const char* pcIniFile = "\\d2tweaks.ini";
GetCurrentDirectory(MAX_PATH, acPathToIni);
lstrcat(acPathToIni, pcIniFile);
if (GetPrivateProfileInt("modules", "ItemDropMessage", 1, acPathToIni) != FALSE) {
for (uint32_t i = 0; i < m_nCountItemListKeys; i++) {
sprintf_s(m_acBuffer, sizeof(m_acBuffer), "ItemList%d", i + 1);
dwLenght = GetPrivateProfileString("ItemDropMessage", m_acBuffer, 0, m_aacItemList[i], MAX_STRING_LENGHT - 1, acPathToIni);
if (dwLenght != 0) {
lstrcat(m_acItemListAll, m_aacItemList[i]);
lstrcat(m_acItemListAll, "|");
}
}
for (uint32_t i = 0; i < m_nCountItemTypesKeys; i++) {
sprintf_s(m_acBuffer, sizeof(m_acBuffer), "ItemTypeList%d", i + 1);
dwLenght = GetPrivateProfileString("ItemDropMessage", m_acBuffer, 0, m_aacItemList[i], MAX_STRING_LENGHT - 1, acPathToIni);
if (dwLenght != 0) {
lstrcat(m_acItemTypesAll, m_aacItemTypes[i]);
lstrcat(m_acItemTypesAll, "|");
}
}
/////// Parse ItemCode
dwLenght = lstrlen(m_acItemListAll);
memcpy(m_acItemListAllTemp, m_acItemListAll, dwLenght + 1);
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
char* token_string_itemcode = strtok(m_acItemListAllTemp, " ,|");
while (token_string_itemcode)
{
m_nCountItemListAll++;
token_string_itemcode = strtok(NULL, " ,|");
}
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
m_stItemList = (item_code*)malloc(m_nCountItemListAll * sizeof(item_code));
memset(m_stItemList, 0, m_nCountItemListAll * sizeof(item_code));
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
memcpy(m_acItemListAllTemp, m_acItemListAll, dwLenght + 1);
token_string_itemcode = strtok(m_acItemListAllTemp, " ,|");
for (uint32_t i = 0; token_string_itemcode != 0; i++)
{
uint32_t cur_string_length = lstrlen(token_string_itemcode);
m_stItemList[i].code0 = token_string_itemcode[0];
m_stItemList[i].code1 = token_string_itemcode[1];
m_stItemList[i].code2 = token_string_itemcode[2];
if (token_string_itemcode[3] == ':') {
// first index quality = 1
for (uint32_t q = 1; q <= 9; q++) {
m_stItemList[i].qualityinclude[q] = FALSE;
}
if (cur_string_length <= 13) { //for example jew:123456789
// p = 4 --> this is first digit after ':'
for (uint32_t p = 4; p <= cur_string_length; p++) {
if (token_string_itemcode[p] == 0) {
break;
}
if (token_string_itemcode[p] >= '0' && token_string_itemcode[p] <= '9') {
m_stItemList[i].qualityinclude[token_string_itemcode[p] - 0x30] = TRUE;
}
}
}
}
if (token_string_itemcode[3] == '-') {
// first index quality = 1
for (uint32_t q = 1; q <= 9; q++) {
m_stItemList[i].qualityinclude[q] = TRUE;
}
if (cur_string_length <= 13) { //for example jew:123456789
// p = 4 --> this is first digit after '-'
for (uint32_t p = 4; p <= cur_string_length; p++) {
if (token_string_itemcode[p] == 0) {
break;
}
if (token_string_itemcode[p] >= '0' && token_string_itemcode[p] <= '9') {
m_stItemList[i].qualityinclude[token_string_itemcode[p] - 0x30] = FALSE;
}
}
}
}
if (token_string_itemcode[3] != '-' && token_string_itemcode[3] != ':')
{
// first index quality = 1
for (uint32_t q = 1; q <= 9; q++) {
m_stItemList[i].qualityinclude[q] = TRUE;
}
}
token_string_itemcode = strtok(NULL, " ,|");
}
/// Parse ItemType
dwLenght = lstrlen(m_acItemTypesAll);
memcpy(m_acItemTypesAllTemp, m_acItemTypesAll, dwLenght + 1);
char* token_string_itemtype_code = strtok(m_acItemTypesAllTemp, ",|");
while (token_string_itemtype_code)
{
m_nCountItemTypesAll++;
token_string_itemtype_code = strtok(NULL, ",|");
}
m_stItemTypes = (item_type*)malloc(m_nCountItemTypesAll * sizeof(item_type));
memset(m_stItemTypes, 0, m_nCountItemTypesAll * sizeof(item_type));
memcpy(m_acItemTypesAllTemp, m_acItemTypesAll, dwLenght + 1);
token_string_itemtype_code = strtok(m_acItemTypesAllTemp, ",|");
for (uint32_t i = 0; token_string_itemtype_code != 0; i++)
{
uint32_t cur_itemtypes_string_length = lstrlen(token_string_itemtype_code);
//m_stItemTypes[i].type0 = token_string_itemtype_code[0];
//m_stItemTypes[i].type1 = token_string_itemtype_code[1];
//m_stItemTypes[i].type2 = token_string_itemtype_code[2];
//m_stItemTypes[i].type3 = token_string_itemtype_code[3];
m_stItemTypes[i].dwtype = *(DWORD*)token_string_itemtype_code;
if (token_string_itemtype_code[4] == ':') {
// first index quality = 1
for (uint32_t q = 1; q <= 9; q++) {
m_stItemTypes[i].qualityinclude[q] = FALSE;
}
if (cur_itemtypes_string_length <= 14) { //for example tors:123456789
// p = 5 --> this is first digit after ':'
for (uint32_t p = 5; p <= cur_itemtypes_string_length; p++) {
if (token_string_itemtype_code[p] == 0) {
break;
}
if (token_string_itemtype_code[p] >= '0' && token_string_itemtype_code[p] <= '9') {
m_stItemTypes[i].qualityinclude[token_string_itemtype_code[p] - 0x30] = TRUE;
}
}
}
}
if (token_string_itemtype_code[4] == '-') {
// first index quality = 1
for (uint32_t q = 1; q <= 9; q++) {
m_stItemTypes[i].qualityinclude[q] = TRUE;
}
if (cur_itemtypes_string_length <= 14) { //for example armo:123456789
// p = 5 --> this is first digit after '-'
for (uint32_t p = 5; p <= cur_itemtypes_string_length; p++) {
if (token_string_itemtype_code[p] == 0) {
break;
}
if (token_string_itemtype_code[p] >= '0' && token_string_itemtype_code[p] <= '9') {
m_stItemTypes[i].qualityinclude[token_string_itemtype_code[p] - 0x30] = FALSE;
}
}
}
}
if (token_string_itemtype_code[4] != '-' && token_string_itemtype_code[4] != ':')
{
// first index quality = 1
for (uint32_t q = 1; q <= 9; q++) {
m_stItemTypes[i].qualityinclude[q] = TRUE;
}
}
token_string_itemtype_code = strtok(NULL, ",|");
}
//singleton<server>::instance().register_tick_handler(this);
singleton<server>::instance().register_packet_handler(common::MESSAGE_TYPE_ITEM_DROPPED_INFO, this);
}
}
void d2_tweaks::server::modules::item_drop_message::tick(diablo2::structures::game* game,
diablo2::structures::unit* unit) {
static common::item_pickup_info_sc packet;
static auto& instance = singleton<d2_tweaks::server::server>::instance();
if (!game || !unit)
return;
if (unit->type != diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER)
return;
}
bool d2_tweaks::server::modules::item_drop_message::handle_packet(diablo2::structures::game* game, diablo2::structures::unit* player, common::packet_header* packet) {
const auto income_item_dropped_packet = static_cast<common::item_dropped_info_cs*>(packet);
static common::item_dropped_info_sc response_item_dropped_packet;
switch (income_item_dropped_packet->message_type)
{
case common::message_types_t::MESSAGE_TYPE_ITEM_DROPPED_INFO:
auto current_unit = diablo2::d2_game::get_server_unit(game, diablo2::structures::unit_type_t::UNIT_TYPE_ITEM, income_item_dropped_packet->item_id);
if (current_unit != 0) {
if (current_unit->type == diablo2::structures::unit_type_t::UNIT_TYPE_ITEM) {
if (current_unit->item_data != 0) {
auto record = diablo2::d2_common::get_item_record(current_unit->data_record_index);
uint32_t quality = diablo2::d2_common::get_item_quality(current_unit);
wchar_t* string_wc = diablo2::d2_lang::get_string_from_index(record->name_str);
static char string_mb[260] = { 0 };
const auto itemtype_record = diablo2::d2_common::get_item_type_record(record->type);
auto itemtype_record_equiv1 = diablo2::d2_common::get_item_type_record(itemtype_record->equiv1);
auto itemtype_record_equiv2 = diablo2::d2_common::get_item_type_record(itemtype_record->equiv2);
wcstombs(string_mb, string_wc, 256);
memset(response_item_dropped_packet.arr_itemtype_codestr_equivstr, 0, sizeof response_item_dropped_packet.arr_itemtype_codestr_equivstr);
// itemtype code <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
*(DWORD*)response_item_dropped_packet.arr_itemtype_codestr_equivstr[0] = *(DWORD*)itemtype_record->code;
// index second code in array
uint32_t index_arr_itemtype = 1;
if (itemtype_record_equiv1) {
//
if (*(DWORD*)itemtype_record_equiv1->code != 0x20202020) {
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> equiv1
*(DWORD*)response_item_dropped_packet.arr_itemtype_codestr_equivstr[index_arr_itemtype] = *(DWORD*)itemtype_record_equiv1->code;
index_arr_itemtype++;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> eqiv1 <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
for (index_arr_itemtype; itemtype_record_equiv1->equiv1 != 0; index_arr_itemtype++) {
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
itemtype_record_equiv1 = diablo2::d2_common::get_item_type_record(itemtype_record_equiv1->equiv1);
if (*(DWORD*)itemtype_record_equiv1->code != 0x20202020) {
*(DWORD*)response_item_dropped_packet.arr_itemtype_codestr_equivstr[index_arr_itemtype] = *(DWORD*)itemtype_record_equiv1->code;
}
else break;
}
}
}
if (itemtype_record_equiv2) {
if (*(DWORD*)itemtype_record_equiv2->code != 0x20202020) {
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> equiv1
*(DWORD*)response_item_dropped_packet.arr_itemtype_codestr_equivstr[index_arr_itemtype] = *(DWORD*)itemtype_record_equiv2->code;
index_arr_itemtype++;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD> eqiv1 <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
for (index_arr_itemtype; itemtype_record_equiv2->equiv2 != 0; index_arr_itemtype++) {
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
itemtype_record_equiv2 = diablo2::d2_common::get_item_type_record(itemtype_record_equiv2->equiv2);
if (*(DWORD*)itemtype_record_equiv2->code != 0x20202020) {
*(DWORD*)response_item_dropped_packet.arr_itemtype_codestr_equivstr[index_arr_itemtype] = *(DWORD*)itemtype_record_equiv2->code;
}
else break;
}
}
}
if (GetKeyState(VK_SCROLL) != 0) {
response_item_dropped_packet.item = income_item_dropped_packet->item_id;
response_item_dropped_packet.code[0] = record->string_code[0];
response_item_dropped_packet.code[1] = record->string_code[1];
response_item_dropped_packet.code[2] = record->string_code[2];
// <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> itemtypes
response_item_dropped_packet.index_arr_itemtype = index_arr_itemtype;
response_item_dropped_packet.quality = quality;
response_item_dropped_packet.showthis = TRUE;
memcpy(response_item_dropped_packet.namestr, string_mb, 128);
//spdlog::debug("[item_drop_message_s] Code={} Type.Code={} Type.Equiv1={} Type.Equiv2={}", response_item_dropped_packet.code, response_item_dropped_packet.itemtype_code, response_item_dropped_packet.itemtype_equiv1, response_item_dropped_packet.itemtype_equiv2);
singleton<server>::instance().send_packet(player->player_data->net_client, &response_item_dropped_packet, sizeof response_item_dropped_packet);
break;
}
for (uint32_t i = 0; i < m_nCountItemTypesAll; i++)
{
for (uint32_t count = 0; count < index_arr_itemtype; count++)
{
if (*(DWORD*)response_item_dropped_packet.arr_itemtype_codestr_equivstr[count] == (DWORD)m_stItemTypes[i].dwtype)
{
if (m_stItemTypes[i].qualityinclude[quality] == TRUE)
{
response_item_dropped_packet.item = income_item_dropped_packet->item_id;
response_item_dropped_packet.code[0] = record->string_code[0];
response_item_dropped_packet.code[1] = record->string_code[1];
response_item_dropped_packet.code[2] = record->string_code[2];
response_item_dropped_packet.index_arr_itemtype = index_arr_itemtype;
response_item_dropped_packet.quality = quality;
response_item_dropped_packet.showthis = TRUE;
memcpy(response_item_dropped_packet.namestr, string_mb, 128);
//spdlog::debug("[item_drop_message_s_itemtypes] Code={} Type.Code={} Type.Equiv1={} Type.Equiv2={}", response_item_dropped_packet.code, response_item_dropped_packet.itemtype_code, response_item_dropped_packet.itemtype_equiv1, response_item_dropped_packet.itemtype_equiv2);
singleton<server>::instance().send_packet(player->player_data->net_client, &response_item_dropped_packet, sizeof response_item_dropped_packet);
goto L1;
}
else {
response_item_dropped_packet.showthis = FALSE;
}
}
}
}
for (uint32_t i = 0; i < m_nCountItemListAll; i++)
{
if (record->string_code[0] == m_stItemList[i].code0 &&
record->string_code[1] == m_stItemList[i].code1 &&
record->string_code[2] == m_stItemList[i].code2)
{
if (m_stItemList[i].qualityinclude[quality] == TRUE)
{
response_item_dropped_packet.item = income_item_dropped_packet->item_id;
response_item_dropped_packet.code[0] = record->string_code[0];
response_item_dropped_packet.code[1] = record->string_code[1];
response_item_dropped_packet.code[2] = record->string_code[2];
response_item_dropped_packet.index_arr_itemtype = index_arr_itemtype;
response_item_dropped_packet.quality = quality;
response_item_dropped_packet.showthis = TRUE;
memcpy(response_item_dropped_packet.namestr, string_mb, 128);
//spdlog::debug("[item_drop_message_s] Code={} Type.Code={} Type.Equiv1={} Type.Equiv2={}", response_item_dropped_packet.code, response_item_dropped_packet.itemtype_code, response_item_dropped_packet.itemtype_equiv1, response_item_dropped_packet.itemtype_equiv2);
singleton<server>::instance().send_packet(player->player_data->net_client, &response_item_dropped_packet, sizeof response_item_dropped_packet);
}
}
else {
response_item_dropped_packet.showthis = FALSE;
}
}
L1:;
}
}
}
break;
}
return true;
}

View File

@@ -0,0 +1,88 @@
#include <d2tweaks/server/modules/item_move/item_move_server.h>
#include <d2tweaks/server/server.h>
#include <diablo2/d2net.h>
#include <diablo2/d2game.h>
#include <diablo2/d2common.h>
#include <diablo2/structures/unit.h>
#include <diablo2/structures/game.h>
#include <diablo2/structures/net_client.h>
#include <diablo2/structures/item_data.h>
#include <diablo2/structures/player_data.h>
MODULE_INIT(item_move)
void d2_tweaks::server::modules::item_move::init() {
char acPathToIni[MAX_PATH] = { 0 };
const char* pcIniFile = "\\d2tweaks.ini";
GetCurrentDirectory(MAX_PATH, acPathToIni);
lstrcat(acPathToIni, pcIniFile);
if (GetPrivateProfileInt("modules", "ItemMover", 1, acPathToIni) != FALSE) {
singleton<server>::instance().register_packet_handler(common::MESSAGE_TYPE_ITEM_MOVE, this);
}
}
bool d2_tweaks::server::modules::item_move::handle_packet(diablo2::structures::game* game,
diablo2::structures::unit* player, common::packet_header* packet) {
static common::item_move_sc resp;
static auto& instance = singleton<server>::instance();
const auto itemMove = static_cast<common::item_move_cs*>(packet);
const auto item = instance.get_server_unit(game, itemMove->item_guid, diablo2::structures::unit_type_t::UNIT_TYPE_ITEM); //0x4 = item
if (item == nullptr)
return true; //block further packet processing
const auto inventoryIndex = diablo2::d2_common::get_inventory_index(player, itemMove->target_page, game->item_format == 101);
uint32_t tx, ty;
if (!find_free_space(player->inventory, item, inventoryIndex, itemMove->target_page, tx, ty))
return true; //block further packet processing
//diablo2::d2_common::set_unit_mode(item, 0); // mode 4 - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, mode 0 - <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD>
item->item_data->page = itemMove->target_page;
diablo2::d2_common::inv_add_item(player->inventory, item, tx, ty, inventoryIndex, false, item->item_data->page);
diablo2::d2_common::inv_update_item(player->inventory, item, false);
diablo2::d2_game::update_inventory_items(game, player);
//send update packet
resp.item_guid = itemMove->item_guid;
resp.target_page = itemMove->target_page;
resp.tx = tx;
resp.ty = ty;
const auto client = player->player_data->net_client;
diablo2::d2_net::send_to_client(1, client->client_id, &resp, sizeof resp);
return true;
}
bool d2_tweaks::server::modules::item_move::find_free_space(diablo2::structures::inventory* inv,
diablo2::structures::unit* item, int32_t inventoryIndex, char page, uint32_t& x, uint32_t& y) {
char data[0x18];
diablo2::d2_common::get_inventory_data(inventoryIndex, 0, data);
const auto mx = static_cast<uint32_t>(data[0]);
const auto my = static_cast<uint32_t>(data[1]);
for (x = 0; x < mx; x++) {
for (y = 0; y < my; y++) {
diablo2::structures::unit* blockingUnit = nullptr;
uint32_t blockingUnitIndex = 0;
if (diablo2::d2_common::can_put_into_slot(inv, item, x, y, inventoryIndex, &blockingUnit, &blockingUnitIndex, page))
return true;
}
}
return false;
}

View File

@@ -0,0 +1,15 @@
#include <d2tweaks/server/modules/server_module.h>
#include <d2tweaks/server/server.h>
d2_tweaks::server::modules::server_module::server_module() {
singleton<server>::instance().register_module(this);
}
bool d2_tweaks::server::modules::server_module::handle_packet(diablo2::structures::game* game,
diablo2::structures::unit* player, common::packet_header* packet) {
return false;
}
void d2_tweaks::server::modules::server_module::tick(diablo2::structures::game* game,
diablo2::structures::unit* unit) {}

View File

@@ -0,0 +1,49 @@
#include <d2tweaks/server/modules/test/test.h>
#include <common/hooking.h>
#include <diablo2/d2common.h>
#include <spdlog/spdlog.h>
#include <diablo2/structures/unit.h>
#include <diablo2/d2game.h>
#include <diablo2/structures/game.h>
MODULE_INIT(test)
static int(__stdcall* g_set_stat_original)(diablo2::structures::unit* unit,
diablo2::unit_stats_t stat,
uint32_t value, int16_t param);
static int __stdcall set_stat(diablo2::structures::unit* unit,
diablo2::unit_stats_t stat,
uint32_t value, int16_t param) {
if (unit->type == diablo2::structures::unit_type_t::UNIT_TYPE_PLAYER &&
stat == diablo2::UNIT_STAT_GOLD) {
spdlog::debug("Setting UNIT_STAT_GOLD stat!");
}
return g_set_stat_original(unit, stat, value, param);
}
static int32_t(__stdcall* g_set_stat_in_list_original)(void*, diablo2::unit_stats_t stat,
uint32_t value, uint32_t param);
static int32_t __stdcall set_stat_in_list(void* a1, diablo2::unit_stats_t stat, uint32_t value, uint32_t param) {
if (stat == diablo2::UNIT_STAT_GOLD) {
spdlog::debug("Setting UNIT_STAT_GOLD stat!");
}
return g_set_stat_in_list_original(a1, stat, value, param);
}
static int(__fastcall* g_regen_tick_original)(diablo2::structures::game*, diablo2::structures::unit*, int32_t, int32_t);
static int __fastcall regen_tick(diablo2::structures::game* game, diablo2::structures::unit* unit, int32_t a3, int32_t a4) {
return g_regen_tick_original(game, unit, a3, a4);
}
void d2_tweaks::server::modules::test::init() {
return;
hooking::hook<10463>(diablo2::d2_common::get_base(), set_stat_in_list, &g_set_stat_in_list_original);
hooking::hook<10517>(diablo2::d2_common::get_base(), set_stat, &g_set_stat_original);
hooking::hook(diablo2::d2_game::get_base() + 0x50F80, regen_tick, &g_regen_tick_original);
}

View File

@@ -0,0 +1,233 @@
#include <d2tweaks/server/modules/trader_update/trader_update_server.h>
#include <d2tweaks/server/server.h>
#include <d2tweaks/common/protocol.h>
#include <spdlog/spdlog.h>
#include <diablo2/d2game.h>
#include <diablo2/d2common.h>
#include <diablo2/d2lang.h>
#include <diablo2/d2net.h>
#include <diablo2/structures/unit.h>
#include <diablo2/structures/room.h>
#include <diablo2/structures/data/items_line.h>
#include <diablo2/structures/data/item_types_line.h>
#include <diablo2/structures/player_data.h>
#include <diablo2/structures/unit.h>
#include <diablo2/structures/inventory.h>
#include <diablo2/structures/item_data.h>
#include <diablo2/structures/net_client.h>
#include <diablo2/structures/path.h>
#include <diablo2/structures/game.h>
#include <diablo2/structures/unit.h>
#include <diablo2/structures/player_data.h>
#include <diablo2/structures/npc_record.h>
#include <common/autopickup_lootfilter.h>
#include <common/ini.h>
#include <common/hooking.h>
#include <DllNotify.h>
#include <D2Template.h>
MODULE_INIT(trader_update)
enum trader_command {
COMMAND_NULL,
COMMAND_FREE_NPC_INVENTORY,
COMMAND_FREE_NPC_GAMBLE,
COMMAND_FILL_NPC_INVENTORY,
COMMAND_FILL_NPC_GAMBLE,
COMMAND_FINISHED,
COMMAND_ABORT
};
static uint32_t m_nNpcId;
static bool m_bGamble;
static uint32_t m_nParam4 = 1;
//void* g_click_trade_menu;
//void* g_click_gamble_menu;
//void* g_ret;
//uint32_t gamble_or_trade; //gamble == 2, trade == 1
//__declspec (naked) void click_menu() {
//__asm {
// pushad;
// pushfd;
// mov eax, [esp + 0x2C];//+0x8
// mov [m_nNpcId], eax;
// mov eax, DLLBASE_D2GAME;
// add eax, 0x99F4A;
// mov [g_ret], eax;
// popfd;
// popad;
//
// mov eax, dword ptr ss : [esp + 0x8]
// sub esp, 0x10C
// jmp[g_ret];
// }
//}
//
//static const DLLPatchStrc gpt_click_menu[] =
//{
// // d2game:$0x99F40
// {D2DLL_D2GAME, 0x99F40 + 0, PATCH_JMP, FALSE, 0x1},
// {D2DLL_D2GAME, 0x99F40 + 1, (DWORD)click_menu, TRUE, 0x0},
// {D2DLL_D2GAME, 0x99F40 + 5, (DWORD)PATCH_NOPBLOCK, FALSE, 0x5},
// {D2DLL_INVALID}
//};
//
//
//__declspec (naked) void click_trade_menu() {
// __asm {
// pushad; //+0x24 esp
// pushfd; //
// //mov eax, [esp + 0x28];//+0x4
// //mov [m_nNpcId], eax;
// mov eax, [esp + 0x34];//+0x10
// mov [m_nParam4], eax;
// mov [m_bGamble], FALSE
// mov eax, DLLBASE_D2GAME;
// add eax, 0x9A086;
// mov [g_ret], eax;
// popfd;
// popad;
// // <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
// mov edx, dword ptr [esp + 0x10];
// mov ecx, edi;
// jmp [g_ret];
// }
//}
//
//__declspec (naked) void click_gamble_menu() {
// __asm {
// pushad; //+0x24 esp
// pushfd; //
// //mov eax, [esp + 0x28];//+0x4
// //mov [m_nNpcId], eax;
// mov eax, [esp + 0x34];//+0x10
// mov [m_nParam4], eax;
// mov [m_bGamble], TRUE
// mov eax, DLLBASE_D2GAME;
// add eax, 0x9A0F0;
// mov [g_ret], eax;
// popfd;
// popad;
// mov eax, dword ptr [esp + 0x10] ;
// mov edx, esi;
// jmp[g_ret];
// }
//}
//
//
//static const DLLPatchStrc gpt_click_trade_menu[] =
//{
// {D2DLL_D2GAME, 0x9A080 + 0, PATCH_JMP, FALSE, 0x1},
// {D2DLL_D2GAME, 0x9A080 + 1, (DWORD)click_trade_menu, TRUE, 0x0},
// {D2DLL_D2GAME, 0x9A080 + 5, (DWORD)PATCH_NOPBLOCK, FALSE, 0x1},
// {D2DLL_INVALID}
//};
//
//static const DLLPatchStrc gpt_click_gamble_menu[] =
//{
// {D2DLL_D2GAME, 0x9A0EA + 0, PATCH_JMP, FALSE, 0x1},
// {D2DLL_D2GAME, 0x9A0EA + 1, (DWORD)click_gamble_menu, TRUE, 0x0},
// {D2DLL_D2GAME, 0x9A0EA + 5, (DWORD)PATCH_NOPBLOCK, FALSE, 0x1},
// {D2DLL_INVALID}
//};
void d2_tweaks::server::modules::trader_update::init() {
char szDir[MAX_PATH];
char szPath[MAX_PATH];
const char szConfig[] = "d2tweaks.ini";
GetCurrentDirectory(MAX_PATH, szDir);
snprintf(szPath, MAX_PATH, "%s\\%s", szDir, szConfig);
CIni config(szPath);
if (config.GetInt("modules", "ReloadTradeGamble", 1) != FALSE) {
singleton<server>::instance().register_packet_handler(common::MESSAGE_TYPE_TRADER_UPDATE, this);
//D2TEMPLATE_ApplyPatch(gpt_click_trade_menu);
//D2TEMPLATE_ApplyPatch(gpt_click_gamble_menu);
//D2TEMPLATE_ApplyPatch(gpt_click_menu);
}
}
void d2_tweaks::server::modules::trader_update::tick(diablo2::structures::game* game, diablo2::structures::unit* unit) {
return;
}
struct ClientFromNumber {
uint8_t padding[0x174];
diablo2::structures::net_client* net_cleint;
};
bool d2_tweaks::server::modules::trader_update::handle_packet(diablo2::structures::game* game, diablo2::structures::unit* player, common::packet_header* packet) {
const auto income_packet_cs = static_cast<common::trader_update_cs*>(packet);
static common::trader_update_sc response_packet_sc;
diablo2::structures::unit* temp_ptNPC = diablo2::d2_game::get_server_unit(game, diablo2::structures::unit_type_t::UNIT_TYPE_MONSTER, income_packet_cs->npc_id);
diablo2::structures::npc_record* npcrecord = diablo2::d2_game::get_npc_record(game, temp_ptNPC, &temp_ptNPC);
diablo2::structures::unit* ptNPC = diablo2::d2_game::get_server_unit(game, diablo2::structures::unit_type_t::UNIT_TYPE_MONSTER, income_packet_cs->npc_id);
if (!ptNPC)
return true;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
if (income_packet_cs->command == COMMAND_FREE_NPC_INVENTORY) {
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
// id net client - 1 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, 3, 5, 7, 9, 11, 13, 15
for (uint32_t i = 0; i < 16; i++) {
diablo2::structures::net_client* netclient = diablo2::d2_game::get_net_client_from_id(game, i);
if (netclient != 0) {
response_packet_sc.command = COMMAND_FREE_NPC_INVENTORY;
response_packet_sc.npc_id = income_packet_cs->npc_id;
response_packet_sc.is_gamble_menu_open = income_packet_cs->is_gamble_menu_open;
singleton<server>::instance().send_packet(netclient, &response_packet_sc, sizeof response_packet_sc);
}
}
}
if (income_packet_cs->command == COMMAND_FREE_NPC_GAMBLE) {
npcrecord->pGamble->pInventory = 0;
npcrecord->pGamble->dwGUID = 0;
npcrecord->pGamble = 0;
response_packet_sc.command = COMMAND_FREE_NPC_GAMBLE;
response_packet_sc.npc_id = income_packet_cs->npc_id;
response_packet_sc.is_gamble_menu_open = income_packet_cs->is_gamble_menu_open;
singleton<server>::instance().send_packet(player->player_data->net_client, &response_packet_sc, sizeof response_packet_sc);
}
if (income_packet_cs->command == COMMAND_FILL_NPC_INVENTORY) {
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
npcrecord->bRefreshInventory = true;
diablo2::d2_game::create_vendor_cache1(game, player, ptNPC, 1, false);
npcrecord->bRefreshInventory = false;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
// id net client - 1 <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD>, <20><><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>, 3, 5, 7, 9, 11, 13, 15
for (uint32_t i = 0; i < 16; i++) {
diablo2::structures::net_client* netclient = diablo2::d2_game::get_net_client_from_id(game, i);
if (netclient != 0) {
response_packet_sc.command = COMMAND_FILL_NPC_INVENTORY;
response_packet_sc.npc_id = income_packet_cs->npc_id;
response_packet_sc.is_gamble_menu_open = income_packet_cs->is_gamble_menu_open;
singleton<server>::instance().send_packet(netclient, &response_packet_sc, sizeof response_packet_sc);
}
}
}
if (income_packet_cs->command == COMMAND_FILL_NPC_GAMBLE) {
diablo2::d2_game::create_vendor_cache1(game, player, ptNPC, 1, true);
response_packet_sc.command = COMMAND_FILL_NPC_GAMBLE;
response_packet_sc.npc_id = income_packet_cs->npc_id;
response_packet_sc.is_gamble_menu_open = income_packet_cs->is_gamble_menu_open;
singleton<server>::instance().send_packet(player->player_data->net_client, &response_packet_sc, sizeof response_packet_sc);
}
return true;
}

View File

@@ -0,0 +1,160 @@
#include <d2tweaks/server/modules/transmute/transmute_server.h>
#include <d2tweaks/server/server.h>
#include <d2tweaks/common/protocol.h>
#include <spdlog/spdlog.h>
#include <diablo2/d2game.h>
#include <diablo2/d2common.h>
#include <diablo2/d2lang.h>
#include <diablo2/d2net.h>
#include <diablo2/structures/unit.h>
#include <diablo2/structures/room.h>
#include <diablo2/structures/data/items_line.h>
#include <diablo2/structures/data/item_types_line.h>
#include <diablo2/structures/player_data.h>
#include <diablo2/structures/unit.h>
#include <diablo2/structures/inventory.h>
#include <diablo2/structures/item_data.h>
#include <diablo2/structures/net_client.h>
#include <diablo2/structures/path.h>
#include <diablo2/structures/game.h>
#include <diablo2/structures/unit.h>
#include <diablo2/structures/player_data.h>
#include <common/autopickup_lootfilter.h>
MODULE_INIT(transmute)
enum transmute_command {
COMMAND_NULL,
COMMAND_TRANSMUTE_START,
COMMAND_TRANSMUTE_END,
COMMAND_CALL_TRANSMUTE,
COMMAND_MOVE_ITEM,
COMMAND_ABORT
};
void d2_tweaks::server::modules::transmute::init() {
char PathToIni[MAX_PATH] = { 0 };
const char IniFile[] = "\\d2tweaks.ini";
GetCurrentDirectory(MAX_PATH, PathToIni);
lstrcat(PathToIni, IniFile);
if (GetPrivateProfileInt("modules", "AutoTransmute", 0, PathToIni) != FALSE) {
//singleton<server>::instance().register_tick_handler(this);
singleton<server>::instance().register_packet_handler(common::MESSAGE_TYPE_TRANSMUTE, this);
}
}
// transmute function d2game
// topfunc d2game:$62130 int __fastcall CRAFT_Transmogrify(D2GameStrc* pGame, D2UnitStrc* pPlayer)
// subfunc d2game:$0x60010 int __fastcall CRAFT_Transmogrify(D2GameStrc* pGame, D2UnitStrc* pPlayer, D2CubemainTXT* pCubeTxt, void* pUnknown)
void d2_tweaks::server::modules::transmute::tick(diablo2::structures::game* game, diablo2::structures::unit* unit) {
return;
}
inline uint64_t TimeStart() {
_asm {
cpuid
rdtsc
}
}
inline uint64_t TimeEnd() {
_asm {
rdtsc
}
}
bool d2_tweaks::server::modules::transmute::handle_packet(diablo2::structures::game* game, diablo2::structures::unit* player, common::packet_header* packet) {
const auto income_packet_cs = static_cast<common::transmute_info_cs*>(packet);
static common::transmute_info_sc response_packet_sc;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
if (income_packet_cs->command == COMMAND_CALL_TRANSMUTE) {
#ifndef NDEBUG
uint64_t time = TimeStart();
#endif
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> item, <20><><EFBFBD> getmaxcuberecipes <20><><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
if (diablo2::d2_game::transmogrify(game, player) == diablo2::d2_common::get_max_cube_recipes()) {
response_packet_sc.command = COMMAND_ABORT;
singleton<server>::instance().send_packet(player->player_data->net_client, &response_packet_sc, sizeof response_packet_sc);
//diablo2::d2_net::send_to_client(1, client->client_id, &response_packet_sc, sizeof response_packet_sc);
}
#ifndef NDEBUG
time = (TimeEnd() - time);
spdlog::debug("Time {}", time);
#endif
}
if (income_packet_cs->command == COMMAND_MOVE_ITEM) {
d2_tweaks::server::modules::transmute::move_item_to(game, player, packet);
}
return true;
}
bool d2_tweaks::server::modules::transmute::move_item_to(diablo2::structures::game* game, diablo2::structures::unit* player, common::packet_header* packet) {
static common::transmute_info_sc resp;
static auto& instance = singleton<server>::instance();
const auto itemMove = static_cast<common::transmute_info_cs*>(packet);
const auto item = instance.get_server_unit(game, itemMove->item_guid, diablo2::structures::unit_type_t::UNIT_TYPE_ITEM); //0x4 = item
if (item == nullptr)
return true; //block further packet processing
const auto inventoryIndex = diablo2::d2_common::get_inventory_index(player, itemMove->target_page, game->item_format == 101);
uint32_t tx, ty;
if (!find_free_space(player->inventory, item, inventoryIndex, itemMove->target_page, tx, ty))
return true; //block further packet processing
item->item_data->page = itemMove->target_page;
diablo2::d2_common::inv_add_item(player->inventory, item, tx, ty, inventoryIndex, false, item->item_data->page);
diablo2::d2_common::inv_update_item(player->inventory, item, false);
diablo2::d2_game::update_inventory_items(game, player);
//send update packet
resp.command = COMMAND_MOVE_ITEM;
resp.item_guid = itemMove->item_guid;
resp.target_page = itemMove->target_page;
resp.tx = tx;
resp.ty = ty;
const auto client = player->player_data->net_client;
//diablo2::d2_net::send_to_client(1, client->client_id, &resp, sizeof resp);
singleton<server>::instance().send_packet(player->player_data->net_client, &resp, sizeof resp);
return true;
}
bool d2_tweaks::server::modules::transmute::find_free_space(diablo2::structures::inventory* inv,
diablo2::structures::unit* item, int32_t inventoryIndex, char page, uint32_t& x, uint32_t& y) {
char data[0x18];
diablo2::d2_common::get_inventory_data(inventoryIndex, 0, data);
const auto mx = static_cast<uint32_t>(data[0]);
const auto my = static_cast<uint32_t>(data[1]);
for (x = 0; x < mx; x++) {
for (y = 0; y < my; y++) {
diablo2::structures::unit* blockingUnit = nullptr;
uint32_t blockingUnitIndex = 0;
if (diablo2::d2_common::can_put_into_slot(inv, item, x, y, inventoryIndex, &blockingUnit, &blockingUnitIndex, page))
return true;
}
}
return false;
}

View File

@@ -0,0 +1,228 @@
#include <d2tweaks/server/server.h>
#include <Windows.h>
#include <spdlog/spdlog.h>
#include <d2tweaks/common/protocol.h>
#include <diablo2/d2game.h>
#include <diablo2/d2net.h>
#include <diablo2/structures/game.h>
#include <diablo2/structures/unit.h>
#include <diablo2/structures/net_client.h>
#include <d2tweaks/common/common.h>
#include <d2tweaks/server/modules/server_module.h>
#include <common/hooking.h>
#include <D2Template.h>
static int32_t(__fastcall* g_get_incoming_packet_info_original)(d2_tweaks::common::packet_header* data, unsigned int dataSize, size_t* packetSizeOut, size_t* someOffset, int* packetGroup, int32_t* a6, int a7, int a8);
static int32_t(__fastcall* g_handle_packet_original)(diablo2::structures::game* game, diablo2::structures::unit* player, d2_tweaks::common::packet_header* data, size_t size);
static int32_t(__fastcall* g_net_tick_original)(diablo2::structures::game*, diablo2::structures::unit*, int32_t, int32_t);
//returns some kind of processing type (i.e. resultGroup == 0x04 means drop packet)
static int32_t __fastcall get_incoming_packet_info(d2_tweaks::common::packet_header* data, unsigned int dataSize, size_t* packetSizeOut, size_t* someOffset, int* packetGroup, int32_t* a6, int a7, int a8) {
static d2_tweaks::common::packet_header dummy;
static auto& instance = singleton<d2_tweaks::common::common>::instance();
const auto resultGroup = g_get_incoming_packet_info_original(data, dataSize, packetSizeOut, someOffset, packetGroup, a6, a7, a8);
if (data->d2_packet_type == dummy.d2_packet_type && resultGroup == 0x04) { //0x04 - drop packet
size_t size;
if (!instance.get_packet_size_cs(data, size))
return resultGroup;
*packetSizeOut = size;
*packetGroup = 1;
*a6 = 100;
return 2;
}
return resultGroup;
}
static int32_t __fastcall handle_packet(diablo2::structures::game* game, diablo2::structures::unit* player, d2_tweaks::common::packet_header* data, size_t size) {
static d2_tweaks::common::packet_header dummy;
static auto& instance = singleton<d2_tweaks::server::server>::instance();
if (data->d2_packet_type == dummy.d2_packet_type) {
if (!instance.handle_packet(game, player, data))
return g_handle_packet_original(game, player, data, size);
return 1;
}
return g_handle_packet_original(game, player, data, size);
}
d2_tweaks::server::server::server(token) {
m_module_id_counter = 0;
m_tick_handler_id_counter = 0;
}
static int32_t g_ebp_send_to_client;
void __fastcall hook_sc_packet_before_sent(uint32_t* client_strc, d2_tweaks::common::packet_header* packet, size_t size) {
#ifndef NDEBUG
__asm {
push [ebp + 0x30];
pop [g_ebp_send_to_client];
}
if (size == -1)
return;
spdlog::error("[d2game SEND] Packet {} Message {} Size {} CallFrom {}", (void*)packet->d2_packet_type, (void*)packet->message_type, size, (void*)g_ebp_send_to_client);
#endif
return;
}
__declspec (naked) void hook_sc_packet_before_sent_wrapper() {
__asm {
pushad;
pushfd;
push [esp+0x28]
call [hook_sc_packet_before_sent]
popfd;
popad;
// <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
push ecx
push ebp
push esi
mov esi, ecx
}
RET_TO_RVA(DLLBASE_D2GAME, 0xC715);
}
// d2game:$0xC710
static const DLLPatchStrc gpt_hook_sc_packet_before_sent[] =
{
{D2DLL_D2GAME, 0xC710 + 0, PATCH_JMP, FALSE, 0x1},
{D2DLL_D2GAME, 0xC710 + 1, (DWORD)hook_sc_packet_before_sent_wrapper, TRUE, 0x0},
{D2DLL_INVALID}
};
void d2_tweaks::server::server::init() {
hooking::hook(reinterpret_cast<void*>(diablo2::d2_game::get_base() + 0x59320), ::handle_packet, &g_handle_packet_original);
hooking::hook(reinterpret_cast<void*>(diablo2::d2_game::get_base() + 0x50F80), net_tick, &g_net_tick_original);
hooking::hook(reinterpret_cast<void*>(diablo2::d2_net::get_base() + 0x1FE0), get_incoming_packet_info, &g_get_incoming_packet_info_original);
//D2TEMPLATE_ApplyPatch(gpt_hook_sc_packet_before_sent);
//disable outgoing packet type checks
DWORD oldProtect;
if (VirtualProtect(diablo2::d2_net::get_base() + 0x18B2, 0x02, PAGE_EXECUTE_READWRITE, &oldProtect))
*reinterpret_cast<uint16_t*>(diablo2::d2_net::get_base() + 0x18B2) = 0xBC3C;
if (VirtualProtect(diablo2::d2_net::get_base() + 0x18D0, 0x02, PAGE_EXECUTE_READWRITE, &oldProtect))
*reinterpret_cast<uint16_t*>(diablo2::d2_net::get_base() + 0x18D0) = 0xBC3C;
for (size_t i = 0; i < sizeof m_modules / sizeof(void*); i++) {
if (m_modules[i] == nullptr)
break;
m_modules[i]->init();
}
}
void d2_tweaks::server::server::send_packet(diablo2::structures::net_client* client, common::packet_header* packet, size_t size) {
diablo2::d2_game::enqueue_packet(client, packet, size);
}
bool d2_tweaks::server::server::handle_packet(diablo2::structures::game* game,
diablo2::structures::unit* player,
common::packet_header* packet) {
auto handler = m_packet_handlers[packet->message_type];
if (!handler)
return false;
return handler->handle_packet(game, player, packet);
}
void d2_tweaks::server::server::register_module(modules::server_module* module) {
m_modules[m_module_id_counter++] = module;
}
void d2_tweaks::server::server::register_tick_handler(modules::server_module* module) {
m_tick_handlers[m_tick_handler_id_counter++] = module;
}
void d2_tweaks::server::server::register_packet_handler(common::message_types_t type, modules::server_module* module) {
if (m_packet_handlers[type] != nullptr) {
spdlog::warn("Serverside packet handler for {0} is already registered!", type);
}
m_packet_handlers[type] = module;
}
diablo2::structures::unit* d2_tweaks::server::server::get_server_unit(diablo2::structures::game* game, uint32_t guid, diablo2::structures::unit_type_t type) {
if (game == nullptr)
return nullptr;
auto typeIndex = static_cast<uint32_t>(type);
if (type == diablo2::structures::unit_type_t::UNIT_TYPE_MISSILE)
typeIndex = 4;
if (type == diablo2::structures::unit_type_t::UNIT_TYPE_ITEM)
typeIndex = 3;
const auto index = guid & 127;
auto result = game->unit_list[typeIndex][index];
while (result != nullptr && result->guid != guid) {
result = result->prev_unit;
}
return result;
}
void d2_tweaks::server::server::iterate_server_units(diablo2::structures::game* game, diablo2::structures::unit_type_t type,
const std::function<bool(diablo2::structures::unit*)>& cb) {
if (!cb)
return;
if (!game)
return;
auto typeIndex = static_cast<uint32_t>(type);
if (type == diablo2::structures::unit_type_t::UNIT_TYPE_MISSILE)
typeIndex = 4;
if (type == diablo2::structures::unit_type_t::UNIT_TYPE_ITEM)
typeIndex = 3;
for (size_t index = 0; index < 128; index++) {
auto unit = game->unit_list[typeIndex][index];
while (unit != nullptr) {
if (!cb(unit))
return;
unit = unit->prev_unit;
}
}
}
int32_t d2_tweaks::server::server::net_tick(diablo2::structures::game* game, diablo2::structures::unit* unit, int32_t a3, int32_t a4) {
static auto& instance = singleton<server>::instance();
for (size_t i = 0; i < sizeof instance.m_modules / sizeof(void*); i++) {
if (instance.m_tick_handlers[i] == nullptr)
break;
instance.m_tick_handlers[i]->tick(game, unit);
}
return g_net_tick_original(game, unit, a3, a4);
}

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;
}