diff --git a/project.godot b/project.godot index 7446976..1af5e6f 100644 --- a/project.godot +++ b/project.godot @@ -11,7 +11,7 @@ config_version=5 [application] config/name="最强导师" -run/main_scene="uid://bmxk4pi4sd1rd" +run/main_scene="uid://b0cu4fa7vohmw" config/features=PackedStringArray("4.5", "C#", "GL Compatibility") config/icon="res://icon.svg" diff --git a/res_src/campus.aseprite b/res_src/campus.aseprite index baa3bf9..ad4ff70 100644 Binary files a/res_src/campus.aseprite and b/res_src/campus.aseprite differ diff --git a/res_src/campus.png b/res_src/campus.png index df0419a..aaa02bd 100644 Binary files a/res_src/campus.png and b/res_src/campus.png differ diff --git a/scenes/CampusController.cs b/scenes/CampusController.cs index b3ed27f..76d95a6 100644 --- a/scenes/CampusController.cs +++ b/scenes/CampusController.cs @@ -1,5 +1,6 @@ using Godot; using System; +using System.Collections.Generic; public partial class CampusController : Node2D { @@ -8,6 +9,16 @@ public partial class CampusController : Node2D private Button _taskToggle; 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 _coveragePoints = new(); + private bool _spawnPending = true; + // Called when the node enters the scene tree for the first time. public override void _Ready() { @@ -24,11 +35,24 @@ public partial class CampusController : Node2D _taskToggle.Toggled += OnTaskToggled; _logToggle.Toggled += OnLogToggled; + + // 导航区域与学生容器初始化 + _navigationRegion = GetNodeOrNull("Sprite2D/NavigationRegion2D"); + _studentsRoot = GetNodeOrNull("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. public override void _Process(double delta) { + TrySpawnStudents(); } private void OnTaskToggled(bool pressed) @@ -61,4 +85,158 @@ public partial class CampusController : Node2D tween.TweenCallback(Callable.From(() => container.Visible = false)); } } -} \ No newline at end of file + + private void TrySpawnStudents() + { + if (!_spawnPending) + { + return; + } + + // 已生成过学生则不重复生成 + if (_studentsRoot != null && _studentsRoot.GetChildCount() > 0) + { + _spawnPending = false; + return; + } + + if (StudentScene == null) + { + StudentScene = ResourceLoader.Load("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 BuildCoveragePoints(Rid map) + { + var points = new List(); + 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 points, Vector2 candidate, float minDistance) + { + for (int i = 0; i < points.Count; i++) + { + if (points[i].DistanceTo(candidate) <= minDistance) + { + return true; + } + } + return false; + } +} diff --git a/scenes/campus.tscn b/scenes/campus.tscn index afe7747..8d22cd6 100644 --- a/scenes/campus.tscn +++ b/scenes/campus.tscn @@ -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="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://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"] texture = ExtResource("1_p4tmp") use_texture_padding = false @@ -2000,16 +2009,22 @@ script = ExtResource("1_controller") texture = ExtResource("1_p4tmp") centered = false +[node name="NavigationRegion2D" type="NavigationRegion2D" parent="Sprite2D"] +navigation_polygon = SubResource("NavigationPolygon_8u8vn") + [node name="Log" parent="." instance=ExtResource("1_hi2p7")] +visible = false offset_left = 655.0 offset_top = 375.0 offset_right = 955.0 offset_bottom = 535.0 [node name="TopBar" parent="." instance=ExtResource("2_p4tmp")] +visible = false offset_bottom = 55.0 [node name="Task" parent="." instance=ExtResource("3_4gjr3")] +visible = false offset_left = 4.0 offset_top = 216.0 offset_right = 305.0 diff --git a/scenes/student_16_native.tscn b/scenes/student_16_native.tscn index ca1167b..397812a 100644 --- a/scenes/student_16_native.tscn +++ b/scenes/student_16_native.tscn @@ -1,6 +1,6 @@ [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="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"] @@ -10,7 +10,7 @@ [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"] -size = Vector2(16, 32) +size = Vector2(16, 16) [sub_resource type="Animation" id="Animation_f0f87"] length = 0.001 @@ -877,10 +877,8 @@ _data = { [node name="Student" type="CharacterBody2D"] z_index = 2 script = ExtResource("1_oesea") -Use16x16Sprites = true [node name="CollisionShape2D" type="CollisionShape2D" parent="."] -position = Vector2(0, -8) shape = SubResource("RectangleShape2D_opr6h") [node name="parts" type="Node2D" parent="."] @@ -935,3 +933,11 @@ autoplay = "RESET" [node name="StudentName" type="Node" parent="."] 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 diff --git a/scripts/CampusStudent.cs b/scripts/CampusStudent.cs new file mode 100644 index 0000000..4886631 --- /dev/null +++ b/scripts/CampusStudent.cs @@ -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 _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"); + _animationPlayer = GetNodeOrNull("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 points, int startIndex) + { + _patrolPoints = points ?? new List(); + 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(Res.GetRandom(Res.Type.Body, Use16X16Sprites)); + _hairstyle.Texture = ResourceLoader.Load(Res.GetRandom(Res.Type.Hair, Use16X16Sprites)); + _outfit.Texture = ResourceLoader.Load(Res.GetRandom(Res.Type.Outfit, Use16X16Sprites)); + _eye.Texture = ResourceLoader.Load(Res.GetRandom(Res.Type.Eye, Use16X16Sprites)); + _accessory.Texture = ResourceLoader.Load(Res.GetRandom(Res.Type.Accessory, Use16X16Sprites)); + _smartphone.Texture = ResourceLoader.Load(Res.GetRandom(Res.Type.Phone, Use16X16Sprites)); + } + + private void CacheSprites() + { + // 缓存子节点引用,避免每帧查找 + _body = GetNode("parts/body"); + _hairstyle = GetNode("parts/hairstyle"); + _outfit = GetNode("parts/outfit"); + _eye = GetNode("parts/eye"); + _accessory = GetNode("parts/accessory"); + _smartphone = GetNode("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 + } +} \ No newline at end of file diff --git a/scripts/CampusStudent.cs.uid b/scripts/CampusStudent.cs.uid new file mode 100644 index 0000000..dcb949e --- /dev/null +++ b/scripts/CampusStudent.cs.uid @@ -0,0 +1 @@ +uid://d2m5r9k3n7q1s diff --git a/scripts/Cube.cs b/scripts/Cube.cs index c469509..9909a91 100644 --- a/scripts/Cube.cs +++ b/scripts/Cube.cs @@ -86,7 +86,7 @@ public partial class Cube : StaticBody2D, ITileDraggable } private readonly Guid _id = Guid.NewGuid(); - public Guid ID => _id; + public Guid Id => _id; public Vector2I TilePosition { get; set; } = new Vector2I(5,5); private bool _isCollided = true; @@ -102,19 +102,19 @@ public partial class Cube : StaticBody2D, ITileDraggable } } } - private static readonly Rect2I _tileRect = new(-1, -2, 4, 5); - public Rect2I TileRect => _tileRect; + private static readonly Rect2I tileRect = new(-1, -2, 4, 5); + public Rect2I TileRect => tileRect; private Vector2I _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,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. public override void _Ready() @@ -147,27 +147,27 @@ public partial class Cube : StaticBody2D, ITileDraggable GlobalPosition = TilePosition * 48; } - private int tableThemeIdx; - private int table2ThemeIdx; - private int chairThemeIdx; - private int chair2ThemeIdx; - private int equipThemeIdx; - private int equip2ThemeIdx; + private int _tableThemeIdx; + private int _table2ThemeIdx; + private int _chairThemeIdx; + private int _chair2ThemeIdx; + private int _equipThemeIdx; + private int _equip2ThemeIdx; public void RandomChangeTheme() { - tableThemeIdx = GD.RandRange(0, tableThemes.Length-1); - chairThemeIdx = GD.RandRange(0, chairThemes.Length-1); - equipThemeIdx = GD.RandRange(0, equipThemes.Length-1); + _tableThemeIdx = GD.RandRange(0, tableThemes.Length-1); + _chairThemeIdx = GD.RandRange(0, chairThemes.Length-1); + _equipThemeIdx = GD.RandRange(0, equipThemes.Length-1); - table2ThemeIdx = GD.RandRange(0, table2Themes.Length-1); - chair2ThemeIdx = GD.RandRange(0, chair2Themes.Length-1); - equip2ThemeIdx = GD.RandRange(0, equip2Themes.Length-1); + _table2ThemeIdx = GD.RandRange(0, table2Themes.Length-1); + _chair2ThemeIdx = GD.RandRange(0, chair2Themes.Length-1); + _equip2ThemeIdx = GD.RandRange(0, equip2Themes.Length-1); - tableThemes[tableThemeIdx].Apply(GetNode("Desk")); - chairThemes[chairThemeIdx].Apply(GetNode("Chair")); - equipThemes[equipThemeIdx].Apply(GetNode("Equip")); - table2Themes[table2ThemeIdx].Apply(GetNode("Desk2")); - chair2Themes[chair2ThemeIdx].Apply(GetNode("Chair2")); - equip2Themes[equip2ThemeIdx].Apply(GetNode("Equip2")); + tableThemes[_tableThemeIdx].Apply(GetNode("Desk")); + chairThemes[_chairThemeIdx].Apply(GetNode("Chair")); + equipThemes[_equipThemeIdx].Apply(GetNode("Equip")); + table2Themes[_table2ThemeIdx].Apply(GetNode("Desk2")); + chair2Themes[_chair2ThemeIdx].Apply(GetNode("Chair2")); + equip2Themes[_equip2ThemeIdx].Apply(GetNode("Equip2")); } private static readonly HashSet blockerTiles = new(new Vector2I[]{ new(0,0), @@ -183,15 +183,15 @@ public partial class Cube : StaticBody2D, ITileDraggable if (blockerTiles.Contains(pos)) { return Lab.MapNodeType.Blocker; } - if (_specialTiles.Any(t => t.Position == pos)) { - return _specialTiles.First(t => t.Position == pos).NodeType; + if (specialTiles.Any(t => t.Position == pos)) { + return specialTiles.First(t => t.Position == pos).NodeType; } return Lab.MapNodeType.Walkable; } public void ChairFaceTo(Vector2I target, int idx) { if (idx == 0) { - var theme = chairThemes[chairThemeIdx]; + var theme = chairThemes[_chairThemeIdx]; } } diff --git a/scripts/Data/Import/GameDataImporter.cs b/scripts/Data/Import/GameDataImporter.cs new file mode 100644 index 0000000..e69de29 diff --git a/scripts/Data/Import/GameDataImporter.cs.uid b/scripts/Data/Import/GameDataImporter.cs.uid new file mode 100644 index 0000000..a7c8914 --- /dev/null +++ b/scripts/Data/Import/GameDataImporter.cs.uid @@ -0,0 +1 @@ +uid://odcll7t6ojce diff --git a/scripts/GameManager.cs b/scripts/GameManager.cs index 758eb15..8f67441 100644 --- a/scripts/GameManager.cs +++ b/scripts/GameManager.cs @@ -10,7 +10,7 @@ public partial class GameManager : Node /// public static bool IsTutorial { get; private set; } 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 --- public enum GamePhase @@ -46,7 +46,7 @@ public partial class GameManager : Node public override void _Ready() { - Input.SetCustomMouseCursor(Arrow2x); + Input.SetCustomMouseCursor(Arrow2X); // MVP Initialization InitializeGame(); diff --git a/scripts/ITileDraggable.cs b/scripts/ITileDraggable.cs index 0875fc5..c37023d 100644 --- a/scripts/ITileDraggable.cs +++ b/scripts/ITileDraggable.cs @@ -23,5 +23,5 @@ public interface ITileDraggable { } Lab.MapNodeType GetTileType(Vector2I pos); - Guid ID { get; } + Guid Id { get; } } \ No newline at end of file diff --git a/scripts/Lab.cs b/scripts/Lab.cs index 24a5ca1..d87991d 100644 --- a/scripts/Lab.cs +++ b/scripts/Lab.cs @@ -22,8 +22,8 @@ public partial class Lab : Node2D new(39,2,1,18), }; - private readonly Dictionary furnitureIDs = new(); - public Dictionary Furniture => furnitureIDs; + private readonly Dictionary _furnitureIDs = new(); + public Dictionary Furniture => _furnitureIDs; // Called when the node enters the scene tree for the first time. public override void _Ready() @@ -38,24 +38,24 @@ public partial class Lab : Node2D Player.Timeline.OnDayChanged += d => label.Text = d.ToLongDateString(); var table = GetNode("Cube"); table.Draggable = true; - MoneyLabel = GetNode