use crate::{ balancer::binary_balancer, station::{StationSpec, station}, }; use factorio_blueprint::abstraction::{ Blueprint, ElectricPoleType, Entity, RailType, UndergroundType, }; use factorio_core::{beltoptions::Beltspeed, prelude::*}; pub fn calculate_station_height( output_height: PositionType, lanes: usize, beltspeed: Beltspeed, ) -> PositionType { let station_height = match beltspeed { Beltspeed::Normal => { if lanes == 1 && output_height % 4 == 1 { 4 + ((3 + output_height) & !0b11) } else { 4 + ((15 + output_height) & !0b11) } } Beltspeed::Fast => { if lanes == 1 || lanes == 2 && output_height % 4 == 3 { 4 + ((3 + output_height) & !0b11) } else { 4 + ((9 + output_height) & !0b11) } } Beltspeed::Express | Beltspeed::Turbo => 4 + ((3 + output_height) & !0b11), }; let station_height = PositionType::max(12, station_height); assert!(station_height % 4 == 0); station_height } pub fn multistation( stations: &[StationSpec], stacker_size: usize, ) -> (Blueprint, PositionType, Vec) { let longest_train = stations .iter() .map(|s| s.train_information.locomotives + s.train_information.wagons) .max() .unwrap(); let mut blueprint = Blueprint::new(); // connection power pole blueprint.add_entity(Entity::new_electric_pole( ElectricPoleType::Big, Position::new(32, 0), )); // stacker let stacker_length = (longest_train * 5).div_ceil(2); blueprint.add_entity(Entity::new_rail( RailType::ChainSignal, Position::new(23, 1), 0, )); for i in 0..stacker_size { blueprint.add_entity(Entity::new_rail( RailType::CurvedA, Position::new(26, 4 + 8 * i as PositionType), 10, )); blueprint.add_entity(Entity::new_rail( RailType::CurvedB, Position::new(22, 14 + 8 * i as PositionType), 10, )); blueprint.add_entity(Entity::new_rail( RailType::RailSignal, Position::new(17, 15 + 8 * i as PositionType), 2, )); for j in 0..stacker_length { blueprint.add_entity(Entity::new_rail( RailType::Straight, Position::new( 16 - 4 * j as PositionType, 20 + 4 * j as PositionType + 8 * i as PositionType, ), 2, )); } blueprint.add_entity(Entity::new_rail( RailType::CurvedB, Position::new( 14 - 4 * stacker_length as PositionType, 22 + 4 * stacker_length as PositionType + 8 * i as PositionType, ), 2, )); blueprint.add_entity(Entity::new_rail( RailType::CurvedA, Position::new( 10 - 4 * stacker_length as PositionType, 32 + 4 * stacker_length as PositionType + 8 * i as PositionType, ), 2, )); blueprint.add_entity(Entity::new_rail( RailType::ChainSignal, Position::new( 15 - 4 * stacker_length as PositionType, 17 + 4 * stacker_length as PositionType + 8 * i as PositionType, ), 2, )); if i != 0 { blueprint.add_entity(Entity::new_rail( RailType::Straight, Position::new(26, 2 + 8 * (i - 1) as PositionType), 0, )); blueprint.add_entity(Entity::new_rail( RailType::Straight, Position::new(26, 6 + 8 * (i - 1) as PositionType), 0, )); blueprint.add_entity(Entity::new_rail( RailType::Straight, Position::new( 10 - 4 * stacker_length as PositionType, 30 + 4 * stacker_length as PositionType + 8 * i as PositionType, ), 0, )); blueprint.add_entity(Entity::new_rail( RailType::Straight, Position::new( 10 - 4 * stacker_length as PositionType, 34 + 4 * stacker_length as PositionType + 8 * i as PositionType, ), 0, )); } } let total_stacker_height = (26 + 4 * stacker_length + 8 * stacker_size) as PositionType; let inrail_x = 10 - 4 * stacker_length as PositionType; let outrail_x = inrail_x + 52 + 4 * (7 * longest_train).div_ceil(2) as PositionType; let mut output_heights = Vec::new(); let mut previous_station_heights = 0; // station for (i, station_spec) in stations.iter().enumerate() { // in turn blueprint.add_entity(Entity::new_rail( RailType::CurvedA, Position::new( inrail_x, total_stacker_height + 6 + previous_station_heights, ), 8, )); blueprint.add_entity(Entity::new_rail( RailType::CurvedB, Position::new( inrail_x + 4, total_stacker_height + 16 + previous_station_heights, ), 8, )); blueprint.add_entity(Entity::new_rail( RailType::CurvedB, Position::new( inrail_x + 12, total_stacker_height + 24 + previous_station_heights, ), 14, )); blueprint.add_entity(Entity::new_rail( RailType::CurvedA, Position::new( inrail_x + 22, total_stacker_height + 28 + previous_station_heights, ), 14, )); blueprint.add_entity(Entity::new_rail( RailType::RailSignal, Position::new( inrail_x + 5, total_stacker_height + 21 + previous_station_heights, ), 14, )); // out turn blueprint.add_entity(Entity::new_rail( RailType::CurvedA, Position::new( outrail_x, total_stacker_height + 6 + previous_station_heights, ), 10, )); blueprint.add_entity(Entity::new_rail( RailType::CurvedB, Position::new( outrail_x - 4, total_stacker_height + 16 + previous_station_heights, ), 10, )); blueprint.add_entity(Entity::new_rail( RailType::CurvedB, Position::new( outrail_x - 12, total_stacker_height + 24 + previous_station_heights, ), 4, )); blueprint.add_entity(Entity::new_rail( RailType::CurvedA, Position::new( outrail_x - 22, total_stacker_height + 28 + previous_station_heights, ), 4, )); blueprint.add_entity(Entity::new_rail( RailType::RailSignal, Position::new( outrail_x - 5, total_stacker_height + 21 + previous_station_heights, ), 10, )); for j in 0..((7 * longest_train).div_ceil(2) - (7 * (station_spec.train_information.locomotives + station_spec.train_information.wagons)) .div_ceil(2)) { blueprint.add_entity(Entity::new_rail( RailType::Straight, Position::new( inrail_x + 28 + 4 * j as PositionType, 28 + total_stacker_height + previous_station_heights, ), 4, )); } // station let mut b = station(station_spec); let output_height = -b.bounding_box().min().y; b.transform(Transformation::new( Direction::Down, Position::new( inrail_x + 26 + 4 * (7 * longest_train).div_ceil(2) as PositionType, 30 + total_stacker_height + previous_station_heights, ), )); blueprint.add_blueprint(b); let mut station_height = 4 + station_spec.get_space_right() + (stations.get(i + 1).map(|s| s.get_space_left()).unwrap_or(0)); // ensure station_height is divisible by 4 station_height = (station_height + 3) & !0b11; match &station_spec.station_type { crate::station::StationType::Empty(_station_type_empty) => (), crate::station::StationType::Belt(station_type_belt) => { // belt output let old_station_height = calculate_station_height( output_height, station_type_belt.lanes, station_type_belt.beltspeed, ); station_height = station_height.max(old_station_height); output_heights .push(30 + total_stacker_height + previous_station_heights + output_height); // rail crossing let (beltdirection, underground_left, underground_right) = match station_type_belt.load { true => ( Direction::Left, UndergroundType::Output, UndergroundType::Input, ), false => ( Direction::Right, UndergroundType::Input, UndergroundType::Output, ), }; let mut belt_x = outrail_x - 28 - 14 * station_spec.train_information.locomotives as PositionType - 3 + 2 * (station_spec.train_information.wagons / station_type_belt.lanes).ilog2() as PositionType; let (balancer_length, mut b) = binary_balancer(station_type_belt.lanes, station_type_belt.beltspeed); if station_type_belt.load { b.transform(Transformation::new( Direction::Down, Position::new(balancer_length, 2 * station_type_belt.lanes as PositionType), )); } b.transform(Transformation::new( Direction::Up, Position::new( belt_x - 1, 31 + total_stacker_height + previous_station_heights + output_height - 2 * station_type_belt.lanes as PositionType, ), )); blueprint.add_blueprint(b); belt_x += balancer_length; for j in 0..station_type_belt.lanes { let (underground_x, signal) = match station_height - (output_height - 2 * j as PositionType) { 5 => { blueprint.add_entity(Entity::new_underground_belt( station_type_belt.beltspeed, underground_left, Position::new( outrail_x - 23, 30 + total_stacker_height + previous_station_heights + output_height - 2 * j as PositionType, ), beltdirection, )); blueprint.add_entity(Entity::new_underground_belt( station_type_belt.beltspeed, underground_right, Position::new( outrail_x - 9, 30 + total_stacker_height + previous_station_heights + output_height - 2 * j as PositionType, ), beltdirection, )); blueprint.add_entity(Entity::new_underground_belt( station_type_belt.beltspeed, underground_left, Position::new( outrail_x - 7, 30 + total_stacker_height + previous_station_heights + output_height - 2 * j as PositionType, ), beltdirection, )); (outrail_x - 23, false) } 7 => { blueprint.add_entity(Entity::new_underground_belt( station_type_belt.beltspeed, underground_left, Position::new( outrail_x - 17, 30 + total_stacker_height + previous_station_heights + output_height - 2 * j as PositionType, ), beltdirection, )); if station_type_belt.beltspeed != Beltspeed::Turbo { blueprint.add_entity(Entity::new_underground_belt( station_type_belt.beltspeed, underground_right, Position::new( outrail_x - 7, 30 + total_stacker_height + previous_station_heights + output_height - 2 * j as PositionType, ), beltdirection, )); blueprint.add_entity(Entity::new_underground_belt( station_type_belt.beltspeed, underground_left, Position::new( outrail_x - 5, 30 + total_stacker_height + previous_station_heights + output_height - 2 * j as PositionType, ), beltdirection, )); } (outrail_x - 17, false) } 9 => { blueprint.add_entity(Entity::new_underground_belt( station_type_belt.beltspeed, underground_left, Position::new( outrail_x - 15, 30 + total_stacker_height + previous_station_heights + output_height - 2 * j as PositionType, ), beltdirection, )); (outrail_x - 15, false) } 11 => { blueprint.add_entity(Entity::new_underground_belt( station_type_belt.beltspeed, underground_left, Position::new( outrail_x - 11, 30 + total_stacker_height + previous_station_heights + output_height - 2 * j as PositionType, ), beltdirection, )); (outrail_x - 11, false) } 13 => { blueprint.add_entity(Entity::new_underground_belt( station_type_belt.beltspeed, underground_left, Position::new( outrail_x - 9, 30 + total_stacker_height + previous_station_heights + output_height - 2 * j as PositionType, ), beltdirection, )); (outrail_x - 9, false) } 15 => { blueprint.add_entity(Entity::new_underground_belt( station_type_belt.beltspeed, underground_left, Position::new( outrail_x - 9, 30 + total_stacker_height + previous_station_heights + output_height - 2 * j as PositionType, ), beltdirection, )); (outrail_x - 9, true) } 17 => { blueprint.add_entity(Entity::new_underground_belt( station_type_belt.beltspeed, underground_left, Position::new( outrail_x - 7, 30 + total_stacker_height + previous_station_heights + output_height - 2 * j as PositionType, ), beltdirection, )); (outrail_x - 7, false) } 19 | 21 | 23 => { blueprint.add_entity(Entity::new_underground_belt( station_type_belt.beltspeed, underground_left, Position::new( outrail_x - 5, 30 + total_stacker_height + previous_station_heights + output_height - 2 * j as PositionType, ), beltdirection, )); (outrail_x - 5, false) } _ => { blueprint.add_entity(Entity::new_underground_belt( station_type_belt.beltspeed, underground_left, Position::new( outrail_x - 3, 30 + total_stacker_height + previous_station_heights + output_height - 2 * j as PositionType, ), beltdirection, )); (outrail_x - 3, false) } }; if signal { blueprint.add_entity(Entity::new_underground_belt( station_type_belt.beltspeed, underground_right, Position::new( outrail_x + 5, 30 + total_stacker_height + previous_station_heights + output_height - 2 * j as PositionType, ), beltdirection, )); } else { blueprint.add_entity(Entity::new_underground_belt( station_type_belt.beltspeed, underground_right, Position::new( outrail_x + 3, 30 + total_stacker_height + previous_station_heights + output_height - 2 * j as PositionType, ), beltdirection, )); blueprint.add_entity(Entity::new_belt( station_type_belt.beltspeed, Position::new( outrail_x + 5, 30 + total_stacker_height + previous_station_heights + output_height - 2 * j as PositionType, ), beltdirection, )); } for l in 0..((underground_x - belt_x) / 2) { blueprint.add_entity(Entity::new_belt( station_type_belt.beltspeed, Position::new( belt_x + 2 * l, 30 + total_stacker_height + previous_station_heights + output_height - 2 * j as PositionType, ), beltdirection, )); } } } }; // rail connection if i != stations.len() - 1 { for j in 0..(station_height / 4) { blueprint.add_entity(Entity::new_rail( RailType::Straight, Position::new( inrail_x, total_stacker_height + 4 + previous_station_heights + 4 * j as PositionType, ), 0, )); blueprint.add_entity(Entity::new_rail( RailType::Straight, Position::new( outrail_x, total_stacker_height + 4 + previous_station_heights + 4 * j as PositionType, ), 0, )); } if station_height > 8 { blueprint.add_entity(Entity::new_rail( RailType::ChainSignal, Position::new( inrail_x - 3, total_stacker_height + 15 + previous_station_heights, ), 0, )); blueprint.add_entity(Entity::new_rail( RailType::RailSignal, Position::new( outrail_x + 3, total_stacker_height + 15 + previous_station_heights, ), 8, )); } } previous_station_heights += station_height; } // output rails blueprint.add_entity(Entity::new_rail( RailType::CurvedA, Position::new(outrail_x, total_stacker_height - 2), 0, )); blueprint.add_entity(Entity::new_rail( RailType::CurvedB, Position::new(outrail_x - 4, total_stacker_height - 12), 0, )); let out_diagonal_length = (outrail_x - 54) / 4; for i in 0..out_diagonal_length { blueprint.add_entity(Entity::new_rail( RailType::Straight, Position::new( outrail_x - 10 - 4 * i as PositionType, total_stacker_height - 18 - 4 * i as PositionType, ), 6, )); if i % 4 == 0 { blueprint.add_entity(Entity::new_rail( RailType::RailSignal, Position::new( outrail_x - 7 - 4 * i as PositionType, total_stacker_height - 19 - 4 * i as PositionType, ), 6, )); } } let remaining_offset = total_stacker_height - 34 - out_diagonal_length * 4; blueprint.add_entity(Entity::new_rail( RailType::CurvedB, Position::new(42, remaining_offset + 14), 8, )); blueprint.add_entity(Entity::new_rail( RailType::CurvedA, Position::new(38, remaining_offset + 4), 8, )); blueprint.add_entity(Entity::new_rail( RailType::RailSignal, Position::new(41, remaining_offset + 7), 7, )); for i in 0..(remaining_offset / 4) { blueprint.add_entity(Entity::new_rail( RailType::Straight, Position::new(38, 2 + 4 * i as PositionType), 0, )); if i % 8 == 7 { blueprint.add_entity(Entity::new_rail( RailType::RailSignal, Position::new(41, 3 + 4 * i as PositionType), 8, )); } } // blueprint.transform(Transformation::new(Direction::Right, Position::new(0, 0))); (blueprint, outrail_x + 5, output_heights) }