factorio_blueprint/factorio-blueprint-generator/src/multistation.rs

691 lines
27 KiB
Rust

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<PositionType>) {
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)
}