diff --git a/app/Http/Controllers/SlotMachineController.php b/app/Http/Controllers/SlotMachineController.php index 6e70ae7..7cadcd9 100644 --- a/app/Http/Controllers/SlotMachineController.php +++ b/app/Http/Controllers/SlotMachineController.php @@ -146,6 +146,7 @@ class SlotMachineController extends Controller } // ⑤ 写游戏日志 + $resultLabel = SlotMachineLog::resultLabel($resultType); SlotMachineLog::create([ 'user_id' => $user->id, 'reel1' => $r1, @@ -156,9 +157,23 @@ class SlotMachineController extends Controller 'payout' => $payout, ]); - // ⑥ 三个7:全服公屏广播 + // ⑥ 广播通知 + $e1 = $symbols[$r1]['emoji']; + $e2 = $symbols[$r2]['emoji']; + $e3 = $symbols[$r3]['emoji']; + if ($resultType === 'jackpot') { + // 三个7:全服公屏广播 $this->broadcastJackpot($user->username, $payout, $cost); + } elseif (in_array($resultType, ['triple_gem', 'triple', 'pair'], true)) { + // 普通中奖:仅向本人发送聊天室系统通知 + $net = $payout - $cost; + $content = "🎰 {$resultLabel}!{$e1}{$e2}{$e3} 赢得 +🪙".number_format($net).' 金币'; + $this->broadcastPersonal($user->username, $content); + } elseif ($resultType === 'curse') { + // 诅咒:通知本人 + $content = "☠️ 三骷髅诅咒!{$e1}{$e2}{$e3} 额外扣除 🪙".number_format($cost).' 金币!'; + $this->broadcastPersonal($user->username, $content); } $user->refresh(); @@ -246,4 +261,28 @@ class SlotMachineController extends Controller broadcast(new MessageSent(1, $msg)); SaveMessageJob::dispatch($msg); } + + /** + * 向特定用户发送聊天室私人系统通知(仅该用户可见)。 + * + * @param string $toUsername 接收用户名 + * @param string $content 消息内容 + */ + private function broadcastPersonal(string $toUsername, string $content): void + { + $msg = [ + 'id' => $this->chatState->nextMessageId(1), + 'room_id' => 1, + 'from_user' => '系统传音', + 'to_user' => $toUsername, + 'content' => $content, + 'is_secret' => true, + 'font_color' => '#f59e0b', + 'action' => '', + 'sent_at' => now()->toDateTimeString(), + ]; + + broadcast(new MessageSent(1, $msg)); + SaveMessageJob::dispatch($msg); + } } diff --git a/resources/views/chat/partials/slot-machine.blade.php b/resources/views/chat/partials/slot-machine.blade.php index b596f51..12dbe33 100644 --- a/resources/views/chat/partials/slot-machine.blade.php +++ b/resources/views/chat/partials/slot-machine.blade.php @@ -9,16 +9,24 @@ - 最近记录展示 --}} -{{-- ─── 老虎机悬浮按钮 ─── --}} -
- + :style="dragging ? 'cursor:grabbing;' : 'cursor:grab;'" + title="老虎机(可拖动)">🎰
{{-- ─── 老虎机主面板 ─── --}} @@ -259,15 +267,51 @@ * 老虎机悬浮按钮 Alpine 组件(检查游戏是否开启) */ function slotFab() { + const STORAGE_KEY = 'slot_fab_pos'; + const saved = JSON.parse(localStorage.getItem(STORAGE_KEY) || 'null'); return { - visible: false, + visible: false, + posX: saved?.x ?? 18, + posY: saved?.y ?? 150, + dragging: false, + _startX: 0, _startY: 0, + _origX: 0, _origY: 0, + _moved: false, + async init() { try { - const res = await fetch('/slot/info'); + const res = await fetch('/slot/info'); const data = await res.json(); this.visible = data.enabled === true; } catch {} }, + + startDrag(e) { + this.dragging = true; + this._moved = false; + this._startX = e.clientX; + this._startY = e.clientY; + this._origX = this.posX; + this._origY = this.posY; + e.currentTarget.setPointerCapture?.(e.pointerId); + }, + + onDrag(e) { + if (!this.dragging) return; + const dx = e.clientX - this._startX; + const dy = e.clientY - this._startY; + if (Math.abs(dx) > 3 || Math.abs(dy) > 3) this._moved = true; + this.posX = Math.max(4, Math.min(window.innerWidth - 60, this._origX - dx)); + this.posY = Math.max(4, Math.min(window.innerHeight - 60, this._origY + dy)); + }, + + endDrag(e) { + if (!this.dragging) return; + this.dragging = false; + localStorage.setItem(STORAGE_KEY, JSON.stringify({ x: this.posX, y: this.posY })); + if (!this._moved) this.openPanel(); + }, + openPanel() { const panel = document.getElementById('slot-panel'); if (panel) Alpine.$data(panel).open();