ttrpg-tools/src/components/dice.tsx

67 lines
1.6 KiB
TypeScript
Raw Normal View History

2026-02-26 00:17:23 +08:00
import { Component, createSignal, Show } from 'solid-js';
export interface DiceProps {
formula: string;
key?: string;
}
function rollDice(formula: string): number {
// 解析骰子公式例如2d6+d8
const parts = formula.split('+').map(p => p.trim());
let total = 0;
for (const part of parts) {
const match = part.match(/^(\d+)?d(\d+)$/i);
if (match) {
const count = parseInt(match[1] || '1');
const sides = parseInt(match[2]);
for (let i = 0; i < count; i++) {
total += Math.floor(Math.random() * sides) + 1;
}
} else {
// 处理固定数字
const num = parseInt(part);
if (!isNaN(num)) {
total += num;
}
}
}
return total;
}
export const Dice: Component<DiceProps> = (props) => {
const [result, setResult] = createSignal<number | null>(null);
const [isRolled, setIsRolled] = createSignal(false);
const handleClick = () => {
if (isRolled()) {
// 重置为公式
setResult(null);
setIsRolled(false);
} else {
// 掷骰子
const rollResult = rollDice(props.formula);
setResult(rollResult);
setIsRolled(true);
}
};
const displayText = () => (isRolled() ? `${result()}` : props.formula);
const queryParams = () => (props.key && isRolled() ? `?dice-${props.key}=${result()}` : '');
return (
<a
href={queryParams()}
onClick={(e) => {
e.preventDefault();
handleClick();
}}
class="inline-flex items-center gap-1 text-blue-600 hover:text-blue-800 cursor-pointer"
>
<span>🎲</span>
<span>{displayText()}</span>
</a>
);
};