金穗收割夜
by AstroStar11504 lines18.8 KB
<!DOCTYPE html>
<html lang="zh-CN" style="margin:0;padding:0;overflow:hidden;">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>金穗收割夜</title>
<style>
*{margin:0;padding:0;box-sizing:border-box;font-family:"微软雅黑",sans-serif;color:#fff;}
body{background:#000;overflow:hidden;touch-action:none;}
#gameBox{width:100vw;height:100vh;position:relative;overflow:hidden;display:none;}
/* 界面通用 */
.page{width:100vw;height:100vh;position:absolute;top:0;left:0;background:#111;display:none;flex-direction:column;align-items:center;justify-content:center;padding:20px;}
.page.active{display:flex;}
h1{font-size:42px;margin-bottom:30px;color:#ffcc44;text-shadow:0 0 12px #ff8800;}
.desc{max-width:80%;font-size:18px;line-height:1.8;margin-bottom:30px;color:#ddd;}
.btn{padding:14px 40px;font-size:22px;background:#882222;border:2px solid #ff6644;border-radius:8px;margin:12px;cursor:pointer;transition:0.2s;}
.btn:hover{background:#aa3333;box-shadow:0 0 15px #ff4422;}
.backBtn{position:absolute;top:20px;left:20px;padding:8px 16px;font-size:16px;background:#333;border:1px solid #666;}
/* 商店 */
#shopPage .goldTip{position:absolute;top:20px;right:20px;font-size:24px;color:#ffdd00;}
.skillItem{width:90%;border:1px solid #555;padding:16px;margin:10px;border-radius:6px;background:#222;}
.skillItem h3{color:#ffaa44;margin-bottom:8px;}
.skillItem p{font-size:14px;color:#bbb;margin-bottom:10px;}
.buySkill{background:#226622;border:1px solid #44aa44;padding:6px 16px;font-size:16px;}
.buySkill.owned{background:#222;color:#888;border-color:#444;cursor:not-allowed;}
/* 地图选择 */
.mapList{display:flex;gap:20px;flex-wrap:wrap;justify-content:center;}
.mapBtn{width:220px;height:140px;border:2px solid #777;display:flex;align-items:center;justify-content:center;font-size:20px;border-radius:6px;cursor:pointer;}
#map1{background:linear-gradient(#ccaa44,#997722);}
#map2{background:linear-gradient(#224422,#112211);}
#map3{background:linear-gradient(#eeeeee,#bbbbbb);color:#111;}
/* 游戏画布 */
#gameCanvas{position:absolute;}
/* 游戏UI */
.gameUi{position:absolute;bottom:10px;left:0;width:100%;display:flex;justify-content:center;gap:10px;z-index:99;}
.skillBtn{width:80px;height:80px;border:2px solid #ff9900;background:#222;display:none;align-items:center;justify-content:center;font-size:14px;border-radius:6px;}
.skillBtn.cd{opacity:0.4;cursor:not-allowed;}
.hpBar{position:absolute;top:10px;left:10px;width:220px;height:26px;border:2px solid #ff3333;background:#111;}
.hpFill{height:100%;background:#ff2222;transition:0.1s;}
/* 结局界面 */
.endTip{font-size:36px;text-align:center;margin-bottom:40px;line-height:2;}
.goldAdd{font-size:28px;color:#ffdd00;margin:20px 0;}
.touchTip{font-size:18px;color:#aaa;}
/* 虚拟方向键 */
.ctrlBox{position:absolute;bottom:110px;left:10px;width:160px;height:160px;display:grid;grid-template-columns:1fr 1fr 1fr;grid-template-rows:1fr 1fr 1fr;gap:4px;}
.ctrlKey{background:rgba(255,255,255,0.15);border:1px solid rgba(255,255,255,0.3);display:flex;align-items:center;justify-content:center;font-size:26px;}
</style>
</head>
<body>
<!-- 音频资源 -->
<audio id="bgmHorror" loop src="https://assets.mixkit.co/sfx/preview/mixkit-horror-low-tension-background-1267.mp3">
<audio id="soundTrain" src="https://assets.mixkit.co/sfx/preview/mixkit-train-horn-blast-1103.mp3">
<audio id="soundBasketball" src="https://assets.mixkit.co/sfx/preview/mixkit-ball-hit-metal-1024.mp3">
<audio id="soundBelial" src="https://assets.mixkit.co/sfx/preview/mixkit-dark-monster-roar-1047.mp3">
<audio id="soundHurt" src="https://assets.mixkit.co/sfx/preview/mixkit-body-hit-soft-997.mp3">
<audio id="soundEat" src="https://assets.mixkit.co/sfx/preview/mixkit-small-slurp-bite-1160.mp3">
<!-- 主菜单界面 -->
<div id="mainPage" class="page active">
<h1>金穗收割夜</h1>
<p class="desc">1978年秋收惨案,百年稻田异次元循环,无尽金色雾霭与追魂收割机正在等你...</p>
<button class="btn" onclick="showPage('storyPage')">开始游戏</button>
<button class="btn" onclick="showPage('shopPage')">商店</button>
</div>
<!-- 剧情介绍界面 -->
<div id="storyPage" class="page">
<button class="backBtn" onclick="showPage('mainPage')">返回</button>
<h1>收割惨案往事</h1>
<p class="desc">城郊百年稻田,原为集体农场。1978年秋收深夜,三台老式联合收割机突然失控,整片田地劳作农工尽数卷入机器碾碎。
农场主为掩盖滔天惨案,用金黄稻谷掩埋血迹与残尸,对外谎称工人连夜搬迁。
自此每到秋收稻熟黄昏,整片田野会被浓稠金雾笼罩,坠入无限收割异次元。误入者困在无尽稻田,被当年失控收割机追杀,还要直面惨死农工化作的稻秆怨灵。</p>
<button class="btn" onclick="showPage('mapSelectPage')">继续</button>
</div>
<!-- 地图选择界面 -->
<div id="mapSelectPage" class="page">
<button class="backBtn" onclick="showPage('storyPage')">返回</button>
<h1>选择被困稻田</h1>
<div class="mapList">
<div class="mapBtn" id="map1" onclick="startGame(1)">①集体农庄</div>
<div class="mapBtn" id="map2" onclick="startGame(2)">②猎人林地</div>
<div class="mapBtn" id="map3" onclick="startGame(3)">③寒冬森林</div>
</div>
</div>
<!-- 商店界面 -->
<div id="shopPage" class="page">
<button class="backBtn" onclick="showPage('mainPage')">退出</button>
<div class="goldTip">金币:<span id="goldNum">100</span></div>
<h1>怨灵技能商店</h1>
<div class="skillItem">
<h3>①大运(100金币)</h3>
<p>召唤火车撞击单台收割机,造成100伤害并眩晕10秒 | 冷却15秒</p>
<button class="buySkill" id="buy1" onclick="buySkill(1)">购买</button>
</div>
<div class="skillItem">
<h3>②嘉豪(100金币)</h3>
<p>持续50秒自动发射篮球攻击收割机,单次20伤害,释放音效:下雨天才是我的主场! | 冷却10秒</p>
<button class="buySkill" id="buy2" onclick="buySkill(2)">购买</button>
</div>
<div class="skillItem">
<h3>③贝利亚(100金币)</h3>
<p>20秒无敌,触碰收割机每秒吸取对方3血量,释放台词:我要来破坏地球了! | 冷却30秒</p>
<button class="buySkill" id="buy3" onclick="buySkill(3)">购买</button>
</div>
</div>
<!-- 游戏容器 -->
<div id="gameBox">
<canvas id="gameCanvas"></canvas>
<!-- 血条 -->
<div class="hpBar"><div class="hpFill" id="hpFill"></div></div>
<!-- 虚拟方向键 -->
<div class="ctrlBox">
<div></div>
<div class="ctrlKey" data-key="up">↑</div>
<div></div>
<div class="ctrlKey" data-key="left">←</div>
<div></div>
<div class="ctrlKey" data-key="right">→</div>
<div></div>
<div class="ctrlKey" data-key="down">↓</div>
<div></div>
</div>
<!-- 技能按钮 -->
<div class="gameUi">
<div class="skillBtn" id="skill1" onclick="useSkill(1)">大运</div>
<div class="skillBtn" id="skill2" onclick="useSkill(2)">嘉豪</div>
<div class="skillBtn" id="skill3" onclick="useSkill(3)">贝利亚</div>
</div>
</div>
<!-- 胜利界面 -->
<div id="winPage" class="page">
<h1>逃出生天</h1>
<p class="endTip">成功逃出稻田!怨灵暂时平息,达成真结局!</p>
<p class="goldAdd">金币 +100</p>
<p class="touchTip">点击任意位置返回主界面</p>
</div>
<!-- 失败界面 -->
<div id="losePage" class="page">
<h1>永困金田</h1>
<p class="endTip">你被收割机卷入,永困金色稻田轮回!</p>
<p class="touchTip">点击任意位置返回主界面</p>
</div>
<script>
// 全局游戏数据
const gameData = {
gold: 100,
ownedSkill: [false, false, false], // 1大运 2嘉豪 3贝利亚
skillCd: [0,0,0],
playerHp: 50,
maxHp: 50,
mapId: 1,
mapScale: 4, // 地图为屏幕4倍大小
mapW: 0, mapH:0,
playerX:0, playerY:0,
exitX:0, exitY:0,
harvesters: [], // {x,y,hp:500,dmg:50,speed,stun:0}
obstacles: [],
chickenList: [], // 寒冬地图回血鸡腿
isBelial: false, belialTimer:0,
jiahaoTimer:0, jiahaoBallList:[],
coldTimer:0, // 寒冬扣血计时
running: false,
keys: {up:false,down:false,left:false,right:false},
canvas: null, ctx:null,
viewX:0, viewY:0 // 视口偏移
};
const audio = {
bgm: document.getElementById("bgmHorror"),
train: document.getElementById("soundTrain"),
ball: document.getElementById("soundBasketball"),
belial: document.getElementById("soundBelial"),
hurt: document.getElementById("soundHurt"),
eat: document.getElementById("soundEat")
}
// 页面切换
function showPage(id){
document.querySelectorAll(".page").forEach(p=>p.classList.remove("active"));
document.getElementById(id).classList.add("active");
document.getElementById("gameBox").style.display = "none";
gameData.running = false;
audio.bgm.pause();
}
// 商店购买技能
function buySkill(idx){
const cost = 100;
if(gameData.gold < cost || gameData.ownedSkill[idx-1]) return;
gameData.gold -= cost;
gameData.ownedSkill[idx-1] = true;
document.getElementById("goldNum").innerText = gameData.gold;
document.getElementById(`buy${idx}`).classList.add("owned");
document.getElementById(`buy${idx}`).innerText = "已拥有";
}
// 初始化画布,启动游戏
function startGame(mapId){
gameData.mapId = mapId;
showPage("mainPage");
document.querySelectorAll(".page").forEach(p=>p.classList.remove("active"));
const box = document.getElementById("gameBox");
box.style.display = "block";
gameData.canvas = document.getElementById("gameCanvas");
gameData.ctx = gameData.canvas.getContext("2d");
// 画布尺寸等于屏幕
gameData.canvas.width = window.innerWidth;
gameData.canvas.height = window.innerHeight;
gameData.mapW = gameData.canvas.width * gameData.mapScale;
gameData.mapH = gameData.canvas.height * gameData.mapScale;
// 重置玩家数据
gameData.playerHp = gameData.maxHp;
gameData.isBelial = false;
gameData.belialTimer = 0;
gameData.jiahaoTimer = 0;
gameData.jiahaoBallList = [];
gameData.coldTimer = 0;
gameData.chickenList = [];
gameData.skillCd = [0,0,0];
// 随机出生、逃生点
gameData.playerX = Math.random() * gameData.mapW * 0.8;
gameData.playerY = Math.random() * gameData.mapH * 0.8;
gameData.exitX = Math.random() * gameData.mapW * 0.8;
gameData.exitY = Math.random() * gameData.mapH * 0.8;
// 生成三台收割机
gameData.harvesters = [];
for(let i=0;i<3;i++){
gameData.harvesters.push({
x: Math.random()*gameData.mapW,
y: Math.random()*gameData.mapH,
hp:500, dmg:50, speed:1.2, stun:0
})
}
// 生成障碍物
gameData.obstacles = [];
const obsCount = 30;
for(let i=0;i<obsCount;i++){
gameData.obstacles.push({
x: Math.random()*gameData.mapW,
y: Math.random()*gameData.mapH,
w:60+Math.random()*80,
h:60+Math.random()*80,
type: mapId===1?"wheat":mapId===2?(Math.random()>0.5?"tree":"fire"):"snowTree"
})
}
// 寒冬地图生成鸡腿
if(mapId ===3){
for(let i=0;i<12;i++){
gameData.chickenList.push({
x: Math.random()*gameData.mapW,
y: Math.random()*gameData.mapH,
eat: false
})
}
}
// 显示已购买技能按钮
document.getElementById("skill1").style.display = gameData.ownedSkill[0]?"flex":"none";
document.getElementById("skill2").style.display = gameData.ownedSkill[1]?"flex":"none";
document.getElementById("skill3").style.display = gameData.ownedSkill[2]?"flex":"none";
// 启动BGM
audio.bgm.currentTime = 0;
audio.bgm.play();
gameData.running = true;
gameLoop();
}
// 使用技能
function useSkill(idx){
const cdList = [15000,10000,30000];
if(gameData.skillCd[idx-1]>0 || !gameData.ownedSkill[idx-1]) return;
gameData.skillCd[idx-1] = cdList[idx-1];
switch(idx){
case 1: // 大运火车
audio.train.play();
// 选中最近收割机眩晕+扣血
let near = gameData.harvesters[0];
let minD = 9999;
gameData.harvesters.forEach(h=>{
const d = Math.hypot(h.x-gameData.playerX, h.y-gameData.playerY);
if(d<minD){minD=d;near=h;}
})
near.hp -=100;
near.stun = 10000;
break;
case 2: // 嘉豪篮球
audio.ball.play();
alert("下雨天才是我的主场!");
gameData.jiahaoTimer = 50000;
break;
case 3: // 贝利亚无敌吸血
audio.belial.play();
alert("我要来破坏地球了!");
gameData.isBelial = true;
gameData.belialTimer = 20000;
break;
}
}
// 虚拟按键绑定
document.querySelectorAll(".ctrlKey").forEach(key=>{
const k = key.dataset.key;
key.addEventListener("touchstart",()=>gameData.keys[k]=true);
key.addEventListener("touchend",()=>gameData.keys[k]=false);
})
// 主游戏循环
let lastTime = 0;
function gameLoop(time=0){
if(!gameData.running) return;
const dt = time - lastTime;
lastTime = time;
update(dt);
render();
requestAnimationFrame(gameLoop);
}
// 游戏逻辑更新
function update(dt){
const spd = 3;
// 玩家移动
if(gameData.keys.up) gameData.playerY -= spd;
if(gameData.keys.down) gameData.playerY += spd;
if(gameData.keys.left) gameData.playerX -= spd;
if(gameData.keys.right) gameData.playerX += spd;
// 边界限制
gameData.playerX = Math.max(0, Math.min(gameData.mapW, gameData.playerX));
gameData.playerY = Math.max(0, Math.min(gameData.mapH, gameData.playerY));
// 视口跟随玩家
gameData.viewX = gameData.playerX - gameData.canvas.width/2;
gameData.viewY = gameData.playerY - gameData.canvas.height/2;
gameData.viewX = Math.max(0, Math.min(gameData.mapW-gameData.canvas.width, gameData.viewX));
gameData.viewY = Math.max(0, Math.min(gameData.mapH-gameData.canvas.height, gameData.viewY));
// 技能冷却计时
for(let i=0;i<3;i++) if(gameData.skillCd[i]>0) gameData.skillCd[i]-=dt;
document.getElementById("skill1").classList.toggle("cd", gameData.skillCd[0]>0);
document.getElementById("skill2").classList.toggle("cd", gameData.skillCd[1]>0);
document.getElementById("skill3").classList.toggle("cd", gameData.skillCd[2]>0);
// 贝利亚无敌计时
if(gameData.belialTimer>0){
gameData.belialTimer -= dt;
if(gameData.belialTimer<=0) gameData.isBelial = false;
}
// 嘉豪自动篮球
if(gameData.jiahaoTimer>0){
gameData.jiahaoTimer -= dt;
if(Math.floor(dt/100)%3===0){
gameData.harvesters.forEach(h=>h.hp -=20);
}
}
// 寒冬地图持续扣血
if(gameData.mapId===3){
gameData.coldTimer += dt;
if(gameData.coldTimer >=5000){
gameData.coldTimer = 0;
if(!gameData.isBelial) takeDmg(2);
}
}
// 鸡腿拾取回血
gameData.chickenList.forEach(ch=>{
if(ch.eat) return;
const dist = Math.hypot(ch.x-gameData.playerX, ch.y-gameData.playerY);
if(dist < 30){
ch.eat = true;
audio.eat.play();
gameData.playerHp = Math.min(gameData.maxHp, gameData.playerHp+10);
}
})
// 收割机AI追击
gameData.harvesters.forEach(h=>{
if(h.stun>0){h.stun -= dt;return;}
const dx = gameData.playerX - h.x;
const dy = gameData.playerY - h.y;
const dist = Math.hypot(dx, dy);
if(dist > 20){
h.x += (dx/dist)*h.speed;
h.y += (dy/dist)*h.speed;
}
// 碰撞扣血 / 贝利亚吸血
if(dist < 35){
if(gameData.isBelial){
h.hp -= 3;
}else{
takeDmg(h.dmg);
}
}
})
// 检查到达逃生出口
const exitDist = Math.hypot(gameData.exitX-gameData.playerX, gameData.exitY-gameData.playerY);
if(exitDist < 40){
winGame();
}
// 更新血条UI
const hpPercent = gameData.playerHp / gameData.maxHp * 100;
document.getElementById("hpFill").style.width = hpPercent+"%";
}
// 玩家受伤
function takeDmg(val){
gameData.playerHp -= val;
audio.hurt.play();
if(gameData.playerHp <=0){
loseGame();
}
}
// 胜利结算
function winGame(){
gameData.running = false;
audio.bgm.pause();
gameData.gold +=100;
document.getElementById("goldNum").innerText = gameData.gold;
document.getElementById("gameBox").style.display = "none";
document.getElementById("winPage").classList.add("active");
}
// 失败结算
function loseGame(){
gameData.running = false;
audio.bgm.pause();
document.getElementById("gameBox").style.display = "none";
document.getElementById("losePage").classList.add("active");
}
// 画面渲染
function render(){
const ctx = gameData.ctx;
const cw = gameData.canvas.width;
const ch = gameData.canvas.height;
ctx.clearRect(0,0,cw,ch);
// 地图底色
if(gameData.mapId===1) ctx.fillStyle="#b89230";
else if(gameData.mapId===2) ctx.fillStyle="#203820";
else ctx.fillStyle="#e8e8ee";
ctx.fillRect(0,0,cw,ch);
ctx.save();
ctx.translate(-gameData.viewX, -gameData.viewY);
// 绘制障碍物
gameData.obstacles.forEach(obs=>{
if(obs.type==="wheat") ctx.fillStyle="#8a6b22";
else if(obs.type==="tree") ctx.fillStyle="#182e18";
else if(obs.type==="fire") ctx.fillStyle="#ff5522";
else ctx.fillStyle="#d0d0e0";
ctx.fillRect(obs.x, obs.y, obs.w, obs.h);
})
// 绘制鸡腿
gameData.chickenList.forEach(ch=>{
if(ch.eat) return;
ctx.fillStyle="#ffaa22";
ctx.beginPath();
ctx.arc(ch.x, ch.y, 16,0,Math.PI*2);
ctx.fill();
})
// 绘制逃生出口
ctx.fillStyle="#33ff33";
ctx.beginPath();
ctx.arc(gameData.exitX, gameData.exitY, 28,0,Math.PI*2);
ctx.fill();
// 绘制收割机
gameData.harvesters.forEach(h=>{
ctx.fillStyle = h.stun>0 ? "#666666" : "#772222";
ctx.fillRect(h.x-25, h.y-25, 50,50);
ctx.fillStyle="#fff";
ctx.fillText(h.hp, h.x-15, h.y-30);
})
// 绘制玩家
ctx.fillStyle = gameData.isBelial ? "#6600aa" : "#2288ff";
ctx.beginPath();
ctx.arc(gameData.playerX, gameData.playerY, 22,0,Math.PI*2);
ctx.fill();
ctx.restore();
}
// 点击结局页面返回主菜单
document.getElementById("winPage").addEventListener("click",()=>showPage("mainPage"));
document.getElementById("losePage").addEventListener("click",()=>showPage("mainPage"));
// 窗口适配
window.addEventListener("resize",()=>{
if(gameData.canvas){
gameData.canvas.width = window.innerWidth;
gameData.canvas.height = window.innerHeight;
gameData.mapW = gameData.canvas.width * gameData.mapScale;
gameData.mapH = gameData.canvas.height * gameData.mapScale;
}
})
</script>
</body>
</html>Game Source: 金穗收割夜
Creator: AstroStar11
Libraries: none
Complexity: complex (504 lines, 18.8 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: game-astrostar11" to link back to the original. Then publish at arcadelab.ai/publish.