Merge branch 'JasonP01-statusChance'

This commit is contained in:
Anuken
2026-09-02 17:01:40 -04:00
10 changed files with 76 additions and 16 deletions
+2
View File
@@ -1238,7 +1238,9 @@ stat.buildspeedmultiplier = Build Speed Multiplier
stat.reactive = Reacts
stat.immunities = Immunities
stat.healing = Healing
stat.status = Status Effect
stat.efficiency = [stat]{0}% Efficiency
stat.chance = [stat]{0}%[lightgray] chance of [white]
stat.hitsize = Hitbox Size
stat.resettime = Reset Time
+9 -5
View File
@@ -266,7 +266,7 @@ public class Damage{
World.raycastEachNoDiagonalWorld(x, y, seg2.x, seg2.y, (cx, cy) -> {
Building tile = world.build(cx, cy);
boolean collide = tile != null && tile.collide(hitter) && hitter.checkUnderBuild(tile, cx * tilesize, cy * tilesize)
&& ((tile.team != team && tile.collide(hitter)) || hitter.type.testCollision(hitter, tile)) && collidedBlocks.add(tile.pos());
&& ((tile.team != team && tile.collide(hitter)) || hitter.type.testCollision(hitter, tile)) && collidedBlocks.add(tile.pos());
if(collide){
collided.add(collidePool.obtain().set(cx * tilesize, cy * tilesize, tile));
@@ -468,14 +468,19 @@ public class Damage{
damage(team, x, y, radius, damage, false, air, ground);
}
/** Applies a status effect to all enemy units in a range. */
public static void status(Team team, float x, float y, float radius, StatusEffect effect, float duration, boolean air, boolean ground){
status(team, x, y, radius, effect, duration, air, ground, 1f);
}
public static void status(Team team, float x, float y, float radius, StatusEffect effect, float duration, boolean air, boolean ground, float chance){
Cons<Unit> cons = entity -> {
if(entity.team == team || !entity.checkTarget(air, ground) || !entity.hittable() || !entity.within(x, y, radius)){
return;
}
entity.apply(effect, duration);
if(Mathf.chance(chance)){
entity.apply(effect, duration);
}
};
rect.setSize(radius * 2).setCenter(x, y);
@@ -485,7 +490,6 @@ public class Damage{
Units.nearby(rect, cons);
}
}
/** Damages all entities and blocks in a radius that are enemies of the team. */
public static void damage(Team team, float x, float y, float radius, float damage, boolean complete){
damage(team, x, y, radius, damage, complete, true, true);
@@ -676,4 +680,4 @@ public class Damage{
target = null;
}
}
}
}
+1 -1
View File
@@ -93,4 +93,4 @@ public class Lightning{
Fx.lightning.at(x, y, rotation, color, lines);
}
}
}
@@ -120,6 +120,10 @@ public class BulletType extends Content implements Cloneable{
public StatusEffect status = StatusEffects.none;
/** Intensity of applied status effect in terms of duration. */
public float statusDuration = 60 * 8f;
/** Chance for this bullet to apply a status effect */
public float statusChance = 1f;
/** Chance for lightning chains to apply a status effect. If -1, uses the parent bullet's statusChance instead. */
public float lightningStatusChance = -1f;
/** Turret only. If false, blocks will not be targeted. */
public boolean targetBlocks = true;
/** Turret only. If false, missiles will not be targeted. */
@@ -513,7 +517,10 @@ public class BulletType extends Content implements Cloneable{
if(impact) Tmp.v3.setAngle(b.rotation() + (knockback < 0 ? 180f : 0f));
unit.impulse(Tmp.v3);
}
unit.apply(status, statusDuration);
if(Mathf.chance(getStatusChance(b))){
unit.apply(status, statusDuration);
}
Events.fire(bulletDamageEvent.set(unit, b));
}
@@ -600,7 +607,7 @@ public class BulletType extends Content implements Cloneable{
Damage.damage(b.team, x, y, splashDamageRadius, splashDamage * b.damageMultiplier(), splashDamagePierce, collidesAir, collidesGround, scaledSplashDamage, b, armorMultiplier);
if(status != StatusEffects.none){
Damage.status(b.team, x, y, splashDamageRadius, status, statusDuration, collidesAir, collidesGround);
Damage.status(b.team, x, y, splashDamageRadius, status, statusDuration, collidesAir, collidesGround, getStatusChance(b));
}
if(heals()){
@@ -638,6 +645,21 @@ public class BulletType extends Content implements Cloneable{
}
}
// used for lightning bullets only
public float getLightningStatusChance(){
return lightningStatusChance < 0f ? statusChance : lightningStatusChance;
}
/** Gets the status chance from lightning bullets */
public float getStatusChance(Bullet b){
if(b.data instanceof Bullet parent &&
(parent.type.lightningType == b.type || parent.type.lightningType == null && b.type == Bullets.damageLightning)){
return parent.type.getLightningStatusChance();
}
return statusChance;
}
/** Called when the bullet reaches the end of its lifetime or is destroyed by something external. */
public void despawned(Bullet b){
if(despawnHit){
@@ -1,5 +1,6 @@
package mindustry.entities.bullet;
import arc.math.*;
import mindustry.*;
import mindustry.content.*;
import mindustry.entities.*;
@@ -57,7 +58,9 @@ public class EmpBulletType extends BasicBulletType{
hitPowerEffect.at(other.x, other.y, b.angleTo(other), hitColor);
chainEffect.at(x, y, 0, hitColor, other);
other.damage(damage * unitDamageScl);
other.apply(status, statusDuration);
if(Mathf.chance(getStatusChance(b))){
other.apply(status, statusDuration);
}
}
});
}
@@ -260,4 +260,4 @@ abstract class StatusComp implements Posc{
public boolean hasEffect(StatusEffect effect){
return applied.get(effect.id);
}
}
}
+1 -1
View File
@@ -39,7 +39,7 @@ public class StatusEffect extends UnlockableContent{
public float intervalDamage;
/** If true, interval damage is armor piercing. */
public boolean intervalDamagePierce = false;
/** Chance of effect appearing. */
/** Chance of visual effect(s) appearing. */
public float effectChance = 0.15f;
/** Should the effect be given a parent. */
public boolean parentizeEffect;
@@ -30,6 +30,7 @@ public class TractorBeamTurret extends BaseTurret{
public Color laserColor = Color.white;
public StatusEffect status = StatusEffects.none;
public float statusDuration = 300;
public float statusChance = 1f;
public Sound shootSound = Sounds.beamParallax;
public float shootSoundVolume = 0.9f;
@@ -59,6 +60,9 @@ public class TractorBeamTurret extends BaseTurret{
stats.add(Stat.targetsAir, targetAir);
stats.add(Stat.targetsGround, targetGround);
if(damage > 0) stats.add(Stat.damage, damage * 60f, StatUnit.perSecond);
if(status != StatusEffects.none && statusChance > 0f){
stats.add(Stat.status, StatValues.statusText(status, statusDuration, statusChance));
}
}
@Override
@@ -125,7 +129,7 @@ public class TractorBeamTurret extends BaseTurret{
target.damageContinuousPierce(damage * eff * timeScale * state.rules.blockDamage(team));
}
if(status != StatusEffects.none){
if(status != StatusEffects.none && Mathf.chance(statusChance)){
target.apply(status, statusDuration);
}
+1
View File
@@ -90,6 +90,7 @@ public class Stat implements Comparable<Stat>{
targetsAir = new Stat("targetsAir", StatCat.function),
targetsGround = new Stat("targetsGround", StatCat.function),
damage = new Stat("damage", StatCat.function),
status = new Stat("status", StatCat.function),
frequency = new Stat("frequency", StatCat.function),
ammo = new Stat("ammo", StatCat.function),
ammoCapacity = new Stat("ammoCapacity", StatCat.function),
+28 -4
View File
@@ -225,6 +225,16 @@ public class StatValues{
return withTooltip(element, content, false);
}
/** Displays the chance for status effects */
public static String statusText(StatusEffect status, float duration, float chance){
return (chance < 1f ? Core.bundle.format("stat.chance", Strings.autoFixed(chance * 100f, 4)) : "") +
(status.hasEmoji() ? status.emoji() + " " : "") +
"[stat]" + status.localizedName +
(status.reactive ? "" :
"[lightgray] ~ [stat]" + Strings.autoFixed(duration / 60f, 1) +
"[lightgray] " + Core.bundle.get("unit.seconds"));
}
/** Displays an item with a specified amount. */
private static Stack stack(TextureRegion region, int amount, @Nullable UnlockableContent content, boolean tooltip){
Stack stack = new Stack();
@@ -713,8 +723,21 @@ public class StatValues{
sep(bt, "@bullet.homing");
}
// Showing the correct value for lightning damage is annoyinh
if(type.lightning > 0){
sep(bt, Core.bundle.format("bullet.lightning", type.lightning, type.lightningDamage < 0 ? type.damage : type.lightningDamage));
sep(bt, Core.bundle.format(
"bullet.lightning",
type.lightning,
type.lightningDamage < 0 ? type.damage : type.lightningDamage
));
BulletType lightningType = type.lightningType == null ? Bullets.damageLightning : type.lightningType;
float chance = type.getLightningStatusChance();
if(lightningType.status != StatusEffects.none && chance > 0f){
sep(bt, statusText(lightningType.status, lightningType.statusDuration, chance))
.with(c -> withTooltip(c, lightningType.status));
}
}
if(type instanceof LaserBulletType b && b.lightningSpacing > 0){
@@ -778,9 +801,10 @@ public class StatValues{
sep(bt, Core.bundle.format("bullet.suppression", Strings.autoFixed(type.suppressionDuration / 60f, 2), Strings.fixed(type.suppressionRange / tilesize, 1)));
}
if(type.status != StatusEffects.none){
sep(bt, (type.status.hasEmoji() ? type.status.emoji() : "") + "[stat]" + type.status.localizedName + (type.status.reactive ? "" : "[lightgray] ~ [stat]" +
Strings.autoFixed(type.statusDuration / 60f, 1) + "[lightgray] " + Core.bundle.get("unit.seconds"))).with(c -> withTooltip(c, type.status));
// It is redundant to show the status effect if it can't be applied
if(type.status != StatusEffects.none && type.statusChance > 0f){
sep(bt, statusText(type.status, type.statusDuration, type.statusChance))
.with(c -> withTooltip(c, type.status));
}
if(!type.targetMissiles){