Adjust rolemaster retry lower bound
This commit is contained in:
@@ -81,7 +81,7 @@ Rolemaster support:
|
|||||||
- Open-ended percentile expressions such as `d100!+85`
|
- Open-ended percentile expressions such as `d100!+85`
|
||||||
- Conditional `FumbleRange` handling for open-ended percentile skills and skill-group defaults
|
- Conditional `FumbleRange` handling for open-ended percentile skills and skill-group defaults
|
||||||
- Persisted and validated automatic retry toggle for open-ended percentile skills; only eligible Rolemaster skills can enable it
|
- Persisted and validated automatic retry toggle for open-ended percentile skills; only eligible Rolemaster skills can enable it
|
||||||
- Automatic retry windows for eligible open-ended skills: results `77-90` retry once with `+5`, and results `91-110` retry once with `+10`
|
- Automatic retry windows for eligible open-ended skills: results `76-90` retry once with `+5`, and results `91-110` retry once with `+10`
|
||||||
- Open-ended high chaining and low-end subtraction with ordered die metadata in roll detail
|
- Open-ended high chaining and low-end subtraction with ordered die metadata in roll detail
|
||||||
- Compact log badges and summaries for open-ended, retry, and fumble-related events, including `Retry +5` and `Retry +10`
|
- Compact log badges and summaries for open-ended, retry, and fumble-related events, including `Retry +5` and `Retry +10`
|
||||||
|
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ public sealed class RolemasterApiTests(WebApplicationFactory<Program> factory) :
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task RolemasterAutoRetryRolls_AppearInLogPageAndDetail()
|
public async Task RolemasterAutoRetryRolls_AppearInLogPageAndDetail()
|
||||||
{
|
{
|
||||||
using var factory = CreateFactory(68, 42, 90, 32, 68);
|
using var factory = CreateFactory(66, 42, 90, 32, 65);
|
||||||
using var client = factory.CreateClient(new() { AllowAutoRedirect = false });
|
using var client = factory.CreateClient(new() { AllowAutoRedirect = false });
|
||||||
|
|
||||||
await RegisterAsync(client, "rolemaster-retry-api", "Password123", "Rolemaster Retry Api");
|
await RegisterAsync(client, "rolemaster-retry-api", "Password123", "Rolemaster Retry Api");
|
||||||
@@ -104,7 +104,7 @@ public sealed class RolemasterApiTests(WebApplicationFactory<Program> factory) :
|
|||||||
var detail = await GetAsync<CampaignRollDetail>(client, $"/api/rolls/{retryFiveRoll.RollId}");
|
var detail = await GetAsync<CampaignRollDetail>(client, $"/api/rolls/{retryFiveRoll.RollId}");
|
||||||
|
|
||||||
Assert.Equal(57, retryFiveRoll.Result);
|
Assert.Equal(57, retryFiveRoll.Result);
|
||||||
Assert.Equal("68+10=78; retry(+5): 42+10=52; final=57", retryFiveRoll.Breakdown);
|
Assert.Equal("66+10=76; retry(+5): 42+10=52; final=57", retryFiveRoll.Breakdown);
|
||||||
Assert.Collection(retryFiveRoll.Dice, die =>
|
Assert.Collection(retryFiveRoll.Dice, die =>
|
||||||
{
|
{
|
||||||
Assert.Equal(1, die.Attempt);
|
Assert.Equal(1, die.Attempt);
|
||||||
@@ -118,16 +118,16 @@ public sealed class RolemasterApiTests(WebApplicationFactory<Program> factory) :
|
|||||||
Assert.Equal(43, retryTenRoll.Result);
|
Assert.Equal(43, retryTenRoll.Result);
|
||||||
Assert.Equal("90+1=91; retry(+10): 32+1=33; final=43", retryTenRoll.Breakdown);
|
Assert.Equal("90+1=91; retry(+10): 32+1=33; final=43", retryTenRoll.Breakdown);
|
||||||
|
|
||||||
Assert.Equal(78, disabledRoll.Result);
|
Assert.Equal(75, disabledRoll.Result);
|
||||||
Assert.Equal("68+10=78", disabledRoll.Breakdown);
|
Assert.Equal("65+10=75", disabledRoll.Breakdown);
|
||||||
Assert.All(disabledRoll.Dice, die => Assert.Null(die.Attempt));
|
Assert.All(disabledRoll.Dice, die => Assert.Null(die.Attempt));
|
||||||
|
|
||||||
Assert.Equal(3, logPage.Entries.Length);
|
Assert.Equal(3, logPage.Entries.Length);
|
||||||
Assert.Equal("68 | open-ended | retry +5", logPage.Entries[0].SummaryText);
|
Assert.Equal("66 | open-ended | retry +5", logPage.Entries[0].SummaryText);
|
||||||
Assert.Equal(["rs5"], Assert.IsType<string[]>(logPage.Entries[0].EventBadges));
|
Assert.Equal(["r66", "rs5"], Assert.IsType<string[]>(logPage.Entries[0].EventBadges));
|
||||||
Assert.Equal("90 | open-ended | retry +10", logPage.Entries[1].SummaryText);
|
Assert.Equal("90 | open-ended | retry +10", logPage.Entries[1].SummaryText);
|
||||||
Assert.Equal(["rs10"], Assert.IsType<string[]>(logPage.Entries[1].EventBadges));
|
Assert.Equal(["rs10"], Assert.IsType<string[]>(logPage.Entries[1].EventBadges));
|
||||||
Assert.Equal("68 | open-ended", logPage.Entries[2].SummaryText);
|
Assert.Equal("65 | open-ended", logPage.Entries[2].SummaryText);
|
||||||
Assert.Null(logPage.Entries[2].EventBadges);
|
Assert.Null(logPage.Entries[2].EventBadges);
|
||||||
|
|
||||||
Assert.Equal(retryFiveRoll.Breakdown, detail.Breakdown);
|
Assert.Equal(retryFiveRoll.Breakdown, detail.Breakdown);
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ public sealed class PayloadBudgetTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void RolemasterRollDetailPayload_StaysWithinBudget_AndRetryMetadataRemainsLazy()
|
public void RolemasterRollDetailPayload_StaysWithinBudget_AndRetryMetadataRemainsLazy()
|
||||||
{
|
{
|
||||||
using var harness = ServiceTestSupport.CreateHarness(68, 42);
|
using var harness = ServiceTestSupport.CreateHarness(66, 42);
|
||||||
var service = harness.Service;
|
var service = harness.Service;
|
||||||
|
|
||||||
service.Register("gm-rm-detail-budget", "Password123", "GM");
|
service.Register("gm-rm-detail-budget", "Password123", "GM");
|
||||||
@@ -148,7 +148,7 @@ public sealed class PayloadBudgetTests
|
|||||||
var roll = ServiceTestSupport.GetValue(service.RollSkill(ownerSession, skill.Id, "public"));
|
var roll = ServiceTestSupport.GetValue(service.RollSkill(ownerSession, skill.Id, "public"));
|
||||||
var logPage = ServiceTestSupport.GetValue(service.GetCampaignLogPage(gmSession, campaign.Id, limit: 5));
|
var logPage = ServiceTestSupport.GetValue(service.GetCampaignLogPage(gmSession, campaign.Id, limit: 5));
|
||||||
var detail = ServiceTestSupport.GetValue(service.GetRollDetail(gmSession, roll.RollId));
|
var detail = ServiceTestSupport.GetValue(service.GetRollDetail(gmSession, roll.RollId));
|
||||||
Assert.Equal("68 | open-ended | retry +5", Assert.Single(logPage.Entries).SummaryText);
|
Assert.Equal("66 | open-ended | retry +5", Assert.Single(logPage.Entries).SummaryText);
|
||||||
|
|
||||||
AssertPayloadWithinBudget(detail, 4 * 1024, "rolemaster roll detail");
|
AssertPayloadWithinBudget(detail, 4 * 1024, "rolemaster roll detail");
|
||||||
|
|
||||||
@@ -161,7 +161,7 @@ public sealed class PayloadBudgetTests
|
|||||||
Assert.DoesNotContain("\"sequence\"", logPageJson, StringComparison.Ordinal);
|
Assert.DoesNotContain("\"sequence\"", logPageJson, StringComparison.Ordinal);
|
||||||
Assert.DoesNotContain("\"breakdown\"", logPageJson, StringComparison.Ordinal);
|
Assert.DoesNotContain("\"breakdown\"", logPageJson, StringComparison.Ordinal);
|
||||||
Assert.Contains("\"kind\":\"rolemaster-open-ended-initial\"", detailJson, StringComparison.Ordinal);
|
Assert.Contains("\"kind\":\"rolemaster-open-ended-initial\"", detailJson, StringComparison.Ordinal);
|
||||||
Assert.Contains("\"signedContribution\":68", detailJson, StringComparison.Ordinal);
|
Assert.Contains("\"signedContribution\":66", detailJson, StringComparison.Ordinal);
|
||||||
Assert.Contains("\"attempt\":1", detailJson, StringComparison.Ordinal);
|
Assert.Contains("\"attempt\":1", detailJson, StringComparison.Ordinal);
|
||||||
Assert.Contains("\"attempt\":2", detailJson, StringComparison.Ordinal);
|
Assert.Contains("\"attempt\":2", detailJson, StringComparison.Ordinal);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,11 +72,11 @@ public sealed class ServiceHelperExtractionTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void RolemasterRetryPolicy_ResolvesRetryBandsAndMarkers()
|
public void RolemasterRetryPolicy_ResolvesRetryBandsAndMarkers()
|
||||||
{
|
{
|
||||||
Assert.Equal(5, RolemasterRetryPolicy.ResolveAutoRetryBonus(77));
|
Assert.Equal(5, RolemasterRetryPolicy.ResolveAutoRetryBonus(76));
|
||||||
Assert.Equal(5, RolemasterRetryPolicy.ResolveAutoRetryBonus(90));
|
Assert.Equal(5, RolemasterRetryPolicy.ResolveAutoRetryBonus(90));
|
||||||
Assert.Equal(10, RolemasterRetryPolicy.ResolveAutoRetryBonus(91));
|
Assert.Equal(10, RolemasterRetryPolicy.ResolveAutoRetryBonus(91));
|
||||||
Assert.Equal(10, RolemasterRetryPolicy.ResolveAutoRetryBonus(110));
|
Assert.Equal(10, RolemasterRetryPolicy.ResolveAutoRetryBonus(110));
|
||||||
Assert.Null(RolemasterRetryPolicy.ResolveAutoRetryBonus(76));
|
Assert.Null(RolemasterRetryPolicy.ResolveAutoRetryBonus(75));
|
||||||
Assert.Null(RolemasterRetryPolicy.ResolveAutoRetryBonus(111));
|
Assert.Null(RolemasterRetryPolicy.ResolveAutoRetryBonus(111));
|
||||||
Assert.Equal(5, RolemasterRetryPolicy.TryExtractRetryBonus("68+10=78; retry(+5): 42+10=52; final=57"));
|
Assert.Equal(5, RolemasterRetryPolicy.TryExtractRetryBonus("68+10=78; retry(+5): 42+10=52; final=57"));
|
||||||
Assert.Equal(10, RolemasterRetryPolicy.TryExtractRetryBonus("90+1=91; retry(+10): 32+1=33; final=43"));
|
Assert.Equal(10, RolemasterRetryPolicy.TryExtractRetryBonus("90+1=91; retry(+10): 32+1=33; final=43"));
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ public sealed class ServiceRolemasterRollTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void RollSkill_RolemasterAutoRetryPlusFive_UsesRetryResultAndMarksAttempts()
|
public void RollSkill_RolemasterAutoRetryPlusFive_UsesRetryResultAndMarksAttempts()
|
||||||
{
|
{
|
||||||
using var harness = ServiceTestSupport.CreateHarness(68, 42);
|
using var harness = ServiceTestSupport.CreateHarness(66, 42);
|
||||||
var service = harness.Service;
|
var service = harness.Service;
|
||||||
|
|
||||||
service.Register("gm-retry-five", "Password123", "GM");
|
service.Register("gm-retry-five", "Password123", "GM");
|
||||||
@@ -192,17 +192,17 @@ public sealed class ServiceRolemasterRollTests
|
|||||||
var logEntry = Assert.Single(ServiceTestSupport.GetValue(service.GetCampaignLogPage(session, campaign.Id, limit: 5)).Entries);
|
var logEntry = Assert.Single(ServiceTestSupport.GetValue(service.GetCampaignLogPage(session, campaign.Id, limit: 5)).Entries);
|
||||||
|
|
||||||
Assert.Equal(57, roll.Result);
|
Assert.Equal(57, roll.Result);
|
||||||
Assert.Equal("68+10=78; retry(+5): 42+10=52; final=57", roll.Breakdown);
|
Assert.Equal("66+10=76; retry(+5): 42+10=52; final=57", roll.Breakdown);
|
||||||
Assert.Equal("68 | open-ended | retry +5", logEntry.SummaryText);
|
Assert.Equal("66 | open-ended | retry +5", logEntry.SummaryText);
|
||||||
Assert.Equal(["rs5"], Assert.IsType<string[]>(logEntry.EventBadges));
|
Assert.Equal(["r66", "rs5"], Assert.IsType<string[]>(logEntry.EventBadges));
|
||||||
Assert.Equal(roll.Breakdown, detail.Breakdown);
|
Assert.Equal(roll.Breakdown, detail.Breakdown);
|
||||||
Assert.Collection(detail.Dice, die =>
|
Assert.Collection(detail.Dice, die =>
|
||||||
{
|
{
|
||||||
Assert.Equal(68, die.Roll);
|
Assert.Equal(66, die.Roll);
|
||||||
Assert.Equal(1, die.Sequence);
|
Assert.Equal(1, die.Sequence);
|
||||||
Assert.Equal(1, die.Attempt);
|
Assert.Equal(1, die.Attempt);
|
||||||
Assert.Equal(RollDieKinds.RolemasterOpenEndedInitial, die.Kind);
|
Assert.Equal(RollDieKinds.RolemasterOpenEndedInitial, die.Kind);
|
||||||
Assert.Equal(68, die.SignedContribution);
|
Assert.Equal(66, die.SignedContribution);
|
||||||
}, die =>
|
}, die =>
|
||||||
{
|
{
|
||||||
Assert.Equal(42, die.Roll);
|
Assert.Equal(42, die.Roll);
|
||||||
@@ -238,7 +238,7 @@ public sealed class ServiceRolemasterRollTests
|
|||||||
[Fact]
|
[Fact]
|
||||||
public void RollSkill_RolemasterAutoRetryDisabled_KeepsOriginalResult()
|
public void RollSkill_RolemasterAutoRetryDisabled_KeepsOriginalResult()
|
||||||
{
|
{
|
||||||
using var harness = ServiceTestSupport.CreateHarness(68);
|
using var harness = ServiceTestSupport.CreateHarness(65);
|
||||||
var service = harness.Service;
|
var service = harness.Service;
|
||||||
|
|
||||||
service.Register("gm-retry-off", "Password123", "GM");
|
service.Register("gm-retry-off", "Password123", "GM");
|
||||||
@@ -250,9 +250,9 @@ public sealed class ServiceRolemasterRollTests
|
|||||||
var roll = ServiceTestSupport.GetValue(service.RollSkill(session, skill.Id, "public"));
|
var roll = ServiceTestSupport.GetValue(service.RollSkill(session, skill.Id, "public"));
|
||||||
var logEntry = Assert.Single(ServiceTestSupport.GetValue(service.GetCampaignLogPage(session, campaign.Id, limit: 5)).Entries);
|
var logEntry = Assert.Single(ServiceTestSupport.GetValue(service.GetCampaignLogPage(session, campaign.Id, limit: 5)).Entries);
|
||||||
|
|
||||||
Assert.Equal(78, roll.Result);
|
Assert.Equal(75, roll.Result);
|
||||||
Assert.Equal("68+10=78", roll.Breakdown);
|
Assert.Equal("65+10=75", roll.Breakdown);
|
||||||
Assert.Equal("68 | open-ended", logEntry.SummaryText);
|
Assert.Equal("65 | open-ended", logEntry.SummaryText);
|
||||||
Assert.Null(logEntry.EventBadges);
|
Assert.Null(logEntry.EventBadges);
|
||||||
Assert.All(roll.Dice, die => Assert.Null(die.Attempt));
|
Assert.All(roll.Dice, die => Assert.Null(die.Attempt));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ public sealed class ServiceRollHelperTests
|
|||||||
Assert.Equal("0=0", RollBreakdownFormatter.BuildBreakdown([], 0, 0));
|
Assert.Equal("0=0", RollBreakdownFormatter.BuildBreakdown([], 0, 0));
|
||||||
Assert.Equal("97+96+45+85=323", RollBreakdownFormatter.BuildRolemasterOpenEndedBreakdown(97, [96, 45], false, 85, 323));
|
Assert.Equal("97+96+45+85=323", RollBreakdownFormatter.BuildRolemasterOpenEndedBreakdown(97, [96, 45], false, 85, 323));
|
||||||
Assert.Equal("(05) -97 -100 -12 +85 = -124", RollBreakdownFormatter.BuildRolemasterOpenEndedBreakdown(5, [97, 100, 12], true, 85, -124));
|
Assert.Equal("(05) -97 -100 -12 +85 = -124", RollBreakdownFormatter.BuildRolemasterOpenEndedBreakdown(5, [97, 100, 12], true, 85, -124));
|
||||||
Assert.Equal("68+10=78; retry(+5): 42+10=52; final=57", RollBreakdownFormatter.BuildRolemasterRetryBreakdown("68+10=78", 5, "42+10=52", 57));
|
Assert.Equal("66+10=76; retry(+5): 42+10=52; final=57", RollBreakdownFormatter.BuildRolemasterRetryBreakdown("66+10=76", 5, "42+10=52", 57));
|
||||||
Assert.Equal("05", RollBreakdownFormatter.FormatRolemasterTriggerRoll(5));
|
Assert.Equal("05", RollBreakdownFormatter.FormatRolemasterTriggerRoll(5));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,19 +29,19 @@ public sealed class ServiceRollHelperTests
|
|||||||
{
|
{
|
||||||
var d6Dice = new[] { new RollDieResult(6, true, false, true, false, false), new RollDieResult(1, false, true, true, false, false) };
|
var d6Dice = new[] { new RollDieResult(6, true, false, true, false, false), new RollDieResult(1, false, true, true, false, false) };
|
||||||
var rolemasterDice = new[] { new RollDieResult(5, false, false, false, false, false, 1, RollDieKinds.RolemasterOpenEndedInitial), new RollDieResult(97, false, false, false, false, false, 2, RollDieKinds.RolemasterOpenEndedLowSubtract, -97), new RollDieResult(100, false, false, false, false, false, 3, RollDieKinds.RolemasterOpenEndedLowSubtract, -100) };
|
var rolemasterDice = new[] { new RollDieResult(5, false, false, false, false, false, 1, RollDieKinds.RolemasterOpenEndedInitial), new RollDieResult(97, false, false, false, false, false, 2, RollDieKinds.RolemasterOpenEndedLowSubtract, -97), new RollDieResult(100, false, false, false, false, false, 3, RollDieKinds.RolemasterOpenEndedLowSubtract, -100) };
|
||||||
var retryDice = new[] { new RollDieResult(68, false, false, false, false, false, 1, RollDieKinds.RolemasterOpenEndedInitial, 68, 1), new RollDieResult(42, false, false, false, false, false, 1, RollDieKinds.RolemasterOpenEndedInitial, 42, 2) };
|
var retryDice = new[] { new RollDieResult(66, false, false, false, false, false, 1, RollDieKinds.RolemasterOpenEndedInitial, 66, 1), new RollDieResult(42, false, false, false, false, false, 1, RollDieKinds.RolemasterOpenEndedInitial, 42, 2) };
|
||||||
const string retryBreakdown = "68+10=78; retry(+5): 42+10=52; final=57";
|
const string retryBreakdown = "66+10=76; retry(+5): 42+10=52; final=57";
|
||||||
|
|
||||||
Assert.Equal("1d20+5", CampaignLogSummaryBuilder.ExtractCustomRollExpression("1d20+5 => 20+5=25", " => "));
|
Assert.Equal("1d20+5", CampaignLogSummaryBuilder.ExtractCustomRollExpression("1d20+5 => 20+5=25", " => "));
|
||||||
Assert.Null(CampaignLogSummaryBuilder.ExtractCustomRollExpression("20+5=25", " => "));
|
Assert.Null(CampaignLogSummaryBuilder.ExtractCustomRollExpression("20+5=25", " => "));
|
||||||
Assert.Equal("6, 1", CampaignLogSummaryBuilder.BuildCompactLogSummary(d6Dice));
|
Assert.Equal("6, 1", CampaignLogSummaryBuilder.BuildCompactLogSummary(d6Dice));
|
||||||
Assert.Equal("(05) -97 -100 | open-ended low", CampaignLogSummaryBuilder.BuildCompactLogSummary(rolemasterDice));
|
Assert.Equal("(05) -97 -100 | open-ended low", CampaignLogSummaryBuilder.BuildCompactLogSummary(rolemasterDice));
|
||||||
Assert.Equal("68 | open-ended | retry +5", CampaignLogSummaryBuilder.BuildCompactLogSummary(retryDice, retryBreakdown));
|
Assert.Equal("66 | open-ended | retry +5", CampaignLogSummaryBuilder.BuildCompactLogSummary(retryDice, retryBreakdown));
|
||||||
Assert.Equal("No detail available.", CampaignLogSummaryBuilder.BuildCompactLogSummary([]));
|
Assert.Equal("No detail available.", CampaignLogSummaryBuilder.BuildCompactLogSummary([]));
|
||||||
Assert.Equal(["w6", "w1"], Assert.IsType<string[]>(CampaignLogSummaryBuilder.BuildCompactLogEventBadges(RulesetKind.D6, null, d6Dice)));
|
Assert.Equal(["w6", "w1"], Assert.IsType<string[]>(CampaignLogSummaryBuilder.BuildCompactLogEventBadges(RulesetKind.D6, null, d6Dice)));
|
||||||
Assert.Equal(["n20"], Assert.IsType<string[]>(CampaignLogSummaryBuilder.BuildCompactLogEventBadges(RulesetKind.Dnd5e, "1d20+5", [new(20, false, false, false, false, false)])));
|
Assert.Equal(["n20"], Assert.IsType<string[]>(CampaignLogSummaryBuilder.BuildCompactLogEventBadges(RulesetKind.Dnd5e, "1d20+5", [new(20, false, false, false, false, false)])));
|
||||||
Assert.Equal(["rf", "r100"], Assert.IsType<string[]>(CampaignLogSummaryBuilder.BuildCompactLogEventBadges(RulesetKind.Rolemaster, "d100!+85", rolemasterDice)));
|
Assert.Equal(["rf", "r100"], Assert.IsType<string[]>(CampaignLogSummaryBuilder.BuildCompactLogEventBadges(RulesetKind.Rolemaster, "d100!+85", rolemasterDice)));
|
||||||
Assert.Equal(["rs5"], Assert.IsType<string[]>(CampaignLogSummaryBuilder.BuildCompactLogEventBadges(RulesetKind.Rolemaster, "d100!+10", retryDice, retryBreakdown)));
|
Assert.Equal(["r66", "rs5"], Assert.IsType<string[]>(CampaignLogSummaryBuilder.BuildCompactLogEventBadges(RulesetKind.Rolemaster, "d100!+10", retryDice, retryBreakdown)));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ public static class RolemasterRetryPolicy
|
|||||||
{
|
{
|
||||||
public static int? ResolveAutoRetryBonus(int firstResult)
|
public static int? ResolveAutoRetryBonus(int firstResult)
|
||||||
{
|
{
|
||||||
if (firstResult is >= 77 and <= 90)
|
if (firstResult is >= 76 and <= 90)
|
||||||
return 5;
|
return 5;
|
||||||
|
|
||||||
if (firstResult is >= 91 and <= 110)
|
if (firstResult is >= 91 and <= 110)
|
||||||
|
|||||||
4
TASKS.md
4
TASKS.md
@@ -8,7 +8,7 @@ This ExecPlan is a living document. The sections `Progress`, `Surprises & Discov
|
|||||||
|
|
||||||
After this change, a Rolemaster skill can opt into an automatic retry when its first result lands in specific retry bands. The player will be able to toggle that behavior while creating or editing a Rolemaster open-ended skill, roll the skill, and then see the retry clearly in the campaign log card through a special badge and readable summary text. The detailed roll view will still show enough information to explain why the retry happened and what final result was recorded.
|
After this change, a Rolemaster skill can opt into an automatic retry when its first result lands in specific retry bands. The player will be able to toggle that behavior while creating or editing a Rolemaster open-ended skill, roll the skill, and then see the retry clearly in the campaign log card through a special badge and readable summary text. The detailed roll view will still show enough information to explain why the retry happened and what final result was recorded.
|
||||||
|
|
||||||
For this feature, an eligible retry result means a Rolemaster open-ended percentile skill roll whose first fully evaluated result, including the skill expression modifier and any low-end subtraction chain, lands in one of the retry windows before any retry bonus is applied. This plan preserves the user-provided thresholds exactly: results `77` through `90` grant a retry with `+5`; results `91` through `110` grant a retry with `+10`.
|
For this feature, an eligible retry result means a Rolemaster open-ended percentile skill roll whose first fully evaluated result, including the skill expression modifier and any low-end subtraction chain, lands in one of the retry windows before any retry bonus is applied. This plan preserves the user-provided thresholds exactly: results `76` through `90` grant a retry with `+5`; results `91` through `110` grant a retry with `+10`.
|
||||||
|
|
||||||
## Progress
|
## Progress
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ For this feature, an eligible retry result means a Rolemaster open-ended percent
|
|||||||
Rationale: The request explicitly asks for “a toggle for a rolemaster skill.” A skill-group default would widen scope into template inheritance and create unclear behavior for ad hoc custom rolls.
|
Rationale: The request explicitly asks for “a toggle for a rolemaster skill.” A skill-group default would widen scope into template inheritance and create unclear behavior for ad hoc custom rolls.
|
||||||
Date/Author: 2026-04-04 / Codex
|
Date/Author: 2026-04-04 / Codex
|
||||||
|
|
||||||
- Decision: The retry windows are interpreted literally from the request: `77-90 => +5`, `91-110 => +10`. A result of `111` counts as success and doesn't need to be retried.
|
- Decision: The retry windows are interpreted literally from the request: `76-90 => +5`, `91-110 => +10`. A result of `111` counts as success and doesn't need to be retried.
|
||||||
Rationale: The user gave concrete inclusive and exclusive bounds. Preserving those exact bounds avoids silently changing game rules inside the plan.
|
Rationale: The user gave concrete inclusive and exclusive bounds. Preserving those exact bounds avoids silently changing game rules inside the plan.
|
||||||
Date/Author: 2026-04-04 / Codex
|
Date/Author: 2026-04-04 / Codex
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user