World play area rule

This commit is contained in:
Anuken 2022-02-16 12:45:48 -05:00
parent e2c7c94663
commit 427d43039b
15 changed files with 196 additions and 54 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 271 B

After

Width:  |  Height:  |  Size: 277 B

Before After
Before After

View file

@ -1071,6 +1071,7 @@ rules.unithealthmultiplier = Unit Health Multiplier
rules.unitdamagemultiplier = Unit Damage Multiplier rules.unitdamagemultiplier = Unit Damage Multiplier
rules.unitcapvariable = Cores Contribute To Unit Cap rules.unitcapvariable = Cores Contribute To Unit Cap
rules.unitcap = Base Unit Cap rules.unitcap = Base Unit Cap
rules.limitarea = Limit Map Area
rules.enemycorebuildradius = Enemy Core No-Build Radius:[lightgray] (tiles) rules.enemycorebuildradius = Enemy Core No-Build Radius:[lightgray] (tiles)
rules.wavespacing = Wave Spacing:[lightgray] (sec) rules.wavespacing = Wave Spacing:[lightgray] (sec)
rules.initialwavespacing = Initial Wave Spacing:[lightgray] (sec) rules.initialwavespacing = Initial Wave Spacing:[lightgray] (sec)

View file

@ -1079,7 +1079,9 @@ public class Fx{
}), }),
ventSteam = new Effect(140f, e -> { ventSteam = new Effect(140f, e -> {
color(e.color, e.fslope() * 0.85f); color(e.color, Pal.vent2, e.fin());
alpha(e.fslope() * 0.78f);
float length = 3f + e.finpow() * 10f; float length = 3f + e.finpow() * 10f;
rand.setSeed(e.id); rand.setSeed(e.id);
@ -1087,7 +1089,7 @@ public class Fx{
v.trns(rand.random(360f), rand.random(length)); v.trns(rand.random(360f), rand.random(length));
Fill.circle(e.x + v.x, e.y + v.y, rand.random(1.2f, 3.5f) + e.fslope() * 1.1f); Fill.circle(e.x + v.x, e.y + v.y, rand.random(1.2f, 3.5f) + e.fslope() * 1.1f);
} }
}), }).layer(Layer.darkness - 1),
drillSteam = new Effect(220f, e -> { drillSteam = new Effect(220f, e -> {

View file

@ -223,6 +223,11 @@ public class Renderer implements ApplicationListener{
} }
} }
public void updateAllDarkness(){
blocks.updateDarkness();
minimap.updateAll();
}
/** @return whether a launch/land cutscene is playing. */ /** @return whether a launch/land cutscene is playing. */
public boolean isCutscene(){ public boolean isCutscene(){
return landTime > 0; return landTime > 0;

View file

@ -231,7 +231,7 @@ public class World{
} }
public Rect getQuadBounds(Rect in){ public Rect getQuadBounds(Rect in){
return in.set(-finalWorldBounds, -finalWorldBounds, world.width() * tilesize + finalWorldBounds * 2, world.height() * tilesize + finalWorldBounds * 2); return in.set(-finalWorldBounds, -finalWorldBounds, width() * tilesize + finalWorldBounds * 2, height() * tilesize + finalWorldBounds * 2);
} }
public void setGenerating(boolean gen){ public void setGenerating(boolean gen){
@ -295,8 +295,8 @@ public class World{
ObjectSet<UnlockableContent> content = new ObjectSet<>(); ObjectSet<UnlockableContent> content = new ObjectSet<>();
//TODO duplicate code? //TODO duplicate code?
for(Tile tile : world.tiles){ for(Tile tile : tiles){
if(world.getDarkness(tile.x, tile.y) >= 3){ if(getDarkness(tile.x, tile.y) >= 3){
continue; continue;
} }
@ -499,7 +499,17 @@ public class World{
if(Vars.state.rules.borderDarkness){ if(Vars.state.rules.borderDarkness){
int edgeBlend = 2; int edgeBlend = 2;
int edgeDst = Math.min(x, Math.min(y, Math.min(Math.abs(x - (tiles.width - 1)), Math.abs(y - (tiles.height - 1))))); int edgeDst;
if(!state.rules.limitMapArea){
edgeDst = Math.min(x, Math.min(y, Math.min(-(x - (tiles.width - 1)), -(y - (tiles.height - 1)))));
}else{
edgeDst =
Math.min(x - state.rules.limitX,
Math.min(y - state.rules.limitY,
Math.min(-(x - (state.rules.limitX + state.rules.limitWidth - 1)), -(y - (state.rules.limitY + state.rules.limitHeight - 1)))));
}
if(edgeDst <= edgeBlend){ if(edgeDst <= edgeBlend){
dark = Math.max((edgeBlend - edgeDst) * (4f / edgeBlend), dark); dark = Math.max((edgeBlend - edgeDst) * (4f / edgeBlend), dark);
} }
@ -529,7 +539,7 @@ public class World{
} }
} }
Tile tile = world.tile(x, y); Tile tile = tile(x, y);
if(tile != null && tile.isDarkened()){ if(tile != null && tile.isDarkened()){
dark = Math.max(dark, tile.data); dark = Math.max(dark, tile.data);
} }

View file

@ -3,6 +3,7 @@ package mindustry.entities.comp;
import arc.math.*; import arc.math.*;
import arc.util.*; import arc.util.*;
import mindustry.annotations.Annotations.*; import mindustry.annotations.Annotations.*;
import mindustry.game.*;
import mindustry.gen.*; import mindustry.gen.*;
import static mindustry.Vars.*; import static mindustry.Vars.*;
@ -12,30 +13,41 @@ abstract class BoundedComp implements Velc, Posc, Healthc, Flyingc{
static final float warpDst = 30f; static final float warpDst = 30f;
@Import float x, y; @Import float x, y;
@Import Team team;
@Override @Override
public void update(){ public void update(){
float bot = 0f, left = 0f, top = world.unitHeight(), right = world.unitWidth();
//TODO hidden map rules only apply to player teams? should they?
if(state.rules.borderDarkness && !team.isAI()){
bot = state.rules.limitY * tilesize;
left = state.rules.limitX * tilesize;
top = state.rules.limitHeight * tilesize + bot;
right = state.rules.limitWidth * tilesize + left;
}
if(!net.client() || isLocal()){ if(!net.client() || isLocal()){
float dx = 0f, dy = 0f; float dx = 0f, dy = 0f;
//repel unit out of bounds //repel unit out of bounds
if(x < 0) dx += (-x/warpDst); if(x < left) dx += (-(x - left)/warpDst);
if(y < 0) dy += (-y/warpDst); if(y < bot) dy += (-(y - bot)/warpDst);
if(x > world.unitWidth()) dx -= (x - world.unitWidth())/warpDst; if(x > right) dx -= (x - right)/warpDst;
if(y > world.unitHeight()) dy -= (y - world.unitHeight())/warpDst; if(y > top) dy -= (y - top)/warpDst;
velAddNet(dx * Time.delta, dy * Time.delta); velAddNet(dx * Time.delta, dy * Time.delta);
} }
//clamp position if not flying //clamp position if not flying
if(isGrounded()){ if(isGrounded()){
x = Mathf.clamp(x, 0, world.width() * tilesize - tilesize); x = Mathf.clamp(x, left, right - tilesize);
y = Mathf.clamp(y, 0, world.height() * tilesize - tilesize); y = Mathf.clamp(y, bot, top - tilesize);
} }
//kill when out of bounds //kill when out of bounds
if(x < -finalWorldBounds || y < -finalWorldBounds || x >= world.width() * tilesize + finalWorldBounds || y >= world.height() * tilesize + finalWorldBounds){ if(x < -finalWorldBounds + left || y < -finalWorldBounds + bot || x >= right + finalWorldBounds || y >= top + finalWorldBounds){
kill(); kill();
} }
} }

View file

@ -134,6 +134,10 @@ public class Rules{
public boolean coreIncinerates = false; public boolean coreIncinerates = false;
/** If false, borders fade out into darkness. Only use with custom backgrounds!*/ /** If false, borders fade out into darkness. Only use with custom backgrounds!*/
public boolean borderDarkness = true; public boolean borderDarkness = true;
/** If true, the map play area is cropped based on the rectangle below. */
public boolean limitMapArea = false;
/** Map area limit rectangle. */
public int limitX, limitY, limitWidth = 1, limitHeight = 1;
/** special tags for additional info. */ /** special tags for additional info. */
public StringMap tags = new StringMap(); public StringMap tags = new StringMap();
/** Name of callback to call for background rendering in mods; see Renderer#addCustomBackground. Runs last. */ /** Name of callback to call for background rendering in mods; see Renderer#addCustomBackground. Runs last. */

View file

@ -63,6 +63,7 @@ public class BlockRenderer{
blockTree = new BlockQuadtree(new Rect(0, 0, world.unitWidth(), world.unitHeight())); blockTree = new BlockQuadtree(new Rect(0, 0, world.unitWidth(), world.unitHeight()));
floorTree = new FloorQuadtree(new Rect(0, 0, world.unitWidth(), world.unitHeight())); floorTree = new FloorQuadtree(new Rect(0, 0, world.unitWidth(), world.unitHeight()));
shadowEvents.clear(); shadowEvents.clear();
updateFloors.clear();
lastCamY = lastCamX = -99; //invalidate camera position so blocks get updated lastCamY = lastCamX = -99; //invalidate camera position so blocks get updated
shadows.getTexture().setFilter(TextureFilter.linear, TextureFilter.linear); shadows.getTexture().setFilter(TextureFilter.linear, TextureFilter.linear);
@ -74,6 +75,12 @@ public class BlockRenderer{
Draw.color(blendShadowColor); Draw.color(blendShadowColor);
for(Tile tile : world.tiles){ for(Tile tile : world.tiles){
recordIndex(tile);
if(tile.floor().updateRender(tile)){
updateFloors.add(new UpdateRenderState(tile, tile.floor()));
}
if(tile.block().hasShadow){ if(tile.block().hasShadow){
Fill.rect(tile.x + 0.5f, tile.y + 0.5f, 1, 1); Fill.rect(tile.x + 0.5f, tile.y + 0.5f, 1, 1);
} }
@ -83,31 +90,7 @@ public class BlockRenderer{
Draw.color(); Draw.color();
shadows.end(); shadows.end();
updateFloors.clear(); updateDarkness();
dark.getTexture().setFilter(TextureFilter.linear);
dark.resize(world.width(), world.height());
dark.begin();
Core.graphics.clear(Color.white);
Draw.proj().setOrtho(0, 0, dark.getWidth(), dark.getHeight());
for(Tile tile : world.tiles){
recordIndex(tile);
if(tile.floor().updateRender(tile)){
updateFloors.add(new UpdateRenderState(tile, tile.floor()));
}
float darkness = world.getDarkness(tile.x, tile.y);
if(darkness > 0){
Draw.colorl(1f - Math.min((darkness + 0.5f) / 4f, 1f));
Fill.rect(tile.x + 0.5f, tile.y + 0.5f, 1, 1);
}
}
Draw.flush();
Draw.color();
dark.end();
}); });
Events.on(TilePreChangeEvent.class, event -> { Events.on(TilePreChangeEvent.class, event -> {
@ -132,6 +115,42 @@ public class BlockRenderer{
}); });
} }
public void updateDarkness(){
darkEvents.clear();
dark.getTexture().setFilter(TextureFilter.linear);
dark.resize(world.width(), world.height());
dark.begin();
//fill darkness with black when map area is limited
Core.graphics.clear(state.rules.limitMapArea ? Color.black : Color.white);
Draw.proj().setOrtho(0, 0, dark.getWidth(), dark.getHeight());
//clear out initial starting area
if(state.rules.limitMapArea){
Draw.color(Color.white);
Fill.crect(state.rules.limitX, state.rules.limitY, state.rules.limitWidth, state.rules.limitHeight);
}
for(Tile tile : world.tiles){
//skip lighting outside rect
if(state.rules.limitMapArea && !Rect.contains(state.rules.limitX, state.rules.limitY, state.rules.limitWidth - 1, state.rules.limitHeight - 1, tile.x, tile.y)){
continue;
}
float darkness = world.getDarkness(tile.x, tile.y);
if(darkness > 0){
float dark = 1f - Math.min((darkness + 0.5f) / 4f, 1f);
Draw.colorl(dark);
Fill.rect(tile.x + 0.5f, tile.y + 0.5f, 1, 1);
}
}
Draw.flush();
Draw.color();
dark.end();
}
public void invalidateTile(Tile tile){ public void invalidateTile(Tile tile){
int avgx = (int)(camera.position.x / tilesize); int avgx = (int)(camera.position.x / tilesize);
int avgy = (int)(camera.position.y / tilesize); int avgy = (int)(camera.position.y / tilesize);

View file

@ -132,5 +132,6 @@ public class Pal{
slagOrange = Color.valueOf("ffa166"), slagOrange = Color.valueOf("ffa166"),
techBlue = Color.valueOf("8ca9e8"), techBlue = Color.valueOf("8ca9e8"),
vent = Color.valueOf("6b4e4e"); vent = Color.valueOf("6b4e4e"),
vent2 = Color.valueOf("261d1d");
} }

View file

@ -1269,11 +1269,15 @@ public class LExecutor{
public static class SetRuleI implements LInstruction{ public static class SetRuleI implements LInstruction{
public LogicRule rule = LogicRule.waveSpacing; public LogicRule rule = LogicRule.waveSpacing;
public int value; public int value, p1, p2, p3, p4;
public SetRuleI(LogicRule rule, int value){ public SetRuleI(LogicRule rule, int value, int p1, int p2, int p3, int p4){
this.rule = rule; this.rule = rule;
this.value = value; this.value = value;
this.p1 = p1;
this.p2 = p2;
this.p3 = p3;
this.p4 = p4;
} }
public SetRuleI(){ public SetRuleI(){
@ -1291,11 +1295,62 @@ public class LExecutor{
case dropZoneRadius -> state.rules.dropZoneRadius = exec.numf(value) * 8f; case dropZoneRadius -> state.rules.dropZoneRadius = exec.numf(value) * 8f;
case unitCap -> state.rules.unitCap = exec.numi(value); case unitCap -> state.rules.unitCap = exec.numi(value);
case lighting -> state.rules.lighting = exec.bool(value); case lighting -> state.rules.lighting = exec.bool(value);
case mapArea -> {
int x = exec.numi(p1), y = exec.numi(p2), w = exec.numi(p3), h = exec.numi(p4);
if(!checkMapArea(x, y, w, h, false)){
Call.setMapArea(x, y, w, h);
}
}
case ambientLight -> state.rules.ambientLight.fromDouble(exec.num(value)); case ambientLight -> state.rules.ambientLight.fromDouble(exec.num(value));
} }
} }
} }
/** @return whether the map area is already set to this value. */
static boolean checkMapArea(int x, int y, int w, int h, boolean set){
x = Math.max(x, 0);
y = Math.max(y, 0);
w = Math.min(world.width(), w);
h = Math.min(world.height(), h);
boolean full = x == 0 && y == 0 && w == world.width() && h == world.height();
if(state.rules.limitMapArea){
if(state.rules.limitX == x && state.rules.limitY == y && state.rules.limitWidth == w && state.rules.limitHeight == h){
return true;
}else if(full){
//disable the rule, covers the whole map
if(set){
state.rules.limitMapArea = false;
if(!headless){
renderer.updateAllDarkness();
}
return false;
}
}
}else if(full){ //was already disabled, no need to change anything
return true;
}
if(set){
state.rules.limitMapArea = true;
state.rules.limitX = x;
state.rules.limitY = y;
state.rules.limitWidth = w;
state.rules.limitHeight = h;
if(!headless){
renderer.updateAllDarkness();
}
}
return false;
}
@Remote(called = Loc.server)
public static void setMapArea(int x, int y, int w, int h){
checkMapArea(x, y, w, h, true);
}
public static class FlushMessageI implements LInstruction{ public static class FlushMessageI implements LInstruction{
public MessageType type = MessageType.announce; public MessageType type = MessageType.announce;
public int duration; public int duration;

View file

@ -1237,18 +1237,38 @@ public class LStatements{
@RegisterStatement("setrule") @RegisterStatement("setrule")
public static class SetRuleStatement extends LStatement{ public static class SetRuleStatement extends LStatement{
public LogicRule rule = LogicRule.waveSpacing; public LogicRule rule = LogicRule.waveSpacing;
public String value = "100"; public String value = "100", p1 = "0", p2 = "0", p3 = "100", p4 = "100";
@Override @Override
public void build(Table table){ public void build(Table table){
rebuild(table);
}
void rebuild(Table table){
table.clearChildren();
table.button(b -> { table.button(b -> {
b.label(() -> rule.name()).growX().wrap().labelAlign(Align.center); b.label(() -> rule.name()).growX().wrap().labelAlign(Align.center);
b.clicked(() -> showSelect(b, LogicRule.all, rule, o -> rule = o, 2, c -> c.width(150f))); b.clicked(() -> showSelect(b, LogicRule.all, rule, o -> {
rule = o;
rebuild(table);
}, 2, c -> c.width(150f)));
}, Styles.logict, () -> {}).size(160f, 40f).pad(4f).color(table.color); }, Styles.logict, () -> {}).size(160f, 40f).pad(4f).color(table.color);
table.add(" = "); table.add(" = ");
field(table, value, s -> value = s); switch(rule){
case mapArea -> {
fields(table, "x", p1, s -> p1 = s);
fields(table, "y", p2, s -> p2 = s);
row(table);
fields(table, "w", p3, s -> p3 = s);
fields(table, "h", p4, s -> p4 = s);
}
default -> {
field(table, value, s -> value = s);
}
}
} }
@Override @Override
@ -1263,7 +1283,7 @@ public class LStatements{
@Override @Override
public LInstruction build(LAssembler builder){ public LInstruction build(LAssembler builder){
return new SetRuleI(rule, builder.var(value)); return new SetRuleI(rule, builder.var(value), builder.var(p1), builder.var(p2), builder.var(p3), builder.var(p4));
} }
} }

View file

@ -9,6 +9,7 @@ public enum LogicRule{
enemyCoreBuildRadius, enemyCoreBuildRadius,
dropZoneRadius, dropZoneRadius,
unitCap, unitCap,
mapArea,
lighting, lighting,
ambientLight; ambientLight;

View file

@ -173,7 +173,7 @@ public class CustomRulesDialog extends BaseDialog{
title("@rules.title.unit"); title("@rules.title.unit");
check("@rules.unitammo", b -> rules.unitAmmo = b, () -> rules.unitAmmo); check("@rules.unitammo", b -> rules.unitAmmo = b, () -> rules.unitAmmo);
check("@rules.unitcapvariable", b -> rules.unitCapVariable = b, () -> rules.unitCapVariable); check("@rules.unitcapvariable", b -> rules.unitCapVariable = b, () -> rules.unitCapVariable);
number("@rules.unitcap", true, f -> rules.unitCap = f, () -> rules.unitCap, -999, 999); numberi("@rules.unitcap", f -> rules.unitCap = f, () -> rules.unitCap, -999, 999);
number("@rules.unitdamagemultiplier", f -> rules.unitDamageMultiplier = f, () -> rules.unitDamageMultiplier); number("@rules.unitdamagemultiplier", f -> rules.unitDamageMultiplier = f, () -> rules.unitDamageMultiplier);
number("@rules.unitbuildspeedmultiplier", f -> rules.unitBuildSpeedMultiplier = f, () -> rules.unitBuildSpeedMultiplier, 0.001f, 50f); number("@rules.unitbuildspeedmultiplier", f -> rules.unitBuildSpeedMultiplier = f, () -> rules.unitBuildSpeedMultiplier, 0.001f, 50f);
@ -194,6 +194,14 @@ public class CustomRulesDialog extends BaseDialog{
check("@rules.lighting", b -> rules.lighting = b, () -> rules.lighting); check("@rules.lighting", b -> rules.lighting = b, () -> rules.lighting);
check("@rules.enemyLights", b -> rules.enemyLights = b, () -> rules.enemyLights); check("@rules.enemyLights", b -> rules.enemyLights = b, () -> rules.enemyLights);
if(experimental){
check("@rules.limitarea", b -> rules.limitMapArea = b, () -> rules.limitMapArea);
numberi("x", x -> state.rules.limitX = x, () -> state.rules.limitX, () -> state.rules.limitMapArea, 0, 10000);
numberi("y", y -> state.rules.limitY = y, () -> state.rules.limitY, () -> state.rules.limitMapArea, 0, 10000);
numberi("w", w -> state.rules.limitWidth = w, () -> state.rules.limitWidth, () -> state.rules.limitMapArea, 0, 10000);
numberi("h", h -> state.rules.limitHeight = h, () -> state.rules.limitHeight, () -> state.rules.limitMapArea, 0, 10000);
}
main.button(b -> { main.button(b -> {
b.left(); b.left();
b.table(Tex.pane, in -> { b.table(Tex.pane, in -> {
@ -270,14 +278,19 @@ public class CustomRulesDialog extends BaseDialog{
number(text, false, cons, prov, condition, 0, Float.MAX_VALUE); number(text, false, cons, prov, condition, 0, Float.MAX_VALUE);
} }
//TODO integer param unused void numberi(String text, Intc cons, Intp prov, int min, int max){
void number(String text, boolean integer, Intc cons, Intp prov, int min, int max){ numberi(text, cons, prov, () -> true, min, max);
}
void numberi(String text, Intc cons, Intp prov, Boolp condition, int min, int max){
main.table(t -> { main.table(t -> {
t.left(); t.left();
t.add(text).left().padRight(5); t.add(text).left().padRight(5)
.update(a -> a.setColor(condition.get() ? Color.white : Color.gray));
t.field((prov.get()) + "", s -> cons.get(Strings.parseInt(s))) t.field((prov.get()) + "", s -> cons.get(Strings.parseInt(s)))
.padRight(100f) .update(a -> a.setDisabled(!condition.get()))
.valid(f -> Strings.parseInt(f) >= min && Strings.parseInt(f) <= max).width(120f).left(); .padRight(100f)
.valid(f -> Strings.parseInt(f) >= min && Strings.parseInt(f) <= max).width(120f).left();
}).padTop(0).row(); }).padTop(0).row();
} }

View file

@ -10,7 +10,6 @@ import mindustry.world.meta.*;
import static mindustry.Vars.*; import static mindustry.Vars.*;
/** A turret that fires a continuous beam with a delay between shots. Liquid coolant is required. Yes, this class name is awful. NEEDS RENAME */ /** A turret that fires a continuous beam with a delay between shots. Liquid coolant is required. Yes, this class name is awful. NEEDS RENAME */
@Deprecated
public class LaserTurret extends PowerTurret{ public class LaserTurret extends PowerTurret{
public float firingMoveFract = 0.25f; public float firingMoveFract = 0.25f;
public float shootDuration = 100f; public float shootDuration = 100f;

View file

@ -24,4 +24,4 @@ android.useAndroidX=true
#used for slow jitpack builds; TODO see if this actually works #used for slow jitpack builds; TODO see if this actually works
org.gradle.internal.http.socketTimeout=100000 org.gradle.internal.http.socketTimeout=100000
org.gradle.internal.http.connectionTimeout=100000 org.gradle.internal.http.connectionTimeout=100000
archash=4775df2392 archash=ea5e9c0c40