diff --git a/res/bundles/bundle.properties b/res/bundles/bundle.properties index 27e8e14..9be4301 100644 --- a/res/bundles/bundle.properties +++ b/res/bundles/bundle.properties @@ -362,6 +362,9 @@ status.fos-support3.name = EMP Mender Generator status.fos-support3.description = Repairs allied structures and damages enemies near the bearer. status.fos-legion-fabricator.name = Legion's Mini-Fabricator Replica status.fos-legion-fabricator.description = Periodically fabricates Legionnaire replicas that follow the player; each one gives +5% damage resistance to the bearer. +status.fos-citadel-stickybomb-launcher.name = Citadel's Sticky Bomb Launcher +status.fos-citadel-stickybomb-launcher.description = Launches persistent sticky bombs.\n[accent]Middle click[] to blow up all planted sticky bombs. +status.fos-citadel-stickybomb-launcher.description-mobile = Launches persistent sticky bombs.\n[accent]Double tap[] to blow up all planted sticky bombs. unit.fos-legionnaire.name = Legionnaire unit.fos-legionnaire-replica.name = Legionnaire Replica diff --git a/res/bundles/bundle_ru.properties b/res/bundles/bundle_ru.properties index 8e70a44..a33bea4 100644 --- a/res/bundles/bundle_ru.properties +++ b/res/bundles/bundle_ru.properties @@ -362,6 +362,9 @@ status.fos-support3.name = Генератор ремонтного ЭМИ status.fos-support3.description = Чинит союзные постройки и боевые единицы и наносит урон врагам около носителя. status.fos-legion-fabricator.name = Мини-фабрика Легиона (реплика) status.fos-legion-fabricator.description = Периодически производит реплики Легионеров, которые следуют за игроком; каждый из них дает +5% устойчивости к урону носителю. +status.fos-citadel-stickybomb-launcher.name = Липучкомет Цитадели +status.fos-citadel-stickybomb-launcher.description = Стреляет постоянными липучими бомбами.\n[accent]Нажмите на колесико мыши[], чтобы взорвать все заложенные липучие бомбы. +status.fos-citadel-stickybomb-launcher.description-mobile = Стреляет постоянными липучими бомбами.\n[accent]Нажмите на экран два раза[], чтобы взорвать все заложенные липучие бомбы. unit.fos-legionnaire.name = Легионер unit.fos-legionnaire-replica.name = Легионер (реплика) diff --git a/src/fos/content/FOSWeaponModules.java b/src/fos/content/FOSWeaponModules.java index 5ef79ab..ad41e99 100644 --- a/src/fos/content/FOSWeaponModules.java +++ b/src/fos/content/FOSWeaponModules.java @@ -1,10 +1,11 @@ package fos.content; -import arc.Events; +import arc.*; import arc.graphics.Color; import fos.type.abilities.UnitResistanceAbility; -import fos.type.bullets.SmartBulletType; +import fos.type.bullets.*; import fos.type.content.WeaponSet; +import mindustry.Vars; import mindustry.content.Fx; import mindustry.entities.abilities.*; import mindustry.entities.bullet.*; @@ -26,7 +27,7 @@ public class FOSWeaponModules { standard1, standard2, standard3, standard4, standard5, shotgun1, shotgun2, shotgun3, shotgun4, shotgun5, support1, support2, support3, support4, support5, - legionFabricator; + legionFabricator, citadelStickyLauncher; public static void load() { // BASIC / ASSAULT RIFLES @@ -291,5 +292,37 @@ public static void load() { ); }); }}.reqs(with(tin, 200, silver, 125, silicon, 150)).produceTime(60 * 20); + citadelStickyLauncher = new WeaponSet("citadel-stickybomb-launcher"){ + { + description = Core.bundle.getOrNull(getContentType() + "." + this.name + ".description" + (Vars.mobile ? "-mobile" : "")); + + weapons.add( + new Weapon("fos-citadel-stickybomb-launcher"){{ + x = 0; y = -2; + mirror = false; + rotate = false; + recoil = 2f; + reload = 60f; + inaccuracy = 6f; + shootSound = Sounds.mud; + bullet = new StickyBulletType(4f, 20f){{ + lifetime = 40f; + width = height = 16f; + + trailWidth = 3f; + trailLength = 12; + trailColor = Pal.plastaniumBack; + backColor = Pal.plastaniumBack; + frontColor = Pal.plastaniumFront; + ejectEffect = Fx.smokeCloud; + + splashDamage = 120f; + splashDamageRadius = 28f; + }}; + }} + ); + reqs = with(tin, 150, diamond, 75, silicon, 150); + } + }; } } diff --git a/src/fos/content/LumoniTechTree.java b/src/fos/content/LumoniTechTree.java index 94ec469..abe8c64 100644 --- a/src/fos/content/LumoniTechTree.java +++ b/src/fos/content/LumoniTechTree.java @@ -228,8 +228,9 @@ public static void load() { ); // BOSS-SPECIFIC WEAPONS node(legionFabricator, Seq.with(new DefeatBoss(legion)), () -> { - //TODO: citadel shotgun? or stickybomb launcher? - soontm(); + node(citadelStickyLauncher, Seq.with(new DefeatBoss(FOSUnitTypes.citadel)), () -> { + soontm(); + }); }); }); diff --git a/src/fos/core/FOSMod.java b/src/fos/core/FOSMod.java index 3e61372..b5a995b 100644 --- a/src/fos/core/FOSMod.java +++ b/src/fos/core/FOSMod.java @@ -7,6 +7,7 @@ import arc.graphics.*; import arc.graphics.g2d.*; import arc.graphics.gl.Shader; +import arc.input.*; import arc.math.Mathf; import arc.scene.*; import arc.scene.ui.ImageButton; @@ -33,6 +34,7 @@ import static arc.Core.*; import static arc.discord.DiscordRPC.*; +import static arc.input.GestureDetector.GestureListener; import static mindustry.Vars.*; @ModAnnotations.RootDirectoryPath(rootDirectoryPath = "") @@ -295,6 +297,29 @@ public void draw() { // add modded hints FOSVars.hints.load(); + + input.addProcessor(new GestureDetector(new GestureListener() { + @Override + public boolean tap(float x, float y, int count, KeyCode button) { + if (((mobile && count == 2) || button == KeyCode.mouseMiddle) && !( + state.isMenu() || + scene.hasMouse() || + control.input.isPlacing() || + control.input.isBreaking() || + control.input.selectedUnit() != null + ) && player.unit() instanceof LumoniPlayerUnitc) { + FOSCall.detonate(); + return true; + } else { + return false; + } + } + + @Override + public boolean touchDown(float x, float y, int pointer, KeyCode button) { + return GestureListener.super.touchDown(x, y, pointer, button); + } + })); } public void constructSettings() { diff --git a/src/fos/type/blocks/special/UpgradeCenter.java b/src/fos/type/blocks/special/UpgradeCenter.java index 63e1c1a..9c7301e 100644 --- a/src/fos/type/blocks/special/UpgradeCenter.java +++ b/src/fos/type/blocks/special/UpgradeCenter.java @@ -21,6 +21,8 @@ import mindustry.world.consumers.ConsumeItemDynamic; import mindustry.world.draw.*; +import static mindustry.Vars.state; + public class UpgradeCenter extends Block { /** Called when player upgrades their weapon * @param player player that upgraded weapon @@ -184,10 +186,10 @@ public boolean shouldConsume() { @Override public void buildConfiguration(Table table) { if (WeaponSet.sets.size > 0) { - ItemSelection.buildTable(UpgradeCenter.this, table, WeaponSet.sets, + ItemSelection.buildTable(UpgradeCenter.this, table, WeaponSet.sets.select(ws -> !isSetBanned(ws)), () -> weaponSet == null ? null : weaponSet, ws -> { - configure(ws == null ? -1 : ws.id); + configure(ws == null || isSetBanned(ws) ? -1 : ws.id); progress = 0; }, selectionRows, selectionColumns @@ -197,7 +199,7 @@ public void buildConfiguration(Table table) { if (weaponSet == null || Vars.player == null) return; FOSCall.upgrade(Vars.player, tile()); deselect(); - }).visible(() -> fraction() >= 1f); + }).visible(() -> !isSetBanned(weaponSet) && fraction() >= 1f); } else { deselect(); } @@ -218,6 +220,11 @@ public boolean acceptItem(Building source, Item item) { Structs.contains(weaponSet.reqs, stack -> stack.item == item); } + protected boolean isSetBanned(WeaponSet set) { + Seq banned = new Seq<>(state.rules.tags.get("fos-bannedMountUpgrades", "").split(";")); + return set != null && banned.contains(set.name); + } + @Override public void write(Writes write) { super.write(write); diff --git a/src/fos/type/bullets/StickyBulletType.java b/src/fos/type/bullets/StickyBulletType.java index f9edb75..d0af3f3 100644 --- a/src/fos/type/bullets/StickyBulletType.java +++ b/src/fos/type/bullets/StickyBulletType.java @@ -2,14 +2,24 @@ import arc.math.Mathf; import arc.math.geom.Vec2; +import mindustry.annotations.Annotations; import mindustry.content.Fx; -import mindustry.entities.bullet.ArtilleryBulletType; +import mindustry.entities.bullet.BasicBulletType; import mindustry.gen.*; import mindustry.graphics.Layer; -public class StickyBulletType extends ArtilleryBulletType { +public class StickyBulletType extends BasicBulletType { /** An interval between the contact with an enemy and the explosion. */ public int explosionDelay; + /** If true, ignores explosion delay and only blows up after double tapping. */ + public boolean persistent = false; + /** Persistent sticky bomb limit. */ + public int bulletCap = 10; + + public StickyBulletType(float speed, float damage) { + this(speed, damage, 10); + persistent = true; + } public StickyBulletType(float speed, float damage, int explosionDelay) { super(speed, damage); @@ -26,6 +36,18 @@ public StickyBulletType(float speed, float damage, int explosionDelay) { pierce = true; collides = true; collidesGround = true; + collidesAir = false; + } + + @Override + public void init(Bullet b) { + super.init(b); + + int counter = Groups.bullet.count(bul -> bul.type == this); + if (persistent && counter > bulletCap) { + Groups.bullet.find(bul -> bul.type == this).remove(); + } + } @Override @@ -63,27 +85,35 @@ public void update(Bullet b) { StickyBulletData data = (StickyBulletData) b.data; - if (data != null && data.target == null) { - b.vel.set(Vec2.ZERO); - return; - } + if (data != null) { + if (persistent) { + if (b.owner instanceof Unitc u && u.dead()) + b.remove(); + b.time = 0f; + } - if (data != null && data.target instanceof Unit u && !u.dead()) { - float bx = b.x(), by = b.y(); - float ox = data.target.x(), oy = data.target.y(); + if (data.target == null) { + b.vel.set(Vec2.ZERO); + return; + } - if (data.initialAngle == null) data.initialAngle = Mathf.angle(bx - ox, by - oy); - if (data.targetRot == null) data.targetRot = u.rotation; + if (data.target instanceof Unit u && !u.dead()) { + float bx = b.x(), by = b.y(); + float ox = data.target.x(), oy = data.target.y(); - float angle = data.initialAngle - data.targetRot + u.rotation; + if (data.initialAngle == null) data.initialAngle = Mathf.angle(bx - ox, by - oy); + if (data.targetRot == null) data.targetRot = u.rotation; - var vx = (u.x + Mathf.cos(angle * Mathf.degRad) * u.hitSize / 2) - b.x; - var vy = (u.y + Mathf.sin(angle * Mathf.degRad) * u.hitSize / 2) - b.y; + float angle = data.initialAngle - data.targetRot + u.rotation; - if (vx == 0 && vy == 0) { - b.vel.set(Vec2.ZERO); - } else { - b.vel.set(vx, vy); + var vx = (u.x + Mathf.cos(angle * Mathf.degRad) * u.hitSize / 2) - b.x; + var vy = (u.y + Mathf.sin(angle * Mathf.degRad) * u.hitSize / 2) - b.y; + + if (vx == 0 && vy == 0) { + b.vel.set(Vec2.ZERO); + } else { + b.vel.set(vx, vy); + } } } } @@ -103,6 +133,15 @@ public void removed(Bullet b) { } } + @Annotations.Remote(called = Annotations.Loc.both, targets = Annotations.Loc.client, forward = true) + public static void detonate(Player player) { + Groups.bullet.each(b -> { + if (b == null || !(b.data instanceof StickyBulletData) || b.owner == null) return; + + if (b.owner == player.unit()) b.lifetime = 1f; + }); + } + public static class StickyBulletData { public Teamc target; public Float initialAngle, targetRot;