Files
tbd-station-14/Content.Shared/FixedPoint/FixedPoint4.cs
pathetic meowmeow 8894eca118 Offbrand medical (#3366)
* Offbrand medical

* what if we regrade

* zombies are mostly there thats it thats a wrap xd

* here's changeling

* some bonus gut punches

* start working on the guidebook

* fix rsi and yaml lints

* my agrichem so fits

* we stay rejuvenated

* my china so laked

* debrute

* fix suicide

* fix the suicide tests

* my surgery so requires laying down

* the guidebook continues

* READ KEB PAGES

* keb vascular recoupler

* read keb medicine

* fix yaml lint

* fix the EntityRemoveConstructionGraphStep

* fix overlay init

* scalpels are not a food tool

* return of the programmer art

* my line so nieuw

* boxes deserve veins too

* mrrrp yaml

* what if we redid brain damage alerts

* bloot pressure

* kill mannitol drowsiness

* get licensed

* my read so me

* get feedbacked nerd

* fine-tune the heart stoppage conditions

* cryostasis adjustments, guidebook adjustments, fix negative strain issues

* my surgery so table

* fix heart surgery and guidebook

* medicine & defibrillator pass

* iv bags and stands

* prefills

* janet gets very sidetracked

* mostly finished iv stuff

* what if we fixed the guidebook

* halve decapoid cryostasis

* my medicines so IV

* finetune cryostasis

* less logspam

* metabolism-aware iv stands and cryopods

* give people painkillers

* yaml lint real

* fix blood build

* finish rebase

* tidy up localization

* clean up my yaml beasties...

* soft curve after exceeding maximum damage

* husks/bonedeaths

Grabbag of Offmed fixes & improvements (#3461)

* CPR moment

* Mob AI fix

* Fix brain oxygenation not updating on regeneration

* sorry gamers you cannot resist the pull

* Troll combat abilities more in softcrit

praying rn (#3467)

dont have CPR be 50% (#3468)

Make offbrand murder easier to contend with (#3473)

* e

* disrupt people in softcrit when attacking them

* ok gamers we're gaming

* forgor

Hopefully final pass before Offbrand merge (#3475)

First pass of Offbrand adjustments (#3477)

Swap blood pressure values in health analyzer (#3476)

Systolic over diastolic

Co-authored-by: Kip <32859367+kipdotnet@users.noreply.github.com>

Offbrand pass 2: Mostly bugfixes (#3480)

Fix zeds causing PVS reloads (#3482)

Offbrand pass 3: I hate surgery I hate surgery I hate surgery I (#3481)

* set up surgery ui

* test fail real

Pain/braingasps (#3487)

Offmed bundle 5 - the evil one (#3489)

* Evil cavity surgery

* les borgues

* nicotine moment

* epinephrine RNG

* legalese

* test fail real

* ok jamers cope with c4

Pass 6
2025-09-27 22:36:57 -04:00

340 lines
9.5 KiB
C#

using System.Globalization;
using System.Linq;
using Robust.Shared.Serialization;
using Robust.Shared.Utility;
namespace Content.Shared.FixedPoint
{
/// <summary>
/// Represents a quantity of something, to a precision of 0.01.
/// To enforce this level of precision, floats are shifted by 2 decimal points, rounded, and converted to an int.
/// </summary>
[Serializable, CopyByRef]
public struct FixedPoint4 : ISelfSerialize, IComparable<FixedPoint4>, IEquatable<FixedPoint4>, IFormattable
{
public long Value { get; private set; }
private const long Shift = 4;
private const long ShiftConstant = 10000; // Must be equal to pow(10, Shift)
public static FixedPoint4 MaxValue { get; } = new(long.MaxValue);
public static FixedPoint4 Epsilon { get; } = new(1);
public static FixedPoint4 Zero { get; } = new(0);
// This value isn't picked by any proper testing, don't @ me.
private const float FloatEpsilon = 0.00001f;
#if DEBUG
static FixedPoint4()
{
// ReSharper disable once CompareOfFloatsByEqualityOperator
DebugTools.Assert(Math.Pow(10, Shift) == ShiftConstant, "ShiftConstant must be equal to pow(10, Shift)");
}
#endif
private readonly double ShiftDown()
{
return Value / (double) ShiftConstant;
}
private FixedPoint4(long value)
{
Value = value;
}
public static FixedPoint4 New(long value)
{
return new(value * ShiftConstant);
}
public static FixedPoint4 FromTenThousandths(long value) => new(value);
public static FixedPoint4 New(float value)
{
return new((long) ApplyFloatEpsilon(value * ShiftConstant));
}
private static float ApplyFloatEpsilon(float value)
{
return value + FloatEpsilon * Math.Sign(value);
}
private static double ApplyFloatEpsilon(double value)
{
return value + FloatEpsilon * Math.Sign(value);
}
/// <summary>
/// Create the closest <see cref="FixedPoint4"/> for a float value, always rounding up.
/// </summary>
public static FixedPoint4 NewCeiling(float value)
{
return new((long) MathF.Ceiling(value * ShiftConstant));
}
public static FixedPoint4 New(double value)
{
return new((long) ApplyFloatEpsilon(value * ShiftConstant));
}
public static FixedPoint4 New(string value)
{
return New(Parse.Float(value));
}
public static FixedPoint4 operator +(FixedPoint4 a) => a;
public static FixedPoint4 operator -(FixedPoint4 a) => new(-a.Value);
public static FixedPoint4 operator +(FixedPoint4 a, FixedPoint4 b)
=> new(a.Value + b.Value);
public static FixedPoint4 operator -(FixedPoint4 a, FixedPoint4 b)
=> new(a.Value - b.Value);
public static FixedPoint4 operator *(FixedPoint4 a, FixedPoint4 b)
{
return new(b.Value * a.Value / ShiftConstant);
}
public static FixedPoint4 operator *(FixedPoint4 a, float b)
{
return new((long) ApplyFloatEpsilon(a.Value * b));
}
public static FixedPoint4 operator *(FixedPoint4 a, double b)
{
return new((long) ApplyFloatEpsilon(a.Value * b));
}
public static FixedPoint4 operator *(FixedPoint4 a, long b)
{
return new(a.Value * b);
}
public static FixedPoint4 operator /(FixedPoint4 a, FixedPoint4 b)
{
return new((long) (ShiftConstant * (long) a.Value / b.Value));
}
public static FixedPoint4 operator /(FixedPoint4 a, float b)
{
return new((long) ApplyFloatEpsilon(a.Value / b));
}
public static bool operator <=(FixedPoint4 a, long b)
{
return a <= New(b);
}
public static bool operator >=(FixedPoint4 a, long b)
{
return a >= New(b);
}
public static bool operator <(FixedPoint4 a, long b)
{
return a < New(b);
}
public static bool operator >(FixedPoint4 a, long b)
{
return a > New(b);
}
public static bool operator ==(FixedPoint4 a, long b)
{
return a == New(b);
}
public static bool operator !=(FixedPoint4 a, long b)
{
return a != New(b);
}
public static bool operator ==(FixedPoint4 a, FixedPoint4 b)
{
return a.Equals(b);
}
public static bool operator !=(FixedPoint4 a, FixedPoint4 b)
{
return !a.Equals(b);
}
public static bool operator <=(FixedPoint4 a, FixedPoint4 b)
{
return a.Value <= b.Value;
}
public static bool operator >=(FixedPoint4 a, FixedPoint4 b)
{
return a.Value >= b.Value;
}
public static bool operator <(FixedPoint4 a, FixedPoint4 b)
{
return a.Value < b.Value;
}
public static bool operator >(FixedPoint4 a, FixedPoint4 b)
{
return a.Value > b.Value;
}
public readonly float Float()
{
return (float) ShiftDown();
}
public readonly double Double()
{
return ShiftDown();
}
public readonly long Long()
{
return Value / ShiftConstant;
}
public readonly int Int()
{
return (int)Long();
}
// Implicit operators ftw
public static implicit operator FixedPoint4(FixedPoint2 n) => new(n.Value * 100); // Offbrand
public static implicit operator FixedPoint4(float n) => New(n);
public static implicit operator FixedPoint4(double n) => New(n);
public static implicit operator FixedPoint4(int n) => New(n);
public static implicit operator FixedPoint4(long n) => New(n);
public static explicit operator FixedPoint2(FixedPoint4 n) => FixedPoint2.FromRaw((int)(n.Value / 100)); // Offbrand
public static explicit operator float(FixedPoint4 n) => n.Float();
public static explicit operator double(FixedPoint4 n) => n.Double();
public static explicit operator int(FixedPoint4 n) => n.Int();
public static explicit operator long(FixedPoint4 n) => n.Long();
public static FixedPoint4 Min(params FixedPoint4[] fixedPoints)
{
return fixedPoints.Min();
}
public static FixedPoint4 Min(FixedPoint4 a, FixedPoint4 b)
{
return a < b ? a : b;
}
public static FixedPoint4 Max(FixedPoint4 a, FixedPoint4 b)
{
return a > b ? a : b;
}
public static long Sign(FixedPoint4 value)
{
if (value < Zero)
{
return -1;
}
if (value > Zero)
{
return 1;
}
return 0;
}
public static FixedPoint4 Abs(FixedPoint4 a)
{
return FromTenThousandths(Math.Abs(a.Value));
}
public static FixedPoint4 Dist(FixedPoint4 a, FixedPoint4 b)
{
return FixedPoint4.Abs(a - b);
}
public static FixedPoint4 Clamp(FixedPoint4 number, FixedPoint4 min, FixedPoint4 max)
{
if (min > max)
{
throw new ArgumentException($"{nameof(min)} {min} cannot be larger than {nameof(max)} {max}");
}
return number < min ? min : number > max ? max : number;
}
public override readonly bool Equals(object? obj)
{
return obj is FixedPoint4 unit &&
Value == unit.Value;
}
public override readonly int GetHashCode()
{
// ReSharper disable once NonReadonlyMemberInGetHashCode
return HashCode.Combine(Value);
}
public void Deserialize(string value)
{
// TODO implement "lossless" serializer.
// I.e., dont use floats.
if (value == "MaxValue")
Value = int.MaxValue;
else
this = New(Parse.Double(value));
}
public override readonly string ToString() => $"{ShiftDown().ToString(CultureInfo.InvariantCulture)}";
public string ToString(string? format, IFormatProvider? formatProvider)
{
return ToString();
}
public readonly string Serialize()
{
// TODO implement "lossless" serializer.
// I.e., dont use floats.
if (Value == int.MaxValue)
return "MaxValue";
return ToString();
}
public readonly bool Equals(FixedPoint4 other)
{
return Value == other.Value;
}
public readonly int CompareTo(FixedPoint4 other)
{
if (other.Value > Value)
{
return -1;
}
if (other.Value < Value)
{
return 1;
}
return 0;
}
}
public static class FixedPoint4EnumerableExt
{
public static FixedPoint4 Sum(this IEnumerable<FixedPoint4> source)
{
var acc = FixedPoint4.Zero;
foreach (var n in source)
{
acc += n;
}
return acc;
}
}
}