#![deny(future_incompatible)]
#![deny(nonstandard_style)]
#![deny(rust_2018_idioms)]
#![deny(unsafe_code)]
#![warn(missing_docs)]
#![warn(unused)]
mod ai;
mod bitmap;
pub mod convert;
mod format;
mod header;
mod map;
mod player;
mod triggers;
mod types;
mod victory;
use format::SCXFormat;
use genie_support::{ReadStringError, WriteStringError};
use std::io::{self, Read, Write};
pub use format::{ScenarioObject, TribeScen};
pub use genie_support::{DecodeStringError, EncodeStringError};
pub use genie_support::{StringKey, UnitTypeID};
pub use header::{DLCOptions, SCXHeader};
pub use map::{Map, Tile};
pub use triggers::{Trigger, TriggerCondition, TriggerEffect, TriggerSystem};
pub use types::*;
pub use victory::{VictoryConditions, VictoryEntry, VictoryPointEntry};
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("must have a file name")]
MissingFileNameError,
#[error("unsupported format version {:?}", .0)]
UnsupportedFormatVersionError(SCXVersion),
#[error("too many disabled techs: got {}, but requested version supports up to 20", .0)]
TooManyDisabledTechsError(i32),
#[error("requested version does not support disabling techs")]
CannotDisableTechsError,
#[error("requested version does not support disabling units")]
CannotDisableUnitsError,
#[error("too many disabled buildings: got {}, but requested version supports up to {}", .0, .1)]
TooManyDisabledBuildingsError(i32, i32),
#[error("requested version does not support disabling buildings")]
CannotDisableBuildingsError,
#[error(transparent)]
DecodeStringError(#[from] DecodeStringError),
#[error(transparent)]
EncodeStringError(#[from] EncodeStringError),
#[error(transparent)]
ParseDiplomaticStanceError(#[from] ParseDiplomaticStanceError),
#[error(transparent)]
ParseDataSetError(#[from] ParseDataSetError),
#[error(transparent)]
ParseDLCPackageError(#[from] ParseDLCPackageError),
#[error(transparent)]
ParseStartingAgeError(#[from] ParseStartingAgeError),
#[error(transparent)]
ParseAIErrorCodeError(#[from] num_enum::TryFromPrimitiveError<ai::AIErrorCode>),
#[error(transparent)]
IoError(#[from] io::Error),
}
impl From<ReadStringError> for Error {
fn from(err: ReadStringError) -> Error {
match err {
ReadStringError::IoError(err) => Error::IoError(err),
ReadStringError::DecodeStringError(err) => Error::DecodeStringError(err),
}
}
}
impl From<WriteStringError> for Error {
fn from(err: WriteStringError) -> Error {
match err {
WriteStringError::IoError(err) => Error::IoError(err),
WriteStringError::EncodeStringError(err) => Error::EncodeStringError(err),
}
}
}
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Clone)]
pub struct Scenario {
format: SCXFormat,
version: VersionBundle,
}
impl Scenario {
pub fn read_from(input: impl Read) -> Result<Self> {
let format = SCXFormat::load_scenario(input)?;
let version = format.version();
Ok(Self { format, version })
}
#[deprecated = "Use Scenario::read_from instead."]
pub fn from<R: Read>(input: &mut R) -> Result<Self> {
Self::read_from(input)
}
pub fn write_to(&self, output: impl Write) -> Result<()> {
self.format.write_to(output, self.version())
}
pub fn write_to_version(&self, output: impl Write, version: &VersionBundle) -> Result<()> {
self.format.write_to(output, version)
}
#[inline]
pub fn format_version(&self) -> SCXVersion {
self.version().format
}
#[inline]
pub fn header_version(&self) -> u32 {
self.version().header
}
#[inline]
pub fn data_version(&self) -> f32 {
self.version().data
}
#[inline]
pub fn header(&self) -> &SCXHeader {
&self.format.header
}
#[inline]
pub fn description(&self) -> Option<&str> {
self.format.tribe_scen.description()
}
#[inline]
pub fn filename(&self) -> &str {
&self.format.tribe_scen.base.name
}
#[inline]
pub fn version(&self) -> &VersionBundle {
&self.version
}
#[inline]
pub fn requires_dlc(&self, dlc: DLCPackage) -> bool {
match &self.header().dlc_options {
Some(options) => options.dependencies.iter().any(|dep| *dep == dlc),
None => false,
}
}
#[inline]
pub fn mod_name(&self) -> Option<&str> {
self.format.mod_name()
}
#[inline]
pub fn objects(&self) -> impl Iterator<Item = &ScenarioObject> {
self.format
.player_objects
.iter()
.map(|list| list.iter())
.flatten()
}
#[inline]
pub fn objects_mut(&mut self) -> impl Iterator<Item = &mut ScenarioObject> {
self.format
.player_objects
.iter_mut()
.map(|list| list.iter_mut())
.flatten()
}
#[inline]
pub fn map(&self) -> &Map {
&self.format.map
}
#[inline]
pub fn map_mut(&mut self) -> &mut Map {
&mut self.format.map
}
#[inline]
pub fn triggers(&self) -> Option<&TriggerSystem> {
self.format.triggers.as_ref()
}
#[inline]
pub fn triggers_mut(&mut self) -> Option<&mut TriggerSystem> {
self.format.triggers.as_mut()
}
}