277 lines
7.5 KiB
C#
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;
|
|
}
|
|
|
|
}
|