\ Amazonのセール 完全に理解した /

【Webゲーム】文字スロット(MOJI-SLOT)🎰好きな文字でパチスロできる!


DIGITAL REEL GAME
SCORE0
CHANCE
BETボタン または レバー を押してください

MOJI-SLOT CONTROL

システム設定

プリセットを選ぶか、各項目を自由にカスタマイズできます。

未保存
01 文字と表示
リールの文字・記号5 / 7

左から1文字ずつ入力。絵文字も1文字として扱います。文字列の貼り付けで一括入力もできます。

02 役の設定

停止結果に「柄」が含まれると成立。スコアは役ごとの固定値です。

03 画面とサウンド
すべてブラウザ内で動作
Web Audio APIで効果音を生成するため、音源ファイルは不要です。
04 抽選とモーション

これを作るきっかけ…

下記ポストのちんすこうマシーンがきっかけで作ってみました。

今日の記事の「ちんすこうの綴りの危うさを可視化するマシーン」の動きだけでもぜひご覧下さい。 https://t.co/F7ecrIXjqX— 沖縄B級ポータルサイト DEEokinawa (@dee_okinawa) September 30, 2026

概要

好きな文字でスロットができる暇つぶし・お遊び用のWEBゲームです。
外部ライブラリ、画像、音源、サーバーAPIは使用しません。HTML/CSSで画面を構成し、JavaScriptで抽選、リール制御、設定保存、役判定、演出を行います。

操作

  1. BETボタンまたはレバーを押してBET
  2. もう一度BETボタンまたはレバーを押すと回転
  3. 各ストップボタンでリールを停止
  4. 全リール停止後、中央ラインの文字列で役を判定
  5. 役成立時は固定スコアを加算し、ランプ、効果音、メッセージ、紙吹雪を表示

停止済みのボタンを再度押した場合は、未停止リールを左から停止します。

プリセット

プロセット:ちんすこう
  • タイトル: MOJI-SLOT
  • 文字: ち / ん / す / こ / う
  • リール数: 5
  • デザイン: 沖縄ゴールド
  • 役: ちんすこう +500 / ちんちん +200 / ちんこ +100 / うんこ +50 / ちん +20
プロセット:LUCKY SEVEN
  • タイトル: LUCKY SEVEN
  • 記号: チェリー / バナナ / ベル / 星 / 7
  • リール数: 3
  • デザイン: クラシックレッド
  • 役: 7揃い +1000 / チェリー +200 / ベル +100 / バナナ +50 / 星 +25

システム設定

  • デザイン3種類
  • 01 文字と表示
    • リール文字・記号3〜7文字
    • リール数3〜7
    • ゲーム設定1〜6
  • 02 役の設定
    • 5種類の役の有効状態、役名、柄
  • 03 画面とサウンド
    • リール1コマの高さ
    • 効果音の音量
  • 04 抽選とモーション
    • チャンスランプ確率
    • 役の引き込み確率
    • 最小回転速度
    • 最大回転速度
    • 減速時間
    • 最大スベリコマ

大まかな仕様

  • 役判定
    成立可能な有効役をスコアの高い順に評価。
    停止結果に役の柄が含まれる場合、最もスコアの高い役を1件だけ採用します。スコアと演出レベルはプリセット内の固定値です。
  • 対応画面
    • スロット本体最大幅: 760px
    • 設定画面最大幅: 920px
    • 650px以下: 設定画面を1カラム化
    • 520px以下: BET、レバー、リール文字を縮小
    • 380px以下: 7リール表示向けに操作部を追加縮小
  • 動作環境
    JavaScript、localStorage、requestAnimationFrame、Web Audio APIが有効なモダンブラウザを対象。
    Intl.Segmenter 非対応環境ではUnicodeコードポイント単位の処理へフォールバックします。]

ソース

ソース:HTML
<!-- MOJI-SLOT -->
<div class="wpslot-app" id="wpslot-app" data-theme="okinawa" aria-busy="false">
  <div class="wpslot-cabinet" aria-label="文字スロット">
    <header class="wpslot-top-panel">
      <div>
        <div class="wpslot-eyebrow">DIGITAL REEL GAME</div>
        <div class="wpslot-logo" id="wpslot-logo">MOJI-SLOT</div>
      </div>
      <div class="wpslot-displays">
        <div class="wpslot-score-display">
          <span>SCORE</span><strong id="wpslot-score">0</strong>
        </div>
        <div id="wpslot-lamp" class="wpslot-lamp" aria-live="polite">
          CHANCE
        </div>
      </div>
    </header>

    <div class="wpslot-paytable" id="wpslot-paytable" aria-label="有効な役"></div>

    <div class="wpslot-stage">
      <div class="wpslot-side-control">
        <button type="button" id="wpslot-bet-btn" class="wpslot-round-bet" aria-label="1ベット">
          <span class="wpslot-bet-num">1</span><span class="wpslot-bet-txt">BET</span>
        </button>
      </div>

      <div class="wpslot-center-area">
        <div class="wpslot-window-frame">
          <div class="wpslot-payline" aria-hidden="true"></div>
          <div class="wpslot-reels" id="wpslot-reels"></div>
          <div class="wpslot-glass" aria-hidden="true"></div>
        </div>
        <div class="wpslot-stop-buttons" id="wpslot-stop-buttons"></div>
      </div>

      <div class="wpslot-side-control">
        <button type="button" class="wpslot-lever-box" id="wpslot-lever" aria-label="スタートレバー">
          <span class="wpslot-lever-shaft"></span><span class="wpslot-lever-ball"></span>
        </button>
      </div>
    </div>

    <div class="wpslot-status-bar" id="wpslot-msg" role="status" aria-live="polite">
      BETボタン または レバー を押してください
    </div>
    <div class="wpslot-footer-row">
      <span id="wpslot-setting-label">設定 6</span>
      <button type="button" id="wpslot-score-reset" class="wpslot-text-button">
        スコアを0に戻す
      </button>
    </div>
    <div class="wpslot-fx" id="wpslot-fx" aria-hidden="true"></div>
  </div>
</div>

<section class="wpsys-panel" id="wpslot-system-settings" aria-labelledby="wpsys-title">
  <header class="wpsys-header">
    <div>
      <p class="wpsys-kicker">MOJI-SLOT CONTROL</p>
      <div class="wpsys-title" id="wpsys-title" role="heading" aria-level="2">
        システム設定
      </div>
      <p>プリセットを選ぶか、各項目を自由にカスタマイズできます。</p>
    </div>
    <span class="wpsys-save-state" id="wpsys-save-state" role="status">未保存</span>
  </header>

  <div class="wpsys-presets" aria-label="プリセット">
    <button type="button" class="wpsys-preset" data-preset="chinsuko">
      <span class="wpsys-preset-icon">ち</span><span><b>ちんすこう</b><small>現行ベース・5リール</small></span>
    </button>
    <button type="button" class="wpsys-preset" data-preset="classic">
      <span class="wpsys-preset-icon" data-symbol="cherry" aria-hidden="true"></span><span><b>LUCKY SEVEN</b><small>絵文字・3リール</small></span>
    </button>
    <button type="button" class="wpsys-preset" data-preset="custom">
      <span class="wpsys-preset-icon" data-symbol="gear" aria-hidden="true"></span><span><b>カスタム</b><small>現在の値を編集</small></span>
    </button>
  </div>

  <div class="wpsys-grid">
    <fieldset class="wpsys-card wpsys-card-wide">
      <legend><span>01</span> 文字と表示</legend>
      <div class="wpsys-form-grid">
        <label class="wpsys-field"><span>タイトル</span><input id="wpsys-machine-title" type="text" maxlength="24" value="MOJI-SLOT"></label>
        <label class="wpsys-field"><span>デザイン</span><select id="wpsys-theme">
            <option value="okinawa">沖縄ゴールド</option>
            <option value="classic">クラシックレッド</option>
            <option value="neon">ネオンパープル</option>
          </select></label>
      </div>
      <div class="wpsys-symbol-head">
        <span>リールの文字・記号</span><output id="wpsys-symbol-count">5 / 7</output>
      </div>
      <p class="wpsys-help">
        左から1文字ずつ入力。絵文字も1文字として扱います。文字列の貼り付けで一括入力もできます。
      </p>
      <div class="wpsys-char-grid" id="wpsys-char-grid" aria-label="リール記号 最大7文字"></div>
      <div class="wpsys-form-grid wpsys-top-gap">
        <label class="wpsys-field"><span>リール数 <small>3〜7</small></span><input id="wpsys-reel-count" type="number" min="3" max="7" step="1"></label>
        <label class="wpsys-field"><span>ゲーム設定(当たりやすさ) <small>1〜6</small></span><select id="wpsys-slot-setting">
            <option>1</option>
            <option>2</option>
            <option>3</option>
            <option>4</option>
            <option>5</option>
            <option>6</option>
          </select></label>
      </div>
    </fieldset>

    <fieldset class="wpsys-card wpsys-card-wide">
      <legend><span>02</span> 役の設定</legend>
      <p class="wpsys-help">
        停止結果に「柄」が含まれると成立。スコアは役ごとの固定値です。
      </p>
      <div class="wpsys-role-table" id="wpsys-role-table"></div>
    </fieldset>

    <fieldset class="wpsys-card">
      <legend><span>03</span> 画面とサウンド</legend>
      <div class="wpsys-range-list">
        <label><span>リール1コマの高さ <output id="wpsys-row-out"></output></span><input id="wpsys-row" type="range" min="52" max="78" step="1"></label>
        <label><span>効果音の音量 <output id="wpsys-volume-out"></output></span><input id="wpsys-volume" type="range" min="0" max="30" step="1"></label>
      </div>
      <div class="wpsys-note">
        <b>すべてブラウザ内で動作</b><br>Web Audio
        APIで効果音を生成するため、音源ファイルは不要です。
      </div>
    </fieldset>

    <fieldset class="wpsys-card">
      <legend><span>04</span> 抽選とモーション</legend>
      <div class="wpsys-range-list">
        <label><span>チャンスランプ確率 <output id="wpsys-chance-out"></output></span><input id="wpsys-chance" type="range" min="0" max="100" step="1"></label>
        <label><span>役の引き込み確率 <output id="wpsys-force-out"></output></span><input id="wpsys-force" type="range" min="0" max="100" step="1"></label>
        <label><span>最小回転速度 <output id="wpsys-speed-min-out"></output></span><input id="wpsys-speed-min" type="range" min="12" max="45" step="1"></label>
        <label><span>最大回転速度 <output id="wpsys-speed-max-out"></output></span><input id="wpsys-speed-max" type="range" min="18" max="60" step="1"></label>
        <label><span>減速時間 <output id="wpsys-stop-out"></output></span><input id="wpsys-stop" type="range" min="260" max="1600" step="20"></label>
        <label><span>最大スベリコマ <output id="wpsys-slip-out"></output></span><input id="wpsys-slip" type="range" min="0" max="8" step="1"></label>
      </div>
    </fieldset>
  </div>

  <footer class="wpsys-actions">
    <button type="button" class="wpsys-secondary" id="wpsys-reload">
      保存済みの値に戻す
    </button>
    <button type="button" class="wpsys-primary" id="wpsys-apply">
      設定を反映
    </button>
  </footer>
</section>
ソース:CSS
/* MOJI-SLOT */
.wpslot-app {
  --accent: #f2c94c;
  --accent-2: #ff7a45;
  --cabinet: #171a21;
  --panel: #0d1118;
  --reel: #fffaf0;
  --ink: #17202a;
  --glow: rgba(242, 201, 76, 0.42);
  --row-h: 64px;
  box-sizing: border-box !important;
  width: 100% !important;
  max-width: 760px !important;
  margin: 20px auto !important;
  padding: 8px !important;
  color: #fff !important;
  font-family:
    Inter,
    ui-sans-serif,
    -apple-system,
    BlinkMacSystemFont,
    "Segoe UI",
    "Noto Sans JP",
    sans-serif !important;
  -webkit-tap-highlight-color: transparent !important;
  touch-action: manipulation !important;
}
.wpslot-app[data-theme="classic"] {
  --accent: #ffd54a;
  --accent-2: #ff315d;
  --cabinet: #17141f;
  --panel: #09070e;
  --reel: #fff;
  --ink: #141414;
  --glow: rgba(255, 49, 93, 0.38);
}
.wpslot-app[data-theme="neon"] {
  --accent: #68f9d2;
  --accent-2: #a878ff;
  --cabinet: #111326;
  --panel: #08091b;
  --reel: #f7f5ff;
  --ink: #1d1734;
  --glow: rgba(104, 249, 210, 0.4);
}
.wpslot-app,
.wpslot-app *,
.wpslot-app *::before,
.wpslot-app *::after {
  box-sizing: border-box !important;
}
.wpslot-cabinet {
  position: relative !important;
  overflow: hidden !important;
  padding: clamp(12px, 3vw, 24px) !important;
  border: 1px solid color-mix(in srgb, var(--accent) 62%, #fff 8%) !important;
  border-radius: 24px !important;
  background:
    radial-gradient(
      circle at 50% -20%,
      color-mix(in srgb, var(--accent) 16%, transparent),
      transparent 48%
    ),
    linear-gradient(145deg, #292e39, var(--cabinet) 58%, #0b0d12) !important;
  box-shadow:
    0 24px 70px rgba(0, 0, 0, 0.38),
    inset 0 1px rgba(255, 255, 255, 0.1),
    inset 0 0 35px rgba(0, 0, 0, 0.35) !important;
}
.wpslot-top-panel {
  display: flex !important;
  align-items: center !important;
  justify-content: space-between !important;
  gap: 12px !important;
  padding: 12px 14px !important;
  margin-bottom: 12px !important;
  border: 1px solid rgba(255, 255, 255, 0.09) !important;
  border-radius: 14px !important;
  background:
    linear-gradient(180deg, rgba(255, 255, 255, 0.055), transparent),
    var(--panel) !important;
}
.wpslot-eyebrow {
  color: rgba(255, 255, 255, 0.45) !important;
  font-size: 9px !important;
  font-weight: 800 !important;
  letter-spacing: 0.18em !important;
}
.wpslot-logo {
  color: var(--accent) !important;
  font-size: clamp(17px, 4vw, 25px) !important;
  font-weight: 950 !important;
  letter-spacing: 0.09em !important;
  text-shadow: 0 0 18px var(--glow) !important;
}
.wpslot-displays {
  display: flex !important;
  align-items: center !important;
  gap: 8px !important;
}
.wpslot-score-display {
  min-width: 82px !important;
  padding: 5px 9px !important;
  text-align: right !important;
  border: 1px solid rgba(255, 255, 255, 0.1) !important;
  border-radius: 9px !important;
  background: #07090d !important;
  box-shadow: inset 0 0 10px #000 !important;
}
.wpslot-score-display span {
  display: block !important;
  color: rgba(255, 255, 255, 0.42) !important;
  font-size: 8px !important;
  font-weight: 800 !important;
  letter-spacing: 0.15em !important;
}
.wpslot-score-display strong {
  color: #8cff9b !important;
  font:
    800 18px/1.1 ui-monospace,
    SFMono-Regular,
    Consolas,
    monospace !important;
  text-shadow: 0 0 8px rgba(140, 255, 155, 0.55) !important;
}
.wpslot-lamp {
  padding: 7px 11px !important;
  border: 1px solid #2b2f38 !important;
  border-radius: 999px !important;
  color: #3d2028 !important;
  background: #09090b !important;
  font-size: 10px !important;
  font-weight: 950 !important;
  letter-spacing: 0.08em !important;
}
.wpslot-lamp.gogo {
  color: #ff315d !important;
  border-color: #ff315d !important;
  background: #fff0f4 !important;
  box-shadow:
    0 0 22px rgba(255, 49, 93, 0.8),
    inset 0 0 9px rgba(255, 49, 93, 0.28) !important;
  animation: wpslot-pulse 0.55s ease-in-out infinite alternate !important;
}
.wpslot-paytable {
  display: flex !important;
  gap: 6px !important;
  min-height: 27px !important;
  margin: 0 2px 12px !important;
  overflow-x: auto !important;
  scrollbar-width: thin !important;
}
.wpslot-role-chip {
  flex: 0 0 auto !important;
  padding: 5px 9px !important;
  border: 1px solid rgba(255, 255, 255, 0.09) !important;
  border-radius: 999px !important;
  background: rgba(0, 0, 0, 0.2) !important;
  color: rgba(255, 255, 255, 0.67) !important;
  font-size: 10px !important;
  white-space: nowrap !important;
}
.wpslot-role-chip b {
  color: var(--accent) !important;
}
.wpslot-stage {
  display: grid !important;
  grid-template-columns: 52px minmax(0, 1fr) 52px !important;
  align-items: center !important;
  gap: clamp(6px, 2vw, 14px) !important;
}
.wpslot-side-control {
  display: flex !important;
  justify-content: center !important;
  align-items: center !important;
}
.wpslot-round-bet {
  width: 50px !important;
  height: 50px !important;
  border: 1px solid #8f6d00 !important;
  border-radius: 50% !important;
  color: #362700 !important;
  background: radial-gradient(
    circle at 35% 28%,
    #fff4a7,
    var(--accent) 42%,
    #9e7500 100%
  ) !important;
  box-shadow:
    0 5px 0 #584000,
    0 9px 15px rgba(0, 0, 0, 0.35),
    inset 0 2px 3px rgba(255, 255, 255, 0.55) !important;
  cursor: pointer !important;
  display: flex !important;
  flex-direction: column !important;
  align-items: center !important;
  justify-content: center !important;
  transition:
    transform 0.12s,
    box-shadow 0.12s !important;
}
.wpslot-round-bet:active,
.wpslot-round-bet.inserted {
  transform: translateY(4px) !important;
  box-shadow:
    0 1px 0 #584000,
    0 0 20px var(--glow) !important;
}
.wpslot-bet-num {
  font-size: 15px !important;
  font-weight: 950 !important;
  line-height: 1 !important;
}
.wpslot-bet-txt {
  font-size: 9px !important;
  font-weight: 950 !important;
  line-height: 1.1 !important;
}
.wpslot-center-area {
  min-width: 0 !important;
  display: flex !important;
  flex-direction: column !important;
  gap: 12px !important;
}
.wpslot-window-frame {
  position: relative !important;
  overflow: hidden !important;
  padding: 6px !important;
  border: 1px solid rgba(255, 255, 255, 0.18) !important;
  border-radius: 13px !important;
  background: #050608 !important;
  box-shadow:
    inset 0 0 20px #000,
    0 8px 24px rgba(0, 0, 0, 0.25) !important;
}
.wpslot-reels {
  display: flex !important;
  gap: 5px !important;
  height: calc(var(--row-h) * 3) !important;
}
.wpslot-reel-col {
  position: relative !important;
  flex: 1 1 0 !important;
  min-width: 0 !important;
  overflow: hidden !important;
  border-radius: 7px !important;
  background: var(--reel) !important;
  box-shadow:
    inset 0 22px 18px rgba(0, 0, 0, 0.25),
    inset 0 -22px 18px rgba(0, 0, 0, 0.25) !important;
}
.wpslot-reel-strip {
  position: absolute !important;
  inset: 0 0 auto !important;
  width: 100% !important;
  will-change: transform !important;
}
.wpslot-symbol {
  height: var(--row-h) !important;
  display: flex !important;
  align-items: center !important;
  justify-content: center !important;
  overflow: hidden !important;
  color: var(--ink) !important;
  font-size: clamp(20px, 5.8vw, 39px) !important;
  font-weight: 950 !important;
  line-height: 1 !important;
  text-shadow: 0 1px rgba(255, 255, 255, 0.7) !important;
  backface-visibility: hidden !important;
}
.wpslot-payline {
  position: absolute !important;
  z-index: 5 !important;
  top: 50% !important;
  left: 4px !important;
  right: 4px !important;
  height: 2px !important;
  pointer-events: none !important;
  background: linear-gradient(
    90deg,
    transparent,
    var(--accent-2) 8%,
    var(--accent-2) 92%,
    transparent
  ) !important;
  box-shadow: 0 0 8px var(--accent-2) !important;
}
.wpslot-payline::before,
.wpslot-payline::after {
  content: "" !important;
  position: absolute !important;
  top: -5px !important;
  width: 0 !important;
  height: 0 !important;
  border-top: 6px solid transparent !important;
  border-bottom: 6px solid transparent !important;
}
.wpslot-payline::before {
  left: 0 !important;
  border-left: 8px solid var(--accent-2) !important;
}
.wpslot-payline::after {
  right: 0 !important;
  border-right: 8px solid var(--accent-2) !important;
}
.wpslot-glass {
  position: absolute !important;
  z-index: 4 !important;
  inset: 0 !important;
  pointer-events: none !important;
  background: linear-gradient(
    115deg,
    rgba(255, 255, 255, 0.12),
    transparent 24%,
    transparent 70%,
    rgba(255, 255, 255, 0.04)
  ) !important;
}
.wpslot-stop-buttons {
  display: flex !important;
  gap: 5px !important;
  padding: 0 6px !important;
}
.wpslot-btn-col {
  flex: 1 1 0 !important;
  min-width: 0 !important;
  display: flex !important;
  justify-content: center !important;
}
.wpslot-round-stop {
  width: clamp(32px, 7vw, 46px) !important;
  height: clamp(32px, 7vw, 46px) !important;
  border: 2px solid rgba(255, 255, 255, 0.9) !important;
  border-radius: 50% !important;
  background: radial-gradient(
    circle at 35% 27%,
    #ff9b9b,
    #f02c42 43%,
    #7c0718
  ) !important;
  box-shadow:
    0 4px 0 #47020d,
    0 7px 12px rgba(0, 0, 0, 0.3) !important;
  cursor: pointer !important;
  transition:
    transform 0.1s,
    filter 0.2s,
    opacity 0.2s !important;
}
.wpslot-round-stop:active:not(.all-locked) {
  transform: translateY(3px) !important;
}
.wpslot-round-stop.is-stopped {
  filter: saturate(0.25) brightness(0.6) !important;
  transform: translateY(3px) !important;
}
.wpslot-round-stop.all-locked {
  opacity: 0.38 !important;
  cursor: not-allowed !important;
}
.wpslot-lever-box {
  position: relative !important;
  width: 44px !important;
  height: 126px !important;
  padding: 0 !important;
  border: 1px solid #3d424c !important;
  border-radius: 24px !important;
  background: #090a0d !important;
  box-shadow:
    inset 0 0 10px #000,
    0 3px 10px rgba(0, 0, 0, 0.3) !important;
  cursor: pointer !important;
}
.wpslot-lever-shaft {
  position: absolute !important;
  top: 17px !important;
  left: 50% !important;
  width: 8px !important;
  height: 47px !important;
  transform: translateX(-50%) !important;
  border-radius: 5px !important;
  background: linear-gradient(
    90deg,
    #353941,
    #e8ebef 40%,
    #fff 55%,
    #555a64
  ) !important;
  transition: top 0.2s cubic-bezier(0.2, 0.8, 0.3, 1) !important;
}
.wpslot-lever-ball {
  position: absolute !important;
  top: 5px !important;
  left: 50% !important;
  width: 36px !important;
  height: 36px !important;
  transform: translateX(-50%) !important;
  border: 1px solid #4c0010 !important;
  border-radius: 50% !important;
  background: radial-gradient(
    circle at 34% 28%,
    #ff9caa,
    #ed294b 45%,
    #7b0018
  ) !important;
  box-shadow:
    0 4px 8px #000,
    inset 0 2px 4px rgba(255, 255, 255, 0.4) !important;
  transition: top 0.2s cubic-bezier(0.2, 0.8, 0.3, 1) !important;
}
.wpslot-lever-box.pulled .wpslot-lever-shaft,
.wpslot-lever-box:active .wpslot-lever-shaft {
  top: 63px !important;
}
.wpslot-lever-box.pulled .wpslot-lever-ball,
.wpslot-lever-box:active .wpslot-lever-ball {
  top: 86px !important;
}
.wpslot-status-bar {
  min-height: 42px !important;
  margin-top: 15px !important;
  padding: 11px 14px !important;
  display: flex !important;
  align-items: center !important;
  justify-content: center !important;
  border: 1px solid rgba(255, 255, 255, 0.07) !important;
  border-radius: 11px !important;
  background: rgba(0, 0, 0, 0.22) !important;
  color: var(--accent) !important;
  text-align: center !important;
  font-size: 13px !important;
  font-weight: 750 !important;
}
.wpslot-status-bar.win {
  color: #fff !important;
  background: linear-gradient(
    90deg,
    color-mix(in srgb, var(--accent-2) 34%, #111),
    color-mix(in srgb, var(--accent) 24%, #111)
  ) !important;
  animation: wpslot-winbar 0.45s ease-in-out 3 alternate !important;
}
.wpslot-footer-row {
  display: flex !important;
  justify-content: space-between !important;
  align-items: center !important;
  margin-top: 8px !important;
  padding: 0 4px !important;
  color: rgba(255, 255, 255, 0.38) !important;
  font-size: 10px !important;
}
.wpslot-text-button {
  padding: 4px !important;
  border: 0 !important;
  color: rgba(255, 255, 255, 0.46) !important;
  background: transparent !important;
  font: inherit !important;
  text-decoration: underline !important;
  cursor: pointer !important;
}
.wpslot-fx {
  position: absolute !important;
  z-index: 20 !important;
  inset: 0 !important;
  overflow: hidden !important;
  pointer-events: none !important;
}
.wpslot-confetti {
  position: absolute !important;
  top: -20px !important;
  width: 8px !important;
  height: 14px !important;
  border-radius: 2px !important;
  animation: wpslot-fall var(--fall, 1.4s) cubic-bezier(0.18, 0.75, 0.35, 1)
    forwards !important;
}
@keyframes wpslot-pulse {
  to {
    transform: scale(1.035);
    filter: brightness(1.12);
  }
}
@keyframes wpslot-winbar {
  to {
    filter: brightness(1.45);
  }
}
@keyframes wpslot-fall {
  to {
    transform: translate3d(var(--drift, 0), 620px, 0) rotate(720deg);
    opacity: 0.15;
  }
}
@media (max-width: 520px) {
  .wpslot-app {
    padding: 2px !important;
  }
  .wpslot-cabinet {
    border-radius: 17px !important;
    padding: 11px !important;
  }
  .wpslot-stage {
    grid-template-columns: 40px minmax(0, 1fr) 40px !important;
    gap: 5px !important;
  }
  .wpslot-round-bet {
    width: 39px !important;
    height: 39px !important;
  }
  .wpslot-lever-box {
    width: 36px !important;
    height: 108px !important;
  }
  .wpslot-lever-ball {
    width: 31px !important;
    height: 31px !important;
  }
  .wpslot-lever-box.pulled .wpslot-lever-ball,
  .wpslot-lever-box:active .wpslot-lever-ball {
    top: 73px !important;
  }
  .wpslot-lever-box.pulled .wpslot-lever-shaft,
  .wpslot-lever-box:active .wpslot-lever-shaft {
    top: 54px !important;
  }
  .wpslot-score-display {
    min-width: 65px !important;
  }
  .wpslot-lamp {
    display: none !important;
  }
  .wpslot-reels {
    gap: 3px !important;
  }
  .wpslot-stop-buttons {
    gap: 3px !important;
  }
  .wpslot-symbol {
    font-size: clamp(17px, 7.2vw, 31px) !important;
  }
}
@media (max-width: 380px) {
  .wpslot-cabinet {
    padding: 9px !important;
  }
  .wpslot-stage {
    grid-template-columns: 34px minmax(0, 1fr) 34px !important;
    gap: 4px !important;
  }
  .wpslot-round-bet {
    width: 34px !important;
    height: 34px !important;
  }
  .wpslot-lever-box {
    width: 32px !important;
  }
  .wpslot-round-stop {
    width: clamp(24px, 7vw, 32px) !important;
    height: clamp(24px, 7vw, 32px) !important;
  }
  .wpslot-top-panel {
    padding: 9px !important;
  }
  .wpslot-score-display {
    min-width: 60px !important;
  }
  .wpslot-role-chip {
    padding: 4px 7px !important;
  }
}
@media (prefers-reduced-motion: reduce) {
  .wpslot-app * {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
  }
}

.wpsys-panel {
  --sys-accent: #f2c94c;
  --sys-accent-2: #ff7a45;
  --sys-card: #171e29;
  --sys-field: #0d131c;
  --sys-border: #2b3544;
  --sys-text: #f5f7fa;
  --sys-muted: #9ba7b8;
  box-sizing: border-box !important;
  isolation: isolate !important;
  width: 100% !important;
  max-width: 920px !important;
  margin: 24px auto !important;
  padding: clamp(16px, 3vw, 28px) !important;
  border: 1px solid #303a49 !important;
  border-radius: 24px !important;
  background:
    radial-gradient(
      circle at 15% -10%,
      rgba(242, 201, 76, 0.13),
      transparent 30%
    ),
    linear-gradient(145deg, #171d28, #10141c 65%) !important;
  color: var(--sys-text) !important;
  color-scheme: dark !important;
  box-shadow: 0 22px 64px rgba(4, 8, 14, 0.35) !important;
  font-family:
    Inter,
    ui-sans-serif,
    -apple-system,
    BlinkMacSystemFont,
    "Segoe UI",
    "Noto Sans JP",
    sans-serif !important;
}
.wpsys-panel,
.wpsys-panel *,
.wpsys-panel *::before,
.wpsys-panel *::after {
  box-sizing: border-box !important;
}
.wpsys-panel button,
.wpsys-panel input,
.wpsys-panel select {
  font: inherit !important;
}
.wpsys-panel fieldset {
  min-width: 0 !important;
}
.wpsys-header {
  display: flex !important;
  align-items: flex-start !important;
  justify-content: space-between !important;
  gap: 18px !important;
  margin: 0 0 20px !important;
  padding: 2px 2px 18px !important;
  border-bottom: 1px solid rgba(255, 255, 255, 0.08) !important;
}
.wpsys-kicker {
  margin: 0 0 5px !important;
  color: var(--sys-accent) !important;
  font-size: 10px !important;
  font-weight: 850 !important;
  letter-spacing: 0.18em !important;
}
.wpsys-title {
  display: block !important;
  margin: 0 !important;
  padding: 0 !important;
  border: 0 !important;
  background: none !important;
  color: #fff !important;
  font-size: clamp(23px, 4vw, 32px) !important;
  font-weight: 900 !important;
  line-height: 1.18 !important;
  box-shadow: none !important;
}
.wpsys-header p:not(.wpsys-kicker) {
  margin: 8px 0 0 !important;
  color: var(--sys-muted) !important;
  font-size: 13px !important;
  line-height: 1.65 !important;
}
.wpsys-save-state {
  flex: 0 0 auto !important;
  max-width: min(100%, 360px) !important;
  padding: 7px 11px !important;
  border: 1px solid #4a3b19 !important;
  border-radius: 999px !important;
  background: #292316 !important;
  color: #f8d86a !important;
  font-size: 11px !important;
  font-weight: 800 !important;
}
.wpsys-save-state.saved {
  border-color: #245c43 !important;
  background: #142d23 !important;
  color: #7ee2aa !important;
}
.wpsys-save-state.error {
  border-color: #71323a !important;
  background: #351a20 !important;
  color: #ff9aa8 !important;
}
.wpsys-presets {
  display: grid !important;
  grid-template-columns: repeat(3, 1fr) !important;
  gap: 10px !important;
  margin: 0 0 16px !important;
}
.wpsys-preset {
  display: flex !important;
  align-items: center !important;
  gap: 10px !important;
  min-width: 0 !important;
  padding: 12px !important;
  border: 1px solid var(--sys-border) !important;
  border-radius: 14px !important;
  background: #151c27 !important;
  color: var(--sys-text) !important;
  text-align: left !important;
  box-shadow: none !important;
  cursor: pointer !important;
  transition:
    transform 0.16s,
    border-color 0.16s,
    background 0.16s,
    box-shadow 0.16s !important;
}
.wpsys-preset:hover {
  transform: translateY(-1px) !important;
  border-color: #9c8540 !important;
  background: #1a2330 !important;
}
.wpsys-preset.active,
.wpsys-preset[aria-pressed="true"] {
  border-color: var(--sys-accent) !important;
  background: linear-gradient(145deg, #25251e, #191f29) !important;
  box-shadow: 0 0 0 3px rgba(242, 201, 76, 0.12) !important;
}
.wpsys-preset-icon {
  flex: 0 0 40px !important;
  width: 40px !important;
  height: 40px !important;
  display: grid !important;
  place-items: center !important;
  border: 1px solid rgba(242, 201, 76, 0.18) !important;
  border-radius: 11px !important;
  background: #24271f !important;
  color: var(--sys-accent) !important;
  font-size: 21px !important;
  font-weight: 900 !important;
}
.wpsys-preset b,
.wpsys-preset small {
  display: block !important;
  overflow: hidden !important;
  text-overflow: ellipsis !important;
  white-space: nowrap !important;
}
.wpsys-preset b {
  color: #fff !important;
  font-size: 13px !important;
}
.wpsys-preset small {
  margin-top: 3px !important;
  color: var(--sys-muted) !important;
  font-size: 10px !important;
}
.wpsys-grid {
  display: grid !important;
  grid-template-columns: 1fr 1fr !important;
  gap: 14px !important;
}
.wpsys-card {
  min-width: 0 !important;
  margin: 0 !important;
  padding: 18px !important;
  border: 1px solid var(--sys-border) !important;
  border-radius: 17px !important;
  background: linear-gradient(155deg, #192230, var(--sys-card)) !important;
  color: var(--sys-text) !important;
  box-shadow: inset 0 1px rgba(255, 255, 255, 0.035) !important;
}
.wpsys-card-wide {
  grid-column: 1 / -1 !important;
}
.wpsys-card legend {
  float: none !important;
  width: auto !important;
  margin: 0 !important;
  padding: 0 8px !important;
  border: 0 !important;
  background: transparent !important;
  color: #fff !important;
  font-size: 13px !important;
  font-weight: 850 !important;
  line-height: 1.3 !important;
}
.wpsys-card legend span {
  margin-right: 4px !important;
  color: var(--sys-accent) !important;
  font-size: 10px !important;
}
.wpsys-form-grid {
  display: grid !important;
  grid-template-columns: 1fr 1fr !important;
  gap: 12px !important;
}
.wpsys-field > span,
.wpsys-symbol-head,
.wpsys-range-list label > span {
  color: #c1cad6 !important;
  font-size: 11px !important;
  font-weight: 750 !important;
}
.wpsys-field > span {
  display: block !important;
  margin: 0 0 6px !important;
}
.wpsys-field small {
  color: #7f8b9d !important;
  font-weight: 500 !important;
}
.wpsys-field input,
.wpsys-field select,
.wpsys-role-row input[type="text"] {
  width: 100% !important;
  margin: 0 !important;
  border: 1px solid #344154 !important;
  border-radius: 10px !important;
  background: var(--sys-field) !important;
  color: var(--sys-text) !important;
  box-shadow: none !important;
  outline: none !important;
}
.wpsys-field input,
.wpsys-field select {
  height: 42px !important;
  padding: 0 11px !important;
}
.wpsys-field input:focus,
.wpsys-field select:focus,
.wpsys-role-row input:focus {
  border-color: var(--sys-accent) !important;
  box-shadow: 0 0 0 3px rgba(242, 201, 76, 0.13) !important;
}
.wpsys-top-gap {
  margin-top: 15px !important;
}
.wpsys-symbol-head {
  display: flex !important;
  align-items: center !important;
  justify-content: space-between !important;
  margin: 15px 0 0 !important;
}
.wpsys-symbol-head output {
  padding: 3px 8px !important;
  border-radius: 7px !important;
  background: #2b281b !important;
  color: var(--sys-accent) !important;
  font-weight: 850 !important;
}
.wpsys-help {
  margin: 6px 0 10px !important;
  color: var(--sys-muted) !important;
  font-size: 10px !important;
  line-height: 1.6 !important;
}
.wpsys-char-grid {
  display: grid !important;
  grid-template-columns: repeat(7, minmax(38px, 1fr)) !important;
  gap: 7px !important;
  direction: ltr !important;
}
.wpsys-char-cell {
  position: relative !important;
  min-width: 0 !important;
}
.wpsys-char-cell::after {
  content: attr(data-index) !important;
  position: absolute !important;
  right: 5px !important;
  bottom: 3px !important;
  color: #667286 !important;
  font-size: 8px !important;
  pointer-events: none !important;
}
.wpsys-char-cell input {
  width: 100% !important;
  height: 54px !important;
  margin: 0 !important;
  padding: 2px !important;
  border: 1px solid #39475b !important;
  border-radius: 10px !important;
  background: #0c121b !important;
  color: #fff !important;
  text-align: center !important;
  font-size: clamp(20px, 4vw, 29px) !important;
  font-weight: 850 !important;
  box-shadow: inset 0 1px 8px rgba(0, 0, 0, 0.25) !important;
  outline: none !important;
}
.wpsys-char-cell input:focus {
  border-color: var(--sys-accent) !important;
  box-shadow: 0 0 0 3px rgba(242, 201, 76, 0.14) !important;
}
.wpsys-char-cell input:placeholder-shown {
  background: #101722 !important;
}
.wpsys-range-list {
  display: grid !important;
  gap: 14px !important;
}
.wpsys-range-list label > span {
  display: flex !important;
  justify-content: space-between !important;
  margin: 0 0 6px !important;
}
.wpsys-range-list output {
  color: var(--sys-accent) !important;
  font-weight: 850 !important;
}
.wpsys-range-list input[type="range"] {
  width: 100% !important;
  height: 5px !important;
  margin: 0 !important;
  padding: 0 !important;
  border: 0 !important;
  background: transparent !important;
  accent-color: var(--sys-accent) !important;
  cursor: pointer !important;
}
.wpsys-note {
  margin: 18px 0 0 !important;
  padding: 12px !important;
  border: 1px solid #343c30 !important;
  border-radius: 11px !important;
  background: #1b211b !important;
  color: var(--sys-muted) !important;
  font-size: 10px !important;
  line-height: 1.6 !important;
}
.wpsys-note b {
  color: #e9d77a !important;
}
.wpsys-role-table {
  display: grid !important;
  gap: 7px !important;
}
.wpsys-role-row {
  display: grid !important;
  grid-template-columns: 34px minmax(100px, 1fr) minmax(110px, 1.3fr) 74px !important;
  align-items: center !important;
  gap: 7px !important;
}
.wpsys-role-row.header {
  color: #7f8b9d !important;
  font-size: 9px !important;
  font-weight: 800 !important;
  text-transform: uppercase !important;
}
.wpsys-role-row input[type="text"] {
  min-width: 0 !important;
  height: 39px !important;
  padding: 0 9px !important;
}
.wpsys-role-enabled {
  display: grid !important;
  place-items: center !important;
}
.wpsys-role-enabled input {
  width: 17px !important;
  height: 17px !important;
  margin: 0 !important;
  accent-color: var(--sys-accent) !important;
}
.wpsys-role-score {
  padding: 8px 6px !important;
  border: 1px solid #4c4220 !important;
  border-radius: 8px !important;
  background: #282318 !important;
  color: var(--sys-accent) !important;
  text-align: center !important;
  font:
    800 11px/1 ui-monospace,
    SFMono-Regular,
    Consolas,
    monospace !important;
}
.wpsys-actions {
  position: sticky !important;
  z-index: 4 !important;
  bottom: 10px !important;
  display: flex !important;
  justify-content: flex-end !important;
  gap: 9px !important;
  margin: 16px 0 0 !important;
  padding: 10px !important;
  border: 1px solid rgba(74, 85, 102, 0.82) !important;
  border-radius: 14px !important;
  background: rgba(15, 20, 29, 0.9) !important;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.26) !important;
  backdrop-filter: blur(12px) !important;
}
.wpsys-actions button {
  min-height: 42px !important;
  margin: 0 !important;
  padding: 0 16px !important;
  border-radius: 10px !important;
  font-size: 12px !important;
  font-weight: 850 !important;
  cursor: pointer !important;
}
.wpsys-secondary {
  border: 1px solid #3b4657 !important;
  background: #171e29 !important;
  color: #c8d0db !important;
}
.wpsys-primary {
  border: 1px solid #e9ba31 !important;
  background: linear-gradient(135deg, #f2c94c, #e89a2d) !important;
  color: #211801 !important;
  box-shadow: 0 8px 20px rgba(232, 154, 45, 0.2) !important;
}
@media (max-width: 650px) {
  .wpsys-panel {
    padding: 14px !important;
    border-radius: 18px !important;
  }
  .wpsys-presets,
  .wpsys-grid,
  .wpsys-form-grid {
    grid-template-columns: 1fr !important;
  }
  .wpsys-card-wide {
    grid-column: auto !important;
  }
  .wpsys-char-grid {
    grid-template-columns: repeat(7, minmax(0, 1fr)) !important;
    gap: 4px !important;
  }
  .wpsys-char-cell input {
    height: 46px !important;
    font-size: clamp(16px, 6vw, 25px) !important;
  }
  .wpsys-role-row {
    grid-template-columns: 24px minmax(58px, 0.8fr) minmax(70px, 1.1fr) 48px !important;
    gap: 4px !important;
  }
  .wpsys-role-row input[type="text"] {
    padding: 0 5px !important;
    font-size: 10px !important;
  }
  .wpsys-role-score {
    padding: 7px 3px !important;
    font-size: 10px !important;
  }
  .wpsys-actions {
    justify-content: stretch !important;
  }
  .wpsys-actions button {
    flex: 1 !important;
    padding: 0 8px !important;
  }
  .wpsys-header {
    display: block !important;
  }
  .wpsys-save-state {
    display: inline-block !important;
    margin-top: 10px !important;
  }
}
ソース:JavaScript
/* MOJI-SLOT */
(function () {
  "use strict";
  function bootMojiSlot() {
    var RULES = {
      symbolMin: 3,
      symbolMax: 7,
      reelMin: 3,
      reelMax: 7,
      roleCount: 5,
      roleScores: [1000, 200, 100, 50, 25],
      roleEffects: ["jackpot", "big", "big", "small", "small"],
    };
    (function () {
      "use strict";
      var CONFIG_KEY = "wpslotModernConfigV1";
      var SCORE_KEY = "wpslotModernScoreV1";
      var root = document.getElementById("wpslot-app");
      if (!root || root.dataset.ready === "1") return;
      root.dataset.ready = "1";

      var fallback = {
        title: "MOJI-SLOT",
        preset: "chinsuko",
        theme: "okinawa",
        symbols: ["ち", "ん", "す", "こ", "う"],
        reelCount: 5,
        slotSetting: 6,
        rowHeight: 64,
        chanceProbability: 0.47,
        forceRoleProbability: 0.72,
        spinMin: 27,
        spinMax: 33,
        stopDuration: 720,
        maxSlip: 4,
        soundVolume: 0.12,
        roles: [
          {
            id: "perfect",
            name: "ちんすこう",
            pattern: "ちんすこう",
            score: 500,
            enabled: true,
            effect: "jackpot",
          },
          {
            id: "chinchin",
            name: "ちんちん",
            pattern: "ちんちん",
            score: 200,
            enabled: true,
            effect: "big",
          },
          {
            id: "chinko",
            name: "ちんこ",
            pattern: "ちんこ",
            score: 100,
            enabled: true,
            effect: "big",
          },
          {
            id: "unko",
            name: "うんこ",
            pattern: "うんこ",
            score: 50,
            enabled: true,
            effect: "small",
          },
          {
            id: "chin",
            name: "ちん",
            pattern: "ちん",
            score: 20,
            enabled: true,
            effect: "small",
          },
        ],
      };

      var el = {
        reels: document.getElementById("wpslot-reels"),
        buttons: document.getElementById("wpslot-stop-buttons"),
        bet: document.getElementById("wpslot-bet-btn"),
        lever: document.getElementById("wpslot-lever"),
        lamp: document.getElementById("wpslot-lamp"),
        msg: document.getElementById("wpslot-msg"),
        logo: document.getElementById("wpslot-logo"),
        score: document.getElementById("wpslot-score"),
        scoreReset: document.getElementById("wpslot-score-reset"),
        paytable: document.getElementById("wpslot-paytable"),
        setting: document.getElementById("wpslot-setting-label"),
        fx: document.getElementById("wpslot-fx"),
      };
      if (!el.reels || !el.buttons || !el.bet || !el.lever) return;

      var segmenter =
        typeof Intl !== "undefined" && Intl.Segmenter
          ? new Intl.Segmenter("ja", { granularity: "grapheme" })
          : null;
      function decodeEntities(value) {
        var text = String(value == null ? "" : value);
        if (text.indexOf("&") < 0) return text;
        var area = document.createElement("textarea");
        area.innerHTML = text;
        return area.value;
      }
      function chars(value) {
        value = decodeEntities(value);
        return segmenter
          ? Array.from(segmenter.segment(value), function (x) {
              return x.segment;
            })
          : Array.from(value);
      }
      function number(v, min, max, d) {
        v = Number(v);
        return Number.isFinite(v) ? Math.min(max, Math.max(min, v)) : d;
      }
      function clone(v) {
        return JSON.parse(JSON.stringify(v));
      }
      function loadConfig() {
        try {
          var saved = JSON.parse(localStorage.getItem(CONFIG_KEY) || "null");
          if (!saved) return clone(fallback);
          var merged = Object.assign({}, fallback, saved);
          merged.symbols = Array.isArray(saved.symbols)
            ? saved.symbols.slice(0, RULES.symbolMax)
            : clone(fallback.symbols);
          merged.roles = Array.isArray(saved.roles)
            ? saved.roles
            : clone(fallback.roles);
          return merged;
        } catch (e) {
          return clone(fallback);
        }
      }

      var cfg = loadConfig(),
        phase = "IDLE",
        reels = [],
        anim = 0,
        score = number(localStorage.getItem(SCORE_KEY), 0, 999999999, 0);
      var selectedRole = null,
        forcedTarget = [],
        audioCtx = null,
        spinToken = 0,
        pendingConfig = null;
      function setMessage(text, isWin) {
        el.msg.textContent = text;
        el.msg.classList.toggle("win", !!isWin);
      }
      function saveScore() {
        try {
          localStorage.setItem(SCORE_KEY, String(score));
        } catch (e) {}
        el.score.textContent = score.toLocaleString("ja-JP");
      }
      function tone(freq, type, duration, delay) {
        if (cfg.soundVolume <= 0) return;
        window.setTimeout(function () {
          try {
            if (!audioCtx)
              audioCtx = new (
                window.AudioContext || window.webkitAudioContext
              )();
            if (audioCtx.state === "suspended") audioCtx.resume();
            var osc = audioCtx.createOscillator(),
              gain = audioCtx.createGain(),
              now = audioCtx.currentTime;
            osc.type = type;
            osc.frequency.setValueAtTime(freq, now);
            gain.gain.setValueAtTime(cfg.soundVolume, now);
            gain.gain.exponentialRampToValueAtTime(0.001, now + duration);
            osc.connect(gain);
            gain.connect(audioCtx.destination);
            osc.start(now);
            osc.stop(now + duration);
          } catch (e) {}
        }, delay || 0);
      }
      function fanfare(effect) {
        var notes =
          effect === "jackpot"
            ? [523, 659, 784, 1047, 1319, 1568]
            : effect === "big"
              ? [523, 659, 784, 1047]
              : [659, 784, 988];
        notes.forEach(function (f, i) {
          tone(f, "triangle", 0.28, i * 115);
        });
      }
      function confetti(amount) {
        el.fx.textContent = "";
        var colors = ["#ffd54a", "#ff315d", "#68f9d2", "#a878ff", "#fff"];
        for (var i = 0; i < amount; i++) {
          var p = document.createElement("i");
          p.className = "wpslot-confetti";
          p.style.left = Math.random() * 100 + "%";
          p.style.background = colors[i % colors.length];
          p.style.setProperty("--drift", (Math.random() - 0.5) * 180 + "px");
          p.style.setProperty("--fall", 1 + Math.random() * 1.1 + "s");
          p.style.animationDelay = Math.random() * 0.28 + "s";
          el.fx.appendChild(p);
        }
        window.setTimeout(function () {
          el.fx.textContent = "";
        }, 2500);
      }
      function validRoles() {
        return (cfg.roles || [])
          .filter(function (role) {
            var p = chars(role.pattern || "");
            return (
              role.enabled !== false &&
              p.length > 0 &&
              p.length <= cfg.reelCount &&
              p.every(function (c) {
                return cfg.symbols.indexOf(c) > -1;
              })
            );
          })
          .sort(function (a, b) {
            return Number(b.score) - Number(a.score);
          });
      }
      function renderPaytable() {
        el.paytable.textContent = "";
        validRoles().forEach(function (role) {
          var chip = document.createElement("span");
          chip.className = "wpslot-role-chip";
          chip.append(document.createTextNode(role.pattern + " "));
          var b = document.createElement("b");
          b.textContent = "+" + role.score;
          chip.appendChild(b);
          el.paytable.appendChild(chip);
        });
      }
      function applyConfig(next, announce) {
        if (phase === "SPINNING") {
          pendingConfig = next;
          if (announce) setMessage("回転終了後に設定を反映します");
          return;
        }
        cfg = Object.assign({}, fallback, next || {});
        cfg.symbols = (
          Array.isArray(cfg.symbols)
            ? cfg.symbols.reduce(function (all, item) {
                return all.concat(chars(item));
              }, [])
            : chars(cfg.symbols)
        )
          .filter(Boolean)
          .slice(0, RULES.symbolMax);
        if (cfg.symbols.length < RULES.symbolMin)
          cfg.symbols = clone(fallback.symbols);
        cfg.roles = (Array.isArray(cfg.roles) ? cfg.roles : fallback.roles).map(
          function (role) {
            var clean = Object.assign({}, role);
            clean.pattern = chars(clean.pattern || "")
              .slice(0, RULES.symbolMax)
              .join("");
            return clean;
          },
        );
        cfg.reelCount = Math.round(
          number(
            cfg.reelCount,
            RULES.reelMin,
            RULES.reelMax,
            cfg.symbols.length,
          ),
        );
        cfg.rowHeight = Math.round(number(cfg.rowHeight, 52, 78, 64));
        cfg.spinMin = number(cfg.spinMin, 12, 55, 27);
        cfg.spinMax = number(cfg.spinMax, cfg.spinMin, 65, 33);
        cfg.stopDuration = number(cfg.stopDuration, 260, 1600, 720);
        cfg.maxSlip = Math.round(number(cfg.maxSlip, 0, 8, 4));
        cfg.soundVolume = number(cfg.soundVolume, 0, 0.3, 0.12);
        cfg.chanceProbability = number(cfg.chanceProbability, 0, 1, 0.47);
        cfg.forceRoleProbability = number(cfg.forceRoleProbability, 0, 1, 0.72);
        root.dataset.theme =
          ["okinawa", "classic", "neon"].indexOf(cfg.theme) > -1
            ? cfg.theme
            : "okinawa";
        root.style.setProperty("--row-h", cfg.rowHeight + "px");
        el.logo.textContent = cfg.title || "MOJI-SLOT";
        el.setting.textContent =
          "設定 " + Math.round(number(cfg.slotSetting, 1, 6, 6));
        renderPaytable();
        setupReels();
        if (announce) setMessage("システム設定を反映しました");
      }
      function symbolCycle(reelIndex, count) {
        var out = [];
        for (var i = 0; i < count; i++)
          out.push(cfg.symbols[(i * 3 + reelIndex * 2) % cfg.symbols.length]);
        for (var j = out.length - 1; j > 0; j--) {
          var k = Math.floor(Math.random() * (j + 1)),
            t = out[j];
          out[j] = out[k];
          out[k] = t;
        }
        cfg.symbols.forEach(function (s, idx) {
          out[(idx * 3 + reelIndex) % out.length] = s;
        });
        return out;
      }
      function setupReels() {
        spinToken++;
        cancelAnimationFrame(anim);
        el.reels.textContent = "";
        el.buttons.textContent = "";
        reels = [];
        phase = "IDLE";
        var baseCount = Math.max(28, cfg.symbols.length * 7);
        for (var i = 0; i < cfg.reelCount; i++)
          (function (index) {
            var col = document.createElement("div"),
              strip = document.createElement("div"),
              cycle = symbolCycle(index, baseCount);
            col.className = "wpslot-reel-col";
            strip.className = "wpslot-reel-strip";
            cycle.concat(cycle, cycle.slice(0, 7)).forEach(function (s) {
              var item = document.createElement("div");
              item.className = "wpslot-symbol";
              item.textContent = s;
              strip.appendChild(item);
            });
            col.appendChild(strip);
            el.reels.appendChild(col);
            var wrap = document.createElement("div"),
              btn = document.createElement("button");
            wrap.className = "wpslot-btn-col";
            btn.type = "button";
            btn.disabled = true;
            btn.className = "wpslot-round-stop all-locked";
            btn.setAttribute("aria-label", index + 1 + "番リールを停止");
            btn.addEventListener("click", function () {
              smartStop(index);
            });
            wrap.appendChild(btn);
            el.buttons.appendChild(wrap);
            var start = Math.floor(Math.random() * baseCount),
              startPos = -(baseCount + start) * cfg.rowHeight;
            strip.style.transform = "translate3d(0," + startPos + "px,0)";
            reels.push({
              index: index,
              strip: strip,
              btn: btn,
              cycle: cycle,
              count: baseCount,
              pos: startPos,
              speed: 0,
              state: "stopped",
              stopAt: 0,
              startSpeed: 0,
              target: null,
              snapTimer: 0,
            });
          })(i);
        el.bet.classList.remove("inserted");
        el.lamp.classList.remove("gogo");
        setMessage("BETボタン または レバー を押してください");
      }
      function input() {
        if (phase === "IDLE") {
          phase = "BET";
          el.bet.classList.add("inserted");
          tone(700, "square", 0.07);
          setMessage("【BET完了】もう一度押すか、レバーでスタート!");
          return;
        }
        if (phase === "BET") startSpin();
      }
      function chooseTarget() {
        selectedRole = null;
        forcedTarget = [];
        var roles = validRoles();
        if (!roles.length || Math.random() >= cfg.forceRoleProbability) return;
        var totalWeight = roles.reduce(function (total, role) {
          return total + 1 / Math.max(1, Number(role.score) || 1);
        }, 0);
        var draw = Math.random() * totalWeight;
        selectedRole = roles[roles.length - 1];
        roles.some(function (role) {
          draw -= 1 / Math.max(1, Number(role.score) || 1);
          if (draw > 0) return false;
          selectedRole = role;
          return true;
        });
        var p = chars(selectedRole.pattern),
          offset = Math.floor(Math.random() * (cfg.reelCount - p.length + 1));
        for (var i = 0; i < cfg.reelCount; i++)
          forcedTarget[i] =
            cfg.symbols[Math.floor(Math.random() * cfg.symbols.length)];
        p.forEach(function (c, j) {
          forcedTarget[offset + j] = c;
        });
      }
      function startSpin() {
        phase = "SPINNING";
        root.setAttribute("aria-busy", "true");
        spinToken++;
        var token = spinToken;
        el.bet.classList.remove("inserted");
        el.msg.classList.remove("win");
        el.lamp.classList.remove("gogo");
        el.lever.classList.add("pulled");
        window.setTimeout(function () {
          el.lever.classList.remove("pulled");
        }, 220);
        chooseTarget();
        var chance = Math.random() < cfg.chanceProbability || !!selectedRole;
        if (chance)
          window.setTimeout(function () {
            if (phase === "SPINNING" && token === spinToken) {
              el.lamp.classList.add("gogo");
              tone(1280, "sawtooth", 0.24);
            }
          }, 140);
        reels.forEach(function (r) {
          clearTimeout(r.snapTimer);
          r.state = "spinning";
          r.speed = cfg.spinMin + Math.random() * (cfg.spinMax - cfg.spinMin);
          r.btn.disabled = false;
          r.btn.classList.remove("all-locked", "is-stopped");
        });
        tone(310, "sine", 0.14);
        setMessage("リール回転中!好きな順番で止めてください");
        cancelAnimationFrame(anim);
        anim = requestAnimationFrame(engine);
      }
      function engine(now) {
        var moving = false;
        reels.forEach(function (r) {
          if (r.state === "spinning") {
            moving = true;
            r.pos += r.speed;
          } else if (r.state === "slowing") {
            moving = true;
            var p = Math.min(1, (now - r.stopAt) / cfg.stopDuration),
              ease = (1 - p) * (1 - p);
            r.pos += Math.max(0.35, r.startSpeed * ease);
            if (p >= 1) beginSnap(r);
          }
          var loop = r.count * cfg.rowHeight;
          while (r.pos >= -loop) r.pos -= loop;
          while (r.pos < -2 * loop) r.pos += loop;
          r.strip.style.transform = "translate3d(0," + r.pos + "px,0)";
        });
        if (moving) anim = requestAnimationFrame(engine);
      }
      function smartStop(index) {
        if (phase !== "SPINNING") return;
        var r = reels[index];
        if (!r || r.state !== "spinning")
          r = reels.find(function (x) {
            return x.state === "spinning";
          });
        if (!r) return;
        r.state = "slowing";
        r.stopAt = performance.now();
        r.startSpeed = r.speed;
        r.target = forcedTarget[r.index] || null;
        r.btn.classList.add("is-stopped");
        tone(470, "square", 0.07);
      }
      function beginSnap(r) {
        if (r.state !== "slowing") return;
        r.state = "snapping";
        var top = Math.floor(-r.pos / cfg.rowHeight),
          slip = 0;
        if (r.target) {
          for (var s = 0; s <= cfg.maxSlip; s++) {
            if (
              r.cycle[(((top - s + 1) % r.count) + r.count) % r.count] ===
              r.target
            ) {
              slip = s;
              break;
            }
          }
        }
        r.pos = -(top - slip) * cfg.rowHeight;
        r.strip.style.transition =
          "transform 180ms cubic-bezier(.2,.85,.3,1.05)";
        r.strip.style.transform = "translate3d(0," + r.pos + "px,0)";
        r.snapTimer = window.setTimeout(function () {
          var loop = r.count * cfg.rowHeight;
          while (r.pos >= -loop) r.pos -= loop;
          while (r.pos < -2 * loop) r.pos += loop;
          r.strip.style.transition = "none";
          r.strip.style.transform = "translate3d(0," + r.pos + "px,0)";
          r.state = "stopped";
          checkStopped();
        }, 195);
      }
      function resultString() {
        return reels
          .map(function (r) {
            var top =
              ((Math.round(-r.pos / cfg.rowHeight) % r.count) + r.count) %
              r.count;
            return r.cycle[(top + 1) % r.count];
          })
          .join("");
      }
      function checkStopped() {
        if (
          !reels.every(function (r) {
            return r.state === "stopped";
          })
        )
          return;
        phase = "IDLE";
        root.setAttribute("aria-busy", "false");
        reels.forEach(function (r) {
          r.btn.disabled = true;
          r.btn.classList.add("all-locked");
        });
        var result = resultString();
        var role = validRoles().find(function (x) {
          return result.indexOf(x.pattern) > -1;
        });
        if (role) {
          var gain = Math.max(0, Math.round(Number(role.score) || 0));
          score += gain;
          saveScore();
          el.lamp.classList.add("gogo");
          setMessage(
            "【" + role.name + "】 " + result + "  +" + gain + " POINT!",
            true,
          );
          fanfare(role.effect);
          confetti(role.effect === "jackpot" ? 54 : 32);
        } else {
          setMessage("停止結果:「" + result + "」 次のBETへ");
          tone(210, "sine", 0.12);
        }
        if (pendingConfig) {
          var queued = pendingConfig;
          pendingConfig = null;
          window.setTimeout(function () {
            applyConfig(queued, true);
          }, 900);
        }
      }
      el.bet.addEventListener("click", input);
      el.lever.addEventListener("click", input);
      el.scoreReset.addEventListener("click", function () {
        if (phase === "SPINNING") return;
        score = 0;
        saveScore();
        setMessage("スコアを0に戻しました");
      });
      window.addEventListener("wpslot:config-changed", function (e) {
        applyConfig(e.detail || loadConfig(), true);
      });
      window.addEventListener("storage", function (e) {
        if (e.key === CONFIG_KEY) applyConfig(loadConfig(), true);
      });
      saveScore();
      applyConfig(cfg, false);
    })();

    (function () {
      "use strict";
      var KEY = "wpslotModernConfigV1",
        root = document.getElementById("wpslot-system-settings");
      if (!root || root.dataset.ready === "1") return;
      root.dataset.ready = "1";
      var seg =
        typeof Intl !== "undefined" && Intl.Segmenter
          ? new Intl.Segmenter("ja", { granularity: "grapheme" })
          : null;
      function decodeEntities(v) {
        var text = String(v == null ? "" : v);
        if (text.indexOf("&") < 0) return text;
        var area = document.createElement("textarea");
        area.innerHTML = text;
        return area.value;
      }
      function chars(v) {
        v = decodeEntities(v);
        return seg
          ? Array.from(seg.segment(v), function (x) {
              return x.segment;
            })
          : Array.from(v);
      }
      function clone(v) {
        return JSON.parse(JSON.stringify(v));
      }
      var cherry = String.fromCodePoint(0x1f352),
        banana = String.fromCodePoint(0x1f34c),
        bell = String.fromCodePoint(0x1f514),
        star = String.fromCodePoint(0x2b50),
        seven = String.fromCodePoint(0x37, 0xfe0f, 0x20e3),
        gear = String.fromCodePoint(0x2699);
      var classicIcon = root.querySelector(
        '[data-preset="classic"] .wpsys-preset-icon',
      );
      if (classicIcon) classicIcon.textContent = cherry;
      var customIcon = root.querySelector(
        '[data-preset="custom"] .wpsys-preset-icon',
      );
      if (customIcon) customIcon.textContent = gear;
      var presets = {
        chinsuko: {
          title: "MOJI-SLOT",
          preset: "chinsuko",
          theme: "okinawa",
          symbols: ["ち", "ん", "す", "こ", "う"],
          reelCount: 5,
          slotSetting: 6,
          rowHeight: 64,
          chanceProbability: 0.47,
          forceRoleProbability: 0.72,
          spinMin: 27,
          spinMax: 33,
          stopDuration: 720,
          maxSlip: 4,
          soundVolume: 0.12,
          roles: [
            {
              id: "perfect",
              name: "ちんすこう",
              pattern: "ちんすこう",
              score: 500,
              enabled: true,
              effect: "jackpot",
            },
            {
              id: "chinchin",
              name: "ちんちん",
              pattern: "ちんちん",
              score: 200,
              enabled: true,
              effect: "big",
            },
            {
              id: "chinko",
              name: "ちんこ",
              pattern: "ちんこ",
              score: 100,
              enabled: true,
              effect: "big",
            },
            {
              id: "unko",
              name: "うんこ",
              pattern: "うんこ",
              score: 50,
              enabled: true,
              effect: "small",
            },
            {
              id: "chin",
              name: "ちん",
              pattern: "ちん",
              score: 20,
              enabled: true,
              effect: "small",
            },
          ],
        },
        classic: {
          title: "LUCKY SEVEN",
          preset: "classic",
          theme: "classic",
          symbols: [cherry, banana, bell, star, seven],
          reelCount: 3,
          slotSetting: 3,
          rowHeight: 68,
          chanceProbability: 0.28,
          forceRoleProbability: 0.45,
          spinMin: 30,
          spinMax: 38,
          stopDuration: 860,
          maxSlip: 4,
          soundVolume: 0.14,
          roles: [
            {
              id: "seven",
              name: "JACKPOT",
              pattern: seven.repeat(3),
              score: 1000,
              enabled: true,
              effect: "jackpot",
            },
            {
              id: "cherry",
              name: "CHERRY",
              pattern: cherry.repeat(3),
              score: 200,
              enabled: true,
              effect: "big",
            },
            {
              id: "bell",
              name: "BELL",
              pattern: bell.repeat(3),
              score: 100,
              enabled: true,
              effect: "big",
            },
            {
              id: "banana",
              name: "BANANA",
              pattern: banana.repeat(3),
              score: 50,
              enabled: true,
              effect: "small",
            },
            {
              id: "star",
              name: "STAR",
              pattern: star.repeat(3),
              score: 25,
              enabled: true,
              effect: "small",
            },
          ],
        },
      };
      var e = {
        title: root.querySelector("#wpsys-machine-title"),
        theme: root.querySelector("#wpsys-theme"),
        chars: root.querySelector("#wpsys-char-grid"),
        count: root.querySelector("#wpsys-symbol-count"),
        reels: root.querySelector("#wpsys-reel-count"),
        setting: root.querySelector("#wpsys-slot-setting"),
        roles: root.querySelector("#wpsys-role-table"),
        state: root.querySelector("#wpsys-save-state"),
        apply: root.querySelector("#wpsys-apply"),
        reload: root.querySelector("#wpsys-reload"),
      };
      var rangeDefs = {
        chance: ["#wpsys-chance", "#wpsys-chance-out", "%"],
        force: ["#wpsys-force", "#wpsys-force-out", "%"],
        speedMin: ["#wpsys-speed-min", "#wpsys-speed-min-out", " px/f"],
        speedMax: ["#wpsys-speed-max", "#wpsys-speed-max-out", " px/f"],
        stop: ["#wpsys-stop", "#wpsys-stop-out", " ms"],
        slip: ["#wpsys-slip", "#wpsys-slip-out", " コマ"],
        row: ["#wpsys-row", "#wpsys-row-out", " px"],
        volume: ["#wpsys-volume", "#wpsys-volume-out", "%"],
      };
      Object.keys(rangeDefs).forEach(function (k) {
        var d = rangeDefs[k];
        e[k] = root.querySelector(d[0]);
        e[k + "Out"] = root.querySelector(d[1]);
        e[k].addEventListener("input", function () {
          e[k + "Out"].textContent = e[k].value + d[2];
          dirty();
        });
      });
      var charInputs = [],
        currentPreset = "custom";
      for (var i = 0; i < RULES.symbolMax; i++)
        (function (index) {
          var wrap = document.createElement("label"),
            inp = document.createElement("input");
          wrap.className = "wpsys-char-cell";
          wrap.dataset.index = String(index + 1);
          inp.type = "text";
          inp.inputMode = "text";
          inp.placeholder = "·";
          inp.setAttribute("aria-label", index + 1 + "文字目");
          inp.addEventListener("input", function () {
            var a = chars(inp.value);
            inp.value = a[0] || "";
            if (a.length > 1) distribute(a, index);
            updateCount();
            dirty();
            if (inp.value && index < 6) charInputs[index + 1].focus();
          });
          inp.addEventListener("paste", function (ev) {
            var text = (ev.clipboardData || window.clipboardData).getData(
              "text",
            );
            if (text) {
              ev.preventDefault();
              distribute(chars(text), index);
              updateCount();
              dirty();
            }
          });
          inp.addEventListener("keydown", function (ev) {
            if (ev.key === "Backspace" && !inp.value && index > 0) {
              charInputs[index - 1].focus();
            }
          });
          wrap.appendChild(inp);
          e.chars.appendChild(wrap);
          charInputs.push(inp);
        })(i);
      function distribute(list, start) {
        list.slice(0, RULES.symbolMax - start).forEach(function (c, j) {
          charInputs[start + j].value = c;
        });
      }
      function symbols() {
        return charInputs
          .map(function (x) {
            return x.value;
          })
          .filter(Boolean);
      }
      function updateCount() {
        e.count.textContent = symbols().length + " / " + RULES.symbolMax;
      }
      function setState(text, type) {
        e.state.textContent = text;
        e.state.classList.toggle("saved", type === "saved");
        e.state.classList.toggle("error", type === "error");
      }
      function dirty() {
        currentPreset = "custom";
        setState("未保存", "dirty");
        root.querySelectorAll(".wpsys-preset").forEach(function (x) {
          var active = x.dataset.preset === "custom";
          x.classList.toggle("active", active);
          x.setAttribute("aria-pressed", String(active));
        });
      }
      function roleTemplate(roles) {
        e.roles.textContent = "";
        var h = document.createElement("div");
        h.className = "wpsys-role-row header";
        ["ON", "役名", "柄", "SCORE"].forEach(function (t) {
          var s = document.createElement("span");
          s.textContent = t;
          h.appendChild(s);
        });
        e.roles.appendChild(h);
        roles.forEach(function (role, index) {
          var row = document.createElement("div");
          row.className = "wpsys-role-row";
          row.dataset.id = role.id || "role" + index;
          row.dataset.effect = role.effect || RULES.roleEffects[index];
          row.dataset.score = String(role.score || RULES.roleScores[index]);
          var checkWrap = document.createElement("label");
          checkWrap.className = "wpsys-role-enabled";
          var check = document.createElement("input");
          check.type = "checkbox";
          check.checked = role.enabled !== false;
          check.setAttribute("aria-label", index + 1 + "番目の役を有効化");
          checkWrap.appendChild(check);
          var name = document.createElement("input");
          name.type = "text";
          name.maxLength = 18;
          name.value = role.name || "";
          name.setAttribute("aria-label", index + 1 + "番目の役名");
          var pattern = document.createElement("input");
          pattern.type = "text";
          pattern.value = chars(role.pattern || "").join("");
          pattern.setAttribute("aria-label", index + 1 + "番目の役柄");
          pattern.addEventListener("input", function () {
            pattern.value = chars(pattern.value)
              .slice(0, RULES.symbolMax)
              .join("");
            dirty();
          });
          var score = document.createElement("span");
          score.className = "wpsys-role-score";
          score.textContent = "+" + row.dataset.score;
          [check, name].forEach(function (x) {
            x.addEventListener("input", dirty);
          });
          row.append(checkWrap, name, pattern, score);
          e.roles.appendChild(row);
        });
      }
      function normalizeRoles(list, presetName) {
        var defaults = presets[presetName] ? presets[presetName].roles : [];
        var base =
          list && list.length
            ? list
            : defaults.length
              ? defaults
              : presets.chinsuko.roles;
        return Array.from({ length: RULES.roleCount }, function (_, i) {
          return (
            base[i] ||
            defaults[i] || {
              id: "role" + i,
              name: "役" + (i + 1),
              pattern: "",
              score: RULES.roleScores[i],
              enabled: false,
              effect: RULES.roleEffects[i],
            }
          );
        });
      }
      function setForm(c, presetName) {
        c = Object.assign({}, presets.chinsuko, c || {});
        e.title.value = c.title || "";
        e.theme.value = c.theme || "okinawa";
        charInputs.forEach(function (x, i) {
          x.value = chars((c.symbols || [])[i] || "")[0] || "";
        });
        e.reels.value = c.reelCount || 5;
        e.setting.value = c.slotSetting || 6;
        e.chance.value = Math.round((c.chanceProbability || 0) * 100);
        e.force.value = Math.round((c.forceRoleProbability || 0) * 100);
        e.speedMin.value = c.spinMin || 27;
        e.speedMax.value = c.spinMax || 33;
        e.stop.value = c.stopDuration || 720;
        e.slip.value = c.maxSlip == null ? 4 : c.maxSlip;
        e.row.value = c.rowHeight || 64;
        e.volume.value = Math.round((c.soundVolume || 0) * 100);
        Object.keys(rangeDefs).forEach(function (k) {
          e[k].dispatchEvent(new Event("input"));
        });
        roleTemplate(normalizeRoles(c.roles, presetName || c.preset));
        updateCount();
        var activePreset = presetName || c.preset || "custom";
        currentPreset = activePreset;
        root.querySelectorAll(".wpsys-preset").forEach(function (x) {
          var active = x.dataset.preset === activePreset;
          x.classList.toggle("active", active);
          x.setAttribute("aria-pressed", String(active));
        });
        setState("未保存", "dirty");
      }
      function readForm() {
        var syms = symbols();
        if (syms.length < RULES.symbolMin)
          throw new Error("リールの文字・記号は3文字以上設定してください。");
        var rc = Math.max(
          RULES.reelMin,
          Math.min(RULES.reelMax, Number(e.reels.value) || RULES.reelMin),
        );
        var roleRows = Array.from(
          e.roles.querySelectorAll(".wpsys-role-row:not(.header)"),
        );
        var roles = roleRows.map(function (row) {
          var inputs = row.querySelectorAll("input");
          return {
            id: row.dataset.id,
            name: inputs[1].value.trim() || "役",
            pattern: chars(inputs[2].value).slice(0, RULES.symbolMax).join(""),
            score: Number(row.dataset.score),
            enabled: inputs[0].checked,
            effect: row.dataset.effect,
          };
        });
        if (
          roles.some(function (r) {
            return r.enabled && !r.pattern;
          })
        )
          throw new Error("有効な役には「柄」を入力してください。");
        if (
          roles.some(function (r) {
            return r.enabled && chars(r.pattern).length > rc;
          })
        )
          throw new Error("役の柄はリール数以下にしてください。");
        if (
          roles.some(function (r) {
            return (
              r.enabled &&
              chars(r.pattern).some(function (c) {
                return syms.indexOf(c) < 0;
              })
            );
          })
        )
          throw new Error(
            "役の柄には設定済みの文字・記号だけを使用してください。",
          );
        return {
          title: e.title.value.trim() || "MOJI-SLOT",
          preset: currentPreset,
          theme: e.theme.value,
          symbols: syms,
          reelCount: rc,
          slotSetting: Number(e.setting.value),
          rowHeight: Number(e.row.value),
          chanceProbability: Number(e.chance.value) / 100,
          forceRoleProbability: Number(e.force.value) / 100,
          spinMin: Number(e.speedMin.value),
          spinMax: Math.max(Number(e.speedMin.value), Number(e.speedMax.value)),
          stopDuration: Number(e.stop.value),
          maxSlip: Number(e.slip.value),
          soundVolume: Number(e.volume.value) / 100,
          roles: roles,
        };
      }
      function load() {
        try {
          return (
            JSON.parse(localStorage.getItem(KEY) || "null") ||
            clone(presets.chinsuko)
          );
        } catch (err) {
          return clone(presets.chinsuko);
        }
      }
      root.querySelectorAll(".wpsys-preset").forEach(function (btn) {
        btn.addEventListener("click", function () {
          if (btn.dataset.preset === "custom") {
            dirty();
            return;
          }
          setForm(clone(presets[btn.dataset.preset]), btn.dataset.preset);
        });
      });
      [e.title, e.theme, e.reels].forEach(function (x) {
        x.addEventListener("input", dirty);
      });
      e.setting.addEventListener("change", function () {
        var s = Number(e.setting.value);
        e.chance.value = Math.round((0.08 + s * 0.065) * 100);
        e.force.value = Math.min(98, Math.round((0.2 + s * 0.13) * 100));
        e.chance.dispatchEvent(new Event("input"));
        e.force.dispatchEvent(new Event("input"));
        dirty();
      });
      e.apply.addEventListener("click", function () {
        try {
          var c = readForm();
          localStorage.setItem(KEY, JSON.stringify(c));
          window.dispatchEvent(
            new CustomEvent("wpslot:config-changed", { detail: c }),
          );
          setState("保存済み", "saved");
        } catch (err) {
          setState(err.message, "error");
        }
      });
      e.reload.addEventListener("click", function () {
        setForm(load(), "custom");
        setState("保存済み", "saved");
      });
      var initial = load();
      if (
        !Array.isArray(initial.roles) ||
        initial.roles.length < RULES.roleCount
      ) {
        var migrationPreset = initial.preset;
        var savedSymbols = Array.isArray(initial.symbols)
          ? initial.symbols.join("")
          : "";
        if (migrationPreset === "custom" && savedSymbols === "ちんすこう")
          migrationPreset = "chinsuko";
        if (
          migrationPreset === "custom" &&
          savedSymbols === presets.classic.symbols.join("")
        )
          migrationPreset = "classic";
        initial.roles = normalizeRoles(initial.roles, migrationPreset);
        try {
          localStorage.setItem(KEY, JSON.stringify(initial));
          window.dispatchEvent(
            new CustomEvent("wpslot:config-changed", { detail: initial }),
          );
        } catch (err) {}
      }
      setForm(initial, initial.preset || "custom");
      setState("保存済み", "saved");
    })();
  }
  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", bootMojiSlot, { once: true });
  } else {
    bootMojiSlot();
  }
})();

サポート募集中

この記事はお役に立てたでしょうか。現在、サポートを募集しています。
コーヒーを一杯 ☕ ご馳走いただけないでしょうか。

この記事が気に入ったら
フォローしてね!

この記事をシェアする

コメント

コメントする

コメントは日本語で入力してください。(スパム対策)

CAPTCHA