phpwork/a.html
2025-02-27 01:15:56 -08:00

922 lines
34 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<title>多词条 Affix Editor支持导入、编辑多个</title>
<style>
body {
font-family: sans-serif;
margin: 20px;
background: #f9f9f9;
}
h1 {
margin-bottom: 10px;
}
fieldset {
margin: 10px 0;
border: 1px solid #ccc;
padding: 10px;
}
legend {
font-weight: bold;
}
label {
margin-right: 10px;
}
.affix-card {
border: 2px dashed #aaa;
padding: 10px;
margin-bottom: 20px;
background: #fff;
}
.condition, .modifier {
padding: 6px;
margin: 5px 0;
border: 1px dashed #aaa;
background: #fafafa;
}
.button-container {
margin-top: 10px;
}
.affix-header {
font-weight: bold;
margin-bottom: 5px;
background: #eee;
padding: 5px;
}
.affix-body {
padding: 5px 10px;
}
#import-json-container {
margin: 10px 0;
}
#import-json-area {
width: 100%;
height: 100px;
}
#output {
white-space: pre-wrap;
background: #f0f0f0;
padding: 10px;
border: 1px solid #ccc;
min-height: 150px;
max-height: 300px;
overflow: auto;
}
button {
cursor: pointer;
}
</style>
</head>
<body>
<h1>多词条 Affix Editor</h1>
<!-- ======= 导入 JSON ======= -->
<fieldset id="import-json-container">
<legend>导入已有JSON</legend>
<p>在下方文本框粘贴已有的多Affix JSON然后点击“导入”</p>
<textarea id="import-json-area" placeholder></textarea>
<br />
<button type="button" onclick="importAffixes()">导入</button>
</fieldset>
<!-- ======= Affix 列表区域 ======= -->
<div id="affixes-list"></div>
<!-- ======= 新增 Affix、导出 JSON ======= -->
<div class="button-container">
<button type="button" onclick="createAffixCard()">+ 新建词条</button>
<button type="button" onclick="generateAllJSON()">生成 JSON</button>
</div>
<pre id="output"></pre>
<script>
/****************************************************************
* 1. 准备:变量、列表、工具函数
****************************************************************/
// 1.1 基础表达式变量
const baseVarList = [
{ value: 'level', label: '#level' },
{ value: 'health', label: '#health' },
{ value: 'time', label: '#time' },
{ value: 'food', label: '#food' },
{ value: 'light', label: '#light' },
{ value: 'oxygen', label: '#oxygen' },
{ value: 'armor_value', label: '#armor_value' },
{ value: 'dimension', label: '#dimension' },
{ value: 'rain', label: '#rain' },
{ value: 'hand_item', label: '#hand_item' }
];
// 1.2 EntityType -> kill_... 变量
const entityTypeNames = [
"ITEM","EXPERIENCE_ORB","AREA_EFFECT_CLOUD","ELDER_GUARDIAN","WITHER_SKELETON","STRAY","EGG",
"LEASH_KNOT","PAINTING","ARROW","SNOWBALL","FIREBALL","SMALL_FIREBALL","ENDER_PEARL","EYE_OF_ENDER",
"POTION","EXPERIENCE_BOTTLE","ITEM_FRAME","WITHER_SKULL","TNT","FALLING_BLOCK","FIREWORK_ROCKET",
"HUSK","SPECTRAL_ARROW","SHULKER_BULLET","DRAGON_FIREBALL","ZOMBIE_VILLAGER","SKELETON_HORSE",
"ZOMBIE_HORSE","ARMOR_STAND","DONKEY","MULE","EVOKER_FANGS","EVOKER","VEX","VINDICATOR","ILLUSIONER",
"COMMAND_BLOCK_MINECART","MINECART","CHEST_MINECART","FURNACE_MINECART","TNT_MINECART","HOPPER_MINECART",
"SPAWNER_MINECART","CREEPER","SKELETON","SPIDER","GIANT","ZOMBIE","SLIME","GHAST","ZOMBIFIED_PIGLIN",
"ENDERMAN","CAVE_SPIDER","SILVERFISH","BLAZE","MAGMA_CUBE","ENDER_DRAGON","WITHER","BAT","WITCH",
"ENDERMITE","GUARDIAN","SHULKER","PIG","SHEEP","COW","CHICKEN","SQUID","WOLF","MOOSHROOM","SNOW_GOLEM",
"OCELOT","IRON_GOLEM","HORSE","RABBIT","POLAR_BEAR","LLAMA","LLAMA_SPIT","PARROT","VILLAGER","END_CRYSTAL",
"TURTLE","PHANTOM","TRIDENT","COD","SALMON","PUFFERFISH","TROPICAL_FISH","DROWNED","DOLPHIN","CAT","PANDA",
"PILLAGER","RAVAGER","TRADER_LLAMA","WANDERING_TRADER","FOX","BEE","HOGLIN","PIGLIN","STRIDER","ZOGLIN",
"PIGLIN_BRUTE","AXOLOTL","GLOW_ITEM_FRAME","GLOW_SQUID","GOAT","MARKER","ALLAY","FROG","TADPOLE","WARDEN",
"CAMEL","BLOCK_DISPLAY","INTERACTION","ITEM_DISPLAY","SNIFFER","TEXT_DISPLAY","BREEZE","WIND_CHARGE",
"BREEZE_WIND_CHARGE","ARMADILLO","BOGGED","OMINOUS_ITEM_SPAWNER","ACACIA_BOAT","ACACIA_CHEST_BOAT",
"BAMBOO_RAFT","BAMBOO_CHEST_RAFT","BIRCH_BOAT","BIRCH_CHEST_BOAT","CHERRY_BOAT","CHERRY_CHEST_BOAT",
"DARK_OAK_BOAT","DARK_OAK_CHEST_BOAT","JUNGLE_BOAT","JUNGLE_CHEST_BOAT","MANGROVE_BOAT","MANGROVE_CHEST_BOAT",
"OAK_BOAT","OAK_CHEST_BOAT","PALE_OAK_BOAT","PALE_OAK_CHEST_BOAT","SPRUCE_BOAT","SPRUCE_CHEST_BOAT",
"CREAKING","FISHING_BOBBER","LIGHTNING_BOLT","PLAYER"
];
const killVarList = entityTypeNames.map(et => {
return { value: 'kill_'+et.toLowerCase(), label: '#kill_'+et.toLowerCase() };
});
const expressionVarList = [...baseVarList, ...killVarList];
// 1.3 Attributes
const attributeList = [
{ value: "max_health", label: "MAX_HEALTH" },
{ value: "follow_range", label: "FOLLOW_RANGE" },
{ value: "knockback_resistance", label: "KNOCKBACK_RESISTANCE" },
{ value: "movement_speed", label: "MOVEMENT_SPEED" },
{ value: "flying_speed", label: "FLYING_SPEED" },
{ value: "attack_damage", label: "ATTACK_DAMAGE" },
{ value: "attack_knockback", label: "ATTACK_KNOCKBACK" },
{ value: "attack_speed", label: "ATTACK_SPEED" },
{ value: "armor", label: "ARMOR" },
{ value: "armor_toughness", label: "ARMOR_TOUGHNESS" },
{ value: "fall_damage_multiplier", label: "FALL_DAMAGE_MULTIPLIER" },
{ value: "luck", label: "LUCK" },
{ value: "max_absorption", label: "MAX_ABSORPTION" },
{ value: "safe_fall_distance", label: "SAFE_FALL_DISTANCE" },
{ value: "scale", label: "SCALE" },
{ value: "step_height", label: "STEP_HEIGHT" },
{ value: "gravity", label: "GRAVITY" },
{ value: "jump_strength", label: "JUMP_STRENGTH" },
{ value: "burning_time", label: "BURNING_TIME" },
{ value: "explosion_knockback_resistance", label: "EXPLOSION_KNOCKBACK_RESISTANCE" },
{ value: "movement_efficiency", label: "MOVEMENT_EFFICIENCY" },
{ value: "oxygen_bonus", label: "OXYGEN_BONUS" },
{ value: "water_movement_efficiency", label: "WATER_MOVEMENT_EFFICIENCY" },
{ value: "tempt_range", label: "TEMPT_RANGE" },
{ value: "block_interaction_range", label: "BLOCK_INTERACTION_RANGE" },
{ value: "entity_interaction_range", label: "ENTITY_INTERACTION_RANGE" },
{ value: "block_break_speed", label: "BLOCK_BREAK_SPEED" },
{ value: "mining_efficiency", label: "MINING_EFFICIENCY" },
{ value: "sneaking_speed", label: "SNEAKING_SPEED" },
{ value: "submerged_mining_speed", label: "SUBMERGED_MINING_SPEED" },
{ value: "sweeping_damage_ratio", label: "SWEEPING_DAMAGE_RATIO" },
{ value: "spawn_reinforcements", label: "SPAWN_REINFORCEMENTS" }
];
// 1.4 PotionEffects
const potionEffectList = [
{ value: "speed", label: "SPEED" },
{ value: "slowness", label: "SLOWNESS" },
{ value: "haste", label: "HASTE" },
{ value: "mining_fatigue", label: "MINING_FATIGUE" },
{ value: "strength", label: "STRENGTH" },
{ value: "instant_health", label: "INSTANT_HEALTH" },
{ value: "instant_damage", label: "INSTANT_DAMAGE" },
{ value: "jump_boost", label: "JUMP_BOOST" },
{ value: "nausea", label: "NAUSEA" },
{ value: "regeneration", label: "REGENERATION" },
{ value: "resistance", label: "RESISTANCE" },
{ value: "fire_resistance", label: "FIRE_RESISTANCE" },
{ value: "water_breathing", label: "WATER_BREATHING" },
{ value: "invisibility", label: "INVISIBILITY" },
{ value: "blindness", label: "BLINDNESS" },
{ value: "night_vision", label: "NIGHT_VISION" },
{ value: "hunger", label: "HUNGER" },
{ value: "weakness", label: "WEAKNESS" },
{ value: "poison", label: "POISON" },
{ value: "wither", label: "WITHER" },
{ value: "health_boost", label: "HEALTH_BOOST" },
{ value: "absorption", label: "ABSORPTION" },
{ value: "saturation", label: "SATURATION" },
{ value: "glowing", label: "GLOWING" },
{ value: "levitation", label: "LEVITATION" },
{ value: "luck", label: "LUCK" },
{ value: "unluck", label: "UNLUCK" },
{ value: "slow_falling", label: "SLOW_FALLING" },
{ value: "conduit_power", label: "CONDUIT_POWER" },
{ value: "dolphins_grace", label: "DOLPHINS_GRACE" },
{ value: "bad_omen", label: "BAD_OMEN" },
{ value: "hero_of_the_village", label: "HERO_OF_THE_VILLAGE" },
{ value: "darkness", label: "DARKNESS" },
{ value: "trial_omen", label: "TRIAL_OMEN" },
{ value: "raid_omen", label: "RAID_OMEN" },
{ value: "wind_charged", label: "WIND_CHARGED" },
{ value: "weaving", label: "WEAVING" },
{ value: "oozing", label: "OOZING" },
{ value: "infested", label: "INFESTED" }
];
// 1.5 Biome
const biomeList = [
{ value: "ocean", label: "OCEAN" },
{ value: "plains", label: "PLAINS" },
{ value: "desert", label: "DESERT" },
{ value: "windswept_hills", label: "WINDSWEPT_HILLS" },
{ value: "forest", label: "FOREST" },
{ value: "taiga", label: "TAIGA" },
{ value: "swamp", label: "SWAMP" },
{ value: "mangrove_swamp", label: "MANGROVE_SWAMP" },
{ value: "river", label: "RIVER" },
{ value: "nether_wastes", label: "NETHER_WASTES" },
{ value: "the_end", label: "THE_END" },
{ value: "frozen_ocean", label: "FROZEN_OCEAN" },
{ value: "frozen_river", label: "FROZEN_RIVER" },
{ value: "snowy_plains", label: "SNOWY_PLAINS" },
{ value: "mushroom_fields", label: "MUSHROOM_FIELDS" },
{ value: "beach", label: "BEACH" },
{ value: "jungle", label: "JUNGLE" },
{ value: "sparse_jungle", label: "SPARSE_JUNGLE" },
{ value: "deep_ocean", label: "DEEP_OCEAN" },
{ value: "stony_shore", label: "STONY_SHORE" },
{ value: "snowy_beach", label: "SNOWY_BEACH" },
{ value: "birch_forest", label: "BIRCH_FOREST" },
{ value: "dark_forest", label: "DARK_FOREST" },
{ value: "pale_garden", label: "PALE_GARDEN" },
{ value: "snowy_taiga", label: "SNOWY_TAIGA" },
{ value: "old_growth_pine_taiga", label: "OLD_GROWTH_PINE_TAIGA" },
{ value: "windswept_forest", label: "WINDSWEPT_FOREST" },
{ value: "savanna", label: "SAVANNA" },
{ value: "savanna_plateau", label: "SAVANNA_PLATEAU" },
{ value: "badlands", label: "BADLANDS" },
{ value: "wooded_badlands", label: "WOODED_BADLANDS" },
{ value: "small_end_islands", label: "SMALL_END_ISLANDS" },
{ value: "end_midlands", label: "END_MIDLANDS" },
{ value: "end_highlands", label: "END_HIGHLANDS" },
{ value: "end_barrens", label: "END_BARRENS" },
{ value: "warm_ocean", label: "WARM_OCEAN" },
{ value: "lukewarm_ocean", label: "LUKEWARM_OCEAN" },
{ value: "cold_ocean", label: "COLD_OCEAN" },
{ value: "deep_lukewarm_ocean", label: "DEEP_LUKEWARM_OCEAN" },
{ value: "deep_cold_ocean", label: "DEEP_COLD_OCEAN" },
{ value: "deep_frozen_ocean", label: "DEEP_FROZEN_OCEAN" },
{ value: "the_void", label: "THE_VOID" },
{ value: "sunflower_plains", label: "SUNFLOWER_PLAINS" },
{ value: "windswept_gravelly_hills", label: "WINDSWEPT_GRAVELLY_HILLS" },
{ value: "flower_forest", label: "FLOWER_FOREST" },
{ value: "ice_spikes", label: "ICE_SPIKES" },
{ value: "old_growth_birch_forest", label: "OLD_GROWTH_BIRCH_FOREST" },
{ value: "old_growth_spruce_taiga", label: "OLD_GROWTH_SPRUCE_TAIGA" },
{ value: "windswept_savanna", label: "WINDSWEPT_SAVANNA" },
{ value: "eroded_badlands", label: "ERODED_BADLANDS" },
{ value: "bamboo_jungle", label: "BAMBOO_JUNGLE" },
{ value: "soul_sand_valley", label: "SOUL_SAND_VALLEY" },
{ value: "crimson_forest", label: "CRIMSON_FOREST" },
{ value: "warped_forest", label: "WARPED_FOREST" },
{ value: "basalt_deltas", label: "BASALT_DELTAS" },
{ value: "dripstone_caves", label: "DRIPSTONE_CAVES" },
{ value: "lush_caves", label: "LUSH_CAVES" },
{ value: "deep_dark", label: "DEEP_DARK" },
{ value: "meadow", label: "MEADOW" },
{ value: "grove", label: "GROVE" },
{ value: "snowy_slopes", label: "SNOWY_SLOPES" },
{ value: "frozen_peaks", label: "FROZEN_PEAKS" },
{ value: "jagged_peaks", label: "JAGGED_PEAKS" },
{ value: "stony_peaks", label: "STONY_PEAKS" },
{ value: "cherry_grove", label: "CHERRY_GROVE" }
];
// 1.6 一个工具函数:从 JSON 中安全地构造 object
function safeParseJSON(text) {
try {
return JSON.parse(text);
} catch(e) {
return null;
}
}
/****************************************************************
* 2. 逻辑:创建 Affix 面板 / 导入 / 输出
****************************************************************/
// ========== 导入 JSON ==========
function importAffixes() {
const text = document.getElementById('import-json-area').value.trim();
if (!text) return;
const obj = safeParseJSON(text);
if (!obj) {
alert("JSON 解析失败,请检查格式!");
return;
}
// obj 形如 { "affixId": {...}, "another_affix": {...} }
// 遍历每个 key 并创建相应的面板
for (const [affixId, affixData] of Object.entries(obj)) {
createAffixCard(affixData);
}
alert("已导入 " + Object.keys(obj).length + " 个词条。");
}
// ========== 创建新的词条面板 ==========
function createAffixCard(importData = null) {
// importData 若非空,则使用其值初始化;否则创建空白
const affixListContainer = document.getElementById('affixes-list');
// 整个 affix-card
const card = document.createElement('div');
card.className = 'affix-card';
// Header
const header = document.createElement('div');
header.className = 'affix-header';
header.textContent = '词条信息';
card.appendChild(header);
// Body
const body = document.createElement('div');
body.className = 'affix-body';
card.appendChild(body);
// 基础信息
const fieldsetBase = document.createElement('fieldset');
const legendBase = document.createElement('legend');
legendBase.textContent = '基础信息';
fieldsetBase.appendChild(legendBase);
// ID
const p1 = document.createElement('p');
const labelId = document.createElement('label');
labelId.textContent = '词条ID: ';
const inputId = document.createElement('input');
inputId.type = 'text';
inputId.placeholder = '如: berserker_mode';
labelId.appendChild(inputId);
// Quality
const labelQuality = document.createElement('label');
labelQuality.textContent = ' 品质: ';
const selectQuality = document.createElement('select');
selectQuality.innerHTML = `
<option value="COMMON">COMMON</option>
<option value="RARE">RARE</option>
<option value="EPIC">EPIC</option>
<option value="LEGENDARY">LEGENDARY</option>
`;
labelQuality.appendChild(selectQuality);
// Type
const labelType = document.createElement('label');
labelType.textContent = ' 类型: ';
const selectType = document.createElement('select');
selectType.innerHTML = `
<option value="POSITIVE">POSITIVE</option>
<option value="NEGATIVE">NEGATIVE</option>
`;
labelType.appendChild(selectType);
p1.appendChild(labelId);
p1.appendChild(labelQuality);
p1.appendChild(labelType);
fieldsetBase.appendChild(p1);
// desc
const p2 = document.createElement('p');
const labelDesc = document.createElement('label');
labelDesc.textContent = '描述: ';
const textDesc = document.createElement('textarea');
textDesc.rows = 2;
textDesc.cols = 50;
textDesc.placeholder = '该词条的简短描述...';
labelDesc.appendChild(textDesc);
p2.appendChild(labelDesc);
fieldsetBase.appendChild(p2);
// interval & logic
const p3 = document.createElement('p');
const labelInt = document.createElement('label');
labelInt.textContent = '检测间隔(秒): ';
const inputInt = document.createElement('input');
inputInt.type = 'number';
inputInt.value = 5;
labelInt.appendChild(inputInt);
const labelLogic = document.createElement('label');
labelLogic.textContent = ' 条件逻辑: ';
const selectLogic = document.createElement('select');
selectLogic.innerHTML = `
<option value="AND">AND</option>
<option value="OR">OR</option>
`;
labelLogic.appendChild(selectLogic);
p3.appendChild(labelInt);
p3.appendChild(labelLogic);
fieldsetBase.appendChild(p3);
body.appendChild(fieldsetBase);
// 条件
const fieldsetCond = document.createElement('fieldset');
const legendCond = document.createElement('legend');
legendCond.textContent = '条件Conditions';
fieldsetCond.appendChild(legendCond);
const condContainer = document.createElement('div');
fieldsetCond.appendChild(condContainer);
const condBtnDiv = document.createElement('div');
condBtnDiv.className = 'button-container';
const condBtn = document.createElement('button');
condBtn.type = 'button';
condBtn.textContent = '+ 添加条件';
condBtn.onclick = () => addConditionDiv(condContainer);
condBtnDiv.appendChild(condBtn);
fieldsetCond.appendChild(condBtnDiv);
body.appendChild(fieldsetCond);
// 修饰符
const fieldsetMod = document.createElement('fieldset');
const legendMod = document.createElement('legend');
legendMod.textContent = '修饰符Modifiers';
fieldsetMod.appendChild(legendMod);
const modContainer = document.createElement('div');
fieldsetMod.appendChild(modContainer);
const modBtnDiv = document.createElement('div');
modBtnDiv.className = 'button-container';
const modBtn = document.createElement('button');
modBtn.type = 'button';
modBtn.textContent = '+ 添加修饰符';
modBtn.onclick = () => addModifierDiv(modContainer);
modBtnDiv.appendChild(modBtn);
fieldsetMod.appendChild(modBtnDiv);
body.appendChild(fieldsetMod);
// 删除词条
const delBtn = document.createElement('button');
delBtn.textContent = '删除该词条';
delBtn.type = 'button';
delBtn.style.marginTop = '10px';
delBtn.onclick = () => affixListContainer.removeChild(card);
body.appendChild(delBtn);
affixListContainer.appendChild(card);
// 如果有 importData就初始化
if (importData) {
inputId.value = importData.id || '';
selectQuality.value = importData.quality || 'COMMON';
selectType.value = importData.type || 'POSITIVE';
textDesc.value = importData.desc || '';
inputInt.value = importData.checkInterval || 5;
selectLogic.value = importData.conditionLogic || 'AND';
// 条件
if (Array.isArray(importData.conditions)) {
importData.conditions.forEach(c => {
addConditionDiv(condContainer, c);
});
}
// 修饰符
if (Array.isArray(importData.modifiers)) {
importData.modifiers.forEach(m => {
addModifierDiv(modContainer, m);
});
}
}
}
// ========== 添加一个 condition div ==========
function addConditionDiv(container, initData = null) {
const div = document.createElement('div');
div.className = 'condition';
const sel = document.createElement('select');
sel.innerHTML = `
<option value="EXPRESSION">EXPRESSION</option>
<option value="PLAYER_LEVEL">PLAYER_LEVEL</option>
<option value="ITEM_EQUIPPED">ITEM_EQUIPPED</option>
<option value="WORLD_TIME">WORLD_TIME</option>
<option value="BIOME">BIOME</option>
<option value="WEATHER">WEATHER</option>
<option value="MOB_KILLS">MOB_KILLS</option>
`;
sel.onchange = () => updateConditionFields(div, sel.value);
const labelType = document.createElement('label');
labelType.textContent = '类型: ';
labelType.appendChild(sel);
const removeBtn = document.createElement('button');
removeBtn.textContent = '删除条件';
removeBtn.type = 'button';
removeBtn.onclick = () => container.removeChild(div);
const fieldsContainer = document.createElement('div');
fieldsContainer.className = 'condition-fields';
div.appendChild(labelType);
div.appendChild(document.createElement('br'));
div.appendChild(fieldsContainer);
div.appendChild(document.createElement('br'));
div.appendChild(removeBtn);
container.appendChild(div);
// 默认
updateConditionFields(div, 'EXPRESSION');
// 如果有 initData设置
if (initData) {
sel.value = initData.type || 'EXPRESSION';
updateConditionFields(div, sel.value);
// 填充值
for (const [k,v] of Object.entries(initData)) {
if (k === 'type') continue;
const node = fieldsContainer.querySelector(`[data-field="${k}"]`);
if (node) node.value = v;
}
}
}
function updateConditionFields(conditionDiv, conditionType) {
const fieldsContainer = conditionDiv.querySelector('.condition-fields');
fieldsContainer.innerHTML = '';
switch(conditionType) {
case 'EXPRESSION': {
const labelExpr = document.createElement('label');
labelExpr.textContent = '表达式: ';
const inputExpr = document.createElement('input');
inputExpr.type = 'text';
inputExpr.placeholder = '#level >= 10 或 #kill_zombie > 20';
inputExpr.dataset.field = 'expression';
labelExpr.appendChild(inputExpr);
// 可插入变量
const varLabel = document.createElement('label');
varLabel.textContent = '可用变量: ';
const varSelect = document.createElement('select');
expressionVarList.forEach(v => {
const opt = document.createElement('option');
opt.value = v.value;
opt.textContent = v.label;
varSelect.appendChild(opt);
});
const varBtn = document.createElement('button');
varBtn.textContent = '插入';
varBtn.type = 'button';
varBtn.onclick = () => {
const chosenVar = varSelect.value;
inputExpr.value += `#${chosenVar} `;
inputExpr.focus();
};
fieldsContainer.appendChild(labelExpr);
fieldsContainer.appendChild(document.createElement('br'));
fieldsContainer.appendChild(varLabel);
varLabel.appendChild(varSelect);
fieldsContainer.appendChild(varBtn);
break;
}
case 'PLAYER_LEVEL': {
const labelLevel = document.createElement('label');
labelLevel.textContent = '最低等级: ';
const inputLevel = document.createElement('input');
inputLevel.type = 'number';
inputLevel.placeholder = '10';
inputLevel.dataset.field = 'level';
labelLevel.appendChild(inputLevel);
fieldsContainer.appendChild(labelLevel);
break;
}
case 'ITEM_EQUIPPED': {
const labelItem = document.createElement('label');
labelItem.textContent = '物品: ';
const inputItem = document.createElement('input');
inputItem.type = 'text';
inputItem.placeholder = 'DIAMOND_CHESTPLATE';
inputItem.dataset.field = 'item';
labelItem.appendChild(inputItem);
fieldsContainer.appendChild(labelItem);
break;
}
case 'WORLD_TIME': {
const labelMin = document.createElement('label');
labelMin.textContent = '时间下限: ';
const inputMin = document.createElement('input');
inputMin.type = 'number';
inputMin.placeholder = '13000';
inputMin.dataset.field = 'min';
labelMin.appendChild(inputMin);
const labelMax = document.createElement('label');
labelMax.textContent = ' 时间上限: ';
const inputMax = document.createElement('input');
inputMax.type = 'number';
inputMax.placeholder = '23000';
inputMax.dataset.field = 'max';
labelMax.appendChild(inputMax);
fieldsContainer.appendChild(labelMin);
fieldsContainer.appendChild(labelMax);
break;
}
case 'BIOME': {
const labelB = document.createElement('label');
labelB.textContent = '生物群系: ';
const selB = document.createElement('select');
selB.dataset.field = 'biome';
biomeList.forEach(b => {
const opt = document.createElement('option');
opt.value = b.value;
opt.textContent = b.label;
selB.appendChild(opt);
});
labelB.appendChild(selB);
fieldsContainer.appendChild(labelB);
break;
}
case 'WEATHER': {
const labelW = document.createElement('label');
labelW.textContent = '下雨: ';
const selW = document.createElement('select');
selW.dataset.field = 'weather';
selW.innerHTML = `
<option value="true">true</option>
<option value="false">false</option>
`;
labelW.appendChild(selW);
fieldsContainer.appendChild(labelW);
break;
}
case 'MOB_KILLS': {
const labelMob = document.createElement('label');
labelMob.textContent = '怪物类型: ';
const inputMob = document.createElement('input');
inputMob.type = 'text';
inputMob.placeholder = 'ZOMBIE';
inputMob.dataset.field = 'mob';
labelMob.appendChild(inputMob);
const labelKills = document.createElement('label');
labelKills.textContent = ' 杀怪数量: ';
const inputKills = document.createElement('input');
inputKills.type = 'number';
inputKills.placeholder = '50';
inputKills.dataset.field = 'kills';
labelKills.appendChild(inputKills);
fieldsContainer.appendChild(labelMob);
fieldsContainer.appendChild(labelKills);
break;
}
}
}
// ========== 添加一个 modifier div ==========
function addModifierDiv(container, initData = null) {
const div = document.createElement('div');
div.className = 'modifier';
const sel = document.createElement('select');
sel.innerHTML = `
<option value="ATTRIBUTE">ATTRIBUTE</option>
<option value="POTION">POTION</option>
`;
sel.onchange = () => updateModifierFields(div, sel.value);
const labelType = document.createElement('label');
labelType.textContent = '类型: ';
labelType.appendChild(sel);
const removeBtn = document.createElement('button');
removeBtn.textContent = '删除修饰符';
removeBtn.type = 'button';
removeBtn.onclick = () => container.removeChild(div);
const fieldsContainer = document.createElement('div');
fieldsContainer.className = 'modifier-fields';
div.appendChild(labelType);
div.appendChild(document.createElement('br'));
div.appendChild(fieldsContainer);
div.appendChild(document.createElement('br'));
div.appendChild(removeBtn);
container.appendChild(div);
updateModifierFields(div, 'ATTRIBUTE');
if (initData) {
sel.value = initData.type || 'ATTRIBUTE';
updateModifierFields(div, sel.value);
for (const [k,v] of Object.entries(initData)) {
if (k === 'type') continue;
const node = fieldsContainer.querySelector(`[data-field="${k}"]`);
if (node) node.value = v;
}
}
}
function updateModifierFields(modDiv, modType) {
const fieldsContainer = modDiv.querySelector('.modifier-fields');
fieldsContainer.innerHTML = '';
if (modType === 'ATTRIBUTE') {
const labelAttr = document.createElement('label');
labelAttr.textContent = '属性: ';
const selAttr = document.createElement('select');
selAttr.dataset.field = 'attribute';
attributeList.forEach(att => {
const opt = document.createElement('option');
opt.value = att.value;
opt.textContent = att.label;
selAttr.appendChild(opt);
});
labelAttr.appendChild(selAttr);
const labelKey = document.createElement('label');
labelKey.textContent = ' keyName: ';
const inputKey = document.createElement('input');
inputKey.type = 'text';
inputKey.placeholder = 'affix_key_name';
inputKey.dataset.field = 'keyName';
labelKey.appendChild(inputKey);
const labelOp = document.createElement('label');
labelOp.textContent = ' operation: ';
const selOp = document.createElement('select');
selOp.dataset.field = 'operation';
selOp.innerHTML = `
<option value="ADD_NUMBER">ADD_NUMBER</option>
<option value="ADD_SCALAR">ADD_SCALAR</option>
<option value="MULTIPLY_SCALAR_1">MULTIPLY_SCALAR_1</option>
`;
labelOp.appendChild(selOp);
const labelInfo = document.createElement('p');
labelInfo.textContent = '数值增益(填写 amount)或表达式增益(填写 expression)二选一:';
const labelAmt = document.createElement('label');
labelAmt.textContent = ' amount: ';
const inputAmt = document.createElement('input');
inputAmt.type = 'number';
inputAmt.step = '0.1';
inputAmt.placeholder = '5.0 或 0.2';
inputAmt.dataset.field = 'amount';
labelAmt.appendChild(inputAmt);
const labelExp = document.createElement('label');
labelExp.textContent = ' expression: ';
const inputExp = document.createElement('input');
inputExp.type = 'text';
inputExp.placeholder = '#kill_zombie * 1.5';
inputExp.dataset.field = 'expression';
labelExp.appendChild(inputExp);
// 表达式插入变量
const varLabel = document.createElement('label');
varLabel.textContent = '插入变量: ';
const varSel = document.createElement('select');
expressionVarList.forEach(v => {
const opt = document.createElement('option');
opt.value = v.value;
opt.textContent = v.label;
varSel.appendChild(opt);
});
const varBtn = document.createElement('button');
varBtn.textContent = '插入';
varBtn.type = 'button';
varBtn.onclick = () => {
const chosenVar = varSel.value;
inputExp.value += `#${chosenVar} `;
inputExp.focus();
};
fieldsContainer.appendChild(labelAttr);
fieldsContainer.appendChild(labelKey);
fieldsContainer.appendChild(labelOp);
fieldsContainer.appendChild(document.createElement('br'));
fieldsContainer.appendChild(labelInfo);
fieldsContainer.appendChild(labelAmt);
fieldsContainer.appendChild(labelExp);
fieldsContainer.appendChild(document.createElement('br'));
fieldsContainer.appendChild(varLabel);
varLabel.appendChild(varSel);
fieldsContainer.appendChild(varBtn);
} else if (modType === 'POTION') {
const labelEff = document.createElement('label');
labelEff.textContent = '效果类型(effect): ';
const selEff = document.createElement('select');
selEff.dataset.field = 'effect';
potionEffectList.forEach(pt => {
const opt = document.createElement('option');
opt.value = pt.value;
opt.textContent = pt.label;
selEff.appendChild(opt);
});
labelEff.appendChild(selEff);
const labelDur = document.createElement('label');
labelDur.textContent = ' 持续时间(ticks): ';
const inputDur = document.createElement('input');
inputDur.type = 'number';
inputDur.placeholder = '200 (约10秒)';
inputDur.dataset.field = 'duration';
labelDur.appendChild(inputDur);
const labelAmp = document.createElement('label');
labelAmp.textContent = ' 等级(amplifier): ';
const inputAmp = document.createElement('input');
inputAmp.type = 'number';
inputAmp.placeholder = '0 为一级';
inputAmp.dataset.field = 'amplifier';
labelAmp.appendChild(inputAmp);
fieldsContainer.appendChild(labelEff);
fieldsContainer.appendChild(labelDur);
fieldsContainer.appendChild(labelAmp);
}
}
// ========== 生成全部 JSON ==========
function generateAllJSON() {
const affixMap = {};
const affixCards = document.querySelectorAll('.affix-card');
affixCards.forEach(card => {
// 基础信息
const inputId = card.querySelector('input[type="text"]');
const selectQuality = card.querySelectorAll('select')[0];
const selectType = card.querySelectorAll('select')[1];
const textDesc = card.querySelector('textarea');
const inputInt = card.querySelector('input[type="number"]');
const selectLogic = card.querySelectorAll('select')[2];
const affixId = inputId.value.trim() || 'my_affix_' + Math.floor(Math.random()*10000);
const affixObj = {
id: affixId,
quality: selectQuality.value,
type: selectType.value,
desc: textDesc.value.trim(),
checkInterval: parseInt(inputInt.value, 10),
conditionLogic: selectLogic.value,
conditions: [],
modifiers: []
};
// 条件
const condContainer = card.querySelector('fieldset:nth-of-type(2) > div');
const condDivs = condContainer.querySelectorAll('.condition');
condDivs.forEach(cd => {
const selType = cd.querySelector('select');
const typeValue = selType.value;
const cobj = { type: typeValue };
const fieldNodes = cd.querySelectorAll('[data-field]');
fieldNodes.forEach(node => {
const k = node.dataset.field;
let val = node.value;
if (['min','max','level','kills'].includes(k)) {
val = parseInt(val, 10);
} else if (k === 'weather') {
val = (val === 'true');
}
if (val !== '') {
cobj[k] = val;
}
});
affixObj.conditions.push(cobj);
});
// 修饰符
const modContainer = card.querySelector('fieldset:nth-of-type(3) > div');
const modDivs = modContainer.querySelectorAll('.modifier');
modDivs.forEach(md => {
const selType = md.querySelector('select');
const typeValue = selType.value;
const mobj = { type: typeValue };
const fieldNodes = md.querySelectorAll('[data-field]');
fieldNodes.forEach(node => {
const k = node.dataset.field;
let val = node.value;
if (k === 'amount') {
val = parseFloat(val);
} else if (['duration','amplifier'].includes(k)) {
val = parseInt(val, 10);
}
if (val !== '') {
mobj[k] = val;
}
});
affixObj.modifiers.push(mobj);
});
affixMap[affixId] = affixObj;
});
document.getElementById('output').textContent = JSON.stringify(affixMap, null, 2);
}
</script>
</body>
</html>