Status effect cleanup

This commit is contained in:
Anuken
2019-01-08 20:32:22 -05:00
parent 5e49d2d8d6
commit dc0be93b25
14 changed files with 152 additions and 203 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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;
}};
}
}

View File

@ -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;
}};
}
}

View File

@ -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);
}

View File

@ -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){

View File

@ -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);
}
}

View File

@ -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.*/

View File

@ -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;

View File

@ -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));
}
}

View File

@ -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);

View File

@ -101,7 +101,7 @@ public class SpawnGroup{
}
if(effect != null){
unit.applyEffect(effect, 10000f);
unit.applyEffect(effect, 999999f);
}
if(items != null){

View File

@ -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<StatusEffect> 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<StatusEffect, TransitionHandler> transitions = new ObjectMap<>();
/**Transition initializer array. Since provided effects are only available after init(), this handles putting things
* in the transitions map.*/
private Array<Object[]> 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<StatusEffect> sup = (Supplier<StatusEffect>) 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<StatusEffect> effect, TransitionHandler handler){
transInit.add(new Object[]{effect, handler});
}
@SuppressWarnings("unchecked")
protected void opposite(Supplier... effect){
for(Supplier<StatusEffect> 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);
}
}

View File

@ -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 */