返回提交历史
Modified
AutoMiningScript.Tests/BulkMiningTests.cs
+34
-0
Modified
AutoMiningScript.Tests/CameraReadinessTests.cs
+18
-0
Modified
AutoMiningScript.Tests/DockingNavigationTests.cs
+40
-2
Modified
AutoMiningScript.Tests/MinerStateTests.cs
+33
-0
Modified
AutoMiningScript.Tests/PortYieldSafetyTests.cs
+7
-0
Modified
AutoMiningScript.Tests/SurveyNavigationTests.cs
+14
-0
Modified
AutoMiningScript/FleetController.cs
+6
-3
Modified
AutoMiningScript/Flight.cs
+3
-3
Modified
AutoMiningScript/Hardware.cs
+2
-2
Modified
AutoMiningScript/MinerController.cs
+24
-12
Modified
AutoMiningScript/Planning.cs
+17
-6
Modified
README.md
+4
-2
Modified
docs/VALIDATION.md
+6
-0
Added
docs/agent-debug/FULL_CYCLE_20261010.md
+30
-0
Added
tools/agent-debug/fleet_cycle.py
+210
-0
Added
tools/agent-debug/tests/test_fleet_cycle.py
+41
-0
Modified
tools/agent-debug/trace_ams.py
+21
-0
SpaceEngineersModDev/AutoMiningScript
Fix fleet exploration capacity and bulk-mining navigation stalls
f2f9797
代码差异
17 个文件
+510
-30
@@ -17,6 +17,29 @@ namespace AutoMiningScript.Tests
17
17
return p.Jobs.Single(j=>j.Kind==P.JobKind.Probe);
18
18
}
19
19
[Theory]
20
[InlineData(8)] [InlineData(12)] [InlineData(16)]
21
public void SpaceExplorationUsesEveryReadyMinerAtADistinctObservation(int count)
22
{
23
var r=new FleetStateTests.Rig(count,true);foreach(var t in r.Miners)t.Radius=4.9;r.RegisterAll();
24
r.Fleet.Command("survey 2000");r.Fleet.Command("start");
25
for(int n=0;n<300;n++){if(n%5==0)foreach(var t in r.Miners)r.Receive("TELEMETRY",t);r.Tick(1);}
26
var jobs=r.Planner.OwnedJobs.Values.ToList();Assert.Equal(count,jobs.Count);
27
Assert.All(jobs,j=>Assert.Equal(P.JobKind.Survey,j.Kind));Assert.Equal(count,jobs.Select(j=>j.Entry).Distinct().Count());
28
for(int i=0;i<jobs.Count;i++)for(int k=i+1;k<jobs.Count;k++)Assert.True(Vector3D.Distance(jobs[i].Entry,jobs[k].Entry)>=11.8);
29
Assert.All(r.Planner.Jobs,j=>Assert.Equal(2000,Vector3D.Distance(Vector3D.Zero,j.Entry+j.Direction*j.Depth),6));
30
}
31
[Theory]
32
[InlineData(0)] [InlineData(12)]
33
public void SpaceObservationGenerationRestoresLegacyAndFleetSizedCursors(int ships)
34
{
35
var p=new P.TaskPlanner(TestRig.Program());p.Survey(Vector3D.Zero,Vector3D.Zero,Vector3D.Forward,Vector3D.Up,2000,ships);
36
var saved=new MyIni();p.Save(saved);if(ships==0)saved.Set("region0","Rows",0);
37
var after=new P.TaskPlanner(TestRig.Program());after.Load(saved);Drain(p);Drain(after);
38
Assert.Equal(p.Jobs.Select(j=>j.Entry),after.Jobs.Select(j=>j.Entry));
39
for(int n=0;n<p.Jobs.Count;n++)Assert.True(Vector3D.Distance(p.Jobs[n].Direction,after.Jobs[n].Direction)<1e-12);
40
Assert.Equal(Math.Max(7,ships),after.Jobs.Select(j=>j.Entry).Distinct().Count());
41
}
42
[Theory]
20
43
[InlineData(12)] [InlineData(16)]
21
44
public void OneSurfaceHitCanEmployTheWholeReadyFleetWithoutOreConfirmation(int count)
22
45
{
@@ -113,5 +136,16 @@ namespace AutoMiningScript.Tests
113
136
var p=new P.TaskPlanner(TestRig.Program("[Survey]\nMaxDepth=80\nProbeDepth=3\n"));Hit(p);Drain(p);
114
137
Assert.All(p.Jobs.Where(j=>j.Kind==P.JobKind.Probe),j=>Assert.Equal(80,j.Depth));
115
138
}
139
140
[Fact]
141
public void GrazingAsteroidDiscoveryBuildsAnInwardPatchInsteadOfFollowingTheTangentialRay()
142
{
143
var p=new P.TaskPlanner(TestRig.Program());p.Survey(Vector3D.Zero,Vector3D.Zero,Vector3D.Forward,Vector3D.Up,2000);
144
var hit=new Vector3D(100,100,-700);var center=new Vector3D(200,250,-560);
145
p.Report(p.Jobs[0],new P.JobReport {Outcome="SurveyHit",Position=hit,Direction=new Vector3D(.2,-.1,.97),EntityId=42,
146
Scan=new P.ScanRecord {Start=Vector3D.Zero,End=hit,Center=center,Size=new Vector3D(512),Asteroid=42}});Drain(p);
147
var direction=Vector3D.Normalize(center-hit);var holes=p.Jobs.Where(j=>j.Kind==P.JobKind.Probe).ToList();Assert.Equal(145,holes.Count);
148
Assert.All(holes,j=>{Assert.True(Vector3D.Distance(direction,j.Direction)<1e-12);Assert.Equal(100,j.Depth);Assert.True(Vector3D.Distance(hit,j.Entry)<=43);});
149
}
116
150
}
117
151
}
@@ -394,6 +394,24 @@ namespace AutoMiningScript.Tests
394
394
Assert.True(selfHits>=18);Assert.True(clearHits>=18);
395
395
}
396
396
397
[Fact]
398
public void ACameraSeeingItsOwnHullCannotPreventTurningToAnUnobstructedLens()
399
{
400
var r=new ReturnNavigationTests.Rig();int sideHits=0;
401
r.Hit=()=>ReturnNavigationTests.Rig.Detection(r.Program.Me.CubeGrid.EntityId);
402
var mount=MatrixD.CreateWorld(Vector3D.Zero,Vector3D.Right,Vector3D.Up);
403
r.Ship.Cameras.Add(Camera(r,"Unobstructed side",2,()=>mount*r.Orientation)
404
.Method("Raycast",a=>{sideHits++;return default(MyDetectedEntityInfo);}).Value);
405
for(int n=0;n<80 && r.Flight.ScanClearance==0;n++)
406
{
407
r.Program.Now+=.05;r.Flight.TrafficAvoid=true;r.Flight.TrafficAt=r.Program.Now;
408
r.Flight.Move(Vector3D.Forward*2,Vector3D.Zero,r.Orientation.Forward,r.Orientation.Up,1.5,.3,true,.05,false,true,0,true);
409
r.Orientation=Field<MatrixD>(r.Flight,"previousDesired").GetOrientation();
410
}
411
Assert.True(sideHits>=18,r.Flight.Problem);Assert.True(r.HasTranslation);
412
Assert.True((r.Orientation.Forward-Vector3D.Forward).Length()>.1);
413
}
414
397
415
[Fact]
398
416
public void ACameraBlockedForOnePointCanStillCoverOtherPoints()
399
417
{
@@ -835,6 +835,24 @@ namespace AutoMiningScript.Tests
835
835
Assert.Equal(0,r.ConnectCalls);
836
836
}
837
837
838
[Theory]
839
[InlineData(true)]
840
[InlineData(false)]
841
public void TrafficSideStepUsesSideCamerasAndTurnsOnlyIfCoverageIsMissing(bool sideCamera)
842
{
843
var r=new DockRig();r.R.Position=r.Contact+Vector3D.Forward*1000;r.R.State(P.FlightState.Returning);
844
if(sideCamera)r.AddCamera(Vector3D.Right);
845
var start=r.R.Position;var target=start+Vector3D.Right*2;double turned=0;
846
for(int n=0;n<160 && r.R.Position.X<start.X+1;n++)
847
{
848
r.R.Program.Now+=.05;r.Flight.TrafficAvoid=true;r.Flight.TrafficAt=r.R.Program.Now;
849
r.Flight.TrafficPoint=target;Call(r.R.Miner,"YieldAtPort",.05);r.Integrate(.05);
850
turned=Math.Max(turned,(r.Orientation.Forward-Vector3D.Forward).Length());
851
}
852
Assert.True(r.Rays>=18);Assert.True(r.R.Position.X>start.X+1,r.Reason);
853
if(sideCamera)Assert.InRange(turned,0,.001);else Assert.True(turned>.1);
854
}
855
838
856
[Fact]
839
857
public void TrafficSideStepScansTheMotherHullWithoutTheDockContactExemption()
840
858
{
@@ -843,13 +861,16 @@ namespace AutoMiningScript.Tests
843
861
r.Flight.SetDockContact(50,wall,100);
844
862
r.Hit=end=>new MyDetectedEntityInfo(50,"Mother side wall",MyDetectedEntityType.LargeGrid,wall,MatrixD.Identity,Vector3.Zero,
845
863
VRage.Game.MyRelationsBetweenPlayerAndBlock.NoOwnership,new BoundingBoxD(wall-new Vector3D(.1),wall+new Vector3D(.1)),1);
864
bool sawWall=false;
846
865
for(int n=0;n<80;n++)
847
866
{
848
867
r.R.Program.Now+=.05;r.Flight.TrafficAvoid=true;r.Flight.TrafficAt=r.R.Program.Now;
849
868
r.Flight.TrafficPoint=r.R.Position+Vector3D.Right*2;Call(r.R.Miner,"YieldAtPort",.05);
869
sawWall|=r.Flight.Problem.Contains("Mother side wall");
850
870
}
851
Assert.True(r.Rays>0);Assert.Equal(0,r.Flight.CommandSpeed);Assert.Contains("Mother side wall",r.Flight.Problem);
852
Assert.Contains(P.L.MinerObstacleBraking,r.Flight.Problem);Assert.Equal(0,r.ConnectCalls);
871
Assert.True(r.Rays>0);Assert.True(sawWall);Assert.Equal(0,r.Flight.CommandSpeed);
872
Assert.Equal(P.L.MinerFindingRoute,r.Flight.Problem);Assert.Equal(0,r.ConnectCalls);
873
Assert.InRange(Vector3D.Distance(r.R.Position,r.Flight.RouteTarget),1.9,3);
853
874
}
854
875
855
876
[Fact]
@@ -869,6 +890,23 @@ namespace AutoMiningScript.Tests
869
890
Assert.True(passed);Assert.Equal(0,Read<int>(r.R.Miner,"returnIndex"));
870
891
}
871
892
893
[Theory]
894
[InlineData(false)]
895
[InlineData(true)]
896
public void ReturnRejoinsANearbyEarlierTrailButStillScansTheShortcut(bool blocked)
897
{
898
var r=new DockRig();r.R.State(P.FlightState.Returning);Write(r.R.Miner,"lane","");
899
r.R.Hardware.Radius=4.8;r.R.Position=r.Contact+Vector3D.Forward*1000;var start=r.R.Position;
900
var path=Read<List<Vector3D>>(r.R.Miner,"breadcrumbs");path.Clear();
901
path.AddRange(new[]{start+Vector3D.Forward*40,start+Vector3D.Forward*8,
902
start+Vector3D.Forward*8+Vector3D.Right*20,start+Vector3D.Right*20,start+Vector3D.Forward*20});
903
Write(r.R.Miner,"returnIndex",4);
904
if(blocked)r.Hit=end=>SphereHit(r,start+Vector3D.Forward*5,2,end);
905
for(int n=0;n<18;n++)r.Tick();
906
Assert.Equal(0,Read<int>(r.R.Miner,"returnIndex"));Assert.True(r.Rays>0);
907
if(blocked)Assert.Equal(0,r.Flight.CommandSpeed);else Assert.True(r.Flight.CommandSpeed>1);
908
}
909
872
910
[Fact]
873
911
public void ASharpReturnCornerIsNotCutByWaypointBlending()
874
912
{
@@ -120,6 +120,39 @@ namespace AutoMiningScript.Tests
120
120
Assert.False(r.Drill.Value.Enabled);
121
121
}
122
122
123
[Theory]
124
[InlineData(false)] [InlineData(true)]
125
public void BulkEntryInspectsTheWholeHoleAndAllowsUnevenOrEmptyEdges(bool emptyEdges)
126
{
127
var r=new MinerRig();r.Task();r.Position=Vector3D.Backward*22;r.State(P.FlightState.Align);int rays=0;
128
r.Camera.Method("Raycast",a=>
129
{
130
var end=(Vector3D)a[0];Assert.Equal(40,-end.Z,6);int ray=rays++;
131
if(emptyEdges && ray!=4)return default(MyDetectedEntityInfo);
132
return new MyDetectedEntityInfo(42,"asteroid",MyDetectedEntityType.Asteroid,Vector3D.Forward*(12+ray*6),MatrixD.Identity,Vector3.Zero,
133
MyRelationsBetweenPlayerAndBlock.NoOwnership,new BoundingBoxD(new Vector3D(-100),new Vector3D(100)),1);
134
});
135
for(int n=0;n<5;n++)Call(r.Miner,"Align",.1);
136
Assert.Equal(5,rays);Assert.Equal(P.FlightState.Drilling,r.StateNow);Assert.Equal(0,Read<P.Job>(r.Miner,"job").Progress);
137
}
138
139
[Fact]
140
public void BulkEntryWithFiveEmptyRaysDoesNotStartBlindDrilling()
141
{
142
var r=new MinerRig();r.Task();r.Position=Vector3D.Backward*22;r.State(P.FlightState.Align);
143
for(int n=0;n<5;n++)Call(r.Miner,"Align",.1);
144
Assert.NotEqual(P.FlightState.Drilling,r.StateNow);Assert.False(r.Drill.Value.Enabled);
145
Assert.Contains(r.Sent,p=>p.Kind=="RESULT" && P.JobReport.FromIni(p.Body).Message==P.L.MinerEntryVoxelUnconfirmed);
146
}
147
148
[Fact]
149
public void ManualEntryRetainsItsShortRangeSurfaceCheck()
150
{
151
var r=new MinerRig();r.Task();Read<P.Job>(r.Miner,"job").Kind=P.JobKind.Manual;r.Position=Vector3D.Backward*22;r.State(P.FlightState.Align);
152
r.Camera.Method("Raycast",a=>{Assert.Equal(5,-((Vector3D)a[0]).Z,6);return default(MyDetectedEntityInfo);});
153
Call(r.Miner,"Align",.1);Assert.NotEqual(P.FlightState.Drilling,r.StateNow);Assert.False(r.Drill.Value.Enabled);
154
}
155
123
156
[Theory]
124
157
[InlineData(false,true)]
125
158
[InlineData(false,false)]
@@ -58,6 +58,13 @@ namespace AutoMiningScript.Tests
58
58
Assert.True(P.Data.Flag(resumed.Body,"traffic","Avoid"));
59
59
Assert.Equal(priority.Id,P.Data.Text(resumed.Body,"traffic","Peer"));
60
60
Assert.Equal(point,P.Data.ReadVector(resumed.Body,"traffic","Point",Vector3D.Zero));
61
62
// The same third ship has stopped: the loser may now scan for a short
63
// detour instead of freezing a stationary cluster indefinitely.
64
third.Position=yielding.Position+lateral*8;third.Velocity=Vector3D.Zero;
65
ObserveFrame(rig);
66
var scan=rig.Sent.Last(p=>p.Kind=="TRAFFIC"&&p.Target==yielding.Id);
67
Assert.True(P.Data.Flag(scan.Body,"traffic","Avoid"));Assert.False(P.Data.Flag(scan.Body,"traffic","Wait"));
61
68
}
62
69
63
70
static FleetStateTests.Rig Encounter(Vector3D first,Vector3D second,Vector3D firstVelocity,Vector3D secondVelocity,double thrustMargin=100,double response=.625)
@@ -258,5 +258,19 @@ namespace AutoMiningScript.Tests
258
258
{Call(r.R.Miner,"Align",.05);r.Orientation=Read<MatrixD>(r.Flight,"previousDesired").GetOrientation();}
259
259
Assert.Equal(P.FlightState.Drilling,r.R.StateNow);
260
260
}
261
262
[Fact]
263
public void MiningApproachCannotLoopForeverOnLocallyValidDetours()
264
{
265
var r=new SurveyRig();r.Job.Kind=P.JobKind.Probe;r.R.Camera.Method("CanScan",a=>true);
266
var approach=Vector3D.Backward*22;
267
for(int n=0;n<1300 && !r.R.Sent.Any(p=>p.Kind=="RESULT");n++)
268
{
269
r.R.Position=approach+new Vector3D(Math.Cos(n*.05)*12,0,Math.Sin(n*.05)*12);r.Tick(false);
270
}
271
var report=P.JobReport.FromIni(Assert.Single(r.R.Sent,p=>p.Kind=="RESULT").Body);
272
Assert.Equal("Blocked",report.Outcome);Assert.False(report.EntryUnreachable);Assert.Null(report.Scan);
273
Assert.InRange(r.R.Program.Now,45,46);Assert.True(Read<bool>(r.R.Miner,"continuing"));
274
}
261
275
}
262
276
}
@@ -365,7 +365,10 @@ namespace AutoMiningScript
365
365
foreach(var third in miners.Values)
366
366
{
367
367
if(!P.HasBudget(.6))return;
368
if(third.Id!=peer.Id && NearTraffic(t,third)){TrafficMessage(t,true,false,yielding.Point,third.Id);return;}
368
// A moving third ship gets braking priority. For slow/static
369
// neighbours the yielding miner scans a short free detour;
370
// stopping every member of a cluster cannot create clearance.
371
if(third.Id!=peer.Id && Data.Length(third.Velocity)>.3 && NearTraffic(t,third)){TrafficMessage(t,true,false,yielding.Point,third.Id);return;}
369
372
}
370
373
if(Data.Distance(t.Position,yielding.Point)<.5 || Data.Dot(yielding.Point-t.Position,t.Position-peer.Position)<0)
371
374
yielding.Point=t.Position+YieldSide(t,peer)*2;
@@ -515,7 +518,7 @@ namespace AutoMiningScript
515
518
else
516
519
{
517
520
if (controller == null) { problem = L.FleetSurveyControllerRequired; return; }
518
P.Log(planner.Survey(controller.GetPosition(), controller.GetNaturalGravity(), controller.WorldMatrix.Forward, controller.WorldMatrix.Up, Data.ReadNumber(packet.Body, "survey", "Radius")));
521
P.Log(planner.Survey(controller.GetPosition(), controller.GetNaturalGravity(), controller.WorldMatrix.Forward, controller.WorldMatrix.Up, Data.ReadNumber(packet.Body, "survey", "Radius"),miners.Count));
519
522
}
520
523
markerReceiptSet.Add(receipt); markerReceipts.Enqueue(receipt);
521
524
while (markerReceipts.Count > 128) markerReceiptSet.Remove(markerReceipts.Dequeue());
@@ -824,7 +827,7 @@ namespace AutoMiningScript
824
827
{
825
828
if (controller == null) throw Data.Invalid(L.FleetSurveyControllerRequired);
826
829
var radius = parts.Length > 1 ? Marking.Number(parts[1]) : 0;
827
P.Log(planner.Survey(controller.GetPosition(), controller.GetNaturalGravity(), controller.WorldMatrix.Forward, controller.WorldMatrix.Up, radius)); return;
830
P.Log(planner.Survey(controller.GetPosition(), controller.GetNaturalGravity(), controller.WorldMatrix.Forward, controller.WorldMatrix.Up, radius,miners.Count)); return;
828
831
}
829
832
if (verb == "start" || verb == ResumeCommand || verb == "pause" || verb == ReturnCommand || verb == "release" || verb == ReturnAllCommand)
830
833
{
@@ -126,7 +126,7 @@ namespace AutoMiningScript
126
126
}
127
127
128
128
public bool Move(Vector3D target, Vector3D targetVelocity, Vector3D forward, Vector3D up,
129
double speed, double tolerance, bool scan, double dt, bool faceTravel = false, bool allowDetour = true, double minimumSpeed = 0)
129
double speed, double tolerance, bool scan, double dt, bool faceTravel = false, bool allowDetour = true, double minimumSpeed = 0, bool turnForCamera = false)
130
130
{
131
131
Blocked = false; Problem = ""; holdActive = false; CommandSpeed = 0;
132
132
if (h.Controller == null) { Blocked = true; Problem = L.MinerMissingController; return false; }
@@ -221,7 +221,7 @@ namespace AutoMiningScript
221
221
Problem = scanFailed ? (cameraProblem.Length>0?cameraProblem:L.MinerCameraUnavailable) : L.MinerEnvelopeScanPending+" "+scanIndex+"/18";
222
222
}
223
223
}
224
if(faceTravel && scan && scanFailed && !scanWaitingForCharge)desired=h.CameraAttitude(cameraTarget,up);
224
if((faceTravel || turnForCamera) && scan && scanFailed && !scanWaitingForCharge)desired=h.CameraAttitude(cameraTarget,up,occludedCameras);
225
225
if (faceTravel && !CanSupport(desired.Forward, desired.Up, 0.1))
226
226
{
227
227
var failure = Problem;
@@ -245,7 +245,7 @@ namespace AutoMiningScript
245
245
void FindDetour(Vector3D position,Vector3D target)
246
246
{
247
247
if(breadcrumbs.Count==16)breadcrumbs.RemoveAt(0);breadcrumbs.Add(position);
248
routeOptions.Clear();double step=Data.Max(6,h.Radius*3+5);
248
routeOptions.Clear();double step=TrafficBraking?Data.Max(2,h.Radius*.6):Data.Max(6,h.Radius*3+5);
249
249
for(int x=-1;x<=1;x++)for(int y=-1;y<=1;y++)for(int z=-1;z<=1;z++)
250
250
if(x!=0 || y!=0 || z!=0)routeOptions.Add(position+Data.TransformNormal(Data.Unit(new Vector3D(x,y,z),basis.Forward),basis)*step);
251
251
routeOptions.Sort((a,b)=>RouteCost(a,target,step).CompareTo(RouteCost(b,target,step)));
@@ -293,12 +293,12 @@ namespace AutoMiningScript
293
293
Math.Abs(Math.Atan2(ray.X,-ray.Z))<=cone && Math.Abs(Math.Atan2(ray.Y,Math.Sqrt(ray.X*ray.X+ray.Z*ray.Z)))<=cone);
294
294
}
295
295
296
public MatrixD CameraAttitude(Vector3D target, Vector3D up)
296
public MatrixD CameraAttitude(Vector3D target, Vector3D up, HashSet<long> occluded = null)
297
297
{
298
298
var current = Controller.WorldMatrix; IMyCameraBlock best = null; double score = -2;
299
299
foreach (var camera in Cameras)
300
300
{
301
if (!IsLocal(camera) || !camera.IsWorking) continue;
301
if (!IsLocal(camera) || !camera.IsWorking || (occluded!=null && occluded.Contains(camera.EntityId))) continue;
302
302
if (CameraCovers(camera,target)) return current;
303
303
var alignment = Data.Dot(camera.WorldMatrix.Forward,Data.Unit(target-camera.GetPosition(),current.Forward));
304
304
if (alignment>score) { score=alignment; best=camera; }
@@ -160,8 +160,9 @@ namespace AutoMiningScript
160
160
if(Data.LengthSquared(step)<.01)step=Data.Perpendicular(normal,ship.Controller.WorldMatrix.Up)*2;
161
161
point=Position+step;
162
162
}
163
var f=near?ship.Controller.WorldMatrix.Forward:Data.Unit(point-Position,ship.Controller.WorldMatrix.Forward);
164
flight.Move(point,near?dock.PointVelocity(point):Data.Zero,f,near?ship.Controller.WorldMatrix.Up:TravelUp(f),1.5,.3,true,dt,!near,false);
163
// A short lateral escape uses the cameras on that side; turning
164
// the whole hull repeatedly invalidates scans and blocks both ships.
165
flight.Move(point,near?dock.PointVelocity(point):Data.Zero,ship.Controller.WorldMatrix.Forward,ship.Controller.WorldMatrix.Up,1.5,.3,true,dt,false,true,0,!near);
165
166
}
166
167
else if(near)flight.Move(Position,dock.PointVelocity(Position),ship.Controller.WorldMatrix.Forward,ship.Controller.WorldMatrix.Up,0,.3,false,dt);
167
168
else flight.Hold(dt);
@@ -170,7 +171,7 @@ namespace AutoMiningScript
170
171
double grace=Data.Min(Data.Max(0,dt),Data.Max(0,10-trafficDelay));trafficDelay+=grace;progressAt+=grace;
171
172
stableSince=-1;reason=L.F(L.MinerTrafficYield,flight.TrafficPeer);
172
173
if(flight.TrafficAvoid && flight.Problem.Length>0)reason+=" / "+flight.Problem;
173
if(SurveyTransitResult(dt,false))return true;
174
if(TransitResult(dt,false))return true;
174
175
if(state==FlightState.DockAlign || state==FlightState.DockApproach)CheckDockTimeout();
175
176
return true;
176
177
}
@@ -246,22 +247,23 @@ namespace AutoMiningScript
246
247
// Observation rays may point sideways from the arrival path. Keep the
247
248
// travel camera facing the actual leg (including detours) until stopped.
248
249
var arrived=flight.Move(target,Data.Zero,face,Data.Perpendicular(face,cruiseUp),CruiseSpeed,surveying?1:.4,true,dt,true);
249
if(SurveyTransitResult(dt,arrived))return;
250
if(TransitResult(dt,arrived))return;
250
251
reason=L.F(L.MinerDockNavigationProgress,L.MinerTravelTaskEntry,distance,Data.Length(ship.Controller.GetShipVelocities().LinearVelocity),flight.AttitudeError*180/Math.PI);
251
252
if(flight.Problem.Length>0)reason=flight.Problem+" / "+reason;
252
253
RecordBreadcrumb();
253
if(!surveying && flight.Blocked && P.Now-stateSince>45) {FinishBlocked(L.MinerRouteBlocked+" / "+flight.Problem);return;}
254
254
if(arrived)Set(surveying?FlightState.Survey:FlightState.Align,L.MinerTaskEntryReached);
255
255
}
256
bool SurveyTransitResult(double dt,bool arrived)
256
bool TransitResult(double dt,bool arrived)
257
257
{
258
if(state!=FlightState.Transit || job==null || job.Kind!=JobKind.Survey)return false;
259
double distance=Data.Distance(Position,job.Entry);
258
if(state!=FlightState.Transit || job==null)return false;
259
bool survey=job.Kind==JobKind.Survey;Vector3D f,u;
260
double distance=Data.Distance(Position,survey?job.Entry:TipTarget(-Approach,out f,out u));
260
261
// Detours/yields must actually advance toward the observation entry.
261
262
if(distance<lastProgress-2){lastProgress=distance;progressAt=P.Now;trafficDelay=0;}
262
263
// A real safety-camera surface observation takes precedence over timeout.
263
if(flight.SurfaceHit!=null){var hit=flight.SurfaceHit;hit.JobId=job.Id;CompleteJob(hit);return true;}
264
if(survey && flight.SurfaceHit!=null){var hit=flight.SurfaceHit;hit.JobId=job.Id;CompleteJob(hit);return true;}
264
265
if(arrived || P.Now-progressAt<=45)return false;
266
if(!survey){flight.Hold(dt);FinishBlocked(L.MinerRouteBlocked+" / "+flight.Problem);return true;}
265
267
flight.Hold(dt);var report=MakeReport("Blocked","",L.MinerSurveyEntryStalled);report.EntryUnreachable=true;CompleteJob(report);return true;
266
268
}
267
269
void Align(double dt)
@@ -280,7 +282,11 @@ namespace AutoMiningScript
280
282
else if(entryProbe==2)offset=-right*halfWidth;
281
283
else if(entryProbe==3)offset=job.Up*halfHeight;
282
284
else if(entryProbe==4)offset=-job.Up*halfHeight;
283
var test=job.Entry+offset+job.Direction*Data.Min(job.Depth,Data.Max(relief,job.Progress+3));
285
// Bulk holes share a reference plane, not a flat asteroid surface.
286
// Inspect the whole reserved depth; empty edges are valid if another
287
// ray confirms the voxel and the drills cover the complete hull.
288
bool bulk=job.Kind==JobKind.Probe;
289
var test=job.Entry+offset+job.Direction*(bulk?job.Depth:Data.Min(job.Depth,Data.Max(relief,job.Progress+3)));
284
290
foreach(var camera in ship.Cameras)
285
291
{
286
292
if(!camera.IsWorking || camera.CubeGrid!=P.Me.CubeGrid || !camera.CanScan(test))continue;
@@ -294,9 +300,10 @@ namespace AutoMiningScript
294
300
}
295
301
if(!ready) {reason=L.MinerWaitEntryScan;if(P.Now-stateSince>60)FinishBlocked(reason);return;}
296
302
if(obstructed) {FinishBlocked(L.MinerEntryOccupied);return;}
297
if(!voxel && job.Progress<=0) {FinishBlocked(L.MinerEntryVoxelUnconfirmed);return;}
298
if(job.Progress<=0 && entryFarthest-entryNearest>relief) {FinishBlocked(L.MinerEntryReliefExcessive);return;}
303
if(!bulk && !voxel && job.Progress<=0) {FinishBlocked(L.MinerEntryVoxelUnconfirmed);return;}
304
if(!bulk && job.Progress<=0 && entryFarthest-entryNearest>relief) {FinishBlocked(L.MinerEntryReliefExcessive);return;}
299
305
if(++entryProbe<5) {reason=L.F(L.MinerEntryScanProgress, entryProbe);return;}
306
if(bulk && entryNearest==double.MaxValue && job.Progress<=0) {FinishBlocked(L.MinerEntryVoxelUnconfirmed);return;}
300
307
ship.SetSampling(true);ResetSample();sampleDepth=job.Progress;lastProgress=job.Progress;progressAt=P.Now;
301
308
Set(FlightState.Drilling,L.MinerDrillingAxis);
302
309
lastProgress=Data.Dot(DrillTip()-job.Entry,job.Direction);progressAt=P.Now;
@@ -459,6 +466,11 @@ namespace AutoMiningScript
459
466
{returnIndex=-1;Set(FlightState.DockAlign,L.MinerDockOuterEntry,true);DockAlign(dt);return;}
460
467
if(returnIndex>=0)
461
468
{
469
// Entry searches can leave loops in the trail. Rejoin its earliest
470
// nearby point instead of replaying the loop; Move still scans the
471
// entire shortcut and finds a detour if its actual space is occupied.
472
for(int n=0;n<returnIndex;n++)if(Data.Distance(Position,breadcrumbs[n])<ship.Radius*2)
473
{returnIndex=n;flight.ResetRoute();break;}
462
474
// Look beyond the cruise braking distance, not just the next 10 m
463
475
// sample. Preserve sharp corners; each blended leg still needs scans.
464
476
double speed=CruiseSpeed,look=speed*1.5+speed*speed/(2*Data.Max(.1,flight.BrakeAcceleration*.65));
@@ -196,7 +196,7 @@ namespace AutoMiningScript
196
196
AddRegion(r);Generate(r);GenerateStep();
197
197
return L.F(L.FleetManualQueued,r.Id,nx*ny);
198
198
}
199
public string Survey(Vector3D center,Vector3D gravity,Vector3D forward,Vector3D up,double requestedRadius)
199
public string Survey(Vector3D center,Vector3D gravity,Vector3D forward,Vector3D up,double requestedRadius,int ships=0)
200
200
{
201
201
var planet=gravity.LengthSquared()>0.01;
202
202
var radius=requestedRadius>0?requestedRadius:N("Survey","Radius",planet?300:2000,20,20000);
@@ -211,7 +211,7 @@ namespace AutoMiningScript
211
211
}
212
212
else
213
213
{
214
r.Columns=p.Config.Integer("Survey","SpaceRays",42,12,128);r.Total=7*r.Columns;
214
r.Columns=p.Config.Integer("Survey","SpaceRays",42,12,128);r.Rows=Data.Max(7,Data.Min(16,ships));r.Total=r.Rows*r.Columns;
215
215
if(Jobs.Count+reserved+r.Total>Limit) throw Data.Invalid(L.FleetSpaceSurveyLimit);
216
216
r.Generation="Space";
217
217
}
@@ -229,6 +229,11 @@ namespace AutoMiningScript
229
229
else if(k<side*3) {x=ring-(k-side*2);y=ring;}
230
230
else {x=-ring;y=ring-(k-side*3);}
231
231
}
232
static Vector3D SkyDirection(MatrixD basis,int ray,int count,double phase=0)
233
{
234
double y=1-2*(ray+.5)/count,phi=(ray+phase)*2.399963229728653,radial=Math.Sqrt(Data.Max(0,1-y*y));
235
return basis.Right*(Math.Cos(phi)*radial)+basis.Up*y+basis.Forward*(Math.Sin(phi)*radial);
236
}
232
237
public void GenerateStep()
233
238
{
234
239
int work=0;
@@ -257,8 +262,8 @@ namespace AutoMiningScript
257
262
{
258
263
int point=index/r.Columns,ray=index%r.Columns;var basis=Data.Frame(r.Center,r.Direction,r.Up);
259
264
var offset=point==1?basis.Right:point==2?basis.Left:point==4?basis.Down:point==5?basis.Forward:point==6?basis.Backward:basis.Up;
260
double y=1-2*(ray+0.5)/r.Columns,phi=ray*2.399963229728653,radial=Math.Sqrt(Data.Max(0,1-y*y));
261
dir=basis.Right*(Math.Cos(phi)*radial)+basis.Up*y+basis.Forward*(Math.Sin(phi)*radial);
265
if(point>=7)offset=SkyDirection(basis,point-7,r.Rows-7,.5);
266
dir=SkyDirection(basis,ray,r.Columns);
262
267
entry+=offset*(point==0?Data.Min(r.BaseClearance,r.Radius*0.2):r.Radius*0.35);
263
268
var local=entry-r.Center;var dot=Data.Dot(local,dir);depth=Data.Min(10000,-dot+Math.Sqrt(dot*dot+r.Radius*r.Radius-local.LengthSquared()));kind=JobKind.Survey;
264
269
}
@@ -321,7 +326,13 @@ namespace AutoMiningScript
321
326
var known=KnownSurface(report);
322
327
if(r!=null&&!known&&Inside(r,report.Position)&&report.EntityId!=0&&report.Direction.LengthSquared()>0.1)
323
328
{
324
var direction=Data.Unit(report.Direction,Data.Forward);var depth=BoundDepth(r,report.Position,direction,N("Survey","MaxDepth",100,1,100));
329
var direction=Data.Unit(report.Direction,Data.Forward);
330
// A grazing discovery ray makes neighbouring holes cut mostly
331
// empty space. Point the patch inward; every hole still checks
332
// actual voxel hits, since the bounds are only an estimate.
333
if(report.Scan!=null && report.Scan.Sized && report.Scan.Asteroid==report.EntityId && Data.Dot(report.Scan.Center-report.Position,direction)>0)
334
direction=Data.Unit(report.Scan.Center-report.Position,direction);
335
var depth=BoundDepth(r,report.Position,direction,N("Survey","MaxDepth",100,1,100));
325
336
if(depth>=1) {var seed=Add(r,JobKind.Probe,report.Position,direction,depth,report.EntityId);if(seed!=null)Expand(seed);}
326
337
}
327
338
}
@@ -498,7 +509,7 @@ namespace AutoMiningScript
498
509
{
499
510
if(r.Generation.Length==0)return;
500
511
if((r.Generation!="Manual"&&r.Generation!="Ground"&&r.Generation!="Space"&&r.Generation!="Mine")||r.Cursor<0||r.Total<1||r.Total>4096||r.Cursor>=r.Total||r.Columns<0||r.Columns>4096||r.Rows<0||r.Rows>4096||r.Spacing<0||r.BaseClearance<0||r.ScanDistance<0)throw Data.Invalid(L.FleetStoredRegionInvalid);
501
if((r.Generation=="Manual"&&(r.Columns<1||r.Rows<1||(long)r.Columns*r.Rows!=r.Total))||(r.Generation=="Space"&&(r.Columns<12||r.Columns>128||r.Total!=7*r.Columns)))throw Data.Invalid(L.FleetStoredRegionInvalid);
512
if((r.Generation=="Manual"&&(r.Columns<1||r.Rows<1||(long)r.Columns*r.Rows!=r.Total))||(r.Generation=="Space"&&(r.Columns<12||r.Columns>128||r.Rows>16||r.Total!=Data.Max(7,r.Rows)*r.Columns)))throw Data.Invalid(L.FleetStoredRegionInvalid);
502
513
if(r.Generation=="Mine" && (!r.Bulk||!r.Patch||r.Columns<1||r.Rows<1||r.Total>(long)r.Columns*r.Rows))throw Data.Invalid(L.FleetStoredRegionInvalid);
503
514
Generate(r);
504
515
}
@@ -184,7 +184,7 @@ XFE AMS 是 Space Engineers 原版编程块采矿舰队系统:自主推进器
184
184
启动
185
185
```
186
186
187
`探索` 在主控当前位置冻结一个有限区域;标点端的同名命令也使用主控当前位置。默认大气半径 300 m、太空半径 2000 m,可用参数覆盖。大气按地表采样点搜索,太空按有限观察点及方向扫描小行星。
187
`探索` 在主控当前位置冻结一个有限区域;标点端的同名命令也使用主控当前位置。默认大气半径 300 m、太空半径 2000 m,可用参数覆盖。大气按地表采样点搜索,太空按有限观察点及方向扫描小行星。新建太空搜索区按登记舰队数量提供 7–16 个独立观察点,十二台舰队可同时领取十二个位置;原有搜索区保持其已保存的布局。
188
188
189
189
航行避障与探索扫描都利用已安装的前后、左右、上下各方向镜头。目标方向已有工作镜头覆盖时保留船体姿态;镜头正在充能时等待或使用同方向其他镜头,不因缺电转船。只有目标射线缺少有效视场时,才按实际镜头安装朝向转动探测,并检查该姿态的悬停与制动能力。扫描阶段以已接受的观察位置停驻,不再要求朝侧面挪回更精确的坐标。射线终点仍由冻结的探索任务决定,实际命中位置与方向回传主控。导航避障射线命中的行星或小行星表面也会交给探矿逻辑:若小行星挡在观察点前方,可直接报告实际命中并申请试钻,不必先穿过小行星到点。主控仍核对冻结区域边界并去重,摄像机列表仍仅在初始化或明确重扫时更新。
190
190
@@ -204,7 +204,9 @@ XFE AMS 是 Space Engineers 原版编程块采矿舰队系统:自主推进器
204
204
205
205
加载旧任务时,主控会逐步扩展孤立试钻点,并把旧环带区域迁移为整片网格,保留已完成孔、正在执行的孔及其进度和归属。因旧“两圈没矿”规则结束的区域可继续开采;迁移后的网格和生成游标会持久保存,重载不会反复创建相同任务。无需清空任务或重新标定。
206
206
207
`[探索] 母舰安全间距=80` 将大气观察点沿反重力方向抬高 80 m;太空首个观察点也避开母舰中心,且保持在搜索球内。搜索区域仍以创建时的世界坐标固定。按母舰体积和附近地形增大净空;不把母舰内部当作可到达的观察位置。入孔前执行中心和四周共五点检查,`[采矿] 入口高差上限=5` 限制新孔入口的地表起伏;已有孔道发现其他船体占用时拒绝钻进。
207
`[探索] 母舰安全间距=80` 将大气观察点沿反重力方向抬高 80 m;太空首个观察点也避开母舰中心,且保持在搜索球内。搜索区域仍以创建时的世界坐标固定。按母舰体积和附近地形增大净空;不把母舰内部当作可到达的观察位置。取得小行星范围后,新开采区朝内部布孔,减少擦边射线造成的空孔。自动开采入孔前沿完整任务深度检查中心及周围五条射线,允许凹凸表面和部分空射线;全部未命中、其他船体占用或钻头不能覆盖机身时拒绝钻进。手动钻孔仍由 `[采矿] 入口高差上限=5` 限制新孔入口的地表起伏。
208
209
前往扫描点或开采入口时,持续 45 秒未接近至少 2 米会报告受阻并申请续派,短暂避让只增加有限宽限时间。近距离避让优先使用侧向镜头平移;实际被自身船体遮挡的镜头不再阻止改用其他镜头或转向探测。返航会消除邻近航迹点形成的回环,缩短后的航段仍须扫描确认空间。
208
210
209
211
请区分结果:`SurveyEmpty` 表示该次扫描没有表面命中;钻采未检出只表示已采样部分没有目标矿增量;“采样完成”不等于已经确定地下无矿;开采区的“指定范围采掘完成”表示其计划钻孔全部完成。受阻、无效采样或任务预算不足的区域不能当作正常完成。
210
212