diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/esp.cpp | 92 |
1 files changed, 63 insertions, 29 deletions
diff --git a/src/esp.cpp b/src/esp.cpp index e273bf6..447bee7 100644 --- a/src/esp.cpp +++ b/src/esp.cpp | |||
| @@ -62,6 +62,12 @@ bool world_to_screen(const Vector &w, float &sx, float &sy) { | |||
| 62 | vfunc<Fn>(g_iface.debugoverlay, 9)(g_iface.debugoverlay, &w, &s); | 62 | vfunc<Fn>(g_iface.debugoverlay, 9)(g_iface.debugoverlay, &w, &s); |
| 63 | if (behind) | 63 | if (behind) |
| 64 | return false; | 64 | return false; |
| 65 | // A point on the view axis (e.g. directly below the feet) can produce | ||
| 66 | // NaN/inf or enormous coordinates; drawing those as ints corrupts the | ||
| 67 | // surface clip state and hides the rest of the overlay. | ||
| 68 | if (!std::isfinite(s.x) || !std::isfinite(s.y) || fabsf(s.x) > 1e7f || | ||
| 69 | fabsf(s.y) > 1e7f) | ||
| 70 | return false; | ||
| 65 | sx = s.x; | 71 | sx = s.x; |
| 66 | sy = s.y; | 72 | sy = s.y; |
| 67 | return true; | 73 | return true; |
| @@ -82,7 +88,8 @@ bool world_to_screen(const Vector &w, float &sx, float &sy) { | |||
| 82 | float tanfov = tanf(75.0f * 0.5f * PI_F / 180.0f); | 88 | float tanfov = tanf(75.0f * 0.5f * PI_F / 180.0f); |
| 83 | sx = (float)g_scr_w * 0.5f + (x / z) * ((float)g_scr_w * 0.5f / tanfov); | 89 | sx = (float)g_scr_w * 0.5f + (x / z) * ((float)g_scr_w * 0.5f / tanfov); |
| 84 | sy = (float)g_scr_h * 0.5f - (y / z) * ((float)g_scr_w * 0.5f / tanfov); | 90 | sy = (float)g_scr_h * 0.5f - (y / z) * ((float)g_scr_w * 0.5f / tanfov); |
| 85 | return true; | 91 | return std::isfinite(sx) && std::isfinite(sy) && fabsf(sx) < 1e7f && |
| 92 | fabsf(sy) < 1e7f; | ||
| 86 | } | 93 | } |
| 87 | 94 | ||
| 88 | // --------------------------------------------------------------------------- | 95 | // --------------------------------------------------------------------------- |
| @@ -2346,8 +2353,30 @@ static void items_collect(int local_idx) { | |||
| 2346 | int active_idx = local_active_weapon_idx(local_idx); | 2353 | int active_idx = local_active_weapon_idx(local_idx); |
| 2347 | 2354 | ||
| 2348 | // Which weapon types the local player already carries. A fallen weapon of | 2355 | // Which weapon types the local player already carries. A fallen weapon of |
| 2349 | // the same type is then just an ammo pickup. | 2356 | // the same type is then just an ammo pickup. Owner handles are unreliable on |
| 2350 | bool owned_weapon[WT_COUNT] = {}; | 2357 | // this client (owner/weapon_owner netvars resolve to -1), so remember every |
| 2358 | // type the player has held since the current local entity appeared. | ||
| 2359 | static uint32_t owned_mask = 0; | ||
| 2360 | static void *last_local = nullptr; | ||
| 2361 | void *local_ent = entitylist_get(local_idx); | ||
| 2362 | if (local_ent != last_local) { | ||
| 2363 | last_local = local_ent; | ||
| 2364 | owned_mask = 0; | ||
| 2365 | } | ||
| 2366 | int owned_idx = active_idx; | ||
| 2367 | if (owned_idx <= 0 && g_iface.engineserver) { | ||
| 2368 | resolve_server_netvars(); | ||
| 2369 | owned_idx = server_active_weapon(local_idx); | ||
| 2370 | } | ||
| 2371 | if (owned_idx > 0) { | ||
| 2372 | void *aw = entitylist_get(owned_idx); | ||
| 2373 | void *anet = entity_networkable(owned_idx); | ||
| 2374 | const char *acls = anet ? entity_class_name(anet, nullptr) : nullptr; | ||
| 2375 | const char *amodel = aw ? entity_model_name(aw) : nullptr; | ||
| 2376 | int at = weapon_type_id(amodel, acls); | ||
| 2377 | if (at > 0) | ||
| 2378 | owned_mask |= 1u << at; | ||
| 2379 | } | ||
| 2351 | for (int idx = 1; idx <= hi; idx++) { | 2380 | for (int idx = 1; idx <= hi; idx++) { |
| 2352 | if (idx == local_idx) | 2381 | if (idx == local_idx) |
| 2353 | continue; | 2382 | continue; |
| @@ -2359,8 +2388,11 @@ static void items_collect(int local_idx) { | |||
| 2359 | const char *model = entity_model_name(ent); | 2388 | const char *model = entity_model_name(ent); |
| 2360 | int wt = weapon_type_id(model, cls); | 2389 | int wt = weapon_type_id(model, cls); |
| 2361 | if (wt > 0) | 2390 | if (wt > 0) |
| 2362 | owned_weapon[wt] = true; | 2391 | owned_mask |= 1u << wt; |
| 2363 | } | 2392 | } |
| 2393 | bool owned_weapon[WT_COUNT] = {}; | ||
| 2394 | for (int t = 1; t < WT_COUNT; t++) | ||
| 2395 | owned_weapon[t] = (owned_mask & (1u << t)) != 0; | ||
| 2364 | static bool cats_logged = false; | 2396 | static bool cats_logged = false; |
| 2365 | if (!cats_logged) { | 2397 | if (!cats_logged) { |
| 2366 | cats_logged = true; | 2398 | cats_logged = true; |
| @@ -2706,12 +2738,6 @@ void esp_draw_projectiles() { | |||
| 2706 | projs_collect(engine_get_local_player()); | 2738 | projs_collect(engine_get_local_player()); |
| 2707 | for (int i = 0; i < g_nprojs; i++) { | 2739 | for (int i = 0; i < g_nprojs; i++) { |
| 2708 | const ProjTarget &p = g_projs[i]; | 2740 | const ProjTarget &p = g_projs[i]; |
| 2709 | float sx, sy; | ||
| 2710 | if (!world_to_screen(p.origin, sx, sy)) | ||
| 2711 | continue; | ||
| 2712 | if (sx < -40 || sx > (float)g_scr_w + 40 || sy < -40 || | ||
| 2713 | sy > (float)g_scr_h + 40) | ||
| 2714 | continue; | ||
| 2715 | int r = 255, g = 170, b = 60; | 2741 | int r = 255, g = 170, b = 60; |
| 2716 | if (p.kind == PK_RPG) { | 2742 | if (p.kind == PK_RPG) { |
| 2717 | r = 255; g = 120; b = 40; | 2743 | r = 255; g = 120; b = 40; |
| @@ -2722,25 +2748,33 @@ void esp_draw_projectiles() { | |||
| 2722 | } else if (p.fuse >= 0.0f && p.fuse < 0.8f) { | 2748 | } else if (p.fuse >= 0.0f && p.fuse < 0.8f) { |
| 2723 | r = 255; g = 60; b = 60; | 2749 | r = 255; g = 60; b = 60; |
| 2724 | } | 2750 | } |
| 2725 | int x = (int)sx, y = (int)sy; | 2751 | float sx, sy; |
| 2726 | surf_color(r, g, b, 255); | 2752 | bool center_ok = world_to_screen(p.origin, sx, sy) && |
| 2727 | surf_rect(0, x - 4, y - 4, x + 4, y + 4); | 2753 | sx >= -40.0f && sx <= (float)g_scr_w + 40.0f && |
| 2728 | surf_rect(1, x - 1, y - 1, x + 2, y + 2); | 2754 | sy >= -40.0f && sy <= (float)g_scr_h + 40.0f; |
| 2729 | char text[64]; | 2755 | if (center_ok) { |
| 2730 | if (g_proj_fuse && p.fuse >= 0.0f) | 2756 | int x = (int)sx, y = (int)sy; |
| 2731 | snprintf(text, sizeof(text), "%s %.1fs", proj_name(p.kind), p.fuse); | 2757 | surf_color(r, g, b, 255); |
| 2732 | else | 2758 | surf_rect(0, x - 4, y - 4, x + 4, y + 4); |
| 2733 | snprintf(text, sizeof(text), "%s", proj_name(p.kind)); | 2759 | surf_rect(1, x - 1, y - 1, x + 2, y + 2); |
| 2734 | int tx = x + 9; | 2760 | char text[64]; |
| 2735 | if (tx > g_scr_w - 80) | 2761 | if (g_proj_fuse && p.fuse >= 0.0f) |
| 2736 | tx = x - 9 - (int)strlen(text) * 6; | 2762 | snprintf(text, sizeof(text), "%s %.1fs", proj_name(p.kind), p.fuse); |
| 2737 | if (tx < 4) | 2763 | else |
| 2738 | tx = 4; | 2764 | snprintf(text, sizeof(text), "%s", proj_name(p.kind)); |
| 2739 | int ty = y - 14; | 2765 | int tx = x + 9; |
| 2740 | if (ty < 4) | 2766 | if (tx > g_scr_w - 80) |
| 2741 | ty = 4; | 2767 | tx = x - 9 - (int)strlen(text) * 6; |
| 2742 | draw_text_small_col(tx + 1, ty + 1, text, 0, 0, 0); | 2768 | if (tx < 4) |
| 2743 | draw_text_small_col(tx, ty, text, r, g, b); | 2769 | tx = 4; |
| 2770 | int ty = y - 14; | ||
| 2771 | if (ty < 4) | ||
| 2772 | ty = 4; | ||
| 2773 | draw_text_small_col(tx + 1, ty + 1, text, 0, 0, 0); | ||
| 2774 | draw_text_small_col(tx, ty, text, r, g, b); | ||
| 2775 | } | ||
| 2776 | // The ring is useful even when the grenade itself is behind the camera or | ||
| 2777 | // off-screen, so it is not gated on center_ok. | ||
| 2744 | if (g_proj_radius && p.radius > 1.0f) { | 2778 | if (g_proj_radius && p.radius > 1.0f) { |
| 2745 | const int SEG = 24; | 2779 | const int SEG = 24; |
| 2746 | float lx = 0.0f, ly = 0.0f; | 2780 | float lx = 0.0f, ly = 0.0f; |
