微件:LevelEditor:修订间差异
来自植物大战僵尸杂交版Wiki
更多操作
无编辑摘要 |
无编辑摘要 |
||
| 第28行: | 第28行: | ||
.image-input-row label { color: #b0d8e8; font-weight: 600; font-size: 0.85rem; white-space: nowrap; margin: 0; } | .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; } | .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-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-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; } | ||
| 第52行: | 第51行: | ||
.image-row { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; } | .image-row { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; } | ||
.image-row input { flex: 1; } | .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; } | .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 { 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; } | .btn-primary:active { transform: translateY(4px); box-shadow: 0 4px 0 #4a7088; } | ||
| 第68行: | 第68行: | ||
.top-link a { color: #ffaaaa; font-weight: bold; } | .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-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: | .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 h3 { color: #b0d8e8; margin: 0 0 16px 0; font-size: 1.6rem; } | ||
.modal p { color: #80a8c0; margin-bottom: 12px; } | .modal p { color: #80a8c0; margin-bottom: 12px; } | ||
| 第78行: | 第78行: | ||
.card-item { background: #16222caa; border-radius: 16px; padding: 10px 14px; margin-bottom: 8px; border: 1px solid #4a6880; } | .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 .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; } | .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-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-scroll { overflow-x: auto; max-width: 100%; } | ||
.map-grid-row { display: flex; gap: 2px; margin-bottom: 2px; align-items: center; } | .map-grid-row { display: flex; gap: 2px; margin-bottom: 2px; align-items: center; } | ||
.map-grid-cell { width: 44px; height: 44px | .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: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.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-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 { 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 input { width: 130px; font-size: 0.78rem; padding: 5px 8px; } | ||
.map-toolbar button { padding: 5px 10px; font-size: 0.78rem; } | .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-label { color: #80b8d0; font-size: 0.75rem; margin: 0 2px; } | ||
.map-toolbar .dim-input { width: 50px; text-align: center; } | .map-toolbar .dim-input { width: 50px; text-align: center; } | ||
.color-palette { display: flex; align-items: center; gap: | .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. | .color-palette label { font-size: 0.72rem; margin: 0; display: flex; align-items: center; gap: 3px; color: #90b8d0; } | ||
.color-palette input[type="color"] { width: | .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-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 { 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); } | .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: | .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 input { font-size: 0.8rem; margin-bottom: 6px; } | ||
.cell-popup button { font-size: 0.75rem; padding: 4px 10px; margin-right: 4px; } | .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-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 { 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; } | ||
| 第109行: | 第112行: | ||
.preset-btn:hover { background: #2a4a5a; color: #c0e8f0; } | .preset-btn:hover { background: #2a4a5a; color: #c0e8f0; } | ||
.preset-btn.active-preset { background: #3a6a7a; border-color: #c0e8f0; color: #fff; } | .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> | </style> | ||
</head> | </head> | ||
| 第128行: | 第133行: | ||
<div class="editor-layout"> | <div class="editor-layout"> | ||
<div class="form-panel" id="formPanel"> | <div class="form-panel" id="formPanel"> | ||
<div class="input-group"> | <div class="input-group"> | ||
<label>🖼️ 关卡大图</label> | <label>🖼️ 关卡大图</label> | ||
| 第138行: | 第142行: | ||
</div> | </div> | ||
<div class="input-group" style="background:#162228aa; border-radius:20px; padding:16px; border:1px solid #4a7088;"> | <div class="input-group" style="background:#162228aa; border-radius:20px; padding:16px; border:1px solid #4a7088;"> | ||
<label>🧟 僵尸卡片 <i>(出现在关卡大图下方)</i></label> | <label>🧟 僵尸卡片 <i>(出现在关卡大图下方)</i></label> | ||
| 第145行: | 第148行: | ||
</div> | </div> | ||
<div class="map-grid-container" id="mapGridEditor"> | <div class="map-grid-container" id="mapGridEditor"> | ||
<label style="margin-bottom:4px;">🗺️ 地图预放置 <i> | <label style="margin-bottom:4px;">🗺️ 地图预放置 <i>(点击格子编辑完整样式,右键清除植物)</i></label> | ||
<div class="color-palette"> | <div class="color-palette"> | ||
<label>🎨 背景色:<input type="color" id="gridBg" value="#1a6b0a" title="表格背景色"></label> | <label>🎨 背景色:<input type="color" id="gridBg" value="#1a6b0a" title="表格背景色"></label> | ||
<label>格子色:<input type="color" id="gridCell" value="#11ff00" title=" | <label>格子色:<input type="color" id="gridCell" value="#11ff00" title="默认格子背景色"></label> | ||
<label>边框色:<input type="color" id="gridBorder" value="#ffffff" title=" | <label>边框色:<input type="color" id="gridBorder" value="#d4e9cd" title="默认格子边框色"></label> | ||
<span style="color:#6080a0; font-size:0. | <label>表头色:<input type="color" id="headerColor" value="#ffffff" title="表头文字颜色"></label> | ||
<span style="color:#6080a0; font-size:0.65rem;">预设:</span> | |||
<div class="preset-colors"> | <div class="preset-colors"> | ||
<span class="preset-dot" data-bg="#1a6b0a" data-cell="#11ff00" data-border="#ffffff" style="background:#11ff00;" title="经典草地"></span> | <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" style="background:#3a3a3a;" 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" style="background:#2d5a3d;" title=" | <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" style="background:#6b4a2a;" title=" | <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" style="background:#2a2a5a;" title=" | <span class="preset-dot" data-bg="#1a1a3a" data-cell="#2a2a5a" data-border="#4a4a8a" data-header="#c0c0ff" style="background:#2a2a5a;" title="暗夜"></span> | ||
</div> | </div> | ||
</div> | </div> | ||
| 第173行: | 第175行: | ||
<button class="preset-btn" data-rows="3" data-cols="5">3×5</button> | <button class="preset-btn" data-rows="3" data-cols="5">3×5</button> | ||
</div> | </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="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 | <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 type="button" class="btn-icon" id="applySizeBtn">应用</button> | ||
<input type="text" id="currentPlantFile" value="豌豆射手.png" placeholder="植物文件" style="width:100px;"> | |||
<input type="text" id="currentPlantFile" value="豌豆射手.png" placeholder=" | <input type="text" id="currentPlantLink" value="豌豆射手" placeholder="链接" style="width:80px;"> | ||
<input type="text" id="currentPlantLink" value="豌豆射手" placeholder="链接" style="width: | |||
<button type="button" class="btn-icon" id="clearMapBtn">清空</button> | <button type="button" class="btn-icon" id="clearMapBtn">清空</button> | ||
</div> | </div> | ||
| 第184行: | 第185行: | ||
</div> | </div> | ||
<div class="infocard-sim"> | <div class="infocard-sim"> | ||
<div class="infocard-header"> | <div class="infocard-header"> | ||
| 第202行: | 第202行: | ||
<div class="section-divider"></div> | <div class="section-divider"></div> | ||
<div class="tab-bar"> | <div class="tab-bar"> | ||
<button class="tab-btn active" data-tab="init">初始化</button> | <button class="tab-btn active" data-tab="init">初始化</button> | ||
| 第211行: | 第210行: | ||
<div id="tabInit" class="tab-content"> | <div id="tabInit" class="tab-content"> | ||
<div class="input-group"> | <div class="input-group"> | ||
<label>📋 地图行为 - 初始化 | <label>📋 地图行为 - 初始化</label> | ||
<div id="initEventsContainer"></div> | <div id="initEventsContainer"></div> | ||
<button type="button" id="addInitEventBtn" class="add-event-btn">➕ 添加初始化事件</button> | <button type="button" id="addInitEventBtn" class="add-event-btn">➕ 添加初始化事件</button> | ||
| 第218行: | 第217行: | ||
<div id="tabPrepare" class="tab-content" style="display:none;"> | <div id="tabPrepare" class="tab-content" style="display:none;"> | ||
<div class="input-group"> | <div class="input-group"> | ||
<label>📋 地图行为 - 准备阶段 | <label>📋 地图行为 - 准备阶段</label> | ||
<div id="prepareEventsContainer"></div> | <div id="prepareEventsContainer"></div> | ||
<button type="button" id="addPrepareEventBtn" class="add-event-btn">➕ 添加准备阶段事件</button> | <button type="button" id="addPrepareEventBtn" class="add-event-btn">➕ 添加准备阶段事件</button> | ||
| 第225行: | 第224行: | ||
<div id="tabStart" class="tab-content" style="display:none;"> | <div id="tabStart" class="tab-content" style="display:none;"> | ||
<div class="input-group"> | <div class="input-group"> | ||
<label>📋 地图行为 - 开始后行为 | <label>📋 地图行为 - 开始后行为</label> | ||
<div id="startEventsContainer"></div> | <div id="startEventsContainer"></div> | ||
<button type="button" id="addStartEventBtn" class="add-event-btn">➕ 添加开始后事件</button> | <button type="button" id="addStartEventBtn" class="add-event-btn">➕ 添加开始后事件</button> | ||
| 第233行: | 第232行: | ||
<div class="section-divider"></div> | <div class="section-divider"></div> | ||
<div class="input-group"> | <div class="input-group"> | ||
<label>🎁 通关奖励 | <label>🎁 通关奖励</label> | ||
<div id="rewardsContainer"></div> | <div id="rewardsContainer"></div> | ||
<button type="button" id="addRewardBtn" class="add-event-btn">➕ 添加奖励</button> | <button type="button" id="addRewardBtn" class="add-event-btn">➕ 添加奖励</button> | ||
</div> | </div> | ||
<div class="input-group"> | <div class="input-group"> | ||
<label>🖼️ 困难模式图片 <i>(可选)</i></label> | <label>🖼️ 困难模式图片 <i>(可选)</i></label> | ||
| 第262行: | 第259行: | ||
</div> | </div> | ||
<div class="wiki-output" id="wikiOutput"></div> | <div class="wiki-output" id="wikiOutput"></div> | ||
<div class="note">⚡ | <div class="note">⚡ 实时生成。点击格子编辑完整样式(支持特殊边框如红线),右键清除植物。</div> | ||
</div> | </div> | ||
</div> | </div> | ||
| 第269行: | 第266行: | ||
<!-- 格子编辑弹窗 --> | <!-- 格子编辑弹窗 --> | ||
<div class="cell-popup" id="cellPopup"> | <div class="cell-popup" id="cellPopup"> | ||
<label style="color:#b0d8e8 | <label style="color:#b0d8e8;">🪴 植物</label> | ||
<input type="text" id="popupFile" placeholder="xxx.png"> | <input type="text" id="popupFile" placeholder="文件名 (xxx.png)"> | ||
<label style="color:#b0d8e8; font-size:0. | <input type="text" id="popupLink" placeholder="链接 (植物名)"> | ||
<div style="border-top:1px solid #3a5a6a; margin:8px 0; padding-top:6px;"> | |||
<div style="display:flex; gap:6px; margin-top: | <label style="color:#b0d8e8; font-size:0.75rem;">🎨 单元格样式 <i style="font-size:0.65rem;">(额外CSS,如 border-right:4px solid red)</i></label> | ||
<button type="button" class="btn-icon" id="popupClear"> | <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> | <button type="button" class="btn-icon" id="popupSave" style="background:#3a6a7a;">保存</button> | ||
</div> | </div> | ||
| 第327行: | 第338行: | ||
]; | ]; | ||
// | // 地图数据:每个格子 {file, link, cellBg, cellBorder, extraStyle} | ||
let mapRows = 5, mapCols = 9; | let mapRows = 5, mapCols = 9; | ||
let mapData = []; | let mapData = []; | ||
let gridBg = '#1a6b0a', gridCellColor = '#11ff00', gridBorderColor = '#ffffff'; | let gridBg = '#1a6b0a', gridCellColor = '#11ff00', gridBorderColor = '#d4e9cd', headerColor = '#ffffff'; | ||
function initMapData() { mapData = Array.from({length:mapRows}, ()=>Array.from({length:mapCols}, ()=>null)); } | function initMapData() { mapData = Array.from({length:mapRows}, ()=>Array.from({length:mapCols}, ()=>null)); } | ||
| 第360行: | 第371行: | ||
const gridCellInput = document.getElementById('gridCell'); | const gridCellInput = document.getElementById('gridCell'); | ||
const gridBorderInput = document.getElementById('gridBorder'); | const gridBorderInput = document.getElementById('gridBorder'); | ||
const headerColorInput = document.getElementById('headerColor'); | |||
let currentEditRow = -1, currentEditCol = -1; | let currentEditRow = -1, currentEditCol = -1; | ||
| 第403行: | 第415行: | ||
gridBg = gridBgInput.value || '#1a6b0a'; | gridBg = gridBgInput.value || '#1a6b0a'; | ||
gridCellColor = gridCellInput.value || '#11ff00'; | gridCellColor = gridCellInput.value || '#11ff00'; | ||
gridBorderColor = gridBorderInput.value || '# | gridBorderColor = gridBorderInput.value || '#d4e9cd'; | ||
headerColor = headerColorInput.value || '#ffffff'; | |||
renderMapGrid(); | renderMapGrid(); | ||
refreshPreview(); | refreshPreview(); | ||
} | } | ||
function applyColorPreset(bg, cell, border) { | function applyColorPreset(bg, cell, border, header) { | ||
gridBgInput.value = bg; | gridBgInput.value = bg; gridCellInput.value = cell; | ||
gridBorderInput.value = border; headerColorInput.value = header||'#ffffff'; | |||
gridBorderInput.value = border; | |||
updateGridColors(); | updateGridColors(); | ||
} | } | ||
| 第419行: | 第429行: | ||
mapRows = newRows; mapCols = newCols; | 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))); | 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; | mapRowsInput.value = mapRows; mapColsInput.value = mapCols; | ||
renderMapGrid(); refreshPreview(); | |||
renderMapGrid(); | |||
} | } | ||
function renderMapGrid() { | function renderMapGrid() { | ||
| 第428行: | 第436行: | ||
const headerRow = document.createElement('div'); headerRow.className='map-grid-row'; | const headerRow = document.createElement('div'); headerRow.className='map-grid-row'; | ||
headerRow.innerHTML = '<div class="map-grid-label"></div>'; | headerRow.innerHTML = '<div class="map-grid-label"></div>'; | ||
for(let c=1;c<=mapCols;c++) headerRow.innerHTML += `<div class="map-grid-label">列${c}</div>`; | for(let c=1;c<=mapCols;c++) headerRow.innerHTML += `<div class="map-grid-label" style="color:${headerColor}">列${c}</div>`; | ||
mapGrid.appendChild(headerRow); | mapGrid.appendChild(headerRow); | ||
for(let r=0;r<mapRows;r++) { | for(let r=0;r<mapRows;r++) { | ||
const row = document.createElement('div'); row.className='map-grid-row'; | const row = document.createElement('div'); row.className='map-grid-row'; | ||
row.innerHTML = `<div class="map-grid-label">行${r+1}</div>`; | row.innerHTML = `<div class="map-grid-label" style="color:${headerColor}">行${r+1}</div>`; | ||
for(let c=0;c<mapCols;c++) { | for(let c=0;c<mapCols;c++) { | ||
const d = mapData[r][c]; | |||
const cell = document.createElement('div'); | const cell = document.createElement('div'); | ||
cell.className = 'map-grid-cell'; | cell.className = 'map-grid-cell'; | ||
cell.style.background = | const cbg = d?.cellBg || gridCellColor; | ||
cell.style. | const cbd = d?.cellBorder || gridBorderColor; | ||
if( | 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('click',()=>openCellPopup(r,c)); | ||
cell.addEventListener('contextmenu',(e)=>{ e.preventDefault(); mapData[r][c]=null; renderMapGrid(); refreshPreview(); }); | cell.addEventListener('contextmenu',(e)=>{ e.preventDefault(); mapData[r][c]=null; renderMapGrid(); refreshPreview(); }); | ||
| 第449行: | 第464行: | ||
currentEditRow = r; currentEditCol = c; | currentEditRow = r; currentEditCol = c; | ||
const d = mapData[r][c]; | const d = mapData[r][c]; | ||
document.getElementById('popupFile').value = d ? | document.getElementById('popupFile').value = d?.file || ''; | ||
document.getElementById(' | 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'; | cellPopup.style.display = 'block'; | ||
const cells = mapGrid.querySelectorAll('.map-grid-cell'); | const cells = mapGrid.querySelectorAll('.map-grid-cell'); | ||
| 第456行: | 第474行: | ||
if(cells[idx]) { | if(cells[idx]) { | ||
const rect = cells[idx].getBoundingClientRect(); | const rect = cells[idx].getBoundingClientRect(); | ||
cellPopup.style.left = Math.min(rect.right+8, window.innerWidth- | cellPopup.style.left = Math.min(rect.right+8, window.innerWidth-290)+'px'; | ||
cellPopup.style.top = Math.min(rect.top, window.innerHeight- | cellPopup.style.top = Math.min(rect.top, window.innerHeight-380)+'px'; | ||
} | } | ||
} | } | ||
| 第464行: | 第482行: | ||
const file = document.getElementById('popupFile').value.trim(); | const file = document.getElementById('popupFile').value.trim(); | ||
const link = document.getElementById('popupLink').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) { | if(file) { | ||
mapData[currentEditRow][currentEditCol] = {file, link: link||file.replace('.png','')}; | 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 { | } else { | ||
mapData[currentEditRow][currentEditCol] = null; | mapData[currentEditRow][currentEditCol] = null; | ||
} | } | ||
cellPopup.style.display = 'none'; | cellPopup.style.display = 'none'; | ||
renderMapGrid(); | renderMapGrid(); refreshPreview(); | ||
} | } | ||
function clearMap() { initMapData(); renderMapGrid(); refreshPreview(); } | function clearMap() { initMapData(); renderMapGrid(); refreshPreview(); } | ||
function buildMapTable() { | 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! !!`; | 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 += ` 列${c} !!`; | for(let c=1;c<=mapCols;c++) table += ` <font color=${headerColor}>列${c}</font> !!`; | ||
table = table.slice(0,-2) + '\n'; | table = table.slice(0,-2) + '\n'; | ||
for(let r=0;r<mapRows;r++) { | for(let r=0;r<mapRows;r++) { | ||
table += `|-\n! 行${r+1}\n`; | table += `|-\n! <font color=${headerColor}>行${r+1}</font>\n`; | ||
for(let c=0;c<mapCols;c++) { | for(let c=0;c<mapCols;c++) { | ||
const d = mapData[r][c]; | const d = mapData[r][c]; | ||
const content = d ? `[[文件:${d.file}|50px|link=${d.link}]]` : ''; | const cbg = d?.cellBg || gridCellColor; | ||
table += `| style=" | 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`; | |||
} | } | ||
} | } | ||
| 第495行: | 第518行: | ||
const tableMatch = src.match(/== 地图预放置 ==\s*\n\{\|[\s\S]*?\n\|\}/); | const tableMatch = src.match(/== 地图预放置 ==\s*\n\{\|[\s\S]*?\n\|\}/); | ||
if(!tableMatch) { initMapData(); renderMapGrid(); return; } | if(!tableMatch) { initMapData(); renderMapGrid(); return; } | ||
const bgMatch = tableMatch[0].match(/background:([#\w]+)/); | const bgMatch = tableMatch[0].match(/background:([#\w]+)/); | ||
if(bgMatch) { gridBg = bgMatch[1]; gridBgInput.value = gridBg; } | 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\|-/); | const headerMatch = tableMatch[0].match(/! !!([\s\S]*?)\n\|-/); | ||
let cols = 9; | let cols = 9; | ||
if(headerMatch) { | if(headerMatch) { const colHeaders = headerMatch[1].match(/列\d+/g); if(colHeaders) cols = colHeaders.length; } | ||
const rows = tableMatch[0].split('\n|-\n'); | const rows = tableMatch[0].split('\n|-\n'); | ||
const rowMatches = rows.filter(r=>/ | const rowMatches = rows.filter(r=>/<font color=[^>]*>行\d/.test(r)); | ||
const newRows = rowMatches.length; | const newRows = rowMatches.length; | ||
if(newRows>0 && cols>0) { | if(newRows>0 && cols>0) { | ||
| 第513行: | 第533行: | ||
initMapData(); | initMapData(); | ||
rowMatches.forEach(row => { | rowMatches.forEach(row => { | ||
const rm = row.match(/ | const rm = row.match(/<font color=[^>]*>行(\d+)<\/font>/); | ||
if(!rm) return; | if(!rm) return; | ||
const rowIdx = parseInt(rm[1])-1; | const rowIdx = parseInt(rm[1])-1; | ||
| 第520行: | 第540行: | ||
cells.forEach((m,ci) => { | cells.forEach((m,ci) => { | ||
if(ci>=mapCols) return; | if(ci>=mapCols) return; | ||
const styleStr = m[1]; | const styleStr = m[1]; | ||
const cbg = styleStr.match(/background:([#\w]+)/); | const cbg = styleStr.match(/background:([#\w]+)/); | ||
const cbd = styleStr.match(/border:[^;] | 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(cbg) gridCellColor = cbg[1]; | ||
if(cbd) gridBorderColor = cbd[1]; | if(cbd) gridBorderColor = cbd[1]; | ||
const content = m[2].trim(); | const content = m[2].trim(); | ||
const fm = content.match(/\[\[文件:([^|]+)\|[^|]+\|link=([^\]]+)\]\]/); | const fm = content.match(/\[\[文件:([^|]+)\|[^|]+\|link=([^\]]+)\]\]/); | ||
if(fm) mapData[rowIdx][ci] = {file:fm[1], link:fm[2]}; | 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 | |||
}; | |||
} | |||
}); | }); | ||
}); | }); | ||
| 第537行: | 第567行: | ||
} | } | ||
// ========== | // ========== 卡片/事件/奖励 ========== | ||
function createCardItem(d) { | function createCardItem(d) { | ||
d = d || { image:'', name:'', health:'', speed:'', type:'', version:'', color:'linear-gradient(135deg, #E8E0F0, #D1C4E9)', border:'#7E57C2' }; | 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'; | const div = document.createElement('div'); div.className='card-item'; | ||
div.innerHTML = ` | 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); | cardsContainer.appendChild(div); | ||
div.querySelector('.card-remove').addEventListener('click',()=>{ if(cardsContainer.children.length>1){ div.remove(); refreshPreview(); } else alert('至少保留一个卡片'); }); | div.querySelector('.card-remove').addEventListener('click',()=>{ if(cardsContainer.children.length>1){ div.remove(); refreshPreview(); } else alert('至少保留一个卡片'); }); | ||
| 第563行: | 第580行: | ||
cardsContainer.querySelectorAll('.card-item').forEach(item => { | cardsContainer.querySelectorAll('.card-item').forEach(item => { | ||
cards.push({ | cards.push({ | ||
image: item.querySelector('.card-image')?.value.trim()||'', | 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()||'', | |||
health: item.querySelector('.card-health')?.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()||'' | |||
type: item.querySelector('.card-type')?.value.trim()||'', | |||
color: item.querySelector('.card-color')?.value.trim()||'', | |||
}); | }); | ||
}); | }); | ||
return cards; | return cards; | ||
} | } | ||
function collectEvents(container, cls) { | function collectEvents(container, cls) { | ||
const e = []; | const e = []; | ||
| 第588行: | 第599行: | ||
const rb = document.createElement('button'); rb.type='button'; rb.className='event-remove-btn'; rb.textContent='✕'; | 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('至少保留一项'); }); | rb.addEventListener('click', ()=>{ if(container.children.length>1){ div.remove(); refreshPreview(); } else alert('至少保留一项'); }); | ||
div.append(inp, rb); | div.append(inp, rb); container.appendChild(div); | ||
} | } | ||
function collectRewards() { | function collectRewards() { | ||
const r = []; | const r = []; | ||
| 第608行: | 第616行: | ||
const rb = document.createElement('button'); rb.type='button'; rb.className='event-remove-btn reward-remove'; rb.textContent='✕'; | 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('至少保留一项奖励'); }); | rb.addEventListener('click', ()=>{ if(rewardsContainer.children.length>1){ div.remove(); refreshPreview(); } else alert('至少保留一项奖励'); }); | ||
div.append(fi, li, rb); | div.append(fi, li, rb); rewardsContainer.appendChild(div); | ||
} | } | ||
function clearContainer(c) { while(c.children.length>0) c.removeChild(c.firstChild); } | function clearContainer(c) { while(c.children.length>0) c.removeChild(c.firstChild); } | ||
// ========== | // ========== 构建Wiki ========== | ||
function buildInfocard() { | function buildInfocard() { | ||
const img = infocardImage.value.trim() || '豌豆进化.png'; | const img = infocardImage.value.trim() || '豌豆进化.png'; | ||
| 第619行: | 第626行: | ||
const attrs = collectAttrs(); | const attrs = collectAttrs(); | ||
const preplaced = []; | const preplaced = []; | ||
for(let r=0;r<mapRows;r++) for(let c=0;c<mapCols;c++) if(mapData[r][c]) preplaced.push(`${mapData[r][c].link}(位置:第${r+1}行第${c+1}列)`); | 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==='地图预放置')) | 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 => { | const parts = attrs.map(a => { | ||
let v = a.value; | let v = a.value; if(a.bold) v = `'''${v}'''`; if(a.color) v = `<font color=${a.color}>${v}</font>`; | ||
return `${a.name}:${v}`; | return `${a.name}:${v}`; | ||
}); | }); | ||
| 第634行: | 第637行: | ||
const cards = collectCards(); | const cards = collectCards(); | ||
if (cards.length === 0) return ''; | if (cards.length === 0) return ''; | ||
const lines = cards.map(c => | 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>`; | return `<div style="float:left" class="pvzhe-card-container">\n${lines.join('\n')}\n</div>`; | ||
} | } | ||
function buildWikiText() { | function buildWikiText() { | ||
const infocard = buildInfocard(); | const infocard = buildInfocard(); | ||
const mf = mapImageFile.value.trim() || '射击模式.png' | 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 cardsBlock = buildCardsBlock() | |||
const initE = collectEvents(initEventsContainer, 'init-event') | |||
const rewards = collectRewards(); | const rewards = collectRewards(); | ||
const hf = hardModeFile.value.trim(), hw = hardModeWidth.value.trim()||'250px', hl = hardModeLink.value.trim(); | const hf=hardModeFile.value.trim(), hw=hardModeWidth.value.trim()||'250px', hl=hardModeLink.value.trim(); | ||
let es = ''; | let es=''; | ||
if(initE.length>0 || prepE.length>0 || startE.length>0) { | 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}`; | |||
let rs = ''; | |||
let headerBlock = `{{返回关卡列表}} | |||
<div style="float:right; margin-left:0px; width:100%; max-width:650px; overflow:hidden; display:flex; align-items:center;"> | |||
<div style="float:right;width:100%;"> | |||
<div style="display: flex; justify-content: flex-end;"> | |||
[[File:${mf}|${mw}|link=${ml}]] | |||
</div>`; | |||
if (cardsBlock) headerBlock += | |||
if (mapTable) headerBlock += | |||
headerBlock += | |||
return `${headerBlock} | |||
${infocard} | |||
${es}${rs}`; | |||
} | } | ||
function refreshPreview(){const t=buildWikiText();wikiOutputDiv.innerHTML=t.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>');} | |||
function applyParsedData(d) { | // ========== 导入 ========== | ||
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 importFromSource(src){ | |||
function bindEvents() { | function bindEvents(){ | ||
document.getElementById('addAttrBtn').addEventListener('click',()=>{ attrContainer.appendChild(createAttrItem()); refreshPreview(); }); | document.getElementById('addAttrBtn').addEventListener('click',()=>{attrContainer.appendChild(createAttrItem());refreshPreview();}); | ||
document.getElementById('addCardBtn').addEventListener('click',()=>{ createCardItem(); refreshPreview(); }); | document.getElementById('addCardBtn').addEventListener('click',()=>{createCardItem();refreshPreview();}); | ||
document.getElementById('addInitEventBtn').addEventListener('click',()=>{ createEventItem(initEventsContainer,'init-event',''); refreshPreview(); }); | document.getElementById('addInitEventBtn').addEventListener('click',()=>{createEventItem(initEventsContainer,'init-event','');refreshPreview();}); | ||
document.getElementById('addPrepareEventBtn').addEventListener('click',()=>{ createEventItem(prepareEventsContainer,'prepare-event',''); refreshPreview(); }); | document.getElementById('addPrepareEventBtn').addEventListener('click',()=>{createEventItem(prepareEventsContainer,'prepare-event','');refreshPreview();}); | ||
document.getElementById('addStartEventBtn').addEventListener('click',()=>{ createEventItem(startEventsContainer,'start-event',''); refreshPreview(); }); | document.getElementById('addStartEventBtn').addEventListener('click',()=>{createEventItem(startEventsContainer,'start-event','');refreshPreview();}); | ||
document.getElementById('addRewardBtn').addEventListener('click',()=>{ createRewardItem('',''); refreshPreview(); }); | document.getElementById('addRewardBtn').addEventListener('click',()=>{createRewardItem('','');refreshPreview();}); | ||
infocardImage.addEventListener('input',refreshPreview); | infocardImage.addEventListener('input',refreshPreview);infocardImageSize.addEventListener('input',refreshPreview); | ||
[mapImageFile,mapImageWidth,mapImageLink,hardModeFile,hardModeWidth,hardModeLink].forEach(i=>i.addEventListener('input',refreshPreview)); | [mapImageFile,mapImageWidth,mapImageLink,hardModeFile,hardModeWidth,hardModeLink].forEach(i=>i.addEventListener('input',refreshPreview)); | ||
document.getElementById('generateBtn').addEventListener('click',refreshPreview); | document.getElementById('generateBtn').addEventListener('click',refreshPreview); | ||
document.getElementById('copyBtn').addEventListener('click',async()=>{ try{ await navigator.clipboard.writeText(buildWikiText()); alert('✅ 已复制'); } catch{ alert('❌ 复制失败'); } }); | 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(); }); | 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('importBtn').addEventListener('click',()=>{importTextarea.value='';importModal.style.display='flex';}); | ||
document.getElementById('cancelImportBtn').addEventListener('click',()=>importModal.style.display='none'); | 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('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())); | document.getElementById('pasteToImportBtn').addEventListener('click',()=>importFromSource(buildWikiText())); | ||
importModal.addEventListener('click',e=>{ if(e.target===importModal) importModal.style.display='none'; }); | 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.getElementById('applySizeBtn').addEventListener('click',()=>{ | 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.querySelectorAll('.preset-btn').forEach(btn=>{ | |||
gridBgInput.addEventListener('input', updateGridColors); | |||
document.querySelectorAll('.preset-dot').forEach(dot=>{ | |||
document.getElementById('clearMapBtn').addEventListener('click',clearMap); | document.getElementById('clearMapBtn').addEventListener('click',clearMap); | ||
document.getElementById('popupSave').addEventListener('click',saveCellPopup); | document.getElementById('popupSave').addEventListener('click',saveCellPopup); | ||
document.getElementById('popupClear').addEventListener('click',()=>{ | 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';});}); | ||
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=>{ | |||
} | } | ||
function init(){ | function init(){ | ||
setAttrItems(DEFAULT_ATTRS); | setAttrItems(DEFAULT_ATTRS);clearContainer(cardsContainer);DEFAULT_CARDS.forEach(c=>createCardItem(c)); | ||
clearContainer(initEventsContainer);createEventItem(initEventsContainer,'init-event','无'); | |||
clearContainer(prepareEventsContainer);createEventItem(prepareEventsContainer,'prepare-event','无'); | |||
clearContainer(initEventsContainer); | clearContainer(startEventsContainer);createEventItem(startEventsContainer,'start-event','无'); | ||
clearContainer(rewardsContainer);createRewardItem('钱袋子.png','钱袋子'); | |||
clearContainer(prepareEventsContainer); | initMapData();mapData[2][0]={file:'豌豆射手.png',link:'豌豆射手',cellBg:gridCellColor,cellBorder:gridBorderColor,extraStyle:''}; | ||
renderMapGrid();bindEvents();refreshPreview(); | |||
clearContainer(startEventsContainer); | |||
clearContainer(rewardsContainer); | |||
initMapData(); | |||
renderMapGrid(); | |||
} | } | ||
init(); | init(); | ||