增加注释

This commit is contained in:
wjsjwr 2026-01-11 23:57:24 +08:00
parent 3a3b09c2c5
commit 541ba1695d
50 changed files with 2153 additions and 245 deletions

View File

@ -4,8 +4,8 @@ using Godot;
using Models; using Models;
/// <summary> /// <summary>
/// Runtime data for a campus agent. This keeps Godot nodes and pure data separate /// 校园代理的运行时数据。将 Godot 节点与纯数据分离,
/// so the behavior system can be tested without scene dependencies. /// 以便行为系统可以在没有场景依赖的情况下进行测试。
/// </summary> /// </summary>
public sealed class CampusAgentRuntime public sealed class CampusAgentRuntime
{ {
@ -34,8 +34,8 @@ public sealed class CampusAgentRuntime
} }
/// <summary> /// <summary>
/// Intent produced by the planner. It captures both the action and the destination, /// 规划器生成的意图。它捕获了行动和目的地,
/// plus an optional planned duration for round-based schedules. /// 加上可选的轮次计划持续时间。
/// </summary> /// </summary>
public sealed class CampusBehaviorIntent public sealed class CampusBehaviorIntent
{ {
@ -62,8 +62,8 @@ public sealed class CampusBehaviorIntent
} }
/// <summary> /// <summary>
/// Shared context passed into providers/states so they can evaluate the same data /// 传递给提供者/状态的共享上下文,以便它们可以评估相同的数据
/// without hard-coding dependencies. /// 而无需硬编码依赖关系。
/// </summary> /// </summary>
public sealed class CampusBehaviorContext public sealed class CampusBehaviorContext
{ {
@ -92,8 +92,8 @@ public sealed class CampusBehaviorContext
} }
/// <summary> /// <summary>
/// Providers represent a single rule in the priority queue. Each provider returns /// 提供者表示优先级队列中的单个规则。
/// a behavior intent or null if it cannot apply to the current context. /// 每个提供者返回一个行为意图,或者如果它不能应用于当前上下文,则返回 null。
/// </summary> /// </summary>
public interface ICampusBehaviorProvider public interface ICampusBehaviorProvider
{ {
@ -101,8 +101,8 @@ public interface ICampusBehaviorProvider
} }
/// <summary> /// <summary>
/// Critical state provider: handles sanity collapse, extreme stress, or exhaustion. /// 紧急状态提供者:处理理智崩溃、极度压力或力竭。
/// This is the highest priority in the decision queue. /// 这是决策队列中的最高优先级。
/// </summary> /// </summary>
public sealed class CriticalBehaviorProvider : ICampusBehaviorProvider public sealed class CriticalBehaviorProvider : ICampusBehaviorProvider
{ {
@ -153,7 +153,7 @@ public sealed class CriticalBehaviorProvider : ICampusBehaviorProvider
} }
/// <summary> /// <summary>
/// Assigned task provider: if the agent has a task, it is executed before needs. /// 指派任务提供者:如果代理有任务,则在需求之前执行。
/// </summary> /// </summary>
public sealed class AssignedTaskBehaviorProvider : ICampusBehaviorProvider public sealed class AssignedTaskBehaviorProvider : ICampusBehaviorProvider
{ {
@ -188,8 +188,8 @@ public sealed class AssignedTaskBehaviorProvider : ICampusBehaviorProvider
} }
/// <summary> /// <summary>
/// Needs provider: hunger, fatigue, mood, and social needs are handled here. /// 需求提供者:处理饥饿、疲劳、情绪和社交需求。
/// It sits below assigned tasks but above trait-driven idle behavior. /// 它位于指派任务之下,但在特质驱动的空闲行为之上。
/// </summary> /// </summary>
public sealed class NeedsBehaviorProvider : ICampusBehaviorProvider public sealed class NeedsBehaviorProvider : ICampusBehaviorProvider
{ {
@ -273,8 +273,7 @@ public sealed class NeedsBehaviorProvider : ICampusBehaviorProvider
} }
/// <summary> /// <summary>
/// Trait-driven provider: applies long-term personality or tag tendencies when /// 特质驱动提供者:当没有紧急需求时,应用长期性格或标签倾向。
/// there is no urgent need.
/// </summary> /// </summary>
public sealed class TraitBehaviorProvider : ICampusBehaviorProvider public sealed class TraitBehaviorProvider : ICampusBehaviorProvider
{ {
@ -405,7 +404,7 @@ public sealed class TraitBehaviorProvider : ICampusBehaviorProvider
} }
/// <summary> /// <summary>
/// Idle provider: default fallback when nothing else applies. /// 闲置提供者:当没有其他适用规则时的默认后备方案。
/// </summary> /// </summary>
public sealed class IdleBehaviorProvider : ICampusBehaviorProvider public sealed class IdleBehaviorProvider : ICampusBehaviorProvider
{ {
@ -420,8 +419,8 @@ public sealed class IdleBehaviorProvider : ICampusBehaviorProvider
} }
/// <summary> /// <summary>
/// Planner executes providers in priority order. This lets us add or remove /// 规划器按优先级顺序执行提供者。这允许我们添加或删除提供者
/// providers without editing the state machine. /// 而无需编辑状态机。
/// </summary> /// </summary>
public sealed class CampusBehaviorPlanner public sealed class CampusBehaviorPlanner
{ {
@ -448,8 +447,7 @@ public sealed class CampusBehaviorPlanner
} }
/// <summary> /// <summary>
/// State interface for the AI FSM. Each state can transition by requesting /// AI FSM 的状态接口。每个状态可以通过请求更改来转换。
/// a change via the owning behavior agent.
/// </summary> /// </summary>
public interface ICampusBehaviorState public interface ICampusBehaviorState
{ {
@ -459,7 +457,7 @@ public interface ICampusBehaviorState
} }
/// <summary> /// <summary>
/// State machine wrapper to enforce enter/exit semantics. /// 状态机包装器,用于强制执行进入/退出语义。
/// </summary> /// </summary>
public sealed class CampusBehaviorStateMachine public sealed class CampusBehaviorStateMachine
{ {
@ -479,8 +477,8 @@ public sealed class CampusBehaviorStateMachine
} }
/// <summary> /// <summary>
/// Decision state: pick a new intent and immediately transition to movement. /// 决策状态:选择一个新的意图并立即转换为移动。
/// This keeps the intent selection isolated and easy to extend. /// 这使意图选择隔离且易于扩展。
/// </summary> /// </summary>
public sealed class CampusDecisionState : ICampusBehaviorState public sealed class CampusDecisionState : ICampusBehaviorState
{ {
@ -500,8 +498,8 @@ public sealed class CampusDecisionState : ICampusBehaviorState
} }
/// <summary> /// <summary>
/// Movement state: navigate to the intent's target location. /// 移动状态:导航到意图的目标位置。
/// Once the agent arrives, it transitions into the action state. /// 一旦代理到达,它将转换为动作状态。
/// </summary> /// </summary>
public sealed class CampusMoveState : ICampusBehaviorState public sealed class CampusMoveState : ICampusBehaviorState
{ {
@ -534,8 +532,8 @@ public sealed class CampusMoveState : ICampusBehaviorState
} }
/// <summary> /// <summary>
/// Action state: apply per-second deltas and update task progress. /// 动作状态:应用每秒增量并更新任务进度。
/// When the action duration expires, transition back to decision. /// 当动作持续时间结束时,转回决策状态。
/// </summary> /// </summary>
public sealed class CampusActionState : ICampusBehaviorState public sealed class CampusActionState : ICampusBehaviorState
{ {
@ -642,8 +640,8 @@ public sealed class CampusActionState : ICampusBehaviorState
} }
/// <summary> /// <summary>
/// Main behavior agent that drives one campus character. It owns the planner, /// 驱动一个校园角色的主要行为代理。它拥有规划器、
/// state machine, and applies baseline stat decay on every tick. /// 状态机,并在每次 Tick 时应用基线属性衰减。
/// </summary> /// </summary>
public sealed class CampusBehaviorAgent public sealed class CampusBehaviorAgent
{ {
@ -1088,8 +1086,8 @@ public sealed class CampusBehaviorAgent
} }
/// <summary> /// <summary>
/// Centralized IDs for traits referenced by the behavior system. /// 行为系统引用的特质的集中 ID。
/// Keeping them here avoids scattering magic strings. /// 将它们放在这里可以避免分散的魔法字符串。
/// </summary> /// </summary>
public static class CampusTraitIds public static class CampusTraitIds
{ {
@ -1098,4 +1096,4 @@ public static class CampusTraitIds
public const string SocialButterfly = "core:trait_social_butterfly"; public const string SocialButterfly = "core:trait_social_butterfly";
public const string NotHuman = "core:trait_not_human"; public const string NotHuman = "core:trait_not_human";
public const string BigEater = "core:trait_big_eater"; public const string BigEater = "core:trait_big_eater";
} }

View File

@ -7,72 +7,72 @@ using Godot;
using Models; using Models;
/// <summary> /// <summary>
/// Location identifiers used by the campus behavior system. /// 校园行为系统使用的位置标识符。
/// These map to Node2D markers in campus.tscn so the AI can pick targets by name. /// 这些标识符映射到 campus.tscn 中的 Node2D 标记点,以便 AI 可以通过名称选择目标。
/// </summary> /// </summary>
public enum CampusLocationId public enum CampusLocationId
{ {
None, None,
Laboratory, Laboratory, // 实验室
Library, Library, // 图书馆
Canteen, Canteen, // 食堂
Dormitory, Dormitory, // 宿舍
ArtificialLake, ArtificialLake, // 人工湖
CoffeeShop, CoffeeShop, // 咖啡店
AdministrationBuilding, AdministrationBuilding, // 行政楼
FootballField, FootballField, // 足球场
RandomWander RandomWander // 随机漫游
} }
/// <summary> /// <summary>
/// Action identifiers used by the behavior planner and state machine. /// 行为规划器和状态机使用的动作标识符。
/// Each action is configured via campus_behavior.json for duration and stat deltas. /// 每个动作通过 campus_behavior.json 配置持续时间和属性变化。
/// </summary> /// </summary>
public enum CampusActionId public enum CampusActionId
{ {
None, None,
Experimenting, Experimenting, // 做实验
Writing, Writing, // 写作
Eating, Eating, // 吃饭
Sleeping, Sleeping, // 睡觉
Chilling, Chilling, // 放松/闲逛
Staring, Staring, // 发呆
CoffeeBreak, CoffeeBreak, // 喝咖啡
Administration, Administration, // 行政工作
Running, Running, // 跑步
Socializing, Socializing, // 社交
Wandering Wandering // 漫步
} }
/// <summary> /// <summary>
/// Priority levels match the design doc ordering: lower value = higher priority. /// 优先级级别,对应设计文档中的顺序:值越小优先级越高。
/// </summary> /// </summary>
public enum CampusBehaviorPriority public enum CampusBehaviorPriority
{ {
Critical = 0, Critical = 0, // 紧急状态(崩溃/力竭)
AssignedTask = 1, AssignedTask = 1, // 指派任务
Needs = 2, Needs = 2, // 基础需求(饿/累/社交)
Trait = 3, Trait = 3, // 特质驱动(性格偏好)
Idle = 4 Idle = 4 // 闲置
} }
/// <summary> /// <summary>
/// Minimal task types for the campus demo. These are not full gameplay tasks, /// 校园演示的最小化任务类型。这些不是完整的游戏任务,
/// just drivers for the assigned-task priority in the AI. /// 仅用于驱动 AI 的“指派任务”优先级。
/// </summary> /// </summary>
public enum CampusTaskType public enum CampusTaskType
{ {
Experiment, Experiment, // 实验
Writing, Writing, // 写作
Administration, Administration, // 行政
Exercise, Exercise, // 锻炼
Coding, Coding, // 编程
Social Social // 社交
} }
/// <summary> /// <summary>
/// Action configuration loaded from JSON. Deltas are applied per second while /// 从 JSON 加载的动作配置。
/// the action is running, so longer actions accumulate more effect. /// 变化量Delta在动作运行时按秒应用因此动作越长积累的效果越多。
/// </summary> /// </summary>
public sealed class CampusActionConfig public sealed class CampusActionConfig
{ {
@ -90,8 +90,8 @@ public sealed class CampusActionConfig
} }
/// <summary> /// <summary>
/// Global behavior configuration for campus AI. This is intentionally data-driven /// 校园 AI 的全局行为配置。
/// so balancing can happen in JSON without touching code. /// 这是数据驱动的,以便在不修改代码的情况下通过 JSON 进行平衡调整。
/// </summary> /// </summary>
public sealed class CampusBehaviorConfig public sealed class CampusBehaviorConfig
{ {
@ -179,8 +179,8 @@ public sealed class CampusBehaviorConfig
} }
/// <summary> /// <summary>
/// Simple location registry that maps logical location ids to scene positions. /// 简单的位置注册表,将逻辑位置 ID 映射到场景位置。
/// This keeps the behavior system independent from scene tree details. /// 保持行为系统独立于场景树细节。
/// </summary> /// </summary>
public sealed class CampusLocationRegistry public sealed class CampusLocationRegistry
{ {
@ -199,8 +199,8 @@ public sealed class CampusLocationRegistry
} }
/// <summary> /// <summary>
/// Tracks current occupancy per location so traits like social phobia can react /// 跟踪每个位置的当前占用情况,以便像社交恐惧症这样的特质可以根据人群规模做出反应,
/// to crowd size without hard-coding scene knowledge. /// 而无需硬编码场景知识。
/// </summary> /// </summary>
public sealed class CampusBehaviorWorld public sealed class CampusBehaviorWorld
{ {
@ -229,7 +229,7 @@ public sealed class CampusBehaviorWorld
} }
/// <summary> /// <summary>
/// Lightweight task container for the campus demo; it just tracks remaining work. /// 校园演示的轻量级任务容器;它只跟踪剩余工作量。
/// </summary> /// </summary>
public sealed class CampusTask public sealed class CampusTask
{ {
@ -251,8 +251,8 @@ public sealed class CampusTask
} }
/// <summary> /// <summary>
/// Custom needs that are not yet part of the core UnitModel (hunger/social/energy). /// 自定义需求,尚未成为核心 UnitModel 的一部分(饥饿/社交/精力)。
/// Uses PropertyValue so it plugs into the existing numeric system. /// 使用 PropertyValue 以便接入现有的数值系统。
/// </summary> /// </summary>
public sealed class CampusAgentNeeds public sealed class CampusAgentNeeds
{ {
@ -268,4 +268,4 @@ public sealed class CampusAgentNeeds
Social = new PropertyValue(social); Social = new PropertyValue(social);
Health = new PropertyValue(health); Health = new PropertyValue(health);
} }
} }

View File

@ -3,63 +3,238 @@ using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using Godot; using Godot;
/// <summary>
/// 校园学生角色控制器
/// </summary>
public partial class CampusStudent : CharacterBody2D public partial class CampusStudent : CharacterBody2D
{ {
/// <summary>
/// 饰品精灵
/// </summary>
private Sprite2D _accessory; private Sprite2D _accessory;
/// <summary>
/// 动画播放器
/// </summary>
private AnimationPlayer _animationPlayer; private AnimationPlayer _animationPlayer;
/// <summary>
/// 身体精灵
/// </summary>
private Sprite2D _body; private Sprite2D _body;
/// <summary>
/// 眼睛精灵
/// </summary>
private Sprite2D _eye; private Sprite2D _eye;
/// <summary>
/// 发型精灵
/// </summary>
private Sprite2D _hairstyle; private Sprite2D _hairstyle;
/// <summary>
/// 上一帧的时间间隔
/// </summary>
private double _lastDelta; private double _lastDelta;
/// <summary>
/// 面朝方向
/// </summary>
private enum FacingDirection
{
Up,
Down,
Left,
Right
}
/// <summary>
/// 上一次的面朝方向
/// </summary>
private FacingDirection _lastFacing = FacingDirection.Down; private FacingDirection _lastFacing = FacingDirection.Down;
/// <summary>
/// 上一次的位置
/// </summary>
private Vector2 _lastPosition; private Vector2 _lastPosition;
/// <summary>
/// 导航代理
/// </summary>
private NavigationAgent2D _navigationAgent; private NavigationAgent2D _navigationAgent;
/// <summary>
/// 服装精灵
/// </summary>
private Sprite2D _outfit; private Sprite2D _outfit;
/// <summary>
/// 是否已配置巡逻
/// </summary>
private bool _patrolConfigured; private bool _patrolConfigured;
/// <summary>
/// 当前巡逻点索引
/// </summary>
private int _patrolIndex; private int _patrolIndex;
/// <summary>
/// 巡逻点列表
/// </summary>
private List<Vector2> _patrolPoints = new(); private List<Vector2> _patrolPoints = new();
/// <summary>
/// 手机精灵
/// </summary>
private Sprite2D _smartphone; private Sprite2D _smartphone;
/// <summary>
/// 卡住计时器
/// </summary>
private float _stuckTimer; private float _stuckTimer;
/// <summary>
/// 导航地图RID
/// </summary>
private Rid _navigationMap; private Rid _navigationMap;
/// <summary>
/// 当前目标点
/// </summary>
private Vector2 _currentTarget = Vector2.Zero; private Vector2 _currentTarget = Vector2.Zero;
/// <summary>
/// 是否有目标
/// </summary>
private bool _hasTarget; private bool _hasTarget;
/// <summary>
/// 是否启用行为控制
/// </summary>
private bool _behaviorControlEnabled; private bool _behaviorControlEnabled;
/// <summary>
/// 行为目标点
/// </summary>
private Vector2 _behaviorTarget = Vector2.Zero; private Vector2 _behaviorTarget = Vector2.Zero;
/// <summary>
/// 是否有行为目标
/// </summary>
private bool _behaviorHasTarget; private bool _behaviorHasTarget;
/// <summary>
/// 手机闲置动画是否激活
/// </summary>
private bool _phoneIdleActive; private bool _phoneIdleActive;
/// <summary>
/// 手机退出动作锁定
/// </summary>
private bool _phoneExitLocked; private bool _phoneExitLocked;
/// <summary>
/// 是否使用物理移动
/// </summary>
private bool _usePhysicsMovement = true; private bool _usePhysicsMovement = true;
/// <summary>
/// 网格路径列表
/// </summary>
private readonly List<Vector2> _gridPath = new(); private readonly List<Vector2> _gridPath = new();
/// <summary>
/// 当前网格路径索引
/// </summary>
private int _gridPathIndex; private int _gridPathIndex;
/// <summary>
/// 网格路径是否激活
/// </summary>
private bool _gridPathActive; private bool _gridPathActive;
/// <summary>
/// 网格路径是否挂起
/// </summary>
private bool _gridPathPending; private bool _gridPathPending;
/// <summary>
/// AStar网格
/// </summary>
private AStarGrid2D _astarGrid; private AStarGrid2D _astarGrid;
/// <summary>
/// AStar区域
/// </summary>
private Rect2I _astarRegion; private Rect2I _astarRegion;
/// <summary>
/// AStar地图迭代版本
/// </summary>
private int _astarMapIteration; private int _astarMapIteration;
/// <summary>
/// 网格重新寻路重试计时器
/// </summary>
private float _gridPathRetryTimer; private float _gridPathRetryTimer;
/// <summary>
/// 导航区域引用
/// </summary>
private NavigationRegion2D _navigationRegion; private NavigationRegion2D _navigationRegion;
/// <summary>
/// 移动速度
/// </summary>
[Export] public float MoveSpeed { get; set; } = 60.0f; [Export] public float MoveSpeed { get; set; } = 60.0f;
/// <summary>
/// 目标到达判定距离
/// </summary>
[Export] public float TargetReachDistance { get; set; } = 6.0f; [Export] public float TargetReachDistance { get; set; } = 6.0f;
/// <summary>
/// 是否使用16x16精灵
/// </summary>
[Export] public bool Use16X16Sprites { get; set; } = true; [Export] public bool Use16X16Sprites { get; set; } = true;
/// <summary>
/// 是否启用避让
/// </summary>
[Export] public bool EnableAvoidance { get; set; } [Export] public bool EnableAvoidance { get; set; }
/// <summary>
/// 卡住重新寻路时间
/// </summary>
[Export] public float StuckRepathSeconds { get; set; } = 0.6f; [Export] public float StuckRepathSeconds { get; set; } = 0.6f;
/// <summary>
/// 卡住距离阈值
/// </summary>
[Export] public float StuckDistanceEpsilon { get; set; } = 2.0f; [Export] public float StuckDistanceEpsilon { get; set; } = 2.0f;
/// <summary>
/// 导航网格吸附距离
/// </summary>
[Export] public float NavMeshClampDistance { get; set; } = 6.0f; [Export] public float NavMeshClampDistance { get; set; } = 6.0f;
/// <summary>
/// 是否使用网格寻路
/// </summary>
[Export] public bool UseGridPathfinding { get; set; } = true; [Export] public bool UseGridPathfinding { get; set; } = true;
/// <summary>
/// 网格单元大小
/// </summary>
[Export] public float GridCellSize { get; set; } = 8.0f; [Export] public float GridCellSize { get; set; } = 8.0f;
/// <summary>
/// 网格可行走容差
/// </summary>
[Export] public float GridWalkableTolerance { get; set; } = 2.0f; [Export] public float GridWalkableTolerance { get; set; } = 2.0f;
/// <summary>
/// 网格搜索节点限制
/// </summary>
[Export] public int GridSearchNodeLimit { get; set; } = 8000; [Export] public int GridSearchNodeLimit { get; set; } = 8000;
/// <summary>
/// 网格重新寻路间隔
/// </summary>
[Export] public float GridRepathInterval { get; set; } = 0.25f; [Export] public float GridRepathInterval { get; set; } = 0.25f;
/// <summary>
/// 调试绘制网格
/// </summary>
[Export] public bool DebugDrawGrid { get; set; } [Export] public bool DebugDrawGrid { get; set; }
/// <summary>
/// 调试仅绘制实心点
/// </summary>
[Export] public bool DebugDrawSolidOnly { get; set; } = true; [Export] public bool DebugDrawSolidOnly { get; set; } = true;
/// <summary>
/// 调试绘制路径
/// </summary>
[Export] public bool DebugDrawPath { get; set; } [Export] public bool DebugDrawPath { get; set; }
/// <summary>
/// 调试绘制半径单元数
/// </summary>
[Export] public int DebugDrawRadiusCells { get; set; } = 20; [Export] public int DebugDrawRadiusCells { get; set; } = 20;
/// <summary>
/// 调试日志网格
/// </summary>
[Export] public bool DebugLogGrid { get; set; } [Export] public bool DebugLogGrid { get; set; }
/// <summary>
/// 环境碰撞掩码
/// </summary>
[Export] public uint EnvironmentCollisionMask { get; set; } = 1u; [Export] public uint EnvironmentCollisionMask { get; set; } = 1u;
/// <summary>
/// 学生碰撞层
/// </summary>
[Export] public uint StudentCollisionLayer { get; set; } = 1u << 1; [Export] public uint StudentCollisionLayer { get; set; } = 1u << 1;
/// <summary>
/// 准备就绪时调用
/// </summary>
public override void _Ready() public override void _Ready()
{ {
_navigationAgent = GetNodeOrNull<NavigationAgent2D>("NavigationAgent2D"); _navigationAgent = GetNodeOrNull<NavigationAgent2D>("NavigationAgent2D");
@ -94,6 +269,10 @@ public partial class CampusStudent : CharacterBody2D
_lastPosition = GlobalPosition; _lastPosition = GlobalPosition;
} }
/// <summary>
/// 物理处理
/// </summary>
/// <param name="delta">时间间隔</param>
public override void _PhysicsProcess(double delta) public override void _PhysicsProcess(double delta)
{ {
_lastDelta = delta; _lastDelta = delta;
@ -212,6 +391,9 @@ public partial class CampusStudent : CharacterBody2D
} }
} }
/// <summary>
/// 绘制调试信息
/// </summary>
public override void _Draw() public override void _Draw()
{ {
if (!DebugDrawGrid && !DebugDrawPath) return; if (!DebugDrawGrid && !DebugDrawPath) return;
@ -269,6 +451,11 @@ public partial class CampusStudent : CharacterBody2D
} }
} }
/// <summary>
/// 配置巡逻
/// </summary>
/// <param name="points">巡逻点列表</param>
/// <param name="startIndex">起始索引</param>
public void ConfigurePatrol(List<Vector2> points, int startIndex) public void ConfigurePatrol(List<Vector2> points, int startIndex)
{ {
_patrolPoints = points ?? new List<Vector2>(); _patrolPoints = points ?? new List<Vector2>();
@ -281,6 +468,9 @@ public partial class CampusStudent : CharacterBody2D
if (_navigationAgent != null) AdvanceTarget(); if (_navigationAgent != null) AdvanceTarget();
} }
/// <summary>
/// 启用行为控制
/// </summary>
public void EnableBehaviorControl() public void EnableBehaviorControl()
{ {
_behaviorControlEnabled = true; _behaviorControlEnabled = true;
@ -289,6 +479,10 @@ public partial class CampusStudent : CharacterBody2D
_patrolPoints.Clear(); _patrolPoints.Clear();
} }
/// <summary>
/// 设置行为目标
/// </summary>
/// <param name="target">目标位置</param>
public void SetBehaviorTarget(Vector2 target) public void SetBehaviorTarget(Vector2 target)
{ {
_behaviorControlEnabled = true; _behaviorControlEnabled = true;
@ -316,6 +510,9 @@ public partial class CampusStudent : CharacterBody2D
} }
} }
/// <summary>
/// 清除行为目标
/// </summary>
public void ClearBehaviorTarget() public void ClearBehaviorTarget()
{ {
_behaviorHasTarget = false; _behaviorHasTarget = false;
@ -329,6 +526,10 @@ public partial class CampusStudent : CharacterBody2D
_gridPath.Clear(); _gridPath.Clear();
} }
/// <summary>
/// 是否已到达行为目标
/// </summary>
/// <returns>到达返回true</returns>
public bool HasReachedBehaviorTarget() public bool HasReachedBehaviorTarget()
{ {
if (!_behaviorHasTarget) return true; if (!_behaviorHasTarget) return true;
@ -344,6 +545,9 @@ public partial class CampusStudent : CharacterBody2D
return _navigationAgent.IsNavigationFinished(); return _navigationAgent.IsNavigationFinished();
} }
/// <summary>
/// 开始玩手机
/// </summary>
public void StartPhoneIdle() public void StartPhoneIdle()
{ {
if (_animationPlayer == null || !_animationPlayer.HasAnimation("phone_up")) return; if (_animationPlayer == null || !_animationPlayer.HasAnimation("phone_up")) return;
@ -354,6 +558,10 @@ public partial class CampusStudent : CharacterBody2D
_animationPlayer.Play("phone_up"); _animationPlayer.Play("phone_up");
} }
/// <summary>
/// 停止玩手机
/// </summary>
/// <param name="lockMovement">是否锁定移动</param>
public void StopPhoneIdle(bool lockMovement = false) public void StopPhoneIdle(bool lockMovement = false)
{ {
if (_animationPlayer == null) if (_animationPlayer == null)
@ -386,6 +594,10 @@ public partial class CampusStudent : CharacterBody2D
} }
} }
/// <summary>
/// 设置导航地图
/// </summary>
/// <param name="map">地图RID</param>
public void SetNavigationMap(Rid map) public void SetNavigationMap(Rid map)
{ {
// 由校园控制器传入导航地图,供本地边界夹紧使用 // 由校园控制器传入导航地图,供本地边界夹紧使用
@ -396,6 +608,9 @@ public partial class CampusStudent : CharacterBody2D
_navigationRegion = FindNavigationRegion(); _navigationRegion = FindNavigationRegion();
} }
/// <summary>
/// 应用随机主题
/// </summary>
public void ApplyRandomTheme() public void ApplyRandomTheme()
{ {
// 随机替换身体与配件贴图,形成不同主题外观 // 随机替换身体与配件贴图,形成不同主题外观
@ -410,6 +625,9 @@ public partial class CampusStudent : CharacterBody2D
_smartphone.Texture = ResourceLoader.Load<Texture2D>(Res.GetRandom(Res.Type.Phone, Use16X16Sprites)); _smartphone.Texture = ResourceLoader.Load<Texture2D>(Res.GetRandom(Res.Type.Phone, Use16X16Sprites));
} }
/// <summary>
/// 缓存精灵节点
/// </summary>
private void CacheSprites() private void CacheSprites()
{ {
// 缓存子节点引用,避免每帧查找 // 缓存子节点引用,避免每帧查找
@ -421,6 +639,9 @@ public partial class CampusStudent : CharacterBody2D
_smartphone = GetNode<Sprite2D>("parts/smartphone"); _smartphone = GetNode<Sprite2D>("parts/smartphone");
} }
/// <summary>
/// 配置碰撞
/// </summary>
private void ConfigureCollision() private void ConfigureCollision()
{ {
// 学生只与环境碰撞,不与其它学生碰撞 // 学生只与环境碰撞,不与其它学生碰撞
@ -429,6 +650,9 @@ public partial class CampusStudent : CharacterBody2D
_usePhysicsMovement = true; _usePhysicsMovement = true;
} }
/// <summary>
/// 推进到下一个目标
/// </summary>
private void AdvanceTarget() private void AdvanceTarget()
{ {
if (_patrolPoints.Count == 0 || _navigationAgent == null) return; if (_patrolPoints.Count == 0 || _navigationAgent == null) return;
@ -455,6 +679,10 @@ public partial class CampusStudent : CharacterBody2D
_stuckTimer = 0.0f; _stuckTimer = 0.0f;
} }
/// <summary>
/// 计算避让速度时的回调
/// </summary>
/// <param name="safeVelocity">安全速度</param>
private void OnVelocityComputed(Vector2 safeVelocity) private void OnVelocityComputed(Vector2 safeVelocity)
{ {
if (_phoneExitLocked) if (_phoneExitLocked)
@ -473,6 +701,10 @@ public partial class CampusStudent : CharacterBody2D
UpdateStuckTimer(_lastDelta); UpdateStuckTimer(_lastDelta);
} }
/// <summary>
/// 更新卡住计时器
/// </summary>
/// <param name="delta">时间间隔</param>
private void UpdateStuckTimer(double delta) private void UpdateStuckTimer(double delta)
{ {
if (StuckRepathSeconds <= 0.0f || !_hasTarget) if (StuckRepathSeconds <= 0.0f || !_hasTarget)
@ -496,6 +728,9 @@ public partial class CampusStudent : CharacterBody2D
} }
} }
/// <summary>
/// 重新规划路径到当前目标
/// </summary>
private void RepathToCurrentTarget() private void RepathToCurrentTarget()
{ {
if (!_hasTarget) return; if (!_hasTarget) return;
@ -516,6 +751,9 @@ public partial class CampusStudent : CharacterBody2D
_navigationAgent.TargetPosition = _currentTarget; _navigationAgent.TargetPosition = _currentTarget;
} }
/// <summary>
/// 尝试构建挂起的网格路径
/// </summary>
private void TryBuildPendingGridPath() private void TryBuildPendingGridPath()
{ {
if (!UseGridPathfinding || !_gridPathPending) return; if (!UseGridPathfinding || !_gridPathPending) return;
@ -540,11 +778,20 @@ public partial class CampusStudent : CharacterBody2D
} }
} }
/// <summary>
/// 导航地图是否准备就绪
/// </summary>
/// <returns>准备就绪返回true</returns>
private bool IsNavigationMapReady() private bool IsNavigationMapReady()
{ {
return _navigationMap.IsValid && NavigationServer2D.MapGetIterationId(_navigationMap) > 0; return _navigationMap.IsValid && NavigationServer2D.MapGetIterationId(_navigationMap) > 0;
} }
/// <summary>
/// 将速度限制在导航网格内
/// </summary>
/// <param name="velocity">目标速度</param>
/// <returns>限制后的速度</returns>
private Vector2 ClampVelocityToNavMesh(Vector2 velocity) private Vector2 ClampVelocityToNavMesh(Vector2 velocity)
{ {
if (!IsNavigationMapReady()) return velocity; if (!IsNavigationMapReady()) return velocity;
@ -566,6 +813,11 @@ public partial class CampusStudent : CharacterBody2D
return velocity; return velocity;
} }
/// <summary>
/// 将目标点限制在导航网格内
/// </summary>
/// <param name="target">目标点</param>
/// <returns>限制后的目标点</returns>
private Vector2 ClampTargetToNavMesh(Vector2 target) private Vector2 ClampTargetToNavMesh(Vector2 target)
{ {
if (!IsNavigationMapReady()) return target; if (!IsNavigationMapReady()) return target;
@ -579,6 +831,10 @@ public partial class CampusStudent : CharacterBody2D
return closest; return closest;
} }
/// <summary>
/// 应用移动
/// </summary>
/// <param name="delta">时间间隔</param>
private void ApplyMovement(double delta) private void ApplyMovement(double delta)
{ {
if (_usePhysicsMovement) if (_usePhysicsMovement)
@ -591,6 +847,10 @@ public partial class CampusStudent : CharacterBody2D
GlobalPosition += Velocity * (float)delta; GlobalPosition += Velocity * (float)delta;
} }
/// <summary>
/// 更新面朝向动画
/// </summary>
/// <param name="velocity">当前速度</param>
private void UpdateFacingAnimation(Vector2 velocity) private void UpdateFacingAnimation(Vector2 velocity)
{ {
if (_phoneIdleActive || _phoneExitLocked) if (_phoneIdleActive || _phoneExitLocked)
@ -626,6 +886,9 @@ public partial class CampusStudent : CharacterBody2D
} }
} }
/// <summary>
/// 播放闲置动画
/// </summary>
private void PlayIdleAnimation() private void PlayIdleAnimation()
{ {
if (_phoneIdleActive || _phoneExitLocked) if (_phoneIdleActive || _phoneExitLocked)
@ -650,6 +913,10 @@ public partial class CampusStudent : CharacterBody2D
} }
} }
/// <summary>
/// 播放指定动画
/// </summary>
/// <param name="animationName">动画名称</param>
private void PlayAnimation(string animationName) private void PlayAnimation(string animationName)
{ {
if (_animationPlayer == null) return; if (_animationPlayer == null) return;
@ -657,6 +924,10 @@ public partial class CampusStudent : CharacterBody2D
if (_animationPlayer.CurrentAnimation != animationName) _animationPlayer.Play(animationName); if (_animationPlayer.CurrentAnimation != animationName) _animationPlayer.Play(animationName);
} }
/// <summary>
/// 动画结束回调
/// </summary>
/// <param name="animationName">动画名称</param>
private void OnAnimationFinished(StringName animationName) private void OnAnimationFinished(StringName animationName)
{ {
if (_animationPlayer == null) return; if (_animationPlayer == null) return;
@ -677,6 +948,11 @@ public partial class CampusStudent : CharacterBody2D
} }
} }
/// <summary>
/// 处理网格路径移动
/// </summary>
/// <param name="delta">时间间隔</param>
/// <returns>如果正在移动返回true</returns>
private bool ProcessGridPathMovement(double delta) private bool ProcessGridPathMovement(double delta)
{ {
if (_gridPathIndex >= _gridPath.Count) if (_gridPathIndex >= _gridPath.Count)
@ -730,6 +1006,11 @@ public partial class CampusStudent : CharacterBody2D
return true; return true;
} }
/// <summary>
/// 转换为轴向速度
/// </summary>
/// <param name="delta">位移</param>
/// <returns>轴向速度</returns>
private Vector2 ToAxisVelocity(Vector2 delta) private Vector2 ToAxisVelocity(Vector2 delta)
{ {
if (Mathf.Abs(delta.X) >= Mathf.Abs(delta.Y)) if (Mathf.Abs(delta.X) >= Mathf.Abs(delta.Y))
@ -740,6 +1021,12 @@ public partial class CampusStudent : CharacterBody2D
return new Vector2(0, Mathf.Sign(delta.Y)); return new Vector2(0, Mathf.Sign(delta.Y));
} }
/// <summary>
/// 构建网格路径
/// </summary>
/// <param name="start">起点</param>
/// <param name="target">终点</param>
/// <returns>路径点列表</returns>
private List<Vector2> BuildGridPath(Vector2 start, Vector2 target) private List<Vector2> BuildGridPath(Vector2 start, Vector2 target)
{ {
if (!IsNavigationMapReady() || GridCellSize <= 0.0f) if (!IsNavigationMapReady() || GridCellSize <= 0.0f)
@ -809,6 +1096,10 @@ public partial class CampusStudent : CharacterBody2D
return SimplifyGridPath(result); return SimplifyGridPath(result);
} }
/// <summary>
/// 移除直接回头的路径点
/// </summary>
/// <param name="path">路径</param>
private void RemoveImmediateBacktracks(List<Vector2> path) private void RemoveImmediateBacktracks(List<Vector2> path)
{ {
if (path == null || path.Count < 3) return; if (path == null || path.Count < 3) return;
@ -826,6 +1117,11 @@ public partial class CampusStudent : CharacterBody2D
} }
} }
/// <summary>
/// 简化网格路径(移除共线点)
/// </summary>
/// <param name="path">路径</param>
/// <returns>简化后的路径</returns>
private List<Vector2> SimplifyGridPath(List<Vector2> path) private List<Vector2> SimplifyGridPath(List<Vector2> path)
{ {
if (path == null || path.Count < 3) return path; if (path == null || path.Count < 3) return path;
@ -863,6 +1159,9 @@ public partial class CampusStudent : CharacterBody2D
return simplified; return simplified;
} }
/// <summary>
/// 确保AStar网格已建立
/// </summary>
private void EnsureAStarGrid() private void EnsureAStarGrid()
{ {
if (!IsNavigationMapReady()) return; if (!IsNavigationMapReady()) return;
@ -916,6 +1215,10 @@ public partial class CampusStudent : CharacterBody2D
} }
} }
/// <summary>
/// 构建网格区域
/// </summary>
/// <returns>网格区域</returns>
private Rect2I BuildGridRegion() private Rect2I BuildGridRegion()
{ {
var bounds = BuildWorldBounds(); var bounds = BuildWorldBounds();
@ -929,6 +1232,10 @@ public partial class CampusStudent : CharacterBody2D
return new Rect2I(new Vector2I(minX, minY), new Vector2I(sizeX, sizeY)); return new Rect2I(new Vector2I(minX, minY), new Vector2I(sizeX, sizeY));
} }
/// <summary>
/// 构建世界边界
/// </summary>
/// <returns>世界边界</returns>
private Rect2 BuildWorldBounds() private Rect2 BuildWorldBounds()
{ {
var viewport = GetViewportRect(); var viewport = GetViewportRect();
@ -979,6 +1286,11 @@ public partial class CampusStudent : CharacterBody2D
return bounds.Merge(navBounds); return bounds.Merge(navBounds);
} }
/// <summary>
/// 单元格是否在区域内
/// </summary>
/// <param name="cell">单元格</param>
/// <returns>如果在区域内返回true</returns>
private bool IsCellInRegion(Vector2I cell) private bool IsCellInRegion(Vector2I cell)
{ {
return cell.X >= _astarRegion.Position.X return cell.X >= _astarRegion.Position.X
@ -987,6 +1299,11 @@ public partial class CampusStudent : CharacterBody2D
&& cell.Y < _astarRegion.Position.Y + _astarRegion.Size.Y; && cell.Y < _astarRegion.Position.Y + _astarRegion.Size.Y;
} }
/// <summary>
/// 单元格是否在边界内
/// </summary>
/// <param name="cell">单元格</param>
/// <returns>如果在边界内返回true</returns>
private bool IsCellInBounds(Vector2I cell) private bool IsCellInBounds(Vector2I cell)
{ {
if (_astarGrid != null) if (_astarGrid != null)
@ -997,6 +1314,11 @@ public partial class CampusStudent : CharacterBody2D
return IsCellInRegion(cell); return IsCellInRegion(cell);
} }
/// <summary>
/// 世界坐标转网格坐标
/// </summary>
/// <param name="world">世界坐标</param>
/// <returns>网格坐标</returns>
private Vector2I WorldToGrid(Vector2 world) private Vector2I WorldToGrid(Vector2 world)
{ {
var size = Mathf.Max(1.0f, GridCellSize); var size = Mathf.Max(1.0f, GridCellSize);
@ -1005,12 +1327,23 @@ public partial class CampusStudent : CharacterBody2D
Mathf.FloorToInt(world.Y / size)); Mathf.FloorToInt(world.Y / size));
} }
/// <summary>
/// 网格坐标转世界坐标
/// </summary>
/// <param name="grid">网格坐标</param>
/// <returns>世界坐标</returns>
private Vector2 GridToWorld(Vector2I grid) private Vector2 GridToWorld(Vector2I grid)
{ {
var size = Mathf.Max(1.0f, GridCellSize); var size = Mathf.Max(1.0f, GridCellSize);
return new Vector2((grid.X + 0.5f) * size, (grid.Y + 0.5f) * size); return new Vector2((grid.X + 0.5f) * size, (grid.Y + 0.5f) * size);
} }
/// <summary>
/// 查找最近的开放单元格
/// </summary>
/// <param name="origin">起点</param>
/// <param name="radius">搜索半径</param>
/// <returns>最近的开放单元格</returns>
private Vector2I FindNearestOpenCell(Vector2I origin, int radius) private Vector2I FindNearestOpenCell(Vector2I origin, int radius)
{ {
if (_astarGrid == null) return origin; if (_astarGrid == null) return origin;
@ -1039,6 +1372,11 @@ public partial class CampusStudent : CharacterBody2D
return origin; return origin;
} }
/// <summary>
/// 世界坐标是否可行走
/// </summary>
/// <param name="world">世界坐标</param>
/// <returns>如果可行走返回true</returns>
private bool IsWorldWalkable(Vector2 world) private bool IsWorldWalkable(Vector2 world)
{ {
if (!IsNavigationMapReady()) return false; if (!IsNavigationMapReady()) return false;
@ -1046,11 +1384,20 @@ public partial class CampusStudent : CharacterBody2D
return closest.DistanceTo(world) <= GetGridTolerance(); return closest.DistanceTo(world) <= GetGridTolerance();
} }
/// <summary>
/// 查找导航区域节点
/// </summary>
/// <returns>导航区域</returns>
private NavigationRegion2D FindNavigationRegion() private NavigationRegion2D FindNavigationRegion()
{ {
return GetTree()?.CurrentScene?.FindChild("NavigationRegion2D", true, false) as NavigationRegion2D; return GetTree()?.CurrentScene?.FindChild("NavigationRegion2D", true, false) as NavigationRegion2D;
} }
/// <summary>
/// 单元格中心是否可行走
/// </summary>
/// <param name="center">中心点</param>
/// <returns>如果可行走返回true</returns>
private bool IsCellWalkable(Vector2 center) private bool IsCellWalkable(Vector2 center)
{ {
if (!IsWorldWalkable(center)) return false; if (!IsWorldWalkable(center)) return false;
@ -1079,16 +1426,12 @@ public partial class CampusStudent : CharacterBody2D
return true; return true;
} }
/// <summary>
/// 获取网格容差
/// </summary>
/// <returns>网格容差</returns>
private float GetGridTolerance() private float GetGridTolerance()
{ {
return Mathf.Max(1.0f, GridWalkableTolerance); return Mathf.Max(1.0f, GridWalkableTolerance);
} }
}
private enum FacingDirection
{
Up,
Down,
Left,
Right
}
}

View File

@ -10,8 +10,15 @@ namespace Core;
[GlobalClass] [GlobalClass]
public partial class ContentCollectionResource : Resource, IContentResourceCollection public partial class ContentCollectionResource : Resource, IContentResourceCollection
{ {
/// <summary>
/// 资源列表
/// </summary>
[Export] public Array<Resource> Items { get; set; } = new(); [Export] public Array<Resource> Items { get; set; } = new();
/// <summary>
/// 获取资源项
/// </summary>
/// <returns>资源项集合</returns>
public IEnumerable<IContentResource> GetItems() public IEnumerable<IContentResource> GetItems()
{ {
foreach (var item in Items) foreach (var item in Items)
@ -22,5 +29,4 @@ public partial class ContentCollectionResource : Resource, IContentResourceColle
} }
} }
} }
} }

View File

@ -20,27 +20,52 @@ namespace Core;
/// </summary> /// </summary>
public interface IContentSource public interface IContentSource
{ {
/// <summary>
/// 优先级
/// </summary>
int Priority { get; } int Priority { get; }
/// <summary>
/// 加载所有指定类型的对象
/// </summary>
/// <typeparam name="T">对象类型</typeparam>
/// <returns>对象集合</returns>
IEnumerable<T> LoadAll<T>() where T : class; IEnumerable<T> LoadAll<T>() where T : class;
} }
/// <summary>
/// 内容合并模式
/// </summary>
public enum ContentMergeMode public enum ContentMergeMode
{ {
Override, Override, // 覆盖
KeepFirst KeepFirst // 保留首次
} }
/// <summary>
/// 内容注册表
/// </summary>
public sealed class ContentRegistry public sealed class ContentRegistry
{ {
private readonly List<IContentSource> _sources = new(); private readonly List<IContentSource> _sources = new();
/// <summary>
/// 合并模式
/// </summary>
public ContentMergeMode MergeMode { get; set; } = ContentMergeMode.Override; public ContentMergeMode MergeMode { get; set; } = ContentMergeMode.Override;
/// <summary>
/// 注册内容源
/// </summary>
/// <param name="source">内容源</param>
public void RegisterSource(IContentSource source) public void RegisterSource(IContentSource source)
{ {
_sources.Add(source); _sources.Add(source);
_sources.Sort((a, b) => a.Priority.CompareTo(b.Priority)); _sources.Sort((a, b) => a.Priority.CompareTo(b.Priority));
} }
/// <summary>
/// 构建游戏内容数据库
/// </summary>
/// <returns>游戏内容数据库</returns>
public GameContentDatabase BuildDatabase() public GameContentDatabase BuildDatabase()
{ {
var db = new GameContentDatabase(); var db = new GameContentDatabase();
@ -55,6 +80,11 @@ public sealed class ContentRegistry
return db; return db;
} }
/// <summary>
/// 从所有源加载指定类型的所有对象
/// </summary>
/// <typeparam name="T">对象类型</typeparam>
/// <returns>对象集合</returns>
private IEnumerable<T> LoadAll<T>() where T : class private IEnumerable<T> LoadAll<T>() where T : class
{ {
foreach (var source in _sources) foreach (var source in _sources)
@ -66,6 +96,13 @@ public sealed class ContentRegistry
} }
} }
/// <summary>
/// 合并对象到目标字典
/// </summary>
/// <typeparam name="T">对象类型</typeparam>
/// <param name="target">目标字典</param>
/// <param name="items">对象集合</param>
/// <param name="idSelector">ID选择器</param>
private void Merge<T>(Dictionary<string, T> target, IEnumerable<T> items, Func<T, string> idSelector) where T : class private void Merge<T>(Dictionary<string, T> target, IEnumerable<T> items, Func<T, string> idSelector) where T : class
{ {
foreach (var item in items) foreach (var item in items)
@ -97,14 +134,29 @@ public sealed class ContentRegistry
/// </summary> /// </summary>
public sealed class ResourceContentSource : IContentSource public sealed class ResourceContentSource : IContentSource
{ {
/// <summary>
/// 优先级
/// </summary>
public int Priority { get; } public int Priority { get; }
/// <summary>
/// 资源路径列表
/// </summary>
public List<string> ResourcePaths { get; } = new(); public List<string> ResourcePaths { get; } = new();
/// <summary>
/// 构造函数
/// </summary>
/// <param name="priority">优先级</param>
public ResourceContentSource(int priority) public ResourceContentSource(int priority)
{ {
Priority = priority; Priority = priority;
} }
/// <summary>
/// 加载所有指定类型的对象
/// </summary>
/// <typeparam name="T">对象类型</typeparam>
/// <returns>对象集合</returns>
public IEnumerable<T> LoadAll<T>() where T : class public IEnumerable<T> LoadAll<T>() where T : class
{ {
foreach (var path in ResourcePaths) foreach (var path in ResourcePaths)
@ -127,6 +179,12 @@ public sealed class ResourceContentSource : IContentSource
} }
} }
/// <summary>
/// 从资源中提取对象
/// </summary>
/// <typeparam name="T">对象类型</typeparam>
/// <param name="resource">资源</param>
/// <returns>对象集合</returns>
private IEnumerable<T> ExtractResources<T>(Resource resource) where T : class private IEnumerable<T> ExtractResources<T>(Resource resource) where T : class
{ {
if (resource is IContentResource content) if (resource is IContentResource content)
@ -163,10 +221,20 @@ public sealed class ResourceContentSource : IContentSource
/// </summary> /// </summary>
public sealed class JsonContentSource : IContentSource public sealed class JsonContentSource : IContentSource
{ {
/// <summary>
/// 优先级
/// </summary>
public int Priority { get; } public int Priority { get; }
/// <summary>
/// 数据路径列表
/// </summary>
public List<string> DataPaths { get; } = new(); public List<string> DataPaths { get; } = new();
private readonly JsonSerializerOptions _options; private readonly JsonSerializerOptions _options;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="priority">优先级</param>
public JsonContentSource(int priority) public JsonContentSource(int priority)
{ {
Priority = priority; Priority = priority;
@ -177,6 +245,11 @@ public sealed class JsonContentSource : IContentSource
_options.Converters.Add(new JsonStringEnumConverter()); _options.Converters.Add(new JsonStringEnumConverter());
} }
/// <summary>
/// 加载所有指定类型的对象
/// </summary>
/// <typeparam name="T">对象类型</typeparam>
/// <returns>对象集合</returns>
public IEnumerable<T> LoadAll<T>() where T : class public IEnumerable<T> LoadAll<T>() where T : class
{ {
foreach (var path in DataPaths) foreach (var path in DataPaths)
@ -232,6 +305,11 @@ public sealed class JsonContentSource : IContentSource
} }
} }
/// <summary>
/// 解析路径
/// </summary>
/// <param name="path">路径</param>
/// <returns>解析后的绝对路径</returns>
private string ResolvePath(string path) private string ResolvePath(string path)
{ {
if (path.StartsWith("res://") || path.StartsWith("user://")) if (path.StartsWith("res://") || path.StartsWith("user://"))
@ -242,6 +320,9 @@ public sealed class JsonContentSource : IContentSource
return path; return path;
} }
/// <summary>
/// 尝试反序列化列表
/// </summary>
private bool TryDeserializeList<T>(string json, out List<T> list) where T : class private bool TryDeserializeList<T>(string json, out List<T> list) where T : class
{ {
try try
@ -256,6 +337,9 @@ public sealed class JsonContentSource : IContentSource
} }
} }
/// <summary>
/// 尝试反序列化单个对象
/// </summary>
private bool TryDeserializeSingle<T>(string json, out T item) where T : class private bool TryDeserializeSingle<T>(string json, out T item) where T : class
{ {
try try
@ -270,6 +354,9 @@ public sealed class JsonContentSource : IContentSource
} }
} }
/// <summary>
/// 尝试反序列化信封结构
/// </summary>
private bool TryDeserializeEnvelope<T>(string json, out JsonContentEnvelope<T> envelope) where T : class private bool TryDeserializeEnvelope<T>(string json, out JsonContentEnvelope<T> envelope) where T : class
{ {
try try
@ -295,5 +382,4 @@ public sealed class JsonContentSource : IContentSource
public List<T> Items { get; set; } public List<T> Items { get; set; }
public T Item { get; set; } public T Item { get; set; }
} }
} }

View File

@ -15,13 +15,26 @@ namespace Core;
/// </summary> /// </summary>
public interface IContentResource public interface IContentResource
{ {
/// <summary>
/// 获取定义类型
/// </summary>
/// <returns>类型</returns>
Type GetDefinitionType(); Type GetDefinitionType();
/// <summary>
/// 转换为定义对象
/// </summary>
/// <returns>定义对象</returns>
object ToDefinition(); object ToDefinition();
} }
/// <summary>
/// 资源集合接口
/// </summary>
public interface IContentResourceCollection public interface IContentResourceCollection
{ {
/// <summary>
/// 获取资源项集合
/// </summary>
/// <returns>资源项集合</returns>
IEnumerable<IContentResource> GetItems(); IEnumerable<IContentResource> GetItems();
} }

View File

@ -20,28 +20,81 @@ namespace Core;
public partial class DisciplineDefinitionResource : Resource, IContentResource public partial class DisciplineDefinitionResource : Resource, IContentResource
{ {
// --- Header --- // --- Header ---
/// <summary>
/// 学科ID
/// </summary>
[Export] public string Id { get; set; } [Export] public string Id { get; set; }
/// <summary>
/// 名称键值
/// </summary>
[Export] public string NameKey { get; set; } [Export] public string NameKey { get; set; }
/// <summary>
/// 名称默认值
/// </summary>
[Export] public string NameFallback { get; set; } [Export] public string NameFallback { get; set; }
/// <summary>
/// 描述键值
/// </summary>
[Export] public string DescriptionKey { get; set; } [Export] public string DescriptionKey { get; set; }
/// <summary>
/// 描述默认值
/// </summary>
[Export] public string DescriptionFallback { get; set; } [Export] public string DescriptionFallback { get; set; }
/// <summary>
/// 图标路径
/// </summary>
[Export] public string IconPath { get; set; } [Export] public string IconPath { get; set; }
/// <summary>
/// 标签列表
/// </summary>
[Export] public Array<string> Tags { get; set; } = new(); [Export] public Array<string> Tags { get; set; } = new();
// --- Buff --- // --- Buff ---
/// <summary>
/// Buff名称键值
/// </summary>
[Export] public string BuffNameKey { get; set; } [Export] public string BuffNameKey { get; set; }
/// <summary>
/// Buff名称默认值
/// </summary>
[Export] public string BuffNameFallback { get; set; } [Export] public string BuffNameFallback { get; set; }
/// <summary>
/// Buff描述键值
/// </summary>
[Export] public string BuffDescriptionKey { get; set; } [Export] public string BuffDescriptionKey { get; set; }
/// <summary>
/// Buff描述默认值
/// </summary>
[Export] public string BuffDescriptionFallback { get; set; } [Export] public string BuffDescriptionFallback { get; set; }
/// <summary>
/// Buff规则ID列表
/// </summary>
[Export] public Array<string> BuffRuleIds { get; set; } = new(); [Export] public Array<string> BuffRuleIds { get; set; } = new();
// --- Pools --- // --- Pools ---
/// <summary>
/// 角色池ID列表
/// </summary>
[Export] public Array<string> RolePoolIds { get; set; } = new(); [Export] public Array<string> RolePoolIds { get; set; } = new();
/// <summary>
/// 物品池ID列表
/// </summary>
[Export] public Array<string> ItemPoolIds { get; set; } = new(); [Export] public Array<string> ItemPoolIds { get; set; } = new();
/// <summary>
/// 任务关键词ID列表
/// </summary>
[Export] public Array<string> TaskKeywordIds { get; set; } = new(); [Export] public Array<string> TaskKeywordIds { get; set; } = new();
/// <summary>
/// 获取定义类型
/// </summary>
/// <returns>类型</returns>
public Type GetDefinitionType() => typeof(DisciplineDefinition); public Type GetDefinitionType() => typeof(DisciplineDefinition);
/// <summary>
/// 转换为定义对象
/// </summary>
/// <returns>定义对象</returns>
public object ToDefinition() public object ToDefinition()
{ {
var header = new DefinitionHeader var header = new DefinitionHeader
@ -98,6 +151,11 @@ public partial class DisciplineDefinitionResource : Resource, IContentResource
return definition; return definition;
} }
/// <summary>
/// 添加范围
/// </summary>
/// <param name="source">源数组</param>
/// <param name="target">目标列表</param>
private static void AddRange(Array<string> source, List<string> target) private static void AddRange(Array<string> source, List<string> target)
{ {
foreach (var value in source) foreach (var value in source)
@ -105,5 +163,4 @@ public partial class DisciplineDefinitionResource : Resource, IContentResource
target.Add(value); target.Add(value);
} }
} }
} }

View File

@ -14,31 +14,57 @@ namespace Core;
/// </summary> /// </summary>
public readonly struct TaskCompletedEvent public readonly struct TaskCompletedEvent
{ {
/// <summary>
/// 完成的任务
/// </summary>
public TaskModel Task { get; } public TaskModel Task { get; }
/// <summary>
/// 构造函数
/// </summary>
/// <param name="task">任务</param>
public TaskCompletedEvent(TaskModel task) public TaskCompletedEvent(TaskModel task)
{ {
Task = task; Task = task;
} }
} }
/// <summary>
/// 任务失败事件
/// </summary>
public readonly struct TaskFailedEvent public readonly struct TaskFailedEvent
{ {
/// <summary>
/// 失败的任务
/// </summary>
public TaskModel Task { get; } public TaskModel Task { get; }
/// <summary>
/// 构造函数
/// </summary>
/// <param name="task">任务</param>
public TaskFailedEvent(TaskModel task) public TaskFailedEvent(TaskModel task)
{ {
Task = task; Task = task;
} }
} }
/// <summary>
/// 回合结束事件
/// </summary>
public readonly struct TurnEndedEvent public readonly struct TurnEndedEvent
{ {
/// <summary>
/// 结束的回合数
/// </summary>
public int Turn { get; } public int Turn { get; }
/// <summary>
/// 构造函数
/// </summary>
/// <param name="turn">回合数</param>
public TurnEndedEvent(int turn) public TurnEndedEvent(int turn)
{ {
Turn = turn; Turn = turn;
} }
} }

View File

@ -17,6 +17,11 @@ public sealed class DomainEventBus
{ {
private readonly Dictionary<Type, List<Delegate>> _handlers = new(); private readonly Dictionary<Type, List<Delegate>> _handlers = new();
/// <summary>
/// 订阅事件
/// </summary>
/// <typeparam name="T">事件类型</typeparam>
/// <param name="handler">事件处理器</param>
public void Subscribe<T>(Action<T> handler) public void Subscribe<T>(Action<T> handler)
{ {
var type = typeof(T); var type = typeof(T);
@ -29,6 +34,11 @@ public sealed class DomainEventBus
list.Add(handler); list.Add(handler);
} }
/// <summary>
/// 取消订阅事件
/// </summary>
/// <typeparam name="T">事件类型</typeparam>
/// <param name="handler">事件处理器</param>
public void Unsubscribe<T>(Action<T> handler) public void Unsubscribe<T>(Action<T> handler)
{ {
var type = typeof(T); var type = typeof(T);
@ -38,6 +48,11 @@ public sealed class DomainEventBus
} }
} }
/// <summary>
/// 发布事件
/// </summary>
/// <typeparam name="T">事件类型</typeparam>
/// <param name="evt">事件对象</param>
public void Publish<T>(T evt) public void Publish<T>(T evt)
{ {
var type = typeof(T); var type = typeof(T);
@ -54,5 +69,4 @@ public sealed class DomainEventBus
} }
} }
} }
} }

View File

@ -16,28 +16,40 @@ public sealed class GameController : IController
{ {
private GameSession _session; private GameSession _session;
/// <summary>
/// 初始化控制器
/// </summary>
/// <param name="session">游戏会话</param>
public void Initialize(GameSession session) public void Initialize(GameSession session)
{ {
_session = session; _session = session;
} }
/// <summary>
/// 开始执行阶段
/// </summary>
public void StartExecution() public void StartExecution()
{ {
if (_session.State.Turn.Phase != GamePhase.Planning) return; if (_session.State.Turn.Phase != GamePhase.Planning) return;
_session.State.Turn.Phase = GamePhase.Execution; _session.State.Turn.Phase = GamePhase.Execution;
} }
/// <summary>
/// 结束执行阶段
/// </summary>
public void EndExecution() public void EndExecution()
{ {
if (_session.State.Turn.Phase != GamePhase.Execution) return; if (_session.State.Turn.Phase != GamePhase.Execution) return;
_session.State.Turn.Phase = GamePhase.Review; _session.State.Turn.Phase = GamePhase.Review;
} }
/// <summary>
/// 开始下一回合
/// </summary>
public void StartNextTurn() public void StartNextTurn()
{ {
if (_session.State.Turn.Phase != GamePhase.Review) return; if (_session.State.Turn.Phase != GamePhase.Review) return;
_session.State.Turn.CurrentTurn++; _session.State.Turn.CurrentTurn++;
_session.State.Turn.Phase = GamePhase.Planning; _session.State.Turn.Phase = GamePhase.Planning;
} }
} }

View File

@ -14,12 +14,34 @@ namespace Core;
/// </summary> /// </summary>
public sealed class GameSession public sealed class GameSession
{ {
/// <summary>
/// 游戏状态
/// </summary>
public GameState State { get; } public GameState State { get; }
/// <summary>
/// 游戏内容数据库
/// </summary>
public GameContentDatabase Content { get; } public GameContentDatabase Content { get; }
/// <summary>
/// 领域事件总线
/// </summary>
public DomainEventBus Events { get; } public DomainEventBus Events { get; }
/// <summary>
/// 本地化服务
/// </summary>
public ILocalizationService Localization { get; } public ILocalizationService Localization { get; }
/// <summary>
/// 游戏系统集合
/// </summary>
public GameSystems Systems { get; } public GameSystems Systems { get; }
/// <summary>
/// 构造游戏会话
/// </summary>
/// <param name="state">游戏状态</param>
/// <param name="content">内容数据库</param>
/// <param name="localization">本地化服务</param>
/// <param name="events">事件总线</param>
public GameSession(GameState state, GameContentDatabase content, ILocalizationService localization, DomainEventBus events) public GameSession(GameState state, GameContentDatabase content, ILocalizationService localization, DomainEventBus events)
{ {
State = state; State = state;
@ -30,6 +52,10 @@ public sealed class GameSession
Systems.Initialize(this); Systems.Initialize(this);
} }
/// <summary>
/// 创建默认游戏会话
/// </summary>
/// <returns>游戏会话实例</returns>
public static GameSession CreateDefault() public static GameSession CreateDefault()
{ {
var registry = new ContentRegistry(); var registry = new ContentRegistry();
@ -50,9 +76,12 @@ public sealed class GameSession
return new GameSession(new GameState(), content, localization, events); return new GameSession(new GameState(), content, localization, events);
} }
/// <summary>
/// 每帧更新
/// </summary>
/// <param name="delta">帧间隔</param>
public void Tick(float delta) public void Tick(float delta)
{ {
Systems.Tick(delta); Systems.Tick(delta);
} }
} }

View File

@ -16,18 +16,48 @@ namespace Core;
/// </summary> /// </summary>
public interface IGameSystem public interface IGameSystem
{ {
/// <summary>
/// 初始化系统
/// </summary>
/// <param name="session">游戏会话</param>
void Initialize(GameSession session); void Initialize(GameSession session);
/// <summary>
/// 每帧更新
/// </summary>
/// <param name="delta">帧间隔</param>
void Tick(float delta); void Tick(float delta);
} }
/// <summary>
/// 游戏系统管理器
/// </summary>
public sealed class GameSystems public sealed class GameSystems
{ {
/// <summary>
/// 回合系统
/// </summary>
public TurnSystem Turn { get; } = new(); public TurnSystem Turn { get; } = new();
/// <summary>
/// 任务系统
/// </summary>
public TaskSystem Task { get; } = new(); public TaskSystem Task { get; } = new();
/// <summary>
/// 经济系统
/// </summary>
public EconomySystem Economy { get; } = new(); public EconomySystem Economy { get; } = new();
/// <summary>
/// 羁绊/协同系统
/// </summary>
public SynergySystem Synergy { get; } = new(); public SynergySystem Synergy { get; } = new();
/// <summary>
/// 分配系统
/// </summary>
public AssignmentSystem Assignment { get; } = new(); public AssignmentSystem Assignment { get; } = new();
/// <summary>
/// 初始化所有系统
/// </summary>
/// <param name="session">游戏会话</param>
public void Initialize(GameSession session) public void Initialize(GameSession session)
{ {
Turn.Initialize(session); Turn.Initialize(session);
@ -37,6 +67,10 @@ public sealed class GameSystems
Assignment.Initialize(session); Assignment.Initialize(session);
} }
/// <summary>
/// 更新所有系统
/// </summary>
/// <param name="delta">帧间隔</param>
public void Tick(float delta) public void Tick(float delta)
{ {
Turn.Tick(delta); Turn.Tick(delta);
@ -47,32 +81,54 @@ public sealed class GameSystems
} }
} }
/// <summary>
/// 回合系统
/// </summary>
public sealed class TurnSystem : IGameSystem public sealed class TurnSystem : IGameSystem
{ {
private GameSession _session; private GameSession _session;
/// <summary>
/// 初始化
/// </summary>
/// <param name="session">游戏会话</param>
public void Initialize(GameSession session) public void Initialize(GameSession session)
{ {
_session = session; _session = session;
} }
/// <summary>
/// 更新
/// </summary>
/// <param name="delta">帧间隔</param>
public void Tick(float delta) public void Tick(float delta)
{ {
// 预留:回合推进计时器/阶段切换 // 预留:回合推进计时器/阶段切换
} }
} }
/// <summary>
/// 任务系统
/// </summary>
public sealed class TaskSystem : IGameSystem public sealed class TaskSystem : IGameSystem
{ {
private GameSession _session; private GameSession _session;
private StatResolver _statResolver; private StatResolver _statResolver;
/// <summary>
/// 初始化
/// </summary>
/// <param name="session">游戏会话</param>
public void Initialize(GameSession session) public void Initialize(GameSession session)
{ {
_session = session; _session = session;
_statResolver = new StatResolver(session); _statResolver = new StatResolver(session);
} }
/// <summary>
/// 更新
/// </summary>
/// <param name="delta">帧间隔</param>
public void Tick(float delta) public void Tick(float delta)
{ {
if (_session.State.Turn.Phase != GamePhase.Execution) if (_session.State.Turn.Phase != GamePhase.Execution)
@ -124,6 +180,10 @@ public sealed class TaskSystem : IGameSystem
} }
} }
/// <summary>
/// 推进任务进度
/// </summary>
/// <param name="delta">时间间隔</param>
private void AdvanceTasks(float delta) private void AdvanceTasks(float delta)
{ {
var state = _session.State; var state = _session.State;
@ -157,6 +217,9 @@ public sealed class TaskSystem : IGameSystem
} }
} }
/// <summary>
/// 获取单位对任务的贡献
/// </summary>
private float GetUnitContribution(UnitEntry entry, TaskModel task, TaskDefinition taskDef, float delta) private float GetUnitContribution(UnitEntry entry, TaskModel task, TaskDefinition taskDef, float delta)
{ {
var unit = entry.Unit; var unit = entry.Unit;
@ -173,6 +236,9 @@ public sealed class TaskSystem : IGameSystem
return effectivePower; return effectivePower;
} }
/// <summary>
/// 获取任务基础效率
/// </summary>
private float GetTaskBasePower(UnitModel unit, TaskKind kind) private float GetTaskBasePower(UnitModel unit, TaskKind kind)
{ {
var academic = _statResolver.GetAttribute(unit, AttributeType.Academic); var academic = _statResolver.GetAttribute(unit, AttributeType.Academic);
@ -194,6 +260,9 @@ public sealed class TaskSystem : IGameSystem
}; };
} }
/// <summary>
/// 获取状态乘数
/// </summary>
private float GetStatusMultiplier(UnitEntry entry) private float GetStatusMultiplier(UnitEntry entry)
{ {
var mood = entry.Unit.Statuses.Mood.Normalized; var mood = entry.Unit.Statuses.Mood.Normalized;
@ -213,6 +282,9 @@ public sealed class TaskSystem : IGameSystem
return Math.Clamp(multiplier, 0.3f, 1.2f); return Math.Clamp(multiplier, 0.3f, 1.2f);
} }
/// <summary>
/// 获取角色乘数
/// </summary>
private float GetRoleMultiplier(UnitModel unit, TaskDefinition taskDef) private float GetRoleMultiplier(UnitModel unit, TaskDefinition taskDef)
{ {
if (taskDef == null) return 1.0f; if (taskDef == null) return 1.0f;
@ -246,6 +318,9 @@ public sealed class TaskSystem : IGameSystem
return 1.0f; return 1.0f;
} }
/// <summary>
/// 获取需求乘数
/// </summary>
private float GetRequirementMultiplier(UnitModel unit, TaskDefinition taskDef) private float GetRequirementMultiplier(UnitModel unit, TaskDefinition taskDef)
{ {
if (taskDef == null) return 1.0f; if (taskDef == null) return 1.0f;
@ -263,6 +338,9 @@ public sealed class TaskSystem : IGameSystem
return multiplier; return multiplier;
} }
/// <summary>
/// 获取学科乘数
/// </summary>
private float GetDisciplineMultiplier(UnitModel unit, TaskDefinition taskDef) private float GetDisciplineMultiplier(UnitModel unit, TaskDefinition taskDef)
{ {
if (taskDef == null) return 1.0f; if (taskDef == null) return 1.0f;
@ -280,6 +358,9 @@ public sealed class TaskSystem : IGameSystem
return 0.7f; return 0.7f;
} }
/// <summary>
/// 获取难度系数
/// </summary>
private float GetDifficultyScale(TaskDifficulty difficulty) private float GetDifficultyScale(TaskDifficulty difficulty)
{ {
return difficulty switch return difficulty switch
@ -292,12 +373,18 @@ public sealed class TaskSystem : IGameSystem
}; };
} }
/// <summary>
/// 获取任务定义
/// </summary>
private TaskDefinition GetTaskDefinition(TaskModel task) private TaskDefinition GetTaskDefinition(TaskModel task)
{ {
if (string.IsNullOrWhiteSpace(task.DefinitionId)) return null; if (string.IsNullOrWhiteSpace(task.DefinitionId)) return null;
return _session.Content.Tasks.TryGetValue(task.DefinitionId, out var definition) ? definition : null; return _session.Content.Tasks.TryGetValue(task.DefinitionId, out var definition) ? definition : null;
} }
/// <summary>
/// 构建单位索引
/// </summary>
private Dictionary<Guid, UnitEntry> BuildUnitIndex() private Dictionary<Guid, UnitEntry> BuildUnitIndex()
{ {
var index = new Dictionary<Guid, UnitEntry>(); var index = new Dictionary<Guid, UnitEntry>();
@ -324,6 +411,9 @@ public sealed class TaskSystem : IGameSystem
return index; return index;
} }
/// <summary>
/// 追踪贡献
/// </summary>
private void TrackContribution(UnitEntry entry, TaskModel task, float deltaContribution) private void TrackContribution(UnitEntry entry, TaskModel task, float deltaContribution)
{ {
if (entry.Student == null) return; if (entry.Student == null) return;
@ -355,6 +445,9 @@ public sealed class TaskSystem : IGameSystem
} }
} }
/// <summary>
/// 经济系统
/// </summary>
public sealed class EconomySystem : IGameSystem public sealed class EconomySystem : IGameSystem
{ {
private GameSession _session; private GameSession _session;
@ -363,6 +456,10 @@ public sealed class EconomySystem : IGameSystem
private const int PostDocSalary = 1200; private const int PostDocSalary = 1200;
private const int JuniorFacultySalary = 2000; private const int JuniorFacultySalary = 2000;
/// <summary>
/// 初始化
/// </summary>
/// <param name="session">游戏会话</param>
public void Initialize(GameSession session) public void Initialize(GameSession session)
{ {
_session = session; _session = session;
@ -371,11 +468,18 @@ public sealed class EconomySystem : IGameSystem
_session.Events.Subscribe<TurnEndedEvent>(OnTurnEnded); _session.Events.Subscribe<TurnEndedEvent>(OnTurnEnded);
} }
/// <summary>
/// 更新
/// </summary>
/// <param name="delta">帧间隔</param>
public void Tick(float delta) public void Tick(float delta)
{ {
// 当前为回合驱动,不在 Tick 中结算 // 当前为回合驱动,不在 Tick 中结算
} }
/// <summary>
/// 任务完成回调
/// </summary>
private void OnTaskCompleted(TaskCompletedEvent evt) private void OnTaskCompleted(TaskCompletedEvent evt)
{ {
var task = evt.Task; var task = evt.Task;
@ -404,6 +508,9 @@ public sealed class EconomySystem : IGameSystem
} }
} }
/// <summary>
/// 任务失败回调
/// </summary>
private void OnTaskFailed(TaskFailedEvent evt) private void OnTaskFailed(TaskFailedEvent evt)
{ {
var task = evt.Task; var task = evt.Task;
@ -412,12 +519,18 @@ public sealed class EconomySystem : IGameSystem
_session.State.Economy.Reputation -= penalty; _session.State.Economy.Reputation -= penalty;
} }
/// <summary>
/// 回合结束回调
/// </summary>
private void OnTurnEnded(TurnEndedEvent evt) private void OnTurnEnded(TurnEndedEvent evt)
{ {
ApplySalaries(); ApplySalaries();
ApplyInterest(); ApplyInterest();
} }
/// <summary>
/// 支付薪水
/// </summary>
private void ApplySalaries() private void ApplySalaries()
{ {
var economy = _session.State.Economy; var economy = _session.State.Economy;
@ -436,6 +549,9 @@ public sealed class EconomySystem : IGameSystem
} }
} }
/// <summary>
/// 计算利息
/// </summary>
private void ApplyInterest() private void ApplyInterest()
{ {
var economy = _session.State.Economy; var economy = _session.State.Economy;
@ -446,6 +562,9 @@ public sealed class EconomySystem : IGameSystem
economy.Money += interest; economy.Money += interest;
} }
/// <summary>
/// 更新利率
/// </summary>
private void UpdateInterestRate() private void UpdateInterestRate()
{ {
var economy = _session.State.Economy; var economy = _session.State.Economy;
@ -456,6 +575,9 @@ public sealed class EconomySystem : IGameSystem
} }
} }
/// <summary>
/// 添加论文
/// </summary>
private void AddPaper(PaperRank rank) private void AddPaper(PaperRank rank)
{ {
var inventory = _session.State.Inventory; var inventory = _session.State.Inventory;
@ -467,6 +589,9 @@ public sealed class EconomySystem : IGameSystem
inventory.PaperCounts[rank] += 1; inventory.PaperCounts[rank] += 1;
} }
/// <summary>
/// 添加物品
/// </summary>
private void AddItem(string itemId, int count) private void AddItem(string itemId, int count)
{ {
if (string.IsNullOrWhiteSpace(itemId)) return; if (string.IsNullOrWhiteSpace(itemId)) return;
@ -479,6 +604,9 @@ public sealed class EconomySystem : IGameSystem
inventory.ItemCounts[itemId] += count; inventory.ItemCounts[itemId] += count;
} }
/// <summary>
/// 根据难度获取论文等级
/// </summary>
private PaperRank GetPaperRankByDifficulty(TaskDifficulty difficulty) private PaperRank GetPaperRankByDifficulty(TaskDifficulty difficulty)
{ {
return difficulty switch return difficulty switch
@ -492,20 +620,34 @@ public sealed class EconomySystem : IGameSystem
} }
} }
/// <summary>
/// 羁绊/协同系统
/// </summary>
public sealed class SynergySystem : IGameSystem public sealed class SynergySystem : IGameSystem
{ {
private GameSession _session; private GameSession _session;
/// <summary>
/// 初始化
/// </summary>
/// <param name="session">游戏会话</param>
public void Initialize(GameSession session) public void Initialize(GameSession session)
{ {
_session = session; _session = session;
} }
/// <summary>
/// 更新
/// </summary>
/// <param name="delta">帧间隔</param>
public void Tick(float delta) public void Tick(float delta)
{ {
RecalculateSynergy(); RecalculateSynergy();
} }
/// <summary>
/// 重新计算协同
/// </summary>
private void RecalculateSynergy() private void RecalculateSynergy()
{ {
var state = _session.State.Synergy; var state = _session.State.Synergy;
@ -518,6 +660,9 @@ public sealed class SynergySystem : IGameSystem
ApplySynergyDefinitions(state); ApplySynergyDefinitions(state);
} }
/// <summary>
/// 统计单位标签
/// </summary>
private void CountUnitTags(SynergyState synergy) private void CountUnitTags(SynergyState synergy)
{ {
var roster = _session.State.Roster; var roster = _session.State.Roster;
@ -534,6 +679,9 @@ public sealed class SynergySystem : IGameSystem
} }
} }
/// <summary>
/// 添加单位标签
/// </summary>
private void AddUnitTags(SynergyState synergy, UnitModel unit) private void AddUnitTags(SynergyState synergy, UnitModel unit)
{ {
if (unit == null) return; if (unit == null) return;
@ -548,6 +696,9 @@ public sealed class SynergySystem : IGameSystem
} }
} }
/// <summary>
/// 增加堆叠计数
/// </summary>
private void AddStack(Dictionary<string, int> stacks, string id) private void AddStack(Dictionary<string, int> stacks, string id)
{ {
if (string.IsNullOrWhiteSpace(id)) return; if (string.IsNullOrWhiteSpace(id)) return;
@ -559,6 +710,9 @@ public sealed class SynergySystem : IGameSystem
stacks[id] += 1; stacks[id] += 1;
} }
/// <summary>
/// 应用协同定义
/// </summary>
private void ApplySynergyDefinitions(SynergyState synergy) private void ApplySynergyDefinitions(SynergyState synergy)
{ {
foreach (var archetype in _session.Content.Archetypes.Values) foreach (var archetype in _session.Content.Archetypes.Values)
@ -572,6 +726,9 @@ public sealed class SynergySystem : IGameSystem
} }
} }
/// <summary>
/// 应用协同层级
/// </summary>
private void ApplySynergyTier(string id, List<SynergyTier> tiers, Dictionary<string, int> stacks, SynergyState synergy) private void ApplySynergyTier(string id, List<SynergyTier> tiers, Dictionary<string, int> stacks, SynergyState synergy)
{ {
if (string.IsNullOrWhiteSpace(id)) return; if (string.IsNullOrWhiteSpace(id)) return;
@ -586,6 +743,9 @@ public sealed class SynergySystem : IGameSystem
} }
} }
/// <summary>
/// 合并修正
/// </summary>
private void MergeModifiers(ModifierBundle target, ModifierBundle source) private void MergeModifiers(ModifierBundle target, ModifierBundle source)
{ {
if (target == null || source == null) return; if (target == null || source == null) return;
@ -595,6 +755,9 @@ public sealed class SynergySystem : IGameSystem
target.RuleIds.AddRange(source.RuleIds); target.RuleIds.AddRange(source.RuleIds);
} }
/// <summary>
/// 清除修正
/// </summary>
private void ClearModifiers(ModifierBundle bundle) private void ClearModifiers(ModifierBundle bundle)
{ {
bundle.AttributeModifiers.Clear(); bundle.AttributeModifiers.Clear();
@ -604,18 +767,28 @@ public sealed class SynergySystem : IGameSystem
} }
} }
/// <summary>
/// 分配系统
/// </summary>
public sealed class AssignmentSystem : IGameSystem public sealed class AssignmentSystem : IGameSystem
{ {
private GameSession _session; private GameSession _session;
/// <summary>
/// 初始化
/// </summary>
/// <param name="session">游戏会话</param>
public void Initialize(GameSession session) public void Initialize(GameSession session)
{ {
_session = session; _session = session;
} }
/// <summary>
/// 更新
/// </summary>
/// <param name="delta">帧间隔</param>
public void Tick(float delta) public void Tick(float delta)
{ {
// 预留:人员分配、交接惩罚等 // 预留:人员分配、交接惩罚等
} }
} }

View File

@ -15,18 +15,43 @@ namespace Core;
/// </summary> /// </summary>
public interface ILocalizationService public interface ILocalizationService
{ {
/// <summary>
/// 翻译本地化文本对象
/// </summary>
/// <param name="text">文本对象</param>
/// <returns>翻译后的字符串</returns>
string Translate(LocalizedText text); string Translate(LocalizedText text);
/// <summary>
/// 翻译键值
/// </summary>
/// <param name="key">键值</param>
/// <param name="fallback">默认值</param>
/// <returns>翻译后的字符串</returns>
string Translate(string key, string fallback = null); string Translate(string key, string fallback = null);
} }
/// <summary>
/// Godot 本地化服务实现
/// </summary>
public sealed class GodotLocalizationService : ILocalizationService public sealed class GodotLocalizationService : ILocalizationService
{ {
/// <summary>
/// 翻译本地化文本对象
/// </summary>
/// <param name="text">文本对象</param>
/// <returns>翻译后的字符串</returns>
public string Translate(LocalizedText text) public string Translate(LocalizedText text)
{ {
if (text == null) return string.Empty; if (text == null) return string.Empty;
return Translate(text.Key, text.Fallback); return Translate(text.Key, text.Fallback);
} }
/// <summary>
/// 翻译键值
/// </summary>
/// <param name="key">键值</param>
/// <param name="fallback">默认值</param>
/// <returns>翻译后的字符串</returns>
public string Translate(string key, string fallback = null) public string Translate(string key, string fallback = null)
{ {
if (string.IsNullOrWhiteSpace(key)) if (string.IsNullOrWhiteSpace(key))
@ -42,5 +67,4 @@ public sealed class GodotLocalizationService : ILocalizationService
return translated; return translated;
} }
} }

View File

@ -14,16 +14,39 @@ namespace Core;
/// </summary> /// </summary>
public sealed class ModManifest public sealed class ModManifest
{ {
/// <summary>
/// Mod ID
/// </summary>
public string Id { get; set; } public string Id { get; set; }
/// <summary>
/// Mod 名称
/// </summary>
public string Name { get; set; } public string Name { get; set; }
/// <summary>
/// 版本
/// </summary>
public string Version { get; set; } public string Version { get; set; }
/// <summary>
/// 依赖列表
/// </summary>
public List<string> Dependencies { get; } = new(); public List<string> Dependencies { get; } = new();
/// <summary>
/// 内容路径列表
/// </summary>
public List<string> ContentPaths { get; } = new(); public List<string> ContentPaths { get; } = new();
} }
/// <summary>
/// Mod 包
/// </summary>
public sealed class ModPackage public sealed class ModPackage
{ {
/// <summary>
/// 清单
/// </summary>
public ModManifest Manifest { get; set; } = new(); public ModManifest Manifest { get; set; } = new();
/// <summary>
/// 根路径
/// </summary>
public string RootPath { get; set; } public string RootPath { get; set; }
} }

View File

@ -14,26 +14,50 @@ namespace Core;
/// </summary> /// </summary>
public interface IView<TModel> public interface IView<TModel>
{ {
/// <summary>
/// 绑定数据模型
/// </summary>
/// <param name="model">数据模型</param>
void Bind(TModel model); void Bind(TModel model);
} }
/// <summary>
/// 控制器接口
/// </summary>
public interface IController public interface IController
{ {
/// <summary>
/// 初始化控制器
/// </summary>
/// <param name="session">游戏会话</param>
void Initialize(GameSession session); void Initialize(GameSession session);
} }
/// <summary>
/// 模型视图基类
/// </summary>
/// <typeparam name="TModel">模型类型</typeparam>
public abstract partial class ModelView<TModel> : Node, IView<TModel> public abstract partial class ModelView<TModel> : Node, IView<TModel>
{ {
/// <summary>
/// 数据模型
/// </summary>
public TModel Model { get; private set; } public TModel Model { get; private set; }
/// <summary>
/// 绑定数据模型
/// </summary>
/// <param name="model">数据模型</param>
public virtual void Bind(TModel model) public virtual void Bind(TModel model)
{ {
Model = model; Model = model;
OnModelBound(); OnModelBound();
} }
/// <summary>
/// 当模型绑定时调用
/// </summary>
protected virtual void OnModelBound() protected virtual void OnModelBound()
{ {
} }
} }

View File

@ -17,11 +17,21 @@ public sealed class StatResolver
{ {
private readonly GameSession _session; private readonly GameSession _session;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="session">游戏会话</param>
public StatResolver(GameSession session) public StatResolver(GameSession session)
{ {
_session = session; _session = session;
} }
/// <summary>
/// 获取计算后的属性值
/// </summary>
/// <param name="unit">单位</param>
/// <param name="type">属性类型</param>
/// <returns>属性值</returns>
public float GetAttribute(UnitModel unit, AttributeType type) public float GetAttribute(UnitModel unit, AttributeType type)
{ {
var value = GetBaseAttribute(unit, type); var value = GetBaseAttribute(unit, type);
@ -32,6 +42,12 @@ public sealed class StatResolver
return value; return value;
} }
/// <summary>
/// 获取基础属性值
/// </summary>
/// <param name="unit">单位</param>
/// <param name="type">属性类型</param>
/// <returns>基础值</returns>
private float GetBaseAttribute(UnitModel unit, AttributeType type) private float GetBaseAttribute(UnitModel unit, AttributeType type)
{ {
return type switch return type switch
@ -46,6 +62,9 @@ public sealed class StatResolver
}; };
} }
/// <summary>
/// 应用学科加成
/// </summary>
private void ApplyDiscipline(string disciplineId, AttributeType type, ref float value) private void ApplyDiscipline(string disciplineId, AttributeType type, ref float value)
{ {
if (string.IsNullOrWhiteSpace(disciplineId)) return; if (string.IsNullOrWhiteSpace(disciplineId)) return;
@ -53,6 +72,9 @@ public sealed class StatResolver
ApplyBundle(discipline.Buff?.Modifiers, type, ref value); ApplyBundle(discipline.Buff?.Modifiers, type, ref value);
} }
/// <summary>
/// 应用特质加成
/// </summary>
private void ApplyTraits(List<string> traitIds, AttributeType type, ref float value) private void ApplyTraits(List<string> traitIds, AttributeType type, ref float value)
{ {
if (traitIds == null || traitIds.Count == 0) return; if (traitIds == null || traitIds.Count == 0) return;
@ -65,6 +87,9 @@ public sealed class StatResolver
} }
} }
/// <summary>
/// 应用装备加成
/// </summary>
private void ApplyItems(List<string> itemIds, AttributeType type, ref float value) private void ApplyItems(List<string> itemIds, AttributeType type, ref float value)
{ {
if (itemIds == null || itemIds.Count == 0) return; if (itemIds == null || itemIds.Count == 0) return;
@ -77,6 +102,9 @@ public sealed class StatResolver
} }
} }
/// <summary>
/// 应用修正包
/// </summary>
private void ApplyBundle(ModifierBundle bundle, AttributeType type, ref float value) private void ApplyBundle(ModifierBundle bundle, AttributeType type, ref float value)
{ {
if (bundle == null) return; if (bundle == null) return;
@ -86,5 +114,4 @@ public sealed class StatResolver
value = (value + modifier.Add) * modifier.Multiplier; value = (value + modifier.Add) * modifier.Multiplier;
} }
} }
} }

View File

@ -4,14 +4,20 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Xml; using System.Xml;
/// <summary>
/// 办公格子/图块对象
/// </summary>
public partial class Cube : StaticBody2D, ITileDraggable public partial class Cube : StaticBody2D, ITileDraggable
{ {
// 定义各种家具的占用区域
private static readonly Rect2I tableRect = new(0, 0, 3, 2); private static readonly Rect2I tableRect = new(0, 0, 3, 2);
private static readonly Rect2I table2Rect = new(0, -1, 3, 1); private static readonly Rect2I table2Rect = new(0, -1, 3, 1);
private static readonly Rect2I chairRect = new(1, 1, 1, 2); private static readonly Rect2I chairRect = new(1, 1, 1, 2);
private static readonly Rect2I chair2Rect = new(1, -2, 1, 2); private static readonly Rect2I chair2Rect = new(1, -2, 1, 2);
private static readonly Rect2I equipRect = new(0, 0, 3, 2); private static readonly Rect2I equipRect = new(0, 0, 3, 2);
private static readonly Rect2I equip2Rect = new(0, -1, 3, 1); private static readonly Rect2I equip2Rect = new(0, -1, 3, 1);
// 定义桌子的主题映射
private static readonly TileMapping[] tableThemes = { private static readonly TileMapping[] tableThemes = {
new(new Vector2I(1, 30), tableRect), new(new Vector2I(1, 30), tableRect),
new(new Vector2I(4, 30), tableRect), new(new Vector2I(4, 30), tableRect),
@ -19,6 +25,7 @@ public partial class Cube : StaticBody2D, ITileDraggable
new(new Vector2I(7, 28), tableRect), new(new Vector2I(7, 28), tableRect),
new(new Vector2I(10, 28), tableRect), new(new Vector2I(10, 28), tableRect),
}; };
// 定义第二种桌子的主题映射
private static readonly TileMapping[] table2Themes = { private static readonly TileMapping[] table2Themes = {
new(new Vector2I(1, 31), table2Rect), new(new Vector2I(1, 31), table2Rect),
new(new Vector2I(4, 31), table2Rect), new(new Vector2I(4, 31), table2Rect),
@ -26,12 +33,14 @@ public partial class Cube : StaticBody2D, ITileDraggable
new(new Vector2I(7, 29), table2Rect), new(new Vector2I(7, 29), table2Rect),
new(new Vector2I(10, 29), table2Rect), new(new Vector2I(10, 29), table2Rect),
}; };
// 定义椅子的主题映射
private static readonly TileMapping[] chairThemes = { private static readonly TileMapping[] chairThemes = {
new(new Vector2I(-1, 7), chairRect), new(new Vector2I(-1, 7), chairRect),
new(new Vector2I(0, 7), chairRect), new(new Vector2I(0, 7), chairRect),
new(new Vector2I(-1, 9), chairRect), new(new Vector2I(-1, 9), chairRect),
new(new Vector2I(0, 9), chairRect), new(new Vector2I(0, 9), chairRect),
}; };
// 定义第二种椅子的主题映射
private static readonly TileMapping[] chair2Themes = { private static readonly TileMapping[] chair2Themes = {
new(new Vector2I(-1, 10), chair2Rect), new(new Vector2I(-1, 10), chair2Rect),
new(new Vector2I(0, 10), chair2Rect), new(new Vector2I(0, 10), chair2Rect),
@ -39,12 +48,13 @@ public partial class Cube : StaticBody2D, ITileDraggable
new(new Vector2I(0, 12), chair2Rect), new(new Vector2I(0, 12), chair2Rect),
}; };
// 定义设备的主题映射
private static readonly TileMapping[] equipThemes = { private static readonly TileMapping[] equipThemes = {
// one laptop // 一台笔记本电脑
new(new Vector2I(13, 26), equipRect), new(new Vector2I(13, 26), equipRect),
// one laptop with one monitor // 一台笔记本电脑加一台显示器
new(new Vector2I(7, 26), equipRect), new(new Vector2I(7, 26), equipRect),
// one desktop PC // 一台台式机
new(new Dictionary<Vector2I, Vector2I>() { new(new Dictionary<Vector2I, Vector2I>() {
[new Vector2I(0, 0)] = new Vector2I(11, 17), [new Vector2I(0, 0)] = new Vector2I(11, 17),
[new Vector2I(1, 0)] = new Vector2I(8, 32), [new Vector2I(1, 0)] = new Vector2I(8, 32),
@ -53,30 +63,34 @@ public partial class Cube : StaticBody2D, ITileDraggable
[new Vector2I(1, 1)] = new Vector2I(8, 33), [new Vector2I(1, 1)] = new Vector2I(8, 33),
[new Vector2I(2, 1)] = new Vector2I(9, 33), [new Vector2I(2, 1)] = new Vector2I(9, 33),
}), }),
// one desktop PC with one monitors // 一台台式机加一台显示器
new(new Vector2I(7, 32), equipRect), new(new Vector2I(7, 32), equipRect),
}; };
// 定义第二种设备的主题映射
private static readonly TileMapping[] equip2Themes = { private static readonly TileMapping[] equip2Themes = {
// one laptop // 一台笔记本电脑
new(new Vector2I(13, 31), equip2Rect), new(new Vector2I(13, 31), equip2Rect),
// one laptop with one monitor // 一台笔记本电脑加一台显示器
new(new Dictionary<Vector2I, Vector2I>() { new(new Dictionary<Vector2I, Vector2I>() {
[new Vector2I(0, -1)] = new Vector2I(13, 32), [new Vector2I(0, -1)] = new Vector2I(13, 32),
[new Vector2I(1, -1)] = new Vector2I(10, 7), [new Vector2I(1, -1)] = new Vector2I(10, 7),
[new Vector2I(2, -1)] = new Vector2I(15, 32), [new Vector2I(2, -1)] = new Vector2I(15, 32),
}), }),
// one desktop PC // 一台台式机
new(new Dictionary<Vector2I, Vector2I>() { new(new Dictionary<Vector2I, Vector2I>() {
[new Vector2I(0, -1)] = new Vector2I(13, 32), [new Vector2I(0, -1)] = new Vector2I(13, 32),
[new Vector2I(1, -1)] = new Vector2I(14, 32), [new Vector2I(1, -1)] = new Vector2I(14, 32),
[new Vector2I(2, -1)] = new Vector2I(15, 30), [new Vector2I(2, -1)] = new Vector2I(15, 30),
}), }),
// one desktop PC with one monitors // 一台台式机加一台显示器
new(new Vector2I(13, 33), equip2Rect), new(new Vector2I(13, 33), equip2Rect),
}; };
private bool _draggable; private bool _draggable;
/// <summary>
/// 是否可拖拽
/// </summary>
public bool Draggable { public bool Draggable {
get => _draggable; get => _draggable;
set { set {
@ -86,10 +100,20 @@ public partial class Cube : StaticBody2D, ITileDraggable
} }
private readonly Guid _id = Guid.NewGuid(); private readonly Guid _id = Guid.NewGuid();
/// <summary>
/// 唯一标识符
/// </summary>
public Guid Id => _id; public Guid Id => _id;
/// <summary>
/// 在网格中的位置
/// </summary>
public Vector2I TilePosition { get; set; } = new Vector2I(5,5); public Vector2I TilePosition { get; set; } = new Vector2I(5,5);
private bool _isCollided = true; private bool _isCollided = true;
/// <summary>
/// 是否处于碰撞状态(显示红色背景)
/// </summary>
public bool IsCollided { public bool IsCollided {
get => _isCollided; get => _isCollided;
set { set {
@ -103,9 +127,15 @@ public partial class Cube : StaticBody2D, ITileDraggable
} }
} }
private static readonly Rect2I tileRect = new(-1, -2, 4, 5); private static readonly Rect2I tileRect = new(-1, -2, 4, 5);
/// <summary>
/// 占用矩形区域
/// </summary>
public Rect2I TileRect => tileRect; public Rect2I TileRect => tileRect;
private Vector2I _mouseOffset; private Vector2I _mouseOffset;
/// <summary>
/// 鼠标拖拽偏移
/// </summary>
public Vector2I MouseOffset => _mouseOffset; public Vector2I MouseOffset => _mouseOffset;
private static readonly ITileDraggable.SpecialTile[] specialTiles = { private static readonly ITileDraggable.SpecialTile[] specialTiles = {
@ -114,15 +144,27 @@ public partial class Cube : StaticBody2D, ITileDraggable
}; };
/// <summary>
/// 特殊图块(如座位)
/// </summary>
public ITileDraggable.SpecialTile[] SpecialTiles => specialTiles; public ITileDraggable.SpecialTile[] SpecialTiles => specialTiles;
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
/// <summary>
/// 节点进入场景树时调用
/// </summary>
public override void _Ready() public override void _Ready()
{ {
RandomChangeTheme(); RandomChangeTheme();
IsCollided = false; IsCollided = false;
} }
/// <summary>
/// 处理输入事件
/// </summary>
/// <param name="viewport">视口</param>
/// <param name="event">输入事件</param>
/// <param name="shapeIdx">形状索引</param>
public override void _InputEvent(Viewport viewport, InputEvent @event, int shapeIdx) public override void _InputEvent(Viewport viewport, InputEvent @event, int shapeIdx)
{ {
if (@event.IsActionPressed("mouse_left_press")) { if (@event.IsActionPressed("mouse_left_press")) {
@ -142,6 +184,10 @@ public partial class Cube : StaticBody2D, ITileDraggable
} }
// Called every frame. 'delta' is the elapsed time since the previous frame. // Called every frame. 'delta' is the elapsed time since the previous frame.
/// <summary>
/// 每帧处理
/// </summary>
/// <param name="delta">时间间隔</param>
public override void _Process(double delta) public override void _Process(double delta)
{ {
GlobalPosition = TilePosition * 48; GlobalPosition = TilePosition * 48;
@ -153,6 +199,10 @@ public partial class Cube : StaticBody2D, ITileDraggable
private int _chair2ThemeIdx; private int _chair2ThemeIdx;
private int _equipThemeIdx; private int _equipThemeIdx;
private int _equip2ThemeIdx; private int _equip2ThemeIdx;
/// <summary>
/// 随机更换主题样式
/// </summary>
public void RandomChangeTheme() { public void RandomChangeTheme() {
_tableThemeIdx = GD.RandRange(0, tableThemes.Length-1); _tableThemeIdx = GD.RandRange(0, tableThemes.Length-1);
_chairThemeIdx = GD.RandRange(0, chairThemes.Length-1); _chairThemeIdx = GD.RandRange(0, chairThemes.Length-1);
@ -177,6 +227,12 @@ public partial class Cube : StaticBody2D, ITileDraggable
new(1,1), new(1,1),
new(2,1), new(2,1),
}); });
/// <summary>
/// 获取指定位置的图块类型
/// </summary>
/// <param name="pos">相对位置</param>
/// <returns>图块类型</returns>
public Lab.MapNodeType GetTileType(Vector2I pos) public Lab.MapNodeType GetTileType(Vector2I pos)
{ {
GD.Print($"query position of {pos}"); GD.Print($"query position of {pos}");
@ -189,6 +245,11 @@ public partial class Cube : StaticBody2D, ITileDraggable
return Lab.MapNodeType.Walkable; return Lab.MapNodeType.Walkable;
} }
/// <summary>
/// 设置椅子朝向(待实现)
/// </summary>
/// <param name="target">目标位置</param>
/// <param name="idx">索引</param>
public void ChairFaceTo(Vector2I target, int idx) { public void ChairFaceTo(Vector2I target, int idx) {
if (idx == 0) { if (idx == 0) {
var theme = chairThemes[_chairThemeIdx]; var theme = chairThemes[_chairThemeIdx];
@ -196,4 +257,4 @@ public partial class Cube : StaticBody2D, ITileDraggable
} }
} }
} }

View File

@ -17,41 +17,105 @@ using Core;
public partial class GameManager : Node public partial class GameManager : Node
{ {
/// <summary> /// <summary>
/// Indicates if the game is currently in tutorial mode. /// 指示当前是否处于教程模式
/// </summary> /// </summary>
public static bool IsTutorial { get; private set; } public static bool IsTutorial { get; private set; }
/// <summary>
/// 下一个场景的路径
/// </summary>
public static string NextScene { get; set; } = null; public static string NextScene { get; set; } = null;
/// <summary>
/// 双倍大小的箭头光标资源
/// </summary>
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");
/// <summary>
/// 最大回合数
/// </summary>
[Export] [Export]
public int MaxTurns = 30; public int MaxTurns = 30;
// --- Global State --- // --- Global State ---
/// <summary>
/// 游戏会话实例
/// </summary>
public GameSession Session { get; private set; } public GameSession Session { get; private set; }
/// <summary>
/// 当前游戏状态
/// </summary>
public GameState State => Session?.State; public GameState State => Session?.State;
/// <summary>
/// 游戏控制器
/// </summary>
public GameController Controller { get; private set; } public GameController Controller { get; private set; }
/// <summary>
/// 当前游戏阶段
/// </summary>
public GamePhase CurrentPhase => State?.Turn.Phase ?? GamePhase.Planning; public GamePhase CurrentPhase => State?.Turn.Phase ?? GamePhase.Planning;
/// <summary>
/// 当前回合数
/// </summary>
public int CurrentTurn => State?.Turn.CurrentTurn ?? 1; public int CurrentTurn => State?.Turn.CurrentTurn ?? 1;
// --- Domain Model --- // --- Domain Model ---
/// <summary>
/// 导师模型
/// </summary>
public MentorModel Mentor => State?.Roster.Mentor; public MentorModel Mentor => State?.Roster.Mentor;
/// <summary>
/// 学生列表
/// </summary>
public List<StudentModel> Students => State?.Roster.Students; public List<StudentModel> Students => State?.Roster.Students;
/// <summary>
/// 当前活动任务列表
/// </summary>
public List<TaskModel> ActiveTasks => State?.Tasks.ActiveTasks; public List<TaskModel> ActiveTasks => State?.Tasks.ActiveTasks;
/// <summary>
/// 经济状态
/// </summary>
public EconomyState Economy => State?.Economy; public EconomyState Economy => State?.Economy;
// --- Signals --- // --- Signals ---
/// <summary>
/// 阶段变更信号
/// </summary>
/// <param name="phase">阶段枚举的整数值</param>
[Signal] public delegate void PhaseChangedEventHandler(int phase); // int cast of GamePhase [Signal] public delegate void PhaseChangedEventHandler(int phase); // int cast of GamePhase
/// <summary>
/// 回合变更信号
/// </summary>
/// <param name="turn">当前回合数</param>
[Signal] public delegate void TurnChangedEventHandler(int turn); [Signal] public delegate void TurnChangedEventHandler(int turn);
// Singleton instance access (if needed, though Godot uses node paths) // Singleton instance access (if needed, though Godot uses node paths)
/// <summary>
/// 单例实例
/// </summary>
public static GameManager Instance { get; private set; } public static GameManager Instance { get; private set; }
/// <summary>
/// 进入场景树时调用
/// </summary>
public override void _EnterTree() public override void _EnterTree()
{ {
Instance = this; Instance = this;
} }
/// <summary>
/// 准备就绪时调用
/// </summary>
public override void _Ready() public override void _Ready()
{ {
Input.SetCustomMouseCursor(Arrow2X); Input.SetCustomMouseCursor(Arrow2X);
@ -60,6 +124,9 @@ public partial class GameManager : Node
InitializeGame(); InitializeGame();
} }
/// <summary>
/// 初始化游戏数据
/// </summary>
private void InitializeGame() private void InitializeGame()
{ {
Session = GameSession.CreateDefault(); Session = GameSession.CreateDefault();
@ -83,6 +150,10 @@ public partial class GameManager : Node
} }
// Called every frame. 'delta' is the elapsed time since the previous frame. // Called every frame. 'delta' is the elapsed time since the previous frame.
/// <summary>
/// 每帧调用
/// </summary>
/// <param name="delta">距离上一帧的时间间隔</param>
public override void _Process(double delta) public override void _Process(double delta)
{ {
if (CurrentPhase == GamePhase.Execution) if (CurrentPhase == GamePhase.Execution)
@ -157,4 +228,4 @@ public partial class GameManager : Node
GD.Print($"Turn {CurrentTurn} Started. Phase: Planning"); GD.Print($"Turn {CurrentTurn} Started. Phase: Planning");
} }
} }

View File

@ -1,6 +1,15 @@
using Godot; using Godot;
/// <summary>
/// 辅助工具类
/// </summary>
public class H { public class H {
/// <summary>
/// 判断矩形是否包含点(包含边界)
/// </summary>
/// <param name="r">矩形区域</param>
/// <param name="p">点坐标</param>
/// <returns>如果点在矩形内包括边界则返回true</returns>
public static bool RectHasPointInclusive(Rect2I r, Vector2I p) { public static bool RectHasPointInclusive(Rect2I r, Vector2I p) {
if (r.HasPoint(p)) { if (r.HasPoint(p)) {
return true; return true;
@ -13,4 +22,4 @@ public class H {
} }
return false; return false;
} }
} }

View File

@ -3,25 +3,73 @@ using System.Diagnostics.SymbolStore;
using System.Dynamic; using System.Dynamic;
using Godot; using Godot;
/// <summary>
/// 可拖拽图块接口
/// </summary>
public interface ITileDraggable { public interface ITileDraggable {
/// <summary>
/// 图块在网格中的位置
/// </summary>
Vector2I TilePosition { get; set; } Vector2I TilePosition { get; set; }
/// <summary>
/// 是否允许拖拽
/// </summary>
bool Draggable { get; set; } bool Draggable { get; set; }
/// <summary>
/// 是否处于碰撞状态(例如位置无效或重叠)
/// </summary>
bool IsCollided { get; set; } bool IsCollided { get; set; }
/// <summary>
/// 图块占据的矩形区域(相对于 TilePosition
/// </summary>
Rect2I TileRect { get; } Rect2I TileRect { get; }
/// <summary>
/// 拖拽时鼠标相对于图块原点的偏移量
/// </summary>
Vector2I MouseOffset { get; } Vector2I MouseOffset { get; }
/// <summary>
/// 特殊功能图块列表(如座位、交互点等)
/// </summary>
SpecialTile[] SpecialTiles { get; } SpecialTile[] SpecialTiles { get; }
/// <summary>
/// 特殊图块定义
/// </summary>
class SpecialTile { class SpecialTile {
/// <summary>
/// 相对位置
/// </summary>
public readonly Vector2I Position; public readonly Vector2I Position;
/// <summary>
/// 节点类型(如可行走、作为座位等)
/// </summary>
public readonly Lab.MapNodeType NodeType; public readonly Lab.MapNodeType NodeType;
/// <summary>
/// 构造特殊图块
/// </summary>
/// <param name="pos">相对位置</param>
/// <param name="node">节点类型</param>
public SpecialTile(Vector2I pos, Lab.MapNodeType node) { public SpecialTile(Vector2I pos, Lab.MapNodeType node) {
Position = pos; Position = pos;
NodeType = node; NodeType = node;
} }
} }
/// <summary>
/// 获取指定相对坐标处的图块类型
/// </summary>
/// <param name="pos">相对坐标</param>
/// <returns>地图节点类型</returns>
Lab.MapNodeType GetTileType(Vector2I pos); Lab.MapNodeType GetTileType(Vector2I pos);
/// <summary>
/// 唯一标识符
/// </summary>
Guid Id { get; } Guid Id { get; }
} }

View File

@ -3,18 +3,26 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
/// <summary>
/// 实验室场景主控脚本
/// </summary>
public partial class Lab : Node2D public partial class Lab : Node2D
{ {
/// <summary>
/// 地图节点类型标志位
/// </summary>
[Flags] [Flags]
public enum MapNodeType public enum MapNodeType
{ {
Invalid = 0, Invalid = 0, // 无效
Walkable = 1, Walkable = 1, // 可行走
Wall = 2, Wall = 2, // 墙壁
Blocker = 4, Blocker = 4, // 阻挡物
SeatUp = 8, SeatUp = 8, // 上方座位
SeatDown = 16, SeatDown = 16, // 下方座位
} }
// 墙壁矩形区域列表
private static readonly Rect2I[] wallRectangles = { private static readonly Rect2I[] wallRectangles = {
new(0,0,40,2), new(0,0,40,2),
new(0,5,1, 15), new(0,5,1, 15),
@ -23,9 +31,15 @@ public partial class Lab : Node2D
}; };
private readonly Dictionary<Guid, ITileDraggable> _furnitureIDs = new(); private readonly Dictionary<Guid, ITileDraggable> _furnitureIDs = new();
/// <summary>
/// 家具字典通过ID索引
/// </summary>
public Dictionary<Guid, ITileDraggable> Furniture => _furnitureIDs; public Dictionary<Guid, ITileDraggable> Furniture => _furnitureIDs;
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
/// <summary>
/// 场景加载完成时调用
/// </summary>
public override void _Ready() public override void _Ready()
{ {
var ticker = GetNode<Timer>("/root/GameManager/OneSecondTicker"); var ticker = GetNode<Timer>("/root/GameManager/OneSecondTicker");
@ -57,6 +71,10 @@ public partial class Lab : Node2D
private Label _moneyLabel; private Label _moneyLabel;
// Called every frame. 'delta' is the elapsed time since the previous frame. // Called every frame. 'delta' is the elapsed time since the previous frame.
/// <summary>
/// 每帧更新
/// </summary>
/// <param name="delta">帧间隔</param>
public override void _Process(double delta) public override void _Process(double delta)
{ {
switch(GD.RandRange(0,2)) { switch(GD.RandRange(0,2)) {
@ -82,12 +100,21 @@ public partial class Lab : Node2D
} }
private bool _isDragging; private bool _isDragging;
/// <summary>
/// 拾起物品
/// </summary>
/// <param name="target">目标物品</param>
public void Pickup(ITileDraggable target) { public void Pickup(ITileDraggable target) {
if (target == null) return; if (target == null) return;
_isDragging = true; _isDragging = true;
DraggingTarget = target; DraggingTarget = target;
} }
/// <summary>
/// 放下物品
/// </summary>
/// <param name="target">目标物品</param>
public void PutDown(ITileDraggable target) { public void PutDown(ITileDraggable target) {
if (target == null) return; if (target == null) return;
_isDragging = false; _isDragging = false;
@ -98,6 +125,10 @@ public partial class Lab : Node2D
DraggingTarget = null; DraggingTarget = null;
UpdateMap(); UpdateMap();
} }
/// <summary>
/// 当前正在拖拽的目标
/// </summary>
public ITileDraggable DraggingTarget { get; set; } public ITileDraggable DraggingTarget { get; set; }
public const int MapWidth = 40; public const int MapWidth = 40;
@ -109,6 +140,9 @@ public partial class Lab : Node2D
private TileMapLayer _tileMap; private TileMapLayer _tileMap;
/// <summary>
/// 更新地图数据
/// </summary>
public void UpdateMap() public void UpdateMap()
{ {
for (int i = 0; i < MapWidth; i++) { for (int i = 0; i < MapWidth; i++) {
@ -132,8 +166,6 @@ public partial class Lab : Node2D
} }
} }
private static bool IsValidPosition(Vector2I pos) private static bool IsValidPosition(Vector2I pos)
{ {
int x = pos.X; int x = pos.X;
@ -143,6 +175,7 @@ public partial class Lab : Node2D
} }
return true; return true;
} }
private List<Vector2I> GetNeighbors(Vector2I pos) { private List<Vector2I> GetNeighbors(Vector2I pos) {
int x = pos.X; int x = pos.X;
int y = pos.Y; int y = pos.Y;
@ -166,6 +199,12 @@ public partial class Lab : Node2D
return neighbor; return neighbor;
} }
/// <summary>
/// 获取最短路径
/// </summary>
/// <param name="start">起点</param>
/// <param name="end">终点</param>
/// <returns>路径点列表</returns>
public List<Vector2I> GetShortestPath(Vector2I start, Vector2I end) public List<Vector2I> GetShortestPath(Vector2I start, Vector2I end)
{ {
for (int j = 0; j < MapHeight; j++) { for (int j = 0; j < MapHeight; j++) {
@ -258,19 +297,35 @@ public partial class Lab : Node2D
} }
} }
/// <summary>
/// 获取指定位置的地图节点类型
/// </summary>
/// <param name="pos">位置</param>
/// <returns>节点类型</returns>
public MapNodeType GetMapNodeTypeOfPosition(Vector2I pos) { public MapNodeType GetMapNodeTypeOfPosition(Vector2I pos) {
if (!IsValidPosition(pos)) return MapNodeType.Invalid; if (!IsValidPosition(pos)) return MapNodeType.Invalid;
return _blocks[pos.X, pos.Y]; return _blocks[pos.X, pos.Y];
} }
/// <summary>
/// 世界坐标转网格坐标
/// </summary>
/// <param name="pos">世界坐标</param>
/// <returns>网格坐标</returns>
public Vector2I Point2Coord(Vector2 pos) { public Vector2I Point2Coord(Vector2 pos) {
return _tileMap.LocalToMap(_tileMap.ToLocal(pos)); return _tileMap.LocalToMap(_tileMap.ToLocal(pos));
} }
/// <summary>
/// 获取家具的特殊位置(如座位)
/// </summary>
/// <param name="fId">家具ID</param>
/// <param name="idx">索引</param>
/// <returns>特殊位置坐标</returns>
public Vector2I GetFurSpecialPosition(Guid fId, int idx) { public Vector2I GetFurSpecialPosition(Guid fId, int idx) {
if (!_furnitureIDs.ContainsKey(fId)) return Vector2I.Zero; if (!_furnitureIDs.ContainsKey(fId)) return Vector2I.Zero;
if (idx < 0 || idx > _furnitureIDs[fId].SpecialTiles.Length) return Vector2I.Zero; if (idx < 0 || idx > _furnitureIDs[fId].SpecialTiles.Length) return Vector2I.Zero;
return _furnitureIDs[fId].SpecialTiles[idx].Position + _furnitureIDs[fId].TilePosition; return _furnitureIDs[fId].SpecialTiles[idx].Position + _furnitureIDs[fId].TilePosition;
} }
} }

View File

@ -2,6 +2,9 @@ using Godot;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
/// <summary>
/// 场景加载器
/// </summary>
public partial class Loader : Control public partial class Loader : Control
{ {
private ProgressBar _progressBar; private ProgressBar _progressBar;
@ -12,6 +15,9 @@ public partial class Loader : Control
}; };
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
/// <summary>
/// 场景加载完成时调用
/// </summary>
public override void _Ready() public override void _Ready()
{ {
_progressBar = GetNode<ProgressBar>("ProgressBar"); _progressBar = GetNode<ProgressBar>("ProgressBar");
@ -31,6 +37,10 @@ public partial class Loader : Control
} }
// Called every frame. 'delta' is the elapsed time since the previous frame. // Called every frame. 'delta' is the elapsed time since the previous frame.
/// <summary>
/// 每帧更新
/// </summary>
/// <param name="delta">帧间隔</param>
public override void _Process(double delta) public override void _Process(double delta)
{ {
Godot.Collections.Array progress = new(); Godot.Collections.Array progress = new();
@ -53,4 +63,4 @@ public partial class Loader : Control
break; break;
} }
} }
} }

View File

@ -1,15 +1,25 @@
using Godot; using Godot;
using System; using System;
/// <summary>
/// Logo场景
/// </summary>
public partial class LogoScene : Node2D public partial class LogoScene : Node2D
{ {
/// <summary>
/// 场景加载完成时调用
/// </summary>
public override void _Ready() { public override void _Ready() {
GetNode<AnimationPlayer>("AnimationPlayer").AnimationFinished += OnAnimationPlayerAnimationFinished; GetNode<AnimationPlayer>("AnimationPlayer").AnimationFinished += OnAnimationPlayerAnimationFinished;
} }
/// <summary>
/// 动画播放结束回调
/// </summary>
/// <param name="animationName">动画名称</param>
public void OnAnimationPlayerAnimationFinished(StringName animationName) public void OnAnimationPlayerAnimationFinished(StringName animationName)
{ {
GD.Print("FFF"); GD.Print("FFF");
GetTree().ChangeSceneToFile("res://scenes/loader.tscn"); GetTree().ChangeSceneToFile("res://scenes/loader.tscn");
} }
} }

View File

@ -12,44 +12,142 @@ namespace Models;
/// </summary> /// </summary>
public static class CoreIds public static class CoreIds
{ {
/// <summary>
/// 默认命名空间
/// </summary>
public const string Namespace = "core"; public const string Namespace = "core";
// Disciplines // Disciplines
/// <summary>
/// 生物学
/// </summary>
public const string DisciplineBiology = "core:discipline_biology"; public const string DisciplineBiology = "core:discipline_biology";
/// <summary>
/// 化学
/// </summary>
public const string DisciplineChemistry = "core:discipline_chemistry"; public const string DisciplineChemistry = "core:discipline_chemistry";
/// <summary>
/// 环境科学
/// </summary>
public const string DisciplineEnvironment = "core:discipline_environment"; public const string DisciplineEnvironment = "core:discipline_environment";
/// <summary>
/// 材料学
/// </summary>
public const string DisciplineMaterials = "core:discipline_materials"; public const string DisciplineMaterials = "core:discipline_materials";
/// <summary>
/// 医学
/// </summary>
public const string DisciplineMedicine = "core:discipline_medicine"; public const string DisciplineMedicine = "core:discipline_medicine";
/// <summary>
/// 计算机
/// </summary>
public const string DisciplineComputer = "core:discipline_computer"; public const string DisciplineComputer = "core:discipline_computer";
/// <summary>
/// 数学
/// </summary>
public const string DisciplineMath = "core:discipline_math"; public const string DisciplineMath = "core:discipline_math";
/// <summary>
/// 物理学
/// </summary>
public const string DisciplinePhysics = "core:discipline_physics"; public const string DisciplinePhysics = "core:discipline_physics";
/// <summary>
/// 机械工程
/// </summary>
public const string DisciplineMechanical = "core:discipline_mechanical"; public const string DisciplineMechanical = "core:discipline_mechanical";
/// <summary>
/// 哲学
/// </summary>
public const string DisciplinePhilosophy = "core:discipline_philosophy"; public const string DisciplinePhilosophy = "core:discipline_philosophy";
/// <summary>
/// 经济学
/// </summary>
public const string DisciplineEconomics = "core:discipline_economics"; public const string DisciplineEconomics = "core:discipline_economics";
/// <summary>
/// 法学
/// </summary>
public const string DisciplineLaw = "core:discipline_law"; public const string DisciplineLaw = "core:discipline_law";
/// <summary>
/// 文学
/// </summary>
public const string DisciplineLiterature = "core:discipline_literature"; public const string DisciplineLiterature = "core:discipline_literature";
/// <summary>
/// 农学
/// </summary>
public const string DisciplineAgriculture = "core:discipline_agriculture"; public const string DisciplineAgriculture = "core:discipline_agriculture";
/// <summary>
/// 管理学
/// </summary>
public const string DisciplineManagement = "core:discipline_management"; public const string DisciplineManagement = "core:discipline_management";
/// <summary>
/// 艺术
/// </summary>
public const string DisciplineArt = "core:discipline_art"; public const string DisciplineArt = "core:discipline_art";
// Archetypes // Archetypes
/// <summary>
/// 卷王
/// </summary>
public const string ArchetypeGrinder = "core:archetype_grinder"; public const string ArchetypeGrinder = "core:archetype_grinder";
/// <summary>
/// 摸鱼
/// </summary>
public const string ArchetypeSlacker = "core:archetype_slacker"; public const string ArchetypeSlacker = "core:archetype_slacker";
/// <summary>
/// 精英
/// </summary>
public const string ArchetypeElite = "core:archetype_elite"; public const string ArchetypeElite = "core:archetype_elite";
/// <summary>
/// 天才
/// </summary>
public const string ArchetypeProdigy = "core:archetype_prodigy"; public const string ArchetypeProdigy = "core:archetype_prodigy";
/// <summary>
/// 吉祥物
/// </summary>
public const string ArchetypeMascot = "core:archetype_mascot"; public const string ArchetypeMascot = "core:archetype_mascot";
// Roles // Roles
/// <summary>
/// 码农
/// </summary>
public const string RoleCoder = "core:role_coder"; public const string RoleCoder = "core:role_coder";
/// <summary>
/// 写手
/// </summary>
public const string RoleWriter = "core:role_writer"; public const string RoleWriter = "core:role_writer";
/// <summary>
/// 实验员
/// </summary>
public const string RoleLabRat = "core:role_lab_rat"; public const string RoleLabRat = "core:role_lab_rat";
/// <summary>
/// 讲演者
/// </summary>
public const string RolePresenter = "core:role_presenter"; public const string RolePresenter = "core:role_presenter";
/// <summary>
/// 记录员
/// </summary>
public const string RoleScribe = "core:role_scribe"; public const string RoleScribe = "core:role_scribe";
/// <summary>
/// 辩论者
/// </summary>
public const string RoleOrator = "core:role_orator"; public const string RoleOrator = "core:role_orator";
/// <summary>
/// 管家
/// </summary>
public const string RoleSteward = "core:role_steward"; public const string RoleSteward = "core:role_steward";
/// <summary>
/// 炼金术士
/// </summary>
public const string RoleAlchemist = "core:role_alchemist"; public const string RoleAlchemist = "core:role_alchemist";
/// <summary>
/// 极客
/// </summary>
public const string RoleGeek = "core:role_geek"; public const string RoleGeek = "core:role_geek";
/// <summary>
/// 勘测员
/// </summary>
public const string RoleSurveyor = "core:role_surveyor"; public const string RoleSurveyor = "core:role_surveyor";
/// <summary>
/// 思考者
/// </summary>
public const string RoleThinker = "core:role_thinker"; public const string RoleThinker = "core:role_thinker";
} }

View File

@ -17,16 +17,36 @@ namespace Models;
/// </summary> /// </summary>
public sealed class LocalizedText public sealed class LocalizedText
{ {
/// <summary>
/// 键值
/// </summary>
public string Key { get; set; } public string Key { get; set; }
/// <summary>
/// 默认值
/// </summary>
public string Fallback { get; set; } public string Fallback { get; set; }
} }
public sealed class DefinitionHeader public sealed class DefinitionHeader
{ {
/// <summary>
/// 定义ID
/// </summary>
public string Id { get; set; } public string Id { get; set; }
/// <summary>
/// 名称
/// </summary>
public LocalizedText Name { get; set; } = new(); public LocalizedText Name { get; set; } = new();
/// <summary>
/// 描述
/// </summary>
public LocalizedText Description { get; set; } = new(); public LocalizedText Description { get; set; } = new();
/// <summary>
/// 图标路径
/// </summary>
public string IconPath { get; set; } public string IconPath { get; set; }
/// <summary>
/// 标签列表
/// </summary>
public List<string> Tags { get; } = new(); public List<string> Tags { get; } = new();
} }

View File

@ -14,18 +14,44 @@ namespace Models;
/// </summary> /// </summary>
public sealed class DisciplineDefinition public sealed class DisciplineDefinition
{ {
/// <summary>
/// 基础头部信息
/// </summary>
public DefinitionHeader Header { get; set; } = new(); public DefinitionHeader Header { get; set; } = new();
/// <summary>
/// 学科Buff
/// </summary>
public DisciplineBuff Buff { get; set; } = new(); public DisciplineBuff Buff { get; set; } = new();
/// <summary>
/// 角色池ID列表
/// </summary>
public List<string> RolePoolIds { get; } = new(); public List<string> RolePoolIds { get; } = new();
/// <summary>
/// 物品池ID列表
/// </summary>
public List<string> ItemPoolIds { get; } = new(); public List<string> ItemPoolIds { get; } = new();
/// <summary>
/// 任务关键词ID列表
/// </summary>
public List<string> TaskKeywordIds { get; } = new(); public List<string> TaskKeywordIds { get; } = new();
} }
/// <summary>
/// 学科Buff定义
/// </summary>
public sealed class DisciplineBuff public sealed class DisciplineBuff
{ {
/// <summary>
/// 名称
/// </summary>
public LocalizedText Name { get; set; } = new(); public LocalizedText Name { get; set; } = new();
/// <summary>
/// 描述
/// </summary>
public LocalizedText Description { get; set; } = new(); public LocalizedText Description { get; set; } = new();
/// <summary>
/// 修正包
/// </summary>
public ModifierBundle Modifiers { get; set; } = new(); public ModifierBundle Modifiers { get; set; } = new();
} }

View File

@ -12,32 +12,31 @@ namespace Models;
/// </summary> /// </summary>
public enum AttributeType public enum AttributeType
{ {
Academic, Academic, // 学术
Engineering, Engineering, // 工程
Writing, Writing, // 写作
Financial, Financial, // 财务
Social, Social, // 社交
Activation Activation // 活跃/执行
} }
public enum ResourceType public enum ResourceType
{ {
Money, Money, // 金钱
Reputation, Reputation, // 声望
ResearchPoints, ResearchPoints, // 科研点
Paper, Paper, // 论文
Inspiration, Inspiration, // 灵感
Time Time // 时间
} }
public enum StatusType public enum StatusType
{ {
Stress, Stress, // 压力
Sanity, Sanity, // 理智
Mood, Mood, // 心情
Stamina, Stamina, // 体力
Loyalty, Loyalty, // 忠诚度
Energy, Energy, // 精力
Health Health // 健康
} }

View File

@ -14,13 +14,36 @@ namespace Models;
/// </summary> /// </summary>
public sealed class GameContentDatabase public sealed class GameContentDatabase
{ {
/// <summary>
/// 学科定义字典
/// </summary>
public Dictionary<string, DisciplineDefinition> Disciplines { get; } = new(); public Dictionary<string, DisciplineDefinition> Disciplines { get; } = new();
/// <summary>
/// 原型定义字典
/// </summary>
public Dictionary<string, ArchetypeDefinition> Archetypes { get; } = new(); public Dictionary<string, ArchetypeDefinition> Archetypes { get; } = new();
/// <summary>
/// 角色定义字典
/// </summary>
public Dictionary<string, RoleDefinition> Roles { get; } = new(); public Dictionary<string, RoleDefinition> Roles { get; } = new();
/// <summary>
/// 特质定义字典
/// </summary>
public Dictionary<string, TraitDefinition> Traits { get; } = new(); public Dictionary<string, TraitDefinition> Traits { get; } = new();
/// <summary>
/// 任务定义字典
/// </summary>
public Dictionary<string, TaskDefinition> Tasks { get; } = new(); public Dictionary<string, TaskDefinition> Tasks { get; } = new();
/// <summary>
/// 物品定义字典
/// </summary>
public Dictionary<string, ItemDefinition> Items { get; } = new(); public Dictionary<string, ItemDefinition> Items { get; } = new();
/// <summary>
/// 论文定义字典
/// </summary>
public Dictionary<string, PaperDefinition> Papers { get; } = new(); public Dictionary<string, PaperDefinition> Papers { get; } = new();
/// <summary>
/// 肉鸽天赋定义字典
/// </summary>
public Dictionary<string, RoguelitePerkDefinition> RoguelitePerks { get; } = new(); public Dictionary<string, RoguelitePerkDefinition> RoguelitePerks { get; } = new();
} }

View File

@ -17,69 +17,179 @@ namespace Models;
/// </summary> /// </summary>
public sealed class GameState public sealed class GameState
{ {
/// <summary>
/// 回合状态
/// </summary>
public TurnState Turn { get; } = new(); public TurnState Turn { get; } = new();
/// <summary>
/// 经济状态
/// </summary>
public EconomyState Economy { get; } = new(); public EconomyState Economy { get; } = new();
/// <summary>
/// 人员状态
/// </summary>
public RosterState Roster { get; } = new(); public RosterState Roster { get; } = new();
/// <summary>
/// 任务状态
/// </summary>
public TaskState Tasks { get; } = new(); public TaskState Tasks { get; } = new();
/// <summary>
/// 库存状态
/// </summary>
public InventoryState Inventory { get; } = new(); public InventoryState Inventory { get; } = new();
/// <summary>
/// 协同状态
/// </summary>
public SynergyState Synergy { get; } = new(); public SynergyState Synergy { get; } = new();
/// <summary>
/// 肉鸽状态
/// </summary>
public RogueliteState Roguelite { get; } = new(); public RogueliteState Roguelite { get; } = new();
} }
/// <summary>
/// 游戏阶段
/// </summary>
public enum GamePhase public enum GamePhase
{ {
Planning, Planning, // 筹备阶段
Execution, Execution, // 执行阶段
Review Review // 结算阶段
} }
/// <summary>
/// 回合状态数据
/// </summary>
public sealed class TurnState public sealed class TurnState
{ {
/// <summary>
/// 当前回合
/// </summary>
public int CurrentTurn { get; set; } = 1; public int CurrentTurn { get; set; } = 1;
/// <summary>
/// 最大回合
/// </summary>
public int MaxTurns { get; set; } = 30; public int MaxTurns { get; set; } = 30;
/// <summary>
/// 当前阶段
/// </summary>
public GamePhase Phase { get; set; } = GamePhase.Planning; public GamePhase Phase { get; set; } = GamePhase.Planning;
} }
/// <summary>
/// 经济状态数据
/// </summary>
public sealed class EconomyState public sealed class EconomyState
{ {
/// <summary>
/// 资金
/// </summary>
public int Money { get; set; } = 50000; public int Money { get; set; } = 50000;
/// <summary>
/// 声望
/// </summary>
public int Reputation { get; set; } public int Reputation { get; set; }
/// <summary>
/// 科研点数
/// </summary>
public int ResearchPoints { get; set; } public int ResearchPoints { get; set; }
/// <summary>
/// 利率
/// </summary>
public float InterestRate { get; set; } = 0.0f; public float InterestRate { get; set; } = 0.0f;
} }
/// <summary>
/// 人员状态数据
/// </summary>
public sealed class RosterState public sealed class RosterState
{ {
/// <summary>
/// 导师模型
/// </summary>
public MentorModel Mentor { get; set; } = new("Player"); public MentorModel Mentor { get; set; } = new("Player");
/// <summary>
/// 学生列表
/// </summary>
public List<StudentModel> Students { get; } = new(); public List<StudentModel> Students { get; } = new();
/// <summary>
/// 职工列表
/// </summary>
public List<StaffModel> Staffs { get; } = new(); public List<StaffModel> Staffs { get; } = new();
} }
/// <summary>
/// 任务状态数据
/// </summary>
public sealed class TaskState public sealed class TaskState
{ {
/// <summary>
/// 活动任务列表
/// </summary>
public List<TaskModel> ActiveTasks { get; } = new(); public List<TaskModel> ActiveTasks { get; } = new();
/// <summary>
/// 已完成任务列表
/// </summary>
public List<TaskModel> CompletedTasks { get; } = new(); public List<TaskModel> CompletedTasks { get; } = new();
/// <summary>
/// 失败任务列表
/// </summary>
public List<TaskModel> FailedTasks { get; } = new(); public List<TaskModel> FailedTasks { get; } = new();
} }
/// <summary>
/// 库存状态数据
/// </summary>
public sealed class InventoryState public sealed class InventoryState
{ {
/// <summary>
/// 物品计数
/// </summary>
public Dictionary<string, int> ItemCounts { get; } = new(); public Dictionary<string, int> ItemCounts { get; } = new();
/// <summary>
/// 论文计数
/// </summary>
public Dictionary<PaperRank, int> PaperCounts { get; } = new(); public Dictionary<PaperRank, int> PaperCounts { get; } = new();
} }
/// <summary>
/// 协同状态数据
/// </summary>
public sealed class SynergyState public sealed class SynergyState
{ {
/// <summary>
/// 原型堆叠数
/// </summary>
public Dictionary<string, int> ArchetypeStacks { get; } = new(); public Dictionary<string, int> ArchetypeStacks { get; } = new();
/// <summary>
/// 角色堆叠数
/// </summary>
public Dictionary<string, int> RoleStacks { get; } = new(); public Dictionary<string, int> RoleStacks { get; } = new();
/// <summary>
/// 激活的协同ID列表
/// </summary>
public List<string> ActiveSynergyIds { get; } = new(); public List<string> ActiveSynergyIds { get; } = new();
/// <summary>
/// 激活的修正包
/// </summary>
public ModifierBundle ActiveModifiers { get; } = new(); public ModifierBundle ActiveModifiers { get; } = new();
} }
/// <summary>
/// 肉鸽状态数据
/// </summary>
public sealed class RogueliteState public sealed class RogueliteState
{ {
/// <summary>
/// 校友卡ID列表
/// </summary>
public List<string> AlumniCardIds { get; } = new(); public List<string> AlumniCardIds { get; } = new();
/// <summary>
/// 遗产解锁ID列表
/// </summary>
public List<string> LegacyUnlockIds { get; } = new(); public List<string> LegacyUnlockIds { get; } = new();
/// <summary>
/// 头衔保留等级
/// </summary>
public int TitleRetentionLevel { get; set; } public int TitleRetentionLevel { get; set; }
} }

View File

@ -14,29 +14,29 @@ namespace Models;
/// </summary> /// </summary>
public enum ItemCategory public enum ItemCategory
{ {
Facility, Facility, // 设施
Equipment, Equipment, // 装备
Consumable Consumable // 消耗品
} }
public enum FacilityPlacement public enum FacilityPlacement
{ {
Lab, Lab, // 实验室
ServerRoom, ServerRoom, // 机房
Admin, Admin, // 行政区
Library, Library, // 图书馆
RestArea, RestArea, // 休息区
Field, Field, // 场地
Global Global // 全局
} }
public enum EquipmentSlot public enum EquipmentSlot
{ {
Tool, Tool, // 工具
Accessory, Accessory, // 饰品
Body, Body, // 身体
Head, Head, // 头部
Hand Hand // 手部
} }
public sealed class ItemDefinition public sealed class ItemDefinition
@ -55,5 +55,4 @@ public sealed class ItemEffect
{ {
public ModifierBundle Modifiers { get; set; } = new(); public ModifierBundle Modifiers { get; set; } = new();
public List<string> RuleIds { get; } = new(); public List<string> RuleIds { get; } = new();
} }

View File

@ -14,22 +14,41 @@ namespace Models;
/// </summary> /// </summary>
public sealed class MentorModel public sealed class MentorModel
{ {
/// <summary>
/// 导师模式
/// </summary>
public enum MentorModeType public enum MentorModeType
{ {
Worker, Worker, // 打工人
Manager Manager // 管理者
} }
/// <summary>
/// 核心单位数据
/// </summary>
public UnitModel Core { get; } public UnitModel Core { get; }
/// <summary>
/// 导师特有资源
/// </summary>
public MentorResources Resources { get; } public MentorResources Resources { get; }
/// <summary>
/// 当前模式
/// </summary>
public MentorModeType Mode { get; set; } = MentorModeType.Worker; public MentorModeType Mode { get; set; } = MentorModeType.Worker;
/// <summary>
/// 名字(便捷访问)
/// </summary>
public string Name public string Name
{ {
get => Core.Name; get => Core.Name;
set => Core.Name = value; set => Core.Name = value;
} }
/// <summary>
/// 构造函数
/// </summary>
/// <param name="name">名字</param>
public MentorModel(string name) public MentorModel(string name)
{ {
Core = new UnitModel(name); Core = new UnitModel(name);
@ -42,10 +61,13 @@ public sealed class MentorModel
/// </summary> /// </summary>
public sealed class MentorResources public sealed class MentorResources
{ {
/// <summary>
/// 精力值
/// </summary>
public StatusValue Energy { get; set; } public StatusValue Energy { get; set; }
public MentorResources() public MentorResources()
{ {
Energy = new StatusValue(100, 100, 0); Energy = new StatusValue(100, 100, 0);
} }
} }

View File

@ -14,30 +14,68 @@ namespace Models;
/// </summary> /// </summary>
public sealed class AttributeModifier public sealed class AttributeModifier
{ {
/// <summary>
/// 属性类型
/// </summary>
public AttributeType Type { get; set; } public AttributeType Type { get; set; }
/// <summary>
/// 加值
/// </summary>
public float Add { get; set; } public float Add { get; set; }
/// <summary>
/// 乘数
/// </summary>
public float Multiplier { get; set; } = 1.0f; public float Multiplier { get; set; } = 1.0f;
} }
public sealed class StatusModifier public sealed class StatusModifier
{ {
/// <summary>
/// 状态类型
/// </summary>
public StatusType Type { get; set; } public StatusType Type { get; set; }
/// <summary>
/// 加值
/// </summary>
public float Add { get; set; } public float Add { get; set; }
/// <summary>
/// 乘数
/// </summary>
public float Multiplier { get; set; } = 1.0f; public float Multiplier { get; set; } = 1.0f;
} }
public sealed class ResourceModifier public sealed class ResourceModifier
{ {
/// <summary>
/// 资源类型
/// </summary>
public ResourceType Type { get; set; } public ResourceType Type { get; set; }
/// <summary>
/// 加值
/// </summary>
public int Add { get; set; } public int Add { get; set; }
/// <summary>
/// 乘数
/// </summary>
public float Multiplier { get; set; } = 1.0f; public float Multiplier { get; set; } = 1.0f;
} }
public sealed class ModifierBundle public sealed class ModifierBundle
{ {
/// <summary>
/// 属性修正列表
/// </summary>
public List<AttributeModifier> AttributeModifiers { get; } = new(); public List<AttributeModifier> AttributeModifiers { get; } = new();
/// <summary>
/// 状态修正列表
/// </summary>
public List<StatusModifier> StatusModifiers { get; } = new(); public List<StatusModifier> StatusModifiers { get; } = new();
/// <summary>
/// 资源修正列表
/// </summary>
public List<ResourceModifier> ResourceModifiers { get; } = new(); public List<ResourceModifier> ResourceModifiers { get; } = new();
/// <summary>
/// 规则ID列表
/// </summary>
public List<string> RuleIds { get; } = new(); public List<string> RuleIds { get; } = new();
} }

View File

@ -14,5 +14,4 @@ public sealed class PaperDefinition
{ {
public DefinitionHeader Header { get; set; } = new(); public DefinitionHeader Header { get; set; } = new();
public PaperRank Rank { get; set; } public PaperRank Rank { get; set; }
} }

View File

@ -20,17 +20,32 @@ public sealed class PropertyValue
public const float DefaultMin = 0f; public const float DefaultMin = 0f;
public const float DefaultMax = 100f; public const float DefaultMax = 100f;
/// <summary>
/// 最小值
/// </summary>
public float Min { get; } public float Min { get; }
/// <summary>
/// 最大值
/// </summary>
public float Max { get; } public float Max { get; }
private float _value; private float _value;
/// <summary>
/// 当前值(自动限制在 Min/Max 范围内)
/// </summary>
public float Value public float Value
{ {
get => _value; get => _value;
set => _value = Clamp(value, Min, Max); set => _value = Clamp(value, Min, Max);
} }
/// <summary>
/// 构造函数
/// </summary>
/// <param name="value">初始值</param>
/// <param name="min">最小值</param>
/// <param name="max">最大值</param>
public PropertyValue(float value = 0, float min = DefaultMin, float max = DefaultMax) public PropertyValue(float value = 0, float min = DefaultMin, float max = DefaultMax)
{ {
if (max < min) if (max < min)
@ -44,13 +59,26 @@ public sealed class PropertyValue
Value = value; Value = value;
} }
/// <summary>
/// 构造函数(整数)
/// </summary>
/// <param name="value">初始值</param>
/// <param name="min">最小值</param>
/// <param name="max">最大值</param>
public PropertyValue(int value, float min = DefaultMin, float max = DefaultMax) public PropertyValue(int value, float min = DefaultMin, float max = DefaultMax)
: this((float)value, min, max) : this((float)value, min, max)
{ {
} }
/// <summary>
/// 获取显示的整数值
/// </summary>
/// <returns>整数值</returns>
public int DisplayInt() => (int)_value; public int DisplayInt() => (int)_value;
/// <summary>
/// 获取归一化值 (0.0 - 1.0)
/// </summary>
public float Normalized public float Normalized
{ {
get get
@ -60,10 +88,22 @@ public sealed class PropertyValue
} }
} }
/// <summary>
/// 克隆
/// </summary>
/// <returns>新实例</returns>
public PropertyValue Clone() => new(Value, Min, Max); public PropertyValue Clone() => new(Value, Min, Max);
/// <summary>
/// 增加值
/// </summary>
/// <param name="delta">增量</param>
public void Add(float delta) => Value += delta; public void Add(float delta) => Value += delta;
/// <summary>
/// 减少值
/// </summary>
/// <param name="delta">减量</param>
public void Subtract(float delta) => Value -= delta; public void Subtract(float delta) => Value -= delta;
public override string ToString() => DisplayInt().ToString(); public override string ToString() => DisplayInt().ToString();
@ -128,4 +168,4 @@ public sealed class PropertyValue
if (value > max) return max; if (value > max) return max;
return value; return value;
} }
} }

View File

@ -12,16 +12,27 @@ namespace Models;
/// </summary> /// </summary>
public enum RoguelitePerkType public enum RoguelitePerkType
{ {
AlumniCard, AlumniCard, // 校友卡
LegacyProgress, LegacyProgress, // 遗产进度
TitleRetention TitleRetention // 头衔保留
} }
public sealed class RoguelitePerkDefinition public sealed class RoguelitePerkDefinition
{ {
/// <summary>
/// 基础头部信息
/// </summary>
public DefinitionHeader Header { get; set; } = new(); public DefinitionHeader Header { get; set; } = new();
/// <summary>
/// 天赋类型
/// </summary>
public RoguelitePerkType Type { get; set; } public RoguelitePerkType Type { get; set; }
/// <summary>
/// 修正包
/// </summary>
public ModifierBundle Modifiers { get; set; } = new(); public ModifierBundle Modifiers { get; set; } = new();
/// <summary>
/// 最大等级
/// </summary>
public int MaxLevel { get; set; } = 1; public int MaxLevel { get; set; } = 1;
} }

View File

@ -14,22 +14,43 @@ namespace Models;
/// </summary> /// </summary>
public sealed class StaffModel public sealed class StaffModel
{ {
/// <summary>
/// 职员类型
/// </summary>
public enum StaffType public enum StaffType
{ {
PostDoc, PostDoc, // 博士后
JuniorFaculty JuniorFaculty // 青年教师
} }
/// <summary>
/// 核心单位数据
/// </summary>
public UnitModel Core { get; } public UnitModel Core { get; }
/// <summary>
/// 职员类型
/// </summary>
public StaffType Type { get; private set; } public StaffType Type { get; private set; }
/// <summary>
/// 动机数据
/// </summary>
public StaffMotivation Motivation { get; } public StaffMotivation Motivation { get; }
/// <summary>
/// 名字(便捷访问)
/// </summary>
public string Name public string Name
{ {
get => Core.Name; get => Core.Name;
set => Core.Name = value; set => Core.Name = value;
} }
/// <summary>
/// 构造函数
/// </summary>
/// <param name="name">名字</param>
/// <param name="type">类型</param>
/// <param name="random">随机数生成器</param>
public StaffModel(string name, StaffType type, Random random = null) public StaffModel(string name, StaffType type, Random random = null)
{ {
Core = new UnitModel(name, random); Core = new UnitModel(name, random);
@ -38,10 +59,21 @@ public sealed class StaffModel
} }
} }
/// <summary>
/// 职员动机组件
/// </summary>
public sealed class StaffMotivation public sealed class StaffMotivation
{ {
/// <summary>
/// 野心
/// </summary>
public StatusValue Ambition { get; set; } = new(50, 100, 0); public StatusValue Ambition { get; set; } = new(50, 100, 0);
/// <summary>
/// 忠诚度
/// </summary>
public StatusValue Loyalty { get; set; } = new(70, 100, 0); public StatusValue Loyalty { get; set; } = new(70, 100, 0);
/// <summary>
/// 合同剩余回合
/// </summary>
public StatusValue ContractTurns { get; set; } = new(6, 12, 0); public StatusValue ContractTurns { get; set; } = new(6, 12, 0);
} }

View File

@ -19,9 +19,18 @@ public sealed class StatusValue
{ {
private PropertyValue _current; private PropertyValue _current;
/// <summary>
/// 达到上限事件
/// </summary>
public event Action OnUpperThresholdReached; public event Action OnUpperThresholdReached;
/// <summary>
/// 达到下限事件
/// </summary>
public event Action OnLowerThresholdReached; public event Action OnLowerThresholdReached;
/// <summary>
/// 当前值
/// </summary>
public PropertyValue Current public PropertyValue Current
{ {
get => _current; get => _current;
@ -32,9 +41,18 @@ public sealed class StatusValue
} }
} }
/// <summary>
/// 上限阈值
/// </summary>
public float UpperThreshold { get; set; } public float UpperThreshold { get; set; }
/// <summary>
/// 下限阈值
/// </summary>
public float LowerThreshold { get; set; } public float LowerThreshold { get; set; }
/// <summary>
/// 构造函数(整数)
/// </summary>
public StatusValue(int current = 0, int upper = 100, int lower = 0) public StatusValue(int current = 0, int upper = 100, int lower = 0)
{ {
_current = new PropertyValue(current, lower, upper); _current = new PropertyValue(current, lower, upper);
@ -42,6 +60,9 @@ public sealed class StatusValue
LowerThreshold = lower; LowerThreshold = lower;
} }
/// <summary>
/// 构造函数(浮点数)
/// </summary>
public StatusValue(float current, float upper, float lower) public StatusValue(float current, float upper, float lower)
{ {
_current = new PropertyValue(current, lower, upper); _current = new PropertyValue(current, lower, upper);
@ -49,6 +70,9 @@ public sealed class StatusValue
LowerThreshold = lower; LowerThreshold = lower;
} }
/// <summary>
/// 构造函数PropertyValue
/// </summary>
public StatusValue(PropertyValue current, float upperThreshold, float lowerThreshold) public StatusValue(PropertyValue current, float upperThreshold, float lowerThreshold)
{ {
_current = current ?? new PropertyValue(0); _current = current ?? new PropertyValue(0);
@ -70,15 +94,21 @@ public sealed class StatusValue
} }
} }
/// <summary>
/// 增加值
/// </summary>
public void Add(float value) public void Add(float value)
{ {
_current.Add(value); _current.Add(value);
CheckThresholds(); CheckThresholds();
} }
/// <summary>
/// 减少值
/// </summary>
public void Subtract(float value) public void Subtract(float value)
{ {
_current.Subtract(value); _current.Subtract(value);
CheckThresholds(); CheckThresholds();
} }
} }

View File

@ -18,25 +18,51 @@ namespace Models;
/// </summary> /// </summary>
public sealed class StudentModel public sealed class StudentModel
{ {
/// <summary>
/// 学生类型
/// </summary>
public enum StudentType public enum StudentType
{ {
MasterCandidate, MasterCandidate, // 硕士生
DoctorCandidate DoctorCandidate // 博士生
} }
/// <summary>
/// 核心单位数据
/// </summary>
public UnitModel Core { get; } public UnitModel Core { get; }
/// <summary>
/// 学生类型
/// </summary>
public StudentType Type { get; private set; } public StudentType Type { get; private set; }
/// <summary>
/// 进度数据
/// </summary>
public StudentProgress Progress { get; } public StudentProgress Progress { get; }
/// <summary>
/// 贡献记录
/// </summary>
public StudentContributions Contributions { get; } public StudentContributions Contributions { get; }
/// <summary>
/// 名字(便捷访问)
/// </summary>
public string Name public string Name
{ {
get => Core.Name; get => Core.Name;
set => Core.Name = value; set => Core.Name = value;
} }
/// <summary>
/// 特质列表(便捷访问)
/// </summary>
public List<string> Traits => Core.Tags.TraitIds; public List<string> Traits => Core.Tags.TraitIds;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="name">名字</param>
/// <param name="random">随机数生成器</param>
public StudentModel(string name, Random random = null) public StudentModel(string name, Random random = null)
{ {
var rng = random ?? Random.Shared; var rng = random ?? Random.Shared;
@ -52,8 +78,17 @@ public sealed class StudentModel
/// </summary> /// </summary>
public sealed class StudentProgress public sealed class StudentProgress
{ {
/// <summary>
/// 体力
/// </summary>
public StatusValue Stamina { get; set; } public StatusValue Stamina { get; set; }
/// <summary>
/// 忠诚度
/// </summary>
public StatusValue Loyalty { get; set; } public StatusValue Loyalty { get; set; }
/// <summary>
/// 年级
/// </summary>
public PropertyValue Grade { get; set; } public PropertyValue Grade { get; set; }
public StudentProgress(StudentModel.StudentType type) public StudentProgress(StudentModel.StudentType type)
@ -70,5 +105,8 @@ public sealed class StudentProgress
/// </summary> /// </summary>
public sealed class StudentContributions public sealed class StudentContributions
{ {
/// <summary>
/// 按任务ID索引的贡献值
/// </summary>
public Dictionary<Guid, PropertyValue> ByTask { get; } = new(); public Dictionary<Guid, PropertyValue> ByTask { get; } = new();
} }

View File

@ -14,30 +14,62 @@ namespace Models;
/// </summary> /// </summary>
public sealed class ArchetypeDefinition public sealed class ArchetypeDefinition
{ {
/// <summary>
/// 基础头部信息
/// </summary>
public DefinitionHeader Header { get; set; } = new(); public DefinitionHeader Header { get; set; } = new();
/// <summary>
/// 羁绊层级列表
/// </summary>
public List<SynergyTier> Tiers { get; } = new(); public List<SynergyTier> Tiers { get; } = new();
} }
public sealed class RoleDefinition public sealed class RoleDefinition
{ {
/// <summary>
/// 基础头部信息
/// </summary>
public DefinitionHeader Header { get; set; } = new(); public DefinitionHeader Header { get; set; } = new();
/// <summary>
/// 羁绊层级列表
/// </summary>
public List<SynergyTier> Tiers { get; } = new(); public List<SynergyTier> Tiers { get; } = new();
// 如果是学科限定角色(如炼金术士/极客),在这里配置允许的学科 Id。 // 如果是学科限定角色(如炼金术士/极客),在这里配置允许的学科 Id。
/// <summary>
/// 允许的学科ID列表
/// </summary>
public List<string> AllowedDisciplineIds { get; } = new(); public List<string> AllowedDisciplineIds { get; } = new();
} }
public sealed class TraitDefinition public sealed class TraitDefinition
{ {
/// <summary>
/// 基础头部信息
/// </summary>
public DefinitionHeader Header { get; set; } = new(); public DefinitionHeader Header { get; set; } = new();
/// <summary>
/// 修正包
/// </summary>
public ModifierBundle Modifiers { get; set; } = new(); public ModifierBundle Modifiers { get; set; } = new();
/// <summary>
/// 规则ID列表
/// </summary>
public List<string> RuleIds { get; } = new(); public List<string> RuleIds { get; } = new();
} }
public sealed class SynergyTier public sealed class SynergyTier
{ {
/// <summary>
/// 需求数量
/// </summary>
public int RequiredCount { get; set; } public int RequiredCount { get; set; }
/// <summary>
/// 修正包
/// </summary>
public ModifierBundle Modifiers { get; set; } = new(); public ModifierBundle Modifiers { get; set; } = new();
/// <summary>
/// 规则ID列表
/// </summary>
public List<string> RuleIds { get; } = new(); public List<string> RuleIds { get; } = new();
} }

View File

@ -77,4 +77,4 @@ public sealed class TaskRewardSnapshot
public int Reputation { get; set; } public int Reputation { get; set; }
public List<string> PaperIds { get; } = new(); public List<string> PaperIds { get; } = new();
public List<string> ItemIds { get; set; } public List<string> ItemIds { get; set; }
} }

View File

@ -33,10 +33,10 @@ public enum TaskKind
public enum TaskDifficulty public enum TaskDifficulty
{ {
Water, Water, // 水
Standard, Standard, // 标准
Hardcore, Hardcore, // 硬核
BlackBox BlackBox // 黑箱
} }
public enum PaperRank public enum PaperRank
@ -97,5 +97,4 @@ public sealed class TaskRewards
public List<string> PaperIds { get; } = new(); public List<string> PaperIds { get; } = new();
public List<string> ItemIds { get; } = new(); public List<string> ItemIds { get; } = new();
public List<string> BuffIds { get; } = new(); public List<string> BuffIds { get; } = new();
} }

View File

@ -18,6 +18,9 @@ namespace Models;
/// </summary> /// </summary>
public static class UnitComponents public static class UnitComponents
{ {
/// <summary>
/// 网格位置组件
/// </summary>
public readonly struct GridPosition public readonly struct GridPosition
{ {
public int X { get; } public int X { get; }
@ -32,6 +35,9 @@ public static class UnitComponents
public static GridPosition Zero => new(0, 0); public static GridPosition Zero => new(0, 0);
} }
/// <summary>
/// 单位身份组件
/// </summary>
public sealed class UnitIdentity public sealed class UnitIdentity
{ {
public Guid Id { get; } = Guid.NewGuid(); public Guid Id { get; } = Guid.NewGuid();
@ -45,6 +51,9 @@ public static class UnitComponents
} }
} }
/// <summary>
/// 单位属性组件
/// </summary>
public sealed class UnitAttributes public sealed class UnitAttributes
{ {
public PropertyValue Academic { get; set; } public PropertyValue Academic { get; set; }
@ -66,6 +75,9 @@ public static class UnitComponents
} }
} }
/// <summary>
/// 单位状态组件
/// </summary>
public sealed class UnitStatuses public sealed class UnitStatuses
{ {
public StatusValue Stress { get; set; } public StatusValue Stress { get; set; }
@ -82,6 +94,9 @@ public static class UnitComponents
} }
} }
/// <summary>
/// 单位标签组件
/// </summary>
public sealed class UnitTags public sealed class UnitTags
{ {
public string DisciplineId { get; set; } public string DisciplineId { get; set; }
@ -92,19 +107,28 @@ public static class UnitComponents
public bool HasTrait(string traitId) => TraitIds.Contains(traitId); public bool HasTrait(string traitId) => TraitIds.Contains(traitId);
} }
/// <summary>
/// 单位指派组件
/// </summary>
public sealed class UnitAssignment public sealed class UnitAssignment
{ {
public Guid? CurrentTaskId { get; set; } public Guid? CurrentTaskId { get; set; }
} }
/// <summary>
/// 单位位置组件
/// </summary>
public sealed class UnitPlacement public sealed class UnitPlacement
{ {
public GridPosition Current { get; set; } = GridPosition.Zero; public GridPosition Current { get; set; } = GridPosition.Zero;
public GridPosition Target { get; set; } = GridPosition.Zero; public GridPosition Target { get; set; } = GridPosition.Zero;
} }
/// <summary>
/// 单位装备组件
/// </summary>
public sealed class UnitEquipment public sealed class UnitEquipment
{ {
public List<string> EquippedItemIds { get; } = new(); public List<string> EquippedItemIds { get; } = new();
} }
} }

View File

@ -17,20 +17,49 @@ namespace Models;
/// </summary> /// </summary>
public sealed class UnitModel public sealed class UnitModel
{ {
/// <summary>
/// 身份组件
/// </summary>
public UnitComponents.UnitIdentity Identity { get; } public UnitComponents.UnitIdentity Identity { get; }
/// <summary>
/// 属性组件
/// </summary>
public UnitComponents.UnitAttributes Attributes { get; } public UnitComponents.UnitAttributes Attributes { get; }
/// <summary>
/// 状态组件
/// </summary>
public UnitComponents.UnitStatuses Statuses { get; } public UnitComponents.UnitStatuses Statuses { get; }
/// <summary>
/// 标签组件
/// </summary>
public UnitComponents.UnitTags Tags { get; } public UnitComponents.UnitTags Tags { get; }
/// <summary>
/// 指派组件
/// </summary>
public UnitComponents.UnitAssignment Assignment { get; } public UnitComponents.UnitAssignment Assignment { get; }
/// <summary>
/// 位置组件
/// </summary>
public UnitComponents.UnitPlacement Placement { get; } public UnitComponents.UnitPlacement Placement { get; }
/// <summary>
/// 装备组件
/// </summary>
public UnitComponents.UnitEquipment Equipment { get; } public UnitComponents.UnitEquipment Equipment { get; }
/// <summary>
/// 名字(便捷访问)
/// </summary>
public string Name public string Name
{ {
get => Identity.Name; get => Identity.Name;
set => Identity.Name = value; set => Identity.Name = value;
} }
/// <summary>
/// 构造函数
/// </summary>
/// <param name="name">名字</param>
/// <param name="random">随机数生成器</param>
public UnitModel(string name, Random random = null) public UnitModel(string name, Random random = null)
{ {
Identity = new UnitComponents.UnitIdentity(name); Identity = new UnitComponents.UnitIdentity(name);
@ -41,4 +70,4 @@ public sealed class UnitModel
Placement = new UnitComponents.UnitPlacement(); Placement = new UnitComponents.UnitPlacement();
Equipment = new UnitComponents.UnitEquipment(); Equipment = new UnitComponents.UnitEquipment();
} }
} }

View File

@ -2,6 +2,9 @@ using Godot;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
/// <summary>
/// 玩家数据管理类
/// </summary>
public partial class Player : Node public partial class Player : Node
{ {
/// <summary> /// <summary>
@ -37,12 +40,23 @@ public partial class Player : Node
public int Total => Facility + Operational + Labor; public int Total => Facility + Operational + Labor;
} }
/// <summary>
/// 时间轴类型
/// </summary>
public class TimelineType public class TimelineType
{ {
/// <summary>
/// 内部日期
/// </summary>
public DateOnly InternalDate { get; set; } public DateOnly InternalDate { get; set; }
private Dictionary<DateOnly, HashSet<Guid>> _events; private Dictionary<DateOnly, HashSet<Guid>> _events;
/// <summary>
/// 订阅事件
/// </summary>
/// <param name="date">日期</param>
/// <param name="eventUuid">事件UUID</param>
public void Subscribe(DateOnly date, Guid eventUuid) public void Subscribe(DateOnly date, Guid eventUuid)
{ {
_events ??= new Dictionary<DateOnly, HashSet<Guid>>(); _events ??= new Dictionary<DateOnly, HashSet<Guid>>();
@ -50,6 +64,11 @@ public partial class Player : Node
_events[date].Add(eventUuid); _events[date].Add(eventUuid);
} }
/// <summary>
/// 取消订阅事件
/// </summary>
/// <param name="date">日期</param>
/// <param name="eventUuid">事件UUID</param>
public void Unsubscribe(DateOnly date, Guid eventUuid) public void Unsubscribe(DateOnly date, Guid eventUuid)
{ {
if (_events == null) return; if (_events == null) return;
@ -57,6 +76,9 @@ public partial class Player : Node
_events[date].Remove(eventUuid); _events[date].Remove(eventUuid);
} }
/// <summary>
/// 进入下一天
/// </summary>
private void NextDay() private void NextDay()
{ {
if (_events == null) return; if (_events == null) return;
@ -78,27 +100,53 @@ public partial class Player : Node
} }
} }
/// <summary>
/// 附加计时器
/// </summary>
/// <param name="ticker">计时器</param>
public void Attach(Timer ticker) public void Attach(Timer ticker)
{ {
ticker.Timeout += NextDay; ticker.Timeout += NextDay;
} }
/// <summary>
/// 构造函数
/// </summary>
/// <param name="startDate">开始日期</param>
public TimelineType(DateOnly startDate) public TimelineType(DateOnly startDate)
{ {
InternalDate = startDate; InternalDate = startDate;
} }
/// <summary>
/// 事件触发委托
/// </summary>
public delegate void EventTriggeredEventHandler(DateOnly date, Guid eventUuid); public delegate void EventTriggeredEventHandler(DateOnly date, Guid eventUuid);
/// <summary>
/// 事件触发事件
/// </summary>
public event EventTriggeredEventHandler OnEventTriggered; public event EventTriggeredEventHandler OnEventTriggered;
/// <summary>
/// 日期变更委托
/// </summary>
public delegate void DayChangedEventHandler(DateOnly date); public delegate void DayChangedEventHandler(DateOnly date);
/// <summary>
/// 日期变更事件
/// </summary>
public event DayChangedEventHandler OnDayChanged; public event DayChangedEventHandler OnDayChanged;
} }
/// <summary>
/// 预算实例
/// </summary>
public static BudgetType Budget { get; set; } = new(); public static BudgetType Budget { get; set; } = new();
/// <summary>
/// 时间轴实例
/// </summary>
public static readonly TimelineType Timeline = new(DateOnly.Parse("2024/11/17")); public static readonly TimelineType Timeline = new(DateOnly.Parse("2024/11/17"));
/// <summary> /// <summary>
@ -117,13 +165,20 @@ public partial class Player : Node
public static DateOnly Date { get; set; } public static DateOnly Date { get; set; }
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
/// <summary>
/// 场景加载完成时调用
/// </summary>
public override void _Ready() public override void _Ready()
{ {
} }
// Called every frame. 'delta' is the elapsed time since the previous frame. // Called every frame. 'delta' is the elapsed time since the previous frame.
/// <summary>
/// 每帧更新
/// </summary>
/// <param name="delta">帧间隔</param>
public override void _Process(double delta) public override void _Process(double delta)
{ {
} }
} }

View File

@ -1,18 +1,31 @@
using Godot; using Godot;
using System; using System;
/// <summary>
/// 资源管理类
/// </summary>
public partial class Res : Node public partial class Res : Node
{ {
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
/// <summary>
/// 场景加载完成时调用
/// </summary>
public override void _Ready() public override void _Ready()
{ {
} }
// Called every frame. 'delta' is the elapsed time since the previous frame. // Called every frame. 'delta' is the elapsed time since the previous frame.
/// <summary>
/// 每帧更新
/// </summary>
/// <param name="delta">帧间隔</param>
public override void _Process(double delta) public override void _Process(double delta)
{ {
} }
/// <summary>
/// 资源类型枚举
/// </summary>
public enum Type public enum Type
{ {
Accessory, Accessory,
@ -23,16 +36,32 @@ public partial class Res : Node
Phone Phone
} }
/// <summary>
/// 获取随机资源路径
/// </summary>
/// <param name="resType">资源类型</param>
/// <returns>资源路径</returns>
public static string GetRandom(Type resType) public static string GetRandom(Type resType)
{ {
return GetRandom(resType, false); return GetRandom(resType, false);
} }
/// <summary>
/// 转换为16x16精灵路径
/// </summary>
/// <param name="path">原路径</param>
/// <returns>16x16路径</returns>
private static string To16Path(string path) private static string To16Path(string path)
{ {
return path.Replace("_48x48_", "_").Replace("_48x48", ""); return path.Replace("_48x48_", "_").Replace("_48x48", "");
} }
/// <summary>
/// 获取随机资源路径可选16x16
/// </summary>
/// <param name="resType">资源类型</param>
/// <param name="use16X16Sprites">是否使用16x16精灵</param>
/// <returns>资源路径</returns>
public static string GetRandom(Type resType, bool use16X16Sprites) public static string GetRandom(Type resType, bool use16X16Sprites)
{ {
var resources = allResources[(int)resType]; var resources = allResources[(int)resType];
@ -48,6 +77,9 @@ public partial class Res : Node
return ResourceLoader.Exists(path16) ? path16 : path; return ResourceLoader.Exists(path16) ? path16 : path;
} }
/// <summary>
/// 身体资源列表
/// </summary>
public static readonly string[] Body = public static readonly string[] Body =
[ [
"res://resources/characters/bodies/Body_48x48_01.png", "res://resources/characters/bodies/Body_48x48_01.png",
@ -60,6 +92,9 @@ public partial class Res : Node
"res://resources/characters/bodies/Body_48x48_08.png" "res://resources/characters/bodies/Body_48x48_08.png"
]; ];
/// <summary>
/// 饰品资源列表
/// </summary>
public static readonly string[] Accessory = public static readonly string[] Accessory =
[ [
"res://resources/characters/accessories/Accessory_01_Ladybug_48x48_01.png", "res://resources/characters/accessories/Accessory_01_Ladybug_48x48_01.png",
@ -145,6 +180,9 @@ public partial class Res : Node
"res://resources/characters/accessories/Accessory_19_Party_Cone_48x48_04.png" "res://resources/characters/accessories/Accessory_19_Party_Cone_48x48_04.png"
]; ];
/// <summary>
/// 眼睛资源列表
/// </summary>
public static readonly string[] Eye = public static readonly string[] Eye =
[ [
"res://resources/characters/eyes/Eyes_48x48_01.png", "res://resources/characters/eyes/Eyes_48x48_01.png",
@ -156,6 +194,9 @@ public partial class Res : Node
"res://resources/characters/eyes/Eyes_48x48_07.png" "res://resources/characters/eyes/Eyes_48x48_07.png"
]; ];
/// <summary>
/// 发型资源列表
/// </summary>
public static readonly string[] Hair = public static readonly string[] Hair =
[ [
"res://resources/characters/hairstyles/Hairstyle_01_48x48_01.png", "res://resources/characters/hairstyles/Hairstyle_01_48x48_01.png",
@ -360,6 +401,9 @@ public partial class Res : Node
"res://resources/characters/hairstyles/Hairstyle_29_48x48_06.png" "res://resources/characters/hairstyles/Hairstyle_29_48x48_06.png"
]; ];
/// <summary>
/// 服装资源列表
/// </summary>
public static readonly string[] Outfit = public static readonly string[] Outfit =
[ [
"res://resources/characters/outfits/Outfit_01_48x48_01.png", "res://resources/characters/outfits/Outfit_01_48x48_01.png",
@ -496,6 +540,9 @@ public partial class Res : Node
"res://resources/characters/outfits/Outfit_33_48x48_03.png" "res://resources/characters/outfits/Outfit_33_48x48_03.png"
]; ];
/// <summary>
/// 手机资源列表
/// </summary>
public static readonly string[] Smartphone = public static readonly string[] Smartphone =
[ [
"res://resources/characters/smartphones/Smartphone_48x48_1.png", "res://resources/characters/smartphones/Smartphone_48x48_1.png",
@ -515,4 +562,4 @@ public partial class Res : Node
Smartphone Smartphone
]; ];
} }

View File

@ -1,9 +1,15 @@
using Godot; using Godot;
using System; using System;
/// <summary>
/// 场景过渡控制器
/// </summary>
public partial class SceneTransit : CanvasLayer public partial class SceneTransit : CanvasLayer
{ {
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
/// <summary>
/// 场景加载完成时调用
/// </summary>
public override void _Ready() public override void _Ready()
{ {
Layer = -1; Layer = -1;
@ -11,10 +17,21 @@ public partial class SceneTransit : CanvasLayer
} }
// Called every frame. 'delta' is the elapsed time since the previous frame. // Called every frame. 'delta' is the elapsed time since the previous frame.
/// <summary>
/// 每帧更新
/// </summary>
/// <param name="delta">帧间隔</param>
public override void _Process(double delta) public override void _Process(double delta)
{ {
} }
/// <summary>
/// 切换场景
/// </summary>
/// <param name="nextScene">下一场景路径</param>
/// <param name="needLoadResources">是否需要加载资源</param>
/// <param name="animation">过渡动画名称</param>
/// <param name="waitSecond">等待时间</param>
public async void Transit(string nextScene, bool needLoadResources = true, string animation = "transit", float waitSecond = 0) public async void Transit(string nextScene, bool needLoadResources = true, string animation = "transit", float waitSecond = 0)
{ {
SetProcess(true); SetProcess(true);
@ -39,4 +56,4 @@ public partial class SceneTransit : CanvasLayer
SetProcess(false); SetProcess(false);
} }
} }

View File

@ -5,15 +5,34 @@ using System.Linq;
using Models; using Models;
// ReSharper disable CheckNamespace // ReSharper disable CheckNamespace
/// <summary>
/// 学生角色控制器
/// </summary>
public partial class Student : CharacterBody2D public partial class Student : CharacterBody2D
{ {
/// <summary>
/// 移动速度
/// </summary>
public float Speed { get; set; } = 8.0f; public float Speed { get; set; } = 8.0f;
/// <summary>
/// 跳跃速度
/// </summary>
public const float JumpVelocity = -400.0f; public const float JumpVelocity = -400.0f;
/// <summary>
/// 是否使用16x16精灵
/// </summary>
[Export] public bool Use16X16Sprites { get; set; } [Export] public bool Use16X16Sprites { get; set; }
// --- MVP: Model Binding --- // --- MVP: Model Binding ---
/// <summary>
/// 学生数据模型
/// </summary>
public StudentModel Model { get; private set; } public StudentModel Model { get; private set; }
/// <summary>
/// 绑定数据模型
/// </summary>
/// <param name="model">学生模型</param>
public void BindData(StudentModel model) public void BindData(StudentModel model)
{ {
Model = model; Model = model;
@ -21,15 +40,35 @@ public partial class Student : CharacterBody2D
} }
// -------------------------- // --------------------------
/// <summary>
/// 下一个状态类型(未使用)
/// </summary>
public int NextType = -1; public int NextType = -1;
private Queue<Vector2I> _pathToGo = new(); private Queue<Vector2I> _pathToGo = new();
private AnimationPlayer _animationPlayer; private AnimationPlayer _animationPlayer;
/// <summary>
/// 角色状态枚举
/// </summary>
public enum CharacterState { Idle, Walking, Sitting, SittingDown, StandingUp } public enum CharacterState { Idle, Walking, Sitting, SittingDown, StandingUp }
/// <summary>
/// 当前状态
/// </summary>
public CharacterState State { get; set; } = CharacterState.Idle; public CharacterState State { get; set; } = CharacterState.Idle;
/// <summary>
/// 方向枚举
/// </summary>
public enum Direction { Up, Down, Left, Right } public enum Direction { Up, Down, Left, Right }
/// <summary>
/// 目标方向
/// </summary>
public Direction TargetDirection { get; set; } = Direction.Up; public Direction TargetDirection { get; set; } = Direction.Up;
/// <summary>
/// 状态队列
/// </summary>
public Queue<CharacterState> StateQueue { get; set; } = new(); public Queue<CharacterState> StateQueue { get; set; } = new();
private Vector2I _targetSpecialPosition; private Vector2I _targetSpecialPosition;
@ -54,6 +93,11 @@ public partial class Student : CharacterBody2D
State = StateQueue.Dequeue(); State = StateQueue.Dequeue();
GlobalPosition = _lastWalkablePosition; GlobalPosition = _lastWalkablePosition;
} }
/// <summary>
/// 物理处理
/// </summary>
/// <param name="delta">时间间隔</param>
public override void _PhysicsProcess(double delta) public override void _PhysicsProcess(double delta)
{ {
if (StateQueue.Count > 0) { if (StateQueue.Count > 0) {
@ -141,6 +185,9 @@ public partial class Student : CharacterBody2D
// MoveAndSlide(); // MoveAndSlide();
} }
/// <summary>
/// 准备就绪时调用
/// </summary>
public override void _Ready() public override void _Ready()
{ {
base._Ready(); base._Ready();
@ -163,6 +210,10 @@ public partial class Student : CharacterBody2D
} }
} }
/// <summary>
/// 移动跟随路径
/// </summary>
/// <param name="path">路径点列表</param>
public void MoveFollowPath(List<Vector2I> path) public void MoveFollowPath(List<Vector2I> path)
{ {
foreach (var p in path) foreach (var p in path)
@ -171,8 +222,14 @@ public partial class Student : CharacterBody2D
} }
} }
/// <summary>
/// 关联的格子ID
/// </summary>
public Guid CubeId; public Guid CubeId;
/// <summary>
/// 随机前往某处
/// </summary>
public void GoTo() { public void GoTo() {
if (State == CharacterState.SittingDown || State == CharacterState.StandingUp || State == CharacterState.Walking) if (State == CharacterState.SittingDown || State == CharacterState.StandingUp || State == CharacterState.Walking)
{ {
@ -201,6 +258,9 @@ public partial class Student : CharacterBody2D
RandomChangeBody(); RandomChangeBody();
} }
/// <summary>
/// 前往座位
/// </summary>
public void GoToSeat() { public void GoToSeat() {
if (State == CharacterState.SittingDown || State == CharacterState.StandingUp || State == CharacterState.Walking) if (State == CharacterState.SittingDown || State == CharacterState.StandingUp || State == CharacterState.Walking)
{ {
@ -248,6 +308,9 @@ public partial class Student : CharacterBody2D
RandomChangeBody(); RandomChangeBody();
} }
/// <summary>
/// 随机更换外观
/// </summary>
private void RandomChangeBody() private void RandomChangeBody()
{ {
GetNode<Sprite2D>("parts/body").Texture = ResourceLoader.Load<Texture2D>(Res.GetRandom(Res.Type.Body, Use16X16Sprites)); GetNode<Sprite2D>("parts/body").Texture = ResourceLoader.Load<Texture2D>(Res.GetRandom(Res.Type.Body, Use16X16Sprites));
@ -257,4 +320,4 @@ public partial class Student : CharacterBody2D
GetNode<Sprite2D>("parts/accessory").Texture = ResourceLoader.Load<Texture2D>(Res.GetRandom(Res.Type.Accessory, 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)); GetNode<Sprite2D>("parts/smartphone").Texture = ResourceLoader.Load<Texture2D>(Res.GetRandom(Res.Type.Phone, Use16X16Sprites));
} }
} }

View File

@ -2,20 +2,33 @@ using Godot;
using System; using System;
using System.IO; using System.IO;
/// <summary>
/// 学生姓名生成器
/// </summary>
public partial class StudentName : Node public partial class StudentName : Node
{ {
// Called when the node enters the scene tree for the first time. // Called when the node enters the scene tree for the first time.
/// <summary>
/// 场景加载完成时调用
/// </summary>
public override void _Ready() public override void _Ready()
{ {
} }
// Called every frame. 'delta' is the elapsed time since the previous frame. // Called every frame. 'delta' is the elapsed time since the previous frame.
/// <summary>
/// 每帧更新
/// </summary>
/// <param name="delta">帧间隔</param>
public override void _Process(double delta) public override void _Process(double delta)
{ {
} }
public string GenerateName() /// <summary>
{ /// 生成随机姓名
string[] = { "大", "小", "飞", "傻", "呆", "雷", "东", "西", "王", "赵", "李", "张", "刘", "周" ,"赵", "钱", "孙", "李", "周", "吴", "郑", "王", "冯", "陈", "褚", "卫", "蒋", "沈", "韩", "杨", "朱", "秦", "尤", "许", "何", "吕", "施", "张", "孔", "曹", "严", "华", "金", "魏", "陶", "姜", "戚", "谢", "邹", "喻", "柏", "水", "窦", "章", "云", "苏", "潘", "葛", "奚", "范", "彭", "郎", "鲁", "韦", "昌", "马", "苗", "凤", "花", "方", "俞", "任", "袁", "柳", "酆", "鲍", "史", "唐", "费", "廉", "岑", "薛", "雷", "贺", "倪", "汤", "滕", "殷", "罗", "毕", "郝", "邬", "安", "常", "乐", "于", "时", "傅", "皮", "卞", "齐", "康", "伍", "余", "元", "卜", "顾", "孟", "平", "黄", "和", "穆", "萧", "尹", "姚", "邵", "湛", "汪", "祁", "毛", "禹", "狄", "米", "贝", "明", "臧", "计", "伏", "成", "戴", "谈", "宋", "茅", "庞", "熊", "纪", "舒", "屈", "项", "祝", "董", "梁", "杜", "阮", "蓝", "闵", "席", "季", "麻", "强", "贾", "路", "娄", "危", "江", "童", "颜", "郭", "梅", "盛", "林", "刁", "钟", "徐", "邱", "骆", "高", "夏", "蔡", "田", "樊", "胡", "凌", "霍", "虞", "万", "支", "柯", "昝", "管", "卢", "莫", "经", "房", "裘", "缪", "干", "解", "应", "宗", "丁", "宣", "贲", "邓", "郁", "单", "杭", "洪", "包", "诸", "左", "石", "崔", "吉", "钮", "龚", "程", "嵇", "邢", "滑", "裴", "陆", "荣", "翁", "荀", "羊", "於", "惠", "甄", "麴", "家", "封", "芮", "羿", "储", "靳", "汲", "邴", "糜", "松", "井", "段", "富", "巫", "乌", "焦", "巴", "弓", "牧", "隗", "山", "谷", "车", "侯", "宓", "蓬", "全", "郗", "班", "仰", "秋", "仲", "伊", "宫", "宁", "仇", "栾", "暴", "甘", "钭", "厉", "戎", "祖", "武", "符", "刘", "景", "詹", "束", "龙", "叶", "幸", "司", "韶", "郜", "黎", "蓟", "薄", "印", "宿", "白", "怀", "蒲", "邰", "从", "鄂", "索", "咸", "籍", "赖", "卓", "蔺", "屠", "蒙", "池", "乔", "阴", "欎", "胥", "能", "苍", "双", "闻", "莘", "党", "翟", "谭", "贡", "劳", "逄", "姬", "申", "扶", "堵", "冉", "宰", "郦", "雍", "舄", "璩", "桑", "桂", "濮", "牛", "寿", "通", "边", "扈", "燕", "冀", "郏", "浦", "尚", "农", "温", "别", "庄", "晏", "柴", "瞿", "阎", "充", "慕", "连", "茹", "习", "宦", "艾", "鱼", "容", "向", "古", "易", "慎", "戈", "廖", "庾", "终", "暨", "居", "衡", "步", "都", "耿", "满", "弘", "匡", "国", "文", "寇", "广", "禄", "阙", "东", "殴", "殳", "沃", "利", "蔚", "越", "夔", "隆", "师", "巩", "厍", "聂", "晁", "勾", "敖", "融", "冷", "訾", "辛", "阚", "那", "简", "饶", "空", "曾", "毋", "沙", "乜", "养", "鞠", "须", "丰", "巢", "关", "蒯", "相", "查", "後", "荆", "红", "游", "竺", "权", "逯", "盖", "益", "桓", "公", "万俟", "司马", "上官", "欧阳", "夏侯", "诸葛", "闻人", "东方", "赫连", "皇甫", "尉迟", "公羊", "澹台", "公冶", "宗政", "濮阳", "淳于", "单于", "太叔", "申屠", "公孙", "仲孙", "轩辕", "令狐", "钟离", "宇文", "长孙", "慕容", "鲜于", "闾丘", "司徒", "司空", "亓官", "司寇", "仉", "督", "子车", "颛孙", "端木", "巫马", "公西", "漆雕", "乐正", "壤驷", "公良", "拓跋", "夹谷", "宰父", "谷梁", "晋", "楚", "闫", "法", "汝", "鄢", "涂", "钦", "段干", "百里", "东郭", "南门", "呼延", "归", "海", "羊舌", "微生", "岳", "帅", "缑", "亢", "况", "后", "有", "琴", "梁丘", "左丘", "东门", "西门", "商", "牟", "佘", "佴", "伯", "赏", "南宫", "墨", "哈", "谯", "笪", "年", "爱", "阳", "佟", "第五", "言", "福", "百", "家", "姓", "终"}; /// </summary>
/// <returns>随机姓名</returns>
public string GenerateName()
{ string[] = { "大", "小", "飞", "傻", "呆", "雷", "东", "西", "王", "赵", "李", "张", "刘", "周" ,"赵", "钱", "孙", "李", "周", "吴", "郑", "王", "冯", "陈", "褚", "卫", "蒋", "沈", "韩", "杨", "朱", "秦", "尤", "许", "何", "吕", "施", "张", "孔", "曹", "严", "华", "金", "魏", "陶", "姜", "戚", "谢", "邹", "喻", "柏", "水", "窦", "章", "云", "苏", "潘", "葛", "奚", "范", "彭", "郎", "鲁", "韦", "昌", "马", "苗", "凤", "花", "方", "俞", "任", "袁", "柳", "酆", "鲍", "史", "唐", "费", "廉", "岑", "薛", "雷", "贺", "倪", "汤", "滕", "殷", "罗", "毕", "郝", "邬", "安", "常", "乐", "于", "时", "傅", "皮", "卞", "齐", "康", "伍", "余", "元", "卜", "顾", "孟", "平", "黄", "和", "穆", "萧", "尹", "姚", "邵", "湛", "汪", "祁", "毛", "禹", "狄", "米", "贝", "明", "臧", "计", "伏", "成", "戴", "谈", "宋", "茅", "庞", "熊", "纪", "舒", "屈", "项", "祝", "董", "梁", "杜", "阮", "蓝", "闵", "席", "季", "麻", "强", "贾", "路", "娄", "危", "江", "童", "颜", "郭", "梅", "盛", "林", "刁", "钟", "徐", "邱", "骆", "高", "夏", "蔡", "田", "樊", "胡", "凌", "霍", "虞", "万", "支", "柯", "昝", "管", "卢", "莫", "经", "房", "裘", "缪", "干", "解", "应", "宗", "丁", "宣", "贲", "邓", "郁", "单", "杭", "洪", "包", "诸", "左", "石", "崔", "吉", "钮", "龚", "程", "嵇", "邢", "滑", "裴", "陆", "荣", "翁", "荀", "羊", "於", "惠", "甄", "麴", "家", "封", "芮", "羿", "储", "靳", "汲", "邴", "糜", "松", "井", "段", "富", "巫", "乌", "焦", "巴", "弓", "牧", "隗", "山", "谷", "车", "侯", "宓", "蓬", "全", "郗", "班", "仰", "秋", "仲", "伊", "宫", "宁", "仇", "栾", "暴", "甘", "钭", "厉", "戎", "祖", "武", "符", "刘", "景", "詹", "束", "龙", "叶", "幸", "司", "韶", "郜", "黎", "蓟", "薄", "印", "宿", "白", "怀", "蒲", "邰", "从", "鄂", "索", "咸", "籍", "赖", "卓", "蔺", "屠", "蒙", "池", "乔", "阴", "欎", "胥", "能", "苍", "双", "闻", "莘", "党", "翟", "谭", "贡", "劳", "逄", "姬", "申", "扶", "堵", "冉", "宰", "郦", "雍", "舄", "璩", "桑", "桂", "濮", "牛", "寿", "通", "边", "扈", "燕", "冀", "郏", "浦", "尚", "农", "温", "别", "庄", "晏", "柴", "瞿", "阎", "充", "慕", "连", "茹", "习", "宦", "艾", "鱼", "容", "向", "古", "易", "慎", "戈", "廖", "庾", "终", "暨", "居", "衡", "步", "都", "耿", "满", "弘", "匡", "国", "文", "寇", "广", "禄", "阙", "东", "殴", "殳", "沃", "利", "蔚", "越", "夔", "隆", "师", "巩", "厍", "聂", "晁", "勾", "敖", "融", "冷", "訾", "辛", "阚", "那", "简", "饶", "空", "曾", "毋", "沙", "乜", "养", "鞠", "须", "丰", "巢", "关", "蒯", "相", "查", "後", "荆", "红", "游", "竺", "权", "逯", "盖", "益", "桓", "公", "万俟", "司马", "上官", "欧阳", "夏侯", "诸葛", "闻人", "东方", "赫连", "皇甫", "尉迟", "公羊", "澹台", "公冶", "宗政", "濮阳", "淳于", "单于", "太叔", "申屠", "公孙", "仲孙", "轩辕", "令狐", "钟离", "宇文", "长孙", "慕容", "鲜于", "闾丘", "司徒", "司空", "亓官", "司寇", "仉", "督", "子车", "颛孙", "端木", "巫马", "公西", "漆雕", "乐正", "壤驷", "公良", "拓跋", "夹谷", "宰父", "谷梁", "晋", "楚", "闫", "法", "汝", "鄢", "涂", "钦", "段干", "百里", "东郭", "南门", "呼延", "归", "海", "羊舌", "微生", "岳", "帅", "缑", "亢", "况", "后", "有", "琴", "梁丘", "左丘", "东门", "西门", "商", "牟", "佘", "佴", "伯", "赏", "南宫", "墨", "哈", "谯", "笪", "年", "爱", "阳", "佟", "第五", "言", "福", "百", "家", "姓", "终"};
string[] = { "金", "猪", "兔", "猫", "鱼", "蛋", "胖", "大", "傻", "酷", "蠢", "聪", "萌", "暴","靖", "铭", "琛", "川", "承", "司", "斯", "宗", "骁", "聪", "在", "钩", "锦", "铎", "楚", "铮", "钦", "则", "楠", "景", "茗", "聿", "启", "尧", "言", "嘉", "桉", "桐", "筒", "竹", "林", "乔", "栋", "家", "翊", "松", "清", "澈", "泫", "浚", "润", "泽", "向", "凡", "文", "浦", "洲", "珩", "玄", "洋", "淮", "雨", "子", "云", "卓", "昱", "南", "晨", "知", "宁", "年", "易", "晗", "炎", "焕", "哲", "煦", "旭", "明", "阳", "朗", "典", "辰", "宸", "野", "安", "为", "亦", "岚", "也", "围", "以", "延", "允", "容", "恩", "衡", "宇", "硕", "已", "意", "也", "坤", "辰", "伊", "米", "安", "恩", "以", "容", "宛", "岚", "又", "衣", "亚", "悠", "允", "画", "灿", "夏", "珞", "煊", "晴", "彤", "诺", "宁", "恬", "钧", "灵", "昭", "琉", "晨", "曦", "南", "毓", "冉", "妍", "澜", "淇", "沐", "潆", "盈", "雨", "文", "冰", "雯", "溪", "子", "云", "汐", "潞", "淇", "妙", "涵", "楠", "景", "茗", "聿", "启", "尧", "言", "嘉", "桉", "桐", "筒", "竹", "林", "乔", "栋", "家", "翊", "松", "真", "心", "新", "悦", "西", "兮", "楚", "初", "千", "锐", "素", "锦", "静", "镜", "斯", "舒", "瑜", "童" }; string[] = { "金", "猪", "兔", "猫", "鱼", "蛋", "胖", "大", "傻", "酷", "蠢", "聪", "萌", "暴","靖", "铭", "琛", "川", "承", "司", "斯", "宗", "骁", "聪", "在", "钩", "锦", "铎", "楚", "铮", "钦", "则", "楠", "景", "茗", "聿", "启", "尧", "言", "嘉", "桉", "桐", "筒", "竹", "林", "乔", "栋", "家", "翊", "松", "清", "澈", "泫", "浚", "润", "泽", "向", "凡", "文", "浦", "洲", "珩", "玄", "洋", "淮", "雨", "子", "云", "卓", "昱", "南", "晨", "知", "宁", "年", "易", "晗", "炎", "焕", "哲", "煦", "旭", "明", "阳", "朗", "典", "辰", "宸", "野", "安", "为", "亦", "岚", "也", "围", "以", "延", "允", "容", "恩", "衡", "宇", "硕", "已", "意", "也", "坤", "辰", "伊", "米", "安", "恩", "以", "容", "宛", "岚", "又", "衣", "亚", "悠", "允", "画", "灿", "夏", "珞", "煊", "晴", "彤", "诺", "宁", "恬", "钧", "灵", "昭", "琉", "晨", "曦", "南", "毓", "冉", "妍", "澜", "淇", "沐", "潆", "盈", "雨", "文", "冰", "雯", "溪", "子", "云", "汐", "潞", "淇", "妙", "涵", "楠", "景", "茗", "聿", "启", "尧", "言", "嘉", "桉", "桐", "筒", "竹", "林", "乔", "栋", "家", "翊", "松", "真", "心", "新", "悦", "西", "兮", "楚", "初", "千", "锐", "素", "锦", "静", "镜", "斯", "舒", "瑜", "童" };
string[] = { "子", "郎", "妹", "宝", "儿", "汉", "君", "爷", "娃", "猪", "鬼", "鸟", "仔", "蛋","靖", "铭", "琛", "川", "承", "司", "斯", "宗", "骁", "聪", "在", "钩", "锦", "铎", "楚", "铮", "钦", "则", "楠", "景", "茗", "聿", "启", "尧", "言", "嘉", "桉", "桐", "筒", "竹", "林", "乔", "栋", "家", "翊", "松", "清", "澈", "泫", "浚", "润", "泽", "向", "凡", "文", "浦", "洲", "珩", "玄", "洋", "淮", "雨", "子", "云", "卓", "昱", "南", "晨", "知", "宁", "年", "易", "晗", "炎", "焕", "哲", "煦", "旭", "明", "阳", "朗", "典", "辰", "宸", "野", "安", "为", "亦", "岚", "也", "围", "以", "延", "允", "容", "恩", "衡", "宇", "硕", "已", "意", "也", "坤", "辰", "伊", "米", "安", "恩", "以", "容", "宛", "岚", "又", "衣", "亚", "悠", "允", "画", "灿", "夏", "珞", "煊", "晴", "彤", "诺", "宁", "恬", "钧", "灵", "昭", "琉", "晨", "曦", "南", "毓", "冉", "妍", "澜", "淇", "沐", "潆", "盈", "雨", "文", "冰", "雯", "溪", "子", "云", "汐", "潞", "淇", "妙", "涵", "楠", "景", "茗", "聿", "启", "尧", "言", "嘉", "桉", "桐", "筒", "竹", "林", "乔", "栋", "家", "翊", "松", "真", "心", "新", "悦", "西", "兮", "楚", "初", "千", "锐", "素", "锦", "静", "镜", "斯", "舒", "瑜", "童" }; string[] = { "子", "郎", "妹", "宝", "儿", "汉", "君", "爷", "娃", "猪", "鬼", "鸟", "仔", "蛋","靖", "铭", "琛", "川", "承", "司", "斯", "宗", "骁", "聪", "在", "钩", "锦", "铎", "楚", "铮", "钦", "则", "楠", "景", "茗", "聿", "启", "尧", "言", "嘉", "桉", "桐", "筒", "竹", "林", "乔", "栋", "家", "翊", "松", "清", "澈", "泫", "浚", "润", "泽", "向", "凡", "文", "浦", "洲", "珩", "玄", "洋", "淮", "雨", "子", "云", "卓", "昱", "南", "晨", "知", "宁", "年", "易", "晗", "炎", "焕", "哲", "煦", "旭", "明", "阳", "朗", "典", "辰", "宸", "野", "安", "为", "亦", "岚", "也", "围", "以", "延", "允", "容", "恩", "衡", "宇", "硕", "已", "意", "也", "坤", "辰", "伊", "米", "安", "恩", "以", "容", "宛", "岚", "又", "衣", "亚", "悠", "允", "画", "灿", "夏", "珞", "煊", "晴", "彤", "诺", "宁", "恬", "钧", "灵", "昭", "琉", "晨", "曦", "南", "毓", "冉", "妍", "澜", "淇", "沐", "潆", "盈", "雨", "文", "冰", "雯", "溪", "子", "云", "汐", "潞", "淇", "妙", "涵", "楠", "景", "茗", "聿", "启", "尧", "言", "嘉", "桉", "桐", "筒", "竹", "林", "乔", "栋", "家", "翊", "松", "真", "心", "新", "悦", "西", "兮", "楚", "初", "千", "锐", "素", "锦", "静", "镜", "斯", "舒", "瑜", "童" };

View File

@ -1,26 +1,32 @@
using Godot; using Godot;
using System; using System;
/// <summary>
/// 测试地图层
/// </summary>
public partial class TestMap : TileMapLayer public partial class TestMap : TileMapLayer
{ {
/// <summary> /// <summary>
/// The last cell that was highlighted. Used to avoid unnecessary updates. /// 上一次高亮的单元格。用于避免不必要的更新。
/// </summary> /// </summary>
private Vector2I _lastHighlightCell; private Vector2I _lastHighlightCell;
private readonly Vector2I _highlightTileCoord = new(0, 5); private readonly Vector2I _highlightTileCoord = new(0, 5);
private readonly Vector2I _vector2INegOne = new(-1, -1); private readonly Vector2I _vector2INegOne = new(-1, -1);
// Called when the node enters the scene tree for the first time.
// Called when the node enters the scene tree for the first time.
/// <summary>
/// 场景加载完成时调用
/// </summary>
public override void _Ready() public override void _Ready()
{ {
// Initialize the lastHighlightCell to an invalid value. // 初始化最后高亮单元格为无效值。
_lastHighlightCell = _vector2INegOne; _lastHighlightCell = _vector2INegOne;
} }
/// <summary> /// <summary>
/// Called every frame. /// 每帧更新。
/// </summary> /// </summary>
/// <param name="delta">the elapsed time since the previous frame.</param> /// <param name="delta">距离上一帧的时间间隔。</param>
public override void _Process(double delta) public override void _Process(double delta)
{ {
Vector2 mousePos = GetLocalMousePosition(); Vector2 mousePos = GetLocalMousePosition();
@ -36,5 +42,4 @@ public partial class TestMap : TileMapLayer
SetCell(cell, 0, _highlightTileCoord); SetCell(cell, 0, _highlightTileCoord);
} }
} }
}
}

View File

@ -3,23 +3,39 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
/// <summary>
/// 图块映射辅助类
/// </summary>
public partial class TileMapping : Node public partial class TileMapping : Node
{ {
/// <summary> /// <summary>
/// From tile coordinates to pixel coordinates. If null, use the offset instead. /// 从图块坐标到像素坐标的映射。如果不为空,则使用此映射。
/// </summary> /// </summary>
private Dictionary<Vector2I, Vector2I> _pixelMap; private Dictionary<Vector2I, Vector2I> _pixelMap;
/// <summary> /// <summary>
/// If pixelMap is null, use this offset to convert tile coordinates to pixel coordinates. /// 如果 pixelMap 为空,使用此偏移量将图块坐标转换为像素坐标。
/// </summary> /// </summary>
private Vector2I _pixelOffset; private Vector2I _pixelOffset;
/// <summary>
/// 图块区域
/// </summary>
private Rect2I _tileRect; private Rect2I _tileRect;
/// <summary>
/// 构造函数,使用坐标映射字典
/// </summary>
/// <param name="map">坐标映射字典</param>
public TileMapping(Dictionary<Vector2I, Vector2I> map) { public TileMapping(Dictionary<Vector2I, Vector2I> map) {
_pixelMap = map; _pixelMap = map;
_pixelOffset = new Vector2I(-int.MaxValue, -int.MaxValue); _pixelOffset = new Vector2I(-int.MaxValue, -int.MaxValue);
} }
/// <summary>
/// 构造函数,使用偏移量和区域
/// </summary>
/// <param name="offset">偏移量</param>
/// <param name="rect">区域</param>
public TileMapping(Vector2I offset, Rect2I rect) { public TileMapping(Vector2I offset, Rect2I rect) {
_pixelMap = null; _pixelMap = null;
_pixelOffset = offset; _pixelOffset = offset;
@ -28,6 +44,10 @@ public partial class TileMapping : Node
public TileMapping() {} public TileMapping() {}
/// <summary>
/// 应用映射到TileMapLayer
/// </summary>
/// <param name="layer">TileMapLayer</param>
public void Apply(TileMapLayer layer) { public void Apply(TileMapLayer layer) {
// if pixelMap is not null, use all the pairs in pixelMap to set the tiles. // if pixelMap is not null, use all the pairs in pixelMap to set the tiles.
if (_pixelMap != null) { if (_pixelMap != null) {
@ -46,4 +66,4 @@ public partial class TileMapping : Node
} }
} }
} }
} }