Partial 7.0 merge - API preview
This commit is contained in:
@@ -7,27 +7,31 @@ import mindustry.world.meta.*;
|
||||
import java.util.*;
|
||||
|
||||
public class Attributes implements JsonSerializable{
|
||||
private final float[] arr = new float[Attribute.all.length];
|
||||
private float[] arr = new float[Attribute.all.length];
|
||||
|
||||
public void clear(){
|
||||
Arrays.fill(arr, 0);
|
||||
}
|
||||
|
||||
public float get(Attribute attr){
|
||||
return arr[attr.ordinal()];
|
||||
check();
|
||||
return arr[attr.id];
|
||||
}
|
||||
|
||||
public void set(Attribute attr, float value){
|
||||
arr[attr.ordinal()] = value;
|
||||
check();
|
||||
arr[attr.id] = value;
|
||||
}
|
||||
|
||||
public void add(Attributes other){
|
||||
check();
|
||||
for(int i = 0; i < arr.length; i++){
|
||||
arr[i] += other.arr[i];
|
||||
}
|
||||
}
|
||||
|
||||
public void add(Attributes other, float scl){
|
||||
check();
|
||||
for(int i = 0; i < arr.length; i++){
|
||||
arr[i] += other.arr[i] * scl;
|
||||
}
|
||||
@@ -35,17 +39,23 @@ public class Attributes implements JsonSerializable{
|
||||
|
||||
@Override
|
||||
public void write(Json json){
|
||||
check();
|
||||
for(Attribute at : Attribute.all){
|
||||
if(arr[at.ordinal()] != 0){
|
||||
json.writeValue(at.name(), arr[at.ordinal()]);
|
||||
if(arr[at.id] != 0){
|
||||
json.writeValue(at.name, arr[at.id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(Json json, JsonValue data){
|
||||
check();
|
||||
for(Attribute at : Attribute.all){
|
||||
arr[at.ordinal()] = data.getFloat(at.name(), 0);
|
||||
arr[at.id] = data.getFloat(at.name, 0);
|
||||
}
|
||||
}
|
||||
|
||||
private void check(){
|
||||
if(arr.length != Attribute.all.length) arr = new float[Attribute.all.length];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ import mindustry.game.EventType.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.logic.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.storage.CoreBlock.*;
|
||||
import mindustry.world.modules.*;
|
||||
@@ -54,7 +54,7 @@ public class ConstructBlock extends Block{
|
||||
@Remote(called = Loc.server)
|
||||
public static void deconstructFinish(Tile tile, Block block, Unit builder){
|
||||
Team team = tile.team();
|
||||
Fx.breakBlock.at(tile.drawx(), tile.drawy(), block.size);
|
||||
block.breakEffect.at(tile.drawx(), tile.drawy(), block.size, block.mapColor);
|
||||
Events.fire(new BlockBuildEndEvent(tile, builder, team, true, null));
|
||||
tile.remove();
|
||||
if(shouldPlay()) Sounds.breaks.at(tile, calcPitch(false));
|
||||
@@ -133,43 +133,36 @@ public class ConstructBlock extends Block{
|
||||
}
|
||||
|
||||
public class ConstructBuild extends Building{
|
||||
/**
|
||||
* The recipe of the block that is being constructed.
|
||||
* If there is no recipe for this block, as is the case with rocks, 'previous' is used.
|
||||
*/
|
||||
public @Nullable Block cblock;
|
||||
/** The recipe of the block that is being (de)constructed. Never null. */
|
||||
public Block current = Blocks.air;
|
||||
/** The block that used to be here. Never null. */
|
||||
public Block previous = Blocks.air;
|
||||
/** Buildings that previously occupied this location. */
|
||||
public @Nullable Seq<Building> prevBuild;
|
||||
|
||||
public float progress = 0;
|
||||
public float buildCost;
|
||||
/**
|
||||
* The block that used to be here.
|
||||
* If a non-recipe block is being deconstructed, this is the block that is being deconstructed.
|
||||
*/
|
||||
public Block previous;
|
||||
public @Nullable Object lastConfig;
|
||||
public @Nullable Unit lastBuilder;
|
||||
public boolean wasConstructing, activeDeconstruct;
|
||||
public float constructColor;
|
||||
|
||||
@Nullable
|
||||
public Unit lastBuilder;
|
||||
|
||||
private float[] accumulator;
|
||||
private float[] totalAccumulator;
|
||||
|
||||
@Override
|
||||
public String getDisplayName(){
|
||||
return Core.bundle.format("block.constructing", cblock == null ? previous.localizedName : cblock.localizedName);
|
||||
return Core.bundle.format("block.constructing", current.localizedName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureRegion getDisplayIcon(){
|
||||
return (cblock == null ? previous : cblock).icon(Cicon.full);
|
||||
return current.fullIcon;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkSolid(){
|
||||
return (cblock != null && cblock.solid) || previous == null || previous.solid;
|
||||
return current.solid || previous.solid;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -180,14 +173,20 @@ public class ConstructBlock extends Block{
|
||||
@Override
|
||||
public void tapped(){
|
||||
//if the target is constructable, begin constructing
|
||||
if(cblock != null){
|
||||
if(current.isPlaceable()){
|
||||
if(control.input.buildWasAutoPaused && !control.input.isBuilding && player.isBuilder()){
|
||||
control.input.isBuilding = true;
|
||||
}
|
||||
player.unit().addBuild(new BuildPlan(tile.x, tile.y, rotation, cblock, lastConfig), false);
|
||||
player.unit().addBuild(new BuildPlan(tile.x, tile.y, rotation, current, lastConfig), false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public double sense(LAccess sensor){
|
||||
if(sensor == LAccess.progress) return Mathf.clamp(progress);
|
||||
return super.sense(sensor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyed(){
|
||||
Fx.blockExplosionSmoke.at(tile);
|
||||
@@ -199,29 +198,33 @@ public class ConstructBlock extends Block{
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
//auto-remove air blocks
|
||||
if(current == Blocks.air){
|
||||
remove();
|
||||
}
|
||||
|
||||
constructColor = Mathf.lerpDelta(constructColor, activeDeconstruct ? 1f : 0f, 0.2f);
|
||||
activeDeconstruct = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
if(!(previous == null || cblock == null || previous == cblock) && Core.atlas.isFound(previous.icon(Cicon.full))){
|
||||
Draw.rect(previous.icon(Cicon.full), x, y, previous.rotate ? rotdeg() : 0);
|
||||
//do not draw air
|
||||
if(current == Blocks.air) return;
|
||||
|
||||
if(previous != current && previous != Blocks.air && previous.fullIcon.found()){
|
||||
Draw.rect(previous.fullIcon, x, y, previous.rotate ? rotdeg() : 0);
|
||||
}
|
||||
|
||||
Draw.draw(Layer.blockBuilding, () -> {
|
||||
Draw.color(Pal.accent, Pal.remove, constructColor);
|
||||
|
||||
Block target = cblock == null ? previous : cblock;
|
||||
for(TextureRegion region : current.getGeneratedIcons()){
|
||||
Shaders.blockbuild.region = region;
|
||||
Shaders.blockbuild.progress = progress;
|
||||
|
||||
if(target != null){
|
||||
for(TextureRegion region : target.getGeneratedIcons()){
|
||||
Shaders.blockbuild.region = region;
|
||||
Shaders.blockbuild.progress = progress;
|
||||
|
||||
Draw.rect(region, x, y, target.rotate ? rotdeg() : 0);
|
||||
Draw.flush();
|
||||
}
|
||||
Draw.rect(region, x, y, current.rotate ? rotdeg() : 0);
|
||||
Draw.flush();
|
||||
}
|
||||
|
||||
Draw.color();
|
||||
@@ -231,10 +234,6 @@ public class ConstructBlock extends Block{
|
||||
public void construct(Unit builder, @Nullable Building core, float amount, Object config){
|
||||
wasConstructing = true;
|
||||
activeDeconstruct = false;
|
||||
if(cblock == null){
|
||||
kill();
|
||||
return;
|
||||
}
|
||||
|
||||
if(builder.isPlayer()){
|
||||
lastBuilder = builder;
|
||||
@@ -242,14 +241,14 @@ public class ConstructBlock extends Block{
|
||||
|
||||
lastConfig = config;
|
||||
|
||||
if(cblock.requirements.length != accumulator.length || totalAccumulator.length != cblock.requirements.length){
|
||||
setConstruct(previous, cblock);
|
||||
if(current.requirements.length != accumulator.length || totalAccumulator.length != current.requirements.length){
|
||||
setConstruct(previous, current);
|
||||
}
|
||||
|
||||
float maxProgress = core == null || team.rules().infiniteResources ? amount : checkRequired(core.items, amount, false);
|
||||
|
||||
for(int i = 0; i < cblock.requirements.length; i++){
|
||||
int reqamount = Math.round(state.rules.buildCostMultiplier * cblock.requirements[i].amount);
|
||||
for(int i = 0; i < current.requirements.length; i++){
|
||||
int reqamount = Math.round(state.rules.buildCostMultiplier * current.requirements[i].amount);
|
||||
accumulator[i] += Math.min(reqamount * maxProgress, reqamount - totalAccumulator[i] + 0.00001f); //add min amount progressed to the accumulator
|
||||
totalAccumulator[i] = Math.min(totalAccumulator[i] + reqamount * maxProgress, reqamount);
|
||||
}
|
||||
@@ -260,16 +259,17 @@ public class ConstructBlock extends Block{
|
||||
|
||||
if(progress >= 1f || state.rules.infiniteResources){
|
||||
if(lastBuilder == null) lastBuilder = builder;
|
||||
constructed(tile, cblock, lastBuilder, (byte)rotation, builder.team, config);
|
||||
constructed(tile, current, lastBuilder, (byte)rotation, builder.team, config);
|
||||
}
|
||||
}
|
||||
|
||||
public void deconstruct(Unit builder, @Nullable Building core, float amount){
|
||||
public void deconstruct(Unit builder, @Nullable CoreBuild core, float amount){
|
||||
//reset accumulated resources when switching modes
|
||||
if(wasConstructing){
|
||||
Arrays.fill(accumulator, 0);
|
||||
Arrays.fill(totalAccumulator, 0);
|
||||
}
|
||||
|
||||
wasConstructing = false;
|
||||
activeDeconstruct = true;
|
||||
float deconstructMultiplier = state.rules.deconstructRefundMultiplier;
|
||||
@@ -278,55 +278,53 @@ public class ConstructBlock extends Block{
|
||||
lastBuilder = builder;
|
||||
}
|
||||
|
||||
if(cblock != null){
|
||||
ItemStack[] requirements = cblock.requirements;
|
||||
if(requirements.length != accumulator.length || totalAccumulator.length != requirements.length){
|
||||
setDeconstruct(cblock);
|
||||
}
|
||||
ItemStack[] requirements = current.requirements;
|
||||
if(requirements.length != accumulator.length || totalAccumulator.length != requirements.length){
|
||||
setDeconstruct(current);
|
||||
}
|
||||
|
||||
//make sure you take into account that you can't deconstruct more than there is deconstructed
|
||||
float clampedAmount = Math.min(amount, progress);
|
||||
//make sure you take into account that you can't deconstruct more than there is deconstructed
|
||||
float clampedAmount = Math.min(amount, progress);
|
||||
|
||||
for(int i = 0; i < requirements.length; i++){
|
||||
int reqamount = Math.round(state.rules.buildCostMultiplier * requirements[i].amount);
|
||||
accumulator[i] += Math.min(clampedAmount * deconstructMultiplier * reqamount, deconstructMultiplier * reqamount - totalAccumulator[i]); //add scaled amount progressed to the accumulator
|
||||
totalAccumulator[i] = Math.min(totalAccumulator[i] + reqamount * clampedAmount * deconstructMultiplier, reqamount);
|
||||
for(int i = 0; i < requirements.length; i++){
|
||||
int reqamount = Math.round(state.rules.buildCostMultiplier * requirements[i].amount);
|
||||
accumulator[i] += Math.min(clampedAmount * deconstructMultiplier * reqamount, deconstructMultiplier * reqamount - totalAccumulator[i]); //add scaled amount progressed to the accumulator
|
||||
totalAccumulator[i] = Math.min(totalAccumulator[i] + reqamount * clampedAmount * deconstructMultiplier, reqamount);
|
||||
|
||||
int accumulated = (int)(accumulator[i]); //get amount
|
||||
int accumulated = (int)(accumulator[i]); //get amount
|
||||
|
||||
if(clampedAmount > 0 && accumulated > 0){ //if it's positive, add it to the core
|
||||
if(core != null && requirements[i].item.unlockedNow()){ //only accept items that are unlocked
|
||||
int accepting = Math.min(accumulated, ((CoreBuild)core).storageCapacity - core.items.get(requirements[i].item));
|
||||
//transfer items directly, as this is not production.
|
||||
core.items.add(requirements[i].item, accepting);
|
||||
accumulator[i] -= accepting;
|
||||
}else{
|
||||
accumulator[i] -= accumulated;
|
||||
}
|
||||
if(clampedAmount > 0 && accumulated > 0){ //if it's positive, add it to the core
|
||||
if(core != null && requirements[i].item.unlockedNow()){ //only accept items that are unlocked
|
||||
int accepting = Math.min(accumulated, core.storageCapacity - core.items.get(requirements[i].item));
|
||||
//transfer items directly, as this is not production.
|
||||
core.items.add(requirements[i].item, accepting);
|
||||
accumulator[i] -= accepting;
|
||||
}else{
|
||||
accumulator[i] -= accumulated;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
progress = Mathf.clamp(progress - amount);
|
||||
|
||||
if(progress <= (previous == null ? 0 : previous.deconstructThreshold) || state.rules.infiniteResources){
|
||||
if(progress <= current.deconstructThreshold || state.rules.infiniteResources){
|
||||
if(lastBuilder == null) lastBuilder = builder;
|
||||
Call.deconstructFinish(tile, this.cblock == null ? previous : this.cblock, lastBuilder);
|
||||
Call.deconstructFinish(tile, this.current, lastBuilder);
|
||||
}
|
||||
}
|
||||
|
||||
private float checkRequired(ItemModule inventory, float amount, boolean remove){
|
||||
float maxProgress = amount;
|
||||
|
||||
for(int i = 0; i < cblock.requirements.length; i++){
|
||||
int sclamount = Math.round(state.rules.buildCostMultiplier * cblock.requirements[i].amount);
|
||||
for(int i = 0; i < current.requirements.length; i++){
|
||||
int sclamount = Math.round(state.rules.buildCostMultiplier * current.requirements[i].amount);
|
||||
int required = (int)(accumulator[i]); //calculate items that are required now
|
||||
|
||||
if(inventory.get(cblock.requirements[i].item) == 0 && sclamount != 0){
|
||||
if(inventory.get(current.requirements[i].item) == 0 && sclamount != 0){
|
||||
maxProgress = 0f;
|
||||
}else if(required > 0){ //if this amount is positive...
|
||||
//calculate how many items it can actually use
|
||||
int maxUse = Math.min(required, inventory.get(cblock.requirements[i].item));
|
||||
int maxUse = Math.min(required, inventory.get(current.requirements[i].item));
|
||||
//get this as a fraction
|
||||
float fraction = maxUse / (float)required;
|
||||
|
||||
@@ -337,7 +335,7 @@ public class ConstructBlock extends Block{
|
||||
|
||||
//remove stuff that is actually used
|
||||
if(remove){
|
||||
inventory.remove(cblock.requirements[i].item, maxUse);
|
||||
inventory.remove(current.requirements[i].item, maxUse);
|
||||
}
|
||||
}
|
||||
//else, no items are required yet, so just keep going
|
||||
@@ -351,13 +349,15 @@ public class ConstructBlock extends Block{
|
||||
}
|
||||
|
||||
public void setConstruct(Block previous, Block block){
|
||||
if(block == null) return;
|
||||
|
||||
this.constructColor = 0f;
|
||||
this.wasConstructing = true;
|
||||
this.cblock = block;
|
||||
this.current = block;
|
||||
this.previous = previous;
|
||||
this.buildCost = block.buildCost * state.rules.buildCostMultiplier;
|
||||
this.accumulator = new float[block.requirements.length];
|
||||
this.totalAccumulator = new float[block.requirements.length];
|
||||
this.buildCost = block.buildCost * state.rules.buildCostMultiplier;
|
||||
}
|
||||
|
||||
public void setDeconstruct(Block previous){
|
||||
@@ -367,12 +367,8 @@ public class ConstructBlock extends Block{
|
||||
this.wasConstructing = false;
|
||||
this.previous = previous;
|
||||
this.progress = 1f;
|
||||
if(previous.buildCost >= 0.01f){
|
||||
this.cblock = previous;
|
||||
this.buildCost = previous.buildCost * state.rules.buildCostMultiplier;
|
||||
}else{
|
||||
this.buildCost = 20f; //default no-requirement build cost is 20
|
||||
}
|
||||
this.current = previous;
|
||||
this.buildCost = previous.buildCost * state.rules.buildCostMultiplier;
|
||||
this.accumulator = new float[previous.requirements.length];
|
||||
this.totalAccumulator = new float[previous.requirements.length];
|
||||
}
|
||||
@@ -381,8 +377,8 @@ public class ConstructBlock extends Block{
|
||||
public void write(Writes write){
|
||||
super.write(write);
|
||||
write.f(progress);
|
||||
write.s(previous == null ? -1 : previous.id);
|
||||
write.s(cblock == null ? -1 : cblock.id);
|
||||
write.s(previous.id);
|
||||
write.s(current.id);
|
||||
|
||||
if(accumulator == null){
|
||||
write.b(-1);
|
||||
@@ -413,13 +409,12 @@ public class ConstructBlock extends Block{
|
||||
}
|
||||
|
||||
if(pid != -1) previous = content.block(pid);
|
||||
if(rid != -1) cblock = content.block(rid);
|
||||
if(rid != -1) current = content.block(rid);
|
||||
|
||||
if(cblock != null){
|
||||
buildCost = cblock.buildCost * state.rules.buildCostMultiplier;
|
||||
}else{
|
||||
buildCost = 20f;
|
||||
}
|
||||
if(previous == null) previous = Blocks.air;
|
||||
if(current == null) current = Blocks.air;
|
||||
|
||||
buildCost = current.buildCost * state.rules.buildCostMultiplier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ public class ItemSelection{
|
||||
if(closeSelect) control.input.frag.config.hideConfig();
|
||||
}).group(group).get();
|
||||
button.changed(() -> consumer.get(button.isChecked() ? item : null));
|
||||
button.getStyle().imageUp = new TextureRegionDrawable(item.icon(Cicon.small));
|
||||
button.getStyle().imageUp = new TextureRegionDrawable(item.uiIcon);
|
||||
button.update(() -> button.setChecked(holder.get() == item));
|
||||
|
||||
if(i++ % 4 == 3){
|
||||
|
||||
@@ -109,7 +109,10 @@ public class Accelerator extends Block{
|
||||
|
||||
if(!state.isCampaign() || !consValid()) return;
|
||||
|
||||
ui.showInfo("@indev.campaign");
|
||||
ui.planet.showPlanetLaunch(state.rules.sector, sector -> {
|
||||
//TODO cutscene, etc...
|
||||
consume();
|
||||
});
|
||||
Events.fire(Trigger.acceleratorUse);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package mindustry.world.blocks.campaign;
|
||||
|
||||
import arc.*;
|
||||
import arc.audio.*;
|
||||
import arc.Graphics.*;
|
||||
import arc.Graphics.Cursor.*;
|
||||
import arc.audio.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
@@ -11,12 +11,14 @@ import arc.math.geom.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.logic.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
@@ -26,9 +28,8 @@ import mindustry.world.meta.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class LaunchPad extends Block{
|
||||
public final int timerLaunch = timers++;
|
||||
/** Time inbetween launches. */
|
||||
public float launchTime;
|
||||
public float launchTime = 1f;
|
||||
public Sound launchSound = Sounds.none;
|
||||
|
||||
public @Load("@-light") TextureRegion lightRegion;
|
||||
@@ -42,6 +43,7 @@ public class LaunchPad extends Block{
|
||||
update = true;
|
||||
configurable = true;
|
||||
drawDisabled = false;
|
||||
flags = EnumSet.of(BlockFlag.launchPad);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -64,6 +66,7 @@ public class LaunchPad extends Block{
|
||||
}
|
||||
|
||||
public class LaunchPadBuild extends Building{
|
||||
public float launchCounter;
|
||||
|
||||
@Override
|
||||
public Cursor getCursor(){
|
||||
@@ -81,6 +84,12 @@ public class LaunchPad extends Block{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double sense(LAccess sensor){
|
||||
if(sensor == LAccess.progress) return Mathf.clamp(launchCounter / launchTime);
|
||||
return super.sense(sensor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
super.draw();
|
||||
@@ -89,7 +98,7 @@ public class LaunchPad extends Block{
|
||||
|
||||
if(lightRegion.found()){
|
||||
Draw.color(lightColor);
|
||||
float progress = Math.min((float)items.total() / itemCapacity, timer.getTime(timerLaunch) / (launchTime / timeScale));
|
||||
float progress = Math.min((float)items.total() / itemCapacity, launchCounter / launchTime);
|
||||
int steps = 3;
|
||||
float step = 1f;
|
||||
|
||||
@@ -106,7 +115,7 @@ public class LaunchPad extends Block{
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
float cooldown = Mathf.clamp(timer.getTime(timerLaunch) / (90f / timeScale));
|
||||
float cooldown = Mathf.clamp(launchCounter / (90f));
|
||||
|
||||
Draw.mixcol(lightColor, 1f - cooldown);
|
||||
|
||||
@@ -125,7 +134,7 @@ public class LaunchPad extends Block{
|
||||
if(!state.isCampaign()) return;
|
||||
|
||||
//launch when full and base conditions are met
|
||||
if(items.total() >= itemCapacity && efficiency() >= 1f && timer(timerLaunch, launchTime / timeScale)){
|
||||
if(items.total() >= itemCapacity && efficiency() >= 1f && (launchCounter += edelta()) >= launchTime){
|
||||
launchSound.at(x, y);
|
||||
LaunchPayload entity = LaunchPayload.create();
|
||||
items.each((item, amount) -> entity.stacks.add(new ItemStack(item, amount)));
|
||||
@@ -136,6 +145,7 @@ public class LaunchPad extends Block{
|
||||
Fx.launchPod.at(this);
|
||||
items.clear();
|
||||
Effect.shake(3f, 3f, this);
|
||||
launchCounter = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,6 +181,25 @@ public class LaunchPad extends Block{
|
||||
deselect();
|
||||
}).size(40f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte version(){
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Writes write){
|
||||
super.write(write);
|
||||
write.f(launchCounter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(Reads read, byte revision){
|
||||
super.read(read, revision);
|
||||
if(revision >= 1){
|
||||
launchCounter = read.f();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EntityDef(LaunchPayloadc.class)
|
||||
|
||||
280
core/src/mindustry/world/blocks/campaign/PayloadLaunchPad.java
Normal file
280
core/src/mindustry/world/blocks/campaign/PayloadLaunchPad.java
Normal file
@@ -0,0 +1,280 @@
|
||||
package mindustry.world.blocks.campaign;
|
||||
|
||||
import arc.*;
|
||||
import arc.Graphics.*;
|
||||
import arc.Graphics.Cursor.*;
|
||||
import arc.audio.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.logic.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.blocks.payloads.*;
|
||||
import mindustry.world.consumers.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
//TODO very much unfinished
|
||||
public class PayloadLaunchPad extends PayloadBlock{
|
||||
/** Time between launches. */
|
||||
public float launchTime;
|
||||
public Sound launchSound = Sounds.none;
|
||||
|
||||
public @Load("@-light") TextureRegion lightRegion;
|
||||
public @Load(value = "@-pod", fallback = "launchpod") TextureRegion podRegion;
|
||||
public Color lightColor = Color.valueOf("eab678");
|
||||
|
||||
public PayloadLaunchPad(String name){
|
||||
super(name);
|
||||
solid = true;
|
||||
update = true;
|
||||
configurable = true;
|
||||
drawDisabled = false;
|
||||
flags = EnumSet.of(BlockFlag.launchPad);
|
||||
acceptsPayload = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStats(){
|
||||
super.setStats();
|
||||
|
||||
stats.add(Stat.launchTime, launchTime / 60f, StatUnit.seconds);
|
||||
}
|
||||
|
||||
public class PayloadLaunchPadBuild extends PayloadBlockBuild<Payload>{
|
||||
public float launchCounter;
|
||||
|
||||
@Override
|
||||
public Cursor getCursor(){
|
||||
return !state.isCampaign() || net.client() ? SystemCursor.arrow : super.getCursor();
|
||||
}
|
||||
|
||||
//cannot be disabled
|
||||
@Override
|
||||
public float efficiency(){
|
||||
return power != null && (block.consumes.has(ConsumeType.power) && !block.consumes.getPower().buffered) ? power.status : 1f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldConsume(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
super.draw();
|
||||
|
||||
if(!state.isCampaign()) return;
|
||||
|
||||
if(lightRegion.found()){
|
||||
Draw.color(lightColor);
|
||||
float progress = launchCounter / launchTime;
|
||||
int steps = 3;
|
||||
float step = 1f;
|
||||
|
||||
for(int i = 0; i < 4; i++){
|
||||
for(int j = 0; j < steps; j++){
|
||||
float alpha = Mathf.curve(progress, (float)j / steps, (j+1f) / steps);
|
||||
float offset = -(j - 1f) * step;
|
||||
|
||||
Draw.color(Pal.metalGrayDark, lightColor, alpha);
|
||||
Draw.rect(lightRegion, x + Geometry.d8edge(i).x * offset, y + Geometry.d8edge(i).y * offset, i * 90);
|
||||
}
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
drawPayload();
|
||||
|
||||
float cooldown = Mathf.clamp(launchCounter / (90f));
|
||||
|
||||
Draw.mixcol(lightColor, 1f - cooldown);
|
||||
|
||||
Draw.rect(podRegion, x, y);
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double sense(LAccess sensor){
|
||||
if(sensor == LAccess.progress) return Mathf.clamp(launchCounter / launchTime);
|
||||
return super.sense(sensor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
if(!state.isCampaign()) return;
|
||||
|
||||
//launch when full and base conditions are met
|
||||
if(payload != null && moveInPayload() && efficiency() >= 1f && (launchCounter += edelta()) >= launchTime){
|
||||
launchSound.at(x, y);
|
||||
LargeLaunchPayload entity = LargeLaunchPayload.create();
|
||||
entity.payload = payload;
|
||||
entity.set(this);
|
||||
entity.lifetime(120f);
|
||||
entity.team(team);
|
||||
entity.add();
|
||||
Fx.launchPod.at(this);
|
||||
payload = null;
|
||||
Effect.shake(3f, 3f, this);
|
||||
launchCounter = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void display(Table table){
|
||||
super.display(table);
|
||||
|
||||
if(!state.isCampaign()) return;
|
||||
|
||||
table.row();
|
||||
table.label(() -> {
|
||||
Sector dest = state.rules.sector == null ? null : state.rules.sector.info.getRealDestination();
|
||||
|
||||
return Core.bundle.format("launch.destination",
|
||||
dest == null ? Core.bundle.get("sectors.nonelaunch") :
|
||||
"[accent]" + dest.name());
|
||||
}).pad(4).wrap().width(200f).left();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildConfiguration(Table table){
|
||||
if(!state.isCampaign() || net.client()){
|
||||
deselect();
|
||||
return;
|
||||
}
|
||||
|
||||
table.button(Icon.upOpen, Styles.clearTransi, () -> {
|
||||
ui.planet.showSelect(state.rules.sector, other -> {
|
||||
if(state.isCampaign()){
|
||||
state.rules.sector.info.destination = other;
|
||||
}
|
||||
});
|
||||
deselect();
|
||||
}).size(40f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte version(){
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Writes write){
|
||||
super.write(write);
|
||||
write.f(launchCounter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(Reads read, byte revision){
|
||||
super.read(read, revision);
|
||||
if(revision >= 1){
|
||||
launchCounter = read.f();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EntityDef(LargeLaunchPayloadc.class)
|
||||
@Component(base = true)
|
||||
static abstract class LargeLaunchPayloadComp implements Drawc, Timedc, Teamc{
|
||||
@Import float x,y;
|
||||
|
||||
Payload payload;
|
||||
transient Interval in = new Interval();
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
float alpha = fout(Interp.pow5Out);
|
||||
float scale = (1f - alpha) * 1.3f + 1f;
|
||||
float cx = cx(), cy = cy();
|
||||
float rotation = fin() * (130f + Mathf.randomSeedRange(id(), 50f));
|
||||
|
||||
Draw.z(Layer.effect + 0.001f);
|
||||
|
||||
Draw.color(Pal.engine);
|
||||
|
||||
float rad = 0.2f + fslope();
|
||||
|
||||
Fill.light(cx, cy, 10, 25f * (rad + scale-1f), Tmp.c2.set(Pal.engine).a(alpha), Tmp.c1.set(Pal.engine).a(0f));
|
||||
|
||||
Draw.alpha(alpha);
|
||||
for(int i = 0; i < 4; i++){
|
||||
Drawf.tri(cx, cy, 6f, 40f * (rad + scale-1f), i * 90f + rotation);
|
||||
}
|
||||
|
||||
Draw.color();
|
||||
|
||||
Draw.z(Layer.weather - 1);
|
||||
|
||||
TextureRegion region = blockOn() instanceof mindustry.world.blocks.campaign.PayloadLaunchPad p ? p.podRegion : Core.atlas.find("launchpod");
|
||||
float rw = region.width * Draw.scl * scale, rh = region.height * Draw.scl * scale;
|
||||
|
||||
Draw.alpha(alpha);
|
||||
Draw.rect(region, cx, cy, rw, rh, rotation);
|
||||
|
||||
Tmp.v1.trns(225f, fin(Interp.pow3In) * 250f);
|
||||
|
||||
Draw.z(Layer.flyingUnit + 1);
|
||||
Draw.color(0, 0, 0, 0.22f * alpha);
|
||||
Draw.rect(region, cx + Tmp.v1.x, cy + Tmp.v1.y, rw, rh, rotation);
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
float cx(){
|
||||
return x + fin(Interp.pow2In) * (12f + Mathf.randomSeedRange(id() + 3, 4f));
|
||||
}
|
||||
|
||||
float cy(){
|
||||
return y + fin(Interp.pow5In) * (100f + Mathf.randomSeedRange(id() + 2, 30f));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
float r = 3f;
|
||||
if(in.get(4f - fin()*2f)){
|
||||
Fx.rocketSmoke.at(cx() + Mathf.range(r), cy() + Mathf.range(r), fin());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(){
|
||||
if(!state.isCampaign()) return;
|
||||
|
||||
Sector destsec = state.rules.sector.info.getRealDestination();
|
||||
|
||||
//actually launch the items upon removal
|
||||
if(team() == state.rules.defaultTeam){
|
||||
if(destsec != null && (destsec != state.rules.sector || net.client())){
|
||||
/*
|
||||
ItemSeq dest = new ItemSeq();
|
||||
|
||||
for(ItemStack stack : stacks){
|
||||
dest.add(stack);
|
||||
|
||||
//update export
|
||||
state.rules.sector.info.handleItemExport(stack);
|
||||
Events.fire(new LaunchItemEvent(stack));
|
||||
}
|
||||
|
||||
if(!net.client()){
|
||||
destsec.addItems(dest);
|
||||
}*/
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,6 @@ public class ForceProjector extends Block{
|
||||
public float cooldownNormal = 1.75f;
|
||||
public float cooldownLiquid = 1.5f;
|
||||
public float cooldownBrokenBase = 0.35f;
|
||||
public float basePowerDraw = 0.2f;
|
||||
public @Load("@-top") TextureRegion topRegion;
|
||||
|
||||
static ForceBuild paramEntity;
|
||||
@@ -40,7 +39,7 @@ public class ForceProjector extends Block{
|
||||
trait.absorb();
|
||||
Fx.absorb.at(trait);
|
||||
paramEntity.hit = 1f;
|
||||
paramEntity.buildup += trait.damage() * paramEntity.warmup;
|
||||
paramEntity.buildup += trait.damage();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -57,6 +56,12 @@ public class ForceProjector extends Block{
|
||||
consumes.add(new ConsumeLiquidFilter(liquid -> liquid.temperature <= 0.5f && liquid.flammability < 0.1f, 0.1f)).boost().update(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
clipSize = Math.max(clipSize, (radius + phaseRadiusBoost + 3f) * 2f);
|
||||
super.init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBars(){
|
||||
super.setBars();
|
||||
@@ -70,10 +75,10 @@ public class ForceProjector extends Block{
|
||||
|
||||
@Override
|
||||
public void setStats(){
|
||||
stats.timePeriod = phaseUseTime;
|
||||
super.setStats();
|
||||
stats.add(Stat.shieldHealth, shieldHealth, StatUnit.none);
|
||||
stats.add(Stat.cooldownTime, (int) (shieldHealth / cooldownBrokenBase / 60f), StatUnit.seconds);
|
||||
stats.add(Stat.powerUse, basePowerDraw * 60f, StatUnit.powerSecond);
|
||||
stats.add(Stat.boostEffect, phaseRadiusBoost / tilesize, StatUnit.blocks);
|
||||
stats.add(Stat.boostEffect, phaseShieldBoost, StatUnit.shieldHealth);
|
||||
}
|
||||
@@ -94,22 +99,12 @@ public class ForceProjector extends Block{
|
||||
public class ForceBuild extends Building implements Ranged{
|
||||
public boolean broken = true;
|
||||
public float buildup, radscl, hit, warmup, phaseHeat;
|
||||
public ForceDraw drawer;
|
||||
|
||||
@Override
|
||||
public float range(){
|
||||
return realRadius();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void created(){
|
||||
super.created();
|
||||
drawer = ForceDraw.create();
|
||||
drawer.build = this;
|
||||
drawer.set(x, y);
|
||||
drawer.add();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldAmbientSound(){
|
||||
return !broken && realRadius() > 1f;
|
||||
@@ -120,7 +115,6 @@ public class ForceProjector extends Block{
|
||||
float radius = realRadius();
|
||||
if(!broken && radius > 1f) Fx.forceShrink.at(x, y, radius, team.color);
|
||||
super.onRemoved();
|
||||
drawer.remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -188,10 +182,6 @@ public class ForceProjector extends Block{
|
||||
public void draw(){
|
||||
super.draw();
|
||||
|
||||
if(drawer != null){
|
||||
drawer.set(x, y);
|
||||
}
|
||||
|
||||
if(buildup > 0f){
|
||||
Draw.alpha(buildup / shieldHealth * 0.75f);
|
||||
Draw.blend(Blending.additive);
|
||||
@@ -199,6 +189,8 @@ public class ForceProjector extends Block{
|
||||
Draw.blend();
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
drawShield();
|
||||
}
|
||||
|
||||
public void drawShield(){
|
||||
@@ -244,21 +236,4 @@ public class ForceProjector extends Block{
|
||||
phaseHeat = read.f();
|
||||
}
|
||||
}
|
||||
|
||||
@EntityDef(value = {ForceDrawc.class}, serialize = false)
|
||||
@Component(base = true)
|
||||
abstract class ForceDrawComp implements Drawc{
|
||||
transient ForceBuild build;
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
build.drawShield();
|
||||
}
|
||||
|
||||
@Replace
|
||||
@Override
|
||||
public float clipSize(){
|
||||
return build.realRadius() * 3f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ public class MendProjector extends Block{
|
||||
|
||||
@Override
|
||||
public void setStats(){
|
||||
stats.timePeriod = useTime;
|
||||
super.setStats();
|
||||
|
||||
stats.add(Stat.repairTime, (int)(100f / healPercent * reload / 60f), StatUnit.seconds);
|
||||
@@ -90,11 +91,17 @@ public class MendProjector extends Block{
|
||||
|
||||
indexer.eachBlock(this, realRange, other -> other.damaged(), other -> {
|
||||
other.heal(other.maxHealth() * (healPercent + phaseHeat * phaseBoost) / 100f * efficiency());
|
||||
Fx.healBlockFull.at(other.x, other.y, other.block.size, Tmp.c1.set(baseColor).lerp(phaseColor, phaseHeat));
|
||||
Fx.healBlockFull.at(other.x, other.y, other.block.size, baseColor);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public double sense(LAccess sensor){
|
||||
if(sensor == LAccess.progress) return Mathf.clamp(charge / reload);
|
||||
return super.sense(sensor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawSelect(){
|
||||
float realRange = range + phaseHeat * phaseRangeBoost;
|
||||
|
||||
@@ -57,6 +57,7 @@ public class OverdriveProjector extends Block{
|
||||
|
||||
@Override
|
||||
public void setStats(){
|
||||
stats.timePeriod = useTime;
|
||||
super.setStats();
|
||||
|
||||
stats.add(Stat.speedIncrease, (int)(100f * speedBoost), StatUnit.percent);
|
||||
|
||||
@@ -14,7 +14,6 @@ import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.consumers.*;
|
||||
import mindustry.world.meta.*;
|
||||
import mindustry.world.meta.values.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@@ -28,7 +27,21 @@ public class ItemTurret extends Turret{
|
||||
|
||||
/** Initializes accepted ammo map. Format: [item1, bullet1, item2, bullet2...] */
|
||||
public void ammo(Object... objects){
|
||||
ammoTypes = OrderedMap.of(objects);
|
||||
ammoTypes = ObjectMap.of(objects);
|
||||
}
|
||||
|
||||
/** Makes copies of all bullets and limits their range. */
|
||||
public void limitRange(){
|
||||
limitRange(1f);
|
||||
}
|
||||
|
||||
/** Makes copies of all bullets and limits their range. */
|
||||
public void limitRange(float margin){
|
||||
for(var entry : ammoTypes.copy().entries()){
|
||||
var copy = entry.value.copy();
|
||||
copy.lifetime = (range + margin) / copy.speed;
|
||||
ammoTypes.put(entry.key, copy);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -36,7 +49,7 @@ public class ItemTurret extends Turret{
|
||||
super.setStats();
|
||||
|
||||
stats.remove(Stat.itemCapacity);
|
||||
stats.add(Stat.ammo, new AmmoListValue<>(ammoTypes));
|
||||
stats.add(Stat.ammo, StatValues.ammo(ammoTypes));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -45,7 +58,7 @@ public class ItemTurret extends Turret{
|
||||
@Override
|
||||
public void build(Building tile, Table table){
|
||||
MultiReqImage image = new MultiReqImage();
|
||||
content.items().each(i -> filter.get(i) && i.unlockedNow(), item -> image.add(new ReqImage(new ItemImage(item.icon(Cicon.medium)),
|
||||
content.items().each(i -> filter.get(i) && i.unlockedNow(), item -> image.add(new ReqImage(new ItemImage(item.uiIcon),
|
||||
() -> tile instanceof ItemTurretBuild it && !it.ammo.isEmpty() && ((ItemEntry)it.ammo.peek()).item == item)));
|
||||
|
||||
table.add(image).size(8 * 4);
|
||||
|
||||
@@ -4,10 +4,10 @@ import arc.math.*;
|
||||
import arc.util.*;
|
||||
import mindustry.entities.bullet.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.logic.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.consumers.*;
|
||||
import mindustry.world.meta.*;
|
||||
import mindustry.world.meta.values.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@@ -34,7 +34,7 @@ public class LaserTurret extends PowerTurret{
|
||||
super.setStats();
|
||||
|
||||
stats.remove(Stat.booster);
|
||||
stats.add(Stat.input, new BoosterListValue(reloadTime, consumes.<ConsumeLiquidBase>get(ConsumeType.liquid).amount, coolantMultiplier, false, l -> consumes.liquidfilters.get(l.id)));
|
||||
stats.add(Stat.input, StatValues.boosters(reloadTime, consumes.<ConsumeLiquidBase>get(ConsumeType.liquid).amount, coolantMultiplier, false, l -> consumes.liquidfilters.get(l.id)));
|
||||
}
|
||||
|
||||
public class LaserTurretBuild extends PowerTurretBuild{
|
||||
@@ -67,8 +67,8 @@ public class LaserTurret extends PowerTurret{
|
||||
Liquid liquid = liquids.current();
|
||||
float maxUsed = consumes.<ConsumeLiquidBase>get(ConsumeType.liquid).amount;
|
||||
|
||||
float used = (cheating() ? maxUsed * Time.delta : Math.min(liquids.get(liquid), maxUsed * Time.delta)) * liquid.heatCapacity * coolantMultiplier;
|
||||
reload -= used;
|
||||
float used = (cheating() ? maxUsed * Time.delta : Math.min(liquids.get(liquid), maxUsed * Time.delta));
|
||||
reload -= used * liquid.heatCapacity * coolantMultiplier;
|
||||
liquids.remove(liquid, used);
|
||||
|
||||
if(Mathf.chance(0.06 * used)){
|
||||
@@ -77,6 +77,13 @@ public class LaserTurret extends PowerTurret{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public double sense(LAccess sensor){
|
||||
//reload reversed for laser turrets
|
||||
if(sensor == LAccess.progress) return Mathf.clamp(1f - reload / reloadTime);
|
||||
return super.sense(sensor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateShooting(){
|
||||
if(bulletLife > 0 && bullet != null){
|
||||
|
||||
@@ -3,6 +3,7 @@ package mindustry.world.blocks.defense.turrets;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.struct.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.bullet.*;
|
||||
import mindustry.gen.*;
|
||||
@@ -11,7 +12,6 @@ import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.consumers.*;
|
||||
import mindustry.world.meta.*;
|
||||
import mindustry.world.meta.values.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@@ -32,14 +32,14 @@ public class LiquidTurret extends Turret{
|
||||
|
||||
/** Initializes accepted ammo map. Format: [liquid1, bullet1, liquid2, bullet2...] */
|
||||
public void ammo(Object... objects){
|
||||
ammoTypes = OrderedMap.of(objects);
|
||||
ammoTypes = ObjectMap.of(objects);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStats(){
|
||||
super.setStats();
|
||||
|
||||
stats.add(Stat.ammo, new AmmoListValue<>(ammoTypes));
|
||||
stats.add(Stat.ammo, StatValues.ammo(ammoTypes));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -88,7 +88,9 @@ public class LiquidTurret extends Turret{
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
unit.ammo(unit.type().ammoCapacity * liquids.currentAmount() / liquidCapacity);
|
||||
if(unit != null){
|
||||
unit.ammo(unit.type().ammoCapacity * liquids.currentAmount() / liquidCapacity);
|
||||
}
|
||||
|
||||
super.updateTile();
|
||||
}
|
||||
@@ -116,8 +118,11 @@ public class LiquidTurret extends Turret{
|
||||
protected void effects(){
|
||||
BulletType type = peekAmmo();
|
||||
|
||||
type.shootEffect.at(x + tr.x, y + tr.y, rotation, liquids.current().color);
|
||||
type.smokeEffect.at(x + tr.x, y + tr.y, rotation, liquids.current().color);
|
||||
Effect fshootEffect = shootEffect == Fx.none ? type.shootEffect : shootEffect;
|
||||
Effect fsmokeEffect = smokeEffect == Fx.none ? type.smokeEffect : smokeEffect;
|
||||
|
||||
fshootEffect.at(x + tr.x, y + tr.y, rotation, liquids.current().color);
|
||||
fsmokeEffect.at(x + tr.x, y + tr.y, rotation, liquids.current().color);
|
||||
shootSound.at(tile);
|
||||
|
||||
if(shootShake > 0){
|
||||
|
||||
@@ -4,7 +4,6 @@ import arc.struct.*;
|
||||
import mindustry.entities.bullet.*;
|
||||
import mindustry.logic.*;
|
||||
import mindustry.world.meta.*;
|
||||
import mindustry.world.meta.values.*;
|
||||
|
||||
public class PowerTurret extends Turret{
|
||||
public BulletType shootType;
|
||||
@@ -18,7 +17,7 @@ public class PowerTurret extends Turret{
|
||||
@Override
|
||||
public void setStats(){
|
||||
super.setStats();
|
||||
stats.add(Stat.ammo, new AmmoListValue<>(OrderedMap.of(this, shootType)));
|
||||
stats.add(Stat.ammo, StatValues.ammo(ObjectMap.of(this, shootType)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -31,7 +30,9 @@ public class PowerTurret extends Turret{
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
unit.ammo(power.status * unit.type().ammoCapacity);
|
||||
if(unit != null){
|
||||
unit.ammo(power.status * unit.type().ammoCapacity);
|
||||
}
|
||||
|
||||
super.updateTile();
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import arc.util.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.consumers.*;
|
||||
import mindustry.world.meta.*;
|
||||
import mindustry.world.meta.values.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@@ -21,7 +20,7 @@ public class ReloadTurret extends BaseTurret{
|
||||
super.setStats();
|
||||
|
||||
if(acceptCoolant){
|
||||
stats.add(Stat.booster, new BoosterListValue(reloadTime, consumes.<ConsumeLiquidBase>get(ConsumeType.liquid).amount, coolantMultiplier, true, l -> consumes.liquidfilters.get(l.id)));
|
||||
stats.add(Stat.booster, StatValues.boosters(reloadTime, consumes.<ConsumeLiquidBase>get(ConsumeType.liquid).amount, coolantMultiplier, true, l -> consumes.liquidfilters.get(l.id)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,16 +28,17 @@ public class ReloadTurret extends BaseTurret{
|
||||
public float reload;
|
||||
|
||||
protected void updateCooling(){
|
||||
float maxUsed = consumes.<ConsumeLiquidBase>get(ConsumeType.liquid).amount;
|
||||
if(reload < reloadTime){
|
||||
float maxUsed = consumes.<ConsumeLiquidBase>get(ConsumeType.liquid).amount;
|
||||
Liquid liquid = liquids.current();
|
||||
|
||||
Liquid liquid = liquids.current();
|
||||
float used = Math.min(liquids.get(liquid), maxUsed * Time.delta) * baseReloadSpeed();
|
||||
reload += used * liquid.heatCapacity * coolantMultiplier;
|
||||
liquids.remove(liquid, used);
|
||||
|
||||
float used = Math.min(Math.min(liquids.get(liquid), maxUsed * Time.delta), Math.max(0, ((reloadTime - reload) / coolantMultiplier) / liquid.heatCapacity)) * baseReloadSpeed();
|
||||
reload += used * liquid.heatCapacity * coolantMultiplier;
|
||||
liquids.remove(liquid, used);
|
||||
|
||||
if(Mathf.chance(0.06 * used)){
|
||||
coolEffect.at(x + Mathf.range(size * tilesize / 2f), y + Mathf.range(size * tilesize / 2f));
|
||||
if(Mathf.chance(0.06 * used)){
|
||||
coolEffect.at(x + Mathf.range(size * tilesize / 2f), y + Mathf.range(size * tilesize / 2f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,6 @@ public class TractorBeamTurret extends BaseTurret{
|
||||
|
||||
//disabled due to version mismatch problems
|
||||
acceptCoolant = false;
|
||||
expanded = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -64,6 +63,13 @@ public class TractorBeamTurret extends BaseTurret{
|
||||
stats.add(Stat.damage, damage * 60f, StatUnit.perSecond);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
super.init();
|
||||
|
||||
clipSize = Math.max(clipSize, (range + tilesize) * 2);
|
||||
}
|
||||
|
||||
public class TractorBeamBuild extends BaseTurretBuild{
|
||||
public @Nullable Unit target;
|
||||
public float lastX, lastY, strength;
|
||||
|
||||
@@ -106,7 +106,7 @@ public class Turret extends ReloadTurret{
|
||||
super.setStats();
|
||||
|
||||
stats.add(Stat.inaccuracy, (int)inaccuracy, StatUnit.degrees);
|
||||
stats.add(Stat.reload, 60f / (reloadTime + 1) * (alternate ? 1 : shots), StatUnit.none);
|
||||
stats.add(Stat.reload, 60f / (reloadTime) * (alternate ? 1 : shots), StatUnit.none);
|
||||
stats.add(Stat.targetsAir, targetAir);
|
||||
stats.add(Stat.targetsGround, targetGround);
|
||||
if(ammoPerShot != 1) stats.add(Stat.ammoUse, ammoPerShot, StatUnit.perShot);
|
||||
@@ -144,7 +144,7 @@ public class Turret extends ReloadTurret{
|
||||
public boolean logicShooting = false;
|
||||
public @Nullable Posc target;
|
||||
public Vec2 targetPos = new Vec2();
|
||||
public BlockUnitc unit = Nulls.blockUnit;
|
||||
public @Nullable BlockUnitc unit;
|
||||
public boolean wasShooting, charging;
|
||||
|
||||
@Override
|
||||
@@ -155,7 +155,7 @@ public class Turret extends ReloadTurret{
|
||||
|
||||
@Override
|
||||
public void control(LAccess type, double p1, double p2, double p3, double p4){
|
||||
if(type == LAccess.shoot && !unit.isPlayer()){
|
||||
if(type == LAccess.shoot && (unit == null || !unit.isPlayer())){
|
||||
targetPos.set(World.unconv((float)p1), World.unconv((float)p2));
|
||||
logicControlTime = logicControlCooldown;
|
||||
logicShooting = !Mathf.zero(p3);
|
||||
@@ -166,7 +166,7 @@ public class Turret extends ReloadTurret{
|
||||
|
||||
@Override
|
||||
public void control(LAccess type, Object p1, double p2, double p3, double p4){
|
||||
if(type == LAccess.shootp && !unit.isPlayer()){
|
||||
if(type == LAccess.shootp && (unit == null || !unit.isPlayer())){
|
||||
logicControlTime = logicControlCooldown;
|
||||
logicShooting = !Mathf.zero(p2);
|
||||
|
||||
@@ -187,16 +187,21 @@ public class Turret extends ReloadTurret{
|
||||
case shootX -> World.conv(targetPos.x);
|
||||
case shootY -> World.conv(targetPos.y);
|
||||
case shooting -> isShooting() ? 1 : 0;
|
||||
case progress -> Mathf.clamp(reload / reloadTime);
|
||||
default -> super.sense(sensor);
|
||||
};
|
||||
}
|
||||
|
||||
public boolean isShooting(){
|
||||
return (isControlled() ? unit.isShooting() : logicControlled() ? logicShooting : target != null);
|
||||
return (isControlled() ? (unit != null && unit.isShooting()) : logicControlled() ? logicShooting : target != null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Unit unit(){
|
||||
if(unit == null){
|
||||
unit = (BlockUnitc)UnitTypes.block.create(team);
|
||||
unit.tile(this);
|
||||
}
|
||||
return (Unit)unit;
|
||||
}
|
||||
|
||||
@@ -205,7 +210,7 @@ public class Turret extends ReloadTurret{
|
||||
}
|
||||
|
||||
public boolean isActive(){
|
||||
return target != null || wasShooting;
|
||||
return (target != null || wasShooting) && enabled;
|
||||
}
|
||||
|
||||
public void targetPosition(Posc pos){
|
||||
@@ -247,10 +252,12 @@ public class Turret extends ReloadTurret{
|
||||
recoil = Mathf.lerpDelta(recoil, 0f, restitution);
|
||||
heat = Mathf.lerpDelta(heat, 0f, cooldown);
|
||||
|
||||
unit.health(health);
|
||||
unit.rotation(rotation);
|
||||
unit.team(team);
|
||||
unit.set(x, y);
|
||||
if(unit != null){
|
||||
unit.health(health);
|
||||
unit.rotation(rotation);
|
||||
unit.team(team);
|
||||
unit.set(x, y);
|
||||
}
|
||||
|
||||
if(logicControlTime > 0){
|
||||
logicControlTime -= Time.delta;
|
||||
@@ -353,14 +360,14 @@ public class Turret extends ReloadTurret{
|
||||
}
|
||||
|
||||
protected void updateShooting(){
|
||||
reload += delta() * peekAmmo().reloadMultiplier * baseReloadSpeed();
|
||||
|
||||
if(reload >= reloadTime && !charging){
|
||||
BulletType type = peekAmmo();
|
||||
|
||||
shoot(type);
|
||||
|
||||
reload = 0f;
|
||||
}else{
|
||||
reload += delta() * peekAmmo().reloadMultiplier * baseReloadSpeed();
|
||||
reload %= reloadTime;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -38,6 +38,11 @@ public class BufferedItemBridge extends ExtendingItemBridge{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doDump(){
|
||||
dump();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Writes write){
|
||||
super.write(write);
|
||||
|
||||
@@ -106,8 +106,7 @@ public class Conveyor extends Block implements Autotiler{
|
||||
public class ConveyorBuild extends Building implements ChainedBuilding{
|
||||
//parallel array data
|
||||
public Item[] ids = new Item[capacity];
|
||||
public float[] xs = new float[capacity];
|
||||
public float[] ys = new float[capacity];
|
||||
public float[] xs = new float[capacity], ys = new float[capacity];
|
||||
//amount of items, always < capacity
|
||||
public int len = 0;
|
||||
//next entity
|
||||
@@ -150,7 +149,7 @@ public class Conveyor extends Block implements Autotiler{
|
||||
tr1.trns(rotation * 90, tilesize, 0);
|
||||
tr2.trns(rotation * 90, -tilesize / 2f, xs[i] * tilesize / 2f);
|
||||
|
||||
Draw.rect(item.icon(Cicon.medium),
|
||||
Draw.rect(item.fullIcon,
|
||||
(tile.x * tilesize + tr1.x * ys[i] + tr2.x),
|
||||
(tile.y * tilesize + tr1.y * ys[i] + tr2.y),
|
||||
itemSize, itemSize);
|
||||
|
||||
187
core/src/mindustry/world/blocks/distribution/Duct.java
Normal file
187
core/src/mindustry/world/blocks/distribution/Duct.java
Normal file
@@ -0,0 +1,187 @@
|
||||
package mindustry.world.blocks.distribution;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.input.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class Duct extends Block implements Autotiler{
|
||||
public float speed = 5f;
|
||||
|
||||
public @Load(value = "@-top-#", length = 5) TextureRegion[] topRegions;
|
||||
public @Load(value = "@-bottom-#", length = 5, fallback = "duct-bottom-#") TextureRegion[] botRegions;
|
||||
|
||||
public Duct(String name){
|
||||
super(name);
|
||||
|
||||
group = BlockGroup.transportation;
|
||||
update = true;
|
||||
solid = false;
|
||||
hasItems = true;
|
||||
conveyorPlacement = true;
|
||||
unloadable = false;
|
||||
itemCapacity = 1;
|
||||
noUpdateDisabled = true;
|
||||
rotate = true;
|
||||
envEnabled = Env.space | Env.terrestrial | Env.underwater;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStats(){
|
||||
super.setStats();
|
||||
|
||||
stats.add(Stat.itemsMoved, 60f / speed, StatUnit.itemsSecond);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawRequestRegion(BuildPlan req, Eachable<BuildPlan> list){
|
||||
int[] bits = getTiling(req, list);
|
||||
|
||||
if(bits == null) return;
|
||||
|
||||
Draw.scl(bits[1], bits[2]);
|
||||
Draw.alpha(0.5f);
|
||||
Draw.rect(botRegions[bits[0]], req.drawx(), req.drawy(), req.rotation * 90);
|
||||
Draw.color();
|
||||
Draw.rect(topRegions[bits[0]], req.drawx(), req.drawy(), req.rotation * 90);
|
||||
Draw.scl();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean blends(Tile tile, int rotation, int otherx, int othery, int otherrot, Block otherblock){
|
||||
return otherblock.outputsItems() && blendsArmored(tile, rotation, otherx, othery, otherrot, otherblock);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureRegion[] icons(){
|
||||
return new TextureRegion[]{Core.atlas.find("duct-bottom"), topRegions[0]};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePlacementLine(Seq<BuildPlan> plans){
|
||||
Placement.calculateDuctBridges(plans, (DuctBridge)Blocks.ductBridge);
|
||||
}
|
||||
|
||||
public class DuctBuild extends Building{
|
||||
public float progress;
|
||||
public @Nullable Item current;
|
||||
public int recDir = 0;
|
||||
public int blendbits, xscl, yscl, blending;
|
||||
public @Nullable Building next;
|
||||
public @Nullable DuctBuild nextc;
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
float rotation = rotdeg();
|
||||
int r = this.rotation;
|
||||
|
||||
//draw extra ducts facing this one for tiling purposes
|
||||
for(int i = 0; i < 4; i++){
|
||||
if((blending & (1 << i)) != 0 && !(i == 0 && nextc != null)){
|
||||
int dir = r - i;
|
||||
float rot = i == 0 ? rotation : (dir)*90;
|
||||
drawAt(x + Geometry.d4x(dir) * tilesize*0.75f, y + Geometry.d4y(dir) * tilesize*0.75f, 0, rot, i != 0 ? SliceMode.bottom : SliceMode.top);
|
||||
}
|
||||
}
|
||||
|
||||
//draw item
|
||||
if(current != null){
|
||||
Draw.z(Layer.blockUnder + 0.1f);
|
||||
Tmp.v1.set(Geometry.d4x(recDir) * tilesize / 2f, Geometry.d4y(recDir) * tilesize / 2f)
|
||||
.lerp(Geometry.d4x(r) * tilesize / 2f, Geometry.d4y(r) * tilesize / 2f,
|
||||
Mathf.clamp((progress + 1f) / 2f));
|
||||
|
||||
Draw.rect(current.fullIcon, x + Tmp.v1.x, y + Tmp.v1.y, itemSize, itemSize);
|
||||
}
|
||||
|
||||
Draw.scl(xscl, yscl);
|
||||
drawAt(x, y, blendbits, rotation, SliceMode.none);
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
protected void drawAt(float x, float y, int bits, float rotation, SliceMode slice){
|
||||
Draw.z(Layer.blockUnder);
|
||||
Draw.rect(sliced(botRegions[bits], slice), x, y, rotation);
|
||||
|
||||
Draw.z(Layer.blockUnder + 0.2f);
|
||||
Draw.color(0.4f, 0.4f, 0.4f, 0.4f);
|
||||
Draw.rect(sliced(botRegions[bits], slice), x, y, rotation);
|
||||
Draw.color();
|
||||
Draw.rect(sliced(topRegions[bits], slice), x, y, rotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
progress += edelta() / speed * 2f;
|
||||
|
||||
if(current != null && next != null){
|
||||
if(progress >= (1f - 1f/speed) && moveForward(current)){
|
||||
items.remove(current, 1);
|
||||
current = null;
|
||||
progress %= (1f - 1f/speed);
|
||||
}
|
||||
}else{
|
||||
progress = 0;
|
||||
}
|
||||
|
||||
if(current == null && items.total() > 0){
|
||||
current = items.first();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptItem(Building source, Item item){
|
||||
return current == null && items.total() == 0 &&
|
||||
(source.block instanceof Duct || Edges.getFacingEdge(source.tile(), tile).relativeTo(tile) == rotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int removeStack(Item item, int amount){
|
||||
int removed = super.removeStack(item, amount);
|
||||
if(item == current) current = null;
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleStack(Item item, int amount, Teamc source){
|
||||
super.handleStack(item, amount, source);
|
||||
current = item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleItem(Building source, Item item){
|
||||
current = item;
|
||||
progress = -1f;
|
||||
recDir = relativeToEdge(source.tile);
|
||||
items.add(item, 1);
|
||||
noSleep();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProximityUpdate(){
|
||||
super.onProximityUpdate();
|
||||
|
||||
int[] bits = buildBlending(tile, rotation, null, true);
|
||||
blendbits = bits[0];
|
||||
xscl = bits[1];
|
||||
yscl = bits[2];
|
||||
blending = bits[4];
|
||||
next = front();
|
||||
nextc = next instanceof DuctBuild d ? d : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
149
core/src/mindustry/world/blocks/distribution/DuctBridge.java
Normal file
149
core/src/mindustry/world/blocks/distribution/DuctBridge.java
Normal file
@@ -0,0 +1,149 @@
|
||||
package mindustry.world.blocks.distribution;
|
||||
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.input.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
//TODO display range
|
||||
public class DuctBridge extends Block{
|
||||
public @Load("@-bridge") TextureRegion bridgeRegion;
|
||||
public @Load("@-bridge-bottom") TextureRegion bridgeBotRegion;
|
||||
//public @Load("@-bridge-top") TextureRegion bridgeTopRegion;
|
||||
public @Load("@-arrow") TextureRegion arrowRegion;
|
||||
public @Load("@-dir") TextureRegion dirRegion;
|
||||
|
||||
public int range = 4;
|
||||
public float moveDelay = 5f;
|
||||
|
||||
public DuctBridge(String name){
|
||||
super(name);
|
||||
update = true;
|
||||
solid = true;
|
||||
rotate = true;
|
||||
itemCapacity = 4;
|
||||
hasItems = true;
|
||||
group = BlockGroup.transportation;
|
||||
noUpdateDisabled = true;
|
||||
envEnabled = Env.space | Env.terrestrial | Env.underwater;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawRequestRegion(BuildPlan req, Eachable<BuildPlan> list){
|
||||
Draw.rect(region, req.drawx(), req.drawy());
|
||||
Draw.rect(dirRegion, req.drawx(), req.drawy(), req.rotation * 90);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureRegion[] icons(){
|
||||
return new TextureRegion[]{region, dirRegion};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changePlacementPath(Seq<Point2> points, int rotation){
|
||||
Placement.calculateNodes(points, this, rotation, (point, other) -> Math.max(Math.abs(point.x - other.x), Math.abs(point.y - other.y)) <= range);
|
||||
}
|
||||
|
||||
public boolean positionsValid(int x1, int y1, int x2, int y2){
|
||||
if(x1 == x2){
|
||||
return Math.abs(y1 - y2) <= range;
|
||||
}else if(y1 == y2){
|
||||
return Math.abs(x1 - x2) <= range;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public class DuctBridgeBuild extends Building{
|
||||
public Building[] occupied = new Building[4];
|
||||
public float progress = 0f;
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Draw.rect(block.region, x, y);
|
||||
Draw.rect(dirRegion, x, y, rotdeg());
|
||||
var link = findLink();
|
||||
if(link != null){
|
||||
Draw.z(Layer.power);
|
||||
Draw.alpha(Renderer.bridgeOpacity);
|
||||
float
|
||||
angle = angleTo(link),
|
||||
cx = (x + link.x)/2f,
|
||||
cy = (y + link.y)/2f,
|
||||
len = Math.max(Math.abs(x - link.x), Math.abs(y - link.y)) - size * tilesize;
|
||||
|
||||
Draw.rect(bridgeRegion, cx, cy, len, tilesize, angle);
|
||||
Draw.color(0.4f, 0.4f, 0.4f, 0.4f * Renderer.bridgeOpacity);
|
||||
Draw.rect(bridgeBotRegion, cx, cy, len, tilesize, angle);
|
||||
Draw.reset();
|
||||
Draw.alpha(Renderer.bridgeOpacity);
|
||||
//Draw.rect(bridgeTopRegion, cx, cy, len, tilesize, angle);
|
||||
|
||||
for(float i = 6f; i <= len + size * tilesize - 5f; i += 5f){
|
||||
Draw.rect(arrowRegion, x + Geometry.d4x(rotation) * i, y + Geometry.d4y(rotation) * i, angle);
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public DuctBridgeBuild findLink(){
|
||||
for(int i = 1; i <= range; i++){
|
||||
Tile other = tile.nearby(Geometry.d4x(rotation) * i, Geometry.d4y(rotation) * i);
|
||||
if(other.build instanceof DuctBridgeBuild build && build.team == team){
|
||||
return build;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
var link = findLink();
|
||||
if(link != null){
|
||||
link.occupied[rotation % 4] = this;
|
||||
if(items.any() && link.items.total() < link.block.itemCapacity){
|
||||
progress += edelta();
|
||||
while(progress > moveDelay){
|
||||
Item next = items.take();
|
||||
if(next != null && link.items.total() < link.block.itemCapacity){
|
||||
link.handleItem(this, next);
|
||||
}
|
||||
progress -= moveDelay;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(link == null && items.any()){
|
||||
Item next = items.first();
|
||||
if(moveForward(next)){
|
||||
items.remove(next, 1);
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 0; i < 4; i++){
|
||||
if(occupied[i] == null || occupied[i].rotation != i || !occupied[i].isValid()){
|
||||
occupied[i] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptItem(Building source, Item item){
|
||||
int rel = this.relativeTo(source);
|
||||
return items.total() < itemCapacity && rel != rotation && occupied[(rel + 2) % 4] == null;
|
||||
}
|
||||
}
|
||||
}
|
||||
125
core/src/mindustry/world/blocks/distribution/DuctRouter.java
Normal file
125
core/src/mindustry/world/blocks/distribution/DuctRouter.java
Normal file
@@ -0,0 +1,125 @@
|
||||
package mindustry.world.blocks.distribution;
|
||||
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
public class DuctRouter extends Block{
|
||||
public float speed = 5f;
|
||||
|
||||
public @Load(value = "@-top") TextureRegion topRegion;
|
||||
|
||||
public DuctRouter(String name){
|
||||
super(name);
|
||||
|
||||
group = BlockGroup.transportation;
|
||||
update = true;
|
||||
solid = false;
|
||||
hasItems = true;
|
||||
conveyorPlacement = true;
|
||||
unloadable = false;
|
||||
itemCapacity = 1;
|
||||
noUpdateDisabled = true;
|
||||
rotate = true;
|
||||
envEnabled = Env.space | Env.terrestrial | Env.underwater;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStats(){
|
||||
super.setStats();
|
||||
|
||||
stats.add(Stat.itemsMoved, 60f / speed, StatUnit.itemsSecond);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureRegion[] icons(){
|
||||
return new TextureRegion[]{region, topRegion};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawRequestRegion(BuildPlan req, Eachable<BuildPlan> list){
|
||||
Draw.rect(region, req.drawx(), req.drawy());
|
||||
Draw.rect(topRegion, req.drawx(), req.drawy(), req.rotation * 90);
|
||||
}
|
||||
|
||||
public class DuctBuild extends Building{
|
||||
public float progress;
|
||||
public @Nullable Item current;
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Draw.rect(region, x, y);
|
||||
Draw.rect(topRegion, x, y, rotdeg());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
progress += edelta() / speed * 2f;
|
||||
|
||||
if(current != null){
|
||||
if(progress >= (1f - 1f/speed)){
|
||||
var target = target();
|
||||
if(target != null){
|
||||
target.handleItem(this, current);
|
||||
cdump = (byte)((cdump + 1) % 3);
|
||||
items.remove(current, 1);
|
||||
current = null;
|
||||
progress %= (1f - 1f/speed);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
progress = 0;
|
||||
}
|
||||
|
||||
if(current == null && items.total() > 0){
|
||||
current = items.first();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Building target(){
|
||||
if(current == null) return null;
|
||||
|
||||
for(int i = -1; i <= 1; i++){
|
||||
Building other = nearby(Mathf.mod(rotation + i + cdump, 4));
|
||||
if(other != null && other.team == team && other.acceptItem(this, current)){
|
||||
return other;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptItem(Building source, Item item){
|
||||
return current == null && items.total() == 0 &&
|
||||
(Edges.getFacingEdge(source.tile(), tile).relativeTo(tile) == rotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int removeStack(Item item, int amount){
|
||||
int removed = super.removeStack(item, amount);
|
||||
if(item == current) current = null;
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleStack(Item item, int amount, Teamc source){
|
||||
super.handleStack(item, amount, source);
|
||||
current = item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleItem(Building source, Item item){
|
||||
current = item;
|
||||
progress = -1f;
|
||||
items.add(item, 1);
|
||||
noSleep();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@ import static mindustry.Vars.*;
|
||||
public class ItemBridge extends Block{
|
||||
private static BuildPlan otherReq;
|
||||
|
||||
public final int timerTransport = timers++;
|
||||
public int range;
|
||||
public float transportTime = 2f;
|
||||
public @Load("@-end") TextureRegion endRegion;
|
||||
@@ -38,7 +37,6 @@ public class ItemBridge extends Block{
|
||||
update = true;
|
||||
solid = true;
|
||||
hasPower = true;
|
||||
expanded = true;
|
||||
itemCapacity = 10;
|
||||
configurable = true;
|
||||
hasItems = true;
|
||||
@@ -95,14 +93,12 @@ public class ItemBridge extends Block{
|
||||
|
||||
Tile link = findLink(x, y);
|
||||
|
||||
Lines.stroke(2f, Pal.placing);
|
||||
for(int i = 0; i < 4; i++){
|
||||
Lines.dashLine(
|
||||
Drawf.dashLine(Pal.placing,
|
||||
x * tilesize + Geometry.d4[i].x * (tilesize / 2f + 2),
|
||||
y * tilesize + Geometry.d4[i].y * (tilesize / 2f + 2),
|
||||
x * tilesize + Geometry.d4[i].x * (range + 0.5f) * tilesize,
|
||||
y * tilesize + Geometry.d4[i].y * (range + 0.5f) * tilesize,
|
||||
range);
|
||||
x * tilesize + Geometry.d4[i].x * (range) * tilesize,
|
||||
y * tilesize + Geometry.d4[i].y * (range) * tilesize);
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
@@ -149,6 +145,12 @@ public class ItemBridge extends Block{
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
super.init();
|
||||
clipSize = Math.max(clipSize, (range + 0.5f) * tilesize * 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePlacementLine(Seq<BuildPlan> plans){
|
||||
for(int i = 0; i < plans.size - 1; i++){
|
||||
@@ -167,18 +169,20 @@ public class ItemBridge extends Block{
|
||||
|
||||
public class ItemBridgeBuild extends Building{
|
||||
public int link = -1;
|
||||
//TODO awful
|
||||
public IntSet incoming = new IntSet();
|
||||
public float uptime;
|
||||
public float time;
|
||||
public float time2;
|
||||
public float cycleSpeed = 1f;
|
||||
public float transportCounter;
|
||||
|
||||
@Override
|
||||
public void playerPlaced(Object config){
|
||||
super.playerPlaced(config);
|
||||
|
||||
Tile link = findLink(tile.x, tile.y);
|
||||
if(linkValid(tile, link) && !proximity.contains(link.build)){
|
||||
if(linkValid(tile, link) && this.link != link.pos() && !proximity.contains(link.build)){
|
||||
link.build.configure(tile.pos());
|
||||
}
|
||||
|
||||
@@ -286,7 +290,7 @@ public class ItemBridge extends Block{
|
||||
|
||||
Tile other = world.tile(link);
|
||||
if(!linkValid(tile, other)){
|
||||
dump();
|
||||
doDump();
|
||||
uptime = 0f;
|
||||
}else{
|
||||
((ItemBridgeBuild)other.build).incoming.add(tile.pos());
|
||||
@@ -301,20 +305,27 @@ public class ItemBridge extends Block{
|
||||
}
|
||||
}
|
||||
|
||||
public void doDump(){
|
||||
//allow dumping multiple times per frame
|
||||
dumpAccumulate();
|
||||
}
|
||||
|
||||
public void updateTransport(Building other){
|
||||
if(uptime >= 0.5f && timer(timerTransport, transportTime)){
|
||||
boolean any = false;
|
||||
transportCounter += edelta();
|
||||
while(transportCounter >= transportTime){
|
||||
Item item = items.take();
|
||||
if(item != null && other.acceptItem(this, item)){
|
||||
other.handleItem(this, item);
|
||||
cycleSpeed = Mathf.lerpDelta(cycleSpeed, 4f, 0.05f); //TODO this is kinda broken, because lerping only happens on a timer
|
||||
}else{
|
||||
cycleSpeed = Mathf.lerpDelta(cycleSpeed, 1f, 0.01f);
|
||||
if(item != null){
|
||||
items.add(item, 1);
|
||||
items.undoFlow(item);
|
||||
}
|
||||
any = true;
|
||||
}else if(item != null){
|
||||
items.add(item, 1);
|
||||
items.undoFlow(item);
|
||||
}
|
||||
transportCounter -= transportTime;
|
||||
}
|
||||
|
||||
cycleSpeed = Mathf.lerpDelta(cycleSpeed, any ? 4f : 1f, any ? 0.05f : 0.01f);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -14,6 +14,7 @@ import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.logic.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.meta.*;
|
||||
@@ -22,7 +23,7 @@ import static mindustry.Vars.*;
|
||||
|
||||
public class MassDriver extends Block{
|
||||
public float range;
|
||||
public float rotateSpeed = 0.04f;
|
||||
public float rotateSpeed = 5f;
|
||||
public float translation = 7f;
|
||||
public int minDistribute = 10;
|
||||
public float knockback = 4f;
|
||||
@@ -73,7 +74,7 @@ public class MassDriver extends Block{
|
||||
//check if a mass driver is selected while placing this driver
|
||||
if(!control.input.frag.config.isShown()) return;
|
||||
Building selected = control.input.frag.config.getSelectedTile();
|
||||
if(selected == null || !(selected.block instanceof MassDriver) || !(selected.within(x * tilesize, y * tilesize, range))) return;
|
||||
if(selected == null || selected.block != this || !selected.within(x * tilesize, y * tilesize, range)) return;
|
||||
|
||||
//if so, draw a dotted line towards it while it is in range
|
||||
float sin = Mathf.absin(Time.time, 6f, 1f);
|
||||
@@ -89,7 +90,7 @@ public class MassDriver extends Block{
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
public class DriverBulletData implements Poolable{
|
||||
public static class DriverBulletData implements Poolable{
|
||||
public MassDriverBuild from, to;
|
||||
public int[] items = new int[content.items().size];
|
||||
|
||||
@@ -105,9 +106,10 @@ public class MassDriver extends Block{
|
||||
public float rotation = 90;
|
||||
public float reload = 0f;
|
||||
public DriverState state = DriverState.idle;
|
||||
public OrderedSet<Tile> waitingShooters = new OrderedSet<>();
|
||||
//TODO use queue? this array usually holds about 3 shooters max anyway
|
||||
public OrderedSet<Building> waitingShooters = new OrderedSet<>();
|
||||
|
||||
public Tile currentShooter(){
|
||||
public Building currentShooter(){
|
||||
return waitingShooters.isEmpty() ? null : waitingShooters.first();
|
||||
}
|
||||
|
||||
@@ -125,9 +127,11 @@ public class MassDriver extends Block{
|
||||
reload = Mathf.clamp(reload - edelta() / reloadTime);
|
||||
}
|
||||
|
||||
var current = currentShooter();
|
||||
|
||||
//cleanup waiting shooters that are not valid
|
||||
if(!shooterValid(currentShooter())){
|
||||
waitingShooters.remove(currentShooter());
|
||||
if(current != null && !shooterValid(current)){
|
||||
waitingShooters.remove(current);
|
||||
}
|
||||
|
||||
//switch states
|
||||
@@ -142,7 +146,7 @@ public class MassDriver extends Block{
|
||||
|
||||
//dump when idle or accepting
|
||||
if(state == DriverState.idle || state == DriverState.accepting){
|
||||
dump();
|
||||
dumpAccumulate();
|
||||
}
|
||||
|
||||
//skip when there's no power
|
||||
@@ -158,7 +162,7 @@ public class MassDriver extends Block{
|
||||
}
|
||||
|
||||
//align to shooter rotation
|
||||
rotation = Mathf.slerpDelta(rotation, tile.angleTo(currentShooter()), rotateSpeed * efficiency());
|
||||
rotation = Angles.moveToward(rotation, tile.angleTo(currentShooter()), rotateSpeed * efficiency());
|
||||
}else if(state == DriverState.shooting){
|
||||
//if there's nothing to shoot at OR someone wants to shoot at this thing, bail
|
||||
if(!hasLink || (!waitingShooters.isEmpty() && (itemCapacity - items.total() >= minDistribute))){
|
||||
@@ -173,15 +177,15 @@ public class MassDriver extends Block{
|
||||
link.block.itemCapacity - link.items.total() >= minDistribute //must have minimum amount of space
|
||||
){
|
||||
MassDriverBuild other = (MassDriverBuild)link;
|
||||
other.waitingShooters.add(tile);
|
||||
other.waitingShooters.add(this);
|
||||
|
||||
if(reload <= 0.0001f){
|
||||
|
||||
//align to target location
|
||||
rotation = Mathf.slerpDelta(rotation, targetRotation, rotateSpeed * efficiency());
|
||||
rotation = Angles.moveToward(rotation, targetRotation, rotateSpeed * efficiency());
|
||||
|
||||
//fire when it's the first in the queue and angles are ready.
|
||||
if(other.currentShooter() == tile &&
|
||||
if(other.currentShooter() == this &&
|
||||
other.state == DriverState.accepting &&
|
||||
Angles.near(rotation, targetRotation, 2f) && Angles.near(other.rotation, targetRotation + 180f, 2f)){
|
||||
//actually fire
|
||||
@@ -189,7 +193,7 @@ public class MassDriver extends Block{
|
||||
float timeToArrive = Math.min(bulletLifetime, dst(other) / bulletSpeed);
|
||||
Time.run(timeToArrive, () -> {
|
||||
//remove waiting shooters, it's done firing
|
||||
other.waitingShooters.remove(tile);
|
||||
other.waitingShooters.remove(this);
|
||||
other.state = DriverState.idle;
|
||||
});
|
||||
//driver is immediately idle
|
||||
@@ -200,6 +204,12 @@ public class MassDriver extends Block{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public double sense(LAccess sensor){
|
||||
if(sensor == LAccess.progress) return Mathf.clamp(1f - reload / reloadTime);
|
||||
return super.sense(sensor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Draw.rect(baseRegion, x, y);
|
||||
@@ -222,9 +232,9 @@ public class MassDriver extends Block{
|
||||
Lines.stroke(1f);
|
||||
Drawf.circles(x, y, (tile.block().size / 2f + 1) * tilesize + sin - 2f, Pal.accent);
|
||||
|
||||
for(Tile shooter : waitingShooters){
|
||||
Drawf.circles(shooter.drawx(), shooter.drawy(), (tile.block().size / 2f + 1) * tilesize + sin - 2f, Pal.place);
|
||||
Drawf.arrow(shooter.drawx(), shooter.drawy(), x, y, size * tilesize + sin, 4f + sin, Pal.place);
|
||||
for(var shooter : waitingShooters){
|
||||
Drawf.circles(shooter.x, shooter.y, (tile.block().size / 2f + 1) * tilesize + sin - 2f, Pal.place);
|
||||
Drawf.arrow(shooter.x, shooter.y, x, y, size * tilesize + sin, 4f + sin, Pal.place);
|
||||
}
|
||||
|
||||
if(linkValid()){
|
||||
@@ -246,7 +256,7 @@ public class MassDriver extends Block{
|
||||
if(link == other.pos()){
|
||||
configure(-1);
|
||||
return false;
|
||||
}else if(other.block instanceof MassDriver && other.dst(tile) <= range && other.team == team){
|
||||
}else if(other.block == block && other.dst(tile) <= range && other.team == team){
|
||||
configure(other.pos());
|
||||
return false;
|
||||
}
|
||||
@@ -281,11 +291,8 @@ public class MassDriver extends Block{
|
||||
x + Angles.trnsx(angle, translation), y + Angles.trnsy(angle, translation),
|
||||
angle, -1f, bulletSpeed, bulletLifetime, data);
|
||||
|
||||
shootEffect.at(x + Angles.trnsx(angle, translation),
|
||||
y + Angles.trnsy(angle, translation), angle);
|
||||
|
||||
smokeEffect.at(x + Angles.trnsx(angle, translation),
|
||||
y + Angles.trnsy(angle, translation), angle);
|
||||
shootEffect.at(x + Angles.trnsx(angle, translation), y + Angles.trnsy(angle, translation), angle);
|
||||
smokeEffect.at(x + Angles.trnsx(angle, translation), y + Angles.trnsy(angle, translation), angle);
|
||||
|
||||
Effect.shake(shake, shake, this);
|
||||
|
||||
@@ -314,16 +321,13 @@ public class MassDriver extends Block{
|
||||
bullet.remove();
|
||||
}
|
||||
|
||||
protected boolean shooterValid(Tile other){
|
||||
if(other == null) return true;
|
||||
if(!(other.build instanceof MassDriverBuild entity)) return false;
|
||||
return entity.link == tile.pos() && tile.dst(other) <= range;
|
||||
protected boolean shooterValid(Building other){
|
||||
return other instanceof MassDriverBuild entity && other.consValid() && entity.block == block && entity.link == pos() && within(other, range);
|
||||
}
|
||||
|
||||
protected boolean linkValid(){
|
||||
if(link == -1) return false;
|
||||
Building link = world.build(this.link);
|
||||
return link instanceof MassDriverBuild && link.team == team && within(link, range);
|
||||
return world.build(this.link) instanceof MassDriverBuild other && other.block == block && other.team == team && within(other, range);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -352,8 +356,7 @@ public class MassDriver extends Block{
|
||||
public enum DriverState{
|
||||
idle, //nothing is shooting at this mass driver and it does not have any target
|
||||
accepting, //currently getting shot at, unload items
|
||||
shooting,
|
||||
unloading;
|
||||
shooting;
|
||||
|
||||
public static final DriverState[] all = values();
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.payloads.*;
|
||||
import mindustry.world.blocks.production.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
@@ -21,7 +20,7 @@ public class PayloadConveyor extends Block{
|
||||
public @Load("@-top") TextureRegion topRegion;
|
||||
public @Load("@-edge") TextureRegion edgeRegion;
|
||||
public Interp interp = Interp.pow5;
|
||||
public float payloadLimit = 2.5f;
|
||||
public float payloadLimit = 2.9f;
|
||||
|
||||
public PayloadConveyor(String name){
|
||||
super(name);
|
||||
@@ -51,6 +50,13 @@ public class PayloadConveyor extends Block{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStats(){
|
||||
super.setStats();
|
||||
|
||||
stats.add(Stat.payloadCapacity, (payloadLimit), StatUnit.blocksSquared);
|
||||
}
|
||||
|
||||
public class PayloadConveyorBuild extends Building{
|
||||
public @Nullable Payload item;
|
||||
public float progress, itemRotation, animation;
|
||||
@@ -59,6 +65,16 @@ public class PayloadConveyor extends Block{
|
||||
public boolean blocked;
|
||||
public int step = -1, stepAccepted = -1;
|
||||
|
||||
@Override
|
||||
public boolean canControlSelect(Player player){
|
||||
return this.item == null && !player.unit().spawnedByCore && player.unit().hitSize / tilesize <= payloadLimit && player.tileOn().build == this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onControlSelect(Player player){
|
||||
acceptPlayerPayload(player, p -> item = p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Payload takePayload(){
|
||||
Payload t = item;
|
||||
@@ -108,6 +124,9 @@ public class PayloadConveyor extends Block{
|
||||
progress = time() % moveTime;
|
||||
|
||||
updatePayload();
|
||||
if(item != null && next == null){
|
||||
PayloadBlock.pushOutput(item, progress / moveTime);
|
||||
}
|
||||
|
||||
//TODO nondeterministic input priority
|
||||
int curStep = curStep();
|
||||
@@ -283,7 +302,7 @@ public class PayloadConveyor extends Block{
|
||||
if(direction == rotation){
|
||||
return !blocked || next != null;
|
||||
}
|
||||
return PayloadAcceptor.blends(this, direction);
|
||||
return PayloadBlock.blends(this, direction);
|
||||
}
|
||||
|
||||
protected TextureRegion clipRegion(Rect bounds, Rect sprite, TextureRegion region){
|
||||
|
||||
@@ -7,6 +7,7 @@ import mindustry.annotations.Annotations.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.logic.*;
|
||||
import mindustry.world.blocks.payloads.*;
|
||||
|
||||
public class PayloadRouter extends PayloadConveyor{
|
||||
@@ -28,6 +29,7 @@ public class PayloadRouter extends PayloadConveyor{
|
||||
|
||||
public class PayloadRouterBuild extends PayloadConveyorBuild{
|
||||
public float smoothRot;
|
||||
public float controlTime = -1f;
|
||||
|
||||
@Override
|
||||
public void add(){
|
||||
@@ -36,7 +38,7 @@ public class PayloadRouter extends PayloadConveyor{
|
||||
}
|
||||
|
||||
public void pickNext(){
|
||||
if(item != null){
|
||||
if(item != null && controlTime <= 0f){
|
||||
int rotations = 0;
|
||||
do{
|
||||
rotation = (rotation + 1) % 4;
|
||||
@@ -47,6 +49,16 @@ public class PayloadRouter extends PayloadConveyor{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void control(LAccess type, double p1, double p2, double p3, double p4){
|
||||
super.control(type, p1, p2, p3, p4);
|
||||
if(type == LAccess.config){
|
||||
rotation = (int)p1;
|
||||
//when manually controlled, routers do not turn automatically for a while, same as turrets
|
||||
controlTime = Building.timeToUncontrol;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePayload(Building source, Payload payload){
|
||||
super.handlePayload(source, payload);
|
||||
@@ -62,6 +74,7 @@ public class PayloadRouter extends PayloadConveyor{
|
||||
public void updateTile(){
|
||||
super.updateTile();
|
||||
|
||||
controlTime -= Time.delta;
|
||||
smoothRot = Mathf.slerpDelta(smoothRot, rotdeg(), 0.2f);
|
||||
}
|
||||
|
||||
|
||||
@@ -145,7 +145,7 @@ public class StackConveyor extends Block implements Autotiler{
|
||||
//item
|
||||
float size = itemSize * Mathf.lerp(Math.min((float)items.total() / itemCapacity, 1), 1f, 0.4f);
|
||||
Drawf.shadow(Tmp.v1.x, Tmp.v1.y, size * 1.2f);
|
||||
Draw.rect(lastItem.icon(Cicon.medium), Tmp.v1.x, Tmp.v1.y, size, size, 0);
|
||||
Draw.rect(lastItem.fullIcon, Tmp.v1.x, Tmp.v1.y, size, size, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
46
core/src/mindustry/world/blocks/environment/Bush.java
Normal file
46
core/src/mindustry/world/blocks/environment/Bush.java
Normal file
@@ -0,0 +1,46 @@
|
||||
package mindustry.world.blocks.environment;
|
||||
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
public class Bush extends Prop{
|
||||
public @Load(value = "@-bot", fallback = "@") TextureRegion botRegion;
|
||||
public @Load(value = "@-center") TextureRegion centerRegion;
|
||||
|
||||
public int lobesMin = 7, lobesMax = 7;
|
||||
public float botAngle = 60f, origin = 0.1f;
|
||||
public float sclMin = 30f, sclMax = 50f, magMin = 5f, magMax = 15f, timeRange = 40f, spread = 0f;
|
||||
|
||||
static Rand rand = new Rand();
|
||||
|
||||
public Bush(String name){
|
||||
super(name);
|
||||
variants = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawBase(Tile tile){
|
||||
rand.setSeed(tile.pos());
|
||||
float offset = rand.random(180f);
|
||||
int lobes = rand.random(lobesMin, lobesMax);
|
||||
for(int i = 0; i < lobes; i++){
|
||||
float ba = i / (float)lobes * 360f + offset + rand.range(spread), angle = ba + Mathf.sin(Time.time + rand.random(0, timeRange), rand.random(sclMin, sclMax), rand.random(magMin, magMax));
|
||||
float w = region.width * Draw.scl, h = region.height * Draw.scl;
|
||||
var region = Angles.angleDist(ba, 225f) <= botAngle ? botRegion : this.region;
|
||||
|
||||
Draw.rect(region,
|
||||
tile.worldx() - Angles.trnsx(angle, origin) + w*0.5f, tile.worldy() - Angles.trnsy(angle, origin),
|
||||
w, h,
|
||||
origin*4f, h/2f,
|
||||
angle
|
||||
);
|
||||
}
|
||||
|
||||
if(centerRegion.found()){
|
||||
Draw.rect(centerRegion, tile.worldx(), tile.worldy());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import arc.*;
|
||||
import arc.audio.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.graphics.g2d.TextureAtlas.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
@@ -15,7 +14,6 @@ import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.graphics.MultiPacker.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
|
||||
@@ -50,8 +48,6 @@ public class Floor extends Block{
|
||||
public @Nullable Liquid liquidDrop = null;
|
||||
/** Multiplier for pumped liquids, used for deep water. */
|
||||
public float liquidMultiplier = 1f;
|
||||
/** item that drops from this block, used for drills */
|
||||
public @Nullable Item itemDrop = null;
|
||||
/** whether this block can be drowned in */
|
||||
public boolean isLiquid;
|
||||
/** if true, this block cannot be mined by players. useful for annoying things like sand. */
|
||||
@@ -119,7 +115,7 @@ public class Floor extends Block{
|
||||
if(wall == null) wall = Blocks.air;
|
||||
|
||||
if(decoration == Blocks.air){
|
||||
decoration = content.blocks().min(b -> b instanceof Boulder && b.minfo.mod == null && b.breakable ? mapColor.diff(b.mapColor) : Float.POSITIVE_INFINITY);
|
||||
decoration = content.blocks().min(b -> b instanceof Prop && b.minfo.mod == null && b.breakable ? mapColor.diff(b.mapColor) : Float.POSITIVE_INFINITY);
|
||||
}
|
||||
|
||||
if(isLiquid && walkEffect == Fx.none){
|
||||
@@ -134,7 +130,7 @@ public class Floor extends Block{
|
||||
@Override
|
||||
public void createIcons(MultiPacker packer){
|
||||
super.createIcons(packer);
|
||||
packer.add(PageType.editor, "editor-" + name, Core.atlas.getPixmap((AtlasRegion)icon(Cicon.full)).crop());
|
||||
packer.add(PageType.editor, "editor-" + name, Core.atlas.getPixmap(fullIcon).crop());
|
||||
|
||||
if(blendGroup != this){
|
||||
return;
|
||||
@@ -147,16 +143,13 @@ public class Floor extends Block{
|
||||
}
|
||||
}
|
||||
|
||||
Color color = new Color();
|
||||
Color color2 = new Color();
|
||||
PixmapRegion image = Core.atlas.getPixmap((AtlasRegion)icons()[0]);
|
||||
PixmapRegion image = Core.atlas.getPixmap(icons()[0]);
|
||||
PixmapRegion edge = Core.atlas.getPixmap("edge-stencil");
|
||||
Pixmap result = new Pixmap(edge.width, edge.height);
|
||||
|
||||
for(int x = 0; x < edge.width; x++){
|
||||
for(int y = 0; y < edge.height; y++){
|
||||
edge.getPixel(x, y, color);
|
||||
result.draw(x, y, color.mul(color2.set(image.getPixel(x % image.width, y % image.height))));
|
||||
result.set(x, y, Color.muli(edge.get(x, y), image.get(x % image.width, y % image.height)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@ import static mindustry.Vars.*;
|
||||
/**An overlay ore for a specific item type.*/
|
||||
public class OreBlock extends OverlayFloor{
|
||||
|
||||
public OreBlock(Item ore){
|
||||
super("ore-" + ore.name);
|
||||
public OreBlock(String name, Item ore){
|
||||
super(name);
|
||||
this.localizedName = ore.localizedName;
|
||||
this.itemDrop = ore;
|
||||
this.variants = 3;
|
||||
@@ -23,6 +23,10 @@ public class OreBlock extends OverlayFloor{
|
||||
this.useColor = true;
|
||||
}
|
||||
|
||||
public OreBlock(Item ore){
|
||||
this("ore-" + ore.name, ore);
|
||||
}
|
||||
|
||||
/** For mod use only!*/
|
||||
public OreBlock(String name){
|
||||
super(name);
|
||||
@@ -39,25 +43,20 @@ public class OreBlock extends OverlayFloor{
|
||||
@OverrideCallSuper
|
||||
public void createIcons(MultiPacker packer){
|
||||
for(int i = 0; i < variants; i++){
|
||||
Pixmap image = new Pixmap(32, 32);
|
||||
PixmapRegion shadow = Core.atlas.getPixmap(itemDrop.name + (i + 1));
|
||||
Pixmap image = shadow.crop();
|
||||
|
||||
int offset = image.getWidth() / tilesize - 1;
|
||||
Color color = new Color();
|
||||
int offset = image.width / tilesize - 1;
|
||||
int shadowColor = Color.rgba8888(0, 0, 0, 0.3f);
|
||||
|
||||
for(int x = 0; x < image.getWidth(); x++){
|
||||
for(int y = offset; y < image.getHeight(); y++){
|
||||
shadow.getPixel(x, y - offset, color);
|
||||
|
||||
if(color.a > 0.001f){
|
||||
color.set(0, 0, 0, 0.3f);
|
||||
image.draw(x, y, color);
|
||||
for(int x = 0; x < image.width; x++){
|
||||
for(int y = offset; y < image.height; y++){
|
||||
if(shadow.getA(x, y) == 0 && shadow.getA(x, y - offset) != 0){
|
||||
image.setRaw(x, y, shadowColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
image.draw(shadow);
|
||||
|
||||
packer.add(PageType.environment, name + (i + 1), image);
|
||||
packer.add(PageType.editor, "editor-" + name + (i + 1), image);
|
||||
|
||||
|
||||
@@ -3,26 +3,25 @@ package mindustry.world.blocks.environment;
|
||||
import arc.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
public class Boulder extends Block{
|
||||
public class Prop extends Block{
|
||||
public int variants;
|
||||
|
||||
public Boulder(String name){
|
||||
public Prop(String name){
|
||||
super(name);
|
||||
breakable = true;
|
||||
alwaysReplace = true;
|
||||
instantDeconstruct = true;
|
||||
|
||||
deconstructThreshold = 0.35f;
|
||||
breakEffect = Fx.breakProp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawBase(Tile tile){
|
||||
if(variants > 0){
|
||||
Draw.rect(variantRegions[Mathf.randomSeed(tile.pos(), 0, Math.max(0, variantRegions.length - 1))], tile.worldx(), tile.worldy());
|
||||
}else{
|
||||
Draw.rect(region, tile.worldx(), tile.worldy());
|
||||
}
|
||||
Draw.rect(variants > 0 ? variantRegions[Mathf.randomSeed(tile.pos(), 0, Math.max(0, variantRegions.length - 1))] : region, tile.worldx(), tile.worldy());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -0,0 +1,25 @@
|
||||
package mindustry.world.blocks.environment;
|
||||
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
public class StaticClusterWall extends StaticWall{
|
||||
public @Load(value = "@-cluster#", length = 1) TextureRegion[] clusters;
|
||||
|
||||
public StaticClusterWall(String name){
|
||||
super(name);
|
||||
variants = 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawBase(Tile tile){
|
||||
super.drawBase(tile);
|
||||
|
||||
if(Mathf.randomSeed(tile.pos(), 10) < 2){
|
||||
Draw.rect(clusters[0], tile.worldx(), tile.worldy(), Mathf.randomSeedRange(tile.pos() + 1, 180f));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,7 +10,7 @@ import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class StaticWall extends Boulder{
|
||||
public class StaticWall extends Prop{
|
||||
public @Load("@-large") TextureRegion large;
|
||||
public TextureRegion[][] split;
|
||||
|
||||
@@ -34,6 +34,11 @@ public class StaticWall extends Boulder{
|
||||
}else{
|
||||
Draw.rect(region, tile.worldx(), tile.worldy());
|
||||
}
|
||||
|
||||
//draw ore on top
|
||||
if(tile.overlay() instanceof WallOreBlock ore){
|
||||
ore.drawBase(tile);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,7 +2,6 @@ package mindustry.world.blocks.environment;
|
||||
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.graphics.*;
|
||||
@@ -15,16 +14,17 @@ public class TreeBlock extends Block{
|
||||
public TreeBlock(String name){
|
||||
super(name);
|
||||
solid = true;
|
||||
expanded = true;
|
||||
clipSize = 90;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawBase(Tile tile){
|
||||
|
||||
float x = tile.worldx(), y = tile.worldy();
|
||||
float rot = Mathf.randomSeed(tile.pos(), 0, 4) * 90 + Mathf.sin(Time.time + x, 50f, 0.5f) + Mathf.sin(Time.time - y, 65f, 0.9f) + Mathf.sin(Time.time + y - x, 85f, 0.9f);
|
||||
float w = region.width * Draw.scl, h = region.height * Draw.scl;
|
||||
float scl = 30f, mag = 0.2f;
|
||||
float
|
||||
x = tile.worldx(), y = tile.worldy(),
|
||||
rot = Mathf.randomSeed(tile.pos(), 0, 4) * 90 + Mathf.sin(Time.time + x, 50f, 0.5f) + Mathf.sin(Time.time - y, 65f, 0.9f) + Mathf.sin(Time.time + y - x, 85f, 0.9f),
|
||||
w = region.width * Draw.scl, h = region.height * Draw.scl,
|
||||
scl = 30f, mag = 0.2f;
|
||||
|
||||
if(shadow.found()){
|
||||
Draw.z(Layer.power - 1);
|
||||
@@ -32,15 +32,9 @@ public class TreeBlock extends Block{
|
||||
}
|
||||
|
||||
Draw.z(Layer.power + 1);
|
||||
Draw.rectv(region, x, y, w, h, rot, vec -> {
|
||||
vec.add(
|
||||
Mathf.sin(vec.y*3 + Time.time, scl, mag) + Mathf.sin(vec.x*3 - Time.time, 70, 0.8f),
|
||||
Mathf.cos(vec.x*3 + Time.time + 8, scl + 6f, mag * 1.1f) + Mathf.sin(vec.y*3 - Time.time, 50, 0.2f)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void tweak(Vec2 vec){
|
||||
|
||||
Draw.rectv(region, x, y, w, h, rot, vec -> vec.add(
|
||||
Mathf.sin(vec.y*3 + Time.time, scl, mag) + Mathf.sin(vec.x*3 - Time.time, 70, 0.8f),
|
||||
Mathf.cos(vec.x*3 + Time.time + 8, scl + 6f, mag * 1.1f) + Mathf.sin(vec.y*3 - Time.time, 50, 0.2f)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package mindustry.world.blocks.environment;
|
||||
|
||||
import mindustry.type.*;
|
||||
|
||||
/**An overlay ore that draws on top of walls. */
|
||||
public class WallOreBlock extends OreBlock{
|
||||
|
||||
public WallOreBlock(Item ore){
|
||||
super("wall-ore-" + ore.name, ore);
|
||||
}
|
||||
|
||||
//mods only
|
||||
public WallOreBlock(String name){
|
||||
super(name);
|
||||
}
|
||||
}
|
||||
30
core/src/mindustry/world/blocks/environment/WavingProp.java
Normal file
30
core/src/mindustry/world/blocks/environment/WavingProp.java
Normal file
@@ -0,0 +1,30 @@
|
||||
package mindustry.world.blocks.environment;
|
||||
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
public class WavingProp extends Prop{
|
||||
|
||||
public WavingProp(String name){
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawBase(Tile tile){
|
||||
var region = variants > 0 ? variantRegions[Mathf.randomSeed(tile.pos(), 0, Math.max(0, variantRegions.length - 1))] : this.region;
|
||||
|
||||
float
|
||||
x = tile.worldx(), y = tile.worldy(),
|
||||
rotmag = 3f, rotscl = 0.5f,
|
||||
rot = Mathf.randomSeedRange(tile.pos(), 20f) - 45 + Mathf.sin(Time.time + x, 50f * rotscl, 0.5f * rotmag) + Mathf.sin(Time.time - y, 65f * rotscl, 0.9f* rotmag) + Mathf.sin(Time.time + y - x, 85f * rotscl, 0.9f* rotmag),
|
||||
w = region.width * Draw.scl, h = region.height * Draw.scl,
|
||||
scl = 30f, mag = 0.3f;
|
||||
|
||||
Draw.rectv(region, x, y, w, h, rot, vec -> vec.add(
|
||||
Mathf.sin(vec.y*3 + Time.time, scl, mag) + Mathf.sin(vec.x*3 - Time.time, 70, 0.8f),
|
||||
Mathf.cos(vec.x*3 + Time.time + 8, scl + 6f, mag * 1.1f) + Mathf.sin(vec.y*3 - Time.time, 50, 0.2f)
|
||||
));
|
||||
}
|
||||
}
|
||||
24
core/src/mindustry/world/blocks/environment/WobbleProp.java
Normal file
24
core/src/mindustry/world/blocks/environment/WobbleProp.java
Normal file
@@ -0,0 +1,24 @@
|
||||
package mindustry.world.blocks.environment;
|
||||
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
public class WobbleProp extends Prop{
|
||||
public float wscl = 25f, wmag = 0.4f, wtscl = 1f, wmag2 = 1f;
|
||||
|
||||
public WobbleProp(String name){
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawBase(Tile tile){
|
||||
var region = variants > 0 ? variantRegions[Mathf.randomSeed(tile.pos(), 0, Math.max(0, variantRegions.length - 1))] : this.region;
|
||||
|
||||
Draw.rectv(region, tile.worldx(), tile.worldy(), region.width * Draw.scl, region.height * Draw.scl, 0, vec -> vec.add(
|
||||
Mathf.sin(vec.y*3 + Time.time, wscl, wmag) + Mathf.sin(vec.x*3 - Time.time, 70 * wtscl, 0.8f * wmag2),
|
||||
Mathf.cos(vec.x*3 + Time.time + 8, wscl + 6f, wmag * 1.1f) + Mathf.sin(vec.y*3 - Time.time, 50 * wtscl, 0.2f * wmag2)
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
package mindustry.world.blocks.experimental;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
import mindustry.world.blocks.payloads.*;
|
||||
import mindustry.world.blocks.production.*;
|
||||
import mindustry.world.consumers.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class BlockForge extends PayloadAcceptor{
|
||||
public float buildSpeed = 0.4f;
|
||||
public int minBlockSize = 1, maxBlockSize = 2;
|
||||
|
||||
public BlockForge(String name){
|
||||
super(name);
|
||||
|
||||
size = 3;
|
||||
update = true;
|
||||
outputsPayload = true;
|
||||
hasItems = true;
|
||||
configurable = true;
|
||||
hasPower = true;
|
||||
rotate = true;
|
||||
|
||||
config(Block.class, (BlockForgeBuild tile, Block block) -> {
|
||||
if(tile.recipe != block) tile.progress = 0f;
|
||||
tile.recipe = block;
|
||||
});
|
||||
|
||||
consumes.add(new ConsumeItemDynamic((BlockForgeBuild e) -> e.recipe != null ? e.recipe.requirements : ItemStack.empty));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureRegion[] icons(){
|
||||
return new TextureRegion[]{region, outRegion};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBars(){
|
||||
super.setBars();
|
||||
|
||||
bars.add("progress", (BlockForgeBuild entity) -> new Bar("bar.progress", Pal.ammo, () -> entity.recipe == null ? 0f : (entity.progress / entity.recipe.buildCost)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStats(){
|
||||
super.setStats();
|
||||
|
||||
stats.add(Stat.output, "@x@ ~ @x@", minBlockSize, minBlockSize, maxBlockSize, maxBlockSize);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void drawRequestRegion(BuildPlan req, Eachable<BuildPlan> list){
|
||||
Draw.rect(region, req.drawx(), req.drawy());
|
||||
Draw.rect(outRegion, req.drawx(), req.drawy(), req.rotation * 90);
|
||||
}
|
||||
|
||||
public class BlockForgeBuild extends PayloadAcceptorBuild<BuildPayload>{
|
||||
public @Nullable Block recipe;
|
||||
public float progress, time, heat;
|
||||
|
||||
@Override
|
||||
public boolean acceptItem(Building source, Item item){
|
||||
return items.get(item) < getMaximumAccepted(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaximumAccepted(Item item){
|
||||
if(recipe == null) return 0;
|
||||
for(ItemStack stack : recipe.requirements){
|
||||
if(stack.item == item) return stack.amount * 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptPayload(Building source, Payload payload){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
boolean produce = recipe != null && consValid() && payload == null;
|
||||
|
||||
if(produce){
|
||||
progress += buildSpeed * edelta();
|
||||
|
||||
if(progress >= recipe.buildCost){
|
||||
consume();
|
||||
payload = new BuildPayload(recipe, team);
|
||||
payVector.setZero();
|
||||
progress %= 1f;
|
||||
}
|
||||
}
|
||||
|
||||
heat = Mathf.lerpDelta(heat, Mathf.num(produce), 0.3f);
|
||||
time += heat * delta();
|
||||
|
||||
moveOutPayload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildConfiguration(Table table){
|
||||
Seq<Block> blocks = Vars.content.blocks().select(b -> b.isVisible() && b.size >= minBlockSize && b.size <= maxBlockSize);
|
||||
|
||||
ItemSelection.buildTable(table, blocks, () -> recipe, this::configure);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object config(){
|
||||
return recipe;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Draw.rect(region, x, y);
|
||||
Draw.rect(outRegion, x, y, rotdeg());
|
||||
|
||||
if(recipe != null){
|
||||
Draw.draw(Layer.blockOver, () -> Drawf.construct(this, recipe, 0, progress / recipe.buildCost, heat, time));
|
||||
}
|
||||
|
||||
drawPayload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawSelect(){
|
||||
if(recipe != null){
|
||||
float dx = x - size * tilesize/2f, dy = y + size * tilesize/2f;
|
||||
TextureRegion icon = recipe.icon(Cicon.medium);
|
||||
Draw.mixcol(Color.darkGray, 1f);
|
||||
//Fixes size because modded content icons are not scaled
|
||||
Draw.rect(icon, dx - 0.7f, dy - 1f, Draw.scl * Draw.xscl * 24f, Draw.scl * Draw.yscl * 24f);
|
||||
Draw.reset();
|
||||
Draw.rect(icon, dx, dy, Draw.scl * Draw.xscl * 24f, Draw.scl * Draw.yscl * 24f);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Writes write){
|
||||
super.write(write);
|
||||
write.s(recipe == null ? -1 : recipe.id);
|
||||
write.f(progress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(Reads read, byte revision){
|
||||
super.read(read, revision);
|
||||
recipe = Vars.content.block(read.s());
|
||||
progress = read.f();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,11 +9,10 @@ import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.blocks.payloads.*;
|
||||
import mindustry.world.blocks.production.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class BlockLoader extends PayloadAcceptor{
|
||||
public class BlockLoader extends PayloadBlock{
|
||||
public final int timerLoad = timers++;
|
||||
|
||||
public float loadTime = 2f;
|
||||
@@ -58,7 +57,7 @@ public class BlockLoader extends PayloadAcceptor{
|
||||
Draw.rect(topRegion, req.drawx(), req.drawy());
|
||||
}
|
||||
|
||||
public class BlockLoaderBuild extends PayloadAcceptorBuild<BuildPayload>{
|
||||
public class BlockLoaderBuild extends PayloadBlockBuild<BuildPayload>{
|
||||
|
||||
@Override
|
||||
public boolean acceptPayload(Building source, Payload payload){
|
||||
@@ -90,7 +89,6 @@ public class BlockLoader extends PayloadAcceptor{
|
||||
Draw.rect(outRegion, x, y, rotdeg());
|
||||
|
||||
Draw.z(Layer.blockOver);
|
||||
payRotation = rotdeg();
|
||||
drawPayload();
|
||||
|
||||
Draw.z(Layer.blockOver + 0.1f);
|
||||
|
||||
@@ -197,14 +197,15 @@ public class LogicBlock extends Block{
|
||||
public Seq<LogicLink> links = new Seq<>();
|
||||
public boolean checkedDuplicates = false;
|
||||
|
||||
public void readCompressed(byte[] data, boolean relative){
|
||||
DataInputStream stream = new DataInputStream(new InflaterInputStream(new ByteArrayInputStream(data)));
|
||||
/** Block of code to run after load. */
|
||||
public @Nullable Runnable loadBlock;
|
||||
|
||||
try{
|
||||
public void readCompressed(byte[] data, boolean relative){
|
||||
try(DataInputStream stream = new DataInputStream(new InflaterInputStream(new ByteArrayInputStream(data)))){
|
||||
int version = stream.read();
|
||||
|
||||
int bytelen = stream.readInt();
|
||||
if(bytelen > maxByteLen) throw new RuntimeException("Malformed logic data! Length: " + bytelen);
|
||||
if(bytelen > maxByteLen) throw new IOException("Malformed logic data! Length: " + bytelen);
|
||||
byte[] bytes = new byte[bytelen];
|
||||
stream.readFully(bytes);
|
||||
|
||||
@@ -243,6 +244,7 @@ public class LogicBlock extends Block{
|
||||
|
||||
updateCode(new String(bytes, charset));
|
||||
}catch(Exception ignored){
|
||||
//invalid logic doesn't matter here
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,11 +352,8 @@ public class LogicBlock extends Block{
|
||||
|
||||
executor.load(asm);
|
||||
}catch(Exception e){
|
||||
Log.err("Failed to compile logic program @", code);
|
||||
Log.err(e);
|
||||
|
||||
//handle malformed code and replace it with nothing
|
||||
executor.load("");
|
||||
executor.load(code = "");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -372,6 +371,12 @@ public class LogicBlock extends Block{
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
//load up code from read()
|
||||
if(loadBlock != null){
|
||||
loadBlock.run();
|
||||
loadBlock = null;
|
||||
}
|
||||
|
||||
executor.team = team;
|
||||
|
||||
if(!checkedDuplicates){
|
||||
@@ -447,9 +452,9 @@ public class LogicBlock extends Block{
|
||||
}
|
||||
|
||||
public Seq<LogicLink> relativeConnections(){
|
||||
Seq<LogicLink> copy = new Seq<>(links.size);
|
||||
for(LogicLink l : links){
|
||||
LogicLink c = l.copy();
|
||||
var copy = new Seq<LogicLink>(links.size);
|
||||
for(var l : links){
|
||||
var c = l.copy();
|
||||
c.x -= tileX();
|
||||
c.y -= tileY();
|
||||
copy.add(c);
|
||||
@@ -582,8 +587,7 @@ public class LogicBlock extends Block{
|
||||
//skip memory, it isn't used anymore
|
||||
read.skip(memory * 8);
|
||||
|
||||
updateCode(code, false, asm -> {
|
||||
|
||||
loadBlock = () -> updateCode(code, false, asm -> {
|
||||
//load up the variables that were stored
|
||||
for(int i = 0; i < varcount; i++){
|
||||
BVar dest = asm.getVar(names[i]);
|
||||
@@ -592,6 +596,7 @@ public class LogicBlock extends Block{
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
29
core/src/mindustry/world/blocks/payloads/BallisticSilo.java
Normal file
29
core/src/mindustry/world/blocks/payloads/BallisticSilo.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package mindustry.world.blocks.payloads;
|
||||
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.blocks.payloads.NuclearWarhead.*;
|
||||
|
||||
public class BallisticSilo extends PayloadBlock{
|
||||
|
||||
public BallisticSilo(String name){
|
||||
super(name);
|
||||
}
|
||||
|
||||
public class BallisticSiloBuild extends PayloadBlockBuild<BuildPayload>{
|
||||
@Override
|
||||
public boolean acceptPayload(Building source, Payload payload){
|
||||
return this.payload == null && payload instanceof BuildPayload b && b.build instanceof NuclearWarheadBuild;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
moveInPayload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
super.draw();
|
||||
drawPayload();
|
||||
}
|
||||
}
|
||||
}
|
||||
89
core/src/mindustry/world/blocks/payloads/BlockForge.java
Normal file
89
core/src/mindustry/world/blocks/payloads/BlockForge.java
Normal file
@@ -0,0 +1,89 @@
|
||||
package mindustry.world.blocks.payloads;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
/** Configurable BlockProducer variant. */
|
||||
public class BlockForge extends BlockProducer{
|
||||
public float buildSpeed = 0.4f;
|
||||
public int minBlockSize = 1, maxBlockSize = 2;
|
||||
|
||||
public BlockForge(String name){
|
||||
super(name);
|
||||
|
||||
size = 3;
|
||||
configurable = true;
|
||||
|
||||
config(Block.class, (BlockForgeBuild tile, Block block) -> {
|
||||
if(tile.recipe != block) tile.progress = 0f;
|
||||
if(canProduce(block)){
|
||||
tile.recipe = block;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStats(){
|
||||
super.setStats();
|
||||
|
||||
stats.add(Stat.output, "@x@ ~ @x@", minBlockSize, minBlockSize, maxBlockSize, maxBlockSize);
|
||||
}
|
||||
|
||||
public boolean canProduce(Block b){
|
||||
return b.isVisible() && b.size >= minBlockSize && b.size <= maxBlockSize;
|
||||
}
|
||||
|
||||
public class BlockForgeBuild extends BlockProducerBuild{
|
||||
public @Nullable Block recipe;
|
||||
|
||||
@Override
|
||||
public @Nullable Block recipe(){
|
||||
return recipe;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildConfiguration(Table table){
|
||||
ItemSelection.buildTable(table, content.blocks().select(BlockForge.this::canProduce), () -> recipe, this::configure);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object config(){
|
||||
return recipe;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawSelect(){
|
||||
if(recipe != null){
|
||||
float dx = x - size * tilesize/2f, dy = y + size * tilesize/2f;
|
||||
TextureRegion icon = recipe.uiIcon;
|
||||
Draw.mixcol(Color.darkGray, 1f);
|
||||
//Fixes size because modded content icons are not scaled
|
||||
Draw.rect(icon, dx - 0.7f, dy - 1f, Draw.scl * Draw.xscl * 24f, Draw.scl * Draw.yscl * 24f);
|
||||
Draw.reset();
|
||||
Draw.rect(icon, dx, dy, Draw.scl * Draw.xscl * 24f, Draw.scl * Draw.yscl * 24f);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Writes write){
|
||||
super.write(write);
|
||||
write.s(recipe == null ? -1 : recipe.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(Reads read, byte revision){
|
||||
super.read(read, revision);
|
||||
recipe = Vars.content.block(read.s());
|
||||
}
|
||||
}
|
||||
}
|
||||
143
core/src/mindustry/world/blocks/payloads/BlockProducer.java
Normal file
143
core/src/mindustry/world/blocks/payloads/BlockProducer.java
Normal file
@@ -0,0 +1,143 @@
|
||||
package mindustry.world.blocks.payloads;
|
||||
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.consumers.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
/** Generic building that produces other buildings. */
|
||||
public abstract class BlockProducer extends PayloadBlock{
|
||||
public float buildSpeed = 0.4f;
|
||||
|
||||
public BlockProducer(String name){
|
||||
super(name);
|
||||
|
||||
size = 3;
|
||||
update = true;
|
||||
outputsPayload = true;
|
||||
hasItems = true;
|
||||
hasPower = true;
|
||||
rotate = true;
|
||||
|
||||
consumes.add(new ConsumeItemDynamic((BlockProducerBuild e) -> e.recipe() != null ? e.recipe().requirements : ItemStack.empty));
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureRegion[] icons(){
|
||||
return new TextureRegion[]{region, outRegion};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBars(){
|
||||
super.setBars();
|
||||
|
||||
bars.add("progress", (BlockProducerBuild entity) -> new Bar("bar.progress", Pal.ammo, () -> entity.recipe() == null ? 0f : (entity.progress / entity.recipe().buildCost)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawRequestRegion(BuildPlan req, Eachable<BuildPlan> list){
|
||||
Draw.rect(region, req.drawx(), req.drawy());
|
||||
Draw.rect(outRegion, req.drawx(), req.drawy(), req.rotation * 90);
|
||||
}
|
||||
|
||||
public abstract class BlockProducerBuild extends PayloadBlockBuild<BuildPayload>{
|
||||
public float progress, time, heat;
|
||||
|
||||
public abstract @Nullable Block recipe();
|
||||
|
||||
@Override
|
||||
public boolean acceptItem(Building source, Item item){
|
||||
return items.get(item) < getMaximumAccepted(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaximumAccepted(Item item){
|
||||
if(recipe() == null) return 0;
|
||||
for(ItemStack stack : recipe().requirements){
|
||||
if(stack.item == item) return stack.amount * 2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptPayload(Building source, Payload payload){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
var recipe = recipe();
|
||||
boolean produce = recipe != null && consValid() && payload == null;
|
||||
|
||||
if(produce){
|
||||
progress += buildSpeed * edelta();
|
||||
|
||||
if(progress >= recipe.buildCost){
|
||||
consume();
|
||||
payload = new BuildPayload(recipe, team);
|
||||
payVector.setZero();
|
||||
progress %= 1f;
|
||||
}
|
||||
}
|
||||
|
||||
heat = Mathf.lerpDelta(heat, Mathf.num(produce), 0.15f);
|
||||
time += heat * delta();
|
||||
|
||||
moveOutPayload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Draw.rect(region, x, y);
|
||||
Draw.rect(outRegion, x, y, rotdeg());
|
||||
|
||||
var recipe = recipe();
|
||||
if(recipe != null){
|
||||
Drawf.shadow(x, y, recipe.size * tilesize * 2f, progress / recipe.buildCost);
|
||||
Draw.draw(Layer.blockBuilding, () -> {
|
||||
Draw.color(Pal.accent);
|
||||
|
||||
for(TextureRegion region : recipe.getGeneratedIcons()){
|
||||
Shaders.blockbuild.region = region;
|
||||
Shaders.blockbuild.progress = progress / recipe.buildCost;
|
||||
|
||||
Draw.rect(region, x, y, recipe.rotate ? rotdeg() : 0);
|
||||
Draw.flush();
|
||||
}
|
||||
|
||||
Draw.color();
|
||||
});
|
||||
Draw.z(Layer.blockBuilding + 1);
|
||||
Draw.color(Pal.accent, heat);
|
||||
|
||||
Lines.lineAngleCenter(x + Mathf.sin(time, 10f, Vars.tilesize / 2f * recipe.size + 1f), y, 90, recipe.size * Vars.tilesize + 1f);
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
drawPayload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Writes write){
|
||||
super.write(write);
|
||||
write.f(progress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(Reads read, byte revision){
|
||||
super.read(read, revision);
|
||||
progress = read.f();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import arc.util.io.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
@@ -34,6 +33,16 @@ public class BuildPayload implements Payload{
|
||||
build.dropped();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float x(){
|
||||
return build.x;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float y(){
|
||||
return build.y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float size(){
|
||||
return build.block.size * tilesize;
|
||||
@@ -55,11 +64,11 @@ public class BuildPayload implements Payload{
|
||||
@Override
|
||||
public void draw(){
|
||||
Drawf.shadow(build.x, build.y, build.block.size * tilesize * 2f);
|
||||
Draw.rect(build.block.icon(Cicon.full), build.x, build.y);
|
||||
Draw.rect(build.block.fullIcon, build.x, build.y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureRegion icon(Cicon icon){
|
||||
return block().icon(icon);
|
||||
public TextureRegion icon(){
|
||||
return block().fullIcon;
|
||||
}
|
||||
}
|
||||
|
||||
18
core/src/mindustry/world/blocks/payloads/NuclearWarhead.java
Normal file
18
core/src/mindustry/world/blocks/payloads/NuclearWarhead.java
Normal file
@@ -0,0 +1,18 @@
|
||||
package mindustry.world.blocks.payloads;
|
||||
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
public class NuclearWarhead extends Block{
|
||||
public float radius = 100f;
|
||||
|
||||
public NuclearWarhead(String name){
|
||||
super(name);
|
||||
solid = true;
|
||||
update = true;
|
||||
}
|
||||
|
||||
public class NuclearWarheadBuild extends Building{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,16 @@
|
||||
package mindustry.world.blocks.payloads;
|
||||
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public interface Payload{
|
||||
public interface Payload extends Position{
|
||||
int payloadUnit = 0, payloadBlock = 1;
|
||||
|
||||
/** sets this payload's position on the map. */
|
||||
@@ -22,6 +22,9 @@ public interface Payload{
|
||||
/** @return hitbox size of the payload. */
|
||||
float size();
|
||||
|
||||
float x();
|
||||
float y();
|
||||
|
||||
/** @return whether this payload was dumped. */
|
||||
default boolean dump(){
|
||||
return false;
|
||||
@@ -41,7 +44,17 @@ public interface Payload{
|
||||
void write(Writes write);
|
||||
|
||||
/** @return icon describing the contents. */
|
||||
TextureRegion icon(Cicon icon);
|
||||
TextureRegion icon();
|
||||
|
||||
@Override
|
||||
default float getX(){
|
||||
return x();
|
||||
}
|
||||
|
||||
@Override
|
||||
default float getY(){
|
||||
return y();
|
||||
}
|
||||
|
||||
static void write(@Nullable Payload payload, Writes write){
|
||||
if(payload == null){
|
||||
|
||||
233
core/src/mindustry/world/blocks/payloads/PayloadBlock.java
Normal file
233
core/src/mindustry/world/blocks/payloads/PayloadBlock.java
Normal file
@@ -0,0 +1,233 @@
|
||||
package mindustry.world.blocks.payloads;
|
||||
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class PayloadBlock extends Block{
|
||||
public float payloadSpeed = 0.5f, payloadRotateSpeed = 5f;
|
||||
|
||||
public @Load(value = "@-top", fallback = "factory-top-@size") TextureRegion topRegion;
|
||||
public @Load(value = "@-out", fallback = "factory-out-@size") TextureRegion outRegion;
|
||||
public @Load(value = "@-in", fallback = "factory-in-@size") TextureRegion inRegion;
|
||||
|
||||
public PayloadBlock(String name){
|
||||
super(name);
|
||||
|
||||
update = true;
|
||||
sync = true;
|
||||
}
|
||||
|
||||
public static boolean blends(Building build, int direction){
|
||||
int size = build.block.size;
|
||||
int trns = build.block.size/2 + 1;
|
||||
Building accept = build.nearby(Geometry.d4(direction).x * trns, Geometry.d4(direction).y * trns);
|
||||
return accept != null &&
|
||||
accept.block.outputsPayload &&
|
||||
|
||||
//if size is the same, block must either be facing this one, or not be rotating
|
||||
((accept.block.size == size
|
||||
&& Math.abs(accept.tileX() - build.tileX()) % size == 0 //check alignment
|
||||
&& Math.abs(accept.tileY() - build.tileY()) % size == 0
|
||||
&& ((accept.block.rotate && accept.tileX() + Geometry.d4(accept.rotation).x * size == build.tileX() && accept.tileY() + Geometry.d4(accept.rotation).y * size == build.tileY())
|
||||
|| !accept.block.rotate
|
||||
|| !accept.block.outputFacing)) ||
|
||||
|
||||
//if the other block is smaller, check alignment
|
||||
(accept.block.size != size &&
|
||||
(accept.rotation % 2 == 0 ? //check orientation; make sure it's aligned properly with this block.
|
||||
Math.abs(accept.y - build.y) <= Math.abs(size * tilesize - accept.block.size * tilesize)/2f : //check Y alignment
|
||||
Math.abs(accept.x - build.x) <= Math.abs(size * tilesize - accept.block.size * tilesize)/2f //check X alignment
|
||||
)) && (!accept.block.rotate || accept.front() == build || !accept.block.outputFacing) //make sure it's facing this block
|
||||
);
|
||||
}
|
||||
|
||||
public static void pushOutput(Payload payload, float progress){
|
||||
float thresh = 0.55f;
|
||||
if(progress >= thresh){
|
||||
boolean legStep = payload instanceof UnitPayload u && u.unit.type.allowLegStep;
|
||||
float size = payload.size(), radius = size/2f, x = payload.x(), y = payload.y(), scl = Mathf.clamp(((progress - thresh) / (1f - thresh)) * 1.1f);
|
||||
|
||||
Groups.unit.intersect(x - size/2f, y - size/2f, size, size, u -> {
|
||||
float dst = u.dst(payload);
|
||||
float rs = radius + u.hitSize/2f;
|
||||
if(u.isGrounded() && u.type.allowLegStep == legStep && dst < rs){
|
||||
u.vel.add(Tmp.v1.set(u.x - x, u.y - y).setLength(Math.min(rs - dst, 1f)).scl(scl));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public class PayloadBlockBuild<T extends Payload> extends Building{
|
||||
public @Nullable T payload;
|
||||
//TODO redundant; already stored in payload?
|
||||
public Vec2 payVector = new Vec2();
|
||||
public float payRotation;
|
||||
public boolean carried;
|
||||
|
||||
public boolean acceptUnitPayload(Unit unit){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canControlSelect(Player player){
|
||||
return !player.unit().spawnedByCore && this.payload == null && acceptUnitPayload(player.unit()) && player.tileOn().build == this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onControlSelect(Player player){
|
||||
float x = player.x, y = player.y;
|
||||
acceptPlayerPayload(player, p -> payload = (T)p);
|
||||
this.payVector.set(x, y).sub(this).clamp(-size * tilesize / 2f, -size * tilesize / 2f, size * tilesize / 2f, size * tilesize / 2f);
|
||||
this.payRotation = player.unit().rotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptPayload(Building source, Payload payload){
|
||||
return this.payload == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePayload(Building source, Payload payload){
|
||||
this.payload = (T)payload;
|
||||
this.payVector.set(source).sub(this).clamp(-size * tilesize / 2f, -size * tilesize / 2f, size * tilesize / 2f, size * tilesize / 2f);
|
||||
this.payRotation = payload.rotation();
|
||||
|
||||
updatePayload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Payload getPayload(){
|
||||
return payload;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pickedUp(){
|
||||
carried = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawTeamTop(){
|
||||
carried = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Payload takePayload(){
|
||||
T t = payload;
|
||||
payload = null;
|
||||
return t;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoved(){
|
||||
super.onRemoved();
|
||||
if(payload != null && !carried) payload.dump();
|
||||
}
|
||||
|
||||
public boolean blends(int direction){
|
||||
return PayloadBlock.blends(this, direction);
|
||||
}
|
||||
|
||||
public void updatePayload(){
|
||||
if(payload != null){
|
||||
payload.set(x + payVector.x, y + payVector.y, payRotation);
|
||||
}
|
||||
}
|
||||
|
||||
/** @return true if the payload is in position. */
|
||||
public boolean moveInPayload(){
|
||||
return moveInPayload(true);
|
||||
}
|
||||
|
||||
/** @return true if the payload is in position. */
|
||||
public boolean moveInPayload(boolean rotate){
|
||||
if(payload == null) return false;
|
||||
|
||||
updatePayload();
|
||||
|
||||
if(rotate){
|
||||
payRotation = Angles.moveToward(payRotation, rotate ? rotdeg() : 90f, payloadRotateSpeed * edelta());
|
||||
}
|
||||
payVector.approach(Vec2.ZERO, payloadSpeed * delta());
|
||||
|
||||
return hasArrived();
|
||||
}
|
||||
|
||||
public void moveOutPayload(){
|
||||
if(payload == null) return;
|
||||
|
||||
updatePayload();
|
||||
|
||||
Vec2 dest = Tmp.v1.trns(rotdeg(), size * tilesize/2f);
|
||||
|
||||
payRotation = Angles.moveToward(payRotation, rotdeg(), payloadRotateSpeed * edelta());
|
||||
payVector.approach(dest, payloadSpeed * delta());
|
||||
|
||||
Building front = front();
|
||||
boolean canDump = front == null || !front.tile().solid();
|
||||
boolean canMove = front != null && (front.block.outputsPayload || front.block.acceptsPayload);
|
||||
|
||||
if(canDump && !canMove){
|
||||
pushOutput(payload, 1f - (payVector.dst(dest) / (size * tilesize / 2f)));
|
||||
}
|
||||
|
||||
if(payVector.within(dest, 0.001f)){
|
||||
payVector.clamp(-size * tilesize / 2f, -size * tilesize / 2f, size * tilesize / 2f, size * tilesize / 2f);
|
||||
|
||||
if(canMove){
|
||||
if(movePayload(payload)){
|
||||
payload = null;
|
||||
}
|
||||
}else if(canDump){
|
||||
dumpPayload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void dumpPayload(){
|
||||
if(payload.dump()){
|
||||
payload = null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasArrived(){
|
||||
return payVector.isZero(0.01f);
|
||||
}
|
||||
|
||||
public void drawPayload(){
|
||||
if(payload != null){
|
||||
updatePayload();
|
||||
|
||||
Draw.z(Layer.blockOver);
|
||||
payload.draw();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Writes write){
|
||||
super.write(write);
|
||||
|
||||
write.f(payVector.x);
|
||||
write.f(payVector.y);
|
||||
write.f(payRotation);
|
||||
Payload.write(payload, write);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(Reads read, byte revision){
|
||||
super.read(read, revision);
|
||||
|
||||
payVector.set(read.f(), read.f());
|
||||
payRotation = read.f();
|
||||
payload = Payload.read(read);
|
||||
}
|
||||
}
|
||||
}
|
||||
481
core/src/mindustry/world/blocks/payloads/PayloadMassDriver.java
Normal file
481
core/src/mindustry/world/blocks/payloads/PayloadMassDriver.java
Normal file
@@ -0,0 +1,481 @@
|
||||
package mindustry.world.blocks.payloads;
|
||||
|
||||
import arc.audio.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.logic.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
import static mindustry.world.blocks.payloads.PayloadMassDriver.PayloadDriverState.*;
|
||||
|
||||
public class PayloadMassDriver extends PayloadBlock{
|
||||
public float range = 100f;
|
||||
public float rotateSpeed = 2f;
|
||||
public float length = 89 / 8f;
|
||||
public float knockback = 5f;
|
||||
public float reloadTime = 30f;
|
||||
public float chargeTime = 100f;
|
||||
public float maxPayloadSize = 3;
|
||||
public float grabWidth = 8f, grabHeight = 11/4f;
|
||||
public Effect shootEffect = Fx.shootBig2;
|
||||
public Effect smokeEffect = Fx.shootPayloadDriver;
|
||||
public Effect receiveEffect = Fx.payloadReceive;
|
||||
public Sound shootSound = Sounds.shootBig;
|
||||
public float shake = 3f;
|
||||
|
||||
public Effect transferEffect = new Effect(11f, 300f, e -> {
|
||||
if(!(e.data instanceof PayloadMassDriverData data)) return;
|
||||
Tmp.v1.set(data.x, data.y).lerp(data.ox, data.oy, Interp.sineIn.apply(e.fin()));
|
||||
data.payload.set(Tmp.v1.x, Tmp.v1.y, e.rotation);
|
||||
data.payload.draw();
|
||||
}).layer(Layer.flyingUnitLow - 1);
|
||||
|
||||
public @Load("@-base") TextureRegion baseRegion;
|
||||
public @Load("@-cap") TextureRegion capRegion;
|
||||
public @Load("@-left") TextureRegion leftRegion;
|
||||
public @Load("@-right") TextureRegion rightRegion;
|
||||
public @Load("@-cap-outline") TextureRegion capOutlineRegion;
|
||||
public @Load("@-left-outline") TextureRegion leftOutlineRegion;
|
||||
public @Load("@-right-outline") TextureRegion rightOutlineRegion;
|
||||
public @Load("bridge-arrow") TextureRegion arrow;
|
||||
|
||||
public PayloadMassDriver(String name){
|
||||
super(name);
|
||||
update = true;
|
||||
solid = true;
|
||||
configurable = true;
|
||||
hasPower = true;
|
||||
outlineIcon = true;
|
||||
sync = true;
|
||||
rotate = true;
|
||||
outputsPayload = true;
|
||||
|
||||
//point2 is relative
|
||||
config(Point2.class, (PayloadDriverBuild tile, Point2 point) -> tile.link = Point2.pack(point.x + tile.tileX(), point.y + tile.tileY()));
|
||||
config(Integer.class, (PayloadDriverBuild tile, Integer point) -> tile.link = point);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStats(){
|
||||
super.setStats();
|
||||
|
||||
stats.add(Stat.payloadCapacity, maxPayloadSize, StatUnit.blocksSquared);
|
||||
stats.add(Stat.reload, 60f / (chargeTime + reloadTime), StatUnit.seconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureRegion[] icons(){
|
||||
return new TextureRegion[]{baseRegion, outRegion, region};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawRequestRegion(BuildPlan req, Eachable<BuildPlan> list){
|
||||
Draw.rect(baseRegion, req.drawx(), req.drawy());
|
||||
Draw.rect(topRegion, req.drawx(), req.drawy());
|
||||
Draw.rect(outRegion, req.drawx(), req.drawy(), req.rotation * 90);
|
||||
Draw.rect(region, req.drawx(), req.drawy());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawPlace(int x, int y, int rotation, boolean valid){
|
||||
super.drawPlace(x, y, rotation, valid);
|
||||
|
||||
Drawf.dashCircle(x * tilesize, y * tilesize, range, Pal.accent);
|
||||
|
||||
//check if a mass driver is selected while placing this driver
|
||||
if(!control.input.frag.config.isShown()) return;
|
||||
Building selected = control.input.frag.config.getSelectedTile();
|
||||
if(selected == null || selected.block != this || !selected.within(x * tilesize, y * tilesize, range)) return;
|
||||
|
||||
//if so, draw a dotted line towards it while it is in range
|
||||
float sin = Mathf.absin(Time.time, 6f, 1f);
|
||||
Tmp.v1.set(x * tilesize + offset, y * tilesize + offset).sub(selected.x, selected.y).limit((size / 2f + 1) * tilesize + sin + 0.5f);
|
||||
float x2 = x * tilesize - Tmp.v1.x, y2 = y * tilesize - Tmp.v1.y,
|
||||
x1 = selected.x + Tmp.v1.x, y1 = selected.y + Tmp.v1.y;
|
||||
int segs = (int)(selected.dst(x * tilesize, y * tilesize) / tilesize);
|
||||
|
||||
Lines.stroke(4f, Pal.gray);
|
||||
Lines.dashLine(x1, y1, x2, y2, segs);
|
||||
Lines.stroke(2f, Pal.placing);
|
||||
Lines.dashLine(x1, y1, x2, y2, segs);
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureRegion[] makeIconRegions(){
|
||||
return new TextureRegion[]{leftRegion, rightRegion, capRegion};
|
||||
}
|
||||
|
||||
public class PayloadDriverBuild extends PayloadBlockBuild<Payload>{
|
||||
public int link = -1;
|
||||
public float turretRotation = 90;
|
||||
public float reload = 0f, charge = 0f;
|
||||
public float targetSize = grabWidth*2f, curSize = targetSize;
|
||||
public float payLength = 0f;
|
||||
public boolean loaded;
|
||||
public boolean charging;
|
||||
public PayloadDriverState state = idle;
|
||||
public Queue<Building> waitingShooters = new Queue<>();
|
||||
public Payload recPayload;
|
||||
|
||||
public Building currentShooter(){
|
||||
return waitingShooters.isEmpty() ? null : waitingShooters.first();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
Building link = world.build(this.link);
|
||||
boolean hasLink = linkValid();
|
||||
|
||||
//discharge when charging isn't happening
|
||||
if(!charging){
|
||||
charge -= Time.delta * 10f;
|
||||
if(charge < 0) charge = 0f;
|
||||
}
|
||||
|
||||
curSize = Mathf.lerpDelta(curSize, targetSize, 0.05f);
|
||||
targetSize = grabWidth*2f;
|
||||
|
||||
if(payload != null){
|
||||
targetSize = payload.size();
|
||||
}
|
||||
|
||||
charging = false;
|
||||
|
||||
if(hasLink){
|
||||
this.link = link.pos();
|
||||
}
|
||||
|
||||
//reload regardless of state
|
||||
reload -= edelta() / reloadTime;
|
||||
if(reload < 0) reload = 0f;
|
||||
|
||||
var current = currentShooter();
|
||||
|
||||
//cleanup waiting shooters that are not valid
|
||||
if(current != null &&
|
||||
!(
|
||||
current instanceof PayloadDriverBuild entity &&
|
||||
entity.consValid() && entity.block == block &&
|
||||
entity.link == pos() && within(current, range)
|
||||
)){
|
||||
waitingShooters.removeFirst();
|
||||
}
|
||||
|
||||
//switch states
|
||||
if(state == idle){
|
||||
//start accepting when idle and there's space
|
||||
if(!waitingShooters.isEmpty() && payload == null){
|
||||
state = accepting;
|
||||
}else if(hasLink){ //switch to shooting if there's a valid link.
|
||||
state = shooting;
|
||||
}
|
||||
}
|
||||
|
||||
//dump when idle or accepting
|
||||
if((state == idle || state == accepting) && payload != null){
|
||||
if(loaded){
|
||||
payLength -= payloadSpeed * delta();
|
||||
if(payLength <= 0f){
|
||||
loaded = false;
|
||||
payVector.setZero();
|
||||
payRotation = Angles.moveToward(payRotation, turretRotation + 180f, payloadRotateSpeed * delta());
|
||||
}
|
||||
}else{
|
||||
moveOutPayload();
|
||||
}
|
||||
}
|
||||
|
||||
//skip when there's no power
|
||||
if(!consValid()){
|
||||
return;
|
||||
}
|
||||
|
||||
if(state == accepting){
|
||||
//if there's nothing shooting at this or items are full, bail out
|
||||
if(currentShooter() == null || payload != null){
|
||||
state = idle;
|
||||
return;
|
||||
}
|
||||
|
||||
if(currentShooter().getPayload() != null){
|
||||
targetSize = recPayload == null ? currentShooter().getPayload().size() : recPayload.size();
|
||||
}
|
||||
|
||||
//align to shooter rotation
|
||||
turretRotation = Angles.moveToward(turretRotation, tile.angleTo(currentShooter()), rotateSpeed * efficiency());
|
||||
}else if(state == shooting){
|
||||
//if there's nothing to shoot at OR someone wants to shoot at this thing, bail
|
||||
if(!hasLink || (!waitingShooters.isEmpty() && payload == null)){
|
||||
state = idle;
|
||||
return;
|
||||
}
|
||||
|
||||
float targetRotation = tile.angleTo(link);
|
||||
boolean movedOut = false;
|
||||
|
||||
payRotation = Angles.moveToward(payRotation, turretRotation, payloadRotateSpeed * delta());
|
||||
if(loaded){
|
||||
float loadLength = length - reload*knockback;
|
||||
payLength += payloadSpeed * delta();
|
||||
if(payLength >= loadLength){
|
||||
payLength = loadLength;
|
||||
movedOut = true;
|
||||
}
|
||||
}else if(moveInPayload()){
|
||||
payLength = 0f;
|
||||
loaded = true;
|
||||
}
|
||||
|
||||
//make sure payload firing can happen
|
||||
if(movedOut && payload != null && link.getPayload() == null){
|
||||
var other = (PayloadDriverBuild)link;
|
||||
|
||||
if(!other.waitingShooters.contains(this)){
|
||||
other.waitingShooters.addLast(this);
|
||||
}
|
||||
|
||||
if(reload <= 0){
|
||||
//align to target location
|
||||
turretRotation = Angles.moveToward(turretRotation, targetRotation, rotateSpeed * efficiency());
|
||||
|
||||
//fire when it's the first in the queue and angles are ready.
|
||||
if(other.currentShooter() == this &&
|
||||
other.state == accepting &&
|
||||
other.reload <= 0f &&
|
||||
Angles.within(turretRotation, targetRotation, 1f) && Angles.within(other.turretRotation, targetRotation + 180f, 1f)){
|
||||
charge += edelta();
|
||||
charging = true;
|
||||
|
||||
if(charge >= chargeTime){
|
||||
float cx = Angles.trnsx(turretRotation, length), cy = Angles.trnsy(turretRotation, length);
|
||||
|
||||
//effects
|
||||
shootEffect.at(x + cx, y + cy, turretRotation);
|
||||
smokeEffect.at(x, y, turretRotation);
|
||||
|
||||
Effect.shake(shake, shake, this);
|
||||
shootSound.at(this, Mathf.random(0.9f, 1.1f));
|
||||
transferEffect.at(x + cx, y + cy, turretRotation, new PayloadMassDriverData(x + cx, y + cy, other.x - cx, other.y - cy, payload));
|
||||
Payload pay = payload;
|
||||
other.recPayload = payload;
|
||||
|
||||
Time.run(transferEffect.lifetime, () -> {
|
||||
receiveEffect.at(other.x - cx/2f, other.y - cy/2f, other.turretRotation);
|
||||
Effect.shake(shake, shake, this);
|
||||
|
||||
//transfer payload
|
||||
other.reload = 1f;
|
||||
other.handlePayload(this, pay);
|
||||
other.payVector.set(-cx, -cy);
|
||||
other.payRotation = turretRotation;
|
||||
other.payLength = length;
|
||||
other.loaded = true;
|
||||
other.updatePayload();
|
||||
other.recPayload = null;
|
||||
|
||||
if(other.waitingShooters.size != 0 && other.waitingShooters.first() == this){
|
||||
other.waitingShooters.removeFirst();
|
||||
}
|
||||
other.state = idle;
|
||||
});
|
||||
|
||||
//reset state after shooting immediately
|
||||
payload = null;
|
||||
payLength = 0f;
|
||||
loaded = false;
|
||||
state = idle;
|
||||
reload = 1f;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public double sense(LAccess sensor){
|
||||
if(sensor == LAccess.progress) return Mathf.clamp(1f - reload / reloadTime);
|
||||
return super.sense(sensor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatePayload(){
|
||||
if(payload != null){
|
||||
if(loaded){
|
||||
payload.set(x + Angles.trnsx(turretRotation, payLength), y + Angles.trnsy(turretRotation, payLength), payRotation);
|
||||
}else{
|
||||
payload.set(x + payVector.x, y + payVector.y, payRotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
float
|
||||
tx = x + Angles.trnsx(turretRotation + 180f, reload * knockback),
|
||||
ty = y + Angles.trnsy(turretRotation + 180f, reload * knockback), r = turretRotation - 90;
|
||||
|
||||
Draw.rect(baseRegion, x, y);
|
||||
|
||||
//draw input
|
||||
for(int i = 0; i < 4; i++){
|
||||
if(blends(i) && i != rotation){
|
||||
Draw.rect(inRegion, x, y, (i * 90) - 180);
|
||||
}
|
||||
}
|
||||
|
||||
Draw.rect(outRegion, x, y, rotdeg());
|
||||
|
||||
if(payload != null){
|
||||
updatePayload();
|
||||
|
||||
Draw.z(loaded ? Layer.blockOver + 0.2f : Layer.blockOver);
|
||||
payload.draw();
|
||||
}
|
||||
|
||||
Draw.z(Layer.blockOver + 0.1f);
|
||||
Draw.rect(topRegion, x, y);
|
||||
|
||||
Draw.z(Layer.turret);
|
||||
//TODO
|
||||
Drawf.shadow(region, tx - (size / 2f), ty - (size / 2f), r);
|
||||
|
||||
Tmp.v1.trns(turretRotation, 0, -(curSize/2f - grabWidth));
|
||||
Tmp.v2.trns(rotation, -Math.max(curSize/2f - grabHeight - length, 0f), 0f);
|
||||
float rx = tx + Tmp.v1.x + Tmp.v2.x, ry = ty + Tmp.v1.y + Tmp.v2.y;
|
||||
float lx = tx - Tmp.v1.x + Tmp.v2.x, ly = ty - Tmp.v1.y + Tmp.v2.y;
|
||||
|
||||
Draw.rect(capOutlineRegion, tx, ty, r);
|
||||
Draw.rect(leftOutlineRegion, lx, ly, r);
|
||||
Draw.rect(rightOutlineRegion, rx, ry, r);
|
||||
|
||||
Draw.rect(leftRegion, lx, ly, r);
|
||||
Draw.rect(rightRegion, rx, ry, r);
|
||||
Draw.rect(capRegion, tx, ty, r);
|
||||
|
||||
Draw.z(Layer.effect);
|
||||
|
||||
if(charge > 0 && linkValid()){
|
||||
Building link = world.build(this.link);
|
||||
|
||||
float fin = Interp.pow2Out.apply(charge / chargeTime), fout = 1f-fin, len = length*1.8f, w = curSize/2f + 7f*fout;
|
||||
Vec2 right = Tmp.v1.trns(turretRotation, len, w);
|
||||
Vec2 left = Tmp.v2.trns(turretRotation, len, -w);
|
||||
|
||||
Lines.stroke(fin * 1.2f, Pal.accent);
|
||||
Lines.line(x + left.x, y + left.y, link.x - right.x, link.y - right.y);
|
||||
Lines.line(x + right.x, y + right.y, link.x - left.x, link.y - left.y);
|
||||
|
||||
for(int i = 0; i < 4; i++){
|
||||
Tmp.v3.set(x, y).lerp(link.x, link.y, 0.5f + (i - 2) * 0.1f);
|
||||
Draw.scl(fin * 1.1f);
|
||||
Draw.rect(arrow, Tmp.v3.x, Tmp.v3.y, turretRotation);
|
||||
Draw.scl();
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawConfigure(){
|
||||
float sin = Mathf.absin(Time.time, 6f, 1f);
|
||||
|
||||
Draw.color(Pal.accent);
|
||||
Lines.stroke(1f);
|
||||
Drawf.circles(x, y, (tile.block().size / 2f + 1) * tilesize + sin - 2f, Pal.accent);
|
||||
|
||||
for(var shooter : waitingShooters){
|
||||
Drawf.circles(shooter.x, shooter.y, (tile.block().size / 2f + 1) * tilesize + sin - 2f, Pal.place);
|
||||
Drawf.arrow(shooter.x, shooter.y, x, y, size * tilesize + sin, 4f + sin, Pal.place);
|
||||
}
|
||||
|
||||
if(linkValid()){
|
||||
Building target = world.build(link);
|
||||
Drawf.circles(target.x, target.y, (target.block().size / 2f + 1) * tilesize + sin - 2f, Pal.place);
|
||||
Drawf.arrow(x, y, target.x, target.y, size * tilesize + sin, 4f + sin);
|
||||
}
|
||||
|
||||
Drawf.dashCircle(x, y, range, Pal.accent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onConfigureTileTapped(Building other){
|
||||
if(this == other){
|
||||
configure(-1);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(link == other.pos()){
|
||||
configure(-1);
|
||||
return false;
|
||||
}else if(other.block instanceof PayloadMassDriver && other.dst(tile) <= range && other.team == team){
|
||||
configure(other.pos());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptPayload(Building source, Payload payload){
|
||||
return super.acceptPayload(source, payload) && payload.size() <= maxPayloadSize * tilesize;
|
||||
}
|
||||
|
||||
protected boolean linkValid(){
|
||||
return link != -1 && world.build(this.link) instanceof PayloadDriverBuild other && other.block == block && other.team == team && within(other, range);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Point2 config(){
|
||||
return Point2.unpack(link).sub(tile.x, tile.y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Writes write){
|
||||
super.write(write);
|
||||
write.i(link);
|
||||
write.f(turretRotation);
|
||||
write.b((byte)state.ordinal());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(Reads read, byte revision){
|
||||
super.read(read, revision);
|
||||
link = read.i();
|
||||
turretRotation = read.f();
|
||||
state = PayloadDriverState.all[read.b()];
|
||||
}
|
||||
}
|
||||
|
||||
public static class PayloadMassDriverData{
|
||||
public float x, y, ox, oy;
|
||||
public Payload payload;
|
||||
|
||||
public PayloadMassDriverData(float x, float y, float ox, float oy, Payload payload){
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.ox = ox;
|
||||
this.oy = oy;
|
||||
this.payload = payload;
|
||||
}
|
||||
}
|
||||
|
||||
public enum PayloadDriverState{
|
||||
idle, accepting, shooting;
|
||||
|
||||
public static final PayloadDriverState[] all = values();
|
||||
}
|
||||
}
|
||||
135
core/src/mindustry/world/blocks/payloads/PayloadSource.java
Normal file
135
core/src/mindustry/world/blocks/payloads/PayloadSource.java
Normal file
@@ -0,0 +1,135 @@
|
||||
package mindustry.world.blocks.payloads;
|
||||
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
/** Generic building that produces other buildings. */
|
||||
public class PayloadSource extends PayloadBlock{
|
||||
|
||||
public PayloadSource(String name){
|
||||
super(name);
|
||||
|
||||
size = 3;
|
||||
update = true;
|
||||
outputsPayload = true;
|
||||
hasPower = true;
|
||||
rotate = true;
|
||||
configurable = true;
|
||||
|
||||
config(Block.class, (PayloadSourceBuild build, Block block) -> {
|
||||
if(canProduce(block) && build.block != block){
|
||||
build.block = block;
|
||||
build.unit = null;
|
||||
build.payload = null;
|
||||
build.scl = 0f;
|
||||
}
|
||||
});
|
||||
|
||||
config(UnitType.class, (PayloadSourceBuild build, UnitType unit) -> {
|
||||
if(canProduce(unit) && build.unit != unit){
|
||||
build.unit = unit;
|
||||
build.block = null;
|
||||
build.payload = null;
|
||||
build.scl = 0f;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureRegion[] icons(){
|
||||
return new TextureRegion[]{region, outRegion, topRegion};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawRequestRegion(BuildPlan req, Eachable<BuildPlan> list){
|
||||
Draw.rect(region, req.drawx(), req.drawy());
|
||||
Draw.rect(outRegion, req.drawx(), req.drawy(), req.rotation * 90);
|
||||
Draw.rect(topRegion, req.drawx(), req.drawy());
|
||||
}
|
||||
|
||||
public boolean canProduce(Block b){
|
||||
return b.isVisible() && b.size < size;
|
||||
}
|
||||
|
||||
public boolean canProduce(UnitType t){
|
||||
return !t.isHidden();
|
||||
}
|
||||
|
||||
public class PayloadSourceBuild extends PayloadBlockBuild<Payload>{
|
||||
public UnitType unit;
|
||||
public Block block;
|
||||
public float scl;
|
||||
|
||||
@Override
|
||||
public void buildConfiguration(Table table){
|
||||
ItemSelection.buildTable(table,
|
||||
content.blocks().select(PayloadSource.this::canProduce).<UnlockableContent>as()
|
||||
.and(content.units().select(PayloadSource.this::canProduce).as()),
|
||||
() -> (UnlockableContent)config(), this::configure);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object config(){
|
||||
return unit == null ? block : unit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptPayload(Building source, Payload payload){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
if(payload == null){
|
||||
scl = 0f;
|
||||
if(unit != null){
|
||||
payload = new UnitPayload(unit.create(team));
|
||||
}else if(block != null){
|
||||
payload = new BuildPayload(block, team);
|
||||
}
|
||||
payVector.setZero();
|
||||
payRotation = rotdeg();
|
||||
}
|
||||
scl = Mathf.lerpDelta(scl, 1f, 0.1f);
|
||||
|
||||
moveOutPayload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Draw.rect(region, x, y);
|
||||
Draw.rect(outRegion, x, y, rotdeg());
|
||||
Draw.rect(topRegion, x, y);
|
||||
|
||||
Draw.scl(scl);
|
||||
drawPayload();
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Writes write){
|
||||
super.write(write);
|
||||
write.s(unit == null ? -1 : unit.id);
|
||||
write.s(block == null ? -1 : block.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(Reads read, byte revision){
|
||||
super.read(read, revision);
|
||||
unit = Vars.content.unit(read.s());
|
||||
block = Vars.content.block(read.s());
|
||||
}
|
||||
}
|
||||
}
|
||||
57
core/src/mindustry/world/blocks/payloads/PayloadVoid.java
Normal file
57
core/src/mindustry/world/blocks/payloads/PayloadVoid.java
Normal file
@@ -0,0 +1,57 @@
|
||||
package mindustry.world.blocks.payloads;
|
||||
|
||||
import arc.audio.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
|
||||
public class PayloadVoid extends PayloadBlock{
|
||||
public Effect incinerateEffect = Fx.blastExplosion;
|
||||
public Sound incinerateSound = Sounds.bang;
|
||||
|
||||
public PayloadVoid(String name){
|
||||
super(name);
|
||||
|
||||
outputsPayload = false;
|
||||
acceptsPayload = true;
|
||||
update = true;
|
||||
rotate = false;
|
||||
size = 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureRegion[] icons(){
|
||||
return new TextureRegion[]{region, topRegion};
|
||||
}
|
||||
|
||||
public class BlockLoaderBuild extends PayloadBlockBuild<Payload>{
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Draw.rect(region, x, y);
|
||||
|
||||
//draw input
|
||||
for(int i = 0; i < 4; i++){
|
||||
if(blends(i)){
|
||||
Draw.rect(inRegion, x, y, (i * 90) - 180);
|
||||
}
|
||||
}
|
||||
|
||||
Draw.rect(topRegion, x, y);
|
||||
|
||||
Draw.z(Layer.blockOver);
|
||||
drawPayload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
if(moveInPayload(false) && cons.valid()){
|
||||
payload = null;
|
||||
incinerateEffect.at(this);
|
||||
incinerateSound.at(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,6 @@ import mindustry.entities.EntityCollisions.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
public class UnitPayload implements Payload{
|
||||
public static final float deactiveDuration = 40f;
|
||||
@@ -38,6 +36,16 @@ public class UnitPayload implements Payload{
|
||||
unit.rotation = rotation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float x(){
|
||||
return unit.x;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float y(){
|
||||
return unit.y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float rotation(){
|
||||
return unit.rotation;
|
||||
@@ -71,6 +79,11 @@ public class UnitPayload implements Payload{
|
||||
if(!nearEmpty) return false;
|
||||
}
|
||||
|
||||
//cannnot dump when there's a lot of overlap going on
|
||||
if(!unit.type.flying && Units.count(unit.x, unit.y, unit.physicSize(), o -> o.isGrounded() && (o.type.allowLegStep == unit.type.allowLegStep)) > 0){
|
||||
return false;
|
||||
}
|
||||
|
||||
//no client dumping
|
||||
if(Vars.net.client()) return true;
|
||||
|
||||
@@ -87,8 +100,8 @@ public class UnitPayload implements Payload{
|
||||
//TODO should not happen
|
||||
if(unit.type == null) return;
|
||||
|
||||
Drawf.shadow(unit.x, unit.y, 20);
|
||||
Draw.rect(unit.type.icon(Cicon.full), unit.x, unit.y, unit.rotation - 90);
|
||||
unit.type.drawSoftShadow(unit);
|
||||
Draw.rect(unit.type.fullIcon, unit.x, unit.y, unit.rotation - 90);
|
||||
unit.type.drawCell(unit);
|
||||
|
||||
//draw warning
|
||||
@@ -106,7 +119,7 @@ public class UnitPayload implements Payload{
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureRegion icon(Cicon icon){
|
||||
return unit.type.icon(icon);
|
||||
public TextureRegion icon(){
|
||||
return unit.type.fullIcon;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,13 @@ public class LightBlock extends Block{
|
||||
config(Integer.class, (LightBuild tile, Integer value) -> tile.color = value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
lightRadius = radius;
|
||||
emitLight = true;
|
||||
super.init();
|
||||
}
|
||||
|
||||
public class LightBuild extends Building{
|
||||
public int color = Pal.accent.rgba();
|
||||
public float smoothTime = 1f;
|
||||
@@ -67,7 +74,7 @@ public class LightBlock extends Block{
|
||||
|
||||
@Override
|
||||
public void drawLight(){
|
||||
Drawf.light(team, x, y, radius * Math.min(smoothTime, 2f), Tmp.c1.set(color), brightness * efficiency());
|
||||
Drawf.light(team, x, y, lightRadius * Math.min(smoothTime, 2f), Tmp.c1.set(color), brightness * efficiency());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -30,6 +30,7 @@ public class NuclearReactor extends PowerGenerator{
|
||||
public Color lightColor = Color.valueOf("7f19ea");
|
||||
public Color coolColor = new Color(1, 1, 1, 0f);
|
||||
public Color hotColor = Color.valueOf("ff9575a3");
|
||||
public Effect explodeEffect = Fx.reactorExplosion;
|
||||
/** ticks to consume 1 fuel */
|
||||
public float itemDuration = 120;
|
||||
/** heating per frame * fullness */
|
||||
@@ -135,26 +136,9 @@ public class NuclearReactor extends PowerGenerator{
|
||||
if((fuel < 5 && heat < 0.5f) || !state.rules.reactorExplosions) return;
|
||||
|
||||
Effect.shake(6f, 16f, x, y);
|
||||
Fx.nuclearShockwave.at(x, y);
|
||||
for(int i = 0; i < 6; i++){
|
||||
Time.run(Mathf.random(40), () -> Fx.nuclearcloud.at(x, y));
|
||||
}
|
||||
|
||||
Damage.damage(x, y, explosionRadius * tilesize, explosionDamage * 4);
|
||||
|
||||
for(int i = 0; i < 20; i++){
|
||||
Time.run(Mathf.random(50), () -> {
|
||||
tr.rnd(Mathf.random(40f));
|
||||
Fx.explosion.at(tr.x + x, tr.y + y);
|
||||
});
|
||||
}
|
||||
|
||||
for(int i = 0; i < 70; i++){
|
||||
Time.run(Mathf.random(80), () -> {
|
||||
tr.rnd(Mathf.random(120f));
|
||||
Fx.nuclearsmoke.at(tr.x + x, tr.y + y);
|
||||
});
|
||||
}
|
||||
explodeEffect.at(x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -35,7 +35,7 @@ public class PowerDiode extends Block{
|
||||
|
||||
@Override
|
||||
public void drawRequestRegion(BuildPlan req, Eachable<BuildPlan> list){
|
||||
Draw.rect(icon(Cicon.full), req.drawx(), req.drawy());
|
||||
Draw.rect(fullIcon, req.drawx(), req.drawy());
|
||||
Draw.rect(arrow, req.drawx(), req.drawy(), !rotate ? 0 : req.rotation * 90);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package mindustry.world.blocks.production;
|
||||
|
||||
import arc.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
/** A crafter that gains efficiency from attribute tiles. */
|
||||
public class AttributeCrafter extends GenericCrafter{
|
||||
public Attribute attribute = Attribute.heat;
|
||||
public float baseEfficiency = 1f;
|
||||
public float boostScale = 1f;
|
||||
public float maxBoost = 1f;
|
||||
|
||||
public AttributeCrafter(String name){
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawPlace(int x, int y, int rotation, boolean valid){
|
||||
drawPlaceText(Core.bundle.format("bar.efficiency",
|
||||
(int)((baseEfficiency + Math.min(maxBoost, boostScale * sumAttribute(attribute, x, y))) * 100f)), x, y, valid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBars(){
|
||||
super.setBars();
|
||||
|
||||
bars.add("efficiency", entity ->
|
||||
new Bar(() ->
|
||||
Core.bundle.format("bar.efficiency", (int)(entity.efficiency() * 100)),
|
||||
() -> Pal.lightOrange,
|
||||
entity::efficiency));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStats(){
|
||||
super.setStats();
|
||||
|
||||
stats.add(Stat.affinities, attribute, boostScale);
|
||||
}
|
||||
|
||||
public class AttributeCrafterBuild extends GenericCrafterBuild{
|
||||
public float attrsum;
|
||||
|
||||
@Override
|
||||
public float efficiency(){
|
||||
return (baseEfficiency + Math.min(maxBoost, boostScale * attrsum)) * super.efficiency();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProximityUpdate(){
|
||||
super.onProximityUpdate();
|
||||
|
||||
attrsum = sumAttribute(attribute, tile.x, tile.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,58 +1,25 @@
|
||||
package mindustry.world.blocks.production;
|
||||
|
||||
import arc.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.meta.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
|
||||
/** A smelter that gains efficiency from attribute tiles. */
|
||||
public class AttributeSmelter extends GenericSmelter{
|
||||
public Attribute attribute = Attribute.heat;
|
||||
public float baseEfficiency = 1f;
|
||||
public float boostScale = 1f;
|
||||
/** @deprecated use AttributeCrafter instead, this is only a transition class. No flame effects are drawn, to encourage transition! */
|
||||
@Deprecated
|
||||
public class AttributeSmelter extends AttributeCrafter{
|
||||
//parameters are kept for compatibility but deliberately unused
|
||||
public Color flameColor = Color.valueOf("ffc999");
|
||||
public @Load("@-top") TextureRegion topRegion;
|
||||
//compat
|
||||
public float maxHeatBoost = 1f;
|
||||
|
||||
public AttributeSmelter(String name){
|
||||
super(name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawPlace(int x, int y, int rotation, boolean valid){
|
||||
drawPlaceText(Core.bundle.format("bar.efficiency",
|
||||
(int)((baseEfficiency + Math.min(maxHeatBoost, boostScale * sumAttribute(attribute, x, y))) * 100f)), x, y, valid);
|
||||
}
|
||||
//unused, kept for compatibility
|
||||
@Deprecated
|
||||
public class AttributeSmelterBuild extends AttributeCrafterBuild{
|
||||
|
||||
@Override
|
||||
public void setBars(){
|
||||
super.setBars();
|
||||
|
||||
bars.add("efficiency", entity ->
|
||||
new Bar(() ->
|
||||
Core.bundle.format("bar.efficiency", (int)(entity.efficiency() * 100)),
|
||||
() -> Pal.lightOrange,
|
||||
entity::efficiency));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStats(){
|
||||
super.setStats();
|
||||
|
||||
stats.add(Stat.affinities, attribute, boostScale);
|
||||
}
|
||||
|
||||
public class AttributeSmelterBuild extends SmelterBuild{
|
||||
public float attrsum;
|
||||
|
||||
@Override
|
||||
public float efficiency(){
|
||||
return (baseEfficiency + Math.min(maxHeatBoost, boostScale * attrsum)) * super.efficiency();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProximityUpdate(){
|
||||
super.onProximityUpdate();
|
||||
|
||||
attrsum = sumAttribute(attribute, tile.x, tile.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
254
core/src/mindustry/world/blocks/production/BeamDrill.java
Normal file
254
core/src/mindustry/world/blocks/production/BeamDrill.java
Normal file
@@ -0,0 +1,254 @@
|
||||
package mindustry.world.blocks.production;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class BeamDrill extends Block{
|
||||
public @Load("minelaser") TextureRegion laser;
|
||||
public @Load("minelaser-end") TextureRegion laserEnd;
|
||||
public @Load("@-top") TextureRegion topRegion;
|
||||
|
||||
public float drillTime = 200f;
|
||||
public int range = 5;
|
||||
public int tier = 1;
|
||||
public float laserWidth = 0.7f;
|
||||
/** Effect randomly played while drilling. */
|
||||
public Effect updateEffect = Fx.mineSmall;
|
||||
|
||||
public BeamDrill(String name){
|
||||
super(name);
|
||||
|
||||
hasItems = true;
|
||||
rotate = true;
|
||||
update = true;
|
||||
solid = true;
|
||||
drawArrow = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
clipSize = Math.max(clipSize, size * tilesize + (range + 1) * tilesize);
|
||||
super.init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean outputsItems(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean rotatedOutput(int x, int y){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureRegion[] icons(){
|
||||
return new TextureRegion[]{region, topRegion};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawRequestRegion(BuildPlan req, Eachable<BuildPlan> list){
|
||||
Draw.rect(region, req.drawx(), req.drawy());
|
||||
Draw.rect(topRegion, req.drawx(), req.drawy(), req.rotation * 90);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawPlace(int x, int y, int rotation, boolean valid){
|
||||
Item item = null;
|
||||
boolean multiple = false;
|
||||
int count = 0;
|
||||
|
||||
for(int i = 0; i < size; i++){
|
||||
getLaserPos(x, y, rotation, i, Tmp.p1);
|
||||
|
||||
int j = 0;
|
||||
Item found = null;
|
||||
for(; j < range; j++){
|
||||
int rx = Tmp.p1.x + Geometry.d4x(rotation)*j, ry = Tmp.p1.y + Geometry.d4y(rotation)*j;
|
||||
Tile other = world.tile(rx, ry);
|
||||
if(other != null){
|
||||
if(other.solid()){
|
||||
Item drop = other.wallDrop();
|
||||
if(drop != null && drop.hardness <= tier){
|
||||
found = drop;
|
||||
count ++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(found != null){
|
||||
//check if multiple items will be drilled
|
||||
if(item != found && item != null){
|
||||
multiple = true;
|
||||
}
|
||||
item = found;
|
||||
}
|
||||
|
||||
int len = Math.min(j, range - 1);
|
||||
Drawf.dashLine(found == null ? Pal.remove : Pal.placing,
|
||||
Tmp.p1.x * tilesize,
|
||||
Tmp.p1.y *tilesize,
|
||||
(Tmp.p1.x + Geometry.d4x(rotation)*len) * tilesize,
|
||||
(Tmp.p1.y + Geometry.d4y(rotation)*len) * tilesize
|
||||
);
|
||||
}
|
||||
|
||||
if(item != null){
|
||||
float width = drawPlaceText(Core.bundle.formatFloat("bar.drillspeed", 60f / drillTime * count, 2), x, y, valid);
|
||||
if(!multiple){
|
||||
float dx = x * tilesize + offset - width/2f - 4f, dy = y * tilesize + offset + size * tilesize / 2f + 5, s = iconSmall / 4f;
|
||||
Draw.mixcol(Color.darkGray, 1f);
|
||||
Draw.rect(item.fullIcon, dx, dy - 1, s, s);
|
||||
Draw.reset();
|
||||
Draw.rect(item.fullIcon, dx, dy, s, s);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void getLaserPos(int tx, int ty, int rotation, int i, Point2 out){
|
||||
int cornerX = tx - (size-1)/2, cornerY = ty - (size-1)/2, s = size;
|
||||
switch(rotation){
|
||||
case 0 -> out.set(cornerX + s, cornerY + i);
|
||||
case 1 -> out.set(cornerX + i, cornerY + s);
|
||||
case 2 -> out.set(cornerX - 1, cornerY + i);
|
||||
case 3 -> out.set(cornerX + i, cornerY - 1);
|
||||
}
|
||||
}
|
||||
|
||||
public class BeamDrillBuild extends Building{
|
||||
public Tile[] facing = new Tile[size];
|
||||
public Point2[] lasers = new Point2[size];
|
||||
public @Nullable Item lastItem;
|
||||
|
||||
public float time;
|
||||
public float warmup;
|
||||
|
||||
@Override
|
||||
public void drawSelect(){
|
||||
|
||||
if(lastItem != null){
|
||||
float dx = x - size * tilesize/2f, dy = y + size * tilesize/2f, s = iconSmall / 4f;
|
||||
Draw.mixcol(Color.darkGray, 1f);
|
||||
Draw.rect(lastItem.fullIcon, dx, dy - 1, s, s);
|
||||
Draw.reset();
|
||||
Draw.rect(lastItem.fullIcon, dx, dy, s, s);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
super.updateTile();
|
||||
|
||||
if(lasers[0] == null) updateLasers();
|
||||
boolean cons = shouldConsume();
|
||||
|
||||
warmup = Mathf.lerpDelta(warmup, Mathf.num(consValid()), 0.1f);
|
||||
lastItem = null;
|
||||
boolean multiple = false;
|
||||
|
||||
//update facing tiles
|
||||
for(int p = 0; p < size; p++){
|
||||
Point2 l = lasers[p];
|
||||
Tile dest = null;
|
||||
for(int i = 0; i < range; i++){
|
||||
int rx = l.x + Geometry.d4x(rotation)*i, ry = l.y + Geometry.d4y(rotation)*i;
|
||||
Tile other = world.tile(rx, ry);
|
||||
if(other != null){
|
||||
if(other.solid()){
|
||||
Item drop = other.wallDrop();
|
||||
if(drop != null && drop.hardness <= tier){
|
||||
if(lastItem != drop && lastItem != null){
|
||||
multiple = true;
|
||||
}
|
||||
lastItem = drop;
|
||||
dest = other;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
facing[p] = dest;
|
||||
if(cons && dest != null && Mathf.chanceDelta(0.05 * warmup)){
|
||||
updateEffect.at(dest.worldx() + Mathf.range(4f), dest.worldy() + Mathf.range(4f), dest.wallDrop().color);
|
||||
}
|
||||
}
|
||||
|
||||
//when multiple items are present, count that as no item
|
||||
if(multiple){
|
||||
lastItem = null;
|
||||
}
|
||||
|
||||
time += edelta();
|
||||
|
||||
if(time >= drillTime){
|
||||
for(Tile tile : facing){
|
||||
Item drop = tile == null ? null : tile.wallDrop();
|
||||
if(items.total() < itemCapacity && drop != null){
|
||||
items.add(drop, 1);
|
||||
}
|
||||
}
|
||||
time %= drillTime;
|
||||
}
|
||||
|
||||
if(timer(timerDump, dumpTime)){
|
||||
dump();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldConsume(){
|
||||
return items.total() < itemCapacity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Draw.rect(block.region, x, y);
|
||||
Draw.rect(topRegion, x, y, rotdeg());
|
||||
|
||||
Draw.z(Layer.power - 1);
|
||||
var dir = Geometry.d4(rotation);
|
||||
|
||||
for(int i = 0; i < size; i++){
|
||||
Tile face = facing[i];
|
||||
if(face != null){
|
||||
Point2 p = lasers[i];
|
||||
Drawf.laser(team, laser, laserEnd, (p.x - dir.x/2f) * tilesize, (p.y - dir.y/2f) * tilesize,
|
||||
face.worldx() - (dir.x/2f)*(tilesize), face.worldy() - (dir.y/2f)*(tilesize),
|
||||
(laserWidth + Mathf.absin(Time.time + i*4 + (id%20)*6, 3f, 0.07f)) * warmup);
|
||||
}
|
||||
}
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProximityUpdate(){
|
||||
//when rotated.
|
||||
updateLasers();
|
||||
}
|
||||
|
||||
void updateLasers(){
|
||||
for(int i = 0; i < size; i++){
|
||||
if(lasers[i] == null) lasers[i] = new Point2();
|
||||
getLaserPos(tileX(), tileY(), rotation, i, lasers[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,20 @@
|
||||
package mindustry.world.blocks.production;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
/**
|
||||
* @deprecated use GenericCrafter or AttributeCrafter with a DrawCultivator instead.
|
||||
* WARNING: If you switch from a class that used Cultivator to a GenericCrafter, make sure you set legacyReadWarmup to true! Failing to do so will break saves.
|
||||
* This class has been gutted of its behavior.
|
||||
* */
|
||||
@Deprecated
|
||||
public class Cultivator extends GenericCrafter{
|
||||
//fields are kept for compatibility
|
||||
public Color plantColor = Color.valueOf("5541b1");
|
||||
public Color plantColorLight = Color.valueOf("7457ce");
|
||||
public Color bottomColor = Color.valueOf("474747");
|
||||
@@ -25,84 +27,11 @@ public class Cultivator extends GenericCrafter{
|
||||
|
||||
public Cultivator(String name){
|
||||
super(name);
|
||||
craftEffect = Fx.none;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBars(){
|
||||
super.setBars();
|
||||
bars.add("multiplier", (CultivatorBuild entity) -> new Bar(() ->
|
||||
Core.bundle.formatFloat("bar.efficiency",
|
||||
((entity.boost + 1f + attribute.env()) * entity.warmup) * 100f, 1),
|
||||
() -> Pal.ammo,
|
||||
() -> entity.warmup));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStats(){
|
||||
super.setStats();
|
||||
|
||||
stats.add(Stat.affinities, attribute);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawPlace(int x, int y, int rotation, boolean valid){
|
||||
super.drawPlace(x, y, rotation, valid);
|
||||
|
||||
drawPlaceText(Core.bundle.formatFloat("bar.efficiency", (1 + sumAttribute(attribute, x, y)) * 100, 1), x, y, valid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureRegion[] icons(){
|
||||
return new TextureRegion[]{region, topRegion};
|
||||
}
|
||||
|
||||
public class CultivatorBuild extends GenericCrafterBuild{
|
||||
public float warmup;
|
||||
public float boost;
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
super.updateTile();
|
||||
|
||||
warmup = Mathf.lerpDelta(warmup, consValid() ? 1f : 0f, 0.015f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Draw.rect(region, x, y);
|
||||
|
||||
Drawf.liquid(middleRegion, x, y, warmup, plantColor);
|
||||
|
||||
Draw.color(bottomColor, plantColorLight, warmup);
|
||||
|
||||
random.setSeed(tile.pos());
|
||||
for(int i = 0; i < 12; i++){
|
||||
float offset = random.nextFloat() * 999999f;
|
||||
float x = random.range(4f), y = random.range(4f);
|
||||
float life = 1f - (((Time.time + offset) / 50f) % recurrence);
|
||||
|
||||
if(life > 0){
|
||||
Lines.stroke(warmup * (life + 0.2f));
|
||||
Lines.poly(x + x, y + y, 8, (1f - life) * 3f);
|
||||
}
|
||||
}
|
||||
|
||||
Draw.color();
|
||||
Draw.rect(topRegion, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProximityUpdate(){
|
||||
super.onProximityAdded();
|
||||
|
||||
boost = sumAttribute(attribute, tile.x, tile.y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getProgressIncrease(float baseTime){
|
||||
return super.getProgressIncrease(baseTime) * (1f + boost + attribute.env());
|
||||
}
|
||||
//compat
|
||||
public float warmup, boost;
|
||||
|
||||
@Override
|
||||
public void write(Writes write){
|
||||
|
||||
@@ -6,6 +6,7 @@ import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
@@ -13,12 +14,12 @@ import mindustry.entities.units.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.logic.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
import mindustry.world.meta.*;
|
||||
import mindustry.world.meta.values.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@@ -122,11 +123,11 @@ public class Drill extends Block{
|
||||
|
||||
if(returnItem != null){
|
||||
float width = drawPlaceText(Core.bundle.formatFloat("bar.drillspeed", 60f / (drillTime + hardnessDrillMultiplier * returnItem.hardness) * returnCount, 2), x, y, valid);
|
||||
float dx = x * tilesize + offset - width/2f - 4f, dy = y * tilesize + offset + size * tilesize / 2f + 5;
|
||||
float dx = x * tilesize + offset - width/2f - 4f, dy = y * tilesize + offset + size * tilesize / 2f + 5, s = iconSmall / 4f;
|
||||
Draw.mixcol(Color.darkGray, 1f);
|
||||
Draw.rect(returnItem.icon(Cicon.small), dx, dy - 1);
|
||||
Draw.rect(returnItem.fullIcon, dx, dy - 1, s, s);
|
||||
Draw.reset();
|
||||
Draw.rect(returnItem.icon(Cicon.small), dx, dy);
|
||||
Draw.rect(returnItem.fullIcon, dx, dy, s, s);
|
||||
|
||||
if(drawMineItem){
|
||||
Draw.color(returnItem.color);
|
||||
@@ -146,7 +147,7 @@ public class Drill extends Block{
|
||||
public void setStats(){
|
||||
super.setStats();
|
||||
|
||||
stats.add(Stat.drillTier, new BlockFilterValue(b -> b instanceof Floor f && f.itemDrop != null && f.itemDrop.hardness <= tier));
|
||||
stats.add(Stat.drillTier, StatValues.blocks(b -> b instanceof Floor f && f.itemDrop != null && f.itemDrop.hardness <= tier));
|
||||
|
||||
stats.add(Stat.drillSpeed, 60f / drillTime * size * size, StatUnit.itemsSecond);
|
||||
if(liquidBoostIntensity != 1){
|
||||
@@ -225,16 +226,18 @@ public class Drill extends Block{
|
||||
@Override
|
||||
public void drawSelect(){
|
||||
if(dominantItem != null){
|
||||
float dx = x - size * tilesize/2f, dy = y + size * tilesize/2f;
|
||||
float dx = x - size * tilesize/2f, dy = y + size * tilesize/2f, s = iconSmall / 4f;
|
||||
Draw.mixcol(Color.darkGray, 1f);
|
||||
Draw.rect(dominantItem.icon(Cicon.small), dx, dy - 1);
|
||||
Draw.rect(dominantItem.fullIcon, dx, dy - 1, s, s);
|
||||
Draw.reset();
|
||||
Draw.rect(dominantItem.icon(Cicon.small), dx, dy);
|
||||
Draw.rect(dominantItem.fullIcon, dx, dy, s, s);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProximityUpdate(){
|
||||
super.onProximityUpdate();
|
||||
|
||||
countOre(tile);
|
||||
dominantItem = returnItem;
|
||||
dominantItems = returnCount;
|
||||
@@ -247,7 +250,7 @@ public class Drill extends Block{
|
||||
}
|
||||
|
||||
if(timer(timerDump, dumpTime)){
|
||||
dump(dominantItem);
|
||||
dump(items.has(dominantItem) ? dominantItem : null);
|
||||
}
|
||||
|
||||
timeDrilled += warmup * delta();
|
||||
@@ -285,6 +288,12 @@ public class Drill extends Block{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public double sense(LAccess sensor){
|
||||
if(sensor == LAccess.progress && dominantItem != null) return Mathf.clamp(progress / (drillTime + hardnessDrillMultiplier * dominantItem.hardness));
|
||||
return super.sense(sensor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawCracks(){}
|
||||
|
||||
@@ -315,6 +324,27 @@ public class Drill extends Block{
|
||||
Draw.color();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte version(){
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Writes write){
|
||||
super.write(write);
|
||||
write.f(progress);
|
||||
write.f(warmup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(Reads read, byte revision){
|
||||
super.read(read, revision);
|
||||
if(revision >= 1){
|
||||
progress = read.f();
|
||||
warmup = read.f();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,10 +18,12 @@ public class Fracker extends SolidPump{
|
||||
hasItems = true;
|
||||
ambientSound = Sounds.drill;
|
||||
ambientSoundVolume = 0.03f;
|
||||
envRequired |= Env.groundOil;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStats(){
|
||||
stats.timePeriod = itemUseTime;
|
||||
super.setStats();
|
||||
|
||||
stats.add(Stat.productionTime, itemUseTime / 60f, StatUnit.seconds);
|
||||
|
||||
@@ -8,6 +8,7 @@ import arc.util.io.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.logic.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.draw.*;
|
||||
@@ -21,6 +22,9 @@ public class GenericCrafter extends Block{
|
||||
public Effect craftEffect = Fx.none;
|
||||
public Effect updateEffect = Fx.none;
|
||||
public float updateEffectChance = 0.04f;
|
||||
public float warmupSpeed = 0.019f;
|
||||
/** Only used for legacy cultivator blocks. */
|
||||
public boolean legacyReadWarmup = false;
|
||||
|
||||
public DrawBlock drawer = new DrawBlock();
|
||||
|
||||
@@ -37,11 +41,12 @@ public class GenericCrafter extends Block{
|
||||
|
||||
@Override
|
||||
public void setStats(){
|
||||
stats.timePeriod = craftTime;
|
||||
super.setStats();
|
||||
stats.add(Stat.productionTime, craftTime / 60f, StatUnit.seconds);
|
||||
|
||||
if(outputItem != null){
|
||||
stats.add(Stat.output, outputItem);
|
||||
stats.add(Stat.output, StatValues.items(craftTime, outputItem));
|
||||
}
|
||||
|
||||
if(outputLiquid != null){
|
||||
@@ -82,9 +87,15 @@ public class GenericCrafter extends Block{
|
||||
drawer.draw(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawLight(){
|
||||
super.drawLight();
|
||||
drawer.drawLight(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldConsume(){
|
||||
if(outputItem != null && items.get(outputItem.item) >= itemCapacity){
|
||||
if(outputItem != null && items.get(outputItem.item) + outputItem.amount > itemCapacity){
|
||||
return false;
|
||||
}
|
||||
return (outputLiquid == null || !(liquids.get(outputLiquid.liquid) >= liquidCapacity - 0.001f)) && enabled;
|
||||
@@ -96,13 +107,13 @@ public class GenericCrafter extends Block{
|
||||
|
||||
progress += getProgressIncrease(craftTime);
|
||||
totalProgress += delta();
|
||||
warmup = Mathf.lerpDelta(warmup, 1f, 0.02f);
|
||||
warmup = Mathf.approachDelta(warmup, 1f, warmupSpeed);
|
||||
|
||||
if(Mathf.chanceDelta(updateEffectChance)){
|
||||
updateEffect.at(getX() + Mathf.range(size * 4f), getY() + Mathf.range(size * 4));
|
||||
}
|
||||
}else{
|
||||
warmup = Mathf.lerp(warmup, 0f, 0.02f);
|
||||
warmup = Mathf.approachDelta(warmup, 0f, warmupSpeed);
|
||||
}
|
||||
|
||||
if(progress >= 1f){
|
||||
@@ -122,7 +133,7 @@ public class GenericCrafter extends Block{
|
||||
progress %= 1f;
|
||||
}
|
||||
|
||||
if(outputItem != null && timer(timerDump, dumpTime)){
|
||||
if(outputItem != null && timer(timerDump, dumpTime / timeScale)){
|
||||
dump(outputItem.item);
|
||||
}
|
||||
|
||||
@@ -131,6 +142,12 @@ public class GenericCrafter extends Block{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public double sense(LAccess sensor){
|
||||
if(sensor == LAccess.progress) return Mathf.clamp(progress);
|
||||
return super.sense(sensor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaximumAccepted(Item item){
|
||||
return itemCapacity;
|
||||
@@ -146,6 +163,7 @@ public class GenericCrafter extends Block{
|
||||
super.write(write);
|
||||
write.f(progress);
|
||||
write.f(warmup);
|
||||
if(legacyReadWarmup) write.f(0f);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -153,6 +171,7 @@ public class GenericCrafter extends Block{
|
||||
super.read(read, revision);
|
||||
progress = read.f();
|
||||
warmup = read.f();
|
||||
if(legacyReadWarmup) read.f();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,10 +8,12 @@ import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
|
||||
/** A GenericCrafter with a new glowing region drawn on top. */
|
||||
/** @deprecated this class has no new functionality over GenericCrafter, use GenericCrafter with a DrawSmelter drawer instead. See vanilla smelter blocks. */
|
||||
@Deprecated
|
||||
public class GenericSmelter extends GenericCrafter{
|
||||
public Color flameColor = Color.valueOf("ffc999");
|
||||
public @Load("@-top") TextureRegion topRegion;
|
||||
public float flameRadius = 3f, flameRadiusIn = 1.9f, flameRadiusScl = 5f, flameRadiusMag = 2f, flameRadiusInMag = 1f;
|
||||
|
||||
public GenericSmelter(String name){
|
||||
super(name);
|
||||
@@ -35,10 +37,10 @@ public class GenericSmelter extends GenericCrafter{
|
||||
Draw.alpha(((1f - g) + Mathf.absin(Time.time, 8f, g) + Mathf.random(r) - r) * warmup);
|
||||
|
||||
Draw.tint(flameColor);
|
||||
Fill.circle(x, y, 3f + Mathf.absin(Time.time, 5f, 2f) + cr);
|
||||
Fill.circle(x, y, flameRadius + Mathf.absin(Time.time, flameRadiusScl, flameRadiusMag) + cr);
|
||||
Draw.color(1f, 1f, 1f, warmup);
|
||||
Draw.rect(topRegion, x, y);
|
||||
Fill.circle(x, y, 1.9f + Mathf.absin(Time.time, 5f, 1f) + cr);
|
||||
Fill.circle(x, y, flameRadiusIn + Mathf.absin(Time.time, flameRadiusScl, flameRadiusInMag) + cr);
|
||||
|
||||
Draw.color();
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package mindustry.world.blocks.production;
|
||||
|
||||
import arc.math.*;
|
||||
import mindustry.world.consumers.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
@@ -47,16 +48,25 @@ public class LiquidConverter extends GenericCrafter{
|
||||
ConsumeLiquid cl = consumes.get(ConsumeType.liquid);
|
||||
|
||||
if(cons.valid()){
|
||||
if(Mathf.chanceDelta(updateEffectChance)){
|
||||
updateEffect.at(getX() + Mathf.range(size * 4f), getY() + Mathf.range(size * 4));
|
||||
}
|
||||
|
||||
warmup = Mathf.lerpDelta(warmup, 1f, 0.02f);
|
||||
float use = Math.min(cl.amount * edelta(), liquidCapacity - liquids.get(outputLiquid.liquid));
|
||||
float ratio = outputLiquid.amount / cl.amount;
|
||||
|
||||
liquids.remove(cl.liquid, Math.min(use, liquids.get(cl.liquid)));
|
||||
|
||||
progress += use / cl.amount;
|
||||
liquids.add(outputLiquid.liquid, use);
|
||||
liquids.add(outputLiquid.liquid, use * ratio);
|
||||
if(progress >= craftTime){
|
||||
consume();
|
||||
progress %= craftTime;
|
||||
}
|
||||
}else{
|
||||
//warmup is still 1 even if not consuming
|
||||
warmup = Mathf.lerp(warmup, cons.canConsume() ? 1f : 0f, 0.02f);
|
||||
}
|
||||
|
||||
dumpLiquid(outputLiquid.liquid);
|
||||
|
||||
@@ -1,186 +1,18 @@
|
||||
package mindustry.world.blocks.production;
|
||||
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.payloads.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class PayloadAcceptor extends Block{
|
||||
public float payloadSpeed = 0.5f, payloadRotateSpeed = 5f;
|
||||
|
||||
public @Load(value = "@-top", fallback = "factory-top-@size") TextureRegion topRegion;
|
||||
public @Load(value = "@-out", fallback = "factory-out-@size") TextureRegion outRegion;
|
||||
public @Load(value = "@-in", fallback = "factory-in-@size") TextureRegion inRegion;
|
||||
/** @deprecated used PayloadBlock instead. */
|
||||
@Deprecated
|
||||
public abstract class PayloadAcceptor extends PayloadBlock{
|
||||
|
||||
public PayloadAcceptor(String name){
|
||||
super(name);
|
||||
|
||||
update = true;
|
||||
sync = true;
|
||||
}
|
||||
|
||||
public static boolean blends(Building build, int direction){
|
||||
int size = build.block.size;
|
||||
int trns = build.block.size/2 + 1;
|
||||
Building accept = build.nearby(Geometry.d4(direction).x * trns, Geometry.d4(direction).y * trns);
|
||||
return accept != null &&
|
||||
accept.block.outputsPayload &&
|
||||
/** @deprecated used PayloadBlockBuild instead. */
|
||||
@Deprecated
|
||||
public class PayloadAcceptorBuild<T extends Payload> extends PayloadBlockBuild<T>{
|
||||
|
||||
//if size is the same, block must either be facing this one, or not be rotating
|
||||
((accept.block.size == size
|
||||
&& Math.abs(accept.tileX() - build.tileX()) % size == 0 //check alignment
|
||||
&& Math.abs(accept.tileY() - build.tileY()) % size == 0
|
||||
&& ((accept.block.rotate && accept.tileX() + Geometry.d4(accept.rotation).x * size == build.tileX() && accept.tileY() + Geometry.d4(accept.rotation).y * size == build.tileY())
|
||||
|| !accept.block.rotate
|
||||
|| !accept.block.outputFacing)) ||
|
||||
|
||||
//if the other block is smaller, check alignment
|
||||
(accept.block.size < size &&
|
||||
(accept.rotation % 2 == 0 ? //check orientation; make sure it's aligned properly with this block.
|
||||
Math.abs(accept.y - build.y) <= (size * tilesize - accept.block.size * tilesize)/2f : //check Y alignment
|
||||
Math.abs(accept.x - build.x) <= (size * tilesize - accept.block.size * tilesize)/2f //check X alignment
|
||||
)) && (!accept.block.rotate || accept.front() == build || !accept.block.outputFacing) //make sure it's facing this block
|
||||
);
|
||||
}
|
||||
|
||||
public class PayloadAcceptorBuild<T extends Payload> extends Building{
|
||||
public @Nullable T payload;
|
||||
public Vec2 payVector = new Vec2();
|
||||
public float payRotation;
|
||||
public boolean carried;
|
||||
|
||||
@Override
|
||||
public boolean acceptPayload(Building source, Payload payload){
|
||||
return this.payload == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePayload(Building source, Payload payload){
|
||||
this.payload = (T)payload;
|
||||
this.payVector.set(source).sub(this).clamp(-size * tilesize / 2f, -size * tilesize / 2f, size * tilesize / 2f, size * tilesize / 2f);
|
||||
this.payRotation = payload.rotation();
|
||||
|
||||
updatePayload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Payload getPayload(){
|
||||
return payload;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pickedUp(){
|
||||
carried = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawTeamTop(){
|
||||
carried = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Payload takePayload(){
|
||||
T t = payload;
|
||||
payload = null;
|
||||
return t;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoved(){
|
||||
super.onRemoved();
|
||||
if(payload != null && !carried) payload.dump();
|
||||
}
|
||||
|
||||
public boolean blends(int direction){
|
||||
return PayloadAcceptor.blends(this, direction);
|
||||
}
|
||||
|
||||
public void updatePayload(){
|
||||
if(payload != null){
|
||||
payload.set(x + payVector.x, y + payVector.y, payRotation);
|
||||
}
|
||||
}
|
||||
|
||||
/** @return true if the payload is in position. */
|
||||
public boolean moveInPayload(){
|
||||
if(payload == null) return false;
|
||||
|
||||
updatePayload();
|
||||
|
||||
payRotation = Angles.moveToward(payRotation, rotate ? rotdeg() : 90f, payloadRotateSpeed * edelta());
|
||||
payVector.approach(Vec2.ZERO, payloadSpeed * delta());
|
||||
|
||||
return hasArrived();
|
||||
}
|
||||
|
||||
public void moveOutPayload(){
|
||||
if(payload == null) return;
|
||||
|
||||
updatePayload();
|
||||
|
||||
Vec2 dest = Tmp.v1.trns(rotdeg(), size* tilesize/2f);
|
||||
|
||||
payRotation = Angles.moveToward(payRotation, rotdeg(), payloadRotateSpeed * edelta());
|
||||
payVector.approach(dest, payloadSpeed * delta());
|
||||
|
||||
if(payVector.within(dest, 0.001f)){
|
||||
payVector.clamp(-size * tilesize / 2f, -size * tilesize / 2f, size * tilesize / 2f, size * tilesize / 2f);
|
||||
|
||||
Building front = front();
|
||||
if(front != null && front.block.outputsPayload){
|
||||
if(movePayload(payload)){
|
||||
payload = null;
|
||||
}
|
||||
}else if(front == null || !front.tile().solid()){
|
||||
dumpPayload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void dumpPayload(){
|
||||
if(payload.dump()){
|
||||
payload = null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasArrived(){
|
||||
return payVector.isZero(0.01f);
|
||||
}
|
||||
|
||||
public void drawPayload(){
|
||||
if(payload != null){
|
||||
updatePayload();
|
||||
|
||||
Draw.z(Layer.blockOver);
|
||||
payload.draw();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Writes write){
|
||||
super.write(write);
|
||||
|
||||
write.f(payVector.x);
|
||||
write.f(payVector.y);
|
||||
write.f(payRotation);
|
||||
Payload.write(payload, write);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(Reads read, byte revision){
|
||||
super.read(read, revision);
|
||||
|
||||
payVector.set(read.f(), read.f());
|
||||
payRotation = read.f();
|
||||
payload = Payload.read(read);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import arc.graphics.g2d.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.liquid.*;
|
||||
import mindustry.world.meta.*;
|
||||
@@ -48,11 +47,11 @@ public class Pump extends LiquidBlock{
|
||||
|
||||
if(liquidDrop != null){
|
||||
float width = drawPlaceText(Core.bundle.formatFloat("bar.pumpspeed", amount * pumpAmount * 60f, 0), x, y, valid);
|
||||
float dx = x * tilesize + offset - width/2f - 4f, dy = y * tilesize + offset + size * tilesize / 2f + 5;
|
||||
float dx = x * tilesize + offset - width/2f - 4f, dy = y * tilesize + offset + size * tilesize / 2f + 5, s = iconSmall / 4f;
|
||||
Draw.mixcol(Color.darkGray, 1f);
|
||||
Draw.rect(liquidDrop.icon(Cicon.small), dx, dy - 1);
|
||||
Draw.rect(liquidDrop.fullIcon, dx, dy - 1, s, s);
|
||||
Draw.reset();
|
||||
Draw.rect(liquidDrop.icon(Cicon.small), dx, dy);
|
||||
Draw.rect(liquidDrop.fullIcon, dx, dy, s, s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package mindustry.world.blocks.production;
|
||||
import arc.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
@@ -11,7 +12,6 @@ import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.consumers.*;
|
||||
import mindustry.world.meta.*;
|
||||
import mindustry.world.meta.values.*;
|
||||
|
||||
/**
|
||||
* Extracts a random list of items from an input item and an input liquid.
|
||||
@@ -34,15 +34,10 @@ public class Separator extends Block{
|
||||
|
||||
@Override
|
||||
public void setStats(){
|
||||
stats.timePeriod = craftTime;
|
||||
super.setStats();
|
||||
|
||||
stats.add(Stat.output, new ItemFilterValue(item -> {
|
||||
for(ItemStack i : results){
|
||||
if(item == i.item) return true;
|
||||
}
|
||||
return false;
|
||||
}));
|
||||
|
||||
stats.add(Stat.output, StatValues.items(item -> Structs.contains(results, i -> i.item == item)));
|
||||
stats.add(Stat.productionTime, craftTime / 60f, StatUnit.seconds);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
package mindustry.world.blocks.production;
|
||||
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.payloads.*;
|
||||
|
||||
public class SingleBlockProducer extends BlockProducer{
|
||||
public Block result = Blocks.router;
|
||||
|
||||
public SingleBlockProducer(String name){
|
||||
super(name);
|
||||
}
|
||||
|
||||
public class SingleBlockProducerBuild extends BlockProducerBuild{
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Block recipe(){
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@ public class ItemSource extends Block{
|
||||
configurable = true;
|
||||
saveConfig = true;
|
||||
noUpdateDisabled = true;
|
||||
envEnabled = Env.any;
|
||||
|
||||
config(Item.class, (ItemSourceBuild tile, Item item) -> tile.outputItem = item);
|
||||
configClear((ItemSourceBuild tile) -> tile.outputItem = null);
|
||||
|
||||
@@ -11,6 +11,7 @@ public class ItemVoid extends Block{
|
||||
super(name);
|
||||
group = BlockGroup.transportation;
|
||||
update = solid = acceptsItems = true;
|
||||
envEnabled = Env.any;
|
||||
}
|
||||
|
||||
public class ItemVoidBuild extends Building{
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package mindustry.world.blocks.sandbox;
|
||||
|
||||
import mindustry.world.blocks.power.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
public class PowerSource extends PowerNode{
|
||||
|
||||
@@ -11,6 +12,8 @@ public class PowerSource extends PowerNode{
|
||||
maxNodes = 100;
|
||||
outputsPower = true;
|
||||
consumesPower = false;
|
||||
//TODO maybe don't?
|
||||
envEnabled = Env.any;
|
||||
}
|
||||
|
||||
public class PowerSourceBuild extends PowerNodeBuild{
|
||||
|
||||
@@ -8,6 +8,7 @@ public class PowerVoid extends PowerBlock{
|
||||
public PowerVoid(String name){
|
||||
super(name);
|
||||
consumes.power(Float.MAX_VALUE);
|
||||
envEnabled = Env.any;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -6,6 +6,7 @@ import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
@@ -19,7 +20,6 @@ import mindustry.logic.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
import mindustry.world.blocks.units.*;
|
||||
import mindustry.world.meta.*;
|
||||
import mindustry.world.modules.*;
|
||||
@@ -37,6 +37,7 @@ public class CoreBlock extends StorageBlock{
|
||||
public int ammoAmount = 5;
|
||||
public float resupplyRate = 10f;
|
||||
public float resupplyRange = 60f;
|
||||
public float captureInvicibility = 60f * 15f;
|
||||
public Item resupplyItem = Items.copper;
|
||||
|
||||
public CoreBlock(String name){
|
||||
@@ -46,12 +47,16 @@ public class CoreBlock extends StorageBlock{
|
||||
update = true;
|
||||
hasItems = true;
|
||||
priority = TargetPriority.core;
|
||||
flags = EnumSet.of(BlockFlag.core, BlockFlag.unitModifier);
|
||||
flags = EnumSet.of(BlockFlag.core);
|
||||
unitCapModifier = 10;
|
||||
loopSound = Sounds.respawning;
|
||||
loopSoundVolume = 1f;
|
||||
drawDisabled = false;
|
||||
canOverdrive = false;
|
||||
|
||||
//support everything
|
||||
envEnabled = Env.any;
|
||||
drawDisabled = false;
|
||||
replaceable = false;
|
||||
}
|
||||
|
||||
@@ -97,6 +102,15 @@ public class CoreBlock extends StorageBlock{
|
||||
));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
//assign to update clipSize internally
|
||||
lightRadius = 30f + 20f * size;
|
||||
emitLight = true;
|
||||
|
||||
super.init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBreak(Tile tile){
|
||||
return false;
|
||||
@@ -156,19 +170,29 @@ public class CoreBlock extends StorageBlock{
|
||||
|
||||
if(!canPlaceOn(world.tile(x, y), player.team())){
|
||||
|
||||
drawPlaceText(Core.bundle.get((player.team().core() != null && player.team().core().items.has(requirements, state.rules.buildCostMultiplier)) || state.rules.infiniteResources ?
|
||||
drawPlaceText(Core.bundle.get(
|
||||
(player.team().core() != null && player.team().core().items.has(requirements, state.rules.buildCostMultiplier)) || state.rules.infiniteResources ?
|
||||
"bar.corereq" :
|
||||
"bar.noresources"
|
||||
), x, y, valid);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public class CoreBuild extends Building implements ControlBlock{
|
||||
public class CoreBuild extends Building{
|
||||
public int storageCapacity;
|
||||
//note that this unit is never actually used for control; the possession handler makes the player respawn when this unit is controlled
|
||||
public BlockUnitc unit = Nulls.blockUnit;
|
||||
public boolean noEffect = false;
|
||||
public Team lastDamage = Team.derelict;
|
||||
public float iframes = -1f;
|
||||
|
||||
@Override
|
||||
public void damage(@Nullable Team source, float damage){
|
||||
if(iframes > 0) return;
|
||||
|
||||
if(source != null && source != team){
|
||||
lastDamage = source;
|
||||
}
|
||||
super.damage(source, damage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double sense(LAccess sensor){
|
||||
@@ -177,23 +201,34 @@ public class CoreBlock extends StorageBlock{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void created(){
|
||||
unit = (BlockUnitc)UnitTypes.block.create(team);
|
||||
unit.tile(this);
|
||||
public boolean canControlSelect(Player player){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Unit unit(){
|
||||
return (Unit)unit;
|
||||
public void onControlSelect(Player player){
|
||||
Fx.spawn.at(player);
|
||||
if(net.client()){
|
||||
control.input.controlledType = null;
|
||||
}
|
||||
|
||||
player.clearUnit();
|
||||
player.deathTimer = Player.deathDelay + 1f;
|
||||
requestSpawn(player);
|
||||
}
|
||||
|
||||
public void requestSpawn(Player player){
|
||||
//do not try to respawn in unsupported environments at all
|
||||
if(!unitType.supportsEnv(state.rules.environment)) return;
|
||||
|
||||
Call.playerSpawn(tile, player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
|
||||
iframes -= Time.delta;
|
||||
|
||||
//resupply nearby units
|
||||
if(items.has(resupplyItem) && timer(timerResupply, resupplyRate) && ResupplyPoint.resupply(this, resupplyRange, ammoAmount, resupplyItem.color)){
|
||||
items.remove(resupplyItem, 1);
|
||||
@@ -208,7 +243,13 @@ public class CoreBlock extends StorageBlock{
|
||||
|
||||
@Override
|
||||
public void onDestroyed(){
|
||||
super.onDestroyed();
|
||||
if(state.rules.coreCapture){
|
||||
//just create an explosion, no fire. this prevents immediate recapture
|
||||
Damage.dynamicExplosion(x, y, 0, 0, 0, tilesize * block.size / 2f, state.rules.damageExplosions);
|
||||
Fx.commandSend.at(x, y, 140f);
|
||||
}else{
|
||||
super.onDestroyed();
|
||||
}
|
||||
|
||||
//add a spawn to the map for future reference - waves should be disabled, so it shouldn't matter
|
||||
if(state.isCampaign() && team == state.rules.waveTeam && team.cores().size <= 1){
|
||||
@@ -221,9 +262,18 @@ public class CoreBlock extends StorageBlock{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterDestroyed(){
|
||||
if(state.rules.coreCapture){
|
||||
tile.setBlock(block, lastDamage);
|
||||
//core is invincible for several seconds to prevent recapture
|
||||
((CoreBuild)tile.build).iframes = captureInvicibility;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawLight(){
|
||||
Drawf.light(team, x, y, 30f + 20f * size, Pal.accent, 0.65f + Mathf.absin(20f, 0.1f));
|
||||
Drawf.light(team, x, y, lightRadius, Pal.accent, 0.65f + Mathf.absin(20f, 0.1f));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -238,6 +288,8 @@ public class CoreBlock extends StorageBlock{
|
||||
|
||||
@Override
|
||||
public void onProximityUpdate(){
|
||||
super.onProximityUpdate();
|
||||
|
||||
for(Building other : state.teams.cores(team)){
|
||||
if(other.tile() != tile){
|
||||
this.items = other.items;
|
||||
|
||||
@@ -10,12 +10,12 @@ import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class Unloader extends Block{
|
||||
public float speed = 1f;
|
||||
public final int timerUnload = timers++;
|
||||
|
||||
public Unloader(String name){
|
||||
super(name);
|
||||
@@ -33,6 +33,12 @@ public class Unloader extends Block{
|
||||
configClear((UnloaderBuild tile) -> tile.sortItem = null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStats(){
|
||||
super.setStats();
|
||||
stats.add(Stat.speed, 60f / speed, StatUnit.itemsSecond);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawRequestConfig(BuildPlan req, Eachable<BuildPlan> list){
|
||||
drawRequestConfigCenter(req, req.config, "unloader-center");
|
||||
@@ -45,6 +51,7 @@ public class Unloader extends Block{
|
||||
}
|
||||
|
||||
public class UnloaderBuild extends Building{
|
||||
public float unloadTimer = 0f;
|
||||
public Item sortItem = null;
|
||||
public Building dumpingTo;
|
||||
public int offset = 0;
|
||||
@@ -52,7 +59,8 @@ public class Unloader extends Block{
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
if(timer(timerUnload, speed / timeScale)){
|
||||
if((unloadTimer += delta()) >= speed){
|
||||
boolean any = false;
|
||||
if(rotations == null || rotations.length != proximity.size){
|
||||
rotations = new int[proximity.size];
|
||||
}
|
||||
@@ -72,6 +80,7 @@ public class Unloader extends Block{
|
||||
//remove item if it's dumped correctly
|
||||
if(put(item)){
|
||||
other.items.remove(item, 1);
|
||||
any = true;
|
||||
|
||||
if(sortItem == null){
|
||||
rotations[pos] = item.id + 1;
|
||||
@@ -84,6 +93,12 @@ public class Unloader extends Block{
|
||||
}
|
||||
}
|
||||
|
||||
if(any){
|
||||
unloadTimer %= speed;
|
||||
}else{
|
||||
unloadTimer = Math.min(unloadTimer, speed);
|
||||
}
|
||||
|
||||
if(proximity.size > 0){
|
||||
offset ++;
|
||||
offset %= proximity.size;
|
||||
|
||||
@@ -13,6 +13,7 @@ import mindustry.entities.units.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.logic.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.blocks.payloads.*;
|
||||
@@ -71,12 +72,12 @@ public class Reconstructor extends UnitBlock{
|
||||
for(var upgrade : upgrades){
|
||||
float size = 8 * 3;
|
||||
if(upgrade[0].unlockedNow() && upgrade[1].unlockedNow()){
|
||||
table.image(upgrade[0].icon(Cicon.small)).size(size).padRight(4).padLeft(10).scaling(Scaling.fit).right();
|
||||
table.image(upgrade[0].uiIcon).size(size).padRight(4).padLeft(10).scaling(Scaling.fit).right();
|
||||
table.add(upgrade[0].localizedName).left();
|
||||
|
||||
table.add("[lightgray] -> ");
|
||||
|
||||
table.image(upgrade[1].icon(Cicon.small)).size(size).padRight(4).scaling(Scaling.fit);
|
||||
table.image(upgrade[1].uiIcon).size(size).padRight(4).scaling(Scaling.fit);
|
||||
table.add(upgrade[1].localizedName).left();
|
||||
table.row();
|
||||
}
|
||||
@@ -107,6 +108,11 @@ public class Reconstructor extends UnitBlock{
|
||||
return progress / constructTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptUnitPayload(Unit unit){
|
||||
return hasUpgrade(unit.type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptPayload(Building source, Payload payload){
|
||||
return this.payload == null
|
||||
@@ -147,7 +153,7 @@ public class Reconstructor extends UnitBlock{
|
||||
if(constructing() && hasArrived()){
|
||||
Draw.draw(Layer.blockOver, () -> {
|
||||
Draw.alpha(1f - progress/ constructTime);
|
||||
Draw.rect(payload.unit.type.icon(Cicon.full), x, y, payload.rotation() - 90);
|
||||
Draw.rect(payload.unit.type.fullIcon, x, y, payload.rotation() - 90);
|
||||
Draw.reset();
|
||||
Drawf.construct(this, upgrade(payload.unit.type), payload.rotation() - 90f, progress / constructTime, speedScl, time);
|
||||
});
|
||||
@@ -193,6 +199,12 @@ public class Reconstructor extends UnitBlock{
|
||||
time += edelta() * speedScl * state.rules.unitBuildSpeedMultiplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double sense(LAccess sensor){
|
||||
if(sensor == LAccess.progress) return Mathf.clamp(fraction());
|
||||
return super.sense(sensor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldConsume(){
|
||||
return constructing();
|
||||
|
||||
@@ -8,29 +8,47 @@ import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.logic.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.consumers.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class RepairPoint extends Block{
|
||||
static final Rect rect = new Rect();
|
||||
static final Rand rand = new Rand();
|
||||
|
||||
public int timerTarget = timers++;
|
||||
public int timerEffect = timers++;
|
||||
|
||||
public float repairRadius = 50f;
|
||||
public float repairSpeed = 0.3f;
|
||||
public float powerUse;
|
||||
public float length = 5f;
|
||||
public float beamWidth = 1f;
|
||||
public float pulseRadius = 6f;
|
||||
public float pulseStroke = 2f;
|
||||
public boolean acceptCoolant = false;
|
||||
|
||||
public @Load("@-base") TextureRegion baseRegion;
|
||||
public @Load("laser") TextureRegion laser;
|
||||
public @Load("laser-end") TextureRegion laserEnd;
|
||||
public float coolantUse = 0.5f;
|
||||
/** Effect displayed when coolant is used. */
|
||||
public Effect coolEffect = Fx.fuelburn;
|
||||
/** How much healing is increased by with heat capacity. */
|
||||
public float coolantMultiplier = 1f;
|
||||
|
||||
public Color laserColor = Color.valueOf("e8ffd7");
|
||||
public @Load(value = "@-base", fallback = "block-@size") TextureRegion baseRegion;
|
||||
public @Load("laser-white") TextureRegion laser;
|
||||
public @Load("laser-white-end") TextureRegion laserEnd;
|
||||
public @Load("laser-top") TextureRegion laserTop;
|
||||
public @Load("laser-top-end") TextureRegion laserTopEnd;
|
||||
|
||||
public Color laserColor = Color.valueOf("98ffa9"), laserTopColor = Color.white.cpy();
|
||||
|
||||
public RepairPoint(String name){
|
||||
super(name);
|
||||
@@ -39,18 +57,30 @@ public class RepairPoint extends Block{
|
||||
flags = EnumSet.of(BlockFlag.repair);
|
||||
hasPower = true;
|
||||
outlineIcon = true;
|
||||
expanded = true;
|
||||
//yeah, this isn't the same thing, but it's close enough
|
||||
group = BlockGroup.projectors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStats(){
|
||||
super.setStats();
|
||||
stats.add(Stat.range, repairRadius / tilesize, StatUnit.blocks);
|
||||
stats.add(Stat.repairSpeed, repairSpeed * 60f, StatUnit.perSecond);
|
||||
|
||||
if(acceptCoolant){
|
||||
stats.add(Stat.booster, StatValues.strengthBoosters(coolantMultiplier, l -> consumes.liquidfilters.get(l.id)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
consumes.powerCond(powerUse, entity -> ((RepairPointBuild)entity).target != null);
|
||||
if(acceptCoolant){
|
||||
hasLiquids = true;
|
||||
consumes.add(new ConsumeLiquidFilter(liquid -> liquid.temperature <= 0.5f && liquid.flammability < 0.1f, coolantUse)).optional(true, true);
|
||||
}
|
||||
|
||||
consumes.powerCond(powerUse, (RepairPointBuild entity) -> entity.target != null);
|
||||
clipSize = Math.max(clipSize, (repairRadius + tilesize) * 2);
|
||||
super.init();
|
||||
}
|
||||
|
||||
@@ -66,8 +96,56 @@ public class RepairPoint extends Block{
|
||||
return new TextureRegion[]{baseRegion, region};
|
||||
}
|
||||
|
||||
public static void drawBeam(float x, float y, float rotation, float length, int id, Sized target, Team team,
|
||||
float strength, float pulseStroke, float pulseRadius, float beamWidth,
|
||||
Vec2 lastEnd, Vec2 offset,
|
||||
Color laserColor, Color laserTopColor,
|
||||
TextureRegion laser, TextureRegion laserEnd, TextureRegion laserTop, TextureRegion laserTopEnd){
|
||||
if(target != null){
|
||||
float
|
||||
originX = x + Angles.trnsx(rotation, length),
|
||||
originY = y + Angles.trnsy(rotation, length);
|
||||
|
||||
rand.setSeed(id + (target instanceof Entityc e ? e.id() : 0));
|
||||
|
||||
lastEnd.set(target).sub(originX, originY);
|
||||
lastEnd.setLength(Math.max(2f, lastEnd.len()));
|
||||
|
||||
lastEnd.add(offset.trns(
|
||||
rand.random(360f) + Time.time/2f,
|
||||
Mathf.sin(Time.time + rand.random(200f), 55f, rand.random(target.hitSize() * 0.2f, target.hitSize() * 0.45f))
|
||||
).rotate(target instanceof Rotc rot ? rot.rotation() : 0f));
|
||||
|
||||
lastEnd.add(originX, originY);
|
||||
}
|
||||
|
||||
if(strength > 0.01f){
|
||||
float
|
||||
originX = x + Angles.trnsx(rotation, length),
|
||||
originY = y + Angles.trnsy(rotation, length);
|
||||
|
||||
Draw.z(Layer.flyingUnit + 1); //above all units
|
||||
|
||||
Draw.color(laserColor);
|
||||
|
||||
float f = (Time.time / 85f + rand.random(1f)) % 1f;
|
||||
|
||||
Draw.alpha(1f - Interp.pow5In.apply(f));
|
||||
Lines.stroke(strength * pulseStroke);
|
||||
Lines.circle(lastEnd.x, lastEnd.y, 1f + f * pulseRadius);
|
||||
|
||||
Draw.color(laserColor);
|
||||
Drawf.laser(team, laser, laserEnd, originX, originY, lastEnd.x, lastEnd.y, strength * beamWidth);
|
||||
Draw.z(Layer.flyingUnit + 1.1f);
|
||||
Draw.color(laserTopColor);
|
||||
Drawf.laser(team, laserTop, laserTopEnd, originX, originY, lastEnd.x, lastEnd.y, strength * beamWidth);
|
||||
Draw.color();
|
||||
}
|
||||
}
|
||||
|
||||
public class RepairPointBuild extends Building implements Ranged{
|
||||
public Unit target;
|
||||
public Vec2 offset = new Vec2(), lastEnd = new Vec2();
|
||||
public float strength, rotation = 90;
|
||||
|
||||
@Override
|
||||
@@ -78,17 +156,9 @@ public class RepairPoint extends Block{
|
||||
Drawf.shadow(region, x - (size / 2f), y - (size / 2f), rotation - 90);
|
||||
Draw.rect(region, x, y, rotation - 90);
|
||||
|
||||
if(target != null && Angles.angleDist(angleTo(target), rotation) < 30f){
|
||||
Draw.z(Layer.flyingUnit + 1); //above all units
|
||||
float ang = angleTo(target);
|
||||
float len = 5f;
|
||||
|
||||
Draw.color(laserColor);
|
||||
Drawf.laser(team, laser, laserEnd,
|
||||
x + Angles.trnsx(ang, len), y + Angles.trnsy(ang, len),
|
||||
target.x(), target.y(), strength);
|
||||
Draw.color();
|
||||
}
|
||||
drawBeam(x, y, rotation, length, id, target, team, strength,
|
||||
pulseStroke, pulseRadius, beamWidth, lastEnd, offset, laserColor, laserTopColor,
|
||||
laser, laserEnd, laserTop, laserTopEnd);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -98,21 +168,33 @@ public class RepairPoint extends Block{
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
boolean targetIsBeingRepaired = false;
|
||||
if(target != null && (target.dead() || target.dst(tile) - target.hitSize/2f > repairRadius || target.health() >= target.maxHealth())){
|
||||
target = null;
|
||||
}else if(target != null && consValid()){
|
||||
target.heal(repairSpeed * strength * edelta());
|
||||
rotation = Mathf.slerpDelta(rotation, angleTo(target), 0.5f * efficiency() * timeScale);
|
||||
targetIsBeingRepaired = true;
|
||||
float multiplier = 1f;
|
||||
if(acceptCoolant){
|
||||
var liq = consumes.<ConsumeLiquidBase>get(ConsumeType.liquid);
|
||||
multiplier = liq.valid(this) ? 1f + liquids.current().heatCapacity * coolantMultiplier : 1f;
|
||||
}
|
||||
|
||||
if(target != null && targetIsBeingRepaired){
|
||||
strength = Mathf.lerpDelta(strength, 1f, 0.08f * Time.delta);
|
||||
}else{
|
||||
strength = Mathf.lerpDelta(strength, 0f, 0.07f * Time.delta);
|
||||
if(target != null && (target.dead() || target.dst(tile) - target.hitSize/2f > repairRadius || target.health() >= target.maxHealth())){
|
||||
target = null;
|
||||
}
|
||||
|
||||
if(target == null){
|
||||
offset.setZero();
|
||||
}
|
||||
|
||||
boolean healed = false;
|
||||
|
||||
if(target != null && consValid()){
|
||||
float angle = Angles.angle(x, y, target.x + offset.x, target.y + offset.y);
|
||||
if(Angles.angleDist(angle, rotation) < 30f){
|
||||
healed = true;
|
||||
target.heal(repairSpeed * strength * edelta() * multiplier);
|
||||
}
|
||||
rotation = Mathf.slerpDelta(rotation, angle, 0.5f * efficiency() * timeScale);
|
||||
}
|
||||
|
||||
strength = Mathf.lerpDelta(strength, healed ? 1f : 0f, 0.08f * Time.delta);
|
||||
|
||||
if(timer(timerTarget, 20)){
|
||||
rect.setSize(repairRadius * 2).setCenter(x, y);
|
||||
target = Units.closest(team, x, y, repairRadius, Unit::damaged);
|
||||
|
||||
@@ -1,20 +1,12 @@
|
||||
package mindustry.world.blocks.units;
|
||||
|
||||
import arc.*;
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.payloads.*;
|
||||
import mindustry.world.blocks.production.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class UnitBlock extends PayloadAcceptor{
|
||||
public class UnitBlock extends PayloadBlock{
|
||||
|
||||
public UnitBlock(String name){
|
||||
super(name);
|
||||
@@ -31,7 +23,7 @@ public class UnitBlock extends PayloadAcceptor{
|
||||
build.spawned();
|
||||
}
|
||||
|
||||
public class UnitBuild extends PayloadAcceptorBuild<UnitPayload>{
|
||||
public class UnitBuild extends PayloadBlockBuild<UnitPayload>{
|
||||
public float progress, time, speedScl;
|
||||
|
||||
public void spawned(){
|
||||
|
||||
@@ -98,7 +98,7 @@ public class UnitFactory extends UnitBlock{
|
||||
table.row();
|
||||
for(var plan : p){
|
||||
if(plan.unit.unlockedNow()){
|
||||
table.image(plan.unit.icon(Cicon.small)).size(8 * 3).padRight(2).right();
|
||||
table.image(plan.unit.uiIcon).size(8 * 3).padRight(2).right();
|
||||
table.add(plan.unit.localizedName).left();
|
||||
table.add(Strings.autoFixed(plan.time / 60f, 1) + " " + Core.bundle.get("unit.seconds")).color(Color.lightGray).padLeft(12).left();
|
||||
table.row();
|
||||
@@ -143,6 +143,7 @@ public class UnitFactory extends UnitBlock{
|
||||
@Override
|
||||
public Object senseObject(LAccess sensor){
|
||||
if(sensor == LAccess.config) return currentPlan == -1 ? null : plans.get(currentPlan).unit;
|
||||
if(sensor == LAccess.progress) return Mathf.clamp(fraction());
|
||||
return super.senseObject(sensor);
|
||||
}
|
||||
|
||||
@@ -172,7 +173,7 @@ public class UnitFactory extends UnitBlock{
|
||||
table.table(t -> {
|
||||
t.left();
|
||||
t.image().update(i -> {
|
||||
i.setDrawable(currentPlan == -1 ? Icon.cancel : reg.set(plans.get(currentPlan).unit.icon(Cicon.medium)));
|
||||
i.setDrawable(currentPlan == -1 ? Icon.cancel : reg.set(plans.get(currentPlan).unit.uiIcon));
|
||||
i.setScaling(Scaling.fit);
|
||||
i.setColor(currentPlan == -1 ? Color.lightGray : Color.white);
|
||||
}).size(32).padBottom(-4).padRight(2);
|
||||
|
||||
Reference in New Issue
Block a user