Files
FoxyzandClaude Opus 5.5 0b8f2131c1 Body step 2.4: landing, movable ground, playground fixes
- legs brake landings at 4.5 g; vertical support is a rate-limited servo
  (no launch when stepping onto a mantled ledge)
- ground control pushes back on movable ground (seesaw tips under the player)
- playground hinges use absolute paths (joint paths resolve from the joint)
- big props grabbable via surface query; Reset props keeps playground pieces
- spike: landing, seesaw, playground joints/grab, person-like wall climb

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 10:10:38 +03:00

147 lines
5.5 KiB
C#

using Godot;
public partial class PropShop : Node3D
{
private struct PropSpec
{
public string Name;
public float Radius;
public Vector3 Box;
public float Mass;
public float Bounce;
public float Friction;
public Color Color;
public Vector3 Position;
}
private static readonly PropSpec[] Specs =
{
new() { Name = "Ping pong · 2.7 g", Radius = 0.02f, Mass = 0.0027f, Bounce = 0.9f, Friction = 0.3f, Color = new(1f, 1f, 1f), Position = new(-0.5f, 0.78f, -0.55f) },
new() { Name = "Bouncy ball · 50 g", Radius = 0.03f, Mass = 0.05f, Bounce = 0.85f, Friction = 0.4f, Color = new(1f, 0.4f, 0.7f), Position = new(-0.3f, 0.79f, -0.45f) },
new() { Name = "Apple · 150 g", Radius = 0.045f, Mass = 0.15f, Bounce = 0.3f, Friction = 0.6f, Color = new(0.85f, 0.15f, 0.1f), Position = new(-0.08f, 0.81f, -0.55f) },
new() { Name = "Crate · 2 kg", Box = new(0.16f, 0.16f, 0.16f), Mass = 2f, Bounce = 0.1f, Friction = 0.8f, Color = new(0.7f, 0.5f, 0.25f), Position = new(0.18f, 0.84f, -0.5f) },
new() { Name = "Brick · 3.5 kg", Box = new(0.2f, 0.06f, 0.095f), Mass = 3.5f, Bounce = 0.05f, Friction = 0.9f, Color = new(0.6f, 0.3f, 0.2f), Position = new(-0.32f, 0.79f, -0.25f) },
new() { Name = "Bowling ball · 7.2 kg", Radius = 0.11f, Mass = 7.2f, Bounce = 0.1f, Friction = 0.5f, Color = new(0.1f, 0.2f, 0.5f), Position = new(0.05f, 0.87f, -0.25f) },
new() { Name = "Anvil · 50 kg", Box = new(0.28f, 0.16f, 0.13f), Mass = 50f, Bounce = 0f, Friction = 0.95f, Color = new(0.25f, 0.25f, 0.28f), Position = new(0.38f, 0.84f, -0.35f) },
};
public override void _Ready()
{
BuildTable();
foreach (PropSpec spec in Specs)
SpawnProp(spec);
}
public void Respawn()
{
// Only the table's own props (the playground's dynamic pieces are props too — they reset
// in place via Playground.Reset).
foreach (Node node in GetChildren())
if (node is RigidBody3D && node.IsInGroup("prop"))
node.QueueFree();
foreach (PropSpec spec in Specs)
SpawnProp(spec);
GD.Print("PropShop: table reset");
}
private void BuildTable()
{
Color wood = new(0.45f, 0.3f, 0.18f);
StaticBody3D table = new();
table.CollisionMask = 0b11101; // world + player body + hands + arms
table.AddChild(new CollisionShape3D
{
Position = new Vector3(0, 0.725f, -0.6f),
Shape = new BoxShape3D { Size = new Vector3(1.4f, 0.05f, 0.8f) },
});
MeshInstance3D top = new()
{
Position = new Vector3(0, 0.725f, -0.6f),
Mesh = new BoxMesh { Size = new Vector3(1.4f, 0.05f, 0.8f) },
};
top.SetSurfaceOverrideMaterial(0, MakeMaterial(wood));
table.AddChild(top);
Vector3[] legOffsets =
{
new(-0.62f, 0, 0.32f),
new(0.62f, 0, 0.32f),
new(-0.62f, 0, -0.32f),
new(0.62f, 0, -0.32f),
};
foreach (Vector3 offset in legOffsets)
{
Vector3 pos = new(offset.X, 0.3625f, -0.6f + offset.Z);
table.AddChild(new CollisionShape3D
{
Position = pos,
Shape = new BoxShape3D { Size = new Vector3(0.06f, 0.725f, 0.06f) },
});
MeshInstance3D leg = new()
{
Position = pos,
Mesh = new BoxMesh { Size = new Vector3(0.06f, 0.725f, 0.06f) },
};
leg.SetSurfaceOverrideMaterial(0, MakeMaterial(wood * 0.8f));
table.AddChild(leg);
}
AddChild(table);
}
private void SpawnProp(PropSpec spec)
{
RigidBody3D body = new()
{
Name = spec.Name,
Mass = spec.Mass,
Position = spec.Position,
PhysicsMaterialOverride = new PhysicsMaterial { Bounce = spec.Bounce, Friction = spec.Friction },
ContinuousCd = spec.Mass < 0.1f,
ContactMonitor = true,
MaxContactsReported = 4,
CollisionMask = 0b11101, // world + player body + hands + arms
};
body.AddToGroup("prop");
if (spec.Radius > 0f)
{
body.AddChild(new CollisionShape3D { Shape = new SphereShape3D { Radius = spec.Radius } });
MeshInstance3D mesh = new()
{
Mesh = new SphereMesh { Radius = spec.Radius, Height = spec.Radius * 2f },
};
mesh.SetSurfaceOverrideMaterial(0, MakeMaterial(spec.Color));
body.AddChild(mesh);
}
else
{
body.AddChild(new CollisionShape3D { Shape = new BoxShape3D { Size = spec.Box } });
MeshInstance3D mesh = new()
{
Mesh = new BoxMesh { Size = spec.Box },
};
mesh.SetSurfaceOverrideMaterial(0, MakeMaterial(spec.Color));
body.AddChild(mesh);
}
float halfHeight = spec.Radius > 0f ? spec.Radius : spec.Box.Y * 0.5f;
body.AddChild(new Label3D
{
Text = spec.Name,
FontSize = 64,
PixelSize = 0.0004f,
OutlineSize = 8,
Billboard = BaseMaterial3D.BillboardModeEnum.Enabled,
Position = new Vector3(0, halfHeight + 0.1f, 0),
});
AddChild(body);
}
private static StandardMaterial3D MakeMaterial(Color color) => new()
{
AlbedoColor = color,
Roughness = 0.7f,
};
}