supervisor-simulator/scripts/Student.cs
2025-12-24 23:32:36 +08:00

261 lines
7.7 KiB
C#

using Godot;
using System;
using System.Collections.Generic;
using System.Linq;
using Models;
// ReSharper disable CheckNamespace
public partial class Student : CharacterBody2D
{
public float Speed { get; set; } = 8.0f;
public const float JumpVelocity = -400.0f;
[Export] public bool Use16x16Sprites { get; set; } = false;
// --- MVP: Model Binding ---
public StudentModel Model { get; private set; }
public void BindData(StudentModel model)
{
Model = model;
GD.Print($"Student bound to model: {Model.Name}");
}
// --------------------------
public int NextType = -1;
private Queue<Vector2I> PathToGo = new();
private AnimationPlayer animationPlayer;
public enum CharacterState { Idle, Walking, Sitting, SittingDown, StandingUp }
public CharacterState State { get; set; } = CharacterState.Idle;
public enum Direction { Up, Down, Left, Right }
public Direction TargetDirection { get; set; } = Direction.Up;
public Queue<CharacterState> StateQueue { get; set; } = new();
private Vector2I targetSpecialPosition;
private Vector2I lastWalkablePosition;
private double duration = 0;
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;
}
} else {
// 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>("BTPlayer");
// var bb = bt.Blackboard;
// bb.BindVarToProperty("Stayed", this, "Speed");
// GD.Print(bb.GetVar("Stayed"));
// GD.Print($"Speed: {Speed}");
animationPlayer = GetNode<AnimationPlayer>("AnimationPlayer");
animationPlayer.Play("idle_front");
var name_test = GetNode<StudentName>("StudentName");
// MVP: Initialize Model if null
if (Model == null)
{
Model = new StudentModel(name_test.GenerateName());
GD.Print($"[Auto-Init] Student: {Model.Name}");
} else {
GD.Print("生成的名字是: " + name_test.GenerateName()); // Keep original log for reference
}
}
public void MoveFollowPath(List<Vector2I> path)
{
foreach (var p in path)
{
PathToGo.Enqueue(new Vector2I(p.X * 48 + 24, p.Y * 48 + 24));
}
}
public Guid CubeId;
public void GoTo() {
if (State == CharacterState.SittingDown || State == CharacterState.StandingUp || State == CharacterState.Walking)
{
return;
}
var lab = GetParentOrNull<Lab>();
if (lab == null)
{
return;
}
Vector2I vec = Vector2I.Zero;
while ((lab.GetMapNodeTypeOfPosition(vec) & Lab.MapNodeType.Walkable) == 0) {
vec.X = GD.RandRange(0, Lab.MAP_WIDTH);
vec.Y = GD.RandRange(0, Lab.MAP_HEIGHT);
}
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<Lab>();
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<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/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/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));
}
}