use std::sync::LazyLock;
use crate::nbt::{NbtMap, NbtValue};
use super::super::loss::{report_loss, LossKind, Severity};
use super::super::registry::RegistryBuilder;
use super::super::types::{MapExt, ValueExt};
const VERSION: i32 = 4061;
const CONVERSIONS: &[(&str, &str, &str)] = &[
(
"trial_chamber/breeze",
"{simultaneous_mobs: 1.0f, simultaneous_mobs_added_per_player: 0.5f, spawn_potentials: [{data: {entity: {id: \"minecraft:breeze\"}}, weight: 1}], ticks_between_spawn: 20, total_mobs: 2.0f, total_mobs_added_per_player: 1.0f}",
"{loot_tables_to_eject: [{data: \"minecraft:spawners/ominous/trial_chamber/key\", weight: 3}, {data: \"minecraft:spawners/ominous/trial_chamber/consumables\", weight: 7}], simultaneous_mobs: 2.0f, total_mobs: 4.0f}",
),
(
"trial_chamber/melee/husk",
"{simultaneous_mobs: 3.0f, simultaneous_mobs_added_per_player: 0.5f, spawn_potentials: [{data: {entity: {id: \"minecraft:husk\"}}, weight: 1}], ticks_between_spawn: 20}",
"{loot_tables_to_eject: [{data: \"minecraft:spawners/ominous/trial_chamber/key\", weight: 3}, {data: \"minecraft:spawners/ominous/trial_chamber/consumables\", weight: 7}], spawn_potentials: [{data: {entity: {id: \"minecraft:husk\"}, equipment: {loot_table: \"minecraft:equipment/trial_chamber_melee\", slot_drop_chances: 0.0f}}, weight: 1}]}",
),
(
"trial_chamber/melee/spider",
"{simultaneous_mobs: 3.0f, simultaneous_mobs_added_per_player: 0.5f, spawn_potentials: [{data: {entity: {id: \"minecraft:spider\"}}, weight: 1}], ticks_between_spawn: 20}",
"{loot_tables_to_eject: [{data: \"minecraft:spawners/ominous/trial_chamber/key\", weight: 3}, {data: \"minecraft:spawners/ominous/trial_chamber/consumables\", weight: 7}],simultaneous_mobs: 4.0f, total_mobs: 12.0f}",
),
(
"trial_chamber/melee/zombie",
"{simultaneous_mobs: 3.0f, simultaneous_mobs_added_per_player: 0.5f, spawn_potentials: [{data: {entity: {id: \"minecraft:zombie\"}}, weight: 1}], ticks_between_spawn: 20}",
"{loot_tables_to_eject: [{data: \"minecraft:spawners/ominous/trial_chamber/key\", weight: 3}, {data: \"minecraft:spawners/ominous/trial_chamber/consumables\", weight: 7}],spawn_potentials: [{data: {entity: {id: \"minecraft:zombie\"}, equipment: {loot_table: \"minecraft:equipment/trial_chamber_melee\", slot_drop_chances: 0.0f}}, weight: 1}]}",
),
(
"trial_chamber/ranged/poison_skeleton",
"{simultaneous_mobs: 3.0f, simultaneous_mobs_added_per_player: 0.5f, spawn_potentials: [{data: {entity: {id: \"minecraft:bogged\"}}, weight: 1}], ticks_between_spawn: 20}",
"{loot_tables_to_eject: [{data: \"minecraft:spawners/ominous/trial_chamber/key\", weight: 3}, {data: \"minecraft:spawners/ominous/trial_chamber/consumables\", weight: 7}],spawn_potentials: [{data: {entity: {id: \"minecraft:bogged\"}, equipment: {loot_table: \"minecraft:equipment/trial_chamber_ranged\", slot_drop_chances: 0.0f}}, weight: 1}]}",
),
(
"trial_chamber/ranged/skeleton",
"{simultaneous_mobs: 3.0f, simultaneous_mobs_added_per_player: 0.5f, spawn_potentials: [{data: {entity: {id: \"minecraft:skeleton\"}}, weight: 1}], ticks_between_spawn: 20}",
"{loot_tables_to_eject: [{data: \"minecraft:spawners/ominous/trial_chamber/key\", weight: 3}, {data: \"minecraft:spawners/ominous/trial_chamber/consumables\", weight: 7}], spawn_potentials: [{data: {entity: {id: \"minecraft:skeleton\"}, equipment: {loot_table: \"minecraft:equipment/trial_chamber_ranged\", slot_drop_chances: 0.0f}}, weight: 1}]}",
),
(
"trial_chamber/ranged/stray",
"{simultaneous_mobs: 3.0f, simultaneous_mobs_added_per_player: 0.5f, spawn_potentials: [{data: {entity: {id: \"minecraft:stray\"}}, weight: 1}], ticks_between_spawn: 20}",
"{loot_tables_to_eject: [{data: \"minecraft:spawners/ominous/trial_chamber/key\", weight: 3}, {data: \"minecraft:spawners/ominous/trial_chamber/consumables\", weight: 7}], spawn_potentials: [{data: {entity: {id: \"minecraft:stray\"}, equipment: {loot_table: \"minecraft:equipment/trial_chamber_ranged\", slot_drop_chances: 0.0f}}, weight: 1}]}",
),
(
"trial_chamber/slow_ranged/poison_skeleton",
"{simultaneous_mobs: 4.0f, simultaneous_mobs_added_per_player: 2.0f, spawn_potentials: [{data: {entity: {id: \"minecraft:bogged\"}}, weight: 1}], ticks_between_spawn: 160}",
"{loot_tables_to_eject: [{data: \"minecraft:spawners/ominous/trial_chamber/key\", weight: 3}, {data: \"minecraft:spawners/ominous/trial_chamber/consumables\", weight: 7}], spawn_potentials: [{data: {entity: {id: \"minecraft:bogged\"}, equipment: {loot_table: \"minecraft:equipment/trial_chamber_ranged\", slot_drop_chances: 0.0f}}, weight: 1}]}",
),
(
"trial_chamber/slow_ranged/skeleton",
"{simultaneous_mobs: 4.0f, simultaneous_mobs_added_per_player: 2.0f, spawn_potentials: [{data: {entity: {id: \"minecraft:skeleton\"}}, weight: 1}], ticks_between_spawn: 160}",
"{loot_tables_to_eject: [{data: \"minecraft:spawners/ominous/trial_chamber/key\", weight: 3}, {data: \"minecraft:spawners/ominous/trial_chamber/consumables\", weight: 7}], spawn_potentials: [{data: {entity: {id: \"minecraft:skeleton\"}, equipment: {loot_table: \"minecraft:equipment/trial_chamber_ranged\", slot_drop_chances: 0.0f}}, weight: 1}]}",
),
(
"trial_chamber/slow_ranged/stray",
"{simultaneous_mobs: 4.0f, simultaneous_mobs_added_per_player: 2.0f, spawn_potentials: [{data: {entity: {id: \"minecraft:stray\"}}, weight: 1}], ticks_between_spawn: 160}",
"{loot_tables_to_eject: [{data: \"minecraft:spawners/ominous/trial_chamber/key\", weight: 3}, {data: \"minecraft:spawners/ominous/trial_chamber/consumables\", weight: 7}],spawn_potentials: [{data: {entity: {id: \"minecraft:stray\"}, equipment: {loot_table: \"minecraft:equipment/trial_chamber_ranged\", slot_drop_chances: 0.0f}}, weight: 1}]}",
),
(
"trial_chamber/small_melee/baby_zombie",
"{simultaneous_mobs: 2.0f, simultaneous_mobs_added_per_player: 0.5f, spawn_potentials: [{data: {entity: {IsBaby: 1b, id: \"minecraft:zombie\"}}, weight: 1}], ticks_between_spawn: 20}",
"{loot_tables_to_eject: [{data: \"minecraft:spawners/ominous/trial_chamber/key\", weight: 3}, {data: \"minecraft:spawners/ominous/trial_chamber/consumables\", weight: 7}], spawn_potentials: [{data: {entity: {IsBaby: 1b, id: \"minecraft:zombie\"}, equipment: {loot_table: \"minecraft:equipment/trial_chamber_melee\", slot_drop_chances: 0.0f}}, weight: 1}]}",
),
(
"trial_chamber/small_melee/cave_spider",
"{simultaneous_mobs: 3.0f, simultaneous_mobs_added_per_player: 0.5f, spawn_potentials: [{data: {entity: {id: \"minecraft:cave_spider\"}}, weight: 1}], ticks_between_spawn: 20}",
"{loot_tables_to_eject: [{data: \"minecraft:spawners/ominous/trial_chamber/key\", weight: 3}, {data: \"minecraft:spawners/ominous/trial_chamber/consumables\", weight: 7}], simultaneous_mobs: 4.0f, total_mobs: 12.0f}",
),
(
"trial_chamber/small_melee/silverfish",
"{simultaneous_mobs: 3.0f, simultaneous_mobs_added_per_player: 0.5f, spawn_potentials: [{data: {entity: {id: \"minecraft:silverfish\"}}, weight: 1}], ticks_between_spawn: 20}",
"{loot_tables_to_eject: [{data: \"minecraft:spawners/ominous/trial_chamber/key\", weight: 3}, {data: \"minecraft:spawners/ominous/trial_chamber/consumables\", weight: 7}], simultaneous_mobs: 4.0f, total_mobs: 12.0f}",
),
(
"trial_chamber/small_melee/slime",
"{simultaneous_mobs: 3.0f, simultaneous_mobs_added_per_player: 0.5f, spawn_potentials: [{data: {entity: {Size: 1, id: \"minecraft:slime\"}}, weight: 3}, {data: {entity: {Size: 2, id: \"minecraft:slime\"}}, weight: 1}], ticks_between_spawn: 20}",
"{loot_tables_to_eject: [{data: \"minecraft:spawners/ominous/trial_chamber/key\", weight: 3}, {data: \"minecraft:spawners/ominous/trial_chamber/consumables\", weight: 7}], simultaneous_mobs: 4.0f, total_mobs: 12.0f}",
),
];
struct Preset {
full_key: String,
normal: NbtMap,
ominous_variants: Vec<NbtMap>,
}
fn parse_snbt(snbt: &str) -> NbtMap {
let compound = quartz_nbt::snbt::parse(snbt).expect("valid V4061 SNBT template");
NbtMap::from_quartz_nbt(&compound)
}
fn merge(base: &NbtMap, other: &NbtMap) -> NbtMap {
let mut out = base.clone();
for (k, v) in other.iter() {
if let NbtValue::Compound(other_child) = v {
if let Some(NbtValue::Compound(base_child)) = out.get(k) {
let merged = merge(base_child, other_child);
out.set_map(k, merged);
continue;
}
}
out.set_generic(k, v.clone());
}
out
}
fn remove_defaults(config: &mut NbtMap) {
if config.get_i32("spawn_range") == Some(4) {
config.take("spawn_range");
}
if config.get("total_mobs").and_then(ValueExt::as_number_f64) == Some(6.0) {
config.take("total_mobs");
}
if config
.get("simultaneous_mobs")
.and_then(ValueExt::as_number_f64)
== Some(2.0)
{
config.take("simultaneous_mobs");
}
if config
.get("total_mobs_added_per_player")
.and_then(ValueExt::as_number_f64)
== Some(2.0)
{
config.take("total_mobs_added_per_player");
}
if config
.get("simultaneous_mobs_added_per_player")
.and_then(ValueExt::as_number_f64)
== Some(1.0)
{
config.take("simultaneous_mobs_added_per_player");
}
if config.get_i32("ticks_between_spawn") == Some(40) {
config.take("ticks_between_spawn");
}
}
static PRESETS: LazyLock<Vec<Preset>> = LazyLock::new(|| {
let mut presets = Vec::with_capacity(CONVERSIONS.len());
for (key_path, normal_snbt, ominous_snbt) in CONVERSIONS {
let full_key = format!("minecraft:{key_path}");
let normal = parse_snbt(normal_snbt);
let ominous = parse_snbt(ominous_snbt);
let ominous_merged = merge(&normal, &ominous);
let mut ominous_merged_defaults = ominous_merged.clone();
remove_defaults(&mut ominous_merged_defaults);
presets.push(Preset {
full_key,
normal,
ominous_variants: vec![ominous, ominous_merged, ominous_merged_defaults],
});
}
presets
});
pub fn register(reg: &mut RegistryBuilder) {
reg.tile_entity.add_converter_for_id(
"minecraft:trial_spawner",
VERSION,
0,
Box::new(|data, _from, _to| {
let normal_config = match data.get_map("normal_config") {
Some(m) => m.clone(),
None => return,
};
let ominous_config = match data.get_map("ominous_config") {
Some(m) => m.clone(),
None => return,
};
let matched = PRESETS.iter().find(|p| {
p.normal == normal_config && p.ominous_variants.contains(&ominous_config)
});
if let Some(preset) = matched {
data.set_string("normal_config", format!("{}/normal", preset.full_key));
data.set_string("ominous_config", format!("{}/ominous", preset.full_key));
}
}),
);
reg.tile_entity.add_reverse_converter_for_id(
"minecraft:trial_spawner",
VERSION,
0,
Box::new(|data: &mut NbtMap, _from, _to| {
let normal_ref = match data.get_string("normal_config") {
Some(s) => s.to_string(),
None => return,
};
let ominous_ref = match data.get_string("ominous_config") {
Some(s) => s.to_string(),
None => return,
};
let normal_key = match normal_ref.strip_suffix("/normal") {
Some(k) => k,
None => return,
};
let ominous_key = match ominous_ref.strip_suffix("/ominous") {
Some(k) => k,
None => return,
};
if normal_key != ominous_key {
return;
}
let preset = match PRESETS.iter().find(|p| p.full_key == normal_key) {
Some(p) => p,
None => return,
};
data.set_map("normal_config", preset.normal.clone());
data.set_map("ominous_config", preset.ominous_variants[0].clone());
report_loss(
VERSION,
LossKind::FingerprintCollapse,
Severity::Approximated,
"trial_spawner ominous_config preset reference expanded to the canonical ominous template; the original encoding (raw / merged / defaults-stripped) is not recoverable",
);
}),
);
}