MVP models
This commit is contained in:
parent
8e81f2a12b
commit
70c19e7972
@ -1,34 +0,0 @@
|
|||||||
using Godot;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
[Tool]
|
|
||||||
public partial class OkToPlayMobilePhone : BTCondition
|
|
||||||
{
|
|
||||||
[Export]
|
|
||||||
public StringName target;
|
|
||||||
|
|
||||||
public override Status _Tick(double delta)
|
|
||||||
{
|
|
||||||
Student student = Agent as Student; // Cast the agent to a Student object
|
|
||||||
if (student == null) return Status.Failure;
|
|
||||||
|
|
||||||
if (student.State == Student.CharacterState.Idle) {
|
|
||||||
return Status.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (student.State == Student.CharacterState.Sitting && student.TargetDirection == Student.Direction.Down) {
|
|
||||||
return Status.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Status.Failure;
|
|
||||||
}
|
|
||||||
public override string[] _GetConfigurationWarnings()
|
|
||||||
{
|
|
||||||
return Array.Empty<string>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override string _GenerateName()
|
|
||||||
{
|
|
||||||
return "OkToPlayMobilePhone";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1 +0,0 @@
|
|||||||
uid://b3ukdvcoxly13
|
|
||||||
@ -1,79 +0,0 @@
|
|||||||
[gd_resource type="BehaviorTree" load_steps=14 format=3 uid="uid://cdsixeqsdfmc1"]
|
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://b3ukdvcoxly13" path="res://ai/tasks/OkToPlayMobilePhone.cs" id="1_lfwnu"]
|
|
||||||
|
|
||||||
[sub_resource type="BlackboardPlan" id="BlackboardPlan_gwwr7"]
|
|
||||||
resource_local_to_scene = false
|
|
||||||
resource_name = ""
|
|
||||||
|
|
||||||
[sub_resource type="BTCondition" id="BTCondition_prnjq"]
|
|
||||||
resource_local_to_scene = false
|
|
||||||
resource_name = ""
|
|
||||||
target = &""
|
|
||||||
script = ExtResource("1_lfwnu")
|
|
||||||
target = &""
|
|
||||||
|
|
||||||
[sub_resource type="BBNode" id="BBNode_7pcle"]
|
|
||||||
resource_local_to_scene = false
|
|
||||||
resource_name = "AnimationPlayer"
|
|
||||||
saved_value = NodePath("AnimationPlayer")
|
|
||||||
|
|
||||||
[sub_resource type="BTPlayAnimation" id="BTPlayAnimation_hx8gl"]
|
|
||||||
resource_local_to_scene = false
|
|
||||||
resource_name = ""
|
|
||||||
await_completion = 1.2
|
|
||||||
animation_player = SubResource("BBNode_7pcle")
|
|
||||||
animation_name = &"phone_up"
|
|
||||||
|
|
||||||
[sub_resource type="BBNode" id="BBNode_6275u"]
|
|
||||||
resource_local_to_scene = false
|
|
||||||
resource_name = "AnimationPlayer"
|
|
||||||
saved_value = NodePath("AnimationPlayer")
|
|
||||||
|
|
||||||
[sub_resource type="BTPlayAnimation" id="BTPlayAnimation_wbk5l"]
|
|
||||||
resource_local_to_scene = false
|
|
||||||
resource_name = ""
|
|
||||||
await_completion = 1.0
|
|
||||||
animation_player = SubResource("BBNode_6275u")
|
|
||||||
animation_name = &"phone_loop"
|
|
||||||
|
|
||||||
[sub_resource type="BTRepeat" id="BTRepeat_1nqee"]
|
|
||||||
resource_local_to_scene = false
|
|
||||||
resource_name = ""
|
|
||||||
children = [SubResource("BTPlayAnimation_wbk5l")]
|
|
||||||
times = 3
|
|
||||||
|
|
||||||
[sub_resource type="BTProbability" id="BTProbability_x3mt2"]
|
|
||||||
resource_local_to_scene = false
|
|
||||||
resource_name = ""
|
|
||||||
children = [SubResource("BTRepeat_1nqee")]
|
|
||||||
run_chance = 0.75
|
|
||||||
|
|
||||||
[sub_resource type="BTRepeatUntilFailure" id="BTRepeatUntilFailure_uatr3"]
|
|
||||||
resource_local_to_scene = false
|
|
||||||
resource_name = ""
|
|
||||||
children = [SubResource("BTProbability_x3mt2")]
|
|
||||||
|
|
||||||
[sub_resource type="BBNode" id="BBNode_2433i"]
|
|
||||||
resource_local_to_scene = false
|
|
||||||
resource_name = "AnimationPlayer"
|
|
||||||
saved_value = NodePath("AnimationPlayer")
|
|
||||||
|
|
||||||
[sub_resource type="BTPlayAnimation" id="BTPlayAnimation_g4biw"]
|
|
||||||
resource_local_to_scene = false
|
|
||||||
resource_name = ""
|
|
||||||
await_completion = 0.5
|
|
||||||
animation_player = SubResource("BBNode_2433i")
|
|
||||||
animation_name = &"phone_down"
|
|
||||||
|
|
||||||
[sub_resource type="BTSequence" id="BTSequence_0drub"]
|
|
||||||
resource_local_to_scene = false
|
|
||||||
resource_name = ""
|
|
||||||
children = [SubResource("BTCondition_prnjq"), SubResource("BTPlayAnimation_hx8gl"), SubResource("BTRepeatUntilFailure_uatr3"), SubResource("BTPlayAnimation_g4biw")]
|
|
||||||
|
|
||||||
[resource]
|
|
||||||
resource_local_to_scene = false
|
|
||||||
resource_name = ""
|
|
||||||
description = "WatchMobilePhone"
|
|
||||||
blackboard_plan = SubResource("BlackboardPlan_gwwr7")
|
|
||||||
root_task = SubResource("BTSequence_0drub")
|
|
||||||
@ -933,15 +933,3 @@ libraries = {
|
|||||||
|
|
||||||
[node name="StudentName" type="Node" parent="."]
|
[node name="StudentName" type="Node" parent="."]
|
||||||
script = ExtResource("8_kvqca")
|
script = ExtResource("8_kvqca")
|
||||||
|
|
||||||
[node name="BTPlayer" type="BTPlayer" parent="."]
|
|
||||||
_import_path = NodePath("")
|
|
||||||
unique_name_in_owner = false
|
|
||||||
process_mode = 0
|
|
||||||
process_priority = 0
|
|
||||||
process_physics_priority = 0
|
|
||||||
process_thread_group = 0
|
|
||||||
physics_interpolation_mode = 0
|
|
||||||
auto_translate_mode = 0
|
|
||||||
editor_description = ""
|
|
||||||
script = null
|
|
||||||
|
|||||||
@ -1,5 +1,7 @@
|
|||||||
using Godot;
|
using Godot;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Models;
|
||||||
|
|
||||||
public partial class GameManager : Node
|
public partial class GameManager : Node
|
||||||
{
|
{
|
||||||
@ -9,17 +11,154 @@ public partial class GameManager : Node
|
|||||||
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");
|
||||||
// Called when the node enters the scene tree for the first time.
|
|
||||||
|
// --- Core Loop Definitions ---
|
||||||
|
public enum GamePhase
|
||||||
|
{
|
||||||
|
Planning, // 筹备阶段:时间暂停,分配任务,购买设施
|
||||||
|
Execution, // 执行阶段:时间流动,学生工作
|
||||||
|
Review, // 结算阶段:回合结束,发工资,结算成果
|
||||||
|
}
|
||||||
|
|
||||||
|
[Export]
|
||||||
|
public int MaxTurns = 30;
|
||||||
|
|
||||||
|
// --- Global State ---
|
||||||
|
public static GamePhase CurrentPhase { get; private set; } = GamePhase.Planning;
|
||||||
|
public static int CurrentTurn { get; private set; } = 1;
|
||||||
|
|
||||||
|
// --- Domain Model ---
|
||||||
|
public MentorModel Mentor { get; private set; } = new MentorModel();
|
||||||
|
public List<StudentModel> Students { get; private set; } = new List<StudentModel>();
|
||||||
|
public List<Task> ActiveTasks { get; private set; } = new List<Task>();
|
||||||
|
|
||||||
|
// --- Signals ---
|
||||||
|
[Signal] public delegate void PhaseChangedEventHandler(int phase); // int cast of GamePhase
|
||||||
|
[Signal] public delegate void TurnChangedEventHandler(int turn);
|
||||||
|
|
||||||
|
// Singleton instance access (if needed, though Godot uses node paths)
|
||||||
|
public static GameManager Instance { get; private set; }
|
||||||
|
|
||||||
|
public override void _EnterTree()
|
||||||
|
{
|
||||||
|
Instance = this;
|
||||||
|
}
|
||||||
|
|
||||||
public override void _Ready()
|
public override void _Ready()
|
||||||
{
|
{
|
||||||
Input.SetCustomMouseCursor(Arrow2x);
|
Input.SetCustomMouseCursor(Arrow2x);
|
||||||
|
|
||||||
|
// MVP Initialization
|
||||||
|
InitializeGame();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeGame()
|
||||||
|
{
|
||||||
|
CurrentTurn = 1;
|
||||||
|
CurrentPhase = GamePhase.Planning;
|
||||||
|
|
||||||
|
// MVP: Add a test student
|
||||||
|
var s1 = new StudentModel("张三");
|
||||||
|
Students.Add(s1);
|
||||||
|
|
||||||
|
// MVP: Add a test task
|
||||||
|
var t1 = new Task("深度学习导论", TaskType.Paper, 1000f, 3);
|
||||||
|
ActiveTasks.Add(t1);
|
||||||
|
|
||||||
|
GD.Print("Game Initialized. Phase: Planning, Turn: 1");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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)
|
||||||
{
|
{
|
||||||
|
if (CurrentPhase == GamePhase.Execution)
|
||||||
|
{
|
||||||
|
// Update game logic (timers, etc.)
|
||||||
|
// In a real implementation, this might manage the global timer for the day/week
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 结束筹备阶段,开始执行
|
||||||
|
/// </summary>
|
||||||
|
public void StartExecution()
|
||||||
|
{
|
||||||
|
if (CurrentPhase != GamePhase.Planning) return;
|
||||||
|
|
||||||
|
CurrentPhase = GamePhase.Execution;
|
||||||
|
EmitSignal(SignalName.PhaseChanged, (int)CurrentPhase);
|
||||||
|
GD.Print("Phase Changed: Execution");
|
||||||
|
|
||||||
|
// Notify all agents to start working (this would be handled by a system listening to the signal)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 结束执行阶段(通常由时间耗尽或玩家手动触发),进入结算
|
||||||
|
/// </summary>
|
||||||
|
public void EndExecution()
|
||||||
|
{
|
||||||
|
if (CurrentPhase != GamePhase.Execution) return;
|
||||||
|
|
||||||
|
CurrentPhase = GamePhase.Review;
|
||||||
|
EmitSignal(SignalName.PhaseChanged, (int)CurrentPhase);
|
||||||
|
GD.Print("Phase Changed: Review");
|
||||||
|
|
||||||
|
PerformReview();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 执行结算逻辑,并准备下一回合
|
||||||
|
/// </summary>
|
||||||
|
private void PerformReview()
|
||||||
|
{
|
||||||
|
// 1. Task progress check
|
||||||
|
foreach (var task in ActiveTasks)
|
||||||
|
{
|
||||||
|
task.Deadline--;
|
||||||
|
if (task.IsCompleted)
|
||||||
|
{
|
||||||
|
GD.Print($"Task {task.Name} Completed!");
|
||||||
|
Mentor.Reputation += task.RewardReputation;
|
||||||
|
Mentor.Money += task.RewardMoney;
|
||||||
|
}
|
||||||
|
else if (task.IsFailed)
|
||||||
|
{
|
||||||
|
GD.Print($"Task {task.Name} Failed!");
|
||||||
|
Mentor.Reputation -= 10; // Penalty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Student status update (Salary, etc.)
|
||||||
|
foreach (var student in Students)
|
||||||
|
{
|
||||||
|
// Deduct salary? Restore some stamina?
|
||||||
|
}
|
||||||
|
|
||||||
|
GD.Print("Review Complete. Waiting for Next Turn confirmation.");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 开始新的回合
|
||||||
|
/// </summary>
|
||||||
|
public void StartNextTurn()
|
||||||
|
{
|
||||||
|
if (CurrentPhase != GamePhase.Review) return;
|
||||||
|
|
||||||
|
CurrentTurn++;
|
||||||
|
if (CurrentTurn > MaxTurns)
|
||||||
|
{
|
||||||
|
GD.Print("Game Over!");
|
||||||
|
// Handle Game Over
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrentPhase = GamePhase.Planning;
|
||||||
|
EmitSignal(SignalName.TurnChanged, CurrentTurn);
|
||||||
|
EmitSignal(SignalName.PhaseChanged, (int)CurrentPhase);
|
||||||
|
|
||||||
|
// Refresh resources/AP
|
||||||
|
Mentor.Energy.Current = Mentor.Energy.UpperThreshold;
|
||||||
|
|
||||||
|
GD.Print($"Turn {CurrentTurn} Started. Phase: Planning");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
40
scripts/Models/MentorModel.cs
Normal file
40
scripts/Models/MentorModel.cs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
namespace Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 导师数据模型 (MentorModel)
|
||||||
|
/// 玩家的数值状态。
|
||||||
|
/// </summary>
|
||||||
|
public class MentorModel
|
||||||
|
{
|
||||||
|
public enum MentorModeType
|
||||||
|
{
|
||||||
|
Worker,
|
||||||
|
Manager
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 精力值 (Energy)
|
||||||
|
/// 用于释放技能(画饼、PUA、甚至亲自写代码)。
|
||||||
|
/// 每回合恢复。
|
||||||
|
/// </summary>
|
||||||
|
public StatusValue Energy { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 经费 (Money/Funds)
|
||||||
|
/// 单位:元。用于发工资、买设备。
|
||||||
|
/// </summary>
|
||||||
|
public int Money { get; set; } = 50000;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 学术声望 (Reputation)
|
||||||
|
/// 影响招生质量、项目申请成功率。
|
||||||
|
/// </summary>
|
||||||
|
public int Reputation { get; set; } = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 算力/数据资源 (ResearchPoints)
|
||||||
|
/// 用于攻克高难度 AI 课题。
|
||||||
|
/// </summary>
|
||||||
|
public int ResearchPoints { get; set; } = 0;
|
||||||
|
|
||||||
|
public MentorModeType Mode { get; set; } = MentorModeType.Worker;
|
||||||
|
}
|
||||||
1
scripts/Models/MentorModel.cs.uid
Normal file
1
scripts/Models/MentorModel.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://dyehdtnucytg2
|
||||||
122
scripts/Models/PropertyValue.cs
Normal file
122
scripts/Models/PropertyValue.cs
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 属性值类型
|
||||||
|
/// 封装了整型数值,提供最大值限制(100)和显示转换。
|
||||||
|
/// </summary>
|
||||||
|
public class PropertyValue
|
||||||
|
{
|
||||||
|
public const int Min = 0;
|
||||||
|
public const int Max = 100;
|
||||||
|
|
||||||
|
private float _value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取或设置当前值。
|
||||||
|
/// 设置时会自动限制在 [Min, Max] 范围内。
|
||||||
|
/// </summary>
|
||||||
|
public float Value
|
||||||
|
{
|
||||||
|
get => _value;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_value = value;
|
||||||
|
if (_value > Max) _value = Max;
|
||||||
|
if (_value < Min) _value = Min;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public PropertyValue(int value)
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PropertyValue(float value)
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PropertyValue() : this(0) { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取显示值 (Value / 100.0f)
|
||||||
|
/// </summary>
|
||||||
|
public int Display() => (int)_value;
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Display().ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 隐式转换 (实现“重载等号”效果) ---
|
||||||
|
|
||||||
|
// 允许 int 直接赋值给 PropertyValue (创建新实例)
|
||||||
|
public static implicit operator PropertyValue(int value)
|
||||||
|
{
|
||||||
|
return new PropertyValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 允许 int 直接赋值给 PropertyValue (创建新实例)
|
||||||
|
public static implicit operator PropertyValue(float value)
|
||||||
|
{
|
||||||
|
return new PropertyValue(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 允许 PropertyValue 像 int 一样使用
|
||||||
|
public static implicit operator int(PropertyValue p)
|
||||||
|
{
|
||||||
|
return (int)p._value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 允许 PropertyValue 像 float 一样使用
|
||||||
|
public static implicit operator float(PropertyValue p)
|
||||||
|
{
|
||||||
|
return p._value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 运算符重载 (支持 int 计算) ---
|
||||||
|
|
||||||
|
public static PropertyValue operator +(PropertyValue a, int b) => new(a._value + b);
|
||||||
|
public static PropertyValue operator +(int a, PropertyValue b) => new(a + b._value);
|
||||||
|
|
||||||
|
public static PropertyValue operator -(PropertyValue a, int b) => new(a._value - b);
|
||||||
|
public static PropertyValue operator -(int a, PropertyValue b) => new(a - b._value);
|
||||||
|
|
||||||
|
public static PropertyValue operator *(PropertyValue a, int b) => new(a._value * b);
|
||||||
|
public static PropertyValue operator *(int a, PropertyValue b) => new(a * b._value);
|
||||||
|
|
||||||
|
public static PropertyValue operator /(PropertyValue a, int b)
|
||||||
|
{
|
||||||
|
return b == 0 ? throw new DivideByZeroException() : new PropertyValue(a._value / b);
|
||||||
|
}
|
||||||
|
public static PropertyValue operator /(int a, PropertyValue b)
|
||||||
|
{
|
||||||
|
return b._value == 0 ? throw new DivideByZeroException() : new PropertyValue(a / b._value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 运算符重载 (支持 float 计算) ---
|
||||||
|
|
||||||
|
public static PropertyValue operator +(PropertyValue a, float b) => new(a._value + b);
|
||||||
|
public static PropertyValue operator +(float a, PropertyValue b) => new(a + b._value);
|
||||||
|
|
||||||
|
public static PropertyValue operator -(PropertyValue a, float b) => new(a._value - b);
|
||||||
|
public static PropertyValue operator -(float a, PropertyValue b) => new(a - b._value);
|
||||||
|
|
||||||
|
public static PropertyValue operator *(PropertyValue a, float b) => new(a._value * b);
|
||||||
|
public static PropertyValue operator *(float a, PropertyValue b) => new(a * b._value);
|
||||||
|
|
||||||
|
public static PropertyValue operator /(PropertyValue a, float b) => new(a._value / b);
|
||||||
|
public static PropertyValue operator /(float a, PropertyValue b) => new(a / b._value);
|
||||||
|
|
||||||
|
// --- 运算符重载 (PropertyValue 之间) ---
|
||||||
|
|
||||||
|
public static PropertyValue operator +(PropertyValue a, PropertyValue b) => new(a._value + b._value);
|
||||||
|
public static PropertyValue operator -(PropertyValue a, PropertyValue b) => new(a._value - b._value);
|
||||||
|
public static PropertyValue operator *(PropertyValue a, PropertyValue b) => new(a._value * b._value);
|
||||||
|
public static PropertyValue operator /(PropertyValue a, PropertyValue b)
|
||||||
|
{
|
||||||
|
return b._value == 0 ? throw new DivideByZeroException() : new PropertyValue(a._value / b._value);
|
||||||
|
}
|
||||||
|
}
|
||||||
1
scripts/Models/PropertyValue.cs.uid
Normal file
1
scripts/Models/PropertyValue.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://l58i2kmspq7c
|
||||||
101
scripts/Models/StatusValue.cs
Normal file
101
scripts/Models/StatusValue.cs
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 状态值 (StatusValue)
|
||||||
|
/// 维护当前状态值以及上限和下限阈值。
|
||||||
|
/// </summary>
|
||||||
|
public class StatusValue
|
||||||
|
{
|
||||||
|
private PropertyValue _current;
|
||||||
|
private PropertyValue _upperThreshold;
|
||||||
|
private PropertyValue _lowerThreshold;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当 Current >= UpperThreshold 时触发此事件
|
||||||
|
/// </summary>
|
||||||
|
public event Action OnUpperThresholdReached;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当 Current <= LowerThreshold 时触发此事件
|
||||||
|
/// </summary>
|
||||||
|
public event Action OnLowerThresholdReached;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前状态值
|
||||||
|
/// 修改时会自动检测阈值。
|
||||||
|
/// </summary>
|
||||||
|
public PropertyValue Current
|
||||||
|
{
|
||||||
|
get => _current;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_current = value;
|
||||||
|
CheckThresholds();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 上限阈值
|
||||||
|
/// </summary>
|
||||||
|
public PropertyValue UpperThreshold
|
||||||
|
{
|
||||||
|
get => _upperThreshold;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_upperThreshold = value;
|
||||||
|
CheckThresholds();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 下限阈值
|
||||||
|
/// </summary>
|
||||||
|
public PropertyValue LowerThreshold
|
||||||
|
{
|
||||||
|
get => _lowerThreshold;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_lowerThreshold = value;
|
||||||
|
CheckThresholds();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public StatusValue(int current = 0, int upper = 100, int lower = 0)
|
||||||
|
{
|
||||||
|
_current = new PropertyValue(current);
|
||||||
|
_upperThreshold = new PropertyValue(upper);
|
||||||
|
_lowerThreshold = new PropertyValue(lower);
|
||||||
|
}
|
||||||
|
|
||||||
|
public StatusValue(PropertyValue current, PropertyValue upper, PropertyValue lower)
|
||||||
|
{
|
||||||
|
_current = current ?? new PropertyValue(0);
|
||||||
|
_upperThreshold = upper ?? new PropertyValue(100);
|
||||||
|
_lowerThreshold = lower ?? new PropertyValue(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CheckThresholds()
|
||||||
|
{
|
||||||
|
if ((int)_current >= (int)_upperThreshold)
|
||||||
|
{
|
||||||
|
OnUpperThresholdReached?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((int)_current <= (int)_lowerThreshold)
|
||||||
|
{
|
||||||
|
OnLowerThresholdReached?.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Add(int value)
|
||||||
|
{
|
||||||
|
Current += value; // 触发 setter -> CheckThresholds
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Subtract(int value)
|
||||||
|
{
|
||||||
|
Current -= value; // 触发 setter -> CheckThresholds
|
||||||
|
}
|
||||||
|
}
|
||||||
1
scripts/Models/StatusValue.cs.uid
Normal file
1
scripts/Models/StatusValue.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://no453n0ubri1
|
||||||
63
scripts/Models/StudentModel.cs
Normal file
63
scripts/Models/StudentModel.cs
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 学生数据模型 (StudentModel)
|
||||||
|
/// 包含学生的数值、状态和特质。
|
||||||
|
/// 与 Godot 的 Node (View) 分离,便于序列化和逻辑处理。
|
||||||
|
/// </summary>
|
||||||
|
public class StudentModel: UnitModel
|
||||||
|
{
|
||||||
|
public enum StudentType
|
||||||
|
{
|
||||||
|
MasterCandidate,
|
||||||
|
DoctorCandidate
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- 静态属性 (Property) ---
|
||||||
|
/// <summary>
|
||||||
|
/// 学生类型
|
||||||
|
/// </summary>
|
||||||
|
public StudentType Type { get; private set; }
|
||||||
|
|
||||||
|
// --- 动态状态 (Status) ---
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 体力 (Stamina)
|
||||||
|
/// 范围 0-100。工作消耗体力,休息恢复。体力过低效率下降。
|
||||||
|
/// </summary>
|
||||||
|
public StatusValue Stamina { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 忠诚度 (Loyalty)
|
||||||
|
/// 范围 0-100。过低可能跳槽或举报。
|
||||||
|
/// </summary>
|
||||||
|
public StatusValue Loyalty { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 年级
|
||||||
|
/// </summary>
|
||||||
|
public StatusValue Grade { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 记录对每个 Task 的贡献量,用于署名分配
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<Guid, PropertyValue> Contributions { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 特质列表 (如 "DDL战士", "卷王")
|
||||||
|
/// </summary>
|
||||||
|
public List<string> Traits { get; set; } = [];
|
||||||
|
|
||||||
|
public StudentModel(string name) : base(name)
|
||||||
|
{
|
||||||
|
var random = new Random();
|
||||||
|
Stamina = new StatusValue(random.Next(100),100, 0);
|
||||||
|
Loyalty = new StatusValue(80, 100, 0);
|
||||||
|
Type = random.Next(2) == 0 ? StudentType.MasterCandidate : StudentType.DoctorCandidate;
|
||||||
|
Grade = new StatusValue(0, Type == StudentType.DoctorCandidate ? 6 : 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
1
scripts/Models/StudentModel.cs.uid
Normal file
1
scripts/Models/StudentModel.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://eoh7yvyhct5d
|
||||||
97
scripts/Models/Task.cs
Normal file
97
scripts/Models/Task.cs
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 任务类型枚举
|
||||||
|
/// </summary>
|
||||||
|
public enum TaskType
|
||||||
|
{
|
||||||
|
Paper, // 论文:重学术(Intelligence) + 表达(Expression)
|
||||||
|
Project, // 项目:重工程(Strength) + 学术(Intelligence)
|
||||||
|
Admin // 杂务:消耗体力,低收益,但必须做
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 任务 (Task)
|
||||||
|
/// 核心游戏对象之一,相当于地图上的“怪物”。
|
||||||
|
/// 学生需要“攻击”任务(消耗工作量)来完成它。
|
||||||
|
/// </summary>
|
||||||
|
public class Task
|
||||||
|
{
|
||||||
|
public Guid Id { get; private set; } = Guid.NewGuid();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 任务名称
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 任务类型
|
||||||
|
/// </summary>
|
||||||
|
public TaskType Type { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 总工作量 (HP)
|
||||||
|
/// </summary>
|
||||||
|
public float TotalWorkload { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前进度
|
||||||
|
/// </summary>
|
||||||
|
public float CurrentProgress { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 难度系数 (Defense)
|
||||||
|
/// 影响学生攻克该任务的效率。若学生能力低于难度,效率会大幅下降。
|
||||||
|
/// </summary>
|
||||||
|
public float Difficulty { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 截止日期 (剩余回合数)
|
||||||
|
/// 归零时如果未完成,触发失败惩罚。
|
||||||
|
/// </summary>
|
||||||
|
public int Deadline { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 奖励:经费
|
||||||
|
/// </summary>
|
||||||
|
public int RewardMoney { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 奖励:声望
|
||||||
|
/// </summary>
|
||||||
|
public int RewardReputation { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 任务是否已完成
|
||||||
|
/// </summary>
|
||||||
|
public bool IsCompleted => CurrentProgress >= TotalWorkload;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 任务是否已失败(超时未完成)
|
||||||
|
/// </summary>
|
||||||
|
public bool IsFailed => Deadline <= 0 && !IsCompleted;
|
||||||
|
|
||||||
|
public Task(string name, TaskType type, float workload, int deadline)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
Type = type;
|
||||||
|
TotalWorkload = workload;
|
||||||
|
Deadline = deadline;
|
||||||
|
CurrentProgress = 0;
|
||||||
|
Difficulty = 1.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 增加进度
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="amount">工作量数值</param>
|
||||||
|
public void AddProgress(float amount)
|
||||||
|
{
|
||||||
|
CurrentProgress += amount;
|
||||||
|
if (CurrentProgress > TotalWorkload) CurrentProgress = TotalWorkload;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
1
scripts/Models/Task.cs.uid
Normal file
1
scripts/Models/Task.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://052kca2012sn
|
||||||
78
scripts/Models/UnitModel.cs
Normal file
78
scripts/Models/UnitModel.cs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
using Godot;
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 所有角色的基类,无论是导师、学生还是博后。
|
||||||
|
/// </summary>
|
||||||
|
public class UnitModel
|
||||||
|
{
|
||||||
|
public Guid Id { get; private set; } = Guid.NewGuid();
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
// 基础属性
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 学术能力,影响科研进度
|
||||||
|
/// </summary>
|
||||||
|
public PropertyValue Academic {get; set;}
|
||||||
|
/// <summary>
|
||||||
|
/// 工程能力,影响实验进度
|
||||||
|
/// </summary>
|
||||||
|
public PropertyValue Engineering {get; set;}
|
||||||
|
/// <summary>
|
||||||
|
/// 写作能力,影响论文发表
|
||||||
|
/// </summary>
|
||||||
|
public PropertyValue Writing {get; set;}
|
||||||
|
/// <summary>
|
||||||
|
/// 财务能力,影响资金相关任务
|
||||||
|
/// </summary>
|
||||||
|
public PropertyValue Financial {get; set;}
|
||||||
|
/// <summary>
|
||||||
|
/// 社交能力
|
||||||
|
/// </summary>
|
||||||
|
public PropertyValue Social {get; set;}
|
||||||
|
/// <summary>
|
||||||
|
/// 行动力,影响各种行为的效率和能动性
|
||||||
|
/// </summary>
|
||||||
|
public PropertyValue Activation {get; set;}
|
||||||
|
|
||||||
|
// 状态属性
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 压力值
|
||||||
|
/// </summary>
|
||||||
|
public StatusValue Stress {get; set;}
|
||||||
|
/// <summary>
|
||||||
|
/// 精神值 (Sanity) / 抗压能力
|
||||||
|
/// 范围 0-100。过低会导致崩溃(Breakdown)。
|
||||||
|
/// </summary>
|
||||||
|
public StatusValue Sanity { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 情绪值,影响工作效率
|
||||||
|
/// </summary>
|
||||||
|
public PropertyValue Mood {get; set;}
|
||||||
|
/// <summary>
|
||||||
|
/// 移动速度
|
||||||
|
/// </summary>
|
||||||
|
public float MoveSpeed {get; set;}
|
||||||
|
|
||||||
|
// 局内属性
|
||||||
|
|
||||||
|
public Vector2I TargetPosition {get; set;}
|
||||||
|
public Vector2I CurrentPosition {get; set;}
|
||||||
|
public Guid TargetTaskId {get; set;}
|
||||||
|
|
||||||
|
protected UnitModel(string name)
|
||||||
|
{
|
||||||
|
Name = name;
|
||||||
|
var random = new Random();
|
||||||
|
Academic = random.Next(PropertyValue.Min, PropertyValue.Max);
|
||||||
|
Engineering = random.Next(PropertyValue.Min, PropertyValue.Max);
|
||||||
|
Writing = random.Next(PropertyValue.Min, PropertyValue.Max);
|
||||||
|
Financial = random.Next(PropertyValue.Min, PropertyValue.Max);
|
||||||
|
Social = random.Next(PropertyValue.Min, PropertyValue.Max);
|
||||||
|
Activation = random.Next(PropertyValue.Min, PropertyValue.Max);
|
||||||
|
}
|
||||||
|
}
|
||||||
1
scripts/Models/UnitModel.cs.uid
Normal file
1
scripts/Models/UnitModel.cs.uid
Normal file
@ -0,0 +1 @@
|
|||||||
|
uid://hremnrofal6l
|
||||||
@ -2,6 +2,7 @@ using Godot;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Models;
|
||||||
// ReSharper disable CheckNamespace
|
// ReSharper disable CheckNamespace
|
||||||
|
|
||||||
public partial class Student : CharacterBody2D
|
public partial class Student : CharacterBody2D
|
||||||
@ -9,6 +10,16 @@ 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;
|
||||||
|
|
||||||
|
// --- 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;
|
public int NextType = -1;
|
||||||
|
|
||||||
private Queue<Vector2I> PathToGo = new();
|
private Queue<Vector2I> PathToGo = new();
|
||||||
@ -140,7 +151,15 @@ public partial class Student : CharacterBody2D
|
|||||||
animationPlayer = GetNode<AnimationPlayer>("AnimationPlayer");
|
animationPlayer = GetNode<AnimationPlayer>("AnimationPlayer");
|
||||||
animationPlayer.Play("idle_front");
|
animationPlayer.Play("idle_front");
|
||||||
var name_test = GetNode<StudentName>("StudentName");
|
var name_test = GetNode<StudentName>("StudentName");
|
||||||
GD.Print("生成的名字是: " + name_test.GenerateName());
|
|
||||||
|
// 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)
|
public void MoveFollowPath(List<Vector2I> path)
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Content Include="docs\design.md" />
|
<Content Include="docs\design.md" />
|
||||||
|
<Content Include="docs\detail_design.md" />
|
||||||
<Content Include="docs\mvp_design.md" />
|
<Content Include="docs\mvp_design.md" />
|
||||||
<Content Include="README.md" />
|
<Content Include="README.md" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user