cs2-outnumbered/Outnumbered/Engine/WorldText.cs
Kamal Tufekcic d701598350
All checks were successful
CI / build (push) Successful in 32s
CI / release (push) Successful in 32s
CI / lint (push) Successful in 30s
initial commit
2026-07-05 13:28:35 +03:00

81 lines
4.2 KiB
C#

using System.Drawing;
using CounterStrikeSharp.API;
using CounterStrikeSharp.API.Core;
using CounterStrikeSharp.API.Modules.Utils;
namespace Outnumbered.Engine;
// The one place a point_worldtext entity is created + configured (HUD panel, the 2 shop panels, the draft cards all
// share it). CreateEntityByName returns a NON-null wrapper even on failure (e.g. the entity limit) with a Zero Handle,
// so the guard checks the raw handle (no memory deref) before touching schema members. Callers supply the per-use
// font/justify/colour/border and own the last-text caching; the volatile "SetMessage" input + the Teleport placement
// go through SetText/Place so every engine touchpoint for this entity lives here.
internal static class WorldText
{
public static CPointWorldText? Create(float fontSize, float worldUnitsPerPx, string fontName, Color color,
bool drawBackground, float border, PointWorldTextJustifyHorizontal_t justify)
{
var e = Utilities.CreateEntityByName<CPointWorldText>(EngineNames.PointWorldText);
if (e is null || e.Handle == nint.Zero) return null;
e.MessageText = " ";
e.Enabled = true;
e.Fullbright = true;
e.FontSize = fontSize;
e.WorldUnitsPerPx = worldUnitsPerPx;
if (!string.IsNullOrEmpty(fontName)) e.FontName = fontName;
e.Color = color;
e.JustifyHorizontal = justify;
e.JustifyVertical = PointWorldTextJustifyVertical_t.POINT_WORLD_TEXT_JUSTIFY_VERTICAL_CENTER;
e.ReorientMode = PointWorldTextReorientMode_t.POINT_WORLD_TEXT_REORIENT_NONE;
e.DrawBackground = drawBackground;
e.BackgroundBorderHeight = border;
e.BackgroundBorderWidth = border;
e.DispatchSpawn();
return e;
}
// Push new text via the point_worldtext "SetMessage" input (the volatile input-name literal lives here, not at the 4
// call sites). Callers keep their own last-text cache and only call this on a change.
public static void SetText(CPointWorldText ent, string text) => ent.AcceptInput("SetMessage", ent, ent, text);
// Position + orient a panel. point_worldtext is moved via Teleport (no velocity); callers compute the eye-relative frame.
public static void Place(CPointWorldText ent, Vector pos, QAngle ang) => ent.Teleport(pos, ang, null);
// Tear a panel down: Remove the entity if it's still live, then null the caller's reference. Symmetric with Create —
// every point_worldtext create + destroy goes through this file.
public static void Destroy(ref CPointWorldText? ent)
{
if (ent is { IsValid: true }) ent.Remove();
ent = null;
}
public const float EyeZFallback = 64f; // ViewOffset.Z fallback when the pawn doesn't report one
// The eye-relative frame for placing a panel in front of a pawn's view: eye position + forward/right/up basis + the
// panel orientation (yaw+270 / 90-pitch = worldtext faces the player, upright across pitch/yaw). false if AbsOrigin is
// null. Callers add only their own per-panel offsets. Shared by the HUD + the shop/draft panels.
public static bool TryEyeFrame(CCSPlayerPawn pawn, out Vector eye, out Vector fwd, out Vector right, out Vector up, out QAngle ang)
{
eye = null!; fwd = null!; right = null!; up = null!; ang = null!;
var origin = pawn.AbsOrigin;
if (origin is null) return false;
var ea = pawn.EyeAngles;
float eyeZ = pawn.ViewOffset?.Z ?? EyeZFallback;
(fwd, right, up) = AngleVectors(ea);
eye = new Vector(origin.X, origin.Y, origin.Z + eyeZ);
ang = new QAngle(0f, ea.Y + 270f, 90f - ea.X);
return true;
}
// Source-engine AngleVectors with roll assumed 0 (HUD/shop panels never roll).
private static (Vector forward, Vector right, Vector up) AngleVectors(QAngle a)
{
const double d2r = Math.PI / 180.0;
double p = a.X * d2r, y = a.Y * d2r;
double sp = Math.Sin(p), cp = Math.Cos(p), sy = Math.Sin(y), cy = Math.Cos(y);
return (
new Vector((float)(cp * cy), (float)(cp * sy), (float)(-sp)),
new Vector((float)sy, (float)(-cy), 0f),
new Vector((float)(sp * cy), (float)(sp * sy), (float)cp));
}
}