├── LICENSE ├── README.md ├── build_win32.bat └── know_thy_enemy.cpp /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Legal Code 2 | 3 | CC0 1.0 Universal 4 | 5 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 | HEREUNDER. 13 | 14 | Statement of Purpose 15 | 16 | The laws of most jurisdictions throughout the world automatically confer 17 | exclusive Copyright and Related Rights (defined below) upon the creator 18 | and subsequent owner(s) (each and all, an "owner") of an original work of 19 | authorship and/or a database (each, a "Work"). 20 | 21 | Certain owners wish to permanently relinquish those rights to a Work for 22 | the purpose of contributing to a commons of creative, cultural and 23 | scientific works ("Commons") that the public can reliably and without fear 24 | of later claims of infringement build upon, modify, incorporate in other 25 | works, reuse and redistribute as freely as possible in any form whatsoever 26 | and for any purposes, including without limitation commercial purposes. 27 | These owners may contribute to the Commons to promote the ideal of a free 28 | culture and the further production of creative, cultural and scientific 29 | works, or to gain reputation or greater distribution for their Work in 30 | part through the use and efforts of others. 31 | 32 | For these and/or other purposes and motivations, and without any 33 | expectation of additional consideration or compensation, the person 34 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 | is an owner of Copyright and Related Rights in the Work, voluntarily 36 | elects to apply CC0 to the Work and publicly distribute the Work under its 37 | terms, with knowledge of his or her Copyright and Related Rights in the 38 | Work and the meaning and intended legal effect of CC0 on those rights. 39 | 40 | 1. Copyright and Related Rights. A Work made available under CC0 may be 41 | protected by copyright and related or neighboring rights ("Copyright and 42 | Related Rights"). Copyright and Related Rights include, but are not 43 | limited to, the following: 44 | 45 | i. the right to reproduce, adapt, distribute, perform, display, 46 | communicate, and translate a Work; 47 | ii. moral rights retained by the original author(s) and/or performer(s); 48 | iii. publicity and privacy rights pertaining to a person's image or 49 | likeness depicted in a Work; 50 | iv. rights protecting against unfair competition in regards to a Work, 51 | subject to the limitations in paragraph 4(a), below; 52 | v. rights protecting the extraction, dissemination, use and reuse of data 53 | in a Work; 54 | vi. database rights (such as those arising under Directive 96/9/EC of the 55 | European Parliament and of the Council of 11 March 1996 on the legal 56 | protection of databases, and under any national implementation 57 | thereof, including any amended or successor version of such 58 | directive); and 59 | vii. other similar, equivalent or corresponding rights throughout the 60 | world based on applicable law or treaty, and any national 61 | implementations thereof. 62 | 63 | 2. Waiver. To the greatest extent permitted by, but not in contravention 64 | of, applicable law, Affirmer hereby overtly, fully, permanently, 65 | irrevocably and unconditionally waives, abandons, and surrenders all of 66 | Affirmer's Copyright and Related Rights and associated claims and causes 67 | of action, whether now known or unknown (including existing as well as 68 | future claims and causes of action), in the Work (i) in all territories 69 | worldwide, (ii) for the maximum duration provided by applicable law or 70 | treaty (including future time extensions), (iii) in any current or future 71 | medium and for any number of copies, and (iv) for any purpose whatsoever, 72 | including without limitation commercial, advertising or promotional 73 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 | member of the public at large and to the detriment of Affirmer's heirs and 75 | successors, fully intending that such Waiver shall not be subject to 76 | revocation, rescission, cancellation, termination, or any other legal or 77 | equitable action to disrupt the quiet enjoyment of the Work by the public 78 | as contemplated by Affirmer's express Statement of Purpose. 79 | 80 | 3. Public License Fallback. Should any part of the Waiver for any reason 81 | be judged legally invalid or ineffective under applicable law, then the 82 | Waiver shall be preserved to the maximum extent permitted taking into 83 | account Affirmer's express Statement of Purpose. In addition, to the 84 | extent the Waiver is so judged Affirmer hereby grants to each affected 85 | person a royalty-free, non transferable, non sublicensable, non exclusive, 86 | irrevocable and unconditional license to exercise Affirmer's Copyright and 87 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 | maximum duration provided by applicable law or treaty (including future 89 | time extensions), (iii) in any current or future medium and for any number 90 | of copies, and (iv) for any purpose whatsoever, including without 91 | limitation commercial, advertising or promotional purposes (the 92 | "License"). The License shall be deemed effective as of the date CC0 was 93 | applied by Affirmer to the Work. Should any part of the License for any 94 | reason be judged legally invalid or ineffective under applicable law, such 95 | partial invalidity or ineffectiveness shall not invalidate the remainder 96 | of the License, and in such case Affirmer hereby affirms that he or she 97 | will not (i) exercise any of his or her remaining Copyright and Related 98 | Rights in the Work or (ii) assert any associated claims and causes of 99 | action with respect to the Work, in either case contrary to Affirmer's 100 | express Statement of Purpose. 101 | 102 | 4. Limitations and Disclaimers. 103 | 104 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 | surrendered, licensed or otherwise affected by this document. 106 | b. Affirmer offers the Work as-is and makes no representations or 107 | warranties of any kind concerning the Work, express, implied, 108 | statutory or otherwise, including without limitation warranties of 109 | title, merchantability, fitness for a particular purpose, non 110 | infringement, or the absence of latent or other defects, accuracy, or 111 | the present or absence of errors, whether or not discoverable, all to 112 | the greatest extent permissible under applicable law. 113 | c. Affirmer disclaims responsibility for clearing rights of other persons 114 | that may apply to the Work or any use thereof, including without 115 | limitation any person's Copyright and Related Rights in the Work. 116 | Further, Affirmer disclaims responsibility for obtaining any necessary 117 | consents, permissions or other rights required for any use of the 118 | Work. 119 | d. Affirmer understands and acknowledges that Creative Commons is not a 120 | party to this document and has no duty or obligation with respect to 121 | this CC0 or use of the Work. 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [Know thy enemy discord](https://discord.gg/6xDbA64rZc) for update notifications and chat. 2 | 3 | > Know thy enemy and know thyself, and need not fear the result of a hundred battles. 4 | > 5 | > \- Sun Tzu 6 | 7 | Some parts of the code are closed source, since it is related to arcdps/GW2 internals. 8 | 9 | # Know-thy-enemy [WvW] 10 | [![](https://img.shields.io/github/downloads/typedeck0/know-thy-enemy/total?style=plastic)](../../releases) 11 | 12 | Tired of commanders saying "They're *twice* our size!"? Well now you can put a number on it! 13 | 14 | Counts the amount and type of player enemies (that your squad hits or is hit by) in an arcdps fight instance (resets when arcdps does). 15 | 16 | ![image](https://user-images.githubusercontent.com/113395677/222940654-ff55d512-85e5-42dc-a289-9075641ce6be.png) 17 |      18 | ![image](https://user-images.githubusercontent.com/113395677/222940678-08786dca-7a06-4b8d-8e75-18ba340e4422.png) 19 |      20 | ![image](https://user-images.githubusercontent.com/113395677/226063666-4c092d1b-0017-421c-9d99-901a53ae5b00.png) 21 | 22 | Columns and small names options. 23 | 24 | ![image](https://user-images.githubusercontent.com/113395677/229323981-305f5725-00c3-439d-a431-a8ee919c032b.png) 25 | 26 | ## ~~Build~~ 27 | ~~Clone. Download [ImGui 1.8](https://github.com/ocornut/imgui/tree/v1.80). Extract the Imgui to the cloned directory. Run the bat file. The DLL will be in the out directory.~~ 28 | ~~Due to some parts being closed source the resulting DLL will not work correctly.~~ 29 | 30 | ~~[msvc](https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2022) x64:~~ 31 | ``` 32 | build_win32.bat 33 | ``` 34 | 35 | ## Install: 36 | make sure you have [arcdps](https://www.deltaconnected.com/arcdps/) 37 | 38 | move the know_thy_enemy.dll([releases](../../releases)) to the folder where the gw2 exe is or the bin64 folder (its in the same folder as your gw2 exe) 39 | 40 | then in-game open the arcdps options window (alt-shift-t) 41 | 42 | Under the "Extensions" tab, you will see a sub page called Know thy enemy, where you can enable and disable the extension. 43 | 44 | 45 | ## Use: 46 | Right click the window to toggle between a history of fights and the current instance 47 | 48 | ## Comments, concerns, and/or proclamations 49 | 50 | post an [issue](../../issues) 51 | 52 | hmu in this new discord https://discord.gg/6xDbA64rZc 53 | 54 | or in-game at woodel.6318 55 | -------------------------------------------------------------------------------- /build_win32.bat: -------------------------------------------------------------------------------- 1 | @REM Build for Visual Studio compiler. Run your copy of vcvars32.bat or vcvarsall.bat to setup command-line compiler. 2 | @set OUT_DIR=out 3 | @set OUT_EXE=know-thy-enemy.dll 4 | @set INCLUDES=/I.\imgui /I.\imgui\backends /I "%WindowsSdkDir%Include\um" /I "%WindowsSdkDir%Include\shared" /I "%DXSDK_DIR%Include" 5 | @set SOURCES=know_thy_enemy.cpp .\imgui\backends\imgui_impl_dx11.cpp .\imgui\backends\imgui_impl_win32.cpp .\imgui\imgui*.cpp 6 | @set LIBS=/LIBPATH:"%DXSDK_DIR%/Lib/x86" d3d11.lib d3dcompiler.lib 7 | mkdir %OUT_DIR% 8 | cl /LD /EHsc /WX /O2 %INCLUDES% /D UNICODE /D _UNICODE %SOURCES% /Fe%OUT_DIR%/%OUT_EXE% /Fo%OUT_DIR%/ /link %LIBS% 9 | 10 | -------------------------------------------------------------------------------- /know_thy_enemy.cpp: -------------------------------------------------------------------------------- 1 | #define WIN32_LEAN_AND_MEAN 2 | #include 3 | #include 4 | #include 5 | #include "imgui/imgui.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "../include/types.h" 16 | #include "../include/arcdeeps.h" 17 | void* filelog; 18 | 19 | const std::array pe_name_lut = { 20 | "Unknown", 21 | "Guardian", 22 | "Dragonhunter", 23 | "Firebrand", 24 | "Willbender", 25 | "Warrior", 26 | "Berserker", 27 | "Spellbreaker", 28 | "Bladesworn", 29 | "Engineer", 30 | "Scrapper", 31 | "Holosmith", 32 | "Mechanist", 33 | "Ranger", 34 | "Druid", 35 | "Soulbeast", 36 | "Untamed", 37 | "Thief", 38 | "Daredevil", 39 | "Deadeye", 40 | "Specter", 41 | "Elementalist", 42 | "Tempest", 43 | "Weaver", 44 | "Catalyst", 45 | "Mesmer", 46 | "Chronomancer", 47 | "Mirage", 48 | "Virtuoso", 49 | "Necromancer", 50 | "Reaper", 51 | "Scourge", 52 | "Harbinger", 53 | "Revenant", 54 | "Herald", 55 | "Renegade", 56 | "Vindicator" 57 | }; 58 | 59 | const std::array pe_short_name_lut = { 60 | "Ukn", 61 | "Gdn", 62 | "Dgh", 63 | "Fbd", 64 | "Wbd", 65 | "War", 66 | "Brs", 67 | "Spb", 68 | "Bds", 69 | "Eng", 70 | "Scr", 71 | "Hls", 72 | "Mec", 73 | "Rgr", 74 | "Dru", 75 | "Slb", 76 | "Unt", 77 | "Thf", 78 | "Dar", 79 | "Ded", 80 | "Spe", 81 | "Ele", 82 | "Tmp", 83 | "Wea", 84 | "Cat", 85 | "Mes", 86 | "Chr", 87 | "Mir", 88 | "Vir", 89 | "Nec", 90 | "Rea", 91 | "Scg", 92 | "Har", 93 | "Rev", 94 | "Her", 95 | "Ren", 96 | "Vin" 97 | }; 98 | 99 | std::mutex mtx; 100 | 101 | const uint8_t MAX_HISTORY_SIZE = 8; //power of 2 102 | 103 | // int is_wvw_state = -1; 104 | bool mod_key1 = false; 105 | bool mod_key2 = false; 106 | uint16_t tab_team_idx = 0; 107 | bool override_tab_max_switch = false; 108 | 109 | char* arcvers; 110 | extern "C" __declspec(dllexport) void* get_init_addr(char* arcversion, ImGuiContext* imguictx, void* id3dptr, HANDLE arcdll, void* mallocfn, void* freefn, uint32_t d3dversion); 111 | extern "C" __declspec(dllexport) void* get_release_addr(); 112 | 113 | /* arcdps exports */ 114 | size_t(*arclog)(char*); 115 | void(*arccolors)(ImVec4**); 116 | wchar_t*(*get_settings_path)(); 117 | uint64_t(*get_ui_settings)(); 118 | uint64_t(*get_key_settings)(); 119 | 120 | char* gw2_ctx = nullptr; 121 | char* arcdll = 0; 122 | uint16_t* pNumAgents = 0; 123 | uint16_t* pAgentIds = 0; 124 | 125 | struct TeamHistories { 126 | std::vector team_ids; 127 | std::vector> histories; 128 | }; 129 | 130 | 131 | std::vector ids = std::vector(); 132 | TeamHistories team_history_map = { 133 | std::vector(), 134 | std::vector>() 135 | }; 136 | std::array, MAX_HISTORY_SIZE> history_labels; 137 | 138 | settings kte_settings = {}; 139 | int kte_loaded = 0; 140 | bool reset_pos = false; 141 | 142 | int history_radio_state = 0; 143 | int cur_history_idx = 0; 144 | int history_to_disp_idx = 0; 145 | 146 | /* dll attach -- from winapi */ 147 | void dll_init(const HANDLE hModule) { 148 | return; 149 | } 150 | 151 | /* dll detach -- from winapi */ 152 | void dll_exit() { 153 | return; 154 | } 155 | 156 | /* dll main -- winapi */ 157 | BOOL APIENTRY DllMain(const HANDLE hModule, const DWORD ulReasonForCall, const LPVOID lpReserved) { 158 | switch (ulReasonForCall) { 159 | case DLL_PROCESS_ATTACH: dll_init(hModule); break; 160 | case DLL_PROCESS_DETACH: dll_exit(); break; 161 | 162 | case DLL_THREAD_ATTACH: break; 163 | case DLL_THREAD_DETACH: break; 164 | } 165 | return 1; 166 | } 167 | 168 | /* log to extensions tab in arcdps log window, thread/async safe */ 169 | void log_arc(char* str) { 170 | if (arclog) 171 | arclog(str); 172 | return; 173 | } 174 | 175 | ImVec4* color_array[5]; 176 | void init_colors() 177 | { 178 | if (arccolors) 179 | arccolors(color_array); 180 | return; 181 | } 182 | 183 | /* log to arcdps.log, thread/async safe */ 184 | void log_file(char* str) { 185 | size_t(*log)(char*) = (size_t(*)(char*))filelog; 186 | if (log) (*log)(str); 187 | return; 188 | } 189 | 190 | /* window callback -- return is assigned to umsg (return zero to not be processed by arcdps or game) */ 191 | uintptr_t mod_wnd(const HWND hWnd, const UINT uMsg, const WPARAM wParam, const LPARAM lParam) 192 | { 193 | if ((get_ui_settings() >> 2) & 1) 194 | { 195 | kte_settings.wFlags |= ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize; 196 | if (uMsg == WM_KEYDOWN || uMsg == WM_SYSKEYDOWN) 197 | { 198 | uint64_t keys = get_key_settings(); 199 | uint16_t* mod_key = (uint16_t*)&keys; 200 | if (wParam == *mod_key) 201 | { 202 | mod_key1 = true; 203 | } 204 | if (wParam == *(mod_key+1)) 205 | { 206 | mod_key2 = true; 207 | } 208 | } 209 | 210 | else if (uMsg == WM_KEYUP || uMsg == WM_SYSKEYUP) 211 | { 212 | uint64_t keys = get_key_settings(); 213 | uint16_t* mod_key = (uint16_t*)&keys; 214 | if (wParam == *mod_key) 215 | { 216 | mod_key1 = false; 217 | } 218 | if (wParam == *(mod_key+1)) 219 | { 220 | mod_key2 = false; 221 | } 222 | } 223 | if (mod_key1 && mod_key2) 224 | kte_settings.wFlags &= ~(ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize); 225 | } 226 | else 227 | kte_settings.wFlags &= ~(ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize); 228 | return uMsg; 229 | } 230 | 231 | unsigned int logstart_time = 0xFFFFFFFF; 232 | unsigned int last_cbt_evt = GetTickCount(); 233 | combat_state cmb_state = combat_state::OUT_BATTLE; 234 | bool log_start = false; 235 | bool log_end = false; 236 | 237 | unsigned short(* get_team_id)(const void* agent) = nullptr; 238 | 239 | void setup_new_histories() 240 | { 241 | bool all_zero = true; 242 | for (auto& team : team_history_map.histories) 243 | all_zero = all_zero && team[cur_history_idx].total == 0; 244 | if (!all_zero) 245 | { 246 | cur_history_idx = (cur_history_idx + 1) & (MAX_HISTORY_SIZE - 1); 247 | for (auto& team : team_history_map.histories) 248 | { 249 | if (team[cur_history_idx].total != 0) 250 | { 251 | team[cur_history_idx].total = 0; 252 | for(auto& profelite : team[cur_history_idx].profelites) 253 | { 254 | profelite.count = 0; 255 | } 256 | } 257 | } 258 | history_to_disp_idx = cur_history_idx; 259 | history_radio_state = 0; 260 | ids.clear(); 261 | override_tab_max_switch = false; 262 | } 263 | } 264 | 265 | uint16_t my_team_id = 0; 266 | bool try_register_agent(const uint16_t id) 267 | { 268 | char* cur_agent; 269 | if (id == 0) 270 | return false; 271 | else if (((cur_agent = read_arc_addr_from_gw2(get_agent(gw2_ctx, id))) != 0) && is_valid_foe(cur_agent)) 272 | { 273 | uint8_t self_maybe = is_self(cur_agent); 274 | unsigned short instid = get_instid(cur_agent); 275 | unsigned short team_id = 0; 276 | 277 | team_id = get_team(cur_agent); 278 | 279 | if (team_id == 0 || team_id == 0xFFFF) 280 | return false; 281 | if (self_maybe) 282 | my_team_id = team_id; 283 | if (team_id == my_team_id) 284 | return false; 285 | 286 | unsigned short prof = get_prof(cur_agent); //0 287 | unsigned short elite = get_elite(cur_agent); //2 288 | 289 | if (std::find(ids.begin(), ids.end(), instid) != ids.end()) //if found 290 | { 291 | return false; 292 | } 293 | if(log_end) 294 | { 295 | log_end = false; 296 | setup_new_histories(); 297 | } 298 | 299 | std::array* team = 0; 300 | for (int i = 0; i < team_history_map.team_ids.size(); i++) 301 | { 302 | if (team_history_map.team_ids[i] == team_id) 303 | { 304 | team = &team_history_map.histories[i]; 305 | break; 306 | } 307 | } 308 | if (team == 0) 309 | { 310 | team_history_map.team_ids.push_back(team_id); 311 | team_history_map.histories.push_back(std::array()); 312 | team = &team_history_map.histories.back(); 313 | } 314 | ids.push_back(instid); 315 | s_team_battle* cur_team = &(*team)[cur_history_idx]; 316 | 317 | uint8_t idx = 1 + ((prof & 0xFF) - 1)*sizeof(s_profelite); 318 | s_profelite* pe = &(cur_team->profelites[idx + 0]); 319 | pe[0].count += (pe[0].elite == (elite & 0xFF)); //core 320 | pe[1].count += (pe[1].elite == (elite & 0xFF)); //elite 321 | pe[2].count += (pe[2].elite == (elite & 0xFF)); //elite 322 | pe[3].count += (pe[3].elite == (elite & 0xFF)); //elite 323 | cur_team->total++; 324 | return true; 325 | } 326 | return false; 327 | } 328 | 329 | /* combat callback -- may be called asynchronously, use id param to keep track of order, first event id will be 2. return ignored */ 330 | /* at least one participant will be party/squad or minion of, or a buff applied by squad in the case of buff remove. not all statechanges present, see evtc statechange enum */ 331 | uintptr_t mod_combat(const cbtevent* ev, const ag* src, const ag* dst, const char* skillname, const uint64_t id, const uint64_t revision) 332 | { 333 | if (!ev) 334 | { 335 | if (!src->elite) 336 | { 337 | if(src->prof) //add 338 | { 339 | if(dst->self) 340 | { 341 | // char buf[64] = {0}; 342 | // snprintf(buf, 64, "%llx|%llx", src->id, dst->id); 343 | // log_arc(buf); 344 | kte_settings.bToShow = isWvw(arcdll); 345 | gw2_ctx = get_arcdeeps((HMODULE)arcdll); 346 | kte_loaded = gw2_ctx != 0 && kte_settings.bToShow; 347 | if (!kte_loaded) 348 | log_arc("maybe? KTE NOT INIT\n"); 349 | } 350 | } 351 | } 352 | } 353 | else if(ev && kte_settings.bEnabled && kte_settings.bToShow) 354 | { 355 | if (ev->is_statechange == CBTS_LOGSTART) 356 | { 357 | logstart_time = GetTickCount(); 358 | } 359 | else if (ev->is_statechange == CBTS_LOGEND) 360 | { 361 | cmb_state = combat_state::OUT_BATTLE; 362 | _strtime_s(&history_labels[cur_history_idx][0], 32); 363 | snprintf(&history_labels[cur_history_idx][8], 32-9, " (%ds)", (GetTickCount() - logstart_time)/1000); 364 | log_end = true; 365 | } 366 | if (ev->is_activation || ev->is_buffremove || ev->is_statechange || ev->buff || src->elite == 0xFFFFFFFF || dst->elite == 0xFFFFFFFF || src->prof == 0 || dst->prof == 0) 367 | return 0; 368 | else if (src && dst && ev->value != 0) 369 | { 370 | if (gw2_ctx == 0) 371 | return 0; 372 | 373 | std::lock_guardlock(mtx); 374 | cmb_state = combat_state::IN_BATTLE; 375 | if (GetTickCount() - last_cbt_evt < 200) 376 | return 0; 377 | 378 | last_cbt_evt = GetTickCount(); 379 | 380 | for (uint16_t i = 0; i < *pNumAgents; i++) 381 | { 382 | uint16_t id = pAgentIds[i]; 383 | // snprintf(buf, 256, "n: %d i: %d id: %04X\n", *pNumAgents, i, id); 384 | // log_file(buf); 385 | try_register_agent(id); 386 | } 387 | } 388 | } 389 | return 0; 390 | } 391 | 392 | void options_end_proc(const char* windowname) 393 | { 394 | ImGui::Checkbox("Know thy enemy##1cb", &kte_settings.bEnabled); 395 | 396 | ImGui::Text("Red team id: "); 397 | ImGui::SameLine(); 398 | ImGui::InputInt("##redteamid", (int *)&kte_settings.red_team, 0, 0); 399 | ImGui::Text("Green team id:"); 400 | ImGui::SameLine(); 401 | ImGui::InputInt("##greenteamid", (int *)&kte_settings.green_team, 0, 0); 402 | ImGui::Text("Blue team id: "); 403 | ImGui::SameLine(); 404 | ImGui::InputInt("##blueteamid", (int *)&kte_settings.blue_team, 0, 0); 405 | 406 | ImGui::Text("Custom 1 name:"); 407 | ImGui::SameLine(); 408 | ImGui::InputText("##cteam1name", kte_settings.cteam1.name.data(), 32); 409 | ImGui::SameLine(); 410 | ImGui::Text("id:"); 411 | ImGui::SameLine(); 412 | ImGui::InputInt("##cteam1id", (int *)&kte_settings.cteam1.id, 0, 0); 413 | 414 | ImGui::Text("Custom 2 name:"); 415 | ImGui::SameLine(); 416 | ImGui::InputText("##cteam2name", kte_settings.cteam2.name.data(), 32); 417 | ImGui::SameLine(); 418 | ImGui::Text("id:"); 419 | ImGui::SameLine(); 420 | ImGui::InputInt("##cteam2id", (int *)&kte_settings.cteam2.id, 0, 0); 421 | 422 | ImGui::Text("Custom 3 name:"); 423 | ImGui::SameLine(); 424 | ImGui::InputText("##cteam3name", kte_settings.cteam3.name.data(), 32); 425 | ImGui::SameLine(); 426 | ImGui::Text("id:"); 427 | ImGui::SameLine(); 428 | ImGui::InputInt("##cteam3id", (int *)&kte_settings.cteam3.id, 0, 0); 429 | ImGui::NewLine(); 430 | ImGui::Separator(); 431 | if (ImGui::Button("Reset postition")) 432 | { 433 | reset_pos = true; 434 | } 435 | ImGui::Text(" "); 436 | if (ImGui::Button("Reset settings")) 437 | { 438 | kte_settings = settings(); 439 | std::wstring path = std::wstring(get_settings_path()); 440 | path = path.substr(0, path.find_last_of(L"\\")+1); 441 | path.append(L"know_thy_enemy_settings.txt"); 442 | std::fstream file(path.c_str(), std::fstream::out | std::fstream::trunc | std::ios::binary); 443 | if (file.good()) 444 | { 445 | file.write((char*)&kte_settings, sizeof(settings)); 446 | } 447 | file.close(); 448 | } 449 | } 450 | 451 | void options_windows_proc(const char* windowname) 452 | { 453 | // log_arc((char*)windowname); 454 | } 455 | 456 | void draw_bar(const float frac, const char* text, const ImVec4& color) 457 | { 458 | ImVec2 upper_left = ImGui::GetCursorScreenPos(); 459 | ImVec2 lower_right = ImVec2(upper_left.x + (ImGui::GetContentRegionAvail().x*frac), upper_left.y + ImGui::GetTextLineHeight() + 2); 460 | //ImGui::ProgressBar(frac, ImVec2(-1, 0), ""); 461 | ImGui::GetWindowDrawList()->AddRectFilled(upper_left, lower_right, ImGui::ColorConvertFloat4ToU32(color)); 462 | 463 | ImVec2 text_start = ImGui::GetCursorPos(); 464 | text_start.y += 1; //middle of bar 465 | float inner_pad = 5; // (ImGui::GetContentRegionAvail().x - ImGui::CalcTextSize(text).x) * 0.5; 466 | text_start.x += inner_pad; 467 | ImVec2 text_shadow = text_start; 468 | text_shadow.x += 1; 469 | text_shadow.y += 1; 470 | ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0,0,0,.6)); 471 | ImGui::SetCursorPos(text_shadow); 472 | ImGui::TextUnformatted(text, text + strlen(text)); 473 | ImGui::PopStyleColor(); 474 | 475 | ImGui::SetCursorPos(text_start); 476 | ImGui::TextUnformatted(text, text + strlen(text)); 477 | text_start.x -= inner_pad; 478 | 479 | text_start.y += ImGui::GetTextLineHeight() + 3; //end of bar + 2 pad 480 | ImGui::SetCursorPos(text_start); 481 | } 482 | 483 | void draw_history_menu() 484 | { 485 | if(team_history_map.team_ids.size() == 0) 486 | { 487 | ImGui::Text("No data..."); 488 | } 489 | else 490 | { 491 | if (ImGui::RadioButton("Current", history_radio_state == 0)) 492 | { 493 | history_radio_state = 0; 494 | history_to_disp_idx = cur_history_idx; 495 | ImGui::CloseCurrentPopup(); 496 | } 497 | int past_history_idx = (cur_history_idx - 1 + MAX_HISTORY_SIZE) & (MAX_HISTORY_SIZE - 1); 498 | for(int i = 0; i < MAX_HISTORY_SIZE-1; i++) 499 | { 500 | if(ImGui::RadioButton(&history_labels[past_history_idx][0], history_radio_state == (i+1))) 501 | { 502 | history_radio_state = i+1; 503 | history_to_disp_idx = past_history_idx; 504 | ImGui::CloseCurrentPopup(); 505 | } 506 | past_history_idx = (past_history_idx - 1 + MAX_HISTORY_SIZE) & (MAX_HISTORY_SIZE - 1); 507 | } 508 | } 509 | } 510 | 511 | void draw_style_menu() 512 | { 513 | bool bTitlebar = (kte_settings.wFlags & ImGuiWindowFlags_NoTitleBar) == 0; 514 | if (ImGui::Checkbox("title bar", &bTitlebar)) 515 | { 516 | kte_settings.wFlags ^= ImGuiWindowFlags_NoTitleBar; 517 | } 518 | bool bScrollbar = (kte_settings.wFlags & ImGuiWindowFlags_NoScrollbar) == 0; 519 | if (ImGui::Checkbox("scroll bar", &bScrollbar)) 520 | { 521 | kte_settings.wFlags ^= ImGuiWindowFlags_NoScrollbar; 522 | } 523 | bool bBackground = (kte_settings.wFlags & ImGuiWindowFlags_NoBackground) == 0; 524 | if (ImGui::Checkbox("background", &bBackground)) 525 | { 526 | kte_settings.wFlags ^= ImGuiWindowFlags_NoBackground; 527 | } 528 | ImGui::Checkbox("title bar background", &kte_settings.bTitleBg); 529 | ImGui::Checkbox("use columns", &kte_settings.bShowColumns); 530 | ImGui::Checkbox("use short names", &kte_settings.bShortNames); 531 | ImGui::Checkbox("hide while in combat", &kte_settings.bHideInCombat); 532 | } 533 | 534 | void imgui_team_class_bars(const s_team_battle& team_combatants_to_disp) 535 | { 536 | if (team_combatants_to_disp.total == 0) 537 | { 538 | if (kte_settings.bShortNames) 539 | draw_bar(1.f, "00 Tot", ImGui::GetStyleColorVec4(ImGuiCol_PlotHistogram)); 540 | else 541 | draw_bar(1.f, "00 Total", ImGui::GetStyleColorVec4(ImGuiCol_PlotHistogram)); 542 | return; 543 | } 544 | std::array combatants_to_disp = team_combatants_to_disp.profelites; 545 | uint8_t total = team_combatants_to_disp.total; 546 | std::sort(combatants_to_disp.begin(), combatants_to_disp.end(), [=](s_profelite& a, s_profelite& b) 547 | { 548 | return a.count > b.count; 549 | }); 550 | 551 | ImGui::PushStyleColor(ImGuiCol_Text, color_array[0][4]); 552 | 553 | char temp[32] = {0}; 554 | if (kte_settings.bShortNames) 555 | snprintf(temp, 32, "%02d Tot", total); 556 | else 557 | snprintf(temp, 32, "%02d Total", total); 558 | draw_bar(1.f, temp, ImGui::GetStyleColorVec4(ImGuiCol_PlotHistogram)); 559 | 560 | uint16_t cur_max = combatants_to_disp[0].count; 561 | for (auto& profelite : combatants_to_disp) 562 | { 563 | if (profelite.count == 0) //shortstop 564 | break; 565 | if (kte_settings.bShortNames) 566 | snprintf(temp, 32, "%02d %s", profelite.count, &pe_short_name_lut[profelite.idx]); 567 | else 568 | snprintf(temp, 32, "%02d %s", profelite.count, &pe_name_lut[profelite.idx]); 569 | draw_bar((float)profelite.count/(float)cur_max, temp, color_array[1][profelite.prof]); 570 | } 571 | 572 | ImGui::PopStyleColor(); 573 | } 574 | 575 | void push_new_team_name(char* buf, const uint16_t team_id) 576 | { 577 | if (kte_settings.cteam1.id == team_id) 578 | { 579 | snprintf(buf, 32, kte_settings.cteam1.name.data()); 580 | } 581 | else if (kte_settings.cteam2.id == team_id) 582 | { 583 | snprintf(buf, 32, kte_settings.cteam2.name.data()); 584 | } 585 | else if (kte_settings.cteam3.id == team_id) 586 | { 587 | snprintf(buf, 32, kte_settings.cteam3.name.data()); 588 | } 589 | else if (kte_settings.blue_team == team_id) 590 | { 591 | snprintf(buf, 32, "Blue"); 592 | } 593 | else if (kte_settings.green_team == team_id) 594 | { 595 | snprintf(buf, 32, "Green"); 596 | } 597 | else if (kte_settings.red_team == team_id) 598 | { 599 | snprintf(buf, 32, "Red"); 600 | } 601 | else 602 | { 603 | snprintf(buf, 32, "Team %d", team_id); 604 | } 605 | } 606 | 607 | void draw_teams_tabbed() 608 | { 609 | ImGuiStyle style = ImGui::GetStyle(); 610 | ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(style.FramePadding.x, 1)); 611 | ImU32 col = ImGui::GetColorU32(ImGuiCol_TableRowBg); 612 | ImGui::PushStyleColor(ImGuiCol_TableHeaderBg, col); 613 | uint8_t cur_max = 0; 614 | if (override_tab_max_switch == false) 615 | { 616 | for (int i = 0; i < team_history_map.team_ids.size(); i++) 617 | { 618 | if (team_history_map.histories[i][history_to_disp_idx].total > cur_max) 619 | { 620 | cur_max = team_history_map.histories[i][history_to_disp_idx].total; 621 | tab_team_idx = i; 622 | } 623 | } 624 | } 625 | if (ImGui::BeginTable("tabtable", 1, 626 | ImGuiTableFlags_BordersInner | ImGuiTableFlags_ContextMenuInBody | ImGuiTableFlags_NoPadOuterX)) 627 | { 628 | ImGui::TableNextRow(); 629 | ImGui::TableSetColumnIndex(0); 630 | for (int i = 0; i < team_history_map.team_ids.size(); i++) 631 | { 632 | col = ImGui::GetColorU32(ImGuiCol_Tab); 633 | if (i == tab_team_idx) 634 | col = ImGui::GetColorU32(ImGuiCol_TabActive); 635 | ImGui::PushStyleColor(ImGuiCol_Button, col); 636 | char temp[32] = {0}; 637 | push_new_team_name(temp, team_history_map.team_ids[i]); 638 | if (ImGui::Button(temp)) 639 | { 640 | override_tab_max_switch = true; 641 | tab_team_idx = i; 642 | } 643 | ImGui::PopStyleColor(); 644 | ImGui::SameLine(); 645 | } 646 | ImGui::NewLine(); 647 | 648 | ImGui::TableNextRow(); 649 | ImGui::TableSetColumnIndex(0); 650 | imgui_team_class_bars(team_history_map.histories[tab_team_idx][history_to_disp_idx]); 651 | ImGui::EndTable(); 652 | } 653 | ImGui::PopStyleColor(); 654 | ImGui::PopStyleVar(); 655 | } 656 | 657 | void draw_teams_columns() 658 | { 659 | ImGuiStyle style = ImGui::GetStyle(); 660 | ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(style.FramePadding.x, 1)); 661 | ImU32 col = ImGui::GetColorU32(ImGuiCol_Tab); 662 | ImGui::PushStyleColor(ImGuiCol_TableHeaderBg, col); 663 | if (ImGui::BeginTable("coltable", team_history_map.team_ids.size(), 664 | ImGuiTableFlags_BordersInner | ImGuiTableFlags_ContextMenuInBody | ImGuiTableFlags_NoPadOuterX)) 665 | { 666 | char temp[32] = {0}; 667 | for (int i = 0; i < team_history_map.team_ids.size(); i++) 668 | { 669 | push_new_team_name(temp, team_history_map.team_ids[i]); 670 | ImGui::TableSetupColumn(temp, ImGuiTableColumnFlags_NoSort | ImGuiTableColumnFlags_WidthStretch); 671 | } 672 | ImGui::TableNextRow(ImGuiTableRowFlags_Headers); 673 | int column = 0; 674 | for (int i = 0; i < team_history_map.team_ids.size(); i++) 675 | { 676 | ImGui::TableSetColumnIndex(column); 677 | const char* column_name = ImGui::TableGetColumnName(column); // Retrieve name passed to TableSetupColumn() 678 | float inner_pad = ImGui::GetColumnWidth(column)*0.5 - ImGui::CalcTextSize(column_name).x*0.5; 679 | ImGui::SetCursorPosX(ImGui::GetCursorPosX() + inner_pad); 680 | ImGui::Text(column_name); 681 | column++; 682 | } 683 | 684 | ImGui::TableNextRow(); 685 | column = 0; 686 | for (int i = 0; i < team_history_map.team_ids.size(); i++) 687 | { 688 | ImGui::TableSetColumnIndex(column); 689 | imgui_team_class_bars(team_history_map.histories[i][history_to_disp_idx]); 690 | column++; 691 | } 692 | ImGui::EndTable(); 693 | } 694 | ImGui::PopStyleColor(); 695 | ImGui::PopStyleVar(); 696 | } 697 | 698 | void draw_battle_wait() 699 | { 700 | if (GetTickCount() - last_cbt_evt > 15*1000) 701 | { 702 | cmb_state = combat_state::OUT_BATTLE; 703 | } 704 | ImGui::NewLine(); 705 | const char* text = "BATTLE IN PROGRESS"; 706 | float inner_pad; 707 | unsigned int width = ImGui::CalcTextSize(text).x + 4; 708 | unsigned int win_width = ImGui::GetWindowWidth(); 709 | if (width >= win_width) 710 | { 711 | const char* text0 = "BATTLE"; 712 | inner_pad = win_width*0.5 - ImGui::CalcTextSize(text0).x*0.5; 713 | ImGui::SetCursorPosX(inner_pad); 714 | ImGui::Text(text0); 715 | 716 | const char* text1 = "IN"; 717 | inner_pad = win_width*0.5 - ImGui::CalcTextSize(text1).x*0.5; 718 | ImGui::SetCursorPosX(inner_pad); 719 | ImGui::Text(text1); 720 | 721 | const char* text2 = "PROGRESS"; 722 | inner_pad = win_width*0.5 - ImGui::CalcTextSize(text2).x*0.5; 723 | ImGui::SetCursorPosX(inner_pad); 724 | ImGui::Text(text2); 725 | 726 | } 727 | else 728 | { 729 | inner_pad = win_width*0.5 - ImGui::CalcTextSize(text).x*0.5; 730 | ImGui::SetCursorPosX(inner_pad); 731 | ImGui::Text(text); 732 | } 733 | ImGui::NewLine(); 734 | inner_pad = win_width*0.5 - ImGui::CalcTextSize("00000000").x*0.5; 735 | ImGui::SetCursorPosX(inner_pad); 736 | ImGui::Text("%08o", GetTickCount() - logstart_time); 737 | } 738 | 739 | display_state get_display_state() 740 | { 741 | if (team_history_map.team_ids.empty()) 742 | { 743 | return display_state::NONE; 744 | } 745 | if (cmb_state == combat_state::IN_BATTLE && cur_history_idx == history_to_disp_idx) 746 | { 747 | return display_state::BATTLE; 748 | } 749 | else if (!kte_settings.bShowColumns) 750 | { 751 | return display_state::HISTORY_TAB; 752 | } 753 | else if (kte_settings.bShowColumns) 754 | { 755 | return display_state::HISTORY_COL; 756 | } 757 | return display_state::NONE; 758 | } 759 | 760 | 761 | int nimgui_proc = 0; 762 | bool not_init_closing = true; 763 | uintptr_t imgui_proc(const uint32_t not_charsel_or_loading, const uint32_t hide_if_combat_or_ooc) 764 | { 765 | if (not_init_closing && not_charsel_or_loading && kte_loaded == 0 && nimgui_proc == 600) 766 | { 767 | ImGui::SetNextWindowPos(ImVec2(ImGui::GetIO().DisplaySize.x/4, ImGui::GetIO().DisplaySize.y/2)); 768 | ImGui::SetNextWindowSize(ImVec2(200, 100)); 769 | if (ImGui::Begin("KTE NOT INIT", ¬_init_closing)) 770 | { 771 | ImGui::Text("KTE NOT INIT"); 772 | } 773 | ImGui::End(); 774 | } 775 | else if (not_charsel_or_loading && nimgui_proc < 300) 776 | nimgui_proc++; 777 | else if (not_charsel_or_loading && kte_settings.bEnabled && kte_settings.bToShow && 778 | (!kte_settings.bHideInCombat || cmb_state == combat_state::OUT_BATTLE)) 779 | { 780 | bool made_title_invis = false; 781 | if (!kte_settings.bTitleBg) 782 | { 783 | ImVec4 color = ImGui::GetStyleColorVec4(ImGuiCol_TitleBg); //xyzw == RGBA 784 | ImGui::PushStyleColor(ImGuiCol_TitleBg, ImVec4(color.x, color.y, color.z, 0.0)); 785 | ImGui::PushStyleColor(ImGuiCol_TitleBgActive, ImVec4(color.x, color.y, color.z, 0.0)); 786 | made_title_invis = true; 787 | } 788 | 789 | ImGui::PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(100, 10)); 790 | 791 | auto style = ImGui::GetStyle(); 792 | if (style.ScrollbarSize == 0) 793 | kte_settings.wFlags |= ImGuiWindowFlags_NoScrollbar; 794 | 795 | ImGui::PushID("KTE"); 796 | if (reset_pos) 797 | { 798 | ImGui::SetNextWindowPos(ImVec2(ImGui::GetIO().DisplaySize.x/4, ImGui::GetIO().DisplaySize.y/2)); 799 | ImGui::SetNextWindowSize(ImVec2(200, 100)); 800 | reset_pos = false; 801 | } 802 | if (ImGui::Begin("Know thy enemy", &kte_settings.bEnabled, kte_settings.wFlags)) 803 | { 804 | switch (get_display_state()) 805 | { 806 | case display_state::BATTLE: 807 | draw_battle_wait(); 808 | break; 809 | case display_state::HISTORY_TAB: 810 | draw_teams_tabbed(); 811 | break; 812 | case display_state::HISTORY_COL: 813 | draw_teams_columns(); 814 | break; 815 | case display_state::NONE: 816 | default: 817 | ImGui::Text("No teams"); 818 | ImGui::Separator(); 819 | ImGui::Text("No hits"); 820 | break; 821 | } 822 | 823 | if(ImGui::BeginPopupContextWindow(NULL, ImGuiPopupFlags_MouseButtonRight)) 824 | { 825 | ImGuiStyle style = ImGui::GetStyle(); 826 | ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(style.FramePadding.x, 0)); 827 | if (ImGui::BeginMenu("History")) 828 | { 829 | draw_history_menu(); 830 | ImGui::EndMenu(); 831 | } 832 | if (ImGui::BeginMenu("Style")) 833 | { 834 | draw_style_menu(); 835 | ImGui::EndMenu(); 836 | } 837 | ImGui::PopStyleVar(); 838 | ImGui::EndPopup(); 839 | } 840 | } 841 | ImGui::End(); 842 | ImGui::PopID(); 843 | ImGui::PopStyleVar(); 844 | if (made_title_invis) 845 | { 846 | ImGui::PopStyleColor(2); 847 | } 848 | } 849 | return 0; 850 | } 851 | 852 | void save_kte_settings() 853 | { 854 | std::wstring path = std::wstring(get_settings_path()); 855 | path = path.substr(0, path.find_last_of(L"\\")+1); 856 | path.append(L"know_thy_enemy_settings.bin"); 857 | std::fstream file(path.c_str(), std::fstream::out | std::fstream::trunc | std::ios::binary); 858 | if (file.good()) 859 | { 860 | file.write((char*)&kte_settings, sizeof(settings)); 861 | } 862 | file.close(); 863 | } 864 | 865 | void init_kte_settings() 866 | { 867 | std::wstring path = std::wstring(get_settings_path()); 868 | path = path.substr(0, path.find_last_of(L"\\")+1); 869 | path.append(L"know_thy_enemy_settings.bin"); 870 | std::ifstream file(path.c_str(), std::fstream::in | std::ios::binary); 871 | std::string line; 872 | bool success = false; 873 | if (file.good()) 874 | { 875 | settings_old temp = {}; 876 | file.read((char*)&temp, sizeof(settings_old)); 877 | if(temp.magic == 0xC0FFEE) 878 | { 879 | memcpy(&kte_settings, &temp, sizeof(settings_old) - sizeof(custom_team_old)*3); 880 | kte_settings.magic = 0xC1FFEE; 881 | memcpy(&kte_settings.cteam1, &temp.cteam1, sizeof(custom_team_old)); 882 | memcpy(&kte_settings.cteam2, &temp.cteam2, sizeof(custom_team_old)); 883 | memcpy(&kte_settings.cteam3, &temp.cteam3, sizeof(custom_team_old)); 884 | success = true; 885 | } 886 | else if(temp.magic == 0xC1FFEE) 887 | { 888 | file.seekg(0, std::ios::beg); 889 | file.read((char*)&kte_settings, sizeof(settings)); 890 | success = true; 891 | } 892 | } 893 | if (!success) 894 | { 895 | file.open(path.c_str(), std::fstream::in | std::fstream::out | std::fstream::trunc); 896 | } 897 | file.close(); 898 | } 899 | 900 | /* release mod -- return ignored */ 901 | uintptr_t mod_release() { 902 | FreeConsole(); 903 | save_kte_settings(); 904 | return 0; 905 | } 906 | 907 | /* export -- arcdps looks for this exported function and calls the address it returns on client exit */ 908 | extern "C" __declspec(dllexport) void* get_release_addr() { 909 | arcvers = 0; 910 | return mod_release; 911 | } 912 | 913 | 914 | /* initialize mod -- return table that arcdps will use for callbacks. exports struct and strings are copied to arcdps memory only once at init */ 915 | static arcdps_exports arc_exports = {0}; 916 | arcdps_exports* mod_init() { 917 | // if (strcmp(arcvers, "20230822.225438-493-x64") != 0) 918 | // { 919 | // log_arc("arcdps mismatch, not loading know_thy_enemy"); 920 | // return 0; 921 | // } 922 | /* for arcdps */ 923 | memset(&arc_exports, 0, sizeof(arcdps_exports)); 924 | arc_exports.sig = 0xC0FFEE; 925 | arc_exports.imguivers = IMGUI_VERSION_NUM; 926 | arc_exports.size = sizeof(arcdps_exports); 927 | arc_exports.out_name = "Know thy enemy"; 928 | arc_exports.out_build = "4.9.13"; 929 | arc_exports.imgui = imgui_proc; 930 | arc_exports.wnd_nofilter = mod_wnd; 931 | arc_exports.combat = mod_combat; 932 | arc_exports.options_end = options_end_proc; 933 | arc_exports.options_windows = options_windows_proc; 934 | init_colors(); 935 | init_kte_settings(); 936 | cur_history_idx = 0; 937 | history_to_disp_idx = 0; 938 | log_arc((char*)"know_thy_enemy mod_init"); // if using vs2015+, project properties > c++ > conformance mode > permissive to avoid const to not const conversion error 939 | return &arc_exports; 940 | } 941 | 942 | /* export -- arcdps looks for this exported function and calls the address it returns on client load */ 943 | extern "C" __declspec(dllexport) void* get_init_addr(char* arcversion, ImGuiContext* imguictx, void* id3dptr, HANDLE _arcdll, void* mallocfn, void* freefn, uint32_t d3dversion) { 944 | // id3dptr is IDirect3D9* if d3dversion==9, or IDXGISwapChain* if d3dversion==11 945 | arcvers = arcversion; 946 | arcdll = (char*)_arcdll; 947 | char* arcCtx = get_arc_context(arcdll); //createthread 948 | pNumAgents = get_p_num_agents(arcCtx); 949 | pAgentIds = get_p_agents(arcCtx); 950 | 951 | get_settings_path = (wchar_t*(*)())GetProcAddress((HMODULE)arcdll, "e0"); 952 | arclog = (size_t(*)(char*))GetProcAddress((HMODULE)arcdll, "e8"); 953 | filelog = (void*)GetProcAddress((HMODULE)arcdll, "e3"); 954 | arccolors = (void(*)(ImVec4**))GetProcAddress((HMODULE)arcdll, "e5"); 955 | get_ui_settings = (uint64_t(*)())GetProcAddress((HMODULE)arcdll, "e6"); 956 | get_key_settings = (uint64_t(*)())GetProcAddress((HMODULE)arcdll, "e7"); 957 | ImGui::SetCurrentContext((ImGuiContext*)imguictx); 958 | ImGui::SetAllocatorFunctions((void *(*)(size_t, void*))mallocfn, (void (*)(void*, void*))freefn); // on imgui 1.80+ 959 | return mod_init; 960 | } 961 | --------------------------------------------------------------------------------