Files
VR-Mess/scripts/Crag.cs
T
FoxyzandClaude Opus 5.5 ca9a299fe8 The crag: a 26 m box-rock cliff with a graded climbing route
North of the playground, facing spawn: bouldering blocks, a ladder to an 8 m rest ledge,
a jug wall, a 0.4 m traverse ledge, an overhang with monkey bars and a lip bar onto a
16 m rest ledge, a chimney to the 26 m summit with throwable props. A 'Crag' teleport
marker at the base is the way down; teleports now land the floor on the marker's height.
Floor widened to 80 m. Regression: scenes/crag_spike.tscn (reach, free space, overlaps,
height, line of sight).

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

359 lines
20 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using Godot;
using System.Collections.Generic;
// The crag: a 26 m cliff of stacked rock boxes north of the playground, one route up, getting
// harder as it goes (built in code like Playground, same layers and grab rules):
// 1. bouldering blocks (1.2 / 2.1 / 3.0 m) to mantle and hop between;
// 2. a ladder up the first tier's face to REST LEDGE 1 (8 m, flag);
// 3. a jug wall (two staggered columns of holds) up the second tier to 13.5 m;
// 4. a narrow 0.4 m ledge traversed sideways along a handrail of holds, open air behind;
// 5. an overhang: monkey bars under a roof, a jug on its lip and a lip bar to pull over onto
// REST LEDGE 2 (16 m, flag);
// 6. a 0.9 m chimney with holds on both walls up the third tier;
// 7. the summit (26 m) with throwable props. Way down: aim at the "Crag" teleport marker at the
// base (or jump — no fall damage).
// The cliff faces south (+Z) toward spawn. Every route point is recorded in Route so crag_spike
// can check reach; route bodies in RoutePieces so it can check nothing intersects them.
public partial class Crag : Node3D
{
public enum Step { Stand, Hold }
public readonly List<(Step Kind, Vector3 Point, string Name)> Route = new();
public readonly List<(CollisionObject3D Body, Aabb Box)> RoutePieces = new();
public readonly List<(CollisionObject3D Body, Aabb Box)> MassPieces = new();
public const float SummitHeight = 26f;
public static readonly Vector3 SummitPoint = new(2.25f, SummitHeight, -18f);
private readonly List<(RigidBody3D Body, Transform3D Start)> _dynamic = new();
private const uint WorldLayer = 1, WorldMask = 0b11101, PropMask = 0b11101;
private readonly RandomNumberGenerator _rng = new() { Seed = 26 };
private static readonly Color[] Rock =
{
new(0.47f, 0.43f, 0.39f), new(0.54f, 0.5f, 0.45f), new(0.41f, 0.39f, 0.37f), new(0.58f, 0.53f, 0.47f),
};
private static readonly Color Hold = new(0.9f, 0.45f, 0.15f), Metal = new(0.45f, 0.47f, 0.52f),
Wood = new(0.72f, 0.55f, 0.33f), Flag = new(0.85f, 0.15f, 0.15f), Bark = new(0.35f, 0.25f, 0.17f),
Leaves = new(0.25f, 0.45f, 0.22f), Pillar = new(0.7f, 0.7f, 0.68f);
public override void _Ready()
{
BuildMass();
BuildRoute();
BuildBase();
BuildSummitProps();
}
public void Reset()
{
foreach (var (body, start) in _dynamic)
{
if (!IsInstanceValid(body))
continue;
body.GlobalTransform = start;
body.LinearVelocity = Vector3.Zero;
body.AngularVelocity = Vector3.Zero;
body.ResetPhysicsInterpolation();
}
}
// ---------------------------------------------------------------- the rock
private void BuildMass()
{
// Three tiers, each set back 1.5 m: A 0–8 m, B 8–16 m, C 16–26 m (C split by the chimney).
Mass("TierA", new Vector3(12f, 8f, 7f), new Vector3(0f, 4f, -16.5f), Rock[0]);
Mass("TierB", new Vector3(10f, 8f, 5.5f), new Vector3(0f, 12f, -17.25f), Rock[1]);
Mass("TierCWest", new Vector3(3.55f, 10f, 4f), new Vector3(-2.225f, 21f, -18f), Rock[2]);
Mass("TierCEast", new Vector3(3.55f, 10f, 4f), new Vector3(2.225f, 21f, -18f), Rock[2]);
Mass("TierCBack", new Vector3(0.9f, 10f, 3f), new Vector3(0f, 21f, -18.5f), Rock[3]);
Mass("Spire", new Vector3(1.3f, 2.6f, 1.3f), new Vector3(3.1f, 27.3f, -19.1f), Rock[1], new Basis(Vector3.Up, 0.3f));
// Shoulders and buttresses break the silhouette (sides and back only — the south faces
// carry the route).
Mass("ShoulderWestA", new Vector3(2.4f, 5.5f, 5f), new Vector3(-7f, 2.75f, -16.8f), Rock[2], new Basis(Vector3.Up, 0.12f));
Mass("ShoulderEastA", new Vector3(2.8f, 6.8f, 5.5f), new Vector3(7.1f, 3.4f, -16.2f), Rock[3], new Basis(Vector3.Up, -0.15f));
Mass("ShoulderEastB", new Vector3(1.8f, 5.5f, 4f), new Vector3(5.6f, 10.75f, -17.8f), Rock[0], new Basis(Vector3.Up, 0.1f));
Mass("ShoulderWestB", new Vector3(1.6f, 3.6f, 3.5f), new Vector3(-5.5f, 9.8f, -18f), Rock[3], new Basis(Vector3.Up, -0.2f));
Mass("ShoulderWestC", new Vector3(1.4f, 6f, 3f), new Vector3(-4.3f, 19f, -18.6f), Rock[0], new Basis(Vector3.Up, 0.18f));
Mass("ShoulderEastC", new Vector3(1.5f, 7.5f, 3.2f), new Vector3(4.4f, 19.75f, -18.4f), Rock[3], new Basis(Vector3.Up, -0.12f));
Mass("BackA", new Vector3(9f, 6f, 1.8f), new Vector3(0.5f, 3f, -20.6f), Rock[1], new Basis(Vector3.Up, 0.06f));
Mass("BackB", new Vector3(6f, 5f, 1.4f), new Vector3(-0.8f, 13.5f, -20.4f), Rock[2], new Basis(Vector3.Up, -0.08f));
// Broken corners: big turned chunks knocking the tier edges out of square.
(Vector3 At, Vector3 Size, Vector3 Turn)[] corners =
{
(new(6.3f, 5.6f, -13.4f), new(2.2f, 4.4f, 2.4f), new(0.08f, 0.35f, -0.1f)),
(new(-6.6f, 5.5f, -13.6f), new(2f, 4.6f, 2.2f), new(-0.06f, -0.4f, 0.08f)),
(new(6.4f, 12.4f, -15.4f), new(1.6f, 4f, 2f), new(0.1f, 0.3f, 0.12f)),
(new(-5.5f, 13.6f, -15.2f), new(1.8f, 3.6f, 2f), new(-0.08f, -0.35f, -0.1f)),
(new(4.3f, 23f, -16.4f), new(1.6f, 4.4f, 1.8f), new(0.05f, 0.4f, 0.1f)),
(new(-4.4f, 22.2f, -16.6f), new(1.6f, 4.8f, 1.8f), new(-0.05f, -0.3f, -0.08f)),
(new(8.2f, 2.4f, -17.8f), new(2.6f, 4.8f, 2f), new(0f, 0.2f, -0.35f)), // slab leaning on the east side
};
for (int i = 0; i < corners.Length; i++)
Mass($"Corner{i}", corners[i].Size, corners[i].At, Rock[(i + 1) % Rock.Length], Basis.FromEuler(corners[i].Turn));
// Talus: fallen blocks around the foot (clear of the route's boulders and the marker).
(Vector3 At, float Size)[] talus =
{
(new(9.5f, 0.9f, -13.5f), 1.8f), (new(11f, 0.6f, -16.2f), 1.2f), (new(-9.6f, 1.1f, -14.2f), 2.2f),
(new(-10.5f, 0.6f, -19.5f), 1.2f), (new(6.5f, 0.7f, -11.2f), 1.4f), (new(-8.3f, 0.45f, -11.8f), 0.9f),
(new(4f, 1.2f, -23f), 2.4f), (new(-5f, 0.8f, -23.4f), 1.6f),
};
for (int i = 0; i < talus.Length; i++)
{
float s = talus[i].Size;
Vector3 size = new(s * _rng.RandfRange(0.9f, 1.4f), s, s * _rng.RandfRange(0.8f, 1.2f));
Mass($"Talus{i}", size, talus[i].At, Rock[i % Rock.Length], Basis.FromEuler(new Vector3(_rng.RandfRange(-0.15f, 0.15f), _rng.RandfRange(-1f, 1f), _rng.RandfRange(-0.15f, 0.15f))));
}
// Rubble on the walls away from the route: protruding, slightly turned blocks.
Vector3[] spots =
{
new(3.5f, 1.2f, -12.8f), new(5f, 3.5f, -12.8f), new(1.6f, 5.6f, -12.8f), new(4.2f, 6.8f, -12.8f),
new(-0.2f, 2.4f, -12.8f), new(0.4f, 10.2f, -14.3f), new(-1.2f, 11.6f, -14.3f), new(-4.5f, 11.0f, -14.3f),
new(-6.1f, 4f, -14.2f), new(6.2f, 7.5f, -14.5f), new(-3.6f, 20.5f, -15.8f), new(3.2f, 22.8f, -15.8f),
new(-2.4f, 24.6f, -15.8f), new(1.8f, 18.4f, -15.8f),
};
for (int i = 0; i < spots.Length; i++)
{
Vector3 size = new(_rng.RandfRange(0.8f, 1.8f), _rng.RandfRange(0.6f, 1.4f), 0.6f);
Basis turn = Basis.FromEuler(new Vector3(_rng.RandfRange(-0.12f, 0.12f), _rng.RandfRange(-0.25f, 0.25f), _rng.RandfRange(-0.15f, 0.15f)));
Mass($"Rubble{i}", size, spots[i], Rock[i % Rock.Length].Darkened(_rng.RandfRange(0f, 0.08f)), turn);
}
}
// ---------------------------------------------------------------- the route
private void BuildRoute()
{
// 1. Bouldering: three blocks rising 1.2 / 0.9 / 0.9 m with small gaps between.
Vector3 start = new(-5.6f, 0f, -8.2f);
AddStep(Step.Stand, start, "start");
RouteBlock("Boulder1", new Vector3(1.4f, 1.2f, 1.4f), new Vector3(-5.6f, 0.6f, -9.6f), Rock[3]);
AddStep(Step.Stand, new Vector3(-5.6f, 1.2f, -9.1f), "boulder 1 (mantle 1.2 m)");
AddStep(Step.Stand, new Vector3(-5.0f, 1.2f, -10.1f), "boulder 1 far edge");
RouteBlock("Boulder2", new Vector3(1.4f, 2.1f, 1.4f), new Vector3(-4.6f, 1.05f, -11.2f), Rock[0]);
AddStep(Step.Stand, new Vector3(-4.8f, 2.1f, -10.7f), "boulder 2 (mantle 0.9 m)");
AddStep(Step.Stand, new Vector3(-4.1f, 2.1f, -11.4f), "boulder 2 far edge");
RouteBlock("Boulder3", new Vector3(1.5f, 3f, 1.6f), new Vector3(-2.7f, 1.5f, -12.2f), Rock[2]);
AddStep(Step.Stand, new Vector3(-3.3f, 3f, -11.8f), "boulder 3 (hop 0.45 m gap, mantle 0.9 m)");
AddStep(Step.Stand, new Vector3(-2.7f, 3f, -12.5f), "foot of the ladder");
// 2. Ladder up tier A's face (z = -13) to rest ledge 1; rails run past the lip to pull over.
const float ladderX = -2.7f, ladderZ = -12.85f, half = 0.3f, rail = 0.03f, rung = 0.022f;
foreach (float sx in new[] { -half, half })
{
RouteBar("LadderRail", new Vector3(ladderX + sx, 3f, ladderZ), new Vector3(ladderX + sx, 9f, ladderZ), rail, Metal);
for (float y = 4f; y < 9f; y += 2f)
RouteBlock("LadderStandoff", new Vector3(0.04f, 0.04f, 0.12f), new Vector3(ladderX + sx, y, -12.94f), Metal);
}
for (float y = 3.3f; y < 8.85f; y += 0.3f)
{
RouteBar("Rung", new Vector3(ladderX - half + rail, y, ladderZ), new Vector3(ladderX + half - rail, y, ladderZ), rung, Metal);
AddStep(Step.Hold, new Vector3(ladderX, y + rung + 0.02f, ladderZ), $"rung {y:0.0} m");
}
// Rest ledge 1: tier A's top strip in front of tier B (1.5 m deep, 12 m wide).
AddStep(Step.Stand, new Vector3(ladderX, 8f, -13.4f), "REST LEDGE 1 (8 m)");
FlagPole("8 m", new Vector3(-5.3f, 8f, -13.7f));
AddStep(Step.Stand, new Vector3(-3f, 8f, -14.1f), "foot of the jug wall");
// 3. Jug wall on tier B's face (z = -14.5): two staggered columns, 0.45 m apart vertically.
int n = 0;
for (float y = 9f; y < 13.2f; y += 0.45f, n++)
{
float x = n % 2 == 0 ? -3.25f : -2.75f;
Jug(new Vector3(x, y, -14.5f), Vector3.Back);
}
// 4. Traverse ledge at 13.5 m: 0.4 m deep; its lip is the last jug.
RouteBlock("TraverseLedge", new Vector3(8.2f, 0.12f, 0.4f), new Vector3(0.5f, 13.44f, -14.3f), Rock[3]);
AddStep(Step.Hold, new Vector3(-3f, 13.52f, -14.08f), "traverse ledge lip");
AddStep(Step.Stand, new Vector3(-3f, 13.5f, -14.3f), "traverse ledge (0.4 m wide, 13.5 m)");
for (float x = -2.4f; x <= 3.05f; x += 0.6f)
Jug(new Vector3(x, 14.9f, -14.5f), Vector3.Back);
AddStep(Step.Stand, new Vector3(3f, 13.5f, -14.3f), "under the overhang");
// 5. Overhang: a roof slab 1.5 m out from tier B, monkey bars under it, a jug on its front
// face and a bar along its top edge to pull over onto rest ledge 2.
RouteBlock("Overhang", new Vector3(3f, 0.6f, 1.5f), new Vector3(3f, 15.7f, -13.75f), Rock[0]);
const float barY = 15.2f, barR = 0.025f;
foreach (float z in new[] { -14.2f, -13.7f, -13.2f })
{
RouteBar("RoofBar", new Vector3(1.7f, barY, z), new Vector3(4.3f, barY, z), barR, Metal);
foreach (float x in new[] { 1.8f, 4.2f })
RouteBar("RoofHanger", new Vector3(x, barY + barR, z), new Vector3(x, 15.4f, z), 0.015f, Metal);
AddStep(Step.Hold, new Vector3(3f, barY - barR - 0.02f, z), $"roof bar z {z:0.0}");
}
Jug(new Vector3(3f, 15.65f, -13f), Vector3.Back);
RouteBar("LipBar", new Vector3(1.7f, 16.03f, -13.05f), new Vector3(4.3f, 16.03f, -13.05f), 0.03f, Metal);
AddStep(Step.Hold, new Vector3(3f, 16.08f, -13.05f), "lip bar");
AddStep(Step.Stand, new Vector3(3f, 16f, -13.6f), "REST LEDGE 2 (16 m)");
FlagPole("16 m", new Vector3(-4.3f, 16f, -15.2f));
AddStep(Step.Stand, new Vector3(0f, 16f, -15.6f), "mouth of the chimney");
// 6. Chimney: a 0.9 m slot between tier C's halves (x ±0.45, z -16 … -17), holds on both
// walls staggered 0.5 m and the back wall in between, topping out at the summit.
n = 0;
for (float y = 16.7f; y < 25.9f; y += 0.25f, n++)
{
bool west = n % 2 == 0;
Jug(new Vector3(west ? -0.45f : 0.45f, y, -16.5f), west ? Vector3.Right : Vector3.Left);
}
AddStep(Step.Hold, new Vector3(-0.47f, SummitHeight + 0.02f, -16.4f), "chimney exit lip");
// 7. Summit.
AddStep(Step.Stand, new Vector3(-1f, SummitHeight, -16.6f), "SUMMIT (26 m)");
Sign($"Summit {SummitHeight:0} m", new Vector3(-2.4f, SummitHeight + 1.6f, -18.5f), 2f);
}
// A hold: a 0.3 m wide, 0.12 m proud box on the face at `at`, sticking out along `out`.
private void Jug(Vector3 at, Vector3 outward)
{
Vector3 size = Mathf.Abs(outward.X) > 0.5f ? new Vector3(0.12f, 0.08f, 0.3f) : new Vector3(0.3f, 0.08f, 0.12f);
Vector3 center = at + outward * 0.06f;
RouteBlock("Jug", size, center, Hold, castShadow: false);
AddStep(Step.Hold, center + Vector3.Up * 0.06f + outward * 0.02f, $"jug {at.Y:0.00} m");
}
private void FlagPole(string text, Vector3 foot)
{
RouteBar("FlagPole", foot, foot + Vector3.Up * 2.4f, 0.03f, Metal);
Block("Flag", new Vector3(0.02f, 0.45f, 0.7f), foot + new Vector3(0f, 2.15f, 0.38f), Flag, false);
Sign(text, foot + Vector3.Up * 2.8f, 1.5f);
}
// ---------------------------------------------------------------- base and summit
private void BuildBase()
{
// Trees and pillars at the foot: things of known size to read the height against.
(Vector3 At, float Height)[] trees = { (new(-10f, 0f, -11.5f), 6f), (new(10.5f, 0f, -12f), 7f), (new(-12f, 0f, -17f), 5.5f), (new(12f, 0f, -18f), 6.5f) };
foreach (var (at, h) in trees)
{
Bar("Trunk", at, at + Vector3.Up * h * 0.6f, 0.18f, Bark);
Block("Canopy", new Vector3(2.2f, h * 0.5f, 2.2f), at + Vector3.Up * h * 0.75f, Leaves, true, new Basis(Vector3.Up, at.X * 0.3f), grab: false);
}
Bar("PillarWest", new Vector3(-8.5f, 0f, -10.5f), new Vector3(-8.5f, 9f, -10.5f), 0.35f, Pillar);
Bar("PillarEast", new Vector3(8.5f, 0f, -10f), new Vector3(8.5f, 12f, -10f), 0.35f, Pillar);
Sign("The Crag — 26 m", new Vector3(-5.6f, 2.6f, -7.6f), 1.5f);
// The way down: a teleport marker at the base (aim at it from the summit).
Node3D marker = GD.Load<PackedScene>("res://scenes/teleport_marker.tscn").Instantiate<Node3D>();
marker.Set("MarkerLabel", "Crag");
marker.Position = new Vector3(1.5f, 0f, -9.5f);
AddChild(marker);
}
private void BuildSummitProps()
{
for (int i = 0; i < 3; i++)
BoxProp($"SummitCrate{i}", Vector3.One * 0.4f, 4f, new Vector3(2f + i * 0.5f, SummitHeight + 0.2f, -17.2f), Wood.Darkened(0.05f * i));
Prop("SummitBarrel", new CylinderShape3D { Radius = 0.28f, Height = 0.85f }, new CylinderMesh { TopRadius = 0.28f, BottomRadius = 0.28f, Height = 0.85f },
12f, new Vector3(-2.8f, SummitHeight + 0.425f, -18.8f), Flag.Darkened(0.2f));
Prop("SummitBall", new SphereShape3D { Radius = 0.15f }, new SphereMesh { Radius = 0.15f, Height = 0.3f }, 1.5f,
new Vector3(-1.6f, SummitHeight + 0.15f, -18.4f), new Color(0.95f, 0.85f, 0.2f));
BoxProp("SummitPlank", new Vector3(2f, 0.04f, 0.2f), 4f, new Vector3(2.4f, SummitHeight + 0.02f, -18.8f), Wood);
}
// ---------------------------------------------------------------- builders
private void AddStep(Step kind, Vector3 point, string name) => Route.Add((kind, point, name));
private StaticBody3D Mass(string name, Vector3 size, Vector3 center, Color color, Basis? basis = null)
{
StaticBody3D b = Block(name, size, center, color, true, basis);
MassPieces.Add((b, BoxAabb(size, b.Transform)));
return b;
}
private StaticBody3D RouteBlock(string name, Vector3 size, Vector3 center, Color color, bool castShadow = true)
{
StaticBody3D b = Block(name, size, center, color, castShadow);
RoutePieces.Add((b, BoxAabb(size, b.Transform)));
return b;
}
private StaticBody3D RouteBar(string name, Vector3 from, Vector3 to, float radius, Color color)
{
StaticBody3D b = Bar(name, from, to, radius, color);
// Bounds of the cylinder: radius across the axis only (the ends are flat).
Vector3 d = (to - from).Normalized(), r = new Vector3(
radius * Mathf.Sqrt(Mathf.Max(0f, 1f - d.X * d.X)), radius * Mathf.Sqrt(Mathf.Max(0f, 1f - d.Y * d.Y)), radius * Mathf.Sqrt(Mathf.Max(0f, 1f - d.Z * d.Z)));
RoutePieces.Add((b, new Aabb(from - r, Vector3.Zero).Expand(from + r).Expand(to - r).Expand(to + r)));
return b;
}
private static Aabb BoxAabb(Vector3 size, Transform3D t) => t * new Aabb(-size * 0.5f, size);
private StaticBody3D Block(string name, Vector3 size, Vector3 center, Color color, bool castShadow, Basis? basis = null, bool grab = true)
{
StaticBody3D body = new() { Name = name, CollisionLayer = WorldLayer, CollisionMask = WorldMask, Transform = new Transform3D(basis ?? Basis.Identity, center) };
if (!grab)
body.AddToGroup(PropGrabber.NoGrabGroup);
body.AddChild(new CollisionShape3D { Shape = new BoxShape3D { Size = size } });
MeshInstance3D mesh = Mesh(new BoxMesh { Size = size }, color);
if (!castShadow)
mesh.CastShadow = GeometryInstance3D.ShadowCastingSetting.Off;
body.AddChild(mesh);
AddChild(body);
return body;
}
private StaticBody3D Bar(string name, Vector3 from, Vector3 to, float radius, Color color)
{
Vector3 axis = to - from;
float len = axis.Length();
Vector3 y = axis / len;
Vector3 x = Mathf.Abs(y.Y) < 0.99f ? Vector3.Up.Cross(y).Normalized() : Vector3.Right;
StaticBody3D body = new() { Name = name, CollisionLayer = WorldLayer, CollisionMask = WorldMask, Transform = new Transform3D(new Basis(x, y, x.Cross(y)), (from + to) * 0.5f) };
body.AddChild(new CollisionShape3D { Shape = new CylinderShape3D { Radius = radius, Height = len } });
body.AddChild(Mesh(new CylinderMesh { TopRadius = radius, BottomRadius = radius, Height = len, RadialSegments = 12 }, color));
AddChild(body);
return body;
}
private RigidBody3D Prop(string name, Shape3D shape, Mesh mesh, float mass, Vector3 center, Color color)
{
RigidBody3D body = new()
{
Name = name,
Mass = mass,
Transform = new Transform3D(Basis.Identity, center),
CollisionMask = PropMask,
ContactMonitor = true,
MaxContactsReported = 4,
PhysicsMaterialOverride = new PhysicsMaterial { Friction = 0.8f, Bounce = 0.05f },
};
body.AddToGroup("prop");
body.AddChild(new CollisionShape3D { Shape = shape });
body.AddChild(Mesh(mesh, color));
AddChild(body);
_dynamic.Add((body, body.GlobalTransform));
return body;
}
private RigidBody3D BoxProp(string name, Vector3 size, float mass, Vector3 center, Color color) =>
Prop(name, new BoxShape3D { Size = size }, new BoxMesh { Size = size }, mass, center, color);
private static MeshInstance3D Mesh(Mesh mesh, Color color)
{
MeshInstance3D m = new() { Mesh = mesh };
m.SetSurfaceOverrideMaterial(0, new StandardMaterial3D { AlbedoColor = color, Roughness = 0.9f });
return m;
}
private void Sign(string text, Vector3 at, float scale)
{
AddChild(new Label3D
{
Text = text,
FontSize = 96,
PixelSize = 0.002f * scale,
OutlineSize = 12,
Billboard = BaseMaterial3D.BillboardModeEnum.FixedY,
Position = at,
});
}
}