🎮ArcadeLab

Remix of 三国大战

by ChromeOtter72
412 lines20.3 KB
▶ Play
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>三国大战|优化完整版</title>
    <link rel="manifest" href="data:application/json;base64,eyJuYW1lIjoi5LiD5Zu95aSn5LqLIiwic2hvcnRfbmFtZSI6IuS4gOWbueWkp+S6siIsInN0YXJ0X3VybCI6Ii8iLCJkaXNwbGF5Ijoic3RhbmRhbGUiLCJiYWNrZ3JvdW5kX2NvbG9yIjoiIzAwMDAwMCIsInRoZW1lX2NvbG9yIjoiIzE1MTUxNSJ9">
    <style>
        *{box-sizing:border-box;margin:0;padding:0;font-family:"Microsoft YaHei",sans-serif}
        body{background:#121212;color:#eee;padding:12px;font-size:15px}
        .container{max-width:720px;margin:0 auto}
        h1{text-align:center;color:#ffd040;font-size:22px;margin-bottom:16px}
        .panel{background:#202020;border-radius:10px;padding:14px;margin-bottom:12px}
        .btn{display:inline-block;padding:9px 14px;border:none;border-radius:7px;background:#c83232;color:white;margin:6px 4px;cursor:pointer;font-size:14px}
        .btn-green{background:#288b36}
        .btn-blue{background:#2b5fbb}
        .btn-yellow{background:#b48616}
        .btn-grey{background:#444444}
        textarea,input{width:100%;padding:10px;margin:8px 0;border-radius:6px;border:none}
        .row{display:flex;gap:8px;flex-wrap:wrap}
        .log{max-height:220px;overflow-y:auto;background:#0a0a0a;padding:10px;border-radius:8px}
        .flex-between{display:flex;justify-content:space-between;align-items:center}
        .slot{padding:8px;background:#2c2c2c;border-radius:6px;margin:6px 0}
        .lord-card{
            background:#292929;
            border:1px solid #444;
            border-radius:10px;
            padding:12px;
            margin:10px 0;
        }
        .portrait-box{
            width:70px;
            height:70px;
            background:#3a3a3a;
            border-radius:8px;
            display:flex;
            align-items:center;
            justify-content:center;
            font-size:32px;
            color:#ffd040;
            float:left;
            margin-right:12px;
        }
        .clearfix{clear:both}
        #startSelect{display:block}
        #gameMain{display:none}
    </style>
</head>
<body>
    <div class="container">
        <h1>⚔️ 三国大战(优化完整版)</h1>

        <div id="startSelect" class="panel">
            <h3>🏴 请选择你的主公势力开局</h3>
            <div class="lord-card" onclick="pickLord('刘备')">
                <div class="portrait-box">👑</div>
                <h4>刘备|仁德之主</h4>
                <p>✅优势:民心高,城防初始较高,粮草充足,适合稳扎稳打防御发育</p>
                <p>初始资源:粮草130000|人口110000|城防95|税收4200/回合</p>
                <div class="clearfix"></div>
                <button class="btn btn-green" onclick="pickLord('刘备')">选择刘备开局</button>
            </div>
            <div class="lord-card" onclick="pickLord('曹操')">
                <div class="portrait-box">⚔️</div>
                <h4>曹操|乱世奸雄</h4>
                <p>✅优势:税收收入高,经济强势,适合扩张征战</p>
                <p>初始资源:粮草110000|人口125000|城防72|税收5300/回合</p>
                <div class="clearfix"></div>
                <button class="btn btn-green" onclick="pickLord('曹操')">选择曹操开局</button>
            </div>
            <div class="lord-card" onclick="pickLord('孙权')">
                <div class="portrait-box">🛡️</div>
                <h4>孙权|江东之主</h4>
                <p>✅优势:粮草储备丰厚,AI敌人初期较少来犯,适合慢慢发展</p>
                <p>初始资源:粮草150000|人口96000|城防86|税收3800/回合</p>
                <div class="clearfix"></div>
                <button class="btn btn-green" onclick="pickLord('孙权')">选择孙权开局</button>
            </div>
        </div>

        <div id="gameMain">
            <div class="panel flex-between">
                <span>🔗 服务器状态:<span id="serverStatus">在线</span></span>
                <button class="btn btn-blue" onclick="reconnectServer()">一键重连服务器</button>
            </div>
            <div class="panel">
                <h3>👑 当前主公:<span id="myLordName"></span></h3>
            </div>
            <div class="panel">
                <h3>💾 存档管理|多槽位 + 导入导出备份</h3>
                <div class="slot">存档槽1 <button class="btn btn-green" onclick="loadSave(1)">读取</button><button class="btn" onclick="saveGame(1)">保存</button></div>
                <div class="slot">存档槽2 <button class="btn btn-green" onclick="loadSave(2)">读取</button><button class="btn" onclick="saveGame(2)">保存</button></div>
                <div class="slot">存档槽3 <button class="btn btn-green" onclick="loadSave(3)">读取</button><button class="btn" onclick="saveGame(3)">保存</button></div>
                <div class="row">
                    <button class="btn btn-yellow" onclick="exportSave()">📥导出存档文件(JSON)</button>
                    <button class="btn btn-yellow" onclick="document.getElementById('importFile').click()">📤导入存档文件</button>
                    <input type="file" id="importFile" accept=".json" hidden onchange="importSave(event)">
                </div>
            </div>

            <div class="panel">
                <h3>🏰 我方城池经济</h3>
                <p>人口:<span id="pop"></span>|粮草:<span id="food"></span>|税收:<span id="tax"></span>/回合|城防:<span id="def"></span></p>
                <div class="row">
                    <button class="btn btn-green" onclick="recruitSoldier()">征兵</button>
                    <button class="btn btn-green" onclick="buildWall()">修筑城防</button>
                    <button class="btn btn-yellow" onclick="recruitHero()">酒馆寻访武将</button>
                    <button class="btn" onclick="nextTurn()">下一回合</button>
                </div>
            </div>

            <div class="panel">
                <h3>👥 麾下武将(带专属特技)</h3>
                <div id="heroList"></div>
            </div>

            <div class="panel" id="eventPanel">
                <h3>📜 当前事件</h3>
                <p id="eventText">暂无事件,静待时局变化</p>
                <div class="row" id="eventBtnGroup"></div>
            </div>

            <div class="panel">
                <div class="flex-between">
                    <h3>📋 战报日志</h3>
                    <button class="btn btn-blue" onclick="toggleLogMode()">切换简略/详细模式</button>
                </div>
                <div class="log" id="gameLog"></div>
            </div>

            <div class="panel">
                <h3>🔊 设置</h3>
                <button class="btn btn-blue" onclick="toggleSound()">开关音效</button>
                <button class="btn btn-grey" onclick="playBgMusic()">播放背景音乐</button>
                <p>💡提示:可添加到手机桌面开启PWA离线游玩</p>
            </div>
        </div>
    </div>

    <script>
        let audioCtx;
        let bgMusicPlaying = false;
        function initAudio(){
            audioCtx = new (window.AudioContext || window.webkitAudioContext)();
        }
        function playBattleSound(){
            if(!gameState.soundOn || !audioCtx) return;
            const osc = audioCtx.createOscillator();
            const gain = audioCtx.createGain();
            osc.connect(gain);gain.connect(audioCtx.destination);
            osc.type="sawtooth";osc.frequency.setValueAtTime(160,audioCtx.currentTime);
            gain.gain.setValueAtTime(0.18,audioCtx.currentTime);
            osc.start(audioCtx.currentTime);osc.stop(audioCtx.currentTime+0.35);
        }
        function playRecruitSound(){
            if(!gameState.soundOn || !audioCtx) return;
            const osc = audioCtx.createOscillator();
            const gain = audioCtx.createGain();
            osc.connect(gain);gain.connect(audioCtx.destination);
            osc.type="sine";osc.frequency.setValueAtTime(440,audioCtx.currentTime);
            gain.Gain.setValueAtTime(0.12,audioCtx.currentTime);
            osc.start(audioCtx.currentTime);osc.stop(audioCtx.currentTime+0.22);
        }
        function playEventSound(){
            if(!gameState.soundOn || !audioCtx) return;
            const osc = audioCtx.createOscillator();
            const gain = audioCtx.createGain();
            osc.connect(gain);gain.connect(audioCtx.destination);
            osc.type="triangle";osc.frequency.setValueAtTime(520,audioCtx.currentTime);
            gain.Gain.setValueAtTime(0.14,audioCtx.currentTime);
            osc.start(audioCtx.currentTime);osc.stop(audioCtx.currentTime+0.3);
        }
        function playBgMusic(){
            if(!audioCtx) initAudio();
            if(bgMusicPlaying){
                alert("背景音乐正在播放");return;
            }
            bgMusicPlaying=true;
            alert("🎵背景音乐已开启");
        }

        const lordConfig = {
            "刘备":{pop:110000,food:130000,tax:4200,def:95},
            "曹操":{pop:125000,food:110000,tax:5300,def:72},
            "孙权":{pop:96000,food:150000,tax:3800,def:86}
        }
        let gameState = null;
        const heroPool = [
            {name:"赵云",skill:"七进七出",atkBonus:32},
            {name:"马超",skill:"铁骑奔袭",atkBonus:28},
            {name:"周瑜",skill:"火烧连营",strategyBonus:38},
            {name:"黄忠",skill:"百步穿杨",atkBonus:26},
            {name:"庞统",skill:"连环计",strategyBonus:35}
        ]
        const eventPool = [
            {title:"黄巾余党作乱",desc:"城外出现黄巾残部,劫掠乡间,如何抉择?opt1:派兵清剿(-500粮草,获得战利品)",opt2:加固守备,不予出击(-80城防资源)"},
            {title:"谋士登门求见",desc:"一位隐士谋士听闻主公威名,前来投奔。opt1:接纳入幕,麾下智谋大增",opt2:婉言谢绝,专注民生"},
            {title:"大旱灾情",desc:"境内久旱无雨,粮草减产。opt1:开仓赈济民心大涨(-6000粮草)",opt2:紧缩粮仓,优先供给军队"}
        ]

        function pickLord(lordName){
            initAudio();
            const cfg = lordConfig[lordName];
            gameState = {
                myLord:lordName,
                pop:cfg.pop,food:cfg.food,tax:cfg.tax,def:cfg.def,turn:1,
                heroes:[],
                aiFactions:[
                    {name:"曹操",aggressive:false,ally:null},
                    {name:"孙权",aggressive:false,ally:null},
                    {name:"袁绍",aggressive:false,ally:null},
                    {name:"刘表",aggressive:false,ally:null}
                ],
                event:null,
                logMode:"simple",soundOn:true
            }
            if(lordName==="刘备") gameState.heroes=[{name:"关羽",skill:"青龙冲锋",atkBonus:35},{name:"张飞",skill:"怒吼破敌",atkBonus:30}];
            if(lordName==="曹操") gameState.heroes=[{name:"夏侯惇",skill:"拔矢啖睛",atkBonus:33},{name:"荀彧",skill:"王佐之才",strategyBonus:42}];
            if(lordName==="孙权") gameState.heroes=[{name:"甘宁",skill:"锦帆突袭",atkBonus:31},{name:"鲁肃",skill:"榻上策",strategyBonus:36}];

            document.getElementById("startSelect").style.display="none";
            document.getElementById("gameMain").style.display="block";
            document.getElementById("myLordName").innerText = lordName;
            renderAll();
            addLog(`🎮【${lordName}】势力开局成功!粮草储备充足,可以安心发育防御,强敌会随回合逐步活跃`);
        }

        let db;
        const DB_NAME = "sanguoWarDB";
        function initDB(){
            const req = indexedDB.open(DB_NAME,1);
            req.onupgradeneeded = e=>{
                db = e.target.result;
                if(!db.objectStoreNames.contains("saves")) db.createObjectStore("saves",{keyPath:"slot"})
            }
            req.onsuccess = e=>{db = e.target.result;console.log("✅本地存档数据库就绪")}
        }
        function saveGame(slot){
            if(!gameState){alert("请先选择主公开局");return}
            const tx = db.transaction("saves","readwrite");
            tx.objectStore("saves").put({slot,data:JSON.parse(JSON.stringify(gameState))})
            alert(`✅存档槽${slot}保存成功!浏览器本地IndexedDB永久保存`)
        }
        function loadSave(slot){
            const tx = db.transaction("saves","readonly");
            const res = tx.objectStore("saves").get(slot);
            res.onsuccess = ()=>{
                if(res.result){
                    gameState = res.result.data;
                    document.getElementById("startSelect").style.display="none";
                    document.getElementById("gameMain").style.display="block";
                    document.getElementById("myLordName").innerText = gameState.myLord;
                    renderAll();
                    alert(`✅读取存档槽${slot}完成`)
                }else alert("❌该槽位暂无存档")
            }
        }
        function exportSave(){
            if(!gameState){alert("请先选择主公开局");return}
            const blob = new Blob([JSON.stringify(gameState,null,2)],{type:"application/json"});
            const url = URL.createObjectURL(blob);
            const a = document.createElement("a");
            a.href = url;a.download="三国大战存档.json";a.click();URL.revokeObjectURL(url);
        }
        function importSave(e){
            const f = e.target.files[0];
            const r = new FileReader();
            r.onload = ev=>{
                try{
                    gameState = JSON.parse(ev.target.result);
                    document.getElementById("startSelect").style.display="none";
                    document.getElementById("gameMain").style.display="block";
                    document.getElementById("myLordName").innerText = gameState.myLord;
                    renderAll();
                    alert("✅存档导入成功")
                }catch(err){alert("❌存档文件损坏")}
            }
            r.readAsText(f)
        }
        function aiTurnLogic(){
            gameState.aiFactions.forEach(faction=>{
                if(gameState.turn <= 8){
                    addLog(`🏘️【${faction.name}】休养生息,整顿领地,暂无大举出兵打算`);
                    return;
                }
                if(faction.ally){
                    addLog(`🤝【${faction.name}】与盟友携手调兵,准备讨伐外敌`)
                }else if(Math.random()>0.75){
                    const target = gameState.aiFactions.filter(x=>x!==faction)[Math.floor(Math.random()*3)];
                    faction.ally = target.name;
                    target.ally = faction.name;
                    addLog(`📜【${faction.name}】与【${target.name}】缔结同盟!`)
                }else if(faction.aggressive && Math.random()>0.4){
                    playBattleSound();
                    addLog(`⚔️【${faction.name}】整备军队,虎视眈眈准备发起进攻!`)
                }else{
                    addLog(`🏘️【${faction.name}】休养生息,发展城池`)
                }
            })
        }
        function triggerRandomEvent(){
            if(Math.random()>0.65){
                playEventSound();
                const evt = eventPool[Math.floor(Math.random()*eventPool.length)];
                gameState.event = evt;
                document.getElementById("eventText").innerText = evt.desc;
                const btnWrap = document.getElementById("eventBtnGroup");
                btnWrap.innerHTML = `<button class="btn btn-green" onclick="eventChoice(1)">${evt.opt1}</button><button class="btn" onclick="eventChoice(2)">${evt.opt2}</button>`
            }else{
                gameState.event = null;
                document.getElementById("eventText").innerText = "暂无事件,静待时局变化";
                document.getElementById("eventBtnGroup").innerHTML = ""
            }
        }
        function eventChoice(opt){
            if(gameState.event.title==="黄巾余党作乱"){
                if(opt===1) gameState.food -=500;
                else gameState.def -=15;
            }else if(gameState.event.title==="谋士登门求见"){
                if(opt===1){
                    const newHero = heroPool[Math.floor(Math.random()*heroPool.length)];
                    gameState.heroes.push(newHero);
                    addLog(`🎉 获得武将【${newHero.name}】特技:${newHero.skill}`)
                }
            }else if(gameState.event.title==="大旱灾情"){
                if(opt===1) gameState.food -=6000;
            }
            gameState.event = null;
            document.getElementById("eventText").innerText = "暂无事件,静待时局变化";
            document.getElementById("eventBtnGroup").innerHTML = "";
            renderAll();
        }
        function recruitSoldier(){
            if(!gameState) return;
            if(gameState.food >= 3000){
                gameState.food -=3000;
                gameState.pop +=4200;
                playRecruitSound();
                addLog("🪖征兵完成,人口兵力上涨,粮草消耗3000")
                renderAll();
            }else alert("❌粮草不足,无法征兵")
        }
        function buildWall(){
            if(!gameState) return;
            if(gameState.food >=2000){
                gameState.food -=2000;
                gameState.def +=12;
                addLog("🧱修筑城防,城防+12,消耗粮草2000")
                renderAll();
            }else alert("❌粮草不足")
        }
        function recruitHero(){
            if(!gameState) return;
            if(gameState.food >=4500){
                gameState.food -=4500;
                const newHero = heroPool[Math.floor(Math.random()*heroPool.length)];
                gameState.heroes.push(newHero);
                playEventSound();
                addLog(`🍶酒馆寻访成功!招募【${newHero.name}】特技:${newHero.skill}`)
                renderAll();
            }else alert("❌粮草不足,无法寻访")
        }
        function nextTurn(){
            if(!gameState) return;
            gameState.turn++;
            gameState.food += gameState.tax;
            aiTurnLogic();
            triggerRandomEvent();
            addLog(`===== 第${gameState.turn}回合结束 =====`)
            renderAll();
        }
        function renderAll(){
            document.getElementById("pop").innerText = gameState.pop;
            document.getElementById("food").innerText = gameState.food;
            document.getElementById("tax").innerText = gameState.tax;
            document.getElementById("def").innerText = gameState.def;
            const heroWrap = document.getElementById("heroList");
            heroWrap.innerHTML = gameState.heroes.map(h=>`<div class="slot">✅${h.name}|特技:${h.skill}</div>`).join("")
        }
        function addLog(text){
            const logDom = document.getElementById("gameLog");
            if(gameState.logMode==="simple"){
                if(!text.includes("=====") && !text.includes("🏘️")) return;
            }
            const p = document.createElement("p");
            p.innerText = text;
            logDom.appendChild(p);
            logDom.scrollTop = logDom.scrollHeight;
        }
        function toggleLogMode(){
            gameState.logMode = gameState.logMode==="simple"?"full":"simple";
            alert(`战报模式切换为:${gameState.logMode==="simple"?"简略":"详细"}`)
        }
        function toggleSound(){
            gameState.soundOn = !gameState.soundOn;
            alert(`音效已${gameState.soundOn?"开启":"关闭"}`)
        }
        function reconnectServer(){
            document.getElementById("serverStatus").innerText = "重连中...";
            setTimeout(()=>{
                document.getElementById("serverStatus").innerText = "在线";
                alert("✅重连完成,单人模式支持离线缓存继续游玩")
            },2200)
        }
        setInterval(()=>{
            if(location.protocol !== "file:") fetch(location.href,{cache:"no-store"}).catch(()=>{})
        },1000*60*3)
        initDB();
    </script>
</body>
</html>

Game Source: Remix of 三国大战

Creator: ChromeOtter72

Libraries: none

Complexity: complex (412 lines, 20.3 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-chromeotter72" to link back to the original. Then publish at arcadelab.ai/publish.