Merge eureka/aetheria-gravity-gravity-subtract
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
5dd1f6b71f
22 changed files with 407 additions and 425 deletions
|
|
@ -31,7 +31,6 @@ Transform:
|
|||
m_Children:
|
||||
- {fileID: 3973935122748960680}
|
||||
- {fileID: 3973935121058605868}
|
||||
- {fileID: 3973935122114073483}
|
||||
- {fileID: 3973935121358855036}
|
||||
m_Father: {fileID: 0}
|
||||
m_RootOrder: 0
|
||||
|
|
@ -53,7 +52,6 @@ MonoBehaviour:
|
|||
Icon: {fileID: 3973935121358855037}
|
||||
Generator: {fileID: 0}
|
||||
SunMaterial: {fileID: 3973935122748960685}
|
||||
GravityWaves: {fileID: 3973935122114073485}
|
||||
--- !u!1 &3973935121058605867
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
|
@ -220,89 +218,6 @@ MeshRenderer:
|
|||
m_SortingLayer: 0
|
||||
m_SortingOrder: 0
|
||||
m_AdditionalVertexStreams: {fileID: 0}
|
||||
--- !u!1 &3973935122114073482
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 3973935122114073483}
|
||||
- component: {fileID: 3973935122114073486}
|
||||
- component: {fileID: 3973935122114073485}
|
||||
m_Layer: 10
|
||||
m_Name: Gravity Waves
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &3973935122114073483
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3973935122114073482}
|
||||
m_LocalRotation: {x: 0.7071068, y: -0, z: -0, w: 0.7071068}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 3973935120991099173}
|
||||
m_RootOrder: 2
|
||||
m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0}
|
||||
--- !u!33 &3973935122114073486
|
||||
MeshFilter:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3973935122114073482}
|
||||
m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0}
|
||||
--- !u!23 &3973935122114073485
|
||||
MeshRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 3973935122114073482}
|
||||
m_Enabled: 1
|
||||
m_CastShadows: 1
|
||||
m_ReceiveShadows: 1
|
||||
m_DynamicOccludee: 1
|
||||
m_StaticShadowCaster: 0
|
||||
m_MotionVectors: 1
|
||||
m_LightProbeUsage: 1
|
||||
m_ReflectionProbeUsage: 1
|
||||
m_RayTracingMode: 2
|
||||
m_RayTraceProcedural: 0
|
||||
m_RenderingLayerMask: 1
|
||||
m_RendererPriority: 0
|
||||
m_Materials:
|
||||
- {fileID: 2100000, guid: 11fbaa87dcc7a3743a6bd3aa5c86e5ae, type: 2}
|
||||
m_StaticBatchInfo:
|
||||
firstSubMesh: 0
|
||||
subMeshCount: 0
|
||||
m_StaticBatchRoot: {fileID: 0}
|
||||
m_ProbeAnchor: {fileID: 0}
|
||||
m_LightProbeVolumeOverride: {fileID: 0}
|
||||
m_ScaleInLightmap: 1
|
||||
m_ReceiveGI: 1
|
||||
m_PreserveUVs: 0
|
||||
m_IgnoreNormalsForChartDetection: 0
|
||||
m_ImportantGI: 0
|
||||
m_StitchLightmapSeams: 1
|
||||
m_SelectedEditorRenderState: 3
|
||||
m_MinimumChartSize: 4
|
||||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingOrder: 0
|
||||
m_AdditionalVertexStreams: {fileID: 0}
|
||||
--- !u!1 &3973935122748960663
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
|
|
|||
|
|
@ -31,7 +31,6 @@ Transform:
|
|||
m_Children:
|
||||
- {fileID: 7852221720971737233}
|
||||
- {fileID: 7852221720969441490}
|
||||
- {fileID: 7852221720741322892}
|
||||
- {fileID: 7852221720816817928}
|
||||
- {fileID: 7852221720964551245}
|
||||
m_Father: {fileID: 0}
|
||||
|
|
@ -54,92 +53,8 @@ MonoBehaviour:
|
|||
Icon: {fileID: 7852221720964551246}
|
||||
Generator: {fileID: 0}
|
||||
SunMaterial: {fileID: 7852221720971737234}
|
||||
GravityWaves: {fileID: 7852221720741322894}
|
||||
FogTint: {fileID: 7852221720816817929}
|
||||
Light: {fileID: 8064377225639657765}
|
||||
--- !u!1 &7852221720741322899
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 7852221720741322892}
|
||||
- component: {fileID: 7852221720741322895}
|
||||
- component: {fileID: 7852221720741322894}
|
||||
m_Layer: 10
|
||||
m_Name: Gravity Waves
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &7852221720741322892
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7852221720741322899}
|
||||
m_LocalRotation: {x: 0.7071068, y: -0, z: -0, w: 0.7071068}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 7852221720480299767}
|
||||
m_RootOrder: 2
|
||||
m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0}
|
||||
--- !u!33 &7852221720741322895
|
||||
MeshFilter:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7852221720741322899}
|
||||
m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0}
|
||||
--- !u!23 &7852221720741322894
|
||||
MeshRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 7852221720741322899}
|
||||
m_Enabled: 1
|
||||
m_CastShadows: 1
|
||||
m_ReceiveShadows: 1
|
||||
m_DynamicOccludee: 1
|
||||
m_StaticShadowCaster: 0
|
||||
m_MotionVectors: 1
|
||||
m_LightProbeUsage: 1
|
||||
m_ReflectionProbeUsage: 1
|
||||
m_RayTracingMode: 2
|
||||
m_RayTraceProcedural: 0
|
||||
m_RenderingLayerMask: 1
|
||||
m_RendererPriority: 0
|
||||
m_Materials:
|
||||
- {fileID: 2100000, guid: 11fbaa87dcc7a3743a6bd3aa5c86e5ae, type: 2}
|
||||
m_StaticBatchInfo:
|
||||
firstSubMesh: 0
|
||||
subMeshCount: 0
|
||||
m_StaticBatchRoot: {fileID: 0}
|
||||
m_ProbeAnchor: {fileID: 0}
|
||||
m_LightProbeVolumeOverride: {fileID: 0}
|
||||
m_ScaleInLightmap: 1
|
||||
m_ReceiveGI: 1
|
||||
m_PreserveUVs: 0
|
||||
m_IgnoreNormalsForChartDetection: 0
|
||||
m_ImportantGI: 0
|
||||
m_StitchLightmapSeams: 1
|
||||
m_SelectedEditorRenderState: 3
|
||||
m_MinimumChartSize: 4
|
||||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingOrder: 0
|
||||
m_AdditionalVertexStreams: {fileID: 0}
|
||||
--- !u!1 &7852221720816817935
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
|
|
|||
BIN
Assets/Resources/Settings.asset
(Stored with Git LFS)
BIN
Assets/Resources/Settings.asset
(Stored with Git LFS)
Binary file not shown.
|
|
@ -16322,7 +16322,6 @@ MonoBehaviour:
|
|||
Settings: {fileID: 11400000, guid: cd2344e2001a37544aab0960c164d4ff, type: 2}
|
||||
ZoneRoot: {fileID: 2095699057}
|
||||
SectorBrushes: {fileID: 1037490739}
|
||||
SectorBoundaryBrush: {fileID: 1476765550}
|
||||
SceneCamera: {fileID: 887677622}
|
||||
FogCameras:
|
||||
- {fileID: 559840917}
|
||||
|
|
@ -16864,7 +16863,6 @@ Transform:
|
|||
m_LocalScale: {x: 3000, y: 3000, z: 3000}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children:
|
||||
- {fileID: 1476765548}
|
||||
- {fileID: 23420357}
|
||||
- {fileID: 1108843909}
|
||||
- {fileID: 1647230682}
|
||||
|
|
@ -22639,96 +22637,6 @@ RectTransform:
|
|||
m_AnchoredPosition: {x: 0, y: 0}
|
||||
m_SizeDelta: {x: 0, y: 0}
|
||||
m_Pivot: {x: 0.5, y: 0.5}
|
||||
--- !u!1 &1476765547
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
serializedVersion: 6
|
||||
m_Component:
|
||||
- component: {fileID: 1476765548}
|
||||
- component: {fileID: 1476765551}
|
||||
- component: {fileID: 1476765550}
|
||||
m_Layer: 9
|
||||
m_Name: Boundary
|
||||
m_TagString: Untagged
|
||||
m_Icon: {fileID: 0}
|
||||
m_NavMeshLayer: 0
|
||||
m_StaticEditorFlags: 0
|
||||
m_IsActive: 1
|
||||
--- !u!4 &1476765548
|
||||
Transform:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1476765547}
|
||||
serializedVersion: 2
|
||||
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
|
||||
m_LocalPosition: {x: 0, y: 0, z: 0}
|
||||
m_LocalScale: {x: 1, y: 1, z: 1}
|
||||
m_ConstrainProportionsScale: 0
|
||||
m_Children: []
|
||||
m_Father: {fileID: 1037490739}
|
||||
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
|
||||
--- !u!23 &1476765550
|
||||
MeshRenderer:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1476765547}
|
||||
m_Enabled: 1
|
||||
m_CastShadows: 0
|
||||
m_ReceiveShadows: 0
|
||||
m_DynamicOccludee: 0
|
||||
m_StaticShadowCaster: 0
|
||||
m_MotionVectors: 2
|
||||
m_LightProbeUsage: 0
|
||||
m_ReflectionProbeUsage: 0
|
||||
m_RayTracingMode: 2
|
||||
m_RayTraceProcedural: 0
|
||||
m_RayTracingAccelStructBuildFlagsOverride: 0
|
||||
m_RayTracingAccelStructBuildFlags: 1
|
||||
m_SmallMeshCulling: 1
|
||||
m_ForceMeshLod: -1
|
||||
m_MeshLodSelectionBias: 0
|
||||
m_RenderingLayerMask: 1
|
||||
m_RendererPriority: 0
|
||||
m_Materials:
|
||||
- {fileID: 2100000, guid: 8f45fb3a0f420d5428f1528827d55fa2, type: 2}
|
||||
m_StaticBatchInfo:
|
||||
firstSubMesh: 0
|
||||
subMeshCount: 0
|
||||
m_StaticBatchRoot: {fileID: 0}
|
||||
m_ProbeAnchor: {fileID: 0}
|
||||
m_LightProbeVolumeOverride: {fileID: 0}
|
||||
m_ScaleInLightmap: 1
|
||||
m_ReceiveGI: 1
|
||||
m_PreserveUVs: 0
|
||||
m_IgnoreNormalsForChartDetection: 0
|
||||
m_ImportantGI: 0
|
||||
m_StitchLightmapSeams: 1
|
||||
m_SelectedEditorRenderState: 3
|
||||
m_MinimumChartSize: 4
|
||||
m_AutoUVMaxDistance: 0.5
|
||||
m_AutoUVMaxAngle: 89
|
||||
m_LightmapParameters: {fileID: 0}
|
||||
m_GlobalIlluminationMeshLod: 0
|
||||
m_SortingLayerID: 0
|
||||
m_SortingLayer: 0
|
||||
m_SortingOrder: 0
|
||||
m_MaskInteraction: 0
|
||||
m_AdditionalVertexStreams: {fileID: 0}
|
||||
--- !u!33 &1476765551
|
||||
MeshFilter:
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_GameObject: {fileID: 1476765547}
|
||||
m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0}
|
||||
--- !u!1 &1485884351
|
||||
GameObject:
|
||||
m_ObjectHideFlags: 0
|
||||
|
|
|
|||
|
|
@ -1061,16 +1061,6 @@ PrefabInstance:
|
|||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7852221720741322892, guid: 046dde0f4600bce41ad33c476f99b309,
|
||||
type: 3}
|
||||
propertyPath: m_LocalScale.x
|
||||
value: 512
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7852221720741322892, guid: 046dde0f4600bce41ad33c476f99b309,
|
||||
type: 3}
|
||||
propertyPath: m_LocalScale.y
|
||||
value: 512
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7852221720816817928, guid: 046dde0f4600bce41ad33c476f99b309,
|
||||
type: 3}
|
||||
propertyPath: m_LocalScale.x
|
||||
|
|
|
|||
|
|
@ -911,16 +911,6 @@ PrefabInstance:
|
|||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7852221720741322892, guid: 046dde0f4600bce41ad33c476f99b309,
|
||||
type: 3}
|
||||
propertyPath: m_LocalScale.x
|
||||
value: 40
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7852221720741322892, guid: 046dde0f4600bce41ad33c476f99b309,
|
||||
type: 3}
|
||||
propertyPath: m_LocalScale.y
|
||||
value: 40
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7852221720969441490, guid: 046dde0f4600bce41ad33c476f99b309,
|
||||
type: 3}
|
||||
propertyPath: m_LocalScale.x
|
||||
|
|
@ -1778,16 +1768,6 @@ PrefabInstance:
|
|||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7852221720741322892, guid: 046dde0f4600bce41ad33c476f99b309,
|
||||
type: 3}
|
||||
propertyPath: m_LocalScale.x
|
||||
value: 40
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7852221720741322892, guid: 046dde0f4600bce41ad33c476f99b309,
|
||||
type: 3}
|
||||
propertyPath: m_LocalScale.y
|
||||
value: 40
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7852221720969441490, guid: 046dde0f4600bce41ad33c476f99b309,
|
||||
type: 3}
|
||||
propertyPath: m_LocalScale.x
|
||||
|
|
@ -5297,16 +5277,6 @@ PrefabInstance:
|
|||
propertyPath: m_LocalEulerAnglesHint.z
|
||||
value: 0
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7852221720741322892, guid: 046dde0f4600bce41ad33c476f99b309,
|
||||
type: 3}
|
||||
propertyPath: m_LocalScale.x
|
||||
value: 40
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7852221720741322892, guid: 046dde0f4600bce41ad33c476f99b309,
|
||||
type: 3}
|
||||
propertyPath: m_LocalScale.y
|
||||
value: 40
|
||||
objectReference: {fileID: 0}
|
||||
- target: {fileID: 7852221720969441490, guid: 046dde0f4600bce41ad33c476f99b309,
|
||||
type: 3}
|
||||
propertyPath: m_LocalScale.x
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ public class GameSettings : ScriptableObject
|
|||
public float HeatstrokePhasingFrequency = 5;
|
||||
public float WormholeDistanceRatio;
|
||||
public float DefaultViewDistance;
|
||||
public float MinimapZoneGravityRange;
|
||||
public float[] MinimapZoomLevels;
|
||||
public int DefaultMinimapZoom;
|
||||
public ExponentialCurve IconSize;
|
||||
|
|
|
|||
|
|
@ -13,15 +13,8 @@ using static CultMath.math;
|
|||
[Serializable, MessagePackObject(keyAsPropertyName:true), JsonObject]
|
||||
public class PlanetSettings
|
||||
{
|
||||
public float ZoneDepthExponent;
|
||||
public float ZoneDepth;
|
||||
public float ZoneBoundaryFog;
|
||||
public ExponentialCurve GravityDepth;
|
||||
public ExponentialCurve GravityRadius;
|
||||
public ExponentialCurve WaveDepth;
|
||||
public ExponentialCurve WaveRadius;
|
||||
public ExponentialCurve WaveFrequency;
|
||||
public ExponentialCurve WaveSpeed;
|
||||
public ExponentialCurve LightRadius;
|
||||
public ExponentialCurve BodyRadius;
|
||||
public float AsteroidVerticalOffset = -5f;
|
||||
|
|
|
|||
|
|
@ -161,6 +161,12 @@ public class Zone
|
|||
Orbits.Add(orbit.Key, new Orbit(Settings, cache.Get(orbit)));
|
||||
}
|
||||
|
||||
// Aetheria gravity (target invariant zone-valid-before-tick): every orbit is placed at the pack's time
|
||||
// when the zone is built, so each well, light and body pose read before the first Update is where the
|
||||
// body is. Velocity stays zero until the first Update differences two placed positions.
|
||||
foreach (var orbit in Orbits)
|
||||
GetOrbitPosition(orbit.Key);
|
||||
|
||||
foreach (var body in pack.Planets)
|
||||
{
|
||||
var planet = cache.Get(body);
|
||||
|
|
@ -266,18 +272,16 @@ public class Zone
|
|||
|
||||
private bool IsExpired(ChunkWear wear) => wear.Damage <= 0f && (!wear.BrokenUntil.HasValue || wear.BrokenUntil.Value <= _time);
|
||||
|
||||
public void AddOrbit(OrbitData orbit)
|
||||
{
|
||||
Orbits.Add(_itemManager.ItemData.RefOf(orbit).Key, new Orbit(Settings, orbit));
|
||||
}
|
||||
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
_time += deltaTime;
|
||||
_updatedOrbits.Clear();
|
||||
// Every previous position is taken before any orbit is placed: GetOrbitPosition places a parent as a
|
||||
// side effect of placing its child, so differencing inside one loop would depend on dictionary order.
|
||||
foreach (var orbit in Orbits)
|
||||
orbit.Value.PreviousPosition = orbit.Value.Position;
|
||||
foreach (var orbit in Orbits)
|
||||
{
|
||||
orbit.Value.PreviousPosition = orbit.Value.Position;
|
||||
orbit.Value.Position = GetOrbitPosition(orbit.Key);
|
||||
orbit.Value.Velocity = (orbit.Value.Position - orbit.Value.PreviousPosition) / deltaTime;
|
||||
}
|
||||
|
|
@ -591,26 +595,6 @@ public class Zone
|
|||
into[i] = beltInstance.Pose(i, _time, parentPosition, Settings, ChunkRadius(new ChunkId(belt, i)));
|
||||
}
|
||||
|
||||
public OrbitData CreateOrbit(CultRecordKey parent, float2 position)
|
||||
{
|
||||
var parentPosition = GetOrbitPosition(parent);
|
||||
var delta = position - parentPosition;
|
||||
var distance = length(delta);
|
||||
var period = Settings.OrbitPeriod.Evaluate(distance);
|
||||
var phase = atan2(delta.y, delta.x) / (PI * 2);
|
||||
var currentPhase = frac(_time / period);
|
||||
var storedPhase = (float) frac(phase - currentPhase);
|
||||
|
||||
var orbit = new OrbitData
|
||||
{
|
||||
Distance = distance,
|
||||
Parent = new CultRecordRef<OrbitData>(parent),
|
||||
Phase = storedPhase
|
||||
};
|
||||
Orbits.Add(_itemManager.ItemData.Upsert(orbit).Key, new Orbit(Settings, orbit));
|
||||
return orbit;
|
||||
}
|
||||
|
||||
public SecurityLevel GetSecurityLevel(float2 pos)
|
||||
{
|
||||
if (GalaxyZone.Owner==null) return SecurityLevel.Open;
|
||||
|
|
@ -630,7 +614,7 @@ public class Zone
|
|||
|
||||
public float GetHeight(float2 position)
|
||||
{
|
||||
var result = -PowerPulse(length(position)/(Pack.Radius*2), Settings.ZoneDepthExponent) * Settings.ZoneDepth;
|
||||
var result = 0f;
|
||||
foreach (var body in PlanetInstances.Values)
|
||||
{
|
||||
var p = position - body.Orbit.Position; //GetOrbitPosition(body.BodyData.Orbit)
|
||||
|
|
@ -641,18 +625,6 @@ public class Zone
|
|||
var depth = body.GravityWellDepth;
|
||||
result -= PowerPulse(sqrt(distSqr) / gravityRadius, body.BodyData.GravityDepthExponent) * depth;
|
||||
}
|
||||
|
||||
if (body is GasGiant gas)
|
||||
{
|
||||
var waveRadius = gas.GravityWavesRadius;
|
||||
if(distSqr < waveRadius*waveRadius)
|
||||
{
|
||||
var depth = gas.GravityWavesDepth;
|
||||
var frequency = Settings.WaveFrequency.Evaluate(body.BodyData.Mass);
|
||||
var speed = gas.GravityWavesSpeed;
|
||||
result -= RadialWaves(sqrt(distSqr) / waveRadius, 8, 1.25f, frequency, (float) (_time * speed)) * depth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
@ -716,12 +688,6 @@ public class Zone
|
|||
return pow((x + 1) * (1 - x), exponent);
|
||||
}
|
||||
|
||||
public static float RadialWaves(float x, float maskExponent, float sineExponent, float frequency, float phase)
|
||||
{
|
||||
//x *= 2;
|
||||
return PowerPulse(x, maskExponent) * cos(pow(x*2, sineExponent) * frequency + phase);
|
||||
}
|
||||
|
||||
public float3 GetNormal(float2 pos, float step = .1f, float mul = 1)
|
||||
{
|
||||
float hL = GetHeight(new float2(pos.x - step, pos.y)) * mul;
|
||||
|
|
@ -763,22 +729,10 @@ public class Planet
|
|||
public class GasGiant : Planet
|
||||
{
|
||||
public GasGiantData GasGiantData;
|
||||
public float GravityWavesDepth;
|
||||
public float GravityWavesRadius;
|
||||
public float GravityWavesSpeed;
|
||||
|
||||
public GasGiant(PlanetSettings settings, GasGiantData data, Orbit orbit) : base(settings, data, orbit)
|
||||
{
|
||||
GasGiantData = data;
|
||||
CalculateProperties();
|
||||
}
|
||||
|
||||
public new void CalculateProperties()
|
||||
{
|
||||
base.CalculateProperties();
|
||||
GravityWavesDepth = Settings.WaveDepth.Evaluate(BodyData.Mass) * GasGiantData.WaveDepthMultiplier;
|
||||
GravityWavesRadius = Settings.WaveRadius.Evaluate(BodyData.Mass) * GasGiantData.WaveRadiusMultiplier;
|
||||
GravityWavesSpeed = Settings.WaveSpeed.Evaluate(BodyData.Mass) * GasGiantData.WaveSpeedMultiplier;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -186,18 +186,6 @@ public class GasGiantData : BodyData
|
|||
[JsonProperty("albedoRotationSpeed"), Key(13)]
|
||||
public float AlbedoRotationSpeed = 1;
|
||||
|
||||
[JsonProperty("gravRadiusMul")] [Key(14)]
|
||||
public float WaveRadiusMultiplier = 1;
|
||||
|
||||
[JsonProperty("depthMul")] [Key(15)]
|
||||
public float WaveDepthMultiplier = 1;
|
||||
|
||||
[JsonProperty("depthExp")] [Key(16)]
|
||||
public float WaveDepthExponent = 8;
|
||||
|
||||
[JsonProperty("depthExp")] [Key(17)]
|
||||
public float WaveSpeedMultiplier = 8;
|
||||
|
||||
[JsonProperty("materialOverrides"), Key(18)]
|
||||
public List<string> MaterialOverrides = new List<string>();
|
||||
|
||||
|
|
|
|||
|
|
@ -9,5 +9,4 @@ using UnityEngine;
|
|||
public class GasGiantObject : PlanetObject
|
||||
{
|
||||
public SunMaterial SunMaterial;
|
||||
public MeshRenderer GravityWaves;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ public class ZoneRenderer : MonoBehaviour
|
|||
public GameSettings Settings;
|
||||
public Transform ZoneRoot;
|
||||
public Transform SectorBrushes;
|
||||
public MeshRenderer SectorBoundaryBrush;
|
||||
public CinemachineVirtualCamera SceneCamera;
|
||||
public Camera[] FogCameras;
|
||||
public Camera[] MinimapCameras;
|
||||
|
|
@ -472,17 +471,12 @@ public class ZoneRenderer : MonoBehaviour
|
|||
else planet = Instantiate(GasGiant, ZoneRoot);
|
||||
|
||||
var gas = (GasGiantObject) planet;
|
||||
var gasGiant = Zone.PlanetInstances[key] as GasGiant;
|
||||
gas.Body.material.SetTexture("_ColorRamp", gasGiantData.Colors.ToGradient(!(planetData is SunData)).ToTexture());
|
||||
// gasGiantData.AlbedoRotationSpeed.Subscribe(f => gas.SunMaterial.AlbedoRotationSpeed = f);
|
||||
// gasGiantData.FirstOffsetRotationSpeed.Subscribe(f => gas.SunMaterial.FirstOffsetRotationSpeed = f);
|
||||
// gasGiantData.SecondOffsetRotationSpeed.Subscribe(f => gas.SunMaterial.SecondOffsetRotationSpeed = f);
|
||||
// gasGiantData.FirstOffsetDomainRotationSpeed.Subscribe(f => gas.SunMaterial.FirstOffsetDomainRotationSpeed = f);
|
||||
// gasGiantData.SecondOffsetDomainRotationSpeed.Subscribe(f => gas.SunMaterial.SecondOffsetDomainRotationSpeed = f);
|
||||
gas.GravityWaves.transform.localScale = gasGiant.GravityWavesRadius * Vector3.one;
|
||||
gas.GravityWaves.material.SetFloat("_Depth", gasGiant.GravityWavesDepth);
|
||||
gas.GravityWaves.material.SetFloat("_Frequency", Settings.PlanetSettings.WaveFrequency.Evaluate(planetData.Mass));
|
||||
//gas.WaveScroll.Speed = Properties.GravitySettings.WaveSpeed.Evaluate(f);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -584,8 +578,6 @@ public class ZoneRenderer : MonoBehaviour
|
|||
planet.Value.Body.transform.localPosition = new Vector3(0, height + planetInstance.BodyRadius * 2, 0);
|
||||
if (planet.Value is GasGiantObject gasGiantObject)
|
||||
{
|
||||
gasGiantObject.GravityWaves.material.SetFloat("_Phase",
|
||||
Zone.Time * ((GasGiant) Zone.PlanetInstances[planet.Key]).GravityWavesSpeed);
|
||||
if (!(planet.Value is SunObject))
|
||||
{
|
||||
var toParent = normalize(Zone.GetOrbitPosition(Zone.Orbits[planetInstance.BodyData.Orbit.Key].Data.Parent.Key) - p);
|
||||
|
|
@ -618,19 +610,13 @@ public class ZoneRenderer : MonoBehaviour
|
|||
}
|
||||
|
||||
//var fogPos = FogCameraParent.position;
|
||||
SectorBoundaryBrush.material.SetFloat("_Power", Settings.PlanetSettings.ZoneDepthExponent);
|
||||
SectorBoundaryBrush.material.SetFloat("_Depth", Settings.PlanetSettings.ZoneDepth + Settings.PlanetSettings.ZoneBoundaryFog);
|
||||
var startDepth = Zone.PowerPulse(Settings.MinimapZoneGravityRange, Settings.PlanetSettings.ZoneDepthExponent) *
|
||||
Settings.PlanetSettings.ZoneDepth;
|
||||
var depthRange = Settings.PlanetSettings.ZoneDepth - startDepth + maxDepth;
|
||||
var depthRange = Mathf.Max(maxDepth, 1f);
|
||||
foreach (var mat in MapGravityMaterials)
|
||||
{
|
||||
mat.SetFloat("_StartDepth", startDepth);
|
||||
mat.SetFloat("_StartDepth", 0f);
|
||||
mat.SetFloat("_DepthRange", depthRange);
|
||||
}
|
||||
//Shader.SetGlobalFloat("_GridOffset", Settings.PlanetSettings.ZoneBoundaryFog);
|
||||
// var gravPos = MinimapGravityQuad.transform.position;
|
||||
// gravPos.y = -Settings.PlanetSettings.ZoneDepth - _maxDepth;
|
||||
// MinimapGravityQuad.transform.position = gravPos;
|
||||
// MinimapTintQuad.transform.position = gravPos - Vector3.up*10;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,11 +81,15 @@ public sealed partial class RunStartTests
|
|||
|
||||
// Idle long enough for stats and power to reach their idle state, then at rest, facing +y,
|
||||
// and, after three seconds of coasting, with nothing observed: the track forgets on its own.
|
||||
// The ship is stepped alone, so the zone's wells pull it while it idles; settling is about stats and power, so
|
||||
// it comes back to where it stood.
|
||||
private static void EvSettle(Ship ship)
|
||||
{
|
||||
var stood = ship.Position.xz;
|
||||
ship.MovementDirection = float2(0, 0);
|
||||
EvFace(ship, float3(0, 0, 1));
|
||||
for (var step = 0; step < 180; step++) ship.Update(EvDt);
|
||||
ship.Position.xz = stood;
|
||||
ship.Velocity = float2(0, 0);
|
||||
ship.Direction = float2(0, 1);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,11 @@ public sealed class FireControlTests : IDisposable
|
|||
// A hull with one weapon hardpoint per (rotation, firingArc) pair, laid out along the X axis of a 1xN
|
||||
// shape so each gets its own cell. FiringArc 0 on a hardpoint means "use GameplaySettings.FiringArc".
|
||||
private (ItemManager items, Ship ship, EquippedItem[] weapons) BuildShipWithHardpoints(
|
||||
params (ItemRotation rotation, float firingArc)[] hardpoints)
|
||||
params (ItemRotation rotation, float firingArc)[] hardpoints) => BuildShipWithHardpoints(false, hardpoints);
|
||||
|
||||
// `well` digs the zone one well at the origin (WellFixture), so heights differ across the zone.
|
||||
private (ItemManager items, Ship ship, EquippedItem[] weapons) BuildShipWithHardpoints(
|
||||
bool well, (ItemRotation rotation, float firingArc)[] hardpoints)
|
||||
{
|
||||
var hullShape = new Shape(Math.Max(hardpoints.Length, 3), 3);
|
||||
foreach (var cell in hullShape.AllCoordinates) hullShape[cell] = true;
|
||||
|
|
@ -78,13 +82,16 @@ public sealed class FireControlTests : IDisposable
|
|||
buildCache.FlushAsync().Wait();
|
||||
}
|
||||
|
||||
_openCache = AetheriaStores.Open(Catalog, catalogWritable: true);
|
||||
_openCache = AetheriaStores.Open(Catalog, Path.Combine(_root, "Run.cc"), catalogWritable: true);
|
||||
var ledger = new ProvenanceLedger();
|
||||
ledger.Lots[1] = new Lot { Origin = new Attributed(), Quality = 1 }; // hull
|
||||
for (var i = 0; i < hardpoints.Length; i++)
|
||||
ledger.Lots[2 + i] = new Lot { Origin = new Attributed(), Quality = 1 }; // guns
|
||||
var items = new ItemManager(_openCache, ledger, TestSettings(), _ => { });
|
||||
var zone = new Zone(items, new PlanetSettings(), new ZonePack(), new GalaxyZone { Name = "Test Zone", Owner = null }, null);
|
||||
var planets = new PlanetSettings();
|
||||
var pack = new ZonePack();
|
||||
if (well) WellFixture.Dig(_openCache, pack, planets, pack.Radius, 1000f, 1f);
|
||||
var zone = new Zone(items, planets, pack, new GalaxyZone { Name = "Test Zone", Owner = null }, null);
|
||||
|
||||
var hullRef = items.ItemData.RefOf<ItemData>(items.ItemData.GetByName<HullData>("TestHull"));
|
||||
var hull = new EquippableItem { Data = hullRef, Durability = 1, Lot = 1 };
|
||||
|
|
@ -182,10 +189,10 @@ public sealed class FireControlTests : IDisposable
|
|||
}
|
||||
|
||||
// A shooter with one in-range, in-arc gun, a detected target at `targetPosition`, and a Minion driving the shooter.
|
||||
private (Ship shooter, Ship target, Zone zone) BuildCombatEncounter(float3 targetPosition, float projectileSpeed = 0f, bool twoTargetGuns = false)
|
||||
private (Ship shooter, Ship target, Zone zone) BuildCombatEncounter(float3 targetPosition, float projectileSpeed = 0f, bool twoTargetGuns = false, bool well = false)
|
||||
{
|
||||
var mounts = twoTargetGuns ? new[] { (ItemRotation.None, 0f), (ItemRotation.None, 0f) } : new[] { (ItemRotation.None, 0f) };
|
||||
var (items, shooter, weapons) = BuildShipWithHardpoints(mounts);
|
||||
var (items, shooter, weapons) = BuildShipWithHardpoints(well, mounts);
|
||||
var weaponData = (InstantWeaponData) weapons[0].Data.Behaviors.Single(b => b is InstantWeaponData);
|
||||
weaponData.Damage = new PerformanceStat { Min = 10, Max = 10 };
|
||||
weaponData.Cooldown = new PerformanceStat { Min = 1, Max = 1 };
|
||||
|
|
@ -376,9 +383,7 @@ public sealed class FireControlTests : IDisposable
|
|||
[Fact]
|
||||
public void TheAIMeasuresTheTargetOnTheGroundPlane()
|
||||
{
|
||||
var (shooter, target, zone) = BuildCombatEncounter(float3(0, 0, 600), twoTargetGuns: true);
|
||||
zone.Settings.ZoneDepth = 1000f;
|
||||
zone.Settings.ZoneDepthExponent = 1f;
|
||||
var (shooter, target, zone) = BuildCombatEncounter(float3(0, 0, 600), twoTargetGuns: true, well: true);
|
||||
var strong = target.Equipment.Single(x => x.Data.Name == "Gun");
|
||||
var weak = target.Equipment.Single(x => x.Data.Name == "WeakGun");
|
||||
zone.Update(1f); // heights follow the zone's depth from the first update
|
||||
|
|
|
|||
|
|
@ -217,7 +217,7 @@ public sealed class FloatingBodyTests : IDisposable
|
|||
// the sign or changing how delta enters the line fails here, not only the Zone function's pin above.
|
||||
private Ship ShipAt(float x, out Zone zone)
|
||||
{
|
||||
var cache = AetheriaStores.Open(Path.Combine(_root, Guid.NewGuid().ToString("N") + ".cc"), catalogWritable: true);
|
||||
var cache = AetheriaStores.Open(Path.Combine(_root, Guid.NewGuid().ToString("N") + ".cc"), Path.Combine(_root, Guid.NewGuid().ToString("N") + ".run.cc"), catalogWritable: true);
|
||||
_caches.Add(cache);
|
||||
var hullShape = new Shape(5, 5);
|
||||
foreach (var cell in hullShape.AllCoordinates) hullShape[cell] = true;
|
||||
|
|
@ -230,8 +230,10 @@ public sealed class FloatingBodyTests : IDisposable
|
|||
QualityPriceModifier = new ExponentialLerp()
|
||||
};
|
||||
var items = new ItemManager(cache, new ProvenanceLedger(), settings, _ => { });
|
||||
var planets = new PlanetSettings { ZoneDepth = 1000, ZoneDepthExponent = 1, GravityStrength = 1 };
|
||||
zone = new Zone(items, planets, new ZonePack { Radius = 1000 }, new GalaxyZone { Name = "Test Zone", Owner = null }, null);
|
||||
var planets = new PlanetSettings { GravityStrength = 1 };
|
||||
var pack = new ZonePack { Radius = 1000 };
|
||||
WellFixture.Dig(cache, pack, planets, 1000, 1000, 1);
|
||||
zone = new Zone(items, planets, pack, new GalaxyZone { Name = "Test Zone", Owner = null }, null);
|
||||
var design = cache.GetByName<HullData>("Skiff");
|
||||
var hull = new EquippableItem
|
||||
{
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ public sealed class FlowFieldTests : IDisposable
|
|||
public FlowFieldTests()
|
||||
{
|
||||
Directory.CreateDirectory(_root);
|
||||
_cache = AetheriaStores.Open(Path.Combine(_root, "flow.cc"), catalogWritable: true);
|
||||
_cache = AetheriaStores.Open(Path.Combine(_root, "flow.cc"), Path.Combine(_root, "flow-run.cc"), catalogWritable: true);
|
||||
_cache.Upsert(new VerseGrammar { Revision = 1 });
|
||||
_cache.FlushAsync().Wait();
|
||||
var settings = new GameplaySettings
|
||||
|
|
@ -46,9 +46,13 @@ public sealed class FlowFieldTests : IDisposable
|
|||
};
|
||||
|
||||
// A zone at `time` (the pack's clock, as a saved zone restores it), flat unless `depth` digs the zone's own well.
|
||||
private Zone ZoneWith(FlowSettings flow, double time = 0, float depth = 0f) => new Zone(_items,
|
||||
new PlanetSettings { Flow = flow, ZoneDepth = depth, ZoneDepthExponent = 2f },
|
||||
new ZonePack { Time = time }, new GalaxyZone { Name = "Flow Zone", Owner = null }, null);
|
||||
private Zone ZoneWith(FlowSettings flow, double time = 0, float depth = 0f)
|
||||
{
|
||||
var planets = new PlanetSettings { Flow = flow };
|
||||
var pack = new ZonePack { Time = time };
|
||||
if (depth > 0f) WellFixture.Dig(_items.ItemData, pack, planets, pack.Radius, depth, 2f);
|
||||
return new Zone(_items, planets, pack, new GalaxyZone { Name = "Flow Zone", Owner = null }, null);
|
||||
}
|
||||
|
||||
private static readonly float3[] Probes =
|
||||
{
|
||||
|
|
|
|||
164
tests/Aetheria.Shared.Tests/GravitySubtractTests.cs
Normal file
164
tests/Aetheria.Shared.Tests/GravitySubtractTests.cs
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using CultMath;
|
||||
using GameCult.Caching;
|
||||
using Xunit;
|
||||
using float2 = CultMath.float2;
|
||||
using float3 = CultMath.float3;
|
||||
|
||||
// flatspace-no-bowl-no-ripples (aetheria-gravity target r3): a zone's height is the sum of its bodies' wells and
|
||||
// nothing else. No zone-wide bowl sized by the pack, and no gas-giant or sun ripple that varies with zone time.
|
||||
public sealed class GravitySubtractTests : IDisposable
|
||||
{
|
||||
private readonly string _root = Path.Combine(Path.GetTempPath(), "aetheria-gravsub-" + Guid.NewGuid().ToString("N"));
|
||||
private readonly CultCache _cache;
|
||||
private readonly ItemManager _items;
|
||||
|
||||
public GravitySubtractTests()
|
||||
{
|
||||
Directory.CreateDirectory(_root);
|
||||
_cache = AetheriaStores.Open(Path.Combine(_root, "grav.cc"), Path.Combine(_root, "grav-run.cc"), catalogWritable: true);
|
||||
_cache.Upsert(new VerseGrammar { Revision = 1 });
|
||||
_cache.FlushAsync().Wait();
|
||||
var settings = new GameplaySettings
|
||||
{
|
||||
DefaultEntitySettings = new EntitySettings(),
|
||||
Tiers = new[] { new RarityTier { Name = "Common", Quality = .5f, Rarity = 0, Color = new float3(1, 1, 1) } },
|
||||
QualityPriceModifier = new ExponentialLerp(),
|
||||
TargetDetectionInfoThreshold = .1f, TargetGearInfoThreshold = .8f,
|
||||
FiringArc = 170, CommitHorizon = .5f, SchematicCellSize = 2f
|
||||
};
|
||||
_items = new ItemManager(_cache, new ProvenanceLedger(), settings, _ => { });
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_cache.Dispose();
|
||||
Directory.Delete(_root, true);
|
||||
}
|
||||
|
||||
// Curves for a body whose well, light and orbit all vary with mass, so a body that read a different
|
||||
// mass or multiplier than its twin would show it.
|
||||
private static PlanetSettings Planets() => new PlanetSettings
|
||||
{
|
||||
GravityStrength = 1,
|
||||
BodyRadius = new ExponentialCurve { Exponent = .25f, Multiplier = 3f },
|
||||
GravityRadius = new ExponentialCurve { Exponent = .25f, Multiplier = 500f },
|
||||
GravityDepth = new ExponentialCurve { Exponent = .175f, Multiplier = 30f },
|
||||
LightRadius = new ExponentialCurve { Exponent = .25f, Multiplier = 300f },
|
||||
OrbitPeriod = new ExponentialCurve()
|
||||
};
|
||||
|
||||
private Zone Bare(float packRadius) => new Zone(_items, Planets(), new ZonePack { Radius = packRadius },
|
||||
new GalaxyZone { Name = "Bare Zone", Owner = null }, null);
|
||||
|
||||
[Fact]
|
||||
public void AZoneWithNoBodiesIsFlat()
|
||||
{
|
||||
foreach (var packRadius in new[] { 1000f, 2500f })
|
||||
{
|
||||
var zone = Bare(packRadius);
|
||||
foreach (var r in new[] { 0f, 300f, 577f, 900f, 2000f })
|
||||
{
|
||||
var at = new float2(r, 0);
|
||||
Assert.Equal(0f, zone.GetHeight(at));
|
||||
Assert.Equal(0f, zone.GetForce(at).x);
|
||||
Assert.Equal(0f, zone.GetForce(at).y);
|
||||
var inPlane = new float2(0, r);
|
||||
Assert.Equal(0f, zone.GetHeight(inPlane));
|
||||
Assert.Equal(0f, zone.GetForce(inPlane).x);
|
||||
Assert.Equal(0f, zone.GetForce(inPlane).y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The fixture the five bowl tests share reproduces the zone bowl it replaced (map B14): the bowl was
|
||||
// -PowerPulse(r / (2 R), exponent) x depth, written out here from its definition, deepest at the centre and
|
||||
// zero from r = R on, so a fixture whose radius or depth drifted from the bowl would change every pull it feeds.
|
||||
[Fact]
|
||||
public void TheWellFixtureReproducesTheFormerBowl()
|
||||
{
|
||||
foreach (var (radius, depth, exponent, at, expected) in new[]
|
||||
{
|
||||
(1000f, 1000f, 1f, 0f, -1000f), (1000f, 1000f, 1f, 400f, -840f), (1000f, 1000f, 1f, 1000f, 0f),
|
||||
(2000f, 64f, 2f, 1000f, -36f), (2000f, 64f, 2f, 300f, -61.1524f)
|
||||
})
|
||||
{
|
||||
var planets = Planets();
|
||||
var pack = new ZonePack { Radius = radius };
|
||||
WellFixture.Dig(_cache, pack, planets, radius, depth, exponent);
|
||||
var zone = new Zone(_items, planets, pack, new GalaxyZone { Name = "Bowl Zone", Owner = null }, null);
|
||||
Assert.InRange(zone.GetHeight(new float2(at, 0)), expected - 1e-2f, expected + 1e-2f);
|
||||
}
|
||||
}
|
||||
|
||||
// One body of `data` at the origin of a zone whose only body it is.
|
||||
private Zone WithBody(BodyData data)
|
||||
{
|
||||
var orbit = _cache.Upsert(new OrbitData { FixedPosition = float2.zero }).Key;
|
||||
data.Orbit = new CultRecordRef<OrbitData>(orbit);
|
||||
data.Mass = 100f;
|
||||
data.GravityRadiusMultiplier = 1.5f;
|
||||
data.GravityDepthMultiplier = .7f;
|
||||
data.GravityDepthExponent = 2f;
|
||||
var body = _cache.Upsert(data).Key;
|
||||
var pack = new ZonePack { Radius = 1000f };
|
||||
pack.Orbits.Add(new CultRecordRef<OrbitData>(orbit));
|
||||
pack.Planets.Add(new CultRecordRef<BodyData>(body));
|
||||
return new Zone(_items, Planets(), pack, new GalaxyZone { Name = "Giant " + data.GetType().Name, Owner = null }, null);
|
||||
}
|
||||
|
||||
// The wells are the whole of a giant's height at every point, not just at a few radii: a ripple confined to
|
||||
// any annulus, or to a band derived from the body's radius, shows against the plain body somewhere in a sweep
|
||||
// that runs from the centre to well past the well's support.
|
||||
[Fact]
|
||||
public void AGiantsWellIsAPlainWellAtEveryTime()
|
||||
{
|
||||
var plain = WithBody(new PlanetData());
|
||||
var giants = new[] { WithBody(new GasGiantData()), WithBody(new SunData()) };
|
||||
var radii = new System.Collections.Generic.List<float> { 0.5f };
|
||||
for (var r = 1f; r <= 100f; r += 1f) radii.Add(r);
|
||||
for (var r = 105f; r <= 2000f; r += 5f) radii.Add(r);
|
||||
var angles = new[] { 0f, 0.7f, 2.1f, 4.0f };
|
||||
foreach (var time in new[] { 0f, 37.5f, 91.25f })
|
||||
{
|
||||
// Zone time 0 is the zones as built; each later time is one Update to that time.
|
||||
var step = time - _clock;
|
||||
if (step > 0f)
|
||||
{
|
||||
plain.Update(step);
|
||||
foreach (var giant in giants) giant.Update(step);
|
||||
_clock = time;
|
||||
}
|
||||
foreach (var r in radii)
|
||||
foreach (var a in angles)
|
||||
{
|
||||
var at = new float2(r * (float)Math.Cos(a), r * (float)Math.Sin(a));
|
||||
foreach (var giant in giants)
|
||||
{
|
||||
Assert.Equal(plain.GetHeight(at), giant.GetHeight(at));
|
||||
Assert.Equal(plain.GetForce(at).x, giant.GetForce(at).x);
|
||||
Assert.Equal(plain.GetForce(at).y, giant.GetForce(at).y);
|
||||
}
|
||||
}
|
||||
Assert.True(plain.GetHeight(new float2(50f, 0)) < 0f, "the plain body digs a well here, so there is something to compare");
|
||||
}
|
||||
}
|
||||
|
||||
private float _clock;
|
||||
|
||||
// CultCache has no primitive for reserving a removed MessagePack key; the wave keys of the gas giant (14-17)
|
||||
// must not come back with a different meaning, because stored records may still carry them.
|
||||
[Fact]
|
||||
public void AGasGiantRecordNeverReusesTheRetiredWaveKeys()
|
||||
{
|
||||
var keys = new System.Collections.Generic.List<int>();
|
||||
for (var t = typeof(GasGiantData); t != null; t = t.BaseType)
|
||||
foreach (var m in t.GetMembers(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic |
|
||||
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.DeclaredOnly))
|
||||
if (m.GetCustomAttributes(typeof(MessagePack.KeyAttribute), false) is MessagePack.KeyAttribute[] ks)
|
||||
foreach (var k in ks) if (k.IntKey.HasValue) keys.Add(k.IntKey.Value);
|
||||
Assert.NotEmpty(keys);
|
||||
foreach (var retired in new[] { 14, 15, 16, 17 }) Assert.DoesNotContain(retired, keys);
|
||||
}
|
||||
}
|
||||
|
|
@ -61,7 +61,7 @@ public sealed class LootTests : IDisposable
|
|||
}
|
||||
|
||||
private Lab Build(string zoneName = "Loot", float probability = .5f, float lifetime = 30f, float velocity = 25f,
|
||||
PlanetSettings planets = null, float spread = 0f)
|
||||
bool well = false, float spread = 0f)
|
||||
{
|
||||
var settings = new GameplaySettings
|
||||
{
|
||||
|
|
@ -73,7 +73,7 @@ public sealed class LootTests : IDisposable
|
|||
UnaidedAccuracy = .05f, UnaidedTracking = 10f, UnaidedPrecision = 2f, AgentMinHitProbability = 0f,
|
||||
LootDropProbability = probability, LootDropVelocity = velocity, FloatingItemLifetime = lifetime
|
||||
};
|
||||
var cache = AetheriaStores.Open(Path.Combine(_root, Guid.NewGuid().ToString("N") + ".cc"), catalogWritable: true);
|
||||
var cache = AetheriaStores.Open(Path.Combine(_root, Guid.NewGuid().ToString("N") + ".cc"), Path.Combine(_root, Guid.NewGuid().ToString("N") + ".run.cc"), catalogWritable: true);
|
||||
_caches.Add(cache);
|
||||
cache.Upsert(new VerseGrammar { Revision = 1 });
|
||||
var hardpoint = new HardpointData { Type = HardpointType.Sensors, Position = new int2(0, 0), Shape = new Shape() };
|
||||
|
|
@ -108,7 +108,14 @@ public sealed class LootTests : IDisposable
|
|||
var ledger = new ProvenanceLedger();
|
||||
for (var i = 1; i <= 9000; i++) ledger.Lots[i] = new Lot { Origin = new Attributed(), Quality = 1 };
|
||||
var items = new ItemManager(cache, ledger, settings, _ => { });
|
||||
var zone = new Zone(items, planets ?? new PlanetSettings(), new ZonePack { Radius = planets == null ? 0 : 1000 }, new GalaxyZone { Name = zoneName, Owner = null }, null);
|
||||
var planets = new PlanetSettings();
|
||||
var pack = new ZonePack { Radius = well ? 1000 : 0 };
|
||||
if (well)
|
||||
{
|
||||
planets.GravityStrength = 1;
|
||||
WellFixture.Dig(cache, pack, planets, 1000, 1000, 1);
|
||||
}
|
||||
var zone = new Zone(items, planets, pack, new GalaxyZone { Name = zoneName, Owner = null }, null);
|
||||
return new Lab { Items = items, Zone = zone };
|
||||
}
|
||||
|
||||
|
|
@ -286,8 +293,7 @@ public sealed class LootTests : IDisposable
|
|||
[Fact]
|
||||
public void AFloatingItemDriftsAsABodyAndExpires()
|
||||
{
|
||||
var planets = new PlanetSettings { ZoneDepth = 1000, ZoneDepthExponent = 1, GravityStrength = 1 };
|
||||
var lab = Build(lifetime: 3f, planets: planets);
|
||||
var lab = Build(lifetime: 3f, well: true);
|
||||
var settings = lab.Items.GameplaySettings;
|
||||
var start = new float2(40, -25);
|
||||
var launch = new float2(6, 2);
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ public sealed class MineTests : IDisposable
|
|||
FiringArc = 170, CommitHorizon = .5f, SchematicCellSize = 2f,
|
||||
UnaidedAccuracy = .05f, UnaidedTracking = 10f, UnaidedPrecision = 1000f, AgentMinHitProbability = 0f
|
||||
};
|
||||
var cache = AetheriaStores.Open(Path.Combine(_root, Guid.NewGuid().ToString("N") + ".cc"), catalogWritable: true);
|
||||
var cache = AetheriaStores.Open(Path.Combine(_root, Guid.NewGuid().ToString("N") + ".cc"), Path.Combine(_root, Guid.NewGuid().ToString("N") + ".run.cc"), catalogWritable: true);
|
||||
_caches.Add(cache);
|
||||
cache.Upsert(new VerseGrammar { Revision = 1 });
|
||||
HullData Hull(string name, Shape shape) => new HullData
|
||||
|
|
@ -118,8 +118,10 @@ public sealed class MineTests : IDisposable
|
|||
var ledger = new ProvenanceLedger();
|
||||
for (var i = 1; i <= 500; i++) ledger.Lots[i] = new Lot { Origin = new Attributed(), Quality = 1 };
|
||||
var items = new ItemManager(cache, ledger, settings, _ => { });
|
||||
var planets = well ? new PlanetSettings { ZoneDepth = 1000, ZoneDepthExponent = 1, GravityStrength = 1 } : new PlanetSettings();
|
||||
var zone = new Zone(items, planets, new ZonePack { Radius = well ? 1000 : 0 }, new GalaxyZone { Name = "Mines", Owner = null }, null);
|
||||
var planets = well ? new PlanetSettings { GravityStrength = 1 } : new PlanetSettings();
|
||||
var pack = new ZonePack { Radius = well ? 1000 : 0 };
|
||||
if (well) WellFixture.Dig(cache, pack, planets, 1000, 1000, 1);
|
||||
var zone = new Zone(items, planets, pack, new GalaxyZone { Name = "Mines", Owner = null }, null);
|
||||
var lab = new Lab { Items = items, Zone = zone, Data = data, ItemData = cache.GetByName<WeaponItemData>("Mine Layer") };
|
||||
|
||||
var layerRef = items.ItemData.RefOf<ItemData>(items.ItemData.GetByName<HullData>("Layer"));
|
||||
|
|
|
|||
|
|
@ -58,10 +58,6 @@ public sealed partial class MiningCut3Tests : IDisposable
|
|||
BodyRadius = new ExponentialCurve(),
|
||||
GravityRadius = new ExponentialCurve(),
|
||||
GravityDepth = new ExponentialCurve(),
|
||||
WaveDepth = new ExponentialCurve(),
|
||||
WaveRadius = new ExponentialCurve(),
|
||||
WaveSpeed = new ExponentialCurve(),
|
||||
WaveFrequency = new ExponentialCurve(),
|
||||
LightRadius = new ExponentialCurve { Constant = lightRadius }
|
||||
};
|
||||
|
||||
|
|
|
|||
21
tests/Aetheria.Shared.Tests/WellFixture.cs
Normal file
21
tests/Aetheria.Shared.Tests/WellFixture.cs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
using CultMath;
|
||||
using GameCult.Caching;
|
||||
|
||||
// The one fixture that gives a test zone a slope: a single body on a parentless orbit at the origin, with
|
||||
// the curves a body needs. Gravity height is the sum of body wells, so a test that wants ground to slide on
|
||||
// digs it here and nowhere else. `radius` is the zone radius the well is sized to (its well reaches 2 x radius,
|
||||
// as the former bowl did); the well is deepest at the centre and zero at and beyond half its radius.
|
||||
internal static class WellFixture
|
||||
{
|
||||
public static void Dig(CultCache cache, ZonePack pack, PlanetSettings settings, float radius, float depth, float exponent)
|
||||
{
|
||||
settings.BodyRadius = new ExponentialCurve();
|
||||
settings.OrbitPeriod = new ExponentialCurve();
|
||||
settings.GravityRadius = new ExponentialCurve { Constant = 2f * radius };
|
||||
settings.GravityDepth = new ExponentialCurve { Constant = depth };
|
||||
var orbit = cache.Upsert(new OrbitData { FixedPosition = float2.zero }).Key;
|
||||
var body = cache.Upsert(new PlanetData { Orbit = new CultRecordRef<OrbitData>(orbit), GravityDepthExponent = exponent }).Key;
|
||||
pack.Orbits.Add(new CultRecordRef<OrbitData>(orbit));
|
||||
pack.Planets.Add(new CultRecordRef<BodyData>(body));
|
||||
}
|
||||
}
|
||||
161
tests/Aetheria.Shared.Tests/ZonePlacedAtBuildTests.cs
Normal file
161
tests/Aetheria.Shared.Tests/ZonePlacedAtBuildTests.cs
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using CultMath;
|
||||
using GameCult.Caching;
|
||||
using Xunit;
|
||||
using static CultMath.math;
|
||||
using float2 = CultMath.float2;
|
||||
using float3 = CultMath.float3;
|
||||
|
||||
// Aetheria gravity, target invariant zone-valid-before-tick: a zone's orbits are placed when it is built, so
|
||||
// its wells, lights and body poses are right before the first Update (follow-up
|
||||
// aetheria-release:follow_up:zone-orbits-unplaced-before-first-tick). The zone here is the production
|
||||
// constructor reading a pack; nothing places an orbit but that constructor and Zone.Update.
|
||||
public sealed class ZonePlacedAtBuildTests : IDisposable
|
||||
{
|
||||
private static readonly float2 RootAt = new float2(300f, -200f);
|
||||
private const float Distance = 400f;
|
||||
private const float Period = 40f;
|
||||
|
||||
private readonly string _root = Path.Combine(Path.GetTempPath(), "aetheria-zoneplaced-" + Guid.NewGuid().ToString("N"));
|
||||
private readonly List<CultCache> _caches = new List<CultCache>();
|
||||
|
||||
public ZonePlacedAtBuildTests() => Directory.CreateDirectory(_root);
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
foreach (var cache in _caches) cache.Dispose();
|
||||
Directory.Delete(_root, true);
|
||||
}
|
||||
|
||||
private static GameplaySettings GameSettings() => new GameplaySettings
|
||||
{
|
||||
DefaultEntitySettings = new EntitySettings(),
|
||||
Tiers = new[] { new RarityTier { Name = "Common", Quality = .5f, Rarity = 0, Color = new float3(1, 1, 1) } },
|
||||
QualityPriceModifier = new ExponentialLerp()
|
||||
};
|
||||
|
||||
private static ExponentialCurve Flat(float v) => new ExponentialCurve { Constant = v };
|
||||
|
||||
// Flat space (no zone depth) with one planet whose well reaches 500 u, a fixed root orbit at RootAt and the
|
||||
// planet orbiting it at Distance from `phase`. Every orbit period is Period seconds.
|
||||
private static PlanetSettings Settings() => new PlanetSettings
|
||||
{
|
||||
OrbitPeriod = Flat(Period),
|
||||
BodyRadius = Flat(20f),
|
||||
GravityRadius = Flat(500f),
|
||||
GravityDepth = Flat(100f),
|
||||
GravityStrength = 1f,
|
||||
LightRadius = Flat(0f)
|
||||
};
|
||||
|
||||
private sealed class Scene
|
||||
{
|
||||
public CultCache Cache;
|
||||
public ItemManager Items;
|
||||
public HullData Hull;
|
||||
public Zone Zone;
|
||||
public CultRecordKey PlanetOrbit;
|
||||
}
|
||||
|
||||
private Scene Build(float phase, double time, bool moonFirst = false)
|
||||
{
|
||||
var dir = Path.Combine(_root, $"zone{_caches.Count}");
|
||||
Directory.CreateDirectory(dir);
|
||||
var cache = AetheriaStores.Open(Path.Combine(dir, "Aetheria.cc"), Path.Combine(dir, "run.cc"), catalogWritable: true);
|
||||
_caches.Add(cache);
|
||||
cache.Upsert(new VerseGrammar { Revision = 1 });
|
||||
var shape = new Shape(5, 5);
|
||||
foreach (var cell in shape.AllCoordinates) shape[cell] = true;
|
||||
var hull = new HullData { Name = "Skiff", HullType = HullType.Ship, Shape = shape, Durability = 10, Mass = 1000 };
|
||||
cache.Upsert(hull);
|
||||
var items = new ItemManager(cache, new ProvenanceLedger(), GameSettings(), _ => { });
|
||||
|
||||
var rootOrbit = cache.Upsert(new OrbitData { FixedPosition = RootAt }).Key;
|
||||
var planetOrbit = cache.Upsert(new OrbitData
|
||||
{
|
||||
Parent = new CultRecordRef<OrbitData>(rootOrbit), Distance = Distance, Phase = phase
|
||||
}).Key;
|
||||
var moonOrbit = cache.Upsert(new OrbitData
|
||||
{
|
||||
Parent = new CultRecordRef<OrbitData>(planetOrbit), Distance = 100f
|
||||
}).Key;
|
||||
var planet = cache.Upsert(new PlanetData { Orbit = new CultRecordRef<OrbitData>(planetOrbit), Mass = 1f }).Key;
|
||||
var pack = new ZonePack
|
||||
{
|
||||
Planets = { new CultRecordRef<BodyData>(planet) },
|
||||
Radius = 5000f,
|
||||
Mass = 10000f,
|
||||
Time = time
|
||||
};
|
||||
// The same three orbits either way; only the order the pack lists them in differs.
|
||||
var listed = moonFirst ? new[] { moonOrbit, planetOrbit, rootOrbit } : new[] { rootOrbit, planetOrbit, moonOrbit };
|
||||
foreach (var key in listed) pack.Orbits.Add(new CultRecordRef<OrbitData>(key));
|
||||
var zone = new Zone(items, Settings(), pack, new GalaxyZone { Name = "Placed", Owner = null }, null);
|
||||
return new Scene { Cache = cache, Items = items, Hull = hull, Zone = zone, PlanetOrbit = planetOrbit };
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(.1f, 0.0)]
|
||||
[InlineData(.35f, 0.0)]
|
||||
[InlineData(.1f, 123.0)]
|
||||
[InlineData(.35f, 123.0)]
|
||||
public void ABodysWellSitsOnItsOrbitBeforeTheFirstTick(float phase, double time)
|
||||
{
|
||||
var s = Build(phase, time);
|
||||
var orbit = s.Zone.Orbits[s.PlanetOrbit];
|
||||
var turns = (float) frac(time / orbit.Period);
|
||||
var expected = RootAt + OrbitData.Evaluate(frac(turns + phase)) * Distance;
|
||||
|
||||
Assert.InRange(length(orbit.Position - expected), 0f, .01f);
|
||||
|
||||
// The well pulls toward the body's place: a point 30 u east of it is pushed west.
|
||||
var force = s.Zone.GetForce(expected + new float2(30f, 0f));
|
||||
Assert.True(length(force) > 0f, "the well exerts a force");
|
||||
Assert.InRange(normalize(force).x, -1f, -.99f);
|
||||
}
|
||||
|
||||
// A parent's velocity is its own orbital velocity on the first tick and every tick after, whichever order
|
||||
// the pack lists a moon and its planet in. dt differs per tick so a velocity taken from a fixed step fails.
|
||||
[Theory]
|
||||
[InlineData(false)]
|
||||
[InlineData(true)]
|
||||
public void TheFirstTickVelocityIsTheOrbitsSpeed(bool moonFirst)
|
||||
{
|
||||
var s = Build(.35f, 0.0, moonFirst);
|
||||
var expected = 2f * PI * Distance / Period;
|
||||
foreach (var dt in new[] { 1f / 60f, 1f / 30f, 1f / 60f })
|
||||
{
|
||||
s.Zone.Update(dt);
|
||||
var speed = length(s.Zone.Orbits[s.PlanetOrbit].Velocity);
|
||||
Assert.InRange(speed, expected * .99f, expected * 1.01f);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void AShipSteppedAloneFeelsTheBodyWhereItIs()
|
||||
{
|
||||
var s = Build(.1f, 0.0);
|
||||
var planetAt = s.Zone.Orbits[s.PlanetOrbit].Position;
|
||||
var ship = new Ship(s.Items, s.Zone, new EquippableItem
|
||||
{
|
||||
Data = s.Cache.RefOf<ItemData>(s.Hull),
|
||||
Durability = s.Hull.Durability,
|
||||
Lot = s.Items.Lots.Add(new Lot
|
||||
{
|
||||
Design = s.Cache.RefOf<ItemData>(s.Hull), Origin = new Attributed(), Quality = .5f, Roles = new List<RoleFill>()
|
||||
})
|
||||
}, new EntitySettings());
|
||||
s.Zone.Admit(ship, piloted: false);
|
||||
var at = planetAt + new float2(0f, 60f);
|
||||
ship.Position = float3(at.x, ship.Position.y, at.y);
|
||||
ship.Velocity = float2.zero;
|
||||
|
||||
ship.Update(1f / 60f); // the ship alone: no Zone.Update has run
|
||||
|
||||
Assert.True(length(ship.Velocity) > 0f, "the ship is pulled");
|
||||
var toward = normalize(planetAt - at);
|
||||
Assert.True(dot(normalize(ship.Velocity), toward) > .99f, "the pull is toward the planet, not toward the origin");
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue