diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay-white.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay-white.png new file mode 100644 index 0000000000..5a9c7dbf45 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay-white.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay.png new file mode 100644 index 0000000000..5f0af691a3 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay0.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay0.png new file mode 100644 index 0000000000..5a9c7dbf45 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay0.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay1.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay1.png new file mode 100644 index 0000000000..07a6780c65 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay10.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay10.png new file mode 100644 index 0000000000..141d0fb706 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay10.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay11.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay11.png new file mode 100644 index 0000000000..afd315feb9 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay11.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay12.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay12.png new file mode 100644 index 0000000000..1f8832639e Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay12.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay13.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay13.png new file mode 100644 index 0000000000..e68bca7993 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay13.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay14.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay14.png new file mode 100644 index 0000000000..843026085b Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay14.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay15.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay15.png new file mode 100644 index 0000000000..eff3d5a3e6 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay15.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay16.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay16.png new file mode 100644 index 0000000000..aa28a073ff Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay16.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay17.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay17.png new file mode 100644 index 0000000000..223dd714f7 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay17.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay18.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay18.png new file mode 100644 index 0000000000..7c4d049855 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay18.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay19.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay19.png new file mode 100644 index 0000000000..00d204f712 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay19.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay2.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay2.png new file mode 100644 index 0000000000..60483ff580 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay20.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay20.png new file mode 100644 index 0000000000..a1979e512c Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay20.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay21.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay21.png new file mode 100644 index 0000000000..7d3af0ad61 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay21.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay22.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay22.png new file mode 100644 index 0000000000..0bdb340968 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay22.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay23.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay23.png new file mode 100644 index 0000000000..804b08332b Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay23.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay24.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay24.png new file mode 100644 index 0000000000..7950a08743 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay24.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay25.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay25.png new file mode 100644 index 0000000000..a3c57307a6 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay25.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay26.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay26.png new file mode 100644 index 0000000000..222f1101b6 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay26.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay27.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay27.png new file mode 100644 index 0000000000..0ad359a401 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay27.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay28.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay28.png new file mode 100644 index 0000000000..66247afac4 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay28.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay29.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay29.png new file mode 100644 index 0000000000..59d18b41b0 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay29.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay3.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay3.png new file mode 100644 index 0000000000..2470376813 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay3.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay30.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay30.png new file mode 100644 index 0000000000..af604e3fde Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay30.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay31.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay31.png new file mode 100644 index 0000000000..e089f729bc Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay31.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay32.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay32.png new file mode 100644 index 0000000000..d4ec76b6b0 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay32.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay33.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay33.png new file mode 100644 index 0000000000..52ac59e9f1 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay33.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay34.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay34.png new file mode 100644 index 0000000000..dbfe5c1a0d Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay34.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay35.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay35.png new file mode 100644 index 0000000000..b9aba5ba4f Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay35.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay36.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay36.png new file mode 100644 index 0000000000..824b5ecbd5 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay36.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay37.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay37.png new file mode 100644 index 0000000000..56c75ecbea Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay37.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay38.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay38.png new file mode 100644 index 0000000000..c53246b16f Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay38.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay39.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay39.png new file mode 100644 index 0000000000..137b74e5c3 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay39.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay4.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay4.png new file mode 100644 index 0000000000..beb408c1e7 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay4.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay40.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay40.png new file mode 100644 index 0000000000..c6f0afde33 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay40.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay41.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay41.png new file mode 100644 index 0000000000..42c5f79fba Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay41.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay42.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay42.png new file mode 100644 index 0000000000..82a6042996 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay42.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay43.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay43.png new file mode 100644 index 0000000000..647e892e9b Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay43.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay44.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay44.png new file mode 100644 index 0000000000..34ca5e76d7 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay44.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay45.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay45.png new file mode 100644 index 0000000000..92d3445a05 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay45.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay46.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay46.png new file mode 100644 index 0000000000..f317416479 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay46.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay47.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay47.png new file mode 100644 index 0000000000..5c1e8c3e4d Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay47.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay48.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay48.png new file mode 100644 index 0000000000..0beecd35ef Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay48.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay49.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay49.png new file mode 100644 index 0000000000..1ac7f7e439 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay49.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay5.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay5.png new file mode 100644 index 0000000000..8a81bdb306 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay5.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay50.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay50.png new file mode 100644 index 0000000000..3503ef3013 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay50.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay51.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay51.png new file mode 100644 index 0000000000..ba0ceb2580 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay51.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay52.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay52.png new file mode 100644 index 0000000000..65e736dd74 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay52.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay53.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay53.png new file mode 100644 index 0000000000..2fcf13e3cc Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay53.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay54.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay54.png new file mode 100644 index 0000000000..5a5d3b9e94 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay54.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay55.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay55.png new file mode 100644 index 0000000000..f537d8c6f5 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay55.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay56.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay56.png new file mode 100644 index 0000000000..be3cc1d7ef Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay56.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay57.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay57.png new file mode 100644 index 0000000000..32d3d68017 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay57.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay58.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay58.png new file mode 100644 index 0000000000..a02b65e74d Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay58.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay59.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay59.png new file mode 100644 index 0000000000..03277509b8 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay59.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay6.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay6.png new file mode 100644 index 0000000000..db2fc20ced Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay6.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay60.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay60.png new file mode 100644 index 0000000000..e0e37e22c7 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay60.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay61.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay61.png new file mode 100644 index 0000000000..6b03307865 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay61.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay62.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay62.png new file mode 100644 index 0000000000..aca39f727a Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay62.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay63.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay63.png new file mode 100644 index 0000000000..a1ffcc3936 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay63.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay7.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay7.png new file mode 100644 index 0000000000..a5ff3420b8 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay7.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay8.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay8.png new file mode 100644 index 0000000000..9deaa88014 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay8.png differ diff --git a/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay9.png b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay9.png new file mode 100644 index 0000000000..32a4421703 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/character-overlay/character-overlay9.png differ diff --git a/core/assets-raw/sprites/blocks/environment/clear-editor.png b/core/assets-raw/sprites/blocks/environment/clear-editor.png new file mode 100644 index 0000000000..f65a75d144 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/clear-editor.png differ diff --git a/core/assets-raw/sprites/blocks/environment/colored-floor-autotile.png b/core/assets-raw/sprites/blocks/environment/colored-floor-autotile.png new file mode 100644 index 0000000000..b3c7d8da22 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/colored-floor-autotile.png differ diff --git a/core/assets-raw/sprites/blocks/environment/colored-floor.png b/core/assets-raw/sprites/blocks/environment/colored-floor.png new file mode 100644 index 0000000000..c7993a6623 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/colored-floor.png differ diff --git a/core/assets-raw/sprites/blocks/environment/colored-wall-autotile.png b/core/assets-raw/sprites/blocks/environment/colored-wall-autotile.png new file mode 100644 index 0000000000..8af8c96283 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/colored-wall-autotile.png differ diff --git a/core/assets-raw/sprites/blocks/environment/colored-wall.png b/core/assets-raw/sprites/blocks/environment/colored-wall.png new file mode 100644 index 0000000000..a5b6b2d8a6 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/colored-wall.png differ diff --git a/core/assets-raw/sprites/blocks/environment/grate-detailed.png b/core/assets-raw/sprites/blocks/environment/grate-detailed.png new file mode 100644 index 0000000000..b295bc8169 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/grate-detailed.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-tiles-1-autotile.png b/core/assets-raw/sprites/blocks/environment/metal-tiles-1-autotile.png new file mode 100644 index 0000000000..c9bb24e5ec Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-tiles-1-autotile.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-tiles-1.png b/core/assets-raw/sprites/blocks/environment/metal-tiles-1.png new file mode 100644 index 0000000000..d20970e1fa Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-tiles-1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-tiles-10-autotile.png b/core/assets-raw/sprites/blocks/environment/metal-tiles-10-autotile.png new file mode 100644 index 0000000000..f6c0d9b491 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-tiles-10-autotile.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-tiles-11-autotile1.png b/core/assets-raw/sprites/blocks/environment/metal-tiles-11-autotile1.png new file mode 100644 index 0000000000..3a5f16377b Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-tiles-11-autotile1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-tiles-11-autotile2.png b/core/assets-raw/sprites/blocks/environment/metal-tiles-11-autotile2.png new file mode 100644 index 0000000000..c7eb393e2e Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-tiles-11-autotile2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-tiles-11-autotile3.png b/core/assets-raw/sprites/blocks/environment/metal-tiles-11-autotile3.png new file mode 100644 index 0000000000..cd7e4d7b39 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-tiles-11-autotile3.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-tiles-11.png b/core/assets-raw/sprites/blocks/environment/metal-tiles-11.png new file mode 100644 index 0000000000..31720bc1b9 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-tiles-11.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-tiles-12-autotile1.png b/core/assets-raw/sprites/blocks/environment/metal-tiles-12-autotile1.png new file mode 100644 index 0000000000..b318f30eff Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-tiles-12-autotile1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-tiles-12-autotile2.png b/core/assets-raw/sprites/blocks/environment/metal-tiles-12-autotile2.png new file mode 100644 index 0000000000..e70ee57e22 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-tiles-12-autotile2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-tiles-12-autotile3.png b/core/assets-raw/sprites/blocks/environment/metal-tiles-12-autotile3.png new file mode 100644 index 0000000000..3a83d7cf96 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-tiles-12-autotile3.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-tiles-12-autotile4.png b/core/assets-raw/sprites/blocks/environment/metal-tiles-12-autotile4.png new file mode 100644 index 0000000000..e70ee57e22 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-tiles-12-autotile4.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-tiles-12.png b/core/assets-raw/sprites/blocks/environment/metal-tiles-12.png new file mode 100644 index 0000000000..f514f879fe Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-tiles-12.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-tiles-2-autotile.png b/core/assets-raw/sprites/blocks/environment/metal-tiles-2-autotile.png new file mode 100644 index 0000000000..86de0953bf Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-tiles-2-autotile.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-tiles-3-autotile.png b/core/assets-raw/sprites/blocks/environment/metal-tiles-3-autotile.png new file mode 100644 index 0000000000..5a30bd7107 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-tiles-3-autotile.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-tiles-4-autotile.png b/core/assets-raw/sprites/blocks/environment/metal-tiles-4-autotile.png new file mode 100644 index 0000000000..f3bbc7bbc3 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-tiles-4-autotile.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-tiles-5-autotile.png b/core/assets-raw/sprites/blocks/environment/metal-tiles-5-autotile.png new file mode 100644 index 0000000000..c812412fc0 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-tiles-5-autotile.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-tiles-6-autotile.png b/core/assets-raw/sprites/blocks/environment/metal-tiles-6-autotile.png new file mode 100644 index 0000000000..c5d1af221d Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-tiles-6-autotile.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-tiles-7-autotile.png b/core/assets-raw/sprites/blocks/environment/metal-tiles-7-autotile.png new file mode 100644 index 0000000000..53c3136338 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-tiles-7-autotile.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-tiles-7-mid-2.png b/core/assets-raw/sprites/blocks/environment/metal-tiles-7-mid-2.png new file mode 100644 index 0000000000..b2528814b0 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-tiles-7-mid-2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-tiles-7-mid-3.png b/core/assets-raw/sprites/blocks/environment/metal-tiles-7-mid-3.png new file mode 100644 index 0000000000..235d30cb7c Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-tiles-7-mid-3.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-tiles-7-mid-4.png b/core/assets-raw/sprites/blocks/environment/metal-tiles-7-mid-4.png new file mode 100644 index 0000000000..d7650d8789 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-tiles-7-mid-4.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-tiles-7-mid-5.png b/core/assets-raw/sprites/blocks/environment/metal-tiles-7-mid-5.png new file mode 100644 index 0000000000..54a79297d9 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-tiles-7-mid-5.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-tiles-7-mid-6.png b/core/assets-raw/sprites/blocks/environment/metal-tiles-7-mid-6.png new file mode 100644 index 0000000000..6ab458be62 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-tiles-7-mid-6.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-tiles-7-mid-7.png b/core/assets-raw/sprites/blocks/environment/metal-tiles-7-mid-7.png new file mode 100644 index 0000000000..b2528814b0 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-tiles-7-mid-7.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-tiles-7-mid-8.png b/core/assets-raw/sprites/blocks/environment/metal-tiles-7-mid-8.png new file mode 100644 index 0000000000..b2528814b0 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-tiles-7-mid-8.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-tiles-7-mid-9.png b/core/assets-raw/sprites/blocks/environment/metal-tiles-7-mid-9.png new file mode 100644 index 0000000000..1c1918d087 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-tiles-7-mid-9.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-tiles-8-autotile.png b/core/assets-raw/sprites/blocks/environment/metal-tiles-8-autotile.png new file mode 100644 index 0000000000..ed83b0e931 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-tiles-8-autotile.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-tiles-8-mid-2.png b/core/assets-raw/sprites/blocks/environment/metal-tiles-8-mid-2.png new file mode 100644 index 0000000000..cc66f1c5f6 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-tiles-8-mid-2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-tiles-9-autotile.png b/core/assets-raw/sprites/blocks/environment/metal-tiles-9-autotile.png new file mode 100644 index 0000000000..784130d2d3 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-tiles-9-autotile.png differ diff --git a/core/assets-raw/sprites/editor/block-border-editor.png b/core/assets-raw/sprites/editor/block-border-editor.png deleted file mode 100644 index daf204a949..0000000000 Binary files a/core/assets-raw/sprites/editor/block-border-editor.png and /dev/null differ diff --git a/core/assets-raw/sprites/editor/clear-editor.png b/core/assets-raw/sprites/editor/clear-editor.png deleted file mode 100644 index 1a3a506d64..0000000000 Binary files a/core/assets-raw/sprites/editor/clear-editor.png and /dev/null differ diff --git a/core/assets-raw/sprites/editor/pack.json b/core/assets-raw/sprites/editor/pack.json deleted file mode 100644 index 775c36ebdd..0000000000 --- a/core/assets-raw/sprites/editor/pack.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - duplicatePadding: true, - combineSubdirectories: true, - flattenPaths: true, - maxWidth: 2048, - maxHeight: 2048, - fast: true, - stripWhitespaceCenter: true -} diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 22bd9db7e7..3dd7ba580b 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -121,6 +121,7 @@ continue = Continue maps.none = [lightgray]No maps found! invalid = Invalid pickcolor = Pick Color +color = Color preparingconfig = Preparing Config preparingcontent = Preparing Content uploadingcontent = Uploading Content @@ -1679,6 +1680,22 @@ block.metal-floor-3.name = Metal Floor 3 block.metal-floor-4.name = Metal Floor 4 block.metal-floor-5.name = Metal Floor 5 block.metal-floor-damaged.name = Damaged Metal Floor +block.metal-tiles-1.name = Metal Tiles 1 +block.metal-tiles-2.name = Metal Tiles 2 +block.metal-tiles-3.name = Metal Tiles 3 +block.metal-tiles-4.name = Metal Tiles 4 +block.metal-tiles-5.name = Metal Tiles 5 +block.metal-tiles-6.name = Metal Tiles 6 +block.metal-tiles-7.name = Metal Tiles 7 +block.metal-tiles-8.name = Metal Tiles 8 +block.metal-tiles-9.name = Metal Tiles 9 +block.metal-tiles-10.name = Metal Tiles 10 +block.metal-tiles-11.name = Metal Tiles 11 +block.metal-tiles-12.name = Metal Tiles 12 +block.colored-floor.name = Colored Floor +block.colored-wall.name = Colored Wall +block.character-overlay.name = Character Overlay +block.character-overlay-white.name = Character Overlay (White) block.dark-panel-1.name = Dark Panel 1 block.dark-panel-2.name = Dark Panel 2 block.dark-panel-3.name = Dark Panel 3 diff --git a/core/assets/icons/icons.properties b/core/assets/icons/icons.properties index 8034be5937..ff3282c8fa 100755 --- a/core/assets/icons/icons.properties +++ b/core/assets/icons/icons.properties @@ -600,3 +600,19 @@ 63082=stone-vent|block-stone-vent-ui 63081=basalt-vent|block-basalt-vent-ui 63080=tile-logic-display|block-tile-logic-display-ui +63079=metal-tiles-1|block-metal-tiles-1-ui +63078=metal-tiles-2|block-metal-tiles-2-ui +63077=metal-tiles-3|block-metal-tiles-3-ui +63076=metal-tiles-4|block-metal-tiles-4-ui +63075=colored-floor|block-colored-floor-ui +63074=metal-tiles-5|block-metal-tiles-5-ui +63073=metal-tiles-6|block-metal-tiles-6-ui +63072=metal-tiles-7|block-metal-tiles-7-ui +63071=metal-tiles-8|block-metal-tiles-8-ui +63070=metal-tiles-9|block-metal-tiles-9-ui +63069=metal-tiles-10|block-metal-tiles-10-ui +63068=colored-wall|block-colored-wall-ui +63067=metal-tiles-11|block-metal-tiles-11-ui +63066=metal-tiles-12|block-metal-tiles-12-ui +63065=character-overlay|block-character-overlay-ui +63064=character-overlay-white|block-character-overlay-white-ui diff --git a/core/src/mindustry/content/Blocks.java b/core/src/mindustry/content/Blocks.java index c6f326cc98..e6e93e4ce1 100644 --- a/core/src/mindustry/content/Blocks.java +++ b/core/src/mindustry/content/Blocks.java @@ -11,6 +11,7 @@ import mindustry.entities.effect.*; import mindustry.entities.part.DrawPart.*; import mindustry.entities.part.*; import mindustry.entities.pattern.*; +import mindustry.game.*; import mindustry.gen.*; import mindustry.graphics.*; import mindustry.type.*; @@ -57,8 +58,18 @@ public class Blocks{ //boulders shaleBoulder, sandBoulder, daciteBoulder, boulder, snowBoulder, basaltBoulder, carbonBoulder, ferricBoulder, beryllicBoulder, yellowStoneBoulder, arkyicBoulder, crystalCluster, vibrantCrystalCluster, crystalBlocks, crystalOrbs, crystallineBoulder, redIceBoulder, rhyoliteBoulder, redStoneBoulder, + metalFloor, metalFloorDamaged, metalFloor2, metalFloor3, metalFloor4, metalFloor5, basalt, magmarock, hotrock, snowWall, saltWall, + //old metal floors darkPanel1, darkPanel2, darkPanel3, darkPanel4, darkPanel5, darkPanel6, darkMetal, + //new metal floors + metalTiles1, metalTiles2, metalTiles3, metalTiles4, metalTiles5, metalTiles6, metalTiles7, metalTiles8, metalTiles9, metalTiles10, metalTiles11, metalTiles12, + + //colored + coloredFloor, coloredWall, + characterOverlayGray, + characterOverlayWhite, + pebbles, tendrils, //ores @@ -314,6 +325,7 @@ public class Blocks{ solid = true; variants = 0; canShadow = false; + drawEdgeOut = false; }}; empty = new EmptyFloor("empty"); @@ -808,6 +820,107 @@ public class Blocks{ darkMetal = new StaticWall("dark-metal"); + metalTiles1 = new Floor("metal-tiles-1"){{ + autotile = true; + drawEdgeOut = false; + drawEdgeIn = false; + }}; + + metalTiles2 = new Floor("metal-tiles-2"){{ + autotile = true; + drawEdgeOut = false; + drawEdgeIn = false; + }}; + + metalTiles3 = new Floor("metal-tiles-3"){{ + autotile = true; + drawEdgeOut = false; + drawEdgeIn = false; + }}; + + metalTiles4 = new Floor("metal-tiles-4"){{ + autotile = true; + drawEdgeOut = false; + drawEdgeIn = false; + }}; + + metalTiles5 = new Floor("metal-tiles-5"){{ + autotile = true; + drawEdgeOut = false; + drawEdgeIn = false; + }}; + + metalTiles6 = new Floor("metal-tiles-6"){{ + autotile = true; + drawEdgeOut = false; + drawEdgeIn = false; + + emitLight = true; + lightRadius = 30f; + lightColor = Team.crux.color.cpy().a(0.3f); + }}; + + metalTiles7 = new Floor("metal-tiles-7"){{ + autotile = true; + drawEdgeOut = false; + drawEdgeIn = false; + autotileMidVariants = 9; + }}; + + metalTiles8 = new Floor("metal-tiles-8"){{ + autotile = true; + drawEdgeOut = false; + drawEdgeIn = false; + autotileMidVariants = 2; + }}; + + metalTiles9 = new Floor("metal-tiles-9"){{ + autotile = true; + drawEdgeOut = false; + drawEdgeIn = false; + }}; + + metalTiles10 = new Floor("metal-tiles-10"){{ + autotile = true; + drawEdgeOut = false; + drawEdgeIn = false; + }}; + + metalTiles11 = new Floor("metal-tiles-11"){{ + autotile = true; + drawEdgeOut = false; + drawEdgeIn = false; + autotileVariants = 3; + }}; + + metalTiles12 = new Floor("metal-tiles-12"){{ + autotile = true; + drawEdgeOut = false; + drawEdgeIn = false; + autotileVariants = 4; + emitLight = true; + lightRadius = 30f; + lightColor = Team.crux.color.cpy().a(0.3f); + }}; + + coloredFloor = new ColoredFloor("colored-floor"){{ + autotile = true; + drawEdgeOut = false; + drawEdgeIn = false; + }}; + + coloredWall = new ColoredWall("colored-wall"){{ + autotile = true; + }}; + + characterOverlayGray = new CharacterOverlay("character-overlay"){{ + color = Pal.metalGrayDark; + }}; + + characterOverlayWhite = new CharacterOverlay("character-overlay-white"){{ + color = Color.white; + }}; + Seq.with(metalFloor, metalFloorDamaged, metalFloor2, metalFloor3, metalFloor4, metalFloor5, darkPanel1, darkPanel2, darkPanel3, darkPanel4, darkPanel5, darkPanel6) .each(b -> b.asFloor().wall = darkMetal); diff --git a/core/src/mindustry/core/Renderer.java b/core/src/mindustry/core/Renderer.java index 3de64e4892..bee18afeea 100644 --- a/core/src/mindustry/core/Renderer.java +++ b/core/src/mindustry/core/Renderer.java @@ -40,7 +40,7 @@ public class Renderer implements ApplicationListener{ public @Nullable Bloom bloom; public @Nullable FrameBuffer backgroundBuffer; public FrameBuffer effectBuffer = new FrameBuffer(); - public boolean animateShields, drawWeather = true, drawStatus, enableEffects, drawDisplays = true, drawLight = true, pixelate = false; + public boolean animateShields, animateWater, drawWeather = true, drawStatus, enableEffects, drawDisplays = true, drawLight = true, pixelate = false; public float weatherAlpha; /** minZoom = zooming out, maxZoom = zooming in, used by cutscenes */ public float minZoom = 1.5f, maxZoom = 6f; @@ -167,6 +167,7 @@ public class Renderer implements ApplicationListener{ laserOpacity = settings.getInt("lasersopacity") / 100f; bridgeOpacity = settings.getInt("bridgeopacity") / 100f; animateShields = settings.getBool("animatedshields"); + animateWater = settings.getBool("animatewater"); drawStatus = settings.getBool("blockstatus"); enableEffects = settings.getBool("effects"); drawDisplays = !settings.getBool("hidedisplays"); @@ -310,7 +311,7 @@ public class Renderer implements ApplicationListener{ graphics.clear(clearColor); Draw.reset(); - if(settings.getBool("animatedwater") || animateShields){ + if(animateWater || animateShields){ effectBuffer.resize(graphics.getWidth(), graphics.getHeight()); } diff --git a/core/src/mindustry/editor/DrawOperation.java b/core/src/mindustry/editor/DrawOperation.java index 6ef2d2e3b4..6fbf9b996d 100755 --- a/core/src/mindustry/editor/DrawOperation.java +++ b/core/src/mindustry/editor/DrawOperation.java @@ -10,6 +10,13 @@ import mindustry.world.blocks.environment.*; import static mindustry.Vars.*; public class DrawOperation{ + static final byte + opFloor = 0, + opBlock = 1, + opRotation = 2, + opTeam = 3, + opOverlay = 4; + private LongSeq array = new LongSeq(); public boolean isEmpty(){ @@ -39,45 +46,58 @@ public class DrawOperation{ } short getTile(Tile tile, byte type){ - if(type == OpType.floor.ordinal()){ - return tile.floorID(); - }else if(type == OpType.block.ordinal()){ - return tile.blockID(); - }else if(type == OpType.rotation.ordinal()){ - return tile.build == null ? 0 : (byte)tile.build.rotation; - }else if(type == OpType.team.ordinal()){ - return (byte)tile.getTeamID(); - }else if(type == OpType.overlay.ordinal()){ - return tile.overlayID(); - } - throw new IllegalArgumentException("Invalid type."); + return switch(type){ + case opFloor -> tile.floorID(); + case opOverlay -> tile.overlayID(); + case opBlock -> tile.blockID(); + case opRotation -> tile.build == null ? 0 : (byte)tile.build.rotation; + case opTeam -> (byte)tile.getTeamID(); + default -> throw new IllegalArgumentException("Invalid type."); + }; } void setTile(Tile tile, byte type, short to){ + if(type == opBlock || type == opTeam || type == opRotation){ + tile.getLinkedTiles(t -> { + editor.renderer.updateBlock(t); + editor.renderer.updateStatic(t.x, t.y); + }); + }else{ + editor.renderer.updateStatic(tile.x, tile.y); + } + editor.load(() -> { - if(type == OpType.floor.ordinal()){ - if(content.block(to) instanceof Floor floor){ - tile.setFloor(floor); + switch(type){ + case opFloor -> { + if(content.block(to) instanceof Floor floor){ + tile.setFloor(floor); + } } - }else if(type == OpType.block.ordinal()){ - tile.getLinkedTiles(t -> editor.renderer.updatePoint(t.x, t.y)); - - Block block = content.block(to); - tile.setBlock(block, tile.team(), tile.build == null ? 0 : tile.build.rotation); - if(tile.build != null){ - tile.build.enabled = true; + case opOverlay -> { + if(content.block(to) instanceof Floor floor){ + tile.setOverlay(floor); + } } - - tile.getLinkedTiles(t -> editor.renderer.updatePoint(t.x, t.y)); - }else if(type == OpType.rotation.ordinal()){ - if(tile.build != null) tile.build.rotation = to; - }else if(type == OpType.team.ordinal()){ - tile.setTeam(Team.get(to)); - }else if(type == OpType.overlay.ordinal()){ - tile.setOverlayID(to); + case opBlock -> { + Block block = content.block(to); + tile.setBlock(block, tile.team(), tile.build == null ? 0 : tile.build.rotation); + if(tile.build != null){ + tile.build.enabled = true; + } + } + case opRotation -> { + if(tile.build != null) tile.build.rotation = to; + } + case opTeam -> tile.setTeam(Team.get(to)); } }); - editor.renderer.updatePoint(tile.x, tile.y); + + if(type == opBlock || type == opTeam || type == opRotation){ + tile.getLinkedTiles(t -> { + editor.renderer.updateBlock(t); + editor.renderer.updateStatic(t.x, t.y); + }); + } } @Struct @@ -87,12 +107,4 @@ public class DrawOperation{ byte type; short value; } - - public enum OpType{ - floor, - block, - rotation, - team, - overlay - } } diff --git a/core/src/mindustry/editor/EditorSpriteCache.java b/core/src/mindustry/editor/EditorSpriteCache.java new file mode 100644 index 0000000000..26cfc652ec --- /dev/null +++ b/core/src/mindustry/editor/EditorSpriteCache.java @@ -0,0 +1,171 @@ +package mindustry.editor; + +import arc.graphics.*; +import arc.graphics.g2d.*; +import arc.graphics.gl.*; +import arc.math.*; +import arc.struct.*; +import arc.util.*; + +public class EditorSpriteCache implements Disposable{ + //xy + color + uv + static final int vertexSize = 2 + 1 + 2; + + private @Nullable Mesh mesh; + private final Seq textures = new Seq<>(8); + private final IntSeq counts = new IntSeq(8); + + private float[] tmpVertices; + + /** Index in tmpVertices of current vertex data. */ + private int index; + + /** @param tmpVertices Temporary buffer to hold vertices while building up sprites. Should be large enough to hold all sprite data this cache will contain. */ + public EditorSpriteCache(float[] tmpVertices){ + this.tmpVertices = tmpVertices; + } + + /** @return whether anything was added to the cache. */ + public boolean isEmpty(){ + return index == 0; + } + + /** + * Builds this cache into a mesh that can be used for rendering. Use after calling {@link #draw(TextureRegion, float, float, float, float, float, float, float, float)}. + * Until this method is called, no mesh is created. + * + * @param indices The shared index data in standard quad format, as seen in SpriteBatch. + * Since this data is static, it should be the same across all caches, and be large enough to accommodate all sprites. + * */ + public void build(IndexData indices){ + if(mesh != null) mesh.dispose(); + + mesh = new Mesh(true, index / vertexSize, 0, + VertexAttribute.position, + VertexAttribute.color, + VertexAttribute.texCoords + ); + mesh.indices = indices; + mesh.setVertices(tmpVertices, 0, index); + } + + /** Adds the specified region to the cache. */ + public void draw(TextureRegion region, float x, float y, float originX, float originY, float width, float height, float rotation, float colorPacked){ + if(mesh != null) throw new IllegalStateException("This cache is already built. Call #clear() before drawing new sprites."); + + // bottom left and top right corner points relative to origin + final float worldOriginX = x + originX; + final float worldOriginY = y + originY; + float fx = -originX; + float fy = -originY; + float fx2 = width - originX; + float fy2 = height - originY; + + float x1, y1, x2, y2, x3, y3, x4, y4; + + // rotate + if(rotation != 0){ + final float cos = Mathf.cosDeg(rotation); + final float sin = Mathf.sinDeg(rotation); + + x1 = cos * fx - sin * fy; + y1 = sin * fx + cos * fy; + + x2 = cos * fx - sin * fy2; + y2 = sin * fx + cos * fy2; + + x3 = cos * fx2 - sin * fy2; + y3 = sin * fx2 + cos * fy2; + + x4 = x1 + (x3 - x2); + y4 = y3 - (y2 - y1); + }else{ + x1 = fx; + y1 = fy; + + x2 = fx; + y2 = fy2; + + x3 = fx2; + y3 = fy2; + + x4 = fx2; + y4 = fy; + } + + x1 += worldOriginX; + y1 += worldOriginY; + x2 += worldOriginX; + y2 += worldOriginY; + x3 += worldOriginX; + y3 += worldOriginY; + x4 += worldOriginX; + y4 += worldOriginY; + + final float u = region.u; + final float v = region.v2; + final float u2 = region.u2; + final float v2 = region.v; + + int idx = index; + float[] verts = tmpVertices; + Texture texture = region.texture; + + verts[idx + 0] = x1; + verts[idx + 1] = y1; + verts[idx + 2] = colorPacked; + verts[idx + 3] = u; + verts[idx + 4] = v; + + verts[idx + 5] = x2; + verts[idx + 6] = y2; + verts[idx + 7] = colorPacked; + verts[idx + 8] = u; + verts[idx + 9] = v2; + + verts[idx + 10] = x3; + verts[idx + 11] = y3; + verts[idx + 12] = colorPacked; + verts[idx + 13] = u2; + verts[idx + 14] = v2; + + verts[idx + 15] = x4; + verts[idx + 16] = y4; + verts[idx + 17] = colorPacked; + verts[idx + 18] = u2; + verts[idx + 19] = v; + + int lastIndex = textures.size - 1; + if(lastIndex < 0 || textures.get(lastIndex) != texture){ + textures.add(texture); + counts.add(6); + }else{ + counts.incr(lastIndex, 6); + } + + index += vertexSize * 4; + } + + /** Renders the cached mesh. The shader must already have the correct view matrix set as a uniform. */ + public void render(Shader shader){ + if(mesh == null) throw new IllegalStateException("Cache is empty, call build() first."); + + int offset = 0; + + for(int i = 0; i < textures.size; i++){ + int count = counts.items[i]; + textures.get(i).bind(); + + mesh.render(shader, Gl.triangles, offset, count); + offset += count; + } + } + + @Override + public void dispose(){ + if(mesh != null){ + mesh.dispose(); + mesh = null; + } + } +} \ No newline at end of file diff --git a/core/src/mindustry/editor/EditorTile.java b/core/src/mindustry/editor/EditorTile.java index b4ff3f1296..3aec80c678 100644 --- a/core/src/mindustry/editor/EditorTile.java +++ b/core/src/mindustry/editor/EditorTile.java @@ -2,9 +2,9 @@ package mindustry.editor; import arc.func.*; import mindustry.content.*; -import mindustry.editor.DrawOperation.*; import mindustry.game.*; import mindustry.gen.*; +import mindustry.graphics.*; import mindustry.world.*; import mindustry.world.blocks.environment.*; import mindustry.world.modules.*; @@ -27,17 +27,17 @@ public class EditorTile extends Tile{ if(type instanceof OverlayFloor){ //don't place on liquids if(floor.hasSurface() || !type.needsSurface){ - setOverlayID(type.id); + setOverlay(type); } return; } - if(floor == type && overlayID() == 0) return; - if(overlayID() != 0) op(OpType.overlay, overlayID()); - if(floor != type) op(OpType.floor, floor.id); + if(floor == type) return; + + op(DrawOperation.opFloor, floor.id); this.floor = type; - this.overlay = (Floor)Blocks.air; + type.floorChanged(this); } @Override @@ -47,30 +47,40 @@ public class EditorTile extends Tile{ @Override public void setBlock(Block type, Team team, int rotation, Prov entityprov){ + Block prev = this.block; + Tile prevCenter = (build == null ? this : build.tile); + if(skip()){ super.setBlock(type, team, rotation, entityprov); return; } if(this.block == type && (build == null || build.rotation == rotation)){ - update(); return; } if(!isCenter()){ EditorTile cen = (EditorTile)build.tile; - cen.op(OpType.rotation, (byte)build.rotation); - cen.op(OpType.team, (byte)build.team.id); - cen.op(OpType.block, block.id); - update(); + cen.op(DrawOperation.opRotation, (byte)build.rotation); + cen.op(DrawOperation.opTeam, (byte)build.team.id); + cen.op(DrawOperation.opBlock, block.id); + updateStatic(); }else{ - if(build != null) op(OpType.rotation, (byte)build.rotation); - if(build != null) op(OpType.team, (byte)build.team.id); - op(OpType.block, block.id); - + if(build != null) op(DrawOperation.opRotation, (byte)build.rotation); + if(build != null) op(DrawOperation.opTeam, (byte)build.team.id); + op(DrawOperation.opBlock, block.id); } super.setBlock(type, team, rotation, entityprov); + + if(requiresBlockUpdate(type) || requiresBlockUpdate(prev)){ + if(prev.size > 1){ + prevCenter.getLinkedTilesAs(prev, tile -> editor.renderer.updateBlock(tile)); + } + getLinkedTiles(tile -> editor.renderer.updateBlock(tile)); + }else{ + renderer.blocks.updateShadowTile(this); + } } @Override @@ -80,11 +90,11 @@ public class EditorTile extends Tile{ return; } - if(getTeamID() == team.id) return; - op(OpType.team, (byte)getTeamID()); + if(getTeamID() == team.id || !synthetic()) return; + op(DrawOperation.opTeam, (byte)getTeamID()); super.setTeam(team); - getLinkedTiles(t -> editor.renderer.updatePoint(t.x, t.y)); + getLinkedTiles(t -> editor.renderer.updateBlock(t.x, t.y)); } @Override @@ -95,8 +105,8 @@ public class EditorTile extends Tile{ } if(!floor.hasSurface() && overlay.asFloor().needsSurface && (overlay instanceof OreBlock || !floor.supportsOverlay)) return; - if(overlay() == overlay) return; - op(OpType.overlay, this.overlay.id); + if(this.overlay == overlay) return; + op(DrawOperation.opOverlay, this.overlay.id); super.setOverlay(overlay); } @@ -105,7 +115,7 @@ public class EditorTile extends Tile{ if(skip()){ super.fireChanged(); }else{ - update(); + updateStatic(); } } @@ -114,7 +124,7 @@ public class EditorTile extends Tile{ if(skip()){ super.firePreChanged(); }else{ - update(); + updateStatic(); } } @@ -154,15 +164,24 @@ public class EditorTile extends Tile{ } } - private void update(){ - editor.renderer.updatePoint(x, y); + @Override + public boolean isDarkened(){ + return skip() && super.isDarkened(); + } + + private boolean requiresBlockUpdate(Block block){ + return block != Blocks.air && block.cacheLayer == CacheLayer.normal; + } + + private void updateStatic(){ + editor.renderer.updateStatic(x, y); } private boolean skip(){ return state.isGame() || editor.isLoading() || world.isGenerating(); } - private void op(OpType type, short value){ - editor.addTileOp(TileOp.get(x, y, (byte)type.ordinal(), value)); + private void op(int type, short value){ + editor.addTileOp(TileOp.get(x, y, (byte)type, value)); } } diff --git a/core/src/mindustry/editor/EditorTool.java b/core/src/mindustry/editor/EditorTool.java index 50c7a2aca9..6848ae8f40 100644 --- a/core/src/mindustry/editor/EditorTool.java +++ b/core/src/mindustry/editor/EditorTool.java @@ -131,7 +131,7 @@ public enum EditorTool{ Block dest = tile.floor(); if(dest == editor.drawBlock) return; tester = t -> t.floor() == dest; - setter = t -> t.setFloorUnder(editor.drawBlock.asFloor()); + setter = t -> t.setFloor(editor.drawBlock.asFloor()); }else{ Block dest = tile.block(); if(dest == editor.drawBlock) return; diff --git a/core/src/mindustry/editor/MapEditor.java b/core/src/mindustry/editor/MapEditor.java index 38839e6108..bb5ff4b102 100644 --- a/core/src/mindustry/editor/MapEditor.java +++ b/core/src/mindustry/editor/MapEditor.java @@ -7,7 +7,6 @@ import arc.math.*; import arc.math.geom.*; import arc.struct.*; import mindustry.content.*; -import mindustry.editor.DrawOperation.*; import mindustry.entities.units.*; import mindustry.game.*; import mindustry.gen.*; @@ -149,6 +148,7 @@ public class MapEditor{ y = Mathf.clamp(y, (drawBlock.size - 1) / 2, height() - drawBlock.size / 2 - 1); if(!hasOverlap(x, y)){ tile(x, y).setBlock(drawBlock, drawTeam, rotation); + addTileOp(TileOp.get((short)x, (short)y, DrawOperation.opTeam, (byte)drawTeam.id)); } }else{ boolean isFloor = drawBlock.isFloor() && drawBlock != Blocks.air; @@ -166,10 +166,14 @@ public class MapEditor{ } }else if(!(tile.block().isMultiblock() && !drawBlock.isMultiblock())){ if(drawBlock.rotate && tile.build != null && tile.build.rotation != rotation){ - addTileOp(TileOp.get(tile.x, tile.y, (byte)OpType.rotation.ordinal(), (byte)rotation)); + addTileOp(TileOp.get(tile.x, tile.y, DrawOperation.opRotation, (byte)rotation)); } tile.setBlock(drawBlock, drawTeam, rotation); + + if(drawBlock.synthetic()){ + addTileOp(TileOp.get(tile.x, tile.y, DrawOperation.opTeam, (byte)drawTeam.id)); + } } }; @@ -375,7 +379,7 @@ public class MapEditor{ if(currentOp == null) currentOp = new DrawOperation(); currentOp.addOperation(data); - renderer.updatePoint(TileOp.x(data), TileOp.y(data)); + renderer.updateStatic(TileOp.x(data), TileOp.y(data)); } class Context implements WorldContext{ diff --git a/core/src/mindustry/editor/MapEditorDialog.java b/core/src/mindustry/editor/MapEditorDialog.java index 3bab193111..5e617806f4 100644 --- a/core/src/mindustry/editor/MapEditorDialog.java +++ b/core/src/mindustry/editor/MapEditorDialog.java @@ -29,6 +29,7 @@ import mindustry.ui.dialogs.*; import mindustry.world.*; import mindustry.world.blocks.environment.*; import mindustry.world.blocks.storage.*; +import mindustry.world.blocks.storage.CoreBlock.*; import mindustry.world.meta.*; import static mindustry.Vars.*; @@ -303,7 +304,7 @@ public class MapEditorDialog extends Dialog implements Disposable{ state.rules = (lastSavedRules == null ? new Rules() : lastSavedRules); lastSavedRules = null; saved = false; - editor.renderer.updateAll(); + editor.renderer.recache(); } private void editInGame(){ @@ -323,9 +324,8 @@ public class MapEditorDialog extends Dialog implements Disposable{ "width", editor.width(), "height", editor.height() )); + state.set(State.playing); world.endMapLoad(); - player.set(world.width() * tilesize/2f, world.height() * tilesize/2f); - Core.camera.position.set(player); player.clearUnit(); for(var unit : Groups.unit){ @@ -338,14 +338,17 @@ public class MapEditorDialog extends Dialog implements Disposable{ Groups.weather.clear(); logic.play(); - if(player.team().core() == null){ - player.set(world.width() * tilesize/2f, world.height() * tilesize/2f); - var unit = (state.rules.hasEnv(Env.scorching) ? UnitTypes.evoke : UnitTypes.alpha).spawn(player.team(), player.x, player.y); - unit.spawnedByCore = true; - player.unit(unit); - } + Point2 center = view.project(Core.graphics.getWidth()/2f, Core.graphics.getHeight()/2f); - player.checkSpawn(); + CoreBuild best = player.bestCore(); + + player.set(center.x * tilesize, center.y * tilesize); + var unit = (best != null ? ((CoreBlock)best.block).unitType : (state.rules.hasEnv(Env.scorching) ? UnitTypes.evoke : UnitTypes.alpha)).spawn(editor.drawTeam, player.x, player.y); + unit.spawnedByCore = true; + player.unit(unit); + player.set(unit); + + Core.camera.position.set(unit.x, unit.y); }); } @@ -748,7 +751,11 @@ public class MapEditorDialog extends Dialog implements Disposable{ //ctrl keys (undo, redo, save) if(Core.input.ctrl()){ if(Core.input.keyTap(KeyCode.z)){ - editor.undo(); + if(Core.input.shift()){ + editor.redo(); + }else{ + editor.undo(); + } } if(Core.input.keyTap(KeyCode.y)){ @@ -827,7 +834,9 @@ public class MapEditorDialog extends Dialog implements Disposable{ if(i == 0) editor.drawBlock = block; - if(++i % 6 == 0){ + int cols = mobile ? 4 : 6; + + if(++i % cols == 0){ blockSelection.row(); } } diff --git a/core/src/mindustry/editor/MapGenerateDialog.java b/core/src/mindustry/editor/MapGenerateDialog.java index bb04261baf..19d435a6ac 100644 --- a/core/src/mindustry/editor/MapGenerateDialog.java +++ b/core/src/mindustry/editor/MapGenerateDialog.java @@ -185,7 +185,7 @@ public class MapGenerateDialog extends BaseDialog{ } //reset undo stack as generation... messes things up - editor.renderer.updateAll(); + editor.renderer.recache(); editor.clearOp(); } diff --git a/core/src/mindustry/editor/MapProcessorsDialog.java b/core/src/mindustry/editor/MapProcessorsDialog.java index 8867c2df13..9c687f08a8 100644 --- a/core/src/mindustry/editor/MapProcessorsDialog.java +++ b/core/src/mindustry/editor/MapProcessorsDialog.java @@ -41,7 +41,7 @@ public class MapProcessorsDialog extends BaseDialog{ foundAny = true; tile.setNet(Blocks.worldProcessor, Team.sharded, 0); if(ui.editor.isShown()){ - Vars.editor.renderer.updatePoint(x, y); + Vars.editor.renderer.updateStatic(x, y); } break outer; } diff --git a/core/src/mindustry/editor/MapRenderer.java b/core/src/mindustry/editor/MapRenderer.java index f3249f40ee..f943117b36 100644 --- a/core/src/mindustry/editor/MapRenderer.java +++ b/core/src/mindustry/editor/MapRenderer.java @@ -3,186 +3,220 @@ package mindustry.editor; import arc.*; import arc.graphics.*; import arc.graphics.g2d.*; -import arc.math.*; +import arc.graphics.gl.*; +import arc.math.geom.*; import arc.struct.*; import arc.util.*; import mindustry.content.*; -import mindustry.game.*; import mindustry.graphics.*; import mindustry.world.*; -import mindustry.world.blocks.environment.*; import static mindustry.Vars.*; public class MapRenderer implements Disposable{ - private static final int chunkSize = 64; - private IndexedRenderer[][] chunks; - private IntSet updates = new IntSet(); - private IntSet delayedUpdates = new IntSet(); - private TextureRegion clearEditor; + private static final int chunkSize = 60; + private static final Seq tmpTiles = new Seq<>(); + + private EditorSpriteCache[][] chunks; + private IntSet recacheChunks = new IntSet(); private int width, height; + private Shader shader; + public void resize(int width, int height){ - updates.clear(); - delayedUpdates.clear(); - if(chunks != null){ - for(int x = 0; x < chunks.length; x++){ - for(int y = 0; y < chunks[0].length; y++){ - chunks[x][y].dispose(); - } - } - } + dispose(); - chunks = new IndexedRenderer[(int)Math.ceil((float)width / chunkSize)][(int)Math.ceil((float)height / chunkSize)]; + recacheChunks.clear(); + chunks = new EditorSpriteCache[(int)Math.ceil((float)width / chunkSize)][(int)Math.ceil((float)height / chunkSize)]; - for(int x = 0; x < chunks.length; x++){ - for(int y = 0; y < chunks[0].length; y++){ - chunks[x][y] = new IndexedRenderer(chunkSize * chunkSize * 2); - } - } this.width = width; this.height = height; - updateAll(); + + recache(); } public void draw(float tx, float ty, float tw, float th){ - Draw.flush(); - clearEditor = Core.atlas.find("clear-editor"); + if(shader == null){ + shader = new Shader( + """ + attribute vec4 a_position; + attribute vec4 a_color; + attribute vec2 a_texCoord0; + uniform mat4 u_projTrans; + varying vec4 v_color; + varying vec2 v_texCoords; + void main(){ + v_color = a_color; + v_color.a = v_color.a * (255.0/254.0); + v_texCoords = a_texCoord0; + gl_Position = u_projTrans * a_position; + } + """, - updates.each(i -> render(i % width, i / width)); - updates.clear(); - - updates.addAll(delayedUpdates); - delayedUpdates.clear(); - - //???? - if(chunks == null){ - return; + """ + varying lowp vec4 v_color; + varying vec2 v_texCoords; + uniform sampler2D u_texture; + + void main(){ + gl_FragColor = v_color * texture2D(u_texture, v_texCoords); + } + """ + ); } - var texture = Core.atlas.find("clear-editor").texture; + Draw.flush(); + + //don't process terrain updates every frame (helps with lag on low end devices) + boolean doUpdate = Core.graphics.getFrameId() % 2 == 0; + + if(doUpdate) renderer.blocks.floor.checkChanges(); + + boolean prev = renderer.animateWater; + renderer.animateWater = false; + + Tmp.v3.set(Core.camera.position); + Core.camera.position.set(world.width()/2f * tilesize, world.height()/2f * tilesize); + Core.camera.width = 999999f; + Core.camera.height = 999999f; + Core.camera.mat.set(Draw.proj()).mul(Tmp.m3.setToTranslation(tx, ty).scale(tw / (width * tilesize), th / (height * tilesize)).translate(4f, 4f)); + renderer.blocks.floor.drawFloor(); + + Tmp.m2.set(Draw.proj()); + + //scissors are always enabled because this is drawn clipped in UI, make sure they don't interfere with drawing shadow events + Gl.disable(Gl.scissorTest); + + if(doUpdate) renderer.blocks.processShadows(); + + Gl.enable(Gl.scissorTest); + + Draw.proj(Core.camera.mat); + + Draw.shader(Shaders.darkness); + Draw.rect(Draw.wrap(renderer.blocks.getShadowBuffer().getTexture()), world.width() * tilesize/2f - tilesize/2f, world.height() * tilesize/2f - tilesize/2f, world.width() * tilesize, -world.height() * tilesize); + Draw.shader(); + + Draw.proj(Tmp.m2); + + renderer.blocks.floor.beginDraw(); + renderer.blocks.floor.drawLayer(CacheLayer.walls); + renderer.animateWater = prev; + + if(chunks == null) return; + + if(doUpdate){ + recacheChunks.each(i -> recacheChunk(Point2.x(i), Point2.y(i))); + recacheChunks.clear(); + } + + shader.bind(); + shader.setUniformMatrix4("u_projTrans", Core.camera.mat); for(int x = 0; x < chunks.length; x++){ for(int y = 0; y < chunks[0].length; y++){ - IndexedRenderer mesh = chunks[x][y]; + EditorSpriteCache mesh = chunks[x][y]; - if(mesh == null){ - continue; + if(mesh == null) continue; + + mesh.render(shader); + } + } + + Core.camera.position.set(Tmp.v3); + } + + void updateStatic(int x, int y){ + renderer.blocks.floor.recacheTile(x, y); + if(x > 0) renderer.blocks.floor.recacheTile(x - 1, y); + if(y > 0) renderer.blocks.floor.recacheTile(x, y - 1); + if(x < world.width() - 1) renderer.blocks.floor.recacheTile(x + 1, y); + if(y < world.height() - 1) renderer.blocks.floor.recacheTile(x, y + 1); + } + + void updateBlock(Tile tile){ + updateBlock(tile.x, tile.y); + renderer.blocks.updateShadowTile(tile); + } + + void updateBlock(int x, int y){ + recacheChunks.add(Point2.pack(x / chunkSize, y / chunkSize)); + } + + void recache(){ + renderer.blocks.floor.clearTiles(); + renderer.blocks.reload(); + + for(int x = 0; x < chunks.length; x++){ + for(int y = 0; y < chunks[0].length; y++){ + recacheChunk(x, y); + } + } + } + + void recacheChunk(int cx, int cy){ + if(chunks[cx][cy] != null){ + chunks[cx][cy].dispose(); + chunks[cx][cy] = null; + } + + EditorSpriteCache cache = new EditorSpriteCache(renderer.blocks.floor.getVertexBuffer()); + + TextureRegion teamRegion = Core.atlas.find("block-border"); + + tmpTiles.clear(); + + for(int x = cx * chunkSize; x < (cx + 1) * chunkSize; x++){ + for(int y = cy * chunkSize; y < (cy + 1) * chunkSize; y++){ + Tile tile = world.tile(x, y); + + if(tile != null && tile.block() != Blocks.air && tile.block().cacheLayer == CacheLayer.normal && tile.isCenter()){ + tmpTiles.add(tile); } - - mesh.getTransformMatrix().setToTranslation(tx, ty).scale(tw / (width * tilesize), th / (height * tilesize)); - mesh.setProjectionMatrix(Draw.proj()); - - mesh.render(texture); } } - } - public void updatePoint(int x, int y){ - updates.add(x + y * width); - } + tmpTiles.sort(Structs.comparingBool(b -> !b.block().synthetic())); - public void updateAll(){ - clearEditor = Core.atlas.find("clear-editor"); - for(int x = 0; x < width; x++){ - for(int y = 0; y < height; y++){ - render(x, y); - } - } - } + for(Tile tile : tmpTiles){ + int x = tile.x, y = tile.y; + Block block = tile.block(); - private TextureRegion getIcon(Block wall, int index){ - return !wall.editorIcon().found() ? - clearEditor : wall.variants > 0 ? - wall.editorVariantRegions()[Mathf.randomSeed(index, 0, wall.editorVariantRegions().length - 1)] : - wall.editorIcon(); - } + TextureRegion region = block.fullIcon; - private void render(int wx, int wy){ - int x = wx / chunkSize, y = wy / chunkSize; - if(x >= chunks.length || y >= chunks[0].length) return; - IndexedRenderer mesh = chunks[x][y]; - Tile tile = editor.tiles().getn(wx, wy); + float width = region.width * region.scl(), height = region.height * region.scl(); - Team team = tile.team(); - Floor floor = tile.floor(); - Floor overlay = tile.overlay(); - Block wall = tile.block(); - - TextureRegion region; - - int idxWall = (wx % chunkSize) + (wy % chunkSize) * chunkSize; - int idxDecal = (wx % chunkSize) + (wy % chunkSize) * chunkSize + chunkSize * chunkSize; - boolean center = tile.isCenter(); - boolean useSyntheticWall = wall.synthetic() || overlay.wallOre; - - //draw synthetic wall or floor OR standard wall if wall ore - if(wall != Blocks.air && useSyntheticWall){ - region = !center ? clearEditor : getIcon(wall, idxWall); - - float width = region.width * region.scl(), height = region.height * region.scl(), ox = wall.offset + (tilesize - width) / 2f, oy = wall.offset + (tilesize - height) / 2f; - - //force fit to tile - if(overlay.wallOre && !wall.synthetic()){ - width = height = tilesize; - ox = oy = 0f; - } - - mesh.draw(idxWall, region, - wx * tilesize + ox, - wy * tilesize + oy, + cache.draw(block.fullIcon, + x * tilesize + block.offset - width / 2f, + y * tilesize + block.offset - height / 2f, + width/2f, height/2f, width, height, - tile.build == null || !wall.rotate ? 0 : tile.build.rotdeg()); - }else{ - region = floor.editorVariantRegions()[Mathf.randomSeed(idxWall, 0, floor.editorVariantRegions().length - 1)]; + tile.build == null || !block.rotate ? 0 : tile.build.rotdeg(), + Color.whiteFloatBits); - mesh.draw(idxWall, region, wx * tilesize, wy * tilesize, 8, 8); + if(tile.build != null){ + cache.draw(teamRegion, + x * tilesize + block.offset - width / 2f, + y * tilesize + block.offset - height / 2f, + 0f, 0f, + teamRegion.width * teamRegion.scl(), teamRegion.height * teamRegion.scl(), + 0f, + tile.build.team.color.toFloatBits()); + } } - float offsetX = -((wall.size + 1) / 3) * tilesize, offsetY = -((wall.size + 1) / 3) * tilesize; + tmpTiles.clear(); - //draw non-synthetic wall or ore - if((wall.update || wall.destructible) && center){ - mesh.setColor(team.color); - region = Core.atlas.find("block-border-editor"); - if(wall.size == 2){ - offsetX += tilesize; - offsetY += tilesize; - } - }else if(!useSyntheticWall && wall != Blocks.air && center){ - region = getIcon(wall, idxWall); - - if(wall == Blocks.cliff){ - mesh.setColor(Tmp.c1.set(floor.mapColor).mul(1.6f)); - region = ((Cliff)Blocks.cliff).editorCliffs[tile.data & 0xff]; - } - - offsetX = tilesize / 2f - region.width * region.scl() / 2f; - offsetY = tilesize / 2f - region.height * region.scl() / 2f; - }else if((wall == Blocks.air || overlay.wallOre) && !overlay.isAir()){ - if(floor.isLiquid){ - mesh.setColor(Tmp.c1.set(1f, 1f, 1f, floor.overlayAlpha)); - } - region = overlay.editorVariantRegions()[Mathf.randomSeed(idxWall, 0, tile.overlay().editorVariantRegions().length - 1)]; - }else{ - region = clearEditor; + if(!cache.isEmpty()){ + cache.build(renderer.blocks.floor.getIndexData()); + chunks[cx][cy] = cache; } - - float width = region.width * region.scl(), height = region.height * region.scl(); - if(!wall.synthetic() && wall != Blocks.air && !wall.isMultiblock()){ - offsetX = offsetY = 0f; - width = height = tilesize; - } - - mesh.draw(idxDecal, region, wx * tilesize + offsetX, wy * tilesize + offsetY, width, height); - mesh.setColor(Color.white); } @Override public void dispose(){ - if(chunks == null){ - return; - } + if(chunks == null) return; + for(int x = 0; x < chunks.length; x++){ for(int y = 0; y < chunks[0].length; y++){ if(chunks[x][y] != null){ diff --git a/core/src/mindustry/editor/MapView.java b/core/src/mindustry/editor/MapView.java index e4f571bfb0..e030350238 100644 --- a/core/src/mindustry/editor/MapView.java +++ b/core/src/mindustry/editor/MapView.java @@ -11,6 +11,7 @@ import arc.scene.*; import arc.scene.event.*; import arc.scene.ui.layout.*; import arc.util.*; +import mindustry.*; import mindustry.graphics.*; import mindustry.input.*; import mindustry.ui.*; @@ -18,7 +19,7 @@ import mindustry.ui.*; import static mindustry.Vars.*; public class MapView extends Element implements GestureListener{ - EditorTool tool = EditorTool.pencil; + EditorTool tool = Vars.mobile ? EditorTool.zoom : EditorTool.pencil; private float offsetx, offsety; private float zoom = 1f; private boolean grid = false; @@ -204,7 +205,7 @@ public class MapView extends Element implements GestureListener{ zoom = Mathf.clamp(zoom, 0.2f, 20f); } - Point2 project(float x, float y){ + public Point2 project(float x, float y){ float ratio = 1f / ((float)editor.width() / editor.height()); float size = Math.min(width, height); float sclwidth = size * zoom; diff --git a/core/src/mindustry/entities/comp/BuilderComp.java b/core/src/mindustry/entities/comp/BuilderComp.java index 65bacbbd2c..3ab723995c 100644 --- a/core/src/mindustry/entities/comp/BuilderComp.java +++ b/core/src/mindustry/entities/comp/BuilderComp.java @@ -145,7 +145,7 @@ abstract class BuilderComp implements Posc, Statusc, Teamc, Rotc{ !Structs.contains(current.block.requirements, i -> !core.items.has(i.item, Math.min(Mathf.round(i.amount * state.rules.buildCostMultiplier), 1))); if(hasAll){ - Call.beginPlace(self(), current.block, team, current.x, current.y, current.rotation); + Call.beginPlace(self(), current.block, team, current.x, current.y, current.rotation, current.block.instantBuild ? current.config : null); if(!net.client() && current.block.instantBuild){ if(plans.size > 0){ diff --git a/core/src/mindustry/graphics/BlockRenderer.java b/core/src/mindustry/graphics/BlockRenderer.java index 4949a90bc3..83e8990426 100644 --- a/core/src/mindustry/graphics/BlockRenderer.java +++ b/core/src/mindustry/graphics/BlockRenderer.java @@ -64,48 +64,7 @@ public class BlockRenderer{ }); Events.on(WorldLoadEvent.class, event -> { - blockTree = new BlockQuadtree(new Rect(0, 0, world.unitWidth(), world.unitHeight())); - blockLightTree = new BlockLightQuadtree(new Rect(0, 0, world.unitWidth(), world.unitHeight())); - floorTree = new FloorQuadtree(new Rect(0, 0, world.unitWidth(), world.unitHeight())); - - shadowEvents.clear(); - updateFloors.clear(); - lastCamY = lastCamX = -99; //invalidate camera position so blocks get updated - hadMapLimit = state.rules.limitMapArea; - - shadows.getTexture().setFilter(TextureFilter.linear, TextureFilter.linear); - shadows.resize(world.width(), world.height()); - shadows.begin(); - Core.graphics.clear(Color.white); - Draw.proj().setOrtho(0, 0, shadows.getWidth(), shadows.getHeight()); - - Draw.color(blendShadowColor); - - for(Tile tile : world.tiles){ - recordIndex(tile); - - if(tile.floor().updateRender(tile)){ - updateFloors.add(new UpdateRenderState(tile, tile.floor())); - } - - if(tile.overlay().updateRender(tile)){ - updateFloors.add(new UpdateRenderState(tile, tile.overlay())); - } - - if(tile.build != null && (tile.team() == player.team() || !state.rules.fog || (tile.build.visibleFlags & (1L << player.team().id)) != 0)){ - tile.build.wasVisible = true; - } - - if(tile.block().displayShadow(tile) && (tile.build == null || tile.build.wasVisible)){ - Fill.rect(tile.x + 0.5f, tile.y + 0.5f, 1, 1); - } - } - - Draw.flush(); - Draw.color(); - shadows.end(); - - updateDarkness(); + reload(); }); //sometimes darkness gets disabled. @@ -150,6 +109,51 @@ public class BlockRenderer{ }); } + public void reload(){ + blockTree = new BlockQuadtree(new Rect(0, 0, world.unitWidth(), world.unitHeight())); + blockLightTree = new BlockLightQuadtree(new Rect(0, 0, world.unitWidth(), world.unitHeight())); + floorTree = new FloorQuadtree(new Rect(0, 0, world.unitWidth(), world.unitHeight())); + + shadowEvents.clear(); + updateFloors.clear(); + lastCamY = lastCamX = -99; //invalidate camera position so blocks get updated + hadMapLimit = state.rules.limitMapArea; + + shadows.getTexture().setFilter(TextureFilter.linear, TextureFilter.linear); + shadows.resize(world.width(), world.height()); + shadows.begin(); + Core.graphics.clear(Color.white); + Draw.proj().setOrtho(0, 0, shadows.getWidth(), shadows.getHeight()); + + Draw.color(blendShadowColor); + + for(Tile tile : world.tiles){ + recordIndex(tile); + + if(tile.floor().updateRender(tile)){ + updateFloors.add(new UpdateRenderState(tile, tile.floor())); + } + + if(tile.overlay().updateRender(tile)){ + updateFloors.add(new UpdateRenderState(tile, tile.overlay())); + } + + if(tile.build != null && (tile.team() == player.team() || !state.rules.fog || (tile.build.visibleFlags & (1L << player.team().id)) != 0)){ + tile.build.wasVisible = true; + } + + if(tile.block().displayShadow(tile) && (tile.build == null || tile.build.wasVisible)){ + Fill.rect(tile.x + 0.5f, tile.y + 0.5f, 1, 1); + } + } + + Draw.flush(); + Draw.color(); + shadows.end(); + + updateDarkness(); + } + public void updateDarkness(){ darkEvents.clear(); dark.getTexture().setFilter(TextureFilter.linear); @@ -197,6 +201,10 @@ public class BlockRenderer{ } } + public FrameBuffer getShadowBuffer(){ + return shadows; + } + public void removeFloorIndex(Tile tile){ if(indexFloor(tile)) floorTree.remove(tile); } @@ -294,7 +302,7 @@ public class BlockRenderer{ } } - public void drawShadows(){ + public void processShadows(){ if(!shadowEvents.isEmpty()){ Draw.flush(); @@ -315,6 +323,10 @@ public class BlockRenderer{ Draw.proj(camera); } + } + + public void drawShadows(){ + processShadows(); float ww = world.width() * tilesize, wh = world.height() * tilesize; float x = camera.position.x + tilesize / 2f, y = camera.position.y + tilesize / 2f; @@ -511,6 +523,10 @@ public class BlockRenderer{ } } + public void updateShadowTile(Tile tile){ + shadowEvents.add(tile); + } + static class BlockQuadtree extends QuadTree{ public BlockQuadtree(Rect bounds){ diff --git a/core/src/mindustry/graphics/CacheLayer.java b/core/src/mindustry/graphics/CacheLayer.java index 31de034b7f..fff6b5f332 100644 --- a/core/src/mindustry/graphics/CacheLayer.java +++ b/core/src/mindustry/graphics/CacheLayer.java @@ -97,20 +97,20 @@ public class CacheLayer{ @Override public void begin(){ - if(!Core.settings.getBool("animatedwater")) return; + if(!renderer.animateWater) return; renderer.effectBuffer.begin(); Core.graphics.clear(Color.clear); - renderer.blocks.floor.beginc(); + renderer.blocks.floor.beginDraw(); } @Override public void end(){ - if(!Core.settings.getBool("animatedwater")) return; + if(!renderer.animateWater) return; renderer.effectBuffer.end(); renderer.effectBuffer.blit(shader); - renderer.blocks.floor.beginc(); + renderer.blocks.floor.beginDraw(); } } } diff --git a/core/src/mindustry/graphics/FloorRenderer.java b/core/src/mindustry/graphics/FloorRenderer.java index 754d6d7d27..31922c8877 100644 --- a/core/src/mindustry/graphics/FloorRenderer.java +++ b/core/src/mindustry/graphics/FloorRenderer.java @@ -111,10 +111,21 @@ public class FloorRenderer{ Events.on(WorldLoadEvent.class, event -> clearTiles()); } + public IndexData getIndexData(){ + return indexData; + } + + public float[] getVertexBuffer(){ + return vertices; + } + /** Queues up a cache change for a tile. Only runs in render loop. */ public void recacheTile(Tile tile){ - //recaching all layers may not be necessary - recacheSet.add(Point2.pack(tile.x / chunksize, tile.y / chunksize)); + recacheTile(tile.x, tile.y); + } + + public void recacheTile(int x, int y){ + recacheSet.add(Point2.pack(x / chunksize, y / chunksize)); } public void drawFloor(){ @@ -127,10 +138,10 @@ public class FloorRenderer{ float pad = tilesize/2f; int - minx = (int)((camera.position.x - camera.width/2f - pad) / chunkunits), - miny = (int)((camera.position.y - camera.height/2f - pad) / chunkunits), - maxx = Mathf.ceil((camera.position.x + camera.width/2f + pad) / chunkunits), - maxy = Mathf.ceil((camera.position.y + camera.height/2f + pad) / chunkunits); + minx = Math.max((int)((camera.position.x - camera.width/2f - pad) / chunkunits), 0), + miny = Math.max((int)((camera.position.y - camera.height/2f - pad) / chunkunits), 0), + maxx = Math.min(Mathf.ceil((camera.position.x + camera.width/2f + pad) / chunkunits), cache.length), + maxy = Math.min(Mathf.ceil((camera.position.y + camera.height/2f + pad) / chunkunits), cache[0].length); int layers = CacheLayer.all.length; @@ -176,14 +187,6 @@ public class FloorRenderer{ underwaterDraw.clear(); } - public void beginc(){ - shader.bind(); - shader.setUniformMatrix4("u_projectionViewMatrix", Core.camera.mat); - - //only ever use the base environment texture - texture.bind(0); - } - public void checkChanges(){ if(recacheSet.size > 0){ //recache one chunk at a time @@ -208,7 +211,11 @@ public class FloorRenderer{ Draw.flush(); - beginc(); + shader.bind(); + shader.setUniformMatrix4("u_projectionViewMatrix", Core.camera.mat); + + //only ever use the base environment texture + texture.bind(0); Gl.enable(Gl.blend); } @@ -221,10 +228,10 @@ public class FloorRenderer{ Camera camera = Core.camera; int - minx = (int)((camera.position.x - camera.width/2f - pad) / chunkunits), - miny = (int)((camera.position.y - camera.height/2f - pad) / chunkunits), - maxx = Mathf.ceil((camera.position.x + camera.width/2f + pad) / chunkunits), - maxy = Mathf.ceil((camera.position.y + camera.height/2f + pad) / chunkunits); + minx = Math.max((int)((camera.position.x - camera.width/2f - pad) / chunkunits), 0), + miny = Math.max((int)((camera.position.y - camera.height/2f - pad) / chunkunits), 0), + maxx = Math.min(Mathf.ceil((camera.position.x + camera.width/2f + pad) / chunkunits), cache.length), + maxy = Math.min(Mathf.ceil((camera.position.y + camera.height/2f + pad) / chunkunits), cache[0].length); layer.begin(); @@ -336,7 +343,7 @@ public class FloorRenderer{ (cx+1) * tilesize * chunksize + tilesize/2f, (cy+1) * tilesize * chunksize + tilesize/2f); mesh.setVertices(vertices, 0, vidx); - //all vertices are shared + //all indices are shared and identical mesh.indices = indexData; return mesh; @@ -514,7 +521,14 @@ public class FloorRenderer{ @Override protected void draw(Texture texture, float[] spriteVertices, int offset, int count){ - throw new IllegalArgumentException("cache vertices unsupported"); + if(spriteVertices.length != spriteSize){ + throw new IllegalArgumentException("cached vertices must be in non-mixcolor format (20 per sprite, 5 per vertex)"); + } + + float[] verts = vertices; + int idx = vidx; + System.arraycopy(spriteVertices, offset, verts, idx, spriteSize); + vidx += spriteSize; } } } diff --git a/core/src/mindustry/graphics/IndexedRenderer.java b/core/src/mindustry/graphics/IndexedRenderer.java deleted file mode 100644 index 954bc13153..0000000000 --- a/core/src/mindustry/graphics/IndexedRenderer.java +++ /dev/null @@ -1,219 +0,0 @@ -package mindustry.graphics; - -import arc.graphics.*; -import arc.graphics.g2d.*; -import arc.graphics.gl.*; -import arc.math.*; -import arc.util.*; - -public class IndexedRenderer implements Disposable{ - private static final int vsize = 5; - - private final static Shader program = new Shader( - """ - attribute vec4 a_position; - attribute vec4 a_color; - attribute vec2 a_texCoord0; - uniform mat4 u_projTrans; - varying vec4 v_color; - varying vec2 v_texCoords; - void main(){ - v_color = a_color; - v_color.a = v_color.a * (255.0/254.0); - v_texCoords = a_texCoord0; - gl_Position = u_projTrans * a_position; - } - """, - - """ - varying lowp vec4 v_color; - varying vec2 v_texCoords; - uniform sampler2D u_texture; - void main(){ - gl_FragColor = v_color * texture2D(u_texture, v_texCoords); - } - """ - ); - private static final float[] tmpVerts = new float[vsize * 6]; - - private Mesh mesh; - - private Mat projMatrix = new Mat(); - private Mat transMatrix = new Mat(); - private Mat combined = new Mat(); - private float color = Color.white.toFloatBits(); - - public IndexedRenderer(int sprites){ - resize(sprites); - } - - public void render(Texture texture){ - Gl.enable(Gl.blend); - - updateMatrix(); - - program.bind(); - texture.bind(); - - program.setUniformMatrix4("u_projTrans", combined); - - mesh.render(program, Gl.triangles, 0, mesh.getMaxVertices()); - } - - public void setColor(Color color){ - this.color = color.toFloatBits(); - } - - public void draw(int index, TextureRegion region, float x, float y, float w, float h){ - float fx2 = x + w; - float fy2 = y + h; - float u = region.u; - float v = region.v2; - float u2 = region.u2; - float v2 = region.v; - - float[] vertices = tmpVerts; - float color = this.color; - - int idx = 0; - vertices[idx++] = x; - vertices[idx++] = y; - vertices[idx++] = color; - vertices[idx++] = u; - vertices[idx++] = v; - - vertices[idx++] = x; - vertices[idx++] = fy2; - vertices[idx++] = color; - vertices[idx++] = u; - vertices[idx++] = v2; - - vertices[idx++] = fx2; - vertices[idx++] = fy2; - vertices[idx++] = color; - vertices[idx++] = u2; - vertices[idx++] = v2; - - //tri2 - vertices[idx++] = fx2; - vertices[idx++] = fy2; - vertices[idx++] = color; - vertices[idx++] = u2; - vertices[idx++] = v2; - - vertices[idx++] = fx2; - vertices[idx++] = y; - vertices[idx++] = color; - vertices[idx++] = u2; - vertices[idx++] = v; - - vertices[idx++] = x; - vertices[idx++] = y; - vertices[idx++] = color; - vertices[idx++] = u; - vertices[idx++] = v; - - mesh.updateVertices(index * vsize * 6, vertices); - } - - public void draw(int index, TextureRegion region, float x, float y, float w, float h, float rotation){ - float u = region.u; - float v = region.v2; - float u2 = region.u2; - float v2 = region.v; - - float originX = w / 2, originY = h / 2; - - float cos = Mathf.cosDeg(rotation); - float sin = Mathf.sinDeg(rotation); - - float fx = -originX; - float fy = -originY; - float fx2 = w - originX; - float fy2 = h - originY; - - float worldOriginX = x + originX; - float worldOriginY = y + originY; - - float x1 = cos * fx - sin * fy + worldOriginX; - float y1 = sin * fx + cos * fy + worldOriginY; - float x2 = cos * fx - sin * fy2 + worldOriginX; - float y2 = sin * fx + cos * fy2 + worldOriginY; - float x3 = cos * fx2 - sin * fy2 + worldOriginX; - float y3 = sin * fx2 + cos * fy2 + worldOriginY; - float x4 = x1 + (x3 - x2); - float y4 = y3 - (y2 - y1); - - float[] vertices = tmpVerts; - float color = this.color; - - int idx = 0; - vertices[idx++] = x1; - vertices[idx++] = y1; - vertices[idx++] = color; - vertices[idx++] = u; - vertices[idx++] = v; - - vertices[idx++] = x2; - vertices[idx++] = y2; - vertices[idx++] = color; - vertices[idx++] = u; - vertices[idx++] = v2; - - vertices[idx++] = x3; - vertices[idx++] = y3; - vertices[idx++] = color; - vertices[idx++] = u2; - vertices[idx++] = v2; - - //tri2 - vertices[idx++] = x3; - vertices[idx++] = y3; - vertices[idx++] = color; - vertices[idx++] = u2; - vertices[idx++] = v2; - - vertices[idx++] = x4; - vertices[idx++] = y4; - vertices[idx++] = color; - vertices[idx++] = u2; - vertices[idx++] = v; - - vertices[idx++] = x1; - vertices[idx++] = y1; - vertices[idx++] = color; - vertices[idx++] = u; - vertices[idx++] = v; - - mesh.updateVertices(index * vsize * 6, vertices); - } - - public Mat getTransformMatrix(){ - return transMatrix; - } - - public void setProjectionMatrix(Mat matrix){ - projMatrix = matrix; - } - - public void resize(int sprites){ - if(mesh != null) mesh.dispose(); - - mesh = new Mesh(true, 6 * sprites, 0, - VertexAttribute.position, - VertexAttribute.color, - VertexAttribute.texCoords); - - //TODO why is this the only way to get it working properly? it should not need an array - mesh.setVertices(new float[6 * sprites * vsize]); - } - - private void updateMatrix(){ - combined.set(projMatrix).mul(transMatrix); - } - - @Override - public void dispose(){ - mesh.dispose(); - } -} diff --git a/core/src/mindustry/graphics/MinimapRenderer.java b/core/src/mindustry/graphics/MinimapRenderer.java index de42d08957..1eb24a901b 100644 --- a/core/src/mindustry/graphics/MinimapRenderer.java +++ b/core/src/mindustry/graphics/MinimapRenderer.java @@ -360,6 +360,7 @@ public class MinimapRenderer{ if(tile == null) return 0; Block real = realBlock(tile); int bc = real.minimapColor(tile); + if(bc == 0 && tile.block() == Blocks.air && tile.overlay() == Blocks.air) bc = tile.floor().minimapColor(tile); Color color = Tmp.c1.set(bc == 0 ? MapIO.colorFor(real, tile.floor(), tile.overlay(), tile.team()) : bc); color.mul(1f - Mathf.clamp(world.getDarkness(tile.x, tile.y) / 4f)); diff --git a/core/src/mindustry/graphics/MultiPacker.java b/core/src/mindustry/graphics/MultiPacker.java index 42bc8d00d7..f3ebb3b5b5 100644 --- a/core/src/mindustry/graphics/MultiPacker.java +++ b/core/src/mindustry/graphics/MultiPacker.java @@ -118,8 +118,7 @@ public class MultiPacker implements Disposable{ environment(4096), ui(4096), - rubble(4096, 2048), - editor(4096, 2048); + rubble(4096, 2048); public static final PageType[] all = values(); diff --git a/core/src/mindustry/input/InputHandler.java b/core/src/mindustry/input/InputHandler.java index d47747fafc..523ba91b87 100644 --- a/core/src/mindustry/input/InputHandler.java +++ b/core/src/mindustry/input/InputHandler.java @@ -1627,6 +1627,9 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ /** Draws a placement icon for a specific block. */ protected void drawPlan(int x, int y, Block block, int rotation){ bplan.set(x, y, rotation, block); + if(block.saveConfig){ + bplan.config = block.lastConfig; + } bplan.animScale = 1f; block.drawPlan(bplan, allPlans(), validPlace(x, y, block, rotation)); } diff --git a/core/src/mindustry/io/MapIO.java b/core/src/mindustry/io/MapIO.java index d918ff29b0..e65b7992fb 100644 --- a/core/src/mindustry/io/MapIO.java +++ b/core/src/mindustry/io/MapIO.java @@ -141,6 +141,23 @@ public class MapIO{ } return tile; } + + @Override + public void onReadTileData(){ + //colored floor/wall tile data will affect the map preview + + if(!tile.block().synthetic() && tile.block() != Blocks.air){ + int color = tile.block().minimapColor(tile); + if(color != 0){ + walls.set(tile.x, walls.height - 1 - tile.y, color); + } + }else if(tile.overlay() == Blocks.air && tile.block() == Blocks.air){ + int color = tile.floor().minimapColor(tile); + if(color != 0){ + floors.set(tile.x, floors.height - 1 - tile.y, color); + } + } + } })); floors.draw(walls, true); @@ -156,7 +173,14 @@ public class MapIO{ for(int x = 0; x < pixmap.width; x++){ for(int y = 0; y < pixmap.height; y++){ Tile tile = tiles.getn(x, y); - pixmap.set(x, pixmap.height - 1 - y, colorFor(tile.block(), tile.floor(), tile.overlay(), tile.team())); + int color = 0; + if(!tile.block().synthetic() && tile.block() != Blocks.air){ + color = tile.block().minimapColor(tile); + }else if(tile.overlay() == Blocks.air && tile.block() == Blocks.air){ + color = tile.floor().minimapColor(tile); + } + if(color == 0) color = colorFor(tile.block(), tile.floor(), tile.overlay(), tile.team()); + pixmap.set(x, pixmap.height - 1 - y, color); } } return pixmap; @@ -202,7 +226,7 @@ public class MapIO{ for(Tile tile : tiles){ //default to stone floor if(tile.floor() == Blocks.air){ - tile.setFloorUnder((Floor)Blocks.stone); + tile.setFloor((Floor)Blocks.stone); } } } diff --git a/core/src/mindustry/io/SaveIO.java b/core/src/mindustry/io/SaveIO.java index 161b0681a4..a575c10056 100644 --- a/core/src/mindustry/io/SaveIO.java +++ b/core/src/mindustry/io/SaveIO.java @@ -20,7 +20,7 @@ public class SaveIO{ /** Save format header. */ public static final byte[] header = {'M', 'S', 'A', 'V'}; public static final IntMap versions = new IntMap<>(); - public static final Seq versionArray = Seq.with(new Save1(), new Save2(), new Save3(), new Save4(), new Save5(), new Save6(), new Save7(), new Save8()); + public static final Seq versionArray = Seq.with(new Save1(), new Save2(), new Save3(), new Save4(), new Save5(), new Save6(), new Save7(), new Save8(), new Save9()); static{ for(SaveVersion version : versionArray){ diff --git a/core/src/mindustry/io/SaveVersion.java b/core/src/mindustry/io/SaveVersion.java index 70cb4b3f46..9ddb6519a2 100644 --- a/core/src/mindustry/io/SaveVersion.java +++ b/core/src/mindustry/io/SaveVersion.java @@ -209,7 +209,7 @@ public abstract class SaveVersion extends SaveFileReader{ //floor + overlay for(int i = 0; i < world.width() * world.height(); i++){ - Tile tile = world.rawTile(i % world.width(), i / world.width()); + Tile tile = world.tiles.geti(i); stream.writeShort(tile.floorID()); stream.writeShort(tile.overlayID()); int consecutives = 0; @@ -230,16 +230,26 @@ public abstract class SaveVersion extends SaveFileReader{ //blocks for(int i = 0; i < world.width() * world.height(); i++){ - Tile tile = world.rawTile(i % world.width(), i / world.width()); + Tile tile = world.tiles.geti(i); stream.writeShort(tile.blockID()); - boolean savedata = tile.floor().saveData || tile.overlay().saveData || tile.block().saveData; + boolean savedata = tile.shouldSaveData(); - byte packed = (byte)((tile.build != null ? 1 : 0) | (savedata ? 2 : 0)); + //in the old version, the second bit was set to indicate presence of data, but that approach was flawed - it didn't allow buildings + data on the same tile + //so now the third bit is used instead + byte packed = (byte)((tile.build != null ? 1 : 0) | (savedata ? 4 : 0)); - //make note of whether there was an entity/rotation here + //make note of whether there was an entity or custom tile data here stream.writeByte(packed); + if(savedata){ + //the new 'extra data' format writes 7 bytes of data instead of 1 + stream.writeByte(tile.data); + stream.writeByte(tile.floorData); + stream.writeByte(tile.overlayData); + stream.writeInt(tile.extraData); + } + //only write the entity for multiblocks once - in the center if(tile.build != null){ if(tile.isCenter()){ @@ -251,16 +261,14 @@ public abstract class SaveVersion extends SaveFileReader{ }else{ stream.writeBoolean(false); } - }else if(savedata){ - stream.writeByte(tile.data); - }else{ + }else if(!savedata){ //don't write consecutive blocks when there is custom data //write consecutive non-entity blocks int consecutives = 0; for(int j = i + 1; j < world.width() * world.height() && consecutives < 255; j++){ Tile nextTile = world.rawTile(j % world.width(), j / world.width()); - if(nextTile.blockID() != tile.blockID()){ + if(nextTile.blockID() != tile.blockID() || savedata != nextTile.shouldSaveData()){ break; } @@ -310,7 +318,19 @@ public abstract class SaveVersion extends SaveFileReader{ boolean isCenter = true; byte packedCheck = stream.readByte(); boolean hadEntity = (packedCheck & 1) != 0; - boolean hadData = (packedCheck & 2) != 0; + //old data format (bit 2): 1 byte only if no building is present + //new data format (bit 3): 7 bytes (3x block-specific bytes + 1x 4-byte extra data int) + boolean hadDataOld = (packedCheck & 2) != 0, hadDataNew = (packedCheck & 4) != 0; + + byte data = 0, floorData = 0, overlayData = 0; + int extraData = 0; + + if(hadDataNew){ + data = stream.readByte(); + floorData = stream.readByte(); + overlayData = stream.readByte(); + extraData = stream.readInt(); + } if(hadEntity){ isCenter = stream.readBoolean(); @@ -321,6 +341,15 @@ public abstract class SaveVersion extends SaveFileReader{ tile.setBlock(block); } + //must be assigned after setBlock, because that can reset data + if(hadDataNew){ + tile.data = data; + tile.floorData = floorData; + tile.overlayData = overlayData; + tile.extraData = extraData; + context.onReadTileData(); + } + if(hadEntity){ if(isCenter){ //only read entity for center blocks if(block.hasBuilding()){ @@ -339,9 +368,12 @@ public abstract class SaveVersion extends SaveFileReader{ context.onReadBuilding(); } - }else if(hadData){ - tile.setBlock(block); - tile.data = stream.readByte(); + }else if(hadDataOld || hadDataNew){ //never read consecutive blocks if there's any kind of data + if(hadDataOld){ + tile.setBlock(block); + //the old data format was only read in the case where there is no building, and only contained a single byte + tile.data = stream.readByte(); + } }else{ int consecutives = stream.readUnsignedByte(); diff --git a/core/src/mindustry/io/versions/Save8.java b/core/src/mindustry/io/versions/Save8.java index 8dc1c0ca01..b65984b044 100644 --- a/core/src/mindustry/io/versions/Save8.java +++ b/core/src/mindustry/io/versions/Save8.java @@ -2,6 +2,7 @@ package mindustry.io.versions; import mindustry.io.*; +/** Adds support for the marker binary data region. The code is unchanged here, because it was easier to add a >= 8 check in the SaveVersion class itself. */ public class Save8 extends SaveVersion{ public Save8(){ diff --git a/core/src/mindustry/io/versions/Save9.java b/core/src/mindustry/io/versions/Save9.java new file mode 100644 index 0000000000..08d1786e18 --- /dev/null +++ b/core/src/mindustry/io/versions/Save9.java @@ -0,0 +1,11 @@ +package mindustry.io.versions; + +import mindustry.io.*; + +/** Adds support for the new 7-byte custom tile data. This can read Save8 data, but Save8 doesn't know how to handle this version's output, thus the version change. */ +public class Save9 extends SaveVersion{ + + public Save9(){ + super(9); + } +} diff --git a/core/src/mindustry/mod/Mods.java b/core/src/mindustry/mod/Mods.java index a21eed63c1..6a21fb042e 100644 --- a/core/src/mindustry/mod/Mods.java +++ b/core/src/mindustry/mod/Mods.java @@ -273,7 +273,6 @@ public class Mods implements Loadable{ ObjectMap pageTypes = ObjectMap.of( Core.atlas.find("white").texture, PageType.main, Core.atlas.find("stone1").texture, PageType.environment, - Core.atlas.find("clear-editor").texture, PageType.editor, Core.atlas.find("whiteui").texture, PageType.ui, Core.atlas.find("rubble-1-0").texture, PageType.rubble ); @@ -405,7 +404,6 @@ public class Mods implements Loadable{ String path = file.path(); return path.contains("sprites/blocks/environment") || path.contains("sprites-override/blocks/environment") ? PageType.environment : - path.contains("sprites/editor") || path.contains("sprites-override/editor") ? PageType.editor : path.contains("sprites/rubble") || path.contains("sprites-override/rubble") ? PageType.rubble : path.contains("sprites/ui") || path.contains("sprites-override/ui") ? PageType.ui : PageType.main; diff --git a/core/src/mindustry/ui/fragments/HudFragment.java b/core/src/mindustry/ui/fragments/HudFragment.java index cac1defa52..969a4e7d9f 100644 --- a/core/src/mindustry/ui/fragments/HudFragment.java +++ b/core/src/mindustry/ui/fragments/HudFragment.java @@ -72,12 +72,28 @@ public class HudFragment{ } }); + Table[] configTable = {null}; + Block[] lastBlock = {null}; + cont.table(search -> { search.image(Icon.zoom).padRight(8); search.field("", text -> rebuildBlockSelection(blockSelection, text)).growX() .name("editor/search").maxTextLength(maxNameLength).get().setMessageText("@players.search"); }).growX().pad(-2).padLeft(6f); cont.row(); + cont.collapser(t -> { + configTable[0] = t; + }, () -> control.input.block != null && control.input.block.editorConfigurable).with(c -> c.setEnforceMinSize(true)).update(col -> { + + if(lastBlock[0] != control.input.block){ + configTable[0].clear(); + if(control.input.block != null){ + control.input.block.buildEditorConfig(configTable[0]); + col.invalidateHierarchy(); + } + lastBlock[0] = control.input.block; + } + }).growX().row(); cont.add(pane).expandY().top().left(); rebuildBlockSelection(blockSelection, ""); diff --git a/core/src/mindustry/ui/fragments/PlacementFragment.java b/core/src/mindustry/ui/fragments/PlacementFragment.java index 60b1ab27b1..b6b4ad7846 100644 --- a/core/src/mindustry/ui/fragments/PlacementFragment.java +++ b/core/src/mindustry/ui/fragments/PlacementFragment.java @@ -126,41 +126,44 @@ public class PlacementFragment{ } boolean updatePick(InputHandler input){ - if(Core.input.keyTap(Binding.pick) && player.isBuilder() && !Core.scene.hasDialog()){ //mouse eyedropper select - var build = world.buildWorld(Core.input.mouseWorld().x, Core.input.mouseWorld().y); + Tile tile = world.tileWorld(Core.input.mouseWorldX(), Core.input.mouseWorldY()); + if(tile != null && Core.input.keyTap(Binding.pick) && player.isBuilder() && !Core.scene.hasDialog()){ //mouse eyedropper select + var build = tile.build; //can't middle click buildings in fog if(build != null && build.inFogTo(player.team())){ build = null; } - Block tryRecipe = build == null ? null : build instanceof ConstructBuild c ? c.current : build.block; + Block tryBlock = build == null ? null : build instanceof ConstructBuild c ? c.current : build.block; Object tryConfig = build == null || !build.block.copyConfig ? null : build.config(); for(BuildPlan req : player.unit().plans()){ if(!req.breaking && req.block.bounds(req.x, req.y, Tmp.r1).contains(Core.input.mouseWorld())){ - tryRecipe = req.block; + tryBlock = req.block; tryConfig = req.config; break; } } - if(tryRecipe == null && state.rules.editor){ - var tile = world.tileWorld(Core.input.mouseWorldX(), Core.input.mouseWorldY()); - if(tile != null){ - tryRecipe = + if(tryBlock == null && state.rules.editor){ + tryBlock = tile.block() != Blocks.air ? tile.block() : tile.overlay() != Blocks.air ? tile.overlay() : tile.floor() != Blocks.air ? tile.floor() : null; - } } - if(tryRecipe != null && ((tryRecipe.isVisible() && unlocked(tryRecipe)) || state.rules.editor)){ - input.block = tryRecipe; - tryRecipe.lastConfig = tryConfig; - if(tryRecipe.isVisible()){ + if(tryBlock != null && build == null && tryConfig == null){ + tryConfig = tryBlock.getConfig(tile); + } + + if(tryBlock != null && ((tryBlock.isVisible() && unlocked(tryBlock)) || state.rules.editor)){ + input.block = tryBlock; + tryBlock.lastConfig = tryConfig; + if(tryBlock.isVisible()){ currentCategory = input.block.category; } + tryBlock.onPicked(tile); return true; } } diff --git a/core/src/mindustry/world/Block.java b/core/src/mindustry/world/Block.java index c4989411b4..a1a07a6736 100644 --- a/core/src/mindustry/world/Block.java +++ b/core/src/mindustry/world/Block.java @@ -80,6 +80,8 @@ public class Block extends UnlockableContent implements Senseable{ public boolean displayFlow = true; /** whether this block is visible in the editor */ public boolean inEditor = true; + /** if true, {@link #buildEditorConfig(Table)} will be called for configuring this block in the editor. */ + public boolean editorConfigurable; /** the last configuration value applied to this block. */ public @Nullable Object lastConfig; /** whether to save the last config and apply it to newly placed blocks */ @@ -380,11 +382,10 @@ public class Block extends UnlockableContent implements Senseable{ protected Seq consumeBuilder = new Seq<>(); protected TextureRegion[] generatedIcons; - protected TextureRegion[] editorVariantRegions; /** Regions indexes from icons() that are rotated. If either of these is not -1, other regions won't be rotated in ConstructBlocks. */ public int regionRotated1 = -1, regionRotated2 = -1; - public TextureRegion region, editorIcon; + public TextureRegion region; public @Load("@-shadow") TextureRegion customShadowRegion; public @Load("@-team") TextureRegion teamRegion; public TextureRegion[] teamRegions, variantRegions, variantShadowRegions; @@ -566,6 +567,11 @@ public class Block extends UnlockableContent implements Senseable{ return forceDark; } + /** If true, the 'map edge' darkness will be applied to this block. */ + public boolean isDarkened(Tile tile){ + return solid && ((!synthetic() && fillsTile) || checkForceDark(tile)); + } + @Override public void setStats(){ super.setStats(); @@ -857,24 +863,6 @@ public class Block extends UnlockableContent implements Senseable{ } } - /** Never use outside of the editor! */ - public TextureRegion editorIcon(){ - return editorIcon == null ? (editorIcon = Core.atlas.find(name + "-icon-editor")) : editorIcon; - } - - /** Never use outside of the editor! */ - public TextureRegion[] editorVariantRegions(){ - if(editorVariantRegions == null){ - variantRegions(); - editorVariantRegions = new TextureRegion[variantRegions.length]; - for(int i = 0; i < variantRegions.length; i++){ - AtlasRegion region = (AtlasRegion)variantRegions[i]; - editorVariantRegions[i] = Core.atlas.find("editor-" + region.name); - } - } - return editorVariantRegions; - } - /** @return special icons to outline and save with an -outline variant. Vanilla only. */ public TextureRegion[] makeIconRegions(){ return new TextureRegion[0]; @@ -940,6 +928,21 @@ public class Block extends UnlockableContent implements Senseable{ return (envEnabled & env) != 0 && (envDisabled & env) == 0 && (envRequired == 0 || (envRequired & env) == envRequired); } + /** Called to set up configuration UI in the editor. {@link #editorConfigurable} must be true. + * Config value should be assigned to lastConfig.*/ + public void buildEditorConfig(Table table){} + + /** Called when the block is picked (middle click). Clientside only! */ + public void onPicked(Tile tile){} + + /** @return the config value returned when this block is picked on a certain tile. This is only called for non-buildings. */ + public Object getConfig(Tile tile){ + return null; + } + + /** Called when this block is set on the specified tile. */ + public void blockChanged(Tile tile){} + /** Called when building of this block begins. */ public void placeBegan(Tile tile, Block previous){ @@ -951,7 +954,7 @@ public class Block extends UnlockableContent implements Senseable{ } /** Called when building of this block ends. */ - public void placeEnded(Tile tile, @Nullable Unit builder){ + public void placeEnded(Tile tile, @Nullable Unit builder, int rotation, @Nullable Object config){ } @@ -1366,13 +1369,6 @@ public class Block extends UnlockableContent implements Senseable{ mapColor.set(image.get(image.width/2, image.height/2)); } - if(variants > 0){ - for(int i = 0; i < variants; i++){ - String rname = name + (i + 1); - packer.add(PageType.editor, "editor-" + rname, Core.atlas.getPixmap(rname)); - } - } - Seq toDispose = new Seq<>(); //generate paletted team regions @@ -1437,8 +1433,6 @@ public class Block extends UnlockableContent implements Senseable{ } } - PixmapRegion editorBase; - if(gen.length > 1){ Pixmap base = Core.atlas.getPixmap(gen[0]).crop(); for(int i = 1; i < gen.length; i++){ @@ -1450,15 +1444,11 @@ public class Block extends UnlockableContent implements Senseable{ } packer.add(PageType.main, "block-" + name + "-full", base); - editorBase = new PixmapRegion(base); toDispose.add(base); }else{ if(gen[0] != null) packer.add(PageType.main, "block-" + name + "-full", Core.atlas.getPixmap(gen[0])); - editorBase = gen[0] == null ? Core.atlas.getPixmap(fullIcon) : Core.atlas.getPixmap(gen[0]); } - packer.add(PageType.editor, name + "-icon-editor", editorBase); - toDispose.each(Pixmap::dispose); } diff --git a/core/src/mindustry/world/Build.java b/core/src/mindustry/world/Build.java index 8340229257..6948e6910e 100644 --- a/core/src/mindustry/world/Build.java +++ b/core/src/mindustry/world/Build.java @@ -66,9 +66,9 @@ public class Build{ Events.fire(new BlockBuildBeginEvent(tile, team, unit, true)); } - /** Places a ConstructBlock at this location. */ + /** Places a ConstructBlock at this location. To preserve bandwidth, a config is only passed in the case of instant-place blocks. */ @Remote(called = Loc.server) - public static void beginPlace(@Nullable Unit unit, Block result, Team team, int x, int y, int rotation){ + public static void beginPlace(@Nullable Unit unit, Block result, Team team, int x, int y, int rotation, @Nullable Object placeConfig){ if(!validPlace(result, team, x, y, rotation)){ return; } @@ -127,7 +127,7 @@ public class Build{ if(result.instantBuild){ Events.fire(new BlockBuildBeginEvent(tile, team, unit, false)); result.placeBegan(tile, tile.block, unit); - ConstructBlock.constructFinish(tile, result, unit, (byte)rotation, team, null); + ConstructBlock.constructFinish(tile, result, unit, (byte)rotation, team, placeConfig); return; } diff --git a/core/src/mindustry/world/Tile.java b/core/src/mindustry/world/Tile.java index 52e656e87d..0a14cc438e 100644 --- a/core/src/mindustry/world/Tile.java +++ b/core/src/mindustry/world/Tile.java @@ -26,8 +26,14 @@ public class Tile implements Position, QuadTreeObject, Displayable{ private static final TileFloorChangeEvent floorChange = new TileFloorChangeEvent(); private static final ObjectSet tileSet = new ObjectSet<>(); - /** Extra data for very specific blocks. */ - public byte data; + /** + * Extra data for specific blocks. Only saved if Block#saveData is true. + * It is generally recommended that blocks only access data in their own category unless necessary - for example, a floor should not read/write overlay data. + * However, one byte may sometimes not be enough to hold enough data, in which case "overlapping" data storage is necessary. + * */ + public byte data, floorData, overlayData; + /** Even more data for blocks. Use with caution; any floor/block can access this value. Due to 8-byte alignment of Java objects, this extra 4-byte field can be added with no additional cost.*/ + public int extraData; /** Tile entity, usually null. */ public @Nullable Building build; public short x, y; @@ -166,7 +172,7 @@ public class Tile implements Position, QuadTreeObject, Displayable{ } public boolean isDarkened(){ - return block.solid && ((!block.synthetic() && block.fillsTile) || block.checkForceDark(this)); + return block.isDarkened(this); } public Floor floor(){ @@ -274,6 +280,8 @@ public class Tile implements Position, QuadTreeObject, Displayable{ changed(); changing = false; + + block.blockChanged(this); } public void setBlock(Block type, Team team){ @@ -284,11 +292,11 @@ public class Tile implements Position, QuadTreeObject, Displayable{ setBlock(type, Team.derelict, 0); } - /** This resets the overlay! */ public void setFloor(Floor type){ + if(this.floor == type) return; + var prev = this.floor; this.floor = type; - this.overlay = (Floor)Blocks.air; if(!headless && !world.isGenerating() && !isEditorTile()){ renderer.blocks.removeFloorIndex(this); @@ -302,24 +310,19 @@ public class Tile implements Position, QuadTreeObject, Displayable{ pathfinder.updateTile(this); } - if(!world.isGenerating() && prev != type){ + if(!world.isGenerating()){ Events.fire(floorChange.set(this, prev, type)); } + + if(this.floor != prev){ + this.floor.floorChanged(this); + } } public boolean isEditorTile(){ return false; } - /** Sets the floor, preserving overlay.*/ - public void setFloorUnder(Floor floor){ - Block overlay = this.overlay; - setFloor(floor); - if(this.overlay != overlay){ - setOverlay(overlay); - } - } - /** Sets the block to air. */ public void setAir(){ setBlock(Blocks.air); @@ -402,11 +405,9 @@ public class Tile implements Position, QuadTreeObject, Displayable{ return floor.id; } - public void setOverlayID(short ore){ - setOverlay(content.block(ore)); - } - public void setOverlay(Block block){ + if(this.overlay == block) return; + this.overlay = (Floor)block; recache(); @@ -421,7 +422,7 @@ public class Tile implements Position, QuadTreeObject, Displayable{ } public void clearOverlay(){ - setOverlayID((short)0); + setOverlay(Blocks.air); } public boolean passable(){ @@ -558,6 +559,10 @@ public class Tile implements Position, QuadTreeObject, Displayable{ null : null; } + public boolean shouldSaveData(){ + return floor.saveData || overlay.saveData || block.saveData; + } + public int staticDarkness(){ return block.solid && block.fillsTile && !block.synthetic() ? data : 0; } diff --git a/core/src/mindustry/world/WorldContext.java b/core/src/mindustry/world/WorldContext.java index f0a2fc4b63..e43cfa3197 100644 --- a/core/src/mindustry/world/WorldContext.java +++ b/core/src/mindustry/world/WorldContext.java @@ -26,6 +26,9 @@ public interface WorldContext{ /** Called when a building is finished reading. */ default void onReadBuilding(){} + /** Called when data finishes reading for a tile. */ + default void onReadTileData(){} + default @Nullable Sector getSector(){ return null; } diff --git a/core/src/mindustry/world/blocks/ConstructBlock.java b/core/src/mindustry/world/blocks/ConstructBlock.java index 3e53462cf1..fab1ac1171 100644 --- a/core/src/mindustry/world/blocks/ConstructBlock.java +++ b/core/src/mindustry/world/blocks/ConstructBlock.java @@ -78,7 +78,7 @@ public class ConstructBlock extends Block{ if(block instanceof OverlayFloor overlay){ tile.setOverlay(overlay); }else if(block instanceof Floor floor){ - tile.setFloorUnder(floor); + tile.setFloor(floor); }else{ tile.setBlock(block, team, rotation); } @@ -112,7 +112,7 @@ public class ConstructBlock extends Block{ if(shouldPlay()) block.placeSound.at(tile, block.placePitchChange ? calcPitch(true) : 1f); } - block.placeEnded(tile, builder); + block.placeEnded(tile, builder, rotation, config); Events.fire(new BlockBuildEndEvent(tile, builder, team, false, config)); } diff --git a/core/src/mindustry/world/blocks/TileBitmask.java b/core/src/mindustry/world/blocks/TileBitmask.java index f911469267..b27660ffaf 100644 --- a/core/src/mindustry/world/blocks/TileBitmask.java +++ b/core/src/mindustry/world/blocks/TileBitmask.java @@ -1,5 +1,8 @@ package mindustry.world.blocks; +import arc.*; +import arc.graphics.g2d.*; + public class TileBitmask{ /** Autotile bitmasks for 8-directional sprites (see tile-gen)*/ public static final int[] values = { @@ -20,4 +23,23 @@ public class TileBitmask{ 3, 0, 3, 0, 15, 42, 15, 12, 3, 0, 3, 0, 15, 42, 15, 12, 2, 1, 2, 1, 9, 45, 9, 19, 2, 1, 2, 1, 14, 18, 14, 13, }; + + public static TextureRegion[] load(String name){ + var regions = new TextureRegion[47]; + for(int i = 0; i < 47; i++){ + regions[i] = Core.atlas.find(name + "-" + i); + } + return regions; + } + + public static TextureRegion[][] loadVariants(String name, int variants){ + var regions = new TextureRegion[variants][47]; + for(int v = 0; v < variants; v++){ + for(int i = 0; i < 47; i++){ + regions[v][i] = Core.atlas.find(name + "-" + (v+1) + "-" + i); + } + } + + return regions; + } } diff --git a/core/src/mindustry/world/blocks/environment/CharacterOverlay.java b/core/src/mindustry/world/blocks/environment/CharacterOverlay.java new file mode 100644 index 0000000000..d95afed719 --- /dev/null +++ b/core/src/mindustry/world/blocks/environment/CharacterOverlay.java @@ -0,0 +1,95 @@ +package mindustry.world.blocks.environment; + +import arc.graphics.*; +import arc.graphics.g2d.*; +import arc.scene.ui.layout.*; +import arc.util.*; +import mindustry.*; +import mindustry.annotations.Annotations.*; +import mindustry.entities.units.*; +import mindustry.gen.*; +import mindustry.world.*; + +public class CharacterOverlay extends OverlayFloor{ + /** This is a special reduced character set that fits in 6 bits! It is not ASCII! */ + public static final String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890\"!?.,;:()[]{}<>|/@\\^-%+=#_&~"; + + public @Load(value = "character-overlay#", length = 64) TextureRegion[] letterRegions; + public Color color = Color.white; + + public CharacterOverlay(String name){ + super(name); + saveData = true; + variants = 0; + rotate = true; + drawArrow = false; + saveConfig = true; + editorConfigurable = true; + } + + @Override + public void drawBase(Tile tile){ + Draw.color(color); + int letterChar = CharOverlayData.character(tile.overlayData); + Draw.rect(letterRegions[letterChar], tile.worldx(), tile.worldy(), CharOverlayData.rotation(tile.overlayData) * 90f); + Draw.color(); + } + + @Override + public Object getConfig(Tile tile){ + return (int)tile.overlayData; + } + + @Override + public void drawPlanRegion(BuildPlan plan, Eachable list){ + byte data = 0; + + if(plan.config instanceof Integer i){ + data = i.byteValue(); + } + + int letterChar = CharOverlayData.character(data); + + TextureRegion reg = letterRegions[letterChar]; + Draw.tint(color); + Draw.rect(reg, plan.drawx(), plan.drawy(), plan.rotation * 90); + Draw.tint(Color.white); + } + + @Override + public void onPicked(Tile tile){ + Vars.control.input.rotation = CharOverlayData.rotation(tile.overlayData); + } + + @Override + public void buildEditorConfig(Table table){ + char value = chars.charAt(lastConfig instanceof Integer i ? CharOverlayData.character(i.byteValue()) : 0); + table.field(value + "", val -> { + if(val.length() == 1){ + lastConfig = (int)charToData(val.charAt(0)); + } + }).valid(t -> t.length() == 1 && chars.indexOf(Character.toUpperCase(t.charAt(0))) != -1).maxTextLength(1); + } + + @Override + public void placeEnded(Tile tile, @Nullable Unit builder, int rotation, @Nullable Object config){ + byte data = 0; + if(config instanceof Integer i){ + data = i.byteValue(); + } + tile.overlayData = CharOverlayData.get(data, (byte)rotation); + } + + public static byte charToData(char c){ + int index = chars.indexOf(Character.toUpperCase(c)); + return index == -1 ? 0 : (byte)index; + } + + @Struct + class CharOverlayDataStruct{ + @StructField(6) + byte character; + @StructField(2) + byte rotation; + } +} diff --git a/core/src/mindustry/world/blocks/environment/Cliff.java b/core/src/mindustry/world/blocks/environment/Cliff.java index d3a0c99771..49dba95d7c 100644 --- a/core/src/mindustry/world/blocks/environment/Cliff.java +++ b/core/src/mindustry/world/blocks/environment/Cliff.java @@ -9,7 +9,6 @@ import mindustry.world.*; public class Cliff extends Block{ public float size = 11f; public @Load(value = "cliffmask#", length = 256) TextureRegion[] cliffs; - public @Load(value = "editor-cliffmask#", length = 256) TextureRegion[] editorCliffs; public Cliff(String name){ super(name); diff --git a/core/src/mindustry/world/blocks/environment/ColoredFloor.java b/core/src/mindustry/world/blocks/environment/ColoredFloor.java new file mode 100644 index 0000000000..0a9ca1fe27 --- /dev/null +++ b/core/src/mindustry/world/blocks/environment/ColoredFloor.java @@ -0,0 +1,199 @@ +package mindustry.world.blocks.environment; + +import arc.graphics.*; +import arc.graphics.g2d.*; +import arc.math.geom.*; +import arc.scene.ui.layout.*; +import arc.util.*; +import mindustry.*; +import mindustry.entities.units.*; +import mindustry.gen.*; +import mindustry.ui.*; +import mindustry.world.*; +import mindustry.world.blocks.*; + +import static mindustry.Vars.*; + +public class ColoredFloor extends Floor{ + /** If the alpha value of the color is set to this value, different colors are ignored and no border is drawn. */ + public static final int flagIgnoreDifferentColor = 1; + /** If the alpha value of the color is set to this value, colors are interpolated across corners. This is essentially linear filtering for the whole "image". */ + public static final int flagSmoothBlend = 2; + + private static final float[] verts = new float[20]; + + public Color defaultColor = Color.white; + protected int defaultColorRgba; + + public ColoredFloor(String name){ + super(name); + saveData = true; + editorConfigurable = true; + saveConfig = true; + } + + @Override + public void init(){ + super.init(); + lastConfig = defaultColorRgba = defaultColor.rgba(); + } + + @Override + public void buildEditorConfig(Table table){ + showColorEdit(table, this); + } + + public static void showColorEdit(Table t, Block block){ + t.button(b -> { + b.margin(4f); + b.left(); + b.table(Tex.pane, in -> { + in.image(Tex.whiteui).update(i -> { + if(block.lastConfig instanceof Integer col){ + i.color.set(col | 0xff); + } + }).grow(); + }).margin(4).size(50f).padRight(10); + b.add("@color"); + }, Styles.cleart, () -> + ui.picker.show( + block.lastConfig instanceof Integer col ? new Color(col | 0xff) : new Color(Color.white), false, + col -> block.lastConfig = col.rgba8888())).left().width(250f).pad(3f).row(); + } + + @Override + public Object getConfig(Tile tile){ + return tile.extraData; + } + + @Override + public void drawBase(Tile tile){ + //make sure to mask out the alpha channel - it's generally undesirable, and leads to invisible blocks when the data is not initialized + Draw.color(tile.extraData | 0xff); + if((tile.extraData & 0xff) == flagSmoothBlend && autotile){ + //Only autotiling is supported right now for the sake of simplicity + int bits = 0; + + for(int i = 0; i < 8; i++){ + Tile other = tile.nearby(Geometry.d8[i]); + //force flagIgnoreDifferentColor by bypassing checkAutotileSame + if(other != null && other.floor().blendGroup == blendGroup){ + bits |= (1 << i); + } + } + var region = autotileRegions[TileBitmask.values[bits]]; + float s = Vars.tilesize/2f; + float x = tile.worldx(), y = tile.worldy(); + + verts[0] = x - s; + verts[1] = y - s; + verts[2] = sample(this, tile.x, tile.y, tile.x - 1, tile.y - 1, tile.x, tile.y - 1, tile.x - 1, tile.y); + verts[3] = region.u; + verts[4] = region.v2; + + verts[5] = x + s; + verts[6] = y - s; + verts[7] = sample(this, tile.x, tile.y, tile.x, tile.y - 1, tile.x + 1, tile.y - 1, tile.x + 1, tile.y); + verts[8] = region.u2; + verts[9] = region.v2; + + verts[10] = x + s; + verts[11] = y + s; + verts[12] = sample(this, tile.x, tile.y, tile.x + 1, tile.y + 1, tile.x, tile.y + 1, tile.x + 1, tile.y); + verts[13] = region.u2; + verts[14] = region.v; + + verts[15] = x - s; + verts[16] = y + s; + verts[17] = sample(this, tile.x, tile.y, tile.x - 1, tile.y + 1, tile.x, tile.y + 1, tile.x - 1, tile.y); + verts[18] = region.u; + verts[19] = region.v; + + Draw.vert(region.texture, verts, 0, verts.length); + }else{ + super.drawBase(tile); + } + Draw.color(); + } + + static float sample(Block target, int tx1, int ty1, int tx2, int ty2, int tx3, int ty3, int tx4, int ty4){ + int total = 0; + float r = 0f, g = 0f, b = 0f; + Tile t1 = Vars.world.tile(tx1, ty1); + Tile t2 = Vars.world.tile(tx2, ty2); + Tile t3 = Vars.world.tile(tx3, ty3); + Tile t4 = Vars.world.tile(tx4, ty4); + + //manually unrolled loops, hooray + if(t1 != null && t1.floor() == target){ + total ++; + r += Color.ri(t1.extraData); + g += Color.gi(t1.extraData); + b += Color.bi(t1.extraData); + } + + if(t2 != null && t2.floor() == target){ + total ++; + r += Color.ri(t2.extraData); + g += Color.gi(t2.extraData); + b += Color.bi(t2.extraData); + } + + if(t3 != null && t3.floor() == target){ + total ++; + r += Color.ri(t3.extraData); + g += Color.gi(t3.extraData); + b += Color.bi(t3.extraData); + } + + if(t4 != null && t4.floor() == target){ + total ++; + r += Color.ri(t4.extraData); + g += Color.gi(t4.extraData); + b += Color.bi(t4.extraData); + } + + return Color.toFloatBits((int)(r/total), (int)(g/total), (int)(b/total), 255); + } + + @Override + public void drawOverlay(Tile tile){ + //make sure color doesn't carry over + Draw.color(); + super.drawOverlay(tile); + } + + @Override + public void floorChanged(Tile tile){ + //reset to white + if(tile.extraData == 0){ + tile.extraData = defaultColorRgba; + } + } + + @Override + public void placeEnded(Tile tile, @Nullable Unit builder, int rotation, @Nullable Object config){ + //config is assumed to be an integer RGBA color + if(config instanceof Integer i){ + tile.extraData = i; + } + } + + @Override + public void drawPlanRegion(BuildPlan plan, Eachable list){ + if(plan.config instanceof Integer i){ + Draw.tint(Tmp.c1.set(i | 0xff)); + } + drawDefaultPlanRegion(plan, list); + } + + @Override + public boolean checkAutotileSame(Tile tile, @Nullable Tile other){ + return other != null && other.floor().blendGroup == blendGroup && ((tile.extraData & 0xff) == flagIgnoreDifferentColor || tile.extraData == other.extraData); + } + + @Override + public int minimapColor(Tile tile){ + return tile.extraData | 0xff; + } +} diff --git a/core/src/mindustry/world/blocks/environment/ColoredWall.java b/core/src/mindustry/world/blocks/environment/ColoredWall.java new file mode 100644 index 0000000000..4f6fcc23e3 --- /dev/null +++ b/core/src/mindustry/world/blocks/environment/ColoredWall.java @@ -0,0 +1,89 @@ +package mindustry.world.blocks.environment; + +import arc.graphics.*; +import arc.graphics.g2d.*; +import arc.scene.ui.layout.*; +import arc.util.*; +import mindustry.entities.units.*; +import mindustry.gen.*; +import mindustry.world.*; + +public class ColoredWall extends StaticWall{ + /** If the alpha value of the color is set to this value, different colors are ignored and no border is drawn. */ + public static final int flagIgnoreDifferentColor = 1; + /** If the alpha value of the color is set to this value, the wall will have darkness applied, as other walls do. */ + public static final int flagApplyDarkness = 2; + + public Color defaultColor = Color.white; + protected int defaultColorRgba; + + public ColoredWall(String name){ + super(name); + saveData = true; + editorConfigurable = true; + saveConfig = true; + } + + @Override + public void init(){ + super.init(); + lastConfig = defaultColorRgba = defaultColor.rgba(); + } + + @Override + public Object getConfig(Tile tile){ + return tile.extraData; + } + + @Override + public void buildEditorConfig(Table table){ + ColoredFloor.showColorEdit(table, this); + } + + @Override + public void drawBase(Tile tile){ + //make sure to mask out the alpha channel - it's generally undesirable, and leads to invisible blocks when thtoe data is not initialized + Draw.color(tile.extraData | 0xff); + super.drawBase(tile); + Draw.color(); + } + + @Override + public void blockChanged(Tile tile){ + //reset to white on first placement + if(tile.extraData == 0){ + tile.extraData = defaultColorRgba; + } + } + + @Override + public void placeEnded(Tile tile, @Nullable Unit builder, int rotation, @Nullable Object config){ + //config is assumed to be an integer RGBA color + if(config instanceof Integer i){ + tile.extraData = i; + } + } + + @Override + public void drawPlanRegion(BuildPlan plan, Eachable list){ + if(plan.config instanceof Integer i){ + Draw.tint(Tmp.c1.set(i | 0xff)); + } + drawDefaultPlanRegion(plan, list); + } + + @Override + public boolean checkAutotileSame(Tile tile, @Nullable Tile other){ + return other != null && other.block() == this && ((tile.extraData == flagIgnoreDifferentColor) || tile.extraData == other.extraData); + } + + @Override + public boolean isDarkened(Tile tile){ + return (tile.extraData == flagApplyDarkness); + } + + @Override + public int minimapColor(Tile tile){ + return tile.extraData | 0xff; + } +} diff --git a/core/src/mindustry/world/blocks/environment/Floor.java b/core/src/mindustry/world/blocks/environment/Floor.java index f16016f2b2..90d253ccac 100644 --- a/core/src/mindustry/world/blocks/environment/Floor.java +++ b/core/src/mindustry/world/blocks/environment/Floor.java @@ -80,9 +80,18 @@ public class Floor extends Block{ public int tilingVariants = 0; /** If true, this floor uses autotiling; variants are not supported. See https://github.com/GglLfr/tile-gen*/ public boolean autotile = false; + /** If >1, the middle region of the autotile has random variants. */ + public int autotileMidVariants = 1; + /** Variants of the main autotile sprite. */ + public int autotileVariants = 1; + /** If true (default), this floor will draw edges of other floors on itself. */ + public boolean drawEdgeIn = true; + /** If true (default), this floor will draw its edges onto other floors. */ + public boolean drawEdgeOut = true; protected TextureRegion[][][] tilingRegions; - protected TextureRegion[] autotileRegions; + protected TextureRegion[] autotileRegions, autotileMidRegions; + protected TextureRegion[][] autotileVariantRegions; protected int tilingSize; protected TextureRegion[][] edges; protected Seq blenders = new Seq<>(); @@ -141,9 +150,15 @@ public class Floor extends Block{ } if(autotile){ - autotileRegions = new TextureRegion[47]; - for(int i = 0; i < 47; i++){ - autotileRegions[i] = Core.atlas.find(name + "-" + i); + autotileRegions = TileBitmask.load(name); + if(autotileVariants > 1){ + autotileVariantRegions = TileBitmask.loadVariants(name, autotileVariants); + } + if(autotileMidVariants > 1){ + autotileMidRegions = new TextureRegion[autotileMidVariants]; + for(int i = 0; i < autotileMidVariants; i++){ + autotileMidRegions[i] = Core.atlas.find(i == 0 ? name + "-13" : name + "-mid-" + (i + 1)); + } } } @@ -195,7 +210,6 @@ public class Floor extends Block{ @Override public void createIcons(MultiPacker packer){ super.createIcons(packer); - packer.add(PageType.editor, "editor-" + name, Core.atlas.getPixmap(fullIcon)); if(blendGroup != this){ return; @@ -226,25 +240,40 @@ public class Floor extends Block{ }else if(autotile){ int bits = 0; + TextureRegion[] regions = autotileVariants > 1 ? autotileVariantRegions[variant(tile.x, tile.y, autotileVariantRegions.length)] : autotileRegions; + for(int i = 0; i < 8; i++){ Tile other = tile.nearby(Geometry.d8[i]); - if(other != null && other.floor().blendGroup == blendGroup){ + if(checkAutotileSame(tile, other)){ bits |= (1 << i); } } - Draw.rect(autotileRegions[TileBitmask.values[bits]], tile.worldx(), tile.worldy()); + int bit = TileBitmask.values[bits]; + TextureRegion region = bit == 13 && autotileMidVariants > 1 ? autotileMidRegions[variant(tile.x, tile.y, autotileMidRegions.length)] : regions[bit]; + + Draw.rect(region, tile.worldx(), tile.worldy()); }else{ Draw.rect(variantRegions[variant(tile.x, tile.y)], tile.worldx(), tile.worldy()); } Draw.alpha(1f); - drawEdges(tile); + if(drawEdgeIn){ + drawEdges(tile); + } drawOverlay(tile); } + public boolean checkAutotileSame(Tile tile, @Nullable Tile other){ + return other != null && other.floor().blendGroup == blendGroup; + } + public int variant(int x, int y){ - return Mathf.randomSeed(Point2.pack(x, y), 0, Math.max(0, variantRegions.length - 1)); + return variant(x, y, variantRegions.length); + } + + public int variant(int x, int y, int max){ + return Mathf.randomSeed(Point2.pack(x, y), 0, Math.max(0, max - 1)); } public void drawOverlay(Tile tile){ @@ -265,6 +294,9 @@ public class Floor extends Block{ return new TextureRegion[]{Core.atlas.find(Core.atlas.has(name) ? name : name + "1")}; } + /** Called when this floor is set on the specified tile. */ + public void floorChanged(Tile tile){} + /** @return whether to index this floor by flag */ public boolean shouldIndex(Tile tile){ return true; @@ -298,7 +330,7 @@ public class Floor extends Block{ Point2 point = Geometry.d8[i]; Tile other = tile.nearby(point); //special case: empty is, well, empty, so never draw emptiness on top, as that would just be an incorrect black texture - if(other != null && other.floor().cacheLayer == layer && other.floor().edges(tile.x, tile.y) != null && other.floor() != Blocks.empty){ + if(other != null && other.floor().drawEdgeOut && other.floor().cacheLayer == layer && other.floor().edges(tile.x, tile.y) != null){ if(!blended.getAndSet(other.floor().id)){ blenders.add(other.floor()); dirs[i] = other.floorID(); @@ -319,7 +351,7 @@ public class Floor extends Block{ Point2 point = Geometry.d8[i]; Tile other = tile.nearby(point); - if(other != null && doEdge(tile, other, other.floor()) && other.floor().cacheLayer == realCache && other.floor().edges(tile.x, tile.y) != null && other.floor() != Blocks.empty){ + if(other != null && other.floor().drawEdgeOut && doEdge(tile, other, other.floor()) && other.floor().cacheLayer == realCache && other.floor().edges(tile.x, tile.y) != null){ if(!blended.getAndSet(other.floor().id)){ blenders.add(other.floor()); } @@ -345,19 +377,6 @@ public class Floor extends Block{ } } - //'new' style of edges with shadows instead of colors, not used currently - protected void drawEdgesFlat(Tile tile, boolean sameLayer){ - for(int i = 0; i < 4; i++){ - Tile other = tile.nearby(i); - if(other != null && doEdge(tile, other, other.floor())){ - Color color = other.floor().mapColor; - Draw.color(color.r, color.g, color.b, 1f); - Draw.rect(edgeRegion, tile.worldx(), tile.worldy(), i*90); - } - } - Draw.color(); - } - public int realBlendId(Tile tile){ if(tile.floor().isLiquid && !tile.overlay().isAir() && !(tile.overlay() instanceof OreBlock)){ return -((tile.overlay().blendId) | (tile.floor().blendId << 15)); diff --git a/core/src/mindustry/world/blocks/environment/OreBlock.java b/core/src/mindustry/world/blocks/environment/OreBlock.java index f6da9d5f89..ec79b82e42 100644 --- a/core/src/mindustry/world/blocks/environment/OreBlock.java +++ b/core/src/mindustry/world/blocks/environment/OreBlock.java @@ -63,10 +63,8 @@ public class OreBlock extends OverlayFloor{ } packer.add(PageType.environment, name + (i + 1), image); - packer.add(PageType.editor, "editor-" + name + (i + 1), image); if(i == 0){ - packer.add(PageType.editor, "editor-block-" + name + "-full", image); packer.add(PageType.main, "block-" + name + "-full", image); } diff --git a/core/src/mindustry/world/blocks/environment/RemoveOre.java b/core/src/mindustry/world/blocks/environment/RemoveOre.java index b50d019c4f..d0a0229a6f 100644 --- a/core/src/mindustry/world/blocks/environment/RemoveOre.java +++ b/core/src/mindustry/world/blocks/environment/RemoveOre.java @@ -44,7 +44,7 @@ public class RemoveOre extends OverlayFloor{ } @Override - public void placeEnded(Tile tile, @Nullable Unit builder){ + public void placeEnded(Tile tile, @Nullable Unit builder, int rotation, Object config){ tile.setOverlay(Blocks.air); } diff --git a/core/src/mindustry/world/blocks/environment/RemoveWall.java b/core/src/mindustry/world/blocks/environment/RemoveWall.java index 0359ee7052..df3a8eec7d 100644 --- a/core/src/mindustry/world/blocks/environment/RemoveWall.java +++ b/core/src/mindustry/world/blocks/environment/RemoveWall.java @@ -43,7 +43,7 @@ public class RemoveWall extends Block{ } @Override - public void placeEnded(Tile tile, @Nullable Unit builder){ + public void placeEnded(Tile tile, @Nullable Unit builder, int rotation, Object config){ tile.setBlock(Blocks.air); if(tile.overlay().wallOre){ tile.setOverlay(Blocks.air); diff --git a/core/src/mindustry/world/blocks/environment/ShallowLiquid.java b/core/src/mindustry/world/blocks/environment/ShallowLiquid.java index 95c4a368ee..87b10a8dd6 100644 --- a/core/src/mindustry/world/blocks/environment/ShallowLiquid.java +++ b/core/src/mindustry/world/blocks/environment/ShallowLiquid.java @@ -47,9 +47,8 @@ public class ShallowLiquid extends Floor{ } } - String baseName = this.name + "" + (++index); + String baseName = this.name + (++index); packer.add(PageType.environment, baseName, res); - packer.add(PageType.editor, "editor-" + baseName, res); res.dispose(); } diff --git a/core/src/mindustry/world/blocks/environment/StaticWall.java b/core/src/mindustry/world/blocks/environment/StaticWall.java index c65c8a186b..fd48bf0c58 100644 --- a/core/src/mindustry/world/blocks/environment/StaticWall.java +++ b/core/src/mindustry/world/blocks/environment/StaticWall.java @@ -4,16 +4,22 @@ import arc.*; import arc.graphics.g2d.*; import arc.math.*; import arc.math.geom.*; +import arc.util.*; import mindustry.annotations.Annotations.*; import mindustry.content.*; import mindustry.graphics.*; import mindustry.world.*; +import mindustry.world.blocks.*; import static mindustry.Vars.*; public class StaticWall extends Prop{ public @Load("@-large") TextureRegion large; public TextureRegion[][] split; + /** If true, this wall uses autotiling; variants are not supported. See https://github.com/GglLfr/tile-gen*/ + public boolean autotile; + + protected TextureRegion[] autotileRegions; public StaticWall(String name){ super(name); @@ -30,15 +36,28 @@ public class StaticWall extends Prop{ @Override public void drawBase(Tile tile){ - int rx = tile.x / 2 * 2; - int ry = tile.y / 2 * 2; + if(autotile){ + int bits = 0; - if(Core.atlas.isFound(large) && eq(rx, ry) && Mathf.randomSeed(Point2.pack(rx, ry)) < 0.5 && split.length >= 2 && split[0].length >= 2){ - Draw.rect(split[tile.x % 2][1 - tile.y % 2], tile.worldx(), tile.worldy()); - }else if(variants > 0){ - Draw.rect(variantRegions[Mathf.randomSeed(tile.pos(), 0, Math.max(0, variantRegions.length - 1))], tile.worldx(), tile.worldy()); + for(int i = 0; i < 8; i++){ + Tile other = tile.nearby(Geometry.d8[i]); + if(checkAutotileSame(tile, other)){ + bits |= (1 << i); + } + } + + Draw.rect(autotileRegions[TileBitmask.values[bits]], tile.worldx(), tile.worldy()); }else{ - Draw.rect(region, tile.worldx(), tile.worldy()); + int rx = tile.x / 2 * 2; + int ry = tile.y / 2 * 2; + + if(Core.atlas.isFound(large) && eq(rx, ry) && Mathf.randomSeed(Point2.pack(rx, ry)) < 0.5 && split.length >= 2 && split[0].length >= 2){ + Draw.rect(split[tile.x % 2][1 - tile.y % 2], tile.worldx(), tile.worldy()); + }else if(variants > 0){ + Draw.rect(variantRegions[Mathf.randomSeed(tile.pos(), 0, Math.max(0, variantRegions.length - 1))], tile.worldx(), tile.worldy()); + }else{ + Draw.rect(region, tile.worldx(), tile.worldy()); + } } //draw ore on top @@ -47,6 +66,10 @@ public class StaticWall extends Prop{ } } + public boolean checkAutotileSame(Tile tile, @Nullable Tile other){ + return other != null && other.block() == this; + } + @Override public void load(){ super.load(); @@ -59,6 +82,10 @@ public class StaticWall extends Prop{ } } } + + if(autotile){ + autotileRegions = TileBitmask.load(name); + } } @Override diff --git a/core/src/mindustry/world/meta/BuildVisibility.java b/core/src/mindustry/world/meta/BuildVisibility.java index 8b7042d6b0..c4809d2e62 100644 --- a/core/src/mindustry/world/meta/BuildVisibility.java +++ b/core/src/mindustry/world/meta/BuildVisibility.java @@ -11,15 +11,15 @@ public class BuildVisibility{ shown = new BuildVisibility(() -> true), debugOnly = new BuildVisibility(() -> false), editorOnly = new BuildVisibility(() -> Vars.state.rules.editor), - coreZoneOnly = new BuildVisibility(() -> Vars.indexer.isBlockPresent(Blocks.coreZone)), + coreZoneOnly = new BuildVisibility(() -> Vars.indexer.isBlockPresent(Blocks.coreZone) || !Vars.state.isGame()), worldProcessorOnly = new BuildVisibility(() -> Vars.state.rules.editor || Vars.state.rules.allowEditWorldProcessors), sandboxOnly = new BuildVisibility(() -> Vars.state == null || Vars.state.rules.infiniteResources), - campaignOnly = new BuildVisibility(() -> Vars.state == null || Vars.state.isCampaign()), + campaignOnly = new BuildVisibility(() -> Vars.state == null || Vars.state.isCampaign() || !Vars.state.isGame()), legacyLaunchPadOnly = new BuildVisibility(() -> (Vars.state == null || Vars.state.isCampaign() && Vars.state.getPlanet().campaignRules.legacyLaunchPads) && Blocks.advancedLaunchPad != null && Blocks.advancedLaunchPad.unlocked()), - notLegacyLaunchPadOnly = new BuildVisibility(() -> (Vars.state == null || Vars.state.rules.infiniteResources || Vars.state.isCampaign() && !Vars.state.getPlanet().campaignRules.legacyLaunchPads)), - lightingOnly = new BuildVisibility(() -> Vars.state == null || Vars.state.rules.lighting || Vars.state.isCampaign()), + notLegacyLaunchPadOnly = new BuildVisibility(() -> (Vars.state == null || !Vars.state.isGame() || Vars.state.rules.infiniteResources || Vars.state.isCampaign() && !Vars.state.getPlanet().campaignRules.legacyLaunchPads)), + lightingOnly = new BuildVisibility(() -> Vars.state == null || Vars.state.rules.lighting || Vars.state.isCampaign() || !Vars.state.isGame()), ammoOnly = new BuildVisibility(() -> Vars.state == null || Vars.state.rules.unitAmmo), - fogOnly = new BuildVisibility(() -> Vars.state == null || Vars.state.rules.fog || Vars.state.rules.editor); + fogOnly = new BuildVisibility(() -> Vars.state == null || Vars.state.rules.fog || Vars.state.rules.editor || !Vars.state.isGame()); private final Boolp visible; diff --git a/desktop/src/mindustry/desktop/DesktopLauncher.java b/desktop/src/mindustry/desktop/DesktopLauncher.java index 5fd21d90f1..0ae268953c 100644 --- a/desktop/src/mindustry/desktop/DesktopLauncher.java +++ b/desktop/src/mindustry/desktop/DesktopLauncher.java @@ -31,7 +31,7 @@ import static mindustry.Vars.*; public class DesktopLauncher extends ClientLauncher{ public final static long discordID = 610508934456934412L; public final String[] args; - + boolean useDiscord = !OS.hasProp("nodiscord"), loadError = false; Throwable steamError; @@ -48,21 +48,17 @@ public class DesktopLauncher extends ClientLauncher{ for(int i = 0; i < arg.length; i++){ if(arg[i].charAt(0) == '-'){ String name = arg[i].substring(1); - try{ - switch(name){ - case "width": width = Strings.parseInt(arg[i + 1], width); break; - case "height": height = Strings.parseInt(arg[i + 1], height); break; - case "glMajor": gl30Major = Strings.parseInt(arg[i + 1], gl30Major); - case "glMinor": gl30Minor = Strings.parseInt(arg[i + 1], gl30Minor); - case "gl3": gl30 = true; break; - case "gl2": gl30 = false; break; - case "coreGl": coreProfile = true; break; - case "antialias": samples = 16; break; - case "debug": Log.level = LogLevel.debug; break; - case "maximized": maximized = Boolean.parseBoolean(arg[i + 1]); break; - } - }catch(NumberFormatException number){ - Log.warn("Invalid parameter number value."); + switch(name){ + case "width": width = Strings.parseInt(arg[i + 1], width); break; + case "height": height = Strings.parseInt(arg[i + 1], height); break; + case "glMajor": gl30Major = Strings.parseInt(arg[i + 1], gl30Major); + case "glMinor": gl30Minor = Strings.parseInt(arg[i + 1], gl30Minor); + case "gl3": gl30 = true; break; + case "gl2": gl30 = false; break; + case "coreGl": coreProfile = true; break; + case "antialias": samples = 16; break; + case "debug": Log.level = LogLevel.debug; break; + case "maximized": maximized = Boolean.parseBoolean(arg[i + 1]); break; } } } @@ -75,7 +71,7 @@ public class DesktopLauncher extends ClientLauncher{ public DesktopLauncher(String[] args){ this.args = args; - + Version.init(); boolean useSteam = Version.modifier.contains("steam"); testMobile = Seq.with(args).contains("-testMobile"); diff --git a/tools/src/mindustry/tools/Generators.java b/tools/src/mindustry/tools/Generators.java index 8fbda7d9f8..bb22f32e1f 100644 --- a/tools/src/mindustry/tools/Generators.java +++ b/tools/src/mindustry/tools/Generators.java @@ -70,23 +70,36 @@ public class Generators{ ObjectMap gens = new ObjectMap<>(); generate("autotiles", () -> { - for(Floor floor : content.blocks().select(b -> b.isFloor() && b.asFloor().autotile).as()){ - Fi basePath = new Fi("../../../assets-raw/sprites_out/blocks/environment/" + floor.name + "-autotile.png"); + for(Block block : content.blocks().select(b -> (b.isFloor() && b.asFloor().autotile) || (b instanceof StaticWall && ((StaticWall)b).autotile))){ + int variants = block instanceof Floor f && f.autotileVariants > 1 ? f.autotileVariants : 1; + for(int v = 0; v < variants; v++){ + Fi basePath = new Fi("../../../assets-raw/sprites_out/blocks/environment/" + block.name + "-autotile" + (variants <= 1 ? "" : "" + (v+1)) + ".png"), iconPath = basePath.parent().child(block.name + ".png"); - if(basePath.exists()){ - //theoretically this might not finish in time, but I doubt that will ever happen - mainExecutor.submit(() -> { - try{ - ImageTileGenerator.generate(basePath, floor.name, new Fi("../../../assets-raw/sprites_out/blocks/environment/" + floor.name)); - }catch(Throwable e){ - Log.err("Failed to autotile: " + floor.name, e); - }finally{ - //the raw autotile source image must never be included, it isn't useful - basePath.delete(); + if(basePath.exists()){ + int variant = v; + //theoretically this might not finish in time, but I doubt that will ever happen + mainExecutor.submit(() -> { + try{ + ImageTileGenerator.generate(basePath, block.name + (variants <= 1 ? "" : "-" + (variant+1)), new Fi("../../../assets-raw/sprites_out/blocks/environment")); + }catch(Throwable e){ + Log.err("Failed to autotile: " + block.name, e); + }finally{ + //the raw autotile source image must never be included, it isn't useful + basePath.delete(); + } + }); + + if(!iconPath.exists() && v == 0){ + //save the bottom right region as the "main" sprite for previews + Pixmap out = new Pixmap(basePath); + Pixmap cropped = out.crop(32, 32, 32, 32); + iconPath.writePng(cropped); + out.dispose(); + gens.put(block, cropped); } - }); - }else{ - Log.warn("Autotile floor '@' not found: @", floor.name, basePath.absolutePath()); + }else{ + Log.warn("Autotile floor '@' not found: @", block.name, basePath.absolutePath()); + } } } }); @@ -248,7 +261,6 @@ public class Generators{ Fi fi = Fi.get("../blocks/environment/cliffmask" + (val & 0xff) + ".png"); fi.writePng(result); - fi.copyTo(Fi.get("../editor").child("editor-" + fi.name())); }); } @@ -310,14 +322,6 @@ public class Generators{ TextureRegion[] regions = block.getGeneratedIcons(); - if(block.variants > 0 || block instanceof Floor){ - for(TextureRegion region : block.variantRegions()){ - GenRegion gen = (GenRegion)region; - if(gen.path == null) continue; - gen.path.copyTo(Fi.get("../editor/editor-" + gen.path.name())); - } - } - for(TextureRegion region : block.makeIconRegions()){ GenRegion gen = (GenRegion)region; save(get(region).outline(block.outlineColor, block.outlineRadius), gen.name + "-outline"); @@ -380,34 +384,37 @@ public class Generators{ save(padded, region.name); } - if(!regions[0].found()){ + Pixmap image; + + if(regions[0].found()){ + image = get(regions[0]); + + int i = 0; + for(TextureRegion region : regions){ + i++; + if(i != regions.length || last == null){ + image.draw(get(region), true); + }else{ + image.draw(last, true); + } + + //draw shard (default team top) on top of first sprite + if(region == block.teamRegions[Team.sharded.id] && shardTeamTop != null){ + image.draw(shardTeamTop, true); + } + } + + if(!(regions.length == 1 && regions[0] == Core.atlas.find(block.name) && shardTeamTop == null)){ + save(image, "block-" + block.name + "-full"); + } + + saveScaled(image, "../ui/block-" + block.name + "-ui", Math.min(image.width, maxUiIcon)); + }else if(gens.containsKey(block)){ + image = gens.get(block); + }else{ continue; } - Pixmap image = get(regions[0]); - - int i = 0; - for(TextureRegion region : regions){ - i++; - if(i != regions.length || last == null){ - image.draw(get(region), true); - }else{ - image.draw(last, true); - } - - //draw shard (default team top) on top of first sprite - if(region == block.teamRegions[Team.sharded.id] && shardTeamTop != null){ - image.draw(shardTeamTop, true); - } - } - - if(!(regions.length == 1 && regions[0] == Core.atlas.find(block.name) && shardTeamTop == null)){ - save(image, "block-" + block.name + "-full"); - } - - save(image, "../editor/" + block.name + "-icon-editor"); - saveScaled(image, "../ui/block-" + block.name + "-ui", Math.min(image.width, maxUiIcon)); - boolean hasEmpty = false; Color average = new Color(), c = new Color(); float asum = 0f; @@ -454,9 +461,8 @@ public class Generators{ } } - String name = floor.name + "" + (++index); + String name = floor.name + (++index); save(res, "../blocks/environment/" + name); - save(res, "../editor/editor-" + name); gens.put(floor, res); } @@ -496,7 +502,7 @@ public class Generators{ Pixmap container = new Pixmap(base.width + 10, base.height + 10); container.draw(base, 5, 5, true); - replace("sector-" + item.name, container.outline(Pal.darkerGray, 5)); + replace("../ui/sector-" + item.name, "sector-" + item.name, container.outline(Pal.darkerGray, 5)); } }); @@ -810,7 +816,6 @@ public class Generators{ replace(ore.variantRegions[i], image); save(image, "../blocks/environment/" + ore.name + (i + 1)); - save(image, "../editor/editor-" + ore.name + (i + 1)); save(image, "block-" + ore.name + "-full"); save(image, "../ui/block-" + ore.name + "-ui"); @@ -819,14 +824,15 @@ public class Generators{ }); generate("edges", () -> { - content.blocks().each(b -> b instanceof Floor && !(b instanceof OverlayFloor), floor -> { + content.blocks().each(b -> b instanceof Floor && !(b instanceof OverlayFloor) && !b.isAir(), floor -> { - if(has(floor.name + "-edge") || floor.blendGroup != floor){ + if(has(floor.name + "-edge") || floor.blendGroup != floor || (!floor.drawEdgeOut)){ return; } try{ - Pixmap image = gens.get(floor, get(floor.getGeneratedIcons()[0])); + Pixmap image = gens.get(floor); + if(image == null) image = get(floor.getGeneratedIcons()[0]); Pixmap edge = get("edge-stencil"); Pixmap result = new Pixmap(edge.width, edge.height); @@ -838,7 +844,9 @@ public class Generators{ save(result, "../blocks/environment/" + floor.name + "-edge"); - }catch(Exception ignored){} + }catch(Exception e){ + Log.err("Failed to generate edge for " + floor, e); + } }); }); diff --git a/tools/src/mindustry/tools/ImagePacker.java b/tools/src/mindustry/tools/ImagePacker.java index 7018acf998..5ab4153826 100644 --- a/tools/src/mindustry/tools/ImagePacker.java +++ b/tools/src/mindustry/tools/ImagePacker.java @@ -29,6 +29,8 @@ public class ImagePacker{ //makes PNG loading slightly faster ArcNativesLoader.load(); + fixSubdirectory("blocks/environment/character-overlay"); + Core.settings = new MockSettings(); Log.logger = new NoopLogHandler(); Vars.content = new ContentLoader(); @@ -200,6 +202,15 @@ public class ImagePacker{ } } + static void fixSubdirectory(String dir){ + Fi folder = Fi.get("../../../assets-raw/sprites_out/" + dir); + Fi parent = folder.parent(); + folder.walk(fi -> { + fi.moveTo(parent.child(fi.name())); + }); + folder.delete(); + } + static String texname(UnlockableContent c){ return c.getContentType() + "-" + c.name + "-ui"; } @@ -251,7 +262,11 @@ public class ImagePacker{ } static void replace(String name, Pixmap image){ - Fi.get(name + ".png").writePng(image); + replace(name, name, image); + } + + static void replace(String path, String name, Pixmap image){ + Fi.get(path + ".png").writePng(image); ((GenRegion)Core.atlas.find(name)).path.delete(); } diff --git a/tools/src/mindustry/tools/ImageTileGenerator.java b/tools/src/mindustry/tools/ImageTileGenerator.java index bc5c01138d..4f9b1e98d8 100644 --- a/tools/src/mindustry/tools/ImageTileGenerator.java +++ b/tools/src/mindustry/tools/ImageTileGenerator.java @@ -56,7 +56,7 @@ public class ImageTileGenerator{ for(int i = 0; i < 47; i++){ int cx = i % 12, cy = i / 12; Pixmap cropped = out.crop(cx * cellSize, cy * cellSize, cellSize, cellSize); - outputDir.child(name + "-" + i).writePng(cropped); + outputDir.child(name + "-" + i + ".png").writePng(cropped); cropped.dispose(); }