Compare commits

...

116 Commits
v123 ... v124.1

Author SHA1 Message Date
Anuken
3d8479355c Fixed random key 2021-02-08 10:45:08 -05:00
MEEP of Faith
86c702861e Remove NullPointerExeption error (#4624) 2021-02-08 10:44:08 -05:00
CxZxRainzz
47ec13eef4 Update servers_v6.json (#4628)
Add CxZx
2021-02-08 10:39:41 -05:00
Anuken
d8cd0d867c Merge remote-tracking branch 'origin/master' 2021-02-08 10:37:09 -05:00
Anuken
d5ede6b1bb Un-blacklisted arc.net 2021-02-08 10:37:06 -05:00
키에르
fd931564aa Add sources jar (#4612)
* Add sources jar

* Update build.gradle

* Fix classes not found

* :core

* formatting

Co-authored-by: Anuken <arnukren@gmail.com>
2021-02-08 09:59:11 -05:00
Anuken
6b19178a13 Fixed #4620 2021-02-08 09:52:41 -05:00
Anuken
64acd6c1e4 Merge remote-tracking branch 'origin/master' 2021-02-08 09:42:57 -05:00
Anuken
560914bebe Added more unit stats to database 2021-02-08 09:42:49 -05:00
Anuken
2edec90dfb Cleanup 2021-02-08 09:25:46 -05:00
Catchears
10c1305eae update german translation for build 124 (#4618)
* update german translation for build 124

* shorten text to fit on the button without newline
2021-02-07 17:16:56 -05:00
Joshua Fan
852c98f004 "[E] to enable building" hint while building is paused (#3857)
* Show building hints while building is paused

* Add hint: [E] to enable building

* Remove .left()

* Fix build enable hint display inconsistencies when changing build auto-pause setting

* Combine building hint tables

* Fix indentation
2021-02-07 16:58:42 -05:00
Patrick 'Quezler' Mounier
292f771eb1 Draws team regions during schematic placement (#4588)
* Draw team regions during schematic placement

* Fall back to sharded for headless

* Only draw team regions if the schematic is in-world

This seems better than `player != null && req.worldContext` since it wouldn't really result in any visual changes but saves some draw operations.

* This seems cleaner

Although the order of the 1st and 2nd if argument is debatable for performance reasons.
2021-02-07 13:27:09 -05:00
Anuken
1acc771edc 'Fixed' some iOS warnings 2021-02-07 13:08:10 -05:00
Anuken
ee61d0dcfa Cleanup 2021-02-07 13:01:30 -05:00
Anuken
e5f2073de2 Minor unit balance tweaks 2021-02-07 12:49:26 -05:00
Anuken
816be9e9bd Force projector visual cleanup 2021-02-07 12:38:24 -05:00
Sunny Kim
2107af1f0f Force projection disappearing visuals (#4504)
* forceShrink

* forceShrink

* more quicker to tell it apart from a shield down
2021-02-07 12:36:29 -05:00
MEEP of Faith
3bb85b4bda Custom shadow offset (#4472) 2021-02-07 12:34:54 -05:00
YellOw139
e86a3e9d09 [Bundle][RO][Ready-for-Stable] Translation & metadata update + minor fix (#4409)
* #3958 hotfix

* update Romanian Steam desc count

* Create title.txt

* Add files via upload

* Sorter gates

* charge

* Polishing

* More mod browser
2021-02-07 12:33:51 -05:00
Szczepan Zalega
957426c27f Fix some typos in Polish translation (#4411)
* Fix some typos in Polish translation

* Update bundle_pl.properties

(fixup)
2021-02-07 12:33:45 -05:00
Vanguard
8bc90a19d2 [bundle] update bundle_ru.properties (#4477)
* 1 line changed

- noname

* 1 line changed

- hint.placeTurret

* 3 new lines

- servers.disclaimer
- hint.coreUpgrade
- hint.presetLaunch

----
NOTE from Felix Corvus (@Remint32)

This commit and PR are the last ones I made to the repository.
I want to sincerely thank the people I worked with to enhance the game and translate it into Russian.

I want to thank you separately:
Prosta4ok_ua (@Prosta4okua), Vanguard (@XEN0PHIL), BasedUser (@BasedUser), Summet (@summetdev), Anuke (@Anuken), The_Slaylord (@TheSlaylord), Lonely_Ymomfe (@ymomfe), and XZimur (@XZimur)

* 5 changed lines; 2 new lines

Changed:
globalitems
sector.extractionOutpost.description
hint.launch
hint.launch.mobile
hint.presetLaunch

New:
hint.coreIncinerate
hint.coopCampaign

* 2 changed line

hint.coopCampaign
item.spore-pod.details
-
В споровом стручке всё же множественное число в описании, говорится про сами споры. THEY EMIT - они выделяют, IT EMITS - он выделяет.

* 2 changed lines

hint.coreIncinerate
block.interplanetary-accelerator.description

* 3 new line; 1 changed line

New:
setting.showweather.name
link.bug.description
rules.weather.always

Changed:
keybinds.mobile

* 4 new lines; 1 changed line; 1 deleted line

New:
public.confirm.really
bullet.buildingdamage
stat.ammouse
unit.pershot

Changed:
bullet.knockback

Deleted:
bar.limitreached

* Update bundle_ru.properties

падежи

* Update bundle_ru.properties

мелочи

* 1 changed line

stat.ammouse

* 1 new line

none.found

* 6 changed; 1 deleted

Changed:
mod.featured.dialog.title
mods.browser.add
mods.github.open
mod.import.github
mod.jarwarn
mods.guide

Deleted:
mod.featured.title

* 2 changed lines

link.bug.description
mod.scripts.disable

* Update achievements.vdf

* Update description.txt

* 1 changed line

item.thorium.description

* 1 new line

rules.coreincinerates

* 1 new line

bar.boost

* 1 new line

max

* Update bundle_ru.properties

* Update bundle_ru.properties

* rip v3.5 maps

* Update bundle_ru.properties

* Update bundle_ru.properties

* Update bundle_ru.properties

* Prosta4ok obnovi repository

* взлЁт

* 3 new lines

mods.browser.sortdate
mods.browser.sortstars
stat.charge

* Update description.txt

* Update description.txt

* 3 changed, 3 new, 1 deleted

Changed:
mods.browser.sortdate
unit.sei.description
unit.risso.description

New:
mods.browser.reinstall
mods.viewcontent
mods.browser

Deleted:
mod.featured.dialog.title

* 1 line changed

mods.github.open
-
"Репо" is not commonly used and "Репозиторий" does not fit in the button

* 1 new line

mod.installed

Co-authored-by: Felix Corvus <remint32@yandex.ru>
Co-authored-by: Prosta4okua <31485341+Prosta4okua@users.noreply.github.com>
Co-authored-by: Antsiferov Andrew <summet.dev@gmail.com>
2021-02-07 12:33:18 -05:00
Prosta4okua
668144cf2a [Bundle]Update bundle_uk_UA.properties (#4434)
* Update bundle_uk_UA.properties

* Update bundle_uk_UA.properties
2021-02-07 12:32:57 -05:00
Anuken
c5ed056601 Removed unused fx 2021-02-07 12:12:15 -05:00
Anuken
ea9d415b92 iOS inset rotation fix 2021-02-07 11:43:34 -05:00
Anuken
c181204700 Fixed mod descriptions going off-screen 2021-02-07 10:44:45 -05:00
Anuken
ca78cb1a3f Fixed #4616 2021-02-07 10:21:38 -05:00
Anuken
fdafa0933f Mod browser layout tweaks 2021-02-07 10:04:46 -05:00
Anuken
ae92813169 Merge remote-tracking branch 'origin/master' 2021-02-07 09:50:37 -05:00
Anuken
8a248dc787 Mod browser layout fixes 2021-02-07 09:50:33 -05:00
Leonid Skorospelov
f90f611a6d plastanium-wall-large schematicPriority should also be 10 (#4613)
plastanium-wall has it, makes sense to put for large one as well
2021-02-07 09:14:19 -05:00
Xasmedy
1c0d148607 Removed same if statement. (#4607)
Removed an if statement that was the same of the one up.
2021-02-06 18:13:48 -05:00
Anuken
95c8b7ae60 Merge branch 'master' of https://github.com/Anuken/Mindustry 2021-02-06 12:06:03 -05:00
Anuken
25d013b768 Mod browser icon support 2021-02-06 12:05:59 -05:00
Patrick 'Quezler' Mounier
9de8c4d8ac Update servers_be.json (#4602) 2021-02-06 09:09:56 -05:00
Anuken
7e4ffe6a7d Fixed builds failing without Android project 2021-02-06 08:43:15 -05:00
Anuken
b90372a226 Testing bloom on iOS 2021-02-06 08:37:02 -05:00
Anuken
9adbfcac0f Re-added jcenter repo 2021-02-05 14:11:52 -05:00
Anuken
d1eb7743ad Better mod browser horizontal layout 2021-02-05 12:12:08 -05:00
Anuken
fc3352bcb1 Java mod import support / Moved mod browser button 2021-02-05 11:21:27 -05:00
Anuken
9b3360d932 Mod content lists 2021-02-04 21:02:57 -05:00
Anuken
3784bfac77 Allow mod re-import / Save mod repo on import 2021-02-04 19:30:52 -05:00
Anuken
54754cd177 Don't reset processors when links change 2021-02-04 18:19:56 -05:00
Anuken
c48034e420 Merge remote-tracking branch 'origin/master' 2021-02-04 14:12:07 -05:00
Anuken
3299279c89 Removed jcenter repositories 2021-02-04 14:12:03 -05:00
Patrick 'Quezler' Mounier
a6c3ebc467 Save rotation of repair points (#4586) 2021-02-04 10:23:17 -05:00
Anuken
9f510f61c7 Fixed #4584 2021-02-04 09:25:52 -05:00
Anuken
3ae48b8753 Merge remote-tracking branch 'origin/master' 2021-02-03 19:17:14 -05:00
Anuken
f41725b8dc Minimap unit rendering fix 2021-02-03 19:17:07 -05:00
Fatonndev
115df3aedd Add obvilionnetwork.ru Recapture and Attack servers (#4582)
* Add obvilionnetwork.ru Recapture and Attack servers

* Update servers_v6.json

* Update servers_v6.json
2021-02-03 17:04:55 -05:00
buthed010203
3c136ad0ba add transportation group to container and vault (#4499)
this allows for them to be placed over belts so that people no longer have to clear an area around the core when placing vaults around it.
2021-02-02 15:32:52 -05:00
Patrick 'Quezler' Mounier
48ee3d255d Make timescale accessible to logic (#4503) 2021-02-02 15:20:36 -05:00
QmelZ
b05f867800 add rotation to ParticleEffect (#4501)
* add rotation to ParticleEffect

* add offset
2021-02-02 13:14:47 -05:00
Anuken
55b790a0d4 Merge remote-tracking branch 'origin/master' 2021-02-02 13:13:45 -05:00
Anuken
57f6c9dfd9 Mech rebalancing 2021-02-02 13:13:38 -05:00
Nikopol
2f9c95afd0 Moving server to C.A.M.S. (#4497)
NikoCHIO now in C.A.M.S.
2021-02-02 09:22:54 -05:00
彭瑞暄
4fe1457d96 Updated zh-TW translations (#4449)
* Update bundle_zh_TW.properties

mod sorting & charge

* Changed "Charge"

A better translation came to mind
2021-02-01 09:29:03 -05:00
Folker
2589a27cb5 Create FileTreeInitEvent (#4489)
* filetree init event

* actions do your thing
2021-02-01 09:27:24 -05:00
Anuken
8704e14fd7 Fixed #4493 / Fixed #4492 / Fixed #4490 2021-02-01 09:24:39 -05:00
Anuken
2b17dbe390 #4487 but properly implemented 2021-01-31 19:44:29 -05:00
Anuken
919012608a Merge remote-tracking branch 'origin/master' 2021-01-31 16:35:17 -05:00
Anuken
5675a5b035 F 2021-01-31 16:35:13 -05:00
RebornTrack970
6724752980 Added server Omega (#4435)
* Added server Omega

This is the hub of my servers. The other ip for it is: omega.my.to:25906 but that one sometimes doesnt work.

* changed the ip to dns version

* the ip seems more useable

some devices cant seem to connet to dns.

Co-authored-by: Anuken <arnukren@gmail.com>
2021-01-31 15:58:26 -05:00
UnCaughT
0e078a9c60 Update servers_v6.json (#4452)
Русскоязычный сервер с Hex сражениями.
2021-01-31 15:57:08 -05:00
_owler_#0954
c0b1dfc55a closed. (#4481) 2021-01-31 15:52:55 -05:00
Anuken
3d201f7042 Fixed #4474 / Fixed #4475 / Fixed #4476 / Fixed #4483 2021-01-31 15:51:42 -05:00
Anuken
f1a5aae30a Merge remote-tracking branch 'origin/master' 2021-01-29 12:02:36 -05:00
Anuken
9142da2b70 Stop shooting upon crawler death 2021-01-29 12:02:31 -05:00
Vajda Simon
022c76bf45 bundle_hu.properties (#4464)
They spoke on Discord because of an incorrect translation.
2021-01-29 09:40:07 -05:00
Patrick 'Quezler' Mounier
bc6db72df0 Make fillitems fill the core to the brim (#4462)
Instead of just setting all items to the capacity of the first found core.
2021-01-29 09:38:26 -05:00
Anuken
d512aa5b21 Fixed #4463 2021-01-29 09:32:29 -05:00
Anuken
7cdf7a21fe Minor power graph optimizations 2021-01-28 12:07:50 -05:00
Anuken
9a5a6e1ce2 Merge remote-tracking branch 'origin/master' 2021-01-28 10:23:07 -05:00
Anuken
5a258d2830 Fixed #4461 2021-01-28 10:23:02 -05:00
Patrick 'Quezler' Mounier
96ee32f8ed Custom Game Underline 2: Electric Boogaloo (#4457)
* Update CustomGameDialog.java

* Remove .row() for consistensy
2021-01-28 09:06:30 -05:00
Joshua Fan
a667a94c8e Draw plastanium conveyors on conveyors layer (#4446) 2021-01-27 12:08:51 -05:00
Anuken
d0572a2a0d Merge remote-tracking branch 'origin/master' 2021-01-27 11:49:08 -05:00
Anuken
47eea79b60 Fixed #4453 / Fixed #4450 2021-01-27 11:49:04 -05:00
Patrick 'Quezler' Mounier
03b37235b3 producer -> generator (#4441) 2021-01-26 11:35:48 -05:00
Patrick 'Quezler' Mounier
6969fbb1e7 Give tsunami the extinguisher flag as well (#4442) 2021-01-26 11:31:36 -05:00
Anuken
ccc59aa61b Minor wave task tweaks 2021-01-26 10:37:36 -05:00
Anuken
c658865907 Dynamic lightning damage tweaks 2021-01-25 17:21:12 -05:00
Sunny Kim
a13d51e09a Charge stat for Item (#4392)
* Stat.discharge, discharge

* Update Items.java

* item.discharge

* full container = somewhat more than 4 full batteries

* Stat.discharge

* stat.discharge = Discharge

* Update core/src/mindustry/type/Item.java

Co-authored-by: Antsiferov Andrew <summet.dev@gmail.com>

* removed dis

* oops

* unit zap

Co-authored-by: Antsiferov Andrew <summet.dev@gmail.com>
2021-01-25 17:10:34 -05:00
Anuken
f827337259 Merge remote-tracking branch 'origin/master' 2021-01-25 16:16:45 -05:00
Anuken
b83c76f728 Improved liquid bridge throughput 2021-01-25 16:16:38 -05:00
Thomas Widyantoko
5666fd4999 new update in_ID translation (#4437)
yes
2021-01-25 14:32:35 -05:00
Anuken
5930e3c350 Cleanup 2021-01-25 12:53:27 -05:00
Anuken
4630e5b3a4 Wave spawning improvements 2021-01-25 10:28:36 -05:00
Anuken
f16990edcb Reconstructors: Accept unit dropped payload when disabled 2021-01-24 20:20:03 -05:00
FrederickDoering
13896589c2 Made disabling payload conveyors output from routers more reliable. (#4433)
Prevented reconstructors from being roughted to when disabled.

Co-authored-by: frederick_efd <frederick.doering@nordson.com>
2021-01-24 20:18:09 -05:00
Anuken
8b5cf5309a Reactive effect flag 2021-01-24 15:22:41 -05:00
Anuken
baedba0d5b Mutual status effect transitions 2021-01-24 14:20:48 -05:00
Anuken
9f926ef71e Merge remote-tracking branch 'origin/master' 2021-01-24 13:08:05 -05:00
Anuken
18a3f8bb68 Fixed #4431 2021-01-24 13:08:01 -05:00
genNAowl
623b7ada11 Let Logic Sense Range (#4425)
Co-authored-by: Leonwang4234 <62972692+Leonwang4234@users.noreply.github.com>
2021-01-24 09:23:12 -05:00
_owler_#0954
6b6d52e2fd Add new minigame (#4427)
PvP version of tower defence
2021-01-24 09:22:44 -05:00
MEEP of Faith
fc91b6b135 Colored construct (#4417) 2021-01-23 15:29:36 -05:00
Anuken
f9c33453f2 Merge remote-tracking branch 'origin/master' 2021-01-23 10:33:10 -05:00
Anuken
ad4650f408 Fixed #4420 / Fixed #4418 / Fixed #4416 2021-01-23 10:33:06 -05:00
Joshua Fan
8ca08a0f14 Make schematics build insulated blocks first, power nodes last (#4414)
* Make schematics build insulated blocks first, power nodes last

* Add schematicPriority property to sort by

* Assign schematicPriority of 10 to power diode
2021-01-23 10:28:26 -05:00
Anuken
792fdef95d Minor parse fix 2021-01-21 21:25:04 -05:00
Anuken
457514a2c8 Fixed #4407 2021-01-21 21:21:02 -05:00
Anuken
027d080c55 Fixed #4403 2021-01-21 16:10:16 -05:00
Anuken
bf8f059c25 Merge remote-tracking branch 'origin/master' 2021-01-21 14:45:51 -05:00
Anuken
a73165353f Fixed #4402 2021-01-21 14:45:43 -05:00
Nikopol
613b5a7703 Update servers_v6.json (#4401) 2021-01-21 10:54:24 -05:00
genNAowl
988791f18d Let Logic set (and read) Illuminator Color (#4374)
* illluminator color logic control

* remove sense

Co-authored-by: Leonwang4234 <62972692+Leonwang4234@users.noreply.github.com>
2021-01-21 09:30:36 -05:00
Patrick 'Quezler' Mounier
1fb608220f Make impact reactor warmup sensable as heat (#4399)
* Make impact reactor warmup sensable as heat

* Update ImpactReactor.java
2021-01-21 09:30:25 -05:00
키에르
56b87a2141 Add kr server (#4378)
* Add kr server

* Update servers_v6.json

Co-authored-by: Blockzilla101 <48705529+Blockzilla101@users.noreply.github.com>

Co-authored-by: Blockzilla101 <48705529+Blockzilla101@users.noreply.github.com>
2021-01-21 09:13:07 -05:00
Anuken
3aee9bddd4 Cleanup 2021-01-21 08:54:14 -05:00
Sunny Kim
65f97edbc5 Add search option for mod browser (#4391)
* add search option

* mods.browser.sortdate

* mods.browser

* Update bundle_ko.properties

* oopsie

* clearPartiali

* half of my brain cells failed to boot
2021-01-21 08:51:43 -05:00
Anuken
d393230add . 2021-01-21 08:50:25 -05:00
Anuken
75d2b0fb69 Fixed #4393 / Fixed #4398 2021-01-21 08:48:36 -05:00
Anuken
0cc9b0b0a3 Mod version parse fix 2021-01-20 16:44:04 -05:00
Anuken
9b6c125233 UI.formatAmount for negative numbers 2021-01-20 16:38:46 -05:00
Anuken
1f2b331bf6 Possible Android mod extension fix 2021-01-20 14:48:54 -05:00
111 changed files with 1004 additions and 495 deletions

View File

@@ -17,21 +17,6 @@ jobs:
java-version: 14 java-version: 14
- name: Set env - name: Set env
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- name: Create artifacts
run: |
./gradlew desktop:dist server:dist core:javadoc -Pbuildversion=${RELEASE_VERSION:1}
- name: Update docs
run: |
cd ../
git config --global user.email "cli@github.com"
git config --global user.name "Github Actions"
git clone --depth=1 https://github.com/MindustryGame/docs.git
cp -a Mindustry/core/build/docs/javadoc/. docs/
cd docs
git add .
git commit -m "Update ${RELEASE_VERSION:1}"
git push https://Anuken:${{ secrets.API_TOKEN_GITHUB }}@github.com/MindustryGame/docs
cd ../Mindustry
- name: Add Arc release - name: Add Arc release
run: | run: |
git clone --depth=1 --branch=master https://github.com/Anuken/Arc ../Arc git clone --depth=1 --branch=master https://github.com/Anuken/Arc ../Arc
@@ -39,6 +24,24 @@ jobs:
git tag ${RELEASE_VERSION} git tag ${RELEASE_VERSION}
git push https://Anuken:${{ secrets.API_TOKEN_GITHUB }}@github.com/Anuken/Arc ${RELEASE_VERSION}; git push https://Anuken:${{ secrets.API_TOKEN_GITHUB }}@github.com/Anuken/Arc ${RELEASE_VERSION};
cd ../Mindustry cd ../Mindustry
- name: Create artifacts
run: |
./gradlew desktop:dist server:dist core:mergedJavadoc -Pbuildversion=${RELEASE_VERSION:1}
- name: Update docs
run: |
cd ../
git config --global user.email "cli@github.com"
git config --global user.name "Github Actions"
git clone --depth=1 https://github.com/MindustryGame/docs.git
cd docs
find . -maxdepth 1 ! -name ".git" ! -name . -exec rm -r {} \;
cd ../
cp -a Mindustry/core/build/javadoc/. docs/
cd docs
git add .
git commit -m "Update ${RELEASE_VERSION:1}"
git push https://Anuken:${{ secrets.API_TOKEN_GITHUB }}@github.com/MindustryGame/docs
cd ../Mindustry
- name: Update F-Droid build string - name: Update F-Droid build string
run: | run: |
git clone --depth=1 --branch=master https://github.com/Anuken/MindustryBuilds ../MindustryBuilds git clone --depth=1 --branch=master https://github.com/Anuken/MindustryBuilds ../MindustryBuilds

1
.gitignore vendored
View File

@@ -43,6 +43,7 @@ ios/robovm.properties
packr-out/ packr-out/
config/ config/
*.gif *.gif
/tests/out
/core/assets/basepartnames /core/assets/basepartnames
version.properties version.properties

View File

@@ -4,7 +4,6 @@ buildscript{
mavenCentral() mavenCentral()
google() google()
maven{ url "https://oss.sonatype.org/content/repositories/snapshots/" } maven{ url "https://oss.sonatype.org/content/repositories/snapshots/" }
jcenter()
} }
dependencies{ dependencies{
@@ -20,8 +19,8 @@ configurations{ natives }
repositories{ repositories{
mavenCentral() mavenCentral()
jcenter()
maven{ url "https://maven.google.com" } maven{ url "https://maven.google.com" }
jcenter() //remove later once google fixes the dependency
} }
dependencies{ dependencies{

View File

@@ -12,7 +12,6 @@ buildscript{
mavenCentral() mavenCentral()
google() google()
maven{ url "https://oss.sonatype.org/content/repositories/snapshots/" } maven{ url "https://oss.sonatype.org/content/repositories/snapshots/" }
jcenter()
maven{ url 'https://jitpack.io' } maven{ url 'https://jitpack.io' }
} }
@@ -184,7 +183,6 @@ allprojects{
maven{ url "https://oss.sonatype.org/content/repositories/snapshots/" } maven{ url "https://oss.sonatype.org/content/repositories/snapshots/" }
maven{ url "https://oss.sonatype.org/content/repositories/releases/" } maven{ url "https://oss.sonatype.org/content/repositories/releases/" }
maven{ url 'https://jitpack.io' } maven{ url 'https://jitpack.io' }
jcenter()
} }
task clearCache{ task clearCache{
@@ -320,6 +318,11 @@ project(":core"){
} }
} }
task sourcesJar(type: Jar, dependsOn: classes){
classifier = 'sources'
from sourceSets.main.allSource
}
dependencies{ dependencies{
compileJava.dependsOn(preGen) compileJava.dependsOn(preGen)
@@ -337,6 +340,28 @@ project(":core"){
annotationProcessor 'com.github.Anuken:jabel:34e4c172e65b3928cd9eabe1993654ea79c409cd' annotationProcessor 'com.github.Anuken:jabel:34e4c172e65b3928cd9eabe1993654ea79c409cd'
} }
afterEvaluate{
task mergedJavadoc(type: Javadoc){
def blacklist = [project(":ios"), project(":desktop"), project(":server"), project(":annotations")]
if(findProject(":android") != null){
blacklist += project(":android")
}
source rootProject.subprojects.collect{ project ->
if(!blacklist.contains(project) && project.hasProperty("sourceSets")){
return project.sourceSets.main.allJava
}
}
classpath = files(rootProject.subprojects.collect { project ->
if(!blacklist.contains(project) && project.hasProperty("sourceSets")){
return project.sourceSets.main.compileClasspath
}
})
destinationDir = new File(buildDir, 'javadoc')
}
}
} }
project(":server"){ project(":server"){

View File

@@ -41,10 +41,13 @@ be.ignore = Ignore
be.noupdates = No updates found. be.noupdates = No updates found.
be.check = Check for updates be.check = Check for updates
mod.featured.dialog.title = Mod Browser (WIP) mods.browser = Mod Browser
mods.browser.selected = Selected mod mods.browser.selected = Selected mod
mods.browser.add = Install mods.browser.add = Install
mods.github.open = View mods.browser.reinstall = Reinstall
mods.github.open = Repo
mods.browser.sortdate = Sort by recent
mods.browser.sortstars = Sort by stars
schematic = Schematic schematic = Schematic
schematic.add = Save Schematic... schematic.add = Save Schematic...
@@ -116,8 +119,10 @@ mods.none = [lightgray]No mods found!
mods.guide = Modding Guide mods.guide = Modding Guide
mods.report = Report Bug mods.report = Report Bug
mods.openfolder = Open Folder mods.openfolder = Open Folder
mods.viewcontent = View Content
mods.reload = Reload mods.reload = Reload
mods.reloadexit = The game will now exit, to reload mods. mods.reloadexit = The game will now exit, to reload mods.
mod.installed = [[Installed]
mod.display = [gray]Mod:[orange] {0} mod.display = [gray]Mod:[orange] {0}
mod.enabled = [lightgray]Enabled mod.enabled = [lightgray]Enabled
mod.disabled = [scarlet]Disabled mod.disabled = [scarlet]Disabled
@@ -307,6 +312,7 @@ cancelbuilding = [accent][[{0}][] to clear plan
selectschematic = [accent][[{0}][] to select+copy selectschematic = [accent][[{0}][] to select+copy
pausebuilding = [accent][[{0}][] to pause building pausebuilding = [accent][[{0}][] to pause building
resumebuilding = [scarlet][[{0}][] to resume building resumebuilding = [scarlet][[{0}][] to resume building
enablebuilding = [scarlet][[{0}][] to enable building
showui = UI hidden.\nPress [accent][[{0}][] to show UI. showui = UI hidden.\nPress [accent][[{0}][] to show UI.
wave = [accent]Wave {0} wave = [accent]Wave {0}
wave.cap = [accent]Wave {0}/{1} wave.cap = [accent]Wave {0}/{1}
@@ -678,6 +684,7 @@ stat.drillspeed = Base Drill Speed
stat.boosteffect = Boost Effect stat.boosteffect = Boost Effect
stat.maxunits = Max Active Units stat.maxunits = Max Active Units
stat.health = Health stat.health = Health
stat.armor = Armor
stat.buildtime = Build Time stat.buildtime = Build Time
stat.maxconsecutive = Max Consecutive stat.maxconsecutive = Max Consecutive
stat.buildcost = Build Cost stat.buildcost = Build Cost
@@ -693,6 +700,7 @@ stat.lightningchance = Lightning Chance
stat.lightningdamage = Lightning Damage stat.lightningdamage = Lightning Damage
stat.flammability = Flammability stat.flammability = Flammability
stat.radioactivity = Radioactivity stat.radioactivity = Radioactivity
stat.charge = Charge
stat.heatcapacity = Heat Capacity stat.heatcapacity = Heat Capacity
stat.viscosity = Viscosity stat.viscosity = Viscosity
stat.temperature = Temperature stat.temperature = Temperature

View File

@@ -41,10 +41,13 @@ be.ignore = Ignorieren
be.noupdates = Keine Aktualisierungen gefunden. be.noupdates = Keine Aktualisierungen gefunden.
be.check = Auf Aktualisierungen prüfen be.check = Auf Aktualisierungen prüfen
mod.featured.dialog.title = Mod Browser (unfertig) mod.featured.dialog.title = Mod Browser
mods.browser.selected = Ausgewählter Mod mods.browser.selected = Ausgewählter Mod
mods.browser.add = Installieren mods.browser.add = Installieren
mods.github.open = Ansehen mods.browser.reinstall = Neu Installieren
mods.github.open = Github
mods.browser.sortdate = Nach neusten sortieren
mods.browser.sortstars = Nach Sternen sortieren
schematic = Entwurf schematic = Entwurf
schematic.add = Entwurf speichern... schematic.add = Entwurf speichern...
@@ -116,8 +119,10 @@ mods.none = [lightgray]Keine Mods gefunden!
mods.guide = Modding-Anleitung mods.guide = Modding-Anleitung
mods.report = Problem melden mods.report = Problem melden
mods.openfolder = Mod-Verzeichnis öffnen mods.openfolder = Mod-Verzeichnis öffnen
mods.viewcontent = Inhalt ansehen
mods.reload = Neu laden mods.reload = Neu laden
mods.reloadexit = Das Spiel wird jetzt beendet, um die Mod-Änderungen anzuwenden. mods.reloadexit = Das Spiel wird jetzt beendet, um die Mod-Änderungen anzuwenden.
mod.installed = [[Installiert]
mod.display = [gray]Mod:[orange] {0} mod.display = [gray]Mod:[orange] {0}
mod.enabled = [lightgray]Aktiviert mod.enabled = [lightgray]Aktiviert
mod.disabled = [scarlet]Deaktiviert mod.disabled = [scarlet]Deaktiviert
@@ -287,6 +292,7 @@ cancel = Abbruch
openlink = Link öffnen openlink = Link öffnen
copylink = Link kopieren copylink = Link kopieren
back = Zurück back = Zurück
max = Max
crash.export = Crash-Logs exportieren crash.export = Crash-Logs exportieren
crash.none = Keine Crash-Logs gefunden. crash.none = Keine Crash-Logs gefunden.
crash.exported = Crash-Logs wurden erfolgreich exportiert. crash.exported = Crash-Logs wurden erfolgreich exportiert.
@@ -361,7 +367,6 @@ editor.center = Zur Mitte
workshop = Workshop workshop = Workshop
waves.title = Wellen waves.title = Wellen
waves.remove = Entfernen waves.remove = Entfernen
waves.never = <nie>
waves.every = alle waves.every = alle
waves.waves = Welle(n) waves.waves = Welle(n)
waves.perspawn = pro Spawn waves.perspawn = pro Spawn
@@ -390,7 +395,7 @@ editor.removeunit = Bereich entfernen
editor.teams = Teams editor.teams = Teams
editor.errorload = Fehler beim Laden der Datei:\n[accent]{0} editor.errorload = Fehler beim Laden der Datei:\n[accent]{0}
editor.errorsave = Fehler beim Speichern der Datei:\n[accent]{0} editor.errorsave = Fehler beim Speichern der Datei:\n[accent]{0}
editor.errorimage = Das ist ein Bild, keine Karte. Wechsle nicht den Dateityp und erwarte, dass es funktioniert.\n\nWenn du eine 'v3.5/build 40'-Karte importieren möchtest, benutze den 'Importiere Terrainbild'-Knopf im Editor. editor.errorimage = Das ist ein Bild, keine Karte.
editor.errorlegacy = Diese Karte ist zu alt und benutzt ein veraltetes Kartenformat, das nicht mehr unterstützt wird. editor.errorlegacy = Diese Karte ist zu alt und benutzt ein veraltetes Kartenformat, das nicht mehr unterstützt wird.
editor.errornot = Dies ist keine Kartendatei. editor.errornot = Dies ist keine Kartendatei.
editor.errorheader = Diese Karte ist entweder nicht gültig oder beschädigt. editor.errorheader = Diese Karte ist entweder nicht gültig oder beschädigt.
@@ -693,6 +698,7 @@ stat.lightningchance = Blitzwahr­schein­lich­keit
stat.lightningdamage = Blitzschaden stat.lightningdamage = Blitzschaden
stat.flammability = Brennbarkeit stat.flammability = Brennbarkeit
stat.radioactivity = Radioaktivität stat.radioactivity = Radioaktivität
stat.charge = Ladung
stat.heatcapacity = Hitzekapazität stat.heatcapacity = Hitzekapazität
stat.viscosity = Viskosität stat.viscosity = Viskosität
stat.temperature = Temperatur stat.temperature = Temperatur
@@ -720,6 +726,7 @@ bar.corereq = Kern-Basis erforderlich
bar.drillspeed = Bohrgeschwindigkeit: {0}/s bar.drillspeed = Bohrgeschwindigkeit: {0}/s
bar.pumpspeed = Pumpengeschwindigkeit: {0}/s bar.pumpspeed = Pumpengeschwindigkeit: {0}/s
bar.efficiency = Effizienz: {0}% bar.efficiency = Effizienz: {0}%
bar.boost = Beschleunigung: {0}%
bar.powerbalance = Strom: {0}/s bar.powerbalance = Strom: {0}/s
bar.powerstored = Gespeichert: {0}/{1} bar.powerstored = Gespeichert: {0}/{1}
bar.poweramount = Strom: {0} bar.poweramount = Strom: {0}
@@ -935,6 +942,7 @@ mode.custom = Angepasste Regeln
rules.infiniteresources = Unbegrenzte Ressourcen rules.infiniteresources = Unbegrenzte Ressourcen
rules.reactorexplosions = Reaktor-Explosionen rules.reactorexplosions = Reaktor-Explosionen
rules.coreincinerates = Kern verbrennt überflüssige Materialien
rules.schematic = Entwürfe erlaubt rules.schematic = Entwürfe erlaubt
rules.wavetimer = Wellen-Timer rules.wavetimer = Wellen-Timer
rules.waves = Wellen rules.waves = Wellen
@@ -1381,8 +1389,8 @@ block.inverted-sorter.description = Wie ein normaler Sortierer, aber gibt das au
block.router.description = Verteilt Materialien auf bis zu drei Richtungen. block.router.description = Verteilt Materialien auf bis zu drei Richtungen.
block.router.details = Ein nötiges Übel. Es ist nicht empfehlenswert, ihn neben Fabriken zu setzen, da er sich dort verstopfen kann. block.router.details = Ein nötiges Übel. Es ist nicht empfehlenswert, ihn neben Fabriken zu setzen, da er sich dort verstopfen kann.
block.distributor.description = Verteilt Materialien auf bis zu sieben Richtungen. block.distributor.description = Verteilt Materialien auf bis zu sieben Richtungen.
block.overflow-gate.description = Gibt Materialien nur zu den Seiten heraus, wenn der fordere Ausgang blockiert ist. Kann nicht neben anderen Überlauf- oder Unterlauftoren verwendet werden. block.overflow-gate.description = Gibt Materialien nur zu den Seiten heraus, wenn der fordere Ausgang blockiert ist.
block.underflow-gate.description = Das Gegenteil eines Überlauftors. Gibt Materialien nur nach vorne heraus, wenn die Seiten blockiert sind. Kann nicht neben anderen Überlauf- oder Unterlauftoren verwendet werden. block.underflow-gate.description = Das Gegenteil eines Überlauftors. Gibt Materialien nur nach vorne heraus, wenn die Seiten blockiert sind.
block.mass-driver.description = Ein Transportblock mit sehr hoher Reichweite. Sammelt mehrere Materialien und schießt sie zu einem verbundenen Massenbeschleuniger. block.mass-driver.description = Ein Transportblock mit sehr hoher Reichweite. Sammelt mehrere Materialien und schießt sie zu einem verbundenen Massenbeschleuniger.
block.mechanical-pump.description = Eine Pumpe, die keinen Strom benötigt. block.mechanical-pump.description = Eine Pumpe, die keinen Strom benötigt.
block.rotary-pump.description = Eine Pumpe, die Strom verbraucht. block.rotary-pump.description = Eine Pumpe, die Strom verbraucht.

View File

@@ -308,7 +308,7 @@ pausebuilding = Használd a(z) [accent][[{0}][] gombot, hogy megállítsd az ép
resumebuilding = Használd a(z) [scarlet][[{0}][] gombot, hogy folytasd az építkezést. resumebuilding = Használd a(z) [scarlet][[{0}][] gombot, hogy folytasd az építkezést.
showui = A kezelőfelület elrejtve.\nNyomja meg a(z) [accent][[{0}][] gombot a megjelenítéséhez. showui = A kezelőfelület elrejtve.\nNyomja meg a(z) [accent][[{0}][] gombot a megjelenítéséhez.
wave = [accent]{0}. Hullám wave = [accent]{0}. Hullám
wave.cap = [accent]{0}/{1}. Hullám wave.cap = [accent]{0}/{1} Hullám
wave.waiting = [lightgray]Következő hullám {0} wave.waiting = [lightgray]Következő hullám {0}
wave.waveInProgress = [lightgray]Hullám folyamatban wave.waveInProgress = [lightgray]Hullám folyamatban
waiting = [lightgray]Várakozás... waiting = [lightgray]Várakozás...
@@ -316,7 +316,7 @@ waiting.players = Várakozás a játékosokra...
wave.enemies = [lightgray]Fennmaradó ellenségek: {0} wave.enemies = [lightgray]Fennmaradó ellenségek: {0}
wave.enemycores = [accent]{0}[lightgray] Ellenséges magok wave.enemycores = [accent]{0}[lightgray] Ellenséges magok
wave.enemycore = [accent]{0}[lightgray] Ellenséges mag wave.enemycore = [accent]{0}[lightgray] Ellenséges mag
wave.enemy = [lightgray]{0} Magmaradt ellenség wave.enemy = [lightgray]{0} Megmaradt ellenség
wave.guardianwarn = Az őrző [accent]{0}[] hullám múlva érkezik. wave.guardianwarn = Az őrző [accent]{0}[] hullám múlva érkezik.
wave.guardianwarn.one = Az őrző [accent]{0}[] hullám múlva érkezik. wave.guardianwarn.one = Az őrző [accent]{0}[] hullám múlva érkezik.
loadimage = Fénykép betöltése loadimage = Fénykép betöltése

View File

@@ -41,11 +41,12 @@ be.ignore = Abaikan
be.noupdates = Tidak ada pembaruan yang ditemukan. be.noupdates = Tidak ada pembaruan yang ditemukan.
be.check = Cek versi baru be.check = Cek versi baru
mod.featured.title = Browser mod
mod.featured.dialog.title = Browser Mod mod.featured.dialog.title = Browser Mod
mods.browser.selected = Mod yang Dipilih mods.browser.selected = Mod yang Dipilih
mods.browser.add = Unduh Mod mods.browser.add = Unduh Mod
mods.github.open = Buka di GitHub mods.github.open = Buka di GitHub
mods.browser.sortdate = Urut berdasarkan waktu
mods.browser.sortstars = Urut berdasarkan bintang
schematic = Skema schematic = Skema
schematic.add = Menyimpan skema... schematic.add = Menyimpan skema...
@@ -90,6 +91,7 @@ joingame = Bermain Bersama
customgame = Permainan Modifikasi customgame = Permainan Modifikasi
newgame = Permainan Baru newgame = Permainan Baru
none = <kosong> none = <kosong>
none.found = [lightgray]<tidak ditemukan>
minimap = Peta Kecil minimap = Peta Kecil
position = Posisi position = Posisi
close = Tutup close = Tutup
@@ -1383,7 +1385,7 @@ block.inverted-sorter.description = Sama seperti penyortir, melainkan mengeluark
block.router.description = Menerima bahan dari satu arah dan mengeluarkannya ke 3 arah yang sama. Bisa juga menyimpan sejumlah bahan. Berguna untuk memisahkan bahan dari satu sumber ke target yang banyak. block.router.description = Menerima bahan dari satu arah dan mengeluarkannya ke 3 arah yang sama. Bisa juga menyimpan sejumlah bahan. Berguna untuk memisahkan bahan dari satu sumber ke target yang banyak.
block.router.details = Bisa sangat menggangu. Jangan meletakannya disamping input produksi, karena bisa tersumbat oleh output. block.router.details = Bisa sangat menggangu. Jangan meletakannya disamping input produksi, karena bisa tersumbat oleh output.
block.distributor.description = Pemisah canggih yang memisah item ke 7 arah berbeda bersamaan. block.distributor.description = Pemisah canggih yang memisah item ke 7 arah berbeda bersamaan.
block.overflow-gate.description = Kombinasi antara pemisah dan penyortir yang hanya mengeluarkan item ke kiri dan/atau ke kanan jika bagian depan tertutup. block.overflow-gate.description = Hanya mengeluarkan item ke kiri dan/atau ke kanan jika bagian depan tertutup.
block.underflow-gate.description = Kebalikan dari gerbang luap. Mengeluarkan ke depan jika kanan dan kiri tertutup. block.underflow-gate.description = Kebalikan dari gerbang luap. Mengeluarkan ke depan jika kanan dan kiri tertutup.
block.mass-driver.description = Blok item transportasi tercanggih. Membawa beberapa item dan menembaknya ke penggerak massal lainnya dari arah yang jauh. block.mass-driver.description = Blok item transportasi tercanggih. Membawa beberapa item dan menembaknya ke penggerak massal lainnya dari arah yang jauh.
block.mechanical-pump.description = Pompa murah dengan pengeluaran yang pelan, tetapi tidak mengkonsumsi tenaga. block.mechanical-pump.description = Pompa murah dengan pengeluaran yang pelan, tetapi tidak mengkonsumsi tenaga.

View File

@@ -45,6 +45,8 @@ mod.featured.dialog.title = 모드 탐색 (WIP)
mods.browser.selected = 선택된 모드 mods.browser.selected = 선택된 모드
mods.browser.add = 모드 설치 mods.browser.add = 모드 설치
mods.github.open = 깃허브 사이트 열기 mods.github.open = 깃허브 사이트 열기
mods.browser.sortdate = 최근 업데이트
mods.browser.sortstars = 추천(스타) 수
schematic = 설계도 schematic = 설계도
schematic.add = 설계도 저장하기 schematic.add = 설계도 저장하기

View File

@@ -11,7 +11,7 @@ link.trello.description = Oficjalna tablica Trello z planowanym funkcjami
link.itch.io.description = Strona itch.io z oficjanymi wersjami do pobrania link.itch.io.description = Strona itch.io z oficjanymi wersjami do pobrania
link.google-play.description = Strona w sklepie Google Play link.google-play.description = Strona w sklepie Google Play
link.f-droid.description = Wykaz Katalogu F-Droid link.f-droid.description = Wykaz Katalogu F-Droid
link.wiki.description = Oficjana Wiki Mindustry link.wiki.description = Oficjalna Wiki Mindustry
link.suggestions.description = Zaproponuj nowe funkcje link.suggestions.description = Zaproponuj nowe funkcje
link.bug.description = Znalazłeś błąd? Zgłoś go tutaj link.bug.description = Znalazłeś błąd? Zgłoś go tutaj
linkfail = Nie udało się otworzyć linku!\nURL został skopiowany. linkfail = Nie udało się otworzyć linku!\nURL został skopiowany.
@@ -1277,7 +1277,7 @@ hint.placeTurret = Postaw \uf861 [accent]Działka[] by bronić się przed wrogam
hint.breaking = Użyj [accent]Prawego przycisku myszy[] i przeciągnij by zniszczyć bloki. hint.breaking = Użyj [accent]Prawego przycisku myszy[] i przeciągnij by zniszczyć bloki.
hint.breaking.mobile = Aktywuj \ue817 [accent]ikonę młota[] w dolnym prawym rogu by zniszczyć bloki.\n\nPrzytrymaj swój palec i przeciągij by wybrać wiele bloków do zniszczenia. hint.breaking.mobile = Aktywuj \ue817 [accent]ikonę młota[] w dolnym prawym rogu by zniszczyć bloki.\n\nPrzytrymaj swój palec i przeciągij by wybrać wiele bloków do zniszczenia.
hint.research = Klikij przycisk \ue875 [accent]Badań[] by odkrwyać nowe technologie. hint.research = Klikij przycisk \ue875 [accent]Badań[] by odkrwyać nowe technologie.
hint.research.mobile = Użyj przycisku \ue875 [accent]Badań[] w \ue88c [accent]Menu[] by odkrwyać nowe technologie. hint.research.mobile = Użyj przycisku \ue875 [accent]Badań[] w \ue88c [accent]Menu[] by odkrywać nowe technologie.
hint.unitControl = Przytrzymaj [accent][[Lewy CTRL][] i [accent]kliknij[] by kontrolować sojusznicze jednostki i działka. hint.unitControl = Przytrzymaj [accent][[Lewy CTRL][] i [accent]kliknij[] by kontrolować sojusznicze jednostki i działka.
hint.unitControl.mobile = [accent][Kliknij dwukrotnie[] by kontrolować sojusznicze jednostki i działka. hint.unitControl.mobile = [accent][Kliknij dwukrotnie[] by kontrolować sojusznicze jednostki i działka.
hint.launch = Gdy zebrałeś wystarczająco materiałów możesz [accent]Wystrzelić[] wybierając \ue827 [accent]Mapę[] w dolnym prawym rogu. hint.launch = Gdy zebrałeś wystarczająco materiałów możesz [accent]Wystrzelić[] wybierając \ue827 [accent]Mapę[] w dolnym prawym rogu.
@@ -1290,10 +1290,10 @@ hint.command = Kliknij [accent][[G][] by ukształtować formacje z pobliskich je
hint.command.mobile = [accent][[Podwójne kliknięcie][] kształtuje formacje z pobliskich jednostek. hint.command.mobile = [accent][[Podwójne kliknięcie][] kształtuje formacje z pobliskich jednostek.
hint.payloadPickup = Kliknij [accent][[[] by podnieść małe bloki lub jednostki. hint.payloadPickup = Kliknij [accent][[[] by podnieść małe bloki lub jednostki.
hint.payloadPickup.mobile = [accent]Kliknij i przytrzymaj[] mały blok by go podnieść. hint.payloadPickup.mobile = [accent]Kliknij i przytrzymaj[] mały blok by go podnieść.
hint.payloadDrop = Kliknij [accent]][] by opuścić podniesoiny towar. hint.payloadDrop = Kliknij [accent]][] by opuścić podniesiony towar.
hint.payloadDrop.mobile = [accent]Kliknij i przytrzymaj[] w puste miejsce by opuścić podniesoiny towar. hint.payloadDrop.mobile = [accent]Kliknij i przytrzymaj[] w puste miejsce by opuścić podniesiony towar.
hint.waveFire = [accent]Strumień[] wypełniony wodą będzie gasić pobiskie pożary. hint.waveFire = [accent]Strumień[] wypełniony wodą będzie gasić pobiskie pożary.
hint.generator = \uf879 [accent]Generatory Spalinowe[] spalają węgiel i przekuzują moc do pobliskich bloków.\n\nMożesz powiększyć odległość transmitowanej mocy używająć \uf87f [accent]Węzły Prądu[]. hint.generator = \uf879 [accent]Generatory Spalinowe[] spalają węgiel i przekazują moc do pobliskich bloków.\n\nMożesz powiększyć odległość transmitowanej mocy używająć \uf87f [accent]Węzły Prądu[].
hint.guardian = Jednostki [accent]Strażnicze[] są uzbrojone. Słaba amunicja - taka jak [accent]Miedź[] oraz [accent]Ołów[] [scarlet]nie jest efektywna[].\n\nUżyj lepszych działek takich jak \uf835 [accent]Naładowane Grafitem[] \uf861Duo/\uf859Salwa by pozbyć się strażników. hint.guardian = Jednostki [accent]Strażnicze[] są uzbrojone. Słaba amunicja - taka jak [accent]Miedź[] oraz [accent]Ołów[] [scarlet]nie jest efektywna[].\n\nUżyj lepszych działek takich jak \uf835 [accent]Naładowane Grafitem[] \uf861Duo/\uf859Salwa by pozbyć się strażników.
hint.coreUpgrade = Rdzenie mogą być ulepszone poprzez [accent]płożenie na nich rdzeń wyższego poziomu[].\n\nPołóż  rdzeń [accent]Fundacji[] na  rdzeń:[accent]Odłamek[] core. Żadna przeszkoda ani blok nie może stać na miejscu rdzenia. hint.coreUpgrade = Rdzenie mogą być ulepszone poprzez [accent]płożenie na nich rdzeń wyższego poziomu[].\n\nPołóż  rdzeń [accent]Fundacji[] na  rdzeń:[accent]Odłamek[] core. Żadna przeszkoda ani blok nie może stać na miejscu rdzenia.
hint.presetLaunch = Szare [accent]sektory[], takie jak [accent]Zamrożony Las[], to sektory do których możesz dotrzeć z każdego miejsca. Nie wymagają podbicia pobliskiego terenu.\n\n[accent]Ponumerowane sektory[], takie jak ten, [accent]są dodatkowe[]. hint.presetLaunch = Szare [accent]sektory[], takie jak [accent]Zamrożony Las[], to sektory do których możesz dotrzeć z każdego miejsca. Nie wymagają podbicia pobliskiego terenu.\n\n[accent]Ponumerowane sektory[], takie jak ten, [accent]są dodatkowe[].
@@ -1357,8 +1357,8 @@ block.copper-wall.description = Tani blok obronny.\nPrzydatny do ochrony rdzenia
block.copper-wall-large.description = Tani blok obronny.\nPrzydatny do ochrony rdzenia i wieżyczek w pierwszych kilku falach.\nObejmuje wiele kratek. block.copper-wall-large.description = Tani blok obronny.\nPrzydatny do ochrony rdzenia i wieżyczek w pierwszych kilku falach.\nObejmuje wiele kratek.
block.titanium-wall.description = Umiarkowanie silny blok obronny.\nZapewnia umiarkowaną ochronę przed wrogami. block.titanium-wall.description = Umiarkowanie silny blok obronny.\nZapewnia umiarkowaną ochronę przed wrogami.
block.titanium-wall-large.description = Umiarkowanie silny blok obronny.\nZapewnia umiarkowaną ochronę przed wrogami.\nObejmuje wiele kratek. block.titanium-wall-large.description = Umiarkowanie silny blok obronny.\nZapewnia umiarkowaną ochronę przed wrogami.\nObejmuje wiele kratek.
block.plastanium-wall.description = Specjajny typ ściany, który pochłania łuki elektryczne oraz blokuje automatyczne łączenie węzłów. block.plastanium-wall.description = Specjalny typ ściany, który pochłania łuki elektryczne oraz blokuje automatyczne łączenie węzłów.
block.plastanium-wall-large.description = Specjajny typ ściany, który pochłania łuki elektryczne oraz blokuje automatyczne łączenie węzłów.\nObejmuje wiele kratek. block.plastanium-wall-large.description = Specjalny typ ściany, który pochłania łuki elektryczne oraz blokuje automatyczne łączenie węzłów.\nObejmuje wiele kratek.
block.thorium-wall.description = Silny blok obronny.\nDobra ochrona przed wrogami. block.thorium-wall.description = Silny blok obronny.\nDobra ochrona przed wrogami.
block.thorium-wall-large.description = Silny blok obronny.\nDobra ochrona przed wrogami.\nObejmuje wiele kratek. block.thorium-wall-large.description = Silny blok obronny.\nDobra ochrona przed wrogami.\nObejmuje wiele kratek.
block.phase-wall.description = Ściana pokryta specjalną mieszanką opartą o Włókna Fazowe, która odbija większość pocisków. block.phase-wall.description = Ściana pokryta specjalną mieszanką opartą o Włókna Fazowe, która odbija większość pocisków.

View File

@@ -41,11 +41,13 @@ be.ignore = Ignoră
be.noupdates = Niciun update disponibil. be.noupdates = Niciun update disponibil.
be.check = Verifică updateurile be.check = Verifică updateurile
mod.featured.title = Mod browser mods.browser = Browser de Moduri
mod.featured.dialog.title = Mod Browser (Neterminat)
mods.browser.selected = Mod selectat mods.browser.selected = Mod selectat
mods.browser.add = Instalează mods.browser.add = Instalează
mods.github.open = Vezi mods.browser.reinstall = Reinstalează
mods.github.open = Github
mods.browser.sortdate = Cele mai recente
mods.browser.sortstars = Cele mai multe stele
schematic = Schemă schematic = Schemă
schematic.add = Salvează Schema... schematic.add = Salvează Schema...
@@ -117,8 +119,10 @@ mods.none = [lightgray]Nu s-au găsit moduri!
mods.guide = Ghid de Modding mods.guide = Ghid de Modding
mods.report = Raportează Bug mods.report = Raportează Bug
mods.openfolder = Deschide Folder mods.openfolder = Deschide Folder
mods.viewcontent = Vezi Conținut
mods.reload = Reîncarcă mods.reload = Reîncarcă
mods.reloadexit = Jocul se va opri ca să reîncarce modurile. mods.reloadexit = Jocul se va opri ca să reîncarce modurile.
mod.installed = [[Instalat]
mod.display = [gray]Mod:[orange] {0} mod.display = [gray]Mod:[orange] {0}
mod.enabled = [lightgray]Activat mod.enabled = [lightgray]Activat
mod.disabled = [scarlet]Dezactivat mod.disabled = [scarlet]Dezactivat
@@ -697,6 +701,7 @@ stat.radioactivity = Radioactivitate
stat.heatcapacity = Capacitate de Căldură stat.heatcapacity = Capacitate de Căldură
stat.viscosity = Vâscozitate stat.viscosity = Vâscozitate
stat.temperature = Temperatură stat.temperature = Temperatură
stat.charge = Sarcină Electrică
stat.speed = Viteză stat.speed = Viteză
stat.buildspeed = Viteză Construcție stat.buildspeed = Viteză Construcție
stat.minespeed = Viteză Minare stat.minespeed = Viteză Minare
@@ -1384,8 +1389,8 @@ block.inverted-sorter.description = Similar sortatorului standard, dar materialu
block.router.description = Distribuie materialele primite în alte 3 direcții în mod egal. block.router.description = Distribuie materialele primite în alte 3 direcții în mod egal.
block.router.details = Un rău necesar. Nu folosi niciodată pt a introduce materiale în blocuri, căci vor fi blocate de produșii blocurilor. block.router.details = Un rău necesar. Nu folosi niciodată pt a introduce materiale în blocuri, căci vor fi blocate de produșii blocurilor.
block.distributor.description = Distribuie materialele primite în alte 7 direcții în mod egal. block.distributor.description = Distribuie materialele primite în alte 7 direcții în mod egal.
block.overflow-gate.description = Transportă materialele doar la stânga și dreapta dacă drumul din față este blocat. Nu poate fi folosită lângă alte porți. block.overflow-gate.description = Transportă materialele doar la stânga și dreapta dacă drumul din față este blocat.
block.underflow-gate.description = Opusul porții de revărsare. Transportă materialele în față dacă benzile din stânga și dreapta sunt blocate. Nu poate fi folosită lângă alte porți. block.underflow-gate.description = Opusul porții de revărsare. Transportă materialele în față dacă benzile din stânga și dreapta sunt blocate.
block.mass-driver.description = Dispozitiv folosit pt transportul materialelor pe distanțe mari. Adună mai multe materiale și apoi le lansează până la un alt distributor în masă pe o rază mare. block.mass-driver.description = Dispozitiv folosit pt transportul materialelor pe distanțe mari. Adună mai multe materiale și apoi le lansează până la un alt distributor în masă pe o rază mare.
block.mechanical-pump.description = Pompează lichide din mediul înconjurător. Nu necesită electricitate. block.mechanical-pump.description = Pompează lichide din mediul înconjurător. Nu necesită electricitate.
block.rotary-pump.description = Pompează lichide din mediul înconjurător. Necesită electricitate. block.rotary-pump.description = Pompează lichide din mediul înconjurător. Necesită electricitate.

View File

@@ -41,10 +41,13 @@ be.ignore = Игнорировать
be.noupdates = Обновления не найдены. be.noupdates = Обновления не найдены.
be.check = Проверить обновления be.check = Проверить обновления
mod.featured.dialog.title = Браузер модификаций mods.browser = Браузер\nмодификаций
mods.browser.selected = Выбранный мод mods.browser.selected = Выбранный мод
mods.browser.add = Скачать mods.browser.add = Скачать
mods.github.open = Открыть mods.browser.reinstall = Переустановить
mods.github.open = Сайт
mods.browser.sortdate = Сортировка по дате
mods.browser.sortstars = Сортировка по звёздам
schematic = Схема schematic = Схема
schematic.add = Сохранить схему… schematic.add = Сохранить схему…
@@ -116,8 +119,10 @@ mods.none = [lightgray]Модификации не найдены!
mods.guide = Руководство по модификациям mods.guide = Руководство по модификациям
mods.report = Доложить об ошибке mods.report = Доложить об ошибке
mods.openfolder = Открыть папку с модификациями mods.openfolder = Открыть папку с модификациями
mods.viewcontent = Просмотреть содержимое
mods.reload = Перезагрузить mods.reload = Перезагрузить
mods.reloadexit = Игра будет закрыта для перезагрузки модификаций. mods.reloadexit = Игра будет закрыта для перезагрузки модификаций.
mod.installed = [[Установлено]
mod.display = [gray]Модификация:[orange] {0} mod.display = [gray]Модификация:[orange] {0}
mod.enabled = [lightgray]Включён mod.enabled = [lightgray]Включён
mod.disabled = [scarlet]Выключен mod.disabled = [scarlet]Выключен
@@ -327,9 +332,9 @@ custom = Пользовательская
builtin = Встроенная builtin = Встроенная
map.delete.confirm = Вы действительно хотите удалить эту карту? Это действие не может быть отменено! map.delete.confirm = Вы действительно хотите удалить эту карту? Это действие не может быть отменено!
map.random = [accent]Случайная карта map.random = [accent]Случайная карта
map.nospawn = Эта карта не имеет ни одного ядра, в котором игрок может появиться! Добавьте[accent] оранжевое[] ядро на эту карту в редакторе. map.nospawn = Эта карта не имеет ни одного ядра, в котором игрок может появиться! Добавьте [accent]оранжевое[] ядро на эту карту в редакторе.
map.nospawn.pvp = У этой карты нет вражеских ядер, в которых игрок может появиться! Добавьте[scarlet] не оранжевое[] ядро на эту карту в редакторе. map.nospawn.pvp = У этой карты нет вражеских ядер, в которых игрок может появиться! Добавьте [scarlet]не оранжевое[] ядро на эту карту в редакторе.
map.nospawn.attack = У этой карты нет вражеских ядер для атаки игроком! Добавьте[scarlet] красное[] ядро на эту карту в редакторе. map.nospawn.attack = У этой карты нет вражеских ядер для атаки игроком! Добавьте [scarlet]красное[] ядро на эту карту в редакторе.
map.invalid = Ошибка загрузки карты: повреждённый или недопустимый файл карты. map.invalid = Ошибка загрузки карты: повреждённый или недопустимый файл карты.
workshop.update = Обновить содержимое workshop.update = Обновить содержимое
workshop.error = Ошибка загрузки информации из Мастерской: {0} workshop.error = Ошибка загрузки информации из Мастерской: {0}
@@ -693,6 +698,7 @@ stat.lightningchance = Шанс удара молнии
stat.lightningdamage = Урон молнии stat.lightningdamage = Урон молнии
stat.flammability = Воспламеняемость stat.flammability = Воспламеняемость
stat.radioactivity = Радиоактивность stat.radioactivity = Радиоактивность
stat.charge = Заряд
stat.heatcapacity = Теплоёмкость stat.heatcapacity = Теплоёмкость
stat.viscosity = Вязкость stat.viscosity = Вязкость
stat.temperature = Температура stat.temperature = Температура
@@ -1383,8 +1389,8 @@ block.inverted-sorter.description = Работает так же, как и ст
block.router.description = Равномерно распределяет входящие предметы по 3 выходящим направлениям. block.router.description = Равномерно распределяет входящие предметы по 3 выходящим направлениям.
block.router.details = Необходимое зло. Не рекомендуется к использованию как блок ввода возле производственных зданий, т.к. может случиться затор выходным материалом. block.router.details = Необходимое зло. Не рекомендуется к использованию как блок ввода возле производственных зданий, т.к. может случиться затор выходным материалом.
block.distributor.description = Равномерно распределяет входящие предметы по 7 выходящим направлениям. block.distributor.description = Равномерно распределяет входящие предметы по 7 выходящим направлениям.
block.overflow-gate.description = Выводит предметы по бокам, только если передний путь заблокирован. Нельзя использовать вплотную к другим затворам или шлюзам. block.overflow-gate.description = Выводит предметы по бокам, только если передний путь заблокирован.
block.underflow-gate.description = Противоположность избыточного затвора. Выводит предметы вперёд только в том случае, если боковые пути заблокированы. Нельзя использовать вплотную к другим шлюзам или затворам. block.underflow-gate.description = Противоположность избыточного затвора. Выводит предметы вперёд только в том случае, если боковые пути заблокированы.
block.mass-driver.description = Постройка для дальней транспортировки предметов. Собирает несколько предметов и затем стреляет ими в другие катапульты. block.mass-driver.description = Постройка для дальней транспортировки предметов. Собирает несколько предметов и затем стреляет ими в другие катапульты.
block.mechanical-pump.description = Перекачивает и выводит жидкости. Не требует энергию. block.mechanical-pump.description = Перекачивает и выводит жидкости. Не требует энергию.
block.rotary-pump.description = Перекачивает и выводит жидкости. Требует энергию. block.rotary-pump.description = Перекачивает и выводит жидкости. Требует энергию.
@@ -1497,10 +1503,10 @@ unit.poly.description = Автоматически восстанавливае
unit.mega.description = Автоматически ремонтирует повреждённые постройки. Может переносить блоки и небольшие единицы. unit.mega.description = Автоматически ремонтирует повреждённые постройки. Может переносить блоки и небольшие единицы.
unit.quad.description = Сбрасывает большие бомбы на наземные цели, восстанавливая союзные постройки и повреждая врагов. Может переносить единицы среднего размера. unit.quad.description = Сбрасывает большие бомбы на наземные цели, восстанавливая союзные постройки и повреждая врагов. Может переносить единицы среднего размера.
unit.oct.description = Защищает союзников поблизости при помощи своего восстанавливающегося щита. Может переносить большинство наземных единиц. unit.oct.description = Защищает союзников поблизости при помощи своего восстанавливающегося щита. Может переносить большинство наземных единиц.
unit.risso.description = Стреляет залпом ракет и пуль по всем врагам поблизости. unit.risso.description = Стреляет залпами ракет и пуль по всем врагам поблизости.
unit.minke.description = Стреляет зажигательными снарядами и стандартными пулями по наземным целям. unit.minke.description = Стреляет зажигательными снарядами и стандартными пулями по наземным целям.
unit.bryde.description = Стреляет дальнобойными артиллерийскими снарядами и ракетами по врагам. unit.bryde.description = Стреляет дальнобойными артиллерийскими снарядами и ракетами по врагам.
unit.sei.description = Стреляет залпом ракет и бронебойных пуль по врагам. unit.sei.description = Стреляет залпами ракет и бронебойных пуль по врагам.
unit.omura.description = Стреляет дальнобойным пробивающим снарядом из рельсотрона по врагам. Производит единицы «Вспышка». unit.omura.description = Стреляет дальнобойным пробивающим снарядом из рельсотрона по врагам. Производит единицы «Вспышка».
unit.alpha.description = Защищает ядро «Осколок» от врагов. Основная строительная единица. unit.alpha.description = Защищает ядро «Осколок» от врагов. Основная строительная единица.
unit.beta.description = Защищает ядро «Штаб» от врагов. Основная строительная единица. unit.beta.description = Защищает ядро «Штаб» от врагов. Основная строительная единица.

View File

@@ -59,8 +59,10 @@ schematic.disabled = [scarlet]Схеми вимкнені[]\nВам не доз
mod.featured.title = Переглядач модифікацій mod.featured.title = Переглядач модифікацій
mod.featured.dialog.title = Переглядач модифікацій mod.featured.dialog.title = Переглядач модифікацій
mods.browser.selected = Обрана модифікація mods.browser.selected = Обрана модифікація
mods.browser.add = Установити модифікацію mods.browser.add = Установити
mods.github.open = Відкрити в Github mods.github.open = Відкрити
mods.browser.sortdate = Сортувати за давністю
mods.browser.sortstars = Сортувати за кількостю зірок
stats = Статистика stats = Статистика
stat.wave = Хвиль відбито:[accent] {0} stat.wave = Хвиль відбито:[accent] {0}
@@ -85,6 +87,7 @@ joingame = Мережева гра
customgame = Користувацька гра customgame = Користувацька гра
newgame = Нова гра newgame = Нова гра
none = <нічого> none = <нічого>
none.found = [lightgray]<нічого не знайдено>
minimap = Мінімапа minimap = Мінімапа
position = Місцезнаходження position = Місцезнаходження
close = Закрити close = Закрити
@@ -278,6 +281,7 @@ cancel = Скасувати
openlink = Перейти за посиланням openlink = Перейти за посиланням
copylink = Скопіювати посилання copylink = Скопіювати посилання
back = Назад back = Назад
max = Макс.
crash.export = Експортувати аварійні звіти crash.export = Експортувати аварійні звіти
crash.none = Аварійних звітів не знайдено crash.none = Аварійних звітів не знайдено
crash.exported = Аварійні звіти експортовано crash.exported = Аварійні звіти експортовано
@@ -351,7 +355,6 @@ editor.center = Центрувати
workshop = Майстерня workshop = Майстерня
waves.title = Хвилі waves.title = Хвилі
waves.remove = Видалити waves.remove = Видалити
waves.never = <ніколи>
waves.every = кожен waves.every = кожен
waves.waves = хвиля(і) waves.waves = хвиля(і)
waves.perspawn = за появу waves.perspawn = за появу
@@ -377,7 +380,7 @@ editor.removeunit = Видалити бойову одиницю
editor.teams = Команди editor.teams = Команди
editor.errorload = Помилка завантаження зображення. editor.errorload = Помилка завантаження зображення.
editor.errorsave = Помилка збереження зображення. editor.errorsave = Помилка збереження зображення.
editor.errorimage = Це зображення, а не мапа.\n\nЯкщо ви хочете імпортувати застарілу мапу, то використовуйте кнопку «Імпортувати застарілу мапу» в редакторі. editor.errorimage = Це зображення, а не мапа.
editor.errorlegacy = Ця мапа занадто стара і використовує попередній формат мапи, який більше не підтримується. editor.errorlegacy = Ця мапа занадто стара і використовує попередній формат мапи, який більше не підтримується.
editor.errornot = Це не мапа. editor.errornot = Це не мапа.
editor.errorheader = Цей файл мапи недійсний або пошкоджений. editor.errorheader = Цей файл мапи недійсний або пошкоджений.
@@ -676,6 +679,7 @@ stat.commandlimit = Максимум у загоні
stat.abilities = Здібності stat.abilities = Здібності
stat.canboost = Можна прискорити stat.canboost = Можна прискорити
stat.flying = Літає stat.flying = Літає
stat.ammouse = Патронів використовує
ability.forcefield = Щитове поле ability.forcefield = Щитове поле
ability.repairfield = Ремонтувальне поле ability.repairfield = Ремонтувальне поле
ability.statusfield = Поле підсилення ability.statusfield = Поле підсилення
@@ -688,6 +692,7 @@ bar.corereq = Необхідне основне ядро
bar.drillspeed = Швидкість буріння: {0} за с. bar.drillspeed = Швидкість буріння: {0} за с.
bar.pumpspeed = Швидкість викачування: {0} за с. bar.pumpspeed = Швидкість викачування: {0} за с.
bar.efficiency = Ефективність: {0}% bar.efficiency = Ефективність: {0}%
bar.boost = Підсилення: {0}%
bar.powerbalance = Енергія: {0} за с. bar.powerbalance = Енергія: {0} за с.
bar.powerstored = Зберігає: {0}/{1} bar.powerstored = Зберігає: {0}/{1}
bar.poweramount = Енергія: {0} bar.poweramount = Енергія: {0}
@@ -696,7 +701,6 @@ bar.powerlines = З’єднань: {0}/{1}
bar.items = Предмети: {0} bar.items = Предмети: {0}
bar.capacity = Місткість: {0} bar.capacity = Місткість: {0}
bar.unitcap = {0} {1}/{2} bar.unitcap = {0} {1}/{2}
bar.limitreached = [scarlet] {0} / {1}[white] {2}\n[lightgray][[одиниця вимкнена]
bar.liquid = Рідина bar.liquid = Рідина
bar.heat = Нагрівання bar.heat = Нагрівання
bar.power = Енергія bar.power = Енергія
@@ -711,6 +715,7 @@ bullet.sapping = [stat]виснажує
bullet.homing = [stat]самонаведення bullet.homing = [stat]самонаведення
bullet.shock = [stat]шок bullet.shock = [stat]шок
bullet.frag = [stat]шкода по ділянці bullet.frag = [stat]шкода по ділянці
bullet.buildingdamage = [stat]{0}%[lightgray] шкода по будівлям
bullet.knockback = [stat]{0}[lightgray] відкидання bullet.knockback = [stat]{0}[lightgray] відкидання
bullet.pierce = [stat]{0}[lightgray]x пробиття bullet.pierce = [stat]{0}[lightgray]x пробиття
bullet.infinitepierce = [stat]пробиття bullet.infinitepierce = [stat]пробиття
@@ -738,6 +743,7 @@ unit.items = предм.
unit.thousands = тис unit.thousands = тис
unit.millions = млн unit.millions = млн
unit.billions = млрд unit.billions = млрд
unit.pershot = за постріл
category.purpose = Призначення category.purpose = Призначення
category.general = Загальне category.general = Загальне
category.power = Енергія category.power = Енергія
@@ -806,6 +812,7 @@ setting.lasersopacity.name = Непрозорість лазерів енерг
setting.bridgeopacity.name = Непрозорість мостів setting.bridgeopacity.name = Непрозорість мостів
setting.playerchat.name = Показувати хмару чата над гравцями setting.playerchat.name = Показувати хмару чата над гравцями
public.confirm = Ви хочете зробити цю гру загальнодоступною?\n[accent]Будь-хто може приєднатися до вашої гри.\n[lightgray]Це можна змінити в Налаштування->Гра->Загальнодоступність гри. public.confirm = Ви хочете зробити цю гру загальнодоступною?\n[accent]Будь-хто може приєднатися до вашої гри.\n[lightgray]Це можна змінити в Налаштування->Гра->Загальнодоступність гри.
public.confirm.really = Якщо ви хочете грати з друзями, використовуйте [green]Запросити друзів[] замість [scarlet]Публічного серверу[]!\nВи справді хочете зробити свою гру [scarlet]публічною[]?
public.beta = Зауважте, що в бета-версії гри ви не можете робити публічні ігри. public.beta = Зауважте, що в бета-версії гри ви не можете робити публічні ігри.
uiscale.reset = Масштаб користувацького інтерфейсу було змінено.\nНатисніть «Гаразд» для підтвердження цього масштабу.\n[scarlet]Повернення налаштувань і вихід через[accent] {0}[] секунд… uiscale.reset = Масштаб користувацького інтерфейсу було змінено.\nНатисніть «Гаразд» для підтвердження цього масштабу.\n[scarlet]Повернення налаштувань і вихід через[accent] {0}[] секунд…
uiscale.cancel = Скасувати і вийти uiscale.cancel = Скасувати і вийти
@@ -880,6 +887,7 @@ keybind.toggle_menus.name = Меню перемикання
keybind.chat_history_prev.name = Попередня історія чату keybind.chat_history_prev.name = Попередня історія чату
keybind.chat_history_next.name = Наступна історія чату keybind.chat_history_next.name = Наступна історія чату
keybind.chat_scroll.name = Прокрутка чату keybind.chat_scroll.name = Прокрутка чату
keybind.chat_mode.name = Змінити режим чату
keybind.drop_unit.name = Скинути бойову одиницю keybind.drop_unit.name = Скинути бойову одиницю
keybind.zoom_minimap.name = Збільшити мінімапу keybind.zoom_minimap.name = Збільшити мінімапу
mode.help.title = Опис режимів гри mode.help.title = Опис режимів гри
@@ -895,6 +903,7 @@ mode.attack.description = Зруйнуйте ворожу базу. \n[gray]По
mode.custom = Користувацькі правила mode.custom = Користувацькі правила
rules.infiniteresources = Нескінченні ресурси rules.infiniteresources = Нескінченні ресурси
rules.reactorexplosions = Вибухи реактора rules.reactorexplosions = Вибухи реактора
rules.coreincinerates = Ядро спалює надлишкові предмети
rules.schematic = Використання схем дозволено rules.schematic = Використання схем дозволено
rules.wavetimer = Таймер для хвиль rules.wavetimer = Таймер для хвиль
rules.waves = Хвилі rules.waves = Хвилі

View File

@@ -45,6 +45,8 @@ mod.featured.dialog.title = 模組瀏覽器 (尚未完成)
mods.browser.selected = 已選模組 mods.browser.selected = 已選模組
mods.browser.add = 安裝 mods.browser.add = 安裝
mods.github.open = 查看 mods.github.open = 查看
mods.browser.sortdate = 以最近篩選
mods.browser.sortstars = 以星數篩選
schematic = 藍圖 schematic = 藍圖
schematic.add = 儲存藍圖…… schematic.add = 儲存藍圖……
@@ -693,6 +695,7 @@ stat.lightningchance = 燃燒機率
stat.lightningdamage = 燃燒傷害 stat.lightningdamage = 燃燒傷害
stat.flammability = 易燃性 stat.flammability = 易燃性
stat.radioactivity = 輻射性 stat.radioactivity = 輻射性
stat.charge = 蓄電力
stat.heatcapacity = 熱容量 stat.heatcapacity = 熱容量
stat.viscosity = 黏度 stat.viscosity = 黏度
stat.temperature = 溫度 stat.temperature = 溫度

Binary file not shown.

View File

@@ -50,8 +50,8 @@ public class Vars implements Loadable{
public static final Charset charset = Charset.forName("UTF-8"); public static final Charset charset = Charset.forName("UTF-8");
/** main application name, capitalized */ /** main application name, capitalized */
public static final String appName = "Mindustry"; public static final String appName = "Mindustry";
/** URL for itch.io donations. */ /** Github API URL. */
public static final String donationURL = "https://anuke.itch.io/mindustry/purchase"; public static final String ghApi = "https://api.github.com";
/** URL for discord invite. */ /** URL for discord invite. */
public static final String discordURL = "https://discord.gg/mindustry"; public static final String discordURL = "https://discord.gg/mindustry";
/** URL for sending crash reports to */ /** URL for sending crash reports to */
@@ -345,7 +345,7 @@ public class Vars implements Loadable{
} }
public static void loadSettings(){ public static void loadSettings(){
settings.setJson(JsonIO.json()); settings.setJson(JsonIO.json);
settings.setAppName(appName); settings.setAppName(appName);
if(steam || (Version.modifier != null && Version.modifier.contains("steam"))){ if(steam || (Version.modifier != null && Version.modifier.contains("steam"))){

View File

@@ -23,7 +23,6 @@ public class Pathfinder implements Runnable{
private static final int updateFPS = 60; private static final int updateFPS = 60;
private static final int updateInterval = 1000 / updateFPS; private static final int updateInterval = 1000 / updateFPS;
private static final int impassable = -1; private static final int impassable = -1;
private static final int fieldTimeout = 1000 * 60 * 2;
public static final int public static final int
fieldCore = 0, fieldCore = 0,
@@ -192,31 +191,6 @@ public class Pathfinder implements Runnable{
//total update time no longer than maxUpdate //total update time no longer than maxUpdate
for(Flowfield data : threadList){ for(Flowfield data : threadList){
updateFrontier(data, maxUpdate / threadList.size); updateFrontier(data, maxUpdate / threadList.size);
//TODO implement timeouts... or don't
/*
//remove flowfields that have 'timed out' so they can be garbage collected and no longer waste space
if(data.refreshRate > 0 && Time.timeSinceMillis(data.lastUpdateTime) > fieldTimeout){
//make sure it doesn't get removed twice
data.lastUpdateTime = Time.millis();
Team team = data.team;
Core.app.post(() -> {
//remove its used state
if(fieldMap[team.id] != null){
fieldMap[team.id].remove(data.target);
fieldMapUsed[team.id].remove(data.target);
}
//remove from main thread list
mainList.remove(data);
});
queue.post(() -> {
//remove from this thread list with a delay
threadList.remove(data);
});
}*/
} }
} }

View File

@@ -73,7 +73,7 @@ public class WaveSpawner{
for(int i = 0; i < spawned; i++){ for(int i = 0; i < spawned; i++){
Unit unit = group.createUnit(state.rules.waveTeam, state.wave - 1); Unit unit = group.createUnit(state.rules.waveTeam, state.wave - 1);
unit.set(spawnX + Mathf.range(spread), spawnY + Mathf.range(spread)); unit.set(spawnX + Mathf.range(spread), spawnY + Mathf.range(spread));
unit.add(); spawnEffect(unit);
} }
}); });
}else{ }else{
@@ -92,7 +92,7 @@ public class WaveSpawner{
} }
} }
Time.runTask(121f, () -> spawning = false); Time.run(121f, () -> spawning = false);
} }
public void doShockwave(float x, float y){ public void doShockwave(float x, float y){
@@ -148,8 +148,7 @@ public class WaveSpawner{
private void eachFlyerSpawn(Floatc2 cons){ private void eachFlyerSpawn(Floatc2 cons){
for(Tile tile : spawns){ for(Tile tile : spawns){
float angle = Angles.angle(world.width() / 2, world.height() / 2, tile.x, tile.y); float angle = Angles.angle(world.width() / 2f, world.height() / 2f, tile.x, tile.y);
float trns = Math.max(world.width(), world.height()) * Mathf.sqrt2 * tilesize; float trns = Math.max(world.width(), world.height()) * Mathf.sqrt2 * tilesize;
float spawnX = Mathf.clamp(world.width() * tilesize / 2f + Angles.trnsx(angle, trns), -margin, world.width() * tilesize + margin); float spawnX = Mathf.clamp(world.width() * tilesize / 2f + Angles.trnsx(angle, trns), -margin, world.width() * tilesize + margin);
float spawnY = Mathf.clamp(world.height() * tilesize / 2f + Angles.trnsy(angle, trns), -margin, world.height() * tilesize + margin); float spawnY = Mathf.clamp(world.height() * tilesize / 2f + Angles.trnsy(angle, trns), -margin, world.height() * tilesize + margin);
@@ -168,6 +167,7 @@ public class WaveSpawner{
} }
private void reset(){ private void reset(){
spawning = false;
spawns.clear(); spawns.clear();
for(Tile tile : world.tiles){ for(Tile tile : world.tiles){
@@ -178,8 +178,11 @@ public class WaveSpawner{
} }
private void spawnEffect(Unit unit){ private void spawnEffect(Unit unit){
Call.spawnEffect(unit.x, unit.y, unit.type); unit.rotation = unit.angleTo(world.width()/2f * tilesize, world.height()/2f * tilesize);
Time.run(30f, unit::add); unit.apply(StatusEffects.unmoving, 30f);
unit.add();
Call.spawnEffect(unit.x, unit.y, unit.rotation, unit.type);
} }
private interface SpawnConsumer{ private interface SpawnConsumer{
@@ -187,8 +190,8 @@ public class WaveSpawner{
} }
@Remote(called = Loc.server, unreliable = true) @Remote(called = Loc.server, unreliable = true)
public static void spawnEffect(float x, float y, UnitType type){ public static void spawnEffect(float x, float y, float rotation, UnitType u){
Fx.unitSpawn.at(x, y, 0f, type); Fx.unitSpawn.at(x, y, rotation, u);
Time.run(30f, () -> Fx.spawn.at(x, y)); Time.run(30f, () -> Fx.spawn.at(x, y));
} }

View File

@@ -37,8 +37,9 @@ public class PhysicsProcess implements AsyncProcess{
//find Unit without bodies and assign them //find Unit without bodies and assign them
for(Unit entity : group){ for(Unit entity : group){
if(entity.type == null) continue;
if(entity.physref() == null){ if(entity.physref == null){
PhysicsBody body = new PhysicsBody(); PhysicsBody body = new PhysicsBody();
body.x = entity.x(); body.x = entity.x();
body.y = entity.y(); body.y = entity.y();
@@ -48,13 +49,13 @@ public class PhysicsProcess implements AsyncProcess{
PhysicRef ref = new PhysicRef(entity, body); PhysicRef ref = new PhysicRef(entity, body);
refs.add(ref); refs.add(ref);
entity.physref(ref); entity.physref = ref;
physics.add(body); physics.add(body);
} }
//save last position //save last position
PhysicRef ref = entity.physref(); PhysicRef ref = entity.physref;
ref.body.layer = ref.body.layer =
entity.type.allowLegStep ? layerLegs : entity.type.allowLegStep ? layerLegs :

View File

@@ -788,6 +788,7 @@ public class Blocks implements ContentList{
health = 130 * wallHealthMultiplier; health = 130 * wallHealthMultiplier;
insulated = true; insulated = true;
absorbLasers = true; absorbLasers = true;
schematicPriority = 10;
}}; }};
plastaniumWallLarge = new Wall("plastanium-wall-large"){{ plastaniumWallLarge = new Wall("plastanium-wall-large"){{
@@ -796,6 +797,7 @@ public class Blocks implements ContentList{
size = 2; size = 2;
insulated = true; insulated = true;
absorbLasers = true; absorbLasers = true;
schematicPriority = 10;
}}; }};
thoriumWall = new Wall("thorium-wall"){{ thoriumWall = new Wall("thorium-wall"){{
@@ -1398,6 +1400,7 @@ public class Blocks implements ContentList{
size = 3; size = 3;
itemCapacity = 1000; itemCapacity = 1000;
flags = EnumSet.of(BlockFlag.storage); flags = EnumSet.of(BlockFlag.storage);
group = BlockGroup.transportation;
}}; }};
container = new StorageBlock("container"){{ container = new StorageBlock("container"){{
@@ -1405,6 +1408,7 @@ public class Blocks implements ContentList{
size = 2; size = 2;
itemCapacity = 300; itemCapacity = 300;
flags = EnumSet.of(BlockFlag.storage); flags = EnumSet.of(BlockFlag.storage);
group = BlockGroup.transportation;
}}; }};
unloader = new Unloader("unloader"){{ unloader = new Unloader("unloader"){{
@@ -1661,6 +1665,7 @@ public class Blocks implements ContentList{
shootEffect = Fx.shootLiquid; shootEffect = Fx.shootLiquid;
range = 190f; range = 190f;
health = 250 * size * size; health = 250 * size * size;
flags = EnumSet.of(BlockFlag.turret, BlockFlag.extinguisher);
}}; }};
fuse = new ItemTurret("fuse"){{ fuse = new ItemTurret("fuse"){{

View File

@@ -26,17 +26,23 @@ public class Fx{
none = new Effect(0, 0f, e -> {}), none = new Effect(0, 0f, e -> {}),
unitSpawn = new Effect(30f, e -> { unitSpawn = new Effect(30f, e -> {
if(!(e.data instanceof UnitType)) return; if(!(e.data instanceof UnitType unit)) return;
alpha(e.fin());
float scl = 1f + e.fout() * 2f; float scl = 1f + e.fout() * 2f;
UnitType unit = e.data();
TextureRegion region = unit.icon(Cicon.full); TextureRegion region = unit.icon(Cicon.full);
alpha(e.fout());
mixcol(Color.white, e.fin());
rect(region, e.x, e.y, 180f);
reset();
alpha(e.fin());
rect(region, e.x, e.y, rect(region, e.x, e.y,
region.width * Draw.scl * scl, region.height * Draw.scl * scl, 180f); region.width * Draw.scl * scl, region.height * Draw.scl * scl, e.rotation - 90);
}), }),
@@ -718,6 +724,19 @@ public class Fx{
Lines.circle(e.x, e.y, 5f * e.fout()); Lines.circle(e.x, e.y, 5f * e.fout());
}), }),
forceShrink = new Effect(20, e -> {
color(e.color, e.fout());
if(renderer.animateShields){
Fill.poly(e.x, e.y, 6, e.rotation * e.fout());
}else{
stroke(1.5f);
Draw.alpha(0.09f);
Fill.poly(e.x, e.y, 6, e.rotation * e.fout());
Draw.alpha(1f);
Lines.poly(e.x, e.y, 6, e.rotation * e.fout());
}
}).layer(Layer.shields),
flakExplosionBig = new Effect(30, e -> { flakExplosionBig = new Effect(30, e -> {
color(Pal.bulletYellowBack); color(Pal.bulletYellowBack);

View File

@@ -74,6 +74,7 @@ public class Items implements ContentList{
surgeAlloy = new Item("surge-alloy", Color.valueOf("f3e979")){{ surgeAlloy = new Item("surge-alloy", Color.valueOf("f3e979")){{
cost = 1.2f; cost = 1.2f;
charge = 0.75f;
}}; }};
sporePod = new Item("spore-pod", Color.valueOf("7457ce")){{ sporePod = new Item("spore-pod", Color.valueOf("7457ce")){{

View File

@@ -21,7 +21,7 @@ public class StatusEffects implements ContentList{
burning = new StatusEffect("burning"){{ burning = new StatusEffect("burning"){{
color = Pal.lightFlame; color = Pal.lightFlame;
damage = 0.12f; //over 8 seconds, this would be 60 damage damage = 0.12f; //over 8 seconds, this would be ~60 damage
effect = Fx.burning; effect = Fx.burning;
init(() -> { init(() -> {
@@ -29,7 +29,7 @@ public class StatusEffects implements ContentList{
trans(tarred, ((unit, time, newTime, result) -> { trans(tarred, ((unit, time, newTime, result) -> {
unit.damagePierce(8f); unit.damagePierce(8f);
Fx.burning.at(unit.x + Mathf.range(unit.bounds() / 2f), unit.y + Mathf.range(unit.bounds() / 2f)); Fx.burning.at(unit.x + Mathf.range(unit.bounds() / 2f), unit.y + Mathf.range(unit.bounds() / 2f));
result.set(this, Math.min(time + newTime, 300f)); result.set(burning, Math.min(time + newTime, 300f));
})); }));
}); });
}}; }};
@@ -45,7 +45,7 @@ public class StatusEffects implements ContentList{
trans(blasted, ((unit, time, newTime, result) -> { trans(blasted, ((unit, time, newTime, result) -> {
unit.damagePierce(18f); unit.damagePierce(18f);
result.set(this, time); result.set(freezing, time);
})); }));
}); });
}}; }};
@@ -72,7 +72,7 @@ public class StatusEffects implements ContentList{
if(unit.team == state.rules.waveTeam){ if(unit.team == state.rules.waveTeam){
Events.fire(Trigger.shock); Events.fire(Trigger.shock);
} }
result.set(this, time); result.set(wet, time);
})); }));
opposite(burning); opposite(burning);
}); });
@@ -97,7 +97,7 @@ public class StatusEffects implements ContentList{
trans(tarred, ((unit, time, newTime, result) -> { trans(tarred, ((unit, time, newTime, result) -> {
unit.damagePierce(8f); unit.damagePierce(8f);
Fx.burning.at(unit.x + Mathf.range(unit.bounds() / 2f), unit.y + Mathf.range(unit.bounds() / 2f)); Fx.burning.at(unit.x + Mathf.range(unit.bounds() / 2f), unit.y + Mathf.range(unit.bounds() / 2f));
result.set(this, Math.min(time + newTime, 200f)); result.set(melting, Math.min(time + newTime, 200f));
})); }));
}); });
}}; }};
@@ -161,10 +161,12 @@ public class StatusEffects implements ContentList{
shocked = new StatusEffect("shocked"){{ shocked = new StatusEffect("shocked"){{
color = Pal.lancerLaser; color = Pal.lancerLaser;
reactive = true;
}}; }};
blasted = new StatusEffect("blasted"){{ blasted = new StatusEffect("blasted"){{
color = Color.valueOf("ff795e"); color = Color.valueOf("ff795e");
reactive = true;
}}; }};
corroded = new StatusEffect("corroded"){{ corroded = new StatusEffect("corroded"){{

View File

@@ -117,7 +117,7 @@ public class UnitTypes implements ContentList{
hitSize = 13f; hitSize = 13f;
rotateSpeed = 3f; rotateSpeed = 3f;
targetAir = false; targetAir = false;
health = 790; health = 800;
armor = 9f; armor = 9f;
mechFrontSway = 0.55f; mechFrontSway = 0.55f;
@@ -137,8 +137,8 @@ public class UnitTypes implements ContentList{
width = height = 14f; width = height = 14f;
collides = true; collides = true;
collidesTiles = true; collidesTiles = true;
splashDamageRadius = 24f; splashDamageRadius = 28f;
splashDamage = 45f; splashDamage = 54f;
backColor = Pal.bulletYellowBack; backColor = Pal.bulletYellowBack;
frontColor = Pal.bulletYellow; frontColor = Pal.bulletYellow;
}}; }};
@@ -150,7 +150,7 @@ public class UnitTypes implements ContentList{
hitSize = 20f; hitSize = 20f;
rotateSpeed = 2.1f; rotateSpeed = 2.1f;
health = 9000; health = 9000;
armor = 11f; armor = 10f;
canDrown = false; canDrown = false;
mechFrontSway = 1f; mechFrontSway = 1f;
@@ -173,7 +173,7 @@ public class UnitTypes implements ContentList{
inaccuracy = 3f; inaccuracy = 3f;
shotDelay = 4f; shotDelay = 4f;
bullet = new BasicBulletType(7f, 50){{ bullet = new BasicBulletType(7f, 45){{
width = 11f; width = 11f;
height = 20f; height = 20f;
lifetime = 25f; lifetime = 25f;
@@ -182,7 +182,7 @@ public class UnitTypes implements ContentList{
lightningLength = 6; lightningLength = 6;
lightningColor = Pal.surge; lightningColor = Pal.surge;
//standard bullet damage is far too much for lightning //standard bullet damage is far too much for lightning
lightningDamage = 20; lightningDamage = 19;
}}; }};
}}, }},
@@ -230,7 +230,7 @@ public class UnitTypes implements ContentList{
ejectEffect = Fx.casing4; ejectEffect = Fx.casing4;
shootSound = Sounds.bang; shootSound = Sounds.bang;
bullet = new BasicBulletType(13f, 60){{ bullet = new BasicBulletType(13f, 65){{
pierce = true; pierce = true;
pierceCap = 10; pierceCap = 10;
width = 14f; width = 14f;
@@ -247,7 +247,7 @@ public class UnitTypes implements ContentList{
fragLifeMin = 0f; fragLifeMin = 0f;
fragCone = 30f; fragCone = 30f;
fragBullet = new BasicBulletType(9f, 15){{ fragBullet = new BasicBulletType(9f, 18){{
width = 10f; width = 10f;
height = 10f; height = 10f;
pierce = true; pierce = true;
@@ -418,8 +418,8 @@ public class UnitTypes implements ContentList{
engineSize = 6f; engineSize = 6f;
lowAltitude = true; lowAltitude = true;
health = 7000f; health = 7200f;
armor = 7f; armor = 8f;
canBoost = true; canBoost = true;
landShake = 4f; landShake = 4f;
immunities = ObjectSet.with(StatusEffects.burning); immunities = ObjectSet.with(StatusEffects.burning);
@@ -435,7 +435,7 @@ public class UnitTypes implements ContentList{
firstShotDelay = Fx.greenLaserChargeSmall.lifetime - 1f; firstShotDelay = Fx.greenLaserChargeSmall.lifetime - 1f;
reload = 160f; reload = 155f;
recoil = 0f; recoil = 0f;
chargeSound = Sounds.lasercharge2; chargeSound = Sounds.lasercharge2;
shootSound = Sounds.beam; shootSound = Sounds.beam;
@@ -443,8 +443,8 @@ public class UnitTypes implements ContentList{
cooldownTime = 200f; cooldownTime = 200f;
bullet = new ContinuousLaserBulletType(){{ bullet = new ContinuousLaserBulletType(){{
damage = 23f; damage = 26f;
length = 160f; length = 170f;
hitEffect = Fx.hitMeltHeal; hitEffect = Fx.hitMeltHeal;
drawSize = 420f; drawSize = 420f;
lifetime = 160f; lifetime = 160f;
@@ -454,7 +454,7 @@ public class UnitTypes implements ContentList{
shootEffect = Fx.greenLaserChargeSmall; shootEffect = Fx.greenLaserChargeSmall;
incendChance = 0.075f; incendChance = 0.08f;
incendSpread = 5f; incendSpread = 5f;
incendAmount = 1; incendAmount = 1;
@@ -1590,7 +1590,7 @@ public class UnitTypes implements ContentList{
}}; }};
sei = new UnitType("sei"){{ sei = new UnitType("sei"){{
health = 10000; health = 10500;
armor = 12f; armor = 12f;
speed = 0.73f; speed = 0.73f;

View File

@@ -200,7 +200,7 @@ public class Logic implements ApplicationListener{
} }
public void skipWave(){ public void skipWave(){
state.wavetime = 0; runWave();
} }
public void runWave(){ public void runWave(){

View File

@@ -396,7 +396,6 @@ public class NetClient implements ApplicationListener{
netClient.byteStream.setBytes(net.decompressSnapshot(data, dataLen)); netClient.byteStream.setBytes(net.decompressSnapshot(data, dataLen));
DataInputStream input = netClient.dataStream; DataInputStream input = netClient.dataStream;
//go through each entity
for(int j = 0; j < amount; j++){ for(int j = 0; j < amount; j++){
int id = input.readInt(); int id = input.readInt();
byte typeID = input.readByte(); byte typeID = input.readByte();
@@ -445,11 +444,16 @@ public class NetClient implements ApplicationListener{
for(int i = 0; i < amount; i++){ for(int i = 0; i < amount; i++){
int pos = input.readInt(); int pos = input.readInt();
short block = input.readShort();
Tile tile = world.tile(pos); Tile tile = world.tile(pos);
if(tile == null || tile.build == null){ if(tile == null || tile.build == null){
Log.warn("Missing entity at @. Skipping block snapshot.", tile); Log.warn("Missing entity at @. Skipping block snapshot.", tile);
break; break;
} }
if(tile.build.block.id != block){
Log.warn("Block ID mismatch at @: @ != @. Skipping block snapshot.", tile, tile.build.block.id, block);
break;
}
tile.build.readAll(Reads.get(input), tile.build.version()); tile.build.readAll(Reads.get(input), tile.build.version());
} }
}catch(Exception e){ }catch(Exception e){

View File

@@ -155,7 +155,7 @@ public class NetServer implements ApplicationListener{
if(!extraMods.isEmpty()){ if(!extraMods.isEmpty()){
result.append("Unnecessary mods:[lightgray]\n").append("> ").append(extraMods.toString("\n> ")); result.append("Unnecessary mods:[lightgray]\n").append("> ").append(extraMods.toString("\n> "));
} }
con.kick(result.toString()); con.kick(result.toString(), 0);
} }
if(!admins.isWhitelisted(packet.uuid, packet.usid)){ if(!admins.isWhitelisted(packet.uuid, packet.usid)){
@@ -182,7 +182,7 @@ public class NetServer implements ApplicationListener{
return; return;
} }
if(Groups.player.contains(player -> player.uuid().equals(packet.uuid) || player.usid().equals(packet.usid) || player.ip().equals(con.address))){ if(Groups.player.contains(player -> player.uuid().equals(packet.uuid) || player.usid().equals(packet.usid))){
con.kick(KickReason.idInUse); con.kick(KickReason.idInUse);
return; return;
} }
@@ -824,6 +824,7 @@ public class NetServer implements ApplicationListener{
sent ++; sent ++;
dataStream.writeInt(entity.pos()); dataStream.writeInt(entity.pos());
dataStream.writeShort(entity.block.id);
entity.writeAll(Writes.get(dataStream)); entity.writeAll(Writes.get(dataStream));
if(syncStream.size() > maxSnapshotSize){ if(syncStream.size() > maxSnapshotSize){
@@ -844,13 +845,15 @@ public class NetServer implements ApplicationListener{
public void writeEntitySnapshot(Player player) throws IOException{ public void writeEntitySnapshot(Player player) throws IOException{
syncStream.reset(); syncStream.reset();
Seq<CoreBuild> cores = state.teams.cores(player.team()); int sum = state.teams.present.sum(t -> t.cores.size);
dataStream.writeByte(cores.size); dataStream.writeByte(sum);
for(CoreBuild entity : cores){ for(TeamData data : state.teams.present){
dataStream.writeInt(entity.tile.pos()); for(CoreBuild entity : data.cores){
entity.items.write(Writes.get(dataStream)); dataStream.writeInt(entity.tile.pos());
entity.items.write(Writes.get(dataStream));
}
} }
dataStream.close(); dataStream.close();

View File

@@ -2,7 +2,6 @@ package mindustry.core;
import arc.*; import arc.*;
import arc.files.*; import arc.files.*;
import arc.fx.*;
import arc.graphics.*; import arc.graphics.*;
import arc.graphics.g2d.*; import arc.graphics.g2d.*;
import arc.graphics.gl.*; import arc.graphics.gl.*;
@@ -38,13 +37,8 @@ public class Renderer implements ApplicationListener{
public float minZoom = 1.5f, maxZoom = 6f; public float minZoom = 1.5f, maxZoom = 6f;
private @Nullable CoreBuild landCore; private @Nullable CoreBuild landCore;
//TODO unused
private FxProcessor fx = new FxProcessor();
private Color clearColor = new Color(0f, 0f, 0f, 1f); private Color clearColor = new Color(0f, 0f, 0f, 1f);
private float targetscale = Scl.scl(4); private float targetscale = Scl.scl(4), camerascale = targetscale, landscale, landTime, weatherAlpha, minZoomScl = Scl.scl(0.01f);
private float camerascale = targetscale;
private float landscale = 0f, landTime, weatherAlpha;
private float minZoomScl = Scl.scl(0.01f);
private float shakeIntensity, shaketime; private float shakeIntensity, shaketime;
public Renderer(){ public Renderer(){
@@ -136,11 +130,6 @@ public class Renderer implements ApplicationListener{
Events.fire(new DisposeEvent()); Events.fire(new DisposeEvent());
} }
@Override
public void resize(int width, int height){
fx.resize(width, height);
}
@Override @Override
public void resume(){ public void resume(){
if(settings.getBool("bloom") && bloom != null){ if(settings.getBool("bloom") && bloom != null){
@@ -175,23 +164,6 @@ public class Renderer implements ApplicationListener{
} }
} }
void beginFx(){
if(!fx.hasEnabledEffects()) return;
Draw.flush();
fx.clear();
fx.begin();
}
void endFx(){
if(!fx.hasEnabledEffects()) return;
Draw.flush();
fx.end();
fx.applyEffects();
fx.render(0, 0, fx.getWidth(), fx.getHeight());
}
void updateShake(float scale){ void updateShake(float scale){
if(shaketime > 0){ if(shaketime > 0){
float intensity = shakeIntensity * (settings.getInt("screenshake", 4) / 4f) * scale; float intensity = shakeIntensity * (settings.getInt("screenshake", 4) / 4f) * scale;

View File

@@ -214,6 +214,13 @@ public class UI implements ApplicationListener, Loadable{
@Override @Override
public void resize(int width, int height){ public void resize(int width, int height){
if(Core.scene == null) return; if(Core.scene == null) return;
int[] insets = Core.graphics.getSafeInsets();
Core.scene.marginLeft = insets[0];
Core.scene.marginRight = insets[1];
Core.scene.marginTop = insets[2];
Core.scene.marginBottom = insets[3];
Core.scene.resize(width, height); Core.scene.resize(width, height);
Events.fire(new ResizeEvent()); Events.fire(new ResizeEvent());
} }
@@ -363,6 +370,16 @@ public class UI implements ApplicationListener, Loadable{
}}.show(); }}.show();
} }
public void showInfoOnHidden(String info, Runnable listener){
new Dialog(""){{
getCell(cont).growX();
cont.margin(15).add(info).width(400f).wrap().get().setAlignment(Align.center, Align.center);
buttons.button("@ok", this::hide).size(110, 50).pad(4);
hidden(listener);
closeOnBack();
}}.show();
}
public void showStartupInfo(String info){ public void showStartupInfo(String info){
new Dialog(""){{ new Dialog(""){{
getCell(cont).growX(); getCell(cont).growX();
@@ -526,16 +543,15 @@ public class UI implements ApplicationListener, Loadable{
dialog.show(); dialog.show();
} }
//TODO move?
public static String formatAmount(int number){ public static String formatAmount(int number){
if(number >= 1_000_000_000){ int mag = Math.abs(number);
if(mag >= 1_000_000_000){
return Strings.fixed(number / 1_000_000_000f, 1) + "[gray]" + Core.bundle.get("unit.billions") + "[]"; return Strings.fixed(number / 1_000_000_000f, 1) + "[gray]" + Core.bundle.get("unit.billions") + "[]";
}else if(number >= 1_000_000){ }else if(mag >= 1_000_000){
return Strings.fixed(number / 1_000_000f, 1) + "[gray]" + Core.bundle.get("unit.millions") + "[]"; return Strings.fixed(number / 1_000_000f, 1) + "[gray]" + Core.bundle.get("unit.millions") + "[]";
}else if(number >= 10_000){ }else if(mag >= 10_000){
return number / 1000 + "[gray]" + Core.bundle.get("unit.thousands") + "[]"; return number / 1000 + "[gray]" + Core.bundle.get("unit.thousands") + "[]";
}else if(number >= 1000){ }else if(mag >= 1000){
return Strings.fixed(number / 1000f, 1) + "[gray]" + Core.bundle.get("unit.thousands") + "[]"; return Strings.fixed(number / 1000f, 1) + "[gray]" + Core.bundle.get("unit.thousands") + "[]";
}else{ }else{
return number + ""; return number + "";

View File

@@ -263,7 +263,9 @@ public class MapEditorDialog extends Dialog implements Disposable{
if(player.team().core() == null){ if(player.team().core() == null){
player.set(world.width() * tilesize/2f, world.height() * tilesize/2f); player.set(world.width() * tilesize/2f, world.height() * tilesize/2f);
player.unit(UnitTypes.alpha.spawn(player.team(), player.x, player.y)); var unit = UnitTypes.alpha.spawn(player.team(), player.x, player.y);
unit.spawnedByCore = true;
player.unit(unit);
} }
}); });
} }

View File

@@ -285,29 +285,31 @@ public class MapGenerateDialog extends BaseDialog{
void showAdd(){ void showAdd(){
BaseDialog selection = new BaseDialog("@add"); BaseDialog selection = new BaseDialog("@add");
selection.setFillParent(false); selection.cont.pane(p -> {
selection.cont.defaults().size(210f, 60f); p.marginRight(14);
int i = 0; p.defaults().size(210f, 60f);
for(Prov<GenerateFilter> gen : filterTypes){ int i = 0;
GenerateFilter filter = gen.get(); for(Prov<GenerateFilter> gen : filterTypes){
GenerateFilter filter = gen.get();
if((filter.isPost() && applied)) continue; if((filter.isPost() && applied)) continue;
selection.cont.button(filter.name(), () -> { p.button(filter.name(), () -> {
filters.add(filter); filters.add(filter);
rebuildFilters();
update();
selection.hide();
});
if(++i % 2 == 0) p.row();
}
p.button("@filter.defaultores", () -> {
maps.addDefaultOres(filters);
rebuildFilters(); rebuildFilters();
update(); update();
selection.hide(); selection.hide();
}); });
if(++i % 2 == 0) selection.cont.row(); }).get().setScrollingDisabled(true, false);
}
selection.cont.button("@filter.defaultores", () -> {
maps.addDefaultOres(filters);
rebuildFilters();
update();
selection.hide();
});
selection.addCloseButton(); selection.addCloseButton();
selection.show(); selection.show();

View File

@@ -39,9 +39,9 @@ public class Damage{
/** Creates a dynamic explosion based on specified parameters. */ /** Creates a dynamic explosion based on specified parameters. */
public static void dynamicExplosion(float x, float y, float flammability, float explosiveness, float power, float radius, boolean damage, boolean fire, @Nullable Team ignoreTeam){ public static void dynamicExplosion(float x, float y, float flammability, float explosiveness, float power, float radius, boolean damage, boolean fire, @Nullable Team ignoreTeam){
if(damage){ if(damage){
for(int i = 0; i < Mathf.clamp(power / 20, 0, 6); i++){ for(int i = 0; i < Mathf.clamp(power / 700, 0, 8); i++){
int branches = 5 + Mathf.clamp((int)(power / 30), 1, 20); int length = 5 + Mathf.clamp((int)(power / 500), 1, 20);
Time.run(i * 2f + Mathf.random(4f), () -> Lightning.create(Team.derelict, Pal.power, 3, x, y, Mathf.random(360f), branches + Mathf.range(2))); Time.run(i * 0.8f + Mathf.random(4f), () -> Lightning.create(Team.derelict, Pal.power, 3, x, y, Mathf.random(360f), length + Mathf.range(2)));
} }
if(fire){ if(fire){

View File

@@ -3,6 +3,7 @@ package mindustry.entities;
import arc.math.*; import arc.math.*;
import arc.math.geom.*; import arc.math.geom.*;
import arc.struct.*; import arc.struct.*;
import mindustry.content.*;
import mindustry.gen.*; import mindustry.gen.*;
import mindustry.world.*; import mindustry.world.*;
@@ -126,7 +127,7 @@ public class EntityCollisions{
public static boolean legsSolid(int x, int y){ public static boolean legsSolid(int x, int y){
Tile tile = world.tile(x, y); Tile tile = world.tile(x, y);
return tile == null || tile.staticDarkness() >= 2 || tile.floor().solid; return tile == null || tile.staticDarkness() >= 2 || (tile.floor().solid && tile.block() == Blocks.air);
} }
public static boolean waterSolid(int x, int y){ public static boolean waterSolid(int x, int y){
@@ -190,14 +191,10 @@ public class EntityCollisions{
yInvExit = y2 - (y1 + h1); yInvExit = y2 - (y1 + h1);
} }
float xEntry, yEntry; float xEntry = xInvEntry / vx1;
float xExit, yExit; float xExit = xInvExit / vx1;
float yEntry = yInvEntry / vy1;
xEntry = xInvEntry / vx1; float yExit = yInvExit / vy1;
xExit = xInvExit / vx1;
yEntry = yInvEntry / vy1;
yExit = yInvExit / vy1;
float entryTime = Math.max(xEntry, yEntry); float entryTime = Math.max(xEntry, yEntry);
float exitTime = Math.min(xExit, yExit); float exitTime = Math.min(xExit, yExit);

View File

@@ -536,6 +536,10 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
} }
public void dumpLiquid(Liquid liquid){ public void dumpLiquid(Liquid liquid){
dumpLiquid(liquid, 2f);
}
public void dumpLiquid(Liquid liquid, float scaling){
int dump = this.cdump; int dump = this.cdump;
if(liquids.get(liquid) <= 0.0001f) return; if(liquids.get(liquid) <= 0.0001f) return;
@@ -551,10 +555,9 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
float ofract = other.liquids.get(liquid) / other.block.liquidCapacity; float ofract = other.liquids.get(liquid) / other.block.liquidCapacity;
float fract = liquids.get(liquid) / block.liquidCapacity; float fract = liquids.get(liquid) / block.liquidCapacity;
if(ofract < fract) transferLiquid(other, (fract - ofract) * block.liquidCapacity / 2f, liquid); if(ofract < fract) transferLiquid(other, (fract - ofract) * block.liquidCapacity / scaling, liquid);
} }
} }
} }
public boolean canDumpLiquid(Building to, Liquid liquid){ public boolean canDumpLiquid(Building to, Liquid liquid){
@@ -979,6 +982,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
int amount = items.get(item); int amount = items.get(item);
explosiveness += item.explosiveness * amount; explosiveness += item.explosiveness * amount;
flammability += item.flammability * amount; flammability += item.flammability * amount;
power += item.charge * amount * 100f;
} }
} }
@@ -1306,6 +1310,8 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
case health -> health; case health -> health;
case maxHealth -> maxHealth; case maxHealth -> maxHealth;
case efficiency -> efficiency(); case efficiency -> efficiency();
case timescale -> timeScale;
case range -> this instanceof Ranged r ? r.range() / tilesize : 0;
case rotation -> rotation; case rotation -> rotation;
case totalItems -> items == null ? 0 : items.total(); case totalItems -> items == null ? 0 : items.total();
case totalLiquids -> liquids == null ? 0 : liquids.total(); case totalLiquids -> liquids == null ? 0 : liquids.total();

View File

@@ -80,6 +80,7 @@ abstract class CommanderComp implements Entityc, Posc{
void command(Formation formation, Seq<Unit> units){ void command(Formation formation, Seq<Unit> units){
clearCommand(); clearCommand();
units.shuffle();
float spacing = hitSize * 0.8f; float spacing = hitSize * 0.8f;
minFormationSpeed = type.speed; minFormationSpeed = type.speed;

View File

@@ -55,10 +55,16 @@ abstract class StatusComp implements Posc, Flyingc{
} }
} }
//otherwise, no opposites found, add direct effect if(!effect.reactive){
StatusEntry entry = Pools.obtain(StatusEntry.class, StatusEntry::new); //otherwise, no opposites found, add direct effect
entry.set(effect, duration); StatusEntry entry = Pools.obtain(StatusEntry.class, StatusEntry::new);
statuses.add(entry); entry.set(effect, duration);
statuses.add(entry);
}
}
void clearStatuses(){
statuses.clear();
} }
/** Removes a status effect. */ /** Removes a status effect. */

View File

@@ -53,7 +53,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
} }
public void approach(Vec2 vector){ public void approach(Vec2 vector){
vel.approachDelta(vector, type.accel * realSpeed() * floorSpeedMultiplier()); vel.approachDelta(vector, type.accel * realSpeed());
} }
public void aimLook(Position pos){ public void aimLook(Position pos){
@@ -82,7 +82,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
/** @return speed with boost multipliers factored in. */ /** @return speed with boost multipliers factored in. */
public float realSpeed(){ public float realSpeed(){
return Mathf.lerp(1f, type.canBoost ? type.boostMultiplier : 1f, elevation) * speed(); return Mathf.lerp(1f, type.canBoost ? type.boostMultiplier : 1f, elevation) * speed() * floorSpeedMultiplier();
} }
/** Iterates through this unit and everything it is controlling. */ /** Iterates through this unit and everything it is controlling. */
@@ -130,6 +130,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
case y -> World.conv(y); case y -> World.conv(y);
case team -> team.id; case team -> team.id;
case shooting -> isShooting() ? 1 : 0; case shooting -> isShooting() ? 1 : 0;
case range -> range() / tilesize;
case shootX -> World.conv(aimX()); case shootX -> World.conv(aimX());
case shootY -> World.conv(aimY()); case shootY -> World.conv(aimY());
case mining -> mining() ? 1 : 0; case mining -> mining() ? 1 : 0;
@@ -410,9 +411,10 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
float explosiveness = 2f + item().explosiveness * stack().amount * 1.53f; float explosiveness = 2f + item().explosiveness * stack().amount * 1.53f;
float flammability = item().flammability * stack().amount / 1.9f; float flammability = item().flammability * stack().amount / 1.9f;
float power = item().charge * stack().amount * 150f;
if(!spawnedByCore){ if(!spawnedByCore){
Damage.dynamicExplosion(x, y, flammability, explosiveness, 0f, bounds() / 2f, state.rules.damageExplosions, item().flammability > 1, team); Damage.dynamicExplosion(x, y, flammability, explosiveness, power, bounds() / 2f, state.rules.damageExplosions, item().flammability > 1, team);
} }
float shake = hitSize / 3f; float shake = hitSize / 3f;

View File

@@ -1,6 +1,5 @@
package mindustry.entities.comp; package mindustry.entities.comp;
import arc.math.*;
import arc.math.geom.*; import arc.math.geom.*;
import arc.util.*; import arc.util.*;
import mindustry.annotations.Annotations.*; import mindustry.annotations.Annotations.*;
@@ -22,7 +21,7 @@ abstract class VelComp implements Posc{
@Override @Override
public void update(){ public void update(){
move(vel.x * Time.delta, vel.y * Time.delta); move(vel.x * Time.delta, vel.y * Time.delta);
vel.scl(Mathf.clamp(1f - drag * Time.delta)); vel.scl(Math.max(1f - drag * Time.delta, 0));
} }
/** @return function to use for check solid state. if null, no checking is done. */ /** @return function to use for check solid state. if null, no checking is done. */

View File

@@ -19,6 +19,7 @@ public class MultiEffect extends Effect{
public void init(){ public void init(){
for(Effect f : effects){ for(Effect f : effects){
clip = Math.max(clip, f.clip); clip = Math.max(clip, f.clip);
lifetime = Math.max(lifetime, f.lifetime);
} }
} }

View File

@@ -16,6 +16,7 @@ public class ParticleEffect extends Effect{
//region only //region only
public float sizeFrom = 2f, sizeTo = 0f; public float sizeFrom = 2f, sizeTo = 0f;
public float offset = 0;
public String region = "circle"; public String region = "circle";
//line only //line only
@@ -48,7 +49,7 @@ public class ParticleEffect extends Effect{
}); });
}else{ }else{
Angles.randLenVectors(e.id, particles, length * fin + baseLength, e.rotation, cone, (x, y) -> { Angles.randLenVectors(e.id, particles, length * fin + baseLength, e.rotation, cone, (x, y) -> {
Draw.rect(tex, e.x + x, e.y + y, rad, rad); Draw.rect(tex, e.x + x, e.y + y, rad, rad, e.rotation + offset);
}); });
} }
} }

View File

@@ -68,6 +68,8 @@ public class EventType{
public static class ContentInitEvent{} public static class ContentInitEvent{}
/** Called when the client game is first loaded. */ /** Called when the client game is first loaded. */
public static class ClientLoadEvent{} public static class ClientLoadEvent{}
/** Called *after* all the modded files have been added into Vars.tree */
public static class FileTreeInitEvent{}
/** Called when a game begins and the world is loaded. */ /** Called when a game begins and the world is loaded. */
public static class WorldLoadEvent{} public static class WorldLoadEvent{}

View File

@@ -31,7 +31,6 @@ public enum Gamemode{
rules.buildCostMultiplier = 1f; rules.buildCostMultiplier = 1f;
rules.buildSpeedMultiplier = 1f; rules.buildSpeedMultiplier = 1f;
rules.unitBuildSpeedMultiplier = 2f; rules.unitBuildSpeedMultiplier = 2f;
rules.unitHealthMultiplier = 3f;
rules.attackMode = true; rules.attackMode = true;
}, map -> map.teams.size > 1), }, map -> map.teams.size > 1),
editor(true, rules -> { editor(true, rules -> {

View File

@@ -46,8 +46,6 @@ public class Rules{
public boolean unitAmmo = false; public boolean unitAmmo = false;
/** How fast unit pads build units. */ /** How fast unit pads build units. */
public float unitBuildSpeedMultiplier = 1f; public float unitBuildSpeedMultiplier = 1f;
/** How much health units start with. */
public float unitHealthMultiplier = 1f;
/** How much damage any other units deal. */ /** How much damage any other units deal. */
public float unitDamageMultiplier = 1f; public float unitDamageMultiplier = 1f;
/** How much health blocks start with. */ /** How much health blocks start with. */

View File

@@ -285,7 +285,7 @@ public class Schematics implements Loadable{
/** Creates an array of build requests from a schematic's data, centered on the provided x+y coordinates. */ /** Creates an array of build requests from a schematic's data, centered on the provided x+y coordinates. */
public Seq<BuildPlan> toRequests(Schematic schem, int x, int y){ public Seq<BuildPlan> toRequests(Schematic schem, int x, int y){
return schem.tiles.map(t -> new BuildPlan(t.x + x - schem.width/2, t.y + y - schem.height/2, t.rotation, t.block, t.config).original(t.x, t.y, schem.width, schem.height)) return schem.tiles.map(t -> new BuildPlan(t.x + x - schem.width/2, t.y + y - schem.height/2, t.rotation, t.block, t.config).original(t.x, t.y, schem.width, schem.height))
.removeAll(s -> (!s.block.isVisible() && !(s.block instanceof CoreBlock)) || !s.block.unlockedNow()); .removeAll(s -> (!s.block.isVisible() && !(s.block instanceof CoreBlock)) || !s.block.unlockedNow()).sort(Structs.comparingInt(s -> -s.block.schematicPriority));
} }
/** @return all the valid loadouts for a specific core type. */ /** @return all the valid loadouts for a specific core type. */

View File

@@ -123,6 +123,7 @@ public class Universe{
} }
/** @return the last selected loadout for this specific core type. */ /** @return the last selected loadout for this specific core type. */
@Nullable
public Schematic getLoadout(CoreBlock core){ public Schematic getLoadout(CoreBlock core){
//for tools - schem //for tools - schem
if(schematics == null) return Loadouts.basicShard; if(schematics == null) return Loadouts.basicShard;
@@ -134,7 +135,7 @@ public class Universe{
Seq<Schematic> all = schematics.getLoadouts(core); Seq<Schematic> all = schematics.getLoadouts(core);
Schematic schem = all.find(s -> s.file != null && s.file.nameWithoutExtension().equals(file)); Schematic schem = all.find(s -> s.file != null && s.file.nameWithoutExtension().equals(file));
return schem == null ? all.first() : schem; return schem == null ? all.any() ? all.first() : null : schem;
} }
/** Runs possible events. Resets event counter. */ /** Runs possible events. Resets event counter. */

View File

@@ -221,9 +221,13 @@ public class Drawf{
} }
public static void construct(float x, float y, TextureRegion region, float rotation, float progress, float speed, float time){ public static void construct(float x, float y, TextureRegion region, float rotation, float progress, float speed, float time){
construct(x, y, region, Pal.accent, rotation, progress, speed, time);
}
public static void construct(float x, float y, TextureRegion region, Color color, float rotation, float progress, float speed, float time){
Shaders.build.region = region; Shaders.build.region = region;
Shaders.build.progress = progress; Shaders.build.progress = progress;
Shaders.build.color.set(Pal.accent); Shaders.build.color.set(color);
Shaders.build.color.a = speed; Shaders.build.color.a = speed;
Shaders.build.time = -time / 20f; Shaders.build.time = -time / 20f;
@@ -235,9 +239,13 @@ public class Drawf{
} }
public static void construct(Building t, TextureRegion region, float rotation, float progress, float speed, float time){ public static void construct(Building t, TextureRegion region, float rotation, float progress, float speed, float time){
construct(t, region, Pal.accent, rotation, progress, speed, time);
}
public static void construct(Building t, TextureRegion region, Color color, float rotation, float progress, float speed, float time){
Shaders.build.region = region; Shaders.build.region = region;
Shaders.build.progress = progress; Shaders.build.progress = progress;
Shaders.build.color.set(Pal.accent); Shaders.build.color.set(color);
Shaders.build.color.a = speed; Shaders.build.color.a = speed;
Shaders.build.time = -time / 20f; Shaders.build.time = -time / 20f;

View File

@@ -96,7 +96,8 @@ public class MinimapRenderer implements Disposable{
Draw.mixcol(unit.team().color, 1f); Draw.mixcol(unit.team().color, 1f);
float scale = Scl.scl(1f) / 2f * scaling * 32f; float scale = Scl.scl(1f) / 2f * scaling * 32f;
Draw.rect(unit.type.icon(Cicon.full), x + rx, y + ry, scale, scale, unit.rotation() - 90); var region = unit.type.icon(Cicon.full);
Draw.rect(region, x + rx, y + ry, scale, scale * (float)region.height / region.width, unit.rotation() - 90);
Draw.reset(); Draw.reset();
} }

View File

@@ -42,7 +42,7 @@ public class DesktopInput extends InputHandler{
/** Selected build request for movement. */ /** Selected build request for movement. */
public @Nullable BuildPlan sreq; public @Nullable BuildPlan sreq;
/** Whether player is currently deleting removal requests. */ /** Whether player is currently deleting removal requests. */
public boolean deleting = false, shouldShoot = false, panning = false; public boolean deleting = false, wasBuilding = true, shouldShoot = false, panning = false;
/** Mouse pan speed. */ /** Mouse pan speed. */
public float panScale = 0.005f, panSpeed = 4.5f, panBoostSpeed = 11f; public float panScale = 0.005f, panSpeed = 4.5f, panBoostSpeed = 11f;
@@ -62,18 +62,19 @@ public class DesktopInput extends InputHandler{
group.fill(t -> { group.fill(t -> {
t.bottom(); t.bottom();
t.visible(() -> { t.visible(() -> {
t.color.a = Mathf.lerpDelta(t.color.a, player.unit().isBuilding() ? 1f : 0f, 0.15f); t.color.a = Mathf.lerpDelta(t.color.a, !isBuilding && !Core.settings.getBool("buildautopause") || player.unit().isBuilding() ? 1f : 0f, 0.15f);
return ui.hudfrag.shown && Core.settings.getBool("hints") && selectRequests.isEmpty() && t.color.a > 0.01f; return ui.hudfrag.shown && Core.settings.getBool("hints") && selectRequests.isEmpty() && t.color.a > 0.01f;
}); });
t.touchable(() -> t.color.a < 0.1f ? Touchable.disabled : Touchable.childrenOnly); t.touchable(() -> t.color.a < 0.1f ? Touchable.disabled : Touchable.childrenOnly);
t.table(Styles.black6, b -> { t.table(Styles.black6, b -> {
b.defaults().left(); b.defaults().left();
b.label(() -> Core.bundle.format(!isBuilding ? "resumebuilding" : "pausebuilding", Core.keybinds.get(Binding.pause_building).key.toString())).style(Styles.outlineLabel); b.label(() -> ((!isBuilding || !wasBuilding) && !Core.settings.getBool("buildautopause") && !player.unit().isBuilding() ?
b.row(); Core.bundle.format("enablebuilding", Core.keybinds.get(Binding.pause_building).key.toString()) :
b.label(() -> Core.bundle.format("cancelbuilding", Core.keybinds.get(Binding.clear_building).key.toString())).style(Styles.outlineLabel); Core.bundle.format(isBuilding ? "pausebuilding" : "resumebuilding", Core.keybinds.get(Binding.pause_building).key.toString()) +
b.row(); "\n" + Core.bundle.format("cancelbuilding", Core.keybinds.get(Binding.clear_building).key.toString()) +
b.label(() -> Core.bundle.format("selectschematic", Core.keybinds.get(Binding.schematic_select).key.toString())).style(Styles.outlineLabel); "\n" + Core.bundle.format("selectschematic", Core.keybinds.get(Binding.schematic_select).key.toString())
)).style(Styles.outlineLabel);
}).margin(10f); }).margin(10f);
}); });
@@ -204,7 +205,6 @@ public class DesktopInput extends InputHandler{
if(input.keyDown(Binding.mouse_move)){ if(input.keyDown(Binding.mouse_move)){
panCam = true; panCam = true;
} }
panning = false;
Core.camera.position.add(Tmp.v1.setZero().add(Core.input.axis(Binding.move_x), Core.input.axis(Binding.move_y)).nor().scl(camSpeed)); Core.camera.position.add(Tmp.v1.setZero().add(Core.input.axis(Binding.move_x), Core.input.axis(Binding.move_y)).nor().scl(camSpeed));
}else if(!player.dead() && !panning){ }else if(!player.dead() && !panning){
@@ -455,6 +455,7 @@ public class DesktopInput extends InputHandler{
buildWasAutoPaused = false; buildWasAutoPaused = false;
if(isBuilding){ if(isBuilding){
wasBuilding = player.unit().isBuilding();
player.shooting = false; player.shooting = false;
} }
} }
@@ -562,6 +563,7 @@ public class DesktopInput extends InputHandler{
} }
mode = none; mode = none;
wasBuilding = true;
} }
if(Core.input.keyTap(Binding.toggle_block_status)){ if(Core.input.keyTap(Binding.toggle_block_status)){

View File

@@ -68,6 +68,14 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
public Seq<BuildPlan> lineRequests = new Seq<>(); public Seq<BuildPlan> lineRequests = new Seq<>();
public Seq<BuildPlan> selectRequests = new Seq<>(); public Seq<BuildPlan> selectRequests = new Seq<>();
public InputHandler(){
Events.on(UnitDestroyEvent.class, e -> {
if(e.unit != null && e.unit.isPlayer() && e.unit.getPlayer().isLocal() && e.unit.type.weapons.contains(w -> w.bullet.killShooter)){
player.shooting = false;
}
});
}
//methods to override //methods to override
@Remote(called = Loc.server, unreliable = true) @Remote(called = Loc.server, unreliable = true)

View File

@@ -13,8 +13,9 @@ import java.io.*;
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public class JsonIO{ public class JsonIO{
private static CustomJson jsonBase = new CustomJson(); private static final CustomJson jsonBase = new CustomJson();
private static Json json = new Json(){
public static final Json json = new Json(){
{ apply(this); } { apply(this); }
@Override @Override
@@ -39,10 +40,6 @@ public class JsonIO{
} }
}; };
public static Json json(){
return json;
}
public static String write(Object object){ public static String write(Object object){
return json.toJson(object, object.getClass()); return json.toJson(object, object.getClass());
} }
@@ -69,7 +66,6 @@ public class JsonIO{
} }
static void apply(Json json){ static void apply(Json json){
json.setIgnoreUnknownFields(true);
json.setElementType(Rules.class, "spawns", SpawnGroup.class); json.setElementType(Rules.class, "spawns", SpawnGroup.class);
json.setElementType(Rules.class, "loadout", ItemStack.class); json.setElementType(Rules.class, "loadout", ItemStack.class);

View File

@@ -21,11 +21,13 @@ public enum LAccess{
maxHealth, maxHealth,
heat, heat,
efficiency, efficiency,
timescale,
rotation, rotation,
x, x,
y, y,
shootX, shootX,
shootY, shootY,
range,
shooting, shooting,
mineX, mineX,
mineY, mineY,
@@ -44,7 +46,8 @@ public enum LAccess{
enabled("to"), //"to" is standard for single parameter access enabled("to"), //"to" is standard for single parameter access
shoot("x", "y", "shoot"), shoot("x", "y", "shoot"),
shootp(true, "unit", "shoot"), shootp(true, "unit", "shoot"),
configure(true, 30, "to"); configure(true, 30, "to"),
color("r", "g", "b");
public final String[] params; public final String[] params;
public final boolean isObj; public final boolean isObj;

View File

@@ -61,9 +61,6 @@ public class LAssembler{
String[] lines = data.split("\n"); String[] lines = data.split("\n");
int index = 0; int index = 0;
for(String line : lines){ for(String line : lines){
//comments
int commentIdx = line.indexOf('#');
if(commentIdx != -1) line = line.substring(0, commentIdx).trim();
if(line.isEmpty()) continue; if(line.isEmpty()) continue;
//remove trailing semicolons in case someone adds them in for no reason //remove trailing semicolons in case someone adds them in for no reason
if(line.endsWith(";")) line = line.substring(0, line.length() - 1); if(line.endsWith(";")) line = line.substring(0, line.length() - 1);
@@ -74,6 +71,7 @@ public class LAssembler{
try{ try{
String[] arr; String[] arr;
if(line.startsWith("#")) continue;
//yes, I am aware that this can be split with regex, but that's slow and even more incomprehensible //yes, I am aware that this can be split with regex, but that's slow and even more incomprehensible
if(line.contains(" ")){ if(line.contains(" ")){
@@ -83,7 +81,9 @@ public class LAssembler{
for(int i = 0; i < line.length() + 1; i++){ for(int i = 0; i < line.length() + 1; i++){
char c = i == line.length() ? ' ' : line.charAt(i); char c = i == line.length() ? ' ' : line.charAt(i);
if(c == '"'){ if(c == '#' && !inString){
break;
}else if(c == '"'){
inString = !inString; inString = !inString;
}else if(c == ' ' && !inString){ }else if(c == ' ' && !inString){
tokens.add(line.substring(lastIdx, Math.min(i, lastIdx + maxTokenLength))); tokens.add(line.substring(lastIdx, Math.min(i, lastIdx + maxTokenLength)));
@@ -96,6 +96,9 @@ public class LAssembler{
arr = new String[]{line}; arr = new String[]{line};
} }
//nothing found
if(arr.length == 0) continue;
String type = arr[0]; String type = arr[0];
//legacy stuff //legacy stuff

View File

@@ -283,13 +283,13 @@ public class LCanvas extends Table{
t.add().growX(); t.add().growX();
t.button(Icon.copy, Styles.logici, () -> { t.button(Icon.copy, Styles.logici, () -> {
}).padRight(6).get().tapped(this::copy); }).size(24f).padRight(6).get().tapped(this::copy);
t.button(Icon.cancel, Styles.logici, () -> { t.button(Icon.cancel, Styles.logici, () -> {
remove(); remove();
dragging = null; dragging = null;
statements.layout(); statements.layout();
}); }).size(24f);
t.addListener(new InputListener(){ t.addListener(new InputListener(){
float lastx, lasty; float lastx, lasty;

View File

@@ -434,7 +434,7 @@ public class LExecutor{
} }
case build -> { case build -> {
if(unit.canBuild() && exec.obj(p3) instanceof Block block){ if(unit.canBuild() && exec.obj(p3) instanceof Block block){
int x = World.toTile(x1), y = World.toTile(y1); int x = World.toTile(x1 - block.offset/tilesize), y = World.toTile(y1 - block.offset/tilesize);
int rot = exec.numi(p4); int rot = exec.numi(p4);
//reset state of last request when necessary //reset state of last request when necessary
@@ -888,6 +888,7 @@ public class LExecutor{
String strValue = String strValue =
v.objval == null ? "null" : v.objval == null ? "null" :
v.objval instanceof String s ? s : v.objval instanceof String s ? s :
v.objval == Blocks.stoneWall ? "solid" : //special alias
v.objval instanceof MappableContent content ? content.name : v.objval instanceof MappableContent content ? content.name :
v.objval instanceof Content ? "[content]" : v.objval instanceof Content ? "[content]" :
v.objval instanceof Building build ? build.block.name : v.objval instanceof Building build ? build.block.name :

View File

@@ -35,6 +35,11 @@ public class LogicDialog extends BaseDialog{
p.table(Tex.button, t -> { p.table(Tex.button, t -> {
TextButtonStyle style = Styles.cleart; TextButtonStyle style = Styles.cleart;
t.defaults().size(280f, 60f).left(); t.defaults().size(280f, 60f).left();
t.button("@schematic.copy", Icon.copy, style, () -> {
dialog.hide();
Core.app.setClipboardText(canvas.save());
}).marginLeft(12f);
t.row(); t.row();
t.button("@schematic.copy.import", Icon.download, style, () -> { t.button("@schematic.copy.import", Icon.download, style, () -> {
dialog.hide(); dialog.hide();
@@ -44,11 +49,6 @@ public class LogicDialog extends BaseDialog{
ui.showException(e); ui.showException(e);
} }
}).marginLeft(12f).disabled(b -> Core.app.getClipboardText() == null); }).marginLeft(12f).disabled(b -> Core.app.getClipboardText() == null);
t.row();
t.button("@schematic.copy", Icon.copy, style, () -> {
dialog.hide();
Core.app.setClipboardText(canvas.save());
}).marginLeft(12f);
}); });
}); });

View File

@@ -4,6 +4,7 @@ package mindustry.mod;
public class ModListing{ public class ModListing{
public String repo, name, author, lastUpdated, description, minGameVersion; public String repo, name, author, lastUpdated, description, minGameVersion;
public boolean hasScripts, hasJava; public boolean hasScripts, hasJava;
public String[] contentTypes = {};
public int stars; public int stars;
@Override @Override

View File

@@ -65,23 +65,39 @@ public class Mods implements Loadable{
}); });
} }
/** @return the loaded mod found by name, or null if not found. */
public @Nullable LoadedMod getMod(String name){
return mods.find(m -> m.name.equals(name));
}
/** @return the loaded mod found by class, or null if not found. */ /** @return the loaded mod found by class, or null if not found. */
public @Nullable LoadedMod getMod(Class<? extends Mod> type){ public @Nullable LoadedMod getMod(Class<? extends Mod> type){
return mods.find(m -> m.enabled() && m.main != null && m.main.getClass() == type); return mods.find(m -> m.enabled() && m.main != null && m.main.getClass() == type);
} }
/** Imports an external mod file.*/ /** Imports an external mod file. Folders are not supported here. */
public void importMod(Fi file) throws IOException{ public LoadedMod importMod(Fi file) throws IOException{
Fi dest = modDirectory.child(file.name()); String baseName = file.nameWithoutExtension();
if(dest.exists()){ String finalName = baseName;
throw new IOException("A file with the same name already exists in the mod folder!"); //find a name to prevent any name conflicts
int count = 1;
while(modDirectory.child(finalName + ".zip").exists()){
finalName = baseName + "" + count++;
} }
Fi dest = modDirectory.child(finalName + ".zip");
file.copyTo(dest); file.copyTo(dest);
try{ try{
mods.add(loadMod(dest)); var loaded = loadMod(dest, true);
mods.add(loaded);
requiresReload = true; requiresReload = true;
sortMods(); sortMods();
//try to load the mod's icon so it displays on import
Core.app.post(() -> {
loadIcon(loaded);
});
return loaded;
}catch(IOException e){ }catch(IOException e){
dest.delete(); dest.delete();
throw e; throw e;
@@ -113,13 +129,18 @@ public class Mods implements Loadable{
private void loadIcons(){ private void loadIcons(){
for(LoadedMod mod : mods){ for(LoadedMod mod : mods){
//try to load icon for each mod that can have one loadIcon(mod);
if(mod.root.child("icon.png").exists()){ }
try{ }
mod.iconTexture = new Texture(mod.root.child("icon.png"));
}catch(Throwable t){ private void loadIcon(LoadedMod mod){
Log.err("Failed to load icon for mod '" + mod.name + "'.", t); //try to load icon for each mod that can have one
} if(mod.root.child("icon.png").exists()){
try{
mod.iconTexture = new Texture(mod.root.child("icon.png"));
mod.iconTexture.setFilter(TextureFilter.linear);
}catch(Throwable t){
Log.err("Failed to load icon for mod '" + mod.name + "'.", t);
} }
} }
} }
@@ -347,6 +368,7 @@ public class Mods implements Loadable{
} }
} }
} }
Events.fire(new FileTreeInitEvent());
//add new keys to each bundle //add new keys to each bundle
I18NBundle bundle = Core.bundle; I18NBundle bundle = Core.bundle;
@@ -585,8 +607,14 @@ public class Mods implements Loadable{
} }
/** Loads a mod file+meta, but does not add it to the list. /** Loads a mod file+meta, but does not add it to the list.
* Note that directories can be loaded as mods.*/ * Note that directories can be loaded as mods. */
private LoadedMod loadMod(Fi sourceFile) throws Exception{ private LoadedMod loadMod(Fi sourceFile) throws Exception{
return loadMod(sourceFile, false);
}
/** Loads a mod file+meta, but does not add it to the list.
* Note that directories can be loaded as mods. */
private LoadedMod loadMod(Fi sourceFile, boolean overwrite) throws Exception{
Time.mark(); Time.mark();
ZipFi rootZip = null; ZipFi rootZip = null;
@@ -614,23 +642,47 @@ public class Mods implements Loadable{
String mainClass = meta.main == null ? camelized.toLowerCase() + "." + camelized + "Mod" : meta.main; String mainClass = meta.main == null ? camelized.toLowerCase() + "." + camelized + "Mod" : meta.main;
String baseName = meta.name.toLowerCase().replace(" ", "-"); String baseName = meta.name.toLowerCase().replace(" ", "-");
if(mods.contains(m -> m.name.equals(baseName))){ var other = mods.find(m -> m.name.equals(baseName));
throw new IllegalArgumentException("A mod with the name '" + baseName + "' is already imported.");
if(other != null){
//steam mods can't really be deleted, they need to be unsubscribed
if(overwrite && !other.hasSteamID()){
//close zip file
if(other.root instanceof ZipFi){
other.root.delete();
}
//delete the old mod directory
if(other.file.isDirectory()){
other.file.deleteDirectory();
}else{
other.file.delete();
}
//unload
mods.remove(other);
}else{
throw new IllegalArgumentException("A mod with the name '" + baseName + "' is already imported.");
}
} }
Mod mainMod; Mod mainMod;
Fi mainFile = zip; Fi mainFile = zip;
String[] path = (mainClass.replace('.', '/') + ".class").split("/"); if(android){
for(String str : path){ mainFile = mainFile.child("classes.dex");
if(!str.isEmpty()){ }else{
mainFile = mainFile.child(str); String[] path = (mainClass.replace('.', '/') + ".class").split("/");
for(String str : path){
if(!str.isEmpty()){
mainFile = mainFile.child(str);
}
} }
} }
//make sure the main class exists before loading it; if it doesn't just don't put it there //make sure the main class exists before loading it; if it doesn't just don't put it there
if(mainFile.exists() && Core.settings.getBool("mod-" + baseName + "-enabled", true)){ //if the mod is explicitly marked as java, try loading it anyway
//mobile versions don't support class mods if((mainFile.exists() || meta.java) &&
Core.settings.getBool("mod-" + baseName + "-enabled", true) && Version.isAtLeast(meta.minGameVersion) && (meta.getMinMajor() >= 105 || headless)){
if(ios){ if(ios){
throw new IllegalArgumentException("Java class mods are not supported on iOS."); throw new IllegalArgumentException("Java class mods are not supported on iOS.");
} }
@@ -700,6 +752,20 @@ public class Mods implements Loadable{
this.name = meta.name.toLowerCase().replace(" ", "-"); this.name = meta.name.toLowerCase().replace(" ", "-");
} }
/** @return whether this is a java class mod. */
public boolean isJava(){
return meta.java || main != null;
}
@Nullable
public String getRepo(){
return Core.settings.getString("mod-" + name + "-repo", meta.repo);
}
public void setRepo(String repo){
Core.settings.put("mod-" + name + "-repo", repo);
}
public boolean enabled(){ public boolean enabled(){
return state == ModState.enabled || state == ModState.contentErrors; return state == ModState.enabled || state == ModState.contentErrors;
} }
@@ -720,11 +786,7 @@ public class Mods implements Loadable{
public boolean isSupported(){ public boolean isSupported(){
if(isOutdated()) return false; if(isOutdated()) return false;
int major = getMinMajor(), minor = getMinMinor(); return Version.isAtLeast(meta.minGameVersion);
if(Version.build <= 0) return true;
return Version.build >= major && Version.revision >= minor;
} }
/** @return whether this mod is outdated, e.g. not compatible with v6. */ /** @return whether this mod is outdated, e.g. not compatible with v6. */
@@ -734,33 +796,7 @@ public class Mods implements Loadable{
} }
public int getMinMajor(){ public int getMinMajor(){
int major = 0; return meta.getMinMajor();
String ver = meta.minGameVersion == null ? "0" : meta.minGameVersion;
if(ver.contains(".")){
String[] split = ver.split("\\.");
if(split.length == 2){
major = Strings.parseInt(split[0], 0);
}
}else{
major = Strings.parseInt(ver, 0);
}
return major;
}
public int getMinMinor(){
String ver = meta.minGameVersion == null ? "0" : meta.minGameVersion;
if(ver.contains(".")){
String[] split = ver.split("\\.");
if(split.length == 2){
return Strings.parseInt(split[1], 0);
}
}
return 0;
} }
@Override @Override
@@ -842,6 +878,8 @@ public class Mods implements Loadable{
public Seq<String> dependencies = Seq.with(); public Seq<String> dependencies = Seq.with();
/** Hidden mods are only server-side or client-side, and do not support adding new content. */ /** Hidden mods are only server-side or client-side, and do not support adding new content. */
public boolean hidden; public boolean hidden;
/** If true, this mod should be loaded as a Java class mod. This is technically optional, but highly recommended. */
public boolean java;
public String displayName(){ public String displayName(){
return displayName == null ? name : displayName; return displayName == null ? name : displayName;
@@ -854,6 +892,12 @@ public class Mods implements Loadable{
if(description != null) description = Strings.stripColors(description); if(description != null) description = Strings.stripColors(description);
} }
public int getMinMajor(){
String ver = minGameVersion == null ? "0" : minGameVersion;
int dot = ver.indexOf(".");
return dot != -1 ? Strings.parseInt(ver.substring(0, dot), 0) : Strings.parseInt(ver, 0);
}
@Override @Override
public String toString() { public String toString() {
return "ModMeta{" + return "ModMeta{" +
@@ -863,6 +907,7 @@ public class Mods implements Loadable{
", main='" + main + '\'' + ", main='" + main + '\'' +
", minGameVersion='" + minGameVersion + '\'' + ", minGameVersion='" + minGameVersion + '\'' +
", hidden=" + hidden + ", hidden=" + hidden +
", repo=" + repo +
'}'; '}';
} }
} }

View File

@@ -57,7 +57,7 @@ public class Scripts implements Disposable{
public String runConsole(String text){ public String runConsole(String text){
try{ try{
Object o = context.evaluateString(scope, text, "console.js", 1, null); Object o = context.evaluateString(scope, text, "console.js", 1, null);
if(o instanceof NativeJavaObject) o = ((NativeJavaObject)o).unwrap(); if(o instanceof NativeJavaObject n) o = n.unwrap();
if(o instanceof Undefined) o = "undefined"; if(o instanceof Undefined) o = "undefined";
return String.valueOf(o); return String.valueOf(o);
}catch(Throwable t){ }catch(Throwable t){

View File

@@ -274,9 +274,7 @@ public class Net{
public void handleServerReceived(NetConnection connection, Object object){ public void handleServerReceived(NetConnection connection, Object object){
if(serverListeners.get(object.getClass()) != null){ if(serverListeners.get(object.getClass()) != null){
if(serverListeners.get(object.getClass()) != null){ serverListeners.get(object.getClass()).get(connection, object);
serverListeners.get(object.getClass()).get(connection, object);
}
Pools.free(object); Pools.free(object);
}else{ }else{
Log.err("Unhandled packet type: '@'!", object.getClass()); Log.err("Unhandled packet type: '@'!", object.getClass());

View File

@@ -17,6 +17,8 @@ public class Item extends UnlockableContent{
public float flammability = 0f; public float flammability = 0f;
/** how radioactive this item is. 0=none, 1=chernobyl ground zero */ /** how radioactive this item is. 0=none, 1=chernobyl ground zero */
public float radioactivity; public float radioactivity;
/** how electrically potent this item is. */
public float charge = 0f;
/** drill hardness of the item */ /** drill hardness of the item */
public int hardness = 0; public int hardness = 0;
/** /**
@@ -41,6 +43,7 @@ public class Item extends UnlockableContent{
stats.addPercent(Stat.explosiveness, explosiveness); stats.addPercent(Stat.explosiveness, explosiveness);
stats.addPercent(Stat.flammability, flammability); stats.addPercent(Stat.flammability, flammability);
stats.addPercent(Stat.radioactivity, radioactivity); stats.addPercent(Stat.radioactivity, radioactivity);
stats.addPercent(Stat.charge, charge);
} }
@Override @Override

View File

@@ -25,6 +25,8 @@ public class StatusEffect extends MappableContent{
public float effectChance = 0.15f; public float effectChance = 0.15f;
/** If true, the effect never disappears. */ /** If true, the effect never disappears. */
public boolean permanent; public boolean permanent;
/** If true, this effect will only react with other effects and cannot be applied. */
public boolean reactive;
/** Tint color of effect. */ /** Tint color of effect. */
public Color color = Color.white.cpy(); public Color color = Color.white.cpy();
/** Effect that happens randomly on top of the affected unit. */ /** Effect that happens randomly on top of the affected unit. */
@@ -63,6 +65,7 @@ public class StatusEffect extends MappableContent{
protected void trans(StatusEffect effect, TransitionHandler handler){ protected void trans(StatusEffect effect, TransitionHandler handler){
transitions.put(effect, handler); transitions.put(effect, handler);
effect.transitions.put(this, handler);
} }
protected void opposite(StatusEffect... effect){ protected void opposite(StatusEffect... effect){

View File

@@ -11,6 +11,7 @@ import arc.scene.ui.*;
import arc.scene.ui.layout.*; import arc.scene.ui.layout.*;
import arc.struct.*; import arc.struct.*;
import arc.util.*; import arc.util.*;
import mindustry.*;
import mindustry.ai.types.*; import mindustry.ai.types.*;
import mindustry.annotations.Annotations.*; import mindustry.annotations.Annotations.*;
import mindustry.content.*; import mindustry.content.*;
@@ -224,6 +225,7 @@ public class UnitType extends UnlockableContent{
Unit inst = constructor.get(); Unit inst = constructor.get();
stats.add(Stat.health, health); stats.add(Stat.health, health);
stats.add(Stat.armor, armor);
stats.add(Stat.speed, speed); stats.add(Stat.speed, speed);
stats.add(Stat.itemCapacity, itemCapacity); stats.add(Stat.itemCapacity, itemCapacity);
stats.add(Stat.range, (int)(maxRange / tilesize), StatUnit.blocks); stats.add(Stat.range, (int)(maxRange / tilesize), StatUnit.blocks);
@@ -292,12 +294,17 @@ public class UnitType extends UnlockableContent{
if(maxRange < 0){ if(maxRange < 0){
maxRange = 0f; maxRange = 0f;
maxRange = Math.max(maxRange, range);
for(Weapon weapon : weapons){ for(Weapon weapon : weapons){
maxRange = Math.max(maxRange, weapon.bullet.range() + hitSize / 2f); maxRange = Math.max(maxRange, weapon.bullet.range() + hitSize / 2f);
} }
} }
if(weapons.isEmpty()){
range = maxRange = miningRange;
}
if(mechStride < 0){ if(mechStride < 0){
mechStride = 4f + (hitSize -8f)/2.1f; mechStride = 4f + (hitSize -8f)/2.1f;
} }

View File

@@ -7,7 +7,7 @@ import arc.scene.ui.layout.*;
import mindustry.graphics.*; import mindustry.graphics.*;
public class BorderImage extends Image{ public class BorderImage extends Image{
public float thickness = 4f; public float thickness = 4f, pad = 0f;
public Color borderColor = Pal.gray; public Color borderColor = Pal.gray;
public BorderImage(){ public BorderImage(){
@@ -40,7 +40,7 @@ public class BorderImage extends Image{
Draw.color(borderColor); Draw.color(borderColor);
Draw.alpha(parentAlpha); Draw.alpha(parentAlpha);
Lines.stroke(Scl.scl(thickness)); Lines.stroke(Scl.scl(thickness));
Lines.rect(x + imageX, y + imageY, imageWidth * scaleX, imageHeight * scaleY); Lines.rect(x + imageX - pad, y + imageY - pad, imageWidth * scaleX + pad*2, imageHeight * scaleY + pad*2);
Draw.reset(); Draw.reset();
} }
} }

View File

@@ -25,8 +25,7 @@ public class CustomGameDialog extends BaseDialog{
void setup(){ void setup(){
clearChildren(); clearChildren();
add(titleTable); add(titleTable).growX().row();
row();
stack(cont, buttons).grow(); stack(cont, buttons).grow();
buttons.bottom(); buttons.bottom();
cont.clear(); cont.clear();

View File

@@ -161,7 +161,6 @@ public class CustomRulesDialog extends BaseDialog{
title("@rules.title.unit"); title("@rules.title.unit");
check("@rules.unitammo", b -> rules.unitAmmo = b, () -> rules.unitAmmo); check("@rules.unitammo", b -> rules.unitAmmo = b, () -> rules.unitAmmo);
number("@rules.unithealthmultiplier", f -> rules.unitHealthMultiplier = f, () -> rules.unitHealthMultiplier);
number("@rules.unitdamagemultiplier", f -> rules.unitDamageMultiplier = f, () -> rules.unitDamageMultiplier); number("@rules.unitdamagemultiplier", f -> rules.unitDamageMultiplier = f, () -> rules.unitDamageMultiplier);
number("@rules.unitbuildspeedmultiplier", f -> rules.unitBuildSpeedMultiplier = f, () -> rules.unitBuildSpeedMultiplier, 0.001f, 50f); number("@rules.unitbuildspeedmultiplier", f -> rules.unitBuildSpeedMultiplier = f, () -> rules.unitBuildSpeedMultiplier, 0.001f, 50f);

View File

@@ -60,9 +60,9 @@ public class DatabaseDialog extends BaseDialog{
list.add(image).size(8 * 4).pad(3); list.add(image).size(8 * 4).pad(3);
ClickListener listener = new ClickListener(); ClickListener listener = new ClickListener();
image.addListener(listener); image.addListener(listener);
if(!Vars.mobile && unlocked(unlock)){ if(!mobile && unlocked(unlock)){
image.addListener(new HandCursorListener()); image.addListener(new HandCursorListener());
image.update(() -> image.color.lerp(!listener.isOver() ? Color.lightGray : Color.white, 0.4f * Time.delta)); image.update(() -> image.color.lerp(!listener.isOver() ? Color.lightGray : Color.white, Mathf.clamp(0.4f * Time.delta)));
} }
if(unlocked(unlock)){ if(unlocked(unlock)){
@@ -71,7 +71,7 @@ public class DatabaseDialog extends BaseDialog{
Core.app.setClipboardText((char)Fonts.getUnicode(unlock.name) + ""); Core.app.setClipboardText((char)Fonts.getUnicode(unlock.name) + "");
ui.showInfoFade("@copied"); ui.showInfoFade("@copied");
}else{ }else{
Vars.ui.content.show(unlock); ui.content.show(unlock);
} }
}); });
image.addListener(new Tooltip(t -> t.background(Tex.button).add(unlock.localizedName))); image.addListener(new Tooltip(t -> t.background(Tex.button).add(unlock.localizedName)));

View File

@@ -5,6 +5,7 @@ import arc.func.*;
import arc.scene.ui.*; import arc.scene.ui.*;
import arc.scene.ui.layout.*; import arc.scene.ui.layout.*;
import arc.struct.*; import arc.struct.*;
import mindustry.content.*;
import mindustry.ctype.*; import mindustry.ctype.*;
import mindustry.game.*; import mindustry.game.*;
import mindustry.gen.*; import mindustry.gen.*;
@@ -105,6 +106,7 @@ public class LaunchLoadoutDialog extends BaseDialog{
int cols = Math.max((int)(Core.graphics.getWidth() / Scl.scl(230)), 1); int cols = Math.max((int)(Core.graphics.getWidth() / Scl.scl(230)), 1);
ButtonGroup<Button> group = new ButtonGroup<>(); ButtonGroup<Button> group = new ButtonGroup<>();
selected = universe.getLoadout(core); selected = universe.getLoadout(core);
if(selected == null) selected = schematics.getLoadouts().get((CoreBlock)Blocks.coreShard).first();
cont.add(Core.bundle.format("launch.from", sector.name())).row(); cont.add(Core.bundle.format("launch.from", sector.name())).row();

View File

@@ -66,7 +66,7 @@ public class MapPlayDialog extends BaseDialog{
cont.add(selmode); cont.add(selmode);
cont.row(); cont.row();
cont.button("@customize", Icon.settings, () -> dialog.show(rules, () -> rules = map.applyRules(selectedGamemode))).width(230); cont.button("@customize", Icon.settings, () -> dialog.show(rules, () -> rules = map.applyRules(selectedGamemode))).height(50f).width(230);
cont.row(); cont.row();
cont.add(new BorderImage(map.safeTexture(), 3f)).size(mobile && !Core.graphics.isPortrait() ? 150f : 250f).get().setScaling(Scaling.fit); cont.add(new BorderImage(map.safeTexture(), 3f)).size(mobile && !Core.graphics.isPortrait() ? 150f : 250f).get().setScaling(Scaling.fit);
//only maps with survival are valid for high scores //only maps with survival are valid for high scores

View File

@@ -5,10 +5,13 @@ import arc.Net.*;
import arc.files.*; import arc.files.*;
import arc.func.*; import arc.func.*;
import arc.graphics.*; import arc.graphics.*;
import arc.graphics.Texture.*;
import arc.graphics.g2d.*; import arc.graphics.g2d.*;
import arc.input.*; import arc.input.*;
import arc.scene.style.*; import arc.scene.style.*;
import arc.scene.ui.TextButton.*; import arc.scene.ui.TextButton.*;
import arc.scene.ui.*;
import arc.scene.ui.layout.*;
import arc.struct.*; import arc.struct.*;
import arc.util.*; import arc.util.*;
import arc.util.io.*; import arc.util.io.*;
@@ -16,8 +19,10 @@ import arc.util.serialization.*;
import mindustry.*; import mindustry.*;
import mindustry.core.*; import mindustry.core.*;
import mindustry.ctype.*; import mindustry.ctype.*;
import mindustry.game.EventType.*;
import mindustry.gen.*; import mindustry.gen.*;
import mindustry.graphics.*; import mindustry.graphics.*;
import mindustry.io.*;
import mindustry.mod.*; import mindustry.mod.*;
import mindustry.mod.Mods.*; import mindustry.mod.Mods.*;
import mindustry.ui.*; import mindustry.ui.*;
@@ -29,20 +34,72 @@ import java.util.*;
import static mindustry.Vars.*; import static mindustry.Vars.*;
public class ModsDialog extends BaseDialog{ public class ModsDialog extends BaseDialog{
private ObjectMap<String, TextureRegion> textureCache = new ObjectMap<>();
private String searchtxt = ""; private String searchtxt = "";
private @Nullable Seq<ModListing> modList; private @Nullable Seq<ModListing> modList;
private boolean orderDate = true;
private BaseDialog currentContent;
private BaseDialog browser;
private Table browserTable;
public ModsDialog(){ public ModsDialog(){
super("@mods"); super("@mods");
addCloseButton(); addCloseButton();
Events.on(DisposeEvent.class, e -> {
textureCache.each((key, val) -> {
if(val.texture.width == val.width){
val.texture.dispose();
}
});
textureCache.clear();
});
browser = new BaseDialog("@mods.browser");
browser.cont.table(table -> {
table.left();
table.image(Icon.zoom);
table.field(searchtxt, res -> {
searchtxt = res;
rebuildBrowser();
}).growX().get();
table.button(Icon.list, Styles.clearPartiali, 32f, () -> {
orderDate = !orderDate;
rebuildBrowser();
}).update(b -> b.getStyle().imageUp = (orderDate ? Icon.list : Icon.star)).size(40f).get()
.addListener(new Tooltip(tip -> tip.label(() -> orderDate ? "@mods.browser.sortdate" : "@mods.browser.sortstars").left()));
}).fillX().padBottom(4);
browser.cont.row();
browser.cont.pane(tablebrow -> {
tablebrow.margin(10f).top();
browserTable = tablebrow;
}).get().setScrollingDisabled(true, false);
browser.addCloseButton();
browser.onResize(this::rebuildBrowser);
buttons.button("@mods.guide", Icon.link, () -> Core.app.openURI(modGuideURL)).size(210, 64f); buttons.button("@mods.guide", Icon.link, () -> Core.app.openURI(modGuideURL)).size(210, 64f);
if(!mobile){
buttons.button("@mods.openfolder", Icon.link, () -> Core.app.openFolder(modDirectory.absolutePath()));
}
shown(this::setup); shown(this::setup);
if(mobile){ if(mobile){
onResize(this::setup); onResize(this::setup);
} }
Events.on(ResizeEvent.class, event -> {
if(currentContent != null){
currentContent.hide();
currentContent = null;
}
});
hidden(() -> { hidden(() -> {
if(mods.requiresReload()){ if(mods.requiresReload()){
reload(); reload();
@@ -77,7 +134,7 @@ public class ModsDialog extends BaseDialog{
ui.showErrorMessage(Core.bundle.format("connectfail", status)); ui.showErrorMessage(Core.bundle.format("connectfail", status));
}else{ }else{
try{ try{
modList = new Json().fromJson(Seq.class, ModListing.class, strResult); modList = JsonIO.json.fromJson(Seq.class, ModListing.class, strResult);
var d = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); var d = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
Func<String, Date> parser = text -> { Func<String, Date> parser = text -> {
@@ -97,7 +154,7 @@ public class ModsDialog extends BaseDialog{
} }
}); });
}, error -> Core.app.post(() -> ui.showException(error))); }, error -> Core.app.post(() -> modError(error)));
}else{ }else{
listener.get(modList); listener.get(modList);
} }
@@ -156,81 +213,15 @@ public class ModsDialog extends BaseDialog{
dialog.hide(); dialog.hide();
ui.showTextInput("@mod.import.github", "", 64, Core.settings.getString("lastmod", ""), text -> { ui.showTextInput("@mod.import.github", "", 64, Core.settings.getString("lastmod", ""), text -> {
//clean up the text in case somebody inputs a URL or adds random spaces
text = text.trim().replace(" ", "");
if(text.startsWith("https://github.com/")) text = text.substring("https://github.com/".length());
Core.settings.put("lastmod", text); Core.settings.put("lastmod", text);
//there's no good way to know if it's a java mod here, so assume it's not
ui.loadfrag.show(); githubImportMod(text, false);
githubImportMod(text);
}); });
}).margin(12f); }).margin(12f);
t.row();
t.button("@mod.featured.dialog.title", Icon.star, bstyle, () -> {
Runnable[] rebuildBrowser = {null};
dialog.hide();
BaseDialog browser = new BaseDialog("$mod.featured.dialog.title");
browser.cont.table(table -> {
table.left();
table.image(Icon.zoom);
table.field(searchtxt, res -> {
searchtxt = res;
rebuildBrowser[0].run();
}).growX().get();
}).fillX().padBottom(4);
browser.cont.row();
browser.cont.pane(tablebrow -> {
tablebrow.margin(10f).top();
rebuildBrowser[0] = () -> {
tablebrow.clear();
tablebrow.add("@loading");
getModList(listings -> {
tablebrow.clear();
for(ModListing mod : listings){
if(mod.hasJava || !searchtxt.isEmpty() && !mod.repo.toLowerCase().contains(searchtxt.toLowerCase()) || (Vars.ios && mod.hasScripts)) continue;
tablebrow.button(btn -> {
btn.top().left();
btn.margin(12f);
btn.table(con -> {
con.left();
con.add("[accent]" + mod.name + "[white]\n[lightgray]Author:[] " + mod.author + "\n[lightgray]\uE809 " + mod.stars +
(Version.isAtLeast(mod.minGameVersion) ? "" : "\n" + Core.bundle.format("mod.requiresversion", mod.minGameVersion)))
.width(388f).wrap().growX().pad(0f, 6f, 0f, 6f).left().labelAlign(Align.left);
con.add().growX().pad(0f, 6f, 0f, 6f);
}).fillY().growX().pad(0f, 6f, 0f, 6f);
}, Styles.modsb, () -> {
var sel = new BaseDialog(mod.name);
sel.cont.add(mod.description).width(mobile ? 400f : 500f).wrap().pad(4f).labelAlign(Align.center, Align.left);
sel.buttons.defaults().size(150f, 54f).pad(2f);
sel.setFillParent(false);
sel.buttons.button("@back", Icon.left, () -> {
sel.clear();
sel.hide();
});
sel.buttons.button("@mods.browser.add", Icon.download, () -> {
sel.hide();
githubImportMod(mod.repo);
});
sel.buttons.button("@mods.github.open", Icon.link, () -> {
Core.app.openURI("https://github.com/" + mod.repo);
});
sel.keyDown(KeyCode.escape, sel::hide);
sel.keyDown(KeyCode.back, sel::hide);
sel.show();
}).width(480f).growX().left().fillY();
tablebrow.row();
}
});
};
rebuildBrowser[0].run();
});
browser.addCloseButton();
browser.show();
}).margin(12f);
}); });
dialog.addCloseButton(); dialog.addCloseButton();
@@ -238,9 +229,7 @@ public class ModsDialog extends BaseDialog{
}).margin(margin); }).margin(margin);
if(!mobile){ buttons.button("@mods.browser", Icon.menu, style, this::showModBrowser).margin(margin);
buttons.button("@mods.openfolder", Icon.link, style, () -> Core.app.openFolder(modDirectory.absolutePath())).margin(margin);
}
}).width(w); }).width(w);
cont.row(); cont.row();
@@ -277,7 +266,7 @@ public class ModsDialog extends BaseDialog{
title.table(text -> { title.table(text -> {
boolean hideDisabled = !mod.isSupported() || mod.hasUnmetDependencies() || mod.hasContentErrors(); boolean hideDisabled = !mod.isSupported() || mod.hasUnmetDependencies() || mod.hasContentErrors();
text.add("" + mod.meta.displayName() + "\n[lightgray]v" + mod.meta.version + (mod.enabled() || hideDisabled ? "" : "\n" + Core.bundle.get("mod.disabled") + "")) text.add("" + Strings.stripColors(mod.meta.displayName()) + "\n[lightgray]v" + Strings.stripColors(trimText(mod.meta.version)) + (mod.enabled() || hideDisabled ? "" : "\n" + Core.bundle.get("mod.disabled") + ""))
.wrap().top().width(300f).growX().left(); .wrap().top().width(300f).growX().left();
text.row(); text.row();
@@ -336,7 +325,7 @@ public class ModsDialog extends BaseDialog{
} }
private void reload(){ private void reload(){
ui.showInfo("@mods.reloadexit", () -> Core.app.exit()); ui.showInfoOnHidden("@mods.reloadexit", () -> Core.app.exit());
} }
private void showMod(LoadedMod mod){ private void showMod(LoadedMod mod){
@@ -348,6 +337,13 @@ public class ModsDialog extends BaseDialog{
dialog.buttons.button("@mods.openfolder", Icon.link, () -> Core.app.openFolder(mod.file.absolutePath())); dialog.buttons.button("@mods.openfolder", Icon.link, () -> Core.app.openFolder(mod.file.absolutePath()));
} }
if(mod.getRepo() != null){
boolean showImport = !mod.hasSteamID();
dialog.buttons.button("@mods.github.open", Icon.link, () -> Core.app.openURI("https://github.com/" + mod.getRepo()));
if(mobile && showImport) dialog.buttons.row();
if(showImport) dialog.buttons.button("@mods.browser.reinstall", Icon.download, () -> githubImportMod(mod.getRepo(), mod.isJava()));
}
//TODO improve this menu later //TODO improve this menu later
dialog.cont.pane(desc -> { dialog.cont.pane(desc -> {
desc.center(); desc.center();
@@ -372,12 +368,12 @@ public class ModsDialog extends BaseDialog{
}).width(400f); }).width(400f);
//TODO maybe enable later Seq<UnlockableContent> all = Seq.with(content.getContentMap()).<Content>flatten().select(c -> c.minfo.mod == mod && c instanceof UnlockableContent).as();
if(false){ if(all.any()){
Seq<UnlockableContent> all = Seq.with(content.getContentMap()).<Content>flatten().select(c -> c.minfo.mod == mod && c instanceof UnlockableContent).as(); dialog.cont.row();
if(all.any()){ dialog.cont.button( "@mods.viewcontent", Icon.book, () -> {
dialog.cont.row(); BaseDialog d = new BaseDialog(mod.meta.displayName());
dialog.cont.pane(cs -> { d.cont.pane(cs -> {
int i = 0; int i = 0;
for(UnlockableContent c : all){ for(UnlockableContent c : all){
cs.button(new TextureRegionDrawable(c.icon(Cicon.medium)), Styles.cleari, Cicon.medium.size, () -> { cs.button(new TextureRegionDrawable(c.icon(Cicon.medium)), Styles.cleari, Cicon.medium.size, () -> {
@@ -385,24 +381,145 @@ public class ModsDialog extends BaseDialog{
}).size(50f).with(im -> { }).size(50f).with(im -> {
var click = im.getClickListener(); var click = im.getClickListener();
im.update(() -> im.getImage().color.lerp(!click.isOver() ? Color.lightGray : Color.white, 0.4f * Time.delta)); im.update(() -> im.getImage().color.lerp(!click.isOver() ? Color.lightGray : Color.white, 0.4f * Time.delta));
});
if(++i % 8 == 0) cs.row(); }).tooltip(c.localizedName);
if(++i % Math.min(Core.graphics.getWidth() / Scl.scl(70), 14) == 0) cs.row();
} }
}).growX().minHeight(60f); }).grow();
} d.addCloseButton();
d.show();
currentContent = d;
}).size(300, 50).pad(4);
} }
dialog.show(); dialog.show();
} }
private void showModBrowser(){
rebuildBrowser();
browser.show();
}
private void rebuildBrowser(){
ObjectSet<String> installed = mods.list().map(m -> m.getRepo()).asSet();
browserTable.clear();
browserTable.add("@loading");
int cols = (int)Math.max(Core.graphics.getWidth() / Scl.scl(480), 1);
getModList(rlistings -> {
browserTable.clear();
int i = 0;
Seq<ModListing> listings = rlistings;
if(!orderDate){
listings = rlistings.copy();
listings.sortComparing(m1 -> -m1.stars);
}
for(ModListing mod : listings){
if((mod.hasJava && Vars.ios) || !searchtxt.isEmpty() && !mod.repo.toLowerCase().contains(searchtxt.toLowerCase()) || (Vars.ios && mod.hasScripts)) continue;
float s = 64f;
browserTable.button(con -> {
con.margin(0f);
con.left();
String repo = mod.repo;
con.add(new BorderImage(){
TextureRegion last;
{
border(installed.contains(repo) ? Pal.accent : Color.lightGray);
setDrawable(Tex.nomap);
pad = Scl.scl(4f);
}
@Override
public void draw(){
super.draw();
//textures are only requested when the rendering happens; this assists with culling
if(!textureCache.containsKey(repo)){
textureCache.put(repo, last = Tex.nomap.getRegion());
Core.net.httpGet("https://raw.githubusercontent.com/Anuken/MindustryMods/master/icons/" + repo.replace("/", "_"), res -> {
if(res.getStatus() == HttpStatus.OK){
Pixmap pix = new Pixmap(res.getResult());
Core.app.post(() -> {
try{
var tex = new Texture(pix);
tex.setFilter(TextureFilter.linear);
textureCache.put(repo, new TextureRegion(tex));
pix.dispose();
}catch(Exception e){
Log.err(e);
}
});
}
}, err -> {});
}
var next = textureCache.get(repo);
if(last != next){
last = next;
setDrawable(next);
}
}
}).size(s).pad(4f * 2f);
con.add(
"[accent]" + mod.name.replace("\n", "") +
(installed.contains(mod.repo) ? "\n[lightgray]" + Core.bundle.get("mod.installed") : "") +
//"[white]\n[lightgray]Author:[] " + trimText(mod.author) +
"\n[lightgray]\uE809 " + mod.stars +
(Version.isAtLeast(mod.minGameVersion) ? "" : "\n" + Core.bundle.format("mod.requiresversion", mod.minGameVersion)))
.width(358f).wrap().grow().pad(4f, 2f, 4f, 6f).top().left().labelAlign(Align.topLeft);
}, Styles.clearPartialt, () -> {
var sel = new BaseDialog(mod.name);
sel.cont.pane(p -> p.add(mod.description + "\n\n[accent]" + Core.bundle.get("editor.author") + "[lightgray] " + mod.author)
.width(mobile ? 400f : 500f).wrap().pad(4f).labelAlign(Align.center, Align.left)).grow();
sel.buttons.defaults().size(150f, 54f).pad(2f);
sel.buttons.button("@back", Icon.left, () -> {
sel.clear();
sel.hide();
});
var found = mods.list().find(l -> mod.repo != null && mod.repo.equals(l.getRepo()));
sel.buttons.button(found == null ? "@mods.browser.add" : "@mods.browser.reinstall", Icon.download, () -> {
sel.hide();
githubImportMod(mod.repo, mod.hasJava);
});
sel.buttons.button("@mods.github.open", Icon.link, () -> {
Core.app.openURI("https://github.com/" + mod.repo);
});
sel.keyDown(KeyCode.escape, sel::hide);
sel.keyDown(KeyCode.back, sel::hide);
sel.show();
}).width(438f).pad(4).growX().left().height(s + 8*2f).fillY();
if(++i % cols == 0) browserTable.row();
}
});
}
private String trimText(String text){
if(text == null) return "";
if(text.contains("\n")){
return text.substring(0, text.indexOf("\n"));
}
return text;
}
private void handleMod(String repo, HttpResponse result){ private void handleMod(String repo, HttpResponse result){
try{ try{
Fi file = tmpDirectory.child(repo.replace("/", "") + ".zip"); Fi file = tmpDirectory.child(repo.replace("/", "") + ".zip");
Streams.copy(result.getResultAsStream(), file.write(false)); Streams.copy(result.getResultAsStream(), file.write(false));
mods.importMod(file); var mod = mods.importMod(file);
mod.setRepo(repo);
file.delete(); file.delete();
Core.app.post(() -> { Core.app.post(() -> {
try{ try{
@@ -417,21 +534,80 @@ public class ModsDialog extends BaseDialog{
} }
} }
private void githubImportMod(String name){ private void importFail(Throwable t){
//try several branches Core.app.post(() -> modError(t));
//TODO use only the main branch as specified in meta }
githubImportBranch("6.0", name, e1 -> {
githubImportBranch("master", name, e2 -> { private void githubImportMod(String repo, boolean isJava){
githubImportBranch("main", name, e3 -> { if(isJava){
ui.showErrorMessage(Core.bundle.format("connectfail", e2)); ui.showConfirm("@warning", "@mod.jarwarn", () -> {
ui.loadfrag.hide(); ui.loadfrag.show();
}); githubImportJavaMod(repo);
}); });
}else{
ui.loadfrag.show();
Core.net.httpGet(ghApi + "/repos/" + repo, res -> {
if(checkError(res)){
var json = Jval.read(res.getResultAsString());
String mainBranch = json.getString("default_branch");
String language = json.getString("language", "<none>");
//this is a crude heuristic for class mods; only required for direct github import
//TODO make a more reliable way to distinguish java mod repos
if(language.equals("Java") || language.equals("Kotlin")){
githubImportJavaMod(repo);
}else{
githubImportBranch(mainBranch, repo, this::showStatus);
}
}
}, this::importFail);
}
}
private void githubImportJavaMod(String repo){
//grab latest release
Core.net.httpGet(ghApi + "/repos/" + repo + "/releases/latest", res -> {
if(checkError(res)){
var json = Jval.read(res.getResultAsString());
var assets = json.get("assets").asArray();
//prioritize dexed jar, as that's what Sonnicon's mod template outputs
var dexedAsset = assets.find(j -> j.getString("name").startsWith("dexed") && j.getString("name").endsWith(".jar"));
var asset = dexedAsset == null ? assets.find(j -> j.getString("name").endsWith(".jar")) : dexedAsset;
if(asset != null){
//grab actual file
var url = asset.getString("browser_download_url");
Core.net.httpGet(url, result -> {
if(checkError(result)){
handleMod(repo, result);
}
}, this::importFail);
}else{
throw new ArcRuntimeException("No JAR file found in releases. Make sure you have a valid jar file in the mod's latest Github Release.");
}
}
}, this::importFail);
}
private boolean checkError(HttpResponse res){
if(res.getStatus() == HttpStatus.OK){
return true;
}else{
showStatus(res.getStatus());
return false;
}
}
private void showStatus(HttpStatus status){
Core.app.post(() -> {
ui.showErrorMessage(Core.bundle.format("connectfail", Strings.capitalize(status.toString().toLowerCase())));
ui.loadfrag.hide();
}); });
} }
private void githubImportBranch(String branch, String repo, Cons<HttpStatus> err){ private void githubImportBranch(String branch, String repo, Cons<HttpStatus> err){
Core.net.httpGet("https://api.github.com/repos/" + repo + "/zipball/" + branch, loc -> { Core.net.httpGet(ghApi + "/repos/" + repo + "/zipball/" + branch, loc -> {
if(loc.getStatus() == HttpStatus.OK){ if(loc.getStatus() == HttpStatus.OK){
if(loc.getHeader("Location") != null){ if(loc.getHeader("Location") != null){
Core.net.httpGet(loc.getHeader("Location"), result -> { Core.net.httpGet(loc.getHeader("Location"), result -> {
@@ -440,13 +616,13 @@ public class ModsDialog extends BaseDialog{
}else{ }else{
handleMod(repo, result); handleMod(repo, result);
} }
}, t2 -> Core.app.post(() -> modError(t2))); }, this::importFail);
}else{ }else{
handleMod(repo, loc); handleMod(repo, loc);
} }
}else{ }else{
err.get(loc.getStatus()); err.get(loc.getStatus());
} }
}, t2 -> Core.app.post(() -> modError(t2))); }, this::importFail);
} }
} }

View File

@@ -409,11 +409,11 @@ public class SettingsMenuDialog extends SettingsDialog{
graphics.checkPref("animatedshields", !mobile); graphics.checkPref("animatedshields", !mobile);
} }
if(!ios){ //if(!ios){
graphics.checkPref("bloom", true, val -> renderer.toggleBloom(val)); graphics.checkPref("bloom", true, val -> renderer.toggleBloom(val));
}else{ //}else{
Core.settings.put("bloom", false); // Core.settings.put("bloom", false);
} //}
graphics.checkPref("pixelate", false, val -> { graphics.checkPref("pixelate", false, val -> {
if(val){ if(val){

View File

@@ -67,7 +67,7 @@ public class PlayerListFragment extends Fragment{
menu.button("@close", this::toggle); menu.button("@close", this::toggle);
}).margin(0f).pad(10f).growX(); }).margin(0f).pad(10f).growX();
}).touchable(Touchable.enabled).margin(14f); }).touchable(Touchable.enabled).margin(14f).minWidth(360f);
}); });
rebuild(); rebuild();

View File

@@ -145,6 +145,8 @@ public class Block extends UnlockableContent{
public boolean conveyorPlacement; public boolean conveyorPlacement;
/** Whether to swap the diagonal placement modes. */ /** Whether to swap the diagonal placement modes. */
public boolean swapDiagonalPlacement; public boolean swapDiagonalPlacement;
/** Build queue priority in schematics. */
public int schematicPriority = 0;
/** /**
* The color of this block when displayed on the minimap or map preview. * The color of this block when displayed on the minimap or map preview.
* Do not set manually! This is overridden when loading for most blocks. * Do not set manually! This is overridden when loading for most blocks.
@@ -426,6 +428,12 @@ public class Block extends UnlockableContent{
TextureRegion reg = getRequestRegion(req, list); TextureRegion reg = getRequestRegion(req, list);
Draw.rect(reg, req.drawx(), req.drawy(), !rotate ? 0 : req.rotation * 90); Draw.rect(reg, req.drawx(), req.drawy(), !rotate ? 0 : req.rotation * 90);
if(req.worldContext && player != null && teamRegion != null && teamRegion.found()){
if(teamRegions[player.team().id] == teamRegion) Draw.color(player.team().color);
Draw.rect(teamRegions[player.team().id], req.drawx(), req.drawy());
Draw.color();
}
drawRequestConfig(req, list); drawRequestConfig(req, list);
} }

View File

@@ -117,6 +117,8 @@ public class ForceProjector extends Block{
@Override @Override
public void onRemoved(){ public void onRemoved(){
float radius = realRadius();
if(!broken && radius > 1f) Fx.forceShrink.at(x, y, radius, team.color);
super.onRemoved(); super.onRemoved();
drawer.remove(); drawer.remove();
} }

View File

@@ -78,6 +78,7 @@ public class Turret extends ReloadTurret{
public @Load(value = "@-base", fallback = "block-@size") TextureRegion baseRegion; public @Load(value = "@-base", fallback = "block-@size") TextureRegion baseRegion;
public @Load("@-heat") TextureRegion heatRegion; public @Load("@-heat") TextureRegion heatRegion;
public float elevation = -1f;
public Cons<TurretBuild> drawer = tile -> Draw.rect(region, tile.x + tr2.x, tile.y + tr2.y, tile.rotation - 90); public Cons<TurretBuild> drawer = tile -> Draw.rect(region, tile.x + tr2.x, tile.y + tr2.y, tile.rotation - 90);
public Cons<TurretBuild> heatDrawer = tile -> { public Cons<TurretBuild> heatDrawer = tile -> {
@@ -111,7 +112,7 @@ public class Turret extends ReloadTurret{
super.setStats(); super.setStats();
stats.add(Stat.inaccuracy, (int)inaccuracy, StatUnit.degrees); stats.add(Stat.inaccuracy, (int)inaccuracy, StatUnit.degrees);
stats.add(Stat.reload, 60f / reloadTime * (alternate ? 1 : shots), StatUnit.none); stats.add(Stat.reload, 60f / (reloadTime + 1) * (alternate ? 1 : shots), StatUnit.none);
stats.add(Stat.targetsAir, targetAir); stats.add(Stat.targetsAir, targetAir);
stats.add(Stat.targetsGround, targetGround); stats.add(Stat.targetsGround, targetGround);
if(ammoPerShot != 1) stats.add(Stat.ammoUse, ammoPerShot, StatUnit.perShot); if(ammoPerShot != 1) stats.add(Stat.ammoUse, ammoPerShot, StatUnit.perShot);
@@ -125,6 +126,7 @@ public class Turret extends ReloadTurret{
} }
if(shootLength < 0) shootLength = size * tilesize / 2f; if(shootLength < 0) shootLength = size * tilesize / 2f;
if(elevation < 0) elevation = size / 2f;
super.init(); super.init();
} }
@@ -234,7 +236,7 @@ public class Turret extends ReloadTurret{
tr2.trns(rotation, -recoil); tr2.trns(rotation, -recoil);
Drawf.shadow(region, x + tr2.x - (size / 2f), y + tr2.y - (size / 2f), rotation - 90); Drawf.shadow(region, x + tr2.x - elevation, y + tr2.y - elevation, rotation - 90);
drawer.get(this); drawer.get(this);
if(heatRegion != Core.atlas.find("error")){ if(heatRegion != Core.atlas.find("error")){

View File

@@ -83,7 +83,7 @@ public class Conveyor extends Block implements Autotiler{
@Override @Override
public Block getReplacement(BuildPlan req, Seq<BuildPlan> requests){ public Block getReplacement(BuildPlan req, Seq<BuildPlan> requests){
Boolf<Point2> cont = p -> requests.contains(o -> o.x == req.x + p.x && o.y == req.y + p.y && o.rotation == req.rotation && (req.block instanceof Conveyor || req.block instanceof Junction)); Boolf<Point2> cont = p -> requests.contains(o -> o.x == req.x + p.x && o.y == req.y + p.y && (req.block instanceof Conveyor || req.block instanceof Junction));
return cont.get(Geometry.d4(req.rotation)) && return cont.get(Geometry.d4(req.rotation)) &&
cont.get(Geometry.d4(req.rotation - 2)) && cont.get(Geometry.d4(req.rotation - 2)) &&
req.tile() != null && req.tile() != null &&

View File

@@ -217,11 +217,9 @@ public class PayloadConveyor extends Block{
@Override @Override
public boolean acceptPayload(Building source, Payload payload){ public boolean acceptPayload(Building source, Payload payload){
if(source == this){ return this.item == null
return this.item == null && payload.fits(payloadLimit); && payload.fits(payloadLimit)
} && (source == this || this.enabled && progress <= 5f);
//accepting payloads from units isn't supported
return this.item == null && progress <= 5f && payload.fits(payloadLimit);
} }
@Override @Override

View File

@@ -109,6 +109,8 @@ public class StackConveyor extends Block implements Autotiler{
@Override @Override
public void draw(){ public void draw(){
Draw.z(Layer.block - 0.2f);
Draw.rect(regions[state], x, y, rotdeg()); Draw.rect(regions[state], x, y, rotdeg());
for(int i = 0; i < 4; i++){ for(int i = 0; i < 4; i++){
@@ -117,7 +119,7 @@ public class StackConveyor extends Block implements Autotiler{
} }
} }
Draw.z(Layer.blockOver); Draw.z(Layer.block - 0.1f);
Tile from = world.tile(link); Tile from = world.tile(link);
@@ -145,6 +147,12 @@ public class StackConveyor extends Block implements Autotiler{
Draw.rect(lastItem.icon(Cicon.medium), Tmp.v1.x, Tmp.v1.y, size, size, 0); Draw.rect(lastItem.icon(Cicon.medium), Tmp.v1.x, Tmp.v1.y, size, size, 0);
} }
@Override
public void drawCracks(){
Draw.z(Layer.block - 0.15f);
super.drawCracks();
}
@Override @Override
public void onProximityUpdate(){ public void onProximityUpdate(){
super.onProximityUpdate(); super.onProximityUpdate();

View File

@@ -29,7 +29,7 @@ public class LiquidBridge extends ItemBridge{
Building other = world.build(link); Building other = world.build(link);
if(other == null || !linkValid(tile, other.tile())){ if(other == null || !linkValid(tile, other.tile())){
dumpLiquid(liquids.current()); dumpLiquid(liquids.current(), 1f);
}else{ }else{
((ItemBridgeBuild)other).incoming.add(tile.pos()); ((ItemBridgeBuild)other).incoming.add(tile.pos());

View File

@@ -28,7 +28,7 @@ public class LiquidExtendingBridge extends ExtendingItemBridge{
Building other = world.build(link); Building other = world.build(link);
if(other == null || !linkValid(tile, other.tile())){ if(other == null || !linkValid(tile, other.tile())){
dumpLiquid(liquids.current()); dumpLiquid(liquids.current(), 1f);
}else{ }else{
((ItemBridgeBuild)other).incoming.add(tile.pos()); ((ItemBridgeBuild)other).incoming.add(tile.pos());

View File

@@ -64,7 +64,7 @@ public class LogicBlock extends Block{
entity.links.add(out); entity.links.add(out);
} }
entity.updateCode(); entity.updateCode(entity.code, true, null);
}); });
} }
@@ -277,15 +277,11 @@ public class LogicBlock extends Block{
return bname + outnum; return bname + outnum;
} }
public void updateCode(){
updateCode(code);
}
public void updateCode(String str){ public void updateCode(String str){
updateCodeVars(str, null); updateCode(str, false, null);
} }
public void updateCodeVars(String str, Cons<LAssembler> assemble){ public void updateCode(String str, boolean keep, Cons<LAssembler> assemble){
if(str != null){ if(str != null){
code = str; code = str;
@@ -317,6 +313,19 @@ public class LogicBlock extends Block{
asm.putConst("@links", executor.links.length); asm.putConst("@links", executor.links.length);
asm.putConst("@ipt", instructionsPerTick); asm.putConst("@ipt", instructionsPerTick);
if(keep){
//store any older variables
for(Var var : executor.vars){
boolean unit = var.name.equals("@unit");
if(!var.constant || unit){
BVar dest = asm.getVar(var.name);
if(dest != null && (!dest.constant || unit)){
dest.value = var.isobj ? var.objval : var.numval;
}
}
}
}
//inject any extra variables //inject any extra variables
if(assemble != null){ if(assemble != null){
assemble.get(asm); assemble.get(asm);
@@ -397,7 +406,7 @@ public class LogicBlock extends Block{
} }
if(changed){ if(changed){
updateCode(); updateCode(code, true, null);
} }
if(enabled){ if(enabled){
@@ -555,7 +564,7 @@ public class LogicBlock extends Block{
//skip memory, it isn't used anymore //skip memory, it isn't used anymore
read.skip(memory * 8); read.skip(memory * 8);
updateCodeVars(code, asm -> { updateCode(code, false, asm -> {
//load up the variables that were stored //load up the variables that were stored
for(int i = 0; i < varcount; i++){ for(int i = 0; i < varcount; i++){

View File

@@ -12,6 +12,7 @@ import mindustry.entities.*;
import mindustry.game.EventType.*; import mindustry.game.EventType.*;
import mindustry.gen.*; import mindustry.gen.*;
import mindustry.graphics.*; import mindustry.graphics.*;
import mindustry.logic.*;
import mindustry.ui.*; import mindustry.ui.*;
import mindustry.world.meta.*; import mindustry.world.meta.*;
@@ -122,6 +123,12 @@ public class ImpactReactor extends PowerGenerator{
Drawf.light(team, x, y, (110f + Mathf.absin(5, 5f)) * warmup, Tmp.c1.set(plasma2).lerp(plasma1, Mathf.absin(7f, 0.2f)), 0.8f * warmup); Drawf.light(team, x, y, (110f + Mathf.absin(5, 5f)) * warmup, Tmp.c1.set(plasma2).lerp(plasma1, Mathf.absin(7f, 0.2f)), 0.8f * warmup);
} }
@Override
public double sense(LAccess sensor){
if(sensor == LAccess.heat) return warmup;
return super.sense(sensor);
}
@Override @Override
public void onDestroyed(){ public void onDestroyed(){
super.onDestroyed(); super.onDestroyed();

View File

@@ -9,6 +9,7 @@ import arc.util.io.*;
import mindustry.annotations.Annotations.*; import mindustry.annotations.Annotations.*;
import mindustry.gen.*; import mindustry.gen.*;
import mindustry.graphics.*; import mindustry.graphics.*;
import mindustry.logic.*;
import mindustry.world.*; import mindustry.world.*;
import static mindustry.Vars.*; import static mindustry.Vars.*;
@@ -32,6 +33,15 @@ public class LightBlock extends Block{
public int color = Pal.accent.rgba(); public int color = Pal.accent.rgba();
public float smoothTime = 1f; public float smoothTime = 1f;
@Override
public void control(LAccess type, double p1, double p2, double p3, double p4){
if(type == LAccess.color){
color = Color.rgba8888((float)p1, (float)p2, (float)p3, 1f);
}
super.control(type, p1, p2, p3, p4);
}
@Override @Override
public void draw(){ public void draw(){
super.draw(); super.draw();

View File

@@ -22,6 +22,7 @@ public class PowerDiode extends Block{
insulated = true; insulated = true;
group = BlockGroup.power; group = BlockGroup.power;
noUpdateDisabled = true; noUpdateDisabled = true;
schematicPriority = 10;
} }
@Override @Override

View File

@@ -224,7 +224,7 @@ public class PowerGraph{
lastPowerNeeded = powerNeeded; lastPowerNeeded = powerNeeded;
lastPowerProduced = powerProduced; lastPowerProduced = powerProduced;
lastScaledPowerIn = powerProduced / Time.delta; lastScaledPowerIn = (powerProduced + energyDelta) / Time.delta;
lastScaledPowerOut = powerNeeded / Time.delta; lastScaledPowerOut = powerNeeded / Time.delta;
lastCapacity = getTotalBatteryCapacity(); lastCapacity = getTotalBatteryCapacity();
lastPowerStored = getBatteryStored(); lastPowerStored = getBatteryStored();
@@ -249,6 +249,8 @@ public class PowerGraph{
} }
public void addGraph(PowerGraph graph){ public void addGraph(PowerGraph graph){
if(graph == this) return;
for(Building tile : graph.all){ for(Building tile : graph.all){
add(tile); add(tile);
} }
@@ -283,25 +285,24 @@ public class PowerGraph{
Building child = queue.removeFirst(); Building child = queue.removeFirst();
add(child); add(child);
for(Building next : child.getPowerConnections(outArray2)){ for(Building next : child.getPowerConnections(outArray2)){
if(!closedSet.contains(next.pos())){ if(closedSet.add(next.pos())){
queue.addLast(next); queue.addLast(next);
closedSet.add(next.pos());
} }
} }
} }
} }
private void removeSingle(Building tile){ /** Used for unit tests only. */
all.remove(tile, true); public void removeList(Building build){
producers.remove(tile, true); all.remove(build);
consumers.remove(tile, true); producers.remove(build);
batteries.remove(tile, true); consumers.remove(build);
batteries.remove(build);
} }
/** Note that this does not actually remove the building from the graph;
* it creates *new* graphs that contain the correct buildings. */
public void remove(Building tile){ public void remove(Building tile){
removeSingle(tile);
//begin by clearing the closed set
closedSet.clear();
//go through all the connections of this tile //go through all the connections of this tile
for(Building other : tile.getPowerConnections(outArray1)){ for(Building other : tile.getPowerConnections(outArray1)){
@@ -317,17 +318,15 @@ public class PowerGraph{
while(queue.size > 0){ while(queue.size > 0){
//get child from queue //get child from queue
Building child = queue.removeFirst(); Building child = queue.removeFirst();
//remove it from this graph
removeSingle(child);
//add it to the new branch graph //add it to the new branch graph
graph.add(child); graph.add(child);
//go through connections //go through connections
for(Building next : child.getPowerConnections(outArray2)){ for(Building next : child.getPowerConnections(outArray2)){
//make sure it hasn't looped back, and that the new graph being assigned hasn't already been assigned //make sure it hasn't looped back, and that the new graph being assigned hasn't already been assigned
//also skip closed tiles //also skip closed tiles
if(next != tile && next.power.graph != graph && !closedSet.contains(next.pos())){ if(next != tile && next.power.graph != graph){
graph.add(next);
queue.addLast(next); queue.addLast(next);
closedSet.add(next.pos());
} }
} }
} }

View File

@@ -43,6 +43,7 @@ public class PowerNode extends PowerBlock{
outputsPower = false; outputsPower = false;
canOverdrive = false; canOverdrive = false;
swapDiagonalPlacement = true; swapDiagonalPlacement = true;
schematicPriority = -10;
drawDisabled = false; drawDisabled = false;
config(Integer.class, (entity, value) -> { config(Integer.class, (entity, value) -> {
@@ -221,12 +222,15 @@ public class PowerNode extends PowerBlock{
@Override @Override
public void drawRequestConfigTop(BuildPlan req, Eachable<BuildPlan> list){ public void drawRequestConfigTop(BuildPlan req, Eachable<BuildPlan> list){
if(req.config instanceof Point2[]){ if(req.config instanceof Point2[] ps){
setupColor(1f); setupColor(1f);
for(Point2 point : (Point2[])req.config){ for(Point2 point : ps){
int px = req.x + point.x, py = req.y + point.y;
otherReq = null; otherReq = null;
list.each(other -> { list.each(other -> {
if((other.x == req.x + point.x && other.y == req.y + point.y) && other != req){ if(other.block != null
&& (px >= other.x - ((other.block.size-1)/2) && py >= other.y - ((other.block.size-1)/2) && px <= other.x + other.block.size/2 && py <= other.y + other.block.size/2)
&& other != req && other.block.hasPower){
otherReq = other; otherReq = other;
} }
}); });

View File

@@ -10,7 +10,7 @@ public class SolarGenerator extends PowerGenerator{
public SolarGenerator(String name){ public SolarGenerator(String name){
super(name); super(name);
//remove the BlockFlag.producer flag to make this a lower priority target than other generators. //remove the BlockFlag.generator flag to make this a lower priority target than other generators.
flags = EnumSet.of(); flags = EnumSet.of();
} }

View File

@@ -106,9 +106,10 @@ public class Reconstructor extends UnitBlock{
@Override @Override
public boolean acceptPayload(Building source, Payload payload){ public boolean acceptPayload(Building source, Payload payload){
return this.payload == null return this.payload == null
&& (this.enabled || source == this)
&& relativeTo(source) != rotation && relativeTo(source) != rotation
&& payload instanceof UnitPayload && payload instanceof UnitPayload pay
&& hasUpgrade(((UnitPayload)payload).unit.type); && hasUpgrade(pay.unit.type);
} }
@Override @Override

View File

@@ -6,6 +6,7 @@ import arc.math.*;
import arc.math.geom.*; import arc.math.geom.*;
import arc.struct.*; import arc.struct.*;
import arc.util.*; import arc.util.*;
import arc.util.io.*;
import mindustry.annotations.Annotations.*; import mindustry.annotations.Annotations.*;
import mindustry.entities.*; import mindustry.entities.*;
import mindustry.gen.*; import mindustry.gen.*;
@@ -123,5 +124,26 @@ public class RepairPoint extends Block{
public BlockStatus status(){ public BlockStatus status(){
return Mathf.equal(efficiency(), 0f, 0.01f) ? BlockStatus.noInput : cons.status(); return Mathf.equal(efficiency(), 0f, 0.01f) ? BlockStatus.noInput : cons.status();
} }
@Override
public void write(Writes write){
super.write(write);
write.f(rotation);
}
@Override
public void read(Reads read, byte revision){
super.read(read, revision);
if(revision >= 1){
rotation = read.f();
}
}
@Override
public byte version(){
return 1;
}
} }
} }

View File

@@ -100,6 +100,7 @@ public class UnitFactory extends UnitBlock{
if(plan.unit.unlockedNow()){ if(plan.unit.unlockedNow()){
table.image(plan.unit.icon(Cicon.small)).size(8 * 3).padRight(2).right(); table.image(plan.unit.icon(Cicon.small)).size(8 * 3).padRight(2).right();
table.add(plan.unit.localizedName).left(); table.add(plan.unit.localizedName).left();
table.add(Strings.autoFixed(plan.time / 60f, 1) + " " + Core.bundle.get("unit.seconds")).color(Color.lightGray).padLeft(12).left();
table.row(); table.row();
} }
} }

View File

@@ -41,7 +41,7 @@ public class ConsumeLiquid extends ConsumeLiquidBase{
@Override @Override
public boolean valid(Building entity){ public boolean valid(Building entity){
return entity != null && entity.liquids != null && entity.liquids.get(liquid) >= use(entity); return entity != null && entity.liquids != null && entity.liquids.get(liquid) >= amount * entity.edelta();
} }
@Override @Override

View File

@@ -7,6 +7,7 @@ import java.util.*;
/** Describes one type of stat for content. */ /** Describes one type of stat for content. */
public enum Stat{ public enum Stat{
health, health,
armor,
size, size,
displaySize, displaySize,
buildTime, buildTime,
@@ -15,6 +16,7 @@ public enum Stat{
explosiveness, explosiveness,
flammability, flammability,
radioactivity, radioactivity,
charge,
heatCapacity, heatCapacity,
viscosity, viscosity,
temperature, temperature,

View File

@@ -109,7 +109,7 @@ public class DesktopLauncher extends ClientLauncher{
steamError = e; steamError = e;
loadError = true; loadError = true;
Log.err(e); Log.err(e);
try(OutputStream s = new FileOutputStream(new File("steam-error-log-" + System.nanoTime() + ".txt"))){ try(OutputStream s = new FileOutputStream("steam-error-log-" + System.nanoTime() + ".txt")){
String log = Strings.neatError(e); String log = Strings.neatError(e);
s.write(log.getBytes()); s.write(log.getBytes());
}catch(Exception e2){ }catch(Exception e2){

View File

@@ -51,6 +51,9 @@ public class SStats implements SteamUserStatsCallback{
stats.storeStats(); stats.storeStats();
} }
}, statSavePeriod * 60, statSavePeriod * 60); }, statSavePeriod * 60, statSavePeriod * 60);
if(Items.thorium.unlocked()) obtainThorium.complete();
if(Items.titanium.unlocked()) obtainTitanium.complete();
}); });
} }

View File

@@ -0,0 +1,9 @@
[This is a truncated changelog, see Github for full notes]
As 6.0 is more or less stable now, updates have slowed down significantly. This is likely to be one of the last notable releases before 7.0.
- Fixed same-line logic comments not parsing
- Fixed mass driver rotating before payload is received
- Fixed bullets dealing splash damage twice upon collision - splash damage of certain bullets has been increased to compensate

View File

@@ -0,0 +1,6 @@
[This is a truncated changelog, see Github for full notes]
- Fixed negative numbers not displaying properly in power nodes
- Fixed mod minVersion parsing
- Fixed mods disappearing after import [Android]
- Disabled duplicate IP prevention system
- Added mod browser sort order button

View File

@@ -0,0 +1,8 @@
[This is a truncated changelog, see Github for full notes]
- Fixed logic blocks resetting state on map load or connection changes
- Fixed "#" in any logic string breaking instructions
- Fixed crawlers not exploding next to cores
- Fixed certain status effect combinations not triggering damage
- Fixed waves being infinitely skippable when game is paused
- Fixed constructed blocks sometimes having low health on servers'
- Fixed enemy core items not syncing

View File

@@ -0,0 +1,52 @@
Crează benzi elaborate de aprovizionare pt a-ți alimenta armele cu muniție, produ materiale de construcție și apără-ți baza de inamici. Joacă cu prietenii pe multiplayer în co-op sau provoacă-i la un meci PvP.
<h2>Funcțiile Jocului</h2>
- Construiește clădiri pt a fabrica o mare varietate de materiale avansate
- Apara-ți baza de inamici
- Joacă cu prietenii pe multiplayer în co-op sau provoacă-i la un meci PvP
- Distribuie lichide și luptă împotriva amenințărilor constante, cum ar fi incendiile devastatoare sau raidurile inamice
- Eficientizează-ți producția folosind lichid de răcire și lubrifiante
- Produ o variatate de unități militare pt managementul automat al bazei sau atacarea bazelor inamice
<h2>Campanie</h2>
- Capturează planeta Serpulo avansând prin peste 250 de sectoare genereate procedural și 16 harți create manual
- Capturează teritoriul și construiește fabrici pt producția de resurse în timp ce joci în alte sectoare
- Apără-ți sectoarele de invaziile inamice periodice
- Coordonează distribuția de resurse între sectoare folosind platforme de lansare
- Cercetează noi fabrici pt a-ți crește progresul tehnologic
- Invită-ți prietenii pt a duce misiuni la bun sfârșit împreună
- Peste 130 de structuri precum fabrici și arme de cercetat și stăpânit
- 33 de tipuri diferite de drone, roboți și nave
<h2>Moduri de Joc</h2>
- <b>Supraviețuire</b>: Construiește arme pt a te apăra de inamici în stil tower defense. Rezistă cât de mult se poate. Îți poți lansa nucelul pt a folosi resursele adunate pt cercetare. Pregătește-ți baza pt a supraviețui atacurilor intermitente ale gardienilor aerieni.
- <b>Atac</b>: Construiește fabrici de unități pt a distruge nucleele inamice, în timp ce îți aperi baza de valurile de inamici. Crează o variatate de tipuri de unități ofensive și de suport pt a te ajuta să-ți atingi obiectivul. Poți activa inteligența artificială a inamicilor, iar ei vor construi structuri pt a se apăra împotriva ta, oferind o extra provocare.
- <b>PvP</b>: Concurează cu alți jucători din până la 4 echipe și câștigă distrugându-le nucleele. Construiește unități sau atacă direct alte baze.
- <b>Sandbox</b>: Joacă folosind resurse infinite, fără niciun risc din partea inamicilor. Folosește blocuri sursă de lichid și material specifice modului sandbox pt a testa designuri și cheamă inamicul când vrei.
<h2>Jocuri Personalizate și Multiplayer Între Platforme</h2>
- 16 hărți create manual pt jocuri personalizate, în plus față de campanie
- Joacă co-op, PvP sau sandbox
- Alătură-te unui server public dedicat, sau invită-ți prietenii pt propria sesiune privată
- Reguli de joc personalizabile: Schimbă costul blocurilor, puterea inamicilor, resursele inițiale, timpul dintre valuri și altele
- Moduri de joc complet configurabile: Combină modurile PvP și PvE
<h2>Editorul de Hărți</h2>
- Modifică terenul folosind interfața editorului
- Editează și vizualizează structurile în joc
- Moduri de unealtă configurabile: Schimbă modul cum funcționează fiecare unealtă
- Un sistem robust de generare a hărților, cu multe tipuri diferite de filtre pt manipularea procedurală a terenului
- Aplică zgomot vizual, distorsionare, netezire, eroziune, simetrie, minereuri generate și teren aleatoriu pt hărțile tale
- Configurează atât generarea aleatorie a minereurilor cât și poziția râurilor și a zonelor cu resurse
- Configurează structura valurilor inamice
- Personalizează regulile de bază ale hărții
- Folosește peste 80 de blocuri de teren diferite

View File

@@ -0,0 +1 @@
Un joc tower defense bazat pe industrie.

View File

@@ -0,0 +1 @@
Un joc tower defense bazat pe industrie.

Some files were not shown because too many files have changed in this diff Show More