Blast doors

This commit is contained in:
Anuken
2022-04-08 13:53:10 -04:00
parent 91acf1647a
commit 759d872115
12 changed files with 160 additions and 32 deletions

View File

@@ -29,7 +29,7 @@ public class ControlPathfinder{
//deep is impassable
PathTile.allDeep(tile) ? impassable :
//impassable same-team or neutral block
PathTile.solid(tile) && (PathTile.team(tile) == team || PathTile.team(tile) == 0) ? impassable :
PathTile.solid(tile) && ((PathTile.team(tile) == team && !PathTile.teamPassable(tile)) || PathTile.team(tile) == 0) ? impassable :
//impassable synthetic enemy block
((PathTile.team(tile) != team && PathTile.team(tile) != 0) && PathTile.solid(tile) ? wallImpassableCap : 0) +
1 +
@@ -152,15 +152,16 @@ public class ControlPathfinder{
if(threads == null) return false;
PathCost costType = unit.type.pathCost;
int team = unit.team.id;
//if the destination can be trivially reached in a straight line, do that.
if((!requests.containsKey(unit) || requests.get(unit).curId != pathId) && !raycast(costType, unit.tileX(), unit.tileY(), World.toTile(destination.x), World.toTile(destination.y))){
if((!requests.containsKey(unit) || requests.get(unit).curId != pathId) && !raycast(team, costType, unit.tileX(), unit.tileY(), World.toTile(destination.x), World.toTile(destination.y))){
out.set(destination);
return true;
}
//destination is impassable, can't go there.
if(solid(costType, world.packArray(World.toTile(destination.x), World.toTile(destination.y)))){
if(solid(team, costType, world.packArray(World.toTile(destination.x), World.toTile(destination.y)))){
return false;
}
@@ -173,7 +174,7 @@ public class ControlPathfinder{
req.cost = costType;
req.destination.set(destination);
req.curId = pathId;
req.team = unit.team.id;
req.team = team;
req.lastUpdateId = state.updateId;
req.lastPos.set(unit);
req.lastWorldUpdate = worldUpdateId;
@@ -222,7 +223,7 @@ public class ControlPathfinder{
Tile tile = tile(items[i]);
float dst = unit.dst2(tile);
//TODO maybe put this on a timer since raycasts can be expensive?
if(dst < minDst && !permissiveRaycast(costType, tileX, tileY, tile.x, tile.y)){
if(dst < minDst && !permissiveRaycast(team, costType, tileX, tileY, tile.x, tile.y)){
req.pathIndex = Math.max(dst <= range * range ? i + 1 : i, req.pathIndex);
minDst = Math.min(dst, minDst);
}
@@ -235,7 +236,7 @@ public class ControlPathfinder{
if((req.raycastTimer += Time.delta) >= 50f){
for(int i = len - 1; i > req.pathIndex; i--){
int val = items[i];
if(!raycast(costType, tileX, tileY, val % wwidth, val / wwidth)){
if(!raycast(team, costType, tileX, tileY, val % wwidth, val / wwidth)){
req.rayPathIndex = i;
break;
}
@@ -288,14 +289,14 @@ public class ControlPathfinder{
requests.clear();
}
private static boolean raycast(PathCost type, int x1, int y1, int x2, int y2){
private static boolean raycast(int team, PathCost type, int x1, int y1, int x2, int y2){
int ww = world.width(), wh = world.height();
int x = x1, dx = Math.abs(x2 - x), sx = x < x2 ? 1 : -1;
int y = y1, dy = Math.abs(y2 - y), sy = y < y2 ? 1 : -1;
int e2, err = dx - dy;
while(x >= 0 && y >= 0 && x < ww && y < wh){
if(avoid(type, x + y * wwidth)) return true;
if(avoid(team, type, x + y * wwidth)) return true;
if(x == x2 && y == y2) return false;
//TODO no diagonals???? is this a good idea?
@@ -326,14 +327,14 @@ public class ControlPathfinder{
return true;
}
private static boolean permissiveRaycast(PathCost type, int x1, int y1, int x2, int y2){
private static boolean permissiveRaycast(int team, PathCost type, int x1, int y1, int x2, int y2){
int ww = world.width(), wh = world.height();
int x = x1, dx = Math.abs(x2 - x), sx = x < x2 ? 1 : -1;
int y = y1, dy = Math.abs(y2 - y), sy = y < y2 ? 1 : -1;
int err = dx - dy;
while(x >= 0 && y >= 0 && x < ww && y < wh){
if(solid(type, x + y * wwidth)) return true;
if(solid(team, type, x + y * wwidth)) return true;
if(x == x2 && y == y2) return false;
//no diagonals
@@ -349,8 +350,8 @@ public class ControlPathfinder{
return true;
}
static boolean cast(PathCost cost, int from, int to){
return raycast(cost, from % wwidth, from / wwidth, to % wwidth, to / wwidth);
static boolean cast(int team, PathCost cost, int from, int to){
return raycast(team, cost, from % wwidth, from / wwidth, to % wwidth, to / wwidth);
}
private Tile tile(int pos){
@@ -367,23 +368,23 @@ public class ControlPathfinder{
return cost.getCost(team, pathfinder.tiles[tilePos]);
}
private static int cost(PathCost cost, int tilePos){
return cost.getCost(-1, pathfinder.tiles[tilePos]);
private static int cost(int team, PathCost cost, int tilePos){
return cost.getCost(team, pathfinder.tiles[tilePos]);
}
private static boolean avoid(PathCost type, int tilePos){
int cost = cost(type, tilePos);
private static boolean avoid(int team, PathCost type, int tilePos){
int cost = cost(team, type, tilePos);
return cost == impassable || cost >= 2;
}
private static boolean solid(PathCost type, int tilePos){
int cost = cost(type, tilePos);
private static boolean solid(int team, PathCost type, int tilePos){
int cost = cost(team, type, tilePos);
return cost == impassable || cost >= 6000;
}
private static float tileCost(PathCost type, int a, int b){
private static float tileCost(int team, PathCost type, int a, int b){
//currently flat cost
return cost(type, b);
return cost(team, type, b);
}
static class PathfindThread extends Thread{
@@ -506,7 +507,7 @@ public class ControlPathfinder{
//in fallback mode, enemy walls are passable
if(tcost(team, cost, next) == impassable) continue;
float add = tileCost(cost, current, next);
float add = tileCost(team, cost, current, next);
float currentCost = costs.get(current);
//the cost can include an impassable enemy wall, so cap the cost if so and add the base cost instead
@@ -564,7 +565,7 @@ public class ControlPathfinder{
int output = 1, input = 2;
while(input < len){
if(cast(cost, result.get(output - 1), result.get(input))){
if(cast(team, cost, result.get(output - 1), result.get(input))){
result.swap(output, input - 1);
output++;
}

View File

@@ -40,7 +40,7 @@ public class Pathfinder implements Runnable{
public static final Seq<PathCost> costTypes = Seq.with(
//ground
(team, tile) ->
(PathTile.allDeep(tile) || (PathTile.team(tile) == team || PathTile.team(tile) == 0) && PathTile.solid(tile)) ? impassable : 1 +
(PathTile.allDeep(tile) || ((PathTile.team(tile) == team && !PathTile.teamPassable(tile)) || PathTile.team(tile) == 0) && PathTile.solid(tile)) ? impassable : 1 +
PathTile.health(tile) * 5 +
(PathTile.nearSolid(tile) ? 2 : 0) +
(PathTile.nearLiquid(tile) ? 6 : 0) +
@@ -146,21 +146,22 @@ public class Pathfinder implements Runnable{
}
/** Packs a tile into its internal representation. */
private int packTile(Tile tile, int prev){
public int packTile(Tile tile, int prev){
boolean nearLiquid = false, nearSolid = false, nearGround = false, solid = tile.solid(), allDeep = tile.floor().isDeep();
for(int i = 0; i < 4; i++){
Tile other = tile.nearby(i);
if(other != null){
if(other != null) {
Floor floor = other.floor();
boolean osolid = other.solid();
if(floor.isLiquid) nearLiquid = true;
if(osolid) nearSolid = true;
//TODO potentially strange behavior when teamPassable is false for other teams?
if(osolid && !other.block().teamPassable) nearSolid = true;
if(!floor.isLiquid) nearGround = true;
if(!floor.isDeep()) allDeep = false;
//other tile is now near solid
if(solid){
if(solid && !tile.block().teamPassable){
tiles[other.array()] |= PathTile.bitMaskNearSolid;
}
}
@@ -179,10 +180,15 @@ public class Pathfinder implements Runnable{
nearSolid,
tile.floor().isDeep(),
tile.floor().damageTaken > 0.00001f,
allDeep
allDeep,
tile.block().teamPassable
);
}
public int get(int x, int y){
return tiles[x + y * wwidth];
}
/** Starts or restarts the pathfinding thread. */
private void start(){
stop();
@@ -551,5 +557,7 @@ public class Pathfinder implements Runnable{
boolean damages;
//whether all tiles nearby are deep
boolean allDeep;
//block teamPassable is true
boolean teamPassable;
}
}

View File

@@ -80,7 +80,7 @@ public class Blocks{
//defense
copperWall, copperWallLarge, titaniumWall, titaniumWallLarge, plastaniumWall, plastaniumWallLarge, thoriumWall, thoriumWallLarge, door, doorLarge,
phaseWall, phaseWallLarge, surgeWall, surgeWallLarge,
berylliumWall, berylliumWallLarge, tungstenWall, tungstenWallLarge, carbideWall, carbideWallLarge,
berylliumWall, berylliumWallLarge, tungstenWall, tungstenWallLarge, blastDoor, carbideWall, carbideWallLarge,
mender, mendProjector, overdriveProjector, overdriveDome, forceProjector, shockMine,
scrapWall, scrapWallLarge, scrapWallHuge, scrapWallGigantic, thruster, //ok, these names are getting ridiculous, but at least I don't have humongous walls yet
@@ -1610,6 +1610,13 @@ public class Blocks{
size = 2;
}};
blastDoor = new AutoDoor("blast-door"){{
requirements(Category.defense, with(Items.tungsten, 24, Items.silicon, 24));
health = 175 * wallHealthMultiplier * 4;
armor = 14f;
size = 2;
}};
carbideWall = new Wall("carbide-wall"){{
requirements(Category.defense, with(Items.thorium, 6, Items.carbide, 6));
health = 240 * wallHealthMultiplier;

View File

@@ -234,7 +234,9 @@ public class ErekirTechTree{
node(tungstenWall, () -> {
node(tungstenWallLarge, () -> {
node(blastDoor, () -> {
});
});
node(carbideWall, () -> {

View File

@@ -1996,12 +1996,12 @@ public class Fx{
dooropen = new Effect(10, e -> {
stroke(e.fout() * 1.6f);
Lines.square(e.x, e.y, tilesize / 2f + e.fin() * 2f);
Lines.square(e.x, e.y, e.rotation * tilesize / 2f + e.fin() * 2f);
}),
doorclose = new Effect(10, e -> {
stroke(e.fout() * 1.6f);
Lines.square(e.x, e.y, tilesize / 2f + e.fout() * 2f);
Lines.square(e.x, e.y, e.rotation * tilesize / 2f + e.fout() * 2f);
}),
dooropenlarge = new Effect(10, e -> {

View File

@@ -88,6 +88,8 @@ public class Block extends UnlockableContent implements Senseable{
public boolean solid;
/** whether this block CAN be solid. */
public boolean solidifes;
/** if true, this counts as a non-solid block to this team. */
public boolean teamPassable;
/** whether this is rotatable */
public boolean rotate;
/** if rotate is true and this is false, the region won't rotate when drawing */

View File

@@ -0,0 +1,107 @@
package mindustry.world.blocks.defense;
import arc.audio.*;
import arc.func.*;
import arc.graphics.g2d.*;
import arc.math.*;
import arc.math.geom.*;
import arc.struct.*;
import arc.util.io.*;
import mindustry.annotations.Annotations.*;
import mindustry.content.*;
import mindustry.entities.*;
import mindustry.gen.*;
import mindustry.logic.*;
import mindustry.world.*;
import static mindustry.Vars.*;
public class AutoDoor extends Wall{
protected final static Rect rect = new Rect();
protected final static Seq<Unit> units = new Seq<>();
protected final static Boolf<Unit> groundCheck = u -> u.isGrounded() && !u.type.allowLegStep;
public final int timerToggle = timers++;
public float checkInterval = 20f;
public Effect openfx = Fx.dooropen;
public Effect closefx = Fx.doorclose;
public Sound doorSound = Sounds.door;
public @Load("@-open") TextureRegion openRegion;
public float triggerMargin = 10f;
public AutoDoor(String name){
super(name);
solid = false;
solidifes = true;
update = true;
teamPassable = true;
noUpdateDisabled = true;
drawDisabled = true;
}
@Remote(called = Loc.server)
public static void autoDoorToggle(Tile tile, boolean open){
if(tile == null || !(tile.build instanceof AutoDoorBuild build)) return;
build.setOpen(open);
}
public class AutoDoorBuild extends Building{
public boolean open = false;
public AutoDoorBuild(){
//make sure it is staggered
timer.reset(timerToggle, Mathf.random(checkInterval));
}
@Override
public void updateTile(){
if(timer(timerToggle, checkInterval) && !net.client()){
units.clear();
team.data().tree().intersect(rect.setSize(size * tilesize + triggerMargin * 2f).setCenter(x, y), units);
boolean shouldOpen = units.contains(groundCheck);
if(open != shouldOpen){
Call.autoDoorToggle(tile, shouldOpen);
}
}
}
@Override
public double sense(LAccess sensor){
if(sensor == LAccess.enabled) return open ? 1 : 0;
return super.sense(sensor);
}
public void setOpen(boolean open){
this.open = open;
(!open ? closefx : openfx).at(this, size);
doorSound.at(this);
pathfinder.updateTile(tile);
}
@Override
public void draw(){
Draw.rect(open ? openRegion : region, x, y);
}
@Override
public boolean checkSolid(){
return !open;
}
@Override
public void write(Writes write){
super.write(write);
write.bool(open);
}
@Override
public void read(Reads read, byte revision){
super.read(read, revision);
open = read.bool();
}
}
}

View File

@@ -100,7 +100,7 @@ public class Door extends Wall{
}
public void effect(){
(open ? closefx : openfx).at(this);
(open ? closefx : openfx).at(this, size);
}
public void updateChained(){