feat: add plain text dice roll detail
Introduces `plainDetail` to the dice engine to provide a non-HTML version of the roll results. This allows components to display the roll breakdown without needing to parse or render HTML.
This commit is contained in:
@@ -38,7 +38,10 @@ export function rollDicePool(pool: DicePool): DicePoolResult {
|
||||
const { keptRolls, droppedRolls } = applyModifier(rolls, pool.modifier);
|
||||
|
||||
// 固定数字总是保留
|
||||
const finalKeptRolls = [...keptRolls, ...allRolls.filter((r) => r.sides === 0)];
|
||||
const finalKeptRolls = [
|
||||
...keptRolls,
|
||||
...allRolls.filter((r) => r.sides === 0),
|
||||
];
|
||||
|
||||
// 计算小计
|
||||
const subtotal = finalKeptRolls.reduce((sum, roll) => {
|
||||
@@ -59,7 +62,7 @@ export function rollDicePool(pool: DicePool): DicePoolResult {
|
||||
*/
|
||||
export function applyModifier(
|
||||
rolls: DieResult[],
|
||||
modifier: DicePool["modifier"]
|
||||
modifier: DicePool["modifier"],
|
||||
): { keptRolls: DieResult[]; droppedRolls: DieResult[] } {
|
||||
if (!modifier) {
|
||||
return { keptRolls: rolls, droppedRolls: [] };
|
||||
@@ -70,7 +73,8 @@ export function applyModifier(
|
||||
|
||||
switch (modifier.type) {
|
||||
case "kh": // Keep Highest - 保留最大的 N 个
|
||||
case "dh": { // Drop Highest - 丢弃最大的 N 个
|
||||
case "dh": {
|
||||
// Drop Highest - 丢弃最大的 N 个
|
||||
const count = modifier.count;
|
||||
const higher = sorted.slice(-count);
|
||||
const rest = sorted.slice(0, -count);
|
||||
@@ -81,7 +85,8 @@ export function applyModifier(
|
||||
}
|
||||
}
|
||||
case "kl": // Keep Lowest - 保留最小的 N 个
|
||||
case "dl": { // Drop Lowest - 丢弃最小的 N 个
|
||||
case "dl": {
|
||||
// Drop Lowest - 丢弃最小的 N 个
|
||||
const count = modifier.count;
|
||||
const lower = sorted.slice(0, count);
|
||||
const rest = sorted.slice(count);
|
||||
@@ -105,29 +110,35 @@ export function roll(formula: string): RollResult {
|
||||
|
||||
const total = poolResults.reduce((sum, result) => sum + result.subtotal, 0);
|
||||
|
||||
// 生成详细表达式 - 使用 HTML 格式
|
||||
const rollSpans: string[] = [];
|
||||
// Generate detail expressions (both HTML and plain text)
|
||||
const htmlSpans: string[] = [];
|
||||
const textSpans: string[] = [];
|
||||
for (const result of poolResults) {
|
||||
// 构建骰子结果 HTML
|
||||
for (const roll of result.rolls) {
|
||||
const isKept = result.keptRolls.some((kept) => kept === roll);
|
||||
if (roll.sides === 0) {
|
||||
// 固定数字,不显示括号
|
||||
rollSpans.push(`<strong>${roll.isNegative ? '-' : ''}${roll.value}</strong>`);
|
||||
const sign = roll.isNegative ? "-" : "";
|
||||
htmlSpans.push(`<strong>${sign}${roll.value}</strong>`);
|
||||
textSpans.push(`${sign}${roll.value}`);
|
||||
} else if (isKept) {
|
||||
rollSpans.push(`<strong>[${roll.value}]</strong>`);
|
||||
htmlSpans.push(`<strong>[${roll.value}]</strong>`);
|
||||
textSpans.push(`[${roll.value}]`);
|
||||
} else {
|
||||
rollSpans.push(`<span class="text-gray-400">[${roll.value}]</span>`);
|
||||
htmlSpans.push(`<span class="text-gray-400">[${roll.value}]</span>`);
|
||||
textSpans.push(`[${roll.value}]`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const rollsStr = rollSpans.join(" + ");
|
||||
const detail = `<strong>${total}</strong> = ${rollsStr}`;
|
||||
const rollHtml = htmlSpans.join(" + ");
|
||||
const rollText = textSpans.join(" + ");
|
||||
const detail = `<strong>${total}</strong> = ${rollHtml}`;
|
||||
const plainDetail = `${total} = ${rollText}`;
|
||||
|
||||
return {
|
||||
pools: poolResults,
|
||||
total,
|
||||
detail,
|
||||
plainDetail,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user