use crate::CreatureToken;
use df_ls_core::{ReferenceTo, Referenceable};
use df_ls_diagnostics::{hash_map, DMExtraInfo, DiagnosticsInfo};
use df_ls_syntax_analysis::{LoopControl, Token, TokenDeserialize, TreeCursor};
use indexmap::{map::Entry, IndexMap};
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Debug, TokenDeserialize, PartialEq, Eq)]
#[allow(clippy::large_enum_variant)]
pub enum GraphicsToken {
#[token_de(token = "TILE_PAGE")]
TilePage(TilePageToken),
#[token_de(token = "CREATURE_GRAPHICS")]
CreatureGraphics(CreatureGraphicsToken),
}
impl Default for GraphicsToken {
fn default() -> Self {
Self::TilePage(TilePageToken::default())
}
}
#[derive(
Serialize, Deserialize, Clone, Debug, Default, TokenDeserialize, PartialEq, Eq, Referenceable,
)]
pub struct TilePageToken {
#[token_de(token = "TILE_PAGE", on_duplicate_to_parent, primary_token)]
#[referenceable(self_reference)]
pub reference: Option<ReferenceTo<Self>>,
#[token_de(token = "FILE")]
pub file: Option<String>,
#[token_de(token = "TILE_DIM")]
pub tile_dimensions: Option<(u32, u32)>,
#[token_de(token = "PAGE_DIM")]
pub page_dimensions: Option<(u32, u32)>,
}
type CreatureGraphicsTokenArg = (
ReferenceTo<TilePageToken>,
u32,
u32,
ColorTypeEnum,
TextureTypeEnum,
);
type TextureGraphicsTokenArg = (
ReferenceTo<TilePageToken>,
u32,
u32,
ColorTypeEnum,
Option<TextureTypeEnum>,
);
#[derive(Serialize, Deserialize, Clone, Debug, TokenDeserialize, PartialEq, Eq)]
#[token_de(enum_value)]
pub enum TextureTypeEnum {
#[token_de(token = "DEFAULT")]
Default,
#[token_de(token = "ADVENTURER")]
Adventurer,
#[token_de(token = "GUARD")]
Guard,
#[token_de(token = "ROYALGUARD")]
RoyalGuard,
#[token_de(token = "ANIMATED")]
Animated,
#[token_de(token = "GHOST")]
Ghost,
}
impl Default for TextureTypeEnum {
fn default() -> Self {
Self::Default
}
}
#[derive(Serialize, Deserialize, Clone, Debug, TokenDeserialize, PartialEq, Eq)]
#[token_de(enum_value)]
pub enum ColorTypeEnum {
#[token_de(token = "ADD_COLOR")]
AddColor,
#[token_de(token = "AS_IS")]
AsIs,
}
impl Default for ColorTypeEnum {
fn default() -> Self {
Self::AddColor
}
}
#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq)]
pub struct CreatureGraphicsToken {
pub reference: Option<ReferenceTo<CreatureToken>>,
pub main_texture_tokens: IndexMap<String, Vec<TextureGraphicsTokenArg>>,
pub other_graphics_tokens: IndexMap<String, Vec<CreatureGraphicsTokenArg>>,
}
impl TokenDeserialize for CreatureGraphicsToken {
fn deserialize_general_token(
cursor: &mut TreeCursor,
source: &str,
diagnostics: &mut DiagnosticsInfo,
mut new_self: Box<Self>,
) -> (LoopControl, Box<Self>) {
let node = cursor.node();
let primary_token_name = "CREATURE_GRAPHICS";
let token = match Token::deserialize_tokens(cursor, source, diagnostics) {
Ok(token) => token,
Err(_err) => {
Token::consume_token(cursor).unwrap();
return (LoopControl::ErrBreak, new_self);
}
};
let primary_token_filled = new_self.reference.is_some();
let primary_token_ref: Option<String> = Some(primary_token_name.to_owned());
if token.check_token_name(source, diagnostics, true).is_err() {
return (LoopControl::Break, new_self);
}
let token_name = match token.checked_get_token_name(source, diagnostics, true) {
Ok(arg) => arg,
Err(_) => {
return (LoopControl::ErrBreak, new_self);
}
};
log::debug!(
"Matching {} in {}",
token_name.value,
"CreatureGraphicsToken"
);
match token_name.value.as_ref() as &str {
"CREATURE_GRAPHICS" => {
if new_self.reference.is_some() {
return (LoopControl::Break, new_self);
}
let value = TokenDeserialize::deserialize_tokens(
cursor,
source,
diagnostics,
);
if let Ok(value) = value {
new_self.reference = Some(*value);
}
}
token_ref @ ("DEFAULT"
| "ADVENTURER"
| "GUARD"
| "ROYALGUARD"
| "ANIMATED"
| "GHOST") => {
if !primary_token_filled {
if let Some(primary_token_ref) = primary_token_ref {
diagnostics.add_message(
DMExtraInfo {
range: node.get_range(),
message_template_data: hash_map! {
"expected_tokens" => format!("`{}`", primary_token_ref),
},
},
"token_is_missing",
);
} else {
diagnostics.add_message(
DMExtraInfo {
range: node.get_range(),
message_template_data: hash_map! {
"expected_tokens" => "<Unknown>".to_owned(),
},
},
"token_is_missing",
);
}
return (LoopControl::ErrBreak, new_self);
}
let value: Result<Box<TextureGraphicsTokenArg>, _> = TokenDeserialize::deserialize_tokens(
cursor,
source,
diagnostics,
);
if let Ok(value) = value {
match new_self.main_texture_tokens.entry(token_ref.to_owned()) {
Entry::Occupied(mut occupied_entry) => {
let entry = occupied_entry.get_mut();
entry.push(*value);
},
Entry::Vacant(empty_entry) => {
empty_entry.insert(vec![*value]);
}
}
}
return (LoopControl::Continue, new_self);
}
token_ref @ ("STANDARD"
| "CHILD"
| "BABY"
| "DRUNK"
| "ADMINISTRATOR"
| "ALCHEMIST"
| "ANIMAL_CARETAKER"
| "ANIMAL_DISSECTOR"
| "ANIMAL_TRAINER"
| "ARCHITECT"
| "ARMORER"
| "BEEKEEPER"
| "BLACKSMITH"
| "BONE_CARVER"
| "BONE_SETTER"
| "BOWYER"
| "BREWER"
| "BUTCHER"
| "CARPENTER"
| "CHEESE_MAKER"
| "CLERK"
| "CLOTHIER"
| "COOK"
| "CRAFTSMAN"
| "DIAGNOSER"
| "DOCTOR"
| "DYER"
| "ENGINEER"
| "ENGRAVER"
| "FARMER"
| "FISHERMAN"
| "FISHERY_WORKER"
| "FISH_CLEANER"
| "FISH_DISSECTOR"
| "FURNACE_OPERATOR"
| "GELDER"
| "GEM_CUTTER"
| "GEM_SETTER"
| "GLASSMAKER"
| "GLAZER"
| "GUILDREP"
| "HERBALIST"
| "HUNTER"
| "JEWELER"
| "LEATHERWORKER"
| "LYE_MAKER"
| "MASON"
| "MECHANIC"
| "MERCHANT"
| "MERCHANTBARON"
| "MERCHANT_NOBILITY"
| "MERCHANTPRINCE"
| "METALCRAFTER"
| "METALSMITH"
| "MILKER"
| "MILLER"
| "MINER"
| "OUTPOSTLIAISON"
| "PLANTER"
| "POTASH_MAKER"
| "POTTER"
| "PRESSER"
| "PUMP_OPERATOR"
| "RANGER"
| "SHEARER"
| "SIEGE_ENGINEER"
| "SIEGE_OPERATOR"
| "SOAP_MAKER"
| "SPINNER"
| "STONECRAFTER"
| "STONEWORKER"
| "STRAND_EXTRACTOR"
| "SURGEON"
| "SUTURER"
| "TANNER"
| "TAX_COLLECTOR"
| "THRESHER"
| "TRADER"
| "TRAPPER"
| "WAX_WORKER"
| "WEAPONSMITH"
| "WEAVER"
| "WOODCRAFTER"
| "WOODCUTTER"
| "WOODWORKER"
| "WOOD_BURNER"
| "AXEMAN"
| "BLOWGUNMAN"
| "BOWMAN"
| "CHAMPION"
| "CROSSBOWMAN"
| "HAMMERMAN"
| "LASHER"
| "MACEMAN"
| "MASTER_AXEMAN"
| "MASTER_BLOWGUNMAN"
| "MASTER_BOWMAN"
| "MASTER_CROSSBOWMAN"
| "MASTER_HAMMERMAN"
| "MASTER_LASHER"
| "MASTER_MACEMAN"
| "MASTER_PIKEMAN"
| "MASTER_SPEARMAN"
| "MASTER_SWORDSMAN"
| "MASTER_THIEF"
| "MASTER_WRESTLER"
| "PIKEMAN"
| "RECRUIT"
| "SPEARMAN"
| "SWORDSMAN"
| "THIEF"
| "TRAINED_HUNTER"
| "TRAINED_WAR"
| "WRESTLER"
| "ACOLYTE"
| "ADVISOR"
| "BARON"
| "BARONESS"
| "BARONESS_CONSORT"
| "BARON_CONSORT"
| "BEAST_HUNTER"
| "BOOKKEEPER"
| "BROKER"
| "CAPTAIN"
| "CAPTAIN_OF_THE_GUARD"
| "CHIEF_MEDICAL_DWARF"
| "COUNT"
| "COUNTESS"
| "COUNTESS_CONSORT"
| "COUNT_CONSORT"
| "CRIMINAL"
| "DIPLOMAT"
| "DRUID"
| "DUCHESS"
| "DUCHESS_CONSORT"
| "DUKE"
| "DUKE_CONSORT"
| "EXECUTIONER"
| "EXPEDITION_LEADER"
| "GENERAL"
| "HAMMERER"
| "HIGH_PRIEST"
| "HOARDMASTER"
| "KING"
| "KING_CONSORT"
| "LEADER"
| "LIEUTENANT"
| "MANAGER"
| "MAYOR"
| "MILITIA_CAPTAIN"
| "MILITIA_COMMANDER"
| "MONARCH"
| "MONARCH_CONSORT"
| "MONSTER_SLAYER"
| "OUTPOST_LIAISON"
| "PRIEST"
| "PRISONER"
| "QUEEN"
| "QUEEN_CONSORT"
| "RANGER_CAPTAIN"
| "SCOUT"
| "SHERIFF"
| "SLAVE"
| "SNATCHER"
| "TREASURER"
| "ASTRONOMER"
| "BARD"
| "BOOKBINDER"
| "CHEMIST"
| "DANCER"
| "GEOGRAPHER"
| "HISTORIAN"
| "MATHEMATICIAN"
| "MESSENGER"
| "MONK"
| "NATURALIST"
| "PAPERMAKER"
| "PEDDLER"
| "PERFORMER"
| "PHILOSOPHER"
| "PILGRIM"
| "POET"
| "PROPHET"
| "SAGE"
| "SCHOLAR"
| "SCRIBE"
| "TAVERN_KEEPER"
| "CUSTOM_OFFICIAL_0"
| "CUSTOM_OFFICIAL_1"
| "CUSTOM_OFFICIAL_2"
| "CUSTOM_OFFICIAL_3"
| "CUSTOM_OFFICIAL_4"
| "CUSTOM_OFFICIAL_5"
| "CUSTOM_OFFICIAL_6"
| "CUSTOM_OFFICIAL_7"
| "CUSTOM_OFFICIAL_8"
| "CUSTOM_OFFICIAL_9"
| "CUSTOM_OFFICIAL_10"
| "CUSTOM_MARKET_OFFICIAL_0"
| "CUSTOM_MARKET_OFFICIAL_1"
| "CUSTOM_MARKET_OFFICIAL_2"
| "CUSTOM_MARKET_OFFICIAL_3"
| "CUSTOM_MARKET_OFFICIAL_4"
| "CUSTOM_MARKET_OFFICIAL_5"
| "DUNGEONMASTER"
| "FORCED_ADMINISTRATOR"
| "FORMER_MEMBER"
| "FORMER_MERCENARY"
| "FORMER_PRISONER"
| "FORMER_SLAVE"
| "HANGOUT"
| "HOME"
| "MERCENARY"
| "MEMBER"
| "ENEMY"
| "SEAT_OF_POWER"
| "SHOPKEEPER"
| "WANDERER") => {
if !primary_token_filled {
if let Some(primary_token_ref) = primary_token_ref {
diagnostics.add_message(
DMExtraInfo {
range: node.get_range(),
message_template_data: hash_map! {
"expected_tokens" => format!("`{}`", primary_token_ref),
},
},
"token_is_missing",
);
} else {
diagnostics.add_message(
DMExtraInfo {
range: node.get_range(),
message_template_data: hash_map! {
"expected_tokens" => "<Unknown>".to_owned(),
},
},
"token_is_missing",
);
}
return (LoopControl::ErrBreak, new_self);
}
let value: Result<Box<CreatureGraphicsTokenArg>, _> = TokenDeserialize::deserialize_tokens(
cursor,
source,
diagnostics,
);
if let Ok(value) = value {
match new_self.other_graphics_tokens.entry(token_ref.to_owned()) {
Entry::Occupied(mut occupied_entry) => {
let entry = occupied_entry.get_mut();
entry.push(*value);
},
Entry::Vacant(empty_entry) => {
empty_entry.insert(vec![*value]);
}
}
}
return (LoopControl::Continue, new_self);
}
_ => {
if *new_self == Self::default() {
return (LoopControl::ErrBreak, new_self);
}
return (LoopControl::Break, new_self);
}
}
(LoopControl::DoNothing, new_self)
}
fn get_allowed_tokens() -> Option<Vec<String>> {
Some(vec![
"CREATURE_GRAPHICS".to_owned(),
"DEFAULT".to_owned(),
"ADVENTURER".to_owned(),
"GUARD".to_owned(),
"ROYALGUARD".to_owned(),
"ANIMATED".to_owned(),
"GHOST".to_owned(),
"STANDARD".to_owned(),
"CHILD".to_owned(),
"BABY".to_owned(),
"DRUNK".to_owned(),
"ADMINISTRATOR".to_owned(),
"ALCHEMIST".to_owned(),
"ANIMAL_CARETAKER".to_owned(),
"ANIMAL_DISSECTOR".to_owned(),
"ANIMAL_TRAINER".to_owned(),
"ARCHITECT".to_owned(),
"ARMORER".to_owned(),
"BEEKEEPER".to_owned(),
"BLACKSMITH".to_owned(),
"BONE_CARVER".to_owned(),
"BONE_SETTER".to_owned(),
"BOWYER".to_owned(),
"BREWER".to_owned(),
"BUTCHER".to_owned(),
"CARPENTER".to_owned(),
"CHEESE_MAKER".to_owned(),
"CLERK".to_owned(),
"CLOTHIER".to_owned(),
"COOK".to_owned(),
"CRAFTSMAN".to_owned(),
"DIAGNOSER".to_owned(),
"DOCTOR".to_owned(),
"DYER".to_owned(),
"ENGINEER".to_owned(),
"ENGRAVER".to_owned(),
"FARMER".to_owned(),
"FISHERMAN".to_owned(),
"FISHERY_WORKER".to_owned(),
"FISH_CLEANER".to_owned(),
"FISH_DISSECTOR".to_owned(),
"FURNACE_OPERATOR".to_owned(),
"GELDER".to_owned(),
"GEM_CUTTER".to_owned(),
"GEM_SETTER".to_owned(),
"GLASSMAKER".to_owned(),
"GLAZER".to_owned(),
"GUILDREP".to_owned(),
"HERBALIST".to_owned(),
"HUNTER".to_owned(),
"JEWELER".to_owned(),
"LEATHERWORKER".to_owned(),
"LYE_MAKER".to_owned(),
"MASON".to_owned(),
"MECHANIC".to_owned(),
"MERCHANT".to_owned(),
"MERCHANTBARON".to_owned(),
"MERCHANT_NOBILITY".to_owned(),
"MERCHANTPRINCE".to_owned(),
"METALCRAFTER".to_owned(),
"METALSMITH".to_owned(),
"MILKER".to_owned(),
"MILLER".to_owned(),
"MINER".to_owned(),
"OUTPOSTLIAISON".to_owned(),
"PLANTER".to_owned(),
"POTASH_MAKER".to_owned(),
"POTTER".to_owned(),
"PRESSER".to_owned(),
"PUMP_OPERATOR".to_owned(),
"RANGER".to_owned(),
"SHEARER".to_owned(),
"SIEGE_ENGINEER".to_owned(),
"SIEGE_OPERATOR".to_owned(),
"SOAP_MAKER".to_owned(),
"SPINNER".to_owned(),
"STONECRAFTER".to_owned(),
"STONEWORKER".to_owned(),
"STRAND_EXTRACTOR".to_owned(),
"SURGEON".to_owned(),
"SUTURER".to_owned(),
"TANNER".to_owned(),
"TAX_COLLECTOR".to_owned(),
"THRESHER".to_owned(),
"TRADER".to_owned(),
"TRAPPER".to_owned(),
"WAX_WORKER".to_owned(),
"WEAPONSMITH".to_owned(),
"WEAVER".to_owned(),
"WOODCRAFTER".to_owned(),
"WOODCUTTER".to_owned(),
"WOODWORKER".to_owned(),
"WOOD_BURNER".to_owned(),
"AXEMAN".to_owned(),
"BLOWGUNMAN".to_owned(),
"BOWMAN".to_owned(),
"CHAMPION".to_owned(),
"CROSSBOWMAN".to_owned(),
"HAMMERMAN".to_owned(),
"LASHER".to_owned(),
"MACEMAN".to_owned(),
"MASTER_AXEMAN".to_owned(),
"MASTER_BLOWGUNMAN".to_owned(),
"MASTER_BOWMAN".to_owned(),
"MASTER_CROSSBOWMAN".to_owned(),
"MASTER_HAMMERMAN".to_owned(),
"MASTER_LASHER".to_owned(),
"MASTER_MACEMAN".to_owned(),
"MASTER_PIKEMAN".to_owned(),
"MASTER_SPEARMAN".to_owned(),
"MASTER_SWORDSMAN".to_owned(),
"MASTER_THIEF".to_owned(),
"MASTER_WRESTLER".to_owned(),
"PIKEMAN".to_owned(),
"RECRUIT".to_owned(),
"SPEARMAN".to_owned(),
"SWORDSMAN".to_owned(),
"THIEF".to_owned(),
"TRAINED_HUNTER".to_owned(),
"TRAINED_WAR".to_owned(),
"WRESTLER".to_owned(),
"ACOLYTE".to_owned(),
"ADVISOR".to_owned(),
"BARON".to_owned(),
"BARONESS".to_owned(),
"BARONESS_CONSORT".to_owned(),
"BARON_CONSORT".to_owned(),
"BEAST_HUNTER".to_owned(),
"BOOKKEEPER".to_owned(),
"BROKER".to_owned(),
"CAPTAIN".to_owned(),
"CAPTAIN_OF_THE_GUARD".to_owned(),
"CHIEF_MEDICAL_DWARF".to_owned(),
"COUNT".to_owned(),
"COUNTESS".to_owned(),
"COUNTESS_CONSORT".to_owned(),
"COUNT_CONSORT".to_owned(),
"CRIMINAL".to_owned(),
"DIPLOMAT".to_owned(),
"DRUID".to_owned(),
"DUCHESS".to_owned(),
"DUCHESS_CONSORT".to_owned(),
"DUKE".to_owned(),
"DUKE_CONSORT".to_owned(),
"EXECUTIONER".to_owned(),
"EXPEDITION_LEADER".to_owned(),
"GENERAL".to_owned(),
"HAMMERER".to_owned(),
"HIGH_PRIEST".to_owned(),
"HOARDMASTER".to_owned(),
"KING".to_owned(),
"KING_CONSORT".to_owned(),
"LEADER".to_owned(),
"LIEUTENANT".to_owned(),
"MANAGER".to_owned(),
"MAYOR".to_owned(),
"MILITIA_CAPTAIN".to_owned(),
"MILITIA_COMMANDER".to_owned(),
"MONARCH".to_owned(),
"MONARCH_CONSORT".to_owned(),
"MONSTER_SLAYER".to_owned(),
"OUTPOST_LIAISON".to_owned(),
"PRIEST".to_owned(),
"PRISONER".to_owned(),
"QUEEN".to_owned(),
"QUEEN_CONSORT".to_owned(),
"RANGER_CAPTAIN".to_owned(),
"SCOUT".to_owned(),
"SHERIFF".to_owned(),
"SLAVE".to_owned(),
"SNATCHER".to_owned(),
"TREASURER".to_owned(),
"ASTRONOMER".to_owned(),
"BARD".to_owned(),
"BOOKBINDER".to_owned(),
"CHEMIST".to_owned(),
"DANCER".to_owned(),
"GEOGRAPHER".to_owned(),
"HISTORIAN".to_owned(),
"MATHEMATICIAN".to_owned(),
"MESSENGER".to_owned(),
"MONK".to_owned(),
"NATURALIST".to_owned(),
"PAPERMAKER".to_owned(),
"PEDDLER".to_owned(),
"PERFORMER".to_owned(),
"PHILOSOPHER".to_owned(),
"PILGRIM".to_owned(),
"POET".to_owned(),
"PROPHET".to_owned(),
"SAGE".to_owned(),
"SCHOLAR".to_owned(),
"SCRIBE".to_owned(),
"TAVERN_KEEPER".to_owned(),
"CUSTOM_OFFICIAL_0".to_owned(),
"CUSTOM_OFFICIAL_1".to_owned(),
"CUSTOM_OFFICIAL_2".to_owned(),
"CUSTOM_OFFICIAL_3".to_owned(),
"CUSTOM_OFFICIAL_4".to_owned(),
"CUSTOM_OFFICIAL_5".to_owned(),
"CUSTOM_OFFICIAL_6".to_owned(),
"CUSTOM_OFFICIAL_7".to_owned(),
"CUSTOM_OFFICIAL_8".to_owned(),
"CUSTOM_OFFICIAL_9".to_owned(),
"CUSTOM_OFFICIAL_10".to_owned(),
"CUSTOM_MARKET_OFFICIAL_0".to_owned(),
"CUSTOM_MARKET_OFFICIAL_1".to_owned(),
"CUSTOM_MARKET_OFFICIAL_2".to_owned(),
"CUSTOM_MARKET_OFFICIAL_3".to_owned(),
"CUSTOM_MARKET_OFFICIAL_4".to_owned(),
"CUSTOM_MARKET_OFFICIAL_5".to_owned(),
"DUNGEONMASTER".to_owned(),
"FORCED_ADMINISTRATOR".to_owned(),
"FORMER_MEMBER".to_owned(),
"FORMER_MERCENARY".to_owned(),
"FORMER_PRISONER".to_owned(),
"FORMER_SLAVE".to_owned(),
"HANGOUT".to_owned(),
"HOME".to_owned(),
"MERCENARY".to_owned(),
"MEMBER".to_owned(),
"ENEMY".to_owned(),
"SEAT_OF_POWER".to_owned(),
"SHOPKEEPER".to_owned(),
"WANDERER".to_owned(),
])
}
}