package mindustry.entities.def; import arc.graphics.g2d.*; import arc.math.*; import arc.util.*; import mindustry.annotations.Annotations.*; import mindustry.entities.*; import mindustry.entities.bullet.*; import mindustry.entities.units.*; import mindustry.gen.*; import mindustry.type.*; @Component abstract class WeaponsComp implements Teamc, Posc, Rotc{ transient float x, y, rotation; /** 1 */ static final int[] one = {1}; /** minimum cursor distance from player, fixes 'cross-eyed' shooting */ static final float minAimDst = 20f; /** temporary weapon sequence number */ static int sequenceNum = 0; /** weapon mount array, never null */ @ReadOnly WeaponMount[] mounts = {}; void setupWeapons(UnitDef def){ mounts = new WeaponMount[def.weapons.size]; for(int i = 0; i < mounts.length; i++){ mounts[i] = new WeaponMount(def.weapons.get(i)); } } /** Aim at something. This will make all mounts point at it. */ void aim(Unitc unit, float x, float y){ Tmp.v1.set(x, y).sub(this.x, this.y); if(Tmp.v1.len() < minAimDst) Tmp.v1.setLength(minAimDst); x = Tmp.v1.x + this.x; y = Tmp.v1.y + this.y; for(WeaponMount mount : mounts){ mount.aimX = x; mount.aimY = y; } } /** Update shooting and rotation for this unit. */ @Override public void update(){ for(WeaponMount mount : mounts){ Weapon weapon = mount.weapon; mount.reload -= Time.delta(); float rotation = this.rotation - 90; //rotate if applicable if(weapon.rotate){ float axisXOffset = weapon.mirror ? 0f : weapon.x; float axisX = this.x + Angles.trnsx(rotation, axisXOffset, weapon.y), axisY = this.y + Angles.trnsy(rotation, axisXOffset, weapon.y); mount.rotation = Angles.moveToward(mount.rotation, Angles.angle(axisX, axisY, mount.aimX, mount.aimY), weapon.rotateSpeed); } //shoot if applicable //TODO only shoot if angle is reached, don't shoot inaccurately if(mount.reload <= 0){ for(int i : (weapon.mirror && !weapon.alternate ? Mathf.signs : one)){ i *= Mathf.sign(weapon.flipped) * Mathf.sign(mount.side); //m a t h float weaponRotation = rotation + (weapon.rotate ? mount.rotation : 0); float mountX = this.x + Angles.trnsx(rotation, weapon.x * i, weapon.y), mountY = this.y + Angles.trnsy(rotation, weapon.x * i, weapon.y); float shootX = mountX + Angles.trnsx(weaponRotation, weapon.shootX * i, weapon.shootY), shootY = mountY + Angles.trnsy(weaponRotation, weapon.shootX * i, weapon.shootY); float shootAngle = weapon.rotate ? weaponRotation : Angles.angle(shootX, shootY, mount.aimX, mount.aimY); shoot(weapon, shootX, shootY, shootAngle); } mount.side = !mount.side; mount.reload = weapon.reload; } } } /** Draw weapon mounts. */ void drawWeapons(){ for(WeaponMount mount : mounts){ Weapon weapon = mount.weapon; for(int i : (weapon.mirror ? Mathf.signs : one)){ i *= Mathf.sign(weapon.flipped); float rotation = this.rotation - 90 + (weapon.rotate ? mount.rotation : 0); float trY = weapon.y - (mount.reload / weapon.reload * weapon.recoil) * (weapon.alternate ? Mathf.num(i == Mathf.sign(mount.side)) : 1); float width = i > 0 ? -weapon.region.getWidth() : weapon.region.getWidth(); Draw.rect(weapon.region, x + Angles.trnsx(rotation, weapon.x * i, trY), y + Angles.trnsy(rotation, weapon.x * i, trY), width * Draw.scl, weapon.region.getHeight() * Draw.scl, rotation - 90); } } } private void shoot(Weapon weapon, float x, float y, float rotation){ float baseX = this.x, baseY = this.y; weapon.shootSound.at(x, y, Mathf.random(0.8f, 1.0f)); sequenceNum = 0; if(weapon.shotDelay > 0.01f){ Angles.shotgun(weapon.shots, weapon.spacing, rotation, f -> { Time.run(sequenceNum * weapon.shotDelay, () -> bullet(weapon, x + this.x - baseX, y + this.y - baseY, f + Mathf.range(weapon.inaccuracy))); sequenceNum++; }); }else{ Angles.shotgun(weapon.shots, weapon.spacing, rotation, f -> bullet(weapon, x, y, f + Mathf.range(weapon.inaccuracy))); } BulletType ammo = weapon.bullet; Tmp.v1.trns(rotation + 180f, ammo.recoil); if(this instanceof Velc){ //TODO apply force? ((Velc)this).vel().add(Tmp.v1); } Tmp.v1.trns(rotation, 3f); boolean parentize = ammo.keepVelocity; Effects.shake(weapon.shake, weapon.shake, x, y); weapon.ejectEffect.at(x, y, rotation); ammo.shootEffect.at(x + Tmp.v1.x, y + Tmp.v1.y, rotation, parentize ? this : null); ammo.smokeEffect.at(x + Tmp.v1.x, y + Tmp.v1.y, rotation, parentize ? this : null); } private void bullet(Weapon weapon, float x, float y, float angle){ Tmp.v1.trns(angle, 3f); weapon.bullet.create(this, team(), x + Tmp.v1.x, y + Tmp.v1.y, angle, (1f - weapon.velocityRnd) + Mathf.random(weapon.velocityRnd)); } }