diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 64b7650e62..85459446aa 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -7,8 +7,6 @@ assignees: '' --- -**Note**: Do not report any new bugs directly relating to the v6 campaign. They will not be fixed or considered at this time. - **Platform**: *Android/iOS/Mac/Windows/Linux* **Build**: *The build number under the title in the main menu. Required. "LATEST" IS NOT A VERSION, I NEED THE EXACT BUILD NUMBER OF YOUR GAME.* diff --git a/README.md b/README.md index 5c9c797297..13f620832f 100644 --- a/README.md +++ b/README.md @@ -9,34 +9,34 @@ _[Trello Board](https://trello.com/b/aE2tcUwF/mindustry-40-plans)_ _[Wiki](https://mindustrygame.github.io/wiki)_ _[Javadoc](https://mindustrygame.github.io/docs/)_ -### Contributing +## Contributing See [CONTRIBUTING](CONTRIBUTING.md). -### Building +## Building Bleeding-edge builds are generated automatically for every commit. You can see them [here](https://github.com/Anuken/MindustryBuilds/releases). If you'd rather compile on your own, follow these instructions. First, make sure you have [JDK 14](https://adoptopenjdk.net/) installed. Open a terminal in the root directory, `cd` to the Mindustry folder and run the following commands: -#### Windows +### Windows _Running:_ `gradlew desktop:run` _Building:_ `gradlew desktop:dist` _Sprite Packing:_ `gradlew tools:pack` -#### Linux/Mac OS +### Linux/Mac OS _Running:_ `./gradlew desktop:run` _Building:_ `./gradlew desktop:dist` _Sprite Packing:_ `./gradlew tools:pack` -#### Server +### Server Server builds are bundled with each released build (in Releases). If you'd rather compile on your own, replace 'desktop' with 'server', e.g. `gradlew server:dist`. -#### Android +### Android 1. Install the Android SDK [here.](https://developer.android.com/studio#downloads) Make sure you're downloading the "Command line tools only", as Android Studio is not required. 2. Set the `ANDROID_HOME` environment variable to point to your unzipped Android SDK directory. @@ -44,7 +44,9 @@ Server builds are bundled with each released build (in Releases). If you'd rathe To debug the application on a connected phone, run `gradlew android:installDebug android:run`. -##### Troubleshooting +### Troubleshooting + +#### Permission Denied If the terminal returns `Permission denied` or `Command not found` on Mac/Linux, run `chmod +x ./gradlew` before running `./gradlew`. *This is a one-time procedure.* @@ -53,11 +55,11 @@ If the terminal returns `Permission denied` or `Command not found` on Mac/Linux, Gradle may take up to several minutes to download files. Be patient.
After building, the output .JAR file should be in `/desktop/build/libs/Mindustry.jar` for desktop builds, and in `/server/build/libs/server-release.jar` for server builds. -### Feature Requests +## Feature Requests Post feature requests and feedback [here](https://github.com/Anuken/Mindustry-Suggestions/issues/new/choose). -### Downloads +## Downloads [Get it on Itch.io}SmtWKuXGq Yz\P?E:Y ΤԢh)(%UDK(.%#V0FN# \ No newline at end of file diff --git a/core/assets/baseparts/1603214967392.msch b/core/assets/baseparts/1603214967392.msch new file mode 100644 index 0000000000..0e30b20a82 --- /dev/null +++ b/core/assets/baseparts/1603214967392.msch @@ -0,0 +1 @@ +mschx%A ᧀZc\.z$dФ/t?3o`Ɲ.L<$NV!b>=e%80r"^!GKug9耦{S_@+$jW\oJSM ܅0?CThqW \ No newline at end of file diff --git a/core/assets/baseparts/1603214996033.msch b/core/assets/baseparts/1603214996033.msch new file mode 100644 index 0000000000..83690987ae --- /dev/null +++ b/core/assets/baseparts/1603214996033.msch @@ -0,0 +1,3 @@ +mschx% + Ӫmic׽C/{bU~A>:n^Xvw?ŧO }c|tt#-)䃑dqPҒr$b@*b2rtҳRZґ@Wr# \ No newline at end of file diff --git a/core/assets/baseparts/1603215171294.msch b/core/assets/baseparts/1603215171294.msch new file mode 100644 index 0000000000..eaef917ff3 --- /dev/null +++ b/core/assets/baseparts/1603215171294.msch @@ -0,0 +1,2 @@ +mschx%ᎃ Ai߃pB`M +!0;060/oK8J\k a|T k~Sq)O-h_|M%35V⾸Ӌ\[}ٱ#ⶼ@0-'hh0@W @JCi4bjp2gㄽ_]䡎Eӱ-K+. \ No newline at end of file diff --git a/core/assets/baseparts/1603215272794.msch b/core/assets/baseparts/1603215272794.msch new file mode 100644 index 0000000000..b2351a3857 --- /dev/null +++ b/core/assets/baseparts/1603215272794.msch @@ -0,0 +1 @@ +mschxMKn D 1P&#$ 9yvtCY_%Jno>1-~K؎i?#BI%q;R8W;r˖#v)!F\7ٔoIy{4ïvg=!;^`4E ` ah:I:!꬐u0hRFC7: TW0t=z*] %wX#H7G4}mWW03Įb6 e> \ No newline at end of file diff --git a/core/assets/baseparts/1603215345320.msch b/core/assets/baseparts/1603215345320.msch new file mode 100644 index 0000000000..8c97534bdf --- /dev/null +++ b/core/assets/baseparts/1603215345320.msch @@ -0,0 +1,4 @@ +mschxEn@\E!dͲ:iҐqvfاȇP^]|g7Bw +Vp]ln5'?|`npr};МC0O[vM߆4vpߑ'*HY~2dJNV(BLr +bHw-cKM52e*W R!KL{X# SAJR5dCV6r]m`t~H覵RvX +2HNk{8'L^+y# +pU%%%JAJ$?M \ No newline at end of file diff --git a/core/assets/baseparts/1603215415778.msch b/core/assets/baseparts/1603215415778.msch new file mode 100644 index 0000000000..6f7f75ab97 --- /dev/null +++ b/core/assets/baseparts/1603215415778.msch @@ -0,0 +1,2 @@ +mschx-k +1 MуP PҮEdM?o2c1e_BϚ/guI08bI~e.pRwGO`madh{Nނ:'"IZ}äz}o)" \ No newline at end of file diff --git a/core/assets/baseparts/1603215454977.msch b/core/assets/baseparts/1603215454977.msch new file mode 100644 index 0000000000..c0a53057ed Binary files /dev/null and b/core/assets/baseparts/1603215454977.msch differ diff --git a/core/assets/baseparts/1603215491407.msch b/core/assets/baseparts/1603215491407.msch new file mode 100644 index 0000000000..ba578a4a55 --- /dev/null +++ b/core/assets/baseparts/1603215491407.msch @@ -0,0 +1,2 @@ +mschx5ኃ01mj9(h~H½eufCn +jgA7}~K>@ }cNOv2a=oc\D[=qt0&?S (Р(p=R$тJT&geJIMʴ* ~#d|EE4I#tFxpzpzʤ3% h'/4 ol```›SfffG?J (, \ No newline at end of file diff --git a/core/assets/baseparts/1603215563717.msch b/core/assets/baseparts/1603215563717.msch new file mode 100644 index 0000000000..460c1d9f8b Binary files /dev/null and b/core/assets/baseparts/1603215563717.msch differ diff --git a/core/assets/baseparts/1603215697527.msch b/core/assets/baseparts/1603215697527.msch new file mode 100644 index 0000000000..427e52f671 Binary files /dev/null and b/core/assets/baseparts/1603215697527.msch differ diff --git a/core/assets/baseparts/1603215771822.msch b/core/assets/baseparts/1603215771822.msch new file mode 100644 index 0000000000..68ee586ea5 Binary files /dev/null and b/core/assets/baseparts/1603215771822.msch differ diff --git a/core/assets/baseparts/1603215801163.msch b/core/assets/baseparts/1603215801163.msch new file mode 100644 index 0000000000..09b9033d84 --- /dev/null +++ b/core/assets/baseparts/1603215801163.msch @@ -0,0 +1 @@ +mschx-[ DJJ aQMӵ~g&31dSo=-q[[LޭFqav.0[ SA/ciF(dPD~+ \ No newline at end of file diff --git a/core/assets/baseparts/1603216038407.msch b/core/assets/baseparts/1603216038407.msch new file mode 100644 index 0000000000..85865ba123 Binary files /dev/null and b/core/assets/baseparts/1603216038407.msch differ diff --git a/core/assets/baseparts/1603216102310.msch b/core/assets/baseparts/1603216102310.msch new file mode 100644 index 0000000000..a05dbc61ed --- /dev/null +++ b/core/assets/baseparts/1603216102310.msch @@ -0,0 +1 @@ +mschx%[j0 EoؓK]e258 ;SJI]">O}\_~Hy !K) %Q5KzFNR2L/9uۋX|iX9u+8pz&։mOh!>t^m1%(M6Ae) \ No newline at end of file diff --git a/core/assets/baseparts/1603216240041.msch b/core/assets/baseparts/1603216240041.msch new file mode 100644 index 0000000000..c0960c09d5 --- /dev/null +++ b/core/assets/baseparts/1603216240041.msch @@ -0,0 +1,3 @@ +mschxMIR0EJk.cp +*cZrF_\n?2^x(Rv Hg^}N'ς$6r4Tl"=ZނbA4W'&wt3-l`PC?6s}+iyf$15a-~Y/gD>X \ No newline at end of file diff --git a/core/assets/baseparts/1603222833941.msch b/core/assets/baseparts/1603222833941.msch new file mode 100644 index 0000000000..565eecf779 Binary files /dev/null and b/core/assets/baseparts/1603222833941.msch differ diff --git a/core/assets/baseparts/1603222912251.msch b/core/assets/baseparts/1603222912251.msch new file mode 100644 index 0000000000..6b33abac34 Binary files /dev/null and b/core/assets/baseparts/1603222912251.msch differ diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 4a854f5c71..3e88aef2e1 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -100,8 +100,7 @@ committingchanges = Comitting Changes done = Done feature.unsupported = Your device does not support this feature. -mods.alphainfo = Keep in mind that mods are in alpha, and[scarlet] may be very buggy[].\nReport any issues you find to the Mindustry GitHub or Discord. -mods.alpha = [accent](Alpha) +mods.alphainfo = Keep in mind that mods are in alpha, and[scarlet] may be very buggy[].\nReport any issues you find to the Mindustry GitHub. mods = Mods mods.none = [lightgray]No mods found! mods.guide = Modding Guide @@ -285,6 +284,7 @@ selectschematic = [accent][[{0}][] to select+copy pausebuilding = [accent][[{0}][] to pause building resumebuilding = [scarlet][[{0}][] to resume building wave = [accent]Wave {0} +wave.cap = [accent]Wave {0}/{1} wave.waiting = [lightgray]Wave in {0} wave.waveInProgress = [lightgray]Wave in progress waiting = [lightgray]Waiting... @@ -478,7 +478,7 @@ requirement.research = Research {0} requirement.capture = Capture {0} bestwave = [lightgray]Best Wave: {0} launch.text = Launch -campaign.multiplayer = While playing multiplayer in campaign, you can only research using items from [accent]your[] sectors, [scarlet]not[] the host's sector that you are on right now.\n\nTo get items to [accent]your[] sectors in multiplayer, use a [accent]launch pad[]. +research.multiplayer = Only the host can research items. uncover = Uncover configure = Configure Loadout #TODO @@ -512,6 +512,7 @@ weather.rain.name = Rain weather.snow.name = Snow weather.sandstorm.name = Sandstorm weather.sporestorm.name = Sporestorm +weather.fog.name = Fog sectors.unexplored = [lightgray]Unexplored sectors.resources = Resources: @@ -521,6 +522,7 @@ sectors.resume = Resume sectors.launch = Launch sectors.select = Select sectors.nonelaunch = [lightgray]none (sun) +sectors.rename = Rename Sector planet.serpulo.name = Serpulo #TODO better name @@ -580,50 +582,64 @@ error.title = [scarlet]An error has occured error.crashtitle = An error has occured unit.nobuild = [scarlet]Unit can't build lastaccessed = [lightgray]Last Accessed: {0} -blocks.input = Input -blocks.output = Output -blocks.booster = Booster -blocks.tiles = Required Tiles -blocks.affinities = Affinities block.unknown = [lightgray]??? -blocks.powercapacity = Power Capacity -blocks.powershot = Power/Shot -blocks.damage = Damage -blocks.targetsair = Targets Air -blocks.targetsground = Targets Ground -blocks.itemsmoved = Move Speed -blocks.launchtime = Time Between Launches -blocks.shootrange = Range -blocks.size = Size -blocks.displaysize = Display Size -blocks.liquidcapacity = Liquid Capacity -blocks.powerrange = Power Range -blocks.linkrange = Link Range -blocks.instructions = Instructions -blocks.powerconnections = Max Connections -blocks.poweruse = Power Use -blocks.powerdamage = Power/Damage -blocks.itemcapacity = Item Capacity -blocks.memorycapacity = Memory Capacity -blocks.basepowergeneration = Base Power Generation -blocks.productiontime = Production Time -blocks.repairtime = Block Full Repair Time -blocks.speedincrease = Speed Increase -blocks.range = Range -blocks.drilltier = Drillables -blocks.drillspeed = Base Drill Speed -blocks.boosteffect = Boost Effect -blocks.maxunits = Max Active Units -blocks.health = Health -blocks.buildtime = Build Time -blocks.maxconsecutive = Max Consecutive -blocks.buildcost = Build Cost -blocks.inaccuracy = Inaccuracy -blocks.shots = Shots -blocks.reload = Shots/Second -blocks.ammo = Ammo -blocks.shieldhealth = Shield Health -blocks.cooldowntime = Cooldown Time + +stat.input = Input +stat.output = Output +stat.booster = Booster +stat.tiles = Required Tiles +stat.affinities = Affinities +stat.powercapacity = Power Capacity +stat.powershot = Power/Shot +stat.damage = Damage +stat.targetsair = Targets Air +stat.targetsground = Targets Ground +stat.itemsmoved = Move Speed +stat.launchtime = Time Between Launches +stat.shootrange = Range +stat.size = Size +stat.displaysize = Display Size +stat.liquidcapacity = Liquid Capacity +stat.powerrange = Power Range +stat.linkrange = Link Range +stat.instructions = Instructions +stat.powerconnections = Max Connections +stat.poweruse = Power Use +stat.powerdamage = Power/Damage +stat.itemcapacity = Item Capacity +stat.memorycapacity = Memory Capacity +stat.basepowergeneration = Base Power Generation +stat.productiontime = Production Time +stat.repairtime = Block Full Repair Time +stat.speedincrease = Speed Increase +stat.range = Range +stat.drilltier = Drillables +stat.drillspeed = Base Drill Speed +stat.boosteffect = Boost Effect +stat.maxunits = Max Active Units +stat.health = Health +stat.buildtime = Build Time +stat.maxconsecutive = Max Consecutive +stat.buildcost = Build Cost +stat.inaccuracy = Inaccuracy +stat.shots = Shots +stat.reload = Shots/Second +stat.ammo = Ammo +stat.shieldhealth = Shield Health +stat.cooldowntime = Cooldown Time +stat.explosiveness = Explosiveness +stat.basedeflectchance = Base Deflect Chance +stat.lightningchance = Lightning Chance +stat.lightningdamage = Lightning Damage +stat.flammability = Flammability +stat.radioactivity = Radioactivity +stat.heatcapacity = HeatCapacity +stat.viscosity = Viscosity +stat.temperature = Temperature +stat.speed = Speed +stat.buildspeed = Build Speed +stat.minespeed = Mine Speed +stat.minetier = Mine Tier bar.drilltierreq = Better Drill Required bar.noresources = Missing Resources @@ -695,7 +711,6 @@ setting.linear.name = Linear Filtering setting.hints.name = Hints setting.flow.name = Display Resource Flow Rate setting.buildautopause.name = Auto-Pause Building -setting.mapcenter.name = Auto Center Map To Player setting.animatedwater.name = Animated Fluids setting.animatedshields.name = Animated Shields setting.antialias.name = Antialias[lightgray] (requires restart)[] @@ -729,7 +744,6 @@ setting.fullscreen.name = Fullscreen setting.borderlesswindow.name = Borderless Window[lightgray] (restart may be required) setting.fps.name = Show FPS & Ping setting.smoothcamera.name = Smooth Camera -setting.blockselectkeys.name = Show Block Select Keys setting.vsync.name = VSync setting.pixelate.name = Pixelate setting.minimap.name = Show Minimap @@ -876,6 +890,7 @@ content.item.name = Items content.liquid.name = Liquids content.unit.name = Units content.block.name = Blocks + item.copper.name = Copper item.lead.name = Lead item.coal.name = Coal @@ -897,23 +912,6 @@ liquid.slag.name = Slag liquid.oil.name = Oil liquid.cryofluid.name = Cryofluid -item.explosiveness = [lightgray]Explosiveness: {0}% -item.flammability = [lightgray]Flammability: {0}% -item.radioactivity = [lightgray]Radioactivity: {0}% - -unit.health = [lightgray]Health: {0} -unit.speed = [lightgray]Speed: {0} -unit.weapon = [lightgray]Weapon: {0} -unit.itemcapacity = [lightgray]Item Capacity: {0} -unit.minespeed = [lightgray]Mining Speed: {0}% -unit.minepower = [lightgray]Mining Power: {0} -unit.ability = [lightgray]Ability: {0} -unit.buildspeed = [lightgray]Building Speed: {0}% - -liquid.heatcapacity = [lightgray]Heat Capacity: {0} -liquid.viscosity = [lightgray]Viscosity: {0} -liquid.temperature = [lightgray]Temperature: {0} - unit.dagger.name = Dagger unit.mace.name = Mace unit.fortress.name = Fortress @@ -1325,5 +1323,4 @@ block.cyclone.description = A large anti-air and anti-ground turret. Fires explo block.spectre.description = A massive dual-barreled cannon. Shoots large armor-piercing bullets at air and ground targets. block.meltdown.description = A massive laser cannon. Charges and fires a persistent laser beam at nearby enemies. Requires coolant to operate. block.repair-point.description = Continuously heals the closest damaged unit in its vicinity. -block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. - +block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. \ No newline at end of file diff --git a/core/assets/bundles/bundle_be.properties b/core/assets/bundles/bundle_be.properties index f62aba4377..590d8094f2 100644 --- a/core/assets/bundles/bundle_be.properties +++ b/core/assets/bundles/bundle_be.properties @@ -570,49 +570,49 @@ info.title = Інфармацыя error.title = [crimson]Адбылася памылка error.crashtitle = Адбылася памылка unit.nobuild = [scarlet]Unit can't build -blocks.input = Уваход -blocks.output = Выхад -blocks.booster = Паскаральнік -blocks.tiles = Неабходныя пліткі -blocks.affinities = Павелічэнне эфектыўнасці +stat.input = Уваход +stat.output = Выхад +stat.booster = Паскаральнік +stat.tiles = Неабходныя пліткі +stat.affinities = Павелічэнне эфектыўнасці block.unknown = [lightgray]??? -blocks.powercapacity = Умяшчальнасць энергіі -blocks.powershot = Энергія/Выстрэл -blocks.damage = Страты -blocks.targetsair = Паветраныя мэты -blocks.targetsground = Наземныя мэты -blocks.itemsmoved = Хуткасць перамяшчэння -blocks.launchtime = Інтэрвал запускаў -blocks.shootrange = Радыус дзеяння -blocks.size = Памер -blocks.displaysize = Display Size -blocks.liquidcapacity = Умяшчальнасць вадкасці -blocks.powerrange = Далёкасць перадачы энергіі -blocks.linkrange = Link Range -blocks.instructions = Instructions -blocks.powerconnections = Колькасць злучэнняў -blocks.poweruse = Спажывае энергіі -blocks.powerdamage = Энергія/страты -blocks.itemcapacity = Умяшчальнасць прадметаў -blocks.basepowergeneration = Базавая генерацыя энергіі -blocks.productiontime = Час вытворчасці -blocks.repairtime = Час поўнай рэгенерацыі -blocks.speedincrease = Павелічэнне хуткасці -blocks.range = Радыус дзеяння -blocks.drilltier = Бурит -blocks.drillspeed = Базавая хуткасць свідравання -blocks.boosteffect = паскараўся эфект -blocks.maxunits = Максімальная колькасць актыўных адзінак -blocks.health = Здароўе -blocks.buildtime = Час будаўніцтва -blocks.maxconsecutive = Max Consecutive -blocks.buildcost = Кошт будаўніцтва -blocks.inaccuracy = Роскід -blocks.shots = Стрэлы -blocks.reload = Стрэлы/секунду -blocks.ammo = Боепрыпасы -blocks.shieldhealth = Shield Health -blocks.cooldowntime = Cooldown Time +stat.powercapacity = Умяшчальнасць энергіі +stat.powershot = Энергія/Выстрэл +stat.damage = Страты +stat.targetsair = Паветраныя мэты +stat.targetsground = Наземныя мэты +stat.itemsmoved = Хуткасць перамяшчэння +stat.launchtime = Інтэрвал запускаў +stat.shootrange = Радыус дзеяння +stat.size = Памер +stat.displaysize = Display Size +stat.liquidcapacity = Умяшчальнасць вадкасці +stat.powerrange = Далёкасць перадачы энергіі +stat.linkrange = Link Range +stat.instructions = Instructions +stat.powerconnections = Колькасць злучэнняў +stat.poweruse = Спажывае энергіі +stat.powerdamage = Энергія/страты +stat.itemcapacity = Умяшчальнасць прадметаў +stat.basepowergeneration = Базавая генерацыя энергіі +stat.productiontime = Час вытворчасці +stat.repairtime = Час поўнай рэгенерацыі +stat.speedincrease = Павелічэнне хуткасці +stat.range = Радыус дзеяння +stat.drilltier = Бурит +stat.drillspeed = Базавая хуткасць свідравання +stat.boosteffect = паскараўся эфект +stat.maxunits = Максімальная колькасць актыўных адзінак +stat.health = Здароўе +stat.buildtime = Час будаўніцтва +stat.maxconsecutive = Max Consecutive +stat.buildcost = Кошт будаўніцтва +stat.inaccuracy = Роскід +stat.shots = Стрэлы +stat.reload = Стрэлы/секунду +stat.ammo = Боепрыпасы +stat.shieldhealth = Shield Health +stat.cooldowntime = Cooldown Time bar.drilltierreq = Патрабуецца свідар лепей bar.noresources = Missing Resources @@ -1302,4 +1302,4 @@ block.cyclone.description = Вялікая турэль, якая можа ве block.spectre.description = Масіўная двуствольное гармата. Страляе буйнымі бранябойнымі кулямі па паветраных і наземных мэтах. block.meltdown.description = Масіўная лазерная гармата. Зараджае і страляе пастаянным лазерным прамянём ў бліжэйшых ворагаў. Патрабуецца астуджальная вадкасць для працы. block.repair-point.description = Бесперапынна лечыць бліжэйшую пашкоджаную баявую адзінку або мех у сваім радыусе. -block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. +block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. \ No newline at end of file diff --git a/core/assets/bundles/bundle_cs.properties b/core/assets/bundles/bundle_cs.properties index 8bcd567380..f9f98951dc 100644 --- a/core/assets/bundles/bundle_cs.properties +++ b/core/assets/bundles/bundle_cs.properties @@ -572,50 +572,50 @@ info.title = Informace error.title = [scarlet]Objevila se chyba[] error.crashtitle = Objevila se chyba unit.nobuild = [scarlet]Jednotka nemůže stavět -blocks.input = Vstup -blocks.output = Výstup -blocks.booster = Posilovač -blocks.tiles = Vyžadované dlaždice -blocks.affinities = Synergie +stat.input = Vstup +stat.output = Výstup +stat.booster = Posilovač +stat.tiles = Vyžadované dlaždice +stat.affinities = Synergie block.unknown = [lightgray]???[] -blocks.powercapacity = Kapacita energie -blocks.powershot = Energie na 1 výstřel -blocks.damage = Poškození -blocks.targetsair = Zaměřuje vzdušné jednotky -blocks.targetsground = Zaměřuje pozemní jednotky -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í -blocks.itemcapacity = Kapacita předmětů -blocks.memorycapacity = Kapacita paměti -blocks.basepowergeneration = Základní generování energie -blocks.productiontime = Čas produkce -blocks.repairtime = Čas do úplné opravy -blocks.speedincrease = Zvýšení rychlosti -blocks.range = Dosah -blocks.drilltier = Lze těžit -blocks.drillspeed = Základní rychlost vrtu -blocks.boosteffect = Účinek posílení -blocks.maxunits = Nejvýše aktivních jednotek -blocks.health = Životy -blocks.buildtime = Čas stavby -blocks.maxconsecutive = Nejvýše po sobě -blocks.buildcost = Cena stavby -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í +stat.powercapacity = Kapacita energie +stat.powershot = Energie na 1 výstřel +stat.damage = Poškození +stat.targetsair = Zaměřuje vzdušné jednotky +stat.targetsground = Zaměřuje pozemní jednotky +stat.itemsmoved = Rychlost pohybu +stat.launchtime = Čas mezi vysláním +stat.shootrange = Dostřel +stat.size = Velikost +stat.displaysize = Velikost zobrazovače +stat.liquidcapacity = Kapacita kapalin +stat.powerrange = Rozsah energie +stat.linkrange = Dosah napojení +stat.instructions = Instrukce +stat.powerconnections = Nejvyšší počet spojení +stat.poweruse = Spotřeba energie +stat.powerdamage = Energie na jednotku poškození +stat.itemcapacity = Kapacita předmětů +stat.memorycapacity = Kapacita paměti +stat.basepowergeneration = Základní generování energie +stat.productiontime = Čas produkce +stat.repairtime = Čas do úplné opravy +stat.speedincrease = Zvýšení rychlosti +stat.range = Dosah +stat.drilltier = Lze těžit +stat.drillspeed = Základní rychlost vrtu +stat.boosteffect = Účinek posílení +stat.maxunits = Nejvýše aktivních jednotek +stat.health = Životy +stat.buildtime = Čas stavby +stat.maxconsecutive = Nejvýše po sobě +stat.buildcost = Cena stavby +stat.inaccuracy = Nepřesnost +stat.shots = Střely +stat.reload = Střel za 1s +stat.ammo = Střelivo +stat.shieldhealth = Zdraví štítu +stat.cooldowntime = Čas na zchladnutí bar.drilltierreq = Je vyžadován lepší vrt bar.noresources = Chybějí zdroje @@ -1312,4 +1312,4 @@ block.cyclone.description = Velká protiletecká a protipozemní střílna. Pál block.spectre.description = Velká střílna s kanónem s dvěma hlavněmi. Střílí velké náboje, které pronikají brněním jak pozemních, tak vzdušných nepřátelských cílů. block.meltdown.description = Masivní laserový kanón. Nabije se a pak pálí nepřetržitý laserový paprsek na nepřátele v okolí. Vyžaduje ke své funkci chlazení. block.repair-point.description = Nepřetržitě léčí nejbližší poškozenou jednotku v poli své působnosti. -block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. +block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. \ No newline at end of file diff --git a/core/assets/bundles/bundle_da.properties b/core/assets/bundles/bundle_da.properties index 0e9d2ce889..b65afebcd3 100644 --- a/core/assets/bundles/bundle_da.properties +++ b/core/assets/bundles/bundle_da.properties @@ -570,49 +570,49 @@ info.title = Info error.title = [crimson]An error has occured error.crashtitle = An error has occured unit.nobuild = [scarlet]Unit can't build -blocks.input = Input -blocks.output = Output -blocks.booster = Booster -blocks.tiles = Required Tiles -blocks.affinities = Affinities +stat.input = Input +stat.output = Output +stat.booster = Booster +stat.tiles = Required Tiles +stat.affinities = Affinities block.unknown = [lightgray]??? -blocks.powercapacity = Power Capacity -blocks.powershot = Power/Shot -blocks.damage = Damage -blocks.targetsair = Targets Air -blocks.targetsground = Targets Ground -blocks.itemsmoved = Move Speed -blocks.launchtime = Time Between Launches -blocks.shootrange = Range -blocks.size = Size -blocks.displaysize = Display Size -blocks.liquidcapacity = Liquid Capacity -blocks.powerrange = Power Range -blocks.linkrange = Link Range -blocks.instructions = Instructions -blocks.powerconnections = Max Connections -blocks.poweruse = Power Use -blocks.powerdamage = Power/Damage -blocks.itemcapacity = Item Capacity -blocks.basepowergeneration = Base Power Generation -blocks.productiontime = Production Time -blocks.repairtime = Block Full Repair Time -blocks.speedincrease = Speed Increase -blocks.range = Range -blocks.drilltier = Drillables -blocks.drillspeed = Base Drill Speed -blocks.boosteffect = Boost Effect -blocks.maxunits = Max Active Units -blocks.health = Health -blocks.buildtime = Build Time -blocks.maxconsecutive = Max Consecutive -blocks.buildcost = Build Cost -blocks.inaccuracy = Inaccuracy -blocks.shots = Shots -blocks.reload = Shots/Second -blocks.ammo = Ammo -blocks.shieldhealth = Shield Health -blocks.cooldowntime = Cooldown Time +stat.powercapacity = Power Capacity +stat.powershot = Power/Shot +stat.damage = Damage +stat.targetsair = Targets Air +stat.targetsground = Targets Ground +stat.itemsmoved = Move Speed +stat.launchtime = Time Between Launches +stat.shootrange = Range +stat.size = Size +stat.displaysize = Display Size +stat.liquidcapacity = Liquid Capacity +stat.powerrange = Power Range +stat.linkrange = Link Range +stat.instructions = Instructions +stat.powerconnections = Max Connections +stat.poweruse = Power Use +stat.powerdamage = Power/Damage +stat.itemcapacity = Item Capacity +stat.basepowergeneration = Base Power Generation +stat.productiontime = Production Time +stat.repairtime = Block Full Repair Time +stat.speedincrease = Speed Increase +stat.range = Range +stat.drilltier = Drillables +stat.drillspeed = Base Drill Speed +stat.boosteffect = Boost Effect +stat.maxunits = Max Active Units +stat.health = Health +stat.buildtime = Build Time +stat.maxconsecutive = Max Consecutive +stat.buildcost = Build Cost +stat.inaccuracy = Inaccuracy +stat.shots = Shots +stat.reload = Shots/Second +stat.ammo = Ammo +stat.shieldhealth = Shield Health +stat.cooldowntime = Cooldown Time bar.drilltierreq = Better Drill Required bar.noresources = Missing Resources @@ -1302,4 +1302,4 @@ block.cyclone.description = A large anti-air and anti-ground turret. Fires explo block.spectre.description = A massive dual-barreled cannon. Shoots large armor-piercing bullets at air and ground targets. block.meltdown.description = A massive laser cannon. Charges and fires a persistent laser beam at nearby enemies. Requires coolant to operate. block.repair-point.description = Continuously heals the closest damaged unit in its vicinity. -block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. +block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. \ No newline at end of file diff --git a/core/assets/bundles/bundle_de.properties b/core/assets/bundles/bundle_de.properties index 67311e3451..5d02ed2f85 100644 --- a/core/assets/bundles/bundle_de.properties +++ b/core/assets/bundles/bundle_de.properties @@ -570,49 +570,49 @@ info.title = Info error.title = [crimson]Ein Fehler ist aufgetreten error.crashtitle = Ein Fehler ist aufgetreten! unit.nobuild = [scarlet]Einheit kann nicht bauen! -blocks.input = Eingang -blocks.output = Ausgang -blocks.booster = Verstärkung -blocks.tiles = Required Tiles -blocks.affinities = Affinities +stat.input = Eingang +stat.output = Ausgang +stat.booster = Verstärkung +stat.tiles = Required Tiles +stat.affinities = Affinities block.unknown = [lightgray]??? -blocks.powercapacity = Kapazität -blocks.powershot = Stromverbrauch/Schuss -blocks.damage = Schaden -blocks.targetsair = Visiert Lufteinheiten an -blocks.targetsground = Visiert Bodeneinheiten an -blocks.itemsmoved = Bewegungsgeschwindigkeit -blocks.launchtime = Zeit zwischen Starts -blocks.shootrange = Reichweite -blocks.size = Größe -blocks.displaysize = Display Size -blocks.liquidcapacity = Flüssigkeitskapazität -blocks.powerrange = Stromreichweite -blocks.linkrange = Link Range -blocks.instructions = Instructions -blocks.powerconnections = Maximale Stromverbindungen -blocks.poweruse = Stromverbrauch -blocks.powerdamage = Stromverbrauch/Schadenspunkt -blocks.itemcapacity = Materialkapazität -blocks.basepowergeneration = Basis-Stromerzeugung -blocks.productiontime = Produktionszeit -blocks.repairtime = Zeit zur vollständigen Reparatur -blocks.speedincrease = Geschwindigkeitserhöhung -blocks.range = Reichweite -blocks.drilltier = Abbaubare Erze -blocks.drillspeed = Bohrgeschwindigkeit -blocks.boosteffect = Verstärkungseffekt -blocks.maxunits = Max. aktive Einheiten -blocks.health = Lebenspunkte -blocks.buildtime = Baudauer -blocks.maxconsecutive = Max Consecutive -blocks.buildcost = Baukosten -blocks.inaccuracy = Ungenauigkeit -blocks.shots = Schüsse -blocks.reload = Schüsse/Sekunde -blocks.ammo = Munition -blocks.shieldhealth = Shield Health -blocks.cooldowntime = Cooldown Time +stat.powercapacity = Kapazität +stat.powershot = Stromverbrauch/Schuss +stat.damage = Schaden +stat.targetsair = Visiert Lufteinheiten an +stat.targetsground = Visiert Bodeneinheiten an +stat.itemsmoved = Bewegungsgeschwindigkeit +stat.launchtime = Zeit zwischen Starts +stat.shootrange = Reichweite +stat.size = Größe +stat.displaysize = Display Size +stat.liquidcapacity = Flüssigkeitskapazität +stat.powerrange = Stromreichweite +stat.linkrange = Link Range +stat.instructions = Instructions +stat.powerconnections = Maximale Stromverbindungen +stat.poweruse = Stromverbrauch +stat.powerdamage = Stromverbrauch/Schadenspunkt +stat.itemcapacity = Materialkapazität +stat.basepowergeneration = Basis-Stromerzeugung +stat.productiontime = Produktionszeit +stat.repairtime = Zeit zur vollständigen Reparatur +stat.speedincrease = Geschwindigkeitserhöhung +stat.range = Reichweite +stat.drilltier = Abbaubare Erze +stat.drillspeed = Bohrgeschwindigkeit +stat.boosteffect = Verstärkungseffekt +stat.maxunits = Max. aktive Einheiten +stat.health = Lebenspunkte +stat.buildtime = Baudauer +stat.maxconsecutive = Max Consecutive +stat.buildcost = Baukosten +stat.inaccuracy = Ungenauigkeit +stat.shots = Schüsse +stat.reload = Schüsse/Sekunde +stat.ammo = Munition +stat.shieldhealth = Shield Health +stat.cooldowntime = Cooldown Time bar.drilltierreq = Besserer Bohrer Benötigt bar.noresources = Missing Resources @@ -1302,4 +1302,4 @@ block.cyclone.description = Ein großer Schnellfeuer-Geschützturm. block.spectre.description = Ein großer Geschützturm, der zwei starke Schüsse gleichzeitig abfeuert. block.meltdown.description = Ein großer Geschützturm, der starke Strahlen mit großer Reichweite abfeuert. block.repair-point.description = Heilt durchgehend die nächste befreundete, beschädigte Einheit in der Umgebung. -block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. +block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. \ No newline at end of file diff --git a/core/assets/bundles/bundle_es.properties b/core/assets/bundles/bundle_es.properties index 8c538a6974..6c24039d7e 100644 --- a/core/assets/bundles/bundle_es.properties +++ b/core/assets/bundles/bundle_es.properties @@ -2,14 +2,14 @@ credits.text = Creado por [royal]Anuken[] - [sky]anukendev@gmail.com[] credits = Créditos contributors = Traductores y Contribuidores discord = ¡Únete al Discord de Mindustry! -link.discord.description = La sala oficial del Discord de Mindustry +link.discord.description = El servidor official de Discord de Mindustry link.reddit.description = El subreddit de Mindustry link.github.description = Código fuente del juego link.changelog.description = Lista de actualizaciones -link.dev-builds.description = Versiones de desarrollo inestables +link.dev-builds.description = Versiones en desarrollo inestables link.trello.description = Tablero de Trello oficial para las características planificadas link.itch.io.description = itch.io es la página donde podes descargar las versiones para PC y web -link.google-play.description = Ficha en la Google Play Store +link.google-play.description = Página de Mindustry en Google Play Store link.f-droid.description = Página de F-Droid del juego link.wiki.description = Wiki oficial de Mindustry link.suggestions.description = Sugerir nuevas funciones @@ -18,7 +18,7 @@ screenshot = Captura de pantalla guardada en {0} screenshot.invalid = Mapa demasiado grande, no hay suficiente memoria para la captura de pantalla. gameover = Tu núcleo ha sido destruido. gameover.pvp = ¡El equipo[accent] {0}[] ha ganado! -highscore = [accent]¡Nueva mejor puntuación! +highscore = [accent]¡Nuevo récord de puntuación! copied = Copiado. indev.popup = [accent]v6[] is currently in [accent]alpha[].\n[lightgray]This means:[]\n[scarlet]- The campaign is completely unfinished[]\n- Content is missing\n - Most [scarlet]Unit AI[] does not work properly\n- Many units are unfinished\n- Everything you see is subject to change or removal.\n\nReport bugs or crashes on [accent]Github[]. indev.notready = This part of the game isn't ready yet @@ -38,12 +38,12 @@ be.ignore = Ignorar be.noupdates = No se encontraron actualizaciones. be.check = Revisando actualizaciones -schematic = Esquemático -schematic.add = Guardar esquemático... -schematics = Esquemáticos -schematic.replace = Un esquemático con ese nombre ya existe. ¿Deseas remplazarlo? -schematic.exists = Un esquemático con ese nombre ya existe. -schematic.import = Importar esquemático... +schematic = Esquema +schematic.add = Guardar esquema... +schematics = Esquemas +schematic.replace = Un esquema con ese nombre ya existe. ¿Deseas remplazarlo? +schematic.exists = Un esquema con ese nombre ya existe. +schematic.import = Importar esquema... schematic.exportfile = Exportar archivo schematic.importfile = Importar archivo schematic.browseworkshop = Buscar en el Steam Workshop @@ -570,49 +570,49 @@ info.title = [accent]Información error.title = [crimson]Un error ha ocurrido. error.crashtitle = Un error ha ocurrido. unit.nobuild = [scarlet]Unit can't build -blocks.input = Entrada -blocks.output = Salida -blocks.booster = Potenciador -blocks.tiles = Tiles requeridos -blocks.affinities = Afinidades +stat.input = Entrada +stat.output = Salida +stat.booster = Potenciador +stat.tiles = Tiles requeridos +stat.affinities = Afinidades block.unknown = [lightgray]??? -blocks.powercapacity = Capacidad de Energía -blocks.powershot = Energía/Disparo -blocks.damage = Daño -blocks.targetsair = Apunta al Aire -blocks.targetsground = Apunta a Tierra -blocks.itemsmoved = Velocidad de movimiento -blocks.launchtime = Tiempo entre lanzamientos -blocks.shootrange = Rango de Disparo -blocks.size = Tamaño -blocks.displaysize = Display Size -blocks.liquidcapacity = Capacidad de Líquidos -blocks.powerrange = Rango de Energía -blocks.linkrange = Link Range -blocks.instructions = Instructions -blocks.powerconnections = Conexiones maximas -blocks.poweruse = Consumo de Energía -blocks.powerdamage = Energía/Daño -blocks.itemcapacity = Capacidad de Objetos -blocks.basepowergeneration = Generación de energía base -blocks.productiontime = Tiempo de producción -blocks.repairtime = Tiempo para Reparar Bloque Completamente -blocks.speedincrease = Aumento de Velocidad -blocks.range = Rango -blocks.drilltier = Taladrables -blocks.drillspeed = Velocidad Base del Taladro -blocks.boosteffect = Efecto del Potenciador -blocks.maxunits = Máximo de Unidades Activas -blocks.health = Vida -blocks.buildtime = Tiempo de construcción -blocks.maxconsecutive = Max Consecutive -blocks.buildcost = Coste de construcción -blocks.inaccuracy = Imprecisión -blocks.shots = Disparos -blocks.reload = Recarga -blocks.ammo = Munición -blocks.shieldhealth = Shield Health -blocks.cooldowntime = Cooldown Time +stat.powercapacity = Capacidad de Energía +stat.powershot = Energía/Disparo +stat.damage = Daño +stat.targetsair = Apunta al Aire +stat.targetsground = Apunta a Tierra +stat.itemsmoved = Velocidad de movimiento +stat.launchtime = Tiempo entre lanzamientos +stat.shootrange = Rango de Disparo +stat.size = Tamaño +stat.displaysize = Display Size +stat.liquidcapacity = Capacidad de Líquidos +stat.powerrange = Rango de Energía +stat.linkrange = Link Range +stat.instructions = Instructions +stat.powerconnections = Conexiones maximas +stat.poweruse = Consumo de Energía +stat.powerdamage = Energía/Daño +stat.itemcapacity = Capacidad de Objetos +stat.basepowergeneration = Generación de energía base +stat.productiontime = Tiempo de producción +stat.repairtime = Tiempo para Reparar Bloque Completamente +stat.speedincrease = Aumento de Velocidad +stat.range = Rango +stat.drilltier = Taladrables +stat.drillspeed = Velocidad Base del Taladro +stat.boosteffect = Efecto del Potenciador +stat.maxunits = Máximo de Unidades Activas +stat.health = Vida +stat.buildtime = Tiempo de construcción +stat.maxconsecutive = Max Consecutive +stat.buildcost = Coste de construcción +stat.inaccuracy = Imprecisión +stat.shots = Disparos +stat.reload = Recarga +stat.ammo = Munición +stat.shieldhealth = Shield Health +stat.cooldowntime = Cooldown Time bar.drilltierreq = Se requiere un mejor taladro. bar.noresources = Missing Resources @@ -1302,4 +1302,4 @@ block.cyclone.description = Una torre grande anti-aérea y anti-terrestre. Dispa block.spectre.description = Un cañon masivo de dos barriles. Dispara balas perforantes a objetivos de aire y tierra. block.meltdown.description = Un cañon láser masivo. Carga y dispara un rayo láser constante a enemigos cercanos. Requiere enfriamiento para operar. block.repair-point.description = Repara la unidad dañada más cercana a su alrededor. -block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. +block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. \ No newline at end of file diff --git a/core/assets/bundles/bundle_et.properties b/core/assets/bundles/bundle_et.properties index d5ac8cfb74..62b0002df6 100644 --- a/core/assets/bundles/bundle_et.properties +++ b/core/assets/bundles/bundle_et.properties @@ -570,49 +570,49 @@ info.title = Info error.title = [crimson]Viga error.crashtitle = Viga unit.nobuild = [scarlet]Unit can't build -blocks.input = Sisend -blocks.output = Väljund -blocks.booster = Kiirendaja -blocks.tiles = Required Tiles -blocks.affinities = Affinities +stat.input = Sisend +stat.output = Väljund +stat.booster = Kiirendaja +stat.tiles = Required Tiles +stat.affinities = Affinities block.unknown = [lightgray]??? -blocks.powercapacity = Energiamahtuvus -blocks.powershot = Energia ühikut/lasu kohta -blocks.damage = Hävituspunkte -blocks.targetsair = Sihib õhku -blocks.targetsground = Sihib maapinnale -blocks.itemsmoved = Transportimise kiirus -blocks.launchtime = Aeg lendutõusude vahel -blocks.shootrange = Ulatus -blocks.size = Suurus -blocks.displaysize = Display Size -blocks.liquidcapacity = Vedelike mahutavus -blocks.powerrange = Energia ulatus -blocks.linkrange = Link Range -blocks.instructions = Instructions -blocks.powerconnections = Max Connections -blocks.poweruse = Energiatarve -blocks.powerdamage = Energiatarve hävituspunkti kohta -blocks.itemcapacity = Ressursside mahutavus -blocks.basepowergeneration = Energiatootlus -blocks.productiontime = Tootmisaeg -blocks.repairtime = Täieliku parandamise aeg -blocks.speedincrease = Kiiruse suurenemine -blocks.range = Ulatus -blocks.drilltier = Kaevandatav -blocks.drillspeed = Puurimise kiirus -blocks.boosteffect = Kiirendaja mõju -blocks.maxunits = Maks. aktiivseid väeüksuseid -blocks.health = Elud -blocks.buildtime = Ehitamise aeg -blocks.maxconsecutive = Max Consecutive -blocks.buildcost = Ehitamise maksumus -blocks.inaccuracy = Ebatäpsus -blocks.shots = Laske -blocks.reload = Lasku/s -blocks.ammo = Laskemoon -blocks.shieldhealth = Shield Health -blocks.cooldowntime = Cooldown Time +stat.powercapacity = Energiamahtuvus +stat.powershot = Energia ühikut/lasu kohta +stat.damage = Hävituspunkte +stat.targetsair = Sihib õhku +stat.targetsground = Sihib maapinnale +stat.itemsmoved = Transportimise kiirus +stat.launchtime = Aeg lendutõusude vahel +stat.shootrange = Ulatus +stat.size = Suurus +stat.displaysize = Display Size +stat.liquidcapacity = Vedelike mahutavus +stat.powerrange = Energia ulatus +stat.linkrange = Link Range +stat.instructions = Instructions +stat.powerconnections = Max Connections +stat.poweruse = Energiatarve +stat.powerdamage = Energiatarve hävituspunkti kohta +stat.itemcapacity = Ressursside mahutavus +stat.basepowergeneration = Energiatootlus +stat.productiontime = Tootmisaeg +stat.repairtime = Täieliku parandamise aeg +stat.speedincrease = Kiiruse suurenemine +stat.range = Ulatus +stat.drilltier = Kaevandatav +stat.drillspeed = Puurimise kiirus +stat.boosteffect = Kiirendaja mõju +stat.maxunits = Maks. aktiivseid väeüksuseid +stat.health = Elud +stat.buildtime = Ehitamise aeg +stat.maxconsecutive = Max Consecutive +stat.buildcost = Ehitamise maksumus +stat.inaccuracy = Ebatäpsus +stat.shots = Laske +stat.reload = Lasku/s +stat.ammo = Laskemoon +stat.shieldhealth = Shield Health +stat.cooldowntime = Cooldown Time bar.drilltierreq = Nõuab paremat puuri bar.noresources = Missing Resources @@ -1302,4 +1302,4 @@ block.cyclone.description = Suur lendavate ja maapealsete väeüksuste vastane k block.spectre.description = Massiivne kaheraudne kahur, mis tulistab soomuskatteid läbistavaid mürske nii lendavate kui ka maapealsete väeüksuste pihta. block.meltdown.description = Massiivne laserkahur, mis tekitab püsiva energiakiire. Vajab töötamiseks jahutusvedelikku. block.repair-point.description = Parandab kõige lähemal asuvat liitlaste väeüksust. -block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. +block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. \ No newline at end of file diff --git a/core/assets/bundles/bundle_eu.properties b/core/assets/bundles/bundle_eu.properties index 5ef1a41b71..7afde97031 100644 --- a/core/assets/bundles/bundle_eu.properties +++ b/core/assets/bundles/bundle_eu.properties @@ -570,49 +570,49 @@ info.title = Informazioa error.title = [crimson]Errore bat gertatu da error.crashtitle = Errore bat gertatu da unit.nobuild = [scarlet]Unit can't build -blocks.input = Sarrera -blocks.output = Irteera -blocks.booster = Indargarria -blocks.tiles = Required Tiles -blocks.affinities = Affinities +stat.input = Sarrera +stat.output = Irteera +stat.booster = Indargarria +stat.tiles = Required Tiles +stat.affinities = Affinities block.unknown = [lightgray]??? -blocks.powercapacity = Energia-edukiera -blocks.powershot = Energia/tiroko -blocks.damage = Kaltea -blocks.targetsair = Airera tirokatzen du -blocks.targetsground = Lurrera tirokatzen du -blocks.itemsmoved = Garraio-abiadura -blocks.launchtime = Egozketen arteko denbora -blocks.shootrange = Irismena -blocks.size = Neurria -blocks.displaysize = Display Size -blocks.liquidcapacity = Likido-edukiera -blocks.powerrange = Energia irismena -blocks.linkrange = Link Range -blocks.instructions = Instructions -blocks.powerconnections = Gehieneko konexioak -blocks.poweruse = Energia-erabilera -blocks.powerdamage = Energia/Kaltea -blocks.itemcapacity = Elementu-edukiera -blocks.basepowergeneration = Oinarrizko energia sorrera -blocks.productiontime = Eraikitze denbora -blocks.repairtime = Blokearen konpontze denbora osoa -blocks.speedincrease = Abiadura areagotzea -blocks.range = Irismena -blocks.drilltier = Ustiagarriak -blocks.drillspeed = Oinarrizko ustiatze-abiadura -blocks.boosteffect = Indartze-efektua -blocks.maxunits = Gehieneko unitate aktiboak -blocks.health = Osasuna -blocks.buildtime = Eraikitze-denbora -blocks.maxconsecutive = Max Consecutive -blocks.buildcost = Eraikitze-kostua -blocks.inaccuracy = Zehazgabetasuna -blocks.shots = Tiroak -blocks.reload = Tiroak/segundoko -blocks.ammo = Munizioa -blocks.shieldhealth = Shield Health -blocks.cooldowntime = Cooldown Time +stat.powercapacity = Energia-edukiera +stat.powershot = Energia/tiroko +stat.damage = Kaltea +stat.targetsair = Airera tirokatzen du +stat.targetsground = Lurrera tirokatzen du +stat.itemsmoved = Garraio-abiadura +stat.launchtime = Egozketen arteko denbora +stat.shootrange = Irismena +stat.size = Neurria +stat.displaysize = Display Size +stat.liquidcapacity = Likido-edukiera +stat.powerrange = Energia irismena +stat.linkrange = Link Range +stat.instructions = Instructions +stat.powerconnections = Gehieneko konexioak +stat.poweruse = Energia-erabilera +stat.powerdamage = Energia/Kaltea +stat.itemcapacity = Elementu-edukiera +stat.basepowergeneration = Oinarrizko energia sorrera +stat.productiontime = Eraikitze denbora +stat.repairtime = Blokearen konpontze denbora osoa +stat.speedincrease = Abiadura areagotzea +stat.range = Irismena +stat.drilltier = Ustiagarriak +stat.drillspeed = Oinarrizko ustiatze-abiadura +stat.boosteffect = Indartze-efektua +stat.maxunits = Gehieneko unitate aktiboak +stat.health = Osasuna +stat.buildtime = Eraikitze-denbora +stat.maxconsecutive = Max Consecutive +stat.buildcost = Eraikitze-kostua +stat.inaccuracy = Zehazgabetasuna +stat.shots = Tiroak +stat.reload = Tiroak/segundoko +stat.ammo = Munizioa +stat.shieldhealth = Shield Health +stat.cooldowntime = Cooldown Time bar.drilltierreq = Zulagailu hobea behar da bar.noresources = Missing Resources @@ -1302,4 +1302,4 @@ block.cyclone.description = Aire zein lurreko defentsarako dorre handia. Torpedo block.spectre.description = Kanoi bikoitz erraldoia. Blindajea zulatu dezaketen bala handiak tirokatzen ditu aireko zein lurreko xedeei. block.meltdown.description = Laser kanoi erraldoia. Etengabeko laser izpi bat kargatu eta jauritzen die inguruko etsaiei. Hozgarria behar du jarduteko. block.repair-point.description = Etengabe konpontzen du inguruko kaltetutako unitate hurbilena. -block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. +block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. \ No newline at end of file diff --git a/core/assets/bundles/bundle_fi.properties b/core/assets/bundles/bundle_fi.properties index ae351b8764..30a231333c 100644 --- a/core/assets/bundles/bundle_fi.properties +++ b/core/assets/bundles/bundle_fi.properties @@ -570,49 +570,49 @@ info.title = Informaatio error.title = [crimson]An error has occured error.crashtitle = An error has occured unit.nobuild = [scarlet]Unit can't build -blocks.input = Sisääntulo -blocks.output = Ulostulo -blocks.booster = Tehostaja -blocks.tiles = Required Tiles -blocks.affinities = Affinities +stat.input = Sisääntulo +stat.output = Ulostulo +stat.booster = Tehostaja +stat.tiles = Required Tiles +stat.affinities = Affinities block.unknown = [lightgray]??? -blocks.powercapacity = Energiakapasiteetti -blocks.powershot = Energiaa/Ammus -blocks.damage = Vahinko -blocks.targetsair = Hyökkää ilmaan -blocks.targetsground = Hyökkää maahan -blocks.itemsmoved = Liikkumisnopeus -blocks.launchtime = Aika laukaisujen välillä -blocks.shootrange = Kantama -blocks.size = Koko -blocks.displaysize = Display Size -blocks.liquidcapacity = Nestekapasiteetti -blocks.powerrange = Energiakantama -blocks.linkrange = Link Range -blocks.instructions = Instructions -blocks.powerconnections = Maksimimäärä yhdistyksiä -blocks.poweruse = Energian käyttö -blocks.powerdamage = Energia/Vahinko -blocks.itemcapacity = Tavarakapasiteetti -blocks.basepowergeneration = Perus energiantuotto -blocks.productiontime = Tuotantoaika -blocks.repairtime = Kokonaisen palikan korjausaika -blocks.speedincrease = Nopeuden kasvu -blocks.range = Etäisyys -blocks.drilltier = Porattavat -blocks.drillspeed = Kanta Poran Nopeus -blocks.boosteffect = Tehostamisem vaikutus -blocks.maxunits = Maksimimäärä yksikköjä -blocks.health = Elämäpisteet -blocks.buildtime = Rakentamisaika -blocks.maxconsecutive = Max Consecutive -blocks.buildcost = Rakentamishinta -blocks.inaccuracy = Epätarkkuus -blocks.shots = Ammusta -blocks.reload = Ammusta/sekunnissa -blocks.ammo = Ammus -blocks.shieldhealth = Shield Health -blocks.cooldowntime = Cooldown Time +stat.powercapacity = Energiakapasiteetti +stat.powershot = Energiaa/Ammus +stat.damage = Vahinko +stat.targetsair = Hyökkää ilmaan +stat.targetsground = Hyökkää maahan +stat.itemsmoved = Liikkumisnopeus +stat.launchtime = Aika laukaisujen välillä +stat.shootrange = Kantama +stat.size = Koko +stat.displaysize = Display Size +stat.liquidcapacity = Nestekapasiteetti +stat.powerrange = Energiakantama +stat.linkrange = Link Range +stat.instructions = Instructions +stat.powerconnections = Maksimimäärä yhdistyksiä +stat.poweruse = Energian käyttö +stat.powerdamage = Energia/Vahinko +stat.itemcapacity = Tavarakapasiteetti +stat.basepowergeneration = Perus energiantuotto +stat.productiontime = Tuotantoaika +stat.repairtime = Kokonaisen palikan korjausaika +stat.speedincrease = Nopeuden kasvu +stat.range = Etäisyys +stat.drilltier = Porattavat +stat.drillspeed = Kanta Poran Nopeus +stat.boosteffect = Tehostamisem vaikutus +stat.maxunits = Maksimimäärä yksikköjä +stat.health = Elämäpisteet +stat.buildtime = Rakentamisaika +stat.maxconsecutive = Max Consecutive +stat.buildcost = Rakentamishinta +stat.inaccuracy = Epätarkkuus +stat.shots = Ammusta +stat.reload = Ammusta/sekunnissa +stat.ammo = Ammus +stat.shieldhealth = Shield Health +stat.cooldowntime = Cooldown Time bar.drilltierreq = Parempi pora vaadittu bar.noresources = Missing Resources @@ -1302,4 +1302,4 @@ block.cyclone.description = A large anti-air and anti-ground turret. Fires explo block.spectre.description = A massive dual-barreled cannon. Shoots large armor-piercing bullets at air and ground targets. block.meltdown.description = A massive laser cannon. Charges and fires a persistent laser beam at nearby enemies. Requires coolant to operate. block.repair-point.description = Continuously heals the closest damaged unit in its vicinity. -block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. +block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. \ No newline at end of file diff --git a/core/assets/bundles/bundle_fil.properties b/core/assets/bundles/bundle_fil.properties index 86c3c0737a..0fa6608526 100644 --- a/core/assets/bundles/bundle_fil.properties +++ b/core/assets/bundles/bundle_fil.properties @@ -570,49 +570,49 @@ info.title = Info error.title = [scarlet]An error has occured error.crashtitle = An error has occured unit.nobuild = [scarlet]Unit can't build -blocks.input = Input -blocks.output = Output -blocks.booster = Booster -blocks.tiles = Required Tiles -blocks.affinities = Affinities +stat.input = Input +stat.output = Output +stat.booster = Booster +stat.tiles = Required Tiles +stat.affinities = Affinities block.unknown = [lightgray]??? -blocks.powercapacity = Power Capacity -blocks.powershot = Power/Shot -blocks.damage = Damage -blocks.targetsair = Targets Air -blocks.targetsground = Targets Ground -blocks.itemsmoved = Move Speed -blocks.launchtime = Time Between Launches -blocks.shootrange = Range -blocks.size = Size -blocks.displaysize = Display Size -blocks.liquidcapacity = Liquid Capacity -blocks.powerrange = Power Range -blocks.linkrange = Link Range -blocks.instructions = Instructions -blocks.powerconnections = Max Connections -blocks.poweruse = Power Use -blocks.powerdamage = Power/Damage -blocks.itemcapacity = Item Capacity -blocks.basepowergeneration = Base Power Generation -blocks.productiontime = Production Time -blocks.repairtime = Block Full Repair Time -blocks.speedincrease = Speed Increase -blocks.range = Range -blocks.drilltier = Drillables -blocks.drillspeed = Base Drill Speed -blocks.boosteffect = Boost Effect -blocks.maxunits = Max Active Units -blocks.health = Health -blocks.buildtime = Build Time -blocks.maxconsecutive = Max Consecutive -blocks.buildcost = Build Cost -blocks.inaccuracy = Inaccuracy -blocks.shots = Shots -blocks.reload = Shots/Second -blocks.ammo = Ammo -blocks.shieldhealth = Shield Health -blocks.cooldowntime = Cooldown Time +stat.powercapacity = Power Capacity +stat.powershot = Power/Shot +stat.damage = Damage +stat.targetsair = Targets Air +stat.targetsground = Targets Ground +stat.itemsmoved = Move Speed +stat.launchtime = Time Between Launches +stat.shootrange = Range +stat.size = Size +stat.displaysize = Display Size +stat.liquidcapacity = Liquid Capacity +stat.powerrange = Power Range +stat.linkrange = Link Range +stat.instructions = Instructions +stat.powerconnections = Max Connections +stat.poweruse = Power Use +stat.powerdamage = Power/Damage +stat.itemcapacity = Item Capacity +stat.basepowergeneration = Base Power Generation +stat.productiontime = Production Time +stat.repairtime = Block Full Repair Time +stat.speedincrease = Speed Increase +stat.range = Range +stat.drilltier = Drillables +stat.drillspeed = Base Drill Speed +stat.boosteffect = Boost Effect +stat.maxunits = Max Active Units +stat.health = Health +stat.buildtime = Build Time +stat.maxconsecutive = Max Consecutive +stat.buildcost = Build Cost +stat.inaccuracy = Inaccuracy +stat.shots = Shots +stat.reload = Shots/Second +stat.ammo = Ammo +stat.shieldhealth = Shield Health +stat.cooldowntime = Cooldown Time bar.drilltierreq = Better Drill Required bar.noresources = Missing Resources @@ -1302,4 +1302,4 @@ block.cyclone.description = A large anti-air and anti-ground turret. Fires explo block.spectre.description = A massive dual-barreled cannon. Shoots large armor-piercing bullets at air and ground targets. block.meltdown.description = A massive laser cannon. Charges and fires a persistent laser beam at nearby enemies. Requires coolant to operate. block.repair-point.description = Continuously heals the closest damaged unit in its vicinity. -block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. +block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. \ No newline at end of file diff --git a/core/assets/bundles/bundle_fr.properties b/core/assets/bundles/bundle_fr.properties index e7749d7347..925465c595 100644 --- a/core/assets/bundles/bundle_fr.properties +++ b/core/assets/bundles/bundle_fr.properties @@ -570,49 +570,49 @@ info.title = Info error.title = [crimson]Une erreur s'est produite error.crashtitle = Une erreur s'est produite unit.nobuild = [scarlet]Cette unité ne peut construire -blocks.input = Entrée -blocks.output = Sortie -blocks.booster = Booster -blocks.tiles = Pré-requis -blocks.affinities = Affinités +stat.input = Entrée +stat.output = Sortie +stat.booster = Booster +stat.tiles = Pré-requis +stat.affinities = Affinités block.unknown = [lightgray]??? -blocks.powercapacity = Capacité d'énergie -blocks.powershot = Énergie/Tir -blocks.damage = Dégâts -blocks.targetsair = Cibles Aériennes -blocks.targetsground = Cibles Terrestres -blocks.itemsmoved = Vitesse de Déplacement -blocks.launchtime = Temps entre chaque lancement -blocks.shootrange = Portée de tir -blocks.size = Taille -blocks.displaysize = Display Size -blocks.liquidcapacity = Capacité liquide -blocks.powerrange = Portée électrique -blocks.linkrange = Link Range -blocks.instructions = Instructions -blocks.powerconnections = Nombre maximal de connections -blocks.poweruse = Énergie utilisée -blocks.powerdamage = Dégâts d'énergie -blocks.itemcapacity = Stockage -blocks.basepowergeneration = Production d'énergie -blocks.productiontime = Durée de production -blocks.repairtime = Durée de réparation complète du Bloc -blocks.speedincrease = Accélération -blocks.range = Portée -blocks.drilltier = Forable -blocks.drillspeed = Vitesse de forage de base -blocks.boosteffect = Effet du Boost -blocks.maxunits = Unités actives max -blocks.health = Santé -blocks.buildtime = Durée de construction -blocks.maxconsecutive = Max Consecutive -blocks.buildcost = Coût de construction -blocks.inaccuracy = Imprécision -blocks.shots = Tirs -blocks.reload = Tirs/Seconde -blocks.ammo = Munitions -blocks.shieldhealth = Shield Health -blocks.cooldowntime = Cooldown Time +stat.powercapacity = Capacité d'énergie +stat.powershot = Énergie/Tir +stat.damage = Dégâts +stat.targetsair = Cibles Aériennes +stat.targetsground = Cibles Terrestres +stat.itemsmoved = Vitesse de Déplacement +stat.launchtime = Temps entre chaque lancement +stat.shootrange = Portée de tir +stat.size = Taille +stat.displaysize = Display Size +stat.liquidcapacity = Capacité liquide +stat.powerrange = Portée électrique +stat.linkrange = Link Range +stat.instructions = Instructions +stat.powerconnections = Nombre maximal de connections +stat.poweruse = Énergie utilisée +stat.powerdamage = Dégâts d'énergie +stat.itemcapacity = Stockage +stat.basepowergeneration = Production d'énergie +stat.productiontime = Durée de production +stat.repairtime = Durée de réparation complète du Bloc +stat.speedincrease = Accélération +stat.range = Portée +stat.drilltier = Forable +stat.drillspeed = Vitesse de forage de base +stat.boosteffect = Effet du Boost +stat.maxunits = Unités actives max +stat.health = Santé +stat.buildtime = Durée de construction +stat.maxconsecutive = Max Consecutive +stat.buildcost = Coût de construction +stat.inaccuracy = Imprécision +stat.shots = Tirs +stat.reload = Tirs/Seconde +stat.ammo = Munitions +stat.shieldhealth = Shield Health +stat.cooldowntime = Cooldown Time bar.drilltierreq = Meilleure Foreuse Requise bar.noresources = Missing Resources @@ -1302,4 +1302,4 @@ block.cyclone.description = Une grande tourelle qui tire rapidement des débris block.spectre.description = Une tourelle massive à double cannon et qui tire de puissantes balles perce-blindages simultanément. block.meltdown.description = Une tourelle massive chargeant et tirant de puissants rayons lasers. Nécessite un liquide de refroidissement. block.repair-point.description = Soigne en permanence l'unité endommagée la plus proche à proximité. -block.segment.description = Endommage et détruit les tirs ennemis. Cependant, les lasers ne peuvent pas être ciblés. +block.segment.description = Endommage et détruit les tirs ennemis. Cependant, les lasers ne peuvent pas être ciblés. \ No newline at end of file diff --git a/core/assets/bundles/bundle_fr_BE.properties b/core/assets/bundles/bundle_fr_BE.properties index 02fe5e58b1..869da14c46 100644 --- a/core/assets/bundles/bundle_fr_BE.properties +++ b/core/assets/bundles/bundle_fr_BE.properties @@ -570,49 +570,49 @@ info.title = Info error.title = [crimson]Une erreur s'est produite error.crashtitle = Une erreur s'est produite unit.nobuild = [scarlet]Unit can't build -blocks.input = Ressource(s) requise(s) -blocks.output = Ressource(s) produite(s) -blocks.booster = Booster -blocks.tiles = Required Tiles -blocks.affinities = Affinities +stat.input = Ressource(s) requise(s) +stat.output = Ressource(s) produite(s) +stat.booster = Booster +stat.tiles = Required Tiles +stat.affinities = Affinities block.unknown = [lightgray]Inconnu -blocks.powercapacity = Capacité d'énergie -blocks.powershot = Énergie/Tir -blocks.damage = Damage -blocks.targetsair = Cible les unités aériennes -blocks.targetsground = Cible les unités terrestres -blocks.itemsmoved = Vitesse de déplacement -blocks.launchtime = Temps entre chaque lancement -blocks.shootrange = Portée -blocks.size = Taille -blocks.displaysize = Display Size -blocks.liquidcapacity = Capacité en liquide -blocks.powerrange = Distance de transmission -blocks.linkrange = Link Range -blocks.instructions = Instructions -blocks.powerconnections = Max Connections -blocks.poweruse = Énergie utilisée -blocks.powerdamage = Énergie/Dégâts -blocks.itemcapacity = Stockage -blocks.basepowergeneration = Production d'énergie de base -blocks.productiontime = Temps de production -blocks.repairtime = Temps pour la réparation totale du bloc -blocks.speedincrease = Augmentation de la vitesse -blocks.range = Portée -blocks.drilltier = Forable -blocks.drillspeed = Vitesse de forage de base -blocks.boosteffect = Effet boostant -blocks.maxunits = Maximum d'unitée active -blocks.health = Santé -blocks.buildtime = Temps de construction -blocks.maxconsecutive = Max Consecutive -blocks.buildcost = Coût de construction -blocks.inaccuracy = Précision -blocks.shots = Tirs -blocks.reload = Tirs/Seconde -blocks.ammo = Munition -blocks.shieldhealth = Shield Health -blocks.cooldowntime = Cooldown Time +stat.powercapacity = Capacité d'énergie +stat.powershot = Énergie/Tir +stat.damage = Damage +stat.targetsair = Cible les unités aériennes +stat.targetsground = Cible les unités terrestres +stat.itemsmoved = Vitesse de déplacement +stat.launchtime = Temps entre chaque lancement +stat.shootrange = Portée +stat.size = Taille +stat.displaysize = Display Size +stat.liquidcapacity = Capacité en liquide +stat.powerrange = Distance de transmission +stat.linkrange = Link Range +stat.instructions = Instructions +stat.powerconnections = Max Connections +stat.poweruse = Énergie utilisée +stat.powerdamage = Énergie/Dégâts +stat.itemcapacity = Stockage +stat.basepowergeneration = Production d'énergie de base +stat.productiontime = Temps de production +stat.repairtime = Temps pour la réparation totale du bloc +stat.speedincrease = Augmentation de la vitesse +stat.range = Portée +stat.drilltier = Forable +stat.drillspeed = Vitesse de forage de base +stat.boosteffect = Effet boostant +stat.maxunits = Maximum d'unitée active +stat.health = Santé +stat.buildtime = Temps de construction +stat.maxconsecutive = Max Consecutive +stat.buildcost = Coût de construction +stat.inaccuracy = Précision +stat.shots = Tirs +stat.reload = Tirs/Seconde +stat.ammo = Munition +stat.shieldhealth = Shield Health +stat.cooldowntime = Cooldown Time bar.drilltierreq = Better Drill Required bar.noresources = Missing Resources @@ -1302,4 +1302,4 @@ block.cyclone.description = Une grande tourelle à tir rapide. block.spectre.description = Une grande tourelle qui tire deux balles puissantes à la fois. block.meltdown.description = Une grande tourelle qui tire de puissants faisceaux à longue portée. block.repair-point.description = Soigne en permanence l'unité endommagée la plus proche à proximité. -block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. +block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. \ No newline at end of file diff --git a/core/assets/bundles/bundle_hu.properties b/core/assets/bundles/bundle_hu.properties index b28d471cdd..c24c9a24ee 100644 --- a/core/assets/bundles/bundle_hu.properties +++ b/core/assets/bundles/bundle_hu.properties @@ -570,49 +570,49 @@ info.title = Info error.title = [crimson]An error has occured error.crashtitle = An error has occured unit.nobuild = [scarlet]Unit can't build -blocks.input = Input -blocks.output = Output -blocks.booster = Booster -blocks.tiles = Required Tiles -blocks.affinities = Affinities +stat.input = Input +stat.output = Output +stat.booster = Booster +stat.tiles = Required Tiles +stat.affinities = Affinities block.unknown = [lightgray]??? -blocks.powercapacity = Power Capacity -blocks.powershot = Power/Shot -blocks.damage = Damage -blocks.targetsair = Targets Air -blocks.targetsground = Targets Ground -blocks.itemsmoved = Move Speed -blocks.launchtime = Time Between Launches -blocks.shootrange = Range -blocks.size = Size -blocks.displaysize = Display Size -blocks.liquidcapacity = Liquid Capacity -blocks.powerrange = Power Range -blocks.linkrange = Link Range -blocks.instructions = Instructions -blocks.powerconnections = Max Connections -blocks.poweruse = Power Use -blocks.powerdamage = Power/Damage -blocks.itemcapacity = Item Capacity -blocks.basepowergeneration = Base Power Generation -blocks.productiontime = Production Time -blocks.repairtime = Block Full Repair Time -blocks.speedincrease = Speed Increase -blocks.range = Range -blocks.drilltier = Drillables -blocks.drillspeed = Base Drill Speed -blocks.boosteffect = Boost Effect -blocks.maxunits = Max Active Units -blocks.health = Health -blocks.buildtime = Build Time -blocks.maxconsecutive = Max Consecutive -blocks.buildcost = Build Cost -blocks.inaccuracy = Inaccuracy -blocks.shots = Shots -blocks.reload = Shots/Second -blocks.ammo = Ammo -blocks.shieldhealth = Shield Health -blocks.cooldowntime = Cooldown Time +stat.powercapacity = Power Capacity +stat.powershot = Power/Shot +stat.damage = Damage +stat.targetsair = Targets Air +stat.targetsground = Targets Ground +stat.itemsmoved = Move Speed +stat.launchtime = Time Between Launches +stat.shootrange = Range +stat.size = Size +stat.displaysize = Display Size +stat.liquidcapacity = Liquid Capacity +stat.powerrange = Power Range +stat.linkrange = Link Range +stat.instructions = Instructions +stat.powerconnections = Max Connections +stat.poweruse = Power Use +stat.powerdamage = Power/Damage +stat.itemcapacity = Item Capacity +stat.basepowergeneration = Base Power Generation +stat.productiontime = Production Time +stat.repairtime = Block Full Repair Time +stat.speedincrease = Speed Increase +stat.range = Range +stat.drilltier = Drillables +stat.drillspeed = Base Drill Speed +stat.boosteffect = Boost Effect +stat.maxunits = Max Active Units +stat.health = Health +stat.buildtime = Build Time +stat.maxconsecutive = Max Consecutive +stat.buildcost = Build Cost +stat.inaccuracy = Inaccuracy +stat.shots = Shots +stat.reload = Shots/Second +stat.ammo = Ammo +stat.shieldhealth = Shield Health +stat.cooldowntime = Cooldown Time bar.drilltierreq = Better Drill Required bar.noresources = Missing Resources @@ -1302,4 +1302,4 @@ block.cyclone.description = A large anti-air and anti-ground turret. Fires explo block.spectre.description = A massive dual-barreled cannon. Shoots large armor-piercing bullets at air and ground targets. block.meltdown.description = A massive laser cannon. Charges and fires a persistent laser beam at nearby enemies. Requires coolant to operate. block.repair-point.description = Continuously heals the closest damaged unit in its vicinity. -block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. +block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. \ No newline at end of file diff --git a/core/assets/bundles/bundle_in_ID.properties b/core/assets/bundles/bundle_in_ID.properties index d601b9e904..e9f6f933eb 100644 --- a/core/assets/bundles/bundle_in_ID.properties +++ b/core/assets/bundles/bundle_in_ID.properties @@ -572,50 +572,50 @@ info.title = Info error.title = [crimson]Sebuah kesalahan telah terjadi error.crashtitle = Sebuah kesalahan telah terjadi unit.nobuild = [scarlet]Unit tidak dapat membangun -blocks.input = Masukan -blocks.output = Pengeluaran -blocks.booster = Pendorong -blocks.tiles = Kotak yang dibutuhkan -blocks.affinities = Afinitas +stat.input = Masukan +stat.output = Pengeluaran +stat.booster = Pendorong +stat.tiles = Kotak yang dibutuhkan +stat.affinities = Afinitas block.unknown = [lightgray]??? -blocks.powercapacity = Kapasitas Tenaga -blocks.powershot = Tenaga/Tembakan -blocks.damage = Kerusakan -blocks.targetsair = Menargetkan Udara -blocks.targetsground = Menargetkan Darat -blocks.itemsmoved = Kecepatan Gerak -blocks.launchtime = Waktu Diantara Peluncuran -blocks.shootrange = Jarak -blocks.size = Ukuran -blocks.displaysize = Ukuran Tampilan -blocks.liquidcapacity = Kapasitas Zat Cair -blocks.powerrange = Jarak Tenaga -blocks.linkrange = Jarak Tautan -blocks.instructions = Instruksi -blocks.powerconnections = Koneksi Maksimal -blocks.poweruse = Penggunaan Tenaga -blocks.powerdamage = Tenaga/Pukulan -blocks.itemcapacity = Kapasitas Item -blocks.memorycapacity = Kapasitas Memori -blocks.basepowergeneration = Basis Generasi Tenaga -blocks.productiontime = Waktu Produksi -blocks.repairtime = Waktu Memperbaiki Blok Penuh -blocks.speedincrease = Tambahan Kecepatan -blocks.range = Jarak -blocks.drilltier = Sumber Daya yang Bisa di Bor -blocks.drillspeed = Basis Kecepatan Bor -blocks.boosteffect = Efek Pendorong -blocks.maxunits = Maks Unit Aktif -blocks.health = Darah -blocks.buildtime = Waktu Pembuatan -blocks.maxconsecutive = Max Consecutive -blocks.buildcost = Biaya Bangunan -blocks.inaccuracy = Jarak Melenceng -blocks.shots = Tembakan -blocks.reload = Tembakan/Detik -blocks.ammo = Amunisi -blocks.shieldhealth = Shield Health -blocks.cooldowntime = Cooldown Time +stat.powercapacity = Kapasitas Tenaga +stat.powershot = Tenaga/Tembakan +stat.damage = Kerusakan +stat.targetsair = Menargetkan Udara +stat.targetsground = Menargetkan Darat +stat.itemsmoved = Kecepatan Gerak +stat.launchtime = Waktu Diantara Peluncuran +stat.shootrange = Jarak +stat.size = Ukuran +stat.displaysize = Ukuran Tampilan +stat.liquidcapacity = Kapasitas Zat Cair +stat.powerrange = Jarak Tenaga +stat.linkrange = Jarak Tautan +stat.instructions = Instruksi +stat.powerconnections = Koneksi Maksimal +stat.poweruse = Penggunaan Tenaga +stat.powerdamage = Tenaga/Pukulan +stat.itemcapacity = Kapasitas Item +stat.memorycapacity = Kapasitas Memori +stat.basepowergeneration = Basis Generasi Tenaga +stat.productiontime = Waktu Produksi +stat.repairtime = Waktu Memperbaiki Blok Penuh +stat.speedincrease = Tambahan Kecepatan +stat.range = Jarak +stat.drilltier = Sumber Daya yang Bisa di Bor +stat.drillspeed = Basis Kecepatan Bor +stat.boosteffect = Efek Pendorong +stat.maxunits = Maks Unit Aktif +stat.health = Darah +stat.buildtime = Waktu Pembuatan +stat.maxconsecutive = Max Consecutive +stat.buildcost = Biaya Bangunan +stat.inaccuracy = Jarak Melenceng +stat.shots = Tembakan +stat.reload = Tembakan/Detik +stat.ammo = Amunisi +stat.shieldhealth = Shield Health +stat.cooldowntime = Cooldown Time bar.drilltierreq = Membutuhkan Bor yang Lebih Baik bar.noresources = Sumber Daya Tidak Ditemukan @@ -1310,4 +1310,4 @@ block.cyclone.description = Menara penembak beruntun besar. block.spectre.description = Menara besar yang menembak dua peluru kuat sekaligus. block.meltdown.description = Menara besar ini menembak sinar panjang yang kuat. block.repair-point.description = Terus menerus memulihkan unit terluka disekitar. -block.segment.description = Merusakkan dan menghancurkan proyektil yang datang. Proyektil laser tidak akan ditargetkan. +block.segment.description = Merusakkan dan menghancurkan proyektil yang datang. Proyektil laser tidak akan ditargetkan. \ No newline at end of file diff --git a/core/assets/bundles/bundle_it.properties b/core/assets/bundles/bundle_it.properties index c10f894851..5fc3245ed6 100644 --- a/core/assets/bundles/bundle_it.properties +++ b/core/assets/bundles/bundle_it.properties @@ -570,49 +570,49 @@ info.title = Info error.title = [crimson]Si è verificato un errore error.crashtitle = Si è verificato un errore unit.nobuild = [scarlet]L'unità non può costruire -blocks.input = Ingresso -blocks.output = Uscita -blocks.booster = Potenziamenti -blocks.tiles = Blocchi Richiesti -blocks.affinities = Affinità +stat.input = Ingresso +stat.output = Uscita +stat.booster = Potenziamenti +stat.tiles = Blocchi Richiesti +stat.affinities = Affinità block.unknown = [lightgray]??? -blocks.powercapacity = Capacità Energetica -blocks.powershot = Danno/Colpo -blocks.damage = Danno -blocks.targetsair = Attacca Nemici Aerei -blocks.targetsground = Attacca Nemici Terreni -blocks.itemsmoved = Velocità di Movimento -blocks.launchtime = Tempo fra Decolli -blocks.shootrange = Raggio -blocks.size = Dimensioni -blocks.displaysize = Display Size -blocks.liquidcapacity = Capacità del Liquido -blocks.powerrange = Raggio Energia -blocks.linkrange = Link Range -blocks.instructions = Instructions -blocks.powerconnections = Connessioni Massime -blocks.poweruse = Utilizzo Energia -blocks.powerdamage = Energia/Danno -blocks.itemcapacity = Capacità -blocks.basepowergeneration = Generazione Energia di Base -blocks.productiontime = Tempo di Produzione -blocks.repairtime = Tempo di Riparazione Completa -blocks.speedincrease = Aumento Velocità -blocks.range = Raggio -blocks.drilltier = Scavabili -blocks.drillspeed = Velocità di Scavo Stabile -blocks.boosteffect = Effetto Boost -blocks.maxunits = Unità Attive Max -blocks.health = Salute -blocks.buildtime = Tempo di Costruzione -blocks.maxconsecutive = Max Consecutive -blocks.buildcost = Costo di Costruzione -blocks.inaccuracy = Inaccuratezza -blocks.shots = Colpi -blocks.reload = Ricarica -blocks.ammo = Munizioni -blocks.shieldhealth = Shield Health -blocks.cooldowntime = Cooldown Time +stat.powercapacity = Capacità Energetica +stat.powershot = Danno/Colpo +stat.damage = Danno +stat.targetsair = Attacca Nemici Aerei +stat.targetsground = Attacca Nemici Terreni +stat.itemsmoved = Velocità di Movimento +stat.launchtime = Tempo fra Decolli +stat.shootrange = Raggio +stat.size = Dimensioni +stat.displaysize = Display Size +stat.liquidcapacity = Capacità del Liquido +stat.powerrange = Raggio Energia +stat.linkrange = Link Range +stat.instructions = Instructions +stat.powerconnections = Connessioni Massime +stat.poweruse = Utilizzo Energia +stat.powerdamage = Energia/Danno +stat.itemcapacity = Capacità +stat.basepowergeneration = Generazione Energia di Base +stat.productiontime = Tempo di Produzione +stat.repairtime = Tempo di Riparazione Completa +stat.speedincrease = Aumento Velocità +stat.range = Raggio +stat.drilltier = Scavabili +stat.drillspeed = Velocità di Scavo Stabile +stat.boosteffect = Effetto Boost +stat.maxunits = Unità Attive Max +stat.health = Salute +stat.buildtime = Tempo di Costruzione +stat.maxconsecutive = Max Consecutive +stat.buildcost = Costo di Costruzione +stat.inaccuracy = Inaccuratezza +stat.shots = Colpi +stat.reload = Ricarica +stat.ammo = Munizioni +stat.shieldhealth = Shield Health +stat.cooldowntime = Cooldown Time bar.drilltierreq = Miglior Trivella Richiesta bar.noresources = Missing Resources @@ -1302,4 +1302,4 @@ block.cyclone.description = Una grande torretta a fuoco rapido. block.spectre.description = Una grande torretta che spara due potenti proiettili contemporaneamente. block.meltdown.description = Una grande torretta che spara un potente laser a lungo raggio. block.repair-point.description = Cura continuamente l'unità danneggiata più vicina. -block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. +block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. \ No newline at end of file diff --git a/core/assets/bundles/bundle_ja.properties b/core/assets/bundles/bundle_ja.properties index fdb915ff96..68c766e79b 100644 --- a/core/assets/bundles/bundle_ja.properties +++ b/core/assets/bundles/bundle_ja.properties @@ -570,49 +570,49 @@ info.title = 情報 error.title = [crimson]エラーが発生しました error.crashtitle = エラーが発生しました unit.nobuild = [scarlet]ユニットを構築できません -blocks.input = 搬入 -blocks.output = 搬出 -blocks.booster = ブースト -blocks.tiles = 必要なタイル -blocks.affinities = 親和性 +stat.input = 搬入 +stat.output = 搬出 +stat.booster = ブースト +stat.tiles = 必要なタイル +stat.affinities = 親和性 block.unknown = [lightgray]??? -blocks.powercapacity = 電力容量 -blocks.powershot = 電力/ショット -blocks.damage = ダメージ -blocks.targetsair = 対空攻撃 -blocks.targetsground = 対地攻撃 -blocks.itemsmoved = 輸送速度 -blocks.launchtime = 発射の待機時間 -blocks.shootrange = 範囲 -blocks.size = 大きさ -blocks.displaysize = Display Size -blocks.liquidcapacity = 液体容量 -blocks.powerrange = 電力範囲 -blocks.linkrange = Link Range -blocks.instructions = Instructions -blocks.powerconnections = 最大接続数 -blocks.poweruse = 電力使用量 -blocks.powerdamage = 電力/ダメージ -blocks.itemcapacity = アイテム容量 -blocks.basepowergeneration = 基本発電量 -blocks.productiontime = 製造速度 -blocks.repairtime = ブロックの完全修復速度 -blocks.speedincrease = 速度向上 -blocks.range = 範囲 -blocks.drilltier = ドリル -blocks.drillspeed = 基本採掘速度 -blocks.boosteffect = ブースト効果 -blocks.maxunits = 最大ユニット数 -blocks.health = 耐久値 -blocks.buildtime = 建設時間 -blocks.maxconsecutive = Max Consecutive -blocks.buildcost = 建設費用 -blocks.inaccuracy = 誤差 -blocks.shots = ショット -blocks.reload = リロード速度 -blocks.ammo = 弾薬 -blocks.shieldhealth = Shield Health -blocks.cooldowntime = Cooldown Time +stat.powercapacity = 電力容量 +stat.powershot = 電力/ショット +stat.damage = ダメージ +stat.targetsair = 対空攻撃 +stat.targetsground = 対地攻撃 +stat.itemsmoved = 輸送速度 +stat.launchtime = 発射の待機時間 +stat.shootrange = 範囲 +stat.size = 大きさ +stat.displaysize = Display Size +stat.liquidcapacity = 液体容量 +stat.powerrange = 電力範囲 +stat.linkrange = Link Range +stat.instructions = Instructions +stat.powerconnections = 最大接続数 +stat.poweruse = 電力使用量 +stat.powerdamage = 電力/ダメージ +stat.itemcapacity = アイテム容量 +stat.basepowergeneration = 基本発電量 +stat.productiontime = 製造速度 +stat.repairtime = ブロックの完全修復速度 +stat.speedincrease = 速度向上 +stat.range = 範囲 +stat.drilltier = ドリル +stat.drillspeed = 基本採掘速度 +stat.boosteffect = ブースト効果 +stat.maxunits = 最大ユニット数 +stat.health = 耐久値 +stat.buildtime = 建設時間 +stat.maxconsecutive = Max Consecutive +stat.buildcost = 建設費用 +stat.inaccuracy = 誤差 +stat.shots = ショット +stat.reload = リロード速度 +stat.ammo = 弾薬 +stat.shieldhealth = Shield Health +stat.cooldowntime = Cooldown Time bar.drilltierreq = より高性能なドリルを使用してください bar.noresources = Missing Resources @@ -1302,4 +1302,4 @@ block.cyclone.description = 大型の連射型ターレットです。 block.spectre.description = 一度に2発の強力な弾を放つ大型のターレットです。 block.meltdown.description = 強力な長距離攻撃が可能な大型のターレットです。 block.repair-point.description = 近くの負傷したユニットを修復します。 -block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. +block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. \ No newline at end of file diff --git a/core/assets/bundles/bundle_ko.properties b/core/assets/bundles/bundle_ko.properties index f03ff17264..82bb299642 100644 --- a/core/assets/bundles/bundle_ko.properties +++ b/core/assets/bundles/bundle_ko.properties @@ -570,49 +570,49 @@ info.title = 정보 error.title = [scarlet]오류가 발생했습니다. error.crashtitle = 오류가 발생했습니다 unit.nobuild = [scarlet]이 유닛은 건설할 수 없습니다. -blocks.input = 입력 -blocks.output = 출력 -blocks.booster = 가속 -blocks.tiles = 필요한 타일 -blocks.affinities = 친화력 +stat.input = 입력 +stat.output = 출력 +stat.booster = 가속 +stat.tiles = 필요한 타일 +stat.affinities = 친화력 block.unknown = [lightgray]??? -blocks.powercapacity = 전력 용량 -blocks.powershot = 전력/발 -blocks.damage = 공격력 -blocks.targetsair = 공중 공격 -blocks.targetsground = 지상 공격 -blocks.itemsmoved = 이동 속도 -blocks.launchtime = 출격 간격 -blocks.shootrange = 사거리 -blocks.size = 크기 -blocks.displaysize = Display Size -blocks.liquidcapacity = 액체 용량 -blocks.powerrange = 전력 범위 -blocks.linkrange = Link Range -blocks.instructions = Instructions -blocks.powerconnections = 최대 연결 수 -blocks.poweruse = 전력 사용 -blocks.powerdamage = 전력/데미지 -blocks.itemcapacity = 저장 용량 -blocks.basepowergeneration = 기본 전력 생성량 -blocks.productiontime = 제작 시간 -blocks.repairtime = 전체 블록 수리시간 -blocks.speedincrease = 속도 증가 -blocks.range = 사거리 -blocks.drilltier = 드릴 -blocks.drillspeed = 기본 드릴 속도 -blocks.boosteffect = 가속 효과 -blocks.maxunits = 최대 활성 유닛수 -blocks.health = 체력 -blocks.buildtime = 건설 시간 -blocks.maxconsecutive = Max Consecutive -blocks.buildcost = 건설 재료 -blocks.inaccuracy = 오차각 -blocks.shots = 공격 속도 -blocks.reload = 발/초 -blocks.ammo = 탄약 -blocks.shieldhealth = 보호막 체력 -blocks.cooldowntime = Cooldown Time +stat.powercapacity = 전력 용량 +stat.powershot = 전력/발 +stat.damage = 공격력 +stat.targetsair = 공중 공격 +stat.targetsground = 지상 공격 +stat.itemsmoved = 이동 속도 +stat.launchtime = 출격 간격 +stat.shootrange = 사거리 +stat.size = 크기 +stat.displaysize = Display Size +stat.liquidcapacity = 액체 용량 +stat.powerrange = 전력 범위 +stat.linkrange = Link Range +stat.instructions = Instructions +stat.powerconnections = 최대 연결 수 +stat.poweruse = 전력 사용 +stat.powerdamage = 전력/데미지 +stat.itemcapacity = 저장 용량 +stat.basepowergeneration = 기본 전력 생성량 +stat.productiontime = 제작 시간 +stat.repairtime = 전체 블록 수리시간 +stat.speedincrease = 속도 증가 +stat.range = 사거리 +stat.drilltier = 드릴 +stat.drillspeed = 기본 드릴 속도 +stat.boosteffect = 가속 효과 +stat.maxunits = 최대 활성 유닛수 +stat.health = 체력 +stat.buildtime = 건설 시간 +stat.maxconsecutive = Max Consecutive +stat.buildcost = 건설 재료 +stat.inaccuracy = 오차각 +stat.shots = 공격 속도 +stat.reload = 발/초 +stat.ammo = 탄약 +stat.shieldhealth = 보호막 체력 +stat.cooldowntime = Cooldown Time bar.drilltierreq = 더 좋은 드릴이 필요 bar.noresources = 자원 부족 @@ -1302,4 +1302,4 @@ block.cyclone.description = 대공 및 대지 포탑. 근처 유닛에게 폭발 block.spectre.description = 거대한 이중 배럴 대포. 공중 및 지상 목표물에 큰 관통 철갑탄을 발사합니다. block.meltdown.description = 거대한 레이저 대포. 근처의 적에게 지속적인 레이버 빔을 충전하여 발사합니다. 냉각수가 있어야 작동합니다. block.repair-point.description = 주변에서 가장 가까운 유닛을 지속적으로 치료합니다. -block.segment.description = 날아오는 발사체를 요격합니다. 레이저는 목표 대상이 아닙니다. +block.segment.description = 날아오는 발사체를 요격합니다. 레이저는 목표 대상이 아닙니다. \ No newline at end of file diff --git a/core/assets/bundles/bundle_lt.properties b/core/assets/bundles/bundle_lt.properties index a318c53f5f..2ebd273b5a 100644 --- a/core/assets/bundles/bundle_lt.properties +++ b/core/assets/bundles/bundle_lt.properties @@ -570,49 +570,49 @@ info.title = Informacija error.title = [crimson]Įvyko klaida error.crashtitle = Įvyko klaida unit.nobuild = [scarlet]Unit can't build -blocks.input = Įeiga -blocks.output = Išeiga -blocks.booster = Stiprintuvas -blocks.tiles = Privalomi -blocks.affinities = Affinities +stat.input = Įeiga +stat.output = Išeiga +stat.booster = Stiprintuvas +stat.tiles = Privalomi +stat.affinities = Affinities block.unknown = [lightgray]??? -blocks.powercapacity = Energijos Talpumas -blocks.powershot = Energija per šūvį -blocks.damage = Žala -blocks.targetsair = Šaudo į oro taikinius -blocks.targetsground = Šaudo į žemės taikinius -blocks.itemsmoved = Judėjimo Greitis -blocks.launchtime = Laikas Tarp Paleidimų -blocks.shootrange = Atstumas -blocks.size = Dydis -blocks.displaysize = Display Size -blocks.liquidcapacity = Skysčių Talpumas -blocks.powerrange = Energijos Skleidimo Atstumas -blocks.linkrange = Link Range -blocks.instructions = Instructions -blocks.powerconnections = Maks. Jungčių Kiekis -blocks.poweruse = Energijos Suvartojimas -blocks.powerdamage = Power/Damage -blocks.itemcapacity = Daiktų Talpumas -blocks.basepowergeneration = Bazinis Energijos Generavimas -blocks.productiontime = Gamybos Laikas -blocks.repairtime = Pilnas bloko sutaisymo laikas -blocks.speedincrease = Greičio Padidėjimas -blocks.range = Atstumas -blocks.drilltier = Gręžiama -blocks.drillspeed = Bazinis Grąžto Greitis -blocks.boosteffect = Pastiprinimo Efektas -blocks.maxunits = Maks. Aktyvių Vienetų Kiekis -blocks.health = Gyvybės -blocks.buildtime = Statymo Laikas -blocks.maxconsecutive = Max Consecutive -blocks.buildcost = Statymo Kaina -blocks.inaccuracy = Netikslumas -blocks.shots = Šūviai -blocks.reload = Šūviai per sekundę -blocks.ammo = Šoviniai -blocks.shieldhealth = Shield Health -blocks.cooldowntime = Cooldown Time +stat.powercapacity = Energijos Talpumas +stat.powershot = Energija per šūvį +stat.damage = Žala +stat.targetsair = Šaudo į oro taikinius +stat.targetsground = Šaudo į žemės taikinius +stat.itemsmoved = Judėjimo Greitis +stat.launchtime = Laikas Tarp Paleidimų +stat.shootrange = Atstumas +stat.size = Dydis +stat.displaysize = Display Size +stat.liquidcapacity = Skysčių Talpumas +stat.powerrange = Energijos Skleidimo Atstumas +stat.linkrange = Link Range +stat.instructions = Instructions +stat.powerconnections = Maks. Jungčių Kiekis +stat.poweruse = Energijos Suvartojimas +stat.powerdamage = Power/Damage +stat.itemcapacity = Daiktų Talpumas +stat.basepowergeneration = Bazinis Energijos Generavimas +stat.productiontime = Gamybos Laikas +stat.repairtime = Pilnas bloko sutaisymo laikas +stat.speedincrease = Greičio Padidėjimas +stat.range = Atstumas +stat.drilltier = Gręžiama +stat.drillspeed = Bazinis Grąžto Greitis +stat.boosteffect = Pastiprinimo Efektas +stat.maxunits = Maks. Aktyvių Vienetų Kiekis +stat.health = Gyvybės +stat.buildtime = Statymo Laikas +stat.maxconsecutive = Max Consecutive +stat.buildcost = Statymo Kaina +stat.inaccuracy = Netikslumas +stat.shots = Šūviai +stat.reload = Šūviai per sekundę +stat.ammo = Šoviniai +stat.shieldhealth = Shield Health +stat.cooldowntime = Cooldown Time bar.drilltierreq = Privalomas Geresnis Grąžtas bar.noresources = Missing Resources @@ -1302,4 +1302,4 @@ block.cyclone.description = Didelis bokštas puolantis, tiek žemę, tiek orą. block.spectre.description = Milžiniškas dvivamzdis bokštas. Šaudo didelius, kiaurai per šarvus einančius šovinius į taikinius esančius ant žemės ir ore. block.meltdown.description = Milžiniška lazerinė patranka. Užsikrauna ir šaudo lazerinius spindulius į aplinkinius priešus. Veikimui reikalingas aušinimo skystis. block.repair-point.description = Pastoviai gydo artimiausius netoliese esančius vienetus. -block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. +block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. \ No newline at end of file diff --git a/core/assets/bundles/bundle_nl.properties b/core/assets/bundles/bundle_nl.properties index 634b6eb00e..41d6e8a8fc 100644 --- a/core/assets/bundles/bundle_nl.properties +++ b/core/assets/bundles/bundle_nl.properties @@ -570,49 +570,49 @@ info.title = Informatie error.title = [crimson]Een fout is opgetreden error.crashtitle = Een fout is opgetreden unit.nobuild = [scarlet]Unit can't build -blocks.input = Input -blocks.output = Output -blocks.booster = Booster -blocks.tiles = Required Tiles -blocks.affinities = Affinities +stat.input = Input +stat.output = Output +stat.booster = Booster +stat.tiles = Required Tiles +stat.affinities = Affinities block.unknown = [lightgray]??? -blocks.powercapacity = Stroomcapaciteit -blocks.powershot = Stroom/Schot -blocks.damage = Schade -blocks.targetsair = Luchtdoelwitten -blocks.targetsground = Gronddoelwitten -blocks.itemsmoved = Beweegingssnelheid -blocks.launchtime = Tijd tussen lanceringen -blocks.shootrange = Bereik -blocks.size = Formaat -blocks.displaysize = Display Size -blocks.liquidcapacity = Vloeistofcapaciteit -blocks.powerrange = Stroombereik -blocks.linkrange = Link Range -blocks.instructions = Instructions -blocks.powerconnections = Maximale Hoeveelheid Connecties -blocks.poweruse = Stroomverbruik -blocks.powerdamage = Stroom/Schade -blocks.itemcapacity = Materiaalcapaciteit -blocks.basepowergeneration = Standaard Stroom Generatie -blocks.productiontime = Productie Tijd -blocks.repairtime = Volledige Blok Repareertijd -blocks.speedincrease = Snelheidsverhoging -blocks.range = Bereik -blocks.drilltier = Valt te delven -blocks.drillspeed = Standaard mine snelheid -blocks.boosteffect = Boost Effect -blocks.maxunits = Maximaal Actieve Units -blocks.health = Levenspunten -blocks.buildtime = Bouwtijd -blocks.maxconsecutive = Max Consecutive -blocks.buildcost = Bouwkosten -blocks.inaccuracy = Onnauwkeurigheid -blocks.shots = Shoten -blocks.reload = Schoten/Seconde -blocks.ammo = Ammunitie -blocks.shieldhealth = Shield Health -blocks.cooldowntime = Cooldown Time +stat.powercapacity = Stroomcapaciteit +stat.powershot = Stroom/Schot +stat.damage = Schade +stat.targetsair = Luchtdoelwitten +stat.targetsground = Gronddoelwitten +stat.itemsmoved = Beweegingssnelheid +stat.launchtime = Tijd tussen lanceringen +stat.shootrange = Bereik +stat.size = Formaat +stat.displaysize = Display Size +stat.liquidcapacity = Vloeistofcapaciteit +stat.powerrange = Stroombereik +stat.linkrange = Link Range +stat.instructions = Instructions +stat.powerconnections = Maximale Hoeveelheid Connecties +stat.poweruse = Stroomverbruik +stat.powerdamage = Stroom/Schade +stat.itemcapacity = Materiaalcapaciteit +stat.basepowergeneration = Standaard Stroom Generatie +stat.productiontime = Productie Tijd +stat.repairtime = Volledige Blok Repareertijd +stat.speedincrease = Snelheidsverhoging +stat.range = Bereik +stat.drilltier = Valt te delven +stat.drillspeed = Standaard mine snelheid +stat.boosteffect = Boost Effect +stat.maxunits = Maximaal Actieve Units +stat.health = Levenspunten +stat.buildtime = Bouwtijd +stat.maxconsecutive = Max Consecutive +stat.buildcost = Bouwkosten +stat.inaccuracy = Onnauwkeurigheid +stat.shots = Shoten +stat.reload = Schoten/Seconde +stat.ammo = Ammunitie +stat.shieldhealth = Shield Health +stat.cooldowntime = Cooldown Time bar.drilltierreq = Betere miner nodig bar.noresources = Missing Resources @@ -1302,4 +1302,4 @@ block.cyclone.description = A large rapid fire turret. block.spectre.description = A large turret which shoots two powerful bullets at once. block.meltdown.description = A large turret which shoots powerful long-range beams. block.repair-point.description = Continuously heals the closest damaged unit in its vicinity. -block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. +block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. \ No newline at end of file diff --git a/core/assets/bundles/bundle_nl_BE.properties b/core/assets/bundles/bundle_nl_BE.properties index ce59b257f7..4f2ed1fdcd 100644 --- a/core/assets/bundles/bundle_nl_BE.properties +++ b/core/assets/bundles/bundle_nl_BE.properties @@ -570,49 +570,49 @@ info.title = Info error.title = [crimson]An error has occured error.crashtitle = An error has occured unit.nobuild = [scarlet]Unit can't build -blocks.input = Input -blocks.output = Output -blocks.booster = Booster -blocks.tiles = Required Tiles -blocks.affinities = Affinities +stat.input = Input +stat.output = Output +stat.booster = Booster +stat.tiles = Required Tiles +stat.affinities = Affinities block.unknown = [lightgray]??? -blocks.powercapacity = Power Capacity -blocks.powershot = Power/Shot -blocks.damage = Damage -blocks.targetsair = Targets Air -blocks.targetsground = Targets Ground -blocks.itemsmoved = Move Speed -blocks.launchtime = Time Between Launches -blocks.shootrange = Range -blocks.size = Size -blocks.displaysize = Display Size -blocks.liquidcapacity = Liquid Capacity -blocks.powerrange = Power Range -blocks.linkrange = Link Range -blocks.instructions = Instructions -blocks.powerconnections = Max Connections -blocks.poweruse = Power Use -blocks.powerdamage = Power/Damage -blocks.itemcapacity = Item Capacity -blocks.basepowergeneration = Base Power Generation -blocks.productiontime = Production Time -blocks.repairtime = Block Full Repair Time -blocks.speedincrease = Speed Increase -blocks.range = Range -blocks.drilltier = Drillables -blocks.drillspeed = Base Drill Speed -blocks.boosteffect = Boost Effect -blocks.maxunits = Max Active Units -blocks.health = Health -blocks.buildtime = Build Time -blocks.maxconsecutive = Max Consecutive -blocks.buildcost = Build Cost -blocks.inaccuracy = Inaccuracy -blocks.shots = Shots -blocks.reload = Shots/Second -blocks.ammo = Ammo -blocks.shieldhealth = Shield Health -blocks.cooldowntime = Cooldown Time +stat.powercapacity = Power Capacity +stat.powershot = Power/Shot +stat.damage = Damage +stat.targetsair = Targets Air +stat.targetsground = Targets Ground +stat.itemsmoved = Move Speed +stat.launchtime = Time Between Launches +stat.shootrange = Range +stat.size = Size +stat.displaysize = Display Size +stat.liquidcapacity = Liquid Capacity +stat.powerrange = Power Range +stat.linkrange = Link Range +stat.instructions = Instructions +stat.powerconnections = Max Connections +stat.poweruse = Power Use +stat.powerdamage = Power/Damage +stat.itemcapacity = Item Capacity +stat.basepowergeneration = Base Power Generation +stat.productiontime = Production Time +stat.repairtime = Block Full Repair Time +stat.speedincrease = Speed Increase +stat.range = Range +stat.drilltier = Drillables +stat.drillspeed = Base Drill Speed +stat.boosteffect = Boost Effect +stat.maxunits = Max Active Units +stat.health = Health +stat.buildtime = Build Time +stat.maxconsecutive = Max Consecutive +stat.buildcost = Build Cost +stat.inaccuracy = Inaccuracy +stat.shots = Shots +stat.reload = Shots/Second +stat.ammo = Ammo +stat.shieldhealth = Shield Health +stat.cooldowntime = Cooldown Time bar.drilltierreq = Better Drill Required bar.noresources = Missing Resources @@ -1302,4 +1302,4 @@ block.cyclone.description = A large rapid fire turret. block.spectre.description = A large turret which shoots two powerful bullets at once. block.meltdown.description = A large turret which shoots powerful long-range beams. block.repair-point.description = Continuously heals the closest damaged unit in its vicinity. -block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. +block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. \ No newline at end of file diff --git a/core/assets/bundles/bundle_pl.properties b/core/assets/bundles/bundle_pl.properties index f0c67c6a88..80502bb870 100644 --- a/core/assets/bundles/bundle_pl.properties +++ b/core/assets/bundles/bundle_pl.properties @@ -570,49 +570,49 @@ info.title = Informacje error.title = [crimson]Wystąpił błąd error.crashtitle = Wystąpił błąd unit.nobuild = [scarlet]Jednostka nie może budować -blocks.input = Wejście -blocks.output = Wyjście -blocks.booster = Wzmacniacz -blocks.tiles = Wymagane Pola -blocks.affinities = Uwydajnienie +stat.input = Wejście +stat.output = Wyjście +stat.booster = Wzmacniacz +stat.tiles = Wymagane Pola +stat.affinities = Uwydajnienie block.unknown = [lightgray]??? -blocks.powercapacity = Pojemność mocy -blocks.powershot = moc/strzał -blocks.damage = Obrażenia -blocks.targetsair = Namierzanie wrogów powietrznych -blocks.targetsground = Namierzanie wrogów lądowych -blocks.itemsmoved = Prędkość poruszania się -blocks.launchtime = Czas pomiędzy wystrzeleniami -blocks.shootrange = Zasięg -blocks.size = Rozmiar -blocks.displaysize = Display Size -blocks.liquidcapacity = Pojemność cieczy -blocks.powerrange = Zakres mocy -blocks.linkrange = Link Range -blocks.instructions = Instructions -blocks.powerconnections = Maksymalna ilość połączeń -blocks.poweruse = Zużycie prądu -blocks.powerdamage = Moc/Zniszczenia -blocks.itemcapacity = Pojemność przedmiotów -blocks.basepowergeneration = Podstawowa generacja mocy -blocks.productiontime = Czas produkcji -blocks.repairtime = Czas pełnej naprawy bloku -blocks.speedincrease = Zwiększenie prędkości -blocks.range = Zasięg -blocks.drilltier = Co może wykopać -blocks.drillspeed = Podstawowa szybkość kopania -blocks.boosteffect = Efekt wzmocnienia -blocks.maxunits = Maksymalna ilość jednostek -blocks.health = Zdrowie -blocks.buildtime = Czas budowy -blocks.maxconsecutive = Max Consecutive -blocks.buildcost = Koszt budowy -blocks.inaccuracy = Niecelność -blocks.shots = Strzały -blocks.reload = Strzałów/sekundę -blocks.ammo = Amunicja -blocks.shieldhealth = Shield Health -blocks.cooldowntime = Cooldown Time +stat.powercapacity = Pojemność mocy +stat.powershot = moc/strzał +stat.damage = Obrażenia +stat.targetsair = Namierzanie wrogów powietrznych +stat.targetsground = Namierzanie wrogów lądowych +stat.itemsmoved = Prędkość poruszania się +stat.launchtime = Czas pomiędzy wystrzeleniami +stat.shootrange = Zasięg +stat.size = Rozmiar +stat.displaysize = Display Size +stat.liquidcapacity = Pojemność cieczy +stat.powerrange = Zakres mocy +stat.linkrange = Link Range +stat.instructions = Instructions +stat.powerconnections = Maksymalna ilość połączeń +stat.poweruse = Zużycie prądu +stat.powerdamage = Moc/Zniszczenia +stat.itemcapacity = Pojemność przedmiotów +stat.basepowergeneration = Podstawowa generacja mocy +stat.productiontime = Czas produkcji +stat.repairtime = Czas pełnej naprawy bloku +stat.speedincrease = Zwiększenie prędkości +stat.range = Zasięg +stat.drilltier = Co może wykopać +stat.drillspeed = Podstawowa szybkość kopania +stat.boosteffect = Efekt wzmocnienia +stat.maxunits = Maksymalna ilość jednostek +stat.health = Zdrowie +stat.buildtime = Czas budowy +stat.maxconsecutive = Max Consecutive +stat.buildcost = Koszt budowy +stat.inaccuracy = Niecelność +stat.shots = Strzały +stat.reload = Strzałów/sekundę +stat.ammo = Amunicja +stat.shieldhealth = Shield Health +stat.cooldowntime = Cooldown Time bar.drilltierreq = Wymagane Lepsze Wiertło bar.noresources = Missing Resources @@ -1302,4 +1302,4 @@ block.cyclone.description = Duża szybkostrzelna wieża. block.spectre.description = Duże działo dwulufowe, które strzela potężnymi pociskami przebijającymi pancerz w jednostki naziemne i powietrzne. block.meltdown.description = Duże działo laserowe, które strzela potężnymi wiązkami dalekiego zasięgu. Wymaga chłodzenia. block.repair-point.description = Bez przerw naprawia najbliższą uszkodzoną jednostkę w jego zasięgu. -block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. +block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. \ No newline at end of file diff --git a/core/assets/bundles/bundle_pt_BR.properties b/core/assets/bundles/bundle_pt_BR.properties index c2d8b20dcf..7f2659d839 100644 --- a/core/assets/bundles/bundle_pt_BR.properties +++ b/core/assets/bundles/bundle_pt_BR.properties @@ -570,49 +570,49 @@ info.title = [accent]Informação error.title = [crimson]Ocorreu um Erro. error.crashtitle = Ocorreu um Erro unit.nobuild = [scarlet]Unit can't build -blocks.input = Entrada -blocks.output = Saída -blocks.booster = Apoio -blocks.tiles = Required Tiles -blocks.affinities = Affinities +stat.input = Entrada +stat.output = Saída +stat.booster = Apoio +stat.tiles = Required Tiles +stat.affinities = Affinities block.unknown = [lightgray]??? -blocks.powercapacity = Capacidade de Energia -blocks.powershot = Energia/tiro -blocks.damage = Dano -blocks.targetsair = Mira no ar -blocks.targetsground = Mira no chão -blocks.itemsmoved = Velocidade de movimento -blocks.launchtime = Tempo entre Disparos. -blocks.shootrange = Alcance -blocks.size = Tamanho -blocks.displaysize = Display Size -blocks.liquidcapacity = Capacidade de Líquido -blocks.powerrange = Alcance da Energia -blocks.linkrange = Link Range -blocks.instructions = Instructions -blocks.powerconnections = Conexões Máximas -blocks.poweruse = Uso de energia -blocks.powerdamage = Dano/Poder -blocks.itemcapacity = Capacidade de Itens -blocks.basepowergeneration = Geração de poder base -blocks.productiontime = Tempo de produção -blocks.repairtime = Tempo de reparo total do bloco -blocks.speedincrease = Aumento de velocidade -blocks.range = Distância -blocks.drilltier = Brocas -blocks.drillspeed = Velocidade base da Broca -blocks.boosteffect = Efeito do Impulso -blocks.maxunits = Máximo de unidades ativas -blocks.health = Saúde -blocks.buildtime = Tempo de construção -blocks.maxconsecutive = Max Consecutive -blocks.buildcost = Custo de construção -blocks.inaccuracy = Imprecisão -blocks.shots = Tiros -blocks.reload = Tiros por segundo -blocks.ammo = Munição -blocks.shieldhealth = Shield Health -blocks.cooldowntime = Cooldown Time +stat.powercapacity = Capacidade de Energia +stat.powershot = Energia/tiro +stat.damage = Dano +stat.targetsair = Mira no ar +stat.targetsground = Mira no chão +stat.itemsmoved = Velocidade de movimento +stat.launchtime = Tempo entre Disparos. +stat.shootrange = Alcance +stat.size = Tamanho +stat.displaysize = Display Size +stat.liquidcapacity = Capacidade de Líquido +stat.powerrange = Alcance da Energia +stat.linkrange = Link Range +stat.instructions = Instructions +stat.powerconnections = Conexões Máximas +stat.poweruse = Uso de energia +stat.powerdamage = Dano/Poder +stat.itemcapacity = Capacidade de Itens +stat.basepowergeneration = Geração de poder base +stat.productiontime = Tempo de produção +stat.repairtime = Tempo de reparo total do bloco +stat.speedincrease = Aumento de velocidade +stat.range = Distância +stat.drilltier = Brocas +stat.drillspeed = Velocidade base da Broca +stat.boosteffect = Efeito do Impulso +stat.maxunits = Máximo de unidades ativas +stat.health = Saúde +stat.buildtime = Tempo de construção +stat.maxconsecutive = Max Consecutive +stat.buildcost = Custo de construção +stat.inaccuracy = Imprecisão +stat.shots = Tiros +stat.reload = Tiros por segundo +stat.ammo = Munição +stat.shieldhealth = Shield Health +stat.cooldowntime = Cooldown Time bar.drilltierreq = Broca melhor necessária. bar.noresources = Missing Resources @@ -1302,4 +1302,4 @@ block.cyclone.description = Uma grande torre que dispara balas explosivas que se block.spectre.description = Um grande canhão massivo. Dispara grandes tiros perfuradores de blindagem em inimigos aéreos e terrestres. block.meltdown.description = Um grande canhão laser massivo. Carrega e dispara um poderoso e persistente feixe nos seus inimigos. Requer uma refrigeração para ser operada. block.repair-point.description = Continuamente repara a unidade danificada mais proxima. -block.segment.description = Destrói projéteis inimigos. Projéteis de laser não são afetados. +block.segment.description = Destrói projéteis inimigos. Projéteis de laser não são afetados. \ No newline at end of file diff --git a/core/assets/bundles/bundle_pt_PT.properties b/core/assets/bundles/bundle_pt_PT.properties index 0b6948191b..c4b5070b3a 100644 --- a/core/assets/bundles/bundle_pt_PT.properties +++ b/core/assets/bundles/bundle_pt_PT.properties @@ -570,49 +570,49 @@ info.title = [accent]Informação error.title = [crimson]Ocorreu um Erro. error.crashtitle = Ocorreu um Erro unit.nobuild = [scarlet]Unit can't build -blocks.input = Entrada -blocks.output = Saida -blocks.booster = Booster -blocks.tiles = Telhas Requeridas -blocks.affinities = Afinidades +stat.input = Entrada +stat.output = Saida +stat.booster = Booster +stat.tiles = Telhas Requeridas +stat.affinities = Afinidades block.unknown = [lightgray]??? -blocks.powercapacity = Capacidade de Energia -blocks.powershot = Energia/tiro -blocks.damage = Dano -blocks.targetsair = Mirar no ar -blocks.targetsground = Mirar no chão -blocks.itemsmoved = Velocidade de movimento -blocks.launchtime = Tempo entre tiros -blocks.shootrange = Alcance -blocks.size = Tamanho -blocks.displaysize = Display Size -blocks.liquidcapacity = Capacidade de Líquido -blocks.powerrange = Alcance da Energia -blocks.linkrange = Link Range -blocks.instructions = Instructions -blocks.powerconnections = Max Connections -blocks.poweruse = Uso de energia -blocks.powerdamage = Dano/Poder -blocks.itemcapacity = Capacidade de Itens -blocks.basepowergeneration = Geração de poder base -blocks.productiontime = Tempo de produção -blocks.repairtime = Tempo de reparo total do bloco -blocks.speedincrease = Aumento de velocidade -blocks.range = Distância -blocks.drilltier = Furáveis -blocks.drillspeed = Velocidade da broca base -blocks.boosteffect = Efeito do Boost -blocks.maxunits = Máximo de unidades ativas -blocks.health = Saúde -blocks.buildtime = Tempo de construção -blocks.maxconsecutive = Max Consecutive -blocks.buildcost = Custo de construção -blocks.inaccuracy = Imprecisão -blocks.shots = Tiros -blocks.reload = Tiros por segundo -blocks.ammo = Munição -blocks.shieldhealth = Shield Health -blocks.cooldowntime = Cooldown Time +stat.powercapacity = Capacidade de Energia +stat.powershot = Energia/tiro +stat.damage = Dano +stat.targetsair = Mirar no ar +stat.targetsground = Mirar no chão +stat.itemsmoved = Velocidade de movimento +stat.launchtime = Tempo entre tiros +stat.shootrange = Alcance +stat.size = Tamanho +stat.displaysize = Display Size +stat.liquidcapacity = Capacidade de Líquido +stat.powerrange = Alcance da Energia +stat.linkrange = Link Range +stat.instructions = Instructions +stat.powerconnections = Max Connections +stat.poweruse = Uso de energia +stat.powerdamage = Dano/Poder +stat.itemcapacity = Capacidade de Itens +stat.basepowergeneration = Geração de poder base +stat.productiontime = Tempo de produção +stat.repairtime = Tempo de reparo total do bloco +stat.speedincrease = Aumento de velocidade +stat.range = Distância +stat.drilltier = Furáveis +stat.drillspeed = Velocidade da broca base +stat.boosteffect = Efeito do Boost +stat.maxunits = Máximo de unidades ativas +stat.health = Saúde +stat.buildtime = Tempo de construção +stat.maxconsecutive = Max Consecutive +stat.buildcost = Custo de construção +stat.inaccuracy = Imprecisão +stat.shots = Tiros +stat.reload = Tiros por segundo +stat.ammo = Munição +stat.shieldhealth = Shield Health +stat.cooldowntime = Cooldown Time bar.drilltierreq = Broca melhor necessária. bar.noresources = Missing Resources @@ -1302,4 +1302,4 @@ block.cyclone.description = Uma grande torre de tiro rapido. block.spectre.description = Uma grande torre que da dois tiros poderosos ao mesmo tempo. block.meltdown.description = Uma grande torre que atira dois raios poderosos ao mesmo tempo. block.repair-point.description = Continuamente repara a unidade danificada mais proxima. -block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. +block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. \ No newline at end of file diff --git a/core/assets/bundles/bundle_ro.properties b/core/assets/bundles/bundle_ro.properties index a5d17cfe96..bef7426679 100644 --- a/core/assets/bundles/bundle_ro.properties +++ b/core/assets/bundles/bundle_ro.properties @@ -573,50 +573,51 @@ error.title = [scarlet]A apărut o eroare. error.crashtitle = A apărut o eroare. unit.nobuild = [scarlet]Unitatea nu poate construi. lastaccessed = [lightgray]Ultima Accesare: {0} -blocks.input = Necesită -blocks.output = Produce -blocks.booster = Booster -blocks.tiles = Teren Necesar -blocks.affinities = Efecte Teren +stat.input = Necesită +stat.output = Produce +stat.booster = Booster +stat.tiles = Teren Necesar +stat.affinities = Efecte Teren block.unknown = [lightgray]??? -blocks.powercapacity = Capacitate electrică -blocks.powershot = Electricitate/Glonț -blocks.damage = Forță -blocks.targetsair = Lovește Aeronave -blocks.targetsground = Lovește Artilerie -blocks.itemsmoved = Viteza de Mișcare a Materialelor -blocks.launchtime = Timp între Lansări -blocks.shootrange = Rază -blocks.size = Mărime -blocks.displaysize = Mărimea Monitorului Logic -blocks.liquidcapacity = Capacitate Lichid -blocks.powerrange = Raza Electrică -blocks.linkrange = Raza Legăturilor -blocks.instructions = Instrucțiuni -blocks.powerconnections = Maxim Conexiuni -blocks.poweruse = Consum Electricitate -blocks.powerdamage = Electricitate/Forța Glonțului -blocks.itemcapacity = Capacitate Materiale -blocks.memorycapacity = Capacitate Memorie -blocks.basepowergeneration = Generare Electricitate (Bază) -blocks.productiontime = Timp Producție -blocks.repairtime = Durata Reparării Blocului -blocks.speedincrease = Creștere Viteză -blocks.range = Rază -blocks.drilltier = Minabile -blocks.drillspeed = Viteză Burghiu (Bază) -blocks.boosteffect = Efect de Boost -blocks.maxunits = Maxim Unități Active -blocks.health = Viață -blocks.buildtime = Timp Construcție -blocks.maxconsecutive = Maxim Consecutive -blocks.buildcost = Cost Construcție -blocks.inaccuracy = Inacuratețe -blocks.shots = Lovituri -blocks.reload = Lovituri/Secundă -blocks.ammo = Muniție -blocks.shieldhealth = Viața Scutului -blocks.cooldowntime = Timp de Reîncărcare + +stat.powercapacity = Capacitate electrică +stat.powershot = Electricitate/Glonț +stat.damage = Forță +stat.targetsair = Lovește Aeronave +stat.targetsground = Lovește Artilerie +stat.itemsmoved = Viteza de Mișcare a Materialelor +stat.launchtime = Timp între Lansări +stat.shootrange = Rază +stat.size = Mărime +stat.displaysize = Mărimea Monitorului Logic +stat.liquidcapacity = Capacitate Lichid +stat.powerrange = Raza Electrică +stat.linkrange = Raza Legăturilor +stat.instructions = Instrucțiuni +stat.powerconnections = Maxim Conexiuni +stat.poweruse = Consum Electricitate +stat.powerdamage = Electricitate/Forța Glonțului +stat.itemcapacity = Capacitate Materiale +stat.memorycapacity = Capacitate Memorie +stat.basepowergeneration = Generare Electricitate (Bază) +stat.productiontime = Timp Producție +stat.repairtime = Durata Reparării Blocului +stat.speedincrease = Creștere Viteză +stat.range = Rază +stat.drilltier = Forță Burghiu +stat.drillspeed = Viteză Burghiu (Bază) +stat.boosteffect = Efect de Boost +stat.maxunits = Maxim Unități Active +stat.health = Viață +stat.buildtime = Timp Construcție +stat.maxconsecutive = Maxim Consecutive +stat.buildcost = Cost Construcție +stat.inaccuracy = Inacuratețe +stat.shots = Lovituri +stat.reload = Lovituri/Secundă +stat.ammo = Muniție +stat.shieldhealth = Viața Scutului +stat.cooldowntime = Timp de Reîncărcare blocks.basedeflectchance = Șansa de Reflexie blocks.lightningchance = Șansă Fulger blocks.lightningdamage = Forță Fulger @@ -1319,4 +1320,4 @@ block.cyclone.description = O mare armă anti-artilerie și anti-aer. Trage cu g block.spectre.description = O armă masivă cu două țevi. Trage cu gloanțe mari care găuresc armurile țintelor aeriene și artileriei. block.meltdown.description = O armă cu laser masivă. Trage cu un laser continuu la inamicii din apropiere. Necesită răcitor pt a opera. block.repair-point.description = Repară încontinuu cea mai deteriorată unitate din vecinătate. -block.segment.description = Deteriorează și distruge proiectilele din apropiere. Laserele nu sunt afectate. +block.segment.description = Deteriorează și distruge proiectilele din apropiere. Laserele nu sunt afectate. \ No newline at end of file diff --git a/core/assets/bundles/bundle_ru.properties b/core/assets/bundles/bundle_ru.properties index 1e12db9ee4..078a1cf6f4 100644 --- a/core/assets/bundles/bundle_ru.properties +++ b/core/assets/bundles/bundle_ru.properties @@ -576,50 +576,50 @@ error.title = [scarlet]Произошла ошибка error.crashtitle = Произошла ошибка unit.nobuild = [scarlet]Боевая единица не может строить lastaccessed = [lightgray]Последняя конфигурация от {0} -blocks.input = Вход -blocks.output = Выход -blocks.booster = Ускоритель -blocks.tiles = Необходимые плитки -blocks.affinities = Увеличение эффективности +stat.input = Вход +stat.output = Выход +stat.booster = Ускоритель +stat.tiles = Необходимые плитки +stat.affinities = Увеличение эффективности block.unknown = [lightgray]??? -blocks.powercapacity = Вместимость энергии -blocks.powershot = Энергия/Выстрел -blocks.damage = Урон -blocks.targetsair = Воздушные цели -blocks.targetsground = Наземные цели -blocks.itemsmoved = Скорость перемещения -blocks.launchtime = Интервал запусков -blocks.shootrange = Радиус действия -blocks.size = Размер -blocks.displaysize = Размер дисплея -blocks.liquidcapacity = Вместимость жидкости -blocks.powerrange = Дальность передачи энергии -blocks.linkrange = Дальность связи -blocks.instructions = Инструкции -blocks.powerconnections = Количество соединений -blocks.poweruse = Потребляет энергии -blocks.powerdamage = Энергия/урон -blocks.itemcapacity = Вместимость предметов -blocks.memorycapacity = Размер памяти -blocks.basepowergeneration = Базовая генерация энергии -blocks.productiontime = Время производства -blocks.repairtime = Время полной регенерации -blocks.speedincrease = Увеличение скорости -blocks.range = Радиус действия -blocks.drilltier = Бурит -blocks.drillspeed = Базовая скорость бурения -blocks.boosteffect = Ускоряющий эффект -blocks.maxunits = Максимальное количество активных единиц -blocks.health = Прочность -blocks.buildtime = Время строительства -blocks.maxconsecutive = Макс. последовательность -blocks.buildcost = Стоимость строительства -blocks.inaccuracy = Разброс -blocks.shots = Выстрелы -blocks.reload = Выстрелы/секунду -blocks.ammo = Боеприпасы -blocks.shieldhealth = Прочность щита -blocks.cooldowntime = Время восстановления +stat.powercapacity = Вместимость энергии +stat.powershot = Энергия/Выстрел +stat.damage = Урон +stat.targetsair = Воздушные цели +stat.targetsground = Наземные цели +stat.itemsmoved = Скорость перемещения +stat.launchtime = Интервал запусков +stat.shootrange = Радиус действия +stat.size = Размер +stat.displaysize = Размер дисплея +stat.liquidcapacity = Вместимость жидкости +stat.powerrange = Дальность передачи энергии +stat.linkrange = Дальность связи +stat.instructions = Инструкции +stat.powerconnections = Количество соединений +stat.poweruse = Потребляет энергии +stat.powerdamage = Энергия/урон +stat.itemcapacity = Вместимость предметов +stat.memorycapacity = Размер памяти +stat.basepowergeneration = Базовая генерация энергии +stat.productiontime = Время производства +stat.repairtime = Время полной регенерации +stat.speedincrease = Увеличение скорости +stat.range = Радиус действия +stat.drilltier = Бурит +stat.drillspeed = Базовая скорость бурения +stat.boosteffect = Ускоряющий эффект +stat.maxunits = Максимальное количество активных единиц +stat.health = Прочность +stat.buildtime = Время строительства +stat.maxconsecutive = Макс. последовательность +stat.buildcost = Стоимость строительства +stat.inaccuracy = Разброс +stat.shots = Выстрелы +stat.reload = Выстрелы/секунду +stat.ammo = Боеприпасы +stat.shieldhealth = Прочность щита +stat.cooldowntime = Время восстановления bar.drilltierreq = Требуется бур получше bar.noresources = Недостаточно ресурсов @@ -1321,5 +1321,4 @@ block.cyclone.description = Большая турель, которая може block.spectre.description = Массивная двуствольная пушка. Стреляет крупными бронебойными снарядами по воздушным и наземным целям. block.meltdown.description = Массивная лазерная пушка. Заряжает и стреляет постоянным лазерным лучом в ближайших врагов. Требуется охлаждающая жидкость для работы. block.repair-point.description = Непрерывно лечит ближайшую поврежденную боевую единицу или мех в своём радиусе. -block.segment.description = Повреждает и разрушает приближающиеся снаряды. Не взаимодействует с лазерными лучами. - +block.segment.description = Повреждает и разрушает приближающиеся снаряды. Не взаимодействует с лазерными лучами. \ No newline at end of file diff --git a/core/assets/bundles/bundle_sv.properties b/core/assets/bundles/bundle_sv.properties index 0d29e6a89e..8d3942eccb 100644 --- a/core/assets/bundles/bundle_sv.properties +++ b/core/assets/bundles/bundle_sv.properties @@ -570,49 +570,49 @@ info.title = Info error.title = [crimson]An error has occured error.crashtitle = An error has occured unit.nobuild = [scarlet]Unit can't build -blocks.input = Inmatning -blocks.output = Utmatning -blocks.booster = Booster -blocks.tiles = Required Tiles -blocks.affinities = Affinities +stat.input = Inmatning +stat.output = Utmatning +stat.booster = Booster +stat.tiles = Required Tiles +stat.affinities = Affinities block.unknown = [lightgray]??? -blocks.powercapacity = Power Capacity -blocks.powershot = Power/Shot -blocks.damage = Skada -blocks.targetsair = Targets Air -blocks.targetsground = Targets Ground -blocks.itemsmoved = Move Speed -blocks.launchtime = Time Between Launches -blocks.shootrange = Range -blocks.size = Storlek -blocks.displaysize = Display Size -blocks.liquidcapacity = Liquid Capacity -blocks.powerrange = Power Range -blocks.linkrange = Link Range -blocks.instructions = Instructions -blocks.powerconnections = Max Connections -blocks.poweruse = Power Use -blocks.powerdamage = Power/Damage -blocks.itemcapacity = Item Capacity -blocks.basepowergeneration = Base Power Generation -blocks.productiontime = Production Time -blocks.repairtime = Block Full Repair Time -blocks.speedincrease = Speed Increase -blocks.range = Range -blocks.drilltier = Drillables -blocks.drillspeed = Base Drill Speed -blocks.boosteffect = Boost Effect -blocks.maxunits = Max Active Units -blocks.health = Health -blocks.buildtime = Build Time -blocks.maxconsecutive = Max Consecutive -blocks.buildcost = Build Cost -blocks.inaccuracy = Inaccuracy -blocks.shots = Skott -blocks.reload = Shots/Second -blocks.ammo = Ammunition -blocks.shieldhealth = Shield Health -blocks.cooldowntime = Cooldown Time +stat.powercapacity = Power Capacity +stat.powershot = Power/Shot +stat.damage = Skada +stat.targetsair = Targets Air +stat.targetsground = Targets Ground +stat.itemsmoved = Move Speed +stat.launchtime = Time Between Launches +stat.shootrange = Range +stat.size = Storlek +stat.displaysize = Display Size +stat.liquidcapacity = Liquid Capacity +stat.powerrange = Power Range +stat.linkrange = Link Range +stat.instructions = Instructions +stat.powerconnections = Max Connections +stat.poweruse = Power Use +stat.powerdamage = Power/Damage +stat.itemcapacity = Item Capacity +stat.basepowergeneration = Base Power Generation +stat.productiontime = Production Time +stat.repairtime = Block Full Repair Time +stat.speedincrease = Speed Increase +stat.range = Range +stat.drilltier = Drillables +stat.drillspeed = Base Drill Speed +stat.boosteffect = Boost Effect +stat.maxunits = Max Active Units +stat.health = Health +stat.buildtime = Build Time +stat.maxconsecutive = Max Consecutive +stat.buildcost = Build Cost +stat.inaccuracy = Inaccuracy +stat.shots = Skott +stat.reload = Shots/Second +stat.ammo = Ammunition +stat.shieldhealth = Shield Health +stat.cooldowntime = Cooldown Time bar.drilltierreq = Bättre Borr Krävs bar.noresources = Missing Resources @@ -1302,4 +1302,4 @@ block.cyclone.description = A large anti-air and anti-ground turret. Fires explo block.spectre.description = A massive dual-barreled cannon. Shoots large armor-piercing bullets at air and ground targets. block.meltdown.description = A massive laser cannon. Charges and fires a persistent laser beam at nearby enemies. Requires coolant to operate. block.repair-point.description = Continuously heals the closest damaged unit in its vicinity. -block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. +block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. \ No newline at end of file diff --git a/core/assets/bundles/bundle_th.properties b/core/assets/bundles/bundle_th.properties index 6a89ac29ca..b86d8e7182 100644 --- a/core/assets/bundles/bundle_th.properties +++ b/core/assets/bundles/bundle_th.properties @@ -570,49 +570,49 @@ info.title = ข้อมูล error.title = [crimson]มีบางอย่างผิดพลาดเกิดขึ้น error.crashtitle = มีบางอย่างผิดพลาดเกิดขึ้น unit.nobuild = [scarlet]Unit can't build -blocks.input = นำเข้า -blocks.output = ส่งออก -blocks.booster = บูสเตอร์ -blocks.tiles = Required Tiles -blocks.affinities = Affinities +stat.input = นำเข้า +stat.output = ส่งออก +stat.booster = บูสเตอร์ +stat.tiles = Required Tiles +stat.affinities = Affinities block.unknown = [lightgray]??? -blocks.powercapacity = ความจุพลังงาน -blocks.powershot = หน่วยพลังงาน/นัด -blocks.damage = ดาเมจ -blocks.targetsair = ยิงอากาศยาน -blocks.targetsground = ยิงภาคพื้นดิน -blocks.itemsmoved = ความเร็วเคลื่อนที่ -blocks.launchtime = เวลาระหว่างการส่ง -blocks.shootrange = ระยะยิง -blocks.size = ขนาด -blocks.displaysize = Display Size -blocks.liquidcapacity = จุของเหลว -blocks.powerrange = ระยะพลังงาน -blocks.linkrange = Link Range -blocks.instructions = Instructions -blocks.powerconnections = จำนวนการเชื่อมต่อสูงสุด -blocks.poweruse = ใช้พลังงาน -blocks.powerdamage = หน่วยพลังงาน/ดาเมจ -blocks.itemcapacity = จุไอเท็ม -blocks.basepowergeneration = กำเนิดพลังงานพื้นฐาน -blocks.productiontime = เวลาที่ใช้ในการผลิต -blocks.repairtime = เวลาที่ใช้ในการซ่อมแซมให้สมบูรณ์ -blocks.speedincrease = เพิ่มความเร็ว -blocks.range = ระยะ -blocks.drilltier = ขุดได้ -blocks.drillspeed = ความเร็วขุดพื้นฐาน -blocks.boosteffect = แอฟเฟ็คของบูสต์ -blocks.maxunits = จำนวนยูนิตสูงสุด -blocks.health = เลือด -blocks.buildtime = เวลาในการสร้าง -blocks.maxconsecutive = Max Consecutive -blocks.buildcost = ใช้ -blocks.inaccuracy = ความคลาดเคลื่อน -blocks.shots = นัด -blocks.reload = นัด/วินาที -blocks.ammo = กระสุน -blocks.shieldhealth = Shield Health -blocks.cooldowntime = Cooldown Time +stat.powercapacity = ความจุพลังงาน +stat.powershot = หน่วยพลังงาน/นัด +stat.damage = ดาเมจ +stat.targetsair = ยิงอากาศยาน +stat.targetsground = ยิงภาคพื้นดิน +stat.itemsmoved = ความเร็วเคลื่อนที่ +stat.launchtime = เวลาระหว่างการส่ง +stat.shootrange = ระยะยิง +stat.size = ขนาด +stat.displaysize = Display Size +stat.liquidcapacity = จุของเหลว +stat.powerrange = ระยะพลังงาน +stat.linkrange = Link Range +stat.instructions = Instructions +stat.powerconnections = จำนวนการเชื่อมต่อสูงสุด +stat.poweruse = ใช้พลังงาน +stat.powerdamage = หน่วยพลังงาน/ดาเมจ +stat.itemcapacity = จุไอเท็ม +stat.basepowergeneration = กำเนิดพลังงานพื้นฐาน +stat.productiontime = เวลาที่ใช้ในการผลิต +stat.repairtime = เวลาที่ใช้ในการซ่อมแซมให้สมบูรณ์ +stat.speedincrease = เพิ่มความเร็ว +stat.range = ระยะ +stat.drilltier = ขุดได้ +stat.drillspeed = ความเร็วขุดพื้นฐาน +stat.boosteffect = แอฟเฟ็คของบูสต์ +stat.maxunits = จำนวนยูนิตสูงสุด +stat.health = เลือด +stat.buildtime = เวลาในการสร้าง +stat.maxconsecutive = Max Consecutive +stat.buildcost = ใช้ +stat.inaccuracy = ความคลาดเคลื่อน +stat.shots = นัด +stat.reload = นัด/วินาที +stat.ammo = กระสุน +stat.shieldhealth = Shield Health +stat.cooldowntime = Cooldown Time bar.drilltierreq = จำเป็นต้องใช้เครื่องขุดที่ดีกว่า bar.noresources = Missing Resources @@ -1302,4 +1302,4 @@ block.cyclone.description = ป้อมปืนต่อต้านอาก block.spectre.description = ปืนใหญ่ลำกล้องคูขนาดยักษ์. ยิงกระสุนเจาะเกราะใส่ศัตรูทั้งบนอากาศและภาดพื้นดิน. block.meltdown.description = ปืนใหญ่เลเซอร์ขนาดยักษ์. ชาร์จแล้วยิงลำแสงเลเซอร์ใส่ศัตรูที่อยู่ใกล้. จำเป็นต้องใช้สารหล่อเย็น. block.repair-point.description = ซ่อมแซมยูนิตที่อยู่ในรัศมีอย่างต่อเนื่อง. -block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. +block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. \ No newline at end of file diff --git a/core/assets/bundles/bundle_tk.properties b/core/assets/bundles/bundle_tk.properties index edfae05b29..cfac48c897 100644 --- a/core/assets/bundles/bundle_tk.properties +++ b/core/assets/bundles/bundle_tk.properties @@ -570,49 +570,49 @@ info.title = [accent]Bilgi error.title = [crimson]Bir hata olustu error.crashtitle = Bir hata olustu unit.nobuild = [scarlet]Unit can't build -blocks.input = Input -blocks.output = Output -blocks.booster = Booster -blocks.tiles = Required Tiles -blocks.affinities = Affinities +stat.input = Input +stat.output = Output +stat.booster = Booster +stat.tiles = Required Tiles +stat.affinities = Affinities block.unknown = [lightgray]??? -blocks.powercapacity = Guc kapasitesi -blocks.powershot = Guc/Saldiri hizi -blocks.damage = Damage -blocks.targetsair = Havayi hedef alir mi? -blocks.targetsground = Targets Ground -blocks.itemsmoved = Move Speed -blocks.launchtime = Time Between Launches -blocks.shootrange = Menzil -blocks.size = Buyukluk -blocks.displaysize = Display Size -blocks.liquidcapacity = Sivi kapasitesi -blocks.powerrange = Menzil -blocks.linkrange = Link Range -blocks.instructions = Instructions -blocks.powerconnections = Max Connections -blocks.poweruse = Guc kullanimi -blocks.powerdamage = Power/Damage -blocks.itemcapacity = Esya kapasitesi -blocks.basepowergeneration = Base Power Generation -blocks.productiontime = Production Time -blocks.repairtime = Block Full Repair Time -blocks.speedincrease = Speed Increase -blocks.range = Range -blocks.drilltier = Kazilabilirler -blocks.drillspeed = Ana kazma hizi -blocks.boosteffect = Boost Effect -blocks.maxunits = Max Active Units -blocks.health = Can -blocks.buildtime = Build Time -blocks.maxconsecutive = Max Consecutive -blocks.buildcost = Build Cost -blocks.inaccuracy = sekme -blocks.shots = vuruslar -blocks.reload = Yeniden doldurma -blocks.ammo = Ammo -blocks.shieldhealth = Shield Health -blocks.cooldowntime = Cooldown Time +stat.powercapacity = Guc kapasitesi +stat.powershot = Guc/Saldiri hizi +stat.damage = Damage +stat.targetsair = Havayi hedef alir mi? +stat.targetsground = Targets Ground +stat.itemsmoved = Move Speed +stat.launchtime = Time Between Launches +stat.shootrange = Menzil +stat.size = Buyukluk +stat.displaysize = Display Size +stat.liquidcapacity = Sivi kapasitesi +stat.powerrange = Menzil +stat.linkrange = Link Range +stat.instructions = Instructions +stat.powerconnections = Max Connections +stat.poweruse = Guc kullanimi +stat.powerdamage = Power/Damage +stat.itemcapacity = Esya kapasitesi +stat.basepowergeneration = Base Power Generation +stat.productiontime = Production Time +stat.repairtime = Block Full Repair Time +stat.speedincrease = Speed Increase +stat.range = Range +stat.drilltier = Kazilabilirler +stat.drillspeed = Ana kazma hizi +stat.boosteffect = Boost Effect +stat.maxunits = Max Active Units +stat.health = Can +stat.buildtime = Build Time +stat.maxconsecutive = Max Consecutive +stat.buildcost = Build Cost +stat.inaccuracy = sekme +stat.shots = vuruslar +stat.reload = Yeniden doldurma +stat.ammo = Ammo +stat.shieldhealth = Shield Health +stat.cooldowntime = Cooldown Time bar.drilltierreq = Better Drill Required bar.noresources = Missing Resources @@ -1302,4 +1302,4 @@ block.cyclone.description = A large rapid fire turret. block.spectre.description = A large turret which shoots two powerful bullets at once. block.meltdown.description = A large turret which shoots powerful long-range beams. block.repair-point.description = Continuously heals the closest damaged unit in its vicinity. -block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. +block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. \ No newline at end of file diff --git a/core/assets/bundles/bundle_tr.properties b/core/assets/bundles/bundle_tr.properties index 1fd6a6618e..395e062453 100644 --- a/core/assets/bundles/bundle_tr.properties +++ b/core/assets/bundles/bundle_tr.properties @@ -570,49 +570,49 @@ info.title = Bilgi error.title = [crimson]Bir hata oldu error.crashtitle = Bir hata oldu unit.nobuild = [scarlet]Unit can't build -blocks.input = Giriş -blocks.output = Çıkış -blocks.booster = Güçlendirici -blocks.tiles = Required Tiles -blocks.affinities = Affinities +stat.input = Giriş +stat.output = Çıkış +stat.booster = Güçlendirici +stat.tiles = Required Tiles +stat.affinities = Affinities block.unknown = [lightgray]??? -blocks.powercapacity = Enerji Kapasitesi -blocks.powershot = Enerji/Atış -blocks.damage = Hasar -blocks.targetsair = Havayı Hedefler Mi -blocks.targetsground = Yeri Hedefler Mi -blocks.itemsmoved = Hareket Hızı -blocks.launchtime = Fırlatmalar Arasındaki Süre -blocks.shootrange = Menzil -blocks.size = Boyut -blocks.displaysize = Display Size -blocks.liquidcapacity = Sıvı Kapasitesi -blocks.powerrange = Enerji Menzili -blocks.linkrange = Link Range -blocks.instructions = Instructions -blocks.powerconnections = Bağlantı sayısı -blocks.poweruse = Enerji Kullanımı -blocks.powerdamage = Enerji/Hasar -blocks.itemcapacity = Eşya Kapasitesi -blocks.basepowergeneration = Temel Enerji Üretimi -blocks.productiontime = Üretim Süresi -blocks.repairtime = Tamir Tamir Edilme Süresi -blocks.speedincrease = Hız Artışı -blocks.range = Menzil -blocks.drilltier = Kazılabilenler -blocks.drillspeed = Temel Matkap Hızı -blocks.boosteffect = Hızlandırma Efekti -blocks.maxunits = Maksimum Aktif Birim -blocks.health = Can -blocks.buildtime = İnşaat Süresi -blocks.maxconsecutive = Max Consecutive -blocks.buildcost = İnşaat Fiyatı -blocks.inaccuracy = İskalama Oranı -blocks.shots = Atışlar -blocks.reload = Atışlar/Sn -blocks.ammo = Mermi -blocks.shieldhealth = Shield Health -blocks.cooldowntime = Cooldown Time +stat.powercapacity = Enerji Kapasitesi +stat.powershot = Enerji/Atış +stat.damage = Hasar +stat.targetsair = Havayı Hedefler Mi +stat.targetsground = Yeri Hedefler Mi +stat.itemsmoved = Hareket Hızı +stat.launchtime = Fırlatmalar Arasındaki Süre +stat.shootrange = Menzil +stat.size = Boyut +stat.displaysize = Display Size +stat.liquidcapacity = Sıvı Kapasitesi +stat.powerrange = Enerji Menzili +stat.linkrange = Link Range +stat.instructions = Instructions +stat.powerconnections = Bağlantı sayısı +stat.poweruse = Enerji Kullanımı +stat.powerdamage = Enerji/Hasar +stat.itemcapacity = Eşya Kapasitesi +stat.basepowergeneration = Temel Enerji Üretimi +stat.productiontime = Üretim Süresi +stat.repairtime = Tamir Tamir Edilme Süresi +stat.speedincrease = Hız Artışı +stat.range = Menzil +stat.drilltier = Kazılabilenler +stat.drillspeed = Temel Matkap Hızı +stat.boosteffect = Hızlandırma Efekti +stat.maxunits = Maksimum Aktif Birim +stat.health = Can +stat.buildtime = İnşaat Süresi +stat.maxconsecutive = Max Consecutive +stat.buildcost = İnşaat Fiyatı +stat.inaccuracy = İskalama Oranı +stat.shots = Atışlar +stat.reload = Atışlar/Sn +stat.ammo = Mermi +stat.shieldhealth = Shield Health +stat.cooldowntime = Cooldown Time bar.drilltierreq = Daha İyi Matkap Gerekli bar.noresources = Missing Resources @@ -1302,4 +1302,4 @@ block.cyclone.description = Büyük bir anti hava ve anti kara tareti. Yakının block.spectre.description = Dev bir çift namlulu top. Hava ve kara birimlerine iri, zırh delici mermiler atar. block.meltdown.description = Dev bir lazer topu. Yüklenip yakındaki düşmanlara uzun süreli lazer ışınları yollar. Çalışması için soğutucu gerekir. block.repair-point.description = Kendisine en yakın hasarlı birimi tamir eder. -block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. +block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. \ No newline at end of file diff --git a/core/assets/bundles/bundle_uk_UA.properties b/core/assets/bundles/bundle_uk_UA.properties index 05deecc5b3..dfc4bae48b 100644 --- a/core/assets/bundles/bundle_uk_UA.properties +++ b/core/assets/bundles/bundle_uk_UA.properties @@ -565,50 +565,50 @@ info.title = Інформація error.title = [crimson]Виникла помилка error.crashtitle = Виникла помилка unit.nobuild = [scarlet]Ця одиниця не може будувати -blocks.input = Ввід -blocks.output = Вивід -blocks.booster = Прискорювач -blocks.tiles = Необхідні плитки -blocks.affinities = Збільшення ефективності +stat.input = Ввід +stat.output = Вивід +stat.booster = Прискорювач +stat.tiles = Необхідні плитки +stat.affinities = Збільшення ефективності block.unknown = [lightgray]??? -blocks.powercapacity = Місткість енергії -blocks.powershot = Енергія за постріл -blocks.damage = Шкода -blocks.targetsair = Повітряні противники -blocks.targetsground = Наземні противники -blocks.itemsmoved = Швидкість переміщення -blocks.launchtime = Час між запусками -blocks.shootrange = Радіус дії -blocks.size = Розмір -blocks.displaysize = Розмір дисплею -blocks.liquidcapacity = Рідинна місткість -blocks.powerrange = Радіус передачі енергії -blocks.linkrange = Радіус з’єднання -blocks.instructions = Інструкції -blocks.powerconnections = Максимальна кількість з’єднань -blocks.poweruse = Енергії використовує -blocks.powerdamage = Енергії за од. шкоди -blocks.itemcapacity = Місткість предметів -blocks.memorycapacity = Ємність пам’яті -blocks.basepowergeneration = Базова генерація енергії -blocks.productiontime = Час виробництва -blocks.repairtime = Час повного відновлення блоку -blocks.speedincrease = Збільшення швидкості -blocks.range = Радіус дії -blocks.drilltier = Видобуває -blocks.drillspeed = Базова швидкість буріння -blocks.boosteffect = Прискорювальний ефект -blocks.maxunits = Максимальна кількість активних одиниць -blocks.health = Здоров’я -blocks.buildtime = Час будування -blocks.maxconsecutive = Максимальна послідовність -blocks.buildcost = Вартість будування -blocks.inaccuracy = Розкид -blocks.shots = Постріли -blocks.reload = Постріли/секунду -blocks.ammo = Боєприпаси -blocks.shieldhealth = Міцність щита -blocks.cooldowntime = Тривалість охолодження +stat.powercapacity = Місткість енергії +stat.powershot = Енергія за постріл +stat.damage = Шкода +stat.targetsair = Повітряні противники +stat.targetsground = Наземні противники +stat.itemsmoved = Швидкість переміщення +stat.launchtime = Час між запусками +stat.shootrange = Радіус дії +stat.size = Розмір +stat.displaysize = Розмір дисплею +stat.liquidcapacity = Рідинна місткість +stat.powerrange = Радіус передачі енергії +stat.linkrange = Радіус з’єднання +stat.instructions = Інструкції +stat.powerconnections = Максимальна кількість з’єднань +stat.poweruse = Енергії використовує +stat.powerdamage = Енергії за од. шкоди +stat.itemcapacity = Місткість предметів +stat.memorycapacity = Ємність пам’яті +stat.basepowergeneration = Базова генерація енергії +stat.productiontime = Час виробництва +stat.repairtime = Час повного відновлення блоку +stat.speedincrease = Збільшення швидкості +stat.range = Радіус дії +stat.drilltier = Видобуває +stat.drillspeed = Базова швидкість буріння +stat.boosteffect = Прискорювальний ефект +stat.maxunits = Максимальна кількість активних одиниць +stat.health = Здоров’я +stat.buildtime = Час будування +stat.maxconsecutive = Максимальна послідовність +stat.buildcost = Вартість будування +stat.inaccuracy = Розкид +stat.shots = Постріли +stat.reload = Постріли/секунду +stat.ammo = Боєприпаси +stat.shieldhealth = Міцність щита +stat.cooldowntime = Тривалість охолодження bar.drilltierreq = Потребується кращий бур bar.noresources = Бракує ресурсів @@ -1300,4 +1300,4 @@ block.cyclone.description = Велика протиповітряна та пр block.spectre.description = Масивна двоствольна гармата. Стріляє великими бронебійними кулями в повітряні та наземні цілі. block.meltdown.description = Масивна лазерна гармата. Заряджає і стріляє лазерним променем у найближчих противників. Для роботи потрібен теплоносій. block.repair-point.description = Безперервно ремонтує найближчу пошкоджену бойову одиницю. -block.segment.description = Пошкоджує та руйнує вхідні снаряди. Окрім лазерних. +block.segment.description = Пошкоджує та руйнує вхідні снаряди. Окрім лазерних. \ No newline at end of file diff --git a/core/assets/bundles/bundle_zh_CN.properties b/core/assets/bundles/bundle_zh_CN.properties index 5c5c529ae2..5744d3d0fd 100644 --- a/core/assets/bundles/bundle_zh_CN.properties +++ b/core/assets/bundles/bundle_zh_CN.properties @@ -570,49 +570,49 @@ info.title = [accent]详情 error.title = [crimson]发生了一个错误 error.crashtitle = 发生了一个错误 unit.nobuild = [scarlet]单位未能建造 -blocks.input = 输入 -blocks.output = 输出 -blocks.booster = 增强物品/液体 -blocks.tiles = 所需地型 -blocks.affinities = 相关 +stat.input = 输入 +stat.output = 输出 +stat.booster = 增强物品/液体 +stat.tiles = 所需地型 +stat.affinities = 相关 block.unknown = [lightgray]??? -blocks.powercapacity = 能量容量 -blocks.powershot = 能量/发射 -blocks.damage = 伤害 -blocks.targetsair = 攻击空中单位 -blocks.targetsground = 攻击地面单位 -blocks.itemsmoved = 移动速度 -blocks.launchtime = 发射间隔时间 -blocks.shootrange = 范围 -blocks.size = 尺寸 -blocks.displaysize = 显示尺寸 -blocks.liquidcapacity = 液体容量 -blocks.powerrange = 能量范围 -blocks.linkrange = 连接范围 -blocks.instructions = 指令数量 -blocks.powerconnections = 最多连接 -blocks.poweruse = 使用能量 -blocks.powerdamage = 功率/损伤 -blocks.itemcapacity = 物品容量 -blocks.basepowergeneration = 基础能源输出 -blocks.productiontime = 生产时间 -blocks.repairtime = 建筑完全修复时间 -blocks.speedincrease = 提速 -blocks.range = 范围 -blocks.drilltier = 可钻探矿物 -blocks.drillspeed = 基础钻探速度 -blocks.boosteffect = 增强效果 -blocks.maxunits = 最大单位数量 -blocks.health = 生命值 -blocks.buildtime = 建造时间 -blocks.maxconsecutive = 最大连续 -blocks.buildcost = 建造花费 -blocks.inaccuracy = 误差 -blocks.shots = 发射数 -blocks.reload = 每秒发射数 -blocks.ammo = 弹药 -blocks.shieldhealth = 盾容 -blocks.cooldowntime = 冷却时间 +stat.powercapacity = 能量容量 +stat.powershot = 能量/发射 +stat.damage = 伤害 +stat.targetsair = 攻击空中单位 +stat.targetsground = 攻击地面单位 +stat.itemsmoved = 移动速度 +stat.launchtime = 发射间隔时间 +stat.shootrange = 范围 +stat.size = 尺寸 +stat.displaysize = 显示尺寸 +stat.liquidcapacity = 液体容量 +stat.powerrange = 能量范围 +stat.linkrange = 连接范围 +stat.instructions = 指令数量 +stat.powerconnections = 最多连接 +stat.poweruse = 使用能量 +stat.powerdamage = 功率/损伤 +stat.itemcapacity = 物品容量 +stat.basepowergeneration = 基础能源输出 +stat.productiontime = 生产时间 +stat.repairtime = 建筑完全修复时间 +stat.speedincrease = 提速 +stat.range = 范围 +stat.drilltier = 可钻探矿物 +stat.drillspeed = 基础钻探速度 +stat.boosteffect = 增强效果 +stat.maxunits = 最大单位数量 +stat.health = 生命值 +stat.buildtime = 建造时间 +stat.maxconsecutive = 最大连续 +stat.buildcost = 建造花费 +stat.inaccuracy = 误差 +stat.shots = 发射数 +stat.reload = 每秒发射数 +stat.ammo = 弹药 +stat.shieldhealth = 盾容 +stat.cooldowntime = 冷却时间 bar.drilltierreq = 需要更好的钻头 bar.noresources = 缺失资源 @@ -1302,4 +1302,4 @@ block.cyclone.description = 大型炮塔,对空对地,发射在敌人周围 block.spectre.description = 超大型炮塔,对空对地,一次射出两颗强大的破甲弹。 block.meltdown.description = 超大型激光炮塔,充能之后持续发射光束,需要冷却剂。 block.repair-point.description = 持续治疗其附近受损最严重的单位。 -block.segment.description = 摧毁袭来的除激光以外的子弹或导弹. +block.segment.description = 摧毁袭来的除激光以外的子弹或导弹. \ No newline at end of file diff --git a/core/assets/bundles/bundle_zh_TW.properties b/core/assets/bundles/bundle_zh_TW.properties index 85efa9a527..e495f4b768 100644 --- a/core/assets/bundles/bundle_zh_TW.properties +++ b/core/assets/bundles/bundle_zh_TW.properties @@ -570,49 +570,49 @@ info.title = 資訊 error.title = [crimson]發生錯誤 error.crashtitle = 發生錯誤 unit.nobuild = [scarlet]單位不能建造 -blocks.input = 輸入 -blocks.output = 輸出 -blocks.booster = 強化 -blocks.tiles = 需求方塊 -blocks.affinities = 親和方塊 +stat.input = 輸入 +stat.output = 輸出 +stat.booster = 強化 +stat.tiles = 需求方塊 +stat.affinities = 親和方塊 block.unknown = [lightgray]??? -blocks.powercapacity = 蓄電量 -blocks.powershot = 能量/射擊 -blocks.damage = 傷害 -blocks.targetsair = 攻擊空中目標 -blocks.targetsground = 攻擊地面目標 -blocks.itemsmoved = 移動速度 -blocks.launchtime = 發射間隔 -blocks.shootrange = 範圍 -blocks.size = 尺寸 -blocks.displaysize = Display Size -blocks.liquidcapacity = 液體容量 -blocks.powerrange = 輸出範圍 -blocks.linkrange = Link Range -blocks.instructions = Instructions -blocks.powerconnections = 最大連接數 -blocks.poweruse = 能量使用 -blocks.powerdamage = 能量/傷害 -blocks.itemcapacity = 物品容量 -blocks.basepowergeneration = 基礎能量生產 -blocks.productiontime = 生產時間 -blocks.repairtime = 方塊完全修復時間 -blocks.speedincrease = 速度提升 -blocks.range = 範圍 -blocks.drilltier = 可鑽取礦物 -blocks.drillspeed = 基本鑽取速度 -blocks.boosteffect = 提升效應 -blocks.maxunits = 最大活躍單位 -blocks.health = 耐久度 -blocks.buildtime = 建設時間 -blocks.maxconsecutive = Max Consecutive -blocks.buildcost = 建造成本 -blocks.inaccuracy = 誤差 -blocks.shots = 射擊數 -blocks.reload = 射擊次數/秒 -blocks.ammo = 彈藥 -blocks.shieldhealth = Shield Health -blocks.cooldowntime = Cooldown Time +stat.powercapacity = 蓄電量 +stat.powershot = 能量/射擊 +stat.damage = 傷害 +stat.targetsair = 攻擊空中目標 +stat.targetsground = 攻擊地面目標 +stat.itemsmoved = 移動速度 +stat.launchtime = 發射間隔 +stat.shootrange = 範圍 +stat.size = 尺寸 +stat.displaysize = Display Size +stat.liquidcapacity = 液體容量 +stat.powerrange = 輸出範圍 +stat.linkrange = Link Range +stat.instructions = Instructions +stat.powerconnections = 最大連接數 +stat.poweruse = 能量使用 +stat.powerdamage = 能量/傷害 +stat.itemcapacity = 物品容量 +stat.basepowergeneration = 基礎能量生產 +stat.productiontime = 生產時間 +stat.repairtime = 方塊完全修復時間 +stat.speedincrease = 速度提升 +stat.range = 範圍 +stat.drilltier = 可鑽取礦物 +stat.drillspeed = 基本鑽取速度 +stat.boosteffect = 提升效應 +stat.maxunits = 最大活躍單位 +stat.health = 耐久度 +stat.buildtime = 建設時間 +stat.maxconsecutive = Max Consecutive +stat.buildcost = 建造成本 +stat.inaccuracy = 誤差 +stat.shots = 射擊數 +stat.reload = 射擊次數/秒 +stat.ammo = 彈藥 +stat.shieldhealth = Shield Health +stat.cooldowntime = Cooldown Time bar.drilltierreq = 需要更好的鑽頭 bar.noresources = Missing Resources @@ -1302,4 +1302,4 @@ block.cyclone.description = 一種對空和對地的大型砲塔。向附近單 block.spectre.description = 一種雙炮管的巨型砲塔。向空中及地面敵人發射大型的穿甲彈。 block.meltdown.description = 一種巨型激光砲塔。充能並發射持續性的激光光束。需要冷卻液以運作。 block.repair-point.description = 持續治療附近最近的受損單位。 -block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. +block.segment.description = Damages and destroys incoming projectiles. Laser projectiles are not targeted. \ No newline at end of file diff --git a/core/assets/contributors b/core/assets/contributors index 69de5a3d8e..9f1cf8c9f4 100644 --- a/core/assets/contributors +++ b/core/assets/contributors @@ -95,3 +95,4 @@ ThePlayerA YellOw139 PetrGasparik LeoDog896 +Summet diff --git a/core/assets/maps/caldera.msav b/core/assets/maps/caldera.msav index fae2a2e862..57bb5c382f 100644 Binary files a/core/assets/maps/caldera.msav and b/core/assets/maps/caldera.msav differ diff --git a/core/assets/maps/craters.msav b/core/assets/maps/craters.msav index b10b354bf0..04c1aa73d3 100644 Binary files a/core/assets/maps/craters.msav and b/core/assets/maps/craters.msav differ diff --git a/core/assets/maps/fork.msav b/core/assets/maps/fork.msav index f4bf6985fe..2fd62bd91b 100644 Binary files a/core/assets/maps/fork.msav and b/core/assets/maps/fork.msav differ diff --git a/core/assets/maps/frozenForest.msav b/core/assets/maps/frozenForest.msav index a2a1f52354..0eb9f1fe56 100644 Binary files a/core/assets/maps/frozenForest.msav and b/core/assets/maps/frozenForest.msav differ diff --git a/core/assets/maps/fungalPass.msav b/core/assets/maps/fungalPass.msav index c154c2e4db..1e3a26f520 100644 Binary files a/core/assets/maps/fungalPass.msav and b/core/assets/maps/fungalPass.msav differ diff --git a/core/assets/maps/islands.msav b/core/assets/maps/islands.msav index 1b833e98d0..90159d57e0 100644 Binary files a/core/assets/maps/islands.msav and b/core/assets/maps/islands.msav differ diff --git a/core/assets/maps/ruinousShores.msav b/core/assets/maps/ruinousShores.msav index a0ef267a99..41e957c0ff 100644 Binary files a/core/assets/maps/ruinousShores.msav and b/core/assets/maps/ruinousShores.msav differ diff --git a/core/assets/maps/saltFlats.msav b/core/assets/maps/saltFlats.msav index 9a70ba342a..556b33700f 100644 Binary files a/core/assets/maps/saltFlats.msav and b/core/assets/maps/saltFlats.msav differ diff --git a/core/assets/maps/shattered.msav b/core/assets/maps/shattered.msav index a822e2d121..549e42cab7 100644 Binary files a/core/assets/maps/shattered.msav and b/core/assets/maps/shattered.msav differ diff --git a/core/assets/maps/tarFields.msav b/core/assets/maps/tarFields.msav index 06eaed164d..bf8b306f95 100644 Binary files a/core/assets/maps/tarFields.msav and b/core/assets/maps/tarFields.msav differ diff --git a/core/assets/maps/tendrils.msav b/core/assets/maps/tendrils.msav index 3034fb5ec1..825437d6c5 100644 Binary files a/core/assets/maps/tendrils.msav and b/core/assets/maps/tendrils.msav differ diff --git a/core/assets/shaders/default.frag b/core/assets/shaders/default.frag new file mode 100644 index 0000000000..93be3cffc9 --- /dev/null +++ b/core/assets/shaders/default.frag @@ -0,0 +1,9 @@ +varying lowp vec4 v_color; +varying lowp vec4 v_mix_color; +varying vec2 v_texCoords; +uniform sampler2D u_texture; + +void main(){ + vec4 c = texture2D(u_texture, v_texCoords); + gl_FragColor = v_color * mix(c, vec4(v_mix_color.rgb, c.a), v_mix_color.a); +} \ No newline at end of file diff --git a/core/assets/sprites/block_colors.png b/core/assets/sprites/block_colors.png index 3e19c96e61..e8845aa072 100644 Binary files a/core/assets/sprites/block_colors.png and b/core/assets/sprites/block_colors.png differ diff --git a/core/assets/sprites/fallback/sprites5.png b/core/assets/sprites/fallback/sprites5.png index e413db1a18..87ee3a7b46 100644 Binary files a/core/assets/sprites/fallback/sprites5.png and b/core/assets/sprites/fallback/sprites5.png differ diff --git a/core/assets/sprites/fallback/sprites6.png b/core/assets/sprites/fallback/sprites6.png index 94069305e4..a47c2d47eb 100644 Binary files a/core/assets/sprites/fallback/sprites6.png and b/core/assets/sprites/fallback/sprites6.png differ diff --git a/core/assets/sprites/fallback/sprites7.png b/core/assets/sprites/fallback/sprites7.png index 9e083fdec4..dd3d40dc80 100644 Binary files a/core/assets/sprites/fallback/sprites7.png and b/core/assets/sprites/fallback/sprites7.png differ diff --git a/core/assets/sprites/fallback/sprites8.png b/core/assets/sprites/fallback/sprites8.png index 4c47261514..212d169c38 100644 Binary files a/core/assets/sprites/fallback/sprites8.png and b/core/assets/sprites/fallback/sprites8.png differ diff --git a/core/assets/sprites/fog.png b/core/assets/sprites/fog.png new file mode 100644 index 0000000000..5121693e58 Binary files /dev/null and b/core/assets/sprites/fog.png differ diff --git a/core/assets/sprites/sprites2.png b/core/assets/sprites/sprites2.png index fba6d8fe48..4a26e4dbe9 100644 Binary files a/core/assets/sprites/sprites2.png and b/core/assets/sprites/sprites2.png differ diff --git a/core/assets/sprites/sprites3.png b/core/assets/sprites/sprites3.png index 6e26e044e2..627f4e763c 100644 Binary files a/core/assets/sprites/sprites3.png and b/core/assets/sprites/sprites3.png differ diff --git a/core/assets/sprites/sprites4.png b/core/assets/sprites/sprites4.png index 4a76287a49..20eed12539 100644 Binary files a/core/assets/sprites/sprites4.png and b/core/assets/sprites/sprites4.png differ diff --git a/core/assets/sprites/sprites5.png b/core/assets/sprites/sprites5.png index a85adcfbb9..2fe5605d60 100644 Binary files a/core/assets/sprites/sprites5.png and b/core/assets/sprites/sprites5.png differ diff --git a/core/src/mindustry/Vars.java b/core/src/mindustry/Vars.java index 37ba22d3cb..2cf07cbea6 100644 --- a/core/src/mindustry/Vars.java +++ b/core/src/mindustry/Vars.java @@ -36,6 +36,8 @@ public class Vars implements Loadable{ public static boolean loadLocales = true; /** Whether the logger is loaded. */ public static boolean loadedLogger = false, loadedFileLogger = false; + /** Whether to show the cliff button in the editor*/ + public static boolean addCliffButton = false; /** Maximum extra padding around deployment schematics. */ public static final int maxLoadoutSchematicPad = 5; /** Maximum schematic size.*/ @@ -86,8 +88,10 @@ public class Vars implements Loadable{ public static final float logicItemTransferRange = 45f; /** duration of time between turns in ticks */ public static final float turnDuration = 2 * Time.toMinutes; - /** turns needed to destroy a sector completely */ - public static final float sectorDestructionTurns = 2f; + /** chance of an invasion per turn, 1 = 100% */ + public static final float baseInvasionChance = 1f / 30f; + /** how many turns have to pass before invasions start */ + public static final int invasionGracePeriod = 20; /** min armor fraction damage; e.g. 0.05 = at least 5% damage */ public static final float minArmorDamage = 0.1f; /** launch animation duration */ @@ -281,10 +285,10 @@ public class Vars implements Loadable{ if(loadedLogger) return; String[] tags = {"[green][D][]", "[royal][I][]", "[yellow][W][]", "[scarlet][E][]", ""}; - String[] stags = {"&lc&fb[D]", "&lg&fb[I]", "&ly&fb[W]", "&lr&fb[E]", ""}; + String[] stags = {"&lc&fb[D]", "&lb&fb[I]", "&ly&fb[W]", "&lr&fb[E]", ""}; Seq logBuffer = new Seq<>(); - Log.setLogger((level, text) -> { + Log.logger = (level, text) -> { String result = text; String rawText = Log.format(stags[level.ordinal()] + "&fr " + text); System.out.println(rawText); @@ -300,9 +304,9 @@ public class Vars implements Loadable{ } } - ui.scriptfrag.addMessage(Log.removeCodes(result)); + ui.scriptfrag.addMessage(Log.removeColors(result)); } - }); + }; Events.on(ClientLoadEvent.class, e -> logBuffer.each(ui.scriptfrag::addMessage)); @@ -315,18 +319,19 @@ public class Vars implements Loadable{ settings.setAppName(appName); Writer writer = settings.getDataDirectory().child("last_log.txt").writer(false); - LogHandler log = Log.getLogger(); - Log.setLogger((level, text) -> { + LogHandler log = Log.logger; + //ignore it + Log.logger = (level, text) -> { log.log(level, text); try{ - writer.write("[" + Character.toUpperCase(level.name().charAt(0)) +"] " + Log.removeCodes(text) + "\n"); + writer.write("[" + Character.toUpperCase(level.name().charAt(0)) +"] " + Log.removeColors(text) + "\n"); writer.flush(); }catch(IOException e){ e.printStackTrace(); //ignore it } - }); + }; loadedFileLogger = true; } diff --git a/core/src/mindustry/ai/BaseAI.java b/core/src/mindustry/ai/BaseAI.java index 3b45d8a17b..2c1fb11617 100644 --- a/core/src/mindustry/ai/BaseAI.java +++ b/core/src/mindustry/ai/BaseAI.java @@ -7,6 +7,7 @@ import arc.util.*; import mindustry.*; import mindustry.ai.BaseRegistry.*; import mindustry.content.*; +import mindustry.core.*; import mindustry.game.*; import mindustry.game.Schematic.*; import mindustry.game.Teams.*; @@ -23,7 +24,7 @@ public class BaseAI{ private static final Vec2 axis = new Vec2(), rotator = new Vec2(); private static final float correctPercent = 0.5f; private static final float step = 5; - private static final int attempts = 5; + private static final int attempts = 4; private static final float emptyChance = 0.01f; private static final int timerStep = 0, timerSpawn = 1; @@ -40,11 +41,11 @@ public class BaseAI{ } public void update(){ - if(timer.get(timerSpawn, 60) && data.hasCore()){ + if(data.team.rules().aiCoreSpawn && timer.get(timerSpawn, 60 * 2.5f) && data.hasCore()){ CoreBlock block = (CoreBlock)data.core().block; //create AI core unit - if(!state.isEditor() && !Groups.unit.contains(u -> u.team() == data.team && u.type() == block.unitType)){ + if(!state.isEditor() && !Groups.unit.contains(u -> u.team() == data.team && u.type == block.unitType)){ Unit unit = block.unitType.create(data.team); unit.set(data.core()); unit.add(); @@ -68,9 +69,14 @@ public class BaseAI{ if(pos == null) return; Tmp.v1.rnd(Mathf.random(range)); - int wx = (int)(world.toTile(pos.getX()) + Tmp.v1.x), wy = (int)(world.toTile(pos.getY()) + Tmp.v1.y); + int wx = (int)(World.toTile(pos.getX()) + Tmp.v1.x), wy = (int)(World.toTile(pos.getY()) + Tmp.v1.y); Tile tile = world.tiles.getc(wx, wy); + //try not to block the spawn point + if(spawner.getSpawns().contains(t -> t.within(tile, tilesize * 40f))){ + continue; + } + Seq parts = null; //pick a completely random base part, and place it a random location diff --git a/core/src/mindustry/ai/BlockIndexer.java b/core/src/mindustry/ai/BlockIndexer.java index 6092c83910..a75b0758ea 100644 --- a/core/src/mindustry/ai/BlockIndexer.java +++ b/core/src/mindustry/ai/BlockIndexer.java @@ -8,6 +8,7 @@ import arc.struct.EnumSet; import arc.struct.*; import arc.util.*; import mindustry.content.*; +import mindustry.core.*; import mindustry.game.EventType.*; import mindustry.game.*; import mindustry.game.Teams.*; @@ -179,8 +180,8 @@ public class BlockIndexer{ public boolean eachBlock(Team team, float wx, float wy, float range, Boolf pred, Cons cons){ intSet.clear(); - int tx = world.toTile(wx); - int ty = world.toTile(wy); + int tx = World.toTile(wx); + int ty = World.toTile(wy); int tileRange = (int)(range / tilesize + 1); boolean any = false; diff --git a/core/src/mindustry/ai/Pathfinder.java b/core/src/mindustry/ai/Pathfinder.java index c24cecd49c..78a596bb9d 100644 --- a/core/src/mindustry/ai/Pathfinder.java +++ b/core/src/mindustry/ai/Pathfinder.java @@ -7,6 +7,7 @@ import arc.struct.*; import arc.util.*; import arc.util.async.*; import mindustry.annotations.Annotations.*; +import mindustry.core.*; import mindustry.game.EventType.*; import mindustry.game.*; import mindustry.gen.*; @@ -85,9 +86,6 @@ public class Pathfinder implements Runnable{ tiles[tile.x][tile.y] = packTile(tile); } - //special preset which may help speed things up; this is optional - preloadPath(getField(state.rules.waveTeam, costGround, fieldCore)); - start(); }); @@ -105,7 +103,7 @@ public class Pathfinder implements Runnable{ boolean nearLiquid = false, nearSolid = false, nearGround = false; for(int i = 0; i < 4; i++){ - Tile other = tile.getNearby(i); + Tile other = tile.nearby(i); if(other != null){ if(other.floor().isLiquid) nearLiquid = true; if(other.solid()) nearSolid = true; @@ -114,7 +112,7 @@ public class Pathfinder implements Runnable{ } return PathTile.get( - tile.build == null ? 0 : Math.min((int)(tile.build.health / 40), 80), + tile.build == null || !tile.solid() ? 0 : Math.min((int)(tile.build.health / 40), 80), tile.getTeamID(), tile.solid(), tile.floor().isLiquid, @@ -444,7 +442,7 @@ public class Pathfinder implements Runnable{ @Override public void getPositions(IntSeq out){ - out.add(Point2.pack(world.toTile(position.getX()), world.toTile(position.getY()))); + out.add(Point2.pack(World.toTile(position.getX()), World.toTile(position.getY()))); } } @@ -453,7 +451,7 @@ public class Pathfinder implements Runnable{ * Data for a flow field to some set of destinations. * Concrete subclasses must specify a way to fetch costs and destinations. * */ - static abstract class Flowfield{ + public static abstract class Flowfield{ /** Refresh rate in milliseconds. Return any number <= 0 to disable. */ protected int refreshRate; /** Team this path is for. Set before using. */ @@ -462,7 +460,7 @@ public class Pathfinder implements Runnable{ protected PathCost cost = costTypes.get(costGround); /** costs of getting to a specific tile */ - int[][] weights; + public int[][] weights; /** search IDs of each position - the highest, most recent search is prioritized and overwritten */ int[][] searches; /** search frontier, these are Pos objects */ diff --git a/core/src/mindustry/ai/WaveSpawner.java b/core/src/mindustry/ai/WaveSpawner.java index 9cecf32ac1..c420972457 100644 --- a/core/src/mindustry/ai/WaveSpawner.java +++ b/core/src/mindustry/ai/WaveSpawner.java @@ -8,6 +8,7 @@ import arc.struct.*; import arc.util.*; import mindustry.annotations.Annotations.*; import mindustry.content.*; +import mindustry.core.*; import mindustry.entities.*; import mindustry.game.EventType.*; import mindustry.game.*; @@ -23,11 +24,21 @@ public class WaveSpawner{ private Seq spawns = new Seq<>(); private boolean spawning = false; private boolean any = false; + private Tile firstSpawn = null; public WaveSpawner(){ Events.on(WorldLoadEvent.class, e -> reset()); } + @Nullable + public Tile getFirstSpawn(){ + firstSpawn = null; + eachGroundSpawn((cx, cy) -> { + firstSpawn = world.tile(cx, cy); + }); + return firstSpawn; + } + public int countSpawns(){ return spawns.size; } @@ -38,7 +49,7 @@ public class WaveSpawner{ /** @return true if the player is near a ground spawn point. */ public boolean playerNear(){ - return !player.dead() && spawns.contains(g -> Mathf.dst(g.x * tilesize, g.y * tilesize, player.x, player.y) < state.rules.dropZoneRadius && player.team() != state.rules.waveTeam); + return state.hasSpawns() && !player.dead() && spawns.contains(g -> Mathf.dst(g.x * tilesize, g.y * tilesize, player.x, player.y) < state.rules.dropZoneRadius && player.team() != state.rules.waveTeam); } public void spawnEnemies(){ @@ -47,7 +58,7 @@ public class WaveSpawner{ for(SpawnGroup group : state.rules.spawns){ if(group.type == null) continue; - int spawned = group.getUnitsSpawned(state.wave - 1); + int spawned = group.getSpawned(state.wave - 1); if(group.type.flying){ float spread = margin / 1.5f; @@ -69,7 +80,7 @@ public class WaveSpawner{ Unit unit = group.createUnit(state.rules.waveTeam, state.wave - 1); unit.set(spawnX + Tmp.v1.x, spawnY + Tmp.v1.y); - Time.run(Math.min(i * 5, 60 * 2), () -> spawnEffect(unit)); + spawnEffect(unit); } }); } @@ -89,9 +100,15 @@ public class WaveSpawner{ Time.run(40f, () -> Damage.damage(state.rules.waveTeam, x, y, state.rules.dropZoneRadius, 99999999f, true)); } + public void eachGroundSpawn(Intc2 cons){ + eachGroundSpawn((x, y, shock) -> cons.get(World.toTile(x), World.toTile(y))); + } + private void eachGroundSpawn(SpawnConsumer cons){ - for(Tile spawn : spawns){ - cons.accept(spawn.worldx(), spawn.worldy(), true); + if(state.hasSpawns()){ + for(Tile spawn : spawns){ + cons.accept(spawn.worldx(), spawn.worldy(), true); + } } if(state.rules.attackMode && state.teams.isActive(state.rules.waveTeam) && !state.teams.playerCores().isEmpty()){ @@ -104,7 +121,7 @@ public class WaveSpawner{ //keep moving forward until the max step amount is reached while(steps++ < maxSteps){ - int tx = world.toTile(core.x + Tmp.v1.x), ty = world.toTile(core.y + Tmp.v1.y); + int tx = World.toTile(core.x + Tmp.v1.x), ty = World.toTile(core.y + Tmp.v1.y); any = false; Geometry.circle(tx, ty, world.width(), world.height(), 3, (x, y) -> { if(world.solid(x, y)){ @@ -161,7 +178,7 @@ public class WaveSpawner{ } private void spawnEffect(Unit unit){ - Call.spawnEffect(unit.x, unit.y, unit.type()); + Call.spawnEffect(unit.x, unit.y, unit.type); Time.run(30f, unit::add); } diff --git a/core/src/mindustry/ai/types/BuilderAI.java b/core/src/mindustry/ai/types/BuilderAI.java index 8e8c74f986..c3558ca58a 100644 --- a/core/src/mindustry/ai/types/BuilderAI.java +++ b/core/src/mindustry/ai/types/BuilderAI.java @@ -79,7 +79,7 @@ public class BuilderAI extends AIController{ float dist = Math.min(cons.dst(unit) - buildingRange, 0); //make sure you can reach the request in time - if(dist / unit.type().speed < cons.buildCost * 0.9f){ + if(dist / unit.type.speed < cons.buildCost * 0.9f){ following = b; found = true; } @@ -112,7 +112,7 @@ public class BuilderAI extends AIController{ @Override public AIController fallback(){ - return unit.type().flying ? new FlyingAI() : new GroundAI(); + return unit.type.flying ? new FlyingAI() : new GroundAI(); } @Override diff --git a/core/src/mindustry/ai/types/FlyingAI.java b/core/src/mindustry/ai/types/FlyingAI.java index 0758f9016a..10aea42e2f 100644 --- a/core/src/mindustry/ai/types/FlyingAI.java +++ b/core/src/mindustry/ai/types/FlyingAI.java @@ -12,7 +12,7 @@ public class FlyingAI extends AIController{ @Override public void updateMovement(){ if(target != null && unit.hasWeapons() && command() == UnitCommand.attack){ - if(unit.type().weapons.first().rotate){ + if(unit.type.weapons.first().rotate){ moveTo(target, unit.range() * 0.8f); unit.lookAt(target); }else{ @@ -34,17 +34,15 @@ public class FlyingAI extends AIController{ Teamc result = target(x, y, range, air, ground); if(result != null) return result; - if(ground) result = targetFlag(x, y, BlockFlag.producer, true); + if(ground) result = targetFlag(x, y, BlockFlag.generator, true); if(result != null) return result; - if(ground) result = targetFlag(x, y, BlockFlag.turret, true); + if(ground) result = targetFlag(x, y, BlockFlag.core, true); if(result != null) return result; return null; } - //TODO clean up - protected void attack(float circleLength){ vec.set(target).sub(unit); @@ -57,7 +55,7 @@ public class FlyingAI extends AIController{ vec.setAngle(Mathf.slerpDelta(unit.vel().angle(), vec.angle(), 0.6f)); } - vec.setLength(unit.type().speed); + vec.setLength(unit.type.speed); unit.moveAt(vec); } diff --git a/core/src/mindustry/ai/types/FormationAI.java b/core/src/mindustry/ai/types/FormationAI.java index cbe12a565e..3226fbc300 100644 --- a/core/src/mindustry/ai/types/FormationAI.java +++ b/core/src/mindustry/ai/types/FormationAI.java @@ -7,6 +7,7 @@ import mindustry.ai.formations.*; import mindustry.entities.units.*; import mindustry.gen.*; import mindustry.type.*; +import mindustry.world.blocks.storage.CoreBlock.*; public class FormationAI extends AIController implements FormationMember{ public Unit leader; @@ -26,14 +27,14 @@ public class FormationAI extends AIController implements FormationMember{ @Override public void updateUnit(){ - UnitType type = unit.type(); + UnitType type = unit.type; if(leader.dead){ unit.resetController(); return; } - if(unit.type().canBoost && unit.canPassOn()){ + if(unit.type.canBoost && unit.canPassOn()){ unit.elevation = Mathf.approachDelta(unit.elevation, 0f, 0.08f); } @@ -42,7 +43,7 @@ public class FormationAI extends AIController implements FormationMember{ unit.aim(leader.aimX(), leader.aimY()); - if(unit.type().rotateShooting){ + if(unit.type.rotateShooting){ unit.lookAt(leader.aimX(), leader.aimY()); }else if(unit.moving()){ unit.lookAt(unit.vel.angle()); @@ -57,6 +58,30 @@ public class FormationAI extends AIController implements FormationMember{ }else{ unit.moveAt(realtarget.sub(unit).limit(type.speed)); } + + if(unit instanceof Minerc mine && leader instanceof Minerc com){ + if(mine.validMine(com.mineTile())){ + mine.mineTile(com.mineTile()); + + CoreBuild core = unit.team.core(); + + if(core != null && com.mineTile().drop() != null && unit.within(core, unit.type.range) && !unit.acceptsItem(com.mineTile().drop())){ + if(core.acceptStack(unit.stack.item, unit.stack.amount, unit) > 0){ + Call.transferItemTo(unit.stack.item, unit.stack.amount, unit.x, unit.y, core); + + unit.clearItem(); + } + } + }else{ + mine.mineTile(null); + } + + } + + if(unit instanceof Builderc build && leader instanceof Builderc com && com.activelyBuilding()){ + build.clearBuilding(); + build.addBuild(com.buildPlan()); + } } @Override @@ -69,7 +94,7 @@ public class FormationAI extends AIController implements FormationMember{ @Override public float formationSize(){ - return unit.hitSize * 1f; + return unit.hitSize * 1.1f; } @Override diff --git a/core/src/mindustry/ai/types/GroundAI.java b/core/src/mindustry/ai/types/GroundAI.java index 157ed2eb9d..2a711235c9 100644 --- a/core/src/mindustry/ai/types/GroundAI.java +++ b/core/src/mindustry/ai/types/GroundAI.java @@ -13,8 +13,6 @@ import java.util.*; import static mindustry.Vars.*; public class GroundAI extends AIController{ - //static final float commandCooldown = 60f * 10; - //float commandTimer = 60*3; @Override public void updateMovement(){ @@ -45,31 +43,17 @@ public class GroundAI extends AIController{ } } - if(unit.type().canBoost && !unit.onSolid()){ + if(unit.type.canBoost && !unit.onSolid()){ unit.elevation = Mathf.approachDelta(unit.elevation, 0f, 0.08f); } - if(!Units.invalidateTarget(target, unit, unit.range()) && unit.type().rotateShooting){ - if(unit.type().hasWeapons()){ - unit.lookAt(Predict.intercept(unit, target, unit.type().weapons.first().bullet.speed)); + if(!Units.invalidateTarget(target, unit, unit.range()) && unit.type.rotateShooting){ + if(unit.type.hasWeapons()){ + unit.lookAt(Predict.intercept(unit, target, unit.type.weapons.first().bullet.speed)); } }else if(unit.moving()){ unit.lookAt(unit.vel().angle()); } - //auto-command works but it's very buggy - /* - if(unit instanceof Commanderc){ - Commanderc c = (Commanderc)unit; - //try to command when missing members - if(c.controlling().size <= unit.type().commandLimit/2){ - commandTimer -= Time.delta; - - if(commandTimer <= 0){ - c.commandNearby(new SquareFormation(), u -> !(u.controller() instanceof FormationAI) && !(u instanceof Commanderc)); - commandTimer = commandCooldown; - } - } - }*/ } } diff --git a/core/src/mindustry/ai/types/LogicAI.java b/core/src/mindustry/ai/types/LogicAI.java index 76b81794a2..1166c2ff96 100644 --- a/core/src/mindustry/ai/types/LogicAI.java +++ b/core/src/mindustry/ai/types/LogicAI.java @@ -98,7 +98,7 @@ public class LogicAI extends AIController{ } } - if(unit.type().canBoost && !unit.type().flying){ + if(unit.type.canBoost && !unit.type.flying){ unit.elevation = Mathf.approachDelta(unit.elevation, Mathf.num(boost || unit.onSolid()), 0.08f); } @@ -129,7 +129,7 @@ public class LogicAI extends AIController{ @Override protected boolean shouldShoot(){ - return shoot && !(unit.type().canBoost && boost); + return shoot && !(unit.type.canBoost && boost); } //always aim for the main target diff --git a/core/src/mindustry/ai/types/MinerAI.java b/core/src/mindustry/ai/types/MinerAI.java index a03481768f..aa7d1403c5 100644 --- a/core/src/mindustry/ai/types/MinerAI.java +++ b/core/src/mindustry/ai/types/MinerAI.java @@ -19,12 +19,14 @@ public class MinerAI extends AIController{ if(!(unit instanceof Minerc miner) || core == null) return; - if(miner.mineTile() != null && !miner.mineTile().within(unit, unit.type().range)){ + if(miner.mineTile() != null && !miner.mineTile().within(unit, unit.type.range)){ miner.mineTile(null); } if(mining){ - targetItem = unit.team.data().mineItems.min(i -> indexer.hasOre(i) && miner.canMine(i), i -> core.items.get(i)); + if(timer.get(timerTarget2, 60 * 4) || targetItem == null){ + targetItem = unit.team.data().mineItems.min(i -> indexer.hasOre(i) && miner.canMine(i), i -> core.items.get(i)); + } //core full of the target item, do nothing if(targetItem != null && core.acceptStack(targetItem, 1, unit) == 0){ @@ -34,7 +36,7 @@ public class MinerAI extends AIController{ } //if inventory is full, drop it off. - if(unit.stack.amount >= unit.type().itemCapacity || (targetItem != null && !unit.acceptsItem(targetItem))){ + if(unit.stack.amount >= unit.type.itemCapacity || (targetItem != null && !unit.acceptsItem(targetItem))){ mining = false; }else{ if(retarget() && targetItem != null){ @@ -42,9 +44,9 @@ public class MinerAI extends AIController{ } if(ore != null){ - moveTo(ore, unit.type().range / 2f); + moveTo(ore, unit.type.range / 2f); - if(unit.within(ore, unit.type().range)){ + if(unit.within(ore, unit.type.range)){ miner.mineTile(ore); } @@ -61,7 +63,7 @@ public class MinerAI extends AIController{ return; } - if(unit.within(core, unit.type().range)){ + if(unit.within(core, unit.type.range)){ if(core.acceptStack(unit.stack.item, unit.stack.amount, unit) > 0){ Call.transferItemTo(unit.stack.item, unit.stack.amount, unit.x, unit.y, core); } @@ -70,7 +72,7 @@ public class MinerAI extends AIController{ mining = true; } - circle(core, unit.type().range / 1.8f); + circle(core, unit.type.range / 1.8f); } } diff --git a/core/src/mindustry/ai/types/RepairAI.java b/core/src/mindustry/ai/types/RepairAI.java index 5f021ae828..36504f908d 100644 --- a/core/src/mindustry/ai/types/RepairAI.java +++ b/core/src/mindustry/ai/types/RepairAI.java @@ -12,7 +12,7 @@ public class RepairAI extends AIController{ if(target instanceof Building){ boolean shoot = false; - if(target.within(unit, unit.type().range)){ + if(target.within(unit, unit.type.range)){ unit.aim(target); shoot = true; } @@ -23,8 +23,8 @@ public class RepairAI extends AIController{ } if(target != null){ - if(!target.within(unit, unit.type().range * 0.65f)){ - moveTo(target, unit.type().range * 0.65f); + if(!target.within(unit, unit.type.range * 0.65f) && target instanceof Building){ + moveTo(target, unit.type.range * 0.65f); } unit.lookAt(target); @@ -33,12 +33,14 @@ public class RepairAI extends AIController{ @Override protected void updateTargeting(){ - target = Units.findDamagedTile(unit.team, unit.x, unit.y); + Building target = Units.findDamagedTile(unit.team, unit.x, unit.y); if(target instanceof ConstructBuild) target = null; if(target == null){ super.updateTargeting(); + }else{ + this.target = target; } } diff --git a/core/src/mindustry/ai/types/SuicideAI.java b/core/src/mindustry/ai/types/SuicideAI.java index 2eae6e409f..0ef3ece228 100644 --- a/core/src/mindustry/ai/types/SuicideAI.java +++ b/core/src/mindustry/ai/types/SuicideAI.java @@ -21,7 +21,7 @@ public class SuicideAI extends GroundAI{ } if(retarget()){ - target = target(unit.x, unit.y, unit.range(), unit.type().targetAir, unit.type().targetGround); + target = target(unit.x, unit.y, unit.range(), unit.type.targetAir, unit.type.targetGround); } Building core = unit.closestEnemyCore(); @@ -30,11 +30,11 @@ public class SuicideAI extends GroundAI{ if(!Units.invalidateTarget(target, unit, unit.range()) && unit.hasWeapons()){ rotate = true; - shoot = unit.within(target, unit.type().weapons.first().bullet.range() + + shoot = unit.within(target, unit.type.weapons.first().bullet.range() + (target instanceof Building ? ((Building)target).block.size * Vars.tilesize / 2f : ((Hitboxc)target).hitSize() / 2f)); - if(unit.type().hasWeapons()){ - unit.aimLook(Predict.intercept(unit, target, unit.type().weapons.first().bullet.speed)); + if(unit.type.hasWeapons()){ + unit.aimLook(Predict.intercept(unit, target, unit.type.weapons.first().bullet.speed)); } //do not move toward walls or transport blocks @@ -65,7 +65,7 @@ public class SuicideAI extends GroundAI{ if(!blocked){ moveToTarget = true; //move towards target directly - unit.moveAt(vec.set(target).sub(unit).limit(unit.type().speed)); + unit.moveAt(vec.set(target).sub(unit).limit(unit.type.speed)); } } diff --git a/core/src/mindustry/async/PhysicsProcess.java b/core/src/mindustry/async/PhysicsProcess.java index 07d44d4717..a60dab7f68 100644 --- a/core/src/mindustry/async/PhysicsProcess.java +++ b/core/src/mindustry/async/PhysicsProcess.java @@ -57,7 +57,7 @@ public class PhysicsProcess implements AsyncProcess{ PhysicRef ref = entity.physref(); ref.body.layer = - entity.type().allowLegStep ? layerLegs : + entity.type.allowLegStep ? layerLegs : entity.isGrounded() ? layerGround : layerFlying; ref.x = entity.x(); ref.y = entity.y(); diff --git a/core/src/mindustry/content/Blocks.java b/core/src/mindustry/content/Blocks.java index 05ed572f7e..8c2d6e2c8f 100644 --- a/core/src/mindustry/content/Blocks.java +++ b/core/src/mindustry/content/Blocks.java @@ -14,6 +14,8 @@ import mindustry.world.blocks.*; import mindustry.world.blocks.campaign.*; import mindustry.world.blocks.defense.*; import mindustry.world.blocks.defense.turrets.*; +import mindustry.world.blocks.defense.turrets.PointDefenseTurret; +import mindustry.world.blocks.defense.turrets.TractorBeamTurret; import mindustry.world.blocks.distribution.*; import mindustry.world.blocks.environment.*; import mindustry.world.blocks.experimental.*; @@ -792,14 +794,16 @@ public class Blocks implements ContentList{ phaseWall = new Wall("phase-wall"){{ requirements(Category.defense, with(Items.phasefabric, 6)); health = 150 * wallHealthMultiplier; - flashHit = deflect = true; + chanceDeflect = 10f; + flashHit = true; }}; phaseWallLarge = new Wall("phase-wall-large"){{ requirements(Category.defense, ItemStack.mult(phaseWall.requirements, 4)); health = 150 * 4 * wallHealthMultiplier; size = 2; - flashHit = deflect = true; + chanceDeflect = 10f; + flashHit = true; }}; surgeWall = new Wall("surge-wall"){{ @@ -919,8 +923,8 @@ public class Blocks implements ContentList{ shockMine = new ShockMine("shock-mine"){{ requirements(Category.effect, with(Items.lead, 25, Items.silicon, 12)); hasShadow = false; - health = 40; - damage = 23; + health = 50; + damage = 25; tileDamage = 7f; length = 10; tendrils = 4; @@ -1184,7 +1188,7 @@ public class Blocks implements ContentList{ requirements(Category.power, with(Items.lead, 100, Items.silicon, 75, Items.phasefabric, 25, Items.plastanium, 75, Items.thorium, 50)); size = 2; powerProduction = 4.5f; - itemDuration = 60 * 15f; + itemDuration = 60 * 18f; }}; solarPanel = new SolarGenerator("solar-panel"){{ @@ -1353,7 +1357,7 @@ public class Blocks implements ContentList{ size = 5; unitCapModifier = 20; - researchCostMultiplier = 0.06f; + researchCostMultiplier = 0.05f; }}; vault = new StorageBlock("vault"){{ @@ -1642,11 +1646,20 @@ public class Blocks implements ContentList{ float brange = range + 10f; - ammo(Items.thorium, new ShrapnelBulletType(){{ + ammo( + Items.thorium, new ShrapnelBulletType(){{ length = brange; damage = 105f; - ammoMultiplier = 6f; - }}); + ammoMultiplier = 5f; + }}, + Items.titanium, new ShrapnelBulletType(){{ + length = brange; + damage = 66f; + ammoMultiplier = 4f; + width = 17f; + reloadMultiplier = 1.3f; + }} + ); }}; ripple = new ItemTurret("ripple"){{ @@ -1713,7 +1726,7 @@ public class Blocks implements ContentList{ despawnEffect = Fx.instBomb; trailSpacing = 20f; damage = 1350; - tileDamageMultiplier = 0.5f; + tileDamageMultiplier = 0.3f; speed = brange; hitShake = 6f; ammoMultiplier = 1f; @@ -1912,7 +1925,7 @@ public class Blocks implements ContentList{ new UnitType[]{UnitTypes.antumbra, UnitTypes.eclipse}, new UnitType[]{UnitTypes.arkyid, UnitTypes.toxopid}, new UnitType[]{UnitTypes.scepter, UnitTypes.reign}, - new UnitType[] {UnitTypes.sei, UnitTypes.omura}, + new UnitType[]{UnitTypes.sei, UnitTypes.omura}, new UnitType[]{UnitTypes.quad, UnitTypes.oct}, new UnitType[]{UnitTypes.vela, UnitTypes.corvus} ); diff --git a/core/src/mindustry/content/Bullets.java b/core/src/mindustry/content/Bullets.java index 221dc44e8d..e2d42c0ef0 100644 --- a/core/src/mindustry/content/Bullets.java +++ b/core/src/mindustry/content/Bullets.java @@ -510,7 +510,7 @@ public class Bullets implements ContentList{ speed = 4f; knockback = 1.3f; puddleSize = 8f; - damage = 6f; + damage = 5f; drag = 0.001f; ammoMultiplier = 2f; statusDuration = 60f * 4f; diff --git a/core/src/mindustry/content/Fx.java b/core/src/mindustry/content/Fx.java index 6ca6ccb24d..5d0c45348d 100644 --- a/core/src/mindustry/content/Fx.java +++ b/core/src/mindustry/content/Fx.java @@ -56,7 +56,7 @@ public class Fx{ mixcol(Pal.accent, 1f); alpha(e.fout()); - rect(block ? ((BlockUnitc)select).tile().block.icon(Cicon.full) : select.type().icon(Cicon.full), select.x, select.y, block ? 0f : select.rotation - 90f); + rect(block ? ((BlockUnitc)select).tile().block.icon(Cicon.full) : select.type.icon(Cicon.full), select.x, select.y, block ? 0f : select.rotation - 90f); alpha(1f); Lines.stroke(e.fslope() * 1f); Lines.square(select.x, select.y, e.fout() * select.hitSize * 2f, 45); @@ -66,7 +66,7 @@ public class Fx{ }), unitDespawn = new Effect(100f, e -> { - if(!(e.data instanceof Unit) || e.data().type() == null) return; + if(!(e.data instanceof Unit) || e.data().type == null) return; Unit select = e.data(); float scl = e.fout(Interp.pow2Out); @@ -74,7 +74,7 @@ public class Fx{ Draw.scl *= scl; mixcol(Pal.accent, 1f); - rect(select.type().icon(Cicon.full), select.x, select.y, select.rotation - 90f); + rect(select.type.icon(Cicon.full), select.x, select.y, select.rotation - 90f); reset(); Draw.scl = p; @@ -1293,6 +1293,14 @@ public class Fx{ }); }), + coreBurn = new Effect(23, e -> { + randLenVectors(e.id, 5, e.fin() * 9f, (x, y) -> { + float len = e.fout() * 4f; + color(Pal.accent, Color.gray, e.fin()); + Fill.circle(e.x + x, e.y + y, len/2f); + }); + }), + plasticburn = new Effect(40, e -> { randLenVectors(e.id, 5, 3f + e.fin() * 5f, (x, y) -> { color(Color.valueOf("e9ead3"), Color.gray, e.fin()); diff --git a/core/src/mindustry/content/SectorPresets.java b/core/src/mindustry/content/SectorPresets.java index ff1f36bc22..e516bab740 100644 --- a/core/src/mindustry/content/SectorPresets.java +++ b/core/src/mindustry/content/SectorPresets.java @@ -18,7 +18,7 @@ public class SectorPresets implements ContentList{ groundZero = new SectorPreset("groundZero", serpulo, 15){{ alwaysUnlocked = true; captureWave = 10; - difficulty = 0; + difficulty = 1; }}; saltFlats = new SectorPreset("saltFlats", serpulo, 101){{ @@ -26,23 +26,23 @@ public class SectorPresets implements ContentList{ }}; frozenForest = new SectorPreset("frozenForest", serpulo, 86){{ - captureWave = 40; - difficulty = 1; + captureWave = 20; + difficulty = 2; }}; craters = new SectorPreset("craters", serpulo, 18){{ - captureWave = 40; + captureWave = 20; difficulty = 2; }}; ruinousShores = new SectorPreset("ruinousShores", serpulo, 19){{ - captureWave = 40; + captureWave = 30; difficulty = 3; }}; stainedMountains = new SectorPreset("stainedMountains", serpulo, 20){{ captureWave = 30; - difficulty = 2; + difficulty = 3; }}; fungalPass = new SectorPreset("fungalPass", serpulo, 21){{ @@ -54,7 +54,7 @@ public class SectorPresets implements ContentList{ }}; tarFields = new SectorPreset("tarFields", serpulo, 23){{ - captureWave = 40; + captureWave = 50; difficulty = 5; }}; diff --git a/core/src/mindustry/content/UnitTypes.java b/core/src/mindustry/content/UnitTypes.java index 9a1b851118..f7ba45b156 100644 --- a/core/src/mindustry/content/UnitTypes.java +++ b/core/src/mindustry/content/UnitTypes.java @@ -10,6 +10,7 @@ import mindustry.entities.bullet.*; import mindustry.gen.*; import mindustry.graphics.*; import mindustry.type.*; +import mindustry.world.meta.*; import static mindustry.Vars.*; @@ -872,7 +873,6 @@ public class UnitTypes implements ContentList{ drag = 0.01f; flying = true; health = 75; - faceTarget = false; engineOffset = 5.5f; range = 140f; @@ -898,6 +898,7 @@ public class UnitTypes implements ContentList{ range = 140f; faceTarget = false; armor = 4f; + targetFlag = BlockFlag.factory; weapons.add(new Weapon(){{ minShootVelocity = 0.75f; @@ -978,6 +979,7 @@ public class UnitTypes implements ContentList{ engineOffset = 21; engineSize = 5.3f; hitSize = 56f; + targetFlag = BlockFlag.battery; BulletType missiles = new MissileBulletType(2.7f, 10){{ width = 8f; @@ -1052,6 +1054,7 @@ public class UnitTypes implements ContentList{ hitSize = 58f; destructibleWreck = false; armor = 13f; + targetFlag = BlockFlag.reactor; BulletType fragBullet = new FlakBulletType(4f, 5){{ shootEffect = Fx.shootBig; @@ -1197,7 +1200,6 @@ public class UnitTypes implements ContentList{ mineTier = 3; health = 500; - armor = 2f; armor = 5f; speed = 1.8f; accel = 0.06f; @@ -1247,6 +1249,7 @@ public class UnitTypes implements ContentList{ buildSpeed = 2.5f; range = 140f; targetAir = false; + targetFlag = BlockFlag.battery; ammoType = AmmoTypes.powerHigh; @@ -1449,13 +1452,13 @@ public class UnitTypes implements ContentList{ trailMult = 0.8f; hitEffect = Fx.massiveExplosion; knockback = 1.5f; - lifetime = 140f; + lifetime = 100f; height = 15.5f; width = 15f; collidesTiles = false; ammoMultiplier = 4f; splashDamageRadius = 60f; - splashDamage = 85f; + splashDamage = 80f; backColor = Pal.missileYellowBack; frontColor = Pal.missileYellow; trailEffect = Fx.artilleryTrail; @@ -1651,11 +1654,11 @@ public class UnitTypes implements ContentList{ mineTier = 1; buildSpeed = 0.5f; drag = 0.05f; - speed = 2.8f; + speed = 3f; rotateSpeed = 15f; accel = 0.1f; itemCapacity = 30; - health = 120f; + health = 150f; engineOffset = 6f; hitSize = 8f; commandLimit = 3; @@ -1666,13 +1669,13 @@ public class UnitTypes implements ContentList{ y = 1f; top = false; - bullet = new BasicBulletType(2.5f, 9){{ + bullet = new BasicBulletType(2.5f, 10){{ width = 7f; height = 9f; lifetime = 60f; shootEffect = Fx.shootSmall; smokeEffect = Fx.shootSmallSmoke; - tileDamageMultiplier = 0.09f; + tileDamageMultiplier = 0.03f; }}; }}); }}; @@ -1686,11 +1689,11 @@ public class UnitTypes implements ContentList{ mineTier = 1; buildSpeed = 0.75f; drag = 0.05f; - speed = 3f; + speed = 3.3f; rotateSpeed = 17f; accel = 0.1f; itemCapacity = 50; - health = 150f; + health = 170f; engineOffset = 6f; hitSize = 9f; rotateShooting = false; @@ -1707,13 +1710,13 @@ public class UnitTypes implements ContentList{ shotDelay = 4f; spacing = 0f; - bullet = new BasicBulletType(3f, 9){{ + bullet = new BasicBulletType(3f, 10){{ width = 7f; height = 9f; lifetime = 60f; shootEffect = Fx.shootSmall; smokeEffect = Fx.shootSmallSmoke; - tileDamageMultiplier = 0.1f; + tileDamageMultiplier = 0.03f; }}; }}); }}; @@ -1727,11 +1730,11 @@ public class UnitTypes implements ContentList{ mineTier = 2; buildSpeed = 1f; drag = 0.05f; - speed = 3.5f; + speed = 3.55f; rotateSpeed = 19f; accel = 0.11f; itemCapacity = 70; - health = 190f; + health = 220f; engineOffset = 6f; hitSize = 10f; commandLimit = 7; @@ -1746,13 +1749,13 @@ public class UnitTypes implements ContentList{ inaccuracy = 3f; shotDelay = 3f; - bullet = new BasicBulletType(3.5f, 9){{ + bullet = new BasicBulletType(3.5f, 10){{ width = 6.5f; height = 11f; lifetime = 70f; shootEffect = Fx.shootSmall; smokeEffect = Fx.shootSmallSmoke; - tileDamageMultiplier = 0.1f; + tileDamageMultiplier = 0.03f; homingPower = 0.04f; }}; }}); diff --git a/core/src/mindustry/content/Weathers.java b/core/src/mindustry/content/Weathers.java index 273fdb5899..b120b7ef2b 100644 --- a/core/src/mindustry/content/Weathers.java +++ b/core/src/mindustry/content/Weathers.java @@ -1,339 +1,89 @@ package mindustry.content; -import arc.*; import arc.graphics.*; -import arc.graphics.Texture.*; -import arc.graphics.g2d.*; -import arc.math.*; import arc.util.*; import mindustry.ctype.*; -import mindustry.gen.*; import mindustry.type.*; -import mindustry.world.*; +import mindustry.type.weather.*; import mindustry.world.meta.*; -import static mindustry.Vars.*; - public class Weathers implements ContentList{ public static Weather rain, snow, sandstorm, - sporestorm; + sporestorm, + fog; @Override public void load(){ - snow = new Weather("snow"){ - TextureRegion region; - float yspeed = 2f, xspeed = 0.25f, padding = 16f, size = 12f, density = 1200f; + snow = new ParticleWeather("snow"){{ + sizeMax = 13f; + sizeMin = 2.6f; + density = 1200f; + attrs.set(Attribute.light, -0.15f); + }}; - { - attrs.set(Attribute.light, -0.15f); - } + rain = new RainWeather("rain"){{ + attrs.set(Attribute.light, -0.2f); + attrs.set(Attribute.water, 0.2f); + status = StatusEffects.wet; + }}; - @Override - public void load(){ - super.load(); + sandstorm = new ParticleWeather("sandstorm"){{ + color = noiseColor = Color.valueOf("f7cba4"); + drawNoise = true; + useWindVector = true; + sizeMax = 140f; + sizeMin = 70f; + minAlpha = 0f; + maxAlpha = 0.2f; + density = 1500f; + baseSpeed = 6.1f; + attrs.set(Attribute.light, -0.1f); + attrs.set(Attribute.water, -0.1f); + opacityMultiplier = 0.8f; + force = 0.1f; + }}; - region = Core.atlas.find("circle-shadow"); - } + sporestorm = new ParticleWeather("sporestorm"){{ + color = noiseColor = Color.valueOf("7457ce"); + particleRegion = "circle"; + drawNoise = true; + statusGround = false; + useWindVector = true; + sizeMax = 5f; + sizeMin = 2.5f; + minAlpha = 0.1f; + maxAlpha = 0.8f; + density = 2000f; + baseSpeed = 4.3f; + attrs.set(Attribute.spores, 1f); + attrs.set(Attribute.light, -0.15f); + status = StatusEffects.sporeSlowed; + opacityMultiplier = 0.85f; + force = 0.1f; + }}; - @Override - public void drawOver(WeatherState state){ - rand.setSeed(0); - Tmp.r1.setCentered(Core.camera.position.x, Core.camera.position.y, Core.graphics.getWidth() / renderer.minScale(), Core.graphics.getHeight() / renderer.minScale()); - Tmp.r1.grow(padding); - Core.camera.bounds(Tmp.r2); - int total = (int)(Tmp.r1.area() / density * state.intensity()); - - for(int i = 0; i < total; i++){ - float scl = rand.random(0.5f, 1f); - float scl2 = rand.random(0.5f, 1f); - float sscl = rand.random(0.2f, 1f); - float x = (rand.random(0f, world.unitWidth()) + Time.time() * xspeed * scl2); - float y = (rand.random(0f, world.unitHeight()) - Time.time() * yspeed * scl); - - x += Mathf.sin(y, rand.random(30f, 80f), rand.random(1f, 7f)); - - x -= Tmp.r1.x; - y -= Tmp.r1.y; - x = Mathf.mod(x, Tmp.r1.width); - y = Mathf.mod(y, Tmp.r1.height); - x += Tmp.r1.x; - y += Tmp.r1.y; - - if(Tmp.r3.setCentered(x, y, size * sscl).overlaps(Tmp.r2)){ - Draw.rect(region, x, y, size * sscl, size * sscl); - } - } - } - }; - - rain = new Weather("rain"){ - float yspeed = 5f, xspeed = 1.5f, padding = 16f, size = 40f, density = 1200f; - TextureRegion[] splashes = new TextureRegion[12]; - - { - attrs.set(Attribute.light, -0.2f); - attrs.set(Attribute.water, 0.2f); - status = StatusEffects.wet; - } - - @Override - public void load(){ - super.load(); - - for(int i = 0; i < splashes.length; i++){ - splashes[i] = Core.atlas.find("splash-" + i); - } - } - - @Override - public void drawOver(WeatherState state){ - Tmp.r1.setCentered(Core.camera.position.x, Core.camera.position.y, Core.graphics.getWidth() / renderer.minScale(), Core.graphics.getHeight() / renderer.minScale()); - Tmp.r1.grow(padding); - Core.camera.bounds(Tmp.r2); - int total = (int)(Tmp.r1.area() / density * state.intensity()); - Lines.stroke(0.75f); - float alpha = Draw.getColor().a; - Draw.color(Color.royal, Color.white, 0.3f); - - for(int i = 0; i < total; i++){ - float scl = rand.random(0.5f, 1f); - float scl2 = rand.random(0.5f, 1f); - float sscl = rand.random(0.2f, 1f); - float x = (rand.random(0f, world.unitWidth()) + Time.time() * xspeed * scl2); - float y = (rand.random(0f, world.unitHeight()) - Time.time() * yspeed * scl); - float tint = rand.random(1f) * alpha; - - x -= Tmp.r1.x; - y -= Tmp.r1.y; - x = Mathf.mod(x, Tmp.r1.width); - y = Mathf.mod(y, Tmp.r1.height); - x += Tmp.r1.x; - y += Tmp.r1.y; - - if(Tmp.r3.setCentered(x, y, size * sscl).overlaps(Tmp.r2)){ - Draw.alpha(tint); - Lines.lineAngle(x, y, Angles.angle(xspeed * scl2, - yspeed * scl), size*sscl/2f); - } - } - } - - @Override - public void drawUnder(WeatherState state){ - Tmp.r1.setCentered(Core.camera.position.x, Core.camera.position.y, Core.graphics.getWidth() / renderer.minScale(), Core.graphics.getHeight() / renderer.minScale()); - Tmp.r1.grow(padding); - Core.camera.bounds(Tmp.r2); - int total = (int)(Tmp.r1.area() / density * state.intensity()) / 2; - Lines.stroke(0.75f); - - float t = Time.time() / 22f; - - for(int i = 0; i < total; i++){ - float offset = rand.random(0f, 1f); - float time = t + offset; - - int pos = (int)((time)); - float life = time % 1f; - float x = (rand.random(0f, world.unitWidth()) + pos*953); - float y = (rand.random(0f, world.unitHeight()) - pos*453); - - x -= Tmp.r1.x; - y -= Tmp.r1.y; - x = Mathf.mod(x, Tmp.r1.width); - y = Mathf.mod(y, Tmp.r1.height); - x += Tmp.r1.x; - y += Tmp.r1.y; - - if(Tmp.r3.setCentered(x, y, life * 4f).overlaps(Tmp.r2)){ - Tile tile = world.tileWorld(x, y); - - if(tile != null && tile.floor().liquidDrop == Liquids.water){ - Draw.color(Tmp.c1.set(tile.floor().mapColor).mul(1.5f).a(state.opacity())); - Draw.rect(splashes[(int)(life * (splashes.length - 1))], x, y); - }else if(tile != null && tile.floor().liquidDrop == null && !tile.floor().solid){ - Draw.color(Color.royal, Color.white, 0.3f); - Draw.alpha(Mathf.slope(life) * state.opacity()); - - float space = 45f; - for(int j : new int[]{-1, 1}){ - Tmp.v1.trns(90f + j*space, 1f + 5f * life); - Lines.lineAngle(x + Tmp.v1.x, y + Tmp.v1.y, 90f + j*space, 3f * (1f - life)); - } - } - } - } - } - }; - - sandstorm = new Weather("sandstorm"){ - TextureRegion region; - float size = 140f, padding = size, invDensity = 1500f, baseSpeed = 6.1f; - float force = 0.4f * 0; - Color color = Color.valueOf("f7cba4"); - Texture noise; - - { - attrs.set(Attribute.light, -0.1f); - opacityMultiplier = 0.8f; - } - - @Override - public void load(){ - region = Core.atlas.find("circle-shadow"); - noise = new Texture("sprites/noiseAlpha.png"); - noise.setWrap(TextureWrap.repeat); - noise.setFilter(TextureFilter.linear); - } - - @Override - public void dispose(){ - noise.dispose(); - } - - @Override - public void update(WeatherState state){ - float speed = force * state.intensity; - float windx = state.windVector.x * speed, windy = state.windVector.y * speed; - - for(Unit unit : Groups.unit){ - unit.impulse(windx, windy); - } - } - - @Override - public void drawOver(WeatherState state){ - Draw.tint(color); - float speed = baseSpeed * state.intensity; - float windx = state.windVector.x * speed, windy = state.windVector.y * speed; - - float scale = 1f / 2000f; - float scroll = Time.time() * scale; - Tmp.tr1.texture = noise; - Core.camera.bounds(Tmp.r1); - Tmp.tr1.set(Tmp.r1.x*scale, Tmp.r1.y*scale, (Tmp.r1.x + Tmp.r1.width)*scale, (Tmp.r1.y + Tmp.r1.height)*scale); - Tmp.tr1.scroll(-windx * scroll, windy * scroll); - Draw.rect(Tmp.tr1, Core.camera.position.x, Core.camera.position.y, Core.camera.width, -Core.camera.height); - - rand.setSeed(0); - Tmp.r1.setCentered(Core.camera.position.x, Core.camera.position.y, Core.graphics.getWidth() / renderer.minScale(), Core.graphics.getHeight() / renderer.minScale()); - Tmp.r1.grow(padding); - Core.camera.bounds(Tmp.r2); - int total = (int)(Tmp.r1.area() / invDensity * state.intensity()); - Draw.tint(color); - float baseAlpha = Draw.getColor().a; - - for(int i = 0; i < total; i++){ - float scl = rand.random(0.5f, 1f); - float scl2 = rand.random(0.5f, 1f); - float sscl = rand.random(0.5f, 1f); - float x = (rand.random(0f, world.unitWidth()) + Time.time() * windx * scl2); - float y = (rand.random(0f, world.unitHeight()) + Time.time() * windy * scl); - float alpha = rand.random(0.2f); - - x += Mathf.sin(y, rand.random(30f, 80f), rand.random(1f, 7f)); - - x -= Tmp.r1.x; - y -= Tmp.r1.y; - x = Mathf.mod(x, Tmp.r1.width); - y = Mathf.mod(y, Tmp.r1.height); - x += Tmp.r1.x; - y += Tmp.r1.y; - - if(Tmp.r3.setCentered(x, y, size * sscl).overlaps(Tmp.r2)){ - Draw.alpha(alpha * baseAlpha); - Draw.rect(region, x, y, size * sscl, size * sscl); - } - } - } - }; - - sporestorm = new Weather("sporestorm"){ - TextureRegion region; - float size = 5f, padding = size, invDensity = 2000f, baseSpeed = 4.3f, force = 0.28f * 0; - Color color = Color.valueOf("7457ce"); - Texture noise; - - { - attrs.set(Attribute.spores, 1f); - attrs.set(Attribute.light, -0.15f); - status = StatusEffects.sporeSlowed; - statusGround = false; - opacityMultiplier = 0.85f; - } - - @Override - public void load(){ - region = Core.atlas.find("circle-shadow"); - noise = new Texture("sprites/noiseAlpha.png"); - noise.setWrap(TextureWrap.repeat); - noise.setFilter(TextureFilter.linear); - } - - @Override - public void update(WeatherState state){ - float speed = force * state.intensity; - float windx = state.windVector.x * speed, windy = state.windVector.y * speed; - - for(Unit unit : Groups.unit){ - unit.impulse(windx, windy); - } - } - - @Override - public void dispose(){ - noise.dispose(); - } - - @Override - public void drawOver(WeatherState state){ - Draw.alpha(state.opacity * 0.8f); - Draw.tint(color); - - float speed = baseSpeed * state.intensity; - float windx = state.windVector.x * speed, windy = state.windVector.y * speed; - - float scale = 1f / 2000f; - float scroll = Time.time() * scale; - Tmp.tr1.texture = noise; - Core.camera.bounds(Tmp.r1); - Tmp.tr1.set(Tmp.r1.x*scale, Tmp.r1.y*scale, (Tmp.r1.x + Tmp.r1.width)*scale, (Tmp.r1.y + Tmp.r1.height)*scale); - Tmp.tr1.scroll(-windx * scroll, windy * scroll); - Draw.rect(Tmp.tr1, Core.camera.position.x, Core.camera.position.y, Core.camera.width, -Core.camera.height); - - rand.setSeed(0); - Tmp.r1.setCentered(Core.camera.position.x, Core.camera.position.y, Core.graphics.getWidth() / renderer.minScale(), Core.graphics.getHeight() / renderer.minScale()); - Tmp.r1.grow(padding); - Core.camera.bounds(Tmp.r2); - int total = (int)(Tmp.r1.area() / invDensity * state.intensity()); - Draw.tint(color); - float baseAlpha = state.opacity; - Draw.alpha(baseAlpha); - - for(int i = 0; i < total; i++){ - float scl = rand.random(0.5f, 1f); - float scl2 = rand.random(0.5f, 1f); - float sscl = rand.random(0.5f, 1f); - float x = (rand.random(0f, world.unitWidth()) + Time.time() * windx * scl2); - float y = (rand.random(0f, world.unitHeight()) + Time.time() * windy * scl); - float alpha = rand.random(0.1f, 0.8f); - - x += Mathf.sin(y, rand.random(30f, 80f), rand.random(1f, 7f)); - - x -= Tmp.r1.x; - y -= Tmp.r1.y; - x = Mathf.mod(x, Tmp.r1.width); - y = Mathf.mod(y, Tmp.r1.height); - x += Tmp.r1.x; - y += Tmp.r1.y; - - if(Tmp.r3.setCentered(x, y, size * sscl).overlaps(Tmp.r2)){ - Draw.alpha(alpha * baseAlpha); - Fill.circle(x, y, size * sscl / 2f); - } - } - } - }; + fog = new ParticleWeather("fog"){{ + duration = 15f * Time.toMinutes; + noiseLayers = 3; + noiseLayerSclM = 0.8f; + noiseLayerAlphaM = 0.7f; + noiseLayerSpeedM = 2f; + noiseLayerSclM = 0.6f; + baseSpeed = 0.05f; + color = noiseColor = Color.grays(0.4f); + noiseScale = 1100f; + noisePath = "fog"; + drawParticles = false; + drawNoise = true; + useWindVector = false; + xspeed = 1f; + yspeed = 0.01f; + attrs.set(Attribute.light, -0.3f); + attrs.set(Attribute.water, 0.05f); + opacityMultiplier = 0.47f; + }}; } } diff --git a/core/src/mindustry/core/Control.java b/core/src/mindustry/core/Control.java index efe2327074..e24881692f 100644 --- a/core/src/mindustry/core/Control.java +++ b/core/src/mindustry/core/Control.java @@ -9,7 +9,6 @@ import arc.math.*; import arc.scene.ui.*; import arc.struct.*; import arc.util.*; -import mindustry.*; import mindustry.audio.*; import mindustry.content.*; import mindustry.core.GameState.*; @@ -25,7 +24,6 @@ import mindustry.maps.Map; import mindustry.type.*; import mindustry.ui.dialogs.*; import mindustry.world.*; -import mindustry.world.blocks.storage.CoreBlock.*; import java.io.*; import java.text.*; @@ -160,9 +158,7 @@ public class Control implements ApplicationListener, Loadable{ //delete the save, it is gone. if(saves.getCurrent() != null && !state.rules.tutorial){ - Sector sector = state.getSector(); - sector.save = null; - saves.getCurrent().delete(); + saves.getCurrent().save(); } } }); @@ -252,19 +248,6 @@ public class Control implements ApplicationListener, Loadable{ }); } - //TODO move - public void handleLaunch(CoreBuild tile){ - LaunchCorec ent = LaunchCore.create(); - ent.set(tile); - ent.block(Blocks.coreShard); - ent.lifetime(Vars.launchDuration); - ent.add(); - - //remove schematic requirements from core - tile.items.remove(universe.getLastLoadout().requirements()); - tile.items.remove(universe.getLaunchResources()); - } - public void playSector(Sector sector){ playSector(sector, sector); } @@ -281,21 +264,29 @@ public class Control implements ApplicationListener, Loadable{ slot.load(); slot.setAutosave(true); state.rules.sector = sector; + state.secinfo = state.rules.sector.info; //if there is no base, simulate a new game and place the right loadout at the spawn position - //TODO this is broken? if(state.rules.defaultTeam.cores().isEmpty()){ - //kill all friendly units, since they should be dead anwyay - for(Unit unit : Groups.unit){ - if(unit.team() == state.rules.defaultTeam){ - unit.remove(); - } + //no spawn set -> delete the sector save + if(sector.info.spawnPosition == 0){ + //delete old save + sector.save = null; + slot.delete(); + //play again + playSector(origin, sector); + return; } - Tile spawn = world.tile(sector.getSpawnPosition()); - //TODO PLACE CORRECT LOADOUT - Schematics.placeLoadout(universe.getLastLoadout(), spawn.x, spawn.y); + //reset wave so things are more fair + state.wave = 1; + + //kill all units, since they should be dead anwyay + Groups.unit.clear(); + + Tile spawn = world.tile(sector.info.spawnPosition); + Schematics.placeLaunchLoadout(spawn.x, spawn.y); //set up camera/player locations player.set(spawn.x * tilesize, spawn.y * tilesize); @@ -317,7 +308,6 @@ public class Control implements ApplicationListener, Loadable{ }else{ net.reset(); logic.reset(); - sector.setSecondsPassed(0); world.loadSector(sector); state.rules.sector = sector; //assign origin when launching diff --git a/core/src/mindustry/core/GameState.java b/core/src/mindustry/core/GameState.java index 949e40d02d..41f14074c8 100644 --- a/core/src/mindustry/core/GameState.java +++ b/core/src/mindustry/core/GameState.java @@ -23,7 +23,7 @@ public class GameState{ /** The current game rules. */ public Rules rules = new Rules(); /** Statistics for this save/game. Displayed after game over. */ - public Stats stats = new Stats(); + public GameStats stats = new GameStats(); /** Global attributes of the environment, calculated by weather. */ public Attributes envAttrs = new Attributes(); /** Sector information. Only valid in the campaign. */ @@ -41,10 +41,17 @@ public class GameState{ } public void set(State astate){ + //cannot pause when in multiplayer + if(astate == State.paused && net.active()) return; + Events.fire(new StateChangeEvent(state, astate)); state = astate; } + public boolean hasSpawns(){ + return rules.waves && !(isCampaign() && rules.attackMode); + } + /** Note that being in a campaign does not necessarily mean having a sector. */ public boolean isCampaign(){ return rules.sector != null; @@ -68,7 +75,7 @@ public class GameState{ } public boolean isPlaying(){ - return state == State.playing; + return (state == State.playing) || (state == State.paused && !isPaused()); } /** @return whether the current state is *not* the menu. */ diff --git a/core/src/mindustry/core/Logic.java b/core/src/mindustry/core/Logic.java index df536552df..a96ea101a9 100644 --- a/core/src/mindustry/core/Logic.java +++ b/core/src/mindustry/core/Logic.java @@ -5,16 +5,15 @@ import arc.math.*; import arc.util.*; import mindustry.annotations.Annotations.*; import mindustry.core.GameState.*; +import mindustry.ctype.*; import mindustry.game.EventType.*; import mindustry.game.*; import mindustry.game.Teams.*; import mindustry.gen.*; +import mindustry.maps.*; import mindustry.type.*; import mindustry.type.Weather.*; import mindustry.world.*; -import mindustry.world.blocks.*; -import mindustry.world.blocks.ConstructBlock.*; -import mindustry.world.blocks.storage.CoreBlock.*; import java.util.*; @@ -39,32 +38,7 @@ public class Logic implements ApplicationListener{ //skip null entities or un-rebuildables, for obvious reasons; also skip client since they can't modify these requests if(tile.build == null || !tile.block().rebuildable || net.client()) return; - if(block instanceof ConstructBlock){ - - ConstructBuild entity = tile.bc(); - - //update block to reflect the fact that something was being constructed - if(entity.cblock != null && entity.cblock.synthetic()){ - block = entity.cblock; - }else{ - //otherwise this was a deconstruction that was interrupted, don't want to rebuild that - return; - } - } - - TeamData data = state.teams.get(tile.team()); - - //remove existing blocks that have been placed here. - //painful O(n) iteration + copy - for(int i = 0; i < data.blocks.size; i++){ - BlockPlan b = data.blocks.get(i); - if(b.x == tile.x && b.y == tile.y){ - data.blocks.removeIndex(i); - break; - } - } - - data.blocks.addFirst(new BlockPlan(tile.x, tile.y, (short)tile.build.rotation, block.id, tile.build.config())); + tile.build.addPlan(true); }); Events.on(BlockBuildEndEvent.class, event -> { @@ -84,51 +58,61 @@ public class Logic implements ApplicationListener{ Events.on(LaunchItemEvent.class, e -> state.secinfo.handleItemExport(e.stack)); //when loading a 'damaged' sector, propagate the damage - Events.on(WorldLoadEvent.class, e -> { + Events.on(SaveLoadEvent.class, e -> { if(state.isCampaign()){ - long seconds = state.rules.sector.getSecondsPassed(); - CoreBuild core = state.rules.defaultTeam.core(); - //THE WAVES NEVER END - state.rules.waves = true; + state.secinfo.write(); - //apply fractional damage based on how many turns have passed for this sector - //float turnsPassed = seconds / (turnDuration / 60f); + //how much wave time has passed + int wavesPassed = state.secinfo.wavesPassed; - //TODO sector damage disabled for now - //if(state.rules.sector.hasWaves() && turnsPassed > 0 && state.rules.sector.hasBase()){ - // SectorDamage.apply(turnsPassed / sectorDestructionTurns); - //} - - //add resources based on turns passed - if(state.rules.sector.save != null && core != null){ - //update correct storage capacity - state.rules.sector.save.meta.secinfo.storageCapacity = core.storageCapacity; - - //add new items received - state.rules.sector.calculateReceivedItems().each((item, amount) -> core.items.add(item, amount)); - - //clear received items - state.rules.sector.setExtraItems(new ItemSeq()); - - //validation - for(Item item : content.items()){ - //ensure positive items - if(core.items.get(item) < 0) core.items.set(item, 0); - //cap the items - if(core.items.get(item) > core.storageCapacity) core.items.set(item, core.storageCapacity); - } + //wave has passed, remove all enemies, they are assumed to be dead + if(wavesPassed > 0){ + Groups.unit.each(u -> { + if(u.team == state.rules.waveTeam){ + u.remove(); + } + }); } - state.rules.sector.setSecondsPassed(0); - } + //simulate passing of waves + if(wavesPassed > 0){ + //simulate wave counter moving forward + state.wave += wavesPassed; + state.wavetime = state.rules.waveSpacing; + SectorDamage.applyCalculatedDamage(); + } + + //reset values + state.secinfo.damage = 0f; + state.secinfo.wavesPassed = 0; + state.secinfo.hasCore = true; + state.secinfo.secondsPassed = 0; + + state.rules.sector.saveInfo(); + } + }); + + Events.on(WorldLoadEvent.class, e -> { //enable infinite ammo for wave team by default state.rules.waveTeam.rules().infiniteAmmo = true; + if(state.isCampaign()){ + //enable building AI + state.rules.waveTeam.rules().ai = true; + state.rules.waveTeam.rules().infiniteResources = true; + } //save settings Core.settings.manualSave(); }); + //sync research + Events.on(ResearchEvent.class, e -> { + if(net.server()){ + Call.researched(e.content); + } + }); + } /** Adds starting items, resets wave time, and sets state to playing. */ @@ -168,11 +152,6 @@ public class Logic implements ApplicationListener{ } public void skipWave(){ - if(state.isCampaign()){ - //warp time spent forward because the wave was just skipped. - state.secinfo.internalTimeSpent += state.wavetime; - } - state.wavetime = 0; } @@ -199,12 +178,13 @@ public class Logic implements ApplicationListener{ state.rules.waves = false; } - //TODO capturing is disabled - /* //if there's a "win" wave and no enemies are present, win automatically - if(state.rules.waves && state.enemies == 0 && state.rules.winWave > 0 && state.wave >= state.rules.winWave && !spawner.isSpawning()){ + if(state.rules.waves && (state.enemies == 0 && state.rules.winWave > 0 && state.wave >= state.rules.winWave && !spawner.isSpawning()) || + (state.rules.attackMode && state.rules.waveTeam.cores().isEmpty())){ //the sector has been conquered - waves get disabled state.rules.waves = false; + //disable attack mode + state.rules.attackMode = false; //fire capture event Events.fire(new SectorCaptureEvent(state.rules.sector)); @@ -213,7 +193,7 @@ public class Logic implements ApplicationListener{ if(!headless){ control.saves.saveSector(state.rules.sector); } - }*/ + } }else{ if(!state.rules.attackMode && state.teams.playerCores().size == 0 && !state.gameOver){ state.gameOver = true; @@ -266,6 +246,15 @@ public class Logic implements ApplicationListener{ netClient.setQuiet(); } + //called when the remote server researches something + @Remote + public static void researched(Content content){ + if(!(content instanceof UnlockableContent u)) return; + + state.rules.researched.add(u.name); + ui.hudfrag.showUnlock(u); + } + @Override public void dispose(){ //save the settings before quitting @@ -283,7 +272,7 @@ public class Logic implements ApplicationListener{ if(state.isGame()){ if(!net.client()){ - state.enemies = Groups.unit.count(u -> u.team() == state.rules.waveTeam && u.type().isCounted); + state.enemies = Groups.unit.count(u -> u.team() == state.rules.waveTeam && u.type.isCounted); } if(!state.isPaused()){ diff --git a/core/src/mindustry/core/NetClient.java b/core/src/mindustry/core/NetClient.java index ae8c7d9a7c..3c6214fd85 100644 --- a/core/src/mindustry/core/NetClient.java +++ b/core/src/mindustry/core/NetClient.java @@ -194,14 +194,14 @@ public class NetClient implements ApplicationListener{ } //server console logging - Log.info("&y@: &lb@", player.name, message); + Log.info("&fi@: @", "&lc" + player.name, "&lw" + message); //invoke event for all clients but also locally //this is required so other clients get the correct name even if they don't know who's sending it yet Call.sendMessage(message, colorizeName(player.id(), player.name), player); }else{ //log command to console but with brackets - Log.info("<&y@: &lm@&lg>", player.name, message); + Log.info("<&fi@: @&fr>", "&lk" + player.name, "&lw" + message); //a command was sent, now get the output if(response.type != ResponseType.valid){ diff --git a/core/src/mindustry/core/NetServer.java b/core/src/mindustry/core/NetServer.java index 77ab732cf6..00f0bc78e8 100644 --- a/core/src/mindustry/core/NetServer.java +++ b/core/src/mindustry/core/NetServer.java @@ -18,6 +18,7 @@ import mindustry.game.EventType.*; import mindustry.game.*; import mindustry.game.Teams.*; import mindustry.gen.*; +import mindustry.graphics.*; import mindustry.net.*; import mindustry.net.Administration.*; import mindustry.net.Packets.*; @@ -299,6 +300,15 @@ public class NetServer implements ApplicationListener{ } }); + clientCommands.register("a", "", "Send a message only to admins.", (args, player) -> { + if(!player.admin){ + player.sendMessage("[scarlet]You must be admin to use this command."); + return; + } + + Groups.player.each(Player::admin, a -> a.sendMessage(args[0], player, "[#" + Pal.adminChat.toString() + "]" + NetClient.colorizeName(player.id, player.name))); + }); + //duration of a a kick in seconds int kickDuration = 60 * 60; //voting round duration in seconds @@ -508,7 +518,8 @@ public class NetServer implements ApplicationListener{ Call.playerDisconnect(player.id()); } - if(Config.showConnectMessages.bool()) Log.info("&lm[@] &lc@ has disconnected. &lg&fi(@)", player.uuid(), player.name, reason); + String message = Strings.format("&lb@&fi&lk has disconnected. &fi&lk[&lb@&fi&lk] (@)", player.name, player.uuid(), reason); + if(Config.showConnectMessages.bool()) Log.info(message); } player.remove(); @@ -575,7 +586,7 @@ public class NetServer implements ApplicationListener{ shooting = false; } - if(!player.dead() && (player.unit().type().flying || !player.unit().type().canBoost)){ + if(!player.dead() && (player.unit().type.flying || !player.unit().type.canBoost)){ boosting = false; } @@ -629,7 +640,7 @@ public class NetServer implements ApplicationListener{ Unit unit = player.unit(); long elapsed = Time.timeSinceMillis(con.lastReceivedClientTime); - float maxSpeed = ((player.unit().type().canBoost && player.unit().isFlying()) ? player.unit().type().boostMultiplier : 1f) * player.unit().type().speed; + float maxSpeed = ((player.unit().type.canBoost && player.unit().isFlying()) ? player.unit().type.boostMultiplier : 1f) * player.unit().type.speed; if(unit.isGrounded()){ maxSpeed *= unit.floorSpeedMultiplier(); } @@ -736,7 +747,8 @@ public class NetServer implements ApplicationListener{ if(Config.showConnectMessages.bool()){ Call.sendMessage("[accent]" + player.name + "[accent] has connected."); - Log.info("&lm[@] &y@ has connected.", player.uuid(), player.name); + String message = Strings.format("&lb@&fi&lk has connected. &fi&lk[&lb@&fi&lk]", player.name, player.uuid()); + Log.info(message); } if(!Config.motd.string().equalsIgnoreCase("off")){ @@ -785,7 +797,7 @@ public class NetServer implements ApplicationListener{ public void openServer(){ try{ net.host(Config.port.num()); - info("&lcOpened a server on port @.", Config.port.num()); + info("Opened a server on port @.", Config.port.num()); }catch(BindException e){ Log.err("Unable to host: Port already in use! Make sure no other servers are running on the same port in your network."); state.set(State.menu); diff --git a/core/src/mindustry/core/World.java b/core/src/mindustry/core/World.java index 03a18c01ed..60beec266b 100644 --- a/core/src/mindustry/core/World.java +++ b/core/src/mindustry/core/World.java @@ -148,7 +148,17 @@ public class World{ return build(Math.round(x / tilesize), Math.round(y / tilesize)); } - public int toTile(float coord){ + /** Convert from world to logic tile coordinates. Whole numbers are at centers of tiles. */ + public static float conv(float coord){ + return coord / tilesize; + } + + /** Convert from tile to world coordinates. */ + public static float unconv(float coord){ + return coord * tilesize; + } + + public static int toTile(float coord){ return Math.round(coord / tilesize); } @@ -253,7 +263,7 @@ public class World{ setSectorRules(sector); if(state.rules.defaultTeam.core() != null){ - sector.setSpawnPosition(state.rules.defaultTeam.core().pos()); + sector.info.spawnPosition = state.rules.defaultTeam.core().pos(); } } @@ -267,8 +277,6 @@ public class World{ ObjectIntMap floorc = new ObjectIntMap<>(); ObjectSet content = new ObjectSet<>(); - float waterFloors = 0, totalFloors = 0; - for(Tile tile : world.tiles){ if(world.getDarkness(tile.x, tile.y) >= 3){ continue; @@ -280,10 +288,6 @@ public class World{ if(liquid != null) content.add(liquid); if(!tile.block().isStatic()){ - totalFloors ++; - if(liquid == Liquids.water){ - waterFloors += tile.floor().isDeep() ? 1f : 0.7f; - } floorc.increment(tile.floor()); if(tile.overlay() != Blocks.air){ floorc.increment(tile.overlay()); @@ -306,7 +310,7 @@ public class World{ //TODO bad code boolean hasSnow = floors[0].name.contains("ice") || floors[0].name.contains("snow"); - boolean hasRain = !hasSnow && floors[0].name.contains("water"); + boolean hasRain = !hasSnow && content.contains(Liquids.water) && !floors[0].name.contains("sand"); boolean hasDesert = !hasSnow && !hasRain && floors[0].name.contains("sand"); boolean hasSpores = floors[0].name.contains("spore") || floors[0].name.contains("moss") || floors[0].name.contains("tainted"); @@ -316,6 +320,7 @@ public class World{ if(hasRain){ state.rules.weather.add(new WeatherEntry(Weathers.rain)); + state.rules.weather.add(new WeatherEntry(Weathers.fog)); } if(hasDesert){ @@ -326,9 +331,9 @@ public class World{ state.rules.weather.add(new WeatherEntry(Weathers.sporestorm)); } - state.secinfo.resources = content.asArray(); - state.secinfo.resources.sort(Structs.comps(Structs.comparing(Content::getContentType), Structs.comparingInt(c -> c.id))); - + sector.info.resources = content.asArray(); + sector.info.resources.sort(Structs.comps(Structs.comparing(Content::getContentType), Structs.comparingInt(c -> c.id))); + sector.saveInfo(); } public Context filterContext(Map map){ diff --git a/core/src/mindustry/ctype/UnlockableContent.java b/core/src/mindustry/ctype/UnlockableContent.java index f6e6d0a2dc..9020cd0ff1 100644 --- a/core/src/mindustry/ctype/UnlockableContent.java +++ b/core/src/mindustry/ctype/UnlockableContent.java @@ -10,11 +10,14 @@ import mindustry.game.EventType.*; import mindustry.graphics.*; import mindustry.type.*; import mindustry.ui.*; +import mindustry.world.meta.*; import static mindustry.Vars.*; /** Base interface for an unlockable content type. */ public abstract class UnlockableContent extends MappableContent{ + /** Stat storage for this content. Initialized on demand. */ + public Stats stats = new Stats(); /** Localized, formal name. Never null. Set to internal name if not found in bundle. */ public String localizedName; /** Localized description. May be null. */ @@ -38,6 +41,18 @@ public abstract class UnlockableContent extends MappableContent{ return minfo.mod == null ? description : description + "\n" + Core.bundle.format("mod.display", minfo.mod.meta.displayName()); } + /** Checks stat initialization state. Call before displaying stats. */ + public void checkStats(){ + if(!stats.intialized){ + setStats(); + stats.intialized = true; + } + } + + /** Intializes stats on demand. Should only be called once. Only called before something is displayed. */ + public void setStats(){ + } + /** Generate any special icons for this content. Called asynchronously.*/ @CallSuper public void createIcons(MultiPacker packer){ @@ -73,7 +88,9 @@ public abstract class UnlockableContent extends MappableContent{ } /** This should show all necessary info about this content in the specified table. */ - public abstract void displayInfo(Table table); + public void display(Table table){ + + } /** Called when this content is unlocked. Use this to unlock other related content. */ public void onUnlock(){ @@ -95,16 +112,25 @@ public abstract class UnlockableContent extends MappableContent{ } } - public final boolean unlocked(){ + /** Unlocks this content, but does not fire any events. */ + public void quiteUnlock(){ + if(!unlocked()){ + unlocked = true; + Core.settings.put(name + "-unlocked", true); + } + } + + public boolean unlocked(){ + if(net.client()) return state.rules.researched.contains(name); return unlocked || alwaysUnlocked; } /** @return whether this content is unlocked, or the player is in a custom (non-campaign) game. */ - public final boolean unlockedNow(){ - return unlocked || alwaysUnlocked || !state.isCampaign(); + public boolean unlockedNow(){ + return unlocked() || !state.isCampaign(); } - public final boolean locked(){ + public boolean locked(){ return !unlocked(); } } diff --git a/core/src/mindustry/editor/EditorTile.java b/core/src/mindustry/editor/EditorTile.java index 5ff533325d..2d08b9d3d2 100644 --- a/core/src/mindustry/editor/EditorTile.java +++ b/core/src/mindustry/editor/EditorTile.java @@ -105,9 +105,9 @@ public class EditorTile extends Tile{ } @Override - protected void changeEntity(Team team, Prov entityprov, int rotation){ + protected void changeBuild(Team team, Prov entityprov, int rotation){ if(skip()){ - super.changeEntity(team, entityprov, rotation); + super.changeBuild(team, entityprov, rotation); return; } diff --git a/core/src/mindustry/editor/MapEditor.java b/core/src/mindustry/editor/MapEditor.java index 49d174779b..1c33964e08 100644 --- a/core/src/mindustry/editor/MapEditor.java +++ b/core/src/mindustry/editor/MapEditor.java @@ -4,6 +4,7 @@ import arc.files.*; import arc.func.*; import arc.graphics.*; import arc.math.*; +import arc.math.geom.*; import arc.struct.*; import mindustry.content.*; import mindustry.editor.DrawOperation.*; @@ -180,6 +181,52 @@ public class MapEditor{ return false; } + public void addCliffs(){ + for(Tile tile : world.tiles){ + if(!tile.block().isStatic() || tile.block() == Blocks.cliff) continue; + + int rotation = 0; + for(int i = 0; i < 8; i++){ + Tile other = world.tiles.get(tile.x + Geometry.d8[i].x, tile.y + Geometry.d8[i].y); + if(other != null && !other.block().isStatic()){ + rotation |= (1 << i); + } + } + + if(rotation != 0){ + tile.setBlock(Blocks.cliff); + } + + tile.data = (byte)rotation; + } + + for(Tile tile : world.tiles){ + if(tile.block() != Blocks.cliff && tile.block().isStatic()){ + tile.setBlock(Blocks.air); + } + } + } + + public void addFloorCliffs(){ + for(Tile tile : world.tiles){ + if(!tile.floor().hasSurface() || tile.block() == Blocks.cliff) continue; + + int rotation = 0; + for(int i = 0; i < 8; i++){ + Tile other = world.tiles.get(tile.x + Geometry.d8[i].x, tile.y + Geometry.d8[i].y); + if(other != null && !other.floor().hasSurface()){ + rotation |= (1 << i); + } + } + + if(rotation != 0){ + tile.setBlock(Blocks.cliff); + } + + tile.data = (byte)rotation; + } + } + public void drawCircle(int x, int y, Cons drawer){ for(int rx = -brushSize; rx <= brushSize; rx++){ for(int ry = -brushSize; ry <= brushSize; ry++){ diff --git a/core/src/mindustry/editor/MapEditorDialog.java b/core/src/mindustry/editor/MapEditorDialog.java index 4f7eb008fd..dfad7c03ab 100644 --- a/core/src/mindustry/editor/MapEditorDialog.java +++ b/core/src/mindustry/editor/MapEditorDialog.java @@ -385,7 +385,7 @@ public class MapEditorDialog extends Dialog implements Disposable{ } public void build(){ - float size = 60f; + float size = 58f; clearChildren(); table(cont -> { @@ -559,10 +559,19 @@ public class MapEditorDialog extends Dialog implements Disposable{ mid.row(); - mid.table(t -> { - t.button("@editor.center", Icon.move, Styles.cleart, () -> view.center()).growX().margin(9f); - }).growX().top(); + if(!mobile){ + mid.table(t -> { + t.button("@editor.center", Icon.move, Styles.cleart, view::center).growX().margin(9f); + }).growX().top(); + } + if(addCliffButton){ + mid.row(); + + mid.table(t -> { + t.button("Cliffs", Icon.terrain, Styles.cleart, editor::addCliffs).growX().margin(9f); + }).growX().top(); + } }).margin(0).left().growY(); diff --git a/core/src/mindustry/editor/MapGenerateDialog.java b/core/src/mindustry/editor/MapGenerateDialog.java index fae31dc7d5..6a06e0b966 100644 --- a/core/src/mindustry/editor/MapGenerateDialog.java +++ b/core/src/mindustry/editor/MapGenerateDialog.java @@ -51,7 +51,7 @@ public class MapGenerateDialog extends BaseDialog{ CachedTile ctile = new CachedTile(){ //nothing. @Override - protected void changeEntity(Team team, Prov entityprov, int rotation){ + protected void changeBuild(Team team, Prov entityprov, int rotation){ } }; diff --git a/core/src/mindustry/editor/WaveGraph.java b/core/src/mindustry/editor/WaveGraph.java index 14b2416085..2436c62c2a 100644 --- a/core/src/mindustry/editor/WaveGraph.java +++ b/core/src/mindustry/editor/WaveGraph.java @@ -154,7 +154,7 @@ public class WaveGraph extends Table{ int sum = 0; for(SpawnGroup spawn : groups){ - int spawned = spawn.getUnitsSpawned(i); + int spawned = spawn.getSpawned(i); values[index][spawn.type.id] += spawned; if(spawned > 0){ used.add(spawn.type); diff --git a/core/src/mindustry/entities/Damage.java b/core/src/mindustry/entities/Damage.java index e22891eba5..b6c9abe807 100644 --- a/core/src/mindustry/entities/Damage.java +++ b/core/src/mindustry/entities/Damage.java @@ -9,6 +9,7 @@ import arc.struct.*; import arc.util.*; import mindustry.annotations.Annotations.*; import mindustry.content.*; +import mindustry.core.*; import mindustry.game.EventType.*; import mindustry.game.*; import mindustry.gen.*; @@ -82,7 +83,7 @@ public class Damage{ furthest = null; - boolean found = 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); return found && furthest != null ? Math.max(6f, b.dst(furthest.worldx(), furthest.worldy())) : length; diff --git a/core/src/mindustry/entities/Lightning.java b/core/src/mindustry/entities/Lightning.java index 738bfa1f1e..2d647bfb1e 100644 --- a/core/src/mindustry/entities/Lightning.java +++ b/core/src/mindustry/entities/Lightning.java @@ -5,6 +5,7 @@ import arc.math.*; import arc.math.geom.*; import arc.struct.*; import mindustry.content.*; +import mindustry.core.*; import mindustry.entities.bullet.*; import mindustry.game.*; import mindustry.gen.*; @@ -48,7 +49,7 @@ public class Lightning{ bhit = false; Vec2 from = lines.get(lines.size - 2); Vec2 to = lines.get(lines.size - 1); - world.raycastEach(world.toTile(from.getX()), world.toTile(from.getY()), world.toTile(to.getX()), world.toTile(to.getY()), (wx, wy) -> { + world.raycastEach(World.toTile(from.getX()), World.toTile(from.getY()), World.toTile(to.getX()), World.toTile(to.getY()), (wx, wy) -> { Tile tile = world.tile(wx, wy); if(tile != null && tile.block().insulated){ diff --git a/core/src/mindustry/entities/Units.java b/core/src/mindustry/entities/Units.java index 1babb75e33..e4591a0ed0 100644 --- a/core/src/mindustry/entities/Units.java +++ b/core/src/mindustry/entities/Units.java @@ -126,7 +126,7 @@ public class Units{ nearby(x, y, width, height, unit -> { if(boolResult) return; - if((unit.isGrounded() && !unit.type().hovering) == ground){ + if((unit.isGrounded() && !unit.type.hovering) == ground){ unit.hitbox(hitrect); if(hitrect.overlaps(x, y, width, height)){ diff --git a/core/src/mindustry/entities/abilities/ForceFieldAbility.java b/core/src/mindustry/entities/abilities/ForceFieldAbility.java index fa9fa5e2c8..d341191a74 100644 --- a/core/src/mindustry/entities/abilities/ForceFieldAbility.java +++ b/core/src/mindustry/entities/abilities/ForceFieldAbility.java @@ -94,7 +94,7 @@ public class ForceFieldAbility extends Ability{ } } - private void checkRadius(Unit unit){ + public void checkRadius(Unit unit){ //timer2 is used to store radius scale as an effect realRad = radiusScale * radius; } diff --git a/core/src/mindustry/entities/bullet/BulletType.java b/core/src/mindustry/entities/bullet/BulletType.java index 0c3a605f1b..8e06cf466e 100644 --- a/core/src/mindustry/entities/bullet/BulletType.java +++ b/core/src/mindustry/entities/bullet/BulletType.java @@ -104,6 +104,8 @@ public abstract class BulletType extends Content{ public float incendChance = 1f; public float homingPower = 0f; public float homingRange = 50f; + /** Use a negative value to disable homing delay. */ + public float homingDelay = -1f; public Color lightningColor = Pal.surge; public int lightning; @@ -137,6 +139,15 @@ public abstract class BulletType extends Content{ this(1f, 1f); } + /** @return estimated damage per shot. this can be very inaccurate. */ + public float estimateDPS(){ + float sum = damage + splashDamage*0.75f; + if(fragBullet != null && fragBullet != this){ + sum += fragBullet.estimateDPS() * fragBullets / 2f; + } + return sum; + } + /** Returns maximum distance the bullet this bullet type has can travel. */ public float range(){ return Math.max(speed * lifetime * (1f - drag), range); @@ -251,7 +262,7 @@ public abstract class BulletType extends Content{ } public void update(Bullet b){ - if(homingPower > 0.0001f){ + if(homingPower > 0.0001f && b.time >= homingDelay){ Teamc target = Units.closestTarget(b.team, b.x, b.y, homingRange, e -> (e.isGrounded() && collidesGround) || (e.isFlying() && collidesAir), t -> collidesGround); if(target != null){ b.vel.setAngle(Mathf.slerpDelta(b.rotation(), b.angleTo(target), homingPower)); diff --git a/core/src/mindustry/entities/bullet/ContinuousLaserBulletType.java b/core/src/mindustry/entities/bullet/ContinuousLaserBulletType.java index a862671040..4c52105b26 100644 --- a/core/src/mindustry/entities/bullet/ContinuousLaserBulletType.java +++ b/core/src/mindustry/entities/bullet/ContinuousLaserBulletType.java @@ -44,6 +44,12 @@ public class ContinuousLaserBulletType extends BulletType{ this(0); } + @Override + public float estimateDPS(){ + //assume firing duration is about 100 by default, may not be accurate there's no way of knowing in this method + return damage * 100f / 5f; + } + @Override public float range(){ return length; diff --git a/core/src/mindustry/entities/bullet/LaserBulletType.java b/core/src/mindustry/entities/bullet/LaserBulletType.java index ad5965f3b8..6d43baf95c 100644 --- a/core/src/mindustry/entities/bullet/LaserBulletType.java +++ b/core/src/mindustry/entities/bullet/LaserBulletType.java @@ -39,6 +39,11 @@ public class LaserBulletType extends BulletType{ this(1f); } + @Override + public float estimateDPS(){ + return super.estimateDPS() * 2f; + } + @Override public void init(){ super.init(); diff --git a/core/src/mindustry/entities/bullet/LiquidBulletType.java b/core/src/mindustry/entities/bullet/LiquidBulletType.java index 2d3de79977..5da33f95e0 100644 --- a/core/src/mindustry/entities/bullet/LiquidBulletType.java +++ b/core/src/mindustry/entities/bullet/LiquidBulletType.java @@ -22,6 +22,8 @@ public class LiquidBulletType extends BulletType{ if(liquid != null){ this.liquid = liquid; this.status = liquid.effect; + lightColor = liquid.lightColor; + lightOpacity = liquid.lightColor.a; } ammoMultiplier = 1f; diff --git a/core/src/mindustry/entities/comp/BuildingComp.java b/core/src/mindustry/entities/comp/BuildingComp.java index 15d79a8942..87518c35ef 100644 --- a/core/src/mindustry/entities/comp/BuildingComp.java +++ b/core/src/mindustry/entities/comp/BuildingComp.java @@ -17,16 +17,19 @@ import arc.util.io.*; import mindustry.annotations.Annotations.*; import mindustry.audio.*; import mindustry.content.*; +import mindustry.core.*; import mindustry.ctype.*; import mindustry.entities.*; import mindustry.game.EventType.*; import mindustry.game.*; +import mindustry.game.Teams.*; import mindustry.gen.*; import mindustry.graphics.*; import mindustry.logic.*; import mindustry.type.*; import mindustry.ui.*; import mindustry.world.*; +import mindustry.world.blocks.ConstructBlock.*; import mindustry.world.blocks.environment.*; import mindustry.world.blocks.payloads.*; import mindustry.world.blocks.power.*; @@ -191,6 +194,36 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, //endregion //region utility methods + public void addPlan(boolean checkPrevious){ + if(!block.rebuildable) return; + + if(self() instanceof ConstructBuild entity){ + //update block to reflect the fact that something was being constructed + if(entity.cblock != null && entity.cblock.synthetic()){ + block = entity.cblock; + }else{ + //otherwise this was a deconstruction that was interrupted, don't want to rebuild that + return; + } + } + + TeamData data = state.teams.get(team); + + if(checkPrevious){ + //remove existing blocks that have been placed here. + //painful O(n) iteration + copy + for(int i = 0; i < data.blocks.size; i++){ + BlockPlan b = data.blocks.get(i); + if(b.x == tile.x && b.y == tile.y){ + data.blocks.removeIndex(i); + break; + } + } + } + + data.blocks.addFirst(new BlockPlan(tile.x, tile.y, (short)rotation, block.id, config())); + } + /** Configure with the current, local player. */ public void configure(Object value){ //save last used config @@ -431,7 +464,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, */ public boolean movePayload(Payload todump){ int trns = block.size/2 + 1; - Tile next = tile.getNearby(Geometry.d4(rotation).x * trns, Geometry.d4(rotation).y * trns); + Tile next = tile.nearby(Geometry.d4(rotation).x * trns, Geometry.d4(rotation).y * trns); if(next != null && next.build != null && next.build.team == team && next.build.acceptPayload(self(), todump)){ next.build.handlePayload(self(), todump); @@ -514,7 +547,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, } public float moveLiquidForward(boolean leaks, Liquid liquid){ - Tile next = tile.getNearby(rotation); + Tile next = tile.nearby(rotation); if(next == null) return 0; @@ -766,9 +799,9 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, } public void drawCracks(){ - if(!damaged() || block.size > Block.maxCrackSize) return; + if(!damaged() || block.size > BlockRenderer.maxCrackSize) return; int id = pos(); - TextureRegion region = Block.cracks[block.size - 1][Mathf.clamp((int)((1f - healthf()) * Block.crackRegions), 0, Block.crackRegions-1)]; + TextureRegion region = renderer.blocks.cracks[block.size - 1][Mathf.clamp((int)((1f - healthf()) * BlockRenderer.crackRegions), 0, BlockRenderer.crackRegions-1)]; Draw.colorl(0.2f, 0.1f + (1f - healthf())* 0.6f); Draw.rect(region, x, y, (id%4)*90); Draw.color(); @@ -878,7 +911,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, /** Called when arbitrary configuration is applied to a tile. */ public void configured(@Nullable Unit builder, @Nullable Object value){ //null is of type void.class; anonymous classes use their superclass. - Class type = value == null ? void.class : value.getClass().isAnonymousClass() ? value.getClass().getSuperclass() : value.getClass(); + Class type = value == null ? void.class : value.getClass().isAnonymousClass() || value.getClass().getSimpleName().startsWith("adapter") ? value.getClass().getSuperclass() : value.getClass(); if(builder != null && builder.isPlayer()){ lastAccessed = builder.getPlayer().name; @@ -1234,8 +1267,8 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, @Override public double sense(LAccess sensor){ return switch(sensor){ - case x -> x; - case y -> y; + case x -> World.conv(x); + case y -> World.conv(y); case team -> team.id; case health -> health; case maxHealth -> maxHealth; @@ -1263,7 +1296,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, case type -> block; case firstItem -> items == null ? null : items.first(); case config -> block.configurations.containsKey(Item.class) || block.configurations.containsKey(Liquid.class) ? config() : null; - case payloadType -> getPayload() instanceof UnitPayload p1 ? p1.unit.type() : getPayload() instanceof BuildPayload p2 ? p2.block() : null; + case payloadType -> getPayload() instanceof UnitPayload p1 ? p1.unit.type : getPayload() instanceof BuildPayload p2 ? p2.block() : null; default -> noSensed; }; diff --git a/core/src/mindustry/entities/comp/BulletComp.java b/core/src/mindustry/entities/comp/BulletComp.java index d9aa007c5e..5a08710762 100644 --- a/core/src/mindustry/entities/comp/BulletComp.java +++ b/core/src/mindustry/entities/comp/BulletComp.java @@ -7,6 +7,7 @@ import arc.math.geom.*; import arc.struct.*; import arc.util.*; import mindustry.annotations.Annotations.*; +import mindustry.core.*; import mindustry.entities.bullet.*; import mindustry.game.*; import mindustry.game.Teams.*; @@ -110,7 +111,7 @@ abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Draw type.update(self()); if(type.collidesTiles && type.collides && type.collidesGround){ - world.raycastEach(world.toTile(lastX()), world.toTile(lastY()), tileX(), tileY(), (x, y) -> { + world.raycastEach(World.toTile(lastX()), World.toTile(lastY()), tileX(), tileY(), (x, y) -> { Building tile = world.build(x, y); if(tile == null || !isAdded()) return false; diff --git a/core/src/mindustry/entities/comp/CommanderComp.java b/core/src/mindustry/entities/comp/CommanderComp.java index b0fbddcf85..124eb06d91 100644 --- a/core/src/mindustry/entities/comp/CommanderComp.java +++ b/core/src/mindustry/entities/comp/CommanderComp.java @@ -59,7 +59,7 @@ abstract class CommanderComp implements Entityc, Posc{ units.clear(); Units.nearby(team, x, y, 150f, u -> { - if(u.isAI() && include.get(u) && u != self() && u.type().flying == type.flying && u.hitSize <= hitSize * 1.1f){ + if(u.isAI() && include.get(u) && u != self() && u.type.flying == type.flying && u.hitSize <= hitSize * 1.1f){ units.add(u); } }); @@ -74,7 +74,7 @@ abstract class CommanderComp implements Entityc, Posc{ void command(Formation formation, Seq units){ clearCommand(); - float spacing = hitSize * 0.65f; + float spacing = hitSize * 0.8f; minFormationSpeed = type.speed; controlling.addAll(units); @@ -82,7 +82,7 @@ abstract class CommanderComp implements Entityc, Posc{ FormationAI ai; unit.controller(ai = new FormationAI(self(), formation)); spacing = Math.max(spacing, ai.formationSize()); - minFormationSpeed = Math.min(minFormationSpeed, unit.type().speed); + minFormationSpeed = Math.min(minFormationSpeed, unit.type.speed); } this.formation = formation; @@ -106,7 +106,7 @@ abstract class CommanderComp implements Entityc, Posc{ //reset controlled units for(Unit unit : controlling){ if(unit.controller().isBeingControlled(self())){ - unit.controller(unit.type().createController()); + unit.controller(unit.type.createController()); } } diff --git a/core/src/mindustry/entities/comp/FireComp.java b/core/src/mindustry/entities/comp/FireComp.java index ea67ca9e19..84fcb6b3b5 100644 --- a/core/src/mindustry/entities/comp/FireComp.java +++ b/core/src/mindustry/entities/comp/FireComp.java @@ -16,7 +16,7 @@ import static mindustry.Vars.*; @EntityDef(value = {Firec.class}, pooled = true) @Component(base = true) abstract class FireComp implements Timedc, Posc, Firec, Syncc{ - private static final float spreadChance = 0.05f, fireballChance = 0.07f; + private static final float spreadChance = 0.04f, fireballChance = 0.06f; @Import float time, lifetime, x, y; diff --git a/core/src/mindustry/entities/comp/PayloadComp.java b/core/src/mindustry/entities/comp/PayloadComp.java index ac1f4fa323..e2945d08f7 100644 --- a/core/src/mindustry/entities/comp/PayloadComp.java +++ b/core/src/mindustry/entities/comp/PayloadComp.java @@ -6,6 +6,7 @@ import arc.util.*; import mindustry.*; import mindustry.annotations.Annotations.*; import mindustry.content.*; +import mindustry.core.*; import mindustry.entities.*; import mindustry.gen.*; import mindustry.type.*; @@ -120,7 +121,7 @@ abstract class PayloadComp implements Posc, Rotc, Hitboxc, Unitc{ /** @return whether the tile has been successfully placed. */ boolean dropBlock(BuildPayload payload){ Building tile = payload.build; - int tx = Vars.world.toTile(x - tile.block.offset), ty = Vars.world.toTile(y - tile.block.offset); + int tx = World.toTile(x - tile.block.offset), ty = World.toTile(y - tile.block.offset); Tile on = Vars.world.tile(tx, ty); if(on != null && Build.validPlace(tile.block, tile.team, tx, ty, tile.rotation, false)){ int rot = (int)((rotation + 45f) / 90f) % 4; diff --git a/core/src/mindustry/entities/comp/PlayerComp.java b/core/src/mindustry/entities/comp/PlayerComp.java index 4ee481b032..736a1b68e5 100644 --- a/core/src/mindustry/entities/comp/PlayerComp.java +++ b/core/src/mindustry/entities/comp/PlayerComp.java @@ -79,7 +79,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra admin = typing = false; textFadeTime = 0f; if(!dead()){ - unit.controller(unit.type().createController()); + unit.controller(unit.type.createController()); unit = Nulls.unit; } } @@ -91,7 +91,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra @Replace public float clipSize(){ - return unit.isNull() ? 20 : unit.type().hitSize * 2f; + return unit.isNull() ? 20 : unit.type.hitSize * 2f; } @Override @@ -123,7 +123,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra deathTimer = 0; //update some basic state to sync things - if(unit.type().canBoost){ + if(unit.type.canBoost){ Tile tile = unit.tileOn(); unit.elevation = Mathf.approachDelta(unit.elevation, (tile != null && tile.solid()) || boosting ? 1f : 0f, 0.08f); } @@ -177,7 +177,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra if(this.unit != Nulls.unit){ //un-control the old unit - this.unit.controller(this.unit.type().createController()); + this.unit.controller(this.unit.type.createController()); } this.unit = unit; if(unit != Nulls.unit){ diff --git a/core/src/mindustry/entities/comp/PosComp.java b/core/src/mindustry/entities/comp/PosComp.java index 15c665739e..6446c0f91f 100644 --- a/core/src/mindustry/entities/comp/PosComp.java +++ b/core/src/mindustry/entities/comp/PosComp.java @@ -2,9 +2,9 @@ package mindustry.entities.comp; import arc.math.geom.*; import arc.util.*; -import mindustry.*; import mindustry.annotations.Annotations.*; import mindustry.content.*; +import mindustry.core.*; import mindustry.world.*; import mindustry.world.blocks.environment.*; @@ -32,11 +32,11 @@ abstract class PosComp implements Position{ } int tileX(){ - return Vars.world.toTile(x); + return World.toTile(x); } int tileY(){ - return Vars.world.toTile(y); + return World.toTile(y); } /** Returns air if this unit is on a non-air top block. */ diff --git a/core/src/mindustry/entities/comp/PuddleComp.java b/core/src/mindustry/entities/comp/PuddleComp.java index 669ee7e1f2..a447bd633a 100644 --- a/core/src/mindustry/entities/comp/PuddleComp.java +++ b/core/src/mindustry/entities/comp/PuddleComp.java @@ -74,7 +74,7 @@ abstract class PuddleComp implements Posc, Puddlec, Drawc{ unit.apply(liquid.effect, 60 * 2); if(unit.vel.len() > 0.1){ - Fx.ripple.at(unit.x, unit.y, unit.type().rippleScale, liquid.color); + Fx.ripple.at(unit.x, unit.y, unit.type.rippleScale, liquid.color); } } } @@ -98,7 +98,7 @@ abstract class PuddleComp implements Posc, Puddlec, Drawc{ boolean onLiquid = tile.floor().isLiquid; float f = Mathf.clamp(amount / (maxLiquid / 1.5f)); float smag = onLiquid ? 0.8f : 0f; - float sscl = 20f; + float sscl = 25f; Draw.color(tmp.set(liquid.color).shiftValue(-0.05f)); Fill.circle(x + Mathf.sin(Time.time() + seeds * 532, sscl, smag), y + Mathf.sin(Time.time() + seeds * 53, sscl, smag), f * 8f); diff --git a/core/src/mindustry/entities/comp/UnitComp.java b/core/src/mindustry/entities/comp/UnitComp.java index 1cf0f7e216..ca85eacd07 100644 --- a/core/src/mindustry/entities/comp/UnitComp.java +++ b/core/src/mindustry/entities/comp/UnitComp.java @@ -10,6 +10,7 @@ import arc.util.*; import mindustry.ai.*; import mindustry.annotations.Annotations.*; import mindustry.content.*; +import mindustry.core.*; import mindustry.ctype.*; import mindustry.entities.*; import mindustry.entities.abilities.*; @@ -36,7 +37,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I @Import int id; private UnitController controller; - private UnitType type; + UnitType type; boolean spawnedByCore; double flag; @@ -88,12 +89,14 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I case rotation -> rotation; case health -> health; case maxHealth -> maxHealth; - case x -> x; - case y -> y; + case ammo -> !state.rules.unitAmmo ? type.ammoCapacity : ammo; + case ammoCapacity -> type.ammoCapacity; + case x -> World.conv(x); + case y -> World.conv(y); case team -> team.id; case shooting -> isShooting() ? 1 : 0; - case shootX -> aimX(); - case shootY -> aimY(); + case shootX -> World.conv(aimX()); + case shootY -> World.conv(aimY()); case flag -> flag; case payloadCount -> self() instanceof Payloadc pay ? pay.payloads().size : 0; default -> 0; @@ -108,7 +111,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I case firstItem -> stack().amount == 0 ? null : item(); case payloadType -> self() instanceof Payloadc pay ? (pay.payloads().isEmpty() ? null : - pay.payloads().peek() instanceof UnitPayload p1 ? p1.unit.type() : + pay.payloads().peek() instanceof UnitPayload p1 ? p1.unit.type : pay.payloads().peek() instanceof BuildPayload p2 ? p2.block() : null) : null; default -> noSensed; }; @@ -161,22 +164,12 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I @Override public void set(UnitType def, UnitController controller){ - type(type); + if(this.type != def){ + setType(def); + } controller(controller); } - @Override - public void type(UnitType type){ - if(this.type == type) return; - - setStats(type); - } - - @Override - public UnitType type(){ - return type; - } - /** @return pathfinder path type for calculating costs */ public int pathType(){ return Pathfinder.costGround; @@ -206,7 +199,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I return Units.getCap(team); } - public void setStats(UnitType type){ + public void setType(UnitType type){ this.type = type; this.maxHealth = type.health; this.drag = type.drag; @@ -224,7 +217,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I @Override public void afterSync(){ //set up type info after reading - setStats(this.type); + setType(this.type); controller.unit(self()); } @@ -284,7 +277,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I drag = type.drag * (isGrounded() ? (floorOn().dragMultiplier) : 1f); //apply knockback based on spawns - if(team != state.rules.waveTeam){ + if(team != state.rules.waveTeam && state.hasSpawns()){ float relativeSize = state.rules.dropZoneRadius + hitSize/2f + 1f; for(Tile spawn : spawner.getSpawns()){ if(within(spawn.worldx(), spawn.worldy(), relativeSize)){ diff --git a/core/src/mindustry/entities/comp/WeaponsComp.java b/core/src/mindustry/entities/comp/WeaponsComp.java index d891dbc1e4..dec0105c14 100644 --- a/core/src/mindustry/entities/comp/WeaponsComp.java +++ b/core/src/mindustry/entities/comp/WeaponsComp.java @@ -166,7 +166,7 @@ abstract class WeaponsComp implements Teamc, Posc, Rotc, Velc, Statusc{ Weapon weapon = mount.weapon; float baseX = this.x, baseY = this.y; - boolean delay = weapon.firstShotDelay > 0f; + boolean delay = weapon.firstShotDelay + weapon.shotDelay > 0f; (delay ? weapon.chargeSound : weapon.shootSound).at(x, y, Mathf.random(0.8f, 1.0f)); diff --git a/core/src/mindustry/entities/units/AIController.java b/core/src/mindustry/entities/units/AIController.java index b02fa5dc00..ca005fe25d 100644 --- a/core/src/mindustry/entities/units/AIController.java +++ b/core/src/mindustry/entities/units/AIController.java @@ -95,7 +95,7 @@ public class AIController implements UnitController{ if(tile == targetTile || (costType == Pathfinder.costWater && !targetTile.floor().isLiquid)) return; - unit.moveAt(vec.trns(unit.angleTo(targetTile), unit.type().speed)); + unit.moveAt(vec.trns(unit.angleTo(targetTile), unit.type.speed)); } protected void updateWeapons(){ @@ -105,7 +105,7 @@ public class AIController implements UnitController{ boolean ret = retarget(); if(ret){ - target = findTarget(unit.x, unit.y, unit.range(), unit.type().targetAir, unit.type().targetGround); + target = findTarget(unit.x, unit.y, unit.range(), unit.type.targetAir, unit.type.targetGround); } if(invalid(target)){ @@ -119,7 +119,7 @@ public class AIController implements UnitController{ float mountX = unit.x + Angles.trnsx(rotation, weapon.x, weapon.y), mountY = unit.y + Angles.trnsy(rotation, weapon.x, weapon.y); - if(unit.type().singleTarget){ + if(unit.type.singleTarget){ targets[i] = target; }else{ if(ret){ @@ -176,7 +176,7 @@ public class AIController implements UnitController{ } protected void circle(Position target, float circleLength){ - circle(target, circleLength, unit.type().speed); + circle(target, circleLength, unit.type.speed); } protected void circle(Position target, float circleLength, float speed){ diff --git a/core/src/mindustry/game/DefaultWaves.java b/core/src/mindustry/game/DefaultWaves.java index 0ce0d3a6a2..d33af5a41a 100644 --- a/core/src/mindustry/game/DefaultWaves.java +++ b/core/src/mindustry/game/DefaultWaves.java @@ -18,6 +18,7 @@ public class DefaultWaves{ new SpawnGroup(dagger){{ end = 10; unitScaling = 2f; + max = 30; }}, new SpawnGroup(crawler){{ @@ -45,6 +46,7 @@ public class DefaultWaves{ begin = 13; spacing = 3; unitScaling = 0.5f; + max = 25; }}, new SpawnGroup(mace){{ @@ -61,7 +63,7 @@ public class DefaultWaves{ unitAmount = 4; spacing = 2; shieldScaling = 10f; - max = 20; + max = 14; }}, new SpawnGroup(mace){{ @@ -81,7 +83,7 @@ public class DefaultWaves{ effect = StatusEffects.overdrive; }}, - new SpawnGroup(mace){{ + new SpawnGroup(pulsar){{ begin = 120; spacing = 2; unitScaling = 3; @@ -94,6 +96,7 @@ public class DefaultWaves{ unitScaling = 1; spacing = 2; shieldScaling = 20f; + max = 20; }}, new SpawnGroup(quasar){{ @@ -111,6 +114,7 @@ public class DefaultWaves{ unitAmount = 1; unitScaling = 3; effect = StatusEffects.shielded; + max = 25; }}, new SpawnGroup(fortress){{ @@ -122,7 +126,7 @@ public class DefaultWaves{ shieldScaling = 30; }}, - new SpawnGroup(dagger){{ + new SpawnGroup(nova){{ begin = 35; spacing = 3; unitAmount = 4; @@ -138,6 +142,7 @@ public class DefaultWaves{ effect = StatusEffects.overdrive; items = new ItemStack(Items.pyratite, 100); end = 130; + max = 30; }}, new SpawnGroup(horizon){{ @@ -156,6 +161,7 @@ public class DefaultWaves{ shields = 100f; shieldScaling = 10f; effect = StatusEffects.overdrive; + max = 20; }}, new SpawnGroup(zenith){{ @@ -233,7 +239,7 @@ public class DefaultWaves{ shieldScaling = 20f; }}, - new SpawnGroup(atrax){{ + new SpawnGroup(toxopid){{ begin = 210; unitAmount = 1; unitScaling = 1; @@ -258,7 +264,7 @@ public class DefaultWaves{ {nova, pulsar, quasar, vela, corvus}, {crawler, atrax, spiroct, arkyid, toxopid}, //{risso, minke, bryde, sei, omura}, //questionable choices - //{mono, poly, mega, quad, oct}, //do not attack + {poly, poly, mega, quad, quad}, {flare, horizon, zenith, antumbra, eclipse} }; @@ -290,7 +296,7 @@ public class DefaultWaves{ begin = f; end = f + next >= cap ? never : f + next; max = 14; - unitScaling = rand.random(1f, 2f); + unitScaling = rand.random(1f, 3f); shields = shieldAmount; shieldScaling = shieldsPerWave; spacing = space; @@ -329,7 +335,7 @@ public class DefaultWaves{ while(step <= cap){ createProgression.get(step); - step += (int)(rand.random(12, 25) * Mathf.lerp(1f, 0.4f, difficulty)); + step += (int)(rand.random(13, 25) * Mathf.lerp(1f, 0.5f, difficulty)); } int bossWave = (int)(rand.random(30, 60) * Mathf.lerp(1f, 0.7f, difficulty)); diff --git a/core/src/mindustry/game/EventType.java b/core/src/mindustry/game/EventType.java index 272c26363f..1ab946c4ac 100644 --- a/core/src/mindustry/game/EventType.java +++ b/core/src/mindustry/game/EventType.java @@ -35,7 +35,13 @@ public class EventType{ preDraw, postDraw, uiDrawBegin, - uiDrawEnd + uiDrawEnd, + //before/after bloom used, skybox or planets drawn + universeDrawBegin, + //skybox drawn and bloom is enabled - use Vars.renderer.planets + universeDraw, + //planets drawn and bloom disabled + universeDrawEnd } public static class WinEvent{} @@ -73,6 +79,15 @@ public class EventType{ } } + /** Called when a sector is destroyed by waves when you're not there. */ + public static class SectorInvasionEvent{ + public final Sector sector; + + public SectorInvasionEvent(Sector sector){ + this.sector = sector; + } + } + public static class LaunchItemEvent{ public final ItemStack stack; @@ -214,8 +229,8 @@ public class EventType{ } /** - * Called when block building begins by placing down the BuildBlock. - * The tile's block will nearly always be a BuildBlock. + * Called when block building begins by placing down the ConstructBlock. + * The tile's block will nearly always be a ConstructBlock. */ public static class BlockBuildBeginEvent{ public final Tile tile; @@ -247,7 +262,7 @@ public class EventType{ /** * Called when a player or drone begins building something. - * This does not necessarily happen when a new BuildBlock is created. + * This does not necessarily happen when a new ConstructBlock is created. */ public static class BuildSelectEvent{ public final Tile tile; diff --git a/core/src/mindustry/game/Stats.java b/core/src/mindustry/game/GameStats.java similarity index 96% rename from core/src/mindustry/game/Stats.java rename to core/src/mindustry/game/GameStats.java index 1d175607ba..8108f2ddad 100644 --- a/core/src/mindustry/game/Stats.java +++ b/core/src/mindustry/game/GameStats.java @@ -6,7 +6,7 @@ import mindustry.type.*; //TODO more stats: //- units constructed -public class Stats{ +public class GameStats{ /** Total items delivered to global resoure counter. Campaign only. */ public ObjectIntMap itemsDelivered = new ObjectIntMap<>(); /** Enemy (red team) units destroyed. */ @@ -40,7 +40,7 @@ public class Stats{ //weigh used fractions float frac = 0f; - Seq obtainable = zone.save == null ? new Seq<>() : zone.save.meta.secinfo.resources.select(i -> i instanceof Item).as(); + Seq obtainable = zone.save == null ? new Seq<>() : zone.info.resources.select(i -> i instanceof Item).as(); for(Item item : obtainable){ frac += Mathf.clamp((float)itemsDelivered.get(item, 0) / capacity) / (float)obtainable.size; } diff --git a/core/src/mindustry/game/Gamemode.java b/core/src/mindustry/game/Gamemode.java index 9de5e71052..f2bb95130a 100644 --- a/core/src/mindustry/game/Gamemode.java +++ b/core/src/mindustry/game/Gamemode.java @@ -23,7 +23,6 @@ public enum Gamemode{ rules.waveTimer = true; rules.waveSpacing /= 2f; - rules.teams.get(rules.waveTeam).ai = true; rules.teams.get(rules.waveTeam).infiniteResources = true; }, map -> map.teams.contains(state.rules.waveTeam.id)), pvp(rules -> { diff --git a/core/src/mindustry/game/Objectives.java b/core/src/mindustry/game/Objectives.java index a4097c51fc..7fc844a180 100644 --- a/core/src/mindustry/game/Objectives.java +++ b/core/src/mindustry/game/Objectives.java @@ -28,7 +28,6 @@ public class Objectives{ } } - //TODO fix public static class SectorComplete extends SectorObjective{ public SectorComplete(SectorPreset zone){ @@ -39,12 +38,12 @@ public class Objectives{ @Override public boolean complete(){ - return preset.sector.save != null && preset.sector.save.meta.wave >= preset.sector.save.meta.rules.winWave; + return preset.sector.save != null && preset.sector.save.meta.wave >= preset.captureWave; } @Override public String display(){ - return Core.bundle.format("requirement.wave", preset.sector.save == null ? "" : preset.sector.save.meta.rules.winWave, preset.localizedName); + return Core.bundle.format("requirement.capture", preset.localizedName); } } diff --git a/core/src/mindustry/game/Rules.java b/core/src/mindustry/game/Rules.java index a101fe8592..2a927cc59b 100644 --- a/core/src/mindustry/game/Rules.java +++ b/core/src/mindustry/game/Rules.java @@ -82,6 +82,8 @@ public class Rules{ public Seq weather = new Seq<>(1); /** Blocks that cannot be placed. */ public ObjectSet bannedBlocks = new ObjectSet<>(); + /** Unlocked content names. Only used in multiplayer when the campaign is enabled. */ + public ObjectSet researched = new ObjectSet<>(); /** Whether ambient lighting is enabled. */ public boolean lighting = false; /** Whether enemy lighting is visible. @@ -104,6 +106,8 @@ public class Rules{ public boolean ai; /** TODO Tier of blocks/designs that the AI uses for building. [0, 1]*/ public float aiTier = 0f; + /** Whether, when AI is enabled, ships should be spawned from the core. */ + public boolean aiCoreSpawn = true; /** If true, blocks don't require power or resources. */ public boolean cheat; /** If true, resources are not consumed when building. */ diff --git a/core/src/mindustry/game/Schematics.java b/core/src/mindustry/game/Schematics.java index 596a571d3a..20fe7c36c4 100644 --- a/core/src/mindustry/game/Schematics.java +++ b/core/src/mindustry/game/Schematics.java @@ -16,6 +16,7 @@ import arc.util.pooling.*; import arc.util.serialization.*; import mindustry.*; import mindustry.content.*; +import mindustry.core.*; import mindustry.ctype.*; import mindustry.entities.units.*; import mindustry.game.EventType.*; @@ -608,8 +609,8 @@ public class Schematics implements Loadable{ wx = wy; wy = -x; } - req.x = (short)(world.toTile(wx - req.block.offset) + ox); - req.y = (short)(world.toTile(wy - req.block.offset) + oy); + req.x = (short)(World.toTile(wx - req.block.offset) + ox); + req.y = (short)(World.toTile(wy - req.block.offset) + oy); req.rotation = (byte)Mathf.mod(req.rotation + direction, 4); }); diff --git a/core/src/mindustry/game/SectorInfo.java b/core/src/mindustry/game/SectorInfo.java index b66dc5bf95..3b2b4df3dd 100644 --- a/core/src/mindustry/game/SectorInfo.java +++ b/core/src/mindustry/game/SectorInfo.java @@ -5,6 +5,7 @@ import arc.struct.*; import arc.util.*; import mindustry.content.*; import mindustry.ctype.*; +import mindustry.maps.*; import mindustry.type.*; import mindustry.world.*; import mindustry.world.blocks.storage.CoreBlock.*; @@ -25,9 +26,9 @@ public class SectorInfo{ /** Export statistics. */ public ObjectMap export = new ObjectMap<>(); /** Items stored in all cores. */ - public ItemSeq coreItems = new ItemSeq(); + public ItemSeq items = new ItemSeq(); /** The best available core type. */ - public Block bestCoreType = Blocks.air; + public Block bestCoreType = Blocks.coreShard; /** Max storage capacity. */ public int storageCapacity = 0; /** Whether a core is available here. */ @@ -38,8 +39,27 @@ public class SectorInfo{ public @Nullable Sector destination; /** Resources known to occur at this sector. */ public Seq resources = new Seq<>(); - /** Time spent at this sector. Do not use unless you know what you're doing. */ - public transient float internalTimeSpent; + /** Whether waves are enabled here. */ + public boolean waves = true; + /** Whether attack mode is enabled here. */ + public boolean attack = false; + /** Wave # from state */ + public int wave = 1, winWave = -1; + /** Time between waves. */ + public float waveSpacing = 60 * 60 * 2; + /** Damage dealt to sector. */ + public float damage; + /** How many waves have passed while the player was away. */ + public int wavesPassed; + /** Packed core spawn position. */ + public int spawnPosition; + /** How long the player has been playing elsewhere. */ + public float secondsPassed; + /** Display name. */ + public @Nullable String name; + + /** Special variables for simulation. */ + public float sumHealth, sumRps, sumDps, waveHealthBase, waveHealthSlope, waveDpsBase, waveDpsSlope; /** Counter refresh state. */ private transient Interval time = new Interval(); @@ -79,26 +99,68 @@ public class SectorInfo{ return export.get(item, ExportStat::new).mean; } + /** Write contents of meta into main storage. */ + public void write(){ + //enable attack mode when there's a core. + if(state.rules.waveTeam.core() != null){ + attack = true; + winWave = 0; + } + + //if there are infinite waves and no win wave, add a win wave. + if(waves && winWave <= 0 && !attack){ + winWave = 30; + } + + state.wave = wave; + state.rules.waves = waves; + state.rules.waveSpacing = waveSpacing; + state.rules.winWave = winWave; + state.rules.attackMode = attack; + + CoreBuild entity = state.rules.defaultTeam.core(); + if(entity != null){ + entity.items.clear(); + entity.items.add(items); + //ensure capacity. + entity.items.each((i, a) -> entity.items.set(i, Math.min(a, entity.storageCapacity))); + } + } + /** Prepare data for writing to a save. */ public void prepare(){ //update core items - coreItems.clear(); + items.clear(); CoreBuild entity = state.rules.defaultTeam.core(); if(entity != null){ ItemModule items = entity.items; for(int i = 0; i < items.length(); i++){ - coreItems.set(content.item(i), items.get(i)); + this.items.set(content.item(i), items.get(i)); } + + spawnPosition = entity.pos(); } + waveSpacing = state.rules.waveSpacing; + wave = state.wave; + winWave = state.rules.winWave; + waves = state.rules.waves; + attack = state.rules.attackMode; hasCore = entity != null; bestCoreType = !hasCore ? Blocks.air : state.rules.defaultTeam.cores().max(e -> e.block.size).block; storageCapacity = entity != null ? entity.storageCapacity : 0; + secondsPassed = 0; + wavesPassed = 0; + damage = 0; - //update sector's internal time spent counter - state.rules.sector.setTimeSpent(internalTimeSpent); + if(state.rules.sector != null){ + state.rules.sector.info = this; + state.rules.sector.saveInfo(); + } + + SectorDamage.writeParameters(this); } /** Update averages of various stats, updates some special sector logic. @@ -107,14 +169,6 @@ public class SectorInfo{ //updating in multiplayer as a client doesn't make sense if(net.client()) return; - internalTimeSpent += Time.delta; - - //autorun turns - if(internalTimeSpent >= turnDuration){ - internalTimeSpent = 0; - universe.runTurn(); - } - CoreBuild ent = state.rules.defaultTeam.core(); //refresh throughput diff --git a/core/src/mindustry/game/SpawnGroup.java b/core/src/mindustry/game/SpawnGroup.java index f9ae7c74d3..86ba7b6324 100644 --- a/core/src/mindustry/game/SpawnGroup.java +++ b/core/src/mindustry/game/SpawnGroup.java @@ -9,6 +9,8 @@ import mindustry.gen.*; import mindustry.io.legacy.*; import mindustry.type.*; +import java.util.*; + import static mindustry.Vars.*; /** @@ -52,8 +54,8 @@ public class SpawnGroup implements Serializable{ //serialization use only } - /** Returns the amount of units spawned on a specific wave. */ - public int getUnitsSpawned(int wave){ + /** @return amount of units spawned on a specific wave. */ + public int getSpawned(int wave){ if(spacing == 0) spacing = 1; if(wave < begin || wave > end || (wave - begin) % spacing != 0){ return 0; @@ -61,6 +63,11 @@ public class SpawnGroup implements Serializable{ return Math.min(unitAmount + (int)(((wave - begin) / spacing) / unitScaling), max); } + /** @return amount of shields each unit has at a specific wave. */ + public float getShield(int wave){ + return Math.max(shields + shieldScaling*(wave - begin), 0); + } + /** * Creates a unit, and assigns correct values based on this group's data. * This method does not add() the unit. @@ -76,7 +83,7 @@ public class SpawnGroup implements Serializable{ unit.addItem(items.item, items.amount); } - unit.shield(Math.max(shields + shieldScaling*(wave - begin), 0)); + unit.shield = getShield(wave); return unit; } @@ -133,4 +140,20 @@ public class SpawnGroup implements Serializable{ ", items=" + items + '}'; } + + @Override + public boolean equals(Object o){ + if(this == o) return true; + if(o == null || getClass() != o.getClass()) return false; + SpawnGroup group = (SpawnGroup)o; + return end == group.end && begin == group.begin && spacing == group.spacing && max == group.max + && Float.compare(group.unitScaling, unitScaling) == 0 && Float.compare(group.shields, shields) == 0 + && Float.compare(group.shieldScaling, shieldScaling) == 0 && unitAmount == group.unitAmount && + type == group.type && effect == group.effect && Structs.eq(items, group.items); + } + + @Override + public int hashCode(){ + return Arrays.hashCode(new Object[]{type, end, begin, spacing, max, unitScaling, shields, shieldScaling, unitAmount, effect, items}); + } } diff --git a/core/src/mindustry/game/Team.java b/core/src/mindustry/game/Team.java index 1234c7b2d7..cbed06f4e7 100644 --- a/core/src/mindustry/game/Team.java +++ b/core/src/mindustry/game/Team.java @@ -31,9 +31,9 @@ public class Team implements Comparable{ Color.valueOf("ffd37f"), Color.valueOf("eab678"), Color.valueOf("d4816b")), crux = new Team(2, "crux", Color.valueOf("f25555"), Color.valueOf("fc8e6c"), Color.valueOf("f25555"), Color.valueOf("a04553")), - green = new Team(3, "green", Color.valueOf("4dd98b")), - purple = new Team(4, "purple", Color.valueOf("9a4bdf")), - blue = new Team(5, "blue", Color.royal.cpy()); + green = new Team(3, "green", Color.valueOf("54d67d")), + purple = new Team(4, "purple", Color.valueOf("995bb0")), + blue = new Team(5, "blue", Color.valueOf("5a4deb")); static{ Mathf.rand.setSeed(8); diff --git a/core/src/mindustry/game/Teams.java b/core/src/mindustry/game/Teams.java index aa390c5d26..6a2972f074 100644 --- a/core/src/mindustry/game/Teams.java +++ b/core/src/mindustry/game/Teams.java @@ -131,7 +131,7 @@ public class Teams{ } private void count(Unit unit){ - unit.team.data().updateCount(unit.type(), 1); + unit.team.data().updateCount(unit.type, 1); if(unit instanceof Payloadc){ ((Payloadc)unit).payloads().each(p -> { @@ -178,15 +178,15 @@ public class Teams{ data.units.add(unit); data.presentFlag = true; - if(data.unitsByType == null || data.unitsByType.length <= unit.type().id){ + if(data.unitsByType == null || data.unitsByType.length <= unit.type.id){ data.unitsByType = new Seq[content.units().size]; } - if(data.unitsByType[unit.type().id] == null){ - data.unitsByType[unit.type().id] = new Seq<>(); + if(data.unitsByType[unit.type.id] == null){ + data.unitsByType[unit.type.id] = new Seq<>(); } - data.unitsByType[unit.type().id].add(unit); + data.unitsByType[unit.type.id].add(unit); count(unit); } diff --git a/core/src/mindustry/game/Universe.java b/core/src/mindustry/game/Universe.java index 892b93b731..9b1064a791 100644 --- a/core/src/mindustry/game/Universe.java +++ b/core/src/mindustry/game/Universe.java @@ -6,6 +6,8 @@ import arc.struct.*; import arc.util.*; import mindustry.content.*; import mindustry.game.EventType.*; +import mindustry.io.legacy.*; +import mindustry.maps.*; import mindustry.type.*; import mindustry.world.blocks.storage.*; @@ -17,6 +19,7 @@ public class Universe{ private int netSeconds; private float secondCounter; private int turn; + private float turnCounter; private Schematic lastLoadout; private ItemSeq lastLaunchResources = new ItemSeq(); @@ -53,17 +56,19 @@ public class Universe{ } } - /** @return sectors attacked on the current planet, minus the ones that are being played on right now. */ - public Seq getAttacked(Planet planet){ - return planet.sectors.select(s -> s.hasWaves() && s.hasBase() && !s.isBeingPlayed() && s.getSecondsPassed() > 1); - } - /** Update planet rotations, global time and relevant state. */ public void update(){ //only update time when not in multiplayer if(!net.client()){ secondCounter += Time.delta / 60f; + turnCounter += Time.delta; + + //auto-run turns + if(turnCounter >= turnDuration){ + turnCounter = 0; + runTurn(); + } if(secondCounter >= 1){ seconds += (int)secondCounter; @@ -132,42 +137,81 @@ public class Universe{ //update relevant sectors for(Planet planet : content.planets()){ for(Sector sector : planet.sectors){ - if(sector.hasSave()){ - int spent = (int)(sector.getTimeSpent() / 60); - int actuallyPassed = Math.max(newSecondsPassed - spent, 0); + if(sector.hasSave() && sector.hasBase()){ //increment seconds passed for this sector by the time that just passed with this turn if(!sector.isBeingPlayed()){ - sector.setSecondsPassed(sector.getSecondsPassed() + actuallyPassed); + //increment time + sector.info.secondsPassed += turnDuration/60f; + + int wavesPassed = (int)(sector.info.secondsPassed*60f / sector.info.waveSpacing); + boolean attacked = sector.info.waves; + + if(attacked){ + sector.info.wavesPassed = wavesPassed; + } + + float damage = attacked ? SectorDamage.getDamage(sector.info) : 0f; + + //damage never goes down until the player visits the sector, so use max + sector.info.damage = Math.max(sector.info.damage, damage); - //TODO sector damage disabled for now //check if the sector has been attacked too many times... - /*if(sector.hasBase() && sector.hasWaves() && sector.getSecondsPassed() * 60f > turnDuration * sectorDestructionTurns){ + if(attacked && damage >= 0.999f){ //fire event for losing the sector Events.fire(new SectorLoseEvent(sector)); - //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; - }*/ + //sector is dead. + sector.info.items.clear(); + sector.info.damage = 1f; + sector.info.hasCore = false; + sector.info.production.clear(); + }else if(attacked && wavesPassed > 0 && sector.info.winWave > 1 && sector.info.wave + wavesPassed >= sector.info.winWave && !sector.hasEnemyBase()){ + //autocapture the sector + sector.info.waves = false; + + //fire the event + Events.fire(new SectorCaptureEvent(sector)); + } + + float scl = sector.getProductionScale(); + + //export to another sector + if(sector.info.destination != null){ + Sector to = sector.info.destination; + if(to.hasBase()){ + ItemSeq items = new ItemSeq(); + //calculated exported items to this sector + sector.info.export.each((item, stat) -> items.add(item, (int)(stat.mean * newSecondsPassed * scl))); + to.addItems(items); + } + } + + //add production, making sure that it's capped + sector.info.production.each((item, stat) -> sector.info.items.add(item, Math.min((int)(stat.mean * newSecondsPassed * scl), sector.info.storageCapacity - sector.info.items.get(item)))); + + sector.saveInfo(); } - //export to another sector - if(sector.save != null && sector.save.meta != null && sector.save.meta.secinfo != null && sector.save.meta.secinfo.destination != null){ - Sector to = sector.save.meta.secinfo.destination; - if(to.save != null){ - ItemSeq items = new ItemSeq(); - //calculated exported items to this sector - sector.save.meta.secinfo.export.each((item, stat) -> items.add(item, (int)(stat.mean * newSecondsPassed))); - to.addItems(items); + //queue random invasions + if(!sector.isAttacked() && turn > invasionGracePeriod){ + //invasion chance depends on # of nearby bases + if(Mathf.chance(baseInvasionChance * sector.near().count(Sector::hasEnemyBase))){ + int waveMax = Math.max(sector.info.winWave, sector.isBeingPlayed() ? state.wave : 0) + Mathf.random(2, 5) * 5; + + //assign invasion-related things + if(sector.isBeingPlayed()){ + state.rules.winWave = waveMax; + state.rules.waves = true; + }else{ + sector.info.winWave = waveMax; + sector.info.waves = true; + sector.saveInfo(); + } + + Events.fire(new SectorInvasionEvent(sector)); } } - - //reset time spent to 0 - sector.setTimeSpent(0f); } } } @@ -184,7 +228,7 @@ public class Universe{ for(Planet planet : content.planets()){ for(Sector sector : planet.sectors){ if(sector.hasSave()){ - count.add(sector.calculateItems()); + count.add(sector.items()); } } } @@ -217,6 +261,11 @@ public class Universe{ private void load(){ seconds = Core.settings.getInt("utimei"); turn = Core.settings.getInt("turn"); + + if(Core.settings.has("unlocks")){ + LegacyIO.readResearch(); + Core.settings.remove("unlocks"); + } } } diff --git a/core/src/mindustry/graphics/BlockRenderer.java b/core/src/mindustry/graphics/BlockRenderer.java index a36230f0aa..afe2e3182b 100644 --- a/core/src/mindustry/graphics/BlockRenderer.java +++ b/core/src/mindustry/graphics/BlockRenderer.java @@ -20,11 +20,14 @@ import static arc.Core.*; import static mindustry.Vars.*; public class BlockRenderer implements Disposable{ + public static final int crackRegions = 8, maxCrackSize = 9; + private static final int initialRequests = 32 * 32; private static final int expandr = 9; private static final Color shadowColor = new Color(0, 0, 0, 0.71f); public final FloorRenderer floor = new FloorRenderer(); + public TextureRegion[][] cracks; private Seq tileview = new Seq<>(false, initialRequests, Tile.class); private Seq lightview = new Seq<>(false, initialRequests, Tile.class); @@ -35,11 +38,20 @@ public class BlockRenderer implements Disposable{ private FrameBuffer dark = new FrameBuffer(); private Seq outArray2 = new Seq<>(); private Seq shadowEvents = new Seq<>(); - private IntSet processedEntities = new IntSet(), processedLinks = new IntSet(); + private IntSet procEntities = new IntSet(), procLinks = new IntSet(), procLights = new IntSet(); private boolean displayStatus = false; public BlockRenderer(){ + Events.on(ClientLoadEvent.class, e -> { + cracks = new TextureRegion[maxCrackSize][crackRegions]; + for(int size = 1; size <= maxCrackSize; size++){ + for(int i = 0; i < crackRegions; i++){ + cracks[size - 1][i] = Core.atlas.find("cracks-" + size + "-" + i); + } + } + }); + Events.on(WorldLoadEvent.class, event -> { shadowEvents.clear(); lastCamY = lastCamX = -99; //invalidate camera position so blocks get updated @@ -179,8 +191,9 @@ public class BlockRenderer implements Disposable{ tileview.clear(); lightview.clear(); - processedEntities.clear(); - processedLinks.clear(); + procEntities.clear(); + procLinks.clear(); + procLights.clear(); int minx = Math.max(avgx - rangex - expandr, 0); int miny = Math.max(avgy - rangey - expandr, 0); @@ -197,25 +210,25 @@ public class BlockRenderer implements Disposable{ tile = tile.build.tile; } - if(block != Blocks.air && block.cacheLayer == CacheLayer.normal && (tile.build == null || !processedEntities.contains(tile.build.id))){ + if(block != Blocks.air && block.cacheLayer == CacheLayer.normal && (tile.build == null || !procEntities.contains(tile.build.id))){ if(block.expanded || !expanded){ - if(tile.build == null || processedLinks.add(tile.build.id)){ + if(tile.build == null || procLinks.add(tile.build.id)){ tileview.add(tile); if(tile.build != null){ - processedEntities.add(tile.build.id); - processedLinks.add(tile.build.id); + procEntities.add(tile.build.id); + procLinks.add(tile.build.id); } } } //lights are drawn even in the expanded range - if(tile.build != null || tile.block().emitLight){ + if(((tile.build != null && procLights.add(tile.build.pos())) || tile.block().emitLight)){ lightview.add(tile); } if(tile.build != null && tile.build.power != null && tile.build.power.links.size > 0){ for(Building other : tile.build.getPowerConnections(outArray2)){ - if(other.block instanceof PowerNode && processedLinks.add(other.id)){ //TODO need a generic way to render connections! + if(other.block instanceof PowerNode && procLinks.add(other.id)){ //TODO need a generic way to render connections! tileview.add(other.tile); } } @@ -223,7 +236,7 @@ public class BlockRenderer implements Disposable{ } //special case for floors - if(block == Blocks.air && tile.floor().emitLight){ + if((block == Blocks.air && tile.floor().emitLight) && procLights.add(tile.pos())){ lightview.add(tile); } } diff --git a/core/src/mindustry/graphics/LightRenderer.java b/core/src/mindustry/graphics/LightRenderer.java index 10ffa727b6..0a05c7409b 100644 --- a/core/src/mindustry/graphics/LightRenderer.java +++ b/core/src/mindustry/graphics/LightRenderer.java @@ -100,7 +100,6 @@ public class LightRenderer{ Draw.vert(ledge.texture, vertices, 0, vertices.length); - Vec2 v3 = Tmp.v2.trnsExact(rot, stroke); u = ledge.u; diff --git a/core/src/mindustry/graphics/MinimapRenderer.java b/core/src/mindustry/graphics/MinimapRenderer.java index cdb84e9044..1c34b4424e 100644 --- a/core/src/mindustry/graphics/MinimapRenderer.java +++ b/core/src/mindustry/graphics/MinimapRenderer.java @@ -96,7 +96,7 @@ public class MinimapRenderer implements Disposable{ Draw.mixcol(unit.team().color, 1f); float scale = Scl.scl(1f) / 2f * scaling * 32f; - Draw.rect(unit.type().icon(Cicon.full), x + rx, y + ry, scale, scale, unit.rotation() - 90); + Draw.rect(unit.type.icon(Cicon.full), x + rx, y + ry, scale, scale, unit.rotation() - 90); Draw.reset(); //only disable player names in multiplayer diff --git a/core/src/mindustry/graphics/OverlayRenderer.java b/core/src/mindustry/graphics/OverlayRenderer.java index 19569ed1a3..9bb3a95fa3 100644 --- a/core/src/mindustry/graphics/OverlayRenderer.java +++ b/core/src/mindustry/graphics/OverlayRenderer.java @@ -85,7 +85,7 @@ public class OverlayRenderer{ //special selection for block "units" Fill.square(select.x, select.y, ((BlockUnitc)select).tile().block.size * tilesize/2f); }else{ - Draw.rect(select.type().icon(Cicon.full), select.x(), select.y(), select.rotation() - 90); + Draw.rect(select.type.icon(Cicon.full), select.x(), select.y(), select.rotation() - 90); } Lines.stroke(unitFade); @@ -121,10 +121,12 @@ public class OverlayRenderer{ Lines.stroke(2f); Draw.color(Color.gray, Color.lightGray, Mathf.absin(Time.time(), 8f, 1f)); - for(Tile tile : spawner.getSpawns()){ - if(tile.within(player.x, player.y, state.rules.dropZoneRadius + spawnerMargin)){ - Draw.alpha(Mathf.clamp(1f - (player.dst(tile) - state.rules.dropZoneRadius) / spawnerMargin)); - Lines.dashCircle(tile.worldx(), tile.worldy(), state.rules.dropZoneRadius); + if(state.hasSpawns()){ + for(Tile tile : spawner.getSpawns()){ + if(tile.within(player.x, player.y, state.rules.dropZoneRadius + spawnerMargin)){ + Draw.alpha(Mathf.clamp(1f - (player.dst(tile) - state.rules.dropZoneRadius) / spawnerMargin)); + Lines.dashCircle(tile.worldx(), tile.worldy(), state.rules.dropZoneRadius); + } } } diff --git a/core/src/mindustry/graphics/Pal.java b/core/src/mindustry/graphics/Pal.java index 753bbbbbed..8bb3cd9e70 100644 --- a/core/src/mindustry/graphics/Pal.java +++ b/core/src/mindustry/graphics/Pal.java @@ -94,5 +94,7 @@ public class Pal{ redDust = Color.valueOf("ffa480"), redderDust = Color.valueOf("ff7b69"), - plasticSmoke = Color.valueOf("f1e479"); + plasticSmoke = Color.valueOf("f1e479"), + + adminChat = Color.valueOf("ff4000"); } diff --git a/core/src/mindustry/graphics/g3d/PlanetGrid.java b/core/src/mindustry/graphics/g3d/PlanetGrid.java index e4972e3eb7..380f937658 100644 --- a/core/src/mindustry/graphics/g3d/PlanetGrid.java +++ b/core/src/mindustry/graphics/g3d/PlanetGrid.java @@ -22,15 +22,16 @@ public class PlanetGrid{ {5, 3, 10, 1, 4}, {2, 5, 4, 0, 11}, {3, 7, 6, 1, 8}, {7, 2, 9, 0, 6} }; - public final int size; - public final Ptile[] tiles; - public final Corner[] corners; - public final Edge[] edges; + public int size; + public Ptile[] tiles; + public Corner[] corners; + public Edge[] edges; - PlanetGrid(int size){ + //this is protected so if you want to make strange grids you should know what you're doing. + protected PlanetGrid(int size){ this.size = size; - tiles = new Ptile[Buildingount(size)]; + tiles = new Ptile[tileCount(size)]; for(int i = 0; i < tiles.length; i++){ tiles[i] = new Ptile(i, i < 12 ? 5 : 6); } @@ -67,7 +68,7 @@ public class PlanetGrid{ return result; } - static PlanetGrid initialGrid(){ + public static PlanetGrid initialGrid(){ PlanetGrid grid = new PlanetGrid(0); for(Ptile t : grid.tiles){ @@ -111,7 +112,7 @@ public class PlanetGrid{ return grid; } - static PlanetGrid subdividedGrid(PlanetGrid prev){ + public static PlanetGrid subdividedGrid(PlanetGrid prev){ PlanetGrid grid = new PlanetGrid(prev.size + 1); int prevTiles = prev.tiles.length; @@ -207,7 +208,7 @@ public class PlanetGrid{ return -1; } - static int Buildingount(int size){ + static int tileCount(int size){ return 10 * Mathf.pow(3, size) + 2; } @@ -220,12 +221,12 @@ public class PlanetGrid{ } public static class Ptile{ - public final int id; - public final int edgeCount; + public int id; + public int edgeCount; - public final Ptile[] tiles; - public final Corner[] corners; - public final Edge[] edges; + public Ptile[] tiles; + public Corner[] corners; + public Edge[] edges; public Vec3 v = new Vec3(); @@ -240,11 +241,11 @@ public class PlanetGrid{ } public static class Corner{ - public final int id; - public final Ptile[] tiles = new Ptile[3]; - public final Corner[] corners = new Corner[3]; - public final Edge[] edges = new Edge[3]; - public final Vec3 v = new Vec3(); + public int id; + public Ptile[] tiles = new Ptile[3]; + public Corner[] corners = new Corner[3]; + public Edge[] edges = new Edge[3]; + public Vec3 v = new Vec3(); public Corner(int id){ this.id = id; @@ -252,9 +253,9 @@ public class PlanetGrid{ } public static class Edge{ - public final int id; - public final Ptile[] tiles = new Ptile[2]; - public final Corner[] corners = new Corner[2]; + public int id; + public Ptile[] tiles = new Ptile[2]; + public Corner[] corners = new Corner[2]; public Edge(int id){ this.id = id; diff --git a/core/src/mindustry/graphics/g3d/PlanetRenderer.java b/core/src/mindustry/graphics/g3d/PlanetRenderer.java index e3eb74a7b5..405d668333 100644 --- a/core/src/mindustry/graphics/g3d/PlanetRenderer.java +++ b/core/src/mindustry/graphics/g3d/PlanetRenderer.java @@ -10,6 +10,7 @@ import arc.math.geom.*; import arc.struct.*; import arc.util.*; import mindustry.content.*; +import mindustry.game.EventType.*; import mindustry.graphics.*; import mindustry.graphics.g3d.PlanetGrid.*; import mindustry.type.*; @@ -38,19 +39,19 @@ public class PlanetRenderer implements Disposable{ public float zoom = 1f; private final Mesh[] outlines = new Mesh[10]; - private final PlaneBatch3D projector = new PlaneBatch3D(); - private final Mat3D mat = new Mat3D(); - private final FrameBuffer buffer = new FrameBuffer(2, 2, true); - private PlanetInterfaceRenderer irenderer; + public final PlaneBatch3D projector = new PlaneBatch3D(); + public final Mat3D mat = new Mat3D(); + public final FrameBuffer buffer = new FrameBuffer(2, 2, true); + public PlanetInterfaceRenderer irenderer; - private final Bloom bloom = new Bloom(Core.graphics.getWidth()/4, Core.graphics.getHeight()/4, true, false){{ + public final Bloom bloom = new Bloom(Core.graphics.getWidth()/4, Core.graphics.getHeight()/4, true, false){{ setThreshold(0.8f); blurPasses = 6; }}; - private final Mesh atmosphere = MeshBuilder.buildHex(Color.white, 2, false, 1.5f); + public final Mesh atmosphere = MeshBuilder.buildHex(Color.white, 2, false, 1.5f); //seed: 8kmfuix03fw - private final CubemapMesh skybox = new CubemapMesh(new Cubemap("cubemaps/stars/")); + public final CubemapMesh skybox = new CubemapMesh(new Cubemap("cubemaps/stars/")); public PlanetRenderer(){ camPos.set(0, 0f, camLength); @@ -82,14 +83,20 @@ public class PlanetRenderer implements Disposable{ projector.proj(cam.combined); batch.proj(cam.combined); + Events.fire(Trigger.universeDrawBegin); + beginBloom(); skybox.render(cam.combined); + Events.fire(Trigger.universeDraw); + renderPlanet(solarSystem); endBloom(); + Events.fire(Trigger.universeDrawEnd); + Gl.enable(Gl.blend); irenderer.renderProjections(); @@ -100,18 +107,21 @@ public class PlanetRenderer implements Disposable{ cam.update(); } - private void beginBloom(){ + public void beginBloom(){ bloom.resize(Core.graphics.getWidth() / 4, Core.graphics.getHeight() / 4); bloom.capture(); } - private void endBloom(){ + public void endBloom(){ bloom.render(); } - private void renderPlanet(Planet planet){ + + public void renderPlanet(Planet planet){ + if(!planet.visible()) return; + //render planet at offsetted position in the world - planet.mesh.render(cam.combined, planet.getTransform(mat)); + planet.draw(cam.combined, planet.getTransform(mat)); renderOrbit(planet); @@ -137,8 +147,8 @@ public class PlanetRenderer implements Disposable{ } } - private void renderOrbit(Planet planet){ - if(planet.parent == null) return; + public void renderOrbit(Planet planet){ + if(planet.parent == null || !planet.visible()) return; Vec3 center = planet.parent.position; float radius = planet.orbitRadius; @@ -147,7 +157,7 @@ public class PlanetRenderer implements Disposable{ batch.flush(Gl.lineLoop); } - private void renderSectors(Planet planet){ + public void renderSectors(Planet planet){ //apply transformed position batch.proj().mul(planet.getTransform(mat)); @@ -268,7 +278,7 @@ public class PlanetRenderer implements Disposable{ } } - private Mesh outline(int size){ + public Mesh outline(int size){ if(outlines[size] == null){ outlines[size] = MeshBuilder.buildHex(new HexMesher(){ @Override diff --git a/core/src/mindustry/input/DesktopInput.java b/core/src/mindustry/input/DesktopInput.java index 7099f254dd..906d96d56e 100644 --- a/core/src/mindustry/input/DesktopInput.java +++ b/core/src/mindustry/input/DesktopInput.java @@ -12,6 +12,7 @@ import arc.scene.ui.*; import arc.scene.ui.layout.*; import arc.util.*; import mindustry.*; +import mindustry.core.*; import mindustry.entities.units.*; import mindustry.game.EventType.*; import mindustry.game.*; @@ -352,9 +353,7 @@ public class DesktopInput extends InputHandler{ ui.planet.show(); }).visible(() -> state.isCampaign()).tooltip("@planetmap"); - table.button(Icon.up, Styles.clearPartiali, () -> { - ui.planet.showLaunch(state.getSector(), player.team().core()); - }).visible(() -> state.isCampaign()).tooltip("@launchcore").disabled(b -> player.team().core() == null); + table.add(); } void pollInput(){ @@ -363,7 +362,7 @@ public class DesktopInput extends InputHandler{ Tile selected = tileAt(Core.input.mouseX(), Core.input.mouseY()); int cursorX = tileX(Core.input.mouseX()); int cursorY = tileY(Core.input.mouseY()); - int rawCursorX = world.toTile(Core.input.mouseWorld().x), rawCursorY = world.toTile(Core.input.mouseWorld().y); + int rawCursorX = World.toTile(Core.input.mouseWorld().x), rawCursorY = World.toTile(Core.input.mouseWorld().y); // automatically pause building if the current build queue is empty if(Core.settings.getBool("buildautopause") && isBuilding && !player.builder().isBuilding()){ @@ -599,11 +598,11 @@ public class DesktopInput extends InputHandler{ } protected void updateMovement(Unit unit){ - boolean omni = unit.type().omniMovement; + 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); - float baseSpeed = unit.type().speed; + float strafePenalty = ground ? 1f : Mathf.lerp(1f, unit.type.strafePenalty, Angles.angleDist(unit.vel().angle(), unit.rotation()) / 180f); + float baseSpeed = unit.type.speed; //limit speed to minimum formation speed to preserve formation if(unit.isCommanding()){ @@ -611,7 +610,7 @@ public class DesktopInput extends InputHandler{ baseSpeed = unit.minFormationSpeed * 0.95f; } - float speed = baseSpeed * Mathf.lerp(1f, unit.isCommanding() ? 1f : unit.type().canBoost ? unit.type().boostMultiplier : 1f, unit.elevation) * strafePenalty; + float speed = baseSpeed * Mathf.lerp(1f, unit.isCommanding() ? 1f : unit.type.canBoost ? unit.type.boostMultiplier : 1f, unit.elevation) * strafePenalty; float xa = Core.input.axis(Binding.move_x); float ya = Core.input.axis(Binding.move_y); boolean boosted = (unit instanceof Mechc && unit.isFlying()); @@ -622,7 +621,7 @@ public class DesktopInput extends InputHandler{ } float mouseAngle = Angles.mouseAngle(unit.x, unit.y); - boolean aimCursor = omni && player.shooting && unit.type().hasWeapons() && unit.type().faceTarget && !boosted && unit.type().rotateShooting; + boolean aimCursor = omni && player.shooting && unit.type.hasWeapons() && unit.type.faceTarget && !boosted && unit.type.rotateShooting; if(aimCursor){ unit.lookAt(mouseAngle); @@ -637,11 +636,11 @@ public class DesktopInput extends InputHandler{ }else{ unit.moveAt(Tmp.v2.trns(unit.rotation, movement.len())); if(!movement.isZero() && ground){ - unit.vel.rotateTo(movement.angle(), unit.type().rotateSpeed); + unit.vel.rotateTo(movement.angle(), unit.type.rotateSpeed); } } - unit.aim(unit.type().faceTarget ? Core.input.mouseWorld() : Tmp.v1.trns(unit.rotation, Core.input.mouseWorld().dst(unit)).add(unit.x, unit.y)); + unit.aim(unit.type.faceTarget ? Core.input.mouseWorld() : Tmp.v1.trns(unit.rotation, Core.input.mouseWorld().dst(unit)).add(unit.x, unit.y)); unit.controlWeapons(true, player.shooting && !boosted); player.boosting = Core.input.keyDown(Binding.boost) && !movement.isZero(); @@ -660,7 +659,7 @@ public class DesktopInput extends InputHandler{ } } - //update commander inut + //update commander unit if(Core.input.keyTap(Binding.command)){ Call.unitCommand(player); } diff --git a/core/src/mindustry/input/InputHandler.java b/core/src/mindustry/input/InputHandler.java index eebe40e15b..99103da3d7 100644 --- a/core/src/mindustry/input/InputHandler.java +++ b/core/src/mindustry/input/InputHandler.java @@ -16,6 +16,7 @@ import arc.util.*; import mindustry.ai.formations.patterns.*; import mindustry.annotations.Annotations.*; import mindustry.content.*; +import mindustry.core.*; import mindustry.entities.*; import mindustry.entities.units.*; import mindustry.game.EventType.*; @@ -158,7 +159,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ Payloadc pay = (Payloadc)unit; if(target.isAI() && target.isGrounded() && pay.canPickup(target) - && target.within(unit, unit.type().hitSize * 2f + target.type().hitSize * 2f)){ + && target.within(unit, unit.type.hitSize * 2f + target.type.hitSize * 2f)){ Call.pickedUnitPayload(unit, target); } } @@ -240,6 +241,11 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ pay.set(x, y); pay.dropLastPayload(); pay.set(prevx, prevy); + pay.controlling().each(u -> { + if(u instanceof Payloadc){ + Call.payloadDropped(u, u.x, u.y); + } + }); } } @@ -365,7 +371,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ if(commander.isCommanding()){ commander.clearCommand(); - }else if(player.unit().type().commandLimit > 0){ + }else if(player.unit().type.commandLimit > 0){ //TODO try out some other formations commander.commandNearby(new CircleFormation()); @@ -398,17 +404,17 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ } if(player.shooting && !wasShooting && player.unit().hasWeapons() && state.rules.unitAmmo && player.unit().ammo <= 0){ - player.unit().type().weapons.first().noAmmoSound.at(player.unit()); + player.unit().type.weapons.first().noAmmoSound.at(player.unit()); } wasShooting = player.shooting; if(!player.dead()){ - controlledType = player.unit().type(); + controlledType = player.unit().type; } if(controlledType != null && player.dead()){ - Unit unit = Units.closest(player.team(), player.x, player.y, u -> !u.isPlayer() && u.type() == controlledType && !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); @@ -418,9 +424,9 @@ 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.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; + unit = world.buildWorld(player.x, player.y) instanceof ControlBlock cont && cont.canControl() ? cont.unit() : null; } if(unit != null){ @@ -437,7 +443,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ Unit unit = player.unit(); if(!(unit instanceof Payloadc pay)) return; - Unit target = Units.closest(player.team(), pay.x(), pay.y(), unit.type().hitSize * 2.5f, u -> u.isAI() && u.isGrounded() && pay.canPickup(u) && u.within(unit, u.hitSize + unit.hitSize * 1.2f)); + Unit target = Units.closest(player.team(), pay.x(), pay.y(), unit.type.hitSize * 2.5f, u -> u.isAI() && u.isGrounded() && pay.canPickup(u) && u.within(unit, u.hitSize + unit.hitSize * 1.2f)); if(target != null){ Call.requestUnitPayload(player, target); }else{ @@ -568,8 +574,8 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ wx = wy; wy = -x; } - req.x = world.toTile(wx - req.block.offset) + ox; - req.y = world.toTile(wy - req.block.offset) + oy; + req.x = World.toTile(wx - req.block.offset) + ox; + req.y = World.toTile(wy - req.block.offset) + oy; req.rotation = Mathf.mod(req.rotation + direction, 4); }); } @@ -934,11 +940,11 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ } int rawTileX(){ - return world.toTile(Core.input.mouseWorld().x); + return World.toTile(Core.input.mouseWorld().x); } int rawTileY(){ - return world.toTile(Core.input.mouseWorld().y); + return World.toTile(Core.input.mouseWorld().y); } int tileX(float cursorX){ @@ -946,7 +952,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ if(selectedBlock()){ vec.sub(block.offset, block.offset); } - return world.toTile(vec.x); + return World.toTile(vec.x); } int tileY(float cursorY){ @@ -954,7 +960,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ if(selectedBlock()){ vec.sub(block.offset, block.offset); } - return world.toTile(vec.y); + return World.toTile(vec.y); } public boolean selectedBlock(){ @@ -984,8 +990,8 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ } Building tile = world.buildWorld(Core.input.mouseWorld().x, Core.input.mouseWorld().y); - if(tile instanceof ControlBlock && tile.team == player.team()){ - return ((ControlBlock)tile).unit(); + if(tile instanceof ControlBlock cont && cont.canControl() && tile.team == player.team()){ + return cont.unit(); } return null; diff --git a/core/src/mindustry/input/MobileInput.java b/core/src/mindustry/input/MobileInput.java index 19f458ea88..981f55bd6f 100644 --- a/core/src/mindustry/input/MobileInput.java +++ b/core/src/mindustry/input/MobileInput.java @@ -14,6 +14,7 @@ import arc.struct.*; import arc.util.*; import mindustry.*; import mindustry.content.*; +import mindustry.core.*; import mindustry.entities.*; import mindustry.entities.units.*; import mindustry.game.EventType.*; @@ -23,6 +24,7 @@ import mindustry.graphics.*; import mindustry.type.*; import mindustry.ui.*; import mindustry.world.*; +import mindustry.world.blocks.*; import static mindustry.Vars.*; import static mindustry.input.PlaceMode.*; @@ -85,7 +87,7 @@ public class MobileInput extends InputHandler implements GestureListener{ if(tile != null && player.team().isEnemy(tile.team)){ player.miner().mineTile(null); target = tile; - }else if(tile != null && player.unit().type().canHeal && tile.team == player.team() && tile.damaged()){ + }else if(tile != null && player.unit().type.canHeal && tile.team == player.team() && tile.damaged()){ player.miner().mineTile(null); target = tile; } @@ -405,14 +407,14 @@ public class MobileInput extends InputHandler implements GestureListener{ protected int schemOriginX(){ Tmp.v1.setZero(); selectRequests.each(r -> Tmp.v1.add(r.drawx(), r.drawy())); - return world.toTile(Tmp.v1.scl(1f / selectRequests.size).x); + return World.toTile(Tmp.v1.scl(1f / selectRequests.size).x); } @Override protected int schemOriginY(){ Tmp.v1.setZero(); selectRequests.each(r -> Tmp.v1.add(r.drawx(), r.drawy())); - return world.toTile(Tmp.v1.scl(1f / selectRequests.size).y); + return World.toTile(Tmp.v1.scl(1f / selectRequests.size).y); } @Override @@ -834,10 +836,10 @@ public class MobileInput extends InputHandler implements GestureListener{ protected void updateMovement(Unit unit){ Rect rect = Tmp.r3; - UnitType type = unit.type(); + UnitType type = unit.type; if(type == null) return; - boolean omni = unit.type().omniMovement; + 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 && @@ -855,7 +857,7 @@ public class MobileInput extends InputHandler implements GestureListener{ float attractDst = 15f; float strafePenalty = legs ? 1f : Mathf.lerp(1f, type.strafePenalty, Angles.angleDist(unit.vel.angle(), unit.rotation) / 180f); - float baseSpeed = unit.type().speed; + float baseSpeed = unit.type.speed; //limit speed to minimum formation speed to preserve formation if(unit.isCommanding()){ @@ -935,7 +937,7 @@ public class MobileInput extends InputHandler implements GestureListener{ unit.aim(player.mouseX = Core.input.mouseWorldX(), player.mouseY = Core.input.mouseWorldY()); }else if(target == null){ player.shooting = false; - if(Core.settings.getBool("autotarget")){ + if(Core.settings.getBool("autotarget") && !(player.unit() instanceof BlockUnitUnit u && u.tile() instanceof ControlBlock c && !c.shouldAutoTarget())){ target = Units.closestTarget(unit.team, unit.x, unit.y, range, u -> u.team != Team.derelict, u -> u.team != Team.derelict); if(allowHealing && target == null){ diff --git a/core/src/mindustry/io/JsonIO.java b/core/src/mindustry/io/JsonIO.java index 39c3a1a821..eb3815ba76 100644 --- a/core/src/mindustry/io/JsonIO.java +++ b/core/src/mindustry/io/JsonIO.java @@ -165,6 +165,18 @@ public class JsonIO{ } }); + json.setSerializer(UnitType.class, new Serializer<>(){ + @Override + public void write(Json json, UnitType object, Class knownType){ + json.writeValue(object.name); + } + + @Override + public UnitType read(Json json, JsonValue jsonData, Class type){ + return Vars.content.getByName(ContentType.unit, jsonData.asString()); + } + }); + json.setSerializer(ItemStack.class, new Serializer<>(){ @Override public void write(Json json, ItemStack object, Class knownType){ diff --git a/core/src/mindustry/io/SaveMeta.java b/core/src/mindustry/io/SaveMeta.java index f2e039f634..c6133889bc 100644 --- a/core/src/mindustry/io/SaveMeta.java +++ b/core/src/mindustry/io/SaveMeta.java @@ -14,12 +14,10 @@ public class SaveMeta{ public Map map; public int wave; public Rules rules; - public SectorInfo secinfo; public StringMap tags; public String[] mods; - public boolean hasProduction; - public SaveMeta(int version, long timestamp, long timePlayed, int build, String map, int wave, Rules rules, SectorInfo secinfo, StringMap tags){ + public SaveMeta(int version, long timestamp, long timePlayed, int build, String map, int wave, Rules rules, StringMap tags){ this.version = version; this.build = build; this.timestamp = timestamp; @@ -29,8 +27,5 @@ public class SaveMeta{ this.rules = rules; this.tags = tags; this.mods = JsonIO.read(String[].class, tags.get("mods", "[]")); - this.secinfo = secinfo; - - secinfo.production.each((e, amount) -> hasProduction |= amount.mean > 0.001f); } } diff --git a/core/src/mindustry/io/SaveVersion.java b/core/src/mindustry/io/SaveVersion.java index cacac7700a..b76a5156e8 100644 --- a/core/src/mindustry/io/SaveVersion.java +++ b/core/src/mindustry/io/SaveVersion.java @@ -40,7 +40,6 @@ public abstract class SaveVersion extends SaveFileReader{ map.get("mapname"), map.getInt("wave"), JsonIO.read(Rules.class, map.get("rules", "{}")), - JsonIO.read(SectorInfo.class, map.get("secinfo", "{}")), map ); } @@ -74,6 +73,7 @@ public abstract class SaveVersion extends SaveFileReader{ //prepare campaign data for writing if(state.isCampaign()){ state.secinfo.prepare(); + state.rules.sector.saveInfo(); } //flush tech node progress @@ -89,7 +89,6 @@ public abstract class SaveVersion extends SaveFileReader{ "wave", state.wave, "wavetime", state.wavetime, "stats", JsonIO.write(state.stats), - "secinfo", state.isCampaign() ? JsonIO.write(state.secinfo) : "{}", "rules", JsonIO.write(state.rules), "mods", JsonIO.write(mods.getModStrings().toArray(String.class)), "width", world.width(), @@ -106,15 +105,14 @@ public abstract class SaveVersion extends SaveFileReader{ state.wave = map.getInt("wave"); state.wavetime = map.getFloat("wavetime", state.rules.waveSpacing); - state.stats = JsonIO.read(Stats.class, map.get("stats", "{}")); - state.secinfo = JsonIO.read(SectorInfo.class, map.get("secinfo", "{}")); + state.stats = JsonIO.read(GameStats.class, map.get("stats", "{}")); state.rules = JsonIO.read(Rules.class, map.get("rules", "{}")); if(state.rules.spawns.isEmpty()) state.rules.spawns = defaultWaves.get(); lastReadBuild = map.getInt("build", -1); - //load time spent on sector into state + //load in sector info if(state.rules.sector != null){ - state.secinfo.internalTimeSpent = state.rules.sector.getStoredTimeSpent(); + state.secinfo = state.rules.sector.info; } if(!headless){ diff --git a/core/src/mindustry/io/TypeIO.java b/core/src/mindustry/io/TypeIO.java index 669cd65d53..2c2c19dcfc 100644 --- a/core/src/mindustry/io/TypeIO.java +++ b/core/src/mindustry/io/TypeIO.java @@ -185,7 +185,7 @@ public class TypeIO{ return unit == null ? Nulls.unit : unit; }else if(type == 1){ //block Building tile = world.build(id); - return tile instanceof ControlBlock ? ((ControlBlock)tile).unit() : Nulls.unit; + return tile instanceof ControlBlock cont ? cont.unit() : Nulls.unit; } return Nulls.unit; } @@ -450,6 +450,16 @@ public class TypeIO{ return color.set(read.i()); } + public static void writeContent(Writes write, Content cont){ + write.b(cont.getContentType().ordinal()); + write.s(cont.id); + } + + public static Content readContent(Reads read){ + byte id = read.b(); + return content.getByID(ContentType.all[id], read.s()); + } + public static void writeLiquid(Writes write, Liquid liquid){ write.s(liquid == null ? -1 : liquid.id); } diff --git a/core/src/mindustry/io/legacy/LegacyIO.java b/core/src/mindustry/io/legacy/LegacyIO.java index 545487d8ca..0bb84f3fc7 100644 --- a/core/src/mindustry/io/legacy/LegacyIO.java +++ b/core/src/mindustry/io/legacy/LegacyIO.java @@ -2,6 +2,8 @@ package mindustry.io.legacy; import arc.*; import arc.struct.*; +import mindustry.*; +import mindustry.ctype.*; import mindustry.ui.dialogs.JoinDialog.*; import java.io.*; @@ -48,4 +50,35 @@ public class LegacyIO{ return arr; } + public static void readResearch(){ + try{ + byte[] bytes = Core.settings.getBytes("unlocks"); + DataInputStream stream = new DataInputStream(new ByteArrayInputStream(bytes)); + + int length = stream.readInt(); + if(length > 0){ + stream.readUTF(); //name of key type + stream.readUTF(); //name of value type + + //each element is an array list + for(int i = 0; i < length; i++){ + ContentType type = ContentType.all[stream.readInt()]; + int arrLength = stream.readInt(); + if(arrLength > 0){ + stream.readUTF(); //type of contents (String) + for(int j = 0; j < arrLength; j++){ + String name = stream.readUTF(); + Content out = Vars.content.getByName(type, name); + if(out instanceof UnlockableContent u){ + u.unlock(); + } + } + } + } + } + }catch(Exception e){ + e.printStackTrace(); + } + } + } diff --git a/core/src/mindustry/logic/Controllable.java b/core/src/mindustry/logic/Controllable.java index 3785eae4b6..025559b4b9 100644 --- a/core/src/mindustry/logic/Controllable.java +++ b/core/src/mindustry/logic/Controllable.java @@ -1,7 +1,10 @@ package mindustry.logic; +import mindustry.game.*; + /** An object that can be controlled with logic. */ public interface Controllable{ void control(LAccess type, double p1, double p2, double p3, double p4); void control(LAccess type, Object p1, double p2, double p3, double p4); + Team team(); } diff --git a/core/src/mindustry/logic/LAccess.java b/core/src/mindustry/logic/LAccess.java index 5dc881a778..0d678554d1 100644 --- a/core/src/mindustry/logic/LAccess.java +++ b/core/src/mindustry/logic/LAccess.java @@ -15,6 +15,8 @@ public enum LAccess{ powerNetCapacity, powerNetIn, powerNetOut, + ammo, + ammoCapacity, health, maxHealth, heat, diff --git a/core/src/mindustry/logic/LAssembler.java b/core/src/mindustry/logic/LAssembler.java index 4f72d121aa..57c2004320 100644 --- a/core/src/mindustry/logic/LAssembler.java +++ b/core/src/mindustry/logic/LAssembler.java @@ -189,16 +189,28 @@ public class LAssembler{ return putConst("___" + symbol, symbol.substring(1, symbol.length() - 1).replace("\\n", "\n")).id; } + //remove spaces for non-strings + symbol = symbol.replace(' ', '_'); + try{ - double value = Double.parseDouble(symbol); + double value = parseDouble(symbol); + if(Double.isNaN(value) || Double.isInfinite(value)) value = 0; + //this creates a hidden const variable with the specified value - String key = "___" + value; - return putConst(key, value).id; + return putConst("___" + value, value).id; }catch(NumberFormatException e){ return putVar(symbol).id; } } + double parseDouble(String symbol) throws NumberFormatException{ + //parse hex/binary syntax + if(symbol.startsWith("0b")) return Long.parseLong(symbol.substring(2), 2); + if(symbol.startsWith("0x")) return Long.parseLong(symbol.substring(2), 16); + + return Double.parseDouble(symbol); + } + /** Adds a constant value by name. */ public BVar putConst(String name, Object value){ BVar var = putVar(name); diff --git a/core/src/mindustry/logic/LCanvas.java b/core/src/mindustry/logic/LCanvas.java index ed98bee8ff..e16aab6621 100644 --- a/core/src/mindustry/logic/LCanvas.java +++ b/core/src/mindustry/logic/LCanvas.java @@ -305,7 +305,7 @@ public class LCanvas extends Table{ statements.finishLayout(); } }); - }).growX(); + }).growX().height(38); row(); diff --git a/core/src/mindustry/logic/LExecutor.java b/core/src/mindustry/logic/LExecutor.java index a8c5de8a9d..6b4f9ced0a 100644 --- a/core/src/mindustry/logic/LExecutor.java +++ b/core/src/mindustry/logic/LExecutor.java @@ -7,6 +7,7 @@ import arc.util.noise.*; import mindustry.*; import mindustry.ai.types.*; import mindustry.content.*; +import mindustry.core.*; import mindustry.ctype.*; import mindustry.entities.*; import mindustry.game.*; @@ -46,6 +47,7 @@ public class LExecutor{ public LongSeq graphicsBuffer = new LongSeq(); public StringBuilder textBuffer = new StringBuilder(); public Building[] links = {}; + public IntSet linkIds = new IntSet(); public Team team = Team.derelict; public boolean initialized(){ @@ -110,12 +112,12 @@ public class LExecutor{ public double num(int index){ Var v = vars[index]; - return v.isobj ? v.objval != null ? 1 : 0 : v.numval; + return v.isobj ? v.objval != null ? 1 : 0 : Double.isNaN(v.numval) || Double.isInfinite(v.numval) ? 0 : v.numval; } public float numf(int index){ Var v = vars[index]; - return v.isobj ? v.objval != null ? 1 : 0 : (float)v.numval; + return v.isobj ? v.objval != null ? 1 : 0 : Double.isNaN(v.numval) || Double.isInfinite(v.numval) ? 0 : (float)v.numval; } public int numi(int index){ @@ -129,7 +131,7 @@ public class LExecutor{ public void setnum(int index, double value){ Var v = vars[index]; if(v.constant) return; - v.numval = value; + v.numval = Double.isNaN(value) || Double.isInfinite(value) ? 0 : value; v.objval = null; v.isobj = false; } @@ -211,9 +213,9 @@ public class LExecutor{ public LLocate locate = LLocate.building; public BlockFlag flag = BlockFlag.core; public int enemy, ore; - public int outX, outY, outFound; + public int outX, outY, outFound, outBuild; - public UnitLocateI(LLocate locate, BlockFlag flag, int enemy, int ore, int outX, int outY, int outFound){ + public UnitLocateI(LLocate locate, BlockFlag flag, int enemy, int ore, int outX, int outY, int outFound, int outBuild){ this.locate = locate; this.flag = flag; this.enemy = enemy; @@ -263,13 +265,14 @@ public class LExecutor{ if(res != null && (!build || res.build != null)){ cache.found = true; //set result if found - exec.setnum(outX, cache.x = build ? res.build.x : res.worldx()); - exec.setnum(outY, cache.y = build ? res.build.y : res.worldy()); + exec.setnum(outX, cache.x = World.conv(build ? res.build.x : res.worldx())); + exec.setnum(outY, cache.y = World.conv(build ? res.build.y : res.worldy())); exec.setnum(outFound, 1); }else{ cache.found = false; exec.setnum(outFound, 0); } + exec.setobj(outFound, res != null && res.build != null && res.build.team == exec.team ? res.build : null); }else{ exec.setbool(outFound, cache.found); exec.setnum(outX, cache.x); @@ -332,14 +335,15 @@ public class LExecutor{ //only control standard AI units if(unitObj instanceof Unit unit && ai != null){ ai.controlTimer = LogicAI.logicControlTimeout; + float x1 = World.unconv(exec.numf(p1)), y1 = World.unconv(exec.numf(p2)), d1 = World.unconv(exec.numf(p3)); switch(type){ case move, stop, approach -> { ai.control = type; - ai.moveX = exec.numf(p1); - ai.moveY = exec.numf(p2); + ai.moveX = x1; + ai.moveY = y1; if(type == LUnitControl.approach){ - ai.moveRad = exec.numf(p3); + ai.moveRad = d1; } //stop mining/building @@ -353,13 +357,13 @@ public class LExecutor{ } } case within -> { - exec.setnum(p4, unit.within(exec.numf(p1), exec.numf(p2), exec.numf(p3)) ? 1 : 0); + exec.setnum(p4, unit.within(x1, y1, d1) ? 1 : 0); } case pathfind -> { ai.control = type; } case target -> { - ai.posTarget.set(exec.numf(p1), exec.numf(p2)); + ai.posTarget.set(x1, y1); ai.aimControl = type; ai.mainTarget = null; ai.shoot = exec.bool(p3); @@ -376,7 +380,7 @@ public class LExecutor{ unit.flag = exec.num(p1); } case mine -> { - Tile tile = world.tileWorld(exec.numf(p1), exec.numf(p2)); + Tile tile = world.tileWorld(x1, y1); if(unit instanceof Minerc miner){ miner.mineTile(miner.validMine(tile) ? tile : null); } @@ -395,7 +399,7 @@ public class LExecutor{ if(unit instanceof Payloadc pay){ //units if(exec.bool(p1)){ - Unit result = Units.closest(unit.team, unit.x, unit.y, unit.type().hitSize * 2f, u -> u.isAI() && u.isGrounded() && pay.canPickup(u) && u.within(unit, u.hitSize + unit.hitSize * 1.2f)); + Unit result = Units.closest(unit.team, unit.x, unit.y, unit.type.hitSize * 2f, u -> u.isAI() && u.isGrounded() && pay.canPickup(u) && u.within(unit, u.hitSize + unit.hitSize * 1.2f)); if(result != null){ Call.pickedUnitPayload(unit, result); @@ -420,7 +424,7 @@ public class LExecutor{ } case build -> { if(unit instanceof Builderc builder && exec.obj(p3) instanceof Block block){ - int x = world.toTile(exec.numf(p1)), y = world.toTile(exec.numf(p2)); + int x = World.toTile(x1), y = World.toTile(y1); int rot = exec.numi(p4); //reset state of last request when necessary @@ -441,17 +445,16 @@ public class LExecutor{ } } case getBlock -> { - float x = exec.numf(p1), y = exec.numf(p2); float range = Math.max(unit.range(), buildingRange); - if(!unit.within(x, y, range)){ + if(!unit.within(x1, y1, range)){ exec.setobj(p3, null); - exec.setnum(p4, 0); + exec.setobj(p4, null); }else{ - Tile tile = world.tileWorld(x, y); + Tile tile = world.tileWorld(x1, y1); //any environmental solid block is returned as StoneWall, aka "@solid" Block block = tile == null ? null : !tile.synthetic() ? (tile.solid() ? Blocks.stoneWall : Blocks.air) : tile.block(); exec.setobj(p3, block); - exec.setnum(p4, tile != null && tile.build != null ? tile.build.rotation : 0); + exec.setobj(p4, tile != null && tile.build != null ? tile.build : null); } } case itemDrop -> { @@ -509,11 +512,11 @@ public class LExecutor{ @Override public void run(LExecutor exec){ Object obj = exec.obj(target); - if(obj instanceof Controllable cont){ + if(obj instanceof Building b && b.team == exec.team && exec.linkIds.contains(b.id)){ if(type.isObj){ - cont.control(type, exec.obj(p1), exec.num(p2), exec.num(p3), exec.num(p4)); + b.control(type, exec.obj(p1), exec.num(p2), exec.num(p3), exec.num(p4)); }else{ - cont.control(type, exec.num(p1), exec.num(p2), exec.num(p3), exec.num(p4)); + b.control(type, exec.num(p1), exec.num(p2), exec.num(p3), exec.num(p4)); } } } @@ -606,6 +609,7 @@ public class LExecutor{ Object target = exec.obj(from); Object sense = exec.obj(type); + //TODO should remote enemy buildings be senseable? if(target instanceof Senseable se){ if(sense instanceof Content){ exec.setnum(to, se.sense(((Content)sense))); @@ -737,7 +741,7 @@ public class LExecutor{ v.objval = f.objval; v.isobj = true; }else{ - v.numval = f.numval; + v.numval = Double.isNaN(f.numval) || Double.isInfinite(f.numval) ? 0 : f.numval; v.isobj = false; } } diff --git a/core/src/mindustry/logic/LStatements.java b/core/src/mindustry/logic/LStatements.java index ba4d31e9a1..57adbdb7fe 100644 --- a/core/src/mindustry/logic/LStatements.java +++ b/core/src/mindustry/logic/LStatements.java @@ -819,7 +819,7 @@ public class LStatements{ public LLocate locate = LLocate.building; public BlockFlag flag = BlockFlag.core; public String enemy = "true", ore = "@copper"; - public String outX = "outx", outY = "outy", outFound = "found"; + public String outX = "outx", outY = "outy", outFound = "found", outBuild = "building"; @Override public void build(Table table){ @@ -905,6 +905,8 @@ public class LStatements{ table.add(" found ").left(); fields(table, outFound, str -> outFound = str); + table.add(" building ").left(); + fields(table, outBuild, str -> outBuild = str); } @@ -915,7 +917,7 @@ public class LStatements{ @Override public LInstruction build(LAssembler builder){ - return new UnitLocateI(locate, flag, builder.var(enemy), builder.var(ore), builder.var(outX), builder.var(outY), builder.var(outFound)); + return new UnitLocateI(locate, flag, builder.var(enemy), builder.var(ore), builder.var(outX), builder.var(outY), builder.var(outFound), builder.var(outBuild)); } } } diff --git a/core/src/mindustry/logic/LUnitControl.java b/core/src/mindustry/logic/LUnitControl.java index 2166b00690..b4c7f8254c 100644 --- a/core/src/mindustry/logic/LUnitControl.java +++ b/core/src/mindustry/logic/LUnitControl.java @@ -15,7 +15,7 @@ public enum LUnitControl{ mine("x", "y"), flag("value"), build("x", "y", "block", "rotation"), - getBlock("x", "y", "result", "resRot"), + getBlock("x", "y", "type", "building"), within("x", "y", "radius", "result"); public final String[] params; diff --git a/core/src/mindustry/maps/SectorDamage.java b/core/src/mindustry/maps/SectorDamage.java index 5f4e267d74..b084f75c10 100644 --- a/core/src/mindustry/maps/SectorDamage.java +++ b/core/src/mindustry/maps/SectorDamage.java @@ -3,11 +3,19 @@ package mindustry.maps; import arc.math.*; import arc.math.geom.*; import arc.struct.*; +import arc.util.*; import mindustry.ai.*; import mindustry.content.*; import mindustry.entities.*; +import mindustry.entities.abilities.*; +import mindustry.game.*; import mindustry.gen.*; +import mindustry.logic.*; +import mindustry.type.*; import mindustry.world.*; +import mindustry.world.blocks.defense.*; +import mindustry.world.blocks.defense.turrets.*; +import mindustry.world.blocks.defense.turrets.Turret.*; import mindustry.world.blocks.storage.*; import static mindustry.Vars.*; @@ -15,6 +23,300 @@ import static mindustry.Vars.*; public class SectorDamage{ //direct damage is for testing only private static final boolean direct = false, rubble = true; + private static final int maxWavesSimulated = 50; + + /** @return calculated capture progress of the enemy */ + public static float getDamage(SectorInfo info){ + float health = info.sumHealth; + int wavesPassed = info.wavesPassed; + int wave = info.wave; + float waveSpace = info.waveSpacing; + + //this approach is O(n), it simulates every wave passing. + //other approaches can assume all the waves come as one, but that's not as fair. + if(wavesPassed > 0){ + int waveBegin = wave; + int waveEnd = wave + wavesPassed; + + //do not simulate every single wave if there's too many + if(wavesPassed > maxWavesSimulated){ + waveBegin = waveEnd - maxWavesSimulated; + } + + for(int i = waveBegin; i <= waveEnd; i++){ + + float efficiency = health / info.sumHealth; + float dps = info.sumDps * efficiency; + float rps = info.sumRps * efficiency; + + float enemyDps = info.waveDpsBase + info.waveDpsSlope * (i); + float enemyHealth = info.waveHealthBase + info.waveHealthSlope * (i); + + //happens due to certain regressions + if(enemyHealth < 0 || enemyDps < 0) continue; + + //calculate time to destroy both sides + float timeDestroyEnemy = dps <= 0.0001f ? Float.POSITIVE_INFINITY : enemyHealth / dps; //if dps == 0, this is infinity + float timeDestroyBase = health / (enemyDps - rps); //if regen > enemyDps this is negative + + //sector is lost, enemy took too long. + if(timeDestroyEnemy > timeDestroyBase){ + health = 0f; + break; + } + + //otherwise, the enemy shoots for timeDestroyEnemy seconds, so calculate damage taken + float damageTaken = timeDestroyEnemy * (enemyDps - rps); + + //damage the base. + health -= damageTaken; + + //regen health after wave. + health = Math.min(health + rps / 60f * waveSpace, info.sumHealth); + } + } + + return 1f - Mathf.clamp(health / info.sumHealth); + } + + /** Applies wave damage based on sector parameters. */ + public static void applyCalculatedDamage(){ + //calculate base damage fraction + float damage = getDamage(state.secinfo); + + //scaled damage has a power component to make it seem a little more realistic (as systems fail, enemy capturing gets easier and easier) + float scaled = Mathf.pow(damage, 1.5f); + + //apply damage to units + float unitDamage = damage * state.secinfo.sumHealth; + Tile spawn = spawner.getFirstSpawn(); + + //damage only units near the spawn point + if(spawn != null){ + Seq allies = new Seq<>(); + for(Unit ally : Groups.unit){ + if(ally.team == state.rules.defaultTeam && ally.within(spawn, state.rules.dropZoneRadius * 2.5f)){ + allies.add(ally); + } + } + + allies.sort(u -> u.dst2(spawn)); + + //damage units one by one, not uniformly + for(var u : allies){ + if(u.health < unitDamage){ + u.remove(); + unitDamage -= u.health; + }else{ + u.health -= unitDamage; + break; + } + } + } + + if(state.secinfo.wavesPassed > 0){ + //simply remove each block in the spawner range if a wave passed + for(Tile spawner : spawner.getSpawns()){ + spawner.circle((int)(state.rules.dropZoneRadius / tilesize), tile -> { + if(tile.team() == state.rules.defaultTeam){ + if(rubble && tile.floor().hasSurface() && Mathf.chance(0.4)){ + Effect.rubble(tile.build.x, tile.build.y, tile.block().size); + } + + tile.remove(); + } + }); + } + } + + //finally apply scaled damage + apply(scaled); + } + + /** Calculates damage simulation parameters before a game is saved. */ + public static void writeParameters(SectorInfo info){ + Building core = state.rules.defaultTeam.core(); + Seq spawns = new Seq<>(); + spawner.eachGroundSpawn((x, y) -> spawns.add(world.tile(x, y))); + + if(spawns.isEmpty() && state.rules.waveTeam.core() != null){ + spawns.add(state.rules.waveTeam.core().tile); + } + + if(core == null || spawns.isEmpty()) return; + + Tile start = spawns.first(); + + Time.mark(); + var field = pathfinder.getField(state.rules.waveTeam, Pathfinder.costGround, Pathfinder.fieldCore); + Seq path = new Seq<>(); + boolean found = false; + + if(field != null && field.weights != null){ + int[][] weights = field.weights; + int count = 0; + Tile current = start; + while(count < world.width() * world.height()){ + int minCost = Integer.MAX_VALUE; + int cx = current.x, cy = current.y; + for(Point2 p : Geometry.d4){ + int nx = cx + p.x, ny = cy + p.y; + + Tile other = world.tile(nx, ny); + if(other != null && weights[nx][ny] < minCost && weights[nx][ny] != -1){ + minCost = weights[nx][ny]; + current = other; + } + } + + path.add(current); + + if(current.build == core){ + found = true; + break; + } + + count ++; + } + } + + if(!found){ + path = Astar.pathfind(start, core.tile, SectorDamage::cost, t -> !(t.block().isStatic() && t.solid())); + } + + //create sparse tile array for fast range query + int sparseSkip = 5, sparseSkip2 = 3; + //TODO if this is slow, use a quadtree + Seq sparse = new Seq<>(path.size / sparseSkip + 1); + Seq sparse2 = new Seq<>(path.size / sparseSkip2 + 1); + + for(int i = 0; i < path.size; i++){ + if(i % sparseSkip == 0){ + sparse.add(path.get(i)); + } + if(i % sparseSkip2 == 0){ + sparse2.add(path.get(i)); + } + } + + //regen is in health per second + //dps is per second + float sumHealth = 0f, sumRps = 0f, sumDps = 0f; + float totalPathBuild = 0; + + //first, calculate the total health of blocks in the path + + //radius around the path that gets counted + int radius = 7; + IntSet counted = new IntSet(); + + for(Tile t : sparse2){ + + //radius is square. + for(int dx = -radius; dx <= radius; dx++){ + for(int dy = -radius; dy <= radius; dy++){ + int wx = dx + t.x, wy = dy + t.y; + if(wx >= 0 && wy >= 0 && wx < world.width() && wy < world.height()){ + Tile tile = world.rawTile(wx, wy); + + if(tile.build != null && tile.team() == state.rules.defaultTeam && counted.add(tile.pos())){ + //health is divided by block size, because multiblocks are counted multiple times. + sumHealth += tile.build.health / tile.block().size; + totalPathBuild += 1f / tile.block().size; + } + } + } + } + } + + float avgHealth = totalPathBuild <= 1 ? sumHealth : sumHealth / totalPathBuild; + + //block dps + regen + extra health/shields + for(Building build : Groups.build){ + float e = build.efficiency(); + if(e > 0.08f){ + if(build.team == state.rules.defaultTeam && build instanceof Ranged ranged && sparse.contains(t -> t.within(build, ranged.range() + radius*tilesize))){ + if(build.block instanceof Turret t && build instanceof TurretBuild b && b.hasAmmo()){ + sumDps += t.shots / t.reloadTime * 60f * b.peekAmmo().estimateDPS() * e; + } + + if(build.block instanceof MendProjector m){ + sumRps += m.healPercent / m.reload * avgHealth * 60f / 100f * e; + } + + if(build.block instanceof ForceProjector f){ + sumHealth += f.breakage * e; + sumRps += 1f * e; + } + } + } + } + + float curEnemyHealth = 0f, curEnemyDps = 0f; + + //unit regen + health + dps + for(Unit unit : Groups.unit){ + //skip player + if(unit.isPlayer()) continue; + + if(unit.team == state.rules.defaultTeam){ + //scale health based on armor - yes, this is inaccurate, but better than nothing + float healthMult = 1f + Mathf.clamp(unit.armor / 20f); + + sumHealth += unit.health*healthMult + unit.shield; + sumDps += unit.type.dpsEstimate; + if(unit.abilities.find(a -> a instanceof HealFieldAbility) instanceof HealFieldAbility h){ + sumRps += h.amount / h.reload * 60f; + } + }else{ + curEnemyDps += unit.type.dpsEstimate; + curEnemyHealth += unit.health; + } + } + + //calculate DPS and health for the next few waves and store in list + var reg = new LinearRegression(); + Seq waveDps = new Seq<>(), waveHealth = new Seq<>(); + + for(int wave = state.wave, i = 0; i < 3; wave += (1 + i++)){ + float sumWaveDps = 0f, sumWaveHealth = 0f; + + //first wave has to take into account current dps + if(wave == state.wave){ + sumWaveDps += curEnemyDps; + sumWaveHealth += curEnemyHealth; + } + + for(SpawnGroup group : state.rules.spawns){ + float healthMult = 1f + Mathf.clamp(group.type.armor / 20f); + StatusEffect effect = (group.effect == null ? StatusEffects.none : group.effect); + int spawned = group.getSpawned(wave); + if(spawned <= 0) continue; + sumWaveHealth += spawned * (group.getShield(wave) + group.type.health * effect.healthMultiplier * healthMult); + sumWaveDps += spawned * group.type.dpsEstimate * effect.damageMultiplier; + } + waveDps.add(new Vec2(wave, sumWaveDps)); + waveHealth.add(new Vec2(wave, sumWaveHealth)); + } + + //calculate linear regression of the wave data and store it + reg.calculate(waveHealth); + info.waveHealthBase = reg.intercept; + info.waveHealthSlope = reg.slope; + + reg.calculate(waveDps); + info.waveDpsBase = reg.intercept; + info.waveDpsSlope = reg.slope; + + //enemy units like to aim for a lot of non-essential things, so increase resulting health slightly + info.sumHealth = sumHealth * 1.2f; + //players tend to have longer range units/turrets, so assume DPS is higher + info.sumDps = sumDps * 1.5f; + info.sumRps = sumRps; + + //finally, find an equation to put it all together and produce a 0-1 number + //due to the way most defenses are structured, this number will likely need a ^4 power or so + } public static void apply(float fraction){ Tiles tiles = world.tiles; @@ -35,23 +337,71 @@ public class SectorDamage{ if(core != null && !frontier.isEmpty()){ for(Tile spawner : frontier){ //find path from spawn to core - //TODO this is broken Seq path = Astar.pathfind(spawner, core.tile, SectorDamage::cost, t -> !(t.block().isStatic() && t.solid())); - int amount = (int)(path.size * fraction); - for(int i = 0; i < amount; i++){ - Tile t = path.get(i); - Geometry.circle(t.x, t.y, tiles.width, tiles.height, 5, (cx, cy) -> { - Tile other = tiles.getn(cx, cy); - //just remove all the buildings in the way - as long as they're not cores! - if(other.build != null && other.team() == state.rules.defaultTeam && !(other.block() instanceof CoreBlock)){ - if(rubble && !other.floor().solid && !other.floor().isLiquid && Mathf.chance(0.4)){ - Effect.rubble(other.build.x, other.build.y, other.block().size); - } + Seq removal = new Seq<>(); - other.remove(); + int radius = 3; + + //only penetrate a certain % by health, not by distance + float totalHealth = damage >= 1f ? 1f : path.sumf(t -> { + float s = 0; + for(int dx = -radius; dx <= radius; dx++){ + for(int dy = -radius; dy <= radius; dy++){ + int wx = dx + t.x, wy = dy + t.y; + if(wx >= 0 && wy >= 0 && wx < world.width() && wy < world.height() && Mathf.within(dx, dy, radius)){ + Tile other = world.rawTile(wx, wy); + s += other.team() == state.rules.defaultTeam ? other.build.health / other.block().size : 0f; + } } - }); + } + return s; + }); + float targetHealth = totalHealth * fraction; + float healthCount = 0; + + out: + for(int i = 0; i < path.size && (healthCount < targetHealth || damage >= 1f); i++){ + Tile t = path.get(i); + + for(int dx = -radius; dx <= radius; dx++){ + for(int dy = -radius; dy <= radius; dy++){ + int wx = dx + t.x, wy = dy + t.y; + if(wx >= 0 && wy >= 0 && wx < world.width() && wy < world.height() && Mathf.within(dx, dy, radius)){ + Tile other = world.rawTile(wx, wy); + + //just remove all the buildings in the way - as long as they're not cores + if(other.build != null && other.team() == state.rules.defaultTeam && !(other.block() instanceof CoreBlock)){ + if(rubble && !other.floor().solid && !other.floor().isLiquid && Mathf.chance(0.4)){ + Effect.rubble(other.build.x, other.build.y, other.block().size); + } + + //since the whole block is removed, count the whole health + healthCount += other.build.health; + + removal.add(other.build); + + if(healthCount >= targetHealth && damage < 0.999f){ + break out; + } + } + } + } + } } + + for(Building r : removal){ + if(r.tile.build == r){ + r.addPlan(false); + r.tile.remove(); + } + } + } + } + + //kill every core if damage is maximum + if(fraction >= 1){ + for(Building c : state.rules.defaultTeam.cores().copy()){ + c.tile.remove(); } } @@ -90,6 +440,7 @@ public class SectorDamage{ Effect.rubble(other.build.x, other.build.y, other.block().size); } + other.build.addPlan(false); other.remove(); } } diff --git a/core/src/mindustry/maps/planet/SerpuloPlanetGenerator.java b/core/src/mindustry/maps/planet/SerpuloPlanetGenerator.java index ee2f6b437c..7e2b5e9aaa 100644 --- a/core/src/mindustry/maps/planet/SerpuloPlanetGenerator.java +++ b/core/src/mindustry/maps/planet/SerpuloPlanetGenerator.java @@ -412,12 +412,12 @@ public class SerpuloPlanetGenerator extends PlanetGenerator{ if(sector.hasEnemyBase()){ basegen.generate(tiles, enemies.map(r -> tiles.getn(r.x, r.y)), tiles.get(spawn.x, spawn.y), state.rules.waveTeam, sector, difficulty); - state.rules.attackMode = true; + state.rules.attackMode = sector.info.attack = true; }else{ - state.rules.winWave = 15 * (int)Math.max(difficulty * 10, 1); + state.rules.winWave = sector.info.winWave = 10 + 5 * (int)Math.max(difficulty * 10, 1); } - state.rules.waves = true; + state.rules.waves = sector.info.waves = true; //TODO better waves state.rules.spawns = DefaultWaves.generate(difficulty); diff --git a/core/src/mindustry/mod/ContentParser.java b/core/src/mindustry/mod/ContentParser.java index b9c119ee32..0d88d01034 100644 --- a/core/src/mindustry/mod/ContentParser.java +++ b/core/src/mindustry/mod/ContentParser.java @@ -190,6 +190,7 @@ public class ContentParser{ "mindustry.world.blocks.defense", "mindustry.world.blocks.defense.turrets", "mindustry.world.blocks.distribution", + "mindustry.world.blocks.environment", "mindustry.world.blocks.liquid", "mindustry.world.blocks.logic", "mindustry.world.blocks.power", @@ -260,8 +261,8 @@ public class ContentParser{ //TODO test this! read(() -> { //add reconstructor type - if(value.hasChild("requirements")){ - JsonValue rec = value.remove("requirements"); + if(value.has("requirements")){ + JsonValue rec = value.remove("requirements"); //intermediate class for parsing class UnitReq{ @@ -286,11 +287,36 @@ public class ContentParser{ } + //read extra default waves + if(value.has("waves")){ + JsonValue waves = value.remove("waves"); + SpawnGroup[] groups = parser.readValue(SpawnGroup[].class, waves); + for(SpawnGroup group : groups){ + group.type = unit; + } + + Vars.defaultWaves.get().addAll(groups); + } + readFields(unit, value, true); }); return unit; }, + ContentType.weather, (TypeParser)(mod, name, value) -> { + Weather item; + if(locate(ContentType.weather, name) != null){ + item = locate(ContentType.weather, name); + readBundle(ContentType.weather, name, value); + }else{ + readBundle(ContentType.weather, name, value); + Class type = resolve(getType(value), "mindustry.type.weather"); + item = make(type); + } + currentContent = item; + read(() -> readFields(item, value)); + return item; + }, ContentType.item, parser(ContentType.item, Item::new), ContentType.liquid, parser(ContentType.liquid, Liquid::new) //ContentType.sector, parser(ContentType.sector, SectorPreset::new) diff --git a/core/src/mindustry/mod/Mods.java b/core/src/mindustry/mod/Mods.java index 12c33c7480..a596f3e7ae 100644 --- a/core/src/mindustry/mod/Mods.java +++ b/core/src/mindustry/mod/Mods.java @@ -629,7 +629,7 @@ public class Mods implements Loadable{ } //make sure the main class exists before loading it; if it doesn't just don't put it there - if(mainFile.exists()){ + if(mainFile.exists() && Core.settings.getBool("mod-" + meta.name.toLowerCase().replace(" ", "-") + "-enabled", true)){ //mobile versions don't support class mods if(ios){ throw new IllegalArgumentException("Java class mods are not supported on iOS."); diff --git a/core/src/mindustry/mod/Scripts.java b/core/src/mindustry/mod/Scripts.java index 1aa260e8fa..f372586863 100644 --- a/core/src/mindustry/mod/Scripts.java +++ b/core/src/mindustry/mod/Scripts.java @@ -59,12 +59,12 @@ public class Scripts implements Disposable{ if(o instanceof Undefined) o = "undefined"; return String.valueOf(o); }catch(Throwable t){ - return getError(t); + return getError(t, false); } } - private String getError(Throwable t){ - t.printStackTrace(); + private String getError(Throwable t, boolean log){ + if(log) Log.err(t); return t.getClass().getSimpleName() + (t.getMessage() == null ? "" : ": " + t.getMessage()); } @@ -138,7 +138,7 @@ public class Scripts implements Disposable{ if(currentMod != null){ file = currentMod.name + "/" + file; } - log(LogLevel.err, file, "" + getError(t)); + log(LogLevel.err, file, "" + getError(t, true)); return false; } } diff --git a/core/src/mindustry/net/Administration.java b/core/src/mindustry/net/Administration.java index f185744c28..ccf5f76755 100644 --- a/core/src/mindustry/net/Administration.java +++ b/core/src/mindustry/net/Administration.java @@ -577,7 +577,10 @@ public class Administration{ autosave("Whether the periodically save the map when playing.", false), autosaveAmount("The maximum amount of autosaves. Older ones get replaced.", 10), autosaveSpacing("Spacing between autosaves in seconds.", 60 * 5), - debug("Enable debug logging", false, () -> Log.setLogLevel(debug() ? LogLevel.debug : LogLevel.info)); + debug("Enable debug logging", false, () -> { + LogLevel level = debug() ? LogLevel.debug : LogLevel.info; + Log.level = level; + }); public static final Config[] all = values(); diff --git a/core/src/mindustry/net/NetworkIO.java b/core/src/mindustry/net/NetworkIO.java index 3a32d5cb6b..f4494e1c4f 100644 --- a/core/src/mindustry/net/NetworkIO.java +++ b/core/src/mindustry/net/NetworkIO.java @@ -3,7 +3,9 @@ package mindustry.net; import arc.*; import arc.util.*; import arc.util.io.*; +import mindustry.content.*; import mindustry.core.*; +import mindustry.ctype.*; import mindustry.game.*; import mindustry.gen.*; import mindustry.io.*; @@ -21,6 +23,18 @@ public class NetworkIO{ public static void writeWorld(Player player, OutputStream os){ try(DataOutputStream stream = new DataOutputStream(os)){ + //write all researched content to rules if hosting + if(state.isCampaign()){ + state.rules.researched.clear(); + for(ContentType type : ContentType.all){ + for(Content c : content.getBy(type)){ + if(c instanceof UnlockableContent u && u.unlocked() && TechTree.get(u) != null){ + state.rules.researched.add(u.name); + } + } + } + } + stream.writeUTF(JsonIO.write(state.rules)); SaveIO.getSaveWriter().writeStringMap(stream, state.map.tags); diff --git a/core/src/mindustry/type/AmmoTypes.java b/core/src/mindustry/type/AmmoTypes.java index 5b7c885519..761bcbc8d0 100644 --- a/core/src/mindustry/type/AmmoTypes.java +++ b/core/src/mindustry/type/AmmoTypes.java @@ -41,15 +41,15 @@ public class AmmoTypes implements ContentList{ @Override public void resupply(Unit unit){ float range = unit.hitSize + 60f; - Tile closest = Vars.indexer.findClosestFlag(unit.x, unit.y, unit.team, BlockFlag.powerRes); + Tile closest = Vars.indexer.findClosestFlag(unit.x, unit.y, unit.team, BlockFlag.battery); if(closest != null && closest.build != null && unit.within(closest.build, range) && closest.build.power != null){ var build = closest.build; if(build.block.consumes.hasPower() && build.block.consumes.getPower().buffered){ float amount = closest.build.power.status * build.block.consumes.getPower().capacity; - float powerPerAmmo = totalPower / unit.type().ammoCapacity; - float ammoRequired = unit.type().ammoCapacity - unit.ammo; + float powerPerAmmo = totalPower / unit.type.ammoCapacity; + float ammoRequired = unit.type.ammoCapacity - unit.ammo; float powerRequired = ammoRequired * powerPerAmmo; float powerTaken = Math.min(amount, powerRequired); diff --git a/core/src/mindustry/type/Item.java b/core/src/mindustry/type/Item.java index 9329efb47e..f52323d655 100644 --- a/core/src/mindustry/type/Item.java +++ b/core/src/mindustry/type/Item.java @@ -1,16 +1,15 @@ package mindustry.type; import arc.graphics.*; -import arc.scene.ui.layout.*; import arc.struct.*; import mindustry.ctype.*; -import mindustry.ui.*; import mindustry.world.blocks.environment.*; +import mindustry.world.meta.*; import static mindustry.Vars.*; public class Item extends UnlockableContent{ - public final Color color; + public Color color; /** how explosive this item is. */ public float explosiveness = 0f; @@ -36,8 +35,10 @@ public class Item extends UnlockableContent{ } @Override - public void displayInfo(Table table){ - ContentDisplay.displayItem(table, this); + public void setStats(){ + stats.addPercent(Stat.explosiveness, explosiveness); + stats.addPercent(Stat.flammability, flammability); + stats.addPercent(Stat.radioactivity, radioactivity); } @Override diff --git a/core/src/mindustry/type/ItemSeq.java b/core/src/mindustry/type/ItemSeq.java index 0407f6a3f8..49055fb5b9 100644 --- a/core/src/mindustry/type/ItemSeq.java +++ b/core/src/mindustry/type/ItemSeq.java @@ -21,6 +21,13 @@ public class ItemSeq implements Iterable, Serializable{ stacks.each(this::add); } + public ItemSeq copy(){ + ItemSeq out = new ItemSeq(); + out.total = total; + System.arraycopy(values, 0, out.values, 0, values.length); + return out; + } + public void each(ItemConsumer cons){ for(int i = 0; i < values.length; i++){ if(values[i] != 0){ @@ -46,6 +53,19 @@ public class ItemSeq implements Iterable, Serializable{ return values[item.id] > 0; } + public boolean has(ItemSeq seq){ + for(int i = 0; i < values.length; i++){ + if(seq.values[i] > values[i]){ + return false; + } + } + return true; + } + + public boolean has(Item item, int amount){ + return values[item.id] >= amount; + } + public int get(Item item){ return values[item.id]; } diff --git a/core/src/mindustry/type/ItemStack.java b/core/src/mindustry/type/ItemStack.java index d5f4097b58..e2e0d2d5f4 100644 --- a/core/src/mindustry/type/ItemStack.java +++ b/core/src/mindustry/type/ItemStack.java @@ -64,6 +64,13 @@ public class ItemStack implements Comparable{ return item.compareTo(itemStack.item); } + @Override + public boolean equals(Object o){ + if(this == o) return true; + if(!(o instanceof ItemStack stack)) return false; + return amount == stack.amount && item == stack.item; + } + @Override public String toString(){ return "ItemStack{" + diff --git a/core/src/mindustry/type/Liquid.java b/core/src/mindustry/type/Liquid.java index 19e77a9987..e62805e60b 100644 --- a/core/src/mindustry/type/Liquid.java +++ b/core/src/mindustry/type/Liquid.java @@ -1,15 +1,14 @@ package mindustry.type; import arc.graphics.*; -import arc.scene.ui.layout.*; import arc.util.*; import mindustry.content.*; import mindustry.ctype.*; -import mindustry.ui.*; +import mindustry.world.meta.*; public class Liquid extends UnlockableContent{ /** Color used in pipes and on the ground. */ - public final Color color; + public Color color; /** Color used in bars. */ public @Nullable Color barColor; /** Color used to draw lights. Note that the alpha channel is used to dictate brightness. */ @@ -46,8 +45,12 @@ public class Liquid extends UnlockableContent{ } @Override - public void displayInfo(Table table){ - ContentDisplay.displayLiquid(table, this); + public void setStats(){ + stats.addPercent(Stat.explosiveness, explosiveness); + stats.addPercent(Stat.flammability, flammability); + stats.addPercent(Stat.temperature, temperature); + stats.addPercent(Stat.heatCapacity, heatCapacity); + stats.addPercent(Stat.viscosity, viscosity); } @Override diff --git a/core/src/mindustry/type/Planet.java b/core/src/mindustry/type/Planet.java index 6153aa4721..4550ae6805 100644 --- a/core/src/mindustry/type/Planet.java +++ b/core/src/mindustry/type/Planet.java @@ -5,7 +5,6 @@ import arc.func.*; import arc.graphics.*; import arc.math.*; import arc.math.geom.*; -import arc.scene.ui.layout.*; import arc.struct.*; import arc.util.*; import arc.util.noise.*; @@ -177,7 +176,7 @@ public class Planet extends UnlockableContent{ public void updateBaseCoverage(){ for(Sector sector : sectors){ float sum = 1f; - for(Sector other : sector.inRange(2)){ + for(Sector other : sector.near()){ if(other.generateEnemyBase){ sum += 1f; } @@ -204,6 +203,10 @@ public class Planet extends UnlockableContent{ @Override public void init(){ + for(Sector sector : sectors){ + sector.loadInfo(); + } + if(generator != null){ Noise.setSeed(id + 1); @@ -255,13 +258,16 @@ public class Planet extends UnlockableContent{ return true; } - @Override - public void displayInfo(Table table){ - - } - @Override public ContentType getContentType(){ return ContentType.planet; } + + public boolean visible(){ + return true; + } + + public void draw(Mat3D projection, Mat3D transform){ + mesh.render(projection, transform); + } } diff --git a/core/src/mindustry/type/Sector.java b/core/src/mindustry/type/Sector.java index 4c1df28945..975c8b82c8 100644 --- a/core/src/mindustry/type/Sector.java +++ b/core/src/mindustry/type/Sector.java @@ -7,6 +7,7 @@ import arc.struct.*; import arc.util.*; import mindustry.*; import mindustry.game.Saves.*; +import mindustry.game.*; import mindustry.graphics.g3d.PlanetGrid.*; import mindustry.world.modules.*; @@ -25,6 +26,7 @@ public class Sector{ public @Nullable SaveSlot save; public @Nullable SectorPreset preset; + public SectorInfo info = new SectorInfo(); /** Number 0-1 indicating the difficulty based on nearby bases. */ public float baseCoverage; @@ -38,60 +40,63 @@ public class Sector{ this.id = tile.id; } - public Seq inRange(int range){ - //TODO cleanup/remove - if(true){ - tmpSeq1.clear(); - neighbors(tmpSeq1::add); - - return tmpSeq1; + /** @return a copy of the items in this sector - may be core items, or stored data. */ + public ItemSeq getItems(){ + if(isBeingPlayed()){ + ItemSeq out = new ItemSeq(); + if(state.rules.defaultTeam.core() != null) out.add(state.rules.defaultTeam.core().items); + return out; + }else{ + return info.items; } - - tmpSeq1.clear(); - tmpSeq2.clear(); - tmpSet.clear(); - - tmpSeq1.add(this); - tmpSet.add(this); - for(int i = 0; i < range; i++){ - while(!tmpSeq1.isEmpty()){ - Sector sec = tmpSeq1.pop(); - tmpSet.add(sec); - sec.neighbors(other -> { - if(tmpSet.add(other)){ - tmpSeq2.add(other); - } - }); - } - tmpSeq1.clear(); - tmpSeq1.addAll(tmpSeq2); - } - - tmpSeq3.clear().addAll(tmpSeq2); - return tmpSeq3; } - public void neighbors(Cons cons){ + public Seq near(){ + tmpSeq1.clear(); + for(Ptile tile : tile.tiles){ + tmpSeq1.add(planet.getSector(tile)); + } + + return tmpSeq1; + } + + public void near(Cons cons){ for(Ptile tile : tile.tiles){ cons.get(planet.getSector(tile)); } } /** @return whether this sector can be landed on at all. - * Only sectors adjacent to non-wave sectors can be landed on. - * TODO also preset sectors*/ + * Only sectors adjacent to non-wave sectors can be landed on. */ public boolean unlocked(){ return hasBase() || (preset != null && preset.alwaysUnlocked); } + public void saveInfo(){ + Core.settings.putJson(planet.name + "-s-" + id + "-info", info); + } + + public void loadInfo(){ + info = Core.settings.getJson(planet.name + "-s-" + id + "-info", SectorInfo.class, SectorInfo::new); + } + + public float getProductionScale(){ + return Math.max(1f - info.damage, 0); + } + + public boolean isAttacked(){ + if(isBeingPlayed()) return state.rules.waves; + return save != null && info.waves && info.hasCore; + } + /** @return whether the player has a base here. */ public boolean hasBase(){ - return save != null && !save.meta.tags.getBool("nocores"); + return save != null && info.hasCore; } /** @return whether the enemy has a generated base here. */ public boolean hasEnemyBase(){ - return generateEnemyBase && (save == null || save.meta.rules.waves); + return generateEnemyBase && (save == null || info.waves); } public boolean isBeingPlayed(){ @@ -99,13 +104,18 @@ public class Sector{ return Vars.state.isGame() && Vars.state.rules.sector == this && !Vars.state.gameOver; } - public boolean isCaptured(){ - return save != null && !save.meta.rules.waves; + public String name(){ + if(preset != null) return preset.localizedName; + return info.name == null ? id + "" : info.name; } - /** @return whether waves are present - if true, any bases here will be attacked. */ - public boolean hasWaves(){ - return save != null && save.meta.rules.waves; + public void setName(String name){ + info.name = name; + saveInfo(); + } + + public boolean isCaptured(){ + return save != null && !info.waves; } public boolean hasSave(){ @@ -130,19 +140,16 @@ public class Sector{ return res % 2 == 0 ? res : res + 1; } - //TODO this should be stored in a more efficient structure, and be updated each turn - public ItemSeq getExtraItems(){ - return Core.settings.getJson(key("extra-items"), ItemSeq.class, ItemSeq::new); - } - - public void setExtraItems(ItemSeq stacks){ - Core.settings.putJson(key("extra-items"), stacks); - } - public void addItem(Item item, int amount){ removeItem(item, -amount); } + public void removeItems(ItemSeq items){ + ItemSeq copy = items.copy(); + copy.each((i, a) -> copy.set(i, -a)); + addItems(copy); + } + public void removeItem(Item item, int amount){ ItemSeq seq = new ItemSeq(); seq.add(item, -amount); @@ -156,137 +163,27 @@ public class Sector{ int cap = state.rules.defaultTeam.core().storageCapacity; items.each((item, amount) -> storage.add(item, Math.min(cap - storage.get(item), amount))); } - }else{ - ItemSeq recv = getExtraItems(); - - if(save != null){ - //"shave off" extra items - - ItemSeq count = new ItemSeq(); - - //add items already present - count.add(save.meta.secinfo.coreItems); - - count.add(calculateReceivedItems()); - - int capacity = save.meta.secinfo.storageCapacity; - - //when over capacity, add that to the extra items - count.each((i, a) -> { - if(a > capacity){ - recv.remove(i, (a - capacity)); - } - }); - } - - recv.add(items); - - setExtraItems(recv); + }else if(hasBase()){ + items.each((item, amount) -> info.items.add(item, Math.min(info.storageCapacity - info.items.get(item), amount))); + saveInfo(); } } - public ItemSeq calculateItems(){ + /** @return items currently in this sector, taking into account playing state. */ + public ItemSeq items(){ ItemSeq count = new ItemSeq(); //for sectors being played on, add items directly if(isBeingPlayed()){ count.add(state.rules.defaultTeam.items()); - }else if(save != null){ + }else{ //add items already present - count.add(save.meta.secinfo.coreItems); - - count.add(calculateReceivedItems()); - - int capacity = save.meta.secinfo.storageCapacity; - - //validation - count.each((item, amount) -> { - //ensure positive items - if(amount < 0) count.set(item, 0); - //cap the items - if(amount > capacity) count.set(item, capacity); - }); + count.add(info.items); } return count; } - public ItemSeq calculateReceivedItems(){ - ItemSeq count = new ItemSeq(); - - if(save != null){ - long seconds = getSecondsPassed(); - - //add produced items - save.meta.secinfo.production.each((item, stat) -> count.add(item, (int)(stat.mean * seconds))); - - //add received items - count.add(getExtraItems()); - } - - return count; - } - - //TODO these methods should maybe move somewhere else and/or be contained in a data object - public void setSpawnPosition(int position){ - put("spawn-position", position); - } - - /** Only valid after this sector has been landed on once. */ - //TODO move to sector data? - public int getSpawnPosition(){ - return Core.settings.getInt(key("spawn-position"), Point2.pack(world.width() / 2, world.height() / 2)); - } - - /** @return time spent in this sector this turn in ticks. */ - public float getTimeSpent(){ - //return currently counting time spent if being played on - if(isBeingPlayed()) return state.secinfo.internalTimeSpent; - - //else return the stored value - return getStoredTimeSpent(); - } - - public void setTimeSpent(float time){ - put("time-spent", time); - - //update counting time - if(isBeingPlayed()){ - state.secinfo.internalTimeSpent = time; - } - } - - public String displayTimeRemaining(){ - float amount = Vars.turnDuration - getTimeSpent(); - int seconds = (int)(amount / 60); - int sf = seconds % 60; - return (seconds / 60) + ":" + (sf < 10 ? "0" : "") + sf; - } - - /** @return the stored amount of time spent in this sector this turn in ticks. - * Do not use unless you know what you're doing. */ - public float getStoredTimeSpent(){ - return Core.settings.getFloat(key("time-spent")); - } - - public void setSecondsPassed(int number){ - put("secondsi-passed", number); - } - - /** @return how much time has passed in this sector without the player resuming here. - * Used for resource production calculations. */ - public int getSecondsPassed(){ - return Core.settings.getInt(key("secondsi-passed")); - } - - private String key(String key){ - return planet.name + "-s-" + id + "-" + key; - } - - private void put(String key, Object value){ - Core.settings.put(key(key), value); - } - public String toString(){ return planet.name + "#" + id; } diff --git a/core/src/mindustry/type/SectorPreset.java b/core/src/mindustry/type/SectorPreset.java index 923d023b25..0e9b90fb30 100644 --- a/core/src/mindustry/type/SectorPreset.java +++ b/core/src/mindustry/type/SectorPreset.java @@ -2,7 +2,6 @@ package mindustry.type; import arc.func.*; import arc.graphics.g2d.*; -import arc.scene.ui.layout.*; import mindustry.ctype.*; import mindustry.game.*; import mindustry.gen.*; @@ -23,6 +22,7 @@ public class SectorPreset extends UnlockableContent{ super(name); this.generator = new FileMapGenerator(name); this.planet = planet; + sector %= planet.sectors.size; this.sector = planet.sectors.get(sector); planet.preset(sector, this); @@ -38,11 +38,6 @@ public class SectorPreset extends UnlockableContent{ return true; } - //neither of these are implemented, as zones are not displayed in a normal fashion... yet - @Override - public void displayInfo(Table table){ - } - @Override public ContentType getContentType(){ return ContentType.sector; diff --git a/core/src/mindustry/type/StatusEffect.java b/core/src/mindustry/type/StatusEffect.java index eb0bedb60c..90488c62ea 100644 --- a/core/src/mindustry/type/StatusEffect.java +++ b/core/src/mindustry/type/StatusEffect.java @@ -56,7 +56,7 @@ public class StatusEffect extends MappableContent{ } if(effect != Fx.none && Mathf.chanceDelta(effectChance)){ - Tmp.v1.rnd(unit.type().hitSize /2f); + Tmp.v1.rnd(unit.type.hitSize /2f); effect.at(unit.x + Tmp.v1.x, unit.y + Tmp.v1.y); } } diff --git a/core/src/mindustry/type/UnitType.java b/core/src/mindustry/type/UnitType.java index 661e735fa7..43d5f05bd9 100644 --- a/core/src/mindustry/type/UnitType.java +++ b/core/src/mindustry/type/UnitType.java @@ -28,6 +28,8 @@ import mindustry.world.blocks.environment.*; import mindustry.world.blocks.payloads.*; import mindustry.world.blocks.units.*; import mindustry.world.consumers.*; +import mindustry.world.meta.*; +import mindustry.world.meta.values.*; import static mindustry.Vars.*; @@ -41,7 +43,7 @@ public class UnitType extends UnlockableContent{ public Prov defaultController = () -> !flying ? new GroundAI() : new FlyingAI(); 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 health = 200f, range = -1, armor = 0f, maxRange = -1f; public float crashDamageMultiplier = 1f; public boolean targetAir = true, targetGround = true; public boolean faceTarget = true, rotateShooting = true, isCounted = true, lowAltitude = false; @@ -58,6 +60,7 @@ public class UnitType extends UnlockableContent{ public Effect fallEffect = Fx.fallSmoke; public Effect fallThrusterEffect = Fx.fallSmoke; public Seq abilities = new Seq<>(); + public BlockFlag targetFlag = BlockFlag.generator; public int legCount = 4, legGroupSize = 2; public float legLength = 10f, legSpeed = 0.1f, legTrns = 1f, legBaseOffset = 0f, legMoveSpace = 1f, legExtension = 0, legPairOffset = 0, legLengthScl = 1f, kinematicScl = 1f, maxStretch = 1.75f; @@ -79,6 +82,8 @@ public class UnitType extends UnlockableContent{ public int mineTier = -1; public float buildSpeed = 1f, mineSpeed = 1f; + /** This is a VERY ROUGH estimate of unit DPS. */ + public float dpsEstimate = -1; public float clipSize = -1; public boolean canDrown = true; public float engineOffset = 5f, engineSize = 2.5f; @@ -115,7 +120,7 @@ public class UnitType extends UnlockableContent{ public Unit create(Team team){ Unit unit = constructor.get(); unit.team = team; - unit.type(this); + unit.setType(this); unit.ammo = ammoCapacity; //fill up on ammo upon creation unit.elevation = flying ? 1f : 0; unit.heal(); @@ -154,11 +159,11 @@ public class UnitType extends UnlockableContent{ table.table(bars -> { bars.defaults().growX().height(20f).pad(4); - bars.add(new Bar("blocks.health", Pal.health, unit::healthf).blink(Color.white)); + bars.add(new Bar("stat.health", Pal.health, unit::healthf).blink(Color.white)); bars.row(); if(state.rules.unitAmmo){ - bars.add(new Bar(ammoType.icon + " " + Core.bundle.get("blocks.ammo"), ammoType.barColor, () -> unit.ammo / ammoCapacity)); + bars.add(new Bar(ammoType.icon + " " + Core.bundle.get("stat.ammo"), ammoType.barColor, () -> unit.ammo / ammoCapacity)); bars.row(); } }).growX(); @@ -186,10 +191,21 @@ public class UnitType extends UnlockableContent{ } } - @Override - public void displayInfo(Table table){ - ContentDisplay.displayUnit(table, this); + public void setStats(){ + Unit inst = constructor.get(); + + stats.add(Stat.health, health); + stats.add(Stat.speed, speed); + stats.add(Stat.itemCapacity, health); + stats.add(Stat.range, (int)(maxRange / tilesize), StatUnit.blocks); + //TODO abilities, maybe try something like DPS + + if(inst instanceof Minerc && mineTier >= 1){ + stats.addPercent(Stat.mineSpeed, mineSpeed); + stats.add(Stat.mineTier, new BlockFilterValue(b -> b instanceof Floor f && f.itemDrop != null && f.itemDrop.hardness <= mineTier && !f.playerUnmineable)); + } + if(inst instanceof Builderc) stats.addPercent(Stat.buildSpeed, buildSpeed); } @CallSuper @@ -215,8 +231,10 @@ public class UnitType extends UnlockableContent{ //set up default range if(range < 0){ range = Float.MAX_VALUE; + maxRange = 0f; for(Weapon weapon : weapons){ range = Math.min(range, weapon.bullet.range() + hitSize /2f); + maxRange = Math.max(maxRange, weapon.bullet.range() + hitSize /2f); } } @@ -266,6 +284,17 @@ public class UnitType extends UnlockableContent{ ammoCapacity = Math.max(1, (int)(shotsPerSecond * targetSeconds)); } + + //calculate estimated DPS for one target based on weapons + if(dpsEstimate < 0){ + dpsEstimate = weapons.sumf(w -> (w.bullet.estimateDPS() / w.reload) * w.shots * 60f); + + //suicide enemy + if(weapons.contains(w -> w.bullet.killShooter)){ + //scale down DPS to be insignificant + dpsEstimate /= 100f; + } + } } @CallSuper @@ -309,7 +338,7 @@ public class UnitType extends UnlockableContent{ if(stacks != null){ ItemStack[] out = new ItemStack[stacks.length]; for(int i = 0; i < out.length; i++){ - out[i] = new ItemStack(stacks[i].item, UI.roundAmount((int)(Math.pow(stacks[i].amount, 1.1) * 50))); + out[i] = new ItemStack(stacks[i].item, UI.roundAmount((int)(Math.pow(stacks[i].amount, 1) * 50))); } return out; @@ -436,7 +465,7 @@ public class UnitType extends UnlockableContent{ applyColor(unit); //draw back items - if(unit.hasItem() && unit.itemTime > 0.01f){ + if(unit.item() != null && unit.itemTime > 0.01f){ float size = (itemSize + Mathf.absin(Time.time(), 5f, 1f)) * unit.itemTime; Draw.mixcol(Pal.accent, Mathf.absin(Time.time(), 5f, 0.5f)); diff --git a/core/src/mindustry/type/Weather.java b/core/src/mindustry/type/Weather.java index 8fb30aecb6..4d17ab7c0e 100644 --- a/core/src/mindustry/type/Weather.java +++ b/core/src/mindustry/type/Weather.java @@ -1,16 +1,18 @@ package mindustry.type; +import arc.*; import arc.func.*; +import arc.graphics.*; import arc.graphics.g2d.*; import arc.math.*; import arc.math.geom.*; -import arc.scene.ui.layout.*; import arc.util.*; import mindustry.annotations.Annotations.*; import mindustry.content.*; import mindustry.ctype.*; import mindustry.gen.*; import mindustry.graphics.*; +import mindustry.world.*; import mindustry.world.blocks.*; import static mindustry.Vars.*; @@ -91,9 +93,136 @@ public abstract class Weather extends UnlockableContent{ } - @Override - public void displayInfo(Table table){ - //do not + public void drawParticles(TextureRegion region, Color color, + float sizeMin, float sizeMax, + float density, float intensity, float opacity, + float windx, float windy, + float minAlpha, float maxAlpha, + float sinSclMin, float sinSclMax, float sinMagMin, float sinMagMax){ + rand.setSeed(0); + Tmp.r1.setCentered(Core.camera.position.x, Core.camera.position.y, Core.graphics.getWidth() / renderer.minScale(), Core.graphics.getHeight() / renderer.minScale()); + Tmp.r1.grow(sizeMax * 1.5f); + Core.camera.bounds(Tmp.r2); + int total = (int)(Tmp.r1.area() / density * intensity); + Draw.color(color, opacity); + + for(int i = 0; i < total; i++){ + float scl = rand.random(0.5f, 1f); + float scl2 = rand.random(0.5f, 1f); + float size = rand.random(sizeMin, sizeMax); + float x = (rand.random(0f, world.unitWidth()) + Time.time() * windx * scl2); + float y = (rand.random(0f, world.unitHeight()) + Time.time() * windy * scl); + float alpha = rand.random(minAlpha, maxAlpha); + + x += Mathf.sin(y, rand.random(sinSclMin, sinSclMax), rand.random(sinMagMin, sinMagMax)); + + x -= Tmp.r1.x; + y -= Tmp.r1.y; + x = Mathf.mod(x, Tmp.r1.width); + y = Mathf.mod(y, Tmp.r1.height); + x += Tmp.r1.x; + y += Tmp.r1.y; + + if(Tmp.r3.setCentered(x, y, size).overlaps(Tmp.r2)){ + Draw.alpha(alpha * opacity); + Draw.rect(region, x, y, size, size); + } + } + } + + public void drawRain(float sizeMin, float sizeMax, float xspeed, float yspeed, float density, float intensity, float stroke, Color color){ + float padding = sizeMax*0.9f; + + Tmp.r1.setCentered(Core.camera.position.x, Core.camera.position.y, Core.graphics.getWidth() / renderer.minScale(), Core.graphics.getHeight() / renderer.minScale()); + Tmp.r1.grow(padding); + Core.camera.bounds(Tmp.r2); + int total = (int)(Tmp.r1.area() / density * intensity); + Lines.stroke(stroke); + float alpha = Draw.getColor().a; + Draw.color(color); + + for(int i = 0; i < total; i++){ + float scl = rand.random(0.5f, 1f); + float scl2 = rand.random(0.5f, 1f); + float size = rand.random(sizeMin, sizeMax); + float x = (rand.random(0f, world.unitWidth()) + Time.time() * xspeed * scl2); + float y = (rand.random(0f, world.unitHeight()) - Time.time() * yspeed * scl); + float tint = rand.random(1f) * alpha; + + x -= Tmp.r1.x; + y -= Tmp.r1.y; + x = Mathf.mod(x, Tmp.r1.width); + y = Mathf.mod(y, Tmp.r1.height); + x += Tmp.r1.x; + y += Tmp.r1.y; + + if(Tmp.r3.setCentered(x, y, size).overlaps(Tmp.r2)){ + Draw.alpha(tint); + Lines.lineAngle(x, y, Angles.angle(xspeed * scl2, - yspeed * scl), size/2f); + } + } + } + + public void drawSplashes(TextureRegion[] splashes, float padding, float density, float intensity, float opacity, float timeScale, float stroke, Color color, Liquid splasher){ + Tmp.r1.setCentered(Core.camera.position.x, Core.camera.position.y, Core.graphics.getWidth() / renderer.minScale(), Core.graphics.getHeight() / renderer.minScale()); + Tmp.r1.grow(padding); + Core.camera.bounds(Tmp.r2); + int total = (int)(Tmp.r1.area() / density * intensity) / 2; + Lines.stroke(stroke); + + float t = Time.time() / timeScale; + + for(int i = 0; i < total; i++){ + float offset = rand.random(0f, 1f); + float time = t + offset; + + int pos = (int)((time)); + float life = time % 1f; + float x = (rand.random(0f, world.unitWidth()) + pos*953); + float y = (rand.random(0f, world.unitHeight()) - pos*453); + + x -= Tmp.r1.x; + y -= Tmp.r1.y; + x = Mathf.mod(x, Tmp.r1.width); + y = Mathf.mod(y, Tmp.r1.height); + x += Tmp.r1.x; + y += Tmp.r1.y; + + if(Tmp.r3.setCentered(x, y, life * 4f).overlaps(Tmp.r2)){ + Tile tile = world.tileWorld(x, y); + + //only create splashes on specific liquid. + if(tile != null && tile.floor().liquidDrop == splasher){ + Draw.color(Tmp.c1.set(tile.floor().mapColor).mul(1.5f).a(opacity)); + Draw.rect(splashes[(int)(life * (splashes.length - 1))], x, y); + }else if(tile != null && tile.floor().liquidDrop == null && !tile.floor().solid){ + Draw.color(color); + Draw.alpha(Mathf.slope(life) * opacity); + + float space = 45f; + for(int j : new int[]{-1, 1}){ + Tmp.v1.trns(90f + j*space, 1f + 5f * life); + Lines.lineAngle(x + Tmp.v1.x, y + Tmp.v1.y, 90f + j*space, 3f * (1f - life)); + } + } + } + } + } + + public void drawNoise(Texture noise, Color color, float noisescl, float opacity, float baseSpeed, float intensity, float vwindx, float vwindy, float offset){ + Draw.alpha(opacity); + Draw.tint(color); + + float speed = baseSpeed * intensity; + float windx = vwindx * speed, windy = vwindy * speed; + + float scale = 1f / noisescl; + float scroll = Time.time() * scale + offset; + Tmp.tr1.texture = noise; + Core.camera.bounds(Tmp.r1); + Tmp.tr1.set(Tmp.r1.x*scale, Tmp.r1.y*scale, (Tmp.r1.x + Tmp.r1.width)*scale, (Tmp.r1.y + Tmp.r1.height)*scale); + Tmp.tr1.scroll(-windx * scroll, -windy * scroll); + Draw.rect(Tmp.tr1, Core.camera.position.x, Core.camera.position.y, Core.camera.width, -Core.camera.height); } @Override diff --git a/core/src/mindustry/type/weather/ParticleWeather.java b/core/src/mindustry/type/weather/ParticleWeather.java new file mode 100644 index 0000000000..060e9cbe62 --- /dev/null +++ b/core/src/mindustry/type/weather/ParticleWeather.java @@ -0,0 +1,90 @@ +package mindustry.type.weather; + +import arc.*; +import arc.graphics.*; +import arc.graphics.Texture.*; +import arc.graphics.g2d.*; +import arc.util.*; +import mindustry.gen.*; +import mindustry.type.*; + +public class ParticleWeather extends Weather{ + public String particleRegion = "circle-shadow"; + public Color color = Color.white.cpy(); + public TextureRegion region; + public float yspeed = -2f, xspeed = 0.25f, padding = 16f, sizeMin = 2.4f, sizeMax = 12f, density = 1200f, minAlpha = 1f, maxAlpha = 1f, force = 0, noiseScale = 2000f, baseSpeed = 6.1f; + public float sinSclMin = 30f, sinSclMax = 80f, sinMagMin = 1f, sinMagMax = 7f; + + public Color noiseColor = color; + public boolean drawNoise = false, drawParticles = true, useWindVector = false; + public int noiseLayers = 1; + public float noiseLayerSpeedM = 1.1f, noiseLayerAlphaM = 0.8f, noiseLayerSclM = 0.99f, noiseLayerColorM = 1f; + public String noisePath = "noiseAlpha"; + public @Nullable Texture noise; + + public ParticleWeather(String name){ + super(name); + } + + @Override + public void load(){ + super.load(); + + region = Core.atlas.find(particleRegion); + + //load noise texture + //TODO mod support + if(drawNoise){ + Core.assets.load("sprites/" + noisePath + ".png", Texture.class); + } + } + + @Override + public void update(WeatherState state){ + float speed = force * state.intensity; + if(speed > 0.001f){ + float windx = state.windVector.x * speed, windy = state.windVector.y * speed; + + for(Unit unit : Groups.unit){ + unit.impulse(windx, windy); + } + } + } + + @Override + public void drawOver(WeatherState state){ + + float windx, windy; + if(useWindVector){ + float speed = baseSpeed * state.intensity; + windx = state.windVector.x * speed; + windy = state.windVector.y * speed; + }else{ + windx = this.xspeed; + windy = this.yspeed; + } + + if(drawNoise){ + if(noise == null){ + noise = Core.assets.get("sprites/" + noisePath + ".png", Texture.class); + noise.setWrap(TextureWrap.repeat); + noise.setFilter(TextureFilter.linear); + } + + float sspeed = 1f, sscl = 1f, salpha = 1f, offset = 0f; + Color col = Tmp.c1.set(noiseColor); + for(int i = 0; i < noiseLayers; i++){ + drawNoise(noise, noiseColor, noiseScale * sscl, state.opacity * salpha * opacityMultiplier, baseSpeed * sspeed, state.intensity, windx, windy, offset); + sspeed *= noiseLayerSpeedM; + salpha *= noiseLayerAlphaM; + sscl *= noiseLayerSclM; + offset += 0.29f; + col.mul(noiseLayerColorM); + } + } + + if(drawParticles){ + drawParticles(region, color, sizeMin, sizeMax, density, state.intensity, state.opacity, windx, windy, minAlpha, maxAlpha, sinSclMin, sinSclMax, sinMagMin, sinMagMax); + } + } +} diff --git a/core/src/mindustry/type/weather/RainWeather.java b/core/src/mindustry/type/weather/RainWeather.java new file mode 100644 index 0000000000..78fd096062 --- /dev/null +++ b/core/src/mindustry/type/weather/RainWeather.java @@ -0,0 +1,38 @@ +package mindustry.type.weather; + +import arc.*; +import arc.graphics.*; +import arc.graphics.g2d.*; +import mindustry.content.*; +import mindustry.gen.*; +import mindustry.type.*; + +public class RainWeather extends Weather{ + public float yspeed = 5f, xspeed = 1.5f, padding = 16f, density = 1200f, stroke = 0.75f, sizeMin = 8f, sizeMax = 40f, splashTimeScale = 22f; + public Liquid liquid = Liquids.water; + public TextureRegion[] splashes = new TextureRegion[12]; + public Color color = Color.valueOf("7a95eaff"); + + public RainWeather(String name){ + super(name); + } + + @Override + public void load(){ + super.load(); + + for(int i = 0; i < splashes.length; i++){ + splashes[i] = Core.atlas.find("splash-" + i); + } + } + + @Override + public void drawOver(WeatherState state){ + drawRain(sizeMin, sizeMax, xspeed, yspeed, density, state.intensity, stroke, color); + } + + @Override + public void drawUnder(WeatherState state){ + drawSplashes(splashes, sizeMax, density, state.intensity, state.opacity, splashTimeScale, stroke, color, liquid); + } +} diff --git a/core/src/mindustry/ui/ContentDisplay.java b/core/src/mindustry/ui/ContentDisplay.java deleted file mode 100644 index 6eefedcd53..0000000000 --- a/core/src/mindustry/ui/ContentDisplay.java +++ /dev/null @@ -1,165 +0,0 @@ -package mindustry.ui; - -import arc.*; -import arc.graphics.*; -import arc.scene.ui.layout.*; -import arc.struct.*; -import arc.util.*; -import mindustry.gen.*; -import mindustry.graphics.*; -import mindustry.type.*; -import mindustry.world.*; -import mindustry.world.meta.*; - -public class ContentDisplay{ - - public static void displayBlock(Table table, Block block){ - - table.table(title -> { - int size = 8 * 6; - - title.image(block.icon(Cicon.xlarge)).size(size); - title.add("[accent]" + block.localizedName).padLeft(5); - }); - - table.row(); - - table.image().height(3).color(Color.lightGray).pad(8).padLeft(0).padRight(0).fillX(); - - table.row(); - - if(block.description != null){ - table.add(block.displayDescription()).padLeft(5).padRight(5).width(400f).wrap().fillX(); - table.row(); - - table.image().height(3).color(Color.lightGray).pad(8).padLeft(0).padRight(0).fillX(); - table.row(); - } - - BlockStats stats = block.stats; - - for(StatCategory cat : stats.toMap().keys()){ - OrderedMap> map = stats.toMap().get(cat); - - if(map.size == 0) continue; - - table.add("@category." + cat.name()).color(Pal.accent).fillX(); - table.row(); - - for(BlockStat stat : map.keys()){ - table.table(inset -> { - inset.left(); - inset.add("[lightgray]" + stat.localized() + ":[] ").left(); - Seq arr = map.get(stat); - for(StatValue value : arr){ - value.display(inset); - inset.add().size(10f); - } - - }).fillX().padLeft(10); - table.row(); - } - } - } - - public static void displayItem(Table table, Item item){ - - table.table(title -> { - title.image(item.icon(Cicon.xlarge)).size(8 * 6); - title.add("[accent]" + item.localizedName).padLeft(5); - }); - - table.row(); - - table.image().height(3).color(Color.lightGray).pad(15).padLeft(0).padRight(0).fillX(); - - table.row(); - - if(item.description != null){ - table.add(item.displayDescription()).padLeft(5).padRight(5).width(400f).wrap().fillX(); - table.row(); - - table.image().height(3).color(Color.lightGray).pad(15).padLeft(0).padRight(0).fillX(); - table.row(); - } - - table.left().defaults().fillX(); - - table.add(Core.bundle.format("item.explosiveness", (int)(item.explosiveness * 100))); - table.row(); - table.add(Core.bundle.format("item.flammability", (int)(item.flammability * 100))); - table.row(); - table.add(Core.bundle.format("item.radioactivity", (int)(item.radioactivity * 100))); - table.row(); - } - - public static void displayLiquid(Table table, Liquid liquid){ - - table.table(title -> { - title.image(liquid.icon(Cicon.xlarge)).size(8 * 6); - title.add("[accent]" + liquid.localizedName).padLeft(5); - }); - - table.row(); - - table.image().height(3).color(Color.lightGray).pad(15).padLeft(0).padRight(0).fillX(); - - table.row(); - - if(liquid.description != null){ - table.add(liquid.displayDescription()).padLeft(5).padRight(5).width(400f).wrap().fillX(); - table.row(); - - table.image().height(3).color(Color.lightGray).pad(15).padLeft(0).padRight(0).fillX(); - table.row(); - } - - table.left().defaults().fillX(); - - table.add(Core.bundle.format("item.explosiveness", (int)(liquid.explosiveness * 100))); - table.row(); - table.add(Core.bundle.format("item.flammability", (int)(liquid.flammability * 100))); - table.row(); - table.add(Core.bundle.format("liquid.heatcapacity", (int)(liquid.heatCapacity * 100))); - table.row(); - table.add(Core.bundle.format("liquid.temperature", (int)(liquid.temperature * 100))); - table.row(); - table.add(Core.bundle.format("liquid.viscosity", (int)(liquid.viscosity * 100))); - table.row(); - } - - public static void displayUnit(Table table, UnitType unit){ - table.table(title -> { - title.image(unit.icon(Cicon.xlarge)).size(8 * 6).scaling(Scaling.fit); - title.add("[accent]" + unit.localizedName).padLeft(5); - }); - - table.row(); - - table.image().height(3).color(Color.lightGray).pad(15).padLeft(0).padRight(0).fillX(); - - table.row(); - - if(unit.description != null){ - table.add(unit.displayDescription()).padLeft(5).padRight(5).width(400f).wrap().fillX(); - table.row(); - - table.image().height(3).color(Color.lightGray).pad(15).padLeft(0).padRight(0).fillX(); - table.row(); - } - - table.left().defaults().fillX(); - - Unit inst = unit.constructor.get(); - - //TODO more stats - table.add(Core.bundle.format("unit.health", unit.health)).row(); - table.add(Core.bundle.format("unit.speed", Strings.fixed(unit.speed, 1))).row(); - table.add(Core.bundle.format("unit.itemcapacity", unit.itemCapacity)).row(); - - if(inst instanceof Minerc) table.add(Core.bundle.format("unit.minespeed", (int)(unit.mineSpeed * 100f))).row(); - if(inst instanceof Builderc) table.add(Core.bundle.format("unit.buildspeed", (int)(unit.buildSpeed * 100f))).row(); - - table.row(); - } -} diff --git a/core/src/mindustry/ui/IntFormat.java b/core/src/mindustry/ui/IntFormat.java index 2ca0e9de1f..fab10cd67f 100644 --- a/core/src/mindustry/ui/IntFormat.java +++ b/core/src/mindustry/ui/IntFormat.java @@ -10,7 +10,7 @@ import arc.func.*; public class IntFormat{ private final StringBuilder builder = new StringBuilder(); private final String text; - private int lastValue = Integer.MIN_VALUE; + private int lastValue = Integer.MIN_VALUE, lastValue2 = Integer.MIN_VALUE; private Func converter = String::valueOf; public IntFormat(String text){ @@ -30,4 +30,14 @@ public class IntFormat{ lastValue = value; return builder; } + + public CharSequence get(int value1, int value2){ + if(lastValue != value1 || lastValue2 != value2){ + builder.setLength(0); + builder.append(Core.bundle.format(text, value1, value2)); + } + lastValue = value1; + lastValue2 = value2; + return builder; + } } diff --git a/core/src/mindustry/ui/Styles.java b/core/src/mindustry/ui/Styles.java index 13e97202e5..802a18004b 100644 --- a/core/src/mindustry/ui/Styles.java +++ b/core/src/mindustry/ui/Styles.java @@ -23,6 +23,7 @@ import static mindustry.gen.Tex.*; @StyleDefaults public class Styles{ + //TODO all these names are inconsistent and not descriptive public static Drawable black, black9, black8, black6, black3, black5, none, flatDown, flatOver; public static ButtonStyle defaultb, waveb; public static TextButtonStyle defaultt, squaret, nodet, cleart, discordt, infot, clearPartialt, clearTogglet, clearToggleMenut, togglet, transt, fullTogglet, logict; diff --git a/core/src/mindustry/ui/dialogs/ContentInfoDialog.java b/core/src/mindustry/ui/dialogs/ContentInfoDialog.java index 0ee1960f9f..89cf556cb1 100644 --- a/core/src/mindustry/ui/dialogs/ContentInfoDialog.java +++ b/core/src/mindustry/ui/dialogs/ContentInfoDialog.java @@ -1,8 +1,14 @@ package mindustry.ui.dialogs; +import arc.graphics.*; import arc.scene.ui.*; import arc.scene.ui.layout.*; +import arc.struct.*; +import arc.util.*; import mindustry.ctype.*; +import mindustry.graphics.*; +import mindustry.ui.*; +import mindustry.world.meta.*; public class ContentInfoDialog extends BaseDialog{ @@ -18,11 +24,62 @@ public class ContentInfoDialog extends BaseDialog{ Table table = new Table(); table.margin(10); - content.displayInfo(table); + //initialize stats if they haven't been yet + content.checkStats(); + + table.table(title1 -> { + int size = 8 * 6; + + title1.image(content.icon(Cicon.xlarge)).size(size).scaling(Scaling.fit); + title1.add("[accent]" + content.localizedName).padLeft(5); + }); + + table.row(); + + table.image().height(3).color(Color.lightGray).pad(8).padLeft(0).padRight(0).fillX(); + + table.row(); + + if(content.description != null){ + table.add(content.displayDescription()).padLeft(5).padRight(5).width(400f).wrap().fillX(); + table.row(); + + table.image().height(3).color(Color.lightGray).pad(8).padLeft(0).padRight(0).fillX(); + table.row(); + } + + Stats stats = content.stats; + + for(StatCat cat : stats.toMap().keys()){ + OrderedMap> map = stats.toMap().get(cat); + + if(map.size == 0) continue; + + //TODO check + if(stats.useCategories){ + table.add("@category." + cat.name()).color(Pal.accent).fillX(); + table.row(); + } + + for(Stat stat : map.keys()){ + table.table(inset -> { + inset.left(); + inset.add("[lightgray]" + stat.localized() + ":[] ").left(); + Seq arr = map.get(stat); + for(StatValue value : arr){ + value.display(inset); + inset.add().size(10f); + } + + }).fillX().padLeft(10); + table.row(); + } + } ScrollPane pane = new ScrollPane(table); cont.add(pane); show(); } + } diff --git a/core/src/mindustry/ui/dialogs/GameOverDialog.java b/core/src/mindustry/ui/dialogs/GameOverDialog.java index b9967e6564..ceea590826 100644 --- a/core/src/mindustry/ui/dialogs/GameOverDialog.java +++ b/core/src/mindustry/ui/dialogs/GameOverDialog.java @@ -2,7 +2,7 @@ package mindustry.ui.dialogs; import arc.*; import mindustry.game.EventType.*; -import mindustry.game.Stats.*; +import mindustry.game.GameStats.*; import mindustry.game.*; import mindustry.type.*; import mindustry.ui.*; diff --git a/core/src/mindustry/ui/dialogs/HostDialog.java b/core/src/mindustry/ui/dialogs/HostDialog.java index 25300fc0b1..1282d47b7d 100644 --- a/core/src/mindustry/ui/dialogs/HostDialog.java +++ b/core/src/mindustry/ui/dialogs/HostDialog.java @@ -76,13 +76,15 @@ public class HostDialog extends BaseDialog{ platform.updateLobby(); }); })); + + if(Version.modifier.contains("beta") || Version.modifier.contains("alpha")){ + Core.settings.put("publichost", false); + platform.updateLobby(); + Core.settings.getBoolOnce("betapublic", () -> ui.showInfo("@public.beta")); + } } - if(Version.modifier.contains("beta")){ - Core.settings.put("publichost", false); - platform.updateLobby(); - Core.settings.getBoolOnce("betapublic", () -> ui.showInfo("@public.beta")); - } + }catch(IOException e){ ui.showException("@server.error", e); } diff --git a/core/src/mindustry/ui/dialogs/JoinDialog.java b/core/src/mindustry/ui/dialogs/JoinDialog.java index 1c80418708..aa4d9c22c8 100644 --- a/core/src/mindustry/ui/dialogs/JoinDialog.java +++ b/core/src/mindustry/ui/dialogs/JoinDialog.java @@ -367,8 +367,10 @@ public class JoinDialog extends BaseDialog{ local.row(); - TextButton button = local.button("", Styles.cleart, () -> safeConnect(host.address, host.port, host.version)) - .width(w).pad(5f).get(); + TextButton button = local.button("", Styles.cleart, () -> { + Events.fire(new ClientPreConnectEvent(host)); + safeConnect(host.address, host.port, host.version); + }).width(w).pad(5f).get(); button.clearChildren(); buildServer(host, button); } @@ -379,8 +381,10 @@ public class JoinDialog extends BaseDialog{ global.row(); - TextButton button = global.button("", Styles.cleart, () -> safeConnect(host.address, host.port, host.version)) - .width(w).pad(5f).get(); + TextButton button = global.button("", Styles.cleart, () -> { + Events.fire(new ClientPreConnectEvent(host)); + safeConnect(host.address, host.port, host.version); + }).width(w).pad(5f).get(); button.clearChildren(); buildServer(host, button); } diff --git a/core/src/mindustry/ui/dialogs/LaunchLoadoutDialog.java b/core/src/mindustry/ui/dialogs/LaunchLoadoutDialog.java index acb240d7c9..62db818bf7 100644 --- a/core/src/mindustry/ui/dialogs/LaunchLoadoutDialog.java +++ b/core/src/mindustry/ui/dialogs/LaunchLoadoutDialog.java @@ -2,6 +2,7 @@ package mindustry.ui.dialogs; import arc.*; import arc.func.*; +import arc.input.*; import arc.scene.ui.*; import arc.scene.ui.layout.*; import arc.struct.*; @@ -29,18 +30,27 @@ public class LaunchLoadoutDialog extends BaseDialog{ super("@configure"); } - public void show(CoreBlock core, Building build, Runnable confirm){ + public void show(CoreBlock core, Sector sector, Runnable confirm){ cont.clear(); buttons.clear(); - addCloseButton(); + buttons.defaults().size(160f, 64f); + buttons.button("@back", Icon.left, this::hide); + + keyDown(key -> { + if(key == KeyCode.escape || key == KeyCode.back){ + Core.app.post(this::hide); + } + }); + + ItemSeq sitems = sector.getItems(); //updates sum requirements Runnable update = () -> { total.clear(); selected.requirements().each(total::add); universe.getLaunchResources().each(total::add); - valid = build.items.has(total); + valid = sitems.has(total); }; Cons rebuild = table -> { @@ -57,8 +67,8 @@ public class LaunchLoadoutDialog extends BaseDialog{ String amountStr = "[lightgray]" + (al + " + [accent]" + as + "[lightgray]"); table.add( - build.items.has(s.item, s.amount) ? amountStr : - "[scarlet]" + (Math.min(build.items.get(s.item), s.amount) + "[lightgray]/" + amountStr)).padLeft(2).left().padRight(4); + sitems.has(s.item, s.amount) ? amountStr : + "[scarlet]" + (Math.min(sitems.get(s.item), s.amount) + "[lightgray]/" + amountStr)).padLeft(2).left().padRight(4); if(++i % 4 == 0){ table.row(); @@ -79,7 +89,7 @@ public class LaunchLoadoutDialog extends BaseDialog{ update.run(); rebuildItems.run(); }); - }); + }).width(204); buttons.button("@launch.text", Icon.ok, () -> { universe.updateLoadout(core, selected); @@ -100,7 +110,7 @@ public class LaunchLoadoutDialog extends BaseDialog{ selected = s; update.run(); rebuildItems.run(); - }).group(group).pad(4).disabled(!build.items.has(s.requirements())).checked(s == selected).size(200f); + }).group(group).pad(4).disabled(!sitems.has(s.requirements())).checked(s == selected).size(200f); if(++i % cols == 0){ t.row(); diff --git a/core/src/mindustry/ui/dialogs/PausedDialog.java b/core/src/mindustry/ui/dialogs/PausedDialog.java index f09e2fe185..fce68f3044 100644 --- a/core/src/mindustry/ui/dialogs/PausedDialog.java +++ b/core/src/mindustry/ui/dialogs/PausedDialog.java @@ -34,14 +34,6 @@ public class PausedDialog extends BaseDialog{ }); if(!mobile){ - //TODO localize - //TODO capturing is disabled, remove? - //cont.label(() -> state.getSector() == null ? "" : - //("[lightgray]Next turn in [accent]" + state.getSector().displayTimeRemaining() + - // (state.rules.winWave > 0 && !state.getSector().isCaptured() ? "\n[lightgray]Reach wave[accent] " + state.rules.winWave + "[] to capture" : ""))) - // .visible(() -> state.getSector() != null).colspan(2); - cont.row(); - float dw = 220f; cont.defaults().width(dw).height(55).pad(5f); @@ -86,10 +78,7 @@ public class PausedDialog extends BaseDialog{ cont.buttonRow("@load", Icon.download, load::show).disabled(b -> net.active()); }else if(state.isCampaign()){ - cont.buttonRow("@launchcore", Icon.up, () -> { - hide(); - ui.planet.showLaunch(state.getSector(), player.team().core()); - }).disabled(b -> player.team().core() == null); + cont.buttonRow("@research", Icon.tree, ui.research::show); cont.row(); diff --git a/core/src/mindustry/ui/dialogs/PlanetDialog.java b/core/src/mindustry/ui/dialogs/PlanetDialog.java index 4d24bbe8b8..d14314977b 100644 --- a/core/src/mindustry/ui/dialogs/PlanetDialog.java +++ b/core/src/mindustry/ui/dialogs/PlanetDialog.java @@ -13,6 +13,7 @@ import arc.scene.event.*; import arc.scene.ui.*; import arc.scene.ui.layout.*; import arc.util.*; +import mindustry.content.*; import mindustry.core.*; import mindustry.ctype.*; import mindustry.game.*; @@ -22,7 +23,6 @@ import mindustry.graphics.g3d.*; import mindustry.type.*; import mindustry.ui.*; import mindustry.world.blocks.storage.*; -import mindustry.world.blocks.storage.CoreBlock.*; import static mindustry.Vars.*; import static mindustry.graphics.g3d.PlanetRenderer.*; @@ -40,7 +40,6 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ public int launchRange; public float zoom = 1f, selectAlpha = 1f; public @Nullable Sector selected, hovered, launchSector; - public CoreBuild launcher; public Mode mode = look; public boolean launching; public Cons listener = s -> {}; @@ -91,9 +90,16 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ mode = look; selected = hovered = launchSector = null; launching = false; + + zoom = 1f; + planets.zoom = 1f; + selectAlpha = 0f; + launchSector = state.getSector(); + if(planets.planet.getLastSector() != null){ lookAt(planets.planet.getLastSector()); } + return super.show(); } @@ -106,7 +112,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ //update view to sector lookAt(sector); zoom = 1f; - planets.zoom = 2f; + planets.zoom = 1f; selectAlpha = 0f; launchSector = sector; @@ -115,37 +121,33 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ super.show(); } - public void showLaunch(Sector sector, CoreBuild launcher){ - if(launcher == null) return; - - this.launcher = launcher; - selected = null; - hovered = null; - launching = false; - - //update view to sector - lookAt(sector); - zoom = 1f; - planets.zoom = 2f; - selectAlpha = 0f; - launchRange = ((CoreBlock)launcher.block).launchRange; - launchSector = sector; - - mode = launch; - - super.show(); - } - - private void lookAt(Sector sector){ + void lookAt(Sector sector){ planets.camPos.set(Tmp.v33.set(sector.tile.v).rotate(Vec3.Y, -sector.planet.getRotation())); } boolean canSelect(Sector sector){ if(mode == select) return sector.hasBase(); - return mode == launch && - (sector.tile.v.within(launchSector.tile.v, (launchRange + 0.5f) * planets.planet.sectorApproxRadius*2) //within range - || (sector.preset != null && sector.preset.unlocked())); //is an unlocked preset + return sector.near().contains(Sector::hasBase)//(sector.tile.v.within(launchSector.tile.v, (launchRange + 0.5f) * planets.planet.sectorApproxRadius*2) //within range + || (sector.preset != null && sector.preset.unlocked()); //is an unlocked preset + } + + Sector findLauncher(Sector to){ + //directly nearby. + if(to.near().contains(launchSector)) return launchSector; + + Sector launchFrom = launchSector; + if(launchFrom == null){ + //TODO pick one with the most resources + launchFrom = to.near().find(Sector::hasBase); + if(launchFrom == null && to.preset != null){ + if(launchSector != null) return launchSector; + launchFrom = planets.planet.sectors.min(s -> !s.hasBase() ? Float.MAX_VALUE : s.tile.v.dst2(to.tile.v)); + if(!launchFrom.hasBase()) launchFrom = null; + } + } + + return launchFrom; } @Override @@ -157,9 +159,6 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ if(selectAlpha > 0.01f){ if(canSelect(sec) || sec.unlocked()){ - if(sec.baseCoverage > 0){ - planets.fill(sec, Tmp.c1.set(Team.crux.color).a(0.5f * sec.baseCoverage * selectAlpha), -0.002f); - } Color color = sec.hasBase() ? Team.sharded.color : @@ -177,8 +176,10 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ } } - if(launchSector != null){ - planets.fill(launchSector, hoverColor, -0.001f); + Sector current = state.getSector() != null && state.getSector().isBeingPlayed() ? state.getSector() : null; + + if(current != null){ + planets.fill(current, hoverColor, -0.001f); } //draw hover border @@ -195,9 +196,10 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ planets.batch.flush(Gl.triangles); - if(mode == launch || mode == select){ - if(hovered != launchSector && hovered != null && canSelect(hovered)){ - planets.drawArc(planet, launchSector.tile.v, hovered.tile.v); + if(hovered != null && !hovered.hasBase()){ + Sector launchFrom = findLauncher(hovered); + if(launchFrom != null && hovered != launchFrom && canSelect(hovered)){ + planets.drawArc(planet, launchFrom.tile.v, hovered.tile.v); } } @@ -217,9 +219,9 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ public void renderProjections(){ if(hovered != null){ planets.drawPlane(hovered, () -> { - Draw.color(Color.white, Pal.accent, Mathf.absin(5f, 1f)); + Draw.color(hovered.isAttacked() ? Pal.remove : Color.white, Pal.accent, Mathf.absin(5f, 1f)); - TextureRegion icon = hovered.locked() && !canSelect(hovered) ? Icon.lock.getRegion() : null; + TextureRegion icon = hovered.locked() && !canSelect(hovered) ? Icon.lock.getRegion() : hovered.isAttacked() ? Icon.warning.getRegion() : null; if(icon != null){ Draw.rect(icon, 0, 0); @@ -244,7 +246,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ addListener(new ElementGestureListener(){ @Override public void tap(InputEvent event, float x, float y, int count, KeyCode button){ - if(hovered != null && ((mode == launch ? canSelect(hovered) && hovered != launchSector : hovered.unlocked()) || debugSelect)){ + if(hovered != null && ((mode == look ? canSelect(hovered) && hovered != launchSector : hovered.unlocked()) || debugSelect)){ selected = hovered; } @@ -263,9 +265,8 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ }, new Table(t -> { t.touchable = Touchable.disabled; - //TODO localize t.top(); - t.label(() -> mode == select ? "@sectors.select" : mode == launch ? "Select Launch Sector" : "").style(Styles.outlineLabel).color(Pal.accent); + t.label(() -> mode == select ? "@sectors.select" : "").style(Styles.outlineLabel).color(Pal.accent); }), new Table(t -> { t.right(); @@ -322,7 +323,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ stable.toFront(); //smooth camera toward the sector - if(mode == launch && launching){ + if(mode == look && launching){ float len = planets.camPos.len(); planets.camPos.slerp(Tmp.v31.set(selected.tile.v).rotate(Vec3.Y,-selected.planet.getRotation()).setLength(len), 0.1f); } @@ -352,69 +353,76 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ stable.clear(); stable.background(Styles.black6); - stable.add("[accent]" + (sector.preset == null ? sector.id : sector.preset.localizedName)).row(); + stable.table(title -> { + title.add("[accent]" + sector.name()); + if(sector.preset == null){ + title.button(Icon.pencilSmall, Styles.clearPartiali, () -> { + ui.showTextInput("@sectors.rename", "@name", 20, sector.name(), v -> { + sector.setName(v); + updateSelected(); + }); + }).size(40f).padLeft(4); + } + }).row(); + stable.image().color(Pal.accent).fillX().height(3f).pad(3f).row(); stable.add(sector.save != null ? sector.save.getPlayTime() : "@sectors.unexplored").row(); - if(sector.hasWaves() || sector.hasEnemyBase()){ + + if(sector.isAttacked() || sector.hasEnemyBase()){ stable.add("[accent]Difficulty: " + (int)(sector.baseCoverage * 10)).row(); } - //TODO sector damage is disabled, remove when finalized - /* - if(sector.hasBase() && sector.hasWaves()){ + if(sector.isAttacked()){ //TODO localize when finalized //these mechanics are likely to change and as such are not added to the bundle stable.add("[scarlet]Under attack!"); stable.row(); - stable.add("[accent]" + Mathf.ceil(sectorDestructionTurns - (sector.getSecondsPassed() * 60) / turnDuration) + " turn(s)\nuntil destruction"); + stable.add("[accent]" + (int)(sector.info.damage * 100) + "% damaged"); stable.row(); - }*/ + } - if(sector.save != null){ + if(sector.save != null && sector.info.resources.any()){ stable.add("@sectors.resources").row(); stable.table(t -> { - - if(sector.save != null && sector.save.meta.secinfo != null && sector.save.meta.secinfo.resources.any()){ - t.left(); - int idx = 0; - int max = 5; - for(UnlockableContent c : sector.save.meta.secinfo.resources){ - t.image(c.icon(Cicon.small)).padRight(3); - if(++idx % max == 0) t.row(); - } - }else{ - t.add("@unknown").color(Color.lightGray); + t.left(); + int idx = 0; + int max = 5; + for(UnlockableContent c : sector.info.resources){ + t.image(c.icon(Cicon.small)).padRight(3); + if(++idx % max == 0) t.row(); } - - }).fillX().row(); } //production - if(sector.hasBase() && sector.save.meta.hasProduction){ - stable.add("@sectors.production").row(); - stable.table(t -> { - t.left(); + if(sector.hasBase()){ + Table t = new Table().left(); - sector.save.meta.secinfo.production.each((item, stat) -> { - int total = (int)(stat.mean * 60); - if(total > 1){ - t.image(item.icon(Cicon.small)).padRight(3); - t.add(UI.formatAmount(total) + " " + Core.bundle.get("unit.perminute")).color(Color.lightGray); - t.row(); - } - }); - }).row(); + float scl = sector.getProductionScale(); + + sector.info.production.each((item, stat) -> { + int total = (int)(stat.mean * 60 * scl); + if(total > 1){ + t.image(item.icon(Cicon.small)).padRight(3); + t.add(UI.formatAmount(total) + " " + Core.bundle.get("unit.perminute")).color(Color.lightGray); + t.row(); + } + }); + + if(t.getChildren().any()){ + stable.add("@sectors.production").row(); + stable.add(t).row(); + } } //stored resources - if(sector.hasBase() && sector.save.meta.secinfo.coreItems.total > 0){ + if(sector.hasBase() && sector.info.items.total > 0){ stable.add("@sectors.stored").row(); stable.table(t -> { t.left(); t.table(res -> { - ItemSeq items = sector.calculateItems(); + ItemSeq items = sector.items(); int i = 0; for(ItemStack stack : items){ @@ -446,17 +454,26 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ } } - if(mode == launch && !sector.hasBase()){ - Sector current = state.rules.sector; + if(mode == look && !sector.hasBase()){ shouldHide = false; - loadouts.show((CoreBlock)launcher.block, launcher, () -> { - control.handleLaunch(launcher); - launching = true; - zoom = 0.5f; + Sector from = findLauncher(sector); + if(from == null){ + //free launch. + control.playSector(sector); + }else{ + CoreBlock block = from.info.bestCoreType instanceof CoreBlock b ? b : (CoreBlock)Blocks.coreShard; - ui.hudfrag.showLaunchDirect(); - Time.runTask(launchDuration, () -> control.playSector(current, sector)); - }); + loadouts.show(block, from, () -> { + from.removeItems(universe.getLastLoadout().requirements()); + from.removeItems(universe.getLaunchResources()); + + launching = true; + zoom = 0.5f; + + ui.hudfrag.showLaunchDirect(); + Time.runTask(launchDuration, () -> control.playSector(from, sector)); + }); + } }else if(mode == select){ listener.get(sector); }else{ @@ -484,7 +501,6 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ selected = null; } } - } }); @@ -494,8 +510,6 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ public enum Mode{ /** Look around for existing sectors. Can only deploy. */ look, - /** Launch to a new location. */ - launch, /** Select a sector for some purpose. */ select } diff --git a/core/src/mindustry/ui/dialogs/ResearchDialog.java b/core/src/mindustry/ui/dialogs/ResearchDialog.java index 9c5e6fd3ed..b79aacbce5 100644 --- a/core/src/mindustry/ui/dialogs/ResearchDialog.java +++ b/core/src/mindustry/ui/dialogs/ResearchDialog.java @@ -60,7 +60,7 @@ public class ResearchDialog extends BaseDialog{ for(Planet planet : content.planets()){ for(Sector sector : planet.sectors){ if(sector.hasSave()){ - ItemSeq cached = sector.calculateItems(); + ItemSeq cached = sector.items(); add(cached); cache.put(sector, cached); } @@ -164,13 +164,10 @@ public class ResearchDialog extends BaseDialog{ @Override public Dialog show(){ - Core.app.post(() -> { - if(net.client()){ - //TODO make this not display every time - //TODO rework this in the future - ui.showInfo("@campaign.multiplayer"); - } - }); + if(net.client()){ + ui.showInfo("@research.multiplayer"); + return null; + } return super.show(); } diff --git a/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java b/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java index 8dd9753fda..c00076d92b 100644 --- a/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java +++ b/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java @@ -250,7 +250,6 @@ public class SettingsMenuDialog extends SettingsDialog{ if(!mobile){ game.checkPref("buildautopause", false); } - game.checkPref("mapcenter", true); if(steam){ game.sliderPref("playerlimit", 16, 2, 32, i -> { @@ -292,7 +291,7 @@ public class SettingsMenuDialog extends SettingsDialog{ } return s + "%"; }); - graphics.sliderPref("bridgeopacity", 75, 0, 100, 5, s -> s + "%"); + graphics.sliderPref("bridgeopacity", 100, 0, 100, 5, s -> s + "%"); if(!mobile){ graphics.checkPref("vsync", true, b -> Core.graphics.setVSync(b)); @@ -340,9 +339,6 @@ public class SettingsMenuDialog extends SettingsDialog{ graphics.checkPref("smoothcamera", true); graphics.checkPref("position", false); graphics.checkPref("fps", false); - if(!mobile){ - graphics.checkPref("blockselectkeys", true); - } graphics.checkPref("playerindicators", true); graphics.checkPref("indicators", true); graphics.checkPref("animatedwater", true); diff --git a/core/src/mindustry/ui/fragments/HudFragment.java b/core/src/mindustry/ui/fragments/HudFragment.java index f56d507c4d..d51c83881d 100644 --- a/core/src/mindustry/ui/fragments/HudFragment.java +++ b/core/src/mindustry/ui/fragments/HudFragment.java @@ -54,7 +54,7 @@ public class HudFragment extends Fragment{ outer: for(int i = state.wave - 1; i <= state.wave + max; i++){ for(SpawnGroup group : state.rules.spawns){ - if(group.effect == StatusEffects.boss && group.getUnitsSpawned(i) > 0){ + if(group.effect == StatusEffects.boss && group.getSpawned(i) > 0){ int diff = (i + 2) - state.wave; //increments at which to warn about incoming guardian @@ -71,12 +71,17 @@ public class HudFragment extends Fragment{ //TODO details and stuff Events.on(SectorCaptureEvent.class, e ->{ //TODO localize - showToast("Sector[accent] captured[]!"); + showToast("Sector [accent]" + (e.sector.isBeingPlayed() ? "" : e.sector.name() + " ") + "[white]captured!"); }); //TODO localize Events.on(SectorLoseEvent.class, e -> { - showToast(Icon.warning, "Sector " + e.sector.id + " [scarlet]lost!"); + showToast(Icon.warning, "Sector [accent]" + e.sector.name() + "[white] lost!"); + }); + + //TODO localize + Events.on(SectorInvasionEvent.class, e -> { + showToast(Icon.warning, "Sector [accent]" + e.sector.name() + "[white] under attack!"); }); Events.on(ResetEvent.class, e -> { @@ -589,6 +594,7 @@ public class HudFragment extends Fragment{ StringBuilder ibuild = new StringBuilder(); IntFormat wavef = new IntFormat("wave"); + IntFormat wavefc = new IntFormat("wave.cap"); IntFormat enemyf = new IntFormat("wave.enemy"); IntFormat enemiesf = new IntFormat("wave.enemies"); IntFormat waitingf = new IntFormat("wave.waiting", i -> { @@ -706,7 +712,7 @@ public class HudFragment extends Fragment{ t.add(new SideBar(() -> player.unit().healthf(), () -> true, true)).width(bw).growY().padRight(pad); t.image(() -> player.icon()).scaling(Scaling.bounded).grow().maxWidth(54f); t.add(new SideBar(() -> player.dead() ? 0f : player.displayAmmo() ? player.unit().ammof() : player.unit().healthf(), () -> !player.displayAmmo(), false)).width(bw).growY().padLeft(pad).update(b -> { - b.color.set(player.displayAmmo() ? player.dead() || player.unit() instanceof BlockUnitc ? Pal.ammo : player.unit().type().ammoType.color : Pal.health); + b.color.set(player.displayAmmo() ? player.dead() || player.unit() instanceof BlockUnitc ? Pal.ammo : player.unit().type.ammoType.color : Pal.health); }); t.getChildren().get(1).toFront(); @@ -714,7 +720,11 @@ public class HudFragment extends Fragment{ table.labelWrap(() -> { builder.setLength(0); - builder.append(wavef.get(state.wave)); + if(state.rules.winWave > 1 && state.rules.winWave >= state.wave && state.isCampaign()){ + builder.append(wavefc.get(state.wave, state.rules.winWave)); + }else{ + builder.append(wavef.get(state.wave)); + } builder.append("\n"); if(state.enemies > 0){ @@ -727,7 +737,7 @@ public class HudFragment extends Fragment{ } if(state.rules.waveTimer){ - builder.append((logic.isWaitingWave() ? Core.bundle.get("wave.waveInProgress") : ( waitingf.get((int)(state.wavetime/60))))); + builder.append((logic.isWaitingWave() ? Core.bundle.get("wave.waveInProgress") : (waitingf.get((int)(state.wavetime/60))))); }else if(state.enemies == 0){ builder.append(Core.bundle.get("waiting")); } diff --git a/core/src/mindustry/ui/fragments/MinimapFragment.java b/core/src/mindustry/ui/fragments/MinimapFragment.java index 43ad3163b0..5acf3bc6d3 100644 --- a/core/src/mindustry/ui/fragments/MinimapFragment.java +++ b/core/src/mindustry/ui/fragments/MinimapFragment.java @@ -111,12 +111,10 @@ public class MinimapFragment extends Fragment{ } public void toggle(){ - if(Core.settings.getBool("mapcenter")){ - float size = baseSize * zoom * world.width(); - float ratio = (float)renderer.minimap.getTexture().height / renderer.minimap.getTexture().width; - panx = (size/2f - player.x() / (world.width() * tilesize) * size) / zoom; - pany = (size*ratio/2f - player.y() / (world.height() * tilesize) * size*ratio) / zoom; - } + float size = baseSize * zoom * world.width(); + float ratio = (float)renderer.minimap.getTexture().height / renderer.minimap.getTexture().width; + panx = (size/2f - player.x() / (world.width() * tilesize) * size) / zoom; + pany = (size*ratio/2f - player.y() / (world.height() * tilesize) * size*ratio) / zoom; shown = !shown; } } diff --git a/core/src/mindustry/ui/fragments/PlacementFragment.java b/core/src/mindustry/ui/fragments/PlacementFragment.java index 7b53a24012..25c009ff63 100644 --- a/core/src/mindustry/ui/fragments/PlacementFragment.java +++ b/core/src/mindustry/ui/fragments/PlacementFragment.java @@ -289,7 +289,7 @@ public class PlacementFragment extends Fragment{ topTable.table(header -> { String keyCombo = ""; - if(!mobile && Core.settings.getBool("blockselectkeys")){ + if(!mobile){ Seq blocks = getByCategory(currentCategory); for(int i = 0; i < blocks.size; i++){ if(blocks.get(i) == displayBlock && (i + 1) / 10 - 1 < blockSelect.length){ diff --git a/core/src/mindustry/world/Block.java b/core/src/mindustry/world/Block.java index 95d9a426e0..09814e1333 100644 --- a/core/src/mindustry/world/Block.java +++ b/core/src/mindustry/world/Block.java @@ -14,6 +14,7 @@ import arc.struct.*; import arc.util.*; import arc.util.pooling.*; import mindustry.annotations.Annotations.*; +import mindustry.content.*; import mindustry.core.*; import mindustry.ctype.*; import mindustry.entities.*; @@ -35,8 +36,6 @@ import java.util.*; import static mindustry.Vars.*; public class Block extends UnlockableContent{ - public static final int crackRegions = 8, maxCrackSize = 9; - public boolean hasItems; public boolean hasLiquids; public boolean hasPower; @@ -52,7 +51,6 @@ public class Block extends UnlockableContent{ public float liquidCapacity = 10f; public float liquidPressure = 1f; - public final BlockStats stats = new BlockStats(); public final BlockBars bars = new BlockBars(); public final Consumers consumes = new Consumers(); @@ -212,8 +210,6 @@ public class Block extends UnlockableContent{ public @Load("@-team") TextureRegion teamRegion; public TextureRegion[] teamRegions; - //TODO make this not static - public static TextureRegion[][] cracks; protected static final Seq tempTiles = new Seq<>(); protected static final Seq tempTileEnts = new Seq<>(); @@ -324,27 +320,30 @@ public class Block extends UnlockableContent{ return update || destructible; } + @Override public void setStats(){ - stats.add(BlockStat.size, "@x@", size, size); - stats.add(BlockStat.health, health, StatUnit.none); + super.setStats(); + + stats.add(Stat.size, "@x@", size, size); + stats.add(Stat.health, health, StatUnit.none); if(canBeBuilt()){ - stats.add(BlockStat.buildTime, buildCost / 60, StatUnit.seconds); - stats.add(BlockStat.buildCost, new ItemListValue(false, requirements)); + stats.add(Stat.buildTime, buildCost / 60, StatUnit.seconds); + stats.add(Stat.buildCost, new ItemListValue(false, requirements)); } if(instantTransfer){ - stats.add(BlockStat.maxConsecutive, 2, StatUnit.none); + stats.add(Stat.maxConsecutive, 2, StatUnit.none); } consumes.display(stats); - // Note: Power stats are added by the consumers. - if(hasLiquids) stats.add(BlockStat.liquidCapacity, liquidCapacity, StatUnit.liquidUnits); - if(hasItems && itemCapacity > 0) stats.add(BlockStat.itemCapacity, itemCapacity, StatUnit.items); + //Note: Power stats are added by the consumers. + if(hasLiquids) stats.add(Stat.liquidCapacity, liquidCapacity, StatUnit.liquidUnits); + if(hasItems && itemCapacity > 0) stats.add(Stat.itemCapacity, itemCapacity, StatUnit.items); } public void setBars(){ - bars.add("health", entity -> new Bar("blocks.health", Pal.health, entity::healthf).blink(Color.white)); + bars.add("health", entity -> new Bar("stat.health", Pal.health, entity::healthf).blink(Color.white)); if(hasLiquids){ Func current; @@ -352,7 +351,7 @@ public class Block extends UnlockableContent{ Liquid liquid = consumes.get(ConsumeType.liquid).liquid; current = entity -> liquid; }else{ - current = entity -> entity.liquids.current(); + current = entity -> entity.liquids == null ? Liquids.water : entity.liquids.current(); } bars.add("liquid", entity -> new Bar(() -> entity.liquids.get(current.get(entity)) <= 0.001f ? Core.bundle.get("bar.liquid") : current.get(entity).localizedName, () -> current.get(entity).barColor(), () -> entity.liquids.get(current.get(entity)) / liquidCapacity)); @@ -621,7 +620,7 @@ public class Block extends UnlockableContent{ public ItemStack[] researchRequirements(){ ItemStack[] out = new ItemStack[requirements.length]; for(int i = 0; i < out.length; i++){ - int quantity = 40 + Mathf.round(Mathf.pow(requirements[i].amount, 1.25f) * 20 * researchCostMultiplier, 10); + int quantity = 40 + Mathf.round(Mathf.pow(requirements[i].amount, 1.15f) * 20 * researchCostMultiplier, 10); out[i] = new ItemStack(requirements[i].item, UI.roundAmount(quantity)); } @@ -637,11 +636,6 @@ public class Block extends UnlockableContent{ } } - @Override - public void displayInfo(Table table){ - ContentDisplay.displayBlock(table, this); - } - @Override public ContentType getContentType(){ return ContentType.block; @@ -672,9 +666,10 @@ public class Block extends UnlockableContent{ if(consumes.has(ConsumeType.item)) hasItems = true; if(consumes.has(ConsumeType.liquid)) hasLiquids = true; - setStats(); setBars(); + stats.useCategories = true; + consumes.init(); if(!outputsPower && consumes.hasPower() && consumes.getPower().buffered){ @@ -687,15 +682,6 @@ public class Block extends UnlockableContent{ public void load(){ region = Core.atlas.find(name); - if(cracks == null || (cracks[0][0].texture != null && cracks[0][0].texture.isDisposed())){ - cracks = new TextureRegion[maxCrackSize][crackRegions]; - for(int size = 1; size <= maxCrackSize; size++){ - for(int i = 0; i < crackRegions; i++){ - cracks[size - 1][i] = Core.atlas.find("cracks-" + size + "-" + i); - } - } - } - ContentRegions.loadRegions(this); //load specific team regions diff --git a/core/src/mindustry/world/Build.java b/core/src/mindustry/world/Build.java index 070ddab553..98409e6d18 100644 --- a/core/src/mindustry/world/Build.java +++ b/core/src/mindustry/world/Build.java @@ -45,7 +45,7 @@ public class Build{ Core.app.post(() -> Events.fire(new BlockBuildBeginEvent(tile, team, true))); } - /** Places a BuildBlock at this location. */ + /** Places a ConstructBlock at this location. */ @Remote(called = Loc.server) public static void beginPlace(Block result, Team team, int x, int y, int rotation){ if(!validPlace(result, team, x, y, rotation)){ diff --git a/core/src/mindustry/world/CachedTile.java b/core/src/mindustry/world/CachedTile.java index 3c14ade132..5777ece759 100644 --- a/core/src/mindustry/world/CachedTile.java +++ b/core/src/mindustry/world/CachedTile.java @@ -21,7 +21,7 @@ public class CachedTile extends Tile{ } @Override - protected void changeEntity(Team team, Prov entityprov, int rotation){ + protected void changeBuild(Team team, Prov entityprov, int rotation){ build = null; Block block = block(); diff --git a/core/src/mindustry/world/Tile.java b/core/src/mindustry/world/Tile.java index 7478f81db4..18696d8930 100644 --- a/core/src/mindustry/world/Tile.java +++ b/core/src/mindustry/world/Tile.java @@ -45,7 +45,7 @@ public class Tile implements Position, QuadTreeObject, Displayable{ this.block = wall; //update entity and create it if needed - changeEntity(Team.derelict, wall::newBuilding, 0); + changeBuild(Team.derelict, wall::newBuilding, 0); changed(); } @@ -186,7 +186,7 @@ public class Tile implements Position, QuadTreeObject, Displayable{ this.block = type; preChanged(); - changeEntity(team, entityprov, (byte)Mathf.mod(rotation, 4)); + changeBuild(team, entityprov, (byte)Mathf.mod(rotation, 4)); if(build != null){ build.team(team); @@ -267,6 +267,10 @@ public class Tile implements Position, QuadTreeObject, Displayable{ Geometry.circle(x, y, world.width(), world.height(), radius, cons); } + public void circle(int radius, Cons cons){ + circle(radius, (x, y) -> cons.get(world.rawTile(x, y))); + } + public void recache(){ if(!headless && !world.isGenerating()){ renderer.blocks.floor.recacheTile(this); @@ -332,6 +336,11 @@ public class Tile implements Position, QuadTreeObject, Displayable{ recache(); } + /** Sets the overlay without a recache. */ + public void setOverlayQuiet(Block block){ + this.overlay = (Floor)block; + } + public void clearOverlay(){ setOverlayID((short)0); } @@ -421,15 +430,15 @@ public class Tile implements Position, QuadTreeObject, Displayable{ getHitbox(rect); } - public Tile getNearby(Point2 relative){ + public Tile nearby(Point2 relative){ return world.tile(x + relative.x, y + relative.y); } - public Tile getNearby(int dx, int dy){ + public Tile nearby(int dx, int dy){ return world.tile(x + dx, y + dy); } - public Tile getNearby(int rotation){ + public Tile nearby(int rotation){ if(rotation == 0) return world.tile(x + 1, y); if(rotation == 1) return world.tile(x, y + 1); if(rotation == 2) return world.tile(x - 1, y); @@ -437,7 +446,7 @@ public class Tile implements Position, QuadTreeObject, Displayable{ return null; } - public Building getNearbyEntity(int rotation){ + public Building nearbyBuild(int rotation){ if(rotation == 0) return world.build(x + 1, y); if(rotation == 1) return world.build(x, y + 1); if(rotation == 2) return world.build(x - 1, y); @@ -494,7 +503,7 @@ public class Tile implements Position, QuadTreeObject, Displayable{ } } - protected void changeEntity(Team team, Prov entityprov, int rotation){ + protected void changeBuild(Team team, Prov entityprov, int rotation){ if(build != null){ int size = build.block.size; build.remove(); diff --git a/core/src/mindustry/world/blocks/Autotiler.java b/core/src/mindustry/world/blocks/Autotiler.java index 2989436fac..027ac93b31 100644 --- a/core/src/mindustry/world/blocks/Autotiler.java +++ b/core/src/mindustry/world/blocks/Autotiler.java @@ -133,7 +133,7 @@ public interface Autotiler{ for(int i = 0; i < 4; i++){ int realDir = Mathf.mod(rotation - i, 4); - if(blends(tile, rotation, directional, i, world) && (tile != null && tile.getNearbyEntity(realDir) != null && !tile.getNearbyEntity(realDir).block.squareSprite)){ + if(blends(tile, rotation, directional, i, world) && (tile != null && tile.nearbyBuild(realDir) != null && !tile.nearbyBuild(realDir).block.squareSprite)){ blendresult[4] |= (1 << i); } } @@ -194,7 +194,7 @@ public interface Autotiler{ // TODO docs -- use for direction? default boolean blends(Tile tile, int rotation, int direction){ - Building other = tile.getNearbyEntity(Mathf.mod(rotation - direction, 4)); + Building other = tile.nearbyBuild(Mathf.mod(rotation - direction, 4)); return other != null && other.team == tile.team() && blends(tile, rotation, other.tileX(), other.tileY(), other.rotation, other.block); } diff --git a/core/src/mindustry/world/blocks/ConstructBlock.java b/core/src/mindustry/world/blocks/ConstructBlock.java index c228397a32..cefdfa6887 100644 --- a/core/src/mindustry/world/blocks/ConstructBlock.java +++ b/core/src/mindustry/world/blocks/ConstructBlock.java @@ -42,7 +42,7 @@ public class ConstructBlock extends Block{ consBlocks[size - 1] = this; } - /** Returns a BuildBlock by size. */ + /** Returns a ConstructBlock by size. */ public static ConstructBlock get(int size){ if(size > maxSize) throw new IllegalArgumentException("No. Don't place ConstructBlock of size greater than " + maxSize); return consBlocks[size - 1]; diff --git a/core/src/mindustry/world/blocks/ControlBlock.java b/core/src/mindustry/world/blocks/ControlBlock.java index 6724ff9205..7688cdab6a 100644 --- a/core/src/mindustry/world/blocks/ControlBlock.java +++ b/core/src/mindustry/world/blocks/ControlBlock.java @@ -10,4 +10,14 @@ public interface ControlBlock{ default boolean isControlled(){ return unit().isPlayer(); } + + /** @return whether this block can be controlled at all. */ + default boolean canControl(){ + return true; + } + + /** @return whether targets should automatically be selected (on mobile) */ + default boolean shouldAutoTarget(){ + return true; + } } diff --git a/core/src/mindustry/world/blocks/campaign/LaunchPad.java b/core/src/mindustry/world/blocks/campaign/LaunchPad.java index 093fff4f70..ff069acc48 100644 --- a/core/src/mindustry/world/blocks/campaign/LaunchPad.java +++ b/core/src/mindustry/world/blocks/campaign/LaunchPad.java @@ -42,7 +42,7 @@ public class LaunchPad extends Block{ public void setStats(){ super.setStats(); - stats.add(BlockStat.launchTime, launchTime / 60f, StatUnit.seconds); + stats.add(Stat.launchTime, launchTime / 60f, StatUnit.seconds); } @Override @@ -115,16 +115,16 @@ public class LaunchPad extends Block{ public void display(Table table){ super.display(table); + if(!state.isCampaign()) return; + table.row(); table.label(() -> { Sector dest = state.secinfo.getRealDestination(); return Core.bundle.format("launch.destination", dest == null ? Core.bundle.get("sectors.nonelaunch") : - dest.preset == null ? - "[accent]Sector " + dest.id : - "[accent]" + dest.preset.localizedName); - }).pad(4); + "[accent]" + dest.name()); + }).pad(4).wrap().width(200f).left(); } @Override @@ -213,7 +213,7 @@ public class LaunchPad extends Block{ //actually launch the items upon removal if(team() == state.rules.defaultTeam){ if(destsec != null && (destsec != state.rules.sector || net.client())){ - ItemSeq dest = destsec.getExtraItems(); + ItemSeq dest = new ItemSeq(); for(ItemStack stack : stacks){ dest.add(stack); @@ -223,7 +223,7 @@ public class LaunchPad extends Block{ Events.fire(new LaunchItemEvent(stack)); } - destsec.setExtraItems(dest); + destsec.addItems(dest); } } } diff --git a/core/src/mindustry/world/blocks/defense/ForceProjector.java b/core/src/mindustry/world/blocks/defense/ForceProjector.java index b508e90965..46fb01d7d5 100644 --- a/core/src/mindustry/world/blocks/defense/ForceProjector.java +++ b/core/src/mindustry/world/blocks/defense/ForceProjector.java @@ -12,6 +12,7 @@ import mindustry.annotations.Annotations.*; import mindustry.content.*; import mindustry.gen.*; import mindustry.graphics.*; +import mindustry.logic.*; import mindustry.world.*; import mindustry.world.consumers.*; import mindustry.world.meta.*; @@ -60,11 +61,11 @@ public class ForceProjector extends Block{ @Override public void setStats(){ super.setStats(); - stats.add(BlockStat.shieldHealth, breakage, StatUnit.none); - stats.add(BlockStat.cooldownTime, (int) (breakage / cooldownBrokenBase / 60f), StatUnit.seconds); - stats.add(BlockStat.powerUse, basePowerDraw * 60f, StatUnit.powerSecond); - stats.add(BlockStat.boostEffect, phaseRadiusBoost / tilesize, StatUnit.blocks); - stats.add(BlockStat.boostEffect, phaseShieldBoost, StatUnit.shieldHealth); + stats.add(Stat.shieldHealth, breakage, StatUnit.none); + stats.add(Stat.cooldownTime, (int) (breakage / cooldownBrokenBase / 60f), StatUnit.seconds); + stats.add(Stat.powerUse, basePowerDraw * 60f, StatUnit.powerSecond); + stats.add(Stat.boostEffect, phaseRadiusBoost / tilesize, StatUnit.blocks); + stats.add(Stat.boostEffect, phaseShieldBoost, StatUnit.shieldHealth); } @Override @@ -80,11 +81,16 @@ public class ForceProjector extends Block{ Draw.color(); } - public class ForceBuild extends Building{ + public class ForceBuild extends Building implements Ranged{ public boolean broken = true; public float buildup, radscl, hit, warmup, phaseHeat; public ForceDraw drawer; + @Override + public float range(){ + return realRadius(); + } + @Override public void created(){ super.created(); diff --git a/core/src/mindustry/world/blocks/defense/MendProjector.java b/core/src/mindustry/world/blocks/defense/MendProjector.java index a2efa6e8b0..cf71928f26 100644 --- a/core/src/mindustry/world/blocks/defense/MendProjector.java +++ b/core/src/mindustry/world/blocks/defense/MendProjector.java @@ -9,6 +9,7 @@ import mindustry.annotations.Annotations.*; import mindustry.content.*; import mindustry.gen.*; import mindustry.graphics.*; +import mindustry.logic.*; import mindustry.world.*; import mindustry.world.meta.*; @@ -43,11 +44,11 @@ public class MendProjector extends Block{ public void setStats(){ super.setStats(); - stats.add(BlockStat.repairTime, (int)(100f / healPercent * reload / 60f), StatUnit.seconds); - stats.add(BlockStat.range, range / tilesize, StatUnit.blocks); + stats.add(Stat.repairTime, (int)(100f / healPercent * reload / 60f), StatUnit.seconds); + stats.add(Stat.range, range / tilesize, StatUnit.blocks); - stats.add(BlockStat.boostEffect, phaseRangeBoost / tilesize, StatUnit.blocks); - stats.add(BlockStat.boostEffect, (phaseBoost + healPercent) / healPercent, StatUnit.timesSpeed); + stats.add(Stat.boostEffect, phaseRangeBoost / tilesize, StatUnit.blocks); + stats.add(Stat.boostEffect, (phaseBoost + healPercent) / healPercent, StatUnit.timesSpeed); } @Override @@ -55,11 +56,16 @@ public class MendProjector extends Block{ Drawf.dashCircle(x * tilesize + offset, y * tilesize + offset, range, Pal.accent); } - public class MendBuild extends Building{ + public class MendBuild extends Building implements Ranged{ float heat; float charge = Mathf.random(reload); float phaseHeat; + @Override + public float range(){ + return range; + } + @Override public void updateTile(){ heat = Mathf.lerpDelta(heat, consValid() || cheating() ? 1f : 0f, 0.08f); diff --git a/core/src/mindustry/world/blocks/defense/OverdriveProjector.java b/core/src/mindustry/world/blocks/defense/OverdriveProjector.java index 56010c7c0f..7cadaede81 100644 --- a/core/src/mindustry/world/blocks/defense/OverdriveProjector.java +++ b/core/src/mindustry/world/blocks/defense/OverdriveProjector.java @@ -8,6 +8,7 @@ import arc.util.io.*; import mindustry.annotations.Annotations.*; import mindustry.gen.*; import mindustry.graphics.*; +import mindustry.logic.*; import mindustry.world.*; import mindustry.world.meta.*; @@ -50,21 +51,26 @@ public class OverdriveProjector extends Block{ public void setStats(){ super.setStats(); - stats.add(BlockStat.speedIncrease, (int)(100f * speedBoost), StatUnit.percent); - stats.add(BlockStat.range, range / tilesize, StatUnit.blocks); - stats.add(BlockStat.productionTime, useTime / 60f, StatUnit.seconds); + stats.add(Stat.speedIncrease, (int)(100f * speedBoost), StatUnit.percent); + stats.add(Stat.range, range / tilesize, StatUnit.blocks); + stats.add(Stat.productionTime, useTime / 60f, StatUnit.seconds); if(hasBoost){ - stats.add(BlockStat.boostEffect, phaseRangeBoost / tilesize, StatUnit.blocks); - stats.add(BlockStat.boostEffect, (int)((speedBoost + speedBoostPhase) * 100f), StatUnit.percent); + stats.add(Stat.boostEffect, phaseRangeBoost / tilesize, StatUnit.blocks); + stats.add(Stat.boostEffect, (int)((speedBoost + speedBoostPhase) * 100f), StatUnit.percent); } } - public class OverdriveBuild extends Building{ + public class OverdriveBuild extends Building implements Ranged{ float heat; float charge = Mathf.random(reload); float phaseHeat; + @Override + public float range(){ + return range; + } + @Override public void drawLight(){ Drawf.light(team, x, y, 50f * efficiency(), baseColor, 0.7f * efficiency()); diff --git a/core/src/mindustry/world/blocks/defense/Wall.java b/core/src/mindustry/world/blocks/defense/Wall.java index abb941b899..a23a2583cd 100644 --- a/core/src/mindustry/world/blocks/defense/Wall.java +++ b/core/src/mindustry/world/blocks/defense/Wall.java @@ -17,16 +17,17 @@ import static mindustry.Vars.*; public class Wall extends Block{ public int variants = 0; - public float lightningChance = -0.001f; + /** Lighting chance. -1 to disable */ + public float lightningChance = -1f; public float lightningDamage = 20f; public int lightningLength = 17; public Color lightningColor = Pal.surge; public Sound lightningSound = Sounds.spark; - public float chanceDeflect = 10f; + /** Bullet deflection chance. -1 to disable */ + public float chanceDeflect = -1f; public boolean flashHit; public Color flashColor = Color.white; - public boolean deflect; public Sound deflectSound = Sounds.none; public Wall(String name){ @@ -38,6 +39,17 @@ public class Wall extends Block{ canOverdrive = false; } + @Override + public void setStats(){ + super.setStats(); + + if(chanceDeflect > 0f) stats.add(Stat.baseDeflectChance, chanceDeflect, StatUnit.none); + if(lightningChance > 0f){ + stats.add(Stat.lightningChance, lightningChance * 100f, StatUnit.percent); + stats.add(Stat.lightningDamage, lightningDamage, StatUnit.none); + } + } + @Override public void load(){ super.load(); @@ -96,7 +108,7 @@ public class Wall extends Block{ hit = 1f; //create lightning if necessary - if(lightningChance > 0){ + if(lightningChance > 0f){ if(Mathf.chance(lightningChance)){ Lightning.create(team, lightningColor, lightningDamage, x, y, bullet.rotation() + 180f, lightningLength); lightningSound.at(tile, Mathf.random(0.9f, 1.1f)); @@ -104,7 +116,7 @@ public class Wall extends Block{ } //deflect bullets if necessary - if(deflect){ + if(chanceDeflect > 0f){ //slow bullets are not deflected if(bullet.vel().len() <= 0.1f || !bullet.type.reflectable) return true; diff --git a/core/src/mindustry/world/blocks/defense/turrets/BaseTurret.java b/core/src/mindustry/world/blocks/defense/turrets/BaseTurret.java new file mode 100644 index 0000000000..e10b1612e9 --- /dev/null +++ b/core/src/mindustry/world/blocks/defense/turrets/BaseTurret.java @@ -0,0 +1,67 @@ +package mindustry.world.blocks.defense.turrets; + +import mindustry.content.*; +import mindustry.entities.*; +import mindustry.gen.*; +import mindustry.graphics.*; +import mindustry.logic.*; +import mindustry.world.*; +import mindustry.world.consumers.*; +import mindustry.world.meta.*; + +import static mindustry.Vars.*; + +public abstract class BaseTurret extends Block{ + public float range = 80f; + public float rotateSpeed = 5; + + public boolean acceptCoolant = true; + /** Effect displayed when coolant is used. */ + public Effect coolEffect = Fx.fuelburn; + /** How much reload is lowered by for each unit of liquid of heat capacity. */ + public float coolantMultiplier = 5f; + + public BaseTurret(String name){ + super(name); + + update = true; + solid = true; + outlineIcon = true; + } + + @Override + public void init(){ + if(acceptCoolant && !consumes.has(ConsumeType.liquid)){ + hasLiquids = true; + consumes.add(new ConsumeLiquidFilter(liquid -> liquid.temperature <= 0.5f && liquid.flammability < 0.1f, 0.2f)).update(false).boost(); + } + + super.init(); + } + + @Override + public void drawPlace(int x, int y, int rotation, boolean valid){ + Drawf.dashCircle(x * tilesize + offset, y * tilesize + offset, range, Pal.placing); + } + + @Override + public void setStats(){ + super.setStats(); + + stats.add(Stat.shootRange, range / tilesize, StatUnit.blocks); + } + + public class BaseTurretBuild extends Building implements Ranged{ + public float rotation = 90; + + @Override + public float range(){ + return range; + } + + @Override + public void drawSelect(){ + Drawf.dashCircle(x, y, range, team.color); + } + } +} diff --git a/core/src/mindustry/world/blocks/defense/turrets/ItemTurret.java b/core/src/mindustry/world/blocks/defense/turrets/ItemTurret.java index ea94a670b9..e3a9ed97eb 100644 --- a/core/src/mindustry/world/blocks/defense/turrets/ItemTurret.java +++ b/core/src/mindustry/world/blocks/defense/turrets/ItemTurret.java @@ -19,7 +19,6 @@ import mindustry.world.meta.values.*; import static mindustry.Vars.*; public class ItemTurret extends Turret{ - public int maxAmmo = 30; public ObjectMap ammoTypes = new ObjectMap<>(); public ItemTurret(String name){ @@ -36,8 +35,8 @@ public class ItemTurret extends Turret{ public void setStats(){ super.setStats(); - stats.remove(BlockStat.itemCapacity); - stats.add(BlockStat.ammo, new AmmoListValue<>(ammoTypes)); + stats.remove(Stat.itemCapacity); + stats.add(Stat.ammo, new AmmoListValue<>(ammoTypes)); consumes.add(new ConsumeItemFilter(i -> ammoTypes.containsKey(i)){ @Override public void build(Building tile, Table table){ @@ -55,7 +54,7 @@ public class ItemTurret extends Turret{ } @Override - public void display(BlockStats stats){ + public void display(Stats stats){ //don't display } }); @@ -84,7 +83,7 @@ public class ItemTurret extends Turret{ public void displayBars(Table bars){ super.displayBars(bars); - bars.add(new Bar("blocks.ammo", Pal.ammo, () -> (float)totalAmmo / maxAmmo)).growX(); + bars.add(new Bar("stat.ammo", Pal.ammo, () -> (float)totalAmmo / maxAmmo)).growX(); bars.row(); } diff --git a/core/src/mindustry/world/blocks/defense/turrets/LaserTurret.java b/core/src/mindustry/world/blocks/defense/turrets/LaserTurret.java index c47a279414..3aefcdd33c 100644 --- a/core/src/mindustry/world/blocks/defense/turrets/LaserTurret.java +++ b/core/src/mindustry/world/blocks/defense/turrets/LaserTurret.java @@ -33,11 +33,11 @@ public class LaserTurret extends PowerTurret{ public void setStats(){ super.setStats(); - stats.remove(BlockStat.booster); - stats.add(BlockStat.input, new BoosterListValue(reloadTime, consumes.get(ConsumeType.liquid).amount, coolantMultiplier, false, l -> consumes.liquidfilters.get(l.id))); - stats.remove(BlockStat.damage); + stats.remove(Stat.booster); + stats.add(Stat.input, new BoosterListValue(reloadTime, consumes.get(ConsumeType.liquid).amount, coolantMultiplier, false, l -> consumes.liquidfilters.get(l.id))); + stats.remove(Stat.damage); //damages every 5 ticks, at least in meltdown's case - stats.add(BlockStat.damage, shootType.damage * 60f / 5f, StatUnit.perSecond); + stats.add(Stat.damage, shootType.damage * 60f / 5f, StatUnit.perSecond); } public class LaserTurretBuild extends PowerTurretBuild{ diff --git a/core/src/mindustry/world/blocks/defense/turrets/LiquidTurret.java b/core/src/mindustry/world/blocks/defense/turrets/LiquidTurret.java index e34d221d53..25725cd0a9 100644 --- a/core/src/mindustry/world/blocks/defense/turrets/LiquidTurret.java +++ b/core/src/mindustry/world/blocks/defense/turrets/LiquidTurret.java @@ -8,6 +8,7 @@ import mindustry.entities.bullet.*; import mindustry.gen.*; import mindustry.graphics.*; import mindustry.type.*; +import mindustry.world.*; import mindustry.world.consumers.*; import mindustry.world.meta.*; import mindustry.world.meta.values.*; @@ -35,7 +36,11 @@ public class LiquidTurret extends Turret{ public void setStats(){ super.setStats(); - stats.add(BlockStat.ammo, new AmmoListValue<>(ammoTypes)); + stats.add(Stat.ammo, new AmmoListValue<>(ammoTypes)); + } + + @Override + public void init(){ consumes.add(new ConsumeLiquidFilter(i -> ammoTypes.containsKey(i), 1f){ @Override public boolean valid(Building entity){ @@ -48,10 +53,12 @@ public class LiquidTurret extends Turret{ } @Override - public void display(BlockStats stats){ + public void display(Stats stats){ } }); + + super.init(); } public class LiquidTurretBuild extends TurretBuild{ @@ -83,7 +90,9 @@ public class LiquidTurret extends Turret{ int tr = (int)(range / tilesize); for(int x = -tr; x <= tr; x++){ for(int y = -tr; y <= tr; y++){ - if(Fires.has(x + tile.x, y + tile.y)){ + Tile other = world.tileWorld(x + tile.x, y + tile.y); + //do not extinguish fires on other team blocks + if(other != null && Fires.has(x + tile.x, y + tile.y) && (other.build == null || other.team() == team)){ target = Fires.get(x + tile.x, y + tile.y); return; } diff --git a/core/src/mindustry/world/blocks/defense/PointDefenseTurret.java b/core/src/mindustry/world/blocks/defense/turrets/PointDefenseTurret.java similarity index 76% rename from core/src/mindustry/world/blocks/defense/PointDefenseTurret.java rename to core/src/mindustry/world/blocks/defense/turrets/PointDefenseTurret.java index 06746bcfaa..0184f9ff7a 100644 --- a/core/src/mindustry/world/blocks/defense/PointDefenseTurret.java +++ b/core/src/mindustry/world/blocks/defense/turrets/PointDefenseTurret.java @@ -1,4 +1,4 @@ -package mindustry.world.blocks.defense; +package mindustry.world.blocks.defense.turrets; import arc.graphics.*; import arc.graphics.g2d.*; @@ -11,12 +11,9 @@ import mindustry.content.*; import mindustry.entities.*; import mindustry.gen.*; import mindustry.graphics.*; -import mindustry.world.*; import mindustry.world.meta.*; -import static mindustry.Vars.*; - -public class PointDefenseTurret extends Block{ +public class PointDefenseTurret extends ReloadTurret{ public final int timerTarget = timers++; public float retargetTime = 5f; @@ -27,9 +24,6 @@ public class PointDefenseTurret extends Block{ public Effect hitEffect = Fx.pointHit; public Effect shootEffect = Fx.sparkShoot; - public float range = 80f; - public float reloadTime = 30f; - public float rotateSpeed = 20; public float shootCone = 5f; public float bulletDamage = 10f; public float shootLength = 3f; @@ -37,13 +31,12 @@ public class PointDefenseTurret extends Block{ public PointDefenseTurret(String name){ super(name); - outlineIcon = true; - update = true; - } + rotateSpeed = 20f; + reloadTime = 30f; - @Override - public void drawPlace(int x, int y, int rotation, boolean valid){ - Drawf.dashCircle(x * tilesize + offset, y * tilesize + offset, range, Pal.accent); + coolantMultiplier = 2f; + //disabled due to version mismatch problems + acceptCoolant = false; } @Override @@ -55,12 +48,10 @@ public class PointDefenseTurret extends Block{ public void setStats(){ super.setStats(); - stats.add(BlockStat.shootRange, range / tilesize, StatUnit.blocks); - stats.add(BlockStat.reload, 60f / reloadTime, StatUnit.none); + stats.add(Stat.reload, 60f / reloadTime, StatUnit.none); } - public class PointDefenseBuild extends Building{ - public float rotation = 90, reload; + public class PointDefenseBuild extends ReloadTurretBuild{ public @Nullable Bullet target; @Override @@ -76,14 +67,18 @@ public class PointDefenseTurret extends Block{ target = null; } + if(acceptCoolant){ + updateCooling(); + } + //look at target if(target != null && target.within(this, range) && target.team != team && target.type() != null && target.type().hittable){ float dest = angleTo(target); rotation = Angles.moveToward(rotation, dest, rotateSpeed * edelta()); - reload -= edelta(); + reload += edelta(); //shoot when possible - if(Angles.within(rotation, dest, shootCone) && reload <= 0f){ + if(Angles.within(rotation, dest, shootCone) && reload >= reloadTime){ if(target.damage() > bulletDamage){ target.damage(target.damage() - bulletDamage); }else{ @@ -95,18 +90,13 @@ public class PointDefenseTurret extends Block{ beamEffect.at(x + Tmp.v1.x, y + Tmp.v1.y, rotation, color, new Vec2().set(target)); shootEffect.at(x + Tmp.v1.x, y + Tmp.v1.y, rotation, color); hitEffect.at(target.x, target.y, color); - reload = reloadTime; + reload = 0; } }else{ target = null; } } - @Override - public void drawSelect(){ - Drawf.dashCircle(x, y, range, Pal.accent); - } - @Override public void draw(){ Draw.rect(baseRegion, x, y); diff --git a/core/src/mindustry/world/blocks/defense/turrets/PowerTurret.java b/core/src/mindustry/world/blocks/defense/turrets/PowerTurret.java index f8b49195da..bf365f4439 100644 --- a/core/src/mindustry/world/blocks/defense/turrets/PowerTurret.java +++ b/core/src/mindustry/world/blocks/defense/turrets/PowerTurret.java @@ -1,6 +1,7 @@ package mindustry.world.blocks.defense.turrets; import mindustry.entities.bullet.*; +import mindustry.logic.*; import mindustry.world.meta.*; public class PowerTurret extends Turret{ @@ -15,7 +16,7 @@ public class PowerTurret extends Turret{ @Override public void setStats(){ super.setStats(); - stats.add(BlockStat.damage, shootType.damage, StatUnit.none); + stats.add(Stat.damage, shootType.damage, StatUnit.none); } @Override @@ -33,6 +34,15 @@ public class PowerTurret extends Turret{ super.updateTile(); } + @Override + public double sense(LAccess sensor){ + return switch(sensor){ + case ammo -> power.status; + case ammoCapacity -> 1; + default -> super.sense(sensor); + }; + } + @Override public BulletType useAmmo(){ //nothing used directly diff --git a/core/src/mindustry/world/blocks/defense/turrets/ReloadTurret.java b/core/src/mindustry/world/blocks/defense/turrets/ReloadTurret.java new file mode 100644 index 0000000000..33538771a1 --- /dev/null +++ b/core/src/mindustry/world/blocks/defense/turrets/ReloadTurret.java @@ -0,0 +1,49 @@ +package mindustry.world.blocks.defense.turrets; + +import arc.math.*; +import arc.util.*; +import mindustry.type.*; +import mindustry.world.consumers.*; +import mindustry.world.meta.*; +import mindustry.world.meta.values.*; + +import static mindustry.Vars.*; + +public abstract class ReloadTurret extends BaseTurret{ + public float reloadTime = 10f; + + public ReloadTurret(String name){ + super(name); + } + + @Override + public void setStats(){ + super.setStats(); + + if(acceptCoolant){ + stats.add(Stat.booster, new BoosterListValue(reloadTime, consumes.get(ConsumeType.liquid).amount, coolantMultiplier, true, l -> consumes.liquidfilters.get(l.id))); + } + } + + public class ReloadTurretBuild extends BaseTurretBuild{ + public float reload; + + protected void updateCooling(){ + float maxUsed = consumes.get(ConsumeType.liquid).amount; + + Liquid liquid = liquids.current(); + + float used = Math.min(Math.min(liquids.get(liquid), maxUsed * Time.delta), Math.max(0, ((reloadTime - reload) / coolantMultiplier) / liquid.heatCapacity)) * baseReloadSpeed(); + reload += used * liquid.heatCapacity * coolantMultiplier; + liquids.remove(liquid, used); + + if(Mathf.chance(0.06 * used)){ + coolEffect.at(x + Mathf.range(size * tilesize / 2f), y + Mathf.range(size * tilesize / 2f)); + } + } + + protected float baseReloadSpeed(){ + return efficiency(); + } + } +} diff --git a/core/src/mindustry/world/blocks/defense/TractorBeamTurret.java b/core/src/mindustry/world/blocks/defense/turrets/TractorBeamTurret.java similarity index 70% rename from core/src/mindustry/world/blocks/defense/TractorBeamTurret.java rename to core/src/mindustry/world/blocks/defense/turrets/TractorBeamTurret.java index 1f74360ed5..debe26f7e6 100644 --- a/core/src/mindustry/world/blocks/defense/TractorBeamTurret.java +++ b/core/src/mindustry/world/blocks/defense/turrets/TractorBeamTurret.java @@ -1,4 +1,4 @@ -package mindustry.world.blocks.defense; +package mindustry.world.blocks.defense.turrets; import arc.graphics.*; import arc.graphics.g2d.*; @@ -9,12 +9,13 @@ import mindustry.annotations.Annotations.*; import mindustry.entities.*; import mindustry.gen.*; import mindustry.graphics.*; -import mindustry.world.*; +import mindustry.type.*; +import mindustry.world.consumers.*; import mindustry.world.meta.*; import static mindustry.Vars.*; -public class TractorBeamTurret extends Block{ +public class TractorBeamTurret extends BaseTurret{ public final int timerTarget = timers++; public float retargetTime = 5f; @@ -22,8 +23,6 @@ public class TractorBeamTurret extends Block{ public @Load("@-laser") TextureRegion laser; public @Load("@-laser-end") TextureRegion laserEnd; - public float range = 80f; - public float rotateSpeed = 10; public float shootCone = 6f; public float laserWidth = 0.6f; public float force = 0.3f; @@ -35,14 +34,11 @@ public class TractorBeamTurret extends Block{ public TractorBeamTurret(String name){ super(name); - update = true; - solid = true; - outlineIcon = true; - } + rotateSpeed = 10f; + coolantMultiplier = 1f; - @Override - public void drawPlace(int x, int y, int rotation, boolean valid){ - Drawf.dashCircle(x * tilesize + offset, y * tilesize + offset, range, Pal.accent); + //disabled due to version mismatch problems + acceptCoolant = false; } @Override @@ -54,17 +50,16 @@ public class TractorBeamTurret extends Block{ public void setStats(){ super.setStats(); - stats.add(BlockStat.shootRange, range / tilesize, StatUnit.blocks); - stats.add(BlockStat.targetsAir, targetAir); - stats.add(BlockStat.targetsGround, targetGround); - stats.add(BlockStat.damage, damage * 60f, StatUnit.perSecond); + stats.add(Stat.targetsAir, targetAir); + stats.add(Stat.targetsGround, targetGround); + stats.add(Stat.damage, damage * 60f, StatUnit.perSecond); } - public class TractorBeamBuild extends Building{ - public float rotation = 90; + public class TractorBeamBuild extends BaseTurretBuild{ public @Nullable Unit target; public float lastX, lastY, strength; public boolean any; + public float coolant = 1f; @Override public void updateTile(){ @@ -74,8 +69,25 @@ public class TractorBeamTurret extends Block{ target = Units.closestEnemy(team, x, y, range, u -> u.checkTarget(targetAir, targetGround)); } + //consume coolant + if(target != null && acceptCoolant){ + float maxUsed = consumes.get(ConsumeType.liquid).amount; + + Liquid liquid = liquids.current(); + + float used = Math.min(Math.min(liquids.get(liquid), maxUsed * Time.delta), Math.max(0, (1f / coolantMultiplier) / liquid.heatCapacity)); + + liquids.remove(liquid, used); + + if(Mathf.chance(0.06 * used)){ + coolEffect.at(x + Mathf.range(size * tilesize / 2f), y + Mathf.range(size * tilesize / 2f)); + } + + coolant = 1f + (used * liquid.heatCapacity * coolantMultiplier); + } + //look at target - if(target != null && target.within(this, range) && target.team() != team && target.type().flying && efficiency() > 0.01f){ + if(target != null && target.within(this, range) && target.team() != team && target.type.flying && efficiency() > 0.01f){ any = true; float dest = angleTo(target); rotation = Angles.moveToward(rotation, dest, rotateSpeed * edelta()); @@ -98,8 +110,8 @@ public class TractorBeamTurret extends Block{ } @Override - public void drawSelect(){ - Drawf.dashCircle(x, y, range, Pal.accent); + public float efficiency() { + return super.efficiency() * coolant; } @Override diff --git a/core/src/mindustry/world/blocks/defense/turrets/Turret.java b/core/src/mindustry/world/blocks/defense/turrets/Turret.java index 411ee782ec..58abb26b12 100644 --- a/core/src/mindustry/world/blocks/defense/turrets/Turret.java +++ b/core/src/mindustry/world/blocks/defense/turrets/Turret.java @@ -12,6 +12,7 @@ import arc.util.*; import arc.util.io.*; import mindustry.annotations.Annotations.*; import mindustry.content.*; +import mindustry.core.*; import mindustry.entities.*; import mindustry.entities.Units.*; import mindustry.entities.bullet.*; @@ -20,15 +21,13 @@ import mindustry.gen.*; import mindustry.graphics.*; import mindustry.logic.*; import mindustry.type.*; -import mindustry.world.*; import mindustry.world.blocks.*; import mindustry.world.consumers.*; import mindustry.world.meta.*; -import mindustry.world.meta.values.*; import static mindustry.Vars.*; -public abstract class Turret extends Block{ +public abstract class Turret extends ReloadTurret{ //after being logic-controlled and this amount of time passes, the turret will resume normal AI public final static float logicControlCooldown = 60 * 2; @@ -41,10 +40,9 @@ public abstract class Turret extends Block{ public Effect ammoUseEffect = Fx.none; public Sound shootSound = Sounds.shoot; + public int maxAmmo = 30; public int ammoPerShot = 1; public float ammoEjectBack = 1f; - public float range = 50f; - public float reloadTime = 10f; public float inaccuracy = 0f; public float velocityInaccuracy = 0f; public int shots = 1; @@ -52,7 +50,6 @@ public abstract class Turret extends Block{ public float recoilAmount = 1f; public float restitution = 0.02f; public float cooldown = 0.02f; - public float rotateSpeed = 5f; //in degrees per tick public float shootCone = 8f; public float shootShake = 0f; public float xRand = 0f; @@ -62,11 +59,7 @@ public abstract class Turret extends Block{ public boolean alternate = false; public boolean targetAir = true; public boolean targetGround = true; - public boolean acceptCoolant = true; - /** How much reload is lowered by for each unit of liquid of heat capacity. */ - public float coolantMultiplier = 5f; - /** Effect displayed when coolant is used. */ - public Effect coolEffect = Fx.fuelburn; + public Sortf unitSort = Unit::dst2; protected Vec2 tr = new Vec2(); @@ -106,15 +99,10 @@ public abstract class Turret extends Block{ public void setStats(){ super.setStats(); - stats.add(BlockStat.shootRange, range / tilesize, StatUnit.blocks); - stats.add(BlockStat.inaccuracy, (int)inaccuracy, StatUnit.degrees); - stats.add(BlockStat.reload, 60f / reloadTime * shots, StatUnit.none); - stats.add(BlockStat.targetsAir, targetAir); - stats.add(BlockStat.targetsGround, targetGround); - - if(acceptCoolant){ - stats.add(BlockStat.booster, new BoosterListValue(reloadTime, consumes.get(ConsumeType.liquid).amount, coolantMultiplier, true, l -> consumes.liquidfilters.get(l.id))); - } + stats.add(Stat.inaccuracy, (int)inaccuracy, StatUnit.degrees); + stats.add(Stat.reload, 60f / reloadTime * shots, StatUnit.none); + stats.add(Stat.targetsAir, targetAir); + stats.add(Stat.targetsGround, targetGround); } @Override @@ -132,32 +120,22 @@ public abstract class Turret extends Block{ return new TextureRegion[]{baseRegion, region}; } - @Override - public void drawPlace(int x, int y, int rotation, boolean valid){ - Drawf.dashCircle(x * tilesize + offset, y * tilesize + offset, range, Pal.placing); - } - public static abstract class AmmoEntry{ public int amount; public abstract BulletType type(); } - public class TurretBuild extends Building implements ControlBlock, Ranged{ + public class TurretBuild extends ReloadTurretBuild implements ControlBlock{ public Seq ammo = new Seq<>(); public int totalAmmo; - public float reload, rotation = 90, recoil, heat, logicControlTime = -1; + public float recoil, heat, logicControlTime = -1; public int shotCounter; public boolean logicShooting = false; public @Nullable Posc target; public Vec2 targetPos = new Vec2(); public BlockUnitc unit = Nulls.blockUnit; - @Override - public float range(){ - return range; - } - @Override public void created(){ unit = (BlockUnitc)UnitTypes.block.create(team); @@ -167,7 +145,7 @@ public abstract class Turret extends Block{ @Override public void control(LAccess type, double p1, double p2, double p3, double p4){ if(type == LAccess.shoot && !unit.isPlayer()){ - targetPos.set((float)p1, (float)p2); + targetPos.set(World.unconv((float)p1), World.unconv((float)p2)); logicControlTime = logicControlCooldown; logicShooting = !Mathf.zero(p3); } @@ -192,9 +170,11 @@ public abstract class Turret extends Block{ @Override public double sense(LAccess sensor){ return switch(sensor){ + case ammo -> totalAmmo; + case ammoCapacity -> maxAmmo; case rotation -> rotation; - case shootX -> targetPos.x; - case shootY -> targetPos.y; + case shootX -> World.conv(targetPos.x); + case shootY -> World.conv(targetPos.y); case shooting -> (isControlled() ? unit.isShooting() : logicControlled() ? logicShooting : validateTarget()) ? 1 : 0; default -> super.sense(sensor); }; @@ -297,11 +277,6 @@ public abstract class Turret extends Block{ } } - @Override - public void drawSelect(){ - Drawf.dashCircle(x, y, range, team.color); - } - @Override public void handleLiquid(Building source, Liquid liquid, float amount){ if(acceptCoolant && liquids.currentAmount() <= 0.001f){ @@ -311,20 +286,6 @@ public abstract class Turret extends Block{ super.handleLiquid(source, liquid, amount); } - protected void updateCooling(){ - float maxUsed = consumes.get(ConsumeType.liquid).amount; - - Liquid liquid = liquids.current(); - - float used = Math.min(Math.min(liquids.get(liquid), maxUsed * Time.delta), Math.max(0, ((reloadTime - reload) / coolantMultiplier) / liquid.heatCapacity)) * baseReloadSpeed(); - reload += used * liquid.heatCapacity * coolantMultiplier; - liquids.remove(liquid, used); - - if(Mathf.chance(0.06 * used)){ - coolEffect.at(x + Mathf.range(size * tilesize / 2f), y + Mathf.range(size * tilesize / 2f)); - } - } - protected boolean validateTarget(){ return !Units.invalidateTarget(target, team, x, y) || isControlled() || logicControlled(); } @@ -449,10 +410,6 @@ public abstract class Turret extends Block{ ammoUseEffect.at(x - Angles.trnsx(rotation, ammoEjectBack), y - Angles.trnsy(rotation, ammoEjectBack), rotation); } - protected float baseReloadSpeed(){ - return efficiency(); - } - @Override public void write(Writes write){ super.write(write); diff --git a/core/src/mindustry/world/blocks/distribution/Conveyor.java b/core/src/mindustry/world/blocks/distribution/Conveyor.java index 457e7e65e7..9ad1f01bea 100644 --- a/core/src/mindustry/world/blocks/distribution/Conveyor.java +++ b/core/src/mindustry/world/blocks/distribution/Conveyor.java @@ -52,7 +52,7 @@ public class Conveyor extends Block implements Autotiler{ super.setStats(); //have to add a custom calculated speed, since the actual movement speed is apparently not linear - stats.add(BlockStat.itemsMoved, displayedSpeed, StatUnit.itemsSecond); + stats.add(Stat.itemsMoved, displayedSpeed, StatUnit.itemsSecond); } @Override @@ -156,7 +156,7 @@ public class Conveyor extends Block implements Autotiler{ lastInserted = build.lastInserted; mid = build.mid; minitem = build.minitem; - items.addAll(build.items); + items.add(build.items); } } diff --git a/core/src/mindustry/world/blocks/distribution/ItemBridge.java b/core/src/mindustry/world/blocks/distribution/ItemBridge.java index 2d09c69eba..6e310d0e55 100644 --- a/core/src/mindustry/world/blocks/distribution/ItemBridge.java +++ b/core/src/mindustry/world/blocks/distribution/ItemBridge.java @@ -202,7 +202,7 @@ public class ItemBridge extends Block{ for(int i = 1; i <= range; i++){ for(int j = 0; j < 4; j++){ - Tile other = tile.getNearby(Geometry.d4[j].x * i, Geometry.d4[j].y * i); + Tile other = tile.nearby(Geometry.d4[j].x * i, Geometry.d4[j].y * i); if(linkValid(tile, other)){ boolean linked = other.pos() == link; @@ -336,16 +336,18 @@ public class ItemBridge extends Block{ Tile other = world.tile(link); + if(items.total() >= itemCapacity) return false; + + if(linked(source)) return true; + if(linkValid(tile, other)){ int rel = relativeTo(other); int rel2 = relativeTo(Edges.getFacingEdge(source, this)); - if(rel == rel2) return false; - }else{ - return linked(source) && items.total() < itemCapacity; + return rel != rel2; } - return items.total() < itemCapacity; + return false; } @Override @@ -359,16 +361,18 @@ public class ItemBridge extends Block{ Tile other = world.tile(link); + if(!(liquids.current() == liquid || liquids.get(liquids.current()) < 0.2f)) return false; + + if(linked(source)) return true; + if(linkValid(tile, other)){ int rel = relativeTo(other.x, other.y); int rel2 = relativeTo(Edges.getFacingEdge(source, this)); - if(rel == rel2) return false; - }else if(!(linked(source))){ - return false; + return rel != rel2; } - return (liquids.current() == liquid || liquids.get(liquids.current()) < 0.2f); + return false; } protected boolean linked(Building source){ diff --git a/core/src/mindustry/world/blocks/distribution/PayloadConveyor.java b/core/src/mindustry/world/blocks/distribution/PayloadConveyor.java index 7c0bbb7821..08035d9c84 100644 --- a/core/src/mindustry/world/blocks/distribution/PayloadConveyor.java +++ b/core/src/mindustry/world/blocks/distribution/PayloadConveyor.java @@ -87,7 +87,7 @@ public class PayloadConveyor extends Block{ } int ntrns = 1 + size/2; - Tile next = tile.getNearby(Geometry.d4(rotation).x * ntrns, Geometry.d4(rotation).y * ntrns); + Tile next = tile.nearby(Geometry.d4(rotation).x * ntrns, Geometry.d4(rotation).y * ntrns); blocked = (next != null && next.solid() && !next.block().outputsPayload) || (this.next != null && (this.next.rotation + 2)%4 == rotation); } diff --git a/core/src/mindustry/world/blocks/distribution/Router.java b/core/src/mindustry/world/blocks/distribution/Router.java index 5b107d5a0e..766a1bafff 100644 --- a/core/src/mindustry/world/blocks/distribution/Router.java +++ b/core/src/mindustry/world/blocks/distribution/Router.java @@ -1,9 +1,12 @@ package mindustry.world.blocks.distribution; +import arc.math.*; +import arc.util.*; import mindustry.content.*; import mindustry.gen.*; import mindustry.type.*; import mindustry.world.*; +import mindustry.world.blocks.*; import mindustry.world.meta.*; public class Router extends Block{ @@ -20,10 +23,30 @@ public class Router extends Block{ noUpdateDisabled = true; } - public class RouterBuild extends Building{ + public class RouterBuild extends Building implements ControlBlock{ public Item lastItem; public Tile lastInput; public float time; + public @Nullable BlockUnitc unit; + + @Override + public Unit unit(){ + if(unit == null){ + unit = (BlockUnitc)UnitTypes.block.create(team); + unit.tile(this); + } + return (Unit)unit; + } + + @Override + public boolean canControl(){ + return size == 1; + } + + @Override + public boolean shouldAutoTarget(){ + return false; + } @Override public void updateTile(){ @@ -72,6 +95,23 @@ public class Router extends Block{ } public Building getTileTarget(Item item, Tile from, boolean set){ + if(unit != null && isControlled()){ + unit.health(health); + unit.ammo(unit.type().ammoCapacity * (items.total() > 0 ? 1f : 0f)); + unit.team(team); + + int angle = Mathf.mod((int)((angleTo(unit.aimX(), unit.aimY()) + 45) / 90), 4); + + if(unit.isShooting()){ + Building other = nearby(angle); + if(other != null && other.acceptItem(this, item)){ + return other; + } + } + + return null; + } + int counter = rotation; for(int i = 0; i < proximity.size; i++){ Building other = proximity.get((i + counter) % proximity.size); diff --git a/core/src/mindustry/world/blocks/distribution/StackConveyor.java b/core/src/mindustry/world/blocks/distribution/StackConveyor.java index e7095a686c..f3c400ad9b 100644 --- a/core/src/mindustry/world/blocks/distribution/StackConveyor.java +++ b/core/src/mindustry/world/blocks/distribution/StackConveyor.java @@ -53,7 +53,7 @@ public class StackConveyor extends Block implements Autotiler{ public void setStats(){ super.setStats(); - stats.add(BlockStat.itemsMoved, Mathf.round(itemCapacity * speed * 60), StatUnit.itemsSecond); + stats.add(Stat.itemsMoved, Mathf.round(itemCapacity * speed * 60), StatUnit.itemsSecond); } @Override @@ -203,7 +203,7 @@ public class StackConveyor extends Block implements Autotiler{ if(front() instanceof StackConveyorBuild e && e.team == team){ // sleep if its occupied if(e.link == -1){ - e.items.addAll(items); + e.items.add(items); e.lastItem = lastItem; e.link = tile.pos(); // ▲ to | from ▼ diff --git a/core/src/mindustry/world/blocks/environment/Floor.java b/core/src/mindustry/world/blocks/environment/Floor.java index a70056717a..cacf375cef 100644 --- a/core/src/mindustry/world/blocks/environment/Floor.java +++ b/core/src/mindustry/world/blocks/environment/Floor.java @@ -183,7 +183,7 @@ public class Floor extends Block{ for(int i = 0; i < 8; i++){ Point2 point = Geometry.d8[i]; - Tile other = tile.getNearby(point); + Tile other = tile.nearby(point); if(other != null && other.floor().cacheLayer == layer && other.floor().edges() != null){ if(!blended.getAndSet(other.floor().id)){ blenders.add(other.floor()); @@ -200,7 +200,7 @@ public class Floor extends Block{ for(int i = 0; i < 8; i++){ Point2 point = Geometry.d8[i]; - Tile other = tile.getNearby(point); + Tile other = tile.nearby(point); if(other != null && doEdge(other.floor()) && other.floor().cacheLayer == cacheLayer && other.floor().edges() != null){ if(!blended.getAndSet(other.floor().id)){ blenders.add(other.floor()); @@ -217,7 +217,7 @@ public class Floor extends Block{ for(Block block : blenders){ for(int i = 0; i < 8; i++){ Point2 point = Geometry.d8[i]; - Tile other = tile.getNearby(point); + Tile other = tile.nearby(point); if(other != null && other.floor() == block){ TextureRegion region = edge((Floor)block, 1 - point.x, 1 - point.y); Draw.rect(region, tile.worldx(), tile.worldy()); @@ -229,7 +229,7 @@ public class Floor extends Block{ //'new' style of edges with shadows instead of colors, not used currently protected void drawEdgesFlat(Tile tile, boolean sameLayer){ for(int i = 0; i < 4; i++){ - Tile other = tile.getNearby(i); + Tile other = tile.nearby(i); if(other != null && doEdge(other.floor())){ Color color = other.floor().mapColor; Draw.color(color.r, color.g, color.b, 1f); diff --git a/core/src/mindustry/world/blocks/environment/StaticTree.java b/core/src/mindustry/world/blocks/environment/StaticTree.java index 78c6133d2f..9b1a7063f4 100644 --- a/core/src/mindustry/world/blocks/environment/StaticTree.java +++ b/core/src/mindustry/world/blocks/environment/StaticTree.java @@ -21,7 +21,7 @@ public class StaticTree extends StaticWall{ float oy = 0; for(int i = 0; i < 4; i++){ - if(tile.getNearby(i) != null && tile.getNearby(i).block() instanceof StaticWall){ + if(tile.nearby(i) != null && tile.nearby(i).block() instanceof StaticWall){ if(i == 0){ r.setWidth(r.width - crop); diff --git a/core/src/mindustry/world/blocks/experimental/BlockForge.java b/core/src/mindustry/world/blocks/experimental/BlockForge.java index 52509674a7..251c6820a0 100644 --- a/core/src/mindustry/world/blocks/experimental/BlockForge.java +++ b/core/src/mindustry/world/blocks/experimental/BlockForge.java @@ -99,7 +99,7 @@ public class BlockForge extends PayloadAcceptor{ public void buildConfiguration(Table table){ Seq blocks = Vars.content.blocks().select(b -> b.isVisible() && b.size <= 2); - ItemSelection.buildTable(table, blocks, () -> recipe, block -> recipe = block); + ItemSelection.buildTable(table, blocks, () -> recipe, this::configure); } @Override diff --git a/core/src/mindustry/world/blocks/liquid/LiquidJunction.java b/core/src/mindustry/world/blocks/liquid/LiquidJunction.java index 09e5b70bb0..80881e2c17 100644 --- a/core/src/mindustry/world/blocks/liquid/LiquidJunction.java +++ b/core/src/mindustry/world/blocks/liquid/LiquidJunction.java @@ -14,7 +14,7 @@ public class LiquidJunction extends LiquidBlock{ @Override public void setStats(){ super.setStats(); - stats.remove(BlockStat.liquidCapacity); + stats.remove(Stat.liquidCapacity); } @Override diff --git a/core/src/mindustry/world/blocks/logic/LogicBlock.java b/core/src/mindustry/world/blocks/logic/LogicBlock.java index aecf5d4149..488c44604d 100644 --- a/core/src/mindustry/world/blocks/logic/LogicBlock.java +++ b/core/src/mindustry/world/blocks/logic/LogicBlock.java @@ -8,6 +8,7 @@ import arc.struct.*; import arc.util.*; import arc.util.io.*; import mindustry.*; +import mindustry.core.*; import mindustry.gen.*; import mindustry.graphics.*; import mindustry.io.*; @@ -116,8 +117,8 @@ public class LogicBlock extends Block{ public void setStats(){ super.setStats(); - stats.add(BlockStat.linkRange, range / 8, StatUnit.blocks); - stats.add(BlockStat.instructions, instructionsPerTick * 60, StatUnit.perSecond); + stats.add(Stat.linkRange, range / 8, StatUnit.blocks); + stats.add(Stat.instructions, instructionsPerTick * 60, StatUnit.perSecond); } @Override @@ -278,10 +279,13 @@ public class LogicBlock extends Block{ //store link objects executor.links = new Building[links.count(l -> l.valid && l.active)]; + executor.linkIds.clear(); int index = 0; for(LogicLink link : links){ if(link.active && link.valid){ - executor.links[index ++] = world.build(link.x, link.y); + Building build = world.build(link.x, link.y); + executor.links[index ++] = build; + if(build != null) executor.linkIds.add(build.id); } } @@ -304,8 +308,8 @@ public class LogicBlock extends Block{ } asm.getVar("@this").value = this; - asm.putConst("@thisx", x); - asm.putConst("@thisy", y); + asm.putConst("@thisx", World.conv(x)); + asm.putConst("@thisy", World.conv(y)); executor.load(asm); }catch(Exception e){ diff --git a/core/src/mindustry/world/blocks/logic/LogicDisplay.java b/core/src/mindustry/world/blocks/logic/LogicDisplay.java index cb062259b8..505791e1b0 100644 --- a/core/src/mindustry/world/blocks/logic/LogicDisplay.java +++ b/core/src/mindustry/world/blocks/logic/LogicDisplay.java @@ -38,7 +38,7 @@ public class LogicDisplay extends Block{ public void setStats(){ super.setStats(); - stats.add(BlockStat.displaySize, "@x@", displaySize, displaySize); + stats.add(Stat.displaySize, "@x@", displaySize, displaySize); } public class LogicDisplayBuild extends Building{ diff --git a/core/src/mindustry/world/blocks/logic/MemoryBlock.java b/core/src/mindustry/world/blocks/logic/MemoryBlock.java index d215238ced..0329744168 100644 --- a/core/src/mindustry/world/blocks/logic/MemoryBlock.java +++ b/core/src/mindustry/world/blocks/logic/MemoryBlock.java @@ -18,7 +18,7 @@ public class MemoryBlock extends Block{ public void setStats(){ super.setStats(); - stats.add(BlockStat.memoryCapacity, memoryCapacity, StatUnit.none); + stats.add(Stat.memoryCapacity, memoryCapacity, StatUnit.none); } public class MemoryBuild extends Building{ diff --git a/core/src/mindustry/world/blocks/logic/SwitchBlock.java b/core/src/mindustry/world/blocks/logic/SwitchBlock.java index bca47a306a..d27cebb1b3 100644 --- a/core/src/mindustry/world/blocks/logic/SwitchBlock.java +++ b/core/src/mindustry/world/blocks/logic/SwitchBlock.java @@ -37,6 +37,11 @@ public class SwitchBlock extends Block{ } } + @Override + public Boolean config(){ + return enabled; + } + @Override public byte version(){ return 1; diff --git a/core/src/mindustry/world/blocks/payloads/UnitPayload.java b/core/src/mindustry/world/blocks/payloads/UnitPayload.java index 724df792fa..2292244864 100644 --- a/core/src/mindustry/world/blocks/payloads/UnitPayload.java +++ b/core/src/mindustry/world/blocks/payloads/UnitPayload.java @@ -43,7 +43,7 @@ public class UnitPayload implements Payload{ @Override public boolean dump(){ - if(!Units.canCreate(unit.team, unit.type())){ + if(!Units.canCreate(unit.team, unit.type)){ deactiveTime = 1f; return false; } @@ -74,7 +74,7 @@ public class UnitPayload implements Payload{ @Override public void draw(){ Drawf.shadow(unit.x, unit.y, 20); - Draw.rect(unit.type().icon(Cicon.full), unit.x, unit.y, unit.rotation - 90); + Draw.rect(unit.type.icon(Cicon.full), unit.x, unit.y, unit.rotation - 90); //draw warning if(deactiveTime > 0){ diff --git a/core/src/mindustry/world/blocks/power/Battery.java b/core/src/mindustry/world/blocks/power/Battery.java index 7c1f364b37..e78cb31b3a 100644 --- a/core/src/mindustry/world/blocks/power/Battery.java +++ b/core/src/mindustry/world/blocks/power/Battery.java @@ -20,7 +20,7 @@ public class Battery extends PowerDistributor{ super(name); outputsPower = true; consumesPower = true; - flags = EnumSet.of(BlockFlag.powerRes); + flags = EnumSet.of(BlockFlag.battery); } public class BatteryBuild extends Building{ diff --git a/core/src/mindustry/world/blocks/power/ImpactReactor.java b/core/src/mindustry/world/blocks/power/ImpactReactor.java index a24fecf2b4..f5dc985d1e 100644 --- a/core/src/mindustry/world/blocks/power/ImpactReactor.java +++ b/core/src/mindustry/world/blocks/power/ImpactReactor.java @@ -55,7 +55,7 @@ public class ImpactReactor extends PowerGenerator{ super.setStats(); if(hasItems){ - stats.add(BlockStat.productionTime, itemDuration / 60f, StatUnit.seconds); + stats.add(Stat.productionTime, itemDuration / 60f, StatUnit.seconds); } } diff --git a/core/src/mindustry/world/blocks/power/ItemLiquidGenerator.java b/core/src/mindustry/world/blocks/power/ItemLiquidGenerator.java index 2d82bfa4c7..12b3f6d691 100644 --- a/core/src/mindustry/world/blocks/power/ItemLiquidGenerator.java +++ b/core/src/mindustry/world/blocks/power/ItemLiquidGenerator.java @@ -72,7 +72,7 @@ public class ItemLiquidGenerator extends PowerGenerator{ super.setStats(); if(hasItems){ - stats.add(BlockStat.productionTime, itemDuration / 60f, StatUnit.seconds); + stats.add(Stat.productionTime, itemDuration / 60f, StatUnit.seconds); } } diff --git a/core/src/mindustry/world/blocks/power/NuclearReactor.java b/core/src/mindustry/world/blocks/power/NuclearReactor.java index ac449d90db..85fe183993 100644 --- a/core/src/mindustry/world/blocks/power/NuclearReactor.java +++ b/core/src/mindustry/world/blocks/power/NuclearReactor.java @@ -5,6 +5,7 @@ import arc.graphics.*; import arc.graphics.g2d.*; import arc.math.*; import arc.math.geom.*; +import arc.struct.*; import arc.util.*; import arc.util.io.*; import mindustry.annotations.Annotations.*; @@ -47,6 +48,7 @@ public class NuclearReactor extends PowerGenerator{ hasItems = true; hasLiquids = true; rebuildable = false; + flags = EnumSet.of(BlockFlag.reactor); } @Override @@ -54,7 +56,7 @@ public class NuclearReactor extends PowerGenerator{ super.setStats(); if(hasItems){ - stats.add(BlockStat.productionTime, itemDuration / 60f, StatUnit.seconds); + stats.add(Stat.productionTime, itemDuration / 60f, StatUnit.seconds); } } diff --git a/core/src/mindustry/world/blocks/power/PowerGenerator.java b/core/src/mindustry/world/blocks/power/PowerGenerator.java index 2307093f27..ae9453d675 100644 --- a/core/src/mindustry/world/blocks/power/PowerGenerator.java +++ b/core/src/mindustry/world/blocks/power/PowerGenerator.java @@ -12,13 +12,13 @@ import mindustry.world.meta.*; public class PowerGenerator extends PowerDistributor{ /** The amount of power produced per tick in case of an efficiency of 1.0, which represents 100%. */ public float powerProduction; - public BlockStat generationType = BlockStat.basePowerGeneration; + public Stat generationType = Stat.basePowerGeneration; public PowerGenerator(String name){ super(name); sync = true; baseExplosiveness = 5f; - flags = EnumSet.of(BlockFlag.producer); + flags = EnumSet.of(BlockFlag.generator); } @Override diff --git a/core/src/mindustry/world/blocks/power/PowerNode.java b/core/src/mindustry/world/blocks/power/PowerNode.java index 5d033c99b7..042b03099e 100644 --- a/core/src/mindustry/world/blocks/power/PowerNode.java +++ b/core/src/mindustry/world/blocks/power/PowerNode.java @@ -124,8 +124,8 @@ public class PowerNode extends PowerBlock{ public void setStats(){ super.setStats(); - stats.add(BlockStat.powerRange, laserRange, StatUnit.blocks); - stats.add(BlockStat.powerConnections, maxNodes, StatUnit.none); + stats.add(Stat.powerRange, laserRange, StatUnit.blocks); + stats.add(Stat.powerConnections, maxNodes, StatUnit.none); } @Override diff --git a/core/src/mindustry/world/blocks/power/ThermalGenerator.java b/core/src/mindustry/world/blocks/power/ThermalGenerator.java index e29b7a6bdb..154bd02a0c 100644 --- a/core/src/mindustry/world/blocks/power/ThermalGenerator.java +++ b/core/src/mindustry/world/blocks/power/ThermalGenerator.java @@ -22,7 +22,7 @@ public class ThermalGenerator extends PowerGenerator{ public void setStats(){ super.setStats(); - stats.add(BlockStat.tiles, attribute, floating); + stats.add(Stat.tiles, attribute, floating); } @Override diff --git a/core/src/mindustry/world/blocks/production/AttributeSmelter.java b/core/src/mindustry/world/blocks/production/AttributeSmelter.java index 987b14e63b..c2650ab030 100644 --- a/core/src/mindustry/world/blocks/production/AttributeSmelter.java +++ b/core/src/mindustry/world/blocks/production/AttributeSmelter.java @@ -37,7 +37,7 @@ public class AttributeSmelter extends GenericSmelter{ public void setStats(){ super.setStats(); - stats.add(BlockStat.affinities, attribute, boostScale); + stats.add(Stat.affinities, attribute, boostScale); } public class AttributeSmelterBuild extends SmelterBuild{ diff --git a/core/src/mindustry/world/blocks/production/Cultivator.java b/core/src/mindustry/world/blocks/production/Cultivator.java index 2dc97d82f6..a5aede4e25 100644 --- a/core/src/mindustry/world/blocks/production/Cultivator.java +++ b/core/src/mindustry/world/blocks/production/Cultivator.java @@ -42,7 +42,7 @@ public class Cultivator extends GenericCrafter{ public void setStats(){ super.setStats(); - stats.add(BlockStat.affinities, attribute); + stats.add(Stat.affinities, attribute); } @Override diff --git a/core/src/mindustry/world/blocks/production/Drill.java b/core/src/mindustry/world/blocks/production/Drill.java index 7b86f1d1f5..ef4ab64b27 100644 --- a/core/src/mindustry/world/blocks/production/Drill.java +++ b/core/src/mindustry/world/blocks/production/Drill.java @@ -16,7 +16,9 @@ import mindustry.graphics.*; import mindustry.type.*; import mindustry.ui.*; import mindustry.world.*; +import mindustry.world.blocks.environment.*; import mindustry.world.meta.*; +import mindustry.world.meta.values.*; import static mindustry.Vars.*; @@ -135,29 +137,11 @@ public class Drill extends Block{ public void setStats(){ super.setStats(); - stats.add(BlockStat.drillTier, table -> { - Seq list = content.blocks().select(b -> b.isFloor() && b.asFloor().itemDrop != null && b.asFloor().itemDrop.hardness <= tier); + stats.add(Stat.drillTier, new BlockFilterValue(b -> b instanceof Floor f && f.itemDrop != null && f.itemDrop.hardness <= tier)); - table.table(l -> { - l.left(); - - for(int i = 0; i < list.size; i++){ - Block item = list.get(i); - - l.image(item.icon(Cicon.small)).size(8 * 3).padRight(2).padLeft(2).padTop(3).padBottom(3); - l.add(item.localizedName).left().padLeft(1).padRight(4); - if(i % 5 == 4){ - l.row(); - } - } - }); - - - }); - - stats.add(BlockStat.drillSpeed, 60f / drillTime * size * size, StatUnit.itemsSecond); + stats.add(Stat.drillSpeed, 60f / drillTime * size * size, StatUnit.itemsSecond); if(liquidBoostIntensity != 1){ - stats.add(BlockStat.boostEffect, liquidBoostIntensity * liquidBoostIntensity, StatUnit.timesSpeed); + stats.add(Stat.boostEffect, liquidBoostIntensity * liquidBoostIntensity, StatUnit.timesSpeed); } } diff --git a/core/src/mindustry/world/blocks/production/Fracker.java b/core/src/mindustry/world/blocks/production/Fracker.java index d894cf05b5..d2dbd5bf69 100644 --- a/core/src/mindustry/world/blocks/production/Fracker.java +++ b/core/src/mindustry/world/blocks/production/Fracker.java @@ -21,7 +21,7 @@ public class Fracker extends SolidPump{ public void setStats(){ super.setStats(); - stats.add(BlockStat.productionTime, itemUseTime / 60f, StatUnit.seconds); + stats.add(Stat.productionTime, itemUseTime / 60f, StatUnit.seconds); } @Override @@ -45,7 +45,7 @@ public class Fracker extends SolidPump{ Draw.rect(region, x, y); super.drawCracks(); - Drawf.liquid(liquidRegion, x, y, liquids.total() / liquidCapacity, result.color); + Drawf.liquid(liquidRegion, x, y, liquids.get(result) / liquidCapacity, result.color); Draw.rect(rotatorRegion, x, y, pumpTime); Draw.rect(topRegion, x, y); diff --git a/core/src/mindustry/world/blocks/production/GenericCrafter.java b/core/src/mindustry/world/blocks/production/GenericCrafter.java index 434c9f4b65..c992be7d78 100644 --- a/core/src/mindustry/world/blocks/production/GenericCrafter.java +++ b/core/src/mindustry/world/blocks/production/GenericCrafter.java @@ -2,6 +2,7 @@ package mindustry.world.blocks.production; import arc.graphics.g2d.*; import arc.math.*; +import arc.struct.*; import arc.util.io.*; import mindustry.content.*; import mindustry.entities.*; @@ -23,9 +24,6 @@ public class GenericCrafter extends Block{ public DrawBlock drawer = new DrawBlock(); - //public Cons drawer = null; - //public Prov drawIcons = null; - public GenericCrafter(String name){ super(name); update = true; @@ -34,6 +32,7 @@ public class GenericCrafter extends Block{ idleSound = Sounds.machine; sync = true; idleSoundVolume = 0.03f; + flags = EnumSet.of(BlockFlag.factory); } @Override @@ -44,14 +43,14 @@ public class GenericCrafter extends Block{ } super.setStats(); - stats.add(BlockStat.productionTime, craftTime / 60f, StatUnit.seconds); + stats.add(Stat.productionTime, craftTime / 60f, StatUnit.seconds); if(outputItem != null){ - stats.add(BlockStat.output, outputItem); + stats.add(Stat.output, outputItem); } if(outputLiquid != null){ - stats.add(BlockStat.output, outputLiquid.liquid, outputLiquid.amount, false); + stats.add(Stat.output, outputLiquid.liquid, outputLiquid.amount, false); } } diff --git a/core/src/mindustry/world/blocks/production/LiquidConverter.java b/core/src/mindustry/world/blocks/production/LiquidConverter.java index 84868d9b61..8a9725d42d 100644 --- a/core/src/mindustry/world/blocks/production/LiquidConverter.java +++ b/core/src/mindustry/world/blocks/production/LiquidConverter.java @@ -26,8 +26,8 @@ public class LiquidConverter extends GenericCrafter{ @Override public void setStats(){ super.setStats(); - stats.remove(BlockStat.output); - stats.add(BlockStat.output, outputLiquid.liquid, outputLiquid.amount * craftTime, false); + stats.remove(Stat.output); + stats.add(Stat.output, outputLiquid.liquid, outputLiquid.amount * craftTime, false); } public class LiquidConverterBuild extends GenericCrafterBuild{ diff --git a/core/src/mindustry/world/blocks/production/PayloadAcceptor.java b/core/src/mindustry/world/blocks/production/PayloadAcceptor.java index 75cfde865f..3b4693c250 100644 --- a/core/src/mindustry/world/blocks/production/PayloadAcceptor.java +++ b/core/src/mindustry/world/blocks/production/PayloadAcceptor.java @@ -95,7 +95,7 @@ public class PayloadAcceptor extends Block{ updatePayload(); payRotation = Mathf.slerpDelta(payRotation, rotate ? rotdeg() : 90f, 0.3f); - payVector.approachDelta(Vec2.ZERO, payloadSpeed); + payVector.approach(Vec2.ZERO, payloadSpeed * delta()); return hasArrived(); } @@ -105,7 +105,7 @@ public class PayloadAcceptor extends Block{ updatePayload(); - payVector.trns(rotdeg(), payVector.len() + edelta() * payloadSpeed); + payVector.trns(rotdeg(), payVector.len() + delta() * payloadSpeed); payRotation = rotdeg(); if(payVector.len() >= size * tilesize/2f){ diff --git a/core/src/mindustry/world/blocks/production/Pump.java b/core/src/mindustry/world/blocks/production/Pump.java index fa2f67999f..c4a6313129 100644 --- a/core/src/mindustry/world/blocks/production/Pump.java +++ b/core/src/mindustry/world/blocks/production/Pump.java @@ -26,7 +26,7 @@ public class Pump extends LiquidBlock{ @Override public void setStats(){ super.setStats(); - stats.add(BlockStat.output, 60f * pumpAmount * size * size, StatUnit.liquidSecond); + stats.add(Stat.output, 60f * pumpAmount * size * size, StatUnit.liquidSecond); } @Override diff --git a/core/src/mindustry/world/blocks/production/Separator.java b/core/src/mindustry/world/blocks/production/Separator.java index bcf66984e3..d9b355cc70 100644 --- a/core/src/mindustry/world/blocks/production/Separator.java +++ b/core/src/mindustry/world/blocks/production/Separator.java @@ -41,14 +41,14 @@ public class Separator extends Block{ super.setStats(); - stats.add(BlockStat.output, new ItemFilterValue(item -> { + stats.add(Stat.output, new ItemFilterValue(item -> { for(ItemStack i : results){ if(item == i.item) return true; } return false; })); - stats.add(BlockStat.productionTime, craftTime / 60f, StatUnit.seconds); + stats.add(Stat.productionTime, craftTime / 60f, StatUnit.seconds); } public class SeparatorBuild extends Building{ diff --git a/core/src/mindustry/world/blocks/production/SolidPump.java b/core/src/mindustry/world/blocks/production/SolidPump.java index 3b29665fde..d49a6499ac 100644 --- a/core/src/mindustry/world/blocks/production/SolidPump.java +++ b/core/src/mindustry/world/blocks/production/SolidPump.java @@ -53,10 +53,10 @@ public class SolidPump extends Pump{ public void setStats(){ super.setStats(); - stats.remove(BlockStat.output); - stats.add(BlockStat.output, result, 60f * pumpAmount, true); + stats.remove(Stat.output); + stats.add(Stat.output, result, 60f * pumpAmount, true); if(attribute != null){ - stats.add(baseEfficiency > 0.0001f ? BlockStat.affinities : BlockStat.tiles, attribute); + stats.add(baseEfficiency > 0.0001f ? Stat.affinities : Stat.tiles, attribute); } } diff --git a/core/src/mindustry/world/blocks/sandbox/PowerVoid.java b/core/src/mindustry/world/blocks/sandbox/PowerVoid.java index c423ba6caf..e97622c609 100644 --- a/core/src/mindustry/world/blocks/sandbox/PowerVoid.java +++ b/core/src/mindustry/world/blocks/sandbox/PowerVoid.java @@ -11,8 +11,8 @@ public class PowerVoid extends PowerBlock{ } @Override - public void init(){ - super.init(); - stats.remove(BlockStat.powerUse); + public void setStats(){ + super.setStats(); + stats.remove(Stat.powerUse); } } diff --git a/core/src/mindustry/world/blocks/storage/CoreBlock.java b/core/src/mindustry/world/blocks/storage/CoreBlock.java index bdb16eb674..039c7cc3cf 100644 --- a/core/src/mindustry/world/blocks/storage/CoreBlock.java +++ b/core/src/mindustry/world/blocks/storage/CoreBlock.java @@ -47,7 +47,7 @@ public class CoreBlock extends StorageBlock{ update = true; hasItems = true; priority = TargetPriority.core; - flags = EnumSet.of(BlockFlag.core, BlockFlag.producer, BlockFlag.unitModifier); + flags = EnumSet.of(BlockFlag.core, BlockFlag.unitModifier); unitCapModifier = 10; activeSound = Sounds.respawning; activeSoundVolume = 1f; @@ -77,12 +77,16 @@ public class CoreBlock extends StorageBlock{ public void setStats(){ super.setStats(); - stats.add(BlockStat.buildTime, 0, StatUnit.seconds); + stats.add(Stat.buildTime, 0, StatUnit.seconds); + } - bars.add("capacity", (CoreBuild e) -> - new Bar( - () -> Core.bundle.format("bar.capacity", UI.formatAmount(e.storageCapacity)), - () -> Pal.items, + @Override + public void setBars(){ + super.setBars(); + + bars.add("capacity", (CoreBuild e) -> new Bar( + () -> Core.bundle.format("bar.capacity", UI.formatAmount(e.storageCapacity)), + () -> Pal.items, () -> e.items.total() / ((float)e.storageCapacity * content.items().count(i -> i.unlockedNow())) )); } @@ -196,6 +200,21 @@ public class CoreBlock extends StorageBlock{ return false; } + @Override + public void onDestroyed(){ + super.onDestroyed(); + + //add a spawn to the map for future reference - waves should be disabled, so it shouldn't matter + if(state.isCampaign() && team == state.rules.waveTeam){ + //do not recache + tile.setOverlayQuiet(Blocks.spawn); + + if(!spawner.getSpawns().contains(tile)){ + spawner.getSpawns().add(tile); + } + } + } + @Override public void drawLight(){ Drawf.light(team, x, y, 30f * size, Pal.accent, 0.5f + Mathf.absin(20f, 0.1f)); @@ -310,23 +329,6 @@ public class CoreBlock extends StorageBlock{ } } - @Override - public void onDestroyed(){ - super.onDestroyed(); - - if(state.isCampaign() && team == state.rules.waveTeam){ - //do not recache - world.setGenerating(true); - tile.setOverlay(Blocks.spawn); - world.setGenerating(false); - - if(!spawner.getSpawns().contains(tile)){ - spawner.getSpawns().add(tile); - } - spawner.doShockwave(x, y); - } - } - @Override public void placed(){ super.placed(); @@ -335,7 +337,7 @@ public class CoreBlock extends StorageBlock{ @Override public void itemTaken(Item item){ - if(state.isCampaign()){ + if(state.isCampaign() && team == state.rules.defaultTeam){ //update item taken amount state.secinfo.handleCoreItem(item, -1); } @@ -344,6 +346,9 @@ public class CoreBlock extends StorageBlock{ @Override public void handleItem(Building source, Item item){ if(net.server() || !net.active()){ + if(team == state.rules.defaultTeam){ + state.secinfo.handleCoreItem(item, 1); + } if(items.get(item) >= getMaximumAccepted(item)){ //create item incineration effect at random intervals diff --git a/core/src/mindustry/world/blocks/storage/StorageBlock.java b/core/src/mindustry/world/blocks/storage/StorageBlock.java index 50eb65a270..32b3713350 100644 --- a/core/src/mindustry/world/blocks/storage/StorageBlock.java +++ b/core/src/mindustry/world/blocks/storage/StorageBlock.java @@ -26,11 +26,11 @@ public class StorageBlock extends Block{ } public static void incinerateEffect(Building self, Building source){ - if(Mathf.chance(0.1)){ + if(Mathf.chance(0.3)){ Tile edge = Edges.getFacingEdge(source, self); Tile edge2 = Edges.getFacingEdge(self, source); if(edge != null && edge2 != null){ - Fx.fuelburn.at((edge.worldx() + edge2.worldx())/2f, (edge.worldy() + edge2.worldy())/2f); + Fx.coreBurn.at((edge.worldx() + edge2.worldx())/2f, (edge.worldy() + edge2.worldy())/2f); } } } @@ -46,7 +46,9 @@ public class StorageBlock extends Block{ @Override public void handleItem(Building source, Item item){ if(linkedCore != null){ - incinerateEffect(this, source); + if(linkedCore.items.get(item) >= ((CoreBuild)linkedCore).storageCapacity){ + incinerateEffect(this, source); + } ((CoreBuild)linkedCore).noEffect = true; linkedCore.handleItem(source, item); }else{ @@ -70,7 +72,7 @@ public class StorageBlock extends Block{ public void overwrote(Seq previous){ for(Building other : previous){ if(other.items != null){ - items.addAll(other.items); + items.add(other.items); } } diff --git a/core/src/mindustry/world/blocks/units/Reconstructor.java b/core/src/mindustry/world/blocks/units/Reconstructor.java index 5b5696bf4c..9374e0d801 100644 --- a/core/src/mindustry/world/blocks/units/Reconstructor.java +++ b/core/src/mindustry/world/blocks/units/Reconstructor.java @@ -63,7 +63,23 @@ public class Reconstructor extends UnitBlock{ public void setStats(){ super.setStats(); - stats.add(BlockStat.productionTime, constructTime / 60f, StatUnit.seconds); + stats.add(Stat.productionTime, constructTime / 60f, StatUnit.seconds); + stats.add(Stat.output, table -> { + table.row(); + for(var upgrade : upgrades){ + float size = 8*3; + if(upgrade[0].unlockedNow() && upgrade[1].unlockedNow()){ + table.image(upgrade[0].icon(Cicon.small)).size(size).padRight(4).padLeft(10).scaling(Scaling.fit).right(); + table.add(upgrade[0].localizedName).left(); + + table.add("[lightgray] -> "); + + table.image(upgrade[1].icon(Cicon.small)).size(size).padRight(4).scaling(Scaling.fit); + table.add(upgrade[1].localizedName).left(); + table.row(); + } + } + }); } @Override @@ -90,7 +106,7 @@ public class Reconstructor extends UnitBlock{ return this.payload == null && relativeTo(source) != rotation && payload instanceof UnitPayload - && hasUpgrade(((UnitPayload)payload).unit.type()); + && hasUpgrade(((UnitPayload)payload).unit.type); } @Override @@ -114,9 +130,9 @@ public class Reconstructor extends UnitBlock{ if(constructing() && hasArrived()){ Draw.draw(Layer.blockOver, () -> { Draw.alpha(1f - progress/ constructTime); - Draw.rect(payload.unit.type().icon(Cicon.full), x, y, rotdeg() - 90); + Draw.rect(payload.unit.type.icon(Cicon.full), x, y, rotdeg() - 90); Draw.reset(); - Drawf.construct(this, upgrade(payload.unit.type()), rotdeg() - 90f, progress / constructTime, speedScl, time); + Drawf.construct(this, upgrade(payload.unit.type), rotdeg() - 90f, progress / constructTime, speedScl, time); }); }else{ Draw.z(Layer.blockOver); @@ -135,7 +151,7 @@ public class Reconstructor extends UnitBlock{ if(payload != null){ //check if offloading - if(!hasUpgrade(payload.unit.type())){ + if(!hasUpgrade(payload.unit.type)){ moveOutPayload(); }else{ //update progress if(moveInPayload()){ @@ -146,7 +162,7 @@ public class Reconstructor extends UnitBlock{ //upgrade the unit if(progress >= constructTime){ - payload.unit = upgrade(payload.unit.type()).create(payload.unit.team()); + payload.unit = upgrade(payload.unit.type).create(payload.unit.team()); progress = 0; Effect.shake(2f, 3f, this); Fx.producesmoke.at(this); @@ -168,12 +184,12 @@ public class Reconstructor extends UnitBlock{ public UnitType unit(){ if(payload == null) return null; - UnitType t = upgrade(payload.unit.type()); + UnitType t = upgrade(payload.unit.type); return t != null && t.unlockedNow() ? t : null; } public boolean constructing(){ - return payload != null && hasUpgrade(payload.unit.type()); + return payload != null && hasUpgrade(payload.unit.type); } public boolean hasUpgrade(UnitType type){ diff --git a/core/src/mindustry/world/blocks/units/RepairPoint.java b/core/src/mindustry/world/blocks/units/RepairPoint.java index abcc4bf738..789f7a8901 100644 --- a/core/src/mindustry/world/blocks/units/RepairPoint.java +++ b/core/src/mindustry/world/blocks/units/RepairPoint.java @@ -42,7 +42,7 @@ public class RepairPoint extends Block{ @Override public void setStats(){ super.setStats(); - stats.add(BlockStat.range, repairRadius / tilesize, StatUnit.blocks); + stats.add(Stat.range, repairRadius / tilesize, StatUnit.blocks); } @Override diff --git a/core/src/mindustry/world/blocks/units/ResupplyPoint.java b/core/src/mindustry/world/blocks/units/ResupplyPoint.java index 2714be2358..6402b20147 100644 --- a/core/src/mindustry/world/blocks/units/ResupplyPoint.java +++ b/core/src/mindustry/world/blocks/units/ResupplyPoint.java @@ -65,10 +65,10 @@ public class ResupplyPoint extends Block{ public static boolean resupply(Team team, float x, float y, float range, float ammoAmount, Color ammoColor, Boolf valid){ if(!state.rules.unitAmmo) return false; - Unit unit = Units.closest(team, x, y, range, u -> u.type().ammoType instanceof ItemAmmoType && u.ammo <= u.type().ammoCapacity - ammoAmount && valid.get(u)); + Unit unit = Units.closest(team, x, y, range, u -> u.type.ammoType instanceof ItemAmmoType && u.ammo <= u.type.ammoCapacity - ammoAmount && valid.get(u)); if(unit != null){ Fx.itemTransfer.at(x, y, ammoAmount / 2f, ammoColor, unit); - unit.ammo = Math.min(unit.ammo + ammoAmount, unit.type().ammoCapacity); + unit.ammo = Math.min(unit.ammo + ammoAmount, unit.type.ammoCapacity); return true; } diff --git a/core/src/mindustry/world/blocks/units/UnitFactory.java b/core/src/mindustry/world/blocks/units/UnitFactory.java index 74248f9932..500385966e 100644 --- a/core/src/mindustry/world/blocks/units/UnitFactory.java +++ b/core/src/mindustry/world/blocks/units/UnitFactory.java @@ -14,6 +14,7 @@ import mindustry.entities.*; import mindustry.entities.units.*; import mindustry.gen.*; import mindustry.graphics.*; +import mindustry.logic.*; import mindustry.type.*; import mindustry.ui.*; import mindustry.world.blocks.*; @@ -86,7 +87,7 @@ public class UnitFactory extends UnitBlock{ public void setStats(){ super.setStats(); - stats.remove(BlockStat.itemCapacity); + stats.remove(Stat.itemCapacity); } @Override @@ -122,6 +123,12 @@ public class UnitFactory extends UnitBlock{ return currentPlan == -1 ? 0 : progress / plans.get(currentPlan).time; } + @Override + public Object senseObject(LAccess sensor){ + if(sensor == LAccess.config) return currentPlan == -1 ? null : plans.get(currentPlan).unit; + return super.senseObject(sensor); + } + @Override public void buildConfiguration(Table table){ Seq units = Seq.with(plans).map(u -> u.unit).filter(u -> u.unlockedNow()); diff --git a/core/src/mindustry/world/consumers/Consume.java b/core/src/mindustry/world/consumers/Consume.java index 410d041a03..3066dbb9bb 100644 --- a/core/src/mindustry/world/consumers/Consume.java +++ b/core/src/mindustry/world/consumers/Consume.java @@ -69,5 +69,5 @@ public abstract class Consume{ public abstract boolean valid(Building entity); - public abstract void display(BlockStats stats); + public abstract void display(Stats stats); } diff --git a/core/src/mindustry/world/consumers/ConsumeItemDynamic.java b/core/src/mindustry/world/consumers/ConsumeItemDynamic.java index a2dd079c20..0c18e77741 100644 --- a/core/src/mindustry/world/consumers/ConsumeItemDynamic.java +++ b/core/src/mindustry/world/consumers/ConsumeItemDynamic.java @@ -73,7 +73,7 @@ public class ConsumeItemDynamic extends Consume{ } @Override - public void display(BlockStats stats){ + public void display(Stats stats){ //should be handled by the block } } diff --git a/core/src/mindustry/world/consumers/ConsumeItemFilter.java b/core/src/mindustry/world/consumers/ConsumeItemFilter.java index e9e98041c4..93d70a46f3 100644 --- a/core/src/mindustry/world/consumers/ConsumeItemFilter.java +++ b/core/src/mindustry/world/consumers/ConsumeItemFilter.java @@ -70,7 +70,7 @@ public class ConsumeItemFilter extends Consume{ } @Override - public void display(BlockStats stats){ - stats.add(booster ? BlockStat.booster : BlockStat.input, new ItemFilterValue(filter)); + public void display(Stats stats){ + stats.add(booster ? Stat.booster : Stat.input, new ItemFilterValue(filter)); } } diff --git a/core/src/mindustry/world/consumers/ConsumeItems.java b/core/src/mindustry/world/consumers/ConsumeItems.java index 01909e2a14..f43746db87 100644 --- a/core/src/mindustry/world/consumers/ConsumeItems.java +++ b/core/src/mindustry/world/consumers/ConsumeItems.java @@ -63,7 +63,7 @@ public class ConsumeItems extends Consume{ } @Override - public void display(BlockStats stats){ - stats.add(booster ? BlockStat.booster : BlockStat.input, new ItemListValue(items)); + public void display(Stats stats){ + stats.add(booster ? Stat.booster : Stat.input, new ItemListValue(items)); } } diff --git a/core/src/mindustry/world/consumers/ConsumeLiquid.java b/core/src/mindustry/world/consumers/ConsumeLiquid.java index 90f3bf0687..839b59c607 100644 --- a/core/src/mindustry/world/consumers/ConsumeLiquid.java +++ b/core/src/mindustry/world/consumers/ConsumeLiquid.java @@ -45,7 +45,7 @@ public class ConsumeLiquid extends ConsumeLiquidBase{ } @Override - public void display(BlockStats stats){ - stats.add(booster ? BlockStat.booster : BlockStat.input, liquid, amount * timePeriod, timePeriod == 60); + public void display(Stats stats){ + stats.add(booster ? Stat.booster : Stat.input, liquid, amount * timePeriod, timePeriod == 60); } } diff --git a/core/src/mindustry/world/consumers/ConsumeLiquidFilter.java b/core/src/mindustry/world/consumers/ConsumeLiquidFilter.java index 962023488b..851cf75fd2 100644 --- a/core/src/mindustry/world/consumers/ConsumeLiquidFilter.java +++ b/core/src/mindustry/world/consumers/ConsumeLiquidFilter.java @@ -49,7 +49,7 @@ public class ConsumeLiquidFilter extends ConsumeLiquidBase{ } @Override - public void display(BlockStats stats){ - stats.add(booster ? BlockStat.booster : BlockStat.input, new LiquidFilterValue(filter, amount * timePeriod, timePeriod == 60f)); + public void display(Stats stats){ + stats.add(booster ? Stat.booster : Stat.input, new LiquidFilterValue(filter, amount * timePeriod, timePeriod == 60f)); } } diff --git a/core/src/mindustry/world/consumers/ConsumePower.java b/core/src/mindustry/world/consumers/ConsumePower.java index 2dc55dbb11..67b23d6c4c 100644 --- a/core/src/mindustry/world/consumers/ConsumePower.java +++ b/core/src/mindustry/world/consumers/ConsumePower.java @@ -54,11 +54,11 @@ public class ConsumePower extends Consume{ } @Override - public void display(BlockStats stats){ + public void display(Stats stats){ if(buffered){ - stats.add(BlockStat.powerCapacity, capacity, StatUnit.none); + stats.add(Stat.powerCapacity, capacity, StatUnit.none); }else{ - stats.add(BlockStat.powerUse, usage * 60f, StatUnit.powerSecond); + stats.add(Stat.powerUse, usage * 60f, StatUnit.powerSecond); } } diff --git a/core/src/mindustry/world/consumers/Consumers.java b/core/src/mindustry/world/consumers/Consumers.java index 4ce410aa04..a811a24d52 100644 --- a/core/src/mindustry/world/consumers/Consumers.java +++ b/core/src/mindustry/world/consumers/Consumers.java @@ -109,7 +109,7 @@ public class Consumers{ return optionalResults; } - public void display(BlockStats stats){ + public void display(Stats stats){ for(Consume c : map){ if(c != null){ c.display(stats); diff --git a/core/src/mindustry/world/meta/BlockFlag.java b/core/src/mindustry/world/meta/BlockFlag.java index 5c9bf2e920..a3fe674e37 100644 --- a/core/src/mindustry/world/meta/BlockFlag.java +++ b/core/src/mindustry/world/meta/BlockFlag.java @@ -4,18 +4,22 @@ package mindustry.world.meta; public enum BlockFlag{ /** Enemy core; primary target for all units. */ core, - /** Producer of important goods. */ - producer, - /** A turret. */ + /** Something that generates power. */ + generator, + /** Any turret. */ turret, + /** A block that transforms resources. */ + factory, /** Repair point. */ repair, /** Rally point. */ rally, /** Block that stored power for resupply. */ - powerRes, + battery, /** Block used for resupply. */ resupply, + /** Any reactor block. */ + reactor, /** Any block that boosts unit capacity. */ unitModifier; diff --git a/core/src/mindustry/world/meta/BlockStat.java b/core/src/mindustry/world/meta/BlockStat.java deleted file mode 100644 index 05e45acbfe..0000000000 --- a/core/src/mindustry/world/meta/BlockStat.java +++ /dev/null @@ -1,68 +0,0 @@ -package mindustry.world.meta; - -import arc.*; - -import java.util.*; - -/** Describes one type of stat for a block. */ -public enum BlockStat{ - health(StatCategory.general), - size(StatCategory.general), - displaySize(StatCategory.general), - buildTime(StatCategory.general), - buildCost(StatCategory.general), - memoryCapacity(StatCategory.general), - - itemCapacity(StatCategory.items), - itemsMoved(StatCategory.items), - launchTime(StatCategory.items), - maxConsecutive(StatCategory.items), - - liquidCapacity(StatCategory.liquids), - - powerCapacity(StatCategory.power), - powerUse(StatCategory.power), - powerDamage(StatCategory.power), - powerRange(StatCategory.power), - powerConnections(StatCategory.power), - basePowerGeneration(StatCategory.power), - - tiles(StatCategory.crafting), - input(StatCategory.crafting), - output(StatCategory.crafting), - productionTime(StatCategory.crafting), - drillTier(StatCategory.crafting), - drillSpeed(StatCategory.crafting), - maxUnits(StatCategory.crafting), - linkRange(StatCategory.crafting), - instructions(StatCategory.crafting), - - speedIncrease(StatCategory.shooting), - repairTime(StatCategory.shooting), - range(StatCategory.shooting), - shootRange(StatCategory.shooting), - inaccuracy(StatCategory.shooting), - shots(StatCategory.shooting), - reload(StatCategory.shooting), - powerShot(StatCategory.shooting), - targetsAir(StatCategory.shooting), - targetsGround(StatCategory.shooting), - damage(StatCategory.shooting), - ammo(StatCategory.shooting), - shieldHealth(StatCategory.shooting), - cooldownTime(StatCategory.shooting), - - booster(StatCategory.optional), - boostEffect(StatCategory.optional), - affinities(StatCategory.optional); - - public final StatCategory category; - - BlockStat(StatCategory category){ - this.category = category; - } - - public String localized(){ - return Core.bundle.get("blocks." + name().toLowerCase(Locale.ROOT)); - } -} diff --git a/core/src/mindustry/world/meta/Stat.java b/core/src/mindustry/world/meta/Stat.java new file mode 100644 index 0000000000..94e4c17b2c --- /dev/null +++ b/core/src/mindustry/world/meta/Stat.java @@ -0,0 +1,85 @@ +package mindustry.world.meta; + +import arc.*; + +import java.util.*; + +/** Describes one type of stat for content. */ +public enum Stat{ + health, + size, + displaySize, + buildTime, + buildCost, + memoryCapacity, + explosiveness, + flammability, + radioactivity, + heatCapacity, + viscosity, + temperature, + speed, + buildSpeed, + mineSpeed, + mineTier, + baseDeflectChance, + lightningChance, + lightningDamage, + + itemCapacity(StatCat.items), + itemsMoved(StatCat.items), + launchTime(StatCat.items), + maxConsecutive(StatCat.items), + + liquidCapacity(StatCat.liquids), + + powerCapacity(StatCat.power), + powerUse(StatCat.power), + powerDamage(StatCat.power), + powerRange(StatCat.power), + powerConnections(StatCat.power), + basePowerGeneration(StatCat.power), + + tiles(StatCat.crafting), + input(StatCat.crafting), + output(StatCat.crafting), + productionTime(StatCat.crafting), + drillTier(StatCat.crafting), + drillSpeed(StatCat.crafting), + maxUnits(StatCat.crafting), + linkRange(StatCat.crafting), + instructions(StatCat.crafting), + + speedIncrease(StatCat.shooting), + repairTime(StatCat.shooting), + range(StatCat.shooting), + shootRange(StatCat.shooting), + inaccuracy(StatCat.shooting), + shots(StatCat.shooting), + reload(StatCat.shooting), + powerShot(StatCat.shooting), + targetsAir(StatCat.shooting), + targetsGround(StatCat.shooting), + damage(StatCat.shooting), + ammo(StatCat.shooting), + shieldHealth(StatCat.shooting), + cooldownTime(StatCat.shooting), + + booster(StatCat.optional), + boostEffect(StatCat.optional), + affinities(StatCat.optional); + + public final StatCat category; + + Stat(StatCat category){ + this.category = category; + } + + Stat(){ + this.category = StatCat.general; + } + + public String localized(){ + return Core.bundle.get("stat." + name().toLowerCase(Locale.ROOT)); + } +} diff --git a/core/src/mindustry/world/meta/StatCategory.java b/core/src/mindustry/world/meta/StatCat.java similarity index 91% rename from core/src/mindustry/world/meta/StatCategory.java rename to core/src/mindustry/world/meta/StatCat.java index e6c91b04bb..8dfd42d3c5 100644 --- a/core/src/mindustry/world/meta/StatCategory.java +++ b/core/src/mindustry/world/meta/StatCat.java @@ -3,7 +3,7 @@ package mindustry.world.meta; import arc.*; /** A specific category for a stat. */ -public enum StatCategory{ +public enum StatCat{ general, power, liquids, diff --git a/core/src/mindustry/world/meta/BlockStats.java b/core/src/mindustry/world/meta/Stats.java similarity index 55% rename from core/src/mindustry/world/meta/BlockStats.java rename to core/src/mindustry/world/meta/Stats.java index ff93a217a9..68f96ff260 100644 --- a/core/src/mindustry/world/meta/BlockStats.java +++ b/core/src/mindustry/world/meta/Stats.java @@ -2,54 +2,71 @@ package mindustry.world.meta; import arc.struct.ObjectMap.*; import arc.struct.*; +import arc.util.*; import mindustry.*; import mindustry.type.*; import mindustry.world.*; import mindustry.world.meta.values.*; /** Hold and organizes a list of block stats. */ -public class BlockStats{ - private final OrderedMap>> map = new OrderedMap<>(); +public class Stats{ + /** Whether to display stats with categories. If false, categories are completely ignored during display. */ + public boolean useCategories = false; + /** Whether these stats are initialized yet. */ + public boolean intialized = false; + + @Nullable + private OrderedMap>> map; private boolean dirty; /** Adds a single float value with this stat, formatted to 2 decimal places. */ - public void add(BlockStat stat, float value, StatUnit unit){ + public void add(Stat stat, float value, StatUnit unit){ add(stat, new NumberValue(value, unit)); } + /** Adds a single float value with this stat and no unit. */ + public void add(Stat stat, float value){ + add(stat, value, StatUnit.none); + } + + /** Adds an integer percent stat value. Value is assumed to be in the 0-1 range. */ + public void addPercent(Stat stat, float value){ + add(stat, new NumberValue((int)(value * 100), StatUnit.percent)); + } + /** Adds a single y/n boolean value. */ - public void add(BlockStat stat, boolean value){ + public void add(Stat stat, boolean value){ add(stat, new BooleanValue(value)); } /** Adds an item value. */ - public void add(BlockStat stat, Item item){ + public void add(Stat stat, Item item){ add(stat, new ItemListValue(new ItemStack(item, 1))); } /** Adds an item value. */ - public void add(BlockStat stat, ItemStack item){ + public void add(Stat stat, ItemStack item){ add(stat, new ItemListValue(item)); } /** Adds an item value. */ - public void add(BlockStat stat, Liquid liquid, float amount, boolean perSecond){ + public void add(Stat stat, Liquid liquid, float amount, boolean perSecond){ add(stat, new LiquidValue(liquid, amount, perSecond)); } - public void add(BlockStat stat, Attribute attr){ + public void add(Stat stat, Attribute attr){ add(stat, attr, false, 1f); } - public void add(BlockStat stat, Attribute attr, float scale){ + public void add(Stat stat, Attribute attr, float scale){ add(stat, attr, false, scale); } - public void add(BlockStat stat, Attribute attr, boolean floating){ + public void add(Stat stat, Attribute attr, boolean floating){ add(stat, attr, floating, 1f); } - public void add(BlockStat stat, Attribute attr, boolean floating, float scale){ + public void add(Stat stat, Attribute attr, boolean floating, float scale){ for(Block block : Vars.content.blocks()){ if(!block.isFloor() || block.asFloor().attributes.get(attr) == 0 || (block.asFloor().isLiquid && !floating)) continue; add(stat, new FloorEfficiencyValue(block.asFloor(), block.asFloor().attributes.get(attr) * scale)); @@ -57,12 +74,14 @@ public class BlockStats{ } /** Adds a single string value with this stat. */ - public void add(BlockStat stat, String format, Object... args){ + public void add(Stat stat, String format, Object... args){ add(stat, new StringValue(format, args)); } /** Adds a stat value. */ - public void add(BlockStat stat, StatValue value){ + public void add(Stat stat, StatValue value){ + if(map == null) map = new OrderedMap<>(); + if(!map.containsKey(stat.category)){ map.put(stat.category, new OrderedMap<>()); } @@ -73,7 +92,9 @@ public class BlockStats{ } /** Removes a stat, if it exists. */ - public void remove(BlockStat stat){ + public void remove(Stat stat){ + if(map == null) map = new OrderedMap<>(); + if(!map.containsKey(stat.category) || !map.get(stat.category).containsKey(stat)){ throw new RuntimeException("No stat entry found: \"" + stat + "\" in block."); } @@ -83,11 +104,13 @@ public class BlockStats{ dirty = true; } - public OrderedMap>> toMap(){ + public OrderedMap>> toMap(){ + if(map == null) map = new OrderedMap<>(); + //sort stats by index if they've been modified if(dirty){ map.orderedKeys().sort(); - for(Entry>> entry : map.entries()){ + for(Entry>> entry : map.entries()){ entry.value.orderedKeys().sort(); } diff --git a/core/src/mindustry/world/meta/values/BlockFilterValue.java b/core/src/mindustry/world/meta/values/BlockFilterValue.java new file mode 100644 index 0000000000..3c4d0fe56e --- /dev/null +++ b/core/src/mindustry/world/meta/values/BlockFilterValue.java @@ -0,0 +1,37 @@ +package mindustry.world.meta.values; + +import arc.func.*; +import arc.scene.ui.layout.*; +import arc.struct.*; +import mindustry.ui.*; +import mindustry.world.*; +import mindustry.world.meta.*; + +import static mindustry.Vars.*; + +public class BlockFilterValue implements StatValue{ + public final Boolf pred; + + public BlockFilterValue(Boolf pred){ + this.pred = pred; + } + + @Override + public void display(Table table){ + Seq list = content.blocks().select(pred); + + table.table(l -> { + l.left(); + + for(int i = 0; i < list.size; i++){ + Block item = list.get(i); + + l.image(item.icon(Cicon.small)).size(8 * 3).padRight(2).padLeft(2).padTop(3).padBottom(3); + l.add(item.localizedName).left().padLeft(1).padRight(4); + if(i % 5 == 4){ + l.row(); + } + } + }); + } +} diff --git a/core/src/mindustry/world/meta/values/BlockListValue.java b/core/src/mindustry/world/meta/values/BlockListValue.java new file mode 100644 index 0000000000..a1cd5adffe --- /dev/null +++ b/core/src/mindustry/world/meta/values/BlockListValue.java @@ -0,0 +1,33 @@ +package mindustry.world.meta.values; + +import arc.scene.ui.layout.*; +import arc.struct.*; +import mindustry.ui.*; +import mindustry.world.*; +import mindustry.world.meta.*; + +public class BlockListValue implements StatValue{ + public final Seq list; + + public BlockListValue(Seq list){ + this.list = list; + } + + @Override + public void display(Table table){ + + table.table(l -> { + l.left(); + + for(int i = 0; i < list.size; i++){ + Block item = list.get(i); + + l.image(item.icon(Cicon.small)).size(8 * 3).padRight(2).padLeft(2).padTop(3).padBottom(3); + l.add(item.localizedName).left().padLeft(1).padRight(4); + if(i % 5 == 4){ + l.row(); + } + } + }); + } +} diff --git a/core/src/mindustry/world/modules/ItemModule.java b/core/src/mindustry/world/modules/ItemModule.java index 16d49484f0..6cd96985cb 100644 --- a/core/src/mindustry/world/modules/ItemModule.java +++ b/core/src/mindustry/world/modules/ItemModule.java @@ -243,6 +243,16 @@ public class ItemModule extends BlockModule{ } } + public void add(ItemSeq stacks){ + stacks.each(this::add); + } + + public void add(ItemModule items){ + for(int i = 0; i < items.items.length; i++){ + add(i, items.items[i]); + } + } + public void add(Item item, int amount){ add(item.id, amount); } @@ -261,12 +271,6 @@ public class ItemModule extends BlockModule{ } } - public void addAll(ItemModule items){ - for(int i = 0; i < items.items.length; i++){ - add(i, items.items[i]); - } - } - public void remove(Item item, int amount){ amount = Math.min(amount, items[item.id]); diff --git a/desktop/src/mindustry/desktop/DesktopLauncher.java b/desktop/src/mindustry/desktop/DesktopLauncher.java index bd9ec3e81b..76b72e3ca3 100644 --- a/desktop/src/mindustry/desktop/DesktopLauncher.java +++ b/desktop/src/mindustry/desktop/DesktopLauncher.java @@ -227,7 +227,9 @@ public class DesktopLauncher extends ClientLauncher{ @Override public void updateLobby(){ - SVars.net.updateLobby(); + if(SVars.net != null){ + SVars.net.updateLobby(); + } } @Override diff --git a/desktop/src/mindustry/desktop/steam/SStats.java b/desktop/src/mindustry/desktop/steam/SStats.java index 7e7c9ecef3..9338aafc5b 100644 --- a/desktop/src/mindustry/desktop/steam/SStats.java +++ b/desktop/src/mindustry/desktop/steam/SStats.java @@ -7,7 +7,7 @@ import mindustry.*; import mindustry.content.*; import mindustry.entities.units.*; import mindustry.game.EventType.*; -import mindustry.game.Stats.*; +import mindustry.game.GameStats.*; import mindustry.gen.*; import mindustry.type.*; @@ -60,7 +60,7 @@ public class SStats implements SteamUserStatsCallback{ // active10Phantoms.complete(); //} - if(Groups.unit.count(u -> u.type() == UnitTypes.crawler && u.team() == player.team()) >= 50){ + if(Groups.unit.count(u -> u.type == UnitTypes.crawler && u.team() == player.team()) >= 50){ active50Crawlers.complete(); } diff --git a/fastlane/metadata/android/en-US/changelogs/29672.txt b/fastlane/metadata/android/en-US/changelogs/29672.txt new file mode 100644 index 0000000000..04c1a277ad --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/29672.txt @@ -0,0 +1,6 @@ +- Fixed "host game" crash, as well as some other prominent bugs +- Logic: Breaking change: All coordinates are now in tiles, not in "world units" +- Logic: Added sensors for ammo (units/turrets) +- Campaign: Added research button in mobile multiplayer (check pause menu) +- Campaign: Added tech tree sharing in multiplayer - only the host can research +- Campaign: Re-added sector invasions, any sector near an enemy base can be invaded diff --git a/gradle.properties b/gradle.properties index 5a37b2f0ab..1642144d7a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=true org.gradle.jvmargs=-Xms256m -Xmx1024m -archash=dfcb9ab4b9f9bb977ed3cff4b8a16c22e076368a +archash=4965359d2857f529f7010c73bc89876d347fa424 diff --git a/jitpack.yml b/jitpack.yml new file mode 100644 index 0000000000..e1dfa83815 --- /dev/null +++ b/jitpack.yml @@ -0,0 +1,4 @@ +before_install: + - wget https://github.com/sormuras/bach/raw/master/install-jdk.sh + - source install-jdk.sh --feature 14 + - jshell --version \ No newline at end of file diff --git a/server/src/mindustry/server/ServerControl.java b/server/src/mindustry/server/ServerControl.java index 48f529b187..7e1be67c52 100644 --- a/server/src/mindustry/server/ServerControl.java +++ b/server/src/mindustry/server/ServerControl.java @@ -30,6 +30,7 @@ import java.time.*; import java.time.format.*; import java.util.*; +import static arc.util.ColorCodes.*; import static arc.util.Log.*; import static mindustry.Vars.*; @@ -37,7 +38,7 @@ public class ServerControl implements ApplicationListener{ private static final int roundExtraTime = 12; private static final int maxLogLength = 1024 * 512; - protected static String[] tags = {"&lc&fb[D]", "&lg&fb[I]", "&ly&fb[W]", "&lr&fb[E]", ""}; + protected static String[] tags = {"&lc&fb[D]&fr", "&lb&fb[I]&fr", "&ly&fb[W]&fr", "&lr&fb[E]", ""}; protected static DateTimeFormatter dateTime = DateTimeFormatter.ofPattern("MM-dd-yyyy HH:mm:ss"), autosaveDate = DateTimeFormatter.ofPattern("MM-dd-yyyy_HH-mm-ss"); @@ -74,22 +75,27 @@ public class ServerControl implements ApplicationListener{ lastMode = Gamemode.survival; } - Log.setLogger((level, text) -> { - String result = "[" + dateTime.format(LocalDateTime.now()) + "] " + format(tags[level.ordinal()] + " " + text + "&fr"); + logger = (level1, text) -> { + String result = bold + lightBlack + "[" + dateTime.format(LocalDateTime.now()) + "] " + reset + format(tags[level1.ordinal()] + " " + text + "&fr"); System.out.println(result); if(Config.logging.bool()){ - logToFile("[" + dateTime.format(LocalDateTime.now()) + "] " + formatColors(tags[level.ordinal()] + " " + text + "&fr", false)); + logToFile("[" + dateTime.format(LocalDateTime.now()) + "] " + formatColors(tags[level1.ordinal()] + " " + text + "&fr", false)); } if(socketOutput != null){ try{ socketOutput.println(formatColors(text + "&fr", false)); - }catch(Throwable e){ - err("Error occurred logging to socket: @", e.getClass().getSimpleName()); + }catch(Throwable e1){ + err("Error occurred logging to socket: @", e1.getClass().getSimpleName()); } } - }); + }; + + formatter = (text, useColors, arg) -> { + text = Strings.format(text.replace("@", "&fb&lb@&fr"), arg); + return useColors ? addColors(text) : removeColors(text); + }; Time.setDeltaProvider(() -> Core.graphics.getDeltaTime() * 60f); @@ -115,12 +121,12 @@ public class ServerControl implements ApplicationListener{ if(args.length > 0){ commands.addAll(Strings.join(" ", args).split(",")); - info("&lmFound @ command-line arguments to parse.", commands.size); + info("Found @ command-line arguments to parse.", commands.size); } if(!Config.startCommands.string().isEmpty()){ String[] startup = Strings.join(" ", Config.startCommands.string()).split(","); - info("&lmFound @ startup commands.", startup.length); + info("Found @ startup commands.", startup.length); commands.addAll(startup); } @@ -128,7 +134,7 @@ public class ServerControl implements ApplicationListener{ CommandResponse response = handler.handleMessage(s); if(response.type != ResponseType.valid){ err("Invalid command argument sent: '@': @", s, response.type.name()); - err("Argument usage: &lc , "); + err("Argument usage: &lb , "); } } }); @@ -141,7 +147,7 @@ public class ServerControl implements ApplicationListener{ if(Version.build == -1){ warn("&lyYour server is running a custom build, which means that client checking is disabled."); - warn("&lyIt is highly advised to specify which version you're using by building with gradle args &lc-Pbuildversion=&lm&ly."); + warn("&lyIt is highly advised to specify which version you're using by building with gradle args &lb&fb-Pbuildversion=&lr"); } //set up default shuffle mode @@ -154,9 +160,9 @@ public class ServerControl implements ApplicationListener{ Events.on(GameOverEvent.class, event -> { if(inExtraRound) return; if(state.rules.waves){ - info("&lcGame over! Reached wave &ly@&lc with &ly@&lc players online on map &ly@&lc.", state.wave, Groups.player.size(), Strings.capitalize(state.map.name())); + info("Game over! Reached wave @ with @ players online on map @.", state.wave, Groups.player.size(), Strings.capitalize(state.map.name())); }else{ - info("&lcGame over! Team &ly@&lc is victorious with &ly@&lc players online on map &ly@&lc.", event.winner.name, Groups.player.size(), Strings.capitalize(state.map.name())); + info("Game over! Team @ is victorious with @ players online on map @.", event.winner.name, Groups.player.size(), Strings.capitalize(state.map.name())); } //set next map to be played @@ -209,11 +215,11 @@ public class ServerControl implements ApplicationListener{ String fileName = "auto_" + mapName + "_" + date + "." + saveExtension; Fi file = saveDirectory.child(fileName); - info("&lbAutosaving..."); + info("Autosaving..."); try{ SaveIO.save(file); - info("&lbAutosave completed."); + info("Autosave completed."); }catch(Throwable e){ err("Autosave failed.", e); } @@ -241,25 +247,25 @@ public class ServerControl implements ApplicationListener{ Timer.schedule(() -> Core.settings.forceSave(), saveInterval, saveInterval); if(!mods.list().isEmpty()){ - info("&lc@ mods loaded.", mods.list().size); + info("@ mods loaded.", mods.list().size); } toggleSocket(Config.socketInput.bool()); - info("&lcServer loaded. Type &ly'help'&lc for help."); + info("Server loaded. Type @ for help.", "'help'"); } private void registerCommands(){ handler.register("help", "Displays this command list.", arg -> { info("Commands:"); for(Command command : handler.getCommandList()){ - info(" &y" + command.text + (command.paramText.isEmpty() ? "" : " ") + command.paramText + " - &lm" + command.description); + info(" &b&lb " + command.text + (command.paramText.isEmpty() ? "" : " &lc&fi") + command.paramText + "&fr - &lw" + command.description); } }); handler.register("version", "Displays server version info.", arg -> { - info("&lmVersion: &lyMindustry @-@ @ / build @", Version.number, Version.modifier, Version.type, Version.build + (Version.revision == 0 ? "" : "." + Version.revision)); - info("&lmJava Version: &ly@", System.getProperty("java.version")); + info("Version: Mindustry @-@ @ / build @", Version.number, Version.modifier, Version.type, Version.build + (Version.revision == 0 ? "" : "." + Version.revision)); + info("Java Version: @", System.getProperty("java.version")); }); handler.register("exit", "Exit the server application.", arg -> { @@ -299,7 +305,7 @@ public class ServerControl implements ApplicationListener{ result = maps.all().find(map -> map.name().equalsIgnoreCase(arg[0].replace('_', ' ')) || map.name().equalsIgnoreCase(arg[0])); if(result == null){ - err("No map with name &y'@'&lr found.", arg[0]); + err("No map with name '@' found.", arg[0]); return; } }else{ @@ -329,21 +335,21 @@ public class ServerControl implements ApplicationListener{ if(!maps.all().isEmpty()){ info("Maps:"); for(Map map : maps.all()){ - info(" &ly@: &lb&fi@ / @x@", map.name(), map.custom ? "Custom" : "Default", map.width, map.height); + info(" @: &fi@ / @x@", map.name(), map.custom ? "Custom" : "Default", map.width, map.height); } }else{ info("No maps found."); } - info("&lyMap directory: &lb&fi@", customMapDirectory.file().getAbsoluteFile().toString()); + info("Map directory: &fi@", customMapDirectory.file().getAbsoluteFile().toString()); }); handler.register("reloadmaps", "Reload all maps from disk.", arg -> { int beforeMaps = maps.all().size; maps.reload(); if(maps.all().size > beforeMaps){ - info("&lc@&ly new map(s) found and reloaded.", maps.all().size - beforeMaps); + info("@ new map(s) found and reloaded.", maps.all().size - beforeMaps); }else{ - info("&lyMaps reloaded."); + info("Maps reloaded."); } }); @@ -352,23 +358,23 @@ public class ServerControl implements ApplicationListener{ info("Status: &rserver closed"); }else{ info("Status:"); - info(" &lyPlaying on map &fi@&fb &lb/&ly Wave @", Strings.capitalize(state.map.name()), state.wave); + info(" Playing on map &fi@ / Wave @", Strings.capitalize(state.map.name()), state.wave); if(state.rules.waves){ - info("&ly @ enemies.", state.enemies); + info(" @ enemies.", state.enemies); }else{ - info("&ly @ seconds until next wave.", (int)(state.wavetime / 60)); + info(" @ seconds until next wave.", (int)(state.wavetime / 60)); } - info(" &ly@ FPS, @ MB used.", Core.graphics.getFramesPerSecond(), Core.app.getJavaHeap() / 1024 / 1024); + info(" @ FPS, @ MB used.", Core.graphics.getFramesPerSecond(), Core.app.getJavaHeap() / 1024 / 1024); if(Groups.player.size() > 0){ - info(" &lyPlayers: @", Groups.player.size()); + info(" Players: @", Groups.player.size()); for(Player p : Groups.player){ - info(" &y@ / @", p.name(), p.uuid()); + info(" @ / @", p.name, p.uuid()); } }else{ - info(" &lyNo players connected."); + info(" No players connected."); } } }); @@ -377,30 +383,30 @@ public class ServerControl implements ApplicationListener{ if(!mods.list().isEmpty()){ info("Mods:"); for(LoadedMod mod : mods.list()){ - info(" &ly@ &lcv@", mod.meta.displayName(), mod.meta.version); + info(" @ &fi@", mod.meta.displayName(), mod.meta.version); } }else{ info("No mods found."); } - info("&lyMod directory: &lb&fi@", modDirectory.file().getAbsoluteFile().toString()); + info("Mod directory: &fi@", modDirectory.file().getAbsoluteFile().toString()); }); handler.register("mod", "", "Display information about a loaded plugin.", arg -> { LoadedMod mod = mods.list().find(p -> p.meta.name.equalsIgnoreCase(arg[0])); if(mod != null){ - info("Name: &ly@", mod.meta.displayName()); - info("Internal Name: &ly@", mod.name); - info("Version: &ly@", mod.meta.version); - info("Author: &ly@", mod.meta.author); - info("Path: &ly@", mod.file.path()); - info("Description: &ly@", mod.meta.description); + info("Name: @", mod.meta.displayName()); + info("Internal Name: @", mod.name); + info("Version: @", mod.meta.version); + info("Author: @", mod.meta.author); + info("Path: @", mod.file.path()); + info("Description: @", mod.meta.description); }else{ - info("No mod with name &ly'@'&lg found."); + info("No mod with name '@' found."); } }); handler.register("js", "", "Run arbitrary Javascript.", arg -> { - info("&lc" + mods.getScripts().runConsole(arg[0])); + info("&fi&lw&fb" + mods.getScripts().runConsole(arg[0])); }); handler.register("say", "", "Send a message to all players.", arg -> { @@ -411,7 +417,7 @@ public class ServerControl implements ApplicationListener{ Call.sendMessage("[scarlet][[Server]:[] " + arg[0]); - info("&lyServer: &lb@", arg[0]); + info("&fi&lcServer: &fr@", "&lw" + arg[0]); }); @@ -426,7 +432,7 @@ public class ServerControl implements ApplicationListener{ JsonValue base = JsonIO.json().fromJson(null, rules); if(arg.length == 0){ - Log.info("&lyRules:\n@", JsonIO.print(rules)); + Log.info("Rules:\n@", JsonIO.print(rules)); }else if(arg.length == 1){ Log.err("Invalid usage. Specify which rule to remove or add."); }else{ @@ -438,7 +444,7 @@ public class ServerControl implements ApplicationListener{ boolean remove = arg[0].equals("remove"); if(remove){ if(base.has(arg[1])){ - Log.info("Rule &lc'@'&lg removed.", arg[1]); + Log.info("Rule '@' removed.", arg[1]); base.remove(arg[1]); }else{ Log.err("Rule not defined, so not removed."); @@ -462,7 +468,7 @@ public class ServerControl implements ApplicationListener{ base.remove(value.name); } base.addChild(arg[1], value); - Log.info("Changed rule: &ly@", value.toString().replace("\n", " ")); + Log.info("Changed rule: @", value.toString().replace("\n", " ")); }catch(Throwable e){ Log.err("Error parsing rule JSON: @", e.getMessage()); } @@ -501,7 +507,7 @@ public class ServerControl implements ApplicationListener{ handler.register("playerlimit", "[off/somenumber]", "Set the server player limit.", arg -> { if(arg.length == 0){ - info("Player limit is currently &lc@.", netServer.admins.getPlayerLimit() == 0 ? "off" : netServer.admins.getPlayerLimit()); + info("Player limit is currently @.", netServer.admins.getPlayerLimit() == 0 ? "off" : netServer.admins.getPlayerLimit()); return; } if(arg[0].equals("off")){ @@ -521,11 +527,11 @@ public class ServerControl implements ApplicationListener{ handler.register("config", "[name] [value...]", "Configure server settings.", arg -> { if(arg.length == 0){ - info("&lyAll config values:"); + info("All config values:"); for(Config c : Config.all){ - Log.info("&ly| &lc@:&lm @", c.name(), c.get()); - Log.info("&ly| | @", c.description); - Log.info("&ly|"); + Log.info("&lk| @: @", c.name(), "&lc&fi" + c.get()); + Log.info("&lk| | &lw" + c.description); + Log.info("&lk|"); } return; } @@ -533,7 +539,7 @@ public class ServerControl implements ApplicationListener{ try{ Config c = Config.valueOf(arg[0]); if(arg.length == 1){ - Log.info("&lc'@'&lg is currently &lc@.", c.name(), c.get()); + Log.info("'@' is currently @.", c.name(), c.get()); }else{ if(c.isBool()){ c.set(arg[1].equals("on") || arg[1].equals("true")); @@ -548,7 +554,7 @@ public class ServerControl implements ApplicationListener{ c.set(arg[1]); } - Log.info("&lc@&lg set to &lc@.", c.name(), c.get()); + Log.info("@ set to @.", c.name(), c.get()); Core.settings.forceSave(); } }catch(IllegalArgumentException e){ @@ -558,9 +564,9 @@ public class ServerControl implements ApplicationListener{ handler.register("subnet-ban", "[add/remove] [address]", "Ban a subnet. This simply rejects all connections with IPs starting with some string.", arg -> { if(arg.length == 0){ - Log.info("Subnets banned: &lc@", netServer.admins.getSubnetBans().isEmpty() ? "" : ""); + Log.info("Subnets banned: @", netServer.admins.getSubnetBans().isEmpty() ? "" : ""); for(String subnet : netServer.admins.getSubnetBans()){ - Log.info("&ly " + subnet + ""); + Log.info("&lw " + subnet + ""); } }else if(arg.length == 1){ err("You must provide a subnet to add or remove."); @@ -572,7 +578,7 @@ public class ServerControl implements ApplicationListener{ } netServer.admins.addSubnetBan(arg[1]); - Log.info("Banned &ly@&lc**", arg[1]); + Log.info("Banned @**", arg[1]); }else if(arg[0].equals("remove")){ if(!netServer.admins.getSubnetBans().contains(arg[1])){ err("That subnet isn't banned."); @@ -580,7 +586,7 @@ public class ServerControl implements ApplicationListener{ } netServer.admins.removeSubnetBan(arg[1]); - Log.info("Unbanned &ly@&lc**", arg[1]); + Log.info("Unbanned @**", arg[1]); }else{ err("Incorrect usage. You must provide add/remove as the second argument."); } @@ -589,12 +595,12 @@ public class ServerControl implements ApplicationListener{ handler.register("whitelisted", "List the entire whitelist.", arg -> { if(netServer.admins.getWhitelisted().isEmpty()){ - info("&lyNo whitelisted players found."); + info("No whitelisted players found."); return; } - info("&lyWhitelist:"); - netServer.admins.getWhitelisted().each(p -> Log.info("- &ly@", p.lastName)); + info("Whitelist:"); + netServer.admins.getWhitelisted().each(p -> info("- @", p.lastName)); }); handler.register("whitelist-add", "", "Add a player to the whitelist by ID.", arg -> { @@ -605,7 +611,7 @@ public class ServerControl implements ApplicationListener{ } netServer.admins.whitelist(arg[0]); - info("Player &ly'@'&lg has been whitelisted.", info.lastName); + info("Player '@' has been whitelisted.", info.lastName); }); handler.register("whitelist-remove", "", "Remove a player to the whitelist by ID.", arg -> { @@ -616,18 +622,18 @@ public class ServerControl implements ApplicationListener{ } netServer.admins.unwhitelist(arg[0]); - info("Player &ly'@'&lg has been un-whitelisted.", info.lastName); + info("Player '@' has been un-whitelisted.", info.lastName); }); handler.register("shuffle", "[none/all/custom/builtin]", "Set map shuffling mode.", arg -> { if(arg.length == 0){ - info("Shuffle mode current set to &ly'@'&lg.", maps.getShuffleMode()); + info("Shuffle mode current set to '@'.", maps.getShuffleMode()); }else{ try{ ShuffleMode mode = ShuffleMode.valueOf(arg[0]); Core.settings.put("shufflemode", mode.name()); maps.setShuffleMode(mode); - info("Shuffle mode set to &ly'@'&lg.", arg[0]); + info("Shuffle mode set to '@'.", arg[0]); }catch(Exception e){ err("Invalid shuffle mode."); } @@ -638,7 +644,7 @@ public class ServerControl implements ApplicationListener{ Map res = maps.all().find(map -> map.name().equalsIgnoreCase(arg[0].replace('_', ' ')) || map.name().equalsIgnoreCase(arg[0])); if(res != null){ nextMapOverride = res; - Log.info("Next map set to &ly'@'.", res.name()); + Log.info("Next map set to '@'.", res.name()); }else{ Log.err("No map '@' found.", arg[0]); } @@ -694,9 +700,9 @@ public class ServerControl implements ApplicationListener{ if(bans.size == 0){ info("No ID-banned players have been found."); }else{ - info("&lyBanned players [ID]:"); + info("Banned players [ID]:"); for(PlayerInfo info : bans){ - info(" &ly @ / Last known name: '@'", info.id, info.lastName); + info(" @ / Last known name: '@'", info.id, info.lastName); } } @@ -705,13 +711,13 @@ public class ServerControl implements ApplicationListener{ if(ipbans.size == 0){ info("No IP-banned players have been found."); }else{ - info("&lmBanned players [IP]:"); + info("Banned players [IP]:"); for(String string : ipbans){ PlayerInfo info = netServer.admins.findByIP(string); if(info != null){ - info(" &lm '@' / Last known name: '@' / ID: '@'", string, info.lastName, info.id); + info(" '@' / Last known name: '@' / ID: '@'", string, info.lastName, info.id); }else{ - info(" &lm '@' (No known name or info)", string); + info(" '@' (No known name or info)", string); } } } @@ -765,7 +771,7 @@ public class ServerControl implements ApplicationListener{ netServer.admins.unAdminPlayer(target.id); } if(playert != null) playert.admin = add; - info("Changed admin status of player: &ly@", target.lastName); + info("Changed admin status of player: @", target.lastName); }else{ err("Nobody with that name or ID could be found. If adding an admin by name, make sure they're online; otherwise, use their UUID."); } @@ -777,7 +783,7 @@ public class ServerControl implements ApplicationListener{ if(admins.size == 0){ info("No admins have been found."); }else{ - info("&lyAdmins:"); + info("Admins:"); for(PlayerInfo info : admins){ info(" &lm @ / ID: '@' / IP: '@'", info.lastName, info.id, info.lastIP); } @@ -788,10 +794,10 @@ public class ServerControl implements ApplicationListener{ if(Groups.player.size() == 0){ info("No players are currently in the server."); }else{ - info("&lyPlayers: @", Groups.player.size()); + info("Players: @", Groups.player.size()); for(Player user : Groups.player){ PlayerInfo userInfo = user.getInfo(); - info(" &lm @ / ID: '@' / IP: '@' / Admin: '@'", userInfo.lastName, userInfo.id, userInfo.lastIP, userInfo.admin); + info(" &lm @ / ID: @ / IP: @ / Admin: @", userInfo.lastName, userInfo.id, userInfo.lastIP, userInfo.admin); } } }); @@ -849,7 +855,7 @@ public class ServerControl implements ApplicationListener{ info("Save files: "); for(Fi file : saveDirectory.list()){ if(file.extension().equals(saveExtension)){ - info("| &ly@", file.nameWithoutExtension()); + info("| @", file.nameWithoutExtension()); } } }); @@ -860,7 +866,7 @@ public class ServerControl implements ApplicationListener{ return; } - info("&lyCore destroyed."); + info("Core destroyed."); inExtraRound = false; Events.fire(new GameOverEvent(Team.crux)); }); @@ -870,16 +876,16 @@ public class ServerControl implements ApplicationListener{ ObjectSet infos = netServer.admins.findByName(arg[0]); if(infos.size > 0){ - info("&lgPlayers found: @", infos.size); + info("Players found: @", infos.size); int i = 0; for(PlayerInfo info : infos){ - info("&lc[@] Trace info for player '@' / UUID @", i++, info.lastName, info.id); - info(" &lyall names used: @", info.names); - info(" &lyIP: @", info.lastIP); - info(" &lyall IPs used: @", info.ips); - info(" &lytimes joined: @", info.timesJoined); - info(" &lytimes kicked: @", info.timesKicked); + info("[@] Trace info for player '@' / UUID @", i++, info.lastName, info.id); + info(" all names used: @", info.names); + info(" IP: @", info.lastIP); + info(" all IPs used: @", info.ips); + info(" times joined: @", info.timesJoined); + info(" times kicked: @", info.timesKicked); } }else{ info("Nobody with that name could be found."); @@ -891,11 +897,11 @@ public class ServerControl implements ApplicationListener{ ObjectSet infos = netServer.admins.searchNames(arg[0]); if(infos.size > 0){ - info("&lgPlayers found: @", infos.size); + info("Players found: @", infos.size); int i = 0; for(PlayerInfo info : infos){ - info("- &lc[@] &ly'@'&lc / &lm@", i++, info.lastName, info.id); + info("- [@] '@' / @", i++, info.lastName, info.id); } }else{ info("Nobody with that name could be found."); @@ -906,10 +912,10 @@ public class ServerControl implements ApplicationListener{ int pre = (int)(Core.app.getJavaHeap() / 1024 / 1024); System.gc(); int post = (int)(Core.app.getJavaHeap() / 1024 / 1024); - info("&ly@&lg MB collected. Memory usage now at &ly@&lg MB.", pre - post, post); + info("@ MB collected. Memory usage now at @ MB.", pre - post, post); }); - handler.register("yes", "Run the above \"did you mean\" suggestion.", arg -> { + handler.register("yes", "Run the last suggested incorrect command.", arg -> { if(yes == null){ err("There is nothing to say yes to."); }else{ @@ -1036,7 +1042,7 @@ public class ServerControl implements ApplicationListener{ serverSocket.bind(new InetSocketAddress(Config.socketInputAddress.string(), Config.socketInputPort.num())); while(true){ Socket client = serverSocket.accept(); - info("&lmReceived command socket connection: &lb@", serverSocket.getLocalSocketAddress()); + info("&lkReceived command socket connection: &fi@", serverSocket.getLocalSocketAddress()); BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream())); socketOutput = new PrintWriter(client.getOutputStream(), true); String line; @@ -1044,7 +1050,7 @@ public class ServerControl implements ApplicationListener{ String result = line; Core.app.post(() -> handleCommandString(result)); } - info("&lmLost command socket connection: &lb@", serverSocket.getLocalSocketAddress()); + info("&lkLost command socket connection: &fi@", serverSocket.getLocalSocketAddress()); socketOutput = null; } }catch(BindException b){ diff --git a/server/src/mindustry/server/ServerLauncher.java b/server/src/mindustry/server/ServerLauncher.java index 4c8a2bb8e6..ad274954ea 100644 --- a/server/src/mindustry/server/ServerLauncher.java +++ b/server/src/mindustry/server/ServerLauncher.java @@ -29,10 +29,10 @@ public class ServerLauncher implements ApplicationListener{ Vars.platform = new Platform(){}; Vars.net = new Net(platform.getNet()); - Log.setLogger((level, text) -> { - String result = "[" + dateTime.format(LocalDateTime.now()) + "] " + format(tags[level.ordinal()] + " " + text + "&fr"); - System.out.println(result); - }); + logger = (level1, text) -> { + String result = "[" + dateTime.format(LocalDateTime.now()) + "] " + format(tags[level1.ordinal()] + " " + text + "&fr"); + System.out.println(result); + }; new HeadlessApplication(new ServerLauncher(), null, throwable -> CrashSender.send(throwable, f -> {})); }catch(Throwable t){ CrashSender.send(t, f -> {}); diff --git a/tests/src/test/java/ApplicationTests.java b/tests/src/test/java/ApplicationTests.java index 01cf659df2..67d30e5ff9 100644 --- a/tests/src/test/java/ApplicationTests.java +++ b/tests/src/test/java/ApplicationTests.java @@ -36,7 +36,7 @@ public class ApplicationTests{ try{ boolean[] begins = {false}; Throwable[] exceptionThrown = {null}; - Log.setUseColors(false); + Log.useColors = false; ApplicationCore core = new ApplicationCore(){ @Override diff --git a/tests/src/test/java/SectorTests.java b/tests/src/test/java/SectorTests.java index 4110670eed..6f07e77313 100644 --- a/tests/src/test/java/SectorTests.java +++ b/tests/src/test/java/SectorTests.java @@ -66,7 +66,7 @@ public class SectorTests{ outer: for(int i = 1; i <= 1000; i++){ for(SpawnGroup spawn : spawns){ - if(spawn.effect == StatusEffects.boss && spawn.getUnitsSpawned(i) > 0){ + if(spawn.effect == StatusEffects.boss && spawn.getSpawned(i) > 0){ bossWave = i; break outer; } @@ -84,7 +84,7 @@ public class SectorTests{ for(int i = 1; i <= bossWave; i++){ int total = 0; for(SpawnGroup spawn : spawns){ - total += spawn.getUnitsSpawned(i); + total += spawn.getSpawned(i); } assertNotEquals(0, total, "Sector " + zone + " has no spawned enemies at wave " + i); diff --git a/tests/src/test/java/power/FakeGraphics.java b/tests/src/test/java/power/FakeGraphics.java index 378f675224..5f64aa61f3 100644 --- a/tests/src/test/java/power/FakeGraphics.java +++ b/tests/src/test/java/power/FakeGraphics.java @@ -63,11 +63,6 @@ public class FakeGraphics extends Graphics{ return 0; } - @Override - public float getRawDeltaTime(){ - return 0; - } - @Override public int getFramesPerSecond(){ return 0; diff --git a/tests/src/test/java/power/PowerTestFixture.java b/tests/src/test/java/power/PowerTestFixture.java index 7bd7e821b2..ceed02b530 100644 --- a/tests/src/test/java/power/PowerTestFixture.java +++ b/tests/src/test/java/power/PowerTestFixture.java @@ -37,7 +37,7 @@ public class PowerTestFixture{ Vars.state = new GameState(); Vars.tree = new FileTree(); content.createBaseContent(); - Log.setUseColors(false); + Log.useColors = false; Time.setDeltaProvider(() -> 0.5f); } diff --git a/tools/src/mindustry/tools/Generators.java b/tools/src/mindustry/tools/Generators.java index d8f67eee8c..645b2ea7f0 100644 --- a/tools/src/mindustry/tools/Generators.java +++ b/tools/src/mindustry/tools/Generators.java @@ -153,9 +153,9 @@ public class Generators{ ImagePacker.generate("cracks", () -> { RidgedPerlin r = new RidgedPerlin(1, 3); - for(int size = 1; size <= Block.maxCrackSize; size++){ + for(int size = 1; size <= BlockRenderer.maxCrackSize; size++){ int dim = size * 32; - int steps = Block.crackRegions; + int steps = BlockRenderer.crackRegions; for(int i = 0; i < steps; i++){ float fract = i / (float)steps; diff --git a/tools/src/mindustry/tools/ImagePacker.java b/tools/src/mindustry/tools/ImagePacker.java index 2533f99af9..1142de8a26 100644 --- a/tools/src/mindustry/tools/ImagePacker.java +++ b/tools/src/mindustry/tools/ImagePacker.java @@ -28,10 +28,10 @@ public class ImagePacker{ Vars.headless = true; ArcNativesLoader.load(); - Log.setLogger(new NoopLogHandler()); + Log.logger = new NoopLogHandler(); Vars.content = new ContentLoader(); Vars.content.createBaseContent(); - Log.setLogger(new DefaultLogHandler()); + Log.logger = new DefaultLogHandler(); Fi.get("../../../assets-raw/sprites_out").walk(path -> { if(!path.extEquals("png")) return;