diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 78bc369..97a30c1 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -10,7 +10,7 @@ jobs: name: Check runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions-rs/toolchain@v1 with: profile: minimal diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml index 4349927..4b783de 100644 --- a/.github/workflows/clippy.yml +++ b/.github/workflows/clippy.yml @@ -11,7 +11,7 @@ jobs: name: Clippy runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions-rs/toolchain@v1 with: profile: minimal diff --git a/.github/workflows/rustfmt.yml b/.github/workflows/rustfmt.yml index cae854c..eb3345a 100644 --- a/.github/workflows/rustfmt.yml +++ b/.github/workflows/rustfmt.yml @@ -10,7 +10,7 @@ jobs: name: Rustfmt runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions-rs/toolchain@v1 with: profile: minimal diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 34a80e5..614a6f1 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -14,7 +14,7 @@ jobs: name: Test Suite runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions-rs/toolchain@v1 with: profile: minimal diff --git a/Cargo.lock b/Cargo.lock index cbbc526..2648ab2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1316,10 +1316,19 @@ source = "git+https://github.com/BloodStainedCrow/egui?rev=4e11a02#4e11a02615078 dependencies = [ "bytemuck", "color-hex", - "emath 0.31.1 (git+https://github.com/BloodStainedCrow/egui?rev=4e11a02)", + "emath 0.31.1", "serde", ] +[[package]] +name = "ecolor" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94bdf37f8d5bd9aa7f753573fdda9cf7343afa73dd28d7bfe9593bd9798fc07e" +dependencies = [ + "emath 0.32.3", +] + [[package]] name = "eframe" version = "0.31.1" @@ -1328,7 +1337,7 @@ dependencies = [ "ahash", "bytemuck", "document-features", - "egui", + "egui 0.31.1", "egui-wgpu", "egui-winit", "egui_glow", @@ -1364,8 +1373,8 @@ dependencies = [ "accesskit", "ahash", "bitflags 2.10.0", - "emath 0.31.1 (git+https://github.com/BloodStainedCrow/egui?rev=4e11a02)", - "epaint", + "emath 0.31.1", + "epaint 0.31.1", "log", "nohash-hasher", "profiling", @@ -1374,13 +1383,29 @@ dependencies = [ "unicode-segmentation", ] +[[package]] +name = "egui" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d5d0306cd61ca75e29682926d71f2390160247f135965242e904a636f51c0dc" +dependencies = [ + "ahash", + "bitflags 2.10.0", + "emath 0.32.3", + "epaint 0.32.3", + "nohash-hasher", + "profiling", + "smallvec", + "unicode-segmentation", +] + [[package]] name = "egui-show-info" version = "0.1.0" source = "git+https://github.com/BloodStainedCrow/egui-show-info#0b1a1a7e6b2b75935b7bb7710de4fc4900a35fa9" dependencies = [ "bimap", - "egui", + "egui 0.31.1", "enum-map", "parking_lot 0.12.5", "petgraph 0.8.2", @@ -1404,8 +1429,8 @@ dependencies = [ "ahash", "bytemuck", "document-features", - "egui", - "epaint", + "egui 0.31.1", + "epaint 0.31.1", "log", "profiling", "thiserror 1.0.69", @@ -1424,7 +1449,7 @@ dependencies = [ "ahash", "arboard", "bytemuck", - "egui", + "egui 0.31.1", "log", "profiling", "raw-window-handle", @@ -1441,7 +1466,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624659a2e972a46f4d5f646557906c55f1cd5a0836eddbe610fdf1afba1b4226" dependencies = [ "ahash", - "egui", + "egui 0.31.1", "enum-map", "log", "mime_guess2", @@ -1456,7 +1481,7 @@ source = "git+https://github.com/BloodStainedCrow/egui?rev=4e11a02#4e11a02615078 dependencies = [ "ahash", "bytemuck", - "egui", + "egui 0.31.1", "glow", "log", "memoffset", @@ -1471,7 +1496,7 @@ name = "egui_graphs" version = "0.25.1" source = "git+https://github.com/BloodStainedCrow/egui_graphs?branch=tree_layout#32aeb647d0ebd65ff0105274397ce4c4258d9e87" dependencies = [ - "egui", + "egui 0.31.1", "petgraph 0.8.2", "rand 0.9.2", "serde", @@ -1479,13 +1504,13 @@ dependencies = [ [[package]] name = "egui_plot" -version = "0.32.1" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14ae092b46ea532f6c69d3e71036fb3b688fd00fd09c2a1e43d17051a8ae43e6" +checksum = "524318041a8ea90c81c738e8985f8ad9e3f9bed636b03c2ff37b218113ed5121" dependencies = [ "ahash", - "egui", - "emath 0.31.1 (registry+https://github.com/rust-lang/crates.io-index)", + "egui 0.32.3", + "emath 0.32.3", ] [[package]] @@ -1494,12 +1519,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" -[[package]] -name = "emath" -version = "0.31.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e4cadcff7a5353ba72b7fea76bf2122b5ebdbc68e8155aa56dfdea90083fe1b" - [[package]] name = "emath" version = "0.31.1" @@ -1509,6 +1528,12 @@ dependencies = [ "serde", ] +[[package]] +name = "emath" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45fd7bc25f769a3c198fe1cf183124bf4de3bd62ef7b4f1eaf6b08711a3af8db" + [[package]] name = "embedded-io" version = "0.4.0" @@ -1588,8 +1613,8 @@ dependencies = [ "ab_glyph", "ahash", "bytemuck", - "ecolor", - "emath 0.31.1 (git+https://github.com/BloodStainedCrow/egui?rev=4e11a02)", + "ecolor 0.31.1", + "emath 0.31.1", "epaint_default_fonts", "log", "nohash-hasher", @@ -1598,6 +1623,21 @@ dependencies = [ "serde", ] +[[package]] +name = "epaint" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63adcea970b7a13094fe97a36ab9307c35a750f9e24bf00bb7ef3de573e0fddb" +dependencies = [ + "ab_glyph", + "ahash", + "ecolor 0.32.3", + "emath 0.32.3", + "nohash-hasher", + "parking_lot 0.12.5", + "profiling", +] + [[package]] name = "epaint_default_fonts" version = "0.31.1" @@ -1692,7 +1732,7 @@ dependencies = [ [[package]] name = "factory" -version = "0.2.0" +version = "0.2.1" dependencies = [ "base64 0.22.1", "bimap", @@ -1704,9 +1744,9 @@ dependencies = [ "charts-rs", "dhat", "directories", - "ecolor", + "ecolor 0.31.1", "eframe", - "egui", + "egui 0.31.1", "egui-show-info", "egui-show-info-derive", "egui_extras", @@ -3972,7 +4012,7 @@ name = "puffin_egui" version = "0.29.1" source = "git+https://github.com/BloodStainedCrow/puffin#420f296797ecf2acdc7156bf65e2b9a2d8e06c8d" dependencies = [ - "egui", + "egui 0.31.1", "egui_extras", "indexmap", "natord", @@ -5125,7 +5165,7 @@ version = "0.1.0" source = "git+https://github.com/BloodStainedCrow/tilelib.git#ddacebc889c872eb4146604e0f4cde1caf2c20d5" dependencies = [ "bytemuck", - "egui", + "egui 0.31.1", "egui-wgpu", "image", "itertools 0.14.0", diff --git a/Cargo.toml b/Cargo.toml index 9b0d06b..bb42419 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ # cargo-features = ["codegen-backend"] [package] name = "factory" -version = "0.2.0" +version = "0.2.1" edition = "2024" rust-version = "1.85" build = "build.rs" @@ -33,7 +33,7 @@ bimap = { version = "0.6.3", features = ["serde", "std"], default-features = fal eframe = { version = "0.31.1", features = ["accesskit", "default_fonts", "wayland", "web_screen_reader", "x11", "wgpu"], optional = true, default-features = false } wgpu = { version = "25.0.2", features = ["webgl"] } egui_extras = { version = "0.31.1", optional = true } -egui_plot = { version = "0.32.1", optional = true } +egui_plot = { version = "0.33.0", optional = true } rand = "0.8.5" bitcode = { version = "0.6.6", features = ["serde"] } egui = { version = "0.31.1", features = ["bytemuck", "serde"], optional = true } diff --git a/README.md b/README.md index c19a723..b70f16a 100644 --- a/README.md +++ b/README.md @@ -1,44 +1,18 @@ # What is this? -This project is an academic recreation of the factory game [Factorio](https://www.factorio.com/) taking additional ideas from [Dyson Sphere Program](https://store.steampowered.com/app/1366540/Dyson_Sphere_Program/). +This project is an academic recreation of the factory game [Factorio](https://www.factorio.com/). -I created it as an exercise to see how far I could optimize the basic concepts and algorithms of the genre in terms of performance, while allowing myself minor changes to the games' rules. +I created it as an exercise to see how far I could optimize the basic mechanics and algorithms of the genre in terms of performance. Another goal that emerged along the way, was learning about the way modern CPUs actually work. -# Roadmap -Currently I adding beacons and thinking about how to efficiently add logistics bots. Then I want to build a comprehensive suit of benchmark test to show if/by how much I was able to improve performance. - # Why did you start? -I was playing the above games and started being unable to expand due to performance issues. So in my hubris I declared: "How hard can it be?". +I was playing Factorio and started being unable to expand due to performance issues. So in my hubris I declared: "How hard can it be?". + +# Current State +Most logic for power grids, belts, splitters, assemblers, labs, inserters, mining drills, solar panels and accumulators is working. This allowed me to recreate a Factorio base, giving me a point for performance comparison. +I was able to run a base comprised of 40 copies of [this](https://factoriobox.1au.us/map/view/2824bc1566bd95b5825baf3bd2eb8fa32de8397526464f5a0327bcb82d64ebf8/#1/nauvis/15/2942/1158/0/447) Factorio Megabase by Smurphy (which Factorio runs at ~40 UPS) at 60 UPS on my machine. + +# Running it +It should run on Linux, Windows and MacOS. Assuming you have [rust and cargo](https://rust-lang.org), just `cargo run --release`. On NixOS the included `shell.nix` contains all you need. -## TODOS -- ~~Place Power Production~~ -- ~~Blueprints so I can actually do perf tests~~ -- ~~Permanently running replay system, so I can easily recreate crashes~~ -- ~~Test harness for replays, to ensure they do not crash~~ -- ~~Automatic insertion limit~~ -- ~~Assembler Module Support~~ -- ~~World listener support (i.e. update whenever something changes in the world, for power, beacons and inserters)~~ -- Lazy Terrain Generation -- ~~Assembler Module Frontend~~ -- ~~Assembler Power Consumption Modifier Support~~ -- ~~Beacons~~ -- ~~FIX Beacon Flicker due to lowering power consumption when beacons are unpowered~~ -- ~~Storage Storage Inserters~~ -- ~~Science Consumption in Labs~~ -- ~~Inserter connections to labs~~ -- ~~Debug inserters~~ -- ~~Production Graphs~~ -- ~~Liquids~~ -- ~~Map View~~ -- ~~Technology~~ -- Mining Drills -- ~~Underground belts~~ -- Fix Underground Pipe connection breaking/overlap -- Place Steam Turbines -- ~~Splitters~~ -- Allow Belts of different types to connect to one another -- Decide if I want beacons to match factorio behaviour or keep the hard switch on/off -- ~~Ore Generation~~ -- Add tile requirements for buildings/recipes (for offshore pump) -- Bots -- MAYBE: A canonical version of the simulation that can be used for diff testing (and as some weird documentation of the mechanics I suppose) \ No newline at end of file +# Attributions +All graphics used with the `graphics` feature are from the Factorio Mod [Krastorio 2 Assets](https://codeberg.org/raiguard/Krastorio2Assets). \ No newline at end of file diff --git a/src/app_state.rs b/src/app_state.rs index c819a19..2ac4173 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -746,8 +746,10 @@ impl StorageStorageInserterStore { hand_size: ITEMCOUNTTYPE, data_store: &DataStore, ) -> InserterIdentifier { - let source = FakeUnionStorage::from_storage_with_statics_at_zero(item, start, data_store); - let dest = FakeUnionStorage::from_storage_with_statics_at_zero(item, dest, data_store); + let source = + FakeUnionStorage::from_storage_with_statics_at_zero(item, start, data_store).unwrap(); + let dest = + FakeUnionStorage::from_storage_with_statics_at_zero(item, dest, data_store).unwrap(); let id: InserterIdentifier = self.inserters[item.into_usize()] .entry(movetime) @@ -829,7 +831,8 @@ impl StorageStorageInserterStore { .0 .update_inserter_src( id, - FakeUnionStorage::from_storage_with_statics_at_zero(item, new_src, data_store), + FakeUnionStorage::from_storage_with_statics_at_zero(item, new_src, data_store) + .unwrap(), ) } @@ -848,7 +851,8 @@ impl StorageStorageInserterStore { .0 .update_inserter_dest( id, - FakeUnionStorage::from_storage_with_statics_at_zero(item, new_dest, data_store), + FakeUnionStorage::from_storage_with_statics_at_zero(item, new_dest, data_store) + .unwrap(), ) } @@ -869,7 +873,8 @@ impl StorageStorageInserterStore { .update_inserter_src_if_equal( id, old_src, - FakeUnionStorage::from_storage_with_statics_at_zero(item, new_src, data_store), + FakeUnionStorage::from_storage_with_statics_at_zero(item, new_src, data_store) + .unwrap(), ) } @@ -890,7 +895,8 @@ impl StorageStorageInserterStore { .update_inserter_dest_if_equal( id, old_dest, - FakeUnionStorage::from_storage_with_statics_at_zero(item, new_dest, data_store), + FakeUnionStorage::from_storage_with_statics_at_zero(item, new_dest, data_store) + .unwrap(), ) } } @@ -1137,8 +1143,7 @@ impl Factory GameState { // Handle storage updates for storage_update in storage_updates { - let mut entity_size = None; - game_state.world.get_entity_at_mut(storage_update.position, data_store).map(|e| { - match (e, storage_update.new_pg_entity.clone()) { - (Entity::Assembler { ty, pos: _, info: AssemblerInfo::Powered { id, pole_position: _, weak_index: _ }, modules: _, rotation }, crate::power::power_grid::PowerGridEntity::Assembler { ty: _, recipe, index }) => { - entity_size = Some(data_store.assembler_info[usize::from(*ty)].size(*rotation)); + let entity = game_state.world.get_entity_at_mut(storage_update.position, data_store).expect(&format!("Did not find entity at {:?} for storage update", storage_update.position)); + + let entity_size = match (entity, storage_update.new_pg_entity.clone()) { + (Entity::Assembler { + ty, + pos: _, + info: AssemblerInfo::Powered { id, pole_position: _, weak_index: _ }, modules: _, rotation }, + crate::power::power_grid::PowerGridEntity::Assembler { ty: _, recipe, index }) => { assert_eq!(id.recipe, recipe); id.grid = storage_update.new_grid; id.assembler_index = index; // FIXME: Store and update the weak_index + + data_store.assembler_info[usize::from(*ty)].size(*rotation) }, (Entity::Lab { pos: _, ty, modules: _, pole_position: Some((_pole_pos, _weak_idx, lab_store_index)) }, crate::power::power_grid::PowerGridEntity::Lab { ty: _, index: new_idx }) => { - entity_size = Some(data_store.lab_info[usize::from(*ty)].size); *lab_store_index = new_idx; // The weak index stays the same since it it still connected to the same power pole + + data_store.lab_info[usize::from(*ty)].size } (_, _) => todo!("Handler storage_update {storage_update:?}") - } - }); + }; // FIXME: Rotation - let e_size = entity_size.unwrap(); + let e_size = entity_size; let inserter_range = data_store.max_inserter_search_range; @@ -1702,7 +1712,7 @@ impl GameState unreachable!(), crate::power::power_grid::PowerGridEntity::Accumulator { .. } => unreachable!(), crate::power::power_grid::PowerGridEntity::Beacon { .. } => unreachable!(), - }.translate(game_state.simulation_state.factory.belts.get_inserter_item(*id, *belt_pos), data_store).unwrap(); + }; game_state.simulation_state.factory.belts.update_belt_storage_inserter_src(*id, *belt_pos, game_state.simulation_state.factory.belts.get_inserter_item(*id, *belt_pos), new_storage, data_store); }, AttachedInserter::BeltBelt { .. } => { @@ -1716,7 +1726,7 @@ impl GameState unreachable!(), crate::power::power_grid::PowerGridEntity::Accumulator { .. } => unreachable!(), crate::power::power_grid::PowerGridEntity::Beacon { .. } => unreachable!(), - }.translate(*item, data_store).unwrap(); + }; let movetime = user_movetime.map(|v| v.into()).unwrap_or(data_store.inserter_infos[*ty as usize].swing_time_ticks); @@ -1740,7 +1750,7 @@ impl GameState unreachable!(), crate::power::power_grid::PowerGridEntity::Accumulator { .. } => unreachable!(), crate::power::power_grid::PowerGridEntity::Beacon { .. } => unreachable!(), - }.translate(game_state.simulation_state.factory.belts.get_inserter_item(*id, *belt_pos), data_store).unwrap(); + }; game_state.simulation_state.factory.belts.update_belt_storage_inserter_dest(*id, *belt_pos, game_state.simulation_state.factory.belts.get_inserter_item(*id, *belt_pos), new_storage, data_store); }, AttachedInserter::BeltBelt { .. } => { @@ -1754,7 +1764,7 @@ impl GameState unreachable!(), crate::power::power_grid::PowerGridEntity::Accumulator { .. } => unreachable!(), crate::power::power_grid::PowerGridEntity::Beacon { .. } => unreachable!(), - }.translate(*item, data_store).unwrap(); + }; let movetime = user_movetime.map(|v| v.into()).unwrap_or(data_store.inserter_infos[*ty as usize].swing_time_ticks); let new_id = game_state.simulation_state.factory.storage_storage_inserters.update_inserter_dest(*item, movetime.into(), *inserter, new_storage, data_store); @@ -1768,7 +1778,7 @@ impl GameState { let id: FluidSystemId<_> = game_state.simulation_state.factory.fluid_store.fluid_box_pos_to_network_id[pos]; if let Some(fluid) = id.fluid { - let storage = match storage_update.new_pg_entity { + let storage = match storage_update.old_pg_entity { crate::power::power_grid::PowerGridEntity::Assembler { ty, recipe, index } => Storage::Assembler { grid: storage_update.old_grid, recipe_idx_with_this_item: recipe.id, index }, crate::power::power_grid::PowerGridEntity::Lab { ty, index } => Storage::Lab { grid: storage_update.old_grid, index }, crate::power::power_grid::PowerGridEntity::LazyPowerProducer { item, index } => todo!(), @@ -1776,13 +1786,9 @@ impl GameState unreachable!(), crate::power::power_grid::PowerGridEntity::Beacon { .. } => unreachable!(), }; - dbg!(storage); - let Some(translated_storage) = storage.translate(fluid, data_store) else { + let Ok(old_storage) = FakeUnionStorage::from_storage_with_statics_at_zero(fluid, storage, data_store) else { return ControlFlow::Continue(()); }; - dbg!(translated_storage); - let old_storage = FakeUnionStorage::from_storage_with_statics_at_zero(fluid, translated_storage, data_store); - dbg!(old_storage); let new_storage = match storage_update.new_pg_entity { crate::power::power_grid::PowerGridEntity::Assembler { ty, recipe, index } => Storage::Assembler { grid: storage_update.new_grid, recipe_idx_with_this_item: recipe.id, index }, @@ -1791,8 +1797,8 @@ impl GameState unreachable!(), crate::power::power_grid::PowerGridEntity::Accumulator { .. } => unreachable!(), crate::power::power_grid::PowerGridEntity::Beacon { .. } => unreachable!(), - }.translate(fluid, data_store).unwrap(); - game_state.simulation_state.factory.fluid_store.update_fluid_conn_if_needed(*pos, old_storage, FakeUnionStorage::from_storage_with_statics_at_zero(fluid, new_storage, data_store)); + }; + game_state.simulation_state.factory.fluid_store.update_fluid_conn_if_needed(*pos, old_storage, FakeUnionStorage::from_storage_with_statics_at_zero(fluid, new_storage, data_store).unwrap()); } }, @@ -2333,7 +2339,7 @@ impl GameState ()>, diff --git a/src/assembler/bucketed.rs b/src/assembler/bucketed.rs index a92f38b..a36c9ab 100644 --- a/src/assembler/bucketed.rs +++ b/src/assembler/bucketed.rs @@ -877,4 +877,8 @@ impl usize { + self.hot_data.len() - self.holes.len() + } } diff --git a/src/assembler/mod.rs b/src/assembler/mod.rs index 6c35fa2..8640c61 100644 --- a/src/assembler/mod.rs +++ b/src/assembler/mod.rs @@ -505,6 +505,58 @@ impl< _ => unreachable!(), } } + + pub fn num_assemblers(&self) -> usize { + let Self { + assemblers_0_1, + assemblers_1_1, + assemblers_2_1, + assemblers_2_2, + assemblers_2_3, + assemblers_3_1, + assemblers_4_1, + assemblers_5_1, + assemblers_6_1, + recipe: _, + } = self; + + assemblers_0_1 + .iter() + .map(|store| store.num_assemblers()) + .sum::() + + assemblers_1_1 + .iter() + .map(|store| store.num_assemblers()) + .sum::() + + assemblers_2_1 + .iter() + .map(|store| store.num_assemblers()) + .sum::() + + assemblers_2_2 + .iter() + .map(|store| store.num_assemblers()) + .sum::() + + assemblers_2_3 + .iter() + .map(|store| store.num_assemblers()) + .sum::() + + assemblers_3_1 + .iter() + .map(|store| store.num_assemblers()) + .sum::() + + assemblers_4_1 + .iter() + .map(|store| store.num_assemblers()) + .sum::() + + assemblers_5_1 + .iter() + .map(|store| store.num_assemblers()) + .sum::() + + assemblers_6_1 + .iter() + .map(|store| store.num_assemblers()) + .sum::() + } } // FIXME: @@ -799,6 +851,8 @@ pub trait MultiAssemblerStore< ret } + + fn num_assemblers(&self) -> usize; } pub mod arrays { diff --git a/src/assembler/simd.rs b/src/assembler/simd.rs index f3066a3..e108e61 100644 --- a/src/assembler/simd.rs +++ b/src/assembler/simd.rs @@ -665,6 +665,7 @@ impl; NUM_INGS] = self @@ -673,13 +674,7 @@ impl usize { + self.len - self.holes.len() + } } #[cfg(test)] diff --git a/src/belt/mod.rs b/src/belt/mod.rs index c9f81fa..fcfe7de 100644 --- a/src/belt/mod.rs +++ b/src/belt/mod.rs @@ -1539,7 +1539,7 @@ impl BeltStore { .inserters .inserters .iter() - .filter_map(|(ins, item)| { + .filter_map(|(ins, item, _movetime, _hand_size)| { let (dir, _state) = ins.state.into(); (dir == Dir::StorageToBelt).then_some(*item) }) @@ -2100,7 +2100,7 @@ impl BeltStore { belt.inserters .inserters .iter() - .filter_map(|(ins, item)| { + .filter_map(|(ins, item, _movetime, _hand_size)| { let (dir, _state) = ins.state.into(); (dir == Dir::StorageToBelt).then_some(*item) }) @@ -2598,8 +2598,9 @@ impl BeltStore { movetime: u16, hand_size: ITEMCOUNTTYPE, ) -> Result<(), SpaceOccupiedError> { - let handle_sushi_belt = - |belt: &mut SushiBelt| belt.add_in_inserter(filter, pos, storage_id); + let handle_sushi_belt = |belt: &mut SushiBelt| { + belt.add_in_inserter(filter, pos, storage_id, movetime, hand_size) + }; match id { BeltTileId::AnyBelt(index, _) => { @@ -2626,7 +2627,7 @@ impl BeltStore { let now_sushi_belt = self.inner.get_sushi_mut(new_index); now_sushi_belt - .add_in_inserter(filter, pos, storage_id) + .add_in_inserter(filter, pos, storage_id, movetime, hand_size) .expect("We already became sushi, it should now work!"); }, } @@ -2667,8 +2668,9 @@ impl BeltStore { movetime: u16, hand_size: ITEMCOUNTTYPE, ) -> Result<(), SpaceOccupiedError> { - let handle_sushi_belt = - |belt: &mut SushiBelt| belt.add_out_inserter(filter, pos, storage_id); + let handle_sushi_belt = |belt: &mut SushiBelt| { + belt.add_out_inserter(filter, pos, storage_id, movetime, hand_size) + }; match id { BeltTileId::AnyBelt(index, _) => { @@ -2695,7 +2697,7 @@ impl BeltStore { let now_sushi_belt = self.inner.get_sushi_mut(new_index); now_sushi_belt - .add_out_inserter(filter, pos, storage_id) + .add_out_inserter(filter, pos, storage_id, movetime, hand_size) .expect("We already became sushi, it should now work!"); }, } @@ -3010,7 +3012,8 @@ impl BeltStore { belt_id.item, new_src, data_store, - ), + ) + .unwrap(), ); }, AnyBelt::Sushi(index) => { @@ -3018,7 +3021,8 @@ impl BeltStore { belt_pos, FakeUnionStorage::from_storage_with_statics_at_zero( src_item, new_src, data_store, - ), + ) + .unwrap(), ); }, AnyBelt::Empty(_) => unimplemented!("Empty belt cannot have inserters"), @@ -3045,7 +3049,8 @@ impl BeltStore { belt_id.item, new_dest, data_store, - ), + ) + .unwrap(), ); }, AnyBelt::Sushi(index) => { @@ -3053,7 +3058,8 @@ impl BeltStore { belt_pos, FakeUnionStorage::from_storage_with_statics_at_zero( dest_item, new_dest, data_store, - ), + ) + .unwrap(), ); }, AnyBelt::Empty(_) => unimplemented!("Empty belt cannot have inserters"), diff --git a/src/belt/smart.rs b/src/belt/smart.rs index 0e93852..850106c 100644 --- a/src/belt/smart.rs +++ b/src/belt/smart.rs @@ -5,6 +5,7 @@ use std::{ u8, }; +use crate::item::Indexable; use crate::{ inserter::{ InserterState, belt_storage_inserter::Dir, @@ -46,10 +47,6 @@ pub static NUM_BELT_FREE_CACHE_HITS: AtomicUsize = AtomicUsize::new(0); // #[cfg(debug_assertions)] pub static NUM_BELT_LOCS_SEARCHED: AtomicUsize = AtomicUsize::new(0); -// HUGE FIXME: -pub const MOVETIME: u8 = 12; -pub const HAND_SIZE: u8 = 12; - #[allow(clippy::module_name_repetitions)] #[cfg_attr(feature = "client", derive(ShowInfo), derive(GetSize))] #[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] @@ -98,6 +95,8 @@ pub struct BeltInserterInfo { pub outgoing: bool, pub state: InserterState, pub connection: FakeUnionStorage, + pub hand_size: ITEMCOUNTTYPE, + pub movetime: u8, } #[derive(Debug)] @@ -180,7 +179,7 @@ impl SmartBelt { inserters: SushiInserterStoreDyn { inserters: inserters .into_iter() - .map(|(inserter, _movetime, _hand_size)| (inserter, item)) + .map(|(inserter, movetime, hand_size)| (inserter, item, movetime, hand_size)) .collect(), }, @@ -347,6 +346,8 @@ impl SmartBelt { outgoing: dir == Dir::BeltToStorage, state, connection: inserter.0.storage_id, + movetime: inserter.1, + hand_size: inserter.2, }); } else if pos > belt_pos { return None; @@ -639,7 +640,14 @@ impl SmartBelt { // We KNOW this position is filled debug_assert!(self.locs[loc_idx]); let mut loc = true; - let _changed = ins.update(&mut loc, storages, *movetime, *hand_size, grid_size); + let _changed = ins.update( + self.item.into_usize(), + &mut loc, + storages, + *movetime, + *hand_size, + grid_size, + ); if !loc { self.locs.set(loc_idx, false); @@ -651,7 +659,14 @@ impl SmartBelt { } else { let mut loc = self.locs.get_mut(loc_idx).unwrap(); - let changed = ins.update(loc.as_mut(), storages, *movetime, *hand_size, grid_size); + let changed = ins.update( + self.item.into_usize(), + loc.as_mut(), + storages, + *movetime, + *hand_size, + grid_size, + ); if changed { // the inserter changed something. diff --git a/src/belt/sushi.rs b/src/belt/sushi.rs index fc298d0..85b8c0f 100644 --- a/src/belt/sushi.rs +++ b/src/belt/sushi.rs @@ -9,6 +9,7 @@ use egui_show_info_derive::ShowInfo; use get_size2::GetSize; use crate::inserter::belt_storage_inserter::Dir; +use crate::item::ITEMCOUNTTYPE; use crate::{ belt::belt::NoSpaceError, item::{IdxTrait, Item, WeakIdxTrait}, @@ -24,8 +25,6 @@ use crate::inserter::FakeUnionStorage; use crate::inserter::belt_storage_inserter_non_const_gen::BeltStorageInserterDyn; use itertools::Either; -use crate::belt::smart::{HAND_SIZE, MOVETIME}; - #[cfg_attr(feature = "client", derive(ShowInfo), derive(GetSize))] #[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] pub struct SushiBelt { @@ -48,7 +47,7 @@ pub struct SushiBelt { #[cfg_attr(feature = "client", derive(ShowInfo), derive(GetSize))] #[derive(Debug, Clone, serde::Deserialize, serde::Serialize)] pub(super) struct SushiInserterStoreDyn { - pub(super) inserters: Box<[(BeltStorageInserterDyn, Item)]>, + pub(super) inserters: Box<[(BeltStorageInserterDyn, Item, u8, ITEMCOUNTTYPE)]>, } #[derive(Debug, PartialEq, Eq)] @@ -82,6 +81,8 @@ impl SushiBelt { filter: Item, pos: BeltLenType, storage_id: FakeUnionStorage, + movetime: u16, + hand_size: ITEMCOUNTTYPE, ) -> Result<(), SpaceOccupiedError> { assert!( usize::from(pos) < self.locs.len(), @@ -92,7 +93,12 @@ impl SushiBelt { let mut pos_after_last_inserter = 0; let mut i = 0; - for offset in self.inserters.inserters.iter().map(|(i, _item)| i.offset) { + for offset in self + .inserters + .inserters + .iter() + .map(|(i, _item, _movetime, _hand_size)| i.offset) + { let next_inserter_pos = pos_after_last_inserter + offset; match next_inserter_pos.cmp(&pos) { @@ -119,6 +125,8 @@ impl SushiBelt { storage_id, ), filter, + movetime.try_into().unwrap_or(u8::MAX), + hand_size, ), ); ins.into_boxed_slice() @@ -126,7 +134,7 @@ impl SushiBelt { let next = self.inserters.inserters.get_mut(i + 1); - if let Some((next_ins, _item)) = next { + if let Some((next_ins, _item, _movetime, _hand_size)) = next { next_ins.offset -= new_inserter_offset + 1; } @@ -138,6 +146,8 @@ impl SushiBelt { filter: Item, pos: BeltLenType, storage_id: FakeUnionStorage, + movetime: u16, + hand_size: ITEMCOUNTTYPE, ) -> Result<(), SpaceOccupiedError> { assert!( usize::from(pos) < self.locs.len(), @@ -148,7 +158,12 @@ impl SushiBelt { let mut pos_after_last_inserter = 0; let mut i = 0; - for offset in self.inserters.inserters.iter().map(|(i, _item)| i.offset) { + for offset in self + .inserters + .inserters + .iter() + .map(|(i, _item, _movetime, _hand_size)| i.offset) + { let next_inserter_pos = pos_after_last_inserter + offset; match next_inserter_pos.cmp(&pos) { @@ -175,6 +190,8 @@ impl SushiBelt { storage_id, ), filter, + movetime.try_into().unwrap_or(u8::MAX), + hand_size, ), ); ins.into_boxed_slice() @@ -182,7 +199,7 @@ impl SushiBelt { let next = self.inserters.inserters.get_mut(i + 1); - if let Some((next_ins, _item)) = next { + if let Some((next_ins, _item, _movetime, _hand_size)) = next { next_ins.offset -= new_inserter_offset + 1; } @@ -193,7 +210,7 @@ impl SushiBelt { pub fn get_inserter_info_at(&self, belt_pos: u16) -> Option { let mut pos = 0; - for (inserter, _item) in self.inserters.inserters.iter() { + for (inserter, _item, movetime, hand_size) in self.inserters.inserters.iter() { pos += inserter.offset; if pos == belt_pos { let (dir, state) = inserter.state.into(); @@ -201,6 +218,9 @@ impl SushiBelt { outgoing: dir == Dir::BeltToStorage, state, connection: inserter.storage_id, + + hand_size: *hand_size, + movetime: *movetime, }); } else if pos > belt_pos { return None; @@ -221,7 +241,12 @@ impl SushiBelt { let mut pos_after_last_inserter = 0; let mut i = 0; - for offset in self.inserters.inserters.iter().map(|(i, _item)| i.offset) { + for offset in self + .inserters + .inserters + .iter() + .map(|(i, _item, _movetime, _hand_size)| i.offset) + { let next_inserter_pos = pos_after_last_inserter + offset; match next_inserter_pos.cmp(&belt_pos) { @@ -243,7 +268,7 @@ impl SushiBelt { pub fn set_inserter_storage_id(&mut self, belt_pos: u16, new: FakeUnionStorage) { let mut pos = 0; - for (inserter, _item) in self.inserters.inserters.iter_mut() { + for (inserter, _item, _movetime, _hand_size) in self.inserters.inserters.iter_mut() { pos += inserter.offset; if pos == belt_pos { inserter.storage_id = new; @@ -265,7 +290,12 @@ impl SushiBelt { let mut pos_after_last_inserter = 0; let mut i = 0; - for offset in self.inserters.inserters.iter().map(|(i, _item)| i.offset) { + for offset in self + .inserters + .inserters + .iter() + .map(|(i, _item, _movetime, _hand_size)| i.offset) + { let next_inserter_pos = pos_after_last_inserter + offset; match next_inserter_pos.cmp(&pos) { @@ -309,7 +339,7 @@ impl SushiBelt { .inserters .inserters .iter() - .map(|(_, item)| *item) + .map(|(_, item, _movetime, _hand_size)| *item) .chain(belt_belt_filter_in.into_iter().filter_map(|info| { let SushiInfo::Pure(item) = info else { unreachable!() @@ -395,12 +425,12 @@ impl SushiBelt { .collect::().into(), inserters: InserterStoreDyn { // FIXME: Some of these inserters might have a different item than what we are converting to. This will result in crashes and item transmutation - inserters: inserters.into_iter().map(|(ins, inserter_item)| { + inserters: inserters.into_iter().map(|(ins, inserter_item, movetime, hand_size)| { assert_eq!(item, inserter_item, "FIXME: We need to handle inserters which will never work again in smart belts"); // if item != inserter_item { // error!("We need to handle inserters which will never work again in smart belts!!!!!!!"); // } - (ins, MOVETIME, HAND_SIZE) + (ins, movetime, hand_size) }).collect(), }, item, @@ -485,7 +515,7 @@ impl SushiBelt { .inserters .inserters .iter() - .map(|(i, _item)| i.offset) + .map(|(i, _item, _movetime, _hand_size)| i.offset) .enumerate(); let mut current_pos = 0; @@ -606,7 +636,7 @@ impl SushiBelt { let free_spots_before_last_inserter_front: u16 = front_inserters .inserters .iter() - .map(|(i, _item)| i.offset) + .map(|(i, _item, _movetime, _hand_size)| i.offset) .sum(); let length_after_last_inserter = TryInto::::try_into(front_len) .expect("Belt should be max u16::MAX long") @@ -614,7 +644,7 @@ impl SushiBelt { - TryInto::::try_into(num_front_inserters) .expect("Belt should be max u16::MAX long"); - if let Some((i, _item)) = back_inserters.inserters.get_mut(0) { + if let Some((i, _item, _movetime, _hand_size)) = back_inserters.inserters.get_mut(0) { i.offset += length_after_last_inserter; } @@ -1047,7 +1077,7 @@ impl Belt for SushiBelt { }); if side == Side::FRONT { - if let Some((i, _ietm)) = self.inserters.inserters.first_mut() { + if let Some((i, _item, _movetime, _hand_size)) = self.inserters.inserters.first_mut() { i.offset -= amount - pos_after_last_removed_inserter; } } diff --git a/src/data/mod.rs b/src/data/mod.rs index d016035..2add391 100644 --- a/src/data/mod.rs +++ b/src/data/mod.rs @@ -1511,33 +1511,34 @@ impl RawDataStore { instantly_finished_technologies, belt_infos: vec![ + // TODO: For now only have one kind of transport belt since connection is still borked BeltInfo { name: "factory_game::fast_transport_belt".to_string().into(), - display_name: "Fast Transport Belt".to_string(), + display_name: "Express Transport Belt".to_string(), has_underground: Some(BeltUndergroundInfo { max_distance: 9 }), has_splitter: None, timer_increase: 45 * 2, }, - BeltInfo { - name: "factory_game::transport_belt".to_string().into(), - display_name: "Transport Belt".to_string(), - has_underground: Some(BeltUndergroundInfo { max_distance: 6 }), - has_splitter: None, - timer_increase: 15 * 2, - }, + // BeltInfo { + // name: "factory_game::transport_belt".to_string().into(), + // display_name: "Transport Belt".to_string(), + // has_underground: Some(BeltUndergroundInfo { max_distance: 6 }), + // has_splitter: None, + // timer_increase: 15 * 2, + // }, ], // FIXME: mining_drill_info: vec![ - MiningDrillInfo { - name: "factory_game::mining_drill".to_string().into(), - display_name: "Electric Mining Drill".to_string().into(), - size: [3, 3], - mining_range: [5, 5], - base_speed: 20, - resource_drain: (1, 1), - output_offset: Some([1, -1]), - }, + // MiningDrillInfo { + // name: "factory_game::mining_drill".to_string().into(), + // display_name: "Electric Mining Drill".to_string().into(), + // size: [3, 3], + // mining_range: [5, 5], + // base_speed: 20, + // resource_drain: (1, 1), + // output_offset: Some([1, -1]), + // }, MiningDrillInfo { name: "factory_game::mining_drill_small_no_output" .to_string() diff --git a/src/frontend/action/action_state_machine.rs b/src/frontend/action/action_state_machine.rs index b494cf5..a699881 100644 --- a/src/frontend/action/action_state_machine.rs +++ b/src/frontend/action/action_state_machine.rs @@ -14,6 +14,7 @@ use log::{error, warn}; use petgraph::Directed; use crate::{ + NewWithDataStore, app_state::SimulationState, belt::splitter::SplitterDistributionMode, blueprint::Blueprint, @@ -46,10 +47,63 @@ pub const WIDTH_PER_LEVEL: usize = 16; pub struct Hotbar { slots: [Option>; 10], } -impl Default for Hotbar { - fn default() -> Self { +impl NewWithDataStore for Hotbar { + fn new( + data_store: impl std::borrow::Borrow>, + ) -> Self { Self { - slots: array::from_fn(|_| None), + slots: array::from_fn(|idx| match idx + 1 { + 1 => Some(HeldObject::Entity(PlaceEntityType::Lab { + pos: Position { x: 0, y: 0 }, + ty: 0, + })), + 2 => Some(HeldObject::Entity(PlaceEntityType::Assembler { + pos: Position { x: 0, y: 0 }, + ty: 0, + rotation: Dir::North, + })), + 3 => Some(HeldObject::Entity(PlaceEntityType::Belt { + pos: Position { x: 0, y: 0 }, + ty: 0, + direction: Dir::North, + })), + 4 => Some(HeldObject::Entity(PlaceEntityType::Inserter { + pos: Position { x: 0, y: 0 }, + ty: 0, + dir: Dir::North, + filter: None, + user_movetime: None, + })), + 5 => Some(HeldObject::Entity(PlaceEntityType::PowerPole { + pos: Position { x: 0, y: 0 }, + ty: 0, + })), + 6 => Some(HeldObject::Entity(PlaceEntityType::Chest { + pos: Position { x: 0, y: 0 }, + ty: 0, + })), + 7 => Some(HeldObject::Entity(PlaceEntityType::Underground { + pos: Position { x: 0, y: 0 }, + ty: 0, + direction: Dir::North, + underground_dir: UndergroundDir::Entrance, + })), + 8 => Some(HeldObject::Entity(PlaceEntityType::SolarPanel { + pos: Position { x: 0, y: 0 }, + ty: 0, + })), + 9 => Some(HeldObject::Entity(PlaceEntityType::FluidTank { + pos: Position { x: 0, y: 0 }, + ty: 0, + rotation: Dir::North, + })), + 10 => Some(HeldObject::Entity(PlaceEntityType::Beacon { + pos: Position { x: 0, y: 0 }, + ty: 0, + })), + + _ => unreachable!(), + }), } } } @@ -222,7 +276,7 @@ impl current_fork_save_in_progress: None, - hotbar: Hotbar::default(), + hotbar: Hotbar::new(data_store), hotbar_window_open: true, } } @@ -1290,7 +1344,7 @@ impl data_store: &DataStore, ) { // Possible Actions - ui.columns_const(|uis: &mut [egui::Ui; 12]| { + ui.columns_const(|uis: &mut [egui::Ui; 13]| { for (i, ui) in uis.iter_mut().enumerate() { let ty_count = match i { 0 => data_store.assembler_info.len(), @@ -1305,6 +1359,7 @@ impl 9 => data_store.solar_panel_info.len(), 10 => data_store.lab_info.len(), 11 => data_store.inserter_infos.len(), + 12 => data_store.mining_drill_info.len(), _ => unreachable!(), } as u8; @@ -1413,6 +1468,14 @@ impl }), &data_store.inserter_infos[ty as usize].display_name, ), + 12 => ( + HeldObject::Entity(PlaceEntityType::MiningDrill { + pos: Position { x: 0, y: 0 }, + ty, + rotation: Dir::North, + }), + &data_store.mining_drill_info[ty as usize].display_name, + ), _ => unreachable!(), }; diff --git a/src/frontend/world/tile.rs b/src/frontend/world/tile.rs index d19f7c1..d6c53e2 100644 --- a/src/frontend/world/tile.rs +++ b/src/frontend/world/tile.rs @@ -419,7 +419,8 @@ fn try_attaching_fluids( conn_fluid, conn_storage, data_store, - ), + ) + .unwrap(), conn_pos, ) }, @@ -431,7 +432,8 @@ fn try_attaching_fluids( conn_fluid, conn_storage, data_store, - ), + ) + .unwrap(), conn_pos, ) }, @@ -3214,17 +3216,13 @@ impl World { - let dest_storage_untranslated = match dest_storage_untranslated { + let dest_storage = match dest_storage_untranslated { Static::Done(storage) => storage, Static::ToInstantiate => { unreachable!("Storages must be instantiated before calling this function") }, }; - let dest_storage = dest_storage_untranslated - .translate(filter, data_store) - .unwrap(); - match simulation_state.factory.belts.add_belt_storage_inserter( filter, start_belt_id, @@ -3233,7 +3231,8 @@ impl World World { - let start_storage_untranslated = match start_storage_untranslated { + let start_storage = match start_storage_untranslated { Static::Done(storage) => storage, Static::ToInstantiate => { unreachable!("Storages must be instantiated before calling this function") }, }; - let start_storage = start_storage_untranslated - .translate(filter, data_store) - .unwrap(); - match simulation_state.factory.belts.add_storage_belt_inserter( filter, dest_belt_id, @@ -3271,7 +3266,8 @@ impl World World { - let start_storage_untranslated = match start_storage_untranslated { + let start_storage = match start_storage_untranslated { Static::Done(storage) => storage, Static::ToInstantiate => { unreachable!("Storages must be instantiated before calling this function") }, }; - let dest_storage_untranslated = match dest_storage_untranslated { + let dest_storage = match dest_storage_untranslated { Static::Done(storage) => storage, Static::ToInstantiate => { unreachable!("Storages must be instantiated before calling this function") }, }; - let start_storage = start_storage_untranslated - .translate(filter, data_store) - .unwrap(); - let dest_storage = dest_storage_untranslated - .translate(filter, data_store) - .unwrap(); - let index = simulation_state.factory.storage_storage_inserters.add_ins( filter, movetime.into(), diff --git a/src/inserter/belt_storage_inserter.rs b/src/inserter/belt_storage_inserter.rs index c41cc76..27a67a9 100644 --- a/src/inserter/belt_storage_inserter.rs +++ b/src/inserter/belt_storage_inserter.rs @@ -82,7 +82,8 @@ impl BeltStorageInserter<{ Dir::BeltToStorage }> { } }, InserterState::WaitingForSpaceInDestination(count) => { - let (max_insert, old) = index_fake_union(storages, self.storage_id, grid_size); + let (max_insert, old) = + index_fake_union(todo!(), storages, self.storage_id, grid_size); let to_insert = min(count, *max_insert - *old); if to_insert > 0 { @@ -134,7 +135,8 @@ impl BeltStorageInserter<{ Dir::StorageToBelt }> { match self.state { InserterState::WaitingForSourceItems(count) => { - let (_max_insert, old) = index_fake_union(storages, self.storage_id, grid_size); + let (_max_insert, old) = + index_fake_union(todo!(), storages, self.storage_id, grid_size); let to_extract = min(max_hand_size - count, *old); diff --git a/src/inserter/belt_storage_inserter_non_const_gen.rs b/src/inserter/belt_storage_inserter_non_const_gen.rs index be40213..8a67da5 100644 --- a/src/inserter/belt_storage_inserter_non_const_gen.rs +++ b/src/inserter/belt_storage_inserter_non_const_gen.rs @@ -88,6 +88,7 @@ impl BeltStorageInserterDyn { #[inline(always)] pub fn update( &mut self, + item_id: usize, mut loc: impl DerefMut + Deref, storages: SingleItemStorages, movetime: u8, @@ -109,7 +110,8 @@ impl BeltStorageInserterDyn { } }, DynInserterState::BSWaitingForSpaceInDestination(count) => { - let (max_insert, old) = index_fake_union(storages, self.storage_id, grid_size); + let (max_insert, old) = + index_fake_union(item_id, storages, self.storage_id, grid_size); let to_insert = min(count, *max_insert - *old); if to_insert > 0 { @@ -144,7 +146,8 @@ impl BeltStorageInserterDyn { false }, DynInserterState::SBWaitingForSourceItems(count) => { - let (_max_insert, old) = index_fake_union(storages, self.storage_id, grid_size); + let (_max_insert, old) = + index_fake_union(item_id, storages, self.storage_id, grid_size); let to_extract = min(max_hand_size - count, *old); diff --git a/src/inserter/belt_storage_pure_buckets.rs b/src/inserter/belt_storage_pure_buckets.rs index a70a156..2be1991 100644 --- a/src/inserter/belt_storage_pure_buckets.rs +++ b/src/inserter/belt_storage_pure_buckets.rs @@ -613,6 +613,7 @@ impl BucketedStorageStorageInserterStore { } fn handle_waiting_for_item_ins( + item_id: usize, inserter: &mut UpdatingInserter, frontend: &mut BucketedStorageStorageInserterStoreFrontend, storages: SingleItemStorages, @@ -633,7 +634,8 @@ impl BucketedStorageStorageInserterStore { } }, Dir::StorageToBelt => { - let (_max_insert, old) = index_fake_union(storages, inserter.storage_id, grid_size); + let (_max_insert, old) = + index_fake_union(item_id, storages, inserter.storage_id, grid_size); let to_extract = min(inserter.max_hand_size - inserter.current_hand, *old); @@ -663,6 +665,7 @@ impl BucketedStorageStorageInserterStore { } fn handle_waiting_for_space_ins( + item_id: usize, inserter: &mut UpdatingInserter, frontend: &mut BucketedStorageStorageInserterStoreFrontend, storages: SingleItemStorages, @@ -673,7 +676,8 @@ impl BucketedStorageStorageInserterStore { ) -> bool { match DIR { Dir::BeltToStorage => { - let (max_insert, old) = index_fake_union(storages, inserter.storage_id, grid_size); + let (max_insert, old) = + index_fake_union(item_id, storages, inserter.storage_id, grid_size); let to_insert = min(inserter.current_hand, *max_insert - *old); @@ -751,6 +755,7 @@ impl BucketedStorageStorageInserterStore { #[profiling::function] pub fn update( &mut self, + item_id: usize, frontend: &mut BucketedStorageStorageInserterStoreFrontend, storages: SingleItemStorages, belts: &mut [SmartBelt], @@ -808,6 +813,7 @@ impl BucketedStorageStorageInserterStore { Dir::BeltToStorage => { let now_moving = self.waiting_for_item.extract_if(.., |inserter| { Self::handle_waiting_for_item_ins::( + item_id, inserter, frontend, storages, @@ -835,6 +841,7 @@ impl BucketedStorageStorageInserterStore { Dir::StorageToBelt => { let now_moving = self.waiting_for_item.extract_if(.., |inserter| { Self::handle_waiting_for_item_ins::( + item_id, inserter, frontend, storages, @@ -912,6 +919,7 @@ impl BucketedStorageStorageInserterStore { ItemIdxType, { Dir::BeltToStorage }, >( + item_id, inserter, frontend, storages, @@ -944,6 +952,7 @@ impl BucketedStorageStorageInserterStore { ItemIdxType, { Dir::StorageToBelt }, >( + item_id, inserter, frontend, storages, @@ -1312,6 +1321,7 @@ mod test { for (storage, belt) in values.into_iter().zip(belt_ids) { if random::() < 1 { store[item].0.update( + item, &mut frontend[item], &mut [ (max_insert.as_slice(), storages_in[item].as_mut_slice()), @@ -1322,6 +1332,7 @@ mod test { current_tick, ); store[item].1.update( + item, &mut frontend[item], &mut [ (max_insert.as_slice(), storages_in[item].as_mut_slice()), @@ -1394,6 +1405,7 @@ mod test { } } store.0.update( + 0, frontend, &mut [ (max_insert.as_slice(), storage_in.as_mut_slice()), @@ -1404,6 +1416,7 @@ mod test { current_tick, ); store.1.update( + 0, frontend, &mut [ (max_insert.as_slice(), storage_in.as_mut_slice()), diff --git a/src/inserter/mod.rs b/src/inserter/mod.rs index 4da4953..c19b6ec 100644 --- a/src/inserter/mod.rs +++ b/src/inserter/mod.rs @@ -183,7 +183,7 @@ impl FakeUnionStorage { item: Item, storage: Storage, data_store: &DataStore, - ) -> Self { + ) -> Result { let grid_size: usize = grid_size(item, data_store); let static_size: usize = static_size(item, data_store); @@ -195,11 +195,16 @@ impl FakeUnionStorage { recipe_idx_with_this_item, index, } => { - assert!( - recipe_idx_with_this_item.into_usize() - < data_store.num_recipes_with_item[item.into_usize()] - ); - Self { + let recipe_idx_with_this_item = *data_store + .recipe_to_translated_index + .get(&( + Recipe { + id: recipe_idx_with_this_item, + }, + item, + )) + .ok_or(())?; + Ok(Self { index: u32::from(index), grid_or_static_flag: u16::from(grid) .checked_add(u16::try_from(grid_offset).unwrap()) @@ -208,21 +213,21 @@ impl FakeUnionStorage { recipe_idx_with_this_item, )) .unwrap(), - } + }) }, - Storage::Lab { grid, index } => Self { + Storage::Lab { grid, index } => Ok(Self { index: u32::from(index), grid_or_static_flag: u16::from(grid) .checked_add(u16::try_from(grid_offset).unwrap()) .expect("Grid ID too high (would overflow the grid_or_static)"), recipe_idx_with_this_item: data_store.num_recipes_with_item[usize_from(item.id)] as u16, - }, - Storage::Static { index, static_id } => Self { + }), + Storage::Static { index, static_id } => Ok(Self { index: u32::try_from(index).unwrap(), grid_or_static_flag: 0, recipe_idx_with_this_item: static_id, - }, + }), } } } @@ -248,30 +253,6 @@ pub enum Storage { } impl Storage { - pub fn translate( - self, - item: Item, - data_store: &DataStore, - ) -> Option { - match self { - Storage::Assembler { - grid, - recipe_idx_with_this_item, - index, - } => Some(Storage::Assembler { - grid, - recipe_idx_with_this_item: *data_store.recipe_to_translated_index.get(&( - Recipe { - id: recipe_idx_with_this_item, - }, - item, - ))?, - index, - }), - storage => Some(storage), - } - } - pub fn change_grid(self, new_id: PowerGridIdentifier) -> Self { match self { Storage::Assembler { @@ -291,43 +272,13 @@ impl Storage { } } - fn into_inner_and_outer_indices( - self, - num_grids_total: usize, - num_recipes: usize, - grid_size: usize, - ) -> (usize, usize) { - match self { - Storage::Assembler { - grid, - recipe_idx_with_this_item, - index, - } => { - debug_assert!( - usize_from(recipe_idx_with_this_item) < num_recipes, - "The recipe stored in an inserter needs to be translated!" - ); - let outer = Into::::into(grid) * grid_size - + Into::::into(recipe_idx_with_this_item); - (outer, index.try_into().unwrap()) - }, - Storage::Lab { grid, index } => { - let outer = Into::::into(grid) * grid_size + num_recipes; - (outer, index.try_into().unwrap()) - }, - Storage::Static { static_id, index } => { - // debug_assert!(usize::from(static_id) < data_store.num_different_static_containers); - let outer = num_grids_total * grid_size + Into::::into(static_id as u8); - (outer, index.try_into().unwrap()) - }, - } - } - - fn into_inner_and_outer_indices_with_statics_at_zero( + fn into_inner_and_outer_indices_with_statics_at_zero( self, + item: Item, num_recipes: usize, grid_size: usize, static_size: usize, + data_store: &DataStore, ) -> (usize, usize) { let grid_offset = static_size.div_ceil(grid_size); @@ -337,6 +288,15 @@ impl Storage { recipe_idx_with_this_item, index, } => { + let recipe_idx_with_this_item = *data_store + .recipe_to_translated_index + .get(&( + Recipe { + id: recipe_idx_with_this_item, + }, + item, + )) + .unwrap(); debug_assert!( usize_from(recipe_idx_with_this_item) < num_recipes, "The recipe stored in an inserter needs to be translated!" @@ -378,6 +338,17 @@ pub enum StaticID { PureSoloOwnedMiningDrill = 1, } +impl TryFrom for StaticID { + type Error = (); + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(Self::Chest), + 1 => Ok(Self::PureSoloOwnedMiningDrill), + _ => Err(()), + } + } +} + #[cfg(test)] mod test { use crate::DATA_STORE; @@ -443,28 +414,28 @@ mod test { proptest! { - #[test] - fn storage_and_fake_union_result_in_same_indices((item, num_grids, storage) in union_test_input()) { - let grid_size = grid_size(item, &DATA_STORE); + // #[test] + // fn storage_and_fake_union_result_in_same_indices((item, num_grids, storage) in union_test_input()) { + // let grid_size = grid_size(item, &DATA_STORE); - let storage_union = FakeUnionStorage::from_storage(item, storage, &DATA_STORE); + // let storage_union = FakeUnionStorage::from_storage(item, storage, &DATA_STORE); - let union_indices = storage_union.into_inner_and_outer_indices(num_grids.into(), grid_size); + // let union_indices = storage_union.into_inner_and_outer_indices(num_grids.into(), grid_size); - let storage_indices = storage.into_inner_and_outer_indices(num_grids.into(), DATA_STORE.num_recipes_with_item[usize_from(item.id)], grid_size); + // let storage_indices = storage.into_inner_and_outer_indices(Item::try_from(0).unwrap(), num_grids.into(), DATA_STORE.num_recipes_with_item[usize_from(item.id)], grid_size, &DATA_STORE); - prop_assert_eq!(union_indices, storage_indices); - } + // prop_assert_eq!(union_indices, storage_indices); + // } #[test] fn storage_and_fake_union_result_in_same_indices_with_statics_at_zero((item, _num_grids, storage) in union_test_input()) { let grid_size = grid_size(item, &DATA_STORE); - let storage_union = FakeUnionStorage::from_storage_with_statics_at_zero(item, storage, &DATA_STORE); + let storage_union = FakeUnionStorage::from_storage_with_statics_at_zero(item, storage, &DATA_STORE).unwrap(); let union_indices = storage_union.into_inner_and_outer_indices_with_statics_at_zero(grid_size); - let storage_indices = storage.into_inner_and_outer_indices_with_statics_at_zero(DATA_STORE.num_recipes_with_item[usize_from(item.id)], grid_size, static_size(item, &DATA_STORE)); + let storage_indices = storage.into_inner_and_outer_indices_with_statics_at_zero(item, DATA_STORE.num_recipes_with_item[usize_from(item.id)], grid_size, static_size(item, &DATA_STORE), &DATA_STORE); prop_assert_eq!(union_indices, storage_indices); } diff --git a/src/inserter/storage_storage_inserter.rs b/src/inserter/storage_storage_inserter.rs index 9b1f087..8eb259c 100644 --- a/src/inserter/storage_storage_inserter.rs +++ b/src/inserter/storage_storage_inserter.rs @@ -43,6 +43,7 @@ impl StorageStorageInserter { pub fn update( &mut self, + item_id: usize, storages: SingleItemStorages, movetime: u8, max_hand_size: ITEMCOUNTTYPE, @@ -53,7 +54,8 @@ impl StorageStorageInserter { match self.state { InserterState::WaitingForSourceItems(count) => { - let (_max_insert, old) = index_fake_union(storages, self.storage_id_in, grid_size); + let (_max_insert, old) = + index_fake_union(item_id, storages, self.storage_id_in, grid_size); let to_extract = min(max_hand_size - count, *old); @@ -69,7 +71,8 @@ impl StorageStorageInserter { } }, InserterState::WaitingForSpaceInDestination(count) => { - let (max_insert, old) = index_fake_union(storages, self.storage_id_out, grid_size); + let (max_insert, old) = + index_fake_union(item_id, storages, self.storage_id_out, grid_size); let to_insert = min(count, *max_insert - *old); diff --git a/src/inserter/storage_storage_with_buckets.rs b/src/inserter/storage_storage_with_buckets.rs index 53a2ac4..8492fbc 100644 --- a/src/inserter/storage_storage_with_buckets.rs +++ b/src/inserter/storage_storage_with_buckets.rs @@ -653,7 +653,8 @@ impl BucketedStorageStorageInserterStore { _current_tick: u32, _movetime: u16, ) -> bool { - let (_max_insert, old) = index_fake_union(storages, inserter.storage_id_in, grid_size); + let (_max_insert, old) = + index_fake_union(todo!(), storages, inserter.storage_id_in, grid_size); let to_extract = min(inserter.max_hand_size - inserter.current_hand, *old); @@ -686,7 +687,8 @@ impl BucketedStorageStorageInserterStore { _current_tick: u32, _movetime: u16, ) -> bool { - let (max_insert, old) = index_fake_union(storages, inserter.storage_id_out, grid_size); + let (max_insert, old) = + index_fake_union(todo!(), storages, inserter.storage_id_out, grid_size); let to_insert = min(inserter.current_hand, *max_insert - *old); diff --git a/src/inserter/storage_storage_with_buckets_indirect.rs b/src/inserter/storage_storage_with_buckets_indirect.rs index 305ffc5..475a111 100644 --- a/src/inserter/storage_storage_with_buckets_indirect.rs +++ b/src/inserter/storage_storage_with_buckets_indirect.rs @@ -272,6 +272,7 @@ impl BucketedStorageStorageInserterStore { } fn handle_waiting_for_item_ins( + item_id: usize, inserter: &mut InserterState, bucket_data: &mut InserterBucketData, @@ -282,7 +283,7 @@ impl BucketedStorageStorageInserterStore { ) -> bool { let storage_id = bucket_data.storage_id_in; - let (_max_insert, old) = index_fake_union(storages, storage_id, grid_size); + let (_max_insert, old) = index_fake_union(item_id, storages, storage_id, grid_size); let old_val = *old; let max_hand_size = bucket_data.max_hand_size; @@ -309,6 +310,7 @@ impl BucketedStorageStorageInserterStore { } fn handle_waiting_for_space_ins( + item_id: usize, inserter: &mut InserterState, bucket_data: &mut InserterBucketData, @@ -319,7 +321,7 @@ impl BucketedStorageStorageInserterStore { ) -> bool { let storage_id = bucket_data.storage_id_out; - let (max_insert, old) = index_fake_union(storages, storage_id, grid_size); + let (max_insert, old) = index_fake_union(item_id, storages, storage_id, grid_size); let old_val = *old; let max_insert = *max_insert; @@ -456,6 +458,7 @@ impl BucketedStorageStorageInserterStore { ); let now_moving = self.waiting_for_item.extract_if(start..end, |inserter| { Self::handle_waiting_for_item_ins( + item_id, &mut self.inserters[inserter.index.index as usize], inserter, storages, @@ -529,6 +532,7 @@ impl BucketedStorageStorageInserterStore { self.waiting_for_space_in_destination .extract_if(start..end, |inserter| { Self::handle_waiting_for_space_ins( + item_id, &mut self.inserters[inserter.index.index as usize], inserter, storages, diff --git a/src/lab.rs b/src/lab.rs index ca53c87..969f3a6 100644 --- a/src/lab.rs +++ b/src/lab.rs @@ -26,7 +26,7 @@ pub struct MultiLabStore { pub sciences: Box<[Vec]>, timer: Vec, prod_timer: Vec, - holes: Vec, + pub holes: Vec, /// Base Crafting Speed in 5% increments /// i.e. 28 => 140% Crafting speed diff --git a/src/liquid/mod.rs b/src/liquid/mod.rs index c199187..3a4cc58 100644 --- a/src/liquid/mod.rs +++ b/src/liquid/mod.rs @@ -682,6 +682,7 @@ impl FluidSystemStore { { if *inc == old_storage { *inc = new_storage; + log::trace!("Found connection to update"); } } for outgoing in self.fluid_systems_with_fluid[fluid.into_usize()][id.index] @@ -693,10 +694,13 @@ impl FluidSystemStore { { if *outgoing == old_storage { *outgoing = new_storage; + log::trace!("Found connection to update"); } } }, - None => {}, + None => { + log::trace!("No need to update fluid connections for empty fluid network"); + }, } } @@ -1553,6 +1557,7 @@ impl FluidSystem { } pub fn update_fluid_system( + item_id: usize, hot_data: &mut FluidSystemHotData, storages: SingleItemStorages, grid_size: usize, @@ -1566,7 +1571,7 @@ pub fn update_fluid_system( if hot_data.current_fluid_level == 0 { break; } - let (max, data) = index_fake_union(storages, outgoing_conn, grid_size); + let (max, data) = index_fake_union(item_id, storages, outgoing_conn, grid_size); let amount_wanted = *max - *data; let amount_extracted = min( @@ -1592,7 +1597,7 @@ pub fn update_fluid_system( if hot_data.current_fluid_level == hot_data.storage_capacity { break; } - let (_max, data) = index_fake_union(storages, incoming_conn, grid_size); + let (_max, data) = index_fake_union(item_id, storages, incoming_conn, grid_size); let amount_wanted = *data; let amount_extracted = min( diff --git a/src/par_generation.rs b/src/par_generation.rs index 62cd876..5634961 100644 --- a/src/par_generation.rs +++ b/src/par_generation.rs @@ -1425,7 +1425,8 @@ fn pipe_stage( _ => unreachable!(), }, data_store, - ), + ) + .unwrap(), pos, ) }), diff --git a/src/power/mod.rs b/src/power/mod.rs index 0d84e66..1126760 100644 --- a/src/power/mod.rs +++ b/src/power/mod.rs @@ -341,12 +341,51 @@ impl PowerGridStorage true, + _ => false, + }) + .count(); + let num_labs_in_sim = + grid.lab_stores.sciences[0].len() - grid.lab_stores.holes.len(); + + assert_eq!(num_labs_in_list, num_labs_in_graph); + assert_eq!(num_labs_in_list, num_labs_in_sim); + + let num_assemblers_in_list: usize = + grid.num_assemblers_of_type.iter().copied().sum(); + let num_assemblers_in_graph = grid + .grid_graph + .weak_components() + .filter(|grid_entity| match grid_entity { + (_, PowerGridEntity::Assembler { .. }) => true, + _ => false, + }) + .count(); + let num_assemblers_in_sim: usize = grid.stores.num_assemblers(); + + assert_eq!(num_assemblers_in_list, num_assemblers_in_graph); + assert_eq!(num_assemblers_in_list, num_assemblers_in_sim); + } + } let mut connected_poles: Vec<_> = connected_poles.into_iter().collect(); let ret = if !connected_poles.is_empty() { // Find the largest grid, and choose it as the base // If the size is a tossup pick the one with the smaller grid_id to reduce holes in the power_grid list - let grid = connected_poles + let kept_grid_id = connected_poles .iter() .map(|pos| self.pole_pos_to_grid_id[pos]) .max_by_key(|grid_id| { @@ -364,7 +403,7 @@ impl PowerGridStorage PowerGridStorage>() { // No need to merge a grid with itself. - if other_grid == grid { + if other_grid == kept_grid_id { continue; } ran_once = true; let storage_updates = self .merge_power_grids( - grid, + kept_grid_id, other_grid, data_store, pole_position, connected_poles.iter().copied(), ) .into_iter() - .flatten(); + .flatten() + .map(|update| { + // TODO: Make debug assert + assert_eq!(update.new_grid, kept_grid_id); + assert_eq!(update.old_grid, other_grid); + + update + }); + let old_len = storage_update_vec.len(); storage_update_vec.extend(storage_updates); + let new_updates = storage_update_vec.len() - old_len; } assert!(ran_once); #[cfg(debug_assertions)] { - for key in self.power_grids[grid as usize].grid_graph.keys() { + for key in self.power_grids[kept_grid_id as usize].grid_graph.keys() { if let Some(index) = connected_poles.iter().position(|v| v == key) { connected_poles.remove(index); } @@ -419,7 +467,7 @@ impl PowerGridStorage PowerGridStorage { max_lazy_power: Watt, pub last_power_mult: u8, + pub power_mult_at_last_beacon_update: u8, pub power_mult_history: Timeline, // FIXME: Not actually storing where the power consumption/production originates is not very useful :/ // pub power_consumption_history: Timeline, @@ -209,6 +210,7 @@ impl PowerGrid PowerGrid PowerGrid PowerGrid= other.power_mult_at_last_beacon_update { + self.power_mult_at_last_beacon_update + } else { + other.power_mult_at_last_beacon_update + } + }, power_mult_history: { if self.last_power_consumption >= other.last_power_consumption { self.power_mult_history @@ -2312,6 +2324,7 @@ impl PowerGrid, ) -> ( ResearchProgress, @@ -2641,23 +2654,65 @@ impl PowerGrid, (_, _, _))> = if next_power_mult - < MIN_BEACON_POWER_MULT - && self.last_power_mult >= MIN_BEACON_POWER_MULT - { - // Disable beacons (But keep power consumption modifier unchanged, to prevent flickering) - self.beacon_affected_entities - .iter() - .map(|(k, v)| (*k, (-v.0, -v.1, -0))) - .collect() - } else if next_power_mult >= MIN_BEACON_POWER_MULT - && self.last_power_mult < MIN_BEACON_POWER_MULT - { - // Enable beacons (But keep power consumption modifier unchanged, to prevent flickering) - self.beacon_affected_entities - .iter() - .map(|(k, v)| (*k, (v.0, v.1, 0))) - .collect() + // let beacon_updates: Vec<(BeaconAffectedEntity<_>, (_, _, _))> = if next_power_mult + // < MIN_BEACON_POWER_MULT + // && self.last_power_mult >= MIN_BEACON_POWER_MULT + // { + // // Disable beacons (But keep power consumption modifier unchanged, to prevent flickering) + // self.beacon_affected_entities + // .iter() + // .map(|(k, v)| (*k, (-v.0, -v.1, -0))) + // .collect() + // } else if next_power_mult >= MIN_BEACON_POWER_MULT + // && self.last_power_mult < MIN_BEACON_POWER_MULT + // { + // // Enable beacons (But keep power consumption modifier unchanged, to prevent flickering) + // self.beacon_affected_entities + // .iter() + // .map(|(k, v)| (*k, (v.0, v.1, 0))) + // .collect() + // } else { + // vec![] + // }; + + // This is scaling beacon effect linearly + // FIXME: For this to be correct, when adding a beacon/adding modules to beacon etc, we need to calculate the effect the same way + + // TODO: AFAIK Factorio does not update the effects of beacons every tick but more sparsely (to save UPS) + // For now I will do the same, and only update the beacon effectiveness every 60 ticks (1/seconds) + // We are still much more effective than Factorio here since AFAIK, they need to update beacosn even if the power satisfaction (and as such the beacon effect) did not change + // In that case I can just not do any updates + let beacon_updates = if current_tick % 60 == 0 { + let ret = if next_power_mult != self.power_mult_at_last_beacon_update { + profiling::scope!("Generate Beacon updates"); + self.beacon_affected_entities + .iter() + .map(|(&entity, &effect)| { + let effect: [i16; 3] = effect.into(); + let old_effect = effect.map(|e| { + i32::from(e) * i32::from(self.power_mult_at_last_beacon_update) / 64 + }); + let new_effect = + effect.map(|e| i32::from(e) * i32::from(next_power_mult) / 64); + + let change = old_effect + .into_iter() + .zip(new_effect) + .map(|(old, new)| new - old) + .map(|v| v.try_into().unwrap()) + .collect_array() + .unwrap(); + + (entity, change.into()) + }) + .collect() + } else { + vec![] + }; + + self.power_mult_at_last_beacon_update = next_power_mult; + + ret } else { vec![] }; diff --git a/src/rendering/eframe_app.rs b/src/rendering/eframe_app.rs index 85b3864..10a12c9 100644 --- a/src/rendering/eframe_app.rs +++ b/src/rendering/eframe_app.rs @@ -325,7 +325,7 @@ impl eframe::App for App { game_state_receiver: recv, }; } - } + } // else if ui.button("Load Debug Save").clicked() { // #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))] // if let Some(path) = rfd::FileDialog::new() diff --git a/src/rendering/mod.rs b/src/rendering/mod.rs index d3782ca..62a9ac8 100644 --- a/src/rendering/mod.rs +++ b/src/rendering/mod.rs @@ -169,6 +169,8 @@ pub struct TextureAtlas { underground: enum_map::EnumMap>, + accumulator: EntitySprite, + mining_drill: EntitySprite, solar_panel: EntitySprite, @@ -273,6 +275,7 @@ fn texture_atlas() -> TextureAtlas { no_power: entity_sprite_from_path_scaled!("temp_assets/no_power.png", 1, 1.0), assembler: entity_sprite_from_path_tiling!("temp_assets/assembler.png", 1), + accumulator: entity_sprite_from_path_tiling!("temp_assets/assembler.png", 1), chest: entity_sprite_from_path_tiling!("temp_assets/outside_world.png", 1), items: vec![sprite_from_path!("temp_assets/plate.png", 1); 200].into_boxed_slice(), @@ -495,6 +498,8 @@ fn texture_atlas() -> TextureAtlas { 1.0 / 2.0 ), + accumulator: entity_sprite_from_path_tiling!("temp_assets/krastorio/energy-storage.png", 1), + lab: entity_sprite_from_path_tiling!("temp_assets/krastorio/advanced-lab.png", 1), dark_square: sprite_from_path!("temp_assets/dark_square.png", 1), diff --git a/src/rendering/render_world.rs b/src/rendering/render_world.rs index 30ee5a2..7f57a49 100644 --- a/src/rendering/render_world.rs +++ b/src/rendering/render_world.rs @@ -1,6 +1,6 @@ use crate::belt::belt::Belt; -use crate::belt::smart::{HAND_SIZE, NUM_BELT_LOCS_SEARCHED, SmartBelt}; use crate::belt::smart::{NUM_BELT_FREE_CACHE_HITS, NUM_BELT_UPDATES}; +use crate::belt::smart::{NUM_BELT_LOCS_SEARCHED, SmartBelt}; use crate::blueprint::blueprint_string::BlueprintString; use crate::chest::ChestSize; use crate::frontend::action::action_state_machine::ForkSaveInfo; @@ -909,11 +909,16 @@ pub fn render_world( let movetime: u16 = user_movetime.map(|v| v.into()).unwrap_or(data_store.inserter_infos[*ty as usize].swing_time_ticks).into(); match info { crate::frontend::world::tile::AttachedInserter::BeltStorage { id, belt_pos } => { - let hand_size = HAND_SIZE; let Some(state) = game_state.simulation_state.factory.belts.get_inserter_info_at(*id, *belt_pos) else { error!("Could not get rendering info for inserter!"); continue; }; + let hand_size = state.hand_size; + + // TODO: Due to clamping this does not currently hold: + // assert_eq!(movetime, u16::from(state.movetime)); + let movetime = u16::from(state.movetime); + let item = game_state.simulation_state.factory.belts.get_inserter_item(*id, *belt_pos); let (mut position, items): (f32, ITEMCOUNTTYPE) = match state.state { @@ -960,7 +965,7 @@ pub fn render_world( // TODO: }, crate::frontend::world::tile::AttachedInserter::StorageStorage { item, inserter } => { - let hand_size = HAND_SIZE; + let hand_size = data_store.inserter_infos[*ty as usize].base_hand_size; let item = *item; let state = game_state.simulation_state.factory.storage_storage_inserters.get_inserter(item, movetime, *inserter, current_tick); @@ -1205,7 +1210,7 @@ pub fn render_world( [usize::from(*ty)].size, 0, entity_layer); }, Entity::Accumulator { ty, pos, .. } => { - texture_atlas.chest.draw([ + texture_atlas.accumulator.draw([ draw_offset.0 + pos.x as f32, draw_offset.1 + pos.y as f32, ], data_store.solar_panel_info @@ -1702,7 +1707,7 @@ pub fn render_world( ); }, crate::frontend::world::tile::PlaceEntityType::Accumulator { pos, ty } => { - texture_atlas.chest.draw( + texture_atlas.accumulator.draw( [draw_offset.0 + pos.x as f32, draw_offset.1 + pos.y as f32], data_store.accumulator_info[usize::from(*ty)].size, 0, @@ -2201,7 +2206,6 @@ pub fn render_ui< lower_dec = 1.0; } - dbg!(lower_dec); lower_dec = lower_dec * ticks_per_value / 60.0 / 60.0; (0..40) @@ -2299,7 +2303,7 @@ pub fn render_ui< actions.push(ActionType::Remove(assembler_pos)); - let area = data_store.mining_drill_info[1].size(*rotation); + let area = data_store.mining_drill_info[0].size(*rotation); for x in assembler_pos.x..(assembler_pos.x + i32::from(area[0])) { for y in assembler_pos.y..(assembler_pos.y + i32::from(area[1])) { @@ -2332,7 +2336,7 @@ pub fn render_ui< entities: EntityPlaceOptions::Single(PlaceEntityType::MiningDrill { pos: assembler_pos, rotation: inserter_rotation, - ty: 1, + ty: 0, }), force: false, })); diff --git a/src/rendering/temp_assets/krastorio/energy-storage.png b/src/rendering/temp_assets/krastorio/energy-storage.png new file mode 100644 index 0000000..8629c67 Binary files /dev/null and b/src/rendering/temp_assets/krastorio/energy-storage.png differ diff --git a/src/storage_list.rs b/src/storage_list.rs index 3c2e82b..cfd7281 100644 --- a/src/storage_list.rs +++ b/src/storage_list.rs @@ -1,3 +1,4 @@ +use core::panic; use std::iter; use std::u16; @@ -5,6 +6,7 @@ use itertools::Itertools; use rayon::iter::IndexedParallelIterator; use strum::IntoEnumIterator; +use crate::DATA_STORE; use crate::assembler::MultiAssemblerStore; use crate::chest::MultiChestStore; use crate::item::Indexable; @@ -132,6 +134,7 @@ pub fn index<'a, 'b, RecipeIdxType: IdxTrait>( #[inline(always)] pub fn index_fake_union<'a, 'b>( + item_id: usize, slice: SingleItemStorages<'a, 'b>, storage_id: FakeUnionStorage, grid_size: usize, @@ -144,7 +147,94 @@ pub fn index_fake_union<'a, 'b>( "Out slice was out of bounds for storage_id {storage_id:?}. len was {len}, index was {outer}, grid_size was {grid_size}.", ); }; - (&subslice.0[inner], &mut subslice.1[inner]) + + let Some(max_insert) = subslice.0.get(inner) else { + let item = Item { + id: item_id.try_into().unwrap(), + }; + let static_size: usize = static_size(item, &DATA_STORE); + let is_static = (storage_id.grid_or_static_flag as usize) < static_size; + let index = storage_id.index; + + if !is_static { + let grid_id = storage_id.grid_or_static_flag as usize - static_size; + + let recipe = DATA_STORE.recipe_to_translated_index.iter().find( + |((_recipe, found_item), index)| { + item == *found_item + && u16::from(**index) == storage_id.recipe_idx_with_this_item + }, + ); + + if let Some(((r, _), _)) = recipe { + // we are a assembler + panic!( + "Failed FakeUnion Index for item {} for Assembler in grid {}, with recipe {} and index {}", + &DATA_STORE.item_names[item_id], + grid_id, + DATA_STORE.recipe_names[r.into_usize()], + index + ); + } else { + // We are a lab + panic!( + "Failed FakeUnion Index for item {} for Lab in grid {}, with index {}", + &DATA_STORE.item_names[item_id], grid_id, index + ); + } + } else { + let static_id = StaticID::try_from(storage_id.recipe_idx_with_this_item as u8).unwrap(); + + panic!( + "Failed FakeUnion Index for item {} for Static {:?}, with index {}", + &DATA_STORE.item_names[item_id], static_id, index + ); + } + }; + let Some(items) = subslice.1.get_mut(inner) else { + let item = Item { + id: item_id.try_into().unwrap(), + }; + let static_size: usize = static_size(item, &DATA_STORE); + let is_static = (storage_id.grid_or_static_flag as usize) < static_size; + let index = storage_id.index; + + if !is_static { + let grid_id = storage_id.grid_or_static_flag as usize - static_size; + + let recipe = DATA_STORE.recipe_to_translated_index.iter().find( + |((_recipe, found_item), index)| { + item == *found_item + && u16::from(**index) == storage_id.recipe_idx_with_this_item + }, + ); + + if let Some(((r, _), _)) = recipe { + // we are a assembler + panic!( + "Failed FakeUnion Index for item {} for Assembler in grid {}, with recipe {} and index {}", + &DATA_STORE.item_names[item_id], + grid_id, + DATA_STORE.recipe_names[r.into_usize()], + index + ); + } else { + // We are a lab + panic!( + "Failed FakeUnion Index for item {} for Lab in grid {}, with index {}", + &DATA_STORE.item_names[item_id], grid_id, index + ); + } + } else { + let static_id = StaticID::try_from(storage_id.recipe_idx_with_this_item as u8).unwrap(); + + panic!( + "Failed FakeUnion Index for item {} for Static {:?}, with index {}", + &DATA_STORE.item_names[item_id], static_id, index + ); + } + }; + (max_insert, items) } #[profiling::function] diff --git a/test_blueprints/solar_tile.bp b/test_blueprints/solar_tile.bp index 392ce4e..3934598 100644 --- a/test_blueprints/solar_tile.bp +++ b/test_blueprints/solar_tile.bp @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7c00805e515c34c57351071faa8c70b8891f0541ad4bae04c6f4ec0fec70ef02 -size 592 +oid sha256:9ffa0b20b177f29b19871a3b201fb430ef7892c99cb63e83248fec42604e3ec1 +size 572