baseline: working salvaged prototype on Godot 4.7.1 (pre-F1)
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
commit
7f0f43b5fb
48 changed files with 2913 additions and 0 deletions
225
Server/Scripts/ServerChunkManager.cs
Normal file
225
Server/Scripts/ServerChunkManager.cs
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
using Godot;
|
||||
using System.Collections.Generic;
|
||||
using IslaApocalypse.Core;
|
||||
|
||||
namespace IslaApocalypse.Server
|
||||
{
|
||||
public partial class ServerChunkManager : Node
|
||||
{
|
||||
private WorldBlueprint _blueprint;
|
||||
private Dictionary<Vector2I, ChunkData> _activeChunks = new Dictionary<Vector2I, ChunkData>();
|
||||
public int chunkSize = 24;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
|
||||
// 1. LOAD CONFIGURATION and the seed-based blueprint data from the MapDataParser!
|
||||
ConfigManager.LoadConfig();
|
||||
chunkSize = ConfigManager.ChunkRadius; // Update chunk radius from config file
|
||||
_blueprint = MapDataParser.LoadMapData(ConfigManager.WorldSeed.ToString());
|
||||
|
||||
if (_blueprint != null)
|
||||
{
|
||||
GD.Print("[Server] Blueprint loaded. Locating Capitol City...");
|
||||
|
||||
// 2. Find the Capitol in the parsed data
|
||||
|
||||
Vector2 capitolPos = new Vector2(2048, 2048); // Safe fallback center
|
||||
foreach (var town in _blueprint.Towns) {
|
||||
if (town.Tier == TownTier.Capitol) {
|
||||
capitolPos = town.Position;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Old original png map coords
|
||||
// Vector2 capitolPos = new Vector2(2405, 3296); // hardcoded for now since we know exactly where it is in this seed, which is the "paradise" biome hub
|
||||
|
||||
|
||||
GD.Print($"[Server] Capitol found at {capitolPos}. Generating chunks...");
|
||||
|
||||
// 3. Convert pixel coordinates to Chunk coordinates
|
||||
int capitolChunkX = (int)(capitolPos.X / Constants.CHUNK_SIZE_X);
|
||||
int capitolChunkZ = (int)(capitolPos.Y / Constants.CHUNK_SIZE_Z);
|
||||
|
||||
int radius = ConfigManager.ChunkRadius;
|
||||
|
||||
for (int x = capitolChunkX - radius; x < capitolChunkX + radius; x++)
|
||||
{
|
||||
for (int z = capitolChunkZ - radius; z < capitolChunkZ + radius; z++)
|
||||
{
|
||||
GenerateChunk(new Vector2I(x, z));
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Teleport the Camera to look down at our creation!
|
||||
Camera3D cam = GetNodeOrNull<Camera3D>("Camera3D");
|
||||
if (cam != null)
|
||||
{
|
||||
// Put the camera 120 meters in the air above the Capitol
|
||||
cam.GlobalPosition = new Vector3(capitolPos.X, 120f, capitolPos.Y);
|
||||
// Point it straight down at the ground
|
||||
cam.LookAt(new Vector3(capitolPos.X, 0, capitolPos.Y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void GenerateChunk(Vector2I chunkCoord)
|
||||
{
|
||||
if (_activeChunks.ContainsKey(chunkCoord)) return;
|
||||
|
||||
ChunkData newChunk = new ChunkData(chunkCoord);
|
||||
|
||||
int startX = chunkCoord.X * Constants.CHUNK_SIZE_X;
|
||||
int startZ = chunkCoord.Y * Constants.CHUNK_SIZE_Z;
|
||||
|
||||
// --- NEW: SPATIAL CULLING FOR ROADS ---
|
||||
// Create a bounding box for this chunk, plus a 15-meter padding to account for the road's dirt shoulders
|
||||
Rect2 chunkBounds = new Rect2(startX - 15, startZ - 15, Constants.CHUNK_SIZE_X + 30, Constants.CHUNK_SIZE_Z + 30);
|
||||
List<Vector2[]> localRoadSegments = new List<Vector2[]>();
|
||||
|
||||
// Filter Highways: Only save the segments that actually cross this specific chunk!
|
||||
foreach (var highway in _blueprint.Highways) {
|
||||
for (int i = 0; i < highway.Length - 1; i++) {
|
||||
Vector2 a = highway[i];
|
||||
Vector2 b = highway[i+1];
|
||||
Rect2 segBounds = new Rect2(Mathf.Min(a.X, b.X), Mathf.Min(a.Y, b.Y), Mathf.Abs(b.X - a.X), Mathf.Abs(b.Y - a.Y));
|
||||
if (chunkBounds.Intersects(segBounds.Grow(1.0f))) {
|
||||
localRoadSegments.Add(new Vector2[] { a, b });
|
||||
}
|
||||
}
|
||||
}
|
||||
// Filter Branch Roads
|
||||
foreach (var branch in _blueprint.BranchRoads) {
|
||||
for (int i = 0; i < branch.Length - 1; i++) {
|
||||
Vector2 a = branch[i];
|
||||
Vector2 b = branch[i+1];
|
||||
Rect2 segBounds = new Rect2(Mathf.Min(a.X, b.X), Mathf.Min(a.Y, b.Y), Mathf.Abs(b.X - a.X), Mathf.Abs(b.Y - a.Y));
|
||||
if (chunkBounds.Intersects(segBounds.Grow(1.0f))) {
|
||||
localRoadSegments.Add(new Vector2[] { a, b });
|
||||
}
|
||||
}
|
||||
}
|
||||
// ---------------------------------------
|
||||
|
||||
for (int x = 0; x <= Constants.CHUNK_SIZE_X; x++)
|
||||
{
|
||||
for (int z = 0; z <= Constants.CHUNK_SIZE_Z; z++)
|
||||
{
|
||||
int globalX = startX + x;
|
||||
int globalZ = startZ + z;
|
||||
|
||||
if (globalX >= _blueprint.MapSize || globalZ >= _blueprint.MapSize || globalX < 0 || globalZ < 0)
|
||||
continue;
|
||||
|
||||
float GetExactSurface(int gX, int gZ, out bool isRoad)
|
||||
{
|
||||
isRoad = false;
|
||||
if (gX >= _blueprint.MapSize) gX = _blueprint.MapSize - 1;
|
||||
if (gZ >= _blueprint.MapSize) gZ = _blueprint.MapSize - 1;
|
||||
|
||||
float raw = _blueprint.HeightMap[gX, gZ];
|
||||
float baseHeight = Mathf.Clamp(raw * (Constants.CHUNK_HEIGHT - 5), 2.0f, Constants.CHUNK_HEIGHT - 2.0f);
|
||||
|
||||
// If no roads are in this chunk, skip the heavy math entirely!
|
||||
if (localRoadSegments.Count == 0) return baseHeight;
|
||||
|
||||
float finalHeight = baseHeight;
|
||||
float minDist = 9999f;
|
||||
Vector2 currentPos = new Vector2(gX, gZ);
|
||||
|
||||
float roadRadius = 4.0f; // The flat asphalt part (8 meters total width)
|
||||
float shoulderRadius = 12.0f; // The sloped dirt/rock carving into the mountain
|
||||
|
||||
float closestRoadElevation = baseHeight;
|
||||
|
||||
foreach (var seg in localRoadSegments)
|
||||
{
|
||||
float dist = DistanceToLineSegment(currentPos, seg[0], seg[1]);
|
||||
if (dist < minDist)
|
||||
{
|
||||
minDist = dist;
|
||||
if (dist <= shoulderRadius)
|
||||
{
|
||||
// Sample the heightmap precisely at the center of the road segment
|
||||
// so the whole road stays at a uniform elevation, ignoring the sloped mountain under it.
|
||||
Vector2 midPoint = (seg[0] + seg[1]) / 2.0f;
|
||||
float roadRaw = _blueprint.HeightMap[(int)midPoint.X, (int)midPoint.Y];
|
||||
closestRoadElevation = Mathf.Clamp(roadRaw * (Constants.CHUNK_HEIGHT - 5), 2.0f, Constants.CHUNK_HEIGHT - 2.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// THE BULLDOZER: Carve the terrain
|
||||
if (minDist <= roadRadius) {
|
||||
finalHeight = closestRoadElevation; // Flatten it completely!
|
||||
isRoad = true;
|
||||
} else if (minDist <= shoulderRadius) {
|
||||
// Smoothly interpolate from the flat road up/down to the natural mountain height
|
||||
float t = (minDist - roadRadius) / (shoulderRadius - roadRadius);
|
||||
t = t * t * (3f - 2f * t); // SmoothStep equation for a beautiful curved slope
|
||||
finalHeight = Mathf.Lerp(closestRoadElevation, baseHeight, t);
|
||||
}
|
||||
|
||||
return finalHeight;
|
||||
}
|
||||
|
||||
// Pass the boolean out for the main voxel so we can paint it!
|
||||
bool isMainRoad = false;
|
||||
float exactSurfaceY = GetExactSurface(globalX, globalZ, out isMainRoad);
|
||||
|
||||
// We discard the 'out' variable for the normals using an underscore '_'
|
||||
float surfaceRight = GetExactSurface(globalX + 1, globalZ, out _);
|
||||
float surfaceFwd = GetExactSurface(globalX, globalZ + 1, out _);
|
||||
|
||||
Biome columnBiome = _blueprint.BiomeMap[globalX, globalZ];
|
||||
|
||||
float hRight = surfaceRight - exactSurfaceY;
|
||||
float hFwd = surfaceFwd - exactSurfaceY;
|
||||
float slopeX = hRight / Constants.VOXEL_SCALE;
|
||||
float slopeZ = hFwd / Constants.VOXEL_SCALE;
|
||||
|
||||
float len = Mathf.Sqrt(slopeX * slopeX + 1.0f + slopeZ * slopeZ);
|
||||
|
||||
for (int y = 0; y <= Constants.CHUNK_HEIGHT; y++)
|
||||
{
|
||||
float verticalDist = y - exactSurfaceY;
|
||||
float density = verticalDist / len;
|
||||
|
||||
newChunk.Densities[x, y, z] = density;
|
||||
|
||||
int blockY = Mathf.RoundToInt(exactSurfaceY);
|
||||
|
||||
// THE PAINT: Pass isMainRoad into the Biome Palette!
|
||||
newChunk.BlockIDs[x, y, z] = BiomePalette.GetVoxelID(columnBiome, blockY, y, isMainRoad);
|
||||
}
|
||||
} // End of Z loop
|
||||
} // End of X loop
|
||||
|
||||
_activeChunks.Add(chunkCoord, newChunk);
|
||||
|
||||
var renderer = new IslaApocalypse.Client.ChunkRenderer();
|
||||
AddChild(renderer);
|
||||
renderer.RenderChunk(newChunk);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the shortest distance from a point to a line segment defined by v and w.
|
||||
/// </summary>
|
||||
private float DistanceToLineSegment(Vector2 point, Vector2 v, Vector2 w)
|
||||
{
|
||||
float l2 = v.DistanceSquaredTo(w);
|
||||
if (l2 == 0) return point.DistanceTo(v); // v == w case
|
||||
|
||||
// Consider the line extending the segment, parameterized as v + t (w - v).
|
||||
// We find projection of point p onto the line.
|
||||
// It falls where t = [(p-v) . (w-v)] / |w-v|^2
|
||||
float t = Mathf.Max(0, Mathf.Min(1, (point - v).Dot(w - v) / l2));
|
||||
|
||||
// Projection falls on the segment
|
||||
Vector2 projection = v + t * (w - v);
|
||||
return point.DistanceTo(projection);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue