supervisor-simulator/scripts/Lab.cs
2024-12-06 23:53:02 +08:00

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 MAP_WIDTH = 40;
public const int MAP_HEIGHT = 21;
private readonly MapNodeType[,] blocks = new MapNodeType[MAP_WIDTH, MAP_HEIGHT];
private Dictionary<Rect2I, ITileDraggable> furniturePlacement = new ();
private TileMapLayer tileMap;
public void UpdateMap()
{
for (int i = 0; i < MAP_WIDTH; i++) {
for (int j = 0; j < MAP_HEIGHT; 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 >= MAP_WIDTH || y < 0 || y >= MAP_HEIGHT) {
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 < MAP_HEIGHT; j++) {
string t = "";
for (int i = 0; i < MAP_WIDTH; 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 * MAP_WIDTH + v.X;
static Vector2I idx2vec2(int i) => new(i % MAP_WIDTH, i / MAP_WIDTH);
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[MAP_WIDTH * MAP_HEIGHT];
int[] prev = new int[MAP_WIDTH * MAP_HEIGHT];
bool[] visited = new bool[MAP_WIDTH * MAP_HEIGHT];
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 min_distance = int.MaxValue;
int u = -1;
for (int j = 0; j < dist.Length; j++) {
if (!visited[j] && dist[j] <= min_distance) {
min_distance = 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;
}
}