diff --git a/core/assets/shaders/shockwave.frag b/core/assets/shaders/shockwave.frag new file mode 100644 index 0000000000..1c5803075b --- /dev/null +++ b/core/assets/shaders/shockwave.frag @@ -0,0 +1,42 @@ +#define MAX_SHOCKWAVES 64 +#define WAVE_RADIUS 4.0 +#define DIFF_SCL 1 +#define WAVE_POW 0.8 + +varying vec2 v_texCoords; + +uniform sampler2D u_texture; +uniform vec2 u_resolution; +uniform vec2 u_campos; +uniform vec4 u_shockwaves[MAX_SHOCKWAVES]; +uniform int u_shockwave_count; + +void main(){ + vec2 worldCoords = v_texCoords * u_resolution + u_campos; + vec2 uv = v_texCoords; + vec2 displacement = vec2(0.0, 0.0); + + for(int i = 0; i < MAX_SHOCKWAVES; i ++){ + vec4 wave = u_shockwaves[i]; + float radius = wave.z; + float strength = wave.w; + float dst = distance(worldCoords, wave.xy); + float realStrength = 1.0 - pow(1.0 - strength, 5.0); + + if(abs(dst - radius) <= WAVE_RADIUS){ + float diff = dst - radius; + float pdiff = 1.0 - pow(abs(diff * DIFF_SCL), WAVE_POW); + float diffTime = diff * pdiff; + vec2 relative = normalize(worldCoords - wave.xy); + + displacement += (relative * diffTime * strength) / u_resolution; + } + + if(i >= u_shockwave_count - 1){ + break; + } + } + + vec4 c = texture2D(u_texture, uv + displacement); + gl_FragColor = c; +} \ No newline at end of file diff --git a/core/src/mindustry/entities/comp/BuildingComp.java b/core/src/mindustry/entities/comp/BuildingComp.java index 13aee3cf65..994a2eaa9a 100644 --- a/core/src/mindustry/entities/comp/BuildingComp.java +++ b/core/src/mindustry/entities/comp/BuildingComp.java @@ -85,6 +85,8 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, transient float efficiency; /** Same as efficiency, but for optional consumers only. */ transient float optionalEfficiency; + /** The efficiency this block would have if consValid() / productionValid() returned true. */ + transient float potentialEfficiency; transient float healSuppressionTime = -1f; transient float lastHealTime = -120f * 10f; @@ -224,7 +226,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, //version 3 has efficiency numbers instead of bools if(version >= 3){ - efficiency = read.ub() / 255f; + efficiency = potentialEfficiency = read.ub() / 255f; optionalEfficiency = read.ub() / 255f; } @@ -1640,7 +1642,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, } public boolean canConsume(){ - return efficiency > 0; + return potentialEfficiency > 0; } /** Scaled delta. */ @@ -1671,40 +1673,43 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, public void updateConsumption(){ //everything is valid when cheating if(!block.hasConsumers || cheating()){ - efficiency = optionalEfficiency = 1f; + potentialEfficiency = efficiency = optionalEfficiency = 1f; return; } //disabled -> nothing works if(!enabled){ - efficiency = optionalEfficiency = 0f; + potentialEfficiency = efficiency = optionalEfficiency = 0f; return; } //TODO why check for old state? boolean prevValid = efficiency > 0, update = shouldConsume() && productionValid(); - if(update){ - float minEfficiency = 1f; + float minEfficiency = 1f; - //assume efficiency is 1 for the calculations below - efficiency = optionalEfficiency = 1f; + //assume efficiency is 1 for the calculations below + efficiency = optionalEfficiency = 1f; - //first pass: get the minimum efficiency of any consumer - for(var cons : block.nonOptionalConsumers){ - minEfficiency = Math.min(minEfficiency, cons.efficiency(self())); - } + //first pass: get the minimum efficiency of any consumer + for(var cons : block.nonOptionalConsumers){ + minEfficiency = Math.min(minEfficiency, cons.efficiency(self())); + } - //same for optionals - for(var cons : block.optionalConsumers){ - optionalEfficiency = Math.min(optionalEfficiency, cons.efficiency(self())); - } + //same for optionals + for(var cons : block.optionalConsumers){ + optionalEfficiency = Math.min(optionalEfficiency, cons.efficiency(self())); + } - //efficiency is now this minimum value - efficiency = minEfficiency; - optionalEfficiency = Math.min(optionalEfficiency, minEfficiency); - }else{ - //should not consume, efficiency now zero + //efficiency is now this minimum value + efficiency = minEfficiency; + optionalEfficiency = Math.min(optionalEfficiency, minEfficiency); + + //assign "potential" + potentialEfficiency = efficiency; + + //no updating means zero efficiency + if(!update){ efficiency = optionalEfficiency = 0f; } diff --git a/core/src/mindustry/graphics/Shaders.java b/core/src/mindustry/graphics/Shaders.java index b4617f0f96..3119fc7767 100644 --- a/core/src/mindustry/graphics/Shaders.java +++ b/core/src/mindustry/graphics/Shaders.java @@ -9,7 +9,9 @@ import arc.graphics.g3d.*; import arc.graphics.gl.*; import arc.math.geom.*; import arc.scene.ui.layout.*; +import arc.struct.*; import arc.util.*; +import mindustry.game.EventType.*; import mindustry.type.*; import static mindustry.Vars.*; @@ -28,6 +30,7 @@ public class Shaders{ public static CloudShader clouds; public static PlanetGridShader planetGrid; public static AtmosphereShader atmosphere; + public static ShockwaveShader shockwave; public static MeshShader mesh; public static Shader unlit; public static Shader screenspace; @@ -67,6 +70,9 @@ public class Shaders{ atmosphere = new AtmosphereShader(); unlit = new LoadShader("planet", "unlit"); screenspace = new LoadShader("screenspace", "screenspace"); + + //disabled for now... + //shockwave = new ShockwaveShader(); } public static class AtmosphereShader extends LoadShader{ @@ -350,6 +356,112 @@ public class Shaders{ } } + public static class ShockwaveShader extends LoadShader{ + static final int max = 64; + static final int size = 5; + static final String[] uniformNames = new String[max]; + + //x y radius life[1-0] lifetime + protected FloatSeq data = new FloatSeq(); + protected boolean hadAny = false; + protected FrameBuffer buffer = new FrameBuffer(); + + public float lifetime = 20f; + + static{ + for(int i = 0; i < max; i++){ + uniformNames[i] = "u_shockwaves[" + i + "]"; + } + } + + public ShockwaveShader(){ + super("shockwave", "screenspace"); + + Events.run(Trigger.update, () -> { + if(state.isPaused()) return; + if(state.isMenu()){ + data.size = 0; + return; + } + + var items = data.items; + for(int i = 0; i < data.size; i += size){ + //decrease lifetime + items[i + 3] -= Time.delta / items[i + 4]; + + if(items[i + 3] <= 0f){ + //swap with head. + if(data.size > size){ + System.arraycopy(items, data.size - size, items, i, size); + } + + data.size -= size; + i -= size; + } + } + }); + + Events.run(Trigger.preDraw, () -> { + hadAny = data.size > 0; + + if(hadAny){ + buffer.resize(Core.graphics.getWidth(), Core.graphics.getHeight()); + buffer.begin(Color.clear); + } + }); + + Events.run(Trigger.postDraw, () -> { + if(hadAny){ + buffer.end(); + Draw.blend(Blending.disabled); + buffer.blit(this); + Draw.blend(); + } + }); + } + + @Override + public void apply(){ + int count = data.size / 4; + + setUniformi("u_shockwave_count", count); + if(count > 0){ + setUniformf("u_resolution", Core.camera.width, Core.camera.height); + setUniformf("u_campos", Core.camera.position.x - Core.camera.width/2f, Core.camera.position.y - Core.camera.height/2f); + + var items = data.items; + for(int i = 0; i < count; i++){ + int offset = i * size; + + setUniformf(uniformNames[i], + items[offset], items[offset + 1], //xy + items[offset + 2] * (1f - items[offset + 3]), //radius * time + items[offset + 3] //time + //lifetime ignored + ); + } + } + } + + public void add(float x, float y, float radius){ + add(x, y, radius, 20f); + } + + public void add(float x, float y, float radius, float lifetime){ + //replace first entry + if(data.size / size >= max){ + var items = data.items; + items[0] = x; + items[1] = y; + items[2] = radius; + items[3] = 1f; + items[4] = lifetime; + }else{ + data.addAll(x, y, radius, 1f, lifetime); + } + } + } + public static class LoadShader extends Shader{ public LoadShader(String frag, String vert){ super(getShaderFi(vert + ".vert"), getShaderFi(frag + ".frag")); diff --git a/core/src/mindustry/world/blocks/defense/turrets/Turret.java b/core/src/mindustry/world/blocks/defense/turrets/Turret.java index 7f3038277f..726d701def 100644 --- a/core/src/mindustry/world/blocks/defense/turrets/Turret.java +++ b/core/src/mindustry/world/blocks/defense/turrets/Turret.java @@ -331,13 +331,13 @@ public class Turret extends ReloadTurret{ //turret always reloads regardless of whether it's targeting something updateReload(); - if(timer(timerTarget, targetInterval)){ - findTarget(); - } - if(hasAmmo()){ if(Float.isNaN(reloadCounter)) reloadCounter = 0; + if(timer(timerTarget, targetInterval)){ + findTarget(); + } + if(validateTarget()){ boolean canShoot = true; @@ -402,7 +402,7 @@ public class Turret extends ReloadTurret{ } protected void turnToTarget(float targetRot){ - rotation = Angles.moveToward(rotation, targetRot, rotateSpeed * delta() * baseReloadSpeed()); + rotation = Angles.moveToward(rotation, targetRot, rotateSpeed * delta() * potentialEfficiency); } public boolean shouldTurn(){ diff --git a/core/src/mindustry/world/blocks/storage/CoreBlock.java b/core/src/mindustry/world/blocks/storage/CoreBlock.java index 9dda4b57f3..7daf4d0e27 100644 --- a/core/src/mindustry/world/blocks/storage/CoreBlock.java +++ b/core/src/mindustry/world/blocks/storage/CoreBlock.java @@ -107,7 +107,7 @@ public class CoreBlock extends StorageBlock{ public void init(){ //assign to update clipSize internally lightRadius = 30f + 20f * size; - fogRadius = Math.max(fogRadius, (int)(lightRadius / 8f * 3f) + 5); + fogRadius = Math.max(fogRadius, (int)(lightRadius / 8f * 3f) + 13); emitLight = true; super.init();