Partial class for the football match world panel. Draws the offside decision as two feathered vertical pitch lines and exposes the current offside values in a report object.
using Sandbox;
using Socer;
using System;
public sealed partial class FootballWorldPanel
{
SceneDynamicObject offsideDecisionObject;
readonly Vertex[] offsideDecisionVertices=new Vertex[36];
float offsideStrokeWidth;
bool offsideDecisionVisible;
void DrawOffsideDecision(MatchSimulation match,bool replay)
{
// Remove the former box even from a panel surviving hotload. The new
// flat, feathered lines have exact touchline endpoints and cast no shadow.
if(offside!=null){offside.GameObject.Destroy();offside=null;}
offsideDecisionVisible=!replay && match.OffsideDecisionTeam>=0 && match.OffsideLineUntil>match.Seconds;
if(!offsideDecisionVisible)
{if(offsideDecisionObject!=null)offsideDecisionObject.RenderingEnabled=false;return;}
offsideDecisionObject??=GroundObject();offsideDecisionObject.RenderingEnabled=true;
int count=0;
void Line(float x,string color)
{
x=Math.Clamp(x,0,105);
float pixel=GroundPixelSize(new Point(x,34));
float half=Math.Max(.028f,pixel),fringe=pixel*.85f;
offsideStrokeWidth=half*2;
var ink=Ink(color);
Vertex V(float y,int band)
{
float offset=band switch{0=>-half-fringe,1=>-half,2=>half,_=>half+fringe};
return new Vertex(WorldPoint(new Point(Math.Clamp(x+offset,0,105),y),.016f),
new Color32((byte)(ink.r*255),(byte)(ink.g*255),(byte)(ink.b*255),(byte)(band is 0 or 3?0:230)));
}
for(int band=0;band<3;band++)
{
var a=V(0,band);var b=V(68,band);var c=V(68,band+1);var d=V(0,band+1);
offsideDecisionVertices[count++]=a;offsideDecisionVertices[count++]=b;offsideDecisionVertices[count++]=c;
offsideDecisionVertices[count++]=a;offsideDecisionVertices[count++]=c;offsideDecisionVertices[count++]=d;
}
}
Line(match.OffsideLineX,"#ebd477");
Line(match.OffsideAttackerX,"#ed796f");
offsideDecisionObject.Init(Graphics.PrimitiveType.Triangles);
offsideDecisionObject.AddVertex(offsideDecisionVertices.AsSpan(0,count));
}
public object OffsideGraphicsReport()=>new{Visible=offsideDecisionVisible,StrokeMetres=offsideStrokeWidth,
Decision=Game.Match.OffsideDecisionTeam,LimitX=Game.Match.OffsideLineX,AttackerX=Game.Match.OffsideAttackerX,
PassBall=new{Game.Match.OffsidePassBall.X,Game.Match.OffsidePassBall.Y},Game.Match.OffsideLineUntil,Game.Match.Seconds,
Flat=true,Feathered=true,Vertices=offsideDecisionVisible?36:0,LegacyBox=offside!=null};
}