supervisor-simulator/scripts/Lab.cs

277 lines
7.5 KiB
C#

using Godot;
using System;
using System.Collections.Generic;
using System.Linq;
public partial class Lab : Node2D
{
[Flags]
public enum MapNodeType
{
Invalid = 0,
Walkable = 1,
Wall = 2,
Blocker = 4,
SeatUp = 8,
SeatDown = 16,
}
private static readonly Rect2I[] wallRectangles = {
new(0,0,40,2),
new(0,5,1, 15),
new(0,20,40,1),
new(39,2,1,18),
};
private readonly Dictionary<Guid, ITileDraggable> _furnitureIDs = new();
public Dictionary<Guid, ITileDraggable> Furniture => _furnitureIDs;
// Called when the node enters the scene tree for the first time.
public override void _Ready()
{
var ticker = GetNode<Timer>("/root/GameManager/OneSecondTicker");
Player.Timeline.Attach(ticker);
Player.Timeline.Subscribe(DateOnly.Parse("2024/11/20"), Guid.NewGuid());
Player.Timeline.OnEventTriggered += (d, e) => GD.Print($"Timeline event triggered: {d}, {e}");
var label = GetNode<Label>("BottomBar/HBoxContainer/Date");
label.Text = Player.Timeline.InternalDate.ToLongDateString();
Player.Timeline.OnDayChanged += d => label.Text = d.ToLongDateString();
var table = GetNode<Cube>("Cube");
table.Draggable = true;
_moneyLabel = GetNode<Label>("BottomBar/HBoxContainer/Money");
var rect = table.TileRect;
rect.Position += table.TilePosition;
_furniturePlacement.Add(rect, table);
_furnitureIDs.Add(table.Id, table);
_tileMap = GetNode<TileMapLayer>("OutGround");
UpdateMap();
var student = GetNode<Student>("Student");
student.CubeId = table.Id;
var r = new Rect2I(0,0,1,1);
GD.Print(H.RectHasPointInclusive(r, new Vector2I(1,0)));
}
private Label _moneyLabel = null;
// Called every frame. 'delta' is the elapsed time since the previous frame.
public override void _Process(double delta)
{
switch(GD.RandRange(0,2)) {
case 0:
Player.Budget.Operational++;
break;
case 1:
Player.Budget.Facility += 10;
break;
case 2:
Player.Budget.Labor += 100;
break;
}
_moneyLabel.Text = Player.Budget.Total.ToString("N0");
if (_isDragging && DraggingTarget != null) {
Vector2 mousePos = GetLocalMousePosition();
Vector2I cell = Point2Coord(mousePos) - DraggingTarget.MouseOffset;
var targetRect = DraggingTarget.TileRect;
targetRect.Position += cell;
DraggingTarget.IsCollided = wallRectangles.Any(r => r.Intersects(targetRect));
DraggingTarget.TilePosition = cell;
}
}
private bool _isDragging = false;
public void Pickup(ITileDraggable target) {
if (target == null) return;
_isDragging = true;
DraggingTarget = target;
}
public void PutDown(ITileDraggable target) {
if (target == null) return;
_isDragging = false;
_furniturePlacement.Remove(_furniturePlacement.First(kv => kv.Value == target).Key);
var rect = target.TileRect;
rect.Position += target.TilePosition;
_furniturePlacement.Add(rect, target);
DraggingTarget = null;
UpdateMap();
}
public ITileDraggable DraggingTarget { get; set; } = null;
public const int MapWidth = 40;
public const int MapHeight = 21;
private readonly MapNodeType[,] _blocks = new MapNodeType[MapWidth, MapHeight];
private Dictionary<Rect2I, ITileDraggable> _furniturePlacement = new ();
private TileMapLayer _tileMap;
public void UpdateMap()
{
for (int i = 0; i < MapWidth; i++) {
for (int j = 0; j < MapHeight; j++) {
Vector2I vec = new (i,j);
if (wallRectangles.Any(w => w.HasPoint(vec))) {
_blocks[i, j] = MapNodeType.Wall;
} else if (_furniturePlacement.Any(f => f.Key.HasPoint(vec))) {
MapNodeType t = 0;
foreach (var kv in _furniturePlacement.Where(f => f.Key.HasPoint(vec))) {
t |= kv.Value.GetTileType(vec - kv.Value.TilePosition);
}
if ((t & MapNodeType.Walkable) != 0 && (t & MapNodeType.Blocker) != 0){
t ^= MapNodeType.Walkable;
}
_blocks[i ,j] = t;
} else {
_blocks[i, j] = MapNodeType.Walkable;
}
}
}
}
private static bool IsValidPosition(Vector2I pos)
{
int x = pos.X;
int y = pos.Y;
if (x < 0 || x >= MapWidth || y < 0 || y >= MapHeight) {
return false;
}
return true;
}
private List<Vector2I> GetNeighbors(Vector2I pos) {
int x = pos.X;
int y = pos.Y;
var neighbor = new List<Vector2I>();
if (!IsValidPosition(pos)) {
return neighbor;
}
if (IsValidPosition(new Vector2I(x-1,y)) && (_blocks[x-1,y] & MapNodeType.Walkable) == MapNodeType.Walkable) {
neighbor.Add(new Vector2I(x-1,y));
}
if (IsValidPosition(new Vector2I(x+1,y)) && (_blocks[x+1,y] & MapNodeType.Walkable) == MapNodeType.Walkable) {
neighbor.Add(new Vector2I(x+1,y));
}
if (IsValidPosition(new Vector2I(x,y-1)) && (_blocks[x,y-1] & MapNodeType.Walkable) == MapNodeType.Walkable) {
neighbor.Add(new Vector2I(x,y-1));
}
if (IsValidPosition(new Vector2I(x,y+1)) && (_blocks[x,y+1] & MapNodeType.Walkable) == MapNodeType.Walkable) {
neighbor.Add(new Vector2I(x,y+1));
}
return neighbor;
}
public List<Vector2I> GetShortestPath(Vector2I start, Vector2I end)
{
for (int j = 0; j < MapHeight; j++) {
string t = "";
for (int i = 0; i < MapWidth; i++) {
if ((_blocks[i,j] & MapNodeType.SeatUp) != 0) {
t += "U";
} else if ((_blocks[i,j] & MapNodeType.SeatDown) != 0) {
t += "D";
} else {
t += $"{_blocks[i,j].ToString()[0]}";
}
}
GD.Print(t);
}
var path = new List<Vector2I>();
if (_tileMap.GetCellSourceId(start) == -1 || _tileMap.GetCellSourceId(end) == -1) {
return path;
}
if (start == end) {
return path;
}
GD.Print($"start = {start}, end = {end}");
static int Vec2Idx(Vector2I v) => v.Y * MapWidth + v.X;
static Vector2I Idx2Vec2(int i) => new(i % MapWidth, i / MapWidth);
HashSet<int> entriesIdx = new();
foreach (var e in GetNeighbors(end)) {
entriesIdx.Add(Vec2Idx(e));
}
if (entriesIdx.Count == 0) {
return path;
}
// Use Dijkstra algorithm to find the shortest path
int[] dist = new int[MapWidth * MapHeight];
int[] prev = new int[MapWidth * MapHeight];
bool[] visited = new bool[MapWidth * MapHeight];
for (int i = 0; i < dist.Length; i++) {
dist[i] = int.MaxValue;
prev[i] = -1;
visited[i] = false;
}
dist[Vec2Idx(start)] = 0;
int answer = -1;
for (int i = 0; i < dist.Length; i++) {
int minDistance = int.MaxValue;
int u = -1;
for (int j = 0; j < dist.Length; j++) {
if (!visited[j] && dist[j] <= minDistance) {
minDistance = dist[j];
u = j;
}
}
if (u == -1) {
return path;
}
if (entriesIdx.Contains(u)){
answer = u;
break;
}
visited[u] = true;
var neighbors = GetNeighbors(Idx2Vec2(u));
int alt = dist[u] + 1;
foreach (var neighbor in neighbors) {
int idx = Vec2Idx(neighbor);
if (visited[idx]) continue;
if (alt < dist[idx]) {
dist[idx] = alt;
prev[idx] = u;
}
}
}
while (answer != Vec2Idx(start)) {
path.Add(Idx2Vec2(answer));
answer = prev[answer];
}
path.Reverse();
GD.Print(string.Join(',', path));
return path;
}
public Vector2I GetTypedBlock(MapNodeType nType, uint idx) {
switch(nType) {
default:
return Vector2I.Zero;
}
}
public MapNodeType GetMapNodeTypeOfPosition(Vector2I pos) {
if (!IsValidPosition(pos)) return MapNodeType.Invalid;
return _blocks[pos.X, pos.Y];
}
public Vector2I Point2Coord(Vector2 pos) {
return _tileMap.LocalToMap(_tileMap.ToLocal(pos));
}
public Vector2I GetFurSpecialPosition(Guid fId, int idx) {
if (!_furnitureIDs.ContainsKey(fId)) return Vector2I.Zero;
if (idx < 0 || idx > _furnitureIDs[fId].SpecialTiles.Length) return Vector2I.Zero;
return _furnitureIDs[fId].SpecialTiles[idx].Position + _furnitureIDs[fId].TilePosition;
}
}