Partial 7.0 merge - API preview
This commit is contained in:
@@ -29,7 +29,6 @@ import mindustry.world.blocks.environment.*;
|
||||
import mindustry.world.blocks.power.*;
|
||||
import mindustry.world.consumers.*;
|
||||
import mindustry.world.meta.*;
|
||||
import mindustry.world.meta.values.*;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.util.*;
|
||||
@@ -45,6 +44,7 @@ public class Block extends UnlockableContent{
|
||||
public boolean consumesPower = true;
|
||||
public boolean outputsPower = false;
|
||||
public boolean outputsPayload = false;
|
||||
public boolean acceptsPayload = false;
|
||||
public boolean outputFacing = true;
|
||||
public boolean acceptsItems = false;
|
||||
|
||||
@@ -77,6 +77,8 @@ public class Block extends UnlockableContent{
|
||||
public boolean solidifes;
|
||||
/** whether this is rotateable */
|
||||
public boolean rotate;
|
||||
/** whether to draw a rotation arrow - this does not apply to lines of blocks */
|
||||
public boolean drawArrow = true;
|
||||
/** for static blocks only: if true, tile data() is saved in world data. */
|
||||
public boolean saveData;
|
||||
/** whether you can break this with rightclick */
|
||||
@@ -105,6 +107,8 @@ public class Block extends UnlockableContent{
|
||||
public boolean noUpdateDisabled = false;
|
||||
/** Whether to use this block's color in the minimap. Only used for overlays. */
|
||||
public boolean useColor = true;
|
||||
/** item that drops from this block, used for drills */
|
||||
public @Nullable Item itemDrop = null;
|
||||
/** tile entity health */
|
||||
public int health = -1;
|
||||
/** base block explosiveness */
|
||||
@@ -115,8 +119,12 @@ public class Block extends UnlockableContent{
|
||||
public int size = 1;
|
||||
/** multiblock offset */
|
||||
public float offset = 0f;
|
||||
/** Whether to draw this block in the expanded draw range. */
|
||||
/** Deprecated for removal: Use clipSize instead.
|
||||
* This does nothing, and is kept to preserve compatibility with v6 mods. */
|
||||
@Deprecated
|
||||
public boolean expanded = false;
|
||||
/** Clipping size of this block. Should be as large as the block will draw. */
|
||||
public float clipSize = -1f;
|
||||
/** Max of timers used. */
|
||||
public int timers = 0;
|
||||
/** Cache layer. Only used for 'cached' rendering. */
|
||||
@@ -144,6 +152,12 @@ public class Block extends UnlockableContent{
|
||||
public boolean consumesTap;
|
||||
/** Whether to draw the glow of the liquid for this block, if it has one. */
|
||||
public boolean drawLiquidLight = true;
|
||||
/** Environmental flags that are *all* required for this block to function. 0 = any environment */
|
||||
public int envRequired = 0;
|
||||
/** The environment flags that this block can function in. If the env matches any of these, it will be enabled. */
|
||||
public int envEnabled = Env.terrestrial;
|
||||
/** The environment flags that this block *cannot* function in. If the env matches any of these, it will be *disabled*. */
|
||||
public int envDisabled = 0;
|
||||
/** Whether to periodically sync this block across the network. */
|
||||
public boolean sync;
|
||||
/** Whether this block uses conveyor-type placement mode. */
|
||||
@@ -167,7 +181,9 @@ public class Block extends UnlockableContent{
|
||||
public Color outlineColor = Color.valueOf("404049");
|
||||
/** Whether any icon region has an outline added. */
|
||||
public boolean outlineIcon = false;
|
||||
/** Which of the icon regions gets the outline added. */
|
||||
/** Outline icon radius. */
|
||||
public int outlineRadius = 4;
|
||||
/** Which of the icon regions gets the outline added. Uses last icon if <= 0. */
|
||||
public int outlinedIcon = -1;
|
||||
/** Whether this block has a shadow under it. */
|
||||
public boolean hasShadow = true;
|
||||
@@ -179,7 +195,7 @@ public class Block extends UnlockableContent{
|
||||
public Color lightColor = Color.white.cpy();
|
||||
/**
|
||||
* Whether this environmental block passively emits light.
|
||||
* Not valid for non-environmental blocks. */
|
||||
* Does not change behavior for non-environmental blocks, but still updates clipSize. */
|
||||
public boolean emitLight = false;
|
||||
/** Radius of the light emitted by this block. */
|
||||
public float lightRadius = 60f;
|
||||
@@ -198,19 +214,23 @@ public class Block extends UnlockableContent{
|
||||
public ItemStack[] requirements = {};
|
||||
/** Category in place menu. */
|
||||
public Category category = Category.distribution;
|
||||
/** Cost of building this block; do not modify directly! */
|
||||
public float buildCost;
|
||||
/** Time to build this block in ticks; do not modify directly! */
|
||||
public float buildCost = 20f;
|
||||
/** Whether this block is visible and can currently be built. */
|
||||
public BuildVisibility buildVisibility = BuildVisibility.hidden;
|
||||
/** Multiplier for speed of building this block. */
|
||||
public float buildCostMultiplier = 1f;
|
||||
/** Build completion at which deconstruction finishes. */
|
||||
public float deconstructThreshold = 0f;
|
||||
/** If true, this block deconstructs immediately. Instant deconstruction implies no resource refund. */
|
||||
public boolean instantDeconstruct = false;
|
||||
/** Effect for breaking the block. Passes size as rotation. */
|
||||
public Effect breakEffect = Fx.breakBlock;
|
||||
/** Multiplier for cost of research in tech tree. */
|
||||
public float researchCostMultiplier = 1;
|
||||
/** Whether this block has instant transfer.*/
|
||||
public boolean instantTransfer = false;
|
||||
/** Whether you can rotate this block with Keybind rotateplaced + Scroll Wheel. */
|
||||
/** Whether you can rotate this block after it is placed. */
|
||||
public boolean quickRotate = true;
|
||||
/** Main subclass. Non-anonymous. */
|
||||
public @Nullable Class<?> subclass;
|
||||
@@ -318,7 +338,7 @@ public class Block extends UnlockableContent{
|
||||
}
|
||||
|
||||
public TextureRegion getDisplayIcon(Tile tile){
|
||||
return tile.build == null ? icon(Cicon.medium) : tile.build.getDisplayIcon();
|
||||
return tile.build == null ? uiIcon : tile.build.getDisplayIcon();
|
||||
}
|
||||
|
||||
public String getDisplayName(Tile tile){
|
||||
@@ -363,7 +383,7 @@ public class Block extends UnlockableContent{
|
||||
|
||||
if(canBeBuilt()){
|
||||
stats.add(Stat.buildTime, buildCost / 60, StatUnit.seconds);
|
||||
stats.add(Stat.buildCost, new ItemListValue(false, requirements));
|
||||
stats.add(Stat.buildCost, StatValues.items(false, requirements));
|
||||
}
|
||||
|
||||
if(instantTransfer){
|
||||
@@ -405,7 +425,9 @@ public class Block extends UnlockableContent{
|
||||
bars.add("items", entity -> new Bar(() -> Core.bundle.format("bar.items", entity.items.total()), () -> Pal.items, () -> (float)entity.items.total() / itemCapacity));
|
||||
}
|
||||
|
||||
if(flags.contains(BlockFlag.unitModifier)) stats.add(Stat.maxUnits, (unitCapModifier < 0 ? "-" : "+") + Math.abs(unitCapModifier));
|
||||
if(unitCapModifier != 0){
|
||||
stats.add(Stat.maxUnits, (unitCapModifier < 0 ? "-" : "+") + Math.abs(unitCapModifier));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canReplace(Block other){
|
||||
@@ -470,7 +492,7 @@ public class Block extends UnlockableContent{
|
||||
}
|
||||
|
||||
public TextureRegion getRequestRegion(BuildPlan req, Eachable<BuildPlan> list){
|
||||
return icon(Cicon.full);
|
||||
return fullIcon;
|
||||
}
|
||||
|
||||
public void drawRequestConfig(BuildPlan req, Eachable<BuildPlan> list){
|
||||
@@ -554,6 +576,11 @@ public class Block extends UnlockableContent{
|
||||
return editorVariantRegions;
|
||||
}
|
||||
|
||||
/** @return special icons to outline and save with an -outline variant. Vanilla only. */
|
||||
public TextureRegion[] makeIconRegions(){
|
||||
return new TextureRegion[0];
|
||||
}
|
||||
|
||||
protected TextureRegion[] icons(){
|
||||
//use team region in vanilla team blocks
|
||||
return teamRegion.found() && minfo.mod == null ? new TextureRegion[]{region, teamRegions[Team.sharded.id]} : new TextureRegion[]{region};
|
||||
@@ -564,7 +591,7 @@ public class Block extends UnlockableContent{
|
||||
}
|
||||
|
||||
public TextureRegion[] variantRegions(){
|
||||
return variantRegions == null ? (variantRegions = new TextureRegion[]{icon(Cicon.full)}) : variantRegions;
|
||||
return variantRegions == null ? (variantRegions = new TextureRegion[]{fullIcon}) : variantRegions;
|
||||
}
|
||||
|
||||
public boolean hasBuilding(){
|
||||
@@ -588,7 +615,12 @@ public class Block extends UnlockableContent{
|
||||
}
|
||||
|
||||
public boolean isPlaceable(){
|
||||
return isVisible() && (!state.rules.bannedBlocks.contains(this) || state.rules.editor);
|
||||
return isVisible() && (!state.rules.bannedBlocks.contains(this) || state.rules.editor) && supportsEnv(state.rules.environment);
|
||||
}
|
||||
|
||||
/** @return whether this block supports a specific environment. */
|
||||
public boolean supportsEnv(int env){
|
||||
return (envEnabled & env) != 0 && (envDisabled & env) == 0 && (envRequired == 0 || (envRequired & env) == envRequired);
|
||||
}
|
||||
|
||||
/** Called when building of this block begins. */
|
||||
@@ -737,16 +769,25 @@ public class Block extends UnlockableContent{
|
||||
health = size * size * 40;
|
||||
}
|
||||
|
||||
clipSize = Math.max(clipSize, size * tilesize);
|
||||
|
||||
if(emitLight){
|
||||
clipSize = Math.max(clipSize, lightRadius * 2f);
|
||||
}
|
||||
|
||||
if(group == BlockGroup.transportation || consumes.has(ConsumeType.item) || category == Category.distribution){
|
||||
acceptsItems = true;
|
||||
}
|
||||
|
||||
offset = ((size + 1) % 2) * tilesize / 2f;
|
||||
|
||||
buildCost = 0f;
|
||||
for(ItemStack stack : requirements){
|
||||
buildCost += stack.amount * stack.item.cost;
|
||||
if(requirements.length > 0){
|
||||
buildCost = 0f;
|
||||
for(ItemStack stack : requirements){
|
||||
buildCost += stack.amount * stack.item.cost;
|
||||
}
|
||||
}
|
||||
|
||||
buildCost *= buildCostMultiplier;
|
||||
|
||||
if(consumes.has(ConsumeType.power)) hasPower = true;
|
||||
@@ -772,9 +813,10 @@ public class Block extends UnlockableContent{
|
||||
}
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
@Override
|
||||
public void load(){
|
||||
super.load();
|
||||
|
||||
region = Core.atlas.find(name);
|
||||
|
||||
ContentRegions.loadRegions(this);
|
||||
@@ -795,62 +837,41 @@ public class Block extends UnlockableContent{
|
||||
public void createIcons(MultiPacker packer){
|
||||
super.createIcons(packer);
|
||||
|
||||
packer.add(PageType.editor, name + "-icon-editor", Core.atlas.getPixmap((AtlasRegion)icon(Cicon.full)));
|
||||
|
||||
if(!synthetic()){
|
||||
PixmapRegion image = Core.atlas.getPixmap((AtlasRegion)icon(Cicon.full));
|
||||
mapColor.set(image.getPixel(image.width/2, image.height/2));
|
||||
PixmapRegion image = Core.atlas.getPixmap(fullIcon);
|
||||
mapColor.set(image.get(image.width/2, image.height/2));
|
||||
}
|
||||
|
||||
getGeneratedIcons();
|
||||
|
||||
Pixmap last = null;
|
||||
|
||||
var gen = icons();
|
||||
|
||||
if(outlineIcon){
|
||||
final int radius = 4;
|
||||
PixmapRegion region = Core.atlas.getPixmap(getGeneratedIcons()[outlinedIcon >= 0 ? outlinedIcon : getGeneratedIcons().length -1]);
|
||||
Pixmap out = new Pixmap(region.width, region.height);
|
||||
Color color = new Color();
|
||||
for(int x = 0; x < region.width; x++){
|
||||
for(int y = 0; y < region.height; y++){
|
||||
|
||||
region.getPixel(x, y, color);
|
||||
out.draw(x, y, color);
|
||||
if(color.a < 1f){
|
||||
boolean found = false;
|
||||
outer:
|
||||
for(int rx = -radius; rx <= radius; rx++){
|
||||
for(int ry = -radius; ry <= radius; ry++){
|
||||
if(Structs.inBounds(rx + x, ry + y, region.width, region.height) && Mathf.within(rx, ry, radius) && color.set(region.getPixel(rx + x, ry + y)).a > 0.01f){
|
||||
found = true;
|
||||
break outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(found){
|
||||
out.draw(x, y, outlineColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
PixmapRegion region = Core.atlas.getPixmap(gen[outlinedIcon >= 0 ? outlinedIcon : gen.length -1]);
|
||||
Pixmap out = last = Pixmaps.outline(region, outlineColor, outlineRadius);
|
||||
if(Core.settings.getBool("linear")){
|
||||
Pixmaps.bleed(out);
|
||||
}
|
||||
last = out;
|
||||
|
||||
packer.add(PageType.main, name, out);
|
||||
}
|
||||
|
||||
if(generatedIcons.length > 1){
|
||||
Pixmap base = Core.atlas.getPixmap(generatedIcons[0]).crop();
|
||||
for(int i = 1; i < generatedIcons.length; i++){
|
||||
if(i == generatedIcons.length - 1 && last != null){
|
||||
base.drawPixmap(last);
|
||||
var editorBase = Core.atlas.getPixmap(fullIcon);
|
||||
|
||||
if(gen.length > 1){
|
||||
Pixmap base = Core.atlas.getPixmap(gen[0]).crop();
|
||||
for(int i = 1; i < gen.length; i++){
|
||||
if(i == gen.length - 1 && last != null){
|
||||
base.draw(last, 0, 0, true);
|
||||
}else{
|
||||
base.draw(Core.atlas.getPixmap(generatedIcons[i]));
|
||||
base.draw(Core.atlas.getPixmap(gen[i]), true);
|
||||
}
|
||||
}
|
||||
packer.add(PageType.main, "block-" + name + "-full", base);
|
||||
generatedIcons = null;
|
||||
Arrays.fill(cicons, null);
|
||||
|
||||
editorBase = new PixmapRegion(base);
|
||||
}
|
||||
|
||||
packer.add(PageType.editor, name + "-icon-editor", editorBase);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,6 +35,13 @@ public class Build{
|
||||
|
||||
int rotation = tile.build != null ? tile.build.rotation : 0;
|
||||
Block previous = tile.block();
|
||||
|
||||
//instantly deconstruct if necessary
|
||||
if(previous.instantDeconstruct){
|
||||
ConstructBlock.deconstructFinish(tile, previous, unit);
|
||||
return;
|
||||
}
|
||||
|
||||
Block sub = ConstructBlock.get(previous.size);
|
||||
|
||||
Seq<Building> prevBuild = new Seq<>(1);
|
||||
@@ -75,6 +82,14 @@ public class Build{
|
||||
return;
|
||||
}
|
||||
|
||||
//break all props in the way
|
||||
tile.getLinkedTilesAs(result, out -> {
|
||||
if(out.block != Blocks.air && out.block.alwaysReplace){
|
||||
out.block.breakEffect.at(out.drawx(), out.drawy(), out.block.size, out.block.mapColor);
|
||||
out.remove();
|
||||
}
|
||||
});
|
||||
|
||||
Block previous = tile.block();
|
||||
Block sub = ConstructBlock.get(result.size);
|
||||
Seq<Building> prevBuild = new Seq<>(9);
|
||||
@@ -159,7 +174,7 @@ public class Build{
|
||||
//this could be buggy and abuse-able, so I'm not enabling it yet
|
||||
//note that this requires a change in BuilderComp as well
|
||||
//(type == check.block() && check.centerX() == x && check.centerY() == y && check.build != null && check.build.health < check.build.maxHealth - 0.0001f) ||
|
||||
(check.build instanceof ConstructBuild build && build.cblock == type && check.centerX() == tile.x && check.centerY() == tile.y)) && //same type in construction
|
||||
(check.build instanceof ConstructBuild build && build.current == type && check.centerX() == tile.x && check.centerY() == tile.y)) && //same type in construction
|
||||
type.bounds(tile.x, tile.y, Tmp.r1).grow(0.01f).contains(check.block.bounds(check.centerX(), check.centerY(), Tmp.r2))) || //no replacement
|
||||
(type.requiresWater && check.floor().liquidDrop != Liquids.water) //requires water but none found
|
||||
) return false;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package mindustry.world;
|
||||
|
||||
import arc.*;
|
||||
import arc.func.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
@@ -11,6 +12,7 @@ import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
@@ -19,7 +21,9 @@ import mindustry.world.blocks.environment.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class Tile implements Position, QuadTreeObject, Displayable{
|
||||
static final ObjectSet<Building> tileSet = new ObjectSet<>();
|
||||
private static final TileChangeEvent tileChange = new TileChangeEvent();
|
||||
private static final TilePreChangeEvent preChange = new TilePreChangeEvent();
|
||||
private static final ObjectSet<Building> tileSet = new ObjectSet<>();
|
||||
|
||||
/** Extra data for very specific blocks. */
|
||||
public byte data;
|
||||
@@ -108,16 +112,18 @@ public class Tile implements Position, QuadTreeObject, Displayable{
|
||||
* Takes flammability of floor liquid into account.
|
||||
*/
|
||||
public float getFlammability(){
|
||||
if(!block.hasItems){
|
||||
if(floor.liquidDrop != null && !block.solid){
|
||||
return floor.liquidDrop.flammability;
|
||||
}
|
||||
if(block == Blocks.air){
|
||||
if(floor.liquidDrop != null) return floor.liquidDrop.flammability;
|
||||
return 0;
|
||||
}else if(build != null){
|
||||
float result = build.items.sum((item, amount) -> item.flammability * amount);
|
||||
float result = 0f;
|
||||
|
||||
if(block.hasItems){
|
||||
result += build.items.sum((item, amount) -> item.flammability * amount) / block.itemCapacity * Mathf.clamp(block.itemCapacity / 2.4f, 1f, 3f);
|
||||
}
|
||||
|
||||
if(block.hasLiquids){
|
||||
result += build.liquids.sum((liquid, amount) -> liquid.flammability * amount / 3f);
|
||||
result += build.liquids.sum((liquid, amount) -> liquid.flammability * amount / 1.6f) / block.liquidCapacity * Mathf.clamp(block.liquidCapacity / 30f, 1f, 2f);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -166,6 +172,7 @@ public class Tile implements Position, QuadTreeObject, Displayable{
|
||||
return build == null ? Team.derelict : build.team;
|
||||
}
|
||||
|
||||
/** Do not call unless you know what you are doing! This does not update the indexer! */
|
||||
public void setTeam(Team team){
|
||||
if(build != null){
|
||||
build.team(team);
|
||||
@@ -197,10 +204,12 @@ public class Tile implements Position, QuadTreeObject, Displayable{
|
||||
|
||||
if(type.isStatic() || this.block.isStatic()){
|
||||
recache();
|
||||
recacheWall();
|
||||
}
|
||||
|
||||
this.block = type;
|
||||
preChanged();
|
||||
|
||||
this.block = type;
|
||||
changeBuild(team, entityprov, (byte)Mathf.mod(rotation, 4));
|
||||
|
||||
if(build != null){
|
||||
@@ -287,10 +296,17 @@ public class Tile implements Position, QuadTreeObject, Displayable{
|
||||
circle(radius, (x, y) -> cons.get(world.rawTile(x, y)));
|
||||
}
|
||||
|
||||
public void recacheWall(){
|
||||
if(!headless && !world.isGenerating()){
|
||||
renderer.blocks.recacheWall(this);
|
||||
}
|
||||
}
|
||||
|
||||
public void recache(){
|
||||
if(!headless && !world.isGenerating()){
|
||||
renderer.blocks.floor.recacheTile(this);
|
||||
renderer.minimap.update(this);
|
||||
//update neighbor tiles as well
|
||||
for(int i = 0; i < 8; i++){
|
||||
Tile other = world.tile(x + Geometry.d8[i].x, y + Geometry.d8[i].y);
|
||||
if(other != null){
|
||||
@@ -487,6 +503,13 @@ public class Tile implements Position, QuadTreeObject, Displayable{
|
||||
return overlay == Blocks.air || overlay.itemDrop == null ? floor.itemDrop : overlay.itemDrop;
|
||||
}
|
||||
|
||||
public @Nullable Item wallDrop(){
|
||||
return block.solid ?
|
||||
block.itemDrop != null ? block.itemDrop :
|
||||
overlay instanceof WallOreBlock ? overlay.itemDrop :
|
||||
null : null;
|
||||
}
|
||||
|
||||
public int staticDarkness(){
|
||||
return block.solid && block.fillsTile && !block.synthetic() ? data : 0;
|
||||
}
|
||||
@@ -497,6 +520,10 @@ public class Tile implements Position, QuadTreeObject, Displayable{
|
||||
}
|
||||
|
||||
protected void preChanged(){
|
||||
if(!world.isGenerating()){
|
||||
Events.fire(preChange.set(this));
|
||||
}
|
||||
|
||||
if(build != null){
|
||||
//only call removed() for the center block - this only gets called once.
|
||||
build.onRemoved();
|
||||
@@ -514,6 +541,9 @@ public class Tile implements Position, QuadTreeObject, Displayable{
|
||||
if(other != null){
|
||||
//reset entity and block *manually* - thus, preChanged() will not be called anywhere else, for multiblocks
|
||||
if(other != this){ //do not remove own entity so it can be processed in changed()
|
||||
//manually call pre-change event for other tile
|
||||
Events.fire(preChange.set(other));
|
||||
|
||||
other.build = null;
|
||||
other.block = Blocks.air;
|
||||
|
||||
@@ -525,12 +555,6 @@ public class Tile implements Position, QuadTreeObject, Displayable{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//recache when static blocks get changed
|
||||
if(block.isStatic()){
|
||||
recache();
|
||||
}
|
||||
}
|
||||
|
||||
protected void changeBuild(Team team, Prov<Building> entityprov, int rotation){
|
||||
@@ -584,12 +608,18 @@ public class Tile implements Position, QuadTreeObject, Displayable{
|
||||
}
|
||||
|
||||
protected void fireChanged(){
|
||||
world.notifyChanged(this);
|
||||
if(!world.isGenerating()){
|
||||
Events.fire(tileChange.set(this));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void display(Table table){
|
||||
Block toDisplay = overlay.itemDrop != null ? overlay : floor;
|
||||
|
||||
Block toDisplay =
|
||||
block.itemDrop != null ? block :
|
||||
overlay.itemDrop != null || wallDrop() != null ? overlay :
|
||||
floor;
|
||||
|
||||
table.table(t -> {
|
||||
t.left();
|
||||
@@ -635,8 +665,7 @@ public class Tile implements Position, QuadTreeObject, Displayable{
|
||||
@Remote(called = Loc.server)
|
||||
public static void setTeam(Building build, Team team){
|
||||
if(build != null){
|
||||
build.team = team;
|
||||
indexer.updateIndices(build.tile);
|
||||
build.changeTeam(team);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -8,10 +8,10 @@ import mindustry.world.meta.*;
|
||||
/** An abstract class that defines a type of resource that a block can consume. */
|
||||
public abstract class Consume{
|
||||
/** If true, this consumer will not influence consumer validity. */
|
||||
protected boolean optional;
|
||||
public boolean optional;
|
||||
/** If true, this consumer will be displayed as a boost input. */
|
||||
protected boolean booster;
|
||||
protected boolean update = true;
|
||||
public boolean booster;
|
||||
public boolean update = true;
|
||||
|
||||
/**
|
||||
* Apply a filter to items accepted.
|
||||
|
||||
@@ -46,7 +46,7 @@ public class ConsumeItemDynamic extends Consume{
|
||||
int i = 0;
|
||||
|
||||
for(ItemStack stack : items.get(tile)){
|
||||
table.add(new ReqImage(new ItemImage(stack.item.icon(Cicon.medium), stack.amount),
|
||||
table.add(new ReqImage(new ItemImage(stack.item.uiIcon, stack.amount),
|
||||
() -> tile.items != null && tile.items.has(stack.item, stack.amount))).padRight(8).left();
|
||||
if(++i % 4 == 0) table.row();
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.meta.*;
|
||||
import mindustry.world.meta.values.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@@ -31,7 +30,7 @@ public class ConsumeItemFilter extends Consume{
|
||||
@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), 1),
|
||||
content.items().each(i -> filter.get(i) && i.unlockedNow(), item -> image.add(new ReqImage(new ItemImage(item.uiIcon, 1),
|
||||
() -> tile.items != null && tile.items.has(item))));
|
||||
|
||||
table.add(image).size(8 * 4);
|
||||
@@ -71,6 +70,6 @@ public class ConsumeItemFilter extends Consume{
|
||||
|
||||
@Override
|
||||
public void display(Stats stats){
|
||||
stats.add(booster ? Stat.booster : Stat.input, new ItemFilterValue(filter));
|
||||
stats.add(booster ? Stat.booster : Stat.input, stats.timePeriod < 0 ? StatValues.items(filter) : StatValues.items(stats.timePeriod, filter));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.meta.*;
|
||||
import mindustry.world.meta.values.*;
|
||||
|
||||
public class ConsumeItems extends Consume{
|
||||
public final ItemStack[] items;
|
||||
@@ -22,7 +21,7 @@ public class ConsumeItems extends Consume{
|
||||
|
||||
@Override
|
||||
public void applyItemFilter(Bits filter){
|
||||
for(ItemStack stack : items){
|
||||
for(var stack : items){
|
||||
filter.set(stack.item.id);
|
||||
}
|
||||
}
|
||||
@@ -36,10 +35,10 @@ public class ConsumeItems extends Consume{
|
||||
public void build(Building tile, Table table){
|
||||
table.table(c -> {
|
||||
int i = 0;
|
||||
for(ItemStack stack : items){
|
||||
c.add(new ReqImage(new ItemImage(stack.item.icon(Cicon.medium), stack.amount),
|
||||
for(var stack : items){
|
||||
c.add(new ReqImage(new ItemImage(stack.item.uiIcon, stack.amount),
|
||||
() -> tile.items != null && tile.items.has(stack.item, stack.amount))).padRight(8);
|
||||
if(++i % 4 == 0) table.row();
|
||||
if(++i % 4 == 0) c.row();
|
||||
}
|
||||
}).left();
|
||||
}
|
||||
@@ -56,7 +55,7 @@ public class ConsumeItems extends Consume{
|
||||
|
||||
@Override
|
||||
public void trigger(Building entity){
|
||||
for(ItemStack stack : items){
|
||||
for(var stack : items){
|
||||
entity.items.remove(stack);
|
||||
}
|
||||
}
|
||||
@@ -68,6 +67,6 @@ public class ConsumeItems extends Consume{
|
||||
|
||||
@Override
|
||||
public void display(Stats stats){
|
||||
stats.add(booster ? Stat.booster : Stat.input, new ItemListValue(items));
|
||||
stats.add(booster ? Stat.booster : Stat.input, stats.timePeriod < 0 ? StatValues.items(items) : StatValues.items(stats.timePeriod, items));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class ConsumeLiquid extends ConsumeLiquidBase{
|
||||
public final Liquid liquid;
|
||||
|
||||
@@ -26,7 +28,7 @@ public class ConsumeLiquid extends ConsumeLiquidBase{
|
||||
|
||||
@Override
|
||||
public void build(Building tile, Table table){
|
||||
table.add(new ReqImage(liquid.icon(Cicon.medium), () -> valid(tile))).size(8 * 4);
|
||||
table.add(new ReqImage(liquid.uiIcon, () -> valid(tile))).size(iconMed).top().left();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -7,7 +7,6 @@ import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.meta.*;
|
||||
import mindustry.world.meta.values.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@@ -28,7 +27,7 @@ public class ConsumeLiquidFilter extends ConsumeLiquidBase{
|
||||
public void build(Building build, Table table){
|
||||
Seq<Liquid> list = content.liquids().select(l -> !l.isHidden() && filter.get(l));
|
||||
MultiReqImage image = new MultiReqImage();
|
||||
list.each(liquid -> image.add(new ReqImage(liquid.icon(Cicon.medium), () ->
|
||||
list.each(liquid -> image.add(new ReqImage(liquid.uiIcon, () ->
|
||||
build.liquids != null && build.liquids.current() == liquid && build.liquids.get(liquid) >= Math.max(use(build), amount * build.delta()))));
|
||||
|
||||
table.add(image).size(8 * 4);
|
||||
@@ -51,6 +50,6 @@ public class ConsumeLiquidFilter extends ConsumeLiquidBase{
|
||||
|
||||
@Override
|
||||
public void display(Stats stats){
|
||||
stats.add(booster ? Stat.booster : Stat.input, new LiquidFilterValue(filter, amount * 60f, true));
|
||||
stats.add(booster ? Stat.booster : Stat.input, StatValues.liquids(filter, amount * 60f, true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,17 +15,21 @@ public class DrawAnimation extends DrawBlock{
|
||||
public TextureRegion liquid, top;
|
||||
|
||||
@Override
|
||||
public void draw(GenericCrafterBuild entity){
|
||||
Draw.rect(entity.block.region, entity.x, entity.y);
|
||||
public void draw(GenericCrafterBuild build){
|
||||
Draw.rect(build.block.region, build.x, build.y);
|
||||
Draw.rect(
|
||||
sine ?
|
||||
frames[(int)Mathf.absin(entity.totalProgress, frameSpeed, frameCount - 0.001f)] :
|
||||
frames[(int)((entity.totalProgress / frameSpeed) % frameCount)],
|
||||
entity.x, entity.y);
|
||||
Draw.color(Color.clear, entity.liquids.current().color, entity.liquids.total() / entity.block.liquidCapacity);
|
||||
Draw.rect(liquid, entity.x, entity.y);
|
||||
Draw.color();
|
||||
Draw.rect(top, entity.x, entity.y);
|
||||
frames[(int)Mathf.absin(build.totalProgress, frameSpeed, frameCount - 0.001f)] :
|
||||
frames[(int)((build.totalProgress / frameSpeed) % frameCount)],
|
||||
build.x, build.y);
|
||||
if(build.liquids != null){
|
||||
Draw.color(Color.clear, build.liquids.current().color, build.liquids.total() / build.block.liquidCapacity);
|
||||
Draw.rect(liquid, build.x, build.y);
|
||||
Draw.color();
|
||||
}
|
||||
if(top.found()){
|
||||
Draw.rect(top, build.x, build.y);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
69
core/src/mindustry/world/draw/DrawArcSmelter.java
Normal file
69
core/src/mindustry/world/draw/DrawArcSmelter.java
Normal file
@@ -0,0 +1,69 @@
|
||||
package mindustry.world.draw;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.production.GenericCrafter.*;
|
||||
|
||||
//TODO
|
||||
public class DrawArcSmelter extends DrawBlock{
|
||||
public TextureRegion top, bottom;
|
||||
public Color flameColor = Color.valueOf("f58349"), midColor = Color.valueOf("f2d585");
|
||||
public float flameRad = 1f, circleSpace = 2f, flameRadiusScl = 3f, flameRadiusMag = 0.3f, circleStroke = 1.5f;
|
||||
|
||||
public float alpha = 0.68f;
|
||||
public int particles = 25;
|
||||
public float particleLife = 40f, particleRad = 7f, particleStroke = 1.1f, particleLen = 3f;
|
||||
|
||||
@Override
|
||||
public void draw(GenericCrafterBuild build){
|
||||
Draw.rect(bottom, build.x, build.y);
|
||||
|
||||
if(build.warmup > 0f && flameColor.a > 0.001f){
|
||||
|
||||
|
||||
Lines.stroke(circleStroke * build.warmup);
|
||||
|
||||
float si = Mathf.absin(flameRadiusScl, flameRadiusMag);
|
||||
float a = alpha * build.warmup;
|
||||
Draw.blend(Blending.additive);
|
||||
|
||||
Draw.color(midColor, a);
|
||||
Fill.circle(build.x, build.y, flameRad + si);
|
||||
|
||||
Draw.color(flameColor, a);
|
||||
Lines.circle(build.x, build.y, (flameRad + circleSpace + si) * build.warmup);
|
||||
|
||||
Lines.stroke(particleStroke * build.warmup);
|
||||
|
||||
float base = (Time.time / particleLife);
|
||||
rand.setSeed(build.id);
|
||||
for(int i = 0; i < particles; i++){
|
||||
float fin = (rand.random(1f) + base) % 1f, fout = 1f - fin;
|
||||
float angle = rand.random(360f);
|
||||
float len = particleRad * Interp.pow2Out.apply(fin);
|
||||
Lines.lineAngle(build.x + Angles.trnsx(angle, len), build.y + Angles.trnsy(angle, len), angle, particleLen * fout * build.warmup);
|
||||
}
|
||||
|
||||
Draw.blend();
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
Draw.rect(top, build.x, build.y);
|
||||
Draw.rect(build.block.region, build.x, build.y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(Block block){
|
||||
top = Core.atlas.find(block.name + "-top");
|
||||
bottom = Core.atlas.find(block.name + "-bottom");
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureRegion[] icons(Block block){
|
||||
return new TextureRegion[]{bottom, block.region, top};
|
||||
}
|
||||
}
|
||||
@@ -1,16 +1,23 @@
|
||||
package mindustry.world.draw;
|
||||
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.production.GenericCrafter.*;
|
||||
|
||||
/** An implementation of custom rendering behavior for a block.
|
||||
* This is used mostly for mods. */
|
||||
public class DrawBlock{
|
||||
protected static final Rand rand = new Rand();
|
||||
|
||||
/** Draws the block. */
|
||||
public void draw(GenericCrafterBuild entity){
|
||||
Draw.rect(entity.block.region, entity.x, entity.y, entity.block.rotate ? entity.rotdeg() : 0);
|
||||
public void draw(GenericCrafterBuild build){
|
||||
Draw.rect(build.block.region, build.x, build.y, build.block.rotate ? build.rotdeg() : 0);
|
||||
}
|
||||
|
||||
/** Draws any extra light for the block. */
|
||||
public void drawLight(GenericCrafterBuild build){
|
||||
|
||||
}
|
||||
|
||||
/** Load any relevant texture regions. */
|
||||
|
||||
57
core/src/mindustry/world/draw/DrawCells.java
Normal file
57
core/src/mindustry/world/draw/DrawCells.java
Normal file
@@ -0,0 +1,57 @@
|
||||
package mindustry.world.draw;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.production.GenericCrafter.*;
|
||||
|
||||
public class DrawCells extends DrawBlock{
|
||||
public TextureRegion bottom, middle;
|
||||
public Color color = Color.white.cpy(), particleColorFrom = Color.black.cpy(), particleColorTo = Color.black.cpy();
|
||||
public int particles = 12;
|
||||
public float range = 4f, recurrence = 6f, radius = 3f, lifetime = 60f;
|
||||
|
||||
@Override
|
||||
public void draw(GenericCrafterBuild build){
|
||||
|
||||
Draw.rect(bottom, build.x, build.y);
|
||||
|
||||
Drawf.liquid(middle, build.x, build.y, build.warmup, color);
|
||||
|
||||
if(build.warmup > 0.001f){
|
||||
rand.setSeed(build.id);
|
||||
for(int i = 0; i < particles; i++){
|
||||
float offset = rand.nextFloat() * 999999f;
|
||||
float x = rand.range(range), y = rand.range(range);
|
||||
float fin = 1f - (((Time.time + offset) / lifetime) % recurrence);
|
||||
float ca = rand.random(0.1f, 1f);
|
||||
float fslope = Mathf.slope(fin);
|
||||
|
||||
if(fin > 0){
|
||||
Draw.color(particleColorFrom, particleColorTo, ca);
|
||||
Draw.alpha(build.warmup);
|
||||
|
||||
Fill.circle(build.x + x, build.y + y, fslope * radius);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Draw.color();
|
||||
Draw.rect(build.block.region, build.x, build.y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(Block block){
|
||||
bottom = Core.atlas.find(block.name + "-bottom");
|
||||
middle = Core.atlas.find(block.name + "-middle");
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureRegion[] icons(Block block){
|
||||
return new TextureRegion[]{bottom, block.region};
|
||||
}
|
||||
}
|
||||
56
core/src/mindustry/world/draw/DrawCultivator.java
Normal file
56
core/src/mindustry/world/draw/DrawCultivator.java
Normal file
@@ -0,0 +1,56 @@
|
||||
package mindustry.world.draw;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.util.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.production.GenericCrafter.*;
|
||||
|
||||
public class DrawCultivator extends DrawBlock{
|
||||
public Color plantColor = Color.valueOf("5541b1");
|
||||
public Color plantColorLight = Color.valueOf("7457ce");
|
||||
public Color bottomColor = Color.valueOf("474747");
|
||||
|
||||
public int bubbles = 12, sides = 8;
|
||||
public float strokeMin = 0.2f, spread = 3f, timeScl = 70f;
|
||||
public float recurrence = 6f, radius = 3f;
|
||||
|
||||
public TextureRegion middle;
|
||||
public TextureRegion top;
|
||||
|
||||
@Override
|
||||
public void draw(GenericCrafterBuild build){
|
||||
Draw.rect(build.block.region, build.x, build.y);
|
||||
|
||||
Drawf.liquid(middle, build.x, build.y, build.warmup, plantColor);
|
||||
|
||||
Draw.color(bottomColor, plantColorLight, build.warmup);
|
||||
|
||||
rand.setSeed(build.pos());
|
||||
for(int i = 0; i < bubbles; i++){
|
||||
float x = rand.range(spread), y = rand.range(spread);
|
||||
float life = 1f - ((Time.time / timeScl + rand.random(recurrence)) % recurrence);
|
||||
|
||||
if(life > 0){
|
||||
Lines.stroke(build.warmup * (life + strokeMin));
|
||||
Lines.poly(build.x + x, build.y + y, sides, (1f - life) * radius);
|
||||
}
|
||||
}
|
||||
|
||||
Draw.color();
|
||||
Draw.rect(top, build.x, build.y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(Block block){
|
||||
middle = Core.atlas.find(block.name + "-middle");
|
||||
top = Core.atlas.find(block.name + "-top");
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureRegion[] icons(Block block){
|
||||
return new TextureRegion[]{block.region, top};
|
||||
}
|
||||
}
|
||||
@@ -11,10 +11,10 @@ public class DrawGlow extends DrawBlock{
|
||||
public TextureRegion top;
|
||||
|
||||
@Override
|
||||
public void draw(GenericCrafterBuild entity){
|
||||
Draw.rect(entity.block.region, entity.x, entity.y);
|
||||
Draw.alpha(Mathf.absin(entity.totalProgress, glowScale, glowAmount) * entity.warmup);
|
||||
Draw.rect(top, entity.x, entity.y);
|
||||
public void draw(GenericCrafterBuild build){
|
||||
Draw.rect(build.block.region, build.x, build.y);
|
||||
Draw.alpha(Mathf.absin(build.totalProgress, glowScale, glowAmount) * build.warmup);
|
||||
Draw.rect(top, build.x, build.y);
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
|
||||
@@ -10,19 +10,19 @@ public class DrawMixer extends DrawBlock{
|
||||
public TextureRegion liquid, top, bottom;
|
||||
|
||||
@Override
|
||||
public void draw(GenericCrafterBuild entity){
|
||||
float rotation = entity.block.rotate ? entity.rotdeg() : 0;
|
||||
public void draw(GenericCrafterBuild build){
|
||||
float rotation = build.block.rotate ? build.rotdeg() : 0;
|
||||
|
||||
Draw.rect(bottom, entity.x, entity.y, rotation);
|
||||
Draw.rect(bottom, build.x, build.y, rotation);
|
||||
|
||||
if(entity.liquids.total() > 0.001f){
|
||||
Draw.color(((GenericCrafter)entity.block).outputLiquid.liquid.color);
|
||||
Draw.alpha(entity.liquids.get(((GenericCrafter)entity.block).outputLiquid.liquid) / entity.block.liquidCapacity);
|
||||
Draw.rect(liquid, entity.x, entity.y, rotation);
|
||||
if(build.liquids.total() > 0.001f){
|
||||
Draw.color(((GenericCrafter)build.block).outputLiquid.liquid.color);
|
||||
Draw.alpha(build.liquids.get(((GenericCrafter)build.block).outputLiquid.liquid) / build.block.liquidCapacity);
|
||||
Draw.rect(liquid, build.x, build.y, rotation);
|
||||
Draw.color();
|
||||
}
|
||||
|
||||
Draw.rect(top, entity.x, entity.y, rotation);
|
||||
Draw.rect(top, build.x, build.y, rotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -9,9 +9,9 @@ public class DrawRotator extends DrawBlock{
|
||||
public TextureRegion rotator;
|
||||
|
||||
@Override
|
||||
public void draw(GenericCrafterBuild entity){
|
||||
Draw.rect(entity.block.region, entity.x, entity.y);
|
||||
Draw.rect(rotator, entity.x, entity.y, entity.totalProgress * 2f);
|
||||
public void draw(GenericCrafterBuild build){
|
||||
Draw.rect(build.block.region, build.x, build.y);
|
||||
Draw.rect(rotator, build.x, build.y, build.totalProgress * 2f);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
57
core/src/mindustry/world/draw/DrawSmelter.java
Normal file
57
core/src/mindustry/world/draw/DrawSmelter.java
Normal file
@@ -0,0 +1,57 @@
|
||||
package mindustry.world.draw;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.production.GenericCrafter.*;
|
||||
|
||||
public class DrawSmelter extends DrawBlock{
|
||||
public Color flameColor = Color.valueOf("ffc999");
|
||||
public TextureRegion top;
|
||||
public float lightRadius = 60f, lightAlpha = 0.65f, lightSinScl = 10f, lightSinMag = 5;
|
||||
public float flameRadius = 3f, flameRadiusIn = 1.9f, flameRadiusScl = 5f, flameRadiusMag = 2f, flameRadiusInMag = 1f;
|
||||
|
||||
public DrawSmelter(){
|
||||
}
|
||||
|
||||
public DrawSmelter(Color flameColor){
|
||||
this.flameColor = flameColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(Block block){
|
||||
top = Core.atlas.find(block.name + "-top");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(GenericCrafterBuild build){
|
||||
Draw.rect(build.block.region, build.x, build.y, build.block.rotate ? build.rotdeg() : 0);
|
||||
|
||||
if(build.warmup > 0f && flameColor.a > 0.001f){
|
||||
float g = 0.3f;
|
||||
float r = 0.06f;
|
||||
float cr = Mathf.random(0.1f);
|
||||
|
||||
Draw.z(Layer.block + 0.01f);
|
||||
|
||||
Draw.alpha(((1f - g) + Mathf.absin(Time.time, 8f, g) + Mathf.random(r) - r) * build.warmup);
|
||||
|
||||
Draw.tint(flameColor);
|
||||
Fill.circle(build.x, build.y, flameRadius + Mathf.absin(Time.time, flameRadiusScl, flameRadiusMag) + cr);
|
||||
Draw.color(1f, 1f, 1f, build.warmup);
|
||||
Draw.rect(top, build.x, build.y);
|
||||
Fill.circle(build.x, build.y, flameRadiusIn + Mathf.absin(Time.time, flameRadiusScl, flameRadiusInMag) + cr);
|
||||
|
||||
Draw.color();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawLight(GenericCrafterBuild build){
|
||||
Drawf.light(build.team, build.x, build.y, (lightRadius + Mathf.absin(lightSinScl, lightSinMag)) * build.warmup * build.block.size, flameColor, lightAlpha);
|
||||
}
|
||||
}
|
||||
@@ -12,22 +12,22 @@ public class DrawWeave extends DrawBlock{
|
||||
public TextureRegion weave, bottom;
|
||||
|
||||
@Override
|
||||
public void draw(GenericCrafterBuild entity){
|
||||
Draw.rect(bottom, entity.x, entity.y);
|
||||
Draw.rect(weave, entity.x, entity.y, entity.totalProgress);
|
||||
public void draw(GenericCrafterBuild build){
|
||||
Draw.rect(bottom, build.x, build.y);
|
||||
Draw.rect(weave, build.x, build.y, build.totalProgress);
|
||||
|
||||
Draw.color(Pal.accent);
|
||||
Draw.alpha(entity.warmup);
|
||||
Draw.alpha(build.warmup);
|
||||
|
||||
Lines.lineAngleCenter(
|
||||
entity.x + Mathf.sin(entity.totalProgress, 6f, Vars.tilesize / 3f * entity.block.size),
|
||||
entity.y,
|
||||
build.x + Mathf.sin(build.totalProgress, 6f, Vars.tilesize / 3f * build.block.size),
|
||||
build.y,
|
||||
90,
|
||||
entity.block.size * Vars.tilesize / 2f);
|
||||
build.block.size * Vars.tilesize / 2f);
|
||||
|
||||
Draw.reset();
|
||||
|
||||
Draw.rect(entity.block.region, entity.x, entity.y);
|
||||
Draw.rect(build.block.region, build.x, build.y);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,23 +2,47 @@ package mindustry.world.meta;
|
||||
|
||||
import mindustry.*;
|
||||
|
||||
public enum Attribute{
|
||||
/** Heat of this block. Used for calculating output of thermal generators. */
|
||||
heat,
|
||||
/** Spore content of this block. Used for increasing cultivator yield. */
|
||||
spores,
|
||||
/** Water content of this block. Used for increasing water extractor yield. */
|
||||
water,
|
||||
/** Oil content of this block. Used for increasing oil extractor yield. */
|
||||
oil,
|
||||
public class Attribute{
|
||||
public static Attribute[] all = {};
|
||||
|
||||
public static final Attribute
|
||||
/** Heat content. Used for thermal generator yield. */
|
||||
heat = add("heat"),
|
||||
/** Spore content. Used for cultivator yield. */
|
||||
spores = add("spores"),
|
||||
/** Water content. Used for water extractor yield. */
|
||||
water = add("water"),
|
||||
/** Oil content. Used for oil extractor yield. */
|
||||
oil = add("oil"),
|
||||
/** Light coverage. Negative values decrease solar panel efficiency. */
|
||||
light;
|
||||
light = add("light");
|
||||
|
||||
public static final Attribute[] all = values();
|
||||
public final int id;
|
||||
public final String name;
|
||||
|
||||
/** @return the envrionmental value for this attribute. */
|
||||
/** @return the environmental value for this attribute. */
|
||||
public float env(){
|
||||
if(Vars.state == null) return 0;
|
||||
return Vars.state.envAttrs.get(this);
|
||||
}
|
||||
|
||||
Attribute(int id, String name){
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString(){
|
||||
return name;
|
||||
}
|
||||
|
||||
/** Automatically registers this attribute for use. Do not call after mod init. */
|
||||
public static Attribute add(String name){
|
||||
Attribute a = new Attribute(all.length, name);
|
||||
Attribute[] prev = all;
|
||||
all = new Attribute[all.length + 1];
|
||||
System.arraycopy(prev, 0, all, 0, a.id);
|
||||
all[a.id] = a;
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,10 +22,13 @@ public enum BlockFlag{
|
||||
resupply,
|
||||
/** Any reactor block. */
|
||||
reactor,
|
||||
/** Any block that boosts unit capacity. */
|
||||
/** This flag is unused, and will be removed. */
|
||||
@Deprecated
|
||||
unitModifier,
|
||||
/** Blocks that extinguishes fires. */
|
||||
extinguisher;
|
||||
extinguisher,
|
||||
/** Just a launch pad. */
|
||||
launchPad;
|
||||
|
||||
public final static BlockFlag[] all = values();
|
||||
|
||||
|
||||
14
core/src/mindustry/world/meta/Env.java
Normal file
14
core/src/mindustry/world/meta/Env.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package mindustry.world.meta;
|
||||
|
||||
/** Environmental flags for different types of locations. */
|
||||
public class Env{
|
||||
public static final int
|
||||
terrestrial = 1,
|
||||
space = 1 << 1,
|
||||
underwater = 1 << 2,
|
||||
spores = 1 << 3,
|
||||
scorching = 1 << 4,
|
||||
groundOil = 1 << 5,
|
||||
groundWater = 1 << 6,
|
||||
any = 0xffffffff;
|
||||
}
|
||||
@@ -70,6 +70,7 @@ public enum Stat{
|
||||
|
||||
speedIncrease(StatCat.function),
|
||||
repairTime(StatCat.function),
|
||||
repairSpeed(StatCat.function),
|
||||
range(StatCat.function),
|
||||
shootRange(StatCat.function),
|
||||
inaccuracy(StatCat.function),
|
||||
|
||||
310
core/src/mindustry/world/meta/StatValues.java
Normal file
310
core/src/mindustry/world/meta/StatValues.java
Normal file
@@ -0,0 +1,310 @@
|
||||
package mindustry.world.meta;
|
||||
|
||||
import arc.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.entities.bullet.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.defense.turrets.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
/** Utilities for displaying certain stats in a table. */
|
||||
public class StatValues{
|
||||
|
||||
public static StatValue string(String value, Object... args){
|
||||
String result = Strings.format(value, args);
|
||||
return table -> table.add(result);
|
||||
}
|
||||
|
||||
public static StatValue bool(boolean value){
|
||||
return table -> table.add(!value ? "@no" : "@yes");
|
||||
}
|
||||
|
||||
public static StatValue number(float value, StatUnit unit){
|
||||
return table -> {
|
||||
int precision = Math.abs((int)value - value) <= 0.001f ? 0 : Math.abs((int)(value * 10) - value * 10) <= 0.001f ? 1 : 2;
|
||||
|
||||
table.add(Strings.fixed(value, precision));
|
||||
table.add((unit.space ? " " : "") + unit.localized());
|
||||
};
|
||||
}
|
||||
|
||||
public static StatValue liquid(Liquid liquid, float amount, boolean perSecond){
|
||||
return table -> table.add(new LiquidDisplay(liquid, amount, perSecond));
|
||||
}
|
||||
|
||||
public static StatValue liquids(Boolf<Liquid> filter, float amount, boolean perSecond){
|
||||
return table -> {
|
||||
Seq<Liquid> list = new Seq<>();
|
||||
|
||||
for(Liquid item : content.liquids()){
|
||||
if(!item.isHidden() && filter.get(item)) list.add(item);
|
||||
}
|
||||
|
||||
for(int i = 0; i < list.size; i++){
|
||||
table.add(new LiquidDisplay(list.get(i), amount, perSecond)).padRight(5);
|
||||
|
||||
if(i != list.size - 1){
|
||||
table.add("/");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static StatValue items(ItemStack... stacks){
|
||||
return items(true, stacks);
|
||||
}
|
||||
|
||||
public static StatValue items(boolean displayName, ItemStack... stacks){
|
||||
return table -> {
|
||||
for(ItemStack stack : stacks){
|
||||
table.add(new ItemDisplay(stack.item, stack.amount, displayName)).padRight(5);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static StatValue items(float timePeriod, ItemStack... stacks){
|
||||
return table -> {
|
||||
for(ItemStack stack : stacks){
|
||||
table.add(new ItemDisplay(stack.item, stack.amount, timePeriod, true)).padRight(5);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static StatValue items(Boolf<Item> filter){
|
||||
return items(-1, filter);
|
||||
}
|
||||
|
||||
public static StatValue items(float timePeriod, Boolf<Item> filter){
|
||||
return table -> {
|
||||
Seq<Item> list = content.items().select(filter);
|
||||
|
||||
for(int i = 0; i < list.size; i++){
|
||||
Item item = list.get(i);
|
||||
|
||||
table.add(timePeriod <= 0 ? new ItemDisplay(item) : new ItemDisplay(item, 0, timePeriod, true)).padRight(5);
|
||||
|
||||
if(i != list.size - 1){
|
||||
table.add("/");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static StatValue content(UnlockableContent content){
|
||||
return table -> {
|
||||
table.add(new Image(content.uiIcon)).size(iconSmall).padRight(3);
|
||||
table.add(content.localizedName).padRight(3);
|
||||
};
|
||||
}
|
||||
|
||||
public static StatValue floorEfficiency(Floor floor, float multiplier, boolean startZero){
|
||||
return table -> table.stack(
|
||||
new Image(floor.uiIcon).setScaling(Scaling.fit),
|
||||
new Table(t -> t.top().right().add((multiplier < 0 ? "[scarlet]" : startZero ? "[accent]" : "[accent]+") + (int)((multiplier) * 100) + "%").style(Styles.outlineLabel))
|
||||
);
|
||||
}
|
||||
|
||||
public static StatValue blocks(Boolf<Block> pred){
|
||||
return blocks(content.blocks().select(pred));
|
||||
}
|
||||
|
||||
public static StatValue blocks(Seq<Block> list){
|
||||
return table -> table.table(l -> {
|
||||
l.left();
|
||||
|
||||
for(int i = 0; i < list.size; i++){
|
||||
Block item = list.get(i);
|
||||
|
||||
l.image(item.uiIcon).size(iconSmall).padRight(2).padLeft(2).padTop(3).padBottom(3);
|
||||
l.add(item.localizedName).left().padLeft(1).padRight(4);
|
||||
if(i % 5 == 4){
|
||||
l.row();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static StatValue boosters(float reload, float maxUsed, float multiplier, boolean baseReload, Boolf<Liquid> filter){
|
||||
return table -> {
|
||||
table.row();
|
||||
table.table(c -> {
|
||||
for(Liquid liquid : content.liquids()){
|
||||
if(!filter.get(liquid)) continue;
|
||||
|
||||
c.image(liquid.uiIcon).size(3 * 8).padRight(4).right().top();
|
||||
c.add(liquid.localizedName).padRight(10).left().top();
|
||||
c.table(Tex.underline, bt -> {
|
||||
bt.left().defaults().padRight(3).left();
|
||||
|
||||
float reloadRate = (baseReload ? 1f : 0f) + maxUsed * multiplier * liquid.heatCapacity;
|
||||
float standardReload = baseReload ? reload : reload / (maxUsed * multiplier * 0.4f);
|
||||
float result = standardReload / (reload / reloadRate);
|
||||
bt.add(Core.bundle.format("bullet.reload", Strings.autoFixed(result, 2)));
|
||||
}).left().padTop(-9);
|
||||
c.row();
|
||||
}
|
||||
}).colspan(table.getColumns());
|
||||
table.row();
|
||||
};
|
||||
}
|
||||
|
||||
public static StatValue strengthBoosters(float multiplier, Boolf<Liquid> filter){
|
||||
return table -> {
|
||||
table.row();
|
||||
table.table(c -> {
|
||||
for(Liquid liquid : content.liquids()){
|
||||
if(!filter.get(liquid)) continue;
|
||||
|
||||
c.image(liquid.uiIcon).size(3 * 8).padRight(4).right().top();
|
||||
c.add(liquid.localizedName).padRight(10).left().top();
|
||||
c.table(Tex.underline, bt -> {
|
||||
bt.left().defaults().padRight(3).left();
|
||||
|
||||
float newRate = (1f + multiplier * liquid.heatCapacity);
|
||||
bt.add(Core.bundle.format("bar.strength", Strings.autoFixed(newRate, 2)));
|
||||
}).left().padTop(-9);
|
||||
c.row();
|
||||
}
|
||||
}).colspan(table.getColumns());
|
||||
table.row();
|
||||
};
|
||||
}
|
||||
|
||||
public static StatValue weapons(UnitType unit, Seq<Weapon> weapons){
|
||||
return table -> {
|
||||
table.row();
|
||||
for(int i = 0; i < weapons.size;i ++){
|
||||
Weapon weapon = weapons.get(i);
|
||||
|
||||
if(weapon.flipSprite){
|
||||
//flipped weapons are not given stats
|
||||
continue;
|
||||
}
|
||||
|
||||
TextureRegion region = !weapon.name.equals("") && weapon.outlineRegion.found() ? weapon.outlineRegion : unit.fullIcon;
|
||||
|
||||
table.image(region).size(60).scaling(Scaling.bounded).right().top();
|
||||
|
||||
table.table(Tex.underline, w -> {
|
||||
w.left().defaults().padRight(3).left();
|
||||
|
||||
if(weapon.inaccuracy > 0){
|
||||
sep(w, "[lightgray]" + Stat.inaccuracy.localized() + ": [white]" + (int)weapon.inaccuracy + " " + StatUnit.degrees.localized());
|
||||
}
|
||||
sep(w, "[lightgray]" + Stat.reload.localized() + ": " + (weapon.mirror ? "2x " : "") + "[white]" + Strings.autoFixed(60f / weapon.reload * weapon.shots, 2));
|
||||
|
||||
ammo(ObjectMap.of(unit, weapon.bullet)).display(w);
|
||||
}).padTop(-9).left();
|
||||
table.row();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static <T extends UnlockableContent> StatValue ammo(ObjectMap<T, BulletType> map){
|
||||
return table -> {
|
||||
|
||||
table.row();
|
||||
|
||||
var orderedKeys = map.keys().toSeq();
|
||||
orderedKeys.sort();
|
||||
|
||||
for(T t : orderedKeys){
|
||||
boolean unit = t instanceof UnitType;
|
||||
|
||||
BulletType type = map.get(t);
|
||||
|
||||
//no point in displaying unit icon twice
|
||||
if(!unit & !(t instanceof PowerTurret)){
|
||||
table.image(icon(t)).size(3 * 8).padRight(4).right().top();
|
||||
table.add(t.localizedName).padRight(10).left().top();
|
||||
}
|
||||
|
||||
table.table(bt -> {
|
||||
bt.left().defaults().padRight(3).left();
|
||||
|
||||
if(type.damage > 0 && (type.collides || type.splashDamage <= 0)){
|
||||
if(type.continuousDamage() > 0){
|
||||
bt.add(Core.bundle.format("bullet.damage", type.continuousDamage()) + StatUnit.perSecond.localized());
|
||||
}else{
|
||||
bt.add(Core.bundle.format("bullet.damage", type.damage));
|
||||
}
|
||||
}
|
||||
|
||||
if(type.buildingDamageMultiplier != 1){
|
||||
sep(bt, Core.bundle.format("bullet.buildingdamage", (int)(type.buildingDamageMultiplier * 100)));
|
||||
}
|
||||
|
||||
if(type.splashDamage > 0){
|
||||
sep(bt, Core.bundle.format("bullet.splashdamage", (int)type.splashDamage, Strings.fixed(type.splashDamageRadius / tilesize, 1)));
|
||||
}
|
||||
|
||||
if(!unit && !Mathf.equal(type.ammoMultiplier, 1f)){
|
||||
sep(bt, Core.bundle.format("bullet.multiplier", (int)type.ammoMultiplier));
|
||||
}
|
||||
|
||||
if(!Mathf.equal(type.reloadMultiplier, 1f)){
|
||||
sep(bt, Core.bundle.format("bullet.reload", Strings.autoFixed(type.reloadMultiplier, 2)));
|
||||
}
|
||||
|
||||
if(type.knockback > 0){
|
||||
sep(bt, Core.bundle.format("bullet.knockback", Strings.autoFixed(type.knockback, 2)));
|
||||
}
|
||||
|
||||
if(type.healPercent > 0f){
|
||||
sep(bt, Core.bundle.format("bullet.healpercent", (int)type.healPercent));
|
||||
}
|
||||
|
||||
if(type.pierce || type.pierceCap != -1){
|
||||
sep(bt, type.pierceCap == -1 ? "@bullet.infinitepierce" : Core.bundle.format("bullet.pierce", type.pierceCap));
|
||||
}
|
||||
|
||||
if(type.incendAmount > 0){
|
||||
sep(bt, "@bullet.incendiary");
|
||||
}
|
||||
|
||||
if(type.status != StatusEffects.none){
|
||||
sep(bt, (type.minfo.mod == null ? type.status.emoji() : "") + "[stat]" + type.status.localizedName);
|
||||
}
|
||||
|
||||
if(type.homingPower > 0.01f){
|
||||
sep(bt, "@bullet.homing");
|
||||
}
|
||||
|
||||
if(type.lightning > 0){
|
||||
sep(bt, Core.bundle.format("bullet.lightning", type.lightning, type.lightningDamage < 0 ? type.damage : type.lightningDamage));
|
||||
}
|
||||
|
||||
if(type.fragBullet != null){
|
||||
sep(bt, "@bullet.frag");
|
||||
}
|
||||
}).padTop(unit ? 0 : -9).left().get().background(unit ? null : Tex.underline);
|
||||
|
||||
table.row();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//for AmmoListValue
|
||||
|
||||
private static void sep(Table table, String text){
|
||||
table.row();
|
||||
table.add(text);
|
||||
}
|
||||
|
||||
private static TextureRegion icon(UnlockableContent t){
|
||||
return t.uiIcon;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user