Merge branch 'master' into patch-2
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 459 B |
|
After Width: | Height: | Size: 355 B |
|
After Width: | Height: | Size: 828 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 720 B |
|
After Width: | Height: | Size: 617 B |
|
After Width: | Height: | Size: 550 B |
|
After Width: | Height: | Size: 350 B |
|
After Width: | Height: | Size: 486 B |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 6.0 KiB |
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 600 B |
|
After Width: | Height: | Size: 748 B |
|
After Width: | Height: | Size: 740 B |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
@@ -489,6 +489,7 @@ loadout = Loadout
|
||||
resources = Resources
|
||||
bannedblocks = Banned Blocks
|
||||
addall = Add All
|
||||
launch.destination = Destination: {0}
|
||||
configure.invalid = Amount must be a number between 0 and {0}.
|
||||
zone.unlocked = [lightgray]{0} unlocked.
|
||||
zone.requirement.complete = Requirement for {0} completed:[lightgray]\n{1}
|
||||
@@ -521,6 +522,8 @@ sectors.production = Production:
|
||||
sectors.stored = Stored:
|
||||
sectors.resume = Resume
|
||||
sectors.launch = Launch
|
||||
sectors.select = Select
|
||||
sectors.nonelaunch = [lightgray]none (sun)
|
||||
|
||||
#NOTE TO TRANSLATORS: don't bother editing these, they'll be removed and/or rewritten anyway
|
||||
sector.groundZero.name = Ground Zero
|
||||
@@ -920,6 +923,8 @@ unit.eclipse.name = Eclipse
|
||||
unit.mono.name = Mono
|
||||
unit.poly.name = Poly
|
||||
unit.mega.name = Mega
|
||||
unit.quad.name = Quad
|
||||
unit.oct.name = Oct
|
||||
unit.risso.name = Risso
|
||||
unit.minke.name = Minke
|
||||
unit.bryde.name = Bryde
|
||||
@@ -930,6 +935,8 @@ unit.beta.name = Beta
|
||||
unit.gamma.name = Gamma
|
||||
unit.scepter.name = Scepter
|
||||
unit.reign.name = Reign
|
||||
unit.vela.name = Vela
|
||||
unit.corvus.name = Corvus
|
||||
|
||||
block.resupply-point.name = Resupply Point
|
||||
block.parallax.name = Parallax
|
||||
|
||||
@@ -20,6 +20,8 @@ gameover = Konec hry
|
||||
gameover.pvp = Vyhrál tým [accent]{0}[]!
|
||||
highscore = [accent]Nový rekord!
|
||||
copied = Zkopírováno.
|
||||
indevpopup = Verze [accent]6[] hry je aktuálně v režimu [accent]alfa vývoje[].\n[lightgray]To znamená:[]\n[scarlet][]- Kampaň je zcela nedokončena[]\n- Obsah může chybět\n - Většina [scarlet]umělé inteligence jednotek[] nefunguje správně\n- Hodně jednotek je nedokončeno\n- Vše, co vidíš, se může změnit nebo i být odstraněno.\n\nChyby a pády prosím hlašte na [accent]Github[].[]
|
||||
indev.notready = Tato část hry ještě není připravena
|
||||
|
||||
load.sound = Zvuky
|
||||
load.map = Mapy
|
||||
@@ -113,6 +115,7 @@ mod.disable = Zakázat
|
||||
mod.content = Obsah:
|
||||
mod.delete.error = Nebylo možnost smazat modifikaci. Soubor může být používán.
|
||||
mod.requiresversion = [scarlet]Minimální požadovaná verze hry: [accent]{0}[]
|
||||
mod.outdated = [scarlet]Nekompatibilní s V6 (chybí minGameVersion: 105)
|
||||
mod.missingdependencies = [scarlet]Chybějící závislosti: {0}[]
|
||||
mod.erroredcontent = [scarlet]V obsahu jsou chyby[]
|
||||
mod.errors = Při načítání obsahu hry se vyskytly problémy.
|
||||
@@ -136,6 +139,8 @@ mod.scripts.disable = Tvoje zařízení nepodporuje skripty. Musíš zakázat ty
|
||||
about.button = O hře
|
||||
name = Jméno:
|
||||
noname = Nejdřív si vyber [accent]jméno ve hře[].
|
||||
planetmap = Planetární mapa
|
||||
launchcore = Vyslat jádro
|
||||
filename = Název souboru:
|
||||
unlocked = Byl odemmknut nový blok!
|
||||
completed = [accent]Dokončeno[]
|
||||
@@ -505,6 +510,18 @@ error.io = Vstupně/výstupní (I/O) chyba sítě.
|
||||
error.any = Ueznámá chyba sítě.
|
||||
error.bloom = Chyba inicializace filtru Bloom.\nTvé zařízení ho nejspíš nepodporuje.
|
||||
|
||||
weather.rain.name = Déšť
|
||||
weather.snow.name = Sníh
|
||||
weather.sandstorm.name = Písečná ouře
|
||||
weather.sporestorm.name = Spórová bouře
|
||||
|
||||
sectors.unexplored = [lightgray]Neprozkoumáno
|
||||
sectors.resources = Zdroje:
|
||||
sectors.production = Výroba:
|
||||
sectors.stored = Uskladněno:
|
||||
sectors.resume = Pokračovat
|
||||
sectors.launch = Vyslat
|
||||
|
||||
#NOTE TO TRANSLATORS: don't bother editing these, they'll be removed and/or rewritten anyway
|
||||
sector.groundZero.name = Základní tábor
|
||||
sector.craters.name = Krátery
|
||||
@@ -573,8 +590,11 @@ blocks.itemsmoved = Rychlost pohybu
|
||||
blocks.launchtime = Čas mezi vysláním
|
||||
blocks.shootrange = Dostřel
|
||||
blocks.size = Velikost
|
||||
blocks.displaysize = Velikost zobrazovače
|
||||
blocks.liquidcapacity = Kapacita kapalin
|
||||
blocks.powerrange = Rozsah energie
|
||||
blocks.linkrange = Dosah napojení
|
||||
blocks.instructions = Instrukce
|
||||
blocks.powerconnections = Nejvyšší počet spojení
|
||||
blocks.poweruse = Spotřeba energie
|
||||
blocks.powerdamage = Energie na jednotku poškození
|
||||
@@ -596,6 +616,8 @@ blocks.inaccuracy = Nepřesnost
|
||||
blocks.shots = Střely
|
||||
blocks.reload = Střel za 1s
|
||||
blocks.ammo = Střelivo
|
||||
blocks.shieldhealth = Zdraví štítu
|
||||
blocks.cooldowntime = Čas na zchladnutí
|
||||
|
||||
bar.drilltierreq = Je vyžadován lepší vrt
|
||||
bar.noresources = Chybějí zdroje
|
||||
@@ -638,9 +660,12 @@ unit.liquidunits = jednotek kapalin
|
||||
unit.powerunits = jednotek energie
|
||||
unit.degrees = úhly
|
||||
unit.seconds = sekundy
|
||||
unit.minutes = minuty
|
||||
unit.persecond = /s
|
||||
unit.perminute = /min
|
||||
unit.timesspeed = x větší rychlost
|
||||
unit.percent = %
|
||||
unit.shieldhealth = zdraví štítu
|
||||
unit.items = předměty
|
||||
unit.thousands = tis
|
||||
unit.millions = mio
|
||||
@@ -727,6 +752,7 @@ category.blocks.name = Výběr bloků
|
||||
command.attack = Útok
|
||||
command.rally = Shromáždění
|
||||
command.retreat = Ústup
|
||||
command.idle = Nečinný
|
||||
placement.blockselectkeys = \n[lightgray]Klávesa:[] [{0},
|
||||
keybind.respawn.name = Znovuzrození
|
||||
keybind.control.name = Ovládací jednotka
|
||||
@@ -739,7 +765,8 @@ keybind.toggle_block_status.name = Toggle Block Statuses
|
||||
keybind.move_x.name = Pohyb vodorovně
|
||||
keybind.move_y.name = Pohyb svisle
|
||||
keybind.mouse_move.name = Následovat myš
|
||||
keybind.boost.name = Boost
|
||||
keybind.pan.name = Následovat kameru
|
||||
keybind.boost.name = Posílení
|
||||
keybind.schematic_select.name = Vybrat oblast
|
||||
keybind.schematic_menu.name = Nabídka šablon
|
||||
keybind.schematic_flip_x.name = Překlopit šablona podle svislé osy
|
||||
@@ -766,6 +793,9 @@ keybind.diagonal_placement.name = Umisťovat úhlopříčně
|
||||
keybind.pick.name = Vybrat blok
|
||||
keybind.break_block.name = Rozbít blok
|
||||
keybind.deselect.name = Odznačit
|
||||
keybind.pickupCargo.name = Vyzvednout náklad
|
||||
keybind.dropCargo.name = Položit náklad
|
||||
keybind.command.name = Velet
|
||||
keybind.shoot.name = Střílet
|
||||
keybind.zoom.name = Přiblížení
|
||||
keybind.menu.name = Hlavní nabídka
|
||||
@@ -800,6 +830,7 @@ rules.reactorexplosions = Výbuch reaktoru
|
||||
rules.wavetimer = Časovač vln
|
||||
rules.waves = Vlny
|
||||
rules.attack = Režim útoku
|
||||
rules.buildai = Umělá inteligence staví
|
||||
rules.enemyCheat = Neomezeně surovin pro umělou inteligenci
|
||||
rules.blockhealthmultiplier = Násobek zdraví bloků
|
||||
rules.blockdamagemultiplier = Násobek poškození bloků
|
||||
@@ -821,8 +852,12 @@ rules.title.unit = Jednotky
|
||||
rules.title.experimental = Experimentální
|
||||
rules.title.environment = Environmentální
|
||||
rules.lighting = Světlo
|
||||
rules.fire = Výstřel
|
||||
rules.explosions = Výbušné poškození bloku/jednotky
|
||||
rules.ambientlight = Světlo prostředí
|
||||
rules.solarpowermultiplier = Násobek sluneční energie
|
||||
rules.weather = Počasí
|
||||
rules.weather.frequency = Četnost:
|
||||
rules.weather.duration = Trvání:
|
||||
|
||||
content.item.name = Předměty
|
||||
content.liquid.name = Kapaliny
|
||||
@@ -848,9 +883,11 @@ liquid.water.name = Voda
|
||||
liquid.slag.name = Roztavený kov
|
||||
liquid.oil.name = Nafta
|
||||
liquid.cryofluid.name = Chladící kapalina
|
||||
|
||||
item.explosiveness = [lightgray]Výbušnost: {0}%[]
|
||||
item.flammability = [lightgray]Zápalnost: {0}%[]
|
||||
item.radioactivity = [lightgray]Radioaktivita: {0}%[]
|
||||
|
||||
unit.health = [lightgray]Životy: {0}[]
|
||||
unit.speed = [lightgray]Rychlost: {0}[]
|
||||
unit.weapon = [lightgray]Zbraň: {0}
|
||||
@@ -872,8 +909,9 @@ unit.pulsar.name = Pulzar
|
||||
unit.quasar.name = Kvasar
|
||||
unit.crawler.name = Slídil
|
||||
unit.atrax.name = Atrax
|
||||
unit.spiroct.name = Spiroct
|
||||
unit.spiroct.name = Spirokt
|
||||
unit.arkyid.name = Arkyid
|
||||
unit.toxopid.name = Toxopid
|
||||
unit.flare.name = Záře
|
||||
unit.horizon.name = Horizont
|
||||
unit.zenith.name = Zenit
|
||||
@@ -885,31 +923,36 @@ unit.mega.name = Mega
|
||||
unit.risso.name = Risso
|
||||
unit.minke.name = Minke
|
||||
unit.bryde.name = Bryde
|
||||
unit.sei.name = Sei
|
||||
unit.omura.name = Omura
|
||||
unit.alpha.name = Alfa
|
||||
unit.beta.name = Beta
|
||||
unit.gamma.name = Gama
|
||||
unit.scepter.name = Žezlo
|
||||
unit.reign.name = Panovník
|
||||
|
||||
block.resupply-point.name = Zásobovací místo
|
||||
block.parallax.name = Paralaxa
|
||||
block.cliff.name = Útes
|
||||
block.sand-boulder.name = Pískovec
|
||||
block.grass.name = Tráva
|
||||
block.slag.name = Slag
|
||||
block.salt.name = Sůl
|
||||
block.saltrocks.name = Solný kámen
|
||||
block.salt-wall.name = Solné skály
|
||||
block.pebbles.name = Oblázky
|
||||
block.tendrils.name = Úponky
|
||||
block.sandrocks.name = Písečný kámen
|
||||
block.sand-wall.name = Písečné skály
|
||||
block.spore-pine.name = Spórová borovice
|
||||
block.sporerocks.name = Spórové kamení
|
||||
block.rock.name = Kámen
|
||||
block.snowrock.name = Sněhový kámen
|
||||
block.spore-wall.name = Spórové skály
|
||||
block.boulder.name = Kámen
|
||||
block.snow-boulder.name = Sněhový kámen
|
||||
block.snow-pine.name = Sněžná borovice
|
||||
block.shale.name = Břidlice
|
||||
block.shale-boulder.name = Břidličný balvan
|
||||
block.moss.name = Mech
|
||||
block.shrubs.name = Křoví
|
||||
block.spore-moss.name = Spórový mech
|
||||
block.shalerocks.name = Břidlicové kamení
|
||||
block.shale-wall.name = Břidlicové skály
|
||||
block.scrap-wall.name = Rozpadlá zeď
|
||||
block.scrap-wall-large.name = Velká rozpadlá zeď
|
||||
block.scrap-wall-huge.name = Obří rozpadlá zeď
|
||||
@@ -936,14 +979,18 @@ block.snow.name = Sníh
|
||||
block.craters.name = Krátery
|
||||
block.sand-water.name = Voda s pískem
|
||||
block.darksand-water.name = Voda s černým pískem
|
||||
block.char.name = Char
|
||||
block.holostone.name = Blok kamene
|
||||
block.char.name = Dřevěné uhlí
|
||||
block.dacite.name = Dacit
|
||||
block.dacite-wall.name = Stěna dacitu
|
||||
block.ice-snow.name = Zasněžený led
|
||||
block.rocks.name = Skály
|
||||
block.icerocks.name = Ledové skály
|
||||
block.snowrocks.name = Sněhové skály
|
||||
block.dunerocks.name = Písečné duny
|
||||
block.stone wall.name = Skály
|
||||
block.ice-wall.name = Ledové skály
|
||||
block.snow-wall.name = Sněhové skály
|
||||
block.dune-wall.name = Písečné duny
|
||||
block.pine.name = Borovice
|
||||
block.dirt.name = Hlína
|
||||
block.dirt-wall.name = Kopec hlíny
|
||||
block.mud.name = Bláto
|
||||
block.white-tree-dead.name = Bílá souška
|
||||
block.white-tree.name = Bílý strom
|
||||
block.spore-cluster.name = Shluk spór
|
||||
@@ -959,10 +1006,9 @@ block.dark-panel-4.name = Tmavá deska 4
|
||||
block.dark-panel-5.name = Tmavá deska 5
|
||||
block.dark-panel-6.name = Tmavá deska 6
|
||||
block.dark-metal.name = Tmavý kov
|
||||
block.ignarock.name = Třaskavec
|
||||
block.basalt.name = Čedič
|
||||
block.hotrock.name = Horký kámen
|
||||
block.magmarock.name = Magmatický kámen
|
||||
block.cliffs.name = Útesy
|
||||
block.copper-wall.name = Měděná zeď
|
||||
block.copper-wall-large.name = Velká měděná zeď
|
||||
block.titanium-wall.name = Titanová zeď
|
||||
@@ -1073,6 +1119,7 @@ block.container.name = Kontejnér
|
||||
block.launch-pad.name = Vysílací plošina
|
||||
block.launch-pad-large.name = Velká vysílací plošina
|
||||
block.segment.name = Úsek
|
||||
block.command-center.name = Velín
|
||||
block.ground-factory.name = Pozemní továrna
|
||||
block.air-factory.name = Letecká továrna
|
||||
block.naval-factory.name = Námořní továrna
|
||||
@@ -1089,7 +1136,9 @@ block.overdrive-dome.name = Velká urychlující kupole
|
||||
block.switch.name = Přepínač
|
||||
block.micro-processor.name = Mikroprocesor
|
||||
block.logic-processor.name = Logický procesor
|
||||
block.hyper-processor.name = Hyperprocesor
|
||||
block.logic-display.name = Zobrazovač logiky
|
||||
block.large-logic-display.name = Velký zobrazovač logiky
|
||||
block.memory-cell.name = Paměťová buňka
|
||||
|
||||
team.blue.name = modrý
|
||||
|
||||
@@ -92,3 +92,4 @@ SnakkiZXZ
|
||||
sk7725
|
||||
ThePlayerA
|
||||
YellOw139
|
||||
PetrGasparik
|
||||
|
||||
@@ -314,3 +314,7 @@
|
||||
63430=omura|unit-omura-medium
|
||||
63429=mud|block-mud-medium
|
||||
63428=sei|unit-sei-medium
|
||||
63427=quad|unit-quad-medium
|
||||
63426=oct|unit-oct-medium
|
||||
63425=vela|unit-vela-medium
|
||||
63424=corvus|unit-corvus-medium
|
||||
|
||||
@@ -74,6 +74,7 @@ importPackage(Packages.mindustry.gen)
|
||||
importPackage(Packages.mindustry.graphics)
|
||||
importPackage(Packages.mindustry.graphics.g3d)
|
||||
importPackage(Packages.mindustry.input)
|
||||
importPackage(Packages.mindustry.io)
|
||||
importPackage(Packages.mindustry.logic)
|
||||
importPackage(Packages.mindustry.maps)
|
||||
importPackage(Packages.mindustry.maps.filters)
|
||||
|
||||
|
Before Width: | Height: | Size: 642 KiB After Width: | Height: | Size: 553 KiB |
|
Before Width: | Height: | Size: 735 KiB After Width: | Height: | Size: 662 KiB |
|
Before Width: | Height: | Size: 1020 KiB After Width: | Height: | Size: 950 KiB |
|
Before Width: | Height: | Size: 185 KiB After Width: | Height: | Size: 503 KiB |
|
Before Width: | Height: | Size: 326 KiB After Width: | Height: | Size: 184 KiB |
|
Before Width: | Height: | Size: 416 KiB After Width: | Height: | Size: 326 KiB |
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 416 KiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
Before Width: | Height: | Size: 2.6 MiB After Width: | Height: | Size: 2.8 MiB |
|
Before Width: | Height: | Size: 186 KiB After Width: | Height: | Size: 185 KiB |
|
Before Width: | Height: | Size: 420 KiB After Width: | Height: | Size: 420 KiB |
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 1.4 MiB |
@@ -72,6 +72,8 @@ public class Vars implements Loadable{
|
||||
public static final int maxTextLength = 150;
|
||||
/** max player name length in bytes */
|
||||
public static final int maxNameLength = 40;
|
||||
/** shadow color for turrets */
|
||||
public static final float turretShadowColor = Color.toFloatBits(0, 0, 0, 0.22f);
|
||||
/** displayed item size when ingame. */
|
||||
public static final float itemSize = 5f;
|
||||
/** units outside of this bound will die instantly */
|
||||
@@ -87,15 +89,17 @@ public class Vars implements Loadable{
|
||||
/** turns needed to destroy a sector completely */
|
||||
public static final float sectorDestructionTurns = 3f;
|
||||
/** min armor fraction damage; e.g. 0.05 = at least 5% damage */
|
||||
public static final float minArmorDamage = 0.05f;
|
||||
public static final float minArmorDamage = 0.1f;
|
||||
/** launch animation duration */
|
||||
public static final float launchDuration = 140f;
|
||||
/** size of tiles in units */
|
||||
public static final int tilesize = 8;
|
||||
/** size of one tile payload (^2) */
|
||||
public static final float tilePayload = tilesize * tilesize;
|
||||
/** tile used in certain situations, instead of null */
|
||||
public static Tile emptyTile;
|
||||
/** for map generator dialog */
|
||||
public static boolean updateEditorOnChange = false;
|
||||
/** size of tiles in units */
|
||||
public static final int tilesize = 8;
|
||||
/** all choosable player colors in join/host dialog */
|
||||
public static final Color[] playerColors = {
|
||||
Color.valueOf("82759a"),
|
||||
|
||||
@@ -43,7 +43,8 @@ public class Pathfinder implements Runnable{
|
||||
PathTile.health(tile) * 5 +
|
||||
(PathTile.nearSolid(tile) ? 2 : 0) +
|
||||
(PathTile.nearLiquid(tile) ? 6 : 0) +
|
||||
(PathTile.deep(tile) ? 70 : 0),
|
||||
(PathTile.deep(tile) ? 70 : 0) +
|
||||
(PathTile.damages(tile) ? 30 : 0),
|
||||
|
||||
//legs
|
||||
(team, tile) -> PathTile.legSolid(tile) ? impassable : 1 +
|
||||
@@ -52,7 +53,8 @@ public class Pathfinder implements Runnable{
|
||||
//water
|
||||
(team, tile) -> PathTile.solid(tile) || !PathTile.liquid(tile) ? 200 : 2 + //TODO cannot go through blocks - pathfinding isn't great
|
||||
(PathTile.nearGround(tile) || PathTile.nearSolid(tile) ? 14 : 0) +
|
||||
(PathTile.deep(tile) ? -1 : 0)
|
||||
(PathTile.deep(tile) ? -1 : 0) +
|
||||
(PathTile.damages(tile) ? 35 : 0)
|
||||
);
|
||||
|
||||
//maps team, cost, type to flow field
|
||||
@@ -122,7 +124,8 @@ public class Pathfinder implements Runnable{
|
||||
nearLiquid,
|
||||
nearGround,
|
||||
nearSolid,
|
||||
tile.floor().isDeep()
|
||||
tile.floor().isDeep(),
|
||||
tile.floor().damageTaken > 0.00001f
|
||||
);
|
||||
}
|
||||
|
||||
@@ -514,5 +517,7 @@ public class Pathfinder implements Runnable{
|
||||
boolean nearSolid;
|
||||
//whether this block is deep / drownable
|
||||
boolean deep;
|
||||
//whether the floor damages
|
||||
boolean damages;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import mindustry.world.blocks.ConstructBlock.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class BuilderAI extends AIController{
|
||||
float buildRadius = 700;
|
||||
float buildRadius = 1500;
|
||||
boolean found = false;
|
||||
@Nullable Builderc following;
|
||||
|
||||
|
||||
@@ -25,8 +25,7 @@ public class FlyingAI extends AIController{
|
||||
}
|
||||
|
||||
if(command() == UnitCommand.rally){
|
||||
target = targetFlag(unit.x, unit.y, BlockFlag.rally, false);
|
||||
moveTo(target, 60f);
|
||||
moveTo(targetFlag(unit.x, unit.y, BlockFlag.rally, false), 60f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package mindustry.ai.types;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import mindustry.ai.formations.*;
|
||||
@@ -32,6 +33,10 @@ public class FormationAI extends AIController implements FormationMember{
|
||||
return;
|
||||
}
|
||||
|
||||
if(unit.type().canBoost && unit.canPassOn()){
|
||||
unit.elevation = Mathf.approachDelta(unit.elevation, 0f, 0.08f);
|
||||
}
|
||||
|
||||
unit.controlWeapons(true, leader.isShooting);
|
||||
// unit.moveAt(Tmp.v1.set(deltaX, deltaY).limit(unit.type().speed));
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ public class GroundAI extends AIController{
|
||||
}
|
||||
}
|
||||
|
||||
if(unit.type().canBoost && !unit.onSolid()){
|
||||
if(unit.type().canBoost && unit.canPassOn()){
|
||||
unit.elevation = Mathf.approachDelta(unit.elevation, 0f, 0.08f);
|
||||
}
|
||||
|
||||
@@ -74,15 +74,12 @@ public class GroundAI extends AIController{
|
||||
}*/
|
||||
}
|
||||
|
||||
protected void moveTo(int pathType){
|
||||
int costType =
|
||||
unit instanceof Legsc ? Pathfinder.costLegs :
|
||||
unit instanceof WaterMovec ? Pathfinder.costWater :
|
||||
Pathfinder.costGround;
|
||||
protected void moveTo(int pathTarget){
|
||||
int costType = unit.pathType();
|
||||
|
||||
Tile tile = unit.tileOn();
|
||||
if(tile == null) return;
|
||||
Tile targetTile = pathfinder.getTargetTile(tile, pathfinder.getField(unit.team, costType, pathType));
|
||||
Tile targetTile = pathfinder.getTargetTile(tile, pathfinder.getField(unit.team, costType, pathTarget));
|
||||
|
||||
if(tile == targetTile || (costType == Pathfinder.costWater && !targetTile.floor().isLiquid)) return;
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ public class MinerAI extends AIController{
|
||||
}
|
||||
|
||||
if(ore != null){
|
||||
moveTo(ore, unit.type().range / 1.5f);
|
||||
moveTo(ore, unit.type().range / 2f);
|
||||
|
||||
if(unit.within(ore, unit.type().range)){
|
||||
miner.mineTile(ore);
|
||||
|
||||
@@ -14,7 +14,9 @@ public class RepairAI extends AIController{
|
||||
if(target != null){
|
||||
if(!target.within(unit, unit.type().range * 0.8f)){
|
||||
moveTo(target, unit.type().range * 0.8f);
|
||||
}else{
|
||||
}
|
||||
|
||||
if(target.within(unit, unit.type().range)){
|
||||
unit.aim(target);
|
||||
shoot = true;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ public class TeamIndexProcess implements AsyncProcess{
|
||||
private QuadTree<Unit>[] trees = new QuadTree[Team.all.length];
|
||||
private int[] counts = new int[Team.all.length];
|
||||
private int[][] typeCounts = new int[Team.all.length][0];
|
||||
private int[][] activeCounts = new int[Team.all.length][0];
|
||||
|
||||
public QuadTree<Unit> tree(Team team){
|
||||
if(trees[team.id] == null) trees[team.id] = new QuadTree<>(Vars.world.getQuadBounds(new Rect()));
|
||||
@@ -30,10 +29,6 @@ public class TeamIndexProcess implements AsyncProcess{
|
||||
return typeCounts[team.id].length <= type.id ? 0 : typeCounts[team.id][type.id];
|
||||
}
|
||||
|
||||
public int countActive(Team team, UnitType type){
|
||||
return activeCounts[team.id].length <= type.id ? 0 : activeCounts[team.id][type.id];
|
||||
}
|
||||
|
||||
public void updateCount(Team team, UnitType type, int amount){
|
||||
counts[team.id] += amount;
|
||||
if(typeCounts[team.id].length <= type.id){
|
||||
@@ -42,16 +37,8 @@ public class TeamIndexProcess implements AsyncProcess{
|
||||
typeCounts[team.id][type.id] += amount;
|
||||
}
|
||||
|
||||
public void updateActiveCount(Team team, UnitType type, int amount){
|
||||
if(activeCounts[team.id].length <= type.id){
|
||||
activeCounts[team.id] = new int[Vars.content.units().size];
|
||||
}
|
||||
activeCounts[team.id][type.id] += amount;
|
||||
}
|
||||
|
||||
private void count(Unit unit){
|
||||
updateCount(unit.team, unit.type(), 1);
|
||||
if(!unit.deactivated) updateActiveCount(unit.team, unit.type(), 1);
|
||||
|
||||
if(unit instanceof Payloadc){
|
||||
((Payloadc)unit).payloads().each(p -> {
|
||||
@@ -77,7 +64,6 @@ public class TeamIndexProcess implements AsyncProcess{
|
||||
}
|
||||
|
||||
Arrays.fill(typeCounts[team.id], 0);
|
||||
Arrays.fill(activeCounts[team.id], 0);
|
||||
}
|
||||
|
||||
Arrays.fill(counts, 0);
|
||||
|
||||
@@ -1267,7 +1267,7 @@ public class Blocks implements ContentList{
|
||||
rotateSpeed = 1.4f;
|
||||
attribute = Attribute.water;
|
||||
|
||||
consumes.power(1f);
|
||||
consumes.power(1.25f);
|
||||
}};
|
||||
|
||||
cultivator = new Cultivator("cultivator"){{
|
||||
@@ -1320,7 +1320,7 @@ public class Blocks implements ContentList{
|
||||
requirements(Category.effect, with(Items.copper, 3000, Items.lead, 3000, Items.silicon, 2000));
|
||||
|
||||
unitType = UnitTypes.beta;
|
||||
health = 2000;
|
||||
health = 3500;
|
||||
itemCapacity = 9000;
|
||||
size = 4;
|
||||
|
||||
@@ -1331,7 +1331,7 @@ public class Blocks implements ContentList{
|
||||
requirements(Category.effect, with(Items.copper, 8000, Items.lead, 8000, Items.silicon, 5000, Items.thorium, 4000));
|
||||
|
||||
unitType = UnitTypes.gamma;
|
||||
health = 4000;
|
||||
health = 6000;
|
||||
itemCapacity = 13000;
|
||||
size = 5;
|
||||
|
||||
@@ -1377,7 +1377,7 @@ public class Blocks implements ContentList{
|
||||
ammoUseEffect = Fx.shellEjectSmall;
|
||||
health = 250;
|
||||
inaccuracy = 2f;
|
||||
rotatespeed = 10f;
|
||||
rotateSpeed = 10f;
|
||||
}};
|
||||
|
||||
scatter = new ItemTurret("scatter"){{
|
||||
@@ -1395,7 +1395,7 @@ public class Blocks implements ContentList{
|
||||
targetGround = false;
|
||||
|
||||
recoilAmount = 2f;
|
||||
rotatespeed = 15f;
|
||||
rotateSpeed = 15f;
|
||||
inaccuracy = 17f;
|
||||
shootCone = 35f;
|
||||
|
||||
@@ -1498,7 +1498,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
reloadTime = 35f;
|
||||
shootCone = 40f;
|
||||
rotatespeed = 8f;
|
||||
rotateSpeed = 8f;
|
||||
powerUse = 4f;
|
||||
targetAir = false;
|
||||
range = 90f;
|
||||
@@ -1597,8 +1597,10 @@ public class Blocks implements ContentList{
|
||||
health = 220 * size * size;
|
||||
shootSound = Sounds.shotgun;
|
||||
|
||||
float brange = range + 10f;
|
||||
|
||||
ammo(Items.thorium, new ShrapnelBulletType(){{
|
||||
length = range + 10f;
|
||||
length = brange;
|
||||
damage = 105f;
|
||||
ammoMultiplier = 6f;
|
||||
}});
|
||||
@@ -1647,7 +1649,7 @@ public class Blocks implements ContentList{
|
||||
range = 200f;
|
||||
size = 3;
|
||||
recoilAmount = 3f;
|
||||
rotatespeed = 10f;
|
||||
rotateSpeed = 10f;
|
||||
inaccuracy = 10f;
|
||||
shootCone = 30f;
|
||||
shootSound = Sounds.shootSnap;
|
||||
@@ -1805,6 +1807,8 @@ public class Blocks implements ContentList{
|
||||
{UnitTypes.spiroct, UnitTypes.arkyid},
|
||||
{UnitTypes.fortress, UnitTypes.scepter},
|
||||
{UnitTypes.bryde, UnitTypes.sei},
|
||||
{UnitTypes.mega, UnitTypes.quad},
|
||||
{UnitTypes.quasar, UnitTypes.vela},
|
||||
};
|
||||
}};
|
||||
|
||||
@@ -1823,7 +1827,9 @@ public class Blocks implements ContentList{
|
||||
{UnitTypes.antumbra, UnitTypes.eclipse},
|
||||
{UnitTypes.arkyid, UnitTypes.toxopid},
|
||||
{UnitTypes.scepter, UnitTypes.reign},
|
||||
{UnitTypes.sei, UnitTypes.omura}
|
||||
{UnitTypes.sei, UnitTypes.omura},
|
||||
{UnitTypes.quad, UnitTypes.oct},
|
||||
{UnitTypes.vela, UnitTypes.corvus}
|
||||
};
|
||||
}};
|
||||
|
||||
|
||||
@@ -145,7 +145,7 @@ public class Fx{
|
||||
|
||||
int i = 0;
|
||||
for(Vec2 p : lines){
|
||||
Fill.square(p.x, p.y, (5f - (float)i++ / lines.size * 2f) * e.fout(), 45);
|
||||
Fill.circle(p.x, p.y, Lines.getStroke() / 2f);
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -290,6 +290,50 @@ public class Fx{
|
||||
Lines.spikes(e.x, e.y, 1f + e.fin() * 6f, e.fout() * 4f, 6);
|
||||
}),
|
||||
|
||||
greenBomb = new Effect(40f, 100f, e -> {
|
||||
color(Pal.heal);
|
||||
stroke(e.fout() * 2f);
|
||||
Lines.circle(e.x, e.y, 4f + e.finpow() * 65f);
|
||||
|
||||
color(Pal.heal);
|
||||
for(int i = 0; i < 4; i++){
|
||||
Drawf.tri(e.x, e.y, 6f, 100f * e.fout(), i*90);
|
||||
}
|
||||
|
||||
color();
|
||||
for(int i = 0; i < 4; i++){
|
||||
Drawf.tri(e.x, e.y, 3f, 35f * e.fout(), i*90);
|
||||
}
|
||||
}),
|
||||
|
||||
greenLaserCharge = new Effect(80f, 100f, e -> {
|
||||
color(Pal.heal);
|
||||
stroke(e.fin() * 2f);
|
||||
Lines.circle(e.x, e.y, 4f + e.fout() * 100f);
|
||||
|
||||
Fill.circle(e.x, e.y, e.fin() * 20);
|
||||
|
||||
randLenVectors(e.id, 20, 40f * e.fout(), (x, y) -> {
|
||||
Fill.circle(e.x + x, e.y + y, e.fin() * 5f);
|
||||
});
|
||||
|
||||
color();
|
||||
|
||||
Fill.circle(e.x, e.y, e.fin() * 10);
|
||||
}),
|
||||
|
||||
greenLaserChargeSmall = new Effect(40f, 100f, e -> {
|
||||
color(Pal.heal);
|
||||
stroke(e.fin() * 2f);
|
||||
Lines.circle(e.x, e.y, e.fout() * 50f);
|
||||
}),
|
||||
|
||||
healWaveDynamic = new Effect(22, e -> {
|
||||
color(Pal.heal);
|
||||
stroke(e.fout() * 2f);
|
||||
Lines.circle(e.x, e.y, 4f + e.finpow() * e.rotation);
|
||||
}),
|
||||
|
||||
healWave = new Effect(22, e -> {
|
||||
color(Pal.heal);
|
||||
stroke(e.fout() * 2f);
|
||||
@@ -400,6 +444,17 @@ public class Fx{
|
||||
|
||||
}),
|
||||
|
||||
hitMeltHeal = new Effect(12, e -> {
|
||||
color(Pal.heal);
|
||||
stroke(e.fout() * 2f);
|
||||
|
||||
randLenVectors(e.id, 6, e.finpow() * 18f, e.rotation, 360f, (x, y) -> {
|
||||
float ang = Mathf.angle(x, y);
|
||||
lineAngle(e.x + x, e.y + y, ang, e.fout() * 4 + 1f);
|
||||
});
|
||||
|
||||
}),
|
||||
|
||||
hitLaser = new Effect(8, e -> {
|
||||
color(Color.white, Pal.heal, e.fin());
|
||||
stroke(0.5f + e.fout());
|
||||
@@ -1229,6 +1284,14 @@ public class Fx{
|
||||
Fill.square(e.x + x, e.y + y, 1f + e.fout() * 3f, 45);
|
||||
});
|
||||
}),
|
||||
|
||||
smokeCloud = new Effect(70, e -> {
|
||||
randLenVectors(e.id, e.fin(), 30, 30f, (x, y, fin, fout) -> {
|
||||
color(Color.gray);
|
||||
alpha((0.5f - Math.abs(fin - 0.5f)) * 2f);
|
||||
Fill.circle(e.x + x, e.y + y, 0.5f + fout * 4f);
|
||||
});
|
||||
}),
|
||||
|
||||
smeltsmoke = new Effect(15, e -> {
|
||||
randLenVectors(e.id, 6, 4f + e.fin() * 5f, (x, y) -> {
|
||||
|
||||
@@ -9,7 +9,7 @@ import mindustry.type.StatusEffect;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class StatusEffects implements ContentList{
|
||||
public static StatusEffect none, burning, freezing, wet, muddy, melting, sapped, tarred, overdrive, overclock, shielded, shocked, blasted, corroded, boss, sporeSlowed;
|
||||
public static StatusEffect none, burning, freezing, unmoving, slow, wet, muddy, melting, sapped, tarred, overdrive, overclock, shielded, shocked, blasted, corroded, boss, sporeSlowed;
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
@@ -45,6 +45,14 @@ public class StatusEffects implements ContentList{
|
||||
});
|
||||
}};
|
||||
|
||||
unmoving = new StatusEffect("unmoving"){{
|
||||
speedMultiplier = 0.001f;
|
||||
}};
|
||||
|
||||
slow = new StatusEffect("slow"){{
|
||||
speedMultiplier = 0.4f;
|
||||
}};
|
||||
|
||||
wet = new StatusEffect("wet"){{
|
||||
color = Color.royal;
|
||||
speedMultiplier = 0.94f;
|
||||
|
||||
@@ -263,7 +263,7 @@ public class TechTree implements ContentList{
|
||||
node(steamGenerator, () -> {
|
||||
node(thermalGenerator, () -> {
|
||||
node(differentialGenerator, () -> {
|
||||
node(thoriumReactor, () -> {
|
||||
node(thoriumReactor, Seq.with(new Research(Liquids.cryofluid)), () -> {
|
||||
node(impactReactor, () -> {
|
||||
|
||||
});
|
||||
@@ -409,7 +409,11 @@ public class TechTree implements ContentList{
|
||||
node(mono, () -> {
|
||||
node(poly, () -> {
|
||||
node(mega, () -> {
|
||||
node(quad, () -> {
|
||||
node(oct, () -> {
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -538,7 +542,7 @@ public class TechTree implements ContentList{
|
||||
}
|
||||
|
||||
public static TechNode node(UnlockableContent content, Seq<Objective> objectives, Runnable children){
|
||||
TechNode node = new TechNode(content, empty, children);
|
||||
TechNode node = new TechNode(content, content.researchRequirements(), children);
|
||||
node.objectives = objectives;
|
||||
return node;
|
||||
}
|
||||
|
||||
@@ -11,15 +11,23 @@ import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class UnitTypes implements ContentList{
|
||||
//region definitions
|
||||
|
||||
//ground
|
||||
//mech
|
||||
public static @EntityDef({Unitc.class, Mechc.class}) UnitType mace, dagger, crawler, fortress, scepter, reign;
|
||||
|
||||
//ground + builder + miner + commander
|
||||
//mech + builder + miner + commander
|
||||
public static @EntityDef({Unitc.class, Mechc.class, Builderc.class, Minerc.class, Commanderc.class}) UnitType nova, pulsar, quasar;
|
||||
|
||||
//mech + commander
|
||||
public static @EntityDef({Unitc.class, Mechc.class, Commanderc.class}) UnitType vela;
|
||||
|
||||
//legs + commander
|
||||
public static @EntityDef({Unitc.class, Legsc.class, Commanderc.class}) UnitType corvus;
|
||||
|
||||
//legs
|
||||
public static @EntityDef({Unitc.class, Legsc.class}) UnitType atrax;
|
||||
|
||||
@@ -39,7 +47,7 @@ public class UnitTypes implements ContentList{
|
||||
public static @EntityDef({Unitc.class, Builderc.class, Minerc.class, Payloadc.class}) UnitType mega;
|
||||
|
||||
//air + building + payload TODO implement
|
||||
public static @EntityDef({Unitc.class, Builderc.class, Minerc.class, Payloadc.class}) UnitType quad, oct;
|
||||
public static @EntityDef({Unitc.class, Builderc.class, Payloadc.class}) UnitType quad, oct;
|
||||
|
||||
//air + building + mining
|
||||
public static @EntityDef({Unitc.class, Builderc.class, Minerc.class}) UnitType alpha, beta, gamma;
|
||||
@@ -257,7 +265,7 @@ public class UnitTypes implements ContentList{
|
||||
itemCapacity = 60;
|
||||
canBoost = true;
|
||||
boostMultiplier = 1.5f;
|
||||
speed = 0.52f;
|
||||
speed = 0.55f;
|
||||
hitsize = 8f;
|
||||
health = 110f;
|
||||
buildSpeed = 0.8f;
|
||||
@@ -283,7 +291,7 @@ public class UnitTypes implements ContentList{
|
||||
itemCapacity = 60;
|
||||
canBoost = true;
|
||||
boostMultiplier = 1.5f;
|
||||
speed = 0.62f;
|
||||
speed = 0.65f;
|
||||
hitsize = 10f;
|
||||
health = 320f;
|
||||
buildSpeed = 0.9f;
|
||||
@@ -365,6 +373,136 @@ public class UnitTypes implements ContentList{
|
||||
}});
|
||||
}};
|
||||
|
||||
vela = new UnitType("vela"){{
|
||||
hitsize = 23f;
|
||||
|
||||
rotateSpeed = 1.6f;
|
||||
canDrown = false;
|
||||
mechFrontSway = 1f;
|
||||
|
||||
mechStepParticles = true;
|
||||
mechStepShake = 0.15f;
|
||||
|
||||
speed = 0.35f;
|
||||
boostMultiplier = 2.1f;
|
||||
engineOffset = 12f;
|
||||
engineSize = 6f;
|
||||
lowAltitude = true;
|
||||
|
||||
health = 6000f;
|
||||
armor = 7f;
|
||||
canBoost = true;
|
||||
landShake = 4f;
|
||||
|
||||
commandLimit = 32;
|
||||
|
||||
weapons.add(new Weapon("vela-weapon"){{
|
||||
mirror = false;
|
||||
top = false;
|
||||
shake = 4f;
|
||||
shootY = 13f;
|
||||
x = y = 0f;
|
||||
|
||||
firstShotDelay = Fx.greenLaserChargeSmall.lifetime - 1f;
|
||||
|
||||
reload = 320f;
|
||||
recoil = 0f;
|
||||
shootSound = Sounds.laser;
|
||||
continuous = true;
|
||||
cooldownTime = 200f;
|
||||
|
||||
bullet = new ContinuousLaserBulletType(16){{
|
||||
length = 150f;
|
||||
hitEffect = Fx.hitMeltHeal;
|
||||
drawSize = 420f;
|
||||
lifetime = 160f;
|
||||
shake = 1f;
|
||||
despawnEffect = Fx.smokeCloud;
|
||||
smokeEffect = Fx.none;
|
||||
|
||||
shootEffect = Fx.greenLaserChargeSmall;
|
||||
|
||||
incendChance = 0.02f;
|
||||
incendSpread = 5f;
|
||||
incendAmount = 1;
|
||||
|
||||
colors = new Color[]{Pal.heal.cpy().a(.2f), Pal.heal.cpy().a(.5f), Pal.heal.cpy().mul(1.2f), Color.white};
|
||||
}};
|
||||
|
||||
shootStatus = StatusEffects.slow;
|
||||
shootStatusDuration = bullet.lifetime + firstShotDelay;
|
||||
}});
|
||||
}};
|
||||
|
||||
corvus = new UnitType("corvus"){{
|
||||
mineTier = 1;
|
||||
hitsize = 29f;
|
||||
itemCapacity = 80;
|
||||
health = 19000f;
|
||||
buildSpeed = 1.7f;
|
||||
armor = 9f;
|
||||
landShake = 1.5f;
|
||||
rotateSpeed = 1.5f;
|
||||
|
||||
commandLimit = 24;
|
||||
|
||||
legCount = 4;
|
||||
legLength = 14f;
|
||||
legBaseOffset = 11f;
|
||||
legMoveSpace = 1.5f;
|
||||
legTrns = 0.58f;
|
||||
hovering = true;
|
||||
visualElevation = 0.2f;
|
||||
allowLegStep = true;
|
||||
|
||||
speed = 0.3f;
|
||||
|
||||
mineTier = 2;
|
||||
mineSpeed = 7f;
|
||||
drawShields = false;
|
||||
|
||||
weapons.add(new Weapon("corvus-weapon"){{
|
||||
top = false;
|
||||
mirror = false;
|
||||
shake = 14f;
|
||||
shootY = 5f;
|
||||
x = y = 0;
|
||||
reload = 350f;
|
||||
recoil = 0f;
|
||||
shootSound = Sounds.laser;
|
||||
|
||||
cooldownTime = 350f;
|
||||
|
||||
shootStatusDuration = 60f * 2f;
|
||||
shootStatus = StatusEffects.unmoving;
|
||||
firstShotDelay = Fx.greenLaserCharge.lifetime;
|
||||
|
||||
bullet = new LaserBulletType(){{
|
||||
length = 500f;
|
||||
damage = 520f;
|
||||
width = 75f;
|
||||
|
||||
lifetime = 65f;
|
||||
|
||||
lightningSpacing = 35f;
|
||||
lightningLength = 5;
|
||||
lightningDelay = 1.1f;
|
||||
lightningLengthRand = 15;
|
||||
lightningDamage = 50;
|
||||
lightningAngleRand = 40f;
|
||||
largeHit = true;
|
||||
lightColor = lightningColor = Pal.heal;
|
||||
|
||||
shootEffect = Fx.greenLaserCharge;
|
||||
|
||||
sideAngle = 15f;
|
||||
sideWidth = 0f;
|
||||
sideLength = 0f;
|
||||
colors = new Color[]{Pal.heal.cpy().a(0.4f), Pal.heal, Color.white};
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
|
||||
//endregion
|
||||
//region ground legs
|
||||
|
||||
@@ -405,6 +543,7 @@ public class UnitTypes implements ContentList{
|
||||
targetAir = false;
|
||||
health = 600;
|
||||
immunities = ObjectSet.with(StatusEffects.burning, StatusEffects.melting);
|
||||
|
||||
legCount = 4;
|
||||
legLength = 9f;
|
||||
legTrns = 0.6f;
|
||||
@@ -603,8 +742,8 @@ public class UnitTypes implements ContentList{
|
||||
drag = 0.1f;
|
||||
speed = 0.5f;
|
||||
hitsize = 21f;
|
||||
health = 23000;
|
||||
armor = 14f;
|
||||
health = 22000;
|
||||
armor = 13f;
|
||||
|
||||
rotateSpeed = 1.9f;
|
||||
|
||||
@@ -729,7 +868,6 @@ public class UnitTypes implements ContentList{
|
||||
faceTarget = false;
|
||||
engineOffset = 5.5f;
|
||||
range = 140f;
|
||||
crashDamageMultiplier = 4f;
|
||||
|
||||
weapons.add(new Weapon(){{
|
||||
y = 0f;
|
||||
@@ -1047,6 +1185,8 @@ public class UnitTypes implements ContentList{
|
||||
|
||||
mineTier = 2;
|
||||
health = 500;
|
||||
armor = 2f;
|
||||
armor = 5f;
|
||||
speed = 1.8f;
|
||||
accel = 0.06f;
|
||||
drag = 0.017f;
|
||||
@@ -1056,7 +1196,7 @@ public class UnitTypes implements ContentList{
|
||||
rotateShooting = false;
|
||||
hitsize = 15f;
|
||||
engineSize = 3f;
|
||||
payloadCapacity = 4 * (8 * 8);
|
||||
payloadCapacity = (2 * 2) * tilePayload;
|
||||
buildSpeed = 2.5f;
|
||||
|
||||
weapons.add(
|
||||
@@ -1076,6 +1216,86 @@ public class UnitTypes implements ContentList{
|
||||
}});
|
||||
}};
|
||||
|
||||
quad = new UnitType("quad"){{
|
||||
armor = 4f;
|
||||
health = 6000;
|
||||
speed = 1.2f;
|
||||
rotateSpeed = 2f;
|
||||
accel = 0.05f;
|
||||
drag = 0.017f;
|
||||
lowAltitude = false;
|
||||
flying = true;
|
||||
engineOffset = 12f;
|
||||
engineSize = 6f;
|
||||
rotateShooting = false;
|
||||
hitsize = 32f;
|
||||
payloadCapacity = (3 * 3) * tilePayload;
|
||||
buildSpeed = 2.5f;
|
||||
range = 140f;
|
||||
targetAir = false;
|
||||
|
||||
weapons.add(
|
||||
new Weapon(){{
|
||||
x = y = 0f;
|
||||
mirror = false;
|
||||
reload = 60f;
|
||||
minShootVelocity = 0.01f;
|
||||
|
||||
bullet = new BasicBulletType(){{
|
||||
sprite = "large-bomb";
|
||||
width = height = 120/4f;
|
||||
|
||||
range = 30f;
|
||||
ignoreRotation = true;
|
||||
|
||||
backColor = Pal.heal;
|
||||
frontColor = Color.white;
|
||||
mixColorTo = Color.white;
|
||||
|
||||
shootCone = 180f;
|
||||
ejectEffect = Fx.none;
|
||||
shootSound = Sounds.none;
|
||||
despawnShake = 4f;
|
||||
|
||||
collidesAir = false;
|
||||
|
||||
lifetime = 70f;
|
||||
|
||||
despawnEffect = Fx.greenBomb;
|
||||
hitEffect = Fx.massiveExplosion;
|
||||
keepVelocity = false;
|
||||
spin = 2f;
|
||||
|
||||
shrinkX = shrinkY = 0.7f;
|
||||
|
||||
speed = 0.001f;
|
||||
collides = false;
|
||||
|
||||
splashDamage = 240f;
|
||||
splashDamageRadius = 115f;
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
|
||||
oct = new UnitType("oct"){{
|
||||
armor = 16f;
|
||||
health = 24000;
|
||||
speed = 0.6f;
|
||||
rotateSpeed = 1f;
|
||||
accel = 0.04f;
|
||||
drag = 0.018f;
|
||||
flying = true;
|
||||
engineOffset = 46f;
|
||||
engineSize = 7.8f;
|
||||
rotateShooting = false;
|
||||
hitsize = 60f;
|
||||
payloadCapacity = (5.3f * 5.3f) * tilePayload;
|
||||
buildSpeed = 4f;
|
||||
drawShields = false;
|
||||
|
||||
abilities.add(new ForceFieldAbility(140f, 4f, 7000f, 60f * 8), new HealFieldAbility(130f, 60f * 2, 140f));
|
||||
}};
|
||||
|
||||
//endregion
|
||||
//region naval attack
|
||||
|
||||
@@ -1086,7 +1306,6 @@ public class UnitTypes implements ContentList{
|
||||
health = 280;
|
||||
accel = 0.4f;
|
||||
rotateSpeed = 3.3f;
|
||||
immunities = ObjectSet.with(StatusEffects.wet);
|
||||
trailLength = 20;
|
||||
rotateShooting = false;
|
||||
|
||||
@@ -1138,14 +1357,12 @@ public class UnitTypes implements ContentList{
|
||||
armor = 4f;
|
||||
accel = 0.3f;
|
||||
rotateSpeed = 2.6f;
|
||||
immunities = ObjectSet.with(StatusEffects.wet);
|
||||
rotateShooting = false;
|
||||
|
||||
trailLength = 20;
|
||||
trailX = 5.5f;
|
||||
trailY = -4f;
|
||||
trailScl = 1.9f;
|
||||
rotateShooting = false;
|
||||
|
||||
abilities.add(new StatusFieldAbility(StatusEffects.overclock, 60f * 6, 60f * 6f, 60f));
|
||||
|
||||
@@ -1180,8 +1397,7 @@ public class UnitTypes implements ContentList{
|
||||
rotateSpeed = 1.8f;
|
||||
drag = 0.17f;
|
||||
hitsize = 16f;
|
||||
armor = 6f;
|
||||
immunities = ObjectSet.with(StatusEffects.wet);
|
||||
armor = 7f;
|
||||
rotateShooting = false;
|
||||
|
||||
trailLength = 22;
|
||||
@@ -1189,7 +1405,7 @@ public class UnitTypes implements ContentList{
|
||||
trailY = -9f;
|
||||
trailScl = 1.5f;
|
||||
|
||||
abilities.add(new HealFieldAbility(22f, 60f * 4, 70f), new ShieldFieldAbility(20f, 40f, 60f * 4, 60f));
|
||||
abilities.add(new ShieldFieldAbility(20f, 40f, 60f * 4, 60f));
|
||||
|
||||
weapons.add(new Weapon("large-artillery"){{
|
||||
reload = 65f;
|
||||
@@ -1276,7 +1492,6 @@ public class UnitTypes implements ContentList{
|
||||
hitsize = 39f;
|
||||
accel = 0.2f;
|
||||
rotateSpeed = 1.3f;
|
||||
immunities = ObjectSet.with(StatusEffects.wet);
|
||||
rotateShooting = false;
|
||||
|
||||
trailLength = 50;
|
||||
@@ -1361,10 +1576,9 @@ public class UnitTypes implements ContentList{
|
||||
armor = 16f;
|
||||
accel = 0.19f;
|
||||
rotateSpeed = 0.9f;
|
||||
immunities = ObjectSet.with(StatusEffects.wet);
|
||||
rotateShooting = false;
|
||||
|
||||
float spawnTime = 60f * 25f;
|
||||
float spawnTime = 60f * 15f;
|
||||
|
||||
abilities.add(new UnitSpawnAbility(flare, spawnTime, 19.25f, -31.75f), new UnitSpawnAbility(flare, spawnTime, -19.25f, -31.75f));
|
||||
|
||||
@@ -1415,7 +1629,7 @@ public class UnitTypes implements ContentList{
|
||||
mineTier = 1;
|
||||
buildSpeed = 0.5f;
|
||||
drag = 0.05f;
|
||||
speed = 2.6f;
|
||||
speed = 2.8f;
|
||||
rotateSpeed = 15f;
|
||||
accel = 0.1f;
|
||||
itemCapacity = 30;
|
||||
@@ -1449,7 +1663,7 @@ public class UnitTypes implements ContentList{
|
||||
mineTier = 1;
|
||||
buildSpeed = 0.75f;
|
||||
drag = 0.05f;
|
||||
speed = 2.9f;
|
||||
speed = 3f;
|
||||
rotateSpeed = 17f;
|
||||
accel = 0.1f;
|
||||
itemCapacity = 50;
|
||||
@@ -1489,7 +1703,7 @@ public class UnitTypes implements ContentList{
|
||||
mineTier = 2;
|
||||
buildSpeed = 1f;
|
||||
drag = 0.05f;
|
||||
speed = 3.4f;
|
||||
speed = 3.5f;
|
||||
rotateSpeed = 19f;
|
||||
accel = 0.11f;
|
||||
itemCapacity = 70;
|
||||
|
||||
@@ -45,4 +45,12 @@ public class Version{
|
||||
build = Strings.canParseInt(map.get("build")) ? Integer.parseInt(map.get("build")) : -1;
|
||||
}
|
||||
}
|
||||
|
||||
/** get menu version without colors */
|
||||
public static String combined(){
|
||||
if(build == -1){
|
||||
return "custom build";
|
||||
}
|
||||
return (type.equals("official") ? modifier : type) + " build " + build + (revision == 0 ? "" : "." + revision);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ public abstract class UnlockableContent extends MappableContent{
|
||||
|
||||
this.localizedName = Core.bundle.get(getContentType() + "." + this.name + ".name", this.name);
|
||||
this.description = Core.bundle.getOrNull(getContentType() + "." + this.name + ".description");
|
||||
this.unlocked = Core.settings != null && Core.settings.getBool(name + "-unlocked", false);
|
||||
this.unlocked = Core.settings != null && Core.settings.getBool(this.name + "-unlocked", false);
|
||||
}
|
||||
|
||||
public String displayDescription(){
|
||||
|
||||
@@ -77,19 +77,24 @@ public class Damage{
|
||||
}
|
||||
}
|
||||
|
||||
/** Collides a bullet with blocks in a laser, taking into account absorption blocks. Resulting length is stored in the bullet's fdata. */
|
||||
public static float collideLaser(Bullet b, float length){
|
||||
public static float findLaserLength(Bullet b, float length){
|
||||
Tmp.v1.trns(b.rotation(), length);
|
||||
|
||||
furthest = null;
|
||||
|
||||
world.raycast(b.tileX(), b.tileY(), world.toTile(b.x + Tmp.v1.x), world.toTile(b.y + Tmp.v1.y),
|
||||
boolean found = world.raycast(b.tileX(), b.tileY(), world.toTile(b.x + Tmp.v1.x), world.toTile(b.y + Tmp.v1.y),
|
||||
(x, y) -> (furthest = world.tile(x, y)) != null && furthest.team() != b.team && furthest.block().absorbLasers);
|
||||
|
||||
float resultLength = furthest != null ? Math.max(6f, b.dst(furthest.worldx(), furthest.worldy())) : length;
|
||||
return found && furthest != null ? Math.max(6f, b.dst(furthest.worldx(), furthest.worldy())) : length;
|
||||
}
|
||||
|
||||
Damage.collideLine(b, b.team, b.type.hitEffect, b.x, b.y, b.rotation(), resultLength);
|
||||
b.fdata = furthest != null ? resultLength : length;
|
||||
/** Collides a bullet with blocks in a laser, taking into account absorption blocks. Resulting length is stored in the bullet's fdata. */
|
||||
public static float collideLaser(Bullet b, float length, boolean large){
|
||||
float resultLength = findLaserLength(b, length);
|
||||
|
||||
collideLine(b, b.team, b.type.hitEffect, b.x, b.y, b.rotation(), resultLength, large);
|
||||
|
||||
b.fdata = resultLength;
|
||||
|
||||
return resultLength;
|
||||
}
|
||||
@@ -103,6 +108,8 @@ public class Damage{
|
||||
* Only enemies of the specified team are damaged.
|
||||
*/
|
||||
public static void collideLine(Bullet hitter, Team team, Effect effect, float x, float y, float angle, float length, boolean large){
|
||||
length = findLaserLength(hitter, length);
|
||||
|
||||
collidedBlocks.clear();
|
||||
tr.trns(angle, length);
|
||||
Intc2 collider = (cx, cy) -> {
|
||||
@@ -150,13 +157,8 @@ public class Damage{
|
||||
if(!e.checkTarget(hitter.type.collidesAir, hitter.type.collidesGround)) return;
|
||||
|
||||
e.hitbox(hitrect);
|
||||
Rect other = hitrect;
|
||||
other.y -= expand;
|
||||
other.x -= expand;
|
||||
other.width += expand * 2;
|
||||
other.height += expand * 2;
|
||||
|
||||
Vec2 vec = Geometry.raycastRect(x, y, x2, y2, other);
|
||||
Vec2 vec = Geometry.raycastRect(x, y, x2, y2, hitrect.grow(expand * 2));
|
||||
|
||||
if(vec != null){
|
||||
effect.at(vec.x, vec.y);
|
||||
|
||||
@@ -40,7 +40,7 @@ public class Effect{
|
||||
}
|
||||
|
||||
public Effect(float life, Cons<EffectContainer> renderer){
|
||||
this(life, 32f, renderer);
|
||||
this(life,50f, renderer);
|
||||
}
|
||||
|
||||
public Effect ground(){
|
||||
|
||||
@@ -24,7 +24,7 @@ public class Units{
|
||||
if(unit != null){
|
||||
unit.dead = true;
|
||||
Fx.unitCapKill.at(unit);
|
||||
Core.app.post(() -> Call.unitDeath(unit.id));
|
||||
Core.app.post(() -> Call.unitDestroy(unit.id));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,21 @@ public class Units{
|
||||
}
|
||||
}
|
||||
|
||||
//destroys immediately
|
||||
@Remote(called = Loc.server)
|
||||
public static void unitDestroy(int uid){
|
||||
Unit unit = Groups.unit.getByID(uid);
|
||||
|
||||
//if there's no unit don't add it later and get it stuck as a ghost
|
||||
if(netClient != null){
|
||||
netClient.addRemovedEntity(uid);
|
||||
}
|
||||
|
||||
if(unit != null){
|
||||
unit.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
@Remote(called = Loc.server)
|
||||
public static void unitDespawn(Unit unit){
|
||||
Fx.unitDespawn.at(unit.x, unit.y, 0, unit);
|
||||
|
||||
@@ -21,8 +21,8 @@ public class ForceFieldAbility extends Ability{
|
||||
/** Cooldown after the shield is broken, in ticks. */
|
||||
public float cooldown = 60f * 5;
|
||||
|
||||
/** State: radius scaling. */
|
||||
protected float radiusScale;
|
||||
/** State. */
|
||||
protected float radiusScale, alpha;
|
||||
|
||||
private static float realRad;
|
||||
private static Unit paramUnit;
|
||||
@@ -40,7 +40,7 @@ public class ForceFieldAbility extends Ability{
|
||||
}
|
||||
|
||||
paramUnit.shield -= trait.damage();
|
||||
paramUnit.shieldAlpha = 1f;
|
||||
paramField.alpha = 1f;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -59,6 +59,8 @@ public class ForceFieldAbility extends Ability{
|
||||
unit.shield += Time.delta * regen;
|
||||
}
|
||||
|
||||
alpha = Math.max(alpha - Time.delta/10f, 0f);
|
||||
|
||||
if(unit.shield > 0){
|
||||
radiusScale = Mathf.lerpDelta(radiusScale, 1f, 0.06f);
|
||||
paramUnit = unit;
|
||||
@@ -78,7 +80,7 @@ public class ForceFieldAbility extends Ability{
|
||||
if(unit.shield > 0){
|
||||
Draw.z(Layer.shields);
|
||||
|
||||
Draw.color(unit.team.color, Color.white, Mathf.clamp(unit.shieldAlpha));
|
||||
Draw.color(unit.team.color, Color.white, Mathf.clamp(alpha));
|
||||
|
||||
if(Core.settings.getBool("animatedshields")){
|
||||
Fill.poly(unit.x, unit.y, 6, realRad);
|
||||
|
||||
@@ -8,7 +8,7 @@ import mindustry.gen.*;
|
||||
public class HealFieldAbility extends Ability{
|
||||
public float amount = 1, reload = 100, range = 60;
|
||||
public Effect healEffect = Fx.heal;
|
||||
public Effect activeEffect = Fx.healWave;
|
||||
public Effect activeEffect = Fx.healWaveDynamic;
|
||||
|
||||
protected float timer;
|
||||
protected boolean wasHealed = false;
|
||||
@@ -37,7 +37,7 @@ public class HealFieldAbility extends Ability{
|
||||
});
|
||||
|
||||
if(wasHealed){
|
||||
activeEffect.at(unit);
|
||||
activeEffect.at(unit, range);
|
||||
}
|
||||
|
||||
timer = 0f;
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
package mindustry.entities.bullet;
|
||||
|
||||
import arc.Core;
|
||||
import arc.graphics.Color;
|
||||
import arc.graphics.g2d.Draw;
|
||||
import arc.graphics.g2d.TextureRegion;
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.Pal;
|
||||
import mindustry.graphics.*;
|
||||
|
||||
/** An extended BulletType for most ammo-based bullets shot from turrets and units. */
|
||||
public class BasicBulletType extends BulletType{
|
||||
public Color backColor = Pal.bulletYellowBack, frontColor = Pal.bulletYellow;
|
||||
public Color mixColorFrom = new Color(1f, 1f, 1f, 0f), mixColorTo = new Color(1f, 1f, 1f, 0f);
|
||||
public float width = 5f, height = 7f;
|
||||
public float shrinkX = 0f, shrinkY = 0.5f;
|
||||
public float spin = 0;
|
||||
@@ -45,10 +46,15 @@ public class BasicBulletType extends BulletType{
|
||||
float width = this.width * ((1f - shrinkX) + shrinkX * b.fout());
|
||||
float offset = -90 + (spin != 0 ? Mathf.randomSeed(b.id, 360f) + b.time * spin : 0f);
|
||||
|
||||
Color mix = Tmp.c1.set(mixColorFrom).lerp(mixColorTo, b.fin());
|
||||
|
||||
Draw.mixcol(mix, mix.a);
|
||||
|
||||
Draw.color(backColor);
|
||||
Draw.rect(backRegion, b.x, b.y, width, height, b.rotation() + offset);
|
||||
Draw.color(frontColor);
|
||||
Draw.rect(frontRegion, b.x, b.y, width, height, b.rotation() + offset);
|
||||
Draw.color();
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +71,8 @@ public abstract class BulletType extends Content{
|
||||
public boolean hittable = true;
|
||||
/** Whether this bullet can be reflected. */
|
||||
public boolean reflectable = true;
|
||||
/** Bullet range override. */
|
||||
public float range = -1f;
|
||||
|
||||
//additional effects
|
||||
|
||||
@@ -96,13 +98,13 @@ public abstract class BulletType extends Content{
|
||||
|
||||
public Color lightningColor = Pal.surge;
|
||||
public int lightning;
|
||||
public int lightningLength = 5;
|
||||
public int lightningLength = 5, lightningLengthRand = 0;
|
||||
/** Use a negative value to use default bullet damage. */
|
||||
public float lightningDamage = -1;
|
||||
|
||||
public float weaveScale = 1f;
|
||||
public float weaveMag = -1f;
|
||||
public float hitShake = 0f;
|
||||
public float hitShake = 0f, despawnShake = 0f;
|
||||
|
||||
public int puddles;
|
||||
public float puddleRange;
|
||||
@@ -126,7 +128,7 @@ public abstract class BulletType extends Content{
|
||||
|
||||
/** Returns maximum distance the bullet this bullet type has can travel. */
|
||||
public float range(){
|
||||
return speed * lifetime * (1f - drag);
|
||||
return Math.max(speed * lifetime * (1f - drag), range);
|
||||
}
|
||||
|
||||
public boolean collides(Bullet bullet, Building tile){
|
||||
@@ -179,7 +181,7 @@ public abstract class BulletType extends Content{
|
||||
}
|
||||
|
||||
for(int i = 0; i < lightning; i++){
|
||||
Lightning.create(b, lightningColor, lightningDamage < 0 ? damage : lightningDamage, b.x, b.y, Mathf.random(360f), lightningLength);
|
||||
Lightning.create(b, lightningColor, lightningDamage < 0 ? damage : lightningDamage, b.x, b.y, Mathf.random(360f), lightningLength + Mathf.random(lightningLengthRand));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,6 +189,8 @@ public abstract class BulletType extends Content{
|
||||
despawnEffect.at(b.x, b.y, b.rotation(), hitColor);
|
||||
hitSound.at(b);
|
||||
|
||||
Effect.shake(despawnShake, despawnShake, b);
|
||||
|
||||
if(fragBullet != null || splashDamageRadius > 0 || lightning > 0){
|
||||
hit(b);
|
||||
}
|
||||
@@ -275,9 +279,8 @@ public abstract class BulletType extends Content{
|
||||
bullet.damage = damage < 0 ? this.damage : damage;
|
||||
bullet.add();
|
||||
|
||||
if(keepVelocity && owner instanceof Hitboxc) bullet.vel.add(((Hitboxc)owner).deltaX() / Time.delta, ((Hitboxc)owner).deltaY() / Time.delta);
|
||||
if(keepVelocity && owner instanceof Velc) bullet.vel.add(((Velc)owner).vel().x, ((Velc)owner).vel().y);
|
||||
return bullet;
|
||||
|
||||
}
|
||||
|
||||
public void createNet(Team team, float x, float y, float angle, float damage, float velocityScl, float lifetimeScl){
|
||||
|
||||
@@ -12,10 +12,13 @@ import mindustry.graphics.*;
|
||||
public class ContinuousLaserBulletType extends BulletType{
|
||||
public float length = 220f;
|
||||
public float shake = 1f;
|
||||
public float fadeTime = 16f;
|
||||
public Color[] colors = {Color.valueOf("ec745855"), Color.valueOf("ec7458aa"), Color.valueOf("ff9c5a"), Color.white};
|
||||
public float[] tscales = {1f, 0.7f, 0.5f, 0.2f};
|
||||
public float[] strokes = {2f, 1.5f, 1f, 0.3f};
|
||||
public float[] lenscales = {1f, 1.12f, 1.15f, 1.17f};
|
||||
public float width = 9f, oscScl = 0.8f, oscMag = 1.5f;
|
||||
public boolean largeHit = true;
|
||||
|
||||
public ContinuousLaserBulletType(float damage){
|
||||
super(0.001f, damage);
|
||||
@@ -25,6 +28,7 @@ public class ContinuousLaserBulletType extends BulletType{
|
||||
hitSize = 4;
|
||||
drawSize = 420f;
|
||||
lifetime = 16f;
|
||||
keepVelocity = false;
|
||||
pierce = true;
|
||||
hittable = false;
|
||||
hitColor = colors[2];
|
||||
@@ -32,6 +36,7 @@ public class ContinuousLaserBulletType extends BulletType{
|
||||
incendAmount = 1;
|
||||
incendSpread = 5;
|
||||
incendChance = 0.4f;
|
||||
lightColor = Color.orange;
|
||||
}
|
||||
|
||||
protected ContinuousLaserBulletType(){
|
||||
@@ -43,13 +48,20 @@ public class ContinuousLaserBulletType extends BulletType{
|
||||
return length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
super.init();
|
||||
|
||||
drawSize = Math.max(drawSize, length*2f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Bullet b){
|
||||
//TODO possible laser absorption from blocks
|
||||
|
||||
//damage every 5 ticks
|
||||
if(b.timer(1, 5f)){
|
||||
Damage.collideLine(b, b.team, hitEffect, b.x, b.y, b.rotation(), length, true);
|
||||
Damage.collideLine(b, b.team, hitEffect, b.x, b.y, b.rotation(), length, largeHit);
|
||||
}
|
||||
|
||||
if(shake > 0){
|
||||
@@ -59,21 +71,23 @@ public class ContinuousLaserBulletType extends BulletType{
|
||||
|
||||
@Override
|
||||
public void draw(Bullet b){
|
||||
float baseLen = length * b.fout();
|
||||
float realLength = Damage.findLaserLength(b, length);
|
||||
float fout = Mathf.clamp(b.time > b.lifetime - fadeTime ? 1f - (b.time - (lifetime - fadeTime)) / fadeTime : 1f);
|
||||
float baseLen = realLength * fout;
|
||||
|
||||
Lines.lineAngle(b.x, b.y, b.rotation(), baseLen);
|
||||
for(int s = 0; s < colors.length; s++){
|
||||
Draw.color(Tmp.c1.set(colors[s]).mul(1f + Mathf.absin(Time.time(), 1f, 0.1f)));
|
||||
for(int i = 0; i < tscales.length; i++){
|
||||
Tmp.v1.trns(b.rotation() + 180f, (lenscales[i] - 1f) * 35f);
|
||||
Lines.stroke((9f + Mathf.absin(Time.time(), 0.8f, 1.5f)) * b.fout() * strokes[s] * tscales[i]);
|
||||
Lines.stroke((width + Mathf.absin(Time.time(), oscScl, oscMag)) * fout * strokes[s] * tscales[i]);
|
||||
Lines.lineAngle(b.x + Tmp.v1.x, b.y + Tmp.v1.y, b.rotation(), baseLen * lenscales[i], false);
|
||||
}
|
||||
}
|
||||
|
||||
Tmp.v1.trns(b.rotation(), baseLen * 1.1f);
|
||||
|
||||
Drawf.light(b.team, b.x, b.y, b.x + Tmp.v1.x, b.y + Tmp.v1.y, 40, Color.orange, 0.7f);
|
||||
Drawf.light(b.team, b.x, b.y, b.x + Tmp.v1.x, b.y + Tmp.v1.y, 40, lightColor, 0.7f);
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
|
||||
@@ -8,18 +8,17 @@ import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
public class LaserBulletType extends BulletType{
|
||||
protected static Tile furthest;
|
||||
|
||||
protected Color[] colors = {Pal.lancerLaser.cpy().mul(1f, 1f, 1f, 0.4f), Pal.lancerLaser, Color.white};
|
||||
protected Effect laserEffect = Fx.lancerLaserShootSmoke;
|
||||
protected float length = 160f;
|
||||
protected float width = 15f;
|
||||
protected float lengthFalloff = 0.5f;
|
||||
protected float sideLength = 29f, sideWidth = 0.7f;
|
||||
protected float sideAngle = 90f;
|
||||
public Color[] colors = {Pal.lancerLaser.cpy().mul(1f, 1f, 1f, 0.4f), Pal.lancerLaser, Color.white};
|
||||
public Effect laserEffect = Fx.lancerLaserShootSmoke;
|
||||
public float length = 160f;
|
||||
public float width = 15f;
|
||||
public float lengthFalloff = 0.5f;
|
||||
public float sideLength = 29f, sideWidth = 0.7f;
|
||||
public float sideAngle = 90f;
|
||||
public float lightningSpacing = -1, lightningDelay = 0.1f, lightningAngleRand;
|
||||
public boolean largeHit = false;
|
||||
|
||||
public LaserBulletType(float damage){
|
||||
super(0.01f, damage);
|
||||
@@ -40,6 +39,13 @@ public class LaserBulletType extends BulletType{
|
||||
this(1f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
super.init();
|
||||
|
||||
drawSize = Math.max(drawSize, length*2f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float range(){
|
||||
return length;
|
||||
@@ -47,8 +53,30 @@ public class LaserBulletType extends BulletType{
|
||||
|
||||
@Override
|
||||
public void init(Bullet b){
|
||||
float resultLength = Damage.collideLaser(b, length);
|
||||
laserEffect.at(b.x, b.y, b.rotation(), resultLength * 0.75f);
|
||||
float resultLength = Damage.collideLaser(b, length, largeHit), rot = b.rotation();
|
||||
|
||||
laserEffect.at(b.x, b.y, rot, resultLength * 0.75f);
|
||||
|
||||
if(lightningSpacing > 0){
|
||||
int idx = 0;
|
||||
for(float i = 0; i <= resultLength; i += lightningSpacing){
|
||||
float cx = b.x + Angles.trnsx(rot, i),
|
||||
cy = b.y + Angles.trnsy(rot, i);
|
||||
|
||||
int f = idx++;
|
||||
|
||||
for(int s : Mathf.signs){
|
||||
Time.run(f * lightningDelay, () -> {
|
||||
if(b.isAdded() && b.type == this){
|
||||
Lightning.create(b, lightningColor,
|
||||
lightningDamage < 0 ? damage : lightningDamage,
|
||||
cx, cy, rot + 90*s + Mathf.range(lightningAngleRand),
|
||||
lightningLength + Mathf.random(lightningLengthRand));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -4,6 +4,12 @@ import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
//TODO this class is bad for multiple reasons, remove/replace it.
|
||||
//- effects unreliable
|
||||
//- not really hitscan but works like it
|
||||
//- buggy trails
|
||||
//- looks bad
|
||||
//- generally unreliable
|
||||
public class RailBulletType extends BulletType{
|
||||
public Effect pierceEffect = Fx.hitBulletSmall, updateEffect = Fx.none;
|
||||
/** Multiplier of damage decreased per health pierced. */
|
||||
@@ -37,7 +43,9 @@ public class RailBulletType extends BulletType{
|
||||
|
||||
@Override
|
||||
public void update(Bullet b){
|
||||
updateEffect.at(b.x, b.y, b.rotation());
|
||||
if(b.timer(1, 0.9f)){
|
||||
updateEffect.at(b.x, b.y, b.rotation());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -13,6 +13,7 @@ public class ShrapnelBulletType extends BulletType{
|
||||
public float length = 100f;
|
||||
public float width = 20f;
|
||||
public Color fromColor = Color.white, toColor = Pal.lancerLaser;
|
||||
public boolean hitLarge = false;
|
||||
|
||||
public int serrations = 7;
|
||||
public float serrationLenScl = 10f, serrationWidth = 4f, serrationSpacing = 8f, serrationSpaceOffset = 80f, serrationFadeOffset = 0.5f;
|
||||
@@ -30,7 +31,14 @@ public class ShrapnelBulletType extends BulletType{
|
||||
|
||||
@Override
|
||||
public void init(Bullet b){
|
||||
Damage.collideLaser(b, length);
|
||||
Damage.collideLaser(b, length, hitLarge);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
super.init();
|
||||
|
||||
drawSize = Math.max(drawSize, length*2f);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,23 +1,18 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.EntityCollisions.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@Component
|
||||
abstract class ElevationMoveComp implements Velc, Posc, Flyingc, Hitboxc{
|
||||
@Import float x, y;
|
||||
|
||||
@Replace
|
||||
@Override
|
||||
public void move(float cx, float cy){
|
||||
if(isFlying()){
|
||||
x += cx;
|
||||
y += cy;
|
||||
}else{
|
||||
collisions.move(this, cx, cy);
|
||||
}
|
||||
public SolidPred solidity(){
|
||||
return isFlying() ? null : EntityCollisions::solid;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,16 +4,16 @@ import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.ai.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.EntityCollisions.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@Component
|
||||
abstract class LegsComp implements Posc, Rotc, Hitboxc, Flyingc, Unitc{
|
||||
@Import float x, y;
|
||||
@@ -26,8 +26,14 @@ abstract class LegsComp implements Posc, Rotc, Hitboxc, Flyingc, Unitc{
|
||||
|
||||
@Replace
|
||||
@Override
|
||||
public void move(float cx, float cy){
|
||||
collisions.moveCheck(this, cx, cy, !type.allowLegStep ? EntityCollisions::solid : EntityCollisions::legsSolid);
|
||||
public SolidPred solidity(){
|
||||
return !type.allowLegStep ? EntityCollisions::solid : EntityCollisions::legsSolid;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Replace
|
||||
public int pathType(){
|
||||
return Pathfinder.costLegs;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
@@ -9,11 +10,24 @@ import mindustry.gen.*;
|
||||
abstract class MechComp implements Posc, Flyingc, Hitboxc, Unitc, Mechc, ElevationMovec{
|
||||
@SyncField(false) @SyncLocal float baseRotation;
|
||||
transient float walkTime, walkExtension;
|
||||
transient private boolean walked;
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
float len = deltaLen();
|
||||
baseRotation = Angles.moveToward(baseRotation, deltaAngle(), type().baseRotateSpeed * Mathf.clamp(len / type().speed / Time.delta) * Time.delta);
|
||||
walkTime += len;
|
||||
//trigger animation only when walking manually
|
||||
if(walked){
|
||||
float len = deltaLen();
|
||||
baseRotation = Angles.moveToward(baseRotation, deltaAngle(), type().baseRotateSpeed * Mathf.clamp(len / type().speed / Time.delta) * Time.delta);
|
||||
walkTime += len;
|
||||
walked = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveAt(Vec2 vector, float acceleration){
|
||||
if(!vector.isZero()){
|
||||
//mark walking state when moving in a controlled manner
|
||||
walked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,15 +25,15 @@ abstract class PayloadComp implements Posc, Rotc, Hitboxc, Unitc{
|
||||
}
|
||||
|
||||
boolean canPickup(Unit unit){
|
||||
return payloadUsed() + unit.hitSize * unit.hitSize <= type.payloadCapacity;
|
||||
return payloadUsed() + unit.hitSize * unit.hitSize <= type.payloadCapacity + 0.001f;
|
||||
}
|
||||
|
||||
boolean canPickup(Building build){
|
||||
return payloadUsed() + build.block.size * build.block.size * Vars.tilesize * Vars.tilesize <= type.payloadCapacity;
|
||||
return payloadUsed() + build.block.size * build.block.size * Vars.tilesize * Vars.tilesize <= type.payloadCapacity + 0.001f;
|
||||
}
|
||||
|
||||
boolean canPickupPayload(Payload pay){
|
||||
return payloadUsed() + pay.size()*pay.size() <= type.payloadCapacity;
|
||||
return payloadUsed() + pay.size()*pay.size() <= type.payloadCapacity + 0.001f;
|
||||
}
|
||||
|
||||
boolean hasPayload(){
|
||||
@@ -98,7 +98,7 @@ abstract class PayloadComp implements Posc, Rotc, Hitboxc, Unitc{
|
||||
Unit u = payload.unit;
|
||||
|
||||
//can't drop ground units
|
||||
if(((tileOn() == null || tileOn().solid()) && u.elevation < 0.1f) || (!floorOn().isLiquid && u instanceof WaterMovec)){
|
||||
if(!u.canPass(tileX(), tileY())){
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import arc.util.*;
|
||||
import mindustry.ai.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.ctype.*;
|
||||
@@ -37,7 +38,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
|
||||
private UnitController controller;
|
||||
private UnitType type;
|
||||
boolean spawnedByCore, deactivated; //TODO remove deactivation boolean
|
||||
boolean spawnedByCore;
|
||||
|
||||
transient Seq<Ability> abilities = new Seq<>(0);
|
||||
|
||||
@@ -69,7 +70,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
|
||||
@Replace
|
||||
public float clipSize(){
|
||||
return type.region.width * 2f;
|
||||
return Math.max(type.region.width * 2f, type.clipSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -103,7 +104,14 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
@Override
|
||||
@Replace
|
||||
public boolean canDrown(){
|
||||
return isGrounded() && !hovering && type.canDrown && !(this instanceof WaterMovec);
|
||||
return isGrounded() && !hovering && type.canDrown;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Replace
|
||||
public boolean canShoot(){
|
||||
//cannot shoot while boosting
|
||||
return !(type.canBoost && isFlying());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -149,8 +157,13 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
return type;
|
||||
}
|
||||
|
||||
/** @return pathfinder path type for calculating costs */
|
||||
public int pathType(){
|
||||
return Pathfinder.costGround;
|
||||
}
|
||||
|
||||
public void lookAt(float angle){
|
||||
rotation = Angles.moveToward(rotation, angle, type.rotateSpeed * Time.delta);
|
||||
rotation = Angles.moveToward(rotation, angle, type.rotateSpeed * Time.delta * speedMultiplier());
|
||||
}
|
||||
|
||||
public void lookAt(Position pos){
|
||||
@@ -206,12 +219,9 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
public void add(){
|
||||
|
||||
//check if over unit cap
|
||||
if(count() > cap() && !spawnedByCore){
|
||||
deactivated = true;
|
||||
|
||||
if(!dead){
|
||||
Call.unitCapDeath(self());
|
||||
}
|
||||
if(count() > cap() && !spawnedByCore && !dead){
|
||||
Call.unitCapDeath(self());
|
||||
teamIndex.updateCount(team, type, -1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -232,22 +242,13 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
//activate the unit when possible
|
||||
if(!net.client() && deactivated && teamIndex.countActive(team, type) < Units.getCap(team)){
|
||||
teamIndex.updateActiveCount(team, type, 1);
|
||||
deactivated = false;
|
||||
}
|
||||
|
||||
if(!deactivated){
|
||||
type.update(self());
|
||||
type.update(self());
|
||||
|
||||
if(abilities.size > 0){
|
||||
for(Ability a : abilities){
|
||||
a.update(self());
|
||||
}
|
||||
if(abilities.size > 0){
|
||||
for(Ability a : abilities){
|
||||
a.update(self());
|
||||
}
|
||||
}else if(!dead){
|
||||
Call.unitCapDeath(self());
|
||||
}
|
||||
|
||||
drag = type.drag * (isGrounded() ? (floorOn().dragMultiplier) : 1f);
|
||||
@@ -305,18 +306,20 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
if(floor.damageTaken > 0f){
|
||||
damageContinuous(floor.damageTaken);
|
||||
}
|
||||
}
|
||||
|
||||
if(tile.solid()){
|
||||
if(type.canBoost){
|
||||
elevation = 1f;
|
||||
}else if(!net.client()){
|
||||
kill();
|
||||
}
|
||||
//kill entities on tiles that are solid to them
|
||||
if(tile != null && !canPassOn()){
|
||||
//boost if possible
|
||||
if(type.canBoost){
|
||||
elevation = 1f;
|
||||
}else if(!net.client()){
|
||||
kill();
|
||||
}
|
||||
}
|
||||
|
||||
//AI only updates on the server
|
||||
if(!net.client() && !dead && !deactivated){
|
||||
if(!net.client() && !dead){
|
||||
controller.updateUnit();
|
||||
}
|
||||
|
||||
@@ -325,11 +328,6 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
resetController();
|
||||
}
|
||||
|
||||
//do not control anything when deactivated
|
||||
if(deactivated){
|
||||
controlWeapons(false, false);
|
||||
}
|
||||
|
||||
//remove units spawned by the core
|
||||
if(spawnedByCore && !isPlayer()){
|
||||
Call.unitDespawn(self());
|
||||
@@ -362,7 +360,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
|
||||
//if this unit crash landed (was flying), damage stuff in a radius
|
||||
if(type.flying){
|
||||
Damage.damage(team,x, y, hitSize * 1.1f, hitSize * type.crashDamageMultiplier, true, false, true);
|
||||
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){
|
||||
|
||||
@@ -3,9 +3,13 @@ package mindustry.entities.comp;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.entities.EntityCollisions.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@Component
|
||||
abstract class VelComp implements Posc{
|
||||
@Import float x, y;
|
||||
@@ -22,12 +26,35 @@ abstract class VelComp implements Posc{
|
||||
vel.scl(Mathf.clamp(1f - drag * Time.delta));
|
||||
}
|
||||
|
||||
/** @return function to use for check solid state. if null, no checking is done. */
|
||||
@Nullable
|
||||
SolidPred solidity(){
|
||||
return null;
|
||||
}
|
||||
|
||||
/** @return whether this entity can move through a location*/
|
||||
boolean canPass(int tileX, int tileY){
|
||||
SolidPred s = solidity();
|
||||
return s == null || !s.solid(tileX, tileY);
|
||||
}
|
||||
|
||||
/** @return whether this entity can exist on its current location*/
|
||||
boolean canPassOn(){
|
||||
return canPass(tileX(), tileY());
|
||||
}
|
||||
|
||||
boolean moving(){
|
||||
return !vel.isZero(0.01f);
|
||||
}
|
||||
|
||||
void move(float cx, float cy){
|
||||
x += cx;
|
||||
y += cy;
|
||||
SolidPred check = solidity();
|
||||
|
||||
if(check != null){
|
||||
collisions.move(self(), cx, cy, check);
|
||||
}else{
|
||||
x += cx;
|
||||
y += cy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,17 +4,17 @@ import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import mindustry.ai.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.EntityCollisions.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
//just a proof of concept
|
||||
@Component
|
||||
abstract class WaterMoveComp implements Posc, Velc, Hitboxc, Flyingc, Unitc{
|
||||
@@ -38,16 +38,8 @@ abstract class WaterMoveComp implements Posc, Velc, Hitboxc, Flyingc, Unitc{
|
||||
|
||||
@Override
|
||||
@Replace
|
||||
public void lookAt(float angle){
|
||||
if(onLiquid()){
|
||||
rotation = Angles.moveToward(rotation, angle, type.rotateSpeed * Time.delta);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@Replace
|
||||
public boolean canShoot(){
|
||||
return onLiquid();
|
||||
public int pathType(){
|
||||
return Pathfinder.costWater;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -74,13 +66,8 @@ abstract class WaterMoveComp implements Posc, Velc, Hitboxc, Flyingc, Unitc{
|
||||
|
||||
@Replace
|
||||
@Override
|
||||
public void move(float cx, float cy){
|
||||
if(isGrounded()){
|
||||
collisions.moveCheck(this, cx, cy, EntityCollisions::waterSolid);
|
||||
}else{
|
||||
x += cx;
|
||||
y += cy;
|
||||
}
|
||||
public SolidPred solidity(){
|
||||
return isFlying() ? null : EntityCollisions::waterSolid;
|
||||
}
|
||||
|
||||
@Replace
|
||||
|
||||
@@ -13,7 +13,7 @@ import mindustry.type.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@Component
|
||||
abstract class WeaponsComp implements Teamc, Posc, Rotc, Velc{
|
||||
abstract class WeaponsComp implements Teamc, Posc, Rotc, Velc, Statusc{
|
||||
@Import float x, y, rotation, reloadMultiplier;
|
||||
@Import Vec2 vel;
|
||||
@Import UnitType type;
|
||||
@@ -85,6 +85,16 @@ abstract class WeaponsComp implements Teamc, Posc, Rotc, Velc{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(){
|
||||
for(WeaponMount mount : mounts){
|
||||
if(mount.bullet != null){
|
||||
mount.bullet.time = mount.bullet.lifetime - 10f;
|
||||
mount.bullet = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Update shooting and rotation for this unit. */
|
||||
@Override
|
||||
public void update(){
|
||||
@@ -93,7 +103,27 @@ abstract class WeaponsComp implements Teamc, Posc, Rotc, Velc{
|
||||
for(WeaponMount mount : mounts){
|
||||
Weapon weapon = mount.weapon;
|
||||
mount.reload = Math.max(mount.reload - Time.delta * reloadMultiplier, 0);
|
||||
mount.heat = Math.max(mount.heat - Time.delta * reloadMultiplier / mount.weapon.cooldownTime, 0);
|
||||
|
||||
float weaponRotation = this.rotation - 90 + (weapon.rotate ? mount.rotation : 0);
|
||||
float mountX = this.x + Angles.trnsx(this.rotation - 90, weapon.x, weapon.y),
|
||||
mountY = this.y + Angles.trnsy(this.rotation - 90, weapon.x, weapon.y);
|
||||
float shootX = mountX + Angles.trnsx(weaponRotation, weapon.shootX, weapon.shootY),
|
||||
shootY = mountY + Angles.trnsy(weaponRotation, weapon.shootX, weapon.shootY);
|
||||
float shootAngle = weapon.rotate ? weaponRotation + 90 : Angles.angle(shootX, shootY, mount.aimX, mount.aimY) + (this.rotation - angleTo(mount.aimX, mount.aimY));
|
||||
|
||||
//update continuous state
|
||||
if(weapon.continuous && mount.bullet != null){
|
||||
if(!mount.bullet.isAdded() || mount.bullet.time >= mount.bullet.lifetime){
|
||||
mount.bullet = null;
|
||||
}else{
|
||||
mount.bullet.rotation(weaponRotation + 90);
|
||||
mount.bullet.set(shootX, shootY);
|
||||
vel.add(Tmp.v1.trns(rotation + 180f, mount.bullet.type.recoil));
|
||||
}
|
||||
}else{
|
||||
//heat decreases when not firing
|
||||
mount.heat = Math.max(mount.heat - Time.delta * reloadMultiplier / mount.weapon.cooldownTime, 0);
|
||||
}
|
||||
|
||||
//flip weapon shoot side for alternating weapons at half reload
|
||||
if(weapon.otherSide != -1 && weapon.alternate && mount.side == weapon.flipSprite &&
|
||||
@@ -104,10 +134,10 @@ abstract class WeaponsComp implements Teamc, Posc, Rotc, Velc{
|
||||
|
||||
//rotate if applicable
|
||||
if(weapon.rotate && (mount.rotate || mount.shoot) && can){
|
||||
float axisX = this.x + Angles.trnsx(rotation - 90, weapon.x, weapon.y),
|
||||
axisY = this.y + Angles.trnsy(rotation - 90, weapon.x, weapon.y);
|
||||
float axisX = this.x + Angles.trnsx(this.rotation - 90, weapon.x, weapon.y),
|
||||
axisY = this.y + Angles.trnsy(this.rotation - 90, weapon.x, weapon.y);
|
||||
|
||||
mount.targetRotation = Angles.angle(axisX, axisY, mount.aimX, mount.aimY) - rotation;
|
||||
mount.targetRotation = Angles.angle(axisX, axisY, mount.aimX, mount.aimY) - this.rotation;
|
||||
mount.rotation = Angles.moveToward(mount.rotation, mount.targetRotation, weapon.rotateSpeed * Time.delta);
|
||||
}else if(!weapon.rotate){
|
||||
mount.rotation = 0;
|
||||
@@ -124,21 +154,9 @@ abstract class WeaponsComp implements Teamc, Posc, Rotc, Velc{
|
||||
mount.reload <= 0.0001f && //reload has to be 0
|
||||
Angles.within(weapon.rotate ? mount.rotation : this.rotation, mount.targetRotation, mount.weapon.shootCone) //has to be within the cone
|
||||
){
|
||||
|
||||
float rotation = this.rotation - 90;
|
||||
float weaponRotation = rotation + (weapon.rotate ? mount.rotation : 0);
|
||||
|
||||
|
||||
float mountX = this.x + Angles.trnsx(rotation, weapon.x, weapon.y),
|
||||
mountY = this.y + Angles.trnsy(rotation, weapon.x, weapon.y);
|
||||
float shootX = mountX + Angles.trnsx(weaponRotation, weapon.shootX, weapon.shootY),
|
||||
shootY = mountY + Angles.trnsy(weaponRotation, weapon.shootX, weapon.shootY);
|
||||
float shootAngle = weapon.rotate ? weaponRotation + 90 : Angles.angle(shootX, shootY, mount.aimX, mount.aimY) + (this.rotation - angleTo(mount.aimX, mount.aimY));
|
||||
|
||||
shoot(weapon, shootX, shootY, mount.aimX, mount.aimY, shootAngle, Mathf.sign(weapon.x));
|
||||
shoot(mount, shootX, shootY, mount.aimX, mount.aimY, shootAngle, Mathf.sign(weapon.x));
|
||||
|
||||
mount.reload = weapon.reload;
|
||||
mount.heat = 1f;
|
||||
|
||||
ammo--;
|
||||
if(ammo < 0) ammo = 0;
|
||||
@@ -146,7 +164,8 @@ abstract class WeaponsComp implements Teamc, Posc, Rotc, Velc{
|
||||
}
|
||||
}
|
||||
|
||||
private void shoot(Weapon weapon, float x, float y, float aimX, float aimY, float rotation, int side){
|
||||
private void shoot(WeaponMount mount, float x, float y, float aimX, float aimY, float rotation, int side){
|
||||
Weapon weapon = mount.weapon;
|
||||
|
||||
float baseX = this.x, baseY = this.y;
|
||||
|
||||
@@ -156,29 +175,44 @@ abstract class WeaponsComp implements Teamc, Posc, Rotc, Velc{
|
||||
float lifeScl = ammo.scaleVelocity ? Mathf.clamp(Mathf.dst(x, y, aimX, aimY) / ammo.range()) : 1f;
|
||||
|
||||
sequenceNum = 0;
|
||||
if(weapon.shotDelay > 0.01f){
|
||||
if(weapon.shotDelay + weapon.firstShotDelay > 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), lifeScl));
|
||||
Time.run(sequenceNum * weapon.shotDelay + weapon.firstShotDelay, () -> {
|
||||
if(!isAdded()) return;
|
||||
mount.bullet = bullet(weapon, x + this.x - baseX, y + this.y - baseY, f + Mathf.range(weapon.inaccuracy), lifeScl);
|
||||
});
|
||||
sequenceNum++;
|
||||
});
|
||||
}else{
|
||||
Angles.shotgun(weapon.shots, weapon.spacing, rotation, f -> bullet(weapon, x, y, f + Mathf.range(weapon.inaccuracy), lifeScl));
|
||||
Angles.shotgun(weapon.shots, weapon.spacing, rotation, f -> mount.bullet = bullet(weapon, x, y, f + Mathf.range(weapon.inaccuracy), lifeScl));
|
||||
}
|
||||
|
||||
vel().add(Tmp.v1.trns(rotation + 180f, ammo.recoil));
|
||||
|
||||
boolean parentize = ammo.keepVelocity;
|
||||
|
||||
Effect.shake(weapon.shake, weapon.shake, x, y);
|
||||
if(weapon.firstShotDelay > 0){
|
||||
Time.run(weapon.firstShotDelay, () -> {
|
||||
if(!isAdded()) return;
|
||||
|
||||
vel.add(Tmp.v1.trns(rotation + 180f, ammo.recoil));
|
||||
Effect.shake(weapon.shake, weapon.shake, x, y);
|
||||
mount.heat = 1f;
|
||||
});
|
||||
}else{
|
||||
vel.add(Tmp.v1.trns(rotation + 180f, ammo.recoil));
|
||||
Effect.shake(weapon.shake, weapon.shake, x, y);
|
||||
mount.heat = 1f;
|
||||
}
|
||||
|
||||
weapon.ejectEffect.at(x, y, rotation * side);
|
||||
ammo.shootEffect.at(x, y, rotation, parentize ? this : null);
|
||||
ammo.smokeEffect.at(x, y, rotation, parentize ? this : null);
|
||||
apply(weapon.shootStatus, weapon.shootStatusDuration);
|
||||
}
|
||||
|
||||
private void bullet(Weapon weapon, float x, float y, float angle, float lifescl){
|
||||
private Bullet bullet(Weapon weapon, float x, float y, float angle, float lifescl){
|
||||
float xr = Mathf.range(weapon.xRand);
|
||||
|
||||
weapon.bullet.create(this, team(),
|
||||
return weapon.bullet.create(this, team(),
|
||||
x + Angles.trnsx(angle, 0, xr),
|
||||
y + Angles.trnsy(angle, 0, xr),
|
||||
angle, (1f - weapon.velocityRnd) + Mathf.random(weapon.velocityRnd), lifescl);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package mindustry.entities.units;
|
||||
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
public class WeaponMount{
|
||||
@@ -21,6 +23,8 @@ public class WeaponMount{
|
||||
public boolean rotate = false;
|
||||
/** extra state for alternating weapons */
|
||||
public boolean side;
|
||||
/** current bullet for continuous weapons */
|
||||
public @Nullable Bullet bullet;
|
||||
|
||||
public WeaponMount(Weapon weapon){
|
||||
this.weapon = weapon;
|
||||
|
||||
@@ -61,6 +61,9 @@ public class Saves{
|
||||
//automatically assign sector save slots
|
||||
for(SaveSlot slot : saves){
|
||||
if(slot.getSector() != null){
|
||||
if(slot.getSector().save != null){
|
||||
Log.warn("Sector @ has two corresponding saves: @ and @", slot.getSector(), slot.getSector().save.file, slot.file);
|
||||
}
|
||||
slot.getSector().save = slot;
|
||||
}
|
||||
}
|
||||
@@ -75,7 +78,6 @@ public class Saves{
|
||||
}
|
||||
|
||||
public void update(){
|
||||
SaveSlot current = this.current;
|
||||
|
||||
if(current != null && state.isGame()
|
||||
&& !(state.isPaused() && Core.scene.hasDialog())){
|
||||
@@ -90,14 +92,13 @@ public class Saves{
|
||||
if(time > Core.settings.getInt("saveinterval") * 60){
|
||||
saving = true;
|
||||
|
||||
Time.runTask(2f, () -> {
|
||||
try{
|
||||
current.save();
|
||||
}catch(Throwable e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
saving = false;
|
||||
});
|
||||
try{
|
||||
current.save();
|
||||
}catch(Throwable e){
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
Time.runTask(3f, () -> saving = false);
|
||||
|
||||
time = 0;
|
||||
}
|
||||
|
||||
@@ -2,9 +2,7 @@ package mindustry.game;
|
||||
|
||||
import arc.files.*;
|
||||
import arc.struct.*;
|
||||
import arc.struct.IntIntMap.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import mindustry.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.mod.Mods.*;
|
||||
import mindustry.type.*;
|
||||
@@ -38,20 +36,16 @@ public class Schematic implements Publishable, Comparable<Schematic>{
|
||||
return tiles.sumf(s -> s.block.consumes.has(ConsumeType.power) ? s.block.consumes.getPower().usage : 0f);
|
||||
}
|
||||
|
||||
public Seq<ItemStack> requirements(){
|
||||
IntIntMap amounts = new IntIntMap();
|
||||
public ItemSeq requirements(){
|
||||
ItemSeq requirements = new ItemSeq();
|
||||
|
||||
tiles.each(t -> {
|
||||
for(ItemStack stack : t.block.requirements){
|
||||
amounts.increment(stack.item.id, stack.amount);
|
||||
requirements.add(stack.item, stack.amount);
|
||||
}
|
||||
});
|
||||
Seq<ItemStack> stacks = new Seq<>();
|
||||
for(Entry ent : amounts.entries()){
|
||||
stacks.add(new ItemStack(Vars.content.item(ent.key), ent.value));
|
||||
}
|
||||
stacks.sort();
|
||||
return stacks;
|
||||
|
||||
return requirements;
|
||||
}
|
||||
|
||||
public boolean hasCore(){
|
||||
|
||||
@@ -33,6 +33,8 @@ public class SectorInfo{
|
||||
public boolean hasCore = true;
|
||||
/** Sector that was launched from. */
|
||||
public @Nullable Sector origin;
|
||||
/** Launch destination. */
|
||||
public @Nullable Sector destination;
|
||||
/** Resources known to occur at this sector. */
|
||||
public Seq<UnlockableContent> resources = new Seq<>();
|
||||
/** Time spent at this sector. Do not use unless you know what you're doing. */
|
||||
@@ -43,6 +45,12 @@ public class SectorInfo{
|
||||
/** Core item storage to prevent spoofing. */
|
||||
private transient int[] lastCoreItems;
|
||||
|
||||
/** @return the real location items go when launched on this sector */
|
||||
public Sector getRealDestination(){
|
||||
//on multiplayer the destination is, by default, the first captured sector (basically random)
|
||||
return !net.client() || destination != null ? destination : state.rules.sector.planet.sectors.find(Sector::hasBase);
|
||||
}
|
||||
|
||||
/** Updates export statistics. */
|
||||
public void handleItemExport(ItemStack stack){
|
||||
handleItemExport(stack.item, stack.amount);
|
||||
|
||||
@@ -20,7 +20,7 @@ public class Universe{
|
||||
private int turn;
|
||||
|
||||
private Schematic lastLoadout;
|
||||
private Seq<ItemStack> lastLaunchResources = new Seq<>();
|
||||
private ItemSeq lastLaunchResources = new ItemSeq();
|
||||
|
||||
public Universe(){
|
||||
load();
|
||||
@@ -104,14 +104,14 @@ public class Universe{
|
||||
}
|
||||
}
|
||||
|
||||
public Seq<ItemStack> getLaunchResources(){
|
||||
lastLaunchResources = Core.settings.getJson("launch-resources", Seq.class, ItemStack.class, Seq::new);
|
||||
public ItemSeq getLaunchResources(){
|
||||
lastLaunchResources = Core.settings.getJson("launch-resources-seq", ItemSeq.class, ItemSeq::new);
|
||||
return lastLaunchResources;
|
||||
}
|
||||
|
||||
public void updateLaunchResources(Seq<ItemStack> stacks){
|
||||
public void updateLaunchResources(ItemSeq stacks){
|
||||
this.lastLaunchResources = stacks;
|
||||
Core.settings.putJson("launch-resources", ItemStack.class, lastLaunchResources);
|
||||
Core.settings.putJson("launch-resources-seq", lastLaunchResources);
|
||||
}
|
||||
|
||||
/** Updates selected loadout for future deployment. */
|
||||
@@ -165,10 +165,23 @@ public class Universe{
|
||||
//if so, just delete the save for now. it's lost.
|
||||
//TODO don't delete it later maybe
|
||||
sector.save.delete();
|
||||
//clear recieved
|
||||
sector.setExtraItems(new ItemSeq());
|
||||
sector.save = null;
|
||||
}
|
||||
}
|
||||
|
||||
//export to another sector
|
||||
if(sector.save.meta.secinfo.destination != null){
|
||||
Sector to = sector.save.meta.secinfo.destination;
|
||||
if(to.save != null){
|
||||
ItemSeq items = to.getExtraItems();
|
||||
//calculated exported items to this sector
|
||||
sector.save.meta.secinfo.export.each((item, stat) -> items.add(item, (int)(stat.mean * newSecondsPassed)));
|
||||
to.setExtraItems(items);
|
||||
}
|
||||
}
|
||||
|
||||
//reset time spent to 0
|
||||
sector.setTimeSpent(0f);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package mindustry.graphics;
|
||||
import arc.graphics.Color;
|
||||
|
||||
public class Pal{
|
||||
public static final Color
|
||||
public static Color
|
||||
|
||||
items = Color.valueOf("2ea756"),
|
||||
command = Color.valueOf("eab678"),
|
||||
|
||||
@@ -200,7 +200,7 @@ public class DesktopInput extends InputHandler{
|
||||
}
|
||||
panning = false;
|
||||
|
||||
Core.camera.position.add(Tmp.v1.setZero().add(Core.input.axis(Binding.move_x), Core.input.axis(Binding.move_y)).nor().scl(Time.delta * camSpeed));
|
||||
Core.camera.position.add(Tmp.v1.setZero().add(Core.input.axis(Binding.move_x), Core.input.axis(Binding.move_y)).nor().scl(camSpeed));
|
||||
}else if(!player.dead() && !panning){
|
||||
Core.camera.position.lerpDelta(player, Core.settings.getBool("smoothcamera") ? 0.08f : 1f);
|
||||
}
|
||||
@@ -354,7 +354,7 @@ public class DesktopInput extends InputHandler{
|
||||
}).visible(() -> state.isCampaign()).tooltip("@planetmap");
|
||||
|
||||
table.button(Icon.up, Styles.clearPartiali, () -> {
|
||||
ui.planet.show(state.getSector(), player.team().core());
|
||||
ui.planet.showLaunch(state.getSector(), player.team().core());
|
||||
}).visible(() -> state.isCampaign()).tooltip("@launchcore").disabled(b -> player.team().core() == null);
|
||||
}
|
||||
|
||||
@@ -596,7 +596,7 @@ public class DesktopInput extends InputHandler{
|
||||
}
|
||||
|
||||
protected void updateMovement(Unit unit){
|
||||
boolean omni = !(unit instanceof WaterMovec);
|
||||
boolean omni = unit.type().omniMovement;
|
||||
boolean ground = unit.isGrounded();
|
||||
|
||||
float strafePenalty = ground ? 1f : Mathf.lerp(1f, unit.type().strafePenalty, Angles.angleDist(unit.vel().angle(), unit.rotation()) / 180f);
|
||||
|
||||
@@ -292,7 +292,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
}else if(unit == null){ //just clear the unit (is this used?)
|
||||
player.clearUnit();
|
||||
//make sure it's AI controlled, so players can't overwrite each other
|
||||
}else if(unit.isAI() && unit.team == player.team() && !unit.deactivated() && !unit.dead){
|
||||
}else if(unit.isAI() && unit.team == player.team() && !unit.dead){
|
||||
if(!net.client()){
|
||||
player.unit(unit);
|
||||
}
|
||||
@@ -365,7 +365,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
}
|
||||
|
||||
if(controlledType != null && player.dead()){
|
||||
Unit unit = Units.closest(player.team(), player.x, player.y, u -> !u.isPlayer() && u.type() == controlledType && !u.deactivated() && !u.dead);
|
||||
Unit unit = Units.closest(player.team(), player.x, player.y, u -> !u.isPlayer() && u.type() == controlledType && !u.dead);
|
||||
|
||||
if(unit != null){
|
||||
Call.unitControl(player, unit);
|
||||
@@ -375,7 +375,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
|
||||
public void checkUnit(){
|
||||
if(controlledType != null){
|
||||
Unit unit = Units.closest(player.team(), player.x, player.y, u -> !u.isPlayer() && u.type() == controlledType && !u.deactivated() && !u.dead);
|
||||
Unit unit = Units.closest(player.team(), player.x, player.y, u -> !u.isPlayer() && u.type() == controlledType && !u.dead);
|
||||
if(unit == null && controlledType == UnitTypes.block){
|
||||
unit = world.buildWorld(player.x, player.y) instanceof ControlBlock ? ((ControlBlock)world.buildWorld(player.x, player.y)).unit() : null;
|
||||
}
|
||||
@@ -931,7 +931,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
}
|
||||
|
||||
public @Nullable Unit selectedUnit(){
|
||||
Unit unit = Units.closest(player.team(), Core.input.mouseWorld().x, Core.input.mouseWorld().y, 40f, u -> u.isAI() && !u.deactivated());
|
||||
Unit unit = Units.closest(player.team(), Core.input.mouseWorld().x, Core.input.mouseWorld().y, 40f, u -> u.isAI());
|
||||
if(unit != null){
|
||||
unit.hitbox(Tmp.r1);
|
||||
Tmp.r1.grow(6f);
|
||||
|
||||
@@ -804,7 +804,7 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
if(type == null) return;
|
||||
|
||||
boolean flying = type.flying;
|
||||
boolean omni = !(unit instanceof WaterMovec);
|
||||
boolean omni = unit.type().omniMovement;
|
||||
boolean legs = unit.isGrounded();
|
||||
boolean allowHealing = type.canHeal;
|
||||
boolean validHealTarget = allowHealing && target instanceof Building && ((Building)target).isValid() && target.team() == unit.team &&
|
||||
|
||||
@@ -126,6 +126,10 @@ public class SaveIO{
|
||||
}
|
||||
}
|
||||
|
||||
public static void load(String saveName) throws SaveException{
|
||||
load(saveDirectory.child(saveName + ".msav"));
|
||||
}
|
||||
|
||||
public static void load(Fi file) throws SaveException{
|
||||
load(file, world.context);
|
||||
}
|
||||
|
||||
@@ -295,16 +295,6 @@ public class SerpuloPlanetGenerator extends PlanetGenerator{
|
||||
|
||||
//TODO better waves
|
||||
state.rules.spawns = defaultWaves.get();
|
||||
|
||||
float waveScaling = 1f + difficulty*2;
|
||||
|
||||
//scale up the spawning base on difficulty (this is just for testing)
|
||||
for(SpawnGroup group : state.rules.spawns){
|
||||
group.unitAmount *= waveScaling;
|
||||
if(group.unitScaling != SpawnGroup.never){
|
||||
group.unitScaling /= waveScaling;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -22,7 +22,7 @@ public class Scripts implements Disposable{
|
||||
private final Seq<String> blacklist = Seq.with(".net.", "java.net", "files", "reflect", "javax", "rhino", "file", "channels", "jdk",
|
||||
"runtime", "util.os", "rmi", "security", "org.", "sun.", "beans", "sql", "http", "exec", "compiler", "process", "system",
|
||||
".awt", "socket", "classloader", "oracle", "invoke", "java.util.function", "java.util.stream", "org.");
|
||||
private final Seq<String> whitelist = Seq.with("mindustry.net", "netserver", "netclient", "com.sun.proxy.$proxy", "mindustry.gen.", "mindustry.logic.", "mindustry.async.");
|
||||
private final Seq<String> whitelist = Seq.with("mindustry.net", "netserver", "netclient", "com.sun.proxy.$proxy", "mindustry.gen.", "mindustry.logic.", "mindustry.async.", "saveio");
|
||||
private final Context context;
|
||||
private final Scriptable scope;
|
||||
private boolean errored;
|
||||
|
||||
@@ -61,7 +61,7 @@ public class ArcNetProvider implements NetProvider{
|
||||
try{
|
||||
net.handleClientReceived(object);
|
||||
}catch(Throwable e){
|
||||
handleException(e);
|
||||
net.handleException(e);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -144,14 +144,14 @@ public class ArcNetProvider implements NetProvider{
|
||||
try{
|
||||
client.run();
|
||||
}catch(Exception e){
|
||||
if(!(e instanceof ClosedSelectorException)) handleException(e);
|
||||
if(!(e instanceof ClosedSelectorException)) net.handleException(e);
|
||||
}
|
||||
});
|
||||
|
||||
client.connect(5000, ip, port, port);
|
||||
success.run();
|
||||
}catch(Exception e){
|
||||
handleException(e);
|
||||
net.handleException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -269,16 +269,6 @@ public class ArcNetProvider implements NetProvider{
|
||||
return null;
|
||||
}
|
||||
|
||||
void handleException(Throwable e){
|
||||
if(e instanceof ArcNetException){
|
||||
Core.app.post(() -> net.showError(new IOException("mismatch")));
|
||||
}else if(e instanceof ClosedChannelException){
|
||||
Core.app.post(() -> net.showError(new IOException("alreadyconnected")));
|
||||
}else{
|
||||
Core.app.post(() -> net.showError(e));
|
||||
}
|
||||
}
|
||||
|
||||
class ArcConnection extends NetConnection{
|
||||
public final Connection connection;
|
||||
|
||||
|
||||
@@ -19,19 +19,33 @@ import java.text.*;
|
||||
import java.util.*;
|
||||
|
||||
import static arc.Core.*;
|
||||
import static mindustry.Vars.mods;
|
||||
import static mindustry.Vars.net;
|
||||
|
||||
public class CrashSender{
|
||||
|
||||
public static String createReport(String error){
|
||||
String report = "Oh no, Mindustry crashed!\n";
|
||||
if(mods.list().size == 0){
|
||||
report += "Please report this at https://github.com/Anuken/Mindustry/issues/new?labels=bug&template=bug_report.md\n\n";
|
||||
}
|
||||
return report + "Version: " + Version.combined() + (Vars.headless ? " (Server)" : "") + "\n"
|
||||
+ "OS: " + System.getProperty("os.name") + " x" + (OS.is64Bit ? "64" : "32") + "\n"
|
||||
+ "Java Version: " + System.getProperty("java.version") + "\n"
|
||||
+ "Java Architecture: " + System.getProperty("sun.arch.data.model") + "\n"
|
||||
+ mods.list().size + " Mods: " + mods.list().toString(", ", mod -> mod.name + ":" + mod.meta.version)
|
||||
+ "\n\n" + error;
|
||||
}
|
||||
|
||||
public static void log(Throwable exception){
|
||||
try{
|
||||
Core.settings.getDataDirectory().child("crashes").child("crash_" + System.currentTimeMillis() + ".txt").writeString(Strings.neatError(exception));
|
||||
Core.settings.getDataDirectory().child("crashes").child("crash_" + System.currentTimeMillis() + ".txt")
|
||||
.writeString(createReport(Strings.neatError(exception)));
|
||||
}catch(Throwable ignored){
|
||||
}
|
||||
}
|
||||
|
||||
public static void send(Throwable exception, Cons<File> writeListener){
|
||||
|
||||
try{
|
||||
exception.printStackTrace();
|
||||
|
||||
@@ -70,7 +84,7 @@ public class CrashSender{
|
||||
try{
|
||||
File file = new File(OS.getAppDataDirectoryString(Vars.appName), "crashes/crash-report-" + new SimpleDateFormat("MM_dd_yyyy_HH_mm_ss").format(new Date()) + ".txt");
|
||||
new Fi(OS.getAppDataDirectoryString(Vars.appName)).child("crashes").mkdirs();
|
||||
new Fi(file).writeString(parseException(exception));
|
||||
new Fi(file).writeString(createReport(parseException(exception)));
|
||||
writeListener.get(file);
|
||||
}catch(Throwable e){
|
||||
Log.err("Failed to save local crash report.", e);
|
||||
@@ -130,7 +144,7 @@ public class CrashSender{
|
||||
boolean[] sent = {false};
|
||||
|
||||
Log.info("Sending crash report.");
|
||||
//post to crash report URL
|
||||
//post to crash report URL, exit code indicates send success
|
||||
httpPost(Vars.crashReportURL, value.toJson(OutputType.json), r -> {
|
||||
Log.info("Crash sent successfully.");
|
||||
sent[0] = true;
|
||||
@@ -138,7 +152,7 @@ public class CrashSender{
|
||||
}, t -> {
|
||||
t.printStackTrace();
|
||||
sent[0] = true;
|
||||
System.exit(1);
|
||||
System.exit(-1);
|
||||
});
|
||||
|
||||
//sleep until report is sent
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
package mindustry.net;
|
||||
|
||||
import arc.*;
|
||||
import arc.struct.*;
|
||||
import arc.func.*;
|
||||
import arc.util.*;
|
||||
import arc.net.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.ArcAnnotate.*;
|
||||
import arc.util.*;
|
||||
import arc.util.pooling.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.net.Packets.*;
|
||||
@@ -13,6 +14,7 @@ import net.jpountz.lz4.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.*;
|
||||
import java.nio.channels.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@@ -36,6 +38,16 @@ public class Net{
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
public void handleException(Throwable e){
|
||||
if(e instanceof ArcNetException){
|
||||
Core.app.post(() -> showError(new IOException("mismatch")));
|
||||
}else if(e instanceof ClosedChannelException){
|
||||
Core.app.post(() -> showError(new IOException("alreadyconnected")));
|
||||
}else{
|
||||
Core.app.post(() -> showError(e));
|
||||
}
|
||||
}
|
||||
|
||||
/** Display a network error. Call on the graphics thread. */
|
||||
public void showError(Throwable e){
|
||||
|
||||
|
||||
@@ -11,11 +11,14 @@ import mindustry.world.modules.ItemModule.*;
|
||||
import java.util.*;
|
||||
|
||||
public class ItemSeq implements Iterable<ItemStack>, Serializable{
|
||||
protected final int[] values;
|
||||
protected final int[] values = new int[Vars.content.items().size];
|
||||
public int total;
|
||||
|
||||
public ItemSeq(){
|
||||
values = new int[Vars.content.items().size];
|
||||
}
|
||||
|
||||
public ItemSeq(Seq<ItemStack> stacks){
|
||||
stacks.each(this::add);
|
||||
}
|
||||
|
||||
public void each(ItemConsumer cons){
|
||||
@@ -26,6 +29,11 @@ public class ItemSeq implements Iterable<ItemStack>, Serializable{
|
||||
}
|
||||
}
|
||||
|
||||
public void clear(){
|
||||
total = 0;
|
||||
Arrays.fill(values, 0);
|
||||
}
|
||||
|
||||
public Seq<ItemStack> toSeq(){
|
||||
Seq<ItemStack> out = new Seq<>();
|
||||
for(int i = 0; i < values.length; i++){
|
||||
|
||||
@@ -275,6 +275,10 @@ public class Sector{
|
||||
Core.settings.put(key(key), value);
|
||||
}
|
||||
|
||||
public String toString(){
|
||||
return planet.name + "#" + id;
|
||||
}
|
||||
|
||||
/** Projects this sector onto a 4-corner square for use in map gen.
|
||||
* Allocates a new object. Do not call in the main loop. */
|
||||
private SectorRect makeRect(){
|
||||
|
||||
@@ -44,7 +44,7 @@ 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, fallSpeed = 0.018f;
|
||||
public float health = 200f, range = -1, armor = 0f;
|
||||
public float crashDamageMultiplier = 3f;
|
||||
public float crashDamageMultiplier = 1f;
|
||||
public boolean targetAir = true, targetGround = true;
|
||||
public boolean faceTarget = true, rotateShooting = true, isCounted = true, lowAltitude = false;
|
||||
public boolean canBoost = false;
|
||||
@@ -55,6 +55,7 @@ public class UnitType extends UnlockableContent{
|
||||
public float visualElevation = -1f;
|
||||
public boolean allowLegStep = false;
|
||||
public boolean hovering = false;
|
||||
public boolean omniMovement = true;
|
||||
public Effect fallEffect = Fx.fallSmoke;
|
||||
public Effect fallThrusterEffect = Fx.fallSmoke;
|
||||
public Seq<Ability> abilities = new Seq<>();
|
||||
@@ -75,6 +76,7 @@ public class UnitType extends UnlockableContent{
|
||||
public int mineTier = -1;
|
||||
public float buildSpeed = 1f, mineSpeed = 1f;
|
||||
|
||||
public float clipSize = -1;
|
||||
public boolean canDrown = true;
|
||||
public float engineOffset = 5f, engineSize = 2.5f;
|
||||
public float strafePenalty = 0.5f;
|
||||
@@ -134,7 +136,7 @@ public class UnitType extends UnlockableContent{
|
||||
|
||||
public void update(Unit unit){
|
||||
|
||||
if(unit instanceof Mechc){
|
||||
if(unit instanceof Mechc && !unit.isFlying()){
|
||||
updateMechEffects(unit);
|
||||
}
|
||||
}
|
||||
@@ -194,13 +196,6 @@ public class UnitType extends UnlockableContent{
|
||||
}).growX();
|
||||
|
||||
table.row();
|
||||
if(unit.deactivated){
|
||||
table.table(d -> {
|
||||
d.left();
|
||||
d.label(() -> Core.bundle.format("bar.limitreached", unit.count(), unit.cap(), Fonts.getUnicodeStr(name)));
|
||||
}).left().visible(() -> unit.deactivated);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -230,6 +225,15 @@ public class UnitType extends UnlockableContent{
|
||||
public void init(){
|
||||
if(constructor == null) throw new IllegalArgumentException("no constructor set up for unit '" + name + "'");
|
||||
|
||||
Unit example = constructor.get();
|
||||
|
||||
//water preset
|
||||
if(example instanceof WaterMovec){
|
||||
canDrown = false;
|
||||
omniMovement = false;
|
||||
immunities.add(StatusEffects.wet);
|
||||
}
|
||||
|
||||
singleTarget = weapons.size <= 1;
|
||||
|
||||
if(itemCapacity < 0){
|
||||
@@ -396,10 +400,6 @@ public class UnitType extends UnlockableContent{
|
||||
unit.trns(-legOffset.x, -legOffset.y);
|
||||
}
|
||||
|
||||
if(unit.deactivated){
|
||||
drawDeactive(unit);
|
||||
}
|
||||
|
||||
if(unit.abilities.size > 0){
|
||||
for(Ability a : unit.abilities){
|
||||
Draw.reset();
|
||||
@@ -410,16 +410,6 @@ public class UnitType extends UnlockableContent{
|
||||
}
|
||||
}
|
||||
|
||||
public void drawDeactive(Unit unit){
|
||||
Draw.color(Color.scarlet);
|
||||
Draw.alpha(0.8f);
|
||||
|
||||
float size = 8f;
|
||||
Draw.rect(Icon.warning.getRegion(), unit.x, unit.y, size, size);
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
public <T extends Unit & Payloadc> void drawPayload(T unit){
|
||||
if(unit.hasPayload()){
|
||||
Payload pay = unit.payloads().first();
|
||||
@@ -525,7 +515,6 @@ public class UnitType extends UnlockableContent{
|
||||
|
||||
float rotation = unit.rotation - 90;
|
||||
float weaponRotation = rotation + (weapon.rotate ? mount.rotation : 0);
|
||||
float width = weapon.region.width;
|
||||
float recoil = -((mount.reload) / weapon.reload * weapon.recoil);
|
||||
float wx = unit.x + Angles.trnsx(rotation, weapon.x, weapon.y) + Angles.trnsx(weaponRotation, 0, recoil),
|
||||
wy = unit.y + Angles.trnsy(rotation, weapon.x, weapon.y) + Angles.trnsy(weaponRotation, 0, recoil);
|
||||
@@ -540,17 +529,16 @@ public class UnitType extends UnlockableContent{
|
||||
|
||||
Draw.rect(weapon.outlineRegion,
|
||||
wx, wy,
|
||||
width * Draw.scl * -Mathf.sign(weapon.flipSprite),
|
||||
weapon.outlineRegion.width * Draw.scl * -Mathf.sign(weapon.flipSprite),
|
||||
weapon.region.height * Draw.scl,
|
||||
weaponRotation);
|
||||
|
||||
|
||||
Draw.z(z);
|
||||
}
|
||||
|
||||
Draw.rect(weapon.region,
|
||||
wx, wy,
|
||||
width * Draw.scl * -Mathf.sign(weapon.flipSprite),
|
||||
weapon.region.width * Draw.scl * -Mathf.sign(weapon.flipSprite),
|
||||
weapon.region.height * Draw.scl,
|
||||
weaponRotation);
|
||||
|
||||
@@ -559,8 +547,8 @@ public class UnitType extends UnlockableContent{
|
||||
Draw.blend(Blending.additive);
|
||||
Draw.rect(weapon.heatRegion,
|
||||
wx, wy,
|
||||
width * Draw.scl * -Mathf.sign(weapon.flipSprite),
|
||||
weapon.region.height * Draw.scl,
|
||||
weapon.heatRegion.width * Draw.scl * -Mathf.sign(weapon.flipSprite),
|
||||
weapon.heatRegion.height * Draw.scl,
|
||||
weaponRotation);
|
||||
Draw.blend();
|
||||
Draw.color();
|
||||
@@ -571,6 +559,8 @@ public class UnitType extends UnlockableContent{
|
||||
}
|
||||
|
||||
public void drawOutline(Unit unit){
|
||||
Draw.reset();
|
||||
|
||||
if(Core.atlas.isFound(outlineRegion)){
|
||||
Draw.rect(outlineRegion, unit.x, unit.y, unit.rotation - 90);
|
||||
}
|
||||
@@ -614,11 +604,6 @@ public class UnitType extends UnlockableContent{
|
||||
Drawf.shadow(leg.base.x, leg.base.y, ssize);
|
||||
}
|
||||
|
||||
//TODO should be below/above legs
|
||||
if(baseRegion.found()){
|
||||
Draw.rect(baseRegion, unit.x, unit.y, rotation);
|
||||
}
|
||||
|
||||
//legs are drawn front first
|
||||
for(int j = legs.length - 1; j >= 0; j--){
|
||||
int i = (j % 2 == 0 ? j/2 : legs.length - 1 - j/2);
|
||||
@@ -656,6 +641,11 @@ public class UnitType extends UnlockableContent{
|
||||
}
|
||||
}
|
||||
|
||||
//TODO should be below/above legs
|
||||
if(baseRegion.found()){
|
||||
Draw.rect(baseRegion, unit.x, unit.y, rotation - 90);
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,8 @@ public class Weapon{
|
||||
public boolean rotate = false;
|
||||
/** whether to draw the outline on top. */
|
||||
public boolean top = true;
|
||||
/** whether to hold the bullet in place while firing */
|
||||
public boolean continuous;
|
||||
/** rotation speed of weapon when rotation is enabled, in degrees/t*/
|
||||
public float rotateSpeed = 20f;
|
||||
/** weapon reload in frames */
|
||||
@@ -54,6 +56,8 @@ public class Weapon{
|
||||
/** fraction of velocity that is random */
|
||||
public float velocityRnd = 0f;
|
||||
/** delay in ticks between shots */
|
||||
public float firstShotDelay = 0;
|
||||
/** delay in ticks between shots */
|
||||
public float shotDelay = 0;
|
||||
/** The half-radius of the cone in which shooting will start. */
|
||||
public float shootCone = 5f;
|
||||
@@ -77,6 +81,10 @@ public class Weapon{
|
||||
public TextureRegion outlineRegion;
|
||||
/** heat region tint */
|
||||
public Color heatColor = Pal.turretHeat;
|
||||
/** status effect applied when shooting */
|
||||
public StatusEffect shootStatus = StatusEffects.none;
|
||||
/** status effect duration when shot */
|
||||
public float shootStatusDuration = 60f * 5f;
|
||||
|
||||
public Weapon(String name){
|
||||
this.name = name;
|
||||
|
||||
@@ -14,7 +14,7 @@ public class ItemDisplay extends Table{
|
||||
}
|
||||
|
||||
public ItemDisplay(Item item, int amount, boolean showName){
|
||||
add(new ItemImage(new ItemStack(item, amount))).size(8 * 4).padRight(amount > 99 ? 12 : 0);
|
||||
add(new ItemImage(new ItemStack(item, amount)));
|
||||
if(showName) add(item.localizedName).padLeft(4 + amount > 99 ? 4 : 0);
|
||||
|
||||
this.item = item;
|
||||
|
||||
@@ -8,15 +8,17 @@ import mindustry.type.*;
|
||||
public class ItemImage extends Stack{
|
||||
|
||||
public ItemImage(TextureRegion region, int amount){
|
||||
Table t = new Table().left().bottom();
|
||||
t.add(amount + "").name("item-label");
|
||||
t.pack();
|
||||
|
||||
add(new Table(o -> {
|
||||
o.left();
|
||||
o.add(new Image(region)).size(32f);
|
||||
}));
|
||||
add(t);
|
||||
|
||||
add(new Table(t -> {
|
||||
t.left().bottom();
|
||||
t.add(amount + "");
|
||||
t.pack();
|
||||
}));
|
||||
}
|
||||
|
||||
public ItemImage(TextureRegion region){
|
||||
@@ -27,12 +29,18 @@ public class ItemImage extends Stack{
|
||||
}
|
||||
|
||||
public ItemImage(ItemStack stack){
|
||||
add(new Image(stack.item.icon(Cicon.medium)));
|
||||
|
||||
add(new Table(o -> {
|
||||
o.left();
|
||||
o.add(new Image(stack.item.icon(Cicon.medium))).size(32f);
|
||||
}));
|
||||
|
||||
if(stack.amount != 0){
|
||||
Table t = new Table().left().bottom();
|
||||
t.add(stack.amount + "").name("item-label").style(Styles.outlineLabel);
|
||||
add(t);
|
||||
add(new Table(t -> {
|
||||
t.left().bottom();
|
||||
t.add(stack.amount + "").style(Styles.outlineLabel);
|
||||
t.pack();
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ public class DatabaseDialog extends BaseDialog{
|
||||
for(int i = 0; i < array.size; i++){
|
||||
UnlockableContent unlock = (UnlockableContent)array.get(i);
|
||||
|
||||
Image image = unlocked(unlock) ? new Image(unlock.icon(Cicon.medium)) : new Image(Icon.lock, Pal.gray);
|
||||
Image image = unlocked(unlock) ? new Image(unlock.icon(Cicon.medium)).setScaling(Scaling.fit) : new Image(Icon.lock, Pal.gray);
|
||||
list.add(image).size(8*4).pad(3);
|
||||
ClickListener listener = new ClickListener();
|
||||
image.addListener(listener);
|
||||
|
||||