Merge branch 'master' of https://github.com/Anuken/Mindustry into tileable-logic-displays
# Conflicts: # core/assets/icons/icons.properties # core/assets/logicids.dat
This commit is contained in:
@@ -124,6 +124,8 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
public boolean saveData;
|
||||
/** whether you can break this with rightclick */
|
||||
public boolean breakable;
|
||||
/** if true, this block will be broken by certain units stepping/moving over it */
|
||||
public boolean unitMoveBreakable;
|
||||
/** whether to add this block to brokenblocks */
|
||||
public boolean rebuildable = true;
|
||||
/** if true, this logic-related block can only be used with privileged processors (or is one itself) */
|
||||
@@ -154,6 +156,8 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
public boolean updateInUnits = true;
|
||||
/** if true, this block updates in payloads in units regardless of the experimental game rule */
|
||||
public boolean alwaysUpdateInUnits = false;
|
||||
/** if true, this block can be picked up in payloads */
|
||||
public boolean canPickup = true;
|
||||
/** if false, only incinerable liquids are dropped when deconstructing; otherwise, all liquids are dropped. */
|
||||
public boolean deconstructDropAllLiquid = false;
|
||||
/** Whether to use this block's color in the minimap. Only used for overlays. */
|
||||
@@ -172,6 +176,8 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
public float armor = 0f;
|
||||
/** base block explosiveness */
|
||||
public float baseExplosiveness = 0f;
|
||||
/** base value for screen shake upon destruction */
|
||||
public float baseShake = 3f;
|
||||
/** bullet that this block spawns when destroyed */
|
||||
public @Nullable BulletType destroyBullet = null;
|
||||
/** if true, destroyBullet is spawned on the block's team instead of Derelict team */
|
||||
@@ -192,6 +198,8 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
public int sizeOffset = 0;
|
||||
/** Clipping size of this block. Should be as large as the block will draw. */
|
||||
public float clipSize = -1f;
|
||||
/** Clipping size for lights only. */
|
||||
public float lightClipSize;
|
||||
/** When placeRangeCheck is enabled, this is the range checked for enemy blocks. */
|
||||
public float placeOverlapRange = 50f;
|
||||
/** Multiplier of damage dealt to this block by tanks. Does not apply to crawlers. */
|
||||
@@ -288,13 +296,13 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
public Sound breakSound = Sounds.breaks;
|
||||
/** Sounds made when this block is destroyed.*/
|
||||
public Sound destroySound = Sounds.boom;
|
||||
/** Range of destroy sound. */
|
||||
public float destroyPitchMin = 1f, destroyPitchMax = 1f;
|
||||
/** How reflective this block is. */
|
||||
public float albedo = 0f;
|
||||
/** Environmental passive light color. */
|
||||
public Color lightColor = Color.white.cpy();
|
||||
/**
|
||||
* Whether this environmental block passively emits light.
|
||||
* Does not change behavior for non-environmental blocks, but still updates clipSize. */
|
||||
/** If true, drawLight() will be called for this block. */
|
||||
public boolean emitLight = false;
|
||||
/** Radius of the light emitted by this block. */
|
||||
public float lightRadius = 60f;
|
||||
@@ -302,11 +310,6 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
/** How much fog this block uncovers, in tiles. Cannot be dynamic. <= 0 to disable. */
|
||||
public int fogRadius = -1;
|
||||
|
||||
/** The sound that this block makes while active. One sound loop. Do not overuse. */
|
||||
public Sound loopSound = Sounds.none;
|
||||
/** Active sound base volume. */
|
||||
public float loopSoundVolume = 0.5f;
|
||||
|
||||
/** The sound that this block makes while idle. Uses one sound loop for all blocks. */
|
||||
public Sound ambientSound = Sounds.none;
|
||||
/** Idle sound base volume. */
|
||||
@@ -316,8 +319,8 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
public ItemStack[] requirements = {};
|
||||
/** Category in place menu. */
|
||||
public Category category = Category.distribution;
|
||||
/** Time to build this block in ticks; do not modify directly! */
|
||||
public float buildCost = 20f;
|
||||
/** Time to build this block in ticks. If this value is <0, it is calculated dynamically. */
|
||||
public float buildTime = -1f;
|
||||
/** Whether this block is visible and can currently be built. */
|
||||
public BuildVisibility buildVisibility = BuildVisibility.hidden;
|
||||
/** Multiplier for speed of building this block. */
|
||||
@@ -342,6 +345,8 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
public ObjectFloatMap<Item> researchCostMultipliers = new ObjectFloatMap<>();
|
||||
/** Override for research cost. Uses multipliers above and building requirements if not set. */
|
||||
public @Nullable ItemStack[] researchCost;
|
||||
/** If set, all blocks will be forced to be this team. */
|
||||
public @Nullable Team forceTeam;
|
||||
/** Whether this block has instant transfer.*/
|
||||
public boolean instantTransfer = false;
|
||||
/** Whether you can rotate this block after it is placed. */
|
||||
@@ -432,6 +437,18 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
drawOverlay(x * tilesize + offset, y * tilesize + offset, rotation);
|
||||
}
|
||||
|
||||
/** Draws a region to overlay a specific side of this block. This method makes sure it is placed at the edge of the side. */
|
||||
public void drawSideRegion(TextureRegion region, float x, float y, int rotation){
|
||||
var p = Geometry.d4[Mathf.mod(rotation, 4)];
|
||||
float s = size * tilesize/2f;
|
||||
|
||||
Draw.rect(region,
|
||||
x + p.x * (s - region.width/2f * region.scl()),
|
||||
y + p.y * (s - region.width/2f * region.scl()),
|
||||
rotation * 90f
|
||||
);
|
||||
}
|
||||
|
||||
public void drawPotentialLinks(int x, int y){
|
||||
if((consumesPower || outputsPower) && hasPower && connectedPower){
|
||||
Tile tile = world.tile(x, y);
|
||||
@@ -490,6 +507,10 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
public void drawOverlay(float x, float y, int rotation){
|
||||
}
|
||||
|
||||
public boolean displayShadow(Tile tile){
|
||||
return hasShadow;
|
||||
}
|
||||
|
||||
public float sumAttribute(@Nullable Attribute attr, int x, int y){
|
||||
if(attr == null) return 0;
|
||||
Tile tile = world.tile(x, y);
|
||||
@@ -555,7 +576,7 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
}
|
||||
|
||||
if(canBeBuilt() && requirements.length > 0){
|
||||
stats.add(Stat.buildTime, buildCost / 60, StatUnit.seconds);
|
||||
stats.add(Stat.buildTime, buildTime / 60, StatUnit.seconds);
|
||||
stats.add(Stat.buildCost, StatValues.items(false, requirements));
|
||||
}
|
||||
|
||||
@@ -885,8 +906,8 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
return buildType.get();
|
||||
}
|
||||
|
||||
public void updateClipRadius(float size){
|
||||
clipSize = Math.max(clipSize, size * tilesize + size * 2f);
|
||||
public void updateClipRadius(float radiusInWorldUnits){
|
||||
clipSize = Math.max(clipSize, this.size * tilesize + radiusInWorldUnits * 2f);
|
||||
}
|
||||
|
||||
public Rect bounds(int x, int y, Rect rect){
|
||||
@@ -1194,6 +1215,10 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
hasShadow = false;
|
||||
}
|
||||
|
||||
if(underBullets){
|
||||
priority = TargetPriority.under;
|
||||
}
|
||||
|
||||
if(fogRadius > 0){
|
||||
flags = flags.with(BlockFlag.hasFogRadius);
|
||||
}
|
||||
@@ -1220,12 +1245,15 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
|
||||
clipSize = Math.max(clipSize, size * tilesize);
|
||||
|
||||
lightClipSize = Math.max(lightClipSize, clipSize);
|
||||
|
||||
if(hasLiquids && drawLiquidLight){
|
||||
clipSize = Math.max(size * 30f * 2f, clipSize);
|
||||
emitLight = true;
|
||||
lightClipSize = Math.max(lightClipSize, size * 30f * 2f);
|
||||
}
|
||||
|
||||
if(emitLight){
|
||||
clipSize = Math.max(clipSize, lightRadius * 2f);
|
||||
lightClipSize = Math.max(lightClipSize, lightRadius * 2f);
|
||||
}
|
||||
|
||||
if(group == BlockGroup.transportation || category == Category.distribution){
|
||||
@@ -1235,14 +1263,18 @@ public class Block extends UnlockableContent implements Senseable{
|
||||
offset = ((size + 1) % 2) * tilesize / 2f;
|
||||
sizeOffset = -((size - 1) / 2);
|
||||
|
||||
if(requirements.length > 0){
|
||||
buildCost = 0f;
|
||||
if(requirements.length > 0 && buildTime < 0){
|
||||
buildTime = 0f;
|
||||
for(ItemStack stack : requirements){
|
||||
buildCost += stack.amount * stack.item.cost;
|
||||
buildTime += stack.amount * stack.item.cost;
|
||||
}
|
||||
}
|
||||
|
||||
buildCost *= buildCostMultiplier;
|
||||
if(buildTime < 0){
|
||||
buildTime = 20f;
|
||||
}
|
||||
|
||||
buildTime *= buildCostMultiplier;
|
||||
|
||||
consumers = consumeBuilder.toArray(Consume.class);
|
||||
optionalConsumers = consumeBuilder.select(consume -> consume.optional && !consume.ignore()).toArray(Consume.class);
|
||||
|
||||
@@ -164,7 +164,12 @@ public class Build{
|
||||
|
||||
/** @return whether a tile can be placed at this location by this team. */
|
||||
public static boolean validPlace(Block type, Team team, int x, int y, int rotation, boolean checkVisible){
|
||||
return validPlaceIgnoreUnits(type, team, x, y, rotation, checkVisible) && checkNoUnitOverlap(type, x, y);
|
||||
return validPlace(type, team, x, y, rotation, checkVisible, true);
|
||||
}
|
||||
|
||||
/** @return whether a tile can be placed at this location by this team. */
|
||||
public static boolean validPlace(Block type, Team team, int x, int y, int rotation, boolean checkVisible, boolean ignoreCoreRadius){
|
||||
return validPlaceIgnoreUnits(type, team, x, y, rotation, checkVisible, ignoreCoreRadius) && checkNoUnitOverlap(type, x, y);
|
||||
}
|
||||
|
||||
/** @return whether a tile can be placed at this location by this team. */
|
||||
@@ -172,14 +177,14 @@ public class Build{
|
||||
return (!type.solid && !type.solidifes) || !Units.anyEntities(x * tilesize + type.offset - type.size * tilesize / 2f, y * tilesize + type.offset - type.size * tilesize / 2f, type.size * tilesize, type.size * tilesize);
|
||||
}
|
||||
|
||||
/** Returns whether a tile can be placed at this location by this team. Ignores units at this location. */
|
||||
public static boolean validPlaceIgnoreUnits(Block type, Team team, int x, int y, int rotation, boolean checkVisible){
|
||||
/** @return whether a tile can be placed at this location by this team. Ignores units at this location. */
|
||||
public static boolean validPlaceIgnoreUnits(Block type, Team team, int x, int y, int rotation, boolean checkVisible, boolean checkCoreRadius){
|
||||
//the wave team can build whatever they want as long as it's visible - banned blocks are not applicable
|
||||
if(type == null || (!state.rules.editor && (checkVisible && (!type.environmentBuildable() || (!type.isPlaceable() && !(state.rules.waves && team == state.rules.waveTeam && type.isVisible())))))){
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!state.rules.editor){
|
||||
if(!state.rules.editor && checkCoreRadius){
|
||||
//find closest core, if it doesn't match the team, placing is not legal
|
||||
if(state.rules.polygonCoreProtection){
|
||||
float mindst = Float.MAX_VALUE;
|
||||
@@ -196,7 +201,7 @@ public class Build{
|
||||
if(closest != null && closest.team != team){
|
||||
return false;
|
||||
}
|
||||
}else if(state.teams.anyEnemyCoresWithin(team, x * tilesize + type.offset, y * tilesize + type.offset, state.rules.enemyCoreBuildRadius + tilesize)){
|
||||
}else if(state.teams.anyEnemyCoresWithinBuildRadius(team, x * tilesize + type.offset, y * tilesize + type.offset)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -240,8 +245,9 @@ public class Build{
|
||||
(type == check.block() && check.build != null && rotation == check.build.rotation && type.rotate && !((type == check.block && team != Team.derelict && check.team() == Team.derelict))) || //same block, same rotation
|
||||
!check.interactable(team) || //cannot interact
|
||||
!check.floor().placeableOn && !type.ignoreBuildDarkness || //solid floor
|
||||
(!checkVisible && !check.block().alwaysReplace) || //replacing a block that should be replaced (e.g. payload placement)
|
||||
!(((type.canReplace(check.block()) || (type == check.block && team != Team.derelict && state.rules.derelictRepair && check.team() == Team.derelict)) || //can replace type OR can replace derelict block of same type
|
||||
//when you have a payload, you cannot place blocks on things, even if normal placement rules allow it. this is a hack that assumes checkVisible = true means it's coming from a payload
|
||||
(!checkVisible && checkCoreRadius && !check.block().alwaysReplace) || //replacing a block that should be replaced (e.g. payload placement)
|
||||
!(((type.canReplace(check.block()) || (check.build != null && check.build.canBeReplaced(type)) || (type == check.block && team != Team.derelict && state.rules.derelictRepair && check.team() == Team.derelict)) || //can replace type OR can replace derelict block of same type
|
||||
(check.build instanceof ConstructBuild build && build.current == type && check.centerX() == tile.x && check.centerY() == tile.y)) && //same type in construction
|
||||
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
|
||||
@@ -249,7 +255,7 @@ public class Build{
|
||||
}
|
||||
}
|
||||
|
||||
if(state.rules.placeRangeCheck && !state.isEditor() && getEnemyOverlap(type, team, x, y) != null){
|
||||
if(state.rules.placeRangeCheck && checkCoreRadius && !state.isEditor() && getEnemyOverlap(type, team, x, y) != null){
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,11 +28,11 @@ public class CachedTile extends Tile{
|
||||
|
||||
if(block.hasBuilding()){
|
||||
Building n = entityprov.get();
|
||||
n.tile(this);
|
||||
n.tile = this;
|
||||
n.block = block;
|
||||
if(block.hasItems) n.items = new ItemModule();
|
||||
if(block.hasLiquids) n.liquids(new LiquidModule());
|
||||
if(block.hasPower) n.power(new PowerModule());
|
||||
if(block.hasLiquids) n.liquids = new LiquidModule();
|
||||
if(block.hasPower) n.power = new PowerModule();
|
||||
build = n;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ public class DirectionalItemBuffer{
|
||||
|
||||
public void accept(int buffer, Item item){
|
||||
if(!accepts(buffer)) return;
|
||||
buffers[buffer][indexes[buffer]++] = BufferItem.get((byte)item.id, Time.time);
|
||||
buffers[buffer][indexes[buffer]++] = BufferItem.get(item.id, Time.time);
|
||||
}
|
||||
|
||||
public Item poll(int buffer, float speed){
|
||||
@@ -54,11 +54,21 @@ public class DirectionalItemBuffer{
|
||||
}
|
||||
|
||||
public void read(Reads read){
|
||||
read(read, false);
|
||||
}
|
||||
|
||||
public void read(Reads read, boolean legacy){
|
||||
for(int i = 0; i < 4; i++){
|
||||
indexes[i] = read.b();
|
||||
byte length = read.b();
|
||||
for(int j = 0; j < length; j++){
|
||||
long value = read.l();
|
||||
|
||||
if(legacy){
|
||||
//read value as the old format with 1-byte items, and create a new one with the new 2-byte format
|
||||
value = BufferItem.get(BufferItemLegacy.item(value), BufferItemLegacy.time(value));
|
||||
}
|
||||
|
||||
if(j < buffers[i].length){
|
||||
buffers[i][j] = value;
|
||||
}
|
||||
@@ -68,6 +78,12 @@ public class DirectionalItemBuffer{
|
||||
|
||||
@Struct
|
||||
class BufferItemStruct{
|
||||
short item;
|
||||
float time;
|
||||
}
|
||||
|
||||
@Struct
|
||||
class BufferItemLegacyStruct{
|
||||
byte item;
|
||||
float time;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ public class Edges{
|
||||
}
|
||||
|
||||
public static Tile getFacingEdge(Building tile, Building other){
|
||||
Tile res = getFacingEdge(tile.block, tile.tileX(), tile.tileY(), other.tile());
|
||||
Tile res = getFacingEdge(tile.block, tile.tileX(), tile.tileY(), other.tile);
|
||||
return res == null ? tile.tile : res;
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import static mindustry.Vars.*;
|
||||
public class Tile implements Position, QuadTreeObject, Displayable{
|
||||
private static final TileChangeEvent tileChange = new TileChangeEvent();
|
||||
private static final TilePreChangeEvent preChange = new TilePreChangeEvent();
|
||||
private static final TileFloorChangeEvent floorChange = new TileFloorChangeEvent();
|
||||
private static final ObjectSet<Building> tileSet = new ObjectSet<>();
|
||||
|
||||
/** Extra data for very specific blocks. */
|
||||
@@ -223,6 +224,8 @@ public class Tile implements Position, QuadTreeObject, Displayable{
|
||||
recacheWall();
|
||||
}
|
||||
|
||||
if(type.forceTeam != null) team = type.forceTeam;
|
||||
|
||||
preChanged();
|
||||
|
||||
this.block = type;
|
||||
@@ -282,6 +285,7 @@ public class Tile implements Position, QuadTreeObject, Displayable{
|
||||
|
||||
/** This resets the overlay! */
|
||||
public void setFloor(Floor type){
|
||||
var prev = this.floor;
|
||||
this.floor = type;
|
||||
this.overlay = (Floor)Blocks.air;
|
||||
|
||||
@@ -293,9 +297,13 @@ public class Tile implements Position, QuadTreeObject, Displayable{
|
||||
if(build != null){
|
||||
build.onProximityUpdate();
|
||||
}
|
||||
if(!world.isGenerating() && pathfinder != null){
|
||||
if(!world.isGenerating() && pathfinder != null && !state.isEditor()){
|
||||
pathfinder.updateTile(this);
|
||||
}
|
||||
|
||||
if(!world.isGenerating() && prev != type){
|
||||
Events.fire(floorChange.set(this, prev, type));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEditorTile(){
|
||||
|
||||
@@ -40,7 +40,6 @@ public class Tiles implements Iterable<Tile>{
|
||||
fires[pos] = f;
|
||||
}
|
||||
|
||||
|
||||
public void each(Intc2 cons){
|
||||
for(int x = 0; x < width; x++){
|
||||
for(int y = 0; y < height; y++){
|
||||
|
||||
@@ -193,8 +193,8 @@ public class ConstructBlock extends Block{
|
||||
@Override
|
||||
public void tapped(){
|
||||
//if the target is constructable, begin constructing
|
||||
if(current.isPlaceable()){
|
||||
if(control.input.buildWasAutoPaused && !control.input.isBuilding && player.isBuilder()){
|
||||
if(current.isPlaceable() && player.isBuilder()){
|
||||
if(control.input.buildWasAutoPaused && !control.input.isBuilding){
|
||||
control.input.isBuilding = true;
|
||||
}
|
||||
player.unit().addBuild(new BuildPlan(tile.x, tile.y, rotation, current, lastConfig), false);
|
||||
@@ -269,6 +269,8 @@ public class ConstructBlock extends Block{
|
||||
setConstruct(previous, current);
|
||||
}
|
||||
|
||||
boolean infinite = team.rules().infiniteResources || state.rules.infiniteResources;
|
||||
|
||||
float maxProgress = core == null || team.rules().infiniteResources ? amount : checkRequired(core.items, amount, false);
|
||||
|
||||
for(int i = 0; i < current.requirements.length; i++){
|
||||
@@ -285,7 +287,7 @@ public class ConstructBlock extends Block{
|
||||
boolean canFinish = true;
|
||||
|
||||
//look at leftover resources to consume, get them from the core if necessary, delay building if not
|
||||
if(!state.rules.infiniteResources){
|
||||
if(!infinite){
|
||||
for(int i = 0; i < itemsLeft.length; i++){
|
||||
if(itemsLeft[i] > 0){
|
||||
if(core != null && core.items.has(current.requirements[i].item, itemsLeft[i])){
|
||||
@@ -422,7 +424,7 @@ public class ConstructBlock extends Block{
|
||||
this.wasConstructing = true;
|
||||
this.current = block;
|
||||
this.previous = previous;
|
||||
this.buildCost = block.buildCost * state.rules.buildCostMultiplier;
|
||||
this.buildCost = block.buildTime * state.rules.buildCostMultiplier;
|
||||
this.itemsLeft = new int[block.requirements.length];
|
||||
this.accumulator = new float[block.requirements.length];
|
||||
this.totalAccumulator = new float[block.requirements.length];
|
||||
@@ -442,7 +444,7 @@ public class ConstructBlock extends Block{
|
||||
this.previous = previous;
|
||||
this.progress = 1f;
|
||||
this.current = previous;
|
||||
this.buildCost = previous.buildCost * state.rules.buildCostMultiplier;
|
||||
this.buildCost = previous.buildTime * state.rules.buildCostMultiplier;
|
||||
this.itemsLeft = new int[previous.requirements.length];
|
||||
this.accumulator = new float[previous.requirements.length];
|
||||
this.totalAccumulator = new float[previous.requirements.length];
|
||||
@@ -500,7 +502,7 @@ public class ConstructBlock extends Block{
|
||||
if(previous == null) previous = Blocks.air;
|
||||
if(current == null) current = Blocks.air;
|
||||
|
||||
buildCost = current.buildCost * state.rules.buildCostMultiplier;
|
||||
buildCost = current.buildTime * state.rules.buildCostMultiplier;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
29
core/src/mindustry/world/blocks/LaunchAnimator.java
Normal file
29
core/src/mindustry/world/blocks/LaunchAnimator.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package mindustry.world.blocks;
|
||||
|
||||
import arc.audio.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
public interface LaunchAnimator{
|
||||
|
||||
void drawLaunch();
|
||||
|
||||
default void drawLaunchGlobalZ(){}
|
||||
|
||||
void beginLaunch(boolean launching);
|
||||
|
||||
void endLaunch();
|
||||
|
||||
void updateLaunch();
|
||||
|
||||
float launchDuration();
|
||||
|
||||
default Music landMusic(){
|
||||
return Musics.land;
|
||||
}
|
||||
|
||||
default Music launchMusic(){
|
||||
return Musics.launch;
|
||||
}
|
||||
|
||||
float zoomLaunch();
|
||||
}
|
||||
@@ -3,61 +3,141 @@ 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.scene.actions.*;
|
||||
import arc.scene.event.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
import mindustry.world.blocks.storage.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class Accelerator extends Block{
|
||||
public @Load("launch-arrow") TextureRegion arrowRegion;
|
||||
public @Load(value = "@-launch-arrow", fallback = "launch-arrow") TextureRegion arrowRegion;
|
||||
public @Load("select-arrow-small") TextureRegion selectArrowRegion;
|
||||
|
||||
//TODO dynamic
|
||||
public Block launching = Blocks.coreNucleus;
|
||||
public int[] capacities = {};
|
||||
/** Core block that is launched. Should match the starting core of the planet being launched to. */
|
||||
public Block launchBlock = Blocks.coreNucleus;
|
||||
public float powerBufferRequirement;
|
||||
/** Override for planets that this block can launch to. If null, the planet's launch candidates are used. */
|
||||
public @Nullable Seq<Planet> launchCandidates;
|
||||
|
||||
//TODO: launching needs audio!
|
||||
|
||||
public Music launchMusic = Musics.coreLaunch;
|
||||
public float launchDuration = 120f;
|
||||
public float chargeDuration = 220f;
|
||||
public float buildDuration = 120f;
|
||||
public Interp landZoomInterp = Interp.pow4In, chargeZoomInterp = Interp.pow4In;
|
||||
public float landZoomFrom = 0.02f, landZoomTo = 4f, chargeZoomTo = 5f;
|
||||
|
||||
public int chargeRings = 4;
|
||||
public float ringRadBase = 60f, ringRadSpacing = 25f, ringRadPow = 1.6f, ringStroke = 3f, ringSpeedup = 1.4f, chargeRingMerge = 2f, ringArrowRad = 3f;
|
||||
public float ringHandleTilt = 0.8f, ringHandleLen = 30f;
|
||||
public Color ringColor = Pal.accent;
|
||||
|
||||
public int launchLightning = 20;
|
||||
public Color lightningColor = Pal.accent;
|
||||
public float lightningDamage = 40;
|
||||
public float lightningOffset = 24f;
|
||||
public int lightningLengthMin = 5, lightningLengthMax = 25;
|
||||
public double lightningLaunchChance = 0.8;
|
||||
|
||||
protected int[] capacities = {};
|
||||
|
||||
public Accelerator(String name){
|
||||
super(name);
|
||||
update = true;
|
||||
solid = true;
|
||||
hasItems = true;
|
||||
hasPower = true;
|
||||
itemCapacity = 8000;
|
||||
configurable = true;
|
||||
emitLight = true;
|
||||
lightRadius = 70f;
|
||||
lightColor = Pal.accent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
itemCapacity = 0;
|
||||
capacities = new int[content.items().size];
|
||||
for(ItemStack stack : launching.requirements){
|
||||
for(ItemStack stack : launchBlock.requirements){
|
||||
capacities[stack.item.id] = stack.amount;
|
||||
itemCapacity += stack.amount;
|
||||
}
|
||||
consumeItems(launching.requirements);
|
||||
consumeItems(launchBlock.requirements);
|
||||
super.init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBars(){
|
||||
super.setBars();
|
||||
|
||||
if(powerBufferRequirement > 0f){
|
||||
addBar("powerBufferRequirement", b -> new Bar(
|
||||
() -> Core.bundle.format("bar.powerbuffer",UI.formatAmount((long)b.power.graph.getBatteryStored()), UI.formatAmount((long)powerBufferRequirement)),
|
||||
() -> Pal.powerBar,
|
||||
() -> b.power.graph.getBatteryStored() / powerBufferRequirement
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean outputsItems(){
|
||||
return false;
|
||||
}
|
||||
|
||||
public class AcceleratorBuild extends Building{
|
||||
public class AcceleratorBuild extends Building implements LaunchAnimator{
|
||||
public float heat, statusLerp;
|
||||
public float progress;
|
||||
public float time, launchHeat;
|
||||
public boolean launching;
|
||||
public float launchTime;
|
||||
|
||||
protected float cloudSeed;
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
super.updateTile();
|
||||
heat = Mathf.lerpDelta(heat, efficiency, 0.05f);
|
||||
heat = Mathf.lerpDelta(heat, launching ? 1f : efficiency, 0.05f);
|
||||
statusLerp = Mathf.lerpDelta(statusLerp, power.status, 0.05f);
|
||||
|
||||
if(!launching){
|
||||
time += Time.delta * efficiency;
|
||||
}else{
|
||||
time = Mathf.slerpDelta(time, 0f, 0.4f);
|
||||
}
|
||||
|
||||
launchHeat = Mathf.lerpDelta(launchHeat, launching ? 1f : 0f, 0.1f);
|
||||
|
||||
if(efficiency >= 0f){
|
||||
progress += Time.delta * efficiency / buildDuration;
|
||||
progress = Math.min(progress, 1f);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float progress(){
|
||||
return progress;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -74,53 +154,117 @@ public class Accelerator extends Block{
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
if(launching){
|
||||
Draw.reset();
|
||||
|
||||
Draw.blend(Blending.additive);
|
||||
Fill.light(x, y, 15, launchBlock.size * tilesize * 1f, Tmp.c2.set(Pal.accent).a(launchTime / chargeDuration), Tmp.c1.set(Pal.accent).a(0f));
|
||||
Draw.blend();
|
||||
|
||||
Draw.rect(launchBlock.fullIcon, x, y);
|
||||
|
||||
Draw.z(Layer.bullet);
|
||||
Draw.mixcol(Pal.accent, Mathf.clamp(launchTime / chargeDuration));
|
||||
Draw.color(1f, 1f, 1f, Interp.pow2In.apply(Mathf.clamp(launchTime / chargeDuration * 0.7f)));
|
||||
Draw.rect(launchBlock.fullIcon, x, y);
|
||||
Draw.reset();
|
||||
}else{
|
||||
Drawf.shadow(x, y, launchBlock.size * tilesize * 2f, progress);
|
||||
Draw.draw(Layer.blockBuilding, () -> {
|
||||
Draw.color(Pal.accent, heat);
|
||||
|
||||
for(TextureRegion region : launchBlock.getGeneratedIcons()){
|
||||
Shaders.blockbuild.region = region;
|
||||
Shaders.blockbuild.time = time;
|
||||
Shaders.blockbuild.progress = progress;
|
||||
|
||||
Draw.rect(region, x, y);
|
||||
Draw.flush();
|
||||
}
|
||||
|
||||
Draw.color();
|
||||
});
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
if(heat < 0.0001f) return;
|
||||
|
||||
float rad = size * tilesize / 2f * 0.74f;
|
||||
float rad = size * tilesize / 2f * 0.74f * Mathf.lerp(1f, 1.3f, launchHeat);
|
||||
float scl = 2f;
|
||||
|
||||
Draw.z(Layer.bullet - 0.0001f);
|
||||
Lines.stroke(1.75f * heat, Pal.accent);
|
||||
Lines.square(x, y, rad * 1.22f, 45f);
|
||||
Lines.square(x, y, rad * 1.22f, Mathf.lerp(45f, 0f, launchHeat));
|
||||
|
||||
//TODO: lock time when launching
|
||||
|
||||
Lines.stroke(3f * heat, Pal.accent);
|
||||
Lines.square(x, y, rad, Time.time / scl);
|
||||
Lines.square(x, y, rad, -Time.time / scl);
|
||||
Lines.square(x, y, rad * Mathf.lerp(1f, 1.3f, launchHeat), 45f + time / scl);
|
||||
Lines.square(x, y, rad * Mathf.lerp(1f, 1.8f, launchHeat), Mathf.lerp(45f, 0f, launchHeat) - time / scl);
|
||||
|
||||
Draw.color(team.color);
|
||||
Draw.alpha(Mathf.clamp(heat * 3f));
|
||||
|
||||
for(int i = 0; i < 4; i++){
|
||||
float rot = i*90f + 45f + (-Time.time /3f)%360f;
|
||||
float length = 26f * heat;
|
||||
float rot = i*90f + 45f + (-time/3f)%360f;
|
||||
float length = 26f * heat * Mathf.lerp(1f, 1.5f, launchHeat);
|
||||
Draw.rect(arrowRegion, x + Angles.trnsx(rot, length), y + Angles.trnsy(rot, length), rot + 180f);
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawLight(){
|
||||
Drawf.light(x, y, lightRadius, lightColor, launchHeat);
|
||||
}
|
||||
|
||||
public boolean canLaunch(){
|
||||
return isValid() && state.isCampaign() && efficiency > 0f && power.graph.getBatteryStored() >= powerBufferRequirement-0.00001f && progress >= 1f && !launching;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor getCursor(){
|
||||
return !state.isCampaign() || efficiency <= 0f ? SystemCursor.arrow : super.getCursor();
|
||||
return canLaunch() ? SystemCursor.hand : super.getCursor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawSelect(){
|
||||
super.drawSelect();
|
||||
|
||||
if(power.graph.getBatteryStored() < powerBufferRequirement && !launching){
|
||||
drawPlaceText(Core.bundle.get("bar.nobatterypower"), tile.x, tile.y, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildConfiguration(Table table){
|
||||
deselect();
|
||||
|
||||
if(!state.isCampaign() || efficiency <= 0f) return;
|
||||
if(!canLaunch()) return;
|
||||
|
||||
ui.showInfo("This block has been removed from the tech tree as of v7, and no longer has a use.\n\nWill it ever be used for anything? Who knows.");
|
||||
ui.planet.showPlanetLaunch(state.rules.sector, launchCandidates == null ? state.rules.sector.planet.launchCandidates : launchCandidates, sector -> {
|
||||
if(canLaunch()){
|
||||
consume();
|
||||
power.graph.useBatteries(powerBufferRequirement);
|
||||
progress = 0f;
|
||||
|
||||
if(false)
|
||||
ui.planet.showPlanetLaunch(state.rules.sector, sector -> {
|
||||
//TODO cutscene, etc...
|
||||
renderer.showLaunch(this);
|
||||
|
||||
//TODO should consume resources based on destination schem
|
||||
consume();
|
||||
Time.runTask(launchDuration() - 6f, () -> {
|
||||
//unlock right before launch
|
||||
launching = false;
|
||||
sector.planet.unlockedOnLand.each(UnlockableContent::unlock);
|
||||
|
||||
universe.clearLoadoutInfo();
|
||||
universe.updateLoadout(sector.planet.generator.defaultLoadout.findCore(), sector.planet.generator.defaultLoadout);
|
||||
universe.clearLoadoutInfo();
|
||||
universe.updateLoadout((CoreBlock)launchBlock);
|
||||
|
||||
control.playSector(sector);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Events.fire(Trigger.acceleratorUse);
|
||||
@@ -135,5 +279,352 @@ public class Accelerator extends Block{
|
||||
public boolean acceptItem(Building source, Item item){
|
||||
return items.get(item) < getMaximumAccepted(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte version(){
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Writes write){
|
||||
super.write(write);
|
||||
write.f(progress);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(Reads read, byte revision){
|
||||
super.read(read, revision);
|
||||
|
||||
if(revision >= 1){
|
||||
progress = read.f();
|
||||
}
|
||||
}
|
||||
|
||||
//launch animator stuff:
|
||||
@Override
|
||||
public float launchDuration(){
|
||||
return launchDuration + chargeDuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Music landMusic(){
|
||||
//unused
|
||||
return launchMusic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Music launchMusic(){
|
||||
return launchMusic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginLaunch(boolean launching){
|
||||
if(!launching) return;
|
||||
|
||||
this.launching = true;
|
||||
Fx.coreLaunchConstruct.at(x, y, launchBlock.size);
|
||||
|
||||
cloudSeed = Mathf.random(1f);
|
||||
float margin = 30f;
|
||||
|
||||
Image image = new Image();
|
||||
image.color.a = 0f;
|
||||
image.touchable = Touchable.disabled;
|
||||
image.setFillParent(true);
|
||||
image.actions(Actions.delay((launchDuration() - margin) / 60f), Actions.fadeIn(margin / 60f, Interp.pow2In), Actions.delay(6f / 60f), Actions.remove());
|
||||
image.update(() -> {
|
||||
image.toFront();
|
||||
ui.loadfrag.toFront();
|
||||
if(state.isMenu()){
|
||||
image.remove();
|
||||
}
|
||||
});
|
||||
Core.scene.add(image);
|
||||
|
||||
Time.run(chargeDuration, () -> {
|
||||
Fx.coreLaunchConstruct.at(x, y, launchBlock.size);
|
||||
Fx.launchAccelerator.at(x, y);
|
||||
Effect.shake(10f, 14f, this);
|
||||
|
||||
for(int i = 0; i < launchLightning; i++){
|
||||
float a = Mathf.random(360f);
|
||||
Lightning.create(team, lightningColor, lightningDamage, x + Angles.trnsx(a, lightningOffset), y + Angles.trnsy(a, lightningOffset), a, Mathf.random(lightningLengthMin, lightningLengthMax));
|
||||
}
|
||||
|
||||
float spacing = 12f;
|
||||
for(int i = 0; i < 13; i++){
|
||||
int fi = i;
|
||||
Time.run(i * 1.1f, () -> {
|
||||
float radius = block.size/2f + 1 + spacing * fi;
|
||||
int rays = Mathf.ceil(radius * Mathf.PI * 2f / 6f);
|
||||
for(int r = 0; r < rays; r++){
|
||||
if(Mathf.chance(0.7f - fi * 0.02f)){
|
||||
float angle = r * 360f / (float)rays;
|
||||
float ox = Angles.trnsx(angle, radius), oy = Angles.trnsy(angle, radius);
|
||||
Tile t = world.tileWorld(x + ox, y + oy);
|
||||
if(t != null){
|
||||
Fx.coreLandDust.at(t.worldx(), t.worldy(), angle + Mathf.range(30f), Tmp.c1.set(t.floor().mapColor).mul(1.7f + Mathf.range(0.15f)));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endLaunch(){
|
||||
launching = false;
|
||||
launchTime = 0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float zoomLaunch(){
|
||||
float rawTime = launchDuration() - renderer.getLandTime();
|
||||
float shake = rawTime < chargeDuration ? Interp.pow10In.apply(Mathf.clamp(rawTime/chargeDuration)) : 0f;
|
||||
|
||||
Core.camera.position.set(x, y).add(Tmp.v1.setToRandomDirection().scl(shake * 2f));
|
||||
|
||||
if(rawTime < chargeDuration){
|
||||
float fin = rawTime / chargeDuration;
|
||||
|
||||
return chargeZoomInterp.apply(Scl.scl(landZoomTo), Scl.scl(chargeZoomTo), fin);
|
||||
}else{
|
||||
float rawFin = renderer.getLandTimeIn();
|
||||
float fin = 1f - Mathf.clamp((1f - rawFin) - (chargeDuration / (launchDuration + chargeDuration))) / (1f - (chargeDuration / (launchDuration + chargeDuration)));
|
||||
|
||||
return landZoomInterp.apply(Scl.scl(landZoomFrom), Scl.scl(landZoomTo), fin);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateLaunch(){
|
||||
float in = renderer.getLandTimeIn() * launchDuration();
|
||||
launchTime = launchDuration() - in;
|
||||
float tsize = Mathf.sample(CoreBlock.thrusterSizes, (in + 35f) / launchDuration());
|
||||
|
||||
float rawFin = renderer.getLandTimeIn();
|
||||
float chargeFin = 1f - Mathf.clamp((1f - rawFin) / (chargeDuration / (launchDuration + chargeDuration)));
|
||||
float chargeFout = 1f - chargeFin;
|
||||
|
||||
if(in > launchDuration){
|
||||
if(Mathf.chanceDelta(lightningLaunchChance * Interp.pow3In.apply(chargeFout))){
|
||||
float a = Mathf.random(360f);
|
||||
Lightning.create(team, lightningColor, lightningDamage, x + Angles.trnsx(a, lightningOffset), y + Angles.trnsy(a, lightningOffset), a, Mathf.random(lightningLengthMin, lightningLengthMax));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawLaunch(){
|
||||
var clouds = Core.assets.get("sprites/clouds.png", Texture.class);
|
||||
|
||||
float rawFin = renderer.getLandTimeIn();
|
||||
float rawTime = launchDuration() - renderer.getLandTime();
|
||||
float fin = 1f - Mathf.clamp((1f - rawFin) - (chargeDuration / (launchDuration + chargeDuration))) / (1f - (chargeDuration / (launchDuration + chargeDuration)));
|
||||
|
||||
float chargeFin = 1f - Mathf.clamp((1f - rawFin) / (chargeDuration / (launchDuration + chargeDuration)));
|
||||
float chargeFout = 1f - chargeFin;
|
||||
|
||||
float cameraScl = renderer.getDisplayScale();
|
||||
|
||||
float fout = 1f - fin;
|
||||
float scl = Scl.scl(4f) / cameraScl;
|
||||
float pfin = Interp.pow3Out.apply(fin), pf = Interp.pow2In.apply(fout);
|
||||
|
||||
//draw particles
|
||||
Draw.color(Pal.lightTrail);
|
||||
Angles.randLenVectors(1, pfin, 100, 800f * scl * pfin, (ax, ay, ffin, ffout) -> {
|
||||
Lines.stroke(scl * ffin * pf * 3f);
|
||||
Lines.lineAngle(x + ax, y + ay, Mathf.angle(ax, ay), (ffin * 20 + 1f) * scl);
|
||||
});
|
||||
Draw.color();
|
||||
|
||||
if(rawTime >= chargeDuration){
|
||||
drawLanding(fin, x, y);
|
||||
}
|
||||
|
||||
Draw.color();
|
||||
Draw.mixcol(Color.white, Interp.pow5In.apply(fout));
|
||||
|
||||
//accent tint indicating that the core was just constructed
|
||||
if(renderer.isLaunching()){
|
||||
float f = Mathf.clamp(1f - fout * 12f);
|
||||
if(f > 0.001f){
|
||||
Draw.mixcol(Pal.accent, f);
|
||||
}
|
||||
}
|
||||
|
||||
//draw clouds
|
||||
if(state.rules.cloudColor.a > 0.0001f){
|
||||
float scaling = CoreBlock.cloudScaling;
|
||||
float sscl = Math.max(1f + Mathf.clamp(fin + CoreBlock.cfinOffset) * CoreBlock.cfinScl, 0f) * cameraScl;
|
||||
|
||||
Tmp.tr1.set(clouds);
|
||||
Tmp.tr1.set(
|
||||
(Core.camera.position.x - Core.camera.width/2f * sscl) / scaling,
|
||||
(Core.camera.position.y - Core.camera.height/2f * sscl) / scaling,
|
||||
(Core.camera.position.x + Core.camera.width/2f * sscl) / scaling,
|
||||
(Core.camera.position.y + Core.camera.height/2f * sscl) / scaling);
|
||||
|
||||
Tmp.tr1.scroll(10f * cloudSeed, 10f * cloudSeed);
|
||||
|
||||
Draw.alpha(Mathf.sample(CoreBlock.cloudAlphas, fin + CoreBlock.calphaFinOffset) * CoreBlock.cloudAlpha);
|
||||
Draw.mixcol(state.rules.cloudColor, state.rules.cloudColor.a);
|
||||
Draw.rect(Tmp.tr1, Core.camera.position.x, Core.camera.position.y, Core.camera.width, Core.camera.height);
|
||||
Draw.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawLaunchGlobalZ(){
|
||||
float rawFin = renderer.getLandTimeIn();
|
||||
|
||||
float chargeFin = 1f - Mathf.clamp((1f - rawFin) / (chargeDuration / (launchDuration + chargeDuration)));
|
||||
float fin = 1f - Mathf.clamp((1f - rawFin) - (chargeDuration / (launchDuration + chargeDuration))) / (1f - (chargeDuration / (launchDuration + chargeDuration)));
|
||||
float fout = 1f - fin;
|
||||
float chargeFout = 1f - chargeFin;
|
||||
|
||||
//fade out rings during launch.
|
||||
chargeFout = Mathf.clamp(chargeFout - fout * 2f);
|
||||
|
||||
float
|
||||
spacing = 1f / (chargeRings + chargeRingMerge);
|
||||
|
||||
for(int i = 0; i < chargeRings; i++){
|
||||
float cfin = Mathf.clamp((chargeFout*ringSpeedup - spacing * i) / (spacing * (1f + chargeRingMerge)));
|
||||
if(cfin > 0){
|
||||
drawRing(ringRadBase + ringRadSpacing * Mathf.pow(i, ringRadPow), cfin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void drawRing(float radius, float fin){
|
||||
Draw.z(Layer.effect);
|
||||
|
||||
float fout = 1f - fin;
|
||||
float rotate = Interp.pow4In.apply(fout) * 90f;
|
||||
float rad = radius + 20f * Interp.pow4In.apply(fout);
|
||||
|
||||
Lines.stroke(ringStroke * fin, ringColor);
|
||||
|
||||
Draw.color(Pal.command, ringColor, fin);
|
||||
|
||||
//handles
|
||||
for(int i = 0; i < 4; i++){
|
||||
float angle = i * 90f + 45f + rotate;
|
||||
Lines.beginLine();
|
||||
Lines.linePoint(Tmp.v1.trns(angle - ringHandleLen, rad * ringHandleTilt).add(x, y));
|
||||
Lines.linePoint(Tmp.v2.trns(angle, rad).add(x, y));
|
||||
Lines.linePoint(Tmp.v3.trns(angle + ringHandleLen, rad * ringHandleTilt).add(x, y));
|
||||
Lines.endLine(false);
|
||||
|
||||
}
|
||||
|
||||
Draw.scl(fin);
|
||||
|
||||
//selection triangles
|
||||
for(int i = 0; i < 4; i++){
|
||||
float angle = i * 90f + rotate;
|
||||
|
||||
|
||||
Draw.rect(selectArrowRegion, x + Angles.trnsx(angle, rad), y + Angles.trnsy(angle, rad), angle + 180f + 45f);
|
||||
|
||||
//shape variant:
|
||||
//Lines.poly(x + Angles.trnsx(angle, rad), y + Angles.trnsy(angle, rad), 3, ringArrowRad * fin, angle + 180f);
|
||||
}
|
||||
|
||||
Draw.scl();
|
||||
|
||||
}
|
||||
|
||||
protected void drawLanding(float fin, float x, float y){
|
||||
float rawTime = launchDuration() - renderer.getLandTime();
|
||||
float fout = 1f - fin;
|
||||
|
||||
float scl = rawTime < chargeDuration ? 1f : Scl.scl(4f) / renderer.getDisplayScale();
|
||||
float shake = 0f;
|
||||
float s = launchBlock.region.width * launchBlock.region.scl() * scl * 3.6f * Interp.pow2Out.apply(fout);
|
||||
float rotation = Interp.pow2In.apply(fout) * 135f;
|
||||
x += Mathf.range(shake);
|
||||
y += Mathf.range(shake);
|
||||
float thrustOpen = 0.25f;
|
||||
float thrusterFrame = fin >= thrustOpen ? 1f : fin / thrustOpen;
|
||||
float thrusterSize = Mathf.sample(CoreBlock.thrusterSizes, fin);
|
||||
|
||||
//when launching, thrusters stay out the entire time.
|
||||
if(renderer.isLaunching()){
|
||||
Interp i = Interp.pow2Out;
|
||||
thrusterFrame = i.apply(Mathf.clamp(fout*13f));
|
||||
thrusterSize = i.apply(Mathf.clamp(fout*9f));
|
||||
}
|
||||
|
||||
Draw.color(Pal.lightTrail);
|
||||
//TODO spikier heat
|
||||
Draw.rect("circle-shadow", x, y, s, s);
|
||||
|
||||
Draw.scl(scl);
|
||||
|
||||
//draw thruster flame
|
||||
float strength = (1f + (launchBlock.size - 3)/2.5f) * scl * thrusterSize * (0.95f + Mathf.absin(2f, 0.1f));
|
||||
float offset = (launchBlock.size - 3) * 3f * scl;
|
||||
|
||||
for(int i = 0; i < 4; i++){
|
||||
Tmp.v1.trns(i * 90 + rotation, 1f);
|
||||
|
||||
Tmp.v1.setLength((launchBlock.size * tilesize/2f + 1f)*scl + strength*2f + offset);
|
||||
Draw.color(team.color);
|
||||
Fill.circle(Tmp.v1.x + x, Tmp.v1.y + y, 6f * strength);
|
||||
|
||||
Tmp.v1.setLength((launchBlock.size * tilesize/2f + 1f)*scl + strength*0.5f + offset);
|
||||
Draw.color(Color.white);
|
||||
Fill.circle(Tmp.v1.x + x, Tmp.v1.y + y, 3.5f * strength);
|
||||
}
|
||||
|
||||
drawLandingThrusters(x, y, rotation, thrusterFrame);
|
||||
|
||||
Drawf.spinSprite(launchBlock.region, x, y, rotation);
|
||||
|
||||
Draw.alpha(Interp.pow4In.apply(thrusterFrame));
|
||||
drawLandingThrusters(x, y, rotation, thrusterFrame);
|
||||
Draw.alpha(1f);
|
||||
|
||||
if(launchBlock.teamRegions[team.id] == launchBlock.teamRegion) Draw.color(team.color);
|
||||
|
||||
Drawf.spinSprite(launchBlock.teamRegions[team.id], x, y, rotation);
|
||||
|
||||
Draw.color();
|
||||
Draw.scl();
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
protected void drawLandingThrusters(float x, float y, float rotation, float frame){
|
||||
CoreBlock core = (CoreBlock)launchBlock;
|
||||
float length = core.thrusterLength * (frame - 1f) - 1f/4f;
|
||||
float alpha = Draw.getColorAlpha();
|
||||
|
||||
//two passes for consistent lighting
|
||||
for(int j = 0; j < 2; j++){
|
||||
for(int i = 0; i < 4; i++){
|
||||
var reg = i >= 2 ? core.thruster2 : core.thruster1;
|
||||
float rot = (i * 90) + rotation % 90f;
|
||||
Tmp.v1.trns(rot, length * Draw.xscl);
|
||||
|
||||
//second pass applies extra layer of shading
|
||||
if(j == 1){
|
||||
Tmp.v1.rotate(-90f);
|
||||
Draw.alpha((rotation % 90f) / 90f * alpha);
|
||||
rot -= 90f;
|
||||
Draw.rect(reg, x + Tmp.v1.x, y + Tmp.v1.y, rot);
|
||||
}else{
|
||||
Draw.alpha(alpha);
|
||||
Draw.rect(reg, x + Tmp.v1.x, y + Tmp.v1.y, rot);
|
||||
}
|
||||
}
|
||||
}
|
||||
Draw.alpha(1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
471
core/src/mindustry/world/blocks/campaign/LandingPad.java
Normal file
471
core/src/mindustry/world/blocks/campaign/LandingPad.java
Normal file
@@ -0,0 +1,471 @@
|
||||
package mindustry.world.blocks.campaign;
|
||||
|
||||
import arc.*;
|
||||
import arc.Graphics.*;
|
||||
import arc.Graphics.Cursor.*;
|
||||
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.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.io.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
import mindustry.world.blocks.liquid.*;
|
||||
import mindustry.world.consumers.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class LandingPad extends Block{
|
||||
static ObjectMap<Item, Seq<LandingPadBuild>> waiting = new ObjectMap<>();
|
||||
static long lastUpdateId = -1;
|
||||
|
||||
static{
|
||||
Events.on(ResetEvent.class, e -> {
|
||||
waiting.clear();
|
||||
lastUpdateId = -1;
|
||||
});
|
||||
}
|
||||
|
||||
public @Load(value = "@-pod", fallback = "advanced-launch-pad-pod") TextureRegion podRegion;
|
||||
public float arrivalDuration = 150f;
|
||||
public float cooldownTime = 150f;
|
||||
public float consumeLiquidAmount = 100f;
|
||||
public Liquid consumeLiquid = Liquids.water;
|
||||
|
||||
public Effect landEffect = Fx.podLandShockwave;
|
||||
public Effect coolingEffect = Fx.none;
|
||||
public float coolingEffectChance = 0.2f;
|
||||
|
||||
public float liquidPad = 2f;
|
||||
public Color bottomColor = Pal.darkerMetal;
|
||||
|
||||
public LandingPad(String name){
|
||||
super(name);
|
||||
|
||||
hasItems = true;
|
||||
hasLiquids = true;
|
||||
solid = true;
|
||||
update = true;
|
||||
configurable = true;
|
||||
acceptsItems = false;
|
||||
canOverdrive = false; //overdriving can't do anything meaningful besides decrease cooldown, which is very small anyway, so don't bother
|
||||
emitLight = true;
|
||||
lightRadius = 90f;
|
||||
|
||||
config(Item.class, (LandingPadBuild build, Item item) -> {
|
||||
if(!build.accessible()) return;
|
||||
|
||||
build.config = item;
|
||||
});
|
||||
configClear((LandingPadBuild build) -> {
|
||||
if(!build.accessible()) return;
|
||||
|
||||
build.config = null;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
consume(new ConsumeLiquid(consumeLiquid, consumeLiquidAmount){
|
||||
|
||||
@Override
|
||||
public void build(Building build, Table table){
|
||||
table.add(new ReqImage(liquid.uiIcon, () -> build.liquids.get(liquid) >= amount)).size(iconMed).top().left();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float efficiency(Building build){
|
||||
return build.liquids.get(consumeLiquid) >= amount ? 1f : 0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void display(Stats stats){
|
||||
stats.add(Stat.input, liquid, amount, false);
|
||||
}
|
||||
}).update(false);
|
||||
|
||||
super.init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBars(){
|
||||
super.setBars();
|
||||
|
||||
addLiquidBar(consumeLiquid);
|
||||
//TODO: does cooldown even need to exist?
|
||||
addBar("cooldown", (LandingPadBuild entity) -> new Bar("bar.cooldown", Pal.lightOrange, () -> entity.cooldown));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean outputsItems(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Remote(called = Loc.server)
|
||||
public static void landingPadLanded(Tile tile){
|
||||
if(tile == null || !(tile.build instanceof LandingPadBuild build)) return;
|
||||
build.handleLanding();
|
||||
}
|
||||
|
||||
public class LandingPadBuild extends Building{
|
||||
public @Nullable Item config;
|
||||
//priority collisions are possible, but should be extremely rare
|
||||
public int priority = Mathf.rand.nextInt();
|
||||
public float cooldown = 0f, landParticleTimer;
|
||||
|
||||
public float arrivingTimer = 0f;
|
||||
public @Nullable Item arriving;
|
||||
public float liquidRemoved;
|
||||
|
||||
public void handleLanding(){
|
||||
if(config == null) return;
|
||||
|
||||
cooldown = 1f;
|
||||
arriving = config;
|
||||
arrivingTimer = 0f;
|
||||
liquidRemoved = 0f;
|
||||
|
||||
if(state.isCampaign() && !isFake()){
|
||||
state.rules.sector.info.importCooldownTimers.put(config, 0f);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean accessible(){
|
||||
//In custom games, this block can be configured by anyone except the player team; this allows for enemy builder AI to use it
|
||||
return state.rules.editor || state.rules.allowEditWorldProcessors || state.isCampaign() || state.rules.infiniteResources || (team != state.rules.defaultTeam && !state.rules.pvp && team != Team.derelict);
|
||||
}
|
||||
|
||||
public void updateTimers(){
|
||||
if(state.isCampaign() && lastUpdateId != state.updateId){
|
||||
lastUpdateId = state.updateId;
|
||||
|
||||
float[] imports = state.rules.sector.info.getImportRates(state.getPlanet());
|
||||
|
||||
for(Item item : content.items()){
|
||||
float importedPerFrame = imports[item.id]/60f;
|
||||
if(importedPerFrame > 0f){
|
||||
float framesBetweenArrival = itemCapacity / importedPerFrame;
|
||||
|
||||
state.rules.sector.info.importCooldownTimers.increment(item, 0f, 1f / framesBetweenArrival * Time.delta);
|
||||
}else{
|
||||
//nothing is being imported, so reset the timer
|
||||
state.rules.sector.info.importCooldownTimers.put(item, 0f);
|
||||
}
|
||||
}
|
||||
|
||||
waiting.each((item, pads) -> {
|
||||
if(pads.size > 0){
|
||||
pads.sort(p -> p.priority);
|
||||
|
||||
var first = pads.first();
|
||||
var head = pads.peek();
|
||||
|
||||
Call.landingPadLanded(first.tile);
|
||||
|
||||
//swap priorities, moving this block to the end of the list (if there is only one block waiting, this does nothing)
|
||||
var tmp = first.priority;
|
||||
first.priority = head.priority;
|
||||
head.priority = tmp;
|
||||
|
||||
pads.clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
if(consumeLiquid != null){
|
||||
Draw.color(bottomColor);
|
||||
Fill.square(x, y, size * tilesize/2f - liquidPad);
|
||||
Draw.color();
|
||||
LiquidBlock.drawTiledFrames(block.size, x, y, liquidPad, liquidPad, liquidPad, liquidPad, consumeLiquid, liquids.get(consumeLiquid) / liquidCapacity);
|
||||
}
|
||||
|
||||
super.draw();
|
||||
|
||||
if(arriving != null){
|
||||
float fin = Mathf.clamp(arrivingTimer), fout = 1f - fin;
|
||||
float alpha = Interp.pow5Out.apply(fin);
|
||||
float scale = (1f - alpha) * 1.3f + 1f;
|
||||
float
|
||||
cx = x,
|
||||
cy = y + Interp.pow4In.apply(fout) * (100f + Mathf.randomSeedRange(id() + 2, 30f));
|
||||
|
||||
float rotation = fout * (90f + Mathf.randomSeedRange(id(), 50f));
|
||||
|
||||
Draw.z(Layer.effect + 0.001f);
|
||||
|
||||
Draw.color(Pal.engine);
|
||||
|
||||
float rad = 0.15f + Interp.pow5Out.apply(Mathf.slope(fin));
|
||||
|
||||
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);
|
||||
|
||||
scale *= podRegion.scl();
|
||||
float rw = podRegion.width * scale, rh = podRegion.height * scale;
|
||||
|
||||
Draw.alpha(alpha);
|
||||
Drawf.shadow(cx, cy, size * tilesize, fin);
|
||||
Draw.rect(podRegion, cx, cy, rw, rh, rotation);
|
||||
|
||||
Tmp.v1.trns(225f, Interp.pow3In.apply(fout) * 250f);
|
||||
|
||||
Draw.z(Layer.flyingUnit + 1);
|
||||
Draw.color(0, 0, 0, 0.22f * alpha);
|
||||
|
||||
Draw.rect(podRegion, cx + Tmp.v1.x, cy + Tmp.v1.y, rw, rh, rotation);
|
||||
|
||||
}else if(cooldown > 0f){
|
||||
|
||||
Drawf.shadow(x, y, size * tilesize, cooldown);
|
||||
Draw.alpha(cooldown);
|
||||
Draw.mixcol(Pal.accent, 1f - cooldown);
|
||||
Draw.rect(podRegion, x, y);
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawLight(){
|
||||
Drawf.light(x, y, lightRadius, Pal.accent, Mathf.clamp(Math.max(cooldown, arrivingTimer * 1.5f)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
updateTimers();
|
||||
|
||||
if(arriving != null){
|
||||
if(!headless){ //pod particles
|
||||
float fin = arrivingTimer;
|
||||
float tsize = Interp.pow5Out.apply(fin);
|
||||
|
||||
landParticleTimer += tsize * Time.delta / 2f;
|
||||
if(landParticleTimer >= 1f){
|
||||
tile.getLinkedTiles(t -> {
|
||||
if(Mathf.chance(0.1f)){
|
||||
Fx.podLandDust.at(t.worldx(), t.worldy(), angleTo(t.worldx(), t.worldy()) + Mathf.range(30f), Tmp.c1.set(t.floor().mapColor).mul(1.5f + Mathf.range(0.15f)));
|
||||
}
|
||||
});
|
||||
|
||||
landParticleTimer = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
arrivingTimer += Time.delta / arrivalDuration;
|
||||
|
||||
float toRemove = Math.min(consumeLiquidAmount / arrivalDuration * Time.delta, consumeLiquidAmount - liquidRemoved);
|
||||
liquidRemoved += toRemove;
|
||||
|
||||
liquids.remove(consumeLiquid, toRemove);
|
||||
|
||||
if(Mathf.chanceDelta(coolingEffectChance * Interp.pow5Out.apply(arrivingTimer))){
|
||||
coolingEffect.at(this);
|
||||
}
|
||||
|
||||
if(arrivingTimer >= 1f){
|
||||
//remove any leftovers to make sure it's precise
|
||||
liquids.remove(consumeLiquid, consumeLiquidAmount - liquidRemoved);
|
||||
|
||||
landEffect.at(this);
|
||||
Effect.shake(3f, 3f, this);
|
||||
|
||||
items.set(arriving, itemCapacity);
|
||||
if(!isFake()){
|
||||
state.getSector().info.handleItemImport(arriving, itemCapacity);
|
||||
}
|
||||
|
||||
arriving = null;
|
||||
arrivingTimer = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
if(items.total() > 0){
|
||||
dumpAccumulate(config == null || items.get(config) != items.total() ? null : config);
|
||||
}
|
||||
|
||||
if(arriving == null){
|
||||
cooldown -= delta() / cooldownTime;
|
||||
cooldown = Mathf.clamp(cooldown);
|
||||
}
|
||||
|
||||
if(config != null && (isFake() || (state.isCampaign() && !state.getPlanet().campaignRules.legacyLaunchPads))){
|
||||
|
||||
if(cooldown <= 0f && efficiency > 0f && items.total() == 0 && (isFake() || (state.rules.sector.info.getImportRate(state.getPlanet(), config) > 0f && state.rules.sector.info.importCooldownTimers.get(config, 0f) >= 1f))){
|
||||
|
||||
if(isFake()){
|
||||
//there is no queue for enemy team blocks, it's all fake
|
||||
Call.landingPadLanded(tile);
|
||||
}else{
|
||||
//queue landing for next frame
|
||||
waiting.get(config, Seq::new).add(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @return whether this pad should receive items forever, essentially acting as an item source for maps. */
|
||||
public boolean isFake(){
|
||||
return team != state.rules.defaultTeam || !state.isCampaign();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDump(Building to, Item item){
|
||||
//hack: canDump is only ever called right before item offload, so count the item as "produced" before that.
|
||||
produced(item);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawSelect(){
|
||||
drawItemSelection(config);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor getCursor(){
|
||||
return !accessible() ? SystemCursor.arrow : super.getCursor();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldShowConfigure(Player player){
|
||||
return accessible();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onConfigureBuildTapped(Building other){
|
||||
if(this == other || !accessible()){
|
||||
deselect();
|
||||
return false;
|
||||
}
|
||||
|
||||
return super.onConfigureBuildTapped(other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildConfiguration(Table table){
|
||||
|
||||
ItemSelection.buildTable(LandingPad.this, table, content.items(), () -> config, this::configure, selectionRows, selectionColumns);
|
||||
|
||||
if(!net.client() && !isFake()){
|
||||
table.row();
|
||||
|
||||
table.table(t -> {
|
||||
t.background(Styles.black6);
|
||||
|
||||
t.button(Icon.downOpen, Styles.clearNonei, 40f, () -> {
|
||||
if(config != null && state.isCampaign()){
|
||||
for(Sector sector : state.getPlanet().sectors){
|
||||
if(sector.hasBase() && sector != state.getSector() && sector.info.destination != state.getSector() && sector.info.hasExport(config)){
|
||||
sector.info.destination = state.getSector();
|
||||
sector.saveInfo();
|
||||
}
|
||||
}
|
||||
state.getSector().info.refreshImportRates(state.getPlanet());
|
||||
}
|
||||
}).disabled(b -> config == null || !state.isCampaign() || (!state.getPlanet().sectors.contains(s -> s.hasBase() && s.info.hasExport(config) && s.info.destination != state.getSector())))
|
||||
.tooltip("@sectors.redirect").get();
|
||||
}).fillX().left();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void display(Table table){
|
||||
super.display(table);
|
||||
|
||||
if(!state.isCampaign() || net.client() || team != player.team() || isFake()) return;
|
||||
|
||||
table.row();
|
||||
table.label(() -> {
|
||||
if(!state.isCampaign() || isFake()) return "";
|
||||
|
||||
if(state.getPlanet().campaignRules.legacyLaunchPads){
|
||||
return Core.bundle.get("landingpad.legacy.disabled");
|
||||
}
|
||||
|
||||
if(config == null) return "";
|
||||
|
||||
int sources = 0;
|
||||
float perSecond = 0f;
|
||||
for(var s : state.getPlanet().sectors){
|
||||
if(s != state.getSector() && s.hasBase() && s.info.destination == state.getSector()){
|
||||
float amount = s.info.getExport(config);
|
||||
if(amount > 0){
|
||||
sources ++;
|
||||
perSecond += s.info.getExport(config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String str = Core.bundle.format("landing.sources", sources == 0 ? Core.bundle.get("none") : sources);
|
||||
if(perSecond > 0){
|
||||
str += "\n" + Core.bundle.format("landing.import", config.emoji(), (int)(perSecond * 60f));
|
||||
}
|
||||
return str;
|
||||
}).pad(4).wrap().width(200f).left();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptItem(Building source, Item item){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Object config(){
|
||||
return config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte version(){
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(Reads read, byte revision){
|
||||
super.read(read, revision);
|
||||
config = TypeIO.readItem(read);
|
||||
priority = read.i();
|
||||
cooldown = read.f();
|
||||
|
||||
if(revision >= 1){
|
||||
arriving = TypeIO.readItem(read);
|
||||
arrivingTimer = read.f();
|
||||
liquidRemoved = read.f();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Writes write){
|
||||
super.write(write);
|
||||
TypeIO.writeItem(write, config);
|
||||
write.i(priority);
|
||||
write.f(cooldown);
|
||||
|
||||
TypeIO.writeItem(write, arriving);
|
||||
write.f(arrivingTimer);
|
||||
write.f(liquidRemoved);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,18 +22,27 @@ import mindustry.logic.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.liquid.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class LaunchPad extends Block{
|
||||
/** Time inbetween launches. */
|
||||
/** Time between launches. */
|
||||
public float launchTime = 1f;
|
||||
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 boolean acceptMultipleItems = false;
|
||||
|
||||
public float lightStep = 1f;
|
||||
public int lightSteps = 3;
|
||||
|
||||
public float liquidPad = 2f;
|
||||
public @Nullable Liquid drawLiquid;
|
||||
public Color bottomColor = Pal.darkerMetal;
|
||||
|
||||
public LaunchPad(String name){
|
||||
super(name);
|
||||
@@ -86,20 +95,23 @@ public class LaunchPad extends Block{
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
super.draw();
|
||||
if(hasLiquids && drawLiquid != null){
|
||||
Draw.color(bottomColor);
|
||||
Fill.square(x, y, size * tilesize/2f - liquidPad);
|
||||
Draw.color();
|
||||
LiquidBlock.drawTiledFrames(block.size, x, y, liquidPad, liquidPad, liquidPad, liquidPad, drawLiquid, liquids.get(drawLiquid) / liquidCapacity);
|
||||
}
|
||||
|
||||
if(!state.isCampaign()) return;
|
||||
super.draw();
|
||||
|
||||
if(lightRegion.found()){
|
||||
Draw.color(lightColor);
|
||||
float progress = Math.min((float)items.total() / itemCapacity, 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;
|
||||
for(int j = 0; j < lightSteps; j++){
|
||||
float alpha = Mathf.curve(progress, (float)j / lightSteps, (j+1f) / lightSteps);
|
||||
float offset = -(j - 1f) * lightStep;
|
||||
|
||||
Draw.color(Pal.metalGrayDark, lightColor, alpha);
|
||||
Draw.rect(lightRegion, x + Geometry.d8edge(i).x * offset, y + Geometry.d8edge(i).y * offset, i * 90);
|
||||
@@ -109,6 +121,7 @@ public class LaunchPad extends Block{
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
Drawf.shadow(x, y, size * tilesize);
|
||||
Draw.rect(podRegion, x, y);
|
||||
|
||||
Draw.reset();
|
||||
@@ -116,12 +129,11 @@ public class LaunchPad extends Block{
|
||||
|
||||
@Override
|
||||
public boolean acceptItem(Building source, Item item){
|
||||
return items.total() < itemCapacity;
|
||||
return items.total() < itemCapacity && (acceptMultipleItems || items.total() == 0 || items.first() == item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
if(!state.isCampaign()) return;
|
||||
|
||||
//increment launchCounter then launch when full and base conditions are met
|
||||
if((launchCounter += edelta()) >= launchTime && items.total() >= itemCapacity){
|
||||
@@ -149,7 +161,7 @@ public class LaunchPad extends Block{
|
||||
|
||||
table.row();
|
||||
table.label(() -> {
|
||||
Sector dest = state.rules.sector == null ? null : state.rules.sector.info.getRealDestination();
|
||||
Sector dest = state.rules.sector == null ? null : state.rules.sector.info.destination;
|
||||
|
||||
return Core.bundle.format("launch.destination",
|
||||
dest == null || !dest.hasBase() ? Core.bundle.get("sectors.nonelaunch") :
|
||||
@@ -157,6 +169,11 @@ public class LaunchPad extends Block{
|
||||
}).pad(4).wrap().width(200f).left();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldShowConfigure(Player player){
|
||||
return state.isCampaign();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildConfiguration(Table table){
|
||||
if(!state.isCampaign() || net.client()){
|
||||
@@ -167,7 +184,11 @@ public class LaunchPad extends Block{
|
||||
table.button(Icon.upOpen, Styles.cleari, () -> {
|
||||
ui.planet.showSelect(state.rules.sector, other -> {
|
||||
if(state.isCampaign() && other.planet == state.rules.sector.planet){
|
||||
var prev = state.rules.sector.info.destination;
|
||||
state.rules.sector.info.destination = other;
|
||||
if(prev != null){
|
||||
prev.info.refreshImportRates(state.getPlanet());
|
||||
}
|
||||
}
|
||||
});
|
||||
deselect();
|
||||
@@ -260,26 +281,24 @@ public class LaunchPad extends Block{
|
||||
|
||||
@Override
|
||||
public void remove(){
|
||||
if(!state.isCampaign()) return;
|
||||
if(!state.isCampaign() || net.client()) return;
|
||||
|
||||
Sector destsec = state.rules.sector.info.getRealDestination();
|
||||
Sector destsec = state.rules.sector.info.destination;
|
||||
|
||||
//actually launch the items upon removal
|
||||
if(team() == state.rules.defaultTeam){
|
||||
if(destsec != null && (destsec != state.rules.sector || net.client())){
|
||||
ItemSeq dest = new ItemSeq();
|
||||
if(team() == state.rules.defaultTeam && destsec != null && destsec != state.rules.sector){
|
||||
ItemSeq dest = new ItemSeq();
|
||||
|
||||
for(ItemStack stack : stacks){
|
||||
dest.add(stack);
|
||||
for(ItemStack stack : stacks){
|
||||
dest.add(stack);
|
||||
|
||||
//update export
|
||||
state.rules.sector.info.handleItemExport(stack);
|
||||
Events.fire(new LaunchItemEvent(stack));
|
||||
}
|
||||
//update export statistics
|
||||
state.rules.sector.info.handleItemExport(stack);
|
||||
Events.fire(new LaunchItemEvent(stack));
|
||||
}
|
||||
|
||||
if(!net.client()){
|
||||
destsec.addItems(dest);
|
||||
}
|
||||
if(state.getPlanet().campaignRules.legacyLaunchPads){
|
||||
destsec.addItems(dest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -146,10 +146,10 @@ public class BuildTurret extends BaseTurret{
|
||||
for(int i = 0; i < blocks.size; i++){
|
||||
var block = blocks.get(i);
|
||||
if(within(block.x * tilesize, block.y * tilesize, range)){
|
||||
var btype = content.block(block.block);
|
||||
var btype = block.block;
|
||||
|
||||
if(Build.validPlace(btype, unit.team(), block.x, block.y, block.rotation) && (state.rules.infiniteResources || team.rules().infiniteResources || team.items().has(btype.requirements, state.rules.buildCostMultiplier))){
|
||||
unit.addBuild(new BuildPlan(block.x, block.y, block.rotation, content.block(block.block), block.config));
|
||||
unit.addBuild(new BuildPlan(block.x, block.y, block.rotation, block.block, block.config));
|
||||
//shift build plan to tail so next unit builds something else
|
||||
blocks.addLast(blocks.removeIndex(i));
|
||||
lastPlan = block;
|
||||
|
||||
@@ -26,6 +26,7 @@ public class Door extends Wall{
|
||||
public Effect openfx = Fx.dooropen;
|
||||
public Effect closefx = Fx.doorclose;
|
||||
public Sound doorSound = Sounds.door;
|
||||
public boolean chainEffect = false;
|
||||
public @Load("@-open") TextureRegion openRegion;
|
||||
|
||||
public Door(String name){
|
||||
@@ -35,17 +36,23 @@ public class Door extends Wall{
|
||||
consumesTap = true;
|
||||
|
||||
config(Boolean.class, (DoorBuild base, Boolean open) -> {
|
||||
doorSound.at(base);
|
||||
base.effect();
|
||||
if(!world.isGenerating()){
|
||||
doorSound.at(base);
|
||||
base.effect();
|
||||
}
|
||||
|
||||
for(DoorBuild entity : base.chained){
|
||||
doorQueue.clear();
|
||||
doorQueue.add(base);
|
||||
|
||||
for(DoorBuild entity : base.chained.isEmpty() ? doorQueue : base.chained){
|
||||
//skip doors with things in them
|
||||
if((Units.anyEntities(entity.tile) && !open) || entity.open == open){
|
||||
continue;
|
||||
}
|
||||
|
||||
if(chainEffect) entity.effect();
|
||||
entity.open = open;
|
||||
pathfinder.updateTile(entity.tile());
|
||||
if(!world.isGenerating()) pathfinder.updateTile(entity.tile);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ public class ForceProjector extends Block{
|
||||
bullet.absorb();
|
||||
paramEffect.at(bullet);
|
||||
paramEntity.hit = 1f;
|
||||
paramEntity.buildup += bullet.damage;
|
||||
paramEntity.buildup += bullet.type.shieldDamage(bullet);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -105,7 +105,7 @@ public class ForceProjector extends Block{
|
||||
|
||||
if(consItems && itemConsumer instanceof ConsumeItems coni){
|
||||
stats.remove(Stat.booster);
|
||||
stats.add(Stat.booster, StatValues.itemBoosters("+{0} " + StatUnit.shieldHealth.localized(), stats.timePeriod, phaseShieldBoost, phaseRadiusBoost, coni.items, this::consumesItem));
|
||||
stats.add(Stat.booster, StatValues.itemBoosters("+{0} " + StatUnit.shieldHealth.localized(), stats.timePeriod, phaseShieldBoost, phaseRadiusBoost, coni.items));
|
||||
stats.add(Stat.booster, StatValues.speedBoosters("", coolantConsumption, Float.MAX_VALUE, true, this::consumesLiquid));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package mindustry.world.blocks.defense;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
@@ -39,6 +40,7 @@ public class MendProjector extends Block{
|
||||
lightRadius = 50f;
|
||||
suppressable = true;
|
||||
envEnabled |= Env.space;
|
||||
flags = EnumSet.of(BlockFlag.blockRepair);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -59,7 +61,7 @@ public class MendProjector extends Block{
|
||||
stats.add(Stat.booster, StatValues.itemBoosters(
|
||||
"{0}" + StatUnit.timesSpeed.localized(),
|
||||
stats.timePeriod, (phaseBoost + healPercent) / healPercent, phaseRangeBoost,
|
||||
cons.items, this::consumesItem)
|
||||
cons.items)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,6 @@ import mindustry.world.meta.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class OverdriveProjector extends Block{
|
||||
@Deprecated
|
||||
public final int timerUse = timers++;
|
||||
|
||||
public @Load("@-top") TextureRegion topRegion;
|
||||
public float reload = 60f;
|
||||
public float range = 80f;
|
||||
@@ -71,7 +68,7 @@ public class OverdriveProjector extends Block{
|
||||
|
||||
if(hasBoost && findConsumer(f -> f instanceof ConsumeItems) instanceof ConsumeItems items){
|
||||
stats.remove(Stat.booster);
|
||||
stats.add(Stat.booster, StatValues.itemBoosters("+{0}%", stats.timePeriod, speedBoostPhase * 100f, phaseRangeBoost, items.items, this::consumesItem));
|
||||
stats.add(Stat.booster, StatValues.itemBoosters("+{0}%", stats.timePeriod, speedBoostPhase * 100f, phaseRangeBoost, items.items));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ public class RegenProjector extends Block{
|
||||
suppressable = true;
|
||||
envEnabled |= Env.space;
|
||||
rotateDraw = false;
|
||||
flags = EnumSet.of(BlockFlag.blockRepair);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -97,7 +98,7 @@ public class RegenProjector extends Block{
|
||||
stats.add(Stat.booster, StatValues.itemBoosters(
|
||||
"{0}" + StatUnit.timesSpeed.localized(),
|
||||
stats.timePeriod, optionalMultiplier, 0f,
|
||||
cons.items, this::consumesItem)
|
||||
cons.items)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import mindustry.entities.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.logic.LAccess;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
@@ -69,7 +70,7 @@ public class ShockwaveTower extends Block{
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
if(potentialEfficiency > 0 && (reloadCounter += Time.delta) >= reload && timer(timerCheck, checkInterval)){
|
||||
if(potentialEfficiency > 0 && (reloadCounter += edelta()) >= reload && timer(timerCheck, checkInterval)){
|
||||
targets.clear();
|
||||
Groups.bullet.intersect(x - range, y - range, range * 2, range * 2, b -> {
|
||||
if(b.team != team && b.type.hittable){
|
||||
@@ -103,6 +104,14 @@ public class ShockwaveTower extends Block{
|
||||
heat = Mathf.clamp(heat - Time.delta / reload * cooldownMultiplier);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public double sense(LAccess sensor) {
|
||||
if(sensor == LAccess.progress) return reloadCounter / reload;
|
||||
return super.sense(sensor);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public float warmup(){
|
||||
return heat;
|
||||
@@ -110,7 +119,7 @@ public class ShockwaveTower extends Block{
|
||||
|
||||
@Override
|
||||
public boolean shouldConsume(){
|
||||
return targets.size != 0;
|
||||
return reloadCounter < reload;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -20,6 +20,7 @@ public class BaseTurret extends Block{
|
||||
public float placeOverlapMargin = 8 * 7f;
|
||||
public float rotateSpeed = 5;
|
||||
public float fogRadiusMultiplier = 1f;
|
||||
public boolean disableOverlapCheck = false;
|
||||
|
||||
/** Effect displayed when coolant is used. */
|
||||
public Effect coolEffect = Fx.fuelburn;
|
||||
@@ -57,7 +58,9 @@ public class BaseTurret extends Block{
|
||||
if(!hasConsumer(coolant)) consume(coolant);
|
||||
}
|
||||
|
||||
placeOverlapRange = Math.max(placeOverlapRange, range + placeOverlapMargin);
|
||||
if(!disableOverlapCheck){
|
||||
placeOverlapRange = Math.max(placeOverlapRange, range + placeOverlapMargin);
|
||||
}
|
||||
fogRadius = Math.max(Mathf.round(range / tilesize * fogRadiusMultiplier), fogRadius);
|
||||
super.init();
|
||||
}
|
||||
|
||||
@@ -50,13 +50,6 @@ public class ContinuousLiquidTurret extends ContinuousTurret{
|
||||
public void display(Stats stats){
|
||||
|
||||
}
|
||||
|
||||
//TODO
|
||||
//@Override
|
||||
//protected float use(Building entity){
|
||||
// BulletType type = ammoTypes.get(entity.liquids.current());
|
||||
// return Math.min(amount * entity.edelta(), entity.block.liquidCapacity) / (type == null ? 1f : type.ammoMultiplier);
|
||||
//}
|
||||
});
|
||||
|
||||
ammoTypes.each((item, type) -> placeOverlapRange = Math.max(placeOverlapRange, range + type.rangeChange + placeOverlapMargin));
|
||||
|
||||
@@ -117,7 +117,7 @@ public class ContinuousTurret extends Turret{
|
||||
entry.bullet.aimX = Tmp.v1.x;
|
||||
entry.bullet.aimY = Tmp.v1.y;
|
||||
if(scaleDamageEfficiency){
|
||||
entry.bullet.damage = entry.bullet.type.damage * Math.min(efficiency, 1f) * entry.bullet.damageMultiplier();
|
||||
entry.bullet.damage = entry.bullet.type.damage * Math.min(efficiency, 1f) * timeScale * entry.bullet.damageMultiplier();
|
||||
}
|
||||
|
||||
if(isShooting() && hasAmmo()){
|
||||
|
||||
@@ -27,7 +27,9 @@ public class LaserTurret extends PowerTurret{
|
||||
super.setStats();
|
||||
|
||||
stats.remove(Stat.booster);
|
||||
stats.add(Stat.input, StatValues.boosters(reload, coolant.amount, coolantMultiplier, false, this::consumesLiquid));
|
||||
if(coolant != null){
|
||||
stats.add(Stat.input, StatValues.boosters(reload, coolant.amount, coolantMultiplier, false, this::consumesLiquid));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package mindustry.world.blocks.defense.turrets;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.consumers.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
@@ -21,23 +18,7 @@ public class ReloadTurret extends BaseTurret{
|
||||
super.setStats();
|
||||
|
||||
if(coolant != null){
|
||||
stats.remove(Stat.booster);
|
||||
|
||||
//TODO this is very hacky, there is no current way to check if a ConsumeLiquidBase accepts something individually. fix later
|
||||
ObjectSet<Liquid> notBooster = content.liquids().select(l -> {
|
||||
for(Consume c : consumers){
|
||||
if(!c.booster && c != coolant &&
|
||||
((c instanceof ConsumeLiquid cl && cl.liquid == l) ||
|
||||
(c instanceof ConsumeLiquids cl2 && Structs.contains(cl2.liquids, s -> s.liquid == l)) ||
|
||||
(c instanceof ConsumeLiquidFilter clf && clf.filter.get(l)))){
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}).asSet();
|
||||
|
||||
stats.add(Stat.booster, StatValues.boosters(reload, coolant.amount, coolantMultiplier, true, l -> l.coolant && consumesLiquid(l) && !notBooster.contains(l)));
|
||||
stats.replace(Stat.booster, StatValues.boosters(reload, coolant.amount, coolantMultiplier, true, coolant::consumes));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,10 +27,10 @@ public class ReloadTurret extends BaseTurret{
|
||||
|
||||
protected void updateCooling(){
|
||||
if(reloadCounter < reload && coolant != null && coolant.efficiency(this) > 0 && efficiency > 0){
|
||||
float capacity = coolant instanceof ConsumeLiquidFilter filter ? filter.getConsumed(this).heatCapacity : 1f;
|
||||
float capacity = coolant instanceof ConsumeLiquidFilter filter ? filter.getConsumed(this).heatCapacity : (coolant.consumes(liquids.current()) ? liquids.current().heatCapacity : 0.4f);
|
||||
float amount = coolant.amount * coolant.efficiency(this);
|
||||
coolant.update(this);
|
||||
reloadCounter += amount * edelta() * capacity * coolantMultiplier;
|
||||
reloadCounter += amount * edelta() * capacity * coolantMultiplier * ammoReloadMultiplier();
|
||||
|
||||
if(Mathf.chance(0.06 * amount)){
|
||||
coolEffect.at(x + Mathf.range(size * tilesize / 2f), y + Mathf.range(size * tilesize / 2f));
|
||||
@@ -57,6 +38,10 @@ public class ReloadTurret extends BaseTurret{
|
||||
}
|
||||
}
|
||||
|
||||
protected float ammoReloadMultiplier(){
|
||||
return 1f;
|
||||
}
|
||||
|
||||
protected float baseReloadSpeed(){
|
||||
return efficiency;
|
||||
}
|
||||
|
||||
@@ -117,7 +117,7 @@ public class TractorBeamTurret extends BaseTurret{
|
||||
//shoot when possible
|
||||
if(Angles.within(rotation, dest, shootCone)){
|
||||
if(damage > 0){
|
||||
target.damageContinuous(damage * eff * state.rules.blockDamage(team));
|
||||
target.damageContinuous(damage * eff * timeScale * state.rules.blockDamage(team));
|
||||
}
|
||||
|
||||
if(status != StatusEffects.none){
|
||||
|
||||
@@ -10,6 +10,7 @@ import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.audio.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.entities.*;
|
||||
@@ -54,22 +55,28 @@ public class Turret extends ReloadTurret{
|
||||
public float inaccuracy = 0f;
|
||||
/** Fraction of bullet velocity that is random. */
|
||||
public float velocityRnd = 0f;
|
||||
/** Fraction of lifetime that is added to bullets with lifeScale. */
|
||||
public float scaleLifetimeOffset = 0f;
|
||||
/** Maximum angle difference in degrees at which turret will still try to shoot. */
|
||||
public float shootCone = 8f;
|
||||
/** Turret shoot point. */
|
||||
public float shootX = 0f, shootY = Float.NEGATIVE_INFINITY;
|
||||
/** Random spread on the X axis. */
|
||||
public float xRand = 0f;
|
||||
/** If true, a range ring is also drawn for minRange. */
|
||||
public boolean drawMinRange;
|
||||
/** Range at which it finds and locks on to the target, but does not shoot. */
|
||||
public float trackingRange = 0f;
|
||||
/** Minimum bullet range. Used for artillery only. */
|
||||
public float minRange = 0f;
|
||||
/** Minimum warmup needed to fire. */
|
||||
public float minWarmup = 0f;
|
||||
/** If true, this turret will accurately target moving targets with respect to charge time. */
|
||||
/** If true, this turret will accurately target moving targets with respect to shoot.firstShotDelay. */
|
||||
public boolean accurateDelay = true;
|
||||
/** If false, this turret can't move while charging. */
|
||||
public boolean moveWhileCharging = true;
|
||||
/** If false, this turret can't reload while charging */
|
||||
public boolean reloadWhileCharging = true;
|
||||
/** How long warmup is maintained even if this turret isn't shooting. */
|
||||
public float warmupMaintainTime = 0f;
|
||||
/** pattern used for bullets */
|
||||
@@ -112,6 +119,10 @@ public class Turret extends ReloadTurret{
|
||||
public Sound shootSound = Sounds.shoot;
|
||||
/** Sound emitted when shoot.firstShotDelay is >0 and shooting begins. */
|
||||
public Sound chargeSound = Sounds.none;
|
||||
/** The sound that this block makes while active. One sound loop. Do not overuse. */
|
||||
public Sound loopSound = Sounds.none;
|
||||
/** Active sound base volume. */
|
||||
public float loopSoundVolume = 0.5f;
|
||||
/** Range for pitch of shoot sound. */
|
||||
public float soundPitchMin = 0.9f, soundPitchMax = 1.1f;
|
||||
/** Backwards Y offset of ammo eject effect. */
|
||||
@@ -157,7 +168,7 @@ public class Turret extends ReloadTurret{
|
||||
super.setStats();
|
||||
|
||||
stats.add(Stat.inaccuracy, (int)inaccuracy, StatUnit.degrees);
|
||||
stats.add(Stat.reload, 60f / (reload) * shoot.shots, StatUnit.perSecond);
|
||||
stats.add(Stat.reload, 60f / (reload + (!reloadWhileCharging ? shoot.firstShotDelay : 0f)) * shoot.shots, StatUnit.perSecond);
|
||||
stats.add(Stat.targetsAir, targetAir);
|
||||
stats.add(Stat.targetsGround, targetGround);
|
||||
if(ammoPerShot != 1) stats.add(Stat.ammoUse, ammoPerShot, StatUnit.perShot);
|
||||
@@ -185,6 +196,10 @@ public class Turret extends ReloadTurret{
|
||||
if(cooldownTime < 0f) cooldownTime = reload;
|
||||
if(newTargetInterval <= 0f) newTargetInterval = targetInterval;
|
||||
|
||||
if(!targetGround){
|
||||
disableOverlapCheck = true;
|
||||
}
|
||||
|
||||
super.init();
|
||||
trackingRange = Math.max(range, trackingRange);
|
||||
}
|
||||
@@ -209,7 +224,16 @@ public class Turret extends ReloadTurret{
|
||||
public void limitRange(BulletType bullet, float margin){
|
||||
float realRange = bullet.rangeChange + range;
|
||||
//doesn't handle drag
|
||||
bullet.lifetime = (realRange + margin) / bullet.speed;
|
||||
bullet.lifetime = (realRange + margin + bullet.extraRangeMargin) / bullet.speed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawPlace(int x, int y, int rotation, boolean valid){
|
||||
super.drawPlace(x, y, rotation, valid);
|
||||
|
||||
if(drawMinRange){
|
||||
Drawf.dashCircle(x * tilesize + offset, y * tilesize + offset, minRange, Pal.placing);
|
||||
}
|
||||
}
|
||||
|
||||
public static abstract class AmmoEntry{
|
||||
@@ -239,14 +263,39 @@ public class Turret extends ReloadTurret{
|
||||
public float heatReq;
|
||||
public float[] sideHeat = new float[4];
|
||||
|
||||
public @Nullable SoundLoop soundLoop = (loopSound == Sounds.none ? null : new SoundLoop(loopSound, loopSoundVolume));
|
||||
|
||||
float lastRangeChange;
|
||||
|
||||
@Override
|
||||
public void remove(){
|
||||
super.remove();
|
||||
if(soundLoop != null){
|
||||
soundLoop.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyed(){
|
||||
super.onDestroyed();
|
||||
if(soundLoop != null){
|
||||
soundLoop.stop();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float estimateDps(){
|
||||
if(!hasAmmo()) return 0f;
|
||||
return shoot.shots / reload * 60f * (peekAmmo() == null ? 0f : peekAmmo().estimateDPS()) * potentialEfficiency * timeScale;
|
||||
}
|
||||
|
||||
public float minRange(){
|
||||
if(peekAmmo() != null){
|
||||
return minRange + peekAmmo().minRangeChange;
|
||||
}
|
||||
return minRange;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float range(){
|
||||
if(peekAmmo() != null){
|
||||
@@ -359,8 +408,8 @@ public class Turret extends ReloadTurret{
|
||||
offset.set(h.deltaX(), h.deltaY()).scl(shoot.firstShotDelay / Time.delta);
|
||||
}
|
||||
|
||||
if(predictTarget){
|
||||
targetPos.set(Predict.intercept(this, pos, offset.x, offset.y, bullet.speed <= 0.01f ? 99999999f : bullet.speed));
|
||||
if(predictTarget && bullet.speed >= 0.01f){
|
||||
targetPos.set(Predict.intercept(this, pos, offset.x, offset.y, bullet.speed));
|
||||
}else{
|
||||
targetPos.set(pos);
|
||||
}
|
||||
@@ -375,10 +424,23 @@ public class Turret extends ReloadTurret{
|
||||
drawer.draw(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawSelect(){
|
||||
super.drawSelect();
|
||||
|
||||
if(drawMinRange){
|
||||
Drawf.dashCircle(x, y, minRange(), team.color);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
if(!validateTarget()) target = null;
|
||||
|
||||
if(soundLoop != null){
|
||||
soundLoop.update(x, y, shouldActiveSound(), activeSoundVolume());
|
||||
}
|
||||
|
||||
float warmupTarget = (isShooting() && canConsume()) || charging() ? 1f : 0f;
|
||||
if(warmupTarget > 0 && !isControlled()){
|
||||
warmupHold = 1f;
|
||||
@@ -420,7 +482,10 @@ public class Turret extends ReloadTurret{
|
||||
}
|
||||
|
||||
//turret always reloads regardless of whether it's targeting something
|
||||
updateReload();
|
||||
if(reloadWhileCharging || !charging()){
|
||||
updateReload();
|
||||
updateCooling();
|
||||
}
|
||||
|
||||
if(state.rules.fog){
|
||||
float newRange = hasAmmo() ? peekAmmo().rangeChange : 0f;
|
||||
@@ -476,10 +541,6 @@ public class Turret extends ReloadTurret{
|
||||
updateShooting();
|
||||
}
|
||||
}
|
||||
|
||||
if(coolant != null){
|
||||
updateCooling();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -559,14 +620,14 @@ public class Turret extends ReloadTurret{
|
||||
|
||||
/** @return whether the turret has ammo. */
|
||||
public boolean hasAmmo(){
|
||||
//used for "side-ammo" like gas in some turrets
|
||||
if(!canConsume()) return false;
|
||||
|
||||
//skip first entry if it has less than the required amount of ammo
|
||||
if(ammo.size >= 2 && ammo.peek().amount < ammoPerShot && ammo.get(ammo.size - 2).amount >= ammoPerShot){
|
||||
ammo.swap(ammo.size - 1, ammo.size - 2);
|
||||
}
|
||||
|
||||
//used for "side-ammo" like gas in some turrets
|
||||
if(!canConsume()) return false;
|
||||
|
||||
return ammo.size > 0 && (ammo.peek().amount >= ammoPerShot || cheating());
|
||||
}
|
||||
|
||||
@@ -575,13 +636,17 @@ public class Turret extends ReloadTurret{
|
||||
}
|
||||
|
||||
protected void updateReload(){
|
||||
float multiplier = hasAmmo() ? peekAmmo().reloadMultiplier : 1f;
|
||||
reloadCounter += delta() * multiplier * baseReloadSpeed();
|
||||
reloadCounter += delta() * ammoReloadMultiplier() * baseReloadSpeed();
|
||||
|
||||
//cap reload for visual reasons
|
||||
reloadCounter = Math.min(reloadCounter, reload);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float ammoReloadMultiplier(){
|
||||
return hasAmmo() ? peekAmmo().reloadMultiplier : 1f;
|
||||
}
|
||||
|
||||
protected void updateShooting(){
|
||||
|
||||
if(reloadCounter >= reload && !charging() && shootWarmup >= minWarmup){
|
||||
@@ -636,7 +701,7 @@ public class Turret extends ReloadTurret{
|
||||
bulletY = y + Angles.trnsy(rotation - 90, shootX + xOffset + xSpread, shootY + yOffset),
|
||||
shootAngle = rotation + angleOffset + Mathf.range(inaccuracy + type.inaccuracy);
|
||||
|
||||
float lifeScl = type.scaleLife ? Mathf.clamp(Mathf.dst(bulletX, bulletY, targetPos.x, targetPos.y) / type.range, minRange / type.range, range() / type.range) : 1f;
|
||||
float lifeScl = type.scaleLife ? Mathf.clamp((1 + scaleLifetimeOffset) * Mathf.dst(bulletX, bulletY, targetPos.x, targetPos.y) / type.range, minRange() / type.range, range() / type.range) : 1f;
|
||||
|
||||
//TODO aimX / aimY for multi shot turrets?
|
||||
handleBullet(type.create(this, team, bulletX, bulletY, shootAngle, -1f, (1f - velocityRnd) + Mathf.random(velocityRnd), lifeScl, null, mover, targetPos.x, targetPos.y), xOffset, yOffset, shootAngle - rotation);
|
||||
@@ -671,12 +736,10 @@ public class Turret extends ReloadTurret{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public float activeSoundVolume(){
|
||||
return shootWarmup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldActiveSound(){
|
||||
return shootWarmup > 0.01f && loopSound != Sounds.none;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public class ArmoredConveyor extends Conveyor{
|
||||
public class ArmoredConveyorBuild extends ConveyorBuild{
|
||||
@Override
|
||||
public boolean acceptItem(Building source, Item item){
|
||||
return super.acceptItem(source, item) && (source.block instanceof Conveyor || Edges.getFacingEdge(source.tile(), tile).relativeTo(tile) == rotation);
|
||||
return super.acceptItem(source, item) && (source.block instanceof Conveyor || Edges.getFacingEdge(source.tile, tile).relativeTo(tile) == rotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ public class Conveyor extends Block implements Autotiler{
|
||||
super.init();
|
||||
|
||||
if(junctionReplacement == null) junctionReplacement = Blocks.junction;
|
||||
if(bridgeReplacement == null || !(bridgeReplacement instanceof ItemBridge)) bridgeReplacement = Blocks.itemBridge;
|
||||
if(bridgeReplacement == null || !(bridgeReplacement instanceof ItemBridge || bridgeReplacement instanceof DuctBridge)) bridgeReplacement = Blocks.itemBridge;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -92,8 +92,9 @@ public class Conveyor extends Block implements Autotiler{
|
||||
@Override
|
||||
public void handlePlacementLine(Seq<BuildPlan> plans){
|
||||
if(bridgeReplacement == null) return;
|
||||
|
||||
Placement.calculateBridges(plans, (ItemBridge)bridgeReplacement, b -> b instanceof Conveyor);
|
||||
boolean hasJuntionReplacement = junctionReplacement != null;
|
||||
if(bridgeReplacement instanceof DuctBridge bridge) Placement.calculateBridges(plans, bridge, hasJuntionReplacement, b -> b instanceof Duct || b instanceof Conveyor);
|
||||
if(bridgeReplacement instanceof ItemBridge bridge) Placement.calculateBridges(plans, bridge, hasJuntionReplacement, b -> b instanceof Conveyor);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -128,6 +128,12 @@ public class DirectionalUnloader extends Block{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawSelect(){
|
||||
super.drawSelect();
|
||||
drawItemSelection(unloadItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildConfiguration(Table table){
|
||||
ItemSelection.buildTable(DirectionalUnloader.this, table, content.items(), () -> unloadItem, this::configure);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package mindustry.world.blocks.distribution;
|
||||
|
||||
import arc.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
@@ -30,7 +31,7 @@ public class Duct extends Block implements Autotiler{
|
||||
public @Load(value = "@-top-#", length = 5) TextureRegion[] topRegions;
|
||||
public @Load(value = "@-bottom-#", length = 5, fallback = "duct-bottom-#") TextureRegion[] botRegions;
|
||||
|
||||
public @Nullable Block bridgeReplacement;
|
||||
public @Nullable Block bridgeReplacement, junctionReplacement;
|
||||
|
||||
public Duct(String name){
|
||||
super(name);
|
||||
@@ -62,7 +63,8 @@ public class Duct extends Block implements Autotiler{
|
||||
public void init(){
|
||||
super.init();
|
||||
|
||||
if(bridgeReplacement == null || !(bridgeReplacement instanceof DuctBridge)) bridgeReplacement = Blocks.ductBridge;
|
||||
if(bridgeReplacement == null || !(bridgeReplacement instanceof DuctBridge || bridgeReplacement instanceof ItemBridge)) bridgeReplacement = Blocks.ductBridge;
|
||||
//if(junctionReplacement == null) junctionReplacement = Blocks.ductJunction;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -103,11 +105,24 @@ public class Duct extends Block implements Autotiler{
|
||||
return new TextureRegion[]{Core.atlas.find("duct-bottom"), topRegions[0]};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Block getReplacement(BuildPlan req, Seq<BuildPlan> plans){
|
||||
if(junctionReplacement == null || !junctionReplacement.unlockedNow() || junctionReplacement.isHidden()) return this;
|
||||
|
||||
Boolf<Point2> cont = p -> plans.contains(o -> o.x == req.x + p.x && o.y == req.y + p.y && (req.block instanceof Duct || req.block instanceof DuctJunction));
|
||||
return cont.get(Geometry.d4(req.rotation)) &&
|
||||
cont.get(Geometry.d4(req.rotation - 2)) &&
|
||||
req.tile() != null &&
|
||||
req.tile().block() instanceof Duct &&
|
||||
Mathf.mod(req.tile().build.rotation - req.rotation, 2) == 1 ? junctionReplacement : this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePlacementLine(Seq<BuildPlan> plans){
|
||||
if(bridgeReplacement == null) return;
|
||||
|
||||
Placement.calculateBridges(plans, (DuctBridge)bridgeReplacement, false, b -> b instanceof Duct || b instanceof StackConveyor || b instanceof Conveyor);
|
||||
boolean hasJunction = junctionReplacement != null && junctionReplacement.unlockedNow() && !junctionReplacement.isHidden();
|
||||
if(bridgeReplacement instanceof ItemBridge bridge) Placement.calculateBridges(plans, bridge, hasJunction, b -> b instanceof Duct || b instanceof StackConveyor || b instanceof Conveyor);
|
||||
if(bridgeReplacement instanceof DuctBridge bridge) Placement.calculateBridges(plans, bridge, hasJunction, b -> b instanceof Duct || b instanceof StackConveyor || b instanceof Conveyor);
|
||||
}
|
||||
|
||||
public class DuctBuild extends Building{
|
||||
@@ -137,7 +152,7 @@ public class Duct extends Block implements Autotiler{
|
||||
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));
|
||||
Mathf.clamp((progress + 1f) / (2f - 1f/speed)));
|
||||
|
||||
Draw.rect(current.fullIcon, x + Tmp.v1.x, y + Tmp.v1.y, itemSize, itemSize);
|
||||
}
|
||||
@@ -188,7 +203,7 @@ public class Duct extends Block implements Autotiler{
|
||||
(armored ?
|
||||
//armored acceptance
|
||||
((source.block.rotate && source.front() == this && source.block.hasItems && source.block.isDuct) ||
|
||||
Edges.getFacingEdge(source.tile(), tile).relativeTo(tile) == rotation) :
|
||||
Edges.getFacingEdge(source.tile, tile).relativeTo(tile) == rotation) :
|
||||
//standard acceptance - do not accept from front
|
||||
!(source.block.rotate && next == source) && Edges.getFacingEdge(source.tile, tile) != null && Math.abs(Edges.getFacingEdge(source.tile, tile).relativeTo(tile.x, tile.y) - rotation) != 2
|
||||
);
|
||||
|
||||
179
core/src/mindustry/world/blocks/distribution/DuctJunction.java
Normal file
179
core/src/mindustry/world/blocks/distribution/DuctJunction.java
Normal file
@@ -0,0 +1,179 @@
|
||||
package mindustry.world.blocks.distribution;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.io.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class DuctJunction extends Block{
|
||||
public Color transparentColor = new Color(0.4f, 0.4f, 0.4f, 0.1f);
|
||||
public @Load("@-bottom") TextureRegion bottomRegion;
|
||||
public @Load("@-top") TextureRegion topRegion;
|
||||
public float speed = 5f;
|
||||
|
||||
public DuctJunction(String name){
|
||||
super(name);
|
||||
update = true;
|
||||
solid = false;
|
||||
underBullets = true;
|
||||
group = BlockGroup.transportation;
|
||||
unloadable = false;
|
||||
floating = true;
|
||||
noUpdateDisabled = true;
|
||||
hasItems = true;
|
||||
|
||||
priority = TargetPriority.transport;
|
||||
envEnabled = Env.space | Env.terrestrial | Env.underwater;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStats(){
|
||||
super.setStats();
|
||||
//4 tems is misleading
|
||||
stats.remove(Stat.itemCapacity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
super.load();
|
||||
squareSprite = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean outputsItems(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
itemCapacity = 4;
|
||||
super.init();
|
||||
}
|
||||
|
||||
public class DuctJunctionBuild extends Building{
|
||||
Item[] itemdata = new Item[4];
|
||||
float[] times = new float[4];
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Draw.z(Layer.blockUnder);
|
||||
Draw.rect(bottomRegion, x, y);
|
||||
|
||||
Draw.z(Layer.blockUnder + 0.1f);
|
||||
|
||||
for(int i = 0; i < 4; i++){
|
||||
Item current = itemdata[i];
|
||||
|
||||
if(current != null){
|
||||
float progress = (Mathf.clamp((times[i] + 1f) / (2f - 1f/speed)) - 0.5f) * 2f;
|
||||
|
||||
Draw.rect(current.fullIcon,
|
||||
x + Geometry.d4x(i) * tilesize / 2f * progress,
|
||||
y + Geometry.d4y(i) * tilesize / 2f * progress,
|
||||
itemSize, itemSize
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Draw.color(transparentColor);
|
||||
Draw.rect(bottomRegion, x, y);
|
||||
Draw.color();
|
||||
|
||||
Draw.z(Layer.blockUnder + 0.2f);
|
||||
|
||||
Draw.rect(topRegion, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
float inc = edelta() / speed * 2f;
|
||||
|
||||
for(int i = 0; i < 4; i++){
|
||||
Item item = itemdata[i];
|
||||
if(item != null){
|
||||
|
||||
times[i] += inc;
|
||||
if(times[i] >= (1f - 1f/speed)){
|
||||
Building next = nearby(i);
|
||||
|
||||
if(next != null && next.team == team && next.acceptItem(this, item)){
|
||||
next.handleItem(this, item);
|
||||
itemdata[i] = null;
|
||||
items.remove(item, 1);
|
||||
times[i] %= (1f - 1f/speed);
|
||||
}
|
||||
}
|
||||
}else{
|
||||
//TODO: reset progress or not?
|
||||
times[i] = 0f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleItem(Building source, Item item){
|
||||
int relative = source.relativeTo(tile);
|
||||
if(relative == -1) return;
|
||||
itemdata[relative] = item;
|
||||
times[relative] = -1f;
|
||||
items.add(item, 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptItem(Building source, Item item){
|
||||
int relative = source.relativeTo(tile);
|
||||
|
||||
if(relative == -1 || itemdata[relative] != null) return false;
|
||||
Building to = nearby(relative);
|
||||
return to != null && to.team == team;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int acceptStack(Item item, int amount, Teamc source){
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int removeStack(Item item, int amount){
|
||||
int removed = 0;
|
||||
for(int i = 0; i < 4 && amount > 0; i++){
|
||||
if(itemdata[i] == item){
|
||||
amount --;
|
||||
removed ++;
|
||||
itemdata[i] = null;
|
||||
items.remove(item, 1);
|
||||
}
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Writes write){
|
||||
super.write(write);
|
||||
for(int i = 0; i < 4; i++){
|
||||
write.f(times[i]);
|
||||
TypeIO.writeItem(write, itemdata[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(Reads read, byte revision){
|
||||
super.read(read, revision);
|
||||
for(int i = 0; i < 4; i++){
|
||||
times[i] = read.f();
|
||||
itemdata[i] = TypeIO.readItem(read);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -89,6 +89,12 @@ public class DuctRouter extends Block{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawSelect(){
|
||||
super.drawSelect();
|
||||
drawItemSelection(sortItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
progress += edelta() / speed * 2f;
|
||||
@@ -141,7 +147,7 @@ public class DuctRouter extends Block{
|
||||
@Override
|
||||
public boolean acceptItem(Building source, Item item){
|
||||
return current == null && items.total() == 0 &&
|
||||
(Edges.getFacingEdge(source.tile(), tile).relativeTo(tile) == rotation);
|
||||
(Edges.getFacingEdge(source.tile, tile).relativeTo(tile) == rotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -79,6 +79,11 @@ public class Junction extends Block{
|
||||
return to != null && to.team == team;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte version(){
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Writes write){
|
||||
super.write(write);
|
||||
@@ -88,7 +93,7 @@ public class Junction extends Block{
|
||||
@Override
|
||||
public void read(Reads read, byte revision){
|
||||
super.read(read, revision);
|
||||
buffer.read(read);
|
||||
buffer.read(read, revision == 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -248,7 +248,7 @@ public class MassDriver extends Block{
|
||||
|
||||
if(linkValid()){
|
||||
Building target = world.build(link);
|
||||
Drawf.circles(target.x, target.y, (target.block().size / 2f + 1) * tilesize + sin - 2f, Pal.place);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ public class OverflowDuct extends Block{
|
||||
@Override
|
||||
public boolean acceptItem(Building source, Item item){
|
||||
return current == null && items.total() == 0 &&
|
||||
(Edges.getFacingEdge(source.tile(), tile).relativeTo(tile) == rotation);
|
||||
(Edges.getFacingEdge(source.tile, tile).relativeTo(tile) == rotation);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -83,7 +83,7 @@ public class Router extends Block{
|
||||
items.add(item, 1);
|
||||
lastItem = item;
|
||||
time = 0f;
|
||||
lastInput = source.tile();
|
||||
lastInput = source.tile;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -83,6 +83,12 @@ public class Sorter extends Block{
|
||||
super.draw();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawSelect(){
|
||||
super.drawSelect();
|
||||
drawItemSelection(sortItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptItem(Building source, Item item){
|
||||
Building to = getTileTarget(item, source, false);
|
||||
|
||||
@@ -85,7 +85,7 @@ public class StackRouter extends DuctRouter{
|
||||
@Override
|
||||
public boolean acceptItem(Building source, Item item){
|
||||
return !unloading && (current == null || item == current) && items.total() < itemCapacity &&
|
||||
(Edges.getFacingEdge(source.tile(), tile).relativeTo(tile) == rotation);
|
||||
(Edges.getFacingEdge(source.tile, tile).relativeTo(tile) == rotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,6 +213,11 @@ public class Floor extends Block{
|
||||
return new TextureRegion[]{Core.atlas.find(Core.atlas.has(name) ? name : name + "1")};
|
||||
}
|
||||
|
||||
/** @return whether to index this floor by flag */
|
||||
public boolean shouldIndex(Tile tile){
|
||||
return true;
|
||||
}
|
||||
|
||||
//TODO currently broken for dynamically edited floor tiles
|
||||
/** @return true if this floor should be updated in the render loop, e.g. for effects. Do NOT overuse this! */
|
||||
public boolean updateRender(Tile tile){
|
||||
@@ -319,11 +324,6 @@ public class Floor extends Block{
|
||||
return edges(x, y)[rx][2 - ry];
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
protected TextureRegion[][] edges(){
|
||||
return edges(0, 0);
|
||||
}
|
||||
|
||||
/** @return whether the edges from {@param other} should be drawn onto this tile **/
|
||||
protected boolean doEdge(Tile tile, Tile otherTile, Floor other){
|
||||
return (other.realBlendId(otherTile) > realBlendId(tile) || edges(tile.x, tile.y) == null);
|
||||
|
||||
@@ -16,6 +16,7 @@ public class Prop extends Block{
|
||||
breakable = true;
|
||||
alwaysReplace = true;
|
||||
instantDeconstruct = true;
|
||||
unitMoveBreakable = true;
|
||||
breakEffect = Fx.breakProp;
|
||||
breakSound = Sounds.rockBreak;
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ public class SeaBush extends Prop{
|
||||
|
||||
@Override
|
||||
public void drawBase(Tile tile){
|
||||
Draw.z(layer);
|
||||
rand.setSeed(tile.pos());
|
||||
float offset = rand.random(180f);
|
||||
int lobes = rand.random(lobesMin, lobesMax);
|
||||
|
||||
@@ -17,7 +17,7 @@ public class StaticWall extends Prop{
|
||||
|
||||
public StaticWall(String name){
|
||||
super(name);
|
||||
breakable = alwaysReplace = false;
|
||||
breakable = alwaysReplace = unitMoveBreakable = false;
|
||||
solid = true;
|
||||
variants = 2;
|
||||
cacheLayer = CacheLayer.walls;
|
||||
|
||||
@@ -4,12 +4,14 @@ import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@@ -41,6 +43,7 @@ public class SteamVent extends Floor{
|
||||
public SteamVent(String name){
|
||||
super(name);
|
||||
variants = 2;
|
||||
flags = EnumSet.of(BlockFlag.steamVent);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -58,6 +61,16 @@ public class SteamVent extends Floor{
|
||||
return checkAdjacent(tile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldIndex(Tile tile){
|
||||
return isCenterVent(tile);
|
||||
}
|
||||
|
||||
public boolean isCenterVent(Tile tile){
|
||||
Tile topRight = tile.nearby(1, 1);
|
||||
return topRight != null && topRight.floor() == tile.floor() && checkAdjacent(topRight);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void renderUpdate(UpdateRenderState state){
|
||||
if(state.tile.nearby(-1, -1) != null && state.tile.nearby(-1, -1).block() == Blocks.air && (state.data += Time.delta) >= effectSpacing){
|
||||
@@ -66,6 +79,7 @@ public class SteamVent extends Floor{
|
||||
}
|
||||
}
|
||||
|
||||
//note that only the top right tile works for this; render order reasons.
|
||||
public boolean checkAdjacent(Tile tile){
|
||||
for(var point : offsets){
|
||||
Tile other = Vars.world.tile(tile.x + point.x, tile.y + point.y);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package mindustry.world.blocks.heat;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.ui.*;
|
||||
@@ -20,6 +21,8 @@ public class HeatProducer extends GenericCrafter{
|
||||
rotate = true;
|
||||
canOverdrive = false;
|
||||
drawArrow = true;
|
||||
//it doesn't count as a standard crafter
|
||||
flags = EnumSet.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -140,7 +140,7 @@ public class Conduit extends LiquidBlock implements Autotiler{
|
||||
if(rotBridgeReplacement instanceof DirectionBridge duct){
|
||||
Placement.calculateBridges(plans, duct, true, b -> b instanceof Conduit);
|
||||
}else{
|
||||
Placement.calculateBridges(plans, (ItemBridge)bridgeReplacement, b -> b instanceof Conduit);
|
||||
Placement.calculateBridges(plans, (ItemBridge)bridgeReplacement, true, b -> b instanceof Conduit);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,9 +23,7 @@ public class LiquidRouter extends LiquidBlock{
|
||||
public class LiquidRouterBuild extends LiquidBuild{
|
||||
@Override
|
||||
public void updateTile(){
|
||||
if(liquids.currentAmount() > 0.01f){
|
||||
dumpLiquid(liquids.current());
|
||||
}
|
||||
dumpLiquid(liquids.current());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -539,7 +539,7 @@ public class LogicBlock extends Block{
|
||||
|
||||
if(accumulator > maxInstructionScale * ipt) accumulator = maxInstructionScale * ipt;
|
||||
|
||||
for(int i = 0; i < (int)accumulator; i++){
|
||||
while(accumulator >= 1f){
|
||||
executor.runOnce();
|
||||
accumulator --;
|
||||
if(executor.yield){
|
||||
|
||||
@@ -2,6 +2,7 @@ package mindustry.world.blocks.logic;
|
||||
|
||||
import arc.util.io.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.logic.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
@@ -55,6 +56,14 @@ public class MemoryBlock extends Block{
|
||||
return accessible();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double sense(LAccess sensor){
|
||||
return switch(sensor){
|
||||
case memoryCapacity -> memoryCapacity;
|
||||
default -> super.sense(sensor);
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void damage(float damage){
|
||||
if(privileged) return;
|
||||
|
||||
@@ -12,6 +12,7 @@ import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import arc.util.pooling.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.ui.dialogs.*;
|
||||
@@ -79,7 +80,7 @@ public class MessageBlock extends Block{
|
||||
font.getData().setScale(1 / 4f / Scl.scl(1f));
|
||||
font.setUseIntegerPositions(false);
|
||||
|
||||
CharSequence text = message == null || message.length() == 0 ? "[lightgray]" + Core.bundle.get("empty") : message;
|
||||
String text = message == null || message.length() == 0 ? "[lightgray]" + Core.bundle.get("empty") : UI.formatIcons(message.toString());
|
||||
|
||||
l.setText(font, text, Color.white, 90f, Align.left, true);
|
||||
float offset = 1f;
|
||||
@@ -87,7 +88,7 @@ public class MessageBlock extends Block{
|
||||
Draw.color(0f, 0f, 0f, 0.2f);
|
||||
Fill.rect(x, y - tilesize/2f - l.height/2f - offset, l.width + offset*2f, l.height + offset*2f);
|
||||
Draw.color();
|
||||
font.setColor(Color.white);
|
||||
font.setColor(message.length() == 0 ? Color.lightGray : Color.white);
|
||||
font.draw(text, x - l.width/2f, y - tilesize/2f - offset, 90f, Align.left, true);
|
||||
font.setUseIntegerPositions(ints);
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@ public abstract class BlockProducer extends PayloadBlock{
|
||||
public void setBars(){
|
||||
super.setBars();
|
||||
|
||||
addBar("progress", (BlockProducerBuild entity) -> new Bar("bar.progress", Pal.ammo, () -> entity.recipe() == null ? 0f : (entity.progress / entity.recipe().buildCost)));
|
||||
addBar("progress", (BlockProducerBuild entity) -> new Bar("bar.progress", Pal.ammo, () -> entity.recipe() == null ? 0f : (entity.progress / entity.recipe().buildTime)));
|
||||
}
|
||||
|
||||
public abstract class BlockProducerBuild extends PayloadBlockBuild<BuildPayload>{
|
||||
@@ -108,7 +108,7 @@ public abstract class BlockProducer extends PayloadBlock{
|
||||
if(produce){
|
||||
progress += buildSpeed * edelta();
|
||||
|
||||
if(progress >= recipe.buildCost){
|
||||
if(progress >= recipe.buildTime){
|
||||
consume();
|
||||
payload = new BuildPayload(recipe, team);
|
||||
payload.block().placeEffect.at(x, y, payload.size() / tilesize);
|
||||
@@ -130,14 +130,14 @@ public abstract class BlockProducer extends PayloadBlock{
|
||||
|
||||
var recipe = recipe();
|
||||
if(recipe != null){
|
||||
Drawf.shadow(x, y, recipe.size * tilesize * 2f, progress / recipe.buildCost);
|
||||
Drawf.shadow(x, y, recipe.size * tilesize * 2f, progress / recipe.buildTime);
|
||||
Draw.draw(Layer.blockBuilding, () -> {
|
||||
Draw.color(Pal.accent);
|
||||
|
||||
for(TextureRegion region : recipe.getGeneratedIcons()){
|
||||
Shaders.blockbuild.region = region;
|
||||
Shaders.blockbuild.time = time;
|
||||
Shaders.blockbuild.progress = progress / recipe.buildCost;
|
||||
Shaders.blockbuild.progress = progress / recipe.buildTime;
|
||||
|
||||
Draw.rect(region, x, y, recipe.rotate ? rotdeg() : 0);
|
||||
Draw.flush();
|
||||
|
||||
@@ -64,7 +64,7 @@ public class BuildPayload implements Payload{
|
||||
|
||||
@Override
|
||||
public float buildTime(){
|
||||
return build.block.buildCost;
|
||||
return build.block.buildTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -82,6 +82,11 @@ public class BuildPayload implements Payload{
|
||||
return build.block.size * tilesize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(){
|
||||
build.remove();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Writes write){
|
||||
write.b(payloadBlock);
|
||||
|
||||
@@ -75,6 +75,8 @@ public interface Payload extends Position{
|
||||
return y();
|
||||
}
|
||||
|
||||
default void remove(){}
|
||||
|
||||
static void write(@Nullable Payload payload, Writes write){
|
||||
if(payload == null){
|
||||
write.bool(false);
|
||||
|
||||
@@ -6,6 +6,7 @@ import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.world.*;
|
||||
@@ -206,7 +207,7 @@ public class PayloadBlock extends Block{
|
||||
payVector.approach(dest, payloadSpeed * delta());
|
||||
|
||||
Building front = front();
|
||||
boolean canDump = front == null || !front.tile().solid();
|
||||
boolean canDump = front == null || !front.tile.solid();
|
||||
boolean canMove = front != null && (front.block.outputsPayload || front.block.acceptsPayload);
|
||||
|
||||
if(canDump && !canMove){
|
||||
@@ -251,6 +252,13 @@ public class PayloadBlock extends Block{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public double sense(Content content){
|
||||
if(payload instanceof UnitPayload up && up.unit.type == content) return 1;
|
||||
if(payload instanceof BuildPayload bp && bp.build.block == content) return 1;
|
||||
return super.sense(content);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Writes write){
|
||||
super.write(write);
|
||||
|
||||
@@ -7,6 +7,7 @@ import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.ctype.Content;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
@@ -47,8 +48,10 @@ public class PayloadConveyor extends Block{
|
||||
public void drawPlace(int x, int y, int rotation, boolean valid){
|
||||
super.drawPlace(x, y, rotation, valid);
|
||||
|
||||
int ntrns = 1 + size/2;
|
||||
|
||||
for(int i = 0; i < 4; i++){
|
||||
Building other = world.build(x + Geometry.d4x[i] * size, y + Geometry.d4y[i] * size);
|
||||
Building other = world.build(x + Geometry.d4x[i] * ntrns, y + Geometry.d4y[i] * ntrns);
|
||||
if(other != null && other.block.outputsPayload && other.block.size == size){
|
||||
Drawf.selected(other.tileX(), other.tileY(), other.block, other.team.color);
|
||||
}
|
||||
@@ -283,6 +286,13 @@ public class PayloadConveyor extends Block{
|
||||
updatePayload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double sense(Content content){
|
||||
if(item instanceof UnitPayload up && up.unit.type == content) return 1;
|
||||
if(item instanceof BuildPayload bp && bp.build.block == content) return 1;
|
||||
return super.sense(content);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoved(){
|
||||
super.onRemoved();
|
||||
|
||||
@@ -5,6 +5,7 @@ import arc.math.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
@@ -201,6 +202,13 @@ public class PayloadDeconstructor extends PayloadBlock{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public double sense(Content content){
|
||||
if(deconstructing instanceof UnitPayload up) return up.unit.type == content ? 1 : 0;
|
||||
if(deconstructing instanceof BuildPayload bp) return bp.build.block == content ? 1 : 0;
|
||||
return super.sense(content);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double sense(LAccess sensor){
|
||||
if(sensor == LAccess.progress) return progress;
|
||||
|
||||
@@ -94,7 +94,7 @@ public class PayloadLoader extends PayloadBlock{
|
||||
//item container
|
||||
(build.build.block.hasItems && build.block().unloadable && build.block().itemCapacity >= 10 && build.block().size <= maxBlockSize) ||
|
||||
//liquid container
|
||||
(build.build.block().hasLiquids && build.block().liquidCapacity >= 10f) ||
|
||||
(build.build.block.hasLiquids && build.block().liquidCapacity >= 10f) ||
|
||||
//battery
|
||||
(build.build.block.consPower != null && build.build.block.consPower.buffered)
|
||||
);
|
||||
|
||||
@@ -22,7 +22,7 @@ import static mindustry.world.blocks.payloads.PayloadMassDriver.PayloadDriverSta
|
||||
|
||||
public class PayloadMassDriver extends PayloadBlock{
|
||||
public float range = 100f;
|
||||
public float rotateSpeed = 2f;
|
||||
public float rotateSpeed = 5f;
|
||||
public float length = 89 / 8f;
|
||||
public float knockback = 5f;
|
||||
public float reload = 30f;
|
||||
@@ -72,7 +72,7 @@ public class PayloadMassDriver extends PayloadBlock{
|
||||
@Override
|
||||
public void init(){
|
||||
super.init();
|
||||
updateClipRadius(range);
|
||||
updateClipRadius(range + 4f);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -430,7 +430,7 @@ public class PayloadMassDriver extends PayloadBlock{
|
||||
|
||||
if(linkValid()){
|
||||
Building target = world.build(link);
|
||||
Drawf.circles(target.x, target.y, (target.block().size / 2f + 1) * tilesize + sin - 2f, Pal.place);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package mindustry.world.blocks.payloads;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
@@ -158,13 +157,7 @@ public class PayloadRouter extends PayloadConveyor{
|
||||
|
||||
@Override
|
||||
public void drawSelect(){
|
||||
if(sorted != null){
|
||||
float dx = x - size * tilesize/2f, dy = y + size * tilesize/2f, s = iconSmall / 4f;
|
||||
Draw.mixcol(Color.darkGray, 1f);
|
||||
Draw.rect(sorted.fullIcon, dx, dy - 1, s, s);
|
||||
Draw.reset();
|
||||
Draw.rect(sorted.fullIcon, dx, dy, s, s);
|
||||
}
|
||||
drawItemSelection(sorted);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -118,7 +118,7 @@ public class UnitPayload implements Payload{
|
||||
}
|
||||
|
||||
//cannot 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){
|
||||
if(!unit.type.flying && Units.count(unit.x, unit.y, unit.physicSize() * 1.05f, o -> o.isGrounded() && (o.type.allowLegStep == unit.type.allowLegStep)) > 0){
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -149,6 +149,7 @@ public class UnitPayload implements Payload{
|
||||
|
||||
float e = unit.elevation;
|
||||
unit.elevation = 0f;
|
||||
Draw.scl(1f, 1f);
|
||||
unit.type.draw(unit);
|
||||
unit.elevation = e;
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.draw.*;
|
||||
@@ -17,9 +16,6 @@ public class Battery extends PowerDistributor{
|
||||
public Color emptyLightColor = Color.valueOf("f8c266");
|
||||
public Color fullLightColor = Color.valueOf("fb9567");
|
||||
|
||||
@Deprecated
|
||||
public @Load("@-top") TextureRegion topRegion;
|
||||
|
||||
public Battery(String name){
|
||||
super(name);
|
||||
outputsPower = true;
|
||||
|
||||
@@ -3,6 +3,7 @@ package mindustry.world.blocks.power;
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.math.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
@@ -21,6 +22,7 @@ public class ConsumeGenerator extends PowerGenerator{
|
||||
public float effectChance = 0.01f;
|
||||
public Effect generateEffect = Fx.none, consumeEffect = Fx.none;
|
||||
public float generateEffectRange = 3f;
|
||||
public float baseLightRadius = 65f;
|
||||
|
||||
public @Nullable LiquidStack outputLiquid;
|
||||
/** If true, this block explodes when outputLiquid exceeds capacity. */
|
||||
@@ -28,6 +30,8 @@ public class ConsumeGenerator extends PowerGenerator{
|
||||
|
||||
public @Nullable ConsumeItemFilter filterItem;
|
||||
public @Nullable ConsumeLiquidFilter filterLiquid;
|
||||
/** Multiplies the itemDuration for a given item. */
|
||||
public ObjectFloatMap<Item> itemDurationMultipliers = new ObjectFloatMap<>();
|
||||
|
||||
public ConsumeGenerator(String name){
|
||||
super(name);
|
||||
@@ -47,6 +51,11 @@ public class ConsumeGenerator extends PowerGenerator{
|
||||
filterItem = findConsumer(c -> c instanceof ConsumeItemFilter);
|
||||
filterLiquid = findConsumer(c -> c instanceof ConsumeLiquidFilter);
|
||||
|
||||
//pass along the duration multipliers to the consumer, so it can display them properly
|
||||
if(filterItem instanceof ConsumeItemEfficiency eff){
|
||||
eff.itemDurationMultipliers = itemDurationMultipliers;
|
||||
}
|
||||
|
||||
if(outputLiquid != null){
|
||||
outputsLiquid = true;
|
||||
hasLiquids = true;
|
||||
@@ -56,14 +65,14 @@ public class ConsumeGenerator extends PowerGenerator{
|
||||
explosionPuddleLiquid = outputLiquid.liquid;
|
||||
}
|
||||
|
||||
//TODO hardcoded
|
||||
emitLight = true;
|
||||
lightRadius = 65f * size;
|
||||
lightRadius = baseLightRadius * size;
|
||||
super.init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStats(){
|
||||
stats.timePeriod = itemDuration;
|
||||
super.setStats();
|
||||
|
||||
if(hasItems){
|
||||
@@ -76,16 +85,18 @@ public class ConsumeGenerator extends PowerGenerator{
|
||||
}
|
||||
|
||||
public class ConsumeGeneratorBuild extends GeneratorBuild{
|
||||
public float warmup, totalTime, efficiencyMultiplier = 1f;
|
||||
public float warmup, totalTime, efficiencyMultiplier = 1f, itemDurationMultiplier = 1;
|
||||
|
||||
@Override
|
||||
public void updateEfficiencyMultiplier(){
|
||||
efficiencyMultiplier = 1f;
|
||||
if(filterItem != null){
|
||||
float m = filterItem.efficiencyMultiplier(this);
|
||||
if(m > 0) efficiencyMultiplier = m;
|
||||
}else if(filterLiquid != null){
|
||||
if(m > 0) efficiencyMultiplier *= m;
|
||||
}
|
||||
if(filterLiquid != null){
|
||||
float m = filterLiquid.efficiencyMultiplier(this);
|
||||
if(m > 0) efficiencyMultiplier = m;
|
||||
if(m > 0) efficiencyMultiplier *= m;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,6 +114,11 @@ public class ConsumeGenerator extends PowerGenerator{
|
||||
generateEffect.at(x + Mathf.range(generateEffectRange), y + Mathf.range(generateEffectRange));
|
||||
}
|
||||
|
||||
//make sure the multiplier doesn't change when there is nothing to consume while it's still running
|
||||
if(filterItem != null && valid && itemDurationMultipliers.size > 0 && filterItem.getConsumed(this) != null){
|
||||
itemDurationMultiplier = itemDurationMultipliers.get(filterItem.getConsumed(this), 1);
|
||||
}
|
||||
|
||||
//take in items periodically
|
||||
if(hasItems && valid && generateTime <= 0f){
|
||||
consume();
|
||||
@@ -122,7 +138,7 @@ public class ConsumeGenerator extends PowerGenerator{
|
||||
}
|
||||
|
||||
//generation time always goes down, but only at the end so consumeTriggerValid doesn't assume fake items
|
||||
generateTime -= delta() / itemDuration;
|
||||
generateTime -= delta() / (itemDuration * itemDurationMultiplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -39,7 +39,7 @@ public class LightBlock extends Block{
|
||||
@Override
|
||||
public void init(){
|
||||
lightRadius = radius*2.5f;
|
||||
clipSize = Math.max(clipSize, lightRadius * 3f);
|
||||
lightClipSize = Math.max(lightClipSize, lightRadius * 3f);
|
||||
emitLight = true;
|
||||
|
||||
super.init();
|
||||
|
||||
@@ -49,6 +49,7 @@ public class NuclearReactor extends PowerGenerator{
|
||||
hasItems = true;
|
||||
hasLiquids = true;
|
||||
rebuildable = false;
|
||||
emitLight = true;
|
||||
flags = EnumSet.of(BlockFlag.reactor, BlockFlag.generator);
|
||||
schematicPriority = -5;
|
||||
envEnabled = Env.any;
|
||||
|
||||
@@ -62,20 +62,23 @@ public class PowerDiode extends Block{
|
||||
PowerGraph frontGraph = front().power.graph;
|
||||
if(backGraph == frontGraph) return;
|
||||
|
||||
// 0f - 1f of battery capacity in use
|
||||
float backStored = backGraph.getBatteryStored() / backGraph.getTotalBatteryCapacity();
|
||||
float frontStored = frontGraph.getBatteryStored() / frontGraph.getTotalBatteryCapacity();
|
||||
float backStored = backGraph.getBatteryStored();
|
||||
float backCapacity = backGraph.getTotalBatteryCapacity();
|
||||
float frontStored = frontGraph.getBatteryStored();
|
||||
float frontCapacity = frontGraph.getTotalBatteryCapacity();
|
||||
|
||||
// try to send if the back side has more % capacity stored than the front side
|
||||
if(backStored > frontStored){
|
||||
// send half of the difference
|
||||
float amount = backGraph.getBatteryStored() * (backStored - frontStored) / 2;
|
||||
// prevent sending more than the front can handle
|
||||
amount = Mathf.clamp(amount, 0, frontGraph.getTotalBatteryCapacity() * (1 - frontStored));
|
||||
if(backStored/backCapacity <= frontStored/frontCapacity) return;
|
||||
|
||||
backGraph.transferPower(-amount);
|
||||
frontGraph.transferPower(amount);
|
||||
}
|
||||
float targetPercentage = (frontStored + backStored) / (frontCapacity + backCapacity);
|
||||
|
||||
// send half of the difference
|
||||
float amount = (targetPercentage * frontCapacity - frontStored) / 2;
|
||||
|
||||
// prevent sending more than the front can handle
|
||||
amount = Mathf.clamp(amount, 0, frontCapacity - frontStored);
|
||||
|
||||
backGraph.transferPower(-amount);
|
||||
frontGraph.transferPower(amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@ public class PowerNode extends PowerBlock{
|
||||
public @Load(value = "@-laser-end", fallback = "laser-end") TextureRegion laserEnd;
|
||||
public float laserRange = 6;
|
||||
public int maxNodes = 3;
|
||||
public boolean autolink = true, drawRange = true;
|
||||
public boolean autolink = true, drawRange = true, sameBlockConnection = false;
|
||||
public float laserScale = 0.25f;
|
||||
public Color laserColor1 = Color.white;
|
||||
public Color laserColor2 = Pal.powerLight;
|
||||
@@ -194,7 +194,7 @@ public class PowerNode extends PowerBlock{
|
||||
}
|
||||
|
||||
protected boolean overlaps(Building src, Building other, float range){
|
||||
return overlaps(src.x, src.y, other.tile(), range);
|
||||
return overlaps(src.x, src.y, other.tile, range);
|
||||
}
|
||||
|
||||
protected boolean overlaps(Tile src, Tile other, float range){
|
||||
@@ -209,9 +209,9 @@ public class PowerNode extends PowerBlock{
|
||||
protected void getPotentialLinks(Tile tile, Team team, Cons<Building> others){
|
||||
if(!autolink) return;
|
||||
|
||||
Boolf<Building> valid = other -> other != null && other.tile() != tile && other.block.connectedPower && other.power != null &&
|
||||
Boolf<Building> valid = other -> other != null && other.tile != tile && other.block.connectedPower && other.power != null &&
|
||||
(other.block.outputsPower || other.block.consumesPower || other.block instanceof PowerNode) &&
|
||||
overlaps(tile.x * tilesize + offset, tile.y * tilesize + offset, other.tile(), laserRange * tilesize) && other.team == team &&
|
||||
overlaps(tile.x * tilesize + offset, tile.y * tilesize + offset, other.tile, laserRange * tilesize) && other.team == team &&
|
||||
!graphs.contains(other.power.graph) &&
|
||||
!PowerNode.insulated(tile, other.tile) &&
|
||||
!(other instanceof PowerNodeBuild obuild && obuild.power.links.size >= ((PowerNode)obuild.block).maxNodes) &&
|
||||
@@ -264,7 +264,7 @@ public class PowerNode extends PowerBlock{
|
||||
//TODO code duplication w/ method above?
|
||||
/** Iterates through linked nodes of a block at a tile. All returned buildings are power nodes. */
|
||||
public static void getNodeLinks(Tile tile, Block block, Team team, Cons<Building> others){
|
||||
Boolf<Building> valid = other -> other != null && other.tile() != tile && other.block instanceof PowerNode node &&
|
||||
Boolf<Building> valid = other -> other != null && other.tile != tile && other.block instanceof PowerNode node &&
|
||||
node.autolink &&
|
||||
other.power.links.size < node.maxNodes &&
|
||||
node.overlaps(other.x, other.y, tile, block, node.laserRange * tilesize) && other.team == team
|
||||
@@ -341,7 +341,7 @@ public class PowerNode extends PowerBlock{
|
||||
}
|
||||
|
||||
public boolean linkValid(Building tile, Building link, boolean checkMaxNodes){
|
||||
if(tile == link || link == null || !link.block.hasPower || !link.block.connectedPower || tile.team != link.team) return false;
|
||||
if(tile == link || link == null || !link.block.hasPower || !link.block.connectedPower || tile.team != link.team || (sameBlockConnection && tile.block != link.block)) return false;
|
||||
|
||||
if(overlaps(tile, link, laserRange * tilesize) || (link.block instanceof PowerNode node && overlaps(link, tile, node.laserRange * tilesize))){
|
||||
if(checkMaxNodes && link.block instanceof PowerNode node){
|
||||
|
||||
@@ -37,9 +37,10 @@ public class ThermalGenerator extends PowerGenerator{
|
||||
outputsLiquid = true;
|
||||
hasLiquids = true;
|
||||
}
|
||||
emitLight = true;
|
||||
super.init();
|
||||
//proper light clipping
|
||||
clipSize = Math.max(clipSize, 45f * size * 2f * 2f);
|
||||
lightClipSize = Math.max(lightClipSize, 45f * size * 2f * 2f);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -45,6 +45,10 @@ public class BeamDrill extends Block{
|
||||
|
||||
/** Multipliers of drill speed for each item. Defaults to 1. */
|
||||
public ObjectFloatMap<Item> drillMultipliers = new ObjectFloatMap<>();
|
||||
/** Special exemption item that this drill can't mine. */
|
||||
public @Nullable Item blockedItem;
|
||||
/** Special exemption items that this drill can't mine. */
|
||||
public @Nullable Seq<Item> blockedItems;
|
||||
|
||||
public Color sparkColor = Color.valueOf("fd9e81"), glowColor = Color.white;
|
||||
public float glowIntensity = 0.2f, pulseIntensity = 0.07f;
|
||||
@@ -76,6 +80,9 @@ public class BeamDrill extends Block{
|
||||
public void init(){
|
||||
updateClipRadius((range + 2) * tilesize);
|
||||
super.init();
|
||||
if(blockedItems == null && blockedItem != null){
|
||||
blockedItems = Seq.with(blockedItem);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -111,7 +118,10 @@ public class BeamDrill extends Block{
|
||||
public void setStats(){
|
||||
super.setStats();
|
||||
|
||||
stats.add(Stat.drillTier, StatValues.drillables(drillTime, 0f, size, drillMultipliers, b -> (b instanceof Floor f && f.wallOre && f.itemDrop != null && f.itemDrop.hardness <= tier) || (b instanceof StaticWall w && w.itemDrop != null && w.itemDrop.hardness <= tier)));
|
||||
stats.add(Stat.drillTier, StatValues.drillables(drillTime, 0f, size, drillMultipliers, b ->
|
||||
(b instanceof Floor f && f.wallOre && f.itemDrop != null && f.itemDrop.hardness <= tier && (blockedItems == null || !blockedItems.contains(f.itemDrop))) ||
|
||||
(b instanceof StaticWall w && w.itemDrop != null && w.itemDrop.hardness <= tier && (blockedItems == null || !blockedItems.contains(w.itemDrop)))
|
||||
));
|
||||
|
||||
stats.add(Stat.drillSpeed, 60f / drillTime * size, StatUnit.itemsSecond);
|
||||
|
||||
@@ -142,7 +152,7 @@ public class BeamDrill extends Block{
|
||||
if(other != null && other.solid()){
|
||||
Item drop = other.wallDrop();
|
||||
if(drop != null){
|
||||
if(drop.hardness <= tier){
|
||||
if(drop.hardness <= tier && (blockedItems == null || !blockedItems.contains(drop))){
|
||||
found = drop;
|
||||
count++;
|
||||
}else{
|
||||
@@ -193,7 +203,7 @@ public class BeamDrill extends Block{
|
||||
Tile other = world.tile(Tmp.p1.x + Geometry.d4x(rotation)*j, Tmp.p1.y + Geometry.d4y(rotation)*j);
|
||||
if(other != null && other.solid()){
|
||||
Item drop = other.wallDrop();
|
||||
if(drop != null && drop.hardness <= tier){
|
||||
if(drop != null && drop.hardness <= tier && (blockedItems == null || !blockedItems.contains(drop))){
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
@@ -221,13 +231,7 @@ public class BeamDrill extends Block{
|
||||
@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);
|
||||
}
|
||||
drawItemSelection(lastItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -237,7 +241,7 @@ public class BeamDrill extends Block{
|
||||
if(lasers[0] == null) updateLasers();
|
||||
|
||||
warmup = Mathf.approachDelta(warmup, Mathf.num(efficiency > 0), 1f / 60f);
|
||||
|
||||
|
||||
updateFacing();
|
||||
|
||||
float multiplier = Mathf.lerp(1f, optionalBoostIntensity, optionalEfficiency);
|
||||
@@ -379,7 +383,7 @@ public class BeamDrill extends Block{
|
||||
if(other != null){
|
||||
if(other.solid()){
|
||||
Item drop = other.wallDrop();
|
||||
if(drop != null && drop.hardness <= tier){
|
||||
if(drop != null && drop.hardness <= tier && (blockedItems == null || !blockedItems.contains(drop))){
|
||||
facingAmount ++;
|
||||
if(lastItem != drop && lastItem != null){
|
||||
multiple = true;
|
||||
|
||||
@@ -11,6 +11,8 @@ import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.consumers.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
public class BurstDrill extends Drill{
|
||||
public float shake = 2f;
|
||||
@@ -35,7 +37,6 @@ public class BurstDrill extends Drill{
|
||||
|
||||
//does not drill in the traditional sense, so this is not even used
|
||||
hardnessDrillMultiplier = 0f;
|
||||
liquidBoostIntensity = 1f;
|
||||
//generally at center
|
||||
drillEffectRnd = 0f;
|
||||
drillEffect = Fx.shockwave;
|
||||
@@ -53,6 +54,19 @@ public class BurstDrill extends Drill{
|
||||
return drillTime / drillMultipliers.get(item, 1f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStats(){
|
||||
super.setStats();
|
||||
|
||||
if(liquidBoostIntensity != 1 && findConsumer(f -> f instanceof ConsumeLiquidBase && f.booster) instanceof ConsumeLiquidBase consBase){
|
||||
stats.remove(Stat.booster);
|
||||
stats.add(Stat.booster,
|
||||
StatValues.speedBoosters("{0}" + StatUnit.timesSpeed.localized(),
|
||||
consBase.amount, liquidBoostIntensity, false, consBase::consumes)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public class BurstDrillBuild extends DrillBuild{
|
||||
//used so the lights don't fade out immediately
|
||||
public float smoothProgress = 0f;
|
||||
@@ -77,7 +91,7 @@ public class BurstDrill extends Drill{
|
||||
if(items.total() <= itemCapacity - dominantItems && dominantItems > 0 && efficiency > 0){
|
||||
warmup = Mathf.approachDelta(warmup, progress / drillTime, 0.01f);
|
||||
|
||||
float speed = efficiency;
|
||||
float speed = Mathf.lerp(1f, liquidBoostIntensity, optionalEfficiency) * efficiency;
|
||||
|
||||
timeDrilled += speedCurve.apply(progress / drillTime) * speed;
|
||||
|
||||
|
||||
@@ -40,6 +40,8 @@ public class Drill extends Block{
|
||||
public float warmupSpeed = 0.015f;
|
||||
/** Special exemption item that this drill can't mine. */
|
||||
public @Nullable Item blockedItem;
|
||||
/** Special exemption items that this drill can't mine. */
|
||||
public @Nullable Seq<Item> blockedItems;
|
||||
|
||||
//return variables for countOre
|
||||
protected @Nullable Item returnItem;
|
||||
@@ -52,7 +54,7 @@ public class Drill extends Block{
|
||||
/** Drill effect randomness. Block size by default. */
|
||||
public float drillEffectRnd = -1f;
|
||||
/** Chance of displaying the effect. Useful for extremely fast drills. */
|
||||
public float drillEffectChance = 1f;
|
||||
public float drillEffectChance = 0.02f;
|
||||
/** Speed the drill bit rotates at. */
|
||||
public float rotateSpeed = 2f;
|
||||
/** Effect randomly played while drilling. */
|
||||
@@ -89,6 +91,9 @@ public class Drill extends Block{
|
||||
@Override
|
||||
public void init(){
|
||||
super.init();
|
||||
if(blockedItems == null && blockedItem != null){
|
||||
blockedItems = Seq.with(blockedItem);
|
||||
}
|
||||
if(drillEffectRnd < 0) drillEffectRnd = size;
|
||||
}
|
||||
|
||||
@@ -155,7 +160,7 @@ public class Drill extends Block{
|
||||
Draw.color();
|
||||
}
|
||||
}else{
|
||||
Tile to = tile.getLinkedTilesAs(this, tempTiles).find(t -> t.drop() != null && (t.drop().hardness > tier || t.drop() == blockedItem));
|
||||
Tile to = tile.getLinkedTilesAs(this, tempTiles).find(t -> t.drop() != null && (t.drop().hardness > tier || (blockedItems != null && blockedItems.contains(t.drop()))));
|
||||
Item item = to == null ? null : to.drop();
|
||||
if(item != null){
|
||||
drawPlaceText(Core.bundle.get("bar.drilltierreq"), x, y, valid);
|
||||
@@ -172,16 +177,16 @@ public class Drill extends Block{
|
||||
super.setStats();
|
||||
|
||||
stats.add(Stat.drillTier, StatValues.drillables(drillTime, hardnessDrillMultiplier, size * size, drillMultipliers, b -> b instanceof Floor f && !f.wallOre && f.itemDrop != null &&
|
||||
f.itemDrop.hardness <= tier && f.itemDrop != blockedItem && (indexer.isBlockPresent(f) || state.isMenu())));
|
||||
f.itemDrop.hardness <= tier && (blockedItems == null || !blockedItems.contains(f.itemDrop)) && (indexer.isBlockPresent(f) || state.isMenu())));
|
||||
|
||||
stats.add(Stat.drillSpeed, 60f / drillTime * size * size, StatUnit.itemsSecond);
|
||||
|
||||
if(liquidBoostIntensity != 1 && findConsumer(f -> f instanceof ConsumeLiquidBase) instanceof ConsumeLiquidBase consBase){
|
||||
if(liquidBoostIntensity != 1 && findConsumer(f -> f instanceof ConsumeLiquidBase && f.booster) instanceof ConsumeLiquidBase consBase){
|
||||
stats.remove(Stat.booster);
|
||||
stats.add(Stat.booster,
|
||||
StatValues.speedBoosters("{0}" + StatUnit.timesSpeed.localized(),
|
||||
consBase.amount,
|
||||
liquidBoostIntensity * liquidBoostIntensity, false, this::consumesLiquid)
|
||||
liquidBoostIntensity * liquidBoostIntensity, false, consBase::consumes)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -227,7 +232,7 @@ public class Drill extends Block{
|
||||
public boolean canMine(Tile tile){
|
||||
if(tile == null || tile.block().isStatic()) return false;
|
||||
Item drops = tile.drop();
|
||||
return drops != null && drops.hardness <= tier && drops != blockedItem;
|
||||
return drops != null && drops.hardness <= tier && (blockedItems == null || !blockedItems.contains(drops));
|
||||
}
|
||||
|
||||
public class DrillBuild extends Building{
|
||||
@@ -256,13 +261,7 @@ public class Drill extends Block{
|
||||
|
||||
@Override
|
||||
public void drawSelect(){
|
||||
if(dominantItem != null){
|
||||
float dx = x - size * tilesize/2f, dy = y + size * tilesize/2f, s = iconSmall / 4f;
|
||||
Draw.mixcol(Color.darkGray, 1f);
|
||||
Draw.rect(dominantItem.fullIcon, dx, dy - 1, s, s);
|
||||
Draw.reset();
|
||||
Draw.rect(dominantItem.fullIcon, dx, dy, s, s);
|
||||
}
|
||||
drawItemSelection(dominantItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -315,11 +314,14 @@ public class Drill extends Block{
|
||||
}
|
||||
|
||||
if(dominantItems > 0 && progress >= delay && items.total() < itemCapacity){
|
||||
offload(dominantItem);
|
||||
int amount = (int)(progress / delay);
|
||||
for(int i = 0; i < amount; i++){
|
||||
offload(dominantItem);
|
||||
}
|
||||
|
||||
progress %= delay;
|
||||
|
||||
if(wasVisible && Mathf.chanceDelta(updateEffectChance * warmup)) drillEffect.at(x + Mathf.range(drillEffectRnd), y + Mathf.range(drillEffectRnd), dominantItem.color);
|
||||
if(wasVisible && Mathf.chanceDelta(drillEffectChance * warmup)) drillEffect.at(x + Mathf.range(drillEffectRnd), y + Mathf.range(drillEffectRnd), dominantItem.color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ public class GenericCrafter extends Block{
|
||||
public Effect craftEffect = Fx.none;
|
||||
public Effect updateEffect = Fx.none;
|
||||
public float updateEffectChance = 0.04f;
|
||||
public float updateEffectSpread = 4f;
|
||||
public float warmupSpeed = 0.019f;
|
||||
/** Only used for legacy cultivator blocks. */
|
||||
public boolean legacyReadWarmup = false;
|
||||
@@ -233,7 +234,7 @@ public class GenericCrafter extends Block{
|
||||
}
|
||||
|
||||
if(wasVisible && Mathf.chanceDelta(updateEffectChance)){
|
||||
updateEffect.at(x + Mathf.range(size * 4f), y + Mathf.range(size * 4));
|
||||
updateEffect.at(x + Mathf.range(size * updateEffectSpread), y + Mathf.range(size * updateEffectSpread));
|
||||
}
|
||||
}else{
|
||||
warmup = Mathf.approachDelta(warmup, 0f, warmupSpeed);
|
||||
|
||||
@@ -85,7 +85,7 @@ public class WallCrafter extends Block{
|
||||
|
||||
if(consItems && itemConsumer instanceof ConsumeItems coni){
|
||||
stats.remove(Stat.booster);
|
||||
stats.add(Stat.booster, StatValues.itemBoosters("{0}" + StatUnit.timesSpeed.localized(), stats.timePeriod, itemBoostIntensity, 0f, coni.items, i -> Structs.contains(coni.items, s -> s.item == i)));
|
||||
stats.add(Stat.booster, StatValues.itemBoosters("{0}" + StatUnit.timesSpeed.localized(), stats.timePeriod, itemBoostIntensity, 0f, coni.items));
|
||||
}
|
||||
|
||||
if(liquidBoostIntensity != 1 && findConsumer(f -> f instanceof ConsumeLiquidBase && f.booster) instanceof ConsumeLiquidBase consBase){
|
||||
@@ -210,7 +210,7 @@ public class WallCrafter extends Block{
|
||||
lastEfficiency = eff * timeScale * efficiency;
|
||||
|
||||
if(cons && (time += edelta() * eff) >= drillTime){
|
||||
items.add(output, 1);
|
||||
offload(output);
|
||||
time %= drillTime;
|
||||
}
|
||||
|
||||
|
||||
@@ -78,6 +78,12 @@ public class ItemSource extends Block{
|
||||
super.draw();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawSelect(){
|
||||
super.drawSelect();
|
||||
drawItemSelection(outputItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
if(outputItem == null) return;
|
||||
@@ -88,6 +94,7 @@ public class ItemSource extends Block{
|
||||
while(counter >= limit){
|
||||
items.set(outputItem, 1);
|
||||
dump(outputItem);
|
||||
produced(outputItem);
|
||||
items.set(outputItem, 0);
|
||||
counter -= limit;
|
||||
}
|
||||
@@ -120,4 +127,4 @@ public class ItemSource extends Block{
|
||||
outputItem = content.item(read.s());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,6 +83,12 @@ public class LiquidSource extends Block{
|
||||
Draw.rect(block.region, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawSelect(){
|
||||
super.drawSelect();
|
||||
drawItemSelection(source);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildConfiguration(Table table){
|
||||
ItemSelection.buildTable(LiquidSource.this, table, content.liquids(), () -> source, this::configure, selectionRows, selectionColumns);
|
||||
@@ -111,4 +117,4 @@ public class LiquidSource extends Block{
|
||||
source = id == -1 ? null : content.liquid(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,22 +27,23 @@ import mindustry.logic.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
import mindustry.world.meta.*;
|
||||
import mindustry.world.modules.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class CoreBlock extends StorageBlock{
|
||||
protected static final float cloudScaling = 1700f, cfinScl = -2f, cfinOffset = 0.3f, calphaFinOffset = 0.25f, cloudAlpha = 0.81f;
|
||||
protected static final float[] cloudAlphas = {0, 0.5f, 1f, 0.1f, 0, 0f};
|
||||
public static final float cloudScaling = 1700f, cfinScl = -2f, cfinOffset = 0.3f, calphaFinOffset = 0.25f, cloudAlpha = 0.81f;
|
||||
public static final float[] cloudAlphas = {0, 0.5f, 1f, 0.1f, 0, 0f};
|
||||
|
||||
//hacky way to pass item modules between methods
|
||||
private static ItemModule nextItems;
|
||||
protected static final float[] thrusterSizes = {0f, 0f, 0f, 0f, 0.3f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 0f};
|
||||
public static final float[] thrusterSizes = {0f, 0f, 0f, 0f, 0.3f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 0f};
|
||||
|
||||
public @Load(value = "@-thruster1", fallback = "clear-effect") TextureRegion thruster1; //top right
|
||||
public @Load(value = "@-thruster2", fallback = "clear-effect") TextureRegion thruster2; //bot left
|
||||
public float thrusterLength = 14f/4f;
|
||||
public float thrusterLength = 14f/4f, thrusterOffset = 0f;
|
||||
public boolean isFirstTier;
|
||||
/** If true, this core type requires a core zone to upgrade. */
|
||||
public boolean requiresCoreZone;
|
||||
@@ -68,8 +69,6 @@ public class CoreBlock extends StorageBlock{
|
||||
priority = TargetPriority.core;
|
||||
flags = EnumSet.of(BlockFlag.core);
|
||||
unitCapModifier = 10;
|
||||
loopSound = Sounds.respawning;
|
||||
loopSoundVolume = 1f;
|
||||
drawDisabled = false;
|
||||
canOverdrive = false;
|
||||
envEnabled |= Env.space;
|
||||
@@ -91,6 +90,10 @@ public class CoreBlock extends StorageBlock{
|
||||
|
||||
if(!net.client()){
|
||||
Unit unit = spawnType.create(tile.team());
|
||||
//reset reload so that the player can't shoot immediately
|
||||
for(var mount : unit.mounts){
|
||||
mount.reload = mount.weapon.reload;
|
||||
}
|
||||
unit.set(core);
|
||||
unit.rotation(90f);
|
||||
unit.impulse(0f, 3f);
|
||||
@@ -146,6 +149,14 @@ public class CoreBlock extends StorageBlock{
|
||||
super.init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(){
|
||||
super.postInit();
|
||||
|
||||
//sync shown planets with unit spawned
|
||||
unitType.shownPlanets.addAll(shownPlanets);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBreak(Tile tile){
|
||||
return state.isEditor();
|
||||
@@ -230,113 +241,14 @@ public class CoreBlock extends StorageBlock{
|
||||
}
|
||||
}
|
||||
|
||||
public void drawLanding(CoreBuild build, float x, float y){
|
||||
float fin = renderer.getLandTimeIn();
|
||||
float fout = 1f - fin;
|
||||
|
||||
float scl = Scl.scl(4f) / renderer.getDisplayScale();
|
||||
float shake = 0f;
|
||||
float s = region.width * region.scl() * scl * 3.6f * Interp.pow2Out.apply(fout);
|
||||
float rotation = Interp.pow2In.apply(fout) * 135f;
|
||||
x += Mathf.range(shake);
|
||||
y += Mathf.range(shake);
|
||||
float thrustOpen = 0.25f;
|
||||
float thrusterFrame = fin >= thrustOpen ? 1f : fin / thrustOpen;
|
||||
float thrusterSize = Mathf.sample(thrusterSizes, fin);
|
||||
|
||||
//when launching, thrusters stay out the entire time.
|
||||
if(renderer.isLaunching()){
|
||||
Interp i = Interp.pow2Out;
|
||||
thrusterFrame = i.apply(Mathf.clamp(fout*13f));
|
||||
thrusterSize = i.apply(Mathf.clamp(fout*9f));
|
||||
}
|
||||
|
||||
Draw.color(Pal.lightTrail);
|
||||
//TODO spikier heat
|
||||
Draw.rect("circle-shadow", x, y, s, s);
|
||||
|
||||
Draw.scl(scl);
|
||||
|
||||
//draw thruster flame
|
||||
float strength = (1f + (size - 3)/2.5f) * scl * thrusterSize * (0.95f + Mathf.absin(2f, 0.1f));
|
||||
float offset = (size - 3) * 3f * scl;
|
||||
|
||||
for(int i = 0; i < 4; i++){
|
||||
Tmp.v1.trns(i * 90 + rotation, 1f);
|
||||
|
||||
Tmp.v1.setLength((size * tilesize/2f + 1f)*scl + strength*2f + offset);
|
||||
Draw.color(build.team.color);
|
||||
Fill.circle(Tmp.v1.x + x, Tmp.v1.y + y, 6f * strength);
|
||||
|
||||
Tmp.v1.setLength((size * tilesize/2f + 1f)*scl + strength*0.5f + offset);
|
||||
Draw.color(Color.white);
|
||||
Fill.circle(Tmp.v1.x + x, Tmp.v1.y + y, 3.5f * strength);
|
||||
}
|
||||
|
||||
drawLandingThrusters(x, y, rotation, thrusterFrame);
|
||||
|
||||
Drawf.spinSprite(region, x, y, rotation);
|
||||
|
||||
Draw.alpha(Interp.pow4In.apply(thrusterFrame));
|
||||
drawLandingThrusters(x, y, rotation, thrusterFrame);
|
||||
Draw.alpha(1f);
|
||||
|
||||
if(teamRegions[build.team.id] == teamRegion) Draw.color(build.team.color);
|
||||
|
||||
Drawf.spinSprite(teamRegions[build.team.id], x, y, rotation);
|
||||
|
||||
Draw.color();
|
||||
Draw.scl();
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
protected void drawLandingThrusters(float x, float y, float rotation, float frame){
|
||||
float length = thrusterLength * (frame - 1f) - 1f/4f;
|
||||
float alpha = Draw.getColorAlpha();
|
||||
|
||||
//two passes for consistent lighting
|
||||
for(int j = 0; j < 2; j++){
|
||||
for(int i = 0; i < 4; i++){
|
||||
var reg = i >= 2 ? thruster2 : thruster1;
|
||||
float rot = (i * 90) + rotation % 90f;
|
||||
Tmp.v1.trns(rot, length * Draw.xscl);
|
||||
|
||||
//second pass applies extra layer of shading
|
||||
if(j == 1){
|
||||
Tmp.v1.rotate(-90f);
|
||||
Draw.alpha((rotation % 90f) / 90f * alpha);
|
||||
rot -= 90f;
|
||||
Draw.rect(reg, x + Tmp.v1.x, y + Tmp.v1.y, rot);
|
||||
}else{
|
||||
Draw.alpha(alpha);
|
||||
Draw.rect(reg, x + Tmp.v1.x, y + Tmp.v1.y, rot);
|
||||
}
|
||||
}
|
||||
}
|
||||
Draw.alpha(1f);
|
||||
}
|
||||
|
||||
public class CoreBuild extends Building{
|
||||
public class CoreBuild extends Building implements LaunchAnimator{
|
||||
public int storageCapacity;
|
||||
public boolean noEffect = false;
|
||||
public Team lastDamage = Team.derelict;
|
||||
public float iframes = -1f;
|
||||
public float thrusterTime = 0f;
|
||||
|
||||
protected float cloudSeed;
|
||||
|
||||
//utility methods for less Block-to-CoreBlock casts. also allows for more customization
|
||||
public float landDuration(){
|
||||
return landDuration;
|
||||
}
|
||||
|
||||
public Music landMusic(){
|
||||
return landMusic;
|
||||
}
|
||||
|
||||
public Music launchMusic(){
|
||||
return launchMusic;
|
||||
}
|
||||
protected float cloudSeed, landParticleTimer;
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
@@ -357,11 +269,26 @@ public class CoreBlock extends StorageBlock{
|
||||
}
|
||||
}
|
||||
|
||||
// `launchType` is null if it's landing instead of launching.
|
||||
public void beginLaunch(@Nullable CoreBlock launchType){
|
||||
@Override
|
||||
public float launchDuration(){
|
||||
return landDuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Music landMusic(){
|
||||
return landMusic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Music launchMusic(){
|
||||
return launchMusic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beginLaunch(boolean launching){
|
||||
cloudSeed = Mathf.random(1f);
|
||||
if(launchType != null){
|
||||
Fx.coreLaunchConstruct.at(x, y, launchType.size);
|
||||
if(launching){
|
||||
Fx.coreLaunchConstruct.at(x, y, size);
|
||||
}
|
||||
|
||||
if(!headless){
|
||||
@@ -373,7 +300,7 @@ public class CoreBlock extends StorageBlock{
|
||||
image.color.a = 0f;
|
||||
image.touchable = Touchable.disabled;
|
||||
image.setFillParent(true);
|
||||
image.actions(Actions.delay((landDuration() - margin) / 60f), Actions.fadeIn(margin / 60f, Interp.pow2In), Actions.delay(6f / 60f), Actions.remove());
|
||||
image.actions(Actions.delay((launchDuration() - margin) / 60f), Actions.fadeIn(margin / 60f, Interp.pow2In), Actions.delay(6f / 60f), Actions.remove());
|
||||
image.update(() -> {
|
||||
image.toFront();
|
||||
ui.loadfrag.toFront();
|
||||
@@ -397,7 +324,7 @@ public class CoreBlock extends StorageBlock{
|
||||
});
|
||||
Core.scene.add(image);
|
||||
|
||||
Time.run(landDuration(), () -> {
|
||||
Time.run(launchDuration(), () -> {
|
||||
launchEffect.at(this);
|
||||
Effect.shake(5f, 5f, this);
|
||||
thrusterTime = 1f;
|
||||
@@ -412,9 +339,11 @@ public class CoreBlock extends StorageBlock{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void endLaunch(){}
|
||||
|
||||
public void drawLanding(CoreBlock block){
|
||||
@Override
|
||||
public void drawLaunch(){
|
||||
var clouds = Core.assets.get("sprites/clouds.png", Texture.class);
|
||||
|
||||
float fin = renderer.getLandTimeIn();
|
||||
@@ -432,7 +361,7 @@ public class CoreBlock extends StorageBlock{
|
||||
});
|
||||
Draw.color();
|
||||
|
||||
block.drawLanding(this, x, y);
|
||||
drawLanding(x, y);
|
||||
|
||||
Draw.color();
|
||||
Draw.mixcol(Color.white, Interp.pow5In.apply(fout));
|
||||
@@ -466,6 +395,92 @@ public class CoreBlock extends StorageBlock{
|
||||
}
|
||||
}
|
||||
|
||||
public void drawLanding(float x, float y){
|
||||
float fin = renderer.getLandTimeIn();
|
||||
float fout = 1f - fin;
|
||||
|
||||
float scl = Scl.scl(4f) / renderer.getDisplayScale();
|
||||
float shake = 0f;
|
||||
float s = region.width * region.scl() * scl * 3.6f * Interp.pow2Out.apply(fout);
|
||||
float rotation = Interp.pow2In.apply(fout) * 135f;
|
||||
x += Mathf.range(shake);
|
||||
y += Mathf.range(shake);
|
||||
float thrustOpen = 0.25f;
|
||||
float thrusterFrame = fin >= thrustOpen ? 1f : fin / thrustOpen;
|
||||
float thrusterSize = Mathf.sample(thrusterSizes, fin);
|
||||
|
||||
//when launching, thrusters stay out the entire time.
|
||||
if(renderer.isLaunching()){
|
||||
Interp i = Interp.pow2Out;
|
||||
thrusterFrame = i.apply(Mathf.clamp(fout*13f));
|
||||
thrusterSize = i.apply(Mathf.clamp(fout*9f));
|
||||
}
|
||||
|
||||
Draw.color(Pal.lightTrail);
|
||||
//TODO spikier heat
|
||||
Draw.rect("circle-shadow", x, y, s, s);
|
||||
|
||||
Draw.scl(scl);
|
||||
|
||||
//draw thruster flame
|
||||
float strength = (1f + (size - 3)/2.5f) * scl * thrusterSize * (0.95f + Mathf.absin(2f, 0.1f));
|
||||
float offset = (size - 3) * 3f * scl;
|
||||
|
||||
for(int i = 0; i < 4; i++){
|
||||
Tmp.v1.trns(i * 90 + rotation, 1f);
|
||||
|
||||
Tmp.v1.setLength((size * tilesize/2f + 1f)*scl + strength*2f + offset);
|
||||
Draw.color(team.color);
|
||||
Fill.circle(Tmp.v1.x + x, Tmp.v1.y + y, 6f * strength);
|
||||
|
||||
Tmp.v1.setLength((size * tilesize/2f + 1f)*scl + strength*0.5f + offset);
|
||||
Draw.color(Color.white);
|
||||
Fill.circle(Tmp.v1.x + x, Tmp.v1.y + y, 3.5f * strength);
|
||||
}
|
||||
|
||||
drawLandingThrusters(x, y, rotation, thrusterFrame);
|
||||
|
||||
Drawf.spinSprite(region, x, y, rotation);
|
||||
|
||||
Draw.alpha(Interp.pow4In.apply(thrusterFrame));
|
||||
drawLandingThrusters(x, y, rotation, thrusterFrame);
|
||||
Draw.alpha(1f);
|
||||
|
||||
if(teamRegions[team.id] == teamRegion) Draw.color(team.color);
|
||||
|
||||
Drawf.spinSprite(teamRegions[team.id], x, y, rotation);
|
||||
|
||||
Draw.color();
|
||||
Draw.scl();
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
protected void drawLandingThrusters(float x, float y, float rotation, float frame){
|
||||
float length = thrusterLength * (frame - 1f) - 1f/4f;
|
||||
float alpha = Draw.getColorAlpha();
|
||||
|
||||
//two passes for consistent lighting
|
||||
for(int j = 0; j < 2; j++){
|
||||
for(int i = 0; i < 4; i++){
|
||||
var reg = i >= 2 ? thruster2 : thruster1;
|
||||
float rot = (i * 90) + rotation % 90f;
|
||||
Tmp.v1.trns(rot, length * Draw.xscl);
|
||||
|
||||
//second pass applies extra layer of shading
|
||||
if(j == 1){
|
||||
Tmp.v1.rotate(-90f);
|
||||
Draw.alpha((rotation % 90f) / 90f * alpha);
|
||||
rot -= 90f;
|
||||
Draw.rect(reg, x + Tmp.v1.x, y + Tmp.v1.y, rot);
|
||||
}else{
|
||||
Draw.alpha(alpha);
|
||||
Draw.rect(reg, x + Tmp.v1.x, y + Tmp.v1.y, rot);
|
||||
}
|
||||
}
|
||||
}
|
||||
Draw.alpha(1f);
|
||||
}
|
||||
|
||||
public void drawThrusters(float frame){
|
||||
float length = thrusterLength * (frame - 1f) - 1f/4f;
|
||||
for(int i = 0; i < 4; i++){
|
||||
@@ -545,28 +560,26 @@ public class CoreBlock extends StorageBlock{
|
||||
}
|
||||
|
||||
/** @return Camera zoom while landing or launching. May optionally do other things such as setting camera position to itself. */
|
||||
public float zoomLaunching(){
|
||||
@Override
|
||||
public float zoomLaunch(){
|
||||
Core.camera.position.set(this);
|
||||
return landZoomInterp.apply(Scl.scl(landZoomFrom), Scl.scl(landZoomTo), renderer.getLandTimeIn());
|
||||
}
|
||||
|
||||
public void updateLaunching(){
|
||||
updateLandParticles();
|
||||
}
|
||||
@Override
|
||||
public void updateLaunch(){
|
||||
float in = renderer.getLandTimeIn() * launchDuration();
|
||||
float tsize = Mathf.sample(thrusterSizes, (in + 35f) / launchDuration());
|
||||
|
||||
public void updateLandParticles(){
|
||||
float in = renderer.getLandTimeIn() * landDuration();
|
||||
float tsize = Mathf.sample(thrusterSizes, (in + 35f) / landDuration());
|
||||
|
||||
renderer.setLandPTimer(renderer.getLandPTimer() + tsize * Time.delta);
|
||||
if(renderer.getLandTime() >= 1f){
|
||||
landParticleTimer += tsize * Time.delta;
|
||||
if(landParticleTimer >= 1f){
|
||||
tile.getLinkedTiles(t -> {
|
||||
if(Mathf.chance(0.4f)){
|
||||
Fx.coreLandDust.at(t.worldx(), t.worldy(), angleTo(t.worldx(), t.worldy()) + Mathf.range(30f), Tmp.c1.set(t.floor().mapColor).mul(1.5f + Mathf.range(0.15f)));
|
||||
}
|
||||
});
|
||||
|
||||
renderer.setLandPTimer(0f);
|
||||
landParticleTimer = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -587,7 +600,7 @@ public class CoreBlock extends StorageBlock{
|
||||
}
|
||||
|
||||
//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 && state.rules.sector.planet.enemyCoreSpawnReplace){
|
||||
if(state.isCampaign() && team == state.rules.waveTeam && team.cores().size <= 1 && spawner.getSpawns().size == 0 && state.rules.sector.planet.enemyCoreSpawnReplace){
|
||||
//do not recache
|
||||
tile.setOverlayQuiet(Blocks.spawn);
|
||||
|
||||
@@ -630,7 +643,7 @@ public class CoreBlock extends StorageBlock{
|
||||
|
||||
@Override
|
||||
public int getMaximumAccepted(Item item){
|
||||
return state.rules.coreIncinerates ? storageCapacity * 20 : storageCapacity;
|
||||
return state.rules.coreIncinerates ? Integer.MAX_VALUE/2 : storageCapacity;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -638,24 +651,23 @@ public class CoreBlock extends StorageBlock{
|
||||
super.onProximityUpdate();
|
||||
|
||||
for(Building other : state.teams.cores(team)){
|
||||
if(other.tile() != tile){
|
||||
if(other.tile != tile){
|
||||
this.items = other.items;
|
||||
}
|
||||
}
|
||||
state.teams.registerCore(this);
|
||||
|
||||
storageCapacity = itemCapacity + proximity().sum(e -> owns(e) ? e.block.itemCapacity : 0);
|
||||
storageCapacity = itemCapacity + proximity.sum(e -> owns(e) ? e.block.itemCapacity : 0);
|
||||
proximity.each(this::owns, t -> {
|
||||
t.items = items;
|
||||
((StorageBuild)t).linkedCore = this;
|
||||
});
|
||||
|
||||
for(Building other : state.teams.cores(team)){
|
||||
if(other.tile() == tile) continue;
|
||||
storageCapacity += other.block.itemCapacity + other.proximity().sum(e -> owns(other, e) ? e.block.itemCapacity : 0);
|
||||
if(other.tile == tile) continue;
|
||||
storageCapacity += other.block.itemCapacity + other.proximity.sum(e -> owns(other, e) ? e.block.itemCapacity : 0);
|
||||
}
|
||||
|
||||
//Team.sharded.core().items.set(Items.surgeAlloy, 12000)
|
||||
if(!world.isGenerating()){
|
||||
for(Item item : content.items()){
|
||||
items.set(item, Math.min(items.get(item), storageCapacity));
|
||||
|
||||
@@ -112,7 +112,7 @@ public class StorageBlock extends Block{
|
||||
//only add prev items when core is not linked
|
||||
if(linkedCore == null){
|
||||
for(Building other : previous){
|
||||
if(other.items != null && other.items != items){
|
||||
if(other.items != null && other.items != items && !(other instanceof StorageBuild b && b.linkedCore != null)){
|
||||
items.add(other.items);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,6 +233,12 @@ public class Unloader extends Block{
|
||||
Draw.color();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawSelect(){
|
||||
super.drawSelect();
|
||||
drawItemSelection(sortItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildConfiguration(Table table){
|
||||
ItemSelection.buildTable(Unloader.this, table, content.items(), () -> sortItem, this::configure, selectionRows, selectionColumns);
|
||||
|
||||
@@ -142,15 +142,12 @@ public class Reconstructor extends UnitBlock{
|
||||
public @Nullable Vec2 commandPos;
|
||||
public @Nullable UnitCommand command;
|
||||
|
||||
boolean constructing;
|
||||
|
||||
public float fraction(){
|
||||
return progress / constructTime;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldActiveSound(){
|
||||
return shouldConsume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec2 getCommandPosition(){
|
||||
return commandPos;
|
||||
@@ -290,6 +287,8 @@ public class Reconstructor extends UnitBlock{
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
//cache value to prevent repeated calls and multithreading issues
|
||||
constructing = constructing();
|
||||
boolean valid = false;
|
||||
|
||||
if(payload != null){
|
||||
@@ -338,7 +337,7 @@ public class Reconstructor extends UnitBlock{
|
||||
|
||||
@Override
|
||||
public boolean shouldConsume(){
|
||||
return constructing() && enabled;
|
||||
return constructing && enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -68,7 +68,7 @@ public class RepairTower extends Block{
|
||||
if(efficiency > 0){
|
||||
for(var target : targets){
|
||||
if(target.damaged()){
|
||||
target.heal(healAmount * efficiency);
|
||||
target.heal(healAmount * edelta());
|
||||
any = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ public class RepairTurret extends Block{
|
||||
}
|
||||
|
||||
consumePowerCond(powerUse, (RepairPointBuild entity) -> entity.target != null);
|
||||
updateClipRadius(repairRadius);
|
||||
updateClipRadius(repairRadius + tilesize);
|
||||
super.init();
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.Vars;
|
||||
import mindustry.ai.types.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
@@ -40,17 +41,19 @@ public class UnitAssembler extends PayloadBlock{
|
||||
public UnitType droneType = UnitTypes.assemblyDrone;
|
||||
public int dronesCreated = 4;
|
||||
public float droneConstructTime = 60f * 4f;
|
||||
public int[] capacities = {};
|
||||
|
||||
public Seq<AssemblerUnitPlan> plans = new Seq<>(4);
|
||||
|
||||
protected @Nullable ConsumePayloadDynamic consPayload;
|
||||
protected @Nullable ConsumeItemDynamic consItem;
|
||||
|
||||
public UnitAssembler(String name){
|
||||
super(name);
|
||||
update = solid = true;
|
||||
rotate = true;
|
||||
rotateDraw = false;
|
||||
acceptsPayload = true;
|
||||
acceptsPayload = hasItems = true;
|
||||
flags = EnumSet.of(BlockFlag.unitAssembler);
|
||||
regionRotated1 = 1;
|
||||
sync = true;
|
||||
@@ -96,6 +99,21 @@ public class UnitAssembler extends PayloadBlock{
|
||||
public void setBars(){
|
||||
super.setBars();
|
||||
|
||||
boolean planLiquids = false;
|
||||
for(int i = 0; i < plans.size; i++){
|
||||
var req = plans.get(i).liquidReq;
|
||||
if(req != null && req.length > 0){
|
||||
for(var stack : req){
|
||||
addLiquidBar(stack.liquid);
|
||||
}
|
||||
planLiquids = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(planLiquids){
|
||||
removeBar("liquid");
|
||||
}
|
||||
|
||||
addBar("progress", (UnitAssemblerBuild e) -> new Bar("bar.progress", Pal.ammo, () -> e.progress));
|
||||
|
||||
addBar("units", (UnitAssemblerBuild e) ->
|
||||
@@ -124,12 +142,31 @@ public class UnitAssembler extends PayloadBlock{
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
updateClipRadius(areaSize * tilesize);
|
||||
updateClipRadius((areaSize + 1) * tilesize);
|
||||
|
||||
consume(consPayload = new ConsumePayloadDynamic((UnitAssemblerBuild build) -> build.plan().requirements));
|
||||
consume(consItem = new ConsumeItemDynamic((UnitAssemblerBuild build) -> build.plan().itemReq != null ? build.plan().itemReq : ItemStack.empty));
|
||||
consume(new ConsumeLiquidsDynamic((UnitAssemblerBuild build) -> build.plan().liquidReq != null ? build.plan().liquidReq : LiquidStack.empty));
|
||||
|
||||
consumeBuilder.each(c -> c.multiplier = b -> state.rules.unitCost(b.team));
|
||||
|
||||
super.init();
|
||||
|
||||
capacities = new int[Vars.content.items().size];
|
||||
for(AssemblerUnitPlan plan : plans){
|
||||
if(plan.itemReq != null){
|
||||
for(ItemStack stack : plan.itemReq){
|
||||
capacities[stack.item.id] = Math.max(capacities[stack.item.id], stack.amount * 2);
|
||||
itemCapacity = Math.max(itemCapacity, stack.amount * 2);
|
||||
}
|
||||
}
|
||||
|
||||
if(plan.liquidReq != null){
|
||||
for(LiquidStack stack : plan.liquidReq){
|
||||
liquidFilter[stack.liquid.id] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -163,16 +200,40 @@ public class UnitAssembler extends PayloadBlock{
|
||||
}).left();
|
||||
|
||||
t.table(req -> {
|
||||
req.right();
|
||||
for(int i = 0; i < plan.requirements.size; i++){
|
||||
if(i % 6 == 0){
|
||||
req.row();
|
||||
req.add().grow(); //it refuses to go to the right unless I do this. please help.
|
||||
|
||||
req.table(solid -> {
|
||||
int length = 0;
|
||||
if(plan.itemReq != null){
|
||||
for(int i = 0; i < plan.itemReq.length; i++){
|
||||
if(length % 6 == 0){
|
||||
solid.row();
|
||||
}
|
||||
solid.add(StatValues.stack(plan.itemReq[i])).pad(5);
|
||||
length++;
|
||||
}
|
||||
}
|
||||
|
||||
PayloadStack stack = plan.requirements.get(i);
|
||||
req.add(StatValues.stack(stack)).pad(5);
|
||||
for(int i = 0; i < plan.requirements.size; i++){
|
||||
if(length % 6 == 0){
|
||||
solid.row();
|
||||
}
|
||||
solid.add(StatValues.stack(plan.requirements.get(i))).pad(5);
|
||||
length++;
|
||||
}
|
||||
}).right();
|
||||
|
||||
LiquidStack[] stacks = plan.liquidReq;
|
||||
if(stacks != null){
|
||||
for(int i = 0; i < plan.liquidReq.length; i++){
|
||||
req.row();
|
||||
|
||||
req.add().grow(); //another one.
|
||||
|
||||
req.add(StatValues.displayLiquid(stacks[i].liquid, stacks[i].amount * 60f, true)).right();
|
||||
}
|
||||
}
|
||||
}).right().grow().pad(10f);
|
||||
}).grow().pad(10f);
|
||||
}else{
|
||||
t.image(Icon.lock).color(Pal.darkerGray).size(40).pad(10);
|
||||
}
|
||||
@@ -185,7 +246,9 @@ public class UnitAssembler extends PayloadBlock{
|
||||
|
||||
public static class AssemblerUnitPlan{
|
||||
public UnitType unit;
|
||||
public Seq<PayloadStack> requirements;
|
||||
@Nullable public Seq<PayloadStack> requirements;
|
||||
@Nullable public ItemStack[] itemReq;
|
||||
@Nullable public LiquidStack[] liquidReq;
|
||||
public float time;
|
||||
|
||||
public AssemblerUnitPlan(UnitType unit, float time, Seq<PayloadStack> requirements){
|
||||
@@ -296,7 +359,7 @@ public class UnitAssembler extends PayloadBlock{
|
||||
@Override
|
||||
public boolean shouldConsume(){
|
||||
//liquid is only consumed when building is being done
|
||||
return enabled && !wasOccupied && Units.canCreate(team, plan().unit) && consPayload.efficiency(this) > 0;
|
||||
return enabled && !wasOccupied && Units.canCreate(team, plan().unit) && consPayload.efficiency(this) > 0 && consItem.efficiency(this) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -452,7 +515,7 @@ public class UnitAssembler extends PayloadBlock{
|
||||
unit.command().commandPosition(commandPos);
|
||||
}
|
||||
unit.set(spawn.x + Mathf.range(0.001f), spawn.y + Mathf.range(0.001f));
|
||||
unit.rotation = 90f;
|
||||
unit.rotation = rotdeg();
|
||||
unit.add();
|
||||
}
|
||||
|
||||
@@ -498,18 +561,19 @@ public class UnitAssembler extends PayloadBlock{
|
||||
var plan = plan();
|
||||
|
||||
//draw the unit construction as outline
|
||||
//TODO flashes when no gallium
|
||||
Draw.draw(Layer.blockBuilding, () -> {
|
||||
Draw.color(Pal.accent, warmup);
|
||||
|
||||
Shaders.blockbuild.region = plan.unit.fullIcon;
|
||||
Shaders.blockbuild.time = Time.time;
|
||||
Shaders.blockbuild.alpha = warmup;
|
||||
//margin due to units not taking up whole region
|
||||
Shaders.blockbuild.progress = Mathf.clamp(progress + 0.05f);
|
||||
|
||||
Draw.rect(plan.unit.fullIcon, sx, sy);
|
||||
Draw.rect(plan.unit.fullIcon, sx, sy, rotdeg() - 90f);
|
||||
Draw.flush();
|
||||
Draw.color();
|
||||
Shaders.blockbuild.alpha = 1f;
|
||||
});
|
||||
|
||||
Draw.reset();
|
||||
@@ -519,7 +583,7 @@ public class UnitAssembler extends PayloadBlock{
|
||||
//draw unit silhouette
|
||||
Draw.mixcol(Tmp.c1.set(Pal.accent).lerp(Pal.remove, invalidWarmup), 1f);
|
||||
Draw.alpha(Math.min(powerWarmup, sameTypeWarmup));
|
||||
Draw.rect(plan.unit.fullIcon, spawn.x, spawn.y);
|
||||
Draw.rect(plan.unit.fullIcon, spawn.x, spawn.y, rotdeg() - 90f);
|
||||
|
||||
//build beams do not draw when invalid
|
||||
Draw.alpha(Math.min(1f - invalidWarmup, warmup));
|
||||
@@ -601,6 +665,17 @@ public class UnitAssembler extends PayloadBlock{
|
||||
plan.requirements.contains(b -> b.item == payload.content() && blocks.get(payload.content()) < Mathf.round(b.amount * state.rules.unitCost(team)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaximumAccepted(Item item){
|
||||
return Mathf.round(capacities[item.id] * state.rules.unitCost(team));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptItem(Building source, Item item){
|
||||
return plan().itemReq != null && items.get(item) < getMaximumAccepted(item) &&
|
||||
Structs.contains(plan().itemReq, stack -> stack.item == item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec2 getCommandPosition(){
|
||||
return commandPos;
|
||||
|
||||
@@ -53,13 +53,13 @@ public class UnitAssemblerModule extends PayloadBlock{
|
||||
@Override
|
||||
public void drawPlanRegion(BuildPlan plan, Eachable<BuildPlan> list){
|
||||
Draw.rect(region, plan.drawx(), plan.drawy());
|
||||
Draw.rect(plan.rotation >= 2 ? sideRegion2 : sideRegion1, plan.drawx(), plan.drawy(), plan.rotation * 90);
|
||||
drawSideRegion(plan.rotation >= 2 ? sideRegion2 : sideRegion1, plan.drawx(), plan.drawy(), plan.rotation);
|
||||
Draw.rect(topRegion, plan.drawx(), plan.drawy());
|
||||
}
|
||||
|
||||
@Override
|
||||
public TextureRegion[] icons(){
|
||||
return new TextureRegion[]{region, sideRegion1, topRegion};
|
||||
return new TextureRegion[]{region, topRegion};
|
||||
}
|
||||
|
||||
public @Nullable UnitAssemblerBuild getLink(Team team, int x, int y, int rotation){
|
||||
@@ -93,11 +93,11 @@ public class UnitAssemblerModule extends PayloadBlock{
|
||||
//draw input conveyors
|
||||
for(int i = 0; i < 4; i++){
|
||||
if(blends(i) && i != rotation){
|
||||
Draw.rect(inRegion, x, y, (i * 90) - 180);
|
||||
drawSideRegion(inRegion, x, y, i - 2);
|
||||
}
|
||||
}
|
||||
|
||||
Draw.rect(rotation >= 2 ? sideRegion2 : sideRegion1, x, y, rotdeg());
|
||||
drawSideRegion(rotation >= 2 ? sideRegion2 : sideRegion1, x, y, rotation);
|
||||
|
||||
Draw.z(Layer.blockOver);
|
||||
payRotation = rotdeg();
|
||||
|
||||
@@ -22,7 +22,7 @@ import static mindustry.Vars.*;
|
||||
|
||||
public class UnitCargoLoader extends Block{
|
||||
public UnitType unitType = UnitTypes.manifold;
|
||||
public float buildTime = 60f * 8f;
|
||||
public float unitBuildTime = 60f * 8f;
|
||||
|
||||
public float polyStroke = 1.8f, polyRadius = 8f;
|
||||
public int polySides = 6;
|
||||
@@ -106,7 +106,7 @@ public class UnitCargoLoader extends Block{
|
||||
readyness = Mathf.approachDelta(readyness, unit != null ? 1f : 0f, 1f / 60f);
|
||||
|
||||
if(unit == null && Units.canCreate(team, unitType)){
|
||||
buildProgress += edelta() / buildTime;
|
||||
buildProgress += edelta() / unitBuildTime;
|
||||
totalProgress += edelta();
|
||||
|
||||
if(buildProgress >= 1f){
|
||||
@@ -142,11 +142,6 @@ public class UnitCargoLoader extends Block{
|
||||
return unit == null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldActiveSound(){
|
||||
return shouldConsume() && warmup > 0.01f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Draw.rect(block.region, x, y);
|
||||
|
||||
@@ -50,6 +50,12 @@ public class UnitCargoUnloadPoint extends Block{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawSelect(){
|
||||
super.drawSelect();
|
||||
drawItemSelection(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
super.updateTile();
|
||||
|
||||
@@ -55,7 +55,7 @@ public class UnitFactory extends UnitBlock{
|
||||
if(build.currentPlan == i) return;
|
||||
build.currentPlan = i < 0 || i >= plans.size ? -1 : i;
|
||||
build.progress = 0;
|
||||
if(build.command != null && !build.unit().commands.contains(build.command)){
|
||||
if(build.command != null && (build.unit() == null || !build.unit().commands.contains(build.command))){
|
||||
build.command = null;
|
||||
}
|
||||
});
|
||||
@@ -236,11 +236,6 @@ public class UnitFactory extends UnitBlock{
|
||||
return super.senseObject(sensor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldActiveSound(){
|
||||
return shouldConsume();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double sense(LAccess sensor){
|
||||
if(sensor == LAccess.progress) return Mathf.clamp(fraction());
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package mindustry.world.consumers;
|
||||
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
/** For mods. I don't use this (yet). */
|
||||
public class ConsumeItemCharged extends ConsumeItemFilter{
|
||||
public class ConsumeItemCharged extends ConsumeItemEfficiency{
|
||||
public float minCharge;
|
||||
|
||||
public ConsumeItemCharged(float minCharge){
|
||||
@@ -16,8 +16,7 @@ public class ConsumeItemCharged extends ConsumeItemFilter{
|
||||
}
|
||||
|
||||
@Override
|
||||
public float efficiencyMultiplier(Building build){
|
||||
var item = getConsumed(build);
|
||||
return item == null ? 0f : item.charge;
|
||||
public float itemEfficiencyMultiplier(Item item){
|
||||
return item.charge;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package mindustry.world.consumers;
|
||||
|
||||
import arc.func.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
public class ConsumeItemEfficiency extends ConsumeItemFilter{
|
||||
/** This has no effect on the consumer itself, but is used for stat display. */
|
||||
public @Nullable ObjectFloatMap<Item> itemDurationMultipliers;
|
||||
|
||||
public ConsumeItemEfficiency(Boolf<Item> item){
|
||||
super(item);
|
||||
}
|
||||
|
||||
public ConsumeItemEfficiency(){
|
||||
}
|
||||
|
||||
@Override
|
||||
public void display(Stats stats){
|
||||
stats.add(booster ? Stat.booster : Stat.input, StatValues.itemEffMultiplier(this::itemEfficiencyMultiplier, stats.timePeriod, filter, itemDurationMultipliers));
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
package mindustry.world.consumers;
|
||||
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
public class ConsumeItemExplosive extends ConsumeItemFilter{
|
||||
public class ConsumeItemExplosive extends ConsumeItemEfficiency{
|
||||
public float minExplosiveness;
|
||||
|
||||
public ConsumeItemExplosive(float minCharge){
|
||||
@@ -15,8 +15,7 @@ public class ConsumeItemExplosive extends ConsumeItemFilter{
|
||||
}
|
||||
|
||||
@Override
|
||||
public float efficiencyMultiplier(Building build){
|
||||
var item = getConsumed(build);
|
||||
return item == null ? 0f : item.explosiveness;
|
||||
public float itemEfficiencyMultiplier(Item item){
|
||||
return item.explosiveness;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,6 +66,16 @@ public class ConsumeItemFilter extends Consume{
|
||||
|
||||
@Override
|
||||
public void display(Stats stats){
|
||||
stats.add(booster ? Stat.booster : Stat.input, stats.timePeriod < 0 ? StatValues.items(filter) : StatValues.items(stats.timePeriod, filter));
|
||||
stats.add(booster ? Stat.booster : Stat.input, StatValues.items(stats.timePeriod, filter));
|
||||
}
|
||||
|
||||
@Override
|
||||
public float efficiencyMultiplier(Building build){
|
||||
var item = getConsumed(build);
|
||||
return item == null ? 0f : itemEfficiencyMultiplier(item);
|
||||
}
|
||||
|
||||
public float itemEfficiencyMultiplier(Item item){
|
||||
return 1f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package mindustry.world.consumers;
|
||||
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
public class ConsumeItemFlammable extends ConsumeItemFilter{
|
||||
public class ConsumeItemFlammable extends ConsumeItemEfficiency{
|
||||
public float minFlammability;
|
||||
|
||||
public ConsumeItemFlammable(float minFlammability){
|
||||
@@ -15,8 +15,7 @@ public class ConsumeItemFlammable extends ConsumeItemFilter{
|
||||
}
|
||||
|
||||
@Override
|
||||
public float efficiencyMultiplier(Building build){
|
||||
var item = getConsumed(build);
|
||||
return item == null ? 0f : item.flammability;
|
||||
public float itemEfficiencyMultiplier(Item item){
|
||||
return item.flammability;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package mindustry.world.consumers;
|
||||
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
public class ConsumeItemRadioactive extends ConsumeItemFilter{
|
||||
public class ConsumeItemRadioactive extends ConsumeItemEfficiency{
|
||||
public float minRadioactivity;
|
||||
|
||||
public ConsumeItemRadioactive(float minRadioactivity){
|
||||
@@ -15,8 +15,7 @@ public class ConsumeItemRadioactive extends ConsumeItemFilter{
|
||||
}
|
||||
|
||||
@Override
|
||||
public float efficiencyMultiplier(Building build){
|
||||
var item = getConsumed(build);
|
||||
return item == null ? 0f : item.radioactivity;
|
||||
public float itemEfficiencyMultiplier(Item item){
|
||||
return item.radioactivity;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ public class ConsumeLiquid extends ConsumeLiquidBase{
|
||||
this(null, 0f);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void apply(Block block){
|
||||
super.apply(block);
|
||||
|
||||
@@ -54,6 +54,12 @@ public class ConsumeLiquidFilter extends ConsumeLiquidBase{
|
||||
return liq != null ? Math.min(build.liquids.get(liq) / (amount * ed * multiplier.get(build)), 1f) : 0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float efficiencyMultiplier(Building build){
|
||||
var liq = getConsumed(build);
|
||||
return liq == null ? 0 : liquidEfficiencyMultiplier(liq);
|
||||
}
|
||||
|
||||
public @Nullable Liquid getConsumed(Building build){
|
||||
if(filter.get(build.liquids.current()) && build.liquids.currentAmount() > 0){
|
||||
return build.liquids.current();
|
||||
@@ -79,4 +85,8 @@ public class ConsumeLiquidFilter extends ConsumeLiquidBase{
|
||||
public boolean consumes(Liquid liquid){
|
||||
return filter.get(liquid);
|
||||
}
|
||||
|
||||
public float liquidEfficiencyMultiplier(Liquid liquid){
|
||||
return 1f;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package mindustry.world.consumers;
|
||||
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
public class ConsumeLiquidFlammable extends ConsumeLiquidFilter{
|
||||
public float minFlammability;
|
||||
@@ -20,8 +21,12 @@ public class ConsumeLiquidFlammable extends ConsumeLiquidFilter{
|
||||
}
|
||||
|
||||
@Override
|
||||
public float efficiencyMultiplier(Building build){
|
||||
var liq = getConsumed(build);
|
||||
return liq == null ? 0f : liq.flammability;
|
||||
public void display(Stats stats){
|
||||
stats.add(booster ? Stat.booster : Stat.input, StatValues.liquidEffMultiplier(l -> l.flammability, amount * 60f, filter));
|
||||
}
|
||||
|
||||
@Override
|
||||
public float liquidEfficiencyMultiplier(Liquid liquid){
|
||||
return liquid.flammability;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package mindustry.world.consumers;
|
||||
|
||||
import arc.func.Func;
|
||||
import arc.scene.ui.layout.Table;
|
||||
import mindustry.Vars;
|
||||
import mindustry.gen.Building;
|
||||
import mindustry.type.LiquidStack;
|
||||
import mindustry.ui.ReqImage;
|
||||
import mindustry.world.Block;
|
||||
import arc.func.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import mindustry.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
public class ConsumeLiquidsDynamic extends Consume{
|
||||
public final Func<Building, LiquidStack[]> liquids;
|
||||
|
||||
@@ -43,7 +43,7 @@ public class ConsumePower extends Consume{
|
||||
public void display(Stats stats){
|
||||
if(buffered){
|
||||
stats.add(Stat.powerCapacity, capacity, StatUnit.none);
|
||||
}else{
|
||||
}else if(usage > 0f){
|
||||
stats.add(Stat.powerUse, usage * 60f, StatUnit.powerSecond);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import mindustry.gen.*;
|
||||
public class DrawArcSmelt extends DrawBlock{
|
||||
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 x = 0, y = 0;
|
||||
public float alpha = 0.68f;
|
||||
public int particles = 25;
|
||||
public float particleLife = 40f, particleRad = 7f, particleStroke = 1.1f, particleLen = 3f;
|
||||
@@ -26,10 +26,10 @@ public class DrawArcSmelt extends DrawBlock{
|
||||
Draw.blend(blending);
|
||||
|
||||
Draw.color(midColor, a);
|
||||
if(drawCenter) Fill.circle(build.x, build.y, flameRad + si);
|
||||
if(drawCenter) Fill.circle(build.x + x, build.y + y, flameRad + si);
|
||||
|
||||
Draw.color(flameColor, a);
|
||||
if(drawCenter) Lines.circle(build.x, build.y, (flameRad + circleSpace + si) * build.warmup());
|
||||
if(drawCenter) Lines.circle(build.x + x, build.y + y, (flameRad + circleSpace + si) * build.warmup());
|
||||
|
||||
Lines.stroke(particleStroke * build.warmup());
|
||||
|
||||
@@ -39,7 +39,7 @@ public class DrawArcSmelt extends DrawBlock{
|
||||
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());
|
||||
Lines.lineAngle(build.x + Angles.trnsx(angle, len) + x, build.y + Angles.trnsy(angle, len) + y, angle, particleLen * fout * build.warmup());
|
||||
}
|
||||
|
||||
Draw.blend();
|
||||
|
||||
@@ -9,8 +9,7 @@ import mindustry.gen.*;
|
||||
|
||||
public class DrawCrucibleFlame extends DrawBlock{
|
||||
public Color flameColor = Color.valueOf("f58349"), midColor = Color.valueOf("f2d585");
|
||||
public float flameRad = 1f, circleSpace = 2f, flameRadiusScl = 10f, flameRadiusMag = 0.6f, circleStroke = 1.5f;
|
||||
|
||||
public float flameRad = 1f, circleSpace = 2f, flameRadiusScl = 10f, flameRadiusMag = 0.6f, circleStroke = 1.5f, x = 0, y = 0;
|
||||
public float alpha = 0.5f;
|
||||
public int particles = 30;
|
||||
public float particleLife = 70f, particleRad = 7f, particleSize = 3f, fadeMargin = 0.4f, rotateScl = 1.5f;
|
||||
@@ -27,10 +26,10 @@ public class DrawCrucibleFlame extends DrawBlock{
|
||||
Draw.blend(Blending.additive);
|
||||
|
||||
Draw.color(midColor, a);
|
||||
Fill.circle(build.x, build.y, flameRad + si);
|
||||
Fill.circle(build.x + x, build.y + y, flameRad + si);
|
||||
|
||||
Draw.color(flameColor, a);
|
||||
Lines.circle(build.x, build.y, (flameRad + circleSpace + si) * build.warmup());
|
||||
Lines.circle(build.x + x, build.y + y, (flameRad + circleSpace + si) * build.warmup());
|
||||
|
||||
float base = (Time.time / particleLife);
|
||||
rand.setSeed(build.id);
|
||||
@@ -40,8 +39,8 @@ public class DrawCrucibleFlame extends DrawBlock{
|
||||
float len = particleRad * particleInterp.apply(fout);
|
||||
Draw.alpha(a * (1f - Mathf.curve(fin, 1f - fadeMargin)));
|
||||
Fill.circle(
|
||||
build.x + Angles.trnsx(angle, len),
|
||||
build.y + Angles.trnsy(angle, len),
|
||||
build.x + Angles.trnsx(angle, len) + x,
|
||||
build.y + Angles.trnsy(angle, len) + y,
|
||||
particleSize * fin * build.warmup()
|
||||
);
|
||||
}
|
||||
|
||||
@@ -25,8 +25,9 @@ public class DrawFlame extends DrawBlock{
|
||||
|
||||
@Override
|
||||
public void load(Block block){
|
||||
block.emitLight = true;
|
||||
top = Core.atlas.find(block.name + "-top");
|
||||
block.clipSize = Math.max(block.clipSize, (lightRadius + lightSinMag) * 2f * block.size);
|
||||
block.lightClipSize = Math.max(block.lightClipSize, (lightRadius + lightSinMag) * 2f * block.size);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -10,24 +10,25 @@ import mindustry.gen.*;
|
||||
public class DrawParticles extends DrawBlock{
|
||||
public Color color = Color.valueOf("f2d585");
|
||||
|
||||
public int sides = 12;
|
||||
public float x = 0, y = 0;
|
||||
public float alpha = 0.5f;
|
||||
public int particles = 30;
|
||||
public float particleLife = 70f, particleRad = 7f, particleSize = 3f, fadeMargin = 0.4f, rotateScl = 3f;
|
||||
public boolean reverse = false;
|
||||
public float particleRotation = 0, particleLife = 70f, particleRad = 7f, particleSize = 3f, fadeMargin = 0.4f, rotateScl = 3f;
|
||||
public boolean reverse = false, poly = false;
|
||||
public Interp particleInterp = new PowIn(1.5f);
|
||||
public Interp particleSizeInterp = Interp.slope;
|
||||
public Blending blending = Blending.normal;
|
||||
|
||||
@Override
|
||||
public void draw(Building build){
|
||||
|
||||
if(build.warmup() > 0f){
|
||||
|
||||
float a = alpha * build.warmup();
|
||||
|
||||
Draw.blend(blending);
|
||||
Draw.color(color);
|
||||
|
||||
float base = (Time.time / particleLife);
|
||||
float base = Time.time / particleLife;
|
||||
rand.setSeed(build.id);
|
||||
for(int i = 0; i < particles; i++){
|
||||
float fin = (rand.random(2f) + base) % 1f;
|
||||
@@ -35,12 +36,23 @@ public class DrawParticles extends DrawBlock{
|
||||
float fout = 1f - fin;
|
||||
float angle = rand.random(360f) + (Time.time / rotateScl) % 360f;
|
||||
float len = particleRad * particleInterp.apply(fout);
|
||||
|
||||
Draw.alpha(a * (1f - Mathf.curve(fin, 1f - fadeMargin)));
|
||||
Fill.circle(
|
||||
build.x + Angles.trnsx(angle, len),
|
||||
build.y + Angles.trnsy(angle, len),
|
||||
particleSize * particleSizeInterp.apply(fin) * build.warmup()
|
||||
);
|
||||
if(poly){
|
||||
Fill.poly(
|
||||
build.x + x + Angles.trnsx(angle, len),
|
||||
build.y + y + Angles.trnsy(angle, len),
|
||||
sides,
|
||||
particleSize * particleSizeInterp.apply(fin) * build.warmup(),
|
||||
particleRotation
|
||||
);
|
||||
}else{
|
||||
Fill.circle(
|
||||
build.x + x + Angles.trnsx(angle, len),
|
||||
build.y + y + Angles.trnsy(angle, len),
|
||||
particleSize * particleSizeInterp.apply(fin) * build.warmup()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Draw.blend();
|
||||
|
||||
@@ -18,6 +18,8 @@ public class DrawPlasma extends DrawFlame{
|
||||
|
||||
@Override
|
||||
public void load(Block block){
|
||||
block.emitLight = true;
|
||||
|
||||
regions = new TextureRegion[plasmas];
|
||||
for(int i = 0; i < regions.length; i++){
|
||||
regions[i] = Core.atlas.find(block.name + suffix + i);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user