use crate::content::Compiled;
use crate::content::block_item::text_key;
use crate::content::ctx::{Ctx, Shared};
use crate::content::file::file_id;
use crate::content::files::{BodyFile, EntityFile, FontFile};
use std::path::Path;
use yog_ids::Format;
const ID: &str = yog_data::field!(yog_registry::EntityDef, id);
const NAME: &str = yog_data::field!(yog_registry::EntityDef, name);
pub(super) fn compile_entity(
cx: &mut Ctx,
text: &str,
path: &Path,
mod_ns: Option<&str>,
out: &mut Compiled,
) -> Option<EntityFile> {
use yog_data::Value;
let mut value = match yog_data::text::parse_value(text) {
Ok(v) => v,
Err(e) => {
cx.err(e.at.line as usize, e.message);
return None;
}
};
let Value::Map(map) = &mut value else {
cx.err(
1,
"an entity is an object: { \"kind\": \"mob\", \"width\": …, \"height\": …, … }",
);
return None;
};
let given = map.get(ID).and_then(Value::as_str).map(str::to_owned);
let (ns, p) = file_id(cx, 1, given.as_deref(), path, mod_ns)?;
let id = yog_ids::join(&ns, &p);
map.insert(ID, Value::from(id.as_str()));
if let Some(name) = map.remove(NAME) {
let key = yog_ids::lang_key(yog_ids::kind::ENTITY, &id);
match yog_data::from_value::<yog_data::Text>(&name) {
Ok(read) => match text_key(cx, &mut out.lang, &read.value, &ns, key.clone()) {
Ok(from) if from != key => out.lang_aliases.push((from, key)),
Ok(_) => {}
Err(e) => cx.err(1, format!("{NAME} {e}")),
},
Err(problems) => {
for pr in problems {
cx.err(1, format!("{NAME}: {}", pr.message));
}
}
}
}
let def = match yog_data::from_value::<yog_registry::EntityDef>(&value) {
Ok(read) => read.value,
Err(problems) => {
for pr in problems {
cx.err(1, format!("{}: {}", pr.path, pr.message));
}
return None;
}
};
let qualify = |s: &Option<String>| s.as_ref().map(|s| yog_ids::qualify_in(&ns, s));
let mut def = def;
if let Some(colors) = def.spawn_egg {
super::looks::spawn_egg(cx.root, out, &ns, &p, colors);
}
def.model = qualify(&def.model);
def.animations = qualify(&def.animations);
Some(EntityFile {
id,
file: cx.file.clone(),
model: def.model.clone(),
animations: def.animations.clone(),
data: yog_data::binary::encode(&yog_data::Schema::write(&def)),
})
}
pub(super) fn compile_font(
cx: &Shared,
shown: &str,
text: &str,
path: &Path,
errors: &mut Vec<String>,
) -> Option<FontFile> {
let font = match yog_data::from_text::<yog_font::Font>(text) {
Ok(read) => read.value,
Err(problems) => {
for problem in problems.0 {
errors.push(format!("{shown}: {problem}"));
}
return None;
}
};
if let Err(problems) = font.validate() {
errors.extend(
problems
.into_iter()
.map(|problem| format!("{shown}: {problem}")),
);
return None;
}
Some(FontFile {
id: cx.content_id(path),
data: font.to_bytes(),
})
}
pub(super) fn compile_body(
cx: &Shared,
shown: &str,
text: &str,
kind: Format,
path: &Path,
errors: &mut Vec<String>,
) -> Option<BodyFile> {
let ns = cx.ns;
let id = cx.content_id(path);
let report = |errors: &mut Vec<String>, e: yog_data::Errors| {
for l in e.0 {
errors.push(format!("{shown}:{l}"));
}
};
let value = if kind == Format::Model {
let file = match yog_data::from_text::<yog_body::format::ModelFile>(text) {
Ok(r) => r.value,
Err(e) => {
report(errors, e);
return None;
}
};
if let Err(e) = yog_body::Model::new(&file) {
errors.push(format!("{shown}: {e}"));
return None;
}
let (tns, tpath) = yog_ids::split(&file.texture);
if tns == ns && !cx.own_textures.contains(&file.texture) {
errors.push(format!(
"{shown}: texture `{}`: no {}/{}",
file.texture,
crate::yres::RESOURCES_DIR,
Format::Texture.file(tpath)
));
return None;
}
yog_data::Schema::write(&file)
} else {
let file = match yog_data::from_text::<yog_body::format::AnimFile>(text) {
Ok(r) => r.value,
Err(e) => {
report(errors, e);
return None;
}
};
if let Err(e) = yog_body::anim::Clips::new(&file) {
errors.push(format!("{shown}: {e}"));
return None;
}
yog_data::Schema::write(&file)
};
Some(BodyFile {
model: kind == Format::Model,
id,
data: yog_data::binary::encode(&value),
})
}