增加注释
This commit is contained in:
parent
3a3b09c2c5
commit
541ba1695d
@ -4,8 +4,8 @@ using Godot;
|
||||
using Models;
|
||||
|
||||
/// <summary>
|
||||
/// Runtime data for a campus agent. This keeps Godot nodes and pure data separate
|
||||
/// so the behavior system can be tested without scene dependencies.
|
||||
/// 校园代理的运行时数据。将 Godot 节点与纯数据分离,
|
||||
/// 以便行为系统可以在没有场景依赖的情况下进行测试。
|
||||
/// </summary>
|
||||
public sealed class CampusAgentRuntime
|
||||
{
|
||||
@ -34,8 +34,8 @@ public sealed class CampusAgentRuntime
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Intent produced by the planner. It captures both the action and the destination,
|
||||
/// plus an optional planned duration for round-based schedules.
|
||||
/// 规划器生成的意图。它捕获了行动和目的地,
|
||||
/// 加上可选的轮次计划持续时间。
|
||||
/// </summary>
|
||||
public sealed class CampusBehaviorIntent
|
||||
{
|
||||
@ -62,8 +62,8 @@ public sealed class CampusBehaviorIntent
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shared context passed into providers/states so they can evaluate the same data
|
||||
/// without hard-coding dependencies.
|
||||
/// 传递给提供者/状态的共享上下文,以便它们可以评估相同的数据
|
||||
/// 而无需硬编码依赖关系。
|
||||
/// </summary>
|
||||
public sealed class CampusBehaviorContext
|
||||
{
|
||||
@ -92,8 +92,8 @@ public sealed class CampusBehaviorContext
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public interface ICampusBehaviorProvider
|
||||
{
|
||||
@ -101,8 +101,8 @@ public interface ICampusBehaviorProvider
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Critical state provider: handles sanity collapse, extreme stress, or exhaustion.
|
||||
/// This is the highest priority in the decision queue.
|
||||
/// 紧急状态提供者:处理理智崩溃、极度压力或力竭。
|
||||
/// 这是决策队列中的最高优先级。
|
||||
/// </summary>
|
||||
public sealed class CriticalBehaviorProvider : ICampusBehaviorProvider
|
||||
{
|
||||
@ -153,7 +153,7 @@ public sealed class CriticalBehaviorProvider : ICampusBehaviorProvider
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assigned task provider: if the agent has a task, it is executed before needs.
|
||||
/// 指派任务提供者:如果代理有任务,则在需求之前执行。
|
||||
/// </summary>
|
||||
public sealed class AssignedTaskBehaviorProvider : ICampusBehaviorProvider
|
||||
{
|
||||
@ -188,8 +188,8 @@ public sealed class AssignedTaskBehaviorProvider : ICampusBehaviorProvider
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Needs provider: hunger, fatigue, mood, and social needs are handled here.
|
||||
/// It sits below assigned tasks but above trait-driven idle behavior.
|
||||
/// 需求提供者:处理饥饿、疲劳、情绪和社交需求。
|
||||
/// 它位于指派任务之下,但在特质驱动的空闲行为之上。
|
||||
/// </summary>
|
||||
public sealed class NeedsBehaviorProvider : ICampusBehaviorProvider
|
||||
{
|
||||
@ -273,8 +273,7 @@ public sealed class NeedsBehaviorProvider : ICampusBehaviorProvider
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Trait-driven provider: applies long-term personality or tag tendencies when
|
||||
/// there is no urgent need.
|
||||
/// 特质驱动提供者:当没有紧急需求时,应用长期性格或标签倾向。
|
||||
/// </summary>
|
||||
public sealed class TraitBehaviorProvider : ICampusBehaviorProvider
|
||||
{
|
||||
@ -405,7 +404,7 @@ public sealed class TraitBehaviorProvider : ICampusBehaviorProvider
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Idle provider: default fallback when nothing else applies.
|
||||
/// 闲置提供者:当没有其他适用规则时的默认后备方案。
|
||||
/// </summary>
|
||||
public sealed class IdleBehaviorProvider : ICampusBehaviorProvider
|
||||
{
|
||||
@ -420,8 +419,8 @@ public sealed class IdleBehaviorProvider : ICampusBehaviorProvider
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Planner executes providers in priority order. This lets us add or remove
|
||||
/// providers without editing the state machine.
|
||||
/// 规划器按优先级顺序执行提供者。这允许我们添加或删除提供者
|
||||
/// 而无需编辑状态机。
|
||||
/// </summary>
|
||||
public sealed class CampusBehaviorPlanner
|
||||
{
|
||||
@ -448,8 +447,7 @@ public sealed class CampusBehaviorPlanner
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// State interface for the AI FSM. Each state can transition by requesting
|
||||
/// a change via the owning behavior agent.
|
||||
/// AI FSM 的状态接口。每个状态可以通过请求更改来转换。
|
||||
/// </summary>
|
||||
public interface ICampusBehaviorState
|
||||
{
|
||||
@ -459,7 +457,7 @@ public interface ICampusBehaviorState
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// State machine wrapper to enforce enter/exit semantics.
|
||||
/// 状态机包装器,用于强制执行进入/退出语义。
|
||||
/// </summary>
|
||||
public sealed class CampusBehaviorStateMachine
|
||||
{
|
||||
@ -479,8 +477,8 @@ public sealed class CampusBehaviorStateMachine
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Decision state: pick a new intent and immediately transition to movement.
|
||||
/// This keeps the intent selection isolated and easy to extend.
|
||||
/// 决策状态:选择一个新的意图并立即转换为移动。
|
||||
/// 这使意图选择隔离且易于扩展。
|
||||
/// </summary>
|
||||
public sealed class CampusDecisionState : ICampusBehaviorState
|
||||
{
|
||||
@ -500,8 +498,8 @@ public sealed class CampusDecisionState : ICampusBehaviorState
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Movement state: navigate to the intent's target location.
|
||||
/// Once the agent arrives, it transitions into the action state.
|
||||
/// 移动状态:导航到意图的目标位置。
|
||||
/// 一旦代理到达,它将转换为动作状态。
|
||||
/// </summary>
|
||||
public sealed class CampusMoveState : ICampusBehaviorState
|
||||
{
|
||||
@ -534,8 +532,8 @@ public sealed class CampusMoveState : ICampusBehaviorState
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Action state: apply per-second deltas and update task progress.
|
||||
/// When the action duration expires, transition back to decision.
|
||||
/// 动作状态:应用每秒增量并更新任务进度。
|
||||
/// 当动作持续时间结束时,转回决策状态。
|
||||
/// </summary>
|
||||
public sealed class CampusActionState : ICampusBehaviorState
|
||||
{
|
||||
@ -642,8 +640,8 @@ public sealed class CampusActionState : ICampusBehaviorState
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public sealed class CampusBehaviorAgent
|
||||
{
|
||||
@ -1088,8 +1086,8 @@ public sealed class CampusBehaviorAgent
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Centralized IDs for traits referenced by the behavior system.
|
||||
/// Keeping them here avoids scattering magic strings.
|
||||
/// 行为系统引用的特质的集中 ID。
|
||||
/// 将它们放在这里可以避免分散的魔法字符串。
|
||||
/// </summary>
|
||||
public static class CampusTraitIds
|
||||
{
|
||||
@ -1098,4 +1096,4 @@ public static class CampusTraitIds
|
||||
public const string SocialButterfly = "core:trait_social_butterfly";
|
||||
public const string NotHuman = "core:trait_not_human";
|
||||
public const string BigEater = "core:trait_big_eater";
|
||||
}
|
||||
}
|
||||
@ -7,72 +7,72 @@ using Godot;
|
||||
using Models;
|
||||
|
||||
/// <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>
|
||||
public enum CampusLocationId
|
||||
{
|
||||
None,
|
||||
Laboratory,
|
||||
Library,
|
||||
Canteen,
|
||||
Dormitory,
|
||||
ArtificialLake,
|
||||
CoffeeShop,
|
||||
AdministrationBuilding,
|
||||
FootballField,
|
||||
RandomWander
|
||||
Laboratory, // 实验室
|
||||
Library, // 图书馆
|
||||
Canteen, // 食堂
|
||||
Dormitory, // 宿舍
|
||||
ArtificialLake, // 人工湖
|
||||
CoffeeShop, // 咖啡店
|
||||
AdministrationBuilding, // 行政楼
|
||||
FootballField, // 足球场
|
||||
RandomWander // 随机漫游
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public enum CampusActionId
|
||||
{
|
||||
None,
|
||||
Experimenting,
|
||||
Writing,
|
||||
Eating,
|
||||
Sleeping,
|
||||
Chilling,
|
||||
Staring,
|
||||
CoffeeBreak,
|
||||
Administration,
|
||||
Running,
|
||||
Socializing,
|
||||
Wandering
|
||||
Experimenting, // 做实验
|
||||
Writing, // 写作
|
||||
Eating, // 吃饭
|
||||
Sleeping, // 睡觉
|
||||
Chilling, // 放松/闲逛
|
||||
Staring, // 发呆
|
||||
CoffeeBreak, // 喝咖啡
|
||||
Administration, // 行政工作
|
||||
Running, // 跑步
|
||||
Socializing, // 社交
|
||||
Wandering // 漫步
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Priority levels match the design doc ordering: lower value = higher priority.
|
||||
/// 优先级级别,对应设计文档中的顺序:值越小优先级越高。
|
||||
/// </summary>
|
||||
public enum CampusBehaviorPriority
|
||||
{
|
||||
Critical = 0,
|
||||
AssignedTask = 1,
|
||||
Needs = 2,
|
||||
Trait = 3,
|
||||
Idle = 4
|
||||
Critical = 0, // 紧急状态(崩溃/力竭)
|
||||
AssignedTask = 1, // 指派任务
|
||||
Needs = 2, // 基础需求(饿/累/社交)
|
||||
Trait = 3, // 特质驱动(性格偏好)
|
||||
Idle = 4 // 闲置
|
||||
}
|
||||
|
||||
/// <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>
|
||||
public enum CampusTaskType
|
||||
{
|
||||
Experiment,
|
||||
Writing,
|
||||
Administration,
|
||||
Exercise,
|
||||
Coding,
|
||||
Social
|
||||
Experiment, // 实验
|
||||
Writing, // 写作
|
||||
Administration, // 行政
|
||||
Exercise, // 锻炼
|
||||
Coding, // 编程
|
||||
Social // 社交
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Action configuration loaded from JSON. Deltas are applied per second while
|
||||
/// the action is running, so longer actions accumulate more effect.
|
||||
/// 从 JSON 加载的动作配置。
|
||||
/// 变化量(Delta)在动作运行时按秒应用,因此动作越长积累的效果越多。
|
||||
/// </summary>
|
||||
public sealed class CampusActionConfig
|
||||
{
|
||||
@ -90,8 +90,8 @@ public sealed class CampusActionConfig
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Global behavior configuration for campus AI. This is intentionally data-driven
|
||||
/// so balancing can happen in JSON without touching code.
|
||||
/// 校园 AI 的全局行为配置。
|
||||
/// 这是数据驱动的,以便在不修改代码的情况下通过 JSON 进行平衡调整。
|
||||
/// </summary>
|
||||
public sealed class CampusBehaviorConfig
|
||||
{
|
||||
@ -179,8 +179,8 @@ public sealed class CampusBehaviorConfig
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Simple location registry that maps logical location ids to scene positions.
|
||||
/// This keeps the behavior system independent from scene tree details.
|
||||
/// 简单的位置注册表,将逻辑位置 ID 映射到场景位置。
|
||||
/// 保持行为系统独立于场景树细节。
|
||||
/// </summary>
|
||||
public sealed class CampusLocationRegistry
|
||||
{
|
||||
@ -199,8 +199,8 @@ public sealed class CampusLocationRegistry
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tracks current occupancy per location so traits like social phobia can react
|
||||
/// to crowd size without hard-coding scene knowledge.
|
||||
/// 跟踪每个位置的当前占用情况,以便像社交恐惧症这样的特质可以根据人群规模做出反应,
|
||||
/// 而无需硬编码场景知识。
|
||||
/// </summary>
|
||||
public sealed class CampusBehaviorWorld
|
||||
{
|
||||
@ -229,7 +229,7 @@ public sealed class CampusBehaviorWorld
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lightweight task container for the campus demo; it just tracks remaining work.
|
||||
/// 校园演示的轻量级任务容器;它只跟踪剩余工作量。
|
||||
/// </summary>
|
||||
public sealed class CampusTask
|
||||
{
|
||||
@ -251,8 +251,8 @@ public sealed class CampusTask
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Custom needs that are not yet part of the core UnitModel (hunger/social/energy).
|
||||
/// Uses PropertyValue so it plugs into the existing numeric system.
|
||||
/// 自定义需求,尚未成为核心 UnitModel 的一部分(饥饿/社交/精力)。
|
||||
/// 使用 PropertyValue 以便接入现有的数值系统。
|
||||
/// </summary>
|
||||
public sealed class CampusAgentNeeds
|
||||
{
|
||||
@ -268,4 +268,4 @@ public sealed class CampusAgentNeeds
|
||||
Social = new PropertyValue(social);
|
||||
Health = new PropertyValue(health);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,63 +3,238 @@ using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using Godot;
|
||||
|
||||
/// <summary>
|
||||
/// 校园学生角色控制器
|
||||
/// </summary>
|
||||
public partial class CampusStudent : CharacterBody2D
|
||||
{
|
||||
/// <summary>
|
||||
/// 饰品精灵
|
||||
/// </summary>
|
||||
private Sprite2D _accessory;
|
||||
/// <summary>
|
||||
/// 动画播放器
|
||||
/// </summary>
|
||||
private AnimationPlayer _animationPlayer;
|
||||
|
||||
/// <summary>
|
||||
/// 身体精灵
|
||||
/// </summary>
|
||||
private Sprite2D _body;
|
||||
/// <summary>
|
||||
/// 眼睛精灵
|
||||
/// </summary>
|
||||
private Sprite2D _eye;
|
||||
/// <summary>
|
||||
/// 发型精灵
|
||||
/// </summary>
|
||||
private Sprite2D _hairstyle;
|
||||
/// <summary>
|
||||
/// 上一帧的时间间隔
|
||||
/// </summary>
|
||||
private double _lastDelta;
|
||||
|
||||
/// <summary>
|
||||
/// 面朝方向
|
||||
/// </summary>
|
||||
private enum FacingDirection
|
||||
{
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 上一次的面朝方向
|
||||
/// </summary>
|
||||
private FacingDirection _lastFacing = FacingDirection.Down;
|
||||
/// <summary>
|
||||
/// 上一次的位置
|
||||
/// </summary>
|
||||
private Vector2 _lastPosition;
|
||||
|
||||
/// <summary>
|
||||
/// 导航代理
|
||||
/// </summary>
|
||||
private NavigationAgent2D _navigationAgent;
|
||||
/// <summary>
|
||||
/// 服装精灵
|
||||
/// </summary>
|
||||
private Sprite2D _outfit;
|
||||
/// <summary>
|
||||
/// 是否已配置巡逻
|
||||
/// </summary>
|
||||
private bool _patrolConfigured;
|
||||
/// <summary>
|
||||
/// 当前巡逻点索引
|
||||
/// </summary>
|
||||
private int _patrolIndex;
|
||||
/// <summary>
|
||||
/// 巡逻点列表
|
||||
/// </summary>
|
||||
private List<Vector2> _patrolPoints = new();
|
||||
/// <summary>
|
||||
/// 手机精灵
|
||||
/// </summary>
|
||||
private Sprite2D _smartphone;
|
||||
/// <summary>
|
||||
/// 卡住计时器
|
||||
/// </summary>
|
||||
private float _stuckTimer;
|
||||
/// <summary>
|
||||
/// 导航地图RID
|
||||
/// </summary>
|
||||
private Rid _navigationMap;
|
||||
/// <summary>
|
||||
/// 当前目标点
|
||||
/// </summary>
|
||||
private Vector2 _currentTarget = Vector2.Zero;
|
||||
/// <summary>
|
||||
/// 是否有目标
|
||||
/// </summary>
|
||||
private bool _hasTarget;
|
||||
/// <summary>
|
||||
/// 是否启用行为控制
|
||||
/// </summary>
|
||||
private bool _behaviorControlEnabled;
|
||||
/// <summary>
|
||||
/// 行为目标点
|
||||
/// </summary>
|
||||
private Vector2 _behaviorTarget = Vector2.Zero;
|
||||
/// <summary>
|
||||
/// 是否有行为目标
|
||||
/// </summary>
|
||||
private bool _behaviorHasTarget;
|
||||
/// <summary>
|
||||
/// 手机闲置动画是否激活
|
||||
/// </summary>
|
||||
private bool _phoneIdleActive;
|
||||
/// <summary>
|
||||
/// 手机退出动作锁定
|
||||
/// </summary>
|
||||
private bool _phoneExitLocked;
|
||||
/// <summary>
|
||||
/// 是否使用物理移动
|
||||
/// </summary>
|
||||
private bool _usePhysicsMovement = true;
|
||||
/// <summary>
|
||||
/// 网格路径列表
|
||||
/// </summary>
|
||||
private readonly List<Vector2> _gridPath = new();
|
||||
/// <summary>
|
||||
/// 当前网格路径索引
|
||||
/// </summary>
|
||||
private int _gridPathIndex;
|
||||
/// <summary>
|
||||
/// 网格路径是否激活
|
||||
/// </summary>
|
||||
private bool _gridPathActive;
|
||||
/// <summary>
|
||||
/// 网格路径是否挂起
|
||||
/// </summary>
|
||||
private bool _gridPathPending;
|
||||
/// <summary>
|
||||
/// AStar网格
|
||||
/// </summary>
|
||||
private AStarGrid2D _astarGrid;
|
||||
/// <summary>
|
||||
/// AStar区域
|
||||
/// </summary>
|
||||
private Rect2I _astarRegion;
|
||||
/// <summary>
|
||||
/// AStar地图迭代版本
|
||||
/// </summary>
|
||||
private int _astarMapIteration;
|
||||
/// <summary>
|
||||
/// 网格重新寻路重试计时器
|
||||
/// </summary>
|
||||
private float _gridPathRetryTimer;
|
||||
/// <summary>
|
||||
/// 导航区域引用
|
||||
/// </summary>
|
||||
private NavigationRegion2D _navigationRegion;
|
||||
|
||||
/// <summary>
|
||||
/// 移动速度
|
||||
/// </summary>
|
||||
[Export] public float MoveSpeed { get; set; } = 60.0f;
|
||||
/// <summary>
|
||||
/// 目标到达判定距离
|
||||
/// </summary>
|
||||
[Export] public float TargetReachDistance { get; set; } = 6.0f;
|
||||
/// <summary>
|
||||
/// 是否使用16x16精灵
|
||||
/// </summary>
|
||||
[Export] public bool Use16X16Sprites { get; set; } = true;
|
||||
/// <summary>
|
||||
/// 是否启用避让
|
||||
/// </summary>
|
||||
[Export] public bool EnableAvoidance { get; set; }
|
||||
/// <summary>
|
||||
/// 卡住重新寻路时间
|
||||
/// </summary>
|
||||
[Export] public float StuckRepathSeconds { get; set; } = 0.6f;
|
||||
/// <summary>
|
||||
/// 卡住距离阈值
|
||||
/// </summary>
|
||||
[Export] public float StuckDistanceEpsilon { get; set; } = 2.0f;
|
||||
/// <summary>
|
||||
/// 导航网格吸附距离
|
||||
/// </summary>
|
||||
[Export] public float NavMeshClampDistance { get; set; } = 6.0f;
|
||||
/// <summary>
|
||||
/// 是否使用网格寻路
|
||||
/// </summary>
|
||||
[Export] public bool UseGridPathfinding { get; set; } = true;
|
||||
/// <summary>
|
||||
/// 网格单元大小
|
||||
/// </summary>
|
||||
[Export] public float GridCellSize { get; set; } = 8.0f;
|
||||
/// <summary>
|
||||
/// 网格可行走容差
|
||||
/// </summary>
|
||||
[Export] public float GridWalkableTolerance { get; set; } = 2.0f;
|
||||
/// <summary>
|
||||
/// 网格搜索节点限制
|
||||
/// </summary>
|
||||
[Export] public int GridSearchNodeLimit { get; set; } = 8000;
|
||||
/// <summary>
|
||||
/// 网格重新寻路间隔
|
||||
/// </summary>
|
||||
[Export] public float GridRepathInterval { get; set; } = 0.25f;
|
||||
/// <summary>
|
||||
/// 调试绘制网格
|
||||
/// </summary>
|
||||
[Export] public bool DebugDrawGrid { get; set; }
|
||||
/// <summary>
|
||||
/// 调试仅绘制实心点
|
||||
/// </summary>
|
||||
[Export] public bool DebugDrawSolidOnly { get; set; } = true;
|
||||
/// <summary>
|
||||
/// 调试绘制路径
|
||||
/// </summary>
|
||||
[Export] public bool DebugDrawPath { get; set; }
|
||||
/// <summary>
|
||||
/// 调试绘制半径单元数
|
||||
/// </summary>
|
||||
[Export] public int DebugDrawRadiusCells { get; set; } = 20;
|
||||
/// <summary>
|
||||
/// 调试日志网格
|
||||
/// </summary>
|
||||
[Export] public bool DebugLogGrid { get; set; }
|
||||
/// <summary>
|
||||
/// 环境碰撞掩码
|
||||
/// </summary>
|
||||
[Export] public uint EnvironmentCollisionMask { get; set; } = 1u;
|
||||
/// <summary>
|
||||
/// 学生碰撞层
|
||||
/// </summary>
|
||||
[Export] public uint StudentCollisionLayer { get; set; } = 1u << 1;
|
||||
|
||||
/// <summary>
|
||||
/// 准备就绪时调用
|
||||
/// </summary>
|
||||
public override void _Ready()
|
||||
{
|
||||
_navigationAgent = GetNodeOrNull<NavigationAgent2D>("NavigationAgent2D");
|
||||
@ -94,6 +269,10 @@ public partial class CampusStudent : CharacterBody2D
|
||||
_lastPosition = GlobalPosition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 物理处理
|
||||
/// </summary>
|
||||
/// <param name="delta">时间间隔</param>
|
||||
public override void _PhysicsProcess(double delta)
|
||||
{
|
||||
_lastDelta = delta;
|
||||
@ -212,6 +391,9 @@ public partial class CampusStudent : CharacterBody2D
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 绘制调试信息
|
||||
/// </summary>
|
||||
public override void _Draw()
|
||||
{
|
||||
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)
|
||||
{
|
||||
_patrolPoints = points ?? new List<Vector2>();
|
||||
@ -281,6 +468,9 @@ public partial class CampusStudent : CharacterBody2D
|
||||
if (_navigationAgent != null) AdvanceTarget();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启用行为控制
|
||||
/// </summary>
|
||||
public void EnableBehaviorControl()
|
||||
{
|
||||
_behaviorControlEnabled = true;
|
||||
@ -289,6 +479,10 @@ public partial class CampusStudent : CharacterBody2D
|
||||
_patrolPoints.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置行为目标
|
||||
/// </summary>
|
||||
/// <param name="target">目标位置</param>
|
||||
public void SetBehaviorTarget(Vector2 target)
|
||||
{
|
||||
_behaviorControlEnabled = true;
|
||||
@ -316,6 +510,9 @@ public partial class CampusStudent : CharacterBody2D
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除行为目标
|
||||
/// </summary>
|
||||
public void ClearBehaviorTarget()
|
||||
{
|
||||
_behaviorHasTarget = false;
|
||||
@ -329,6 +526,10 @@ public partial class CampusStudent : CharacterBody2D
|
||||
_gridPath.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否已到达行为目标
|
||||
/// </summary>
|
||||
/// <returns>到达返回true</returns>
|
||||
public bool HasReachedBehaviorTarget()
|
||||
{
|
||||
if (!_behaviorHasTarget) return true;
|
||||
@ -344,6 +545,9 @@ public partial class CampusStudent : CharacterBody2D
|
||||
return _navigationAgent.IsNavigationFinished();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 开始玩手机
|
||||
/// </summary>
|
||||
public void StartPhoneIdle()
|
||||
{
|
||||
if (_animationPlayer == null || !_animationPlayer.HasAnimation("phone_up")) return;
|
||||
@ -354,6 +558,10 @@ public partial class CampusStudent : CharacterBody2D
|
||||
_animationPlayer.Play("phone_up");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 停止玩手机
|
||||
/// </summary>
|
||||
/// <param name="lockMovement">是否锁定移动</param>
|
||||
public void StopPhoneIdle(bool lockMovement = false)
|
||||
{
|
||||
if (_animationPlayer == null)
|
||||
@ -386,6 +594,10 @@ public partial class CampusStudent : CharacterBody2D
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置导航地图
|
||||
/// </summary>
|
||||
/// <param name="map">地图RID</param>
|
||||
public void SetNavigationMap(Rid map)
|
||||
{
|
||||
// 由校园控制器传入导航地图,供本地边界夹紧使用
|
||||
@ -396,6 +608,9 @@ public partial class CampusStudent : CharacterBody2D
|
||||
_navigationRegion = FindNavigationRegion();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 应用随机主题
|
||||
/// </summary>
|
||||
public void ApplyRandomTheme()
|
||||
{
|
||||
// 随机替换身体与配件贴图,形成不同主题外观
|
||||
@ -410,6 +625,9 @@ public partial class CampusStudent : CharacterBody2D
|
||||
_smartphone.Texture = ResourceLoader.Load<Texture2D>(Res.GetRandom(Res.Type.Phone, Use16X16Sprites));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 缓存精灵节点
|
||||
/// </summary>
|
||||
private void CacheSprites()
|
||||
{
|
||||
// 缓存子节点引用,避免每帧查找
|
||||
@ -421,6 +639,9 @@ public partial class CampusStudent : CharacterBody2D
|
||||
_smartphone = GetNode<Sprite2D>("parts/smartphone");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 配置碰撞
|
||||
/// </summary>
|
||||
private void ConfigureCollision()
|
||||
{
|
||||
// 学生只与环境碰撞,不与其它学生碰撞
|
||||
@ -429,6 +650,9 @@ public partial class CampusStudent : CharacterBody2D
|
||||
_usePhysicsMovement = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 推进到下一个目标
|
||||
/// </summary>
|
||||
private void AdvanceTarget()
|
||||
{
|
||||
if (_patrolPoints.Count == 0 || _navigationAgent == null) return;
|
||||
@ -455,6 +679,10 @@ public partial class CampusStudent : CharacterBody2D
|
||||
_stuckTimer = 0.0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算避让速度时的回调
|
||||
/// </summary>
|
||||
/// <param name="safeVelocity">安全速度</param>
|
||||
private void OnVelocityComputed(Vector2 safeVelocity)
|
||||
{
|
||||
if (_phoneExitLocked)
|
||||
@ -473,6 +701,10 @@ public partial class CampusStudent : CharacterBody2D
|
||||
UpdateStuckTimer(_lastDelta);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新卡住计时器
|
||||
/// </summary>
|
||||
/// <param name="delta">时间间隔</param>
|
||||
private void UpdateStuckTimer(double delta)
|
||||
{
|
||||
if (StuckRepathSeconds <= 0.0f || !_hasTarget)
|
||||
@ -496,6 +728,9 @@ public partial class CampusStudent : CharacterBody2D
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重新规划路径到当前目标
|
||||
/// </summary>
|
||||
private void RepathToCurrentTarget()
|
||||
{
|
||||
if (!_hasTarget) return;
|
||||
@ -516,6 +751,9 @@ public partial class CampusStudent : CharacterBody2D
|
||||
_navigationAgent.TargetPosition = _currentTarget;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试构建挂起的网格路径
|
||||
/// </summary>
|
||||
private void TryBuildPendingGridPath()
|
||||
{
|
||||
if (!UseGridPathfinding || !_gridPathPending) return;
|
||||
@ -540,11 +778,20 @@ public partial class CampusStudent : CharacterBody2D
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 导航地图是否准备就绪
|
||||
/// </summary>
|
||||
/// <returns>准备就绪返回true</returns>
|
||||
private bool IsNavigationMapReady()
|
||||
{
|
||||
return _navigationMap.IsValid && NavigationServer2D.MapGetIterationId(_navigationMap) > 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将速度限制在导航网格内
|
||||
/// </summary>
|
||||
/// <param name="velocity">目标速度</param>
|
||||
/// <returns>限制后的速度</returns>
|
||||
private Vector2 ClampVelocityToNavMesh(Vector2 velocity)
|
||||
{
|
||||
if (!IsNavigationMapReady()) return velocity;
|
||||
@ -566,6 +813,11 @@ public partial class CampusStudent : CharacterBody2D
|
||||
return velocity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将目标点限制在导航网格内
|
||||
/// </summary>
|
||||
/// <param name="target">目标点</param>
|
||||
/// <returns>限制后的目标点</returns>
|
||||
private Vector2 ClampTargetToNavMesh(Vector2 target)
|
||||
{
|
||||
if (!IsNavigationMapReady()) return target;
|
||||
@ -579,6 +831,10 @@ public partial class CampusStudent : CharacterBody2D
|
||||
return closest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 应用移动
|
||||
/// </summary>
|
||||
/// <param name="delta">时间间隔</param>
|
||||
private void ApplyMovement(double delta)
|
||||
{
|
||||
if (_usePhysicsMovement)
|
||||
@ -591,6 +847,10 @@ public partial class CampusStudent : CharacterBody2D
|
||||
GlobalPosition += Velocity * (float)delta;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新面朝向动画
|
||||
/// </summary>
|
||||
/// <param name="velocity">当前速度</param>
|
||||
private void UpdateFacingAnimation(Vector2 velocity)
|
||||
{
|
||||
if (_phoneIdleActive || _phoneExitLocked)
|
||||
@ -626,6 +886,9 @@ public partial class CampusStudent : CharacterBody2D
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 播放闲置动画
|
||||
/// </summary>
|
||||
private void PlayIdleAnimation()
|
||||
{
|
||||
if (_phoneIdleActive || _phoneExitLocked)
|
||||
@ -650,6 +913,10 @@ public partial class CampusStudent : CharacterBody2D
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 播放指定动画
|
||||
/// </summary>
|
||||
/// <param name="animationName">动画名称</param>
|
||||
private void PlayAnimation(string animationName)
|
||||
{
|
||||
if (_animationPlayer == null) return;
|
||||
@ -657,6 +924,10 @@ public partial class CampusStudent : CharacterBody2D
|
||||
if (_animationPlayer.CurrentAnimation != animationName) _animationPlayer.Play(animationName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 动画结束回调
|
||||
/// </summary>
|
||||
/// <param name="animationName">动画名称</param>
|
||||
private void OnAnimationFinished(StringName animationName)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (_gridPathIndex >= _gridPath.Count)
|
||||
@ -730,6 +1006,11 @@ public partial class CampusStudent : CharacterBody2D
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 转换为轴向速度
|
||||
/// </summary>
|
||||
/// <param name="delta">位移</param>
|
||||
/// <returns>轴向速度</returns>
|
||||
private Vector2 ToAxisVelocity(Vector2 delta)
|
||||
{
|
||||
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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构建网格路径
|
||||
/// </summary>
|
||||
/// <param name="start">起点</param>
|
||||
/// <param name="target">终点</param>
|
||||
/// <returns>路径点列表</returns>
|
||||
private List<Vector2> BuildGridPath(Vector2 start, Vector2 target)
|
||||
{
|
||||
if (!IsNavigationMapReady() || GridCellSize <= 0.0f)
|
||||
@ -809,6 +1096,10 @@ public partial class CampusStudent : CharacterBody2D
|
||||
return SimplifyGridPath(result);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除直接回头的路径点
|
||||
/// </summary>
|
||||
/// <param name="path">路径</param>
|
||||
private void RemoveImmediateBacktracks(List<Vector2> path)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (path == null || path.Count < 3) return path;
|
||||
@ -863,6 +1159,9 @@ public partial class CampusStudent : CharacterBody2D
|
||||
return simplified;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 确保AStar网格已建立
|
||||
/// </summary>
|
||||
private void EnsureAStarGrid()
|
||||
{
|
||||
if (!IsNavigationMapReady()) return;
|
||||
@ -916,6 +1215,10 @@ public partial class CampusStudent : CharacterBody2D
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构建网格区域
|
||||
/// </summary>
|
||||
/// <returns>网格区域</returns>
|
||||
private Rect2I BuildGridRegion()
|
||||
{
|
||||
var bounds = BuildWorldBounds();
|
||||
@ -929,6 +1232,10 @@ public partial class CampusStudent : CharacterBody2D
|
||||
return new Rect2I(new Vector2I(minX, minY), new Vector2I(sizeX, sizeY));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构建世界边界
|
||||
/// </summary>
|
||||
/// <returns>世界边界</returns>
|
||||
private Rect2 BuildWorldBounds()
|
||||
{
|
||||
var viewport = GetViewportRect();
|
||||
@ -979,6 +1286,11 @@ public partial class CampusStudent : CharacterBody2D
|
||||
return bounds.Merge(navBounds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 单元格是否在区域内
|
||||
/// </summary>
|
||||
/// <param name="cell">单元格</param>
|
||||
/// <returns>如果在区域内返回true</returns>
|
||||
private bool IsCellInRegion(Vector2I cell)
|
||||
{
|
||||
return cell.X >= _astarRegion.Position.X
|
||||
@ -987,6 +1299,11 @@ public partial class CampusStudent : CharacterBody2D
|
||||
&& cell.Y < _astarRegion.Position.Y + _astarRegion.Size.Y;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 单元格是否在边界内
|
||||
/// </summary>
|
||||
/// <param name="cell">单元格</param>
|
||||
/// <returns>如果在边界内返回true</returns>
|
||||
private bool IsCellInBounds(Vector2I cell)
|
||||
{
|
||||
if (_astarGrid != null)
|
||||
@ -997,6 +1314,11 @@ public partial class CampusStudent : CharacterBody2D
|
||||
return IsCellInRegion(cell);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 世界坐标转网格坐标
|
||||
/// </summary>
|
||||
/// <param name="world">世界坐标</param>
|
||||
/// <returns>网格坐标</returns>
|
||||
private Vector2I WorldToGrid(Vector2 world)
|
||||
{
|
||||
var size = Mathf.Max(1.0f, GridCellSize);
|
||||
@ -1005,12 +1327,23 @@ public partial class CampusStudent : CharacterBody2D
|
||||
Mathf.FloorToInt(world.Y / size));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 网格坐标转世界坐标
|
||||
/// </summary>
|
||||
/// <param name="grid">网格坐标</param>
|
||||
/// <returns>世界坐标</returns>
|
||||
private Vector2 GridToWorld(Vector2I grid)
|
||||
{
|
||||
var size = Mathf.Max(1.0f, GridCellSize);
|
||||
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)
|
||||
{
|
||||
if (_astarGrid == null) return origin;
|
||||
@ -1039,6 +1372,11 @@ public partial class CampusStudent : CharacterBody2D
|
||||
return origin;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 世界坐标是否可行走
|
||||
/// </summary>
|
||||
/// <param name="world">世界坐标</param>
|
||||
/// <returns>如果可行走返回true</returns>
|
||||
private bool IsWorldWalkable(Vector2 world)
|
||||
{
|
||||
if (!IsNavigationMapReady()) return false;
|
||||
@ -1046,11 +1384,20 @@ public partial class CampusStudent : CharacterBody2D
|
||||
return closest.DistanceTo(world) <= GetGridTolerance();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查找导航区域节点
|
||||
/// </summary>
|
||||
/// <returns>导航区域</returns>
|
||||
private NavigationRegion2D FindNavigationRegion()
|
||||
{
|
||||
return GetTree()?.CurrentScene?.FindChild("NavigationRegion2D", true, false) as NavigationRegion2D;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 单元格中心是否可行走
|
||||
/// </summary>
|
||||
/// <param name="center">中心点</param>
|
||||
/// <returns>如果可行走返回true</returns>
|
||||
private bool IsCellWalkable(Vector2 center)
|
||||
{
|
||||
if (!IsWorldWalkable(center)) return false;
|
||||
@ -1079,16 +1426,12 @@ public partial class CampusStudent : CharacterBody2D
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取网格容差
|
||||
/// </summary>
|
||||
/// <returns>网格容差</returns>
|
||||
private float GetGridTolerance()
|
||||
{
|
||||
return Mathf.Max(1.0f, GridWalkableTolerance);
|
||||
}
|
||||
|
||||
private enum FacingDirection
|
||||
{
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -10,8 +10,15 @@ namespace Core;
|
||||
[GlobalClass]
|
||||
public partial class ContentCollectionResource : Resource, IContentResourceCollection
|
||||
{
|
||||
/// <summary>
|
||||
/// 资源列表
|
||||
/// </summary>
|
||||
[Export] public Array<Resource> Items { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 获取资源项
|
||||
/// </summary>
|
||||
/// <returns>资源项集合</returns>
|
||||
public IEnumerable<IContentResource> GetItems()
|
||||
{
|
||||
foreach (var item in Items)
|
||||
@ -22,5 +29,4 @@ public partial class ContentCollectionResource : Resource, IContentResourceColle
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -20,27 +20,52 @@ namespace Core;
|
||||
/// </summary>
|
||||
public interface IContentSource
|
||||
{
|
||||
/// <summary>
|
||||
/// 优先级
|
||||
/// </summary>
|
||||
int Priority { get; }
|
||||
/// <summary>
|
||||
/// 加载所有指定类型的对象
|
||||
/// </summary>
|
||||
/// <typeparam name="T">对象类型</typeparam>
|
||||
/// <returns>对象集合</returns>
|
||||
IEnumerable<T> LoadAll<T>() where T : class;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 内容合并模式
|
||||
/// </summary>
|
||||
public enum ContentMergeMode
|
||||
{
|
||||
Override,
|
||||
KeepFirst
|
||||
Override, // 覆盖
|
||||
KeepFirst // 保留首次
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 内容注册表
|
||||
/// </summary>
|
||||
public sealed class ContentRegistry
|
||||
{
|
||||
private readonly List<IContentSource> _sources = new();
|
||||
/// <summary>
|
||||
/// 合并模式
|
||||
/// </summary>
|
||||
public ContentMergeMode MergeMode { get; set; } = ContentMergeMode.Override;
|
||||
|
||||
/// <summary>
|
||||
/// 注册内容源
|
||||
/// </summary>
|
||||
/// <param name="source">内容源</param>
|
||||
public void RegisterSource(IContentSource source)
|
||||
{
|
||||
_sources.Add(source);
|
||||
_sources.Sort((a, b) => a.Priority.CompareTo(b.Priority));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构建游戏内容数据库
|
||||
/// </summary>
|
||||
/// <returns>游戏内容数据库</returns>
|
||||
public GameContentDatabase BuildDatabase()
|
||||
{
|
||||
var db = new GameContentDatabase();
|
||||
@ -55,6 +80,11 @@ public sealed class ContentRegistry
|
||||
return db;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从所有源加载指定类型的所有对象
|
||||
/// </summary>
|
||||
/// <typeparam name="T">对象类型</typeparam>
|
||||
/// <returns>对象集合</returns>
|
||||
private IEnumerable<T> LoadAll<T>() where T : class
|
||||
{
|
||||
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
|
||||
{
|
||||
foreach (var item in items)
|
||||
@ -97,14 +134,29 @@ public sealed class ContentRegistry
|
||||
/// </summary>
|
||||
public sealed class ResourceContentSource : IContentSource
|
||||
{
|
||||
/// <summary>
|
||||
/// 优先级
|
||||
/// </summary>
|
||||
public int Priority { get; }
|
||||
/// <summary>
|
||||
/// 资源路径列表
|
||||
/// </summary>
|
||||
public List<string> ResourcePaths { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="priority">优先级</param>
|
||||
public ResourceContentSource(int priority)
|
||||
{
|
||||
Priority = priority;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载所有指定类型的对象
|
||||
/// </summary>
|
||||
/// <typeparam name="T">对象类型</typeparam>
|
||||
/// <returns>对象集合</returns>
|
||||
public IEnumerable<T> LoadAll<T>() where T : class
|
||||
{
|
||||
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
|
||||
{
|
||||
if (resource is IContentResource content)
|
||||
@ -163,10 +221,20 @@ public sealed class ResourceContentSource : IContentSource
|
||||
/// </summary>
|
||||
public sealed class JsonContentSource : IContentSource
|
||||
{
|
||||
/// <summary>
|
||||
/// 优先级
|
||||
/// </summary>
|
||||
public int Priority { get; }
|
||||
/// <summary>
|
||||
/// 数据路径列表
|
||||
/// </summary>
|
||||
public List<string> DataPaths { get; } = new();
|
||||
private readonly JsonSerializerOptions _options;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="priority">优先级</param>
|
||||
public JsonContentSource(int priority)
|
||||
{
|
||||
Priority = priority;
|
||||
@ -177,6 +245,11 @@ public sealed class JsonContentSource : IContentSource
|
||||
_options.Converters.Add(new JsonStringEnumConverter());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载所有指定类型的对象
|
||||
/// </summary>
|
||||
/// <typeparam name="T">对象类型</typeparam>
|
||||
/// <returns>对象集合</returns>
|
||||
public IEnumerable<T> LoadAll<T>() where T : class
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (path.StartsWith("res://") || path.StartsWith("user://"))
|
||||
@ -242,6 +320,9 @@ public sealed class JsonContentSource : IContentSource
|
||||
return path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试反序列化列表
|
||||
/// </summary>
|
||||
private bool TryDeserializeList<T>(string json, out List<T> list) where T : class
|
||||
{
|
||||
try
|
||||
@ -256,6 +337,9 @@ public sealed class JsonContentSource : IContentSource
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试反序列化单个对象
|
||||
/// </summary>
|
||||
private bool TryDeserializeSingle<T>(string json, out T item) where T : class
|
||||
{
|
||||
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
|
||||
{
|
||||
try
|
||||
@ -295,5 +382,4 @@ public sealed class JsonContentSource : IContentSource
|
||||
public List<T> Items { get; set; }
|
||||
public T Item { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -15,13 +15,26 @@ namespace Core;
|
||||
/// </summary>
|
||||
public interface IContentResource
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取定义类型
|
||||
/// </summary>
|
||||
/// <returns>类型</returns>
|
||||
Type GetDefinitionType();
|
||||
/// <summary>
|
||||
/// 转换为定义对象
|
||||
/// </summary>
|
||||
/// <returns>定义对象</returns>
|
||||
object ToDefinition();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 资源集合接口
|
||||
/// </summary>
|
||||
public interface IContentResourceCollection
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取资源项集合
|
||||
/// </summary>
|
||||
/// <returns>资源项集合</returns>
|
||||
IEnumerable<IContentResource> GetItems();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -20,28 +20,81 @@ namespace Core;
|
||||
public partial class DisciplineDefinitionResource : Resource, IContentResource
|
||||
{
|
||||
// --- Header ---
|
||||
/// <summary>
|
||||
/// 学科ID
|
||||
/// </summary>
|
||||
[Export] public string Id { get; set; }
|
||||
/// <summary>
|
||||
/// 名称键值
|
||||
/// </summary>
|
||||
[Export] public string NameKey { get; set; }
|
||||
/// <summary>
|
||||
/// 名称默认值
|
||||
/// </summary>
|
||||
[Export] public string NameFallback { get; set; }
|
||||
/// <summary>
|
||||
/// 描述键值
|
||||
/// </summary>
|
||||
[Export] public string DescriptionKey { get; set; }
|
||||
/// <summary>
|
||||
/// 描述默认值
|
||||
/// </summary>
|
||||
[Export] public string DescriptionFallback { get; set; }
|
||||
/// <summary>
|
||||
/// 图标路径
|
||||
/// </summary>
|
||||
[Export] public string IconPath { get; set; }
|
||||
/// <summary>
|
||||
/// 标签列表
|
||||
/// </summary>
|
||||
[Export] public Array<string> Tags { get; set; } = new();
|
||||
|
||||
// --- Buff ---
|
||||
/// <summary>
|
||||
/// Buff名称键值
|
||||
/// </summary>
|
||||
[Export] public string BuffNameKey { get; set; }
|
||||
/// <summary>
|
||||
/// Buff名称默认值
|
||||
/// </summary>
|
||||
[Export] public string BuffNameFallback { get; set; }
|
||||
/// <summary>
|
||||
/// Buff描述键值
|
||||
/// </summary>
|
||||
[Export] public string BuffDescriptionKey { get; set; }
|
||||
/// <summary>
|
||||
/// Buff描述默认值
|
||||
/// </summary>
|
||||
[Export] public string BuffDescriptionFallback { get; set; }
|
||||
/// <summary>
|
||||
/// Buff规则ID列表
|
||||
/// </summary>
|
||||
[Export] public Array<string> BuffRuleIds { get; set; } = new();
|
||||
|
||||
// --- Pools ---
|
||||
/// <summary>
|
||||
/// 角色池ID列表
|
||||
/// </summary>
|
||||
[Export] public Array<string> RolePoolIds { get; set; } = new();
|
||||
/// <summary>
|
||||
/// 物品池ID列表
|
||||
/// </summary>
|
||||
[Export] public Array<string> ItemPoolIds { get; set; } = new();
|
||||
/// <summary>
|
||||
/// 任务关键词ID列表
|
||||
/// </summary>
|
||||
[Export] public Array<string> TaskKeywordIds { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 获取定义类型
|
||||
/// </summary>
|
||||
/// <returns>类型</returns>
|
||||
public Type GetDefinitionType() => typeof(DisciplineDefinition);
|
||||
|
||||
/// <summary>
|
||||
/// 转换为定义对象
|
||||
/// </summary>
|
||||
/// <returns>定义对象</returns>
|
||||
public object ToDefinition()
|
||||
{
|
||||
var header = new DefinitionHeader
|
||||
@ -98,6 +151,11 @@ public partial class DisciplineDefinitionResource : Resource, IContentResource
|
||||
return definition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加范围
|
||||
/// </summary>
|
||||
/// <param name="source">源数组</param>
|
||||
/// <param name="target">目标列表</param>
|
||||
private static void AddRange(Array<string> source, List<string> target)
|
||||
{
|
||||
foreach (var value in source)
|
||||
@ -105,5 +163,4 @@ public partial class DisciplineDefinitionResource : Resource, IContentResource
|
||||
target.Add(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -14,31 +14,57 @@ namespace Core;
|
||||
/// </summary>
|
||||
public readonly struct TaskCompletedEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// 完成的任务
|
||||
/// </summary>
|
||||
public TaskModel Task { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="task">任务</param>
|
||||
public TaskCompletedEvent(TaskModel task)
|
||||
{
|
||||
Task = task;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 任务失败事件
|
||||
/// </summary>
|
||||
public readonly struct TaskFailedEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// 失败的任务
|
||||
/// </summary>
|
||||
public TaskModel Task { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="task">任务</param>
|
||||
public TaskFailedEvent(TaskModel task)
|
||||
{
|
||||
Task = task;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 回合结束事件
|
||||
/// </summary>
|
||||
public readonly struct TurnEndedEvent
|
||||
{
|
||||
/// <summary>
|
||||
/// 结束的回合数
|
||||
/// </summary>
|
||||
public int Turn { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="turn">回合数</param>
|
||||
public TurnEndedEvent(int turn)
|
||||
{
|
||||
Turn = turn;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -17,6 +17,11 @@ public sealed class DomainEventBus
|
||||
{
|
||||
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)
|
||||
{
|
||||
var type = typeof(T);
|
||||
@ -29,6 +34,11 @@ public sealed class DomainEventBus
|
||||
list.Add(handler);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 取消订阅事件
|
||||
/// </summary>
|
||||
/// <typeparam name="T">事件类型</typeparam>
|
||||
/// <param name="handler">事件处理器</param>
|
||||
public void Unsubscribe<T>(Action<T> handler)
|
||||
{
|
||||
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)
|
||||
{
|
||||
var type = typeof(T);
|
||||
@ -54,5 +69,4 @@ public sealed class DomainEventBus
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -16,28 +16,40 @@ public sealed class GameController : IController
|
||||
{
|
||||
private GameSession _session;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化控制器
|
||||
/// </summary>
|
||||
/// <param name="session">游戏会话</param>
|
||||
public void Initialize(GameSession session)
|
||||
{
|
||||
_session = session;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 开始执行阶段
|
||||
/// </summary>
|
||||
public void StartExecution()
|
||||
{
|
||||
if (_session.State.Turn.Phase != GamePhase.Planning) return;
|
||||
_session.State.Turn.Phase = GamePhase.Execution;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 结束执行阶段
|
||||
/// </summary>
|
||||
public void EndExecution()
|
||||
{
|
||||
if (_session.State.Turn.Phase != GamePhase.Execution) return;
|
||||
_session.State.Turn.Phase = GamePhase.Review;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 开始下一回合
|
||||
/// </summary>
|
||||
public void StartNextTurn()
|
||||
{
|
||||
if (_session.State.Turn.Phase != GamePhase.Review) return;
|
||||
_session.State.Turn.CurrentTurn++;
|
||||
_session.State.Turn.Phase = GamePhase.Planning;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -14,12 +14,34 @@ namespace Core;
|
||||
/// </summary>
|
||||
public sealed class GameSession
|
||||
{
|
||||
/// <summary>
|
||||
/// 游戏状态
|
||||
/// </summary>
|
||||
public GameState State { get; }
|
||||
/// <summary>
|
||||
/// 游戏内容数据库
|
||||
/// </summary>
|
||||
public GameContentDatabase Content { get; }
|
||||
/// <summary>
|
||||
/// 领域事件总线
|
||||
/// </summary>
|
||||
public DomainEventBus Events { get; }
|
||||
/// <summary>
|
||||
/// 本地化服务
|
||||
/// </summary>
|
||||
public ILocalizationService Localization { get; }
|
||||
/// <summary>
|
||||
/// 游戏系统集合
|
||||
/// </summary>
|
||||
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)
|
||||
{
|
||||
State = state;
|
||||
@ -30,6 +52,10 @@ public sealed class GameSession
|
||||
Systems.Initialize(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建默认游戏会话
|
||||
/// </summary>
|
||||
/// <returns>游戏会话实例</returns>
|
||||
public static GameSession CreateDefault()
|
||||
{
|
||||
var registry = new ContentRegistry();
|
||||
@ -50,9 +76,12 @@ public sealed class GameSession
|
||||
return new GameSession(new GameState(), content, localization, events);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 每帧更新
|
||||
/// </summary>
|
||||
/// <param name="delta">帧间隔</param>
|
||||
public void Tick(float delta)
|
||||
{
|
||||
Systems.Tick(delta);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -16,18 +16,48 @@ namespace Core;
|
||||
/// </summary>
|
||||
public interface IGameSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化系统
|
||||
/// </summary>
|
||||
/// <param name="session">游戏会话</param>
|
||||
void Initialize(GameSession session);
|
||||
/// <summary>
|
||||
/// 每帧更新
|
||||
/// </summary>
|
||||
/// <param name="delta">帧间隔</param>
|
||||
void Tick(float delta);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 游戏系统管理器
|
||||
/// </summary>
|
||||
public sealed class GameSystems
|
||||
{
|
||||
/// <summary>
|
||||
/// 回合系统
|
||||
/// </summary>
|
||||
public TurnSystem Turn { get; } = new();
|
||||
/// <summary>
|
||||
/// 任务系统
|
||||
/// </summary>
|
||||
public TaskSystem Task { get; } = new();
|
||||
/// <summary>
|
||||
/// 经济系统
|
||||
/// </summary>
|
||||
public EconomySystem Economy { get; } = new();
|
||||
/// <summary>
|
||||
/// 羁绊/协同系统
|
||||
/// </summary>
|
||||
public SynergySystem Synergy { get; } = new();
|
||||
/// <summary>
|
||||
/// 分配系统
|
||||
/// </summary>
|
||||
public AssignmentSystem Assignment { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 初始化所有系统
|
||||
/// </summary>
|
||||
/// <param name="session">游戏会话</param>
|
||||
public void Initialize(GameSession session)
|
||||
{
|
||||
Turn.Initialize(session);
|
||||
@ -37,6 +67,10 @@ public sealed class GameSystems
|
||||
Assignment.Initialize(session);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新所有系统
|
||||
/// </summary>
|
||||
/// <param name="delta">帧间隔</param>
|
||||
public void Tick(float delta)
|
||||
{
|
||||
Turn.Tick(delta);
|
||||
@ -47,32 +81,54 @@ public sealed class GameSystems
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 回合系统
|
||||
/// </summary>
|
||||
public sealed class TurnSystem : IGameSystem
|
||||
{
|
||||
private GameSession _session;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化
|
||||
/// </summary>
|
||||
/// <param name="session">游戏会话</param>
|
||||
public void Initialize(GameSession session)
|
||||
{
|
||||
_session = session;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新
|
||||
/// </summary>
|
||||
/// <param name="delta">帧间隔</param>
|
||||
public void Tick(float delta)
|
||||
{
|
||||
// 预留:回合推进计时器/阶段切换
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 任务系统
|
||||
/// </summary>
|
||||
public sealed class TaskSystem : IGameSystem
|
||||
{
|
||||
private GameSession _session;
|
||||
private StatResolver _statResolver;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化
|
||||
/// </summary>
|
||||
/// <param name="session">游戏会话</param>
|
||||
public void Initialize(GameSession session)
|
||||
{
|
||||
_session = session;
|
||||
_statResolver = new StatResolver(session);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新
|
||||
/// </summary>
|
||||
/// <param name="delta">帧间隔</param>
|
||||
public void Tick(float delta)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
var unit = entry.Unit;
|
||||
@ -173,6 +236,9 @@ public sealed class TaskSystem : IGameSystem
|
||||
return effectivePower;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取任务基础效率
|
||||
/// </summary>
|
||||
private float GetTaskBasePower(UnitModel unit, TaskKind kind)
|
||||
{
|
||||
var academic = _statResolver.GetAttribute(unit, AttributeType.Academic);
|
||||
@ -194,6 +260,9 @@ public sealed class TaskSystem : IGameSystem
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取状态乘数
|
||||
/// </summary>
|
||||
private float GetStatusMultiplier(UnitEntry entry)
|
||||
{
|
||||
var mood = entry.Unit.Statuses.Mood.Normalized;
|
||||
@ -213,6 +282,9 @@ public sealed class TaskSystem : IGameSystem
|
||||
return Math.Clamp(multiplier, 0.3f, 1.2f);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取角色乘数
|
||||
/// </summary>
|
||||
private float GetRoleMultiplier(UnitModel unit, TaskDefinition taskDef)
|
||||
{
|
||||
if (taskDef == null) return 1.0f;
|
||||
@ -246,6 +318,9 @@ public sealed class TaskSystem : IGameSystem
|
||||
return 1.0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取需求乘数
|
||||
/// </summary>
|
||||
private float GetRequirementMultiplier(UnitModel unit, TaskDefinition taskDef)
|
||||
{
|
||||
if (taskDef == null) return 1.0f;
|
||||
@ -263,6 +338,9 @@ public sealed class TaskSystem : IGameSystem
|
||||
return multiplier;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取学科乘数
|
||||
/// </summary>
|
||||
private float GetDisciplineMultiplier(UnitModel unit, TaskDefinition taskDef)
|
||||
{
|
||||
if (taskDef == null) return 1.0f;
|
||||
@ -280,6 +358,9 @@ public sealed class TaskSystem : IGameSystem
|
||||
return 0.7f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取难度系数
|
||||
/// </summary>
|
||||
private float GetDifficultyScale(TaskDifficulty difficulty)
|
||||
{
|
||||
return difficulty switch
|
||||
@ -292,12 +373,18 @@ public sealed class TaskSystem : IGameSystem
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取任务定义
|
||||
/// </summary>
|
||||
private TaskDefinition GetTaskDefinition(TaskModel task)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(task.DefinitionId)) return null;
|
||||
return _session.Content.Tasks.TryGetValue(task.DefinitionId, out var definition) ? definition : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构建单位索引
|
||||
/// </summary>
|
||||
private Dictionary<Guid, UnitEntry> BuildUnitIndex()
|
||||
{
|
||||
var index = new Dictionary<Guid, UnitEntry>();
|
||||
@ -324,6 +411,9 @@ public sealed class TaskSystem : IGameSystem
|
||||
return index;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 追踪贡献
|
||||
/// </summary>
|
||||
private void TrackContribution(UnitEntry entry, TaskModel task, float deltaContribution)
|
||||
{
|
||||
if (entry.Student == null) return;
|
||||
@ -355,6 +445,9 @@ public sealed class TaskSystem : IGameSystem
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 经济系统
|
||||
/// </summary>
|
||||
public sealed class EconomySystem : IGameSystem
|
||||
{
|
||||
private GameSession _session;
|
||||
@ -363,6 +456,10 @@ public sealed class EconomySystem : IGameSystem
|
||||
private const int PostDocSalary = 1200;
|
||||
private const int JuniorFacultySalary = 2000;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化
|
||||
/// </summary>
|
||||
/// <param name="session">游戏会话</param>
|
||||
public void Initialize(GameSession session)
|
||||
{
|
||||
_session = session;
|
||||
@ -371,11 +468,18 @@ public sealed class EconomySystem : IGameSystem
|
||||
_session.Events.Subscribe<TurnEndedEvent>(OnTurnEnded);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新
|
||||
/// </summary>
|
||||
/// <param name="delta">帧间隔</param>
|
||||
public void Tick(float delta)
|
||||
{
|
||||
// 当前为回合驱动,不在 Tick 中结算
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 任务完成回调
|
||||
/// </summary>
|
||||
private void OnTaskCompleted(TaskCompletedEvent evt)
|
||||
{
|
||||
var task = evt.Task;
|
||||
@ -404,6 +508,9 @@ public sealed class EconomySystem : IGameSystem
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 任务失败回调
|
||||
/// </summary>
|
||||
private void OnTaskFailed(TaskFailedEvent evt)
|
||||
{
|
||||
var task = evt.Task;
|
||||
@ -412,12 +519,18 @@ public sealed class EconomySystem : IGameSystem
|
||||
_session.State.Economy.Reputation -= penalty;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 回合结束回调
|
||||
/// </summary>
|
||||
private void OnTurnEnded(TurnEndedEvent evt)
|
||||
{
|
||||
ApplySalaries();
|
||||
ApplyInterest();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 支付薪水
|
||||
/// </summary>
|
||||
private void ApplySalaries()
|
||||
{
|
||||
var economy = _session.State.Economy;
|
||||
@ -436,6 +549,9 @@ public sealed class EconomySystem : IGameSystem
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算利息
|
||||
/// </summary>
|
||||
private void ApplyInterest()
|
||||
{
|
||||
var economy = _session.State.Economy;
|
||||
@ -446,6 +562,9 @@ public sealed class EconomySystem : IGameSystem
|
||||
economy.Money += interest;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新利率
|
||||
/// </summary>
|
||||
private void UpdateInterestRate()
|
||||
{
|
||||
var economy = _session.State.Economy;
|
||||
@ -456,6 +575,9 @@ public sealed class EconomySystem : IGameSystem
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加论文
|
||||
/// </summary>
|
||||
private void AddPaper(PaperRank rank)
|
||||
{
|
||||
var inventory = _session.State.Inventory;
|
||||
@ -467,6 +589,9 @@ public sealed class EconomySystem : IGameSystem
|
||||
inventory.PaperCounts[rank] += 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加物品
|
||||
/// </summary>
|
||||
private void AddItem(string itemId, int count)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(itemId)) return;
|
||||
@ -479,6 +604,9 @@ public sealed class EconomySystem : IGameSystem
|
||||
inventory.ItemCounts[itemId] += count;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据难度获取论文等级
|
||||
/// </summary>
|
||||
private PaperRank GetPaperRankByDifficulty(TaskDifficulty difficulty)
|
||||
{
|
||||
return difficulty switch
|
||||
@ -492,20 +620,34 @@ public sealed class EconomySystem : IGameSystem
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 羁绊/协同系统
|
||||
/// </summary>
|
||||
public sealed class SynergySystem : IGameSystem
|
||||
{
|
||||
private GameSession _session;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化
|
||||
/// </summary>
|
||||
/// <param name="session">游戏会话</param>
|
||||
public void Initialize(GameSession session)
|
||||
{
|
||||
_session = session;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新
|
||||
/// </summary>
|
||||
/// <param name="delta">帧间隔</param>
|
||||
public void Tick(float delta)
|
||||
{
|
||||
RecalculateSynergy();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重新计算协同
|
||||
/// </summary>
|
||||
private void RecalculateSynergy()
|
||||
{
|
||||
var state = _session.State.Synergy;
|
||||
@ -518,6 +660,9 @@ public sealed class SynergySystem : IGameSystem
|
||||
ApplySynergyDefinitions(state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 统计单位标签
|
||||
/// </summary>
|
||||
private void CountUnitTags(SynergyState synergy)
|
||||
{
|
||||
var roster = _session.State.Roster;
|
||||
@ -534,6 +679,9 @@ public sealed class SynergySystem : IGameSystem
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加单位标签
|
||||
/// </summary>
|
||||
private void AddUnitTags(SynergyState synergy, UnitModel unit)
|
||||
{
|
||||
if (unit == null) return;
|
||||
@ -548,6 +696,9 @@ public sealed class SynergySystem : IGameSystem
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 增加堆叠计数
|
||||
/// </summary>
|
||||
private void AddStack(Dictionary<string, int> stacks, string id)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(id)) return;
|
||||
@ -559,6 +710,9 @@ public sealed class SynergySystem : IGameSystem
|
||||
stacks[id] += 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 应用协同定义
|
||||
/// </summary>
|
||||
private void ApplySynergyDefinitions(SynergyState synergy)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(id)) return;
|
||||
@ -586,6 +743,9 @@ public sealed class SynergySystem : IGameSystem
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 合并修正
|
||||
/// </summary>
|
||||
private void MergeModifiers(ModifierBundle target, ModifierBundle source)
|
||||
{
|
||||
if (target == null || source == null) return;
|
||||
@ -595,6 +755,9 @@ public sealed class SynergySystem : IGameSystem
|
||||
target.RuleIds.AddRange(source.RuleIds);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除修正
|
||||
/// </summary>
|
||||
private void ClearModifiers(ModifierBundle bundle)
|
||||
{
|
||||
bundle.AttributeModifiers.Clear();
|
||||
@ -604,18 +767,28 @@ public sealed class SynergySystem : IGameSystem
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 分配系统
|
||||
/// </summary>
|
||||
public sealed class AssignmentSystem : IGameSystem
|
||||
{
|
||||
private GameSession _session;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化
|
||||
/// </summary>
|
||||
/// <param name="session">游戏会话</param>
|
||||
public void Initialize(GameSession session)
|
||||
{
|
||||
_session = session;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新
|
||||
/// </summary>
|
||||
/// <param name="delta">帧间隔</param>
|
||||
public void Tick(float delta)
|
||||
{
|
||||
// 预留:人员分配、交接惩罚等
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -15,18 +15,43 @@ namespace Core;
|
||||
/// </summary>
|
||||
public interface ILocalizationService
|
||||
{
|
||||
/// <summary>
|
||||
/// 翻译本地化文本对象
|
||||
/// </summary>
|
||||
/// <param name="text">文本对象</param>
|
||||
/// <returns>翻译后的字符串</returns>
|
||||
string Translate(LocalizedText text);
|
||||
/// <summary>
|
||||
/// 翻译键值
|
||||
/// </summary>
|
||||
/// <param name="key">键值</param>
|
||||
/// <param name="fallback">默认值</param>
|
||||
/// <returns>翻译后的字符串</returns>
|
||||
string Translate(string key, string fallback = null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Godot 本地化服务实现
|
||||
/// </summary>
|
||||
public sealed class GodotLocalizationService : ILocalizationService
|
||||
{
|
||||
/// <summary>
|
||||
/// 翻译本地化文本对象
|
||||
/// </summary>
|
||||
/// <param name="text">文本对象</param>
|
||||
/// <returns>翻译后的字符串</returns>
|
||||
public string Translate(LocalizedText text)
|
||||
{
|
||||
if (text == null) return string.Empty;
|
||||
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)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(key))
|
||||
@ -42,5 +67,4 @@ public sealed class GodotLocalizationService : ILocalizationService
|
||||
|
||||
return translated;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -14,16 +14,39 @@ namespace Core;
|
||||
/// </summary>
|
||||
public sealed class ModManifest
|
||||
{
|
||||
/// <summary>
|
||||
/// Mod ID
|
||||
/// </summary>
|
||||
public string Id { get; set; }
|
||||
/// <summary>
|
||||
/// Mod 名称
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
/// <summary>
|
||||
/// 版本
|
||||
/// </summary>
|
||||
public string Version { get; set; }
|
||||
/// <summary>
|
||||
/// 依赖列表
|
||||
/// </summary>
|
||||
public List<string> Dependencies { get; } = new();
|
||||
/// <summary>
|
||||
/// 内容路径列表
|
||||
/// </summary>
|
||||
public List<string> ContentPaths { get; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mod 包
|
||||
/// </summary>
|
||||
public sealed class ModPackage
|
||||
{
|
||||
/// <summary>
|
||||
/// 清单
|
||||
/// </summary>
|
||||
public ModManifest Manifest { get; set; } = new();
|
||||
/// <summary>
|
||||
/// 根路径
|
||||
/// </summary>
|
||||
public string RootPath { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
@ -14,26 +14,50 @@ namespace Core;
|
||||
/// </summary>
|
||||
public interface IView<TModel>
|
||||
{
|
||||
/// <summary>
|
||||
/// 绑定数据模型
|
||||
/// </summary>
|
||||
/// <param name="model">数据模型</param>
|
||||
void Bind(TModel model);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 控制器接口
|
||||
/// </summary>
|
||||
public interface IController
|
||||
{
|
||||
/// <summary>
|
||||
/// 初始化控制器
|
||||
/// </summary>
|
||||
/// <param name="session">游戏会话</param>
|
||||
void Initialize(GameSession session);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 模型视图基类
|
||||
/// </summary>
|
||||
/// <typeparam name="TModel">模型类型</typeparam>
|
||||
public abstract partial class ModelView<TModel> : Node, IView<TModel>
|
||||
{
|
||||
/// <summary>
|
||||
/// 数据模型
|
||||
/// </summary>
|
||||
public TModel Model { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 绑定数据模型
|
||||
/// </summary>
|
||||
/// <param name="model">数据模型</param>
|
||||
public virtual void Bind(TModel model)
|
||||
{
|
||||
Model = model;
|
||||
OnModelBound();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当模型绑定时调用
|
||||
/// </summary>
|
||||
protected virtual void OnModelBound()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -17,11 +17,21 @@ public sealed class StatResolver
|
||||
{
|
||||
private readonly GameSession _session;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="session">游戏会话</param>
|
||||
public StatResolver(GameSession session)
|
||||
{
|
||||
_session = session;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取计算后的属性值
|
||||
/// </summary>
|
||||
/// <param name="unit">单位</param>
|
||||
/// <param name="type">属性类型</param>
|
||||
/// <returns>属性值</returns>
|
||||
public float GetAttribute(UnitModel unit, AttributeType type)
|
||||
{
|
||||
var value = GetBaseAttribute(unit, type);
|
||||
@ -32,6 +42,12 @@ public sealed class StatResolver
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取基础属性值
|
||||
/// </summary>
|
||||
/// <param name="unit">单位</param>
|
||||
/// <param name="type">属性类型</param>
|
||||
/// <returns>基础值</returns>
|
||||
private float GetBaseAttribute(UnitModel unit, AttributeType type)
|
||||
{
|
||||
return type switch
|
||||
@ -46,6 +62,9 @@ public sealed class StatResolver
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 应用学科加成
|
||||
/// </summary>
|
||||
private void ApplyDiscipline(string disciplineId, AttributeType type, ref float value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(disciplineId)) return;
|
||||
@ -53,6 +72,9 @@ public sealed class StatResolver
|
||||
ApplyBundle(discipline.Buff?.Modifiers, type, ref value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 应用特质加成
|
||||
/// </summary>
|
||||
private void ApplyTraits(List<string> traitIds, AttributeType type, ref float value)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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)
|
||||
{
|
||||
if (bundle == null) return;
|
||||
@ -86,5 +114,4 @@ public sealed class StatResolver
|
||||
value = (value + modifier.Add) * modifier.Multiplier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -4,14 +4,20 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
|
||||
/// <summary>
|
||||
/// 办公格子/图块对象
|
||||
/// </summary>
|
||||
public partial class Cube : StaticBody2D, ITileDraggable
|
||||
{
|
||||
// 定义各种家具的占用区域
|
||||
private static readonly Rect2I tableRect = new(0, 0, 3, 2);
|
||||
private static readonly Rect2I table2Rect = new(0, -1, 3, 1);
|
||||
private static readonly Rect2I chairRect = new(1, 1, 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 equip2Rect = new(0, -1, 3, 1);
|
||||
|
||||
// 定义桌子的主题映射
|
||||
private static readonly TileMapping[] tableThemes = {
|
||||
new(new Vector2I(1, 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(10, 28), tableRect),
|
||||
};
|
||||
// 定义第二种桌子的主题映射
|
||||
private static readonly TileMapping[] table2Themes = {
|
||||
new(new Vector2I(1, 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(10, 29), table2Rect),
|
||||
};
|
||||
// 定义椅子的主题映射
|
||||
private static readonly TileMapping[] chairThemes = {
|
||||
new(new Vector2I(-1, 7), chairRect),
|
||||
new(new Vector2I(0, 7), chairRect),
|
||||
new(new Vector2I(-1, 9), chairRect),
|
||||
new(new Vector2I(0, 9), chairRect),
|
||||
};
|
||||
// 定义第二种椅子的主题映射
|
||||
private static readonly TileMapping[] chair2Themes = {
|
||||
new(new Vector2I(-1, 10), chair2Rect),
|
||||
new(new Vector2I(0, 10), chair2Rect),
|
||||
@ -39,12 +48,13 @@ public partial class Cube : StaticBody2D, ITileDraggable
|
||||
new(new Vector2I(0, 12), chair2Rect),
|
||||
};
|
||||
|
||||
// 定义设备的主题映射
|
||||
private static readonly TileMapping[] equipThemes = {
|
||||
// one laptop
|
||||
// 一台笔记本电脑
|
||||
new(new Vector2I(13, 26), equipRect),
|
||||
// one laptop with one monitor
|
||||
// 一台笔记本电脑加一台显示器
|
||||
new(new Vector2I(7, 26), equipRect),
|
||||
// one desktop PC
|
||||
// 一台台式机
|
||||
new(new Dictionary<Vector2I, Vector2I>() {
|
||||
[new Vector2I(0, 0)] = new Vector2I(11, 17),
|
||||
[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(2, 1)] = new Vector2I(9, 33),
|
||||
}),
|
||||
// one desktop PC with one monitors
|
||||
// 一台台式机加一台显示器
|
||||
new(new Vector2I(7, 32), equipRect),
|
||||
};
|
||||
|
||||
// 定义第二种设备的主题映射
|
||||
private static readonly TileMapping[] equip2Themes = {
|
||||
// one laptop
|
||||
// 一台笔记本电脑
|
||||
new(new Vector2I(13, 31), equip2Rect),
|
||||
// one laptop with one monitor
|
||||
// 一台笔记本电脑加一台显示器
|
||||
new(new Dictionary<Vector2I, Vector2I>() {
|
||||
[new Vector2I(0, -1)] = new Vector2I(13, 32),
|
||||
[new Vector2I(1, -1)] = new Vector2I(10, 7),
|
||||
[new Vector2I(2, -1)] = new Vector2I(15, 32),
|
||||
}),
|
||||
// one desktop PC
|
||||
// 一台台式机
|
||||
new(new Dictionary<Vector2I, Vector2I>() {
|
||||
[new Vector2I(0, -1)] = new Vector2I(13, 32),
|
||||
[new Vector2I(1, -1)] = new Vector2I(14, 32),
|
||||
[new Vector2I(2, -1)] = new Vector2I(15, 30),
|
||||
}),
|
||||
// one desktop PC with one monitors
|
||||
// 一台台式机加一台显示器
|
||||
new(new Vector2I(13, 33), equip2Rect),
|
||||
};
|
||||
|
||||
private bool _draggable;
|
||||
/// <summary>
|
||||
/// 是否可拖拽
|
||||
/// </summary>
|
||||
public bool Draggable {
|
||||
get => _draggable;
|
||||
set {
|
||||
@ -86,10 +100,20 @@ public partial class Cube : StaticBody2D, ITileDraggable
|
||||
}
|
||||
|
||||
private readonly Guid _id = Guid.NewGuid();
|
||||
/// <summary>
|
||||
/// 唯一标识符
|
||||
/// </summary>
|
||||
public Guid Id => _id;
|
||||
|
||||
/// <summary>
|
||||
/// 在网格中的位置
|
||||
/// </summary>
|
||||
public Vector2I TilePosition { get; set; } = new Vector2I(5,5);
|
||||
|
||||
private bool _isCollided = true;
|
||||
/// <summary>
|
||||
/// 是否处于碰撞状态(显示红色背景)
|
||||
/// </summary>
|
||||
public bool IsCollided {
|
||||
get => _isCollided;
|
||||
set {
|
||||
@ -103,9 +127,15 @@ public partial class Cube : StaticBody2D, ITileDraggable
|
||||
}
|
||||
}
|
||||
private static readonly Rect2I tileRect = new(-1, -2, 4, 5);
|
||||
/// <summary>
|
||||
/// 占用矩形区域
|
||||
/// </summary>
|
||||
public Rect2I TileRect => tileRect;
|
||||
|
||||
private Vector2I _mouseOffset;
|
||||
/// <summary>
|
||||
/// 鼠标拖拽偏移
|
||||
/// </summary>
|
||||
public Vector2I MouseOffset => _mouseOffset;
|
||||
|
||||
private static readonly ITileDraggable.SpecialTile[] specialTiles = {
|
||||
@ -114,15 +144,27 @@ public partial class Cube : StaticBody2D, ITileDraggable
|
||||
};
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 特殊图块(如座位)
|
||||
/// </summary>
|
||||
public ITileDraggable.SpecialTile[] SpecialTiles => specialTiles;
|
||||
|
||||
// Called when the node enters the scene tree for the first time.
|
||||
/// <summary>
|
||||
/// 节点进入场景树时调用
|
||||
/// </summary>
|
||||
public override void _Ready()
|
||||
{
|
||||
RandomChangeTheme();
|
||||
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)
|
||||
{
|
||||
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.
|
||||
/// <summary>
|
||||
/// 每帧处理
|
||||
/// </summary>
|
||||
/// <param name="delta">时间间隔</param>
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
GlobalPosition = TilePosition * 48;
|
||||
@ -153,6 +199,10 @@ public partial class Cube : StaticBody2D, ITileDraggable
|
||||
private int _chair2ThemeIdx;
|
||||
private int _equipThemeIdx;
|
||||
private int _equip2ThemeIdx;
|
||||
|
||||
/// <summary>
|
||||
/// 随机更换主题样式
|
||||
/// </summary>
|
||||
public void RandomChangeTheme() {
|
||||
_tableThemeIdx = GD.RandRange(0, tableThemes.Length-1);
|
||||
_chairThemeIdx = GD.RandRange(0, chairThemes.Length-1);
|
||||
@ -177,6 +227,12 @@ public partial class Cube : StaticBody2D, ITileDraggable
|
||||
new(1,1),
|
||||
new(2,1),
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定位置的图块类型
|
||||
/// </summary>
|
||||
/// <param name="pos">相对位置</param>
|
||||
/// <returns>图块类型</returns>
|
||||
public Lab.MapNodeType GetTileType(Vector2I pos)
|
||||
{
|
||||
GD.Print($"query position of {pos}");
|
||||
@ -189,6 +245,11 @@ public partial class Cube : StaticBody2D, ITileDraggable
|
||||
return Lab.MapNodeType.Walkable;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置椅子朝向(待实现)
|
||||
/// </summary>
|
||||
/// <param name="target">目标位置</param>
|
||||
/// <param name="idx">索引</param>
|
||||
public void ChairFaceTo(Vector2I target, int idx) {
|
||||
if (idx == 0) {
|
||||
var theme = chairThemes[_chairThemeIdx];
|
||||
@ -196,4 +257,4 @@ public partial class Cube : StaticBody2D, ITileDraggable
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,41 +17,105 @@ using Core;
|
||||
public partial class GameManager : Node
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates if the game is currently in tutorial mode.
|
||||
/// 指示当前是否处于教程模式
|
||||
/// </summary>
|
||||
public static bool IsTutorial { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 下一个场景的路径
|
||||
/// </summary>
|
||||
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");
|
||||
|
||||
/// <summary>
|
||||
/// 最大回合数
|
||||
/// </summary>
|
||||
[Export]
|
||||
public int MaxTurns = 30;
|
||||
|
||||
// --- Global State ---
|
||||
|
||||
/// <summary>
|
||||
/// 游戏会话实例
|
||||
/// </summary>
|
||||
public GameSession Session { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前游戏状态
|
||||
/// </summary>
|
||||
public GameState State => Session?.State;
|
||||
|
||||
/// <summary>
|
||||
/// 游戏控制器
|
||||
/// </summary>
|
||||
public GameController Controller { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前游戏阶段
|
||||
/// </summary>
|
||||
public GamePhase CurrentPhase => State?.Turn.Phase ?? GamePhase.Planning;
|
||||
|
||||
/// <summary>
|
||||
/// 当前回合数
|
||||
/// </summary>
|
||||
public int CurrentTurn => State?.Turn.CurrentTurn ?? 1;
|
||||
|
||||
// --- Domain Model ---
|
||||
|
||||
/// <summary>
|
||||
/// 导师模型
|
||||
/// </summary>
|
||||
public MentorModel Mentor => State?.Roster.Mentor;
|
||||
|
||||
/// <summary>
|
||||
/// 学生列表
|
||||
/// </summary>
|
||||
public List<StudentModel> Students => State?.Roster.Students;
|
||||
|
||||
/// <summary>
|
||||
/// 当前活动任务列表
|
||||
/// </summary>
|
||||
public List<TaskModel> ActiveTasks => State?.Tasks.ActiveTasks;
|
||||
|
||||
/// <summary>
|
||||
/// 经济状态
|
||||
/// </summary>
|
||||
public EconomyState Economy => State?.Economy;
|
||||
|
||||
// --- Signals ---
|
||||
/// <summary>
|
||||
/// 阶段变更信号
|
||||
/// </summary>
|
||||
/// <param name="phase">阶段枚举的整数值</param>
|
||||
[Signal] public delegate void PhaseChangedEventHandler(int phase); // int cast of GamePhase
|
||||
|
||||
/// <summary>
|
||||
/// 回合变更信号
|
||||
/// </summary>
|
||||
/// <param name="turn">当前回合数</param>
|
||||
[Signal] public delegate void TurnChangedEventHandler(int turn);
|
||||
|
||||
// Singleton instance access (if needed, though Godot uses node paths)
|
||||
/// <summary>
|
||||
/// 单例实例
|
||||
/// </summary>
|
||||
public static GameManager Instance { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 进入场景树时调用
|
||||
/// </summary>
|
||||
public override void _EnterTree()
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 准备就绪时调用
|
||||
/// </summary>
|
||||
public override void _Ready()
|
||||
{
|
||||
Input.SetCustomMouseCursor(Arrow2X);
|
||||
@ -60,6 +124,9 @@ public partial class GameManager : Node
|
||||
InitializeGame();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化游戏数据
|
||||
/// </summary>
|
||||
private void InitializeGame()
|
||||
{
|
||||
Session = GameSession.CreateDefault();
|
||||
@ -83,6 +150,10 @@ public partial class GameManager : Node
|
||||
}
|
||||
|
||||
// Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
/// <summary>
|
||||
/// 每帧调用
|
||||
/// </summary>
|
||||
/// <param name="delta">距离上一帧的时间间隔</param>
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
if (CurrentPhase == GamePhase.Execution)
|
||||
@ -157,4 +228,4 @@ public partial class GameManager : Node
|
||||
|
||||
GD.Print($"Turn {CurrentTurn} Started. Phase: Planning");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,15 @@
|
||||
using Godot;
|
||||
|
||||
/// <summary>
|
||||
/// 辅助工具类
|
||||
/// </summary>
|
||||
public class H {
|
||||
/// <summary>
|
||||
/// 判断矩形是否包含点(包含边界)
|
||||
/// </summary>
|
||||
/// <param name="r">矩形区域</param>
|
||||
/// <param name="p">点坐标</param>
|
||||
/// <returns>如果点在矩形内(包括边界)则返回true</returns>
|
||||
public static bool RectHasPointInclusive(Rect2I r, Vector2I p) {
|
||||
if (r.HasPoint(p)) {
|
||||
return true;
|
||||
@ -13,4 +22,4 @@ public class H {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,25 +3,73 @@ using System.Diagnostics.SymbolStore;
|
||||
using System.Dynamic;
|
||||
using Godot;
|
||||
|
||||
/// <summary>
|
||||
/// 可拖拽图块接口
|
||||
/// </summary>
|
||||
public interface ITileDraggable {
|
||||
/// <summary>
|
||||
/// 图块在网格中的位置
|
||||
/// </summary>
|
||||
Vector2I TilePosition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否允许拖拽
|
||||
/// </summary>
|
||||
bool Draggable { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否处于碰撞状态(例如位置无效或重叠)
|
||||
/// </summary>
|
||||
bool IsCollided { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 图块占据的矩形区域(相对于 TilePosition)
|
||||
/// </summary>
|
||||
Rect2I TileRect { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 拖拽时鼠标相对于图块原点的偏移量
|
||||
/// </summary>
|
||||
Vector2I MouseOffset { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 特殊功能图块列表(如座位、交互点等)
|
||||
/// </summary>
|
||||
SpecialTile[] SpecialTiles { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 特殊图块定义
|
||||
/// </summary>
|
||||
class SpecialTile {
|
||||
/// <summary>
|
||||
/// 相对位置
|
||||
/// </summary>
|
||||
public readonly Vector2I Position;
|
||||
/// <summary>
|
||||
/// 节点类型(如可行走、作为座位等)
|
||||
/// </summary>
|
||||
public readonly Lab.MapNodeType NodeType;
|
||||
|
||||
/// <summary>
|
||||
/// 构造特殊图块
|
||||
/// </summary>
|
||||
/// <param name="pos">相对位置</param>
|
||||
/// <param name="node">节点类型</param>
|
||||
public SpecialTile(Vector2I pos, Lab.MapNodeType node) {
|
||||
Position = pos;
|
||||
NodeType = node;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定相对坐标处的图块类型
|
||||
/// </summary>
|
||||
/// <param name="pos">相对坐标</param>
|
||||
/// <returns>地图节点类型</returns>
|
||||
Lab.MapNodeType GetTileType(Vector2I pos);
|
||||
|
||||
/// <summary>
|
||||
/// 唯一标识符
|
||||
/// </summary>
|
||||
Guid Id { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,18 +3,26 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
/// <summary>
|
||||
/// 实验室场景主控脚本
|
||||
/// </summary>
|
||||
public partial class Lab : Node2D
|
||||
{
|
||||
/// <summary>
|
||||
/// 地图节点类型标志位
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum MapNodeType
|
||||
{
|
||||
Invalid = 0,
|
||||
Walkable = 1,
|
||||
Wall = 2,
|
||||
Blocker = 4,
|
||||
SeatUp = 8,
|
||||
SeatDown = 16,
|
||||
Invalid = 0, // 无效
|
||||
Walkable = 1, // 可行走
|
||||
Wall = 2, // 墙壁
|
||||
Blocker = 4, // 阻挡物
|
||||
SeatUp = 8, // 上方座位
|
||||
SeatDown = 16, // 下方座位
|
||||
}
|
||||
|
||||
// 墙壁矩形区域列表
|
||||
private static readonly Rect2I[] wallRectangles = {
|
||||
new(0,0,40,2),
|
||||
new(0,5,1, 15),
|
||||
@ -23,9 +31,15 @@ public partial class Lab : Node2D
|
||||
};
|
||||
|
||||
private readonly Dictionary<Guid, ITileDraggable> _furnitureIDs = new();
|
||||
/// <summary>
|
||||
/// 家具字典,通过ID索引
|
||||
/// </summary>
|
||||
public Dictionary<Guid, ITileDraggable> Furniture => _furnitureIDs;
|
||||
|
||||
// Called when the node enters the scene tree for the first time.
|
||||
/// <summary>
|
||||
/// 场景加载完成时调用
|
||||
/// </summary>
|
||||
public override void _Ready()
|
||||
{
|
||||
var ticker = GetNode<Timer>("/root/GameManager/OneSecondTicker");
|
||||
@ -57,6 +71,10 @@ public partial class Lab : Node2D
|
||||
|
||||
private Label _moneyLabel;
|
||||
// Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
/// <summary>
|
||||
/// 每帧更新
|
||||
/// </summary>
|
||||
/// <param name="delta">帧间隔</param>
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
switch(GD.RandRange(0,2)) {
|
||||
@ -82,12 +100,21 @@ public partial class Lab : Node2D
|
||||
}
|
||||
|
||||
private bool _isDragging;
|
||||
|
||||
/// <summary>
|
||||
/// 拾起物品
|
||||
/// </summary>
|
||||
/// <param name="target">目标物品</param>
|
||||
public void Pickup(ITileDraggable target) {
|
||||
if (target == null) return;
|
||||
_isDragging = true;
|
||||
DraggingTarget = target;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 放下物品
|
||||
/// </summary>
|
||||
/// <param name="target">目标物品</param>
|
||||
public void PutDown(ITileDraggable target) {
|
||||
if (target == null) return;
|
||||
_isDragging = false;
|
||||
@ -98,6 +125,10 @@ public partial class Lab : Node2D
|
||||
DraggingTarget = null;
|
||||
UpdateMap();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当前正在拖拽的目标
|
||||
/// </summary>
|
||||
public ITileDraggable DraggingTarget { get; set; }
|
||||
|
||||
public const int MapWidth = 40;
|
||||
@ -109,6 +140,9 @@ public partial class Lab : Node2D
|
||||
|
||||
private TileMapLayer _tileMap;
|
||||
|
||||
/// <summary>
|
||||
/// 更新地图数据
|
||||
/// </summary>
|
||||
public void UpdateMap()
|
||||
{
|
||||
for (int i = 0; i < MapWidth; i++) {
|
||||
@ -132,8 +166,6 @@ public partial class Lab : Node2D
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static bool IsValidPosition(Vector2I pos)
|
||||
{
|
||||
int x = pos.X;
|
||||
@ -143,6 +175,7 @@ public partial class Lab : Node2D
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private List<Vector2I> GetNeighbors(Vector2I pos) {
|
||||
int x = pos.X;
|
||||
int y = pos.Y;
|
||||
@ -166,6 +199,12 @@ public partial class Lab : Node2D
|
||||
return neighbor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取最短路径
|
||||
/// </summary>
|
||||
/// <param name="start">起点</param>
|
||||
/// <param name="end">终点</param>
|
||||
/// <returns>路径点列表</returns>
|
||||
public List<Vector2I> GetShortestPath(Vector2I start, Vector2I end)
|
||||
{
|
||||
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) {
|
||||
if (!IsValidPosition(pos)) return MapNodeType.Invalid;
|
||||
return _blocks[pos.X, pos.Y];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 世界坐标转网格坐标
|
||||
/// </summary>
|
||||
/// <param name="pos">世界坐标</param>
|
||||
/// <returns>网格坐标</returns>
|
||||
public Vector2I Point2Coord(Vector2 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) {
|
||||
if (!_furnitureIDs.ContainsKey(fId)) return Vector2I.Zero;
|
||||
if (idx < 0 || idx > _furnitureIDs[fId].SpecialTiles.Length) return Vector2I.Zero;
|
||||
return _furnitureIDs[fId].SpecialTiles[idx].Position + _furnitureIDs[fId].TilePosition;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,9 @@ using Godot;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/// <summary>
|
||||
/// 场景加载器
|
||||
/// </summary>
|
||||
public partial class Loader : Control
|
||||
{
|
||||
private ProgressBar _progressBar;
|
||||
@ -12,6 +15,9 @@ public partial class Loader : Control
|
||||
};
|
||||
|
||||
// Called when the node enters the scene tree for the first time.
|
||||
/// <summary>
|
||||
/// 场景加载完成时调用
|
||||
/// </summary>
|
||||
public override void _Ready()
|
||||
{
|
||||
_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.
|
||||
/// <summary>
|
||||
/// 每帧更新
|
||||
/// </summary>
|
||||
/// <param name="delta">帧间隔</param>
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
Godot.Collections.Array progress = new();
|
||||
@ -53,4 +63,4 @@ public partial class Loader : Control
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,15 +1,25 @@
|
||||
using Godot;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Logo场景
|
||||
/// </summary>
|
||||
public partial class LogoScene : Node2D
|
||||
{
|
||||
/// <summary>
|
||||
/// 场景加载完成时调用
|
||||
/// </summary>
|
||||
public override void _Ready() {
|
||||
GetNode<AnimationPlayer>("AnimationPlayer").AnimationFinished += OnAnimationPlayerAnimationFinished;
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// 动画播放结束回调
|
||||
/// </summary>
|
||||
/// <param name="animationName">动画名称</param>
|
||||
public void OnAnimationPlayerAnimationFinished(StringName animationName)
|
||||
{
|
||||
GD.Print("FFF");
|
||||
GetTree().ChangeSceneToFile("res://scenes/loader.tscn");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -12,44 +12,142 @@ namespace Models;
|
||||
/// </summary>
|
||||
public static class CoreIds
|
||||
{
|
||||
/// <summary>
|
||||
/// 默认命名空间
|
||||
/// </summary>
|
||||
public const string Namespace = "core";
|
||||
|
||||
// Disciplines
|
||||
/// <summary>
|
||||
/// 生物学
|
||||
/// </summary>
|
||||
public const string DisciplineBiology = "core:discipline_biology";
|
||||
/// <summary>
|
||||
/// 化学
|
||||
/// </summary>
|
||||
public const string DisciplineChemistry = "core:discipline_chemistry";
|
||||
/// <summary>
|
||||
/// 环境科学
|
||||
/// </summary>
|
||||
public const string DisciplineEnvironment = "core:discipline_environment";
|
||||
/// <summary>
|
||||
/// 材料学
|
||||
/// </summary>
|
||||
public const string DisciplineMaterials = "core:discipline_materials";
|
||||
/// <summary>
|
||||
/// 医学
|
||||
/// </summary>
|
||||
public const string DisciplineMedicine = "core:discipline_medicine";
|
||||
/// <summary>
|
||||
/// 计算机
|
||||
/// </summary>
|
||||
public const string DisciplineComputer = "core:discipline_computer";
|
||||
/// <summary>
|
||||
/// 数学
|
||||
/// </summary>
|
||||
public const string DisciplineMath = "core:discipline_math";
|
||||
/// <summary>
|
||||
/// 物理学
|
||||
/// </summary>
|
||||
public const string DisciplinePhysics = "core:discipline_physics";
|
||||
/// <summary>
|
||||
/// 机械工程
|
||||
/// </summary>
|
||||
public const string DisciplineMechanical = "core:discipline_mechanical";
|
||||
/// <summary>
|
||||
/// 哲学
|
||||
/// </summary>
|
||||
public const string DisciplinePhilosophy = "core:discipline_philosophy";
|
||||
/// <summary>
|
||||
/// 经济学
|
||||
/// </summary>
|
||||
public const string DisciplineEconomics = "core:discipline_economics";
|
||||
/// <summary>
|
||||
/// 法学
|
||||
/// </summary>
|
||||
public const string DisciplineLaw = "core:discipline_law";
|
||||
/// <summary>
|
||||
/// 文学
|
||||
/// </summary>
|
||||
public const string DisciplineLiterature = "core:discipline_literature";
|
||||
/// <summary>
|
||||
/// 农学
|
||||
/// </summary>
|
||||
public const string DisciplineAgriculture = "core:discipline_agriculture";
|
||||
/// <summary>
|
||||
/// 管理学
|
||||
/// </summary>
|
||||
public const string DisciplineManagement = "core:discipline_management";
|
||||
/// <summary>
|
||||
/// 艺术
|
||||
/// </summary>
|
||||
public const string DisciplineArt = "core:discipline_art";
|
||||
|
||||
// Archetypes
|
||||
/// <summary>
|
||||
/// 卷王
|
||||
/// </summary>
|
||||
public const string ArchetypeGrinder = "core:archetype_grinder";
|
||||
/// <summary>
|
||||
/// 摸鱼
|
||||
/// </summary>
|
||||
public const string ArchetypeSlacker = "core:archetype_slacker";
|
||||
/// <summary>
|
||||
/// 精英
|
||||
/// </summary>
|
||||
public const string ArchetypeElite = "core:archetype_elite";
|
||||
/// <summary>
|
||||
/// 天才
|
||||
/// </summary>
|
||||
public const string ArchetypeProdigy = "core:archetype_prodigy";
|
||||
/// <summary>
|
||||
/// 吉祥物
|
||||
/// </summary>
|
||||
public const string ArchetypeMascot = "core:archetype_mascot";
|
||||
|
||||
// Roles
|
||||
/// <summary>
|
||||
/// 码农
|
||||
/// </summary>
|
||||
public const string RoleCoder = "core:role_coder";
|
||||
/// <summary>
|
||||
/// 写手
|
||||
/// </summary>
|
||||
public const string RoleWriter = "core:role_writer";
|
||||
/// <summary>
|
||||
/// 实验员
|
||||
/// </summary>
|
||||
public const string RoleLabRat = "core:role_lab_rat";
|
||||
/// <summary>
|
||||
/// 讲演者
|
||||
/// </summary>
|
||||
public const string RolePresenter = "core:role_presenter";
|
||||
/// <summary>
|
||||
/// 记录员
|
||||
/// </summary>
|
||||
public const string RoleScribe = "core:role_scribe";
|
||||
/// <summary>
|
||||
/// 辩论者
|
||||
/// </summary>
|
||||
public const string RoleOrator = "core:role_orator";
|
||||
/// <summary>
|
||||
/// 管家
|
||||
/// </summary>
|
||||
public const string RoleSteward = "core:role_steward";
|
||||
/// <summary>
|
||||
/// 炼金术士
|
||||
/// </summary>
|
||||
public const string RoleAlchemist = "core:role_alchemist";
|
||||
/// <summary>
|
||||
/// 极客
|
||||
/// </summary>
|
||||
public const string RoleGeek = "core:role_geek";
|
||||
/// <summary>
|
||||
/// 勘测员
|
||||
/// </summary>
|
||||
public const string RoleSurveyor = "core:role_surveyor";
|
||||
/// <summary>
|
||||
/// 思考者
|
||||
/// </summary>
|
||||
public const string RoleThinker = "core:role_thinker";
|
||||
}
|
||||
|
||||
}
|
||||
@ -17,16 +17,36 @@ namespace Models;
|
||||
/// </summary>
|
||||
public sealed class LocalizedText
|
||||
{
|
||||
/// <summary>
|
||||
/// 键值
|
||||
/// </summary>
|
||||
public string Key { get; set; }
|
||||
/// <summary>
|
||||
/// 默认值
|
||||
/// </summary>
|
||||
public string Fallback { get; set; }
|
||||
}
|
||||
|
||||
public sealed class DefinitionHeader
|
||||
{
|
||||
/// <summary>
|
||||
/// 定义ID
|
||||
/// </summary>
|
||||
public string Id { get; set; }
|
||||
/// <summary>
|
||||
/// 名称
|
||||
/// </summary>
|
||||
public LocalizedText Name { get; set; } = new();
|
||||
/// <summary>
|
||||
/// 描述
|
||||
/// </summary>
|
||||
public LocalizedText Description { get; set; } = new();
|
||||
/// <summary>
|
||||
/// 图标路径
|
||||
/// </summary>
|
||||
public string IconPath { get; set; }
|
||||
/// <summary>
|
||||
/// 标签列表
|
||||
/// </summary>
|
||||
public List<string> Tags { get; } = new();
|
||||
}
|
||||
|
||||
}
|
||||
@ -14,18 +14,44 @@ namespace Models;
|
||||
/// </summary>
|
||||
public sealed class DisciplineDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// 基础头部信息
|
||||
/// </summary>
|
||||
public DefinitionHeader Header { get; set; } = new();
|
||||
/// <summary>
|
||||
/// 学科Buff
|
||||
/// </summary>
|
||||
public DisciplineBuff Buff { get; set; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// 角色池ID列表
|
||||
/// </summary>
|
||||
public List<string> RolePoolIds { get; } = new();
|
||||
/// <summary>
|
||||
/// 物品池ID列表
|
||||
/// </summary>
|
||||
public List<string> ItemPoolIds { get; } = new();
|
||||
/// <summary>
|
||||
/// 任务关键词ID列表
|
||||
/// </summary>
|
||||
public List<string> TaskKeywordIds { get; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 学科Buff定义
|
||||
/// </summary>
|
||||
public sealed class DisciplineBuff
|
||||
{
|
||||
/// <summary>
|
||||
/// 名称
|
||||
/// </summary>
|
||||
public LocalizedText Name { get; set; } = new();
|
||||
/// <summary>
|
||||
/// 描述
|
||||
/// </summary>
|
||||
public LocalizedText Description { get; set; } = new();
|
||||
/// <summary>
|
||||
/// 修正包
|
||||
/// </summary>
|
||||
public ModifierBundle Modifiers { get; set; } = new();
|
||||
}
|
||||
|
||||
}
|
||||
@ -12,32 +12,31 @@ namespace Models;
|
||||
/// </summary>
|
||||
public enum AttributeType
|
||||
{
|
||||
Academic,
|
||||
Engineering,
|
||||
Writing,
|
||||
Financial,
|
||||
Social,
|
||||
Activation
|
||||
Academic, // 学术
|
||||
Engineering, // 工程
|
||||
Writing, // 写作
|
||||
Financial, // 财务
|
||||
Social, // 社交
|
||||
Activation // 活跃/执行
|
||||
}
|
||||
|
||||
public enum ResourceType
|
||||
{
|
||||
Money,
|
||||
Reputation,
|
||||
ResearchPoints,
|
||||
Paper,
|
||||
Inspiration,
|
||||
Time
|
||||
Money, // 金钱
|
||||
Reputation, // 声望
|
||||
ResearchPoints, // 科研点
|
||||
Paper, // 论文
|
||||
Inspiration, // 灵感
|
||||
Time // 时间
|
||||
}
|
||||
|
||||
public enum StatusType
|
||||
{
|
||||
Stress,
|
||||
Sanity,
|
||||
Mood,
|
||||
Stamina,
|
||||
Loyalty,
|
||||
Energy,
|
||||
Health
|
||||
}
|
||||
|
||||
Stress, // 压力
|
||||
Sanity, // 理智
|
||||
Mood, // 心情
|
||||
Stamina, // 体力
|
||||
Loyalty, // 忠诚度
|
||||
Energy, // 精力
|
||||
Health // 健康
|
||||
}
|
||||
@ -14,13 +14,36 @@ namespace Models;
|
||||
/// </summary>
|
||||
public sealed class GameContentDatabase
|
||||
{
|
||||
/// <summary>
|
||||
/// 学科定义字典
|
||||
/// </summary>
|
||||
public Dictionary<string, DisciplineDefinition> Disciplines { get; } = new();
|
||||
/// <summary>
|
||||
/// 原型定义字典
|
||||
/// </summary>
|
||||
public Dictionary<string, ArchetypeDefinition> Archetypes { get; } = new();
|
||||
/// <summary>
|
||||
/// 角色定义字典
|
||||
/// </summary>
|
||||
public Dictionary<string, RoleDefinition> Roles { get; } = new();
|
||||
/// <summary>
|
||||
/// 特质定义字典
|
||||
/// </summary>
|
||||
public Dictionary<string, TraitDefinition> Traits { get; } = new();
|
||||
/// <summary>
|
||||
/// 任务定义字典
|
||||
/// </summary>
|
||||
public Dictionary<string, TaskDefinition> Tasks { get; } = new();
|
||||
/// <summary>
|
||||
/// 物品定义字典
|
||||
/// </summary>
|
||||
public Dictionary<string, ItemDefinition> Items { get; } = new();
|
||||
/// <summary>
|
||||
/// 论文定义字典
|
||||
/// </summary>
|
||||
public Dictionary<string, PaperDefinition> Papers { get; } = new();
|
||||
/// <summary>
|
||||
/// 肉鸽天赋定义字典
|
||||
/// </summary>
|
||||
public Dictionary<string, RoguelitePerkDefinition> RoguelitePerks { get; } = new();
|
||||
}
|
||||
|
||||
}
|
||||
@ -17,69 +17,179 @@ namespace Models;
|
||||
/// </summary>
|
||||
public sealed class GameState
|
||||
{
|
||||
/// <summary>
|
||||
/// 回合状态
|
||||
/// </summary>
|
||||
public TurnState Turn { get; } = new();
|
||||
/// <summary>
|
||||
/// 经济状态
|
||||
/// </summary>
|
||||
public EconomyState Economy { get; } = new();
|
||||
/// <summary>
|
||||
/// 人员状态
|
||||
/// </summary>
|
||||
public RosterState Roster { get; } = new();
|
||||
/// <summary>
|
||||
/// 任务状态
|
||||
/// </summary>
|
||||
public TaskState Tasks { get; } = new();
|
||||
/// <summary>
|
||||
/// 库存状态
|
||||
/// </summary>
|
||||
public InventoryState Inventory { get; } = new();
|
||||
/// <summary>
|
||||
/// 协同状态
|
||||
/// </summary>
|
||||
public SynergyState Synergy { get; } = new();
|
||||
/// <summary>
|
||||
/// 肉鸽状态
|
||||
/// </summary>
|
||||
public RogueliteState Roguelite { get; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 游戏阶段
|
||||
/// </summary>
|
||||
public enum GamePhase
|
||||
{
|
||||
Planning,
|
||||
Execution,
|
||||
Review
|
||||
Planning, // 筹备阶段
|
||||
Execution, // 执行阶段
|
||||
Review // 结算阶段
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 回合状态数据
|
||||
/// </summary>
|
||||
public sealed class TurnState
|
||||
{
|
||||
/// <summary>
|
||||
/// 当前回合
|
||||
/// </summary>
|
||||
public int CurrentTurn { get; set; } = 1;
|
||||
/// <summary>
|
||||
/// 最大回合
|
||||
/// </summary>
|
||||
public int MaxTurns { get; set; } = 30;
|
||||
/// <summary>
|
||||
/// 当前阶段
|
||||
/// </summary>
|
||||
public GamePhase Phase { get; set; } = GamePhase.Planning;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 经济状态数据
|
||||
/// </summary>
|
||||
public sealed class EconomyState
|
||||
{
|
||||
/// <summary>
|
||||
/// 资金
|
||||
/// </summary>
|
||||
public int Money { get; set; } = 50000;
|
||||
/// <summary>
|
||||
/// 声望
|
||||
/// </summary>
|
||||
public int Reputation { get; set; }
|
||||
/// <summary>
|
||||
/// 科研点数
|
||||
/// </summary>
|
||||
public int ResearchPoints { get; set; }
|
||||
/// <summary>
|
||||
/// 利率
|
||||
/// </summary>
|
||||
public float InterestRate { get; set; } = 0.0f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 人员状态数据
|
||||
/// </summary>
|
||||
public sealed class RosterState
|
||||
{
|
||||
/// <summary>
|
||||
/// 导师模型
|
||||
/// </summary>
|
||||
public MentorModel Mentor { get; set; } = new("Player");
|
||||
/// <summary>
|
||||
/// 学生列表
|
||||
/// </summary>
|
||||
public List<StudentModel> Students { get; } = new();
|
||||
/// <summary>
|
||||
/// 职工列表
|
||||
/// </summary>
|
||||
public List<StaffModel> Staffs { get; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 任务状态数据
|
||||
/// </summary>
|
||||
public sealed class TaskState
|
||||
{
|
||||
/// <summary>
|
||||
/// 活动任务列表
|
||||
/// </summary>
|
||||
public List<TaskModel> ActiveTasks { get; } = new();
|
||||
/// <summary>
|
||||
/// 已完成任务列表
|
||||
/// </summary>
|
||||
public List<TaskModel> CompletedTasks { get; } = new();
|
||||
/// <summary>
|
||||
/// 失败任务列表
|
||||
/// </summary>
|
||||
public List<TaskModel> FailedTasks { get; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 库存状态数据
|
||||
/// </summary>
|
||||
public sealed class InventoryState
|
||||
{
|
||||
/// <summary>
|
||||
/// 物品计数
|
||||
/// </summary>
|
||||
public Dictionary<string, int> ItemCounts { get; } = new();
|
||||
/// <summary>
|
||||
/// 论文计数
|
||||
/// </summary>
|
||||
public Dictionary<PaperRank, int> PaperCounts { get; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 协同状态数据
|
||||
/// </summary>
|
||||
public sealed class SynergyState
|
||||
{
|
||||
/// <summary>
|
||||
/// 原型堆叠数
|
||||
/// </summary>
|
||||
public Dictionary<string, int> ArchetypeStacks { get; } = new();
|
||||
/// <summary>
|
||||
/// 角色堆叠数
|
||||
/// </summary>
|
||||
public Dictionary<string, int> RoleStacks { get; } = new();
|
||||
/// <summary>
|
||||
/// 激活的协同ID列表
|
||||
/// </summary>
|
||||
public List<string> ActiveSynergyIds { get; } = new();
|
||||
/// <summary>
|
||||
/// 激活的修正包
|
||||
/// </summary>
|
||||
public ModifierBundle ActiveModifiers { get; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 肉鸽状态数据
|
||||
/// </summary>
|
||||
public sealed class RogueliteState
|
||||
{
|
||||
/// <summary>
|
||||
/// 校友卡ID列表
|
||||
/// </summary>
|
||||
public List<string> AlumniCardIds { get; } = new();
|
||||
/// <summary>
|
||||
/// 遗产解锁ID列表
|
||||
/// </summary>
|
||||
public List<string> LegacyUnlockIds { get; } = new();
|
||||
/// <summary>
|
||||
/// 头衔保留等级
|
||||
/// </summary>
|
||||
public int TitleRetentionLevel { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
@ -14,29 +14,29 @@ namespace Models;
|
||||
/// </summary>
|
||||
public enum ItemCategory
|
||||
{
|
||||
Facility,
|
||||
Equipment,
|
||||
Consumable
|
||||
Facility, // 设施
|
||||
Equipment, // 装备
|
||||
Consumable // 消耗品
|
||||
}
|
||||
|
||||
public enum FacilityPlacement
|
||||
{
|
||||
Lab,
|
||||
ServerRoom,
|
||||
Admin,
|
||||
Library,
|
||||
RestArea,
|
||||
Field,
|
||||
Global
|
||||
Lab, // 实验室
|
||||
ServerRoom, // 机房
|
||||
Admin, // 行政区
|
||||
Library, // 图书馆
|
||||
RestArea, // 休息区
|
||||
Field, // 场地
|
||||
Global // 全局
|
||||
}
|
||||
|
||||
public enum EquipmentSlot
|
||||
{
|
||||
Tool,
|
||||
Accessory,
|
||||
Body,
|
||||
Head,
|
||||
Hand
|
||||
Tool, // 工具
|
||||
Accessory, // 饰品
|
||||
Body, // 身体
|
||||
Head, // 头部
|
||||
Hand // 手部
|
||||
}
|
||||
|
||||
public sealed class ItemDefinition
|
||||
@ -55,5 +55,4 @@ public sealed class ItemEffect
|
||||
{
|
||||
public ModifierBundle Modifiers { get; set; } = new();
|
||||
public List<string> RuleIds { get; } = new();
|
||||
}
|
||||
|
||||
}
|
||||
@ -14,22 +14,41 @@ namespace Models;
|
||||
/// </summary>
|
||||
public sealed class MentorModel
|
||||
{
|
||||
/// <summary>
|
||||
/// 导师模式
|
||||
/// </summary>
|
||||
public enum MentorModeType
|
||||
{
|
||||
Worker,
|
||||
Manager
|
||||
Worker, // 打工人
|
||||
Manager // 管理者
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 核心单位数据
|
||||
/// </summary>
|
||||
public UnitModel Core { get; }
|
||||
/// <summary>
|
||||
/// 导师特有资源
|
||||
/// </summary>
|
||||
public MentorResources Resources { get; }
|
||||
/// <summary>
|
||||
/// 当前模式
|
||||
/// </summary>
|
||||
public MentorModeType Mode { get; set; } = MentorModeType.Worker;
|
||||
|
||||
/// <summary>
|
||||
/// 名字(便捷访问)
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get => Core.Name;
|
||||
set => Core.Name = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="name">名字</param>
|
||||
public MentorModel(string name)
|
||||
{
|
||||
Core = new UnitModel(name);
|
||||
@ -42,10 +61,13 @@ public sealed class MentorModel
|
||||
/// </summary>
|
||||
public sealed class MentorResources
|
||||
{
|
||||
/// <summary>
|
||||
/// 精力值
|
||||
/// </summary>
|
||||
public StatusValue Energy { get; set; }
|
||||
|
||||
public MentorResources()
|
||||
{
|
||||
Energy = new StatusValue(100, 100, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -14,30 +14,68 @@ namespace Models;
|
||||
/// </summary>
|
||||
public sealed class AttributeModifier
|
||||
{
|
||||
/// <summary>
|
||||
/// 属性类型
|
||||
/// </summary>
|
||||
public AttributeType Type { get; set; }
|
||||
/// <summary>
|
||||
/// 加值
|
||||
/// </summary>
|
||||
public float Add { get; set; }
|
||||
/// <summary>
|
||||
/// 乘数
|
||||
/// </summary>
|
||||
public float Multiplier { get; set; } = 1.0f;
|
||||
}
|
||||
|
||||
public sealed class StatusModifier
|
||||
{
|
||||
/// <summary>
|
||||
/// 状态类型
|
||||
/// </summary>
|
||||
public StatusType Type { get; set; }
|
||||
/// <summary>
|
||||
/// 加值
|
||||
/// </summary>
|
||||
public float Add { get; set; }
|
||||
/// <summary>
|
||||
/// 乘数
|
||||
/// </summary>
|
||||
public float Multiplier { get; set; } = 1.0f;
|
||||
}
|
||||
|
||||
public sealed class ResourceModifier
|
||||
{
|
||||
/// <summary>
|
||||
/// 资源类型
|
||||
/// </summary>
|
||||
public ResourceType Type { get; set; }
|
||||
/// <summary>
|
||||
/// 加值
|
||||
/// </summary>
|
||||
public int Add { get; set; }
|
||||
/// <summary>
|
||||
/// 乘数
|
||||
/// </summary>
|
||||
public float Multiplier { get; set; } = 1.0f;
|
||||
}
|
||||
|
||||
public sealed class ModifierBundle
|
||||
{
|
||||
/// <summary>
|
||||
/// 属性修正列表
|
||||
/// </summary>
|
||||
public List<AttributeModifier> AttributeModifiers { get; } = new();
|
||||
/// <summary>
|
||||
/// 状态修正列表
|
||||
/// </summary>
|
||||
public List<StatusModifier> StatusModifiers { get; } = new();
|
||||
/// <summary>
|
||||
/// 资源修正列表
|
||||
/// </summary>
|
||||
public List<ResourceModifier> ResourceModifiers { get; } = new();
|
||||
/// <summary>
|
||||
/// 规则ID列表
|
||||
/// </summary>
|
||||
public List<string> RuleIds { get; } = new();
|
||||
}
|
||||
|
||||
}
|
||||
@ -14,5 +14,4 @@ public sealed class PaperDefinition
|
||||
{
|
||||
public DefinitionHeader Header { get; set; } = new();
|
||||
public PaperRank Rank { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
@ -20,17 +20,32 @@ public sealed class PropertyValue
|
||||
public const float DefaultMin = 0f;
|
||||
public const float DefaultMax = 100f;
|
||||
|
||||
/// <summary>
|
||||
/// 最小值
|
||||
/// </summary>
|
||||
public float Min { get; }
|
||||
/// <summary>
|
||||
/// 最大值
|
||||
/// </summary>
|
||||
public float Max { get; }
|
||||
|
||||
private float _value;
|
||||
|
||||
/// <summary>
|
||||
/// 当前值(自动限制在 Min/Max 范围内)
|
||||
/// </summary>
|
||||
public float Value
|
||||
{
|
||||
get => _value;
|
||||
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)
|
||||
{
|
||||
if (max < min)
|
||||
@ -44,13 +59,26 @@ public sealed class PropertyValue
|
||||
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)
|
||||
: this((float)value, min, max)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取显示的整数值
|
||||
/// </summary>
|
||||
/// <returns>整数值</returns>
|
||||
public int DisplayInt() => (int)_value;
|
||||
|
||||
/// <summary>
|
||||
/// 获取归一化值 (0.0 - 1.0)
|
||||
/// </summary>
|
||||
public float Normalized
|
||||
{
|
||||
get
|
||||
@ -60,10 +88,22 @@ public sealed class PropertyValue
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 克隆
|
||||
/// </summary>
|
||||
/// <returns>新实例</returns>
|
||||
public PropertyValue Clone() => new(Value, Min, Max);
|
||||
|
||||
/// <summary>
|
||||
/// 增加值
|
||||
/// </summary>
|
||||
/// <param name="delta">增量</param>
|
||||
public void Add(float delta) => Value += delta;
|
||||
|
||||
/// <summary>
|
||||
/// 减少值
|
||||
/// </summary>
|
||||
/// <param name="delta">减量</param>
|
||||
public void Subtract(float delta) => Value -= delta;
|
||||
|
||||
public override string ToString() => DisplayInt().ToString();
|
||||
@ -128,4 +168,4 @@ public sealed class PropertyValue
|
||||
if (value > max) return max;
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -12,16 +12,27 @@ namespace Models;
|
||||
/// </summary>
|
||||
public enum RoguelitePerkType
|
||||
{
|
||||
AlumniCard,
|
||||
LegacyProgress,
|
||||
TitleRetention
|
||||
AlumniCard, // 校友卡
|
||||
LegacyProgress, // 遗产进度
|
||||
TitleRetention // 头衔保留
|
||||
}
|
||||
|
||||
public sealed class RoguelitePerkDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// 基础头部信息
|
||||
/// </summary>
|
||||
public DefinitionHeader Header { get; set; } = new();
|
||||
/// <summary>
|
||||
/// 天赋类型
|
||||
/// </summary>
|
||||
public RoguelitePerkType Type { get; set; }
|
||||
/// <summary>
|
||||
/// 修正包
|
||||
/// </summary>
|
||||
public ModifierBundle Modifiers { get; set; } = new();
|
||||
/// <summary>
|
||||
/// 最大等级
|
||||
/// </summary>
|
||||
public int MaxLevel { get; set; } = 1;
|
||||
}
|
||||
|
||||
}
|
||||
@ -14,22 +14,43 @@ namespace Models;
|
||||
/// </summary>
|
||||
public sealed class StaffModel
|
||||
{
|
||||
/// <summary>
|
||||
/// 职员类型
|
||||
/// </summary>
|
||||
public enum StaffType
|
||||
{
|
||||
PostDoc,
|
||||
JuniorFaculty
|
||||
PostDoc, // 博士后
|
||||
JuniorFaculty // 青年教师
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 核心单位数据
|
||||
/// </summary>
|
||||
public UnitModel Core { get; }
|
||||
/// <summary>
|
||||
/// 职员类型
|
||||
/// </summary>
|
||||
public StaffType Type { get; private set; }
|
||||
/// <summary>
|
||||
/// 动机数据
|
||||
/// </summary>
|
||||
public StaffMotivation Motivation { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 名字(便捷访问)
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get => Core.Name;
|
||||
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)
|
||||
{
|
||||
Core = new UnitModel(name, random);
|
||||
@ -38,10 +59,21 @@ public sealed class StaffModel
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 职员动机组件
|
||||
/// </summary>
|
||||
public sealed class StaffMotivation
|
||||
{
|
||||
/// <summary>
|
||||
/// 野心
|
||||
/// </summary>
|
||||
public StatusValue Ambition { get; set; } = new(50, 100, 0);
|
||||
/// <summary>
|
||||
/// 忠诚度
|
||||
/// </summary>
|
||||
public StatusValue Loyalty { get; set; } = new(70, 100, 0);
|
||||
/// <summary>
|
||||
/// 合同剩余回合
|
||||
/// </summary>
|
||||
public StatusValue ContractTurns { get; set; } = new(6, 12, 0);
|
||||
}
|
||||
|
||||
}
|
||||
@ -19,9 +19,18 @@ public sealed class StatusValue
|
||||
{
|
||||
private PropertyValue _current;
|
||||
|
||||
/// <summary>
|
||||
/// 达到上限事件
|
||||
/// </summary>
|
||||
public event Action OnUpperThresholdReached;
|
||||
/// <summary>
|
||||
/// 达到下限事件
|
||||
/// </summary>
|
||||
public event Action OnLowerThresholdReached;
|
||||
|
||||
/// <summary>
|
||||
/// 当前值
|
||||
/// </summary>
|
||||
public PropertyValue Current
|
||||
{
|
||||
get => _current;
|
||||
@ -32,9 +41,18 @@ public sealed class StatusValue
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 上限阈值
|
||||
/// </summary>
|
||||
public float UpperThreshold { get; set; }
|
||||
/// <summary>
|
||||
/// 下限阈值
|
||||
/// </summary>
|
||||
public float LowerThreshold { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数(整数)
|
||||
/// </summary>
|
||||
public StatusValue(int current = 0, int upper = 100, int lower = 0)
|
||||
{
|
||||
_current = new PropertyValue(current, lower, upper);
|
||||
@ -42,6 +60,9 @@ public sealed class StatusValue
|
||||
LowerThreshold = lower;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数(浮点数)
|
||||
/// </summary>
|
||||
public StatusValue(float current, float upper, float lower)
|
||||
{
|
||||
_current = new PropertyValue(current, lower, upper);
|
||||
@ -49,6 +70,9 @@ public sealed class StatusValue
|
||||
LowerThreshold = lower;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数(PropertyValue)
|
||||
/// </summary>
|
||||
public StatusValue(PropertyValue current, float upperThreshold, float lowerThreshold)
|
||||
{
|
||||
_current = current ?? new PropertyValue(0);
|
||||
@ -70,15 +94,21 @@ public sealed class StatusValue
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 增加值
|
||||
/// </summary>
|
||||
public void Add(float value)
|
||||
{
|
||||
_current.Add(value);
|
||||
CheckThresholds();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 减少值
|
||||
/// </summary>
|
||||
public void Subtract(float value)
|
||||
{
|
||||
_current.Subtract(value);
|
||||
CheckThresholds();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -18,25 +18,51 @@ namespace Models;
|
||||
/// </summary>
|
||||
public sealed class StudentModel
|
||||
{
|
||||
/// <summary>
|
||||
/// 学生类型
|
||||
/// </summary>
|
||||
public enum StudentType
|
||||
{
|
||||
MasterCandidate,
|
||||
DoctorCandidate
|
||||
MasterCandidate, // 硕士生
|
||||
DoctorCandidate // 博士生
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 核心单位数据
|
||||
/// </summary>
|
||||
public UnitModel Core { get; }
|
||||
/// <summary>
|
||||
/// 学生类型
|
||||
/// </summary>
|
||||
public StudentType Type { get; private set; }
|
||||
/// <summary>
|
||||
/// 进度数据
|
||||
/// </summary>
|
||||
public StudentProgress Progress { get; }
|
||||
/// <summary>
|
||||
/// 贡献记录
|
||||
/// </summary>
|
||||
public StudentContributions Contributions { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 名字(便捷访问)
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get => Core.Name;
|
||||
set => Core.Name = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 特质列表(便捷访问)
|
||||
/// </summary>
|
||||
public List<string> Traits => Core.Tags.TraitIds;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="name">名字</param>
|
||||
/// <param name="random">随机数生成器</param>
|
||||
public StudentModel(string name, Random random = null)
|
||||
{
|
||||
var rng = random ?? Random.Shared;
|
||||
@ -52,8 +78,17 @@ public sealed class StudentModel
|
||||
/// </summary>
|
||||
public sealed class StudentProgress
|
||||
{
|
||||
/// <summary>
|
||||
/// 体力
|
||||
/// </summary>
|
||||
public StatusValue Stamina { get; set; }
|
||||
/// <summary>
|
||||
/// 忠诚度
|
||||
/// </summary>
|
||||
public StatusValue Loyalty { get; set; }
|
||||
/// <summary>
|
||||
/// 年级
|
||||
/// </summary>
|
||||
public PropertyValue Grade { get; set; }
|
||||
|
||||
public StudentProgress(StudentModel.StudentType type)
|
||||
@ -70,5 +105,8 @@ public sealed class StudentProgress
|
||||
/// </summary>
|
||||
public sealed class StudentContributions
|
||||
{
|
||||
/// <summary>
|
||||
/// 按任务ID索引的贡献值
|
||||
/// </summary>
|
||||
public Dictionary<Guid, PropertyValue> ByTask { get; } = new();
|
||||
}
|
||||
}
|
||||
@ -14,30 +14,62 @@ namespace Models;
|
||||
/// </summary>
|
||||
public sealed class ArchetypeDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// 基础头部信息
|
||||
/// </summary>
|
||||
public DefinitionHeader Header { get; set; } = new();
|
||||
/// <summary>
|
||||
/// 羁绊层级列表
|
||||
/// </summary>
|
||||
public List<SynergyTier> Tiers { get; } = new();
|
||||
}
|
||||
|
||||
public sealed class RoleDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// 基础头部信息
|
||||
/// </summary>
|
||||
public DefinitionHeader Header { get; set; } = new();
|
||||
/// <summary>
|
||||
/// 羁绊层级列表
|
||||
/// </summary>
|
||||
public List<SynergyTier> Tiers { get; } = new();
|
||||
|
||||
// 如果是学科限定角色(如炼金术士/极客),在这里配置允许的学科 Id。
|
||||
/// <summary>
|
||||
/// 允许的学科ID列表
|
||||
/// </summary>
|
||||
public List<string> AllowedDisciplineIds { get; } = new();
|
||||
}
|
||||
|
||||
public sealed class TraitDefinition
|
||||
{
|
||||
/// <summary>
|
||||
/// 基础头部信息
|
||||
/// </summary>
|
||||
public DefinitionHeader Header { get; set; } = new();
|
||||
/// <summary>
|
||||
/// 修正包
|
||||
/// </summary>
|
||||
public ModifierBundle Modifiers { get; set; } = new();
|
||||
/// <summary>
|
||||
/// 规则ID列表
|
||||
/// </summary>
|
||||
public List<string> RuleIds { get; } = new();
|
||||
}
|
||||
|
||||
public sealed class SynergyTier
|
||||
{
|
||||
/// <summary>
|
||||
/// 需求数量
|
||||
/// </summary>
|
||||
public int RequiredCount { get; set; }
|
||||
/// <summary>
|
||||
/// 修正包
|
||||
/// </summary>
|
||||
public ModifierBundle Modifiers { get; set; } = new();
|
||||
/// <summary>
|
||||
/// 规则ID列表
|
||||
/// </summary>
|
||||
public List<string> RuleIds { get; } = new();
|
||||
}
|
||||
|
||||
}
|
||||
@ -77,4 +77,4 @@ public sealed class TaskRewardSnapshot
|
||||
public int Reputation { get; set; }
|
||||
public List<string> PaperIds { get; } = new();
|
||||
public List<string> ItemIds { get; set; }
|
||||
}
|
||||
}
|
||||
@ -33,10 +33,10 @@ public enum TaskKind
|
||||
|
||||
public enum TaskDifficulty
|
||||
{
|
||||
Water,
|
||||
Standard,
|
||||
Hardcore,
|
||||
BlackBox
|
||||
Water, // 水
|
||||
Standard, // 标准
|
||||
Hardcore, // 硬核
|
||||
BlackBox // 黑箱
|
||||
}
|
||||
|
||||
public enum PaperRank
|
||||
@ -97,5 +97,4 @@ public sealed class TaskRewards
|
||||
public List<string> PaperIds { get; } = new();
|
||||
public List<string> ItemIds { get; } = new();
|
||||
public List<string> BuffIds { get; } = new();
|
||||
}
|
||||
|
||||
}
|
||||
@ -18,6 +18,9 @@ namespace Models;
|
||||
/// </summary>
|
||||
public static class UnitComponents
|
||||
{
|
||||
/// <summary>
|
||||
/// 网格位置组件
|
||||
/// </summary>
|
||||
public readonly struct GridPosition
|
||||
{
|
||||
public int X { get; }
|
||||
@ -32,6 +35,9 @@ public static class UnitComponents
|
||||
public static GridPosition Zero => new(0, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 单位身份组件
|
||||
/// </summary>
|
||||
public sealed class UnitIdentity
|
||||
{
|
||||
public Guid Id { get; } = Guid.NewGuid();
|
||||
@ -45,6 +51,9 @@ public static class UnitComponents
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 单位属性组件
|
||||
/// </summary>
|
||||
public sealed class UnitAttributes
|
||||
{
|
||||
public PropertyValue Academic { get; set; }
|
||||
@ -66,6 +75,9 @@ public static class UnitComponents
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 单位状态组件
|
||||
/// </summary>
|
||||
public sealed class UnitStatuses
|
||||
{
|
||||
public StatusValue Stress { get; set; }
|
||||
@ -82,6 +94,9 @@ public static class UnitComponents
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 单位标签组件
|
||||
/// </summary>
|
||||
public sealed class UnitTags
|
||||
{
|
||||
public string DisciplineId { get; set; }
|
||||
@ -92,19 +107,28 @@ public static class UnitComponents
|
||||
public bool HasTrait(string traitId) => TraitIds.Contains(traitId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 单位指派组件
|
||||
/// </summary>
|
||||
public sealed class UnitAssignment
|
||||
{
|
||||
public Guid? CurrentTaskId { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 单位位置组件
|
||||
/// </summary>
|
||||
public sealed class UnitPlacement
|
||||
{
|
||||
public GridPosition Current { get; set; } = GridPosition.Zero;
|
||||
public GridPosition Target { get; set; } = GridPosition.Zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 单位装备组件
|
||||
/// </summary>
|
||||
public sealed class UnitEquipment
|
||||
{
|
||||
public List<string> EquippedItemIds { get; } = new();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -17,20 +17,49 @@ namespace Models;
|
||||
/// </summary>
|
||||
public sealed class UnitModel
|
||||
{
|
||||
/// <summary>
|
||||
/// 身份组件
|
||||
/// </summary>
|
||||
public UnitComponents.UnitIdentity Identity { get; }
|
||||
/// <summary>
|
||||
/// 属性组件
|
||||
/// </summary>
|
||||
public UnitComponents.UnitAttributes Attributes { get; }
|
||||
/// <summary>
|
||||
/// 状态组件
|
||||
/// </summary>
|
||||
public UnitComponents.UnitStatuses Statuses { get; }
|
||||
/// <summary>
|
||||
/// 标签组件
|
||||
/// </summary>
|
||||
public UnitComponents.UnitTags Tags { get; }
|
||||
/// <summary>
|
||||
/// 指派组件
|
||||
/// </summary>
|
||||
public UnitComponents.UnitAssignment Assignment { get; }
|
||||
/// <summary>
|
||||
/// 位置组件
|
||||
/// </summary>
|
||||
public UnitComponents.UnitPlacement Placement { get; }
|
||||
/// <summary>
|
||||
/// 装备组件
|
||||
/// </summary>
|
||||
public UnitComponents.UnitEquipment Equipment { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 名字(便捷访问)
|
||||
/// </summary>
|
||||
public string Name
|
||||
{
|
||||
get => Identity.Name;
|
||||
set => Identity.Name = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="name">名字</param>
|
||||
/// <param name="random">随机数生成器</param>
|
||||
public UnitModel(string name, Random random = null)
|
||||
{
|
||||
Identity = new UnitComponents.UnitIdentity(name);
|
||||
@ -41,4 +70,4 @@ public sealed class UnitModel
|
||||
Placement = new UnitComponents.UnitPlacement();
|
||||
Equipment = new UnitComponents.UnitEquipment();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,9 @@ using Godot;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/// <summary>
|
||||
/// 玩家数据管理类
|
||||
/// </summary>
|
||||
public partial class Player : Node
|
||||
{
|
||||
/// <summary>
|
||||
@ -37,12 +40,23 @@ public partial class Player : Node
|
||||
public int Total => Facility + Operational + Labor;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 时间轴类型
|
||||
/// </summary>
|
||||
public class TimelineType
|
||||
{
|
||||
/// <summary>
|
||||
/// 内部日期
|
||||
/// </summary>
|
||||
public DateOnly InternalDate { get; set; }
|
||||
|
||||
private Dictionary<DateOnly, HashSet<Guid>> _events;
|
||||
|
||||
/// <summary>
|
||||
/// 订阅事件
|
||||
/// </summary>
|
||||
/// <param name="date">日期</param>
|
||||
/// <param name="eventUuid">事件UUID</param>
|
||||
public void Subscribe(DateOnly date, Guid eventUuid)
|
||||
{
|
||||
_events ??= new Dictionary<DateOnly, HashSet<Guid>>();
|
||||
@ -50,6 +64,11 @@ public partial class Player : Node
|
||||
_events[date].Add(eventUuid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 取消订阅事件
|
||||
/// </summary>
|
||||
/// <param name="date">日期</param>
|
||||
/// <param name="eventUuid">事件UUID</param>
|
||||
public void Unsubscribe(DateOnly date, Guid eventUuid)
|
||||
{
|
||||
if (_events == null) return;
|
||||
@ -57,6 +76,9 @@ public partial class Player : Node
|
||||
_events[date].Remove(eventUuid);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 进入下一天
|
||||
/// </summary>
|
||||
private void NextDay()
|
||||
{
|
||||
if (_events == null) return;
|
||||
@ -78,27 +100,53 @@ public partial class Player : Node
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 附加计时器
|
||||
/// </summary>
|
||||
/// <param name="ticker">计时器</param>
|
||||
public void Attach(Timer ticker)
|
||||
{
|
||||
ticker.Timeout += NextDay;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数
|
||||
/// </summary>
|
||||
/// <param name="startDate">开始日期</param>
|
||||
public TimelineType(DateOnly startDate)
|
||||
{
|
||||
InternalDate = startDate;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 事件触发委托
|
||||
/// </summary>
|
||||
public delegate void EventTriggeredEventHandler(DateOnly date, Guid eventUuid);
|
||||
|
||||
/// <summary>
|
||||
/// 事件触发事件
|
||||
/// </summary>
|
||||
public event EventTriggeredEventHandler OnEventTriggered;
|
||||
|
||||
/// <summary>
|
||||
/// 日期变更委托
|
||||
/// </summary>
|
||||
public delegate void DayChangedEventHandler(DateOnly date);
|
||||
|
||||
/// <summary>
|
||||
/// 日期变更事件
|
||||
/// </summary>
|
||||
public event DayChangedEventHandler OnDayChanged;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 预算实例
|
||||
/// </summary>
|
||||
public static BudgetType Budget { get; set; } = new();
|
||||
/// <summary>
|
||||
/// 时间轴实例
|
||||
/// </summary>
|
||||
public static readonly TimelineType Timeline = new(DateOnly.Parse("2024/11/17"));
|
||||
|
||||
/// <summary>
|
||||
@ -117,13 +165,20 @@ public partial class Player : Node
|
||||
public static DateOnly Date { get; set; }
|
||||
|
||||
// Called when the node enters the scene tree for the first time.
|
||||
/// <summary>
|
||||
/// 场景加载完成时调用
|
||||
/// </summary>
|
||||
public override void _Ready()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
// Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
/// <summary>
|
||||
/// 每帧更新
|
||||
/// </summary>
|
||||
/// <param name="delta">帧间隔</param>
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,18 +1,31 @@
|
||||
using Godot;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// 资源管理类
|
||||
/// </summary>
|
||||
public partial class Res : Node
|
||||
{
|
||||
// Called when the node enters the scene tree for the first time.
|
||||
/// <summary>
|
||||
/// 场景加载完成时调用
|
||||
/// </summary>
|
||||
public override void _Ready()
|
||||
{
|
||||
}
|
||||
|
||||
// Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
/// <summary>
|
||||
/// 每帧更新
|
||||
/// </summary>
|
||||
/// <param name="delta">帧间隔</param>
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 资源类型枚举
|
||||
/// </summary>
|
||||
public enum Type
|
||||
{
|
||||
Accessory,
|
||||
@ -23,16 +36,32 @@ public partial class Res : Node
|
||||
Phone
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取随机资源路径
|
||||
/// </summary>
|
||||
/// <param name="resType">资源类型</param>
|
||||
/// <returns>资源路径</returns>
|
||||
public static string GetRandom(Type resType)
|
||||
{
|
||||
return GetRandom(resType, false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 转换为16x16精灵路径
|
||||
/// </summary>
|
||||
/// <param name="path">原路径</param>
|
||||
/// <returns>16x16路径</returns>
|
||||
private static string To16Path(string path)
|
||||
{
|
||||
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)
|
||||
{
|
||||
var resources = allResources[(int)resType];
|
||||
@ -48,6 +77,9 @@ public partial class Res : Node
|
||||
return ResourceLoader.Exists(path16) ? path16 : path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 身体资源列表
|
||||
/// </summary>
|
||||
public static readonly string[] Body =
|
||||
[
|
||||
"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"
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// 饰品资源列表
|
||||
/// </summary>
|
||||
public static readonly string[] Accessory =
|
||||
[
|
||||
"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"
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// 眼睛资源列表
|
||||
/// </summary>
|
||||
public static readonly string[] Eye =
|
||||
[
|
||||
"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"
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// 发型资源列表
|
||||
/// </summary>
|
||||
public static readonly string[] Hair =
|
||||
[
|
||||
"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"
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// 服装资源列表
|
||||
/// </summary>
|
||||
public static readonly string[] Outfit =
|
||||
[
|
||||
"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"
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// 手机资源列表
|
||||
/// </summary>
|
||||
public static readonly string[] Smartphone =
|
||||
[
|
||||
"res://resources/characters/smartphones/Smartphone_48x48_1.png",
|
||||
@ -515,4 +562,4 @@ public partial class Res : Node
|
||||
Smartphone
|
||||
];
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,15 @@
|
||||
using Godot;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// 场景过渡控制器
|
||||
/// </summary>
|
||||
public partial class SceneTransit : CanvasLayer
|
||||
{
|
||||
// Called when the node enters the scene tree for the first time.
|
||||
/// <summary>
|
||||
/// 场景加载完成时调用
|
||||
/// </summary>
|
||||
public override void _Ready()
|
||||
{
|
||||
Layer = -1;
|
||||
@ -11,10 +17,21 @@ public partial class SceneTransit : CanvasLayer
|
||||
}
|
||||
|
||||
// Called every frame. 'delta' is the elapsed time since the previous frame.
|
||||
/// <summary>
|
||||
/// 每帧更新
|
||||
/// </summary>
|
||||
/// <param name="delta">帧间隔</param>
|
||||
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)
|
||||
{
|
||||
SetProcess(true);
|
||||
@ -39,4 +56,4 @@ public partial class SceneTransit : CanvasLayer
|
||||
SetProcess(false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -5,15 +5,34 @@ using System.Linq;
|
||||
using Models;
|
||||
// ReSharper disable CheckNamespace
|
||||
|
||||
/// <summary>
|
||||
/// 学生角色控制器
|
||||
/// </summary>
|
||||
public partial class Student : CharacterBody2D
|
||||
{
|
||||
/// <summary>
|
||||
/// 移动速度
|
||||
/// </summary>
|
||||
public float Speed { get; set; } = 8.0f;
|
||||
/// <summary>
|
||||
/// 跳跃速度
|
||||
/// </summary>
|
||||
public const float JumpVelocity = -400.0f;
|
||||
/// <summary>
|
||||
/// 是否使用16x16精灵
|
||||
/// </summary>
|
||||
[Export] public bool Use16X16Sprites { get; set; }
|
||||
|
||||
// --- MVP: Model Binding ---
|
||||
/// <summary>
|
||||
/// 学生数据模型
|
||||
/// </summary>
|
||||
public StudentModel Model { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 绑定数据模型
|
||||
/// </summary>
|
||||
/// <param name="model">学生模型</param>
|
||||
public void BindData(StudentModel model)
|
||||
{
|
||||
Model = model;
|
||||
@ -21,15 +40,35 @@ public partial class Student : CharacterBody2D
|
||||
}
|
||||
// --------------------------
|
||||
|
||||
/// <summary>
|
||||
/// 下一个状态类型(未使用)
|
||||
/// </summary>
|
||||
public int NextType = -1;
|
||||
|
||||
private Queue<Vector2I> _pathToGo = new();
|
||||
private AnimationPlayer _animationPlayer;
|
||||
|
||||
/// <summary>
|
||||
/// 角色状态枚举
|
||||
/// </summary>
|
||||
public enum CharacterState { Idle, Walking, Sitting, SittingDown, StandingUp }
|
||||
/// <summary>
|
||||
/// 当前状态
|
||||
/// </summary>
|
||||
public CharacterState State { get; set; } = CharacterState.Idle;
|
||||
|
||||
/// <summary>
|
||||
/// 方向枚举
|
||||
/// </summary>
|
||||
public enum Direction { Up, Down, Left, Right }
|
||||
/// <summary>
|
||||
/// 目标方向
|
||||
/// </summary>
|
||||
public Direction TargetDirection { get; set; } = Direction.Up;
|
||||
|
||||
/// <summary>
|
||||
/// 状态队列
|
||||
/// </summary>
|
||||
public Queue<CharacterState> StateQueue { get; set; } = new();
|
||||
|
||||
private Vector2I _targetSpecialPosition;
|
||||
@ -54,6 +93,11 @@ public partial class Student : CharacterBody2D
|
||||
State = StateQueue.Dequeue();
|
||||
GlobalPosition = _lastWalkablePosition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 物理处理
|
||||
/// </summary>
|
||||
/// <param name="delta">时间间隔</param>
|
||||
public override void _PhysicsProcess(double delta)
|
||||
{
|
||||
if (StateQueue.Count > 0) {
|
||||
@ -141,6 +185,9 @@ public partial class Student : CharacterBody2D
|
||||
// MoveAndSlide();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 准备就绪时调用
|
||||
/// </summary>
|
||||
public override void _Ready()
|
||||
{
|
||||
base._Ready();
|
||||
@ -163,6 +210,10 @@ public partial class Student : CharacterBody2D
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移动跟随路径
|
||||
/// </summary>
|
||||
/// <param name="path">路径点列表</param>
|
||||
public void MoveFollowPath(List<Vector2I> path)
|
||||
{
|
||||
foreach (var p in path)
|
||||
@ -171,8 +222,14 @@ public partial class Student : CharacterBody2D
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 关联的格子ID
|
||||
/// </summary>
|
||||
public Guid CubeId;
|
||||
|
||||
/// <summary>
|
||||
/// 随机前往某处
|
||||
/// </summary>
|
||||
public void GoTo() {
|
||||
if (State == CharacterState.SittingDown || State == CharacterState.StandingUp || State == CharacterState.Walking)
|
||||
{
|
||||
@ -201,6 +258,9 @@ public partial class Student : CharacterBody2D
|
||||
RandomChangeBody();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 前往座位
|
||||
/// </summary>
|
||||
public void GoToSeat() {
|
||||
if (State == CharacterState.SittingDown || State == CharacterState.StandingUp || State == CharacterState.Walking)
|
||||
{
|
||||
@ -248,6 +308,9 @@ public partial class Student : CharacterBody2D
|
||||
RandomChangeBody();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 随机更换外观
|
||||
/// </summary>
|
||||
private void RandomChangeBody()
|
||||
{
|
||||
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/smartphone").Texture = ResourceLoader.Load<Texture2D>(Res.GetRandom(Res.Type.Phone, Use16X16Sprites));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,20 +2,33 @@ using Godot;
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
/// <summary>
|
||||
/// 学生姓名生成器
|
||||
/// </summary>
|
||||
public partial class StudentName : Node
|
||||
{
|
||||
// Called when the node enters the scene tree for the first time.
|
||||
/// <summary>
|
||||
/// 场景加载完成时调用
|
||||
/// </summary>
|
||||
public override void _Ready()
|
||||
{
|
||||
}
|
||||
|
||||
// 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 string GenerateName()
|
||||
{
|
||||
string[] 姓氏 = { "大", "小", "飞", "傻", "呆", "雷", "东", "西", "王", "赵", "李", "张", "刘", "周" ,"赵", "钱", "孙", "李", "周", "吴", "郑", "王", "冯", "陈", "褚", "卫", "蒋", "沈", "韩", "杨", "朱", "秦", "尤", "许", "何", "吕", "施", "张", "孔", "曹", "严", "华", "金", "魏", "陶", "姜", "戚", "谢", "邹", "喻", "柏", "水", "窦", "章", "云", "苏", "潘", "葛", "奚", "范", "彭", "郎", "鲁", "韦", "昌", "马", "苗", "凤", "花", "方", "俞", "任", "袁", "柳", "酆", "鲍", "史", "唐", "费", "廉", "岑", "薛", "雷", "贺", "倪", "汤", "滕", "殷", "罗", "毕", "郝", "邬", "安", "常", "乐", "于", "时", "傅", "皮", "卞", "齐", "康", "伍", "余", "元", "卜", "顾", "孟", "平", "黄", "和", "穆", "萧", "尹", "姚", "邵", "湛", "汪", "祁", "毛", "禹", "狄", "米", "贝", "明", "臧", "计", "伏", "成", "戴", "谈", "宋", "茅", "庞", "熊", "纪", "舒", "屈", "项", "祝", "董", "梁", "杜", "阮", "蓝", "闵", "席", "季", "麻", "强", "贾", "路", "娄", "危", "江", "童", "颜", "郭", "梅", "盛", "林", "刁", "钟", "徐", "邱", "骆", "高", "夏", "蔡", "田", "樊", "胡", "凌", "霍", "虞", "万", "支", "柯", "昝", "管", "卢", "莫", "经", "房", "裘", "缪", "干", "解", "应", "宗", "丁", "宣", "贲", "邓", "郁", "单", "杭", "洪", "包", "诸", "左", "石", "崔", "吉", "钮", "龚", "程", "嵇", "邢", "滑", "裴", "陆", "荣", "翁", "荀", "羊", "於", "惠", "甄", "麴", "家", "封", "芮", "羿", "储", "靳", "汲", "邴", "糜", "松", "井", "段", "富", "巫", "乌", "焦", "巴", "弓", "牧", "隗", "山", "谷", "车", "侯", "宓", "蓬", "全", "郗", "班", "仰", "秋", "仲", "伊", "宫", "宁", "仇", "栾", "暴", "甘", "钭", "厉", "戎", "祖", "武", "符", "刘", "景", "詹", "束", "龙", "叶", "幸", "司", "韶", "郜", "黎", "蓟", "薄", "印", "宿", "白", "怀", "蒲", "邰", "从", "鄂", "索", "咸", "籍", "赖", "卓", "蔺", "屠", "蒙", "池", "乔", "阴", "欎", "胥", "能", "苍", "双", "闻", "莘", "党", "翟", "谭", "贡", "劳", "逄", "姬", "申", "扶", "堵", "冉", "宰", "郦", "雍", "舄", "璩", "桑", "桂", "濮", "牛", "寿", "通", "边", "扈", "燕", "冀", "郏", "浦", "尚", "农", "温", "别", "庄", "晏", "柴", "瞿", "阎", "充", "慕", "连", "茹", "习", "宦", "艾", "鱼", "容", "向", "古", "易", "慎", "戈", "廖", "庾", "终", "暨", "居", "衡", "步", "都", "耿", "满", "弘", "匡", "国", "文", "寇", "广", "禄", "阙", "东", "殴", "殳", "沃", "利", "蔚", "越", "夔", "隆", "师", "巩", "厍", "聂", "晁", "勾", "敖", "融", "冷", "訾", "辛", "阚", "那", "简", "饶", "空", "曾", "毋", "沙", "乜", "养", "鞠", "须", "丰", "巢", "关", "蒯", "相", "查", "後", "荆", "红", "游", "竺", "权", "逯", "盖", "益", "桓", "公", "万俟", "司马", "上官", "欧阳", "夏侯", "诸葛", "闻人", "东方", "赫连", "皇甫", "尉迟", "公羊", "澹台", "公冶", "宗政", "濮阳", "淳于", "单于", "太叔", "申屠", "公孙", "仲孙", "轩辕", "令狐", "钟离", "宇文", "长孙", "慕容", "鲜于", "闾丘", "司徒", "司空", "亓官", "司寇", "仉", "督", "子车", "颛孙", "端木", "巫马", "公西", "漆雕", "乐正", "壤驷", "公良", "拓跋", "夹谷", "宰父", "谷梁", "晋", "楚", "闫", "法", "汝", "鄢", "涂", "钦", "段干", "百里", "东郭", "南门", "呼延", "归", "海", "羊舌", "微生", "岳", "帅", "缑", "亢", "况", "后", "有", "琴", "梁丘", "左丘", "东门", "西门", "商", "牟", "佘", "佴", "伯", "赏", "南宫", "墨", "哈", "谯", "笪", "年", "爱", "阳", "佟", "第五", "言", "福", "百", "家", "姓", "终"};
|
||||
/// <summary>
|
||||
/// 生成随机姓名
|
||||
/// </summary>
|
||||
/// <returns>随机姓名</returns>
|
||||
public string GenerateName()
|
||||
{ string[] 姓氏 = { "大", "小", "飞", "傻", "呆", "雷", "东", "西", "王", "赵", "李", "张", "刘", "周" ,"赵", "钱", "孙", "李", "周", "吴", "郑", "王", "冯", "陈", "褚", "卫", "蒋", "沈", "韩", "杨", "朱", "秦", "尤", "许", "何", "吕", "施", "张", "孔", "曹", "严", "华", "金", "魏", "陶", "姜", "戚", "谢", "邹", "喻", "柏", "水", "窦", "章", "云", "苏", "潘", "葛", "奚", "范", "彭", "郎", "鲁", "韦", "昌", "马", "苗", "凤", "花", "方", "俞", "任", "袁", "柳", "酆", "鲍", "史", "唐", "费", "廉", "岑", "薛", "雷", "贺", "倪", "汤", "滕", "殷", "罗", "毕", "郝", "邬", "安", "常", "乐", "于", "时", "傅", "皮", "卞", "齐", "康", "伍", "余", "元", "卜", "顾", "孟", "平", "黄", "和", "穆", "萧", "尹", "姚", "邵", "湛", "汪", "祁", "毛", "禹", "狄", "米", "贝", "明", "臧", "计", "伏", "成", "戴", "谈", "宋", "茅", "庞", "熊", "纪", "舒", "屈", "项", "祝", "董", "梁", "杜", "阮", "蓝", "闵", "席", "季", "麻", "强", "贾", "路", "娄", "危", "江", "童", "颜", "郭", "梅", "盛", "林", "刁", "钟", "徐", "邱", "骆", "高", "夏", "蔡", "田", "樊", "胡", "凌", "霍", "虞", "万", "支", "柯", "昝", "管", "卢", "莫", "经", "房", "裘", "缪", "干", "解", "应", "宗", "丁", "宣", "贲", "邓", "郁", "单", "杭", "洪", "包", "诸", "左", "石", "崔", "吉", "钮", "龚", "程", "嵇", "邢", "滑", "裴", "陆", "荣", "翁", "荀", "羊", "於", "惠", "甄", "麴", "家", "封", "芮", "羿", "储", "靳", "汲", "邴", "糜", "松", "井", "段", "富", "巫", "乌", "焦", "巴", "弓", "牧", "隗", "山", "谷", "车", "侯", "宓", "蓬", "全", "郗", "班", "仰", "秋", "仲", "伊", "宫", "宁", "仇", "栾", "暴", "甘", "钭", "厉", "戎", "祖", "武", "符", "刘", "景", "詹", "束", "龙", "叶", "幸", "司", "韶", "郜", "黎", "蓟", "薄", "印", "宿", "白", "怀", "蒲", "邰", "从", "鄂", "索", "咸", "籍", "赖", "卓", "蔺", "屠", "蒙", "池", "乔", "阴", "欎", "胥", "能", "苍", "双", "闻", "莘", "党", "翟", "谭", "贡", "劳", "逄", "姬", "申", "扶", "堵", "冉", "宰", "郦", "雍", "舄", "璩", "桑", "桂", "濮", "牛", "寿", "通", "边", "扈", "燕", "冀", "郏", "浦", "尚", "农", "温", "别", "庄", "晏", "柴", "瞿", "阎", "充", "慕", "连", "茹", "习", "宦", "艾", "鱼", "容", "向", "古", "易", "慎", "戈", "廖", "庾", "终", "暨", "居", "衡", "步", "都", "耿", "满", "弘", "匡", "国", "文", "寇", "广", "禄", "阙", "东", "殴", "殳", "沃", "利", "蔚", "越", "夔", "隆", "师", "巩", "厍", "聂", "晁", "勾", "敖", "融", "冷", "訾", "辛", "阚", "那", "简", "饶", "空", "曾", "毋", "沙", "乜", "养", "鞠", "须", "丰", "巢", "关", "蒯", "相", "查", "後", "荆", "红", "游", "竺", "权", "逯", "盖", "益", "桓", "公", "万俟", "司马", "上官", "欧阳", "夏侯", "诸葛", "闻人", "东方", "赫连", "皇甫", "尉迟", "公羊", "澹台", "公冶", "宗政", "濮阳", "淳于", "单于", "太叔", "申屠", "公孙", "仲孙", "轩辕", "令狐", "钟离", "宇文", "长孙", "慕容", "鲜于", "闾丘", "司徒", "司空", "亓官", "司寇", "仉", "督", "子车", "颛孙", "端木", "巫马", "公西", "漆雕", "乐正", "壤驷", "公良", "拓跋", "夹谷", "宰父", "谷梁", "晋", "楚", "闫", "法", "汝", "鄢", "涂", "钦", "段干", "百里", "东郭", "南门", "呼延", "归", "海", "羊舌", "微生", "岳", "帅", "缑", "亢", "况", "后", "有", "琴", "梁丘", "左丘", "东门", "西门", "商", "牟", "佘", "佴", "伯", "赏", "南宫", "墨", "哈", "谯", "笪", "年", "爱", "阳", "佟", "第五", "言", "福", "百", "家", "姓", "终"};
|
||||
string[] 名字前缀 = { "金", "猪", "兔", "猫", "鱼", "蛋", "胖", "大", "傻", "酷", "蠢", "聪", "萌", "暴","靖", "铭", "琛", "川", "承", "司", "斯", "宗", "骁", "聪", "在", "钩", "锦", "铎", "楚", "铮", "钦", "则", "楠", "景", "茗", "聿", "启", "尧", "言", "嘉", "桉", "桐", "筒", "竹", "林", "乔", "栋", "家", "翊", "松", "清", "澈", "泫", "浚", "润", "泽", "向", "凡", "文", "浦", "洲", "珩", "玄", "洋", "淮", "雨", "子", "云", "卓", "昱", "南", "晨", "知", "宁", "年", "易", "晗", "炎", "焕", "哲", "煦", "旭", "明", "阳", "朗", "典", "辰", "宸", "野", "安", "为", "亦", "岚", "也", "围", "以", "延", "允", "容", "恩", "衡", "宇", "硕", "已", "意", "也", "坤", "辰", "伊", "米", "安", "恩", "以", "容", "宛", "岚", "又", "衣", "亚", "悠", "允", "画", "灿", "夏", "珞", "煊", "晴", "彤", "诺", "宁", "恬", "钧", "灵", "昭", "琉", "晨", "曦", "南", "毓", "冉", "妍", "澜", "淇", "沐", "潆", "盈", "雨", "文", "冰", "雯", "溪", "子", "云", "汐", "潞", "淇", "妙", "涵", "楠", "景", "茗", "聿", "启", "尧", "言", "嘉", "桉", "桐", "筒", "竹", "林", "乔", "栋", "家", "翊", "松", "真", "心", "新", "悦", "西", "兮", "楚", "初", "千", "锐", "素", "锦", "静", "镜", "斯", "舒", "瑜", "童" };
|
||||
string[] 名字后缀 = { "子", "郎", "妹", "宝", "儿", "汉", "君", "爷", "娃", "猪", "鬼", "鸟", "仔", "蛋","靖", "铭", "琛", "川", "承", "司", "斯", "宗", "骁", "聪", "在", "钩", "锦", "铎", "楚", "铮", "钦", "则", "楠", "景", "茗", "聿", "启", "尧", "言", "嘉", "桉", "桐", "筒", "竹", "林", "乔", "栋", "家", "翊", "松", "清", "澈", "泫", "浚", "润", "泽", "向", "凡", "文", "浦", "洲", "珩", "玄", "洋", "淮", "雨", "子", "云", "卓", "昱", "南", "晨", "知", "宁", "年", "易", "晗", "炎", "焕", "哲", "煦", "旭", "明", "阳", "朗", "典", "辰", "宸", "野", "安", "为", "亦", "岚", "也", "围", "以", "延", "允", "容", "恩", "衡", "宇", "硕", "已", "意", "也", "坤", "辰", "伊", "米", "安", "恩", "以", "容", "宛", "岚", "又", "衣", "亚", "悠", "允", "画", "灿", "夏", "珞", "煊", "晴", "彤", "诺", "宁", "恬", "钧", "灵", "昭", "琉", "晨", "曦", "南", "毓", "冉", "妍", "澜", "淇", "沐", "潆", "盈", "雨", "文", "冰", "雯", "溪", "子", "云", "汐", "潞", "淇", "妙", "涵", "楠", "景", "茗", "聿", "启", "尧", "言", "嘉", "桉", "桐", "筒", "竹", "林", "乔", "栋", "家", "翊", "松", "真", "心", "新", "悦", "西", "兮", "楚", "初", "千", "锐", "素", "锦", "静", "镜", "斯", "舒", "瑜", "童" };
|
||||
|
||||
|
||||
@ -1,26 +1,32 @@
|
||||
using Godot;
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 测试地图层
|
||||
/// </summary>
|
||||
public partial class TestMap : TileMapLayer
|
||||
{
|
||||
/// <summary>
|
||||
/// The last cell that was highlighted. Used to avoid unnecessary updates.
|
||||
/// 上一次高亮的单元格。用于避免不必要的更新。
|
||||
/// </summary>
|
||||
private Vector2I _lastHighlightCell;
|
||||
private readonly Vector2I _highlightTileCoord = new(0, 5);
|
||||
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()
|
||||
{
|
||||
// Initialize the lastHighlightCell to an invalid value.
|
||||
// 初始化最后高亮单元格为无效值。
|
||||
_lastHighlightCell = _vector2INegOne;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called every frame.
|
||||
/// 每帧更新。
|
||||
/// </summary>
|
||||
/// <param name="delta">the elapsed time since the previous frame.</param>
|
||||
/// <param name="delta">距离上一帧的时间间隔。</param>
|
||||
public override void _Process(double delta)
|
||||
{
|
||||
Vector2 mousePos = GetLocalMousePosition();
|
||||
@ -36,5 +42,4 @@ public partial class TestMap : TileMapLayer
|
||||
SetCell(cell, 0, _highlightTileCoord);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -3,23 +3,39 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
/// <summary>
|
||||
/// 图块映射辅助类
|
||||
/// </summary>
|
||||
public partial class TileMapping : Node
|
||||
{
|
||||
/// <summary>
|
||||
/// From tile coordinates to pixel coordinates. If null, use the offset instead.
|
||||
/// 从图块坐标到像素坐标的映射。如果不为空,则使用此映射。
|
||||
/// </summary>
|
||||
private Dictionary<Vector2I, Vector2I> _pixelMap;
|
||||
/// <summary>
|
||||
/// If pixelMap is null, use this offset to convert tile coordinates to pixel coordinates.
|
||||
/// 如果 pixelMap 为空,使用此偏移量将图块坐标转换为像素坐标。
|
||||
/// </summary>
|
||||
private Vector2I _pixelOffset;
|
||||
|
||||
/// <summary>
|
||||
/// 图块区域
|
||||
/// </summary>
|
||||
private Rect2I _tileRect;
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数,使用坐标映射字典
|
||||
/// </summary>
|
||||
/// <param name="map">坐标映射字典</param>
|
||||
public TileMapping(Dictionary<Vector2I, Vector2I> map) {
|
||||
_pixelMap = map;
|
||||
_pixelOffset = new Vector2I(-int.MaxValue, -int.MaxValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构造函数,使用偏移量和区域
|
||||
/// </summary>
|
||||
/// <param name="offset">偏移量</param>
|
||||
/// <param name="rect">区域</param>
|
||||
public TileMapping(Vector2I offset, Rect2I rect) {
|
||||
_pixelMap = null;
|
||||
_pixelOffset = offset;
|
||||
@ -28,6 +44,10 @@ public partial class TileMapping : Node
|
||||
|
||||
public TileMapping() {}
|
||||
|
||||
/// <summary>
|
||||
/// 应用映射到TileMapLayer
|
||||
/// </summary>
|
||||
/// <param name="layer">TileMapLayer</param>
|
||||
public void Apply(TileMapLayer layer) {
|
||||
// if pixelMap is not null, use all the pairs in pixelMap to set the tiles.
|
||||
if (_pixelMap != null) {
|
||||
@ -46,4 +66,4 @@ public partial class TileMapping : Node
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user