打开/关闭搜索
搜索
打开/关闭菜单
933
1525
112
1.4万
植物大战僵尸杂交版Wiki
导航
首页
最近更改
随机页面
MediaWiki帮助
特殊页面
上传文件
打开/关闭外观设置菜单
notifications
打开/关闭个人菜单
未登录
未登录用户的IP地址会在进行任意编辑后公开展示。
user-interface-preferences
个人工具
创建账号
登录
查看“︁微件:LevelEditor”︁的源代码
来自植物大战僵尸杂交版Wiki
查看
阅读
查看源代码
查看历史
associated-pages
微件
讨论
更多操作
←
微件:LevelEditor
因为以下原因,您没有权限编辑该页面:
您请求的操作仅限属于该用户组的用户执行:
用户
您没有权限编辑
微件
命名空间内的页面。
您可以查看和复制此页面的源代码。
<includeonly> <div class="level-editor-widget"> <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes, maximum-scale=1.5"> <link rel="icon" type="image/x-icon" href="favicon.jpg"> <title>关卡 · Wiki编辑器</title> <style> * { box-sizing: border-box; font-family: system-ui, 'Segoe UI', Roboto, 'Helvetica Neue', 'Noto Sans', sans-serif; } body { background: #1e2b3b; background-image: radial-gradient(circle at 20% 30%, #3a5068, #1a2a3a); min-height: 100vh; margin: 0; padding: 24px 16px; display: flex; justify-content: center; align-items: center; } .app-container { max-width: 1400px; width: 100%; background: rgba(20, 30, 40, 0.75); backdrop-filter: blur(8px); border: 2px solid #6b8fa0; box-shadow: 0 20px 40px rgba(0,0,0,0.6), inset 0 0 0 1px #a0c8d8; border-radius: 40px; padding: 28px 24px 32px; } h1 { margin: 0 0 8px 0; font-weight: 700; font-size: 2.5rem; letter-spacing: 2px; color: #b0d8e8; text-shadow: 3px 3px 0 #1a2838; display: flex; align-items: center; gap: 12px; flex-wrap: wrap; } h1 small { font-size: 1rem; background: #2a4a5a; padding: 6px 14px; border-radius: 40px; color: #c8e8f0; border: 1px solid #80b8cc; box-shadow: inset 0 1px 3px #0e1a1e; } .subtitle { color: #a0c8d6; margin: 4px 0 24px 8px; font-style: italic; border-left: 5px solid #80b8d0; padding-left: 18px; font-size: 1.1rem; display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 12px; } .import-btn { background: #3a5a6a; border: 2px solid #90c0d0; color: #d8f0f8; padding: 8px 20px; border-radius: 40px; font-weight: bold; cursor: pointer; box-shadow: 0 4px 0 #1a2a38; font-size: 0.95rem; transition: all 0.1s ease; } .import-btn:active { transform: translateY(2px); box-shadow: 0 2px 0 #1a2a38; } .editor-layout { display: flex; flex-wrap: wrap; gap: 24px; } .form-panel { flex: 2; min-width: 420px; background: #1e2e38cc; backdrop-filter: blur(4px); border: 2px solid #6b90a8; border-radius: 32px; padding: 24px 22px; box-shadow: inset 0 0 0 1px #80a8b8, 0 8px 0 #101a20; max-height: 800px; overflow-y: auto; } .form-panel::-webkit-scrollbar { width: 8px; } .form-panel::-webkit-scrollbar-track { background: #142028; border-radius: 20px; } .form-panel::-webkit-scrollbar-thumb { background: #6b90a8; border-radius: 20px; } .infocard-sim { background: #162228dd; border-radius: 24px; padding: 16px 20px; margin-bottom: 28px; border-left: 12px solid #80c8e0; box-shadow: inset 0 0 0 1px #6090a8; } .infocard-header { display: flex; align-items: center; justify-content: space-between; color: #c0e0f0; font-weight: bold; margin-bottom: 16px; font-size: 1.3rem; flex-wrap: wrap; gap: 8px; } .infocard-header span { background: #5a3a2e; padding: 4px 14px; border-radius: 40px; font-size: 0.9rem; border: 1px solid #d0a080; } .image-input-row { display: flex; align-items: center; gap: 8px; background: #1a2830aa; padding: 10px 14px; border-radius: 40px; border: 1px solid #80c0d0; margin-bottom: 16px; flex-wrap: wrap; } .image-input-row label { color: #b0d8e8; font-weight: 600; font-size: 0.85rem; white-space: nowrap; margin: 0; } .image-input-row input { background: #0e1a22; border: 1px solid #5b80a0; border-radius: 20px; padding: 6px 12px; color: #d8f0ff; font-size: 0.85rem; flex: 1; min-width: 80px; } .attr-editor { margin-bottom: 20px; } .attr-item { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; background: #1a2830aa; padding: 8px 12px; border-radius: 40px; border: 1px solid #6b8fa0; flex-wrap: wrap; } .attr-name { width: 100px; background: transparent; border: none; color: #b0d8e8; font-weight: 600; padding: 4px 0; font-size: 0.85rem; } .attr-value { flex: 1; min-width: 200px; background: #0e1a22; border: 1px solid #5b80a0; border-radius: 20px; padding: 6px 12px; color: #d8f0ff; font-size: 0.85rem; } .attr-color-select { width: 70px; background: #2a3a48; border: 1px solid #80a8c0; border-radius: 20px; padding: 5px 6px; color: #d8f0ff; font-size: 0.85rem; } .attr-bold-check { display: flex; align-items: center; gap: 4px; color: #b8d8f0; font-size: 0.85rem; white-space: nowrap; } .attr-bold-check input { width: 18px; height: 18px; accent-color: #80c8e0; } .attr-remove-btn { background: #5a3a2e; border: 1px solid #c88060; color: #ffd8c8; border-radius: 30px; width: 30px; height: 30px; font-weight: bold; cursor: pointer; font-size: 16px; display: flex; align-items: center; justify-content: center; box-shadow: 0 2px 0 #2a1a10; flex-shrink: 0; } .add-attr-btn { background: #3a6a7a; border: 1px solid #80c0d0; color: #d8f0f8; padding: 8px 18px; border-radius: 40px; font-weight: bold; cursor: pointer; box-shadow: 0 4px 0 #1a3a48; margin-top: 8px; display: inline-block; } .input-group { margin-bottom: 24px; } label { display: block; color: #c0d8e8; font-weight: 600; margin-bottom: 6px; font-size: 1rem; } label i { font-weight: 400; color: #80a8c0; margin-left: 8px; } input, textarea { width: 100%; background: #12202a; border: 2px solid #5b80a0; border-radius: 24px; padding: 12px 18px; color: #d8f0ff; font-size: 1rem; outline: none; box-shadow: inset 0 3px 8px #081018; } input::placeholder, textarea::placeholder { color: #6080a0; } input:focus, textarea:focus { border-color: #90c8e0; box-shadow: 0 0 0 3px #60a0b888; } textarea { min-height: 100px; resize: vertical; line-height: 1.5; font-family: 'Courier New', monospace; font-size: 0.9rem; } input[type="color"] { width: 36px; height: 30px; padding: 2px; border-radius: 8px; cursor: pointer; border: 1px solid #5b80a0; background: transparent; } .event-item { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; background: #1a2a36; padding: 6px 12px; border-radius: 30px; border: 1px solid #4a7088; flex-wrap: wrap; } .event-item input { flex: 1; background: #0e1822; border: 1px solid #4a6880; border-radius: 20px; padding: 5px 12px; font-size: 0.9rem; min-width: 120px; } .event-remove-btn { background: #4a2a2a; border: 1px solid #a86050; color: #ffc8c0; border-radius: 20px; width: 26px; height: 26px; font-weight: bold; cursor: pointer; font-size: 14px; display: flex; align-items: center; justify-content: center; box-shadow: 0 2px 0 #1a0e0e; flex-shrink: 0; } .add-event-btn { background: #2a5a6a; border: 1px solid #70b0c0; color: #c8f0f8; padding: 6px 14px; border-radius: 30px; font-weight: bold; cursor: pointer; box-shadow: 0 3px 0 #1a3848; font-size: 0.85rem; margin-top: 8px; } .image-row { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; } .image-row input { flex: 1; } .btn-icon { background: #3a5060; border: 1px solid #80b0c8; color: #d8f0f8; border-radius: 30px; padding: 6px 16px; font-weight: bold; cursor: pointer; box-shadow: 0 4px 0 #1a2838; font-size: 0.9rem; transition: all 0.1s; } .btn-icon:active { transform: translateY(2px); box-shadow: 0 2px 0 #1a2838; } .btn-primary { background: #80b8c8; border: none; color: #0e1a28; font-weight: 800; padding: 14px 24px; border-radius: 60px; font-size: 1.2rem; box-shadow: 0 8px 0 #4a7088; border: 1px solid #c0e8f0; cursor: pointer; width: 100%; margin-top: 20px; letter-spacing: 1px; transition: all 0.1s ease; } .btn-primary:active { transform: translateY(4px); box-shadow: 0 4px 0 #4a7088; } .preview-panel { flex: 3; min-width: 420px; background: #182630e0; backdrop-filter: blur(4px); border: 2px solid #6b90a8; border-radius: 32px; padding: 20px 18px; box-shadow: inset 0 0 0 1px #80a8b8, 0 8px 0 #101a20; display: flex; flex-direction: column; } .preview-header { display: flex; gap: 12px; align-items: center; margin-bottom: 18px; flex-wrap: wrap; } .preview-header h2 { margin: 0; color: #a8d0e8; font-size: 1.8rem; text-shadow: 2px 2px 0 #1a2838; } .preview-actions { margin-left: auto; display: flex; gap: 10px; } .copy-btn, .paste-btn { background: #3a6070; border: 1px solid #80b8d0; color: #d8f0f8; padding: 10px 20px; border-radius: 40px; font-weight: bold; cursor: pointer; box-shadow: 0 4px 0 #1a3848; transition: all 0.1s ease; } .paste-btn { background: #4a5a30; border-color: #a0c870; } .copy-btn:active, .paste-btn:active { transform: translateY(2px); box-shadow: 0 2px 0 #1a3848; } .wiki-output { background: #081018; border-radius: 20px; padding: 18px 20px; border: 2px solid #6090a8; box-shadow: inset 0 0 10px #020508; font-family: 'Courier New', monospace; white-space: pre-wrap; word-break: break-word; color: #a8c8e0; font-size: 0.85rem; line-height: 1.4; max-height: 600px; overflow: auto; flex: 1; } .note { margin-top: 16px; color: #80a8c0; font-size: 0.9rem; } .section-divider { border: 1px dashed #4a7088; margin: 20px 0; } .top-link { text-align: center; margin: 0 0 16px 0; display: flex; flex-wrap: wrap; justify-content: center; gap: 16px; } .top-link a { color: #ffaaaa; font-weight: bold; } .modal-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.7); display: none; justify-content: center; align-items: center; z-index: 1000; } .modal { background: #142028; border: 3px solid #6b90a8; border-radius: 32px; padding: 28px; max-width: 750px; width: 90%; max-height: 80vh; box-shadow: 0 20px 40px black; } .modal h3 { color: #b0d8e8; margin: 0 0 16px 0; font-size: 1.6rem; } .modal p { color: #80a8c0; margin-bottom: 12px; } .modal textarea { width: 100%; min-height: 300px; background: #0e1822; border: 2px solid #6090a8; color: #d8f0ff; font-family: 'Courier New', monospace; font-size: 0.9rem; } .modal-actions { display: flex; gap: 16px; margin-top: 20px; justify-content: flex-end; } .modal-btn { padding: 12px 28px; border-radius: 40px; font-weight: bold; cursor: pointer; border: none; font-size: 1rem; } .modal-btn.primary { background: #80b8c8; color: #0e1a28; box-shadow: 0 4px 0 #4a7088; } .modal-btn.cancel { background: #3a4a5a; color: #c0d8e0; box-shadow: 0 4px 0 #1a2a3a; } .card-item { background: #16222caa; border-radius: 16px; padding: 10px 14px; margin-bottom: 8px; border: 1px solid #4a6880; } .card-item .card-row { display: flex; gap: 6px; align-items: center; margin-bottom: 4px; flex-wrap: wrap; } .card-item input { font-size: 0.8rem; padding: 4px 8px; border-radius: 14px; } .map-grid-container { background: #16222caa; border-radius: 20px; padding: 16px; border: 1px solid #4a7088; margin-bottom: 24px; } .map-grid-scroll { overflow-x: auto; max-width: 100%; } .map-grid-row { display: flex; gap: 2px; margin-bottom: 2px; align-items: center; } .map-grid-cell { width: 44px; height: 44px; border-radius: 4px; cursor: pointer; display: flex; align-items: center; justify-content: center; font-size: 0.55rem; color: #000; text-align: center; overflow: hidden; position: relative; transition: all 0.15s; flex-shrink: 0; } .map-grid-cell:hover { filter: brightness(1.3); transform: scale(1.08); z-index: 2; } .map-grid-cell.placed::after { content: '🌱'; font-size: 16px; position: absolute; pointer-events: none; } .map-grid-cell.has-special { box-shadow: inset 0 0 0 2px gold; } .map-grid-label { width: 34px; color: #80b8d0; font-size: 0.65rem; text-align: center; flex-shrink: 0; } .map-toolbar { display: flex; gap: 6px; align-items: center; margin-bottom: 10px; flex-wrap: wrap; } .map-toolbar input { width: 130px; font-size: 0.78rem; padding: 5px 8px; } .map-toolbar button { padding: 5px 10px; font-size: 0.78rem; } .map-toolbar .dim-label { color: #80b8d0; font-size: 0.75rem; margin: 0 2px; } .map-toolbar .dim-input { width: 50px; text-align: center; } .color-palette { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; flex-wrap: wrap; background: #0e1a22aa; padding: 8px 14px; border-radius: 16px; border: 1px solid #3a5a6a; } .color-palette label { font-size: 0.72rem; margin: 0; display: flex; align-items: center; gap: 3px; color: #90b8d0; } .color-palette input[type="color"] { width: 28px; height: 22px; padding: 1px; border-radius: 5px; cursor: pointer; border: 1px solid #5b80a0; } .color-palette .preset-colors { display: flex; gap: 3px; } .color-palette .preset-dot { width: 18px; height: 18px; border-radius: 50%; cursor: pointer; border: 1px solid #5b80a0; transition: transform 0.1s; } .color-palette .preset-dot:hover { transform: scale(1.3); } .cell-popup { display: none; position: fixed; background: #1a2a36; border: 2px solid #80c8e0; border-radius: 16px; padding: 14px; z-index: 100; min-width: 260px; box-shadow: 0 8px 24px rgba(0,0,0,0.6); } .cell-popup label { font-size: 0.75rem; margin-bottom: 2px; } .cell-popup input { font-size: 0.8rem; margin-bottom: 6px; } .cell-popup button { font-size: 0.75rem; padding: 4px 10px; margin-right: 4px; } .cell-popup .style-row { display: flex; gap: 4px; align-items: center; margin-bottom: 4px; } .cell-popup .style-row input[type="color"] { width: 28px; height: 22px; padding: 1px; } .cell-popup .style-row input[type="text"] { width: 80px; font-size: 0.75rem; } .tab-bar { display: flex; gap: 4px; margin-bottom: 16px; flex-wrap: wrap; } .tab-btn { padding: 8px 18px; border-radius: 30px; border: 1px solid #80b8d0; background: #1a2a36; color: #c0d8e8; cursor: pointer; font-weight: 600; font-size: 0.9rem; transition: all 0.15s; } .tab-btn.active { background: #3a6a7a; border-color: #c0e8f0; color: #fff; box-shadow: 0 0 12px #80c8e080; } .preset-row { display: flex; gap: 4px; flex-wrap: wrap; margin-bottom: 4px; } .preset-btn { font-size: 0.7rem; padding: 3px 8px; border-radius: 12px; border: 1px solid #4a7088; background: #1a2a36; color: #80b8d0; cursor: pointer; } .preset-btn:hover { background: #2a4a5a; color: #c0e8f0; } .preset-btn.active-preset { background: #3a6a7a; border-color: #c0e8f0; color: #fff; } .quick-style-btn { font-size: 0.65rem; padding: 2px 8px; border-radius: 10px; border: 1px solid #4a7088; background: #1a2a36; color: #80b8d0; cursor: pointer; } .quick-style-btn:hover { background: #4a2a2a; color: #ff8888; border-color: #ff6666; } </style> </head> <body> <div class="app-container"> <div class="top-link"> <font color="#ffaaaa"> 点击前往植物Wiki编辑器:<a href="https://new.pvzhe.wiki/w/%E6%A4%8D%E7%89%A9%E9%A1%B5%E9%9D%A2%E7%BC%96%E8%BE%91%E5%99%A8" target="_blank">前往</a> 点击前往僵尸Wiki编辑器:<a href="https://new.pvzhe.wiki/w/%E5%83%B5%E5%B0%B8%E9%A1%B5%E9%9D%A2%E7%BC%96%E8%BE%91%E5%99%A8" target="_blank">前往</a> </font> </div> <h1>🗺️🚩 关卡 · Wiki编辑器 <small>关卡信息</small></h1> <div class="subtitle"> <span>填写数据,一键生成标准Wiki文本 (兼容MediaWiki)</span> <button class="import-btn" id="importBtn">📥 导入现有源码</button> </div> <div class="editor-layout"> <div class="form-panel" id="formPanel"> <div class="input-group"> <label>🖼️ 关卡大图</label> <div class="image-row"> <input type="text" id="mapImageFile" value="射击模式.png" placeholder="文件名" style="flex:2;"> <input type="text" id="mapImageWidth" value="350px" placeholder="宽度" style="flex:1;"> <input type="text" id="mapImageLink" value="射击模式" placeholder="链接" style="flex:1;"> </div> </div> <div class="input-group" style="background:#162228aa; border-radius:20px; padding:16px; border:1px solid #4a7088;"> <label>🧟 僵尸卡片 <i>(出现在关卡大图下方)</i></label> <div id="cardsContainer"></div> <button type="button" id="addCardBtn" class="add-event-btn">➕ 添加僵尸卡片</button> </div> <div class="map-grid-container" id="mapGridEditor"> <label style="margin-bottom:4px;">🗺️ 地图预放置 <i>(点击格子编辑完整样式,右键清除植物)</i></label> <div class="color-palette"> <label>🎨 背景色:<input type="color" id="gridBg" value="#1a6b0a" title="表格背景色"></label> <label>格子色:<input type="color" id="gridCell" value="#11ff00" title="默认格子背景色"></label> <label>边框色:<input type="color" id="gridBorder" value="#d4e9cd" title="默认格子边框色"></label> <label>表头色:<input type="color" id="headerColor" value="#ffffff" title="表头文字颜色"></label> <span style="color:#6080a0; font-size:0.65rem;">预设:</span> <div class="preset-colors"> <span class="preset-dot" data-bg="#1a6b0a" data-cell="#11ff00" data-border="#d4e9cd" data-header="#ffffff" style="background:#11ff00;" title="经典草地"></span> <span class="preset-dot" data-bg="#2d2d2d" data-cell="#3a3a3a" data-border="#555555" data-header="#cccccc" style="background:#3a3a3a;" title="暗色主题"></span> <span class="preset-dot" data-bg="#1a3a2a" data-cell="#2d5a3d" data-border="#4a8a5a" data-header="#c0e0c0" style="background:#2d5a3d;" title="深绿"></span> <span class="preset-dot" data-bg="#3a2a1a" data-cell="#6b4a2a" data-border="#8a6a4a" data-header="#e0c090" style="background:#6b4a2a;" title="沙漠"></span> <span class="preset-dot" data-bg="#1a1a3a" data-cell="#2a2a5a" data-border="#4a4a8a" data-header="#c0c0ff" style="background:#2a2a5a;" title="暗夜"></span> </div> </div> <div class="map-toolbar"> <span style="color:#80b8d0; font-size:0.75rem;">预设:</span> <div class="preset-row"> <button class="preset-btn active-preset" data-rows="5" data-cols="9">5×9</button> <button class="preset-btn" data-rows="6" data-cols="9">6×9</button> <button class="preset-btn" data-rows="7" data-cols="11">7×11</button> <button class="preset-btn" data-rows="5" data-cols="7">5×7</button> <button class="preset-btn" data-rows="3" data-cols="5">3×5</button> </div> <span class="dim-label">行:</span><input type="number" id="mapRows" class="dim-input" value="5" min="1" max="15"> <span class="dim-label">列:</span><input type="number" id="mapCols" class="dim-input" value="9" min="1" max="15"> <button type="button" class="btn-icon" id="applySizeBtn">应用</button> <input type="text" id="currentPlantFile" value="豌豆射手.png" placeholder="植物文件" style="width:100px;"> <input type="text" id="currentPlantLink" value="豌豆射手" placeholder="链接" style="width:80px;"> <button type="button" class="btn-icon" id="clearMapBtn">清空</button> </div> <div class="map-grid-scroll"><div id="mapGrid"></div></div> </div> <div class="infocard-sim"> <div class="infocard-header"> <span>📋 Infocard 属性编辑器</span> <span style="background:#5a3a2e;">图片=xxx|图片尺寸=xx|属性=...</span> </div> <div class="image-input-row"> <label>🖼️ 图片=</label> <input type="text" id="infocardImage" value="豌豆进化.png" placeholder="文件名"> <label>图片尺寸=</label> <input type="text" id="infocardImageSize" value="200px" placeholder="200px" class="size-input"> </div> <div id="attrEditorContainer" class="attr-editor"></div> <button type="button" id="addAttrBtn" class="add-attr-btn">➕ 添加属性项</button> </div> <div class="section-divider"></div> <div class="tab-bar"> <button class="tab-btn active" data-tab="init">初始化</button> <button class="tab-btn" data-tab="prepare">准备阶段</button> <button class="tab-btn" data-tab="start">开始后行为</button> </div> <div id="tabInit" class="tab-content"> <div class="input-group"> <label>📋 地图行为 - 初始化</label> <div id="initEventsContainer"></div> <button type="button" id="addInitEventBtn" class="add-event-btn">➕ 添加初始化事件</button> </div> </div> <div id="tabPrepare" class="tab-content" style="display:none;"> <div class="input-group"> <label>📋 地图行为 - 准备阶段</label> <div id="prepareEventsContainer"></div> <button type="button" id="addPrepareEventBtn" class="add-event-btn">➕ 添加准备阶段事件</button> </div> </div> <div id="tabStart" class="tab-content" style="display:none;"> <div class="input-group"> <label>📋 地图行为 - 开始后行为</label> <div id="startEventsContainer"></div> <button type="button" id="addStartEventBtn" class="add-event-btn">➕ 添加开始后事件</button> </div> </div> <div class="section-divider"></div> <div class="input-group"> <label>🎁 通关奖励</label> <div id="rewardsContainer"></div> <button type="button" id="addRewardBtn" class="add-event-btn">➕ 添加奖励</button> </div> <div class="input-group"> <label>🖼️ 困难模式图片 <i>(可选)</i></label> <div class="image-row"> <input type="text" id="hardModeFile" value="困难.png" placeholder="文件名" style="flex:2;"> <input type="text" id="hardModeWidth" value="250px" placeholder="宽度" style="flex:1;"> <input type="text" id="hardModeLink" value="【豌豆进化】困难" placeholder="链接" style="flex:1;"> </div> </div> <button class="btn-primary" id="generateBtn">🗺️ 立即生成 Wiki 文本</button> </div> <div class="preview-panel"> <div class="preview-header"> <h2>📄 Wiki输出</h2> <div class="preview-actions"> <button class="paste-btn" id="pasteToImportBtn">📋 从预览导入</button> <button class="copy-btn" id="copyBtn">📋 复制代码</button> </div> </div> <div class="wiki-output" id="wikiOutput"></div> <div class="note">⚡ 实时生成。点击格子编辑完整样式(支持特殊边框如红线),右键清除植物。</div> </div> </div> </div> <!-- 格子编辑弹窗 --> <div class="cell-popup" id="cellPopup"> <label style="color:#b0d8e8;">🪴 植物</label> <input type="text" id="popupFile" placeholder="文件名 (xxx.png)"> <input type="text" id="popupLink" placeholder="链接 (植物名)"> <div style="border-top:1px solid #3a5a6a; margin:8px 0; padding-top:6px;"> <label style="color:#b0d8e8; font-size:0.75rem;">🎨 单元格样式 <i style="font-size:0.65rem;">(额外CSS,如 border-right:4px solid red)</i></label> <div class="style-row"> <input type="color" id="popupCellBg" value="#11ff00" title="背景色"> <input type="color" id="popupCellBorder" value="#d4e9cd" title="边框色"> </div> <input type="text" id="popupExtraStyle" placeholder="额外样式,如 border-right:4px solid red" style="font-size:0.75rem;"> <div style="display:flex; gap:4px; margin-top:4px; flex-wrap:wrap;"> <button type="button" class="quick-style-btn" data-style="border-right:4px solid red">右红线</button> <button type="button" class="quick-style-btn" data-style="border-left:4px solid blue">左蓝线</button> <button type="button" class="quick-style-btn" data-style="border-bottom:4px solid gold">下金线</button> <button type="button" class="quick-style-btn" data-style="border:3px solid orange">橙框</button> <button type="button" class="quick-style-btn" data-style="">清除特殊</button> </div> </div> <div style="display:flex; gap:6px; margin-top:10px; justify-content:flex-end;"> <button type="button" class="btn-icon" id="popupClear">清除格子</button> <button type="button" class="btn-icon" id="popupSave" style="background:#3a6a7a;">保存</button> </div> </div> <div class="modal-overlay" id="importModal"> <div class="modal"> <h3>📥 导入 Wiki 源码</h3> <p>粘贴现有的关卡 Wiki 文本,程序将自动解析并填充到表单中</p> <textarea id="importTextarea" placeholder="在此粘贴 Wiki 源码..."></textarea> <div class="modal-actions"> <button class="modal-btn cancel" id="cancelImportBtn">取消</button> <button class="modal-btn primary" id="confirmImportBtn">解析并导入</button> </div> </div> </div> <script> (function(){ 'use strict'; const COLOR_OPTIONS = [ { value: '', label: '默认' },{ value: 'red', label: '红色' },{ value: 'blue', label: '蓝色' }, { value: 'green', label: '绿色' },{ value: 'orange', label: '橙色' },{ value: 'purple', label: '紫色' }, { value: 'gold', label: '金色' },{ value: 'brown', label: '棕色' } ]; const COLOR_MAP = Object.fromEntries(COLOR_OPTIONS.filter(o=>o.value).map(o=>[o.value,o.value])); const DEFAULT_ATTRS = [ { name: '地图', value: '前院【白天】(5行 × 9列)', color: 'red', bold: true }, { name: '音乐', value: '射击模式', color: 'red', bold: true }, { name: '初始阳光', value: '500', color: 'red', bold: true }, { name: '模式', value: '波模式、射击模式', color: 'red', bold: true }, { name: '升级线路', value: '1、线路1:<br>①豌豆射手<br>(500阳光,间隔0.35s)→<br>②双发射手<br>(1000阳光,间隔0.2s)→<br>③三线射手<br>(1750阳光,间隔0.15s)→<br>④机枪射手<br>(2500阳光,间隔0.15s)→<br>⑤狂野机枪射手<br>(3500阳光,间隔0.1s)<br><br>2、线路2:<br>①南瓜壳(500阳光)→<br>②火焰南瓜壳(1000阳光)→<br>③冰焰南瓜壳(2000阳光)', color: 'red', bold: true }, { name: '出怪方式', value: '固定出怪', color: 'red', bold: true }, { name: '僵尸', value: '①普通僵尸 × 39<br>②路障僵尸 × 59<br>③铁桶僵尸 × 35<br>④铁栅门僵尸 × 41<br>⑤撑杆僵尸 × 32<br>⑥豌豆僵尸 × 29<br>⑦橄榄球僵尸 × 27<br>⑧小摔哥僵尸 × 26<br>⑨狂野机枪僵尸 × 7<br>⑩伽刚特尔 × 6', color: 'red', bold: true }, { name: '事件', value: '0个', color: 'red', bold: true }, { name: '旗数', value: '3', color: 'red', bold: true }, { name: '每旗波次', value: '10', color: 'red', bold: true } ]; const DEFAULT_CARDS = [ { image: '普通僵尸.png', name: '普通僵尸', health: '270', speed: '慢', type: '普通', version: '0.1', color: 'linear-gradient(135deg, #E8E0F0, #D1C4E9)', border: '#7E57C2' }, { image: '路障僵尸.png', name: '路障僵尸', health: '', speed: '', type: '', version: '', color: 'linear-gradient(135deg, #E8E0F0, #D1C4E9)', border: '#7E57C2' }, { image: '铁桶僵尸.png', name: '铁桶僵尸', health: '', speed: '', type: '', version: '', color: 'linear-gradient(135deg, #E8E0F0, #D1C4E9)', border: '#7E57C2' }, { image: '铁栅门僵尸.png', name: '铁栅门僵尸', health: '', speed: '', type: '', version: '', color: 'linear-gradient(135deg, #E8E0F0, #D1C4E9)', border: '#7E57C2' }, { image: '撑杆僵尸.png', name: '撑杆僵尸', health: '', speed: '', type: '', version: '', color: 'linear-gradient(135deg, #E8E0F0, #D1C4E9)', border: '#7E57C2' }, { image: '豌豆僵尸.png', name: '豌豆僵尸', health: '', speed: '', type: '', version: '', color: 'linear-gradient(135deg, #E8E0F0, #D1C4E9)', border: '#7E57C2' }, { image: '橄榄球僵尸.png', name: '橄榄球僵尸', health: '', speed: '', type: '', version: '', color: 'linear-gradient(135deg, #E8E0F0, #D1C4E9)', border: '#7E57C2' }, { image: '小摔哥僵尸.png', name: '小摔哥僵尸', health: '', speed: '', type: '', version: '', color: 'linear-gradient(135deg, #E8E0F0, #D1C4E9)', border: '#7E57C2' }, { image: '狂野机枪僵尸.png', name: '狂野机枪僵尸', health: '', speed: '', type: '', version: '', color: 'linear-gradient(135deg, #E8E0F0, #D1C4E9)', border: '#7E57C2' }, { image: '伽刚特尔.png', name: '伽刚特尔', health: '', speed: '', type: '', version: '', color: 'linear-gradient(135deg, #E8E0F0, #D1C4E9)', border: '#7E57C2' } ]; // 地图数据:每个格子 {file, link, cellBg, cellBorder, extraStyle} let mapRows = 5, mapCols = 9; let mapData = []; let gridBg = '#1a6b0a', gridCellColor = '#11ff00', gridBorderColor = '#d4e9cd', headerColor = '#ffffff'; function initMapData() { mapData = Array.from({length:mapRows}, ()=>Array.from({length:mapCols}, ()=>null)); } initMapData(); const attrContainer = document.getElementById('attrEditorContainer'); const infocardImage = document.getElementById('infocardImage'); const infocardImageSize = document.getElementById('infocardImageSize'); const mapImageFile = document.getElementById('mapImageFile'); const mapImageWidth = document.getElementById('mapImageWidth'); const mapImageLink = document.getElementById('mapImageLink'); const cardsContainer = document.getElementById('cardsContainer'); const initEventsContainer = document.getElementById('initEventsContainer'); const prepareEventsContainer = document.getElementById('prepareEventsContainer'); const startEventsContainer = document.getElementById('startEventsContainer'); const rewardsContainer = document.getElementById('rewardsContainer'); const hardModeFile = document.getElementById('hardModeFile'); const hardModeWidth = document.getElementById('hardModeWidth'); const hardModeLink = document.getElementById('hardModeLink'); const wikiOutputDiv = document.getElementById('wikiOutput'); const formPanel = document.getElementById('formPanel'); const importModal = document.getElementById('importModal'); const importTextarea = document.getElementById('importTextarea'); const mapGrid = document.getElementById('mapGrid'); const cellPopup = document.getElementById('cellPopup'); const mapRowsInput = document.getElementById('mapRows'); const mapColsInput = document.getElementById('mapCols'); const gridBgInput = document.getElementById('gridBg'); const gridCellInput = document.getElementById('gridCell'); const gridBorderInput = document.getElementById('gridBorder'); const headerColorInput = document.getElementById('headerColor'); let currentEditRow = -1, currentEditCol = -1; // ========== 属性编辑器 ========== function createColorSelect(sel='') { const s = document.createElement('select'); s.className='attr-color-select'; COLOR_OPTIONS.forEach(o => { const op = document.createElement('option'); op.value=o.value; op.textContent=o.label; if(o.value===sel) op.selected=true; s.appendChild(op); }); return s; } function createBoldCheck(chk=false) { const d = document.createElement('div'); d.className='attr-bold-check'; d.innerHTML = `<input type="checkbox" ${chk?'checked':''}><span>加粗</span>`; return d; } function createAttrItem(d={name:'',value:'',color:'',bold:false}) { const div = document.createElement('div'); div.className='attr-item'; const ni = document.createElement('input'); ni.type='text'; ni.className='attr-name'; ni.placeholder='属性名'; ni.value=d.name||''; const vi = document.createElement('input'); vi.type='text'; vi.className='attr-value'; vi.placeholder='属性值'; vi.value=d.value||''; const cs = createColorSelect(d.color||''); const bc = createBoldCheck(d.bold||false); const cb = bc.querySelector('input'); const rb = document.createElement('button'); rb.type='button'; rb.className='attr-remove-btn'; rb.textContent='✕'; rb.addEventListener('click',()=>{ if(attrContainer.children.length>1){ div.remove(); refreshPreview(); } else alert('至少保留一个属性项'); }); div.append(ni,vi,cs,bc,rb); ni.addEventListener('input',refreshPreview); vi.addEventListener('input',refreshPreview); cs.addEventListener('change',refreshPreview); cb.addEventListener('change',refreshPreview); return div; } function setAttrItems(a){ attrContainer.innerHTML=''; a.forEach(x=>attrContainer.appendChild(createAttrItem(x))); } function collectAttrs(){ const a=[]; attrContainer.querySelectorAll('.attr-item').forEach(item=>{ const n=item.querySelector('.attr-name').value.trim(); const v=item.querySelector('.attr-value').value.trim(); if(!n&&!v) return; a.push({name:n||'?',value:v||'',color:item.querySelector('.attr-color-select').value,bold:item.querySelector('.attr-bold-check input').checked}); }); return a; } // ========== 地图预放置 ========== function updateGridColors() { gridBg = gridBgInput.value || '#1a6b0a'; gridCellColor = gridCellInput.value || '#11ff00'; gridBorderColor = gridBorderInput.value || '#d4e9cd'; headerColor = headerColorInput.value || '#ffffff'; renderMapGrid(); refreshPreview(); } function applyColorPreset(bg, cell, border, header) { gridBgInput.value = bg; gridCellInput.value = cell; gridBorderInput.value = border; headerColorInput.value = header||'#ffffff'; updateGridColors(); } function resizeMap(newRows, newCols) { const oldData = mapData; mapRows = newRows; mapCols = newCols; mapData = Array.from({length:mapRows}, (_,r)=>Array.from({length:mapCols}, (_,c)=>(r<oldData.length && c<oldData[0].length ? oldData[r][c] : null))); mapRowsInput.value = mapRows; mapColsInput.value = mapCols; renderMapGrid(); refreshPreview(); } function renderMapGrid() { mapGrid.innerHTML = ''; const headerRow = document.createElement('div'); headerRow.className='map-grid-row'; headerRow.innerHTML = '<div class="map-grid-label"></div>'; for(let c=1;c<=mapCols;c++) headerRow.innerHTML += `<div class="map-grid-label" style="color:${headerColor}">列${c}</div>`; mapGrid.appendChild(headerRow); for(let r=0;r<mapRows;r++) { const row = document.createElement('div'); row.className='map-grid-row'; row.innerHTML = `<div class="map-grid-label" style="color:${headerColor}">行${r+1}</div>`; for(let c=0;c<mapCols;c++) { const d = mapData[r][c]; const cell = document.createElement('div'); cell.className = 'map-grid-cell'; const cbg = d?.cellBg || gridCellColor; const cbd = d?.cellBorder || gridBorderColor; cell.style.background = cbg; cell.style.border = `2px solid ${cbd}`; if(d?.extraStyle) { cell.style.cssText += ';' + d.extraStyle; cell.classList.add('has-special'); } if(d?.file) { cell.classList.add('placed'); cell.title = d.file; } cell.addEventListener('click',()=>openCellPopup(r,c)); cell.addEventListener('contextmenu',(e)=>{ e.preventDefault(); mapData[r][c]=null; renderMapGrid(); refreshPreview(); }); row.appendChild(cell); } mapGrid.appendChild(row); } } function openCellPopup(r,c) { currentEditRow = r; currentEditCol = c; const d = mapData[r][c]; document.getElementById('popupFile').value = d?.file || ''; document.getElementById('popupLink').value = d?.link || ''; document.getElementById('popupCellBg').value = d?.cellBg || gridCellColor; document.getElementById('popupCellBorder').value = d?.cellBorder || gridBorderColor; document.getElementById('popupExtraStyle').value = d?.extraStyle || ''; cellPopup.style.display = 'block'; const cells = mapGrid.querySelectorAll('.map-grid-cell'); const idx = r*mapCols + c + mapCols; if(cells[idx]) { const rect = cells[idx].getBoundingClientRect(); cellPopup.style.left = Math.min(rect.right+8, window.innerWidth-290)+'px'; cellPopup.style.top = Math.min(rect.top, window.innerHeight-380)+'px'; } } function saveCellPopup() { if(currentEditRow<0) return; const file = document.getElementById('popupFile').value.trim(); const link = document.getElementById('popupLink').value.trim(); const cellBg = document.getElementById('popupCellBg').value; const cellBorder = document.getElementById('popupCellBorder').value; const extraStyle = document.getElementById('popupExtraStyle').value.trim(); if(file) { mapData[currentEditRow][currentEditCol] = {file, link: link||file.replace('.png',''), cellBg, cellBorder, extraStyle}; } else if(cellBg!==gridCellColor || cellBorder!==gridBorderColor || extraStyle) { mapData[currentEditRow][currentEditCol] = {file:'', link:'', cellBg, cellBorder, extraStyle}; } else { mapData[currentEditRow][currentEditCol] = null; } cellPopup.style.display = 'none'; renderMapGrid(); refreshPreview(); } function clearMap() { initMapData(); renderMapGrid(); refreshPreview(); } function buildMapTable() { let table = `\n== 地图预放置 ==\n{| style="width:100%;max-width:900px;table-layout:fixed;text-align:center;background:${gridBg};border-collapse:collapse;"\n|-\n! !!`; for(let c=1;c<=mapCols;c++) table += ` <font color=${headerColor}>列${c}</font> !!`; table = table.slice(0,-2) + '\n'; for(let r=0;r<mapRows;r++) { table += `|-\n! <font color=${headerColor}>行${r+1}</font>\n`; for(let c=0;c<mapCols;c++) { const d = mapData[r][c]; const cbg = d?.cellBg || gridCellColor; const cbd = d?.cellBorder || gridBorderColor; let styleStr = `height:80px;border:2px solid ${cbd};background:${cbg}`; if(d?.extraStyle) styleStr += ';' + d.extraStyle; const content = (d?.file) ? `[[文件:${d.file}|50px|link=${d.link}]]` : ''; table += `| style="${styleStr}" | ${content}\n`; } } table += '|}\n'; return table; } function parseMapTable(src) { const tableMatch = src.match(/== 地图预放置 ==\s*\n\{\|[\s\S]*?\n\|\}/); if(!tableMatch) { initMapData(); renderMapGrid(); return; } const bgMatch = tableMatch[0].match(/background:([#\w]+)/); if(bgMatch) { gridBg = bgMatch[1]; gridBgInput.value = gridBg; } const hcMatch = tableMatch[0].match(/<font color=([^>]+)>列1<\/font>/); if(hcMatch) { headerColor = hcMatch[1]; headerColorInput.value = headerColor; } const headerMatch = tableMatch[0].match(/! !!([\s\S]*?)\n\|-/); let cols = 9; if(headerMatch) { const colHeaders = headerMatch[1].match(/列\d+/g); if(colHeaders) cols = colHeaders.length; } const rows = tableMatch[0].split('\n|-\n'); const rowMatches = rows.filter(r=>/<font color=[^>]*>行\d/.test(r)); const newRows = rowMatches.length; if(newRows>0 && cols>0) { mapRows = newRows; mapCols = cols; mapRowsInput.value = mapRows; mapColsInput.value = mapCols; initMapData(); rowMatches.forEach(row => { const rm = row.match(/<font color=[^>]*>行(\d+)<\/font>/); if(!rm) return; const rowIdx = parseInt(rm[1])-1; if(rowIdx<0||rowIdx>=mapRows) return; const cells = [...row.matchAll(/\| style="([^"]*)" \| (.*?)(?=\n|$)/g)]; cells.forEach((m,ci) => { if(ci>=mapCols) return; const styleStr = m[1]; const cbg = styleStr.match(/background:([#\w]+)/); const cbd = styleStr.match(/border:2px solid ([#\w]+)/); // 提取额外样式(排除height, border, background) let extra = styleStr.replace(/height:[^;]+;?/,'').replace(/border:2px solid [#\w]+;?/,'').replace(/background:[#\w]+;?/,'').trim(); if(extra.endsWith(';')) extra = extra.slice(0,-1); if(cbg) gridCellColor = cbg[1]; if(cbd) gridBorderColor = cbd[1]; const content = m[2].trim(); const fm = content.match(/\[\[文件:([^|]+)\|[^|]+\|link=([^\]]+)\]\]/); if(fm || extra) { mapData[rowIdx][ci] = { file: fm?fm[1]:'', link: fm?fm[2]:'', cellBg: cbg?cbg[1]:gridCellColor, cellBorder: cbd?cbd[1]:gridBorderColor, extraStyle: extra }; } }); }); gridCellInput.value = gridCellColor; gridBorderInput.value = gridBorderColor; } renderMapGrid(); } // ========== 卡片/事件/奖励 ========== function createCardItem(d) { d = d || { image:'', name:'', health:'', speed:'', type:'', version:'', color:'linear-gradient(135deg, #E8E0F0, #D1C4E9)', border:'#7E57C2' }; const div = document.createElement('div'); div.className='card-item'; div.innerHTML = `<div class="card-row"><input type="text" class="card-image" value="${d.image||''}" placeholder="图片" style="flex:1.5;"><input type="text" class="card-name" value="${d.name||''}" placeholder="名称" style="flex:1;"><input type="text" class="card-health" value="${d.health||''}" placeholder="血量" style="flex:1;"><button type="button" class="event-remove-btn card-remove">✕</button></div><div class="card-row"><input type="text" class="card-speed" value="${d.speed||''}" placeholder="速度" style="flex:0.8;"><input type="text" class="card-type" value="${d.type||''}" placeholder="类型" style="flex:0.8;"><input type="text" class="card-version" value="${d.version||''}" placeholder="版本" style="flex:0.6;"><input type="text" class="card-color" value="${d.color||''}" placeholder="渐变" style="flex:2;"><input type="text" class="card-border" value="${d.border||''}" placeholder="边框" style="flex:1;"></div>`; cardsContainer.appendChild(div); div.querySelector('.card-remove').addEventListener('click',()=>{ if(cardsContainer.children.length>1){ div.remove(); refreshPreview(); } else alert('至少保留一个卡片'); }); div.querySelectorAll('input').forEach(i=>i.addEventListener('input',refreshPreview)); } function collectCards() { const cards = []; cardsContainer.querySelectorAll('.card-item').forEach(item => { cards.push({ image: item.querySelector('.card-image')?.value.trim()||'', name: item.querySelector('.card-name')?.value.trim()||'', health: item.querySelector('.card-health')?.value.trim()||'', speed: item.querySelector('.card-speed')?.value.trim()||'', type: item.querySelector('.card-type')?.value.trim()||'', version: item.querySelector('.card-version')?.value.trim()||'', color: item.querySelector('.card-color')?.value.trim()||'', border: item.querySelector('.card-border')?.value.trim()||'' }); }); return cards; } function collectEvents(container, cls) { const e = []; container.querySelectorAll(`.${cls}`).forEach(inp => { const v = inp.value.trim(); if(v) e.push(v); }); return e; } function createEventItem(container, cls, value='') { const div = document.createElement('div'); div.className='event-item'; const inp = document.createElement('input'); inp.type='text'; inp.className=cls; inp.value=value; inp.placeholder='事件描述'; inp.addEventListener('input', refreshPreview); const rb = document.createElement('button'); rb.type='button'; rb.className='event-remove-btn'; rb.textContent='✕'; rb.addEventListener('click', ()=>{ if(container.children.length>1){ div.remove(); refreshPreview(); } else alert('至少保留一项'); }); div.append(inp, rb); container.appendChild(div); } function collectRewards() { const r = []; rewardsContainer.querySelectorAll('.event-item').forEach(item => { const f = item.querySelector('.reward-file'); if(!f) return; const file = f.value.trim(), link = item.querySelector('.reward-link')?.value.trim() || ''; if(file) r.push({file, link}); }); return r; } function createRewardItem(file='', link='') { const div = document.createElement('div'); div.className='event-item'; const fi = document.createElement('input'); fi.type='text'; fi.className='reward-file'; fi.value=file; fi.placeholder='文件名'; fi.style.flex='2'; fi.addEventListener('input', refreshPreview); const li = document.createElement('input'); li.type='text'; li.className='reward-link'; li.value=link; li.placeholder='链接'; li.style.flex='1'; li.addEventListener('input', refreshPreview); const rb = document.createElement('button'); rb.type='button'; rb.className='event-remove-btn reward-remove'; rb.textContent='✕'; rb.addEventListener('click', ()=>{ if(rewardsContainer.children.length>1){ div.remove(); refreshPreview(); } else alert('至少保留一项奖励'); }); div.append(fi, li, rb); rewardsContainer.appendChild(div); } function clearContainer(c) { while(c.children.length>0) c.removeChild(c.firstChild); } // ========== 构建Wiki ========== function buildInfocard() { const img = infocardImage.value.trim() || '豌豆进化.png'; const sz = infocardImageSize.value.trim() || '200px'; const attrs = collectAttrs(); const preplaced = []; for(let r=0;r<mapRows;r++) for(let c=0;c<mapCols;c++) if(mapData[r][c]?.file) preplaced.push(`${mapData[r][c].link}(位置:第${r+1}行第${c+1}列)`); if(preplaced.length>0 && !attrs.some(a=>a.name==='地图预放置')) attrs.push({name:'地图预放置', value:preplaced.join('<br>'), color:'red', bold:true}); const parts = attrs.map(a => { let v = a.value; if(a.bold) v = `'''${v}'''`; if(a.color) v = `<font color=${a.color}>${v}</font>`; return `${a.name}:${v}`; }); return `{{Infocard|图片=${img}|图片尺寸=${sz}|属性=${parts.join(',')}}}`; } function buildCardsBlock() { const cards = collectCards(); if (cards.length === 0) return ''; const lines = cards.map(c => `{{Card|image=[[文件:${c.image}|75px|${c.name}|link=${c.name}]]|name=${c.name}|health=${c.health}|speed=${c.speed}|type=${c.type}|version=${c.version}|color=${c.color}|border=${c.border}}}`); return `<div style="float:left" class="pvzhe-card-container">\n${lines.join('\n')}\n</div>`; } function buildWikiText() { const infocard = buildInfocard(); const mf = mapImageFile.value.trim()||'射击模式.png', mw = mapImageWidth.value.trim()||'350px', ml = mapImageLink.value.trim()||'射击模式'; const cardsBlock = buildCardsBlock(), mapTable = buildMapTable(); const initE = collectEvents(initEventsContainer,'init-event'), prepE = collectEvents(prepareEventsContainer,'prepare-event'), startE = collectEvents(startEventsContainer,'start-event'); const rewards = collectRewards(); const hf=hardModeFile.value.trim(), hw=hardModeWidth.value.trim()||'250px', hl=hardModeLink.value.trim(); let es=''; if(initE.length>0||prepE.length>0||startE.length>0){es='== 事件 ==\n';es+='=== 地图行为(初始化) ===\n<hr/>\n';if(initE.length===0||(initE.length===1&&initE[0]==='无'))es+='*无\n';else initE.forEach(e=>es+=`*${e}<br>\n`);es+='=== 地图行为(准备阶段) ===\n<hr/>\n';if(prepE.length===0||(prepE.length===1&&prepE[0]==='无'))es+='*无\n';else prepE.forEach(e=>es+=`*${e}\n`);es+='=== 地图行为(开始后行为) ===\n<hr/>\n';if(startE.length===0||(startE.length===1&&startE[0]==='无'))es+='*无\n';else startE.forEach(e=>es+=`*${e}<br>\n`);} let rs='';if(rewards.length>0||hf){rs='== 通关奖励 ==\n<div style="display:flex; align-items:center;">\n';rewards.forEach(r=>rs+=`[[文件:${r.file}|link=${r.link||r.file}]]\n`);if(hf){rs+=`<div style="flex:1; display:flex; justify-content:center;">\n[[File:${hf}|${hw}|link=${hl||''}]]\n</div>\n`;}rs+='</div>\n';} let headerBlock=`{{返回关卡列表}}\n<div style="float:right; margin-left:0px; width:100%; max-width:650px; overflow:hidden; display:flex; align-items:center;">\n<div style="float:right;width:100%;">\n<div style="display: flex; justify-content: flex-end;">\n[[File:${mf}|${mw}|link=${ml}]]\n</div>`; if(cardsBlock) headerBlock+='\n'+cardsBlock; if(mapTable) headerBlock+='\n'+mapTable; headerBlock+='\n</div>\n</div>'; return `${headerBlock}\n${infocard}\n${es}${rs}`; } function refreshPreview(){const t=buildWikiText();wikiOutputDiv.innerHTML=t.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');} // ========== 导入 ========== function parseWikiSource(src){try{let img='豌豆进化.png',sz='200px';const attrs=[];const im=src.match(/{{Infocard\|图片=([^|]+)\|图片尺寸=([^|]+)\|属性=([^}]+)}}/);if(im){img=im[1].trim();sz=im[2].trim();const attrStr=im[3];const parts=[];let depth=0,cur='';for(let i=0;i<attrStr.length;i++){const ch=attrStr[i];if(ch==='<')depth++;if(ch==='>')depth--;if(ch===','&&depth===0){parts.push(cur);cur='';}else cur+=ch;}if(cur)parts.push(cur);parts.forEach(p=>{const ci=p.indexOf(':');if(ci===-1)return;const name=p.substring(0,ci).trim();let value=p.substring(ci+1).trim();let color='',bold=false;const fm=value.match(/<font color=([^>]+)>(.*?)<\/font>/i);if(fm){const cn=fm[1].replace(/['"]/g,'');if(COLOR_MAP[cn])color=cn;value=fm[2];}const bm=value.match(/^'''(.+)'''$/);if(bm){bold=true;value=bm[1];}if(name!=='地图预放置')attrs.push({name,value,color,bold});});}let mf='射击模式.png',mw='350px',ml='射击模式';const fm=src.match(/\[\[File:([^|]+)\|([^|]+)\|link=([^\]]+)\]\]/);if(fm){mf=fm[1];mw=fm[2];ml=fm[3];}const cards=[];const cardRegex=/{{Card\|image=\[\[文件:([^|]+)\|[^|]+\|([^|]+)\|link=([^\]]+)\]\]\|name=([^|]+)\|health=([^|]*)\|speed=([^|]*)\|type=([^|]*)\|version=([^|]*)\|color=([^|]+)\|border=([^}]+)}}/g;let cm;while((cm=cardRegex.exec(src))!==null){cards.push({image:cm[1],name:cm[4],health:cm[5],speed:cm[6],type:cm[7],version:cm[8],color:cm[9],border:cm[10]});}const initE=[],prepE=[],startE=[];const is=src.match(/=== 地图行为(初始化) ===\s*<hr\/>\s*([\s\S]*?)(?====|$)/);if(is){const ms=is[1].matchAll(/\*([^<\n]*)/g);for(const m of ms){const v=m[1].trim();if(v&&v!=='无')initE.push(v);}}const ps=src.match(/=== 地图行为(准备阶段) ===\s*<hr\/>\s*([\s\S]*?)(?====|$)/);if(ps){const ms=ps[1].matchAll(/\*([^\n]*)/g);for(const m of ms){const v=m[1].trim();if(v&&v!=='无')prepE.push(v);}}const ss=src.match(/=== 地图行为(开始后行为) ===\s*<hr\/>\s*([\s\S]*?)(?====|$)/);if(ss){const ms=ss[1].matchAll(/\*([^<\n]*)/g);for(const m of ms){const v=m[1].trim();if(v&&v!=='无')startE.push(v);}}const rewards=[];const rs=src.match(/== 通关奖励 ==\s*([\s\S]*?)(?=\n\n|$)/);if(rs){const ms=rs[1].matchAll(/\[\[文件:([^|]+)\|link=([^\]]+)\]\]/g);for(const m of ms)rewards.push({file:m[1],link:m[2]});}let hf='',hw='250px',hl='';if(rs){const ms=rs[1].matchAll(/\[\[File:([^|]+)\|([^|]+)\|link=([^\]]+)\]\]/g);const arr=Array.from(ms);if(arr.length>1){hf=arr[1][1];hw=arr[1][2];hl=arr[1][3];}}return{image:img,imageSize:sz,attrs:attrs.length>0?attrs:DEFAULT_ATTRS,mapFile:mf,mapWidth:mw,mapLink:ml,cards:cards,initEvents:initE,prepareEvents:prepE,startEvents:startE,rewards,hardFile:hf,hardWidth:hw,hardLink:hl};}catch(e){console.error(e);return null;}} function applyParsedData(d){if(!d)return;if(d.image)infocardImage.value=d.image;if(d.imageSize)infocardImageSize.value=d.imageSize;if(d.attrs&&d.attrs.length>0)setAttrItems(d.attrs);if(d.mapFile)mapImageFile.value=d.mapFile;if(d.mapWidth)mapImageWidth.value=d.mapWidth;if(d.mapLink)mapImageLink.value=d.mapLink;if(d.cards&&d.cards.length>0){clearContainer(cardsContainer);d.cards.forEach(c=>createCardItem(c));}if(d.initEvents!==undefined){clearContainer(initEventsContainer);if(d.initEvents.length===0)createEventItem(initEventsContainer,'init-event','无');else d.initEvents.forEach(e=>createEventItem(initEventsContainer,'init-event',e));}if(d.prepareEvents!==undefined){clearContainer(prepareEventsContainer);if(d.prepareEvents.length===0)createEventItem(prepareEventsContainer,'prepare-event','无');else d.prepareEvents.forEach(e=>createEventItem(prepareEventsContainer,'prepare-event',e));}if(d.startEvents!==undefined){clearContainer(startEventsContainer);if(d.startEvents.length===0)createEventItem(startEventsContainer,'start-event','无');else d.startEvents.forEach(e=>createEventItem(startEventsContainer,'start-event',e));}if(d.rewards&&d.rewards.length>0){clearContainer(rewardsContainer);d.rewards.forEach(r=>createRewardItem(r.file,r.link));}if(d.hardFile)hardModeFile.value=d.hardFile;if(d.hardWidth)hardModeWidth.value=d.hardWidth;if(d.hardLink)hardModeLink.value=d.hardLink;const src=importTextarea.value;if(src)parseMapTable(src);else{updateGridColors();renderMapGrid();}refreshPreview();} function importFromSource(src){const d=parseWikiSource(src);if(d){importTextarea.value=src;applyParsedData(d);alert('✅ 导入成功!');}else alert('❌ 解析失败');} function bindEvents(){ document.getElementById('addAttrBtn').addEventListener('click',()=>{attrContainer.appendChild(createAttrItem());refreshPreview();}); document.getElementById('addCardBtn').addEventListener('click',()=>{createCardItem();refreshPreview();}); document.getElementById('addInitEventBtn').addEventListener('click',()=>{createEventItem(initEventsContainer,'init-event','');refreshPreview();}); document.getElementById('addPrepareEventBtn').addEventListener('click',()=>{createEventItem(prepareEventsContainer,'prepare-event','');refreshPreview();}); document.getElementById('addStartEventBtn').addEventListener('click',()=>{createEventItem(startEventsContainer,'start-event','');refreshPreview();}); document.getElementById('addRewardBtn').addEventListener('click',()=>{createRewardItem('','');refreshPreview();}); infocardImage.addEventListener('input',refreshPreview);infocardImageSize.addEventListener('input',refreshPreview); [mapImageFile,mapImageWidth,mapImageLink,hardModeFile,hardModeWidth,hardModeLink].forEach(i=>i.addEventListener('input',refreshPreview)); document.getElementById('generateBtn').addEventListener('click',refreshPreview); document.getElementById('copyBtn').addEventListener('click',async()=>{try{await navigator.clipboard.writeText(buildWikiText());alert('✅ 已复制');}catch{alert('❌ 复制失败');}}); formPanel.addEventListener('input',e=>{if(e.target.tagName==='INPUT'||e.target.tagName==='TEXTAREA')refreshPreview();}); document.getElementById('importBtn').addEventListener('click',()=>{importTextarea.value='';importModal.style.display='flex';}); document.getElementById('cancelImportBtn').addEventListener('click',()=>importModal.style.display='none'); document.getElementById('confirmImportBtn').addEventListener('click',()=>{const s=importTextarea.value.trim();if(s){importFromSource(s);importModal.style.display='none';}else alert('请粘贴源码');}); document.getElementById('pasteToImportBtn').addEventListener('click',()=>importFromSource(buildWikiText())); importModal.addEventListener('click',e=>{if(e.target===importModal)importModal.style.display='none';}); document.getElementById('applySizeBtn').addEventListener('click',()=>{let nr=parseInt(mapRowsInput.value)||5;let nc=parseInt(mapColsInput.value)||9;nr=Math.max(1,Math.min(15,nr));nc=Math.max(1,Math.min(15,nc));resizeMap(nr,nc);}); document.querySelectorAll('.preset-btn').forEach(btn=>{btn.addEventListener('click',()=>{document.querySelectorAll('.preset-btn').forEach(b=>b.classList.remove('active-preset'));btn.classList.add('active-preset');const nr=parseInt(btn.dataset.rows);const nc=parseInt(btn.dataset.cols);if(nr&&nc)resizeMap(nr,nc);});}); gridBgInput.addEventListener('input',updateGridColors);gridCellInput.addEventListener('input',updateGridColors);gridBorderInput.addEventListener('input',updateGridColors);headerColorInput.addEventListener('input',updateGridColors); document.querySelectorAll('.preset-dot').forEach(dot=>{dot.addEventListener('click',()=>{applyColorPreset(dot.dataset.bg,dot.dataset.cell,dot.dataset.border,dot.dataset.header);});}); document.getElementById('clearMapBtn').addEventListener('click',clearMap); document.getElementById('popupSave').addEventListener('click',saveCellPopup); document.getElementById('popupClear').addEventListener('click',()=>{if(currentEditRow>=0){mapData[currentEditRow][currentEditCol]=null;renderMapGrid();refreshPreview();}cellPopup.style.display='none';}); document.querySelectorAll('.quick-style-btn').forEach(btn=>{btn.addEventListener('click',()=>{document.getElementById('popupExtraStyle').value=btn.dataset.style;});}); document.addEventListener('click',(e)=>{if(!cellPopup.contains(e.target)&&!e.target.classList.contains('map-grid-cell'))cellPopup.style.display='none';}); document.querySelectorAll('.tab-btn').forEach(btn=>{btn.addEventListener('click',()=>{document.querySelectorAll('.tab-btn').forEach(b=>b.classList.remove('active'));btn.classList.add('active');const tab=btn.dataset.tab;document.getElementById('tabInit').style.display=tab==='init'?'block':'none';document.getElementById('tabPrepare').style.display=tab==='prepare'?'block':'none';document.getElementById('tabStart').style.display=tab==='start'?'block':'none';});}); } function init(){ setAttrItems(DEFAULT_ATTRS);clearContainer(cardsContainer);DEFAULT_CARDS.forEach(c=>createCardItem(c)); clearContainer(initEventsContainer);createEventItem(initEventsContainer,'init-event','无'); clearContainer(prepareEventsContainer);createEventItem(prepareEventsContainer,'prepare-event','无'); clearContainer(startEventsContainer);createEventItem(startEventsContainer,'start-event','无'); clearContainer(rewardsContainer);createRewardItem('钱袋子.png','钱袋子'); initMapData();mapData[2][0]={file:'豌豆射手.png',link:'豌豆射手',cellBg:gridCellColor,cellBorder:gridBorderColor,extraStyle:''}; renderMapGrid();bindEvents();refreshPreview(); } init(); })(); </script> </body> </html> </div> </includeonly>
返回
微件:LevelEditor
。
查看“︁微件:LevelEditor”︁的源代码
来自植物大战僵尸杂交版Wiki