上传文件至 /

This commit is contained in:
usami 2025-02-27 01:15:56 -08:00
parent 309efa4fd2
commit 072fbcf4d7

921
a.html Normal file
View File

@ -0,0 +1,921 @@
<!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>