785 lines
23 KiB
Rust
785 lines
23 KiB
Rust
use crate::{BlueprintEntity, BlueprintPosition};
|
|
use clap::ValueEnum;
|
|
use factorio_core::{
|
|
aabb::AABB,
|
|
beltoptions::Beltspeed,
|
|
misc::PositionMap,
|
|
pathfield::PathField,
|
|
prelude::*,
|
|
quaterdirection::QuaterDirection,
|
|
visualize::{Color, Visualization},
|
|
};
|
|
use std::{
|
|
collections::{HashMap, HashSet},
|
|
sync::atomic::AtomicUsize,
|
|
};
|
|
|
|
mod power_connection;
|
|
mod roboports;
|
|
pub mod serde;
|
|
mod visualize;
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
pub enum UndergroundType {
|
|
Input,
|
|
Output,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ValueEnum)]
|
|
pub enum InserterType {
|
|
Burner,
|
|
Normal,
|
|
Long,
|
|
Fast,
|
|
Bulk,
|
|
Stack,
|
|
}
|
|
|
|
impl InserterType {
|
|
fn string(&self) -> String {
|
|
match self {
|
|
InserterType::Burner => "burner-inserter",
|
|
InserterType::Normal => "inserter",
|
|
InserterType::Long => "long-handed-inserter",
|
|
InserterType::Fast => "fast-inserter",
|
|
InserterType::Bulk => "bulk-inserter",
|
|
InserterType::Stack => "stack-inserter",
|
|
}
|
|
.to_owned()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ValueEnum)]
|
|
pub enum ChestType {
|
|
Wood,
|
|
Iron,
|
|
Steel,
|
|
}
|
|
|
|
impl ChestType {
|
|
fn string(&self) -> String {
|
|
match self {
|
|
ChestType::Wood => "wooden-chest",
|
|
ChestType::Iron => "iron-chest",
|
|
ChestType::Steel => "steel-chest",
|
|
}
|
|
.to_owned()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
pub enum ElectricPoleType {
|
|
Small,
|
|
Medium,
|
|
Big,
|
|
Substation,
|
|
}
|
|
|
|
impl ElectricPoleType {
|
|
fn string(&self) -> String {
|
|
match self {
|
|
ElectricPoleType::Small => "small-electric-pole",
|
|
ElectricPoleType::Medium => "medium-electric-pole",
|
|
ElectricPoleType::Big => "big-electric-pole",
|
|
ElectricPoleType::Substation => "substation",
|
|
}
|
|
.to_owned()
|
|
}
|
|
|
|
fn size(&self) -> Position {
|
|
match self {
|
|
ElectricPoleType::Small | ElectricPoleType::Medium => Position::new(2, 2),
|
|
ElectricPoleType::Big | ElectricPoleType::Substation => Position::new(4, 4),
|
|
}
|
|
}
|
|
|
|
fn alignment(&self) -> PositionType {
|
|
match self {
|
|
ElectricPoleType::Small | ElectricPoleType::Medium => 1,
|
|
ElectricPoleType::Big | ElectricPoleType::Substation => 0,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, ValueEnum)]
|
|
pub enum Quality {
|
|
Normal,
|
|
Uncommon,
|
|
Rare,
|
|
Epic,
|
|
Legendary,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
pub enum RailType {
|
|
Straight,
|
|
CurvedA,
|
|
CurvedB,
|
|
RailSignal,
|
|
ChainSignal,
|
|
}
|
|
|
|
impl RailType {
|
|
fn string(&self) -> String {
|
|
match self {
|
|
RailType::Straight => "straight-rail",
|
|
RailType::CurvedA => "curved-rail-a",
|
|
RailType::CurvedB => "curved-rail-b",
|
|
RailType::RailSignal => "rail-signal",
|
|
RailType::ChainSignal => "rail-chain-signal",
|
|
}
|
|
.to_string()
|
|
}
|
|
|
|
fn size(&self) -> Position {
|
|
Position::new(12, 12)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
enum DirectionType {
|
|
Dir(Direction),
|
|
QuarterDir(QuaterDirection),
|
|
}
|
|
|
|
impl DirectionType {
|
|
fn unwrap_dir(&self) -> Direction {
|
|
match self {
|
|
DirectionType::Dir(direction) => *direction,
|
|
DirectionType::QuarterDir(_quater_direction) => panic!(),
|
|
}
|
|
}
|
|
|
|
fn get_index(&self) -> u8 {
|
|
match self {
|
|
DirectionType::Dir(direction) => direction.get_index() * 4,
|
|
DirectionType::QuarterDir(quater_direction) => quater_direction.to_int_direction(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct Entity {
|
|
entity: EntityType,
|
|
position: Position,
|
|
direction: DirectionType,
|
|
quality: Quality,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub enum EntityType {
|
|
Belt(Beltspeed),
|
|
UndergroundBelt(Beltspeed, UndergroundType),
|
|
Splitter {
|
|
beltspeed: Beltspeed,
|
|
input_priority_left: Option<bool>,
|
|
output_priority_left: Option<bool>,
|
|
filter: Option<String>,
|
|
},
|
|
ElectricPole(ElectricPoleType),
|
|
Inserter {
|
|
inserter_type: InserterType,
|
|
override_stack_size: Option<u8>,
|
|
},
|
|
Production {
|
|
name: String,
|
|
recipe: Option<String>,
|
|
size: Position,
|
|
},
|
|
Rail {
|
|
rail_type: RailType,
|
|
},
|
|
Roboport,
|
|
Chest(ChestType),
|
|
Unknown {
|
|
name: String,
|
|
size: Position,
|
|
misc: HashMap<String, serde_json::Value>,
|
|
},
|
|
}
|
|
|
|
impl Entity {
|
|
pub fn new(entity: EntityType, position: Position, direction: Direction) -> Self {
|
|
Self {
|
|
entity,
|
|
position,
|
|
direction: DirectionType::Dir(direction),
|
|
quality: Quality::Normal,
|
|
}
|
|
}
|
|
|
|
pub fn new_quarter_direction(
|
|
entity: EntityType,
|
|
position: Position,
|
|
direction: QuaterDirection,
|
|
) -> Self {
|
|
Self {
|
|
entity,
|
|
position,
|
|
direction: DirectionType::QuarterDir(direction),
|
|
quality: Quality::Normal,
|
|
}
|
|
}
|
|
|
|
pub fn new_belt(beltspeed: Beltspeed, position: Position, direction: Direction) -> Self {
|
|
Self::new(EntityType::Belt(beltspeed), position, direction)
|
|
}
|
|
|
|
pub fn new_underground_belt(
|
|
beltspeed: Beltspeed,
|
|
underground_type: UndergroundType,
|
|
position: Position,
|
|
direction: Direction,
|
|
) -> Self {
|
|
Self::new(
|
|
EntityType::UndergroundBelt(beltspeed, underground_type),
|
|
position,
|
|
direction,
|
|
)
|
|
}
|
|
|
|
pub fn new_chest(chest_type: ChestType, position: Position) -> Self {
|
|
Self::new(EntityType::Chest(chest_type), position, Direction::Up)
|
|
}
|
|
|
|
pub fn new_splitter(beltspeed: Beltspeed, position: Position, direction: Direction) -> Self {
|
|
Self::new(
|
|
EntityType::Splitter {
|
|
beltspeed,
|
|
input_priority_left: None,
|
|
output_priority_left: None,
|
|
filter: None,
|
|
},
|
|
position,
|
|
direction,
|
|
)
|
|
}
|
|
|
|
pub fn new_splitter_with_priority(
|
|
beltspeed: Beltspeed,
|
|
position: Position,
|
|
direction: Direction,
|
|
input_priority_left: Option<bool>,
|
|
output_priority_left: Option<bool>,
|
|
filter: Option<String>,
|
|
) -> Self {
|
|
Self::new(
|
|
EntityType::Splitter {
|
|
beltspeed,
|
|
input_priority_left,
|
|
output_priority_left,
|
|
filter,
|
|
},
|
|
position,
|
|
direction,
|
|
)
|
|
}
|
|
pub fn new_inserter(
|
|
inserter_type: InserterType,
|
|
override_stack_size: Option<u8>,
|
|
position: Position,
|
|
direction: Direction,
|
|
) -> Self {
|
|
Self::new(
|
|
EntityType::Inserter {
|
|
inserter_type,
|
|
override_stack_size,
|
|
},
|
|
position,
|
|
direction,
|
|
)
|
|
}
|
|
|
|
pub fn new_electric_pole(electric_pole_type: ElectricPoleType, position: Position) -> Self {
|
|
Self::new(
|
|
EntityType::ElectricPole(electric_pole_type),
|
|
position,
|
|
Direction::Up,
|
|
)
|
|
}
|
|
|
|
pub fn new_production(
|
|
name: impl AsRef<str>,
|
|
recipe: Option<impl AsRef<str>>,
|
|
position: Position,
|
|
direction: Direction,
|
|
size: Position,
|
|
) -> Self {
|
|
Self::new(
|
|
EntityType::Production {
|
|
name: name.as_ref().to_owned(),
|
|
recipe: recipe.map(|s| s.as_ref().to_owned()),
|
|
size,
|
|
},
|
|
position,
|
|
direction,
|
|
)
|
|
}
|
|
|
|
pub fn new_rail(
|
|
rail_type: RailType,
|
|
position: Position,
|
|
direction: impl Into<QuaterDirection>,
|
|
) -> Self {
|
|
Self::new_quarter_direction(EntityType::Rail { rail_type }, position, direction.into())
|
|
}
|
|
|
|
pub fn new_roboport(position: Position) -> Self {
|
|
Self::new(EntityType::Roboport, position, Direction::Up)
|
|
}
|
|
|
|
pub fn new_unknown(
|
|
name: impl AsRef<str>,
|
|
position: Position,
|
|
direction: Direction,
|
|
size: Position,
|
|
) -> Self {
|
|
Self::new(
|
|
EntityType::Unknown {
|
|
name: name.as_ref().to_owned(),
|
|
size,
|
|
misc: HashMap::new(),
|
|
},
|
|
position,
|
|
direction,
|
|
)
|
|
}
|
|
|
|
pub fn quality(mut self, quality: Quality) -> Self {
|
|
self.quality = quality;
|
|
self
|
|
}
|
|
|
|
pub fn get_name(&self) -> String {
|
|
match &self.entity {
|
|
EntityType::Belt(beltspeed) => beltspeed.string(),
|
|
EntityType::UndergroundBelt(beltspeed, _underground_type) => {
|
|
beltspeed.string_underground()
|
|
}
|
|
EntityType::Splitter {
|
|
beltspeed,
|
|
input_priority_left: _,
|
|
output_priority_left: _,
|
|
filter: _,
|
|
} => beltspeed.string_splitter(),
|
|
EntityType::Unknown {
|
|
name,
|
|
size: _,
|
|
misc: _,
|
|
} => name.clone(),
|
|
EntityType::ElectricPole(electric_pole_type) => electric_pole_type.string(),
|
|
EntityType::Inserter {
|
|
inserter_type,
|
|
override_stack_size: _,
|
|
} => inserter_type.string(),
|
|
EntityType::Production {
|
|
name,
|
|
recipe: _,
|
|
size: _,
|
|
} => name.clone(),
|
|
EntityType::Rail { rail_type } => rail_type.string(),
|
|
EntityType::Roboport => "roboport".to_string(),
|
|
EntityType::Chest(chest_type) => chest_type.string(),
|
|
}
|
|
}
|
|
|
|
pub fn get_maybe_underground_type_string(&self) -> Option<String> {
|
|
match &self.entity {
|
|
EntityType::UndergroundBelt(_, underground_type) => match underground_type {
|
|
UndergroundType::Input => Some(String::from("input")),
|
|
UndergroundType::Output => Some(String::from("output")),
|
|
},
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn get_maybe_override_stack_size(&self) -> Option<u8> {
|
|
match &self.entity {
|
|
EntityType::Inserter {
|
|
inserter_type: _,
|
|
override_stack_size,
|
|
} => *override_stack_size,
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn get_maybe_recipe(&self) -> Option<String> {
|
|
match &self.entity {
|
|
EntityType::Production {
|
|
name: _,
|
|
recipe,
|
|
size: _,
|
|
} => recipe.clone(),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn get_maybe_input_priority(&self) -> Option<String> {
|
|
match self.entity {
|
|
EntityType::Splitter {
|
|
beltspeed: _,
|
|
input_priority_left,
|
|
output_priority_left: _,
|
|
filter: _,
|
|
} => match input_priority_left {
|
|
Some(true) => Some("left".to_string()),
|
|
Some(false) => Some("right".to_string()),
|
|
None => None,
|
|
},
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn get_maybe_output_priority(&self) -> Option<String> {
|
|
match self.entity {
|
|
EntityType::Splitter {
|
|
beltspeed: _,
|
|
input_priority_left: _,
|
|
output_priority_left,
|
|
filter: _,
|
|
} => match output_priority_left {
|
|
Some(true) => Some("left".to_string()),
|
|
Some(false) => Some("right".to_string()),
|
|
None => None,
|
|
},
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn get_maybe_filter(&self) -> Option<String> {
|
|
match &self.entity {
|
|
EntityType::Splitter {
|
|
beltspeed: _,
|
|
input_priority_left: _,
|
|
output_priority_left: _,
|
|
filter,
|
|
} => filter.clone(),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub fn size(&self) -> Position {
|
|
match &self.entity {
|
|
EntityType::Splitter { .. } => Position::new(4, 2),
|
|
EntityType::Unknown {
|
|
name: _,
|
|
size,
|
|
misc: _,
|
|
} => *size,
|
|
EntityType::Production {
|
|
name: _,
|
|
recipe: _,
|
|
size,
|
|
} => *size,
|
|
EntityType::ElectricPole(electric_pole_type) => electric_pole_type.size(),
|
|
EntityType::Rail { rail_type } => rail_type.size(),
|
|
EntityType::Roboport => Position::new(8, 8),
|
|
_ => Position::new(2, 2),
|
|
}
|
|
}
|
|
|
|
pub fn transform(&mut self, transform: Transformation) {
|
|
self.position = self.position.transform(transform);
|
|
self.direction = match self.direction {
|
|
DirectionType::Dir(direction) => DirectionType::Dir(direction.transform(transform)),
|
|
DirectionType::QuarterDir(quater_direction) => {
|
|
DirectionType::QuarterDir(quater_direction.transform(transform))
|
|
}
|
|
};
|
|
}
|
|
|
|
pub fn get_aabb(&self) -> AABB {
|
|
let halve_size = self.size() / 2;
|
|
match self.direction {
|
|
DirectionType::Dir(direction) => match direction {
|
|
Direction::Up | Direction::Down => AABB::new(
|
|
self.position - halve_size,
|
|
self.position + halve_size - Position::new(1, 1),
|
|
),
|
|
Direction::Right | Direction::Left => AABB::new(
|
|
self.position - Position::new(halve_size.y, halve_size.x),
|
|
self.position + Position::new(halve_size.y, halve_size.x) - Position::new(1, 1),
|
|
),
|
|
},
|
|
DirectionType::QuarterDir(_) => {
|
|
AABB::new(self.position - halve_size, self.position + halve_size)
|
|
}
|
|
}
|
|
}
|
|
fn visualize(&self, v: &mut Visualization, offset: Position) {
|
|
match &self.entity {
|
|
EntityType::Belt(_beltspeed) => {
|
|
v.add_symbol(
|
|
(self.position - Position::new(1, 1)) / 2 + offset,
|
|
factorio_core::visualize::Symbol::Arrow(self.direction.unwrap_dir()),
|
|
Some(factorio_core::visualize::Color::white()),
|
|
None,
|
|
);
|
|
}
|
|
EntityType::UndergroundBelt(_beltspeed, underground_type) => match underground_type {
|
|
UndergroundType::Input => {
|
|
v.add_symbol(
|
|
(self.position - Position::new(1, 1)) / 2 + offset,
|
|
factorio_core::visualize::Symbol::ArrowEnter(self.direction.unwrap_dir()),
|
|
Some(factorio_core::visualize::Color::white()),
|
|
None,
|
|
);
|
|
}
|
|
UndergroundType::Output => {
|
|
v.add_symbol(
|
|
(self.position - Position::new(1, 1)) / 2 + offset,
|
|
factorio_core::visualize::Symbol::ArrowExit(self.direction.unwrap_dir()),
|
|
Some(factorio_core::visualize::Color::white()),
|
|
None,
|
|
);
|
|
}
|
|
},
|
|
EntityType::Splitter { .. } => (),
|
|
EntityType::ElectricPole(electric_pole_type) => match electric_pole_type {
|
|
ElectricPoleType::Small => v.add_symbol(
|
|
(self.position - Position::new(1, 1)) / 2 + offset,
|
|
factorio_core::visualize::Symbol::Char('s'),
|
|
Some(Color::cyan()),
|
|
None,
|
|
),
|
|
ElectricPoleType::Medium => v.add_symbol(
|
|
(self.position - Position::new(1, 1)) / 2 + offset,
|
|
factorio_core::visualize::Symbol::Char('m'),
|
|
Some(Color::cyan()),
|
|
None,
|
|
),
|
|
ElectricPoleType::Big => {
|
|
for (dx, dy) in [(-2, -2), (0, -2), (-2, 0), (0, 0)] {
|
|
v.add_symbol(
|
|
(self.position + Position::new(dx, dy)) / 2 + offset,
|
|
factorio_core::visualize::Symbol::Char('l'),
|
|
Some(Color::cyan()),
|
|
None,
|
|
)
|
|
}
|
|
}
|
|
ElectricPoleType::Substation => {
|
|
for (dx, dy) in [(-2, -2), (0, -2), (-2, 0), (0, 0)] {
|
|
v.add_symbol(
|
|
(self.position + Position::new(dx, dy)) / 2 + offset,
|
|
factorio_core::visualize::Symbol::Char('S'),
|
|
Some(Color::cyan()),
|
|
None,
|
|
)
|
|
}
|
|
}
|
|
},
|
|
EntityType::Roboport => {
|
|
for dx in -2..2 {
|
|
for dy in -2..2 {
|
|
v.add_symbol(
|
|
(self.position) / 2 + Position::new(dx, dy) + offset,
|
|
factorio_core::visualize::Symbol::Char('R'),
|
|
Some(Color::yellow()),
|
|
None,
|
|
)
|
|
}
|
|
}
|
|
}
|
|
_ => (),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
pub struct EntityKey(usize);
|
|
|
|
static ENTITY_COUNTER: AtomicUsize = AtomicUsize::new(0);
|
|
|
|
pub struct Blueprint {
|
|
entities: Vec<(EntityKey, Entity)>,
|
|
keys: HashMap<EntityKey, usize>,
|
|
wires: HashSet<(EntityKey, u8, EntityKey, u8)>,
|
|
}
|
|
|
|
impl Blueprint {
|
|
pub fn new() -> Self {
|
|
Self {
|
|
entities: Vec::new(),
|
|
keys: HashMap::new(),
|
|
wires: HashSet::new(),
|
|
}
|
|
}
|
|
|
|
pub fn add_entity(&mut self, entity: Entity) -> EntityKey {
|
|
let id = EntityKey(ENTITY_COUNTER.fetch_add(1, std::sync::atomic::Ordering::Relaxed));
|
|
let pos = self.entities.len();
|
|
|
|
self.keys.insert(id, pos);
|
|
self.entities.push((id, entity));
|
|
|
|
id
|
|
}
|
|
|
|
pub fn add_path<'a>(
|
|
&mut self,
|
|
path: impl IntoIterator<Item = &'a PathField>,
|
|
beltspeed: Beltspeed,
|
|
) {
|
|
for &p in path {
|
|
match p {
|
|
PathField::Belt { pos, dir } => {
|
|
self.add_entity(Entity::new_belt(
|
|
beltspeed,
|
|
2 * pos + Position::new(1, 1),
|
|
dir,
|
|
));
|
|
}
|
|
PathField::Underground { pos, dir, len } => {
|
|
self.add_entity(Entity::new_underground_belt(
|
|
beltspeed,
|
|
UndergroundType::Input,
|
|
2 * pos + Position::new(1, 1),
|
|
dir,
|
|
));
|
|
self.add_entity(Entity::new_underground_belt(
|
|
beltspeed,
|
|
UndergroundType::Output,
|
|
2 * pos.in_direction(&dir, len as PositionType) + Position::new(1, 1),
|
|
dir,
|
|
));
|
|
}
|
|
};
|
|
}
|
|
}
|
|
|
|
pub fn add_wire(&mut self, a: EntityKey, endpoint_a: u8, b: EntityKey, endpoint_b: u8) {
|
|
self.wires.insert((a, endpoint_a, b, endpoint_b));
|
|
}
|
|
|
|
pub fn add_blueprint(&mut self, other: Self) {
|
|
let previous_entities = self.entities.len();
|
|
self.entities.extend(other.entities);
|
|
self.keys.extend(
|
|
other
|
|
.keys
|
|
.into_iter()
|
|
.map(|(k, v)| (k, v + previous_entities)),
|
|
);
|
|
self.wires.extend(other.wires);
|
|
}
|
|
|
|
pub fn to_blueprint(&self) -> super::Blueprint {
|
|
let entities = self
|
|
.entities
|
|
.iter()
|
|
.enumerate()
|
|
.map(|(i, (_, e))| {
|
|
BlueprintEntity::builder(
|
|
e.get_name(),
|
|
i as u32 + 1,
|
|
BlueprintPosition::new(0.5 * e.position.x as f64, 0.5 * e.position.y as f64),
|
|
)
|
|
.direction(match e.direction {
|
|
DirectionType::Dir(direction) => 4 * direction.get_index(),
|
|
DirectionType::QuarterDir(quater_direction) => {
|
|
quater_direction.to_int_direction()
|
|
}
|
|
})
|
|
.maybe_underground_type(e.get_maybe_underground_type_string())
|
|
.maybe_override_stack_size(e.get_maybe_override_stack_size())
|
|
.maybe_recipe(e.get_maybe_recipe())
|
|
.maybe_input_priority(e.get_maybe_input_priority())
|
|
.maybe_output_priority(e.get_maybe_output_priority())
|
|
.maybe_filter(e.get_maybe_filter())
|
|
.build()
|
|
})
|
|
.collect();
|
|
|
|
let wires = self
|
|
.wires
|
|
.iter()
|
|
.map(|&(a, endpoint_a, b, endpoint_b)| {
|
|
[
|
|
self.keys[&a] as u32 + 1,
|
|
endpoint_a as u32,
|
|
self.keys[&b] as u32 + 1,
|
|
endpoint_b as u32,
|
|
]
|
|
})
|
|
.collect();
|
|
|
|
super::Blueprint::builder()
|
|
.label(String::from("test"))
|
|
.entities(entities)
|
|
.wires(wires)
|
|
.build()
|
|
}
|
|
|
|
pub fn get_aabb(&self) -> Option<AABB> {
|
|
self.entities
|
|
.iter()
|
|
.map(|(_, e)| e.get_aabb())
|
|
.reduce(AABB::combine)
|
|
}
|
|
|
|
pub fn transform(&mut self, transform: Transformation) {
|
|
for (_, e) in &mut self.entities {
|
|
e.transform(transform);
|
|
}
|
|
}
|
|
|
|
pub fn bounding_box(&self) -> AABB {
|
|
self.entities
|
|
.iter()
|
|
.map(|(_, e)| AABB::new(e.position, e.position))
|
|
.reduce(|a, b| a.combine(b))
|
|
.unwrap()
|
|
}
|
|
|
|
pub fn placeable(&self, pos: Position, size: Position) -> bool {
|
|
let aabb = AABB::new(pos - size / 2, pos + size / 2 - Position::new(1, 1));
|
|
!self
|
|
.entities
|
|
.iter()
|
|
.any(|(_, e)| AABB::collision(e.get_aabb(), aabb))
|
|
}
|
|
|
|
pub fn placibility_map(&self) -> Placibility {
|
|
let mut blocked = PositionMap::new(self.get_aabb().unwrap(), &false);
|
|
// dbg!(self.get_aabb(), self.get_aabb().unwrap().size());
|
|
|
|
for (_, e) in self.entities.iter() {
|
|
let aabb = e.get_aabb();
|
|
for y in aabb.min().y..=aabb.max().y {
|
|
for x in aabb.min().x..=aabb.max().x {
|
|
blocked[Position::new(x, y)] = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
Placibility { blocked }
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Placibility {
|
|
blocked: PositionMap<bool>,
|
|
}
|
|
|
|
impl Placibility {
|
|
pub fn placeable(&self, pos: Position, size: Position) -> bool {
|
|
let aabb = AABB::new(pos - size / 2, pos + size / 2 - Position::new(1, 1));
|
|
for y in aabb.min().y..=aabb.max().y {
|
|
for x in aabb.min().x..=aabb.max().x {
|
|
if !self.blocked.get_aabb().contains_pos(Position::new(x, y))
|
|
|| self.blocked[Position::new(x, y)]
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
true
|
|
}
|
|
}
|
|
|
|
impl Default for Blueprint {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|