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