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, }; }