using System; namespace Ashfall.Core { [Serializable] public struct V2 { public float x, y; public V2(float x, float y) { this.x = x; this.y = y; } public float Length => (float)Math.Sqrt(x * x + y * y); public float SqrLength => x * x + y * y; public bool Finite => MathEx.Finite(x) && MathEx.Finite(y); public V2 Normalized => Length > .0001f ? this / Length : new V2(); public static V2 operator +(V2 a, V2 b) => new V2(a.x + b.x, a.y + b.y); public static V2 operator -(V2 a, V2 b) => new V2(a.x - b.x, a.y - b.y); public static V2 operator *(V2 a, float b) => new V2(a.x * b, a.y * b); public static V2 operator /(V2 a, float b) => new V2(a.x / b, a.y / b); public static float Distance(V2 a, V2 b) => (a - b).Length; public static float Dot(V2 a, V2 b) => a.x * b.x + a.y * b.y; public static V2 Lerp(V2 a, V2 b, float t) => a + (b - a) * MathEx.Clamp(t, 0, 1); public V2 Rotate(float degrees) { double r = degrees * Math.PI / 180; return new V2((float)(x * Math.Cos(r) - y * Math.Sin(r)), (float)(x * Math.Sin(r) + y * Math.Cos(r))); } public override string ToString() => $"({x:0.00}, {y:0.00})"; } [Serializable] public struct Box2 { public V2 center, size; public Box2(V2 center, V2 size) { this.center = center; this.size = size; } public bool Contains(V2 p, float inset = 0) => Math.Abs(p.x - center.x) <= size.x * .5f - inset && Math.Abs(p.y - center.y) <= size.y * .5f - inset; public bool Overlaps(Box2 b, float gap = 0) => Math.Abs(center.x - b.center.x) < (size.x + b.size.x) * .5f + gap && Math.Abs(center.y - b.center.y) < (size.y + b.size.y) * .5f + gap; public bool BlocksSegment(V2 a, V2 b, float padding = 0) { float near = 0, far = 1; V2 d = b - a; return Clip(a.x, d.x, center.x - size.x / 2 - padding, center.x + size.x / 2 + padding, ref near, ref far) && Clip(a.y, d.y, center.y - size.y / 2 - padding, center.y + size.y / 2 + padding, ref near, ref far); } private static bool Clip(float start, float direction, float min, float max, ref float near, ref float far) { if (Math.Abs(direction) < .00001f) return start >= min && start <= max; float a = (min - start) / direction, b = (max - start) / direction; if (a > b) { float t = a; a = b; b = t; } near = Math.Max(near, a); far = Math.Min(far, b); return near <= far; } } public static class MathEx { public static bool Finite(float f) => !float.IsNaN(f) && !float.IsInfinity(f); public static float Clamp(float f, float a, float b) => Math.Max(a, Math.Min(f, b)); public static float Approach(float value, float target, float amount) => value < target ? Math.Min(target, value + amount) : Math.Max(target, value - amount); public static float Angle(V2 direction) => (float)(Math.Atan2(direction.y, direction.x) * 180 / Math.PI) - 90; public static float DeltaAngle(float a, float b) => ((b - a + 540) % 360 + 360) % 360 - 180; public static float NormalizeAngle(float value) => (value % 360 + 360) % 360; } public static class Projection { public const float GroundScale = .819152f; public const float HeightScale = .573576f; public static V2 ToScreen(V2 ground, float height = 0) => new V2(ground.x, ground.y * GroundScale + height * HeightScale); public static V2 ToGround(V2 screen, float height = 0) => new V2(screen.x, (screen.y - height * HeightScale) / GroundScale); public static V2 InteriorToWorld(V2 local, V2 origin, float heading) => origin + local.Rotate(heading); public static V2 WorldToInterior(V2 world, V2 origin, float heading) => (world - origin).Rotate(-heading); } public enum Species { Human, Brood } public enum BroodForm { Worm, Adult } public enum Family { Mechanical, Biological } public enum SpaceKind { Exterior, Building, Vehicle } public enum StationRole { Driver, Gunner } public enum FlightMode { Ground, Low, Cruise } public enum PieceKind { Floor, Wall, Door, Stairs, Research, Generator, Storage, Turret } public enum FacilityKind { Research, Generator, Storage, Workshop, Medical } public enum CommandKind { Interact, LeaveStation, InstallModule, Build, Dismantle, Research, Craft, Transfer, Cruise, ChangeAltitude, Repair, SwitchSpecies, Ability, Collect, Evolve } [Serializable] public class ActionResult { public bool success; public string message; public static ActionResult Ok(string text = "完成") => new ActionResult { success = true, message = text }; public static ActionResult Fail(string text) => new ActionResult { success = false, message = text }; } }