2026-02-26 00:47:26 +08:00
|
|
|
|
import { customElement, noShadowDOM } from 'solid-element';
|
|
|
|
|
|
import { createSignal } from 'solid-js';
|
2026-02-26 00:17:23 +08:00
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-02-26 00:47:26 +08:00
|
|
|
|
customElement('ttrpg-dice', { key: '' }, (props, { element }) => {
|
|
|
|
|
|
noShadowDOM();
|
2026-02-26 00:17:23 +08:00
|
|
|
|
const [result, setResult] = createSignal<number | null>(null);
|
|
|
|
|
|
const [isRolled, setIsRolled] = createSignal(false);
|
|
|
|
|
|
|
2026-02-26 00:47:26 +08:00
|
|
|
|
// 从 element 的 textContent 获取骰子公式
|
|
|
|
|
|
const formula = element?.textContent?.trim() || '';
|
|
|
|
|
|
|
2026-02-26 00:17:23 +08:00
|
|
|
|
const handleClick = () => {
|
|
|
|
|
|
if (isRolled()) {
|
|
|
|
|
|
// 重置为公式
|
|
|
|
|
|
setResult(null);
|
|
|
|
|
|
setIsRolled(false);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 掷骰子
|
2026-02-26 00:47:26 +08:00
|
|
|
|
const rollResult = rollDice(formula);
|
2026-02-26 00:17:23 +08:00
|
|
|
|
setResult(rollResult);
|
|
|
|
|
|
setIsRolled(true);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-02-26 00:47:26 +08:00
|
|
|
|
const displayText = () => (isRolled() ? `${result()}` : formula);
|
2026-02-26 00:17:23 +08:00
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
<a
|
2026-02-26 00:47:26 +08:00
|
|
|
|
href={props.key && isRolled() ? `?dice-${props.key}=${result()}` : '#'}
|
2026-02-26 00:17:23 +08:00
|
|
|
|
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>
|
|
|
|
|
|
);
|
2026-02-26 00:47:26 +08:00
|
|
|
|
});
|
|
|
|
|
|
|