Files

77 lines
3.3 KiB
C#
Raw Permalink Normal View History

using Theriapolis.Core.Rules.Stats;
namespace Theriapolis.Core.Rules.Character;
/// <summary>
/// Pure data describing the *deltas* a level-up produces. Phase 6.5 M0:
/// <see cref="LevelUpFlow"/> computes one of these from
/// <c>(character, targetLevel, levelUpSeed)</c>; the player confirms; then
/// <see cref="Character.ApplyLevelUp"/> applies it.
///
/// Splitting compute from apply keeps the level-up screen previewable
/// (the player sees the rolled HP and feature list before committing) and
/// makes mid-flight save/load deterministic — the same seed always produces
/// the same payload.
/// </summary>
public sealed class LevelUpResult
{
/// <summary>The level being advanced *to*. After Apply, <c>Character.Level == NewLevel</c>.</summary>
public int NewLevel { get; init; }
/// <summary>HP gained on this level-up. Already incorporates CON modifier.</summary>
public int HpGained { get; init; }
/// <summary>Average-rounded-up HP value used (for "take average" path); rolled value used otherwise.</summary>
public int HpHitDieResult { get; init; }
/// <summary>True if the player picked the "take average" option; false if rolled.</summary>
public bool HpWasAveraged { get; init; }
/// <summary>Class feature ids unlocked at this level (per <see cref="Data.ClassDef.LevelTable"/>).</summary>
public string[] ClassFeaturesUnlocked { get; init; } = Array.Empty<string>();
/// <summary>
/// Subclass feature ids unlocked at this level (post-L3, when SubclassId
/// is set). Empty for pre-subclass and non-subclass-feature levels.
/// </summary>
public string[] SubclassFeaturesUnlocked { get; init; } = Array.Empty<string>();
/// <summary>True if this level grants a subclass selection slot (level 3 by default).</summary>
public bool GrantsSubclassChoice { get; init; }
/// <summary>True if this level grants an Ability Score Improvement choice (levels 4 / 8 / 12 / 16 / 19).</summary>
public bool GrantsAsiChoice { get; init; }
/// <summary>The proficiency bonus *after* this level-up.</summary>
public int NewProficiencyBonus { get; init; }
}
/// <summary>
/// The player's choices at level-up that need confirmation before
/// <see cref="Character.ApplyLevelUp"/> commits the deltas. Leave fields
/// null/empty when the corresponding slot isn't open at this level.
/// </summary>
public sealed class LevelUpChoices
{
/// <summary>
/// At level 3 (or whatever <see cref="C.SUBCLASS_SELECTION_LEVEL"/>
/// becomes), the player picks a subclass id. Must reference one of
/// <c>character.ClassDef.SubclassIds</c>.
/// </summary>
public string? SubclassId { get; set; }
/// <summary>
/// At ASI levels (<see cref="C.ASI_LEVELS"/>), the player picks ability
/// score improvements. Either:
/// - one ability +2 (cap at <see cref="C.ABILITY_SCORE_CAP_PRE_L20"/>)
/// - two abilities +1 each (each cap at <see cref="C.ABILITY_SCORE_CAP_PRE_L20"/>)
/// </summary>
public Dictionary<AbilityId, int> AsiAdjustments { get; set; } = new();
/// <summary>
/// True if the player picked "take average HP" instead of "roll".
/// Default is "average" — predictable, avoids the dump-stat-roll problem.
/// </summary>
public bool TakeAverageHp { get; set; } = true;
}