diff --git a/core/assets-raw/sprites/effects/large-orb-back.png b/core/assets-raw/sprites/effects/large-orb-back.png new file mode 100644 index 0000000000..261446c517 Binary files /dev/null and b/core/assets-raw/sprites/effects/large-orb-back.png differ diff --git a/core/assets-raw/sprites/effects/large-orb.png b/core/assets-raw/sprites/effects/large-orb.png new file mode 100644 index 0000000000..414d8d2ab4 Binary files /dev/null and b/core/assets-raw/sprites/effects/large-orb.png differ diff --git a/core/assets-raw/sprites/units/obviate-blade-heat.png b/core/assets-raw/sprites/units/obviate-blade-heat.png new file mode 100644 index 0000000000..b6d8f68a40 Binary files /dev/null and b/core/assets-raw/sprites/units/obviate-blade-heat.png differ diff --git a/core/assets-raw/sprites/units/obviate-blade.png b/core/assets-raw/sprites/units/obviate-blade.png new file mode 100644 index 0000000000..e1528e1bbc Binary files /dev/null and b/core/assets-raw/sprites/units/obviate-blade.png differ diff --git a/core/assets-raw/sprites/units/obviate-cell.png b/core/assets-raw/sprites/units/obviate-cell.png index 80c3606926..9c807c22dc 100644 Binary files a/core/assets-raw/sprites/units/obviate-cell.png and b/core/assets-raw/sprites/units/obviate-cell.png differ diff --git a/core/assets-raw/sprites/units/obviate-preview.png b/core/assets-raw/sprites/units/obviate-preview.png new file mode 100644 index 0000000000..aac82cee84 Binary files /dev/null and b/core/assets-raw/sprites/units/obviate-preview.png differ diff --git a/core/assets-raw/sprites/units/obviate-side.png b/core/assets-raw/sprites/units/obviate-side.png new file mode 100644 index 0000000000..ebba8b2e87 Binary files /dev/null and b/core/assets-raw/sprites/units/obviate-side.png differ diff --git a/core/assets-raw/sprites/units/obviate.png b/core/assets-raw/sprites/units/obviate.png index aac82cee84..a952fabb87 100644 Binary files a/core/assets-raw/sprites/units/obviate.png and b/core/assets-raw/sprites/units/obviate.png differ diff --git a/core/src/mindustry/content/Fx.java b/core/src/mindustry/content/Fx.java index ca6ebce0bd..6f918c4c95 100644 --- a/core/src/mindustry/content/Fx.java +++ b/core/src/mindustry/content/Fx.java @@ -1004,6 +1004,11 @@ public class Fx{ Fill.circle(e.x, e.y, e.rotation * e.fout()); }).layer(Layer.bullet - 0.001f), //below bullets + missileTrailShort = new Effect(22, e -> { + color(e.color); + Fill.circle(e.x, e.y, e.rotation * e.fout()); + }).layer(Layer.bullet - 0.001f), + absorb = new Effect(12, e -> { color(Pal.accent); stroke(2f * e.fout()); diff --git a/core/src/mindustry/content/UnitTypes.java b/core/src/mindustry/content/UnitTypes.java index 04d4a21131..ad2be53ac9 100644 --- a/core/src/mindustry/content/UnitTypes.java +++ b/core/src/mindustry/content/UnitTypes.java @@ -3399,6 +3399,10 @@ public class UnitTypes{ itemCapacity = 0; useEngineElevation = false; + engineColor = Pal.sapBullet; + + abilities.add(new MoveEffectAbility(0f, -7f, Pal.sapBulletBack, Fx.missileTrailShort, 4f)); + for(float f : new float[]{-3f, 3f}){ parts.add(new HoverPart(){{ x = 3.9f; @@ -3413,18 +3417,25 @@ public class UnitTypes{ } weapons.add(new Weapon("osc-weapon"){{ - y = 3f; - x = 3f; + y = -2f; + x = 4f; + top = true; mirror = true; - layerOffset = -0.0001f; reload = 40f; + baseRotation = -35f; + shootCone = 360f; + + shoot = new ShootSpread(){{ + shots = 2; + spread = 11f; + }}; bullet = new BasicBulletType(5f, 20){{ - pierceCap = 2; - pierceBuilding = false; + homingPower = 0.2f; + homingDelay = 4f; width = 7f; height = 12f; - lifetime = 25f; + lifetime = 50f; shootEffect = Fx.sparkShoot; smokeEffect = Fx.shootBigSmoke; hitColor = backColor = trailColor = Pal.suppress; @@ -3486,8 +3497,8 @@ public class UnitTypes{ obviate = new ErekirUnitType("obviate"){{ flying = true; drag = 0.08f; - speed = 1.9f; - rotateSpeed = 4f; + speed = 1.8f; + rotateSpeed = 2.5f; accel = 0.09f; health = 2300f; armor = 3f; @@ -3499,46 +3510,106 @@ public class UnitTypes{ lowAltitude = true; setEnginesMirror( - new UnitEngine(59 / 4f, -25 / 4f, 3.1f, 315f) + new UnitEngine(38 / 4f, -46 / 4f, 3.1f, 315f) ); - parts.add(new RegionPart("-heat"){{ - blending = Blending.additive; - progress = PartProgress.heat; - outline = false; - colorTo = new Color(1f, 0.5f, 0.5f, 0.7f); - color = colorTo.cpy().a(0f); - layerOffset = 0.01f; + parts.add( + new RegionPart("-blade"){{ + moveRot = -10f; + moveX = -1f; + moves.add(new PartMove(PartProgress.reload, 2f, 1f, -5f)); + progress = PartProgress.warmup; + mirror = true; + + children.add(new RegionPart("-side"){{ + moveX = 2f; + moveY = -2f; + progress = PartProgress.warmup; + under = true; + mirror = true; + moves.add(new PartMove(PartProgress.reload, -2f, 2f, 0f)); + }}); }}); - //TODO this is really dumb. weapons.add(new Weapon(){{ - y = 2f; - shootY = 0f; x = 0f; + y = -2f; + shootY = 0f; reload = 140f; mirror = false; - continuous = true; - // minWarmup = 0.5f; + minWarmup = 0.95f; + shake = 3f; + cooldownTime = reload - 10f; - bullet = new ContinuousLaserBulletType(140){{ - shake = 0f; - lifetime = 120f; - colors = new Color[]{Color.valueOf("bf92f9").a(0.4f), Color.valueOf("bf92f9"), Color.white}; - //TODO merge - chargeEffect = new MultiEffect(Fx.lancerLaserCharge, Fx.lancerLaserChargeBegin); - width = 4f; - largeHit = false; + bullet = new BasicBulletType(){{ + shoot = new ShootHelix(){{ + mag = 1f; + scl = 5f; + }}; - buildingDamageMultiplier = 0.25f; - hitEffect = Fx.hitLancer; - hitSize = 4; - drawSize = 400f; - length = 173f; - ammoMultiplier = 1f; - pierceCap = 4; - hitColor = colors[1]; + shootEffect = new MultiEffect(Fx.shootTitan, new WaveEffect(){{ + colorTo = Pal.sapBulletBack; + sizeTo = 26f; + lifetime = 14f; + strokeFrom = 4f; + }}); + smokeEffect = Fx.shootSmokeTitan; + hitColor = Pal.sapBullet; + + sprite = "large-orb"; + trailEffect = Fx.missileTrail; + trailInterval = 3f; + trailParam = 4f; + speed = 3f; + damage = 80f; + lifetime = 70f; + width = height = 15f; + backColor = Pal.sapBulletBack; + frontColor = Pal.sapBullet; + shrinkX = shrinkY = 0f; + trailColor = Pal.sapBulletBack; + trailLength = 12; + trailWidth = 2.2f; + despawnEffect = hitEffect = new ExplosionEffect(){{ + waveColor = Pal.sapBullet; + smokeColor = Color.gray; + sparkColor = Pal.sap; + waveStroke = 4f; + waveRad = 20f; + }}; + + intervalBullet = new LightningBulletType(){{ + damage = 15; + collidesAir = false; + ammoMultiplier = 1f; + lightningColor = Pal.sapBullet; + lightningLength = 2; + lightningLengthRand = 6; + + //for visual stats only. + buildingDamageMultiplier = 0.25f; + + lightningType = new BulletType(0.0001f, 0f){{ + lifetime = Fx.lightning.lifetime; + hitEffect = Fx.hitLancer; + despawnEffect = Fx.none; + status = StatusEffects.shocked; + statusDuration = 10f; + hittable = false; + lightColor = Color.white; + buildingDamageMultiplier = 0.25f; + }}; + }}; + + bulletInterval = 4f; + + lightningColor = Pal.sapBullet; + lightningDamage = 19; + lightning = 8; + lightningLength = 2; + lightningLengthRand = 8; }}; + }}); }}; diff --git a/core/src/mindustry/entities/abilities/MoveEffectAbility.java b/core/src/mindustry/entities/abilities/MoveEffectAbility.java new file mode 100644 index 0000000000..bbb8d36a6c --- /dev/null +++ b/core/src/mindustry/entities/abilities/MoveEffectAbility.java @@ -0,0 +1,41 @@ +package mindustry.entities.abilities; + +import arc.graphics.*; +import arc.util.*; +import mindustry.content.*; +import mindustry.entities.*; +import mindustry.gen.*; + +public class MoveEffectAbility extends Ability{ + public float minVelocity = 0.08f; + public float interval = 3f; + public float x, y; + public boolean rotateEffect = false; + public float effectParam = 3f; + public boolean teamColor = false; + public Color color = Color.white; + public Effect effect = Fx.missileTrail; + + protected float counter; + + public MoveEffectAbility(float x, float y, Color color, Effect effect, float interval){ + this.x = x; + this.y = y; + this.color = color; + this.effect = effect; + this.interval = interval; + } + + public MoveEffectAbility(){ + } + + @Override + public void update(Unit unit){ + counter += Time.delta; + if(unit.vel.len2() >= minVelocity * minVelocity && (counter >= interval)){ + Tmp.v1.trns(unit.rotation - 90f, x, y); + counter %= interval; + effect.at(Tmp.v1.x + unit.x, Tmp.v1.y + unit.y, rotateEffect ? unit.rotation : effectParam, teamColor ? unit.team.color : color); + } + } +} diff --git a/core/src/mindustry/entities/bullet/BulletType.java b/core/src/mindustry/entities/bullet/BulletType.java index ee7d5e918c..9b99917b3a 100644 --- a/core/src/mindustry/entities/bullet/BulletType.java +++ b/core/src/mindustry/entities/bullet/BulletType.java @@ -151,7 +151,7 @@ public class BulletType extends Content implements Cloneable{ public float fragVelocityMin = 0.2f, fragVelocityMax = 1f, fragLifeMin = 1f, fragLifeMax = 1f; public @Nullable BulletType fragBullet = null; public float bulletInterval = 20f; - public int intervalBulletCount = 1; + public int intervalBullets = 1; public @Nullable BulletType intervalBullet; public Color hitColor = Color.white; public Color healColor = Pal.heal; @@ -431,7 +431,7 @@ public class BulletType extends Content implements Cloneable{ public void updateBulletInterval(Bullet b){ if(intervalBullet != null && b.timer.get(2, bulletInterval)){ - for(int i = 0; i < intervalBulletCount; i++){ + for(int i = 0; i < intervalBullets; i++){ intervalBullet.create(b, b.x, b.y, Mathf.random(360f)); } } diff --git a/core/src/mindustry/entities/pattern/ShootBarrel.java b/core/src/mindustry/entities/pattern/ShootBarrel.java index 5c564e5551..18e6610ab6 100644 --- a/core/src/mindustry/entities/pattern/ShootBarrel.java +++ b/core/src/mindustry/entities/pattern/ShootBarrel.java @@ -11,6 +11,7 @@ public class ShootBarrel extends ShootPattern{ barrels = barrels.clone(); for(int i = 0; i < barrels.length; i += 3){ barrels[i] *= -1; + barrels[i + 2] *= -1; } } diff --git a/core/src/mindustry/entities/pattern/ShootHelix.java b/core/src/mindustry/entities/pattern/ShootHelix.java index 126c90532b..88ca6d20c0 100644 --- a/core/src/mindustry/entities/pattern/ShootHelix.java +++ b/core/src/mindustry/entities/pattern/ShootHelix.java @@ -10,7 +10,7 @@ public class ShootHelix extends ShootPattern{ for(int i = 0; i < shots; i++){ for(int sign : Mathf.signs){ handler.shoot(0, 0, 0, firstShotDelay + shotDelay * i, - b -> b.moveRelative(0f, Mathf.sin(b.time + offset, 2f, mag * sign))); + b -> b.moveRelative(0f, Mathf.sin(b.time + offset, scl, mag * sign))); } } } diff --git a/core/src/mindustry/entities/pattern/ShootMulti.java b/core/src/mindustry/entities/pattern/ShootMulti.java index 0d1c3ebefa..34497957cd 100644 --- a/core/src/mindustry/entities/pattern/ShootMulti.java +++ b/core/src/mindustry/entities/pattern/ShootMulti.java @@ -29,9 +29,9 @@ public class ShootMulti extends ShootPattern{ source.shoot(totalShots, (x, y, rotation, delay, move) -> { for(var pattern : dest){ pattern.shoot(totalShots, (x2, y2, rot2, delay2, mover) -> { - handler.shoot(x + x2, y + y2, rotation + rot2, delay + delay2, b -> { - move.move(b); - mover.move(b); + handler.shoot(x + x2, y + y2, rotation + rot2, delay + delay2, move == null && mover == null ? null : b -> { + if(move != null) move.move(b); + if(mover != null) mover.move(b); }); }); } diff --git a/core/src/mindustry/mod/Mods.java b/core/src/mindustry/mod/Mods.java index 3af4656afa..fc4208c003 100644 --- a/core/src/mindustry/mod/Mods.java +++ b/core/src/mindustry/mod/Mods.java @@ -353,23 +353,13 @@ public class Mods implements Loadable{ Log.debug("Total time to generate & flush textures synchronously: @", Time.elapsed()); } - private PageType getPage(AtlasRegion region){ - return - region.texture == Core.atlas.find("white").texture ? PageType.main : - region.texture == Core.atlas.find("stone1").texture ? PageType.environment : - region.texture == Core.atlas.find("clear-editor").texture ? PageType.editor : - region.texture == Core.atlas.find("whiteui").texture ? PageType.ui : - region.texture == Core.atlas.find("rubble-1-0").texture ? PageType.rubble : - PageType.main; - } - private PageType getPage(Fi file){ String path = file.path(); return - path.contains("sprites/blocks/environment") ? PageType.environment : - path.contains("sprites/editor") ? PageType.editor : - path.contains("sprites/rubble") ? PageType.editor : - path.contains("sprites/ui") ? PageType.ui : + path.contains("sprites/blocks/environment") || path.contains("sprites-override/blocks/environment") ? PageType.environment : + path.contains("sprites/editor") || path.contains("sprites-override/editor") ? PageType.editor : + path.contains("sprites/rubble") || path.contains("sprites-override/rubble") ? PageType.rubble : + path.contains("sprites/ui") || path.contains("sprites-override/ui") ? PageType.ui : PageType.main; } diff --git a/core/src/mindustry/type/UnitType.java b/core/src/mindustry/type/UnitType.java index fe5cc51fbd..49be96dae7 100644 --- a/core/src/mindustry/type/UnitType.java +++ b/core/src/mindustry/type/UnitType.java @@ -212,7 +212,7 @@ public class UnitType extends UnlockableContent{ public Sound deathSound = Sounds.bang; public Seq weapons = new Seq<>(); - public TextureRegion baseRegion, legRegion, region, shadowRegion, cellRegion, + public TextureRegion baseRegion, legRegion, region, previewRegion, shadowRegion, cellRegion, softShadowRegion, jointRegion, footRegion, legBaseRegion, baseJointRegion, outlineRegion, treadRegion; public TextureRegion[] wreckRegions, segmentRegions, segmentOutlineRegions; public TextureRegion[][] treadRegions; @@ -590,6 +590,7 @@ public class UnitType extends UnlockableContent{ } weapons.each(Weapon::load); region = Core.atlas.find(name); + previewRegion = Core.atlas.find(name + "-preview", name); legRegion = Core.atlas.find(name + "-leg"); jointRegion = Core.atlas.find(name + "-joint"); baseJointRegion = Core.atlas.find(name + "-joint-base"); diff --git a/core/src/mindustry/type/Weapon.java b/core/src/mindustry/type/Weapon.java index 047d348a19..9ee9cfdfed 100644 --- a/core/src/mindustry/type/Weapon.java +++ b/core/src/mindustry/type/Weapon.java @@ -41,6 +41,8 @@ public class Weapon implements Cloneable{ public boolean alternate = true; /** whether to rotate toward the target independently of unit */ public boolean rotate = false; + /** rotation at which this weapon is locked to if rotate = false. TODO buggy!*/ + public float baseRotation = 0f; /** whether to draw the outline on top. */ public boolean top = true; /** whether to hold the bullet in place while firing */ @@ -180,7 +182,7 @@ public class Weapon implements Cloneable{ float rotation = unit.rotation - 90, - weaponRotation = rotation + (rotate ? mount.rotation : 0), + weaponRotation = rotation + (rotate ? mount.rotation : baseRotation), wx = unit.x + Angles.trnsx(rotation, x, y) + Angles.trnsx(weaponRotation, 0, -mount.recoil), wy = unit.y + Angles.trnsy(rotation, x, y) + Angles.trnsy(weaponRotation, 0, -mount.recoil); @@ -263,12 +265,12 @@ public class Weapon implements Cloneable{ } } }else if(!rotate){ - mount.rotation = 0; + mount.rotation = baseRotation; mount.targetRotation = unit.angleTo(mount.aimX, mount.aimY); } float - weaponRotation = unit.rotation - 90 + (rotate ? mount.rotation : 0), + 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), @@ -346,7 +348,7 @@ public class Weapon implements Cloneable{ mount.warmup >= minWarmup && //must be warmed up unit.vel.len() >= minShootVelocity && //check velocity requirements mount.reload <= 0.0001f && //reload has to be 0 - Angles.within(rotate ? mount.rotation : unit.rotation, mount.targetRotation, shootCone) //has to be within the cone + Angles.within(rotate ? mount.rotation : unit.rotation + baseRotation, mount.targetRotation, shootCone) //has to be within the cone ){ shoot(unit, mount, bulletX, bulletY, shootAngle); @@ -368,7 +370,7 @@ public class Weapon implements Cloneable{ } protected float bulletRotation(Unit unit, WeaponMount mount, float bulletX, float bulletY){ - return rotate ? unit.rotation + mount.rotation : Angles.angle(bulletX, bulletY, mount.aimX, mount.aimY) + (unit.rotation - unit.angleTo(mount.aimX, mount.aimY)); + return rotate ? unit.rotation + mount.rotation : Angles.angle(bulletX, bulletY, mount.aimX, mount.aimY) + (unit.rotation - unit.angleTo(mount.aimX, mount.aimY)) + baseRotation; } protected void shoot(Unit unit, WeaponMount mount, float shootX, float shootY, float rotation){ @@ -393,7 +395,7 @@ public class Weapon implements Cloneable{ if(!unit.isAdded()) return; float - weaponRotation = unit.rotation - 90 + (rotate ? mount.rotation : 0), + 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 + xOffset, this.shootY + yOffset), @@ -421,6 +423,7 @@ public class Weapon implements Cloneable{ public void flip(){ x *= -1; shootX *= -1; + baseRotation *= -1f; flipSprite = !flipSprite; shoot = shoot.copy(); shoot.flip(); diff --git a/tools/src/mindustry/tools/Generators.java b/tools/src/mindustry/tools/Generators.java index 0e4032aabd..d55f4227fe 100644 --- a/tools/src/mindustry/tools/Generators.java +++ b/tools/src/mindustry/tools/Generators.java @@ -557,7 +557,7 @@ public class Generators{ if(sample instanceof Legsc) outliner.get(type.legRegion); if(sample instanceof Tankc) outliner.get(type.treadRegion); - Pixmap image = type.segments > 0 ? get(type.segmentRegions[0]) : outline.get(get(type.region)); + Pixmap image = type.segments > 0 ? get(type.segmentRegions[0]) : outline.get(get(type.previewRegion)); Func weaponRegion = weapon -> Core.atlas.has(weapon.name + "-preview") ? get(weapon.name + "-preview") : get(weapon.region); Cons2 drawWeapon = (weapon, pixmap) -> @@ -585,7 +585,7 @@ public class Generators{ if(type.needsBodyOutline()){ save(image, type.name + "-outline"); }else{ - replace(type.name, image); + replace(type.name, type.segments > 0 ? get(type.segmentRegions[0]) : outline.get(get(type.region))); } //draw weapons that are under the base @@ -596,13 +596,13 @@ public class Generators{ //draw over the weapons under the image if(anyUnder){ - image.draw(outline.get(get(type.region)), true); + image.draw(outline.get(get(type.previewRegion)), true); } //draw treads if(sample instanceof Tankc){ image.draw(outline.get(get(type.treadRegion)), true); - image.draw(get(type.region), true); + image.draw(get(type.previewRegion), true); } //draw mech parts @@ -610,7 +610,7 @@ public class Generators{ drawCenter(image, get(type.baseRegion)); drawCenter(image, get(type.legRegion)); drawCenter(image, get(type.legRegion).flipX()); - image.draw(get(type.region), true); + image.draw(get(type.previewRegion), true); } //draw weapon outlines on base @@ -622,7 +622,7 @@ public class Generators{ } //draw base region on top to mask weapons - if(type.drawCell) image.draw(get(type.region), true); + if(type.drawCell) image.draw(get(type.previewRegion), true); if(type.drawCell){ Pixmap baseCell = get(type.cellRegion);