diff --git a/aurorastation.dme b/aurorastation.dme index 3e4435d371a..ca06dca764e 100644 --- a/aurorastation.dme +++ b/aurorastation.dme @@ -2099,6 +2099,7 @@ #include "code\modules\effects\map_effects\marker\airlock_helper.dm" #include "code\modules\effects\map_effects\marker\airlock_shuttle.dm" #include "code\modules\effects\map_effects\marker\large_tank.dm" +#include "code\modules\effects\map_effects\marker\mapmanip.dm" #include "code\modules\effects\maze_generation\maze_generator.dm" #include "code\modules\effects\maze_generation\maze_generator_blockwise.dm" #include "code\modules\effects\maze_generation\maze_helper_atoms.dm" diff --git a/bapi.dll b/bapi.dll index 2e781de7658..cd321f3dede 100644 Binary files a/bapi.dll and b/bapi.dll differ diff --git a/code/modules/effects/map_effects/marker/mapmanip.dm b/code/modules/effects/map_effects/marker/mapmanip.dm new file mode 100644 index 00000000000..02e70d9447a --- /dev/null +++ b/code/modules/effects/map_effects/marker/mapmanip.dm @@ -0,0 +1,14 @@ + +/obj/effect/map_effect/marker/mapmanip/submap/extract + name = "mapmanip marker, extract submap" + icon = 'icons/effects/map_effects_96x96.dmi' + icon_state = "mapmanip_extract" + pixel_x = -32 + pixel_y = -32 + +/obj/effect/map_effect/marker/mapmanip/submap/insert + name = "mapmanip marker, insert submap" + icon = 'icons/effects/map_effects_96x96.dmi' + icon_state = "mapmanip_insert" + pixel_x = -32 + pixel_y = -32 diff --git a/html/changelogs/DreamySkrell-bapi-mapmanip-insert-extract.yml b/html/changelogs/DreamySkrell-bapi-mapmanip-insert-extract.yml new file mode 100644 index 00000000000..d389bf2adcf --- /dev/null +++ b/html/changelogs/DreamySkrell-bapi-mapmanip-insert-extract.yml @@ -0,0 +1,7 @@ + +author: ChangeMe + +delete-after: True + +changes: + - rscadd: "BAPI - mapmanip insert/extract." diff --git a/icons/effects/map_effects.dmi b/icons/effects/map_effects.dmi index 0c7b0fc8a1c..6a4123d9b95 100644 Binary files a/icons/effects/map_effects.dmi and b/icons/effects/map_effects.dmi differ diff --git a/icons/effects/map_effects_96x96.dmi b/icons/effects/map_effects_96x96.dmi index aa74dfb56a9..88a300fdcb0 100644 Binary files a/icons/effects/map_effects_96x96.dmi and b/icons/effects/map_effects_96x96.dmi differ diff --git a/maps/sccv_horizon/code/sccv_horizon.dm b/maps/sccv_horizon/code/sccv_horizon.dm index c08ac4da642..345cca8f02b 100644 --- a/maps/sccv_horizon/code/sccv_horizon.dm +++ b/maps/sccv_horizon/code/sccv_horizon.dm @@ -193,3 +193,9 @@ for(var/obj/machinery/computer/holodeck_control/holo in GLOB.holodeck_controls) if(!holo.active) holo.load_random_program() + +/obj/effect/map_effect/marker/mapmanip/submap/extract/sccv_horizon/ops_warehouse_small_storage + name = "Ops Warehouse, Small Storage" + +/obj/effect/map_effect/marker/mapmanip/submap/insert/sccv_horizon/ops_warehouse_small_storage + name = "Ops Warehouse, Small Storage" diff --git a/maps/sccv_horizon/sccv_horizon-1_deck_1.dmm b/maps/sccv_horizon/sccv_horizon-1_deck_1.dmm index b69251a9bf2..ba712bd3b25 100644 --- a/maps/sccv_horizon/sccv_horizon-1_deck_1.dmm +++ b/maps/sccv_horizon/sccv_horizon-1_deck_1.dmm @@ -1117,6 +1117,7 @@ /area/engineering/atmos/propulsion) "aQN" = ( /obj/machinery/firealarm/north, +/obj/structure/cargo_receptacle/horizon, /turf/simulated/floor/tiled, /area/operations/storage) "aRG" = ( @@ -2871,15 +2872,6 @@ }, /turf/simulated/floor/reinforced, /area/rnd/test_range) -"ccU" = ( -/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ - dir = 5 - }, -/obj/machinery/atmospherics/pipe/simple/hidden/supply{ - dir = 6 - }, -/turf/simulated/floor/tiled, -/area/operations/storage) "ccZ" = ( /obj/item/material/shard{ icon_state = "small" @@ -12495,10 +12487,6 @@ /obj/structure/disposalpipe/segment, /turf/simulated/floor/plating, /area/maintenance/operations) -"iJJ" = ( -/obj/machinery/atmospherics/unary/vent_scrubber/on, -/turf/simulated/floor/tiled, -/area/operations/storage) "iJM" = ( /obj/machinery/door/blast/shutters/open{ dir = 8; @@ -15222,12 +15210,6 @@ /obj/structure/extinguisher_cabinet/west, /turf/simulated/floor/tiled, /area/hangar/canary) -"kAn" = ( -/obj/machinery/atmospherics/unary/vent_pump/on{ - dir = 1 - }, -/turf/simulated/floor/tiled, -/area/operations/storage) "kAG" = ( /obj/machinery/portable_atmospherics/canister/carbon_dioxide, /obj/machinery/door/window/northleft{ @@ -19773,10 +19755,6 @@ }, /turf/simulated/floor/plating, /area/engineering/atmos/air) -"nzG" = ( -/obj/machinery/firealarm/south, -/turf/simulated/floor/tiled, -/area/operations/storage) "nAn" = ( /obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ dir = 4 @@ -21059,8 +21037,8 @@ /turf/simulated/floor/tiled/dark, /area/hangar/auxiliary) "owJ" = ( -/obj/structure/cargo_receptacle/horizon, -/turf/simulated/floor/tiled, +/obj/effect/map_effect/marker/mapmanip/submap/insert/sccv_horizon/ops_warehouse_small_storage, +/turf/template_noop, /area/operations/storage) "owM" = ( /obj/structure/shuttle_part/scc/research{ @@ -21835,13 +21813,6 @@ }, /turf/simulated/floor/tiled, /area/rnd/xenoarch_atrium) -"pcv" = ( -/obj/machinery/camera/network/supply{ - c_tag = "Operations - Warehouse Starboard Aft Storage Room"; - dir = 4 - }, -/turf/simulated/floor/tiled, -/area/operations/storage) "pcw" = ( /obj/effect/floor_decal/industrial/outline/yellow, /obj/machinery/portable_atmospherics/canister/oxygen, @@ -30375,9 +30346,7 @@ /turf/simulated/floor/plating, /area/shuttle/intrepid/cockpit) "uTy" = ( -/obj/effect/floor_decal/industrial/outline/yellow, -/obj/machinery/alarm/west, -/turf/simulated/floor/tiled, +/turf/template_noop, /area/operations/storage) "uTz" = ( /obj/machinery/atmospherics/pipe/simple/hidden/supply, @@ -45638,11 +45607,11 @@ hdK bmx fif qdE -tiV uTy -pcv -giX -tiV +uTy +uTy +uTy +owJ qdE tiV qQa @@ -45841,11 +45810,11 @@ lVt vtn fif qdE -owJ -iJJ -ccU -kAn -nzG +uTy +uTy +uTy +uTy +uTy qdE aQN nCV @@ -46044,11 +46013,11 @@ fif uCB fif qdE -iQI -giX -sWr -giX -iQI +uTy +uTy +uTy +uTy +uTy qdE iQI giX diff --git a/maps/sccv_horizon/sccv_horizon-1_deck_1.jsonc b/maps/sccv_horizon/sccv_horizon-1_deck_1.jsonc new file mode 100644 index 00000000000..2249f10ecee --- /dev/null +++ b/maps/sccv_horizon/sccv_horizon-1_deck_1.jsonc @@ -0,0 +1,10 @@ +[ + { + "type": "InsertExtract", + "submap_size_x": 3, + "submap_size_y": 5, + "submaps_dmm": "submaps/ops_warehouse_small_storage.dmm", + "marker_extract": "/obj/effect/map_effect/marker/mapmanip/submap/extract/sccv_horizon/ops_warehouse_small_storage", + "marker_insert": "/obj/effect/map_effect/marker/mapmanip/submap/insert/sccv_horizon/ops_warehouse_small_storage" + } +] diff --git a/maps/sccv_horizon/submaps/ops_warehouse_small_storage.dmm b/maps/sccv_horizon/submaps/ops_warehouse_small_storage.dmm new file mode 100644 index 00000000000..2fd7592ace5 --- /dev/null +++ b/maps/sccv_horizon/submaps/ops_warehouse_small_storage.dmm @@ -0,0 +1,822 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/obj/machinery/atmospherics/unary/vent_scrubber/on, +/turf/simulated/floor/tiled, +/area/operations/storage) +"b" = ( +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/machinery/light/small{ + dir = 4 + }, +/turf/simulated/floor/tiled, +/area/operations/storage) +"c" = ( +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/machinery/light/small{ + dir = 4 + }, +/obj/structure/closet/crate, +/turf/simulated/floor/tiled, +/area/operations/storage) +"d" = ( +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/machinery/alarm/west, +/obj/item/stack/material/steel/full, +/obj/structure/table/rack, +/turf/simulated/floor/tiled, +/area/operations/storage) +"f" = ( +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/structure/table/rack, +/obj/random/tech_supply, +/obj/random/bomb_supply, +/turf/simulated/floor/tiled, +/area/operations/storage) +"g" = ( +/turf/simulated/floor/tiled, +/area/operations/storage) +"h" = ( +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/structure/largecrate, +/turf/simulated/floor/tiled, +/area/operations/storage) +"i" = ( +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/machinery/alarm/west, +/turf/simulated/floor/tiled, +/area/operations/storage) +"j" = ( +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/machinery/light/small{ + dir = 4 + }, +/obj/random/junk, +/turf/simulated/floor/tiled, +/area/operations/storage) +"k" = ( +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/machinery/alarm/west, +/obj/structure/closet/crate, +/turf/simulated/floor/tiled, +/area/operations/storage) +"l" = ( +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/machinery/light/small{ + dir = 8 + }, +/obj/effect/map_effect/marker/mapmanip/submap/extract/sccv_horizon/ops_warehouse_small_storage, +/obj/structure/trash_pile, +/turf/simulated/floor/tiled, +/area/operations/storage) +"m" = ( +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/machinery/light/small{ + dir = 8 + }, +/obj/effect/map_effect/marker/mapmanip/submap/extract/sccv_horizon/ops_warehouse_small_storage, +/obj/structure/table/rack, +/obj/random/tech_supply, +/obj/random/tool, +/obj/random/loot, +/turf/simulated/floor/tiled, +/area/operations/storage) +"n" = ( +/obj/machinery/firealarm/south, +/turf/simulated/floor/tiled, +/area/operations/storage) +"p" = ( +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/machinery/light/small{ + dir = 8 + }, +/obj/structure/closet/crate/loot, +/turf/simulated/floor/tiled, +/area/operations/storage) +"q" = ( +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/structure/trash_pile, +/turf/simulated/floor/tiled, +/area/operations/storage) +"u" = ( +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/machinery/light/small{ + dir = 4 + }, +/obj/item/stack/material/plastic/full, +/obj/structure/table/rack, +/turf/simulated/floor/tiled, +/area/operations/storage) +"v" = ( +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/machinery/light/small{ + dir = 8 + }, +/obj/effect/map_effect/marker/mapmanip/submap/extract/sccv_horizon/ops_warehouse_small_storage, +/obj/item/stack/material/cardboard/full, +/obj/structure/table/rack, +/turf/simulated/floor/tiled, +/area/operations/storage) +"w" = ( +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/machinery/light/small{ + dir = 8 + }, +/obj/effect/map_effect/marker/mapmanip/submap/extract/sccv_horizon/ops_warehouse_small_storage, +/turf/simulated/floor/tiled, +/area/operations/storage) +"x" = ( +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/machinery/light/small{ + dir = 4 + }, +/obj/item/stack/material/steel/full, +/obj/structure/table/rack, +/turf/simulated/floor/tiled, +/area/operations/storage) +"y" = ( +/obj/effect/floor_decal/industrial/outline/yellow, +/turf/simulated/floor/tiled, +/area/operations/storage) +"A" = ( +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/item/stack/material/wood/full, +/obj/structure/table/rack, +/turf/simulated/floor/tiled, +/area/operations/storage) +"B" = ( +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/machinery/light/small{ + dir = 4 + }, +/obj/structure/trash_pile, +/turf/simulated/floor/tiled, +/area/operations/storage) +"C" = ( +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/structure/closet/crate/freezer/rations, +/turf/simulated/floor/tiled, +/area/operations/storage) +"E" = ( +/turf/template_noop, +/area/space) +"F" = ( +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/structure/closet/crate/tool, +/obj/random/toolbox, +/obj/random/tool, +/obj/random/tool, +/obj/random/tool, +/obj/random/tech_supply, +/obj/random/tech_supply, +/obj/random/tech_supply, +/turf/simulated/floor/tiled, +/area/operations/storage) +"G" = ( +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/machinery/light/small{ + dir = 8 + }, +/turf/simulated/floor/tiled, +/area/operations/storage) +"I" = ( +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/structure/closet/crate, +/turf/simulated/floor/tiled, +/area/operations/storage) +"J" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 4 + }, +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 4 + }, +/turf/simulated/floor/tiled, +/area/operations/storage) +"K" = ( +/obj/machinery/camera/network/supply{ + c_tag = "Operations - Warehouse Starboard Aft Storage Room"; + dir = 4 + }, +/turf/simulated/floor/tiled, +/area/operations/storage) +"L" = ( +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/machinery/light/small{ + dir = 4 + }, +/obj/structure/table/rack, +/obj/random/tech_supply, +/obj/random/loot, +/turf/simulated/floor/tiled, +/area/operations/storage) +"N" = ( +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/machinery/light/small{ + dir = 4 + }, +/obj/structure/table/rack, +/obj/random/tech_supply, +/obj/random/tech_supply, +/turf/simulated/floor/tiled, +/area/operations/storage) +"O" = ( +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/machinery/light/small{ + dir = 8 + }, +/obj/structure/closet/crate/medical, +/obj/random/firstaid, +/obj/random/medical, +/obj/random/medical, +/obj/random/medical, +/obj/random/medical, +/obj/random/medical, +/obj/random/medical, +/turf/simulated/floor/tiled, +/area/operations/storage) +"P" = ( +/obj/machinery/atmospherics/pipe/simple/hidden/scrubbers{ + dir = 5 + }, +/obj/machinery/atmospherics/pipe/simple/hidden/supply{ + dir = 6 + }, +/turf/simulated/floor/tiled, +/area/operations/storage) +"Q" = ( +/obj/machinery/atmospherics/unary/vent_pump/on{ + dir = 1 + }, +/turf/simulated/floor/tiled, +/area/operations/storage) +"R" = ( +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/machinery/light/small{ + dir = 4 + }, +/obj/structure/closet/crate/loot, +/turf/simulated/floor/tiled, +/area/operations/storage) +"S" = ( +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/structure/closet/crate/trashcart, +/obj/random/junk, +/obj/random/junk, +/obj/random/junk, +/turf/simulated/floor/tiled, +/area/operations/storage) +"T" = ( +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/structure/table/rack, +/obj/random/tech_supply, +/obj/random/tool, +/turf/simulated/floor/tiled, +/area/operations/storage) +"U" = ( +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/machinery/alarm/west, +/obj/structure/trash_pile, +/turf/simulated/floor/tiled, +/area/operations/storage) +"Y" = ( +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/machinery/light/small{ + dir = 4 + }, +/obj/structure/largecrate, +/turf/simulated/floor/tiled, +/area/operations/storage) +"Z" = ( +/obj/effect/floor_decal/industrial/outline/yellow, +/obj/machinery/light/small{ + dir = 8 + }, +/obj/structure/trash_pile, +/turf/simulated/floor/tiled, +/area/operations/storage) + +(1,1,1) = {" +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +"} +(2,1,1) = {" +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +"} +(3,1,1) = {" +E +E +E +G +U +K +y +l +E +E +E +G +i +K +y +w +E +E +E +"} +(4,1,1) = {" +E +E +E +g +a +P +Q +n +E +E +E +g +a +P +Q +n +E +E +E +"} +(5,1,1) = {" +E +E +E +B +y +J +q +R +E +E +E +b +y +J +y +b +E +E +E +"} +(6,1,1) = {" +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +"} +(7,1,1) = {" +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +"} +(8,1,1) = {" +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +"} +(9,1,1) = {" +E +E +E +G +d +K +A +v +E +E +E +p +k +K +I +w +E +E +E +"} +(10,1,1) = {" +E +E +E +g +a +P +Q +n +E +E +E +g +a +P +Q +n +E +E +E +"} +(11,1,1) = {" +E +E +E +u +y +J +y +x +E +E +E +c +y +J +y +c +E +E +E +"} +(12,1,1) = {" +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +"} +(13,1,1) = {" +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +"} +(14,1,1) = {" +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +"} +(15,1,1) = {" +E +E +E +G +i +K +f +m +E +E +E +G +i +K +h +w +E +E +E +"} +(16,1,1) = {" +E +E +E +g +a +P +Q +n +E +E +E +g +a +P +Q +n +E +E +E +"} +(17,1,1) = {" +E +E +E +L +T +J +y +b +E +E +E +Y +h +J +y +b +E +E +E +"} +(18,1,1) = {" +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +"} +(19,1,1) = {" +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +"} +(20,1,1) = {" +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +"} +(21,1,1) = {" +E +E +E +Z +i +K +S +w +E +E +E +O +i +K +C +w +E +E +E +"} +(22,1,1) = {" +E +E +E +g +a +P +Q +n +E +E +E +g +a +P +Q +n +E +E +E +"} +(23,1,1) = {" +E +E +E +b +S +J +y +j +E +E +E +N +F +J +y +b +E +E +E +"} +(24,1,1) = {" +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +"} +(25,1,1) = {" +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +E +"} diff --git a/rust/bapi/Cargo.lock b/rust/bapi/Cargo.lock index b471b35fd13..c5a361e79dd 100644 --- a/rust/bapi/Cargo.lock +++ b/rust/bapi/Cargo.lock @@ -45,7 +45,7 @@ dependencies = [ "attribute-derive-macro", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -61,7 +61,7 @@ dependencies = [ "proc-macro2", "quote", "quote-use", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -75,8 +75,17 @@ name = "bapi" version = "1.0.0" dependencies = [ "byondapi", + "diff", "dmm-tools", "eyre", + "fxhash", + "grid", + "itertools 0.10.5", + "linked-hash-map", + "rand", + "serde", + "serde_json", + "walkdir", ] [[package]] @@ -88,7 +97,7 @@ dependencies = [ "bitflags 2.5.0", "cexpr", "clang-sys", - "itertools", + "itertools 0.12.1", "lazy_static", "lazycell", "log", @@ -98,7 +107,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.60", + "syn 2.0.66", "which", ] @@ -151,7 +160,7 @@ source = "git+https://github.com/spacestation13/byondapi-rs?rev=5afdd9863a0fc1d8 dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -168,24 +177,30 @@ dependencies = [ [[package]] name = "bytemuck" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" +checksum = "78834c15cb5d5efe3452d58b1e8ba890dd62d21907f867f383358198e56ebca5" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60" +checksum = "1ee891b04274a59bd38b412188e24b849617b2e45a0fd8d057deb63e7403761b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "cexpr" version = "0.6.0" @@ -203,9 +218,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clang-sys" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ "glob", "libc", @@ -226,9 +241,9 @@ checksum = "3776b2bcc4e914db501bb9be9572dd706e344b9eb8f882894f3daa651d281381" [[package]] name = "crc32fast" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] @@ -252,9 +267,15 @@ checksum = "62d671cc41a825ebabc75757b62d3d168c577f9149b2d49ece1dad1f72119d25" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + [[package]] name = "dmm-tools" version = "0.1.0" @@ -306,9 +327,9 @@ dependencies = [ [[package]] name = "either" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" [[package]] name = "equivalent" @@ -318,9 +339,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" dependencies = [ "libc", "windows-sys", @@ -355,6 +376,15 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] + [[package]] name = "get-size" version = "0.1.4" @@ -369,7 +399,7 @@ checksum = "13a1bcfb855c1f340d5913ab542e36f25a1c56f57de79022928297632435dec2" dependencies = [ "attribute-derive", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -389,6 +419,12 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "grid" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eec1c01eb1de97451ee0d60de7d81cf1e72aabefb021616027f3d1c3ec1c723c" + [[package]] name = "hashbrown" version = "0.12.3" @@ -471,6 +507,15 @@ version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f958d3d68f4167080a18141e10381e7634563984a537f2a49a30fd8e53ac5767" +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + [[package]] name = "itertools" version = "0.12.1" @@ -480,6 +525,12 @@ dependencies = [ "either", ] +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + [[package]] name = "lazy_static" version = "1.4.0" @@ -494,9 +545,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.154" +version = "0.2.155" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libloading" @@ -509,10 +560,16 @@ dependencies = [ ] [[package]] -name = "linux-raw-sys" -version = "0.4.13" +name = "linked-hash-map" +version = "0.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" + +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lodepng" @@ -557,9 +614,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" dependencies = [ "adler", ] @@ -589,9 +646,9 @@ dependencies = [ [[package]] name = "num-complex" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23c6602fda94a57c990fe0df199a035d83576b496aa29f4e634a8ac6004e68a6" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" dependencies = [ "num-traits", ] @@ -632,7 +689,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -680,7 +737,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -700,12 +757,12 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "prettyplease" -version = "0.2.19" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ac2cf0f2e4f42b49f5ffd07dae8d746508ef7526c13940e5f524012ae6c6550" +checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -754,9 +811,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.84" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "ec96c6a92621310b51366f1e28d05ef11489516e93be030060e5fc12024a49d6" dependencies = [ "unicode-ident", ] @@ -778,7 +835,7 @@ checksum = "a7b5abe3fe82fdeeb93f44d66a7b444dedf2e4827defb0a8e69c437b2de2ef94" dependencies = [ "quote", "quote-use-macros", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -790,7 +847,7 @@ dependencies = [ "derive-where", "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] [[package]] @@ -895,6 +952,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + [[package]] name = "same-file" version = "1.0.6" @@ -906,28 +969,39 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.200" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddc6f9cc94d67c0e21aaf7eda3a010fd3af78ebf6e096aa6e2e13c79749cce4f" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.200" +version = "1.0.203" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "856f046b9400cee3c8c94ed572ecdb752444c24528c035cd35882aad6f492bcb" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", +] + +[[package]] +name = "serde_json" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +dependencies = [ + "itoa", + "ryu", + "serde", ] [[package]] @@ -961,9 +1035,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.60" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", @@ -990,9 +1064,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" [[package]] name = "toml_edit" @@ -1153,5 +1227,5 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.60", + "syn 2.0.66", ] diff --git a/rust/bapi/Cargo.toml b/rust/bapi/Cargo.toml index 24e60549bcb..0eff71c1413 100644 --- a/rust/bapi/Cargo.toml +++ b/rust/bapi/Cargo.toml @@ -11,9 +11,28 @@ crate-type = ["cdylib"] # ------------------------------------------------------- [dependencies] + # byondapi byondapi = { git = "https://github.com/spacestation13/byondapi-rs", rev = "5afdd9863a0fc1d8d1d42dd2128c9978f54e12ca" } # error-handling eyre = "0.6.12" # spacemandmm, also used by strongmandmm dmmtools = { git = "https://github.com/SpaceManiac/SpacemanDMM", rev = "6c5a751516ae0e8add4b2aa4388a1e84e96e7082", package = "dmm-tools" } +# diffs between two strings/texts/files, used in tests +diff = "0.1" +# general utility lib for iterator operations +itertools = "0.10.5" +# hash map that preserves insertion order +linked-hash-map = "0.5.6" +# fast hashing function +fxhash = "0.2.1" +# grid type, basically just a list-of-lists with a nicer interface +grid = "0.10" +# generating random numbers +rand = "^0.8" +# interface for serialization +serde = { version = "1.0", features = ["derive"] } +# json implementation for serde +serde_json = { version = "1.0" } +# walking through a dir recursively +walkdir = "2.5.0" diff --git a/rust/bapi/README.md b/rust/bapi/README.md index 61de6a99370..b20aa087f40 100644 --- a/rust/bapi/README.md +++ b/rust/bapi/README.md @@ -22,11 +22,12 @@ Safety related stuff: Panics are unrecoverable, so this is the best we can do in this case. Panics should not really happen though, except in a "fatal error and literally cannot proceed further" situation. - Unhandled errors (like `foo()?;`) call stack trace proc and return null from the function. + Errors should be propagated up, using the `eyre` library, with every function returning `eyre::Result`. - Some unsafety may be required, like to parse args in `_ffi` functions, but should not be present any where else. And if for whatever reason it is needed, it should be documented well. - All rust code (generally, outside of `_ffi`) should be nice, safe, and idiomatic. -# Other stuff +# Compiling and testing Command to compile bapi. The resulting `.dll` should be in `./rust/bapi/target/i686-pc-windows-msvc/release/...`. The server should try to find and use the `.dll` in that location, otherwise use `./bapi.dll` in main dir. diff --git a/rust/bapi/src/lib.rs b/rust/bapi/src/lib.rs index 27616b573ca..df871377da5 100644 --- a/rust/bapi/src/lib.rs +++ b/rust/bapi/src/lib.rs @@ -1,7 +1,11 @@ +// internal modules +mod mapmanip; + use byondapi::prelude::*; +use eyre::{Context, ContextCompat}; /// Call stack trace dm method with message. -fn dm_call_stack_trace(msg: String) { +pub(crate) fn dm_call_stack_trace(msg: String) { let msg = byondapi::value::ByondValue::try_from(msg).unwrap(); byondapi::global_call::call_global_id( { @@ -23,20 +27,13 @@ fn setup_panic_handler() { })) } -/// Turns spacemandmm map object to string. -pub fn map_to_string(map: &dmmtools::dmm::Map) -> eyre::Result { - let mut v = vec![]; - map.to_writer(&mut v)?; - let s = String::from_utf8(v)?; - Ok(s) -} - /// #[no_mangle] pub unsafe extern "C" fn read_dmm_file_ffi( argc: byondapi::sys::u4c, argv: *mut byondapi::value::ByondValue, ) -> byondapi::value::ByondValue { + setup_panic_handler(); let args = unsafe { ::byondapi::parse_args(argc, argv) }; match read_dmm_file(args.get(0).map(ByondValue::clone).unwrap_or_default()) { Ok(val) => val, @@ -51,18 +48,48 @@ pub unsafe extern "C" fn read_dmm_file_ffi( fn read_dmm_file(path: ByondValue) -> eyre::Result { setup_panic_handler(); - let path: String = path.get_string()?; - let path: std::path::PathBuf = path.try_into()?; + let path: String = path + .get_string() + .wrap_err(format!("path arg is not a string: {:?}", path))?; + let path: std::path::PathBuf = path + .clone() + .try_into() + .wrap_err(format!("path arg is not a valid file path: {}", path))?; - // some checks, just return null if path is bad for whatever reason + // just return null if path is bad for whatever reason if !path.is_file() || !path.exists() { return Ok(ByondValue::null()); } // read file and parse with spacemandmm - let dmm = dmmtools::dmm::Map::from_file(&path)?; + let mut dmm = dmmtools::dmm::Map::from_file(&path).wrap_err(format!( + "spacemandmm parsing error; dmm file path: {}; see error from spacemandmm below for more information", + path.display() + ))?; - // return the map converted to string - let str = map_to_string(&dmm)?; - Ok(ByondValue::new_str(str)?) + // do mapmanip if defined for this dmm + let path_mapmanip_config = { + let mut p = path.clone(); + p.set_extension("jsonc"); + p + }; + if path_mapmanip_config.exists() { + // get path for dir of this dmm + let path_dir = path.parent().wrap_err("no parent")?; + // parse config + let config = crate::mapmanip::mapmanip_config_parse(&path_mapmanip_config).wrap_err( + format!("config parse fail; path: {:?}", path_mapmanip_config), + )?; + // do actual map manipulation + dmm = crate::mapmanip::mapmanip(path_dir, dmm, &config).wrap_err("mapmanip fail")?; + } + + // convert the map back to a string + let dmm = crate::mapmanip::core::map_to_string(&dmm).wrap_err(format!( + "error in converting map back to string; dmm file path:dd {}", + path.display() + ))?; + + // and return it + Ok(ByondValue::new_str(dmm)?) } diff --git a/rust/bapi/src/mapmanip/.gitignore b/rust/bapi/src/mapmanip/.gitignore new file mode 100644 index 00000000000..99912a86714 --- /dev/null +++ b/rust/bapi/src/mapmanip/.gitignore @@ -0,0 +1 @@ +test-out/* diff --git a/rust/bapi/src/mapmanip/core/map_to_string.rs b/rust/bapi/src/mapmanip/core/map_to_string.rs new file mode 100644 index 00000000000..f931d97ac20 --- /dev/null +++ b/rust/bapi/src/mapmanip/core/map_to_string.rs @@ -0,0 +1,7 @@ +/// Turns spacemandmm map object to string. +pub fn map_to_string(map: &dmmtools::dmm::Map) -> eyre::Result { + let mut v = vec![]; + map.to_writer(&mut v)?; + let s = String::from_utf8(v)?; + Ok(s) +} diff --git a/rust/bapi/src/mapmanip/core/mod.rs b/rust/bapi/src/mapmanip/core/mod.rs new file mode 100644 index 00000000000..96d469e1e1f --- /dev/null +++ b/rust/bapi/src/mapmanip/core/mod.rs @@ -0,0 +1,36 @@ +pub mod to_dict_map; +pub use to_dict_map::to_dict_map; +pub mod to_grid_map; +pub use to_grid_map::to_grid_map; + +pub mod map_to_string; +pub use map_to_string::map_to_string; + +use dmmtools::dmm; + +/// +#[derive(Clone, Debug)] +pub struct Tile { + /// + pub key_suggestion: dmm::Key, + /// + pub prefabs: Vec, +} + +/// This is analogous to `dmmtools::dmm::Map`, but instead of being structured like dmm maps are, +/// where they have a dictionary of keys-to-prefabs and a separate grid of keys, +/// this is only a direct coord-to-prefab grid. +/// It is not memory efficient, but it allows for much greater flexibility of manipulation. +#[derive(Clone, Debug)] +pub struct GridMap { + /// + pub size: dmm::Coord3, + /// + pub grid: std::collections::BTreeMap, +} + +impl GridMap { + pub fn from_file(path: &std::path::Path) -> Option { + Some(to_grid_map(&dmm::Map::from_file(&path).ok()?)) + } +} diff --git a/rust/bapi/src/mapmanip/core/to_dict_map.rs b/rust/bapi/src/mapmanip/core/to_dict_map.rs new file mode 100644 index 00000000000..aa1698129cd --- /dev/null +++ b/rust/bapi/src/mapmanip/core/to_dict_map.rs @@ -0,0 +1,69 @@ +use crate::mapmanip::GridMap; +use dmmtools::dmm::{self, Coord2}; +use eyre::ContextCompat; +use fxhash::FxHashMap; +use std::collections::BTreeSet; + +fn coord3_to_index(coord: dmm::Coord3, size: dmm::Coord3) -> (usize, usize, usize) { + ( + coord.z as usize - 1, + (size.y - coord.y) as usize, + coord.x as usize - 1, + ) +} + +fn int_to_key(i: u16) -> dmm::Key { + // Unsafe is used here to convert basic int to key type, as `dmm::Key` interior var is private. + // This is "safe", as key will always be `u16` to maintain compability with the `dmm` format. + // Could be at some point be made safe when the key type is made public in the `dmm_tools` crate. + unsafe { std::mem::transmute::(i) } +} + +pub fn to_dict_map(grid_map: &GridMap) -> eyre::Result { + let mut dict_map = dmm::Map::new( + grid_map.size.x as usize, + grid_map.size.y as usize, + grid_map.size.z as usize, + "".to_string(), + "".to_string(), + ); + dict_map.dictionary.clear(); + + let mut used_dict_keys = BTreeSet::::new(); + + let mut dictionary_reverse = FxHashMap::, dmm::Key>::default(); + + for tile in grid_map.grid.values() { + if !dictionary_reverse.contains_key(&tile.prefabs) { + if used_dict_keys.contains(&tile.key_suggestion) { + let next_free_key = (0..65534) + .map(int_to_key) + .filter(|k| !used_dict_keys.contains(k)) + .next() + .wrap_err(format!("ran out of free keys"))?; + dictionary_reverse.insert(tile.prefabs.clone(), next_free_key); + used_dict_keys.insert(next_free_key); + } else { + dictionary_reverse.insert(tile.prefabs.clone(), tile.key_suggestion.clone()); + used_dict_keys.insert(tile.key_suggestion.clone()); + } + } + } + + for x in 1..(grid_map.size.x + 1) { + for y in 1..(grid_map.size.y + 1) { + let coord = Coord2::new(x, y); + if let Some(tile) = grid_map.grid.get(&coord) { + let key = dictionary_reverse.get(&tile.prefabs).unwrap().clone(); + dict_map.dictionary.insert(key, tile.prefabs.clone()); + dict_map.grid[coord3_to_index(coord.z(1), grid_map.size)] = key; + } else { + panic!(); + } + } + } + + dict_map.adjust_key_length(); + + Ok(dict_map) +} diff --git a/rust/bapi/src/mapmanip/core/to_grid_map.rs b/rust/bapi/src/mapmanip/core/to_grid_map.rs new file mode 100644 index 00000000000..253c2f085d3 --- /dev/null +++ b/rust/bapi/src/mapmanip/core/to_grid_map.rs @@ -0,0 +1,31 @@ +use super::Tile; +use crate::mapmanip::core::GridMap; +use dmmtools::dmm::{self, Coord3}; +use std::ops::Index; + +fn tuple_to_size(xyz: (usize, usize, usize)) -> Coord3 { + Coord3::new(xyz.0 as i32, xyz.1 as i32, xyz.2 as i32) +} + +pub fn to_grid_map(dict_map: &dmm::Map) -> GridMap { + let mut grid_map = GridMap { + size: tuple_to_size(dict_map.dim_xyz()), + grid: Default::default(), + }; + + for x in 1..grid_map.size.x + 1 { + for y in 1..grid_map.size.y + 1 { + let coord = dmm::Coord2::new(x, y); + let key = dict_map.index(coord.z(1)); + let prefabs = dict_map.dictionary[key].clone(); + let tile = Tile { + key_suggestion: *key, + prefabs, + }; + let coord = dmm::Coord2::new(coord.x, coord.y); + grid_map.grid.insert(coord, tile); + } + } + + grid_map +} diff --git a/rust/bapi/src/mapmanip/mod.rs b/rust/bapi/src/mapmanip/mod.rs new file mode 100644 index 00000000000..2997dfe29d8 --- /dev/null +++ b/rust/bapi/src/mapmanip/mod.rs @@ -0,0 +1,95 @@ +pub mod core; +pub mod tools; + +pub use core::GridMap; +use eyre::Context; +use rand::seq::SliceRandom; +use serde::{Deserialize, Serialize}; + +#[cfg(test)] +mod test; + +/// foobar +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(tag = "type")] +pub enum MapManipulation { + InsertExtract { + submap_size_x: i64, + submap_size_y: i64, + submaps_dmm: String, + marker_extract: String, + marker_insert: String, + }, +} + +pub fn mapmanip_config_parse(config_path: &std::path::Path) -> eyre::Result> { + let config = std::fs::read_to_string(config_path) + .wrap_err(format!("mapmanip config read err: {:?}", config_path))?; + let config = serde_json::from_str::>(&config) + .wrap_err(format!("mapmanip config json parse err: {:?}", config_path))?; + Ok(config) +} + +pub fn mapmanip( + map_dir_path: &std::path::Path, + map: dmmtools::dmm::Map, + config: &Vec, +) -> eyre::Result { + // convert to gridmap + let mut map = core::to_grid_map(&map); + + // go through all the manipulations in `.jsonc` config for this `.dmm` + for manipulation in config { + match manipulation { + MapManipulation::InsertExtract { + submap_size_x, + submap_size_y, + submaps_dmm, + marker_extract, + marker_insert, + } => { + let submap_size = dmmtools::dmm::Coord2::new( + (*submap_size_x) + .try_into() + .wrap_err("invalid submap_size_x")?, + (*submap_size_y) + .try_into() + .wrap_err("invalid submap_size_y")?, + ); + + // get the submaps map + let submaps_dmm: std::path::PathBuf = submaps_dmm.try_into().unwrap(); + let submaps_dmm = map_dir_path.join(submaps_dmm); + let submaps_map = GridMap::from_file(&submaps_dmm).unwrap(); + + // find all the extract markers + let mut marker_extract_coords = vec![]; + for (coord, tile) in submaps_map.grid.iter() { + if tile.prefabs.iter().any(|p| p.path == *marker_extract) { + marker_extract_coords.push(*coord); + } + } + + // find all the insert markers + let mut marker_insert_coords = vec![]; + for (coord, tile) in map.grid.iter() { + if tile.prefabs.iter().any(|p| p.path == *marker_insert) { + marker_insert_coords.push(*coord); + } + } + + // do all the extracts-inserts + for insert_coord in marker_insert_coords { + let extract_coord = *marker_extract_coords + .choose(&mut rand::thread_rng()) + .unwrap(); + let extracted = + tools::extract_sub_map(&submaps_map, extract_coord, submap_size); + tools::insert_sub_map(&extracted, insert_coord, &mut map); + } + } + } + } + + Ok(core::to_dict_map(&map).wrap_err("failed on `to_dict_map`")?) +} diff --git a/rust/bapi/src/mapmanip/test-in/_tiny_test_map.dmm b/rust/bapi/src/mapmanip/test-in/_tiny_test_map.dmm new file mode 100644 index 00000000000..48ad3798f2d --- /dev/null +++ b/rust/bapi/src/mapmanip/test-in/_tiny_test_map.dmm @@ -0,0 +1,359 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/turf/simulated/floor/marble, +/area/space) +"b" = ( +/obj/random/mre, +/turf/simulated/floor/shuttle, +/area/space) +"f" = ( +/obj/random/energy, +/turf/simulated/wall, +/area/space) +"j" = ( +/obj/random/energy_antag, +/turf/simulated/wall, +/area/space) +"k" = ( +/obj/random/action_figure, +/obj/random/action_figure, +/turf/simulated/floor/lino/diamond, +/area/space) +"n" = ( +/obj/random/mre, +/turf/simulated/floor/marble, +/area/space) +"o" = ( +/obj/random/mre, +/turf/simulated/wall, +/area/space) +"p" = ( +/obj/random/handgun, +/turf/simulated/floor/marble, +/area/space) +"q" = ( +/obj/random/action_figure, +/turf/simulated/floor/shuttle, +/area/space) +"r" = ( +/obj/random/action_figure, +/obj/aiming_overlay, +/turf/simulated/floor/lino/diamond, +/area/space) +"s" = ( +/obj/random/action_figure, +/turf/simulated/floor/lino/diamond, +/area/space) +"u" = ( +/obj/random/handgun, +/turf/simulated/wall, +/area/space) +"y" = ( +/obj/random/glowstick, +/turf/simulated/floor/marble, +/area/space) +"z" = ( +/obj/random/firstaid, +/turf/simulated/floor/marble, +/area/space) +"B" = ( +/turf/simulated/floor/lino/diamond, +/area/space) +"E" = ( +/turf/simulated/wall, +/area/space) +"F" = ( +/turf/simulated/floor/shuttle, +/area/space) +"G" = ( +/obj/random/finances, +/turf/simulated/wall, +/area/space) +"H" = ( +/obj/random/gift, +/turf/simulated/floor/marble, +/area/space) +"J" = ( +/obj/random/chameleon, +/turf/simulated/floor/shuttle, +/area/space) +"Q" = ( +/obj/aiming_overlay, +/turf/simulated/floor/marble, +/area/space) +"S" = ( +/obj/aiming_overlay, +/turf/simulated/floor/lino/diamond, +/area/space) +"U" = ( +/obj/random/mre, +/turf/simulated/floor/lino/diamond, +/area/space) +"W" = ( +/obj/random/chameleon, +/turf/simulated/floor/lino/diamond, +/area/space) +"Y" = ( +/obj/random/action_figure, +/turf/simulated/floor/marble, +/area/space) +"Z" = ( +/obj/random/chameleon, +/turf/simulated/floor/marble, +/area/space) + +(1,1,1) = {" +E +j +j +E +E +E +E +E +E +E +E +E +G +G +a +"} +(2,1,1) = {" +f +B +B +B +B +B +B +B +B +B +B +B +B +B +z +"} +(3,1,1) = {" +f +B +B +B +B +Q +a +a +a +a +a +a +a +B +z +"} +(4,1,1) = {" +E +B +B +a +a +Q +B +B +B +B +B +B +B +B +a +"} +(5,1,1) = {" +E +B +s +Y +B +S +B +B +B +B +B +s +B +F +a +"} +(6,1,1) = {" +E +s +s +Y +s +S +s +B +B +B +B +s +B +F +a +"} +(7,1,1) = {" +E +B +B +a +B +r +s +k +k +B +B +s +B +F +a +"} +(8,1,1) = {" +E +W +a +a +B +S +B +B +k +k +B +s +F +F +a +"} +(9,1,1) = {" +o +W +n +U +U +S +U +U +U +s +k +U +b +U +n +"} +(10,1,1) = {" +E +W +Z +B +B +B +B +B +B +B +k +B +F +B +a +"} +(11,1,1) = {" +E +B +Z +W +B +B +B +B +B +B +q +F +F +B +a +"} +(12,1,1) = {" +E +B +B +W +W +B +B +F +F +F +F +s +B +B +a +"} +(13,1,1) = {" +u +B +B +B +B +J +J +J +B +B +B +s +B +B +H +"} +(14,1,1) = {" +u +B +B +B +F +F +B +W +W +B +B +B +s +B +H +"} +(15,1,1) = {" +a +p +p +a +a +a +a +a +a +a +a +a +y +y +a +"} diff --git a/rust/bapi/src/mapmanip/test-in/extracted.dmm b/rust/bapi/src/mapmanip/test-in/extracted.dmm new file mode 100644 index 00000000000..4df41ac838e --- /dev/null +++ b/rust/bapi/src/mapmanip/test-in/extracted.dmm @@ -0,0 +1,115 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/turf/simulated/floor/lino/diamond, +/area/space) +"b" = ( +/obj/random/action_figure, +/obj/aiming_overlay, +/turf/simulated/floor/lino/diamond, +/area/space) +"k" = ( +/obj/random/action_figure, +/turf/simulated/floor/lino/diamond, +/area/space) +"p" = ( +/turf/simulated/floor/marble, +/area/space) +"u" = ( +/obj/aiming_overlay, +/turf/simulated/floor/marble, +/area/space) +"G" = ( +/obj/random/mre, +/turf/simulated/floor/lino/diamond, +/area/space) +"H" = ( +/obj/random/chameleon, +/turf/simulated/floor/shuttle, +/area/space) +"L" = ( +/obj/random/action_figure, +/obj/random/action_figure, +/turf/simulated/floor/lino/diamond, +/area/space) +"N" = ( +/turf/simulated/floor/shuttle, +/area/space) +"R" = ( +/obj/random/chameleon, +/turf/simulated/floor/lino/diamond, +/area/space) +"T" = ( +/obj/aiming_overlay, +/turf/simulated/floor/lino/diamond, +/area/space) + +(1,1,1) = {" +p +u +a +a +a +"} +(2,1,1) = {" +a +T +a +a +a +"} +(3,1,1) = {" +k +T +k +a +a +"} +(4,1,1) = {" +a +b +k +L +L +"} +(5,1,1) = {" +a +T +a +a +L +"} +(6,1,1) = {" +G +T +G +G +G +"} +(7,1,1) = {" +a +a +a +a +a +"} +(8,1,1) = {" +a +a +a +a +a +"} +(9,1,1) = {" +R +a +a +N +N +"} +(10,1,1) = {" +a +H +H +H +a +"} diff --git a/rust/bapi/src/mapmanip/test-in/inserted.dmm b/rust/bapi/src/mapmanip/test-in/inserted.dmm new file mode 100644 index 00000000000..dad09b97a86 --- /dev/null +++ b/rust/bapi/src/mapmanip/test-in/inserted.dmm @@ -0,0 +1,355 @@ +//MAP CONVERTED BY dmm2tgm.py THIS HEADER COMMENT PREVENTS RECONVERSION, DO NOT REMOVE +"a" = ( +/turf/simulated/floor/marble, +/area/space) +"b" = ( +/obj/random/mre, +/turf/simulated/floor/shuttle, +/area/space) +"f" = ( +/obj/random/energy, +/turf/simulated/wall, +/area/space) +"j" = ( +/obj/random/energy_antag, +/turf/simulated/wall, +/area/space) +"k" = ( +/obj/random/action_figure, +/obj/random/action_figure, +/turf/simulated/floor/lino/diamond, +/area/space) +"n" = ( +/obj/random/mre, +/turf/simulated/floor/marble, +/area/space) +"o" = ( +/obj/random/mre, +/turf/simulated/wall, +/area/space) +"p" = ( +/obj/random/handgun, +/turf/simulated/floor/marble, +/area/space) +"r" = ( +/obj/random/action_figure, +/obj/aiming_overlay, +/turf/simulated/floor/lino/diamond, +/area/space) +"s" = ( +/obj/random/action_figure, +/turf/simulated/floor/lino/diamond, +/area/space) +"u" = ( +/obj/random/handgun, +/turf/simulated/wall, +/area/space) +"y" = ( +/obj/random/glowstick, +/turf/simulated/floor/marble, +/area/space) +"z" = ( +/obj/random/firstaid, +/turf/simulated/floor/marble, +/area/space) +"B" = ( +/turf/simulated/floor/lino/diamond, +/area/space) +"E" = ( +/turf/simulated/wall, +/area/space) +"F" = ( +/turf/simulated/floor/shuttle, +/area/space) +"G" = ( +/obj/random/finances, +/turf/simulated/wall, +/area/space) +"H" = ( +/obj/random/gift, +/turf/simulated/floor/marble, +/area/space) +"J" = ( +/obj/random/chameleon, +/turf/simulated/floor/shuttle, +/area/space) +"Q" = ( +/obj/aiming_overlay, +/turf/simulated/floor/marble, +/area/space) +"S" = ( +/obj/aiming_overlay, +/turf/simulated/floor/lino/diamond, +/area/space) +"U" = ( +/obj/random/mre, +/turf/simulated/floor/lino/diamond, +/area/space) +"W" = ( +/obj/random/chameleon, +/turf/simulated/floor/lino/diamond, +/area/space) +"Y" = ( +/obj/random/action_figure, +/turf/simulated/floor/marble, +/area/space) +"Z" = ( +/obj/random/chameleon, +/turf/simulated/floor/marble, +/area/space) + +(1,1,1) = {" +E +j +j +E +E +E +E +E +E +E +E +E +G +G +a +"} +(2,1,1) = {" +f +B +B +B +B +B +B +B +B +B +B +B +B +B +z +"} +(3,1,1) = {" +f +B +B +B +B +Q +a +a +a +a +a +a +a +B +z +"} +(4,1,1) = {" +E +B +B +a +a +Q +B +B +B +B +B +B +B +B +a +"} +(5,1,1) = {" +E +B +s +Y +B +S +B +B +B +B +B +s +B +F +a +"} +(6,1,1) = {" +E +s +s +Y +s +S +s +a +Q +B +B +B +B +F +a +"} +(7,1,1) = {" +E +B +B +a +B +r +s +B +S +B +B +B +B +F +a +"} +(8,1,1) = {" +E +W +a +a +B +S +B +s +S +s +B +B +F +F +a +"} +(9,1,1) = {" +o +W +n +U +U +S +U +B +r +s +k +k +b +U +n +"} +(10,1,1) = {" +E +W +Z +B +B +B +B +B +S +B +B +k +F +B +a +"} +(11,1,1) = {" +E +B +Z +W +B +B +B +U +S +U +U +U +F +B +a +"} +(12,1,1) = {" +E +B +B +W +W +B +B +B +B +B +B +B +B +B +a +"} +(13,1,1) = {" +u +B +B +B +B +J +J +B +B +B +B +B +B +B +H +"} +(14,1,1) = {" +u +B +B +B +F +F +B +W +B +B +F +F +s +B +H +"} +(15,1,1) = {" +a +p +p +a +a +a +a +B +J +J +J +B +y +y +a +"} diff --git a/rust/bapi/src/mapmanip/test.rs b/rust/bapi/src/mapmanip/test.rs new file mode 100644 index 00000000000..da5c8bd6716 --- /dev/null +++ b/rust/bapi/src/mapmanip/test.rs @@ -0,0 +1,174 @@ +use dmmtools::dmm::{self, Coord2}; +use itertools::Itertools; + +fn print_diff(left: &str, right: &str) { + for (i, diff) in diff::lines(&left, &right).iter().enumerate() { + match diff { + diff::Result::Left(l) => println!("{} diff - : {}", i, l), + diff::Result::Both(l, r) => { + assert_eq!(l, r); + } + diff::Result::Right(r) => println!("{} diff + : {}", i, r), + } + } +} + +fn all_test_dmm() -> Vec { + std::fs::read_dir("src/mapmanip/test-in") + .unwrap() + .map(|r| r.unwrap().path()) + .filter(|p| p.extension().unwrap() == "dmm") + .sorted() + .collect_vec() +} + +#[test] +fn grid_check() { + let path = std::path::Path::new("src/mapmanip/test-in/_tiny_test_map.dmm"); + println!("path: {}", path.display()); + + let grid_map = crate::mapmanip::core::GridMap::from_file(&path).unwrap(); + assert!(grid_map.grid[&dmm::Coord2::new(2, 1)] + .prefabs + .iter() + .any(|p| p.path == "/obj/random/firstaid")); + assert!(grid_map.grid[&dmm::Coord2::new(1, 2)] + .prefabs + .iter() + .any(|p| p.path == "/obj/random/finances")); + assert!(grid_map.grid[&dmm::Coord2::new(14, 15)] + .prefabs + .iter() + .any(|p| p.path == "/obj/random/handgun")); + assert!(grid_map.grid[&dmm::Coord2::new(15, 14)] + .prefabs + .iter() + .any(|p| p.path == "/obj/random/handgun")); +} + +#[test] +fn to_grid_and_back() { + for path in all_test_dmm() { + println!("path: {}", path.display()); + + let dict_map_original = dmmtools::dmm::Map::from_file(&path).unwrap(); + let grid_map = crate::mapmanip::core::to_grid_map(&dict_map_original); + let dict_map_again = crate::mapmanip::core::to_dict_map(&grid_map); + let map_str_original = crate::mapmanip::core::map_to_string(&dict_map_original).unwrap(); + let map_str_from_grid = crate::mapmanip::core::map_to_string(&dict_map_again).unwrap(); + + dict_map_again + .to_file(&std::path::Path::new("src/mapmanip/test-out").join(path.file_name().unwrap())) + .unwrap(); + + print_diff(&map_str_original, &map_str_from_grid); + + if map_str_original != map_str_from_grid { + assert!(false); + } + } +} + +#[test] +fn extract() { + let path_src = std::path::Path::new("src/mapmanip/test-in/_tiny_test_map.dmm"); + let path_xtr = std::path::Path::new("src/mapmanip/test-in/extracted.dmm"); + let path_xtr_out = std::path::Path::new("src/mapmanip/test-out/extracted_out.dmm"); + + let dict_map_src = dmmtools::dmm::Map::from_file(&path_src).unwrap(); + let dict_map_xtr_expected = dmmtools::dmm::Map::from_file(&path_xtr).unwrap(); + + let grid_map_src = crate::mapmanip::core::to_grid_map(&dict_map_src); + let grid_map_xtr = crate::mapmanip::tools::extract_sub_map( + &grid_map_src, + Coord2::new(4, 7), + Coord2::new(10, 5), + ); + let grid_map_xtr_expected = crate::mapmanip::core::to_grid_map(&dict_map_xtr_expected); + + let dict_map_xtr = crate::mapmanip::core::to_dict_map(&grid_map_xtr); + dict_map_xtr.to_file(path_xtr_out).unwrap(); + + assert_eq!( + grid_map_xtr_expected.grid.keys().collect::>(), + grid_map_xtr.grid.keys().collect::>(), + ); + + for key in grid_map_xtr_expected.grid.keys() { + let tile_xtr_expected = grid_map_xtr_expected.grid.get(key).unwrap(); + let tile_xtr = grid_map_xtr.grid.get(key).unwrap(); + assert_eq!(tile_xtr_expected.prefabs, tile_xtr.prefabs); + } +} + +#[test] +fn insert() { + let path_xtr = std::path::Path::new("src/mapmanip/test-in/extracted.dmm"); + let path_dst = std::path::Path::new("src/mapmanip/test-in/_tiny_test_map.dmm"); + let path_dst_expected = std::path::Path::new("src/mapmanip/test-in/inserted.dmm"); + + let grid_map_dst_expected = + crate::mapmanip::core::GridMap::from_file(&path_dst_expected).unwrap(); + let grid_map_xtr = crate::mapmanip::core::GridMap::from_file(&path_xtr).unwrap(); + let mut grid_map_dst = crate::mapmanip::core::GridMap::from_file(&path_dst).unwrap(); + crate::mapmanip::tools::insert_sub_map(&grid_map_xtr, Coord2::new(6, 4), &mut grid_map_dst); + + assert_eq!( + grid_map_dst_expected.grid.keys().collect::>(), + grid_map_dst.grid.keys().collect::>(), + ); + + for key in grid_map_dst_expected.grid.keys() { + let tile_dst_expected = grid_map_dst_expected.grid.get(key).unwrap(); + let tile_dst = grid_map_dst.grid.get(key).unwrap(); + assert_eq!(tile_dst_expected.prefabs, tile_dst.prefabs); + } +} + +#[test] +fn keys_deduplicated() { + // make sure that if multiple tiles have the same key_suggestion + // they get assigned different keys + + let path_src = std::path::Path::new("src/mapmanip/test-in/_tiny_test_map.dmm"); + let dict_map_src = dmmtools::dmm::Map::from_file(&path_src).unwrap(); + let grid_map_src = crate::mapmanip::core::to_grid_map(&dict_map_src); + + let mut grid_map_out = crate::mapmanip::core::to_grid_map(&dict_map_src); + for tile in grid_map_out.grid.values_mut() { + tile.key_suggestion = dmm::Key::default(); + } + let dict_map_out = crate::mapmanip::core::to_dict_map(&grid_map_out); + let grid_map_out = crate::mapmanip::core::to_grid_map(&dict_map_out); + + for key in grid_map_src.grid.keys() { + let tile_src = grid_map_src.grid.get(key).unwrap(); + let tile_out = grid_map_out.grid.get(key).unwrap(); + assert_eq!(tile_src.prefabs, tile_out.prefabs); + } + + assert_eq!(dict_map_src.dictionary.len(), dict_map_out.dictionary.len()) +} + +#[test] +fn mapmanip_configs_parse() { + let foo = vec![crate::mapmanip::MapManipulation::InsertExtract { + submap_size_x: 1, + submap_size_y: 2, + submaps_dmm: "a".to_owned(), + marker_extract: "b".to_owned(), + marker_insert: "c".to_owned(), + }]; + dbg!(serde_json::to_string(&foo)); + + let mapmanip_configs = walkdir::WalkDir::new("../../maps") + .into_iter() + .map(|d| d.unwrap().path().to_owned()) + .filter(|p| p.extension().is_some()) + .filter(|p| p.extension().unwrap() == "jsonc") + .collect_vec(); + assert_ne!(mapmanip_configs.len(), 0); + for config in mapmanip_configs { + let _ = crate::mapmanip::mapmanip_config_parse(&config); + } +} diff --git a/rust/bapi/src/mapmanip/tools/extract_sub_map.rs b/rust/bapi/src/mapmanip/tools/extract_sub_map.rs new file mode 100644 index 00000000000..8f40df5c24c --- /dev/null +++ b/rust/bapi/src/mapmanip/tools/extract_sub_map.rs @@ -0,0 +1,29 @@ +use crate::mapmanip::core::GridMap; +use dmmtools::dmm; +use dmmtools::dmm::Coord2; +use std::collections::BTreeMap; + +/// Returns part of map of `xtr_size` and at `xtr_coord` from `src_map`. +pub fn extract_sub_map( + src_map: &GridMap, + xtr_coord: dmm::Coord2, + xtr_size: dmm::Coord2, +) -> GridMap { + let mut dst_map = GridMap { + size: xtr_size.z(1), + grid: BTreeMap::new(), + }; + + for x in 1..(xtr_size.x + 1) { + for y in 1..(xtr_size.y + 1) { + if let Some(tile) = src_map + .grid + .get(&Coord2::new(xtr_coord.x + x - 1, xtr_coord.y + y - 1)) + { + dst_map.grid.insert(Coord2::new(x, y), tile.clone()); + } + } + } + + dst_map +} diff --git a/rust/bapi/src/mapmanip/tools/insert_sub_map.rs b/rust/bapi/src/mapmanip/tools/insert_sub_map.rs new file mode 100644 index 00000000000..5b8165e4a87 --- /dev/null +++ b/rust/bapi/src/mapmanip/tools/insert_sub_map.rs @@ -0,0 +1,17 @@ +use crate::mapmanip::GridMap; +use dmmtools::dmm; +use dmmtools::dmm::Coord2; + +/// Takes `xtr_map` and puts it at `coord` in `dst_map`. +pub fn insert_sub_map(xtr_map: &GridMap, coord: dmm::Coord2, dst_map: &mut GridMap) { + for x in 1..(xtr_map.size.x + 1) { + for y in 1..(xtr_map.size.y + 1) { + let coord_src = Coord2::new(x, y); + let coord_dst = Coord2::new(coord.x + x - 1, coord.y + y - 1); + let tile_src = xtr_map.grid.get(&coord_src).unwrap().clone(); + if let Some(cell_dst) = dst_map.grid.get_mut(&coord_dst) { + *cell_dst = tile_src; + } + } + } +} diff --git a/rust/bapi/src/mapmanip/tools/mod.rs b/rust/bapi/src/mapmanip/tools/mod.rs new file mode 100644 index 00000000000..3ff16827d05 --- /dev/null +++ b/rust/bapi/src/mapmanip/tools/mod.rs @@ -0,0 +1,5 @@ +mod extract_sub_map; +pub use extract_sub_map::extract_sub_map; + +mod insert_sub_map; +pub use insert_sub_map::insert_sub_map; diff --git a/tools/ci/libbapi_ci.so b/tools/ci/libbapi_ci.so index 22e2115dcef..13e2dc47f4e 100644 Binary files a/tools/ci/libbapi_ci.so and b/tools/ci/libbapi_ci.so differ