using UnityEngine; namespace DepthsOfGreed.HandbookUI { public enum AttackCoverage { Automatic, Circle, Sector } public static class AttackRangeGeometry { public static float CoverageAngle(CombatDefinition c) { if(c.coverage==AttackCoverage.Circle)return 360; if(c.coverage==AttackCoverage.Sector || (c.rotation==RotationMode.Fixed && c.attackShape==AttackShape.Cone)) return Mathf.Clamp(c.coneAngle,1,360); return 360; } // Shared optional targeting helper. No shots or damage are performed here. public static bool CanReach(CombatDefinition c,Vector2 origin,Vector2 target,float baseAngleDegrees) { if(c==null)return false; Vector2 delta=target-origin;float distance=delta.magnitude; if(distanceMathf.Max(0,c.range)))return false; float coverage=CoverageAngle(c); return coverage>=360 || distance<.0001f || Mathf.Abs(Mathf.DeltaAngle(baseAngleDegrees+c.forwardAngleOffset,Mathf.Atan2(delta.y,delta.x)*Mathf.Rad2Deg))<=coverage*.5f; } } public sealed class AttackRangePreview : MonoBehaviour { public LineRenderer outerEdge,minimumEdge,sectorLeft,sectorRight; public bool showRange=true; [Min(1)] public float unlimitedDirectionLength=10; public Color rangeColor=new Color(.25f,.75f,1f,.35f); public void Show(HandbookTowerEntry entry) { GetComponent()?.Configure(entry); var c=entry?entry.combat:null; bool ability=entry && entry.use==BuildUse.Ability; bool visible=showRange && c!=null && (ability?c.areaRadius>0:c.range>0 || c.unlimitedRange); gameObject.SetActive(visible);if(!visible)return; float max=ability?c.areaRadius:c.unlimitedRange?unlimitedDirectionLength:c.range; float min=ability?0:Mathf.Clamp(c.minimumRange,0,max); float angle=ability?360:AttackRangeGeometry.CoverageAngle(c); float offset=ability?0:c.forwardAngleOffset; bool unlimited=!ability && c.unlimitedRange; outerEdge.gameObject.SetActive(!unlimited);minimumEdge.gameObject.SetActive(min>0);sectorLeft.gameObject.SetActive(angle<360);sectorRight.gameObject.SetActive(angle<360); Arc(outerEdge,max,angle,offset);Arc(minimumEdge,min,angle,offset); sectorLeft.loop=sectorRight.loop=false;sectorLeft.positionCount=sectorRight.positionCount=2; sectorLeft.SetPosition(0,Point(min,offset-angle*.5f));sectorLeft.SetPosition(1,Point(max,offset-angle*.5f)); sectorRight.SetPosition(0,Point(min,offset+angle*.5f));sectorRight.SetPosition(1,Point(max,offset+angle*.5f)); foreach(var line in new[]{outerEdge,minimumEdge,sectorLeft,sectorRight})line.startColor=line.endColor=rangeColor; } static Vector3 Point(float radius,float angle)=>new Vector3(Mathf.Cos(angle*Mathf.Deg2Rad)*radius,Mathf.Sin(angle*Mathf.Deg2Rad)*radius,0); static void Arc(LineRenderer line,float radius,float angle,float offset) { int count=97;line.loop=angle>=360;line.positionCount=angle>=360?count-1:count; for(int i=0;i