Player movement. To Fix: player out of area.
This commit is contained in:
parent
f3a4e31f31
commit
bce6fffc3e
@ -11,7 +11,7 @@ config_version=5
|
|||||||
[application]
|
[application]
|
||||||
|
|
||||||
config/name="最强导师"
|
config/name="最强导师"
|
||||||
run/main_scene="uid://bmxk4pi4sd1rd"
|
run/main_scene="uid://b0cu4fa7vohmw"
|
||||||
config/features=PackedStringArray("4.5", "C#", "GL Compatibility")
|
config/features=PackedStringArray("4.5", "C#", "GL Compatibility")
|
||||||
config/icon="res://icon.svg"
|
config/icon="res://icon.svg"
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 178 KiB After Width: | Height: | Size: 177 KiB |
@ -1,5 +1,6 @@
|
|||||||
using Godot;
|
using Godot;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
public partial class CampusController : Node2D
|
public partial class CampusController : Node2D
|
||||||
{
|
{
|
||||||
@ -8,6 +9,16 @@ public partial class CampusController : Node2D
|
|||||||
private Button _taskToggle;
|
private Button _taskToggle;
|
||||||
private Button _logToggle;
|
private Button _logToggle;
|
||||||
|
|
||||||
|
[Export] public PackedScene StudentScene { get; set; }
|
||||||
|
[Export] public int StudentCount { get; set; } = 6;
|
||||||
|
[Export] public float CoverageStep { get; set; } = 48.0f;
|
||||||
|
[Export] public int MaxCoveragePoints { get; set; } = 200;
|
||||||
|
|
||||||
|
private NavigationRegion2D _navigationRegion;
|
||||||
|
private Node2D _studentsRoot;
|
||||||
|
private readonly List<Vector2> _coveragePoints = new();
|
||||||
|
private bool _spawnPending = true;
|
||||||
|
|
||||||
// Called when the node enters the scene tree for the first time.
|
// Called when the node enters the scene tree for the first time.
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
{
|
{
|
||||||
@ -24,11 +35,24 @@ public partial class CampusController : Node2D
|
|||||||
|
|
||||||
_taskToggle.Toggled += OnTaskToggled;
|
_taskToggle.Toggled += OnTaskToggled;
|
||||||
_logToggle.Toggled += OnLogToggled;
|
_logToggle.Toggled += OnLogToggled;
|
||||||
|
|
||||||
|
// 导航区域与学生容器初始化
|
||||||
|
_navigationRegion = GetNodeOrNull<NavigationRegion2D>("Sprite2D/NavigationRegion2D");
|
||||||
|
_studentsRoot = GetNodeOrNull<Node2D>("Students");
|
||||||
|
if (_studentsRoot == null)
|
||||||
|
{
|
||||||
|
_studentsRoot = new Node2D { Name = "Students" };
|
||||||
|
AddChild(_studentsRoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 等待导航地图同步完成后再生成学生
|
||||||
|
_spawnPending = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called every frame. 'delta' is the elapsed time since the previous frame.
|
// Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||||
public override void _Process(double delta)
|
public override void _Process(double delta)
|
||||||
{
|
{
|
||||||
|
TrySpawnStudents();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnTaskToggled(bool pressed)
|
private void OnTaskToggled(bool pressed)
|
||||||
@ -61,4 +85,158 @@ public partial class CampusController : Node2D
|
|||||||
tween.TweenCallback(Callable.From(() => container.Visible = false));
|
tween.TweenCallback(Callable.From(() => container.Visible = false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
private void TrySpawnStudents()
|
||||||
|
{
|
||||||
|
if (!_spawnPending)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 已生成过学生则不重复生成
|
||||||
|
if (_studentsRoot != null && _studentsRoot.GetChildCount() > 0)
|
||||||
|
{
|
||||||
|
_spawnPending = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StudentScene == null)
|
||||||
|
{
|
||||||
|
StudentScene = ResourceLoader.Load<PackedScene>("res://scenes/student_16_native.tscn");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_navigationRegion == null || _navigationRegion.NavigationPolygon == null)
|
||||||
|
{
|
||||||
|
GD.PushWarning("校园导航区域未配置或缺失导航多边形,无法生成巡游学生。");
|
||||||
|
_spawnPending = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导航地图可能还未就绪,需要等待同步完成后再采样
|
||||||
|
var map = GetNavigationMap();
|
||||||
|
if (!map.IsValid)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 强制刷新一次导航数据,确保首帧同步
|
||||||
|
NavigationServer2D.MapForceUpdate(map);
|
||||||
|
if (NavigationServer2D.MapGetIterationId(map) == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SpawnStudents(map);
|
||||||
|
_spawnPending = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SpawnStudents(Rid map)
|
||||||
|
{
|
||||||
|
_coveragePoints.Clear();
|
||||||
|
_coveragePoints.AddRange(BuildCoveragePoints(map));
|
||||||
|
if (_coveragePoints.Count == 0)
|
||||||
|
{
|
||||||
|
GD.PushWarning("未采样到可行走区域,跳过学生生成。");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < StudentCount; i++)
|
||||||
|
{
|
||||||
|
var instance = StudentScene.Instantiate();
|
||||||
|
if (instance is not CampusStudent student)
|
||||||
|
{
|
||||||
|
GD.PushWarning("student_16_native.tscn 需要挂载 CampusStudent 脚本。");
|
||||||
|
instance.QueueFree();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
_studentsRoot.AddChild(student);
|
||||||
|
student.Name = $"CampusStudent_{i + 1}";
|
||||||
|
|
||||||
|
// 随机放置在可行走区域,并设置不同的巡游起点
|
||||||
|
var randomIndex = GD.RandRange(0, _coveragePoints.Count - 1);
|
||||||
|
student.GlobalPosition = _coveragePoints[randomIndex];
|
||||||
|
student.ConfigurePatrol(_coveragePoints, i * 7);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Vector2> BuildCoveragePoints(Rid map)
|
||||||
|
{
|
||||||
|
var points = new List<Vector2>();
|
||||||
|
var polygon = _navigationRegion.NavigationPolygon;
|
||||||
|
if (polygon == null || polygon.Vertices.Length == 0)
|
||||||
|
{
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据导航多边形顶点计算采样范围
|
||||||
|
var min = polygon.Vertices[0];
|
||||||
|
var max = polygon.Vertices[0];
|
||||||
|
foreach (var v in polygon.Vertices)
|
||||||
|
{
|
||||||
|
min = new Vector2(Mathf.Min(min.X, v.X), Mathf.Min(min.Y, v.Y));
|
||||||
|
max = new Vector2(Mathf.Max(max.X, v.X), Mathf.Max(max.Y, v.Y));
|
||||||
|
}
|
||||||
|
|
||||||
|
var step = Mathf.Max(8.0f, CoverageStep);
|
||||||
|
var minDistance = step * 0.45f;
|
||||||
|
for (float x = min.X; x <= max.X; x += step)
|
||||||
|
{
|
||||||
|
for (float y = min.Y; y <= max.Y; y += step)
|
||||||
|
{
|
||||||
|
var local = new Vector2(x, y);
|
||||||
|
var global = _navigationRegion.ToGlobal(local);
|
||||||
|
var closest = NavigationServer2D.MapGetClosestPoint(map, global);
|
||||||
|
if (closest.DistanceTo(global) > minDistance)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!HasNearbyPoint(points, closest, minDistance))
|
||||||
|
{
|
||||||
|
points.Add(closest);
|
||||||
|
if (points.Count >= MaxCoveragePoints)
|
||||||
|
{
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 兜底:至少给一个可行走点
|
||||||
|
if (points.Count == 0)
|
||||||
|
{
|
||||||
|
var centerLocal = (min + max) * 0.5f;
|
||||||
|
var centerGlobal = _navigationRegion.ToGlobal(centerLocal);
|
||||||
|
points.Add(NavigationServer2D.MapGetClosestPoint(map, centerGlobal));
|
||||||
|
}
|
||||||
|
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Rid GetNavigationMap()
|
||||||
|
{
|
||||||
|
var map = NavigationServer2D.RegionGetMap(_navigationRegion.GetRid());
|
||||||
|
if (!map.IsValid)
|
||||||
|
{
|
||||||
|
var world = GetWorld2D();
|
||||||
|
if (world != null)
|
||||||
|
{
|
||||||
|
map = world.NavigationMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool HasNearbyPoint(List<Vector2> points, Vector2 candidate, float minDistance)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < points.Count; i++)
|
||||||
|
{
|
||||||
|
if (points[i].DistanceTo(candidate) <= minDistance)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
[gd_scene load_steps=8 format=3 uid="uid://b0cu4fa7vohmw"]
|
[gd_scene load_steps=9 format=3 uid="uid://b0cu4fa7vohmw"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://ew4ig6hnrsau" path="res://scenes/CampusController.cs" id="1_controller"]
|
[ext_resource type="Script" uid="uid://ew4ig6hnrsau" path="res://scenes/CampusController.cs" id="1_controller"]
|
||||||
[ext_resource type="PackedScene" uid="uid://cf6b1t8pujosf" path="res://scenes/ui-elements/log_panel.tscn" id="1_hi2p7"]
|
[ext_resource type="PackedScene" uid="uid://cf6b1t8pujosf" path="res://scenes/ui-elements/log_panel.tscn" id="1_hi2p7"]
|
||||||
@ -6,6 +6,15 @@
|
|||||||
[ext_resource type="PackedScene" uid="uid://db2qcx61nc0q4" path="res://scenes/ui-elements/top-bar.tscn" id="2_p4tmp"]
|
[ext_resource type="PackedScene" uid="uid://db2qcx61nc0q4" path="res://scenes/ui-elements/top-bar.tscn" id="2_p4tmp"]
|
||||||
[ext_resource type="PackedScene" uid="uid://drmjsqoy8htc8" path="res://scenes/ui-elements/task_list.tscn" id="3_4gjr3"]
|
[ext_resource type="PackedScene" uid="uid://drmjsqoy8htc8" path="res://scenes/ui-elements/task_list.tscn" id="3_4gjr3"]
|
||||||
|
|
||||||
|
[sub_resource type="NavigationPolygon" id="NavigationPolygon_8u8vn"]
|
||||||
|
vertices = PackedVector2Array(960, 544, 0, 544, 416, 512, 464, 512, 384, 512, 384, 464, 176, 496, 304, 464, 144, 496, 200, 352, 200, 264, 232, 304, 232, 464, 232, 288, 144, 208, 144, 192, 160, 192, 160, 208, 240, 208, 240, 16, 272, 16, 272, 232, 432, 232, 432, 192, 448, 192, 448, 232, 608, 232, 608, 16, 640, 16, 640, 240, 704, 240, 704, 192, 728, 192, 728, 168, 760, 168, 760, 192, 768, 192, 768, 16, 800, 16, 800, 224, 840, 224, 840, 192, 856, 192, 856, 224, 864, 224, 864, 288, 832, 288, 832, 224, 800, 288, 912, 304, 912, 288, 936, 288, 952, 336, 936, 184, 952, 184, 832, 336, 832, 304, 832, 512, 800, 512, 576, 288, 576, 304, 544, 304, 544, 288, 960, 512, 120, 360, 128, 360, 128, 400, 192, 400, 192, 352, 288, 464, 288, 448, 304, 448, 176, 464, 144, 464, 0, 496, 96, 496, 96, 288, 120, 264, 0, 288, 0, 0, 32, 0, 32, 208, 416, 288, 464, 288)
|
||||||
|
polygons = Array[PackedInt32Array]([PackedInt32Array(0, 1, 2, 3), PackedInt32Array(4, 2, 1), PackedInt32Array(5, 4, 1, 6, 7), PackedInt32Array(8, 6, 1), PackedInt32Array(9, 10, 11, 12), PackedInt32Array(13, 11, 10), PackedInt32Array(14, 15, 16, 17), PackedInt32Array(18, 19, 20, 21), PackedInt32Array(22, 23, 24, 25), PackedInt32Array(26, 27, 28, 29), PackedInt32Array(30, 31, 32), PackedInt32Array(32, 33, 34, 35), PackedInt32Array(36, 37, 38, 39), PackedInt32Array(40, 41, 42, 43), PackedInt32Array(43, 44, 45, 46), PackedInt32Array(40, 43, 46), PackedInt32Array(47, 40, 46), PackedInt32Array(39, 47, 46, 48), PackedInt32Array(49, 50, 51, 52), PackedInt32Array(51, 53, 54, 52), PackedInt32Array(49, 52, 55, 56), PackedInt32Array(55, 57, 58, 48), PackedInt32Array(56, 55, 48), PackedInt32Array(46, 56, 48), PackedInt32Array(39, 48, 59, 30), PackedInt32Array(59, 60, 61, 62), PackedInt32Array(57, 63, 0), PackedInt32Array(64, 65, 66), PackedInt32Array(67, 68, 9, 12), PackedInt32Array(69, 70, 71, 7), PackedInt32Array(69, 7, 6), PackedInt32Array(12, 69, 6), PackedInt32Array(12, 6, 72, 67), PackedInt32Array(66, 67, 72, 73), PackedInt32Array(1, 74, 75), PackedInt32Array(8, 1, 75), PackedInt32Array(73, 8, 75, 76, 66), PackedInt32Array(64, 66, 76), PackedInt32Array(77, 64, 76), PackedInt32Array(78, 79, 80, 81), PackedInt32Array(76, 78, 81, 14, 77), PackedInt32Array(10, 77, 14, 17), PackedInt32Array(10, 17, 18, 21, 13), PackedInt32Array(82, 13, 21, 22), PackedInt32Array(82, 22, 25), PackedInt32Array(30, 32, 35, 39), PackedInt32Array(82, 25, 26, 83), PackedInt32Array(35, 36, 39), PackedInt32Array(29, 30, 59), PackedInt32Array(26, 29, 59, 62), PackedInt32Array(26, 62, 83), PackedInt32Array(82, 83, 3, 2), PackedInt32Array(0, 3, 58), PackedInt32Array(0, 58, 57)])
|
||||||
|
outlines = Array[PackedVector2Array]([PackedVector2Array(0, 0, 32, 0, 32, 208, 144, 208, 144, 192, 160, 192, 160, 208, 240, 208, 240, 16, 272, 16, 272, 232, 432, 232, 432, 192, 448, 192, 448, 232, 608, 232, 608, 16, 640, 16, 640, 240, 704, 240, 704, 192, 728, 192, 728, 168, 760, 168, 760, 192, 768, 192, 768, 16, 800, 16, 800, 224, 832, 224, 840, 224, 840, 192, 856, 192, 856, 224, 864, 224, 864, 288, 832, 288, 832, 304, 912, 304, 912, 288, 936, 288, 936, 184, 952, 184, 952, 336, 832, 336, 832, 512, 960, 512, 960, 544, 0, 544, 0, 496, 96, 496, 96, 288, 0, 288), PackedVector2Array(144, 496, 176, 496, 176, 464, 144, 464), PackedVector2Array(128, 400, 192, 400, 192, 352, 200, 352, 200, 264, 120, 264, 120, 360, 128, 360), PackedVector2Array(232, 304, 232, 464, 288, 464, 288, 448, 304, 448, 304, 464, 384, 464, 384, 512, 416, 512, 416, 288, 232, 288), PackedVector2Array(464, 288, 464, 512, 800, 512, 800, 288, 576, 288, 576, 304, 544, 304, 544, 288), PackedVector2Array(144, 416, 176, 416, 176, 448, 144, 448)])
|
||||||
|
parsed_geometry_type = 1
|
||||||
|
parsed_collision_mask = 4294967294
|
||||||
|
source_geometry_mode = 1
|
||||||
|
agent_radius = 8.0
|
||||||
|
|
||||||
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_4gjr3"]
|
[sub_resource type="TileSetAtlasSource" id="TileSetAtlasSource_4gjr3"]
|
||||||
texture = ExtResource("1_p4tmp")
|
texture = ExtResource("1_p4tmp")
|
||||||
use_texture_padding = false
|
use_texture_padding = false
|
||||||
@ -2000,16 +2009,22 @@ script = ExtResource("1_controller")
|
|||||||
texture = ExtResource("1_p4tmp")
|
texture = ExtResource("1_p4tmp")
|
||||||
centered = false
|
centered = false
|
||||||
|
|
||||||
|
[node name="NavigationRegion2D" type="NavigationRegion2D" parent="Sprite2D"]
|
||||||
|
navigation_polygon = SubResource("NavigationPolygon_8u8vn")
|
||||||
|
|
||||||
[node name="Log" parent="." instance=ExtResource("1_hi2p7")]
|
[node name="Log" parent="." instance=ExtResource("1_hi2p7")]
|
||||||
|
visible = false
|
||||||
offset_left = 655.0
|
offset_left = 655.0
|
||||||
offset_top = 375.0
|
offset_top = 375.0
|
||||||
offset_right = 955.0
|
offset_right = 955.0
|
||||||
offset_bottom = 535.0
|
offset_bottom = 535.0
|
||||||
|
|
||||||
[node name="TopBar" parent="." instance=ExtResource("2_p4tmp")]
|
[node name="TopBar" parent="." instance=ExtResource("2_p4tmp")]
|
||||||
|
visible = false
|
||||||
offset_bottom = 55.0
|
offset_bottom = 55.0
|
||||||
|
|
||||||
[node name="Task" parent="." instance=ExtResource("3_4gjr3")]
|
[node name="Task" parent="." instance=ExtResource("3_4gjr3")]
|
||||||
|
visible = false
|
||||||
offset_left = 4.0
|
offset_left = 4.0
|
||||||
offset_top = 216.0
|
offset_top = 216.0
|
||||||
offset_right = 305.0
|
offset_right = 305.0
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[gd_scene load_steps=23 format=3 uid="uid://bmxk4pi4sd1rd"]
|
[gd_scene load_steps=23 format=3 uid="uid://bmxk4pi4sd1rd"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://bcy30mq13r02a" path="res://scripts/Student.cs" id="1_oesea"]
|
[ext_resource type="Script" uid="uid://d2m5sak3n7q1s" path="res://scripts/CampusStudent.cs" id="1_oesea"]
|
||||||
[ext_resource type="Texture2D" uid="uid://ckjn1gxbn26iq" path="res://resources/characters/bodies/Body_01.png" id="3_jhea4"]
|
[ext_resource type="Texture2D" uid="uid://ckjn1gxbn26iq" path="res://resources/characters/bodies/Body_01.png" id="3_jhea4"]
|
||||||
[ext_resource type="Script" uid="uid://bp2483mr5lay" path="res://scripts/StudentName.cs" id="8_kvqca"]
|
[ext_resource type="Script" uid="uid://bp2483mr5lay" path="res://scripts/StudentName.cs" id="8_kvqca"]
|
||||||
[ext_resource type="Texture2D" uid="uid://bvngikeasxn3n" path="res://resources/characters/accessories/Accessory_06_Policeman_Hat_01.png" id="30_npp82"]
|
[ext_resource type="Texture2D" uid="uid://bvngikeasxn3n" path="res://resources/characters/accessories/Accessory_06_Policeman_Hat_01.png" id="30_npp82"]
|
||||||
@ -10,7 +10,7 @@
|
|||||||
[ext_resource type="Texture2D" uid="uid://bscmf27jf2qf0" path="res://resources/characters/smartphones/Smartphone_1.png" id="435_7vwql"]
|
[ext_resource type="Texture2D" uid="uid://bscmf27jf2qf0" path="res://resources/characters/smartphones/Smartphone_1.png" id="435_7vwql"]
|
||||||
|
|
||||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_opr6h"]
|
[sub_resource type="RectangleShape2D" id="RectangleShape2D_opr6h"]
|
||||||
size = Vector2(16, 32)
|
size = Vector2(16, 16)
|
||||||
|
|
||||||
[sub_resource type="Animation" id="Animation_f0f87"]
|
[sub_resource type="Animation" id="Animation_f0f87"]
|
||||||
length = 0.001
|
length = 0.001
|
||||||
@ -877,10 +877,8 @@ _data = {
|
|||||||
[node name="Student" type="CharacterBody2D"]
|
[node name="Student" type="CharacterBody2D"]
|
||||||
z_index = 2
|
z_index = 2
|
||||||
script = ExtResource("1_oesea")
|
script = ExtResource("1_oesea")
|
||||||
Use16x16Sprites = true
|
|
||||||
|
|
||||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
||||||
position = Vector2(0, -8)
|
|
||||||
shape = SubResource("RectangleShape2D_opr6h")
|
shape = SubResource("RectangleShape2D_opr6h")
|
||||||
|
|
||||||
[node name="parts" type="Node2D" parent="."]
|
[node name="parts" type="Node2D" parent="."]
|
||||||
@ -935,3 +933,11 @@ autoplay = "RESET"
|
|||||||
|
|
||||||
[node name="StudentName" type="Node" parent="."]
|
[node name="StudentName" type="Node" parent="."]
|
||||||
script = ExtResource("8_kvqca")
|
script = ExtResource("8_kvqca")
|
||||||
|
|
||||||
|
[node name="NavigationAgent2D" type="NavigationAgent2D" parent="."]
|
||||||
|
path_desired_distance = 4.0
|
||||||
|
target_desired_distance = 4.0
|
||||||
|
path_max_distance = 5000.0
|
||||||
|
path_postprocessing = 1
|
||||||
|
radius = 8.0
|
||||||
|
debug_enabled = true
|
||||||
|
|||||||
246
scripts/CampusStudent.cs
Normal file
246
scripts/CampusStudent.cs
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using Godot;
|
||||||
|
|
||||||
|
public partial class CampusStudent : CharacterBody2D
|
||||||
|
{
|
||||||
|
private Sprite2D _accessory;
|
||||||
|
private AnimationPlayer _animationPlayer;
|
||||||
|
|
||||||
|
private Sprite2D _body;
|
||||||
|
private Sprite2D _eye;
|
||||||
|
private Sprite2D _hairstyle;
|
||||||
|
private double _lastDelta;
|
||||||
|
private FacingDirection _lastFacing = FacingDirection.Down;
|
||||||
|
private Vector2 _lastPosition;
|
||||||
|
|
||||||
|
private NavigationAgent2D _navigationAgent;
|
||||||
|
private Sprite2D _outfit;
|
||||||
|
private bool _patrolConfigured;
|
||||||
|
private int _patrolIndex;
|
||||||
|
private List<Vector2> _patrolPoints = new();
|
||||||
|
private Sprite2D _smartphone;
|
||||||
|
private float _stuckTimer;
|
||||||
|
[Export] public float MoveSpeed { get; set; } = 60.0f;
|
||||||
|
[Export] public float TargetReachDistance { get; set; } = 6.0f;
|
||||||
|
[Export] public bool Use16X16Sprites { get; set; } = true;
|
||||||
|
[Export] public bool EnableAvoidance { get; set; } = true;
|
||||||
|
[Export] public float StuckRepathSeconds { get; set; } = 0.6f;
|
||||||
|
[Export] public float StuckDistanceEpsilon { get; set; } = 2.0f;
|
||||||
|
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
_navigationAgent = GetNodeOrNull<NavigationAgent2D>("NavigationAgent2D");
|
||||||
|
_animationPlayer = GetNodeOrNull<AnimationPlayer>("AnimationPlayer");
|
||||||
|
CacheSprites();
|
||||||
|
|
||||||
|
if (_navigationAgent != null)
|
||||||
|
{
|
||||||
|
// 让寻路点更“贴近目标”,避免走到边缘时抖动
|
||||||
|
_navigationAgent.PathDesiredDistance = TargetReachDistance;
|
||||||
|
_navigationAgent.TargetDesiredDistance = TargetReachDistance;
|
||||||
|
_navigationAgent.AvoidanceEnabled = EnableAvoidance;
|
||||||
|
|
||||||
|
if (EnableAvoidance)
|
||||||
|
// 开启避让时使用安全速度回调进行移动
|
||||||
|
_navigationAgent.VelocityComputed += OnVelocityComputed;
|
||||||
|
|
||||||
|
if (_patrolConfigured) AdvanceTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayIdleAnimation();
|
||||||
|
_lastPosition = GlobalPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void _PhysicsProcess(double delta)
|
||||||
|
{
|
||||||
|
_lastDelta = delta;
|
||||||
|
if (_navigationAgent == null || _patrolPoints.Count == 0)
|
||||||
|
{
|
||||||
|
PlayIdleAnimation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 到达目标点或无路可走时,切换到下一个巡游点
|
||||||
|
if (_navigationAgent.IsNavigationFinished() || _navigationAgent.DistanceToTarget() <= TargetReachDistance)
|
||||||
|
AdvanceTarget();
|
||||||
|
|
||||||
|
var nextPosition = _navigationAgent.GetNextPathPosition();
|
||||||
|
var toNext = nextPosition - GlobalPosition;
|
||||||
|
if (toNext.LengthSquared() < 0.01f)
|
||||||
|
{
|
||||||
|
if (!EnableAvoidance)
|
||||||
|
{
|
||||||
|
Velocity = Vector2.Zero;
|
||||||
|
MoveAndSlide();
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayIdleAnimation();
|
||||||
|
UpdateStuckTimer(delta);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var direction = toNext.Normalized();
|
||||||
|
var desiredVelocity = direction * MoveSpeed;
|
||||||
|
|
||||||
|
if (EnableAvoidance)
|
||||||
|
{
|
||||||
|
// 交给导航系统做群体避让
|
||||||
|
_navigationAgent.Velocity = desiredVelocity;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 未启用避让时直接移动
|
||||||
|
Velocity = desiredVelocity;
|
||||||
|
MoveAndSlide();
|
||||||
|
UpdateFacingAnimation(Velocity);
|
||||||
|
UpdateStuckTimer(delta);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ConfigurePatrol(List<Vector2> points, int startIndex)
|
||||||
|
{
|
||||||
|
_patrolPoints = points ?? new List<Vector2>();
|
||||||
|
if (_patrolPoints.Count == 0) return;
|
||||||
|
|
||||||
|
_patrolIndex = (startIndex % _patrolPoints.Count + _patrolPoints.Count) % _patrolPoints.Count;
|
||||||
|
_patrolConfigured = true;
|
||||||
|
ApplyRandomTheme();
|
||||||
|
|
||||||
|
if (_navigationAgent != null) AdvanceTarget();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ApplyRandomTheme()
|
||||||
|
{
|
||||||
|
// 随机替换身体与配件贴图,形成不同主题外观
|
||||||
|
if (_body == null) CacheSprites();
|
||||||
|
|
||||||
|
Debug.Assert(_body != null, nameof(_body) + " != null");
|
||||||
|
_body.Texture = ResourceLoader.Load<Texture2D>(Res.GetRandom(Res.Type.Body, Use16X16Sprites));
|
||||||
|
_hairstyle.Texture = ResourceLoader.Load<Texture2D>(Res.GetRandom(Res.Type.Hair, Use16X16Sprites));
|
||||||
|
_outfit.Texture = ResourceLoader.Load<Texture2D>(Res.GetRandom(Res.Type.Outfit, Use16X16Sprites));
|
||||||
|
_eye.Texture = ResourceLoader.Load<Texture2D>(Res.GetRandom(Res.Type.Eye, Use16X16Sprites));
|
||||||
|
_accessory.Texture = ResourceLoader.Load<Texture2D>(Res.GetRandom(Res.Type.Accessory, Use16X16Sprites));
|
||||||
|
_smartphone.Texture = ResourceLoader.Load<Texture2D>(Res.GetRandom(Res.Type.Phone, Use16X16Sprites));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CacheSprites()
|
||||||
|
{
|
||||||
|
// 缓存子节点引用,避免每帧查找
|
||||||
|
_body = GetNode<Sprite2D>("parts/body");
|
||||||
|
_hairstyle = GetNode<Sprite2D>("parts/hairstyle");
|
||||||
|
_outfit = GetNode<Sprite2D>("parts/outfit");
|
||||||
|
_eye = GetNode<Sprite2D>("parts/eye");
|
||||||
|
_accessory = GetNode<Sprite2D>("parts/accessory");
|
||||||
|
_smartphone = GetNode<Sprite2D>("parts/smartphone");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AdvanceTarget()
|
||||||
|
{
|
||||||
|
if (_patrolPoints.Count == 0 || _navigationAgent == null) return;
|
||||||
|
|
||||||
|
// 避免当前点过近导致原地抖动,最多尝试一轮
|
||||||
|
for (var i = 0; i < _patrolPoints.Count; i++)
|
||||||
|
{
|
||||||
|
var target = _patrolPoints[_patrolIndex];
|
||||||
|
_patrolIndex = (_patrolIndex + 1) % _patrolPoints.Count;
|
||||||
|
if (GlobalPosition.DistanceTo(target) > TargetReachDistance * 1.5f)
|
||||||
|
{
|
||||||
|
_navigationAgent.TargetPosition = target;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_navigationAgent.TargetPosition = _patrolPoints[_patrolIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnVelocityComputed(Vector2 safeVelocity)
|
||||||
|
{
|
||||||
|
// 使用安全速度移动,避免与其它角色硬碰硬卡住
|
||||||
|
Velocity = safeVelocity;
|
||||||
|
MoveAndSlide();
|
||||||
|
UpdateFacingAnimation(Velocity);
|
||||||
|
UpdateStuckTimer(_lastDelta);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateStuckTimer(double delta)
|
||||||
|
{
|
||||||
|
// 若短时间内几乎没有位移,则换一个目标点脱困
|
||||||
|
if (GlobalPosition.DistanceTo(_lastPosition) <= StuckDistanceEpsilon)
|
||||||
|
_stuckTimer += (float)delta;
|
||||||
|
else
|
||||||
|
_stuckTimer = 0.0f;
|
||||||
|
|
||||||
|
_lastPosition = GlobalPosition;
|
||||||
|
|
||||||
|
if (_stuckTimer >= StuckRepathSeconds)
|
||||||
|
{
|
||||||
|
_stuckTimer = 0.0f;
|
||||||
|
AdvanceTarget();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateFacingAnimation(Vector2 velocity)
|
||||||
|
{
|
||||||
|
if (velocity.LengthSquared() < 0.01f)
|
||||||
|
{
|
||||||
|
PlayIdleAnimation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Mathf.Abs(velocity.X) >= Mathf.Abs(velocity.Y))
|
||||||
|
_lastFacing = velocity.X >= 0 ? FacingDirection.Right : FacingDirection.Left;
|
||||||
|
else
|
||||||
|
_lastFacing = velocity.Y >= 0 ? FacingDirection.Down : FacingDirection.Up;
|
||||||
|
|
||||||
|
switch (_lastFacing)
|
||||||
|
{
|
||||||
|
case FacingDirection.Left:
|
||||||
|
PlayAnimation("walk_left");
|
||||||
|
break;
|
||||||
|
case FacingDirection.Right:
|
||||||
|
PlayAnimation("walk_right");
|
||||||
|
break;
|
||||||
|
case FacingDirection.Up:
|
||||||
|
PlayAnimation("walk_up");
|
||||||
|
break;
|
||||||
|
case FacingDirection.Down:
|
||||||
|
PlayAnimation("walk_down");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PlayIdleAnimation()
|
||||||
|
{
|
||||||
|
switch (_lastFacing)
|
||||||
|
{
|
||||||
|
case FacingDirection.Left:
|
||||||
|
PlayAnimation("idle_left");
|
||||||
|
break;
|
||||||
|
case FacingDirection.Right:
|
||||||
|
PlayAnimation("idle_right");
|
||||||
|
break;
|
||||||
|
case FacingDirection.Up:
|
||||||
|
PlayAnimation("idle_back");
|
||||||
|
break;
|
||||||
|
case FacingDirection.Down:
|
||||||
|
PlayAnimation("idle_front");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PlayAnimation(string animationName)
|
||||||
|
{
|
||||||
|
if (_animationPlayer == null) return;
|
||||||
|
|
||||||
|
if (_animationPlayer.CurrentAnimation != animationName) _animationPlayer.Play(animationName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum FacingDirection
|
||||||
|
{
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
Left,
|
||||||
|
Right
|
||||||
|
}
|
||||||
|
}
|
||||||
1
scripts/CampusStudent.cs.uid
Normal file
1
scripts/CampusStudent.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://d2m5r9k3n7q1s
|
||||||
@ -86,7 +86,7 @@ public partial class Cube : StaticBody2D, ITileDraggable
|
|||||||
}
|
}
|
||||||
|
|
||||||
private readonly Guid _id = Guid.NewGuid();
|
private readonly Guid _id = Guid.NewGuid();
|
||||||
public Guid ID => _id;
|
public Guid Id => _id;
|
||||||
|
|
||||||
public Vector2I TilePosition { get; set; } = new Vector2I(5,5);
|
public Vector2I TilePosition { get; set; } = new Vector2I(5,5);
|
||||||
private bool _isCollided = true;
|
private bool _isCollided = true;
|
||||||
@ -102,19 +102,19 @@ public partial class Cube : StaticBody2D, ITileDraggable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private static readonly Rect2I _tileRect = new(-1, -2, 4, 5);
|
private static readonly Rect2I tileRect = new(-1, -2, 4, 5);
|
||||||
public Rect2I TileRect => _tileRect;
|
public Rect2I TileRect => tileRect;
|
||||||
|
|
||||||
private Vector2I _mouseOffset;
|
private Vector2I _mouseOffset;
|
||||||
public Vector2I MouseOffset => _mouseOffset;
|
public Vector2I MouseOffset => _mouseOffset;
|
||||||
|
|
||||||
private static readonly ITileDraggable.SpecialTile[] _specialTiles = {
|
private static readonly ITileDraggable.SpecialTile[] specialTiles = {
|
||||||
new(new Vector2I(1,-1), Lab.MapNodeType.SeatDown | Lab.MapNodeType.Walkable),
|
new(new Vector2I(1,-1), Lab.MapNodeType.SeatDown | Lab.MapNodeType.Walkable),
|
||||||
new(new Vector2I(1,2), Lab.MapNodeType.SeatUp | Lab.MapNodeType.Walkable),
|
new(new Vector2I(1,2), Lab.MapNodeType.SeatUp | Lab.MapNodeType.Walkable),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
public ITileDraggable.SpecialTile[] SpecialTiles => _specialTiles;
|
public ITileDraggable.SpecialTile[] SpecialTiles => specialTiles;
|
||||||
|
|
||||||
// Called when the node enters the scene tree for the first time.
|
// Called when the node enters the scene tree for the first time.
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
@ -147,27 +147,27 @@ public partial class Cube : StaticBody2D, ITileDraggable
|
|||||||
GlobalPosition = TilePosition * 48;
|
GlobalPosition = TilePosition * 48;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int tableThemeIdx;
|
private int _tableThemeIdx;
|
||||||
private int table2ThemeIdx;
|
private int _table2ThemeIdx;
|
||||||
private int chairThemeIdx;
|
private int _chairThemeIdx;
|
||||||
private int chair2ThemeIdx;
|
private int _chair2ThemeIdx;
|
||||||
private int equipThemeIdx;
|
private int _equipThemeIdx;
|
||||||
private int equip2ThemeIdx;
|
private int _equip2ThemeIdx;
|
||||||
public void RandomChangeTheme() {
|
public void RandomChangeTheme() {
|
||||||
tableThemeIdx = GD.RandRange(0, tableThemes.Length-1);
|
_tableThemeIdx = GD.RandRange(0, tableThemes.Length-1);
|
||||||
chairThemeIdx = GD.RandRange(0, chairThemes.Length-1);
|
_chairThemeIdx = GD.RandRange(0, chairThemes.Length-1);
|
||||||
equipThemeIdx = GD.RandRange(0, equipThemes.Length-1);
|
_equipThemeIdx = GD.RandRange(0, equipThemes.Length-1);
|
||||||
|
|
||||||
table2ThemeIdx = GD.RandRange(0, table2Themes.Length-1);
|
_table2ThemeIdx = GD.RandRange(0, table2Themes.Length-1);
|
||||||
chair2ThemeIdx = GD.RandRange(0, chair2Themes.Length-1);
|
_chair2ThemeIdx = GD.RandRange(0, chair2Themes.Length-1);
|
||||||
equip2ThemeIdx = GD.RandRange(0, equip2Themes.Length-1);
|
_equip2ThemeIdx = GD.RandRange(0, equip2Themes.Length-1);
|
||||||
|
|
||||||
tableThemes[tableThemeIdx].Apply(GetNode<TileMapLayer>("Desk"));
|
tableThemes[_tableThemeIdx].Apply(GetNode<TileMapLayer>("Desk"));
|
||||||
chairThemes[chairThemeIdx].Apply(GetNode<TileMapLayer>("Chair"));
|
chairThemes[_chairThemeIdx].Apply(GetNode<TileMapLayer>("Chair"));
|
||||||
equipThemes[equipThemeIdx].Apply(GetNode<TileMapLayer>("Equip"));
|
equipThemes[_equipThemeIdx].Apply(GetNode<TileMapLayer>("Equip"));
|
||||||
table2Themes[table2ThemeIdx].Apply(GetNode<TileMapLayer>("Desk2"));
|
table2Themes[_table2ThemeIdx].Apply(GetNode<TileMapLayer>("Desk2"));
|
||||||
chair2Themes[chair2ThemeIdx].Apply(GetNode<TileMapLayer>("Chair2"));
|
chair2Themes[_chair2ThemeIdx].Apply(GetNode<TileMapLayer>("Chair2"));
|
||||||
equip2Themes[equip2ThemeIdx].Apply(GetNode<TileMapLayer>("Equip2"));
|
equip2Themes[_equip2ThemeIdx].Apply(GetNode<TileMapLayer>("Equip2"));
|
||||||
}
|
}
|
||||||
private static readonly HashSet<Vector2I> blockerTiles = new(new Vector2I[]{
|
private static readonly HashSet<Vector2I> blockerTiles = new(new Vector2I[]{
|
||||||
new(0,0),
|
new(0,0),
|
||||||
@ -183,15 +183,15 @@ public partial class Cube : StaticBody2D, ITileDraggable
|
|||||||
if (blockerTiles.Contains(pos)) {
|
if (blockerTiles.Contains(pos)) {
|
||||||
return Lab.MapNodeType.Blocker;
|
return Lab.MapNodeType.Blocker;
|
||||||
}
|
}
|
||||||
if (_specialTiles.Any(t => t.Position == pos)) {
|
if (specialTiles.Any(t => t.Position == pos)) {
|
||||||
return _specialTiles.First(t => t.Position == pos).NodeType;
|
return specialTiles.First(t => t.Position == pos).NodeType;
|
||||||
}
|
}
|
||||||
return Lab.MapNodeType.Walkable;
|
return Lab.MapNodeType.Walkable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ChairFaceTo(Vector2I target, int idx) {
|
public void ChairFaceTo(Vector2I target, int idx) {
|
||||||
if (idx == 0) {
|
if (idx == 0) {
|
||||||
var theme = chairThemes[chairThemeIdx];
|
var theme = chairThemes[_chairThemeIdx];
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
0
scripts/Data/Import/GameDataImporter.cs
Normal file
0
scripts/Data/Import/GameDataImporter.cs
Normal file
1
scripts/Data/Import/GameDataImporter.cs.uid
Normal file
1
scripts/Data/Import/GameDataImporter.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://odcll7t6ojce
|
||||||
@ -10,7 +10,7 @@ public partial class GameManager : Node
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static bool IsTutorial { get; private set; }
|
public static bool IsTutorial { get; private set; }
|
||||||
public static string NextScene { get; set; } = null;
|
public static string NextScene { get; set; } = null;
|
||||||
public static readonly Resource Arrow2x = ResourceLoader.Load("res://temp_res/kenney_ui-pack-space-expansion/PNG/Extra/Double/cursor_f.png");
|
public static readonly Resource Arrow2X = ResourceLoader.Load("res://temp_res/kenney_ui-pack-space-expansion/PNG/Extra/Double/cursor_f.png");
|
||||||
|
|
||||||
// --- Core Loop Definitions ---
|
// --- Core Loop Definitions ---
|
||||||
public enum GamePhase
|
public enum GamePhase
|
||||||
@ -46,7 +46,7 @@ public partial class GameManager : Node
|
|||||||
|
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
{
|
{
|
||||||
Input.SetCustomMouseCursor(Arrow2x);
|
Input.SetCustomMouseCursor(Arrow2X);
|
||||||
|
|
||||||
// MVP Initialization
|
// MVP Initialization
|
||||||
InitializeGame();
|
InitializeGame();
|
||||||
|
|||||||
@ -23,5 +23,5 @@ public interface ITileDraggable {
|
|||||||
}
|
}
|
||||||
Lab.MapNodeType GetTileType(Vector2I pos);
|
Lab.MapNodeType GetTileType(Vector2I pos);
|
||||||
|
|
||||||
Guid ID { get; }
|
Guid Id { get; }
|
||||||
}
|
}
|
||||||
108
scripts/Lab.cs
108
scripts/Lab.cs
@ -22,8 +22,8 @@ public partial class Lab : Node2D
|
|||||||
new(39,2,1,18),
|
new(39,2,1,18),
|
||||||
};
|
};
|
||||||
|
|
||||||
private readonly Dictionary<Guid, ITileDraggable> furnitureIDs = new();
|
private readonly Dictionary<Guid, ITileDraggable> _furnitureIDs = new();
|
||||||
public Dictionary<Guid, ITileDraggable> Furniture => furnitureIDs;
|
public Dictionary<Guid, ITileDraggable> Furniture => _furnitureIDs;
|
||||||
|
|
||||||
// Called when the node enters the scene tree for the first time.
|
// Called when the node enters the scene tree for the first time.
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
@ -38,24 +38,24 @@ public partial class Lab : Node2D
|
|||||||
Player.Timeline.OnDayChanged += d => label.Text = d.ToLongDateString();
|
Player.Timeline.OnDayChanged += d => label.Text = d.ToLongDateString();
|
||||||
var table = GetNode<Cube>("Cube");
|
var table = GetNode<Cube>("Cube");
|
||||||
table.Draggable = true;
|
table.Draggable = true;
|
||||||
MoneyLabel = GetNode<Label>("BottomBar/HBoxContainer/Money");
|
_moneyLabel = GetNode<Label>("BottomBar/HBoxContainer/Money");
|
||||||
|
|
||||||
var rect = table.TileRect;
|
var rect = table.TileRect;
|
||||||
rect.Position += table.TilePosition;
|
rect.Position += table.TilePosition;
|
||||||
furniturePlacement.Add(rect, table);
|
_furniturePlacement.Add(rect, table);
|
||||||
furnitureIDs.Add(table.ID, table);
|
_furnitureIDs.Add(table.Id, table);
|
||||||
|
|
||||||
tileMap = GetNode<TileMapLayer>("OutGround");
|
_tileMap = GetNode<TileMapLayer>("OutGround");
|
||||||
UpdateMap();
|
UpdateMap();
|
||||||
|
|
||||||
var student = GetNode<Student>("Student");
|
var student = GetNode<Student>("Student");
|
||||||
student.CubeId = table.ID;
|
student.CubeId = table.Id;
|
||||||
|
|
||||||
var r = new Rect2I(0,0,1,1);
|
var r = new Rect2I(0,0,1,1);
|
||||||
GD.Print(H.RectHasPointInclusive(r, new Vector2I(1,0)));
|
GD.Print(H.RectHasPointInclusive(r, new Vector2I(1,0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Label MoneyLabel = null;
|
private Label _moneyLabel = null;
|
||||||
// Called every frame. 'delta' is the elapsed time since the previous frame.
|
// Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||||
public override void _Process(double delta)
|
public override void _Process(double delta)
|
||||||
{
|
{
|
||||||
@ -70,7 +70,7 @@ public partial class Lab : Node2D
|
|||||||
Player.Budget.Labor += 100;
|
Player.Budget.Labor += 100;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
MoneyLabel.Text = Player.Budget.Total.ToString("N0");
|
_moneyLabel.Text = Player.Budget.Total.ToString("N0");
|
||||||
if (_isDragging && DraggingTarget != null) {
|
if (_isDragging && DraggingTarget != null) {
|
||||||
Vector2 mousePos = GetLocalMousePosition();
|
Vector2 mousePos = GetLocalMousePosition();
|
||||||
Vector2I cell = Point2Coord(mousePos) - DraggingTarget.MouseOffset;
|
Vector2I cell = Point2Coord(mousePos) - DraggingTarget.MouseOffset;
|
||||||
@ -91,42 +91,42 @@ public partial class Lab : Node2D
|
|||||||
public void PutDown(ITileDraggable target) {
|
public void PutDown(ITileDraggable target) {
|
||||||
if (target == null) return;
|
if (target == null) return;
|
||||||
_isDragging = false;
|
_isDragging = false;
|
||||||
furniturePlacement.Remove(furniturePlacement.First(kv => kv.Value == target).Key);
|
_furniturePlacement.Remove(_furniturePlacement.First(kv => kv.Value == target).Key);
|
||||||
var rect = target.TileRect;
|
var rect = target.TileRect;
|
||||||
rect.Position += target.TilePosition;
|
rect.Position += target.TilePosition;
|
||||||
furniturePlacement.Add(rect, target);
|
_furniturePlacement.Add(rect, target);
|
||||||
DraggingTarget = null;
|
DraggingTarget = null;
|
||||||
UpdateMap();
|
UpdateMap();
|
||||||
}
|
}
|
||||||
public ITileDraggable DraggingTarget { get; set; } = null;
|
public ITileDraggable DraggingTarget { get; set; } = null;
|
||||||
|
|
||||||
public const int MAP_WIDTH = 40;
|
public const int MapWidth = 40;
|
||||||
public const int MAP_HEIGHT = 21;
|
public const int MapHeight = 21;
|
||||||
|
|
||||||
private readonly MapNodeType[,] blocks = new MapNodeType[MAP_WIDTH, MAP_HEIGHT];
|
private readonly MapNodeType[,] _blocks = new MapNodeType[MapWidth, MapHeight];
|
||||||
|
|
||||||
private Dictionary<Rect2I, ITileDraggable> furniturePlacement = new ();
|
private Dictionary<Rect2I, ITileDraggable> _furniturePlacement = new ();
|
||||||
|
|
||||||
private TileMapLayer tileMap;
|
private TileMapLayer _tileMap;
|
||||||
|
|
||||||
public void UpdateMap()
|
public void UpdateMap()
|
||||||
{
|
{
|
||||||
for (int i = 0; i < MAP_WIDTH; i++) {
|
for (int i = 0; i < MapWidth; i++) {
|
||||||
for (int j = 0; j < MAP_HEIGHT; j++) {
|
for (int j = 0; j < MapHeight; j++) {
|
||||||
Vector2I vec = new (i,j);
|
Vector2I vec = new (i,j);
|
||||||
if (wallRectangles.Any(w => w.HasPoint(vec))) {
|
if (wallRectangles.Any(w => w.HasPoint(vec))) {
|
||||||
blocks[i, j] = MapNodeType.Wall;
|
_blocks[i, j] = MapNodeType.Wall;
|
||||||
} else if (furniturePlacement.Any(f => f.Key.HasPoint(vec))) {
|
} else if (_furniturePlacement.Any(f => f.Key.HasPoint(vec))) {
|
||||||
MapNodeType t = 0;
|
MapNodeType t = 0;
|
||||||
foreach (var kv in furniturePlacement.Where(f => f.Key.HasPoint(vec))) {
|
foreach (var kv in _furniturePlacement.Where(f => f.Key.HasPoint(vec))) {
|
||||||
t |= kv.Value.GetTileType(vec - kv.Value.TilePosition);
|
t |= kv.Value.GetTileType(vec - kv.Value.TilePosition);
|
||||||
}
|
}
|
||||||
if ((t & MapNodeType.Walkable) != 0 && (t & MapNodeType.Blocker) != 0){
|
if ((t & MapNodeType.Walkable) != 0 && (t & MapNodeType.Blocker) != 0){
|
||||||
t ^= MapNodeType.Walkable;
|
t ^= MapNodeType.Walkable;
|
||||||
}
|
}
|
||||||
blocks[i ,j] = t;
|
_blocks[i ,j] = t;
|
||||||
} else {
|
} else {
|
||||||
blocks[i, j] = MapNodeType.Walkable;
|
_blocks[i, j] = MapNodeType.Walkable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -138,7 +138,7 @@ public partial class Lab : Node2D
|
|||||||
{
|
{
|
||||||
int x = pos.X;
|
int x = pos.X;
|
||||||
int y = pos.Y;
|
int y = pos.Y;
|
||||||
if (x < 0 || x >= MAP_WIDTH || y < 0 || y >= MAP_HEIGHT) {
|
if (x < 0 || x >= MapWidth || y < 0 || y >= MapHeight) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -151,16 +151,16 @@ public partial class Lab : Node2D
|
|||||||
return neighbor;
|
return neighbor;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsValidPosition(new Vector2I(x-1,y)) && (blocks[x-1,y] & MapNodeType.Walkable) == MapNodeType.Walkable) {
|
if (IsValidPosition(new Vector2I(x-1,y)) && (_blocks[x-1,y] & MapNodeType.Walkable) == MapNodeType.Walkable) {
|
||||||
neighbor.Add(new Vector2I(x-1,y));
|
neighbor.Add(new Vector2I(x-1,y));
|
||||||
}
|
}
|
||||||
if (IsValidPosition(new Vector2I(x+1,y)) && (blocks[x+1,y] & MapNodeType.Walkable) == MapNodeType.Walkable) {
|
if (IsValidPosition(new Vector2I(x+1,y)) && (_blocks[x+1,y] & MapNodeType.Walkable) == MapNodeType.Walkable) {
|
||||||
neighbor.Add(new Vector2I(x+1,y));
|
neighbor.Add(new Vector2I(x+1,y));
|
||||||
}
|
}
|
||||||
if (IsValidPosition(new Vector2I(x,y-1)) && (blocks[x,y-1] & MapNodeType.Walkable) == MapNodeType.Walkable) {
|
if (IsValidPosition(new Vector2I(x,y-1)) && (_blocks[x,y-1] & MapNodeType.Walkable) == MapNodeType.Walkable) {
|
||||||
neighbor.Add(new Vector2I(x,y-1));
|
neighbor.Add(new Vector2I(x,y-1));
|
||||||
}
|
}
|
||||||
if (IsValidPosition(new Vector2I(x,y+1)) && (blocks[x,y+1] & MapNodeType.Walkable) == MapNodeType.Walkable) {
|
if (IsValidPosition(new Vector2I(x,y+1)) && (_blocks[x,y+1] & MapNodeType.Walkable) == MapNodeType.Walkable) {
|
||||||
neighbor.Add(new Vector2I(x,y+1));
|
neighbor.Add(new Vector2I(x,y+1));
|
||||||
}
|
}
|
||||||
return neighbor;
|
return neighbor;
|
||||||
@ -168,57 +168,57 @@ public partial class Lab : Node2D
|
|||||||
|
|
||||||
public List<Vector2I> GetShortestPath(Vector2I start, Vector2I end)
|
public List<Vector2I> GetShortestPath(Vector2I start, Vector2I end)
|
||||||
{
|
{
|
||||||
for (int j = 0; j < MAP_HEIGHT; j++) {
|
for (int j = 0; j < MapHeight; j++) {
|
||||||
string t = "";
|
string t = "";
|
||||||
for (int i = 0; i < MAP_WIDTH; i++) {
|
for (int i = 0; i < MapWidth; i++) {
|
||||||
if ((blocks[i,j] & MapNodeType.SeatUp) != 0) {
|
if ((_blocks[i,j] & MapNodeType.SeatUp) != 0) {
|
||||||
t += "U";
|
t += "U";
|
||||||
} else if ((blocks[i,j] & MapNodeType.SeatDown) != 0) {
|
} else if ((_blocks[i,j] & MapNodeType.SeatDown) != 0) {
|
||||||
t += "D";
|
t += "D";
|
||||||
} else {
|
} else {
|
||||||
t += $"{blocks[i,j].ToString()[0]}";
|
t += $"{_blocks[i,j].ToString()[0]}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GD.Print(t);
|
GD.Print(t);
|
||||||
}
|
}
|
||||||
var path = new List<Vector2I>();
|
var path = new List<Vector2I>();
|
||||||
if (tileMap.GetCellSourceId(start) == -1 || tileMap.GetCellSourceId(end) == -1) {
|
if (_tileMap.GetCellSourceId(start) == -1 || _tileMap.GetCellSourceId(end) == -1) {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
if (start == end) {
|
if (start == end) {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
GD.Print($"start = {start}, end = {end}");
|
GD.Print($"start = {start}, end = {end}");
|
||||||
static int vec2idx(Vector2I v) => v.Y * MAP_WIDTH + v.X;
|
static int Vec2Idx(Vector2I v) => v.Y * MapWidth + v.X;
|
||||||
static Vector2I idx2vec2(int i) => new(i % MAP_WIDTH, i / MAP_WIDTH);
|
static Vector2I Idx2Vec2(int i) => new(i % MapWidth, i / MapWidth);
|
||||||
|
|
||||||
HashSet<int> entriesIdx = new();
|
HashSet<int> entriesIdx = new();
|
||||||
foreach (var e in GetNeighbors(end)) {
|
foreach (var e in GetNeighbors(end)) {
|
||||||
entriesIdx.Add(vec2idx(e));
|
entriesIdx.Add(Vec2Idx(e));
|
||||||
}
|
}
|
||||||
if (entriesIdx.Count == 0) {
|
if (entriesIdx.Count == 0) {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use Dijkstra algorithm to find the shortest path
|
// Use Dijkstra algorithm to find the shortest path
|
||||||
int[] dist = new int[MAP_WIDTH * MAP_HEIGHT];
|
int[] dist = new int[MapWidth * MapHeight];
|
||||||
int[] prev = new int[MAP_WIDTH * MAP_HEIGHT];
|
int[] prev = new int[MapWidth * MapHeight];
|
||||||
bool[] visited = new bool[MAP_WIDTH * MAP_HEIGHT];
|
bool[] visited = new bool[MapWidth * MapHeight];
|
||||||
|
|
||||||
for (int i = 0; i < dist.Length; i++) {
|
for (int i = 0; i < dist.Length; i++) {
|
||||||
dist[i] = int.MaxValue;
|
dist[i] = int.MaxValue;
|
||||||
prev[i] = -1;
|
prev[i] = -1;
|
||||||
visited[i] = false;
|
visited[i] = false;
|
||||||
}
|
}
|
||||||
dist[vec2idx(start)] = 0;
|
dist[Vec2Idx(start)] = 0;
|
||||||
|
|
||||||
int answer = -1;
|
int answer = -1;
|
||||||
for (int i = 0; i < dist.Length; i++) {
|
for (int i = 0; i < dist.Length; i++) {
|
||||||
int min_distance = int.MaxValue;
|
int minDistance = int.MaxValue;
|
||||||
int u = -1;
|
int u = -1;
|
||||||
for (int j = 0; j < dist.Length; j++) {
|
for (int j = 0; j < dist.Length; j++) {
|
||||||
if (!visited[j] && dist[j] <= min_distance) {
|
if (!visited[j] && dist[j] <= minDistance) {
|
||||||
min_distance = dist[j];
|
minDistance = dist[j];
|
||||||
u = j;
|
u = j;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -230,10 +230,10 @@ public partial class Lab : Node2D
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
visited[u] = true;
|
visited[u] = true;
|
||||||
var neighbors = GetNeighbors(idx2vec2(u));
|
var neighbors = GetNeighbors(Idx2Vec2(u));
|
||||||
int alt = dist[u] + 1;
|
int alt = dist[u] + 1;
|
||||||
foreach (var neighbor in neighbors) {
|
foreach (var neighbor in neighbors) {
|
||||||
int idx = vec2idx(neighbor);
|
int idx = Vec2Idx(neighbor);
|
||||||
if (visited[idx]) continue;
|
if (visited[idx]) continue;
|
||||||
if (alt < dist[idx]) {
|
if (alt < dist[idx]) {
|
||||||
dist[idx] = alt;
|
dist[idx] = alt;
|
||||||
@ -242,8 +242,8 @@ public partial class Lab : Node2D
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
while (answer != vec2idx(start)) {
|
while (answer != Vec2Idx(start)) {
|
||||||
path.Add(idx2vec2(answer));
|
path.Add(Idx2Vec2(answer));
|
||||||
answer = prev[answer];
|
answer = prev[answer];
|
||||||
}
|
}
|
||||||
path.Reverse();
|
path.Reverse();
|
||||||
@ -260,17 +260,17 @@ public partial class Lab : Node2D
|
|||||||
|
|
||||||
public MapNodeType GetMapNodeTypeOfPosition(Vector2I pos) {
|
public MapNodeType GetMapNodeTypeOfPosition(Vector2I pos) {
|
||||||
if (!IsValidPosition(pos)) return MapNodeType.Invalid;
|
if (!IsValidPosition(pos)) return MapNodeType.Invalid;
|
||||||
return blocks[pos.X, pos.Y];
|
return _blocks[pos.X, pos.Y];
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector2I Point2Coord(Vector2 pos) {
|
public Vector2I Point2Coord(Vector2 pos) {
|
||||||
return tileMap.LocalToMap(tileMap.ToLocal(pos));
|
return _tileMap.LocalToMap(_tileMap.ToLocal(pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Vector2I GetFurSpecialPosition(Guid fID, int idx) {
|
public Vector2I GetFurSpecialPosition(Guid fId, int idx) {
|
||||||
if (!furnitureIDs.ContainsKey(fID)) return Vector2I.Zero;
|
if (!_furnitureIDs.ContainsKey(fId)) return Vector2I.Zero;
|
||||||
if (idx < 0 || idx > furnitureIDs[fID].SpecialTiles.Length) return Vector2I.Zero;
|
if (idx < 0 || idx > _furnitureIDs[fId].SpecialTiles.Length) return Vector2I.Zero;
|
||||||
return furnitureIDs[fID].SpecialTiles[idx].Position + furnitureIDs[fID].TilePosition;
|
return _furnitureIDs[fId].SpecialTiles[idx].Position + _furnitureIDs[fId].TilePosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,9 +4,9 @@ using System.Collections.Generic;
|
|||||||
|
|
||||||
public partial class Loader : Control
|
public partial class Loader : Control
|
||||||
{
|
{
|
||||||
private ProgressBar progressBar;
|
private ProgressBar _progressBar;
|
||||||
private int sceneLoaded = 0;
|
private int _sceneLoaded = 0;
|
||||||
private List<string> resToLoad = new (){
|
private List<string> _resToLoad = new (){
|
||||||
"res://scenes/lab.tscn",
|
"res://scenes/lab.tscn",
|
||||||
"res://scenes/player.tscn"
|
"res://scenes/player.tscn"
|
||||||
};
|
};
|
||||||
@ -14,7 +14,7 @@ public partial class Loader : Control
|
|||||||
// Called when the node enters the scene tree for the first time.
|
// Called when the node enters the scene tree for the first time.
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
{
|
{
|
||||||
progressBar = GetNode<ProgressBar>("ProgressBar");
|
_progressBar = GetNode<ProgressBar>("ProgressBar");
|
||||||
string nextScene = GameManager.NextScene;
|
string nextScene = GameManager.NextScene;
|
||||||
if (nextScene != null) {
|
if (nextScene != null) {
|
||||||
GameManager.NextScene = "res://scenes/lab.tscn";
|
GameManager.NextScene = "res://scenes/lab.tscn";
|
||||||
@ -25,8 +25,8 @@ public partial class Loader : Control
|
|||||||
// resToLoad.AddRange(Res.Hair);
|
// resToLoad.AddRange(Res.Hair);
|
||||||
// resToLoad.AddRange(Res.Outfit);
|
// resToLoad.AddRange(Res.Outfit);
|
||||||
// resToLoad.AddRange(Res.Smartphone);
|
// resToLoad.AddRange(Res.Smartphone);
|
||||||
ResourceLoader.LoadThreadedRequest(resToLoad[sceneLoaded]);
|
ResourceLoader.LoadThreadedRequest(_resToLoad[_sceneLoaded]);
|
||||||
progressBar.MaxValue = 100 * resToLoad.Count;
|
_progressBar.MaxValue = 100 * _resToLoad.Count;
|
||||||
SetProcess(true);
|
SetProcess(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,22 +34,22 @@ public partial class Loader : Control
|
|||||||
public override void _Process(double delta)
|
public override void _Process(double delta)
|
||||||
{
|
{
|
||||||
Godot.Collections.Array progress = new();
|
Godot.Collections.Array progress = new();
|
||||||
switch(ResourceLoader.LoadThreadedGetStatus(resToLoad[sceneLoaded], progress))
|
switch(ResourceLoader.LoadThreadedGetStatus(_resToLoad[_sceneLoaded], progress))
|
||||||
{
|
{
|
||||||
case ResourceLoader.ThreadLoadStatus.InProgress:
|
case ResourceLoader.ThreadLoadStatus.InProgress:
|
||||||
progressBar.Value = 100 * sceneLoaded + (int)((float)progress[0] * 100);
|
_progressBar.Value = 100 * _sceneLoaded + (int)((float)progress[0] * 100);
|
||||||
break;
|
break;
|
||||||
case ResourceLoader.ThreadLoadStatus.Loaded:
|
case ResourceLoader.ThreadLoadStatus.Loaded:
|
||||||
sceneLoaded++;
|
_sceneLoaded++;
|
||||||
if(sceneLoaded < resToLoad.Count) {
|
if(_sceneLoaded < _resToLoad.Count) {
|
||||||
ResourceLoader.LoadThreadedRequest(resToLoad[sceneLoaded]);
|
ResourceLoader.LoadThreadedRequest(_resToLoad[_sceneLoaded]);
|
||||||
} else {
|
} else {
|
||||||
SetProcess(false);
|
SetProcess(false);
|
||||||
GetTree().ChangeSceneToFile("res://scenes/lab.tscn");
|
GetTree().ChangeSceneToFile("res://scenes/lab.tscn");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
GD.Print("Error loading scene: " + resToLoad[sceneLoaded]);
|
GD.Print("Error loading scene: " + _resToLoad[_sceneLoaded]);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -43,18 +43,18 @@ public partial class Player : Node
|
|||||||
|
|
||||||
private Dictionary<DateOnly, HashSet<Guid>> _events;
|
private Dictionary<DateOnly, HashSet<Guid>> _events;
|
||||||
|
|
||||||
public void Subscribe(DateOnly date, Guid eventUUID)
|
public void Subscribe(DateOnly date, Guid eventUuid)
|
||||||
{
|
{
|
||||||
_events ??= new Dictionary<DateOnly, HashSet<Guid>>();
|
_events ??= new Dictionary<DateOnly, HashSet<Guid>>();
|
||||||
if (!_events.ContainsKey(date)) _events[date] = new HashSet<Guid>();
|
if (!_events.ContainsKey(date)) _events[date] = new HashSet<Guid>();
|
||||||
_events[date].Add(eventUUID);
|
_events[date].Add(eventUuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Unsubscribe(DateOnly date, Guid eventUUID)
|
public void Unsubscribe(DateOnly date, Guid eventUuid)
|
||||||
{
|
{
|
||||||
if (_events == null) return;
|
if (_events == null) return;
|
||||||
if (!_events.ContainsKey(date)) return;
|
if (!_events.ContainsKey(date)) return;
|
||||||
_events[date].Remove(eventUUID);
|
_events[date].Remove(eventUuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void NextDay()
|
private void NextDay()
|
||||||
@ -69,11 +69,11 @@ public partial class Player : Node
|
|||||||
if (_events.ContainsKey(InternalDate))
|
if (_events.ContainsKey(InternalDate))
|
||||||
{
|
{
|
||||||
var events = _events[InternalDate];
|
var events = _events[InternalDate];
|
||||||
foreach (var eventUUID in events)
|
foreach (var eventUuid in events)
|
||||||
{
|
{
|
||||||
// TODO: Trigger event
|
// TODO: Trigger event
|
||||||
OnEventTriggered?.Invoke(InternalDate, eventUUID);
|
OnEventTriggered?.Invoke(InternalDate, eventUuid);
|
||||||
GD.Print("event triggered! eventUUID: " + eventUUID.ToString());
|
GD.Print("event triggered! eventUUID: " + eventUuid.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,7 +88,7 @@ public partial class Player : Node
|
|||||||
InternalDate = startDate;
|
InternalDate = startDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public delegate void EventTriggeredEventHandler(DateOnly date, Guid eventUUID);
|
public delegate void EventTriggeredEventHandler(DateOnly date, Guid eventUuid);
|
||||||
|
|
||||||
public event EventTriggeredEventHandler OnEventTriggered;
|
public event EventTriggeredEventHandler OnEventTriggered;
|
||||||
|
|
||||||
|
|||||||
@ -15,12 +15,12 @@ public partial class Res : Node
|
|||||||
|
|
||||||
public enum Type
|
public enum Type
|
||||||
{
|
{
|
||||||
ACCESSORY,
|
Accessory,
|
||||||
BODY,
|
Body,
|
||||||
EYE,
|
Eye,
|
||||||
HAIR,
|
Hair,
|
||||||
OUTFIT,
|
Outfit,
|
||||||
PHONE
|
Phone
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetRandom(Type resType)
|
public static string GetRandom(Type resType)
|
||||||
@ -33,7 +33,7 @@ public partial class Res : Node
|
|||||||
return path.Replace("_48x48_", "_").Replace("_48x48", "");
|
return path.Replace("_48x48_", "_").Replace("_48x48", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static string GetRandom(Type resType, bool use16x16Sprites)
|
public static string GetRandom(Type resType, bool use16X16Sprites)
|
||||||
{
|
{
|
||||||
var resources = allResources[(int)resType];
|
var resources = allResources[(int)resType];
|
||||||
if (resources == null) {
|
if (resources == null) {
|
||||||
@ -42,7 +42,7 @@ public partial class Res : Node
|
|||||||
}
|
}
|
||||||
int index = GD.RandRange(0, resources.Length - 1);
|
int index = GD.RandRange(0, resources.Length - 1);
|
||||||
var path = resources[index];
|
var path = resources[index];
|
||||||
if (!use16x16Sprites) return path;
|
if (!use16X16Sprites) return path;
|
||||||
|
|
||||||
var path16 = To16Path(path);
|
var path16 = To16Path(path);
|
||||||
return ResourceLoader.Exists(path16) ? path16 : path;
|
return ResourceLoader.Exists(path16) ? path16 : path;
|
||||||
|
|||||||
@ -9,7 +9,7 @@ public partial class Student : CharacterBody2D
|
|||||||
{
|
{
|
||||||
public float Speed { get; set; } = 8.0f;
|
public float Speed { get; set; } = 8.0f;
|
||||||
public const float JumpVelocity = -400.0f;
|
public const float JumpVelocity = -400.0f;
|
||||||
[Export] public bool Use16x16Sprites { get; set; } = false;
|
[Export] public bool Use16X16Sprites { get; set; } = false;
|
||||||
|
|
||||||
// --- MVP: Model Binding ---
|
// --- MVP: Model Binding ---
|
||||||
public StudentModel Model { get; private set; }
|
public StudentModel Model { get; private set; }
|
||||||
@ -23,8 +23,8 @@ public partial class Student : CharacterBody2D
|
|||||||
|
|
||||||
public int NextType = -1;
|
public int NextType = -1;
|
||||||
|
|
||||||
private Queue<Vector2I> PathToGo = new();
|
private Queue<Vector2I> _pathToGo = new();
|
||||||
private AnimationPlayer animationPlayer;
|
private AnimationPlayer _animationPlayer;
|
||||||
public enum CharacterState { Idle, Walking, Sitting, SittingDown, StandingUp }
|
public enum CharacterState { Idle, Walking, Sitting, SittingDown, StandingUp }
|
||||||
public CharacterState State { get; set; } = CharacterState.Idle;
|
public CharacterState State { get; set; } = CharacterState.Idle;
|
||||||
public enum Direction { Up, Down, Left, Right }
|
public enum Direction { Up, Down, Left, Right }
|
||||||
@ -32,27 +32,27 @@ public partial class Student : CharacterBody2D
|
|||||||
|
|
||||||
public Queue<CharacterState> StateQueue { get; set; } = new();
|
public Queue<CharacterState> StateQueue { get; set; } = new();
|
||||||
|
|
||||||
private Vector2I targetSpecialPosition;
|
private Vector2I _targetSpecialPosition;
|
||||||
private Vector2I lastWalkablePosition;
|
private Vector2I _lastWalkablePosition;
|
||||||
private double duration = 0;
|
private double _duration = 0;
|
||||||
|
|
||||||
|
|
||||||
private void SitDown(double delta) {
|
private void SitDown(double delta) {
|
||||||
duration += delta;
|
_duration += delta;
|
||||||
if (!(duration > 0.3)) return;
|
if (!(_duration > 0.3)) return;
|
||||||
if (TargetDirection == Direction.Up) {
|
if (TargetDirection == Direction.Up) {
|
||||||
|
|
||||||
}
|
}
|
||||||
animationPlayer.Play("idle_front");
|
_animationPlayer.Play("idle_front");
|
||||||
State = StateQueue.Dequeue();
|
State = StateQueue.Dequeue();
|
||||||
GlobalPosition = targetSpecialPosition;
|
GlobalPosition = _targetSpecialPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GettingUp(double delta) {
|
private void GettingUp(double delta) {
|
||||||
duration += delta;
|
_duration += delta;
|
||||||
if (!(duration > 0.3)) return;
|
if (!(_duration > 0.3)) return;
|
||||||
State = StateQueue.Dequeue();
|
State = StateQueue.Dequeue();
|
||||||
GlobalPosition = lastWalkablePosition;
|
GlobalPosition = _lastWalkablePosition;
|
||||||
}
|
}
|
||||||
public override void _PhysicsProcess(double delta)
|
public override void _PhysicsProcess(double delta)
|
||||||
{
|
{
|
||||||
@ -64,7 +64,7 @@ public partial class Student : CharacterBody2D
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (StateQueue.Peek() == CharacterState.StandingUp && State == CharacterState.Sitting) {
|
if (StateQueue.Peek() == CharacterState.StandingUp && State == CharacterState.Sitting) {
|
||||||
duration = 0;
|
_duration = 0;
|
||||||
State = StateQueue.Dequeue();
|
State = StateQueue.Dequeue();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -77,17 +77,17 @@ public partial class Student : CharacterBody2D
|
|||||||
// GD.Print($"{State} -> Empty");
|
// GD.Print($"{State} -> Empty");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PathToGo.Count == 0)
|
if (_pathToGo.Count == 0)
|
||||||
{
|
{
|
||||||
if (State != CharacterState.Walking) return;
|
if (State != CharacterState.Walking) return;
|
||||||
if (StateQueue.Count > 0) {
|
if (StateQueue.Count > 0) {
|
||||||
if (StateQueue.Peek() == CharacterState.SittingDown) {
|
if (StateQueue.Peek() == CharacterState.SittingDown) {
|
||||||
duration = 0;
|
_duration = 0;
|
||||||
}
|
}
|
||||||
State = StateQueue.Dequeue();
|
State = StateQueue.Dequeue();
|
||||||
}
|
}
|
||||||
if (State == CharacterState.Idle) {
|
if (State == CharacterState.Idle) {
|
||||||
animationPlayer.Play("idle_front");
|
_animationPlayer.Play("idle_front");
|
||||||
}
|
}
|
||||||
return;// No path to follow.
|
return;// No path to follow.
|
||||||
}
|
}
|
||||||
@ -95,10 +95,10 @@ public partial class Student : CharacterBody2D
|
|||||||
if (State != CharacterState.Walking) return;
|
if (State != CharacterState.Walking) return;
|
||||||
|
|
||||||
Vector2 velocity = new();
|
Vector2 velocity = new();
|
||||||
var nextPoint = PathToGo.Peek();
|
var nextPoint = _pathToGo.Peek();
|
||||||
if ((int)GlobalPosition.X == nextPoint.X && (int)GlobalPosition.Y == nextPoint.Y)
|
if ((int)GlobalPosition.X == nextPoint.X && (int)GlobalPosition.Y == nextPoint.Y)
|
||||||
{
|
{
|
||||||
PathToGo.Dequeue();
|
_pathToGo.Dequeue();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,10 +109,10 @@ public partial class Student : CharacterBody2D
|
|||||||
velocity.Y = Speed;
|
velocity.Y = Speed;
|
||||||
if (GlobalPosition.Y > nextPoint.Y)
|
if (GlobalPosition.Y > nextPoint.Y)
|
||||||
{
|
{
|
||||||
animationPlayer.Play("walk_up");
|
_animationPlayer.Play("walk_up");
|
||||||
velocity.Y = -velocity.Y;
|
velocity.Y = -velocity.Y;
|
||||||
} else {
|
} else {
|
||||||
animationPlayer.Play("walk_down");
|
_animationPlayer.Play("walk_down");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if ((int)GlobalPosition.Y == nextPoint.Y)
|
else if ((int)GlobalPosition.Y == nextPoint.Y)
|
||||||
@ -123,10 +123,10 @@ public partial class Student : CharacterBody2D
|
|||||||
if (GlobalPosition.X > nextPoint.X)
|
if (GlobalPosition.X > nextPoint.X)
|
||||||
{
|
{
|
||||||
|
|
||||||
animationPlayer.Play("walk_left");
|
_animationPlayer.Play("walk_left");
|
||||||
velocity.X = -velocity.X;
|
velocity.X = -velocity.X;
|
||||||
} else {
|
} else {
|
||||||
animationPlayer.Play("walk_right");
|
_animationPlayer.Play("walk_right");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
GlobalPosition = GlobalPosition with
|
GlobalPosition = GlobalPosition with
|
||||||
@ -149,17 +149,17 @@ public partial class Student : CharacterBody2D
|
|||||||
// bb.BindVarToProperty("Stayed", this, "Speed");
|
// bb.BindVarToProperty("Stayed", this, "Speed");
|
||||||
// GD.Print(bb.GetVar("Stayed"));
|
// GD.Print(bb.GetVar("Stayed"));
|
||||||
// GD.Print($"Speed: {Speed}");
|
// GD.Print($"Speed: {Speed}");
|
||||||
animationPlayer = GetNode<AnimationPlayer>("AnimationPlayer");
|
_animationPlayer = GetNode<AnimationPlayer>("AnimationPlayer");
|
||||||
animationPlayer.Play("idle_front");
|
_animationPlayer.Play("idle_front");
|
||||||
var name_test = GetNode<StudentName>("StudentName");
|
var nameTest = GetNode<StudentName>("StudentName");
|
||||||
|
|
||||||
// MVP: Initialize Model if null
|
// MVP: Initialize Model if null
|
||||||
if (Model == null)
|
if (Model == null)
|
||||||
{
|
{
|
||||||
Model = new StudentModel(name_test.GenerateName());
|
Model = new StudentModel(nameTest.GenerateName());
|
||||||
GD.Print($"[Auto-Init] Student: {Model.Name}");
|
GD.Print($"[Auto-Init] Student: {Model.Name}");
|
||||||
} else {
|
} else {
|
||||||
GD.Print("生成的名字是: " + name_test.GenerateName()); // Keep original log for reference
|
GD.Print("生成的名字是: " + nameTest.GenerateName()); // Keep original log for reference
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,7 +167,7 @@ public partial class Student : CharacterBody2D
|
|||||||
{
|
{
|
||||||
foreach (var p in path)
|
foreach (var p in path)
|
||||||
{
|
{
|
||||||
PathToGo.Enqueue(new Vector2I(p.X * 48 + 24, p.Y * 48 + 24));
|
_pathToGo.Enqueue(new Vector2I(p.X * 48 + 24, p.Y * 48 + 24));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,20 +185,20 @@ public partial class Student : CharacterBody2D
|
|||||||
}
|
}
|
||||||
Vector2I vec = Vector2I.Zero;
|
Vector2I vec = Vector2I.Zero;
|
||||||
while ((lab.GetMapNodeTypeOfPosition(vec) & Lab.MapNodeType.Walkable) == 0) {
|
while ((lab.GetMapNodeTypeOfPosition(vec) & Lab.MapNodeType.Walkable) == 0) {
|
||||||
vec.X = GD.RandRange(0, Lab.MAP_WIDTH);
|
vec.X = GD.RandRange(0, Lab.MapWidth);
|
||||||
vec.Y = GD.RandRange(0, Lab.MAP_HEIGHT);
|
vec.Y = GD.RandRange(0, Lab.MapHeight);
|
||||||
}
|
}
|
||||||
var pos = GlobalPosition;
|
var pos = GlobalPosition;
|
||||||
if (State == CharacterState.Sitting) {
|
if (State == CharacterState.Sitting) {
|
||||||
StateQueue.Enqueue(CharacterState.StandingUp);
|
StateQueue.Enqueue(CharacterState.StandingUp);
|
||||||
StateQueue.Enqueue(CharacterState.Walking);
|
StateQueue.Enqueue(CharacterState.Walking);
|
||||||
pos = lastWalkablePosition;
|
pos = _lastWalkablePosition;
|
||||||
} else if (State == CharacterState.Idle) {
|
} else if (State == CharacterState.Idle) {
|
||||||
State = CharacterState.Walking;
|
State = CharacterState.Walking;
|
||||||
}
|
}
|
||||||
StateQueue.Enqueue(CharacterState.Idle);
|
StateQueue.Enqueue(CharacterState.Idle);
|
||||||
MoveFollowPath(lab.GetShortestPath(lab.Point2Coord(pos), vec));
|
MoveFollowPath(lab.GetShortestPath(lab.Point2Coord(pos), vec));
|
||||||
randomChangeBody();
|
RandomChangeBody();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void GoToSeat() {
|
public void GoToSeat() {
|
||||||
@ -216,23 +216,23 @@ public partial class Student : CharacterBody2D
|
|||||||
if (i == 1) {
|
if (i == 1) {
|
||||||
// Seat up
|
// Seat up
|
||||||
TargetDirection = Direction.Up;
|
TargetDirection = Direction.Up;
|
||||||
targetSpecialPosition = new Vector2I(target.X * 48 + 24, target.Y * 48 + 12);
|
_targetSpecialPosition = new Vector2I(target.X * 48 + 24, target.Y * 48 + 12);
|
||||||
} else {
|
} else {
|
||||||
TargetDirection = Direction.Down;
|
TargetDirection = Direction.Down;
|
||||||
targetSpecialPosition = new Vector2I(target.X * 48 + 24, target.Y * 48 + 18);
|
_targetSpecialPosition = new Vector2I(target.X * 48 + 24, target.Y * 48 + 18);
|
||||||
}
|
}
|
||||||
var pos = GlobalPosition;
|
var pos = GlobalPosition;
|
||||||
if (State == CharacterState.Sitting) {
|
if (State == CharacterState.Sitting) {
|
||||||
StateQueue.Enqueue(CharacterState.StandingUp);
|
StateQueue.Enqueue(CharacterState.StandingUp);
|
||||||
StateQueue.Enqueue(CharacterState.Walking);
|
StateQueue.Enqueue(CharacterState.Walking);
|
||||||
pos = lastWalkablePosition;
|
pos = _lastWalkablePosition;
|
||||||
} else if (State == CharacterState.Idle) {
|
} else if (State == CharacterState.Idle) {
|
||||||
State = CharacterState.Walking;
|
State = CharacterState.Walking;
|
||||||
}
|
}
|
||||||
StateQueue.Enqueue(CharacterState.SittingDown);
|
StateQueue.Enqueue(CharacterState.SittingDown);
|
||||||
StateQueue.Enqueue(CharacterState.Sitting);
|
StateQueue.Enqueue(CharacterState.Sitting);
|
||||||
var path = lab.GetShortestPath(lab.Point2Coord(pos), target);
|
var path = lab.GetShortestPath(lab.Point2Coord(pos), target);
|
||||||
lastWalkablePosition = new Vector2I(path.Last().X * 48 + 24, path.Last().Y * 48 + 24);
|
_lastWalkablePosition = new Vector2I(path.Last().X * 48 + 24, path.Last().Y * 48 + 24);
|
||||||
// if (lastWalkablePosition.X < targetSpecialPosition.X) {
|
// if (lastWalkablePosition.X < targetSpecialPosition.X) {
|
||||||
// TargetDirection = Direction.Right;
|
// TargetDirection = Direction.Right;
|
||||||
// } else if (lastWalkablePosition.X > targetSpecialPosition.X) {
|
// } else if (lastWalkablePosition.X > targetSpecialPosition.X) {
|
||||||
@ -245,16 +245,16 @@ public partial class Student : CharacterBody2D
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
MoveFollowPath(path);
|
MoveFollowPath(path);
|
||||||
randomChangeBody();
|
RandomChangeBody();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void randomChangeBody()
|
private void RandomChangeBody()
|
||||||
{
|
{
|
||||||
GetNode<Sprite2D>("parts/body").Texture = ResourceLoader.Load<Texture2D>(Res.GetRandom(Res.Type.BODY, Use16x16Sprites));
|
GetNode<Sprite2D>("parts/body").Texture = ResourceLoader.Load<Texture2D>(Res.GetRandom(Res.Type.Body, Use16X16Sprites));
|
||||||
GetNode<Sprite2D>("parts/hairstyle").Texture = ResourceLoader.Load<Texture2D>(Res.GetRandom(Res.Type.HAIR, Use16x16Sprites));
|
GetNode<Sprite2D>("parts/hairstyle").Texture = ResourceLoader.Load<Texture2D>(Res.GetRandom(Res.Type.Hair, Use16X16Sprites));
|
||||||
GetNode<Sprite2D>("parts/outfit").Texture = ResourceLoader.Load<Texture2D>(Res.GetRandom(Res.Type.OUTFIT, Use16x16Sprites));
|
GetNode<Sprite2D>("parts/outfit").Texture = ResourceLoader.Load<Texture2D>(Res.GetRandom(Res.Type.Outfit, Use16X16Sprites));
|
||||||
GetNode<Sprite2D>("parts/eye").Texture = ResourceLoader.Load<Texture2D>(Res.GetRandom(Res.Type.EYE, Use16x16Sprites));
|
GetNode<Sprite2D>("parts/eye").Texture = ResourceLoader.Load<Texture2D>(Res.GetRandom(Res.Type.Eye, Use16X16Sprites));
|
||||||
GetNode<Sprite2D>("parts/accessory").Texture = ResourceLoader.Load<Texture2D>(Res.GetRandom(Res.Type.ACCESSORY, Use16x16Sprites));
|
GetNode<Sprite2D>("parts/accessory").Texture = ResourceLoader.Load<Texture2D>(Res.GetRandom(Res.Type.Accessory, Use16X16Sprites));
|
||||||
GetNode<Sprite2D>("parts/smartphone").Texture = ResourceLoader.Load<Texture2D>(Res.GetRandom(Res.Type.PHONE, Use16x16Sprites));
|
GetNode<Sprite2D>("parts/smartphone").Texture = ResourceLoader.Load<Texture2D>(Res.GetRandom(Res.Type.Phone, Use16X16Sprites));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,14 +7,14 @@ public partial class TestMap : TileMapLayer
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The last cell that was highlighted. Used to avoid unnecessary updates.
|
/// The last cell that was highlighted. Used to avoid unnecessary updates.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private Vector2I lastHighlightCell;
|
private Vector2I _lastHighlightCell;
|
||||||
private readonly Vector2I highlightTileCoord = new(0, 5);
|
private readonly Vector2I _highlightTileCoord = new(0, 5);
|
||||||
private readonly Vector2I vector2INegOne = new(-1, -1);
|
private readonly Vector2I _vector2INegOne = new(-1, -1);
|
||||||
// Called when the node enters the scene tree for the first time.
|
// Called when the node enters the scene tree for the first time.
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
{
|
{
|
||||||
// Initialize the lastHighlightCell to an invalid value.
|
// Initialize the lastHighlightCell to an invalid value.
|
||||||
lastHighlightCell = vector2INegOne;
|
_lastHighlightCell = _vector2INegOne;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -26,14 +26,14 @@ public partial class TestMap : TileMapLayer
|
|||||||
Vector2 mousePos = GetLocalMousePosition();
|
Vector2 mousePos = GetLocalMousePosition();
|
||||||
Vector2I cell = LocalToMap(mousePos);
|
Vector2I cell = LocalToMap(mousePos);
|
||||||
|
|
||||||
if (cell != lastHighlightCell)
|
if (cell != _lastHighlightCell)
|
||||||
{
|
{
|
||||||
if (lastHighlightCell != vector2INegOne)
|
if (_lastHighlightCell != _vector2INegOne)
|
||||||
{
|
{
|
||||||
EraseCell(lastHighlightCell);
|
EraseCell(_lastHighlightCell);
|
||||||
}
|
}
|
||||||
lastHighlightCell = cell;
|
_lastHighlightCell = cell;
|
||||||
SetCell(cell, 0, highlightTileCoord);
|
SetCell(cell, 0, _highlightTileCoord);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -8,40 +8,40 @@ public partial class TileMapping : Node
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// From tile coordinates to pixel coordinates. If null, use the offset instead.
|
/// From tile coordinates to pixel coordinates. If null, use the offset instead.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private Dictionary<Vector2I, Vector2I> pixelMap;
|
private Dictionary<Vector2I, Vector2I> _pixelMap;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If pixelMap is null, use this offset to convert tile coordinates to pixel coordinates.
|
/// If pixelMap is null, use this offset to convert tile coordinates to pixel coordinates.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private Vector2I pixelOffset;
|
private Vector2I _pixelOffset;
|
||||||
private Rect2I tileRect;
|
private Rect2I _tileRect;
|
||||||
|
|
||||||
public TileMapping(Dictionary<Vector2I, Vector2I> map) {
|
public TileMapping(Dictionary<Vector2I, Vector2I> map) {
|
||||||
pixelMap = map;
|
_pixelMap = map;
|
||||||
pixelOffset = new Vector2I(-int.MaxValue, -int.MaxValue);
|
_pixelOffset = new Vector2I(-int.MaxValue, -int.MaxValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public TileMapping(Vector2I offset, Rect2I rect) {
|
public TileMapping(Vector2I offset, Rect2I rect) {
|
||||||
pixelMap = null;
|
_pixelMap = null;
|
||||||
pixelOffset = offset;
|
_pixelOffset = offset;
|
||||||
tileRect = rect;
|
_tileRect = rect;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TileMapping() {}
|
public TileMapping() {}
|
||||||
|
|
||||||
public void Apply(TileMapLayer layer) {
|
public void Apply(TileMapLayer layer) {
|
||||||
// if pixelMap is not null, use all the pairs in pixelMap to set the tiles.
|
// if pixelMap is not null, use all the pairs in pixelMap to set the tiles.
|
||||||
if (pixelMap != null) {
|
if (_pixelMap != null) {
|
||||||
var sourceId = layer.GetCellSourceId(pixelMap.Keys.First());
|
var sourceId = layer.GetCellSourceId(_pixelMap.Keys.First());
|
||||||
foreach (var pair in pixelMap) {
|
foreach (var pair in _pixelMap) {
|
||||||
layer.SetCell(pair.Key, sourceId, pair.Value);
|
layer.SetCell(pair.Key, sourceId, pair.Value);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var sourceId = layer.GetCellSourceId(tileRect.Position);
|
var sourceId = layer.GetCellSourceId(_tileRect.Position);
|
||||||
GD.Print(tileRect);
|
GD.Print(_tileRect);
|
||||||
for (int x = tileRect.Position.X; x < tileRect.End.X; x++) {
|
for (int x = _tileRect.Position.X; x < _tileRect.End.X; x++) {
|
||||||
for (int y = tileRect.Position.Y; y < tileRect.End.Y; y++) {
|
for (int y = _tileRect.Position.Y; y < _tileRect.End.Y; y++) {
|
||||||
var pos = new Vector2I(x, y);
|
var pos = new Vector2I(x, y);
|
||||||
layer.SetCell(pos, sourceId, pos + pixelOffset);
|
layer.SetCell(pos, sourceId, pos + _pixelOffset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user