using System;
namespace Luneraria.Core
{
///
/// Ground vector (x, z) with 32-bit components and plain single-precision arithmetic, like
/// Godot's Vector2 (tools/godot-golden/float_probe.gd: 4000 of 4000 samples identical).
/// Scalars of the rules are doubles; they are rounded to float where they meet a vector.
/// Every intermediate result is rounded to float explicitly: ECMA-335 (I.12.1.3) allows a
/// runtime to keep float intermediates at higher precision, and Unity's Mono does. Without
/// the casts `x * x + y * y` differs in the last bit between Mono and .NET/IL2CPP/Godot,
/// and the combat dynamics amplify that into different decisions (docs/port/U1.5-UNITY.md).
///
[Serializable]
public struct Vec2 : IEquatable
{
public float X;
public float Y;
public static readonly Vec2 Zero = new Vec2(0f, 0f);
public static readonly Vec2 Right = new Vec2(1f, 0f);
public static readonly Vec2 Left = new Vec2(-1f, 0f);
public Vec2(float x, float y)
{
X = x;
Y = y;
}
/// From doubles: each component rounded to float.
public Vec2(double x, double y)
{
X = (float)x;
Y = (float)y;
}
public static Vec2 operator +(Vec2 a, Vec2 b) { return new Vec2((float)(a.X + b.X), (float)(a.Y + b.Y)); }
public static Vec2 operator -(Vec2 a, Vec2 b) { return new Vec2((float)(a.X - b.X), (float)(a.Y - b.Y)); }
public static Vec2 operator -(Vec2 a) { return new Vec2(-a.X, -a.Y); }
public static Vec2 operator *(Vec2 a, double s)
{
float f = (float)s;
return new Vec2((float)(a.X * f), (float)(a.Y * f));
}
public static Vec2 operator *(double s, Vec2 a)
{
float f = (float)s;
return new Vec2((float)(f * a.X), (float)(f * a.Y));
}
public static Vec2 operator /(Vec2 a, double s)
{
float f = (float)s;
return new Vec2((float)(a.X / f), (float)(a.Y / f));
}
public static bool operator ==(Vec2 a, Vec2 b) { return a.X == b.X && a.Y == b.Y; }
public static bool operator !=(Vec2 a, Vec2 b) { return !(a == b); }
public float Length() { return (float)MathF.Sqrt(LengthSquared()); }
public float LengthSquared() { return (float)((float)(X * X) + (float)(Y * Y)); }
public float DistanceTo(Vec2 p)
{
return (float)MathF.Sqrt(DistanceSquaredTo(p));
}
public float DistanceSquaredTo(Vec2 p)
{
float dx = (float)(X - p.X);
float dy = (float)(Y - p.Y);
return (float)((float)(dx * dx) + (float)(dy * dy));
}
public float Dot(Vec2 p) { return (float)((float)(X * p.X) + (float)(Y * p.Y)); }
public Vec2 Normalized()
{
float l = LengthSquared();
if (l != 0f)
{
l = (float)MathF.Sqrt(l);
return new Vec2((float)(X / l), (float)(Y / l));
}
return this;
}
/// from + (to - from) * weight per component, weight rounded to float.
public Vec2 Lerp(Vec2 to, double weight)
{
float w = (float)weight;
return new Vec2((float)(X + (float)((float)(to.X - X) * w)), (float)(Y + (float)((float)(to.Y - Y) * w)));
}
public Vec2 LimitLength(double length)
{
float limit = (float)length;
float l = Length();
Vec2 v = this;
if (l > 0f && limit < l)
{
v.X = (float)(v.X / l);
v.Y = (float)(v.Y / l);
v.X = (float)(v.X * limit);
v.Y = (float)(v.Y * limit);
}
return v;
}
public Vec2 Rotated(double angle)
{
float a = (float)angle;
float sine = (float)MathF.Sin(a);
float cosi = (float)MathF.Cos(a);
return new Vec2((float)((float)(X * cosi) - (float)(Y * sine)), (float)((float)(X * sine) + (float)(Y * cosi)));
}
public bool Equals(Vec2 other) { return X.Equals(other.X) && Y.Equals(other.Y); }
public override bool Equals(object obj) { return obj is Vec2 other && Equals(other); }
public override int GetHashCode() { return (X.GetHashCode() * 397) ^ Y.GetHashCode(); }
public override string ToString() { return "(" + X.ToString("R", System.Globalization.CultureInfo.InvariantCulture) + ", " + Y.ToString("R", System.Globalization.CultureInfo.InvariantCulture) + ")"; }
}
/// Integer grid cell for the spatial buckets of a combat step.
public struct Vec2I : IEquatable
{
public int X;
public int Y;
public Vec2I(int x, int y)
{
X = x;
Y = y;
}
public bool Equals(Vec2I other) { return X == other.X && Y == other.Y; }
public override bool Equals(object obj) { return obj is Vec2I other && Equals(other); }
public override int GetHashCode() { return (X * 73856093) ^ (Y * 19349663); }
}
}