mindustry logic execution, map- and schematic- parsing and rendering
map deserialization (#2)
bendn 2023-07-06
parent ed1449b · commit 45b0b22
-rw-r--r--Cargo.toml9
-rw-r--r--assets/blocks/environment/arkyic-boulder1.pngbin0 -> 594 bytes
-rw-r--r--assets/blocks/environment/arkyic-boulder2.pngbin0 -> 535 bytes
-rw-r--r--assets/blocks/environment/arkyic-boulder3.pngbin0 -> 491 bytes
-rw-r--r--assets/blocks/environment/basalt-boulder1.pngbin0 -> 682 bytes
-rw-r--r--assets/blocks/environment/basalt-boulder2.pngbin0 -> 683 bytes
-rw-r--r--assets/blocks/environment/beryllic-boulder1.pngbin0 -> 644 bytes
-rw-r--r--assets/blocks/environment/beryllic-boulder2.pngbin0 -> 678 bytes
-rw-r--r--assets/blocks/environment/boulder1.pngbin0 -> 414 bytes
-rw-r--r--assets/blocks/environment/boulder2.pngbin0 -> 402 bytes
-rw-r--r--assets/blocks/environment/carbon-boulder1.pngbin0 -> 655 bytes
-rw-r--r--assets/blocks/environment/carbon-boulder2.pngbin0 -> 672 bytes
-rw-r--r--assets/blocks/environment/crystal-blocks1.pngbin0 -> 3032 bytes
-rw-r--r--assets/blocks/environment/crystal-blocks2.pngbin0 -> 2397 bytes
-rw-r--r--assets/blocks/environment/crystal-blocks3.pngbin0 -> 1760 bytes
-rw-r--r--assets/blocks/environment/crystal-cluster1.pngbin0 -> 4884 bytes
-rw-r--r--assets/blocks/environment/crystal-cluster2.pngbin0 -> 2688 bytes
-rw-r--r--assets/blocks/environment/crystal-cluster3.pngbin0 -> 2392 bytes
-rw-r--r--assets/blocks/environment/crystal-orbs1.pngbin0 -> 2020 bytes
-rw-r--r--assets/blocks/environment/crystal-orbs2.pngbin0 -> 1682 bytes
-rw-r--r--assets/blocks/environment/crystal-orbs3.pngbin0 -> 1658 bytes
-rw-r--r--assets/blocks/environment/crystalline-boulder1.pngbin0 -> 605 bytes
-rw-r--r--assets/blocks/environment/crystalline-boulder2.pngbin0 -> 689 bytes
-rw-r--r--assets/blocks/environment/dacite-boulder1.pngbin0 -> 697 bytes
-rw-r--r--assets/blocks/environment/dacite-boulder2.pngbin0 -> 717 bytes
-rw-r--r--assets/blocks/environment/ferric-boulder1.pngbin0 -> 481 bytes
-rw-r--r--assets/blocks/environment/ferric-boulder2.pngbin0 -> 486 bytes
-rw-r--r--assets/blocks/environment/glowblob1.pngbin509 -> 0 bytes
-rw-r--r--assets/blocks/environment/pack.json9
-rw-r--r--assets/blocks/environment/pur-bush-bot.pngbin0 -> 205 bytes
-rw-r--r--assets/blocks/environment/pur-bush.pngbin0 -> 256 bytes
-rw-r--r--assets/blocks/environment/red-ice-boulder1.pngbin0 -> 691 bytes
-rw-r--r--assets/blocks/environment/red-ice-boulder2.pngbin0 -> 656 bytes
-rw-r--r--assets/blocks/environment/red-ice-boulder3.pngbin0 -> 623 bytes
-rw-r--r--assets/blocks/environment/red-stone-boulder1.pngbin0 -> 544 bytes
-rw-r--r--assets/blocks/environment/red-stone-boulder2.pngbin0 -> 559 bytes
-rw-r--r--assets/blocks/environment/red-stone-boulder3.pngbin0 -> 543 bytes
-rw-r--r--assets/blocks/environment/red-stone-boulder4.pngbin0 -> 621 bytes
-rw-r--r--assets/blocks/environment/redweed1.pngbin0 -> 551 bytes
-rw-r--r--assets/blocks/environment/redweed2.pngbin0 -> 307 bytes
-rw-r--r--assets/blocks/environment/redweed3.pngbin0 -> 417 bytes
-rw-r--r--assets/blocks/environment/rhyolite-boulder1.pngbin0 -> 605 bytes
-rw-r--r--assets/blocks/environment/rhyolite-boulder2.pngbin0 -> 565 bytes
-rw-r--r--assets/blocks/environment/rhyolite-boulder3.pngbin0 -> 554 bytes
-rw-r--r--assets/blocks/environment/sand-boulder1.pngbin0 -> 292 bytes
-rw-r--r--assets/blocks/environment/sand-boulder2.pngbin0 -> 313 bytes
-rw-r--r--assets/blocks/environment/shale-boulder1.pngbin0 -> 271 bytes
-rw-r--r--assets/blocks/environment/shale-boulder2.pngbin0 -> 297 bytes
-rw-r--r--assets/blocks/environment/snow-boulder1.pngbin0 -> 411 bytes
-rw-r--r--assets/blocks/environment/snow-boulder2.pngbin0 -> 410 bytes
-rw-r--r--assets/blocks/environment/spore-cluster1.pngbin0 -> 271 bytes
-rw-r--r--assets/blocks/environment/spore-cluster2.pngbin0 -> 303 bytes
-rw-r--r--assets/blocks/environment/spore-cluster3.pngbin0 -> 289 bytes
-rw-r--r--assets/blocks/environment/tendrils1.pngbin239 -> 0 bytes
-rw-r--r--assets/blocks/environment/tendrils2.pngbin245 -> 0 bytes
-rw-r--r--assets/blocks/environment/tendrils3.pngbin244 -> 0 bytes
-rw-r--r--assets/blocks/environment/vibrant-crystal-cluster1.pngbin0 -> 1682 bytes
-rw-r--r--assets/blocks/environment/vibrant-crystal-cluster2.pngbin0 -> 2631 bytes
-rw-r--r--assets/blocks/environment/vibrant-crystal-cluster3.pngbin0 -> 2838 bytes
-rw-r--r--assets/blocks/environment/white-tree-dead.pngbin0 -> 9855 bytes
-rw-r--r--assets/blocks/environment/white-tree.pngbin0 -> 7561 bytes
-rw-r--r--assets/blocks/environment/yellow-stone-boulder1.pngbin0 -> 636 bytes
-rw-r--r--assets/blocks/environment/yellow-stone-boulder2.pngbin0 -> 720 bytes
-rw-r--r--assets/blocks/environment/yellowcoral-center.pngbin159 -> 0 bytes
-rw-r--r--assets/blocks/environment/yellowcoral.pngbin495 -> 0 bytes
-rw-r--r--assets/blocks/production/cultivator-middle.pngbin331 -> 549 bytes
-rw-r--r--src/block/content.rs8
-rw-r--r--src/block/distribution.rs70
-rw-r--r--src/block/drills.rs12
-rw-r--r--src/block/environment.rs184
-rw-r--r--src/block/liquid.rs22
-rw-r--r--src/block/logic.rs10
-rw-r--r--src/block/mod.rs55
-rw-r--r--src/block/payload.rs55
-rw-r--r--src/block/power.rs4
-rw-r--r--src/block/production.rs35
-rw-r--r--src/block/simple.rs68
-rw-r--r--src/block/turrets.rs12
-rw-r--r--src/block/walls.rs12
-rw-r--r--src/data/dynamic.rs103
-rw-r--r--src/data/map.rs462
-rw-r--r--src/data/mod.rs160
-rw-r--r--src/data/planet.rs10
-rw-r--r--src/data/renderer.rs133
-rw-r--r--src/data/schematic.rs397
-rw-r--r--src/data/sector.rs44
-rw-r--r--src/data/weather.rs10
-rw-r--r--src/exe/draw.rs2
-rw-r--r--src/exe/map.rs22
-rw-r--r--src/exe/mod.rs2
-rw-r--r--src/fluid/mod.rs3
-rw-r--r--src/item/mod.rs2
-rw-r--r--src/item/storage.rs173
-rw-r--r--src/lib.rs7
-rw-r--r--src/logic/mod.rs6
-rw-r--r--src/modifier.rs2
-rw-r--r--src/team.rs16
-rw-r--r--src/unit/mod.rs3
-rw-r--r--src/utils/image.rs84
-rw-r--r--src/utils/mod.rs1
100 files changed, 1528 insertions, 679 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 60a51a1..ca487ee 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "mindus"
-version = "1.0.8"
+version = "1.1.0"
edition = "2021"
description = "A library for working with mindustry data formats (eg schematics) (fork of plandustry)"
authors = [
@@ -21,6 +21,12 @@ image = { version = "0.24.6", features = ["png"], default-features = false }
const-str = "0.5.5"
color-hex = "0.2.0"
zip = { version = "0.6.6", features = ["zstd"], default-features = false }
+tinyrand = "0.5.0"
+seq-macro = "0.3.4"
+tinyrand-std = "0.5.0"
+dashmap = "5.4.0"
+fast_image_resize = "2.7.3"
+thiserror = "1.0.41"
[build-dependencies]
zip = { version = "0.6.6", features = ["zstd"], default-features = false }
@@ -34,3 +40,4 @@ path = "src/exe/mod.rs"
debug = 2
opt-level = 3
lto = true
+incremental = true
diff --git a/assets/blocks/environment/arkyic-boulder1.png b/assets/blocks/environment/arkyic-boulder1.png
new file mode 100644
index 0000000..bec5753
--- /dev/null
+++ b/assets/blocks/environment/arkyic-boulder1.png
Binary files differ
diff --git a/assets/blocks/environment/arkyic-boulder2.png b/assets/blocks/environment/arkyic-boulder2.png
new file mode 100644
index 0000000..a9ecf43
--- /dev/null
+++ b/assets/blocks/environment/arkyic-boulder2.png
Binary files differ
diff --git a/assets/blocks/environment/arkyic-boulder3.png b/assets/blocks/environment/arkyic-boulder3.png
new file mode 100644
index 0000000..9e9f5cc
--- /dev/null
+++ b/assets/blocks/environment/arkyic-boulder3.png
Binary files differ
diff --git a/assets/blocks/environment/basalt-boulder1.png b/assets/blocks/environment/basalt-boulder1.png
new file mode 100644
index 0000000..f88209e
--- /dev/null
+++ b/assets/blocks/environment/basalt-boulder1.png
Binary files differ
diff --git a/assets/blocks/environment/basalt-boulder2.png b/assets/blocks/environment/basalt-boulder2.png
new file mode 100644
index 0000000..7c19c92
--- /dev/null
+++ b/assets/blocks/environment/basalt-boulder2.png
Binary files differ
diff --git a/assets/blocks/environment/beryllic-boulder1.png b/assets/blocks/environment/beryllic-boulder1.png
new file mode 100644
index 0000000..275c90b
--- /dev/null
+++ b/assets/blocks/environment/beryllic-boulder1.png
Binary files differ
diff --git a/assets/blocks/environment/beryllic-boulder2.png b/assets/blocks/environment/beryllic-boulder2.png
new file mode 100644
index 0000000..f5d2444
--- /dev/null
+++ b/assets/blocks/environment/beryllic-boulder2.png
Binary files differ
diff --git a/assets/blocks/environment/boulder1.png b/assets/blocks/environment/boulder1.png
new file mode 100644
index 0000000..ae54a00
--- /dev/null
+++ b/assets/blocks/environment/boulder1.png
Binary files differ
diff --git a/assets/blocks/environment/boulder2.png b/assets/blocks/environment/boulder2.png
new file mode 100644
index 0000000..6e6e310
--- /dev/null
+++ b/assets/blocks/environment/boulder2.png
Binary files differ
diff --git a/assets/blocks/environment/carbon-boulder1.png b/assets/blocks/environment/carbon-boulder1.png
new file mode 100644
index 0000000..6efee1f
--- /dev/null
+++ b/assets/blocks/environment/carbon-boulder1.png
Binary files differ
diff --git a/assets/blocks/environment/carbon-boulder2.png b/assets/blocks/environment/carbon-boulder2.png
new file mode 100644
index 0000000..f717be1
--- /dev/null
+++ b/assets/blocks/environment/carbon-boulder2.png
Binary files differ
diff --git a/assets/blocks/environment/crystal-blocks1.png b/assets/blocks/environment/crystal-blocks1.png
new file mode 100644
index 0000000..f49567c
--- /dev/null
+++ b/assets/blocks/environment/crystal-blocks1.png
Binary files differ
diff --git a/assets/blocks/environment/crystal-blocks2.png b/assets/blocks/environment/crystal-blocks2.png
new file mode 100644
index 0000000..3a6dea7
--- /dev/null
+++ b/assets/blocks/environment/crystal-blocks2.png
Binary files differ
diff --git a/assets/blocks/environment/crystal-blocks3.png b/assets/blocks/environment/crystal-blocks3.png
new file mode 100644
index 0000000..95b2e66
--- /dev/null
+++ b/assets/blocks/environment/crystal-blocks3.png
Binary files differ
diff --git a/assets/blocks/environment/crystal-cluster1.png b/assets/blocks/environment/crystal-cluster1.png
new file mode 100644
index 0000000..5adf9a9
--- /dev/null
+++ b/assets/blocks/environment/crystal-cluster1.png
Binary files differ
diff --git a/assets/blocks/environment/crystal-cluster2.png b/assets/blocks/environment/crystal-cluster2.png
new file mode 100644
index 0000000..40a0353
--- /dev/null
+++ b/assets/blocks/environment/crystal-cluster2.png
Binary files differ
diff --git a/assets/blocks/environment/crystal-cluster3.png b/assets/blocks/environment/crystal-cluster3.png
new file mode 100644
index 0000000..d9ba7e9
--- /dev/null
+++ b/assets/blocks/environment/crystal-cluster3.png
Binary files differ
diff --git a/assets/blocks/environment/crystal-orbs1.png b/assets/blocks/environment/crystal-orbs1.png
new file mode 100644
index 0000000..21bd356
--- /dev/null
+++ b/assets/blocks/environment/crystal-orbs1.png
Binary files differ
diff --git a/assets/blocks/environment/crystal-orbs2.png b/assets/blocks/environment/crystal-orbs2.png
new file mode 100644
index 0000000..04a98d7
--- /dev/null
+++ b/assets/blocks/environment/crystal-orbs2.png
Binary files differ
diff --git a/assets/blocks/environment/crystal-orbs3.png b/assets/blocks/environment/crystal-orbs3.png
new file mode 100644
index 0000000..bad75c0
--- /dev/null
+++ b/assets/blocks/environment/crystal-orbs3.png
Binary files differ
diff --git a/assets/blocks/environment/crystalline-boulder1.png b/assets/blocks/environment/crystalline-boulder1.png
new file mode 100644
index 0000000..95d583a
--- /dev/null
+++ b/assets/blocks/environment/crystalline-boulder1.png
Binary files differ
diff --git a/assets/blocks/environment/crystalline-boulder2.png b/assets/blocks/environment/crystalline-boulder2.png
new file mode 100644
index 0000000..d94d92b
--- /dev/null
+++ b/assets/blocks/environment/crystalline-boulder2.png
Binary files differ
diff --git a/assets/blocks/environment/dacite-boulder1.png b/assets/blocks/environment/dacite-boulder1.png
new file mode 100644
index 0000000..ffddeef
--- /dev/null
+++ b/assets/blocks/environment/dacite-boulder1.png
Binary files differ
diff --git a/assets/blocks/environment/dacite-boulder2.png b/assets/blocks/environment/dacite-boulder2.png
new file mode 100644
index 0000000..aa25894
--- /dev/null
+++ b/assets/blocks/environment/dacite-boulder2.png
Binary files differ
diff --git a/assets/blocks/environment/ferric-boulder1.png b/assets/blocks/environment/ferric-boulder1.png
new file mode 100644
index 0000000..63685cb
--- /dev/null
+++ b/assets/blocks/environment/ferric-boulder1.png
Binary files differ
diff --git a/assets/blocks/environment/ferric-boulder2.png b/assets/blocks/environment/ferric-boulder2.png
new file mode 100644
index 0000000..93e19a3
--- /dev/null
+++ b/assets/blocks/environment/ferric-boulder2.png
Binary files differ
diff --git a/assets/blocks/environment/glowblob1.png b/assets/blocks/environment/glowblob1.png
deleted file mode 100644
index 404c2bd..0000000
--- a/assets/blocks/environment/glowblob1.png
+++ /dev/null
Binary files differ
diff --git a/assets/blocks/environment/pack.json b/assets/blocks/environment/pack.json
deleted file mode 100644
index 8a0d47c..0000000
--- a/assets/blocks/environment/pack.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- duplicatePadding: true,
- combineSubdirectories: true,
- flattenPaths: true,
- maxWidth: 2048,
- maxHeight: 2048,
- fast: true,
- stripWhitespaceCenter: false
-}
diff --git a/assets/blocks/environment/pur-bush-bot.png b/assets/blocks/environment/pur-bush-bot.png
new file mode 100644
index 0000000..5704eb7
--- /dev/null
+++ b/assets/blocks/environment/pur-bush-bot.png
Binary files differ
diff --git a/assets/blocks/environment/pur-bush.png b/assets/blocks/environment/pur-bush.png
new file mode 100644
index 0000000..98b5faf
--- /dev/null
+++ b/assets/blocks/environment/pur-bush.png
Binary files differ
diff --git a/assets/blocks/environment/red-ice-boulder1.png b/assets/blocks/environment/red-ice-boulder1.png
new file mode 100644
index 0000000..6a37193
--- /dev/null
+++ b/assets/blocks/environment/red-ice-boulder1.png
Binary files differ
diff --git a/assets/blocks/environment/red-ice-boulder2.png b/assets/blocks/environment/red-ice-boulder2.png
new file mode 100644
index 0000000..6491366
--- /dev/null
+++ b/assets/blocks/environment/red-ice-boulder2.png
Binary files differ
diff --git a/assets/blocks/environment/red-ice-boulder3.png b/assets/blocks/environment/red-ice-boulder3.png
new file mode 100644
index 0000000..ebb2cb6
--- /dev/null
+++ b/assets/blocks/environment/red-ice-boulder3.png
Binary files differ
diff --git a/assets/blocks/environment/red-stone-boulder1.png b/assets/blocks/environment/red-stone-boulder1.png
new file mode 100644
index 0000000..27fdd14
--- /dev/null
+++ b/assets/blocks/environment/red-stone-boulder1.png
Binary files differ
diff --git a/assets/blocks/environment/red-stone-boulder2.png b/assets/blocks/environment/red-stone-boulder2.png
new file mode 100644
index 0000000..40d7a7b
--- /dev/null
+++ b/assets/blocks/environment/red-stone-boulder2.png
Binary files differ
diff --git a/assets/blocks/environment/red-stone-boulder3.png b/assets/blocks/environment/red-stone-boulder3.png
new file mode 100644
index 0000000..e85c09e
--- /dev/null
+++ b/assets/blocks/environment/red-stone-boulder3.png
Binary files differ
diff --git a/assets/blocks/environment/red-stone-boulder4.png b/assets/blocks/environment/red-stone-boulder4.png
new file mode 100644
index 0000000..9cf450c
--- /dev/null
+++ b/assets/blocks/environment/red-stone-boulder4.png
Binary files differ
diff --git a/assets/blocks/environment/redweed1.png b/assets/blocks/environment/redweed1.png
new file mode 100644
index 0000000..2953862
--- /dev/null
+++ b/assets/blocks/environment/redweed1.png
Binary files differ
diff --git a/assets/blocks/environment/redweed2.png b/assets/blocks/environment/redweed2.png
new file mode 100644
index 0000000..30243ff
--- /dev/null
+++ b/assets/blocks/environment/redweed2.png
Binary files differ
diff --git a/assets/blocks/environment/redweed3.png b/assets/blocks/environment/redweed3.png
new file mode 100644
index 0000000..a8bec50
--- /dev/null
+++ b/assets/blocks/environment/redweed3.png
Binary files differ
diff --git a/assets/blocks/environment/rhyolite-boulder1.png b/assets/blocks/environment/rhyolite-boulder1.png
new file mode 100644
index 0000000..888aeed
--- /dev/null
+++ b/assets/blocks/environment/rhyolite-boulder1.png
Binary files differ
diff --git a/assets/blocks/environment/rhyolite-boulder2.png b/assets/blocks/environment/rhyolite-boulder2.png
new file mode 100644
index 0000000..7b6f910
--- /dev/null
+++ b/assets/blocks/environment/rhyolite-boulder2.png
Binary files differ
diff --git a/assets/blocks/environment/rhyolite-boulder3.png b/assets/blocks/environment/rhyolite-boulder3.png
new file mode 100644
index 0000000..25059fd
--- /dev/null
+++ b/assets/blocks/environment/rhyolite-boulder3.png
Binary files differ
diff --git a/assets/blocks/environment/sand-boulder1.png b/assets/blocks/environment/sand-boulder1.png
new file mode 100644
index 0000000..4c2cbd9
--- /dev/null
+++ b/assets/blocks/environment/sand-boulder1.png
Binary files differ
diff --git a/assets/blocks/environment/sand-boulder2.png b/assets/blocks/environment/sand-boulder2.png
new file mode 100644
index 0000000..902a0d7
--- /dev/null
+++ b/assets/blocks/environment/sand-boulder2.png
Binary files differ
diff --git a/assets/blocks/environment/shale-boulder1.png b/assets/blocks/environment/shale-boulder1.png
new file mode 100644
index 0000000..6d280cc
--- /dev/null
+++ b/assets/blocks/environment/shale-boulder1.png
Binary files differ
diff --git a/assets/blocks/environment/shale-boulder2.png b/assets/blocks/environment/shale-boulder2.png
new file mode 100644
index 0000000..5ba4033
--- /dev/null
+++ b/assets/blocks/environment/shale-boulder2.png
Binary files differ
diff --git a/assets/blocks/environment/snow-boulder1.png b/assets/blocks/environment/snow-boulder1.png
new file mode 100644
index 0000000..c7feac2
--- /dev/null
+++ b/assets/blocks/environment/snow-boulder1.png
Binary files differ
diff --git a/assets/blocks/environment/snow-boulder2.png b/assets/blocks/environment/snow-boulder2.png
new file mode 100644
index 0000000..5f148d7
--- /dev/null
+++ b/assets/blocks/environment/snow-boulder2.png
Binary files differ
diff --git a/assets/blocks/environment/spore-cluster1.png b/assets/blocks/environment/spore-cluster1.png
new file mode 100644
index 0000000..50475fa
--- /dev/null
+++ b/assets/blocks/environment/spore-cluster1.png
Binary files differ
diff --git a/assets/blocks/environment/spore-cluster2.png b/assets/blocks/environment/spore-cluster2.png
new file mode 100644
index 0000000..4d19d2d
--- /dev/null
+++ b/assets/blocks/environment/spore-cluster2.png
Binary files differ
diff --git a/assets/blocks/environment/spore-cluster3.png b/assets/blocks/environment/spore-cluster3.png
new file mode 100644
index 0000000..3802836
--- /dev/null
+++ b/assets/blocks/environment/spore-cluster3.png
Binary files differ
diff --git a/assets/blocks/environment/tendrils1.png b/assets/blocks/environment/tendrils1.png
deleted file mode 100644
index 90345f2..0000000
--- a/assets/blocks/environment/tendrils1.png
+++ /dev/null
Binary files differ
diff --git a/assets/blocks/environment/tendrils2.png b/assets/blocks/environment/tendrils2.png
deleted file mode 100644
index b234cc4..0000000
--- a/assets/blocks/environment/tendrils2.png
+++ /dev/null
Binary files differ
diff --git a/assets/blocks/environment/tendrils3.png b/assets/blocks/environment/tendrils3.png
deleted file mode 100644
index e998b41..0000000
--- a/assets/blocks/environment/tendrils3.png
+++ /dev/null
Binary files differ
diff --git a/assets/blocks/environment/vibrant-crystal-cluster1.png b/assets/blocks/environment/vibrant-crystal-cluster1.png
new file mode 100644
index 0000000..33cad73
--- /dev/null
+++ b/assets/blocks/environment/vibrant-crystal-cluster1.png
Binary files differ
diff --git a/assets/blocks/environment/vibrant-crystal-cluster2.png b/assets/blocks/environment/vibrant-crystal-cluster2.png
new file mode 100644
index 0000000..441acd6
--- /dev/null
+++ b/assets/blocks/environment/vibrant-crystal-cluster2.png
Binary files differ
diff --git a/assets/blocks/environment/vibrant-crystal-cluster3.png b/assets/blocks/environment/vibrant-crystal-cluster3.png
new file mode 100644
index 0000000..c259fa9
--- /dev/null
+++ b/assets/blocks/environment/vibrant-crystal-cluster3.png
Binary files differ
diff --git a/assets/blocks/environment/white-tree-dead.png b/assets/blocks/environment/white-tree-dead.png
new file mode 100644
index 0000000..cfb6d65
--- /dev/null
+++ b/assets/blocks/environment/white-tree-dead.png
Binary files differ
diff --git a/assets/blocks/environment/white-tree.png b/assets/blocks/environment/white-tree.png
new file mode 100644
index 0000000..91f17de
--- /dev/null
+++ b/assets/blocks/environment/white-tree.png
Binary files differ
diff --git a/assets/blocks/environment/yellow-stone-boulder1.png b/assets/blocks/environment/yellow-stone-boulder1.png
new file mode 100644
index 0000000..3f7bc89
--- /dev/null
+++ b/assets/blocks/environment/yellow-stone-boulder1.png
Binary files differ
diff --git a/assets/blocks/environment/yellow-stone-boulder2.png b/assets/blocks/environment/yellow-stone-boulder2.png
new file mode 100644
index 0000000..c075836
--- /dev/null
+++ b/assets/blocks/environment/yellow-stone-boulder2.png
Binary files differ
diff --git a/assets/blocks/environment/yellowcoral-center.png b/assets/blocks/environment/yellowcoral-center.png
deleted file mode 100644
index 7ca539b..0000000
--- a/assets/blocks/environment/yellowcoral-center.png
+++ /dev/null
Binary files differ
diff --git a/assets/blocks/environment/yellowcoral.png b/assets/blocks/environment/yellowcoral.png
deleted file mode 100644
index efbd52a..0000000
--- a/assets/blocks/environment/yellowcoral.png
+++ /dev/null
Binary files differ
diff --git a/assets/blocks/production/cultivator-middle.png b/assets/blocks/production/cultivator-middle.png
index f74af42..6c1b20f 100644
--- a/assets/blocks/production/cultivator-middle.png
+++ b/assets/blocks/production/cultivator-middle.png
Binary files differ
diff --git a/src/block/content.rs b/src/block/content.rs
index d07eb05..55d5545 100644
--- a/src/block/content.rs
+++ b/src/block/content.rs
@@ -1,5 +1,5 @@
//! everything
-use crate::content::content_enum;
+use crate::content::{content_enum, Content};
content_enum! {
pub enum Type / Block for u16 | TryFromU16Error
@@ -418,3 +418,9 @@ content_enum! {
"world-message",
}
}
+use crate::block::*;
+impl Type {
+ pub fn to<'l>(&self, reg: &'l BlockRegistry) -> Option<&'l Block> {
+ reg.get(self.get_name())
+ }
+}
diff --git a/src/block/distribution.rs b/src/block/distribution.rs
index c68a851..69db26b 100644
--- a/src/block/distribution.rs
+++ b/src/block/distribution.rs
@@ -2,23 +2,39 @@
use std::error::Error;
use std::fmt;
-use image::RgbaImage;
-
-use crate::block::make_register;
-use crate::block::simple::{cost, make_simple, state_impl};
+use crate::block::simple::*;
+use crate::block::*;
use crate::content;
use crate::data::dynamic::DynType;
-use crate::data::renderer::load;
+use crate::data::renderer::{load, ImageHolder};
use crate::item;
+use crate::utils::ImageUtils;
make_simple!(ConveyorBlock);
+make_simple!(
+ JunctionBlock,
+ |_, _, _, _| None,
+ |_, _, _, _, _, buff: &mut crate::data::DataRead| {
+ // format:
+ // - iterate 4
+ // - u8
+ // - iterate u8
+ // - i64
+ for _ in 0..4 {
+ let _ = buff.read_u8()?;
+ let n = buff.read_u8()? as usize;
+ buff.skip(n * 8)?;
+ }
+ Ok(())
+ }
+);
make_register! {
"conveyor" => ConveyorBlock::new(1, false, cost!(Copper: 1));
"titanium-conveyor" => ConveyorBlock::new(1, false, cost!(Copper: 1, Lead: 1, Titanium: 1));
"plastanium-conveyor" => ConveyorBlock::new(1, false, cost!(Graphite: 1, Silicon: 1, Plastanium: 1));
"armored-conveyor" => ConveyorBlock::new(1, false, cost!(Metaglass: 1, Thorium: 1, Plastanium: 1));
- "junction" => ConveyorBlock::new(1, true, cost!(Copper: 2));
+ "junction" => JunctionBlock::new(1, true, cost!(Copper: 2));
"bridge-conveyor" => BridgeBlock::new(1, false, cost!(Copper: 6, Lead: 6), 4, true);
"phase-conveyor" => BridgeBlock::new(1, false, cost!(Lead: 10, Graphite: 10, Silicon: 7, PhaseFabric: 5), 12, true);
"sorter" => ItemBlock::new(1, true, cost!(Copper: 2, Lead: 2));
@@ -106,28 +122,27 @@ impl BlockLogic for ItemBlock {
}
}
- fn draw(&self, category: &str, name: &str, state: Option<&State>) -> Option<RgbaImage> {
+ fn draw(&self, category: &str, name: &str, state: Option<&State>) -> Option<ImageHolder> {
if !matches!(
name,
"unloader" | "item-source" | "sorter" | "inverted-sorter"
) {
return None;
}
- let mut p = load(category, name).unwrap();
+ let mut p = load(category, name).unwrap().clone();
if let Some(state) = state {
if let Some(s) = Self::get_state(state) {
- let mut top = load(category, "center").unwrap();
- crate::utils::image::tint(&mut top, s.color());
- image::imageops::overlay(&mut p, &top, 0, 0);
- return Some(p);
+ let mut top = load(category, "center").unwrap().clone();
+ p.overlay(top.tint(s.color()), 0, 0);
+ return Some(ImageHolder::from(p));
}
}
if name == "unloader" {
- return Some(p);
+ return Some(ImageHolder::from(p));
}
- let mut null = load("distribution", "cross-full").unwrap();
- image::imageops::overlay(&mut null, &p, 0, 0);
- Some(null)
+ let mut null = load("distribution", "cross-full").unwrap().clone();
+ null.overlay(&p, 0, 0);
+ Some(ImageHolder::from(null))
}
}
@@ -294,6 +309,29 @@ impl BlockLogic for BridgeBlock {
Some((dx, dy)) => Ok(DynData::Point2(*dx, *dy)),
}
}
+
+ /// format:
+ /// - out: `i32`
+ /// - warmup: `f32`
+ /// - iterate `links<u8>`
+ /// - in+: `i32`
+ /// - moved: `bool`
+ fn read(
+ &self,
+ _: &str,
+ _: &str,
+ _: &super::BlockRegistry,
+ _: &crate::data::map::EntityMapping,
+ buff: &mut crate::data::DataRead,
+ ) -> Result<(), crate::data::ReadError> {
+ buff.read_i32()?;
+ buff.read_f32()?;
+ for _ in 0..buff.read_u8()? {
+ buff.read_i32()?;
+ }
+ buff.read_bool()?;
+ Ok(())
+ }
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
diff --git a/src/block/drills.rs b/src/block/drills.rs
index eb50b87..67d72f4 100644
--- a/src/block/drills.rs
+++ b/src/block/drills.rs
@@ -1,13 +1,14 @@
//! extraction of raw resources (mine part)
use crate::block::make_register;
use crate::block::simple::{cost, make_simple};
-use crate::data::renderer::read_with;
-make_simple!(DrillBlock, |_, _, name, _| {
- if name == "cliff-crusher" {
+use crate::data::renderer::*;
+
+make_simple!(DrillBlock, |me: &DrillBlock, _, name, _| {
+ if matches!(name, "cliff-crusher" | "large-plasma-bore" | "plasma-bore") {
const SFX: &[&str; 3] = &["", "-top", "-rotator"];
- return Some(read_with("drills", "cliff-crusher", SFX, 2u16));
+ return Some(ImageHolder::Own(read_with("drills", name, SFX, me.size)));
}
- None
+ Some(ImageHolder::Borrow(load("drills", name).unwrap()))
});
make_register! {
@@ -16,7 +17,6 @@ make_register! {
"laser-drill" => DrillBlock::new(3, true, cost!(Copper: 35, Graphite: 30, Titanium: 20, Silicon: 30));
"blast-drill" => DrillBlock::new(4, true, cost!(Copper: 65, Titanium: 50, Thorium: 75, Silicon: 60));
"water-extractor" => DrillBlock::new(2, true, cost!(Copper: 30, Lead: 30, Metaglass: 30, Graphite: 30));
- "cultivator" => DrillBlock::new(2, true, cost!(Copper: 25, Lead: 25, Silicon: 10));
"oil-extractor" => DrillBlock::new(3, true, cost!(Copper: 150, Lead: 115, Graphite: 175, Thorium: 115, Silicon: 75));
"vent-condenser" => DrillBlock::new(3, true, cost!(Graphite: 20, Beryllium: 60));
"cliff-crusher" => DrillBlock::new(2, false, cost!(Beryllium: 100, Graphite: 40));
diff --git a/src/block/environment.rs b/src/block/environment.rs
new file mode 100644
index 0000000..9eee40c
--- /dev/null
+++ b/src/block/environment.rs
@@ -0,0 +1,184 @@
+//! grass
+use crate::block::make_register;
+use crate::block::simple::make_simple;
+use crate::data::renderer::*;
+use tinyrand::{Rand, RandRange, Seeded, StdRand};
+use tinyrand_std::clock_seed::ClockSeed;
+
+macro_rules! register_env {
+ ($($field:literal: $size:literal @ $variations:literal;)+) => {
+ make_register!(
+ $($field => EnvironmentBlock::new($size, true, &[]);)*
+ );
+
+ make_simple!(EnvironmentBlock, |_, _, name, _| {
+ let mut rand = StdRand::seed(ClockSeed::default().next_u64());
+ match name {
+ $($field => {
+ #[allow(clippy::reversed_empty_ranges)]
+ if $variations == 1 { Some(ImageHolder::Borrow(load("environment", $field).unwrap())) }
+ else if $variations == 0 { return None }
+ else { Some(ImageHolder::Borrow(load("environment", &format!("{}{}", $field, rand.next_range(1usize..$variations))).unwrap())) }
+ },)*
+ _ => { unreachable!() }
+ }
+ });
+ };
+}
+
+register_env! {
+ "build1": 1@0;
+ "build2": 1@0;
+ "build3": 1@0;
+ "arkycite-floor": 1@1;
+ "arkyic-stone": 1@3;
+ "arkyic-vent": 3@2;
+ "arkyic-wall-large": 2@1;
+ "arkyic-wall": 1@3;
+ "basalt": 1@3;
+ "beryllic-stone-wall-large": 2@1;
+ "beryllic-stone-wall": 1@2;
+ "beryllic-stone": 1@4;
+ "bluemat": 1@3;
+ "carbon-stone": 1@4;
+ "carbon-vent": 3@2;
+ "carbon-wall-large": 2@1;
+ "carbon-wall": 1@2;
+ "char": 1@3;
+ "cliff": 1@7;
+ "core-zone": 1@1;
+ "crater-stone": 1@6;
+ "crystal-floor": 1@4;
+ "crystalline-stone-wall-large": 2@1;
+ "crystalline-stone-wall": 1@4;
+ "crystalline-stone": 1@5;
+ "crystalline-vent": 3@2;
+ "dacite-wall-large": 2@1;
+ "dacite-wall": 1@2;
+ "dacite": 1@3;
+ "dark-metal-large": 2@1;
+ "dark-metal": 1@2;
+ "dark-panel-1": 1@1;
+ "dark-panel-2": 1@1;
+ "dark-panel-3": 1@1;
+ "dark-panel-4": 1@1;
+ "dark-panel-5": 1@1;
+ "dark-panel-6": 1@1;
+ "metal-floor": 1@1;
+ "metal-floor-2": 1@1;
+ "metal-floor-3": 1@1;
+ "metal-floor-4": 1@1;
+ "metal-floor-5": 1@1;
+ "metal-floor-damaged": 1@3;
+ "darksand-tainted-water": 1@1;
+ "darksand-water": 1@1;
+ "darksand": 1@3;
+ "deep-tainted-water": 1@1;
+ "deep-water": 1@1;
+ "dense-red-stone": 1@4;
+ "dirt-wall-large": 2@1;
+ "dirt-wall": 1@2;
+ "dirt": 1@3;
+ "dune-wall-large": 2@1;
+ "dune-wall": 1@2;
+ "ferric-craters": 1@3; // ferris section
+ "ferric-stone-wall-large": 2@1;
+ "ferric-stone-wall": 1@2;
+ "ferric-stone": 1@4;
+ "graphite-wall-large": 2@1;
+ "graphite-wall": 1@3;
+ "grass": 1@3;
+ "hotrock": 1@3;
+ "ice-snow": 1@3;
+ "ice-wall-large": 2@1;
+ "ice-wall": 1@2;
+ "ice": 1@3;
+ "magmarock": 1@3;
+ "molten-slag": 1@1;
+ "moss": 1@3;
+ "mud": 1@3;
+ "ore-beryllium": 1@3;
+ "ore-coal": 1@3;
+ "ore-copper": 1@3;
+ "ore-crystal-thorium": 1@3;
+ "ore-lead": 1@3;
+ "ore-scrap": 1@3;
+ "ore-thorium": 1@3;
+ "ore-titanium": 1@3;
+ "ore-tungsten": 1@3;
+ "ore-wall-beryllium": 1@3;
+ "ore-wall-thorium": 1@3;
+ "ore-wall-tungsten": 1@3;
+ "pebbles": 1@3;
+ "pine": 1@1;
+ "pooled-cryofluid": 1@1;
+ "red-diamond-wall": 1@3;
+ "red-ice-wall-large": 2@1;
+ "red-ice": 1@3;
+ "red-stone-vent": 3@2;
+ "red-stone-wall-large": 2@1;
+ "red-stone-wall": 1@3;
+ "red-stone": 1@4;
+ "redmat": 1@3;
+ "regolith-wall-large": 2@1;
+ "regolith": 1@3;
+ "rhyolite-crater": 1@3;
+ "rhyolite-vent": 3@2;
+ "rhyolite-wall-large": 2@1;
+ "rhyolite-wall": 1@2;
+ "rhyolite": 1@3;
+ "rough-rhyolite": 1@3;
+ "salt-wall-large": 2@1;
+ "salt-wall": 1@2;
+ "salt": 1@1;
+ "sand-floor": 1@3;
+ "sand-wall-large": 2@1;
+ "sand-wall": 1@2;
+ "sand-water": 1@1;
+ "shale-wall-large": 2@1;
+ "shale-wall": 1@2;
+ "shale": 1@3;
+ "shallow-water": 1@1;
+ "shrubs-large": 2@1;
+ "shrubs": 1@2;
+ "snow-pine": 1@1;
+ "snow-wall-large": 2@1;
+ "snow-wall": 1@2;
+ "snow": 1@3;
+ "space": 1@1;
+ "spawn": 1@1;
+ "spore-moss": 1@3;
+ "spore-pine": 1@1;
+ "spore-wall-large": 2@1;
+ "spore-wall": 1@2;
+ "stone-wall-large": 2@1;
+ "stone-wall": 1@2;
+ "stone": 1@3;
+ "tainted-water": 1@1;
+ "tar": 1@1;
+ "yellow-stone-plates": 1@3;
+ "yellow-stone-vent": 3@2;
+ "yellow-stone-wall-large": 2@1;
+ "yellow-stone-wall": 1@2;
+ "yellow-stone": 1@3;
+ // props
+ "snow-boulder": 1@2;
+ "shale-boulder": 1@2;
+ "arkyic-boulder": 1@3;
+ "basalt-boulder": 1@2;
+ "beryllic-boulder": 1@2;
+ "boulder": 1@2;
+ "carbon-boulder": 1@2;
+ "crystalline-boulder": 1@2;
+ "dacite-boulder": 1@2;
+ "ferric-boulder": 1@2;
+ "red-ice-boulder": 1@3;
+ "red-stone-boulder": 1@4;
+ "rhyolite-boulder": 1@3;
+ "sand-boulder": 1@2;
+ "yellow-sand-boulder": 1@2;
+ // these are tall but uh
+ "crystal-blocks": 1@3;
+ "crytal-cluster": 1@3;
+ "crystal-orbs": 1@3;
+}
diff --git a/src/block/liquid.rs b/src/block/liquid.rs
index f007747..888bd0d 100644
--- a/src/block/liquid.rs
+++ b/src/block/liquid.rs
@@ -3,12 +3,13 @@ use std::error::Error;
use std::fmt;
use crate::block::distribution::BridgeBlock;
-use crate::block::make_register;
-use crate::block::simple::{cost, make_simple, state_impl};
+use crate::block::simple::*;
+use crate::block::*;
use crate::content;
use crate::data::dynamic::DynType;
use crate::data::renderer::load;
use crate::fluid;
+use crate::utils::ImageUtils;
make_simple!(LiquidBlock);
@@ -101,19 +102,18 @@ impl BlockLogic for FluidBlock {
}
}
- fn draw(&self, category: &str, name: &str, state: Option<&State>) -> Option<image::RgbaImage> {
- let mut p = load(category, name).unwrap();
+ fn draw(&self, category: &str, name: &str, state: Option<&State>) -> Option<ImageHolder> {
+ let mut p = load(category, name).unwrap().clone();
if let Some(state) = state {
if let Some(s) = Self::get_state(state) {
- let mut top = load("distribution", "center").unwrap();
- crate::utils::image::tint(&mut top, s.color());
- image::imageops::overlay(&mut p, &top, 0, 0);
- return Some(p);
+ let mut top = load("distribution", "center").unwrap().clone();
+ p.overlay(top.tint(s.color()), 0, 0);
+ return Some(ImageHolder::Own(p));
}
}
- let mut null = load("distribution", "cross-full").unwrap();
- image::imageops::overlay(&mut null, &p, 0, 0);
- Some(null)
+ let mut null = load("distribution", "cross-full").unwrap().clone();
+ null.overlay(&p, 0, 0);
+ Some(ImageHolder::Own(null))
}
}
diff --git a/src/block/logic.rs b/src/block/logic.rs
index 76aa440..24c7b2c 100644
--- a/src/block/logic.rs
+++ b/src/block/logic.rs
@@ -9,15 +9,14 @@ use flate2::{
FlushDecompress, Status,
};
-use crate::block::make_register;
-use crate::block::simple::{cost, make_simple, state_impl};
+use crate::block::simple::*;
+use crate::block::*;
use crate::data::dynamic::DynType;
use crate::data::{self, DataRead, DataWrite};
make_simple!(LogicBlock);
make_register! {
- // todo reinforced proc
"reinforced-message" => MessageLogic::new(1, true, cost!(Graphite: 10, Beryllium: 5));
"message" => MessageLogic::new(1, true, cost!(Copper: 5, Graphite: 5));
"switch" => SwitchLogic::new(1, true, cost!(Copper: 5, Graphite: 5));
@@ -28,6 +27,11 @@ make_register! {
"memory-bank" => LogicBlock::new(2, true, cost!(Copper: 30, Graphite: 80, Silicon: 80, PhaseFabric: 30));
"logic-display" => LogicBlock::new(3, true, cost!(Lead: 100, Metaglass: 50, Silicon: 50));
"large-logic-display" => LogicBlock::new(6, true, cost!(Lead: 200, Metaglass: 100, Silicon: 150, PhaseFabric: 75));
+ // todo canvas (cost!(Silicon: 30, Beryllium: 10))
+ // editor only
+ "world-processor" => LogicBlock::new(1, true, &[]);
+ "world-message" => MessageLogic::new(1, true, &[]);
+ "world-cell" => LogicBlock::new(1, true, &[]);
}
pub struct MessageLogic {
diff --git a/src/block/mod.rs b/src/block/mod.rs
index ca20821..642518b 100644
--- a/src/block/mod.rs
+++ b/src/block/mod.rs
@@ -1,8 +1,8 @@
//! deal with blocks.
//!
-//! categorized as mindustry categorizes them in its assets folder, for easy drawing
-//! with the exception of sandbox.
-use image::RgbaImage;
+//! categorized as mindustry categorizes them in its assets folder, for easy drawing.
+//!
+//! with the exception of sandbox, that is.
use std::any::Any;
use std::borrow::Cow;
use std::error::Error;
@@ -10,8 +10,10 @@ use std::fmt;
use crate::access::BoxAccess;
use crate::data::dynamic::{DynData, DynType};
-use crate::data::GridPos;
-use crate::item::storage::Storage as ItemStorage;
+use crate::data::map::EntityMapping;
+use crate::data::renderer::ImageHolder;
+use crate::data::{DataRead, GridPos, ReadError as DataReadError};
+use crate::item::storage::ItemStorage;
use crate::registry::RegistryEntry;
pub mod campaign;
@@ -19,6 +21,7 @@ pub mod content;
pub mod defense;
pub mod distribution;
pub mod drills;
+pub mod environment;
pub mod liquid;
pub mod logic;
pub mod payload;
@@ -50,9 +53,21 @@ pub trait BlockLogic {
fn serialize_state(&self, state: &State) -> Result<DynData, SerializeError>;
- fn draw(&self, _category: &str, _name: &str, _state: Option<&State>) -> Option<RgbaImage> {
+ fn draw(&self, _category: &str, _name: &str, _state: Option<&State>) -> Option<ImageHolder> {
None
}
+ // TODO: use data
+ #[allow(unused_variables)]
+ fn read(
+ &self,
+ category: &str,
+ name: &str,
+ reg: &BlockRegistry,
+ mapping: &EntityMapping,
+ buff: &mut DataRead,
+ ) -> Result<(), DataReadError> {
+ Ok(())
+ }
}
// i wish i could derive
@@ -66,7 +81,7 @@ macro_rules! impl_block {
self.symmetric
}
- fn create_build_cost(&self) -> Option<$crate::item::storage::Storage> {
+ fn create_build_cost(&self) -> Option<$crate::item::storage::ItemStorage> {
if self.build_cost.is_empty() {
None
} else {
@@ -213,16 +228,16 @@ impl Block {
}
/// draw this block, with this state
- pub fn image(&self, state: Option<&State>) -> RgbaImage {
- if let Some(p) = self
- .logic
- .as_ref()
- .draw(&self.category, &self.name, state)
- {
+ pub fn image(&self, state: Option<&State>) -> ImageHolder {
+ if let Some(p) = self.logic.as_ref().draw(&self.category, &self.name, state) {
return p;
}
use crate::data::renderer::read;
- read(&self.category, &self.name, self.get_size())
+ ImageHolder::Own(read(&self.category, &self.name, self.get_size()))
+ }
+
+ pub fn has_building(&self) -> bool {
+ &self.category != "environment"
}
/// size.
@@ -270,6 +285,17 @@ impl Block {
pub(crate) fn serialize_state(&self, state: &State) -> Result<DynData, SerializeError> {
self.logic.serialize_state(state)
}
+
+ #[doc(hidden)]
+ pub fn read(
+ &self,
+ buff: &mut DataRead,
+ reg: &BlockRegistry,
+ mapping: &EntityMapping,
+ ) -> Result<(), DataReadError> {
+ self.logic
+ .read(&self.category, &self.name, reg, mapping, buff)
+ }
}
impl fmt::Debug for Block {
@@ -455,4 +481,5 @@ fn register(reg: &mut BlockRegistry<'_>) {
campaign::register(reg);
logic::register(reg);
walls::register(reg);
+ environment::register(reg);
}
diff --git a/src/block/payload.rs b/src/block/payload.rs
index 1cf52fe..c743934 100644
--- a/src/block/payload.rs
+++ b/src/block/payload.rs
@@ -2,12 +2,17 @@
use std::error::Error;
use std::fmt;
-use crate::block::simple::{cost, make_simple, state_impl};
-use crate::block::{self, distribution::BridgeBlock, make_register};
-use crate::content;
+use crate::block::content::Type as BlockEnum;
+use crate::block::distribution::BridgeBlock;
+use crate::block::simple::*;
+use crate::block::{self, *};
+use crate::content::{self, Content};
use crate::data::dynamic::DynType;
+use crate::data::ReadError;
use crate::unit;
+use super::BlockRegistry;
+
make_simple!(ConstructorBlock);
const GROUND_UNITS: &[unit::Type] = &[unit::Type::Dagger, unit::Type::Crawler, unit::Type::Nova];
@@ -240,6 +245,50 @@ impl BlockLogic for PayloadBlock {
Payload::Unit(unit) => Ok(DynData::Content(content::Type::Unit, (*unit).into())),
}
}
+
+ /// format:
+ /// - exists: `bool`
+ /// - if !exists: ok
+ /// - type: `u8`
+ /// - if type == 1 (payload block):
+ /// - block: `u16`
+ /// - version: `u8`
+ /// - [`crate::block::Block::read`] (recursion :ferrisHmm:),
+ /// - if type == 2 (paylood unit):
+ /// - id: `u8`
+ /// - unit read???????? TODO
+ fn read(
+ &self,
+ _: &str,
+ _: &str,
+ reg: &BlockRegistry,
+ entity_mapping: &crate::data::map::EntityMapping,
+ buff: &mut crate::data::DataRead,
+ ) -> Result<(), crate::data::ReadError> {
+ if !buff.read_bool()? {
+ return Ok(());
+ }
+ let t = buff.read_u8()?;
+ const BLOCK: u8 = 1;
+ const UNIT: u8 = 0;
+ match t {
+ BLOCK => {
+ let b = buff.read_u16()?;
+ let b = BlockEnum::try_from(b).unwrap_or(BlockEnum::Router);
+ let b = reg.get(b.get_name()).unwrap();
+ b.read(buff, reg, entity_mapping)?;
+ }
+ UNIT => {
+ let u = buff.read_u8()?;
+ let Some(_u) = entity_mapping.get(&u) else {
+ return Err(ReadError::Expected("map entry"));
+ };
+ // unit::Type::try_from(u).unwrap_or(unit::Type::Alpha).read(todo!());
+ }
+ _ => return Err(ReadError::Expected("0 | 1")),
+ }
+ Ok(())
+ }
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
diff --git a/src/block/power.rs b/src/block/power.rs
index 2df3e14..f5048c6 100644
--- a/src/block/power.rs
+++ b/src/block/power.rs
@@ -2,8 +2,8 @@
use std::error::Error;
use std::fmt;
-use crate::block::make_register;
-use crate::block::simple::{cost, make_simple, state_impl};
+use crate::block::simple::*;
+use crate::block::*;
use crate::data::dynamic::DynType;
make_simple!(GeneratorBlock);
diff --git a/src/block/production.rs b/src/block/production.rs
index 7484ab0..7d8e7df 100644
--- a/src/block/production.rs
+++ b/src/block/production.rs
@@ -3,6 +3,7 @@ use crate::block::make_register;
use crate::block::simple::{cost, make_simple};
make_register! {
+ "cultivator" => ProductionBlock::new(2, true, cost!(Copper: 25, Lead: 25, Silicon: 10));
"graphite-press" => ProductionBlock::new(2, true, cost!(Copper: 75, Lead: 30));
"multi-press" => ProductionBlock::new(3, true, cost!(Lead: 100, Graphite: 50, Titanium: 100, Silicon: 25));
"silicon-smelter" => ProductionBlock::new(2, true, cost!(Copper: 30, Lead: 25));
@@ -24,9 +25,9 @@ make_register! {
"silicon-arc-furnace" => ProductionBlock::new(3, true, cost!(Beryllium: 70, Graphite: 80));
"electrolyzer" => ProductionBlock::new(3, true, cost!(Silicon: 50, Graphite: 40, Beryllium: 130, Tungsten: 80));
"atmospheric-concentrator" => ProductionBlock::new(3, true, cost!(Oxide: 60, Beryllium: 180, Silicon: 150));
- "oxidation-chamber" => ProductionBlock::new(3, true, cost!(Tungsten: 120, Graphite: 80, Silicon: 100, Beryllium: 120));
- "electric-heater" => ProductionBlock::new(2, false, cost!(Tungsten: 30, Oxide: 30));
- "slag-heater" => ProductionBlock::new(3, false, cost!(Tungsten: 50, Oxide: 20, Beryllium: 20));
+ "oxidation-chamber" => HeatCrafter::new(3, true, cost!(Tungsten: 120, Graphite: 80, Silicon: 100, Beryllium: 120));
+ "electric-heater" => HeatCrafter::new(2, false, cost!(Tungsten: 30, Oxide: 30));
+ "slag-heater" => HeatCrafter::new(3, false, cost!(Tungsten: 50, Oxide: 20, Beryllium: 20));
"phase-heater" => ProductionBlock::new(2, false, cost!(Oxide: 30, Carbide: 30, Beryllium: 30));
"heat-redirector" => ProductionBlock::new(3, false, cost!(Tungsten: 10, Graphite: 10));
"heat-router" => ProductionBlock::new(3, false, cost!(Tungsten: 15, Graphite: 10));
@@ -41,4 +42,30 @@ make_register! {
"heat-source" => ProductionBlock::new(1, false, &[]);
}
-make_simple!(ProductionBlock);
+make_simple!(
+ ProductionBlock,
+ |_, _, _, _| None,
+ |_, _, _, _, _, buff: &mut crate::data::DataRead| {
+ // format:
+ // - progress: `f32`
+ // - warmup: `f32`
+ buff.read_f32()?;
+ buff.read_f32()?;
+ Ok(())
+ }
+);
+
+make_simple!(
+ HeatCrafter,
+ |_, _, _, _| None,
+ |_, _, _, _, _, buff: &mut crate::data::DataRead| {
+ // format:
+ // - progress: `f32`
+ // - warmup: `f32`
+ // - heat: f32
+ buff.read_f32()?;
+ buff.read_f32()?;
+ buff.read_f32()?;
+ Ok(())
+ }
+);
diff --git a/src/block/simple.rs b/src/block/simple.rs
index c78eb0e..2dca7fd 100644
--- a/src/block/simple.rs
+++ b/src/block/simple.rs
@@ -23,15 +23,19 @@ macro_rules! state_impl {
pub(crate) use state_impl;
macro_rules! make_simple {
- ($name: ident, $draw: expr) => {
+ ($name: ident, $draw: expr, $read: expr) => {
pub struct $name {
size: u8,
symmetric: bool,
- build_cost: BuildCost,
+ build_cost: crate::block::simple::BuildCost,
}
impl $name {
#[must_use]
- pub const fn new(size: u8, symmetric: bool, build_cost: BuildCost) -> Self {
+ pub const fn new(
+ size: u8,
+ symmetric: bool,
+ build_cost: crate::block::simple::BuildCost,
+ ) -> Self {
assert!(size != 0, "invalid size");
Self {
size,
@@ -41,51 +45,69 @@ macro_rules! make_simple {
}
}
- use crate::block::{
- impl_block, simple::BuildCost, BlockLogic, DataConvertError, DeserializeError,
- SerializeError, State,
- };
- use crate::data::dynamic::DynData;
- use crate::data::GridPos;
- impl BlockLogic for $name {
- impl_block!();
-
- fn data_from_i32(&self, _: i32, _: GridPos) -> Result<DynData, DataConvertError> {
- Ok(DynData::Empty)
+ impl crate::block::BlockLogic for $name {
+ crate::block::impl_block!();
+
+ fn data_from_i32(
+ &self,
+ _: i32,
+ _: crate::data::GridPos,
+ ) -> Result<crate::DynData, crate::block::DataConvertError> {
+ Ok(crate::DynData::Empty)
}
- fn deserialize_state(&self, _: DynData) -> Result<Option<State>, DeserializeError> {
+ fn deserialize_state(
+ &self,
+ _: crate::DynData,
+ ) -> Result<Option<crate::block::State>, crate::block::DeserializeError> {
Ok(None)
}
- fn clone_state(&self, _: &State) -> State {
+ fn clone_state(&self, _: &crate::block::State) -> crate::block::State {
panic!("{} has no custom state", stringify!($name))
}
- fn mirror_state(&self, _: &mut State, _: bool, _: bool) {
+ fn mirror_state(&self, _: &mut crate::block::State, _: bool, _: bool) {
panic!("{} has no custom state", stringify!($name));
}
- fn rotate_state(&self, _: &mut State, _: bool) {
+ fn rotate_state(&self, _: &mut crate::block::State, _: bool) {
panic!("{} has no custom state", stringify!($name));
}
- fn serialize_state(&self, _: &State) -> Result<DynData, SerializeError> {
- Ok(DynData::Empty)
+ fn serialize_state(
+ &self,
+ _: &crate::block::State,
+ ) -> Result<crate::DynData, crate::block::SerializeError> {
+ Ok(crate::DynData::Empty)
}
fn draw(
&self,
category: &str,
name: &str,
- state: Option<&State>,
- ) -> Option<image::RgbaImage> {
+ state: Option<&crate::block::State>,
+ ) -> Option<crate::data::renderer::ImageHolder> {
$draw(self, category, name, state)
}
+
+ fn read(
+ &self,
+ category: &str,
+ name: &str,
+ reg: &crate::block::BlockRegistry,
+ entity_mapping: &crate::data::map::EntityMapping,
+ buff: &mut crate::data::DataRead,
+ ) -> Result<(), crate::data::ReadError> {
+ $read(self, category, name, reg, entity_mapping, buff)
+ }
}
};
+ ($name: ident, $draw: expr) => {
+ crate::block::simple::make_simple!($name, $draw, |_, _, _, _, _, _| Ok(()));
+ };
($name: ident) => {
- crate::block::simple::make_simple!($name, |_, _, _, _| { None });
+ crate::block::simple::make_simple!($name, |_, _, _, _| None, |_, _, _, _, _, _| { Ok(()) });
};
}
pub(crate) use make_simple;
diff --git a/src/block/turrets.rs b/src/block/turrets.rs
index 5aeaeaa..160c991 100644
--- a/src/block/turrets.rs
+++ b/src/block/turrets.rs
@@ -1,4 +1,4 @@
-//! idk why its not in the [crate::block::defense] module
+//! idk why its not in the [`crate::block::defense`] module
use crate::block::make_register;
use crate::block::simple::cost;
@@ -32,15 +32,15 @@ make_register! {
"malign" => TurretBlock::new(5, true, cost!(Carbide: 400, Beryllium: 2000, Silicon: 800, Graphite: 800, PhaseFabric: 300));
}
-use crate::data::renderer::load;
+use crate::data::renderer::*;
+use crate::utils::ImageUtils;
crate::block::simple::make_simple!(TurretBlock, |me: &Self, _, name, _| {
let path = match name {
"breach" | "diffuse" | "sublimate" | "titan" | "disperse" | "afflict" | "lustre"
| "scathe" | "malign" => format!("bases/reinforced-block-{}", me.size),
_ => format!("bases/block-{}", me.size),
};
- let mut base = load("turrets", &path).unwrap();
- let top = load("turrets", name).unwrap();
- image::imageops::overlay(&mut base, &top, 0, 0);
- Some(base)
+ let mut base = load("turrets", &path).unwrap().value().clone();
+ base.overlay(load("turrets", name).unwrap().value(), 0, 0);
+ Some(ImageHolder::from(base))
});
diff --git a/src/block/walls.rs b/src/block/walls.rs
index 1b6f25a..d746cdf 100644
--- a/src/block/walls.rs
+++ b/src/block/walls.rs
@@ -1,15 +1,17 @@
//! walls
-use crate::block::make_register;
-use crate::block::simple::{cost, make_simple, state_impl};
+use crate::block::simple::*;
+use crate::block::*;
use crate::data::dynamic::DynType;
-use crate::data::renderer::{load, read_with, TOP};
+use crate::data::renderer::{load, read_with, ImageHolder, TOP};
make_simple!(WallBlock, |_, _, name, _| {
if name == "thruster" {
const SFX: &[&str; 1] = &[TOP];
- return Some(read_with("turrets", "thruster", SFX, 4u32));
+ return Some(ImageHolder::Own(read_with(
+ "turrets", "thruster", SFX, 4u32,
+ )));
}
- Some(load("walls", name).unwrap())
+ Some(ImageHolder::Borrow(load("walls", name).unwrap()))
});
make_register! {
diff --git a/src/data/dynamic.rs b/src/data/dynamic.rs
index b44e68c..b901725 100644
--- a/src/data/dynamic.rs
+++ b/src/data/dynamic.rs
@@ -1,6 +1,5 @@
//! variable type
-use std::error::Error;
-use std::fmt;
+use thiserror::Error;
use crate::content;
use crate::data::command::{self, UnitCommand};
@@ -351,102 +350,46 @@ impl Serializer<DynData> for DynSerializer {
}
}
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Error)]
pub enum ReadError {
- Underlying(data::ReadError),
+ #[error("failed to read from buffer")]
+ Underlying(#[from] data::ReadError),
+ #[error("invalid dynamic data type ({0})")]
Type(u8),
- ContentType(content::TryFromU8Error),
+ #[error("content type not found")]
+ ContentType(#[from] content::TryFromU8Error),
+ #[error("integer array too long ({0})")]
IntArrayLen(i16),
+ #[error("point2 array too long ({0})")]
Point2ArrayLen(i8),
+ #[error("invalid logic field ({0})")]
LogicField(u8),
+ #[error("byte array too long ({0})")]
ByteArrayLen(i32),
- UnitCommand(command::TryFromU8Error),
+ #[error("unit command not found")]
+ UnitCommand(#[from] command::TryFromU8Error),
+ #[error("boolean array too long ({0}")]
BoolArrayLen(i32),
+ #[error("vec2 array too long ({0})")]
Vec2ArrayLen(i16),
}
-impl From<data::ReadError> for ReadError {
- fn from(err: data::ReadError) -> Self {
- Self::Underlying(err)
- }
-}
-
-impl From<content::TryFromU8Error> for ReadError {
- fn from(err: content::TryFromU8Error) -> Self {
- Self::ContentType(err)
- }
-}
-
-impl From<command::TryFromU8Error> for ReadError {
- fn from(err: command::TryFromU8Error) -> Self {
- Self::UnitCommand(err)
- }
-}
-
-impl fmt::Display for ReadError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Self::Underlying(..) => f.write_str("failed to read from buffer"),
- Self::Type(id) => write!(f, "invalid dynamic data type ({id})"),
- Self::ContentType(..) => f.write_str("content type not found"),
- Self::IntArrayLen(len) => write!(f, "integer array too long ({len})"),
- Self::Point2ArrayLen(len) => write!(f, "point2 array too long ({len})"),
- Self::LogicField(id) => write!(f, "invalid logic field ({id})"),
- Self::ByteArrayLen(len) => write!(f, "byte array too long ({len})"),
- Self::UnitCommand(..) => f.write_str("unit command not found"),
- Self::BoolArrayLen(len) => write!(f, "boolean array too long ({len})"),
- Self::Vec2ArrayLen(len) => write!(f, "vec2 array too long ({len})"),
- }
- }
-}
-
-impl Error for ReadError {
- fn source(&self) -> Option<&(dyn Error + 'static)> {
- match self {
- Self::Underlying(e) => Some(e),
- _ => None,
- }
- }
-}
-
-#[derive(Debug, PartialEq)]
+#[derive(Debug, PartialEq, Error)]
pub enum WriteError {
- Underlying(data::WriteError),
+ #[error("failed to write to buffer")]
+ Underlying(#[from] data::WriteError),
+ #[error("integer array too long ({0})")]
IntArrayLen(usize),
+ #[error("point2 array too long ({0})")]
Point2ArrayLen(usize),
+ #[error("byte array too long ({0})")]
ByteArrayLen(usize),
+ #[error("boolean array too long ({0})")]
BoolArrayLen(usize),
+ #[error("vec2 array too long ({0})")]
Vec2ArrayLen(usize),
}
-impl From<data::WriteError> for WriteError {
- fn from(err: data::WriteError) -> Self {
- Self::Underlying(err)
- }
-}
-
-impl fmt::Display for WriteError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Self::Underlying(..) => f.write_str("failed to write to buffer"),
- Self::IntArrayLen(len) => write!(f, "integer array too long ({len})"),
- Self::Point2ArrayLen(len) => write!(f, "point2 array too long ({len})"),
- Self::ByteArrayLen(len) => write!(f, "byte array too long ({len})"),
- Self::BoolArrayLen(len) => write!(f, "boolean array too long ({len})"),
- Self::Vec2ArrayLen(len) => write!(f, "vec2 array too long ({len})"),
- }
- }
-}
-
-impl Error for WriteError {
- fn source(&self) -> Option<&(dyn Error + 'static)> {
- match self {
- Self::Underlying(e) => Some(e),
- _ => None,
- }
- }
-}
-
#[cfg(test)]
mod test {
use super::*;
diff --git a/src/data/map.rs b/src/data/map.rs
new file mode 100644
index 0000000..4b1e5bd
--- /dev/null
+++ b/src/data/map.rs
@@ -0,0 +1,462 @@
+//! the map module
+//! ### format
+//! note: utf = `len<u16>` + utf8(read(len))
+//!
+//! note: each section has a `u32` denoting its length
+//!
+//! key: `: T` and `x<T>` both mean read T, `iterate T` means iterate `read_T()` times
+//!
+//! ZLIB compressed stream contains:
+//! - header: 4b = `MSCH`
+//! - version: `u32` (should be 7)
+//! - tag section `<u32>`
+//! - 1 byte of idk (skip)
+//! - string map (`u16` for map len, iterate each, read `utf`)
+//! - 4 bytes of idk (skip)
+//! - content header section `<u32>`:
+//! - iterate `i8` (should = `8`)'//! - the type: `i8` (0: item, block: 1, liquid: 4, status: 5, unit: 6, weather: 7, sector: 9, planet: 13//! - item count: `u1\'6` (item: 22, block: 412, liquid: 11, status: 21, unit: 66, weather: 6, sector: 35, planet: 7)
+//! - these types all have their own modules: [`crate::item`], [`crate::block::content`], [`crate::fluid`], [`crate::modifier`], [`crate::unit`], [`crate::data::weather`], [`crate::data::sector`], [`crate::data::planet`]
+//! - iterate `u16`
+//! - name: `utf`
+//! - map section `<u32>`
+//! - width: `u16`, height: `u16`
+//! - floor and tiles:
+//! - for `i` in `w * h`
+//! - `x = i % w`, `y = i / w`
+//! - floor id: `u16`
+//! - overlay id: `u16`
+//! - consecutives: `u8`
+//! - iterate `(i + 1)..(i + 1 + consecutives)`
+//! - `x = j % w`, `y = j / w`
+//! - i += consecutives
+//! - blocks
+//! - for `i` in `w * h`
+//! - block id: `u16`
+//! - packed?: `i8`
+//! - entity = `(packed & 1) not 0`
+//! - data = `(packed & 2) not 0`
+//! - if entity: central: `bool`
+//! - if entity:
+//! - if central:
+//! - chunk len: `u16`
+//! - if block == building:
+//! - revision: `i8`
+//! - tile.build.readAll
+//! - else skip `chunk len`
+//! - or data
+//! - data: `i8`
+//! - else
+//! - consecutives: `u8`
+//! - iterate `(i + 1)..(i + 1 + consecutives)`
+//! - same block
+//! - i += consecutives
+//! - entities section `<u32>`
+//! - entity mapping
+//! - iterate `u16`
+//! - id: `i16`, name: `utf`
+//! - team build plans
+//! - for t in `teams<u32>`
+//! - team = `team#<u32>`
+//! - iterate `plans<u32>`
+//! - x: `u16`, y: `u16`, rot: `u16`, id: `u16`
+//! - o: `DynData` (refer to [crate::data::dynamic::DynSerializer])
+//! - world entities
+//! - iterate `u32`
+//! - len: `u16`
+//! - type: `u8`
+//! - if !mapping\[type\]
+//! - skip(len - 1)
+//! - continue
+//! - id: `u32`
+//! - entity read
+use std::collections::HashMap;
+use thiserror::Error;
+
+use crate::block::content::Type as BlockEnum;
+use crate::block::{Block, BlockRegistry, Rotation};
+use crate::data::dynamic::DynSerializer;
+use crate::data::renderer::*;
+use crate::data::DataRead;
+use crate::fluid::Type as Fluid;
+use crate::item::storage::Storage;
+use crate::item::Type as Item;
+use crate::team::Team;
+
+use super::GridPos;
+use super::Serializer;
+use crate::content::Content;
+use crate::utils::image::ImageUtils;
+
+/// a tile in a map
+pub struct Tile<'l> {
+ pub pos: GridPos,
+ pub floor: &'l Block,
+ pub ore: Option<&'l Block>,
+ pub build: Option<Build<'l>>,
+}
+
+pub type EntityMapping = HashMap<u8, Box<dyn Content>>;
+impl<'l> Tile<'l> {
+ fn set_block(&mut self, block: &'l Block) {
+ self.build = Some(Build {
+ block,
+ items: Storage::new(),
+ liquids: Storage::new(),
+ rotation: Rotation::Up,
+ team: crate::team::SHARDED,
+ data: 0,
+ });
+ }
+
+ /// check if this tile contains a building.
+ pub fn has_building(&self) -> bool {
+ if let Some(b) = &self.build {
+ return b.block.has_building();
+ }
+ false
+ }
+
+ /// size of this tile
+ ///
+ /// ._.
+ ///
+ /// dont think about it too much
+ pub fn size(&self) -> u8 {
+ if let Some(b) = &self.build {
+ return b.block.get_size();
+ }
+ 1
+ }
+
+ pub fn image(&self) -> ImageHolder {
+ // building covers floore
+ let i = if let Some(b) = &self.build {
+ b.image()
+ } else {
+ let mut i = self.floor.image(None).own();
+ if let Some(ore) = self.ore {
+ i.overlay(ore.image(None).borrow(), 0, 0);
+ }
+ ImageHolder::from(i)
+ };
+ i
+ }
+}
+
+impl std::fmt::Debug for Tile<'_> {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(
+ f,
+ "Tile<{},{}>@{}+{}{}",
+ self.pos.0,
+ self.pos.1,
+ self.floor.name(),
+ if let Some(ore) = &self.ore {
+ ore.name()
+ } else {
+ ""
+ },
+ if let Some(build) = &self.build {
+ format!(":{}", build.block.name())
+ } else {
+ "".to_string()
+ }
+ )
+ }
+}
+
+/// a build on a tile in a map
+#[derive(Debug)]
+pub struct Build<'l> {
+ pub block: &'l Block,
+ pub items: Storage<Item>,
+ pub liquids: Storage<Fluid>,
+ // pub health: f32,
+ pub rotation: Rotation,
+ pub team: Team,
+ pub data: i8,
+}
+
+impl Build<'_> {
+ pub fn image(&self) -> ImageHolder {
+ self.block.image(None)
+ }
+
+ pub fn read(
+ &mut self,
+ buff: &mut DataRead<'_>,
+ reg: &BlockRegistry,
+ map: &EntityMapping,
+ ) -> Result<(), ReadError> {
+ // health
+ let _ = buff.read_f32()?; // 4
+ let rot = dbg!(buff.read_u8()?); // 5
+ self.rotation = Rotation::try_from(rot & 127).unwrap_or(Rotation::Up);
+ if (rot & 128) == 0 {
+ return Err(ReadError::Version(rot & 128));
+ }
+
+ let _t = dbg!(buff.read_u8()?); // 6
+ let _v = dbg!(buff.read_u8()?); // 7
+ let mask = dbg!(buff.read_u8()?); // 8
+ if dbg!((mask & 1) != 0) {
+ self.items.clear();
+ // 10
+ for _ in 0..dbg!(buff.read_u16()?) {
+ let item = buff.read_u16()?;
+ let amount = buff.read_u32()?;
+ if let Ok(item) = Item::try_from(item) {
+ self.items.set(item, amount);
+ }
+ }
+ }
+ if mask & 2 == 0 {
+ let n = buff.read_u16()? as usize;
+ buff.skip((n * 4) + 1)?;
+ }
+ if mask & 4 == 0 {
+ self.liquids.clear();
+ for _ in 0..buff.read_u16()? {
+ let fluid = buff.read_u16()?;
+ let amount = buff.read_f32()?;
+ if let Ok(fluid) = Fluid::try_from(fluid) {
+ self.liquids.set(fluid, (amount * 100.0) as u32);
+ }
+ }
+ }
+ // "efficiency"?
+ let _ = buff.read_u8()?;
+ let _ = buff.read_u8()?;
+ // visible flags
+ let _ = buff.read_i64()?;
+ // "overriden by subclasses"
+ self.block.read(buff, reg, map)?;
+ Ok(())
+ }
+}
+
+/// a map
+#[derive(Debug)]
+pub struct Map<'l> {
+ pub width: u32,
+ pub height: u32,
+ pub tags: HashMap<String, String>,
+ pub tiles: Vec<Tile<'l>>,
+}
+
+const MAP_HEADER: [u8; 4] = [b'M', b'S', b'A', b'V'];
+
+/// error ocurring when reading a map fails
+#[derive(Debug, Error)]
+pub enum ReadError {
+ #[error("failed to read from buffer")]
+ Read(#[from] super::ReadError),
+ #[error("incorrect header ({0:?})")]
+ Header([u8; 4]),
+ #[error("unsupported version ({0})")]
+ Version(u8),
+ #[error("unknown block {0:?}")]
+ NoSuchBlock(String),
+ #[error("failed to read block data")]
+ ReadState(#[from] super::dynamic::ReadError),
+}
+
+/// serde map
+pub struct MapSerializer<'l>(pub &'l BlockRegistry<'l>);
+impl<'l> Serializer<Map<'l>> for MapSerializer<'l> {
+ type ReadError = ReadError;
+ type WriteError = ();
+ /// deserialize a map
+ ///
+ /// notes:
+ /// - does not deserialize data
+ /// - does not deserialize entities
+ fn deserialize(&mut self, buff: &mut DataRead<'_>) -> Result<Map<'l>, Self::ReadError> {
+ let buff = buff.deflate()?;
+ let mut buff = DataRead::new(&buff);
+ {
+ let mut b = [0; 4];
+ buff.read_bytes(&mut b)?;
+ if b != MAP_HEADER {
+ return Err(ReadError::Header(b));
+ }
+ }
+ let version = buff.read_u32()?;
+ if version != 7 {
+ return Err(ReadError::Version(version.try_into().unwrap_or(0)));
+ }
+ let mut tags = HashMap::new();
+ buff.read_chunk(|buff| {
+ buff.skip(1)?;
+ for _ in 0..buff.read_u8()? {
+ let key = buff.read_utf()?;
+ let value = buff.read_utf()?;
+ tags.insert(key.to_owned(), value.to_owned());
+ }
+ Ok::<(), super::ReadError>(())
+ })?;
+ buff.read_chunk(|buff| {
+ // we skip these (just keep the respective modules updated)
+ for _ in 0..buff.read_i8()? {
+ // let _ty = buff.read_u8()?;
+ // for _ in 0..buff.read_i16()? {
+ // let name = dbg!(buff.read_utf()?);
+ // }
+ buff.skip(1)?;
+ for _ in 0..buff.read_u16()? {
+ let n = buff.read_u16()?;
+ buff.skip(n as usize)?;
+ }
+ }
+ Ok::<(), super::ReadError>(())
+ })?;
+
+ // map section
+ let mut w = 0;
+ let mut h = 0;
+ let mut tiles = vec![];
+ buff.read_chunk(|buff| {
+ w = buff.read_u16()? as u32;
+ h = buff.read_u16()? as u32;
+ let count = w * h;
+ let mut i = 0;
+ while i < count {
+ let x = (i % w) as u16;
+ let y = (i / w) as u16;
+ let floor_id = buff.read_u16()?;
+ let overlay_id = buff.read_u16()?;
+ let floor = BlockEnum::try_from(floor_id)
+ .unwrap_or(BlockEnum::Stone)
+ .to(self.0)
+ .unwrap_or(&crate::block::environment::STONE);
+ let ore = BlockEnum::try_from(overlay_id)
+ .unwrap_or(BlockEnum::Air)
+ .to(self.0);
+ debug_assert!(
+ x < w as u16 && y < h as u16,
+ "{x} or {y} out of bounds ({floor:?} {ore:?})"
+ );
+ tiles.push(Tile {
+ floor,
+ ore,
+ pos: GridPos(x, y),
+ build: None,
+ });
+ let consecutives = buff.read_u8()? as u32;
+ if consecutives > 0 {
+ for i in (i + 1)..(i + 1 + consecutives) {
+ let x = (i % w) as u16;
+ let y = (i / w) as u16;
+ tiles.push(Tile {
+ floor,
+ ore,
+ pos: GridPos(x, y),
+ build: None,
+ })
+ }
+ i += consecutives;
+ }
+ i += 1;
+ }
+ let mut i = 0usize;
+ while i < count as usize {
+ let block_id = buff.read_u16()?;
+ let packed = buff.read_u8()?;
+ let entity = (packed & 1) != 0;
+ let data = (packed & 2) != 0;
+ let central = if entity { buff.read_bool()? } else { false };
+ let block = BlockEnum::try_from(block_id)
+ .map_err(|_| ReadError::NoSuchBlock(block_id.to_string()))?;
+ let block = if block != BlockEnum::Air {
+ Some(
+ self.0
+ .get(block.get_name())
+ .ok_or(ReadError::NoSuchBlock(block.to_string()))?,
+ )
+ } else {
+ None
+ };
+ if central {
+ if let Some(block) = block {
+ tiles[i].set_block(block);
+ }
+ }
+ if entity {
+ if central {
+ // TODO: actually read
+ let n = buff.read_u16()?;
+ buff.skip(n as usize)?;
+ // let _ = buff.read_i8()?;
+ // tiles[i]
+ // .build
+ // .as_mut()
+ // .unwrap()
+ // // map not initialized yet
+ // .read(&mut buff, self.0, &HashMap::new())?;
+ }
+ } else if data {
+ if let Some(block) = block {
+ tiles[i].set_block(block);
+ }
+ tiles[i].build.as_mut().unwrap().data = buff.read_i8()?;
+ } else {
+ let consecutives = buff.read_u8()? as usize;
+ for tile in tiles.iter_mut().take(consecutives).skip(i + 1) {
+ if let Some(block) = block {
+ tile.set_block(block);
+ }
+ }
+ i += consecutives;
+ }
+ i += 1
+ }
+ Ok::<(), ReadError>(())
+ })?;
+ let mut mapping = EntityMapping::new();
+ buff.read_chunk(|buff| {
+ for _ in 0..buff.read_u16()? {
+ let id = buff.read_i16()? as u8;
+ let nam = buff.read_utf()?;
+ dbg!(nam);
+ mapping.insert(id, Box::new(Item::Copper));
+ // mapping.push(content::Type::get_name(nam));
+ }
+ for _ in 0..buff.read_u32()? {
+ buff.skip(4)?;
+ for _ in 0..buff.read_u32()? {
+ buff.skip(8usize)?;
+ let _ = DynSerializer::deserialize(&mut DynSerializer, buff)?;
+ }
+ }
+ for _ in 0..buff.read_u32()? {
+ let len = buff.read_u16()? as usize;
+ let ty = buff.read_u8()?;
+ if !mapping.contains_key(&ty) {
+ buff.skip(len - 1)?;
+ continue;
+ }
+ let _id = buff.read_u32()?;
+ // TODO
+ }
+ Ok::<(), ReadError>(())
+ })?;
+ // skip custom chunks
+ buff.skip_chunk()?;
+ Ok(Map {
+ width: w,
+ height: h,
+ tags,
+ tiles,
+ })
+ }
+
+ /// serialize a map (todo)
+ /// panics: always
+ fn serialize(
+ &mut self,
+ _: &mut super::DataWrite<'_>,
+ _: &Map<'_>,
+ ) -> Result<(), Self::WriteError> {
+ todo!()
+ }
+}
diff --git a/src/data/mod.rs b/src/data/mod.rs
index 2426f44..589a33c 100644
--- a/src/data/mod.rs
+++ b/src/data/mod.rs
@@ -7,16 +7,29 @@ use std::collections::HashMap;
use std::error::Error;
use std::fmt;
use std::str::Utf8Error;
+use thiserror::Error;
mod base64;
mod command;
pub mod dynamic;
+pub mod map;
+pub mod planet;
pub mod renderer;
pub mod schematic;
+pub mod sector;
+pub mod weather;
#[derive(Debug)]
pub struct DataRead<'d> {
data: &'d [u8],
+ // used with read_chunk
+ read: usize,
+}
+
+impl fmt::Display for DataRead<'_> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", String::from_utf8_lossy(self.data))
+ }
}
macro_rules! make_read {
@@ -32,6 +45,7 @@ macro_rules! make_read {
let mut output = [0u8; LEN];
output.copy_from_slice(&self.data[..LEN]);
self.data = &self.data[LEN..];
+ self.read += LEN;
Ok(<$type>::from_be_bytes(output))
}
};
@@ -40,7 +54,7 @@ macro_rules! make_read {
impl<'d> DataRead<'d> {
#[must_use]
pub fn new(data: &'d [u8]) -> Self {
- Self { data }
+ Self { data, read: 0 }
}
pub fn read_bool(&mut self) -> Result<bool, ReadError> {
@@ -75,6 +89,7 @@ impl<'d> DataRead<'d> {
}
let result = std::str::from_utf8(&self.data[..end])?;
self.data = &self.data[end..];
+ self.read += end;
Ok(result)
}
@@ -87,9 +102,48 @@ impl<'d> DataRead<'d> {
}
dst.copy_from_slice(&self.data[..dst.len()]);
self.data = &self.data[dst.len()..];
+ self.read += dst.len();
Ok(())
}
+ pub fn skip(&mut self, n: usize) -> Result<(), ReadError> {
+ if self.data.len() < n {
+ return Err(ReadError::Underflow {
+ need: n,
+ have: self.data.len(),
+ });
+ }
+ self.data = &self.data[n..];
+ self.read += n;
+ Ok(())
+ }
+
+ pub fn skip_chunk(&mut self) -> Result<usize, ReadError> {
+ let len = self.read_u32()? as usize;
+ self.skip(len)?;
+ Ok(len)
+ }
+
+ pub fn read_chunk<E>(&mut self, f: impl FnOnce(&mut DataRead) -> Result<(), E>) -> Result<(), E>
+ where
+ E: Error + From<ReadError>,
+ {
+ let len = self.read_u32()? as usize;
+ self.read = 0;
+ let r = f(self);
+ match r {
+ Err(e) => {
+ // skip this chunk
+ let n = len - self.read;
+ if n != 0 {
+ self.skip(n)?;
+ };
+ Err(e)
+ }
+ Ok(_) => Ok(()),
+ }
+ }
+
pub fn read_vec(&mut self, dst: &mut Vec<u8>, len: usize) -> Result<(), ReadError> {
if self.data.len() < len {
return Err(ReadError::Underflow {
@@ -99,6 +153,7 @@ impl<'d> DataRead<'d> {
}
dst.extend_from_slice(&self.data[..len]);
self.data = &self.data[len..];
+ self.read += len;
Ok(())
}
@@ -137,56 +192,31 @@ impl<'d> DataRead<'d> {
raw.reserve(1024);
}
assert_eq!(dec.total_out() as usize, raw.len());
+ self.read = 0;
Ok(raw)
}
}
-#[derive(Debug)]
+#[derive(Debug, Error)]
pub enum ReadError {
+ #[error("decompressor stalled before completion")]
DecompressStall,
- Decompress(DecompressError),
+ #[error("zlib decompession failed")]
+ Decompress(#[from] DecompressError),
+ #[error("buffer underflow (expected {need} but got {have})")]
Underflow { need: usize, have: usize },
- Utf8(Utf8Error),
+ #[error("expected {0}")]
+ Expected(&'static str),
+ #[error("malformed utf8 in string")]
+ Utf8 {
+ #[from]
+ source: Utf8Error,
+ },
}
impl PartialEq for ReadError {
fn eq(&self, _: &Self) -> bool {
- return false;
- }
-}
-
-impl From<DecompressError> for ReadError {
- fn from(value: DecompressError) -> Self {
- Self::Decompress(value)
- }
-}
-
-impl From<Utf8Error> for ReadError {
- fn from(err: Utf8Error) -> Self {
- Self::Utf8(err)
- }
-}
-
-impl fmt::Display for ReadError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Self::Underflow { need, have } => {
- write!(f, "buffer underflow (expected {need} but got {have})")
- }
- Self::Decompress(..) => f.write_str("zlib decompression failed"),
- Self::DecompressStall => f.write_str("decompressor stalled before completion"),
- Self::Utf8(..) => f.write_str("malformed utf-8 in string"),
- }
- }
-}
-
-impl Error for ReadError {
- fn source(&self) -> Option<&(dyn Error + 'static)> {
- match self {
- Self::Utf8(e) => Some(e),
- Self::Decompress(e) => Some(e),
- _ => None,
- }
+ false
}
}
@@ -281,7 +311,9 @@ impl<'d> DataWrite<'d> {
pub fn inflate(self, to: &mut DataWrite) -> Result<(), WriteError> {
// compress into the provided buffer
- let WriteBuff::Vec( raw) = self.data else { unreachable!("write buffer not owned") };
+ let WriteBuff::Vec(raw) = self.data else {
+ unreachable!("write buffer not owned")
+ };
let mut comp = Compress::new(Compression::default(), true);
// compress the immediate buffer into a temp buffer to copy it to buff? no thanks
match to.data {
@@ -337,50 +369,26 @@ impl Default for DataWrite<'static> {
}
}
-#[derive(Debug)]
+#[derive(Debug, Error)]
pub enum WriteError {
+ #[error("buffer overflow (expected {need} but got {have})")]
Overflow { need: usize, have: usize },
+ #[error("string too long ({len} bytes of {})", u16::MAX)]
TooLong { len: usize },
- Compress(CompressError),
+ #[error("zlib compression failed")]
+ Compress {
+ #[from]
+ source: CompressError,
+ },
+ #[error("compression overflow with {0} bytes of input remaining")]
CompressEof(usize),
+ #[error("compressor stalled before completion")]
CompressStall,
}
-impl From<CompressError> for WriteError {
- fn from(value: CompressError) -> Self {
- Self::Compress(value)
- }
-}
-
impl PartialEq for WriteError {
fn eq(&self, _: &Self) -> bool {
- return false;
- }
-}
-
-impl fmt::Display for WriteError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Self::Overflow { need, have } => {
- write!(f, "buffer overflow (expected {need} but got {have})")
- }
- Self::Compress(..) => f.write_str("zlib compression failed"),
- Self::CompressEof(remain) => write!(
- f,
- "compression overflow with {remain} bytes of input remaining"
- ),
- Self::CompressStall => f.write_str("compressor stalled before completion"),
- Self::TooLong { len } => write!(f, "string too long ({len} bytes of {})", u16::MAX),
- }
- }
-}
-
-impl Error for WriteError {
- fn source(&self) -> Option<&(dyn Error + 'static)> {
- match self {
- Self::Compress(e) => Some(e),
- _ => None,
- }
+ false
}
}
diff --git a/src/data/planet.rs b/src/data/planet.rs
new file mode 100644
index 0000000..ff20bcd
--- /dev/null
+++ b/src/data/planet.rs
@@ -0,0 +1,10 @@
+//! planets
+//!
+//! [source](https://github.com/Anuken/Mindustry/blob/master/core/src/mindustry/content/Planets.java)
+use crate::content::numeric_enum;
+
+numeric_enum! {
+ pub enum Planet for u8 | TryFromU8Error {
+ Sun, Erekir, Gier, Notva, Tantros, Serpulo, Verlius
+ }
+}
diff --git a/src/data/renderer.rs b/src/data/renderer.rs
index 05486d8..a1af661 100644
--- a/src/data/renderer.rs
+++ b/src/data/renderer.rs
@@ -1,24 +1,86 @@
//! schematic drawing
-use std::io::{BufReader, Cursor};
-use std::path::Path;
-
+use dashmap::mapref::one::Ref;
+use dashmap::DashMap;
use image::codecs::png::PngDecoder;
-use image::imageops::overlay;
use image::{DynamicImage, RgbaImage};
+use std::io::{BufReader, Cursor};
+use std::path::{Path, PathBuf};
+use std::sync::OnceLock;
use zip::ZipArchive;
+use crate::block::environment::METAL_FLOOR;
+use crate::data::map::Tile;
use crate::team::SHARDED;
-use crate::utils::image::*;
+use crate::utils::ImageUtils;
+use crate::Map;
+pub use std::borrow::Borrow;
use super::schematic::Schematic;
-pub(crate) fn load(category: &str, name: &str) -> Option<RgbaImage> {
- let mut p = Path::new("blocks").join(category).join(name);
- p.set_extension("png");
- load_raw(p)
+type Cache = DashMap<PathBuf, RgbaImage>;
+fn cache() -> &'static Cache {
+ CACHE.get_or_init(Cache::new)
+}
+
+pub enum ImageHolder {
+ Borrow(Ref<'static, PathBuf, RgbaImage>),
+ Own(RgbaImage),
+}
+
+impl ImageHolder {
+ pub fn own(self) -> RgbaImage {
+ match self {
+ Self::Own(x) => x,
+ Self::Borrow(x) => x.clone(),
+ }
+ }
}
-pub(crate) fn load_raw(f: impl AsRef<Path>) -> Option<RgbaImage> {
+impl Borrow<RgbaImage> for ImageHolder {
+ fn borrow(&self) -> &RgbaImage {
+ match self {
+ Self::Own(x) => x,
+ Self::Borrow(x) => x.value(),
+ }
+ }
+}
+
+impl From<Option<Ref<'static, PathBuf, RgbaImage>>> for ImageHolder {
+ fn from(value: Option<Ref<'static, PathBuf, RgbaImage>>) -> Self {
+ Self::Borrow(value.unwrap())
+ }
+}
+
+impl From<Ref<'static, PathBuf, RgbaImage>> for ImageHolder {
+ fn from(value: Ref<'static, PathBuf, RgbaImage>) -> Self {
+ Self::Borrow(value)
+ }
+}
+
+impl From<RgbaImage> for ImageHolder {
+ fn from(value: RgbaImage) -> Self {
+ Self::Own(value)
+ }
+}
+
+static CACHE: OnceLock<Cache> = OnceLock::new();
+pub(crate) fn load(category: &str, name: &str) -> Option<Ref<'static, PathBuf, RgbaImage>> {
+ let key = Path::new("blocks").join(category).join(name);
+ let mut p = key.clone();
+ use dashmap::mapref::entry::Entry::*;
+ Some(match cache().entry(key) {
+ Occupied(v) => v.into_ref().downgrade(),
+ Vacant(entry) => {
+ p.set_extension("png");
+ let Some(i) = load_raw(p) else {
+ return None;
+ };
+ entry.insert(i).downgrade()
+ }
+ })
+}
+
+fn load_raw(f: impl AsRef<Path>) -> Option<RgbaImage> {
let f = std::fs::File::open(Path::new("target/out").join(f)).ok()?;
let r = PngDecoder::new(BufReader::new(f)).unwrap();
Some(DynamicImage::from_decoder(r).unwrap().into_rgba8())
@@ -55,11 +117,12 @@ where
{
let mut c = RgbaImage::new(size.into() * 32, size.into() * 32);
for suffix in suffixes {
- if let Some(mut p) = load(category, &format!("{name}{suffix}")) {
+ if let Some(p) = load(category, &format!("{name}{suffix}")) {
if suffix == &"-team" {
- tint(&mut p, SHARDED.color());
+ c.overlay(p.clone().tint(SHARDED.color()), 0, 0);
+ continue;
}
- image::imageops::overlay(&mut c, &p, 0, 0);
+ c.overlay(&p, 0, 0);
}
}
c
@@ -76,17 +139,49 @@ impl<'l> Renderer {
/// s.put(0, 0, &block::distribution::DISTRIBUTOR);
/// s.put(0, 3, &block::distribution::ROUTER);
/// s.put(1, 3, &block::walls::COPPER_WALL);
- /// let output /*: RgbaImage */ = Renderer::render(&s);
+ /// let output /*: RgbaImage */ = Renderer::render_schematic(&s);
/// ```
- pub fn render(s: &'l Schematic<'_>) -> RgbaImage {
+ pub fn render_schematic(s: &'l Schematic<'_>) -> RgbaImage {
load_zip();
let mut canvas = RgbaImage::new((s.width * 32).into(), (s.height * 32).into());
// fill background
- repeat(&mut canvas, &load("environment", "metal-floor").unwrap());
+ canvas.repeat(METAL_FLOOR.image(None).borrow());
for tile in s.block_iter() {
- let x = (tile.pos.0 - ((tile.block.get_size() - 1) / 2) as u16) as i64;
- let y = (s.height - tile.pos.1 - ((tile.block.get_size() / 2) + 1) as u16) as i64;
- overlay(&mut canvas, &tile.image(), x * 32, y * 32);
+ let x = (tile.pos.0 - ((tile.block.get_size() - 1) / 2) as u16) as u32;
+ let y = (s.height - tile.pos.1 - ((tile.block.get_size() / 2) + 1) as u16) as u32;
+ canvas.overlay(tile.image().borrow(), x * 32, y * 32);
+ }
+ canvas
+ }
+
+ pub fn render_map(m: &'l Map<'_>) -> RgbaImage {
+ load_zip();
+ let mut canvas = RgbaImage::new(m.width * 8, m.height * 8);
+ const VEC: Vec<&Tile<'_>> = vec![];
+ let mut layers = [VEC; 2];
+ for tile in m.tiles.iter() {
+ if tile.has_building() {
+ layers[1].push(tile)
+ } else {
+ layers[0].push(tile)
+ }
+ }
+ for tiles in layers {
+ for tile in tiles {
+ let s = if let Some(build) = &tile.build {
+ build.block.get_size()
+ } else {
+ 1
+ };
+ let x = (tile.pos.0 - ((s - 1) / 2) as u16) as u32;
+ let y = (m.height as u16 - tile.pos.1 - ((s / 2) + 1) as u16) as u32;
+ canvas.overlay(
+ // SAFETY: surely not 0. (tile.size can never be 0). im not sure if you can load a 0 sized image.. but you might be able to.
+ unsafe { &tile.image().own().scale(tile.size() as u32 * 8) },
+ x * 8,
+ y * 8,
+ );
+ }
}
canvas
}
diff --git a/src/data/schematic.rs b/src/data/schematic.rs
index 19026f2..75378ba 100644
--- a/src/data/schematic.rs
+++ b/src/data/schematic.rs
@@ -1,24 +1,22 @@
//! schematic parsing
use std::collections::hash_map::Entry;
use std::collections::HashMap;
-use std::error::Error;
use std::fmt::{self, Write};
use std::iter::FusedIterator;
use std::slice::Iter;
-
-use image::RgbaImage;
+use thiserror::Error;
use crate::block::{self, Block, BlockRegistry, Rotation, State};
use crate::data::base64;
use crate::data::dynamic::{self, DynData, DynSerializer};
use crate::data::{self, DataRead, DataWrite, GridPos, Serializer};
-use crate::item::storage::Storage as ItemStorage;
+use crate::item::storage::ItemStorage;
use crate::registry::RegistryEntry;
/// biggest schematic
-pub const MAX_DIMENSION: u16 = 128;
+pub const MAX_DIMENSION: u16 = 256;
/// most possible blocks
-pub const MAX_BLOCKS: u32 = 128 * 128;
+pub const MAX_BLOCKS: u32 = 256 * 256;
/// a placement in a schematic
pub struct Placement<'l> {
@@ -47,7 +45,7 @@ impl<'l> Placement<'l> {
}
/// draws this placement in particular
- pub fn image(&self) -> RgbaImage {
+ pub fn image(&self) -> crate::data::renderer::ImageHolder {
self.block.image(self.get_state())
}
@@ -602,12 +600,12 @@ impl<'l> Schematic<'l> {
}
}
if left > 0 || top > 0 || right > 0 || bottom > 0 {
- return Err(ResizeError::Truncated {
+ return Err(TruncatedError {
right,
top,
left,
bottom,
- });
+ })?;
}
self.width = w;
self.height = h;
@@ -664,25 +662,16 @@ impl<'l> Schematic<'l> {
}
/// error created by creating a new schematic
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Error)]
pub enum NewError {
+ #[error("invalid schematic width ({0})")]
Width(u16),
+ #[error("invalid schematic height ({0})")]
Height(u16),
}
-
-impl fmt::Display for NewError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Self::Width(w) => write!(f, "invalid schematic width ({w})"),
- Self::Height(h) => write!(f, "invalid schematic height ({h})"),
- }
- }
-}
-
-impl Error for NewError {}
-
/// error created by doing stuff out of bounds
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+#[derive(Copy, Clone, Debug, Eq, PartialEq, Error)]
+#[error("position {x} / {y} out of bounds {w} / {h}")]
pub struct PosError {
pub x: u16,
pub y: u16,
@@ -690,23 +679,9 @@ pub struct PosError {
pub h: u16,
}
-impl fmt::Display for PosError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(
- f,
- "position {x} / {y} out of bounds {w} / {h}",
- x = self.x,
- y = self.y,
- w = self.w,
- h = self.h
- )
- }
-}
-
-impl Error for PosError {}
-
-#[derive(Debug)]
+#[derive(Debug, Error)]
pub enum PlaceError {
+ #[error("invalid block placement {x} / {y} (size {sz}) within {w} / {h}")]
Bounds {
x: u16,
y: u16,
@@ -714,111 +689,63 @@ pub enum PlaceError {
w: u16,
h: u16,
},
- Overlap {
- x: u16,
- y: u16,
- },
- Deserialize(block::DeserializeError),
-}
-
-impl From<block::DeserializeError> for PlaceError {
- fn from(value: block::DeserializeError) -> Self {
- PlaceError::Deserialize(value)
- }
+ #[error("overlapping an existing block at {x} / {y}")]
+ Overlap { x: u16, y: u16 },
+ #[error("block state deserialization failed")]
+ Deserialize(#[from] block::DeserializeError),
}
-impl fmt::Display for PlaceError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Self::Bounds { x, y, sz, w, h } => write!(
- f,
- "invalid block placement {x} / {y} (size {sz}) within {w} / {h}"
- ),
- Self::Overlap { x, y } => write!(f, "overlapping an existing block at {x} / {y}"),
- Self::Deserialize(..) => f.write_str("block state deserialization failed"),
- }
- }
-}
-
-impl Error for PlaceError {
- fn source(&self) -> Option<&(dyn Error + 'static)> {
- match self {
- PlaceError::Deserialize(e) => Some(e),
- _ => None,
- }
- }
-}
-
-#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+#[derive(Debug, Error)]
pub enum ResizeError {
+ #[error("invalid target width ({0})")]
TargetWidth(u16),
+ #[error("invalid target height ({0})")]
TargetHeight(u16),
- XOffset {
- dx: i16,
- old_w: u16,
- new_w: u16,
- },
- YOffset {
- dy: i16,
- old_h: u16,
- new_h: u16,
- },
- Truncated {
- right: u16,
- top: u16,
- left: u16,
- bottom: u16,
- },
+ #[error("horizontal offset {dx} not in [-{new_w}, {old_w}]")]
+ XOffset { dx: i16, old_w: u16, new_w: u16 },
+ #[error("vertical offset {dy} not in [-{new_h}, {old_h}]")]
+ YOffset { dy: i16, old_h: u16, new_h: u16 },
+ #[error(transparent)]
+ Truncated(#[from] TruncatedError),
+}
+
+#[derive(Error, Debug)]
+pub struct TruncatedError {
+ right: u16,
+ top: u16,
+ left: u16,
+ bottom: u16,
}
-impl fmt::Display for ResizeError {
+impl fmt::Display for TruncatedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Self::TargetWidth(w) => write!(f, "invalid target width ({w})"),
- Self::TargetHeight(w) => write!(f, "invalid target height ({w})"),
- Self::XOffset { dx, old_w, new_w } => {
- write!(f, "horizontal offset {dx} not in [-{new_w}, {old_w}]")
- }
- Self::YOffset { dy, old_h, new_h } => {
- write!(f, "vertical offset {dy} not in [-{new_h}, {old_h}]")
- }
- Self::Truncated {
- right,
- top,
- left,
- bottom,
- } => {
- macro_rules! fmt_dir {
- ($f:ident, $first:ident, $name:expr, $value:expr) => {
- if $value != 0 {
- if $first {
- f.write_str(" (")?;
- $first = false;
- } else {
- f.write_str(", ")?;
- }
- write!(f, "{}: {}", $name, $value)?;
- }
- };
+ macro_rules! fmt_dir {
+ ($f:ident, $first:ident, $name:expr, $value:expr) => {
+ if $value != 0 {
+ if $first {
+ f.write_str(" (")?;
+ $first = false;
+ } else {
+ f.write_str(", ")?;
+ }
+ write!(f, "{}: {}", $name, $value)?;
}
+ };
+ }
- f.write_str("truncated blocks")?;
- let mut first = true;
- fmt_dir!(f, first, "right", *right);
- fmt_dir!(f, first, "top", *top);
- fmt_dir!(f, first, "left", *left);
- fmt_dir!(f, first, "bottom", *bottom);
- if !first {
- f.write_char(')')?;
- }
- Ok(())
- }
+ f.write_str("truncated blocks")?;
+ let mut first = true;
+ fmt_dir!(f, first, "right", self.right);
+ fmt_dir!(f, first, "top", self.top);
+ fmt_dir!(f, first, "left", self.left);
+ fmt_dir!(f, first, "bottom", self.bottom);
+ if !first {
+ f.write_char(')')?;
}
+ Ok(())
}
}
-impl Error for ResizeError {}
-
impl fmt::Debug for Schematic<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(self, f)
@@ -1011,8 +938,8 @@ impl<'l> Serializer<Schematic<'l>> for SchematicSerializer<'l> {
if version > 1 {
return Err(ReadError::Version(version));
}
- let mut buff = buff.deflate()?;
- let mut buff = DataRead::new(&mut buff);
+ let buff = buff.deflate()?;
+ let mut buff = DataRead::new(&buff);
let w = buff.read_i16()?;
let h = buff.read_i16()?;
if w < 0 || h < 0 || w as u16 > MAX_DIMENSION || h as u16 > MAX_DIMENSION {
@@ -1028,7 +955,7 @@ impl<'l> Serializer<Schematic<'l>> for SchematicSerializer<'l> {
block_table.reserve(num_table as usize);
for _ in 0..num_table {
let name = buff.read_utf()?;
- match self.0.get(&name) {
+ match self.0.get(name) {
None => return Err(ReadError::NoSuchBlock(name.to_owned())),
Some(b) => block_table.push(b),
}
@@ -1113,122 +1040,44 @@ impl<'l> Serializer<Schematic<'l>> for SchematicSerializer<'l> {
}
}
-#[derive(Debug)]
+#[derive(Debug, Error)]
pub enum ReadError {
- Read(data::ReadError),
+ #[error("failed to read from buffer")]
+ Read(#[from] data::ReadError),
+ #[error("incorrect header ({0:08X})")]
Header(u32),
+ #[error("unsupported version ({0})")]
Version(u8),
+ #[error("invalid schematic dimensions ({0} * {1})")]
Dimensions(i16, i16),
+ #[error("invalid block table size ({0})")]
TableSize(i8),
+ #[error("unknown block {0:?}")]
NoSuchBlock(String),
+ #[error("invalid total block count ({0})")]
BlockCount(i32),
+ #[error("invalid block index ({0} / {1})")]
BlockIndex(i8, usize),
- BlockConfig(block::DataConvertError),
- ReadState(dynamic::ReadError),
- Placement(PlaceError),
-}
-
-impl From<data::ReadError> for ReadError {
- fn from(value: data::ReadError) -> Self {
- Self::Read(value)
- }
+ #[error("block config conversion failed")]
+ BlockConfig(#[from] block::DataConvertError),
+ #[error("failed to read block data")]
+ ReadState(#[from] dynamic::ReadError),
+ #[error("deserialized block could not be placed")]
+ Placement(#[from] PlaceError),
}
-impl From<dynamic::ReadError> for ReadError {
- fn from(value: dynamic::ReadError) -> Self {
- Self::ReadState(value)
- }
-}
-
-impl From<block::DataConvertError> for ReadError {
- fn from(value: block::DataConvertError) -> Self {
- Self::BlockConfig(value)
- }
-}
-
-impl From<PlaceError> for ReadError {
- fn from(value: PlaceError) -> Self {
- Self::Placement(value)
- }
-}
-
-impl fmt::Display for ReadError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Self::Read(..) => f.write_str("failed to read from buffer"),
- Self::Header(hdr) => write!(f, "incorrect header ({hdr:08X})"),
- Self::Version(ver) => write!(f, "unsupported version ({ver})"),
- Self::Dimensions(w, h) => write!(f, "invalid schematic dimensions ({w} * {h})"),
- Self::TableSize(cnt) => write!(f, "invalid block table size ({cnt})"),
- Self::NoSuchBlock(name) => write!(f, "unknown block {name:?}"),
- Self::BlockCount(cnt) => write!(f, "invalid total block count ({cnt})"),
- Self::BlockIndex(idx, cnt) => write!(f, "invalid block index ({idx} / {cnt})"),
- Self::BlockConfig(..) => f.write_str("block config conversion failed"),
- Self::ReadState(..) => f.write_str("failed to read block data"),
- Self::Placement(..) => f.write_str("deserialized block could not be placed"),
- }
- }
-}
-
-impl Error for ReadError {
- fn source(&self) -> Option<&(dyn Error + 'static)> {
- match self {
- Self::Read(e) => Some(e),
- Self::BlockConfig(e) => Some(e),
- Self::ReadState(e) => Some(e),
- Self::Placement(e) => Some(e),
- _ => None,
- }
- }
-}
-
-#[derive(Debug)]
+#[derive(Debug, Error)]
pub enum WriteError {
- Write(data::WriteError),
+ #[error("failed to write data to buffer")]
+ Write(#[from] data::WriteError),
+ #[error("tag list too long ({0})")]
TagCount(usize),
+ #[error("block table too long ({0})")]
TableSize(usize),
- StateSerialize(block::SerializeError),
- WriteState(dynamic::WriteError),
-}
-
-impl From<data::WriteError> for WriteError {
- fn from(value: data::WriteError) -> Self {
- Self::Write(value)
- }
-}
-
-impl From<block::SerializeError> for WriteError {
- fn from(value: block::SerializeError) -> Self {
- Self::StateSerialize(value)
- }
-}
-
-impl From<dynamic::WriteError> for WriteError {
- fn from(value: dynamic::WriteError) -> Self {
- Self::WriteState(value)
- }
-}
-
-impl fmt::Display for WriteError {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Self::Write(..) => f.write_str("failed to write data to buffer"),
- Self::TagCount(len) => write!(f, "tag list too long ({len})"),
- Self::TableSize(len) => write!(f, "block table too long ({len})"),
- Self::StateSerialize(e) => e.fmt(f),
- Self::WriteState(..) => f.write_str("failed to write block data"),
- }
- }
-}
-
-impl Error for WriteError {
- fn source(&self) -> Option<&(dyn Error + 'static)> {
- match self {
- Self::Write(e) => Some(e),
- Self::StateSerialize(e) => e.source(),
- _ => None,
- }
- }
+ #[error(transparent)]
+ StateSerialize(#[from] block::SerializeError),
+ #[error("failed to write block data")]
+ WriteState(#[from] dynamic::WriteError),
}
impl<'l> SchematicSerializer<'l> {
@@ -1265,76 +1114,20 @@ impl<'l> SchematicSerializer<'l> {
}
}
-#[derive(Debug)]
+#[derive(Debug, Error)]
pub enum R64Error {
- Base64(base64::DecodeError),
- Content(ReadError),
+ #[error("base-64 decoding failed")]
+ Base64(#[from] base64::DecodeError),
+ #[error(transparent)]
+ Content(#[from] ReadError),
}
-impl From<base64::DecodeError> for R64Error {
- fn from(value: base64::DecodeError) -> Self {
- Self::Base64(value)
- }
-}
-
-impl From<ReadError> for R64Error {
- fn from(value: ReadError) -> Self {
- Self::Content(value)
- }
-}
-
-impl fmt::Display for R64Error {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Self::Base64(..) => f.write_str("base-64 decoding failed"),
- Self::Content(e) => e.fmt(f),
- }
- }
-}
-
-impl Error for R64Error {
- fn source(&self) -> Option<&(dyn Error + 'static)> {
- match self {
- Self::Base64(e) => Some(e),
- Self::Content(e) => e.source(),
- }
- }
-}
-
-#[derive(Debug)]
+#[derive(Debug, Error)]
pub enum W64Error {
- Base64(base64::EncodeError),
- Content(WriteError),
-}
-
-impl From<base64::EncodeError> for W64Error {
- fn from(value: base64::EncodeError) -> Self {
- Self::Base64(value)
- }
-}
-
-impl From<WriteError> for W64Error {
- fn from(value: WriteError) -> Self {
- Self::Content(value)
- }
-}
-
-impl fmt::Display for W64Error {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Self::Base64(..) => f.write_str("base-64 encoding failed"),
- Self::Content(e) => e.fmt(f),
- }
- }
-}
-
-impl Error for W64Error {
- fn source(&self) -> Option<&(dyn Error + 'static)> {
- match self {
- Self::Base64(e) => Some(e),
- Self::Content(e) => e.source(),
- }
- }
+ #[error("base-64 encoding failed")]
+ Base64(#[from] base64::EncodeError),
+ #[error(transparent)]
+ Content(#[from] WriteError),
}
pub struct PosIter {
diff --git a/src/data/sector.rs b/src/data/sector.rs
new file mode 100644
index 0000000..ac8c471
--- /dev/null
+++ b/src/data/sector.rs
@@ -0,0 +1,44 @@
+//! sectors
+//!
+//! [source](https://github.com/Anuken/Mindustry/blob/master/core/src/mindustry/content/SectorPresets.java)
+use crate::content::numeric_enum;
+
+numeric_enum! {
+ pub enum Sector for u8 | TryFromU8Error {
+ GroundZero,
+ SaltFlats,
+ FrozenForest,
+ BiomassFacility,
+ Craters,
+ RuinousShores,
+ WindsweptIslands,
+ StainedMountains,
+ ExtractionOutpost,
+ Coastline,
+ NavalFortress,
+ FungalPass,
+ Overgrowth,
+ TarFields,
+ Impact0078,
+ DesolateRift,
+ NuclearComplex,
+ PlanetaryTerminal,
+ Onset,
+ Aegis,
+ Lake,
+ Intersect,
+ Atlas,
+ Split,
+ Basin,
+ Marsh,
+ Peaks,
+ Ravine,
+ CalderaErekir,
+ Stronghold,
+ Crevice,
+ Siege,
+ Crossroads,
+ Karst,
+ Origin,
+ }
+}
diff --git a/src/data/weather.rs b/src/data/weather.rs
new file mode 100644
index 0000000..8df9f59
--- /dev/null
+++ b/src/data/weather.rs
@@ -0,0 +1,10 @@
+//! weathers
+//!
+//! [source](https://github.com/Anuken/Mindustry/blob/master/core/src/mindustry/content/Weathers.java)
+use crate::content::numeric_enum;
+
+numeric_enum! {
+ pub enum Weather for u8 | TryFromU8Error {
+ Snow, Rain, Sandstorm, Sporestorm, Fog, SuspendParticles
+ }
+}
diff --git a/src/exe/draw.rs b/src/exe/draw.rs
index 6a061d6..0a95634 100644
--- a/src/exe/draw.rs
+++ b/src/exe/draw.rs
@@ -18,7 +18,7 @@ pub fn main(args: Args) {
if !first || need_space {
println!();
}
- Renderer::render(&s).save("x.png").unwrap();
+ Renderer::render_schematic(&s).save("x.png").unwrap();
}
// continue processing literals & maybe interactive mode
Err(e) => {
diff --git a/src/exe/map.rs b/src/exe/map.rs
new file mode 100644
index 0000000..13955cd
--- /dev/null
+++ b/src/exe/map.rs
@@ -0,0 +1,22 @@
+use mindus::data::DataRead;
+use mindus::{build_registry, Renderer};
+use mindus::{MapSerializer, Serializer};
+use std::env::Args;
+
+use super::print_err;
+pub fn main(args: Args) {
+ let reg = build_registry();
+ let mut ms = MapSerializer(&reg);
+
+ // process schematics from command line
+ for curr in args {
+ if let Ok(s) = std::fs::read(curr) {
+ match ms.deserialize(&mut DataRead::new(&s)) {
+ Err(e) => print_err!(e, "fail"),
+ Ok(m) => {
+ Renderer::render_map(&m).save("x.png").unwrap();
+ }
+ }
+ }
+ }
+}
diff --git a/src/exe/mod.rs b/src/exe/mod.rs
index 0159b96..e56922a 100644
--- a/src/exe/mod.rs
+++ b/src/exe/mod.rs
@@ -1,4 +1,5 @@
mod draw;
+mod map;
mod print;
macro_rules! print_err {
@@ -30,6 +31,7 @@ fn main() {
None => eprintln!("Not enough arguments, valid commands are: draw, print"),
Some(s) if s == "print" => print::main(args),
Some(s) if s == "draw" => draw::main(args),
+ Some(s) if s == "map" => map::main(args),
Some(s) => eprintln!("Unknown argument {s}, valid commands are: draw, print"),
}
}
diff --git a/src/fluid/mod.rs b/src/fluid/mod.rs
index 45254b9..7437744 100644
--- a/src/fluid/mod.rs
+++ b/src/fluid/mod.rs
@@ -1,3 +1,6 @@
+//! fluids
+//!
+//! [source](https://github.com/Anuken/Mindustry/blob/master/core/src/mindustry/content/Liquids.java)
use crate::content::color_content_enum;
color_content_enum! {
diff --git a/src/item/mod.rs b/src/item/mod.rs
index 863d3b0..5d29176 100644
--- a/src/item/mod.rs
+++ b/src/item/mod.rs
@@ -1,4 +1,6 @@
//! the different kinds of items
+//!
+//! [source](https://github.com/Anuken/Mindustry/blob/master/core/src/mindustry/content/Items.java)
pub mod storage;
use crate::content::color_content_enum;
color_content_enum! {
diff --git a/src/item/storage.rs b/src/item/storage.rs
index 590c399..45397f5 100644
--- a/src/item/storage.rs
+++ b/src/item/storage.rs
@@ -1,45 +1,98 @@
use std::error::Error;
use std::fmt;
use std::iter::{Enumerate, FusedIterator};
+use std::marker::PhantomData;
use std::slice;
use crate::item;
#[derive(Clone, Debug, Eq)]
-/// holds item counts
-pub struct Storage {
+/// stores data
+pub struct Storage<T> {
base: Vec<u32>,
total: u64,
+ holds: PhantomData<T>,
}
-impl Storage {
- #[must_use]
- pub const fn new() -> Self {
+pub type ItemStorage = Storage<item::Type>;
+
+impl<T> Default for Storage<T> {
+ fn default() -> Self {
Self {
- base: Vec::new(),
+ base: Vec::default(),
total: 0,
+ holds: Default::default(),
}
}
+}
+impl<T> Storage<T>
+where
+ u16: From<T>,
+{
#[must_use]
- pub fn is_empty(&self) -> bool {
- self.total == 0
+ /// create a new storage
+ ///
+ /// ```
+ /// # use mindus::item::storage::ItemStorage;
+ /// // ItemStorage is a alias to Storage<Item>
+ /// let s = ItemStorage::new();
+ /// ```
+ pub fn new() -> Self {
+ Self::default()
}
#[must_use]
- pub fn get_total(&self) -> u64 {
- self.total
+ /// check if its empty
+ ///
+ /// ```
+ /// # use mindus::item::storage::ItemStorage;
+ /// # use mindus::item;
+ ///
+ /// let mut s = ItemStorage::new();
+ /// assert!(s.is_empty());
+ /// s.set(item::Type::Copper, 500);
+ /// assert!(!s.is_empty());
+ /// s.sub(item::Type::Copper, 500, 0);
+ /// assert!(s.is_empty());
+ /// ```
+ pub fn is_empty(&self) -> bool {
+ self.total == 0
}
+ /// get item count of certain element
+ ///
+ /// ```
+ /// # use mindus::item::storage::ItemStorage;
+ /// # use mindus::item;
+ ///
+ /// let mut s = ItemStorage::new();
+ /// assert!(s.get(item::Type::Coal) == 0);
+ /// s.set(item::Type::Coal, 500);
+ /// assert!(s.get(item::Type::Titanium) == 0);
+ /// assert!(s.get(item::Type::Coal) == 500);
+ /// s.sub(item::Type::Coal, 500, 0);
+ /// assert!(s.get(item::Type::Coal) == 0);
+ /// ```
#[must_use]
- pub fn get(&self, ty: item::Type) -> u32 {
+ pub fn get(&self, ty: T) -> u32 {
match self.base.get(u16::from(ty) as usize) {
None => 0,
Some(cnt) => *cnt,
}
}
-
- pub fn set(&mut self, ty: item::Type, count: u32) -> u32 {
+ /// set item count of certain element
+ ///
+ /// ```
+ /// # use mindus::item::storage::ItemStorage;
+ /// # use mindus::item;
+ ///
+ /// let mut s = ItemStorage::new();
+ /// s.set(item::Type::Coal, 500);
+ /// s.set(item::Type::Copper, 500);
+ /// assert!(s.get(item::Type::Copper) == 500);
+ /// ```
+ pub fn set(&mut self, ty: T, count: u32) -> u32 {
let idx = u16::from(ty) as usize;
match self.base.get_mut(idx) {
None => {
@@ -57,7 +110,21 @@ impl Storage {
}
}
- pub fn add(&mut self, ty: item::Type, add: u32, max: u32) -> (u32, u32) {
+ /// add to a certain elements item count, capping.
+ ///
+ /// ```
+ /// # use mindus::item::storage::ItemStorage;
+ /// # use mindus::item;
+ ///
+ /// let mut s = ItemStorage::new();
+ /// s.add(item::Type::Coal, 500, 500);
+ /// assert!(s.get(item::Type::Coal) == 500);
+ /// s.add(item::Type::Coal, 500, 10000);
+ /// assert!(s.get(item::Type::Coal) == 1000);
+ /// s.add(item::Type::Coal, 500, 1250);
+ /// assert!(s.get(item::Type::Coal) == 1250);
+ /// ```
+ pub fn add(&mut self, ty: T, add: u32, max: u32) -> (u32, u32) {
let idx = u16::from(ty) as usize;
match self.base.get_mut(idx) {
None => {
@@ -80,12 +147,8 @@ impl Storage {
}
}
- pub fn try_add(
- &mut self,
- ty: item::Type,
- add: u32,
- max: u32,
- ) -> Result<(u32, u32), TryAddError> {
+ /// like [`Storage::add`] but fails
+ pub fn try_add(&mut self, ty: T, add: u32, max: u32) -> Result<(u32, u32), TryAddError> {
let idx = u16::from(ty) as usize;
match self.base.get_mut(idx) {
None => {
@@ -95,12 +158,7 @@ impl Storage {
self.total += u64::from(add);
Ok((add, add))
} else {
- Err(TryAddError {
- ty,
- have: 0,
- add,
- max,
- })
+ Err(TryAddError { have: 0, add, max })
}
}
Some(curr) => {
@@ -110,7 +168,6 @@ impl Storage {
Ok((add, *curr))
} else {
Err(TryAddError {
- ty,
have: *curr,
add,
max,
@@ -120,7 +177,7 @@ impl Storage {
}
}
- pub fn sub(&mut self, ty: item::Type, sub: u32, min: u32) -> (u32, u32) {
+ pub fn sub(&mut self, ty: T, sub: u32, min: u32) -> (u32, u32) {
match self.base.get_mut(u16::from(ty) as usize) {
None => (0, 0),
Some(curr) => {
@@ -136,20 +193,10 @@ impl Storage {
}
}
- pub fn try_sub(
- &mut self,
- ty: item::Type,
- sub: u32,
- min: u32,
- ) -> Result<(u32, u32), TrySubError> {
+ pub fn try_sub(&mut self, ty: T, sub: u32, min: u32) -> Result<(u32, u32), TrySubError> {
let idx = u16::from(ty) as usize;
match self.base.get_mut(idx) {
- None => Err(TrySubError {
- ty,
- have: 0,
- sub,
- min,
- }),
+ None => Err(TrySubError { have: 0, sub, min }),
Some(curr) => {
if *curr >= min && *curr - min >= sub {
*curr -= sub;
@@ -157,7 +204,6 @@ impl Storage {
Ok((sub, *curr))
} else {
Err(TrySubError {
- ty,
have: *curr,
sub,
min,
@@ -167,9 +213,9 @@ impl Storage {
}
}
- pub fn add_all(&mut self, other: &Storage, max_each: u32) -> (u64, u64) {
+ pub fn add_all(&mut self, other: &Storage<T>, max_each: u32) -> (u64, u64) {
let mut added = 0u64;
- if max_each > 0 && other.get_total() > 0 {
+ if max_each > 0 && other.total > 0 {
let mut iter = other.base.iter().enumerate();
// resize our vector only once and if necessary
let (last, add_last) = iter.rfind(|(_, n)| **n != 0).unwrap();
@@ -198,9 +244,9 @@ impl Storage {
(added, self.total)
}
- pub fn pull_all(&mut self, other: &mut Storage, max_each: u32) -> (u64, u64, u64) {
+ pub fn pull_all(&mut self, other: &mut Storage<T>, max_each: u32) -> (u64, u64, u64) {
let mut added = 0u64;
- if max_each > 0 && other.get_total() > 0 {
+ if max_each > 0 && other.total > 0 {
let mut iter = other.base.iter_mut().enumerate();
// resize our vector only once and if necessary
let (last, add_last) = iter.rfind(|(_, n)| **n != 0).unwrap();
@@ -232,9 +278,9 @@ impl Storage {
(added, self.total, other.total)
}
- pub fn sub_all(&mut self, other: &Storage, min_each: u32) -> (u64, u64) {
+ pub fn sub_all(&mut self, other: &Storage<T>, min_each: u32) -> (u64, u64) {
let mut subbed = 0u64;
- if self.get_total() > 0 && other.get_total() > 0 {
+ if self.total > 0 && other.total > 0 {
// no need for resizing, we only remove
// process items by increasing ID
for (idx, sub) in other.base.iter().enumerate() {
@@ -254,9 +300,9 @@ impl Storage {
(subbed, self.total)
}
- pub fn diff_all(&mut self, other: &mut Storage, min_each: u32) -> (u64, u64, u64) {
+ pub fn diff_all(&mut self, other: &mut Storage<T>, min_each: u32) -> (u64, u64, u64) {
let mut subbed = 0u64;
- if self.get_total() > 0 && other.get_total() > 0 {
+ if self.total > 0 && other.total > 0 {
// no need for resizing, we only remove
// consider only indexes present in both
let end = self.base.len().min(other.base.len());
@@ -300,7 +346,7 @@ impl Storage {
}
// manual because padding with zeros doesn't affect equality
-impl PartialEq for Storage {
+impl<T> PartialEq for Storage<T> {
fn eq(&self, other: &Self) -> bool {
let mut li = self.base.iter().fuse();
let mut ri = other.base.iter().fuse();
@@ -317,16 +363,17 @@ impl PartialEq for Storage {
}
}
-impl fmt::Display for Storage {
+impl<T> fmt::Display for Storage<T>
+where
+ u16: From<T>,
+{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- let mut first = true;
- for (ty, cnt) in self.iter_nonzero() {
- if first {
- first = false;
- } else {
- f.write_str(", ")?;
- }
+ let mut iter = self.iter_nonzero();
+ if let Some((ty, cnt)) = iter.next() {
write!(f, "{cnt} {ty}")?;
+ for (ty, cnt) in iter {
+ write!(f, ", {cnt} {ty}")?;
+ }
}
Ok(())
}
@@ -334,7 +381,6 @@ impl fmt::Display for Storage {
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct TryAddError {
- pub ty: item::Type,
pub have: u32,
pub add: u32,
pub max: u32,
@@ -344,8 +390,8 @@ impl fmt::Display for TryAddError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
- "adding {} {:?} to current {} would exceed {}",
- self.add, self.ty, self.have, self.max
+ "adding {:?} to current {} would exceed {}",
+ self.add, self.have, self.max
)
}
}
@@ -354,7 +400,6 @@ impl Error for TryAddError {}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct TrySubError {
- pub ty: item::Type,
pub have: u32,
pub sub: u32,
pub min: u32,
@@ -364,8 +409,8 @@ impl fmt::Display for TrySubError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
- "removing {} {:?} from current {} would drop below {}",
- self.sub, self.ty, self.have, self.min
+ "removing {} from current {} would drop below {}",
+ self.sub, self.have, self.min
)
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 0107304..1fe9fe7 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -3,16 +3,17 @@ mod access;
pub mod block;
mod content;
pub mod data;
-mod fluid;
+pub mod fluid;
pub mod item;
mod logic;
-mod modifier;
+pub mod modifier;
mod registry;
mod team;
-mod unit;
+pub mod unit;
mod utils;
pub use block::build_registry;
pub use data::dynamic::DynData;
+pub use data::map::{Map, MapSerializer};
pub use data::renderer::Renderer;
pub use data::schematic::{Schematic, SchematicSerializer};
pub use data::Serializer;
diff --git a/src/logic/mod.rs b/src/logic/mod.rs
index 967ca22..b8a98fc 100644
--- a/src/logic/mod.rs
+++ b/src/logic/mod.rs
@@ -23,7 +23,8 @@ macro_rules!match_select
}
impl LogicField {
- #[must_use] pub fn is_readable(&self) -> bool {
+ #[must_use]
+ pub fn is_readable(&self) -> bool {
match_select!(
self,
LogicField,
@@ -73,7 +74,8 @@ impl LogicField {
)
}
- #[must_use] pub fn is_writable(&self) -> bool {
+ #[must_use]
+ pub fn is_writable(&self) -> bool {
match_select!(self, LogicField, Enabled, Shoot, ShootP, Config, Color)
}
}
diff --git a/src/modifier.rs b/src/modifier.rs
index 0459ce2..bf1d867 100644
--- a/src/modifier.rs
+++ b/src/modifier.rs
@@ -1,4 +1,6 @@
//! modifiers units can be afflicted with
+//!
+//! [source](https://github.com/Anuken/Mindustry/blob/master/core/src/mindustry/content/SectorPresets.java)
use crate::content::content_enum;
content_enum! {
diff --git a/src/team.rs b/src/team.rs
index f25f7a4..596798f 100644
--- a/src/team.rs
+++ b/src/team.rs
@@ -121,14 +121,14 @@ impl Team {
Rgb(color_hex::color_from_hex!($x))
};
}
- match self {
- &SHARDED => h!("ffd37f"),
- &DERELICT => h!("4d4e58"),
- &CRUX => h!("f25555"),
- &MALIS => h!("a27ce5"),
- &GREEN => h!("54d67d"),
- &BLUE => h!("6c87fd"),
- &NEOPLASTIC => h!("e05438"),
+ match *self {
+ SHARDED => h!("ffd37f"),
+ DERELICT => h!("4d4e58"),
+ CRUX => h!("f25555"),
+ MALIS => h!("a27ce5"),
+ GREEN => h!("54d67d"),
+ BLUE => h!("6c87fd"),
+ NEOPLASTIC => h!("e05438"),
_ => h!("a9a9a9"),
}
}
diff --git a/src/unit/mod.rs b/src/unit/mod.rs
index c063d2a..603b376 100644
--- a/src/unit/mod.rs
+++ b/src/unit/mod.rs
@@ -1,3 +1,6 @@
+//! units
+//!
+//! [source](https://github.com/Anuken/Mindustry/blob/master/core/src/mindustry/content/UnitTypes.java)
use crate::content::content_enum;
content_enum! {
diff --git a/src/utils/image.rs b/src/utils/image.rs
index 867d1fc..de6cf6b 100644
--- a/src/utils/image.rs
+++ b/src/utils/image.rs
@@ -1,27 +1,71 @@
+use fast_image_resize as fr;
use image::{Rgb, Rgba, RgbaImage};
+use std::num::NonZeroU32;
+pub trait ImageUtils {
+ fn tint(&mut self, color: Rgb<u8>) -> &mut Self;
-pub fn tint(image: &mut RgbaImage, color: Rgb<u8>) {
- let [tr, tg, tb] = [
- color[0] as f32 / 255.0,
- color[1] as f32 / 255.0,
- color[2] as f32 / 255.0,
- ];
- for Rgba([r, g, b, _]) in image.pixels_mut() {
- *r = (*r as f32 * tr) as u8;
- *g = (*g as f32 * tg) as u8;
- *b = (*b as f32 * tb) as u8;
- }
+ fn repeat(&mut self, with: &RgbaImage) -> &mut Self;
+
+ fn overlay(&mut self, with: &RgbaImage, x: u32, y: u32) -> &mut Self;
+
+ unsafe fn scale(self, to: u32) -> Self;
}
-pub fn repeat(to: &mut RgbaImage, from: &RgbaImage) {
- for x in 0..(to.width() / from.width()) {
- for y in 0..(to.height() / from.height()) {
- image::imageops::overlay(
- to,
- from,
- (x * from.width()).into(),
- (y * from.height()).into(),
- );
+impl ImageUtils for RgbaImage {
+ fn tint(&mut self, color: Rgb<u8>) -> &mut Self {
+ let [tr, tg, tb] = [
+ color[0] as f32 / 255.0,
+ color[1] as f32 / 255.0,
+ color[2] as f32 / 255.0,
+ ];
+ for Rgba([r, g, b, _]) in self.pixels_mut() {
+ *r = (*r as f32 * tr) as u8;
+ *g = (*g as f32 * tg) as u8;
+ *b = (*b as f32 * tb) as u8;
+ }
+ self
+ }
+
+ fn repeat(&mut self, with: &RgbaImage) -> &mut Self {
+ for x in 0..(self.width() / with.width()) {
+ for y in 0..(self.height() / with.height()) {
+ self.overlay(with, x * with.width(), y * with.height());
+ }
}
+ self
+ }
+
+ fn overlay(&mut self, with: &RgbaImage, x: u32, y: u32) -> &mut Self {
+ for j in 0..with.height() {
+ for i in 0..with.width() {
+ let get = with.get_pixel(i, j);
+ if get[3] > 5 {
+ self.put_pixel(i + x, j + y, *get);
+ }
+ }
+ }
+ self
+ }
+
+ /// scales a image
+ ///
+ /// SAFETY: to and width and height cannot be 0.
+ unsafe fn scale(self, to: u32) -> Self {
+ debug_assert_ne!(to, 0);
+ debug_assert_ne!(self.width(), 0);
+ debug_assert_ne!(self.height(), 0);
+ let to = NonZeroU32::new_unchecked(to);
+ let src = fr::Image::from_vec_u8(
+ NonZeroU32::new_unchecked(self.width()),
+ NonZeroU32::new_unchecked(self.height()),
+ self.into_vec(),
+ fr::PixelType::U8x4,
+ )
+ .unwrap();
+ let mut dst = fr::Image::new(to, to, fr::PixelType::U8x4);
+ fr::Resizer::new(fr::ResizeAlg::Nearest)
+ .resize(&src.view(), &mut dst.view_mut())
+ .unwrap();
+ RgbaImage::from_raw(to.get(), to.get(), dst.into_vec()).unwrap()
}
}
diff --git a/src/utils/mod.rs b/src/utils/mod.rs
index 14995d4..4d12b15 100644
--- a/src/utils/mod.rs
+++ b/src/utils/mod.rs
@@ -1 +1,2 @@
pub mod image;
+pub use self::image::ImageUtils;