diff --git a/core/assets/baseparts/1591368647700.msch b/core/assets/baseparts/1591368647700.msch
new file mode 100644
index 0000000000..4235369db1
--- /dev/null
+++ b/core/assets/baseparts/1591368647700.msch
@@ -0,0 +1,2 @@
+mschxœ-ŒÑ
+Ã E¯U»BÊ>Ä‡}’³aZ‹Õ�ýý’v	¹9¹$Á„›‚Ù|&¨Çˆ{(ûNÕ}|J.ùú"èµÌ±QvGé5–}£ž}‹Á­5¦„y�G«ñÙ[©˜BÙÞôe."ê’�SIÓPÂÆŠ¯™þÀaÎ™
-hY¸	Ÿ+özdå‡:—øh”ƒA›M
\ No newline at end of file
diff --git a/core/assets/baseparts/1591368658042.msch b/core/assets/baseparts/1591368658042.msch
new file mode 100644
index 0000000000..d1e42464a1
--- /dev/null
+++ b/core/assets/baseparts/1591368658042.msch
@@ -0,0 +1,4 @@
+mschxœ%‹Ñ
+Ã E¯Z»BÊ>Ä—ý‘³2[‹Õ•ýýr)!9	çL
+Ãî·õñå8bu—ÏÙe_?fíóšÎVÓ»·R1…²ãO¶9µ¸¹³ô"–c�}ó-·Ö”3€E
+†07,¡y+�‡aJ‹ÝHgèË×�71@sXF�©oHàIc
\ No newline at end of file
diff --git a/core/assets/baseparts/1591368707268.msch b/core/assets/baseparts/1591368707268.msch
new file mode 100644
index 0000000000..ee88f4b397
--- /dev/null
+++ b/core/assets/baseparts/1591368707268.msch
@@ -0,0 +1,2 @@
+mschxœ%LÑ
+Ã <›*…AÇ>Ä§}‘kC+ØV¢²ßŸYIrwÜ%Á„É`<ÃÁ0o‡×råÌâ¿!%Ÿ‚lŒq1á+¾\MÆ3ŸÜŽPãâW‰©§k,Uâ§ÕK0oòÞ÷}.ÀÜƒ‚¹aèe”Fµµ€%êÊuKAwB*þ™½?X=V"5�~ÿ[ë
\ No newline at end of file
diff --git a/core/assets/baseparts/1591368730003.msch b/core/assets/baseparts/1591368730003.msch
new file mode 100644
index 0000000000..f7974af4e2
--- /dev/null
+++ b/core/assets/baseparts/1591368730003.msch
@@ -0,0 +1,2 @@
+mschxœ%LÑ
+Ã <›*…AÇ>Ä·ý�kC+ØV¢²ßŸYIrwÜ%Á„É`<ÃÁ0o‡×råÌâ¿!%Ÿ‚lŒq1á+¾\MÆ3ŸÜŽPãâW‰©§k,Uâ§ÕK0oòÞ÷}.ÀÜƒ‚¹aèe”Fµµ€%êÊuKAwB*þ™½?X=V"5�~ÿ[Öì
\ No newline at end of file
diff --git a/core/assets/baseparts/1591368779629.msch b/core/assets/baseparts/1591368779629.msch
new file mode 100644
index 0000000000..2254a17355
Binary files /dev/null and b/core/assets/baseparts/1591368779629.msch differ
diff --git a/core/assets/baseparts/1591369683871.msch b/core/assets/baseparts/1591369683871.msch
new file mode 100644
index 0000000000..6a03cd7f4e
Binary files /dev/null and b/core/assets/baseparts/1591369683871.msch differ
diff --git a/core/assets/baseparts/1591369726023.msch b/core/assets/baseparts/1591369726023.msch
new file mode 100644
index 0000000000..eb87221260
Binary files /dev/null and b/core/assets/baseparts/1591369726023.msch differ
diff --git a/core/src/mindustry/ai/BaseRegistry.java b/core/src/mindustry/ai/BaseRegistry.java
index 7161995c3a..b2fa4f29dc 100644
--- a/core/src/mindustry/ai/BaseRegistry.java
+++ b/core/src/mindustry/ai/BaseRegistry.java
@@ -8,12 +8,15 @@ import mindustry.game.*;
 import mindustry.game.Schematic.*;
 import mindustry.type.*;
 import mindustry.world.*;
+import mindustry.world.blocks.production.*;
 import mindustry.world.blocks.sandbox.*;
 import mindustry.world.blocks.storage.*;
 import mindustry.world.meta.*;
 
 import java.io.*;
 
+import static mindustry.Vars.tilesize;
+
 public class BaseRegistry{
     public Array<BasePart> cores = new Array<>();
     public Array<BasePart> parts = new Array<>();
@@ -35,6 +38,8 @@ public class BaseRegistry{
                 Schematic schem = Schematics.read(Core.files.internal("baseparts/" + name));
 
                 BasePart part = new BasePart(schem);
+                Tmp.v1.setZero();
+                int drills = 0;
 
                 for(Stile tile : schem.tiles){
                     //make note of occupied positions
@@ -56,6 +61,12 @@ public class BaseRegistry{
                         Liquid config = (Liquid)tile.config;
                         if(config != null) part.requiredLiquid = config;
                     }
+
+                    //calculate averages
+                    if(tile.block instanceof Drill){
+                        Tmp.v1.add(tile.x*tilesize + tile.block.offset(), tile.y*tilesize + tile.block.offset());
+                        drills ++;
+                    }
                 }
                 schem.tiles.removeAll(s -> s.block.buildVisibility == BuildVisibility.sandboxOnly);
 
@@ -63,6 +74,15 @@ public class BaseRegistry{
 
                 (part.core != null ? cores : parts).add(part);
 
+                if(drills > 0){
+                    Tmp.v1.scl(1f / drills).scl(1f / tilesize);
+                    part.centerX = (int)Tmp.v1.x;
+                    part.centerY = (int)Tmp.v1.y;
+                }else{
+                    part.centerX = part.schematic.width/2;
+                    part.centerY = part.schematic.height/2;
+                }
+
                 if(part.requiredItem != null){
                     itemParts.get(part.requiredItem, Array::new).add(part);
                 }
@@ -80,6 +100,9 @@ public class BaseRegistry{
         public final Schematic schematic;
         public final GridBits occupied;
 
+        //offsets for drills
+        public int centerX, centerY;
+
         public @Nullable Liquid requiredLiquid;
         public @Nullable Item requiredItem;
         public @Nullable Block core;
diff --git a/core/src/mindustry/game/Schematic.java b/core/src/mindustry/game/Schematic.java
index eeeda0fbd7..f573c8d029 100644
--- a/core/src/mindustry/game/Schematic.java
+++ b/core/src/mindustry/game/Schematic.java
@@ -1,10 +1,11 @@
 package mindustry.game;
 
+import arc.files.*;
 import arc.struct.*;
 import arc.struct.IntIntMap.*;
-import arc.files.*;
 import arc.util.ArcAnnotate.*;
 import mindustry.*;
+import mindustry.content.*;
 import mindustry.mod.Mods.*;
 import mindustry.type.*;
 import mindustry.world.*;
@@ -125,5 +126,23 @@ public class Schematic implements Publishable, Comparable<Schematic>{
             this.config = config;
             this.rotation = rotation;
         }
+
+        //pooling only
+        public Stile(){
+            block = Blocks.air;
+        }
+
+        public Stile set(Stile other){
+            block = other.block;
+            x = other.x;
+            y = other.y;
+            config = other.config;
+            rotation = other.rotation;
+            return this;
+        }
+
+        public Stile copy(){
+            return new Stile(block, x, y, config, rotation);
+        }
     }
 }
diff --git a/core/src/mindustry/maps/generators/BaseGenerator.java b/core/src/mindustry/maps/generators/BaseGenerator.java
index 5607036a1b..e708bfe255 100644
--- a/core/src/mindustry/maps/generators/BaseGenerator.java
+++ b/core/src/mindustry/maps/generators/BaseGenerator.java
@@ -3,8 +3,10 @@ package mindustry.maps.generators;
 import arc.math.*;
 import arc.math.geom.*;
 import arc.struct.*;
+import arc.util.pooling.*;
 import mindustry.ai.BaseRegistry.*;
 import mindustry.content.*;
+import mindustry.entities.units.*;
 import mindustry.game.*;
 import mindustry.game.Schematic.*;
 import mindustry.gen.*;
@@ -17,6 +19,10 @@ import mindustry.world.blocks.production.*;
 import static mindustry.Vars.*;
 
 public class BaseGenerator{
+    private static final Schematic tmpSchem = new Schematic(new Array<>(), new StringMap(), 0, 0);
+    private static final Schematic tmpSchem2 = new Schematic(new Array<>(), new StringMap(), 0, 0);
+    private static final Vec2 axis = new Vec2(), rotator = new Vec2();
+
     private Tiles tiles;
     private Team team;
     private ObjectMap<Item, OreBlock> ores = new ObjectMap<>();
@@ -36,12 +42,12 @@ public class BaseGenerator{
 
         float bracket = 0.1f;
         int range = 200;
-        int wallAngle = 180;
+        int wallAngle = 70; //180 for full coverage
         BasePart coreschem = bases.cores.getFrac(bracket);
 
         Block wall = wallsSmall.getFrac(bracket), wallLarge = wallsLarge.getFrac(bracket);
 
-        //TODO random flipping and rotation
+        //TODO random rotation
 
         for(Tile tile : cores){
             tile.clearOverlay();
@@ -67,67 +73,81 @@ public class BaseGenerator{
             });
         }
 
-        //second pass: small walls
-        for(Tile core : cores){
-            core.circle(range, (x, y) -> {
-                Tile tile = tiles.getn(x, y);
-                if(tile.block().alwaysReplace){
-                    boolean any = false;
+        if(wallAngle > 0){
+            //second pass: small walls
+            for(Tile core : cores){
+                core.circle(range, (x, y) -> {
+                    Tile tile = tiles.getn(x, y);
+                    if(tile.block().alwaysReplace){
+                        boolean any = false;
 
-                    for(Point2 p : Geometry.d8){
-                        if(Angles.angleDist(Angles.angle(p.x, p.y), spawn.angleTo(core)) > wallAngle){
-                            continue;
+                        for(Point2 p : Geometry.d8){
+                            if(Angles.angleDist(Angles.angle(p.x, p.y), spawn.angleTo(core)) > wallAngle){
+                                continue;
+                            }
+
+                            Tile o = tiles.get(tile.x + p.x, tile.y + p.y);
+                            if(o != null && o.team() == team && !(o.block() instanceof Wall)){
+                                any = true;
+                                break;
+                            }
                         }
 
-                        Tile o = tiles.get(tile.x + p.x, tile.y + p.y);
-                        if(o != null && o.team() == team && !(o.block() instanceof Wall)){
-                            any = true;
-                            break;
+                        if(any){
+                            tile.setBlock(wall, team);
+                        }
+                    }
+                });
+            }
+
+            //third pass: large walls
+            for(Tile core : cores){
+                core.circle(range, (x, y) -> {
+                    int walls = 0;
+                    for(int cx = 0; cx < 2; cx++){
+                        for(int cy = 0; cy < 2; cy++){
+                            Tile tile = tiles.get(x + cx, y + cy);
+                            if(tile == null || tile.block().size != 1 || (tile.block() != wall && !tile.block().alwaysReplace)) return;
+
+                            if(tile.block() == wall){
+                                walls ++;
+                            }
                         }
                     }
 
-                    if(any){
-                        tile.setBlock(wall, team);
+                    if(walls >= 3){
+                        tiles.getn(x, y).setBlock(wallLarge, team);
                     }
-                }
-            });
-        }
-
-        //third pass: large walls
-        for(Tile core : cores){
-            core.circle(range, (x, y) -> {
-                int walls = 0;
-                for(int cx = 0; cx < 2; cx++){
-                    for(int cy = 0; cy < 2; cy++){
-                        Tile tile = tiles.get(x + cx, y + cy);
-                        if(tile == null || tile.block().size != 1 || (tile.block() != wall && !tile.block().alwaysReplace)) return;
-
-                        if(tile.block() == wall){
-                            walls ++;
-                        }
-                    }
-                }
-
-                if(walls >= 3){
-                    tiles.getn(x, y).setBlock(wallLarge, team);
-                }
-            });
+                });
+            }
         }
     }
 
     boolean tryPlace(BasePart part, int x, int y){
-        int cx = x - part.schematic.width/2, cy = y - part.schematic.height/2;
-        for(int rx = cx; rx <= cx + part.schematic.width; rx++){
-            for(int ry = cy; ry <= cy + part.schematic.height; ry++){
+        int rotation = 0;
+        axis.set((int)(part.schematic.width / 2f), (int)(part.schematic.height / 2f));
+        Schematic result = rotate(part.schematic, rotation);
+
+        rotator.set(part.centerX, part.centerY).rotateAround(axis, rotation * 90);
+        int cx = x - (int)rotator.x;
+        int cy = y - (int)rotator.y;
+
+        for(int rx = cx; rx <= cx + result.width; rx++){
+            for(int ry = cy; ry <= cy + result.height; ry++){
                 Tile tile = tiles.get(rx, ry);
-                if(tile == null || ((!tile.block().alwaysReplace || world.getDarkness(rx, ry) > 0) && part.occupied.get(rx - cx, ry - cy))){
+                int ox = rx - cx, oy = ry - cy;
+                rotator.set(ox, oy).rotateAround(axis, rotation * 90);
+                ox = (int)rotator.x;
+                oy = (int)rotator.y;
+
+                if(tile == null || ((!tile.block().alwaysReplace || world.getDarkness(rx, ry) > 0) && part.occupied.get(ox, oy))){
                     return false;
                 }
             }
         }
 
         if(part.requiredItem != null){
-            for(Stile tile : part.schematic.tiles){
+            for(Stile tile : result.tiles){
                 if(tile.block instanceof Drill){
                     tile.block.iterateTaken(tile.x + cx, tile.y + cy, (ex, ey) -> {
                         tiles.getn(ex, ey).setOverlay(ores.get(part.requiredItem));
@@ -136,8 +156,68 @@ public class BaseGenerator{
             }
         }
 
-        Schematics.place(part.schematic, x, y, team);
+        Schematics.place(result, cx + result.width/2, cy + result.height/2, team);
 
         return true;
     }
+
+    Schematic rotate(Schematic input, int times){
+        if(times == 0) return input;
+
+        boolean sign = times < 0;
+        for(int i = 0; i < Math.abs(times); i++){
+            input = rotated(input, sign);
+        }
+        return input;
+    }
+
+    Schematic rotated(Schematic input, boolean counter){
+        int direction = Mathf.sign(counter);
+        Schematic schem = input == tmpSchem ? tmpSchem2 : tmpSchem2;
+        schem.width = input.width;
+        schem.height = input.height;
+        Pools.freeAll(schem.tiles);
+        schem.tiles.clear();
+        for(Stile tile : input.tiles){
+            schem.tiles.add(Pools.obtain(Stile.class, Stile::new).set(tile));
+        }
+
+        int ox = schem.width/2, oy = schem.height/2;
+
+        schem.tiles.each(req -> {
+            req.config = BuildRequest.pointConfig(req.config, p -> {
+                int cx = p.x, cy = p.y;
+                int lx = cx;
+
+                if(direction >= 0){
+                    cx = -cy;
+                    cy = lx;
+                }else{
+                    cx = cy;
+                    cy = -lx;
+                }
+                p.set(cx, cy);
+            });
+
+            //rotate actual request, centered on its multiblock position
+            float wx = (req.x - ox) * tilesize + req.block.offset(), wy = (req.y - oy) * tilesize + req.block.offset();
+            float x = wx;
+            if(direction >= 0){
+                wx = -wy;
+                wy = x;
+            }else{
+                wx = wy;
+                wy = -x;
+            }
+            req.x = (short)(world.toTile(wx - req.block.offset()) + ox);
+            req.y = (short)(world.toTile(wy - req.block.offset()) + oy);
+            req.rotation = (byte)Mathf.mod(req.rotation + direction, 4);
+        });
+
+        //assign flipped values, since it's rotated
+        schem.width = input.height;
+        schem.height = input.width;
+
+        return schem;
+    }
 }
diff --git a/gradle.properties b/gradle.properties
index 6d96d5473a..722a1885f4 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,3 +1,3 @@
 org.gradle.daemon=true
 org.gradle.jvmargs=-Xms256m -Xmx1024m
-archash=1fc9e8ef5ecca01e058eab1ff4d7fd673aa5431d
+archash=02f15d353cddc9725ad46bafeeae77c3f2719bac
