Regen suppression unit + system
This commit is contained in:
BIN
core/assets-raw/sprites/units/quell-cell.png
Normal file
BIN
core/assets-raw/sprites/units/quell-cell.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 995 B |
BIN
core/assets-raw/sprites/units/quell.png
Normal file
BIN
core/assets-raw/sprites/units/quell.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.6 KiB |
@@ -491,3 +491,4 @@
|
|||||||
63215=basic-assembler-module|block-basic-assembler-module-ui
|
63215=basic-assembler-module|block-basic-assembler-module-ui
|
||||||
63214=beryllium-wall|block-beryllium-wall-ui
|
63214=beryllium-wall|block-beryllium-wall-ui
|
||||||
63213=beryllium-wall-large|block-beryllium-wall-large-ui
|
63213=beryllium-wall-large|block-beryllium-wall-large-ui
|
||||||
|
63212=quell|unit-quell-ui
|
||||||
|
|||||||
Binary file not shown.
@@ -238,6 +238,8 @@ public class BlockIndexer{
|
|||||||
|
|
||||||
/** Does not work with null teams. */
|
/** Does not work with null teams. */
|
||||||
public boolean eachBlock(Team team, Rect rect, Boolf<Building> pred, Cons<Building> cons){
|
public boolean eachBlock(Team team, Rect rect, Boolf<Building> pred, Cons<Building> cons){
|
||||||
|
if(team == null) return false;
|
||||||
|
|
||||||
breturnArray.clear();
|
breturnArray.clear();
|
||||||
|
|
||||||
var buildings = team.data().buildings;
|
var buildings = team.data().buildings;
|
||||||
|
|||||||
@@ -1447,6 +1447,30 @@ public class Fx{
|
|||||||
Fill.square(e.x, e.y, e.fslope() * 1.5f + 0.14f, 45f);
|
Fill.square(e.x, e.y, e.fslope() * 1.5f + 0.14f, 45f);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
regenSuppressParticle = new Effect(30f, e -> {
|
||||||
|
color(Pal.sapBullet, e.color, e.fin());
|
||||||
|
stroke(e.fout() * 1.4f + 0.5f);
|
||||||
|
|
||||||
|
randLenVectors(e.id, 4, 17f * e.fin(), (x, y) -> {
|
||||||
|
lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), e.fslope() * 3f + 0.5f);
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
|
||||||
|
regenSuppressSeek = new Effect(140f, e -> {
|
||||||
|
e.lifetime = Mathf.randomSeed(e.id, 120f, 200f);
|
||||||
|
|
||||||
|
if(!(e.data instanceof Position to)) return;
|
||||||
|
|
||||||
|
Tmp.v2.set(to).sub(e.x, e.y).nor().rotate90(1).scl(Mathf.randomSeedRange(e.id, 1f) * 50f);
|
||||||
|
|
||||||
|
Tmp.bz2.set(Tmp.v1.set(e.x, e.y), Tmp.v2.add(e.x, e.y), Tmp.v3.set(to));
|
||||||
|
|
||||||
|
Tmp.bz2.valueAt(Tmp.v4, e.fout());
|
||||||
|
|
||||||
|
color(Pal.sapBullet);
|
||||||
|
Fill.circle(Tmp.v4.x, Tmp.v4.y, e.fslope() * 2f + 0.1f);
|
||||||
|
}).followParent(false).rotWithParent(false),
|
||||||
|
|
||||||
surgeCruciSmoke = new Effect(160f, e -> {
|
surgeCruciSmoke = new Effect(160f, e -> {
|
||||||
color(Pal.slagOrange);
|
color(Pal.slagOrange);
|
||||||
alpha(0.6f);
|
alpha(0.6f);
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ public class UnitTypes{
|
|||||||
|
|
||||||
//air + payload
|
//air + payload
|
||||||
public static @EntityDef({Unitc.class, Payloadc.class}) UnitType mega,
|
public static @EntityDef({Unitc.class, Payloadc.class}) UnitType mega,
|
||||||
incite, emanate;
|
incite, emanate, quell;
|
||||||
|
|
||||||
//air + payload, legacy
|
//air + payload, legacy
|
||||||
public static @EntityDef(value = {Unitc.class, Payloadc.class}, legacy = true) UnitType quad;
|
public static @EntityDef(value = {Unitc.class, Payloadc.class}, legacy = true) UnitType quad;
|
||||||
@@ -2502,7 +2502,36 @@ public class UnitTypes{
|
|||||||
//endregion
|
//endregion
|
||||||
//region erekir - flying
|
//region erekir - flying
|
||||||
|
|
||||||
//TODO
|
//TODO orb, suppress healing
|
||||||
|
quell = new UnitType("quell"){{
|
||||||
|
envDisabled = 0;
|
||||||
|
|
||||||
|
outlineColor = Pal.darkOutline;
|
||||||
|
lowAltitude = false;
|
||||||
|
flying = true;
|
||||||
|
drag = 0.06f;
|
||||||
|
speed = 1.1f;
|
||||||
|
rotateSpeed = 3.5f;
|
||||||
|
accel = 0.1f;
|
||||||
|
health = 3000f;
|
||||||
|
armor = 4f;
|
||||||
|
hitSize = 36f;
|
||||||
|
payloadCapacity = Mathf.sqr(3f) * tilePayload;
|
||||||
|
|
||||||
|
engineSize = 4.8f;
|
||||||
|
engineOffset = 61 / 4f;
|
||||||
|
|
||||||
|
abilities.add(new SuppressionFieldAbility(){{
|
||||||
|
orbRadius = 5.3f;
|
||||||
|
}});
|
||||||
|
|
||||||
|
float es = 3.9f;
|
||||||
|
|
||||||
|
setEnginesMirror(
|
||||||
|
new UnitEngine(62 / 4f, -60 / 4f, es, 315f),
|
||||||
|
new UnitEngine(72 / 4f, -29 / 4f, 3f, 315f)
|
||||||
|
);
|
||||||
|
}};
|
||||||
|
|
||||||
//endregion
|
//endregion
|
||||||
//region erekir - neoplasm
|
//region erekir - neoplasm
|
||||||
@@ -2646,9 +2675,8 @@ public class UnitTypes{
|
|||||||
}};
|
}};
|
||||||
|
|
||||||
emanate = new UnitType("emanate"){{
|
emanate = new UnitType("emanate"){{
|
||||||
//TODO not a real enemy, should not be counted or have flying AI
|
defaultController = BuilderAI::new;
|
||||||
defaultController = FlyingAI::new;
|
isCounted = false;
|
||||||
//isCounted = false;
|
|
||||||
envDisabled = 0;
|
envDisabled = 0;
|
||||||
|
|
||||||
outlineColor = Pal.darkOutline;
|
outlineColor = Pal.darkOutline;
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
package mindustry.entities.abilities;
|
||||||
|
|
||||||
|
import arc.graphics.*;
|
||||||
|
import arc.graphics.g2d.*;
|
||||||
|
import arc.math.*;
|
||||||
|
import arc.struct.*;
|
||||||
|
import arc.util.*;
|
||||||
|
import mindustry.*;
|
||||||
|
import mindustry.content.*;
|
||||||
|
import mindustry.gen.*;
|
||||||
|
import mindustry.graphics.*;
|
||||||
|
|
||||||
|
import static mindustry.Vars.*;
|
||||||
|
|
||||||
|
public class SuppressionFieldAbility extends Ability{
|
||||||
|
protected static Rand rand = new Rand();
|
||||||
|
protected static Seq<Building> builds = new Seq<>();
|
||||||
|
|
||||||
|
public float reload = 60f * 1.5f;
|
||||||
|
public float range = 200f;
|
||||||
|
|
||||||
|
public float orbRadius = 4.5f, orbMidScl = 0.62f, orbSinScl = 8f, orbSinMag = 1f;
|
||||||
|
public Color color1 = Pal.sap.cpy().mul(1.6f), color2 = Pal.sap;
|
||||||
|
public float layer = Layer.effect;
|
||||||
|
|
||||||
|
public int particles = 15;
|
||||||
|
public float particleSize = 4f;
|
||||||
|
public float particleLen = 7f;
|
||||||
|
public float rotateScl = 3f;
|
||||||
|
public float particleLife = 110f;
|
||||||
|
public Interp particleInterp = f -> Interp.circleOut.apply(Interp.slope.apply(f));
|
||||||
|
public Color particleColor = Pal.sap.cpy().a(0.8f);
|
||||||
|
|
||||||
|
public float applyParticleChance = 13f;
|
||||||
|
|
||||||
|
protected boolean any;
|
||||||
|
protected float timer;
|
||||||
|
protected float heat = 0f;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void update(Unit unit){
|
||||||
|
if((timer += Time.delta) >= reload){
|
||||||
|
any = false;
|
||||||
|
builds.clear();
|
||||||
|
Vars.indexer.eachBlock(null, unit.x,unit.y, range, build -> true, build -> {
|
||||||
|
if(build.team != unit.team){
|
||||||
|
float prev = build.healSuppressionTime;
|
||||||
|
build.applyHealSuppression(reload + 1f);
|
||||||
|
|
||||||
|
any = true;
|
||||||
|
|
||||||
|
//add prev check so ability spam doesn't lead to particle spam (essentially, recently suppressed blocks don't get new particles)
|
||||||
|
if(!headless && prev - Time.time <= reload/2f){
|
||||||
|
builds.add(build);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//to prevent particle spam, the amount of particles is to remain constant (scales with number of buildings)
|
||||||
|
float scaledChance = applyParticleChance / builds.size;
|
||||||
|
for(var build : builds){
|
||||||
|
if(Mathf.chance(scaledChance)){
|
||||||
|
Time.run(Mathf.random(reload), () -> {
|
||||||
|
Fx.regenSuppressSeek.at(build.x + Mathf.range(build.block.size * tilesize / 2f), build.y + Mathf.range(build.block.size * tilesize / 2f), 0f, unit);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
timer = 0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
heat = Mathf.lerpDelta(heat, any ? 1f : 0f, 0.09f);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void draw(Unit unit){
|
||||||
|
Draw.z(layer);
|
||||||
|
|
||||||
|
float rad = orbRadius + Mathf.absin(orbSinScl, orbSinMag);
|
||||||
|
|
||||||
|
Draw.color(color2);
|
||||||
|
Fill.circle(unit.x, unit.y, rad);
|
||||||
|
|
||||||
|
Draw.color(color1);
|
||||||
|
Fill.circle(unit.x, unit.y, rad * orbMidScl);
|
||||||
|
|
||||||
|
float base = (Time.time / particleLife);
|
||||||
|
rand.setSeed(unit.id);
|
||||||
|
Draw.color(particleColor);
|
||||||
|
for(int i = 0; i < particles; i++){
|
||||||
|
float fin = (rand.random(1f) + base) % 1f, fout = 1f - fin;
|
||||||
|
float angle = rand.random(360f) + (Time.time / rotateScl + unit.rotation) % 360f;
|
||||||
|
float len = particleLen * particleInterp.apply(fout);
|
||||||
|
Fill.circle(
|
||||||
|
unit.x + Angles.trnsx(angle, len),
|
||||||
|
unit.y + Angles.trnsy(angle, len),
|
||||||
|
particleSize * Mathf.slope(fin)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO improve
|
||||||
|
if(heat > 0.001f){
|
||||||
|
Draw.color(Pal.sapBullet);
|
||||||
|
Lines.stroke(1.2f * heat * Mathf.absin(10f, 1f));
|
||||||
|
Lines.circle(unit.x, unit.y, range);
|
||||||
|
}
|
||||||
|
|
||||||
|
Draw.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -76,6 +76,8 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
|||||||
LiquidModule liquids;
|
LiquidModule liquids;
|
||||||
ConsumeModule cons;
|
ConsumeModule cons;
|
||||||
|
|
||||||
|
public transient float healSuppressionTime = -1f;
|
||||||
|
|
||||||
private transient float timeScale = 1f, timeScaleDuration;
|
private transient float timeScale = 1f, timeScaleDuration;
|
||||||
private transient float dumpAccum;
|
private transient float dumpAccum;
|
||||||
|
|
||||||
@@ -329,6 +331,14 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
|||||||
timeScale = Math.max(timeScale, intensity);
|
timeScale = Math.max(timeScale, intensity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void applyHealSuppression(float amount){
|
||||||
|
healSuppressionTime = Math.max(healSuppressionTime, Time.time + amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHealSuppressed(){
|
||||||
|
return Time.time <= healSuppressionTime;
|
||||||
|
}
|
||||||
|
|
||||||
public Building nearby(int dx, int dy){
|
public Building nearby(int dx, int dy){
|
||||||
return world.build(tile.x + dx, tile.y + dy);
|
return world.build(tile.x + dx, tile.y + dy);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,19 @@ public class MendProjector extends Block{
|
|||||||
indexer.eachBlock(player.team(), x * tilesize + offset, y * tilesize + offset, range, other -> true, other -> Drawf.selected(other, Tmp.c1.set(baseColor).a(Mathf.absin(4f, 1f))));
|
indexer.eachBlock(player.team(), x * tilesize + offset, y * tilesize + offset, range, other -> true, other -> Drawf.selected(other, Tmp.c1.set(baseColor).a(Mathf.absin(4f, 1f))));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return whether a building has regen/healing suppressed; if so, spawns particles on it. */
|
||||||
|
public static boolean checkSuppression(Building build){
|
||||||
|
if(build.isHealSuppressed()){
|
||||||
|
if(Mathf.chanceDelta(0.04)){
|
||||||
|
Fx.regenSuppressParticle.at(build.x + Mathf.range(build.block.size * tilesize/2f - 1f), build.y + Mathf.range(build.block.size * tilesize/2f - 1f));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public class MendBuild extends Building implements Ranged{
|
public class MendBuild extends Building implements Ranged{
|
||||||
public float heat, charge = Mathf.random(reload), phaseHeat, smoothEfficiency;
|
public float heat, charge = Mathf.random(reload), phaseHeat, smoothEfficiency;
|
||||||
|
|
||||||
@@ -75,21 +88,23 @@ public class MendProjector extends Block{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateTile(){
|
public void updateTile(){
|
||||||
|
boolean canHeal = !checkSuppression(this);
|
||||||
|
|
||||||
smoothEfficiency = Mathf.lerpDelta(smoothEfficiency, efficiency(), 0.08f);
|
smoothEfficiency = Mathf.lerpDelta(smoothEfficiency, efficiency(), 0.08f);
|
||||||
heat = Mathf.lerpDelta(heat, consValid() || cheating() ? 1f : 0f, 0.08f);
|
heat = Mathf.lerpDelta(heat, consValid() && canHeal ? 1f : 0f, 0.08f);
|
||||||
charge += heat * delta();
|
charge += heat * delta();
|
||||||
|
|
||||||
phaseHeat = Mathf.lerpDelta(phaseHeat, Mathf.num(cons.optionalValid()), 0.1f);
|
phaseHeat = Mathf.lerpDelta(phaseHeat, Mathf.num(cons.optionalValid()), 0.1f);
|
||||||
|
|
||||||
if(cons.optionalValid() && timer(timerUse, useTime) && efficiency() > 0){
|
if(cons.optionalValid() && timer(timerUse, useTime) && efficiency() > 0 && canHeal){
|
||||||
consume();
|
consume();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(charge >= reload){
|
if(charge >= reload && canHeal){
|
||||||
float realRange = range + phaseHeat * phaseRangeBoost;
|
float realRange = range + phaseHeat * phaseRangeBoost;
|
||||||
charge = 0f;
|
charge = 0f;
|
||||||
|
|
||||||
indexer.eachBlock(this, realRange, Building::damaged, other -> {
|
indexer.eachBlock(this, realRange, b -> b.damaged() && !b.isHealSuppressed(), other -> {
|
||||||
other.heal(other.maxHealth() * (healPercent + phaseHeat * phaseBoost) / 100f * efficiency());
|
other.heal(other.maxHealth() * (healPercent + phaseHeat * phaseBoost) / 100f * efficiency());
|
||||||
Fx.healBlockFull.at(other.x, other.y, other.block.size, baseColor);
|
Fx.healBlockFull.at(other.x, other.y, other.block.size, baseColor);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -85,6 +85,8 @@ public class RegenProjector extends Block{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateTile(){
|
public void updateTile(){
|
||||||
|
//TODO particles when heal suppressed
|
||||||
|
|
||||||
if(lastChange != world.tileChanges){
|
if(lastChange != world.tileChanges){
|
||||||
lastChange = world.tileChanges;
|
lastChange = world.tileChanges;
|
||||||
updateTargets();
|
updateTargets();
|
||||||
@@ -95,10 +97,15 @@ public class RegenProjector extends Block{
|
|||||||
totalTime += warmup * Time.delta;
|
totalTime += warmup * Time.delta;
|
||||||
didRegen = false;
|
didRegen = false;
|
||||||
|
|
||||||
|
//no healing when suppressed
|
||||||
|
if(MendProjector.checkSuppression(this)){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(consValid()){
|
if(consValid()){
|
||||||
//use Math.max to prevent stacking
|
//use Math.max to prevent stacking
|
||||||
for(Building build : targets){
|
for(Building build : targets){
|
||||||
if(!build.damaged()) continue;
|
if(!build.damaged() || build.isHealSuppressed()) continue;
|
||||||
|
|
||||||
didRegen = true;
|
didRegen = true;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user