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;
|
|
// 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;
|
|
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; }
|
|
|
|
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;
|
|
}
|
|
|
|
}
|