Merge remote-tracking branch 'origin/master'

This commit is contained in:
Anuken
2026-09-01 17:18:41 -04:00
3 changed files with 106 additions and 23 deletions
+2 -2
View File
@@ -3413,7 +3413,7 @@ name.token.read = olvasás
name.token.write = írás
name.token.from = innen:
name.token.char = karakter
name.token.linknum = hivatkozás\#
name.token.linknum = hivatkozás#
name.token.draw = rajzolás
name.token.clear = törlés
name.token.apply = alkalmaz
@@ -3472,7 +3472,7 @@ name.token.volume = hangerő
name.token.pitch = hangmagasság
name.token.pan = térhatás
name.token.limit = korlát
name.token.of-id- = azonosító alapján\#
name.token.of-id- = azonosító alapján#
name.token.replace = csere?
name.token.for = ezért:
name.token.success = siker
+18 -5
View File
@@ -355,19 +355,25 @@ public class UnitTypes{
nova = new UnitType("nova"){{
canBoost = true;
boostMultiplier = 1.5f;
boostMultiplier = 2f;
speed = 0.55f;
hitSize = 8f;
health = 120f;
health = 200f;
buildSpeed = 0.3f;
armor = 1f;
abilities.add(new RepairFieldAbility(10f, 60f * 4, 60f));
abilities.add(new RepairFieldAbility(20f, 60f * 2, 100f){{
sameTypeHealMult = 0.15f;
maxTargets = 6;
smartHeal = true;
smartDowntime = 60 * 4f;
}});
weapons.add(new Weapon("heal-weapon"){{
top = false;
shootY = 2f;
reload = 24f;
reload = 30f;
x = 4.5f;
alternate = false;
ejectEffect = Fx.none;
@@ -377,6 +383,9 @@ public class UnitTypes{
bullet = new LaserBoltBulletType(5.2f, 13){{
lifetime = 30f;
healPercent = 5f;
pierce = true;
pierceBuilding = true;
pierceCap = 2;
collidesTeam = true;
backColor = Pal.heal;
frontColor = Color.white;
@@ -1370,7 +1379,11 @@ public class UnitTypes{
mineSpeed = 3.5f;
wreckSoundVolume = 0.9f;
abilities.add(new RepairFieldAbility(5f, 60f * 8, 50f));
abilities.add(new RepairFieldAbility(){{
amount = 5f;
reload = 60f * 8f;
range = 50f;
}});
weapons.add(new Weapon("poly-weapon"){{
top = false;
@@ -4,10 +4,12 @@ import arc.*;
import arc.audio.*;
import arc.math.*;
import arc.scene.ui.layout.*;
import arc.struct.*;
import arc.util.*;
import mindustry.content.*;
import mindustry.entities.*;
import mindustry.gen.*;
import java.util.*;
import static mindustry.Vars.*;
@@ -20,11 +22,29 @@ public class RepairFieldAbility extends Ability{
public boolean parentizeEffects = false;
/** Multiplies healing to units of the same type by this amount. */
public float sameTypeHealMult = 1f;
/** Maximum number of units healed. */
public int maxTargets = -1;
protected float timer;
protected boolean wasHealed = false;
/** If true, this ability will consider missing hp, number of targets, cooldowns...etc. */
public boolean smartHeal = false;
/** If a damaged unit health % is lower than this heal it as soon as possible. Values close to 1f will likely waste healing potential. */
public float smartHealPercent = 0f;
/** A multiplier on potential healing efficiency. High values -> efficient aoe healing. Low values -> efficient single target healing. */
public float smartHealStrength = 1f;
/** If all damaged units havent been hurt for at least this amount of time, force healing them, regardless of thresholds. */
public float smartDowntime = 60f * 8f;
/** How often to check for healing when at least 1 damaged unit is found. */
public float smartInterval = 20f;
/** Random initial reload multiplier to check for damaged units. Useful for desyncing healer timers once. */
public float randDesync = -1f;
RepairFieldAbility(){}
//a high capacity is needed to account for t1 spam in custom gamemodes, but set too high it becomes expensive
protected static Seq<Unit> targets = new Seq<>(50);
protected float healTimer, downTimer;
protected float healthMissing, sumMaxHealth, sumTypeMult, healthChange;
protected boolean hasHealed, healNow;
public RepairFieldAbility(){}
public RepairFieldAbility(float amount, float reload, float range){
this.amount = amount;
@@ -57,30 +77,80 @@ public class RepairFieldAbility extends Ability{
t.row();
t.add(abilityStat("sametypehealmultiplier", (sameTypeHealMult < 1f ? "[negstat]" : "") + Strings.autoFixed(sameTypeHealMult * 100f, 2)));
}
if(maxTargets > 0){
t.row();
t.add(abilityStat("maxtargets", maxTargets));
}
}
@Override
public void update(Unit unit){
timer += Time.delta;
healTimer += Time.delta;
downTimer = smartHeal && healthChange >= healthMissing && healthMissing > 0f ? downTimer + Time.delta : 0f;
if(timer >= reload){
wasHealed = false;
if(healTimer >= reload){
targets.clear();
hasHealed = healNow = false;
healthChange = healthMissing;
healthMissing = sumMaxHealth = sumTypeMult = 0f;
boolean limitTargets = maxTargets >= 0;
float healPercentMult = healPercent / 100f;
Units.nearby(unit.team, unit.x, unit.y, range, other -> {
//check for 2 more targets just in case
if(limitTargets && targets.size >= maxTargets + 2) return;
if(other.damaged()){
healEffect.at(other, parentizeEffects);
wasHealed = true;
targets.add(other);
if(smartHeal){
float maxHealth = other.maxHealth();
healthMissing += maxHealth - other.health();
sumMaxHealth += maxHealth;
sumTypeMult += unit.type == other.type ? sameTypeHealMult : 1f;
if(other.healthf() < smartHealPercent) healNow = true;
}
}
float healMult = unit.type == other.type ? sameTypeHealMult : 1f;
other.heal((amount + healPercent / 100f * other.maxHealth()) * healMult);
});
int targetCount = targets.size;
if(wasHealed){
activeEffect.at(unit, range);
sound.at(unit, 1f + Mathf.range(0.1f), soundVolume);
//mixed approach, care both about groups and single low hp units
float ratio = amount + healPercentMult * sumMaxHealth * (sumTypeMult / targetCount);
float requiredHeals = (healthMissing * 0.7f + healthMissing / (limitTargets ? maxTargets : targetCount) * 0.3f) / smartHealStrength / ratio;
if(requiredHeals >= 1f || !smartHeal || healNow || downTimer >= smartDowntime){
//sort closest if number of targets is limited
if(limitTargets){
boolean isSameType = sameTypeHealMult < 1f;
targets.sort(u -> u.dst2(unit.x, unit.y) + (isSameType && u.type() == unit.type ? 6400f : 0f));
}
int len = limitTargets ? Math.min(targetCount, maxTargets) : targetCount;
for(int i = 0; i < len; i++){
Unit other = targets.get(i);
if(other.damaged()){
float maxHealth = other.maxHealth();
float healMult = unit.type == other.type ? sameTypeHealMult : 1f;
other.heal((amount + healPercentMult * maxHealth) * healMult);
healEffect.at(other, parentizeEffects);
hasHealed = true;
}
}
if(hasHealed){
healTimer = 0f;
activeEffect.at(unit, range);
sound.at(unit, 1f + Mathf.range(0.1f), soundVolume);
}
//increase how often this checks if there are damaged units but still below the healing threshold
}else if(smartHeal && targetCount > 0){
healTimer = reload >= (2f * smartInterval) ? reload - smartInterval : smartInterval;
}else if(randDesync > 0){
healTimer = Mathf.random(randDesync) * reload;
}else{
healTimer = 0;
}
timer = 0f;
}
}
}
}