Add ruleset-aware Rolemaster editors

This commit is contained in:
2026-04-03 01:11:10 +02:00
parent 960197354a
commit 61ea310179
11 changed files with 486 additions and 31 deletions

View File

@@ -1,4 +1,5 @@
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using Microsoft.AspNetCore.Components;
using RpgRoller.Contracts;
@@ -16,13 +17,19 @@ public partial class CharacterPanel
CreateSkillInitialModel = new()
{
Name = string.Empty,
RulesetId = SelectedCampaignRulesetId,
DiceRollDefinition = selectedGroup?.DiceRollDefinition ?? string.Empty,
SkillGroupId = selectedGroup?.Id.ToString() ?? string.Empty,
WildDice = selectedGroup?.WildDice ?? (IsD6 ? 1 : 0),
AllowFumble = selectedGroup?.AllowFumble ?? IsD6,
FumbleRange = selectedGroup?.FumbleRange
WildDice = selectedGroup?.WildDice ?? (IsD6Ruleset ? 1 : 0),
AllowFumble = selectedGroup?.AllowFumble ?? IsD6Ruleset,
FumbleRange = selectedGroup?.FumbleRange,
RolemasterRollType = RulesetFormHelpers.InferRolemasterRollType(selectedGroup?.DiceRollDefinition),
RolemasterModifier = RulesetFormHelpers.InferRolemasterModifier(selectedGroup?.DiceRollDefinition)
};
if (IsRolemasterRuleset && string.IsNullOrWhiteSpace(CreateSkillInitialModel.DiceRollDefinition))
CreateSkillInitialModel.DiceRollDefinition = RulesetFormHelpers.BuildRolemasterExpression(CreateSkillInitialModel.RolemasterRollType, CreateSkillInitialModel.RolemasterModifier);
CreateSkillFormVersion++;
ShowCreateSkillModal = true;
}
@@ -33,11 +40,14 @@ public partial class CharacterPanel
EditSkillInitialModel = new()
{
Name = skill.Name,
RulesetId = SelectedCampaignRulesetId,
DiceRollDefinition = skill.DiceRollDefinition,
SkillGroupId = skill.SkillGroupId?.ToString() ?? string.Empty,
WildDice = skill.WildDice,
AllowFumble = skill.AllowFumble,
FumbleRange = skill.FumbleRange
FumbleRange = skill.FumbleRange,
RolemasterRollType = RulesetFormHelpers.InferRolemasterRollType(skill.DiceRollDefinition),
RolemasterModifier = RulesetFormHelpers.InferRolemasterModifier(skill.DiceRollDefinition)
};
EditSkillFormVersion++;
@@ -98,10 +108,15 @@ public partial class CharacterPanel
private void OpenCreateSkillGroupModal()
{
SkillGroupState.Model.Name = string.Empty;
SkillGroupState.Model.RulesetId = SelectedCampaignRulesetId;
SkillGroupState.Model.DiceRollDefinition = string.Empty;
SkillGroupState.Model.WildDice = IsD6 ? 1 : 0;
SkillGroupState.Model.AllowFumble = IsD6;
SkillGroupState.Model.WildDice = IsD6Ruleset ? 1 : 0;
SkillGroupState.Model.AllowFumble = IsD6Ruleset;
SkillGroupState.Model.FumbleRange = null;
SkillGroupState.Model.RolemasterRollType = RulesetFormHelpers.RolemasterRollTypes.Initiative;
SkillGroupState.Model.RolemasterModifier = 0;
if (IsRolemasterRuleset)
SynchronizeSkillGroupExpression();
SkillGroupState.ResetValidation();
ShowCreateSkillGroupModal = true;
}
@@ -110,10 +125,14 @@ public partial class CharacterPanel
{
EditingSkillGroupId = skillGroup.Id;
SkillGroupState.Model.Name = skillGroup.Name;
SkillGroupState.Model.RulesetId = SelectedCampaignRulesetId;
SkillGroupState.Model.DiceRollDefinition = skillGroup.DiceRollDefinition;
SkillGroupState.Model.WildDice = skillGroup.WildDice;
SkillGroupState.Model.AllowFumble = skillGroup.AllowFumble;
SkillGroupState.Model.FumbleRange = skillGroup.FumbleRange;
SkillGroupState.Model.RolemasterRollType = RulesetFormHelpers.InferRolemasterRollType(skillGroup.DiceRollDefinition);
SkillGroupState.Model.RolemasterModifier = RulesetFormHelpers.InferRolemasterModifier(skillGroup.DiceRollDefinition);
NormalizeSkillGroupFumbleRange();
SkillGroupState.ResetValidation();
ShowEditSkillGroupModal = true;
}
@@ -136,9 +155,25 @@ public partial class CharacterPanel
if (string.IsNullOrWhiteSpace(SkillGroupState.Model.DiceRollDefinition))
SkillGroupState.Errors["diceRollDefinition"] = "Expression is required.";
if (IsD6 && SkillGroupState.Model.WildDice < 1)
if (IsD6Ruleset && SkillGroupState.Model.WildDice < 1)
SkillGroupState.Errors["wildDice"] = "D6 groups require at least one wild die.";
if (IsRolemasterRuleset)
{
if (IsSkillGroupRolemasterOpenEnded && !SkillGroupState.Model.FumbleRange.HasValue)
SkillGroupState.Errors["fumbleRange"] = "Open-ended Rolemaster groups require a fumble range.";
}
else
{
SkillGroupState.Model.FumbleRange = null;
}
if (!IsD6Ruleset)
{
SkillGroupState.Model.WildDice = 0;
SkillGroupState.Model.AllowFumble = false;
}
if (!SelectedCharacterId.HasValue)
SkillGroupState.Errors["character"] = "Select a character first.";
@@ -184,9 +219,25 @@ public partial class CharacterPanel
if (string.IsNullOrWhiteSpace(SkillGroupState.Model.DiceRollDefinition))
SkillGroupState.Errors["diceRollDefinition"] = "Expression is required.";
if (IsD6 && SkillGroupState.Model.WildDice < 1)
if (IsD6Ruleset && SkillGroupState.Model.WildDice < 1)
SkillGroupState.Errors["wildDice"] = "D6 groups require at least one wild die.";
if (IsRolemasterRuleset)
{
if (IsSkillGroupRolemasterOpenEnded && !SkillGroupState.Model.FumbleRange.HasValue)
SkillGroupState.Errors["fumbleRange"] = "Open-ended Rolemaster groups require a fumble range.";
}
else
{
SkillGroupState.Model.FumbleRange = null;
}
if (!IsD6Ruleset)
{
SkillGroupState.Model.WildDice = 0;
SkillGroupState.Model.AllowFumble = false;
}
if (!EditingSkillGroupId.HasValue)
SkillGroupState.Errors["group"] = "Select a skill group first.";
@@ -270,6 +321,66 @@ public partial class CharacterPanel
return string.Concat(words[0][0], words[1][0]).ToUpperInvariant();
}
private void OnSkillGroupExpressionChanged(ChangeEventArgs args)
{
SkillGroupState.Model.DiceRollDefinition = args.Value?.ToString() ?? string.Empty;
if (IsRolemasterRuleset)
{
SkillGroupState.Model.RolemasterRollType = RulesetFormHelpers.InferRolemasterRollType(SkillGroupState.Model.DiceRollDefinition);
SkillGroupState.Model.RolemasterModifier = RulesetFormHelpers.InferRolemasterModifier(SkillGroupState.Model.DiceRollDefinition);
NormalizeSkillGroupFumbleRange();
}
}
private void OnSkillGroupRolemasterRollTypeChanged(ChangeEventArgs args)
{
SkillGroupState.Model.RolemasterRollType = args.Value?.ToString() ?? RulesetFormHelpers.RolemasterRollTypes.Initiative;
NormalizeSkillGroupFumbleRange();
SynchronizeSkillGroupExpression();
}
private void OnSkillGroupRolemasterModifierChanged(ChangeEventArgs args)
{
var rawValue = args.Value?.ToString();
if (!int.TryParse(rawValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out var modifier))
modifier = 0;
SkillGroupState.Model.RolemasterModifier = modifier;
SynchronizeSkillGroupExpression();
}
private void SynchronizeSkillGroupExpression()
{
if (!IsRolemasterRuleset)
return;
SkillGroupState.Model.DiceRollDefinition = RulesetFormHelpers.BuildRolemasterExpression(SkillGroupState.Model.RolemasterRollType, SkillGroupState.Model.RolemasterModifier);
}
private void NormalizeSkillGroupFumbleRange()
{
if (!IsRolemasterRuleset)
{
SkillGroupState.Model.FumbleRange = null;
return;
}
if (IsSkillGroupRolemasterOpenEnded)
{
SkillGroupState.Model.FumbleRange ??= 5;
return;
}
SkillGroupState.Model.FumbleRange = null;
}
private bool IsD6Ruleset => RulesetFormHelpers.IsD6(SelectedCampaignRulesetId);
private bool IsRolemasterRuleset => RulesetFormHelpers.IsRolemaster(SelectedCampaignRulesetId);
private bool IsSkillGroupRolemasterOpenEnded => string.Equals(SkillGroupState.Model.RolemasterRollType, RulesetFormHelpers.RolemasterRollTypes.OpenEndedPercentile, StringComparison.OrdinalIgnoreCase);
private string SkillGroupExpressionHelpText => IsRolemasterRuleset
? $"{RulesetFormHelpers.RolemasterExampleText(SkillGroupState.Model.RolemasterRollType)}. Negative modifiers are allowed."
: "Enter the default expression for skills created in this group.";
private bool ShowCreateSkillModal { get; set; }
private bool ShowEditSkillModal { get; set; }
private bool ShowCreateSkillGroupModal { get; set; }
@@ -309,7 +420,7 @@ public partial class CharacterPanel
public IReadOnlyList<CharacterSheetSkillGroup> SelectedCharacterSkillGroups { get; set; } = [];
[Parameter]
public bool IsD6 { get; set; }
public string SelectedCampaignRulesetId { get; set; } = string.Empty;
[Parameter]
public string RollVisibility { get; set; } = "public";