239 lines
6.0 KiB
C#
239 lines
6.0 KiB
C#
using Godot;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
|
|
public partial class Lab : Node2D
|
|
{
|
|
// 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<Table>("Table");
|
|
Player.Timeline.OnDayChanged += d => table.RandomChangeTheme();
|
|
|
|
MoneyLabel = GetNode<Label>("BottomBar/HBoxContainer/Money");
|
|
|
|
tileMap = GetNode<TileMapLayer>("TestMap");
|
|
UpdateMap();
|
|
}
|
|
|
|
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");
|
|
}
|
|
|
|
|
|
private static readonly Vector2I tableTileCoord = new(0, 6);
|
|
private static readonly Vector2I waterTileCoord = new(6, 9);
|
|
private static readonly Vector2I doorTileCoord = new(9, 3);
|
|
private static readonly Vector2I axeTileCoord = new(10, 9);
|
|
private static readonly Vector2I blockTileCoord = new(4, 3);
|
|
|
|
public enum MapNodeType
|
|
{
|
|
Table,
|
|
Water,
|
|
Door,
|
|
Axe,
|
|
Block,
|
|
Road,
|
|
}
|
|
|
|
|
|
private const int MAP_WIDTH = 24;
|
|
private const int MAP_HEIGHT = 13;
|
|
|
|
private readonly MapNodeType[,] blocks = new MapNodeType[MAP_WIDTH, MAP_HEIGHT];
|
|
|
|
|
|
private TileMapLayer tileMap;
|
|
|
|
public void UpdateMap()
|
|
{
|
|
for (int i = 0; i < MAP_WIDTH; i++) {
|
|
for (int j = 0; j < MAP_HEIGHT; j++) {
|
|
var tile = tileMap.GetCellAtlasCoords(new Vector2I(i,j));
|
|
if (tile == tableTileCoord) {
|
|
blocks[i,j] = MapNodeType.Table;
|
|
} else if (tile == waterTileCoord) {
|
|
blocks[i,j] = MapNodeType.Water;
|
|
} else if (tile == doorTileCoord) {
|
|
blocks[i,j] = MapNodeType.Door;
|
|
} else if (tile == axeTileCoord) {
|
|
blocks[i,j] = MapNodeType.Axe;
|
|
} else if (tile == blockTileCoord) {
|
|
blocks[i,j] = MapNodeType.Block;
|
|
} else {
|
|
blocks[i,j] = MapNodeType.Road;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
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.Road) {
|
|
neighbor.Add(new Vector2I(x-1,y));
|
|
}
|
|
if (IsValidPosition(new Vector2I(x+1,y)) && blocks[x+1,y] == MapNodeType.Road) {
|
|
neighbor.Add(new Vector2I(x+1,y));
|
|
}
|
|
if (IsValidPosition(new Vector2I(x,y-1)) && blocks[x,y-1] == MapNodeType.Road) {
|
|
neighbor.Add(new Vector2I(x,y-1));
|
|
}
|
|
if (IsValidPosition(new Vector2I(x,y+1)) && blocks[x,y+1] == MapNodeType.Road) {
|
|
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++) {
|
|
t += $"{blocks[i,j]} ";
|
|
}
|
|
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);
|
|
GD.Print(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;
|
|
}
|
|
}
|
|
GD.Print($"u={u}");
|
|
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;
|
|
GD.Print($"Alt={alt}");
|
|
foreach (var neighbor in neighbors) {
|
|
int idx = vec2idx(neighbor);
|
|
if (visited[idx]) continue;
|
|
GD.Print($"Neighbor: {neighbor}, id={idx}, dist={dist[idx]}");
|
|
if (alt < dist[idx]) {
|
|
dist[idx] = alt;
|
|
prev[idx] = u;
|
|
GD.Print($"Prev({idx}) = {u}");
|
|
}
|
|
}
|
|
}
|
|
|
|
while (answer != vec2idx(start)) {
|
|
path.Add(idx2vec2(answer));
|
|
answer = prev[answer];
|
|
}
|
|
path.Reverse();
|
|
return path;
|
|
}
|
|
|
|
public Vector2I GetTypedBlock(MapNodeType nType, uint idx) {
|
|
Vector2I tileCoord;
|
|
switch(nType) {
|
|
case MapNodeType.Table:
|
|
tileCoord = tableTileCoord;
|
|
break;
|
|
case MapNodeType.Water:
|
|
tileCoord = waterTileCoord;
|
|
break;
|
|
case MapNodeType.Door:
|
|
tileCoord = doorTileCoord;
|
|
break;
|
|
case MapNodeType.Axe:
|
|
tileCoord = axeTileCoord;
|
|
break;
|
|
default:
|
|
return Vector2I.Zero;
|
|
}
|
|
var candidates = tileMap.GetUsedCellsById(0, tileCoord, -1);
|
|
idx %= (uint)candidates.Count;
|
|
return candidates[(int)idx];
|
|
}
|
|
|
|
public Vector2I Point2Coord(Vector2 pos) {
|
|
return tileMap.LocalToMap(tileMap.ToLocal(pos));
|
|
}
|
|
|
|
}
|