From 60621520eed7676900a66f6d04a09571c664418c Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 4 Feb 2022 15:37:51 -0500 Subject: [PATCH] WIP Disrupt missiles --- .../assets-raw/sprites/units/conquer.aseprite | Bin 8411 -> 0 bytes .../sprites/units/weapons/disrupt-missile.png | Bin 0 -> 502 bytes .../units/weapons/disrupt-weapon-blade.png | Bin 0 -> 886 bytes .../sprites/units/weapons/disrupt-weapon.png | Bin 0 -> 815 bytes .../sprites/units/weapons/quell-weapon.png | Bin 839 -> 844 bytes core/assets/icons/icons.properties | 1 + core/src/mindustry/ai/types/MissileAI.java | 16 ++-- core/src/mindustry/content/Blocks.java | 4 +- core/src/mindustry/content/UnitTypes.java | 70 +++++++++++------- core/src/mindustry/entities/Damage.java | 13 +++- .../mindustry/entities/bullet/BulletType.java | 11 ++- .../entities/bullet/ExplosionBulletType.java | 28 +++++++ .../src/mindustry/entities/comp/UnitComp.java | 6 +- .../part/{WeaponPart.java => DrawPart.java} | 11 ++- .../mindustry/entities/part/RegionPart.java | 30 +++++--- .../mindustry/entities/units/UnitDecal.java | 36 --------- core/src/mindustry/mod/ContentParser.java | 7 +- core/src/mindustry/type/UnitType.java | 41 +++++----- core/src/mindustry/type/Weapon.java | 46 ++++++------ .../mindustry/type/unit/MissileUnitType.java | 5 +- .../mindustry/ui/dialogs/PlanetDialog.java | 2 +- core/src/mindustry/world/draw/DrawTurret.java | 4 +- 22 files changed, 188 insertions(+), 143 deletions(-) delete mode 100644 core/assets-raw/sprites/units/conquer.aseprite create mode 100644 core/assets-raw/sprites/units/weapons/disrupt-missile.png create mode 100644 core/assets-raw/sprites/units/weapons/disrupt-weapon-blade.png create mode 100644 core/assets-raw/sprites/units/weapons/disrupt-weapon.png create mode 100644 core/src/mindustry/entities/bullet/ExplosionBulletType.java rename core/src/mindustry/entities/part/{WeaponPart.java => DrawPart.java} (85%) delete mode 100644 core/src/mindustry/entities/units/UnitDecal.java diff --git a/core/assets-raw/sprites/units/conquer.aseprite b/core/assets-raw/sprites/units/conquer.aseprite deleted file mode 100644 index e1a86d06503499e562449e3114fe7e224d8d4fb4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8411 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 diff --git a/core/assets-raw/sprites/units/weapons/disrupt-missile.png b/core/assets-raw/sprites/units/weapons/disrupt-missile.png new file mode 100644 index 0000000000000000000000000000000000000000..6aadf02c8a51708130f99ecee95bd394f057a871 GIT binary patch literal 502 zcmVPx$ut`KgRA_%wVqq&-3N{fTVCzRDcELhPV-x%&Nn;bVN~eB=t&qkv zf~8=aE>^Q_hMjqD?zA-I?8qNTpJ#{73e-5&AHlty69HlQF$ddSFf< zz!uqOhE2wgszAwkR%jBXg)v1a5@yDUbrG%$IViA1+;J=t7as4&=X*q;vDohLBp3jhEB07*qoM6N<$g2{K}3IG5A literal 0 HcmV?d00001 diff --git a/core/assets-raw/sprites/units/weapons/disrupt-weapon-blade.png b/core/assets-raw/sprites/units/weapons/disrupt-weapon-blade.png new file mode 100644 index 0000000000000000000000000000000000000000..3cc4e4aaae19ce7c5729c4a8fa89679b9c12f4ba GIT binary patch literal 886 zcmV-+1Bv{JP)Px&FiAu~RCt{2n!Ab=K@^66#b6#_^G!yC22&qGVTM5r6k{_8#n3aS7|4iY1-pa|e@?E|Ee5?CzF$^-QSXj&eq8vv_(-w){f0l$9y z-F{5e3V=<%?*{lbpv24c&-4HxT017P4)A$qw76!g)M*_NaRDu>0LL+@7bAkf9U{N zr?h(IAKwNx(er7E`_w>1So+Rv91w{5d+H!Wt6$(I(l?X$IIJM+2E<3foM2MFX#h`a7>eV?hFYwf;x z!Si)5IfNEGUzJzfD)|_E100~iDJ>-;f2fH3p(65!ipU=-B0n`khPJ>2Px%=}AOERCt{2+P!KOK^TDHPqDBSECWJ_O~BS$5TYo-%3v+tfQ46JksA*pV>DvyL%ua00000000000000000000 z0000004VN8>j8*}y#Mg=cgp^+U%hO89nA-j8qsMTv+2omMRZ!mYhiM$&NqdXvXvb@fY1-HqXL{1ui09`QrtP=4w_;AC z9)dc)E)%o@>&jj5%LsoJR5h)xeN7dPlVqSHEN(Wh=G(#5y0A|kSJ=gH{*Up(AX z9sBU?VD$6l)rp9RY~6q0-6u76DCSMy2Q?zjnFg|M6FPS4><=~ZhD>3J=w{MTDAe3% z(_@s3>2oXh0jC4q-F!RVU{hrObi8~ouTIvM_~F^X+A`Nf6cKszXnniY_jjW)=Dw$T z*SvOVA0MqoKM(IwYY_*gF-6wzmm=5q%#?`EFy?gDK7F?9-EYWupG;cX(}*J10}MqJ znZID)GS3llIHtL!KX)w_(IG=8{`K>o>KNLXnr)UMI-#+p#Ec3VI^o>%46(*ciRgsJ z7#q_ZQX5h88WVE+?vL4jDTZUp9n%!jIum1KhF+>epM}hgC1*@^$k-{r6m#zBZMAfa z*k7pSnpN+SsZTWDg=iiuTKlJa=KHh8@zF~4Y83pgz4Q3F>1(3pLP8OjmT%~rORh1@ z`SSUb{k!(kj&rmF%Q+KkjbW+9;yp5V%#smHjX~2u9m%@PQ=}~I+_V!({nWP3%zuI9 znoaMK|5HrswH67J<2K-1-Z_?ai3w3`hLwofYebR4f3lJ@X3r5tD*WwF&8K}x>HB+? zhezv}H6D`Hp2DqS)_T*Z`Z`lPX3f{NuHFl+p?i9-?Peg3HfB!|MLG;AvK+Iwh$5Ya t6!FLGF(Lo}0000000000004l$*B`1ZhM8W>hh+c&002ovPDHLkV1k@Nhp+$u literal 0 HcmV?d00001 diff --git a/core/assets-raw/sprites/units/weapons/quell-weapon.png b/core/assets-raw/sprites/units/weapons/quell-weapon.png index 15aaca767625350da6b3ba3000e74be40edfac40..44f39f794f85a6d332aa7faf94940b26396b2ed8 100644 GIT binary patch delta 808 zcmV+@1K0e=2FwPKF@FY0L_t(&f$f>UYg9oH$0wq2e}KIpC&B|83rkxe0VMJK(sw+X^yj;`MSUsVi*S-A-T-wo4zl~uV|)9H`!&yaYHQ&s3x8Z${OHmaA5LhZRZ6Ge^FqfuitEn z?a*!o^C%Q5Pk)dD%b!Psg+XzN;I`gi@O77ZfCo7(%CXX?w|)U^R#Ue2rRHStYQl*1q2{;=&aVJ>ma z&iS{3sa3EBlrKpt(XJbs?V5i8fDhx5n73%O&JYHcMt{ERhN@ljpGpE_4c;X;c=(I8 zS#|UK5o_~+kq^BOo5ZNGE2Z9Y&+a&5kp zuu7n2KuVAT8$Njk0C&qKPOi-trGe#TPYPxk@e~Y!P#RcKnS|oNxH@irec@U{$49d} zKqwBZs-Q*XV3xwvBac4~2*CLEOC5O+ceP66S$}{4j5C#9zi>+h6}kLUpQzK5Lp5i$ z0d;Z4B@y|epnReQF)p&U^F0000eg-?MU~|o33K3CIuo6suRaLE|?v9_k$#~l3%$?LxAI@lil_nS)`U&r%G<-SuXu)cu+0M>FRPR7$tO6ce| zo$R*{r>yPAuTwageaXr%Yo~G#*=5BsLoExaB%TRM8{lDZrt+s9X9aVAP=rK3ej1A7 zf?W${kt-BVkbeWi*S*2qpt!^^00-&`_S(ZqDxp*<#(v&CeJ;+Yc&EHe$HQL{R3A6F zhu>_d_d9Qj0{7|PbWimSBF3BP=c#w<`^n670M6%&B6~@E6%^(0_s@Uq_>eG{c*@TC z*McRx;1m!pNh;B{ZPfOZe*l25ld+h$Xw>dd2rR99+kZAmd&++-35+#ZOK$M?4{Ouv z>F+vgvw*1|6cW(E_6UX>hgw&ph8u_CTd))#OAW<6&f`RK!Qy!wnB_Pz9lqExm4FqD z8jt;2&qs4d5dui1vUt2#`I5#3|2YSKR+}UR3V*nyz=B=+gC@6w$?pBArV}T~f#K^E zSV>ds)_-V= unit.type.homingDelay && shooter != null){ + unit.lookAt(shooter.aimX, shooter.aimY); } //move forward forever @@ -26,8 +29,7 @@ public class MissileAI extends AIController{ } @Override - public boolean retarget(){ - //more frequent retarget. TODO won't this lag? - return timer.get(timerTarget, 10f); + public void updateTargeting(){ + //no } } diff --git a/core/src/mindustry/content/Blocks.java b/core/src/mindustry/content/Blocks.java index 9b0a534038..3862b910f6 100644 --- a/core/src/mindustry/content/Blocks.java +++ b/core/src/mindustry/content/Blocks.java @@ -2529,7 +2529,7 @@ public class Blocks{ thrusterLength = 40/4f; armor = 10f; - unitCapModifier = 12; + unitCapModifier = 10; researchCostMultiplier = 0.11f; }}; @@ -2544,7 +2544,7 @@ public class Blocks{ thrusterLength = 48/4f; armor = 15f; - unitCapModifier = 16; + unitCapModifier = 14; researchCostMultiplier = 0.11f; }}; diff --git a/core/src/mindustry/content/UnitTypes.java b/core/src/mindustry/content/UnitTypes.java index e080706a70..869502cf83 100644 --- a/core/src/mindustry/content/UnitTypes.java +++ b/core/src/mindustry/content/UnitTypes.java @@ -12,7 +12,6 @@ import mindustry.entities.abilities.*; import mindustry.entities.bullet.*; import mindustry.entities.effect.*; import mindustry.entities.part.*; -import mindustry.entities.units.*; import mindustry.gen.*; import mindustry.graphics.*; import mindustry.type.*; @@ -2503,7 +2502,7 @@ public class UnitTypes{ }}; conquer = new TankUnitType("conquer"){{ - hitSize = 44f; + hitSize = 46f; treadPullOffset = 1; speed = 0.48f; health = 20000; @@ -2664,7 +2663,12 @@ public class UnitTypes{ }}; }}); - decals.add(new UnitDecal("conquer-glow", Color.red, Blending.additive, -1f)); + parts.add(new RegionPart("-glow"){{ + color = Color.red; + blending = Blending.additive; + layer = -1f; + outline = false; + }}); }}; //endregion @@ -2919,13 +2923,8 @@ public class UnitTypes{ mirror = false; reload = 1f; shootOnDeath = true; - bullet = new BulletType(){{ - rangeOverride = 20f; + bullet = new ExplosionBulletType(120f, 30f){{ shootEffect = Fx.massiveExplosion; - killShooter = true; - //TODO status? - splashDamageRadius = 30f; - splashDamage = 120f; }}; }}); }}; @@ -2946,7 +2945,7 @@ public class UnitTypes{ flying = true; drag = 0.07f; speed = 1f; - rotateSpeed = 2.5f; + rotateSpeed = 2f; accel = 0.1f; health = 10000f; armor = 7f; @@ -2979,16 +2978,36 @@ public class UnitTypes{ } //TODO needs weapons! cool missiles or something - if(false) - weapons.add(new Weapon("quell-weapon"){{ - x = 51 / 4f; - y = 5 / 4f; + weapons.add(new Weapon("disrupt-weapon"){{ + x = 78f / 4f; + y = -10f / 4f; + mirror = true; rotate = true; - rotateSpeed = 2f; - reload = 70f; - layerOffset = -0.001f; + rotateSpeed = 0.4f; + reload = 60f; + layerOffset = -20f; recoil = 1f; - rotationLimit = 60f; + rotationLimit = 22f; + minWarmup = 0.95f; + shootWarmupSpeed = 0.1f; + shootY = 2f; + shootCone = 40f; + shots = 3; + shotDelay = 5f; + inaccuracy = 28f; + + parts.add(new RegionPart("-blade"){{ + heatProgress = PartProgress.warmup; + progress = PartProgress.warmup.blend(PartProgress.reload, 0.15f); + heatColor = Color.valueOf("9c50ff"); + x = 5 / 4f; + y = 0f; + rotMove = -33f; + moveY = -1f; + moveX = -1f; + under = true; + mirror = true; + }}); bullet = new BulletType(){{ shootEffect = Fx.shootBig; @@ -2998,24 +3017,20 @@ public class UnitTypes{ keepVelocity = false; }}; - unitSpawned = new MissileUnitType("quell-missile"){{ - speed = 3.8f; + unitSpawned = new MissileUnitType("disrupt-missile"){{ + speed = 4.5f; maxRange = 80f; outlineColor = Pal.darkOutline; health = 45; + homingDelay = 10f; weapons.add(new Weapon(){{ shootCone = 360f; mirror = false; reload = 1f; shootOnDeath = true; - bullet = new BulletType(){{ - rangeOverride = 20f; + bullet = new ExplosionBulletType(120f, 30f){{ shootEffect = Fx.massiveExplosion; - killShooter = true; - //TODO status? - splashDamageRadius = 30f; - splashDamage = 120f; }}; }}); }}; @@ -3107,7 +3122,8 @@ public class UnitTypes{ shootEffect = Fx.colorSpark; hitEffect = smokeEffect = despawnEffect = Fx.hitLaserColor; - damage = 1; + //TODO 0, or 1? + damage = 0; }}; }}); }}; diff --git a/core/src/mindustry/entities/Damage.java b/core/src/mindustry/entities/Damage.java index 0ab16352f9..21006bddd6 100644 --- a/core/src/mindustry/entities/Damage.java +++ b/core/src/mindustry/entities/Damage.java @@ -387,11 +387,17 @@ public class Damage{ /** 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, boolean air, boolean ground){ + damage(team, x, y, radius, damage, complete, air, ground, false); + } + + /** 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, boolean air, boolean ground, boolean scaled){ Cons cons = entity -> { - if(entity.team == team || !entity.within(x, y, radius) || (entity.isFlying() && !air) || (entity.isGrounded() && !ground)){ + if(entity.team == team || !entity.within(x, y, radius + (scaled ? entity.hitSize / 2f : 0f)) || (entity.isFlying() && !air) || (entity.isGrounded() && !ground)){ return; } - float amount = calculateDamage(x, y, entity.getX(), entity.getY(), radius, damage); + + float amount = calculateDamage(scaled ? Math.max(0, entity.dst(x, y) - entity.type.hitSize/2) : entity.dst(x, y), radius, damage); entity.damage(amount); //TODO better velocity displacement float dst = tr.set(entity.getX() - x, entity.getY() - y).len(); @@ -504,8 +510,7 @@ public class Damage{ } } - private static float calculateDamage(float x, float y, float tx, float ty, float radius, float damage){ - float dist = Mathf.dst(x, y, tx, ty); + private static float calculateDamage(float dist, float radius, float damage){ float falloff = 0.4f; float scaled = Mathf.lerp(1f - dist / radius, 1f, falloff); return damage * scaled; diff --git a/core/src/mindustry/entities/bullet/BulletType.java b/core/src/mindustry/entities/bullet/BulletType.java index 0ee7e4e2d9..81441e4ac8 100644 --- a/core/src/mindustry/entities/bullet/BulletType.java +++ b/core/src/mindustry/entities/bullet/BulletType.java @@ -80,6 +80,8 @@ public class BulletType extends Content implements Cloneable{ public boolean instantDisappear; /** Damage dealt in splash. 0 to disable.*/ public float splashDamage = 0f; + /** If true, splash damage is "correctly" affected by unit hitbox size. Used for projectiles that do not collide / have splash as their main source of damage. */ + public boolean scaledSplashDamage = false; /** Knockback in velocity. */ public float knockback; /** Should knockback follow the bullet's direction */ @@ -142,6 +144,7 @@ public class BulletType extends Content implements Cloneable{ public @Nullable BulletType fragBullet = null; public Color hitColor = Color.white; public Color healColor = Pal.heal; + public Effect healEffect = Fx.healBlockFull; /** Bullets spawned when this bullet is created. Rarely necessary, used for visuals. */ public Seq spawnBullets = new Seq<>(); @@ -249,7 +252,7 @@ public class BulletType extends Content implements Cloneable{ } if(heals()&& build.team == b.team && !(build.block instanceof ConstructBlock)){ - Fx.healBlockFull.at(build.x, build.y, build.block.size, healColor); + healEffect.at(build.x, build.y, build.block.size, healColor); build.heal(healPercent / 100f * build.maxHealth + healAmount); }else if(build.team != b.team && direct){ hit(b); @@ -308,7 +311,7 @@ public class BulletType extends Content implements Cloneable{ } if(splashDamageRadius > 0 && !b.absorbed){ - Damage.damage(b.team, x, y, splashDamageRadius, splashDamage * b.damageMultiplier(), collidesAir, collidesGround); + Damage.damage(b.team, x, y, splashDamageRadius, splashDamage * b.damageMultiplier(), false, collidesAir, collidesGround, scaledSplashDamage); if(status != StatusEffects.none){ Damage.status(b.team, x, y, splashDamageRadius, status, statusDuration, collidesAir, collidesGround); @@ -316,7 +319,7 @@ public class BulletType extends Content implements Cloneable{ if(heals()){ indexer.eachBlock(b.team, x, y, splashDamageRadius, Building::damaged, other -> { - Fx.healBlockFull.at(other.x, other.y, other.block.size, healColor); + healEffect.at(other.x, other.y, other.block.size, healColor); other.heal(healPercent / 100f * other.maxHealth() + healAmount); }); } @@ -375,7 +378,7 @@ public class BulletType extends Content implements Cloneable{ } if(instantDisappear){ - b.time = lifetime; + b.time = lifetime + 1f; } if(spawnBullets.size > 0){ diff --git a/core/src/mindustry/entities/bullet/ExplosionBulletType.java b/core/src/mindustry/entities/bullet/ExplosionBulletType.java new file mode 100644 index 0000000000..0ad13fdb88 --- /dev/null +++ b/core/src/mindustry/entities/bullet/ExplosionBulletType.java @@ -0,0 +1,28 @@ +package mindustry.entities.bullet; + +import mindustry.content.*; + +public class ExplosionBulletType extends BulletType{ + + public ExplosionBulletType(float splashDamage, float splashDamageRadius){ + this.splashDamage = splashDamage; + this.splashDamageRadius = splashDamageRadius; + rangeOverride = Math.max(rangeOverride, splashDamageRadius * 2f / 3f); + } + + public ExplosionBulletType(){ + } + + { + hittable = false; + lifetime = 1f; + speed = 0f; + rangeOverride = 20f; + shootEffect = Fx.massiveExplosion; + instantDisappear = true; + scaledSplashDamage = true; + killShooter = true; + collides = false; + keepVelocity = false; + } +} diff --git a/core/src/mindustry/entities/comp/UnitComp.java b/core/src/mindustry/entities/comp/UnitComp.java index 8c5f626906..83bba188c5 100644 --- a/core/src/mindustry/entities/comp/UnitComp.java +++ b/core/src/mindustry/entities/comp/UnitComp.java @@ -513,7 +513,9 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I float shake = hitSize / 3f; - Effect.scorch(x, y, (int)(hitSize / 5)); + if(type.createScorch){ + Effect.scorch(x, y, (int)(hitSize / 5)); + } Effect.shake(shake, shake, this); type.deathSound.at(this); @@ -536,7 +538,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I Damage.damage(team, x, y, Mathf.pow(hitSize, 0.94f) * 1.25f, Mathf.pow(hitSize, 0.75f) * type.crashDamageMultiplier * 5f, true, false, true); } - if(!headless){ + if(!headless && type.createScorch){ for(int i = 0; i < type.wreckRegions.length; i++){ if(type.wreckRegions[i].found()){ float range = type.hitSize /4f; diff --git a/core/src/mindustry/entities/part/WeaponPart.java b/core/src/mindustry/entities/part/DrawPart.java similarity index 85% rename from core/src/mindustry/entities/part/WeaponPart.java rename to core/src/mindustry/entities/part/DrawPart.java index b529aa1d88..00e465e07e 100644 --- a/core/src/mindustry/entities/part/WeaponPart.java +++ b/core/src/mindustry/entities/part/DrawPart.java @@ -4,7 +4,7 @@ import arc.graphics.g2d.*; import arc.math.*; import arc.struct.*; -public abstract class WeaponPart{ +public abstract class DrawPart{ public static final PartParams params = new PartParams(); /** If true, turret shading is used. Don't touch this, it is set up in unit/block init()! */ @@ -37,11 +37,14 @@ public abstract class WeaponPart{ } public interface PartProgress{ + /** Reload of the weapon - 1 right after shooting, 0 when ready to fire*/ PartProgress - reload = p -> p.reload, + /** Reload, but smoothed out, so there is no sudden jump between 0-1. */ smoothReload = p -> p.smoothReload, + /** Weapon warmup, 0 when not firing, 1 when actively shooting. Not equivalent to heat. */ warmup = p -> p.warmup, + /** Weapon heat, 1 when just fired, 0, when it has cooled down (duration depends on weapon) */ heat = p -> p.heat; float get(PartParams p); @@ -74,6 +77,10 @@ public abstract class WeaponPart{ return p -> get(p) * other.get(p); } + default PartProgress mul(float amount){ + return p -> get(p) * amount; + } + default PartProgress min(PartProgress other){ return p -> Math.min(get(p), other.get(p)); } diff --git a/core/src/mindustry/entities/part/RegionPart.java b/core/src/mindustry/entities/part/RegionPart.java index 98ecfbd7ff..37d3a5d905 100644 --- a/core/src/mindustry/entities/part/RegionPart.java +++ b/core/src/mindustry/entities/part/RegionPart.java @@ -8,10 +8,13 @@ import arc.struct.*; import arc.util.*; import mindustry.graphics.*; -public class RegionPart extends WeaponPart{ +public class RegionPart extends DrawPart{ protected PartParams childParam = new PartParams(); + /** Appended to unit/weapon/block name and drawn. */ public String suffix = ""; + /** Overrides suffix if set. */ + public @Nullable String name; public TextureRegion heat; public TextureRegion[] regions = {}; public TextureRegion[] outlines = {}; @@ -34,12 +37,19 @@ public class RegionPart extends WeaponPart{ public float x, y, moveX, moveY; public @Nullable Color color, colorTo; public Color heatColor = Pal.turretHeat.cpy(); - public Seq children = new Seq<>(); + public Seq children = new Seq<>(); public RegionPart(String region){ this.suffix = region; } + public RegionPart(String region, Blending blending, Color color){ + this.suffix = region; + this.blending = blending; + this.color = color; + outline = false; + } + public RegionPart(){ } @@ -119,25 +129,27 @@ public class RegionPart extends WeaponPart{ @Override public void load(String name){ + String realName = this.name == null ? name + suffix : this.name; + if(drawRegion){ //TODO l/r if(mirror && turretShading){ regions = new TextureRegion[]{ - Core.atlas.find(name + suffix + "1"), - Core.atlas.find(name + suffix + "2") + Core.atlas.find(realName + "1"), + Core.atlas.find(realName + "2") }; outlines = new TextureRegion[]{ - Core.atlas.find(name + suffix + "1-outline"), - Core.atlas.find(name + suffix + "2-outline") + Core.atlas.find(realName + "1-outline"), + Core.atlas.find(realName + "2-outline") }; }else{ - regions = new TextureRegion[]{Core.atlas.find(name + suffix)}; - outlines = new TextureRegion[]{Core.atlas.find(name + suffix + "-outline")}; + regions = new TextureRegion[]{Core.atlas.find(realName)}; + outlines = new TextureRegion[]{Core.atlas.find(realName + "-outline")}; } } - heat = Core.atlas.find(name + suffix + "-heat"); + heat = Core.atlas.find(realName + "-heat"); for(var child : children){ child.load(name); } diff --git a/core/src/mindustry/entities/units/UnitDecal.java b/core/src/mindustry/entities/units/UnitDecal.java deleted file mode 100644 index cdd488d6fe..0000000000 --- a/core/src/mindustry/entities/units/UnitDecal.java +++ /dev/null @@ -1,36 +0,0 @@ -package mindustry.entities.units; - -import arc.graphics.*; -import arc.graphics.g2d.*; -import mindustry.graphics.*; - -/** A sprite drawn in addition to the base unit sprites. */ -public class UnitDecal{ - public String region = "error"; - public float x, y, rotation; - public float layer = Layer.flyingUnit + 1f; - public float xScale = 1f, yScale = 1f; - public Blending blending = Blending.normal; - public Color color = Color.white; - - public TextureRegion loadedRegion; - - public UnitDecal(String region, float x, float y, float rotation, float layer, Color color){ - this.region = region; - this.x = x; - this.y = y; - this.rotation = rotation; - this.layer = layer; - this.color = color; - } - - public UnitDecal(String region, Color color, Blending blending, float layer){ - this.region = region; - this.color = color; - this.layer = layer; - this.blending = blending; - } - - public UnitDecal(){ - } -} diff --git a/core/src/mindustry/mod/ContentParser.java b/core/src/mindustry/mod/ContentParser.java index 6a077d6bc8..19b4553eb7 100644 --- a/core/src/mindustry/mod/ContentParser.java +++ b/core/src/mindustry/mod/ContentParser.java @@ -25,7 +25,7 @@ import mindustry.entities.abilities.*; import mindustry.entities.bullet.*; import mindustry.entities.effect.*; import mindustry.entities.part.*; -import mindustry.entities.part.WeaponPart.*; +import mindustry.entities.part.DrawPart.*; import mindustry.game.*; import mindustry.game.Objectives.*; import mindustry.gen.*; @@ -130,13 +130,14 @@ public class ContentParser{ readFields(result, data); return result; }); - put(WeaponPart.class, (type, data) -> { + put(DrawPart.class, (type, data) -> { var bc = resolve(data.getString("type", ""), RegionPart.class); data.remove("type"); var result = make(bc); readFields(result, data); return result; }); + //TODO this is untested put(PartProgress.class, (type, data) -> { //simple case: it's a string or number constant if(data.isString()) return field(PartProgress.class, data.asString()); @@ -166,7 +167,7 @@ public class ContentParser{ case "mul" -> base.mul(parser.readValue(PartProgress.class, data.get("other"))); case "min" -> base.min(parser.readValue(PartProgress.class, data.get("other"))); case "sin" -> base.sin(data.getFloat("scl"), data.getFloat("mag")); - case "absin" -> base.sin(data.getFloat("scl"), data.getFloat("mag")); + case "absin" -> base.absin(data.getFloat("scl"), data.getFloat("mag")); case "curve" -> base.curve(parser.readValue(Interp.class, data.get("interp"))); default -> throw new RuntimeException("Unknown operation '" + op + "', check PartProgress class for a list of methods."); }; diff --git a/core/src/mindustry/type/UnitType.java b/core/src/mindustry/type/UnitType.java index c368b3118e..370c810e46 100644 --- a/core/src/mindustry/type/UnitType.java +++ b/core/src/mindustry/type/UnitType.java @@ -20,6 +20,7 @@ import mindustry.core.*; import mindustry.ctype.*; import mindustry.entities.*; import mindustry.entities.abilities.*; +import mindustry.entities.part.*; import mindustry.entities.units.*; import mindustry.game.*; import mindustry.gen.*; @@ -59,7 +60,6 @@ public class UnitType extends UnlockableContent{ public float speed = 1.1f, boostMultiplier = 1f, rotateSpeed = 5f, baseRotateSpeed = 5f; public float drag = 0.3f, accel = 0.5f, landShake = 0f, rippleScale = 1f, riseSpeed = 0.08f, fallSpeed = 0.018f; public float health = 200f, range = -1, miningRange = 70f, armor = 0f, maxRange = -1f, buildRange = Vars.buildingRange; - public float lifetime = 60f * 5f; //for missiles only public float crashDamageMultiplier = 1f; public boolean targetAir = true, targetGround = true; public boolean faceTarget = true, rotateShooting = true, isCounted = true, lowAltitude = false, circleTarget = false; @@ -68,6 +68,7 @@ public class UnitType extends UnlockableContent{ public boolean playerControllable = true; public boolean allowedInPayloads = true; public boolean createWreck = true; + public boolean createScorch = true; public boolean useUnitCap = true; public boolean destructibleWreck = true; /** If true, this modded unit always has a -outline region generated for its base. Normally, outlines are ignored if there are no top = false weapons. */ @@ -94,8 +95,8 @@ public class UnitType extends UnlockableContent{ public Effect fallThrusterEffect = Fx.fallSmoke; public Effect deathExplosionEffect = Fx.dynamicExplosion; public @Nullable Effect treadEffect; - /** Additional sprites that are drawn with the unit. */ - public Seq decals = new Seq<>(); + /** Extra (usually animated) parts */ + public Seq parts = new Seq<>(DrawPart.class); public Seq abilities = new Seq<>(); /** Flags to target based on priority. Null indicates that the closest target should be found. The closest enemy core is used as a fallback. */ public BlockFlag[] targetFlags = {null}; @@ -138,6 +139,10 @@ public class UnitType extends UnlockableContent{ public Sound mineSound = Sounds.minebeam; public float mineSoundVolume = 0.6f; + //missiles only! + public float lifetime = 60f * 5f; + public float homingDelay = 10f; + /** This is a VERY ROUGH estimate of unit DPS. */ public float dpsEstimate = -1; public float clipSize = -1; @@ -522,6 +527,9 @@ public class UnitType extends UnlockableContent{ public void load(){ super.load(); + for(var part : parts){ + part.load(name); + } weapons.each(Weapon::load); region = Core.atlas.find(name); legRegion = Core.atlas.find(name + "-leg"); @@ -562,10 +570,6 @@ public class UnitType extends UnlockableContent{ segmentOutlineRegions[i] = Core.atlas.find(name + "-segment-outline" + i); } - for(var decal : decals){ - decal.loadedRegion = Core.atlas.find(decal.region); - } - clipSize = Math.max(region.width * 2f, clipSize); } @@ -798,18 +802,19 @@ public class UnitType extends UnlockableContent{ unit.trns(-legOffset.x, -legOffset.y); } - if(decals.size > 0){ - float base = unit.rotation - 90; - for(var d : decals){ - Draw.blend(d.blending); - Draw.z(d.layer <= 0f ? z : d.layer); - Draw.scl(d.xScale, d.yScale); - Draw.color(d.color); - Draw.rect(d.loadedRegion, unit.x + Angles.trnsx(base, d.x, d.y), unit.y + Angles.trnsy(base, d.x, d.y), base + d.rotation); - Draw.blend(); + //TODO how/where do I draw under? + if(parts.size > 0){ + //TODO does it need an outline? + WeaponMount first = unit.mounts.length > 0 ? unit.mounts[0] : null; + if(unit.mounts.length > 0){ + DrawPart.params.set(first.warmup, first.reload / weapons.first().reload, first.smoothReload, first.heat, unit.x, unit.y, unit.rotation); + }else{ + DrawPart.params.set(0f, 0f, 0f, 0f, unit.x, unit.y, unit.rotation); + } + for(int i = 0; i < parts.size; i++){ + var part = parts.items[i]; + part.draw(DrawPart.params); } - Draw.reset(); - Draw.z(z); } for(Ability a : unit.abilities){ diff --git a/core/src/mindustry/type/Weapon.java b/core/src/mindustry/type/Weapon.java index 7f47e61784..c1e5d9e8d2 100644 --- a/core/src/mindustry/type/Weapon.java +++ b/core/src/mindustry/type/Weapon.java @@ -10,6 +10,7 @@ import arc.math.geom.*; import arc.scene.ui.layout.*; import arc.struct.*; import arc.util.*; +import mindustry.ai.types.*; import mindustry.audio.*; import mindustry.content.*; import mindustry.entities.*; @@ -131,7 +132,7 @@ public class Weapon implements Cloneable{ /** whether this weapon should fire when its owner dies */ public boolean shootOnDeath = false; /** extra animated parts */ - public Seq parts = new Seq<>(WeaponPart.class); + public Seq parts = new Seq<>(DrawPart.class); public Weapon(String name){ this.name = name; @@ -195,12 +196,12 @@ public class Weapon implements Cloneable{ Draw.xscl = -Mathf.sign(flipSprite); if(parts.size > 0){ - WeaponPart.params.set(mount.warmup, mount.reload / reload, mount.smoothReload, mount.heat, wx, wy, weaponRotation + 90); + DrawPart.params.set(mount.warmup, mount.reload / reload, mount.smoothReload, mount.heat, wx, wy, weaponRotation + 90); for(int i = 0; i < parts.size; i++){ var part = parts.items[i]; if(part.under){ - part.draw(WeaponPart.params); + part.draw(DrawPart.params); } } } @@ -217,18 +218,10 @@ public class Weapon implements Cloneable{ if(parts.size > 0){ //TODO does it need an outline? - /* - if(outline.found()){ - //draw outline under everything when parts are involved - Draw.z(Layer.turret - 0.01f); - Draw.rect(outline, build.x + tb.recoilOffset.x, build.y + tb.recoilOffset.y, tb.drawrot()); - Draw.z(Layer.turret); - }*/ - for(int i = 0; i < parts.size; i++){ var part = parts.items[i]; if(!part.under){ - part.draw(WeaponPart.params); + part.draw(DrawPart.params); } } } @@ -380,10 +373,8 @@ public class Weapon implements Cloneable{ float baseX = offset.x, baseY = offset.y, baseRot = unit.rotation + mount.rotation; boolean delay = firstShotDelay + shotDelay > 0f; - (delay ? chargeSound : continuous ? Sounds.none : shootSound).at(shootX, shootY, Mathf.random(soundPitchMin, soundPitchMax)); - - BulletType ammo = bullet; - float lifeScl = ammo.scaleVelocity ? Mathf.clamp(Mathf.dst(shootX, shootY, aimX, aimY) / ammo.range()) : 1f; + float lifeScl = bullet.scaleVelocity ? Mathf.clamp(Mathf.dst(shootX, shootY, aimX, aimY) / bullet.range()) : 1f; + Unit parent = bullet.keepVelocity || parentizeEffects ? unit : null; //TODO far too complicated and similar to Turret @@ -393,6 +384,7 @@ public class Weapon implements Cloneable{ Time.run(sequenceNum * shotDelay + firstShotDelay, () -> { if(!unit.isAdded()) return; + //TODO this is a flawed system, recalculate everything instead. getShootPos(unit, mount, offset).sub(baseX, baseY); float rotOffset = unit.rotation + mount.rotation - baseRot; @@ -407,13 +399,11 @@ public class Weapon implements Cloneable{ Angles.shotgun(shots, spacing, rotation, f -> mount.bullet = bullet(unit, shootX, shootY, f + Mathf.range(inaccuracy), lifeScl)); } - boolean parentize = ammo.keepVelocity || parentizeEffects; - if(delay){ Time.run(firstShotDelay, () -> { if(!unit.isAdded()) return; - unit.vel.add(Tmp.v1.trns(rotation + 180f, ammo.recoil)); + unit.vel.add(Tmp.v1.trns(rotation + 180f, bullet.recoil)); Effect.shake(shake, shake, shootX, shootY); mount.recoil = recoil; mount.heat = 1f; @@ -423,21 +413,26 @@ public class Weapon implements Cloneable{ getShootPos(unit, mount, offset).sub(baseX, baseY); - ammo.chargeShootEffect.at(shootX + offset.x, shootY + offset.y, rotation, parentize ? unit : null); + bullet.chargeShootEffect.at(shootX + offset.x, shootY + offset.y, rotation, parent); }); }else{ - unit.vel.add(Tmp.v1.trns(rotation + 180f, ammo.recoil)); + unit.vel.add(Tmp.v1.trns(rotation + 180f, bullet.recoil)); Effect.shake(shake, shake, shootX, shootY); mount.recoil = recoil; mount.heat = 1f; } - ejectEffect.at(mountX, mountY, rotation * side); - ammo.shootEffect.at(shootX, shootY, rotation, ammo.hitColor, parentize ? unit : null); - ammo.smokeEffect.at(shootX, shootY, rotation, ammo.hitColor, parentize ? unit : null); + (delay ? chargeSound : continuous ? Sounds.none : shootSound).at(shootX, shootY, Mathf.random(soundPitchMin, soundPitchMax)); + effects(parent, mountX, mountY, shootX, shootY, rotation, side); unit.apply(shootStatus, shootStatusDuration); } + protected void effects(Unit parent, float mountX, float mountY, float shootX, float shootY, float rotation, int side){ + ejectEffect.at(mountX, mountY, rotation * side); + bullet.shootEffect.at(shootX, shootY, rotation, bullet.hitColor, parent); + bullet.smokeEffect.at(shootX, shootY, rotation, bullet.hitColor, parent); + } + protected @Nullable Bullet bullet(Unit unit, float shootX, float shootY, float angle, float lifescl){ float xr = Mathf.range(xRand), @@ -452,6 +447,9 @@ public class Weapon implements Cloneable{ spawned.rotation = angle; //immediately spawn at top speed, since it was launched spawned.vel.trns(angle, unitSpawned.speed); + if(spawned.controller() instanceof MissileAI ai){ + ai.shooter = unit; + } spawned.add(); //TODO assign AI target here? return null; diff --git a/core/src/mindustry/type/unit/MissileUnitType.java b/core/src/mindustry/type/unit/MissileUnitType.java index b70382b0dc..6610f36149 100644 --- a/core/src/mindustry/type/unit/MissileUnitType.java +++ b/core/src/mindustry/type/unit/MissileUnitType.java @@ -13,6 +13,7 @@ public class MissileUnitType extends UnitType{ playerControllable = false; createWreck = false; + createScorch = false; logicControllable = false; isCounted = false; useUnitCap = false; @@ -26,8 +27,8 @@ public class MissileUnitType extends UnitType{ trailLength = 7; hidden = true; speed = 4f; - lifetime = 60f * 3f; - rotateSpeed = 3f; + lifetime = 60f * 1.6f; + rotateSpeed = 2.5f; range = 30f; //TODO weapons, etc } diff --git a/core/src/mindustry/ui/dialogs/PlanetDialog.java b/core/src/mindustry/ui/dialogs/PlanetDialog.java index 52b2fc6382..0673db5990 100644 --- a/core/src/mindustry/ui/dialogs/PlanetDialog.java +++ b/core/src/mindustry/ui/dialogs/PlanetDialog.java @@ -1077,7 +1077,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ //make sure there are no under-attack sectors (other than this one) //TODO abandon button? for(Planet planet : content.planets()){ - if(!planet.allowWaveSimulation){ + if(!planet.allowWaveSimulation && !debugSelect){ int attackedCount = planet.sectors.count(s -> s.isAttacked() && s != sector); //if there are two or more attacked sectors... something went wrong, don't show the dialog to prevent softlock diff --git a/core/src/mindustry/world/draw/DrawTurret.java b/core/src/mindustry/world/draw/DrawTurret.java index 5a26148ac7..6d18c66ddc 100644 --- a/core/src/mindustry/world/draw/DrawTurret.java +++ b/core/src/mindustry/world/draw/DrawTurret.java @@ -17,7 +17,7 @@ import mindustry.world.blocks.defense.turrets.Turret.*; public class DrawTurret extends DrawBlock{ protected static final Rand rand = new Rand(); - public Seq parts = new Seq<>(); + public Seq parts = new Seq<>(); public String basePrefix = ""; /** Overrides the liquid to draw in the liquid region. */ public @Nullable Liquid liquidDraw; @@ -66,7 +66,7 @@ public class DrawTurret extends DrawBlock{ } //TODO no smooth reload - var params = WeaponPart.params.set(build.warmup(), 1f - tb.progress(), 1f - tb.progress(), tb.heat, tb.x + tb.recoilOffset.x, tb.y + tb.recoilOffset.y, tb.rotation); + var params = DrawPart.params.set(build.warmup(), 1f - tb.progress(), 1f - tb.progress(), tb.heat, tb.x + tb.recoilOffset.x, tb.y + tb.recoilOffset.y, tb.rotation); for(var part : parts){ part.draw(params);