using System; using System.Collections.Generic; using System.Linq; using Godot; using Models; // ReSharper disable CheckNamespace /// /// 学生角色控制器 /// public partial class Student : CharacterBody2D { /// /// 角色状态枚举 /// public enum CharacterState { Idle, Walking, Sitting, SittingDown, StandingUp } /// /// 方向枚举 /// public enum Direction { Up, Down, Left, Right } /// /// 跳跃速度 /// public const float JumpVelocity = -400.0f; private readonly Queue _pathToGo = new(); private AnimationPlayer _animationPlayer; private double _duration; private Vector2I _lastWalkablePosition; private Vector2I _targetSpecialPosition; /// /// 关联的格子ID /// public Guid CubeId; // -------------------------- /// /// 下一个状态类型(未使用) /// public int NextType = -1; /// /// 移动速度 /// public float Speed { get; set; } = 8.0f; /// /// 是否使用16x16精灵 /// [Export] public bool Use16X16Sprites { get; set; } // --- MVP: Model Binding --- /// /// 学生数据模型 /// public StudentModel Model { get; private set; } /// /// 当前状态 /// public CharacterState State { get; set; } = CharacterState.Idle; /// /// 目标方向 /// public Direction TargetDirection { get; set; } = Direction.Up; /// /// 状态队列 /// public Queue StateQueue { get; set; } = new(); /// /// 绑定数据模型 /// /// 学生模型 public void BindData(StudentModel model) { Model = model; GD.Print($"Student bound to model: {Model.Name}"); } private void SitDown(double delta) { _duration += delta; if (!(_duration > 0.3)) return; if (TargetDirection == Direction.Up) { } _animationPlayer.Play("idle_front"); State = StateQueue.Dequeue(); GlobalPosition = _targetSpecialPosition; } private void GettingUp(double delta) { _duration += delta; if (!(_duration > 0.3)) return; State = StateQueue.Dequeue(); GlobalPosition = _lastWalkablePosition; } /// /// 物理处理 /// /// 时间间隔 public override void _PhysicsProcess(double delta) { if (StateQueue.Count > 0) { // GD.Print($"{State} -> {StateQueue.Peek()}"); if (StateQueue.Peek() == CharacterState.Sitting && State == CharacterState.SittingDown) { SitDown(delta); return; // do not handle potentially conflicting command } if (StateQueue.Peek() == CharacterState.StandingUp && State == CharacterState.Sitting) { _duration = 0; State = StateQueue.Dequeue(); return; } if (StateQueue.Peek() == CharacterState.Walking && State == CharacterState.StandingUp) { GettingUp(delta); return; } } // GD.Print($"{State} -> Empty"); if (_pathToGo.Count == 0) { if (State != CharacterState.Walking) return; if (StateQueue.Count > 0) { if (StateQueue.Peek() == CharacterState.SittingDown) _duration = 0; State = StateQueue.Dequeue(); } if (State == CharacterState.Idle) _animationPlayer.Play("idle_front"); return; // No path to follow. } if (State != CharacterState.Walking) return; Vector2 velocity = new(); var nextPoint = _pathToGo.Peek(); if ((int)GlobalPosition.X == nextPoint.X && (int)GlobalPosition.Y == nextPoint.Y) { _pathToGo.Dequeue(); return; } if ((int)GlobalPosition.X == nextPoint.X) { // Move Y // velocity.Y = Math.Max(Speed, Math.Abs(nextPoint.Y - GlobalPosition.Y)); velocity.Y = Speed; if (GlobalPosition.Y > nextPoint.Y) { _animationPlayer.Play("walk_up"); velocity.Y = -velocity.Y; } else { _animationPlayer.Play("walk_down"); } } else if ((int)GlobalPosition.Y == nextPoint.Y) { // move X // velocity.X = Math.Max(Speed, Math.Abs(nextPoint.X - GlobalPosition.X)); velocity.X = Speed; if (GlobalPosition.X > nextPoint.X) { _animationPlayer.Play("walk_left"); velocity.X = -velocity.X; } else { _animationPlayer.Play("walk_right"); } } GlobalPosition = GlobalPosition with { X = GlobalPosition.X + velocity.X, Y = GlobalPosition.Y + velocity.Y }; // GD.Print($"{GlobalPosition} -> {nextPoint}"); // Velocity = velocity; // MoveAndSlide(); } /// /// 准备就绪时调用 /// public override void _Ready() { base._Ready(); // var bt = GetNode("BTPlayer"); // var bb = bt.Blackboard; // bb.BindVarToProperty("Stayed", this, "Speed"); // GD.Print(bb.GetVar("Stayed")); // GD.Print($"Speed: {Speed}"); _animationPlayer = GetNode("AnimationPlayer"); _animationPlayer.Play("idle_front"); var nameTest = GetNode("StudentName"); // MVP: Initialize Model if null if (Model == null) { Model = new StudentModel(nameTest.GenerateName()); GD.Print($"[Auto-Init] Student: {Model.Name}"); } else { GD.Print("生成的名字是: " + nameTest.GenerateName()); // Keep original log for reference } } /// /// 移动跟随路径 /// /// 路径点列表 public void MoveFollowPath(List path) { foreach (var p in path) _pathToGo.Enqueue(new Vector2I(p.X * 48 + 24, p.Y * 48 + 24)); } /// /// 随机前往某处 /// public void GoTo() { if (State == CharacterState.SittingDown || State == CharacterState.StandingUp || State == CharacterState.Walking) return; var lab = GetParentOrNull(); if (lab == null) return; var vec = Vector2I.Zero; while ((lab.GetMapNodeTypeOfPosition(vec) & Lab.MapNodeType.Walkable) == 0) { vec.X = GD.RandRange(0, Lab.MapWidth); vec.Y = GD.RandRange(0, Lab.MapHeight); } var pos = GlobalPosition; if (State == CharacterState.Sitting) { StateQueue.Enqueue(CharacterState.StandingUp); StateQueue.Enqueue(CharacterState.Walking); pos = _lastWalkablePosition; } else if (State == CharacterState.Idle) { State = CharacterState.Walking; } StateQueue.Enqueue(CharacterState.Idle); MoveFollowPath(lab.GetShortestPath(lab.Point2Coord(pos), vec)); RandomChangeBody(); } /// /// 前往座位 /// public void GoToSeat() { if (State == CharacterState.SittingDown || State == CharacterState.StandingUp || State == CharacterState.Walking) return; var lab = GetParentOrNull(); if (lab == null) return; var i = GD.RandRange(0, 1); var target = lab.GetFurSpecialPosition(CubeId, i); if (i == 1) { // Seat up TargetDirection = Direction.Up; _targetSpecialPosition = new Vector2I(target.X * 48 + 24, target.Y * 48 + 12); } else { TargetDirection = Direction.Down; _targetSpecialPosition = new Vector2I(target.X * 48 + 24, target.Y * 48 + 18); } var pos = GlobalPosition; if (State == CharacterState.Sitting) { StateQueue.Enqueue(CharacterState.StandingUp); StateQueue.Enqueue(CharacterState.Walking); pos = _lastWalkablePosition; } else if (State == CharacterState.Idle) { State = CharacterState.Walking; } StateQueue.Enqueue(CharacterState.SittingDown); StateQueue.Enqueue(CharacterState.Sitting); var path = lab.GetShortestPath(lab.Point2Coord(pos), target); _lastWalkablePosition = new Vector2I(path.Last().X * 48 + 24, path.Last().Y * 48 + 24); // if (lastWalkablePosition.X < targetSpecialPosition.X) { // TargetDirection = Direction.Right; // } else if (lastWalkablePosition.X > targetSpecialPosition.X) { // TargetDirection = Direction.Left; // } else { // if (lastWalkablePosition.Y < targetSpecialPosition.Y) { // TargetDirection = Direction.Up; // } else if (lastWalkablePosition.Y > targetSpecialPosition.Y) { // TargetDirection = Direction.Down; // } // } MoveFollowPath(path); RandomChangeBody(); } /// /// 随机更换外观 /// private void RandomChangeBody() { GetNode("parts/body").Texture = ResourceLoader.Load(Res.GetRandom(Res.Type.Body, Use16X16Sprites)); GetNode("parts/hairstyle").Texture = ResourceLoader.Load(Res.GetRandom(Res.Type.Hair, Use16X16Sprites)); GetNode("parts/outfit").Texture = ResourceLoader.Load(Res.GetRandom(Res.Type.Outfit, Use16X16Sprites)); GetNode("parts/eye").Texture = ResourceLoader.Load(Res.GetRandom(Res.Type.Eye, Use16X16Sprites)); GetNode("parts/accessory").Texture = ResourceLoader.Load(Res.GetRandom(Res.Type.Accessory, Use16X16Sprites)); GetNode("parts/smartphone").Texture = ResourceLoader.Load(Res.GetRandom(Res.Type.Phone, Use16X16Sprites)); } }