wip: opt-in landing-pad contract + Core.Liminal.Field v2 pads (L145)
Layers may publish certified arrival/departure pads; the passage planner binds endpoints and tangents to them; no-pad layouts serialize unchanged (owner canonical hashes match v227). Pads removed the endpoint grade failures; seeds 0-4 passed at a 45 m margin, but seed 5 still has 289 blocked passage-capsule samples ~29 m before the departure pad, where the composed field stays solid (-0.162) inside the tube over native ground (-3.6). Ripple audit now keeps true route distance across masked gaps (with a synthetic control). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016dhxM43dnWvUu2B5Ph1iG1
This commit is contained in:
1 parent
a45ba9bb10
commit
7d654ff861
14 files changed
+1228
-66
No files matched your search
@@ -542,7 +542,8 @@ namespace world_assembly_detail
|
||||
const auto& liminal = stratum.liminalField;
|
||||
if (liminal.version != 0)
|
||||
{
|
||||
if (liminal.version != CoreLiminalFieldRecipeVersionV1
|
||||
if ((liminal.version != CoreLiminalFieldRecipeVersionV1
|
||||
&& liminal.version != CoreLiminalFieldRecipeVersionV2)
|
||||
|| static_cast<std::uint32_t>(liminal.variant) >= CoreLiminalFieldVariantCountV1
|
||||
|| liminal.seed != effectiveRecipe.seed
|
||||
|| liminal.variant != SelectCoreLiminalFieldVariantV1(liminal.seed)
|
||||
@@ -2719,6 +2720,402 @@ bool PlanCoreLiminalFieldV1(WorldAssemblyRecipeV1& recipe,
|
||||
return true;
|
||||
}
|
||||
|
||||
std::array<std::array<std::int64_t,2>,6> CoreLiminalRouteXYV2(
|
||||
std::int64_t startX,std::int64_t startY,std::int64_t finishX,
|
||||
std::int64_t finishY,std::int64_t qPerMetre)
|
||||
{
|
||||
return {{{{startX,startY}},{{-40*qPerMetre,40*qPerMetre}},
|
||||
{{-40*qPerMetre,-40*qPerMetre}},{{40*qPerMetre,-40*qPerMetre}},
|
||||
{{40*qPerMetre,40*qPerMetre}},{{finishX,finishY}}}};
|
||||
}
|
||||
|
||||
std::vector<WorldAssemblyRoutePointV1> BuildCoreLiminalRouteV2(
|
||||
const WorldAssemblySurfaceProfileV1& profile,
|
||||
const ResolvedLayoutLandingPadV1& arrivalPad,
|
||||
const ResolvedLayoutLandingPadV1& departurePad,
|
||||
const WorldAssemblyLiminalVariantDefinitionV1& definition,
|
||||
std::uint32_t seed,std::int32_t strateIndex,std::int64_t coreMicrons,
|
||||
std::int64_t qPerMetre,
|
||||
std::uint64_t& totalHorizontal,std::string& error)
|
||||
{
|
||||
error.clear();totalHorizontal=0;
|
||||
const auto groundHeightAt=[&](std::int64_t x,std::int64_t y)
|
||||
{
|
||||
if(profile.kind==WorldAssemblySurfaceProfileKindV1::flatField)
|
||||
return profile.h0Subvoxels;
|
||||
const std::int64_t turnX=world_assembly_detail::SurfaceProfileTurnAtCoordinate(
|
||||
x,profile.wavelengthXSubvoxels,profile.phaseXTurnsQ32);
|
||||
const std::int64_t turnY=world_assembly_detail::SurfaceProfileTurnAtCoordinate(
|
||||
y,profile.wavelengthYSubvoxels,profile.phaseYTurnsQ32);
|
||||
return profile.h0Subvoxels
|
||||
+world_assembly_detail::MulQ30(profile.amplitudeXSubvoxels,
|
||||
world_assembly_detail::CosineQ30(turnX))
|
||||
+world_assembly_detail::MulQ30(profile.amplitudeYSubvoxels,
|
||||
world_assembly_detail::CosineQ30(turnY));
|
||||
};
|
||||
const auto horizontalLengthQ=[](std::int64_t dx,std::int64_t dy)
|
||||
{return world_assembly_detail::IntegerSqrt(static_cast<std::uint64_t>(dx*dx+dy*dy));};
|
||||
const auto micrometresToQ=[&](std::int64_t micrometres)
|
||||
{return (micrometres*WorldAssemblySubvoxelsPerVoxel+coreMicrons/2)/coreMicrons;};
|
||||
const auto routeXY=CoreLiminalRouteXYV2(arrivalPad.positionSubvoxels[0],
|
||||
arrivalPad.positionSubvoxels[1],departurePad.positionSubvoxels[0],
|
||||
departurePad.positionSubvoxels[1],qPerMetre);
|
||||
std::vector<WorldAssemblyRoutePointV1> route;
|
||||
const std::int64_t pointSpacing=12*qPerMetre;
|
||||
const std::int64_t routeRadius=std::max<std::int64_t>(
|
||||
micrometresToQ(definition.routeHalfWidthMicrometres),micrometresToQ(500000));
|
||||
const auto routeId=[&](std::size_t ordinal)
|
||||
{
|
||||
std::uint64_t id=14695981039346656037ull;
|
||||
const auto add=[&](std::uint64_t value,std::uint32_t bytes)
|
||||
{for(std::uint32_t byte=0;byte<bytes;++byte)
|
||||
{id^=static_cast<std::uint8_t>(value>>(byte*8));id*=1099511628211ull;}};
|
||||
add(seed,4);add(static_cast<std::uint32_t>(strateIndex),4);add(ordinal,4);
|
||||
return id?id:1;
|
||||
};
|
||||
const auto appendRoutePoint=[&](std::int64_t x,std::int64_t y)
|
||||
{
|
||||
WorldAssemblyRoutePointV1 point;
|
||||
point.positionSubvoxels={x,y,groundHeightAt(x,y)};
|
||||
point.radiusSubvoxels=routeRadius;
|
||||
point.stableFeatureId=routeId(route.size());
|
||||
route.push_back(point);
|
||||
};
|
||||
const auto interpolate=[](std::int64_t start,std::int64_t delta,
|
||||
std::uint64_t step,std::uint64_t steps)
|
||||
{
|
||||
const std::int64_t numerator=delta*static_cast<std::int64_t>(step);
|
||||
const std::int64_t divisor=static_cast<std::int64_t>(steps);
|
||||
return start+(numerator>=0?(numerator+divisor/2)/divisor
|
||||
:(numerator-divisor/2)/divisor);
|
||||
};
|
||||
appendRoutePoint(routeXY.front()[0],routeXY.front()[1]);
|
||||
for(std::size_t segment=1;segment<routeXY.size();++segment)
|
||||
{
|
||||
const std::int64_t dx=routeXY[segment][0]-routeXY[segment-1][0];
|
||||
const std::int64_t dy=routeXY[segment][1]-routeXY[segment-1][1];
|
||||
const std::uint64_t length=horizontalLengthQ(dx,dy);
|
||||
if(length==0){error="Core.Liminal.Field v2 route contains a zero-horizontal segment";return {};}
|
||||
totalHorizontal+=length;
|
||||
const std::uint64_t steps=std::max<std::uint64_t>(1,
|
||||
(length+static_cast<std::uint64_t>(pointSpacing)-1)
|
||||
/static_cast<std::uint64_t>(pointSpacing));
|
||||
for(std::uint64_t step=1;step<=steps;++step)
|
||||
appendRoutePoint(interpolate(routeXY[segment-1][0],dx,step,steps),
|
||||
interpolate(routeXY[segment-1][1],dy,step,steps));
|
||||
}
|
||||
if(route.size()<2||totalHorizontal<300*static_cast<std::uint64_t>(qPerMetre)
|
||||
||route.front().positionSubvoxels[2]!=arrivalPad.floorHeightSubvoxels
|
||||
||route.back().positionSubvoxels[2]!=departurePad.floorHeightSubvoxels)
|
||||
{error="Core.Liminal.Field v2 pad-to-pad route is incomplete or too short";return {};}
|
||||
return route;
|
||||
}
|
||||
|
||||
bool PrepareCoreLiminalLandingPadsV2(WorldAssemblyRecipeV1& recipe,
|
||||
WorldAssemblyStratumPlanV1& stratum,ResolvedLayoutSnapshotV3& layout,
|
||||
std::string& error)
|
||||
{
|
||||
error.clear();
|
||||
if(layout.coreVoxelSizeMicrometres==0||stratum.strateIndex<0
|
||||
||stratum.topChunkZ<stratum.bottomChunkZ)
|
||||
{error="Core.Liminal.Field v2 needs a finite stratum and resolved Core voxel size";return false;}
|
||||
auto resolved=std::find_if(layout.strates.begin(),layout.strates.end(),
|
||||
[&](const auto& value){return value.strateIndex==stratum.strateIndex
|
||||
&&value.generatorKind=="SurfaceWorld";});
|
||||
if(resolved==layout.strates.end()||!resolved->content.presentInCurrentField)
|
||||
{error="Core.Liminal.Field v2 must target a resolved SurfaceWorld content band";return false;}
|
||||
|
||||
constexpr std::int64_t Q=WorldAssemblySubvoxelsPerVoxel;
|
||||
const std::int64_t coreMicrons=layout.coreVoxelSizeMicrometres;
|
||||
const auto micrometresToQ=[&](std::int64_t micrometres)
|
||||
{
|
||||
const std::int64_t numerator=micrometres*Q;
|
||||
return numerator>=0?(numerator+coreMicrons/2)/coreMicrons
|
||||
:(numerator-coreMicrons/2)/coreMicrons;
|
||||
};
|
||||
const std::int64_t qPerMetre=micrometresToQ(1000000);
|
||||
if(qPerMetre<=0){error="Core.Liminal.Field v2 could not resolve fixed-scale metres";return false;}
|
||||
|
||||
const std::uint32_t seed=recipe.seed;
|
||||
const WorldAssemblyLiminalVariantV1 variant=SelectCoreLiminalFieldVariantV1(seed);
|
||||
const auto& definition=CoreLiminalFieldVariantsV1[static_cast<std::size_t>(variant)];
|
||||
const std::int64_t bottom=static_cast<std::int64_t>(stratum.bottomChunkZ)
|
||||
*recipe.layoutChunkWidthSubvoxels;
|
||||
const std::int64_t top=(static_cast<std::int64_t>(stratum.topChunkZ)+1)
|
||||
*recipe.layoutChunkWidthSubvoxels;
|
||||
const std::int64_t contentBottom=micrometresToQ(resolved->content.bottomMicrometres);
|
||||
const std::int64_t profileRelief=variant==WorldAssemblyLiminalVariantV1::repeatingHills
|
||||
?2*micrometresToQ(definition.hillAmplitudeMicrometres):0;
|
||||
const std::int64_t deckSweepRadius=static_cast<std::int64_t>(std::ceil(std::hypot(
|
||||
static_cast<double>(definition.routeHalfWidthMicrometres+250000),
|
||||
static_cast<double>(definition.routeHalfThicknessMicrometres+250000))));
|
||||
const std::int64_t floorClearance=definition.routeHalfThicknessMicrometres
|
||||
+deckSweepRadius+250000;
|
||||
// Keep the field above the lower tunnel's mouth transition so the routed
|
||||
// departure has a clean open section before it reaches the cave wall.
|
||||
constexpr std::int64_t MinimumFieldGroundMarginMicrometres=20000000;
|
||||
const std::int64_t fieldGroundMargin=std::max(floorClearance,
|
||||
MinimumFieldGroundMarginMicrometres);
|
||||
const std::int64_t floorHeight=contentBottom+profileRelief
|
||||
+micrometresToQ(fieldGroundMargin);
|
||||
if(floorHeight<=bottom||top<=floorHeight+8*qPerMetre)
|
||||
{error="Core.Liminal.Field v2 has no room for its supported field and cap";return false;}
|
||||
|
||||
WorldAssemblySurfaceProfileV1 profile;
|
||||
profile.version=1;
|
||||
profile.kind=definition.surfaceKind;
|
||||
profile.h0Subvoxels=floorHeight;
|
||||
if(profile.kind==WorldAssemblySurfaceProfileKindV1::periodicHills)
|
||||
{
|
||||
profile.amplitudeXSubvoxels=micrometresToQ(definition.hillAmplitudeMicrometres);
|
||||
profile.amplitudeYSubvoxels=profile.amplitudeXSubvoxels;
|
||||
profile.wavelengthXSubvoxels=micrometresToQ(definition.hillWavelengthMicrometres);
|
||||
profile.wavelengthYSubvoxels=profile.wavelengthXSubvoxels;
|
||||
ResolveWorldAssemblySurfaceProfilePhasesV1(profile,seed);
|
||||
}
|
||||
if(!world_assembly_detail::ValidateSurfaceProfile(profile,seed,error))return false;
|
||||
|
||||
stratum.hasRecipeOverride=true;
|
||||
stratum.recipeOverride=recipe;
|
||||
stratum.recipeOverride.surfaceProfile=profile;
|
||||
stratum.recipeOverride.contentHash=0;
|
||||
stratum.surfaceProfile=profile;
|
||||
stratum.capHeightSubvoxels=top-4*qPerMetre;
|
||||
stratum.capUndulationSubvoxels=0;
|
||||
stratum.capDetailAmplitudeSubvoxels=0;
|
||||
stratum.capRidgeAmplitudeSubvoxels=0;
|
||||
stratum.waterEnabled=false;
|
||||
stratum.waterSurfaceSubvoxels=0;
|
||||
|
||||
const auto groundHeightAt=[&](std::int64_t x,std::int64_t y)
|
||||
{
|
||||
if(profile.kind==WorldAssemblySurfaceProfileKindV1::flatField)
|
||||
return profile.h0Subvoxels;
|
||||
const std::int64_t turnX=world_assembly_detail::SurfaceProfileTurnAtCoordinate(
|
||||
x,profile.wavelengthXSubvoxels,profile.phaseXTurnsQ32);
|
||||
const std::int64_t turnY=world_assembly_detail::SurfaceProfileTurnAtCoordinate(
|
||||
y,profile.wavelengthYSubvoxels,profile.phaseYTurnsQ32);
|
||||
return profile.h0Subvoxels
|
||||
+world_assembly_detail::MulQ30(profile.amplitudeXSubvoxels,
|
||||
world_assembly_detail::CosineQ30(turnX))
|
||||
+world_assembly_detail::MulQ30(profile.amplitudeYSubvoxels,
|
||||
world_assembly_detail::CosineQ30(turnY));
|
||||
};
|
||||
const auto routeXY=CoreLiminalRouteXYV2(-12*qPerMetre,0,12*qPerMetre,0,qPerMetre);
|
||||
const auto squareRoot=[](std::uint64_t value)
|
||||
{return world_assembly_detail::IntegerSqrt(value);};
|
||||
const auto directionQ16=[&](std::int64_t dx,std::int64_t dy)
|
||||
{
|
||||
const std::uint64_t length=squareRoot(static_cast<std::uint64_t>(dx*dx+dy*dy));
|
||||
if(length==0)return std::array<std::int32_t,2>{{0,0}};
|
||||
const auto rounded=[&](std::int64_t value)
|
||||
{return value>=0?(value+static_cast<std::int64_t>(length/2))
|
||||
/static_cast<std::int64_t>(length)
|
||||
:(value-static_cast<std::int64_t>(length/2))
|
||||
/static_cast<std::int64_t>(length);};
|
||||
return std::array<std::int32_t,2>{{
|
||||
static_cast<std::int32_t>(rounded(dx*65536)),
|
||||
static_cast<std::int32_t>(rounded(dy*65536))}};
|
||||
};
|
||||
const auto arrivalDirection=directionQ16(routeXY[1][0]-routeXY[0][0],
|
||||
routeXY[1][1]-routeXY[0][1]);
|
||||
const auto departureDirection=directionQ16(routeXY.back()[0]-routeXY[routeXY.size()-2][0],
|
||||
routeXY.back()[1]-routeXY[routeXY.size()-2][1]);
|
||||
const std::int64_t playerCenterAboveFloor=micrometresToQ(1130000);
|
||||
const std::int64_t landingClearance=micrometresToQ(6000000);
|
||||
const std::int64_t supportDepth=micrometresToQ(2000000);
|
||||
const auto makePad=[&](std::string id,ResolvedLayoutLandingPadRoleV1 role,
|
||||
const std::array<std::int64_t,2>& xy,const std::array<std::int32_t,2>& direction)
|
||||
{
|
||||
ResolvedLayoutLandingPadV1 pad;
|
||||
pad.stableId=std::move(id);pad.role=role;
|
||||
pad.floorHeightSubvoxels=groundHeightAt(xy[0],xy[1]);
|
||||
pad.positionSubvoxels={xy[0],xy[1],pad.floorHeightSubvoxels+playerCenterAboveFloor};
|
||||
pad.approachDirectionQ16=direction;
|
||||
pad.clearanceSubvoxels=landingClearance;
|
||||
pad.supportDepthSubvoxels=supportDepth;
|
||||
pad.certifiedWalkableWithSupport=true;
|
||||
return pad;
|
||||
};
|
||||
resolved->recipeId=std::string(CoreLiminalFieldRecipeIdV1);
|
||||
resolved->recipeVersion=CoreLiminalFieldRecipeVersionV2;
|
||||
resolved->landingPads.clear();
|
||||
resolved->landingPads.push_back(makePad("core/liminal-field/landing/arrival/000",
|
||||
ResolvedLayoutLandingPadRoleV1::arrival,routeXY.front(),arrivalDirection));
|
||||
resolved->landingPads.push_back(makePad("core/liminal-field/landing/departure/000",
|
||||
ResolvedLayoutLandingPadRoleV1::departure,routeXY.back(),departureDirection));
|
||||
std::uint64_t routeLength=0;
|
||||
stratum.route=BuildCoreLiminalRouteV2(profile,resolved->landingPads[0],
|
||||
resolved->landingPads[1],definition,seed,stratum.strateIndex,coreMicrons,
|
||||
qPerMetre,routeLength,error);
|
||||
if(!error.empty())return false;
|
||||
stratum.routeConstraints={1,micrometresToQ(340000),micrometresToQ(1760000),
|
||||
17560,micrometresToQ(125000),micrometresToQ(250000),
|
||||
micrometresToQ(1000000),micrometresToQ(120000),3};
|
||||
resolved->anchors.erase(std::remove_if(resolved->anchors.begin(),resolved->anchors.end(),
|
||||
[](const auto& anchor){return anchor.kind=="route_point"
|
||||
||anchor.kind=="liminal_route_point"||anchor.kind=="liminal_variant"
|
||||
||anchor.kind=="heightfield_profile";}),resolved->anchors.end());
|
||||
ResolvedLayoutAnchorV2 profileAnchor;
|
||||
profileAnchor.kind="heightfield_profile";
|
||||
profileAnchor.stableId="heightfield-profile/liminal/"
|
||||
+std::to_string(static_cast<std::uint32_t>(profile.kind));
|
||||
profileAnchor.units="profile kind enum";
|
||||
profileAnchor.values={1,static_cast<std::int64_t>(profile.kind)};
|
||||
resolved->anchors.push_back(std::move(profileAnchor));
|
||||
std::uint64_t routeAnchorDistance=0,lastRouteAnchorDistance=0;
|
||||
for(std::size_t pointIndex=0;pointIndex<stratum.route.size();++pointIndex)
|
||||
{
|
||||
if(pointIndex>0)
|
||||
{
|
||||
const auto& previous=stratum.route[pointIndex-1].positionSubvoxels;
|
||||
const auto& current=stratum.route[pointIndex].positionSubvoxels;
|
||||
const auto dx=static_cast<std::uint64_t>(current[0]-previous[0]);
|
||||
const auto dy=static_cast<std::uint64_t>(current[1]-previous[1]);
|
||||
routeAnchorDistance+=world_assembly_detail::IntegerSqrt(dx*dx+dy*dy);
|
||||
}
|
||||
const bool first=pointIndex==0;
|
||||
const bool last=pointIndex+1==stratum.route.size();
|
||||
const bool spaced=routeAnchorDistance-lastRouteAnchorDistance
|
||||
>=static_cast<std::uint64_t>(8*qPerMetre);
|
||||
if(!first&&!last&&!spaced)continue;
|
||||
const auto& point=stratum.route[pointIndex];
|
||||
ResolvedLayoutAnchorV2 routeAnchor;
|
||||
routeAnchor.kind="route_point";
|
||||
routeAnchor.stableId="route/"+std::to_string(pointIndex);
|
||||
routeAnchor.units="1/256 Core voxel";
|
||||
routeAnchor.values={point.positionSubvoxels[0],point.positionSubvoxels[1],
|
||||
point.positionSubvoxels[2]+playerCenterAboveFloor,point.radiusSubvoxels};
|
||||
resolved->anchors.push_back(std::move(routeAnchor));
|
||||
lastRouteAnchorDistance=routeAnchorDistance;
|
||||
}
|
||||
if(!layout.finalize(error))return false;
|
||||
const auto reportPad=[&](const ResolvedLayoutLandingPadV1& pad)
|
||||
{
|
||||
std::printf("[ResolvedLandingPad] strate=%d id=%s role=%s position_m=(%.3f,%.3f,%.3f) "
|
||||
"floor_m=%.3f approach_q16=(%d,%d) clearance_m=%.3f support_m=%.3f "
|
||||
"certified_walkable_with_support=true status=PASS\n",stratum.strateIndex,
|
||||
pad.stableId.c_str(),pad.role==ResolvedLayoutLandingPadRoleV1::arrival?"arrival":"departure",
|
||||
pad.positionSubvoxels[0]/static_cast<double>(qPerMetre),
|
||||
pad.positionSubvoxels[1]/static_cast<double>(qPerMetre),
|
||||
pad.positionSubvoxels[2]/static_cast<double>(qPerMetre),
|
||||
pad.floorHeightSubvoxels/static_cast<double>(qPerMetre),
|
||||
pad.approachDirectionQ16[0],pad.approachDirectionQ16[1],
|
||||
pad.clearanceSubvoxels/static_cast<double>(qPerMetre),
|
||||
pad.supportDepthSubvoxels/static_cast<double>(qPerMetre));
|
||||
};
|
||||
reportPad(resolved->landingPads[0]);reportPad(resolved->landingPads[1]);
|
||||
std::printf("[LiminalPadRoute] points=%zu path_length_m=%.3f spacing_max_m=4.000 "
|
||||
"profile_ground_following=true status=PASS\n",stratum.route.size(),
|
||||
routeLength/static_cast<double>(qPerMetre));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PlanCoreLiminalFieldV2(WorldAssemblyRecipeV1& recipe,
|
||||
WorldAssemblyStratumPlanV1& stratum,const ResolvedLayoutSnapshotV3& layout,
|
||||
std::string& error)
|
||||
{
|
||||
error.clear();
|
||||
if(layout.coreVoxelSizeMicrometres==0||stratum.strateIndex<0
|
||||
||stratum.topChunkZ<stratum.bottomChunkZ)
|
||||
{error="Core.Liminal.Field v2 needs a finite stratum and resolved Core voxel size";return false;}
|
||||
const auto resolved=std::find_if(layout.strates.begin(),layout.strates.end(),
|
||||
[&](const auto& value){return value.strateIndex==stratum.strateIndex
|
||||
&&value.recipeId==CoreLiminalFieldRecipeIdV1
|
||||
&&value.recipeVersion==CoreLiminalFieldRecipeVersionV2;});
|
||||
if(resolved==layout.strates.end()||!resolved->content.presentInCurrentField
|
||||
||stratum.surfaceProfile.version!=1)
|
||||
{error="Core.Liminal.Field v2 needs its prepared profile and resolved landing pads";return false;}
|
||||
const ResolvedLayoutLandingPadV1* arrivalPad=nullptr;
|
||||
const ResolvedLayoutLandingPadV1* departurePad=nullptr;
|
||||
for(const auto& pad:resolved->landingPads)
|
||||
{
|
||||
if(pad.role==ResolvedLayoutLandingPadRoleV1::arrival)
|
||||
{if(arrivalPad){error="Core.Liminal.Field v2 has multiple arrival pads";return false;}arrivalPad=&pad;}
|
||||
if(pad.role==ResolvedLayoutLandingPadRoleV1::departure)
|
||||
{if(departurePad){error="Core.Liminal.Field v2 has multiple departure pads";return false;}departurePad=&pad;}
|
||||
}
|
||||
if(!arrivalPad||!departurePad||!arrivalPad->certifiedWalkableWithSupport
|
||||
||!departurePad->certifiedWalkableWithSupport)
|
||||
{error="Core.Liminal.Field v2 requires certified arrival and departure pads";return false;}
|
||||
|
||||
const ResolvedLayoutPassageV1* arrival=nullptr;
|
||||
const ResolvedLayoutPassageV1* departure=nullptr;
|
||||
for(const auto& passage:layout.passages)
|
||||
{
|
||||
if(passage.lowerStrateIndex==stratum.strateIndex)
|
||||
{if(arrival){error="Core.Liminal.Field v2 requires one arrival passage";return false;}arrival=&passage;}
|
||||
if(passage.upperStrateIndex==stratum.strateIndex)
|
||||
{if(departure){error="Core.Liminal.Field v2 requires one departure passage";return false;}departure=&passage;}
|
||||
}
|
||||
if(!arrival||!departure||arrival->lowerMouthSubvoxels!=arrivalPad->positionSubvoxels
|
||||
||departure->upperMouthSubvoxels!=departurePad->positionSubvoxels)
|
||||
{error="Core.Liminal.Field v2 passage mouths are not bound to the certified layer pads";return false;}
|
||||
|
||||
constexpr std::int64_t Q=WorldAssemblySubvoxelsPerVoxel;
|
||||
const std::int64_t coreMicrons=layout.coreVoxelSizeMicrometres;
|
||||
const auto micrometresToQ=[&](std::int64_t micrometres)
|
||||
{
|
||||
const std::int64_t numerator=micrometres*Q;
|
||||
return numerator>=0?(numerator+coreMicrons/2)/coreMicrons
|
||||
:(numerator-coreMicrons/2)/coreMicrons;
|
||||
};
|
||||
const std::int64_t qPerMetre=micrometresToQ(1000000);
|
||||
const std::int64_t playerCenterAboveFloor=micrometresToQ(1130000);
|
||||
if(qPerMetre<=0||arrivalPad->positionSubvoxels[2]
|
||||
!=arrivalPad->floorHeightSubvoxels+playerCenterAboveFloor
|
||||
||departurePad->positionSubvoxels[2]
|
||||
!=departurePad->floorHeightSubvoxels+playerCenterAboveFloor)
|
||||
{error="Core.Liminal.Field v2 pad floor and capsule-center heights differ";return false;}
|
||||
|
||||
const std::uint32_t seed=recipe.seed;
|
||||
const WorldAssemblyLiminalVariantV1 variant=SelectCoreLiminalFieldVariantV1(seed);
|
||||
const auto& definition=CoreLiminalFieldVariantsV1[static_cast<std::size_t>(variant)];
|
||||
const auto& profile=stratum.surfaceProfile;
|
||||
std::uint64_t totalHorizontal=0;
|
||||
std::vector<WorldAssemblyRoutePointV1> route=BuildCoreLiminalRouteV2(profile,
|
||||
*arrivalPad,*departurePad,definition,seed,stratum.strateIndex,coreMicrons,
|
||||
qPerMetre,totalHorizontal,error);
|
||||
if(!error.empty())return false;
|
||||
if(route.size()!=stratum.route.size())
|
||||
{error="Core.Liminal.Field v2 route differs from the path used to certify its pads";return false;}
|
||||
for(std::size_t index=0;index<route.size();++index)
|
||||
if(route[index].stableFeatureId!=stratum.route[index].stableFeatureId
|
||||
||route[index].positionSubvoxels!=stratum.route[index].positionSubvoxels
|
||||
||route[index].radiusSubvoxels!=stratum.route[index].radiusSubvoxels)
|
||||
{error="Core.Liminal.Field v2 route differs from the path used to certify its pads";return false;}
|
||||
|
||||
WorldAssemblyLiminalFieldPlanV1 liminal;
|
||||
liminal.version=CoreLiminalFieldRecipeVersionV2;
|
||||
liminal.variant=variant;liminal.seed=seed;
|
||||
liminal.arrivalMouthSubvoxels=arrivalPad->positionSubvoxels;
|
||||
liminal.departureMouthSubvoxels=departurePad->positionSubvoxels;
|
||||
liminal.floorHeightSubvoxels=profile.h0Subvoxels;
|
||||
liminal.routeHalfWidthSubvoxels=micrometresToQ(definition.routeHalfWidthMicrometres);
|
||||
liminal.routeHalfThicknessSubvoxels=micrometresToQ(definition.routeHalfThicknessMicrometres);
|
||||
liminal.routeOffsetSubvoxels=150*qPerMetre;
|
||||
liminal.supportSpacingSubvoxels=micrometresToQ(definition.supportSpacingMicrometres);
|
||||
liminal.supportRadiusSubvoxels=micrometresToQ(definition.supportRadiusMicrometres);
|
||||
liminal.mouthFloorOffsetSubvoxels=playerCenterAboveFloor;
|
||||
stratum.route=std::move(route);
|
||||
stratum.routeConstraints={1,micrometresToQ(340000),micrometresToQ(1760000),
|
||||
17560,micrometresToQ(125000),micrometresToQ(250000),
|
||||
micrometresToQ(1000000),micrometresToQ(120000),3};
|
||||
stratum.liminalField=liminal;
|
||||
std::string validationError;
|
||||
if(!world_assembly_detail::ValidateSurfaceProfile(stratum.surfaceProfile,seed,validationError)
|
||||
||!world_assembly_detail::ValidateStratum(stratum,recipe,validationError))
|
||||
{error=validationError.empty()?"Core.Liminal.Field v2 produced an invalid plan":validationError;return false;}
|
||||
std::printf("[CoreLiminalFieldPlan] recipe=%.*s version=%u variant=%.*s seed=%u "
|
||||
"arrival_pad=%s departure_pad=%s route_points=%zu path_length_m=%.3f "
|
||||
"profile_ground_following=true route_grade_limit_deg=15.000 status=PASS\n",
|
||||
static_cast<int>(CoreLiminalFieldRecipeIdV1.size()),CoreLiminalFieldRecipeIdV1.data(),
|
||||
liminal.version,static_cast<int>(definition.name.size()),definition.name.data(),seed,
|
||||
arrivalPad->stableId.c_str(),departurePad->stableId.c_str(),stratum.route.size(),
|
||||
totalHorizontal/static_cast<double>(qPerMetre));
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string WorldAssemblyManifest::ToJson() const
|
||||
{
|
||||
std::string error;
|
||||
|
||||
Reference in new issue
Block a user