可信源码风险提示:保存或下载前请阅读源码和权限声明。
// 第一视角生存·安全动画版(避开 stack)
let hp = 20, hunger = 20, day = 1;
let wood = 0, stone = 0, food = 0, torch = 0, portal = 0;
let phase = 'day';
let msg = '点击“行动”开始';
let lookOffset = 0;
let mineFlash = 0;
let animTimer = null;
const viewSig = ui.signal(0);
const hudSig = ui.signal(0);
function skyColor() {
if (phase === 'night') return '#0B1026';
if (lookOffset < -0.3) return '#4A90D9';
if (lookOffset > 0.3) return '#8FD0F0';
return '#6BB3E0';
}
function groundColor() {
return phase === 'night' ? '#1A1A1A' : '#3E7B2F';
}
function startAnim() {
if (animTimer) return;
animTimer = setInterval(() => {
if (mineFlash > 0) mineFlash = Math.max(0, mineFlash - 0.2);
viewSig.update(n => n + 1);
}, 250);
}
function statusText() {
if (phase === 'win') return '🎉 传送门建成,通关!';
if (phase === 'dead') return '💀 你倒下了……';
return `第${day}天 ${phase === 'day' ? '☀️' : '🌙'} ❤️${hp} 🍖${hunger}`;
}
function checkHunger() {
if (hunger <= 0) {
hp -= 2;
msg += '(饥饿扣血)';
if (hp <= 0) { phase = 'dead'; msg = '你饿死了……'; }
}
}
async function action() {
if (phase === 'win' || phase === 'dead') return;
if (phase === 'night') {
let dmg = 4;
if (torch > 0) { torch--; dmg = 1; msg = '火把击退怪物,受1伤'; }
else msg = '无火把,受4伤';
hp -= dmg;
if (food > 0 && hp < 20) { food--; hp = Math.min(20, hp + 3); msg += ',吃食物+3血'; }
if (hp <= 0) { phase = 'dead'; msg = '你被击倒……'; hudSig.update(n => n + 1); viewSig.update(n => n + 1); return; }
if (wood >= 3 && stone >= 3) { wood -= 3; stone -= 3; portal++; msg += `,传送门${portal}/3`; }
if (portal >= 3) { phase = 'win'; hudSig.update(n => n + 1); viewSig.update(n => n + 1); return; }
day++; phase = 'day'; hunger = Math.max(0, hunger - 2); checkHunger();
msg += `,撑到第${day}天`;
hudSig.update(n => n + 1); viewSig.update(n => n + 1);
return;
}
const r = await dialog.select({
title: '白天行动',
items: [
{ label: '砍树 +2木', value: 'wood' },
{ label: '挖石 +1石', value: 'stone' },
{ label: '找食物 +2', value: 'food' },
{ label: '做火把(1木1石)', value: 'torch' },
{ label: '向左看', value: 'lookL' },
{ label: '向右看', value: 'lookR' },
{ label: '进入夜晚', value: 'night' }
]
});
if (r.action !== 'confirm') return;
if (r.value === 'wood') { wood += 2; hunger = Math.max(0, hunger - 1); msg = '+2木头'; mineFlash = 1; }
else if (r.value === 'stone') { stone += 1; hunger = Math.max(0, hunger - 1); msg = '+1石头'; mineFlash = 1; }
else if (r.value === 'food') { food += 2; hunger = Math.max(0, hunger - 1); msg = '+2食物'; }
else if (r.value === 'torch') {
if (wood >= 1 && stone >= 1) { wood--; stone--; torch++; msg = '做好1火把'; }
else msg = '材料不够';
}
else if (r.value === 'lookL') { lookOffset = Math.max(-1, lookOffset - 0.5); msg = '向左看'; }
else if (r.value === 'lookR') { lookOffset = Math.min(1, lookOffset + 0.5); msg = '向右看'; }
else if (r.value === 'night') { phase = 'night'; msg = '天黑了,准备守夜'; }
checkHunger();
hudSig.update(n => n + 1);
viewSig.update(n => n + 1);
}
function reset() {
hp = 20; hunger = 20; day = 1;
wood = 0; stone = 0; food = 0; torch = 0; portal = 0;
phase = 'day'; msg = '点击“行动”开始'; lookOffset = 0; mineFlash = 0;
hudSig.update(n => n + 1); viewSig.update(n => n + 1);
}
async function doSave() {
await script.data.set('fp2', { hp, hunger, day, wood, stone, food, torch, portal, phase, lookOffset });
script.toast('已保存');
}
async function doLoad() {
const d = await script.data.get('fp2', null);
if (d) {
hp = d.hp; hunger = d.hunger; day = d.day;
wood = d.wood; stone = d.stone; food = d.food;
torch = d.torch; portal = d.portal; phase = d.phase;
lookOffset = d.lookOffset || 0;
msg = '读取成功';
} else msg = '没有存档';
hudSig.update(n => n + 1); viewSig.update(n => n + 1);
}
// 第一视角:用 row 拼天空、地面、准星,全部纯色块
function renderView() {
const cw = typeof ui.getWidth === 'function' ? ui.getWidth() : 324;
const skyH = 160;
const groundH = 100;
const flash = mineFlash > 0.5;
const horizonShift = Math.round(lookOffset * 30);
const nodes = [];
// 天空:一行纯色块
nodes.push(ui.row([], { width: cw, height: skyH, background: skyColor(), id: 'sky' }));
// 地平线:一条窄色带,随视角左右移动
nodes.push(ui.row([], {
width: 40, height: 6,
background: phase === 'night' ? '#2A2A3A' : '#A8D8A0',
id: 'horizon'
}));
// 准星:一个方块,挖掘时变红
const crossSize = flash ? 12 : 8;
nodes.push(ui.row([], {
width: crossSize, height: crossSize,
background: flash ? '#FF3B30' : '#FFFFFF',
id: 'cross'
}));
// 地面
nodes.push(ui.row([], { width: cw, height: groundH, background: groundColor(), id: 'ground' }));
// 底部两只“手”
nodes.push(ui.row([
ui.row([], { width: 36, height: 50, background: phase === 'night' ? '#5A3A1A' : '#C68642', id: 'handL' }),
ui.row([], { width: 12, height: 10, background: '#00000000', id: 'gap' }),
ui.row([], { width: 36, height: 50, background: phase === 'night' ? '#5A3A1A' : '#C68642', id: 'handR' })
], { gap: 4, justify: 'center', width: cw }));
return ui.column(nodes, { gap: 0, width: cw });
}
function render() {
const cw = typeof ui.getWidth === 'function' ? ui.getWidth() : 324;
const nodes = [];
nodes.push(renderView());
nodes.push(ui.text(statusText(), { lines: 1, size: 16, align: 'center' }));
nodes.push(ui.text(msg, { lines: 1, size: 13, color: '#FFD700', align: 'center' }));
nodes.push(ui.text(`木${wood} 石${stone} 食${food} 火${torch} 门${portal}/3`, { lines: 1, size: 13, align: 'center' }));
if (phase === 'day' || phase === 'night') {
nodes.push(ui.button(phase === 'day' ? '行动' : '守夜!', action, {
id: 'act', height: 56, tone: phase === 'day' ? 'primary' : 'danger'
}));
} else {
nodes.push(ui.button('重新开始', () => { reset(); }, { id: 'restart', height: 56, tone: 'primary' }));
}
nodes.push(ui.row([
ui.button('保存', doSave, { id: 's', flex: 1, tone: 'neutral', height: 44 }),
ui.button('读取', doLoad, { id: 'l', flex: 1, tone: 'neutral', height: 44 }),
ui.button('日志', () => ui.hide(), { id: 'log', flex: 1, tone: 'neutral', height: 44 })
], { gap: 6, width: cw }));
return nodes;
}
ui.render(render);
startAnim();
viewSig.get();
hudSig.get();
console.log('第一视角生存·安全版已启动');