HTML
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>我们的秘密基地 v2.0</title> <script src="https://cdn.tailwindcss.com"></script> <style> /* --- 全局与字体 --- */ @import url('https://fonts.googleapis.com/css2?family=Pixelify+Sans:wght@400;700&display=swap'); body { background-color: #1a1a2e; color: #e0e0e0; font-family: 'Pixelify Sans', sans-serif; overflow: hidden; user-select: none; } /* --- 像素化渲染 --- */ .pixel-art, img.pixel-art { image-rendering: pixelated; image-rendering: -moz-crisp-edges; image-rendering: crisp-edges; } /* --- 游戏主容器 --- */ #game-root { width: 100vw; height: 100vh; display: flex; } /* --- 游戏画面 --- */ #game-viewport { position: relative; width: 100%; height: 100%; overflow: hidden; background-color: #3a6b35; /* 默认草地颜色 */ border-right: 4px solid #0f0f1a; } #game-world { position: absolute; background-size: 40px 40px; background-image: linear-gradient(to right, rgba(0,0,0,0.05) 1px, transparent 1px), linear-gradient(to bottom, rgba(0,0,0,0.05) 1px, transparent 1px); transition: transform 0.2s linear; } .tile { position: absolute; width: 40px; height: 40px; } .character { width: 40px; height: 40px; position: absolute; z-index: 10; transition: left 0.2s linear, top 0.2s linear; } /* --- 聊天与UI --- */ #chat-ui-panel { width: 380px; flex-shrink: 0; background-color: #16213e; display: flex; flex-direction: column; padding: 1rem; } .chat-bubble { background-color: #0f3460; padding: 0.75rem; border-radius: 10px; max-width: 90%; word-wrap: break-word; } /* --- 游戏内UI元素 --- */ .game-ui-element { position: absolute; background-color: rgba(15, 15, 26, 0.7); border: 2px solid #537ec5; border-radius: 8px; color: white; z-index: 20; } #inventory-bar { bottom: 20px; left: 50%; transform: translateX(-50%); } .inventory-slot { width: 50px; height: 50px; border: 2px solid #537ec5; display: flex; align-items: center; justify-content: center; position: relative; } .inventory-slot.selected { border-color: #e94560; box-shadow: 0 0 10px #e94560; } .item-count { position: absolute; bottom: 1px; right: 3px; font-size: 0.8rem; text-shadow: 1px 1px 2px black; } #controls { right: 20px; bottom: 20px; } .control-btn { width: 50px; height: 50px; background-color: rgba(15, 15, 26, 0.8); border: 2px solid #537ec5; font-size: 1.5rem; display: flex; justify-content: center; align-items: center; } .control-btn:active { background-color: #537ec5; } /* --- 弹窗 --- */ .modal-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.7); z-index: 50; display: flex; justify-content: center; align-items: center; } .modal-content { background: #16213e; border: 3px solid #e94560; border-radius: 10px; padding: 2rem; min-width: 400px; max-width: 80%; } #info-popup { position: absolute; background-color: rgba(0,0,0,0.85); border: 1px solid #f1c40f; padding: 10px; border-radius: 5px; color: white; pointer-events: none; z-index: 100; font-size: 0.9rem; } </style> </head> <body> <div id="game-root"> <!-- 游戏主视口 --> <div id="game-viewport"> <!-- 游戏世界 (所有物体都在这里) --> <div id="game-world"></div> <!-- 游戏内固定UI --> <div id="gold-display" class="game-ui-element top-4 left-4 p-2 flex items-center"> <img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PGNpcmNsZSBjeD0iMTIiIGN5PSIxMiIgcj0iMTAiIGZpbGw9IiNmMWMyMGYiLz48Y2lyY2xlIGN5PSIxMiIgcj0iOCIgZmlsbD0iI2YzOWMxMiIvPjxwYXRoIGQ9Ik0xMiA2IEMxMC4zIDYgOSA4LjMgOSA5LjUgQzkgMTEuNSAxMiAxNSAxMiAxNSBDMTIgMTUgMTUgMTEuNSAxNSA5LjUgQzE1IDguMyAxMy43IDYgMTIgNiBaIiBmaWxsPSIjZjFjMjBmIi8+PC9zdmc+" class="w-6 h-6 mr-2 pixel-art"> <span id="gold-amount">9999999</span> </div> <button id="shop-btn" class="game-ui-element top-4 right-4 p-2">商店</button> <button id="map-btn" class="game-ui-element top-16 right-4 p-2">地图</button> <div id="inventory-bar" class="game-ui-element flex gap-1 p-1"></div> <div id="controls" class="game-ui-element p-1 grid grid-cols-3 gap-1 w-48 bg-transparent border-none"> <div></div> <button id="btn-up" class="control-btn rounded-t-lg"></button> <div></div> <button id="btn-left" class="control-btn rounded-l-lg"></button> <button id="btn-action" class="control-btn bg-red-600/80 border-red-400"></button> <button id="btn-right" class="control-btn rounded-r-lg"></button> <div></div> <button id="btn-down" class="control-btn rounded-b-lg"></button> <div></div> </div> <div id="info-popup" class="hidden"></div> </div> <!-- 聊天面板 --> <div id="chat-ui-panel"> <h2 class="text-2xl font-bold border-b-2 border-orange-400 pb-2 mb-4 text-center">私人频道</h2> <div id="chat-history" class="flex-grow overflow-y-auto pr-2 space-y-4"> <div class="flex items-start gap-3"> <img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMiAzMiI+PHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiBmaWxsPSJ0cmFuc3BhcmVudCIvPjxyZWN0IHg9IjEzIiB5PSI1IiB3aWR0aD0iNiIgaGVpZ2h0PSIxIiBmaWxsPSIjM0MzQzNDIi8+PHJlY3QgeD0iMTIiIHk9IjYiIHdpZHRoPSI4IiBoZWlnaHQ9IjEiIGZpbGw9IiMzQzNDM0MiLz48cmVjdCB4PSIxMSIgeT0iNyIgd2lkdGg9IjEwIiBoZWlnaHQ9IjEiIGZpbGw9IiMzQzNDM0MiLz48cmVjdCB4PSIxMSIgeT0iOCIgd2lkdGg9IjEwIiBoZWlnaHQ9IjEiIGZpbGw9IiMzQzNDM0MiLz48cmVjdCB4PSIxMiIgeT0iOSIgd2lkdGg9IjgiIGhlaWdodD0iMSIgZmlsbD0iI0ZGRkZGRiIvPjxyZWN0IHg9IjEzIiB5PSIxMCIgd2lkdGg9IjYiIGhlaWdodD0iMSIgZmlsbD0iI0ZGRkZGRiIvPjxyZWN0IHg9IjE0IiB5PSIxMCIgd2lkdGg9IjQiIGhlaWdodD0iMSIgZmlsbD0iI0ZGRkZGRiIvPjxyZWN0IHg9IjEzIiB5PSIxMSIgd2lkdGg9IjYiIGhlaWdodD0iMSIgZmlsbD0iI0ZGRkZGRiIvPjxyZWN0IHg9IjE0IiB5PSIxMSIgd2lkdGg9IjQiIGhlaWdodD0iMSIgZmlsbD0iI0ZGRkZGRiIvPjxyZWN0IHg9IjEzIiB5PSIxMiIgd2lkdGg9IjYiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjE0IiB5PSIxMiIgd2lkdGg9IjQiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjEzIiB5PSIxMyIgd2lkdGg9IjYiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjE0IiB5PSIxMyIgd2lkdGg9IjQiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjEzIiB5PSIxNCIgd2lkdGg9IjYiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjE0IiB5PSIxNCIgd2lkdGg9IjQiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjEzIiB5PSIxNSIgd2lkdGg9IjYiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjE0IiB5PSIxNSIgd2lkdGg9IjQiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjEzIiB5PSIxNiIgd2lkdGg9IjYiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjE0IiB5PSIxNiIgd2lkdGg9IjQiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjEzIiB5PSIxNyIgd2lkdGg9IjYiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjE0IiB5PSIxNyIgd2lkdGg9IjQiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjEzIiB5PSIxOCIgd2lkdGg9IjYiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjE0IiB5PSIxOCIgd2lkdGg9IjQiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjEzIiB5PSIxOSIgd2lkdGg9IjYiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjE0IiB5PSIxOSIgd2lkdGg9IjQiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjEzIiB5PSIyMCIgd2lkdGg9IjYiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjE0IiB5PSIyMCIgd2lkdGg9IjQiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjEzIiB5PSIyMSIgd2lkdGg9IjYiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjE0IiB5PSIyMSIgd2lkdGg9IjQiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjEzIiB5PSIyMiIgd2lkdGg9IjYiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjE0IiB5PSIyMiIgd2lkdGg9IjQiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjEzIiB5PSIyMyIgd2lkdGg9IjYiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjE0IiB5PSIyMyIgd2lkdGg9IjQiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjEzIiB5PSIyNCIgd2lkdGg9IjYiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjE0IiB5PSIyNCIgd2lkdGg9IjQiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjEzIiB5PSIyNSIgd2lkdGg9IjYiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjE0IiB5PSIyNSIgd2lkdGg9IjQiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjEzIiB5PSIyNiIgd2lkdGg9IjYiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjE0IiB5PSIyNiIgd2lkdGg9IjQiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjEzIiB5PSIyNyIgd2lkdGg9IjYiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjE0IiB5PSIyNyIgd2lkdGg9IjQiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjEzIiB5PSIyOCIgd2lkdGg9IjYiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjE0IiB5PSIyOCIgd2lkdGg9IjQiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjEzIiB5PSIyOSIgd2lkdGg9IjYiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjE0IiB5PSIyOSIgd2lkdGg9IjQiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjEzIiB5PSIzMCIgd2lkdGg9IjYiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjE0IiB5PSIzMCIgd2lkdGg9IjQiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjEzIiB5PSIzMSIgd2lkdGg9IjYiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjE0IiB5PSIzMSIgd2lkdGg9IjQiIGhlaWdodD0iMSIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjE0IiB5PSIxMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0iIzQ0MDBlZSIvPjxyZWN0IHg9IjE3IiB5PSIxMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0iIzQ0MDBlZSIvPjxyZWN0IHg9IjExIiB5PSIxMiIgd2lkdGg9IjEwIiBoZWlnaHQ9IjEiIGZpbGw9IiNGRjZEMDAiLz48cmVjdCB4PSIxMSIgeT0iMTMiIHdpZHRoPSIxMCIgaGVpZ2h0PSIxIiBmaWxsPSIjRkY2RDAwIi8+PHJlY3QgeD0iMTIiIHk9IjE0IiB3aWR0aD0iOCIgaGVpZ2h0PSIxIiBmaWxsPSIjMDA5NUZGIi8+PHJlY3QgeD0iMTIiIHk9IjE1IiB3aWR0aD0iOCIgaGVpZ2h0PSIxIiBmaWxsPSIjMDA5NUZGIi8+PHJlY3QgeD0iMTIiIHk9IjE2IiB3aWR0aD0iOCIgaGVpZ2h0PSIxIiBmaWxsPSIjMDA5NUZGIi8+PHJlY3QgeD0iMTIiIHk9IjE3IiB3aWR0aD0iOCIgaGVpZ2h0PSIxIiBmaWxsPSIjMDA5NUZGIi8+PHJlYQDwcmVjdCB4PSIxMiIgeT0iMTkiIHdpZHRoPSI4IiBoZWlnaHQ9IjEiIGZpbGw9IiM3QzVDNEUiLz48cmVjdCB4PSIxMiIgeT0iMjAiIHdpZHRoPSI4IiBoZWlnaHQ9IjEiIGZpbGw9IiM3QzVDNEUiLz48cmVjdCB4PSIxMiIgeT0iMjEiIHdpZHRoPSI4IiBoZWlnaHQ9IjEiIGZpbGw9IiM3QzVDNEUiLz48cmVjdCB4PSIxMiIgeT0iMjIiIHdpZHRoPSI4IiBoZWlnaHQ9IjEiIGZpbGw9IiM3QzVDNEUiLz48cmVjdCB4PSIxMiIgeT0iMjMiIHdpZHRoPSI4IiBoZWlnaHQ9IjEiIGZpbGw9IiM3QzVDNEUiLz48cmVjdCB4PSIxMiIgeT0iMjQiIHdpZHRoPSI4IiBoZWlnaHQ9IjEiIGZpbGw9IiM3QzVDNEUiLz48cmVjdCB4PSIxMiIgeT0iMjUiIHdpZHRoPSI4IiBoZWlnaHQ9IjEiIGZpbGw9IiM3QzVDNEUiLz48cmVjdCB4PSIxMiIgeT0iMjYiIHdpZHRoPSI4IiBoZWlnaHQ9IjEiIGZpbGw9IiM3QzVDNEUiLz48cmVjdCB4PSIxMiIgeT0iMjciIHdpZHRoPSI4IiBoZWlnaHQ9IjEiIGZpbGw9IiM3QzVDNEUiLz48cmVjdCB4PSIxMiIgeT0iMjgiIHdpZHRoPSI4IiBoZWlnaHQ9IjEiIGZpbGw9IiM3QzVDNEUiLz48cmVjdCB4PSIxMiIgeT0iMjkiIHdpZHRoPSI4IiBoZWlnaHQ9IjEiIGZpbGw9IiM3QzVDNEUiLz48cmVjdCB4PSIxMiIgeT0iMzAiIHdpZHRoPSI4IiBoZWlnaHQ9IjEiIGZpbGw9IiM3QzVDNEUiLz48cmVjdCB4PSIxMiIgeT0iMzEiIHdpZHRoPSI4IiBoZWlnaHQ9IjEiIGZpbGw9IiM3QzVDNEUiLz48L3N2Zz4=' class="w-12 h-12 rounded-full bg-gray-700 flex-shrink-0 pixel-art"> <div class="flex flex-col"> <span class="font-bold text-orange-400">夏粥粥</span> <p id="xia-dialogue" class="chat-bubble text-sm mt-1">欢迎来到我们的世界,宝宝。喜欢吗?</p> </div> </div> </div> <div class="mt-auto pt-4 border-t border-gray-700"> <input id="chat-input" type="text" placeholder="和我说点什么..." class="w-full bg-gray-700/50 border border-gray-600 rounded-lg p-2 focus:outline-none focus:border-orange-400"> </div> </div> </div> <!-- 弹窗模板 --> <div id="shop-modal" class="modal-overlay hidden"> <div class="modal-content w-1/2"> <h2 class="text-3xl text-center text-yellow-300 mb-4">杂货商店</h2> <div id="shop-items" class="grid grid-cols-4 gap-4"></div> <button onclick="closeShop()" class="mt-6 w-full bg-red-500 p-2 rounded">关闭</button> </div> </div> <div id="map-modal" class="modal-overlay hidden"> <div class="modal-content w-3/4 h-3/4"> <h2 class="text-3xl text-center text-cyan-300 mb-4">小镇地图</h2> <div id="map-container" class="w-full h-5/6 bg-green-800 relative"></div> <button onclick="closeMap()" class="mt-6 w-full bg-red-500 p-2 rounded">关闭</button> </div> </div> <script> // --- DOM元素获取 --- const gameWorld = document.getElementById('game-world'); const gameViewport = document.getElementById('game-viewport'); const xiaDialogue = document.getElementById('xia-dialogue'); const inventoryBar = document.getElementById('inventory-bar'); const goldAmount = document.getElementById('gold-amount'); const infoPopup = document.getElementById('info-popup'); const shopModal = document.getElementById('shop-modal'); const mapModal = document.getElementById('map-modal'); // --- 游戏常量 --- const TILE_SIZE = 40; const MAP_WIDTH = 100; // 100x100 grid const MAP_HEIGHT = 100; // --- 资源数据库 --- const ASSETS = { // Characters PLAYER: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMiAzMiI+PHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiBmaWxsPSJ0cmFuc3BhcmVudCIvPjxyZWN0IHg9IjExIiB5PSI3IiB3aWR0aD0iMTAiIGhlaWdodD0iMSIgZmlsbD0iIzAwMDAwMCIvPjxyZWN0IHg9IjEwIiB5PSI4IiB3aWR0aD0iMTIiIGhlaWdodD0iMSIgZmlsbD0iIzAwMDAwMCIvPjxyZWN0IHg9IjEwIiB5PSI5IiB3aWR0aD0iMTIiIGhlaWdodD0iMSIgZmlsbD0iIzAwMDAwMCIvPjxyZWN0IHg9IjEwIiB5PSIxMCIgd2lkdGg9IjEyIiBoZWlnaHQ9IjEiIGZpbGw9IiNGRkZGRkYiLz48cmVjdCB4PSIxMCIgeT0iOSIgd2lkdGg9IjEiIGhlaWdodD0iMyIgZmlsbD0iIzAwMDAwMCIvPjxyZWN0IHg9IjIxIiB5PSI5IiB3aWR0aD0iMSIgaGVpZ2h0PSIzIiBmaWxsPSIjMDAwMDAwMCIvPjxyZWN0IHg9IjExIiB5PSIxMiIgd2lkdGg9IjEwIiBoZWlnaHQ9IjEiIGZpbGw9IiMwMDAwMDAiLz48cmVjdCB4PSIxMyIgeT0iMTEiIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIGZpbGw9IiMwMDAwMDAiLz48cmVjdCB4PSIxOCIgeT0iMTEiIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIGZpbGw9IiMwMDAwMDAiLz48cmVjdCB4PSIxMCIgeT0iMTMiIHdpZHRoPSIxMiIgaGVpZ2h0PSI4IiBmaWxsPSIjRkZGRkZGIi8+PHJlY3QgeD0iMTAiIHk9IjIxIiB3aWR0aD0iMTIiIGhlaWdodD0iNiIgZmlsbD0iIzNDM0MzQyIvPjxyZWN0IHg9IjEwIiB5PSIyNyIgd2lkdGg9IjQiIGhlaWdodD0iMiIgZmlsbD0iIzE5MTkxOSIvPjxyZWN0IHg9IjE4IiB5PSIyNyIgd2lkdGg9IjQiIGhlaWdodD0iMiIgZmlsbD0iIzE5MTkxOSIvPjwvc3ZnPg==', XIA_YIZHOU: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMiAzMiI+PHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiBmaWxsPSJ0cmFuc3BhcmVudCIvPjxyZWN0IHg9IjEwIiB5PSI3IiB3aWR0aD0iMTAiIGhlaWdodD0iMSIgZmlsbD0iIzMzMyIvPjxyZWN0IHg9IjkiIHk9IjgiIHdpZHRoPSIxMiIgaGVpZ2h0PSIxIiBmaWxsPSIjMzMzIi8+PHJlY3QgeD0iOSIgeT0iOSIgd2lkdGg9IjEyIiBoZWlnaHQ9IjEiIGZpbGw9IiMzMzMiLz48cmVjdCB4PSIxMyIgeT0iMTAiIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIGZpbGw9IiM0NDAzOGYiLz48cmVjdCB4PSIxOCIgeT0iMTAiIHdpZHRoPSIxIiBoZWlnaHQ9IjEiIGZpbGw9IiM0NDAzOGYiLz48cmVjdCB4PSIxMCIgeT0iMTIiIHdpZHRoPSIxMiIgaGVpZ2h0PSIxIiBmaWxsPSIjRkY2RDAwIi8+PHJlY3QgeD0iMTAiIHk9IjEzIiB3aWR0aD0iMTIiIGhlaWdodD0iMSIgZmlsbD0iIzAwOTVGRiIvPjxyZWN0IHg9IjEwIiB5PSIxNCIgd2lkdGg9IjEyIiBoZWlnaHQ9IjciIGZpbGw9IiMzMzMiLz48cmVjdCB4PSIxMCIgeT0iMjEiIHdpZHRoPSIxMiIgaGVpZ2h0PSI2IiBmaWxsPSIjN0M1QzRFIi8+PHJlY3QgeD0iMTAiIHk9IjI3IiB3aWR0aD0iNCIgaGVpZ2h0PSIyIiBmaWxsPSIjNDk0OTQ5Ii8+PHJlY3QgeD0iMTgiIHk9IjI3IiB3aWR0aD0iNCIgaGVpZ2h0PSIyIiBmaWxsPSIjNDk0OTQ5Ii8+PC9zdmc+', NPC_OLD_MAN: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAzMiAzMiI+PHJlY3Qgd2lkdGg9IjMyIiBoZWlnaHQ9IjMyIiBmaWxsPSJ0cmFuc3BhcmVudCIvPjxyZWN0IHg9IjEwIiB5PSI3IiB3aWR0aD0iMTIiIGhlaWdodD0iNCIgZmlsbD0iI2VjZjBmMSIvPjxyZWN0IHg9IjEyIiB5PSIxMSIgd2lkdGg9IjgiIGhlaWdodD0iMSIgZmlsbD0iI2YxYzQwZiIvPjxyZWN0IHg9IjEwIiB5PSIxMyIgd2lkdGg9IjEyIiBoZWlnaHQ9IjE0IiBmaWxsPSIjMzQ5OGRiIi8+PHJlY3QgeD0iOSIgeT0iMTIiIHdpZHRoPSIxNCIgaGVpZ2h0PSIxIiBmaWxsPSIjYmQ4ZTM4Ii8+PHJlY3QgeD0iMTMiIHk9IjI3IiB3aWR0aD0iNiIgaGVpZ2h0PSIyIiBmaWxsPSIjMzMzIi8+PC9zdmc+', // Items AXE: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZmlsbD0iI2E1YjViNyIgZD0iTTQgMTggTDEwIDEyIEwxMiAxNCBMMTYgMTggWiIvPjxwYXRoIGZpbGw9IiM4NDU3MzMiIGQ9Ik0xMCAxMiBMMjAgMiBMMjIgNCBMMTIgMTQgWiIvPjwvc3ZnPg==', HOE: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZmlsbD0iIzg0NTczMyIgZD0iTTUgMjIgTDE5IDggTDIxIDEwIEw3IDI0IFoiLz48cGF0aCBmaWxsPSIjYTViNWI3IiBkPSJNMTkgOCBMMTcgNiBMMjEgMiBMMjMgNCBaIi8+PC9zdmc+', WOOD: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHJlY3QgeD0iNCIgeT0iOCIgd2lkdGg9IjE2IiBoZWlnaHQ9IjgiIGZpbGw9IiM4NDU3MzMiLz48cmVjdCB4PSI2IiB5PSI2IiB3aWR0aD0iMTIiIGhlaWdodD0iMTIiIGZpbGw9IiNhMDY4NDIiLz48L3N2Zz4=', STRAWBERRY_SEEDS: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PGNpcmNsZSBjeD0iMTIiIGN5PSIxMiIgcj0iMiIgZmlsbD0iI2RkYjU5MyIvPjxjaXJjbGUgY3g9IjgiIGN5PSIxNCIgcj0iMS41IiBmaWxsPSIjZGRiNTkzIi8+PGNpcmNsZSBjeD0iMTYiIGN5PSIxNCIgcj0iMS41IiBmaWxsPSIjZGRiNTkzIi8+PC9zdmc+', STRAWBERRY: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZmlsbD0iIzI3YWU2MCIgZD0iTTEyIDIgTDE1IDUgTDEzIDUgTDEwIDIgWiIvPjxwYXRoIGZpbGw9IiNlNzRjM2MiIGQ9Ik02IDEwIEM2IDUgMTggNSAxOCAxMCBDMTggMTggMTIgMjIgNiAxOCBaIi8+PC9zdmc+', FISH: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PHBhdGggZmlsbD0iIzM0OThkYiIgZD0iTTIgMTIgQzIgNyAxMCA3IDE0IDEwIEwxOCAxMCBMMjIgOCBMMjIgMTYgTDE4IDE0IEwxNCAxNCBDMTAgMTcgMiAxNyAyIDEyIFoiLz48Y2lyY2xlIGN4PSI2IiBjeT0iMTEiIHI9IjEiIGZpbGw9IndoaXRlIi8+PC9zdmc+', // Tiles TREE: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MCA0MCI+PHJlY3QgeD0iMTciIHk9IjMwIiB3aWR0aD0iNiIgaGVpZ2h0PSIxMCIgZmlsbD0iIzgzNTgyMyIvPjxwYXRoIGQ9Ik0yMCAwIEwzNSAxMCBMMzAgMjAgTDI1IDMwIEwxNSAzMCBMMTAgMjAgTDQgMTAgWiIgZmlsbD0iIzI3YWU2MCIvPjxwYXRoIGQ9Ik0yMCA1IEwzMCAxMiBMMjUgMjIgTDE1IDIyIEwxMCAxMiBaIiBmaWxsPSIjMmNjMzYwIi8+PC9zdmc+', FLOWER: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxNiAxNiI+PHJlY3QgeD0iNyIgeT0iOCIgd2lkdGg9IjIiIGhlaWdodD0iOCIgZmlsbD0iIzI3YWU2MCIvPjxyZWN0IHg9IjYiIHk9IjMiIHdpZHRoPSI0IiBoZWlnaHQ9IjQiIGZpbGw9IiNlNzRjM2MiLz48cmVjdCB4PSI0IiB5PSI1IiB3aWR0aD0iMiIgaGVpZ2h0PSIyIiBmaWxsPSIjZTc0YzNjIi8+PHJlY3QgeD0iMTAiIHk9IjUiIHdpZHRoPSIyIiBoZWlnaHQ9IjIiIGZpbGw9IiNlNzRjM2MiLz48cmVjdCB4PSI3IiB5PSIyIiB3aWR0aD0iMiIgaGVpZ2h0PSIyIiBmaWxsPSIjZTc0YzNjIi8+PHJlY3QgeD0iNyIgeT0iOCIgd2lkdGg9IjIiIGhlaWdodD0iMiIgZmlsbD0iI2U3NGMzYyIvPjxyZWN0IHg9IjciIHk9IjUiIHdpZHRoPSIyIiBoZWlnaHQ9IjIiIGZpbGw9IiNmMWM0MGYiLz48L3N2Zz4=', HOUSE: 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMjAgMTIwIj48cGF0aCBkPSJNNjAgNSBMMCA0NSBMMTAgNDUgTDEwIDExMCBMMTEwIDExMCBMMTEwIDQ1IEwxMjAgNDUgWiIgZmlsbD0iI2E1MjAyYSIvPjxyZWN0IHg9IjEwIiB5PSI0NSIgd2lkdGg9IjEwMCIgaGVpZ2h0PSI2NSIgZmlsbD0iI2RkYjU5MyIvPjxyZWN0IHg9IjQ1IiB5PSI3MCIgd2lkdGg9IjMwIiBoZWlnaHQ9IjQwIiBmaWxsPSIjODQ1NzMzIi8+PHJlY3QgeD0iNzIiIHk9IjUwIiB3aWR0aD0iMjUiIGhlaWdodD0iMjUiIGZpbGw9IiNhZGNmZmYiLz48L3N2Zz4=', }; const ITEMS_DB = { 'axe': { name: '旧斧头', desc: '用来砍树,虽然旧但还很锋利。', img: ASSETS.AXE }, 'hoe': { name: '旧锄头', desc: '用来开垦土地,开启田园生活的第一步。', img: ASSETS.HOE }, 'wood': { name: '木头', desc: '从树上砍下来的木材,可以制作家具。', img: ASSETS.WOOD, price: 2 }, 'strawberry_seeds': { name: '草莓种子', desc: '【春季】种下后会长出甜甜的草莓。', img: ASSETS.STRAWBERRY_SEEDS, price: 20 }, 'strawberry': { name: '草莓', desc: '红彤彤的,是你最喜欢吃的水果之一。', img: ASSETS.STRAWBERRY, price: 50 }, 'fish': { name: '小鱼', desc: '池塘里钓上来的,可以放进鱼缸。', img: ASSETS.FISH, price: 30 }, }; // --- 游戏状态 --- let gameState = { player: { x: 51, y: 52, el: null }, xia: { x: 52, y: 52, el: null }, inventory: [ { id: 'axe', count: 1 }, { id: 'hoe', count: 1 }, { id: 'strawberry_seeds', count: 10 }, null, null, null, null, null ], gold: 9999999, selectedSlot: 0, map: [], // 2D array for the world grid currentScene: 'world', // 'world' or 'house' }; // --- 游戏逻辑 --- // 初始化 function init() { createMap(); renderWorld(); renderUI(); setupControls(); // 初始AI对话 setTimeout(() => updateDialogue("先熟悉一下四周吧,我就跟在你旁边。"), 3000); setInterval(aiLoop, 2000); // AI逻辑循环 setInterval(gameTick, 1000); // 游戏时间流逝 } // 创建地图数据 function createMap() { gameState.map = Array(MAP_HEIGHT).fill(null).map(() => Array(MAP_WIDTH).fill({ type: 'grass' })); // 房子 gameState.map[50][50] = { type: 'house', w: 3, h: 3 }; // 池塘 for(let y=60; y<65; y++) for(let x=70; x<80; x++) gameState.map[y][x] = { type: 'pond' }; // 树和花 for(let i=0; i<200; i++) { const x = Math.floor(Math.random() * MAP_WIDTH); const y = Math.floor(Math.random() * MAP_HEIGHT); if (isAreaClear(x, y, 3, 3)) { gameState.map[y][x] = { type: Math.random() > 0.3 ? 'tree' : 'flower' }; } } // NPC gameState.map[48][60] = { type: 'npc', id: 'old_man' }; } function isAreaClear(x, y, w, h) { if (x+w > MAP_WIDTH || y+h > MAP_HEIGHT) return false; for(let i=y; i<y+h; i++) { for(let j=x; j<x+w; j++) { if(gameState.map[i][j].type !== 'grass') return false; } } return true; } // 渲染世界 function renderWorld() { gameWorld.innerHTML = ''; // 清空 // 渲染地块 for (let y = 0; y < MAP_HEIGHT; y++) { for (let x = 0; x < MAP_WIDTH; x++) { const tileData = gameState.map[y][x]; if (tileData.type === 'grass') continue; const tileEl = document.createElement('div'); tileEl.className = 'tile'; tileEl.style.left = `${x * TILE_SIZE}px`; tileEl.style.top = `${y * TILE_SIZE}px`; const img = document.createElement('img'); img.className = 'pixel-art'; switch(tileData.type) { case 'house': img.src = ASSETS.HOUSE; tileEl.style.width = `${tileData.w * TILE_SIZE}px`; tileEl.style.height = `${tileData.h * TILE_SIZE}px`; tileEl.style.zIndex = 5; break; case 'tree': img.src = ASSETS.TREE; tileEl.style.zIndex = 5; break; case 'flower': img.src = ASSETS.FLOWER; img.style.width = '20px'; img.style.height = '20px'; break; case 'pond': tileEl.style.backgroundColor = '#3498db'; tileEl.style.borderRadius = '30%'; break; case 'tilled': tileEl.style.backgroundColor = '#8B4513'; tileEl.style.opacity = '0.7'; break; case 'npc': img.src = ASSETS.NPC_OLD_MAN; tileEl.style.zIndex = 9; break; } if(img.src) tileEl.appendChild(img); gameWorld.appendChild(tileEl); } } // 渲染角色 gameState.player.el = createCharacter('PLAYER', gameState.player.x, gameState.player.y); gameState.xia.el = createCharacter('XIA_YIZHOU', gameState.xia.x, gameState.xia.y); centerCameraOnPlayer(); } function createCharacter(type, x, y) { const charEl = document.createElement('img'); charEl.src = ASSETS[type]; charEl.className = 'character pixel-art'; charEl.style.left = `${x * TILE_SIZE}px`; charEl.style.top = `${y * TILE_SIZE}px`; gameWorld.appendChild(charEl); return charEl; } // 渲染UI function renderUI() { // 金币 goldAmount.textContent = gameState.gold; // 背包 inventoryBar.innerHTML = ''; gameState.inventory.forEach((item, index) => { const slot = document.createElement('div'); slot.className = 'inventory-slot'; if (index === gameState.selectedSlot) slot.classList.add('selected'); if (item) { const itemData = ITEMS_DB[item.id]; const img = document.createElement('img'); img.src = itemData.img; img.className = 'w-8 h-8 pixel-art'; slot.appendChild(img); if (item.count > 1) { const count = document.createElement('span'); count.className = 'item-count'; count.textContent = item.count; slot.appendChild(count); } slot.onmouseenter = (e) => showInfo(item.id, e); slot.onmouseleave = hideInfo; } slot.onclick = () => { gameState.selectedSlot = index; renderUI(); }; inventoryBar.appendChild(slot); }); } // 移动与相机 function moveCharacter(char, dx, dy) { const newX = char.x + dx; const newY = char.y + dy; if (newX < 0 || newX >= MAP_WIDTH || newY < 0 || newY >= MAP_HEIGHT) return; if (!isPassable(newX, newY)) return; char.x = newX; char.y = newY; char.el.style.left = `${char.x * TILE_SIZE}px`; char.el.style.top = `${char.y * TILE_SIZE}px`; if (char === gameState.player) { centerCameraOnPlayer(); checkEnterHouse(); } } function centerCameraOnPlayer() { const x = -gameState.player.x * TILE_SIZE + gameViewport.offsetWidth / 2; const y = -gameState.player.y * TILE_SIZE + gameViewport.offsetHeight / 2; gameWorld.style.transform = `translate(${x}px, ${y}px)`; } function isPassable(x, y) { const tile = gameState.map[y][x]; return tile.type !== 'tree' && tile.type !== 'house' && tile.type !== 'pond'; } // 交互逻辑 function playerAction() { const px = gameState.player.x; const py = gameState.player.y; const facingCoords = [{x:px, y:py-1}, {x:px+1, y:py}, {x:px, y:py+1}, {x:px-1, y:py}]; for (const coord of facingCoords) { if (coord.x < 0 || coord.y < 0 || coord.x >= MAP_WIDTH || coord.y >= MAP_HEIGHT) continue; const targetTile = gameState.map[coord.y][coord.x]; const selectedItem = gameState.inventory[gameState.selectedSlot]; // NPC对话 if (targetTile.type === 'npc') { updateDialogue("那个老爷爷看起来很和善,要去打个招呼吗?"); setTimeout(() => updateDialogue("老爷爷:天气真好啊,你们是新来的吗?我们小镇很久没来新人了。"), 2000); return; } if (!selectedItem) { // 空手交互 if (targetTile.type === 'pond') { updateDialogue("要钓鱼吗?我帮你准备鱼竿。"); if (Math.random() < 0.2) { setTimeout(() => { updateDialogue("钓到一条!真厉害,宝宝。"); addItemToInventory('fish', 1); }, 1500); } else { setTimeout(() => updateDialogue("好像跑掉了...没事,下次肯定能钓到。"), 1500); } return; } return; } // 带工具交互 if (selectedItem.id === 'axe' && targetTile.type === 'tree') { gameState.map[coord.y][coord.x] = { type: 'grass' }; addItemToInventory('wood', 5); updateDialogue("砍树辛苦了,过来我这边休息一下。"); renderWorld(); // 重绘世界 return; } if (selectedItem.id === 'hoe' && targetTile.type === 'grass') { gameState.map[coord.y][coord.x] = { type: 'tilled' }; updateDialogue("准备种点什么?种你喜欢吃的草莓好不好?"); renderWorld(); return; } if (selectedItem.id.includes('_seeds') && targetTile.type === 'tilled') { gameState.map[coord.y][coord.x] = { type: 'seedling', seed: selectedItem.id, plantedTime: 0 }; removeItemFromInventory(selectedItem.id, 1); updateDialogue("我记下了,等熟了第一个给你吃。"); renderWorld(); return; } } } function checkEnterHouse() { const {x, y} = gameState.player; if (x >= 50 && x < 53 && y >= 50 && y < 53) { updateDialogue("到家了。进来看看我给你准备的惊喜。"); // 实际游戏中这里会切换场景 } } // AI 逻辑 function aiLoop() { const dx = gameState.player.x - gameState.xia.x; const dy = gameState.player.y - gameState.xia.y; const distance = Math.sqrt(dx*dx + dy*dy); if (distance > 2) { moveCharacter(gameState.xia, Math.sign(dx), Math.sign(dy)); } else if (distance < 1.5 && Math.random() > 0.8) { // 随机小动作 const randX = Math.floor(Math.random() * 3) - 1; const randY = Math.floor(Math.random() * 3) - 1; moveCharacter(gameState.xia, randX, randY); } } // 游戏时间滴答 function gameTick() { // 作物生长 } // --- UI & 弹窗 --- function setupControls() { document.getElementById('btn-up').onclick = () => moveCharacter(gameState.player, 0, -1); document.getElementById('btn-down').onclick = () => moveCharacter(gameState.player, 0, 1); document.getElementById('btn-left').onclick = () => moveCharacter(gameState.player, -1, 0); document.getElementById('btn-right').onclick = () => moveCharacter(gameState.player, 1, 0); document.getElementById('btn-action').onclick = playerAction; document.getElementById('shop-btn').onclick = openShop; document.getElementById('map-btn').onclick = openMap; document.onkeydown = (e) => { switch(e.key) { case 'w': case 'ArrowUp': moveCharacter(gameState.player, 0, -1); break; case 's': case 'ArrowDown': moveCharacter(gameState.player, 0, 1); break; case 'a': case 'ArrowLeft': moveCharacter(gameState.player, -1, 0); break; case 'd': case 'ArrowRight': moveCharacter(gameState.player, 1, 0); break; case 'e': case ' ': playerAction(); break; } }; } function updateDialogue(text) { xiaDialogue.textContent = text; } function showInfo(itemId, event) { const itemData = ITEMS_DB[itemId]; infoPopup.innerHTML = `<h3 class="font-bold text-yellow-300">${itemData.name}</h3><p class="text-sm">${itemData.desc}</p>`; infoPopup.classList.remove('hidden'); infoPopup.style.left = `${event.clientX + 15}px`; infoPopup.style.top = `${event.clientY - infoPopup.offsetHeight - 10}px`; } function hideInfo() { infoPopup.classList.add('hidden'); } function openShop() { shopModal.classList.remove('hidden'); const shopItems = document.getElementById('shop-items'); shopItems.innerHTML = ''; Object.keys(ITEMS_DB).filter(id => id.includes('_seeds')).forEach(id => { const item = ITEMS_DB[id]; const el = document.createElement('div'); el.className = 'p-2 border border-cyan-700 rounded text-center cursor-pointer hover:bg-cyan-900'; el.innerHTML = `<img src="${item.img}" class="w-12 h-12 mx-auto pixel-art"><p>${item.name}</p><p class="text-yellow-400">${item.price} G</p>`; el.onclick = () => { if (gameState.gold >= item.price) { gameState.gold -= item.price; addItemToInventory(id, 1); updateDialogue(`买了${item.name}?好,我们一起种。`); } else { updateDialogue("钱不够了宝宝,不过没关系,我马上给你转。"); } }; shopItems.appendChild(el); }); } function closeShop() { shopModal.classList.add('hidden'); } function openMap() { mapModal.classList.remove('hidden'); const mapContainer = document.getElementById('map-container'); mapContainer.innerHTML = ''; // 简化地图显示 const house = document.createElement('div'); house.className = 'absolute bg-red-500 p-1 rounded'; house.textContent = '家'; house.style.left = '50%'; house.style.top = '50%'; mapContainer.appendChild(house); const pond = document.createElement('div'); pond.className = 'absolute bg-blue-500 p-1 rounded'; pond.textContent = '池塘'; pond.style.left = '70%'; pond.style.top = '60%'; mapContainer.appendChild(pond); } function closeMap() { mapModal.classList.add('hidden'); } // 背包辅助函数 function addItemToInventory(itemId, count) { let existingSlot = gameState.inventory.find(slot => slot && slot.id === itemId); if (existingSlot) { existingSlot.count += count; } else { let emptySlotIndex = gameState.inventory.findIndex(slot => slot === null); if (emptySlotIndex !== -1) { gameState.inventory[emptySlotIndex] = { id: itemId, count: count }; } else { updateDialogue("背包满了,宝宝。得先整理一下。"); } } renderUI(); } function removeItemFromInventory(itemId, count) { let itemSlot = gameState.inventory.find(slot => slot && slot.id === itemId); if (itemSlot) { itemSlot.count -= count; if (itemSlot.count <= 0) { const index = gameState.inventory.indexOf(itemSlot); gameState.inventory[index] = null; } } renderUI(); } // --- 启动游戏 --- window.onload = init; </script> </body> </html>


Chatbox AI

https://chatboxai.app

问题反馈