diff --git a/core/src/io/anuke/mindustry/content/Blocks.java b/core/src/io/anuke/mindustry/content/Blocks.java index a72d1695df..abbebcdf0c 100644 --- a/core/src/io/anuke/mindustry/content/Blocks.java +++ b/core/src/io/anuke/mindustry/content/Blocks.java @@ -123,7 +123,7 @@ public class Blocks implements ContentList{ liquidDrop = Liquids.water; isLiquid = true; status = StatusEffects.wet; - statusIntensity = 1f; + statusDuration = 120f; drownTime = 140f; cacheLayer = CacheLayer.water; minimapColor = Color.valueOf("465a96"); @@ -134,7 +134,7 @@ public class Blocks implements ContentList{ speedMultiplier = 0.5f; variants = 0; status = StatusEffects.wet; - statusIntensity = 0.9f; + statusDuration = 90f; liquidDrop = Liquids.water; isLiquid = true; cacheLayer = CacheLayer.water; @@ -145,7 +145,7 @@ public class Blocks implements ContentList{ drownTime = 150f; liquidColor = Color.valueOf("292929"); status = StatusEffects.tarred; - statusIntensity = 1f; + statusDuration = 240f; speedMultiplier = 0.19f; variants = 0; liquidDrop = Liquids.oil; diff --git a/core/src/io/anuke/mindustry/content/Bullets.java b/core/src/io/anuke/mindustry/content/Bullets.java index f951108519..94d397b6f5 100644 --- a/core/src/io/anuke/mindustry/content/Bullets.java +++ b/core/src/io/anuke/mindustry/content/Bullets.java @@ -321,15 +321,13 @@ public class Bullets implements ContentList{ incendChance = 0.3f; }}; - damageLightning = new BulletType(0.0001f, 0f){ - { - lifetime = Lightning.lifetime; - hitEffect = Fx.hitLancer; - despawnEffect = Fx.none; - status = StatusEffects.shocked; - statusIntensity = 1f; - } - }; + damageLightning = new BulletType(0.0001f, 0f){{ + lifetime = Lightning.lifetime; + hitEffect = Fx.hitLancer; + despawnEffect = Fx.none; + status = StatusEffects.shocked; + statusDuration = 10f; + }}; healBullet = new BulletType(5.2f, 13){ float healPercent = 3f; diff --git a/core/src/io/anuke/mindustry/content/Liquids.java b/core/src/io/anuke/mindustry/content/Liquids.java index 84985d1430..6ee2e641b9 100644 --- a/core/src/io/anuke/mindustry/content/Liquids.java +++ b/core/src/io/anuke/mindustry/content/Liquids.java @@ -5,7 +5,7 @@ import io.anuke.mindustry.game.ContentList; import io.anuke.mindustry.type.Liquid; public class Liquids implements ContentList{ - public static Liquid water, slag, oil, cryofluid; + public static Liquid water, slag, oil, cryofluid, acid; @Override public void load(){ @@ -32,11 +32,16 @@ public class Liquids implements ContentList{ effect = StatusEffects.tarred; }}; - cryofluid = new Liquid("cryofluid", Color.SKY){{ + cryofluid = new Liquid("cryofluid", Color.valueOf("6ecdec")){{ heatCapacity = 0.9f; temperature = 0.25f; tier = 1; effect = StatusEffects.freezing; }}; + + acid = new Liquid("acid", Color.valueOf("e9f9b3")){{ + heatCapacity = 0.1f; //don't use acid as coolant, it's bad + effect = StatusEffects.corroded; + }}; } } diff --git a/core/src/io/anuke/mindustry/content/StatusEffects.java b/core/src/io/anuke/mindustry/content/StatusEffects.java index bc4f0c420a..fe676e591d 100644 --- a/core/src/io/anuke/mindustry/content/StatusEffects.java +++ b/core/src/io/anuke/mindustry/content/StatusEffects.java @@ -2,169 +2,80 @@ package io.anuke.mindustry.content; import io.anuke.arc.entities.Effects; import io.anuke.arc.math.Mathf; -import io.anuke.arc.util.Time; -import io.anuke.mindustry.entities.StatusController.StatusEntry; -import io.anuke.mindustry.entities.Unit; import io.anuke.mindustry.game.ContentList; import io.anuke.mindustry.type.StatusEffect; public class StatusEffects implements ContentList{ - public static StatusEffect none, burning, freezing, wet, melting, tarred, overdrive, shielded, shocked; + public static StatusEffect none, burning, freezing, wet, melting, tarred, overdrive, shielded, shocked, corroded; @Override public void load(){ - none = new StatusEffect(0); + none = new StatusEffect(); - burning = new StatusEffect(4 * 60f){ - { - oppositeScale = 0.5f; - } + burning = new StatusEffect(){{ + damage = 0.04f; + effect = Fx.burning; - @Override - public StatusEntry getTransition(Unit unit, StatusEffect to, float time, float newTime, StatusEntry result){ - if(to == tarred){ - unit.damage(1f); - Effects.effect(Fx.burning, unit.x + Mathf.range(unit.getSize() / 2f), unit.y + Mathf.range(unit.getSize() / 2f)); - return result.set(this, Math.min(time + newTime, baseDuration + tarred.baseDuration)); - } + opposite(() -> wet, () -> freezing); + trans(() -> tarred, ((unit, time, newTime, result) -> { + unit.damage(1f); + Effects.effect(Fx.burning, unit.x + Mathf.range(unit.getSize() / 2f), unit.y + Mathf.range(unit.getSize() / 2f)); + result.set(this, Math.min(time + newTime, 300f)); + })); + }}; - return super.getTransition(unit, to, time, newTime, result); - } + freezing = new StatusEffect(){{ + speedMultiplier = 0.6f; + armorMultiplier = 0.8f; + effect = Fx.freezing; - @Override - public void update(Unit unit, float time){ - unit.damagePeriodic(0.04f); + opposite(() -> melting, () -> burning); + }}; - if(Mathf.chance(Time.delta() * 0.2f)){ - Effects.effect(Fx.burning, unit.x + Mathf.range(unit.getSize() / 2f), unit.y + Mathf.range(unit.getSize() / 2f)); - } - } - }; + wet = new StatusEffect(){{ + speedMultiplier = 0.9f; + effect = Fx.wet; - freezing = new StatusEffect(5 * 60f){ - { - oppositeScale = 0.4f; - speedMultiplier = 0.6f; - armorMultiplier = 0.8f; - } + trans(() -> shocked, ((unit, time, newTime, result) -> unit.damage(15f))); + opposite(() -> burning, () -> shocked); + }}; - @Override - public void update(Unit unit, float time){ + melting = new StatusEffect(){{ + speedMultiplier = 0.8f; + armorMultiplier = 0.8f; + damage = 0.3f; + effect = Fx.melting; - if(Mathf.chance(Time.delta() * 0.15f)){ - Effects.effect(Fx.freezing, unit.x + Mathf.range(unit.getSize() / 2f), unit.y + Mathf.range(unit.getSize() / 2f)); - } - } - }; + trans(() -> tarred, ((unit, time, newTime, result) -> result.set(this, Math.min(time + newTime / 2f, 140f)))); + opposite(() -> wet, () -> freezing); + }}; - wet = new StatusEffect(3 * 60f){ - { - oppositeScale = 0.5f; - speedMultiplier = 0.9f; - } + tarred = new StatusEffect(){{ + speedMultiplier = 0.6f; + effect = Fx.oily; - @Override - public StatusEntry getTransition(Unit unit, StatusEffect to, float time, float newTime, StatusEntry result){ - if(to == shocked){ - //get shocked when wet - unit.damage(15f); - return result.set(this, time); - } + trans(() -> melting, ((unit, time, newTime, result) -> result.set(burning, newTime + time))); + trans(() -> burning, ((unit, time, newTime, result) -> result.set(burning, newTime + time))); + }}; - return super.getTransition(unit, to, time, newTime, result); - } + overdrive = new StatusEffect(){{ + armorMultiplier = 0.95f; + speedMultiplier = 1.15f; + damageMultiplier = 1.4f; + damage = -0.01f; + effect = Fx.overdriven; + }}; - @Override - public void update(Unit unit, float time){ - if(Mathf.chance(Time.delta() * 0.15f)){ - Effects.effect(Fx.wet, unit.x + Mathf.range(unit.getSize() / 2f), unit.y + Mathf.range(unit.getSize() / 2f)); - } - } - }; + shielded = new StatusEffect(){{ + armorMultiplier = 3f; + }}; - melting = new StatusEffect(5 * 60f){ - { - oppositeScale = 0.2f; - speedMultiplier = 0.8f; - armorMultiplier = 0.8f; - } + shocked = new StatusEffect(); - @Override - public StatusEntry getTransition(Unit unit, StatusEffect to, float time, float newTime, StatusEntry result){ - if(to == tarred){ - return result.set(this, Math.min(time + newTime / 2f, baseDuration)); - } - - return super.getTransition(unit, to, time, newTime, result); - } - - @Override - public void update(Unit unit, float time){ - unit.damagePeriodic(0.3f); - - if(Mathf.chance(Time.delta() * 0.2f)){ - Effects.effect(Fx.melting, unit.x + Mathf.range(unit.getSize() / 2f), unit.y + Mathf.range(unit.getSize() / 2f)); - } - } - }; - - tarred = new StatusEffect(4 * 60f){ - { - speedMultiplier = 0.6f; - } - - @Override - public void update(Unit unit, float time){ - if(Mathf.chance(Time.delta() * 0.15f)){ - Effects.effect(Fx.oily, unit.x + Mathf.range(unit.getSize() / 2f), unit.y + Mathf.range(unit.getSize() / 2f)); - } - } - - @Override - public StatusEntry getTransition(Unit unit, StatusEffect to, float time, float newTime, StatusEntry result){ - if(to == melting || to == burning){ - return result.set(to, newTime + time); - } - - return result.set(to, newTime); - } - }; - - overdrive = new StatusEffect(60f*15){ - { - armorMultiplier = 0.95f; - speedMultiplier = 1.15f; - damageMultiplier = 1.4f; - } - - @Override - public void update(Unit unit, float time){ - //idle regen boosted - unit.health += 0.01f * Time.delta(); - - if(Mathf.chance(Time.delta() * 0.25f)){ - Effects.effect(Fx.overdriven, unit.x + Mathf.range(unit.getSize() / 2f), unit.y + Mathf.range(unit.getSize() / 2f), 0f, unit); - } - } - }; - - shielded = new StatusEffect(6f){ - { - armorMultiplier = 3f; - } - }; - - shocked = new StatusEffect(1f){ - { - armorMultiplier = 3f; - } - }; - - wet.setOpposites(shocked); - melting.setOpposites(wet, freezing); - wet.setOpposites(burning); - freezing.setOpposites(burning, melting); - burning.setOpposites(wet, freezing); + //no effects, just small amounts of damage. + corroded = new StatusEffect(){{ + damage = 0.1f; + }}; } } diff --git a/core/src/io/anuke/mindustry/entities/StatusController.java b/core/src/io/anuke/mindustry/entities/StatusController.java index a324e71ab4..d7f37fe5c4 100644 --- a/core/src/io/anuke/mindustry/entities/StatusController.java +++ b/core/src/io/anuke/mindustry/entities/StatusController.java @@ -28,24 +28,21 @@ public class StatusController implements Saveable{ private float damageMultiplier; private float armorMultiplier; - public void handleApply(Unit unit, StatusEffect effect, float intensity){ + public void handleApply(Unit unit, StatusEffect effect, float duration){ if(effect == StatusEffects.none) return; //don't apply empty effects - float newTime = effect.baseDuration * intensity; - if(statuses.size > 0){ //check for opposite effects for(StatusEntry entry : statuses){ //extend effect if(entry.effect == effect){ - entry.time = Math.max(entry.time, newTime); + entry.time = Math.max(entry.time, duration); return; - }else if(entry.effect.isOpposite(effect)){ //find opposite - entry.effect.getTransition(unit, effect, entry.time, newTime, globalResult); + }else if(entry.effect.reactsWith(effect)){ //find opposite + entry.effect.getTransition(unit, effect, entry.time, duration, globalResult); entry.time = globalResult.time; if(globalResult.effect != entry.effect){ - entry.effect.onTransition(unit, globalResult.effect); entry.effect = globalResult.effect; } @@ -57,7 +54,7 @@ public class StatusController implements Saveable{ //otherwise, no opposites found, add direct effect StatusEntry entry = Pools.obtain(StatusEntry.class, StatusEntry::new); - entry.set(effect, newTime); + entry.set(effect, duration); statuses.add(entry); } diff --git a/core/src/io/anuke/mindustry/entities/Unit.java b/core/src/io/anuke/mindustry/entities/Unit.java index 8a10379838..6d67e5d3ad 100644 --- a/core/src/io/anuke/mindustry/entities/Unit.java +++ b/core/src/io/anuke/mindustry/entities/Unit.java @@ -261,7 +261,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ } if(onLiquid){ - status.handleApply(this, floor.status, floor.statusIntensity); + status.handleApply(this, floor.status, floor.statusDuration); if(floor.damageTaken > 0f){ damagePeriodic(floor.damageTaken); @@ -292,9 +292,9 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ velocity.scl(Mathf.clamp(1f - drag() * (isFlying() ? 1f : floor.dragMultiplier) * Time.delta())); } - public void applyEffect(StatusEffect effect, float intensity){ + public void applyEffect(StatusEffect effect, float duration){ if(dead || Net.client()) return; //effects are synced and thus not applied through clients - status.handleApply(this, effect, intensity); + status.handleApply(this, effect, duration); } public void damagePeriodic(float amount){ diff --git a/core/src/io/anuke/mindustry/entities/bullet/Bullet.java b/core/src/io/anuke/mindustry/entities/bullet/Bullet.java index 3bd1fe6ad7..a860cb4ef6 100644 --- a/core/src/io/anuke/mindustry/entities/bullet/Bullet.java +++ b/core/src/io/anuke/mindustry/entities/bullet/Bullet.java @@ -197,7 +197,7 @@ public class Bullet extends SolidEntity implements DamageTrait, ScaleTrait, Pool if(other instanceof Unit){ Unit unit = (Unit) other; unit.velocity().add(Tmp.v3.set(other.getX(), other.getY()).sub(x, y).setLength(type.knockback / unit.mass())); - unit.applyEffect(type.status, type.statusIntensity); + unit.applyEffect(type.status, type.statusDuration); } } diff --git a/core/src/io/anuke/mindustry/entities/bullet/BulletType.java b/core/src/io/anuke/mindustry/entities/bullet/BulletType.java index 10002296bf..59ef42b6d6 100644 --- a/core/src/io/anuke/mindustry/entities/bullet/BulletType.java +++ b/core/src/io/anuke/mindustry/entities/bullet/BulletType.java @@ -40,7 +40,7 @@ public abstract class BulletType extends Content{ /**Status effect applied on hit.*/ public StatusEffect status = StatusEffects.none; /**Intensity of applied status effect in terms of duration.*/ - public float statusIntensity = 0.5f; + public float statusDuration = 60 * 1f; /**What fraction of armor is pierced, 0-1*/ public float armorPierce = 0f; /**Whether to sync this bullet to clients.*/ diff --git a/core/src/io/anuke/mindustry/entities/bullet/LiquidBulletType.java b/core/src/io/anuke/mindustry/entities/bullet/LiquidBulletType.java index b7a74f4486..8b1677360d 100644 --- a/core/src/io/anuke/mindustry/entities/bullet/LiquidBulletType.java +++ b/core/src/io/anuke/mindustry/entities/bullet/LiquidBulletType.java @@ -25,7 +25,7 @@ public class LiquidBulletType extends BulletType{ lifetime = 70f; status = liquid.effect; - statusIntensity = 0.5f; + statusDuration = 90f; despawnEffect = Fx.none; hitEffect = Fx.hitLiquid; drag = 0.01f; diff --git a/core/src/io/anuke/mindustry/entities/effect/Fire.java b/core/src/io/anuke/mindustry/entities/effect/Fire.java index 4ca534df54..28a2dc42f5 100644 --- a/core/src/io/anuke/mindustry/entities/effect/Fire.java +++ b/core/src/io/anuke/mindustry/entities/effect/Fire.java @@ -151,7 +151,9 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable{ if(damage){ entity.damage(0.4f); } - Damage.damageUnits(null, tile.worldx(), tile.worldy(), tilesize, 3f, unit -> !unit.isFlying(), unit -> unit.applyEffect(StatusEffects.burning, 0.8f)); + Damage.damageUnits(null, tile.worldx(), tile.worldy(), tilesize, 3f, + unit -> !unit.isFlying(), + unit -> unit.applyEffect(StatusEffects.burning, 60 * 5)); } } diff --git a/core/src/io/anuke/mindustry/entities/effect/Puddle.java b/core/src/io/anuke/mindustry/entities/effect/Puddle.java index 8ca9022031..9ca41a7705 100644 --- a/core/src/io/anuke/mindustry/entities/effect/Puddle.java +++ b/core/src/io/anuke/mindustry/entities/effect/Puddle.java @@ -205,7 +205,7 @@ public class Puddle extends SolidEntity implements SaveTrait, Poolable, DrawTrai unit.hitbox(rect2); if(!rect.overlaps(rect2)) return; - unit.applyEffect(liquid.effect, 0.5f); + unit.applyEffect(liquid.effect, 60 * 2); if(unit.velocity().len() > 0.1){ Effects.effect(Fx.ripple, liquid.color, unit.x, unit.y); diff --git a/core/src/io/anuke/mindustry/game/SpawnGroup.java b/core/src/io/anuke/mindustry/game/SpawnGroup.java index 93cbc2a30d..98640d4616 100644 --- a/core/src/io/anuke/mindustry/game/SpawnGroup.java +++ b/core/src/io/anuke/mindustry/game/SpawnGroup.java @@ -101,7 +101,7 @@ public class SpawnGroup{ } if(effect != null){ - unit.applyEffect(effect, 10000f); + unit.applyEffect(effect, 999999f); } if(items != null){ diff --git a/core/src/io/anuke/mindustry/type/StatusEffect.java b/core/src/io/anuke/mindustry/type/StatusEffect.java index 2180699a83..bb9b1ea82f 100644 --- a/core/src/io/anuke/mindustry/type/StatusEffect.java +++ b/core/src/io/anuke/mindustry/type/StatusEffect.java @@ -1,31 +1,79 @@ package io.anuke.mindustry.type; +import io.anuke.arc.collection.Array; +import io.anuke.arc.collection.ObjectMap; +import io.anuke.arc.entities.Effects; +import io.anuke.arc.entities.Effects.Effect; +import io.anuke.arc.function.Supplier; import io.anuke.arc.graphics.Color; -import io.anuke.arc.collection.ObjectSet; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.util.Time; +import io.anuke.mindustry.content.Fx; import io.anuke.mindustry.entities.StatusController.StatusEntry; import io.anuke.mindustry.entities.Unit; import io.anuke.mindustry.game.Content; public class StatusEffect extends Content{ - /**Duration of this status effect in ticks at maximum power.*/ - public final float baseDuration; - public float damageMultiplier = 1f; //damage dealt public float armorMultiplier = 1f; //armor points public float speedMultiplier = 1f; //speed public Color color = Color.WHITE.cpy(); //tint color - /**Set of 'opposite' effects, which will decrease the duration of this effect when applied.*/ - protected ObjectSet opposites = new ObjectSet<>(); - /**The strength of time decrease when met with an opposite effect, as a fraction of the other's duration.*/ - protected float oppositeScale = 0.5f; + /**Transition handler map.*/ + private ObjectMap transitions = new ObjectMap<>(); + /**Transition initializer array. Since provided effects are only available after init(), this handles putting things + * in the transitions map.*/ + private Array transInit = new Array<>(); - public StatusEffect(float baseDuration){ - this.baseDuration = baseDuration; + /**Damage per frame.*/ + protected float damage; + /**Effect that happens randomly on top of the affected unit.*/ + protected Effect effect = Fx.none; + + @SuppressWarnings("unchecked") + @Override + public void init(){ + for(Object[] pair : transInit){ + Supplier sup = (Supplier) pair[0]; + TransitionHandler handler = (TransitionHandler) pair[1]; + transitions.put(sup.get(), handler); + } + transInit.clear(); } /**Runs every tick on the affected unit while time is greater than 0.*/ public void update(Unit unit, float time){ + if(damage > 0){ + unit.damagePeriodic(damage); + }else if(damage < 0){ //heal unit + unit.healBy(damage * Time.delta()); + } + + if(effect != Fx.none && Mathf.chance(Time.delta() * 0.15f)){ + Effects.effect(Fx.overdriven, unit.x + Mathf.range(unit.getSize() / 2f), unit.y + Mathf.range(unit.getSize() / 2f)); + } + } + + protected void trans(Supplier effect, TransitionHandler handler){ + transInit.add(new Object[]{effect, handler}); + } + + @SuppressWarnings("unchecked") + protected void opposite(Supplier... effect){ + for(Supplier sup : effect){ + trans(sup, (unit, time, newTime, result) -> { + time -= newTime * 0.5f; + if(time > 0){ + result.set(this, time); + return; + } + result.set(sup.get(), newTime); + }); + } + } + + public boolean reactsWith(StatusEffect effect){ + return transitions.containsKey(effect); } /** @@ -36,32 +84,20 @@ public class StatusEffect extends Content{ * @param newTime The time that the new status effect will last */ public StatusEntry getTransition(Unit unit, StatusEffect to, float time, float newTime, StatusEntry result){ - if(opposites.contains(to)){ - time -= newTime * oppositeScale; - if(time > 0){ - return result.set(this, time); - } + if(transitions.containsKey(to)){ + transitions.get(to).handle(unit, time, newTime, result); + return result; } return result.set(to, newTime); } - /**Called when this effect transitions to a new status effect.*/ - public void onTransition(Unit unit, StatusEffect to){ - } - - public boolean isOpposite(StatusEffect other){ - return opposites.size > 0 && opposites.contains(other); - } - - public void setOpposites(StatusEffect... effects){ - for(StatusEffect e : effects){ - opposites.add(e); - } - } - @Override public ContentType getContentType(){ return ContentType.status; } + + public interface TransitionHandler{ + void handle(Unit unit, float time, float newTime, StatusEntry result); + } } diff --git a/core/src/io/anuke/mindustry/world/blocks/Floor.java b/core/src/io/anuke/mindustry/world/blocks/Floor.java index aabc7e3020..bd28b454cf 100644 --- a/core/src/io/anuke/mindustry/world/blocks/Floor.java +++ b/core/src/io/anuke/mindustry/world/blocks/Floor.java @@ -39,7 +39,7 @@ public class Floor extends Block{ /** Status effect applied when walking on. */ public StatusEffect status = StatusEffects.none; /** Intensity of applied status effect. */ - public float statusIntensity = 0.6f; + public float statusDuration = 60f; /** Color of this floor's liquid. Used for tinting sprites. */ public Color liquidColor; /** liquids that drop from this block, used for pumps */