633 lines
22 KiB
Rust
633 lines
22 KiB
Rust
use factorio_blueprint::abstraction::{Blueprint, Entity, EntityType, RailType, UndergroundType};
|
|
use factorio_core::{beltoptions::Beltspeed, prelude::*};
|
|
|
|
use crate::{balancer::binary_balancer, station::basic_station};
|
|
|
|
pub struct StationSpec {
|
|
pub locomotives: usize,
|
|
pub wagons: usize,
|
|
pub load: bool,
|
|
pub beltspeed: Beltspeed,
|
|
pub lanes: usize,
|
|
}
|
|
|
|
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 {
|
|
let longest_train = stations
|
|
.iter()
|
|
.map(|s| s.locomotives + s.wagons)
|
|
.max()
|
|
.unwrap();
|
|
|
|
let mut blueprint = Blueprint::new();
|
|
|
|
// 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 previous_station_heights = 0;
|
|
// station
|
|
for (i, station) 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 + 3,
|
|
total_stacker_height + 17 + previous_station_heights,
|
|
),
|
|
15,
|
|
));
|
|
|
|
// 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 - 3,
|
|
total_stacker_height + 17 + previous_station_heights,
|
|
),
|
|
9,
|
|
));
|
|
|
|
for j in 0..((7 * longest_train).div_ceil(2)
|
|
- (7 * (station.locomotives + station.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 = basic_station(
|
|
station.load,
|
|
station.locomotives,
|
|
station.wagons,
|
|
station.lanes,
|
|
station.beltspeed,
|
|
factorio_core::beltoptions::Belttype::Full,
|
|
);
|
|
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);
|
|
|
|
// belt output
|
|
let station_height =
|
|
calculate_station_height(output_height, station.lanes, station.beltspeed);
|
|
|
|
// rail crossing
|
|
let (beltdirection, underground_left, underground_right) = match station.load {
|
|
true => (
|
|
Direction::Left,
|
|
UndergroundType::Output,
|
|
UndergroundType::Input,
|
|
),
|
|
false => (
|
|
Direction::Right,
|
|
UndergroundType::Input,
|
|
UndergroundType::Output,
|
|
),
|
|
};
|
|
let mut belt_x = outrail_x - 28 - 14 * station.locomotives as PositionType - 3
|
|
+ 2 * (station.wagons / station.lanes).ilog2() as PositionType;
|
|
|
|
let (balancer_length, mut b) = binary_balancer(station.lanes, station.beltspeed);
|
|
|
|
if station.load {
|
|
b.transform(Transformation::new(
|
|
Direction::Down,
|
|
Position::new(balancer_length, 2 * station.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.lanes as PositionType,
|
|
),
|
|
));
|
|
|
|
blueprint.add_blueprint(b);
|
|
|
|
belt_x += balancer_length;
|
|
|
|
for j in 0..station.lanes {
|
|
let (underground_x, signal) = match station_height
|
|
- (output_height - 2 * j as PositionType)
|
|
{
|
|
5 => {
|
|
blueprint.add_entity(Entity::new_underground_belt(
|
|
station.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.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.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.beltspeed,
|
|
underground_left,
|
|
Position::new(
|
|
outrail_x - 17,
|
|
30 + total_stacker_height + previous_station_heights + output_height
|
|
- 2 * j as PositionType,
|
|
),
|
|
beltdirection,
|
|
));
|
|
if station.beltspeed != Beltspeed::Turbo {
|
|
blueprint.add_entity(Entity::new_underground_belt(
|
|
station.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.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.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.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.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.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.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.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.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.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.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.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.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,
|
|
));
|
|
}
|
|
|
|
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
|
|
}
|