XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

AutoMiningScript

【SpaceEngineer】全自动挖矿脚本

公开
关注 0 Fork 0 Star 0
UTF-8
#if FLEET || TESTS
using System;
using System.Collections.Generic;
using VRageMath;

namespace AutoMiningScript
{
    public partial class Program
    {
        public partial class Dashboard
        {
            sealed class RegionView
            {
                public Vector3D Center, Up, Right;
                public double Extent = 1, Area;
                public bool Search;
                public int Total, Complete, Scanned, Rays, Sampled, Empty, Unknown;
                public readonly bool[] Cells = new bool[576];
                public readonly List<Job> Dots = Data.CreateList<Job>();
                public readonly List<ScanRecord> Rocks = Data.CreateList<ScanRecord>();
                public readonly Dictionary<long,int> RockIds = Data.CreateDictionary<long,int>();
                public Vector2 Project(Vector3D point)
                {
                    var d = point-Center; return ScreenPoint((float)Data.Dot(d,Right),(float)Data.Dot(d,Up));
                }
                public static double RockArea(ScanRecord rock) => Math.PI*Data.LengthSquared(rock.Size)/4;
                public void Rock(ScanRecord rock)
                {
                    if (rock.Asteroid == 0) return;
                    int index;
                    if (RockIds.TryGetValue(rock.Asteroid,out index))
                    {
                        if (!rock.Sized) return;
                        var old = Rocks[index]; Area -= RockArea(old);
                        if (!old.Sized) Unknown--; Rocks[index] = rock;
                    }
                    else { RockIds.Add(rock.Asteroid,Rocks.Count); Rocks.Add(rock); if (!rock.Sized) Unknown++; }
                    Area += RockArea(rock);
                }
                // Raster cells mean "a ray crossed here", never full-cell clearance.
                // Clip before rasterizing: off-map parallel rays must not paint an edge.
                public void Ray(ScanRecord scan)
                {
                    var a = Project(scan.Start); var d = Project(scan.End)-a; double lo=0,hi=1;
                    for (int axis=0;axis<2;axis++)
                    {
                        double x=axis==0?a.X:a.Y, v=axis==0?d.X:d.Y;
                        if (Math.Abs(v)<.00001) { if (Math.Abs(x)>Extent) return; continue; }
                        double t=(-Extent-x)/v,u=(Extent-x)/v;
                        lo=Data.Max(lo,Data.Min(t,u)); hi=Data.Min(hi,Data.Max(t,u));
                    }
                    if(lo>hi)return;
                    var start=a+d*(float)lo; var span=d*(float)(hi-lo);
                    int steps=Data.Max(1,(int)Math.Ceiling(Data.Max(Math.Abs(span.X),Math.Abs(span.Y))*12/Extent));
                    for(int n=0;n<=steps;n++)
                    {
                        var q=start+span*((float)n/steps);
                        int x=(int)Data.Clamp(Math.Floor((q.X/Extent+1)*12),0,23),y=(int)Data.Clamp(Math.Floor((q.Y/Extent+1)*12),0,23);
                        Cells[y*24+x]=true;
                    }
                }
            }
            Dictionary<string,RegionView> mapViews=Data.CreateDictionary<string,RegionView>(),buildingViews;
            Dictionary<long,List<string>> mapOres=Data.CreateDictionary<long,List<string>>(),buildingOres;
            List<string> mapRegions=Data.CreateList<string>(),buildingRegions,buildingPages;
            List<int> mapParts=Data.CreateList<int>(),buildingParts;
            List<Job> buildingSource;
            Job buildingFirst;
            int jobCursor,buildingDone,completedJobs,pageCursor;
            int scanTotal,scanComplete,buildingScanTotal,buildingScanComplete;
            double nextJobRefresh;

            void RefreshJobs(RoleLogic role)
            {
                var jobs=role.GetJobs(); Job first=jobs.Count==0?null:jobs[0];
                var replaced=jobs!=buildingSource || first!=buildingFirst;
                if(buildingViews==null && p.Now<nextJobRefresh && !replaced)return;
                if(buildingViews==null || replaced || jobCursor>jobs.Count)
                {
                    buildingSource=jobs; buildingFirst=first; buildingViews=Data.CreateDictionary<string,RegionView>();
                    buildingOres=Data.CreateDictionary<long,List<string>>();buildingScanTotal=buildingScanComplete=0;
                    buildingRegions=Data.CreateList<string>();buildingPages=Data.CreateList<string>();buildingParts=Data.CreateList<int>();jobCursor=buildingDone=pageCursor=0;
                }
                int work=0;
                while(jobCursor<jobs.Count && work++<64 && p.HasBudget(.55))
                {
                    Job j=jobs[jobCursor++];RegionView view;
                    var region=role.MapRegion(j.RegionId);
                    if(!buildingViews.TryGetValue(j.RegionId,out view))
                    {
                        var direction=region==null?j.Direction:region.Direction;
                        view=new RegionView {Center=region==null?j.Entry:region.Center,Search=region==null?j.Kind==JobKind.Survey:region.Search,
                            Up=Data.Perpendicular(direction,region==null?j.Up:region.Up)};
                        view.Right=Data.Cross(direction,view.Up);
                        if(view.Search)view.Extent=Data.Max(1,region==null?j.Depth+j.Radius:region.Radius);
                        buildingViews[j.RegionId]=view;buildingRegions.Add(j.RegionId);
                    }
                    var pos=view.Project(j.Entry);
                    if(!view.Search)view.Extent=Data.Max(view.Extent,Data.Max(Math.Abs(pos.X),Math.Abs(pos.Y))+j.Radius);
                    view.Total++;if(Finished(j)){view.Complete++;buildingDone++;}
                    if(j.Kind==JobKind.Survey){view.Rays++;buildingScanTotal++;if(Finished(j))buildingScanComplete++;}
                    if(j.Kind==JobKind.Probe && j.Outcome=="Complete")view.Sampled++;
                    if(j.Outcome=="SurveyEmpty")view.Empty++;
                    if(view.Dots.Count<400)view.Dots.Add(j);
                    if(j.Scan!=null)
                    {
                        view.Scanned++;if(view.Search)view.Ray(j.Scan);view.Rock(j.Scan);
                    }
                    else if(j.Kind==JobKind.Probe && j.EntityId!=0 && region!=null && !region.Gravity)
                        view.Rock(new ScanRecord {Asteroid=j.EntityId,Center=j.Entry,End=j.Entry});
                    // Probe jobs move into deposit regions; ore evidence belongs to the
                    // asteroid and must remain visible on the original search map.
                    long asteroid=j.Scan==null?j.EntityId:j.Scan.Asteroid;
                    if(asteroid!=0 && j.ConfirmedOres.Length>0)
                    {
                        List<string> ores;if(!buildingOres.TryGetValue(asteroid,out ores)){ores=Data.CreateList<string>();buildingOres[asteroid]=ores;}
                        foreach(var item in j.ConfirmedOres.Split(',')){var ore=item.Trim();if(ore.Length>0&&!ores.Contains(ore))ores.Add(ore);}
                    }
                }
                if(jobCursor<jobs.Count)return;
                work=0;
                while(pageCursor<buildingRegions.Count && work++<64 && p.HasBudget(.55))
                {
                    var id=buildingRegions[pageCursor++];int pages=Data.Max(1,(buildingViews[id].Rocks.Count+7)/8);
                    for(int n=0;n<pages;n++){buildingPages.Add(id);buildingParts.Add(n);}
                }
                if(pageCursor<buildingRegions.Count)return;
                mapViews=buildingViews;mapRegions=buildingPages;mapParts=buildingParts;completedJobs=buildingDone;
                mapOres=buildingOres;scanTotal=buildingScanTotal;scanComplete=buildingScanComplete;
                buildingViews=null;nextJobRefresh=p.Now+.5;
            }

            static string Area(double value) => value>=1e6?F(value/1e6,"0.00")+" km2":F(value,"0")+" m2";
            static readonly string[] MapOreNames={"Iron","Cobalt","Nickel","Silicon","Magnesium","Silver","Gold","Platinum","Uranium","Ice","Stone"};
            static readonly string[] MapOreSymbols={"Fe","Co","Ni","Si","Mg","Ag","Au","Pt","U","Ice","St"};
            static readonly Color[] MapOreColors={Rgb(220,120,85),Rgb(80,145,255),Rgb(145,200,120),Rgb(245,190,175),Rgb(245,135,220),Rgb(220,225,230),Rgb(255,205,65),Rgb(190,165,250),Rgb(170,245,70),Rgb(90,225,240),Rgb(160,140,115)};
            static Color OreColor(string ore){int n=Array.IndexOf(MapOreNames,ore);return n<0?White:MapOreColors[n];}
            static Vector2 CircleDirection(int step)
            {double angle=step*Math.PI/16;return ScreenPoint((float)Math.Cos(angle),(float)Math.Sin(angle));}
            void Map(Canvas c,List<Telemetry> fleet,int listPage)
            {
                if(mapRegions.Count==0){c.Text(L.DisplayNoArea,22,110,.55f,Amber);return;}
                int page=listPage%mapRegions.Count;var id=mapRegions[page];var view=mapViews[id];
                c.Text(view.Search?L.DisplayExplorationMap:L.DisplayMiningPlane,22,93,.6f,White);
                c.TextFit(view.Search?L.F(L.DisplayScanSummary,view.Scanned,view.Rays,view.Rocks.Count,view.Unknown):L.F(L.DisplayMapLegend,view.Sampled,view.Empty),22,120,c.Width-44,.40f,Blue);
                c.TextFit(L.F(L.DisplayScanProgress,Percent(scanTotal==0?0:(double)scanComplete/scanTotal),scanComplete,scanTotal),22,139,c.Width-44,.36f,Green);
                c.Box(22,154,c.Width-44,3,Panel);c.Box(22,154,scanTotal==0?0:(c.Width-44)*scanComplete/scanTotal,3,Green);
                float left=22,top=165,width=c.Width-44,height=c.Height-288,bottom=top+height;
                var origin=ScreenPoint(c.Width/2,top+height/2);var scale=(float)((Data.Min(width,height)-12)/(2*view.Extent));
                c.Box(left,top,width,height,Panel);
                c.Line(ScreenPoint(left,origin.Y),ScreenPoint(left+width,origin.Y),Background);
                c.Line(ScreenPoint(origin.X,top),ScreenPoint(origin.X,bottom),Background);
                if(view.Search)
                {
                    var cell=(float)(view.Extent/12)*scale;
                    for(int n=0;n<576;n++)if(view.Cells[n])c.Box(origin.X+(n%24-12)*cell,origin.Y-(n/24-11)*cell,cell-0.4f,cell-0.4f,Rgb(34,84,113));
                    for(int n=0;n<32;n++)
                    {
                        c.Line(origin+CircleDirection(n)*(float)view.Extent*scale,origin+CircleDirection(n+1)*(float)view.Extent*scale,Muted);
                    }
                }
                var dots=new HashSet<int>();
                foreach(var j in view.Dots)
                {
                    var point=origin+view.Project(j.Entry)*ScreenPoint(scale,-scale);
                    int cell=(int)(point.X/3)+(int)(point.Y/3)*4096;
                    if(!dots.Add(cell))continue;
                    c.Dot(point.X,point.Y,2,j.Outcome=="Blocked"||j.Outcome=="InvalidSample"?Red:j.Owner.Length>0?Amber:Finished(j)?Green:Muted);
                }
                var slots=new bool[32];
                int start=mapParts[page]*8,end=Data.Min(view.Rocks.Count,start+8);
                for(int n=start;n<end;n++)
                {
                    var rock=view.Rocks[n];var point=origin+view.Project(rock.Center)*ScreenPoint(scale,-scale);
                    List<string> ores;if(!mapOres.TryGetValue(rock.Asteroid,out ores)||ores.Count==0)ores=null;
                    var color=ores==null?Muted:OreColor(ores[0]);
                    float radius=rock.Sized?(float)Data.Length(rock.Size)*scale/2:4;
                    for(int k=0;k<32;k++)
                    {
                        var from=point+CircleDirection(k)*radius;
                        var to=point+CircleDirection(k+1)*radius;
                        if(from.X>=left&&from.X<=left+width&&from.Y>=top&&from.Y<=bottom&&to.X>=left&&to.X<=left+width&&to.Y>=top&&to.Y<=bottom)
                            c.Line(from,to,ores==null?Muted:OreColor(ores[k*ores.Count/32]),2);
                    }
                    point.X=(float)Data.Clamp(point.X,left+4,left+width-4);point.Y=(float)Data.Clamp(point.Y,top+3,bottom-3);
                    float y=top+(n-start)*height/8;slots[16+(n-start)*2]=slots[17+(n-start)*2]=true;
                    c.Line(point,ScreenPoint(left+width-174,y+7),color);
                    c.Box(left+width-173,y,173,26,Background);
                    c.Text("A"+(n+1)+" "+(rock.Sized?"~"+Area(RegionView.RockArea(rock)):"?"),left+width-170,y,.36f,color);
                    if(ores!=null)for(int k=0;k<ores.Count;k++)
                    {
                        int index=Array.IndexOf(MapOreNames,ores[k]);
                        c.TextFit(index<0?ores[k]:MapOreSymbols[index],left+width-170+k*165f/ores.Count,y+13,165f/ores.Count,.26f,OreColor(ores[k]));
                    }
                }
                // Two banks of label slots keep even co-located miners' IDs distinct.
                foreach(var t in fleet)
                {
                    var point=origin+view.Project(t.Position)*ScreenPoint(scale,-scale);
                    var outside=point.X<left||point.X>left+width||point.Y<top||point.Y>bottom;
                    point.X=(float)Data.Clamp(point.X,left+3,left+width-3);point.Y=(float)Data.Clamp(point.Y,top+3,bottom-3);
                    int bank=point.X>origin.X?16:0,row=(int)Data.Clamp((point.Y-top)*16/height,0,15),slot=-1;
                    for(int n=0;n<32;n++){int candidate=(bank+row+n)%32;if(!slots[candidate]){slot=candidate;break;}}
                    if(slot<0)break;slots[slot]=true;
                    float x=slot<16?left+5:left+width-170,y=top+(slot%16)*height/16;
                    var color=!Fresh(t)||t.State==FlightState.Fault?Red:Blue;
                    c.Line(point,ScreenPoint(slot<16?x+164:x,y+7),color);
                    c.Text("+",point.X,point.Y,.5f,color);
                    c.Box(x-2,y-1,169,Data.Max(13,c.TextHeight(.36f)+2),Background);
                    c.TextFit((outside?"*":"")+t.Id+(Fresh(t)?"":" / "+L.DisplayOffline),x,y,165,.36f,color);
                }
                c.TextFit(id+"  "+view.Complete+"/"+view.Total+"  +/- "+F(view.Extent,"0")+"m",22,bottom+8,width,.40f,White);
                c.TextFit(L.F(L.DisplayRockArea,Area(view.Area),view.Rocks.Count==0?0:start+1,end,view.Rocks.Count),22,bottom+28,width,.38f,Amber);
                c.TextFit(L.DisplayScanLegend,22,bottom+49,width,.36f,Blue);
                c.TextFit(L.DisplayScanCaution,22,bottom+69,width,.34f,Muted);
            }
        }
    }
}
#endif