diff --git a/usermode/UpgradeLog.htm b/usermode/UpgradeLog.htm new file mode 100644 index 0000000..2e1e3b4 --- /dev/null +++ b/usermode/UpgradeLog.htm @@ -0,0 +1,273 @@ + + + + Raport migracji +

+ Raport migracji -

Przegląd

ProjektŚcieżkaBłędyOstrzeżeniaKomunikaty
usermodeusermode.vcxproj100
Rozwiązaniecs2_webradar.sln001

Rozwiązanie i projekty

usermode

Komunikat
usermode.vcxproj: + Nie odnaleziono aplikacji, na której jest oparty ten typ projektu. Kliknij następujące łącze, aby uzyskać więcej informacji: 8bc9ceb8-8b4a-11d0-8d11-00a0c91bc942

Rozwiązanie

Komunikat
+ Pokaż 1 dodatkowe wiadomości +
cs2_webradar.sln: + Plik rozwiązania nie wymaga migracji.
+ Ukryj 1 dodatkowe wiadomości +
\ No newline at end of file diff --git a/usermode/config.json b/usermode/config.json new file mode 100644 index 0000000..ce92b01 --- /dev/null +++ b/usermode/config.json @@ -0,0 +1,5 @@ +{ + "m_use_localhost": true, + "m_local_ip": "192.168.x.x", + "m_public_ip": "x.x.x.x" +} \ No newline at end of file diff --git a/usermode/ext/easywsclient/easywsclient.cpp b/usermode/ext/easywsclient/easywsclient.cpp index 0372c4a..93b7ba2 100644 --- a/usermode/ext/easywsclient/easywsclient.cpp +++ b/usermode/ext/easywsclient/easywsclient.cpp @@ -1,4 +1,3 @@ - #ifdef _WIN32 #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) #define _CRT_SECURE_NO_WARNINGS // _CRT_SECURE_NO_WARNINGS for sscanf errors in MSVC2013 Express diff --git a/usermode/src/common.hpp b/usermode/src/common.hpp index 95e23bf..796a30e 100644 --- a/usermode/src/common.hpp +++ b/usermode/src/common.hpp @@ -16,14 +16,44 @@ /* custom defines */ #define LOG_INFO(str, ...) \ - printf(" [info] " str "\n", __VA_ARGS__) + { \ + FILE* log = fopen("WR_Log.txt", "a+"); \ + if (log) { \ + fprintf(log, "[INFO] " str "\n", __VA_ARGS__); \ + fclose(log); \ + } \ + } #define LOG_WARNING(str, ...) \ - printf(" [warning] " str "\n", __VA_ARGS__) + { \ + FILE* log = fopen("WR_Log.txt", "a+"); \ + if (log) { \ + fprintf(log, "[WARNING] " str "\n", __VA_ARGS__); \ + fclose(log); \ + } \ + } #define LOG_ERROR(str, ...) \ { \ const auto filename = std::filesystem::path(__FILE__).filename().string(); \ - printf(" [error] [%s:%d] " str "\n", filename.c_str(), __LINE__, __VA_ARGS__); \ - std::this_thread::sleep_for(std::chrono::seconds(5)); \ - } \ No newline at end of file + FILE* log = fopen("WR_Log.txt", "a+"); \ + if (log) { \ + fprintf(log, "[ERROR] [%s:%d] " str "\n", filename.c_str(), __LINE__, __VA_ARGS__); \ + fclose(log); \ + } \ + } + +#define LOG_CLEAR() \ + { \ + FILE* log = fopen("WR_Log.txt", "w+"); \ + if (log) { \ + fclose(log); \ + } \ + } + +#define SLEEP(sec) \ + { \ + auto wait_to = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()) + sec; \ + while (wait_to >= std::chrono::system_clock::to_time_t(std::chrono::system_clock::now())) {}; \ + } + \ No newline at end of file diff --git a/usermode/src/core/schema.hpp b/usermode/src/core/schema.hpp index c6683fc..d2eb77f 100644 --- a/usermode/src/core/schema.hpp +++ b/usermode/src/core/schema.hpp @@ -17,6 +17,12 @@ return m_memory->read_t(name_ptr); \ } +#define SCHEMA_ADD_ARRAY(arrtype, arrsize, name, field) \ + inline std::array name() \ + { \ + return m_memory->read_t>(reinterpret_cast(this) + schema::get_offset(fnv1a::hash_const(field))); \ + } + #define SCHEMA_ADD_OFFSET(type, name, offset) \ inline type name() \ { \ diff --git a/usermode/src/dllmain.cpp b/usermode/src/dllmain.cpp index 657f0bf..229ca0b 100644 --- a/usermode/src/dllmain.cpp +++ b/usermode/src/dllmain.cpp @@ -1,81 +1,154 @@ #include "pch.hpp" -bool main() +HWND g_hMainWnd = NULL; +HWND g_hLogEdit = NULL; +UINT const WM_APP_LOG = WM_APP + 1; + +void LogMessage(const std::string& msg, int type = 1) +{ + std::string prefixMsg = ""; + if (!g_hMainWnd) return; + + char* pText = new char[msg.length() + 10]; + switch (type) { + case 1: + prefixMsg = "[INFO] " + msg; + break; + case 2: + prefixMsg = "[WARNING] " + msg; + break; + case 3: + prefixMsg = "[ERROR] " + msg; + break; + default: + prefixMsg = "[OTHER] " + msg; + break; + } + + + strcpy_s(pText, prefixMsg.length() + 1, prefixMsg.c_str()); + + PostMessage(g_hMainWnd, WM_APP_LOG, 0, (LPARAM)pText); +} + +DWORD WINAPI AppLogic(LPVOID lpParam) { - if (!utils::is_updated()) + LOG_CLEAR(); + + if (!utils::is_updated()) { + LogMessage("Radar is not updated! Check LOG for more info.", 3); return {}; + } + + LogMessage("Radar is up to date."); + LOG_INFO("Radar is up to date."); config_data_t config_data = {}; - if (!cfg::setup(config_data)) + switch (cfg::setup(config_data)) { - std::this_thread::sleep_for(std::chrono::seconds(5)); - return {}; + case 0: + break; + case 1: + LogMessage("Couldn't open config.json file, please check files and configure it.", 3); + return {}; + break; + case 2: + LogMessage("Failed to parse config.json, please check syntax.", 3); + return {}; + break; + case 3: + LogMessage("Failed to deserialize config.json.", 3); + return {}; + break; + default: + LogMessage("Error", 3); + break; } - LOG_INFO("config system initialization completed"); + LogMessage("Config system initialization completed."); if (!exc::setup()) { - std::this_thread::sleep_for(std::chrono::seconds(5)); + LogMessage("Exception setup failed! Check LOG for more info.", 3); return {}; } - LOG_INFO("exception handler initialization completed"); + LogMessage("Exception handler initialization completed."); - if (!m_memory->setup()) - { - std::this_thread::sleep_for(std::chrono::seconds(5)); - return {}; + switch (m_memory->setup()) { + case 0: + LogMessage("Found CS2.exe, continuing..."); + break; + case 1: + LogMessage("One or more anti-cheats are running, please close them.", 3); + return {}; + break; + case 2: + LogMessage("Waiting for CS2.exe process..."); + while (m_memory->setup() == 2) {SLEEP(1)}; + LogMessage("Found CS2.exe, initializing..."); + SLEEP(5); + break; + case 3: + LogMessage("Memory initialization failed.", 3); + return {}; + break; } - LOG_INFO("memory initialization completed"); + LogMessage("Memory initialization completed."); if (!i::setup()) { - std::this_thread::sleep_for(std::chrono::seconds(5)); - return {}; + LogMessage("Waiting for game to load..."); + while (!i::setup()) { SLEEP(3); }; + LogMessage("Game loaded."); } - LOG_INFO("interfaces initialization completed"); + LogMessage("Interfaces initialization completed."); if (!schema::setup()) { - std::this_thread::sleep_for(std::chrono::seconds(5)); + LogMessage("Schema setup failed! Check LOG for more info.", 3); return {}; } - LOG_INFO("schema initialization completed"); + LogMessage("Schema initialization completed."); WSADATA wsa_data = {}; const auto wsa_startup = WSAStartup(MAKEWORD(2, 2), &wsa_data); if (wsa_startup != 0) { - std::this_thread::sleep_for(std::chrono::seconds(5)); return {}; } - LOG_INFO("winsock initialization completed"); + LogMessage("Winsock initialization completed."); const auto ipv4_address = utils::get_ipv4_address(config_data); if (ipv4_address.empty()) - LOG_WARNING("failed to automatically get your ipv4 address!\n we will use '%s' from 'config.json'. if the local ip is wrong, please set it", config_data.m_local_ip); + LogMessage(std::format("Failed to automatically get your ipv4 address!\n we will use '{}' from 'config.json'. If the local ip is wrong, please set it.", config_data.m_local_ip), 2); const auto formatted_address = std::format("ws://{}:22006/cs2_webradar", ipv4_address); static auto web_socket = easywsclient::WebSocket::from_url(formatted_address); - if (!web_socket) - { - LOG_ERROR("failed to connect to the web socket ('%s')", formatted_address.c_str()); - return {}; + + while (!web_socket) { + web_socket = easywsclient::WebSocket::from_url(formatted_address); + if (!web_socket) + { + LogMessage(std::format("Failed to connect to the web socket ({}), retrying...", formatted_address.c_str()), 3); + } } - LOG_INFO("connected to the web socket ('%s')", formatted_address.data()); + LogMessage(std::format("Connected to the web socket ({}).", formatted_address.data())); auto start = std::chrono::system_clock::now(); + bool in_match = false; for (;;) { const auto now = std::chrono::system_clock::now(); const auto duration = now - start; - if (duration >= std::chrono::milliseconds(100)) + if (duration >= std::chrono::milliseconds(30)) { start = now; sdk::update(); - f::run(); - + in_match = f::run(); + if (!in_match) { + f::m_data["m_map"] = "invalid"; + } web_socket->send(f::m_data.dump()); } @@ -83,7 +156,118 @@ bool main() std::this_thread::sleep_for(std::chrono::milliseconds(1)); } - system("pause"); + LogMessage("WebRadar had stopped for no apparent reason...", 3); return true; +} + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_CREATE: + { + g_hLogEdit = CreateWindow(TEXT("EDIT"), + TEXT(""), + WS_CHILD | WS_VISIBLE | WS_BORDER | + ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY, + 10, 0, 360, 210, + hWnd, (HMENU)1, NULL, NULL); + + SendMessage(g_hLogEdit, WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), TRUE); + break; + } + + case WM_APP_LOG: + { + char* pText = (char*)lParam; + + int len = GetWindowTextLength(g_hLogEdit); + SendMessage(g_hLogEdit, EM_SETSEL, (WPARAM)len, (LPARAM)len); + SendMessage(g_hLogEdit, EM_REPLACESEL, 0, (LPARAM)pText); + SendMessage(g_hLogEdit, EM_REPLACESEL, 0, (LPARAM)TEXT("\r\n")); + + delete[] pText; + break; + } + + case WM_DESTROY: + { + g_hLogEdit = NULL; + g_hMainWnd = NULL; + PostQuitMessage(0); + break; + } + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + return 0; +} + +int APIENTRY WinMain(HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPSTR lpCmdLine, + int nCmdShow) +{ + UNREFERENCED_PARAMETER(hPrevInstance); + UNREFERENCED_PARAMETER(lpCmdLine); + + const char CLASS_NAME[] = "AppWindowClass"; + + WNDCLASS wc = {}; + wc.lpfnWndProc = WndProc; + wc.hInstance = hInstance; + wc.lpszClassName = CLASS_NAME; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + + RegisterClass(&wc); + + g_hMainWnd = CreateWindowEx( + 0, + CLASS_NAME, + "WBFCS", + WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX & ~WS_THICKFRAME, + + CW_USEDEFAULT, CW_USEDEFAULT, 400, 260, + + NULL, + NULL, + hInstance, + NULL + ); + + if (g_hMainWnd == NULL) + { + return 0; + } + + ShowWindow(g_hMainWnd, nCmdShow); + UpdateWindow(g_hMainWnd); + + HANDLE hThread = CreateThread( + NULL, + 0, + AppLogic, + (LPVOID)g_hMainWnd, + 0, + NULL + ); + + if (hThread == NULL) + { + MessageBox(g_hMainWnd, "Failed to create worker thread!", "Fatal Error", MB_OK | MB_ICONERROR); + return 0; + } + CloseHandle(hThread); + + MSG msg = {}; + while (GetMessage(&msg, NULL, 0, 0) > 0) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + return (int)msg.wParam; } \ No newline at end of file diff --git a/usermode/src/features/bomb/bomb.cpp b/usermode/src/features/bomb/bomb.cpp index cf5b1d5..550077e 100644 --- a/usermode/src/features/bomb/bomb.cpp +++ b/usermode/src/features/bomb/bomb.cpp @@ -10,6 +10,8 @@ void f::bomb::get_carried_bomb(c_base_entity* bomb) m_data["m_bomb"]["x"] = scene_origin.m_x; m_data["m_bomb"]["y"] = scene_origin.m_y; + m_data["m_bomb"]["owner_entity"] = reinterpret_cast(bomb->m_hOwnerEntity()); + } void f::bomb::get_planted_bomb(c_planted_c4* planted_c4) diff --git a/usermode/src/features/dropped_weapons/dropped_weapons.cpp b/usermode/src/features/dropped_weapons/dropped_weapons.cpp new file mode 100644 index 0000000..28ffa67 --- /dev/null +++ b/usermode/src/features/dropped_weapons/dropped_weapons.cpp @@ -0,0 +1,38 @@ +#include "pch.hpp" + +static std::unordered_map dropped_weapon_cache; + +bool f::dropped_weapons::is_weapon(std::string weapon_name) +{ + auto is_in_cache = dropped_weapon_cache.find(weapon_name); + if (is_in_cache != dropped_weapon_cache.end()) { + return is_in_cache->second; + } + + bool is_weapon = weapon_name.starts_with("weapon_"); + + dropped_weapon_cache[weapon_name] = is_weapon; + + return is_weapon; +} + + +void f::dropped_weapons::get_weapon(c_base_entity* weapon) +{ + const auto vec_origin = weapon->m_pGameSceneNode()->m_vecOrigin(); + + std::string weapon_name = reinterpret_cast(weapon)->m_WeaponData()->m_szName(); + + if (weapon_name.empty()) + return; + + weapon_name.erase(weapon_name.begin(), weapon_name.begin() + 7); + + if (vec_origin.is_zero()) + return; + + + m_dropped_weapon_data["m_name"] = weapon_name; + m_dropped_weapon_data["m_x"] = vec_origin.m_x; + m_dropped_weapon_data["m_y"] = vec_origin.m_y; +} \ No newline at end of file diff --git a/usermode/src/features/features.cpp b/usermode/src/features/features.cpp index dec5deb..e0fc3af 100644 --- a/usermode/src/features/features.cpp +++ b/usermode/src/features/features.cpp @@ -1,24 +1,29 @@ -#include "pch.hpp" +#include "pch.hpp" -void f::run() + +bool f::run() { const auto local_team = sdk::m_local_controller->m_iTeamNum(); if (local_team == e_team::none || local_team == e_team::spec) - return; + return false; m_data = nlohmann::json{}; m_player_data = nlohmann::json{}; + m_grenade_data = nlohmann::json{}; + m_grenade_thrown_data = nlohmann::json{}; + m_dropped_weapon_data = nlohmann::json{}; m_data["m_local_team"] = local_team; get_map(); get_player_info(); + return true; } void f::get_map() { const auto map_name = i::m_global_vars->m_map_name(); - if (map_name.empty() || map_name.find("") != std::string::npos) + if (map_name.empty() || map_name.find("") != std::string::npos || map_name == "SetVelocityLimit") { m_data["m_map"] = "invalid"; @@ -32,6 +37,9 @@ void f::get_map() void f::get_player_info() { m_data["m_players"] = nlohmann::json::array(); + m_data["m_grenades"]["landed"] = nlohmann::json::array(); + m_data["m_grenades"]["thrown"] = nlohmann::json::array(); + m_data["m_dropped_weapons"] = nlohmann::json::array(); const auto highest_idx = 1024; for (int32_t idx = 0; idx < highest_idx; idx++) @@ -50,6 +58,8 @@ void f::get_player_info() const auto hashed_class_name = fnv1a::hash(class_name); + std::string designer_name = entity->m_pEntity()->m_designerName(); + if (hashed_class_name == fnv1a::hash("CCSPlayerController")) { const auto player = i::m_game_entity_system->get(entity_handle); @@ -78,5 +88,69 @@ void f::get_player_info() const auto planted_c4 = reinterpret_cast(entity); f::bomb::get_planted_bomb(planted_c4); } + else if (hashed_class_name == fnv1a::hash("C_SmokeGrenadeProjectile")) { + + m_grenade_data = {}; + m_grenade_thrown_data = {}; + + const auto smoke = reinterpret_cast(entity); + f::grenades::get_smoke(smoke); + + if (m_grenade_data.empty()) { + f::grenades::get_thrown(reinterpret_cast(entity), designer_name); + + if (!m_grenade_thrown_data.empty()) { + + m_grenade_thrown_data["m_idx"] = idx; + m_data["m_grenades"]["thrown"].push_back(m_grenade_thrown_data); + + } + + continue; + } + + m_data["m_grenades"]["landed"].push_back(m_grenade_data); + } + else if (hashed_class_name == fnv1a::hash("C_Inferno")) { + + m_grenade_data = {}; + + const auto molo = reinterpret_cast(entity); + f::grenades::get_molo(molo); + + if (m_grenade_data.empty()) + continue; + + m_data["m_grenades"]["landed"].push_back(m_grenade_data); + } + else if (hashed_class_name == fnv1a::hash("C_HEGrenadeProjectile") || hashed_class_name == fnv1a::hash("C_FlashbangProjectile") || hashed_class_name == fnv1a::hash("C_DecoyProjectile") || hashed_class_name == fnv1a::hash("C_MolotovProjectile")) + { + m_grenade_thrown_data = {}; + + f::grenades::get_thrown(reinterpret_cast(entity), designer_name); + + if (m_grenade_thrown_data.empty()) + continue; + + m_grenade_thrown_data["m_idx"] = idx; + + m_data["m_grenades"]["thrown"].push_back(m_grenade_thrown_data); + } + else if (!designer_name.empty()) { + if (f::dropped_weapons::is_weapon(designer_name)) { + + m_dropped_weapon_data = {}; + + const auto weapon = reinterpret_cast(entity); + f::dropped_weapons::get_weapon(weapon); + + if (m_dropped_weapon_data.empty()) + continue; + + m_dropped_weapon_data["m_idx"] = idx; + + m_data["m_dropped_weapons"].push_back(m_dropped_weapon_data); + } + } } } \ No newline at end of file diff --git a/usermode/src/features/features.hpp b/usermode/src/features/features.hpp index dbe435e..06110c2 100644 --- a/usermode/src/features/features.hpp +++ b/usermode/src/features/features.hpp @@ -13,13 +13,29 @@ namespace f::bomb void get_planted_bomb(c_planted_c4* planted_c4); } +namespace f::dropped_weapons +{ + bool is_weapon(std::string weapon_name); + void get_weapon(c_base_entity* weapon); +} + +namespace f::grenades +{ + void get_smoke(c_smoke_grenade* smoke); + void get_molo(c_molo_grenade* molo); + void get_thrown(c_base_grenade* nade, std::string designer_name); +} + namespace f { - void run(); + bool run(); void get_map(); void get_player_info(); inline nlohmann::json m_data = {}; inline nlohmann::json m_player_data = {}; + inline nlohmann::json m_grenade_data = {}; + inline nlohmann::json m_grenade_thrown_data = {}; + inline nlohmann::json m_dropped_weapon_data = {}; inline uint32_t m_bomb_idx = 0; } \ No newline at end of file diff --git a/usermode/src/features/grenades/grenades.cpp b/usermode/src/features/grenades/grenades.cpp new file mode 100644 index 0000000..24e7cc5 --- /dev/null +++ b/usermode/src/features/grenades/grenades.cpp @@ -0,0 +1,75 @@ +#include "pch.hpp" + +#define TICK_INTERVAL 0.015625f +#define TICKS_TO_TIME(t) (TICK_INTERVAL*(t)) +#define PRIu16 "hu" + +void f::grenades::get_smoke(c_smoke_grenade* smoke) +{ + const auto curtime = i::m_global_vars->m_curtime(); + + const auto dis_time = 21.5f - (curtime - TICKS_TO_TIME(smoke->m_nSmokeEffectTickBegin())); + if (dis_time <= 0.f) + return; + + const auto detPos = smoke->m_vSmokeDetonationPos(); + + if (detPos.is_zero()) + return; + + m_grenade_data["m_type"] = "smoke"; + m_grenade_data["m_timeleft"] = dis_time; + m_grenade_data["m_x"] = detPos.m_x; + m_grenade_data["m_y"] = detPos.m_y; +} + +void f::grenades::get_molo(c_molo_grenade* molo) +{ + const auto curtime = i::m_global_vars->m_curtime(); + const auto fireBurning = molo->m_bFireIsBurning(); + const auto firePositions = molo->m_firePositions(); + const auto nadeTime = 7.f; + + const auto dis_time = nadeTime - (curtime - TICKS_TO_TIME(molo->m_nFireEffectTickBegin())); + if (dis_time <= 0.f) + return; + + const auto vec_origin = molo->m_pGameSceneNode()->m_vecOrigin(); + + + m_grenade_data["m_firePositions"] = nlohmann::json{}; + + for (int i = 0; i <= molo->m_fireCount(); i++) + { + if (!fireBurning[i]) + continue; + + m_grenade_data["m_firePositions"].push_back({ firePositions[i].m_x, firePositions[i].m_y }); + } + + if (m_grenade_data["m_firePositions"].empty()) + return; + + m_grenade_data["m_type"] = "molo"; + m_grenade_data["m_timeleft"] = dis_time; + m_grenade_data["m_x"] = vec_origin.m_x; + m_grenade_data["m_y"] = vec_origin.m_y; + +} + +void f::grenades::get_thrown(c_base_grenade* nade, std::string designer_name) +{ + const auto nadePos = nade->m_pGameSceneNode()->m_vecOrigin(); + + if (designer_name.empty()) + return; + + designer_name.erase(designer_name.end() - 11, designer_name.end()); + + if (nadePos.is_zero()) + return; + + m_grenade_thrown_data["m_type"] = designer_name; + m_grenade_thrown_data["m_x"] = nadePos.m_x; + m_grenade_thrown_data["m_y"] = nadePos.m_y; +} \ No newline at end of file diff --git a/usermode/src/features/players/players.cpp b/usermode/src/features/players/players.cpp index 3b156ea..291613f 100644 --- a/usermode/src/features/players/players.cpp +++ b/usermode/src/features/players/players.cpp @@ -19,10 +19,12 @@ bool f::players::get_data(int32_t idx, c_cs_player_controller* player, c_cs_play m_player_data["m_armor"] = player_pawn->m_ArmorValue(); m_player_data["m_position"]["x"] = vec_origin.m_x; m_player_data["m_position"]["y"] = vec_origin.m_y; + m_player_data["m_position"]["z"] = vec_origin.m_z; m_player_data["m_eye_angle"] = player_pawn->m_angEyeAngles().m_y; m_player_data["m_has_helmet"] = player_pawn->m_pItemServices()->m_bHasHelmet(); m_player_data["m_has_defuser"] = player_pawn->m_pItemServices()->m_bHasDefuser(); m_player_data["m_weapons"] = nlohmann::json{}; + m_player_data["m_flashed"] = player_pawn->m_flFlashOverlayAlpha(); if (team == e_team::t && !is_dead) m_player_data["m_has_bomb"] = m_bomb_idx == (player->m_hPawn().get_entry_idx() & 0xffff); diff --git a/usermode/src/sdk/entity.hpp b/usermode/src/sdk/entity.hpp index 2d7177c..899d088 100644 --- a/usermode/src/sdk/entity.hpp +++ b/usermode/src/sdk/entity.hpp @@ -47,7 +47,7 @@ class c_entity_identity public: SCHEMA_ADD_OFFSET(uintptr_t, m_pClassInfo, 0x08); SCHEMA_ADD_OFFSET(uint32_t, m_Idx, 0x10); - SCHEMA_ADD_FIELD(const char*, m_designerName, "CEntityIdentity->m_designerName"); + SCHEMA_ADD_STRING(m_designerName, "CEntityIdentity->m_designerName"); SCHEMA_ADD_FIELD(uint32_t, m_flags, "CEntityIdentity->m_flags"); bool is_valid() @@ -82,6 +82,7 @@ class c_game_scene_node { public: SCHEMA_ADD_FIELD(f_vector, m_vecAbsOrigin, "CGameSceneNode->m_vecAbsOrigin"); + SCHEMA_ADD_FIELD(f_vector, m_vecOrigin, "CGameSceneNode->m_vecOrigin"); }; class c_base_entity : public c_entity_instance @@ -91,7 +92,6 @@ class c_base_entity : public c_entity_instance SCHEMA_ADD_FIELD(int32_t, m_iHealth, "C_BaseEntity->m_iHealth"); SCHEMA_ADD_FIELD(e_team, m_iTeamNum, "C_BaseEntity->m_iTeamNum"); SCHEMA_ADD_FIELD(c_base_entity*, m_hOwnerEntity, "C_BaseEntity->m_hOwnerEntity"); - const f_vector& get_scene_origin(); }; @@ -122,6 +122,7 @@ class c_cs_player_pawn : public c_base_player_pawn public: SCHEMA_ADD_FIELD(int32_t, m_ArmorValue, "C_CSPlayerPawn->m_ArmorValue"); SCHEMA_ADD_FIELD(f_vector, m_angEyeAngles, "C_CSPlayerPawn->m_angEyeAngles"); + SCHEMA_ADD_FIELD(float, m_flFlashOverlayAlpha, "C_CSPlayerPawnBase->m_flFlashOverlayAlpha"); const std::string get_model_name(); }; @@ -175,4 +176,26 @@ class c_base_player_weapon : public c_base_entity SCHEMA_ADD_FIELD_OFFSET(c_cs_weapon_base_v_data*, m_WeaponData, "C_BaseEntity->m_nSubclassID", 0x08); c_base_player_weapon* get(const int32_t idx); +}; + +class c_smoke_grenade : public c_base_entity +{ +public: + SCHEMA_ADD_FIELD(int, m_nSmokeEffectTickBegin, "C_SmokeGrenadeProjectile->m_nSmokeEffectTickBegin"); + SCHEMA_ADD_FIELD(f_vector, m_vSmokeDetonationPos, "C_SmokeGrenadeProjectile->m_vSmokeDetonationPos"); +}; + +class c_molo_grenade : public c_base_entity +{ +public: + SCHEMA_ADD_ARRAY(bool, 64, m_bFireIsBurning, "C_Inferno->m_bFireIsBurning"); + SCHEMA_ADD_ARRAY(f_vector, 64, m_firePositions, "C_Inferno->m_firePositions"); + SCHEMA_ADD_FIELD(int, m_fireCount, "C_Inferno->m_fireCount"); + SCHEMA_ADD_FIELD(int, m_nFireEffectTickBegin, "C_Inferno->m_nFireEffectTickBegin"); +}; + +class c_base_grenade : public c_base_entity +{ +public: + SCHEMA_ADD_FIELD_OFFSET(c_cs_weapon_base_v_data*, m_WeaponData, "C_BaseEntity->m_nSubclassID", 0x08); }; \ No newline at end of file diff --git a/usermode/src/utils/config.cpp b/usermode/src/utils/config.cpp index 6399363..e0f7817 100644 --- a/usermode/src/utils/config.cpp +++ b/usermode/src/utils/config.cpp @@ -1,27 +1,25 @@ #include "pch.hpp" -bool cfg::setup(config_data_t& config_data) +int cfg::setup(config_data_t& config_data) { std::ifstream file("config.json"); if (!file.is_open()) { - LOG_WARNING("cannot open file 'config.json'"); - std::ofstream example_config("config.json"); example_config << std::format("{}", R"({ "m_use_localhost": true, "m_local_ip": "192.168.x.x", "m_public_ip": "x.x.x.x" })"); - - return {}; + LOG_WARNING("Couldn't open config.json file, please check files and configure it."); + return 1; } const auto parsed_data = nlohmann::json::parse(file); if (parsed_data.empty()) { - LOG_ERROR("failed to parse 'config.json'"); - return {}; + LOG_WARNING("Failed to parse config.json, please check syntax."); + return 2; } try @@ -30,9 +28,9 @@ bool cfg::setup(config_data_t& config_data) } catch (const std::exception& e) { - LOG_ERROR("failed to deserialize 'config_data_t' (%s)", e.what()); - return {}; + LOG_WARNING("Failed to deserialize config.json."); + return 3; } - return true; + return 0; } \ No newline at end of file diff --git a/usermode/src/utils/config.hpp b/usermode/src/utils/config.hpp index b9c9598..6e84c12 100644 --- a/usermode/src/utils/config.hpp +++ b/usermode/src/utils/config.hpp @@ -11,5 +11,5 @@ struct config_data_t namespace cfg { - bool setup(config_data_t& config_data); + int setup(config_data_t& config_data); } \ No newline at end of file diff --git a/usermode/src/utils/memory.cpp b/usermode/src/utils/memory.cpp index b9061ad..2d758a0 100644 --- a/usermode/src/utils/memory.cpp +++ b/usermode/src/utils/memory.cpp @@ -1,29 +1,23 @@ #include "pch.hpp" -bool c_memory::setup() + int c_memory::setup() { - if (is_anticheat_running()) - return {}; - - const auto process_id = this->get_process_id("cs2.exe"); - if (!process_id.has_value()) - { - LOG_ERROR("failed to get process id for 'cs2.exe'\n make sure the game is running"); - return {}; + if (is_anticheat_running()) { + LOG_WARNING("Detected running Anti-Cheat software."); + return 1; + } + + auto process_id = this->get_process_id("cs2.exe"); + if (!process_id.has_value()) { + LOG_WARNING("No CS2.exe process.") + return 2; } this->m_id = process_id.value(); - auto handle = this->hijack_handle(); - if (!handle.has_value()) - { - LOG_WARNING("failed to hijack a handle for 'cs2.exe', we will continue using the classic method"); - this->m_handle = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, false, this->m_id); - } - else - this->m_handle = handle.value(); + this->m_handle = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, false, this->m_id); - return this->m_handle != nullptr; + return this->m_handle != nullptr?0:3; } std::optional c_memory::get_process_id(const std::string_view& process_name) @@ -48,102 +42,6 @@ std::optional c_memory::get_process_id(const std::string_view& process return {}; } -std::optional c_memory::hijack_handle() -{ - auto cleanup = [](std::vector& handle_info, void*& process_handle) - { - handle_info.clear(); - - if (process_handle != INVALID_HANDLE_VALUE) - { - CloseHandle(process_handle); - process_handle = nullptr; - } - }; - - const auto ntdll = GetModuleHandleA("ntdll.dll"); - if (!ntdll) - return {}; - - using fn_nt_query_system_information = long(__stdcall*)(unsigned long, void*, unsigned long, unsigned long*); - const auto nt_query_system_information = reinterpret_cast(GetProcAddress(ntdll, "NtQuerySystemInformation")); - if (!nt_query_system_information) - return {}; - - using fn_nt_duplicate_object = long(__stdcall*)(void*, void*, void*, void**, unsigned long, unsigned long, unsigned long); - const auto nt_duplicate_object = reinterpret_cast(GetProcAddress(ntdll, "NtDuplicateObject")); - if (!nt_duplicate_object) - return {}; - - using fn_nt_open_process = long(__stdcall*)(void**, unsigned long, OBJECT_ATTRIBUTES*, CLIENT_ID*); - const auto nt_open_process = reinterpret_cast(GetProcAddress(ntdll, "NtOpenProcess")); - if (!nt_open_process) - return {}; - - using fn_rtl_adjust_privilege = long(__stdcall*)(unsigned long, unsigned char, unsigned char, unsigned char*); - const auto rtl_adjust_privilege = reinterpret_cast(GetProcAddress(ntdll, "RtlAdjustPrivilege")); - if (!rtl_adjust_privilege) - return {}; - - uint8_t old_privilege = 0; - rtl_adjust_privilege(0x14, 1, 0, &old_privilege); - - OBJECT_ATTRIBUTES object_attributes{}; - InitializeObjectAttributes(&object_attributes, nullptr, 0, nullptr, nullptr); - - std::vector handle_info(sizeof(system_handle_info_t)); - std::pair handle{ nullptr, nullptr }; - CLIENT_ID client_id{}; - - unsigned long status = 0; - do - { - handle_info.resize(handle_info.size() * 2); - status = nt_query_system_information(0x10, handle_info.data(), static_cast(handle_info.size()), nullptr); - } while (status == 0xc0000004); - - if (!NT_SUCCESS(status)) - { - cleanup(handle_info, handle.first); - return {}; - } - - const auto system_handle_info = reinterpret_cast(handle_info.data()); - for (uint32_t idx = 0; idx < system_handle_info->m_handle_count; ++idx) - { - const auto system_handle = system_handle_info->m_handles[idx]; - if (reinterpret_cast(system_handle.m_handle) == INVALID_HANDLE_VALUE || system_handle.m_object_type_number != 0x07) - continue; - - client_id.UniqueProcess = reinterpret_cast(system_handle.m_process_id); - - if (handle.first != nullptr && handle.first == INVALID_HANDLE_VALUE) - { - CloseHandle(handle.first); - continue; - } - - const auto open_process = nt_open_process(&handle.first, PROCESS_DUP_HANDLE, &object_attributes, &client_id); - if (!NT_SUCCESS(open_process)) - continue; - - const auto duplicate_object = nt_duplicate_object(handle.first, reinterpret_cast(system_handle.m_handle), GetCurrentProcess(), &handle.second, PROCESS_ALL_ACCESS, 0, 0); - if (!NT_SUCCESS(duplicate_object)) - continue; - - if (GetProcessId(handle.second) == this->m_id) - { - cleanup(handle_info, handle.first); - return handle.second; - } - - CloseHandle(handle.second); - } - - cleanup(handle_info, handle.first); - return {}; -} - std::optional c_memory::find_pattern(const std::string_view& module_name, const std::string_view& pattern) { constexpr auto pattern_to_bytes = [](const std::string_view& pattern) diff --git a/usermode/src/utils/memory.hpp b/usermode/src/utils/memory.hpp index f371785..b70f1a1 100644 --- a/usermode/src/utils/memory.hpp +++ b/usermode/src/utils/memory.hpp @@ -27,9 +27,8 @@ class c_memory CloseHandle(this->m_handle); } - bool setup(); + int setup(); std::optional get_process_id(const std::string_view& process_name); - std::optional hijack_handle(); std::optional find_pattern(const std::string_view& module_name, const std::string_view& pattern); std::pair, std::optional> get_module_info(const std::string_view& module_name); bool is_anticheat_running(); diff --git a/usermode/src/utils/utils.cpp b/usermode/src/utils/utils.cpp index 216e999..3744dba 100644 --- a/usermode/src/utils/utils.cpp +++ b/usermode/src/utils/utils.cpp @@ -1,5 +1,4 @@ #include "pch.hpp" - std::string utils::get_ipv4_address(config_data_t& config_data) { if (config_data.m_use_localhost) diff --git a/usermode/usermode.hpp b/usermode/usermode.hpp index 6cffd2c..d498ae8 100644 --- a/usermode/usermode.hpp +++ b/usermode/usermode.hpp @@ -45,7 +45,6 @@ #include "sdk/interfaces/game_entity_system.hpp" #include "sdk/interfaces/schema_system.hpp" #include "sdk/interfaces/global_vars.hpp" - #include "core/sdk.hpp" /* features */ diff --git a/usermode/usermode.vcxproj b/usermode/usermode.vcxproj index 0b59d3d..04d1e9a 100644 --- a/usermode/usermode.vcxproj +++ b/usermode/usermode.vcxproj @@ -1,154 +1,161 @@ - - - - - release - x64 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - NotUsing - - - - - - - - - - - Create - - - - - - - - - - - - 16.0 - Win32Proj - {6A55EA2E-776D-410E-B7F1-DEA56E9BDFCD} - usermode - 10.0 - usermode - - - - Application - false - v143 - true - MultiByte - - - - - - - - - - - - false - $(SolutionDir)$(Configuration)\ - $(SolutionDir)\$(Configuration)\intermediates\ - $(IncludePath) - $(LibraryPath) - - - - TurnOffAllWarnings - true - true - false - NDEBUG;_CRT_SECURE_NO_WARNINGS;NOMINMAX;CURL_STATICLIB;%(PreprocessorDefinitions) - true - stdcpplatest - true - MinSpace - OnlyExplicitInline - Size - true - true - MultiThreaded - true - false - Use - pch.hpp - NotSet - $(ProjectDir);$(ProjectDir)\src\;%(AdditionalIncludeDirectories) - false - false - - - Console - true - true - true - crypt32.lib;wldap32.lib;normaliz.lib;libcurl_a.lib;%(AdditionalDependencies) - Default - RequireAdministrator - $(SolutionDir)ext\curl\lib\;%(AdditionalLibraryDirectories) - - - - - + + + + + release + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NotUsing + + + + + + + + + + + + + Create + + + + + + + + + + + + 16.0 + Win32Proj + {6A55EA2E-776D-410E-B7F1-DEA56E9BDFCD} + usermode + 10.0 + usermode + + + + Application + false + v143 + true + MultiByte + + + + + + + + + + + + + + + false + $(SolutionDir)$(Configuration)\ + $(SolutionDir)\$(Configuration)\intermediates\ + $(IncludePath) + $(LibraryPath) + + + + TurnOffAllWarnings + true + true + false + NDEBUG;_CRT_SECURE_NO_WARNINGS;NOMINMAX;CURL_STATICLIB;%(PreprocessorDefinitions) + true + stdcpplatest + true + MinSpace + OnlyExplicitInline + Size + true + true + MultiThreaded + true + false + Use + pch.hpp + NotSet + $(ProjectDir);$(ProjectDir)\src\;%(AdditionalIncludeDirectories) + false + false + + + Windows + true + true + true + crypt32.lib;wldap32.lib;normaliz.lib;libcurl_a.lib;%(AdditionalDependencies) + Default + RequireAdministrator + $(SolutionDir)ext\curl\lib\;%(AdditionalLibraryDirectories) + MachineX64 + + + + + + \ No newline at end of file diff --git a/usermode/usermode.vcxproj.user b/usermode/usermode.vcxproj.user index 966b4ff..429333d 100644 --- a/usermode/usermode.vcxproj.user +++ b/usermode/usermode.vcxproj.user @@ -1,6 +1,6 @@ - - - - true - + + + + true + \ No newline at end of file diff --git a/webapp/index.html b/webapp/index.html index e188fe8..0c21144 100644 --- a/webapp/index.html +++ b/webapp/index.html @@ -4,7 +4,7 @@ - cs2_webradar + CS2 Radar
diff --git a/webapp/package-lock.json b/webapp/package-lock.json index 3121ed4..a54965b 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -23,6 +23,7 @@ "eslint-plugin-react-hooks": "^4.6.0", "eslint-plugin-react-refresh": "^0.4.3", "postcss": "^8.4.31", + "react-draggable": "^4.5.0", "tailwindcss": "^3.3.3", "vite": "^4.4.5" } @@ -1659,6 +1660,16 @@ "node": ">=12" } }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -4322,6 +4333,21 @@ "react": "^18.3.1" } }, + "node_modules/react-draggable": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/react-draggable/-/react-draggable-4.5.0.tgz", + "integrity": "sha512-VC+HBLEZ0XJxnOxVAZsdRi8rD04Iz3SiiKOoYzamjylUcju/hP9np/aZdLHf/7WOD268WMoNJMvYfB5yAK45cw==", + "dev": true, + "license": "MIT", + "dependencies": { + "clsx": "^2.1.1", + "prop-types": "^15.8.1" + }, + "peerDependencies": { + "react": ">= 16.3.0", + "react-dom": ">= 16.3.0" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", diff --git a/webapp/public/assets/characters/cs_observer.png b/webapp/public/assets/characters/cs_observer.png new file mode 100644 index 0000000..a403f9e Binary files /dev/null and b/webapp/public/assets/characters/cs_observer.png differ diff --git a/webapp/public/assets/icons/blind.png b/webapp/public/assets/icons/blind.png new file mode 100644 index 0000000..dd00e7e Binary files /dev/null and b/webapp/public/assets/icons/blind.png differ diff --git a/webapp/public/assets/icons/down.png b/webapp/public/assets/icons/down.png new file mode 100644 index 0000000..eb52f8a Binary files /dev/null and b/webapp/public/assets/icons/down.png differ diff --git a/webapp/public/assets/icons/up.png b/webapp/public/assets/icons/up.png new file mode 100644 index 0000000..34ec771 Binary files /dev/null and b/webapp/public/assets/icons/up.png differ diff --git a/webapp/public/data/de_nuke/data.json b/webapp/public/data/de_nuke/data.json index 73ad59d..f20dc1b 100644 --- a/webapp/public/data/de_nuke/data.json +++ b/webapp/public/data/de_nuke/data.json @@ -1,5 +1,7 @@ { "x": -3453, "y": 2887, - "scale": 7 + "scale": 7, + "leveling": true, + "level_change": -490 } \ No newline at end of file diff --git a/webapp/public/data/de_nuke/radar_lower.png b/webapp/public/data/de_nuke/radar_lower.png new file mode 100644 index 0000000..6cb797e Binary files /dev/null and b/webapp/public/data/de_nuke/radar_lower.png differ diff --git a/webapp/public/data/de_vertigo/data.json b/webapp/public/data/de_vertigo/data.json index 16dbfc7..7889f9f 100644 --- a/webapp/public/data/de_vertigo/data.json +++ b/webapp/public/data/de_vertigo/data.json @@ -2,5 +2,7 @@ "x": -3168, "y": 1762, "scale": 4.0, - "value": 11701 + "value": 11701, + "leveling": true, + "level_change": 11635 } \ No newline at end of file diff --git a/webapp/public/data/de_vertigo/radar_lower.png b/webapp/public/data/de_vertigo/radar_lower.png new file mode 100644 index 0000000..bb9b69f Binary files /dev/null and b/webapp/public/data/de_vertigo/radar_lower.png differ diff --git a/webapp/src/app.css b/webapp/src/app.css index f1063f9..e4b74d7 100644 --- a/webapp/src/app.css +++ b/webapp/src/app.css @@ -33,3 +33,34 @@ body { .banner-link:hover { box-shadow: 0px 2.5px 0 0 #1E3A54; } + +#level_indicator { + position: absolute; + width: auto; + left: 30%; + bottom: 45%; +} + +select option { + background-color: #0b1a33; + color: rgba(100, 146, 180, 0.5) rgba(20, 40, 55, 0.3); +} + +::-webkit-scrollbar { + width: 8px; +} + +::-webkit-scrollbar-track { + background: rgba(20, 40, 55, 0.3); + border-radius: 4px; +} + +::-webkit-scrollbar-thumb { + background: rgba(100, 146, 180, 0.5); + border-radius: 4px; + transition: background 0.3s; +} + +::-webkit-scrollbar-thumb:hover { + background: rgba(100, 146, 180, 0.8); +} \ No newline at end of file diff --git a/webapp/src/app.jsx b/webapp/src/app.jsx index 8b73919..da47d92 100644 --- a/webapp/src/app.jsx +++ b/webapp/src/app.jsx @@ -1,10 +1,11 @@ import ReactDOM from "react-dom/client"; import { useEffect, useState } from "react"; import "./App.css"; -import PlayerCard from "./components/PlayerCard"; -import Radar from "./components/Radar"; +import PlayerCard from "./components/playercard"; +import Radar from "./components/radar"; import { getLatency, Latency } from "./components/latency"; import MaskedIcon from "./components/maskedicon"; +import { colorSchemePallette } from "./utilities/utilities"; const CONNECTION_TIMEOUT = 5000; @@ -12,9 +13,22 @@ const CONNECTION_TIMEOUT = 5000; const USE_LOCALHOST = 0; /* you can get your public ip from https://ipinfo.io/ip */ -const PUBLIC_IP = "your ip goes here".trim(); +const PUBLIC_IP = "PUBLIC_IP".trim(); const PORT = 22006; + + + + + + + + + + + +let tempPlayer_ = null; + const EFFECTIVE_IP = USE_LOCALHOST ? "localhost" : PUBLIC_IP.match(/[a-zA-Z]/) ? window.location.hostname : PUBLIC_IP; const DEFAULT_SETTINGS = { @@ -23,27 +37,85 @@ const DEFAULT_SETTINGS = { showAllNames: false, showEnemyNames: true, showViewCones: false, + showOnlyEnemies: false, + followYourself: false, + showDroppedWeapons: true, + droppedWeaponSize: 0.5, + droppedWeaponGlow: true, + droppedWeaponIgnoreNade: false, + showGrenades: true, + thrownGrenadeSize: 0.5, + thrownGrenadeColor: "#FF0000", + whichPlayerAreYou: "0", + colorScheme: "default", + settings_version: "1.0" }; const loadSettings = () => { const savedSettings = localStorage.getItem("radarSettings"); - return savedSettings ? JSON.parse(savedSettings) : DEFAULT_SETTINGS; + let parsedSettings = JSON.parse(savedSettings); + return (savedSettings && parsedSettings.settings_version && parsedSettings.settings_version == DEFAULT_SETTINGS.settings_version) ? parsedSettings : DEFAULT_SETTINGS; +}; + +const PlayerSelectionModal = ({ players, onSelect, localTeam }) => { + players = players + return ( +
+
+

Select Yourself

+

This is used to show you different map levels.

Please choose YOURSELF!

+
    + {players + .filter(player => player.m_steam_id !== "0") + .filter(player => player.m_team === localTeam) + .map((player) => ( +
  • onSelect(player.m_steam_id)} + className="p-3 bg-gray-700 hover:bg-radar-blue text-white rounded-md cursor-pointer transition-all duration-200 flex justify-between items-center" + > + {player.m_name} + + {player.m_steam_id} + +
  • + ))} +
+
+
+ ); }; const App = () => { const [averageLatency, setAverageLatency] = useState(0); const [playerArray, setPlayerArray] = useState([]); + const [grenadeData, setGrenadeData] = useState([]); + const [droppedWeaponsData, setDroppedWeaponsData] = useState([]); const [mapData, setMapData] = useState(); const [localTeam, setLocalTeam] = useState(); const [bombData, setBombData] = useState(); const [settings, setSettings] = useState(loadSettings()); - const [bannerOpened, setBannerOpened] = useState(true) + const [showPlayerPrompt, setShowPlayerPrompt] = useState(false); - // Save settings to local storage whenever they change useEffect(() => { localStorage.setItem("radarSettings", JSON.stringify(settings)); }, [settings]); + useEffect(() => { + if (settings.whichPlayerAreYou === "0" && playerArray.length > 0 || settings.whichPlayerAreYou === undefined && playerArray.length > 0 ) { + setShowPlayerPrompt(true); + } else if (settings.whichPlayerAreYou !== "0" && settings.whichPlayerAreYou !== undefined) { + setShowPlayerPrompt(false); + } + }, [playerArray, settings.whichPlayerAreYou]); + const handlePlayerSelect = (playerIdx) => { + setSettings((prevSettings) => ({ + ...prevSettings, + whichPlayerAreYou: playerIdx, + })); + setShowPlayerPrompt(false); + }; + useEffect(() => { const fetchData = async () => { let webSocket = null; @@ -100,23 +172,33 @@ const App = () => { setAverageLatency(getLatency()); const parsedData = JSON.parse(await event.data.text()); - setPlayerArray(parsedData.m_players); setLocalTeam(parsedData.m_local_team); setBombData(parsedData.m_bomb); + setGrenadeData(parsedData.m_grenades); + setDroppedWeaponsData(parsedData.m_dropped_weapons); const map = parsedData.m_map; if (map !== "invalid") { + setPlayerArray(parsedData.m_players); setMapData({ ...(await (await fetch(`data/${map}/data.json`)).json()), name: map, }); document.body.style.backgroundImage = `url(./data/${map}/background.png)`; + } else { + setMapData({ name: "invalid" }); + setPlayerArray([]); + document.body.style.backgroundImage = `url(./data/de_mirage/background.png)`; } }; }; fetchData(); }, []); + + if (playerArray && playerArray.length > 0) { + tempPlayer_ = playerArray.find((player) => player.m_steam_id === settings.whichPlayerAreYou); + } return (
{ backdropFilter: `blur(7.5px)`, }} > - {bannerOpened && ( -
- - €3.49 - - HURRACAN - Plug & play feature rich shareable CS2 Web Radar - Learn more - - -
- )} -
+ {showPlayerPrompt && playerArray.length > 0 && ( + + )} + +
{bombData && bombData.m_blow_time > 0 && !bombData.m_is_defused && (
@@ -149,10 +225,10 @@ const App = () => { color={ (bombData.m_is_defusing && bombData.m_blow_time - bombData.m_defuse_time > 0 && - `bg-radar-green`) || + `#00FF00`) || (bombData.m_blow_time - bombData.m_defuse_time < 0 && - `bg-radar-red`) || - `bg-radar-secondary` + `#FF0000`) || + `${colorSchemePallette[settings.colorScheme][1]}` } /> {`${bombData.m_blow_time.toFixed(1)}s ${(bombData.m_is_defusing && @@ -171,31 +247,36 @@ const App = () => { />
    - {playerArray + {(playerArray && playerArray.length > 0 && playerArray .filter((player) => player.m_team == 2) .map((player) => ( - ))} + )))}
- {(playerArray.length > 0 && mapData && ( - + {(playerArray && playerArray.length > 0 && mapData && mapData.name !== "invalid" && settings.whichPlayerAreYou && ( +
+ +
)) || (

- Connected! Waiting for data from usermode + Connected! Please wait for the host to join the match.

)} @@ -204,7 +285,7 @@ const App = () => { id="counterTerrorist" className="lg:flex hidden flex-col gap-7 m-0 p-0" > - {playerArray + {(playerArray && playerArray.length > 0 && playerArray .filter((player) => player.m_team == 3) .map((player) => ( { playerData={player} settings={settings} /> - ))} + )))}
diff --git a/webapp/src/components/Draggable.jsx b/webapp/src/components/Draggable.jsx new file mode 100644 index 0000000..1532ca5 --- /dev/null +++ b/webapp/src/components/Draggable.jsx @@ -0,0 +1,78 @@ +import { useEffect, useRef, useState } from "react"; + +const Draggable = ({ children, imgref, avrPing, ...props }) => { + const [position, setPosition] = useState({ x: 0, y: 0 }); + const [isDragging, setIsDragging] = useState(false); + const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 }); + const draggableRef = useRef(null); + let moveoverride = "false"; + let newtransx = "0"; + let newtransy = "0"; + if (imgref) { + moveoverride = imgref.getAttribute("moveoverride"); + newtransx = imgref.getAttribute("newtransx"); + newtransy = imgref.getAttribute("newtransy"); + } + + const handleMouseDown = (e) => { + if (e.button !== 0 || moveoverride!="false") return; + setIsDragging(true); + setDragOffset({ + x: e.clientX - position.x, + y: e.clientY - position.y, + }); + }; + + const handleMouseMove = (e) => { + if (!isDragging) return; + if (e.clientX - dragOffset.x < imgref.width*0.5 && e.clientX - dragOffset.x > imgref.width*-0.5 && e.clientY - dragOffset.y < imgref.height*0.5 && e.clientY - dragOffset.y > imgref.height*-0.5) { + setPosition({ + x: e.clientX - dragOffset.x, + y: e.clientY - dragOffset.y, + }); + } + }; + + const handleMouseUp = () => { + setIsDragging(false); + }; + + useEffect(()=>{ + if (moveoverride=="true") { + setPosition({x: newtransx, y: newtransy}) + } + }, [moveoverride, newtransx, newtransy]) + + return ( +
+ {children && typeof children === "object" && children.type + ? { + ...children, + props: { + ...children.props, + isbeingdragged: `${isDragging}`, + moveoverride: `${false}`, + style: { + ...children.props.style, + transform: `translate(${position.x}px, ${position.y}px)`, + transition: `transform ${avrPing}ms linear, scale 100ms linear` + }, + }, + } + : children} +
+ ); +}; + +export default Draggable; diff --git a/webapp/src/components/SettingsButton.jsx b/webapp/src/components/SettingsButton.jsx index a175dd7..0442005 100644 --- a/webapp/src/components/SettingsButton.jsx +++ b/webapp/src/components/SettingsButton.jsx @@ -1,10 +1,21 @@ -import { useState } from "react"; +import { useState, useRef } from "react"; const SettingsButton = ({ settings, onSettingsChange }) => { const [isOpen, setIsOpen] = useState(false); + const settingsMenu = useRef(null); + const settingsBut = useRef(null); + + const closeSettingsIfOpen = (e)=>{ + if(isOpen && !settingsMenu.current?.contains(e.target) && !settingsBut.current?.contains(e.target)){ + setIsOpen(false) + } + } + + document.addEventListener('mousedown',closeSettingsIfOpen) return ( -
+
+ {window.innerHeight>500 && ( + )} {isOpen && ( -
+

Radar Settings

-
+
- Dot Size + Player Dot Size {settings.dotSize}x
{ }} />
- +
+ + + + + {(!settings.showOnlyEnemies) && ( + )} + + + + {settings.showGrenades && ( +
+ + + +
+ ㅤㅤㅤ↑ Grenade Size + {settings.thrownGrenadeSize}x +
+ onSettingsChange({ ...settings, thrownGrenadeSize: parseFloat(e.target.value) })} + className="w-full ml-11 mb-2 h-2 rounded-lg appearance-none cursor-pointer accent-radar-primary" + style={{ + background: `linear-gradient(to right, #b1d0e7 ${((settings.thrownGrenadeSize - 0.1) / 1.9) * 100}%, rgba(59, 130, 246, 0.2) ${((settings.thrownGrenadeSize - 0.1) / 1.9) * 100}%)`, + width: "80%" + }} + /> + +
+ )} + + + + {settings.showDroppedWeapons && ( +
+ + + + + +
+ ㅤㅤㅤ↑ Weapon Size + {settings.droppedWeaponSize}x +
+ onSettingsChange({ ...settings, droppedWeaponSize: parseFloat(e.target.value) })} + className="w-full ml-11 mb-2 h-2 rounded-lg appearance-none cursor-pointer accent-radar-primary" + style={{ + background: `linear-gradient(to right, #b1d0e7 ${((settings.droppedWeaponSize - 0.1) / 1.9) * 100}%, rgba(59, 130, 246, 0.2) ${((settings.droppedWeaponSize - 0.1) / 1.9) * 100}%)`, + width: "80%" + }} + /> + +
+ )} + + + +
diff --git a/webapp/src/components/bomb.jsx b/webapp/src/components/bomb.jsx index 1c925d2..9c29cf9 100644 --- a/webapp/src/components/bomb.jsx +++ b/webapp/src/components/bomb.jsx @@ -1,5 +1,5 @@ import { useRef } from "react"; -import { getRadarPosition, teamEnum } from "../utilities/utilities"; +import { getRadarPosition, teamEnum, calculatePositionWithScale } from "../utilities/utilities"; const Bomb = ({ bombData, mapData, radarImage, localTeam, averageLatency, settings }) => { const radarPosition = getRadarPosition(mapData, bombData); @@ -8,12 +8,11 @@ const Bomb = ({ bombData, mapData, radarImage, localTeam, averageLatency, settin const bombBounding = (bombRef.current && bombRef.current.getBoundingClientRect()) || { width: 0, height: 0 }; - const radarImageBounding = (radarImage !== undefined && - radarImage.getBoundingClientRect()) || { width: 0, height: 0 }; - const radarImageTranslation = { - x: radarImageBounding.width * radarPosition.x - bombBounding.width * 0.5, - y: radarImageBounding.height * radarPosition.y - bombBounding.height * 0.5, - }; + const scaledPos = calculatePositionWithScale(radarImage, radarPosition); + const radarImageTranslation = { + x: (scaledPos[0] - bombBounding.width * 0.5), + y: (scaledPos[1] - bombBounding.height * 0.5), + }; // Calculate bomb size based on settings const baseSize = 1.5; // Base size in vw @@ -34,7 +33,7 @@ const Bomb = ({ bombData, mapData, radarImage, localTeam, averageLatency, settin `#c90b0b` }`, WebkitMask: `url('./assets/icons/c4_sml.png') no-repeat center / contain`, - opacity: `1`, + opacity: `${settings.showOnlyEnemies && bombData.owner_entity < 141288103162675 && localTeam == teamEnum.terrorist ? 0 : 1}`, zIndex: `1`, }} /> diff --git a/webapp/src/components/droppedweapons.jsx b/webapp/src/components/droppedweapons.jsx new file mode 100644 index 0000000..b4fecdb --- /dev/null +++ b/webapp/src/components/droppedweapons.jsx @@ -0,0 +1,43 @@ +import { useRef } from "react"; +import { getRadarPosition, teamEnum, colorSchemePallette, weaponList, calculatePositionWithScale } from "../utilities/utilities"; +import MaskedIcon from "./maskedicon"; + +const DroppedWeapon = ({ droppedWeaponData, mapData, settings, averageLatency, radarImage }) => { + + const radarPosition = getRadarPosition(mapData, { x: droppedWeaponData.m_x, y: droppedWeaponData.m_y }); + + const wepRef = useRef(); + const wepBounding = (wepRef.current && + wepRef.current.getBoundingClientRect()) || { width: 0, height: 0 }; + const scaledPos = calculatePositionWithScale(radarImage, radarPosition); + const radarImageTranslation = { + x: (scaledPos[0] - wepBounding.width * 0.5), + y: (scaledPos[1] - wepBounding.height * 0.5), + }; + +return ( +
+ + {(settings.droppedWeaponIgnoreNade && !weaponList["grenades"].includes(droppedWeaponData.m_name) || !settings.droppedWeaponIgnoreNade) && + + + } + +
+) + +}; + +export default DroppedWeapon; \ No newline at end of file diff --git a/webapp/src/components/grenade.jsx b/webapp/src/components/grenade.jsx new file mode 100644 index 0000000..51b236d --- /dev/null +++ b/webapp/src/components/grenade.jsx @@ -0,0 +1,104 @@ +import { useRef } from "react"; +import GrenadeEffects from "./grenadeeffects.jsx"; +import { getRadarPosition, teamEnum, calculatePositionWithScale } from "../utilities/utilities"; +import MaskedIcon from "./maskedicon.jsx"; + +const Grenade = ({ grenadeData, mapData, settings, averageLatency, radarImage, type }) => { + + const firePositions = grenadeData.m_firePositions || {}; + + const radarPosition = getRadarPosition(mapData, { x: grenadeData.m_x, y: grenadeData.m_y }); + + const grenRef = useRef(); + const grenBounding = (grenRef.current && + grenRef.current.getBoundingClientRect()) || { width: 0, height: 0 }; + const scaledPos = calculatePositionWithScale(radarImage, radarPosition); + const radarImageTranslation = { + x: (scaledPos[0] - grenBounding.width * 0.5), + y: (scaledPos[1] - grenBounding.height * 0.5), + }; + + if(type == "landed") { + if (grenadeData.m_type == "smoke") { + return ( + + ); + } else if (grenadeData.m_type == "molo") { + + return ( +
+ +
+ + {firePositions[0]!=null && firePositions.map((firePosition, index) => ( + + ))} + +
+ +
+ + + +
+ +
+ ); + + } + + } else { + return ( +
+ + + +
+ + ) + } +}; + +export default Grenade; \ No newline at end of file diff --git a/webapp/src/components/grenadeeffects.jsx b/webapp/src/components/grenadeeffects.jsx new file mode 100644 index 0000000..d7ed68a --- /dev/null +++ b/webapp/src/components/grenadeeffects.jsx @@ -0,0 +1,72 @@ +import { useRef } from "react"; +import { getRadarPosition, teamEnum, calculatePositionWithScale } from "../utilities/utilities"; + +const GrenadeEffects = ({ grenadeData, type, mapData, settings, averageLatency, radarImage }) => { + + let radarScale = 1; try { let scale = radarImage.style.scale; if (scale) radarScale = scale;} catch {} + + const smokeSize = 3 * radarScale; + const fireSize = 1.3 * radarScale; + const radarPosition = getRadarPosition(mapData, { x: grenadeData.m_x, y: grenadeData.m_y }); + + const grenRef = useRef(); + const grenBounding = (grenRef.current && + grenRef.current.getBoundingClientRect()) || { width: 0, height: 0 }; + + const scaledPos = calculatePositionWithScale(radarImage, radarPosition); + const radarImageTranslation = { + x: (scaledPos[0] - grenBounding.width * 0.5), + y: (scaledPos[1] - grenBounding.height * 0.5), + }; + + if (type == "smoke") { + return ( +
+ +
+ + + +
+ ); + } else if (type == "molo") { + return ( +
+ ); + } +}; + +export default GrenadeEffects; \ No newline at end of file diff --git a/webapp/src/components/maskedicon.jsx b/webapp/src/components/maskedicon.jsx index a42726b..3a3f183 100644 --- a/webapp/src/components/maskedicon.jsx +++ b/webapp/src/components/maskedicon.jsx @@ -1,11 +1,11 @@ const MaskedIcon = ({ path, height, color }) => { return (
diff --git a/webapp/src/components/player.jsx b/webapp/src/components/player.jsx index 2ccd4b1..970c52e 100644 --- a/webapp/src/components/player.jsx +++ b/webapp/src/components/player.jsx @@ -1,6 +1,5 @@ import { useRef, useState, useEffect } from "react"; -import { getRadarPosition, playerColors } from "../utilities/utilities"; - +import { getRadarPosition, playerColors, calculatePositionWithScale, calculateMapOffsetForCentering } from "../utilities/utilities"; let playerRotations = []; const calculatePlayerRotation = (playerData) => { @@ -18,18 +17,28 @@ const Player = ({ playerData, mapData, radarImage, localTeam, averageLatency, se const [lastKnownPosition, setLastKnownPosition] = useState(null); const radarPosition = getRadarPosition(mapData, playerData.m_position) || { x: 0, y: 0 }; const invalidPosition = radarPosition.x <= 0 && radarPosition.y <= 0; - const playerRef = useRef(); const playerBounding = (playerRef.current && playerRef.current.getBoundingClientRect()) || { width: 0, height: 0 }; const playerRotation = calculatePlayerRotation(playerData); + const [scaledSize, setScaledSize] = useState(0.7 * settings.dotSize); - const radarImageBounding = (radarImage !== undefined && - radarImage.getBoundingClientRect()) || { width: 0, height: 0 }; - - const scaledSize = 0.7 * settings.dotSize; + useEffect(() => { + if (window.innerHeight<=500) setScaledSize(0.7 * settings.dotSize+1.5); else setScaledSize(0.7 * settings.dotSize); + }, [window.innerHeight]) - // Store the last known position when the player dies + useEffect(() => { + if (settings.showOnlyEnemies && playerData.m_team === localTeam) { + if (!settings.followYourself) { + setScaledSize(0.0); + } else if (settings.followYourself && playerData.m_steam_id!=settings.whichPlayerAreYou) { + setScaledSize(0.0); + } else { + setScaledSize(0.7 * settings.dotSize); + } + if (settings.showAllNames) {settings.showAllNames = false;} } else { setScaledSize(0.7 * settings.dotSize) } + }, [settings.showOnlyEnemies, settings.followYourself]) + useEffect(() => { if (playerData.m_is_dead) { if (!lastKnownPosition) { @@ -38,15 +47,29 @@ const Player = ({ playerData, mapData, radarImage, localTeam, averageLatency, se } else { setLastKnownPosition(null); } - }, [playerData.m_is_dead, radarPosition, lastKnownPosition]); + }, [playerData.m_is_dead, radarPosition]); const effectivePosition = playerData.m_is_dead ? lastKnownPosition || { x: 0, y: 0 } : radarPosition; + const scaledPos = calculatePositionWithScale(radarImage, effectivePosition); const radarImageTranslation = { - x: radarImageBounding.width * effectivePosition.x - playerBounding.width * 0.5, - y: radarImageBounding.height * effectivePosition.y - playerBounding.height * 0.5, + x: (scaledPos[0] - playerBounding.width * 0.5), + y: (scaledPos[1] - playerBounding.height * 0.5), }; + if (playerData.m_steam_id==settings.whichPlayerAreYou) { + if (radarImage && mapData && playerData.m_position && settings.followYourself) { + const radarOffset = calculateMapOffsetForCentering(playerData.m_position, radarImage, mapData); + if (radarImage.getAttribute("isbeingdragged")=="false") { + radarImage.setAttribute("moveoverride", "true") + radarImage.setAttribute("newtransx", `${Math.floor(radarOffset.x)}`) + radarImage.setAttribute("newtransy", `${Math.floor(radarOffset.y)}`) + } + } else if (radarImage) { + radarImage.setAttribute("moveoverride", "false") + } + } + return (
) : null} + + {/* Rotating container for player elements */}
{/* View cone (kept exactly as it was) */} - {settings.showViewCones && !playerData.m_is_dead && ( + {(settings.showOnlyEnemies && playerData.m_team === localTeam && settings.showViewCones && !playerData.m_is_dead) || (settings.showViewCones && !playerData.m_is_dead) && (
)}
+ {(mapData.leveling && !playerData.m_is_dead) && ( + + )}
); }; diff --git a/webapp/src/components/playercard.jsx b/webapp/src/components/playercard.jsx index 31abd50..22a20fb 100644 --- a/webapp/src/components/playercard.jsx +++ b/webapp/src/components/playercard.jsx @@ -1,47 +1,74 @@ import { useState, useEffect } from "react"; import MaskedIcon from "./maskedicon"; -import { playerColors, teamEnum } from "../utilities/utilities"; +import { playerColors, teamEnum, colorSchemePallette } from "../utilities/utilities"; -const PlayerCard = ({ playerData, isOnRightSide }) => { +const PlayerCard = ({ playerData, isOnRightSide, settings }) => { const [modelName, setModelName] = useState(playerData.m_model_name); + function getArmor(armor) { + if (armor<=100 && armor>0) { + return armor; + } else { + return 0; + } + } + useEffect(() => { - if (playerData.m_model_name) + if (playerData.m_model_name&&!playerData.m_is_dead) setModelName(playerData.m_model_name); - }, [playerData.m_model_name]); + else + setModelName("cs_observer"); + }, [playerData.m_model_name, playerData.m_is_dead]); return (
  • -
    +
    +
    - window.open( - `https://steamcommunity.com/profiles/${playerData.m_steam_id}`, - "_blank", - "noopener,noreferrer" - ) - } + className={`flex flex-col gap-[0.375rem] justify-center items-center`} > - {playerData.m_name} +
    + window.open( + `https://steamcommunity.com/profiles/${playerData.m_steam_id}`, + "_blank", + "noopener,noreferrer" + ) + } + > + {playerData.m_name} +
    + {/*
    */} +
    + + +
    +
    -
    - +
    { +
    +
    +
    {playerData.m_health}
    @@ -71,9 +104,9 @@ const PlayerCard = ({ playerData, isOnRightSide }) => { (playerData.m_has_helmet && `kevlar_helmet`) || `kevlar` }.svg`} height={16} - color={`bg-radar-secondary`} + color={`${colorSchemePallette[settings.colorScheme][1]}`} /> - {playerData.m_armor} + {getArmor(playerData.m_armor)}
  • @@ -85,8 +118,8 @@ const PlayerCard = ({ playerData, isOnRightSide }) => { color={`${ (playerData.m_weapons.m_active == playerData.m_weapons.m_primary && - `bg-radar-primary`) || - `bg-radar-secondary` + `${colorSchemePallette[settings.colorScheme][0]}`) || + `${colorSchemePallette[settings.colorScheme][1]}` }`} /> )} @@ -98,8 +131,8 @@ const PlayerCard = ({ playerData, isOnRightSide }) => { color={`${ (playerData.m_weapons.m_active == playerData.m_weapons.m_secondary && - `bg-radar-primary`) || - `bg-radar-secondary` + `${colorSchemePallette[settings.colorScheme][0]}`) || + `${colorSchemePallette[settings.colorScheme][1]}` }`} /> )} @@ -113,8 +146,8 @@ const PlayerCard = ({ playerData, isOnRightSide }) => { height={28} color={`${ (playerData.m_weapons.m_active == melee && - `bg-radar-primary`) || - `bg-radar-secondary` + `${colorSchemePallette[settings.colorScheme][0]}`) || + `${colorSchemePallette[settings.colorScheme][1]}` }`} /> ))} @@ -135,8 +168,8 @@ const PlayerCard = ({ playerData, isOnRightSide }) => { height={28} color={`${ (playerData.m_weapons.m_active == utility && - `bg-radar-primary`) || - `bg-radar-secondary` + `${colorSchemePallette[settings.colorScheme][0]}`) || + `${colorSchemePallette[settings.colorScheme][1]}` }`} /> ))} @@ -155,7 +188,8 @@ const PlayerCard = ({ playerData, isOnRightSide }) => { ].map((_, i) => (
    ))} @@ -164,7 +198,7 @@ const PlayerCard = ({ playerData, isOnRightSide }) => { )) || (playerData.m_team == teamEnum.terrorist && @@ -175,8 +209,8 @@ const PlayerCard = ({ playerData, isOnRightSide }) => { color={ ((playerData.m_weapons && playerData.m_weapons.m_active) == `c4` && - `bg-radar-primary`) || - `bg-radar-secondary` + `${colorSchemePallette[settings.colorScheme][0]}`) || + `${colorSchemePallette[settings.colorScheme][1]}` } /> ))} diff --git a/webapp/src/components/radar.jsx b/webapp/src/components/radar.jsx index 6a361ed..134b497 100644 --- a/webapp/src/components/radar.jsx +++ b/webapp/src/components/radar.jsx @@ -1,6 +1,9 @@ -import { useRef } from "react"; +import { useRef, useState } from "react"; +import Draggable from "./Draggable" import Player from "./player"; import Bomb from "./bomb"; +import Grenade from "./grenade"; +import DroppedWeapon from "./droppedweapons"; const Radar = ({ playerArray, @@ -9,13 +12,30 @@ const Radar = ({ localTeam, averageLatency, bombData, - settings + settings, + grenadeData, + droppedWeaponsData, }) => { const radarImageRef = useRef(); + const [radarScale, setRadarScale] = useState(1); + + const onScroll = (e) => { + const delta = e.deltaY * -0.001; + const newScale = radarScale + delta; + if (newScale>0.3&&newScale<4) setRadarScale(newScale) + }; + + return (
    - + + + + {playerArray.map((player) => ( )} + + {grenadeData && grenadeData["landed"]!={} && settings.showGrenades && grenadeData["landed"].map((grenade) => ( + + ))} + + {grenadeData && grenadeData["thrown"]!={} && settings.showGrenades && grenadeData["thrown"].map((grenade) => ( + + ))} + + {settings.showDroppedWeapons && droppedWeaponsData && droppedWeaponsData!={} && droppedWeaponsData.map((droppedWeapon) => ( + + ))} +
    ); }; diff --git a/webapp/src/main.jsx b/webapp/src/main.jsx index 9981940..31d28fb 100644 --- a/webapp/src/main.jsx +++ b/webapp/src/main.jsx @@ -1,6 +1,6 @@ import React from 'react' import ReactDOM from 'react-dom/client' -import App from './App.jsx' +import App from './app.jsx' ReactDOM.createRoot(document.getElementById('root')).render( diff --git a/webapp/src/utilities/utilities.jsx b/webapp/src/utilities/utilities.jsx index ceb13b8..2a1d834 100644 --- a/webapp/src/utilities/utilities.jsx +++ b/webapp/src/utilities/utilities.jsx @@ -1,4 +1,6 @@ export const getRadarPosition = (mapData, entityCoords) => { + const divide = 1024; + if (!entityCoords.x || !entityCoords.y) { return { x: 0, y: 0 }; } @@ -8,13 +10,71 @@ export const getRadarPosition = (mapData, entityCoords) => { } const position = { - x: (entityCoords.x - mapData.x) / mapData.scale / 1024, - y: (((entityCoords.y - mapData.y) / mapData.scale) * -1.0) / 1024, + x: (entityCoords.x - mapData.x) / mapData.scale / divide, + y: (((entityCoords.y - mapData.y) / mapData.scale) * -1.0) / divide, }; return position; }; +export const calculatePositionWithScale = (radarImage, radarPosition) => { + const radarImageBounding = (radarImage !== undefined && + radarImage.getBoundingClientRect()) || { width: 0, height: 0 }; + let transformRadar = [0,0,0] + try { + let match = radarImage.style.transform.match(/translate\(([^,]+)px,\s*([^)]+)px\)/) + if (match) transformRadar = match; + } catch {} + let radarScale = 1; + try { + let scale = radarImage.style.scale; + if (scale) radarScale = scale; + } catch {} + + const unscaledWidth = radarImageBounding.width / radarScale; + const unscaledHeight = radarImageBounding.height / radarScale; + const centerX = unscaledWidth / 2; + const centerY = unscaledHeight / 2; + + const posX = unscaledWidth * radarPosition.x + Number(transformRadar[1]); + const posY = unscaledHeight * radarPosition.y + Number(transformRadar[2]); + + const scaledPosX = centerX + (posX - centerX) * radarScale; + const scaledPosY = centerY + (posY - centerY) * radarScale; + return [scaledPosX, scaledPosY] +} + +export const calculateMapOffsetForCentering = (playerPosition, radarImage, mapData) => { + if (!playerPosition || !radarImage) { + return { x: 0, y: 0 }; + } + + const radarPosition = getRadarPosition(mapData, playerPosition) || { x: 0, y: 0 }; + let radarScale = 1; + if (radarImage) radarScale = radarImage.style.scale; + + if (!radarPosition) { + return { x: 0, y: 0 }; + } + + const radarImageBounding = (radarImage !== undefined && + radarImage.getBoundingClientRect()) || { width: 0, height: 0 }; + + const unscaledWidth = radarImageBounding.width / radarScale; + const unscaledHeight = radarImageBounding.height / radarScale; + + const playerXOnMap = unscaledWidth * radarPosition.x + const playerYOnMap = unscaledHeight * radarPosition.y + + const centerX = radarImage.width/2; + const centerY = radarImage.height/2; + + const offsetX = centerX - playerXOnMap; + const offsetY = centerY - playerYOnMap; + + return { x: offsetX, y: offsetY }; +}; + export const playerColors = [ // blue "#84c8ed", @@ -35,9 +95,117 @@ export const playerColors = [ "#ffffff", ]; +export const weaponList = { + "pistols": [ + "cs75a", + "deagle", + "elite", //dual berettas + "fiveseven", + "glock", + "hkp2000", //p2000 + "p250", + "revolver", + "tec9", + "usp-silencer", + "usp-silencer-off" + ], + "rifles": [ + "ak47", + "aug", + "awp", + "famas", + "g3sg1", + "galilar", + "m4a1", + "m4a1-silencer", + "m4a1-silencer-off", + "scar20", + "sg556", + "ssg08" + ], + "smgs": [ + "mac10", + "mp5sd", + "mp7", + "mp9", + "bizon", + "p90", + "ump45" + ], + "heavys": [ + "mag7", + "nova", + "sawedoff", + "xm1014", + "m249", + "negev" + ], + "grenades": [ + "smokegrenade", + "molotov", + "incgrenade", + "hegrenade", + "flashbang", + "decoy", + ] +} + export const teamEnum = { none: 0, spectator: 1, terrorist: 2, counterTerrorist: 3, }; + +export const colorSchemePallette = { + undefined: [ + "#b1d0e7", + "#6492b4" + ], + "default": [ + "#b1d0e7", + "#6492b4" + ], + "white": [ + "#FFFFFF", + "#a1a1a1ff" + ], + "light_blue": [ + "#32c8ffff", + "#0183aaff" + ], + "dark_blue": [ + "#1685c0ff", + "#005383ff" + ], + "purple": [ + "#cca0ffff", + "#9933FF" + ], + "red": [ + "#ff6f6fff", + "#CC3333" + ], + "orange": [ + "#ffbd60ff", + "#FF8C00" + ], + + "yellow": [ + "#ffe47aff", + "#ffe600ff" + ], + + "green": [ + "#71d492ff", + "#00B050" + ], + "light_green": [ + "#a9ffa9ff", + "#41ff41ff" + ], + "pink": [ + "#ff8fa0ff", + "#ff3d9eff" + ] +} \ No newline at end of file diff --git a/webapp/tailwind.config.js b/webapp/tailwind.config.js index 2a06269..a84f15e 100644 --- a/webapp/tailwind.config.js +++ b/webapp/tailwind.config.js @@ -10,9 +10,13 @@ export default { radar: { "primary": "#b1d0e7", "secondary": "#6492b4", + "green": "#50904c", - "red": "#c90b0be6" - } + "red": "#c90b0be6", + + "redbutton": "rgb(70, 0, 0)", + "redbutton_hover": "rgb(150, 0, 0)" + }, }, }, },