🎮ArcadeLab

像素农夫物语 V0.1 愚人节恐怖限定

by BraveDragon92
1026 lines35.6 KB
▶ Play
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>像素农夫物语 V0.1 愚人节恐怖限定</title>
<style>
*{
    margin:0;
    padding:0;
    box-sizing:border-box;
    image-rendering:pixelated;
    font-family:"Courier New",monospace;
}
body{
    background:#0a0000;
    display:flex;
    flex-direction:column;
    align-items:center;
    padding:10px;
    color:#fff;
    overflow-x:hidden;
}
#gameBox{
    border:4px solid #880000;
    background:#120000;
    width:800px;
    height:500px;
    position:relative;
    overflow:hidden;
}
canvas{display:block;}
.ui-panel{
    width:800px;
    background:#1a0000;
    padding:8px 12px;
    display:flex;
    justify-content:space-between;
    align-items:center;
    border:2px solid #770000;
    gap:8px;
    flex-wrap:wrap;
    margin-bottom:6px;
}
.btn{
    padding:6px 12px;
    background:#990000;
    color:#fff;
    border:none;
    font-weight:bold;
    cursor:pointer;
    font-size:14px;
}
.btn:hover{background:#cc0000;}
.btn-disabled{background:#333;color:#777;cursor:not-allowed;}

.shop-modal{
    position:fixed;
    width:620px;
    height:560px;
    left:50%;
    top:50%;
    transform:translate(-50%,-50%);
    background:#0b0000;
    border:3px solid #ff3333;
    z-index:999999;
    padding:20px;
    display:none;
}
.shop-close{
    position:absolute;
    right:16px;
    top:12px;
    font-size:26px;
    color:#f33;
    cursor:pointer;
}
.shop-title{
    color:#ff4444;
    text-align:center;
    font-size:22px;
    margin-bottom:16px;
    letter-spacing:2px;
}
.shop-item{
    display:flex;
    justify-content:space-between;
    align-items:center;
    border:1px solid #600;
    padding:10px;
    margin:8px 0;
}

.spring-modal{
    position:fixed;
    width:500px;
    height:340px;
    left:50%;
    top:50%;
    transform:translate(-50%,-50%);
    background:#1c0000;
    border:3px solid #ffaa00;
    z-index:999999;
    padding:22px;
    display:none;
}
.spring-title{
    color:#ffcc00;
    text-align:center;
    font-size:22px;
    margin-bottom:16px;
}
.spring-text{
    line-height:2;
    font-size:15px;
    color:#ffdddd;
    text-align:center;
}

.load-mask{
    position:fixed;
    top:0;
    left:0;
    width:100vw;
    height:100vh;
    background:#000;
    z-index:99999;
    display:flex;
    flex-direction:column;
    align-items:center;
    justify-content:center;
}
.garble-text{
    color:#ff2222;
    font-size:26px;
    letter-spacing:8px;
    line-height:2.2;
    text-align:center;
}

.jump-scare{
    position:fixed;
    top:0;
    left:0;
    width:100vw;
    height:100vh;
    background:radial-gradient(circle,#500 0%,#050000 50%,#000 90%);
    z-index:99998;
    display:none;
    align-items:center;
    justify-content:center;
    overflow:hidden;
}
.jump-scare .garble-layer{
    position:absolute;
    width:100%;
    height:100%;
    overflow:hidden;
    z-index:1;
    pointer-events:none;
}
.jump-scare .garble-text-piece{
    position:absolute;
    color:#ff0f0f;
    font-size:13px;
    font-weight:900;
    text-shadow:0 0 12px #f00,0 0 26px #700;
    white-space:nowrap;
    animation:garbleFlash 0.05s alternate infinite,garbleMove 1.2s linear infinite;
}
.face-shadow-1,.face-shadow-2{
    width:400px;
    height:400px;
    border-radius:50%;
    position:absolute;
    z-index:2;
    opacity:0.25;
    pointer-events:none;
}
.face-shadow-1{
    background:radial-gradient(circle,#f00 0%,transparent 70%);
    animation:shadow1Drift 0.06s alternate infinite;
}
.face-shadow-2{
    background:radial-gradient(circle,#900 0%,transparent 70%);
    animation:shadow2Drift 0.07s alternate infinite;
}
.scare-face-main{
    width:400px;
    height:400px;
    border-radius:50%;
    position:relative;
    z-index:10;
    pointer-events:none;
    background:radial-gradient(circle,#f00 0%,#400 45%,#000 75%);
    animation:faceTwist 0.05s alternate infinite,faceShake 0.03s infinite;
    box-shadow:0 0 60px #f00,0 0 120px #900,0 0 180px #300,inset 0 0 80px #200;
}
.eye-left,.eye-right{
    position:absolute;
    top:22%;
    width:22%;
    height:26%;
    background:#000;
    border-radius:50%;
    box-shadow:inset 0 0 20px #f00,0 12px 0 #f00;
}
.eye-left{left:24%;}
.eye-right{right:24%;}
.blood-tear-l,.blood-tear-r{
    position:absolute;
    top:48%;
    width:4px;
    height:90px;
    background:linear-gradient(#f00,transparent);
    animation:bloodDrop 0.6s linear infinite;
}
.blood-tear-l{left:34%;}
.blood-tear-r{right:34%;}
.crack-main{
    position:absolute;
    top:0;
    left:50%;
    width:4px;
    height:100%;
    background:#000;
    transform-origin:center top;
    animation:crackWobble 0.04s alternate infinite;
    box-shadow:0 0 8px #f00;
}
.crack-s1{
    position:absolute;
    top:28%;
    left:30%;
    width:90px;
    height:2px;
    background:#000;
    transform:rotate(-28deg);
    box-shadow:0 0 5px #f00;
}
.crack-s2{
    position:absolute;
    top:30%;
    right:30%;
    width:90px;
    height:2px;
    background:#000;
    transform:rotate(28deg);
    box-shadow:0 0 5px #f00;
}
.crack-s3{
    position:absolute;
    top:55%;
    left:22%;
    width:60px;
    height:2px;
    background:#000;
    transform:rotate(-18deg);
    box-shadow:0 0 5px #f00;
}
.crack-s4{
    position:absolute;
    top:58%;
    right:22%;
    width:60px;
    height:2px;
    background:#000;
    transform:rotate(18deg);
    box-shadow:0 0 5px #f00;
}
.scare-mouth{
    position:absolute;
    bottom:8%;
    left:50%;
    transform:translateX(-50%);
    width:68%;
    height:42%;
    background:#000;
    border-radius:0 0 70% 70%;
    box-shadow:inset 0 -20px 30px #f00,inset 0 10px 15px #300;
}
.teeth-top,.teeth-bottom{
    position:absolute;
    left:50%;
    transform:translateX(-50%);
    width:62%;
    height:16%;
    display:flex;
    justify-content:space-between;
}
.teeth-top{top:10%;}
.teeth-bottom{bottom:10%;}
.teeth-top span{
    width:14%;
    height:100%;
    background:#fff;
    clip-path:polygon(50% 100%,0 0,100% 0);
    box-shadow:0 0 10px #f00;
}
.teeth-bottom span{
    width:14%;
    height:100%;
    background:#fff;
    clip-path:polygon(50% 0,0 100%,100% 100%);
    box-shadow:0 0 10px #f00;
}
.mouth-flesh{
    position:absolute;
    width:80%;
    height:70%;
    left:10%;
    top:15%;
    background:radial-gradient(#700,transparent);
    opacity:0.6;
}

.sign-name{
    position:fixed;
    right:16px;
    bottom:16px;
    color:#ff2222;
    font-size:18px;
    font-weight:bold;
    text-shadow:0 0 8px #f00;
    z-index:9999;
    letter-spacing:2px;
}
.flash-screen{
    position:fixed;
    inset:0;
    background:#fff;
    opacity:0;
    pointer-events:none;
    z-index:99997;
}
.blood-word{
    position:absolute;
    color:#f00;
    font-weight:bold;
    pointer-events:none;
    z-index:100;
    text-shadow:0 0 6px #f00;
}
.tip-desc{
    color:#992222;
    margin-top:10px;
    font-size:13px;
}
.screen-distortion{
    position:fixed;
    inset:0;
    background:radial-gradient(circle,transparent 40%,rgba(120,0,0,0.18) 100%);
    opacity:0;
    pointer-events:none;
    z-index:99996;
    transition:opacity 0.12s;
}
.static-noise{
    position:fixed;
    inset:0;
    opacity:0;
    pointer-events:none;
    z-index:99995;
    background-image:url("data:image/svg+xml,%3Csvg width='256' height='256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.12'/%3E%3C/svg%3E");
}
.warning-ui{
    position:fixed;
    left:18px;
    top:18px;
    color:#ff2222;
    font-size:13px;
    line-height:1.6;
    text-shadow:0 0 8px #f00;
    z-index:9999;
    opacity:0;
    transition:opacity 0.15s;
}
.soul-banner-effect{
    position:fixed;
    inset:0;
    background:radial-gradient(circle,rgba(170,20,220,0.28) 0%,transparent 72%);
    opacity:0;
    pointer-events:none;
    z-index:99990;
    transition:opacity 0.2s;
}

@keyframes faceTwist{
    0%{transform:scale(1) rotate(-6deg) skewX(-4deg);filter:blur(0px)}
    100%{transform:scale(1.38) rotate(6deg) skewX(4deg);filter:blur(1.5px)}
}
@keyframes faceShake{
    0%{transform:translate(-2px,-2px)}
    50%{transform:translate(2px,2px)}
    100%{transform:translate(-2px,-2px)}
}
@keyframes crackWobble{
    0%{transform:translateX(-50%) rotate(-5deg)}
    100%{transform:translateX(-50%) rotate(5deg)}
}
@keyframes bloodDrop{
    0%{height:0;opacity:0}
    40%{height:90px;opacity:1}
    100%{height:90px;opacity:0}
}
@keyframes garbleFlash{
    0%{opacity:0.4;transform:translate(-3px)}
    50%{opacity:1;transform:translate(3px)}
    100%{opacity:0.4;transform:translate(-3px)}
}
@keyframes garbleMove{
    0%{transform:translateY(0)}
    100%{transform:translateY(16px)}
}
@keyframes shadow1Drift{
    0%{transform:translate(-18px,12px) scale(1.06)}
    100%{transform:translate(-6px,4px) scale(0.94)}
}
@keyframes shadow2Drift{
    0%{transform:translate(16px,-10px) scale(0.93)}
    100%{transform:translate(4px,-6px) scale(1.09)}
}
</style>
</head>
<body>
<div class="sign-name">汪天倪</div>
<div class="soul-banner-effect" id="bannerCover"></div>

<div class="shop-modal" id="birthdayShop">
    <span class="shop-close" onclick="closeBirthShop()">×</span>
    <h2 class="shop-title">🎂生日永久商店(仅10月4日可购买)</h2>
    <div class="shop-item"><span>玩家永久移速提升</span><button class="btn" onclick="buyItem('addSpeed',300)">300金币</button></div>
    <div class="shop-item"><span>全怪物永久移速降低25%</span><button class="btn" onclick="buyItem('slowAllMonster',400)">400金币</button></div>
    <div class="shop-item"><span>永久抵抗画面畸变噪点</span><button class="btn" onclick="buyItem('antiNoise',250)">250金币</button></div>
    <div class="shop-item"><span>一次性清屏全部怪物(可囤次数)</span><button class="btn" onclick="buyItem('clearMonster',500)">500金币</button></div>
    <div class="shop-item"><span>农作物永久瞬间成熟</span><button class="btn" onclick="buyItem('cropMature',450)">450金币</button></div>
    <div class="shop-item"><span>【故人·辟邪师】帅气仙侠外观,大范围压制鬼怪</span><button class="btn" onclick="buyItem('exorcist',550)">550金币</button></div>
    <div class="shop-item"><span>【万魂幡】收拢禁锢全场妖邪,可囤使用次数</span><button class="btn" onclick="buyItem('soulBanner',1200)">1200金币</button></div>
</div>

<div class="spring-modal" id="springGift">
    <h2 class="spring-title">🧨新春生肖永久礼包</h2>
    <div class="spring-text" id="springText"></div>
    <div style="text-align:center;margin-top:24px;"><button class="btn" onclick="closeSpringModal()">确认领取</button></div>
</div>

<div class="load-mask" id="loadMask">
    <div class="garble-text" id="garbleBox">
        █▓▒░系统数据流损坏 █▓▒░<br>资源加载异常,请勿关闭页面<br>剩余等待时间:<span id="timeCount">10</span>
    </div>
</div>

<div class="jump-scare" id="jumpScare">
    <div class="garble-layer" id="garbleLayer"></div>
    <div class="face-shadow-1"></div>
    <div class="face-shadow-2"></div>
    <div class="scare-face-main">
        <div class="eye-left"></div><div class="eye-right"></div>
        <div class="blood-tear-l"></div><div class="blood-tear-r"></div>
        <div class="crack-main"></div><div class="crack-s1"></div><div class="crack-s2"></div><div class="crack-s3"></div><div class="crack-s4"></div>
        <div class="scare-mouth">
            <div class="mouth-flesh"></div>
            <div class="teeth-top"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div>
            <div class="teeth-bottom"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></div>
        </div>
    </div>
</div>

<div class="flash-screen" id="flashMask"></div>
<div class="screen-distortion" id="distortionMask"></div>
<div class="static-noise" id="noiseMask"></div>
<div class="warning-ui" id="warnText">检测恐怖实体靠近</div>

<h2 style="color:#ff3333;text-align:center">像素农夫物语 V0.1 愚人节恐怖限定版</h2>
<div class="ui-panel">
    <div>金币:<span id="goldShow">0</span> | 田地:<span id="landShow">4</span> | 播种:<span id="plantShow">0</span>/15</div>
    <div>
        <button class="btn" onclick="saveGame()">存档</button>
        <button class="btn" onclick="loadGame()">读档</button>
        <button class="btn btn-disabled" id="shopBtn" onclick="openBirthShop()">生日商店</button>
        <button class="btn" onclick="useSoulBanner()">使用万魂幡</button>
    </div>
</div>

<div id="gameBox"><canvas id="gameCanvas" width="800" height="500"></canvas></div>
<p class="tip-desc">⚠ 频闪、流血鬼脸、刺耳噪音、多怪物追击,心脏病、光敏、胆小者禁止游玩</p>

<script>
function judgeWinVersion(){
    const ua = navigator.userAgent.toLowerCase();
    return ua.includes("windows nt 6.1") || ua.includes("windows nt 6.2") || ua.includes("windows nt 6.3");
}
if(!judgeWinVersion()){
    alert("如果你是 win 7 或 win 8 的话,请找创作者修理问题。如果没有问题的话证明你不是 Win 7 或 Win 8,而是其他版本,其他版本得在 4 月份或者 4 月 1 日游玩。");
    throw "非适配系统终止运行";
}

function isBirthDate(){
    const now = new Date();
    return now.getMonth() === 9 && now.getDate() === 4;
}
function getZodiac(year){
    const zodiacList = ["马","羊","猴","鸡","狗","猪","鼠","牛","虎","兔","龙","蛇"];
    const offset = (year - 2026) % 12;
    const idx = offset < 0 ? offset + 12 : offset;
    return zodiacList[idx];
}
function isSpringDay(){
    const d = new Date();
    const m = d.getMonth()+1;
    const day = d.getDate();
    return (m === 1 && day >= 29) || (m === 2 && day <= 20);
}
const zodiacBuff = {
    "鼠":{desc:"身法灵动,移速小幅增加",addSpeed:0.3,monsterSlow:0},
    "牛":{desc:"抗性更强,怪物移速降低",addSpeed:0,monsterSlow:0.15},
    "虎":{desc:"威慑鬼怪,怪物更容易徘徊不追击",addSpeed:0,monsterSlow:0.25},
    "兔":{desc:"身形轻盈,移动更快,闪屏变少",addSpeed:0.4,monsterSlow:0},
    "龙":{desc:"鸿运加持,收割金币永久+30%",addSpeed:0,monsterSlow:0,goldPlus:1.3},
    "蛇":{desc:"潜行隐匿,怪物远距离丢失视野",addSpeed:0,monsterSlow:0.2},
    "马":{desc:"奔行迅捷,高额移速加成",addSpeed:0.5,monsterSlow:0},
    "羊":{desc:"田园安稳,作物生长加速",addSpeed:0,monsterSlow:0,growRate:1.4},
    "猴":{desc:"灵活闪避,概率自动瞬移躲怪",addSpeed:0.2,monsterSlow:0},
    "鸡":{desc:"驱散阴暗,画面畸变弱化",addSpeed:0,monsterSlow:0},
    "狗":{desc:"感知敏锐,危险预警更早",addSpeed:0,monsterSlow:0.1},
    "猪":{desc:"沃土丰收,成熟大概率双倍收益",addSpeed:0,monsterSlow:0}
};

const canvas = document.getElementById("gameCanvas");
const ctx = canvas.getContext("2d");
const loadMask = document.getElementById("loadMask");
const garbleBox = document.getElementById("garbleBox");
const timeCount = document.getElementById("timeCount");
const jumpScare = document.getElementById("jumpScare");
const garbleLayer = document.getElementById("garbleLayer");
const flashMask = document.getElementById("flashMask");
const distortionMask = document.getElementById("distortionMask");
const noiseMask = document.getElementById("noiseMask");
const warnUI = document.getElementById("warnText");
const birthdayShop = document.getElementById("birthdayShop");
const shopButton = document.getElementById("shopBtn");
const springGiftDom = document.getElementById("springGift");
const springTextDom = document.getElementById("springText");
const bannerCover = document.getElementById("bannerCover");

let gold = 0;
let totalPlant = 0;
let landList = [];
const player = {x:380,y:320,w:44,h:64,baseSpeed:2.6};
let tick = 0;
let flashCd = 0;
let gameOver = false;
let isScare = false;
let bloodTextArr = [];
let entityList = [];
const keyBoard = {};
let bannerLockFrame = 0;

let permanentItem = {
    addSpeed: false,
    slowAllMonster: false,
    antiNoise: false,
    clearMonsterCount: 0,
    cropMature: false,
    exorcist: false,
    soulBannerCount: 0,
    zodiacName: "",
    zodiacReceived: false
};

const entityConfig = [
    {name:"黑影鬼魂",w:60,h:80,speed:0.8,color:"#000",eye:"#f00",ai:"lurk"},
    {name:"无面步行者",w:52,h:86,speed:1.1,color:"#111",eye:"#a00",ai:"stalk"},
    {name:"爬行畸变者",w:74,h:46,speed:1.6,color:"#200505",eye:"#f44",ai:"crawl"},
    {name:"高速突袭幽影",w:42,h:66,speed:2.4,color:"#050505",eye:"#f22",ai:"rush"},
    {name:"沉默巨人",w:90,h:110,speed:0.45,color:"#0a0202",eye:"#b00",ai:"giant"},
    {name:"裂隙血肉怪",w:66,h:72,speed:1.0,color:"#300",eye:"#f00",ai:"flesh"},
    {name:"暗影低语者",w:56,h:76,speed:1.3,color:"#080808",eye:"#d00",ai:"whisper"},
    {name:"瞬移灵体",w:48,h:62,speed:0.9,color:"#150000",eye:"#f33",ai:"teleport"},
    {name:"伪装农夫",w:44,h:64,speed:1.0,color:"#181818",eye:"#f00",ai:"disguise"},
    {name:"镜像残影",w:44,h:64,speed:1.2,color:"#222",eye:"#f44",ai:"mirror"},
    {name:"活稻草人",w:38,h:70,speed:0,color:"#2b1818",eye:"#f00",ai:"scarecrow"},
    {name:"血红爬行者",w:58,h:38,speed:1.9,color:"#450000",eye:"#f66",ai:"backCrawl"}
];

document.addEventListener("keydown",e=>keyBoard[e.key.toLowerCase()]=true);
document.addEventListener("keyup",e=>keyBoard[e.key.toLowerCase()]=false);

function openBirthShop(){
    if(!isBirthDate()){alert("当前不是10月4日,商店锁定无法购买");return;}
    birthdayShop.style.display = "block";
}
function closeBirthShop(){birthdayShop.style.display = "none";}
function buyItem(key,cost){
    if(gold < cost){alert("金币不足");return;}
    gold -= cost;
    if(key === "clearMonster") permanentItem.clearMonsterCount += 1;
    else if(key === "soulBanner") permanentItem.soulBannerCount += 1;
    else permanentItem[key] = true;
    document.getElementById("goldShow").innerText = gold;
    alert("购买成功,道具永久存入存档,读档永久生效!");
}

function useSoulBanner(){
    if(permanentItem.soulBannerCount <= 0){
        alert("无万魂幡使用次数,请在生日商店囤货");
        return;
    }
    permanentItem.soulBannerCount -= 1;
    bannerLockFrame = 180;
    bannerCover.style.opacity = 1;
    alert("万魂幡展开!所有妖邪被收拢禁锢3秒");
}

function openSpringModal(zodiac){
    const buff = zodiacBuff[zodiac];
    springTextDom.innerText = `本年度生肖:${zodiac}\n专属永久效果:${buff.desc}\n效果永久绑定存档`;
    permanentItem.zodiacName = zodiac;
    permanentItem.zodiacReceived = true;
    springGiftDom.style.display = "block";
}
function closeSpringModal(){springGiftDom.style.display = "none";}

let loadTime = 10;
const loadTimer = setInterval(()=>{
    loadTime--;
    timeCount.innerText = loadTime;
    garbleBox.innerHTML = randomGarble(35)+"<br>资源加载异常,请勿关闭页面<br>剩余等待时间:"+loadTime;
    if(loadTime <= 0){
        clearInterval(loadTimer);
        loadMask.style.display = "none";
        if(isBirthDate()) shopButton.classList.remove("btn-disabled");
        else shopButton.classList.add("btn-disabled");
        if(isSpringDay() && !permanentItem.zodiacReceived){
            const year = new Date().getFullYear();
            openSpringModal(getZodiac(year));
        }
    }
},1000);
function randomGarble(len){
    const chars = "█▓▒░§¶<>!@#$%&*?~";
    let str = "";
    for(let i=0;i<len;i++) str += chars[Math.floor(Math.random()*chars.length)];
    return str;
}

function screenWhiteFlash(){
    flashMask.style.opacity = 1;
    setTimeout(()=>flashMask.style.opacity=0,80);
}
function buildJumpGarble(){
    garbleLayer.innerHTML = "";
    const strPool = "█▓▒░0123456789abcdef!@#%&";
    for(let i=0;i<42;i++){
        for(let j=0;j<95;j++){
            const span = document.createElement("span");
            span.className = "garble-text-piece";
            span.innerText = strPool[Math.floor(Math.random()*strPool.length)];
            span.style.left = j*12+"px";
            span.style.top = i*14+"px";
            span.style.animationDelay = Math.random()*0.4+"s";
            garbleLayer.appendChild(span);
        }
    }
}
function clearJumpGarble(){garbleLayer.innerHTML="";}

function triggerScare(){
    if(isScare) return;
    isScare = true;
    buildJumpGarble();
    jumpScare.style.display = "flex";
    playHorrorAudio();
    const flashLoop = setInterval(screenWhiteFlash,60);
    setTimeout(()=>{
        clearInterval(flashLoop);
        jumpScare.style.display = "none";
        clearJumpGarble();
        isScare = false;
    },2600);
}
function playHorrorAudio(){
    const audioCtx = new (window.AudioContext||window.webkitAudioContext)();
    const osc1 = audioCtx.createOscillator();
    osc1.type = "sawtooth";
    osc1.frequency.setValueAtTime(90,audioCtx.currentTime);
    osc1.frequency.exponentialRampToValueAtTime(20,audioCtx.currentTime+0.8);
    const osc2 = audioCtx.createOscillator();
    osc2.type = "square";
    osc2.frequency.setValueAtTime(600,audioCtx.currentTime);
    osc2.frequency.exponentialRampToValueAtTime(1500,audioCtx.currentTime+0.4);
    const oscNoise = audioCtx.createOscillator();
    oscNoise.type = "triangle";
    oscNoise.frequency.setValueAtTime(220,audioCtx.currentTime);
    const gain = audioCtx.createGain();
    gain.gain.setValueAtTime(0,audioCtx.currentTime);
    gain.gain.linearRampToValueAtTime(0.8,audioCtx.currentTime+0.04);
    gain.gain.linearRampToValueAtTime(0,audioCtx.currentTime+2.6);
    osc1.connect(gain);osc2.connect(gain);oscNoise.connect(gain);gain.connect(audioCtx.destination);
    osc1.start();osc2.start();oscNoise.start();
    osc1.stop(audioCtx.currentTime+2.6);osc2.stop(audioCtx.currentTime+2.6);oscNoise.stop(audioCtx.currentTime+2.6);
}
function playerDieReset(){
    if(gameOver) return;
    gameOver = true;
    triggerScare();
    setTimeout(()=>{
        gold = 0; totalPlant = 0; player.x=380;player.y=320;
        entityList = []; initFarmLand(); gameOver = false;
        document.getElementById("goldShow").innerText = gold;
        document.getElementById("plantShow").innerText = totalPlant;
    },3200);
}

function spawnEntity(){
    const type = entityConfig[Math.floor(Math.random()*entityConfig.length)];
    let x,y;
    const side = Math.floor(Math.random()*4);
    if(side===0){x=Math.random()*800;y=-80;}
    else if(side===1){x=810;y=Math.random()*500;}
    else if(side===2){x=Math.random()*800;y=510;}
    else {x=-80;y=Math.random()*500;}
    entityList.push({
        x,y,w:type.w,h:type.h,baseSpeed:type.speed,nowSpeed:type.speed,
        fillColor:type.color,eyeColor:type.eye,aiType:type.ai,
        timer:0,disguise:true,reveal:false,teleCd:0
    });
}
function rectHit(a,b){
    return a.x < b.x+b.w && a.x+a.w > b.x && a.y < b.y+b.h && a.y+a.h > b.y;
}
function refreshEntityAI(){
    let globalSlow = permanentItem.slowAllMonster ? 0.75 : 1;
    const zodiacNow = zodiacBuff[permanentItem.zodiacName] || {};
    if(zodiacNow.monsterSlow) globalSlow *= (1-zodiacNow.monsterSlow);
    const exorcistRange = 180;

    for(let e of entityList){
        const dx = player.x - e.x;
        const dy = player.y - e.y;
        const dist = Math.hypot(dx,dy);
        e.timer++;

        if(bannerLockFrame > 0){
            e.nowSpeed = 0;
            const centerX = 400, centerY = 250;
            const cdx = centerX - e.x;
            const cdy = centerY - e.y;
            const cDist = Math.hypot(cdx,cdy);
            if(cDist > 6){
                e.x += cdx/cDist * 1.2;
                e.y += cdy/cDist * 1.2;
            }
            continue;
        }

        let exorcistSlow = 1;
        if(permanentItem.exorcist && dist < exorcistRange){
            exorcistSlow = 0.6;
            if(e.aiType === "teleport") e.teleCd += 120;
            if(e.aiType === "rush") e.timer = 0;
        }

        switch(e.aiType){
            case "lurk": e.nowSpeed = dist<250 ? e.baseSpeed*1.6 : e.baseSpeed*0.5; break;
            case "stalk": e.nowSpeed = dist<180 ? e.baseSpeed*2.2 : e.baseSpeed*0.35; break;
            case "crawl": e.nowSpeed=e.baseSpeed; if(dist<130) e.y += dy>0 ? 1.5:-1.5; break;
            case "rush": e.nowSpeed=e.baseSpeed; if(e.timer>180&&dist<160){e.nowSpeed=e.baseSpeed*2.8;if(e.timer>220)e.timer=0;} break;
            case "giant": e.nowSpeed = dist<220 ? e.baseSpeed*1.8:e.baseSpeed; break;
            case "flesh":
                e.nowSpeed=e.baseSpeed;
                if(dist<160 && !permanentItem.antiNoise){distortionMask.style.opacity=0.35;noiseMask.style.opacity=0.25;}
                break;
            case "whisper": e.nowSpeed=e.baseSpeed; break;
            case "teleport":
                e.teleCd++;
                if(e.teleCd>350 && Math.random()<0.008){
                    e.x = player.x + (Math.random()-0.5)*220;
                    e.y = player.y + (Math.random()-0.5)*160;
                    e.teleCd=0; screenWhiteFlash();
                }
                break;
            case "disguise":
                if(e.timer>200 || dist<90){e.disguise=false;e.reveal=true;e.nowSpeed=e.baseSpeed*2.4;screenWhiteFlash();}
                break;
            case "mirror":
                e.nowSpeed=e.baseSpeed;
                if(keyBoard["w"]) e.y -= e.nowSpeed;
                if(keyBoard["s"]) e.y += e.nowSpeed;
                if(keyBoard["a"]) e.x -= e.nowSpeed;
                if(keyBoard["d"]) e.x += e.nowSpeed;
                break;
            case "scarecrow": e.nowSpeed=dist<120 ? e.baseSpeed*2 : 0; break;
            case "backCrawl":
                e.nowSpeed=e.baseSpeed;
                if(dist<180){
                    const angle = Math.atan2(dy,dx)+Math.PI/6;
                    e.x += Math.cos(angle)*e.nowSpeed;
                    e.y += Math.sin(angle)*e.nowSpeed;
                }
                break;
        }
        e.nowSpeed *= globalSlow * exorcistSlow;
        if(dist>0){e.x += (dx/dist)*e.nowSpeed; e.y += (dy/dist)*e.nowSpeed;}
        if(rectHit(player,e)) playerDieReset();
    }
}

function drawBlock(x,y,w,h,color){
    ctx.fillStyle = color;
    ctx.fillRect(Math.floor(x),Math.floor(y),w,h);
}

function drawPlayer(){
    let addSpd = permanentItem.addSpeed ? 0.4 : 0;
    const zodiacNow = zodiacBuff[permanentItem.zodiacName];
    if(zodiacNow?.addSpeed) addSpd += zodiacNow.addSpeed;
    const realSpeed = player.baseSpeed + addSpd;

    if(!gameOver){
        if(keyBoard["w"]||keyBoard["arrowup"]) player.y -= realSpeed;
        if(keyBoard["s"]||keyBoard["arrowdown"]) player.y += realSpeed;
        if(keyBoard["a"]||keyBoard["arrowleft"]) player.x -= realSpeed;
        if(keyBoard["d"]||keyBoard["arrowright"]) player.x += realSpeed;
        player.x = Math.max(0,Math.min(800-player.w,player.x));
        player.y = Math.max(0,Math.min(500-player.h,player.y));
    }

    if(permanentItem.exorcist){
        drawBlock(player.x+10,player.y,24,24,"#ffe9d1");
        drawBlock(player.x+6,player.y-6,30,12,"#0f0f0f");
        drawBlock(player.x+14,player.y-10,16,8,"#ffdd00");
        drawBlock(player.x+13,player.y+7,6,6,"#000");
        drawBlock(player.x+25,player.y+7,6,6,"#000");
        drawBlock(player.x+15,player.y+19,10,4,"#dd2222");
        drawBlock(player.x+5,player.y+24,34,28,"#f8faff");
        drawBlock(player.x+7,player.y+26,30,4,"#ffcc00");
        drawBlock(player.x+8,player.y+46,28,4,"#ffcc00");
        drawBlock(player.x+8,player.y+28,4,18,"#ffcc00");
        drawBlock(player.x+26,player.y+28,4,18,"#ffcc00");
        drawBlock(player.x+9,player.y+52,13,12,"#222");
        drawBlock(player.x+22,player.y+52,13,12,"#222");
        drawBlock(player.x+37,player.y+27,7,24,"#cacaca");
        drawBlock(player.x+36,player.y+26,9,4,"#882222");
        ctx.beginPath();
        ctx.arc(player.x+22,player.y+32,42,0,Math.PI*2);
        ctx.strokeStyle = "rgba(255,222,70,0.25)";
        ctx.lineWidth = 2;
        ctx.stroke();
    }else{
        drawBlock(player.x+10,player.y,24,24,"#ffdab9");
        drawBlock(player.x+8,player.y-4,28,10,"#8b4513");
        drawBlock(player.x+12,player.y+8,5,5,"#000");
        drawBlock(player.x+26,player.y+8,5,5,"#000");
        drawBlock(player.x+16,player.y+18,12,3,"#cc6666");
        drawBlock(player.x+6,player.y+24,32,26,"#0066cc");
        drawBlock(player.x+10,player.y+26,24,8,"#f00");
        drawBlock(player.x+8,player.y+50,12,14,"#333");
        drawBlock(player.x+24,player.y+50,12,14,"#333");
        drawBlock(player.x+36,player.y+30,10,6,"#ffdab9");
    }
}

function drawAllEntity(){
    ctx.globalAlpha = 0.78;
    for(let e of entityList){
        if(e.disguise){
            drawBlock(e.x+10,e.y,24,24,"#ffdab9");
            drawBlock(e.x+8,e.y-4,28,10,"#8b4513");
            drawBlock(e.x+12,e.y+8,5,5,tick%120<60?"#f00":"#000");
            drawBlock(e.x+26,e.y+8,5,5,tick%120<60?"#f00":"#000");
            drawBlock(e.x+6,e.y+24,32,26,"#0066cc");
            drawBlock(e.x+8,e.y+50,12,14,"#333");
            drawBlock(e.x+24,e.y+50,12,14,"#333");
        }else{
            drawBlock(e.x,e.y,e.w,e.h,e.fillColor);
            const eyeW = e.w*0.18;
            const eyeH = e.h*0.18;
            drawBlock(e.x+e.w*0.22,e.y+e.h*0.25,eyeW,eyeH,e.eyeColor);
            drawBlock(e.x+e.w*0.6,e.y+e.h*0.25,eyeW,eyeH,e.eyeColor);
            if(e.aiType==="flesh"){
                ctx.strokeStyle="#f00";ctx.lineWidth=2;
                ctx.beginPath();ctx.moveTo(e.x+e.w/2,e.y);ctx.lineTo(e.x+e.w*0.45,e.y+e.h*0.35);ctx.lineTo(e.x+e.w*0.55,e.y+e.h*0.55);ctx.stroke();
            }
            if(e.aiType==="backCrawl"){
                for(let i=0;i<4;i++) drawBlock(e.x+e.w*0.15+i*12,e.y+e.h,5,10,e.fillColor);
            }
            if(e.aiType==="scarecrow"){
                drawBlock(e.x+e.w*0.1,e.y+e.h*0.3,e.w*0.8,4,"#5a3838");
                drawBlock(e.x+e.w*0.45,e.y+e.h*0.2,4,e.h*0.6,"#5a3838");
            }
        }
    }
    ctx.globalAlpha = 1;
}

function initFarmLand(){
    landList = [];
    let startX = 120;
    for(let i=0;i<4;i++){
        landList.push({
            x:startX+i*80,y:380,w:70,h:80,
            seed:false,grow:0,ripe:false,shake:Math.random()*100,bloodPlant:Math.random()<0.35
        });
    }
}
function drawFarmLand(){
    const zodiacNow = zodiacBuff[permanentItem.zodiacName];
    let growSpeed = zodiacNow?.growRate || 1;
    if(permanentItem.cropMature) growSpeed = 999;
    landList.forEach(land=>{
        drawBlock(land.x,land.y,land.w,land.h,"#180000");
        ctx.strokeStyle="#770000";ctx.lineWidth=2;
        ctx.strokeRect(land.x,land.y,land.w,land.h);
        if(land.seed){
            const shakeOff = Math.sin(tick*0.12+land.shake)*5;
            const plantH = land.ripe ? 98 : land.grow*14*growSpeed;
            const stem = land.bloodPlant ? "#770000":"#442211";
            const fruit = land.bloodPlant ? "#f00":"#aa2222";
            drawBlock(land.x+34+shakeOff,land.y-plantH,6,plantH,stem);
            drawBlock(land.x+28+shakeOff,land.y-plantH-12,14,16,fruit);
            if(!land.ripe) land.grow += 0.03;
            if(land.grow >=7) land.ripe = true;
        }
    });
}

canvas.onclick = function(e){
    if(gameOver) return;
    const rect = canvas.getBoundingClientRect();
    const mx = e.clientX - rect.left;
    const my = e.clientY - rect.top;
    landList.forEach(land=>{
        if(mx>land.x && mx<land.x+land.w && my>land.y && my<land.y+land.h){
            if(!land.seed){
                land.seed=true;land.grow=0;land.bloodPlant=Math.random()<0.35;
                totalPlant++;
                document.getElementById("plantShow").innerText = totalPlant;
                if(totalPlant>=15){triggerScare();totalPlant=0;document.getElementById("plantShow").innerText=0;}
            }else if(land.ripe){
                let earn = 200;
                if(zodiacBuff[permanentItem.zodiacName]?.goldPlus) earn *= zodiacBuff[permanentItem.zodiacName].goldPlus;
                if(permanentItem.cropMature && Math.random()<0.5) earn *=2;
                gold += earn;
                document.getElementById("goldShow").innerText = gold;
                land.seed=false;land.ripe=false;land.grow=0;
            }
        }
    })
}

function saveGame(){
    const saveData = {gold,totalPlant,landList,player,permanentItem};
    localStorage.setItem("farmHorrorSave",JSON.stringify(saveData));
    alert("存档保存完毕,辟邪师、万魂幡次数、生肖buff永久写入本地");
}
function loadGame(){
    const saveStr = localStorage.getItem("farmHorrorSave");
    if(!saveStr){alert("暂无存档");return;}
    try{
        const data = JSON.parse(saveStr);
        gold = data.gold;
        totalPlant = data.totalPlant;
        landList = data.landList;
        player.x = data.player.x;player.y=data.player.y;
        permanentItem = data.permanentItem;
        document.getElementById("goldShow").innerText = gold;
        document.getElementById("plantShow").innerText = totalPlant;
        alert("读档成功,所有生日道具、生肖效果完整恢复");
    }catch(err){alert("存档文件损坏");}
}

function gameMainLoop(){
    tick++;
    flashCd--;
    if(bannerLockFrame>0){
        bannerLockFrame--;
        if(bannerLockFrame<=0) bannerCover.style.opacity=0;
    }
    distortionMask.style.opacity = Math.max(0,Number(distortionMask.style.opacity)-0.008);
    noiseMask.style.opacity = Math.max(0,Number(noiseMask.style.opacity)-0.008);

    ctx.fillStyle="#0a0000";
    ctx.fillRect(0,0,800,500);

    if(!gameOver && tick%750===0 && entityList.length<12) spawnEntity();
    if(!gameOver) refreshEntityAI();

    if(tick%400===0){
        const wordPool = ["田里有东西","别收割","它盯着你","逃不掉","别回头","它们从土里爬出来"];
        const wordDom = document.createElement("div");
        wordDom.className="blood-word";
        wordDom.innerText = wordPool[Math.floor(Math.random()*wordPool.length)];
        wordDom.style.left = Math.random()*720+"px";
        wordDom.style.top = Math.random()*420+"px";
        wordDom.style.fontSize = 12+Math.random()*14+"px";
        gameBox.appendChild(wordDom);
        bloodTextArr.push({dom:wordDom,life:220});
    }

    bloodTextArr.forEach((item,idx)=>{
        item.life--;
        if(item.life<=0){item.dom.remove();bloodTextArr.splice(idx,1);}
    })

    if(flashCd<=0 && Math.random()<0.002){screenWhiteFlash();flashCd=300;}

    let minDist = 9999;
    entityList.forEach(e=>{
        const d = Math.hypot(player.x-e.x,player.y-e.y);
        if(d<minDist) minDist=d;
    })
    warnUI.style.opacity = Math.max(0,Number(warnUI.style.opacity)-0.012);
    if(minDist<160){warnUI.style.opacity=1;warnUI.innerText="实体靠近,注意躲避";}
    if(minDist<100){warnUI.style.opacity=1;warnUI.innerText="极度危险!立刻拉开距离";screenWhiteFlash();}

    drawFarmLand();
    drawAllEntity();
    drawPlayer();

    requestAnimationFrame(gameMainLoop);
}

initFarmLand();
gameMainLoop();
</script>
</body>

Game Source: 像素农夫物语 V0.1 愚人节恐怖限定

Creator: BraveDragon92

Libraries: none

Complexity: complex (1026 lines, 35.6 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: v0-1-bravedragon92-mrykx12g" to link back to the original. Then publish at arcadelab.ai/publish.