Somewhat functional base building AI
This commit is contained in:
@@ -1,14 +1,164 @@
|
|||||||
package mindustry.ai;
|
package mindustry.ai;
|
||||||
|
|
||||||
|
import arc.math.*;
|
||||||
|
import arc.math.geom.*;
|
||||||
|
import arc.struct.*;
|
||||||
|
import arc.util.*;
|
||||||
|
import mindustry.*;
|
||||||
|
import mindustry.ai.BaseRegistry.*;
|
||||||
|
import mindustry.content.*;
|
||||||
|
import mindustry.game.*;
|
||||||
|
import mindustry.game.Schematic.*;
|
||||||
import mindustry.game.Teams.*;
|
import mindustry.game.Teams.*;
|
||||||
|
import mindustry.type.*;
|
||||||
|
import mindustry.world.*;
|
||||||
|
import mindustry.world.blocks.defense.*;
|
||||||
|
import mindustry.world.blocks.production.*;
|
||||||
|
import mindustry.world.blocks.storage.CoreBlock.*;
|
||||||
|
|
||||||
|
import static mindustry.Vars.*;
|
||||||
|
|
||||||
public class BaseAI{
|
public class BaseAI{
|
||||||
|
private static final Vec2 axis = new Vec2(), rotator = new Vec2();
|
||||||
|
private static final float correctPercent = 0.5f;
|
||||||
|
private static final float step = 5;
|
||||||
|
private static final int attempts = 5;
|
||||||
|
private static final float emptyChance = 0.01f;
|
||||||
|
|
||||||
public void update(TeamData data){
|
private static int correct = 0, incorrect = 0;
|
||||||
|
|
||||||
|
private int lastX, lastY, lastW, lastH;
|
||||||
|
private boolean triedWalls;
|
||||||
|
|
||||||
|
TeamData data;
|
||||||
|
Interval timer = new Interval();
|
||||||
|
|
||||||
|
public BaseAI(TeamData data){
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(){
|
||||||
|
|
||||||
//only schedule when there's something to build.
|
//only schedule when there's something to build.
|
||||||
if(data.blocks.isEmpty()){
|
if(data.blocks.isEmpty() && timer.get(step)){
|
||||||
//TODO
|
if(!triedWalls){
|
||||||
|
tryWalls();
|
||||||
|
triedWalls = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(int i = 0; i < attempts; i++){
|
||||||
|
int range = 150;
|
||||||
|
CoreEntity core = data.cores.random();
|
||||||
|
|
||||||
|
Tmp.v1.rnd(Mathf.random(range));
|
||||||
|
int wx = (int)(core.tileX() + Tmp.v1.x), wy = (int)(core.tileY() + Tmp.v1.y);
|
||||||
|
Tile tile = world.tiles.getc(wx, wy);
|
||||||
|
|
||||||
|
Array<BasePart> parts = null;
|
||||||
|
|
||||||
|
//pick a completely random base part, and place it a random location
|
||||||
|
//((yes, very intelligent))
|
||||||
|
if(tile.drop() != null && Vars.bases.forResource(tile.drop()).any()){
|
||||||
|
parts = Vars.bases.forResource(tile.drop());
|
||||||
|
}else if(Mathf.chance(emptyChance)){
|
||||||
|
parts = Vars.bases.parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(parts != null){
|
||||||
|
BasePart part = parts.random();
|
||||||
|
if(tryPlace(part, tile.x, tile.y)){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean tryPlace(BasePart part, int x, int y){
|
||||||
|
int rotation = Mathf.range(2);
|
||||||
|
axis.set((int)(part.schematic.width / 2f), (int)(part.schematic.height / 2f));
|
||||||
|
Schematic result = Schematics.rotate(part.schematic, rotation);
|
||||||
|
int rotdeg = rotation*90;
|
||||||
|
rotator.set(part.centerX, part.centerY).rotateAround(axis, rotdeg);
|
||||||
|
//bottom left schematic corner
|
||||||
|
int cx = x - (int)rotator.x;
|
||||||
|
int cy = y - (int)rotator.y;
|
||||||
|
|
||||||
|
//chekc valid placeability
|
||||||
|
for(Stile tile : result.tiles){
|
||||||
|
int realX = tile.x + cx, realY = tile.y + cy;
|
||||||
|
if(!Build.validPlace(tile.block, data.team, realX, realY, tile.rotation)){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//make sure at least X% of resource requirements are met
|
||||||
|
correct = incorrect = 0;
|
||||||
|
|
||||||
|
if(part.required instanceof Item){
|
||||||
|
for(Stile tile : result.tiles){
|
||||||
|
if(tile.block instanceof Drill){
|
||||||
|
|
||||||
|
tile.block.iterateTaken(tile.x + cx, tile.y + cy, (ex, ey) -> {
|
||||||
|
Tile res = world.rawTile(ex, ey);
|
||||||
|
if(res.drop() == part.required){
|
||||||
|
correct ++;
|
||||||
|
}else{
|
||||||
|
incorrect ++;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//fail if not enough fit requirements
|
||||||
|
if((float)correct / incorrect < correctPercent){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//queue it
|
||||||
|
for(Stile tile : result.tiles){
|
||||||
|
data.blocks.add(new BlockPlan(cx + tile.x, cy + tile.y, tile.rotation, tile.block.id, tile.config));
|
||||||
|
}
|
||||||
|
|
||||||
|
lastX = cx - 1;
|
||||||
|
lastY = cy - 1;
|
||||||
|
lastW = result.width + 2;
|
||||||
|
lastH = result.height + 2;
|
||||||
|
|
||||||
|
triedWalls = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tryWalls(){
|
||||||
|
Block wall = Blocks.copperWall;
|
||||||
|
Tile spawn = state.rules.defaultTeam.core() != null ? state.rules.defaultTeam.core().tile : data.team.core().tile;
|
||||||
|
|
||||||
|
for(int wx = lastX; wx <= lastX + lastW; wx++){
|
||||||
|
for(int wy = lastY; wy <= lastY + lastH; wy++){
|
||||||
|
Tile tile = world.tile(wx, wy);
|
||||||
|
|
||||||
|
if(tile == null || !tile.block().alwaysReplace) continue;
|
||||||
|
|
||||||
|
boolean any = false;
|
||||||
|
|
||||||
|
for(Point2 p : Geometry.d8){
|
||||||
|
if(Angles.angleDist(Angles.angle(p.x, p.y), spawn.angleTo(tile)) > 70){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tile o = world.tile(tile.x + p.x, tile.y + p.y);
|
||||||
|
if(o != null && o.team() == data.team && !(o.block() instanceof Wall)){
|
||||||
|
any = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(any && Build.validPlace(wall, data.team, tile.x, tile.y, 0)){
|
||||||
|
data.blocks.add(new BlockPlan(tile.x, tile.y, (short)0, wall.id, null));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,8 +18,12 @@ public class BuilderAI extends AIController{
|
|||||||
public void update(){
|
public void update(){
|
||||||
Builderc builder = (Builderc)unit;
|
Builderc builder = (Builderc)unit;
|
||||||
|
|
||||||
|
if(builder.moving()){
|
||||||
|
builder.lookAt(builder.vel().angle());
|
||||||
|
}
|
||||||
|
|
||||||
//approach request if building
|
//approach request if building
|
||||||
if(builder.building()){
|
if(builder.buildRequest() != null){
|
||||||
BuildRequest req = builder.buildRequest();
|
BuildRequest req = builder.buildRequest();
|
||||||
|
|
||||||
boolean valid =
|
boolean valid =
|
||||||
|
|||||||
@@ -1708,7 +1708,7 @@ public class Blocks implements ContentList{
|
|||||||
requirements(Category.turret, ItemStack.with(Items.silicon, 80, Items.thorium, 80, Items.surgealloy, 50));
|
requirements(Category.turret, ItemStack.with(Items.silicon, 80, Items.thorium, 80, Items.surgealloy, 50));
|
||||||
|
|
||||||
hasPower = true;
|
hasPower = true;
|
||||||
consumes.power(2f);
|
consumes.power(3f);
|
||||||
size = 2;
|
size = 2;
|
||||||
shootLength = 5f;
|
shootLength = 5f;
|
||||||
bulletDamage = 12f;
|
bulletDamage = 12f;
|
||||||
|
|||||||
@@ -311,8 +311,16 @@ public class Logic implements ApplicationListener{
|
|||||||
updateWeather();
|
updateWeather();
|
||||||
|
|
||||||
for(TeamData data : state.teams.getActive()){
|
for(TeamData data : state.teams.getActive()){
|
||||||
if(data.hasAI()){
|
if(data.hasAI() && data.hasCore()){
|
||||||
data.ai.update(data);
|
data.ai.update();
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO this is terrible
|
||||||
|
//fills enemy core with resources
|
||||||
|
if(state.rules.enemyInfiniteResources && state.rules.waves && data.team == state.rules.waveTeam && data.hasCore()){
|
||||||
|
for(Item item : content.items()){
|
||||||
|
data.core().items.set(item, data.core().block.itemCapacity);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ public enum Gamemode{
|
|||||||
}),
|
}),
|
||||||
attack(rules -> {
|
attack(rules -> {
|
||||||
rules.attackMode = true;
|
rules.attackMode = true;
|
||||||
|
rules.waves = true;
|
||||||
|
rules.waveTimer = true;
|
||||||
}, map -> map.teams.contains(state.rules.waveTeam.id)),
|
}, map -> map.teams.contains(state.rules.waveTeam.id)),
|
||||||
pvp(rules -> {
|
pvp(rules -> {
|
||||||
rules.pvp = true;
|
rules.pvp = true;
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ public class Rules{
|
|||||||
public boolean waves;
|
public boolean waves;
|
||||||
/** Whether the enemy AI has infinite resources in most of their buildings and turrets. */
|
/** Whether the enemy AI has infinite resources in most of their buildings and turrets. */
|
||||||
public boolean enemyCheat;
|
public boolean enemyCheat;
|
||||||
|
/** Whether the enemy AI has infinite resources in their core only. TODO remove */
|
||||||
|
public boolean enemyInfiniteResources = true;
|
||||||
/** Whether the game objective is PvP. Note that this enables automatic hosting. */
|
/** Whether the game objective is PvP. Note that this enables automatic hosting. */
|
||||||
public boolean pvp;
|
public boolean pvp;
|
||||||
/** Whether reactors can explode and damage other blocks. */
|
/** Whether reactors can explode and damage other blocks. */
|
||||||
@@ -69,6 +71,8 @@ public class Rules{
|
|||||||
/** Whether to draw shadows of blocks at map edges and static blocks.
|
/** Whether to draw shadows of blocks at map edges and static blocks.
|
||||||
* Do not change unless you know exactly what you are doing.*/
|
* Do not change unless you know exactly what you are doing.*/
|
||||||
public boolean drawDarkness = true;
|
public boolean drawDarkness = true;
|
||||||
|
/** EXPERIMENTAL building AI. TODO remove */
|
||||||
|
public boolean buildAI = true;
|
||||||
/** Starting items put in cores */
|
/** Starting items put in cores */
|
||||||
public Array<ItemStack> loadout = Array.with(ItemStack.with(Items.copper, 100));
|
public Array<ItemStack> loadout = Array.with(ItemStack.with(Items.copper, 100));
|
||||||
/** Weather events that occur here. */
|
/** Weather events that occur here. */
|
||||||
|
|||||||
@@ -6,12 +6,14 @@ import arc.files.*;
|
|||||||
import arc.graphics.*;
|
import arc.graphics.*;
|
||||||
import arc.graphics.g2d.*;
|
import arc.graphics.g2d.*;
|
||||||
import arc.graphics.gl.*;
|
import arc.graphics.gl.*;
|
||||||
|
import arc.math.*;
|
||||||
import arc.math.geom.*;
|
import arc.math.geom.*;
|
||||||
import arc.struct.*;
|
import arc.struct.*;
|
||||||
import arc.util.ArcAnnotate.*;
|
import arc.util.ArcAnnotate.*;
|
||||||
import arc.util.*;
|
import arc.util.*;
|
||||||
import arc.util.io.*;
|
import arc.util.io.*;
|
||||||
import arc.util.io.Streams.*;
|
import arc.util.io.Streams.*;
|
||||||
|
import arc.util.pooling.*;
|
||||||
import arc.util.serialization.*;
|
import arc.util.serialization.*;
|
||||||
import mindustry.*;
|
import mindustry.*;
|
||||||
import mindustry.content.*;
|
import mindustry.content.*;
|
||||||
@@ -38,6 +40,8 @@ import static mindustry.Vars.*;
|
|||||||
|
|
||||||
/** Handles schematics.*/
|
/** Handles schematics.*/
|
||||||
public class Schematics implements Loadable{
|
public class Schematics implements Loadable{
|
||||||
|
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);
|
||||||
public static final String base64Header = "bXNjaAB";
|
public static final String base64Header = "bXNjaAB";
|
||||||
|
|
||||||
private static final byte[] header = {'m', 's', 'c', 'h'};
|
private static final byte[] header = {'m', 's', 'c', 'h'};
|
||||||
@@ -526,5 +530,69 @@ public class Schematics implements Loadable{
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
//region misc utility
|
||||||
|
|
||||||
|
/** @return a temporary schematic representing the input rotated 90 degrees counterclockwise N times. */
|
||||||
|
public static 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static 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;
|
||||||
|
}
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -148,11 +148,12 @@ public class Teams{
|
|||||||
public final Array<CoreEntity> cores = new Array<>();
|
public final Array<CoreEntity> cores = new Array<>();
|
||||||
public final Array<Team> enemies = new Array<>();
|
public final Array<Team> enemies = new Array<>();
|
||||||
public final Team team;
|
public final Team team;
|
||||||
|
public final BaseAI ai;
|
||||||
public Queue<BlockPlan> blocks = new Queue<>();
|
public Queue<BlockPlan> blocks = new Queue<>();
|
||||||
public BaseAI ai = new BaseAI();
|
|
||||||
|
|
||||||
public TeamData(Team team){
|
public TeamData(Team team){
|
||||||
this.team = team;
|
this.team = team;
|
||||||
|
this.ai = new BaseAI(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean active(){
|
public boolean active(){
|
||||||
@@ -173,7 +174,7 @@ public class Teams{
|
|||||||
|
|
||||||
/** @return whether this team is controlled by the AI and builds bases. */
|
/** @return whether this team is controlled by the AI and builds bases. */
|
||||||
public boolean hasAI(){
|
public boolean hasAI(){
|
||||||
return state.rules.attackMode && team == state.rules.waveTeam;
|
return state.rules.attackMode && team == state.rules.waveTeam && state.rules.buildAI;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -191,9 +192,9 @@ public class Teams{
|
|||||||
public final short x, y, rotation, block;
|
public final short x, y, rotation, block;
|
||||||
public final Object config;
|
public final Object config;
|
||||||
|
|
||||||
public BlockPlan(short x, short y, short rotation, short block, Object config){
|
public BlockPlan(int x, int y, short rotation, short block, Object config){
|
||||||
this.x = x;
|
this.x = (short)x;
|
||||||
this.y = y;
|
this.y = (short)y;
|
||||||
this.rotation = rotation;
|
this.rotation = rotation;
|
||||||
this.block = block;
|
this.block = block;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
|
|||||||
@@ -4,10 +4,8 @@ import arc.func.*;
|
|||||||
import arc.math.*;
|
import arc.math.*;
|
||||||
import arc.math.geom.*;
|
import arc.math.geom.*;
|
||||||
import arc.struct.*;
|
import arc.struct.*;
|
||||||
import arc.util.pooling.*;
|
|
||||||
import mindustry.ai.BaseRegistry.*;
|
import mindustry.ai.BaseRegistry.*;
|
||||||
import mindustry.content.*;
|
import mindustry.content.*;
|
||||||
import mindustry.entities.units.*;
|
|
||||||
import mindustry.game.*;
|
import mindustry.game.*;
|
||||||
import mindustry.game.Schematic.*;
|
import mindustry.game.Schematic.*;
|
||||||
import mindustry.gen.*;
|
import mindustry.gen.*;
|
||||||
@@ -21,8 +19,6 @@ import mindustry.world.blocks.production.*;
|
|||||||
import static mindustry.Vars.*;
|
import static mindustry.Vars.*;
|
||||||
|
|
||||||
public class BaseGenerator{
|
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 static final Vec2 axis = new Vec2(), rotator = new Vec2();
|
||||||
|
|
||||||
private final static int range = 180;
|
private final static int range = 180;
|
||||||
@@ -138,15 +134,13 @@ public class BaseGenerator{
|
|||||||
|
|
||||||
void pass(Cons<Tile> cons){
|
void pass(Cons<Tile> cons){
|
||||||
Tile core = cores.first();
|
Tile core = cores.first();
|
||||||
//for(Tile core : cores){
|
|
||||||
core.circle(range, (x, y) -> cons.get(tiles.getn(x, y)));
|
core.circle(range, (x, y) -> cons.get(tiles.getn(x, y)));
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean tryPlace(BasePart part, int x, int y){
|
boolean tryPlace(BasePart part, int x, int y){
|
||||||
int rotation = Mathf.range(2);
|
int rotation = Mathf.range(2);
|
||||||
axis.set((int)(part.schematic.width / 2f), (int)(part.schematic.height / 2f));
|
axis.set((int)(part.schematic.width / 2f), (int)(part.schematic.height / 2f));
|
||||||
Schematic result = rotate(part.schematic, rotation);
|
Schematic result = Schematics.rotate(part.schematic, rotation);
|
||||||
int rotdeg = rotation*90;
|
int rotdeg = rotation*90;
|
||||||
|
|
||||||
rotator.set(part.centerX, part.centerY).rotateAround(axis, rotdeg);
|
rotator.set(part.centerX, part.centerY).rotateAround(axis, rotdeg);
|
||||||
@@ -211,64 +205,4 @@ public class BaseGenerator{
|
|||||||
|
|
||||||
return tile == null || !tile.block().alwaysReplace || world.getDarkness(x, y) > 0;
|
return tile == null || !tile.block().alwaysReplace || world.getDarkness(x, y) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user