Merge branch 'Anuken:master' into do-you-hear-the-voices-too
This commit is contained in:
@@ -69,6 +69,7 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform
|
||||
return (Float.isNaN(result) || Float.isInfinite(result)) ? 1f : Mathf.clamp(result, 0.0001f, 60f / 10f);
|
||||
});
|
||||
|
||||
UI.loadColors();
|
||||
batch = new SortedSpriteBatch();
|
||||
assets = new AssetManager();
|
||||
assets.setLoader(Texture.class, "." + mapExtension, new MapPreviewLoader());
|
||||
|
||||
@@ -19,7 +19,23 @@ public class UnitGroup{
|
||||
public int collisionLayer;
|
||||
public volatile float[] positions, originalPositions;
|
||||
public volatile boolean valid;
|
||||
public long lastSpeedUpdate = -1;
|
||||
public float minSpeed = 999999f;
|
||||
|
||||
public void updateMinSpeed(){
|
||||
if(lastSpeedUpdate == Vars.state.updateId) return;
|
||||
|
||||
lastSpeedUpdate = Vars.state.updateId;
|
||||
minSpeed = 999999f;
|
||||
|
||||
for(Unit unit : units){
|
||||
//don't factor in the floor speed multiplier
|
||||
Floor on = unit.isFlying() ? Blocks.air.asFloor() : unit.floorOn();
|
||||
minSpeed = Math.min(unit.speed() / on.speedMultiplier, minSpeed);
|
||||
}
|
||||
|
||||
if(Float.isInfinite(minSpeed) || Float.isNaN(minSpeed)) minSpeed = 999999f;
|
||||
}
|
||||
|
||||
public void calculateFormation(Vec2 dest, int collisionLayer){
|
||||
this.collisionLayer = collisionLayer;
|
||||
@@ -33,19 +49,16 @@ public class UnitGroup{
|
||||
cy /= units.size;
|
||||
positions = new float[units.size * 2];
|
||||
|
||||
|
||||
//all positions are relative to the center
|
||||
for(int i = 0; i < units.size; i ++){
|
||||
Unit unit = units.get(i);
|
||||
positions[i * 2] = unit.x - cx;
|
||||
positions[i * 2 + 1] = unit.y - cy;
|
||||
unit.command().groupIndex = i;
|
||||
|
||||
//don't factor in the floor speed multiplier
|
||||
Floor on = unit.isFlying() ? Blocks.air.asFloor() : unit.floorOn();
|
||||
minSpeed = Math.min(unit.speed() / on.speedMultiplier, minSpeed);
|
||||
}
|
||||
|
||||
if(Float.isInfinite(minSpeed) || Float.isNaN(minSpeed)) minSpeed = 999999f;
|
||||
updateMinSpeed();
|
||||
|
||||
//run on new thread to prevent stutter
|
||||
Vars.mainExecutor.submit(() -> {
|
||||
|
||||
@@ -148,6 +148,10 @@ public class CommandAI extends AIController{
|
||||
}
|
||||
}
|
||||
|
||||
if(group != null){
|
||||
group.updateMinSpeed();
|
||||
}
|
||||
|
||||
if(!net.client() && command == UnitCommand.enterPayloadCommand && unit.buildOn() != null && (targetPos == null || (world.buildWorld(targetPos.x, targetPos.y) != null && world.buildWorld(targetPos.x, targetPos.y) == unit.buildOn()))){
|
||||
var build = unit.buildOn();
|
||||
tmpPayload.unit = unit;
|
||||
|
||||
@@ -2,6 +2,8 @@ package mindustry.ai.types;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
@@ -29,6 +31,11 @@ public class MissileAI extends AIController{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Teamc target(float x, float y, float range, boolean air, boolean ground){
|
||||
return Units.closestTarget(unit.team, x, y, range, u -> u.checkTarget(air, ground), t -> ground && (!t.block.underBullets || (shooter != null && t == Vars.world.buildWorld(shooter.aimX, shooter.aimY))));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean retarget(){
|
||||
//more frequent retarget due to high speed. TODO won't this lag?
|
||||
|
||||
@@ -2433,6 +2433,7 @@ public class Blocks{
|
||||
itemDuration = 140f;
|
||||
ambientSound = Sounds.pulse;
|
||||
ambientSoundVolume = 0.07f;
|
||||
liquidCapacity = 60f;
|
||||
|
||||
consumePower(25f);
|
||||
consumeItem(Items.blastCompound);
|
||||
@@ -4566,6 +4567,7 @@ public class Blocks{
|
||||
loopSoundVolume = 0.6f;
|
||||
deathSound = Sounds.largeExplosion;
|
||||
targetAir = false;
|
||||
targetUnderBlocks = false;
|
||||
|
||||
fogRadius = 6f;
|
||||
|
||||
@@ -5500,23 +5502,6 @@ public class Blocks{
|
||||
);
|
||||
}};
|
||||
|
||||
mechRefabricator = new Reconstructor("mech-refabricator"){{
|
||||
requirements(Category.units, with(Items.beryllium, 250, Items.tungsten, 120, Items.silicon, 150));
|
||||
regionSuffix = "-dark";
|
||||
|
||||
size = 3;
|
||||
consumePower(2.5f);
|
||||
consumeLiquid(Liquids.hydrogen, 3f / 60f);
|
||||
consumeItems(with(Items.silicon, 50, Items.tungsten, 40));
|
||||
|
||||
constructTime = 60f * 45f;
|
||||
researchCostMultiplier = 0.75f;
|
||||
|
||||
upgrades.addAll(
|
||||
new UnitType[]{UnitTypes.merui, UnitTypes.cleroi}
|
||||
);
|
||||
}};
|
||||
|
||||
shipRefabricator = new Reconstructor("ship-refabricator"){{
|
||||
requirements(Category.units, with(Items.beryllium, 200, Items.tungsten, 100, Items.silicon, 150, Items.oxide, 40));
|
||||
regionSuffix = "-dark";
|
||||
@@ -5535,6 +5520,23 @@ public class Blocks{
|
||||
researchCost = with(Items.beryllium, 500, Items.tungsten, 200, Items.silicon, 300, Items.oxide, 80);
|
||||
}};
|
||||
|
||||
mechRefabricator = new Reconstructor("mech-refabricator"){{
|
||||
requirements(Category.units, with(Items.beryllium, 250, Items.tungsten, 120, Items.silicon, 150));
|
||||
regionSuffix = "-dark";
|
||||
|
||||
size = 3;
|
||||
consumePower(2.5f);
|
||||
consumeLiquid(Liquids.hydrogen, 3f / 60f);
|
||||
consumeItems(with(Items.silicon, 50, Items.tungsten, 40));
|
||||
|
||||
constructTime = 60f * 45f;
|
||||
researchCostMultiplier = 0.75f;
|
||||
|
||||
upgrades.addAll(
|
||||
new UnitType[]{UnitTypes.merui, UnitTypes.cleroi}
|
||||
);
|
||||
}};
|
||||
|
||||
//yes very silly name
|
||||
primeRefabricator = new Reconstructor("prime-refabricator"){{
|
||||
requirements(Category.units, with(Items.thorium, 250, Items.oxide, 200, Items.tungsten, 200, Items.silicon, 400));
|
||||
|
||||
@@ -4063,6 +4063,8 @@ public class UnitTypes{
|
||||
isEnemy = false;
|
||||
envDisabled = 0;
|
||||
|
||||
range = 60f;
|
||||
faceTarget = true;
|
||||
targetPriority = -2;
|
||||
lowAltitude = false;
|
||||
mineWalls = true;
|
||||
@@ -4127,8 +4129,10 @@ public class UnitTypes{
|
||||
isEnemy = false;
|
||||
envDisabled = 0;
|
||||
|
||||
range = 60f;
|
||||
targetPriority = -2;
|
||||
lowAltitude = false;
|
||||
faceTarget = true;
|
||||
mineWalls = true;
|
||||
mineFloor = false;
|
||||
mineHardnessScaling = false;
|
||||
@@ -4204,6 +4208,8 @@ public class UnitTypes{
|
||||
isEnemy = false;
|
||||
envDisabled = 0;
|
||||
|
||||
range = 65f;
|
||||
faceTarget = true;
|
||||
targetPriority = -2;
|
||||
lowAltitude = false;
|
||||
mineWalls = true;
|
||||
|
||||
@@ -86,11 +86,12 @@ public class GameState{
|
||||
}
|
||||
|
||||
public boolean isPaused(){
|
||||
return is(State.paused);
|
||||
return state == State.paused;
|
||||
}
|
||||
|
||||
/** @return whether there is an unpaused game in progress. */
|
||||
public boolean isPlaying(){
|
||||
return (state == State.playing) || (state == State.paused && !isPaused());
|
||||
return state == State.playing;
|
||||
}
|
||||
|
||||
/** @return whether the current state is *not* the menu. */
|
||||
|
||||
@@ -357,7 +357,8 @@ public class Logic implements ApplicationListener{
|
||||
|
||||
//map is over, no more world processor objective stuff
|
||||
state.rules.disableWorldProcessors = true;
|
||||
state.rules.objectives.clear();
|
||||
|
||||
Call.clearObjectives();
|
||||
|
||||
//save, just in case
|
||||
if(!headless && !net.client()){
|
||||
@@ -460,9 +461,6 @@ public class Logic implements ApplicationListener{
|
||||
|
||||
if(!state.isEditor()){
|
||||
state.rules.objectives.update();
|
||||
if(state.rules.objectives.checkChanged() && net.server()){
|
||||
Call.setObjectives(state.rules.objectives);
|
||||
}
|
||||
}
|
||||
|
||||
if(state.rules.waves && state.rules.waveTimer && !state.gameOver){
|
||||
|
||||
@@ -340,15 +340,23 @@ public class NetClient implements ApplicationListener{
|
||||
state.rules = rules;
|
||||
}
|
||||
|
||||
//NOTE: avoid using this, runs into packet/buffer size limitations
|
||||
@Remote(variants = Variant.both)
|
||||
public static void setObjectives(MapObjectives executor){
|
||||
state.rules.objectives = executor;
|
||||
}
|
||||
|
||||
@Remote(called = Loc.server)
|
||||
public static void objectiveCompleted(String[] flagsRemoved, String[] flagsAdded){
|
||||
state.rules.objectiveFlags.removeAll(flagsRemoved);
|
||||
state.rules.objectiveFlags.addAll(flagsAdded);
|
||||
@Remote(variants = Variant.both, called = Loc.server)
|
||||
public static void clearObjectives(){
|
||||
state.rules.objectives.clear();
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both, called = Loc.server)
|
||||
public static void completeObjective(int index){
|
||||
var obj = state.rules.objectives.get(index);
|
||||
if(obj != null){
|
||||
obj.done();
|
||||
}
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
|
||||
@@ -372,18 +372,20 @@ public class Renderer implements ApplicationListener{
|
||||
});
|
||||
}
|
||||
|
||||
float scaleFactor = 4f / renderer.getDisplayScale();
|
||||
|
||||
//draw objective markers
|
||||
state.rules.objectives.eachRunning(obj -> {
|
||||
for(var marker : obj.markers){
|
||||
if(!marker.minimap){
|
||||
marker.drawWorld();
|
||||
if(marker.world){
|
||||
marker.draw(marker.autoscale ? scaleFactor : 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
for(var marker : state.markers){
|
||||
if(!marker.isHidden() && !marker.minimap){
|
||||
marker.drawWorld();
|
||||
if(marker.world){
|
||||
marker.draw(marker.autoscale ? scaleFactor : 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -101,8 +101,6 @@ public class UI implements ApplicationListener, Loadable{
|
||||
|
||||
@Override
|
||||
public void loadSync(){
|
||||
loadColors();
|
||||
|
||||
Fonts.outline.getData().markupEnabled = true;
|
||||
Fonts.def.getData().markupEnabled = true;
|
||||
Fonts.def.setOwnsTexture(false);
|
||||
@@ -281,7 +279,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
|
||||
public void showTextInput(String titleText, String text, int textLength, String def, boolean numbers, boolean allowEmpty, Cons<String> confirmed, Runnable closed){
|
||||
if(mobile){
|
||||
var description = text;
|
||||
var description = (text.startsWith("@") ? Core.bundle.get(text.substring(1)) : text);
|
||||
var empty = allowEmpty;
|
||||
Core.input.getTextInput(new TextInput(){{
|
||||
this.title = (titleText.startsWith("@") ? Core.bundle.get(titleText.substring(1)) : titleText);
|
||||
|
||||
@@ -115,6 +115,7 @@ public abstract class UnlockableContent extends MappableContent{
|
||||
var result = Pixmaps.outline(base, outlineColor, outlineRadius);
|
||||
Drawf.checkBleed(result);
|
||||
packer.add(page, regName, result);
|
||||
result.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -126,6 +127,7 @@ public abstract class UnlockableContent extends MappableContent{
|
||||
var result = Pixmaps.outline(base, outlineColor, outlineRadius);
|
||||
Drawf.checkBleed(result);
|
||||
packer.add(PageType.main, name, result);
|
||||
result.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -301,6 +301,14 @@ public class MapEditor{
|
||||
if(previous.in(px, py)){
|
||||
tiles.set(x, y, previous.getn(px, py));
|
||||
Tile tile = tiles.getn(x, y);
|
||||
|
||||
Object config = null;
|
||||
|
||||
//fetch the old config first, configs can be relative to block position (tileX/tileY) before those are reassigned
|
||||
if(tile.build != null && tile.isCenter()){
|
||||
config = tile.build.config();
|
||||
}
|
||||
|
||||
tile.x = (short)x;
|
||||
tile.y = (short)y;
|
||||
|
||||
@@ -309,9 +317,12 @@ public class MapEditor{
|
||||
tile.build.y = y * tilesize + tile.block().offset;
|
||||
|
||||
//shift links to account for map resize
|
||||
Object config = tile.build.config();
|
||||
if(config != null){
|
||||
Object out = BuildPlan.pointConfig(tile.block(), config, p -> p.sub(offsetX, offsetY));
|
||||
Object out = BuildPlan.pointConfig(tile.block(), config, p -> {
|
||||
if(!tile.build.block.ignoreResizeConfig){
|
||||
p.sub(offsetX, offsetY);
|
||||
}
|
||||
});
|
||||
if(out != config){
|
||||
tile.build.configureAny(out);
|
||||
}
|
||||
|
||||
@@ -91,6 +91,8 @@ public class MapLocalesDialog extends BaseDialog{
|
||||
t.button("@edit", Icon.edit, this::editDialog).size(210f, 64f);
|
||||
}).growX();
|
||||
|
||||
resized(this::buildMain);
|
||||
|
||||
buttons.button("?", () -> ui.showInfo("@locales.info")).size(60f, 64f).uniform();
|
||||
|
||||
shown(this::setup);
|
||||
@@ -158,7 +160,7 @@ public class MapLocalesDialog extends BaseDialog{
|
||||
|
||||
saved = false;
|
||||
buildMain();
|
||||
}).padTop(10f).size(400f, 50f).fillX().row();
|
||||
}).padTop(10f).size(cardWidth, 50f).fillX().row();
|
||||
}).right();
|
||||
}
|
||||
|
||||
@@ -184,7 +186,7 @@ public class MapLocalesDialog extends BaseDialog{
|
||||
|
||||
selectedLocale = name;
|
||||
buildTables();
|
||||
}).update(b -> b.setChecked(selectedLocale.equals(name))).size(300f, 50f);
|
||||
}).update(b -> b.setChecked(selectedLocale.equals(name))).width(200f).minHeight(50f);
|
||||
p.button(Icon.edit, Styles.flati, () -> localeEditDialog(name)).size(50f);
|
||||
p.button(Icon.trash, Styles.flati, () -> ui.showConfirm("@confirm", "@locales.deletelocale", () -> {
|
||||
locales.remove(name);
|
||||
@@ -196,7 +198,7 @@ public class MapLocalesDialog extends BaseDialog{
|
||||
}
|
||||
}
|
||||
}).row();
|
||||
langs.button("@add", Icon.add, this::addLocaleDialog).padTop(10f).width(400f);
|
||||
langs.button("@add", Icon.add, this::addLocaleDialog).padTop(10f).width(250f);
|
||||
}
|
||||
|
||||
private void buildMain(){
|
||||
@@ -206,7 +208,7 @@ public class MapLocalesDialog extends BaseDialog{
|
||||
|
||||
main.image().color(Pal.gray).height(3f).growX().expandY().top().row();
|
||||
main.pane(p -> {
|
||||
int cols = Math.max(1, (Core.graphics.getWidth() - 630) / ((int)cardWidth + 10));
|
||||
int cols = Math.max(1, (int)((Core.graphics.getWidth() / Scl.scl() - 410f) / cardWidth) - 1);
|
||||
if(props.size == 0){
|
||||
main.add("@empty").center().row();
|
||||
return;
|
||||
@@ -512,7 +514,7 @@ public class MapLocalesDialog extends BaseDialog{
|
||||
|
||||
propView.image().color(Pal.gray).height(3f).fillX().top().row();
|
||||
propView.pane(p -> {
|
||||
int cols = (Core.graphics.getWidth() - 100) / ((int)cardWidth + 10);
|
||||
int cols = Math.max(1, (int)((Core.graphics.getWidth() / Scl.scl() - 100f) / cardWidth));
|
||||
if(cols == 0){
|
||||
propView.add("@empty").center().row();
|
||||
return;
|
||||
@@ -720,7 +722,9 @@ public class MapLocalesDialog extends BaseDialog{
|
||||
if(!locales.containsKey(key)) return "";
|
||||
|
||||
for(var prop : locales.get(key).entries()){
|
||||
data.append(prop.key).append(" = ").append(prop.value).append("\n");
|
||||
// Convert \n in plain text to \\n, then convert newlines to \n
|
||||
data.append(prop.key).append(" = ").append(prop.value
|
||||
.replace("\\n", "\\\\n").replace("\n", "\\n")).append("\n");
|
||||
}
|
||||
|
||||
return data.toString();
|
||||
@@ -738,7 +742,9 @@ public class MapLocalesDialog extends BaseDialog{
|
||||
}else{
|
||||
int sepIndex = line.indexOf(" = ");
|
||||
if(sepIndex != -1 && !currentLocale.isEmpty()){
|
||||
bundles.get(currentLocale).put(line.substring(0, sepIndex), line.substring(sepIndex + 3));
|
||||
// Convert \n in file to newlines in text, then revert newlines with escape characters
|
||||
bundles.get(currentLocale).put(line.substring(0, sepIndex), line.substring(sepIndex + 3)
|
||||
.replace("\\n", "\n").replace("\\\n", "\\n"));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -752,7 +758,9 @@ public class MapLocalesDialog extends BaseDialog{
|
||||
for(var line : data.split("\\r?\\n|\\r")){
|
||||
int sepIndex = line.indexOf(" = ");
|
||||
if(sepIndex != -1){
|
||||
map.put(line.substring(0, sepIndex), line.substring(sepIndex + 3));
|
||||
// Convert \n in file to newlines in text, then revert newlines with escape characters
|
||||
map.put(line.substring(0, sepIndex), line.substring(sepIndex + 3)
|
||||
.replace("\\n", "\n").replace("\\\n", "\\n"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -246,6 +246,38 @@ public class MapObjectivesDialog extends BaseDialog{
|
||||
show();
|
||||
}});
|
||||
|
||||
setInterpreter(Vertices.class, float[].class, (cont, name, type, field, remover, indexer, get, set) -> cont.table(main -> {
|
||||
float[] data = get.get();
|
||||
|
||||
name(cont, name, remover, indexer);
|
||||
cont.table(t -> {
|
||||
t.left().defaults().left();
|
||||
|
||||
String[] names = {"x", "y", "color", "u", "v"};
|
||||
int stride = 6;
|
||||
int vertices = data.length / stride;
|
||||
|
||||
for(int i = 0; i < vertices; i++){
|
||||
int offset = i * stride;
|
||||
|
||||
t.table(row -> {
|
||||
for(int j = 0; j < names.length; j++){
|
||||
int index = offset + j;
|
||||
|
||||
if("color".equals(names[j])) {
|
||||
getInterpreter(Color.class).build(row, names[j], new TypeInfo(Color.class), null, null, null, () -> new Color().abgr8888(data[index]), value -> data[index] = value.toFloatBits());
|
||||
}else{
|
||||
float scale = j <= 1 ? tilesize : 1;
|
||||
getInterpreter(float.class).build(row, names[j], new TypeInfo(float.class), null, null, null, () -> data[index] / scale, value -> data[index] = value * scale);
|
||||
}
|
||||
|
||||
row.add().pad(4);
|
||||
}
|
||||
}).row();
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
// Types that use the default interpreter. It would be nice if all types could use it, but I don't know how to reliably prevent classes like [? extends Content] from using it.
|
||||
for(var obj : MapObjectives.allObjectiveTypes) setInterpreter(obj.get().getClass(), defaultInterpreter());
|
||||
for(var mark : MapObjectives.allMarkerTypes) setInterpreter(mark.get().getClass(), defaultInterpreter());
|
||||
@@ -290,10 +322,12 @@ public class MapObjectivesDialog extends BaseDialog{
|
||||
t.button(Icon.downOpen, Styles.emptyi, () -> indexer.get(false)).fill().padRight(4f);
|
||||
}
|
||||
|
||||
t.button(Icon.add, Styles.emptyi, () -> getProvider(type.element.raw).get(type.element, res -> {
|
||||
arr.add(res);
|
||||
rebuild[0].run();
|
||||
})).fill();
|
||||
if(!field.isAnnotationPresent(Immutable.class)) {
|
||||
t.button(Icon.add, Styles.emptyi, () -> getProvider(type.element.raw).get(type.element, res -> {
|
||||
arr.add(res);
|
||||
rebuild[0].run();
|
||||
})).fill();
|
||||
}
|
||||
}).growX().height(46f).pad(0f, -10f, 0f, -10f).get();
|
||||
|
||||
main.row().table(Tex.button, t -> rebuild[0] = () -> {
|
||||
@@ -312,10 +346,10 @@ public class MapObjectivesDialog extends BaseDialog{
|
||||
|
||||
getInterpreter((Class<Object>)arr.get(index).getClass()).build(
|
||||
t, "", new TypeInfo(arr.get(index).getClass()),
|
||||
field, () -> {
|
||||
field, field == null || !field.isAnnotationPresent(Immutable.class) ? () -> {
|
||||
arr.remove(index);
|
||||
rebuild[0].run();
|
||||
}, field == null || !field.isAnnotationPresent(Unordered.class) ? in -> {
|
||||
} : null, field == null || !field.isAnnotationPresent(Unordered.class) ? in -> {
|
||||
if(in && index > 0){
|
||||
arr.swap(index, index - 1);
|
||||
rebuild[0].run();
|
||||
|
||||
@@ -192,8 +192,18 @@ public class Units{
|
||||
|
||||
/** Returns the nearest enemy tile in a range. */
|
||||
public static Building findEnemyTile(Team team, float x, float y, float range, Boolf<Building> pred){
|
||||
return findEnemyTile(team, x, y, range, false, pred);
|
||||
}
|
||||
|
||||
/** Returns the nearest enemy tile in a range. */
|
||||
public static Building findEnemyTile(Team team, float x, float y, float range, boolean checkUnder, Boolf<Building> pred){
|
||||
if(team == Team.derelict) return null;
|
||||
|
||||
if(checkUnder){
|
||||
Building target = indexer.findEnemyTile(team, x, y, range, build -> !build.block.underBullets && pred.get(build));
|
||||
if(target != null) return target;
|
||||
}
|
||||
|
||||
return indexer.findEnemyTile(team, x, y, range, pred);
|
||||
}
|
||||
|
||||
@@ -243,7 +253,7 @@ public class Units{
|
||||
if(unit != null){
|
||||
return unit;
|
||||
}else{
|
||||
return findEnemyTile(team, x, y, range, tilePred);
|
||||
return findEnemyTile(team, x, y, range, true, tilePred);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,7 +265,7 @@ public class Units{
|
||||
if(unit != null){
|
||||
return unit;
|
||||
}else{
|
||||
return findEnemyTile(team, x, y, range, tilePred);
|
||||
return findEnemyTile(team, x, y, range, true, tilePred);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,10 +12,20 @@ import mindustry.world.meta.*;
|
||||
|
||||
public class ArmorPlateAbility extends Ability{
|
||||
public TextureRegion plateRegion;
|
||||
public Color color = Color.valueOf("d1efff");
|
||||
public TextureRegion shineRegion;
|
||||
public String plateSuffix = "-armor";
|
||||
public String shineSuffix = "-shine";
|
||||
/** Color of the shine. If null, uses team color. */
|
||||
public @Nullable Color color = null;
|
||||
public float shineSpeed = 1f;
|
||||
public float z = -1;
|
||||
|
||||
/** Whether to draw the plate region. */
|
||||
public boolean drawPlate = true;
|
||||
/** Whether to draw the shine over the plate region. */
|
||||
public boolean drawShine = true;
|
||||
|
||||
public float healthMultiplier = 0.2f;
|
||||
public float z = Layer.effect;
|
||||
|
||||
protected float warmup;
|
||||
|
||||
@@ -34,24 +44,39 @@ public class ArmorPlateAbility extends Ability{
|
||||
|
||||
@Override
|
||||
public void draw(Unit unit){
|
||||
if(!drawPlate && !drawShine) return;
|
||||
|
||||
if(warmup > 0.001f){
|
||||
if(plateRegion == null){
|
||||
plateRegion = Core.atlas.find(unit.type.name + "-armor", unit.type.region);
|
||||
plateRegion = Core.atlas.find(unit.type.name + plateSuffix, unit.type.region);
|
||||
shineRegion = Core.atlas.find(unit.type.name + shineSuffix, plateRegion);
|
||||
}
|
||||
|
||||
Draw.draw(z <= 0 ? Draw.z() : z, () -> {
|
||||
Shaders.armor.region = plateRegion;
|
||||
Shaders.armor.progress = warmup;
|
||||
Shaders.armor.time = -Time.time / 20f;
|
||||
float pz = Draw.z();
|
||||
if(z > 0) Draw.z(z);
|
||||
|
||||
Draw.rect(Shaders.armor.region, unit.x, unit.y, unit.rotation - 90f);
|
||||
Draw.color(color);
|
||||
Draw.shader(Shaders.armor);
|
||||
Draw.rect(Shaders.armor.region, unit.x, unit.y, unit.rotation - 90f);
|
||||
Draw.shader();
|
||||
if(drawPlate){
|
||||
Draw.alpha(warmup);
|
||||
Draw.rect(plateRegion, unit.x, unit.y, unit.rotation - 90f);
|
||||
Draw.alpha(1f);
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
});
|
||||
if(drawShine){
|
||||
Draw.draw(Draw.z(), () -> {
|
||||
Shaders.armor.region = shineRegion;
|
||||
Shaders.armor.progress = warmup;
|
||||
Shaders.armor.time = -Time.time / 20f * shineSpeed;
|
||||
|
||||
Draw.color(color == null ? unit.team.color : color);
|
||||
Draw.shader(Shaders.armor);
|
||||
Draw.rect(shineRegion, unit.x, unit.y, unit.rotation - 90f);
|
||||
Draw.shader();
|
||||
|
||||
Draw.reset();
|
||||
});
|
||||
}
|
||||
|
||||
Draw.z(pz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,6 +174,8 @@ public class BulletType extends Content implements Cloneable{
|
||||
public float fragVelocityMin = 0.2f, fragVelocityMax = 1f;
|
||||
/** Random range of frag lifetime as a multiplier. */
|
||||
public float fragLifeMin = 1f, fragLifeMax = 1f;
|
||||
/** Random offset of frag bullets from the parent bullet. */
|
||||
public float fragOffsetMin = 1f, fragOffsetMax = 7f;
|
||||
|
||||
/** Bullet that is created at a fixed interval. */
|
||||
public @Nullable BulletType intervalBullet;
|
||||
@@ -387,14 +389,14 @@ public class BulletType extends Content implements Cloneable{
|
||||
|
||||
if(entity instanceof Healthc h){
|
||||
float damage = b.damage;
|
||||
float shield = entity instanceof Shieldc s ? Math.max(s.shield(), 0f) : 0f;
|
||||
if(maxDamageFraction > 0){
|
||||
float cap = h.maxHealth() * maxDamageFraction;
|
||||
if(entity instanceof Shieldc s){
|
||||
cap += Math.max(s.shield(), 0f);
|
||||
}
|
||||
float cap = h.maxHealth() * maxDamageFraction + shield;
|
||||
damage = Math.min(damage, cap);
|
||||
//cap health to effective health for handlePierce to handle it properly
|
||||
health = Math.min(health, cap);
|
||||
}else{
|
||||
health += shield;
|
||||
}
|
||||
if(pierceArmor){
|
||||
h.damagePierce(damage);
|
||||
@@ -509,7 +511,7 @@ public class BulletType extends Content implements Cloneable{
|
||||
public void createFrags(Bullet b, float x, float y){
|
||||
if(fragBullet != null && (fragOnAbsorb || !b.absorbed)){
|
||||
for(int i = 0; i < fragBullets; i++){
|
||||
float len = Mathf.random(1f, 7f);
|
||||
float len = Mathf.random(fragOffsetMin, fragOffsetMax);
|
||||
float a = b.rotation() + Mathf.range(fragRandomSpread / 2) + fragAngle + ((i - fragBullets/2) * fragSpread);
|
||||
fragBullet.create(b, x + Angles.trnsx(a, len), y + Angles.trnsy(a, len), a, Mathf.random(fragVelocityMin, fragVelocityMax), Mathf.random(fragLifeMin, fragLifeMax));
|
||||
}
|
||||
|
||||
@@ -86,9 +86,9 @@ abstract class BuilderComp implements Posc, Statusc, Teamc, Rotc{
|
||||
buildAlpha = Mathf.lerpDelta(buildAlpha, activelyBuilding() ? 1f : 0f, 0.15f);
|
||||
}
|
||||
|
||||
//validate regardless of whether building is enabled.
|
||||
validatePlans();
|
||||
|
||||
if(!updateBuilding || !canBuild()){
|
||||
validatePlans();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -99,19 +99,18 @@ abstract class BuilderComp implements Posc, Statusc, Teamc, Rotc{
|
||||
if(Float.isNaN(buildCounter) || Float.isInfinite(buildCounter)) buildCounter = 0f;
|
||||
buildCounter = Math.min(buildCounter, 10f);
|
||||
|
||||
boolean instant = state.rules.instantBuild && state.rules.infiniteResources;
|
||||
|
||||
//random attempt to fix a freeze that only occurs on Android
|
||||
int maxPerFrame = 10, count = 0;
|
||||
int maxPerFrame = instant ? plans.size : 10, count = 0;
|
||||
|
||||
while(buildCounter >= 1 && count++ < maxPerFrame){
|
||||
var core = core();
|
||||
|
||||
if((core == null && !infinite)) return;
|
||||
|
||||
while((buildCounter >= 1 || instant) && count++ < maxPerFrame && plans.size > 0){
|
||||
buildCounter -= 1f;
|
||||
|
||||
validatePlans();
|
||||
|
||||
var core = core();
|
||||
|
||||
//nothing to build, or core doesn't exist
|
||||
if(buildPlan() == null || (core == null && !infinite)) return;
|
||||
|
||||
//find the next build plan
|
||||
if(plans.size > 1){
|
||||
int total = 0;
|
||||
|
||||
@@ -127,10 +127,10 @@ abstract class StatusComp implements Posc, Flyingc{
|
||||
return entry;
|
||||
}
|
||||
|
||||
/** Uses a dynamic status effect to override speed. */
|
||||
/** Uses a dynamic status effect to override speed (in tiles/second). */
|
||||
public void statusSpeed(float speed){
|
||||
//type.speed should never be 0
|
||||
applyDynamicStatus().speedMultiplier = speed / type.speed;
|
||||
applyDynamicStatus().speedMultiplier = speed / (type.speed * 60f / tilesize);
|
||||
}
|
||||
|
||||
/** Uses a dynamic status effect to change damage. */
|
||||
|
||||
@@ -21,18 +21,32 @@ public class SoundEffect extends Effect{
|
||||
public Effect effect;
|
||||
|
||||
public SoundEffect(){
|
||||
startDelay = -1;
|
||||
}
|
||||
|
||||
public SoundEffect(Sound sound, Effect effect){
|
||||
this();
|
||||
this.sound = sound;
|
||||
this.effect = effect;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
if(startDelay < 0){
|
||||
startDelay = effect.startDelay;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create(float x, float y, float rotation, Color color, Object data){
|
||||
if(!shouldCreate()) return;
|
||||
|
||||
sound.at(x, y, Mathf.random(minPitch, maxPitch), Mathf.random(minVolume, maxVolume));
|
||||
if(startDelay > 0){
|
||||
Time.run(startDelay, () -> sound.at(x, y, Mathf.random(minPitch, maxPitch), Mathf.random(minVolume, maxVolume)));
|
||||
}else{
|
||||
sound.at(x, y, Mathf.random(minPitch, maxPitch), Mathf.random(minVolume, maxVolume));
|
||||
}
|
||||
|
||||
effect.create(x, y, rotation, color, data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,8 +85,10 @@ public abstract class DrawPart{
|
||||
/** Weapon heat, 1 when just fired, 0, when it has cooled down (duration depends on weapon) */
|
||||
heat = p -> p.heat,
|
||||
/** Lifetime fraction, 0 to 1. Only for missiles. */
|
||||
life = p -> p.life;
|
||||
|
||||
life = p -> p.life,
|
||||
/** Current unscaled value of Time.time. */
|
||||
time = p -> Time.time;
|
||||
|
||||
float get(PartParams p);
|
||||
|
||||
static PartProgress constant(float value){
|
||||
@@ -167,6 +169,14 @@ public abstract class DrawPart{
|
||||
default PartProgress absin(float scl, float mag){
|
||||
return p -> get(p) + Mathf.absin(scl, mag);
|
||||
}
|
||||
|
||||
default PartProgress mod(float amount){
|
||||
return p -> Mathf.mod(get(p), amount);
|
||||
}
|
||||
|
||||
default PartProgress loop(float time){
|
||||
return p -> Mathf.mod(get(p)/time, 1);
|
||||
}
|
||||
|
||||
default PartProgress apply(PartProgress other, PartFunc func){
|
||||
return p -> func.get(get(p), other.get(p));
|
||||
|
||||
@@ -227,7 +227,7 @@ public class AIController implements UnitController{
|
||||
}
|
||||
|
||||
public Teamc target(float x, float y, float range, boolean air, boolean ground){
|
||||
return Units.closestTarget(unit.team, x, y, range, u -> u.checkTarget(air, ground), t -> ground);
|
||||
return Units.closestTarget(unit.team, x, y, range, u -> u.checkTarget(air, ground), t -> ground && (unit.type.targetUnderBlocks || !t.block.underBullets));
|
||||
}
|
||||
|
||||
public boolean retarget(){
|
||||
|
||||
@@ -35,6 +35,7 @@ public enum Gamemode{
|
||||
}, map -> map.teams.size > 1),
|
||||
editor(true, rules -> {
|
||||
rules.infiniteResources = true;
|
||||
rules.instantBuild = true;
|
||||
rules.editor = true;
|
||||
rules.waves = false;
|
||||
rules.waveTimer = false;
|
||||
|
||||
@@ -39,8 +39,6 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
* @see #eachRunning(Cons)
|
||||
*/
|
||||
public Seq<MapObjective> all = new Seq<>(4);
|
||||
/** @see #checkChanged() */
|
||||
protected transient boolean changed;
|
||||
|
||||
static{
|
||||
registerObjective(
|
||||
@@ -61,12 +59,15 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
|
||||
registerMarker(
|
||||
ShapeTextMarker::new,
|
||||
MinimapMarker::new,
|
||||
PointMarker::new,
|
||||
ShapeMarker::new,
|
||||
TextMarker::new,
|
||||
LineMarker::new,
|
||||
TextureMarker::new
|
||||
TextureMarker::new,
|
||||
QuadMarker::new
|
||||
);
|
||||
|
||||
registerLegacyMarker("Minimap", PointMarker::new);
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
@@ -96,6 +97,15 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
}
|
||||
}
|
||||
|
||||
public static void registerLegacyMarker(String name, Prov<? extends ObjectiveMarker> prov) {
|
||||
Class<?> type = prov.get().getClass();
|
||||
|
||||
markerNameToType.put(name, prov);
|
||||
markerNameToType.put(Strings.camelize(name), prov);
|
||||
JsonIO.classTag(Strings.camelize(name), type);
|
||||
JsonIO.classTag(name, type);
|
||||
}
|
||||
|
||||
/** Adds all given objectives to the executor as root objectives. */
|
||||
public void add(MapObjective... objectives){
|
||||
for(var objective : objectives) flatten(objective);
|
||||
@@ -114,21 +124,13 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
eachRunning(obj -> {
|
||||
//objectives cannot get completed on the client, but they do try to update for timers and such
|
||||
if(obj.update() && !net.client()){
|
||||
obj.completed = true;
|
||||
obj.done();
|
||||
Call.completeObjective(all.indexOf(obj));
|
||||
}
|
||||
|
||||
changed |= obj.changed;
|
||||
obj.changed = false;
|
||||
});
|
||||
}
|
||||
|
||||
/** @return True if map rules should be synced. Reserved for {@link Vars#logic}; do not invoke directly! */
|
||||
public boolean checkChanged(){
|
||||
boolean has = changed;
|
||||
changed = false;
|
||||
|
||||
return has;
|
||||
public @Nullable MapObjective get(int index){
|
||||
return index < 0 || index >= all.size ? null : all.get(index);
|
||||
}
|
||||
|
||||
/** @return Whether there are any qualified objectives at all. */
|
||||
@@ -137,7 +139,6 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
}
|
||||
|
||||
public void clear(){
|
||||
if(all.size > 0) changed = true;
|
||||
all.clear();
|
||||
}
|
||||
|
||||
@@ -179,7 +180,7 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
/** Whether this objective has been done yet. This is internally set. */
|
||||
private boolean completed;
|
||||
/** Internal value. Do not modify! */
|
||||
private transient boolean depFinished, changed;
|
||||
private transient boolean depFinished;
|
||||
|
||||
/** @return True if this objective is done and should be removed from the executor. */
|
||||
public abstract boolean update();
|
||||
@@ -189,13 +190,9 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
|
||||
/** Called once after {@link #update()} returns true, before this objective is removed. */
|
||||
public void done(){
|
||||
changed();
|
||||
Call.objectiveCompleted(flagsRemoved, flagsAdded);
|
||||
}
|
||||
|
||||
/** Notifies the executor that map rules should be synced. */
|
||||
protected void changed(){
|
||||
changed = true;
|
||||
state.rules.objectiveFlags.removeAll(flagsRemoved);
|
||||
state.rules.objectiveFlags.addAll(flagsAdded);
|
||||
completed = true;
|
||||
}
|
||||
|
||||
/** @return True if all {@link #parents} are completed, rendering this objective able to execute. */
|
||||
@@ -617,38 +614,26 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
/** Internal use only! Do not access. */
|
||||
public transient int arrayIndex;
|
||||
|
||||
/** Whether to display marker on minimap instead of world. {@link MinimapMarker} ignores this value. */
|
||||
/** Whether to display marker in the world. */
|
||||
public boolean world = true;
|
||||
/** Whether to display marker on minimap. */
|
||||
public boolean minimap = false;
|
||||
/** Whether to scale marker corresponding to player's zoom level. {@link MinimapMarker} ignores this value. */
|
||||
/** Whether to scale marker corresponding to player's zoom level. */
|
||||
public boolean autoscale = false;
|
||||
/** Hides the marker, used by world processors. */
|
||||
protected boolean hidden = false;
|
||||
/** On which z-sorting layer is marker drawn. */
|
||||
protected float drawLayer = Layer.overlayUI;
|
||||
|
||||
/** Draws the marker. Actual marker position and scale are calculated in {@link #drawWorld()} and {@link #drawMinimap(MinimapRenderer)}. */
|
||||
public void baseDraw(float x, float y, float scaleFactor){}
|
||||
|
||||
/** Called in the main renderer. */
|
||||
public void drawWorld(){}
|
||||
|
||||
/** Called in the small and large map. */
|
||||
public void drawMinimap(MinimapRenderer minimap){}
|
||||
|
||||
/** Whether the marker is hidden */
|
||||
public boolean isHidden(){
|
||||
return hidden;
|
||||
}
|
||||
public void draw(float scaleFactor){}
|
||||
|
||||
/** Control marker with world processor code. Ignores NaN (null) values. */
|
||||
public void control(LMarkerControl type, double p1, double p2, double p3){
|
||||
if(Double.isNaN(p1)) return;
|
||||
|
||||
switch(type){
|
||||
case visibility -> hidden = Mathf.equal((float)p1, 0f);
|
||||
case drawLayer -> drawLayer = (float)p1;
|
||||
case world -> world = !Mathf.equal((float)p1, 0f);
|
||||
case minimap -> minimap = !Mathf.equal((float)p1, 0f);
|
||||
case autoscale -> autoscale = !Mathf.equal((float)p1, 0f);
|
||||
case drawLayer -> drawLayer = (float)p1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -688,34 +673,19 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
/** Position of marker, in world coordinates */
|
||||
public @TilePos Vec2 pos = new Vec2();
|
||||
|
||||
/** Called in the main renderer. */
|
||||
@Override
|
||||
public void drawWorld(){
|
||||
baseDraw(pos.x, pos.y, autoscale ? 4f / renderer.getDisplayScale() : 1f);
|
||||
}
|
||||
|
||||
/** Called in the small and large map. */
|
||||
@Override
|
||||
public void drawMinimap(MinimapRenderer minimap){
|
||||
minimap.transform(Tmp.v1.set(pos.x + 4f, pos.y + 4f));
|
||||
baseDraw(Tmp.v1.x, Tmp.v1.y, minimap.getScaleFactor(autoscale));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void control(LMarkerControl type, double p1, double p2, double p3){
|
||||
super.control(type, p1, p2, p3);
|
||||
|
||||
if(!Double.isNaN(p1)){
|
||||
if(type == LMarkerControl.pos){
|
||||
pos.x = (float)p1 * tilesize;
|
||||
}else{
|
||||
super.control(type, p1, p2, p3);
|
||||
}
|
||||
}
|
||||
|
||||
if(!Double.isNaN(p2)){
|
||||
if(type == LMarkerControl.pos){
|
||||
pos.y = (float)p2 * tilesize;
|
||||
}else{
|
||||
super.control(type, p1, p2, p3);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -763,15 +733,15 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
public ShapeTextMarker(){}
|
||||
|
||||
@Override
|
||||
public void baseDraw(float x, float y, float scaleFactor){
|
||||
public void draw(float scaleFactor){
|
||||
//in case some idiot decides to make 9999999 sides and freeze the game
|
||||
int sides = Math.min(this.sides, 300);
|
||||
|
||||
Draw.z(drawLayer);
|
||||
Lines.stroke(3f * scaleFactor, Pal.gray);
|
||||
Lines.poly(x, y, sides, (radius + 1f) * scaleFactor, rotation);
|
||||
Lines.poly(pos.x, pos.y, sides, (radius + 1f) * scaleFactor, rotation);
|
||||
Lines.stroke(scaleFactor, color);
|
||||
Lines.poly(x, y, sides, (radius + 1f) * scaleFactor, rotation);
|
||||
Lines.poly(pos.x, pos.y, sides, (radius + 1f) * scaleFactor, rotation);
|
||||
Draw.reset();
|
||||
|
||||
if(fetchedText == null){
|
||||
@@ -781,11 +751,13 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
// font size cannot be 0
|
||||
if(Mathf.equal(fontSize, 0f)) return;
|
||||
|
||||
WorldLabel.drawAt(fetchedText, x, y + radius * scaleFactor + textHeight * scaleFactor, drawLayer, flags, fontSize * scaleFactor);
|
||||
WorldLabel.drawAt(fetchedText, pos.x, pos.y + radius * scaleFactor + textHeight * scaleFactor, drawLayer, flags, fontSize * scaleFactor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void control(LMarkerControl type, double p1, double p2, double p3){
|
||||
super.control(type, p1, p2, p3);
|
||||
|
||||
if(!Double.isNaN(p1)){
|
||||
switch(type){
|
||||
case fontSize -> fontSize = (float)p1;
|
||||
@@ -799,9 +771,8 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
}
|
||||
case radius -> radius = (float)p1;
|
||||
case rotation -> rotation = (float)p1;
|
||||
case color -> color.set(Tmp.c1.fromDouble(p1));
|
||||
case color -> color.fromDouble(p1);
|
||||
case shape -> sides = (int)p1;
|
||||
default -> super.control(type, p1, p2, p3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -814,7 +785,6 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
flags &= ~WorldLabel.flagOutline;
|
||||
}
|
||||
}
|
||||
default -> super.control(type, p1, p2, p3);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -830,73 +800,50 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
}
|
||||
}
|
||||
|
||||
/** Displays a circle on the minimap. */
|
||||
public static class MinimapMarker extends ObjectiveMarker{
|
||||
public Point2 pos = new Point2();
|
||||
/** Displays a circle in the world. */
|
||||
public static class PointMarker extends PosMarker{
|
||||
public float radius = 5f, stroke = 11f;
|
||||
public Color color = Color.valueOf("f25555");
|
||||
|
||||
public MinimapMarker(int x, int y){
|
||||
public PointMarker(int x, int y){
|
||||
this.pos.set(x, y);
|
||||
}
|
||||
|
||||
public MinimapMarker(int x, int y, Color color){
|
||||
public PointMarker(int x, int y, Color color){
|
||||
this.pos.set(x, y);
|
||||
this.color = color;
|
||||
minimap = true;
|
||||
}
|
||||
|
||||
public MinimapMarker(int x, int y, float radius, float stroke, Color color){
|
||||
public PointMarker(int x, int y, float radius, float stroke, Color color){
|
||||
this.pos.set(x, y);
|
||||
this.stroke = stroke;
|
||||
this.radius = radius;
|
||||
this.color = color;
|
||||
minimap = true;
|
||||
}
|
||||
|
||||
public MinimapMarker(){}
|
||||
public PointMarker(){}
|
||||
|
||||
@Override
|
||||
public void baseDraw(float x, float y, float scaleFactor){
|
||||
public void draw(float scaleFactor){
|
||||
float rad = radius * tilesize * scaleFactor;
|
||||
float fin = Interp.pow2Out.apply((Time.globalTime / 100f) % 1f);
|
||||
|
||||
Draw.z(drawLayer);
|
||||
Lines.stroke(Scl.scl((1f - fin) * stroke + 0.1f), color);
|
||||
Lines.circle(x, y, rad * fin);
|
||||
Lines.circle(pos.x, pos.y, rad * fin);
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawWorld(){
|
||||
minimap = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawMinimap(MinimapRenderer minimap){
|
||||
minimap.transform(Tmp.v1.set(pos.x * tilesize, pos.y * tilesize));
|
||||
baseDraw(Tmp.v1.x, Tmp.v1.y, minimap.getScaleFactor(autoscale));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void control(LMarkerControl type, double p1, double p2, double p3){
|
||||
super.control(type, p1, p2, p3);
|
||||
|
||||
if(!Double.isNaN(p1)){
|
||||
switch(type){
|
||||
case pos -> pos.x = (int)p1;
|
||||
case radius -> radius = (float)p1;
|
||||
case stroke -> stroke = (float)p1;
|
||||
case color -> color.set(Tmp.c1.fromDouble(p1));
|
||||
case minimap -> minimap = true;
|
||||
default -> super.control(type, p1, p2, p3);
|
||||
}
|
||||
}
|
||||
|
||||
if(!Double.isNaN(p2)){
|
||||
if(type == LMarkerControl.pos){
|
||||
pos.y = (int)p2;
|
||||
}else{
|
||||
super.control(type, p1, p2, p3);
|
||||
case color -> color.fromDouble(p1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -922,7 +869,7 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
public ShapeMarker(){}
|
||||
|
||||
@Override
|
||||
public void baseDraw(float x, float y, float scaleFactor){
|
||||
public void draw(float scaleFactor){
|
||||
//in case some idiot decides to make 9999999 sides and freeze the game
|
||||
int sides = Math.min(this.sides, 200);
|
||||
|
||||
@@ -930,14 +877,14 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
if(!fill){
|
||||
if(outline){
|
||||
Lines.stroke((stroke + 2f) * scaleFactor, Pal.gray);
|
||||
Lines.poly(x, y, sides, (radius + 1f) * scaleFactor, rotation);
|
||||
Lines.poly(pos.x, pos.y, sides, (radius + 1f) * scaleFactor, rotation);
|
||||
}
|
||||
|
||||
Lines.stroke(stroke * scaleFactor, color);
|
||||
Lines.poly(x, y, sides, (radius + 1f) * scaleFactor, rotation);
|
||||
Lines.poly(pos.x, pos.y, sides, (radius + 1f) * scaleFactor, rotation);
|
||||
}else{
|
||||
Draw.color(color);
|
||||
Fill.poly(x, y, sides, radius * scaleFactor, rotation);
|
||||
Fill.poly(pos.x, pos.y, sides, radius * scaleFactor, rotation);
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
@@ -945,29 +892,27 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
|
||||
@Override
|
||||
public void control(LMarkerControl type, double p1, double p2, double p3){
|
||||
super.control(type, p1, p2, p3);
|
||||
|
||||
if(!Double.isNaN(p1)){
|
||||
switch(type){
|
||||
case radius -> radius = (float)p1;
|
||||
case stroke -> stroke = (float)p1;
|
||||
case rotation -> rotation = (float)p1;
|
||||
case color -> color.set(Tmp.c1.fromDouble(p1));
|
||||
case color -> color.fromDouble(p1);
|
||||
case shape -> sides = (int)p1;
|
||||
default -> super.control(type, p1, p2, p3);
|
||||
}
|
||||
}
|
||||
|
||||
if(!Double.isNaN(p2)){
|
||||
switch(type){
|
||||
case shape -> fill = !Mathf.equal((float)p2, 0f);
|
||||
default -> super.control(type, p1, p2, p3);
|
||||
}
|
||||
}
|
||||
|
||||
if(!Double.isNaN(p3)){
|
||||
if(type == LMarkerControl.shape){
|
||||
outline = !Mathf.equal((float)p3, 0f);
|
||||
}else{
|
||||
super.control(type, p1, p2, p3);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -996,7 +941,7 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
public TextMarker(){}
|
||||
|
||||
@Override
|
||||
public void baseDraw(float x, float y, float scaleFactor){
|
||||
public void draw(float scaleFactor){
|
||||
// font size cannot be 0
|
||||
if(Mathf.equal(fontSize, 0f)) return;
|
||||
|
||||
@@ -1004,11 +949,13 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
fetchedText = fetchText(text);
|
||||
}
|
||||
|
||||
WorldLabel.drawAt(fetchedText, x, y, drawLayer, flags, fontSize * scaleFactor);
|
||||
WorldLabel.drawAt(fetchedText, pos.x, pos.y, drawLayer, flags, fontSize * scaleFactor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void control(LMarkerControl type, double p1, double p2, double p3){
|
||||
super.control(type, p1, p2, p3);
|
||||
|
||||
if(!Double.isNaN(p1)){
|
||||
switch(type){
|
||||
case fontSize -> fontSize = (float)p1;
|
||||
@@ -1019,7 +966,6 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
flags &= ~WorldLabel.flagBackground;
|
||||
}
|
||||
}
|
||||
default -> super.control(type, p1, p2, p3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1032,7 +978,6 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
flags &= ~WorldLabel.flagOutline;
|
||||
}
|
||||
}
|
||||
default -> super.control(type, p1, p2, p3);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1053,7 +998,8 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
public @TilePos Vec2 endPos = new Vec2();
|
||||
public float stroke = 1f;
|
||||
public boolean outline = true;
|
||||
public Color color = Color.valueOf("ffd37f");
|
||||
public Color color1 = Color.valueOf("ffd37f");
|
||||
public Color color2 = Color.valueOf("ffd37f");
|
||||
|
||||
public LineMarker(float x1, float y1, float x2, float y2, float stroke){
|
||||
this.stroke = stroke;
|
||||
@@ -1068,44 +1014,46 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
|
||||
public LineMarker(){}
|
||||
|
||||
public void baseLineDraw(float x1, float y1, float x2, float y2, float scaleFactor){
|
||||
@Override
|
||||
public void draw(float scaleFactor){
|
||||
Draw.z(drawLayer);
|
||||
if(outline){
|
||||
Lines.stroke((stroke + 2f) * scaleFactor, Pal.gray);
|
||||
Lines.line(x1, y1, x2, y2);
|
||||
Lines.line(pos.x, pos.y, endPos.x, endPos.y);
|
||||
}
|
||||
|
||||
Lines.stroke(stroke * scaleFactor, color);
|
||||
Lines.line(x1, y1, x2, y2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawWorld(){
|
||||
baseLineDraw(pos.x, pos.y, endPos.x, endPos.y, autoscale ? 4f / renderer.getDisplayScale() : 1f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawMinimap(MinimapRenderer minimap){
|
||||
minimap.transform(Tmp.v1.set(pos.x + 4f, pos.y + 4f));
|
||||
minimap.transform(Tmp.v2.set(endPos.x + 4f, endPos.y + 4f));
|
||||
baseLineDraw(Tmp.v1.x, Tmp.v1.y, Tmp.v2.x, Tmp.v2.y, minimap.getScaleFactor(autoscale));
|
||||
Lines.stroke(stroke * scaleFactor, Color.white);
|
||||
Lines.line(pos.x, pos.y, color1, endPos.x, endPos.y, color2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void control(LMarkerControl type, double p1, double p2, double p3){
|
||||
super.control(type, p1, p2, p3);
|
||||
|
||||
if(!Double.isNaN(p1)){
|
||||
switch(type){
|
||||
case endPos -> endPos.x = (float)p1 * tilesize;
|
||||
case stroke -> stroke = (float)p1;
|
||||
case color -> color.set(Tmp.c1.fromDouble(p1));
|
||||
default -> super.control(type, p1, p2, p3);
|
||||
case color -> color1.set(color2.fromDouble(p1));
|
||||
}
|
||||
}
|
||||
|
||||
if(!Double.isNaN(p2)){
|
||||
switch(type){
|
||||
case endPos -> endPos.y = (float)p2 * tilesize;
|
||||
default -> super.control(type, p1, p2, p3);
|
||||
}
|
||||
}
|
||||
|
||||
if(!Double.isNaN(p1) && !Double.isNaN(p2)){
|
||||
switch (type){
|
||||
case posi -> ((int)p1 == 0 ? pos : (int)p1 == 1 ? endPos : Tmp.v1).x = (float)p2 * tilesize;
|
||||
case colori -> ((int)p1 == 0 ? color1 : (int)p1 == 1 ? color2 : Tmp.c1).fromDouble(p2);
|
||||
}
|
||||
}
|
||||
|
||||
if(!Double.isNaN(p1) && !Double.isNaN(p3)){
|
||||
switch(type){
|
||||
case posi -> ((int)p1 == 0 ? pos : (int)p1 == 1 ? endPos : Tmp.v1).y = (float)p3 * tilesize;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1135,25 +1083,25 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
|
||||
@Override
|
||||
public void control(LMarkerControl type, double p1, double p2, double p3){
|
||||
super.control(type, p1, p2, p3);
|
||||
|
||||
if(!Double.isNaN(p1)){
|
||||
switch(type){
|
||||
case rotation -> rotation = (float)p1;
|
||||
case textureSize -> width = (float)p1 * tilesize;
|
||||
case color -> color.set(Tmp.c1.fromDouble(p1));
|
||||
default -> super.control(type, p1, p2, p3);
|
||||
case color -> color.fromDouble(p1);
|
||||
}
|
||||
}
|
||||
|
||||
if(!Double.isNaN(p2)){
|
||||
switch(type){
|
||||
case textureSize -> height = (float)p2 * tilesize;
|
||||
default -> super.control(type, p1, p2, p3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void baseDraw(float x, float y, float scaleFactor){
|
||||
public void draw(float scaleFactor){
|
||||
if(textureName.isEmpty()) return;
|
||||
|
||||
if(fetchedRegion == null) setTexture(textureName);
|
||||
@@ -1164,7 +1112,7 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
|
||||
Draw.z(drawLayer);
|
||||
Draw.color(color);
|
||||
Draw.rect(fetchedRegion, x, y, width * scaleFactor, height * scaleFactor, rotation);
|
||||
Draw.rect(fetchedRegion, pos.x, pos.y, width * scaleFactor, height * scaleFactor, rotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1172,19 +1120,126 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
this.textureName = textureName;
|
||||
|
||||
if(fetchedRegion == null) fetchedRegion = new TextureRegion();
|
||||
lookupRegion(textureName, fetchedRegion);
|
||||
}
|
||||
|
||||
TextureRegion region = Core.atlas.find(textureName);
|
||||
if(region.found()){
|
||||
fetchedRegion.set(region);
|
||||
}else{
|
||||
if(Core.assets.isLoaded(textureName, Texture.class)){
|
||||
fetchedRegion.set(Core.assets.get(textureName, Texture.class));
|
||||
}else{
|
||||
fetchedRegion.set(Core.atlas.find("error"));
|
||||
}
|
||||
|
||||
public static class QuadMarker extends ObjectiveMarker{
|
||||
public String textureName = "white";
|
||||
public @Vertices float[] vertices = new float[24];
|
||||
private boolean mapRegion = true;
|
||||
|
||||
private transient TextureRegion fetchedRegion;
|
||||
|
||||
public QuadMarker() {
|
||||
for(int i = 0; i < 4; i++){
|
||||
vertices[i * 6 + 2] = Color.white.toFloatBits();
|
||||
vertices[i * 6 + 5] = Color.clearFloatBits;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(float scaleFactor){
|
||||
if(fetchedRegion == null) setTexture(textureName);
|
||||
|
||||
Draw.z(drawLayer);
|
||||
Draw.vert(fetchedRegion.texture, vertices, 0, vertices.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void control(LMarkerControl type, double p1, double p2, double p3){
|
||||
super.control(type, p1, p2, p3);
|
||||
|
||||
if(!Double.isNaN(p1)){
|
||||
switch(type){
|
||||
case color -> {
|
||||
float col = Tmp.c1.fromDouble(p1).toFloatBits();
|
||||
for(int i = 0; i < 4; i++) vertices[i * 6 + 2] = col;
|
||||
}
|
||||
case pos -> vertices[0] = (float)p1 * tilesize;
|
||||
case posi -> setPos((int)p1, p2, p3);
|
||||
case uvi -> setUv((int)p1, p2, p3);
|
||||
}
|
||||
}
|
||||
|
||||
if(!Double.isNaN(p2)){
|
||||
switch(type){
|
||||
case pos -> vertices[1] = (float)p1 * tilesize;
|
||||
}
|
||||
}
|
||||
|
||||
if(!Double.isNaN(p1) && !Double.isNaN(p2)){
|
||||
switch(type){
|
||||
case colori -> setColor((int)p1, p2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTexture(String textureName){
|
||||
this.textureName = textureName;
|
||||
|
||||
boolean firstUpdate = fetchedRegion == null;
|
||||
|
||||
if(fetchedRegion == null) fetchedRegion = new TextureRegion();
|
||||
Tmp.tr1.set(fetchedRegion);
|
||||
|
||||
lookupRegion(textureName, fetchedRegion);
|
||||
|
||||
if(firstUpdate){
|
||||
if(mapRegion){
|
||||
mapRegion = false;
|
||||
|
||||
// possibly from the editor, we need to clamp the values
|
||||
for(int i = 0; i < 4; i++){
|
||||
vertices[i * 6 + 3] = Mathf.map(Mathf.clamp(vertices[i * 6 + 3]), fetchedRegion.u, fetchedRegion.u2);
|
||||
vertices[i * 6 + 4] = Mathf.map(1 - Mathf.clamp(vertices[i * 6 + 4]), fetchedRegion.v, fetchedRegion.v2);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
for(int i = 0; i < 4; i++){
|
||||
vertices[i * 6 + 3] = Mathf.map(vertices[i * 6 + 3], Tmp.tr1.u, Tmp.tr1.u2, fetchedRegion.u, fetchedRegion.u2);
|
||||
vertices[i * 6 + 4] = Mathf.map(vertices[i * 6 + 4], Tmp.tr1.v, Tmp.tr1.v2, fetchedRegion.v, fetchedRegion.v2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void setPos(int i, double x, double y){
|
||||
if(i >= 0 && i < 4){
|
||||
if(!Double.isNaN(x)) vertices[i * 6] = (float)x * tilesize;
|
||||
if(!Double.isNaN(y)) vertices[i * 6 + 1] = (float)y * tilesize;
|
||||
}
|
||||
}
|
||||
|
||||
private void setColor(int i, double c){
|
||||
if(i >= 0 && i < 4){
|
||||
vertices[i * 6 + 2] = Tmp.c1.fromDouble(c).toFloatBits();
|
||||
}
|
||||
}
|
||||
|
||||
private void setUv(int i, double u, double v){
|
||||
if(i >= 0 && i < 4){
|
||||
if(fetchedRegion == null) setTexture(textureName);
|
||||
|
||||
if(!Double.isNaN(u)) vertices[i * 6 + 3] = Mathf.map(Mathf.clamp((float)u), fetchedRegion.u, fetchedRegion.u2);
|
||||
if(!Double.isNaN(v)) vertices[i * 6 + 4] = Mathf.map(1 - Mathf.clamp((float)v), fetchedRegion.v, fetchedRegion.v2);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static void lookupRegion(String name, TextureRegion out){
|
||||
TextureRegion region = Core.atlas.find(name);
|
||||
if(region.found()){
|
||||
out.set(region);
|
||||
}else{
|
||||
if(Core.assets.isLoaded(name, Texture.class)){
|
||||
out.set(Core.assets.get(name, Texture.class));
|
||||
}else{
|
||||
out.set(Core.atlas.find("error"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** For arrays or {@link Seq}s; does not create element rearrangement buttons. */
|
||||
@@ -1192,6 +1247,16 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
|
||||
@Retention(RUNTIME)
|
||||
public @interface Unordered{}
|
||||
|
||||
/** For arrays or {@link Seq}s; does not add the new and delete buttons */
|
||||
@Target(FIELD)
|
||||
@Retention(RUNTIME)
|
||||
public @interface Immutable{}
|
||||
|
||||
/** For {@code float[]}; treats it as an array of vertices. */
|
||||
@Target(FIELD)
|
||||
@Retention(RUNTIME)
|
||||
public @interface Vertices{}
|
||||
|
||||
/** For {@code byte}; treats it as a world label flag. */
|
||||
@Target(FIELD)
|
||||
@Retention(RUNTIME)
|
||||
|
||||
@@ -105,6 +105,10 @@ public class Rules{
|
||||
public boolean coreDestroyClear = false;
|
||||
/** If true, banned blocks are hidden from the build menu. */
|
||||
public boolean hideBannedBlocks = false;
|
||||
/** If true, most blocks (including environmental walls) can be deconstructed. This is only meant to be used internally in sandbox/test maps. */
|
||||
public boolean allowEnvironmentDeconstruct = false;
|
||||
/** If true, buildings will be constructed instantly, with no limit on blocks placed per second. This is highly experimental and may cause lag! */
|
||||
public boolean instantBuild = false;
|
||||
/** If true, bannedBlocks becomes a whitelist. */
|
||||
public boolean blockWhitelist = false;
|
||||
/** If true, bannedUnits becomes a whitelist. */
|
||||
|
||||
@@ -31,7 +31,7 @@ public class Schematic implements Publishable, Comparable<Schematic>{
|
||||
}
|
||||
|
||||
public float powerProduction(){
|
||||
return tiles.sumf(s -> s.block instanceof PowerGenerator p ? p.powerProduction : 0f);
|
||||
return tiles.sumf(s -> s.block instanceof PowerGenerator p ? p.getDisplayedPowerProduction() : 0f);
|
||||
}
|
||||
|
||||
public float powerConsumption(){
|
||||
|
||||
@@ -31,7 +31,7 @@ public class Team implements Comparable<Team>{
|
||||
derelict = new Team(0, "derelict", Color.valueOf("4d4e58")),
|
||||
sharded = new Team(1, "sharded", Pal.accent.cpy(), Color.valueOf("ffd37f"), Color.valueOf("eab678"), Color.valueOf("d4816b")),
|
||||
crux = new Team(2, "crux", Color.valueOf("f25555"), Color.valueOf("fc8e6c"), Color.valueOf("f25555"), Color.valueOf("a04553")),
|
||||
malis = new Team(3, "malis", Color.valueOf("a27ce5"), Color.valueOf("c195fb"), Color.valueOf("665c9f"), Color.valueOf("484988")),
|
||||
malis = new Team(3, "malis", Color.valueOf("a27ce5"), Color.valueOf("c7a4f5"), Color.valueOf("896fd6"), Color.valueOf("504cba")),
|
||||
|
||||
//TODO temporarily no palettes for these teams.
|
||||
green = new Team(4, "green", Color.valueOf("54d67d")),//Color.valueOf("96f58c"), Color.valueOf("54d67d"), Color.valueOf("28785c")),
|
||||
|
||||
@@ -146,16 +146,30 @@ public class MinimapRenderer{
|
||||
|
||||
rect.set((dx - sz) * tilesize, (dy - sz) * tilesize, sz * 2 * tilesize, sz * 2 * tilesize);
|
||||
|
||||
Tmp.m2.set(Draw.trans());
|
||||
|
||||
float scaleFactor;
|
||||
var trans = Tmp.m1.idt();
|
||||
trans.translate(lastX, lastY);
|
||||
if(!worldSpace){
|
||||
trans.scl(Tmp.v1.set(scaleFactor = lastW / rect.width, lastH / rect.height));
|
||||
trans.translate(-rect.x, -rect.y);
|
||||
}else{
|
||||
trans.scl(Tmp.v1.set(scaleFactor = lastW / world.unitWidth(), lastH / world.unitHeight()));
|
||||
}
|
||||
trans.translate(tilesize / 2f, tilesize / 2f);
|
||||
Draw.trans(trans);
|
||||
|
||||
scaleFactor = 1f / scaleFactor;
|
||||
|
||||
for(Unit unit : units){
|
||||
if(unit.inFogTo(player.team()) || !unit.type.drawMinimap) continue;
|
||||
|
||||
float rx = !fullView ? (unit.x - rect.x) / rect.width * w : unit.x / (world.width() * tilesize) * w;
|
||||
float ry = !fullView ? (unit.y - rect.y) / rect.width * h : unit.y / (world.height() * tilesize) * h;
|
||||
float scale = Scl.scl(1f) * tilesize * 3;
|
||||
var region = unit.icon();
|
||||
|
||||
Draw.mixcol(unit.team.color, 1f);
|
||||
float scale = Scl.scl(1f) / 2f * scaling * 32f;
|
||||
var region = unit.icon();
|
||||
Draw.rect(region, x + rx, y + ry, scale, scale * (float)region.height / region.width, unit.rotation() - 90);
|
||||
Draw.rect(region, unit.x, unit.y, scale, scale * (float)region.height / region.width, unit.rotation() - 90);
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
@@ -186,15 +200,14 @@ public class MinimapRenderer{
|
||||
//crisp pixels
|
||||
dynamicTex.setFilter(TextureFilter.nearest);
|
||||
|
||||
if(worldSpace){
|
||||
region.set(0f, 0f, 1f, 1f);
|
||||
}
|
||||
|
||||
Tmp.tr1.set(dynamicTex);
|
||||
Tmp.tr1.set(region.u, 1f - region.v, region.u2, 1f - region.v2);
|
||||
Tmp.tr1.set(0f, 1f, 1f, 0f);
|
||||
|
||||
float wf = world.width() * tilesize;
|
||||
float hf = world.height() * tilesize;
|
||||
|
||||
Draw.color(state.rules.dynamicColor, 0.5f);
|
||||
Draw.rect(Tmp.tr1, x + w/2f, y + h/2f, w, h);
|
||||
Draw.rect(Tmp.tr1, wf / 2, hf / 2, wf, hf);
|
||||
|
||||
if(state.rules.staticFog){
|
||||
staticTex.setFilter(TextureFilter.nearest);
|
||||
@@ -202,7 +215,7 @@ public class MinimapRenderer{
|
||||
Tmp.tr1.texture = staticTex;
|
||||
//must be black to fit with borders
|
||||
Draw.color(0f, 0f, 0f, 1f);
|
||||
Draw.rect(Tmp.tr1, x + w/2f, y + h/2f, w, h);
|
||||
Draw.rect(Tmp.tr1, wf / 2, hf / 2, wf, hf);
|
||||
}
|
||||
|
||||
Draw.color();
|
||||
@@ -211,23 +224,21 @@ public class MinimapRenderer{
|
||||
|
||||
//TODO might be useful in the standard minimap too
|
||||
if(fullView){
|
||||
drawSpawns(x, y, w, h, scaling);
|
||||
drawSpawns();
|
||||
|
||||
if(!mobile){
|
||||
//draw bounds for camera - not drawn on mobile because you can't shift it by tapping anyway
|
||||
Rect r = Core.camera.bounds(Tmp.r1);
|
||||
Vec2 bot = transform(Tmp.v1.set(r.x, r.y));
|
||||
Vec2 top = transform(Tmp.v2.set(r.x + r.width, r.y + r.height));
|
||||
Lines.stroke(Scl.scl(3f));
|
||||
Lines.stroke(Scl.scl(3f) * scaleFactor);
|
||||
Draw.color(Pal.accent);
|
||||
Lines.rect(bot.x,bot.y, top.x - bot.x, top.y - bot.y);
|
||||
Lines.rect(r.x, r.y, r.width, r.height);
|
||||
Draw.reset();
|
||||
}
|
||||
}
|
||||
|
||||
LongSeq indicators = control.indicators.list();
|
||||
float fin = ((Time.globalTime / 30f) % 1f);
|
||||
float rad = scale(fin * 5f + tilesize - 2f);
|
||||
float rad = fin * 5f + tilesize - 2f;
|
||||
Lines.stroke(Scl.scl((1f - fin) * 4f + 0.5f));
|
||||
|
||||
for(int i = 0; i < indicators.size; i++){
|
||||
@@ -244,31 +255,32 @@ public class MinimapRenderer{
|
||||
offset = build.block.offset / tilesize;
|
||||
}
|
||||
|
||||
Vec2 v = transform(Tmp.v1.set((ix + 0.5f + offset) * tilesize, (iy + 0.5f + offset) * tilesize));
|
||||
|
||||
Draw.color(Color.orange, Color.scarlet, Mathf.clamp(time / 70f));
|
||||
|
||||
Lines.square(v.x, v.y, rad);
|
||||
Lines.square((ix + 0.5f + offset) * tilesize, (iy + 0.5f + offset) * tilesize, rad);
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
|
||||
//TODO autoscale markers
|
||||
state.rules.objectives.eachRunning(obj -> {
|
||||
for(var marker : obj.markers){
|
||||
if(marker.minimap){
|
||||
marker.drawMinimap(this);
|
||||
marker.draw(1);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
for(var marker : state.markers){
|
||||
if(!marker.isHidden() && marker.minimap){
|
||||
marker.drawMinimap(this);
|
||||
if(marker.minimap){
|
||||
marker.draw(1);
|
||||
}
|
||||
}
|
||||
|
||||
Draw.trans(Tmp.m2);
|
||||
}
|
||||
|
||||
public void drawSpawns(float x, float y, float w, float h, float scaling){
|
||||
public void drawSpawns(){
|
||||
if(!state.rules.showSpawns || !state.hasSpawns() || !state.rules.waves) return;
|
||||
|
||||
TextureRegion icon = Icon.units.getRegion();
|
||||
@@ -277,44 +289,21 @@ public class MinimapRenderer{
|
||||
|
||||
Draw.color(state.rules.waveTeam.color, Tmp.c2.set(state.rules.waveTeam.color).value(1.2f), Mathf.absin(Time.time, 16f, 1f));
|
||||
|
||||
float rad = scale(state.rules.dropZoneRadius);
|
||||
float rad = state.rules.dropZoneRadius;
|
||||
float curve = Mathf.curve(Time.time % 240f, 120f, 240f);
|
||||
|
||||
for(Tile tile : spawner.getSpawns()){
|
||||
float tx = ((tile.x + 0.5f) / world.width()) * w;
|
||||
float ty = ((tile.y + 0.5f) / world.height()) * h;
|
||||
float tx = tile.worldx();
|
||||
float ty = tile.worldy();
|
||||
|
||||
Draw.rect(icon, x + tx, y + ty, icon.width, icon.height);
|
||||
Lines.circle(x + tx, y + ty, rad);
|
||||
if(curve > 0f) Lines.circle(x + tx, y + ty, rad * Interp.pow3Out.apply(curve));
|
||||
Draw.rect(icon, tx, ty, icon.width, icon.height);
|
||||
Lines.circle(tx, ty, rad);
|
||||
if(curve > 0f) Lines.circle(tx, ty, rad * Interp.pow3Out.apply(curve));
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
//TODO horrible code, everywhere.
|
||||
public Vec2 transform(Vec2 position){
|
||||
if(!worldSpace){
|
||||
position.sub(rect.x, rect.y).scl(lastW / rect.width, lastH / rect.height);
|
||||
}else{
|
||||
position.scl(lastW / world.unitWidth(), lastH / world.unitHeight());
|
||||
}
|
||||
|
||||
return position.add(lastX, lastY);
|
||||
}
|
||||
|
||||
public float scale(float radius){
|
||||
return worldSpace ? (radius / (baseSize / 2f)) * 5f * lastScl : lastW / rect.width * radius;
|
||||
}
|
||||
|
||||
public float getScaleFactor(boolean zoomAutoScale){
|
||||
if(!zoomAutoScale){
|
||||
return worldSpace ? (1 / (baseSize / 2f)) * 5f * lastScl : lastW / rect.width;
|
||||
}else{
|
||||
return worldSpace ? (1 / (baseSize / 2f)) * 5f : lastW / 256f;
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable TextureRegion getRegion(){
|
||||
if(texture == null) return null;
|
||||
|
||||
|
||||
@@ -97,8 +97,12 @@ public class MultiPacker implements Disposable{
|
||||
|
||||
@Override
|
||||
public void dispose(){
|
||||
for(PixmapPacker packer : packers){
|
||||
packer.dispose();
|
||||
for(int i = 0; i < PageType.all.length; i ++){
|
||||
var packer = packers[i];
|
||||
//the UI packer's image is later used when merging with the font, don't dispose it
|
||||
if(i != PageType.ui.ordinal()){
|
||||
packer.forceDispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -474,6 +474,6 @@ public class Shaders{
|
||||
}
|
||||
|
||||
public static Fi getShaderFi(String file){
|
||||
return Core.files.internal("shaders/" + file);
|
||||
return tree.get("shaders/" + file);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,17 +203,7 @@ public class PlanetRenderer implements Disposable{
|
||||
}
|
||||
|
||||
public void setPlane(Sector sector){
|
||||
float rotation = -sector.planet.getRotation();
|
||||
float length = 0.01f;
|
||||
|
||||
projector.setPlane(
|
||||
//origin on sector position
|
||||
Tmp.v33.set(sector.tile.v).setLength((outlineRad + length) * sector.planet.radius).rotate(Vec3.Y, rotation).add(sector.planet.position),
|
||||
//face up
|
||||
sector.plane.project(Tmp.v32.set(sector.tile.v).add(Vec3.Y)).sub(sector.tile.v, sector.planet.radius).rotate(Vec3.Y, rotation).nor(),
|
||||
//right vector
|
||||
Tmp.v31.set(Tmp.v32).rotate(Vec3.Y, -rotation).add(sector.tile.v).rotate(sector.tile.v, 90).sub(sector.tile.v).rotate(Vec3.Y, rotation).nor()
|
||||
);
|
||||
sector.planet.setPlane(sector, projector);
|
||||
}
|
||||
|
||||
public void fill(Sector sector, Color color, float offset){
|
||||
|
||||
@@ -1179,7 +1179,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
|
||||
var last = lastSchematic;
|
||||
|
||||
ui.showTextInput("@schematic.add", "@name", "", text -> {
|
||||
ui.showTextInput("@schematic.add", "@name", 1000, "", text -> {
|
||||
Schematic replacement = schematics.all().find(s -> s.name().equals(text));
|
||||
if(replacement != null){
|
||||
ui.showConfirm("@confirm", "@schematic.replace", () -> {
|
||||
@@ -1973,11 +1973,13 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
var end = world.build(endX, endY);
|
||||
if(diagonal && (block == null || block.allowDiagonal)){
|
||||
if(block != null && start instanceof ChainedBuilding && end instanceof ChainedBuilding
|
||||
&& block.canReplace(end.block) && block.canReplace(start.block)){
|
||||
&& block.canReplace(end.block) && block.canReplace(start.block)){
|
||||
points = Placement.upgradeLine(startX, startY, endX, endY);
|
||||
}else{
|
||||
points = Placement.pathfindLine(block != null && block.conveyorPlacement, startX, startY, endX, endY);
|
||||
}
|
||||
}else if(block != null && block.allowRectanglePlacement){
|
||||
points = Placement.normalizeRectangle(startX, startY, endX, endY, block.size);
|
||||
}else{
|
||||
points = Placement.normalizeLine(startX, startY, endX, endY);
|
||||
}
|
||||
|
||||
@@ -743,6 +743,11 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
queueCommandMode = false;
|
||||
}
|
||||
|
||||
//cannot rebuild and place at the same time
|
||||
if(block != null){
|
||||
rebuildMode = false;
|
||||
}
|
||||
|
||||
if(player.dead()){
|
||||
mode = none;
|
||||
manualShooting = false;
|
||||
@@ -766,7 +771,7 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
renderer.scaleCamera(Core.input.axisTap(Binding.zoom));
|
||||
}
|
||||
|
||||
if(!Core.settings.getBool("keyboard") && !locked){
|
||||
if(!Core.settings.getBool("keyboard") && !locked && !scene.hasKeyboard()){
|
||||
//move camera around
|
||||
float camSpeed = 6f;
|
||||
Core.camera.position.add(Tmp.v1.setZero().add(Core.input.axis(Binding.move_x), Core.input.axis(Binding.move_y)).nor().scl(Time.delta * camSpeed));
|
||||
@@ -967,7 +972,6 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
boolean allowHealing = type.canHeal;
|
||||
boolean validHealTarget = allowHealing && target instanceof Building b && b.isValid() && target.team() == unit.team && b.damaged() && target.within(unit, type.range);
|
||||
boolean boosted = (unit instanceof Mechc && unit.isFlying());
|
||||
|
||||
//reset target if:
|
||||
// - in the editor, or...
|
||||
// - it's both an invalid standard target and an invalid heal target
|
||||
|
||||
@@ -58,6 +58,22 @@ public class Placement{
|
||||
return points;
|
||||
}
|
||||
|
||||
/** Normalize two points into a rectangle. */
|
||||
public static Seq<Point2> normalizeRectangle(int startX, int startY, int endX, int endY, int blockSize){
|
||||
Pools.freeAll(points);
|
||||
points.clear();
|
||||
|
||||
int minX = Math.min(startX, endX), minY = Math.min(startY, endY), maxX = Math.max(startX, endX), maxY = Math.max(startY, endY);
|
||||
|
||||
for(int y = 0; y <= maxY - minY; y += blockSize){
|
||||
for(int x = 0; x <= maxX - minX; x += blockSize){
|
||||
points.add(Pools.obtain(Point2.class, Point2::new).set(startX + x * Mathf.sign(endX - startX), startY + y * Mathf.sign(endY - startY)));
|
||||
}
|
||||
}
|
||||
|
||||
return points;
|
||||
}
|
||||
|
||||
public static Seq<Point2> upgradeLine(int startX, int startY, int endX, int endY){
|
||||
closed.clear();
|
||||
Pools.freeAll(points);
|
||||
|
||||
@@ -27,45 +27,63 @@ public class GlobalVars{
|
||||
public static final Rand rand = new Rand();
|
||||
|
||||
//non-constants that depend on state
|
||||
private static int varTime, varTick, varSecond, varMinute, varWave, varWaveTime, varServer, varClient, varClientLocale, varClientUnit, varClientName, varClientTeam, varClientMobile;
|
||||
private static int varTime, varTick, varSecond, varMinute, varWave, varWaveTime, varMapW, varMapH, varServer, varClient, varClientLocale, varClientUnit, varClientName, varClientTeam, varClientMobile;
|
||||
|
||||
private ObjectIntMap<String> namesToIds = new ObjectIntMap<>();
|
||||
private Seq<Var> vars = new Seq<>(Var.class);
|
||||
private Seq<VarEntry> varEntries = new Seq<>();
|
||||
private IntSet privilegedIds = new IntSet();
|
||||
private UnlockableContent[][] logicIdToContent;
|
||||
private int[][] contentIdToLogicId;
|
||||
|
||||
public void init(){
|
||||
put("the end", null);
|
||||
putEntryOnly("sectionProcessor");
|
||||
|
||||
putEntryOnly("@this");
|
||||
putEntryOnly("@thisx");
|
||||
putEntryOnly("@thisy");
|
||||
putEntryOnly("@links");
|
||||
putEntryOnly("@ipt");
|
||||
|
||||
putEntryOnly("sectionGeneral");
|
||||
|
||||
put("the end", null, false, true);
|
||||
//add default constants
|
||||
put("false", 0);
|
||||
put("true", 1);
|
||||
put("null", null);
|
||||
putEntry("false", 0);
|
||||
putEntry("true", 1);
|
||||
put("null", null, false, true);
|
||||
|
||||
//math
|
||||
put("@pi", Mathf.PI);
|
||||
put("π", Mathf.PI); //for the "cool" kids
|
||||
put("@e", Mathf.E);
|
||||
put("@degToRad", Mathf.degRad);
|
||||
put("@radToDeg", Mathf.radDeg);
|
||||
putEntry("@pi", Mathf.PI);
|
||||
put("π", Mathf.PI, false, true); //for the "cool" kids
|
||||
putEntry("@e", Mathf.E);
|
||||
putEntry("@degToRad", Mathf.degRad);
|
||||
putEntry("@radToDeg", Mathf.radDeg);
|
||||
|
||||
putEntryOnly("sectionMap");
|
||||
|
||||
//time
|
||||
varTime = put("@time", 0);
|
||||
varTick = put("@tick", 0);
|
||||
varSecond = put("@second", 0);
|
||||
varMinute = put("@minute", 0);
|
||||
varWave = put("@waveNumber", 0);
|
||||
varWaveTime = put("@waveTime", 0);
|
||||
varTime = putEntry("@time", 0);
|
||||
varTick = putEntry("@tick", 0);
|
||||
varSecond = putEntry("@second", 0);
|
||||
varMinute = putEntry("@minute", 0);
|
||||
varWave = putEntry("@waveNumber", 0);
|
||||
varWaveTime = putEntry("@waveTime", 0);
|
||||
|
||||
varServer = put("@server", 0, true);
|
||||
varClient = put("@client", 0, true);
|
||||
varMapW = putEntry("@mapw", 0);
|
||||
varMapH = putEntry("@maph", 0);
|
||||
|
||||
putEntryOnly("sectionNetwork");
|
||||
|
||||
varServer = putEntry("@server", 0, true);
|
||||
varClient = putEntry("@client", 0, true);
|
||||
|
||||
//privileged desynced client variables
|
||||
varClientLocale = put("@clientLocale", null, true);
|
||||
varClientUnit = put("@clientUnit", null, true);
|
||||
varClientName = put("@clientName", null, true);
|
||||
varClientTeam = put("@clientTeam", 0, true);
|
||||
varClientMobile = put("@clientMobile", 0, true);
|
||||
varClientLocale = putEntry("@clientLocale", null, true);
|
||||
varClientUnit = putEntry("@clientUnit", null, true);
|
||||
varClientName = putEntry("@clientName", null, true);
|
||||
varClientTeam = putEntry("@clientTeam", 0, true);
|
||||
varClientMobile = putEntry("@clientMobile", 0, true);
|
||||
|
||||
//special enums
|
||||
put("@ctrlProcessor", ctrlProcessor);
|
||||
@@ -115,6 +133,8 @@ public class GlobalVars{
|
||||
logicIdToContent = new UnlockableContent[ContentType.all.length][];
|
||||
contentIdToLogicId = new int[ContentType.all.length][];
|
||||
|
||||
putEntryOnly("sectionLookup");
|
||||
|
||||
Fi ids = Core.files.internal("logicids.dat");
|
||||
if(ids.exists()){
|
||||
//read logic ID mapping data (generated in ImagePacker)
|
||||
@@ -125,7 +145,7 @@ public class GlobalVars{
|
||||
contentIdToLogicId[ctype.ordinal()] = new int[Vars.content.getBy(ctype).size];
|
||||
|
||||
//store count constants
|
||||
put("@" + ctype.name() + "Count", amount);
|
||||
putEntry("@" + ctype.name() + "Count", amount);
|
||||
|
||||
for(int i = 0; i < amount; i++){
|
||||
String name = in.readUTF();
|
||||
@@ -159,6 +179,9 @@ public class GlobalVars{
|
||||
vars.items[varWave].numval = state.wave;
|
||||
vars.items[varWaveTime].numval = state.wavetime / 60f;
|
||||
|
||||
vars.items[varMapW].numval = world.width();
|
||||
vars.items[varMapH].numval = world.height();
|
||||
|
||||
//network
|
||||
vars.items[varServer].numval = (net.server() || !net.active()) ? 1 : 0;
|
||||
vars.items[varClient].numval = net.client() ? 1 : 0;
|
||||
@@ -173,6 +196,10 @@ public class GlobalVars{
|
||||
}
|
||||
}
|
||||
|
||||
public Seq<VarEntry> getEntries(){
|
||||
return varEntries;
|
||||
}
|
||||
|
||||
/** @return a piece of content based on its logic ID. This is not equivalent to content ID. */
|
||||
public @Nullable Content lookupContent(ContentType type, int id){
|
||||
var arr = logicIdToContent[type.ordinal()];
|
||||
@@ -209,6 +236,11 @@ public class GlobalVars{
|
||||
|
||||
/** Adds a constant value by name. */
|
||||
public int put(String name, Object value, boolean privileged){
|
||||
return put(name, value, privileged, true);
|
||||
}
|
||||
|
||||
/** Adds a constant value by name. */
|
||||
public int put(String name, Object value, boolean privileged, boolean hidden){
|
||||
int existingIdx = namesToIds.get(name, -1);
|
||||
if(existingIdx != -1){ //don't overwrite existing vars (see #6910)
|
||||
Log.debug("Failed to add global logic variable '@', as it already exists.", name);
|
||||
@@ -228,10 +260,44 @@ public class GlobalVars{
|
||||
namesToIds.put(name, index);
|
||||
if(privileged) privilegedIds.add(index);
|
||||
vars.add(var);
|
||||
|
||||
if(!hidden){
|
||||
varEntries.add(new VarEntry(index, name, "", "", privileged));
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
public int put(String name, Object value){
|
||||
return put(name, value, false);
|
||||
}
|
||||
|
||||
public int putEntry(String name, Object value){
|
||||
return put(name, value, false, false);
|
||||
}
|
||||
|
||||
public int putEntry(String name, Object value, boolean privileged){
|
||||
return put(name, value, privileged, false);
|
||||
}
|
||||
|
||||
public void putEntryOnly(String name){
|
||||
varEntries.add(new VarEntry(0, name, "", "", false));
|
||||
}
|
||||
|
||||
/** An entry that describes a variable for documentation purposes. This is *only* used inside UI for global variables. */
|
||||
public static class VarEntry{
|
||||
public int id;
|
||||
public String name, description, icon;
|
||||
public boolean privileged;
|
||||
|
||||
public VarEntry(int id, String name, String description, String icon, boolean privileged){
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
this.icon = icon;
|
||||
this.privileged = privileged;
|
||||
}
|
||||
|
||||
public VarEntry(){
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
61
core/src/mindustry/logic/GlobalVarsDialog.java
Normal file
61
core/src/mindustry/logic/GlobalVarsDialog.java
Normal file
@@ -0,0 +1,61 @@
|
||||
package mindustry.logic;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.ui.dialogs.*;
|
||||
|
||||
public class GlobalVarsDialog extends BaseDialog{
|
||||
|
||||
public GlobalVarsDialog(){
|
||||
super("@logic.globals");
|
||||
|
||||
addCloseButton();
|
||||
shown(this::setup);
|
||||
onResize(this::setup);
|
||||
}
|
||||
|
||||
void setup(){
|
||||
float prefWidth = Math.min(Core.graphics.getWidth() * 0.9f / Scl.scl(1f) - 240f, 600f);
|
||||
cont.clearChildren();
|
||||
|
||||
cont.pane(t -> {
|
||||
t.margin(10f).marginRight(16f);
|
||||
t.defaults().fillX().fillY();
|
||||
for(var entry : Vars.logicVars.getEntries()){
|
||||
|
||||
if(entry.name.startsWith("section")){
|
||||
Color color = Pal.accent;
|
||||
t.add("@lglobal." + entry.name).fillX().center().labelAlign(Align.center).colspan(4).color(color).padTop(4f).padBottom(2f).row();
|
||||
t.image(Tex.whiteui).height(4f).color(color).colspan(4).padBottom(8f).row();
|
||||
}else{
|
||||
Color varColor = Pal.gray;
|
||||
float stub = 8f, mul = 0.5f, pad = 4;
|
||||
|
||||
String desc = entry.description;
|
||||
if(desc == null || desc.isEmpty()){
|
||||
desc = Core.bundle.get("lglobal." + entry.name, "");
|
||||
}
|
||||
|
||||
String fdesc = desc;
|
||||
|
||||
t.add(new Image(Tex.whiteui, varColor.cpy().mul(mul))).width(stub);
|
||||
t.stack(new Image(Tex.whiteui, varColor), new Label(" " + entry.name + " ", Styles.outlineLabel)).padRight(pad);
|
||||
|
||||
t.add(new Image(Tex.whiteui, Pal.gray.cpy().mul(mul))).width(stub);
|
||||
t.table(Tex.pane, out -> out.add(fdesc).style(Styles.outlineLabel).width(prefWidth).padLeft(2).padRight(2).wrap()).padRight(pad);
|
||||
|
||||
t.row();
|
||||
|
||||
t.add().fillX().colspan(4).height(4).row();
|
||||
}
|
||||
}
|
||||
}).grow();
|
||||
}
|
||||
}
|
||||
@@ -67,7 +67,7 @@ public enum LAccess{
|
||||
all = values(),
|
||||
senseable = Seq.select(all, t -> t.params.length <= 1).toArray(LAccess.class),
|
||||
controls = Seq.select(all, t -> t.params.length > 0).toArray(LAccess.class),
|
||||
settable = {x, y, rotation, speed, armor, health, team, flag, totalPower, payloadType};
|
||||
settable = {x, y, rotation, speed, armor, health, shield, team, flag, totalPower, payloadType};
|
||||
|
||||
LAccess(String... params){
|
||||
this.params = params;
|
||||
|
||||
@@ -706,7 +706,7 @@ public class LExecutor{
|
||||
int address = exec.numi(position);
|
||||
Building from = exec.building(target);
|
||||
|
||||
if(from instanceof MemoryBuild mem && (exec.privileged || from.team == exec.team) && address >= 0 && address < mem.memory.length){
|
||||
if(from instanceof MemoryBuild mem && (exec.privileged || (from.team == exec.team && !mem.block.privileged)) && address >= 0 && address < mem.memory.length){
|
||||
mem.memory[address] = exec.num(value);
|
||||
}
|
||||
}
|
||||
@@ -1025,14 +1025,17 @@ public class LExecutor{
|
||||
exec.textBuffer.setLength(0);
|
||||
}
|
||||
}else{
|
||||
int num1 = exec.numi(p1);
|
||||
int num1 = exec.numi(p1), xval = packSign(exec.numi(x)), yval = packSign(exec.numi(y));
|
||||
|
||||
if(type == LogicDisplay.commandImage){
|
||||
num1 = exec.obj(p1) instanceof UnlockableContent u ? u.iconId : 0;
|
||||
}else if(type == LogicDisplay.commandScale){
|
||||
xval = packSign((int)(exec.numf(x) / LogicDisplay.scaleStep));
|
||||
yval = packSign((int)(exec.numf(y) / LogicDisplay.scaleStep));
|
||||
}
|
||||
|
||||
//add graphics calls, cap graphics buffer size
|
||||
exec.graphicsBuffer.add(DisplayCmd.get(type, packSign(exec.numi(x)), packSign(exec.numi(y)), packSign(num1), packSign(exec.numi(p2)), packSign(exec.numi(p3)), packSign(exec.numi(p4))));
|
||||
exec.graphicsBuffer.add(DisplayCmd.get(type, xval, yval, packSign(num1), packSign(exec.numi(p2)), packSign(exec.numi(p3)), packSign(exec.numi(p4))));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2049,6 +2052,11 @@ public class LExecutor{
|
||||
state.markers.add(id, marker);
|
||||
}
|
||||
|
||||
@Remote(called = Loc.server, variants = Variant.both, unreliable = true)
|
||||
public static void removeMarker(int id){
|
||||
state.markers.remove(id);
|
||||
}
|
||||
|
||||
@Remote(called = Loc.server, variants = Variant.both, unreliable = true)
|
||||
public static void updateMarker(int id, LMarkerControl control, double p1, double p2, double p3){
|
||||
var marker = state.markers.get(id);
|
||||
|
||||
@@ -2,7 +2,7 @@ package mindustry.logic;
|
||||
|
||||
public enum LMarkerControl{
|
||||
remove,
|
||||
visibility("true/false"),
|
||||
world("true/false"),
|
||||
minimap("true/false"),
|
||||
autoscale("true/false"),
|
||||
pos("x", "y"),
|
||||
@@ -18,7 +18,10 @@ public enum LMarkerControl{
|
||||
textHeight("height"),
|
||||
labelFlags("background", "outline"),
|
||||
texture("printFlush", "name"),
|
||||
textureSize("width", "height");
|
||||
textureSize("width", "height"),
|
||||
posi("index", "x", "y"),
|
||||
uvi("index", "x", "y"),
|
||||
colori("index", "color");
|
||||
|
||||
public final String[] params;
|
||||
|
||||
|
||||
@@ -260,6 +260,13 @@ public class LStatements{
|
||||
}, 2, cell -> cell.size(165, 50)));
|
||||
}, Styles.logict, () -> {}).size(165, 40).color(s.color).left().padLeft(2);
|
||||
}
|
||||
case translate, scale -> {
|
||||
fields(s, "x", x, v -> x = v);
|
||||
fields(s, "y", y, v -> y = v);
|
||||
}
|
||||
case rotate -> {
|
||||
fields(s, "degrees", p1, v -> p1 = v);
|
||||
}
|
||||
}
|
||||
}).expand().left();
|
||||
}
|
||||
@@ -1999,11 +2006,14 @@ public class LStatements{
|
||||
table.table(t -> {
|
||||
t.setColor(table.color);
|
||||
|
||||
fields(t, type.params[i], i == 0 ? p1 : i == 1 ? p2 : p3, i == 0 ? v -> p1 = v : i == 1 ? v -> p2 = v : v -> p3 = v).width(100f);
|
||||
String value = i == 0 ? p1 : i == 1 ? p2 : p3;
|
||||
Cons<String> setter = i == 0 ? v -> p1 = v : i == 1 ? v -> p2 = v : v -> p3 = v;
|
||||
|
||||
if(type == LMarkerControl.color){
|
||||
col(t, p1, res -> {
|
||||
p1 = "%" + res.toString().substring(0, res.a >= 1f ? 6 : 8);
|
||||
fields(t, type.params[i], value, setter).width(100f);
|
||||
|
||||
if(type == LMarkerControl.color || (type == LMarkerControl.colori && i == 1)){
|
||||
col(t, value, res -> {
|
||||
setter.get("%" + res.toString().substring(0, res.a >= 1f ? 6 : 8));
|
||||
build(table);
|
||||
});
|
||||
}else if(type == LMarkerControl.drawLayer){
|
||||
@@ -2013,10 +2023,10 @@ public class LStatements{
|
||||
o.row();
|
||||
o.table(s -> {
|
||||
s.left();
|
||||
for(var layer : Layer.class.getFields()){
|
||||
float value = Reflect.get(Layer.class, layer.getName());
|
||||
s.button(layer.getName() + " = " + value, Styles.logicTogglet, () -> {
|
||||
p1 = Float.toString(value);
|
||||
for(var field : Layer.class.getFields()){
|
||||
float layer = Reflect.get(field);
|
||||
s.button(field.getName() + " = " + layer, Styles.logicTogglet, () -> {
|
||||
p1 = Float.toString(layer);
|
||||
rebuild(table);
|
||||
hide.run();
|
||||
}).size(240f, 40f).row();
|
||||
|
||||
@@ -26,6 +26,7 @@ public class LogicDialog extends BaseDialog{
|
||||
Cons<String> consumer = s -> {};
|
||||
boolean privileged;
|
||||
@Nullable LExecutor executor;
|
||||
GlobalVarsDialog globalsDialog = new GlobalVarsDialog();
|
||||
|
||||
public LogicDialog(){
|
||||
super("logic");
|
||||
@@ -51,7 +52,7 @@ public class LogicDialog extends BaseDialog{
|
||||
add(buttons).growX().name("canvas");
|
||||
}
|
||||
|
||||
private Color typeColor(Var s, Color color){
|
||||
public static Color typeColor(Var s, Color color){
|
||||
return color.set(
|
||||
!s.isobj ? Pal.place :
|
||||
s.objval == null ? Color.darkGray :
|
||||
@@ -65,7 +66,7 @@ public class LogicDialog extends BaseDialog{
|
||||
);
|
||||
}
|
||||
|
||||
private String typeName(Var s){
|
||||
public static String typeName(Var s){
|
||||
return
|
||||
!s.isobj ? "number" :
|
||||
s.objval == null ? "null" :
|
||||
@@ -178,6 +179,8 @@ public class LogicDialog extends BaseDialog{
|
||||
});
|
||||
|
||||
dialog.addCloseButton();
|
||||
dialog.buttons.button("@logic.globals", Icon.list, () -> globalsDialog.show()).size(210f, 64f);
|
||||
|
||||
dialog.show();
|
||||
}).name("variables").disabled(b -> executor == null || executor.vars.length == 0);
|
||||
|
||||
|
||||
@@ -57,6 +57,12 @@ public class LogicFx{
|
||||
return map.get(name);
|
||||
}
|
||||
|
||||
/** Adds an effect entry to the map. */
|
||||
public static void add(String name, EffectEntry entry){
|
||||
entry.name = name;
|
||||
map.put(name, entry);
|
||||
}
|
||||
|
||||
public static String[] all(){
|
||||
return map.orderedKeys().toArray(String.class);
|
||||
}
|
||||
|
||||
@@ -271,8 +271,9 @@ public class ContentParser{
|
||||
return new Vec3(data.getFloat("x", 0f), data.getFloat("y", 0f), data.getFloat("z", 0f));
|
||||
});
|
||||
put(Sound.class, (type, data) -> {
|
||||
var field = fieldOpt(Sounds.class, data);
|
||||
if(data.isArray()) return new RandomSound(parser.readValue(Sound[].class, data));
|
||||
|
||||
var field = fieldOpt(Sounds.class, data);
|
||||
return field != null ? field : Vars.tree.loadSound(data.asString());
|
||||
});
|
||||
put(Music.class, (type, data) -> {
|
||||
@@ -445,6 +446,17 @@ public class ContentParser{
|
||||
if(value.has("consumes") && value.get("consumes").isObject()){
|
||||
for(JsonValue child : value.get("consumes")){
|
||||
switch(child.name){
|
||||
case "remove" -> {
|
||||
String[] values = child.isString() ? new String[]{child.asString()} : child.asStringArray();
|
||||
for(String type : values){
|
||||
Class<?> consumeType = resolve("Consume" + Strings.capitalize(type), Consume.class);
|
||||
if(consumeType != Consume.class){
|
||||
block.removeConsumers(b -> consumeType.isAssignableFrom(b.getClass()));
|
||||
}else{
|
||||
Log.warn("Unknown consumer type '@' (Class: @) in consume: remove.", type, "Consume" + Strings.capitalize(type));
|
||||
}
|
||||
}
|
||||
}
|
||||
case "item" -> block.consumeItem(find(ContentType.item, child.asString()));
|
||||
case "itemCharged" -> block.consume((Consume)parser.readValue(ConsumeItemCharged.class, child));
|
||||
case "itemFlammable" -> block.consume((Consume)parser.readValue(ConsumeItemFlammable.class, child));
|
||||
|
||||
@@ -577,6 +577,10 @@ public class Administration{
|
||||
changed.run();
|
||||
}
|
||||
|
||||
public boolean isDefault(){
|
||||
return Structs.eq(get(), defaultValue);
|
||||
}
|
||||
|
||||
private static boolean debug(){
|
||||
return Config.debug.bool();
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public enum Achievement{
|
||||
issueAttackCommand,
|
||||
active100Units(SStat.maxUnitActive, 100),
|
||||
build1000Units(SStat.unitsBuilt, 1000),
|
||||
buildAllUnits(SStat.unitTypesBuilt, 30),
|
||||
buildAllUnits(SStat.unitTypesBuilt, 50),
|
||||
buildT5,
|
||||
pickupT5,
|
||||
active10Polys,
|
||||
|
||||
@@ -84,8 +84,6 @@ public class Liquid extends UnlockableContent implements Senseable{
|
||||
super.init();
|
||||
|
||||
if(gas){
|
||||
//gases can't be coolants
|
||||
coolant = false;
|
||||
//always "boils", it's a gas
|
||||
boilPoint = -1;
|
||||
//ensure no accidental global mutation
|
||||
|
||||
@@ -536,4 +536,26 @@ public class Planet extends UnlockableContent{
|
||||
}
|
||||
batch.flush(Gl.lineStrip);
|
||||
}
|
||||
|
||||
public Vec3 lookAt(Sector sector, Vec3 out){
|
||||
return out.set(sector.tile.v).rotate(Vec3.Y, -getRotation());
|
||||
}
|
||||
|
||||
public Vec3 project(Sector sector, Camera3D cam, Vec3 out){
|
||||
return cam.project(out.set(sector.tile.v).setLength(outlineRad * radius).rotate(Vec3.Y, -getRotation()).add(position));
|
||||
}
|
||||
|
||||
public void setPlane(Sector sector, PlaneBatch3D projector){
|
||||
float rotation = -getRotation();
|
||||
float length = 0.01f;
|
||||
|
||||
projector.setPlane(
|
||||
//origin on sector position
|
||||
Tmp.v33.set(sector.tile.v).setLength((outlineRad + length) * radius).rotate(Vec3.Y, rotation).add(position),
|
||||
//face up
|
||||
sector.plane.project(Tmp.v32.set(sector.tile.v).add(Vec3.Y)).sub(sector.tile.v, radius).rotate(Vec3.Y, rotation).nor(),
|
||||
//right vector
|
||||
Tmp.v31.set(Tmp.v32).rotate(Vec3.Y, -rotation).add(sector.tile.v).rotate(sector.tile.v, 90).sub(sector.tile.v).rotate(Vec3.Y, rotation).nor()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -236,7 +236,7 @@ public class Sector{
|
||||
|
||||
/** Projects this sector onto a 4-corner square for use in map gen.
|
||||
* Allocates a new object. Do not call in the main loop. */
|
||||
private SectorRect makeRect(){
|
||||
protected SectorRect makeRect(){
|
||||
Vec3[] corners = new Vec3[tile.corners.length];
|
||||
for(int i = 0; i < corners.length; i++){
|
||||
corners[i] = tile.corners[i].v.cpy().setLength(planet.radius);
|
||||
|
||||
@@ -215,6 +215,8 @@ public class UnitType extends UnlockableContent implements Senseable{
|
||||
naval = false,
|
||||
/** if false, RTS AI controlled units do not automatically attack things while moving. This is automatically assigned. */
|
||||
autoFindTarget = true,
|
||||
/** If false, 'under' blocks like conveyors are not targeted. */
|
||||
targetUnderBlocks = true,
|
||||
/** if true, this unit will always shoot while moving regardless of slowdown */
|
||||
alwaysShootWhenMoving = false,
|
||||
|
||||
@@ -960,13 +962,14 @@ public class UnitType extends UnlockableContent implements Senseable{
|
||||
getRegionsToOutline(toOutline);
|
||||
|
||||
for(var region : toOutline){
|
||||
if(region instanceof AtlasRegion atlas){
|
||||
if(region instanceof AtlasRegion atlas && !Core.atlas.has(atlas.name + "-outline")){
|
||||
String regionName = atlas.name;
|
||||
Pixmap outlined = Pixmaps.outline(Core.atlas.getPixmap(region), outlineColor, outlineRadius);
|
||||
|
||||
Drawf.checkBleed(outlined);
|
||||
|
||||
packer.add(PageType.main, regionName + "-outline", outlined);
|
||||
outlined.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1019,7 +1022,9 @@ public class UnitType extends UnlockableContent implements Senseable{
|
||||
}
|
||||
|
||||
packer.add(PageType.main, name + "-treads" + r + "-" + i, frame);
|
||||
frame.dispose();
|
||||
}
|
||||
slice.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -430,7 +430,7 @@ public class Weapon implements Cloneable{
|
||||
}
|
||||
|
||||
protected Teamc findTarget(Unit unit, float x, float y, float range, boolean air, boolean ground){
|
||||
return Units.closestTarget(unit.team, x, y, range + Math.abs(shootY), u -> u.checkTarget(air, ground), t -> ground);
|
||||
return Units.closestTarget(unit.team, x, y, range + Math.abs(shootY), u -> u.checkTarget(air, ground), t -> ground && (unit.type.targetUnderBlocks || !t.block.underBullets));
|
||||
}
|
||||
|
||||
protected boolean checkTarget(Unit unit, Teamc target, float x, float y, float range){
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package mindustry.ui.dialogs;
|
||||
|
||||
import arc.*;
|
||||
import arc.freetype.FreeTypeFontGenerator.*;
|
||||
import arc.graphics.*;
|
||||
import arc.input.*;
|
||||
import arc.math.*;
|
||||
@@ -35,6 +36,7 @@ public class JoinDialog extends BaseDialog{
|
||||
int refreshes;
|
||||
boolean showHidden;
|
||||
TextButtonStyle style;
|
||||
Task fontIgnoreDirtyTask;
|
||||
|
||||
String lastIp;
|
||||
int lastPort, lastColumns = -1;
|
||||
@@ -413,6 +415,15 @@ public class JoinDialog extends BaseDialog{
|
||||
net.pingHost(resaddress, resport, res -> {
|
||||
if(refreshes != cur) return;
|
||||
|
||||
//don't recache the texture for a while
|
||||
if(fontIgnoreDirtyTask == null){
|
||||
FreeTypeFontData.ignoreDirty = true;
|
||||
fontIgnoreDirtyTask = Time.runTask(0.6f * 60f, () -> {
|
||||
FreeTypeFontData.ignoreDirty = false;
|
||||
fontIgnoreDirtyTask = null;
|
||||
});
|
||||
}
|
||||
|
||||
if(!serverSearch.isEmpty() && !(group.name.toLowerCase().contains(serverSearch)
|
||||
|| res.name.toLowerCase().contains(serverSearch)
|
||||
|| res.description.toLowerCase().contains(serverSearch)
|
||||
|
||||
@@ -167,7 +167,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
|
||||
//show selection of Erekir/Serpulo campaign if the user has no bases, and hasn't selected yet (essentially a "have they played campaign before" check)
|
||||
shown(() -> {
|
||||
if(!settings.getBool("campaignselect") && !content.planets().contains(p -> p.sectors.contains(s -> s.hasBase()))){
|
||||
if(!settings.getBool("campaignselect") && !content.planets().contains(p -> p.sectors.contains(Sector::hasBase))){
|
||||
var diag = new BaseDialog("@campaign.select");
|
||||
|
||||
Planet[] selected = {null};
|
||||
@@ -214,7 +214,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
}
|
||||
|
||||
//unlock defaults for older campaign saves (TODO move? where to?)
|
||||
if(content.planets().contains(p -> p.sectors.contains(s -> s.hasBase())) || Blocks.scatter.unlocked() || Blocks.router.unlocked()){
|
||||
if(content.planets().contains(p -> p.sectors.contains(Sector::hasBase)) || Blocks.scatter.unlocked() || Blocks.router.unlocked()){
|
||||
Seq.with(Blocks.junction, Blocks.mechanicalDrill, Blocks.conveyor, Blocks.duo, Items.copper, Items.lead).each(UnlockableContent::quietUnlock);
|
||||
}
|
||||
}
|
||||
@@ -372,11 +372,17 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
super.show();
|
||||
}
|
||||
|
||||
void lookAt(Sector sector){
|
||||
public void lookAt(Sector sector){
|
||||
if(sector.tile == Ptile.empty) return;
|
||||
|
||||
//TODO should this even set `state.planet`? the other lookAt() doesn't, so...
|
||||
state.planet = sector.planet;
|
||||
state.camPos.set(Tmp.v33.set(sector.tile.v).rotate(Vec3.Y, -sector.planet.getRotation()));
|
||||
sector.planet.lookAt(sector, state.camPos);
|
||||
}
|
||||
|
||||
public void lookAt(Sector sector, float alpha){
|
||||
float len = state.camPos.len();
|
||||
state.camPos.slerp(sector.planet.lookAt(sector, Tmp.v33).setLength(len), alpha);
|
||||
}
|
||||
|
||||
boolean canSelect(Sector sector){
|
||||
@@ -648,10 +654,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
}
|
||||
}).visible(() -> mode != select),
|
||||
|
||||
new Table(c -> {
|
||||
expandTable = c;
|
||||
})).grow();
|
||||
|
||||
new Table(c -> expandTable = c)).grow();
|
||||
rebuildExpand();
|
||||
}
|
||||
|
||||
@@ -770,11 +773,6 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
}
|
||||
}
|
||||
|
||||
public void lookAt(Sector sector, float alpha){
|
||||
float len = state.camPos.len();
|
||||
state.camPos.slerp(Tmp.v31.set(sector.tile.v).rotate(Vec3.Y, -sector.planet.getRotation()).setLength(len), alpha);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void act(float delta){
|
||||
super.act(delta);
|
||||
@@ -801,7 +799,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
hoverLabel.touchable = Touchable.disabled;
|
||||
hoverLabel.color.a = state.uiAlpha;
|
||||
|
||||
Vec3 pos = planets.cam.project(Tmp.v31.set(hovered.tile.v).setLength(PlanetRenderer.outlineRad * state.planet.radius).rotate(Vec3.Y, -state.planet.getRotation()).add(state.planet.position));
|
||||
Vec3 pos = hovered.planet.project(hovered, planets.cam, Tmp.v31);
|
||||
hoverLabel.setPosition(pos.x - Core.scene.marginLeft, pos.y - Core.scene.marginBottom, Align.center);
|
||||
|
||||
hoverLabel.getText().setLength(0);
|
||||
|
||||
@@ -36,8 +36,6 @@ public class TraceDialog extends BaseDialog{
|
||||
c.add(Core.bundle.format("trace.ip", info.ip)).row();
|
||||
c.button(Icon.copySmall, style, () -> copy(info.uuid)).size(s).padRight(4f);
|
||||
c.add(Core.bundle.format("trace.id", info.uuid)).row();
|
||||
c.button(Icon.copySmall, style, () -> copy(player.locale)).size(s).padRight(4f);
|
||||
c.add(Core.bundle.format("trace.language", player.locale)).row();
|
||||
}).row();
|
||||
|
||||
table.add(Core.bundle.format("trace.modclient", info.modded)).row();
|
||||
|
||||
@@ -72,9 +72,9 @@ public class HudFragment{
|
||||
|
||||
Events.on(SectorCaptureEvent.class, e -> {
|
||||
if(e.sector.isBeingPlayed()){
|
||||
ui.announce(Core.bundle.format("sector.capture", ""), 5f);
|
||||
ui.announce("@sector.capture.current", 5f);
|
||||
}else{
|
||||
showToast(Core.bundle.format("sector.capture", e.sector.name() + " "));
|
||||
showToast(Core.bundle.format("sector.capture", e.sector.name()));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -219,6 +219,8 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
public int unitCapModifier = 0;
|
||||
/** Whether the block can be tapped and selected to configure. */
|
||||
public boolean configurable;
|
||||
/** If true, this block does not have pointConfig with a transform called on map resize. */
|
||||
public boolean ignoreResizeConfig;
|
||||
/** If true, this building can be selected like a unit when commanding. */
|
||||
public boolean commandable;
|
||||
/** If true, the building inventory can be shown with the config. */
|
||||
@@ -245,6 +247,8 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
public boolean allowDiagonal = true;
|
||||
/** Whether to swap the diagonal placement modes. */
|
||||
public boolean swapDiagonalPlacement;
|
||||
/** Whether to allow rectangular placement, as opposed to a line. */
|
||||
public boolean allowRectanglePlacement = false;
|
||||
/** Build queue priority in schematics. */
|
||||
public int schematicPriority = 0;
|
||||
/**
|
||||
@@ -322,6 +326,8 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
public float deconstructThreshold = 0f;
|
||||
/** If true, this block deconstructs immediately. Instant deconstruction implies no resource refund. */
|
||||
public boolean instantDeconstruct = false;
|
||||
/** If true, this block constructs immediately. This implies no resource requirement, and ignores configs - do not use, this is for performance only! */
|
||||
public boolean instantBuild = false;
|
||||
/** Effect for placing the block. Passes size as rotation. */
|
||||
public Effect placeEffect = Fx.placeBlock;
|
||||
/** Effect for breaking the block. Passes size as rotation. */
|
||||
@@ -378,7 +384,7 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
/** Dump timer ID.*/
|
||||
protected final int timerDump = timers++;
|
||||
/** How often to try dumping items in ticks, e.g. 5 = 12 times/sec*/
|
||||
protected final int dumpTime = 5;
|
||||
public int dumpTime = 5;
|
||||
|
||||
public Block(String name){
|
||||
super(name);
|
||||
@@ -952,6 +958,14 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
consumeBuilder.remove(cons);
|
||||
}
|
||||
|
||||
public void removeConsumers(Boolf<Consume> b){
|
||||
consumeBuilder.removeAll(b);
|
||||
//the power was removed, unassign it
|
||||
if(!consumeBuilder.contains(c -> c instanceof ConsumePower)){
|
||||
consPower = null;
|
||||
}
|
||||
}
|
||||
|
||||
public ConsumeLiquid consumeLiquid(Liquid liquid, float amount){
|
||||
return consume(new ConsumeLiquid(liquid, amount));
|
||||
}
|
||||
@@ -1280,6 +1294,8 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
}
|
||||
}
|
||||
|
||||
Seq<Pixmap> toDispose = new Seq<>();
|
||||
|
||||
//generate paletted team regions
|
||||
if(teamRegion != null && teamRegion.found()){
|
||||
for(Team team : Team.all){
|
||||
@@ -1304,6 +1320,7 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
Drawf.checkBleed(out);
|
||||
|
||||
packer.add(PageType.main, name + "-team-" + team.name, out);
|
||||
toDispose.add(out);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1323,6 +1340,7 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
Pixmap out = last = Pixmaps.outline(region, outlineColor, outlineRadius);
|
||||
Drawf.checkBleed(out);
|
||||
packer.add(PageType.main, atlasRegion.name, out);
|
||||
toDispose.add(out);
|
||||
}
|
||||
|
||||
var toOutline = new Seq<TextureRegion>();
|
||||
@@ -1336,6 +1354,7 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
Drawf.checkBleed(outlined);
|
||||
|
||||
packer.add(PageType.main, regionName + "-outline", outlined);
|
||||
toDispose.add(outlined);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1353,12 +1372,15 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
packer.add(PageType.main, "block-" + name + "-full", base);
|
||||
|
||||
editorBase = new PixmapRegion(base);
|
||||
toDispose.add(base);
|
||||
}else{
|
||||
if(gen[0] != null) packer.add(PageType.main, "block-" + name + "-full", Core.atlas.getPixmap(gen[0]));
|
||||
editorBase = gen[0] == null ? Core.atlas.getPixmap(fullIcon) : Core.atlas.getPixmap(gen[0]);
|
||||
}
|
||||
|
||||
packer.add(PageType.editor, name + "-icon-editor", editorBase);
|
||||
|
||||
toDispose.each(Pixmap::dispose);
|
||||
}
|
||||
|
||||
public int planRotation(int rot){
|
||||
|
||||
@@ -50,6 +50,7 @@ public class Build{
|
||||
if(tile.build != null){
|
||||
prevBuild.add(tile.build);
|
||||
tile.build.onDeconstructed(unit);
|
||||
tile.build.dead = true;
|
||||
}
|
||||
|
||||
tile.setBlock(sub, team, rotation);
|
||||
@@ -122,6 +123,14 @@ public class Build{
|
||||
}
|
||||
});
|
||||
|
||||
//complete it immediately
|
||||
if(result.instantBuild){
|
||||
Events.fire(new BlockBuildBeginEvent(tile, team, unit, false));
|
||||
result.placeBegan(tile, tile.block, unit);
|
||||
ConstructBlock.constructFinish(tile, result, unit, (byte)rotation, team, null);
|
||||
return;
|
||||
}
|
||||
|
||||
Block previous = tile.block();
|
||||
Block sub = ConstructBlock.get(result.size);
|
||||
var prevBuild = new Seq<Building>(9);
|
||||
@@ -190,6 +199,11 @@ public class Build{
|
||||
|
||||
if(tile == null) return false;
|
||||
|
||||
//floors have different checks
|
||||
if(type.isFloor()){
|
||||
return type.isOverlay() ? tile.overlay() != type : tile.floor() != type;
|
||||
}
|
||||
|
||||
//campaign darkness check
|
||||
if(world.getDarkness(x, y) >= 3){
|
||||
return false;
|
||||
@@ -199,10 +213,6 @@ public class Build{
|
||||
return false;
|
||||
}
|
||||
|
||||
if((type.isFloor() && tile.floor() == type) || (type.isOverlay() && tile.overlay() == type)){
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!type.canPlaceOn(tile, team, rotation)){
|
||||
return false;
|
||||
}
|
||||
@@ -223,7 +233,7 @@ public class Build{
|
||||
(check.floor().isDeep() && !type.floating && !type.requiresWater && !type.placeableLiquid) || //deep water
|
||||
(type == check.block() && check.build != null && rotation == check.build.rotation && type.rotate && !((type == check.block && check.team() == Team.derelict))) || //same block, same rotation
|
||||
!check.interactable(team) || //cannot interact
|
||||
!check.floor().placeableOn || //solid wall
|
||||
!check.floor().placeableOn || //solid floor
|
||||
(!checkVisible && !check.block().alwaysReplace) || //replacing a block that should be replaced (e.g. payload placement)
|
||||
!(((type.canReplace(check.block()) || (type == check.block && state.rules.derelictRepair && check.team() == Team.derelict)) || //can replace type OR can replace derelict block of same type
|
||||
(check.build instanceof ConstructBuild build && build.current == type && check.centerX() == tile.x && check.centerY() == tile.y)) && //same type in construction
|
||||
@@ -281,9 +291,9 @@ public class Build{
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Returns whether the tile at this position is breakable by this team */
|
||||
/** @return whether the tile at this position is breakable by this team */
|
||||
public static boolean validBreak(Team team, int x, int y){
|
||||
Tile tile = world.tile(x, y);
|
||||
return tile != null && tile.block().canBreak(tile) && tile.breakable() && tile.interactable(team);
|
||||
return tile != null && tile.block() != Blocks.air && (tile.block().canBreak(tile) && (tile.breakable() || state.rules.allowEnvironmentDeconstruct)) && tile.interactable(team);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,15 +75,13 @@ public class ConstructBlock extends Block{
|
||||
|
||||
if(block instanceof OverlayFloor overlay){
|
||||
tile.setOverlay(overlay);
|
||||
tile.setBlock(Blocks.air);
|
||||
}else if(block instanceof Floor floor){
|
||||
tile.setFloorUnder(floor);
|
||||
tile.setBlock(Blocks.air);
|
||||
}else{
|
||||
tile.setBlock(block, team, rotation);
|
||||
}
|
||||
|
||||
if(tile.build != null){
|
||||
if(tile.build != null && tile.build.block == block){
|
||||
tile.build.health = block.health * healthf;
|
||||
|
||||
if(config != null){
|
||||
@@ -100,11 +98,11 @@ public class ConstructBlock extends Block{
|
||||
|
||||
//make sure block indexer knows it's damaged
|
||||
indexer.notifyHealthChanged(tile.build);
|
||||
}
|
||||
|
||||
//last builder was this local client player, call placed()
|
||||
if(tile.build != null && !headless && builder == player.unit()){
|
||||
tile.build.playerPlaced(config);
|
||||
//last builder was this local client player, call placed()
|
||||
if(!headless && builder == player.unit()){
|
||||
tile.build.playerPlaced(config);
|
||||
}
|
||||
}
|
||||
|
||||
if(fogControl.isVisibleTile(team, tile.x, tile.y)){
|
||||
|
||||
@@ -29,6 +29,7 @@ public class StackConveyor extends Block implements Autotiler{
|
||||
public @Load("@-stack") TextureRegion stackRegion;
|
||||
/** requires power to work properly */
|
||||
public @Load(value = "@-glow") TextureRegion glowRegion;
|
||||
public @Load(value = "@-edge-glow", fallback = "@-glow") TextureRegion edgeGlowRegion;
|
||||
|
||||
public float glowAlpha = 1f;
|
||||
public Color glowColor = Pal.redLight;
|
||||
@@ -154,7 +155,7 @@ public class StackConveyor extends Block implements Autotiler{
|
||||
Draw.z(Layer.blockAdditive);
|
||||
Draw.color(glowColor, glowAlpha * power.status);
|
||||
Draw.blend(Blending.additive);
|
||||
Draw.rect(glowRegion, x, y, rotation * 90);
|
||||
Draw.rect(state == stateLoad ? edgeGlowRegion : glowRegion, x, y, rotation * 90);
|
||||
Draw.blend();
|
||||
Draw.color();
|
||||
Draw.z(Layer.block - 0.1f);
|
||||
|
||||
@@ -77,19 +77,22 @@ public class Floor extends Block{
|
||||
public int blendId = -1;
|
||||
|
||||
protected TextureRegion[][] edges;
|
||||
protected Seq<Block> blenders = new Seq<>();
|
||||
protected Seq<Floor> blenders = new Seq<>();
|
||||
protected Bits blended = new Bits(256);
|
||||
protected int[] dirs = new int[8];
|
||||
protected TextureRegion edgeRegion;
|
||||
|
||||
public Floor(String name){
|
||||
super(name);
|
||||
variants = 3;
|
||||
this(name, 3);
|
||||
}
|
||||
|
||||
public Floor(String name, int variants){
|
||||
super(name);
|
||||
this.variants = variants;
|
||||
placeableLiquid = true;
|
||||
allowRectanglePlacement = true;
|
||||
instantBuild = true;
|
||||
placeEffect = Fx.rotateBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -174,18 +177,23 @@ public class Floor extends Block{
|
||||
}
|
||||
|
||||
packer.add(PageType.environment, name + "-edge", result);
|
||||
result.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawBase(Tile tile){
|
||||
Mathf.rand.setSeed(tile.pos());
|
||||
Draw.rect(variantRegions[Mathf.randomSeed(tile.pos(), 0, Math.max(0, variantRegions.length - 1))], tile.worldx(), tile.worldy());
|
||||
Draw.rect(variantRegions[variant(tile.x, tile.y)], tile.worldx(), tile.worldy());
|
||||
|
||||
Draw.alpha(1f);
|
||||
drawEdges(tile);
|
||||
drawOverlay(tile);
|
||||
}
|
||||
|
||||
public int variant(int x, int y){
|
||||
return Mathf.randomSeed(Point2.pack(x, y), 0, Math.max(0, variantRegions.length - 1));
|
||||
}
|
||||
|
||||
public void drawOverlay(Tile tile){
|
||||
Floor floor = tile.overlay();
|
||||
if(floor != Blocks.air && floor != this){
|
||||
@@ -233,8 +241,8 @@ public class Floor extends Block{
|
||||
for(int i = 0; i < 8; i++){
|
||||
Point2 point = Geometry.d8[i];
|
||||
Tile other = tile.nearby(point);
|
||||
//special case: empty is, well, empty, so never draw emptyness on top, as that would just be an incorrect black texture
|
||||
if(other != null && other.floor().cacheLayer == layer && other.floor().edges() != null && other.floor() != Blocks.empty){
|
||||
//special case: empty is, well, empty, so never draw emptiness on top, as that would just be an incorrect black texture
|
||||
if(other != null && other.floor().cacheLayer == layer && other.floor().edges(tile.x, tile.y) != null && other.floor() != Blocks.empty){
|
||||
if(!blended.getAndSet(other.floor().id)){
|
||||
blenders.add(other.floor());
|
||||
dirs[i] = other.floorID();
|
||||
@@ -255,8 +263,7 @@ public class Floor extends Block{
|
||||
Point2 point = Geometry.d8[i];
|
||||
Tile other = tile.nearby(point);
|
||||
|
||||
if(other != null && doEdge(tile, other, other.floor()) && other.floor().cacheLayer == realCache && other.floor().edges() != null && other.floor() != Blocks.empty){
|
||||
|
||||
if(other != null && doEdge(tile, other, other.floor()) && other.floor().cacheLayer == realCache && other.floor().edges(tile.x, tile.y) != null && other.floor() != Blocks.empty){
|
||||
if(!blended.getAndSet(other.floor().id)){
|
||||
blenders.add(other.floor());
|
||||
}
|
||||
@@ -270,12 +277,12 @@ public class Floor extends Block{
|
||||
protected void drawBlended(Tile tile, boolean checkId){
|
||||
blenders.sort(a -> a.id);
|
||||
|
||||
for(Block block : blenders){
|
||||
for(Floor block : blenders){
|
||||
for(int i = 0; i < 8; i++){
|
||||
Point2 point = Geometry.d8[i];
|
||||
Tile other = tile.nearby(point);
|
||||
if(other != null && other.floor() == block && (!checkId || dirs[i] == block.id)){
|
||||
TextureRegion region = edge((Floor)block, 1 - point.x, 1 - point.y);
|
||||
TextureRegion region = block.edge(tile.x, tile.y, 1 - point.x, 1 - point.y);
|
||||
Draw.rect(region, tile.worldx(), tile.worldy());
|
||||
}
|
||||
}
|
||||
@@ -302,17 +309,23 @@ public class Floor extends Block{
|
||||
return blendId;
|
||||
}
|
||||
|
||||
/** Returns the edge array that should be used to draw at the specified tile position. */
|
||||
protected TextureRegion[][] edges(int x, int y){
|
||||
return blendGroup.asFloor().edges;
|
||||
}
|
||||
|
||||
protected TextureRegion edge(int x, int y, int rx, int ry){
|
||||
return edges(x, y)[rx][2 - ry];
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
protected TextureRegion[][] edges(){
|
||||
return ((Floor)blendGroup).edges;
|
||||
return edges(0, 0);
|
||||
}
|
||||
|
||||
/** @return whether the edges from {@param other} should be drawn onto this tile **/
|
||||
protected boolean doEdge(Tile tile, Tile otherTile, Floor other){
|
||||
return (other.realBlendId(otherTile) > realBlendId(tile) || edges() == null);
|
||||
}
|
||||
|
||||
TextureRegion edge(Floor block, int x, int y){
|
||||
return block.edges()[x][2 - y];
|
||||
return (other.realBlendId(otherTile) > realBlendId(tile) || edges(tile.x, tile.y) == null);
|
||||
}
|
||||
|
||||
public static class UpdateRenderState{
|
||||
@@ -325,5 +338,4 @@ public class Floor extends Block{
|
||||
this.floor = floor;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -69,6 +69,8 @@ public class OreBlock extends OverlayFloor{
|
||||
packer.add(PageType.editor, "editor-block-" + name + "-full", image);
|
||||
packer.add(PageType.main, "block-" + name + "-full", image);
|
||||
}
|
||||
|
||||
image.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ public class StaticWall extends Prop{
|
||||
solid = true;
|
||||
variants = 2;
|
||||
cacheLayer = CacheLayer.walls;
|
||||
allowRectanglePlacement = true;
|
||||
instantBuild = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -45,6 +45,7 @@ public class LogicBlock extends Block{
|
||||
configurable = true;
|
||||
group = BlockGroup.logic;
|
||||
schematicPriority = 5;
|
||||
ignoreResizeConfig = true;
|
||||
|
||||
//universal, no real requirements
|
||||
envEnabled = Env.any;
|
||||
@@ -354,8 +355,6 @@ public class LogicBlock extends Block{
|
||||
}
|
||||
}
|
||||
|
||||
asm.putConst("@mapw", world.width());
|
||||
asm.putConst("@maph", world.height());
|
||||
asm.putConst("@links", executor.links.length);
|
||||
asm.putConst("@ipt", instructionsPerTick);
|
||||
|
||||
|
||||
@@ -4,8 +4,10 @@ import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.graphics.gl.*;
|
||||
import arc.math.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
@@ -29,7 +31,15 @@ public class LogicDisplay extends Block{
|
||||
commandTriangle = 9,
|
||||
commandImage = 10,
|
||||
//note that this command actually only draws 1 character, unpacked in instruction
|
||||
commandPrint = 11;
|
||||
commandPrint = 11,
|
||||
|
||||
commandTranslate = 12,
|
||||
commandScale = 13,
|
||||
commandRotate = 14,
|
||||
commandResetTransform = 15
|
||||
;
|
||||
|
||||
public static final float scaleStep = 0.05f;
|
||||
|
||||
public int maxSides = 25;
|
||||
|
||||
@@ -58,6 +68,7 @@ public class LogicDisplay extends Block{
|
||||
public float color = Color.whiteFloatBits;
|
||||
public float stroke = 1f;
|
||||
public LongQueue commands = new LongQueue(256);
|
||||
public @Nullable Mat transform;
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
@@ -79,14 +90,18 @@ public class LogicDisplay extends Block{
|
||||
if(!commands.isEmpty()){
|
||||
Draw.draw(Draw.z(), () -> {
|
||||
Tmp.m1.set(Draw.proj());
|
||||
Tmp.m2.set(Draw.trans());
|
||||
Draw.proj(0, 0, displaySize, displaySize);
|
||||
if(transform != null){
|
||||
Draw.trans(transform);
|
||||
}
|
||||
buffer.begin();
|
||||
Draw.color(color);
|
||||
Lines.stroke(stroke);
|
||||
|
||||
while(!commands.isEmpty()){
|
||||
long c = commands.removeFirst();
|
||||
byte type = DisplayCmd.type(c);
|
||||
int type = DisplayCmd.type(c);
|
||||
int x = unpackSign(DisplayCmd.x(c)), y = unpackSign(DisplayCmd.y(c)),
|
||||
p1 = unpackSign(DisplayCmd.p1(c)), p2 = unpackSign(DisplayCmd.p2(c)), p3 = unpackSign(DisplayCmd.p3(c)), p4 = unpackSign(DisplayCmd.p4(c));
|
||||
|
||||
@@ -117,11 +132,16 @@ public class LogicDisplay extends Block{
|
||||
Draw.rect(Tmp.tr1, x + Tmp.tr1.width/2f + glyph.xoffset, y + Tmp.tr1.height/2f + glyph.yoffset + Fonts.logic.getData().capHeight + Fonts.logic.getData().ascent, Tmp.tr1.width, Tmp.tr1.height);
|
||||
}
|
||||
}
|
||||
case commandTranslate -> Draw.trans((transform == null ? (transform = new Mat()) : transform).translate(x, y));
|
||||
case commandScale -> Draw.trans((transform == null ? (transform = new Mat()) : transform).scale(x * scaleStep, y * scaleStep));
|
||||
case commandRotate-> Draw.trans((transform == null ? (transform = new Mat()) : transform).rotate(p1));
|
||||
case commandResetTransform -> Draw.trans((transform == null ? (transform = new Mat()) : transform).idt());
|
||||
}
|
||||
}
|
||||
|
||||
buffer.end();
|
||||
Draw.proj(Tmp.m1);
|
||||
Draw.trans(Tmp.m2);
|
||||
Draw.reset();
|
||||
});
|
||||
}
|
||||
@@ -135,6 +155,41 @@ public class LogicDisplay extends Block{
|
||||
Draw.blend();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte version(){
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Writes write){
|
||||
super.write(write);
|
||||
|
||||
if(transform != null){
|
||||
write.bool(true);
|
||||
for(int i = 0; i < transform.val.length; i++){
|
||||
write.f(transform.val[i]);
|
||||
}
|
||||
}else{
|
||||
write.bool(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(Reads read, byte revision){
|
||||
super.read(read, revision);
|
||||
|
||||
if(revision >= 1){
|
||||
boolean hasTransform = read.bool();
|
||||
if(hasTransform){
|
||||
transform = new Mat();
|
||||
|
||||
for(int i = 0; i < transform.val.length; i++){
|
||||
transform.val[i] = read.f();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(){
|
||||
super.remove();
|
||||
@@ -163,7 +218,12 @@ public class LogicDisplay extends Block{
|
||||
triangle,
|
||||
image,
|
||||
//note that this command actually only draws 1 character, unpacked in instruction
|
||||
print;
|
||||
print,
|
||||
translate,
|
||||
scale,
|
||||
rotate,
|
||||
reset
|
||||
;
|
||||
|
||||
public static final GraphicsType[] all = values();
|
||||
}
|
||||
@@ -171,7 +231,7 @@ public class LogicDisplay extends Block{
|
||||
@Struct
|
||||
static class DisplayCmdStruct{
|
||||
@StructField(4)
|
||||
public byte type;
|
||||
public int type;
|
||||
|
||||
//at least 9 bits are required for full 360 degrees
|
||||
@StructField(10)
|
||||
|
||||
@@ -42,6 +42,11 @@ public class SwitchBlock extends Block{
|
||||
if(privileged) return;
|
||||
super.damage(damage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canPickup(){
|
||||
return !privileged;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean collide(Bullet other){
|
||||
|
||||
@@ -46,6 +46,10 @@ public class PowerGenerator extends PowerDistributor{
|
||||
flags = EnumSet.of(BlockFlag.generator);
|
||||
}
|
||||
|
||||
public float getDisplayedPowerProduction(){
|
||||
return powerProduction;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureRegion[] icons(){
|
||||
return drawer.finalIcons(this);
|
||||
|
||||
@@ -26,6 +26,11 @@ public class ThermalGenerator extends PowerGenerator{
|
||||
noUpdateDisabled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDisplayedPowerProduction(){
|
||||
return powerProduction / displayEfficiencyScale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
if(outputLiquid != null){
|
||||
|
||||
@@ -22,6 +22,7 @@ public class DrawTurret extends DrawBlock{
|
||||
public String basePrefix = "";
|
||||
/** Overrides the liquid to draw in the liquid region. */
|
||||
public @Nullable Liquid liquidDraw;
|
||||
public float turretLayer = Layer.turret, shadowLayer = Layer.turret - 0.5f, heatLayer = Layer.turretHeat;
|
||||
public TextureRegion base, liquid, top, heat, preview, outline;
|
||||
|
||||
public DrawTurret(String basePrefix){
|
||||
@@ -52,11 +53,11 @@ public class DrawTurret extends DrawBlock{
|
||||
Draw.rect(base, build.x, build.y);
|
||||
Draw.color();
|
||||
|
||||
Draw.z(Layer.turret - 0.5f);
|
||||
Draw.z(shadowLayer);
|
||||
|
||||
Drawf.shadow(preview, build.x + tb.recoilOffset.x - turret.elevation, build.y + tb.recoilOffset.y - turret.elevation, tb.drawrot());
|
||||
|
||||
Draw.z(Layer.turret);
|
||||
Draw.z(turretLayer);
|
||||
|
||||
drawTurret(turret, tb);
|
||||
drawHeat(turret, tb);
|
||||
@@ -64,9 +65,9 @@ public class DrawTurret extends DrawBlock{
|
||||
if(parts.size > 0){
|
||||
if(outline.found()){
|
||||
//draw outline under everything when parts are involved
|
||||
Draw.z(Layer.turret - 0.01f);
|
||||
Draw.z(turretLayer - 0.01f);
|
||||
Draw.rect(outline, build.x + tb.recoilOffset.x, build.y + tb.recoilOffset.y, tb.drawrot());
|
||||
Draw.z(Layer.turret);
|
||||
Draw.z(turretLayer);
|
||||
}
|
||||
|
||||
float progress = tb.progress();
|
||||
@@ -99,7 +100,7 @@ public class DrawTurret extends DrawBlock{
|
||||
public void drawHeat(Turret block, TurretBuild build){
|
||||
if(build.heat <= 0.00001f || !heat.found()) return;
|
||||
|
||||
Drawf.additive(heat, block.heatColor.write(Tmp.c1).a(build.heat), build.x + build.recoilOffset.x, build.y + build.recoilOffset.y, build.drawrot(), Layer.turretHeat);
|
||||
Drawf.additive(heat, block.heatColor.write(Tmp.c1).a(build.heat), build.x + build.recoilOffset.x, build.y + build.recoilOffset.y, build.drawrot(), heatLayer);
|
||||
}
|
||||
|
||||
/** Load any relevant texture regions. */
|
||||
|
||||
Reference in New Issue
Block a user