Galactica/crates/content/src/part/faction.rs

130 lines
3.3 KiB
Rust

use anyhow::{bail, Result};
use serde::Deserialize;
use std::collections::HashMap;
use crate::{handle::FactionHandle, Content, ContentBuildContext};
pub(crate) mod syntax {
use std::collections::HashMap;
use serde::Deserialize;
// Raw serde syntax structs.
// These are never seen by code outside this crate.
#[derive(Debug, Deserialize)]
pub struct Faction {
pub display_name: String,
pub color: [f32; 3],
pub relationship: HashMap<String, super::Relationship>,
}
}
/// How two factions should interact with each other.
/// Relationships are directional: the relationship of
/// `a` to `b` may not equal the relationship of `b` to `a`.
///
/// Relationships dictate how a ship of THIS faction
/// will interact with a ship of the OTHER faction.
#[derive(Debug, Deserialize, Clone, Copy)]
pub enum Relationship {
/// Attack this faction
#[serde(rename = "hostile")]
Hostile,
/// Ignore this faction
#[serde(rename = "neutral")]
Neutral,
/// Protect this faction
#[serde(rename = "friend")]
Friend,
}
/// Represents a game faction
#[derive(Debug, Clone)]
pub struct Faction {
/// The name of this faction
pub name: String,
/// This faction's color.
/// Format is RGB, with each color between 0 and 1.
pub color: [f32; 3],
/// This faction's handle
pub handle: FactionHandle,
/// Relationships between this faction and other factions
/// This is guaranteed to contain an entry for ALL factions.
pub relationships: HashMap<FactionHandle, Relationship>,
}
impl crate::Build for Faction {
type InputSyntaxType = HashMap<String, syntax::Faction>;
fn build(
factions: Self::InputSyntaxType,
_build_context: &mut ContentBuildContext,
content: &mut Content,
) -> Result<()> {
// Keeps track of position in faction array.
// This lets us build FactionHandles before finishing all factions.
let faction_names: Vec<String> = factions.keys().map(|x| x.to_owned()).collect();
// Indexing will break if this is false.
assert!(content.factions.len() == 0);
for f_idx in 0..faction_names.len() {
let faction_name = &faction_names[f_idx];
let faction = &factions[faction_name];
// Handle for this faction
let h = FactionHandle { index: f_idx };
// Compute relationships
let mut relationships = HashMap::new();
for i in 0..faction_names.len() {
let f_other = &faction_names[i];
let h_other = FactionHandle { index: i };
if let Some(r) = faction.relationship.get(f_other) {
relationships.insert(h_other, *r);
} else {
// Default relationship, if not specified
// Look at reverse direction...
let other = factions[f_other].relationship.get(faction_name);
relationships.insert(
h_other,
// ... and pick a relationship based on that.
match other {
Some(Relationship::Hostile) => Relationship::Hostile {},
_ => Relationship::Neutral {},
},
);
}
}
if faction.color[0] > 1.0
|| faction.color[0] < 0.0
|| faction.color[1] > 1.0
|| faction.color[1] < 0.0
|| faction.color[2] > 1.0
|| faction.color[2] < 0.0
{
bail!(
"Invalid color for faction `{}`. Value out of range.",
faction_name
);
}
content.factions.push(Self {
name: faction_name.to_owned(),
handle: h,
relationships,
color: faction.color,
});
}
return Ok(());
}
}