using System.Numerics;
namespace Flashlight;
///
/// Position and orientation to apply to the flashlight entity.
/// is a Source QAngle laid out as (pitch, yaw, roll).
///
public readonly record struct LightTransform(Vector3 Origin, Vector3 Angles);
public static class FlashlightLogic
{
public static bool TryToggle(ref bool isOn, ref bool canToggle)
{
if (!canToggle)
{
return false;
}
isOn = !isOn;
canToggle = false;
return true;
}
public static bool IsUsePressedEdge(bool usePressed, bool wasUsePressed)
{
return usePressed && !wasUsePressed;
}
public static float GetEyeOffsetZ(bool isCrouching, float standOffset, float crouchOffset)
{
return isCrouching ? crouchOffset : standOffset;
}
public static Vector3 CalculateLightOrigin(
Vector3 pawnOrigin,
Vector3 forward,
float eyeOffsetZ,
float forwardDistance)
{
return new Vector3(
pawnOrigin.X + forward.X * forwardDistance,
pawnOrigin.Y + forward.Y * forwardDistance,
pawnOrigin.Z + eyeOffsetZ + forward.Z * forwardDistance);
}
public static Vector3 ForwardFromAnglesDegrees(float pitch, float yaw)
{
var pitchRad = pitch * (MathF.PI / 180f);
var yawRad = yaw * (MathF.PI / 180f);
var cosPitch = MathF.Cos(pitchRad);
return new Vector3(
cosPitch * MathF.Cos(yawRad),
cosPitch * MathF.Sin(yawRad),
-MathF.Sin(pitchRad));
}
///
/// Forward vector on the horizontal plane only, ignoring pitch.
///
public static Vector3 HorizontalForwardFromYawDegrees(float yaw)
{
var yawRad = yaw * (MathF.PI / 180f);
return new Vector3(MathF.Cos(yawRad), MathF.Sin(yawRad), 0f);
}
///
/// World transform for the flashlight given the player's current pawn origin and view angles.
///
///
/// The angles carry the full view rotation so the beam tracks pitch as well as yaw, while the
/// origin is only pushed forward on the horizontal plane. Offsetting the origin along the full
/// pitched forward vector would drop the light through the floor when looking straight down
/// (a crouched eye height of 46 minus a 54 unit offset ends up below the ground).
///
public static LightTransform CalculateLightTransform(
Vector3 pawnOrigin,
float pitch,
float yaw,
float roll,
float eyeOffsetZ,
float forwardDistance)
{
var forward = HorizontalForwardFromYawDegrees(yaw);
var origin = CalculateLightOrigin(pawnOrigin, forward, eyeOffsetZ, forwardDistance);
return new LightTransform(origin, new Vector3(pitch, yaw, roll));
}
public static bool ShouldCreateLight(bool isOn, bool hasValidLight)
{
return isOn && !hasValidLight;
}
public static bool ShouldDestroyLight(bool isOn, bool hasValidLight)
{
return !isOn && hasValidLight;
}
///
/// Returns whether may use the flashlight.
/// Team values match CS2: 2 = Terrorist, 3 = Counter-Terrorist.
///
public static bool IsTeamAllowed(string allowedTeam, byte team)
{
return allowedTeam switch
{
"T" => team == 2,
"CT" => team == 3,
_ => true
};
}
}