From 63d8aed9a55af8f0f4596c936bef549b0dc10bb9 Mon Sep 17 00:00:00 2001 From: Anuken Date: Tue, 28 Nov 2017 01:00:59 -0500 Subject: [PATCH] Implemented shield hit effects and bullet absorption and powered rturret --- build.gradle | 3 +- core/assets/shaders/shield.fragment | 18 +++++++++- core/src/io/anuke/mindustry/Vars.java | 2 +- core/src/io/anuke/mindustry/core/Control.java | 7 ++-- .../src/io/anuke/mindustry/core/Renderer.java | 26 ++++++++++++++ .../mindustry/entities/effect/Shaders.java | 5 +++ .../mindustry/entities/effect/Shield.java | 34 +++++++++++------- .../mindustry/ui/fragments/HudFragment.java | 2 ++ .../blocks/types/defense/PowerTurret.java | 6 ++++ .../blocks/types/defense/RepairTurret.java | 8 ++++- .../blocks/types/defense/ShieldBlock.java | 29 +++++++++++---- desktop/mindustry-saves/0.mins | Bin 18939 -> 1976 bytes desktop/mindustry-saves/1.mins | Bin 2873 -> 2130 bytes 13 files changed, 115 insertions(+), 25 deletions(-) diff --git a/build.gradle b/build.gradle index d4fa2d5114..fec2213330 100644 --- a/build.gradle +++ b/build.gradle @@ -79,8 +79,7 @@ project(":core") { apply plugin: "java" dependencies { - compile 'com.github.Anuken:ucore:d43f3b48ec' - //compile fileTree(dir: '../core/lib', include: '*.jar') + // compile 'com.github.Anuken:ucore:61c43bf' compile "com.badlogicgames.gdx:gdx:$gdxVersion" compile "com.badlogicgames.gdx:gdx-ai:1.8.1" } diff --git a/core/assets/shaders/shield.fragment b/core/assets/shaders/shield.fragment index e2cdb0dfc1..eed64e553c 100644 --- a/core/assets/shaders/shield.fragment +++ b/core/assets/shaders/shield.fragment @@ -3,6 +3,9 @@ precision mediump float; precision mediump int; #endif +#define MAX_HITS 64 +#define HIT_RADIUS 12.0 + uniform sampler2D u_texture; uniform vec4 u_color; @@ -10,6 +13,8 @@ uniform vec2 u_texsize; uniform float u_time; uniform float u_scaling; uniform vec2 u_offset; +uniform int u_hitamount; +uniform vec3 u_hits[MAX_HITS]; varying vec4 v_color; varying vec2 v_texCoord; @@ -49,8 +54,19 @@ void main() { } color.a = 0.18; + + for(int i = 0; i < u_hitamount; i ++){ + vec3 hit = u_hits[i]; + float rad = hit.z * HIT_RADIUS; + float fract = 1.0 - hit.z; + + if(abs(distance(vec2(hit.x, hit.y), coords - u_texsize/2.0) - rad) < 1.0){ + color = mix(color, u_color* vec4(si, si, si, 1.0), (1.0 * fract)); + color.a = 0.18 + 0.82 *fract; + } + } } - + gl_FragColor = color; } } diff --git a/core/src/io/anuke/mindustry/Vars.java b/core/src/io/anuke/mindustry/Vars.java index a414bb24f4..734a962d5e 100644 --- a/core/src/io/anuke/mindustry/Vars.java +++ b/core/src/io/anuke/mindustry/Vars.java @@ -42,7 +42,7 @@ public class Vars{ //whether to draw chunk borders public static boolean debugChunks = false; //whether turrets have infinite ammo (only with debug) - public static boolean infiniteAmmo = true; + public static boolean infiniteAmmo = false; //whether to show paths of enemies public static boolean showPaths = true; //number of save slots-- increasing may lead to layout issues diff --git a/core/src/io/anuke/mindustry/core/Control.java b/core/src/io/anuke/mindustry/core/Control.java index 11d41b6fc3..7d78f2e6c6 100644 --- a/core/src/io/anuke/mindustry/core/Control.java +++ b/core/src/io/anuke/mindustry/core/Control.java @@ -59,9 +59,12 @@ public class Control extends Module{ float respawntime; public Control(){ - if(Mindustry.args.contains("-debug", false)){ + if(Mindustry.args.contains("-debug", false)) Vars.debug = true; - } + if(Mindustry.args.contains("-profile", false)) + Vars.profile = true; + if(Mindustry.args.contains("-debugGL", false)) + Vars.debugGL = true; UCore.log("Total blocks loaded: " + Block.getAllBlocks().size); diff --git a/core/src/io/anuke/mindustry/core/Renderer.java b/core/src/io/anuke/mindustry/core/Renderer.java index eba3b1d418..9f8a956a53 100644 --- a/core/src/io/anuke/mindustry/core/Renderer.java +++ b/core/src/io/anuke/mindustry/core/Renderer.java @@ -11,6 +11,7 @@ import com.badlogic.gdx.graphics.Texture; import com.badlogic.gdx.graphics.profiling.GLProfiler; import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.math.Vector2; +import com.badlogic.gdx.utils.FloatArray; import io.anuke.mindustry.Vars; import io.anuke.mindustry.core.GameState.State; @@ -39,6 +40,8 @@ public class Renderer extends RendererModule{ int targetscale = baseCameraScale; int chunksize = 32; Cache[][] floorCache; + FloatArray shieldHits = new FloatArray(); + float shieldHitDuration = 18f; public Renderer() { Core.cameraScale = baseCameraScale; @@ -127,6 +130,10 @@ public class Renderer extends RendererModule{ drawDefault(); Profiler.end("draw"); + if(Profiler.updating()) + Profiler.getTimes().put("draw", Profiler.getTimes().get("draw") + - Profiler.getTimes().get("blockDraw") + - Profiler.getTimes().get("entityDraw")); if(Vars.debug && Vars.debugGL && Timers.get("profile", 60)){ UCore.log("shaders: " + GLProfiler.shaderSwitches, "calls: " + GLProfiler.drawCalls, "bindings: " + GLProfiler.textureBindings, "vertices: " + GLProfiler.vertexCount.average); @@ -195,11 +202,26 @@ public class Renderer extends RendererModule{ } void drawShield(){ + for(int i = 0; i < shieldHits.size/3; i ++){ + //float x = hits.get(i*3+0); + //float y = hits.get(i*3+1); + float time = shieldHits.get(i*3+2); + + time += Timers.delta() / shieldHitDuration; + shieldHits.set(i*3 + 2, time); + + if(time >= 1f){ + shieldHits.removeRange(i*3, i*3 + 2); + i --; + } + } + Texture texture = Graphics.getSurface("shield").texture(); Shaders.shield.color.set(Color.SKY); Tmp.tr2.setRegion(texture); Shaders.shield.region = Tmp.tr2; + Shaders.shield.hits = shieldHits; Graphics.end(); Graphics.shader(Shaders.shield); @@ -211,6 +233,10 @@ public class Renderer extends RendererModule{ Graphics.end(); Graphics.beginCam(); } + + public void addShieldHit(float x, float y){ + shieldHits.addAll(x, y, 0f); + } void renderTiles(){ int chunksx = world.width() / chunksize, chunksy = world.height() / chunksize; diff --git a/core/src/io/anuke/mindustry/entities/effect/Shaders.java b/core/src/io/anuke/mindustry/entities/effect/Shaders.java index e21609fe1c..4d40b2c007 100644 --- a/core/src/io/anuke/mindustry/entities/effect/Shaders.java +++ b/core/src/io/anuke/mindustry/entities/effect/Shaders.java @@ -1,6 +1,7 @@ package io.anuke.mindustry.entities.effect; import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.utils.FloatArray; import io.anuke.ucore.core.Core; import io.anuke.ucore.core.Settings; @@ -28,7 +29,9 @@ public class Shaders{ } public static class Shield extends Shader{ + public static final int MAX_HITS = 3*64; public Color color = new Color(); + public FloatArray hits; public Shield(){ super("shield", "default"); @@ -38,6 +41,8 @@ public class Shaders{ public void apply(){ float scale = Settings.getBool("pixelate") ? 1 : Core.cameraScale / Core.camera.zoom; float scaling = Core.cameraScale / 4f / Core.camera.zoom; + shader.setUniform3fv("u_hits[0]", hits.items, 0, Math.min(hits.size, MAX_HITS)); + shader.setUniformi("u_hitamount", Math.min(hits.size, MAX_HITS)/3); shader.setUniformf("u_color", color); shader.setUniformf("u_time", Timers.time()); shader.setUniformf("u_scaling", scaling); diff --git a/core/src/io/anuke/mindustry/entities/effect/Shield.java b/core/src/io/anuke/mindustry/entities/effect/Shield.java index 2e4597aa4e..1f1193e1f3 100644 --- a/core/src/io/anuke/mindustry/entities/effect/Shield.java +++ b/core/src/io/anuke/mindustry/entities/effect/Shield.java @@ -1,7 +1,10 @@ package io.anuke.mindustry.entities.effect; import com.badlogic.gdx.graphics.Color; +import com.badlogic.gdx.math.Interpolation; +import io.anuke.mindustry.entities.Bullet; +import io.anuke.mindustry.entities.enemies.Enemy; import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.blocks.types.defense.ShieldBlock; import io.anuke.ucore.core.Draw; @@ -14,6 +17,8 @@ import io.anuke.ucore.util.Mathf; public class Shield extends Entity{ public boolean active; + public boolean hitPlayers = true; + private float uptime = 0f; private final Tile tile; //TODO @@ -30,11 +35,14 @@ public class Shield extends Entity{ @Override public void update(){ + float alpha = 0.1f; + Interpolation interp = Interpolation.fade; + if(active){ - uptime += Timers.delta() / 90f; + uptime = interp.apply(uptime, 1f, alpha * Timers.delta()); }else{ - uptime -= Timers.delta() / 60f; - if(uptime < 0) + uptime = interp.apply(uptime, 0f, alpha * Timers.delta()); + if(uptime <= 0.05f) remove(); } uptime = Mathf.clamp(uptime); @@ -46,14 +54,14 @@ public class Shield extends Entity{ ShieldBlock block = (ShieldBlock)tile.block(); - Entities.getNearby(x, y, block.shieldRadius * 2*uptime + 10, entity->{ - if(entity instanceof BulletEntity){ - BulletEntity bullet = (BulletEntity)entity; + Entities.getNearby(Entities.getGroup(Bullet.class), x, y, block.shieldRadius * 2*uptime + 10, entity->{ + BulletEntity bullet = (BulletEntity)entity; + if((bullet.owner instanceof Enemy || hitPlayers)){ float dst = entity.distanceTo(this); - if(Math.abs(dst - block.shieldRadius) < 2){ - bullet.velocity.scl(-1); + if(dst < drawRadius()/2f){ + ((ShieldBlock)tile.block()).handleBullet(tile, bullet); } } }); @@ -65,10 +73,7 @@ public class Shield extends Entity{ return; } - ShieldBlock block = (ShieldBlock)tile.block(); - - float rad = block.shieldRadius*2 + Mathf.sin(Timers.time(), 25f, 2f); - rad *= uptime; + float rad = drawRadius(); Graphics.surface("shield", false); Draw.color(Color.ROYAL); @@ -78,6 +83,11 @@ public class Shield extends Entity{ Graphics.surface(); } + float drawRadius(){ + ShieldBlock block = (ShieldBlock)tile.block(); + return (block.shieldRadius*2 + Mathf.sin(Timers.time(), 25f, 2f)) * uptime; + } + public void removeDelay(){ active = false; } diff --git a/core/src/io/anuke/mindustry/ui/fragments/HudFragment.java b/core/src/io/anuke/mindustry/ui/fragments/HudFragment.java index 5a9ebcc900..10c1a67fd4 100644 --- a/core/src/io/anuke/mindustry/ui/fragments/HudFragment.java +++ b/core/src/io/anuke/mindustry/ui/fragments/HudFragment.java @@ -163,6 +163,7 @@ public class HudFragment implements Fragment{ } public void updateItems(){ + itemtable.clear(); itemtable.left(); @@ -183,6 +184,7 @@ public class HudFragment implements Fragment{ } public void fadeRespawn(boolean in){ + respawntable.addAction(Actions.color(in ? new Color(0, 0, 0, 0.3f) : Color.CLEAR, 0.3f)); } } diff --git a/core/src/io/anuke/mindustry/world/blocks/types/defense/PowerTurret.java b/core/src/io/anuke/mindustry/world/blocks/types/defense/PowerTurret.java index fd5d6361ec..ec105ee656 100644 --- a/core/src/io/anuke/mindustry/world/blocks/types/defense/PowerTurret.java +++ b/core/src/io/anuke/mindustry/world/blocks/types/defense/PowerTurret.java @@ -41,6 +41,12 @@ public class PowerTurret extends Turret implements PowerAcceptor{ Draw.dashcircle(tile.worldx() + offset.x, tile.worldy() + offset.y, range); Draw.reset(); + drawPowerBar(tile); + } + + public void drawPowerBar(Tile tile){ + Vector2 offset = getPlaceOffset(); + PowerTurretEntity entity = tile.entity(); float fract = (float)entity.power / powerCapacity; diff --git a/core/src/io/anuke/mindustry/world/blocks/types/defense/RepairTurret.java b/core/src/io/anuke/mindustry/world/blocks/types/defense/RepairTurret.java index 13d48f0b07..89aad46348 100644 --- a/core/src/io/anuke/mindustry/world/blocks/types/defense/RepairTurret.java +++ b/core/src/io/anuke/mindustry/world/blocks/types/defense/RepairTurret.java @@ -36,7 +36,9 @@ public class RepairTurret extends PowerTurret{ @Override public void update(Tile tile){ - TurretEntity entity = tile.entity(); + PowerTurretEntity entity = tile.entity(); + + if(entity.power < powerUsed) return; if(Timers.get(entity, "blocktarget", targetInterval)){ entity.blockTarget = Vars.world.findTileTarget(tile.worldx(), tile.worldy(), tile, range, true); @@ -51,6 +53,8 @@ public class RepairTurret extends PowerTurret{ if(entity.blockTarget.health > entity.blockTarget.health) entity.blockTarget.health = entity.blockTarget.maxhealth; + + entity.power -= powerUsed; } } } @@ -60,6 +64,8 @@ public class RepairTurret extends PowerTurret{ Draw.color("green"); Draw.dashcircle(tile.worldx(), tile.worldy(), range); Draw.reset(); + + drawPowerBar(tile); } @Override diff --git a/core/src/io/anuke/mindustry/world/blocks/types/defense/ShieldBlock.java b/core/src/io/anuke/mindustry/world/blocks/types/defense/ShieldBlock.java index 53cb4f6774..11be93223a 100644 --- a/core/src/io/anuke/mindustry/world/blocks/types/defense/ShieldBlock.java +++ b/core/src/io/anuke/mindustry/world/blocks/types/defense/ShieldBlock.java @@ -1,17 +1,20 @@ package io.anuke.mindustry.world.blocks.types.defense; +import io.anuke.mindustry.Vars; import io.anuke.mindustry.entities.TileEntity; +import io.anuke.mindustry.entities.effect.Fx; import io.anuke.mindustry.entities.effect.Shield; import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.blocks.types.PowerBlock; +import io.anuke.ucore.core.Effects; import io.anuke.ucore.core.Timers; +import io.anuke.ucore.entities.BulletEntity; public class ShieldBlock extends PowerBlock{ - private static boolean debugShield = false; - public float shieldRadius = 40f; public float powerDrain = 0.005f; - + public float powerPerDamage = 0.1f; + public ShieldBlock(String name) { super(name); voltage = powerDrain; @@ -23,18 +26,18 @@ public class ShieldBlock extends PowerBlock{ if(entity.shield == null){ entity.shield = new Shield(tile); - if(debugShield) + if(Vars.infiniteAmmo && Vars.debug) entity.shield.add(); } - if(entity.power > powerDrain * Timers.delta()){ + if(entity.power > powerPerDamage){ if(!entity.shield.active && entity.power > powerDrain * Timers.delta() * 10f){ entity.shield.add(); } entity.power -= powerDrain * Timers.delta(); }else{ - if(entity.shield.active && !debugShield){ + if(entity.shield.active && !(Vars.infiniteAmmo && Vars.debug)){ entity.shield.removeDelay(); } } @@ -45,6 +48,20 @@ public class ShieldBlock extends PowerBlock{ public TileEntity getEntity(){ return new ShieldEntity(); } + + public void handleBullet(Tile tile, BulletEntity bullet){ + ShieldEntity entity = tile.entity(); + + if(entity.power < bullet.getDamage() * powerPerDamage){ + return; + } + + bullet.remove(); + Effects.effect(Fx.laserhit, bullet); + Vars.renderer.addShieldHit(bullet.x, bullet.y); + + entity.power -= bullet.getDamage() * powerPerDamage; + } static class ShieldEntity extends PowerEntity{ Shield shield; diff --git a/desktop/mindustry-saves/0.mins b/desktop/mindustry-saves/0.mins index fcc59b83e9969a47cea91e1fa1b9d194d2886cef..a4159bda141bc5416f816a22fdddbff6a8e8b9f7 100644 GIT binary patch literal 1976 zcmZvdOGuPa6vxkunuZ}d-#6ooj^k^Z9G$Yv2kL0%e3}^*6p_#aBh4UpEviklC?Z+~ z(G#O2N!rz-78Pjr!pb0`aMJ@5BdTrN&bjydUvTm+?)>imobx~T-ub>6BFZK5^m`Js zgCrxvxkQxud1ff}XrY(a1C;Hh$mvs=JPmttMkoQ!>Qj&CC7MlWy%T_8*Dq<}j0!52Jjn_c+ z#h|~# z)<9c(4;A_Mp@r%|#fIt{pw&MBne znc-^wCA|Dgcm;G`k9bwf31oO$h{`(^^5xnoy#-mTc3z^B?}bOgR z)CrejQv&h``y7z%q#ejh?TJ2`UOvyAo~6aucjPU5J7=X_y66mfEWCX@1)OtkB&dBW zWEr83nb+e)3DdRKP=g`6OLybrm~)-r$719V_vyOq`ud+=aVgMs74%#{&Q;z8vbxju zE(tIG*K9Y>bmcr-=OyL-<%a!Bc=?y`3g|rb<<&{C8vLHeyA|;USG)vSo786HcSbd_ zUM^nSY4Ji+8Pt)KY`Kh*_s1%{IYcF!;2i8xPL&d`A#Wy8#VZrryeO8%JDNtUKz>ox zH3oZCR=q4^`9-P-;C}#oMazmw>xJS+pM8k&_ ku@*5@uA{8QwZ_i1KT)yzh@oLn^Dj`#Z%|VHYI#@x06;*VrvLx| literal 18939 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 Lnty|$e-!-(h=-s+ literal 2873 zcmZveTWnNS6o%JyDo{&lr{~N}TROLM&Xi86(xLV;Y0;LMVk4J8qKKD=s(3;4!5E{w zAPE{{Vtnv{+JG@pqlp21A>L6gt$-+r0mTQzK#+J#B{9ZCV&YnRpMQ7ebav7+-~QLX z*4lfYJ#C4oo+vayLr74)It<)=g(6_g;)5KsF}$E-N;>d@H`&_EsukGCKYi;pVpXi zQ_#AXf(G}3n)ZPPq^s%{Nr1ANxLdE$3a=r0!|RGT+M|iPb!MP>4ty)#09kK>;w~tq ziK}EywD6XSvR3E-Im;BGH>h(zqRsC?v)%{woe5)34E=ef)a*OLnik#(2_%-n4$igp$&ZwT6_!0{sQC}Y8rtS zKP=aZ)Y)|^kuTPW*1|kb`dW5G0iW~WVNm@j$Qo4{!!nmD){)A&9f-$uyhB=>uG-U4 z!&)2v$jBX0AKZ1(YLK;BudY1TIyzsoeV^p19wGW=+c_dzdxvFrqDQOLiw5g)vmKX+ zV%j?{HCM_4)Zj~&I;w~lY8UlVWUkuses+!$ZQ{!jEj}$WCq|Dw<5R4-19VJ4zOY?} zZYn}sSOVFlz-B}AGTp_Urvo}CL`yG7nsn6Bl{~B2!jD+#ML!ao7LdMKL(5FJywMhn z!Do-@PP{c7?X``&xyRU~Sfty`t3JiXrhMw35s*ICn)xfWzMQYwzOSrL%eJe$Q|%q* zM*0<2sTU2_WAn~OdEeR_c!h8COIAg(NsxP+ zs*{uLc(Qh~iQKQWD0B6F5Yh9UVHP{#Y~Ww?Jf!$3l`|@lwEV1{o6DU`m&IkCDT~WD zJrjsKWpUk(O-{KT6S37f(XkNYNJ1aJu+#nvg((VSe_{wd&r?pslv8p*U8&SyOgVrUp zXFv^SL5bTzNu#xV2Q4lCj;ZBXn?b$bgRDCh@gMM3O}y+jLrFtT_aGx~C~_~f$azr0 zkaq!ykcN;X+{*cln wR})A3ufUgn7|{+xi+)A4&rtq1XaoE^mj_p={~eTn95g%&s{0c(@T8{y0Vkd3EdT%j