use serde::{Deserialize, Serialize};
use tracing::warn;
use crate::parser::{
clean_search_vec, helpers::build_object_id_from_pieces, object_types::ObjectType, RawMetadata,
RawObject, Searchable,
};
use super::{
custom_extension::CustomGraphicExtension,
phf_table::{CUSTOM_GRAPHIC_TAGS, GROWTH_TAGS, PLANT_GRAPHIC_TEMPLATES},
sprite_graphic::SpriteGraphic,
sprite_layer::SpriteLayer,
tokens::GraphicType,
};
#[allow(clippy::module_name_repetitions)]
#[derive(Serialize, Deserialize, Debug, Clone, Default, specta::Type)]
#[serde(rename_all = "camelCase")]
pub struct Graphic {
#[serde(skip_serializing_if = "Option::is_none")]
metadata: Option<RawMetadata>,
identifier: String,
object_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
caste_identifier: Option<String>,
kind: GraphicType,
#[serde(skip_serializing_if = "Option::is_none")]
sprites: Option<Vec<SpriteGraphic>>,
#[serde(skip_serializing_if = "Option::is_none")]
layers: Option<Vec<(String, Vec<SpriteLayer>)>>,
#[serde(skip_serializing_if = "Option::is_none")]
growths: Option<Vec<(String, Vec<SpriteGraphic>)>>,
#[serde(skip_serializing_if = "Option::is_none")]
custom_extensions: Option<Vec<CustomGraphicExtension>>,
#[serde(skip_serializing_if = "Option::is_none")]
tags: Option<Vec<String>>,
#[serde(skip)]
layer_mode: bool,
}
impl Graphic {
#[must_use]
pub fn empty() -> Self {
Self {
metadata: Some(
RawMetadata::default()
.with_object_type(ObjectType::Graphics)
.with_hidden(true),
),
..Default::default()
}
}
#[must_use]
pub fn new(identifier: &str, metadata: &RawMetadata, graphic_type: GraphicType) -> Self {
Self {
identifier: String::from(identifier),
metadata: Some(metadata.clone()),
object_id: build_object_id_from_pieces(metadata, identifier, &ObjectType::Graphics),
kind: graphic_type,
..Self::default()
}
}
fn add_layer_if_not_exists(&mut self, layer_name: String) {
if let Some(layers) = self.layers.as_mut() {
if !layers.iter().any(|(name, _)| name == &layer_name) {
layers.push((layer_name, Vec::new()));
}
} else {
self.layers = Some(vec![(layer_name, Vec::new())]);
}
}
fn parse_layer_set_from_value(&mut self, value: &str) {
self.add_layer_if_not_exists(String::from(value));
}
fn parse_layer_from_value(&mut self, value: &str) {
if let Some(layer) = SpriteLayer::parse_layer_from_value(value) {
if self.layers.is_none() {
self.add_layer_if_not_exists(String::from("default"));
}
if let Some(layers) = self.layers.as_mut() {
#[allow(clippy::unwrap_used)]
layers.last_mut().unwrap().1.push(layer);
}
}
}
fn parse_layer_condition_token(&mut self, key: &str, value: &str) {
if let Some(layers) = self.layers.as_mut() {
#[allow(clippy::unwrap_used)]
if let Some(layer) = layers.last_mut().unwrap().1.last_mut() {
layer.parse_condition_token(key, value);
} else {
warn!(
"Graphic::parse_condition_token: [{}] Failed to parse {}:{} as LayerCondition",
self.identifier, key, value
);
}
} else {
warn!(
"Graphic::parse_condition_token: [{}] Failed to parse {}:{} as LayerCondition (No existing layers)",
self.identifier, key, value
);
}
}
#[allow(clippy::too_many_lines)]
pub fn parse_sprite_from_tag(&mut self, key: &str, value: &str, graphic_type: GraphicType) {
if key == "LAYER_SET" {
self.parse_layer_set_from_value(value);
self.layer_mode = true;
return;
}
if key == "LAYER" {
self.parse_layer_from_value(value);
self.layer_mode = true;
return;
}
if key == "LAYER_GROUP" {
self.layer_mode = true;
return;
}
if key == "END_LAYER_GROUP" {
self.layer_mode = false;
return;
}
if key == "TREE_TILE" {
return;
}
if key == "GROWTH" {
if let Some(growths) = self.growths.as_mut() {
growths.push((String::from(value), Vec::new()));
} else {
self.growths = Some(vec![(String::from(value), Vec::new())]);
}
return;
}
if value.is_empty() {
if let Some(tags) = self.tags.as_mut() {
tags.push(String::from(key));
} else {
self.tags = Some(vec![String::from(key)]);
}
return;
}
if let Some(extension_type) = CUSTOM_GRAPHIC_TAGS.get(key) {
if let Some(custom_extension) =
CustomGraphicExtension::from_value(*extension_type, value)
{
if let Some(custom_extensions) = self.custom_extensions.as_mut() {
custom_extensions.push(custom_extension);
} else {
self.custom_extensions = Some(vec![custom_extension]);
}
} else {
warn!(
"Graphic::parse_sprite_from_tag:_extension_type [{}] Failed to parse {},{} as CustomGraphicExtension",
self.identifier,
key,
value
);
}
return;
}
if let Some(_growth_type) = GROWTH_TAGS.get(key) {
if let Some(sprite_graphic) = SpriteGraphic::from_token(key, value, graphic_type) {
if let Some(growths) = self.growths.as_mut() {
if let Some(growth) = growths.last_mut() {
growth.1.push(sprite_graphic);
};
}
} else {
warn!(
"Graphic::parse_sprite_from_tag:_growth_type [{}] Failed to parse {},{} as SpriteGraphic",
self.identifier,
key,
value
);
}
return;
}
if let Some(_plant_graphic_template) = PLANT_GRAPHIC_TEMPLATES.get(key) {
if let Some(sprite_graphic) =
SpriteGraphic::from_token(key, value, GraphicType::Template)
{
if let Some(growths) = self.growths.as_mut() {
if let Some(growth) = growths.last_mut() {
growth.1.push(sprite_graphic);
};
}
} else {
warn!(
"Graphic::parse_sprite_from_tag:_plant_graphic_template [{}] Failed to parse {},{} as SpriteGraphic",
self.identifier,
key,
value
);
}
return;
}
if self.layer_mode {
self.parse_layer_condition_token(key, value);
return;
}
if let Some(sprite_graphic) = SpriteGraphic::from_token(key, value, graphic_type) {
if let Some(sprites) = self.sprites.as_mut() {
sprites.push(sprite_graphic);
} else {
self.sprites = Some(vec![sprite_graphic]);
}
} else {
warn!(
"Graphic::parse_sprite_from_tag:_from_token [{}] Failed to parse [{}:{}] as SpriteGraphic::{:?}",
self.identifier,
key,
value,
graphic_type
);
}
}
#[must_use]
pub const fn get_graphic_type(&self) -> GraphicType {
self.kind
}
#[must_use]
pub fn get_tile_pages(&self) -> Vec<String> {
let mut vec = Vec::new();
if let Some(sprites) = &self.sprites {
for sprite in sprites {
vec.push(sprite.get_tile_page_id().to_string());
}
}
if let Some(layers) = &self.layers {
for layer in layers {
for sprite in &layer.1 {
vec.push(sprite.get_tile_page_id().to_string());
}
}
}
vec
}
#[must_use]
pub fn cleaned(&self) -> Self {
let mut cleaned = self.clone();
if let Some(metadata) = &cleaned.metadata {
if metadata.is_hidden() {
cleaned.metadata = None;
}
}
if let Some(custom_extensions) = &cleaned.custom_extensions {
if custom_extensions.is_empty() {
cleaned.custom_extensions = None;
}
}
if let Some(tags) = &cleaned.tags {
if tags.is_empty() {
cleaned.tags = None;
}
}
if let Some(sprites) = &cleaned.sprites {
let mut new_sprites = Vec::new();
for sprite in sprites {
new_sprites.push(sprite.cleaned());
}
cleaned.sprites = Some(new_sprites);
}
if let Some(layers) = &cleaned.layers {
let mut new_layers = Vec::new();
for (name, sprites) in layers {
let mut new_sprites = Vec::new();
for sprite in sprites {
new_sprites.push(sprite.cleaned());
}
new_layers.push((name.clone(), new_sprites));
}
cleaned.layers = Some(new_layers);
}
if let Some(growths) = &cleaned.growths {
let mut new_growths = Vec::new();
for (name, sprites) in growths {
let mut new_sprites = Vec::new();
for sprite in sprites {
new_sprites.push(sprite.cleaned());
}
new_growths.push((name.clone(), new_sprites));
}
cleaned.growths = Some(new_growths);
}
cleaned
}
}
#[typetag::serde]
impl RawObject for Graphic {
fn get_metadata(&self) -> RawMetadata {
self.metadata.as_ref().map_or_else(
|| {
warn!("Metadata is missing for {}", self.get_identifier());
RawMetadata::default()
.with_object_type(ObjectType::Graphics)
.with_hidden(true)
},
std::clone::Clone::clone,
)
}
fn get_identifier(&self) -> &str {
&self.identifier
}
fn get_name(&self) -> &str {
&self.identifier
}
fn is_empty(&self) -> bool {
self.identifier.is_empty()
}
fn get_type(&self) -> &ObjectType {
&ObjectType::Graphics
}
fn clean_self(&mut self) {
*self = self.cleaned();
}
fn parse_tag(&mut self, key: &str, value: &str) {
warn!(
"Graphics tag attempted parse with wrong method: {}:{} for {}",
key,
value,
self.get_identifier()
);
}
fn get_object_id(&self) -> &str {
&self.object_id
}
}
impl Searchable for Graphic {
fn get_search_vec(&self) -> Vec<String> {
let mut vec = Vec::new();
vec.push(self.get_identifier().to_string());
vec.push(format!("{:?}", self.kind));
vec.push("graphic".to_string());
clean_search_vec(vec.as_slice())
}
}