use crate::{creature::Creature, RawObject, VARIATION_ARGUMENT_RE};
use super::Requirements;
#[derive(
serde::Serialize, serde::Deserialize, Debug, Clone, PartialEq, Eq, Default, specta::Type,
)]
pub enum CreatureVariationRule {
#[default]
Unknown,
RemoveTag {
tag: String,
value: Option<String>,
},
NewTag {
tag: String,
value: Option<String>,
},
AddTag {
tag: String,
value: Option<String>,
},
ConvertTag {
tag: String,
target: Option<String>,
replacement: Option<String>,
},
ConditionalNewTag {
tag: String,
value: Option<String>,
argument_index: usize,
argument_requirement: String,
},
ConditionalAddTag {
tag: String,
value: Option<String>,
argument_index: usize,
argument_requirement: String,
},
ConditionalRemoveTag {
tag: String,
value: Option<String>,
argument_index: usize,
argument_requirement: String,
},
ConditionalConvertTag {
tag: String,
target: Option<String>,
replacement: Option<String>,
argument_index: usize,
argument_requirement: String,
},
}
impl CreatureVariationRule {
#[must_use]
#[allow(clippy::too_many_lines)]
pub fn with_args(&self, args: &[&str]) -> Self {
if args.is_empty() {
return self.clone();
}
match self {
Self::RemoveTag { tag, value } => {
Self::RemoveTag {
tag: replace_args_in_string(tag, args),
value: value
.as_ref()
.map(|value| replace_args_in_string(value, args)),
}
}
Self::NewTag { tag, value } | Self::AddTag { tag, value } => {
Self::NewTag {
tag: replace_args_in_string(tag, args),
value: value
.as_ref()
.map(|value| replace_args_in_string(value, args)),
}
}
Self::ConvertTag {
tag,
target,
replacement,
} => {
Self::ConvertTag {
tag: replace_args_in_string(tag, args),
target: target
.as_ref()
.map(|value| replace_args_in_string(value, args)),
replacement: replacement
.as_ref()
.map(|value| replace_args_in_string(value, args)),
}
}
Self::ConditionalRemoveTag {
tag,
value,
argument_requirement,
argument_index,
} => {
Self::ConditionalRemoveTag {
tag: replace_args_in_string(tag, args),
value: value
.as_ref()
.map(|value| replace_args_in_string(value, args)),
argument_requirement: String::from(
VARIATION_ARGUMENT_RE.replace_all(
argument_requirement.as_str(),
|caps: ®ex::Captures| argument_as_string(caps, args),
),
),
argument_index: *argument_index,
}
}
Self::ConditionalNewTag {
tag,
value,
argument_requirement,
argument_index,
}
| Self::ConditionalAddTag {
tag,
value,
argument_requirement,
argument_index,
} => {
Self::ConditionalNewTag {
tag: replace_args_in_string(tag, args),
value: value
.as_ref()
.map(|value| replace_args_in_string(value, args)),
argument_requirement: String::from(
VARIATION_ARGUMENT_RE.replace_all(
argument_requirement.as_str(),
|caps: ®ex::Captures| argument_as_string(caps, args),
),
),
argument_index: *argument_index,
}
}
Self::ConditionalConvertTag {
tag,
target,
replacement,
argument_index,
argument_requirement,
} => {
Self::ConditionalConvertTag {
tag: replace_args_in_string(tag, args),
target: target
.as_ref()
.map(|value| replace_args_in_string(value, args)),
replacement: replacement
.as_ref()
.map(|value| replace_args_in_string(value, args)),
argument_requirement: String::from(
VARIATION_ARGUMENT_RE.replace_all(
argument_requirement.as_str(),
|caps: ®ex::Captures| argument_as_string(caps, args),
),
),
argument_index: *argument_index,
}
}
Self::Unknown => {
Self::Unknown
}
}
}
pub fn apply(&self, creature: &mut Creature, args: &[&str]) {
match self.with_args(args) {
Self::RemoveTag { tag, .. } => {
remove_tag(creature, &tag);
}
Self::NewTag { tag, value } | Self::AddTag { tag, value } => {
apply_new_tag(creature, &tag, value.as_deref());
}
Self::ConvertTag {
tag,
target,
replacement,
} => convert_tag(creature, &tag, target.as_deref(), replacement.as_deref()),
Self::ConditionalNewTag {
tag,
value,
argument_index,
argument_requirement,
}
| Self::ConditionalAddTag {
tag,
value,
argument_index,
argument_requirement,
} => {
if args.len() < argument_index {
tracing::warn!(
"Creature Variation Argument index {} is out of bounds for {:?}",
argument_index,
args
);
return;
}
if let Some(argument_value) = args.get(argument_index - 1) {
if argument_value == &argument_requirement {
apply_new_tag(creature, &tag, value.as_deref());
}
}
}
Self::ConditionalRemoveTag {
tag,
argument_index,
argument_requirement,
..
} => {
if args.len() < argument_index {
tracing::warn!(
"Creature Variation Argument index {} is out of bounds for {:?}",
argument_index,
args
);
return;
}
if let Some(argument_value) = args.get(argument_index - 1) {
if argument_value == &argument_requirement {
remove_tag(creature, &tag);
}
}
}
Self::ConditionalConvertTag {
tag,
target,
replacement,
argument_index,
argument_requirement,
} => {
if args.len() < argument_index {
tracing::warn!(
"Creature Variation Argument index {} is out of bounds for {:?}",
argument_index,
args
);
return;
}
if let Some(argument_value) = args.get(argument_index - 1) {
if argument_value == &argument_requirement {
convert_tag(creature, &tag, target.as_deref(), replacement.as_deref());
}
}
}
Self::Unknown => {}
}
}
}
fn replace_args_in_string(string: &str, args: &[&str]) -> String {
VARIATION_ARGUMENT_RE
.replace_all(string, |caps: ®ex::Captures| {
argument_as_string(caps, args)
})
.to_string()
}
fn apply_new_tag(creature: &mut Creature, tag: &str, value: Option<&str>) {
(creature as &mut dyn Requirements).add_tag_and_value(tag, value.unwrap_or_default());
}
fn remove_tag(creature: &mut Creature, tag: &str) {
(creature as &mut dyn Requirements).remove_tag(tag);
}
fn convert_tag(
creature: &mut Creature,
tag: &str,
target: Option<&str>,
replacement: Option<&str>,
) {
if let Some(target) = target {
if let Some(replacement) = replacement {
tracing::trace!(
"Converting tag {}:{} to {}:{} on creature {}",
tag,
target,
tag,
replacement,
creature.get_identifier()
);
(creature as &mut dyn Requirements).remove_tag_and_value(tag, target);
(creature as &mut dyn Requirements).add_tag_and_value(tag, replacement);
} else {
tracing::trace!(
"Converting tag {}:{} to {}:{} on creature {}",
tag,
target,
replacement.unwrap_or_default(),
target,
creature.get_identifier(),
);
(creature as &mut dyn Requirements).remove_tag_and_value(tag, target);
(creature as &mut dyn Requirements).add_tag_and_value(tag, target);
}
} else {
tracing::trace!(
"Converting tag {} to {} on creature {}",
tag,
replacement.unwrap_or_default(),
creature.get_identifier()
);
(creature as &mut dyn Requirements).remove_tag(tag);
(creature as &mut dyn Requirements).add_tag(replacement.unwrap_or_default());
}
}
fn argument_as_string(caps: ®ex::Captures, args: &[&str]) -> String {
if let Some(index) = caps.get(1) {
let index = index.as_str().parse::<usize>().unwrap_or_default();
if let Some(argument_value) = args.get(index - 1) {
return (*argument_value).to_string();
}
}
if let Some(arg) = caps.get(0) {
tracing::warn!(
"Creature Variation Argument is invalid. Argument captured: '{}'",
arg.as_str()
);
return arg.as_str().to_string();
}
String::new()
}