Merge branch 'master' of https://github.com/Anuken/Mindustry into 7.0-features
Conflicts: core/src/mindustry/editor/MapInfoDialog.java core/src/mindustry/maps/filters/ClearFilter.java core/src/mindustry/mod/ClassMap.java gradle.properties
This commit is contained in:
@@ -492,6 +492,7 @@ filter.option.block = Block
|
|||||||
filter.option.floor = Floor
|
filter.option.floor = Floor
|
||||||
filter.option.flooronto = Target Floor
|
filter.option.flooronto = Target Floor
|
||||||
filter.option.target = Target
|
filter.option.target = Target
|
||||||
|
filter.option.replacement = Replacement
|
||||||
filter.option.wall = Wall
|
filter.option.wall = Wall
|
||||||
filter.option.ore = Ore
|
filter.option.ore = Ore
|
||||||
filter.option.floor2 = Secondary Floor
|
filter.option.floor2 = Secondary Floor
|
||||||
@@ -994,6 +995,7 @@ rules.waves = Waves
|
|||||||
rules.attack = Attack Mode
|
rules.attack = Attack Mode
|
||||||
rules.buildai = AI Building
|
rules.buildai = AI Building
|
||||||
rules.corecapture = Capture Core On Destruction
|
rules.corecapture = Capture Core On Destruction
|
||||||
|
rules.polygoncoreprotection = Polygonal Core Protection
|
||||||
rules.enemyCheat = Infinite AI (Red Team) Resources
|
rules.enemyCheat = Infinite AI (Red Team) Resources
|
||||||
rules.blockhealthmultiplier = Block Health Multiplier
|
rules.blockhealthmultiplier = Block Health Multiplier
|
||||||
rules.blockdamagemultiplier = Block Damage Multiplier
|
rules.blockdamagemultiplier = Block Damage Multiplier
|
||||||
|
|||||||
Binary file not shown.
BIN
core/assets/maps/passage.msav
Normal file
BIN
core/assets/maps/passage.msav
Normal file
Binary file not shown.
Binary file not shown.
@@ -1113,6 +1113,47 @@ public class Fx{
|
|||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
impactReactorExplosion = new Effect(30, 500f, b -> {
|
||||||
|
float intensity = 8f;
|
||||||
|
float baseLifetime = 25f + intensity * 15f;
|
||||||
|
b.lifetime = 50f + intensity * 64f;
|
||||||
|
|
||||||
|
color(Pal.lighterOrange);
|
||||||
|
alpha(0.8f);
|
||||||
|
for(int i = 0; i < 5; i++){
|
||||||
|
rand.setSeed(b.id*2 + i);
|
||||||
|
float lenScl = rand.random(0.25f, 1f);
|
||||||
|
int fi = i;
|
||||||
|
b.scaled(b.lifetime * lenScl, e -> {
|
||||||
|
randLenVectors(e.id + fi - 1, e.fin(Interp.pow10Out), (int)(2.8f * intensity), 25f * intensity, (x, y, in, out) -> {
|
||||||
|
float fout = e.fout(Interp.pow5Out) * rand.random(0.5f, 1f);
|
||||||
|
float rad = fout * ((2f + intensity) * 2.35f);
|
||||||
|
|
||||||
|
Fill.circle(e.x + x, e.y + y, rad);
|
||||||
|
Drawf.light(e.x + x, e.y + y, rad * 2.6f, Pal.lighterOrange, 0.7f);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
b.scaled(baseLifetime, e -> {
|
||||||
|
Draw.color();
|
||||||
|
e.scaled(5 + intensity * 2f, i -> {
|
||||||
|
stroke((3.1f + intensity/5f) * i.fout());
|
||||||
|
Lines.circle(e.x, e.y, (3f + i.fin() * 14f) * intensity);
|
||||||
|
Drawf.light(e.x, e.y, i.fin() * 14f * 2f * intensity, Color.white, 0.9f * e.fout());
|
||||||
|
});
|
||||||
|
|
||||||
|
color(Color.white, Pal.lighterOrange, e.fin());
|
||||||
|
stroke((2f * e.fout()));
|
||||||
|
|
||||||
|
Draw.z(Layer.effect + 0.001f);
|
||||||
|
randLenVectors(e.id + 1, e.finpow() + 0.001f, (int)(8 * intensity), 30f * intensity, (x, y, in, out) -> {
|
||||||
|
lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), 1f + out * 4 * (4f + intensity));
|
||||||
|
Drawf.light(e.x + x, e.y + y, (out * 4 * (3f + intensity)) * 3.5f, Draw.getColor(), 0.8f);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
|
||||||
blockExplosion = new Effect(30, e -> {
|
blockExplosion = new Effect(30, e -> {
|
||||||
e.scaled(7, i -> {
|
e.scaled(7, i -> {
|
||||||
stroke(3.1f * i.fout());
|
stroke(3.1f * i.fout());
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import arc.util.*;
|
|||||||
import mindustry.*;
|
import mindustry.*;
|
||||||
import mindustry.game.*;
|
import mindustry.game.*;
|
||||||
import mindustry.io.*;
|
import mindustry.io.*;
|
||||||
|
import mindustry.maps.filters.*;
|
||||||
import mindustry.ui.*;
|
import mindustry.ui.*;
|
||||||
import mindustry.ui.dialogs.*;
|
import mindustry.ui.dialogs.*;
|
||||||
|
|
||||||
@@ -74,7 +75,11 @@ public class MapInfoDialog extends BaseDialog{
|
|||||||
t.row();
|
t.row();
|
||||||
t.add("@editor.generation").padRight(8).left();
|
t.add("@editor.generation").padRight(8).left();
|
||||||
t.button("@edit", () -> {
|
t.button("@edit", () -> {
|
||||||
generate.show(maps.readFilters(editor.tags.get("genfilters", "")),
|
//randomize so they're not all the same seed
|
||||||
|
var res = maps.readFilters(editor.tags.get("genfilters", ""));
|
||||||
|
res.each(GenerateFilter::randomize);
|
||||||
|
|
||||||
|
generate.show(res,
|
||||||
filters -> {
|
filters -> {
|
||||||
//reset seed to 0 so it is not written
|
//reset seed to 0 so it is not written
|
||||||
filters.each(f -> f.seed = 0);
|
filters.each(f -> f.seed = 0);
|
||||||
|
|||||||
@@ -34,13 +34,18 @@ public class Damage{
|
|||||||
dynamicExplosion(x, y, flammability, explosiveness, power, radius, damage, true, null, Fx.dynamicExplosion);
|
dynamicExplosion(x, y, flammability, explosiveness, power, radius, damage, true, null, Fx.dynamicExplosion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Creates a dynamic explosion based on specified parameters. */
|
||||||
|
public static void dynamicExplosion(float x, float y, float flammability, float explosiveness, float power, float radius, boolean damage, Effect explosionFx){
|
||||||
|
dynamicExplosion(x, y, flammability, explosiveness, power, radius, damage, true, null, explosionFx);
|
||||||
|
}
|
||||||
|
|
||||||
/** Creates a dynamic explosion based on specified parameters. */
|
/** Creates a dynamic explosion based on specified parameters. */
|
||||||
public static void dynamicExplosion(float x, float y, float flammability, float explosiveness, float power, float radius, boolean damage, boolean fire, @Nullable Team ignoreTeam){
|
public static void dynamicExplosion(float x, float y, float flammability, float explosiveness, float power, float radius, boolean damage, boolean fire, @Nullable Team ignoreTeam){
|
||||||
dynamicExplosion(x, y, flammability, explosiveness, power, radius, damage, fire, ignoreTeam, Fx.dynamicExplosion);
|
dynamicExplosion(x, y, flammability, explosiveness, power, radius, damage, fire, ignoreTeam, Fx.dynamicExplosion);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a dynamic explosion based on specified parameters. */
|
/** Creates a dynamic explosion based on specified parameters. */
|
||||||
public static void dynamicExplosion(float x, float y, float flammability, float explosiveness, float power, float radius, boolean damage, boolean fire, @Nullable Team ignoreTeam, Effect explosion){
|
public static void dynamicExplosion(float x, float y, float flammability, float explosiveness, float power, float radius, boolean damage, boolean fire, @Nullable Team ignoreTeam, Effect explosionFx){
|
||||||
if(damage){
|
if(damage){
|
||||||
for(int i = 0; i < Mathf.clamp(power / 700, 0, 8); i++){
|
for(int i = 0; i < Mathf.clamp(power / 700, 0, 8); i++){
|
||||||
int length = 5 + Mathf.clamp((int)(power / 500), 1, 20);
|
int length = 5 + Mathf.clamp((int)(power / 500), 1, 20);
|
||||||
@@ -74,7 +79,7 @@ public class Damage{
|
|||||||
|
|
||||||
float shake = Math.min(explosiveness / 4f + 3f, 9f);
|
float shake = Math.min(explosiveness / 4f + 3f, 9f);
|
||||||
Effect.shake(shake, shake, x, y);
|
Effect.shake(shake, shake, x, y);
|
||||||
explosion.at(x, y, radius / 8f);
|
explosionFx.at(x, y, radius / 8f);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void createIncend(float x, float y, float range, int amount){
|
public static void createIncend(float x, float y, float range, int amount){
|
||||||
|
|||||||
@@ -1059,7 +1059,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Damage.dynamicExplosion(x, y, flammability, explosiveness * 3.5f, power, tilesize * block.size / 2f, state.rules.damageExplosions);
|
Damage.dynamicExplosion(x, y, flammability, explosiveness * 3.5f, power, tilesize * block.size / 2f, state.rules.damageExplosions, block.destroyEffect);
|
||||||
|
|
||||||
if(!floor().solid && !floor().isLiquid){
|
if(!floor().solid && !floor().isLiquid){
|
||||||
Effect.rubble(x, y, block.size);
|
Effect.rubble(x, y, block.size);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import mindustry.net.*;
|
|||||||
import mindustry.net.Packets.*;
|
import mindustry.net.Packets.*;
|
||||||
import mindustry.type.*;
|
import mindustry.type.*;
|
||||||
import mindustry.world.*;
|
import mindustry.world.*;
|
||||||
|
import mindustry.world.blocks.storage.CoreBlock.*;
|
||||||
|
|
||||||
public class EventType{
|
public class EventType{
|
||||||
|
|
||||||
@@ -283,6 +284,15 @@ public class EventType{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Called when a core block is placed/removed or its team is changed. */
|
||||||
|
public static class CoreChangeEvent{
|
||||||
|
public CoreBuild core;
|
||||||
|
|
||||||
|
public CoreChangeEvent(CoreBuild core){
|
||||||
|
this.core = core;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static class StateChangeEvent{
|
public static class StateChangeEvent{
|
||||||
public final State from, to;
|
public final State from, to;
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,8 @@ public class Rules{
|
|||||||
public float deconstructRefundMultiplier = 0.5f;
|
public float deconstructRefundMultiplier = 0.5f;
|
||||||
/** No-build zone around enemy core radius. */
|
/** No-build zone around enemy core radius. */
|
||||||
public float enemyCoreBuildRadius = 400f;
|
public float enemyCoreBuildRadius = 400f;
|
||||||
|
/** If true, no-build zones are calculated based on the closest core. */
|
||||||
|
public boolean polygonCoreProtection = false;
|
||||||
/** Radius around enemy wave drop zones.*/
|
/** Radius around enemy wave drop zones.*/
|
||||||
public float dropZoneRadius = 300f;
|
public float dropZoneRadius = 300f;
|
||||||
/** Time between waves in ticks. */
|
/** Time between waves in ticks. */
|
||||||
|
|||||||
@@ -5,14 +5,18 @@ import arc.graphics.*;
|
|||||||
import arc.graphics.g2d.*;
|
import arc.graphics.g2d.*;
|
||||||
import arc.math.*;
|
import arc.math.*;
|
||||||
import arc.math.geom.*;
|
import arc.math.geom.*;
|
||||||
|
import arc.struct.*;
|
||||||
import arc.util.*;
|
import arc.util.*;
|
||||||
import mindustry.*;
|
import mindustry.*;
|
||||||
import mindustry.ai.types.*;
|
import mindustry.ai.types.*;
|
||||||
import mindustry.entities.*;
|
import mindustry.entities.*;
|
||||||
|
import mindustry.game.EventType.*;
|
||||||
|
import mindustry.game.*;
|
||||||
|
import mindustry.game.Teams.*;
|
||||||
import mindustry.gen.*;
|
import mindustry.gen.*;
|
||||||
import mindustry.input.*;
|
import mindustry.input.*;
|
||||||
import mindustry.ui.*;
|
|
||||||
import mindustry.world.*;
|
import mindustry.world.*;
|
||||||
|
import mindustry.world.blocks.storage.CoreBlock.*;
|
||||||
|
|
||||||
import static mindustry.Vars.*;
|
import static mindustry.Vars.*;
|
||||||
|
|
||||||
@@ -23,6 +27,42 @@ public class OverlayRenderer{
|
|||||||
|
|
||||||
private float buildFade, unitFade;
|
private float buildFade, unitFade;
|
||||||
private Sized lastSelect;
|
private Sized lastSelect;
|
||||||
|
private Seq<CoreEdge> cedges = new Seq<>();
|
||||||
|
private boolean updatedCores;
|
||||||
|
|
||||||
|
public OverlayRenderer(){
|
||||||
|
Events.on(WorldLoadEvent.class, e -> {
|
||||||
|
updatedCores = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
Events.on(CoreChangeEvent.class, e -> {
|
||||||
|
updatedCores = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateCoreEdges(){
|
||||||
|
if(!updatedCores){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedCores = false;
|
||||||
|
cedges.clear();
|
||||||
|
|
||||||
|
Seq<Vec2> pos = new Seq<>();
|
||||||
|
Seq<CoreBuild> teams = new Seq<>();
|
||||||
|
for(TeamData team : state.teams.active){
|
||||||
|
for(CoreBuild b : team.cores){
|
||||||
|
teams.add(b);
|
||||||
|
pos.add(new Vec2(b.x, b.y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//if this is laggy, it could be shoved in another thread.
|
||||||
|
var result = Voronoi.generate(pos.toArray(Vec2.class), 0, world.unitWidth(), 0, world.unitHeight());
|
||||||
|
for(var edge : result){
|
||||||
|
cedges.add(new CoreEdge(edge.x1, edge.y1, edge.x2, edge.y2, teams.get(edge.site1).team, teams.get(edge.site2).team));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void drawBottom(){
|
public void drawBottom(){
|
||||||
InputHandler input = control.input;
|
InputHandler input = control.input;
|
||||||
@@ -117,15 +157,32 @@ public class OverlayRenderer{
|
|||||||
Lines.stroke(buildFade * 2f);
|
Lines.stroke(buildFade * 2f);
|
||||||
|
|
||||||
if(buildFade > 0.005f){
|
if(buildFade > 0.005f){
|
||||||
state.teams.eachEnemyCore(player.team(), core -> {
|
if(state.rules.polygonCoreProtection){
|
||||||
float dst = core.dst(player);
|
updateCoreEdges();
|
||||||
if(dst < state.rules.enemyCoreBuildRadius * 2.2f){
|
Draw.color(Pal.accent);
|
||||||
Draw.color(Color.darkGray);
|
|
||||||
Lines.circle(core.x, core.y - 2, state.rules.enemyCoreBuildRadius);
|
for(int i = 0; i < 2; i++){
|
||||||
Draw.color(Pal.accent, core.team.color, 0.5f + Mathf.absin(Time.time, 10f, 0.5f));
|
float offset = (i == 0 ? -2f : 0f);
|
||||||
Lines.circle(core.x, core.y, state.rules.enemyCoreBuildRadius);
|
for(CoreEdge edge : cedges){
|
||||||
|
Team displayed = edge.displayed();
|
||||||
|
if(displayed != null){
|
||||||
|
Draw.color(i == 0 ? Color.darkGray : Tmp.c1.set(displayed.color).lerp(Pal.accent, Mathf.absin(Time.time, 10f, 0.2f)));
|
||||||
|
Lines.line(edge.x1, edge.y1 + offset, edge.x2, edge.y2 + offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
Draw.color();
|
||||||
|
}else{
|
||||||
|
state.teams.eachEnemyCore(player.team(), core -> {
|
||||||
|
if(Core.camera.bounds(Tmp.r1).overlaps(Tmp.r2.setCentered(core.x, core.y, state.rules.enemyCoreBuildRadius * 2f))){
|
||||||
|
Draw.color(Color.darkGray);
|
||||||
|
Lines.circle(core.x, core.y - 2, state.rules.enemyCoreBuildRadius);
|
||||||
|
Draw.color(Pal.accent, core.team.color, 0.5f + Mathf.absin(Time.time, 10f, 0.5f));
|
||||||
|
Lines.circle(core.x, core.y, state.rules.enemyCoreBuildRadius);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Lines.stroke(2f);
|
Lines.stroke(2f);
|
||||||
@@ -192,4 +249,28 @@ public class OverlayRenderer{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class CoreEdge{
|
||||||
|
float x1, y1, x2, y2;
|
||||||
|
Team t1, t2;
|
||||||
|
|
||||||
|
public CoreEdge(float x1, float y1, float x2, float y2, Team t1, Team t2){
|
||||||
|
this.x1 = x1;
|
||||||
|
this.y1 = y1;
|
||||||
|
this.x2 = x2;
|
||||||
|
this.y2 = y2;
|
||||||
|
this.t1 = t1;
|
||||||
|
this.t2 = t2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
Team displayed(){
|
||||||
|
return
|
||||||
|
t1 == t2 ? null :
|
||||||
|
t1 == player.team() ? t2 :
|
||||||
|
t2 == player.team() ? t1 :
|
||||||
|
t2.id == 0 ? t1 :
|
||||||
|
t1.id < t2.id && t1.id != 0 ? t1 : t2;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
643
core/src/mindustry/graphics/Voronoi.java
Executable file
643
core/src/mindustry/graphics/Voronoi.java
Executable file
@@ -0,0 +1,643 @@
|
|||||||
|
package mindustry.graphics;
|
||||||
|
|
||||||
|
import arc.math.geom.*;
|
||||||
|
import arc.struct.*;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
//TODO in dire need of cleanup
|
||||||
|
public class Voronoi{
|
||||||
|
private final static int LE = 0;
|
||||||
|
private final static int RE = 1;
|
||||||
|
|
||||||
|
//TODO make local
|
||||||
|
int siteidx;
|
||||||
|
Site[] sites;
|
||||||
|
int nsites;
|
||||||
|
float borderMinX, borderMaxX, borderMinY, borderMaxY;
|
||||||
|
float ymin;
|
||||||
|
float deltay;
|
||||||
|
int nvertices = 0;
|
||||||
|
int nedges;
|
||||||
|
Site bottomsite;
|
||||||
|
int PQcount;
|
||||||
|
int PQmin;
|
||||||
|
int PQhashsize;
|
||||||
|
Halfedge[] PQhash;
|
||||||
|
int ELhashsize;
|
||||||
|
Halfedge[] ELhash;
|
||||||
|
Seq<GraphEdge> allEdges;
|
||||||
|
float minDistanceBetweenSites = 1f;
|
||||||
|
|
||||||
|
public static Seq<GraphEdge> generate(Vec2[] values, float minX, float maxX, float minY, float maxY){
|
||||||
|
return new Voronoi().generateVoronoi(values, minX, maxX, minY, maxY);
|
||||||
|
}
|
||||||
|
|
||||||
|
Seq<GraphEdge> generateVoronoi(Vec2[] values, float minX, float maxX, float minY, float maxY){
|
||||||
|
allEdges = new Seq<>();
|
||||||
|
|
||||||
|
nsites = values.length;
|
||||||
|
|
||||||
|
float sn = (float)nsites + 4;
|
||||||
|
int rtsites = (int)Math.sqrt(sn);
|
||||||
|
|
||||||
|
sites = new Site[nsites];
|
||||||
|
Vec2 first = values[0];
|
||||||
|
float xmin = first.x;
|
||||||
|
ymin = first.y;
|
||||||
|
float xmax = first.x;
|
||||||
|
float ymax = first.y;
|
||||||
|
for(int i = 0; i < nsites; i++){
|
||||||
|
sites[i] = new Site();
|
||||||
|
sites[i].coord.set(values[i]);
|
||||||
|
sites[i].sitenbr = i;
|
||||||
|
|
||||||
|
if(values[i].x < xmin){
|
||||||
|
xmin = values[i].x;
|
||||||
|
}else if(values[i].x > xmax){
|
||||||
|
xmax = values[i].x;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(values[i].y < ymin){
|
||||||
|
ymin = values[i].y;
|
||||||
|
}else if(values[i].y > ymax){
|
||||||
|
ymax = values[i].y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Arrays.sort(sites, (p1, p2) -> {
|
||||||
|
Vec2 s1 = p1.coord, s2 = p2.coord;
|
||||||
|
if(s1.y < s2.y){
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
|
if(s1.y > s2.y){
|
||||||
|
return (1);
|
||||||
|
}
|
||||||
|
return Float.compare(s1.x, s2.x);
|
||||||
|
});
|
||||||
|
|
||||||
|
deltay = ymax - ymin;
|
||||||
|
float deltax = xmax - xmin;
|
||||||
|
|
||||||
|
// Check bounding box inputs - if mins are bigger than maxes, swap them
|
||||||
|
float temp;
|
||||||
|
if(minX > maxX){
|
||||||
|
temp = minX;
|
||||||
|
minX = maxX;
|
||||||
|
maxX = temp;
|
||||||
|
}
|
||||||
|
if(minY > maxY){
|
||||||
|
temp = minY;
|
||||||
|
minY = maxY;
|
||||||
|
maxY = temp;
|
||||||
|
}
|
||||||
|
borderMinX = minX;
|
||||||
|
borderMinY = minY;
|
||||||
|
borderMaxX = maxX;
|
||||||
|
borderMaxY = maxY;
|
||||||
|
|
||||||
|
siteidx = 0;
|
||||||
|
|
||||||
|
PQcount = 0;
|
||||||
|
PQmin = 0;
|
||||||
|
PQhashsize = 4 * rtsites;
|
||||||
|
PQhash = new Halfedge[PQhashsize];
|
||||||
|
|
||||||
|
for(int i2 = 0; i2 < PQhashsize; i2 += 1){
|
||||||
|
PQhash[i2] = new Halfedge();
|
||||||
|
}
|
||||||
|
int i1;
|
||||||
|
ELhashsize = 2 * rtsites;
|
||||||
|
ELhash = new Halfedge[ELhashsize];
|
||||||
|
|
||||||
|
for(i1 = 0; i1 < ELhashsize; i1 += 1){
|
||||||
|
ELhash[i1] = null;
|
||||||
|
}
|
||||||
|
Halfedge ELleftend = newHe(null, 0);
|
||||||
|
Halfedge ELrightend = newHe(null, 0);
|
||||||
|
ELleftend.ELleft = null;
|
||||||
|
ELleftend.ELright = ELrightend;
|
||||||
|
ELrightend.ELleft = ELleftend;
|
||||||
|
ELrightend.ELright = null;
|
||||||
|
ELhash[0] = ELleftend;
|
||||||
|
ELhash[ELhashsize - 1] = ELrightend;
|
||||||
|
|
||||||
|
bottomsite = next();
|
||||||
|
Site newsite = next();
|
||||||
|
Halfedge lbnd;
|
||||||
|
Vec2 newintstar = null;
|
||||||
|
Edge e;
|
||||||
|
while(true){
|
||||||
|
if(PQcount != 0){
|
||||||
|
Vec2 answer = new Vec2();
|
||||||
|
|
||||||
|
while(PQhash[PQmin].PQnext == null){
|
||||||
|
PQmin += 1;
|
||||||
|
}
|
||||||
|
answer.x = PQhash[PQmin].PQnext.vertex.coord.x;
|
||||||
|
answer.y = PQhash[PQmin].PQnext.ystar;
|
||||||
|
newintstar = (answer);
|
||||||
|
}
|
||||||
|
|
||||||
|
Halfedge rbnd;
|
||||||
|
Halfedge bisector;
|
||||||
|
Site p;
|
||||||
|
Site bot;
|
||||||
|
|
||||||
|
if(newsite != null && (PQcount == 0 || newsite.coord.y < newintstar.y || (newsite.coord.y == newintstar.y && newsite.coord.x < newintstar.x))){
|
||||||
|
int bucket = (int)(((newsite.coord).x - xmin) / deltax * ELhashsize);
|
||||||
|
|
||||||
|
if(bucket < 0){
|
||||||
|
bucket = 0;
|
||||||
|
}
|
||||||
|
if(bucket >= ELhashsize){
|
||||||
|
bucket = ELhashsize - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Halfedge he = getHash(bucket);
|
||||||
|
if(he == null){
|
||||||
|
for(int i = 1; i < ELhashsize; i += 1){
|
||||||
|
if((he = getHash(bucket - i)) != null){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if((he = getHash(bucket + i)) != null){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(he == ELleftend || (he != ELrightend && right(he, (newsite.coord)))){
|
||||||
|
do{
|
||||||
|
he = he.ELright;
|
||||||
|
}while(he != ELrightend && right(he, (newsite.coord)));
|
||||||
|
he = he.ELleft;
|
||||||
|
}else{
|
||||||
|
do{
|
||||||
|
he = he.ELleft;
|
||||||
|
}while(he != ELleftend && !right(he, (newsite.coord)));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(bucket > 0 && bucket < ELhashsize - 1){
|
||||||
|
ELhash[bucket] = he;
|
||||||
|
}
|
||||||
|
lbnd = he;
|
||||||
|
rbnd = lbnd.ELright;
|
||||||
|
|
||||||
|
bot = rightreg(lbnd);
|
||||||
|
e = bisect(bot, newsite);
|
||||||
|
|
||||||
|
bisector = newHe(e, LE);
|
||||||
|
insert(lbnd, bisector);
|
||||||
|
|
||||||
|
if((p = intersect(lbnd, bisector)) != null){
|
||||||
|
pqdelete(lbnd);
|
||||||
|
pqinsert(lbnd, p, p.coord.dst(newsite.coord));
|
||||||
|
}
|
||||||
|
lbnd = bisector;
|
||||||
|
bisector = newHe(e, RE);
|
||||||
|
insert(lbnd, bisector);
|
||||||
|
|
||||||
|
if((p = intersect(bisector, rbnd)) != null){
|
||||||
|
pqinsert(bisector, p, p.coord.dst(newsite.coord));
|
||||||
|
}
|
||||||
|
newsite = next();
|
||||||
|
}else if(!(PQcount == 0)){
|
||||||
|
Halfedge curr;
|
||||||
|
|
||||||
|
curr = PQhash[PQmin].PQnext;
|
||||||
|
PQhash[PQmin].PQnext = curr.PQnext;
|
||||||
|
PQcount -= 1;
|
||||||
|
lbnd = (curr);
|
||||||
|
Halfedge llbnd = lbnd.ELleft;
|
||||||
|
rbnd = lbnd.ELright;
|
||||||
|
Halfedge rrbnd = (rbnd.ELright);
|
||||||
|
bot = leftReg(lbnd);
|
||||||
|
Site top = rightreg(rbnd);
|
||||||
|
|
||||||
|
Site v = lbnd.vertex;
|
||||||
|
v.sitenbr = nvertices;
|
||||||
|
nvertices += 1;
|
||||||
|
endpoint(lbnd.ELedge, lbnd.ELpm, v);
|
||||||
|
endpoint(rbnd.ELedge, rbnd.ELpm, v);
|
||||||
|
delete(lbnd);
|
||||||
|
pqdelete(rbnd);
|
||||||
|
delete(rbnd);
|
||||||
|
int pm = LE;
|
||||||
|
|
||||||
|
if(bot.coord.y > top.coord.y){
|
||||||
|
Site temp1 = bot;
|
||||||
|
bot = top;
|
||||||
|
top = temp1;
|
||||||
|
pm = RE;
|
||||||
|
}
|
||||||
|
|
||||||
|
e = bisect(bot, top);
|
||||||
|
bisector = newHe(e, pm);
|
||||||
|
insert(llbnd, bisector);
|
||||||
|
endpoint(e, RE - pm, v);
|
||||||
|
|
||||||
|
if((p = intersect(llbnd, bisector)) != null){
|
||||||
|
pqdelete(llbnd);
|
||||||
|
pqinsert(llbnd, p, p.coord.dst(bot.coord));
|
||||||
|
}
|
||||||
|
|
||||||
|
if((p = intersect(bisector, rrbnd)) != null){
|
||||||
|
pqinsert(bisector, p, p.coord.dst(bot.coord));
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(lbnd = (ELleftend.ELright); lbnd != ELrightend; lbnd = (lbnd.ELright)){
|
||||||
|
e = lbnd.ELedge;
|
||||||
|
clipLine(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return allEdges;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Site next(){
|
||||||
|
return siteidx < nsites ? sites[siteidx ++] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Edge bisect(Site s1, Site s2){
|
||||||
|
Edge newedge = new Edge();
|
||||||
|
|
||||||
|
// store the sites that this edge is bisecting
|
||||||
|
newedge.reg[0] = s1;
|
||||||
|
newedge.reg[1] = s2;
|
||||||
|
// to begin with, there are no endpoints on the bisector - it goes to
|
||||||
|
// infinity
|
||||||
|
newedge.ep[0] = null;
|
||||||
|
newedge.ep[1] = null;
|
||||||
|
|
||||||
|
// get the difference in x dist between the sites
|
||||||
|
float dx = s2.coord.x - s1.coord.x;
|
||||||
|
float dy = s2.coord.y - s1.coord.y;
|
||||||
|
// make sure that the difference in positive
|
||||||
|
float adx = dx > 0 ? dx : -dx;
|
||||||
|
float ady = dy > 0 ? dy : -dy;
|
||||||
|
newedge.c = s1.coord.x * dx + s1.coord.y * dy + (dx * dx + dy * dy) * 0.5f;// get the slope of the line
|
||||||
|
|
||||||
|
if(adx > ady){
|
||||||
|
newedge.a = 1.0f;
|
||||||
|
newedge.b = dy / dx;
|
||||||
|
newedge.c /= dx;// set formula of line, with x fixed to 1
|
||||||
|
}else{
|
||||||
|
newedge.b = 1.0f;
|
||||||
|
newedge.a = dx / dy;
|
||||||
|
newedge.c /= dy;// set formula of line, with y fixed to 1
|
||||||
|
}
|
||||||
|
|
||||||
|
newedge.edgenbr = nedges;
|
||||||
|
|
||||||
|
nedges += 1;
|
||||||
|
return newedge;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int pqbucket(Halfedge he){
|
||||||
|
int bucket;
|
||||||
|
|
||||||
|
bucket = (int)((he.ystar - ymin) / deltay * PQhashsize);
|
||||||
|
if(bucket < 0){
|
||||||
|
bucket = 0;
|
||||||
|
}
|
||||||
|
if(bucket >= PQhashsize){
|
||||||
|
bucket = PQhashsize - 1;
|
||||||
|
}
|
||||||
|
if(bucket < PQmin){
|
||||||
|
PQmin = bucket;
|
||||||
|
}
|
||||||
|
return bucket;
|
||||||
|
}
|
||||||
|
|
||||||
|
// push the HalfEdge into the ordered linked list of vertices
|
||||||
|
private void pqinsert(Halfedge he, Site v, float offset){
|
||||||
|
Halfedge last, next;
|
||||||
|
|
||||||
|
he.vertex = v;
|
||||||
|
he.ystar = v.coord.y + offset;
|
||||||
|
last = PQhash[pqbucket(he)];
|
||||||
|
while((next = last.PQnext) != null
|
||||||
|
&& (he.ystar > next.ystar || (he.ystar == next.ystar && v.coord.x > next.vertex.coord.x))){
|
||||||
|
last = next;
|
||||||
|
}
|
||||||
|
he.PQnext = last.PQnext;
|
||||||
|
last.PQnext = he;
|
||||||
|
PQcount += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove the HalfEdge from the list of vertices
|
||||||
|
private void pqdelete(Halfedge he){
|
||||||
|
Halfedge last;
|
||||||
|
|
||||||
|
if(he.vertex != null){
|
||||||
|
last = PQhash[pqbucket(he)];
|
||||||
|
while(last.PQnext != he){
|
||||||
|
last = last.PQnext;
|
||||||
|
}
|
||||||
|
|
||||||
|
last.PQnext = he.PQnext;
|
||||||
|
PQcount -= 1;
|
||||||
|
he.vertex = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Halfedge newHe(Edge e, int pm){
|
||||||
|
Halfedge answer = new Halfedge();
|
||||||
|
answer.ELedge = e;
|
||||||
|
answer.ELpm = pm;
|
||||||
|
answer.PQnext = null;
|
||||||
|
answer.vertex = null;
|
||||||
|
return answer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Site leftReg(Halfedge he){
|
||||||
|
if(he.ELedge == null){
|
||||||
|
return bottomsite;
|
||||||
|
}
|
||||||
|
return he.ELpm == LE ? he.ELedge.reg[LE] : he.ELedge.reg[RE];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void insert(Halfedge lb, Halfedge newHe){
|
||||||
|
newHe.ELleft = lb;
|
||||||
|
newHe.ELright = lb.ELright;
|
||||||
|
lb.ELright.ELleft = newHe;
|
||||||
|
lb.ELright = newHe;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This delete routine can't reclaim node, since pointers from hash table
|
||||||
|
* may be present.
|
||||||
|
*/
|
||||||
|
private void delete(Halfedge he){
|
||||||
|
he.ELleft.ELright = he.ELright;
|
||||||
|
he.ELright.ELleft = he.ELleft;
|
||||||
|
he.deleted = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get entry from hash table, pruning any deleted nodes */
|
||||||
|
private Halfedge getHash(int b){
|
||||||
|
Halfedge he;
|
||||||
|
|
||||||
|
if(b < 0 || b >= ELhashsize){
|
||||||
|
return (null);
|
||||||
|
}
|
||||||
|
he = ELhash[b];
|
||||||
|
if(he == null || !he.deleted){
|
||||||
|
return (he);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hash table points to deleted half edge. Patch as necessary. */
|
||||||
|
ELhash[b] = null;
|
||||||
|
return (null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clipLine(Edge e){
|
||||||
|
float pxmin, pxmax, pymin, pymax;
|
||||||
|
Site s1, s2;
|
||||||
|
float x1 = 0, x2 = 0, y1 = 0, y2 = 0;
|
||||||
|
|
||||||
|
x1 = e.reg[0].coord.x;
|
||||||
|
x2 = e.reg[1].coord.x;
|
||||||
|
y1 = e.reg[0].coord.y;
|
||||||
|
y2 = e.reg[1].coord.y;
|
||||||
|
|
||||||
|
// if the distance between the two points this line was created from is
|
||||||
|
// less than the square root of 2, then ignore it
|
||||||
|
if(Math.sqrt(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1))) < minDistanceBetweenSites){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pxmin = borderMinX;
|
||||||
|
pxmax = borderMaxX;
|
||||||
|
pymin = borderMinY;
|
||||||
|
pymax = borderMaxY;
|
||||||
|
|
||||||
|
if(e.a == 1.0 && e.b >= 0.0){
|
||||||
|
s1 = e.ep[1];
|
||||||
|
s2 = e.ep[0];
|
||||||
|
}else{
|
||||||
|
s1 = e.ep[0];
|
||||||
|
s2 = e.ep[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(e.a == 1.0){
|
||||||
|
y1 = pymin;
|
||||||
|
if(s1 != null && s1.coord.y > pymin){
|
||||||
|
y1 = s1.coord.y;
|
||||||
|
}
|
||||||
|
if(y1 > pymax){
|
||||||
|
y1 = pymax;
|
||||||
|
}
|
||||||
|
x1 = e.c - e.b * y1;
|
||||||
|
y2 = pymax;
|
||||||
|
if(s2 != null && s2.coord.y < pymax){
|
||||||
|
y2 = s2.coord.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(y2 < pymin){
|
||||||
|
y2 = pymin;
|
||||||
|
}
|
||||||
|
x2 = (e.c) - (e.b) * y2;
|
||||||
|
if(((x1 > pxmax) & (x2 > pxmax)) | ((x1 < pxmin) & (x2 < pxmin))){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(x1 > pxmax){
|
||||||
|
x1 = pxmax;
|
||||||
|
y1 = (e.c - x1) / e.b;
|
||||||
|
}
|
||||||
|
if(x1 < pxmin){
|
||||||
|
x1 = pxmin;
|
||||||
|
y1 = (e.c - x1) / e.b;
|
||||||
|
}
|
||||||
|
if(x2 > pxmax){
|
||||||
|
x2 = pxmax;
|
||||||
|
y2 = (e.c - x2) / e.b;
|
||||||
|
}
|
||||||
|
if(x2 < pxmin){
|
||||||
|
x2 = pxmin;
|
||||||
|
y2 = (e.c - x2) / e.b;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
x1 = pxmin;
|
||||||
|
if(s1 != null && s1.coord.x > pxmin){
|
||||||
|
x1 = s1.coord.x;
|
||||||
|
}
|
||||||
|
if(x1 > pxmax){
|
||||||
|
x1 = pxmax;
|
||||||
|
}
|
||||||
|
y1 = e.c - e.a * x1;
|
||||||
|
x2 = pxmax;
|
||||||
|
if(s2 != null && s2.coord.x < pxmax){
|
||||||
|
x2 = s2.coord.x;
|
||||||
|
}
|
||||||
|
if(x2 < pxmin){
|
||||||
|
x2 = pxmin;
|
||||||
|
}
|
||||||
|
y2 = e.c - e.a * x2;
|
||||||
|
if(((y1 > pymax) & (y2 > pymax)) | ((y1 < pymin) & (y2 < pymin))){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(y1 > pymax){
|
||||||
|
y1 = pymax;
|
||||||
|
x1 = (e.c - y1) / e.a;
|
||||||
|
}
|
||||||
|
if(y1 < pymin){
|
||||||
|
y1 = pymin;
|
||||||
|
x1 = (e.c - y1) / e.a;
|
||||||
|
}
|
||||||
|
if(y2 > pymax){
|
||||||
|
y2 = pymax;
|
||||||
|
x2 = (e.c - y2) / e.a;
|
||||||
|
}
|
||||||
|
if(y2 < pymin){
|
||||||
|
y2 = pymin;
|
||||||
|
x2 = (e.c - y2) / e.a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
GraphEdge newEdge = new GraphEdge();
|
||||||
|
allEdges.add(newEdge);
|
||||||
|
newEdge.x1 = x1;
|
||||||
|
newEdge.y1 = y1;
|
||||||
|
newEdge.x2 = x2;
|
||||||
|
newEdge.y2 = y2;
|
||||||
|
|
||||||
|
newEdge.site1 = e.reg[0].sitenbr;
|
||||||
|
newEdge.site2 = e.reg[1].sitenbr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void endpoint(Edge e, int lr, Site s){
|
||||||
|
e.ep[lr] = s;
|
||||||
|
if(e.ep[RE - lr] == null){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
clipLine(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean right(Halfedge el, Vec2 p){
|
||||||
|
Edge e = el.ELedge;
|
||||||
|
Site topsite = e.reg[1];
|
||||||
|
boolean rightOf = p.x > topsite.coord.x;
|
||||||
|
if(rightOf && el.ELpm == LE){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if(!rightOf && el.ELpm == RE){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean above;
|
||||||
|
if(e.a == 1.0){
|
||||||
|
float dyp = p.y - topsite.coord.y;
|
||||||
|
float dxp = p.x - topsite.coord.x;
|
||||||
|
boolean fast = false;
|
||||||
|
if((!rightOf & (e.b < 0.0)) | (rightOf & (e.b >= 0.0))){
|
||||||
|
above = dyp >= e.b * dxp;
|
||||||
|
fast = above;
|
||||||
|
}else{
|
||||||
|
above = p.x + p.y * e.b > e.c;
|
||||||
|
if(e.b < 0.0){
|
||||||
|
above = !above;
|
||||||
|
}
|
||||||
|
if(!above){
|
||||||
|
fast = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!fast){
|
||||||
|
float dxs = topsite.coord.x - (e.reg[0]).coord.x;
|
||||||
|
above = e.b * (dxp * dxp - dyp * dyp) < dxs * dyp
|
||||||
|
* (1.0 + 2.0 * dxp / dxs + e.b * e.b);
|
||||||
|
if(e.b < 0.0){
|
||||||
|
above = !above;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
float yl = e.c - e.a * p.x;
|
||||||
|
float t1 = p.y - yl;
|
||||||
|
float t2 = p.x - topsite.coord.x;
|
||||||
|
float t3 = yl - topsite.coord.y;
|
||||||
|
above = t1 * t1 > t2 * t2 + t3 * t3;
|
||||||
|
}
|
||||||
|
return ((el.ELpm == LE) == above);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Site rightreg(Halfedge he){
|
||||||
|
if(he.ELedge == null) return bottomsite;
|
||||||
|
|
||||||
|
return (he.ELpm == LE ? he.ELedge.reg[RE] : he.ELedge.reg[LE]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Site intersect(Halfedge el1, Halfedge el2){
|
||||||
|
Edge e1, e2, e;
|
||||||
|
Halfedge el;
|
||||||
|
float d, xint, yint;
|
||||||
|
boolean right_of_site;
|
||||||
|
Site v;
|
||||||
|
|
||||||
|
e1 = el1.ELedge;
|
||||||
|
e2 = el2.ELedge;
|
||||||
|
if(e1 == null || e2 == null){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(e1.reg[1] == e2.reg[1]){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
d = e1.a * e2.b - e1.b * e2.a;
|
||||||
|
if(-1.0e-10 < d && d < 1.0e-10){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
xint = (e1.c * e2.b - e2.c * e1.b) / d;
|
||||||
|
yint = (e2.c * e1.a - e1.c * e2.a) / d;
|
||||||
|
|
||||||
|
if((e1.reg[1].coord.y < e2.reg[1].coord.y)
|
||||||
|
|| (e1.reg[1].coord.y == e2.reg[1].coord.y && e1.reg[1].coord.x < e2.reg[1].coord.x)){
|
||||||
|
el = el1;
|
||||||
|
e = e1;
|
||||||
|
}else{
|
||||||
|
el = el2;
|
||||||
|
e = e2;
|
||||||
|
}
|
||||||
|
|
||||||
|
right_of_site = xint >= e.reg[1].coord.x;
|
||||||
|
if((right_of_site && el.ELpm == LE)
|
||||||
|
|| (!right_of_site && el.ELpm == RE)){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
v = new Site();
|
||||||
|
v.coord.x = xint;
|
||||||
|
v.coord.y = yint;
|
||||||
|
return (v);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Site{
|
||||||
|
Vec2 coord = new Vec2();
|
||||||
|
int sitenbr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Halfedge{
|
||||||
|
Halfedge ELleft, ELright;
|
||||||
|
Edge ELedge;
|
||||||
|
boolean deleted;
|
||||||
|
int ELpm;
|
||||||
|
Site vertex;
|
||||||
|
float ystar;
|
||||||
|
Halfedge PQnext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class GraphEdge{
|
||||||
|
public float x1, y1, x2, y2;
|
||||||
|
|
||||||
|
public int site1, site2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Edge{
|
||||||
|
float a = 0, b = 0, c = 0;
|
||||||
|
Site[] ep = new Site[2];
|
||||||
|
Site[] reg = new Site[2];
|
||||||
|
int edgenbr;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,9 +29,9 @@ import static mindustry.Vars.*;
|
|||||||
|
|
||||||
public class Maps{
|
public class Maps{
|
||||||
/** List of all built-in maps. Filenames only. */
|
/** List of all built-in maps. Filenames only. */
|
||||||
private static String[] defaultMapNames = {"maze", "fortress", "labyrinth", "islands", "tendrils", "caldera", "wasteland", "shattered", "fork", "triad", "mudFlats", "moltenLake", "archipelago", "debrisField", "veins", "glacier"};
|
private static String[] defaultMapNames = {"maze", "fortress", "labyrinth", "islands", "tendrils", "caldera", "wasteland", "shattered", "fork", "triad", "mudFlats", "moltenLake", "archipelago", "debrisField", "veins", "glacier", "passage"};
|
||||||
/** Maps tagged as PvP */
|
/** Maps tagged as PvP */
|
||||||
static final String[] pvpMaps = {"veins", "glacier"};
|
static final String[] pvpMaps = {"veins", "glacier", "passage"};
|
||||||
/** All maps stored in an ordered array. */
|
/** All maps stored in an ordered array. */
|
||||||
private Seq<Map> maps = new Seq<>();
|
private Seq<Map> maps = new Seq<>();
|
||||||
/** Serializer for meta. */
|
/** Serializer for meta. */
|
||||||
|
|||||||
@@ -7,12 +7,14 @@ import mindustry.world.*;
|
|||||||
import static mindustry.maps.filters.FilterOption.*;
|
import static mindustry.maps.filters.FilterOption.*;
|
||||||
|
|
||||||
public class ClearFilter extends GenerateFilter{
|
public class ClearFilter extends GenerateFilter{
|
||||||
protected Block block = Blocks.air;
|
protected Block target = Blocks.stone;
|
||||||
|
protected Block replace = Blocks.air;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public FilterOption[] options(){
|
public FilterOption[] options(){
|
||||||
return new BlockOption[]{
|
return new BlockOption[]{
|
||||||
new BlockOption("block", () -> block, b -> block = b, b -> oresOnly.get(b) || wallsOnly.get(b))
|
new BlockOption("target", () -> target, b -> target = b, anyOptional),
|
||||||
|
new BlockOption("replacement", () -> replace, b -> replace = b, anyOptional)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,12 +26,21 @@ public class ClearFilter extends GenerateFilter{
|
|||||||
@Override
|
@Override
|
||||||
public void apply(GenerateInput in){
|
public void apply(GenerateInput in){
|
||||||
|
|
||||||
if(in.block == block){
|
if(in.block == target || in.floor == target || (target.isOverlay() && in.overlay == target)){
|
||||||
in.block = Blocks.air;
|
//special case: when air is the result, replace only the overlay or wall
|
||||||
}
|
if(replace == Blocks.air){
|
||||||
|
if(in.overlay == target){
|
||||||
if(in.overlay == block){
|
in.overlay = Blocks.air;
|
||||||
in.overlay = Blocks.air;
|
}else{
|
||||||
|
in.block = Blocks.air;
|
||||||
|
}
|
||||||
|
}else if(replace.isOverlay()){ //replace the best match based on type
|
||||||
|
in.overlay = replace;
|
||||||
|
}else if(replace.isFloor()){
|
||||||
|
in.floor = replace;
|
||||||
|
}else{
|
||||||
|
in.block = replace;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package mindustry.mod;
|
package mindustry.mod;
|
||||||
|
|
||||||
import arc.struct.*;
|
import arc.struct.*;
|
||||||
|
import mindustry.world.blocks.environment.*;
|
||||||
import mindustry.world.blocks.payloads.*;
|
import mindustry.world.blocks.payloads.*;
|
||||||
|
|
||||||
/** Generated class. Maps simple class names to concrete classes. For use in JSON mods. */
|
/** Generated class. Maps simple class names to concrete classes. For use in JSON mods. */
|
||||||
@@ -173,8 +174,11 @@ public class ClassMap{
|
|||||||
classes.put("OreBlock", mindustry.world.blocks.environment.OreBlock.class);
|
classes.put("OreBlock", mindustry.world.blocks.environment.OreBlock.class);
|
||||||
classes.put("OverlayFloor", mindustry.world.blocks.environment.OverlayFloor.class);
|
classes.put("OverlayFloor", mindustry.world.blocks.environment.OverlayFloor.class);
|
||||||
classes.put("Prop", mindustry.world.blocks.environment.Prop.class);
|
classes.put("Prop", mindustry.world.blocks.environment.Prop.class);
|
||||||
|
classes.put("Bush", Bush.class);
|
||||||
|
classes.put("WavingProp", WavingProp.class);
|
||||||
classes.put("ShallowLiquid", mindustry.world.blocks.environment.ShallowLiquid.class);
|
classes.put("ShallowLiquid", mindustry.world.blocks.environment.ShallowLiquid.class);
|
||||||
classes.put("SpawnBlock", mindustry.world.blocks.environment.SpawnBlock.class);
|
classes.put("SpawnBlock", mindustry.world.blocks.environment.SpawnBlock.class);
|
||||||
|
classes.put("StaticClusterWall", StaticClusterWall.class);
|
||||||
classes.put("StaticTree", mindustry.world.blocks.environment.StaticTree.class);
|
classes.put("StaticTree", mindustry.world.blocks.environment.StaticTree.class);
|
||||||
classes.put("StaticWall", mindustry.world.blocks.environment.StaticWall.class);
|
classes.put("StaticWall", mindustry.world.blocks.environment.StaticWall.class);
|
||||||
classes.put("TreeBlock", mindustry.world.blocks.environment.TreeBlock.class);
|
classes.put("TreeBlock", mindustry.world.blocks.environment.TreeBlock.class);
|
||||||
|
|||||||
@@ -170,7 +170,8 @@ public class CustomRulesDialog extends BaseDialog{
|
|||||||
check("@rules.attack", b -> rules.attackMode = b, () -> rules.attackMode);
|
check("@rules.attack", b -> rules.attackMode = b, () -> rules.attackMode);
|
||||||
check("@rules.buildai", b -> rules.teams.get(rules.waveTeam).ai = rules.teams.get(rules.waveTeam).infiniteResources = b, () -> rules.teams.get(rules.waveTeam).ai);
|
check("@rules.buildai", b -> rules.teams.get(rules.waveTeam).ai = rules.teams.get(rules.waveTeam).infiniteResources = b, () -> rules.teams.get(rules.waveTeam).ai);
|
||||||
check("@rules.corecapture", b -> rules.coreCapture = b, () -> rules.coreCapture);
|
check("@rules.corecapture", b -> rules.coreCapture = b, () -> rules.coreCapture);
|
||||||
number("@rules.enemycorebuildradius", f -> rules.enemyCoreBuildRadius = f * tilesize, () -> Math.min(rules.enemyCoreBuildRadius / tilesize, 200));
|
check("@rules.polygoncoreprotection", b -> rules.polygonCoreProtection = b, () -> rules.polygonCoreProtection);
|
||||||
|
number("@rules.enemycorebuildradius", f -> rules.enemyCoreBuildRadius = f * tilesize, () -> Math.min(rules.enemyCoreBuildRadius / tilesize, 200), () -> !rules.polygonCoreProtection);
|
||||||
|
|
||||||
title("@rules.title.environment");
|
title("@rules.title.environment");
|
||||||
check("@rules.explosions", b -> rules.damageExplosions = b, () -> rules.damageExplosions);
|
check("@rules.explosions", b -> rules.damageExplosions = b, () -> rules.damageExplosions);
|
||||||
|
|||||||
@@ -229,6 +229,8 @@ public class Block extends UnlockableContent{
|
|||||||
public boolean instantDeconstruct = false;
|
public boolean instantDeconstruct = false;
|
||||||
/** Effect for breaking the block. Passes size as rotation. */
|
/** Effect for breaking the block. Passes size as rotation. */
|
||||||
public Effect breakEffect = Fx.breakBlock;
|
public Effect breakEffect = Fx.breakBlock;
|
||||||
|
/** Effect for destroying the block. */
|
||||||
|
public Effect destroyEffect = Fx.dynamicExplosion;
|
||||||
/** Multiplier for cost of research in tech tree. */
|
/** Multiplier for cost of research in tech tree. */
|
||||||
public float researchCostMultiplier = 1;
|
public float researchCostMultiplier = 1;
|
||||||
/** Whether this block has instant transfer.*/
|
/** Whether this block has instant transfer.*/
|
||||||
|
|||||||
@@ -10,9 +10,11 @@ import mindustry.content.*;
|
|||||||
import mindustry.entities.*;
|
import mindustry.entities.*;
|
||||||
import mindustry.game.EventType.*;
|
import mindustry.game.EventType.*;
|
||||||
import mindustry.game.*;
|
import mindustry.game.*;
|
||||||
|
import mindustry.game.Teams.*;
|
||||||
import mindustry.gen.*;
|
import mindustry.gen.*;
|
||||||
import mindustry.world.blocks.*;
|
import mindustry.world.blocks.*;
|
||||||
import mindustry.world.blocks.ConstructBlock.*;
|
import mindustry.world.blocks.ConstructBlock.*;
|
||||||
|
import mindustry.world.blocks.storage.CoreBlock.*;
|
||||||
|
|
||||||
import static mindustry.Vars.*;
|
import static mindustry.Vars.*;
|
||||||
|
|
||||||
@@ -132,7 +134,23 @@ public class Build{
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(state.teams.eachEnemyCore(team, core -> Mathf.dst(x * tilesize + type.offset, y * tilesize + type.offset, core.x, core.y) < state.rules.enemyCoreBuildRadius + type.size * tilesize / 2f)){
|
//find closest core, if it doesn't match the team, placing is not legal
|
||||||
|
if(state.rules.polygonCoreProtection){
|
||||||
|
float mindst = Float.MAX_VALUE;
|
||||||
|
CoreBuild closest = null;
|
||||||
|
for(TeamData data : state.teams.active){
|
||||||
|
for(CoreBuild tile : data.cores){
|
||||||
|
float dst = tile.dst2(x * tilesize + type.offset, y * tilesize + type.offset);
|
||||||
|
if(dst < mindst){
|
||||||
|
closest = tile;
|
||||||
|
mindst = dst;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(closest != null && closest.team != team){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}else if(state.teams.eachEnemyCore(team, core -> Mathf.dst(x * tilesize + type.offset, y * tilesize + type.offset, core.x, core.y) < state.rules.enemyCoreBuildRadius + type.size * tilesize / 2f)){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ public class ImpactReactor extends PowerGenerator{
|
|||||||
public float itemDuration = 60f;
|
public float itemDuration = 60f;
|
||||||
public int explosionRadius = 23;
|
public int explosionRadius = 23;
|
||||||
public int explosionDamage = 1900;
|
public int explosionDamage = 1900;
|
||||||
|
public Effect explodeEffect = Fx.impactReactorExplosion;
|
||||||
|
|
||||||
public Color plasma1 = Color.valueOf("ffd06b"), plasma2 = Color.valueOf("ff361b");
|
public Color plasma1 = Color.valueOf("ffd06b"), plasma2 = Color.valueOf("ff361b");
|
||||||
|
|
||||||
@@ -137,32 +138,14 @@ public class ImpactReactor extends PowerGenerator{
|
|||||||
public void onDestroyed(){
|
public void onDestroyed(){
|
||||||
super.onDestroyed();
|
super.onDestroyed();
|
||||||
|
|
||||||
if(warmup < 0.4f || !state.rules.reactorExplosions) return;
|
if(warmup < 0.3f || !state.rules.reactorExplosions) return;
|
||||||
|
|
||||||
Sounds.explosionbig.at(tile);
|
Sounds.explosionbig.at(tile);
|
||||||
|
|
||||||
Effect.shake(6f, 16f, x, y);
|
|
||||||
Fx.impactShockwave.at(x, y);
|
|
||||||
for(int i = 0; i < 6; i++){
|
|
||||||
Time.run(Mathf.random(80), () -> Fx.impactcloud.at(x, y));
|
|
||||||
}
|
|
||||||
|
|
||||||
Damage.damage(x, y, explosionRadius * tilesize, explosionDamage * 4);
|
Damage.damage(x, y, explosionRadius * tilesize, explosionDamage * 4);
|
||||||
|
|
||||||
|
Effect.shake(6f, 16f, x, y);
|
||||||
for(int i = 0; i < 20; i++){
|
explodeEffect.at(x, y);
|
||||||
Time.run(Mathf.random(80), () -> {
|
|
||||||
Tmp.v1.rnd(Mathf.random(40f));
|
|
||||||
Fx.explosion.at(Tmp.v1.x + x, Tmp.v1.y + y);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for(int i = 0; i < 70; i++){
|
|
||||||
Time.run(Mathf.random(90), () -> {
|
|
||||||
Tmp.v1.rnd(Mathf.random(120f));
|
|
||||||
Fx.impactsmoke.at(Tmp.v1.x + x, Tmp.v1.y + y);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -194,6 +194,20 @@ public class CoreBlock extends StorageBlock{
|
|||||||
super.damage(source, damage);
|
super.damage(source, damage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void created(){
|
||||||
|
super.created();
|
||||||
|
|
||||||
|
Events.fire(new CoreChangeEvent(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void changeTeam(Team next){
|
||||||
|
super.changeTeam(next);
|
||||||
|
|
||||||
|
Events.fire(new CoreChangeEvent(this));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double sense(LAccess sensor){
|
public double sense(LAccess sensor){
|
||||||
if(sensor == LAccess.itemCapacity) return storageCapacity;
|
if(sensor == LAccess.itemCapacity) return storageCapacity;
|
||||||
@@ -260,6 +274,8 @@ public class CoreBlock extends StorageBlock{
|
|||||||
spawner.getSpawns().add(tile);
|
spawner.getSpawns().add(tile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Events.fire(new CoreChangeEvent(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -10,4 +10,4 @@ kapt.include.compile.classpath=false
|
|||||||
kotlin.stdlib.default.dependency=false
|
kotlin.stdlib.default.dependency=false
|
||||||
#needed for android compilation
|
#needed for android compilation
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
archash=07ced971f4c8b8b5a61aa3a84b29c90aa497cb48
|
archash=df2786dd8de1ebc1b70c453d6dcac62c2ecf9e18
|
||||||
|
|||||||
Reference in New Issue
Block a user