using System.Collections.Generic;
using Luneraria.Core;
using UnityEngine;
namespace Luneraria.View
{
///
/// S4 fog of war in the terrain: a grid over the map (2 m cells) that follows the ground and
/// darkens every point no vision source of the viewer's team reaches (LuneFog.Vision: own
/// units 9 m, own buildings 10 m, wards 12 m). The alpha has a soft 2 m edge and is
/// recomputed ten times a second. Presentation only; the rules never read it.
///
public sealed class LuneFogOverlay : MonoBehaviour
{
public const float Cell = 2f;
public const float Darkness = .55f;
public const float Edge = 2f;
public const float Interval = .1f;
Mesh _mesh;
Vec2[] _points;
Color32[] _colors;
float _timer;
/// Darkness of every grid point (0 = seen), for tests.
public float[] Alpha { get; private set; }
public int Refreshes { get; private set; }
public void Build(LuneGround ground, Material material)
{
int side = Mathf.CeilToInt((float)(LuneMap.HalfExtent * 2.0) / Cell) + 1;
var vertices = new Vector3[side * side];
_points = new Vec2[side * side];
for (int row = 0; row < side; row++)
{
for (int column = 0; column < side; column++)
{
var map = new Vec2(-LuneMap.HalfExtent + column * Cell, -LuneMap.HalfExtent + row * Cell);
float height = ground != null ? ground.StandHeight(map) : 0f;
_points[row * side + column] = map;
vertices[row * side + column] = LuneSpace.World(map, height + .45f);
}
}
var triangles = new List();
for (int row = 0; row + 1 < side; row++)
{
for (int column = 0; column + 1 < side; column++)
{
int a = row * side + column;
triangles.AddRange(new[] { a, a + side, a + 1, a + 1, a + side, a + side + 1 });
}
}
_colors = new Color32[vertices.Length];
Alpha = new float[vertices.Length];
_mesh = new Mesh { name = "FogOverlay", indexFormat = UnityEngine.Rendering.IndexFormat.UInt32 };
_mesh.SetVertices(vertices);
_mesh.SetTriangles(triangles, 0);
_mesh.SetColors(_colors);
_mesh.RecalculateBounds();
gameObject.AddComponent().sharedMesh = _mesh;
var renderer = gameObject.AddComponent();
renderer.sharedMaterial = material;
renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off;
renderer.receiveShadows = false;
}
/// Recomputes the darkness at most every Interval seconds (force: now).
public void Tick(float dt, List> sources, bool force = false)
{
_timer -= dt;
if (!force && _timer > 0f)
return;
_timer = Interval;
Refreshes++;
for (int i = 0; i < _points.Length; i++)
{
double best = double.PositiveInfinity;
foreach (KeyValuePair source in sources)
{
double gap = _points[i].DistanceTo(source.Key) - source.Value;
if (gap < best)
best = gap;
}
float t = Mathf.Clamp01((float)(best + Edge * .5) / Edge);
Alpha[i] = Darkness * t;
_colors[i] = new Color32(0, 0, 0, (byte)Mathf.RoundToInt(Alpha[i] * 255f));
}
_mesh.SetColors(_colors);
}
}
}