Skip to content

Commit

Permalink
Brunt rework - stats are still WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
Slotterleet committed Jul 23, 2024
1 parent b053a2b commit da0421c
Show file tree
Hide file tree
Showing 35 changed files with 316 additions and 256 deletions.
3 changes: 2 additions & 1 deletion res/bundles/bundle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ stat.fos-hackchancemultiplier = Hack Chance Multiplier
stat.fos-hackhpthreshold = Health Thresholds
stat.fos-maxbeams = Maximum Beams
stat.fos-damagereduction = Damage Reduction
stat.fos-healthtrigger = activates at [accent]<{0}%[] health

unittype = [gray]Type: []
unittype.infantry = [accent]Infantry[]
Expand Down Expand Up @@ -396,7 +397,7 @@ unit.fos-assault.description = Fires homing bullets at enemy targets.
unit.fos-abrupt.name = Abrupt
unit.fos-abrupt.description = Fires barrages of bombs at enemy targets.
unit.fos-brunt.name = Brunt
unit.fos-brunt.description = Self-destructs, causing a huge explosion.
unit.fos-brunt.description = Shoots slowing missiles at enemy targets. Activates a self-destruct protocol after sustaining enough damage, causing a huge explosion.
# TODO: bug names
unit.fos-grain.name = Grain
unit.fos-grain.description = An agile insect species that burrows underground to evade defenses and ambush ore detectors, shutting down most types in a single slash.
Expand Down
3 changes: 2 additions & 1 deletion res/bundles/bundle_ru.properties
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ stat.fos-hackchancemultiplier = Множитель шанса взлома
stat.fos-hackhpthreshold = Пороги прочности
stat.fos-maxbeams = Максимум лучей
stat.fos-damagereduction = Поглощение урона
stat.fos-healthtrigger = включается при [accent]<{0}%[] прочности

unittype = [gray]Тип: []
unittype.infantry = [accent]Пехота[]
Expand Down Expand Up @@ -396,7 +397,7 @@ unit.fos-assault.description = Стреляет самонаводящимися
unit.fos-abrupt.name = Перфорация
unit.fos-abrupt.description = Стреляет залпами бомб по вражеским целям.
unit.fos-brunt.name = Подавление
unit.fos-brunt.description = Самоуничтожается, вызывая огромный взрыв.
unit.fos-brunt.description = Стреляет замедляющими ракетами по вражеским целям. Активирует протокол самоуничтожения при получении достаточного урона, вызывая огромный взрыв.
# TODO: названия жуков
unit.fos-grain.name = Песчинка
unit.fos-grain.description = Ловкий вид насекомых, зарывающийся под землю, чтобы обойти оборону и атаковать детекторы руды, отключая большинство типов одним ударом.
Expand Down
Binary file removed res/sprites/units/bosses/citadel-front.png
Binary file not shown.
File renamed without changes
Binary file added res/sprites/units/bosses/citadel/citadel-front.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
File renamed without changes
Binary file added res/sprites/units/destroyers/brunt-cell.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added res/sprites/units/destroyers/brunt.png
Binary file removed res/sprites/units/dummy-submarine-cell.png
Diff not rendered.
Binary file removed res/sprites/units/dummy-submarine.png
Diff not rendered.
Binary file removed res/sprites/units/warden-treads.png
Diff not rendered.
20 changes: 19 additions & 1 deletion src/fos/content/FOSFx.java
Original file line number Diff line number Diff line change
Expand Up @@ -226,5 +226,23 @@ public class FOSFx {

burrowDust = new Effect(180f, e -> {
burrowDustSingle.at(e.x, e.y, e.color);
});
}),

bruntChargeSmoke = new Effect(80f, e -> {
color(Pal.reactorPurple2);
alpha(e.fin());

randLenVectors(e.id, 1, e.fout() * 20f, (x, y) -> {
float rad = 12f * e.fout();

Fill.circle(e.x + x, e.y + y, rad);
Drawf.light(e.x + x, e.y + y, rad * 2.5f, Pal.reactorPurple, 0.5f);
});
}).followParent(true).layer(Layer.flyingUnit + 0.01f),

bruntCharge = new Effect(300f, e -> {
if (Mathf.chance(0.08f) && e.time < 220f) {
bruntChargeSmoke.at(e.x, e.y);
}
}).followParent(true);
}
324 changes: 117 additions & 207 deletions src/fos/content/FOSUnitTypes.java

Large diffs are not rendered by default.

46 changes: 0 additions & 46 deletions src/fos/type/abilities/DamageFieldAbility.java

This file was deleted.

176 changes: 176 additions & 0 deletions src/fos/type/units/weapons/HealthTriggerWeapon.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
package fos.type.units.weapons;

import arc.Core;
import arc.math.*;
import arc.math.geom.Vec2;
import arc.scene.ui.layout.Table;
import arc.util.*;
import mindustry.audio.SoundLoop;
import mindustry.entities.*;
import mindustry.entities.units.WeaponMount;
import mindustry.gen.*;
import mindustry.type.*;

import static mindustry.Vars.*;

public class HealthTriggerWeapon extends Weapon {
/** Health fraction needed to reach before activating the weapon. */
public float healthFrac = 0.5f;

public HealthTriggerWeapon() {
super();
}

public HealthTriggerWeapon(String name) {
super(name);
}

@Override
public void addStats(UnitType u, Table t) {
super.addStats(u, t);

t.row();
t.add("[lightgray]" + Core.bundle.format("stat.fos-healthtrigger", Mathf.round(healthFrac * 100)));
}

@Override
public void update(Unit unit, WeaponMount mount) {
boolean can = unit.canShoot() && unit.health / unit.maxHealth < healthFrac;
float lastReload = mount.reload;
mount.reload = Math.max(mount.reload - Time.delta * unit.reloadMultiplier, 0);
mount.recoil = Mathf.approachDelta(mount.recoil, 0, unit.reloadMultiplier / recoilTime);
if(recoils > 0){
if(mount.recoils == null) mount.recoils = new float[recoils];
for(int i = 0; i < recoils; i++){
mount.recoils[i] = Mathf.approachDelta(mount.recoils[i], 0, unit.reloadMultiplier / recoilTime);
}
}
mount.smoothReload = Mathf.lerpDelta(mount.smoothReload, mount.reload / reload, smoothReloadSpeed);
mount.charge = mount.charging && shoot.firstShotDelay > 0 ? Mathf.approachDelta(mount.charge, 1, 1 / shoot.firstShotDelay) : 0;

float warmupTarget = (can && mount.shoot) || (continuous && mount.bullet != null) || mount.charging ? 1f : 0f;
if(linearWarmup){
mount.warmup = Mathf.approachDelta(mount.warmup, warmupTarget, shootWarmupSpeed);
}else{
mount.warmup = Mathf.lerpDelta(mount.warmup, warmupTarget, shootWarmupSpeed);
}

//rotate if applicable
if(rotate && (mount.rotate || mount.shoot) && can){
float axisX = unit.x + Angles.trnsx(unit.rotation - 90, x, y),
axisY = unit.y + Angles.trnsy(unit.rotation - 90, x, y);

mount.targetRotation = Angles.angle(axisX, axisY, mount.aimX, mount.aimY) - unit.rotation;
mount.rotation = Angles.moveToward(mount.rotation, mount.targetRotation, rotateSpeed * Time.delta);
if(rotationLimit < 360){
float dst = Angles.angleDist(mount.rotation, baseRotation);
if(dst > rotationLimit/2f){
mount.rotation = Angles.moveToward(mount.rotation, baseRotation, dst - rotationLimit/2f);
}
}
}else if(!rotate){
mount.rotation = baseRotation;
mount.targetRotation = unit.angleTo(mount.aimX, mount.aimY);
}

float
weaponRotation = unit.rotation - 90 + (rotate ? mount.rotation : baseRotation),
mountX = unit.x + Angles.trnsx(unit.rotation - 90, x, y),
mountY = unit.y + Angles.trnsy(unit.rotation - 90, x, y),
bulletX = mountX + Angles.trnsx(weaponRotation, this.shootX, this.shootY),
bulletY = mountY + Angles.trnsy(weaponRotation, this.shootX, this.shootY),
shootAngle = bulletRotation(unit, mount, bulletX, bulletY);

//find a new target
if(!controllable && autoTarget){
if((mount.retarget -= Time.delta) <= 0f){
mount.target = findTarget(unit, mountX, mountY, bullet.range, bullet.collidesAir, bullet.collidesGround);
mount.retarget = mount.target == null ? targetInterval : targetSwitchInterval;
}

if(mount.target != null && checkTarget(unit, mount.target, mountX, mountY, bullet.range)){
mount.target = null;
}

boolean shoot = false;

if(mount.target != null){
shoot = mount.target.within(mountX, mountY, bullet.range + Math.abs(shootY) + (mount.target instanceof Sized s ? s.hitSize()/2f : 0f)) && can;

if(predictTarget){
Vec2 to = Predict.intercept(unit, mount.target, bullet.speed);
mount.aimX = to.x;
mount.aimY = to.y;
}else{
mount.aimX = mount.target.x();
mount.aimY = mount.target.y();
}
}

mount.shoot = mount.rotate = shoot;

//note that shooting state is not affected, as these cannot be controlled
//logic will return shooting as false even if these return true, which is fine
}

if(alwaysShooting) mount.shoot = true;

//update continuous state
if(continuous && mount.bullet != null){
if(!mount.bullet.isAdded() || mount.bullet.time >= mount.bullet.lifetime || mount.bullet.type != bullet){
mount.bullet = null;
}else{
mount.bullet.rotation(weaponRotation + 90);
mount.bullet.set(bulletX, bulletY);
mount.reload = reload;
mount.recoil = 1f;
unit.vel.add(Tmp.v1.trns(unit.rotation + 180f, mount.bullet.type.recoil * Time.delta));
if(shootSound != Sounds.none && !headless){
if(mount.sound == null) mount.sound = new SoundLoop(shootSound, 1f);
mount.sound.update(bulletX, bulletY, true);
}

if(alwaysContinuous && mount.shoot){
mount.bullet.time = mount.bullet.lifetime * mount.bullet.type.optimalLifeFract * mount.warmup;
mount.bullet.keepAlive = true;

unit.apply(shootStatus, shootStatusDuration);
}
}
}else{
//heat decreases when not firing
mount.heat = Math.max(mount.heat - Time.delta * unit.reloadMultiplier / cooldownTime, 0);

if(mount.sound != null){
mount.sound.update(bulletX, bulletY, false);
}
}

//flip weapon shoot side for alternating weapons
boolean wasFlipped = mount.side;
if(otherSide != -1 && alternate && mount.side == flipSprite && mount.reload <= reload / 2f && lastReload > reload / 2f){
unit.mounts[otherSide].side = !unit.mounts[otherSide].side;
mount.side = !mount.side;
}

//shoot if applicable
if(mount.shoot && //must be shooting
can && //must be able to shoot
(!useAmmo || unit.ammo > 0 || !state.rules.unitAmmo || unit.team.rules().infiniteAmmo) && //check ammo
(!alternate || wasFlipped == flipSprite) &&
mount.warmup >= minWarmup && //must be warmed up
unit.vel.len() >= minShootVelocity && //check velocity requirements
(mount.reload <= 0.0001f || (alwaysContinuous && mount.bullet == null)) && //reload has to be 0, or it has to be an always-continuous weapon
(alwaysShooting || Angles.within(rotate ? mount.rotation : unit.rotation + baseRotation, mount.targetRotation, shootCone)) //has to be within the cone
){
shoot(unit, mount, bulletX, bulletY, shootAngle);

mount.reload = reload;

if(useAmmo){
unit.ammo--;
if(unit.ammo < 0) unit.ammo = 0;
}
}
}
}

0 comments on commit da0421c

Please sign in to comment.