迁移老虎机游戏脚本
This commit is contained in:
@@ -289,260 +289,4 @@
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
/**
|
||||
* 老虎机悬浮按钮 Alpine 组件(检查游戏是否开启)
|
||||
*/
|
||||
function slotFab() {
|
||||
const STORAGE_KEY = 'slot_fab_pos';
|
||||
const saved = JSON.parse(localStorage.getItem(STORAGE_KEY) || 'null');
|
||||
return {
|
||||
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 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();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 老虎机主面板 Alpine 组件
|
||||
*/
|
||||
function slotPanel() {
|
||||
// 所有图案的 emoji 数组,与服务端权重一致(用于转动时随机展示)
|
||||
const ALL_EMOJIS = ['🍒', '🍋', '🍊', '🍇', '🔔', '💎', '💀', '7️⃣'];
|
||||
|
||||
return {
|
||||
show: false,
|
||||
|
||||
// 配置
|
||||
costPerSpin: 100,
|
||||
dailyLimit: 0,
|
||||
remaining: null,
|
||||
balance: 0,
|
||||
|
||||
// 转轮状态
|
||||
spinning: false,
|
||||
reel1Stopped: false,
|
||||
reel2Stopped: false,
|
||||
reel3Stopped: false,
|
||||
spinEmojis: ['🎰', '🎰', '🎰'],
|
||||
resultEmojis: ['❓', '❓', '❓'],
|
||||
|
||||
// 结果
|
||||
resultType: '',
|
||||
resultLabel: '',
|
||||
netChange: 0,
|
||||
|
||||
// 历史
|
||||
history: [],
|
||||
|
||||
// 动画定时器
|
||||
_spinInterval: null,
|
||||
_stopTimers: [],
|
||||
|
||||
/**
|
||||
* Alpine 初始化: 监听 show 变化自动加载数据(解决从游戏大厅入口不调用 open() 时历史不刷新的问题)
|
||||
*/
|
||||
init() {
|
||||
this.$watch('show', async (val) => {
|
||||
if (val) {
|
||||
await this.loadInfo();
|
||||
await this.loadHistory();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 打开面板并加载数据
|
||||
*/
|
||||
async open() {
|
||||
this.show = true;
|
||||
await this.loadInfo();
|
||||
await this.loadHistory();
|
||||
},
|
||||
|
||||
/**
|
||||
* 加载游戏配置和余额
|
||||
*/
|
||||
async loadInfo() {
|
||||
try {
|
||||
const res = await fetch('/slot/info');
|
||||
const data = await res.json();
|
||||
if (!data.enabled) {
|
||||
this.show = false;
|
||||
return;
|
||||
}
|
||||
this.costPerSpin = data.cost_per_spin;
|
||||
this.dailyLimit = data.daily_limit;
|
||||
this.remaining = data.remaining;
|
||||
// 从聊天室全局变量读取余额
|
||||
this.balance = window.__chatUser?.jjb ?? 0;
|
||||
} catch {}
|
||||
},
|
||||
|
||||
/**
|
||||
* 加载历史记录
|
||||
*/
|
||||
async loadHistory() {
|
||||
try {
|
||||
const res = await fetch('/slot/history');
|
||||
const data = await res.json();
|
||||
this.history = data.history || [];
|
||||
} catch {}
|
||||
},
|
||||
|
||||
/**
|
||||
* 执行转动
|
||||
*/
|
||||
async doSpin() {
|
||||
if (this.spinning) return;
|
||||
if (this.dailyLimit > 0 && this.remaining <= 0) return;
|
||||
|
||||
this.spinning = true;
|
||||
this.resultType = '';
|
||||
this.resultLabel = '';
|
||||
this.netChange = 0;
|
||||
this.reel1Stopped = false;
|
||||
this.reel2Stopped = false;
|
||||
this.reel3Stopped = false;
|
||||
|
||||
// 开始随机滚动动画
|
||||
this._spinInterval = setInterval(() => {
|
||||
this.spinEmojis = [
|
||||
ALL_EMOJIS[Math.floor(Math.random() * ALL_EMOJIS.length)],
|
||||
ALL_EMOJIS[Math.floor(Math.random() * ALL_EMOJIS.length)],
|
||||
ALL_EMOJIS[Math.floor(Math.random() * ALL_EMOJIS.length)],
|
||||
];
|
||||
}, 80);
|
||||
|
||||
// 请求后端
|
||||
let data;
|
||||
try {
|
||||
const res = await fetch('/slot/spin', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'X-CSRF-TOKEN': document.querySelector('meta[name=csrf-token]')?.content,
|
||||
},
|
||||
});
|
||||
data = await res.json();
|
||||
} catch {
|
||||
clearInterval(this._spinInterval);
|
||||
this.spinning = false;
|
||||
window.chatDialog?.alert('网络异常,请稍后重试', '错误', '#ef4444');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data.ok) {
|
||||
clearInterval(this._spinInterval);
|
||||
this.spinning = false;
|
||||
window.chatDialog?.alert(data.message || '转动失败', '提示', '#ef4444');
|
||||
return;
|
||||
}
|
||||
|
||||
// 逐列停止(延迟效果)
|
||||
const stopReel = (reelIndex, emoji, delay) => {
|
||||
return new Promise(resolve => setTimeout(() => {
|
||||
clearInterval(this._spinInterval);
|
||||
this['reel' + (reelIndex + 1) + 'Stopped'] = true;
|
||||
this.spinEmojis = [...this.spinEmojis];
|
||||
this.spinEmojis[reelIndex] = emoji;
|
||||
resolve();
|
||||
}, delay));
|
||||
};
|
||||
|
||||
await stopReel(0, data.emojis[0], 600);
|
||||
this._spinInterval = setInterval(() => {
|
||||
this.spinEmojis = [data.emojis[0],
|
||||
ALL_EMOJIS[Math.floor(Math.random() * ALL_EMOJIS.length)],
|
||||
ALL_EMOJIS[Math.floor(Math.random() * ALL_EMOJIS.length)]
|
||||
];
|
||||
}, 80);
|
||||
await stopReel(1, data.emojis[1], 500);
|
||||
this._spinInterval = setInterval(() => {
|
||||
this.spinEmojis = [data.emojis[0], data.emojis[1],
|
||||
ALL_EMOJIS[Math.floor(Math.random() * ALL_EMOJIS.length)]
|
||||
];
|
||||
}, 80);
|
||||
await stopReel(2, data.emojis[2], 400);
|
||||
|
||||
clearInterval(this._spinInterval);
|
||||
|
||||
// 显示结果
|
||||
this.resultEmojis = data.emojis;
|
||||
this.resultType = data.result_type;
|
||||
this.resultLabel = data.result_label;
|
||||
this.netChange = data.payout > 0 ? data.payout - this.costPerSpin : -this.costPerSpin + (data
|
||||
.payout < 0 ? data.payout : 0);
|
||||
this.balance = data.balance;
|
||||
this.spinning = false;
|
||||
|
||||
// 更新全局余额(让聊天界面同步)
|
||||
if (window.__chatUser) window.__chatUser.jjb = data.balance;
|
||||
|
||||
if (this.dailyLimit > 0 && this.remaining !== null) {
|
||||
this.remaining = Math.max(0, this.remaining - 1);
|
||||
}
|
||||
|
||||
await this.loadHistory();
|
||||
},
|
||||
|
||||
/**
|
||||
* 关闭面板
|
||||
*/
|
||||
close() {
|
||||
clearInterval(this._spinInterval);
|
||||
this.spinning = false;
|
||||
this.show = false;
|
||||
},
|
||||
};
|
||||
}
|
||||
</script>
|
||||
{{-- 老虎机 Alpine 组件已迁移到 resources/js/chat-room/slot-machine.js --}}
|
||||
|
||||
Reference in New Issue
Block a user