diff --git a/.gitignore b/.gitignore index 296fbf66f7..00ff121460 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,8 @@ -##Packr, build stuff - +logs/ /core/assets/mindustry-saves/ /core/assets/mindustry-maps/ +/core/assets/bundles/output/ +/core/assets/.gifimages/ /deploy/ /desktop/packr-out/ /desktop/packr-export/ @@ -9,13 +10,26 @@ /desktop/mindustry-maps/ /desktop/gifexport/ /core/lib/ +/core/assets-raw/sprites/generated/ +/core/assets-raw/sprites_out/ /annotations/build/ -/kryonet/build/ +/annotations/out/ +/net/build/ +/tools/build/ +/tests/build/ /server/build/ +/test_files/ +/annotations/build/ /android/assets/mindustry-maps/ /android/assets/mindustry-saves/ /core/assets/gifexport/ /core/assets/version.properties +/core/assets/locales +/ios/src/io/anuke/mindustry/gen/ +/core/src/io/anuke/mindustry/gen/ +ios/robovm.properties +packr-out/ +config/ *.gif version.properties diff --git a/.travis.yml b/.travis.yml index 06a66fa107..4db5c34b9c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,23 +1,31 @@ -language: android - jdk: - - openjdk8 - -android: - components: - - android-26 - - # Additional components - - extra-google-google_play_services - - extra-google-m2repository - - extra-android-m2repository - - addon-google_apis-google-26 - -script: - - ./gradlew desktop:dist - -after_success: - - chmod +x upload-build.sh - - chmod +x cleanup_builds.sh - - ./upload-build.sh - +- openjdk8 +script: +- git clone --depth=1 --branch=master https://github.com/Anuken/Arc ../Arc +- "./gradlew test" +- "./gradlew desktop:dist -Pbuildversion=${TRAVIS_TAG:1}" +- "./gradlew server:dist -Pbuildversion=${TRAVIS_TAG:1}" +- git config --global user.name "Wiki Updater" +- git clone --depth=1 --branch=master https://github.com/MindustryGame/wiki ../wiki +- git clone --depth=1 --branch=master https://github.com/Anuken/Mindustry-Wiki-Generator ../Mindustry-Wiki-Generator +- cd ../Mindustry-Wiki-Generator +- "./gradlew run" +- cd ../wiki +- git add . +- git commit -m "Update to match commit ${TRAVIS_COMMIT}" +- git push https://Anuken:${GH_PUSH_TOKEN}@github.com/MindustryGame/wiki +deploy: + provider: releases + skip_cleanup: true + draft: false + api_key: + secure: Cv5wFtWt62/A24EvSEQvMow7gKPbZ3oATEFPuSghhB2TQz1dA40Zee3Qvk4LFlpLrhYo4K0ZSczCZRGpR+hCd8+Dpww52bheYEvWuh3ZQfvu/fXtEx2j5PwP1qMpmIgSxETV/gkD7l9FImdh0VzktYiAvQfmi0bEocG9/D4QwjFpNat7iwBdcMiw1MvAygpdIWRsjiw0RKlB2mWarmoHhQ7Gu7qlU3j50uaEvcrtmU0pBUPggNQwQRv32i9NPvNFxrqqlUjDLIS8JFea99zCkp8BwYqbEvBIMzd+Qip1/stLJJA3+cDUClbsDtg8rAVetzpOrdLEEBmqShFe5MDl2yEHcsgpN9CFsyTaUfvB3P3rVjizvycMm42IsUkXQiarm5xTQ/TIA8Rd8AHiSKuweNCg1Fd5SFaRtKy8JVLXuxyfUccmyje6hhz2L4lS2Wfj3mAG7sqZUCXhWP79EKdGkiPOjKv4CwXEKmuH3BMVqPlNUZJr9Eg3sV1FG0h2l+MVOOnR635qdUbb49sYojYxVruMLX0BH1c4ZCu230m8CUoWA1Em1QNI75ya7+9Y5T6AsgWDVpBvdUo9fWNbdp+VQ0GskFQsJD5wtnxbcbHeFiERAgGBm7z6qt9u9LrQpBH+dsW52ADvYsu3L4nQEa+sdMHwTTwmGY+iUvsxu0DqxGg= + file: + - desktop/build/libs/desktop-release.jar + - server/build/libs/server-release.jar + on: + repo: Anuken/Mindustry + tags: true +env: + global: + secure: TqlUl/ojjkCMVOGbCTKz7Cnr4F08UyWzY/CiJ0vvUOGJGZ1qm7XavAlDf5XT0egU4mvr37THubFO8vojbqmrmy0oZnYh3njKFA8axgyZ8PyKkjGHOfd0i6qyEWsOr9H90/2X8r3LwEeLaDFyHpu3wljIGBjweg53g2qwmDwCFa9UR80FJZ+xDB+rD6B3cXT0DTEkCoLZXLqXm0Y3HvBdSuBL1LR/FNb2BSxNq+tNLGiz1kdQZV5erausbbZypBoGxzz63xAnyz2kkFz73A8xQYVTzGbFodTPz7HM13GVZ5s43I03Y+HYyHBgBaSLziO2hi2kzVJccOwzBp7wS4fs1MqsFY5+IeWJ9k+hm89NiYT7+6zlEgoUMlIniny1qLqWTzx7btUeuC/y/h5TVBNgaV+z0jmHycHfeSyq5I+vmX4J8qe3wmaN8TcdqYKU5nIznOTk3CM5Fzu0Bs9vkCkOxmormmcjMFW1RbdOLc/hpZWZggsBA88sNEAI8eq+r5QEeqzeCx8YKoZDjdrsqvgLMc3El3gS9oMGxkn0Y/TEcqs9Tc4BXtTkqIA68hD0DYzlAxYjVbbkAI9Hh9lHNvV3Dr/oCkGXQ/HflM143kj1L3tSBZpqeqQE2XhngB5nqpS3OZTmZbMTQ8qD2luU18yaTGMLF5tJS/fdKPRx0gQ1kL8= diff --git a/README.md b/README.md index 1bd4d0aec7..46d06bbaf1 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,45 @@ ![Imgur](https://i.imgur.com/w4N0yhv.png) -[![Build Status](https://travis-ci.org/Anuken/Mindustry.svg?branch=master)](https://travis-ci.org/Anuken/Mindustry) -[![Waffle.io - Columns and their card count](https://badge.waffle.io/Anuken/Mindustry.svg?columns=all)](https://waffle.io/Anuken/Mindustry) +[![Build Status](https://travis-ci.org/Anuken/Mindustry.svg?branch=master)](https://travis-ci.org/Anuken/Mindustry) +[![Discord](https://img.shields.io/discord/391020510269669376.svg)](https://discord.gg/mindustry) -A pixelated sandbox tower defense game made using [LibGDX](https://libgdx.badlogicgames.com/). Winner of the [GDL Metal Monstrosity Jam](https://itch.io/jam/gdl---metal-monstrosity-jam). +A sandbox tower defense game written in Java. -_[Issue tracker](https://waffle.io/Anuken/Mindustry)_ _[Trello Board](https://trello.com/b/aE2tcUwF/mindustry-40-plans)_ -_[Wiki](http://mindustry.wikia.com/wiki/Mindustry_Wiki)_ -_[Discord](https://discord.gg/r8BkXNd)_ +_[Wiki](http://mindustry.wikia.com/wiki/Mindustry_Wiki)_ ### Building -Bleeding-edge live builds are generated automatically for every commit. You can see them [here](https://github.com/Anuken/Mindustry/wiki). +Bleeding-edge live builds are generated automatically for every commit. You can see them [here](https://jenkins.hellomouse.net/job/mindustry/). If you'd rather compile on your own, follow these instructions. -First, make sure you have Java 8 and JDK 8 installed. Open a terminal in the root directory, and run the following commands: +First, make sure you have Java 8 and JDK 8 installed. Open a terminal in the root directory, `cd` to the Mindustry folder and run the following commands: +#### Windows -**_Windows_** +_Running:_ `gradlew desktop:run` +_Building:_ `gradlew desktop:dist` -_Running:_ `gradlew.bat desktop:run` -_Building:_ `gradlew.bat desktop:dist` - - -**_Linux_** +#### Linux/Mac OS _Running:_ `./gradlew desktop:run` _Building:_ `./gradlew desktop:dist` +#### Server + +Server builds are bundled with each released build (in Releases). If you'd rather compile on your own, replace 'desktop' with 'server', e.g. `gradlew server:dist`. + +##### Troubleshooting + +If the terminal returns `Permission denied` or `Command not found` on Mac/Linux, run `chmod +x ./gradlew` before running `./gradlew`. *This is a one-time procedure.* + --- Gradle may take up to several minutes to download files. Be patient.
-After building, the output .JAR file should be in the output JAR file should be in `/desktop/build/libs/desktop-release.jar.` +After building, the output .JAR file should be in `/desktop/build/libs/desktop-release.jar` for desktop builds, and in `/server/build/libs/server-release.jar` for server builds. ### Downloads - + - + diff --git a/TRANSLATING.md b/TRANSLATING.md new file mode 100644 index 0000000000..945fc76788 --- /dev/null +++ b/TRANSLATING.md @@ -0,0 +1,39 @@ +## Translating for Mindustry + +**DISCLAIMER:** *Currently, 4.0 is far from done, which means that things such as block names, descriptions, and core text will be changing often. If you begin translating now, you might have to re-do large chunks of the bundle before final release.* + + +To begin, log in to your GitHub account, or if you don't have one yet, create it [here](https://github.com/). + +Consult [this list](https://www.science.co.il/language/Locale-codes.php) to find the locale code for your language. Once you've found it, +head over to the translation bundle folder and check the [list of bundles](https://github.com/Anuken/Mindustry/tree/master/core/assets/bundles) that have already been created. +You're looking for a file called "`bundle_`(insert locale code here)`.properties`". If you don't find one, create one manually (more info below). + +#### Editing an existing translation + +If a translation bundle already exists, that means someone has already started working on a translation. To edit it or translate text, simply click the file and press the edit (pencil) button in the top right. Once you're done editing, press the green "propose file change" button at the bottom, then "create pull request" (twice). +Once this is done, all you need to do is wait for me to approve your changes. + +#### Creating a new translation bundle + +If a translation bundle for your language *doesn't* exist, you need to create one yourself. +In the folder with all the bundles in it, click the *'create new file'* button, and name it `bundle_(locale code here).properties`. +Then, copy-paste the entire contents of the [English translation bundle](https://raw.githubusercontent.com/Anuken/Mindustry/master/core/assets/bundles/bundle.properties) into the file, and translate all the necessary text to your language. +Once you are done, press the *propose new file* button at the bottom, then 'create pull request' twice. + +#### Useful Information + +- When you see text surrounded by square brackets, such as `[RED]`, `[]` or `[accent]`, this indicates a color code. Don't translate it. +- `{0}` means an argument that will be replaced when the text is displayed. For example, `Wave: {0}` will replace the `{0}` with whatever wave you are in. +- Empty lines are fine, and it doesn't matter in what order you place the text. +- `\n` means "new line". If you want to split text into multiple lines, use `\n` to do it. + +#### Testing your translation bundle + +There are two ways to test the translation bundle: +1) Assuming you have the PC version downloaded, download your bundle file, name it `bundle.properties`, then place it in the same folder as the Mindustry desktop executable and run it. *You should get a popup message in-game confirming that you have loaded an external translation.* +2) For advanced users: simply download your fork of mindustry and compile/run the game. + +**And that's it.** + +*(...of course, that's never really it. Bother me on Discord when something inevitably goes wrong.)* diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 01d804c3fd..6e42e10a0c 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -1,33 +1,65 @@ + package="io.anuke.mindustry"> - - - - - + + + + - + android:theme="@style/GdxTheme" android:fullBackupContent="@xml/backup_rules"> + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - diff --git a/android/build.gradle b/android/build.gradle index bc3c955d02..491aaf0a5a 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -1,31 +1,59 @@ +buildscript{ + repositories{ + mavenLocal() + mavenCentral() + google() + maven{ url "https://oss.sonatype.org/content/repositories/snapshots/" } + jcenter() + } -repositories { - mavenCentral() - jcenter() - maven { - url "https://maven.google.com" + dependencies{ + classpath 'com.android.tools.build:gradle:3.4.1' } } -dependencies { - implementation 'com.android.support:support-v4:22.1.1' - implementation 'org.sufficientlysecure:donations:2.5' - implementation 'com.google.android.gms:play-services-auth:11.8.0' +apply plugin: "com.android.application" + +configurations{ natives } + +repositories{ + mavenCentral() + jcenter() + maven{ + url "https://maven.google.com" + } +} + +dependencies{ + implementation project(":core") + implementation project(":net") + + implementation arcModule("backends:backend-android") + natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-armeabi" + natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-armeabi-v7a" + natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-arm64-v8a" + natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-x86" + natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-x86_64" + natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-armeabi" + natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-armeabi-v7a" + natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-arm64-v8a" + natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-x86" + natives "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-x86_64" } task deploy(type: Copy){ dependsOn "assembleRelease" - from "build/outputs/apk/google/release/android-google-release.apk" - into "../deploy/"; - rename ("android-google-release.apk", appName + "-android-" + getVersionString() + ".apk"); + from "build/outputs/apk/release/android-release.apk" + into "../deploy/" + rename("android-release.apk", appName + "-android-" + getVersionString() + ".apk") } -android { - buildToolsVersion '26.0.2' - compileSdkVersion 26 - sourceSets { - main { +android{ + buildToolsVersion '28.0.3' + compileSdkVersion 28 + sourceSets{ + main{ manifest.srcFile 'AndroidManifest.xml' java.srcDirs = ['src'] aidl.srcDirs = ['src'] @@ -35,13 +63,13 @@ android { jniLibs.srcDirs = ['libs'] } - instrumentTest.setRoot('tests') + androidTest.setRoot('tests') } - packagingOptions { + packagingOptions{ exclude 'META-INF/robovm/ios/robovm.xml' } - defaultConfig { + defaultConfig{ def vfile = file('../core/assets/version.properties') def code = 0 @@ -60,62 +88,58 @@ android { applicationId "io.anuke.mindustry" minSdkVersion 14 - targetSdkVersion 27 + targetSdkVersion 28 versionCode code versionName versionNameResult } - compileOptions { + compileOptions{ sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } flavorDimensions "google" - productFlavors { - google { - buildConfigField "boolean", "DONATIONS_GOOGLE", "true" - } - } - - signingConfigs { - release { - if(project.hasProperty("RELEASE_STORE_FILE")) { + signingConfigs{ + release{ + if(project.hasProperty("RELEASE_STORE_FILE")){ storeFile file(RELEASE_STORE_FILE) storePassword RELEASE_STORE_PASSWORD keyAlias RELEASE_KEY_ALIAS keyPassword RELEASE_KEY_PASSWORD }else{ - println("No keystore info property found!"); + println("No keystore info property found!") } } } - buildTypes { - release { - signingConfig signingConfigs.release + if(project.hasProperty("RELEASE_STORE_FILE")) { + buildTypes { + release { + signingConfig signingConfigs.release + } } } } // called every time gradle gets executed, takes the native dependencies of // the natives configuration, and extracts them to the proper libs/ folders // so they get packed with the APK. -task copyAndroidNatives() { - file("libs/armeabi/").mkdirs(); - file("libs/armeabi-v7a/").mkdirs(); - file("libs/arm64-v8a/").mkdirs(); - file("libs/x86_64/").mkdirs(); - file("libs/x86/").mkdirs(); +task copyAndroidNatives(){ + file("libs/armeabi/").mkdirs() + file("libs/armeabi-v7a/").mkdirs() + file("libs/arm64-v8a/").mkdirs() + file("libs/x86_64/").mkdirs() + file("libs/x86/").mkdirs() - configurations.natives.files.each { jar -> + configurations.natives.files.each{ jar -> def outputDir = null - if (jar.name.endsWith("natives-arm64-v8a.jar")) outputDir = file("libs/arm64-v8a") - if (jar.name.endsWith("natives-armeabi-v7a.jar")) outputDir = file("libs/armeabi-v7a") - if (jar.name.endsWith("natives-armeabi.jar")) outputDir = file("libs/armeabi") - if (jar.name.endsWith("natives-x86_64.jar")) outputDir = file("libs/x86_64") - if (jar.name.endsWith("natives-x86.jar")) outputDir = file("libs/x86") - if (outputDir != null) { - copy { + if(jar.name.endsWith("natives-arm64-v8a.jar")) outputDir = file("libs/arm64-v8a") + if(jar.name.endsWith("natives-armeabi-v7a.jar")) outputDir = file("libs/armeabi-v7a") + if(jar.name.endsWith("natives-armeabi.jar")) outputDir = file("libs/armeabi") + if(jar.name.endsWith("natives-x86_64.jar")) outputDir = file("libs/x86_64") + if(jar.name.endsWith("natives-x86.jar")) outputDir = file("libs/x86") + if(outputDir != null){ + copy{ from zipTree(jar) into outputDir include "*.so" @@ -123,76 +147,25 @@ task copyAndroidNatives() { } } } -task run(type: Exec) { + +task run(type: Exec){ def path def localProperties = project.file("../local.properties") - if (localProperties.exists()) { + if(localProperties.exists()){ Properties properties = new Properties() - localProperties.withInputStream { instr -> + localProperties.withInputStream{ instr -> properties.load(instr) } def sdkDir = properties.getProperty('sdk.dir') - if (sdkDir) { + if(sdkDir){ path = sdkDir - } else { + }else{ path = "$System.env.ANDROID_HOME" } - } else { + }else{ path = "$System.env.ANDROID_HOME" } def adb = path + "/platform-tools/adb" commandLine "$adb", 'shell', 'am', 'start', '-n', 'io.anuke.mindustry/io.anuke.mindustry.AndroidLauncher' -} -// sets up the Android Eclipse project, using the old Ant based build. -eclipse { - // need to specify Java source sets explicitly, SpringSource Gradle Eclipse plugin - // ignores any nodes added in classpath.file.withXml - sourceSets { - main { - java.srcDirs "src", 'gen' - } - } - - jdt { - sourceCompatibility = 1.7 - targetCompatibility = 1.7 - } - - classpath { - plusConfigurations += [project.configurations.compile] - containers 'com.android.ide.eclipse.adt.ANDROID_FRAMEWORK', 'com.android.ide.eclipse.adt.LIBRARIES' - } - - project { - name = appName + "-android" - natures 'com.android.ide.eclipse.adt.AndroidNature' - buildCommands.clear(); - buildCommand "com.android.ide.eclipse.adt.ResourceManagerBuilder" - buildCommand "com.android.ide.eclipse.adt.PreCompilerBuilder" - buildCommand "org.eclipse.jdt.core.javabuilder" - buildCommand "com.android.ide.eclipse.adt.ApkBuilder" - } -} -// sets up the Android Idea project, using the old Ant based build. -idea { - module { - sourceDirs += file("src"); - scopes = [COMPILE: [plus: [project.configurations.compile]]] - - iml { - withXml { - def node = it.asNode() - def builder = NodeBuilder.newInstance(); - builder.current = node; - builder.component(name: "FacetManager") { - facet(type: "android", name: "Android") { - configuration { - option(name: "UPDATE_PROPERTY_FILES", value: "true") - } - } - } - } - } - } -} +} \ No newline at end of file diff --git a/android/ic_launcher-web.png b/android/ic_launcher-web.png index 4ad2ddd445..658c1fd713 100644 Binary files a/android/ic_launcher-web.png and b/android/ic_launcher-web.png differ diff --git a/android/proguard-project.txt b/android/proguard-project.txt index b166b1e81c..1f34b8cae8 100644 --- a/android/proguard-project.txt +++ b/android/proguard-project.txt @@ -20,26 +20,10 @@ #} -verbose - --dontwarn android.support.** --dontwarn com.badlogic.gdx.backends.android.AndroidFragmentApplication --dontwarn com.badlogic.gdx.utils.GdxBuild --dontwarn com.badlogic.gdx.physics.box2d.utils.Box2DBuild --dontwarn com.badlogic.gdx.jnigen.BuildTarget* --dontwarn com.badlogic.gdx.graphics.g2d.freetype.FreetypeBuild - --keep class com.badlogic.gdx.controllers.android.AndroidControllers - --keepclassmembers class com.badlogic.gdx.backends.android.AndroidInput* { - (com.badlogic.gdx.Application, android.content.Context, java.lang.Object, com.badlogic.gdx.backends.android.AndroidApplicationConfiguration); -} - --keepclassmembers class com.badlogic.gdx.physics.box2d.World { - boolean contactFilter(long, long); - void beginContact(long); - void endContact(long); - void preSolve(long, long); - void postSolve(long, long); - boolean reportFixture(long); - float reportRayFixture(long, float, float, float, float, float); +-verbose +-ignorewarnings +-keep class io.anuke.mindustry.game.Rules +-keep class io.anuke.mindustry.desktop.DesktopLauncher +-keepclasseswithmembers public class * { + public static void main(java.lang.String[]); } diff --git a/android/project.properties b/android/project.properties index 4ab125693c..f13bd4b19c 100644 --- a/android/project.properties +++ b/android/project.properties @@ -9,6 +9,5 @@ # # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt - # Project target. target=android-19 diff --git a/android/res/drawable-hdpi/ic_launcher.png b/android/res/drawable-hdpi/ic_launcher.png deleted file mode 100644 index b43bb84e6e..0000000000 Binary files a/android/res/drawable-hdpi/ic_launcher.png and /dev/null differ diff --git a/android/res/drawable-hdpi/ic_launcher_round.png b/android/res/drawable-hdpi/ic_launcher_round.png deleted file mode 100644 index c79dccd9df..0000000000 Binary files a/android/res/drawable-hdpi/ic_launcher_round.png and /dev/null differ diff --git a/android/res/drawable-mdpi/ic_launcher.png b/android/res/drawable-mdpi/ic_launcher.png deleted file mode 100644 index 91d97a6051..0000000000 Binary files a/android/res/drawable-mdpi/ic_launcher.png and /dev/null differ diff --git a/android/res/drawable-xhdpi/ic_launcher.png b/android/res/drawable-xhdpi/ic_launcher.png deleted file mode 100644 index 2adf4e8f9a..0000000000 Binary files a/android/res/drawable-xhdpi/ic_launcher.png and /dev/null differ diff --git a/android/res/drawable-xxhdpi/ic_launcher.png b/android/res/drawable-xxhdpi/ic_launcher.png deleted file mode 100644 index a2131b698e..0000000000 Binary files a/android/res/drawable-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/android/res/drawable-xxxhdpi/ic_launcher.png b/android/res/drawable-xxxhdpi/ic_launcher.png deleted file mode 100644 index f625a1e78e..0000000000 Binary files a/android/res/drawable-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/android/res/drawable/background.png b/android/res/drawable/background.png deleted file mode 100644 index b02bc10b64..0000000000 Binary files a/android/res/drawable/background.png and /dev/null differ diff --git a/android/res/drawable/ic_launcher.png b/android/res/drawable/ic_launcher.png deleted file mode 100644 index b43bb84e6e..0000000000 Binary files a/android/res/drawable/ic_launcher.png and /dev/null differ diff --git a/android/res/drawable/ic_launcher_background.png b/android/res/drawable/ic_launcher_background.png index 0674f294a8..8e013ea019 100644 Binary files a/android/res/drawable/ic_launcher_background.png and b/android/res/drawable/ic_launcher_background.png differ diff --git a/android/res/drawable/ic_launcher_foreground.png b/android/res/drawable/ic_launcher_foreground.png index d9ba2d1a9b..7adbf4e2c5 100644 Binary files a/android/res/drawable/ic_launcher_foreground.png and b/android/res/drawable/ic_launcher_foreground.png differ diff --git a/android/res/layout-v14/gdxdialogs_inputtext.xml b/android/res/layout-v14/gdxdialogs_inputtext.xml deleted file mode 100755 index e54b6a5016..0000000000 --- a/android/res/layout-v14/gdxdialogs_inputtext.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - - - \ No newline at end of file diff --git a/android/res/layout/donations_activity.xml b/android/res/layout/donations_activity.xml deleted file mode 100644 index 15b305760f..0000000000 --- a/android/res/layout/donations_activity.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/android/res/layout/gdxdialogs_inputtext.xml b/android/res/layout/gdxdialogs_inputtext.xml deleted file mode 100755 index 656600a753..0000000000 --- a/android/res/layout/gdxdialogs_inputtext.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/android/res/mipmap-hdpi/ic_launcher.png b/android/res/mipmap-hdpi/ic_launcher.png index 2447c3f621..f4d594a423 100644 Binary files a/android/res/mipmap-hdpi/ic_launcher.png and b/android/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/res/mipmap-hdpi/ic_launcher_background.png b/android/res/mipmap-hdpi/ic_launcher_background.png index f9543146b7..4a792ac88c 100644 Binary files a/android/res/mipmap-hdpi/ic_launcher_background.png and b/android/res/mipmap-hdpi/ic_launcher_background.png differ diff --git a/android/res/mipmap-hdpi/ic_launcher_foreground.png b/android/res/mipmap-hdpi/ic_launcher_foreground.png index 5e0ac93602..f4bace551a 100644 Binary files a/android/res/mipmap-hdpi/ic_launcher_foreground.png and b/android/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/android/res/mipmap-hdpi/ic_launcher_round.png b/android/res/mipmap-hdpi/ic_launcher_round.png index 686f48cb81..ffd1c7fca6 100644 Binary files a/android/res/mipmap-hdpi/ic_launcher_round.png and b/android/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/android/res/mipmap-mdpi/ic_launcher.png b/android/res/mipmap-mdpi/ic_launcher.png index 8946c07d2a..4071a357a3 100644 Binary files a/android/res/mipmap-mdpi/ic_launcher.png and b/android/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/res/mipmap-mdpi/ic_launcher_background.png b/android/res/mipmap-mdpi/ic_launcher_background.png index 7f784e8f33..50c0775712 100644 Binary files a/android/res/mipmap-mdpi/ic_launcher_background.png and b/android/res/mipmap-mdpi/ic_launcher_background.png differ diff --git a/android/res/mipmap-mdpi/ic_launcher_foreground.png b/android/res/mipmap-mdpi/ic_launcher_foreground.png index 29ee0aca67..10c2324cba 100644 Binary files a/android/res/mipmap-mdpi/ic_launcher_foreground.png and b/android/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/android/res/mipmap-mdpi/ic_launcher_round.png b/android/res/mipmap-mdpi/ic_launcher_round.png index 1952f47833..5b314cf65f 100644 Binary files a/android/res/mipmap-mdpi/ic_launcher_round.png and b/android/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/android/res/mipmap-xhdpi/ic_launcher.png b/android/res/mipmap-xhdpi/ic_launcher.png index 7d2b5292be..f76fa1cd09 100644 Binary files a/android/res/mipmap-xhdpi/ic_launcher.png and b/android/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/res/mipmap-xhdpi/ic_launcher_background.png b/android/res/mipmap-xhdpi/ic_launcher_background.png index 17e0f3f893..6b4646a1c2 100644 Binary files a/android/res/mipmap-xhdpi/ic_launcher_background.png and b/android/res/mipmap-xhdpi/ic_launcher_background.png differ diff --git a/android/res/mipmap-xhdpi/ic_launcher_foreground.png b/android/res/mipmap-xhdpi/ic_launcher_foreground.png index b18c416546..34f6137a4d 100644 Binary files a/android/res/mipmap-xhdpi/ic_launcher_foreground.png and b/android/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/android/res/mipmap-xhdpi/ic_launcher_round.png b/android/res/mipmap-xhdpi/ic_launcher_round.png index 1d6e1d523c..260458750c 100644 Binary files a/android/res/mipmap-xhdpi/ic_launcher_round.png and b/android/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/android/res/mipmap-xxhdpi/ic_launcher.png b/android/res/mipmap-xxhdpi/ic_launcher.png index 96deec6e8a..cde10344a7 100644 Binary files a/android/res/mipmap-xxhdpi/ic_launcher.png and b/android/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/res/mipmap-xxhdpi/ic_launcher_background.png b/android/res/mipmap-xxhdpi/ic_launcher_background.png index c3458d1cfd..bfda7fa416 100644 Binary files a/android/res/mipmap-xxhdpi/ic_launcher_background.png and b/android/res/mipmap-xxhdpi/ic_launcher_background.png differ diff --git a/android/res/mipmap-xxhdpi/ic_launcher_foreground.png b/android/res/mipmap-xxhdpi/ic_launcher_foreground.png index 7b5c3d24be..637925a18a 100644 Binary files a/android/res/mipmap-xxhdpi/ic_launcher_foreground.png and b/android/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/android/res/mipmap-xxhdpi/ic_launcher_round.png b/android/res/mipmap-xxhdpi/ic_launcher_round.png index 375d7b0634..8d00a8c395 100644 Binary files a/android/res/mipmap-xxhdpi/ic_launcher_round.png and b/android/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/android/res/mipmap-xxxhdpi/ic_launcher.png b/android/res/mipmap-xxxhdpi/ic_launcher.png index 12f7a9e6ee..db23766b64 100644 Binary files a/android/res/mipmap-xxxhdpi/ic_launcher.png and b/android/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/res/mipmap-xxxhdpi/ic_launcher_background.png b/android/res/mipmap-xxxhdpi/ic_launcher_background.png index e3f671b50f..96b6c95fbd 100644 Binary files a/android/res/mipmap-xxxhdpi/ic_launcher_background.png and b/android/res/mipmap-xxxhdpi/ic_launcher_background.png differ diff --git a/android/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/android/res/mipmap-xxxhdpi/ic_launcher_foreground.png index 3cb7d5ad21..e8739f65cf 100644 Binary files a/android/res/mipmap-xxxhdpi/ic_launcher_foreground.png and b/android/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/android/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/res/mipmap-xxxhdpi/ic_launcher_round.png index 3ce9bec872..f3f3aaa547 100644 Binary files a/android/res/mipmap-xxxhdpi/ic_launcher_round.png and b/android/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/android/res/values-v21/styles.xml b/android/res/values-v21/styles.xml index e07288c056..699b6a05d7 100644 --- a/android/res/values-v21/styles.xml +++ b/android/res/values-v21/styles.xml @@ -1,6 +1,5 @@ - - - \ No newline at end of file diff --git a/android/res/values/strings.xml b/android/res/values/strings.xml index 5bc3a457fa..b77280e581 100644 --- a/android/res/values/strings.xml +++ b/android/res/values/strings.xml @@ -2,14 +2,5 @@ Mindustry - - 1 Dollar - 2 Dollars - 5 Dollars - 10 Dollars - 15 Dollars - 25 Dollars - 50 Dollars - - + \ No newline at end of file diff --git a/android/res/xml/backup_rules.xml b/android/res/xml/backup_rules.xml new file mode 100644 index 0000000000..bfa7569b2e --- /dev/null +++ b/android/res/xml/backup_rules.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/android/src/io/anuke/mindustry/AndroidLauncher.java b/android/src/io/anuke/mindustry/AndroidLauncher.java index a37db1b058..3f75f746fe 100644 --- a/android/src/io/anuke/mindustry/AndroidLauncher.java +++ b/android/src/io/anuke/mindustry/AndroidLauncher.java @@ -3,179 +3,184 @@ package io.anuke.mindustry; import android.Manifest; import android.content.Context; import android.content.Intent; +import android.content.pm.ActivityInfo; import android.content.pm.PackageManager; +import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.provider.Settings.Secure; import android.telephony.TelephonyManager; -import android.util.Log; -import com.badlogic.gdx.backends.android.AndroidApplication; -import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration; -import com.badlogic.gdx.utils.Base64Coder; -import com.google.android.gms.common.GoogleApiAvailability; -import com.google.android.gms.common.GooglePlayServicesNotAvailableException; -import com.google.android.gms.common.GooglePlayServicesRepairableException; -import com.google.android.gms.security.ProviderInstaller; -import io.anuke.kryonet.DefaultThreadImpl; -import io.anuke.kryonet.KryoClient; -import io.anuke.kryonet.KryoServer; -import io.anuke.mindustry.core.ThreadHandler.ThreadProvider; +import io.anuke.arc.Core; +import io.anuke.arc.backends.android.surfaceview.AndroidApplication; +import io.anuke.arc.backends.android.surfaceview.AndroidApplicationConfiguration; +import io.anuke.arc.files.FileHandle; +import io.anuke.arc.function.Consumer; +import io.anuke.arc.function.Predicate; +import io.anuke.arc.scene.ui.layout.Unit; +import io.anuke.arc.util.Strings; +import io.anuke.arc.util.serialization.Base64Coder; import io.anuke.mindustry.core.Platform; -import io.anuke.mindustry.net.Net; -import io.anuke.ucore.core.Settings; -import io.anuke.ucore.scene.ui.TextField; -import io.anuke.ucore.scene.ui.layout.Unit; +import io.anuke.mindustry.game.Saves.SaveSlot; +import io.anuke.mindustry.io.SaveIO; +import io.anuke.mindustry.net.*; +import io.anuke.mindustry.ui.dialogs.FileChooser; -import java.text.DateFormat; -import java.text.NumberFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.Locale; -import java.util.Random; +import java.io.*; +import java.util.ArrayList; + +import static io.anuke.mindustry.Vars.*; public class AndroidLauncher extends AndroidApplication{ - boolean doubleScaleTablets = true; - int WRITE_REQUEST_CODE = 1; + public static final int PERMISSION_REQUEST_CODE = 1; + boolean doubleScaleTablets = true; + FileChooser chooser; - @Override - protected void onCreate(Bundle savedInstanceState){ - super.onCreate(savedInstanceState); + @Override + protected void onCreate(Bundle savedInstanceState){ + super.onCreate(savedInstanceState); + AndroidApplicationConfiguration config = new AndroidApplicationConfiguration(); + config.useImmersiveMode = true; + config.depth = 0; + Platform.instance = new Platform(){ - AndroidApplicationConfiguration config = new AndroidApplicationConfiguration(); - config.useImmersiveMode = true; + @Override + public void hide(){ + moveTaskToBack(true); + } - Platform.instance = new Platform(){ - DateFormat format = SimpleDateFormat.getDateTimeInstance(); - - @Override - public boolean hasDiscord() { - return isPackageInstalled("com.discord"); - } - - @Override - public String format(Date date){ - return format.format(date); - } - - @Override - public String format(int number){ - return NumberFormat.getIntegerInstance().format(number); - } - - @Override - public void addDialog(TextField field, int length){ - TextFieldDialogListener.add(field, 0, length); - } - - @Override - public String getLocaleName(Locale locale){ - return locale.getDisplayName(locale); - } - - @Override - public void openDonations() { - showDonations(); - } - - @Override - public void requestWritePerms() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED && - checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, - Manifest.permission.READ_EXTERNAL_STORAGE}, WRITE_REQUEST_CODE); - }else{ - - if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, WRITE_REQUEST_CODE); - } - - if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { - requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, WRITE_REQUEST_CODE); - } - } - } - } - - @Override - public ThreadProvider getThreadProvider() { - return new DefaultThreadImpl(); - } - - @Override - public boolean isDebug() { - return false; - } - - @Override - public byte[] getUUID() { - try { - String s = Secure.getString(getContext().getContentResolver(), - Secure.ANDROID_ID); - - int len = s.length(); - byte[] data = new byte[len / 2]; - for (int i = 0; i < len; i += 2) { - data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) - + Character.digit(s.charAt(i + 1), 16)); - } - - if(new String(Base64Coder.encode(data)).equals("AAAAAAAAAOA=")) throw new RuntimeException("Bad UUID."); - - return data; - }catch (Exception e){ - - String uuid = Settings.getString("uuid", ""); - if(uuid.isEmpty()){ - byte[] result = new byte[8]; - new Random().nextBytes(result); - uuid = new String(Base64Coder.encode(result)); - Settings.putString("uuid", uuid); - Settings.save(); - return result; + @Override + public String getUUID(){ + try{ + String s = Secure.getString(getContext().getContentResolver(), Secure.ANDROID_ID); + int len = s.length(); + byte[] data = new byte[len / 2]; + for(int i = 0; i < len; i += 2){ + data[i / 2] = (byte)((Character.digit(s.charAt(i), 16) << 4) + + Character.digit(s.charAt(i + 1), 16)); } - return Base64Coder.decode(uuid); - } - } - }; + String result = new String(Base64Coder.encode(data)); + if(result.equals("AAAAAAAAAOA=")) throw new RuntimeException("Bad UUID."); + return result; + }catch(Exception e){ + return super.getUUID(); + } + } - try { - ProviderInstaller.installIfNeeded(this); - } catch (GooglePlayServicesRepairableException e) { - GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance(); - apiAvailability.getErrorDialog(this, e.getConnectionStatusCode(), 0).show(); - } catch (GooglePlayServicesNotAvailableException e) { - Log.e("SecurityException", "Google Play Services not available."); - } + @Override + public void shareFile(FileHandle file){ + } - if(doubleScaleTablets && isTablet(this.getContext())){ - Unit.dp.addition = 0.5f; - } - - config.hideStatusBar = true; + @Override + public void showFileChooser(String text, String content, Consumer cons, boolean open, Predicate filetype){ + chooser = new FileChooser(text, file -> filetype.test(file.extension().toLowerCase()), open, cons); + if(Build.VERSION.SDK_INT < Build.VERSION_CODES.M || (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED && + checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)){ + chooser.show(); + chooser = null; + }else{ + ArrayList perms = new ArrayList<>(); + if(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){ + perms.add(Manifest.permission.WRITE_EXTERNAL_STORAGE); + } + if(checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){ + perms.add(Manifest.permission.READ_EXTERNAL_STORAGE); + } + requestPermissions(perms.toArray(new String[0]), PERMISSION_REQUEST_CODE); + } + } - Net.setClientProvider(new KryoClient()); - Net.setServerProvider(new KryoServer()); + @Override + public void beginForceLandscape(){ + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE); + } + @Override + public void endForceLandscape(){ + setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR); + } + + @Override + public boolean canDonate(){ + return true; + } + }; + + if(doubleScaleTablets && isTablet(this.getContext())){ + Unit.dp.addition = 0.5f; + } + + config.hideStatusBar = true; + Net.setClientProvider(new ArcNetClient()); + Net.setServerProvider(new ArcNetServer()); initialize(new Mindustry(), config); - } - - private boolean isPackageInstalled(String packagename) { - try { - getPackageManager().getPackageInfo(packagename, 0); - return true; - } catch (Exception e) { - return false; - } - } - - private boolean isTablet(Context context) { - TelephonyManager manager = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); - return manager.getPhoneType() == TelephonyManager.PHONE_TYPE_NONE; - } - - private void showDonations(){ - Intent intent = new Intent(this, DonationsActivity.class); - startActivity(intent); - } + checkFiles(getIntent()); + } + + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults){ + if(requestCode == PERMISSION_REQUEST_CODE){ + for(int i : grantResults){ + if(i != PackageManager.PERMISSION_GRANTED) return; + } + if(chooser != null){ + chooser.show(); + } + } + } + + private void checkFiles(Intent intent){ + try{ + Uri uri = intent.getData(); + if(uri != null){ + File myFile = null; + String scheme = uri.getScheme(); + if(scheme.equals("file")){ + String fileName = uri.getEncodedPath(); + myFile = new File(fileName); + }else if(!scheme.equals("content")){ + //error + return; + } + boolean save = uri.getPath().endsWith(saveExtension); + boolean map = uri.getPath().endsWith(mapExtension); + InputStream inStream; + if(myFile != null) inStream = new FileInputStream(myFile); + else inStream = getContentResolver().openInputStream(uri); + Core.app.post(() -> { + if(save){ //open save + System.out.println("Opening save."); + FileHandle file = Core.files.local("temp-save." + saveExtension); + file.write(inStream, false); + if(SaveIO.isSaveValid(file)){ + try{ + SaveSlot slot = control.saves.importSave(file); + ui.load.runLoadSave(slot); + }catch(IOException e){ + ui.showError(Core.bundle.format("save.import.fail", Strings.parseException(e, true))); + } + }else{ + ui.showError("$save.import.invalid"); + } + }else if(map){ //open map + FileHandle file = Core.files.local("temp-map." + mapExtension); + file.write(inStream, false); + Core.app.post(() -> { + System.out.println("Opening map."); + if(!ui.editor.isShown()){ + ui.editor.show(); + } + ui.editor.beginEditMap(file); + }); + } + }); + } + }catch(IOException e){ + e.printStackTrace(); + } + } + + private boolean isTablet(Context context){ + TelephonyManager manager = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); + return manager != null && manager.getPhoneType() == TelephonyManager.PHONE_TYPE_NONE; + } } diff --git a/android/src/io/anuke/mindustry/AndroidTextFieldDialog.java b/android/src/io/anuke/mindustry/AndroidTextFieldDialog.java deleted file mode 100644 index fd6fa9fec2..0000000000 --- a/android/src/io/anuke/mindustry/AndroidTextFieldDialog.java +++ /dev/null @@ -1,119 +0,0 @@ -package io.anuke.mindustry; - - -import android.app.Activity; -import android.app.AlertDialog; -import android.text.InputFilter; -import android.view.LayoutInflater; -import android.view.View; -import android.view.WindowManager.LayoutParams; -import android.widget.EditText; -import com.badlogic.gdx.Gdx; - -public class AndroidTextFieldDialog{ - private Activity activity; - private EditText userInput; - private AlertDialog.Builder builder; - private TextPromptListener listener; - private boolean isBuild; - - public AndroidTextFieldDialog() { - this.activity = (Activity)Gdx.app; - load(); - } - - public AndroidTextFieldDialog show() { - - activity.runOnUiThread(() -> { - Gdx.app.error("Android Dialogs", AndroidTextFieldDialog.class.getSimpleName() + " now shown."); - AlertDialog dialog = builder.create(); - - dialog.getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_STATE_VISIBLE); - - dialog.show(); - - }); - - return this; - } - - private AndroidTextFieldDialog load() { - - activity.runOnUiThread(() -> { - - AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(activity); - LayoutInflater li = LayoutInflater.from(activity); - - View promptsView = li.inflate(getResourceId("gdxdialogs_inputtext", "layout"), null); - - alertDialogBuilder.setView(promptsView); - - userInput = (EditText) promptsView.findViewById(getResourceId("gdxDialogsEditTextInput", "id")); - - alertDialogBuilder.setCancelable(false); - builder = alertDialogBuilder; - - isBuild = true; - }); - - // Wait till TextPrompt is built. - while (!isBuild) { - try { - Thread.sleep(10); - } catch (InterruptedException e) { } - } - - return this; - } - - public int getResourceId(String pVariableName, String pVariableType) { - try { - return activity.getResources().getIdentifier(pVariableName, pVariableType, activity.getPackageName()); - } catch (Exception e) { - Gdx.app.error("Android Dialogs", "Cannot find resouce with name: " + pVariableName - + " Did you copy the layouts to /res/layouts and /res/layouts_v14 ?"); - e.printStackTrace(); - return -1; - } - } - - public AndroidTextFieldDialog setText(CharSequence value) { - userInput.append(value); - return this; - } - - public AndroidTextFieldDialog setCancelButtonLabel(CharSequence label) { - builder.setNegativeButton(label, (dialog, id) -> dialog.cancel()); - return this; - } - - public AndroidTextFieldDialog setConfirmButtonLabel(CharSequence label) { - builder.setPositiveButton(label, (dialog, id) -> { - if (listener != null && !userInput.getText().toString().isEmpty()) { - listener.confirm(userInput.getText().toString()); - } - - }); - return this; - } - - public AndroidTextFieldDialog setTextPromptListener(TextPromptListener listener) { - this.listener = listener; - return this; - } - - public AndroidTextFieldDialog setInputType(int type) { - userInput.setInputType(type); - return this; - } - - public AndroidTextFieldDialog setMaxLength(int length) { - userInput.setFilters(new InputFilter[] { new InputFilter.LengthFilter(length) }); - return this; - } - - public interface TextPromptListener{ - void confirm(String text); - } - -} diff --git a/android/src/io/anuke/mindustry/DonationsActivity.java b/android/src/io/anuke/mindustry/DonationsActivity.java deleted file mode 100644 index 690ccc3413..0000000000 --- a/android/src/io/anuke/mindustry/DonationsActivity.java +++ /dev/null @@ -1,79 +0,0 @@ -package io.anuke.mindustry; - -import android.content.Intent; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentActivity; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentTransaction; -import android.view.View; -import android.widget.Button; - -import org.sufficientlysecure.donations.DonationsFragment; - -public class DonationsActivity extends FragmentActivity { - DonationsFragment donationsFragment; - - /** - * Google - */ - private static final String GOOGLE_PUBKEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzG93KhpfBPKTo2jF0yxbWkkmMKwsPNM4SsMj1aDq7vv6n3R+mqJVfprOJxFfJh7JchXTflLIgiaKXFAiU70gJbMTniEWnEaFSxAeF09a7U0RjOwN+7rFwjCG91c2CpYxPanBTQP4zasc1ODPVzq4q6/4ByjhenN71V4WmR08NFIAodcfFPrOkDPil7i8y7cgcd1Ky53U0TS+LLYJttAK3XdTK4s7VE3I5IKoeNa4uwCmIM59R67q2k3cXjLk/nP6MP+y++EzHN/PTiR1sVg4dMP8K31RPw/1QNLPQwJz6Wc872oWwb7xo5gkoXbDc5WPPydsi8F3SyKNaYwzN6CDFQIDAQAB"; - private static final String[] GOOGLE_CATALOG = new String[]{ - "mindustry.donation.1", "mindustry.donation.2", "mindustry.donation.5", - "mindustry.donation.10", "mindustry.donation.15", - "mindustry.donation.25", "mindustry.donation.50" }; - - /** - * Called when the activity is first created. - */ - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setTheme(R.style.GdxTheme); - - setContentView(R.layout.donations_activity); - - FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); - if (BuildConfig.DONATIONS_GOOGLE) { - donationsFragment = DonationsFragment.newInstance(BuildConfig.DEBUG, true, GOOGLE_PUBKEY, GOOGLE_CATALOG, - getResources().getStringArray(R.array.donation_google_catalog_values), false, null, null, - null, false, null, null, false, null); - } - - - ft.replace(R.id.donations_activity_container, donationsFragment, "donationsFragment"); - ft.commit(); - } - - public void onStart(){ - super.onStart(); - Button b = ((Button)findViewById(org.sufficientlysecure.donations.R.id.donations__google_android_market_donate_button)); - b.setOnClickListener(new View.OnClickListener() { - @Override public void onClick(View view) { - donationsFragment.donateGoogleOnClick(donationsFragment.getView()); - b.setEnabled(false); - } - }); - } - - - - /** - * Needed for Google Play In-app Billing. It uses startIntentSenderForResult(). The result is not propagated to - * the Fragment like in startActivityForResult(). Thus we need to propagate manually to our Fragment. - */ - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - Button b = ((Button)findViewById(org.sufficientlysecure.donations.R.id.donations__google_android_market_donate_button)); - b.setEnabled(true); - - FragmentManager fragmentManager = getSupportFragmentManager(); - Fragment fragment = fragmentManager.findFragmentByTag("donationsFragment"); - if (fragment != null) { - fragment.onActivityResult(requestCode, resultCode, data); - //TODO donation event, set settings? - } - } -} diff --git a/android/src/io/anuke/mindustry/TextFieldDialogListener.java b/android/src/io/anuke/mindustry/TextFieldDialogListener.java deleted file mode 100644 index c6f328d785..0000000000 --- a/android/src/io/anuke/mindustry/TextFieldDialogListener.java +++ /dev/null @@ -1,67 +0,0 @@ -package io.anuke.mindustry; - - -import android.text.InputType; -import com.badlogic.gdx.Application.ApplicationType; -import com.badlogic.gdx.Gdx; -import io.anuke.ucore.scene.event.ChangeListener; -import io.anuke.ucore.scene.event.ClickListener; -import io.anuke.ucore.scene.event.InputEvent; -import io.anuke.ucore.scene.event.InputListener; -import io.anuke.ucore.scene.ui.TextField; - -public class TextFieldDialogListener extends ClickListener{ - private TextField field; - private int type; - private int max; - - public static void add(TextField field, int type, int max){ - field.addListener(new TextFieldDialogListener(field, type, max)); - field.addListener(new InputListener(){ - public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) { - Gdx.input.setOnscreenKeyboardVisible(false); - return false; - } - }); - } - - public static void add(TextField field){ - add(field, 0, 16); - } - - //type - 0 is text, 1 is numbers, 2 is decimals - public TextFieldDialogListener(TextField field, int type, int max){ - this.field = field; - this.type = type; - this.max = max; - } - - public void clicked(final InputEvent event, float x, float y){ - - if(Gdx.app.getType() == ApplicationType.Desktop) return; - - AndroidTextFieldDialog dialog = new AndroidTextFieldDialog(); - - dialog.setTextPromptListener(text -> { - field.clearText(); - field.appendText(text); - field.fire(new ChangeListener.ChangeEvent()); - Gdx.graphics.requestRendering(); - }); - - if(type == 0){ - dialog.setInputType(InputType.TYPE_CLASS_TEXT); - }else if(type == 1){ - dialog.setInputType(InputType.TYPE_CLASS_NUMBER); - }else if(type == 2){ - dialog.setInputType(InputType.TYPE_NUMBER_FLAG_DECIMAL); - } - - dialog.setConfirmButtonLabel("OK").setText(field.getText()); - dialog.setCancelButtonLabel("Cancel"); - dialog.setMaxLength(max); - dialog.show(); - event.cancel(); - - } -} diff --git a/annotations/build.gradle b/annotations/build.gradle new file mode 100644 index 0000000000..d894ed0abf --- /dev/null +++ b/annotations/build.gradle @@ -0,0 +1,6 @@ +apply plugin: "java" + +sourceCompatibility = 1.8 +sourceSets.main.java.srcDirs = ["src/main/java/"] +sourceSets.main.resources.srcDirs = ["src/main/resources/"] + diff --git a/annotations/src/main/java/io/anuke/annotations/Annotations.java b/annotations/src/main/java/io/anuke/annotations/Annotations.java new file mode 100644 index 0000000000..1d5b62ed46 --- /dev/null +++ b/annotations/src/main/java/io/anuke/annotations/Annotations.java @@ -0,0 +1,150 @@ +package io.anuke.annotations; + +import java.lang.annotation.*; + +public class Annotations{ + + /** Indicates that a method should always call its super version. */ + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.SOURCE) + public @interface CallSuper{ + + } + + /** Annotation that allows overriding CallSuper annotation. To be used on method that overrides method with CallSuper annotation from parent class.*/ + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.SOURCE) + public @interface OverrideCallSuper { + } + + /** Indicates that a method return or field can be null.*/ + @Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE}) + @Retention(RetentionPolicy.SOURCE) + public @interface Nullable{ + + } + + /** Indicates that a method return or field cannot be null.*/ + @Target({ElementType.METHOD, ElementType.FIELD}) + @Retention(RetentionPolicy.SOURCE) + public @interface NonNull{ + + } + + /** Marks a class as serializable. */ + @Target(ElementType.TYPE) + @Retention(RetentionPolicy.SOURCE) + public @interface Serialize{ + + } + + /** Marks a class as a special value type struct. Class name must end in 'Struct'. */ + @Target(ElementType.TYPE) + @Retention(RetentionPolicy.SOURCE) + public @interface Struct{ + + } + + /** Marks a field of a struct. Optional. */ + @Target(ElementType.FIELD) + @Retention(RetentionPolicy.SOURCE) + public @interface StructField{ + /** Size of a struct field in bits. Not valid on booleans or floating point numbers. */ + int value(); + } + + public enum PacketPriority{ + /** Gets put in a queue and processed if not connected. */ + normal, + /** Gets handled immediately, regardless of connection status. */ + high, + /** Does not get handled unless client is connected. */ + low + } + + /** A set of two booleans, one specifying server and one specifying client. */ + public enum Loc{ + /** Method can only be invoked on the client from the server. */ + server(true, false), + /** Method can only be invoked on the server from the client. */ + client(false, true), + /** Method can be invoked from anywhere */ + both(true, true), + /** Neither server nor client. */ + none(false, false); + + /** If true, this method can be invoked ON clients FROM servers. */ + public final boolean isServer; + /** If true, this method can be invoked ON servers FROM clients. */ + public final boolean isClient; + + Loc(boolean server, boolean client){ + this.isServer = server; + this.isClient = client; + } + } + + public enum Variant{ + /** Method can only be invoked targeting one player. */ + one(true, false), + /** Method can only be invoked targeting all players. */ + all(false, true), + /** Method targets both one player and all players. */ + both(true, true); + + public final boolean isOne, isAll; + + Variant(boolean isOne, boolean isAll){ + this.isOne = isOne; + this.isAll = isAll; + } + } + + /** Marks a method as invokable remotely across a server/client connection. */ + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.SOURCE) + public @interface Remote{ + /** Specifies the locations from which this method can be invoked. */ + Loc targets() default Loc.server; + + /** Specifies which methods are generated. Only affects server-to-client methods. */ + Variant variants() default Variant.all; + + /** The local locations where this method is called locally, when invoked. */ + Loc called() default Loc.none; + + /** Whether to forward this packet to all other clients upon recieval. Client only. */ + boolean forward() default false; + + /** + * Whether the packet for this method is sent with UDP instead of TCP. + * UDP is faster, but is prone to packet loss and duplication. + */ + boolean unreliable() default false; + + /** Priority of this event. */ + PacketPriority priority() default PacketPriority.normal; + } + + /** + * Specifies that this method will be used to write classes of the type returned by {@link #value()}.
+ * This method must return void and have two parameters, the first being of type {@link java.nio.ByteBuffer} and the second + * being the type returned by {@link #value()}. + */ + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.SOURCE) + public @interface WriteClass{ + Class value(); + } + + /** + * Specifies that this method will be used to read classes of the type returned by {@link #value()}.
+ * This method must return the type returned by {@link #value()}, + * and have one parameter, being of type {@link java.nio.ByteBuffer}. + */ + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.SOURCE) + public @interface ReadClass{ + Class value(); + } +} diff --git a/annotations/src/main/java/io/anuke/annotations/CallSuperAnnotationProcessor.java b/annotations/src/main/java/io/anuke/annotations/CallSuperAnnotationProcessor.java new file mode 100644 index 0000000000..1bdc75c786 --- /dev/null +++ b/annotations/src/main/java/io/anuke/annotations/CallSuperAnnotationProcessor.java @@ -0,0 +1,65 @@ +package io.anuke.annotations; + +import com.sun.source.util.TreePath; +import com.sun.source.util.Trees; +import com.sun.tools.javac.tree.JCTree; +import com.sun.tools.javac.tree.JCTree.JCExpressionStatement; +import io.anuke.annotations.Annotations.OverrideCallSuper; + +import javax.annotation.processing.*; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.tools.Diagnostic.Kind; +import java.util.List; +import java.util.Set; + +@SupportedAnnotationTypes("java.lang.Override") +public class CallSuperAnnotationProcessor extends AbstractProcessor{ + private Trees trees; + + @Override + public void init (ProcessingEnvironment pe) { + super.init(pe); + trees = Trees.instance(pe); + } + + public boolean process (Set annotations, RoundEnvironment roundEnv) { + for (Element e : roundEnv.getElementsAnnotatedWith(Override.class)) { + if (e.getAnnotation(OverrideCallSuper.class) != null) return false; + + CodeAnalyzerTreeScanner codeScanner = new CodeAnalyzerTreeScanner(); + codeScanner.setMethodName(e.getSimpleName().toString()); + + TreePath tp = trees.getPath(e.getEnclosingElement()); + codeScanner.scan(tp, trees); + + if (codeScanner.isCallSuperUsed()) { + List list = codeScanner.getMethod().getBody().getStatements(); + + if (!doesCallSuper(list, codeScanner.getMethodName())) { + processingEnv.getMessager().printMessage(Kind.ERROR, "Overriding method '" + codeScanner.getMethodName() + "' must explicitly call super method from its parent class.", e); + } + } + } + + return false; + } + + private boolean doesCallSuper (List list, String methodName) { + for (Object object : list) { + if (object instanceof JCTree.JCExpressionStatement) { + JCTree.JCExpressionStatement expr = (JCExpressionStatement) object; + String exprString = expr.toString(); + if (exprString.startsWith("super." + methodName) && exprString.endsWith(");")) return true; + } + } + + return false; + } + + @Override + public SourceVersion getSupportedSourceVersion () { + return SourceVersion.RELEASE_8; + } +} diff --git a/annotations/src/main/java/io/anuke/annotations/ClassEntry.java b/annotations/src/main/java/io/anuke/annotations/ClassEntry.java new file mode 100644 index 0000000000..a9be2ec134 --- /dev/null +++ b/annotations/src/main/java/io/anuke/annotations/ClassEntry.java @@ -0,0 +1,15 @@ +package io.anuke.annotations; + +import java.util.ArrayList; + +/** Represents a class witha list method entries to include in it. */ +public class ClassEntry{ + /** All methods in this generated class. */ + public final ArrayList methods = new ArrayList<>(); + /** Simple class name. */ + public final String name; + + public ClassEntry(String name){ + this.name = name; + } +} diff --git a/annotations/src/main/java/io/anuke/annotations/CodeAnalyzerTreeScanner.java b/annotations/src/main/java/io/anuke/annotations/CodeAnalyzerTreeScanner.java new file mode 100644 index 0000000000..c2d543e3be --- /dev/null +++ b/annotations/src/main/java/io/anuke/annotations/CodeAnalyzerTreeScanner.java @@ -0,0 +1,98 @@ +package io.anuke.annotations; + +import com.sun.source.tree.*; +import com.sun.source.util.TreePathScanner; +import com.sun.source.util.Trees; +import com.sun.tools.javac.code.Scope; +import com.sun.tools.javac.code.Symbol; +import com.sun.tools.javac.code.Symbol.MethodSymbol; +import com.sun.tools.javac.code.Type.ClassType; +import com.sun.tools.javac.tree.JCTree.JCIdent; +import com.sun.tools.javac.tree.JCTree.JCTypeApply; +import io.anuke.annotations.Annotations.CallSuper; + +import java.lang.annotation.Annotation; + +class CodeAnalyzerTreeScanner extends TreePathScanner { + private String methodName; + private MethodTree method; + private boolean callSuperUsed; + + @Override + public Object visitClass (ClassTree classTree, Trees trees) { + Tree extendTree = classTree.getExtendsClause(); + + if (extendTree instanceof JCTypeApply) { //generic classes case + JCTypeApply generic = (JCTypeApply) extendTree; + extendTree = generic.clazz; + } + + if (extendTree instanceof JCIdent) { + JCIdent tree = (JCIdent) extendTree; + Scope members = tree.sym.members(); + + if (checkScope(members)) + return super.visitClass(classTree, trees); + + if (checkSuperTypes((ClassType) tree.type)) + return super.visitClass(classTree, trees); + + } + callSuperUsed = false; + + return super.visitClass(classTree, trees); + } + + public boolean checkSuperTypes (ClassType type) { + if (type.supertype_field != null && type.supertype_field.tsym != null) { + if (checkScope(type.supertype_field.tsym.members())) + return true; + else + return checkSuperTypes((ClassType) type.supertype_field); + } + + return false; + } + + public boolean checkScope (Scope members) { + for (Symbol s : members.getElements()) { + if (s instanceof MethodSymbol) { + MethodSymbol ms = (MethodSymbol) s; + + if (ms.getSimpleName().toString().equals(methodName)) { + Annotation annotation = ms.getAnnotation(CallSuper.class); + if (annotation != null) { + callSuperUsed = true; + return true; + } + } + } + } + + return false; + } + + @Override + public Object visitMethod (MethodTree methodTree, Trees trees) { + if (methodTree.getName().toString().equals(methodName)) + method = methodTree; + + return super.visitMethod(methodTree, trees); + } + + public void setMethodName (String methodName) { + this.methodName = methodName; + } + + public String getMethodName () { + return methodName; + } + + public MethodTree getMethod () { + return method; + } + + public boolean isCallSuperUsed () { + return callSuperUsed; + } +} \ No newline at end of file diff --git a/annotations/src/main/java/io/anuke/annotations/IOFinder.java b/annotations/src/main/java/io/anuke/annotations/IOFinder.java new file mode 100644 index 0000000000..9276b9b887 --- /dev/null +++ b/annotations/src/main/java/io/anuke/annotations/IOFinder.java @@ -0,0 +1,90 @@ +package io.anuke.annotations; + +import io.anuke.annotations.Annotations.ReadClass; +import io.anuke.annotations.Annotations.WriteClass; + +import javax.annotation.processing.RoundEnvironment; +import javax.lang.model.element.Element; +import javax.lang.model.type.MirroredTypeException; +import javax.tools.Diagnostic.Kind; +import java.util.HashMap; +import java.util.Set; + +/** + * This class finds reader and writer methods annotated by the {@link io.anuke.annotations.Annotations.WriteClass} + * and {@link io.anuke.annotations.Annotations.ReadClass} annotations. + */ +public class IOFinder{ + + /** + * Finds all class serializers for all types and returns them. Logs errors when necessary. + * Maps fully qualified class names to their serializers. + */ + public HashMap findSerializers(RoundEnvironment env){ + HashMap result = new HashMap<>(); + + //get methods with the types + Set writers = env.getElementsAnnotatedWith(WriteClass.class); + Set readers = env.getElementsAnnotatedWith(ReadClass.class); + + //look for writers first + for(Element writer : writers){ + WriteClass writean = writer.getAnnotation(WriteClass.class); + String typeName = getValue(writean); + + //make sure there's only one read method + if(readers.stream().filter(elem -> getValue(elem.getAnnotation(ReadClass.class)).equals(typeName)).count() > 1){ + Utils.messager.printMessage(Kind.ERROR, "Multiple writer methods for type '" + typeName + "'", writer); + } + + //make sure there's only one write method + long count = readers.stream().filter(elem -> getValue(elem.getAnnotation(ReadClass.class)).equals(typeName)).count(); + if(count == 0){ + Utils.messager.printMessage(Kind.ERROR, "Writer method does not have an accompanying reader: ", writer); + }else if(count > 1){ + Utils.messager.printMessage(Kind.ERROR, "Writer method has multiple reader for type: ", writer); + } + + Element reader = readers.stream().filter(elem -> getValue(elem.getAnnotation(ReadClass.class)).equals(typeName)).findFirst().get(); + + //add to result list + result.put(typeName, new ClassSerializer(Utils.getMethodName(reader), Utils.getMethodName(writer), typeName)); + } + + return result; + } + + private String getValue(WriteClass write){ + try{ + Class type = write.value(); + return type.getName(); + }catch(MirroredTypeException e){ + return e.getTypeMirror().toString(); + } + } + + private String getValue(ReadClass read){ + try{ + Class type = read.value(); + return type.getName(); + }catch(MirroredTypeException e){ + return e.getTypeMirror().toString(); + } + } + + /** Information about read/write methods for a specific class type. */ + public static class ClassSerializer{ + /** Fully qualified method name of the reader. */ + public final String readMethod; + /** Fully qualified method name of the writer. */ + public final String writeMethod; + /** Fully qualified class type name. */ + public final String classType; + + public ClassSerializer(String readMethod, String writeMethod, String classType){ + this.readMethod = readMethod; + this.writeMethod = writeMethod; + this.classType = classType; + } + } +} diff --git a/annotations/src/main/java/io/anuke/annotations/MethodEntry.java b/annotations/src/main/java/io/anuke/annotations/MethodEntry.java new file mode 100644 index 0000000000..d642671641 --- /dev/null +++ b/annotations/src/main/java/io/anuke/annotations/MethodEntry.java @@ -0,0 +1,51 @@ +package io.anuke.annotations; + +import io.anuke.annotations.Annotations.*; + +import javax.lang.model.element.ExecutableElement; + +/** Class that repesents a remote method to be constructed and put into a class. */ +public class MethodEntry{ + /** Simple target class name. */ + public final String className; + /** Fully qualified target method to call. */ + public final String targetMethod; + /** Whether this method can be called on a client/server. */ + public final Loc where; + /** + * Whether an additional 'one' and 'all' method variant is generated. At least one of these must be true. + * Only applicable to client (server-invoked) methods. + */ + public final Variant target; + /** Whether this method is called locally as well as remotely. */ + public final Loc local; + /** Whether this method is unreliable and uses UDP. */ + public final boolean unreliable; + /** Whether to forward this method call to all other clients when a client invokes it. Server only. */ + public final boolean forward; + /** Unique method ID. */ + public final int id; + /** The element method associated with this entry. */ + public final ExecutableElement element; + /** The assigned packet priority. Only used in clients. */ + public final PacketPriority priority; + + public MethodEntry(String className, String targetMethod, Loc where, Variant target, + Loc local, boolean unreliable, boolean forward, int id, ExecutableElement element, PacketPriority priority){ + this.className = className; + this.forward = forward; + this.targetMethod = targetMethod; + this.where = where; + this.target = target; + this.local = local; + this.id = id; + this.element = element; + this.unreliable = unreliable; + this.priority = priority; + } + + @Override + public int hashCode(){ + return targetMethod.hashCode(); + } +} diff --git a/annotations/src/main/java/io/anuke/annotations/RemoteMethodAnnotationProcessor.java b/annotations/src/main/java/io/anuke/annotations/RemoteMethodAnnotationProcessor.java new file mode 100644 index 0000000000..b9fbba57a8 --- /dev/null +++ b/annotations/src/main/java/io/anuke/annotations/RemoteMethodAnnotationProcessor.java @@ -0,0 +1,154 @@ +package io.anuke.annotations; + +import com.squareup.javapoet.*; +import io.anuke.annotations.Annotations.Loc; +import io.anuke.annotations.Annotations.Remote; +import io.anuke.annotations.IOFinder.ClassSerializer; + +import javax.annotation.processing.*; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.*; +import javax.tools.Diagnostic.Kind; +import java.util.*; +import java.util.stream.Collectors; + + +/** The annotation processor for generating remote method call code. */ +@SupportedSourceVersion(SourceVersion.RELEASE_8) +@SupportedAnnotationTypes({ +"io.anuke.annotations.Annotations.Remote", +"io.anuke.annotations.Annotations.WriteClass", +"io.anuke.annotations.Annotations.ReadClass", +}) +public class RemoteMethodAnnotationProcessor extends AbstractProcessor{ + /** Maximum size of each event packet. */ + public static final int maxPacketSize = 4096; + /** Warning on top of each autogenerated file. */ + public static final String autogenWarning = "Autogenerated file. Do not modify!\n"; + /** Name of the base package to put all the generated classes. */ + private static final String packageName = "io.anuke.mindustry.gen"; + + /** Name of class that handles reading and invoking packets on the server. */ + private static final String readServerName = "RemoteReadServer"; + /** Name of class that handles reading and invoking packets on the client. */ + private static final String readClientName = "RemoteReadClient"; + /** Simple class name of generated class name. */ + private static final String callLocation = "Call"; + + /** Processing round number. */ + private int round; + + //class serializers + private HashMap serializers; + //all elements with the Remote annotation + private Set elements; + //map of all classes to generate by name + private HashMap classMap; + //list of all method entries + private ArrayList methods; + //list of all method entries + private ArrayList classes; + + @Override + public synchronized void init(ProcessingEnvironment processingEnv){ + super.init(processingEnv); + //put all relevant utils into utils class + Utils.typeUtils = processingEnv.getTypeUtils(); + Utils.elementUtils = processingEnv.getElementUtils(); + Utils.filer = processingEnv.getFiler(); + Utils.messager = processingEnv.getMessager(); + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv){ + if(round > 1) return false; //only process 2 rounds + + round++; + + try{ + + //round 1: find all annotations, generate *writers* + if(round == 1){ + //get serializers + serializers = new IOFinder().findSerializers(roundEnv); + //last method ID used + int lastMethodID = 0; + //find all elements with the Remote annotation + elements = roundEnv.getElementsAnnotatedWith(Remote.class); + //map of all classes to generate by name + classMap = new HashMap<>(); + //list of all method entries + methods = new ArrayList<>(); + //list of all method entries + classes = new ArrayList<>(); + + List orderedElements = new ArrayList<>(elements); + orderedElements.sort(Comparator.comparing(Object::toString)); + + //create methods + for(Element element : orderedElements){ + Remote annotation = element.getAnnotation(Remote.class); + + //check for static + if(!element.getModifiers().contains(Modifier.STATIC) || !element.getModifiers().contains(Modifier.PUBLIC)){ + Utils.messager.printMessage(Kind.ERROR, "All @Remote methods must be public and static: ", element); + } + + //can't generate none methods + if(annotation.targets() == Loc.none){ + Utils.messager.printMessage(Kind.ERROR, "A @Remote method's targets() cannot be equal to 'none':", element); + } + + //get and create class entry if needed + if(!classMap.containsKey(callLocation)){ + ClassEntry clas = new ClassEntry(callLocation); + classMap.put(callLocation, clas); + classes.add(clas); + } + + ClassEntry entry = classMap.get(callLocation); + + //create and add entry + MethodEntry method = new MethodEntry(entry.name, Utils.getMethodName(element), annotation.targets(), annotation.variants(), + annotation.called(), annotation.unreliable(), annotation.forward(), lastMethodID++, (ExecutableElement)element, annotation.priority()); + + entry.methods.add(method); + methods.add(method); + } + + //create read/write generators + RemoteWriteGenerator writegen = new RemoteWriteGenerator(serializers); + + //generate the methods to invoke (write) + writegen.generateFor(classes, packageName); + + return true; + }else if(round == 2){ //round 2: generate all *readers* + RemoteReadGenerator readgen = new RemoteReadGenerator(serializers); + + //generate server readers + readgen.generateFor(methods.stream().filter(method -> method.where.isClient).collect(Collectors.toList()), readServerName, packageName, true); + //generate client readers + readgen.generateFor(methods.stream().filter(method -> method.where.isServer).collect(Collectors.toList()), readClientName, packageName, false); + + //create class for storing unique method hash + TypeSpec.Builder hashBuilder = TypeSpec.classBuilder("MethodHash").addModifiers(Modifier.PUBLIC); + hashBuilder.addJavadoc(autogenWarning); + hashBuilder.addField(FieldSpec.builder(int.class, "HASH", Modifier.STATIC, Modifier.PUBLIC, Modifier.FINAL) + .initializer("$1L", Objects.hash(methods)).build()); + + //build and write resulting hash class + TypeSpec spec = hashBuilder.build(); + JavaFile.builder(packageName, spec).build().writeTo(Utils.filer); + + return true; + } + + }catch(Exception e){ + e.printStackTrace(); + throw new RuntimeException(e); + } + + return false; + } +} diff --git a/annotations/src/main/java/io/anuke/annotations/RemoteReadGenerator.java b/annotations/src/main/java/io/anuke/annotations/RemoteReadGenerator.java new file mode 100644 index 0000000000..d23bd592c9 --- /dev/null +++ b/annotations/src/main/java/io/anuke/annotations/RemoteReadGenerator.java @@ -0,0 +1,144 @@ +package io.anuke.annotations; + +import com.squareup.javapoet.*; +import io.anuke.annotations.IOFinder.ClassSerializer; + +import javax.lang.model.element.*; +import javax.tools.Diagnostic.Kind; +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.List; + +/** Generates code for reading remote invoke packets on the client and server. */ +public class RemoteReadGenerator{ + private final HashMap serializers; + + /** Creates a read generator that uses the supplied serializer setup. */ + public RemoteReadGenerator(HashMap serializers){ + this.serializers = serializers; + } + + /** + * Generates a class for reading remote invoke packets. + * @param entries List of methods to use. + * @param className Simple target class name. + * @param packageName Full target package name. + * @param needsPlayer Whether this read method requires a reference to the player sender. + */ + public void generateFor(List entries, String className, String packageName, boolean needsPlayer) + throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException, IOException{ + + TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className).addModifiers(Modifier.PUBLIC); + classBuilder.addJavadoc(RemoteMethodAnnotationProcessor.autogenWarning); + + //create main method builder + MethodSpec.Builder readMethod = MethodSpec.methodBuilder("readPacket") + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .addParameter(ByteBuffer.class, "buffer") //buffer to read form + .addParameter(int.class, "id") //ID of method type to read + .returns(void.class); + + if(needsPlayer){ + //since the player type isn't loaded yet, creating a type def is necessary + //this requires reflection since the TypeName constructor is private for some reason + Constructor cons = TypeName.class.getDeclaredConstructor(String.class); + cons.setAccessible(true); + + TypeName playerType = cons.newInstance("io.anuke.mindustry.entities.type.Player"); + //add player parameter + readMethod.addParameter(playerType, "player"); + } + + CodeBlock.Builder readBlock = CodeBlock.builder(); //start building block of code inside read method + boolean started = false; //whether an if() statement has been written yet + + for(MethodEntry entry : entries){ + //write if check for this entry ID + if(!started){ + started = true; + readBlock.beginControlFlow("if(id == " + entry.id + ")"); + }else{ + readBlock.nextControlFlow("else if(id == " + entry.id + ")"); + } + + readBlock.beginControlFlow("try"); + + //concatenated list of variable names for method invocation + StringBuilder varResult = new StringBuilder(); + + //go through each parameter + for(int i = 0; i < entry.element.getParameters().size(); i++){ + VariableElement var = entry.element.getParameters().get(i); + + if(!needsPlayer || i != 0){ //if client, skip first parameter since it's always of type player and doesn't need to be read + //full type name of parameter + String typeName = var.asType().toString(); + //name of parameter + String varName = var.getSimpleName().toString(); + //captialized version of type name for reading primitives + String capName = typeName.equals("byte") ? "" : Character.toUpperCase(typeName.charAt(0)) + typeName.substring(1); + + //write primitives automatically + if(Utils.isPrimitive(typeName)){ + if(typeName.equals("boolean")){ + readBlock.addStatement("boolean " + varName + " = buffer.get() == 1"); + }else{ + readBlock.addStatement(typeName + " " + varName + " = buffer.get" + capName + "()"); + } + }else{ + //else, try and find a serializer + ClassSerializer ser = serializers.get(typeName); + + if(ser == null){ //make sure a serializer exists! + Utils.messager.printMessage(Kind.ERROR, "No @ReadClass method to read class type: '" + typeName + "'", var); + return; + } + + //add statement for reading it + readBlock.addStatement(typeName + " " + varName + " = " + ser.readMethod + "(buffer)"); + } + + //append variable name to string builder + varResult.append(var.getSimpleName()); + if(i != entry.element.getParameters().size() - 1) varResult.append(", "); + }else{ + varResult.append("player"); + if(i != entry.element.getParameters().size() - 1) varResult.append(", "); + } + } + + //execute the relevant method before the forward + //if it throws a ValidateException, the method won't be forwarded + readBlock.addStatement("$N." + entry.element.getSimpleName() + "(" + varResult.toString() + ")", ((TypeElement)entry.element.getEnclosingElement()).getQualifiedName().toString()); + + //call forwarded method, don't forward on the client reader + if(entry.forward && entry.where.isServer && needsPlayer){ + //call forwarded method + readBlock.addStatement(packageName + "." + entry.className + "." + entry.element.getSimpleName() + + "__forward(player.con.id" + (varResult.length() == 0 ? "" : ", ") + varResult.toString() + ")"); + } + + readBlock.nextControlFlow("catch (java.lang.Exception e)"); + readBlock.addStatement("throw new java.lang.RuntimeException(\"Failed to to read remote method '" + entry.element.getSimpleName() + "'!\", e)"); + readBlock.endControlFlow(); + } + + //end control flow if necessary + if(started){ + readBlock.nextControlFlow("else"); + readBlock.addStatement("throw new $1N(\"Invalid read method ID: \" + id + \"\")", RuntimeException.class.getName()); //handle invalid method IDs + readBlock.endControlFlow(); + } + + //add block and method to class + readMethod.addCode(readBlock.build()); + classBuilder.addMethod(readMethod.build()); + + //build and write resulting class + TypeSpec spec = classBuilder.build(); + JavaFile.builder(packageName, spec).build().writeTo(Utils.filer); + } +} diff --git a/annotations/src/main/java/io/anuke/annotations/RemoteWriteGenerator.java b/annotations/src/main/java/io/anuke/annotations/RemoteWriteGenerator.java new file mode 100644 index 0000000000..411f8ead86 --- /dev/null +++ b/annotations/src/main/java/io/anuke/annotations/RemoteWriteGenerator.java @@ -0,0 +1,224 @@ +package io.anuke.annotations; + +import com.squareup.javapoet.*; +import io.anuke.annotations.Annotations.Loc; +import io.anuke.annotations.IOFinder.ClassSerializer; + +import javax.lang.model.element.*; +import javax.tools.Diagnostic.Kind; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.HashMap; +import java.util.List; + +/** Generates code for writing remote invoke packets on the client and server. */ +public class RemoteWriteGenerator{ + private final HashMap serializers; + + /** Creates a write generator that uses the supplied serializer setup. */ + public RemoteWriteGenerator(HashMap serializers){ + this.serializers = serializers; + } + + /** Generates all classes in this list. */ + public void generateFor(List entries, String packageName) throws IOException{ + + for(ClassEntry entry : entries){ + //create builder + TypeSpec.Builder classBuilder = TypeSpec.classBuilder(entry.name).addModifiers(Modifier.PUBLIC); + classBuilder.addJavadoc(RemoteMethodAnnotationProcessor.autogenWarning); + + //add temporary write buffer + classBuilder.addField(FieldSpec.builder(ByteBuffer.class, "TEMP_BUFFER", Modifier.STATIC, Modifier.PRIVATE, Modifier.FINAL) + .initializer("ByteBuffer.allocate($1L)", RemoteMethodAnnotationProcessor.maxPacketSize).build()); + + //go through each method entry in this class + for(MethodEntry methodEntry : entry.methods){ + //write the 'send event to all players' variant: always happens for clients, but only happens if 'all' is enabled on the server method + if(methodEntry.where.isClient || methodEntry.target.isAll){ + writeMethodVariant(classBuilder, methodEntry, true, false); + } + + //write the 'send event to one player' variant, which is only applicable on the server + if(methodEntry.where.isServer && methodEntry.target.isOne){ + writeMethodVariant(classBuilder, methodEntry, false, false); + } + + //write the forwarded method version + if(methodEntry.where.isServer && methodEntry.forward){ + writeMethodVariant(classBuilder, methodEntry, true, true); + } + } + + //build and write resulting class + TypeSpec spec = classBuilder.build(); + JavaFile.builder(packageName, spec).build().writeTo(Utils.filer); + } + } + + /** Creates a specific variant for a method entry. */ + private void writeMethodVariant(TypeSpec.Builder classBuilder, MethodEntry methodEntry, boolean toAll, boolean forwarded){ + ExecutableElement elem = methodEntry.element; + + //create builder + MethodSpec.Builder method = MethodSpec.methodBuilder(elem.getSimpleName().toString() + (forwarded ? "__forward" : "")) //add except suffix when forwarding + .addModifiers(Modifier.STATIC, Modifier.SYNCHRONIZED) + .returns(void.class); + + //forwarded methods aren't intended for use, and are not public + if(!forwarded){ + method.addModifiers(Modifier.PUBLIC); + } + + //validate client methods to make sure + if(methodEntry.where.isClient){ + if(elem.getParameters().isEmpty()){ + Utils.messager.printMessage(Kind.ERROR, "Client invoke methods must have a first parameter of type Player.", elem); + return; + } + + if(!elem.getParameters().get(0).asType().toString().equals("io.anuke.mindustry.entities.type.Player")){ + Utils.messager.printMessage(Kind.ERROR, "Client invoke methods should have a first parameter of type Player.", elem); + return; + } + } + + //if toAll is false, it's a 'send to one player' variant, so add the player as a parameter + if(!toAll){ + method.addParameter(int.class, "playerClientID"); + } + + //add sender to ignore + if(forwarded){ + method.addParameter(int.class, "exceptSenderID"); + } + + //call local method if applicable, shouldn't happen when forwarding method as that already happens by default + if(!forwarded && methodEntry.local != Loc.none){ + //add in local checks + if(methodEntry.local != Loc.both){ + method.beginControlFlow("if(" + getCheckString(methodEntry.local) + " || !io.anuke.mindustry.net.Net.active())"); + } + + //concatenate parameters + int index = 0; + StringBuilder results = new StringBuilder(); + for(VariableElement var : elem.getParameters()){ + //special case: calling local-only methods uses the local player + if(index == 0 && methodEntry.where == Loc.client){ + results.append("io.anuke.mindustry.Vars.player"); + }else{ + results.append(var.getSimpleName()); + } + if(index != elem.getParameters().size() - 1) results.append(", "); + index++; + } + + //add the statement to call it + method.addStatement("$N." + elem.getSimpleName() + "(" + results.toString() + ")", + ((TypeElement)elem.getEnclosingElement()).getQualifiedName().toString()); + + if(methodEntry.local != Loc.both){ + method.endControlFlow(); + } + } + + //start control flow to check if it's actually client/server so no netcode is called + method.beginControlFlow("if(" + getCheckString(methodEntry.where) + ")"); + + //add statement to create packet from pool + method.addStatement("$1N packet = $2N.obtain($1N.class, $1N::new)", "io.anuke.mindustry.net.Packets.InvokePacket", "io.anuke.arc.util.pooling.Pools"); + //assign buffer + method.addStatement("packet.writeBuffer = TEMP_BUFFER"); + //assign priority + method.addStatement("packet.priority = (byte)" + methodEntry.priority.ordinal()); + //assign method ID + method.addStatement("packet.type = (byte)" + methodEntry.id); + //rewind buffer + method.addStatement("TEMP_BUFFER.position(0)"); + + for(int i = 0; i < elem.getParameters().size(); i++){ + //first argument is skipped as it is always the player caller + if((!methodEntry.where.isServer/* || methodEntry.mode == Loc.both*/) && i == 0){ + continue; + } + + VariableElement var = elem.getParameters().get(i); + + //add parameter to method + method.addParameter(TypeName.get(var.asType()), var.getSimpleName().toString()); + + //name of parameter + String varName = var.getSimpleName().toString(); + //name of parameter type + String typeName = var.asType().toString(); + //captialized version of type name for writing primitives + String capName = typeName.equals("byte") ? "" : Character.toUpperCase(typeName.charAt(0)) + typeName.substring(1); + //special case: method can be called from anywhere to anywhere + //thus, only write the player when the SERVER is writing data, since the client is the only one who reads it + boolean writePlayerSkipCheck = methodEntry.where == Loc.both && i == 0; + + if(writePlayerSkipCheck){ //write begin check + method.beginControlFlow("if(io.anuke.mindustry.net.Net.server())"); + } + + if(Utils.isPrimitive(typeName)){ //check if it's a primitive, and if so write it + if(typeName.equals("boolean")){ //booleans are special + method.addStatement("TEMP_BUFFER.put(" + varName + " ? (byte)1 : 0)"); + }else{ + method.addStatement("TEMP_BUFFER.put" + + capName + "(" + varName + ")"); + } + }else{ + //else, try and find a serializer + ClassSerializer ser = serializers.get(typeName); + + if(ser == null){ //make sure a serializer exists! + Utils.messager.printMessage(Kind.ERROR, "No @WriteClass method to write class type: '" + typeName + "'", var); + return; + } + + //add statement for writing it + method.addStatement(ser.writeMethod + "(TEMP_BUFFER, " + varName + ")"); + } + + if(writePlayerSkipCheck){ //write end check + method.endControlFlow(); + } + } + + //assign packet length + method.addStatement("packet.writeLength = TEMP_BUFFER.position()"); + + String sendString; + + if(forwarded){ //forward packet + if(!methodEntry.local.isClient){ //if the client doesn't get it called locally, forward it back after validation + sendString = "send("; + }else{ + sendString = "sendExcept(exceptSenderID, "; + } + }else if(toAll){ //send to all players / to server + sendString = "send("; + }else{ //send to specific client from server + sendString = "sendTo(playerClientID, "; + } + + //send the actual packet + method.addStatement("io.anuke.mindustry.net.Net." + sendString + "packet, " + + (methodEntry.unreliable ? "io.anuke.mindustry.net.Net.SendMode.udp" : "io.anuke.mindustry.net.Net.SendMode.tcp") + ")"); + + + //end check for server/client + method.endControlFlow(); + + //add method to class, finally + classBuilder.addMethod(method.build()); + } + + private String getCheckString(Loc loc){ + return loc.isClient && loc.isServer ? "io.anuke.mindustry.net.Net.server() || io.anuke.mindustry.net.Net.client()" : + loc.isClient ? "io.anuke.mindustry.net.Net.client()" : + loc.isServer ? "io.anuke.mindustry.net.Net.server()" : "false"; + } +} diff --git a/annotations/src/main/java/io/anuke/annotations/SerializeAnnotationProcessor.java b/annotations/src/main/java/io/anuke/annotations/SerializeAnnotationProcessor.java new file mode 100644 index 0000000000..1fba8bc972 --- /dev/null +++ b/annotations/src/main/java/io/anuke/annotations/SerializeAnnotationProcessor.java @@ -0,0 +1,128 @@ +package io.anuke.annotations; + +import com.squareup.javapoet.*; +import io.anuke.annotations.Annotations.Serialize; + +import javax.annotation.processing.*; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.*; +import javax.lang.model.util.ElementFilter; +import java.io.*; +import java.lang.reflect.Field; +import java.util.List; +import java.util.Set; + +@SupportedSourceVersion(SourceVersion.RELEASE_8) +@SupportedAnnotationTypes("io.anuke.annotations.Annotations.Serialize") +public class SerializeAnnotationProcessor extends AbstractProcessor{ + /** Target class name. */ + private static final String className = "Serialization"; + /** Name of the base package to put all the generated classes. */ + private static final String packageName = "io.anuke.mindustry.gen"; + + private int round; + + @Override + public synchronized void init(ProcessingEnvironment processingEnv){ + super.init(processingEnv); + //put all relevant utils into utils class + Utils.typeUtils = processingEnv.getTypeUtils(); + Utils.elementUtils = processingEnv.getElementUtils(); + Utils.filer = processingEnv.getFiler(); + Utils.messager = processingEnv.getMessager(); + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv){ + if(round++ != 0) return false; //only process 1 round + + try{ + Set elements = ElementFilter.typesIn(roundEnv.getElementsAnnotatedWith(Serialize.class)); + + TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className).addModifiers(Modifier.PUBLIC); + classBuilder.addAnnotation(AnnotationSpec.builder(SuppressWarnings.class).addMember("value", "\"unchecked\"").build()); + classBuilder.addJavadoc(RemoteMethodAnnotationProcessor.autogenWarning); + + + MethodSpec.Builder method = MethodSpec.methodBuilder("init").addModifiers(Modifier.PUBLIC, Modifier.STATIC); + + for(TypeElement elem : elements){ + TypeName type = TypeName.get(elem.asType()); + String simpleTypeName = type.toString().substring(type.toString().lastIndexOf('.') + 1); + + TypeSpec.Builder serializer = TypeSpec.anonymousClassBuilder("") + .addSuperinterface(ParameterizedTypeName.get( + ClassName.bestGuess("io.anuke.arc.Settings.TypeSerializer"), type)); + + MethodSpec.Builder writeMethod = MethodSpec.methodBuilder("write") + .returns(void.class) + .addParameter(DataOutput.class, "stream") + .addParameter(type, "object") + .addException(IOException.class) + .addModifiers(Modifier.PUBLIC); + + MethodSpec.Builder readMethod = MethodSpec.methodBuilder("read") + .returns(type) + .addParameter(DataInput.class, "stream") + .addException(IOException.class) + .addModifiers(Modifier.PUBLIC); + + readMethod.addStatement("$L object = new $L()", type, type); + + List fields = ElementFilter.fieldsIn(Utils.elementUtils.getAllMembers(elem)); + for(VariableElement field : fields){ + if(field.getModifiers().contains(Modifier.STATIC) || field.getModifiers().contains(Modifier.TRANSIENT) || field.getModifiers().contains(Modifier.PRIVATE)) + continue; + + String name = field.getSimpleName().toString(); + String typeName = Utils.typeUtils.erasure(field.asType()).toString().replace('$', '.'); + String capName = Character.toUpperCase(typeName.charAt(0)) + typeName.substring(1); + + if(field.asType().getKind().isPrimitive()){ + writeMethod.addStatement("stream.write" + capName + "(object." + name + ")"); + readMethod.addStatement("object." + name + "= stream.read" + capName + "()"); + }else{ + writeMethod.addStatement("io.anuke.arc.Core.settings.getSerializer(" + typeName + ".class).write(stream, object." + name + ")"); + readMethod.addStatement("object." + name + " = (" + typeName + ")io.anuke.arc.Core.settings.getSerializer(" + typeName + ".class).read(stream)"); + } + } + + readMethod.addStatement("return object"); + + serializer.addMethod(writeMethod.build()); + serializer.addMethod(readMethod.build()); + + method.addStatement("io.anuke.arc.Core.settings.setSerializer($N, $L)", Utils.elementUtils.getBinaryName(elem).toString().replace('$', '.') + ".class", serializer.build()); + + name(writeMethod, "write" + simpleTypeName); + name(readMethod, "read" + simpleTypeName); + + writeMethod.addModifiers(Modifier.STATIC); + readMethod.addModifiers(Modifier.STATIC); + + classBuilder.addMethod(writeMethod.build()); + classBuilder.addMethod(readMethod.build()); + } + + classBuilder.addMethod(method.build()); + + //write result + JavaFile.builder(packageName, classBuilder.build()).build().writeTo(Utils.filer); + + return true; + }catch(Exception e){ + e.printStackTrace(); + throw new RuntimeException(e); + } + } + + static void name(MethodSpec.Builder builder, String name){ + try{ + Field field = builder.getClass().getDeclaredField("name"); + field.setAccessible(true); + field.set(builder, name); + }catch(Exception e){ + throw new RuntimeException(e); + } + } +} diff --git a/annotations/src/main/java/io/anuke/annotations/StructAnnotationProcessor.java b/annotations/src/main/java/io/anuke/annotations/StructAnnotationProcessor.java new file mode 100644 index 0000000000..fa89b6e132 --- /dev/null +++ b/annotations/src/main/java/io/anuke/annotations/StructAnnotationProcessor.java @@ -0,0 +1,226 @@ +package io.anuke.annotations; + +import com.squareup.javapoet.*; +import io.anuke.annotations.Annotations.Struct; +import io.anuke.annotations.Annotations.StructField; + +import javax.annotation.processing.*; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.*; +import javax.lang.model.type.TypeKind; +import javax.lang.model.util.ElementFilter; +import javax.tools.Diagnostic.Kind; +import java.util.List; +import java.util.Set; + +/** + * Generates ""value types"" classes that are packed into integer primitives of the most aproppriate size. + * It would be nice if Java didn't make crazy hacks like this necessary. + */ +@SupportedSourceVersion(SourceVersion.RELEASE_8) +@SupportedAnnotationTypes({ +"io.anuke.annotations.Annotations.Struct" +}) +public class StructAnnotationProcessor extends AbstractProcessor{ + /** Name of the base package to put all the generated classes. */ + private static final String packageName = "io.anuke.mindustry.gen"; + private int round; + + @Override + public synchronized void init(ProcessingEnvironment processingEnv){ + super.init(processingEnv); + //put all relevant utils into utils class + Utils.typeUtils = processingEnv.getTypeUtils(); + Utils.elementUtils = processingEnv.getElementUtils(); + Utils.filer = processingEnv.getFiler(); + Utils.messager = processingEnv.getMessager(); + } + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv){ + if(round++ != 0) return false; //only process 1 round + + try{ + Set elements = ElementFilter.typesIn(roundEnv.getElementsAnnotatedWith(Struct.class)); + + for(TypeElement elem : elements){ + + if(!elem.getSimpleName().toString().endsWith("Struct")){ + Utils.messager.printMessage(Kind.ERROR, "All classes annotated with @Struct must have their class names end in 'Struct'.", elem); + continue; + } + + String structName = elem.getSimpleName().toString().substring(0, elem.getSimpleName().toString().length() - "Struct".length()); + String structParam = structName.toLowerCase(); + + TypeSpec.Builder classBuilder = TypeSpec.classBuilder(structName) + .addModifiers(Modifier.FINAL, Modifier.PUBLIC); + + try{ + List variables = ElementFilter.fieldsIn(elem.getEnclosedElements()); + int structSize = variables.stream().mapToInt(StructAnnotationProcessor::varSize).sum(); + int structTotalSize = (structSize <= 8 ? 8 : structSize <= 16 ? 16 : structSize <= 32 ? 32 : 64); + + if(variables.size() == 0){ + Utils.messager.printMessage(Kind.ERROR, "making a struct with no fields is utterly pointles.", elem); + continue; + } + + //obtain type which will be stored + Class structType = typeForSize(structSize); + + //[constructor] get(fields...) : structType + MethodSpec.Builder constructor = MethodSpec.methodBuilder("get") + .addModifiers(Modifier.STATIC, Modifier.PUBLIC) + .returns(structType); + + StringBuilder cons = new StringBuilder(); + StringBuilder doc = new StringBuilder(); + doc.append("Bits used: ").append(structSize).append(" / ").append(structTotalSize).append("\n"); + + int offset = 0; + for(VariableElement var : variables){ + int size = varSize(var); + TypeName varType = TypeName.get(var.asType()); + String varName = var.getSimpleName().toString(); + + //add val param to constructor + constructor.addParameter(varType, varName); + + //[get] field(structType) : fieldType + MethodSpec.Builder getter = MethodSpec.methodBuilder(var.getSimpleName().toString()) + .addModifiers(Modifier.STATIC, Modifier.PUBLIC) + .returns(varType) + .addParameter(structType, structParam); + //[set] field(structType, fieldType) : structType + MethodSpec.Builder setter = MethodSpec.methodBuilder(var.getSimpleName().toString()) + .addModifiers(Modifier.STATIC, Modifier.PUBLIC) + .returns(structType) + .addParameter(structType, structParam).addParameter(varType, "value"); + + //[getter] + if(varType == TypeName.BOOLEAN){ + //bools: single bit, is simplified + getter.addStatement("return ($L & (1L << $L)) != 0", structParam, offset); + }else if(varType == TypeName.FLOAT){ + //floats: need conversion + getter.addStatement("return Float.intBitsToFloat((int)(($L >>> $L) & $L))", structParam, offset, bitString(size, structTotalSize)); + }else{ + //bytes, shorts, chars, ints + getter.addStatement("return ($T)(($L >>> $L) & $L)", varType, structParam, offset, bitString(size, structTotalSize)); + } + + //[setter] + [constructor building] + if(varType == TypeName.BOOLEAN){ + cons.append(" | (").append(varName).append(" ? ").append("1L << ").append(offset).append("L : 0)"); + + //bools: single bit, needs special case to clear things + setter.beginControlFlow("if(value)"); + setter.addStatement("return ($T)(($L & ~(1L << $LL)))", structType, structParam, offset); + setter.nextControlFlow("else"); + setter.addStatement("return ($T)(($L & ~(1L << $LL)) | (1L << $LL))", structType, structParam, offset, offset); + setter.endControlFlow(); + }else if(varType == TypeName.FLOAT){ + cons.append(" | (").append("(").append(structType).append(")").append("Float.floatToIntBits(").append(varName).append(") << ").append(offset).append("L)"); + + //floats: need conversion + setter.addStatement("return ($T)(($L & $L) | (($T)Float.floatToIntBits(value) << $LL))", structType, structParam, bitString(offset, size, structTotalSize), structType, offset); + }else{ + cons.append(" | (((").append(structType).append(")").append(varName).append(" << ").append(offset).append("L)").append(" & ").append(bitString(offset, size, structTotalSize)).append(")"); + + //bytes, shorts, chars, ints + setter.addStatement("return ($T)(($L & $L) | (($T)value << $LL))", structType, structParam, bitString(offset, size, structTotalSize), structType, offset); + } + + doc.append("
").append(varName).append(" [").append(offset).append("..").append(size + offset).append("]\n"); + + //add finished methods + classBuilder.addMethod(getter.build()); + classBuilder.addMethod(setter.build()); + + offset += size; + } + + classBuilder.addJavadoc(doc.toString()); + + //add constructor final statement + add to class and build + constructor.addStatement("return ($T)($L)", structType, cons.toString().substring(3)); + classBuilder.addMethod(constructor.build()); + + JavaFile.builder(packageName, classBuilder.build()).build().writeTo(Utils.filer); + }catch(IllegalArgumentException e){ + e.printStackTrace(); + Utils.messager.printMessage(Kind.ERROR, e.getMessage(), elem); + } + } + + return true; + }catch(Exception e){ + e.printStackTrace(); + throw new RuntimeException(e); + } + } + + static String bitString(int offset, int size, int totalSize){ + StringBuilder builder = new StringBuilder(); + for(int i = 0; i < offset; i++) builder.append('0'); + for(int i = 0; i < size; i++) builder.append('1'); + for(int i = 0; i < totalSize - size - offset; i++) builder.append('0'); + return "0b" + builder.reverse().toString() + "L"; + } + + static String bitString(int size, int totalSize){ + StringBuilder builder = new StringBuilder(); + for(int i = 0; i < size; i++) builder.append('1'); + for(int i = 0; i < totalSize - size; i++) builder.append('0'); + return "0b" + builder.reverse().toString() + "L"; + } + + static int varSize(VariableElement var) throws IllegalArgumentException{ + if(!var.asType().getKind().isPrimitive()){ + throw new IllegalArgumentException("All struct fields must be primitives: " + var); + } + + StructField an = var.getAnnotation(StructField.class); + if(var.asType().getKind() == TypeKind.BOOLEAN && an != null && an.value() != 1){ + throw new IllegalArgumentException("Booleans can only be one bit long... why would you do this?"); + } + + if(var.asType().getKind() == TypeKind.FLOAT && an != null && an.value() != 32){ + throw new IllegalArgumentException("Float size can't be changed. Very sad."); + } + + return an == null ? typeSize(var.asType().getKind()) : an.value(); + } + + static Class typeForSize(int size) throws IllegalArgumentException{ + if(size <= 8){ + return byte.class; + }else if(size <= 16){ + return short.class; + }else if(size <= 32){ + return int.class; + }else if(size <= 64){ + return long.class; + } + throw new IllegalArgumentException("Too many fields, must fit in 64 bits. Curent size: " + size); + } + + /** returns a type's element size in bits. */ + static int typeSize(TypeKind kind) throws IllegalArgumentException{ + switch(kind){ + case BOOLEAN: + return 1; + case BYTE: + return 8; + case SHORT: + return 16; + case FLOAT: + case CHAR: + case INT: + return 32; + default: + throw new IllegalArgumentException("Invalid type kind: " + kind + ". Note that doubles and longs are not supported."); + } + } +} diff --git a/annotations/src/main/java/io/anuke/annotations/Utils.java b/annotations/src/main/java/io/anuke/annotations/Utils.java new file mode 100644 index 0000000000..fa54f4c12d --- /dev/null +++ b/annotations/src/main/java/io/anuke/annotations/Utils.java @@ -0,0 +1,24 @@ +package io.anuke.annotations; + +import javax.annotation.processing.Filer; +import javax.annotation.processing.Messager; +import javax.lang.model.element.Element; +import javax.lang.model.element.TypeElement; +import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; + +public class Utils{ + public static Types typeUtils; + public static Elements elementUtils; + public static Filer filer; + public static Messager messager; + + public static String getMethodName(Element element){ + return ((TypeElement)element.getEnclosingElement()).getQualifiedName().toString() + "." + element.getSimpleName(); + } + + public static boolean isPrimitive(String type){ + return type.equals("boolean") || type.equals("byte") || type.equals("short") || type.equals("int") + || type.equals("long") || type.equals("float") || type.equals("double") || type.equals("char"); + } +} diff --git a/annotations/src/main/resources/META-INF/services/javax.annotation.processing.Processor b/annotations/src/main/resources/META-INF/services/javax.annotation.processing.Processor new file mode 100644 index 0000000000..5954bfccb9 --- /dev/null +++ b/annotations/src/main/resources/META-INF/services/javax.annotation.processing.Processor @@ -0,0 +1,4 @@ +io.anuke.annotations.RemoteMethodAnnotationProcessor +io.anuke.annotations.SerializeAnnotationProcessor +io.anuke.annotations.StructAnnotationProcessor +io.anuke.annotations.CallSuperAnnotationProcessor \ No newline at end of file diff --git a/build.gradle b/build.gradle index 8bce5a80b6..d09ef15d71 100644 --- a/build.gradle +++ b/build.gradle @@ -1,37 +1,65 @@ -buildscript { - repositories { +buildscript{ + repositories{ mavenLocal() mavenCentral() google() - maven { url "https://oss.sonatype.org/content/repositories/snapshots/" } + maven{ url "https://oss.sonatype.org/content/repositories/snapshots/" } jcenter() } - dependencies { - classpath 'com.mobidevelop.robovm:robovm-gradle-plugin:2.3.0' - classpath 'de.richsource.gradle.plugins:gwt-gradle-plugin:0.6' - classpath 'com.android.tools.build:gradle:3.0.1' - classpath "com.badlogicgames.gdx:gdx-tools:1.9.8" + + dependencies{ + classpath 'com.mobidevelop.robovm:robovm-gradle-plugin:2.3.6' + classpath "com.badlogicgames.gdx:gdx-tools:1.9.9" + classpath "com.badlogicgames.packr:packr:2.1-SNAPSHOT" } } -allprojects { - apply plugin: "eclipse" - apply plugin: "idea" - +allprojects{ version = 'release' - ext { - versionNumber = '3.5' - versionType = 'release' + ext{ + versionNumber = '4' + versionModifier = 'beta' + if(!project.hasProperty("versionType")) versionType = 'official' appName = 'Mindustry' - gdxVersion = '1.9.8' - roboVMVersion = '2.3.0' - aiVersion = '1.8.1' - uCoreVersion = 'd5af97f93813d8767423521b1fcc5a5e0f7241d9' + gdxVersion = '1.9.9' + roboVMVersion = '2.3.6' + arcHash = null + + debugged = { + return new File(projectDir.parent, '../debug').exists() && !project.hasProperty("release") && project.hasProperty("args") + } + + localArc = { + return (!project.hasProperty("release")) && new File(projectDir.parent, '../Arc').exists() + } + + getArcHash = { + //get latest commit hash from gtihub since JITPack's '-snapshot' version doesn't work correctly + if(arcHash == null){ + try{ + arcHash = 'git ls-remote https://github.com/Anuken/Arc.git'.execute().text.split("\t")[0] + }catch(e){ + e.printStackTrace() + arcHash = "-SNAPSHOT" + } + } + return arcHash + } + + arcModule = { String name -> + if(localArc()){ + return project(":Arc:$name") + }else{ + //skip to last submodule + if(name.contains(':')) name = name.split(':').last() + return "com.github.Anuken.Arc:$name:${getArcHash()}" + } + } getVersionString = { String buildVersion = getBuildVersion() - return "$versionNumber-$versionType-$buildVersion" + return "$versionNumber-$versionModifier-$buildVersion" } getBuildVersion = { @@ -43,159 +71,203 @@ allprojects { return project.ext.mainClassName.substring(0, project.ext.mainClassName.indexOf("desktop") - 1) } + generateLocales = { + def output = 'en\n' + def bundles = new File(project(':core').projectDir, 'assets/bundles/') + bundles.listFiles().each{ other -> + if(other.name == "bundle.properties") return + output += other.name.substring("bundle".length() + 1, other.name.lastIndexOf('.')) + "\n" + } + new File(project(':core').projectDir, 'assets/locales').text = output + } + writeVersion = { - def pfile = new File('core/assets/version.properties') + def pfile = new File(project(':core').projectDir, 'assets/version.properties') def props = new Properties() try{ pfile.createNewFile() - }catch (Exception e){} - - if(pfile.exists()) { + }catch(Exception ignored){ + } + if(pfile.exists()){ props.load(new FileInputStream(pfile)) - String code = getBuildVersion() + String buildid = getBuildVersion() - props["name"] = appName - props["version"] = versionType - props["code"] = versionNumber - props["build"] = code + props["type"] = versionType + props["number"] = versionNumber + props["modifier"] = versionModifier + props["build"] = buildid props.store(pfile.newWriter(), "Autogenerated file. Do not modify.") } } } - repositories { + repositories{ mavenLocal() mavenCentral() - maven { url "https://oss.sonatype.org/content/repositories/snapshots/" } - maven { url "https://oss.sonatype.org/content/repositories/releases/" } - maven { url 'https://jitpack.io' } + maven{ url "https://oss.sonatype.org/content/repositories/snapshots/" } + maven{ url "https://oss.sonatype.org/content/repositories/releases/" } + maven{ url 'https://jitpack.io' } + jcenter() } + + tasks.withType(Javadoc).all{ enabled = false } } -project(":desktop") { +project(":desktop"){ apply plugin: "java" - dependencies { + dependencies{ compile project(":core") - compile project(":kryonet") - compile "com.badlogicgames.gdx:gdx-backend-lwjgl3:$gdxVersion" + compile project(":net") + + if(debugged()) compile project(":debug") + compile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop" - compile "com.badlogicgames.gdx:gdx-controllers-lwjgl3:$gdxVersion" - compile 'com.github.MinnDevelopment:java-discord-rpc:v2.0.0' + compile "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-desktop" + + compile arcModule("backends:backend-lwjgl3") + compile 'com.github.MinnDevelopment:java-discord-rpc:v2.0.2' } } -project(":html") { - apply plugin: "gwt" - apply plugin: "war" - - - dependencies { - compile project(":core") - compile "com.badlogicgames.gdx:gdx-backend-gwt:$gdxVersion" - compile "com.badlogicgames.gdx:gdx:$gdxVersion:sources" - compile "com.badlogicgames.gdx:gdx-backend-gwt:$gdxVersion:sources" - compile "com.badlogicgames.gdx:gdx-ai:$aiVersion:sources" - - compile "com.badlogicgames.gdx:gdx-controllers:$gdxVersion:sources" - compile "com.badlogicgames.gdx:gdx-controllers-gwt:$gdxVersion" - compile "com.badlogicgames.gdx:gdx-controllers-gwt:$gdxVersion:sources" - compile "com.sksamuel.gwt:gwt-websockets:1.0.4" - compile "com.sksamuel.gwt:gwt-websockets:1.0.4:sources" - } -} - -project(":android") { - apply plugin: "android" - - configurations { natives } - - dependencies { - implementation project(":core") - implementation project(":kryonet") - implementation "com.badlogicgames.gdx:gdx-backend-android:$gdxVersion" - natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-armeabi" - natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-armeabi-v7a" - natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-arm64-v8a" - natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-x86" - natives "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-x86_64" - implementation "com.badlogicgames.gdx:gdx-ai:$aiVersion" - implementation "com.badlogicgames.gdx:gdx-controllers-android:$gdxVersion" - } -} - -project(":ios") { +project(":ios"){ apply plugin: "java" apply plugin: "robovm" + task incrementConfig{ + def vfile = file('robovm.properties') - dependencies { + def props = new Properties() + if(vfile.exists()){ + props.load(new FileInputStream(vfile)) + } + + props['app.id'] = 'io.anuke.mindustry' + props['app.version'] = '4.0' + props['app.mainclass'] = 'io.anuke.mindustry.IOSLauncher' + props['app.executable'] = 'IOSLauncher' + props['app.name'] = 'Mindustry' + props['app.build'] = (!props.containsKey("app.build") ? 40 : props['app.build'].toInteger() + 1) + "" + props.store(vfile.newWriter(), null) + } + + dependencies{ compile project(":core") - implementation project(":kryonet") + compile project(":net") + compileOnly project(":annotations") + + compile arcModule("backends:backend-robovm") compile "com.mobidevelop.robovm:robovm-rt:$roboVMVersion" compile "com.mobidevelop.robovm:robovm-cocoatouch:$roboVMVersion" - compile "com.badlogicgames.gdx:gdx-backend-robovm:$gdxVersion" compile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-ios" - } - - robovm { - + compile "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-ios" } } -project(":core") { +project(":core"){ apply plugin: "java" - dependencies { - boolean comp = System.properties["release"] == null || System.properties["release"] == "false" + task finish{ + generateLocales() + } - if(!comp){ - println("NOTICE: Compiling release build.") - }else{ - println("Compiling DEBUG build.") + dependencies{ + if(System.properties["user.name"] == "anuke"){ + task cleanGen{ + doFirst{ + delete{ + delete "../core/src/io/anuke/mindustry/gen/" + } + } + } + + task copyGen{ + doLast{ + copy{ + from("../core/build/generated/sources/annotationProcessor/java/main/io/anuke/mindustry/gen"){ + include "**/*.java" + } + + into "../core/src/io/anuke/mindustry/gen" + } + } + } + + compileJava.dependsOn(cleanGen) + compileJava.finalizedBy(copyGen) } - if(new File('../uCore').exists() && comp){ - compile project(":uCore") - }else{ - compile "com.github.anuken:ucore:$uCoreVersion" - } + compile arcModule("arc-core") + compile arcModule("extensions:freetype") + compile arcModule("extensions:arcnet") + if(localArc() && debugged()) compile arcModule("extensions:recorder") - if(new File('../GDXGifRecorder').exists() && comp) { - compile project(":GDXGifRecorder") - } - compile "com.badlogicgames.gdx:gdx:$gdxVersion" - compile "com.badlogicgames.gdx:gdx-ai:$aiVersion" - compile "com.badlogicgames.gdx:gdx-controllers:$gdxVersion" + compileOnly project(":annotations") + annotationProcessor project(":annotations") } } -project(":server") { +project(":server"){ apply plugin: "java" - dependencies { + dependencies{ compile project(":core") - compile project(":kryonet") - compile "com.badlogicgames.gdx:gdx-backend-headless:$gdxVersion" + compile project(":net") + compile arcModule("backends:backend-headless") + } +} + +project(":tests"){ + apply plugin: "java" + + dependencies{ + testImplementation project(":core") + testImplementation "org.junit.jupiter:junit-jupiter-params:5.3.1" + testImplementation "org.junit.jupiter:junit-jupiter-api:5.3.1" + testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.3.1" + compile arcModule("backends:backend-headless") + } + + test{ + useJUnitPlatform() + workingDir = new File("../core/assets") + } +} + +project(":tools"){ + apply plugin: "java" + + dependencies{ + compile project(":core") + + //for render tests compile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop" + compile "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-desktop" + + compile arcModule("backends:backend-lwjgl3") } } -project(":kryonet") { +project(":annotations"){ apply plugin: "java" - dependencies { - compile project(":core") - compile 'com.github.crykn:kryonet:2.22.1' - compile "org.java-websocket:Java-WebSocket:1.3.7" + dependencies{ + compile 'com.squareup:javapoet:1.11.0' + compile files("${System.getProperty('java.home')}/../lib/tools.jar") } } -tasks.eclipse.doLast { - delete ".project" +project(":net"){ + apply plugin: "java" + + dependencies{ + compile project(":core") + compile "org.lz4:lz4-java:1.4.1" + compile 'com.github.Anuken:WaifUPnP:05eb46bc577fd7674596946ba288c96c0cedd893' + } } diff --git a/core/assets-raw/fonts/Exo2-Regular.ttf b/core/assets-raw/fonts/Exo2-Regular.ttf new file mode 100644 index 0000000000..8e09f6fb43 Binary files /dev/null and b/core/assets-raw/fonts/Exo2-Regular.ttf differ diff --git a/core/assets-raw/fonts/NanumBarunGothic.ttf b/core/assets-raw/fonts/NanumBarunGothic.ttf new file mode 100644 index 0000000000..658cd3c239 Binary files /dev/null and b/core/assets-raw/fonts/NanumBarunGothic.ttf differ diff --git a/core/assets-raw/fonts/OpenSansEmoji.ttf b/core/assets-raw/fonts/OpenSansEmoji.ttf new file mode 100644 index 0000000000..57d86a62bb Binary files /dev/null and b/core/assets-raw/fonts/OpenSansEmoji.ttf differ diff --git a/core/assets-raw/fonts/font_latin.ttf b/core/assets-raw/fonts/font_latin.ttf new file mode 100644 index 0000000000..cebae09c33 Binary files /dev/null and b/core/assets-raw/fonts/font_latin.ttf differ diff --git a/core/assets-raw/sprites/backgrounds/background.png b/core/assets-raw/sprites/backgrounds/background.png deleted file mode 100644 index 016328304f..0000000000 Binary files a/core/assets-raw/sprites/backgrounds/background.png and /dev/null differ diff --git a/core/assets-raw/sprites/blank.png b/core/assets-raw/sprites/blank.png deleted file mode 100644 index 65099a7b1d..0000000000 Binary files a/core/assets-raw/sprites/blank.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/blackrock1.png b/core/assets-raw/sprites/blocks/blackrock1.png deleted file mode 100644 index 8a3b7f166b..0000000000 Binary files a/core/assets-raw/sprites/blocks/blackrock1.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/blackrockshadow1.png b/core/assets-raw/sprites/blocks/blackrockshadow1.png deleted file mode 100644 index fcf89d8970..0000000000 Binary files a/core/assets-raw/sprites/blocks/blackrockshadow1.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/blackstone1.png b/core/assets-raw/sprites/blocks/blackstone1.png deleted file mode 100644 index 42266645c8..0000000000 Binary files a/core/assets-raw/sprites/blocks/blackstone1.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/blackstone2.png b/core/assets-raw/sprites/blocks/blackstone2.png deleted file mode 100644 index e62cc82c15..0000000000 Binary files a/core/assets-raw/sprites/blocks/blackstone2.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/blackstone3.png b/core/assets-raw/sprites/blocks/blackstone3.png deleted file mode 100644 index 10f3b44734..0000000000 Binary files a/core/assets-raw/sprites/blocks/blackstone3.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/blackstoneblock1.png b/core/assets-raw/sprites/blocks/blackstoneblock1.png deleted file mode 100644 index ceac507a11..0000000000 Binary files a/core/assets-raw/sprites/blocks/blackstoneblock1.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/blackstoneblock2.png b/core/assets-raw/sprites/blocks/blackstoneblock2.png deleted file mode 100644 index 0e804da468..0000000000 Binary files a/core/assets-raw/sprites/blocks/blackstoneblock2.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/blackstoneblock3.png b/core/assets-raw/sprites/blocks/blackstoneblock3.png deleted file mode 100644 index ebe4fb638d..0000000000 Binary files a/core/assets-raw/sprites/blocks/blackstoneblock3.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/blackstoneedge.png b/core/assets-raw/sprites/blocks/blackstoneedge.png deleted file mode 100644 index 945f4efdd8..0000000000 Binary files a/core/assets-raw/sprites/blocks/blackstoneedge.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/block-2x2.png b/core/assets-raw/sprites/blocks/block-2x2.png deleted file mode 100644 index 1616811924..0000000000 Binary files a/core/assets-raw/sprites/blocks/block-2x2.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/block-3x3.png b/core/assets-raw/sprites/blocks/block-3x3.png deleted file mode 100644 index 2752d719a0..0000000000 Binary files a/core/assets-raw/sprites/blocks/block-3x3.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/block-middle.png b/core/assets-raw/sprites/blocks/block-middle.png deleted file mode 100644 index 601527a90d..0000000000 Binary files a/core/assets-raw/sprites/blocks/block-middle.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/block.png b/core/assets-raw/sprites/blocks/block.png deleted file mode 100644 index 7fe4b2a3a9..0000000000 Binary files a/core/assets-raw/sprites/blocks/block.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/chainturret-icon.png b/core/assets-raw/sprites/blocks/chainturret-icon.png deleted file mode 100644 index e18f11a28e..0000000000 Binary files a/core/assets-raw/sprites/blocks/chainturret-icon.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/chainturret-icon_old.png b/core/assets-raw/sprites/blocks/chainturret-icon_old.png deleted file mode 100644 index 12e4f5f7d5..0000000000 Binary files a/core/assets-raw/sprites/blocks/chainturret-icon_old.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/chainturret.png b/core/assets-raw/sprites/blocks/chainturret.png deleted file mode 100644 index 5ed8460c85..0000000000 Binary files a/core/assets-raw/sprites/blocks/chainturret.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/chainturret_old.png b/core/assets-raw/sprites/blocks/chainturret_old.png deleted file mode 100644 index 3b03c296f1..0000000000 Binary files a/core/assets-raw/sprites/blocks/chainturret_old.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/coal1.png b/core/assets-raw/sprites/blocks/coal1.png deleted file mode 100644 index 8f784eff3c..0000000000 Binary files a/core/assets-raw/sprites/blocks/coal1.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/coal2.png b/core/assets-raw/sprites/blocks/coal2.png deleted file mode 100644 index 65b531eac5..0000000000 Binary files a/core/assets-raw/sprites/blocks/coal2.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/coal3.png b/core/assets-raw/sprites/blocks/coal3.png deleted file mode 100644 index 1457793e91..0000000000 Binary files a/core/assets-raw/sprites/blocks/coal3.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/coaldrill.png b/core/assets-raw/sprites/blocks/coaldrill.png deleted file mode 100644 index 1f633ad9ec..0000000000 Binary files a/core/assets-raw/sprites/blocks/coaldrill.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/coalgenerator-top.png b/core/assets-raw/sprites/blocks/coalgenerator-top.png deleted file mode 100644 index 211c6afbec..0000000000 Binary files a/core/assets-raw/sprites/blocks/coalgenerator-top.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/coalgenerator.png b/core/assets-raw/sprites/blocks/coalgenerator.png deleted file mode 100644 index 7500e84951..0000000000 Binary files a/core/assets-raw/sprites/blocks/coalgenerator.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/coalpurifier.png b/core/assets-raw/sprites/blocks/coalpurifier.png deleted file mode 100644 index a0ab6216b9..0000000000 Binary files a/core/assets-raw/sprites/blocks/coalpurifier.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/combustiongenerator.png b/core/assets-raw/sprites/blocks/combustiongenerator.png deleted file mode 100644 index 968b4224a7..0000000000 Binary files a/core/assets-raw/sprites/blocks/combustiongenerator.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/compositewall.png b/core/assets-raw/sprites/blocks/compositewall.png deleted file mode 100644 index a4bbc1c9cb..0000000000 Binary files a/core/assets-raw/sprites/blocks/compositewall.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/conduit.png b/core/assets-raw/sprites/blocks/conduit.png deleted file mode 100644 index cccbd3da93..0000000000 Binary files a/core/assets-raw/sprites/blocks/conduit.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/conduitbottom.png b/core/assets-raw/sprites/blocks/conduitbottom.png deleted file mode 100644 index a869a8cdae..0000000000 Binary files a/core/assets-raw/sprites/blocks/conduitbottom.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/conduitliquid.png b/core/assets-raw/sprites/blocks/conduitliquid.png deleted file mode 100644 index d223f13561..0000000000 Binary files a/core/assets-raw/sprites/blocks/conduitliquid.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/conduittop.png b/core/assets-raw/sprites/blocks/conduittop.png deleted file mode 100644 index 8a9894c8b5..0000000000 Binary files a/core/assets-raw/sprites/blocks/conduittop.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/conveyor.png b/core/assets-raw/sprites/blocks/conveyor.png deleted file mode 100644 index 7f7f7e47d5..0000000000 Binary files a/core/assets-raw/sprites/blocks/conveyor.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/conveyormove.png b/core/assets-raw/sprites/blocks/conveyormove.png deleted file mode 100644 index 2976fc1ae9..0000000000 Binary files a/core/assets-raw/sprites/blocks/conveyormove.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/conveyortunnel.png b/core/assets-raw/sprites/blocks/conveyortunnel.png deleted file mode 100644 index f1f4bf4e48..0000000000 Binary files a/core/assets-raw/sprites/blocks/conveyortunnel.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/core.png b/core/assets-raw/sprites/blocks/core.png deleted file mode 100644 index 44f0512a37..0000000000 Binary files a/core/assets-raw/sprites/blocks/core.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/cross.png b/core/assets-raw/sprites/blocks/cross.png deleted file mode 100644 index c3350ae4e2..0000000000 Binary files a/core/assets-raw/sprites/blocks/cross.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/crucible.png b/core/assets-raw/sprites/blocks/crucible.png deleted file mode 100644 index 42cb0367a3..0000000000 Binary files a/core/assets-raw/sprites/blocks/crucible.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/deepwater.png b/core/assets-raw/sprites/blocks/deepwater.png deleted file mode 100644 index 373e04e03e..0000000000 Binary files a/core/assets-raw/sprites/blocks/deepwater.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/defense/force-projector-top.png b/core/assets-raw/sprites/blocks/defense/force-projector-top.png new file mode 100644 index 0000000000..78a2d51a4e Binary files /dev/null and b/core/assets-raw/sprites/blocks/defense/force-projector-top.png differ diff --git a/core/assets-raw/sprites/blocks/defense/force-projector.png b/core/assets-raw/sprites/blocks/defense/force-projector.png new file mode 100644 index 0000000000..282bb537a9 Binary files /dev/null and b/core/assets-raw/sprites/blocks/defense/force-projector.png differ diff --git a/core/assets-raw/sprites/blocks/defense/mend-projector-top.png b/core/assets-raw/sprites/blocks/defense/mend-projector-top.png new file mode 100644 index 0000000000..8fa8a2da3a Binary files /dev/null and b/core/assets-raw/sprites/blocks/defense/mend-projector-top.png differ diff --git a/core/assets-raw/sprites/blocks/defense/mend-projector.png b/core/assets-raw/sprites/blocks/defense/mend-projector.png new file mode 100644 index 0000000000..eb06e9501e Binary files /dev/null and b/core/assets-raw/sprites/blocks/defense/mend-projector.png differ diff --git a/core/assets-raw/sprites/blocks/defense/mender-top.png b/core/assets-raw/sprites/blocks/defense/mender-top.png new file mode 100644 index 0000000000..36015652c7 Binary files /dev/null and b/core/assets-raw/sprites/blocks/defense/mender-top.png differ diff --git a/core/assets-raw/sprites/blocks/defense/mender.png b/core/assets-raw/sprites/blocks/defense/mender.png new file mode 100644 index 0000000000..8c539251fb Binary files /dev/null and b/core/assets-raw/sprites/blocks/defense/mender.png differ diff --git a/core/assets-raw/sprites/blocks/defense/overdrive-projector-top.png b/core/assets-raw/sprites/blocks/defense/overdrive-projector-top.png new file mode 100644 index 0000000000..83d1e772b4 Binary files /dev/null and b/core/assets-raw/sprites/blocks/defense/overdrive-projector-top.png differ diff --git a/core/assets-raw/sprites/blocks/defense/overdrive-projector.png b/core/assets-raw/sprites/blocks/defense/overdrive-projector.png new file mode 100644 index 0000000000..6ff5f9bed7 Binary files /dev/null and b/core/assets-raw/sprites/blocks/defense/overdrive-projector.png differ diff --git a/core/assets-raw/sprites/blocks/defense/shock-mine.png b/core/assets-raw/sprites/blocks/defense/shock-mine.png new file mode 100644 index 0000000000..41f624b06a Binary files /dev/null and b/core/assets-raw/sprites/blocks/defense/shock-mine.png differ diff --git a/core/assets-raw/sprites/blocks/dirt1.png b/core/assets-raw/sprites/blocks/dirt1.png deleted file mode 100644 index 29aa55a74b..0000000000 Binary files a/core/assets-raw/sprites/blocks/dirt1.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/dirt2.png b/core/assets-raw/sprites/blocks/dirt2.png deleted file mode 100644 index 993c3071e2..0000000000 Binary files a/core/assets-raw/sprites/blocks/dirt2.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/dirt3.png b/core/assets-raw/sprites/blocks/dirt3.png deleted file mode 100644 index cd2ae7103d..0000000000 Binary files a/core/assets-raw/sprites/blocks/dirt3.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/dirtedge.png b/core/assets-raw/sprites/blocks/dirtedge.png deleted file mode 100644 index 8b39940e2d..0000000000 Binary files a/core/assets-raw/sprites/blocks/dirtedge.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/distribution/bridge-arrow.png b/core/assets-raw/sprites/blocks/distribution/bridge-arrow.png new file mode 100644 index 0000000000..e478d18d76 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/bridge-arrow.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/bridge-conveyor-arrow.png b/core/assets-raw/sprites/blocks/distribution/bridge-conveyor-arrow.png new file mode 100644 index 0000000000..acf07f760a Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/bridge-conveyor-arrow.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/bridge-conveyor-bridge.png b/core/assets-raw/sprites/blocks/distribution/bridge-conveyor-bridge.png new file mode 100644 index 0000000000..1ae054b4a0 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/bridge-conveyor-bridge.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/bridge-conveyor-end.png b/core/assets-raw/sprites/blocks/distribution/bridge-conveyor-end.png new file mode 100644 index 0000000000..b553fc29a7 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/bridge-conveyor-end.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/bridge-conveyor.png b/core/assets-raw/sprites/blocks/distribution/bridge-conveyor.png new file mode 100644 index 0000000000..afe8a5b17c Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/bridge-conveyor.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/center.png b/core/assets-raw/sprites/blocks/distribution/center.png new file mode 100644 index 0000000000..ef9b77ae97 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/center.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-0-0.png b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-0-0.png new file mode 100644 index 0000000000..2e0a43ea3b Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-0-0.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-0-1.png b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-0-1.png new file mode 100644 index 0000000000..24048d1a43 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-0-1.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-0-2.png b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-0-2.png new file mode 100644 index 0000000000..73b23fc35b Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-0-2.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-0-3.png b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-0-3.png new file mode 100644 index 0000000000..6306aa871a Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-0-3.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-1-0.png b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-1-0.png new file mode 100644 index 0000000000..5227b0a105 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-1-0.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-1-1.png b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-1-1.png new file mode 100644 index 0000000000..d7807ed213 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-1-1.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-1-2.png b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-1-2.png new file mode 100644 index 0000000000..809e73f91e Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-1-2.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-1-3.png b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-1-3.png new file mode 100644 index 0000000000..cbbcc06765 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-1-3.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-2-0.png b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-2-0.png new file mode 100644 index 0000000000..7d83503a4c Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-2-0.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-2-1.png b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-2-1.png new file mode 100644 index 0000000000..7c096f931d Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-2-1.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-2-2.png b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-2-2.png new file mode 100644 index 0000000000..8f3bc82e43 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-2-2.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-2-3.png b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-2-3.png new file mode 100644 index 0000000000..2ae54594cb Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-2-3.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-3-0.png b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-3-0.png new file mode 100644 index 0000000000..4ab7a2b834 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-3-0.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-3-1.png b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-3-1.png new file mode 100644 index 0000000000..078385b52e Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-3-1.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-3-2.png b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-3-2.png new file mode 100644 index 0000000000..353aed8146 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-3-2.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-3-3.png b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-3-3.png new file mode 100644 index 0000000000..b0ca93a9ef Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-3-3.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-4-0.png b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-4-0.png new file mode 100644 index 0000000000..7cf6b7a50c Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-4-0.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-4-1.png b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-4-1.png new file mode 100644 index 0000000000..20edd79d73 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-4-1.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-4-2.png b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-4-2.png new file mode 100644 index 0000000000..d2857c3270 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-4-2.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-4-3.png b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-4-3.png new file mode 100644 index 0000000000..2533da2648 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/conveyor-4-3.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-0-0.png b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-0-0.png new file mode 100644 index 0000000000..8b950f97f8 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-0-0.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-0-1.png b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-0-1.png new file mode 100644 index 0000000000..3254dc8756 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-0-1.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-0-2.png b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-0-2.png new file mode 100644 index 0000000000..7f69cf0669 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-0-2.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-0-3.png b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-0-3.png new file mode 100644 index 0000000000..8675cc94d0 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-0-3.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-1-0.png b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-1-0.png new file mode 100644 index 0000000000..1329d5537d Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-1-0.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-1-1.png b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-1-1.png new file mode 100644 index 0000000000..ee5360c7fe Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-1-1.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-1-2.png b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-1-2.png new file mode 100644 index 0000000000..92c8101998 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-1-2.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-1-3.png b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-1-3.png new file mode 100644 index 0000000000..427ffeacd3 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-1-3.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-2-0.png b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-2-0.png new file mode 100644 index 0000000000..9a3d6926f6 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-2-0.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-2-1.png b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-2-1.png new file mode 100644 index 0000000000..9961efb201 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-2-1.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-2-2.png b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-2-2.png new file mode 100644 index 0000000000..14ba76c0fa Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-2-2.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-2-3.png b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-2-3.png new file mode 100644 index 0000000000..c9f72835e5 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-2-3.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-3-0.png b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-3-0.png new file mode 100644 index 0000000000..2707bc9288 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-3-0.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-3-1.png b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-3-1.png new file mode 100644 index 0000000000..352665be11 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-3-1.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-3-2.png b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-3-2.png new file mode 100644 index 0000000000..4ce88b7534 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-3-2.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-3-3.png b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-3-3.png new file mode 100644 index 0000000000..a555b6b661 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-3-3.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-4-0.png b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-4-0.png new file mode 100644 index 0000000000..da3dafa312 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-4-0.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-4-1.png b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-4-1.png new file mode 100644 index 0000000000..9e040e9590 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-4-1.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-4-2.png b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-4-2.png new file mode 100644 index 0000000000..a2b0ee6665 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-4-2.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-4-3.png b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-4-3.png new file mode 100644 index 0000000000..6a96832452 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/conveyors/titanium-conveyor-4-3.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/distributor.png b/core/assets-raw/sprites/blocks/distribution/distributor.png new file mode 100644 index 0000000000..4669dbc99e Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/distributor.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/junction.png b/core/assets-raw/sprites/blocks/distribution/junction.png new file mode 100644 index 0000000000..f973535bfb Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/junction.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/mass-driver-base.png b/core/assets-raw/sprites/blocks/distribution/mass-driver-base.png new file mode 100644 index 0000000000..da1f082874 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/mass-driver-base.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/mass-driver.png b/core/assets-raw/sprites/blocks/distribution/mass-driver.png new file mode 100644 index 0000000000..8f835d1a40 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/mass-driver.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/overflow-gate.png b/core/assets-raw/sprites/blocks/distribution/overflow-gate.png new file mode 100644 index 0000000000..3b47f57453 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/overflow-gate.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/phase-conveyor-arrow.png b/core/assets-raw/sprites/blocks/distribution/phase-conveyor-arrow.png new file mode 100644 index 0000000000..d514df7dfa Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/phase-conveyor-arrow.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/phase-conveyor-bridge.png b/core/assets-raw/sprites/blocks/distribution/phase-conveyor-bridge.png new file mode 100644 index 0000000000..176c3fc9ce Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/phase-conveyor-bridge.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/phase-conveyor-end.png b/core/assets-raw/sprites/blocks/distribution/phase-conveyor-end.png new file mode 100644 index 0000000000..090155a3f4 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/phase-conveyor-end.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/phase-conveyor.png b/core/assets-raw/sprites/blocks/distribution/phase-conveyor.png new file mode 100644 index 0000000000..e2fca93c08 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/phase-conveyor.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/router.png b/core/assets-raw/sprites/blocks/distribution/router.png new file mode 100644 index 0000000000..61201a705a Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/router.png differ diff --git a/core/assets-raw/sprites/blocks/distribution/sorter.png b/core/assets-raw/sprites/blocks/distribution/sorter.png new file mode 100644 index 0000000000..03876f3138 Binary files /dev/null and b/core/assets-raw/sprites/blocks/distribution/sorter.png differ diff --git a/core/assets-raw/sprites/blocks/door-large-icon.png b/core/assets-raw/sprites/blocks/door-large-icon.png deleted file mode 100644 index 87dd1a0fdf..0000000000 Binary files a/core/assets-raw/sprites/blocks/door-large-icon.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/door-large-open.png b/core/assets-raw/sprites/blocks/door-large-open.png deleted file mode 100644 index 42037513d6..0000000000 Binary files a/core/assets-raw/sprites/blocks/door-large-open.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/door-large.png b/core/assets-raw/sprites/blocks/door-large.png deleted file mode 100644 index a40f67f0be..0000000000 Binary files a/core/assets-raw/sprites/blocks/door-large.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/door-open.png b/core/assets-raw/sprites/blocks/door-open.png deleted file mode 100644 index dafd8a137b..0000000000 Binary files a/core/assets-raw/sprites/blocks/door-open.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/door.png b/core/assets-raw/sprites/blocks/door.png deleted file mode 100644 index bcbe7a5d03..0000000000 Binary files a/core/assets-raw/sprites/blocks/door.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/doubleturret.png b/core/assets-raw/sprites/blocks/doubleturret.png deleted file mode 100644 index b31f837139..0000000000 Binary files a/core/assets-raw/sprites/blocks/doubleturret.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/drills/blast-drill-rim.png b/core/assets-raw/sprites/blocks/drills/blast-drill-rim.png new file mode 100644 index 0000000000..b71305c856 Binary files /dev/null and b/core/assets-raw/sprites/blocks/drills/blast-drill-rim.png differ diff --git a/core/assets-raw/sprites/blocks/drills/blast-drill-rotator.png b/core/assets-raw/sprites/blocks/drills/blast-drill-rotator.png new file mode 100644 index 0000000000..98a2834240 Binary files /dev/null and b/core/assets-raw/sprites/blocks/drills/blast-drill-rotator.png differ diff --git a/core/assets-raw/sprites/blocks/drills/blast-drill-top.png b/core/assets-raw/sprites/blocks/drills/blast-drill-top.png new file mode 100644 index 0000000000..189c04a372 Binary files /dev/null and b/core/assets-raw/sprites/blocks/drills/blast-drill-top.png differ diff --git a/core/assets-raw/sprites/blocks/drills/blast-drill.png b/core/assets-raw/sprites/blocks/drills/blast-drill.png new file mode 100644 index 0000000000..18697e720f Binary files /dev/null and b/core/assets-raw/sprites/blocks/drills/blast-drill.png differ diff --git a/core/assets-raw/sprites/blocks/drills/laser-drill-rim.png b/core/assets-raw/sprites/blocks/drills/laser-drill-rim.png new file mode 100644 index 0000000000..3db73498f5 Binary files /dev/null and b/core/assets-raw/sprites/blocks/drills/laser-drill-rim.png differ diff --git a/core/assets-raw/sprites/blocks/drills/laser-drill-rotator.png b/core/assets-raw/sprites/blocks/drills/laser-drill-rotator.png new file mode 100644 index 0000000000..44aa88d6b8 Binary files /dev/null and b/core/assets-raw/sprites/blocks/drills/laser-drill-rotator.png differ diff --git a/core/assets-raw/sprites/blocks/drills/laser-drill-top.png b/core/assets-raw/sprites/blocks/drills/laser-drill-top.png new file mode 100644 index 0000000000..bc2c136aea Binary files /dev/null and b/core/assets-raw/sprites/blocks/drills/laser-drill-top.png differ diff --git a/core/assets-raw/sprites/blocks/drills/laser-drill.png b/core/assets-raw/sprites/blocks/drills/laser-drill.png new file mode 100644 index 0000000000..95cbc0a8e0 Binary files /dev/null and b/core/assets-raw/sprites/blocks/drills/laser-drill.png differ diff --git a/core/assets-raw/sprites/blocks/drills/mechanical-drill-rotator.png b/core/assets-raw/sprites/blocks/drills/mechanical-drill-rotator.png new file mode 100644 index 0000000000..23fe1726e8 Binary files /dev/null and b/core/assets-raw/sprites/blocks/drills/mechanical-drill-rotator.png differ diff --git a/core/assets-raw/sprites/blocks/drills/mechanical-drill-top.png b/core/assets-raw/sprites/blocks/drills/mechanical-drill-top.png new file mode 100644 index 0000000000..3c38f1b6ad Binary files /dev/null and b/core/assets-raw/sprites/blocks/drills/mechanical-drill-top.png differ diff --git a/core/assets-raw/sprites/blocks/drills/mechanical-drill.png b/core/assets-raw/sprites/blocks/drills/mechanical-drill.png new file mode 100644 index 0000000000..8b888ae205 Binary files /dev/null and b/core/assets-raw/sprites/blocks/drills/mechanical-drill.png differ diff --git a/core/assets-raw/sprites/blocks/drills/oil-extractor-liquid.png b/core/assets-raw/sprites/blocks/drills/oil-extractor-liquid.png new file mode 100644 index 0000000000..9a058e3fc9 Binary files /dev/null and b/core/assets-raw/sprites/blocks/drills/oil-extractor-liquid.png differ diff --git a/core/assets-raw/sprites/blocks/drills/oil-extractor-rotator.png b/core/assets-raw/sprites/blocks/drills/oil-extractor-rotator.png new file mode 100644 index 0000000000..a076b412c0 Binary files /dev/null and b/core/assets-raw/sprites/blocks/drills/oil-extractor-rotator.png differ diff --git a/core/assets-raw/sprites/blocks/drills/oil-extractor-top.png b/core/assets-raw/sprites/blocks/drills/oil-extractor-top.png new file mode 100644 index 0000000000..fd1d1046a2 Binary files /dev/null and b/core/assets-raw/sprites/blocks/drills/oil-extractor-top.png differ diff --git a/core/assets-raw/sprites/blocks/drills/oil-extractor.png b/core/assets-raw/sprites/blocks/drills/oil-extractor.png new file mode 100644 index 0000000000..8568a087e4 Binary files /dev/null and b/core/assets-raw/sprites/blocks/drills/oil-extractor.png differ diff --git a/core/assets-raw/sprites/blocks/drills/pneumatic-drill-rotator.png b/core/assets-raw/sprites/blocks/drills/pneumatic-drill-rotator.png new file mode 100644 index 0000000000..b9b498872d Binary files /dev/null and b/core/assets-raw/sprites/blocks/drills/pneumatic-drill-rotator.png differ diff --git a/core/assets-raw/sprites/blocks/drills/pneumatic-drill-top.png b/core/assets-raw/sprites/blocks/drills/pneumatic-drill-top.png new file mode 100644 index 0000000000..6b9dd1f20e Binary files /dev/null and b/core/assets-raw/sprites/blocks/drills/pneumatic-drill-top.png differ diff --git a/core/assets-raw/sprites/blocks/drills/pneumatic-drill.png b/core/assets-raw/sprites/blocks/drills/pneumatic-drill.png new file mode 100644 index 0000000000..05e6832c37 Binary files /dev/null and b/core/assets-raw/sprites/blocks/drills/pneumatic-drill.png differ diff --git a/core/assets-raw/sprites/blocks/drills/water-extractor-liquid.png b/core/assets-raw/sprites/blocks/drills/water-extractor-liquid.png new file mode 100644 index 0000000000..2ac1564812 Binary files /dev/null and b/core/assets-raw/sprites/blocks/drills/water-extractor-liquid.png differ diff --git a/core/assets-raw/sprites/blocks/drills/water-extractor-rotator.png b/core/assets-raw/sprites/blocks/drills/water-extractor-rotator.png new file mode 100644 index 0000000000..c20a0aea4c Binary files /dev/null and b/core/assets-raw/sprites/blocks/drills/water-extractor-rotator.png differ diff --git a/core/assets-raw/sprites/blocks/drills/water-extractor-top.png b/core/assets-raw/sprites/blocks/drills/water-extractor-top.png new file mode 100644 index 0000000000..088e510dc5 Binary files /dev/null and b/core/assets-raw/sprites/blocks/drills/water-extractor-top.png differ diff --git a/core/assets-raw/sprites/blocks/drills/water-extractor.png b/core/assets-raw/sprites/blocks/drills/water-extractor.png new file mode 100644 index 0000000000..61030e7560 Binary files /dev/null and b/core/assets-raw/sprites/blocks/drills/water-extractor.png differ diff --git a/core/assets-raw/sprites/blocks/duriumwall-large-icon.png b/core/assets-raw/sprites/blocks/duriumwall-large-icon.png deleted file mode 100644 index 02b6bc82ce..0000000000 Binary files a/core/assets-raw/sprites/blocks/duriumwall-large-icon.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/duriumwall-large.png b/core/assets-raw/sprites/blocks/duriumwall-large.png deleted file mode 100644 index 7aa89afbd9..0000000000 Binary files a/core/assets-raw/sprites/blocks/duriumwall-large.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/duriumwall.png b/core/assets-raw/sprites/blocks/duriumwall.png deleted file mode 100644 index cdb08fd654..0000000000 Binary files a/core/assets-raw/sprites/blocks/duriumwall.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/enemyspawn.png b/core/assets-raw/sprites/blocks/enemyspawn.png deleted file mode 100644 index e401b6eaef..0000000000 Binary files a/core/assets-raw/sprites/blocks/enemyspawn.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/environment/char1.png b/core/assets-raw/sprites/blocks/environment/char1.png new file mode 100644 index 0000000000..d8a1dae14f Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/char1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/char2.png b/core/assets-raw/sprites/blocks/environment/char2.png new file mode 100644 index 0000000000..c37787ba4f Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/char2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/char3.png b/core/assets-raw/sprites/blocks/environment/char3.png new file mode 100644 index 0000000000..c45e69823a Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/char3.png differ diff --git a/core/assets-raw/sprites/blocks/environment/cliffs1.png b/core/assets-raw/sprites/blocks/environment/cliffs1.png new file mode 100644 index 0000000000..52b99e2de6 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/cliffs1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/coal1.png b/core/assets-raw/sprites/blocks/environment/coal1.png new file mode 100644 index 0000000000..a65216e00d Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/coal1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/coal2.png b/core/assets-raw/sprites/blocks/environment/coal2.png new file mode 100644 index 0000000000..9cf4691919 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/coal2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/coal3.png b/core/assets-raw/sprites/blocks/environment/coal3.png new file mode 100644 index 0000000000..5f2cf4730f Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/coal3.png differ diff --git a/core/assets-raw/sprites/blocks/environment/copper1.png b/core/assets-raw/sprites/blocks/environment/copper1.png new file mode 100644 index 0000000000..c7b4dcc9ed Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/copper1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/copper2.png b/core/assets-raw/sprites/blocks/environment/copper2.png new file mode 100644 index 0000000000..d62a9f201a Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/copper2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/copper3.png b/core/assets-raw/sprites/blocks/environment/copper3.png new file mode 100644 index 0000000000..47128d4f81 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/copper3.png differ diff --git a/core/assets-raw/sprites/blocks/environment/craters1.png b/core/assets-raw/sprites/blocks/environment/craters1.png new file mode 100644 index 0000000000..4b2172bc61 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/craters1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/craters2.png b/core/assets-raw/sprites/blocks/environment/craters2.png new file mode 100644 index 0000000000..1170ab3c4f Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/craters2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/craters3.png b/core/assets-raw/sprites/blocks/environment/craters3.png new file mode 100644 index 0000000000..9a23d383f7 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/craters3.png differ diff --git a/core/assets-raw/sprites/blocks/environment/craters4.png b/core/assets-raw/sprites/blocks/environment/craters4.png new file mode 100644 index 0000000000..3d77e3080f Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/craters4.png differ diff --git a/core/assets-raw/sprites/blocks/environment/craters5.png b/core/assets-raw/sprites/blocks/environment/craters5.png new file mode 100644 index 0000000000..d163324486 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/craters5.png differ diff --git a/core/assets-raw/sprites/blocks/environment/craters6.png b/core/assets-raw/sprites/blocks/environment/craters6.png new file mode 100644 index 0000000000..a504bcf640 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/craters6.png differ diff --git a/core/assets-raw/sprites/blocks/environment/dark-metal-large.png b/core/assets-raw/sprites/blocks/environment/dark-metal-large.png new file mode 100644 index 0000000000..2b30255f53 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/dark-metal-large.png differ diff --git a/core/assets-raw/sprites/blocks/environment/dark-metal1.png b/core/assets-raw/sprites/blocks/environment/dark-metal1.png new file mode 100644 index 0000000000..907b253157 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/dark-metal1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/dark-metal2.png b/core/assets-raw/sprites/blocks/environment/dark-metal2.png new file mode 100644 index 0000000000..3eb3558f6c Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/dark-metal2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/dark-panel-1.png b/core/assets-raw/sprites/blocks/environment/dark-panel-1.png new file mode 100644 index 0000000000..3035832574 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/dark-panel-1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/dark-panel-2.png b/core/assets-raw/sprites/blocks/environment/dark-panel-2.png new file mode 100644 index 0000000000..cead3de9bc Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/dark-panel-2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/dark-panel-3.png b/core/assets-raw/sprites/blocks/environment/dark-panel-3.png new file mode 100644 index 0000000000..bf321e76a6 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/dark-panel-3.png differ diff --git a/core/assets-raw/sprites/blocks/environment/dark-panel-4.png b/core/assets-raw/sprites/blocks/environment/dark-panel-4.png new file mode 100644 index 0000000000..0a76f30595 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/dark-panel-4.png differ diff --git a/core/assets-raw/sprites/blocks/environment/dark-panel-5.png b/core/assets-raw/sprites/blocks/environment/dark-panel-5.png new file mode 100644 index 0000000000..3f3c511fe4 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/dark-panel-5.png differ diff --git a/core/assets-raw/sprites/blocks/environment/dark-panel-6.png b/core/assets-raw/sprites/blocks/environment/dark-panel-6.png new file mode 100644 index 0000000000..6ff46b0373 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/dark-panel-6.png differ diff --git a/core/assets-raw/sprites/blocks/environment/darksand-tainted-water.png b/core/assets-raw/sprites/blocks/environment/darksand-tainted-water.png new file mode 100644 index 0000000000..fce4fce352 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/darksand-tainted-water.png differ diff --git a/core/assets-raw/sprites/blocks/environment/darksand-water.png b/core/assets-raw/sprites/blocks/environment/darksand-water.png new file mode 100644 index 0000000000..9074571f83 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/darksand-water.png differ diff --git a/core/assets-raw/sprites/blocks/environment/darksand1.png b/core/assets-raw/sprites/blocks/environment/darksand1.png new file mode 100644 index 0000000000..0478f5a78d Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/darksand1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/darksand2.png b/core/assets-raw/sprites/blocks/environment/darksand2.png new file mode 100644 index 0000000000..2ee445aba9 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/darksand2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/darksand3.png b/core/assets-raw/sprites/blocks/environment/darksand3.png new file mode 100644 index 0000000000..f6e829199b Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/darksand3.png differ diff --git a/core/assets-raw/sprites/blocks/environment/deepwater.png b/core/assets-raw/sprites/blocks/environment/deepwater.png new file mode 100644 index 0000000000..2389c5985d Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/deepwater.png differ diff --git a/core/assets-raw/sprites/blocks/environment/dunerocks-large.png b/core/assets-raw/sprites/blocks/environment/dunerocks-large.png new file mode 100644 index 0000000000..23082592c7 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/dunerocks-large.png differ diff --git a/core/assets-raw/sprites/blocks/environment/dunerocks1.png b/core/assets-raw/sprites/blocks/environment/dunerocks1.png new file mode 100644 index 0000000000..923945dd74 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/dunerocks1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/dunerocks2.png b/core/assets-raw/sprites/blocks/environment/dunerocks2.png new file mode 100644 index 0000000000..85783da449 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/dunerocks2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/edge-stencil.png b/core/assets-raw/sprites/blocks/environment/edge-stencil.png new file mode 100644 index 0000000000..4d45e75fde Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/edge-stencil.png differ diff --git a/core/assets-raw/sprites/blocks/environment/edge.png b/core/assets-raw/sprites/blocks/environment/edge.png new file mode 100644 index 0000000000..566dd28288 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/edge.png differ diff --git a/core/assets-raw/sprites/blocks/environment/edgier.png b/core/assets-raw/sprites/blocks/environment/edgier.png new file mode 100644 index 0000000000..98d675f453 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/edgier.png differ diff --git a/core/assets-raw/sprites/blocks/environment/grass1.png b/core/assets-raw/sprites/blocks/environment/grass1.png new file mode 100644 index 0000000000..adf810314d Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/grass1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/grass2.png b/core/assets-raw/sprites/blocks/environment/grass2.png new file mode 100644 index 0000000000..2f37e91369 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/grass2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/grass3.png b/core/assets-raw/sprites/blocks/environment/grass3.png new file mode 100644 index 0000000000..704219e7f9 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/grass3.png differ diff --git a/core/assets-raw/sprites/blocks/environment/holostone1.png b/core/assets-raw/sprites/blocks/environment/holostone1.png new file mode 100644 index 0000000000..cc68b49c90 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/holostone1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/holostone2.png b/core/assets-raw/sprites/blocks/environment/holostone2.png new file mode 100644 index 0000000000..1c74d19a1b Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/holostone2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/holostone3.png b/core/assets-raw/sprites/blocks/environment/holostone3.png new file mode 100644 index 0000000000..008f5a85f1 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/holostone3.png differ diff --git a/core/assets-raw/sprites/blocks/environment/hotrock1.png b/core/assets-raw/sprites/blocks/environment/hotrock1.png new file mode 100644 index 0000000000..d331a9b07c Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/hotrock1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/hotrock2.png b/core/assets-raw/sprites/blocks/environment/hotrock2.png new file mode 100644 index 0000000000..5bfe3110eb Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/hotrock2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/hotrock3.png b/core/assets-raw/sprites/blocks/environment/hotrock3.png new file mode 100644 index 0000000000..1d37b221a3 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/hotrock3.png differ diff --git a/core/assets-raw/sprites/blocks/environment/ice-snow1.png b/core/assets-raw/sprites/blocks/environment/ice-snow1.png new file mode 100644 index 0000000000..d671587375 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/ice-snow1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/ice-snow2.png b/core/assets-raw/sprites/blocks/environment/ice-snow2.png new file mode 100644 index 0000000000..115a8a08a2 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/ice-snow2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/ice-snow3.png b/core/assets-raw/sprites/blocks/environment/ice-snow3.png new file mode 100644 index 0000000000..944fb54588 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/ice-snow3.png differ diff --git a/core/assets-raw/sprites/blocks/environment/ice1.png b/core/assets-raw/sprites/blocks/environment/ice1.png new file mode 100644 index 0000000000..7e3bfcedfa Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/ice1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/ice2.png b/core/assets-raw/sprites/blocks/environment/ice2.png new file mode 100644 index 0000000000..9a2643a694 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/ice2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/ice3.png b/core/assets-raw/sprites/blocks/environment/ice3.png new file mode 100644 index 0000000000..08821cc029 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/ice3.png differ diff --git a/core/assets-raw/sprites/blocks/environment/icerocks-large.png b/core/assets-raw/sprites/blocks/environment/icerocks-large.png new file mode 100644 index 0000000000..4a9ffd8e52 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/icerocks-large.png differ diff --git a/core/assets-raw/sprites/blocks/environment/icerocks1.png b/core/assets-raw/sprites/blocks/environment/icerocks1.png new file mode 100644 index 0000000000..8e11fd1ddf Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/icerocks1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/icerocks2.png b/core/assets-raw/sprites/blocks/environment/icerocks2.png new file mode 100644 index 0000000000..9b2cd18d2f Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/icerocks2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/ignarock1.png b/core/assets-raw/sprites/blocks/environment/ignarock1.png new file mode 100644 index 0000000000..fff2086d21 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/ignarock1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/ignarock2.png b/core/assets-raw/sprites/blocks/environment/ignarock2.png new file mode 100644 index 0000000000..da2c7412b7 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/ignarock2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/ignarock3.png b/core/assets-raw/sprites/blocks/environment/ignarock3.png new file mode 100644 index 0000000000..e303b57082 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/ignarock3.png differ diff --git a/core/assets-raw/sprites/blocks/environment/lead1.png b/core/assets-raw/sprites/blocks/environment/lead1.png new file mode 100644 index 0000000000..ea20a9261d Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/lead1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/lead2.png b/core/assets-raw/sprites/blocks/environment/lead2.png new file mode 100644 index 0000000000..7741575e00 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/lead2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/lead3.png b/core/assets-raw/sprites/blocks/environment/lead3.png new file mode 100644 index 0000000000..c7e5ab676a Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/lead3.png differ diff --git a/core/assets-raw/sprites/blocks/environment/magmarock1.png b/core/assets-raw/sprites/blocks/environment/magmarock1.png new file mode 100644 index 0000000000..a25af14de1 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/magmarock1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/magmarock2.png b/core/assets-raw/sprites/blocks/environment/magmarock2.png new file mode 100644 index 0000000000..7360fb1690 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/magmarock2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/magmarock3.png b/core/assets-raw/sprites/blocks/environment/magmarock3.png new file mode 100644 index 0000000000..7516823fc6 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/magmarock3.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-floor-2.png b/core/assets-raw/sprites/blocks/environment/metal-floor-2.png new file mode 100644 index 0000000000..563338725b Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-floor-2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-floor-3.png b/core/assets-raw/sprites/blocks/environment/metal-floor-3.png new file mode 100644 index 0000000000..7fbb9243b4 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-floor-3.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-floor-5.png b/core/assets-raw/sprites/blocks/environment/metal-floor-5.png new file mode 100644 index 0000000000..ed2bc67e17 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-floor-5.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-floor-damaged1.png b/core/assets-raw/sprites/blocks/environment/metal-floor-damaged1.png new file mode 100644 index 0000000000..8b1e45793d Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-floor-damaged1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-floor-damaged2.png b/core/assets-raw/sprites/blocks/environment/metal-floor-damaged2.png new file mode 100644 index 0000000000..5af2f8fbd2 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-floor-damaged2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-floor-damaged3.png b/core/assets-raw/sprites/blocks/environment/metal-floor-damaged3.png new file mode 100644 index 0000000000..fe999c003c Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-floor-damaged3.png differ diff --git a/core/assets-raw/sprites/blocks/environment/metal-floor.png b/core/assets-raw/sprites/blocks/environment/metal-floor.png new file mode 100644 index 0000000000..d0085b65d9 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/metal-floor.png differ diff --git a/core/assets-raw/sprites/blocks/environment/moss1.png b/core/assets-raw/sprites/blocks/environment/moss1.png new file mode 100644 index 0000000000..800bb05170 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/moss1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/moss2.png b/core/assets-raw/sprites/blocks/environment/moss2.png new file mode 100644 index 0000000000..2d338650e1 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/moss2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/moss3.png b/core/assets-raw/sprites/blocks/environment/moss3.png new file mode 100644 index 0000000000..e840a1936a Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/moss3.png differ diff --git a/core/assets-raw/sprites/blocks/environment/pack.json b/core/assets-raw/sprites/blocks/environment/pack.json new file mode 100644 index 0000000000..2612f17acc --- /dev/null +++ b/core/assets-raw/sprites/blocks/environment/pack.json @@ -0,0 +1,8 @@ +{ + duplicatePadding: true, + combineSubdirectories: true, + flattenPaths: true, + maxWidth: 2048, + maxHeight: 2048, + fast: true +} diff --git a/core/assets-raw/sprites/blocks/environment/pack_fallback.json b/core/assets-raw/sprites/blocks/environment/pack_fallback.json new file mode 100644 index 0000000000..853c7fc32f --- /dev/null +++ b/core/assets-raw/sprites/blocks/environment/pack_fallback.json @@ -0,0 +1,8 @@ +{ + duplicatePadding: true, + combineSubdirectories: true, + flattenPaths: true, + maxWidth: 1024, + maxHeight: 1024, + fast: true +} diff --git a/core/assets-raw/sprites/blocks/environment/pebbles1.png b/core/assets-raw/sprites/blocks/environment/pebbles1.png new file mode 100644 index 0000000000..825833c5b6 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/pebbles1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/pebbles2.png b/core/assets-raw/sprites/blocks/environment/pebbles2.png new file mode 100644 index 0000000000..075f05dfcd Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/pebbles2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/pebbles3.png b/core/assets-raw/sprites/blocks/environment/pebbles3.png new file mode 100644 index 0000000000..846f5fbc3f Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/pebbles3.png differ diff --git a/core/assets-raw/sprites/blocks/environment/pine.png b/core/assets-raw/sprites/blocks/environment/pine.png new file mode 100644 index 0000000000..1642bbbfd0 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/pine.png differ diff --git a/core/assets-raw/sprites/blocks/environment/rock1.png b/core/assets-raw/sprites/blocks/environment/rock1.png new file mode 100644 index 0000000000..75d81ad163 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/rock1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/rock2.png b/core/assets-raw/sprites/blocks/environment/rock2.png new file mode 100644 index 0000000000..b4a900c4fd Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/rock2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/rocks-large.png b/core/assets-raw/sprites/blocks/environment/rocks-large.png new file mode 100644 index 0000000000..b967a8a367 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/rocks-large.png differ diff --git a/core/assets-raw/sprites/blocks/environment/rocks1.png b/core/assets-raw/sprites/blocks/environment/rocks1.png new file mode 100644 index 0000000000..7179dbe50d Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/rocks1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/rocks2.png b/core/assets-raw/sprites/blocks/environment/rocks2.png new file mode 100644 index 0000000000..9b83808267 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/rocks2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/salt.png b/core/assets-raw/sprites/blocks/environment/salt.png new file mode 100644 index 0000000000..df9fd72915 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/salt.png differ diff --git a/core/assets-raw/sprites/blocks/environment/saltrocks-large.png b/core/assets-raw/sprites/blocks/environment/saltrocks-large.png new file mode 100644 index 0000000000..f0ec9c74f3 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/saltrocks-large.png differ diff --git a/core/assets-raw/sprites/blocks/environment/saltrocks1.png b/core/assets-raw/sprites/blocks/environment/saltrocks1.png new file mode 100644 index 0000000000..5ad770404e Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/saltrocks1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/saltrocks2.png b/core/assets-raw/sprites/blocks/environment/saltrocks2.png new file mode 100644 index 0000000000..b44d62eba4 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/saltrocks2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/sand-water.png b/core/assets-raw/sprites/blocks/environment/sand-water.png new file mode 100644 index 0000000000..4ac2222ed3 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/sand-water.png differ diff --git a/core/assets-raw/sprites/blocks/environment/sand1.png b/core/assets-raw/sprites/blocks/environment/sand1.png new file mode 100644 index 0000000000..ae6ca2d3be Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/sand1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/sand2.png b/core/assets-raw/sprites/blocks/environment/sand2.png new file mode 100644 index 0000000000..2d6b23f129 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/sand2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/sand3.png b/core/assets-raw/sprites/blocks/environment/sand3.png new file mode 100644 index 0000000000..b03562c494 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/sand3.png differ diff --git a/core/assets-raw/sprites/blocks/environment/sandrocks-large.png b/core/assets-raw/sprites/blocks/environment/sandrocks-large.png new file mode 100644 index 0000000000..ff3efd9395 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/sandrocks-large.png differ diff --git a/core/assets-raw/sprites/blocks/environment/sandrocks1.png b/core/assets-raw/sprites/blocks/environment/sandrocks1.png new file mode 100644 index 0000000000..2217bbd5e0 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/sandrocks1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/sandrocks2.png b/core/assets-raw/sprites/blocks/environment/sandrocks2.png new file mode 100644 index 0000000000..b2dacd5abf Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/sandrocks2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/scrap1.png b/core/assets-raw/sprites/blocks/environment/scrap1.png new file mode 100644 index 0000000000..9fb8fb03f2 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/scrap1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/scrap2.png b/core/assets-raw/sprites/blocks/environment/scrap2.png new file mode 100644 index 0000000000..f133d159f5 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/scrap2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/scrap3.png b/core/assets-raw/sprites/blocks/environment/scrap3.png new file mode 100644 index 0000000000..f602404e34 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/scrap3.png differ diff --git a/core/assets-raw/sprites/blocks/environment/shale-boulder1.png b/core/assets-raw/sprites/blocks/environment/shale-boulder1.png new file mode 100644 index 0000000000..00cc2f2ccb Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/shale-boulder1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/shale-boulder2.png b/core/assets-raw/sprites/blocks/environment/shale-boulder2.png new file mode 100644 index 0000000000..e03c20b62c Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/shale-boulder2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/shale1.png b/core/assets-raw/sprites/blocks/environment/shale1.png new file mode 100644 index 0000000000..6f242df279 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/shale1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/shale2.png b/core/assets-raw/sprites/blocks/environment/shale2.png new file mode 100644 index 0000000000..2edf629f90 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/shale2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/shale3.png b/core/assets-raw/sprites/blocks/environment/shale3.png new file mode 100644 index 0000000000..95382a191c Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/shale3.png differ diff --git a/core/assets-raw/sprites/blocks/environment/shalerocks-large.png b/core/assets-raw/sprites/blocks/environment/shalerocks-large.png new file mode 100644 index 0000000000..325945d278 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/shalerocks-large.png differ diff --git a/core/assets-raw/sprites/blocks/environment/shalerocks1.png b/core/assets-raw/sprites/blocks/environment/shalerocks1.png new file mode 100644 index 0000000000..f489ee1d03 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/shalerocks1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/shalerocks2.png b/core/assets-raw/sprites/blocks/environment/shalerocks2.png new file mode 100644 index 0000000000..356255acb4 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/shalerocks2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/shrubs-large.png b/core/assets-raw/sprites/blocks/environment/shrubs-large.png new file mode 100644 index 0000000000..dd0b6612d9 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/shrubs-large.png differ diff --git a/core/assets-raw/sprites/blocks/environment/shrubs1.png b/core/assets-raw/sprites/blocks/environment/shrubs1.png new file mode 100644 index 0000000000..caff49a543 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/shrubs1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/shrubs2.png b/core/assets-raw/sprites/blocks/environment/shrubs2.png new file mode 100644 index 0000000000..a4f583d86f Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/shrubs2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/snow1.png b/core/assets-raw/sprites/blocks/environment/snow1.png new file mode 100644 index 0000000000..39f893df24 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/snow1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/snow2.png b/core/assets-raw/sprites/blocks/environment/snow2.png new file mode 100644 index 0000000000..3d1a76608d Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/snow2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/snow3.png b/core/assets-raw/sprites/blocks/environment/snow3.png new file mode 100644 index 0000000000..c52f98cdae Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/snow3.png differ diff --git a/core/assets-raw/sprites/blocks/environment/snowrock1.png b/core/assets-raw/sprites/blocks/environment/snowrock1.png new file mode 100644 index 0000000000..d81d328b9c Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/snowrock1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/snowrock2.png b/core/assets-raw/sprites/blocks/environment/snowrock2.png new file mode 100644 index 0000000000..99a4b14ce0 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/snowrock2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/snowrocks-large.png b/core/assets-raw/sprites/blocks/environment/snowrocks-large.png new file mode 100644 index 0000000000..01cfa406f8 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/snowrocks-large.png differ diff --git a/core/assets-raw/sprites/blocks/environment/snowrocks1.png b/core/assets-raw/sprites/blocks/environment/snowrocks1.png new file mode 100644 index 0000000000..32bb74348d Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/snowrocks1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/snowrocks2.png b/core/assets-raw/sprites/blocks/environment/snowrocks2.png new file mode 100644 index 0000000000..8ddeafa1ff Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/snowrocks2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/spawn.png b/core/assets-raw/sprites/blocks/environment/spawn.png new file mode 100644 index 0000000000..dc1a4c30a7 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/spawn.png differ diff --git a/core/assets-raw/sprites/blocks/environment/spore-cluster1.png b/core/assets-raw/sprites/blocks/environment/spore-cluster1.png new file mode 100644 index 0000000000..9e3738039b Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/spore-cluster1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/spore-cluster2.png b/core/assets-raw/sprites/blocks/environment/spore-cluster2.png new file mode 100644 index 0000000000..4aaa8d86e3 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/spore-cluster2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/spore-cluster3.png b/core/assets-raw/sprites/blocks/environment/spore-cluster3.png new file mode 100644 index 0000000000..57462c7762 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/spore-cluster3.png differ diff --git a/core/assets-raw/sprites/blocks/environment/spore-moss1.png b/core/assets-raw/sprites/blocks/environment/spore-moss1.png new file mode 100644 index 0000000000..b1e7fbbf34 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/spore-moss1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/spore-moss2.png b/core/assets-raw/sprites/blocks/environment/spore-moss2.png new file mode 100644 index 0000000000..cf05c4279e Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/spore-moss2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/spore-moss3.png b/core/assets-raw/sprites/blocks/environment/spore-moss3.png new file mode 100644 index 0000000000..8b519681f9 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/spore-moss3.png differ diff --git a/core/assets-raw/sprites/blocks/environment/spore-pine.png b/core/assets-raw/sprites/blocks/environment/spore-pine.png new file mode 100644 index 0000000000..661c4caeb7 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/spore-pine.png differ diff --git a/core/assets-raw/sprites/blocks/environment/sporerocks-large.png b/core/assets-raw/sprites/blocks/environment/sporerocks-large.png new file mode 100644 index 0000000000..4153ac2798 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/sporerocks-large.png differ diff --git a/core/assets-raw/sprites/blocks/environment/sporerocks1.png b/core/assets-raw/sprites/blocks/environment/sporerocks1.png new file mode 100644 index 0000000000..9672003dfb Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/sporerocks1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/sporerocks2.png b/core/assets-raw/sprites/blocks/environment/sporerocks2.png new file mode 100644 index 0000000000..668e3d2cc6 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/sporerocks2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/stone1.png b/core/assets-raw/sprites/blocks/environment/stone1.png new file mode 100644 index 0000000000..e9f42a172d Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/stone1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/stone2.png b/core/assets-raw/sprites/blocks/environment/stone2.png new file mode 100644 index 0000000000..09bc9f3e5a Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/stone2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/stone3.png b/core/assets-raw/sprites/blocks/environment/stone3.png new file mode 100644 index 0000000000..ca2487d441 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/stone3.png differ diff --git a/core/assets-raw/sprites/blocks/environment/tainted-water.png b/core/assets-raw/sprites/blocks/environment/tainted-water.png new file mode 100644 index 0000000000..1618a1b0e1 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/tainted-water.png differ diff --git a/core/assets-raw/sprites/blocks/environment/tar.png b/core/assets-raw/sprites/blocks/environment/tar.png new file mode 100644 index 0000000000..8ad3f8ec68 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/tar.png differ diff --git a/core/assets-raw/sprites/blocks/environment/tendrils1.png b/core/assets-raw/sprites/blocks/environment/tendrils1.png new file mode 100644 index 0000000000..904eb41418 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/tendrils1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/tendrils2.png b/core/assets-raw/sprites/blocks/environment/tendrils2.png new file mode 100644 index 0000000000..895843750c Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/tendrils2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/tendrils3.png b/core/assets-raw/sprites/blocks/environment/tendrils3.png new file mode 100644 index 0000000000..d71b9bc1c7 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/tendrils3.png differ diff --git a/core/assets-raw/sprites/blocks/environment/thorium1.png b/core/assets-raw/sprites/blocks/environment/thorium1.png new file mode 100644 index 0000000000..ce0de1271c Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/thorium1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/thorium2.png b/core/assets-raw/sprites/blocks/environment/thorium2.png new file mode 100644 index 0000000000..9372bf6f81 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/thorium2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/thorium3.png b/core/assets-raw/sprites/blocks/environment/thorium3.png new file mode 100644 index 0000000000..de787e98a6 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/thorium3.png differ diff --git a/core/assets-raw/sprites/blocks/environment/titanium1.png b/core/assets-raw/sprites/blocks/environment/titanium1.png new file mode 100644 index 0000000000..9ba087608a Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/titanium1.png differ diff --git a/core/assets-raw/sprites/blocks/environment/titanium2.png b/core/assets-raw/sprites/blocks/environment/titanium2.png new file mode 100644 index 0000000000..c6add6ad54 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/titanium2.png differ diff --git a/core/assets-raw/sprites/blocks/environment/titanium3.png b/core/assets-raw/sprites/blocks/environment/titanium3.png new file mode 100644 index 0000000000..946f55ab46 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/titanium3.png differ diff --git a/core/assets-raw/sprites/blocks/environment/water.png b/core/assets-raw/sprites/blocks/environment/water.png new file mode 100644 index 0000000000..631f99201a Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/water.png differ diff --git a/core/assets-raw/sprites/blocks/environment/white-tree-dead.png b/core/assets-raw/sprites/blocks/environment/white-tree-dead.png new file mode 100644 index 0000000000..4831e1879e Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/white-tree-dead.png differ diff --git a/core/assets-raw/sprites/blocks/environment/white-tree.png b/core/assets-raw/sprites/blocks/environment/white-tree.png new file mode 100644 index 0000000000..af2b61c5ca Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/white-tree.png differ diff --git a/core/assets-raw/sprites/blocks/extra/block-border.png b/core/assets-raw/sprites/blocks/extra/block-border.png new file mode 100644 index 0000000000..415c5a6849 Binary files /dev/null and b/core/assets-raw/sprites/blocks/extra/block-border.png differ diff --git a/core/assets-raw/sprites/blocks/extra/block-middle.png b/core/assets-raw/sprites/blocks/extra/block-middle.png new file mode 100644 index 0000000000..2a856aef48 Binary files /dev/null and b/core/assets-raw/sprites/blocks/extra/block-middle.png differ diff --git a/core/assets-raw/sprites/blocks/extra/block-select.png b/core/assets-raw/sprites/blocks/extra/block-select.png new file mode 100644 index 0000000000..f2c77caa2b Binary files /dev/null and b/core/assets-raw/sprites/blocks/extra/block-select.png differ diff --git a/core/assets-raw/sprites/blocks/extra/conduit-liquid.png b/core/assets-raw/sprites/blocks/extra/conduit-liquid.png new file mode 100644 index 0000000000..24d37b73c8 Binary files /dev/null and b/core/assets-raw/sprites/blocks/extra/conduit-liquid.png differ diff --git a/core/assets-raw/sprites/blocks/extra/place-arrow.png b/core/assets-raw/sprites/blocks/extra/place-arrow.png new file mode 100644 index 0000000000..5ae905b23f Binary files /dev/null and b/core/assets-raw/sprites/blocks/extra/place-arrow.png differ diff --git a/core/assets-raw/sprites/blocks/extra/rubble-1-0.png b/core/assets-raw/sprites/blocks/extra/rubble-1-0.png new file mode 100644 index 0000000000..6da7d9e583 Binary files /dev/null and b/core/assets-raw/sprites/blocks/extra/rubble-1-0.png differ diff --git a/core/assets-raw/sprites/blocks/extra/rubble-1-1.png b/core/assets-raw/sprites/blocks/extra/rubble-1-1.png new file mode 100644 index 0000000000..354f448d7e Binary files /dev/null and b/core/assets-raw/sprites/blocks/extra/rubble-1-1.png differ diff --git a/core/assets-raw/sprites/blocks/extra/rubble-2-0.png b/core/assets-raw/sprites/blocks/extra/rubble-2-0.png new file mode 100644 index 0000000000..406e27a2f9 Binary files /dev/null and b/core/assets-raw/sprites/blocks/extra/rubble-2-0.png differ diff --git a/core/assets-raw/sprites/blocks/extra/rubble-2-1.png b/core/assets-raw/sprites/blocks/extra/rubble-2-1.png new file mode 100644 index 0000000000..57e13f6c02 Binary files /dev/null and b/core/assets-raw/sprites/blocks/extra/rubble-2-1.png differ diff --git a/core/assets-raw/sprites/blocks/extra/rubble-3-0.png b/core/assets-raw/sprites/blocks/extra/rubble-3-0.png new file mode 100644 index 0000000000..83853df7a2 Binary files /dev/null and b/core/assets-raw/sprites/blocks/extra/rubble-3-0.png differ diff --git a/core/assets-raw/sprites/blocks/extra/rubble-3-1.png b/core/assets-raw/sprites/blocks/extra/rubble-3-1.png new file mode 100644 index 0000000000..83853df7a2 Binary files /dev/null and b/core/assets-raw/sprites/blocks/extra/rubble-3-1.png differ diff --git a/core/assets-raw/sprites/blocks/extra/rubble-4-0.png b/core/assets-raw/sprites/blocks/extra/rubble-4-0.png new file mode 100644 index 0000000000..f937d25c8e Binary files /dev/null and b/core/assets-raw/sprites/blocks/extra/rubble-4-0.png differ diff --git a/core/assets-raw/sprites/blocks/extra/rubble-4-1.png b/core/assets-raw/sprites/blocks/extra/rubble-4-1.png new file mode 100644 index 0000000000..f937d25c8e Binary files /dev/null and b/core/assets-raw/sprites/blocks/extra/rubble-4-1.png differ diff --git a/core/assets-raw/sprites/blocks/flameturret.png b/core/assets-raw/sprites/blocks/flameturret.png deleted file mode 100644 index 734af9bed1..0000000000 Binary files a/core/assets-raw/sprites/blocks/flameturret.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/fluxpump.png b/core/assets-raw/sprites/blocks/fluxpump.png deleted file mode 100644 index 3ed76c6a32..0000000000 Binary files a/core/assets-raw/sprites/blocks/fluxpump.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/grass1.png b/core/assets-raw/sprites/blocks/grass1.png deleted file mode 100644 index 68768bb0d2..0000000000 Binary files a/core/assets-raw/sprites/blocks/grass1.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/grass2.png b/core/assets-raw/sprites/blocks/grass2.png deleted file mode 100644 index 9b17dd53a9..0000000000 Binary files a/core/assets-raw/sprites/blocks/grass2.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/grass3.png b/core/assets-raw/sprites/blocks/grass3.png deleted file mode 100644 index 6896fa10c2..0000000000 Binary files a/core/assets-raw/sprites/blocks/grass3.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/grassblock1.png b/core/assets-raw/sprites/blocks/grassblock1.png deleted file mode 100644 index cceb8b43d1..0000000000 Binary files a/core/assets-raw/sprites/blocks/grassblock1.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/grassblock2.png b/core/assets-raw/sprites/blocks/grassblock2.png deleted file mode 100644 index a66ee2ab2b..0000000000 Binary files a/core/assets-raw/sprites/blocks/grassblock2.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/grassedge.png b/core/assets-raw/sprites/blocks/grassedge.png deleted file mode 100644 index 32b6f87c0f..0000000000 Binary files a/core/assets-raw/sprites/blocks/grassedge.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/ice1.png b/core/assets-raw/sprites/blocks/ice1.png deleted file mode 100644 index 9dbb8ae6d4..0000000000 Binary files a/core/assets-raw/sprites/blocks/ice1.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/ice2.png b/core/assets-raw/sprites/blocks/ice2.png deleted file mode 100644 index 9a742ed753..0000000000 Binary files a/core/assets-raw/sprites/blocks/ice2.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/ice3.png b/core/assets-raw/sprites/blocks/ice3.png deleted file mode 100644 index 7e05199777..0000000000 Binary files a/core/assets-raw/sprites/blocks/ice3.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/iceedge.png b/core/assets-raw/sprites/blocks/iceedge.png deleted file mode 100644 index 3e8a2382a0..0000000000 Binary files a/core/assets-raw/sprites/blocks/iceedge.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/icerock1.png b/core/assets-raw/sprites/blocks/icerock1.png deleted file mode 100644 index 88d318c1a4..0000000000 Binary files a/core/assets-raw/sprites/blocks/icerock1.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/icerock2.png b/core/assets-raw/sprites/blocks/icerock2.png deleted file mode 100644 index fe695e87a1..0000000000 Binary files a/core/assets-raw/sprites/blocks/icerock2.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/icerockshadow1.png b/core/assets-raw/sprites/blocks/icerockshadow1.png deleted file mode 100644 index 0b44bf9a7d..0000000000 Binary files a/core/assets-raw/sprites/blocks/icerockshadow1.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/icerockshadow2.png b/core/assets-raw/sprites/blocks/icerockshadow2.png deleted file mode 100644 index b5a8023d6c..0000000000 Binary files a/core/assets-raw/sprites/blocks/icerockshadow2.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/iron1.png b/core/assets-raw/sprites/blocks/iron1.png deleted file mode 100644 index 7e8c332d80..0000000000 Binary files a/core/assets-raw/sprites/blocks/iron1.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/iron2.png b/core/assets-raw/sprites/blocks/iron2.png deleted file mode 100644 index 97bcdf16b6..0000000000 Binary files a/core/assets-raw/sprites/blocks/iron2.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/iron3.png b/core/assets-raw/sprites/blocks/iron3.png deleted file mode 100644 index 6110bcfd78..0000000000 Binary files a/core/assets-raw/sprites/blocks/iron3.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/irondrill.png b/core/assets-raw/sprites/blocks/irondrill.png deleted file mode 100644 index ef0550b268..0000000000 Binary files a/core/assets-raw/sprites/blocks/irondrill.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/ironwall.png b/core/assets-raw/sprites/blocks/ironwall.png deleted file mode 100644 index 9d0971a89a..0000000000 Binary files a/core/assets-raw/sprites/blocks/ironwall.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/junction.png b/core/assets-raw/sprites/blocks/junction.png deleted file mode 100644 index b1b9237d42..0000000000 Binary files a/core/assets-raw/sprites/blocks/junction.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/laserturret.png b/core/assets-raw/sprites/blocks/laserturret.png deleted file mode 100644 index 7452832d41..0000000000 Binary files a/core/assets-raw/sprites/blocks/laserturret.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/lava.png b/core/assets-raw/sprites/blocks/lava.png deleted file mode 100644 index 8ef9d3966b..0000000000 Binary files a/core/assets-raw/sprites/blocks/lava.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/lavaedge.png b/core/assets-raw/sprites/blocks/lavaedge.png deleted file mode 100644 index 69ce2f2332..0000000000 Binary files a/core/assets-raw/sprites/blocks/lavaedge.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/lavasmelter.png b/core/assets-raw/sprites/blocks/lavasmelter.png deleted file mode 100644 index df2b3b1f9e..0000000000 Binary files a/core/assets-raw/sprites/blocks/lavasmelter.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/liquid/bridge-conduit-arrow.png b/core/assets-raw/sprites/blocks/liquid/bridge-conduit-arrow.png new file mode 100644 index 0000000000..2c88950956 Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/bridge-conduit-arrow.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/bridge-conduit-bridge.png b/core/assets-raw/sprites/blocks/liquid/bridge-conduit-bridge.png new file mode 100644 index 0000000000..bfb44d16b6 Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/bridge-conduit-bridge.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/bridge-conduit-end.png b/core/assets-raw/sprites/blocks/liquid/bridge-conduit-end.png new file mode 100644 index 0000000000..4fe3d03201 Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/bridge-conduit-end.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/bridge-conduit.png b/core/assets-raw/sprites/blocks/liquid/bridge-conduit.png new file mode 100644 index 0000000000..f1c7bc3055 Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/bridge-conduit.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/conduit-bottom-0.png b/core/assets-raw/sprites/blocks/liquid/conduit-bottom-0.png new file mode 100644 index 0000000000..b5f2d11a9a Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/conduit-bottom-0.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/conduit-bottom-1.png b/core/assets-raw/sprites/blocks/liquid/conduit-bottom-1.png new file mode 100644 index 0000000000..7153704def Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/conduit-bottom-1.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/conduit-bottom-2.png b/core/assets-raw/sprites/blocks/liquid/conduit-bottom-2.png new file mode 100644 index 0000000000..7d5b6fb9f9 Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/conduit-bottom-2.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/conduit-bottom-3.png b/core/assets-raw/sprites/blocks/liquid/conduit-bottom-3.png new file mode 100644 index 0000000000..977188d577 Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/conduit-bottom-3.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/conduit-bottom-4.png b/core/assets-raw/sprites/blocks/liquid/conduit-bottom-4.png new file mode 100644 index 0000000000..0328b79b3e Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/conduit-bottom-4.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/conduit-bottom-5.png b/core/assets-raw/sprites/blocks/liquid/conduit-bottom-5.png new file mode 100644 index 0000000000..e7ef35006f Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/conduit-bottom-5.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/conduit-bottom-6.png b/core/assets-raw/sprites/blocks/liquid/conduit-bottom-6.png new file mode 100644 index 0000000000..9b4b292794 Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/conduit-bottom-6.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/conduit-bottom.png b/core/assets-raw/sprites/blocks/liquid/conduit-bottom.png new file mode 100644 index 0000000000..4908fa83c9 Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/conduit-bottom.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/conduit-top-0.png b/core/assets-raw/sprites/blocks/liquid/conduit-top-0.png new file mode 100644 index 0000000000..9dc9384971 Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/conduit-top-0.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/conduit-top-1.png b/core/assets-raw/sprites/blocks/liquid/conduit-top-1.png new file mode 100644 index 0000000000..e219a5751d Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/conduit-top-1.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/conduit-top-2.png b/core/assets-raw/sprites/blocks/liquid/conduit-top-2.png new file mode 100644 index 0000000000..30d039d2d8 Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/conduit-top-2.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/conduit-top-3.png b/core/assets-raw/sprites/blocks/liquid/conduit-top-3.png new file mode 100644 index 0000000000..c7feafdfa8 Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/conduit-top-3.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/conduit-top-4.png b/core/assets-raw/sprites/blocks/liquid/conduit-top-4.png new file mode 100644 index 0000000000..58ab8e6927 Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/conduit-top-4.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/conduit-top-5.png b/core/assets-raw/sprites/blocks/liquid/conduit-top-5.png new file mode 100644 index 0000000000..72461ba0b6 Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/conduit-top-5.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/conduit-top-6.png b/core/assets-raw/sprites/blocks/liquid/conduit-top-6.png new file mode 100644 index 0000000000..d30fc7a8b6 Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/conduit-top-6.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/liquid-junction.png b/core/assets-raw/sprites/blocks/liquid/liquid-junction.png new file mode 100644 index 0000000000..737f148b04 Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/liquid-junction.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/liquid-router-bottom.png b/core/assets-raw/sprites/blocks/liquid/liquid-router-bottom.png new file mode 100644 index 0000000000..5b48c24adf Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/liquid-router-bottom.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/liquid-router-liquid.png b/core/assets-raw/sprites/blocks/liquid/liquid-router-liquid.png new file mode 100644 index 0000000000..a32ca6161a Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/liquid-router-liquid.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/liquid-router-top.png b/core/assets-raw/sprites/blocks/liquid/liquid-router-top.png new file mode 100644 index 0000000000..474a4ecac8 Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/liquid-router-top.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/liquid-tank-bottom.png b/core/assets-raw/sprites/blocks/liquid/liquid-tank-bottom.png new file mode 100644 index 0000000000..b4743171f0 Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/liquid-tank-bottom.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/liquid-tank-liquid.png b/core/assets-raw/sprites/blocks/liquid/liquid-tank-liquid.png new file mode 100644 index 0000000000..6ce6c746da Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/liquid-tank-liquid.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/liquid-tank-top.png b/core/assets-raw/sprites/blocks/liquid/liquid-tank-top.png new file mode 100644 index 0000000000..9455df4454 Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/liquid-tank-top.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/mechanical-pump.png b/core/assets-raw/sprites/blocks/liquid/mechanical-pump.png new file mode 100644 index 0000000000..dcc74a737a Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/mechanical-pump.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/phase-conduit-arrow.png b/core/assets-raw/sprites/blocks/liquid/phase-conduit-arrow.png new file mode 100644 index 0000000000..a1a0cefc09 Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/phase-conduit-arrow.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/phase-conduit-bridge.png b/core/assets-raw/sprites/blocks/liquid/phase-conduit-bridge.png new file mode 100644 index 0000000000..e681a09e98 Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/phase-conduit-bridge.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/phase-conduit-end.png b/core/assets-raw/sprites/blocks/liquid/phase-conduit-end.png new file mode 100644 index 0000000000..cdcc52224b Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/phase-conduit-end.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/phase-conduit.png b/core/assets-raw/sprites/blocks/liquid/phase-conduit.png new file mode 100644 index 0000000000..1956ea3565 Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/phase-conduit.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/pulse-conduit-top-0.png b/core/assets-raw/sprites/blocks/liquid/pulse-conduit-top-0.png new file mode 100644 index 0000000000..c6a453fed9 Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/pulse-conduit-top-0.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/pulse-conduit-top-1.png b/core/assets-raw/sprites/blocks/liquid/pulse-conduit-top-1.png new file mode 100644 index 0000000000..62f03471c4 Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/pulse-conduit-top-1.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/pulse-conduit-top-2.png b/core/assets-raw/sprites/blocks/liquid/pulse-conduit-top-2.png new file mode 100644 index 0000000000..3cea1a8e8d Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/pulse-conduit-top-2.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/pulse-conduit-top-3.png b/core/assets-raw/sprites/blocks/liquid/pulse-conduit-top-3.png new file mode 100644 index 0000000000..657e292eeb Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/pulse-conduit-top-3.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/pulse-conduit-top-4.png b/core/assets-raw/sprites/blocks/liquid/pulse-conduit-top-4.png new file mode 100644 index 0000000000..0dda362ff1 Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/pulse-conduit-top-4.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/pulse-conduit-top-5.png b/core/assets-raw/sprites/blocks/liquid/pulse-conduit-top-5.png new file mode 100644 index 0000000000..7ada32da91 Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/pulse-conduit-top-5.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/pulse-conduit-top-6.png b/core/assets-raw/sprites/blocks/liquid/pulse-conduit-top-6.png new file mode 100644 index 0000000000..f43d03fa16 Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/pulse-conduit-top-6.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/rotary-pump.png b/core/assets-raw/sprites/blocks/liquid/rotary-pump.png new file mode 100644 index 0000000000..68aa196101 Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/rotary-pump.png differ diff --git a/core/assets-raw/sprites/blocks/liquid/thermal-pump.png b/core/assets-raw/sprites/blocks/liquid/thermal-pump.png new file mode 100644 index 0000000000..807fa4bc15 Binary files /dev/null and b/core/assets-raw/sprites/blocks/liquid/thermal-pump.png differ diff --git a/core/assets-raw/sprites/blocks/liquiditemjunction.png b/core/assets-raw/sprites/blocks/liquiditemjunction.png deleted file mode 100644 index 9df1857781..0000000000 Binary files a/core/assets-raw/sprites/blocks/liquiditemjunction.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/liquidjunction.png b/core/assets-raw/sprites/blocks/liquidjunction.png deleted file mode 100644 index 0a0bfdcfba..0000000000 Binary files a/core/assets-raw/sprites/blocks/liquidjunction.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/liquidrouter.png b/core/assets-raw/sprites/blocks/liquidrouter.png deleted file mode 100644 index 2b542a739f..0000000000 Binary files a/core/assets-raw/sprites/blocks/liquidrouter.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/machineturret.png b/core/assets-raw/sprites/blocks/machineturret.png deleted file mode 100644 index 4757ab8631..0000000000 Binary files a/core/assets-raw/sprites/blocks/machineturret.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/mechs/dart-mech-pad.png b/core/assets-raw/sprites/blocks/mechs/dart-mech-pad.png new file mode 100644 index 0000000000..70f16847d2 Binary files /dev/null and b/core/assets-raw/sprites/blocks/mechs/dart-mech-pad.png differ diff --git a/core/assets-raw/sprites/blocks/mechs/delta-mech-pad.png b/core/assets-raw/sprites/blocks/mechs/delta-mech-pad.png new file mode 100644 index 0000000000..03f04549c6 Binary files /dev/null and b/core/assets-raw/sprites/blocks/mechs/delta-mech-pad.png differ diff --git a/core/assets-raw/sprites/blocks/mechs/glaive-ship-pad.png b/core/assets-raw/sprites/blocks/mechs/glaive-ship-pad.png new file mode 100644 index 0000000000..d2f9db2a65 Binary files /dev/null and b/core/assets-raw/sprites/blocks/mechs/glaive-ship-pad.png differ diff --git a/core/assets-raw/sprites/blocks/mechs/javelin-ship-pad.png b/core/assets-raw/sprites/blocks/mechs/javelin-ship-pad.png new file mode 100644 index 0000000000..0c98d4566a Binary files /dev/null and b/core/assets-raw/sprites/blocks/mechs/javelin-ship-pad.png differ diff --git a/core/assets-raw/sprites/blocks/mechs/omega-mech-pad.png b/core/assets-raw/sprites/blocks/mechs/omega-mech-pad.png new file mode 100644 index 0000000000..0f3d98c97e Binary files /dev/null and b/core/assets-raw/sprites/blocks/mechs/omega-mech-pad.png differ diff --git a/core/assets-raw/sprites/blocks/mechs/tau-mech-pad.png b/core/assets-raw/sprites/blocks/mechs/tau-mech-pad.png new file mode 100644 index 0000000000..f929bedde8 Binary files /dev/null and b/core/assets-raw/sprites/blocks/mechs/tau-mech-pad.png differ diff --git a/core/assets-raw/sprites/blocks/mechs/trident-ship-pad.png b/core/assets-raw/sprites/blocks/mechs/trident-ship-pad.png new file mode 100644 index 0000000000..dd9d17e0dd Binary files /dev/null and b/core/assets-raw/sprites/blocks/mechs/trident-ship-pad.png differ diff --git a/core/assets-raw/sprites/blocks/megarepairturret.png b/core/assets-raw/sprites/blocks/megarepairturret.png deleted file mode 100644 index 7bf6173a93..0000000000 Binary files a/core/assets-raw/sprites/blocks/megarepairturret.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/mortarturret.png b/core/assets-raw/sprites/blocks/mortarturret.png deleted file mode 100644 index 3435a9f0ca..0000000000 Binary files a/core/assets-raw/sprites/blocks/mortarturret.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/mossblock.png b/core/assets-raw/sprites/blocks/mossblock.png deleted file mode 100644 index 194f078ee7..0000000000 Binary files a/core/assets-raw/sprites/blocks/mossblock.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/mossstone.png b/core/assets-raw/sprites/blocks/mossstone.png deleted file mode 100644 index f49713af7c..0000000000 Binary files a/core/assets-raw/sprites/blocks/mossstone.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/nuclearreactor-center.png b/core/assets-raw/sprites/blocks/nuclearreactor-center.png deleted file mode 100644 index 5b5a2688cc..0000000000 Binary files a/core/assets-raw/sprites/blocks/nuclearreactor-center.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/nuclearreactor-icon.png b/core/assets-raw/sprites/blocks/nuclearreactor-icon.png deleted file mode 100644 index 2a97a1bc63..0000000000 Binary files a/core/assets-raw/sprites/blocks/nuclearreactor-icon.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/nuclearreactor-lights.png b/core/assets-raw/sprites/blocks/nuclearreactor-lights.png deleted file mode 100644 index 69a28b33df..0000000000 Binary files a/core/assets-raw/sprites/blocks/nuclearreactor-lights.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/nuclearreactor-small.png b/core/assets-raw/sprites/blocks/nuclearreactor-small.png deleted file mode 100644 index 6d9f0bfd35..0000000000 Binary files a/core/assets-raw/sprites/blocks/nuclearreactor-small.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/nuclearreactor.png b/core/assets-raw/sprites/blocks/nuclearreactor.png deleted file mode 100644 index 5e28126f38..0000000000 Binary files a/core/assets-raw/sprites/blocks/nuclearreactor.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/oil.png b/core/assets-raw/sprites/blocks/oil.png deleted file mode 100644 index fc632673d3..0000000000 Binary files a/core/assets-raw/sprites/blocks/oil.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/oiledge.png b/core/assets-raw/sprites/blocks/oiledge.png deleted file mode 100644 index cd971062a4..0000000000 Binary files a/core/assets-raw/sprites/blocks/oiledge.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/oilrefinery.png b/core/assets-raw/sprites/blocks/oilrefinery.png deleted file mode 100644 index acfe53f47f..0000000000 Binary files a/core/assets-raw/sprites/blocks/oilrefinery.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/omnidrill.png b/core/assets-raw/sprites/blocks/omnidrill.png deleted file mode 100644 index 5ff4798aab..0000000000 Binary files a/core/assets-raw/sprites/blocks/omnidrill.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/plasmaturret.png b/core/assets-raw/sprites/blocks/plasmaturret.png deleted file mode 100644 index e6e7a1d328..0000000000 Binary files a/core/assets-raw/sprites/blocks/plasmaturret.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/playerspawn.png b/core/assets-raw/sprites/blocks/playerspawn.png deleted file mode 100644 index 448213f999..0000000000 Binary files a/core/assets-raw/sprites/blocks/playerspawn.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/power/battery-large.png b/core/assets-raw/sprites/blocks/power/battery-large.png new file mode 100644 index 0000000000..5917051f44 Binary files /dev/null and b/core/assets-raw/sprites/blocks/power/battery-large.png differ diff --git a/core/assets-raw/sprites/blocks/power/battery.png b/core/assets-raw/sprites/blocks/power/battery.png new file mode 100644 index 0000000000..44a863f123 Binary files /dev/null and b/core/assets-raw/sprites/blocks/power/battery.png differ diff --git a/core/assets-raw/sprites/blocks/power/combustion-generator-top.png b/core/assets-raw/sprites/blocks/power/combustion-generator-top.png new file mode 100644 index 0000000000..8df8df9a57 Binary files /dev/null and b/core/assets-raw/sprites/blocks/power/combustion-generator-top.png differ diff --git a/core/assets-raw/sprites/blocks/power/combustion-generator.png b/core/assets-raw/sprites/blocks/power/combustion-generator.png new file mode 100644 index 0000000000..8fb412f555 Binary files /dev/null and b/core/assets-raw/sprites/blocks/power/combustion-generator.png differ diff --git a/core/assets-raw/sprites/blocks/power/differential-generator-top.png b/core/assets-raw/sprites/blocks/power/differential-generator-top.png new file mode 100644 index 0000000000..0e1b4fda69 Binary files /dev/null and b/core/assets-raw/sprites/blocks/power/differential-generator-top.png differ diff --git a/core/assets-raw/sprites/blocks/power/differential-generator.png b/core/assets-raw/sprites/blocks/power/differential-generator.png new file mode 100644 index 0000000000..96ff71e8ac Binary files /dev/null and b/core/assets-raw/sprites/blocks/power/differential-generator.png differ diff --git a/core/assets-raw/sprites/blocks/power/impact-reactor-bottom.png b/core/assets-raw/sprites/blocks/power/impact-reactor-bottom.png new file mode 100644 index 0000000000..6b16fca94c Binary files /dev/null and b/core/assets-raw/sprites/blocks/power/impact-reactor-bottom.png differ diff --git a/core/assets-raw/sprites/blocks/power/impact-reactor-light.png b/core/assets-raw/sprites/blocks/power/impact-reactor-light.png new file mode 100644 index 0000000000..0563c2a151 Binary files /dev/null and b/core/assets-raw/sprites/blocks/power/impact-reactor-light.png differ diff --git a/core/assets-raw/sprites/blocks/power/impact-reactor-plasma-0.png b/core/assets-raw/sprites/blocks/power/impact-reactor-plasma-0.png new file mode 100644 index 0000000000..bbbfe6a38c Binary files /dev/null and b/core/assets-raw/sprites/blocks/power/impact-reactor-plasma-0.png differ diff --git a/core/assets-raw/sprites/blocks/power/impact-reactor-plasma-1.png b/core/assets-raw/sprites/blocks/power/impact-reactor-plasma-1.png new file mode 100644 index 0000000000..25cdb575f7 Binary files /dev/null and b/core/assets-raw/sprites/blocks/power/impact-reactor-plasma-1.png differ diff --git a/core/assets-raw/sprites/blocks/power/impact-reactor-plasma-2.png b/core/assets-raw/sprites/blocks/power/impact-reactor-plasma-2.png new file mode 100644 index 0000000000..b50d0313d8 Binary files /dev/null and b/core/assets-raw/sprites/blocks/power/impact-reactor-plasma-2.png differ diff --git a/core/assets-raw/sprites/blocks/power/impact-reactor-plasma-3.png b/core/assets-raw/sprites/blocks/power/impact-reactor-plasma-3.png new file mode 100644 index 0000000000..21ee665e25 Binary files /dev/null and b/core/assets-raw/sprites/blocks/power/impact-reactor-plasma-3.png differ diff --git a/core/assets-raw/sprites/blocks/power/impact-reactor.png b/core/assets-raw/sprites/blocks/power/impact-reactor.png new file mode 100644 index 0000000000..c1dd5e46c8 Binary files /dev/null and b/core/assets-raw/sprites/blocks/power/impact-reactor.png differ diff --git a/core/assets-raw/sprites/blocks/power/power-node-large.png b/core/assets-raw/sprites/blocks/power/power-node-large.png new file mode 100644 index 0000000000..add53dd280 Binary files /dev/null and b/core/assets-raw/sprites/blocks/power/power-node-large.png differ diff --git a/core/assets-raw/sprites/blocks/power/power-node.png b/core/assets-raw/sprites/blocks/power/power-node.png new file mode 100644 index 0000000000..e8f6498ee1 Binary files /dev/null and b/core/assets-raw/sprites/blocks/power/power-node.png differ diff --git a/core/assets-raw/sprites/blocks/power/power-source.png b/core/assets-raw/sprites/blocks/power/power-source.png new file mode 100644 index 0000000000..f4c6473df4 Binary files /dev/null and b/core/assets-raw/sprites/blocks/power/power-source.png differ diff --git a/core/assets-raw/sprites/blocks/power/power-void.png b/core/assets-raw/sprites/blocks/power/power-void.png new file mode 100644 index 0000000000..fee2a2b3e8 Binary files /dev/null and b/core/assets-raw/sprites/blocks/power/power-void.png differ diff --git a/core/assets-raw/sprites/blocks/power/rtg-generator-top.png b/core/assets-raw/sprites/blocks/power/rtg-generator-top.png new file mode 100644 index 0000000000..dc89fca155 Binary files /dev/null and b/core/assets-raw/sprites/blocks/power/rtg-generator-top.png differ diff --git a/core/assets-raw/sprites/blocks/power/rtg-generator.png b/core/assets-raw/sprites/blocks/power/rtg-generator.png new file mode 100644 index 0000000000..414a606c8c Binary files /dev/null and b/core/assets-raw/sprites/blocks/power/rtg-generator.png differ diff --git a/core/assets-raw/sprites/blocks/power/solar-panel-large.png b/core/assets-raw/sprites/blocks/power/solar-panel-large.png new file mode 100644 index 0000000000..782d792039 Binary files /dev/null and b/core/assets-raw/sprites/blocks/power/solar-panel-large.png differ diff --git a/core/assets-raw/sprites/blocks/power/solar-panel.png b/core/assets-raw/sprites/blocks/power/solar-panel.png new file mode 100644 index 0000000000..0703c1be90 Binary files /dev/null and b/core/assets-raw/sprites/blocks/power/solar-panel.png differ diff --git a/core/assets-raw/sprites/blocks/power/surge-tower.png b/core/assets-raw/sprites/blocks/power/surge-tower.png new file mode 100644 index 0000000000..f381af0813 Binary files /dev/null and b/core/assets-raw/sprites/blocks/power/surge-tower.png differ diff --git a/core/assets-raw/sprites/blocks/power/thermal-generator.png b/core/assets-raw/sprites/blocks/power/thermal-generator.png new file mode 100644 index 0000000000..07b1da1567 Binary files /dev/null and b/core/assets-raw/sprites/blocks/power/thermal-generator.png differ diff --git a/core/assets-raw/sprites/blocks/power/thorium-reactor-center.png b/core/assets-raw/sprites/blocks/power/thorium-reactor-center.png new file mode 100644 index 0000000000..1254159ab9 Binary files /dev/null and b/core/assets-raw/sprites/blocks/power/thorium-reactor-center.png differ diff --git a/core/assets-raw/sprites/blocks/power/thorium-reactor-lights.png b/core/assets-raw/sprites/blocks/power/thorium-reactor-lights.png new file mode 100644 index 0000000000..fe7b76179c Binary files /dev/null and b/core/assets-raw/sprites/blocks/power/thorium-reactor-lights.png differ diff --git a/core/assets-raw/sprites/blocks/power/thorium-reactor.png b/core/assets-raw/sprites/blocks/power/thorium-reactor.png new file mode 100644 index 0000000000..7b7fdc1c79 Binary files /dev/null and b/core/assets-raw/sprites/blocks/power/thorium-reactor.png differ diff --git a/core/assets-raw/sprites/blocks/power/turbine-generator-top.png b/core/assets-raw/sprites/blocks/power/turbine-generator-top.png new file mode 100644 index 0000000000..d7cf0d8547 Binary files /dev/null and b/core/assets-raw/sprites/blocks/power/turbine-generator-top.png differ diff --git a/core/assets-raw/sprites/blocks/power/turbine-generator.png b/core/assets-raw/sprites/blocks/power/turbine-generator.png new file mode 100644 index 0000000000..b434c5c6fa Binary files /dev/null and b/core/assets-raw/sprites/blocks/power/turbine-generator.png differ diff --git a/core/assets-raw/sprites/blocks/powerbooster.png b/core/assets-raw/sprites/blocks/powerbooster.png deleted file mode 100644 index 5d0ce18461..0000000000 Binary files a/core/assets-raw/sprites/blocks/powerbooster.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/poweredconveyor.png b/core/assets-raw/sprites/blocks/poweredconveyor.png deleted file mode 100644 index bae08a3d20..0000000000 Binary files a/core/assets-raw/sprites/blocks/poweredconveyor.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/poweredconveyormove.png b/core/assets-raw/sprites/blocks/poweredconveyormove.png deleted file mode 100644 index 60d4002873..0000000000 Binary files a/core/assets-raw/sprites/blocks/poweredconveyormove.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/powerlaser.png b/core/assets-raw/sprites/blocks/powerlaser.png deleted file mode 100644 index a20be88c15..0000000000 Binary files a/core/assets-raw/sprites/blocks/powerlaser.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/powerlasercorner.png b/core/assets-raw/sprites/blocks/powerlasercorner.png deleted file mode 100644 index 52c2858c7d..0000000000 Binary files a/core/assets-raw/sprites/blocks/powerlasercorner.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/powerlaserrouter.png b/core/assets-raw/sprites/blocks/powerlaserrouter.png deleted file mode 100644 index ea7c8122f3..0000000000 Binary files a/core/assets-raw/sprites/blocks/powerlaserrouter.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/production/alloy-smelter-top.png b/core/assets-raw/sprites/blocks/production/alloy-smelter-top.png new file mode 100644 index 0000000000..c946c0dd3e Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/alloy-smelter-top.png differ diff --git a/core/assets-raw/sprites/blocks/production/alloy-smelter.png b/core/assets-raw/sprites/blocks/production/alloy-smelter.png new file mode 100644 index 0000000000..048022a525 Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/alloy-smelter.png differ diff --git a/core/assets-raw/sprites/blocks/production/blast-mixer.png b/core/assets-raw/sprites/blocks/production/blast-mixer.png new file mode 100644 index 0000000000..7bb42d17ac Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/blast-mixer.png differ diff --git a/core/assets-raw/sprites/blocks/production/coal-centrifuge.png b/core/assets-raw/sprites/blocks/production/coal-centrifuge.png new file mode 100644 index 0000000000..d32ffe368b Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/coal-centrifuge.png differ diff --git a/core/assets-raw/sprites/blocks/production/cryofluidmixer-bottom.png b/core/assets-raw/sprites/blocks/production/cryofluidmixer-bottom.png new file mode 100644 index 0000000000..4a97b6c5ff Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/cryofluidmixer-bottom.png differ diff --git a/core/assets-raw/sprites/blocks/production/cryofluidmixer-liquid.png b/core/assets-raw/sprites/blocks/production/cryofluidmixer-liquid.png new file mode 100644 index 0000000000..929c629499 Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/cryofluidmixer-liquid.png differ diff --git a/core/assets-raw/sprites/blocks/production/cryofluidmixer-top.png b/core/assets-raw/sprites/blocks/production/cryofluidmixer-top.png new file mode 100644 index 0000000000..26fe248540 Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/cryofluidmixer-top.png differ diff --git a/core/assets-raw/sprites/blocks/production/cultivator-middle.png b/core/assets-raw/sprites/blocks/production/cultivator-middle.png new file mode 100644 index 0000000000..29f0d1fcf4 Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/cultivator-middle.png differ diff --git a/core/assets-raw/sprites/blocks/production/cultivator-top.png b/core/assets-raw/sprites/blocks/production/cultivator-top.png new file mode 100644 index 0000000000..fd33bf6210 Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/cultivator-top.png differ diff --git a/core/assets-raw/sprites/blocks/production/cultivator.png b/core/assets-raw/sprites/blocks/production/cultivator.png new file mode 100644 index 0000000000..6d1eecd2b0 Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/cultivator.png differ diff --git a/core/assets-raw/sprites/blocks/production/graphite-press.png b/core/assets-raw/sprites/blocks/production/graphite-press.png new file mode 100644 index 0000000000..39d6adbe36 Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/graphite-press.png differ diff --git a/core/assets-raw/sprites/blocks/production/incinerator.png b/core/assets-raw/sprites/blocks/production/incinerator.png new file mode 100644 index 0000000000..9c20fa5308 Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/incinerator.png differ diff --git a/core/assets-raw/sprites/blocks/production/item-source.png b/core/assets-raw/sprites/blocks/production/item-source.png new file mode 100644 index 0000000000..b384226e5f Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/item-source.png differ diff --git a/core/assets-raw/sprites/blocks/production/item-void.png b/core/assets-raw/sprites/blocks/production/item-void.png new file mode 100644 index 0000000000..473040a95d Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/item-void.png differ diff --git a/core/assets-raw/sprites/blocks/production/kiln-top.png b/core/assets-raw/sprites/blocks/production/kiln-top.png new file mode 100644 index 0000000000..336852ceee Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/kiln-top.png differ diff --git a/core/assets-raw/sprites/blocks/production/kiln.png b/core/assets-raw/sprites/blocks/production/kiln.png new file mode 100644 index 0000000000..67e03032c2 Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/kiln.png differ diff --git a/core/assets-raw/sprites/blocks/production/liquid-source.png b/core/assets-raw/sprites/blocks/production/liquid-source.png new file mode 100644 index 0000000000..2c493d30f5 Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/liquid-source.png differ diff --git a/core/assets-raw/sprites/blocks/production/melter.png b/core/assets-raw/sprites/blocks/production/melter.png new file mode 100644 index 0000000000..2f4c3a0500 Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/melter.png differ diff --git a/core/assets-raw/sprites/blocks/production/multi-press.png b/core/assets-raw/sprites/blocks/production/multi-press.png new file mode 100644 index 0000000000..5fea3ff19f Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/multi-press.png differ diff --git a/core/assets-raw/sprites/blocks/production/phase-weaver-bottom.png b/core/assets-raw/sprites/blocks/production/phase-weaver-bottom.png new file mode 100644 index 0000000000..17518b9014 Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/phase-weaver-bottom.png differ diff --git a/core/assets-raw/sprites/blocks/production/phase-weaver-weave.png b/core/assets-raw/sprites/blocks/production/phase-weaver-weave.png new file mode 100644 index 0000000000..b016588b17 Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/phase-weaver-weave.png differ diff --git a/core/assets-raw/sprites/blocks/production/phase-weaver.png b/core/assets-raw/sprites/blocks/production/phase-weaver.png new file mode 100644 index 0000000000..0192b0e5dd Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/phase-weaver.png differ diff --git a/core/assets-raw/sprites/blocks/production/plastanium-compressor-top.png b/core/assets-raw/sprites/blocks/production/plastanium-compressor-top.png new file mode 100644 index 0000000000..2e8dc35787 Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/plastanium-compressor-top.png differ diff --git a/core/assets-raw/sprites/blocks/production/plastanium-compressor.png b/core/assets-raw/sprites/blocks/production/plastanium-compressor.png new file mode 100644 index 0000000000..522b6f8b88 Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/plastanium-compressor.png differ diff --git a/core/assets-raw/sprites/blocks/production/pulverizer-rotator.png b/core/assets-raw/sprites/blocks/production/pulverizer-rotator.png new file mode 100644 index 0000000000..c39062caa2 Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/pulverizer-rotator.png differ diff --git a/core/assets-raw/sprites/blocks/production/pulverizer.png b/core/assets-raw/sprites/blocks/production/pulverizer.png new file mode 100644 index 0000000000..cc51a06fa5 Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/pulverizer.png differ diff --git a/core/assets-raw/sprites/blocks/production/pump-liquid.png b/core/assets-raw/sprites/blocks/production/pump-liquid.png new file mode 100644 index 0000000000..e3d4c58e55 Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/pump-liquid.png differ diff --git a/core/assets-raw/sprites/blocks/production/pyratite-mixer.png b/core/assets-raw/sprites/blocks/production/pyratite-mixer.png new file mode 100644 index 0000000000..d7d33d09aa Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/pyratite-mixer.png differ diff --git a/core/assets-raw/sprites/blocks/production/separator-liquid.png b/core/assets-raw/sprites/blocks/production/separator-liquid.png new file mode 100644 index 0000000000..f9adfeb523 Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/separator-liquid.png differ diff --git a/core/assets-raw/sprites/blocks/production/separator.png b/core/assets-raw/sprites/blocks/production/separator.png new file mode 100644 index 0000000000..a3810f08d8 Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/separator.png differ diff --git a/core/assets-raw/sprites/blocks/production/silicon-smelter-top.png b/core/assets-raw/sprites/blocks/production/silicon-smelter-top.png new file mode 100644 index 0000000000..6120871f13 Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/silicon-smelter-top.png differ diff --git a/core/assets-raw/sprites/blocks/production/silicon-smelter.png b/core/assets-raw/sprites/blocks/production/silicon-smelter.png new file mode 100644 index 0000000000..72a92aea73 Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/silicon-smelter.png differ diff --git a/core/assets-raw/sprites/blocks/production/spore-press-frame0.png b/core/assets-raw/sprites/blocks/production/spore-press-frame0.png new file mode 100644 index 0000000000..73ed9f86a9 Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/spore-press-frame0.png differ diff --git a/core/assets-raw/sprites/blocks/production/spore-press-frame1.png b/core/assets-raw/sprites/blocks/production/spore-press-frame1.png new file mode 100644 index 0000000000..ef61bd309c Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/spore-press-frame1.png differ diff --git a/core/assets-raw/sprites/blocks/production/spore-press-frame2.png b/core/assets-raw/sprites/blocks/production/spore-press-frame2.png new file mode 100644 index 0000000000..f527f3146c Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/spore-press-frame2.png differ diff --git a/core/assets-raw/sprites/blocks/production/spore-press-liquid.png b/core/assets-raw/sprites/blocks/production/spore-press-liquid.png new file mode 100644 index 0000000000..63944ceee2 Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/spore-press-liquid.png differ diff --git a/core/assets-raw/sprites/blocks/production/spore-press-top.png b/core/assets-raw/sprites/blocks/production/spore-press-top.png new file mode 100644 index 0000000000..1856922336 Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/spore-press-top.png differ diff --git a/core/assets-raw/sprites/blocks/production/spore-press.png b/core/assets-raw/sprites/blocks/production/spore-press.png new file mode 100644 index 0000000000..414cebdb9c Binary files /dev/null and b/core/assets-raw/sprites/blocks/production/spore-press.png differ diff --git a/core/assets-raw/sprites/blocks/pulseconduit.png b/core/assets-raw/sprites/blocks/pulseconduit.png deleted file mode 100644 index 8a634d9746..0000000000 Binary files a/core/assets-raw/sprites/blocks/pulseconduit.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/pulseconduitbottom.png b/core/assets-raw/sprites/blocks/pulseconduitbottom.png deleted file mode 100644 index 304dc71403..0000000000 Binary files a/core/assets-raw/sprites/blocks/pulseconduitbottom.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/pulseconduittop.png b/core/assets-raw/sprites/blocks/pulseconduittop.png deleted file mode 100644 index a2d6cc9c54..0000000000 Binary files a/core/assets-raw/sprites/blocks/pulseconduittop.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/pump.png b/core/assets-raw/sprites/blocks/pump.png deleted file mode 100644 index 8c81843452..0000000000 Binary files a/core/assets-raw/sprites/blocks/pump.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/repairturret.png b/core/assets-raw/sprites/blocks/repairturret.png deleted file mode 100644 index 66c8b6e288..0000000000 Binary files a/core/assets-raw/sprites/blocks/repairturret.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/rock1.png b/core/assets-raw/sprites/blocks/rock1.png deleted file mode 100644 index 4d1fcdddea..0000000000 Binary files a/core/assets-raw/sprites/blocks/rock1.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/rock2.png b/core/assets-raw/sprites/blocks/rock2.png deleted file mode 100644 index c1298987cc..0000000000 Binary files a/core/assets-raw/sprites/blocks/rock2.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/rockshadow1.png b/core/assets-raw/sprites/blocks/rockshadow1.png deleted file mode 100644 index 2d9cafbedc..0000000000 Binary files a/core/assets-raw/sprites/blocks/rockshadow1.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/rockshadow2.png b/core/assets-raw/sprites/blocks/rockshadow2.png deleted file mode 100644 index ea2870359e..0000000000 Binary files a/core/assets-raw/sprites/blocks/rockshadow2.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/router.png b/core/assets-raw/sprites/blocks/router.png deleted file mode 100644 index 8094c272f6..0000000000 Binary files a/core/assets-raw/sprites/blocks/router.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/rtgenerator-top.png b/core/assets-raw/sprites/blocks/rtgenerator-top.png deleted file mode 100644 index 56e04563b4..0000000000 Binary files a/core/assets-raw/sprites/blocks/rtgenerator-top.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/rtgenerator.png b/core/assets-raw/sprites/blocks/rtgenerator.png deleted file mode 100644 index b0a625ba7b..0000000000 Binary files a/core/assets-raw/sprites/blocks/rtgenerator.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/sand1.png b/core/assets-raw/sprites/blocks/sand1.png deleted file mode 100644 index 119e4a9e8b..0000000000 Binary files a/core/assets-raw/sprites/blocks/sand1.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/sand2.png b/core/assets-raw/sprites/blocks/sand2.png deleted file mode 100644 index 21042edd9f..0000000000 Binary files a/core/assets-raw/sprites/blocks/sand2.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/sand3.png b/core/assets-raw/sprites/blocks/sand3.png deleted file mode 100644 index 5e0536ef8c..0000000000 Binary files a/core/assets-raw/sprites/blocks/sand3.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/sandblock1.png b/core/assets-raw/sprites/blocks/sandblock1.png deleted file mode 100644 index 52b8edb4ba..0000000000 Binary files a/core/assets-raw/sprites/blocks/sandblock1.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/sandblock2.png b/core/assets-raw/sprites/blocks/sandblock2.png deleted file mode 100644 index 646fdd4831..0000000000 Binary files a/core/assets-raw/sprites/blocks/sandblock2.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/sandblock3.png b/core/assets-raw/sprites/blocks/sandblock3.png deleted file mode 100644 index ca0093a2e6..0000000000 Binary files a/core/assets-raw/sprites/blocks/sandblock3.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/sandedge.png b/core/assets-raw/sprites/blocks/sandedge.png deleted file mode 100644 index 361c12f800..0000000000 Binary files a/core/assets-raw/sprites/blocks/sandedge.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/shadow.png b/core/assets-raw/sprites/blocks/shadow.png deleted file mode 100644 index a4f6243e61..0000000000 Binary files a/core/assets-raw/sprites/blocks/shadow.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/shieldgenerator.png b/core/assets-raw/sprites/blocks/shieldgenerator.png deleted file mode 100644 index 4da9e9ccf5..0000000000 Binary files a/core/assets-raw/sprites/blocks/shieldgenerator.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/shotgunturret.png b/core/assets-raw/sprites/blocks/shotgunturret.png deleted file mode 100644 index 752f95fb0c..0000000000 Binary files a/core/assets-raw/sprites/blocks/shotgunturret.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/shrub.png b/core/assets-raw/sprites/blocks/shrub.png deleted file mode 100644 index aa5d6ee818..0000000000 Binary files a/core/assets-raw/sprites/blocks/shrub.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/shrubshadow.png b/core/assets-raw/sprites/blocks/shrubshadow.png deleted file mode 100644 index f78fab2767..0000000000 Binary files a/core/assets-raw/sprites/blocks/shrubshadow.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/smelter-middle.png b/core/assets-raw/sprites/blocks/smelter-middle.png deleted file mode 100644 index caf10170b4..0000000000 Binary files a/core/assets-raw/sprites/blocks/smelter-middle.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/smelter.png b/core/assets-raw/sprites/blocks/smelter.png deleted file mode 100644 index dd197aad34..0000000000 Binary files a/core/assets-raw/sprites/blocks/smelter.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/sniperturret.png b/core/assets-raw/sprites/blocks/sniperturret.png deleted file mode 100644 index 1d1a2a3649..0000000000 Binary files a/core/assets-raw/sprites/blocks/sniperturret.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/snow1.png b/core/assets-raw/sprites/blocks/snow1.png deleted file mode 100644 index 7ae5cee5cc..0000000000 Binary files a/core/assets-raw/sprites/blocks/snow1.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/snow2.png b/core/assets-raw/sprites/blocks/snow2.png deleted file mode 100644 index 7336d8e818..0000000000 Binary files a/core/assets-raw/sprites/blocks/snow2.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/snow3.png b/core/assets-raw/sprites/blocks/snow3.png deleted file mode 100644 index f423934952..0000000000 Binary files a/core/assets-raw/sprites/blocks/snow3.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/snowblock1.png b/core/assets-raw/sprites/blocks/snowblock1.png deleted file mode 100644 index a3bb781fe6..0000000000 Binary files a/core/assets-raw/sprites/blocks/snowblock1.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/snowblock2.png b/core/assets-raw/sprites/blocks/snowblock2.png deleted file mode 100644 index 508e42fe7e..0000000000 Binary files a/core/assets-raw/sprites/blocks/snowblock2.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/snowblock3.png b/core/assets-raw/sprites/blocks/snowblock3.png deleted file mode 100644 index 93d1b071e2..0000000000 Binary files a/core/assets-raw/sprites/blocks/snowblock3.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/snowedge.png b/core/assets-raw/sprites/blocks/snowedge.png deleted file mode 100644 index e42c06473c..0000000000 Binary files a/core/assets-raw/sprites/blocks/snowedge.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/sorter.png b/core/assets-raw/sprites/blocks/sorter.png deleted file mode 100644 index 987efbc821..0000000000 Binary files a/core/assets-raw/sprites/blocks/sorter.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/steelconveyor.png b/core/assets-raw/sprites/blocks/steelconveyor.png deleted file mode 100644 index ff6a0abd21..0000000000 Binary files a/core/assets-raw/sprites/blocks/steelconveyor.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/steelconveyormove.png b/core/assets-raw/sprites/blocks/steelconveyormove.png deleted file mode 100644 index 371a4a8720..0000000000 Binary files a/core/assets-raw/sprites/blocks/steelconveyormove.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/steelwall-large-icon.png b/core/assets-raw/sprites/blocks/steelwall-large-icon.png deleted file mode 100644 index a38f508587..0000000000 Binary files a/core/assets-raw/sprites/blocks/steelwall-large-icon.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/steelwall-large.png b/core/assets-raw/sprites/blocks/steelwall-large.png deleted file mode 100644 index 217e30ff5c..0000000000 Binary files a/core/assets-raw/sprites/blocks/steelwall-large.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/steelwall.png b/core/assets-raw/sprites/blocks/steelwall.png deleted file mode 100644 index ab8652474a..0000000000 Binary files a/core/assets-raw/sprites/blocks/steelwall.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/stone1.png b/core/assets-raw/sprites/blocks/stone1.png deleted file mode 100644 index ba1dd7c5a7..0000000000 Binary files a/core/assets-raw/sprites/blocks/stone1.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/stone2.png b/core/assets-raw/sprites/blocks/stone2.png deleted file mode 100644 index 19f4ea319e..0000000000 Binary files a/core/assets-raw/sprites/blocks/stone2.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/stone3.png b/core/assets-raw/sprites/blocks/stone3.png deleted file mode 100644 index c5d121ca8d..0000000000 Binary files a/core/assets-raw/sprites/blocks/stone3.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/stoneblock1.png b/core/assets-raw/sprites/blocks/stoneblock1.png deleted file mode 100644 index 0783b4b26f..0000000000 Binary files a/core/assets-raw/sprites/blocks/stoneblock1.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/stoneblock2.png b/core/assets-raw/sprites/blocks/stoneblock2.png deleted file mode 100644 index db2b3a59dd..0000000000 Binary files a/core/assets-raw/sprites/blocks/stoneblock2.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/stoneblock3.png b/core/assets-raw/sprites/blocks/stoneblock3.png deleted file mode 100644 index 67fcf95616..0000000000 Binary files a/core/assets-raw/sprites/blocks/stoneblock3.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/stonedrill.png b/core/assets-raw/sprites/blocks/stonedrill.png deleted file mode 100644 index 0fd9b107be..0000000000 Binary files a/core/assets-raw/sprites/blocks/stonedrill.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/stoneedge.png b/core/assets-raw/sprites/blocks/stoneedge.png deleted file mode 100644 index abae1e4174..0000000000 Binary files a/core/assets-raw/sprites/blocks/stoneedge.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/stoneformer.png b/core/assets-raw/sprites/blocks/stoneformer.png deleted file mode 100644 index baeedc7505..0000000000 Binary files a/core/assets-raw/sprites/blocks/stoneformer.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/stonewall.png b/core/assets-raw/sprites/blocks/stonewall.png deleted file mode 100644 index 4bacdfdee2..0000000000 Binary files a/core/assets-raw/sprites/blocks/stonewall.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/storage/container.png b/core/assets-raw/sprites/blocks/storage/container.png new file mode 100644 index 0000000000..ca98f41fd3 Binary files /dev/null and b/core/assets-raw/sprites/blocks/storage/container.png differ diff --git a/core/assets-raw/sprites/blocks/storage/core-foundation.png b/core/assets-raw/sprites/blocks/storage/core-foundation.png new file mode 100644 index 0000000000..1304e5500d Binary files /dev/null and b/core/assets-raw/sprites/blocks/storage/core-foundation.png differ diff --git a/core/assets-raw/sprites/blocks/storage/core-nucleus.png b/core/assets-raw/sprites/blocks/storage/core-nucleus.png new file mode 100644 index 0000000000..90c5a6c1f4 Binary files /dev/null and b/core/assets-raw/sprites/blocks/storage/core-nucleus.png differ diff --git a/core/assets-raw/sprites/blocks/storage/core-shard.png b/core/assets-raw/sprites/blocks/storage/core-shard.png new file mode 100644 index 0000000000..44c445aaa8 Binary files /dev/null and b/core/assets-raw/sprites/blocks/storage/core-shard.png differ diff --git a/core/assets-raw/sprites/blocks/storage/launch-pad-large.png b/core/assets-raw/sprites/blocks/storage/launch-pad-large.png new file mode 100644 index 0000000000..ac04aef1c4 Binary files /dev/null and b/core/assets-raw/sprites/blocks/storage/launch-pad-large.png differ diff --git a/core/assets-raw/sprites/blocks/storage/launch-pad.png b/core/assets-raw/sprites/blocks/storage/launch-pad.png new file mode 100644 index 0000000000..020670ad28 Binary files /dev/null and b/core/assets-raw/sprites/blocks/storage/launch-pad.png differ diff --git a/core/assets-raw/sprites/blocks/storage/unloader.png b/core/assets-raw/sprites/blocks/storage/unloader.png new file mode 100644 index 0000000000..39ec7a30a5 Binary files /dev/null and b/core/assets-raw/sprites/blocks/storage/unloader.png differ diff --git a/core/assets-raw/sprites/blocks/storage/vault.png b/core/assets-raw/sprites/blocks/storage/vault.png new file mode 100644 index 0000000000..e82235cc75 Binary files /dev/null and b/core/assets-raw/sprites/blocks/storage/vault.png differ diff --git a/core/assets-raw/sprites/blocks/teleporter-top.png b/core/assets-raw/sprites/blocks/teleporter-top.png deleted file mode 100644 index 5e624107a8..0000000000 Binary files a/core/assets-raw/sprites/blocks/teleporter-top.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/teleporter.png b/core/assets-raw/sprites/blocks/teleporter.png deleted file mode 100644 index 74f3d68a16..0000000000 Binary files a/core/assets-raw/sprites/blocks/teleporter.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/thermalgenerator.png b/core/assets-raw/sprites/blocks/thermalgenerator.png deleted file mode 100644 index 62ce54ca84..0000000000 Binary files a/core/assets-raw/sprites/blocks/thermalgenerator.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/titancannon-icon.png b/core/assets-raw/sprites/blocks/titancannon-icon.png deleted file mode 100644 index 718e833971..0000000000 Binary files a/core/assets-raw/sprites/blocks/titancannon-icon.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/titancannon-icon_old.png b/core/assets-raw/sprites/blocks/titancannon-icon_old.png deleted file mode 100644 index 9d4cee27fb..0000000000 Binary files a/core/assets-raw/sprites/blocks/titancannon-icon_old.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/titancannon.png b/core/assets-raw/sprites/blocks/titancannon.png deleted file mode 100644 index a1e47ed71b..0000000000 Binary files a/core/assets-raw/sprites/blocks/titancannon.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/titancannon_old.png b/core/assets-raw/sprites/blocks/titancannon_old.png deleted file mode 100644 index fc492458c2..0000000000 Binary files a/core/assets-raw/sprites/blocks/titancannon_old.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/titanium1.png b/core/assets-raw/sprites/blocks/titanium1.png deleted file mode 100644 index 5cf051540b..0000000000 Binary files a/core/assets-raw/sprites/blocks/titanium1.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/titanium2.png b/core/assets-raw/sprites/blocks/titanium2.png deleted file mode 100644 index 9aad0c978f..0000000000 Binary files a/core/assets-raw/sprites/blocks/titanium2.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/titanium3.png b/core/assets-raw/sprites/blocks/titanium3.png deleted file mode 100644 index f8f1ed80a3..0000000000 Binary files a/core/assets-raw/sprites/blocks/titanium3.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/titaniumdrill.png b/core/assets-raw/sprites/blocks/titaniumdrill.png deleted file mode 100644 index d2f8fef5fc..0000000000 Binary files a/core/assets-raw/sprites/blocks/titaniumdrill.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/titaniumpurifier.png b/core/assets-raw/sprites/blocks/titaniumpurifier.png deleted file mode 100644 index 82d2b85848..0000000000 Binary files a/core/assets-raw/sprites/blocks/titaniumpurifier.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/titaniumshieldwall.png b/core/assets-raw/sprites/blocks/titaniumshieldwall.png deleted file mode 100644 index b33dfe5ff5..0000000000 Binary files a/core/assets-raw/sprites/blocks/titaniumshieldwall.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/titaniumwall-large-icon.png b/core/assets-raw/sprites/blocks/titaniumwall-large-icon.png deleted file mode 100644 index 6361f084c5..0000000000 Binary files a/core/assets-raw/sprites/blocks/titaniumwall-large-icon.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/titaniumwall-large.png b/core/assets-raw/sprites/blocks/titaniumwall-large.png deleted file mode 100644 index 807fde31b1..0000000000 Binary files a/core/assets-raw/sprites/blocks/titaniumwall-large.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/titaniumwall.png b/core/assets-raw/sprites/blocks/titaniumwall.png deleted file mode 100644 index 583c63e4a3..0000000000 Binary files a/core/assets-raw/sprites/blocks/titaniumwall.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/turret.png b/core/assets-raw/sprites/blocks/turret.png deleted file mode 100644 index 3edb816320..0000000000 Binary files a/core/assets-raw/sprites/blocks/turret.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/turrets/arc-heat.png b/core/assets-raw/sprites/blocks/turrets/arc-heat.png new file mode 100644 index 0000000000..de36f57ce4 Binary files /dev/null and b/core/assets-raw/sprites/blocks/turrets/arc-heat.png differ diff --git a/core/assets-raw/sprites/blocks/turrets/arc.png b/core/assets-raw/sprites/blocks/turrets/arc.png new file mode 100644 index 0000000000..b90265b520 Binary files /dev/null and b/core/assets-raw/sprites/blocks/turrets/arc.png differ diff --git a/core/assets-raw/sprites/blocks/turrets/bases/block-1.png b/core/assets-raw/sprites/blocks/turrets/bases/block-1.png new file mode 100644 index 0000000000..43a8576c0f Binary files /dev/null and b/core/assets-raw/sprites/blocks/turrets/bases/block-1.png differ diff --git a/core/assets-raw/sprites/blocks/turrets/bases/block-2.png b/core/assets-raw/sprites/blocks/turrets/bases/block-2.png new file mode 100644 index 0000000000..b425980c32 Binary files /dev/null and b/core/assets-raw/sprites/blocks/turrets/bases/block-2.png differ diff --git a/core/assets-raw/sprites/blocks/turrets/bases/block-3.png b/core/assets-raw/sprites/blocks/turrets/bases/block-3.png new file mode 100644 index 0000000000..4997c7fc3e Binary files /dev/null and b/core/assets-raw/sprites/blocks/turrets/bases/block-3.png differ diff --git a/core/assets-raw/sprites/blocks/turrets/bases/block-4.png b/core/assets-raw/sprites/blocks/turrets/bases/block-4.png new file mode 100644 index 0000000000..7206b69d95 Binary files /dev/null and b/core/assets-raw/sprites/blocks/turrets/bases/block-4.png differ diff --git a/core/assets-raw/sprites/blocks/turrets/cyclone.png b/core/assets-raw/sprites/blocks/turrets/cyclone.png new file mode 100644 index 0000000000..e2f0dbbfcb Binary files /dev/null and b/core/assets-raw/sprites/blocks/turrets/cyclone.png differ diff --git a/core/assets-raw/sprites/blocks/turrets/duo.png b/core/assets-raw/sprites/blocks/turrets/duo.png new file mode 100644 index 0000000000..efcdb60832 Binary files /dev/null and b/core/assets-raw/sprites/blocks/turrets/duo.png differ diff --git a/core/assets-raw/sprites/blocks/turrets/fuse.png b/core/assets-raw/sprites/blocks/turrets/fuse.png new file mode 100644 index 0000000000..39fedd6b51 Binary files /dev/null and b/core/assets-raw/sprites/blocks/turrets/fuse.png differ diff --git a/core/assets-raw/sprites/blocks/turrets/hail-heat.png b/core/assets-raw/sprites/blocks/turrets/hail-heat.png new file mode 100644 index 0000000000..daad1ac936 Binary files /dev/null and b/core/assets-raw/sprites/blocks/turrets/hail-heat.png differ diff --git a/core/assets-raw/sprites/blocks/turrets/hail.png b/core/assets-raw/sprites/blocks/turrets/hail.png new file mode 100644 index 0000000000..ffa58615d2 Binary files /dev/null and b/core/assets-raw/sprites/blocks/turrets/hail.png differ diff --git a/core/assets-raw/sprites/blocks/turrets/lancer-heat.png b/core/assets-raw/sprites/blocks/turrets/lancer-heat.png new file mode 100644 index 0000000000..1327410b66 Binary files /dev/null and b/core/assets-raw/sprites/blocks/turrets/lancer-heat.png differ diff --git a/core/assets-raw/sprites/blocks/turrets/lancer.png b/core/assets-raw/sprites/blocks/turrets/lancer.png new file mode 100644 index 0000000000..60f667d960 Binary files /dev/null and b/core/assets-raw/sprites/blocks/turrets/lancer.png differ diff --git a/core/assets-raw/sprites/blocks/turrets/meltdown-heat.png b/core/assets-raw/sprites/blocks/turrets/meltdown-heat.png new file mode 100644 index 0000000000..e9b1fc0ed3 Binary files /dev/null and b/core/assets-raw/sprites/blocks/turrets/meltdown-heat.png differ diff --git a/core/assets-raw/sprites/blocks/turrets/meltdown.png b/core/assets-raw/sprites/blocks/turrets/meltdown.png new file mode 100644 index 0000000000..824443f1d5 Binary files /dev/null and b/core/assets-raw/sprites/blocks/turrets/meltdown.png differ diff --git a/core/assets-raw/sprites/blocks/turrets/ripple-heat.png b/core/assets-raw/sprites/blocks/turrets/ripple-heat.png new file mode 100644 index 0000000000..6b88b97cb3 Binary files /dev/null and b/core/assets-raw/sprites/blocks/turrets/ripple-heat.png differ diff --git a/core/assets-raw/sprites/blocks/turrets/ripple.png b/core/assets-raw/sprites/blocks/turrets/ripple.png new file mode 100644 index 0000000000..9013b07a94 Binary files /dev/null and b/core/assets-raw/sprites/blocks/turrets/ripple.png differ diff --git a/core/assets-raw/sprites/blocks/turrets/salvo-heat.png b/core/assets-raw/sprites/blocks/turrets/salvo-heat.png new file mode 100644 index 0000000000..5022fafc1d Binary files /dev/null and b/core/assets-raw/sprites/blocks/turrets/salvo-heat.png differ diff --git a/core/assets-raw/sprites/blocks/turrets/salvo-panel-left.png b/core/assets-raw/sprites/blocks/turrets/salvo-panel-left.png new file mode 100644 index 0000000000..de0af2f724 Binary files /dev/null and b/core/assets-raw/sprites/blocks/turrets/salvo-panel-left.png differ diff --git a/core/assets-raw/sprites/blocks/turrets/salvo-panel-right.png b/core/assets-raw/sprites/blocks/turrets/salvo-panel-right.png new file mode 100644 index 0000000000..462f082f2a Binary files /dev/null and b/core/assets-raw/sprites/blocks/turrets/salvo-panel-right.png differ diff --git a/core/assets-raw/sprites/blocks/turrets/salvo.png b/core/assets-raw/sprites/blocks/turrets/salvo.png new file mode 100644 index 0000000000..c15a9d47cd Binary files /dev/null and b/core/assets-raw/sprites/blocks/turrets/salvo.png differ diff --git a/core/assets-raw/sprites/blocks/turrets/scatter.png b/core/assets-raw/sprites/blocks/turrets/scatter.png new file mode 100644 index 0000000000..3a9caea04a Binary files /dev/null and b/core/assets-raw/sprites/blocks/turrets/scatter.png differ diff --git a/core/assets-raw/sprites/blocks/turrets/scorch-heat.png b/core/assets-raw/sprites/blocks/turrets/scorch-heat.png new file mode 100644 index 0000000000..84863ae266 Binary files /dev/null and b/core/assets-raw/sprites/blocks/turrets/scorch-heat.png differ diff --git a/core/assets-raw/sprites/blocks/turrets/scorch.png b/core/assets-raw/sprites/blocks/turrets/scorch.png new file mode 100644 index 0000000000..1ab0fbda81 Binary files /dev/null and b/core/assets-raw/sprites/blocks/turrets/scorch.png differ diff --git a/core/assets-raw/sprites/blocks/turrets/spectre.png b/core/assets-raw/sprites/blocks/turrets/spectre.png new file mode 100644 index 0000000000..71ab4e5d0e Binary files /dev/null and b/core/assets-raw/sprites/blocks/turrets/spectre.png differ diff --git a/core/assets-raw/sprites/blocks/turrets/swarmer.png b/core/assets-raw/sprites/blocks/turrets/swarmer.png new file mode 100644 index 0000000000..60aeb73158 Binary files /dev/null and b/core/assets-raw/sprites/blocks/turrets/swarmer.png differ diff --git a/core/assets-raw/sprites/blocks/turrets/wave-liquid.png b/core/assets-raw/sprites/blocks/turrets/wave-liquid.png new file mode 100644 index 0000000000..289f824356 Binary files /dev/null and b/core/assets-raw/sprites/blocks/turrets/wave-liquid.png differ diff --git a/core/assets-raw/sprites/blocks/turrets/wave.png b/core/assets-raw/sprites/blocks/turrets/wave.png new file mode 100644 index 0000000000..458b42bfb8 Binary files /dev/null and b/core/assets-raw/sprites/blocks/turrets/wave.png differ diff --git a/core/assets-raw/sprites/blocks/units/crawler-factory-top.png b/core/assets-raw/sprites/blocks/units/crawler-factory-top.png new file mode 100644 index 0000000000..32c053ef19 Binary files /dev/null and b/core/assets-raw/sprites/blocks/units/crawler-factory-top.png differ diff --git a/core/assets-raw/sprites/blocks/units/crawler-factory.png b/core/assets-raw/sprites/blocks/units/crawler-factory.png new file mode 100644 index 0000000000..72105e12eb Binary files /dev/null and b/core/assets-raw/sprites/blocks/units/crawler-factory.png differ diff --git a/core/assets-raw/sprites/blocks/units/dagger-factory-top.png b/core/assets-raw/sprites/blocks/units/dagger-factory-top.png new file mode 100644 index 0000000000..3297ef4435 Binary files /dev/null and b/core/assets-raw/sprites/blocks/units/dagger-factory-top.png differ diff --git a/core/assets-raw/sprites/blocks/units/dagger-factory.png b/core/assets-raw/sprites/blocks/units/dagger-factory.png new file mode 100644 index 0000000000..72105e12eb Binary files /dev/null and b/core/assets-raw/sprites/blocks/units/dagger-factory.png differ diff --git a/core/assets-raw/sprites/blocks/units/draug-factory-top.png b/core/assets-raw/sprites/blocks/units/draug-factory-top.png new file mode 100644 index 0000000000..c20fe8d186 Binary files /dev/null and b/core/assets-raw/sprites/blocks/units/draug-factory-top.png differ diff --git a/core/assets-raw/sprites/blocks/units/draug-factory.png b/core/assets-raw/sprites/blocks/units/draug-factory.png new file mode 100644 index 0000000000..72105e12eb Binary files /dev/null and b/core/assets-raw/sprites/blocks/units/draug-factory.png differ diff --git a/core/assets-raw/sprites/blocks/units/fortress-factory-top.png b/core/assets-raw/sprites/blocks/units/fortress-factory-top.png new file mode 100644 index 0000000000..0686a2c329 Binary files /dev/null and b/core/assets-raw/sprites/blocks/units/fortress-factory-top.png differ diff --git a/core/assets-raw/sprites/blocks/units/fortress-factory.png b/core/assets-raw/sprites/blocks/units/fortress-factory.png new file mode 100644 index 0000000000..136bf7754d Binary files /dev/null and b/core/assets-raw/sprites/blocks/units/fortress-factory.png differ diff --git a/core/assets-raw/sprites/blocks/units/ghoul-factory-top.png b/core/assets-raw/sprites/blocks/units/ghoul-factory-top.png new file mode 100644 index 0000000000..0686a2c329 Binary files /dev/null and b/core/assets-raw/sprites/blocks/units/ghoul-factory-top.png differ diff --git a/core/assets-raw/sprites/blocks/units/ghoul-factory.png b/core/assets-raw/sprites/blocks/units/ghoul-factory.png new file mode 100644 index 0000000000..38566929b8 Binary files /dev/null and b/core/assets-raw/sprites/blocks/units/ghoul-factory.png differ diff --git a/core/assets-raw/sprites/blocks/units/phantom-factory-top.png b/core/assets-raw/sprites/blocks/units/phantom-factory-top.png new file mode 100644 index 0000000000..1107f27d6c Binary files /dev/null and b/core/assets-raw/sprites/blocks/units/phantom-factory-top.png differ diff --git a/core/assets-raw/sprites/blocks/units/phantom-factory.png b/core/assets-raw/sprites/blocks/units/phantom-factory.png new file mode 100644 index 0000000000..72105e12eb Binary files /dev/null and b/core/assets-raw/sprites/blocks/units/phantom-factory.png differ diff --git a/core/assets-raw/sprites/blocks/units/repair-point-base.png b/core/assets-raw/sprites/blocks/units/repair-point-base.png new file mode 100644 index 0000000000..d5ead6f001 Binary files /dev/null and b/core/assets-raw/sprites/blocks/units/repair-point-base.png differ diff --git a/core/assets-raw/sprites/blocks/units/repair-point.png b/core/assets-raw/sprites/blocks/units/repair-point.png new file mode 100644 index 0000000000..5fe3b143fd Binary files /dev/null and b/core/assets-raw/sprites/blocks/units/repair-point.png differ diff --git a/core/assets-raw/sprites/blocks/units/revenant-factory-top.png b/core/assets-raw/sprites/blocks/units/revenant-factory-top.png new file mode 100644 index 0000000000..79ca249876 Binary files /dev/null and b/core/assets-raw/sprites/blocks/units/revenant-factory-top.png differ diff --git a/core/assets-raw/sprites/blocks/units/revenant-factory.png b/core/assets-raw/sprites/blocks/units/revenant-factory.png new file mode 100644 index 0000000000..acc7389987 Binary files /dev/null and b/core/assets-raw/sprites/blocks/units/revenant-factory.png differ diff --git a/core/assets-raw/sprites/blocks/units/spirit-factory-top.png b/core/assets-raw/sprites/blocks/units/spirit-factory-top.png new file mode 100644 index 0000000000..a825a19d9b Binary files /dev/null and b/core/assets-raw/sprites/blocks/units/spirit-factory-top.png differ diff --git a/core/assets-raw/sprites/blocks/units/spirit-factory.png b/core/assets-raw/sprites/blocks/units/spirit-factory.png new file mode 100644 index 0000000000..72105e12eb Binary files /dev/null and b/core/assets-raw/sprites/blocks/units/spirit-factory.png differ diff --git a/core/assets-raw/sprites/blocks/units/titan-factory-top.png b/core/assets-raw/sprites/blocks/units/titan-factory-top.png new file mode 100644 index 0000000000..0686a2c329 Binary files /dev/null and b/core/assets-raw/sprites/blocks/units/titan-factory-top.png differ diff --git a/core/assets-raw/sprites/blocks/units/titan-factory.png b/core/assets-raw/sprites/blocks/units/titan-factory.png new file mode 100644 index 0000000000..83eecc696c Binary files /dev/null and b/core/assets-raw/sprites/blocks/units/titan-factory.png differ diff --git a/core/assets-raw/sprites/blocks/units/wraith-factory-top.png b/core/assets-raw/sprites/blocks/units/wraith-factory-top.png new file mode 100644 index 0000000000..f1075f1097 Binary files /dev/null and b/core/assets-raw/sprites/blocks/units/wraith-factory-top.png differ diff --git a/core/assets-raw/sprites/blocks/units/wraith-factory.png b/core/assets-raw/sprites/blocks/units/wraith-factory.png new file mode 100644 index 0000000000..72105e12eb Binary files /dev/null and b/core/assets-raw/sprites/blocks/units/wraith-factory.png differ diff --git a/core/assets-raw/sprites/blocks/uranium1.png b/core/assets-raw/sprites/blocks/uranium1.png deleted file mode 100644 index c89c866a93..0000000000 Binary files a/core/assets-raw/sprites/blocks/uranium1.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/uranium2.png b/core/assets-raw/sprites/blocks/uranium2.png deleted file mode 100644 index 20eb054ec3..0000000000 Binary files a/core/assets-raw/sprites/blocks/uranium2.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/uranium3.png b/core/assets-raw/sprites/blocks/uranium3.png deleted file mode 100644 index dfa5ed2cc2..0000000000 Binary files a/core/assets-raw/sprites/blocks/uranium3.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/uraniumdrill.png b/core/assets-raw/sprites/blocks/uraniumdrill.png deleted file mode 100644 index 7849b7cecd..0000000000 Binary files a/core/assets-raw/sprites/blocks/uraniumdrill.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/walls/copper-wall-large.png b/core/assets-raw/sprites/blocks/walls/copper-wall-large.png new file mode 100644 index 0000000000..e3b5102636 Binary files /dev/null and b/core/assets-raw/sprites/blocks/walls/copper-wall-large.png differ diff --git a/core/assets-raw/sprites/blocks/walls/copper-wall.png b/core/assets-raw/sprites/blocks/walls/copper-wall.png new file mode 100644 index 0000000000..6105f1ecf3 Binary files /dev/null and b/core/assets-raw/sprites/blocks/walls/copper-wall.png differ diff --git a/core/assets-raw/sprites/blocks/walls/door-large-open.png b/core/assets-raw/sprites/blocks/walls/door-large-open.png new file mode 100644 index 0000000000..b66dfdace9 Binary files /dev/null and b/core/assets-raw/sprites/blocks/walls/door-large-open.png differ diff --git a/core/assets-raw/sprites/blocks/walls/door-large.png b/core/assets-raw/sprites/blocks/walls/door-large.png new file mode 100644 index 0000000000..0736d8ca38 Binary files /dev/null and b/core/assets-raw/sprites/blocks/walls/door-large.png differ diff --git a/core/assets-raw/sprites/blocks/walls/door-open.png b/core/assets-raw/sprites/blocks/walls/door-open.png new file mode 100644 index 0000000000..0fbe23ebac Binary files /dev/null and b/core/assets-raw/sprites/blocks/walls/door-open.png differ diff --git a/core/assets-raw/sprites/blocks/walls/door.png b/core/assets-raw/sprites/blocks/walls/door.png new file mode 100644 index 0000000000..2218e23067 Binary files /dev/null and b/core/assets-raw/sprites/blocks/walls/door.png differ diff --git a/core/assets-raw/sprites/blocks/walls/phase-wall-large.png b/core/assets-raw/sprites/blocks/walls/phase-wall-large.png new file mode 100644 index 0000000000..3ebc1d33e0 Binary files /dev/null and b/core/assets-raw/sprites/blocks/walls/phase-wall-large.png differ diff --git a/core/assets-raw/sprites/blocks/walls/phase-wall.png b/core/assets-raw/sprites/blocks/walls/phase-wall.png new file mode 100644 index 0000000000..7566b2b17a Binary files /dev/null and b/core/assets-raw/sprites/blocks/walls/phase-wall.png differ diff --git a/core/assets-raw/sprites/blocks/walls/scrap-wall-gigantic.png b/core/assets-raw/sprites/blocks/walls/scrap-wall-gigantic.png new file mode 100644 index 0000000000..a5ed4a3117 Binary files /dev/null and b/core/assets-raw/sprites/blocks/walls/scrap-wall-gigantic.png differ diff --git a/core/assets-raw/sprites/blocks/walls/scrap-wall-huge1.png b/core/assets-raw/sprites/blocks/walls/scrap-wall-huge1.png new file mode 100644 index 0000000000..4a23912802 Binary files /dev/null and b/core/assets-raw/sprites/blocks/walls/scrap-wall-huge1.png differ diff --git a/core/assets-raw/sprites/blocks/walls/scrap-wall-huge2.png b/core/assets-raw/sprites/blocks/walls/scrap-wall-huge2.png new file mode 100644 index 0000000000..aa87272b1c Binary files /dev/null and b/core/assets-raw/sprites/blocks/walls/scrap-wall-huge2.png differ diff --git a/core/assets-raw/sprites/blocks/walls/scrap-wall-huge3.png b/core/assets-raw/sprites/blocks/walls/scrap-wall-huge3.png new file mode 100644 index 0000000000..04f1382d4d Binary files /dev/null and b/core/assets-raw/sprites/blocks/walls/scrap-wall-huge3.png differ diff --git a/core/assets-raw/sprites/blocks/walls/scrap-wall-large1.png b/core/assets-raw/sprites/blocks/walls/scrap-wall-large1.png new file mode 100644 index 0000000000..6f5b66a672 Binary files /dev/null and b/core/assets-raw/sprites/blocks/walls/scrap-wall-large1.png differ diff --git a/core/assets-raw/sprites/blocks/walls/scrap-wall-large2.png b/core/assets-raw/sprites/blocks/walls/scrap-wall-large2.png new file mode 100644 index 0000000000..d271f8e16a Binary files /dev/null and b/core/assets-raw/sprites/blocks/walls/scrap-wall-large2.png differ diff --git a/core/assets-raw/sprites/blocks/walls/scrap-wall-large3.png b/core/assets-raw/sprites/blocks/walls/scrap-wall-large3.png new file mode 100644 index 0000000000..89bdcffae7 Binary files /dev/null and b/core/assets-raw/sprites/blocks/walls/scrap-wall-large3.png differ diff --git a/core/assets-raw/sprites/blocks/walls/scrap-wall-large4.png b/core/assets-raw/sprites/blocks/walls/scrap-wall-large4.png new file mode 100644 index 0000000000..ef76eb0c51 Binary files /dev/null and b/core/assets-raw/sprites/blocks/walls/scrap-wall-large4.png differ diff --git a/core/assets-raw/sprites/blocks/walls/scrap-wall1.png b/core/assets-raw/sprites/blocks/walls/scrap-wall1.png new file mode 100644 index 0000000000..b0be0f2747 Binary files /dev/null and b/core/assets-raw/sprites/blocks/walls/scrap-wall1.png differ diff --git a/core/assets-raw/sprites/blocks/walls/scrap-wall2.png b/core/assets-raw/sprites/blocks/walls/scrap-wall2.png new file mode 100644 index 0000000000..078bd13aa2 Binary files /dev/null and b/core/assets-raw/sprites/blocks/walls/scrap-wall2.png differ diff --git a/core/assets-raw/sprites/blocks/walls/scrap-wall3.png b/core/assets-raw/sprites/blocks/walls/scrap-wall3.png new file mode 100644 index 0000000000..de2e17d6f2 Binary files /dev/null and b/core/assets-raw/sprites/blocks/walls/scrap-wall3.png differ diff --git a/core/assets-raw/sprites/blocks/walls/scrap-wall4.png b/core/assets-raw/sprites/blocks/walls/scrap-wall4.png new file mode 100644 index 0000000000..0cd3df9ca4 Binary files /dev/null and b/core/assets-raw/sprites/blocks/walls/scrap-wall4.png differ diff --git a/core/assets-raw/sprites/blocks/walls/scrap-wall5.png b/core/assets-raw/sprites/blocks/walls/scrap-wall5.png new file mode 100644 index 0000000000..0cd3df9ca4 Binary files /dev/null and b/core/assets-raw/sprites/blocks/walls/scrap-wall5.png differ diff --git a/core/assets-raw/sprites/blocks/walls/surge-wall-large.png b/core/assets-raw/sprites/blocks/walls/surge-wall-large.png new file mode 100644 index 0000000000..2c17759178 Binary files /dev/null and b/core/assets-raw/sprites/blocks/walls/surge-wall-large.png differ diff --git a/core/assets-raw/sprites/blocks/walls/surge-wall.png b/core/assets-raw/sprites/blocks/walls/surge-wall.png new file mode 100644 index 0000000000..154fb95ce8 Binary files /dev/null and b/core/assets-raw/sprites/blocks/walls/surge-wall.png differ diff --git a/core/assets-raw/sprites/blocks/walls/thorium-wall-large.png b/core/assets-raw/sprites/blocks/walls/thorium-wall-large.png new file mode 100644 index 0000000000..0a6f50e3ba Binary files /dev/null and b/core/assets-raw/sprites/blocks/walls/thorium-wall-large.png differ diff --git a/core/assets-raw/sprites/blocks/walls/thorium-wall.png b/core/assets-raw/sprites/blocks/walls/thorium-wall.png new file mode 100644 index 0000000000..e4ccef59f4 Binary files /dev/null and b/core/assets-raw/sprites/blocks/walls/thorium-wall.png differ diff --git a/core/assets-raw/sprites/blocks/walls/thruster.png b/core/assets-raw/sprites/blocks/walls/thruster.png new file mode 100644 index 0000000000..0725cc31b2 Binary files /dev/null and b/core/assets-raw/sprites/blocks/walls/thruster.png differ diff --git a/core/assets-raw/sprites/blocks/walls/titanium-wall-large.png b/core/assets-raw/sprites/blocks/walls/titanium-wall-large.png new file mode 100644 index 0000000000..69194bf884 Binary files /dev/null and b/core/assets-raw/sprites/blocks/walls/titanium-wall-large.png differ diff --git a/core/assets-raw/sprites/blocks/walls/titanium-wall.png b/core/assets-raw/sprites/blocks/walls/titanium-wall.png new file mode 100644 index 0000000000..8eca3fe8a4 Binary files /dev/null and b/core/assets-raw/sprites/blocks/walls/titanium-wall.png differ diff --git a/core/assets-raw/sprites/blocks/water.png b/core/assets-raw/sprites/blocks/water.png deleted file mode 100644 index 9855f81c1a..0000000000 Binary files a/core/assets-raw/sprites/blocks/water.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/wateredge.png b/core/assets-raw/sprites/blocks/wateredge.png deleted file mode 100644 index cca7fdd058..0000000000 Binary files a/core/assets-raw/sprites/blocks/wateredge.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/waveturret.png b/core/assets-raw/sprites/blocks/waveturret.png deleted file mode 100644 index 7ca8d8a3f8..0000000000 Binary files a/core/assets-raw/sprites/blocks/waveturret.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/weaponfactory-icon.png b/core/assets-raw/sprites/blocks/weaponfactory-icon.png deleted file mode 100644 index 27065c56af..0000000000 Binary files a/core/assets-raw/sprites/blocks/weaponfactory-icon.png and /dev/null differ diff --git a/core/assets-raw/sprites/blocks/weaponfactory.png b/core/assets-raw/sprites/blocks/weaponfactory.png deleted file mode 100644 index 0938f64971..0000000000 Binary files a/core/assets-raw/sprites/blocks/weaponfactory.png and /dev/null differ diff --git a/core/assets-raw/sprites/bullet.png b/core/assets-raw/sprites/bullet.png deleted file mode 100644 index 5f1c82cdbc..0000000000 Binary files a/core/assets-raw/sprites/bullet.png and /dev/null differ diff --git a/core/assets-raw/sprites/chainbullet.png b/core/assets-raw/sprites/chainbullet.png deleted file mode 100644 index aaf1228374..0000000000 Binary files a/core/assets-raw/sprites/chainbullet.png and /dev/null differ diff --git a/core/assets-raw/sprites/circle.png b/core/assets-raw/sprites/circle.png deleted file mode 100644 index 154fd0dd83..0000000000 Binary files a/core/assets-raw/sprites/circle.png and /dev/null differ diff --git a/core/assets-raw/sprites/circle2.png b/core/assets-raw/sprites/circle2.png deleted file mode 100644 index 52a2d983f8..0000000000 Binary files a/core/assets-raw/sprites/circle2.png and /dev/null differ diff --git a/core/assets-raw/sprites/editor/block-border-editor.png b/core/assets-raw/sprites/editor/block-border-editor.png new file mode 100644 index 0000000000..415c5a6849 Binary files /dev/null and b/core/assets-raw/sprites/editor/block-border-editor.png differ diff --git a/core/assets-raw/sprites/editor/clear-editor.png b/core/assets-raw/sprites/editor/clear-editor.png new file mode 100644 index 0000000000..a804d435b6 Binary files /dev/null and b/core/assets-raw/sprites/editor/clear-editor.png differ diff --git a/core/assets-raw/sprites/editor/pack.json b/core/assets-raw/sprites/editor/pack.json new file mode 100644 index 0000000000..2612f17acc --- /dev/null +++ b/core/assets-raw/sprites/editor/pack.json @@ -0,0 +1,8 @@ +{ + duplicatePadding: true, + combineSubdirectories: true, + flattenPaths: true, + maxWidth: 2048, + maxHeight: 2048, + fast: true +} diff --git a/core/assets-raw/sprites/effects/bullet-back.png b/core/assets-raw/sprites/effects/bullet-back.png new file mode 100644 index 0000000000..d5dccee0d7 Binary files /dev/null and b/core/assets-raw/sprites/effects/bullet-back.png differ diff --git a/core/assets-raw/sprites/effects/bullet.png b/core/assets-raw/sprites/effects/bullet.png new file mode 100644 index 0000000000..0b5dd8e8ac Binary files /dev/null and b/core/assets-raw/sprites/effects/bullet.png differ diff --git a/core/assets-raw/sprites/effects/casing.png b/core/assets-raw/sprites/effects/casing.png new file mode 100644 index 0000000000..37b0613251 Binary files /dev/null and b/core/assets-raw/sprites/effects/casing.png differ diff --git a/core/assets-raw/sprites/effects/circle-shadow.png b/core/assets-raw/sprites/effects/circle-shadow.png new file mode 100644 index 0000000000..2e9aaec802 Binary files /dev/null and b/core/assets-raw/sprites/effects/circle-shadow.png differ diff --git a/core/assets-raw/sprites/effects/error.png b/core/assets-raw/sprites/effects/error.png new file mode 100644 index 0000000000..f784bec923 Binary files /dev/null and b/core/assets-raw/sprites/effects/error.png differ diff --git a/core/assets-raw/sprites/effects/laser-end.png b/core/assets-raw/sprites/effects/laser-end.png new file mode 100644 index 0000000000..dc90d3620b Binary files /dev/null and b/core/assets-raw/sprites/effects/laser-end.png differ diff --git a/core/assets-raw/sprites/effects/laser.png b/core/assets-raw/sprites/effects/laser.png new file mode 100644 index 0000000000..0072419acc Binary files /dev/null and b/core/assets-raw/sprites/effects/laser.png differ diff --git a/core/assets-raw/sprites/effects/minelaser-end.png b/core/assets-raw/sprites/effects/minelaser-end.png new file mode 100644 index 0000000000..46ab615894 Binary files /dev/null and b/core/assets-raw/sprites/effects/minelaser-end.png differ diff --git a/core/assets-raw/sprites/effects/minelaser.png b/core/assets-raw/sprites/effects/minelaser.png new file mode 100644 index 0000000000..21acfa15d4 Binary files /dev/null and b/core/assets-raw/sprites/effects/minelaser.png differ diff --git a/core/assets-raw/sprites/effects/missile-back.png b/core/assets-raw/sprites/effects/missile-back.png new file mode 100644 index 0000000000..0aa9893a33 Binary files /dev/null and b/core/assets-raw/sprites/effects/missile-back.png differ diff --git a/core/assets-raw/sprites/effects/missile.png b/core/assets-raw/sprites/effects/missile.png new file mode 100644 index 0000000000..9f9f3831c3 Binary files /dev/null and b/core/assets-raw/sprites/effects/missile.png differ diff --git a/core/assets-raw/sprites/effects/scale_marker.png b/core/assets-raw/sprites/effects/scale_marker.png new file mode 100644 index 0000000000..109b5dafa3 Binary files /dev/null and b/core/assets-raw/sprites/effects/scale_marker.png differ diff --git a/core/assets-raw/sprites/effects/scorch1.png b/core/assets-raw/sprites/effects/scorch1.png new file mode 100644 index 0000000000..9044dc696c Binary files /dev/null and b/core/assets-raw/sprites/effects/scorch1.png differ diff --git a/core/assets-raw/sprites/effects/scorch2.png b/core/assets-raw/sprites/effects/scorch2.png new file mode 100644 index 0000000000..e5f2f11105 Binary files /dev/null and b/core/assets-raw/sprites/effects/scorch2.png differ diff --git a/core/assets-raw/sprites/effects/scorch3.png b/core/assets-raw/sprites/effects/scorch3.png new file mode 100644 index 0000000000..555ca5f288 Binary files /dev/null and b/core/assets-raw/sprites/effects/scorch3.png differ diff --git a/core/assets-raw/sprites/effects/scorch4.png b/core/assets-raw/sprites/effects/scorch4.png new file mode 100644 index 0000000000..981a1c8ac4 Binary files /dev/null and b/core/assets-raw/sprites/effects/scorch4.png differ diff --git a/core/assets-raw/sprites/effects/scorch5.png b/core/assets-raw/sprites/effects/scorch5.png new file mode 100644 index 0000000000..0a8d1e2ef2 Binary files /dev/null and b/core/assets-raw/sprites/effects/scorch5.png differ diff --git a/core/assets-raw/sprites/effects/shell-back.png b/core/assets-raw/sprites/effects/shell-back.png new file mode 100644 index 0000000000..2702b8f463 Binary files /dev/null and b/core/assets-raw/sprites/effects/shell-back.png differ diff --git a/core/assets-raw/sprites/effects/shell.png b/core/assets-raw/sprites/effects/shell.png new file mode 100644 index 0000000000..72969437f9 Binary files /dev/null and b/core/assets-raw/sprites/effects/shell.png differ diff --git a/core/assets-raw/sprites/effects/shot.png b/core/assets-raw/sprites/effects/shot.png new file mode 100644 index 0000000000..98a45162f4 Binary files /dev/null and b/core/assets-raw/sprites/effects/shot.png differ diff --git a/core/assets-raw/sprites/effects/transfer-arrow.png b/core/assets-raw/sprites/effects/transfer-arrow.png new file mode 100644 index 0000000000..16d71b0c5e Binary files /dev/null and b/core/assets-raw/sprites/effects/transfer-arrow.png differ diff --git a/core/assets-raw/sprites/effects/transfer-end.png b/core/assets-raw/sprites/effects/transfer-end.png new file mode 100644 index 0000000000..8579ff179e Binary files /dev/null and b/core/assets-raw/sprites/effects/transfer-end.png differ diff --git a/core/assets-raw/sprites/effects/transfer.png b/core/assets-raw/sprites/effects/transfer.png new file mode 100644 index 0000000000..653219d8dd Binary files /dev/null and b/core/assets-raw/sprites/effects/transfer.png differ diff --git a/core/assets-raw/sprites/enemies/blastenemy-t1.png b/core/assets-raw/sprites/enemies/blastenemy-t1.png deleted file mode 100644 index 54d3d77722..0000000000 Binary files a/core/assets-raw/sprites/enemies/blastenemy-t1.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/blastenemy-t2.png b/core/assets-raw/sprites/enemies/blastenemy-t2.png deleted file mode 100644 index d48d5c3447..0000000000 Binary files a/core/assets-raw/sprites/enemies/blastenemy-t2.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/blastenemy-t3.png b/core/assets-raw/sprites/enemies/blastenemy-t3.png deleted file mode 100644 index 18c2172c68..0000000000 Binary files a/core/assets-raw/sprites/enemies/blastenemy-t3.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/empenemy-t1.png b/core/assets-raw/sprites/enemies/empenemy-t1.png deleted file mode 100644 index 0161483346..0000000000 Binary files a/core/assets-raw/sprites/enemies/empenemy-t1.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/empenemy-t2.png b/core/assets-raw/sprites/enemies/empenemy-t2.png deleted file mode 100644 index 2779405f89..0000000000 Binary files a/core/assets-raw/sprites/enemies/empenemy-t2.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/empenemy-t3.png b/core/assets-raw/sprites/enemies/empenemy-t3.png deleted file mode 100644 index c82d2e6943..0000000000 Binary files a/core/assets-raw/sprites/enemies/empenemy-t3.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/fastenemy-t1.png b/core/assets-raw/sprites/enemies/fastenemy-t1.png deleted file mode 100644 index dd6eedee5a..0000000000 Binary files a/core/assets-raw/sprites/enemies/fastenemy-t1.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/fastenemy-t2.png b/core/assets-raw/sprites/enemies/fastenemy-t2.png deleted file mode 100644 index 525fb577ea..0000000000 Binary files a/core/assets-raw/sprites/enemies/fastenemy-t2.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/fastenemy-t3.png b/core/assets-raw/sprites/enemies/fastenemy-t3.png deleted file mode 100644 index fc447b6bd7..0000000000 Binary files a/core/assets-raw/sprites/enemies/fastenemy-t3.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/flamerenemy-t1.png b/core/assets-raw/sprites/enemies/flamerenemy-t1.png deleted file mode 100644 index 4d13b33bea..0000000000 Binary files a/core/assets-raw/sprites/enemies/flamerenemy-t1.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/flamerenemy-t2.png b/core/assets-raw/sprites/enemies/flamerenemy-t2.png deleted file mode 100644 index c4b331c34b..0000000000 Binary files a/core/assets-raw/sprites/enemies/flamerenemy-t2.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/flamerenemy-t3.png b/core/assets-raw/sprites/enemies/flamerenemy-t3.png deleted file mode 100644 index 91bfb2c5d7..0000000000 Binary files a/core/assets-raw/sprites/enemies/flamerenemy-t3.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/fortressenemy-t1.png b/core/assets-raw/sprites/enemies/fortressenemy-t1.png deleted file mode 100644 index 08af9a95b3..0000000000 Binary files a/core/assets-raw/sprites/enemies/fortressenemy-t1.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/fortressenemy-t2.png b/core/assets-raw/sprites/enemies/fortressenemy-t2.png deleted file mode 100644 index 07eec2cd31..0000000000 Binary files a/core/assets-raw/sprites/enemies/fortressenemy-t2.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/fortressenemy-t3.png b/core/assets-raw/sprites/enemies/fortressenemy-t3.png deleted file mode 100644 index 7beb99e5f7..0000000000 Binary files a/core/assets-raw/sprites/enemies/fortressenemy-t3.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/healerenemy-t1.png b/core/assets-raw/sprites/enemies/healerenemy-t1.png deleted file mode 100644 index a2a019f515..0000000000 Binary files a/core/assets-raw/sprites/enemies/healerenemy-t1.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/healerenemy-t2.png b/core/assets-raw/sprites/enemies/healerenemy-t2.png deleted file mode 100644 index c46199589e..0000000000 Binary files a/core/assets-raw/sprites/enemies/healerenemy-t2.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/healerenemy-t3.png b/core/assets-raw/sprites/enemies/healerenemy-t3.png deleted file mode 100644 index 736533e97e..0000000000 Binary files a/core/assets-raw/sprites/enemies/healerenemy-t3.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/mortarenemy-t1.png b/core/assets-raw/sprites/enemies/mortarenemy-t1.png deleted file mode 100644 index 71d6ace244..0000000000 Binary files a/core/assets-raw/sprites/enemies/mortarenemy-t1.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/mortarenemy-t2.png b/core/assets-raw/sprites/enemies/mortarenemy-t2.png deleted file mode 100644 index 71072a08ea..0000000000 Binary files a/core/assets-raw/sprites/enemies/mortarenemy-t2.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/mortarenemy-t3.png b/core/assets-raw/sprites/enemies/mortarenemy-t3.png deleted file mode 100644 index 74845b4a2b..0000000000 Binary files a/core/assets-raw/sprites/enemies/mortarenemy-t3.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/rapidenemy-t1.png b/core/assets-raw/sprites/enemies/rapidenemy-t1.png deleted file mode 100644 index 4266c7839a..0000000000 Binary files a/core/assets-raw/sprites/enemies/rapidenemy-t1.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/rapidenemy-t2.png b/core/assets-raw/sprites/enemies/rapidenemy-t2.png deleted file mode 100644 index d92b7a1e96..0000000000 Binary files a/core/assets-raw/sprites/enemies/rapidenemy-t2.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/rapidenemy-t3.png b/core/assets-raw/sprites/enemies/rapidenemy-t3.png deleted file mode 100644 index e8fd4c0fe4..0000000000 Binary files a/core/assets-raw/sprites/enemies/rapidenemy-t3.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/standardenemy-t1.png b/core/assets-raw/sprites/enemies/standardenemy-t1.png deleted file mode 100644 index 72d033db76..0000000000 Binary files a/core/assets-raw/sprites/enemies/standardenemy-t1.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/standardenemy-t2.png b/core/assets-raw/sprites/enemies/standardenemy-t2.png deleted file mode 100644 index 642d06cb6c..0000000000 Binary files a/core/assets-raw/sprites/enemies/standardenemy-t2.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/standardenemy-t3.png b/core/assets-raw/sprites/enemies/standardenemy-t3.png deleted file mode 100644 index c02539e6c5..0000000000 Binary files a/core/assets-raw/sprites/enemies/standardenemy-t3.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/tankenemy-t1.png b/core/assets-raw/sprites/enemies/tankenemy-t1.png deleted file mode 100644 index 059bc6e6c7..0000000000 Binary files a/core/assets-raw/sprites/enemies/tankenemy-t1.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/tankenemy-t2.png b/core/assets-raw/sprites/enemies/tankenemy-t2.png deleted file mode 100644 index a959046fc9..0000000000 Binary files a/core/assets-raw/sprites/enemies/tankenemy-t2.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/tankenemy-t3.png b/core/assets-raw/sprites/enemies/tankenemy-t3.png deleted file mode 100644 index 836275cdb3..0000000000 Binary files a/core/assets-raw/sprites/enemies/tankenemy-t3.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/targetenemy-t1.png b/core/assets-raw/sprites/enemies/targetenemy-t1.png deleted file mode 100644 index 72d033db76..0000000000 Binary files a/core/assets-raw/sprites/enemies/targetenemy-t1.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/titanenemy-t1.png b/core/assets-raw/sprites/enemies/titanenemy-t1.png deleted file mode 100644 index 204488c950..0000000000 Binary files a/core/assets-raw/sprites/enemies/titanenemy-t1.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/titanenemy-t2.png b/core/assets-raw/sprites/enemies/titanenemy-t2.png deleted file mode 100644 index 9acc6a5181..0000000000 Binary files a/core/assets-raw/sprites/enemies/titanenemy-t2.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemies/titanenemy-t3.png b/core/assets-raw/sprites/enemies/titanenemy-t3.png deleted file mode 100644 index 162a2c50bd..0000000000 Binary files a/core/assets-raw/sprites/enemies/titanenemy-t3.png and /dev/null differ diff --git a/core/assets-raw/sprites/enemyarrow.png b/core/assets-raw/sprites/enemyarrow.png deleted file mode 100644 index 04e4f069be..0000000000 Binary files a/core/assets-raw/sprites/enemyarrow.png and /dev/null differ diff --git a/core/assets-raw/sprites/icon-coal.png b/core/assets-raw/sprites/icon-coal.png deleted file mode 100644 index 1da1774788..0000000000 Binary files a/core/assets-raw/sprites/icon-coal.png and /dev/null differ diff --git a/core/assets-raw/sprites/icon-dirium.png b/core/assets-raw/sprites/icon-dirium.png deleted file mode 100644 index df7bfef7ab..0000000000 Binary files a/core/assets-raw/sprites/icon-dirium.png and /dev/null differ diff --git a/core/assets-raw/sprites/icon-iron.png b/core/assets-raw/sprites/icon-iron.png deleted file mode 100644 index 46b05fb505..0000000000 Binary files a/core/assets-raw/sprites/icon-iron.png and /dev/null differ diff --git a/core/assets-raw/sprites/icon-sand.png b/core/assets-raw/sprites/icon-sand.png deleted file mode 100644 index 6492eb7b9d..0000000000 Binary files a/core/assets-raw/sprites/icon-sand.png and /dev/null differ diff --git a/core/assets-raw/sprites/icon-steel.png b/core/assets-raw/sprites/icon-steel.png deleted file mode 100644 index 1f4cd1eda8..0000000000 Binary files a/core/assets-raw/sprites/icon-steel.png and /dev/null differ diff --git a/core/assets-raw/sprites/icon-stone.png b/core/assets-raw/sprites/icon-stone.png deleted file mode 100644 index 3eabbf1cec..0000000000 Binary files a/core/assets-raw/sprites/icon-stone.png and /dev/null differ diff --git a/core/assets-raw/sprites/icon-titanium.png b/core/assets-raw/sprites/icon-titanium.png deleted file mode 100644 index 01e75295de..0000000000 Binary files a/core/assets-raw/sprites/icon-titanium.png and /dev/null differ diff --git a/core/assets-raw/sprites/icon-uranium.png b/core/assets-raw/sprites/icon-uranium.png deleted file mode 100644 index 0dd3f67a71..0000000000 Binary files a/core/assets-raw/sprites/icon-uranium.png and /dev/null differ diff --git a/core/assets-raw/sprites/items/item-biomatter.png b/core/assets-raw/sprites/items/item-biomatter.png new file mode 100644 index 0000000000..6dae991e87 Binary files /dev/null and b/core/assets-raw/sprites/items/item-biomatter.png differ diff --git a/core/assets-raw/sprites/items/item-blast-compound.png b/core/assets-raw/sprites/items/item-blast-compound.png new file mode 100644 index 0000000000..c14eb34b11 Binary files /dev/null and b/core/assets-raw/sprites/items/item-blast-compound.png differ diff --git a/core/assets-raw/sprites/items/item-coal.png b/core/assets-raw/sprites/items/item-coal.png new file mode 100644 index 0000000000..13727cd802 Binary files /dev/null and b/core/assets-raw/sprites/items/item-coal.png differ diff --git a/core/assets-raw/sprites/items/item-copper.png b/core/assets-raw/sprites/items/item-copper.png new file mode 100644 index 0000000000..664c9cc6f3 Binary files /dev/null and b/core/assets-raw/sprites/items/item-copper.png differ diff --git a/core/assets-raw/sprites/items/item-graphite.png b/core/assets-raw/sprites/items/item-graphite.png new file mode 100644 index 0000000000..68b074bdc7 Binary files /dev/null and b/core/assets-raw/sprites/items/item-graphite.png differ diff --git a/core/assets-raw/sprites/items/item-lead.png b/core/assets-raw/sprites/items/item-lead.png new file mode 100644 index 0000000000..ce09ac6185 Binary files /dev/null and b/core/assets-raw/sprites/items/item-lead.png differ diff --git a/core/assets-raw/sprites/items/item-metaglass.png b/core/assets-raw/sprites/items/item-metaglass.png new file mode 100644 index 0000000000..3c45a8873f Binary files /dev/null and b/core/assets-raw/sprites/items/item-metaglass.png differ diff --git a/core/assets-raw/sprites/items/item-phase-fabric.png b/core/assets-raw/sprites/items/item-phase-fabric.png new file mode 100644 index 0000000000..6f226c7a97 Binary files /dev/null and b/core/assets-raw/sprites/items/item-phase-fabric.png differ diff --git a/core/assets-raw/sprites/items/item-plastanium.png b/core/assets-raw/sprites/items/item-plastanium.png new file mode 100644 index 0000000000..6ce75034f3 Binary files /dev/null and b/core/assets-raw/sprites/items/item-plastanium.png differ diff --git a/core/assets-raw/sprites/items/item-pyratite.png b/core/assets-raw/sprites/items/item-pyratite.png new file mode 100644 index 0000000000..eec30df17d Binary files /dev/null and b/core/assets-raw/sprites/items/item-pyratite.png differ diff --git a/core/assets-raw/sprites/items/item-sand.png b/core/assets-raw/sprites/items/item-sand.png new file mode 100644 index 0000000000..ac94898030 Binary files /dev/null and b/core/assets-raw/sprites/items/item-sand.png differ diff --git a/core/assets-raw/sprites/items/item-scrap.png b/core/assets-raw/sprites/items/item-scrap.png new file mode 100644 index 0000000000..0de9f03656 Binary files /dev/null and b/core/assets-raw/sprites/items/item-scrap.png differ diff --git a/core/assets-raw/sprites/items/item-silicon.png b/core/assets-raw/sprites/items/item-silicon.png new file mode 100644 index 0000000000..5d4cfab004 Binary files /dev/null and b/core/assets-raw/sprites/items/item-silicon.png differ diff --git a/core/assets-raw/sprites/items/item-spore-pod.png b/core/assets-raw/sprites/items/item-spore-pod.png new file mode 100644 index 0000000000..aa69408ee7 Binary files /dev/null and b/core/assets-raw/sprites/items/item-spore-pod.png differ diff --git a/core/assets-raw/sprites/items/item-surge-alloy.png b/core/assets-raw/sprites/items/item-surge-alloy.png new file mode 100644 index 0000000000..9fc6aa4561 Binary files /dev/null and b/core/assets-raw/sprites/items/item-surge-alloy.png differ diff --git a/core/assets-raw/sprites/items/item-thorium.png b/core/assets-raw/sprites/items/item-thorium.png new file mode 100644 index 0000000000..2cd39c8f49 Binary files /dev/null and b/core/assets-raw/sprites/items/item-thorium.png differ diff --git a/core/assets-raw/sprites/items/item-titanium.png b/core/assets-raw/sprites/items/item-titanium.png new file mode 100644 index 0000000000..3e15e1e7c7 Binary files /dev/null and b/core/assets-raw/sprites/items/item-titanium.png differ diff --git a/core/assets-raw/sprites/items/liquid-cryofluid.png b/core/assets-raw/sprites/items/liquid-cryofluid.png new file mode 100644 index 0000000000..198c21ad17 Binary files /dev/null and b/core/assets-raw/sprites/items/liquid-cryofluid.png differ diff --git a/core/assets-raw/sprites/items/liquid-oil.png b/core/assets-raw/sprites/items/liquid-oil.png new file mode 100644 index 0000000000..e0fbc07cf2 Binary files /dev/null and b/core/assets-raw/sprites/items/liquid-oil.png differ diff --git a/core/assets-raw/sprites/items/liquid-slag.png b/core/assets-raw/sprites/items/liquid-slag.png new file mode 100644 index 0000000000..87c2a41d07 Binary files /dev/null and b/core/assets-raw/sprites/items/liquid-slag.png differ diff --git a/core/assets-raw/sprites/items/liquid-water.png b/core/assets-raw/sprites/items/liquid-water.png new file mode 100644 index 0000000000..025c812967 Binary files /dev/null and b/core/assets-raw/sprites/items/liquid-water.png differ diff --git a/core/assets-raw/sprites/laser.png b/core/assets-raw/sprites/laser.png deleted file mode 100644 index 4b23a386ca..0000000000 Binary files a/core/assets-raw/sprites/laser.png and /dev/null differ diff --git a/core/assets-raw/sprites/laserend.png b/core/assets-raw/sprites/laserend.png deleted file mode 100644 index d678fb0a81..0000000000 Binary files a/core/assets-raw/sprites/laserend.png and /dev/null differ diff --git a/core/assets-raw/sprites/laserfull.png b/core/assets-raw/sprites/laserfull.png deleted file mode 100644 index 9ea6687b8f..0000000000 Binary files a/core/assets-raw/sprites/laserfull.png and /dev/null differ diff --git a/core/assets-raw/sprites/mechs/mech-standard-icon.png b/core/assets-raw/sprites/mechs/mech-standard-icon.png deleted file mode 100644 index 4546324c10..0000000000 Binary files a/core/assets-raw/sprites/mechs/mech-standard-icon.png and /dev/null differ diff --git a/core/assets-raw/sprites/mechs/mech-standard.png b/core/assets-raw/sprites/mechs/mech-standard.png deleted file mode 100644 index 1b343a9775..0000000000 Binary files a/core/assets-raw/sprites/mechs/mech-standard.png and /dev/null differ diff --git a/core/assets-raw/sprites/mechs/mechs/alpha-mech-base.png b/core/assets-raw/sprites/mechs/mechs/alpha-mech-base.png new file mode 100644 index 0000000000..703f86e430 Binary files /dev/null and b/core/assets-raw/sprites/mechs/mechs/alpha-mech-base.png differ diff --git a/core/assets-raw/sprites/mechs/mechs/alpha-mech-leg.png b/core/assets-raw/sprites/mechs/mechs/alpha-mech-leg.png new file mode 100644 index 0000000000..d60217a0bc Binary files /dev/null and b/core/assets-raw/sprites/mechs/mechs/alpha-mech-leg.png differ diff --git a/core/assets-raw/sprites/mechs/mechs/alpha-mech.png b/core/assets-raw/sprites/mechs/mechs/alpha-mech.png new file mode 100644 index 0000000000..83d62471d5 Binary files /dev/null and b/core/assets-raw/sprites/mechs/mechs/alpha-mech.png differ diff --git a/core/assets-raw/sprites/mechs/mechs/delta-mech-base.png b/core/assets-raw/sprites/mechs/mechs/delta-mech-base.png new file mode 100644 index 0000000000..1e954dfd87 Binary files /dev/null and b/core/assets-raw/sprites/mechs/mechs/delta-mech-base.png differ diff --git a/core/assets-raw/sprites/mechs/mechs/delta-mech-leg.png b/core/assets-raw/sprites/mechs/mechs/delta-mech-leg.png new file mode 100644 index 0000000000..39a650e4b0 Binary files /dev/null and b/core/assets-raw/sprites/mechs/mechs/delta-mech-leg.png differ diff --git a/core/assets-raw/sprites/mechs/mechs/delta-mech.png b/core/assets-raw/sprites/mechs/mechs/delta-mech.png new file mode 100644 index 0000000000..e3d6e6424b Binary files /dev/null and b/core/assets-raw/sprites/mechs/mechs/delta-mech.png differ diff --git a/core/assets-raw/sprites/mechs/mechs/omega-mech-armor.png b/core/assets-raw/sprites/mechs/mechs/omega-mech-armor.png new file mode 100644 index 0000000000..47d531379a Binary files /dev/null and b/core/assets-raw/sprites/mechs/mechs/omega-mech-armor.png differ diff --git a/core/assets-raw/sprites/mechs/mechs/omega-mech-base.png b/core/assets-raw/sprites/mechs/mechs/omega-mech-base.png new file mode 100644 index 0000000000..659de584d9 Binary files /dev/null and b/core/assets-raw/sprites/mechs/mechs/omega-mech-base.png differ diff --git a/core/assets-raw/sprites/mechs/mechs/omega-mech-leg.png b/core/assets-raw/sprites/mechs/mechs/omega-mech-leg.png new file mode 100644 index 0000000000..870875a6d4 Binary files /dev/null and b/core/assets-raw/sprites/mechs/mechs/omega-mech-leg.png differ diff --git a/core/assets-raw/sprites/mechs/mechs/omega-mech.png b/core/assets-raw/sprites/mechs/mechs/omega-mech.png new file mode 100644 index 0000000000..1f7c0ad626 Binary files /dev/null and b/core/assets-raw/sprites/mechs/mechs/omega-mech.png differ diff --git a/core/assets-raw/sprites/mechs/mechs/tau-mech-base.png b/core/assets-raw/sprites/mechs/mechs/tau-mech-base.png new file mode 100644 index 0000000000..6c83eaa41a Binary files /dev/null and b/core/assets-raw/sprites/mechs/mechs/tau-mech-base.png differ diff --git a/core/assets-raw/sprites/mechs/mechs/tau-mech-leg.png b/core/assets-raw/sprites/mechs/mechs/tau-mech-leg.png new file mode 100644 index 0000000000..cf45f61eb9 Binary files /dev/null and b/core/assets-raw/sprites/mechs/mechs/tau-mech-leg.png differ diff --git a/core/assets-raw/sprites/mechs/mechs/tau-mech.png b/core/assets-raw/sprites/mechs/mechs/tau-mech.png new file mode 100644 index 0000000000..9b6b934213 Binary files /dev/null and b/core/assets-raw/sprites/mechs/mechs/tau-mech.png differ diff --git a/core/assets-raw/sprites/mechs/ship-standard.png b/core/assets-raw/sprites/mechs/ship-standard.png deleted file mode 100644 index d0d0dd52ec..0000000000 Binary files a/core/assets-raw/sprites/mechs/ship-standard.png and /dev/null differ diff --git a/core/assets-raw/sprites/mechs/ships/dart-ship.png b/core/assets-raw/sprites/mechs/ships/dart-ship.png new file mode 100644 index 0000000000..6ca70bcc85 Binary files /dev/null and b/core/assets-raw/sprites/mechs/ships/dart-ship.png differ diff --git a/core/assets-raw/sprites/mechs/ships/glaive-ship.png b/core/assets-raw/sprites/mechs/ships/glaive-ship.png new file mode 100644 index 0000000000..34aac96310 Binary files /dev/null and b/core/assets-raw/sprites/mechs/ships/glaive-ship.png differ diff --git a/core/assets-raw/sprites/mechs/ships/javelin-ship-shield.png b/core/assets-raw/sprites/mechs/ships/javelin-ship-shield.png new file mode 100644 index 0000000000..b7bc640522 Binary files /dev/null and b/core/assets-raw/sprites/mechs/ships/javelin-ship-shield.png differ diff --git a/core/assets-raw/sprites/mechs/ships/javelin-ship.png b/core/assets-raw/sprites/mechs/ships/javelin-ship.png new file mode 100644 index 0000000000..174d6e59cd Binary files /dev/null and b/core/assets-raw/sprites/mechs/ships/javelin-ship.png differ diff --git a/core/assets-raw/sprites/mechs/ships/trident-ship.png b/core/assets-raw/sprites/mechs/ships/trident-ship.png new file mode 100644 index 0000000000..0a4c15e5a3 Binary files /dev/null and b/core/assets-raw/sprites/mechs/ships/trident-ship.png differ diff --git a/core/assets-raw/sprites/pack.json b/core/assets-raw/sprites/pack.json index 490bc040eb..2612f17acc 100644 --- a/core/assets-raw/sprites/pack.json +++ b/core/assets-raw/sprites/pack.json @@ -1,5 +1,8 @@ { - duplicatePadding: true, - combineSubdirectories: true, - flattenPaths: true + duplicatePadding: true, + combineSubdirectories: true, + flattenPaths: true, + maxWidth: 2048, + maxHeight: 2048, + fast: true } diff --git a/core/assets-raw/sprites/pack_fallback.json b/core/assets-raw/sprites/pack_fallback.json new file mode 100644 index 0000000000..853c7fc32f --- /dev/null +++ b/core/assets-raw/sprites/pack_fallback.json @@ -0,0 +1,8 @@ +{ + duplicatePadding: true, + combineSubdirectories: true, + flattenPaths: true, + maxWidth: 1024, + maxHeight: 1024, + fast: true +} diff --git a/core/assets-raw/sprites/shapes/blank.png b/core/assets-raw/sprites/shapes/blank.png new file mode 100644 index 0000000000..6e438b2f30 Binary files /dev/null and b/core/assets-raw/sprites/shapes/blank.png differ diff --git a/core/assets-raw/sprites/shapes/circle.png b/core/assets-raw/sprites/shapes/circle.png new file mode 100644 index 0000000000..e2e6c0b2ec Binary files /dev/null and b/core/assets-raw/sprites/shapes/circle.png differ diff --git a/core/assets-raw/sprites/shapes/clear.png b/core/assets-raw/sprites/shapes/clear.png new file mode 100644 index 0000000000..dc8beb206c Binary files /dev/null and b/core/assets-raw/sprites/shapes/clear.png differ diff --git a/core/assets-raw/sprites/shapes/shape-3.png b/core/assets-raw/sprites/shapes/shape-3.png new file mode 100644 index 0000000000..5e98717513 Binary files /dev/null and b/core/assets-raw/sprites/shapes/shape-3.png differ diff --git a/core/assets-raw/sprites/shell.png b/core/assets-raw/sprites/shell.png deleted file mode 100644 index 747f735e65..0000000000 Binary files a/core/assets-raw/sprites/shell.png and /dev/null differ diff --git a/core/assets-raw/sprites/shot-long.png b/core/assets-raw/sprites/shot-long.png deleted file mode 100644 index 6d9ebaab45..0000000000 Binary files a/core/assets-raw/sprites/shot-long.png and /dev/null differ diff --git a/core/assets-raw/sprites/shot.png b/core/assets-raw/sprites/shot.png deleted file mode 100644 index 133c78b5f5..0000000000 Binary files a/core/assets-raw/sprites/shot.png and /dev/null differ diff --git a/core/assets-raw/sprites/titanshell.png b/core/assets-raw/sprites/titanshell.png deleted file mode 100644 index 7f90039999..0000000000 Binary files a/core/assets-raw/sprites/titanshell.png and /dev/null differ diff --git a/core/assets-raw/sprites/ui/bar-top.9.png b/core/assets-raw/sprites/ui/bar-top.9.png new file mode 100644 index 0000000000..e06822ecf0 Binary files /dev/null and b/core/assets-raw/sprites/ui/bar-top.9.png differ diff --git a/core/assets-raw/sprites/ui/bar.9.png b/core/assets-raw/sprites/ui/bar.9.png new file mode 100644 index 0000000000..5e9ab3f2a0 Binary files /dev/null and b/core/assets-raw/sprites/ui/bar.9.png differ diff --git a/core/assets-raw/sprites/ui/border.9.png b/core/assets-raw/sprites/ui/border.9.png deleted file mode 100644 index 7987b86920..0000000000 Binary files a/core/assets-raw/sprites/ui/border.9.png and /dev/null differ diff --git a/core/assets-raw/sprites/ui/button-disabled.9.png b/core/assets-raw/sprites/ui/button-disabled.9.png new file mode 100644 index 0000000000..b2e4d7eb25 Binary files /dev/null and b/core/assets-raw/sprites/ui/button-disabled.9.png differ diff --git a/core/assets-raw/sprites/ui/button-down.9.png b/core/assets-raw/sprites/ui/button-down.9.png index 771f74cee4..b26d54d0a1 100644 Binary files a/core/assets-raw/sprites/ui/button-down.9.png and b/core/assets-raw/sprites/ui/button-down.9.png differ diff --git a/core/assets-raw/sprites/ui/button-edge-1.9.png b/core/assets-raw/sprites/ui/button-edge-1.9.png new file mode 100644 index 0000000000..b91afee83a Binary files /dev/null and b/core/assets-raw/sprites/ui/button-edge-1.9.png differ diff --git a/core/assets-raw/sprites/ui/button-edge-2.9.png b/core/assets-raw/sprites/ui/button-edge-2.9.png new file mode 100644 index 0000000000..063dfa6c69 Binary files /dev/null and b/core/assets-raw/sprites/ui/button-edge-2.9.png differ diff --git a/core/assets-raw/sprites/ui/button-edge-3.9.png b/core/assets-raw/sprites/ui/button-edge-3.9.png new file mode 100644 index 0000000000..a182e728a6 Binary files /dev/null and b/core/assets-raw/sprites/ui/button-edge-3.9.png differ diff --git a/core/assets-raw/sprites/ui/button-edge-4.9.png b/core/assets-raw/sprites/ui/button-edge-4.9.png new file mode 100644 index 0000000000..c8e554c5e3 Binary files /dev/null and b/core/assets-raw/sprites/ui/button-edge-4.9.png differ diff --git a/core/assets-raw/sprites/ui/button-map-down.9.png b/core/assets-raw/sprites/ui/button-map-down.9.png deleted file mode 100644 index 00441b4c4c..0000000000 Binary files a/core/assets-raw/sprites/ui/button-map-down.9.png and /dev/null differ diff --git a/core/assets-raw/sprites/ui/button-map-over.9.png b/core/assets-raw/sprites/ui/button-map-over.9.png deleted file mode 100644 index afd0581f73..0000000000 Binary files a/core/assets-raw/sprites/ui/button-map-over.9.png and /dev/null differ diff --git a/core/assets-raw/sprites/ui/button-map.9.png b/core/assets-raw/sprites/ui/button-map.9.png deleted file mode 100644 index 06ab4f2546..0000000000 Binary files a/core/assets-raw/sprites/ui/button-map.9.png and /dev/null differ diff --git a/core/assets-raw/sprites/ui/button-over.9.png b/core/assets-raw/sprites/ui/button-over.9.png index 4f979ec719..fe5f260ce5 100644 Binary files a/core/assets-raw/sprites/ui/button-over.9.png and b/core/assets-raw/sprites/ui/button-over.9.png differ diff --git a/core/assets-raw/sprites/ui/button-right-down.9.png b/core/assets-raw/sprites/ui/button-right-down.9.png new file mode 100644 index 0000000000..efbabad92d Binary files /dev/null and b/core/assets-raw/sprites/ui/button-right-down.9.png differ diff --git a/core/assets-raw/sprites/ui/button-right-over.9.png b/core/assets-raw/sprites/ui/button-right-over.9.png new file mode 100644 index 0000000000..1f6d0fa234 Binary files /dev/null and b/core/assets-raw/sprites/ui/button-right-over.9.png differ diff --git a/core/assets-raw/sprites/ui/button-right.9.png b/core/assets-raw/sprites/ui/button-right.9.png new file mode 100644 index 0000000000..cd3d8d52e1 Binary files /dev/null and b/core/assets-raw/sprites/ui/button-right.9.png differ diff --git a/core/assets-raw/sprites/ui/button-select.9.png b/core/assets-raw/sprites/ui/button-select.9.png index 21fe14ac54..73c217fc2b 100644 Binary files a/core/assets-raw/sprites/ui/button-select.9.png and b/core/assets-raw/sprites/ui/button-select.9.png differ diff --git a/core/assets-raw/sprites/ui/button.9.png b/core/assets-raw/sprites/ui/button.9.png index f20f95b827..b88439e267 100644 Binary files a/core/assets-raw/sprites/ui/button.9.png and b/core/assets-raw/sprites/ui/button.9.png differ diff --git a/core/assets-raw/sprites/ui/check-disabled.png b/core/assets-raw/sprites/ui/check-disabled.png new file mode 100644 index 0000000000..68f22ed90e Binary files /dev/null and b/core/assets-raw/sprites/ui/check-disabled.png differ diff --git a/core/assets-raw/sprites/ui/check-off.png b/core/assets-raw/sprites/ui/check-off.png index d73a1a6e1f..79b86aabc1 100644 Binary files a/core/assets-raw/sprites/ui/check-off.png and b/core/assets-raw/sprites/ui/check-off.png differ diff --git a/core/assets-raw/sprites/ui/check-on-disabled.png b/core/assets-raw/sprites/ui/check-on-disabled.png new file mode 100644 index 0000000000..bd65bb214f Binary files /dev/null and b/core/assets-raw/sprites/ui/check-on-disabled.png differ diff --git a/core/assets-raw/sprites/ui/check-on-over.png b/core/assets-raw/sprites/ui/check-on-over.png index 710e173506..716e6fffb7 100644 Binary files a/core/assets-raw/sprites/ui/check-on-over.png and b/core/assets-raw/sprites/ui/check-on-over.png differ diff --git a/core/assets-raw/sprites/ui/check-on.png b/core/assets-raw/sprites/ui/check-on.png index a067b42e28..e4a8d11538 100644 Binary files a/core/assets-raw/sprites/ui/check-on.png and b/core/assets-raw/sprites/ui/check-on.png differ diff --git a/core/assets-raw/sprites/ui/check-over.png b/core/assets-raw/sprites/ui/check-over.png index 812f0c222e..5f618e5c86 100644 Binary files a/core/assets-raw/sprites/ui/check-over.png and b/core/assets-raw/sprites/ui/check-over.png differ diff --git a/core/assets-raw/sprites/ui/clear.png b/core/assets-raw/sprites/ui/clear.png index a38cc5d663..9e1edf5b13 100644 Binary files a/core/assets-raw/sprites/ui/clear.png and b/core/assets-raw/sprites/ui/clear.png differ diff --git a/core/assets-raw/sprites/ui/content-background-locked.9.png b/core/assets-raw/sprites/ui/content-background-locked.9.png new file mode 100644 index 0000000000..60e1941d4f Binary files /dev/null and b/core/assets-raw/sprites/ui/content-background-locked.9.png differ diff --git a/core/assets-raw/sprites/ui/content-background-noitems.9.png b/core/assets-raw/sprites/ui/content-background-noitems.9.png new file mode 100644 index 0000000000..f612ec98d4 Binary files /dev/null and b/core/assets-raw/sprites/ui/content-background-noitems.9.png differ diff --git a/core/assets-raw/sprites/ui/content-background-over.9.png b/core/assets-raw/sprites/ui/content-background-over.9.png new file mode 100644 index 0000000000..7e1a6e6c2d Binary files /dev/null and b/core/assets-raw/sprites/ui/content-background-over.9.png differ diff --git a/core/assets-raw/sprites/ui/content-background.9.png b/core/assets-raw/sprites/ui/content-background.9.png new file mode 100644 index 0000000000..cdd8df0abc Binary files /dev/null and b/core/assets-raw/sprites/ui/content-background.9.png differ diff --git a/core/assets-raw/sprites/ui/cursor.png b/core/assets-raw/sprites/ui/cursor.png index 67ff25cb9a..83efefe2b3 100644 Binary files a/core/assets-raw/sprites/ui/cursor.png and b/core/assets-raw/sprites/ui/cursor.png differ diff --git a/core/assets-raw/sprites/ui/discord-banner-over.png b/core/assets-raw/sprites/ui/discord-banner-over.png deleted file mode 100644 index 87a8ab0504..0000000000 Binary files a/core/assets-raw/sprites/ui/discord-banner-over.png and /dev/null differ diff --git a/core/assets-raw/sprites/ui/discord-banner.png b/core/assets-raw/sprites/ui/discord-banner.png index 39248244a8..ac4d3b1ec9 100644 Binary files a/core/assets-raw/sprites/ui/discord-banner.png and b/core/assets-raw/sprites/ui/discord-banner.png differ diff --git a/core/assets-raw/sprites/ui/empty-sector.png b/core/assets-raw/sprites/ui/empty-sector.png new file mode 100644 index 0000000000..a0bad794cd Binary files /dev/null and b/core/assets-raw/sprites/ui/empty-sector.png differ diff --git a/core/assets-raw/sprites/ui/icons/controller-cursor.png b/core/assets-raw/sprites/ui/icons/controller-cursor.png deleted file mode 100644 index 0fed7f4245..0000000000 Binary files a/core/assets-raw/sprites/ui/icons/controller-cursor.png and /dev/null differ diff --git a/core/assets-raw/sprites/ui/icons/icon-about.png b/core/assets-raw/sprites/ui/icons/icon-about.png index 1b03a29a2c..3943975b97 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-about.png and b/core/assets-raw/sprites/ui/icons/icon-about.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-add.png b/core/assets-raw/sprites/ui/icons/icon-add.png index eb325782ec..328c8ca469 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-add.png and b/core/assets-raw/sprites/ui/icons/icon-add.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-admin-small.png b/core/assets-raw/sprites/ui/icons/icon-admin-small.png index c5cf9ef819..6a2afe9e24 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-admin-small.png and b/core/assets-raw/sprites/ui/icons/icon-admin-small.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-admin.png b/core/assets-raw/sprites/ui/icons/icon-admin.png index 3d3811462a..f4101c0423 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-admin.png and b/core/assets-raw/sprites/ui/icons/icon-admin.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-areaDelete.png b/core/assets-raw/sprites/ui/icons/icon-areaDelete.png deleted file mode 100644 index 97b95ea94f..0000000000 Binary files a/core/assets-raw/sprites/ui/icons/icon-areaDelete.png and /dev/null differ diff --git a/core/assets-raw/sprites/ui/icons/icon-arrow-16.png b/core/assets-raw/sprites/ui/icons/icon-arrow-16.png new file mode 100644 index 0000000000..954b1995fd Binary files /dev/null and b/core/assets-raw/sprites/ui/icons/icon-arrow-16.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-arrow-down.png b/core/assets-raw/sprites/ui/icons/icon-arrow-down.png index b6403714ec..bf9e1ff860 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-arrow-down.png and b/core/assets-raw/sprites/ui/icons/icon-arrow-down.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-arrow-left.png b/core/assets-raw/sprites/ui/icons/icon-arrow-left.png index ceac5c958a..15678f32b1 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-arrow-left.png and b/core/assets-raw/sprites/ui/icons/icon-arrow-left.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-arrow-right.png b/core/assets-raw/sprites/ui/icons/icon-arrow-right.png index 23fd286809..b09556172d 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-arrow-right.png and b/core/assets-raw/sprites/ui/icons/icon-arrow-right.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-arrow-up.png b/core/assets-raw/sprites/ui/icons/icon-arrow-up.png index ebf5bab7c6..8a7546126d 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-arrow-up.png and b/core/assets-raw/sprites/ui/icons/icon-arrow-up.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-arrow.png b/core/assets-raw/sprites/ui/icons/icon-arrow.png index d8f82bf42e..954b1995fd 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-arrow.png and b/core/assets-raw/sprites/ui/icons/icon-arrow.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-back.png b/core/assets-raw/sprites/ui/icons/icon-back.png index be3d74fd80..ef470961d6 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-back.png and b/core/assets-raw/sprites/ui/icons/icon-back.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-ban.png b/core/assets-raw/sprites/ui/icons/icon-ban.png index f8df32c20c..1ee4a5c3bf 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-ban.png and b/core/assets-raw/sprites/ui/icons/icon-ban.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-break.png b/core/assets-raw/sprites/ui/icons/icon-break.png new file mode 100644 index 0000000000..c4b079735c Binary files /dev/null and b/core/assets-raw/sprites/ui/icons/icon-break.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-cancel.png b/core/assets-raw/sprites/ui/icons/icon-cancel.png index bf4c7ed9a0..03ea12174f 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-cancel.png and b/core/assets-raw/sprites/ui/icons/icon-cancel.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-chat.png b/core/assets-raw/sprites/ui/icons/icon-chat.png index b7c02421aa..85f52fa41c 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-chat.png and b/core/assets-raw/sprites/ui/icons/icon-chat.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-check.png b/core/assets-raw/sprites/ui/icons/icon-check.png index 67bd7b09cd..660cb75bf3 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-check.png and b/core/assets-raw/sprites/ui/icons/icon-check.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-close-down.png b/core/assets-raw/sprites/ui/icons/icon-close-down.png deleted file mode 100644 index 204c2eb911..0000000000 Binary files a/core/assets-raw/sprites/ui/icons/icon-close-down.png and /dev/null differ diff --git a/core/assets-raw/sprites/ui/icons/icon-close-over.png b/core/assets-raw/sprites/ui/icons/icon-close-over.png deleted file mode 100644 index e526739fb6..0000000000 Binary files a/core/assets-raw/sprites/ui/icons/icon-close-over.png and /dev/null differ diff --git a/core/assets-raw/sprites/ui/icons/icon-close.png b/core/assets-raw/sprites/ui/icons/icon-close.png deleted file mode 100644 index 02b26e0a25..0000000000 Binary files a/core/assets-raw/sprites/ui/icons/icon-close.png and /dev/null differ diff --git a/core/assets-raw/sprites/ui/icons/icon-copy.png b/core/assets-raw/sprites/ui/icons/icon-copy.png new file mode 100644 index 0000000000..0f03ee5d04 Binary files /dev/null and b/core/assets-raw/sprites/ui/icons/icon-copy.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-crafting.png b/core/assets-raw/sprites/ui/icons/icon-crafting.png index 0e156c60c9..9a5fd055e6 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-crafting.png and b/core/assets-raw/sprites/ui/icons/icon-crafting.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-cursor.png b/core/assets-raw/sprites/ui/icons/icon-cursor.png index 7f8d2c80d8..1025a08a9e 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-cursor.png and b/core/assets-raw/sprites/ui/icons/icon-cursor.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-database.png b/core/assets-raw/sprites/ui/icons/icon-database.png new file mode 100644 index 0000000000..059fd6f219 Binary files /dev/null and b/core/assets-raw/sprites/ui/icons/icon-database.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-defense.png b/core/assets-raw/sprites/ui/icons/icon-defense.png index f807b78001..bb336dac7c 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-defense.png and b/core/assets-raw/sprites/ui/icons/icon-defense.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-dev-builds.png b/core/assets-raw/sprites/ui/icons/icon-dev-builds.png index f845d619d4..27f46b6624 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-dev-builds.png and b/core/assets-raw/sprites/ui/icons/icon-dev-builds.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-diagonal.png b/core/assets-raw/sprites/ui/icons/icon-diagonal.png new file mode 100644 index 0000000000..262be1cebc Binary files /dev/null and b/core/assets-raw/sprites/ui/icons/icon-diagonal.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-discord.png b/core/assets-raw/sprites/ui/icons/icon-discord.png index 9451cfa1a2..76bc4c8c43 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-discord.png and b/core/assets-raw/sprites/ui/icons/icon-discord.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-distribution.png b/core/assets-raw/sprites/ui/icons/icon-distribution.png index 0c1ea5112e..066b95ade8 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-distribution.png and b/core/assets-raw/sprites/ui/icons/icon-distribution.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-donate.png b/core/assets-raw/sprites/ui/icons/icon-donate.png index ea5c95a660..cad39eb1ba 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-donate.png and b/core/assets-raw/sprites/ui/icons/icon-donate.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-dots.png b/core/assets-raw/sprites/ui/icons/icon-dots.png index 8b597e0dd4..a8d656d6cc 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-dots.png and b/core/assets-raw/sprites/ui/icons/icon-dots.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-editor.png b/core/assets-raw/sprites/ui/icons/icon-editor.png index 4e25b087a0..6cb0a64f9f 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-editor.png and b/core/assets-raw/sprites/ui/icons/icon-editor.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-effect.png b/core/assets-raw/sprites/ui/icons/icon-effect.png new file mode 100644 index 0000000000..3099afb586 Binary files /dev/null and b/core/assets-raw/sprites/ui/icons/icon-effect.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-elevation.png b/core/assets-raw/sprites/ui/icons/icon-elevation.png new file mode 100644 index 0000000000..cbc30c8cc1 Binary files /dev/null and b/core/assets-raw/sprites/ui/icons/icon-elevation.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-eraser.png b/core/assets-raw/sprites/ui/icons/icon-eraser.png new file mode 100644 index 0000000000..fc72acc9f2 Binary files /dev/null and b/core/assets-raw/sprites/ui/icons/icon-eraser.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-exit.png b/core/assets-raw/sprites/ui/icons/icon-exit.png index 13d45d242f..fe1de5d9e4 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-exit.png and b/core/assets-raw/sprites/ui/icons/icon-exit.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-file-image.png b/core/assets-raw/sprites/ui/icons/icon-file-image.png new file mode 100644 index 0000000000..254ea95f3d Binary files /dev/null and b/core/assets-raw/sprites/ui/icons/icon-file-image.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-file-text.png b/core/assets-raw/sprites/ui/icons/icon-file-text.png old mode 100755 new mode 100644 index 3aea932bbb..41b4fbc342 Binary files a/core/assets-raw/sprites/ui/icons/icon-file-text.png and b/core/assets-raw/sprites/ui/icons/icon-file-text.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-file.png b/core/assets-raw/sprites/ui/icons/icon-file.png new file mode 100644 index 0000000000..0f41e17cbf Binary files /dev/null and b/core/assets-raw/sprites/ui/icons/icon-file.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-fill.png b/core/assets-raw/sprites/ui/icons/icon-fill.png old mode 100755 new mode 100644 index 027b395162..79bc8b2c39 Binary files a/core/assets-raw/sprites/ui/icons/icon-fill.png and b/core/assets-raw/sprites/ui/icons/icon-fill.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-floppy-16.png b/core/assets-raw/sprites/ui/icons/icon-floppy-16.png new file mode 100644 index 0000000000..6fd3f90850 Binary files /dev/null and b/core/assets-raw/sprites/ui/icons/icon-floppy-16.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-floppy.png b/core/assets-raw/sprites/ui/icons/icon-floppy.png index 8455ee3b44..e8ba657ab7 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-floppy.png and b/core/assets-raw/sprites/ui/icons/icon-floppy.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-folder-parent.png b/core/assets-raw/sprites/ui/icons/icon-folder-parent.png old mode 100755 new mode 100644 index 82b70ca831..160f2be902 Binary files a/core/assets-raw/sprites/ui/icons/icon-folder-parent.png and b/core/assets-raw/sprites/ui/icons/icon-folder-parent.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-folder.png b/core/assets-raw/sprites/ui/icons/icon-folder.png old mode 100755 new mode 100644 index 6d9471196b..c55313ef32 Binary files a/core/assets-raw/sprites/ui/icons/icon-folder.png and b/core/assets-raw/sprites/ui/icons/icon-folder.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-github.png b/core/assets-raw/sprites/ui/icons/icon-github.png index 4eebcb7ec4..0de41be50d 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-github.png and b/core/assets-raw/sprites/ui/icons/icon-github.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-google-play.png b/core/assets-raw/sprites/ui/icons/icon-google-play.png index 944fd50253..8464c51159 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-google-play.png and b/core/assets-raw/sprites/ui/icons/icon-google-play.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-grid.png b/core/assets-raw/sprites/ui/icons/icon-grid.png old mode 100755 new mode 100644 index 6e6506ec39..5427571a16 Binary files a/core/assets-raw/sprites/ui/icons/icon-grid.png and b/core/assets-raw/sprites/ui/icons/icon-grid.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-hold.png b/core/assets-raw/sprites/ui/icons/icon-hold.png deleted file mode 100644 index 43e6c78ba1..0000000000 Binary files a/core/assets-raw/sprites/ui/icons/icon-hold.png and /dev/null differ diff --git a/core/assets-raw/sprites/ui/icons/icon-holdDelete.png b/core/assets-raw/sprites/ui/icons/icon-holdDelete.png deleted file mode 100644 index ea2f55b269..0000000000 Binary files a/core/assets-raw/sprites/ui/icons/icon-holdDelete.png and /dev/null differ diff --git a/core/assets-raw/sprites/ui/icons/icon-home.png b/core/assets-raw/sprites/ui/icons/icon-home.png old mode 100755 new mode 100644 index 087443880f..b97dba2a11 Binary files a/core/assets-raw/sprites/ui/icons/icon-home.png and b/core/assets-raw/sprites/ui/icons/icon-home.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-host.png b/core/assets-raw/sprites/ui/icons/icon-host.png index 82d5d2d153..6253c986bd 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-host.png and b/core/assets-raw/sprites/ui/icons/icon-host.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-info.png b/core/assets-raw/sprites/ui/icons/icon-info.png index 759a89dbdb..2ea8dcb014 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-info.png and b/core/assets-raw/sprites/ui/icons/icon-info.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-itch.io.png b/core/assets-raw/sprites/ui/icons/icon-itch.io.png index 0bf3a6e71c..b5bca0e50a 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-itch.io.png and b/core/assets-raw/sprites/ui/icons/icon-itch.io.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-item.png b/core/assets-raw/sprites/ui/icons/icon-item.png new file mode 100644 index 0000000000..9dd6da2249 Binary files /dev/null and b/core/assets-raw/sprites/ui/icons/icon-item.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-line.png b/core/assets-raw/sprites/ui/icons/icon-line.png index faeb0de7ea..5ce2cfdc78 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-line.png and b/core/assets-raw/sprites/ui/icons/icon-line.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-link.png b/core/assets-raw/sprites/ui/icons/icon-link.png index 3a0e25bf29..71446afcc0 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-link.png and b/core/assets-raw/sprites/ui/icons/icon-link.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-liquid-consume.png b/core/assets-raw/sprites/ui/icons/icon-liquid-consume.png new file mode 100644 index 0000000000..48c93ed9d2 Binary files /dev/null and b/core/assets-raw/sprites/ui/icons/icon-liquid-consume.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-liquid.png b/core/assets-raw/sprites/ui/icons/icon-liquid.png new file mode 100644 index 0000000000..050ea2ef38 Binary files /dev/null and b/core/assets-raw/sprites/ui/icons/icon-liquid.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-load-image.png b/core/assets-raw/sprites/ui/icons/icon-load-image.png index f20d4a01ed..88a162b628 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-load-image.png and b/core/assets-raw/sprites/ui/icons/icon-load-image.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-load-map.png b/core/assets-raw/sprites/ui/icons/icon-load-map.png index 336b5efe20..4aa244ab7c 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-load-map.png and b/core/assets-raw/sprites/ui/icons/icon-load-map.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-load.png b/core/assets-raw/sprites/ui/icons/icon-load.png index 00e35485aa..ecbd0c08cf 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-load.png and b/core/assets-raw/sprites/ui/icons/icon-load.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-loading.png b/core/assets-raw/sprites/ui/icons/icon-loading.png index d53ace4c51..0fd2d5265d 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-loading.png and b/core/assets-raw/sprites/ui/icons/icon-loading.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-locked.png b/core/assets-raw/sprites/ui/icons/icon-locked.png new file mode 100644 index 0000000000..cdf11fe658 Binary files /dev/null and b/core/assets-raw/sprites/ui/icons/icon-locked.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-map.png b/core/assets-raw/sprites/ui/icons/icon-map.png new file mode 100644 index 0000000000..54f6a56169 Binary files /dev/null and b/core/assets-raw/sprites/ui/icons/icon-map.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-menu-large.png b/core/assets-raw/sprites/ui/icons/icon-menu-large.png new file mode 100644 index 0000000000..36175b9942 Binary files /dev/null and b/core/assets-raw/sprites/ui/icons/icon-menu-large.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-menu.png b/core/assets-raw/sprites/ui/icons/icon-menu.png index e244e9f819..0469f99721 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-menu.png and b/core/assets-raw/sprites/ui/icons/icon-menu.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-missing.png b/core/assets-raw/sprites/ui/icons/icon-missing.png new file mode 100644 index 0000000000..6a9c5d9012 Binary files /dev/null and b/core/assets-raw/sprites/ui/icons/icon-missing.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-none.png b/core/assets-raw/sprites/ui/icons/icon-none.png index 427d8006d9..fcf22bc9da 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-none.png and b/core/assets-raw/sprites/ui/icons/icon-none.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-paste.png b/core/assets-raw/sprites/ui/icons/icon-paste.png new file mode 100644 index 0000000000..606165ed34 Binary files /dev/null and b/core/assets-raw/sprites/ui/icons/icon-paste.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-pause.png b/core/assets-raw/sprites/ui/icons/icon-pause.png index a87060e0b3..7471836eaa 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-pause.png and b/core/assets-raw/sprites/ui/icons/icon-pause.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-pencil-small.png b/core/assets-raw/sprites/ui/icons/icon-pencil-small.png deleted file mode 100644 index fad56e9439..0000000000 Binary files a/core/assets-raw/sprites/ui/icons/icon-pencil-small.png and /dev/null differ diff --git a/core/assets-raw/sprites/ui/icons/icon-pencil.png b/core/assets-raw/sprites/ui/icons/icon-pencil.png old mode 100755 new mode 100644 index da2f01f479..053a283d08 Binary files a/core/assets-raw/sprites/ui/icons/icon-pencil.png and b/core/assets-raw/sprites/ui/icons/icon-pencil.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-pick.png b/core/assets-raw/sprites/ui/icons/icon-pick.png old mode 100755 new mode 100644 index 05090f51a5..dfd7743030 Binary files a/core/assets-raw/sprites/ui/icons/icon-pick.png and b/core/assets-raw/sprites/ui/icons/icon-pick.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-play-2.png b/core/assets-raw/sprites/ui/icons/icon-play-2.png index 8a4295d1f9..c001e60b04 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-play-2.png and b/core/assets-raw/sprites/ui/icons/icon-play-2.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-play-custom.png b/core/assets-raw/sprites/ui/icons/icon-play-custom.png new file mode 100644 index 0000000000..78716a2fb0 Binary files /dev/null and b/core/assets-raw/sprites/ui/icons/icon-play-custom.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-play.png b/core/assets-raw/sprites/ui/icons/icon-play.png index 7d0cb2d206..8530e6b09d 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-play.png and b/core/assets-raw/sprites/ui/icons/icon-play.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-players.png b/core/assets-raw/sprites/ui/icons/icon-players.png index a4493dbd0b..be4746291f 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-players.png and b/core/assets-raw/sprites/ui/icons/icon-players.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-power.png b/core/assets-raw/sprites/ui/icons/icon-power.png index bc8282d39a..64cda180db 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-power.png and b/core/assets-raw/sprites/ui/icons/icon-power.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-production.png b/core/assets-raw/sprites/ui/icons/icon-production.png index 2191bedfac..8d037a43a2 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-production.png and b/core/assets-raw/sprites/ui/icons/icon-production.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-quit.png b/core/assets-raw/sprites/ui/icons/icon-quit.png index dc4f747a45..26a9634945 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-quit.png and b/core/assets-raw/sprites/ui/icons/icon-quit.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-redo.png b/core/assets-raw/sprites/ui/icons/icon-redo.png old mode 100755 new mode 100644 index 0a0291301a..4c53eb56ef Binary files a/core/assets-raw/sprites/ui/icons/icon-redo.png and b/core/assets-raw/sprites/ui/icons/icon-redo.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-refresh.png b/core/assets-raw/sprites/ui/icons/icon-refresh.png index 2b3c7b31f1..cd1050f017 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-refresh.png and b/core/assets-raw/sprites/ui/icons/icon-refresh.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-rename.png b/core/assets-raw/sprites/ui/icons/icon-rename.png index 73f3fb8391..4a6731a31b 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-rename.png and b/core/assets-raw/sprites/ui/icons/icon-rename.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-resize.png b/core/assets-raw/sprites/ui/icons/icon-resize.png index 00d11bee80..ee53c8470f 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-resize.png and b/core/assets-raw/sprites/ui/icons/icon-resize.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-rotate-arrow.png b/core/assets-raw/sprites/ui/icons/icon-rotate-arrow.png index c2ea8f2703..22039f3020 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-rotate-arrow.png and b/core/assets-raw/sprites/ui/icons/icon-rotate-arrow.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-rotate-left.png b/core/assets-raw/sprites/ui/icons/icon-rotate-left.png index 22b3b3c582..9b05efec5f 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-rotate-left.png and b/core/assets-raw/sprites/ui/icons/icon-rotate-left.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-rotate-right.png b/core/assets-raw/sprites/ui/icons/icon-rotate-right.png index d163dfcb5c..2009c9394d 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-rotate-right.png and b/core/assets-raw/sprites/ui/icons/icon-rotate-right.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-rotate.png b/core/assets-raw/sprites/ui/icons/icon-rotate.png index 8426b9ef47..3c989a555f 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-rotate.png and b/core/assets-raw/sprites/ui/icons/icon-rotate.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-save-image.png b/core/assets-raw/sprites/ui/icons/icon-save-image.png index 7971c82f7d..5234c4f2f4 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-save-image.png and b/core/assets-raw/sprites/ui/icons/icon-save-image.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-save-map.png b/core/assets-raw/sprites/ui/icons/icon-save-map.png index 781664e578..9066752d13 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-save-map.png and b/core/assets-raw/sprites/ui/icons/icon-save-map.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-save.png b/core/assets-raw/sprites/ui/icons/icon-save.png index f05dfa89d2..1ed3fc4549 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-save.png and b/core/assets-raw/sprites/ui/icons/icon-save.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-settings.png b/core/assets-raw/sprites/ui/icons/icon-settings.png index 8c0cfc49f6..51a969c401 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-settings.png and b/core/assets-raw/sprites/ui/icons/icon-settings.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-spray.png b/core/assets-raw/sprites/ui/icons/icon-spray.png new file mode 100644 index 0000000000..17198d657b Binary files /dev/null and b/core/assets-raw/sprites/ui/icons/icon-spray.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-terrain.png b/core/assets-raw/sprites/ui/icons/icon-terrain.png index da5f25366d..cc9c57380e 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-terrain.png and b/core/assets-raw/sprites/ui/icons/icon-terrain.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-tools.png b/core/assets-raw/sprites/ui/icons/icon-tools.png index 55f8b11575..2ece1ae8f4 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-tools.png and b/core/assets-raw/sprites/ui/icons/icon-tools.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-touch.png b/core/assets-raw/sprites/ui/icons/icon-touch.png deleted file mode 100644 index 4e96232fab..0000000000 Binary files a/core/assets-raw/sprites/ui/icons/icon-touch.png and /dev/null differ diff --git a/core/assets-raw/sprites/ui/icons/icon-touchDelete.png b/core/assets-raw/sprites/ui/icons/icon-touchDelete.png deleted file mode 100644 index 29b2cd89ea..0000000000 Binary files a/core/assets-raw/sprites/ui/icons/icon-touchDelete.png and /dev/null differ diff --git a/core/assets-raw/sprites/ui/icons/icon-trash-16.png b/core/assets-raw/sprites/ui/icons/icon-trash-16.png index 07d2de70a1..a1de49233b 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-trash-16.png and b/core/assets-raw/sprites/ui/icons/icon-trash-16.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-trash.png b/core/assets-raw/sprites/ui/icons/icon-trash.png index 0f1c0ab56b..e0f419f2f3 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-trash.png and b/core/assets-raw/sprites/ui/icons/icon-trash.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-tree.png b/core/assets-raw/sprites/ui/icons/icon-tree.png new file mode 100644 index 0000000000..0731f6a7e5 Binary files /dev/null and b/core/assets-raw/sprites/ui/icons/icon-tree.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-trello.png b/core/assets-raw/sprites/ui/icons/icon-trello.png index c1184271b9..8e63ab3208 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-trello.png and b/core/assets-raw/sprites/ui/icons/icon-trello.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-turret.png b/core/assets-raw/sprites/ui/icons/icon-turret.png new file mode 100644 index 0000000000..4f49eec29f Binary files /dev/null and b/core/assets-raw/sprites/ui/icons/icon-turret.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-tutorial.png b/core/assets-raw/sprites/ui/icons/icon-tutorial.png index 77f494c3e6..40ba90a5e4 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-tutorial.png and b/core/assets-raw/sprites/ui/icons/icon-tutorial.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-undo.png b/core/assets-raw/sprites/ui/icons/icon-undo.png old mode 100755 new mode 100644 index b56d258984..cf5dd4c154 Binary files a/core/assets-raw/sprites/ui/icons/icon-undo.png and b/core/assets-raw/sprites/ui/icons/icon-undo.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-units.png b/core/assets-raw/sprites/ui/icons/icon-units.png new file mode 100644 index 0000000000..8af1689c6f Binary files /dev/null and b/core/assets-raw/sprites/ui/icons/icon-units.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-unlocks.png b/core/assets-raw/sprites/ui/icons/icon-unlocks.png new file mode 100644 index 0000000000..36b637b0b9 Binary files /dev/null and b/core/assets-raw/sprites/ui/icons/icon-unlocks.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-upgrade.png b/core/assets-raw/sprites/ui/icons/icon-upgrade.png new file mode 100644 index 0000000000..af4b3f4576 Binary files /dev/null and b/core/assets-raw/sprites/ui/icons/icon-upgrade.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-weapon.png b/core/assets-raw/sprites/ui/icons/icon-weapon.png deleted file mode 100644 index 003074bc56..0000000000 Binary files a/core/assets-raw/sprites/ui/icons/icon-weapon.png and /dev/null differ diff --git a/core/assets-raw/sprites/ui/icons/icon-wiki.png b/core/assets-raw/sprites/ui/icons/icon-wiki.png index 6bd8d29014..a4395e4ed5 100644 Binary files a/core/assets-raw/sprites/ui/icons/icon-wiki.png and b/core/assets-raw/sprites/ui/icons/icon-wiki.png differ diff --git a/core/assets-raw/sprites/ui/icons/icon-zoom-small.png b/core/assets-raw/sprites/ui/icons/icon-zoom-small.png deleted file mode 100644 index 35c117cda3..0000000000 Binary files a/core/assets-raw/sprites/ui/icons/icon-zoom-small.png and /dev/null differ diff --git a/core/assets-raw/sprites/ui/icons/icon-zoom.png b/core/assets-raw/sprites/ui/icons/icon-zoom.png old mode 100755 new mode 100644 index 7a882ceb06..02736c1a4b Binary files a/core/assets-raw/sprites/ui/icons/icon-zoom.png and b/core/assets-raw/sprites/ui/icons/icon-zoom.png differ diff --git a/core/assets-raw/sprites/ui/info-banner.png b/core/assets-raw/sprites/ui/info-banner.png new file mode 100644 index 0000000000..3568944de5 Binary files /dev/null and b/core/assets-raw/sprites/ui/info-banner.png differ diff --git a/core/assets-raw/sprites/ui/inventory.9.png b/core/assets-raw/sprites/ui/inventory.9.png new file mode 100644 index 0000000000..53a5632587 Binary files /dev/null and b/core/assets-raw/sprites/ui/inventory.9.png differ diff --git a/core/assets-raw/sprites/ui/logotext.png b/core/assets-raw/sprites/ui/logotext.png index bc872e39bb..64976a6c47 100644 Binary files a/core/assets-raw/sprites/ui/logotext.png and b/core/assets-raw/sprites/ui/logotext.png differ diff --git a/core/assets-raw/sprites/ui/pane-2.9.png b/core/assets-raw/sprites/ui/pane-2.9.png new file mode 100644 index 0000000000..e2fe497574 Binary files /dev/null and b/core/assets-raw/sprites/ui/pane-2.9.png differ diff --git a/core/assets-raw/sprites/ui/pane-button.9.png b/core/assets-raw/sprites/ui/pane-button.9.png deleted file mode 100644 index c870b0de34..0000000000 Binary files a/core/assets-raw/sprites/ui/pane-button.9.png and /dev/null differ diff --git a/core/assets-raw/sprites/ui/pane.9.png b/core/assets-raw/sprites/ui/pane.9.png index 0d4dcbfff4..a21c4607aa 100644 Binary files a/core/assets-raw/sprites/ui/pane.9.png and b/core/assets-raw/sprites/ui/pane.9.png differ diff --git a/core/assets-raw/sprites/ui/scroll-horizontal.9.png b/core/assets-raw/sprites/ui/scroll-horizontal.9.png index 71469bb2e8..72c4cda25f 100644 Binary files a/core/assets-raw/sprites/ui/scroll-horizontal.9.png and b/core/assets-raw/sprites/ui/scroll-horizontal.9.png differ diff --git a/core/assets-raw/sprites/ui/scroll-knob-horizontal-black.9.png b/core/assets-raw/sprites/ui/scroll-knob-horizontal-black.9.png new file mode 100644 index 0000000000..7f5dd4137f Binary files /dev/null and b/core/assets-raw/sprites/ui/scroll-knob-horizontal-black.9.png differ diff --git a/core/assets-raw/sprites/ui/scroll-knob-horizontal.9.png b/core/assets-raw/sprites/ui/scroll-knob-horizontal.9.png deleted file mode 100644 index 699845f70e..0000000000 Binary files a/core/assets-raw/sprites/ui/scroll-knob-horizontal.9.png and /dev/null differ diff --git a/core/assets-raw/sprites/ui/scroll-knob-vertical-black.9.png b/core/assets-raw/sprites/ui/scroll-knob-vertical-black.9.png index 80ace6e0c2..1b5c9d39d8 100644 Binary files a/core/assets-raw/sprites/ui/scroll-knob-vertical-black.9.png and b/core/assets-raw/sprites/ui/scroll-knob-vertical-black.9.png differ diff --git a/core/assets-raw/sprites/ui/scroll-knob-vertical.9.png b/core/assets-raw/sprites/ui/scroll-knob-vertical.9.png deleted file mode 100644 index 7e8f69bce7..0000000000 Binary files a/core/assets-raw/sprites/ui/scroll-knob-vertical.9.png and /dev/null differ diff --git a/core/assets-raw/sprites/ui/scroll.9.png b/core/assets-raw/sprites/ui/scroll.9.png index e83e20b63b..4c92865fcb 100644 Binary files a/core/assets-raw/sprites/ui/scroll.9.png and b/core/assets-raw/sprites/ui/scroll.9.png differ diff --git a/core/assets-raw/sprites/ui/sector-select.png b/core/assets-raw/sprites/ui/sector-select.png new file mode 100644 index 0000000000..79987b507f Binary files /dev/null and b/core/assets-raw/sprites/ui/sector-select.png differ diff --git a/core/assets-raw/sprites/ui/selection.png b/core/assets-raw/sprites/ui/selection.png index a8ed5d2767..b75e1ce6d2 100644 Binary files a/core/assets-raw/sprites/ui/selection.png and b/core/assets-raw/sprites/ui/selection.png differ diff --git a/core/assets-raw/sprites/ui/slider-knob-down.png b/core/assets-raw/sprites/ui/slider-knob-down.png index 369c253da6..2822be93af 100644 Binary files a/core/assets-raw/sprites/ui/slider-knob-down.png and b/core/assets-raw/sprites/ui/slider-knob-down.png differ diff --git a/core/assets-raw/sprites/ui/slider-knob-over.png b/core/assets-raw/sprites/ui/slider-knob-over.png index dce3813656..2822be93af 100644 Binary files a/core/assets-raw/sprites/ui/slider-knob-over.png and b/core/assets-raw/sprites/ui/slider-knob-over.png differ diff --git a/core/assets-raw/sprites/ui/slider-knob.png b/core/assets-raw/sprites/ui/slider-knob.png index 9e7bd21cc6..4d98387c78 100644 Binary files a/core/assets-raw/sprites/ui/slider-knob.png and b/core/assets-raw/sprites/ui/slider-knob.png differ diff --git a/core/assets-raw/sprites/ui/slider-vertical.png b/core/assets-raw/sprites/ui/slider-vertical.png index 5ec9736ba6..91d7c16e1f 100644 Binary files a/core/assets-raw/sprites/ui/slider-vertical.png and b/core/assets-raw/sprites/ui/slider-vertical.png differ diff --git a/core/assets-raw/sprites/ui/slider.png b/core/assets-raw/sprites/ui/slider.png index a722706e2b..278674e9a0 100644 Binary files a/core/assets-raw/sprites/ui/slider.png and b/core/assets-raw/sprites/ui/slider.png differ diff --git a/core/assets-raw/sprites/ui/text-sides-down.png b/core/assets-raw/sprites/ui/text-sides-down.png deleted file mode 100644 index 151c269d38..0000000000 Binary files a/core/assets-raw/sprites/ui/text-sides-down.png and /dev/null differ diff --git a/core/assets-raw/sprites/ui/text-sides-over.png b/core/assets-raw/sprites/ui/text-sides-over.png deleted file mode 100644 index 445ec86471..0000000000 Binary files a/core/assets-raw/sprites/ui/text-sides-over.png and /dev/null differ diff --git a/core/assets-raw/sprites/ui/text-sides.png b/core/assets-raw/sprites/ui/text-sides.png deleted file mode 100644 index 93993100bb..0000000000 Binary files a/core/assets-raw/sprites/ui/text-sides.png and /dev/null differ diff --git a/core/assets-raw/sprites/ui/textfield-over.9.png b/core/assets-raw/sprites/ui/textfield-over.9.png deleted file mode 100644 index 3fe1f1ec4f..0000000000 Binary files a/core/assets-raw/sprites/ui/textfield-over.9.png and /dev/null differ diff --git a/core/assets-raw/sprites/ui/textfield.9.png b/core/assets-raw/sprites/ui/textfield.9.png deleted file mode 100644 index fda1b6679a..0000000000 Binary files a/core/assets-raw/sprites/ui/textfield.9.png and /dev/null differ diff --git a/core/assets-raw/sprites/ui/underline-2.9.png b/core/assets-raw/sprites/ui/underline-2.9.png new file mode 100644 index 0000000000..f703111db2 Binary files /dev/null and b/core/assets-raw/sprites/ui/underline-2.9.png differ diff --git a/core/assets-raw/sprites/ui/underline-disabled.9.png b/core/assets-raw/sprites/ui/underline-disabled.9.png new file mode 100644 index 0000000000..753a3e9c24 Binary files /dev/null and b/core/assets-raw/sprites/ui/underline-disabled.9.png differ diff --git a/core/assets-raw/sprites/ui/underline-red.9.png b/core/assets-raw/sprites/ui/underline-red.9.png new file mode 100644 index 0000000000..5a6bc60651 Binary files /dev/null and b/core/assets-raw/sprites/ui/underline-red.9.png differ diff --git a/core/assets-raw/sprites/ui/underline.9.png b/core/assets-raw/sprites/ui/underline.9.png new file mode 100644 index 0000000000..e42d29c78f Binary files /dev/null and b/core/assets-raw/sprites/ui/underline.9.png differ diff --git a/core/assets-raw/sprites/ui/window-empty.9.png b/core/assets-raw/sprites/ui/window-empty.9.png index a5bae440b4..cbc2915f60 100644 Binary files a/core/assets-raw/sprites/ui/window-empty.9.png and b/core/assets-raw/sprites/ui/window-empty.9.png differ diff --git a/core/assets-raw/sprites/ui/window.9.png b/core/assets-raw/sprites/ui/window.9.png deleted file mode 100644 index 4efe2c7353..0000000000 Binary files a/core/assets-raw/sprites/ui/window.9.png and /dev/null differ diff --git a/core/assets-raw/sprites/units/chaos-array-base.png b/core/assets-raw/sprites/units/chaos-array-base.png new file mode 100644 index 0000000000..eaff7f46ad Binary files /dev/null and b/core/assets-raw/sprites/units/chaos-array-base.png differ diff --git a/core/assets-raw/sprites/units/chaos-array-leg.png b/core/assets-raw/sprites/units/chaos-array-leg.png new file mode 100644 index 0000000000..62ba2d8b78 Binary files /dev/null and b/core/assets-raw/sprites/units/chaos-array-leg.png differ diff --git a/core/assets-raw/sprites/units/chaos-array.png b/core/assets-raw/sprites/units/chaos-array.png new file mode 100644 index 0000000000..5500e743df Binary files /dev/null and b/core/assets-raw/sprites/units/chaos-array.png differ diff --git a/core/assets-raw/sprites/units/crawler-base.png b/core/assets-raw/sprites/units/crawler-base.png new file mode 100644 index 0000000000..509f802a94 Binary files /dev/null and b/core/assets-raw/sprites/units/crawler-base.png differ diff --git a/core/assets-raw/sprites/units/crawler-leg.png b/core/assets-raw/sprites/units/crawler-leg.png new file mode 100644 index 0000000000..68e76d7f8d Binary files /dev/null and b/core/assets-raw/sprites/units/crawler-leg.png differ diff --git a/core/assets-raw/sprites/units/crawler.png b/core/assets-raw/sprites/units/crawler.png new file mode 100644 index 0000000000..88f81e6476 Binary files /dev/null and b/core/assets-raw/sprites/units/crawler.png differ diff --git a/core/assets-raw/sprites/units/dagger-base.png b/core/assets-raw/sprites/units/dagger-base.png new file mode 100644 index 0000000000..06a1bd3c59 Binary files /dev/null and b/core/assets-raw/sprites/units/dagger-base.png differ diff --git a/core/assets-raw/sprites/units/dagger-leg.png b/core/assets-raw/sprites/units/dagger-leg.png new file mode 100644 index 0000000000..444d18cf09 Binary files /dev/null and b/core/assets-raw/sprites/units/dagger-leg.png differ diff --git a/core/assets-raw/sprites/units/dagger.png b/core/assets-raw/sprites/units/dagger.png new file mode 100644 index 0000000000..25fb15a197 Binary files /dev/null and b/core/assets-raw/sprites/units/dagger.png differ diff --git a/core/assets-raw/sprites/units/draug.png b/core/assets-raw/sprites/units/draug.png new file mode 100644 index 0000000000..e8bd233336 Binary files /dev/null and b/core/assets-raw/sprites/units/draug.png differ diff --git a/core/assets-raw/sprites/units/eradicator-base.png b/core/assets-raw/sprites/units/eradicator-base.png new file mode 100644 index 0000000000..2c143879ef Binary files /dev/null and b/core/assets-raw/sprites/units/eradicator-base.png differ diff --git a/core/assets-raw/sprites/units/eradicator-leg.png b/core/assets-raw/sprites/units/eradicator-leg.png new file mode 100644 index 0000000000..3b822e3b46 Binary files /dev/null and b/core/assets-raw/sprites/units/eradicator-leg.png differ diff --git a/core/assets-raw/sprites/units/eradicator.png b/core/assets-raw/sprites/units/eradicator.png new file mode 100644 index 0000000000..0554a3e349 Binary files /dev/null and b/core/assets-raw/sprites/units/eradicator.png differ diff --git a/core/assets-raw/sprites/units/eruptor-base.png b/core/assets-raw/sprites/units/eruptor-base.png new file mode 100644 index 0000000000..6eefd52275 Binary files /dev/null and b/core/assets-raw/sprites/units/eruptor-base.png differ diff --git a/core/assets-raw/sprites/units/eruptor-leg.png b/core/assets-raw/sprites/units/eruptor-leg.png new file mode 100644 index 0000000000..5172f16d0e Binary files /dev/null and b/core/assets-raw/sprites/units/eruptor-leg.png differ diff --git a/core/assets-raw/sprites/units/eruptor.png b/core/assets-raw/sprites/units/eruptor.png new file mode 100644 index 0000000000..5832adb8f2 Binary files /dev/null and b/core/assets-raw/sprites/units/eruptor.png differ diff --git a/core/assets-raw/sprites/units/fortress-base.png b/core/assets-raw/sprites/units/fortress-base.png new file mode 100644 index 0000000000..27138867ff Binary files /dev/null and b/core/assets-raw/sprites/units/fortress-base.png differ diff --git a/core/assets-raw/sprites/units/fortress-leg.png b/core/assets-raw/sprites/units/fortress-leg.png new file mode 100644 index 0000000000..258cdbf0c1 Binary files /dev/null and b/core/assets-raw/sprites/units/fortress-leg.png differ diff --git a/core/assets-raw/sprites/units/fortress.png b/core/assets-raw/sprites/units/fortress.png new file mode 100644 index 0000000000..dad65f876b Binary files /dev/null and b/core/assets-raw/sprites/units/fortress.png differ diff --git a/core/assets-raw/sprites/units/ghoul.png b/core/assets-raw/sprites/units/ghoul.png new file mode 100644 index 0000000000..17bbd381da Binary files /dev/null and b/core/assets-raw/sprites/units/ghoul.png differ diff --git a/core/assets-raw/sprites/units/lich.png b/core/assets-raw/sprites/units/lich.png new file mode 100644 index 0000000000..d80c0b0294 Binary files /dev/null and b/core/assets-raw/sprites/units/lich.png differ diff --git a/core/assets-raw/sprites/units/phantom.png b/core/assets-raw/sprites/units/phantom.png new file mode 100644 index 0000000000..173b2c991b Binary files /dev/null and b/core/assets-raw/sprites/units/phantom.png differ diff --git a/core/assets-raw/sprites/units/power-cell.png b/core/assets-raw/sprites/units/power-cell.png new file mode 100644 index 0000000000..dfbc73a20b Binary files /dev/null and b/core/assets-raw/sprites/units/power-cell.png differ diff --git a/core/assets-raw/sprites/units/reaper.png b/core/assets-raw/sprites/units/reaper.png new file mode 100644 index 0000000000..c6baf138b1 Binary files /dev/null and b/core/assets-raw/sprites/units/reaper.png differ diff --git a/core/assets-raw/sprites/units/revenant.png b/core/assets-raw/sprites/units/revenant.png new file mode 100644 index 0000000000..0355715192 Binary files /dev/null and b/core/assets-raw/sprites/units/revenant.png differ diff --git a/core/assets-raw/sprites/units/spirit.png b/core/assets-raw/sprites/units/spirit.png new file mode 100644 index 0000000000..1f9a4e89ec Binary files /dev/null and b/core/assets-raw/sprites/units/spirit.png differ diff --git a/core/assets-raw/sprites/units/titan-base.png b/core/assets-raw/sprites/units/titan-base.png new file mode 100644 index 0000000000..27138867ff Binary files /dev/null and b/core/assets-raw/sprites/units/titan-base.png differ diff --git a/core/assets-raw/sprites/units/titan-leg.png b/core/assets-raw/sprites/units/titan-leg.png new file mode 100644 index 0000000000..be3a3ee575 Binary files /dev/null and b/core/assets-raw/sprites/units/titan-leg.png differ diff --git a/core/assets-raw/sprites/units/titan.png b/core/assets-raw/sprites/units/titan.png new file mode 100644 index 0000000000..b3505074ce Binary files /dev/null and b/core/assets-raw/sprites/units/titan.png differ diff --git a/core/assets-raw/sprites/units/wraith.png b/core/assets-raw/sprites/units/wraith.png new file mode 100644 index 0000000000..b95d632036 Binary files /dev/null and b/core/assets-raw/sprites/units/wraith.png differ diff --git a/core/assets-raw/sprites/weapons/artillery-equip.png b/core/assets-raw/sprites/weapons/artillery-equip.png new file mode 100644 index 0000000000..38f11655f8 Binary files /dev/null and b/core/assets-raw/sprites/weapons/artillery-equip.png differ diff --git a/core/assets-raw/sprites/weapons/beam-equip.png b/core/assets-raw/sprites/weapons/beam-equip.png deleted file mode 100644 index 4509ed3ade..0000000000 Binary files a/core/assets-raw/sprites/weapons/beam-equip.png and /dev/null differ diff --git a/core/assets-raw/sprites/weapons/beam.png b/core/assets-raw/sprites/weapons/beam.png deleted file mode 100644 index e7bf82c60f..0000000000 Binary files a/core/assets-raw/sprites/weapons/beam.png and /dev/null differ diff --git a/core/assets-raw/sprites/weapons/blaster-equip.png b/core/assets-raw/sprites/weapons/blaster-equip.png index 6a90610d9f..e8d35d7c9e 100644 Binary files a/core/assets-raw/sprites/weapons/blaster-equip.png and b/core/assets-raw/sprites/weapons/blaster-equip.png differ diff --git a/core/assets-raw/sprites/weapons/blaster.png b/core/assets-raw/sprites/weapons/blaster.png deleted file mode 100644 index b677a58bcd..0000000000 Binary files a/core/assets-raw/sprites/weapons/blaster.png and /dev/null differ diff --git a/core/assets-raw/sprites/weapons/bomber-equip.png b/core/assets-raw/sprites/weapons/bomber-equip.png new file mode 100644 index 0000000000..b5a0872660 Binary files /dev/null and b/core/assets-raw/sprites/weapons/bomber-equip.png differ diff --git a/core/assets-raw/sprites/weapons/chain-blaster-equip.png b/core/assets-raw/sprites/weapons/chain-blaster-equip.png new file mode 100644 index 0000000000..3cde6228a0 Binary files /dev/null and b/core/assets-raw/sprites/weapons/chain-blaster-equip.png differ diff --git a/core/assets-raw/sprites/weapons/chaos-equip.png b/core/assets-raw/sprites/weapons/chaos-equip.png new file mode 100644 index 0000000000..9c7f91085c Binary files /dev/null and b/core/assets-raw/sprites/weapons/chaos-equip.png differ diff --git a/core/assets-raw/sprites/weapons/clustergun-equip.png b/core/assets-raw/sprites/weapons/clustergun-equip.png deleted file mode 100644 index 8f5c6db8a2..0000000000 Binary files a/core/assets-raw/sprites/weapons/clustergun-equip.png and /dev/null differ diff --git a/core/assets-raw/sprites/weapons/clustergun.png b/core/assets-raw/sprites/weapons/clustergun.png deleted file mode 100644 index 1f76fcf8be..0000000000 Binary files a/core/assets-raw/sprites/weapons/clustergun.png and /dev/null differ diff --git a/core/assets-raw/sprites/weapons/eradication-equip.png b/core/assets-raw/sprites/weapons/eradication-equip.png new file mode 100644 index 0000000000..15a7748677 Binary files /dev/null and b/core/assets-raw/sprites/weapons/eradication-equip.png differ diff --git a/core/assets-raw/sprites/weapons/eruption-equip.png b/core/assets-raw/sprites/weapons/eruption-equip.png new file mode 100644 index 0000000000..fb63f47a8f Binary files /dev/null and b/core/assets-raw/sprites/weapons/eruption-equip.png differ diff --git a/core/assets-raw/sprites/weapons/flakgun-equip.png b/core/assets-raw/sprites/weapons/flakgun-equip.png new file mode 100644 index 0000000000..c264823bb3 Binary files /dev/null and b/core/assets-raw/sprites/weapons/flakgun-equip.png differ diff --git a/core/assets-raw/sprites/weapons/flamethrower-equip.png b/core/assets-raw/sprites/weapons/flamethrower-equip.png new file mode 100644 index 0000000000..b7ce3aaeee Binary files /dev/null and b/core/assets-raw/sprites/weapons/flamethrower-equip.png differ diff --git a/core/assets-raw/sprites/weapons/heal-blaster-equip.png b/core/assets-raw/sprites/weapons/heal-blaster-equip.png new file mode 100644 index 0000000000..4499364ab9 Binary files /dev/null and b/core/assets-raw/sprites/weapons/heal-blaster-equip.png differ diff --git a/core/assets-raw/sprites/weapons/lich-missiles-equip.png b/core/assets-raw/sprites/weapons/lich-missiles-equip.png new file mode 100644 index 0000000000..d8fbd8549b Binary files /dev/null and b/core/assets-raw/sprites/weapons/lich-missiles-equip.png differ diff --git a/core/assets-raw/sprites/weapons/missiles-equip.png b/core/assets-raw/sprites/weapons/missiles-equip.png new file mode 100644 index 0000000000..b5a0872660 Binary files /dev/null and b/core/assets-raw/sprites/weapons/missiles-equip.png differ diff --git a/core/assets-raw/sprites/weapons/reaper-gun-equip.png b/core/assets-raw/sprites/weapons/reaper-gun-equip.png new file mode 100644 index 0000000000..016e6dacf4 Binary files /dev/null and b/core/assets-raw/sprites/weapons/reaper-gun-equip.png differ diff --git a/core/assets-raw/sprites/weapons/revenant-missiles-equip.png b/core/assets-raw/sprites/weapons/revenant-missiles-equip.png new file mode 100644 index 0000000000..6aaac4c6d7 Binary files /dev/null and b/core/assets-raw/sprites/weapons/revenant-missiles-equip.png differ diff --git a/core/assets-raw/sprites/weapons/shockgun-equip.png b/core/assets-raw/sprites/weapons/shockgun-equip.png index 96a33a55a5..5710b7c655 100644 Binary files a/core/assets-raw/sprites/weapons/shockgun-equip.png and b/core/assets-raw/sprites/weapons/shockgun-equip.png differ diff --git a/core/assets-raw/sprites/weapons/shockgun.png b/core/assets-raw/sprites/weapons/shockgun.png deleted file mode 100644 index 9ed4e1521b..0000000000 Binary files a/core/assets-raw/sprites/weapons/shockgun.png and /dev/null differ diff --git a/core/assets-raw/sprites/weapons/swarmer-equip.png b/core/assets-raw/sprites/weapons/swarmer-equip.png new file mode 100644 index 0000000000..68de627f44 Binary files /dev/null and b/core/assets-raw/sprites/weapons/swarmer-equip.png differ diff --git a/core/assets-raw/sprites/weapons/triblaster-equip.png b/core/assets-raw/sprites/weapons/triblaster-equip.png deleted file mode 100644 index 231869b8e4..0000000000 Binary files a/core/assets-raw/sprites/weapons/triblaster-equip.png and /dev/null differ diff --git a/core/assets-raw/sprites/weapons/triblaster.png b/core/assets-raw/sprites/weapons/triblaster.png deleted file mode 100644 index 1b6e1db871..0000000000 Binary files a/core/assets-raw/sprites/weapons/triblaster.png and /dev/null differ diff --git a/core/assets-raw/sprites/weapons/vulcan-equip.png b/core/assets-raw/sprites/weapons/vulcan-equip.png deleted file mode 100644 index 1c9ddf9401..0000000000 Binary files a/core/assets-raw/sprites/weapons/vulcan-equip.png and /dev/null differ diff --git a/core/assets-raw/sprites/weapons/vulcan.png b/core/assets-raw/sprites/weapons/vulcan.png deleted file mode 100644 index a4632649f9..0000000000 Binary files a/core/assets-raw/sprites/weapons/vulcan.png and /dev/null differ diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index fd1153ce6f..a61e7d4ec5 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -1,558 +1,984 @@ -text.about=Created by [ROYAL]Anuken[] - [SKY]anukendev@gmail.com[]\nOriginally an entry in the [orange]GDL[] Metal Monstrosity Jam.\n\nCredits:\n- SFX made with [YELLOW]bfxr[]\n- Music made by [GREEN]a beat a day[]\n\nSpecial thanks to:\n- [coral]MitchellFJN[]: extensive playtesting and feedback\n- [sky]Luxray5474[]: wiki work, code contributions\n- [lime]Epowerj[]: code build system, icon\n- All the beta testers on itch.io and Google Play\n -text.credits=Credits -text.discord=Join the mindustry discord! -text.changes=[SCARLET]Attention!\n[]Some important game mechanics have been changed.\n\n- [accent]Teleporters[] now use power.\n- [accent]Smelteries[] and [accent]crucibles[] now have a maximum item capacity.\n- [accent]Crucibles[] now require coal as fuel. -text.link.discord.description=the official Mindustry discord chatroom -text.link.github.description=Game source code -text.link.dev-builds.description=Unstable development builds -text.link.trello.description=Official trello board for planned features -text.link.itch.io.description=itch.io page with PC downloads and web version -text.link.google-play.description=Google Play store listing -text.link.wiki.description=official Mindustry wiki -text.linkfail=Failed to open link!\nThe URL has been copied to your cliboard. -text.editor.web=The web version does not support the editor!\nDownload the game to use it. -text.multiplayer.web=This version of the game does not support multiplayer!\nTo play multiplayer from your browser, use the "multiplayer web version" link at the itch.io page. -text.gameover=The core was destroyed. -text.highscore=[YELLOW]New highscore! -text.lasted=You lasted until wave -text.level.highscore=High Score: [accent]{0} -text.level.delete.title=Confirm Delete -text.level.delete=Are you sure you want to delete\nthe map "[orange]{0}"? -text.level.select=Level Select -text.level.mode=Gamemode: -text.savegame=Save Game -text.loadgame=Load Game -text.joingame=Join Game -text.newgame=New Game -text.quit=Quit -text.about.button=About -text.name=Name: -text.public=Public -text.players={0} players online -text.server.player.host={0} (host) -text.players.single={0} player online -text.server.mismatch=Packet error: possible client/server version mismatch.\nMake sure you and the host have the\nlatest version of Mindustry! -text.server.closing=[accent]Closing server... -text.server.kicked.kick=You have been kicked from the server! -text.server.kicked.fastShoot=You are shooting too quickly. -text.server.kicked.invalidPassword=Invalid password! -text.server.kicked.clientOutdated=Outdated client! Update your game! -text.server.kicked.serverOutdated=Outdated server! Ask the host to update! -text.server.kicked.banned=You are banned on this server. -text.server.kicked.recentKick=You have been kicked recently.\nWait before connecting again. -text.server.connected={0} has joined. -text.server.disconnected={0} has disconnected. -text.nohost=Can't host server on a custom map! -text.host.info=The [accent]host[] button hosts a server on ports [scarlet]6567[] and [scarlet]6568.[]\nAnybody on the same [LIGHT_GRAY]wifi or local network[] should be able to see your server in their server list.\n\nIf you want people to be able to connect from anywhere by IP, [accent]port forwarding[] is required.\n\n[LIGHT_GRAY]Note: If someone is experiencing trouble connecting to your LAN game, make sure you have allowed Mindustry access to your local network in your firewall settings. -text.join.info=Here, you can enter a [accent]server IP[] to connect to, or discover [accent]local network[] servers to connect to.\nBoth LAN and WAN multiplayer is supported.\n\n[LIGHT_GRAY]Note: There is no automatic global server list; if you want to connect to someone by IP, you would need to ask the host for their IP. -text.hostserver=Host Server -text.host=Host -text.hosting=[accent]Opening server... -text.hosts.refresh=Refresh -text.hosts.discovering=Discovering LAN games -text.server.refreshing=Refreshing server -text.hosts.none=[lightgray]No LAN games found! -text.host.invalid=[scarlet]Can't connect to host. -text.server.friendlyfire=Friendly Fire -text.trace=Trace Player -text.trace.playername=Player name: [accent]{0} -text.trace.ip=IP: [accent]{0} -text.trace.id=Unique ID: [accent]{0} -text.trace.android=Android Client: [accent]{0} -text.trace.modclient=Custom Client: [accent]{0} -text.trace.totalblocksbroken=Total blocks broken: [accent]{0} -text.trace.structureblocksbroken=Structure blocks broken: [accent]{0} -text.trace.lastblockbroken=Last block broken: [accent]{0} -text.trace.totalblocksplaced=Total blocks placed: [accent]{0} -text.trace.lastblockplaced=Last block placed: [accent]{0} -text.invalidid=Invalid client ID! Submit a bug report. -text.server.bans=Bans -text.server.bans.none=No banned players found! -text.server.admins=Admins -text.server.admins.none=No admins found! -text.server.rollback=Rollback -text.server.rollback.numberfield=Rollback Amount: -text.server.add=Add Server -text.server.delete=Are you sure you want to delete this server? -text.server.hostname=Host: {0} -text.server.edit=Edit Server -text.server.outdated=[crimson]Outdated Server![] -text.server.outdated.client=[crimson]Outdated Client![] -text.server.version=[lightgray]Version: {0} -text.server.custombuild=[yellow]Custom Build -text.confirmban=Are you sure you want to ban this player? -text.confirmunban=Are you sure you want to unban this player? -text.confirmadmin=Are you sure you want to make this player an admin? -text.confirmunadmin=Are you sure you want to remove admin status from this player? -text.joingame.byip=Join by IP... -text.joingame.title=Join Game -text.joingame.ip=IP: -text.disconnect=Disconnected. -text.disconnect.data=Failed to load world data! -text.connecting=[accent]Connecting... -text.connecting.data=[accent]Loading world data... -text.connectfail=[crimson]Failed to connect to server: [orange]{0} -text.server.port=Port: -text.server.addressinuse=Address already in use! -text.server.invalidport=Invalid port number! -text.server.error=[crimson]Error hosting server: [orange]{0} -text.tutorial.back=< Prev -text.tutorial.next=Next > -text.save.new=New Save -text.save.overwrite=Are you sure you want to overwrite\nthis save slot? -text.overwrite=Overwrite -text.save.none=No saves found! -text.saveload=[accent]Saving... -text.savefail=Failed to save game! -text.save.delete.confirm=Are you sure you want to delete this save? -text.save.delete=Delete -text.save.export=Export Save -text.save.import.invalid=[orange]This save is invalid!\n\nNote that[scarlet]importing saves with custom maps[orange]\nfrom other devices does not work! -text.save.import.fail=[crimson]Failed to import save: [orange]{0} -text.save.export.fail=[crimson]Failed to export save: [orange]{0} -text.save.import=Import Save -text.save.newslot=Save name: -text.save.rename=Rename -text.save.rename.text=New name: -text.selectslot=Select a save. -text.slot=[accent]Slot {0} -text.save.corrupted=[orange]Save file corrupted or invalid! -text.empty= -text.on=On -text.off=Off -text.save.autosave=Autosave: {0} -text.save.map=Map: {0} -text.save.wave=Wave {0} -text.save.difficulty=Difficulty: {0} -text.save.date=Last Saved: {0} -text.confirm=Confirm -text.delete=Delete -text.ok=OK -text.open=Open -text.cancel=Cancel -text.openlink=Open Link -text.copylink=Copy Link -text.back=Back -text.quit.confirm=Are you sure you want to quit? -text.changelog.title=Changelog -text.changelog.loading=Getting changelog... -text.changelog.error.android=[orange]Note that the changelog sometimes does not work on Android 4.4 and below!\nThis is due to an internal Android bug. -text.changelog.error.ios=[orange]The changelog is currently not supported in iOS. -text.changelog.error=[scarlet]Error getting changelog!\nCheck your internet connection. -text.changelog.current=[yellow][[Current version] -text.changelog.latest=[orange][[Latest version] -text.loading=[accent]Loading... -text.wave=[orange]Wave {0} -text.wave.waiting=Wave in {0} -text.waiting=Waiting... -text.enemies={0} Enemies -text.enemies.single={0} Enemy -text.loadimage=Load Image -text.saveimage=Save Image -text.oregen=Ore Generation -text.editor.badsize=[orange]Invalid image dimensions![]\nValid map dimensions: {0} -text.editor.errorimageload=Error loading image file:\n[orange]{0} -text.editor.errorimagesave=Error saving image file:\n[orange]{0} -text.editor.generate=Generate -text.editor.resize=Resize -text.editor.loadmap=Load Map -text.editor.savemap=Save Map -text.editor.loadimage=Load Image -text.editor.saveimage=Save Image -text.editor.unsaved=[scarlet]You have unsaved changes![]\nAre you sure you want to exit? -text.editor.brushsize=Brush size: {0} -text.editor.noplayerspawn=This map has no player spawnpoint! -text.editor.manyplayerspawns=Maps cannot have more than one\nplayer spawnpoint! -text.editor.manyenemyspawns=Cannot have more than\n{0} enemy spawnpoints! -text.editor.resizemap=Resize Map -text.editor.resizebig=[scarlet]Warning!\n[]Maps larger than 256 units may be laggy and unstable. -text.editor.mapname=Map Name: -text.editor.overwrite=[accent]Warning!\nThis overwrites an existing map. -text.editor.failoverwrite=[crimson]Cannot overwrite default map! -text.editor.selectmap=Select a map to load: -text.width=Width: -text.height=Height: -text.randomize=Randomize -text.apply=Apply -text.update=Update -text.menu=Menu -text.play=Play -text.load=Load -text.save=Save -text.language.restart=Please restart your game for the language settings to take effect. -text.settings.language=Language -text.settings=Settings -text.tutorial=Tutorial -text.editor=Editor -text.mapeditor=Map Editor -text.donate=Donate -text.settings.reset=Reset to Defaults -text.settings.controls=Controls -text.settings.game=Game -text.settings.sound=Sound -text.settings.graphics=Graphics -text.upgrades=Upgrades -text.purchased=[LIME]Created! -text.weapons=Weapons -text.paused=Paused -text.respawn=Respawning in -text.info.title=[accent]Info -text.error.title=[crimson]An error has occured -text.error.crashmessage=[SCARLET]An unexpected error has occured, which would have caused a crash.\n[]Please report the exact circumstances under which this error occured to the developer: \n[ORANGE]anukendev@gmail.com[] -text.error.crashtitle=An error has occured -text.mode.break=Break mode: {0} -text.mode.place=Place mode: {0} -placemode.hold.name=line -placemode.areadelete.name=area -placemode.touchdelete.name=touch -placemode.holddelete.name=hold -placemode.none.name=none -placemode.touch.name=touch -placemode.cursor.name=cursor -text.blocks.extrainfo=[accent]extra block info: -text.blocks.blockinfo=Block Info -text.blocks.editlogs=Edit Logs -text.block.editlogsnotfound=[red]There are no edit logs for this location. -text.blocks.powercapacity=Power Capacity -text.blocks.powershot=Power/shot -text.blocks.powersecond=Power/second -text.blocks.powerdraindamage=Power Drain/damage -text.blocks.shieldradius=Shield Radius -text.blocks.itemspeedsecond=Item Speed/second -text.blocks.range=Range -text.blocks.size=Size -text.blocks.powerliquid=Power/Liquid -text.blocks.maxliquidsecond=Max liquid/second -text.blocks.liquidcapacity=Liquid capacity -text.blocks.liquidsecond=Liquid/second -text.blocks.damageshot=Damage/shot -text.blocks.ammocapacity=Ammo Capacity -text.blocks.ammo=Ammo -text.blocks.ammoitem=Ammo/item -text.blocks.maxitemssecond=Max items/second -text.blocks.powerrange=Power range -text.blocks.lasertilerange=Laser tile range -text.blocks.capacity=Capacity -text.blocks.itemcapacity=Item Capacity -text.blocks.maxpowergenerationsecond=Max Power Generation/second -text.blocks.powergenerationsecond=Power Generation/second -text.blocks.generationsecondsitem=Generation Seconds/item -text.blocks.input=Input -text.blocks.inputliquid=Input Liquid -text.blocks.inputitem=Input Item -text.blocks.output=Output -text.blocks.secondsitem=Seconds/item -text.blocks.maxpowertransfersecond=Max power transfer/second -text.blocks.explosive=Highly explosive! -text.blocks.repairssecond=Repaired/second -text.blocks.health=Health -text.blocks.inaccuracy=Inaccuracy -text.blocks.shots=Shots -text.blocks.shotssecond=Shots/second -text.blocks.fuel=Fuel -text.blocks.fuelduration=Fuel Duration -text.blocks.maxoutputsecond=Max output/second -text.blocks.inputcapacity=Input capacity -text.blocks.outputcapacity=Output capacity -text.blocks.poweritem=Power/Item -text.placemode=Place Mode -text.breakmode=Break Mode -text.health=health -setting.difficulty.easy=easy -setting.difficulty.normal=normal -setting.difficulty.hard=hard -setting.difficulty.insane=insane -setting.difficulty.purge=purge -setting.difficulty.name=Difficulty: -setting.screenshake.name=Screen Shake -setting.smoothcam.name=Smooth Camera -setting.indicators.name=Enemy Indicators -setting.effects.name=Display Effects -setting.sensitivity.name=Controller Sensitivity -setting.saveinterval.name=Autosave Interval -setting.seconds={0} Seconds -setting.fullscreen.name=Fullscreen -setting.multithread.name=Multithreading -setting.fps.name=Show FPS -setting.vsync.name=VSync -setting.lasers.name=Show Power Lasers -setting.previewopacity.name=Placing Preview Opacity -setting.healthbars.name=Show Entity Health bars -setting.pixelate.name=Pixelate Screen -setting.musicvol.name=Music Volume -setting.mutemusic.name=Mute Music -setting.sfxvol.name=SFX Volume -setting.mutesound.name=Mute Sound -map.maze.name=maze -map.fortress.name=fortress -map.sinkhole.name=sinkhole -map.caves.name=caves -map.volcano.name=volcano -map.caldera.name=caldera -map.scorch.name=scorch -map.desert.name=desert -map.island.name=island -map.grassland.name=grassland -map.tundra.name=tundra -map.spiral.name=spiral -map.tutorial.name=tutorial -tutorial.intro.text=[yellow]Welcome to the tutorial.[] To begin, press 'next'. -tutorial.moveDesktop.text=To move, use the [orange][[WASD][] keys. Hold [orange]shift[] to boost. Hold [orange]CTRL[] while using the [orange]scrollwheel[] to zoom in or out. -tutorial.shoot.text=Use your mouse to aim, hold [orange]left mouse button[] to shoot. Try practicing on the [yellow]target[]. -tutorial.moveAndroid.text=To pan the view, drag one finger across the screen. Pinch and drag to zoom in or out. -tutorial.placeSelect.text=Try selecting a [yellow]conveyor[] from the block menu in the bottom right. -tutorial.placeConveyorDesktop.text=Use the [orange][[scrollwheel][] to rotate the conveyor to face [orange]forwards[], then place it in the [yellow]marked location[] using the [orange][[left mouse button][]. -tutorial.placeConveyorAndroid.text=Use the [orange][[rotate button][] to rotate the conveyor to face [orange]forwards[], drag it into position with one finger, then place it in the [yellow]marked location[] using the [orange][[checkmark][]. -tutorial.placeConveyorAndroidInfo.text=Alternatively, you can press the crosshair icon in the bottom left to switch to [orange][[touch mode][], and place blocks by tapping on the screen. In touch mode, blocks can be rotated with the arrow at the bottom left. Press [yellow]next[] to try it out. -tutorial.placeDrill.text=Now, select and place a [yellow]stone drill[] at the marked location. -tutorial.blockInfo.text=If you want to learn more about a block, you can tap the [orange]question mark[] in the top right to read its description. -tutorial.deselectDesktop.text=You can de-select a block using the [orange][[right mouse button][]. -tutorial.deselectAndroid.text=You can deselect a block by pressing the [orange]X[] button. -tutorial.drillPlaced.text=The drill will now produce [yellow]stone,[] output it onto the conveyor, then move it into the [yellow]core[]. -tutorial.drillInfo.text=Different ores need different drills. Stone requires stone drills, iron requires iron drills, etc. -tutorial.drillPlaced2.text=Moving items into the core puts them in your [yellow]item inventory[], in the top left. Placing blocks uses items from your inventory. -tutorial.moreDrills.text=You can link many drills and conveyors up together, like so. -tutorial.deleteBlock.text=You can delete blocks by clicking the [orange]right mouse button[] on the block you want to delete. Try deleting this conveyor. -tutorial.deleteBlockAndroid.text=You can delete blocks by [orange]selecting the crosshair[] in the [orange]break mode menu[] in the bottom left and tapping a block. Try deleting this conveyor. -tutorial.placeTurret.text=Now, select and place a [yellow]turret[] at the [yellow]marked location[]. -tutorial.placedTurretAmmo.text=This turret will now accept [yellow]ammo[] from the conveyor. You can see how much ammo it has by hovering over it and checking the [green]green bar[]. -tutorial.turretExplanation.text=Turrets will automatically shoot at the nearest enemy in range, as long as they have enough ammo. -tutorial.waves.text=Every [yellow]60[] seconds, a wave of [coral]enemies[] will spawn in specific locations and attempt to destroy the core. -tutorial.coreDestruction.text=Your objective is to [yellow]defend the core[]. If the core is destroyed, you [coral]lose the game[]. -tutorial.pausingDesktop.text=If you ever need to take a break, press the [orange]pause button[] in the top left or [orange]space[] to pause the game. You can still select and place blocks while paused, but cannot move or shoot. -tutorial.pausingAndroid.text=If you ever need to take a break, press the [orange]pause button[] in the top left to pause the game. You can still break and place blocks while paused. -tutorial.purchaseWeapons.text=You can purchase new [yellow]weapons[] for your mech by opening the upgrade menu in the bottom left. -tutorial.switchWeapons.text=Switch weapons by either clicking its icon in the bottom left, or using numbers [orange][[1-9][]. -tutorial.spawnWave.text=Here comes a wave now. Destroy them. -tutorial.pumpDesc.text=In later waves, you might need to use [yellow]pumps[] to distribute liquids for generators or extractors. -tutorial.pumpPlace.text=Pumps work similarly to drills, except that they produce liquids instead of items. Try placing a pump on the [yellow]designated oil[]. -tutorial.conduitUse.text=Now place a [orange]conduit[] leading away from the pump. -tutorial.conduitUse2.text=And a few more... -tutorial.conduitUse3.text=And a few more... -tutorial.generator.text=Now, place a [orange]combustion generator[] block at the end of the conduit. -tutorial.generatorExplain.text=This generator will now create [yellow]power[] from the oil. -tutorial.lasers.text=Power is distributed using [yellow]power lasers[]. Rotate and place one here. -tutorial.laserExplain.text=The generator will now move power into the laser block. An [yellow]opaque[] beam means that it is currently transmitting power, and a [yellow]transparent[] beam means it is not. -tutorial.laserMore.text=You can check how much power a block has by hovering over it and checking the [yellow]yellow bar[] at the top. -tutorial.healingTurret.text=This laser can be used to power a [lime]repair turret[]. Place one here. -tutorial.healingTurretExplain.text=As long as it has power, this turret will [lime]repair nearby blocks.[] When playing, make sure you get one in your base as quickly as possible! -tutorial.smeltery.text=Many blocks require [orange]steel[] to make, which requires a [orange]smelter[] to craft. Place one here. -tutorial.smelterySetup.text=This smelter will now produce [orange]steel[] from the input iron, using coal as fuel. -tutorial.tunnelExplain.text=Also note that the items are going through a [orange]tunnel block[] and emerging on the other side, going through the stone block. Keep in mind that tunnels can only go through up to 2 blocks. -tutorial.end.text=And that concludes the tutorial! Good luck! -text.keybind.title=Rebind Keys -keybind.move_x.name=move_x -keybind.move_y.name=move_y -keybind.select.name=select -keybind.break.name=break -keybind.shoot.name=shoot -keybind.zoom_hold.name=zoom_hold -keybind.zoom.name=zoom -keybind.block_info.name=block_info -keybind.menu.name=menu -keybind.pause.name=pause -keybind.dash.name=dash -keybind.chat.name=chat -keybind.player_list.name=player_list -keybind.console.name=console -keybind.rotate_alt.name=rotate_alt -keybind.rotate.name=rotate -keybind.weapon_1.name=weapon_1 -keybind.weapon_2.name=weapon_2 -keybind.weapon_3.name=weapon_3 -keybind.weapon_4.name=weapon_4 -keybind.weapon_5.name=weapon_5 -keybind.weapon_6.name=weapon_6 -mode.text.help.title=Description of modes -mode.waves.name=waves -mode.waves.description=the normal mode. limited resources and automatic incoming waves. -mode.sandbox.name=sandbox -mode.sandbox.description=infinite resources and no timer for waves. -mode.freebuild.name=freebuild -mode.freebuild.description=limited resources and no timer for waves. -upgrade.standard.name=standard -upgrade.standard.description=The standard mech. -upgrade.blaster.name=blaster -upgrade.blaster.description=Shoots a slow, weak bullet. -upgrade.triblaster.name=triblaster -upgrade.triblaster.description=Shoots 3 bullets in a spread. -upgrade.clustergun.name=clustergun -upgrade.clustergun.description=Shoots an inaccurate spread of explosive grenades. -upgrade.beam.name=beam cannon -upgrade.beam.description=Shoots a long-range piercing laser beam. -upgrade.vulcan.name=vulcan -upgrade.vulcan.description=Shoots a barrage of fast bullets. -upgrade.shockgun.name=shockgun -upgrade.shockgun.description=Shoots a devastating blast of charged shrapnel. -item.stone.name=stone -item.iron.name=iron -item.coal.name=coal -item.steel.name=steel -item.titanium.name=titanium -item.dirium.name=dirium -item.uranium.name=uranium -item.sand.name=sand -liquid.water.name=water -liquid.plasma.name=plasma -liquid.lava.name=lava -liquid.oil.name=oil -block.weaponfactory.name=weapon factory -block.weaponfactory.fulldescription=Used to create weapons for the player mech. Click to use. Automatically takes resources from the core. -block.air.name=air -block.blockpart.name=blockpart -block.deepwater.name=deepwater -block.water.name=water -block.lava.name=lava -block.oil.name=oil -block.stone.name=stone -block.blackstone.name=blackstone -block.iron.name=iron -block.coal.name=coal -block.titanium.name=titanium -block.uranium.name=uranium -block.dirt.name=dirt -block.sand.name=sand -block.ice.name=ice -block.snow.name=snow -block.grass.name=grass -block.sandblock.name=sandblock -block.snowblock.name=snowblock -block.stoneblock.name=stoneblock -block.blackstoneblock.name=blackstoneblock -block.grassblock.name=grassblock -block.mossblock.name=mossblock -block.shrub.name=shrub -block.rock.name=rock -block.icerock.name=icerock -block.blackrock.name=blackrock -block.dirtblock.name=dirtblock -block.stonewall.name=stone wall -block.stonewall.fulldescription=A cheap defensive block. Useful for protecting the core and turrets in the first few waves. -block.ironwall.name=iron wall -block.ironwall.fulldescription=A basic defensive block. Provides protection from enemies. -block.steelwall.name=steel wall -block.steelwall.fulldescription=A standard defensive block. adequate protection from enemies. -block.titaniumwall.name=titanium wall -block.titaniumwall.fulldescription=A strong defensive block. Provides protection from enemies. -block.duriumwall.name=dirium wall -block.duriumwall.fulldescription=A very strong defensive block. Provides protection from enemies. -block.compositewall.name=composite wall -block.steelwall-large.name=large steel wall -block.steelwall-large.fulldescription=A standard defensive block. Spans multiple tiles. -block.titaniumwall-large.name=large titanium wall -block.titaniumwall-large.fulldescription=A strong defensive block. Spans multiple tiles. -block.duriumwall-large.name=large dirium wall -block.duriumwall-large.fulldescription=A very strong defensive block. Spans multiple tiles. -block.titaniumshieldwall.name=shielded wall -block.titaniumshieldwall.fulldescription=A strong defensive block, with an extra built-in shield. Requires power. Uses energy to absorb enemy bullets. It is recommended to use power boosters to provide energy to this block. -block.repairturret.name=repair turret -block.repairturret.fulldescription=Repairs nearby damaged blocks in range at a slow rate. Uses small amounts of power. -block.megarepairturret.name=repair turret II -block.megarepairturret.fulldescription=Repairs nearby damaged blocks in range at a decent rate. Uses power. -block.shieldgenerator.name=shield generator -block.shieldgenerator.fulldescription=An advanced defensive block. Shields all the blocks in a radius from attack. Uses power at a slow rate when idle, but drains energy quickly on bullet contact. -block.door.name=door -block.door.fulldescription=A block than can be opened and closed by tapping it. -block.door-large.name=large door -block.door-large.fulldescription=A block than can be opened and closed by tapping it. -block.conduit.name=conduit -block.conduit.fulldescription=Basic liquid transport block. Works like a conveyor, but with liquids. Best used with pumps or other conduits. Can be used as a bridge over liquids for enemies and players. -block.pulseconduit.name=pulse conduit -block.pulseconduit.fulldescription=Advanced liquid transport block. Transports liquids faster and stores more than standard conduits. -block.liquidrouter.name=liquid router -block.liquidrouter.fulldescription=Works similarly to a router. Accepts liquid input from one side and outputs it to the other sides. Useful for splitting liquid from a single conduit into multiple other conduits. -block.conveyor.name=conveyor -block.conveyor.fulldescription=Basic item transport block. Moves items forward and automatically deposits them into turrets or crafters. Rotatable. Can be used as a bridge over liquids for enemies and players. -block.steelconveyor.name=steel conveyor -block.steelconveyor.fulldescription=Advanced item transport block. Moves items faster than standard conveyors. -block.poweredconveyor.name=pulse conveyor -block.poweredconveyor.fulldescription=The ultimate item transport block. Moves items faster than steel conveyors. -block.router.name=router -block.router.fulldescription=Accepts items from one direction and outputs them to 3 other directions. Can also store a certain amount of items.Useful for splitting the materials from one drill into multiple turrets. -block.junction.name=junction -block.junction.fulldescription=Acts as a bridge for two crossing conveyor belts. Useful in situations with two different conveyors carrying different materials to different locations. -block.conveyortunnel.name=conveyor tunnel -block.conveyortunnel.fulldescription=Transports item under blocks. To use, place one tunnel leading into the block to be tunneled under, and one on the other side. Make sure both tunnels face opposite directions, which is towards the blocks they are inputting or outputting to. -block.liquidjunction.name=liquid junction -block.liquidjunction.fulldescription=Acts as a bridge for two crossing conduits. Useful in situations with two different conduits carrying different liquids to different locations. -block.liquiditemjunction.name=liquid-item junction -block.liquiditemjunction.fulldescription=Acts as a bridge for crossing conduits and conveyors. -block.powerbooster.name=power booster -block.powerbooster.fulldescription=Distributes power to all blocks within its radius. -block.powerlaser.name=power laser -block.powerlaser.fulldescription=Creates a laser that transmits power to the block in front of it. Does not generate any power itself. Best used with generators or other lasers. -block.powerlaserrouter.name=laser router -block.powerlaserrouter.fulldescription=Laser that distributes power to three directions at once. Useful in situations where it is required to power multiple blocks from one generator. -block.powerlasercorner.name=laser corner -block.powerlasercorner.fulldescription=Laser that distributes power to two directions at once. Useful in situations where it is required to power multiple blocks from one generator, and a router is imprecise. -block.teleporter.name=teleporter -block.teleporter.fulldescription=Advanced item transport block. Teleporters input items to other teleporters of the same color. Does nothing if no teleporters of the same color exist. If multiple teleporters exist of the same color, a random one is selected. Uses power. Tap to change color. -block.sorter.name=sorter -block.sorter.fulldescription=Sorts item by material type. Material to accept is indicated by the color in the block. All items that match the sort material are outputted forward, everything else is outputted to the left and right. -block.core.name=core -block.pump.name=pump -block.pump.fulldescription=Pumps liquids from a source block- usually water, lava or oil. Outputs liquid into nearby conduits. -block.fluxpump.name=fluxpump -block.fluxpump.fulldescription=An advanced version of the pump. Stores more liquid and pumps liquid faster. -block.smelter.name=smelter -block.smelter.fulldescription=The essential crafting block. When inputted 1 iron and 1 coal as fuel, outputs one steel. It is advised to input iron and coal on different belts to prevent clogging. -block.crucible.name=crucible -block.crucible.fulldescription=An advanced crafting block. When inputted 1 titanium, 1 steel and 1 coal as fuel, outputs one dirium. It is advised to input coal, steel and titanium on different belts to prevent clogging. -block.coalpurifier.name=coal extractor -block.coalpurifier.fulldescription=A basic extractor block. Outputs coal when supplied with large amounts of water and stone. -block.titaniumpurifier.name=titanium extractor -block.titaniumpurifier.fulldescription=A standard extractor block. Outputs titanium when supplied with large amounts of water and iron. -block.oilrefinery.name=oil refinery -block.oilrefinery.fulldescription=Refines large amounts of oil into coal items. Useful for fueling coal-based turrets when coal veins are scarce. -block.stoneformer.name=stone former -block.stoneformer.fulldescription=Soldifies liquid lava into stone. Useful for producing massive amounts of stone for coal purifiers. -block.lavasmelter.name=lava smelter -block.lavasmelter.fulldescription=Uses lava to convert iron to steel. An alternative to smelteries. Useful in situations where coal is scarce. -block.stonedrill.name=stone drill -block.stonedrill.fulldescription=The essential drill. When placed on stone tiles, outputs stone at a slow pace indefinitely. -block.irondrill.name=iron drill -block.irondrill.fulldescription=A basic drill. When placed on iron ore tiles, outputs iron at a slow pace indefinitely. -block.coaldrill.name=coal drill -block.coaldrill.fulldescription=A basic drill. When placed on coal ore tiles, outputs coal at a slow pace indefinitely. -block.uraniumdrill.name=uranium drill -block.uraniumdrill.fulldescription=An advanced drill. When placed on uranium ore tiles, outputs uranium at a slow pace indefinitely. -block.titaniumdrill.name=titanium drill -block.titaniumdrill.fulldescription=An advanced drill. When placed on titanium ore tiles, outputs titanium at a slow pace indefinitely. -block.omnidrill.name=omnidrill -block.omnidrill.fulldescription=The ultimate drill. Will mine any ore it is placed on at a rapid pace. -block.coalgenerator.name=coal generator -block.coalgenerator.fulldescription=The essential generator. Generates power from coal. Outputs power as lasers to its 4 sides. -block.thermalgenerator.name=thermal generator -block.thermalgenerator.fulldescription=Generates power from lava. Outputs power as lasers to its 4 sides. -block.combustiongenerator.name=combustion generator -block.combustiongenerator.fulldescription=Generates power from oil. Outputs power as lasers to its 4 sides. -block.rtgenerator.name=RTG generator -block.rtgenerator.fulldescription=Generates small amounts of power from the radioactive decay of uranium. Outputs power as lasers to its 4 sides. -block.nuclearreactor.name=nuclear reactor -block.nuclearreactor.fulldescription=An advanced version of the RTG Generator, and the ultimate power generator. Generates power from uranium. Requires constant water cooling. Highly volatile; will explode violently if insufficient amounts of coolant are supplied. -block.turret.name=turret -block.turret.fulldescription=A basic, cheap turret. Uses stone for ammo. Has slightly more range than the double-turret. -block.doubleturret.name=double turret -block.doubleturret.fulldescription=A slightly more powerful version of the turret. Uses stone for ammo. Does significantly more damage, but has a lower range. Shoots two bullets. -block.machineturret.name=gattling turret -block.machineturret.fulldescription=A standard all-around turret. Uses iron for ammo. Has a fast fire rate with decent damage. -block.shotgunturret.name=splitter turret -block.shotgunturret.fulldescription=A standard turret. Uses iron for ammo. Shoots a spread of 7 bullets. Lower range, but higher damage output than the gattling turret. -block.flameturret.name=flamer turret -block.flameturret.fulldescription=Advanced close-range turret. Uses coal for ammo. Has very low range, but very high damage. Good for close quarters. Recommended to be used behind walls. -block.sniperturret.name=railgun turret -block.sniperturret.fulldescription=Advanced long-range turret. Uses steel for ammo. Very high damage, but low fire rate. Expensive to use, but can be placed far away from enemy lines due to its range. -block.mortarturret.name=flak turret -block.mortarturret.fulldescription=Advanced low-accuracy splash-damage turret. Uses coal for ammo. Shoots a barrage of bullets that explode into shrapnel. Useful for large crowds of enemies. -block.laserturret.name=laser turret -block.laserturret.fulldescription=Advanced single-target turret. Uses power. Good medium-range all-around turret. Single-target only. Never misses. -block.waveturret.name=tesla turret -block.waveturret.fulldescription=Advanced multi-target turret. Uses power. Medium range. Never misses.Average to low damage, but can hit multiple enemies simultaneously with chain lighting. -block.plasmaturret.name=plasma turret -block.plasmaturret.fulldescription=Highly advanced version of the flamer turret. Uses coal as ammo. Very high damage, low to medium range. -block.chainturret.name=chain turret -block.chainturret.fulldescription=The ultimate rapid-fire turret. Uses uranium as ammo. Shoots large slugs at a high fire rate. Medium range. Spans multiple tiles. Extremely tough. -block.titancannon.name=titan cannon -block.titancannon.fulldescription=The ultimate long-range turret. Uses uranium as ammo. Shoots large splash-damage shells at a medium rate of fire. Long range. Spans multiple tiles. Extremely tough. -block.playerspawn.name=playerspawn -block.enemyspawn.name=enemyspawn \ No newline at end of file +credits.text = Created by [ROYAL]Anuken[] - [SKY]anukendev@gmail.com[] +credits = Credits +contributors = Translators and Contributors +discord = Join the Mindustry Discord! +link.discord.description = The official Mindustry Discord chatroom +link.github.description = Game source code +link.dev-builds.description = Unstable development builds +link.trello.description = Official Trello board for planned features +link.itch.io.description = itch.io page with PC downloads and web version +link.google-play.description = Google Play store listing +link.wiki.description = Official Mindustry wiki +linkfail = Failed to open link!\nThe URL has been copied to your clipboard. +screenshot = Screenshot saved to {0} +screenshot.invalid = Map too large, potentially not enough memory for screenshot. +gameover = Game Over +gameover.pvp = The[accent] {0}[] team is victorious! +highscore = [accent]New highscore! + +stat.wave = Waves Defeated:[accent] {0} +stat.enemiesDestroyed = Enemies Destroyed:[accent] {0} +stat.built = Buildings Built:[accent] {0} +stat.destroyed = Buildings Destroyed:[accent] {0} +stat.deconstructed = Buildings Deconstructed:[accent] {0} +stat.delivered = Resources Launched: +stat.rank = Final Rank: [accent]{0} + +placeline = You have selected a block.\nYou can[accent] place in a line[] by[accent] holding down your finger for a few seconds[] and dragging in a direction.\n\n[scarlet]DO IT. +removearea = You have selected removal mode.\nYou can[accent] remove blocks in a rectangle[] by[accent] holding down your finger for a few seconds[] and dragging.\n\n[scarlet]DO IT. + +launcheditems = [accent]Launched Items +map.delete = Are you sure you want to delete the map "[accent]{0}[]"? +level.highscore = High Score: [accent]{0} +level.select = Level Select +level.mode = Gamemode: +showagain = Don't show again next session +coreattack = < Core is under attack! > +nearpoint = [[ [scarlet]LEAVE DROP POINT IMMEDIATELY[] ]\nannihilation imminent +database = Core Database +savegame = Save Game +loadgame = Load Game +joingame = Join Game +addplayers = Add/Remove Players +customgame = Custom Game +newgame = New Game +none = +minimap = Minimap +close = Close +quit = Quit +maps = Maps +continue = Continue +maps.none = [LIGHT_GRAY]No maps found! +about.button = About +name = Name: +noname = Pick a[accent] player name[] first. +filename = File Name: +unlocked = New content unlocked! +completed = [accent]Completed +techtree = Tech Tree +research.list = [LIGHT_GRAY]Research: +research = Research +researched = [LIGHT_GRAY]{0} researched. +players = {0} players online +players.single = {0} player online +server.closing = [accent]Closing server... +server.kicked.kick = You have been kicked from the server! +server.kicked.serverClose = Server closed. +server.kicked.clientOutdated = Outdated client! Update your game! +server.kicked.serverOutdated = Outdated server! Ask the host to update! +server.kicked.banned = You are banned on this server. +server.kicked.recentKick = You have been kicked recently.\nWait before connecting again. +server.kicked.nameInUse = There is someone with that name\nalready on this server. +server.kicked.nameEmpty = Your chosen name is invalid. +server.kicked.idInUse = You are already on this server! Connecting with two accounts is not permitted. +server.kicked.customClient = This server does not support custom builds. Download an official version. +server.kicked.gameover = Game over! +host.info = The [accent]host[] button hosts a server on port [scarlet]6567[]. \nAnybody on the same [LIGHT_GRAY]wifi or local network[] should be able to see your server in their server list.\n\nIf you want people to be able to connect from anywhere by IP, [accent]port forwarding[] is required.\n\n[LIGHT_GRAY]Note: If someone is experiencing trouble connecting to your LAN game, make sure you have allowed Mindustry access to your local network in your firewall settings. +join.info = Here, you can enter a [accent]server IP[] to connect to, or discover [accent]local network[] servers to connect to.\nBoth LAN and WAN multiplayer is supported.\n\n[LIGHT_GRAY]Note: There is no automatic global server list; if you want to connect to someone by IP, you would need to ask the host for their IP. +hostserver = Host Game +hostserver.mobile = Host\nGame +host = Host +hosting = [accent]Opening server... +hosts.refresh = Refresh +hosts.discovering = Discovering LAN games +server.refreshing = Refreshing server +hosts.none = [lightgray]No local games found! +host.invalid = [scarlet]Can't connect to host. +trace = Trace Player +trace.playername = Player name: [accent]{0} +trace.ip = IP: [accent]{0} +trace.id = Unique ID: [accent]{0} +trace.mobile = Mobile Client: [accent]{0} +trace.modclient = Custom Client: [accent]{0} +invalidid = Invalid client ID! Submit a bug report. +server.bans = Bans +server.bans.none = No banned players found! +server.admins = Admins +server.admins.none = No admins found! +server.add = Add Server +server.delete = Are you sure you want to delete this server? +server.hostname = Host: {0} +server.edit = Edit Server +server.outdated = [crimson]Outdated Server![] +server.outdated.client = [crimson]Outdated Client![] +server.version = [lightgray]Version: {0} {1} +server.custombuild = [yellow]Custom Build +confirmban = Are you sure you want to ban this player? +confirmkick = Are you sure you want to kick this player? +confirmunban = Are you sure you want to unban this player? +confirmadmin = Are you sure you want to make this player an admin? +confirmunadmin = Are you sure you want to remove admin status from this player? +joingame.title = Join Game +joingame.ip = Address: +disconnect = Disconnected. +disconnect.data = Failed to load world data! +connecting = [accent]Connecting... +connecting.data = [accent]Loading world data... +server.port = Port: +server.addressinuse = Address already in use! +server.invalidport = Invalid port number! +server.error = [crimson]Error hosting server: [accent]{0} +save.old = This save is for an older version of the game, and can no longer be used.\n\n[LIGHT_GRAY]Save backwards compatibility will be implemented in the full 4.0 release. +save.new = New Save +save.overwrite = Are you sure you want to overwrite\nthis save slot? +overwrite = Overwrite +save.none = No saves found! +saveload = [accent]Saving... +savefail = Failed to save game! +save.delete.confirm = Are you sure you want to delete this save? +save.delete = Delete +save.export = Export Save +save.import.invalid = [accent]This save is invalid! +save.import.fail = [crimson]Failed to import save: [accent]{0} +save.export.fail = [crimson]Failed to export save: [accent]{0} +save.import = Import Save +save.newslot = Save name: +save.rename = Rename +save.rename.text = New name: +selectslot = Select a save. +slot = [accent]Slot {0} +save.corrupted = [accent]Save file corrupted or invalid!\nIf you have just updated your game, this is probably a change in the save format and [scarlet]not[] a bug. +empty = +on = On +off = Off +save.autosave = Autosave: {0} +save.map = Map: {0} +save.wave = Wave {0} +save.difficulty = Difficulty: {0} +save.date = Last Saved: {0} +save.playtime = Playtime: {0} +warning = Warning. +confirm = Confirm +delete = Delete +ok = OK +open = Open +customize = Customize Rules +cancel = Cancel +openlink = Open Link +copylink = Copy Link +back = Back +quit.confirm = Are you sure you want to quit? +changelog.title = Changelog +changelog.loading = Getting changelog... +changelog.error.android = [accent]Note that the changelog sometimes does not work on Android 4.4 and below!\nThis is due to an internal Android bug. +changelog.error.ios = [accent]The changelog is currently not supported in iOS. +changelog.error = [scarlet]Error getting changelog!\nCheck your internet connection. +changelog.current = [yellow][[Current version] +changelog.latest = [accent][[Latest version] +loading = [accent]Loading... +saving = [accent]Saving... +wave = [accent]Wave {0} +wave.waiting = [LIGHT_GRAY]Wave in {0} +wave.waveInProgress = [LIGHT_GRAY]Wave in progress +waiting = [LIGHT_GRAY]Waiting... +waiting.players = Waiting for players... +wave.enemies = [LIGHT_GRAY]{0} Enemies Remaining +wave.enemy = [LIGHT_GRAY]{0} Enemy Remaining +loadimage = Load Image +saveimage = Save Image +unknown = Unknown +custom = Custom +builtin = Built-In +map.delete.confirm = Are you sure you want to delete this map? This action cannot be undone! +map.random = [accent]Random Map +map.nospawn = This map does not have any cores for the player to spawn in! Add a[ROYAL] blue[] core to this map in the editor. +map.nospawn.pvp = This map does not have any enemy cores for player to spawn into! Add[SCARLET] non-blue[] cores to this map in the editor. +map.nospawn.attack = This map does not have any enemy cores for player to attack! Add[SCARLET] red[] cores to this map in the editor. +map.invalid = Error loading map: corrupted or invalid map file. +editor.brush = Brush +editor.openin = Open In Editor +editor.oregen = Ore Generation +editor.oregen.info = Ore Generation: +editor.mapinfo = Map Info +editor.author = Author: +editor.description = Description: +editor.waves = Waves: +editor.rules = Rules: +editor.ingame = Edit In-Game +waves.title = Waves +waves.remove = Remove +waves.never = +waves.every = every +waves.waves = wave(s) +waves.perspawn = per spawn +waves.to = to +waves.boss = Boss +waves.preview = Preview +waves.edit = Edit... +waves.copy = Copy to Clipboard +waves.load = Load from Clipboard +waves.invalid = Invalid waves in clipboard. +waves.copied = Waves copied. +waves.none = No enemies defined.\nNote that empty wave layouts will automatically be replaced with the default layout. +editor.default = [LIGHT_GRAY] +edit = Edit... +editor.name = Name: +editor.spawn = Spawn Unit +editor.removeunit = Remove Unit +editor.teams = Teams +editor.elevation = Elevation +editor.errorload = Error loading file:\n[accent]{0} +editor.errorsave = Error saving file:\n[accent]{0} +editor.errorimage = That's an image, not a map. Don't go around changing extensions expecting it to work.\n\nIf you want to import a legacy map, use the 'import legacy map' button in the editor. +editor.errorlegacy = This map is too old, and uses a legacy map format that is no longer supported. +editor.errorheader = This map file is either not valid or corrupt. +editor.errorname = Map has no name defined. +editor.update = Update +editor.randomize = Randomize +editor.apply = Apply +editor.generate = Generate +editor.resize = Resize +editor.loadmap = Load Map +editor.savemap = Save Map +editor.saved = Saved! +editor.save.noname = Your map does not have a name! Set one in the 'map info' menu. +editor.save.overwrite = Your map overwrites a built-in map! Pick a different name in the 'map info' menu. +editor.import.exists = [scarlet]Unable to import:[] a built-in map named '{0}' already exists! +editor.import = Import... +editor.importmap = Import Map +editor.importmap.description = Import an already existing map +editor.importfile = Import File +editor.importfile.description = Import an external map file +editor.importimage = Import Legacy Image +editor.importimage.description = Import an external map image file +editor.export = Export... +editor.exportfile = Export File +editor.exportfile.description = Export a map file +editor.exportimage = Export Terrain Image +editor.exportimage.description = Export a map image file +editor.loadimage = Import Terrain +editor.saveimage = Export Terrain +editor.unsaved = [scarlet]You have unsaved changes![]\nAre you sure you want to exit? +editor.resizemap = Resize Map +editor.mapname = Map Name: +editor.overwrite = [accent]Warning!\nThis overwrites an existing map. +editor.overwrite.confirm = [scarlet]Warning![] A map with this name already exists. Are you sure you want to overwrite it? +editor.selectmap = Select a map to load: + +toolmode.replace = Replace +toolmode.replace.description = Draws only on solid blocks. +toolmode.replaceall = Replace All +toolmode.replaceall.description = Replace all blocks in map. +toolmode.orthogonal = Orthogonal +toolmode.orthogonal.description = Draws only orthogonal lines. +toolmode.square = Square +toolmode.square.description = Square brush. +toolmode.eraseores = Erase Ores +toolmode.eraseores.description = Erase only ores. +toolmode.fillteams = Fill Teams +toolmode.fillteams.description = Fill teams instead of blocks. +toolmode.drawteams = Draw Teams +toolmode.drawteams.description = Draw teams instead of blocks. + +filters.empty = [LIGHT_GRAY]No filters! Add one with the button below. +filter.distort = Distort +filter.noise = Noise +filter.ore = Ore +filter.rivernoise = River Noise +filter.scatter = Scatter +filter.terrain = Terrain +filter.option.scale = Scale +filter.option.chance = Chance +filter.option.mag = Magnitude +filter.option.threshold = Threshold +filter.option.circle-scale = Circle Scale +filter.option.octaves = Octaves +filter.option.falloff = Falloff +filter.option.block = Block +filter.option.floor = Floor +filter.option.flooronto = Target Floor +filter.option.wall = Wall +filter.option.ore = Ore +filter.option.floor2 = Secondary Floor +filter.option.threshold2 = Secondary Threshold +filter.option.radius = Radius +filter.option.percentile = Percentile + +width = Width: +height = Height: +menu = Menu +play = Play +load = Load +save = Save +fps = FPS: {0} +tps = TPS: {0} +ping = Ping: {0}ms +language.restart = Please restart your game for the language settings to take effect. +settings = Settings +tutorial = Tutorial +editor = Editor +mapeditor = Map Editor +donate = Donate + +abandon = Abandon +abandon.text = This zone and all its resources will be lost to the enemy. +locked = Locked +complete = [LIGHT_GRAY]Reach: +zone.requirement = Wave {0} in zone {1} +resume = Resume Zone:\n[LIGHT_GRAY]{0} +bestwave = [LIGHT_GRAY]Best Wave: {0} +launch = < LAUNCH > +launch.title = Launch Successful +launch.next = [LIGHT_GRAY]next opportunity at wave {0} +launch.unable = [scarlet]Unable to LAUNCH.[] {0} Enemies. +launch.confirm = This will launch all resources in your core.\nYou will not be able to return to this base. +uncover = Uncover +configure = Configure Loadout +configure.locked = [LIGHT_GRAY]Unlock configuring loadout: Wave {0}. +zone.unlocked = [LIGHT_GRAY]{0} unlocked. +zone.requirement.complete = Wave {0} reached:\n{1} zone requirements met. +zone.config.complete = Wave {0} reached:\nLoadout config unlocked. +zone.resources = Resources Detected: +add = Add... +boss.health = Boss Health + +connectfail = [crimson]Connection error:\n\n[accent]{0} +error.unreachable = Server unreachable.\nIs the address spelled correctly? +error.invalidaddress = Invalid address. +error.timedout = Timed out!\nMake sure the host has port forwarding set up, and that the address is correct! +error.mismatch = Packet error:\npossible client/server version mismatch.\nMake sure you and the host have the latest version of Mindustry! +error.alreadyconnected = Already connected. +error.mapnotfound = Map file not found! +error.io = Network I/O error. +error.any = Unknown network error. + +zone.groundZero.name = Ground Zero +zone.desertWastes.name = Desert Wastes +zone.craters.name = The Craters +zone.frozenForest.name = Frozen Forest +zone.ruinousShores.name = Ruinous Shores +zone.stainedMountains.name = Stained Mountains +zone.desolateRift.name = Desolate Rift +zone.nuclearComplex.name = Nuclear Production Complex +zone.overgrowth.name = Overgrowth +zone.tarFields.name = Tar Fields +zone.saltFlats.name = Salt Flats [scarlet][[WIP] + +settings.language = Language +settings.reset = Reset to Defaults +settings.rebind = Rebind +settings.controls = Controls +settings.game = Game +settings.sound = Sound +settings.graphics = Graphics +settings.cleardata = Clear Game Data... +settings.clear.confirm = Are you sure you want to clear this data?\nWhat is done cannot be undone! +settings.clearall.confirm = [scarlet]WARNING![]\nThis will clear all data, including saves, maps, unlocks and keybinds.\nOnce you press 'ok' the game will wipe all data and automatically exit. +settings.clearunlocks = Clear Unlocks +settings.clearall = Clear All +paused = [accent]< Paused > +yes = Yes +no = No +info.title = Info +error.title = [crimson]An error has occured +error.crashtitle = An error has occured +blocks.input = Input +blocks.output = Output +blocks.booster = Booster +block.unknown = [LIGHT_GRAY]??? +blocks.powercapacity = Power Capacity +blocks.powershot = Power/Shot +blocks.targetsair = Targets Air +blocks.targetsground = Targets Ground +blocks.itemsmoved = Move Speed +blocks.launchtime = Time Between Launches +blocks.shootrange = Range +blocks.size = Size +blocks.liquidcapacity = Liquid Capacity +blocks.powerrange = Power Range +blocks.poweruse = Power Use +blocks.powerdamage = Power/Damage +blocks.itemcapacity = Item Capacity +blocks.basepowergeneration = Base Power Generation +blocks.productiontime = Production Time +blocks.repairtime = Block Full Repair Time +blocks.speedincrease = Speed Increase +blocks.range = Range +blocks.drilltier = Drillables +blocks.drillspeed = Base Drill Speed +blocks.boosteffect = Boost Effect +blocks.maxunits = Max Active Units +blocks.health = Health +blocks.buildtime = Build Time +blocks.inaccuracy = Inaccuracy +blocks.shots = Shots +blocks.reload = Shots/Second +blocks.ammo = Ammo + +bar.drillspeed = Drill Speed: {0}/s +bar.efficiency = Efficiency: {0}% +bar.powerbalance = Power: {0}/s +bar.poweramount = Power: {0} +bar.poweroutput = Power Output: {0} +bar.items = Items: {0} +bar.liquid = Liquid +bar.heat = Heat +bar.power = Power +bar.progress = Build Progress +bar.spawned = Units: {0}/{1} + +bullet.damage = [stat]{0}[lightgray] damage +bullet.splashdamage = [stat]{0}[lightgray] area dmg ~[stat] {1}[lightgray] tiles +bullet.incendiary = [stat]incendiary +bullet.homing = [stat]homing +bullet.shock = [stat]shock +bullet.frag = [stat]frag +bullet.knockback = [stat]{0}[lightgray] knockback +bullet.freezing = [stat]freezing +bullet.tarred = [stat]tarred +bullet.multiplier = [stat]{0}[lightgray]x ammo multiplier +bullet.reload = [stat]{0}[lightgray]x fire rate + +unit.blocks = blocks +unit.powersecond = power units/second +unit.liquidsecond = liquid units/second +unit.itemssecond = items/second +unit.liquidunits = liquid units +unit.powerunits = power units +unit.degrees = degrees +unit.seconds = seconds +unit.persecond = /sec +unit.timesspeed = x speed +unit.percent = % +unit.items = items +category.general = General +category.power = Power +category.liquids = Liquids +category.items = Items +category.crafting = Input/Output +category.shooting = Shooting +category.optional = Optional Enhancements +setting.landscape.name = Lock Landscape +setting.shadows.name = Shadows +setting.linear.name = Linear Filtering +setting.animatedwater.name = Animated Water +setting.animatedshields.name = Animated Shields +setting.antialias.name = Antialias[LIGHT_GRAY] (requires restart)[] +setting.indicators.name = Enemy/Ally Indicators +setting.autotarget.name = Auto-Target +setting.fpscap.name = Max FPS +setting.fpscap.none = None +setting.fpscap.text = {0} FPS +setting.swapdiagonal.name = Always Diagonal Placement +setting.difficulty.training = Training +setting.difficulty.easy = Easy +setting.difficulty.normal = Normal +setting.difficulty.hard = Hard +setting.difficulty.insane = Insane +setting.difficulty.name = Difficulty: +setting.screenshake.name = Screen Shake +setting.effects.name = Display Effects +setting.sensitivity.name = Controller Sensitivity +setting.saveinterval.name = Save Interval +setting.seconds = {0} Seconds +setting.fullscreen.name = Fullscreen +setting.borderlesswindow.name = Borderless Window[LIGHT_GRAY] (may require restart) +setting.fps.name = Show FPS +setting.vsync.name = VSync +setting.lasers.name = Show Power Lasers +setting.pixelate.name = Pixelate[LIGHT_GRAY] (disables animations) +setting.minimap.name = Show Minimap +setting.musicvol.name = Music Volume +setting.mutemusic.name = Mute Music +setting.sfxvol.name = SFX Volume +setting.mutesound.name = Mute Sound +setting.crashreport.name = Send Anonymous Crash Reports +setting.chatopacity.name = Chat Opacity +setting.playerchat.name = Display In-Game Chat +keybind.title = Rebind Keys +category.general.name = General +category.view.name = View +category.multiplayer.name = Multiplayer +command.attack = Attack +command.retreat = Retreat +command.patrol = Patrol +keybind.gridMode.name = Block Select +keybind.gridModeShift.name = Category Select +keybind.press = Press a key... +keybind.press.axis = Press an axis or key... +keybind.screenshot.name = Map Screenshot +keybind.move_x.name = Move x +keybind.move_y.name = Move y +keybind.select.name = Select/Shoot +keybind.diagonal_placement.name = Diagonal Placement +keybind.pick.name = Pick Block +keybind.break_block.name = Break Block +keybind.deselect.name = Deselect +keybind.shoot.name = Shoot +keybind.zoom_hold.name = Zoom Hold +keybind.zoom.name = Zoom +keybind.menu.name = Menu +keybind.pause.name = Pause +keybind.minimap.name = Minimap +keybind.dash.name = Dash +keybind.chat.name = Chat +keybind.player_list.name = Player list +keybind.console.name = Console +keybind.rotate.name = Rotate +keybind.toggle_menus.name = Toggle menus +keybind.chat_history_prev.name = Chat history prev +keybind.chat_history_next.name = Chat history next +keybind.chat_scroll.name = Chat scroll +keybind.drop_unit.name = Drop Unit +keybind.zoom_minimap.name = Zoom minimap +mode.help.title = Description of modes +mode.survival.name = Survival +mode.survival.description = The normal mode. Limited resources and automatic incoming waves. +mode.sandbox.name = Sandbox +mode.sandbox.description = Infinite resources and no timer for waves. +mode.pvp.name = PvP +mode.pvp.description = Fight against other players locally. Requires at least 2 differently-colored cores in the map to play. +mode.attack.name = Attack +mode.attack.description = Destroy the enemy's base. No waves. Requires a red core in the map to play. +mode.custom = Custom Rules + +rules.infiniteresources = Infinite Resources +rules.wavetimer = Wave Timer +rules.waves = Waves +rules.attack = Attack Mode +rules.enemyCheat = Infinite AI (Red Team) Resources +rules.unitdrops = Unit Drops +rules.unitbuildspeedmultiplier = Unit Production Speed Multiplier +rules.unithealthmultiplier = Unit Health Multiplier +rules.playerhealthmultiplier = Player Health Multiplier +rules.playerdamagemultiplier = Player Damage Multiplier +rules.unitdamagemultiplier = Unit Damage Multiplier +rules.enemycorebuildradius = Enemy Core No-Build Radius:[LIGHT_GRAY] (tiles) +rules.respawntime = Respawn Time:[LIGHT_GRAY] (sec) +rules.wavespacing = Wave Spacing:[LIGHT_GRAY] (sec) +rules.buildcostmultiplier = Build Cost Multiplier +rules.buildspeedmultiplier = Build Speed Multiplier +rules.waitForWaveToEnd = Waves wait for enemies +rules.dropzoneradius = Drop Zone Radius:[LIGHT_GRAY] (tiles) +rules.respawns = Max respawns per wave +rules.limitedRespawns = Limit Respawns +rules.title.waves = Waves +rules.title.respawns = Respawns +rules.title.resourcesbuilding = Resources & Building +rules.title.player = Players +rules.title.enemy = Enemies +rules.title.unit = Units + +content.item.name = Items +content.liquid.name = Liquids +content.unit.name = Units +content.block.name = Blocks +content.mech.name = Mechs +item.copper.name = Copper +item.copper.description = A useful structure material. Used extensively in all types of blocks. +item.lead.name = Lead +item.lead.description = A basic starter material. Used extensively in electronics and liquid transportation blocks. +item.coal.name = Coal +item.coal.description = A common and readily available fuel. +item.graphite.name = Graphite +item.graphite.description = Mineralized carbon, used for ammunition and electrical insulation. +item.titanium.name = Titanium +item.titanium.description = A rare super-light metal used extensively in liquid transportation, drills and aircraft. +item.thorium.name = Thorium +item.thorium.description = A dense, radioactive metal used as structural support and nuclear fuel. +item.silicon.name = Silicon +item.silicon.description = An extremely useful semiconductor, with applications in solar panels and many complex electronics. +item.plastanium.name = Plastanium +item.plastanium.description = A light, ductile material used in advanced aircraft and fragmentation ammunition. +item.phase-fabric.name = Phase Fabric +item.phase-fabric.description = A near-weightless substance used in advanced electronics and self-repairing technology. +item.surge-alloy.name = Surge Alloy +item.surge-alloy.description = An advanced alloy with unique electrical properties. +item.spore-pod.name = Spore Pod +item.spore-pod.description = Used for conversion into oil, explosives and fuel. +item.sand.name = Sand +item.sand.description = A common material that is used extensively in smelting, both in alloying and as a flux. +item.blast-compound.name = Blast Compound +item.blast-compound.description = A volatile compound used in bombs and explosives. While it can burned as fuel, this is not advised. +item.pyratite.name = Pyratite +item.pyratite.description = An extremely flammable substance used in incendiary weapons. +item.metaglass.name = Metaglass +item.metaglass.description = A super-tough glass compound. Extensively used for liquid distribution and storage. +item.scrap.name = Scrap +item.scrap.description = Leftover remnants of old structures and units. Contains trace amounts of many different metals. +liquid.water.name = Water +liquid.slag.name = Slag +liquid.oil.name = Oil +liquid.cryofluid.name = Cryofluid +mech.alpha-mech.name = Alpha +mech.alpha-mech.weapon = Heavy Repeater +mech.alpha-mech.ability = Regeneration +mech.alpha-mech.description = The standard mech. Has decent speed and damage output. +mech.delta-mech.name = Delta +mech.delta-mech.weapon = Arc Generator +mech.delta-mech.ability = Discharge +mech.delta-mech.description = A fast, lightly-armored mech made for hit-and-run attacks. Does little damage against structures, but can kill large groups of enemy units very quickly with its arc lightning weapons. +mech.tau-mech.name = Tau +mech.tau-mech.weapon = Restruct Laser +mech.tau-mech.ability = Repair Burst +mech.tau-mech.description = The support mech. Heals allied blocks by shooting at them. Can heal allies in a radius with its repair ability. +mech.omega-mech.name = Omega +mech.omega-mech.weapon = Swarm Missiles +mech.omega-mech.ability = Armored Configuration +mech.omega-mech.description = A bulky and well-armored mech, made for front-line assaults. Its armor ability can block up to 90% of incoming damage. +mech.dart-ship.name = Dart +mech.dart-ship.weapon = Repeater +mech.dart-ship.description = The standard ship. Reasonably fast and light, but has little offensive capability and low mining speed. +mech.javelin-ship.name = Javelin +mech.javelin-ship.description = A hit-and-run strike ship. While initially slow, it can accelerate to great speeds and fly by enemy outposts, dealing large amounts of damage with its lightning ability and missiles. +mech.javelin-ship.weapon = Burst Missiles +mech.javelin-ship.ability = Discharge Booster +mech.trident-ship.name = Trident +mech.trident-ship.description = A heavy bomber. Reasonably well armored. +mech.trident-ship.weapon = Bomb Bay +mech.glaive-ship.name = Glaive +mech.glaive-ship.description = A large, well-armored gunship. Equipped with an incendiary repeater. Good acceleration and maximum speed. +mech.glaive-ship.weapon = Flame Repeater +item.explosiveness = [LIGHT_GRAY]Explosiveness: {0}% +item.flammability = [LIGHT_GRAY]Flammability: {0}% +item.radioactivity = [LIGHT_GRAY]Radioactivity: {0}% +unit.health = [LIGHT_GRAY]Health: {0} +unit.speed = [LIGHT_GRAY]Speed: {0} +mech.weapon = [LIGHT_GRAY]Weapon: {0} +mech.health = [LIGHT_GRAY]Health: {0} +mech.itemcapacity = [LIGHT_GRAY]Item Capacity: {0} +mech.minespeed = [LIGHT_GRAY]Mining Speed: {0}% +mech.minepower = [LIGHT_GRAY]Mining Power: {0} +mech.ability = [LIGHT_GRAY]Ability: {0} +mech.buildspeed = [LIGHT_GRAY]Building Speed: {0}% +liquid.heatcapacity = [LIGHT_GRAY]Heat Capacity: {0} +liquid.viscosity = [LIGHT_GRAY]Viscosity: {0} +liquid.temperature = [LIGHT_GRAY]Temperature: {0} + +block.grass.name = Grass +block.salt.name = Salt +block.saltrocks.name = Salt Rocks +block.pebbles.name = Pebbles +block.tendrils.name = Tendrils +block.sandrocks.name = Sand Rocks +block.spore-pine.name = Spore Pine +block.sporerocks.name = Spore Rocks +block.rock.name = Rock +block.snowrock.name = Snow Rock +block.shale.name = Shale +block.shale-boulder.name = Shale Boulder +block.moss.name = Moss +block.shrubs.name = Shrubs +block.spore-moss.name = Spore Moss +block.shalerocks.name = Shale Rocks +block.scrap-wall.name = Scrap Wall +block.scrap-wall-large.name = Large Scrap Wall +block.scrap-wall-huge.name = Huge Scrap Wall +block.scrap-wall-gigantic.name = Gigantic Scrap Wall +block.thruster.name = Thruster +block.kiln.name = Kiln +block.kiln.description = Smelts sand and lead into metaglass. Requires small amounts of power. +block.graphite-press.name = Graphite Press +block.multi-press.name = Multi-Press +block.constructing = {0} [LIGHT_GRAY](Constructing) +block.spawn.name = Enemy Spawn +block.core-shard.name = Core: Shard +block.core-foundation.name = Core: Foundation +block.core-nucleus.name = Core: Nucleus +block.deepwater.name = Deep Water +block.water.name = Water +block.tainted-water.name = Tainted Water +block.darksand-tainted-water.name = Dark Sand Tainted Water +block.tar.name = Tar +block.stone.name = Stone +block.sand.name = Sand +block.darksand.name = Dark Sand +block.ice.name = Ice +block.snow.name = Snow +block.craters.name = Craters +block.sand-water.name = Sand water +block.darksand-water.name = Dark Sand Water +block.char.name = Char +block.holostone.name = Holo stone +block.ice-snow.name = Ice Snow +block.rocks.name = Rocks +block.icerocks.name = Ice rocks +block.snowrocks.name = Snow Rocks +block.dunerocks.name = Dune Rocks +block.pine.name = Pine +block.white-tree-dead.name = White Tree Dead +block.white-tree.name = White Tree +block.spore-cluster.name = Spore Cluster +block.metal-floor.name = Metal Floor 1 +block.metal-floor-2.name = Metal Floor 2 +block.metal-floor-3.name = Metal Floor 3 +block.metal-floor-5.name = Metal Floor 4 +block.metal-floor-damaged.name = Metal Floor Damaged +block.dark-panel-1.name = Dark Panel 1 +block.dark-panel-2.name = Dark Panel 2 +block.dark-panel-3.name = Dark Panel 3 +block.dark-panel-4.name = Dark Panel 4 +block.dark-panel-5.name = Dark Panel 5 +block.dark-panel-6.name = Dark Panel 6 +block.dark-metal.name = Dark Metal +block.ignarock.name = Igna Rock +block.hotrock.name = Hot Rock +block.magmarock.name = Magma Rock +block.cliffs.name = Cliffs +block.copper-wall.name = Copper Wall +block.copper-wall-large.name = Large Copper Wall +block.titanium-wall.name = Titanium Wall +block.titanium-wall-large.name = Large Titanium Wall +block.phase-wall.name = Phase Wall +block.phase-wall-large.name = Large Phase Wall +block.thorium-wall.name = Thorium Wall +block.thorium-wall-large.name = Large Thorium Wall +block.door.name = Door +block.door-large.name = Large Door +block.duo.name = Duo +block.scorch.name = Scorch +block.scatter.name = Scatter +block.hail.name = Hail +block.lancer.name = Lancer +block.conveyor.name = Conveyor +block.titanium-conveyor.name = Titanium Conveyor +block.junction.name = Junction +block.router.name = Router +block.distributor.name = Distributor +block.sorter.name = Sorter +block.sorter.description = Sorts items. If an item matches the selection, it is allowed to pass. Otherwise, the item is outputted to the left and right. +block.overflow-gate.name = Overflow Gate +block.overflow-gate.description = A combination splitter and router that only outputs to the left and right if the front path is blocked. +block.silicon-smelter.name = Silicon Smelter +block.phase-weaver.name = Phase Weaver +block.pulverizer.name = Pulverizer +block.cryofluidmixer.name = Cryofluid Mixer +block.melter.name = Melter +block.incinerator.name = Incinerator +block.spore-press.name = Spore Press +block.separator.name = Separator +block.coal-centrifuge.name = Coal Centrifuge +block.power-node.name = Power Node +block.power-node-large.name = Large Power Node +block.surge-tower.name = Surge Tower +block.battery.name = Battery +block.battery-large.name = Large Battery +block.combustion-generator.name = Combustion Generator +block.turbine-generator.name = Turbine Generator +block.differential-generator.name = Differential Generator +block.impact-reactor.name = Impact Reactor +block.mechanical-drill.name = Mechanical Drill +block.pneumatic-drill.name = Pneumatic Drill +block.laser-drill.name = Laser Drill +block.water-extractor.name = Water Extractor +block.cultivator.name = Cultivator +block.dart-mech-pad.name = Alpha Mech Pad +block.delta-mech-pad.name = Delta Mech Pad +block.javelin-ship-pad.name = Javelin Ship Pad +block.trident-ship-pad.name = Trident Ship Pad +block.glaive-ship-pad.name = Glaive Ship Pad +block.omega-mech-pad.name = Omega Mech Pad +block.tau-mech-pad.name = Tau Mech Pad +block.conduit.name = Conduit +block.mechanical-pump.name = Mechanical Pump +block.item-source.name = Item Source +block.item-void.name = Item Void +block.liquid-source.name = Liquid Source +block.power-void.name = Power Void +block.power-source.name = Power Infinite +block.unloader.name = Unloader +block.vault.name = Vault +block.wave.name = Wave +block.swarmer.name = Swarmer +block.salvo.name = Salvo +block.ripple.name = Ripple +block.phase-conveyor.name = Phase Conveyor +block.bridge-conveyor.name = Bridge Conveyor +block.plastanium-compressor.name = Plastanium Compressor +block.pyratite-mixer.name = Pyratite Mixer +block.blast-mixer.name = Blast Mixer +block.solar-panel.name = Solar Panel +block.solar-panel-large.name = Large Solar Panel +block.oil-extractor.name = Oil Extractor +block.draug-factory.name = Draug Miner Drone Factory +block.spirit-factory.name = Spirit Repair Drone Factory +block.phantom-factory.name = Phantom Builder Drone Factory +block.wraith-factory.name = Wraith Fighter Factory +block.ghoul-factory.name = Ghoul Bomber Factory +block.dagger-factory.name = Dagger Mech Factory +block.crawler-factory.name = Crawler Mech Factory +block.titan-factory.name = Titan Mech Factory +block.fortress-factory.name = Fortress Mech Factory +block.revenant-factory.name = Revenant Fighter Factory +block.repair-point.name = Repair Point +block.pulse-conduit.name = Pulse Conduit +block.phase-conduit.name = Phase Conduit +block.liquid-router.name = Liquid Router +block.liquid-tank.name = Liquid Tank +block.liquid-junction.name = Liquid Junction +block.bridge-conduit.name = Bridge Conduit +block.rotary-pump.name = Rotary Pump +block.thorium-reactor.name = Thorium Reactor +block.mass-driver.name = Mass Driver +block.blast-drill.name = Airblast Drill +block.thermal-pump.name = Thermal Pump +block.thermal-generator.name = Thermal Generator +block.alloy-smelter.name = Alloy Smelter +block.mender.name = Mender +block.mend-projector.name = Mend Projector +block.surge-wall.name = Surge Wall +block.surge-wall-large.name = Large Surge Wall +block.cyclone.name = Cyclone +block.fuse.name = Fuse +block.shock-mine.name = Shock Mine +block.overdrive-projector.name = Overdrive Projector +block.force-projector.name = Force Projector +block.arc.name = Arc +block.rtg-generator.name = RTG Generator +block.spectre.name = Spectre +block.meltdown.name = Meltdown +block.container.name = Container +block.launch-pad.name = Launch Pad +block.launch-pad.description = Launches batches of items without any need for a core launch. +block.launch-pad-large.name = Large Launch Pad +team.blue.name = blue +team.red.name = red +team.orange.name = orange +team.none.name = gray +team.green.name = green +team.purple.name = purple +unit.spirit.name = Spirit Repair Drone +unit.draug.name = Draug Miner Drone +unit.spirit.description = Automatically repairs blocks. +unit.phantom.name = Phantom Builder Drone +unit.phantom.description = An advanced drone unit. Helps players build blocks +unit.dagger.name = Dagger +unit.dagger.description = A basic ground unit. Useful in swarms. +unit.crawler.name = Crawler +unit.titan.name = Titan +unit.titan.description = An advanced, armored ground unit. Attacks both ground and air targets. +unit.ghoul.name = Ghoul Bomber +unit.ghoul.description = A heavy carpet bomber. +unit.wraith.name = Wraith Fighter +unit.wraith.description = A fast, hit-and-run interceptor unit. +unit.fortress.name = Fortress +unit.fortress.description = A heavy artillery ground unit. +unit.revenant.name = Revenant +unit.eruptor.name = Eruptor +unit.chaos-array.name = Chaos Array +unit.eradicator.name = Eradicator +unit.lich.name = Lich +unit.reaper.name = Reaper +tutorial.begin = Your mission here is to eradicate the[LIGHT_GRAY] enemy[].\n\nBegin by[accent] mining copper[]. Tap a copper ore vein near your core to do this. +tutorial.drill = Mining manually is inefficient.\n[accent]Drills []can mine automatically.\nPlace one on a copper vein. +tutorial.conveyor = [accent]Conveyors[] are used to transport items to the core.\nMake a line of conveyors from the drill to the core. +tutorial.morecopper = More copper is required.\n\nEither mine it manually, or place more drills. +tutorial.turret = Defensive structures must be built to repel the[LIGHT_GRAY] enemy[].\nBuild a duo turret near your base. +tutorial.drillturret = Duo turrets require[accent] copper ammo []to shoot.\nPlace a drill next to the turret to supply it with mined copper. +tutorial.waves = The[LIGHT_GRAY] enemy[] approaches.\n\nDefend your core for 2 waves. Build more turrets. +tutorial.lead = More ores are available. Explore and mine[accent] lead[].\n\nDrag from your unit to the core to transfer resources. +tutorial.smelter = Copper and lead are weak metals.\nSuperior[accent] Dense Alloy[] can be created in a smelter.\n\nBuild one. +tutorial.densealloy = The smelter will now produce alloy.\nGet some.\nImprove the production if necessary. +tutorial.siliconsmelter = The core will now create a[accent] spirit drone[] for mining and repairing blocks.\n\nFactories for other units can be created with [accent] silicon.\nMake a silicon smelter. +tutorial.silicondrill = Silicon requires[accent] coal[] and[accent] sand[].\nStart by making drills. +tutorial.generator = This technology requires power.\nCreate a[accent] combustion generator[] for it. +tutorial.generatordrill = Combustion generators need fuel.\nFuel it with coal from a drill. +tutorial.node = Power requires transport.\nCreate a[accent] power node[] next to your combustion generator to transfer its power. +tutorial.nodelink = Power can be transferred through contacting power blocks and generators, or by linked power nodes.\n\nLink power by tapping the node and selecting the generator and silicon smelter. +tutorial.silicon = Silicon is being produced. Get some.\n\nImproving the production system is advised. +tutorial.daggerfactory = Construct a[accent] dagger mech factory.[]\n\nThis will be used to create attack mechs. +tutorial.router = Factories need resources to function.\nCreate a router to split conveyor resources. +tutorial.dagger = Link power nodes to the factory.\nOnce requirements are met, a mech will be created.\n\nCreate more drills, generators and conveyors as necessary. +tutorial.battle = The[LIGHT_GRAY] enemy[] has revealed their core.\nDestroy it with your unit and dagger mechs. +block.copper-wall.description = A cheap defensive block.\nUseful for protecting the core and turrets in the first few waves. +block.copper-wall-large.description = A cheap defensive block.\nUseful for protecting the core and turrets in the first few waves.\nSpans multiple tiles. +block.thorium-wall.description = A strong defensive block.\nGood protection from enemies. +block.thorium-wall-large.description = A strong defensive block.\nGood protection from enemies.\nSpans multiple tiles. +block.phase-wall.description = Not as strong as a thorium wall but will deflect bullets unless they are too powerful. +block.phase-wall-large.description = Not as strong as a thorium wall but will deflect bullets unless they are too powerful.\nSpans multiple tiles. +block.surge-wall.description = The strongest defensive block.\nHas a small chance of triggering lightning towards the attacker. +block.surge-wall-large.description = The strongest defensive block.\nHas a small chance of triggering lightning towards the attacker.\nSpans multiple tiles. +block.door.description = A small door that can be opened and closed by tapping on it.\nIf opened, enemies can shoot and move through. +block.door-large.description = A large door that can be opened and closed by tapping on it.\nIf opened, enemies can shoot and move through.\nSpans multiple tiles. +block.mend-projector.description = Periodically heals blocks in its vicinity. +block.overdrive-projector.description = Increases the speed of nearby buildings like drills and conveyors. +block.force-projector.description = Creates a hexagonal force field around itself, protecting buildings and units inside from damage through bullets. +block.shock-mine.description = Damages enemies stepping on the mine. Nearly invisible to the enemy. +block.duo.description = A small, cheap turret. Useful against ground units. +block.scatter.description = A medium-sized anti-air turret. Sprays clumps of lead or scrap flak at enemy units. +block.arc.description = A small close-range turret which shoots electricity in a random arc towards the enemy. +block.hail.description = A small artillery turret. +block.lancer.description = A medium-sized turret which shoots charged electricity beams. +block.wave.description = A medium-sized rapid-fire turret which shoots liquid bubbles. +block.salvo.description = A medium-sized turret which fires shots in salvos. +block.swarmer.description = A medium-sized turret which shoots burst missiles. +block.ripple.description = A large artillery turret which fires several shots simultaneously. +block.cyclone.description = A large rapid fire turret. +block.fuse.description = A large turret which shoots powerful short-range beams. +block.spectre.description = A large turret which shoots two powerful bullets at once. +block.meltdown.description = A large turret which shoots powerful long-range beams. +block.conveyor.description = Basic item transport block. Moves items forward and automatically deposits them into turrets or crafters. Rotatable. +block.titanium-conveyor.description = Advanced item transport block. Moves items faster than standard conveyors. +block.phase-conveyor.description = Advanced item transport block. Uses power to teleport items to a connected phase conveyor over several tiles. +block.junction.description = Acts as a bridge for two crossing conveyor belts. Useful in situations with two different conveyors carrying different materials to different locations. +block.mass-driver.description = Ultimate item transport block. Collects several items and then shoots them to another mass driver over a long range. +block.silicon-smelter.description = Reduces sand with highly pure coal in order to produce silicon. +block.plastanium-compressor.description = Produces plastanium from oil and titanium. +block.phase-weaver.description = Produces phase fabric from radioactive thorium and high amounts of sand. +block.alloy-smelter.description = Produces surge alloy from titanium, lead, silicon and copper. +block.pulverizer.description = Crushes scrap into sand. Useful when there is a lack of natural sand. +block.pyratite-mixer.description = Mixes coal, lead and sand into highly flammable pyratite. +block.blast-mixer.description = Uses oil for transforming pyratite into the less flammable but more explosive blast compound. +block.cryofluidmixer.description = Combines water and titanium into cryofluid which is much more efficient for cooling. +block.melter.description = Melts down scrap into slag for further processing or usage in turrets. +block.incinerator.description = Gets rid of any excess item or liquid. +block.spore-press.description = Compresses spore pods into oil. +block.separator.description = Extracts useful minerals from slag. +block.power-node.description = Transmits power to connected nodes. Up to four power sources, sinks or nodes can be connected. The node will receive power from or supply power to any adjacent blocks. +block.power-node-large.description = Has a larger radius than the power node and connects to up to six power sources, sinks or nodes. +block.battery.description = Stores power whenever there is an abundance and provides power whenever there is a shortage, as long as there is capacity left. +block.battery-large.description = Stores much more power than a regular battery. +block.combustion-generator.description = Generates power by burning oil or flammable materials. +block.turbine-generator.description = More efficient than a combustion generator, but requires additional water. +block.thermal-generator.description = Generates power when placed in hot locations. +block.solar-panel.description = Provides a small amount of power from the sun. +block.solar-panel-large.description = Provides much better power supply than a standard solar panel, but is also much more expensive to build. +block.thorium-reactor.description = Generates huge amounts of power from highly radioactive thorium. Requires constant cooling. Will explode violently if insufficient amounts of coolant are supplied. Power output depends on fullness, with base power generated at full capacity. +block.rtg-generator.description = A radioisotope thermoelectric generator which does not require cooling but provides less power than a thorium reactor. +block.unloader.description = Unloads items from a container, vault or core onto a conveyor or directly into an adjacent block. The type of item to be unloaded can be changed by tapping on the unloader. +block.container.description = Stores a small amount of items of each type. An[LIGHT_GRAY] unloader[] can be used to retrieve items from the container. +block.vault.description = Stores a large amount of items of each type. An[LIGHT_GRAY] unloader[] can be used to retrieve items from the vault. +block.mechanical-drill.description = A cheap drill. When placed on appropriate tiles, outputs items at a slow pace indefinitely. +block.pneumatic-drill.description = An improved drill which is faster and able to process harder materials by making use of air pressure. +block.laser-drill.description = Allows drilling even faster through laser technology, but requires power. Additionally, radioactive thorium can be retrieved with this drill. +block.blast-drill.description = The ultimate drill. Requires large amounts of power. +block.water-extractor.description = Extracts water from the ground. Use it when there is no lake nearby. +block.cultivator.description = Cultivates tiny concentrations of spores into industry-ready pods. +block.oil-extractor.description = Uses large amounts of power in order to extract oil from sand. Use it when there is no direct source of oil nearby. +block.trident-ship-pad.description = Leave your current vessel and change into a reasonably well armored heavy bomber.\nUse the pad by double tapping while standing on it. +block.javelin-ship-pad.description = Leave your current vessel and change into a strong and fast interceptor with lightning weapons.\nUse the pad by double tapping while standing on it. +block.glaive-ship-pad.description = Leave your current vessel and change into a large, well-armored gunship.\nUse the pad by double tapping while standing on it. +block.tau-mech-pad.description = Leave your current vessel and change into a support mech which can heal friendly buildings and units.\nUse the pad by double tapping while standing on it. +block.delta-mech-pad.description = Leave your current vessel and change into a fast, lightly-armored mech made for hit-and-run attacks.\nUse the pad by double tapping while standing on it. +block.omega-mech-pad.description = Leave your current vessel and change into a bulky and well-armored mech, made for front-line assaults.\nUse the pad by double tapping while standing on it. +block.spirit-factory.description = Produces light drones which mine ore and repair blocks. +block.phantom-factory.description = Produces advanced drone units which are significantly more effective than a spirit drone. +block.wraith-factory.description = Produces fast, hit-and-run interceptor units. +block.ghoul-factory.description = Produces heavy carpet bombers. +block.dagger-factory.description = Produces basic ground units. +block.titan-factory.description = Produces advanced, armored ground units. + +block.fortress-factory.description = Produces heavy artillery ground units. +block.revenant-factory.description = Produces heavy laser air units. +block.repair-point.description = Continuously heals the closest damaged unit in its vicinity. +block.conduit.description = Basic liquid transport block. Works like a conveyor, but with liquids. Best used with extractors, pumps or other conduits. +block.pulse-conduit.description = Advanced liquid transport block. Transports liquids faster and stores more than standard conduits. +block.phase-conduit.description = Advanced liquid transport block. Uses power to teleport liquids to a connected phase conduit over several tiles. +block.liquid-router.description = Accepts liquids from one direction and outputs them to up to 3 other directions equally. Can also store a certain amount of liquid. Useful for splitting the liquids from one source to multiple targets. +block.liquid-tank.description = Stores a large amount of liquids. Use it for creating buffers when there is a non-constant demand of materials or as a safeguard for cooling vital blocks. +block.liquid-junction.description = Acts as a bridge for two crossing conduits. Useful in situations with two different conduits carrying different liquids to different locations. +block.bridge-conduit.description = Advanced liquid transport block. Allows transporting liquids over up to 3 tiles of any terrain or building. +block.mechanical-pump.description = A cheap pump with slow output, but no power consumption. +block.rotary-pump.description = An advanced pump which doubles up speed by using power. +block.thermal-pump.description = The ultimate pump. +block.router.description = Accepts items from one direction and outputs them to up to 3 other directions equally. Useful for splitting the materials from one source to multiple targets. +block.distributor.description = An advanced router which splits items to up to 7 other directions equally. +block.bridge-conveyor.description = Advanced item transport block. Allows transporting items over up to 3 tiles of any terrain or building. +block.item-source.description = Infinitely outputs items. Sandbox only. +block.liquid-source.description = Infinitely outputs liquids. Sandbox only. +block.item-void.description = Destroys any items which go into it without using power. Sandbox only. +block.power-source.description = Infinitely outputs power. Sandbox only. +block.power-void.description = Voids all power inputted into it. Sandbox only. +liquid.water.description = Commonly used for cooling machines and waste processing. +liquid.oil.description = Can be burnt, exploded or used as a coolant. +liquid.cryofluid.description = The most efficient liquid for cooling things down. diff --git a/core/assets/bundles/bundle_cs.properties b/core/assets/bundles/bundle_cs.properties new file mode 100644 index 0000000000..b40685d38e --- /dev/null +++ b/core/assets/bundles/bundle_cs.properties @@ -0,0 +1,947 @@ +credits.text = Vytvořil [ROYAL]Anuken[] - [SKY]anukendev@gmail.com[] +credits = Kredity +contributors = Překladatelé a Sponzoři +discord = Připoj se k Mindustry na Discordu! +link.discord.description = Oficiální Mindustry chatroom na Discordu! +link.github.description = Zdrojový kód hry +link.dev-builds.description = Nestabilní verze vývoje hry +link.trello.description = Oficiální Trello board pro plánované funkce +link.itch.io.description = itch.io stránka pro stažení PC nebo webové verze +link.google-play.description = Google Play store +link.wiki.description = Oficiální Mindustry wiki +linkfail = Nepodařilo se otevřít odkaz!\nURL byla zkopírována do schránky. +screenshot = Snímek obrazovky uložen {0} +screenshot.invalid = Map too large, potentially not enough memory for screenshot. +gameover = Konec hry +gameover.pvp = [accent] {0}[] Tým Vyhrál! +highscore = [accent]Nový Rekord! +stat.wave = Waves Defeated:[accent] {0} +stat.enemiesDestroyed = Enemies Destroyed:[accent] {0} +stat.built = Buildings Built:[accent] {0} +stat.destroyed = Buildings Destroyed:[accent] {0} +stat.deconstructed = Buildings Deconstructed:[accent] {0} +stat.delivered = Resources Launched: +stat.rank = Final Rank: [accent]{0} +placeline = You have selected a block.\nYou can[accent] place in a line[] by[accent] holding down your finger for a few seconds[] and dragging in a direction.\nTry it. +removearea = You have selected removal mode.\nYou can[accent] remove blocks in a rectangle[] by[accent] holding down your finger for a few seconds[] and dragging.\nTry it. +launcheditems = [accent]Launched Items +map.delete = Jsi si jistý že chceš smazat mapu "[accent]{0}[]"? +level.highscore = Nejvyšší skóre: [accent]{0} +level.select = Výběr levelu +level.mode = Herní mód: +showagain = Znovu neukazovat ! +coreattack = < Jádro je pod útokem! > +nearpoint = [[ [scarlet]LEAVE DROP POINT IMMEDIATELY[] ]\nannihilation imminent +outofbounds = [[ OUT OF BOUNDS ]\n[]self-destruct in {0} +database = Core Database +savegame = Uložit hru +loadgame = Načíst hru +joingame = Připojit se ke hře +addplayers = Přidat/Odebrat hráče +customgame = Vlastní hra +newgame = New Game +none = +minimap = Minimap +close = Zavřít +quit = Ukončit +maps = Mapy +continue = Pokračovat +maps.none = [LIGHT_GRAY]Žádné mapy nebyly nalezeny! +about.button = O hře +name = Name: +noname = Nejdřív si vyber[accent] herní jméno[]. +filename = Jméno složky: +unlocked = Nový blok odemknut! +completed = [accent]Completed +techtree = Tech Tree +research.list = [LIGHT_GRAY]Research: +research = Research +researched = [LIGHT_GRAY]{0} researched. +players = {0} hráčů online +players.single = {0} hráč online +server.closing = [accent]Zavírám server... +server.kicked.kick = Byl jsi vykopnut ze serveru! +server.kicked.serverClose = Server je zavřený. +server.kicked.clientOutdated = Zastaralý klient hry! Aktualizuj si hru! +server.kicked.serverOutdated = Zastaralý server! Řekni hostiteli o aktualizaci! +server.kicked.banned = Jsi zabanován na tomto serveru. +server.kicked.recentKick = Před nedávnem jsi byl vykopnut.\nPočkej než se znovu připojíš. +server.kicked.nameInUse = Někdo se stejným jménem\nje aktuálně na serveru. +server.kicked.nameEmpty = Tvé jméno je neplatné. +server.kicked.idInUse = Již jsi na tomhle serveru připojen! Připojování se dvěma účty není povoleno. +server.kicked.customClient = Tento server nepodporuje vlastní verze hry. Stáhni si oficiální verzi. +server.kicked.gameover = Game over! +host.info = [accent]hostitel[] hostuje server na portu [scarlet]6567[]. \nKdokoliv na stejné [LIGHT_GRAY]wifi nebo místní síti[] by měl vidět server ve svém listu serverů.\n\nJestli chcete aby se uživatelé připojovali odkudkoliv pomocí IP, [accent]přesměrování portů[] je nutné.\n\n[LIGHT_GRAY]Poznámka: Jestli někdo má problém s připojením ke své LAN hře, ujistěte se že má Mindustry povolený přístup k místní síti v nastavení Firewallu. +join.info = Tady můžeš vložit [accent]IP serveru[] ke kterému se chceš připojit, nebo objevit [accent]Servery Místní sítě[] ke kterým se chceš připojit.\nLAN i Multiplayer jsou podporovány.\n\n[LIGHT_GRAY]Poznámka: Není žádný globální seznam serverů; Pokud se budeš chtít připojit k někomu pomocí IP, budeš jí muset znát od hostitele. +hostserver = Hostovat hru +hostserver.mobile = Hostovat\nHru +host = Hostitel +hosting = [accent]Otevírám server... +hosts.refresh = Obnovit +hosts.discovering = Hledám hry LAN +server.refreshing = Obnovuji servery +hosts.none = [lightgray]Žádné místní hry nebyly nalezeny! +host.invalid = [scarlet]Nejde se připojit k hostiteli. +trace = Vystopovat hráče +trace.playername = Jméno hráče: [accent]{0} +trace.ip = IP: [accent]{0} +trace.id = Unikátní ID: [accent]{0} +trace.mobile = Mobile Client: [accent]{0} +trace.modclient = Vlastní Klient: [accent]{0} +invalidid = Neplatná IP klienta! Poslat zprávu o chybě. +server.bans = Bany. +server.bans.none = Žádní hráči s banem nebyli nalezeni. +server.admins = Admini +server.admins.none = Žádní admini nebyli nalezeni. +server.add = Přidat server +server.delete = Jsi si jistý že chceš smazat tento server? +server.hostname = Hostitel: {0} +server.edit = Upravit server +server.outdated = [crimson]Zastaralý server![] +server.outdated.client = [crimson]Zastaralý klient![] +server.version = [lightgray]Verze: {0} {1} +server.custombuild = [yellow]Vlastní verze +confirmban = Jsi si jistý že chceš zabanovat tohoto hráče? +confirmkick = Jsi si jistý že chceš vykopnout tohoto hráče? +confirmunban = Jsi si jistý že chceš odbanovat tohoto hráče +confirmadmin = Jsi si jistý že chceš tohoto hráče pasovat na admina? +confirmunadmin = Jsi si jistý že chceš odebrat práva tomuto hráči? +joingame.title = Připojit se ke hře +joingame.ip = Adresa: +disconnect = Odpojen. +disconnect.data = Chyba načtení dat světa! +connecting = [accent]Připojuji se... +connecting.data = [accent]Načítám data světa... +server.port = Port: +server.addressinuse = Adresu již někdo používá! +server.invalidport = Neplatné číslo portu! +server.error = [crimson]Chyba při hostování serveru: [accent]{0} +save.old = Tato uložená pozice je pro starší verzi hry a již není možno jí použít.\n\n[LIGHT_GRAY]Zpětná kompatibilita bude implementována v plné verzi 4.0. +save.new = Nové uložení +save.overwrite = Jsi si jistý že chceš přepsat\ntento ukládaci slot? +overwrite = Přepsat +save.none = Žádné uložené pozice nebyly nalezeny +saveload = [accent]Ukládám... +savefail = Nepodařilo se uložit hru! +save.delete.confirm = Jsi si jistý že chceš smazat toto uložení? +save.delete = Smazat +save.export = Exportovat uložení +save.import.invalid = [accent]Toto uložení je neplatné! +save.import.fail = [crimson]Nepodařilo se importovat uložení: [accent]{0} +save.export.fail = [crimson]Nepodařilo se exportovat uložení: [accent]{0} +save.import = Importovat uložení +save.newslot = Uložit hru: +save.rename = Přejmenovat +save.rename.text = Nové jméno: +selectslot = Vyber uložení. +slot = [accent]Slot {0} +save.corrupted = [accent]Uložení je poškozené nebo neplatné\nPokud jsi právě aktualizoval svou hru, je to možná změnou formátu pro ukládání a [scarlet]NE[] chyba hry. +empty = +on = On +off = Off +save.autosave = Automatické uložení: {0} +save.map = Mapa: {0} +save.wave = Vlna {0} +save.difficulty = Obtížnost: {0} +save.date = Naposledy uloženo: {0} +save.playtime = Herní čas: {0} +warning = Warning. +confirm = Potvrdit +delete = Smazat +ok = OK +open = Otevřít +customize = Customize +cancel = Zrušit +openlink = Otevřít Odkaz +copylink = Zkopírovat Odkaz +back = Zpět +quit.confirm = Jsi si jistý že chceš ukončit ? +changelog.title = Záznam změn +changelog.loading = Načítání záznamu změn... +changelog.error.android = [accent]Berte v potaz že záznam změn někdy nefunguje na Android 4.4 a níž!\nJe to kvůli interní chybě v systému Android. +changelog.error.ios = [accent]Záznam změn nění aktuálně podporován v systému IOS. +changelog.error = [scarlet]Chyba v načítání záznamu změn!\nZkontrolujte své připojení k internetu. +changelog.current = [yellow][[Aktuální verze] +changelog.latest = [accent][[nejnovější verze] +loading = [accent]Načítám... +saving = [accent]Ukládám... +wave = [accent]Vlna {0} +wave.waiting = [LIGHT_GRAY]Vlna za {0} +wave.waveInProgress = [LIGHT_GRAY]Wave in progress +waiting = [LIGHT_GRAY]Čekám... +waiting.players = Čekání na hráče... +wave.enemies = [LIGHT_GRAY]{0} Nepřátel zbývá +wave.enemy = [LIGHT_GRAY]{0} Nepřítel zbývá +loadimage = Nahrát obrázek +saveimage = Uložit obrázek +unknown = Neznámý +custom = Vlastní +builtin = Zabudovaný +map.delete.confirm = Jsi si jistý žechceš tuto mapu smazat? tato akce je nevratná! +map.random = [accent]Náhodná mapa +map.nospawn = Tato mapa nemá žádná jádra pro hráče ke spawnutí! přidej[ROYAL] blue[] jádro na tuto mapu v editoru. +map.nospawn.pvp = Tato mapa nemá žádné nepřátelské jádra pro hráče ke spawnutí! přidej[SCARLET] red[] jádro na tuto mapu v editoru. +map.nospawn.attack = This map does not have any enemy cores for player to attack! Add[SCARLET] red[] cores to this map in the editor. +map.invalid = Chyba v načítání mapy: poškozený nebo neplatný soubor mapy. +editor.brush = Štětec +editor.openin = Otevřít v editoru. +editor.oregen = Generovat nerostné zdroje. +editor.oregen.info = Generování nerostných zdrojů: +editor.mapinfo = Informace o mapě +editor.author = Autor: +editor.description = Popis: +editor.waves = Waves: +editor.rules = Rules: +editor.ingame = Edit In-Game +waves.title = Waves +waves.remove = Remove +waves.never = +waves.every = every +waves.waves = wave(s) +waves.perspawn = per spawn +waves.to = to +waves.boss = Boss +waves.preview = Preview +waves.edit = Edit... +waves.copy = Copy to Clipboard +waves.load = Load from Clipboard +waves.invalid = Invalid waves in clipboard. +waves.copied = Waves copied. +editor.default = [LIGHT_GRAY] +edit = Edit... +editor.name = Jméno: +editor.spawn = Spawn Unit +editor.removeunit = Remove Unit +editor.teams = Týmy +editor.elevation = Výška +editor.errorload = Error loading file:\n[accent]{0} +editor.errorsave = Error saving file:\n[accent]{0} +editor.errorimage = That's an image, not a map. Don't go around changing extensions expecting it to work.\n\nIf you want to import a legacy map, use the 'import legacy map' button in the editor. +editor.errorlegacy = This map is too old, and uses a legacy map format that is no longer supported. +editor.errorheader = This map file is either not valid or corrupt. +editor.errorname = Map has no name defined. +editor.update = Update +editor.randomize = Randomize +editor.apply = Apply +editor.generate = Generovat +editor.resize = Změnit velikost +editor.loadmap = Načíst mapu +editor.savemap = Uložit mapu +editor.saved = Uloženo! +editor.save.noname = Tvoje mapa nemá jméno! Jméno nastavíš v Informacích o mapě. +editor.save.overwrite = Tvoje mapa přepisuje vestavěnou mapu! Vyber odlišné jméno v Informacích o mapě. +editor.import.exists = [scarlet]Není možno importovat:[] vestavěná mapa jménem '{0}' již existuje! +editor.import = Import +editor.importmap = Importovat mapu +editor.importmap.description = Importovat již existující mapu +editor.importfile = Importovat soubor +editor.importfile.description = Importovat externí soubor mapy +editor.importimage = Importovat Legacy Obrázek +editor.importimage.description = Importovat exrerní obrázek mapy +editor.export = Export +editor.exportfile = Exportovat soubor +editor.exportfile.description = Exportovat soubor mapy +editor.exportimage = Exportovat obrázek terénu +editor.exportimage.description = Exportovat obrázek souboru mapy +editor.loadimage = Importovat terén +editor.saveimage = Exportovat terén +editor.unsaved = [scarlet]Máš neuložené změny![]\nPřesto chceš ukončit? +editor.resizemap = Změnit velikost mapy +editor.mapname = Jméno mapy: +editor.overwrite = [accent]Varování!\nToto přepíše již existující mapu. +editor.overwrite.confirm = [scarlet]Varování![] Mapa s tímto jménem již existuje. Jsi si jistý že ji chceš přepsat? +editor.selectmap = Vyber mapu k načtení: +filters.empty = [LIGHT_GRAY]No filters! Add one with the button below. +filter.distort = Distort +filter.noise = Noise +filter.ore = Ore +filter.rivernoise = River Noise +filter.scatter = Scatter +filter.terrain = Terrain +filter.option.scale = Scale +filter.option.chance = Chance +filter.option.mag = Magnitude +filter.option.threshold = Threshold +filter.option.circle-scale = Circle Scale +filter.option.octaves = Octaves +filter.option.falloff = Falloff +filter.option.block = Block +filter.option.floor = Floor +filter.option.wall = Wall +filter.option.ore = Ore +filter.option.floor2 = Secondary Floor +filter.option.threshold2 = Secondary Threshold +filter.option.radius = Radius +filter.option.percentile = Percentile +width = Šířka: +height = Výška: +menu = Hlavní menu +play = Hrát +load = Načíst +save = Uložit +fps = FPS: {0} +tps = TPS: {0} +ping = Odezva: {0}ms +language.restart = Prosím restartuj hru aby se provedla změna jazyka! +settings = Nastavení +tutorial = Tutoriál +editor = Editor +mapeditor = Editor map +donate = Darovat +abandon = Abandon +abandon.text = This zone and all its resources will be lost to the enemy. +locked = Locked +complete = [LIGHT_GRAY]Complete: +zone.requirement = Wave {0} in zone {1} +resume = Resume Zone:\n[LIGHT_GRAY]{0} +bestwave = [LIGHT_GRAY]Best: {0} +launch = Launch +launch.title = Launch Successful +launch.next = [LIGHT_GRAY]next opportunity at wave {0} +launch.unable = [scarlet]Unable to LAUNCH.[] Enemies. +launch.confirm = This will launch all resources in your core.\nYou will not be able to return to this base. +uncover = Uncover +configure = Configure Loadout +configure.locked = [LIGHT_GRAY]Reach wave {0}\nto configure loadout. +zone.unlocked = [LIGHT_GRAY]{0} unlocked. +zone.requirement.complete = Wave {0} reached:\n{1} zone requirements met. +zone.config.complete = Wave {0} reached:\nLoadout config unlocked. +zone.resources = Resources Detected: +add = Add... +boss.health = Boss Health +connectfail = [crimson]Nepovedlo se připojení k serveru:\n\n[accent]{0} +error.unreachable = Server je nedostupný.\nJe adresa napsaná správně? +error.invalidaddress = Neplatná adresa. +error.timedout = Čas vypršel!\nUjisti se že hostitel má nastavené přesměrování portů a adresa je napsaná správně! +error.mismatch = Chyba Packetu:\nKlient/Verze serveru se neshodují.\nUjisti se že máš nejnovější verzi Mindustry! +error.alreadyconnected = Již připojeno. +error.mapnotfound = Soubor mapy nebyl nalezen! +error.io = Network I/O error. +error.any = neznámá chyba sítě. +zone.groundZero.name = Ground Zero +zone.desertWastes.name = Desert Wastes +zone.craters.name = The Craters +zone.frozenForest.name = Frozen Forest +zone.ruinousShores.name = Ruinous Shores +zone.stainedMountains.name = Stained Mountains +zone.desolateRift.name = Desolate Rift +zone.nuclearComplex.name = Nuclear Production Complex +zone.overgrowth.name = Overgrowth +zone.tarFields.name = Tar Fields +settings.language = Jazyk +settings.reset = nastavit výchozí +settings.rebind = Přenastavit +settings.controls = Ovládání +settings.game = Hra +settings.sound = zvuky +settings.graphics = Zobrazení +settings.cleardata = Resetovat data hry... +settings.clear.confirm = Jsi si jistý že chceš resetovat obsah hry?\nTento krok je nevratný! +settings.clearall.confirm = [scarlet]Varování![]\nToto vyresetuje všechna data, včetně uložení, map, odemykatelných a nastavení ovládání.\nJakmile stiskneš 'ok' data se vymažou a hra se automaticky ukončí. +settings.clearunlocks = Vymazání odemykatelných +settings.clearall = Vymazat všechno +paused = [accent]< Pauza > +yes = Ano +no = Ne +info.title = Informace +error.title = [crimson]Objevila se chyba +error.crashtitle = Objevila se chyba +blocks.input = Input +blocks.output = Output +blocks.booster = Booster +block.unknown = [LIGHT_GRAY]??? +blocks.powercapacity = Kapacita energie +blocks.powershot = Energie na výstřel +blocks.targetsair = Zaměřuje vzdušné jednotky +blocks.targetsground = Targets Ground +blocks.itemsmoved = Move Speed +blocks.launchtime = Time Between Launches +blocks.shootrange = Dostřel +blocks.size = velikost +blocks.liquidcapacity = Kapacita tekutin +blocks.powerrange = Rozsah energie +blocks.poweruse = Spotřebuje energie +blocks.powerdamage = Energie na poškození +blocks.itemcapacity = kapacita předmětů +blocks.basepowergeneration = Základní generování energie +blocks.productiontime = Production Time +blocks.repairtime = Block Full Repair Time +blocks.speedincrease = Speed Increase +blocks.range = Range +blocks.drilltier = Vrtatelné +blocks.drillspeed = Základní rychlost vrtu +blocks.boosteffect = Boost Effect +blocks.maxunits = Max Active Units +blocks.health = Životy +blocks.buildtime = Build Time +blocks.inaccuracy = Nepřesnost/výchylka +blocks.shots = Střely +blocks.reload = Střely za sekundu +blocks.ammo = Ammo +bar.drillspeed = Drill Speed: {0}/s +bar.efficiency = Efficiency: {0}% +bar.powerbalance = Power: {0} +bar.poweramount = Power: {0} +bar.poweroutput = Power Output: {0} +bar.items = Items: {0} +bar.liquid = Liquid +bar.heat = Heat +bar.power = Power +bar.progress = Build Progress +bar.spawned = Units: {0}/{1} +bullet.damage = [stat]{0}[lightgray] dmg +bullet.splashdamage = [stat]{0}[lightgray] area dmg ~[stat] {1}[lightgray] tiles +bullet.incendiary = [stat]incendiary +bullet.homing = [stat]homing +bullet.shock = [stat]shock +bullet.frag = [stat]frag +bullet.knockback = [stat]{0}[lightgray] knockback +bullet.freezing = [stat]freezing +bullet.tarred = [stat]tarred +bullet.multiplier = [stat]{0}[lightgray]x ammo multiplier +bullet.reload = [stat]{0}[lightgray]x reload +unit.blocks = Bloky +unit.powersecond = jednotek energie/sekunda +unit.liquidsecond = jednotek tekutin/sekundu +unit.itemssecond = předmětů/sekundu +unit.liquidunits = jednotek tekutin +unit.powerunits = jednotek energie +unit.degrees = úhly +unit.seconds = sekundy +unit.persecond = /sec +unit.timesspeed = x speed +unit.percent = % +unit.items = předměty +category.general = Všeobecné +category.power = Energie +category.liquids = Tekutiny +category.items = Předměty +category.crafting = Vyžaduje +category.shooting = Střílí +category.optional = Volitelné vylepšení +setting.landscape.name = Lock Landscape +setting.shadows.name = Shadows +setting.linear.name = Linear Filtering +setting.animatedwater.name = Animated Water +setting.animatedshields.name = Animated Shields +setting.antialias.name = Antialias[LIGHT_GRAY] (requires restart)[] +setting.indicators.name = Indikátor pro spojence +setting.autotarget.name = Automaticky zaměřuje +setting.fpscap.name = Max FPS +setting.fpscap.none = žádný +setting.fpscap.text = {0} FPS +setting.swapdiagonal.name = Always Diagonal Placement +setting.difficulty.training = Trénink +setting.difficulty.easy = lehká +setting.difficulty.normal = normální +setting.difficulty.hard = težká +setting.difficulty.insane = šílená +setting.difficulty.name = Obtížnost: +setting.screenshake.name = Třes obrazu +setting.effects.name = Zobrazit efekty +setting.sensitivity.name = Citlivost ovladače +setting.saveinterval.name = Interval automatického ukládání +setting.seconds = {0} Sekund +setting.fullscreen.name = Celá obrazovka +setting.borderlesswindow.name = Borderless Window[LIGHT_GRAY] (may require restart) +setting.fps.name = Ukázat snímky/sekundu +setting.vsync.name = Vertikální synchronizace +setting.lasers.name = Ukázat laser energie +setting.pixelate.name = Pixelate [LIGHT_GRAY](may decrease performance) +setting.minimap.name = Ukázat minimapu +setting.musicvol.name = Hlasitost hudby +setting.mutemusic.name = Ztišit hudbu +setting.sfxvol.name = SFX hlasitost +setting.mutesound.name = Ztišit zvuky +setting.crashreport.name = Poslat anonymní spis o zhroucení hry +setting.chatopacity.name = Chat Opacity +setting.playerchat.name = Display In-Game Chat +keybind.title = Přenastavit klávesy +category.general.name = Všeobecné +category.view.name = Pohled +category.multiplayer.name = Multiplayer +command.attack = Útok +command.retreat = Ústup +command.patrol = Hlídkovat +keybind.gridMode.name = Výběr bloků +keybind.gridModeShift.name = Výběr kategorie +keybind.press = Stiskni klívesu... +keybind.press.axis = Stiskni osu nebo klávesu... +keybind.screenshot.name = Sníměk mapy +keybind.move_x.name = Pohyb na X +keybind.move_y.name = Pohyb na Y +keybind.select.name = Vybrat/Střílet +keybind.diagonal_placement.name = Diagonal Placement +keybind.pick.name = Pick Block +keybind.break_block.name = Break Block +keybind.deselect.name = Odznačit +keybind.shoot.name = Střílet +keybind.zoom_hold.name = Přiblížení-podržení +keybind.zoom.name = přiblížení +keybind.menu.name = Hlavní nabídka +keybind.pause.name = pauza +keybind.minimap.name = Minimap +keybind.dash.name = Sprint +keybind.chat.name = Chat +keybind.player_list.name = Seznam hráčů +keybind.console.name = Konzole +keybind.rotate.name = Otočit +keybind.toggle_menus.name = Přepínání nabídek +keybind.chat_history_prev.name = Předchozí historie chatu +keybind.chat_history_next.name = Další historie chatu +keybind.chat_scroll.name = Chat posun +keybind.drop_unit.name = Zahodit jednotku +keybind.zoom_minimap.name = Přiblížit minimapu +mode.help.title = Popis módů +mode.survival.name = Survival +mode.survival.description = The normal mode. Limited resources and automatic incoming waves. +mode.sandbox.name = Sandbox +mode.sandbox.description = Nekonečné zdroje a žádný čas pro vlny nepřátel. +mode.pvp.name = PvP +mode.pvp.description = Bojuj proti ostatním hráčům v lokální síti. +mode.attack.name = Útok +mode.attack.description = No waves, with the goal to destroy the enemy base. +mode.custom = Custom Rules +rules.infiniteresources = Infinite Resources +rules.wavetimer = Wave Timer +rules.waves = Waves +rules.enemyCheat = Infinite AI Resources +rules.unitdrops = Unit Drops +rules.unitbuildspeedmultiplier = Unit Creation Speed Multiplier +rules.unithealthmultiplier = Unit Health Multiplier +rules.playerhealthmultiplier = Player Health Multiplier +rules.playerdamagemultiplier = Player Damage Multiplier +rules.unitdamagemultiplier = Unit Damage Multiplier +rules.enemycorebuildradius = Enemy Core No-Build Radius:[LIGHT_GRAY] (tiles) +rules.respawntime = Respawn Time:[LIGHT_GRAY] (sec) +rules.wavespacing = Wave Spacing:[LIGHT_GRAY] (sec) +rules.buildcostmultiplier = Build Cost Multiplier +rules.buildspeedmultiplier = Build Speed Multiplier +rules.waitForWaveToEnd = Waves wait for enemies +rules.dropzoneradius = Drop Zone Radius:[LIGHT_GRAY] (tiles) +rules.respawns = Max respawns per wave +rules.limitedRespawns = Limit Respawns +rules.title.waves = Waves +rules.title.respawns = Respawns +rules.title.resourcesbuilding = Resources & Building +rules.title.player = Players +rules.title.enemy = Enemies +rules.title.unit = Units +content.item.name = Předměty +content.liquid.name = Tekutiny +content.unit.name = jednotky +content.block.name = Blocks +content.mech.name = Mechy +item.copper.name = Měď +item.copper.description = Užitečný strukturální materiál. Používá se rozsáhle v ostatních typech bloků. +item.lead.name = Olovo +item.lead.description = Základní počáteční materiál. Požívá se rozsáhle v elektronice a v blocích pro transport tekutin. +item.coal.name = Uhlí +item.coal.description = Běžné a snadno dostupné palivo, pochází z Ostravy. +item.graphite.name = Graphite +item.titanium.name = Titánium +item.titanium.description = Vzácný, velice lehký kov, používá se rozsáhle v trasportu tekutin, vrtech a letounech. +item.thorium.name = Thorium +item.thorium.description = Hustý, radioaktivní materiál, používá se jako strukturální podpora a jako nuklearní palivo. +item.silicon.name = Křemík +item.silicon.description = Extrémně užitečný polovodič, aplikuje se v solárních panelech a v komplexní elektronice. +item.plastanium.name = Plastanium +item.plastanium.description = Lehký, kujný materiál, používá se v pokročilém letectví a jako fragmentační střelivo. +item.phase-fabric.name = Fázová tkanina +item.phase-fabric.description = Skoro beztížná substance používaná v pokročilé elektronice a v sebeopravné technologii. +item.surge-alloy.name = Impulzní slitina +item.surge-alloy.description = Pokročilá slitina s unikátními elektronickými vlastnostmi. +item.spore-pod.name = Spore Pod +item.spore-pod.description = Used for conversion into oil, explosives and fuel. +item.sand.name = Písek +item.sand.description = Běžný materiál rozšířeně používaný v spalování slitin. +item.blast-compound.name = Výbušná směs +item.blast-compound.description = Těkavá směs používaná v bombácha a výbušninách. Dá se spalovat ale jako palivo se nedoporučuje. +item.pyratite.name = Pyratite +item.pyratite.description = Extrémně vznětlivá substance, používá ve vznětovém střelivu. +item.metaglass.name = Metaglass +item.metaglass.description = A super-tough glass compound. Extensively used for liquid distribution and storage. +item.scrap.name = Scrap +item.scrap.description = Leftover remnants of old structures and units. Contains trace amounts of many different metals. +liquid.water.name = Voda +liquid.slag.name = Slag +liquid.oil.name = Ropa +liquid.cryofluid.name = Cryofluid +mech.alpha-mech.name = Alfa +mech.alpha-mech.weapon = Těžký Opakovač +mech.alpha-mech.ability = Roj dronů +mech.alpha-mech.description = Standartní mech. Má slušnou rychlost a poškození; Může vytvořit až 3 drony Pro zvýšenou ofenzivní způsobilost. +mech.delta-mech.name = Delta +mech.delta-mech.weapon = Obloukový generátor +mech.delta-mech.ability = Průtok +mech.delta-mech.description = Rychlý, Lehce obrněný mech vytvořený pro udeř a uteč akce. Působí malé poškození vůči struktůrám, ale může zneškodnit velkou skupinu nepřátelských jednotek velmi rychle svýmy elektro-obloukovými zbraněmi +mech.tau-mech.name = Tau +mech.tau-mech.weapon = Restruktní Laser +mech.tau-mech.ability = Opravná dávka +mech.tau-mech.description = Podpůrný mech. Léčí spojenecké stavby a jednotky střelbou do nich. Může léčit i spojence ve svém poli působení. +mech.omega-mech.name = Omega +mech.omega-mech.weapon = Rojové střely +mech.omega-mech.ability = Obrněná Konfigurace +mech.omega-mech.description = Objemný a velice dovře obrněný mech, určen pro útok v přední linii. Jeho schopnost obrnění blokuje až 90% příchozího poškození. +mech.dart-ship.name = Šipka +mech.dart-ship.weapon = Opakovač +mech.dart-ship.description = Standartní loď. Poměrně rychlý a lehký, má malou ofenzívu a pomalou rychlost těžení. +mech.javelin-ship.name = Oštěp +mech.javelin-ship.description = Loď stylu udeř a uteč. Zpočátku pomalý ale umí akcelerovat do obrovské rychlosti a létat u nepřátelských základen a působit značné škody svými elektrickými zbraněmi a raketami. +mech.javelin-ship.weapon = Dávka Raket +mech.javelin-ship.ability = Výbojový Posilovač +mech.trident-ship.name = Trojzubec +mech.trident-ship.description = Těžký bombardér. Docela dobře obrněný. +mech.trident-ship.weapon = Bombová zátoka +mech.glaive-ship.name = Glaiva +mech.glaive-ship.description = Obrovská, Dobře obrněná střelecká loď. Vybavena zápalným opakovačem. Dobrá akcelerace a maximální rychlost. +mech.glaive-ship.weapon = Plamenný Opakovač +item.explosiveness = [LIGHT_GRAY]Výbušnost: {0}% +item.flammability = [LIGHT_GRAY]Zápalnost: {0}% +item.radioactivity = [LIGHT_GRAY]Radioaktivita: {0}% +unit.health = [LIGHT_GRAY]Životy: {0} +unit.speed = [LIGHT_GRAY]Rychlost: {0} +mech.weapon = [LIGHT_GRAY]Zbraň: {0} +mech.health = [LIGHT_GRAY]Health: {0} +mech.itemcapacity = [LIGHT_GRAY]Kapacita předmětů: {0} +mech.minespeed = [LIGHT_GRAY]Rychlost těžení: {0} +mech.minepower = [LIGHT_GRAY]Síla těžení: {0} +mech.ability = [LIGHT_GRAY]Schopnost: {0} +mech.buildspeed = [LIGHT_GRAY]Building Speed: {0}% +liquid.heatcapacity = [LIGHT_GRAY]Kapacita teploty: {0} +liquid.viscosity = [LIGHT_GRAY]Viskozita: {0} +liquid.temperature = [LIGHT_GRAY]Teplota: {0} +block.grass.name = Grass +block.salt.name = Salt +block.saltrocks.name = Salt Rocks +block.pebbles.name = Pebbles +block.tendrils.name = Tendrils +block.sandrocks.name = Sand Rocks +block.spore-pine.name = Spore Pine +block.sporerocks.name = Spore Rocks +block.rock.name = Rock +block.snowrock.name = Snow Rock +block.shale.name = Shale +block.shale-boulder.name = Shale Boulder +block.moss.name = Moss +block.shrubs.name = Shrubs +block.spore-moss.name = Spore Moss +block.shalerocks.name = Shale Rocks +block.scrap-wall.name = Scrap Wall +block.scrap-wall-large.name = Large Scrap Wall +block.scrap-wall-huge.name = Huge Scrap Wall +block.scrap-wall-gigantic.name = Gigantic Scrap Wall +block.thruster.name = Thruster +block.kiln.name = Kiln +block.kiln.description = Smelts sand and lead into metaglass. Requires small amounts of power. +block.graphite-press.name = Graphite Press +block.multi-press.name = Multi-Press +block.constructing = {0} [LIGHT_GRAY](Constructing) +block.spawn.name = Nepřátelský Spawn +block.core-shard.name = Core: Shard +block.core-foundation.name = Core: Foundation +block.core-nucleus.name = Core: Nucleus +block.deepwater.name = Hluboká voda +block.water.name = Voda +block.tainted-water.name = Tainted Water +block.darksand-tainted-water.name = Dark Sand Tainted Water +block.tar.name = Tar +block.stone.name = Kámen +block.sand.name = Písek +block.darksand.name = Dark Sand +block.ice.name = Led +block.snow.name = Sníh +block.craters.name = Craters +block.sand-water.name = Sand water +block.darksand-water.name = Dark Sand Water +block.char.name = Char +block.holostone.name = Holo stone +block.ice-snow.name = Ice Snow +block.rocks.name = Rocks +block.icerocks.name = Ice rocks +block.snowrocks.name = Snow Rocks +block.dunerocks.name = Dune Rocks +block.pine.name = Pine +block.white-tree-dead.name = White Tree Dead +block.white-tree.name = White Tree +block.spore-cluster.name = Spore Cluster +block.metal-floor.name = Metal Floor +block.metal-floor-2.name = Metal Floor 2 +block.metal-floor-3.name = Metal Floor 3 +block.metal-floor-5.name = Metal Floor 5 +block.metal-floor-damaged.name = Metal Floor Damaged +block.dark-panel-1.name = Dark Panel 1 +block.dark-panel-2.name = Dark Panel 2 +block.dark-panel-3.name = Dark Panel 3 +block.dark-panel-4.name = Dark Panel 4 +block.dark-panel-5.name = Dark Panel 5 +block.dark-panel-6.name = Dark Panel 6 +block.dark-metal.name = Dark Metal +block.ignarock.name = Igna Rock +block.hotrock.name = Hot Rock +block.magmarock.name = Magma Rock +block.cliffs.name = Cliffs +block.copper-wall.name = Měděná zeď +block.copper-wall-large.name = Velká měděná zeď +block.titanium-wall.name = Titanium Wall +block.titanium-wall-large.name = Large Titanium Wall +block.phase-wall.name = Fázová stěna +block.phase-wall-large.name = Velká fázová stěna +block.thorium-wall.name = Thoriová stěna +block.thorium-wall-large.name = Velká thoriová stěna +block.door.name = Dveře +block.door-large.name = Velké dveře +block.duo.name = Duo +block.scorch.name = Scorch +block.scatter.name = Scatter +block.hail.name = Hail +block.lancer.name = Lancer +block.conveyor.name = Dopravník +block.titanium-conveyor.name = Titániový dopravník +block.junction.name = Křižovatka +block.router.name = Směrovač +block.distributor.name = Distributor +block.sorter.name = Dělička +block.sorter.description = Třídí předměty. Jestli je předmět shodný s výběrem, je mu dovoleno projít. Naopak neshodné předměty jsou vypuštěny do prava nebo do leva. +block.overflow-gate.name = Brána přetečení +block.overflow-gate.description = Kombinace distributoru a děličky která má výstup do leva nebo do prava jen pokud je přední strana zablokovaná. +block.silicon-smelter.name = Silicon Smelter +block.phase-weaver.name = Tkalcovna pro fázovou tkaninu +block.pulverizer.name = Rozmělňovač +block.cryofluidmixer.name = Cryofluid mixér +block.melter.name = Tavírna +block.incinerator.name = Spalovna +block.spore-press.name = Spore Press +block.separator.name = Separátor +block.coal-centrifuge.name = Coal Centrifuge +block.power-node.name = Energetický uzel +block.power-node-large.name = Velký energetický uzel +block.surge-tower.name = Surge Tower +block.battery.name = Baterie +block.battery-large.name = Velká baterie +block.combustion-generator.name = Spalovací generátor +block.turbine-generator.name = Turbínový generátor +block.differential-generator.name = Differential Generator +block.impact-reactor.name = Impact Reactor +block.mechanical-drill.name = Mechanický vrt +block.pneumatic-drill.name = Pneumatický vrt +block.laser-drill.name = Laserový vrt +block.water-extractor.name = Vodní extraktor +block.cultivator.name = Kultivátor +block.dart-mech-pad.name = Dart Mech Pad +block.delta-mech-pad.name = Plošina pro Delta Mech +block.javelin-ship-pad.name = Plošina pro Oštěp Mech +block.trident-ship-pad.name = Plošina pro loď Trojzubec +block.glaive-ship-pad.name = Plošina pro loď Glaiva +block.omega-mech-pad.name = Plošina pro Omega Mech +block.tau-mech-pad.name = Plošina pro Tau Mech +block.conduit.name = Potrubí +block.mechanical-pump.name = Mechanická pumpa +block.item-source.name = Zdroj předmětů +block.item-void.name = Prázdnota pro předměty +block.liquid-source.name = Zdroj tekutin +block.power-void.name = Prázdnota pro energii +block.power-source.name = Nekonečný zdroj energie +block.unloader.name = Odbavovač +block.vault.name = Trezor +block.wave.name = Vlna +block.swarmer.name = Rojiště +block.salvo.name = Salva +block.ripple.name = Vlnění +block.phase-conveyor.name = Fázový přepravník +block.bridge-conveyor.name = Mostový přepravník +block.plastanium-compressor.name = Kompresor na Plastanium +block.pyratite-mixer.name = Pyratit mixér +block.blast-mixer.name = Výbušninový mixér +block.solar-panel.name = Solární panel +block.solar-panel-large.name = Velký solární panel +block.oil-extractor.name = Ropný Extraktor +block.spirit-factory.name = Továrna na Spirit Drony +block.phantom-factory.name = Továrna na Fantom Drony +block.wraith-factory.name = Továrna na Wraithy +block.ghoul-factory.name = Továrna na Ghůl Bombardéry +block.dagger-factory.name = Továrna na Dagger Mechy +block.crawler-factory.name = Crawler Mech Factory +block.titan-factory.name = Továrna na Titán Mechy +block.fortress-factory.name = Továrna na Fortress Mechy +block.revenant-factory.name = Továrna na Revenanty +block.repair-point.name = Opravný Bod +block.pulse-conduit.name = Pulzní potrubí +block.phase-conduit.name = Fázové potrubí +block.liquid-router.name = Směrovač tekutin +block.liquid-tank.name = Nádrž na tekutiny +block.liquid-junction.name = Křižovatka tekutin +block.bridge-conduit.name = Mostové potrubí +block.rotary-pump.name = Rotační pumpa +block.thorium-reactor.name = Thoriový Reaktor +block.mass-driver.name = Hromadný Distributor +block.blast-drill.name = Tlakovzdušný vrt +block.thermal-pump.name = Termální pumpa +block.thermal-generator.name = Termální Generátor +block.alloy-smelter.name = Slitinová pec +block.mender.name = Mender +block.mend-projector.name = Opravný projektor +block.surge-wall.name = Impulzní stěna +block.surge-wall-large.name = Velká Impulzní stěna +block.cyclone.name = Cyklón +block.fuse.name = Fůze +block.shock-mine.name = Šoková mina +block.overdrive-projector.name = Vysokorychlostní projektor +block.force-projector.name = Silový projektor +block.arc.name = Oblouk +block.rtg-generator.name = RTG Generátor +block.spectre.name = Spektr +block.meltdown.name = Meltdown +block.container.name = Kontejnér +block.launch-pad.name = Launch Pad +block.launch-pad.description = Launches batches of items without any need for a core launch. Unfinished. +block.launch-pad-large.name = Large Launch Pad +team.blue.name = modrá +team.red.name = červená +team.orange.name = oranžová +team.none.name = šedá +team.green.name = zelená +team.purple.name = fialová +unit.spirit.name = Spirit Dron +unit.spirit.description = Startovní dron. Standartně se objevuje u jádra. Automaticky těží rudy a opravuje stavby. +unit.phantom.name = Fantom Dron +unit.phantom.description = Pokročilý dron. Automaticky těží rudy a opravuje stavby. Podstatně víc efektivní než Spirit dron. +unit.dagger.name = Dagger +unit.dagger.description = Základní pozemní jednotka. Efektivní ve velkém počtu. +unit.crawler.name = Crawler +unit.titan.name = Titán +unit.titan.description = Pokročilá, obrněná pozemní jednotka. Útočí jak na pozemní tak vzdušné nepřátelské jednotky. +unit.ghoul.name = Ghůl Bombardér +unit.ghoul.description = Těžký, kobercový bombardér. +unit.wraith.name = Bojovník Wraith +unit.wraith.description = Rychlý, udeř a uteč stíhací letoun. +unit.fortress.name = Pevnost +unit.fortress.description = Težká, pozemní artilérní jednotka. +unit.revenant.name = Revenant +unit.eruptor.name = Eruptor +unit.chaos-array.name = Chaos Array +unit.eradicator.name = Eradicator +unit.lich.name = Lich +unit.reaper.name = Reaper +tutorial.begin = Tvá mise tady je vyhladit [LIGHT_GRAY] nepřítele[].\n\nZačneš tím že[accent] budeš těžit měd[]. Klikni na měděnou rudu poblíž tvého jádra pro těžbu. +tutorial.drill = Manuální zěžba je neefektivní.\n[accent]Vrty []budou těžit automaticky.\npolož jeden na měděnou rudu. +tutorial.conveyor = [accent]Dopravníky[] jsou zapotřebí k dopravě materiálu k jádru.\nVytvoř řadu dopravníku od vrtu až k jádru. +tutorial.morecopper = Je za potřebí více mědi.\n\nBuď ho můžeš natěžit manuáně nebo polož více vrtů. +tutorial.turret = Defenzivní stavby musí být postaveny za účelem obrany vůči[LIGHT_GRAY] nepříteli[].\nPostav střílnu Duo blízko svého jádra. +tutorial.drillturret = Duo střílny požadují[accent] měd jako střelivo []ke střelbě.\nPolož vrt blízko střílny pro zásobování mědí. +tutorial.waves = [LIGHT_GRAY] nepřítel[] je přibližuje.\n\nBraň své jádro po dobu dvou vln, postav více střílen. +tutorial.lead = Více rud je zde dostupných. Najdi a těž[accent] Olovo[].\n\nPřetáhni od tvojí jednotky k jádru pro přesun materiálu. +tutorial.smelter = Měd a olovo jsou slabé materiály.\nLepší[accent] hustá slitina[] může být vytvořena v peci.\n\npostav si jednu pec. +tutorial.densealloy = Pec teď bude produkovat slitinu.\nVyráběj si ji.\nZvyš počet produkce pokud je nutno. +tutorial.siliconsmelter = Jádro teď vytvoří[accent] spirit drona[] pro těžení a opravu bloků.\n\nTovárny pro ostatní jednotky mohou být vytvořeny za pomocí [accent] Křemíku.\nPostav Křemíkovou pec. +tutorial.silicondrill = Křemík vyžaduje[accent] uhlí[] a[accent] písek[].\nZačni tím že položíš pár vrtů. +tutorial.generator = Tato technologie vyžaduje energii k provozu.\nVytvoř[accent] spalovací generátor[]k produkci energie. +tutorial.generatordrill = Spalovací generátory vyžadujou palivo.\nZásobuj ho uhlím z vrtu. +tutorial.node = Energie vyžaduje přenos.\nVytvoř[accent] energetický uzel[] vedle svého generátoru pro přenos energie. +tutorial.nodelink = Energie může být přenášena zkrz kontakt s energetickými bloky a generátory, nebo propojením zkrze energetické uzly.\n\nPropoj energii kliknutím na uzel a následným výběrem generátoru a křemíkovou pecí. +tutorial.silicon = Křemík se teď produkuje. Vyráběj si.\n\nJe doporučeno zvýšit produkci. +tutorial.daggerfactory = Postav[accent] Továrnu na Dagger mechy.[]\n\nToto bude využito pro konstrukci útočné síly. +tutorial.router = Továrny potřebujou k provozu materiál.\nPolož na dopravník směrovač pro oddělení části nákladu k továrně. +tutorial.dagger = Propoj energetické uzly s továrnou.\nJakmile jsou požadavky splněny, Mechy se začnou stavět.\n\nPokládej vrty, generátory a dopravníky dle libosti. +tutorial.battle = [LIGHT_GRAY] Nepřítel[] prozradil lokaci svého jádra.\nZnič ho svými bojovými jednotkami. +block.copper-wall.description = Levný defenzivní blok.\nUžitečný k obraně tvého jádra a střílen v prvotních vlnách nepřátel. +block.copper-wall-large.description = Levný defenzivní blok.\nUžitečný k obraně tvého jádra a střílen v prvotních vlnách nepřátel.\nZabírá více polí. +block.thorium-wall.description = Sílný defenzivní blok.\nDobrá obrana vůči nepřátelům. +block.thorium-wall-large.description = Sílný defenzivní blok.\nDobrá obrana vůči nepřátelům..\nZabírá více polí. +block.phase-wall.description = Né tak silná jako zeď Thoria ale odráží nepřátelské projektily dokud nejsou moc silné. +block.phase-wall-large.description = Né tak silná jako zeď Thoria ale odráží nepřátelské projektily dokud nejsou moc silné.\nZabírá více polí. +block.surge-wall.description = Nejsilnější defenzivní blok.\nMá malou šanci vystřelit elektrický paprsek vůči útočníkovi. +block.surge-wall-large.description = Nejsilnější defenzivní blok.\nMá malou šanci vystřelit elektrický paprsek vůči útočníkovi.\nZabírá více polí. +block.door.description = Malé dveře, které se dají otevřít nebo zavřít kliknutím na ně.\nKdyž otevřené nepřátelé mohou střílet a dostat se skrz. +block.door-large.description = Velké dveře, které se dají otevřít nebo zavřít kliknutím na ně.\nKdyž otevřené nepřátelé mohou střílet a dostat se skrz.\nZabírá více polí. +block.mend-projector.description = Kontinuálně léčí bloky ve poli svého působení. +block.overdrive-projector.description = Zrychluje funkce blízkých struktůr jako jsou vrty a dopravníky. +block.force-projector.description = Vytvoří okolo sebe šestihrané silové pole, chrání jednotky a budovy uvnitř sebe vůči střelám. +block.shock-mine.description = Působí poškození nepřátelským jednotkám při sešlápnutí. Skoro neviditelné nepřáteli. +block.duo.description = Malá, levná střílna. +block.scatter.description = A medium-sized anti-air turret. Sprays clumps of lead or scrap flak at enemy units. +block.arc.description = Malá střílna, která střílí elektřinu v náhodném oblouku po nepřátelských jednotkách. +block.hail.description = Malá artilérní střílna. +block.lancer.description = Středně velká střílna, která střílí nabité elektrické paprsky. +block.wave.description = Středně vělká, rychle pálící střílna, která střílí krystalizované bubliny. +block.salvo.description = Středně velká střílna, která střílí v salvách. +block.swarmer.description = Středně velká střílna, která střílí rakety v dávkách. +block.ripple.description = Velká artilérní střílna, která vystřelí několik projektilů najednou. +block.cyclone.description = Velká rychle pálící střílna. +block.fuse.description = Velká střílna, která střílí paprsky krátkého dosahu. +block.spectre.description = Velká střílna, která vystřelí dva mocné projektily naráz. +block.meltdown.description = Velká střílna, která vystřelí mocný paprsek dalekého dosahu. +block.conveyor.description = Základní blok přepravy předmětů. Nese předměty kupředu a automaticky plní střílny nebo bloky výroby do kterých směřují. dá se otáčet do různých směrů. +block.titanium-conveyor.description = Pokročilý blok přepravy předmětů. Nese předměty rychleji jak standartní dopravníky. +block.phase-conveyor.description = Pokročilý blok přepravy předmětů. Využívá energii k přepravě od jednoho bodu k druhému po velice dlouhé vzdálenosti. +block.junction.description = Chová se jako most pro dva křížící se pásy dopravníků. Užitečný při situaci kdy dva rozdílné dopravníky dopravují dva rozdílné materiálny na rozdílné místa. +block.mass-driver.description = Ultimátní blok přepravy předmětů. Sbírá několik druhů předmětů a vystřelí je k dalšímu hromadnému distributoru přes veliké vzdálenosti. +block.silicon-smelter.description = Redukuje písek s vysoce čistým koksem za účelem výroby křemíku. +block.plastanium-compressor.description = Produkuje plastánium za pomocí titánia a ropy. +block.phase-weaver.description = Produkuje fázovou tkaninu z radioaktivního thoria a velkého množství písku. +block.alloy-smelter.description = Produkuje impulzní slitinu z titánia, olova, křemíku a mědi. +block.pulverizer.description = Drtí kámen na písek. Užitečné když se v oblasti nenalézá písek. +block.pyratite-mixer.description = Míchá uhlí, olovo a písek do velice hořlavého pyratitu. +block.blast-mixer.description = Používá ropu k přeměně pyratitu do méně hořlavé ale více explozivní těkavé směsi. +block.cryofluidmixer.description = Kombinuje vodu a titánium do cryofluid, která je více efektivní pro chlazení. +block.melter.description = Taví kámen při velice vysokých teplotách na lávu. +block.incinerator.description = Zbaví tě přebytku předmětů. +block.spore-press.description = Compresses spore pods into oil. +block.separator.description = Vystaví kámen velkému tlaku vody k získání různých materiálů obsažené v kameni. +block.power-node.description = Vysílá energii mezi propojenými uzly. Dokáže se propojit až se čtyřmi uzly či stavbami najednou. Uzel bude dostávat zásobu energie a bude ji distribuovat mezi připojené bloky. +block.power-node-large.description = Má větší dosah než standartní energetický uzel and a dokáže propojit až 6 staveb nebo uzly. +block.battery.description = Ukládá energii kdykoliv kdy je nadbytek ,poskytuje energii kdykolik když je pokles energie v síti, tak dlouho doku zbývá kapacita. +block.battery-large.description = Uloží více energie než standartní baterie. +block.combustion-generator.description = Generuje energii spalováním ropy nebo jinných hořlavých materiálů. +block.turbine-generator.description = Více efektivní než spalovací generátor, ale vyžaduje dodatečný přísun vody. +block.thermal-generator.description = Generuje obrovské množství energie z lávy. +block.solar-panel.description = Poskytuje malé množství energie ze slunce. +block.solar-panel-large.description = Poskytuje mnohem lepší zdroj energie než standartní solární panel, za to je mnohem nákladnější na stavbu. +block.thorium-reactor.description = Generuje obrovské množství energie z radioaktivního thoria. Vyžaduje konstantní chlazení. Způsobí velikou explozi je-li zásobován nedostatečným množstvím chlazení. Výstup energie závisí na plnosti obsahu generátoru, základní generování energie se aktivuje při poloviční kapacitě. +block.rtg-generator.description = Rádioizotopní Termoelektrický Generátor nevyžaduje chlazení, za to generuje méně energie než Thoriový generátor. +block.unloader.description = Vykládá předměty z kontejnéru, trezoru nebo jádra na dopravník nebo přímo do produktivních bloků. Druh předmětu pro vykládání lze měti kliknutím na odbavovač. +block.container.description = Ukládá malé množství předmětů každého typu. Připojené kontejnéry, trezory nebo jádra se budou chovat jako samostatné skladovací jednotky. [LIGHT_GRAY] Odbavovač[] lze použít pro odbavení předmětů z kontejnéru. +block.vault.description = Ukládá velké množství předmětů každého typu. Připojené kontejnéry, trezory nebo jádra se budou chovat jako samostatné skladovací jednotky. [LIGHT_GRAY] Odbavovač[] lže použít pro odbavení předmětů z trezoru. +block.mechanical-drill.description = Levný vrt. Při položení na vhodné pole, natrvalo a pomalu produkuje materiál na který byl položen. +block.pneumatic-drill.description = Vylepšený vrt, který je rychlejší a je schopen zpracovat trdší materiály za pomocí tlaku. +block.laser-drill.description = Dovoluje vrtat ještě rychleji díky laserové technologii, požaduje energii k provozu. Dodatečně, dokáže vrtat žíly radioaktivního thoria. +block.blast-drill.description = Ultimátní vrt, vyžaduje velké množství energie k provozu. +block.water-extractor.description = Extrahuje vodu ze země. Vhodný k použití když se v oblasti nenachází zdroj vody. +block.cultivator.description = Kultivuje půdu vodou za účelem získání biohmoty. +block.oil-extractor.description = Vyžaduje velké množství energie na extrakci ropy z písku. Použíj ho když se v oblasti nenachází žádný zdroj ropy. +block.trident-ship-pad.description = Zanech zde své aktuální plavidlo a změň ho do docela dobře obrněného těžkého bombardéru.\nPoužíj ho poklikáním když se nacházíš nad ním. +block.javelin-ship-pad.description = Zanech zde své aktuální plavidlo a změn ho na silný a rychlý stíhač s bleskovými zbraněmi.\nPoužíj ho poklikáním když se nacházíš nad ním. +block.glaive-ship-pad.description = Zanech zde své aktuální plavidlo a změn ho na velkou, dobře obrněnou střeleckou loď.\nPoužíj ho poklikáním když se nacházíš nad ním. +block.tau-mech-pad.description = Zanech zde své aktuální plavidlo a změn ho na na podpůrného mecha, který léčí spojenecké budovy a jednotky.\nPoužíj ho poklikáním když se nacházíš nad ním. +block.delta-mech-pad.description = Zanech zde své aktuální plavidlo a změn ho na rychlého, lehce obrněného mecha určeného pro udeř a uteč operace.\nPoužíj ho poklikáním když se nacházíš nad ním. +block.omega-mech-pad.description = Zanech zde své aktuální plavidlo a změn ho na objemného dobře obrněného mecha, určeného pro útok v přední linii.\nPoužíj ho poklikáním když se nacházíš nad ním. +block.spirit-factory.description = Produkuje lehké drony, kteří teží minerály a opravují budovy +block.phantom-factory.description = Produkuje pokročilé drony kteří jsou podstatně efektivnější jak spirit droni. +block.wraith-factory.description = Produkuje rychlé, udeř a uteč stíhače. +block.ghoul-factory.description = Produkuje těžké kobercové bombardéry. +block.dagger-factory.description = Produkuje standartní pozemní jednotky. +block.titan-factory.description = Produkuje pokročilé, orněné pozemní jednotky. +block.fortress-factory.description = Produkuje těžké artilérní, pozmení jednotky. +block.revenant-factory.description = Produkuje vzdušné, težké laserové stíhače.. +block.repair-point.description = Kontinuálně léčí nejbližší budovy a jednotky. +block.conduit.description = Základní blok přepravy tekutin. Funguje jako dopravník, ale na tekutiny, chápeš ne ? Užívá se s extraktory, pumpami nebo jiným potrubím. +block.pulse-conduit.description = Pokročilý blok přepravy tekutin. Přepravuje tekutiny rychleji a více než standartní potrubí. +block.phase-conduit.description = Pokročilý blok přepravy tekutin. Používá energii k teleportu tekutin do druhého bodu přez několik polí. +block.liquid-router.description = Příjmá tekutiny z jednoho směru a vypouští je rovnoměrně do zbylých tří směrů. Dokáže uložit na krátkou dobu nějaký obsah tekutin. Užitečný při rozdělení jednoho zdroje směřující do různých cílů. +block.liquid-tank.description = Uloží velké množství tekutin. Použíj ho pro vyrovnávací zásoby vody když je příděl nestabilní nebo jako záložní chlazení pro generátory. +block.liquid-junction.description = Chová se jako most pro dvě křížící se potrubí. Užitečný v situacích když dvě rozdílné potrubí nesou rozdílný obsah na rozdílná místa. +block.bridge-conduit.description = Pokročilý blok přepravy tekutin. Dovoluje transportovat tekutiny až přez tři pole jakéhokoliv terénu nebo budovy. +block.mechanical-pump.description = Levná pumpa s pomalým tokem, ale nevyžaduje nergii k provozu. +block.rotary-pump.description = Pokročilá pumpa která, zdvojnásobuje přísun tekutin za použití energie. +block.thermal-pump.description = Ultimátní pumpa. Trojnásobně rychlejší než mechanická pumpa a jediná pumpa která dokáže pracovat s lávou. +block.router.description = Příijmá předměty z jednoho směru a posílá je rovnoměrně do zbylých tří směrů. Užitečný při rozdělení jednoho zdroje směřující do různých cílů. +block.distributor.description = Pokročilý směrovač, který z libovolného počtu vstupů vytvoří libovolný počet výstupu a rozdělí přísun předmětů rovnoměrně do každého z nich, obdoba Multiplexeru a Demultiplexeru. +block.bridge-conveyor.description = Pokročilý blok přepravy předmětů. Dovoluje transport předmětů až přez tři pole jakéhokoliv terénu nebo budovy. +block.item-source.description = Nekonečný zdroj předmětů. Jen pro Sandbox. +block.liquid-source.description = Nekonečný zdroj tekutin. Jen pro Sandbox. +block.item-void.description = Likviduje jakéhokoliv vstupní předmět bež použití energie. Jen pro Sandbox. +block.power-source.description = Nekonečný zdroj energie. Jen pro Sandbox. +block.power-void.description = Prázdnota pro veškerou energii vstupující do něj. Jen pro Sandbox. +liquid.water.description = Nejčastěji se používá ke chlazení a zpracování odpadu. +liquid.oil.description = Může být spálen, vybouchnout nebo použit jako chlazení. +liquid.cryofluid.description = Nejefektivnější tekutina pro chlazení. diff --git a/core/assets/bundles/bundle_de.properties b/core/assets/bundles/bundle_de.properties index be8fa12cf5..ee5a45f580 100644 --- a/core/assets/bundles/bundle_de.properties +++ b/core/assets/bundles/bundle_de.properties @@ -1,479 +1,947 @@ -text.about = Erstellt von [ROYAL] Anuken. [] \nUrsprünglich ein Eintrag im [orange] GDL [] MM Jam.\n\nCredits: \n- SFX gemacht mit [yellow] bfxr [] - Musik gemacht von [green] RoccoW [] / gefunden auf [lime] FreeMusicArchive.org [] \n\nBesonderer Dank geht an: \n- [coral] MitchellFJN []: Umfangreicher Spieletest und Feedback \n- [sky] Luxray5474 []: Wiki-Arbeit, Code-Beiträge \n- Alle Beta-Tester auf itch.io und Google Play\n -text.discord = Trete dem Mindustry Discord bei! -text.gameover = Der Kern wurde zerstört. -text.highscore = [YELLOW] Neuer Highscore! -text.lasted = Du hast bis zur folgenden Welle überlebt -text.level.highscore = High Score: [accent] {0} -text.level.delete.title = Löschen bestätigen -text.level.delete = Bist du sicher, dass du die Karte \"[orange] {0}\" löschen möchtest? -text.level.select = Level Auswahl -text.level.mode = Spielmodus: -text.savegame = Spiel speichern -text.loadgame = Spiel laden -text.joingame = Spiel beitreten -text.quit = Verlassen -text.about.button = Info -text.name = Name: -text.public = Öffentlich -text.players = {0} Spieler online -text.players.single = {0} Spieler online -text.server.mismatch = Paketfehler: Mögliche Client / Server-Version stimmt nicht überein. Stell sicher, dass du und der Host die neueste Version von Mindustry haben! -.server.closing = [accent] Server wird geschlossen... -text.server.kicked.kick = Du wurdest vom Server gekickt! -text.server.kicked.invalidPassword = Falsches Passwort. -text.server.connected = {0} ist beigetreten -text.server.disconnected = {0} hat die Verbindung getrennt. -text.nohost = Server kann nicht auf einer benutzerdefinierten Karte gehostet werden! -text.hostserver = Server hosten -text.host = Host -text.hosting = [accent] Server wird geöffnet... -text.hosts.refresh = Aktualisieren -text.hosts.discovering = Suche nach LAN-Spielen -text.server.refreshing = Server wird aktualisiert -text.hosts.none = [lightgray] Keine LAN Spiele gefunden! -text.host.invalid = [scarlet] Kann keine Verbindung zum Host herstellen. -text.server.add = Server hinzufügen -text.server.delete = Bist du dir sicher das du diesen Server löschen möchtest? -text.server.hostname = Host: {0} -text.server.edit = Server bearbeiten -text.joingame.byip = Über IP beitreten ... -text.joingame.title = Spiel beitreten -text.joingame.ip = IP: -text.disconnect = Verbindung unterbrochen. -text.connecting = [accent] Verbindet... -text.connecting.data = [accent] Weltdaten werden geladen... -text.connectfail = [crimson] Verbindung zum Server konnte nicht hergestellt werden: [orange]{0} -text.server.port = Port: -text.server.invalidport = Falscher Port! -text.server.error = [crimson] Fehler beim Hosten des Servers: [orange] {0} -text.tutorial.back = < Zurück -text.tutorial.next = Weiter > -text.save.new = Neuer Spielstand -text.save.overwrite = Möchten du diesen Spielstand wirklich überschreiben? -text.overwrite = Überschreiben -text.save.none = Keine Spielstände gefunden! -text.saveload = [accent] Speichern ... -text.savefail = Fehler beim Speichern des Spiels! -text.save.delete.confirm = Möchtest du diesen Spielstand wirklich löschen? -text.save.delete = Löschen -text.save.export = Spielstand exportieren -text.save.import.invalid = [orange] Dieser Spielstand ist ungültig! -text.save.import.fail = [crimson] Spielstand konnte nicht importiert werden: [orange] {0} -text.save.export.fail = [crimson] Spielstand konnte nicht exportiert werden: [orange] {0} -text.save.import = Spielstand importieren -text.save.newslot = Name speichern: -text.save.rename = Umbenennen -text.save.rename.text = Neuer Name -text.selectslot = Wähle einen Spielstand -text.slot = [accent] Platz {0} -text.save.corrupted = [orange] Datei beschädigt oder ungültig! -text.empty = -text.on = An -text.off = Aus -text.save.autosave = Automatisches Speichern: {0} -text.save.map = Karte: {0} -text.save.wave = Welle: {0} -text.save.date = Zuletzt gespeichert: {0} -text.confirm = Bestätigen -text.delete = Löschen -text.ok = OK -text.open = Öffnen -text.cancel = Abbruch -text.openlink = Link öffnen -text.back = Zurück -text.quit.confirm = Willst du wirklich aufhören? -text.loading = [accent] Wird geladen ... -text.wave = [orange] Welle {0} -text.wave.waiting = Welle in {0} -text.waiting = Warten... -text.enemies = {0} Feinde -text.enemies.single = {0} Feind -text.loadimage = Bild laden -text.saveimage = Bild speichern -text.editor.badsize = [orange]Ungültige Bildabmessungen! [] Gültige Kartenabmessungen: {0} -text.editor.errorimageload = Fehler beim Laden des Bildes: [orange] {0} -text.editor.errorimagesave = Fehler beim Speichern des Bildes: [orange] {0} -text.editor.generate = Generieren -text.editor.resize = Grösse\nanpassen -text.editor.loadmap = Karte\nladen -text.editor.savemap = Karte\nspeichern -text.editor.loadimage = Bild\nladen -text.editor.saveimage = Bild\nspeichern -text.editor.unsaved = [crimson] Du hast Änderungen nicht gespeichert [] Möchtest du wirklich aufhören? -text.editor.brushsize = Pinselgrösse: {0} -text.editor.noplayerspawn = Diese Karte hat keinen Spielerspawnpunkt! -text.editor.manyplayerspawns = Maps können nicht mehr als einen Spawnpunkt des Spielers haben! -text.editor.manyenemyspawns = Kann nicht mehr als {0} feindliche Spawnpunkte haben! -text.editor.resizemap = Grösse der Karte ändern -text.editor.resizebig = [crimson] Warnung! [] Karten, die grösser als 256 Einheiten sind, können ruckeln und instabil sein. -text.editor.mapname = Map Name -text.editor.overwrite = [accent] Warnung! Dies überschreibt eine vorhandene Map. -text.editor.failoverwrite = [crimson] Die Standardkarte kann nicht überschrieben werden! -text.editor.selectmap = Wähle eine Map zum Laden: -text.width = Breite: -text.height = Höhe: -text.randomize = Zufällig -text.apply = Anwenden -text.update = Aktualisieren -text.menu = Menü -text.play = Spielen -text.load = Laden -text.save = Speichern -text.settings = Einstellungen -text.tutorial = Tutorial -text.editor = Bearbeiter -text.mapeditor = Karten Bearbeiter -text.donate = Spenden -text.settings.reset = Auf Standard zurücksetzen -text.settings.controls = Steuerung -text.settings.game = Spiel -text.settings.sound = Audio -text.settings.graphics = Grafiken -text.upgrades = Verbesserungen -text.purchased = [LIME] Erstellt! -text.weapons = Waffen -text.paused = Pausiert -text.respawn = Respawn in -text.error.title = [crimson] Ein Fehler ist aufgetreten -text.error.crashmessage = [SCARLET] Es ist ein unerwarteter Fehler aufgetreten, der einen Absturz verursacht hätte. [] Bitte geben Sie die genauen Umstände an, unter denen dieser Fehler passiert ist, für den Entwickler: [ORANGE] anukendev@gmail.com [] -text.error.crashtitle = EIn Fehler ist aufgetreten! -text.mode.break = Zerstörungsmodus: {0} -text.mode.place = Platzierungsmodus: {0} -placemode.hold.name = Zeile -placemode.areadelete.name = Gebiet -placemode.touchdelete.name = berühren -placemode.holddelete.name = halten -placemode.none.name = keine -placemode.touch.name = berühren -placemode.cursor.name = Mauszeiger -text.blocks.extrainfo = [accent] Extra Blockinfo: -text.blocks.blockinfo = Blockinfo: -text.blocks.powercapacity = Energiekapazität -text.blocks.powershot = Energie / Schuss -text.blocks.powersecond = Energie / Sekunde -text.blocks.powerdraindamage = Energieabnahme / Schaden -text.blocks.shieldradius = Schildradius -text.blocks.itemspeedsecond = Gegenstands Geschwindigkeit / Sekunde -text.blocks.range = Reichweite -text.blocks.size = Grösse -text.blocks.powerliquid = Energie / Flüssigkeit -text.blocks.maxliquidsecond = Max Flüssigkeit / Sekunde -text.blocks.liquidcapacity = Flüssigkeitskapazität -text.blocks.liquidsecond = Flüssigkeit / Sekunde -text.blocks.damageshot = Schaden / Schuss -text.blocks.ammocapacity = Munitionskapazität -text.blocks.ammo = Munition -text.blocks.ammoitem = Munition / Gegenstand -text.blocks.maxitemssecond = Max Gegenstände / Sekunde -text.blocks.powerrange = Energiereichweite -text.blocks.lasertilerange = Laser Reichweite -text.blocks.capacity = Kapazität -text.blocks.itemcapacity = Gegenstand Kapazität -text.blocks.maxpowergenerationsecond = Max Energieerzeugung / Sekunde -text.blocks.powergenerationsecond = Energieerzeugung / Sekunde -text.blocks.generationsecondsitem = Generation Sekunden / Gegenstand -text.blocks.input = Eingabe -text.blocks.inputliquid = Flüssigkeiten Eingabe -text.blocks.inputitem = Eingabe Gegenstand -text.blocks.output = Ausgabe -text.blocks.secondsitem = Sekunden / Item -text.blocks.maxpowertransfersecond = Max Energieübertragung / Sekunde -text.blocks.explosive = Hochexplosiv! -text.blocks.repairssecond = Reparaturen / Sekunde -text.blocks.health = Lebenspunkte -text.blocks.inaccuracy = Ungenauigkeit -text.blocks.shots = Schüsse -text.blocks.shotssecond = Schüsse / Sekunde -text.blocks.fuel = Treibstoff -text.blocks.fuelduration = Treibstoffdauer -text.blocks.maxoutputsecond = Max Ausgabe / Sekunde -text.blocks.inputcapacity = Eingabekapazität -text.blocks.outputcapacity = Ausgabekapazität -text.blocks.poweritem = Energie / Gegenstand -text.placemode = Platzierungsmodus -text.breakmode = Zerstörungsmodus -text.health = Lebenspunkte +credits.text = Created by [ROYAL]Anuken[] - [SKY]anukendev@gmail.com[]\n\n[GRAY](In case you can't tell, this text is currently unfinished.\nTranslators, don't edit it yet!) +credits = Danksagungen +contributors = Übersetzer und Mitwirkende +discord = Trete dem Mindustry Discord bei! +link.discord.description = Der offizielle Mindustry Discord Chatroom +link.github.description = Quellcode des Spiels +link.dev-builds.description = Entwicklungs-Builds (instabil) +link.trello.description = Offizielles Trello Board für geplante Features +link.itch.io.description = itch.io Seite mit Downloads und der Web-Version des Spiels +link.google-play.description = Google Play Store Seite +link.wiki.description = Offizelles Mindustry Wiki +linkfail = Fehler beim Öffnen des Links!\nDie URL wurde in die Zwischenablage kopiert. +screenshot = Screenshot saved to {0} +screenshot.invalid = Map too large, potentially not enough memory for screenshot. +gameover = Der Kern wurde zerstört. +gameover.pvp = Das[accent] {0}[] Team ist siegreich! +highscore = [YELLOW] Neuer Highscore! +stat.wave = Wellen Besiegt:[accent] {0} +stat.enemiesDestroyed = Gegner Zerstört:[accent] {0} +stat.built = Gebäude Gebaut:[accent] {0} +stat.destroyed = Gebäude Zerstört:[accent] {0} +stat.deconstructed = Gebäude Abgebaut:[accent] {0} +stat.delivered = Resources Launched: +stat.rank = Final Rank: [accent]{0} +placeline = You have selected a block.\nYou can[accent] place in a line[] by[accent] holding down your finger for a few seconds[] and dragging in a direction.\nTry it. +removearea = You have selected removal mode.\nYou can[accent] remove blocks in a rectangle[] by[accent] holding down your finger for a few seconds[] and dragging.\nTry it. +launcheditems = [accent]Launched Items +map.delete = Bist du sicher, dass du die Karte "[accent]{0}[]" löschen möchtest? +level.highscore = High Score: [accent]{0} +level.select = Level Auswahl +level.mode = Spielmodus: +showagain = Nächstes mal nicht mehr anzeigen +coreattack = < Die Basis wird angegriffen! > +nearpoint = [[ [scarlet]SOFORT DEN DROP POINT VERLASSEN[] ]\nVernichtung droht +outofbounds = [[ OUT OF BOUNDS ]\n[]self-destruct in {0} +database = Core Database +savegame = Spiel speichern +loadgame = Spiel laden +joingame = Spiel beitreten +addplayers = Hinzufügen/Entfernen von Spielern +customgame = Benutzerdefiniertes Spiel +newgame = Neues Spiel +none = +minimap = Minimap +close = Schließen +quit = Verlassen +maps = Karten +continue = Weiter +maps.none = [LIGHT_GRAY]Keine Karten gefunden! +about.button = Info +name = Name: +noname = Pick a[accent] player name[] first. +filename = Dateiname: +unlocked = Neuen Block freigeschaltet! +completed = [accent]Completed +techtree = Tech Tree +research.list = [LIGHT_GRAY]Forschung: +research = Forschung +researched = [LIGHT_GRAY]{0} erforscht. +players = {0} Spieler online +players.single = {0} Spieler online +server.closing = [accent]Schließe den Server... +server.kicked.kick = Du wurdest vom Server gekickt! +server.kicked.serverClose = Server geschlossen. +server.kicked.clientOutdated = Veralteter Client! Aktualisiere dein Spiel! +server.kicked.serverOutdated = Veralteter Server! Bitte den Host um ein Update! +server.kicked.banned = Du wurdest vom Server verbannt. +server.kicked.recentKick = Du wurdest gerade gekickt.\nWarte bevor du dich wieder verbindest. +server.kicked.nameInUse = Es ist bereits ein Spieler \nmit diesem Namen auf dem Server. +server.kicked.nameEmpty = Dein Name muss zumindest einen Buchstaben oder eine Zahl enthalten. +server.kicked.idInUse = Du bist bereits auf dem Server! Anmeldungen mit zwei Accounts sind nicht gestattet. +server.kicked.customClient = Der Server akzeptiert keine Custom Builds von Mindustry. Lade dir die offizielle Version herunter. +server.kicked.gameover = Game over! +host.info = Der [accent]host[] Knopf startet einen Server auf den Ports [scarlet]6567[] und [scarlet]6568.[]\nJeder im gleichen [LIGHT_GRAY]W-Lan oder lokalem Netzwerk[] sollte deinen Server in seiner Server Liste sehen können.\n\nWenn du Leuten die Verbindung über IP ermöglichen willst, benötigst du [accent]Port-Forwarding[].\n\n[LIGHT_GRAY]Hinweis: Falls es Probleme mit der Verbindung im Netzwerk gibt, stell sicher, dass Mindustry in deinen Firewall Einstellungen Zugriff auf das lokale Netzwerk hat. +join.info = Hier kannst du eine [accent]Server IP[] eingeben um dich zu verbinden oder Server im [accent]lokalem Netzwerk[] entdecken und dich mit ihnen verbinden.\nSowohl Spielen über das lokale Netzwerk als auch Spielen über das Internet werden unterstützt.\n\n[LIGHT_GRAY]Hinweis: Es gibt keine globale Server Liste; Wenn du dich mit jemand per IP verbinden willst musst du den Host nach seiner IP fragen. +hostserver = Server hosten +hostserver.mobile = Host\nGame +host = Host +hosting = [accent] Server wird geöffnet... +hosts.refresh = Aktualisieren +hosts.discovering = Suche nach LAN-Spielen +server.refreshing = Server wird aktualisiert +hosts.none = [lightgray] Keine LAN Spiele gefunden! +host.invalid = [scarlet] Kann keine Verbindung zum Host herstellen. +trace = Spieler verfolgen +trace.playername = Spielername: [accent]{0} +trace.ip = IP: [accent]{0} +trace.id = Eindeutige ID: [accent]{0} +trace.mobile = Mobile Client: [accent]{0} +trace.modclient = Custom Client: [accent]{0} +invalidid = Ungültige Client ID! Berichte den Bug. +server.bans = Bans +server.bans.none = Keine gebannten Spieler gefunden! +server.admins = Admins +server.admins.none = Keine Admins gefunden! +server.add = Server hinzufügen +server.delete = Bist du dir sicher, dass du diesen Server löschen möchtest? +server.hostname = Host: {0} +server.edit = Server bearbeiten +server.outdated = [crimson]Veralteter Server![] +server.outdated.client = [crimson]Veralteter Client![] +server.version = [lightgray]Version: {0} +server.custombuild = [yellow]Custom Build +confirmban = Bist du sicher, dass du diesen Spieler verbannen möchtest? +confirmkick = Bist du sicher, dass du diesen Spieler kicken willst? +confirmunban = Bist du sicher, dass du die Verbannung des Spielers rückgängig machen willst? +confirmadmin = Bist du sicher, dass du diesen Spieler zu einem Admin machen möchtest? +confirmunadmin = Bis du sicher, dass dieser Spieler kein Admin mehr sein soll? +joingame.title = Spiel beitreten +joingame.ip = IP: +disconnect = Verbindung unterbrochen. +disconnect.data = Fehler beim Laden der Welt! +connecting = [accent] Verbinde... +connecting.data = [accent] Welt wird geladen... +server.port = Port: +server.addressinuse = Adresse bereits in Verwendung! +server.invalidport = Falscher Port! +server.error = [crimson] Fehler beim Hosten des Servers: [accent] {0} +save.old = Dieser Spielstand ist von einer älteren Version des Spiels, und kann nicht mehr verwendet werden.\n\n[LIGHT_GRAY]Abwärtskompatibilität von Speicherständen wird in der 4.0 Vollversion hinzugefügt. +save.new = Neuer Spielstand +save.overwrite = Möchtest du diesen Spielstand wirklich überschreiben? +overwrite = Überschreiben +save.none = Keine Spielstände gefunden! +saveload = [accent] Speichern ... +savefail = Fehler beim Speichern des Spiels! +save.delete.confirm = Möchtest du diesen Spielstand wirklich löschen? +save.delete = Löschen +save.export = Spielstand exportieren +save.import.invalid = [accent] Dieser Spielstand ist ungültig! +save.import.fail = [crimson] Spielstand konnte nicht importiert werden: [accent] {0} +save.export.fail = [crimson] Spielstand konnte nicht exportiert werden: [accent] {0} +save.import = Spielstand importieren +save.newslot = Name speichern: +save.rename = Umbenennen +save.rename.text = Neuer Name +selectslot = Wähle einen Spielstand +slot = [accent] Platz {0} +save.corrupted = [accent] Datei beschädigt oder ungültig! +empty = +on = An +off = Aus +save.autosave = Automatisches Speichern: {0} +save.map = Karte: {0} +save.wave = Welle: {0} +save.difficulty = Schwierigkeitsgrad: {0} +save.date = Zuletzt gespeichert: {0} +save.playtime = Spielzeit: {0} +warning = Warning. +confirm = Bestätigen +delete = Löschen +ok = OK +open = Öffnen +customize = Customize +cancel = Abbruch +openlink = Link öffnen +copylink = Kopiere Link +back = Zurück +quit.confirm = Willst du wirklich aufhören? +changelog.title = Changelog +changelog.loading = Lade Änderungshistorie... +changelog.error.android = [accent]Beachte: Die Änderungshistorie funktioniert manchmal nicht auf Android 4.4 (und älter)!\nDies liegt an einem Android bug. +changelog.error.ios = [accent]Die Änderungshistorie wird aktuell nicht von IOS unterstützt. +changelog.error = [scarlet]Fehler beim Laden der Änderungshistorie!\nPrüfe deine Internetverbindung. +changelog.current = [yellow][[Current version] +changelog.latest = [accent][[Latest version] +loading = [accent]Wird geladen ... +saving = [accent]Speichere... +wave = [accent]Welle {0} +wave.waiting = Welle in {0} +wave.waveInProgress = [LIGHT_GRAY]Wave in progress +waiting = Warten... +waiting.players = Warte auf Spieler... +wave.enemies = [LIGHT_GRAY]{0} Gegner verbleiben +wave.enemy = [LIGHT_GRAY]{0} Gegner verbleiben +loadimage = Bild laden +saveimage = Bild speichern +unknown = Unbekannt +custom = Benutzerdefiniert +builtin = Enthalten +map.delete.confirm = Bist du sicher, dass du diese Karte löschen willst? Die Aktion kann nicht rückgänig gemacht werden! +map.random = [accent]Zufällige Karte +map.nospawn = Diese Karte hat keine Kerne in denen die Spieler beginnen können! Füge einen [ROYAL]blue[] Kern zu dieser Karte im Editor hinzu. +map.nospawn.pvp = Diese Karte hat keine gegnerischen Kerne wo Gegner starten könnten! Füge über den Editor [SCARLET] rote[] Kerne zu dieser Karte hinzu. +map.nospawn.attack = This map does not have any enemy cores for player to attack! Add[SCARLET] red[] cores to this map in the editor. +map.invalid = Fehler beim Laden der Karte: Beschädigtes oder invalide Karten Datei. +editor.brush = Pinsel +editor.openin = Öffne im Editor +editor.oregen = Erze generieren +editor.oregen.info = Erze generiert: +editor.mapinfo = Karten Info +editor.author = Autor: +editor.description = Beschreibung: +editor.waves = Wellen: +editor.rules = Rules: +editor.ingame = Edit In-Game +waves.title = Wellen +waves.remove = Entfernen +waves.never = +waves.every = every +waves.waves = Welle(n) +waves.perspawn = per spawn +waves.to = to +waves.boss = Boss +waves.preview = Vorschau +waves.edit = Bearbeiten... +waves.copy = Aus der Zwischenablage kopieren +waves.load = Aus der Zwischenablage laden +waves.invalid = Ungültige Wellen in der Zwischenablage. +waves.copied = Wellen kopiert. +editor.default = [LIGHT_GRAY] +edit = Bearbeiten... +editor.name = Name: +editor.spawn = Spawn Unit +editor.removeunit = Remove Unit +editor.teams = Teams +editor.elevation = Höhe +editor.errorload = Fehler beim laden der Datei:\n[accent]{0} +editor.errorsave = Fehler beim speichern der Datei:\n[accent]{0} +editor.errorimage = That's an image, not a map. Don't go around changing extensions expecting it to work.\n\nIf you want to import a legacy map, use the 'import legacy map' button in the editor. +editor.errorlegacy = This map is too old, and uses a legacy map format that is no longer supported. +editor.errorheader = This map file is either not valid or corrupt. +editor.errorname = Karte hat keinen Namen. +editor.update = Update +editor.randomize = Randomize +editor.apply = Anwenden +editor.generate = Generieren +editor.resize = Grösse\nanpassen +editor.loadmap = Karte\nladen +editor.savemap = Karte\nspeichern +editor.saved = Gespeichert! +editor.save.noname = Deine Karte hat keinen Namen! Setze einen Namen im [accent]Karten Info[] Menu. +editor.save.overwrite = Deine Karte überschreibt eine built-in Karte! Wähle einen anderen Karten Namen im [accent]'Karten info'[] Menu. +editor.import.exists = [scarlet]Fehler beim Import:[] Ein built-in Karte namens '{0}' existiert bereits! +editor.import = Import... +editor.importmap = Importiere Karte +editor.importmap.description = Importiere von einer bestehende Karte +editor.importfile = Importiere Datei +editor.importfile.description = Importiere aus einer Karten Datei +editor.importimage = Importiere Terrain Bild +editor.importimage.description = Importiere aus einer Terrain Bild Datei +editor.export = Export... +editor.exportfile = Export in Datei +editor.exportfile.description = Exportiere in eine Karten Datei +editor.exportimage = Export in Terrain Bild Datei +editor.exportimage.description = Exportiere in eine Karten Bild Datei +editor.loadimage = Bild\nladen +editor.saveimage = Bild\nspeichern +editor.unsaved = [crimson] Du hast Änderungen nicht gespeichert [] Möchtest du wirklich aufhören? +editor.resizemap = Grösse der Karte ändern +editor.mapname = Karten Name +editor.overwrite = [accent] Warnung! Dies überschreibt eine vorhandene Karte. +editor.overwrite.confirm = [scarlet]Warnung![] Eine Karte mit diesem Namen existiert bereits. Bist du sicher, dass du sie überschreiben willst? +editor.selectmap = Wähle eine Karte zum Laden: +filters.empty = [LIGHT_GRAY]No filters! Add one with the button below. +filter.distort = Distort +filter.noise = Noise +filter.ore = Erz +filter.rivernoise = River Noise +filter.scatter = Scatter +filter.terrain = Terrain +filter.option.scale = Scale +filter.option.chance = Chance +filter.option.mag = Magnitude +filter.option.threshold = Threshold +filter.option.circle-scale = Circle Scale +filter.option.octaves = Octaves +filter.option.falloff = Falloff +filter.option.block = Block +filter.option.floor = Floor +filter.option.wall = Wand +filter.option.ore = Erz +filter.option.floor2 = Secondary Floor +filter.option.threshold2 = Secondary Threshold +filter.option.radius = Radius +filter.option.percentile = Percentile +width = Breite: +height = Höhe: +menu = Menü +play = Spielen +load = Laden +save = Speichern +fps = FPS: {0} +tps = TPS: {0} +ping = Ping: {0}ms +language.restart = Bitte Starte dein Spiel neu, damit die Sprach-Einstellung aktiv werden. +settings = Einstellungen +tutorial = Tutorial +editor = Editor +mapeditor = Karten Editor +donate = Spenden +abandon = Abandon +abandon.text = Diese Zone sowie alle Ressourcen werden dem Gegner überlassen. +locked = Locked +complete = [LIGHT_GRAY]Complete: +zone.requirement = Welle {0} in Zone {1} +resume = Zu Zone zurückkehren:\n[LIGHT_GRAY]{0} +bestwave = [LIGHT_GRAY]Best: {0} +launch = Launch +launch.title = Launch Successful +launch.next = [LIGHT_GRAY]nächste Möglichkeit bei Welle {0} +launch.unable = [scarlet]Unable to LAUNCH.[] Enemies. +launch.confirm = This will launch all resources in your core.\nYou will not be able to return to this base. +uncover = Uncover +configure = Configure Loadout +configure.locked = [LIGHT_GRAY]Reach wave {0}\nto configure loadout. +zone.unlocked = [LIGHT_GRAY]{0} freigeschaltet. +zone.requirement.complete = Welle {0} erreicht:\n{1} Anforderungen der Zone erfüllt. +zone.config.complete = Wave {0} reached:\nLoadout config unlocked. +zone.resources = Resources Detected: +add = Hinzufügen... +boss.health = Boss Health +connectfail = [crimson] Verbindung zum Server konnte nicht hergestellt werden: [accent]{0} +error.unreachable = Server nicht erreichbar. +error.invalidaddress = Ungültige Adresse. +error.timedout = Zeitüberschreitung!\nStelle sicher, dass die Portweiterleitung auf dem Host richtig eingerichtet ist, und die Adresse stimmt! +error.mismatch = Paketfehler:\nClient und Server passen möglicherweise nicht zusammen.\nStelle sicher, dass du und der Host jeweils die neueste Version von Mindustry haben! +error.alreadyconnected = Bereits verbunden. +error.mapnotfound = Kartendatei nicht gefunden! +error.io = Network I/O error. +error.any = Unbekannter Netzwerkfehler. +zone.groundZero.name = Ground Zero +zone.desertWastes.name = Desert Wastes +zone.craters.name = The Craters +zone.frozenForest.name = Frozen Forest +zone.ruinousShores.name = Ruinous Shores +zone.stainedMountains.name = Stained Mountains +zone.desolateRift.name = Desolate Rift +zone.nuclearComplex.name = Nuclear Production Complex +zone.overgrowth.name = Overgrowth +zone.tarFields.name = Tar Fields +settings.language = Sprache +settings.reset = Auf Standard zurücksetzen +settings.rebind = Zuweisen +settings.controls = Steuerung +settings.game = Spiel +settings.sound = Audio +settings.graphics = Grafiken +settings.cleardata = Spieldaten zurücksetzen... +settings.clear.confirm = Bist du sicher, dass du die Spieldaten zurücksetzen willst?\n Diese Aktion kann nicht rückgänig gemacht werden! +settings.clearall.confirm = [scarlet]Warnung![]\nDas wird jegliche Spieldaten zurücksetzen inklusive Speicherstände, Karten, Freischaltungen und Tastenbelegungen.\n Nachdem du 'OK' drückst wird alles zurückgesetzt und das Spiel schließt sich automatisch. +settings.clearunlocks = Freischaltungen zurücksetzen +settings.clearall = Alles zurücksetzen +paused = Pausiert +yes = Ja +no = Nein +info.title = [accent]Info +error.title = [crimson] Ein Fehler ist aufgetreten +error.crashtitle = Ein Fehler ist aufgetreten! +blocks.input = Input +blocks.output = Output +blocks.booster = Booster +block.unknown = [LIGHT_GRAY]??? +blocks.powercapacity = Kapazität +blocks.powershot = Stromverbrauch/Schuss +blocks.targetsair = Visiert Luft Einheiten an +blocks.targetsground = Visiert Boden Einheiten an +blocks.itemsmoved = Bewegungsgeschwindigkeit +blocks.launchtime = Time Between Launches +blocks.shootrange = Reichweite +blocks.size = Größe +blocks.liquidcapacity = Flüssigkeitskapazität +blocks.powerrange = Stromreichweite +blocks.poweruse = Stromverbrauch +blocks.powerdamage = Stromverbrauch/Schadenspunkt +blocks.itemcapacity = Materialkapazität +blocks.basepowergeneration = Basis-Stromerzeugung +blocks.productiontime = Produktionszeit +blocks.repairtime = Block Full Repair Time +blocks.speedincrease = Geschwindigkeitserhöhung +blocks.range = Reichweite +blocks.drilltier = Abbaubare Erze +blocks.drillspeed = Bohrgeschwindigkeit +blocks.boosteffect = Boost Effect +blocks.maxunits = Max Active Units +blocks.health = Lebenspunkte +blocks.buildtime = Build Time +blocks.inaccuracy = Ungenauigkeit +blocks.shots = Schüsse +blocks.reload = Schüsse/Sekunde +blocks.ammo = Ammo +bar.drillspeed = Bohrgeschwindigkeit: {0}/s +bar.efficiency = Effizienz: {0}% +bar.powerbalance = Power: {0} +bar.poweramount = Power: {0} +bar.poweroutput = Power Output: {0} +bar.items = Items: {0} +bar.liquid = Flüssigkeit +bar.heat = Hitze +bar.power = Power +bar.progress = Build Progress +bar.spawned = Units: {0}/{1} +bullet.damage = [stat]{0}[lightgray] Schaden +bullet.splashdamage = [stat]{0}[lightgray] area dmg ~[stat] {1}[lightgray] tiles +bullet.incendiary = [stat]entzündend +bullet.homing = [stat]verfolgend +bullet.shock = [stat]schock +bullet.frag = [stat]explosiv +bullet.knockback = [stat]{0}[lightgray] knockback +bullet.freezing = [stat]gefrierend +bullet.tarred = [stat]geteert +bullet.multiplier = [stat]{0}[lightgray]x ammo multiplier +bullet.reload = [stat]{0}[lightgray]x reload +unit.blocks = Blöcke +unit.powersecond = Stromeinheiten/Sekunde +unit.liquidsecond = Flüssigkeitseinheiten/Sekunde +unit.itemssecond = Materialeinheiten/Sekunde +unit.liquidunits = Flüssigkeitseinheiten +unit.powerunits = Stromeinheiten +unit.degrees = Grad +unit.seconds = Sekunden +unit.persecond = /sec +unit.timesspeed = x speed +unit.percent = % +unit.items = Materialeinheiten +category.general = Generell +category.power = Strom +category.liquids = Flüssigkeiten +category.items = Materialien +category.crafting = Erzeugung +category.shooting = Schießen +category.optional = Optional Enhancements +setting.landscape.name = Lock Landscape +setting.shadows.name = Schatten +setting.linear.name = Linear Filtering +setting.animatedwater.name = Animiertes Wasser +setting.animatedshields.name = Animierte Schilde +setting.antialias.name = Antialias[LIGHT_GRAY] (benötigt Neustart)[] +setting.indicators.name = Ally Indicators +setting.autotarget.name = Auto-Zielauswahl +setting.fpscap.name = Max FPS +setting.fpscap.none = kein +setting.fpscap.text = {0} FPS +setting.swapdiagonal.name = Always Diagonal Placement +setting.difficulty.training = Training setting.difficulty.easy = Leicht setting.difficulty.normal = Normal setting.difficulty.hard = Schwer setting.difficulty.insane = Unmöglich -setting.difficulty.purge = Auslöschung setting.difficulty.name = Schwierigkeit -setting.screenshake.name = Bildschirm wackeln -setting.smoothcam.name = Glatte Kamera -setting.indicators.name = Feind Indikatoren +setting.screenshake.name = Bildschirmwackeln setting.effects.name = Effekte anzeigen -setting.sensitivity.name = Kontroller Empfindlichkeit +setting.sensitivity.name = Controller-Empfindlichkeit setting.saveinterval.name = Autosave Häufigkeit setting.seconds = {0} Sekunden +setting.fullscreen.name = Vollbild +setting.borderlesswindow.name = Borderless Window[LIGHT_GRAY] (may require restart) setting.fps.name = Zeige FPS setting.vsync.name = VSync -setting.lasers.name = Zeige Energielaser -setting.healthbars.name = Zeige Objekt Lebensbalken -setting.pixelate.name = Pixel Bildschirm +setting.lasers.name = Zeige Stromlaser +setting.pixelate.name = Verpixeln [LIGHT_GRAY](Könnte die Leistung beeinträchtigen) +setting.minimap.name = Zeige die Minimap setting.musicvol.name = Musiklautstärke setting.mutemusic.name = Musik stummschalten -setting.sfxvol.name = Audioeffekte Lautstärke +setting.sfxvol.name = Audioeffekt-Lautstärke setting.mutesound.name = Audioeffekte stummschalten -map.maze.name = Labyrinth -map.fortress.name = Festung -map.sinkhole.name = Sinkloch -map.caves.name = Höhlen -map.volcano.name = Vulkan -map.caldera.name = Lavakessel -map.scorch.name = Flammen -map.desert.name = Wüste -map.island.name = Insel -map.grassland.name = Grasland -map.tundra.name = Kältesteppe -map.spiral.name = Spirale -map.tutorial.name = Tutorial -tutorial.intro.text = [gelb] Willkommen zum Tutorial [] Um zu beginnen, drücke 'weiter'. -tutorial.moveDesktop.text = Verwende zum Verschieben die Tasten [orange] [[WASD] []. Halte [orange] Shift [] gedrückt, um zu erhöhen. Halte [orange] CTRL/STRG [] gedrückt, während du das [orange] Scrollrad [] zum Vergrössern oder Verkleinern verwendest. -tutorial.shoot.text = Ziele mit der Maus, halte die [orange] linke Maustaste [] gedrückt, um zu schiessen. Versuche es mit dem [gelben] Ziel []. -tutorial.moveAndroid.text = Um die Ansicht zu verschieben, ziehe einen Finger über den Bildschirm. Drücke und ziehe, um zu vergrössern oder zu verkleinern. -tutorial.placeSelect.text = Versuche, ein [gelbes] Förderband [] aus dem Blockmenü unten rechts auszuwählen. -tutorial.placeConveyorDesktop.text = Verwende das [orange] [[scrollwheel] [], um das Förderband nach vorne zu bewegen [orange] vorwärts [], und platziere es dann an der [gelben] markierten Stelle [] mit [orange] [[linke Maustaste] []. -tutorial.placeConveyorAndroid.text = Verwende die [orange] [[rotieren-Taste] [], um das Förderband nach vorne [orange] zu drehen [], ziehe es mit einem Finger in Position und platziere es dann an der [gelben] markierten Stelle [] mit der [Orange] [[Häkchen][]. -tutorial.placeConveyorAndroidInfo.text = Alternativ kannst du das Fadenkreuzsymbol unten links drücken, um zum [orange] [[touch mode] [] zu wechseln, und Blöcke durch Tippen auf den Bildschirm platzieren. Im Touch-Modus können Blöcke mit dem Pfeil unten links gedreht werden. Drücke [gelb] neben [], um es auszuprobieren. -tutorial.placeDrill.text = Wähle nun einen [gelben] Steinbohrer [] an der markierten Stelle aus und platziere ihn. -tutorial.blockInfo.text = Wenn du mehr über einen Block erfahren möchtest, tippe oben rechts auf das [orange] Fragezeichen [], um dessen Beschreibung zu lesen. -tutorial.deselectDesktop.text = Du kannst einen Block mit [Orange] [[Rechte Maustaste] [] abwählen. -tutorial.deselectAndroid.text = Du kannst einen Block abwählen, indem du die [orange] X [] -Taste drücken. -tutorial.drillPlaced.text = Der Bohrer erzeugt nun [gelben] Stein, [] gib den Stein nun auf das Förderband aus und bewege ihn dann in den [gelben] Kern []. -tutorial.drillInfo.text = Verschiedene Erze benötigen unterschiedliche Bohrer. Stein erfordert Steinbohrer, Eisen erfordert Eisenbohrer usw. -tutorial.drillPlaced2.text = Wenn du Gegenstände in den Kern verschiebst, steckst du sie in dein [gelbes] Inventar [] oben links. Das Platzieren von Blöcken verwendet Gegenstände aus deinem Inventar. -tutorial.moreDrills.text = Du kannst so viele Bohrer und Förderer miteinander verbinden wie du lust hast. -tutorial.deleteBlock.text = Du kannst Blöcke löschen, indem du mit der [orange] rechte Maustaste [] auf dem Block klickst, den du löschen möchtest. Versuche, dieses Förderband zu löschen. -tutorial.deleteBlockAndroid.text = Du kannst Blöcke löschen, indem du [orange] das Fadenkreuz [] im [orange] Pausenmodusmenü [] links unten auswählst und auf einen Block tippst. Versuche, dieses Fliessband zu löschen. -tutorial.placeTurret.text = Wähle nun einen [gelben] Turm [] an der [gelben] markierten Stelle [] und platziere ihn. -tutorial.placedTurretAmmo.text = Dieser Turm nimmt nun [gelbe] Munition [] vom Förderband an. Du kannst sehen, wie viel Munition es hat, indem du darüber schweben und den [grünen] grünen Balken [] prüfen. -tutorial.turretExplanation.text = Geschütze schiessen automatisch auf den nächsten Feind in Reichweite, solange sie genug Munition haben. -tutorial.waves.text = Jede [yellow] 60 [] Sekunden wird eine Welle von [coral] Feinden [] an einem bestimmten Orten erscheinen und versuchen, den Kern zu zerstören. -tutorial.coreDestruction.text = Dein Ziel ist es, den Kern [yellow] zu verteidigen. Wenn der Kern zerstört wird, verlierst du [coral] das Spiel []. -tutorial.pausingDesktop.text = Wenn du jemals eine Pause machen möchtest, drücke die [orange] Pause-Taste [] oben links oder [orange]space[], um das Spiel anzuhalten. Du kannst auch Blöcke immer noch auswählen und platzieren, während du das Spiel pausiert ist, aber Sie können sich nicht bewegen oder schiessen. -tutorial.pausingAndroid.text = Wenn du jemals eine Pause machen willst, drück einfach die [orange] Pause-Taste [] oben links, um das Spiel anzuhalten. Sie können immer noch Sachen auswählen und Blöcke während der Pause platzieren. -tutorial.purchaseWeapons.text = Du kannst neue [yellow] Waffen [] für deinen Mech kaufen, indem du das Verbesserungs-Menü unten links öffnest. -tutorial.switchWeapons.text = Schalte Waffen, indem du entweder auf das Symbol unten links klickst oder Nummern verwendest [orange] [[1-9] []. -tutorial.spawnWave.text = Hier kommt die erste Welle. Zerstöre sie. -tutorial.pumpDesc.text = In späteren Wellen müsst du möglicherweise [yellow] Pumpen [] verwenden, um Flüssigkeiten für Generatoren oder Extraktoren zu verteilen. -tutorial.pumpPlace.text = Pumpen arbeiten ähnlich wie Bohrer, ausser dass sie anstelle von Gegenständen Flüssigkeiten produzieren. Versuch mal, eine Pumpe auf das [yellow] gekennzeichnete Öl [] zu setzen. -tutorial.conduitUse.text = Stellen Sie nun eine [orange] Leitungsrohr [] von der Pumpe weg. -tutorial.conduitUse2.text = Und noch ein paar mehr ... -tutorial.conduitUse3.text = Und noch ein paar mehr ... -tutorial.generator.text = Stellen Sie nun einen [orange] Verbrennungsgenerator [] Block am Ende des Leitungsrohres auf. -tutorial.generatorExplain.text = Dieser Generator erzeugt nun [yellow] Energie [] aus dem Öl. -tutorial.lasers.text = Die Energie wird mit [yellow] Energielasern [] verteilt. Drehe und platziere einen hier. -tutorial.laserExplain.text = Der Generator wird nun Energie in den Laserblock bewegen. Ein [yellow] undurchsichtiger [] Strahl bedeutet, dass er gerade Leistung überträgt, und ein [yellow] transparenter [] Strahl bedeutet, dass dies nicht der Fall ist. -tutorial.laserMore.text = Du kannst überprüfen, wie viel Energie ein Block hat, indem du darüber schweben und den [yellow] gelben Balken [] oben prüfen. -tutorial.healingTurret.text = Dieser Laser kann verwendet werden, um einen [lime] -Reparaturgeschütz [] anzutreiben. Platziere einen hier. -tutorial.healingTurretExplain.text = Solange er Kraft hat, repariert dieser Turm in der Nähe Blöcke. [] Wenn du spielst, stelle sicher, dass du so schnell wie möglich einen in deiner Basis bekommst! -tutorial.smeltery.text = Viele Blöcke benötigen [orange] Stahl [], um eine [orange] Schmelzer [] herzustellen. Platziere einen hier. -tutorial.smelterySetup.text = Diese Schmelzer wird nun [orange] Stahl [] aus dem Eingangs-Eisen produzieren, wobei Kohle als Brennstoff verwendet wird. -tutorial.end.text = Und damit ist das Tutorial abgeschlossen! Viel Glück! -keybind.move_x.name = bewege_x -keybind.move_y.name = bewege_y -keybind.select.name = wählen -keybind.break.name = Unterbrechung -keybind.shoot.name = Schiess -keybind.zoom_hold.name = zoomen_halten -keybind.zoom.name = zoomen +setting.crashreport.name = Anonyme Absturzberichte senden +setting.chatopacity.name = Chat Opacity +setting.playerchat.name = Display In-Game Chat +keybind.title = Tasten zuweisen +category.general.name = Allgemein +category.view.name = Ansicht +category.multiplayer.name = Mehrspieler +command.attack = Angreifen +command.retreat = Rückzug +command.patrol = Patrouillieren +keybind.gridMode.name = Block Select +keybind.gridModeShift.name = Kategorie auswählen +keybind.press = Drücke eine Taste... +keybind.press.axis = Drücke eine Taste oder bewege eine Achse... +keybind.screenshot.name = Map Screenshot +keybind.move_x.name = X-Achse +keybind.move_y.name = Y-Achse +keybind.select.name = Auswählen/Schießen +keybind.diagonal_placement.name = Diagonal platzieren +keybind.pick.name = Pick Block +keybind.break_block.name = Block zerstören +keybind.deselect.name = Auswahl aufheben +keybind.shoot.name = Schießen +keybind.zoom_hold.name = Zoom halten +keybind.zoom.name = Zoomen keybind.menu.name = Menü keybind.pause.name = Pause +keybind.minimap.name = Minimap keybind.dash.name = Bindestrich -keybind.rotate_alt.name = drehen_alt +keybind.chat.name = Chat +keybind.player_list.name = Spielerliste +keybind.console.name = Konsole keybind.rotate.name = Drehen -keybind.weapon_1.name = Waffe_1 -keybind.weapon_2.name = Waffe_2 -keybind.weapon_3.name = Waffe_3 -keybind.weapon_4.name = Waffe_4 -keybind.weapon_5.name = Waffe_5 -keybind.weapon_6.name = Waffe_6 -mode.waves.name = Wellen +keybind.toggle_menus.name = Menüs umschalten +keybind.chat_history_prev.name = Chat Historie zurück +keybind.chat_history_next.name = Chat Historie vor +keybind.chat_scroll.name = Chat scrollen +keybind.drop_unit.name = Einheit absetzen +keybind.zoom_minimap.name = Minimap-Zoom +mode.help.title = Beschreibung der Modi +mode.survival.name = Survival +mode.survival.description = Der normale Modus. Ressourcen sind limitiert und Wellen kommen automatisch. mode.sandbox.name = Sandkasten -mode.freebuild.name = Freier Bau -upgrade.standard.name = Standard -upgrade.standard.description = Der Standardmech. -upgrade.blaster.name = Blaster -upgrade.blaster.description = Schiesst eine langsame, schwache Kugel. -upgrade.triblaster.name = Dreifach-Blaster -upgrade.triblaster.description = Schiesst 3 Kugeln in einer Ausbreitung. -upgrade.clustergun.name = Klumpen-Waffe -upgrade.clustergun.description = Schiesst eine ungenaue Verbreitung von explosiven Granaten. -upgrade.beam.name = Strahlkanone -upgrade.beam.description = Schiesst einen weitreichenden durchdringenden Laserstrahl. -upgrade.vulcan.name = Vulkan -upgrade.vulcan.description = Schiesst eine Flut von schnellen Kugeln. -upgrade.shockgun.name = Schock-Waffe -upgrade.shockgun.description = Erschiesst eine verheerende Explosion von geladenen Granatsplittern. -item.stone.name = Stein -item.iron.name = Eisen +mode.sandbox.description = Unendliche Ressourcen und kein Timer für Wellen. +mode.pvp.name = PvP +mode.pvp.description = Kämpfe gegen andere Spieler local. +mode.attack.name = Attack +mode.attack.description = Keine Wellen, das Ziel ist es die gegnerische Basis zu zerstören. +mode.custom = Angepasste Regeln +rules.infiniteresources = Unbegrenzte Ressourcen +rules.wavetimer = Wellen Timer +rules.waves = Wellen +rules.enemyCheat = Unbegrenzte Ressourcen für KI +rules.unitdrops = Unit Drops +rules.unitbuildspeedmultiplier = Unit Creation Speed Multiplier +rules.unithealthmultiplier = Unit Health Multiplier +rules.playerhealthmultiplier = Player Health Multiplier +rules.playerdamagemultiplier = Player Damage Multiplier +rules.unitdamagemultiplier = Unit Damage Multiplier +rules.enemycorebuildradius = Enemy Core No-Build Radius:[LIGHT_GRAY] (tiles) +rules.respawntime = Respawn Time:[LIGHT_GRAY] (sec) +rules.wavespacing = Wave Spacing:[LIGHT_GRAY] (sec) +rules.buildcostmultiplier = Build Cost Multiplier +rules.buildspeedmultiplier = Build Speed Multiplier +rules.waitForWaveToEnd = Waves wait for enemies +rules.dropzoneradius = Drop Zone Radius:[LIGHT_GRAY] (tiles) +rules.respawns = Max respawns per wave +rules.limitedRespawns = Limit Respawns +rules.title.waves = Waves +rules.title.respawns = Respawns +rules.title.resourcesbuilding = Resources & Building +rules.title.player = Players +rules.title.enemy = Enemies +rules.title.unit = Units +content.item.name = Materialien +content.liquid.name = Flüssigkeiten +content.unit.name = Einheiten +content.block.name = Blocks +content.mech.name = Mechs +item.copper.name = Kupfer +item.copper.description = Ein nützliches Material. Wird in allen Arten von Blöcken verwendet. +item.lead.name = Blei +item.lead.description = Ein grundliegendes Material. Häufig in Elektronik und Flüssigkeits-Transport-Blöcken verwendet. item.coal.name = Kohle -item.steel.name = Stahl +item.coal.description = Ein sehr häufiger vorkommender Kraftstoff. +item.graphite.name = Graphite item.titanium.name = Titan -item.dirium.name = Dirium -item.uranium.name = Uran +item.titanium.description = Ein seltenes, sehr leichtes Metall. Häufig in Flüssigkeits-Transport-Blöcken, Abbauanlagen und Flugzeugen verwendet. +item.thorium.name = Uran +item.thorium.description = Ein dichtes radioaktives Metall, welches als strukturelle Unterstützung und nuklearer Kraftstoff verwendet wird. +item.silicon.name = Silizium +item.silicon.description = Ein sehr nützlicher Halbleiter. Findet Anwendung in Solaranlagen und komplexer Elektronik. +item.plastanium.name = Plastanium +item.plastanium.description = Ein leichtes dehnbares Material welches in Flugzeugen und Splittermunition verwendet wird. +item.phase-fabric.name = Phasengewebe +item.phase-fabric.description = Eine nahezu gewichtslose Substanz, die in fortgeschrittener Elektronik und in selbstreparierender Technologie verwendet wird. +item.surge-alloy.name = Spannungsstoß-Legierung +item.surge-alloy.description = Eine fortgeschrittene Legierung mit einzigartigen elektrischen Eigenschaften. +item.spore-pod.name = Spore Pod +item.spore-pod.description = Used for conversion into oil, explosives and fuel. item.sand.name = Sand +item.sand.description = Ein gängiges Material, welches häufig in geschmolzener Form, flüssig oder als Legierung verwendet wird. +item.blast-compound.name = Explosive Mischung +item.blast-compound.description = Eine flüchtige Mischung, die in Bomben und Sprengstoffen Verwendung findet. Es besteht die Möglichkeit, es als Treibstoff zu verwenden, aber dies ist nicht empfehlenswert. +item.pyratite.name = Pyratit +item.pyratite.description = Eine extrem leicht entflammbare Substanz. Findet Verwendeung in Brandwaffen. +item.metaglass.name = Metaglass +item.metaglass.description = Eine super harte Glasmischung. Wird zur Verteilung und Lagerung von Flüssigkeiten benutzt. +item.scrap.name = Scrap +item.scrap.description = Überreste alter Gebäude und Einheiten. Enthalten Spuren verschiedenster Metalle. liquid.water.name = Wasser -liquid.plasma.name = Plasma -liquid.lava.name = Lava +liquid.slag.name = Slag liquid.oil.name = Öl -block.weaponfactory.name = Waffenfabrik -block.air.name = Luft -block.blockpart.name = Blockteil -block.deepwater.name = tiefes Wasser +liquid.cryofluid.name = Kryoflüssigkeit +mech.alpha-mech.name = Alpha +mech.alpha-mech.weapon = Schwerer Mehrlader +mech.alpha-mech.ability = Drohnenschwarm +mech.alpha-mech.description = Der Standard-Mech. Ist angemessen schnell und hat ordentlich Schaden. Kann für erweiterte offensive Fähigkeiten bis zu 3 Drohnen erzeugen. +mech.delta-mech.name = Delta +mech.delta-mech.weapon = Lichtbogen-Generator +mech.delta-mech.ability = Entladen +mech.delta-mech.description = Ein schneller, leicht gepanzerter Mech, der für Überfälle gemacht wurde. Verursacht wenig Schaden gegen Gebäude aber tötet Gruppen von Gegnern durch seine Lichtbogen-Waffen. +mech.tau-mech.name = Tau +mech.tau-mech.weapon = Restrukturierlaser +mech.tau-mech.ability = Reparatursalve +mech.tau-mech.description = Der Support Mech. Kann Blöcke durch Schüsse heilen. Kann Feuer löschen und verbündete in seinem Aktionsradius heilen. +mech.omega-mech.name = Omega +mech.omega-mech.weapon = Raketenschwarm +mech.omega-mech.ability = Rüstungskonfiguration +mech.omega-mech.description = Ein klobiger und gut gepanzerter Mech, der für den Angriff an der Front entwickelt wurde. Seine Rüstungsfähigkeit ermöglicht es ihm, 90% des Schadens abzuwehren. +mech.dart-ship.name = Dart +mech.dart-ship.weapon = Mehrlader +mech.dart-ship.description = Das Standard-Schiff. Einigermaßen schnell und leicht. Hat nur wenig Offensivkraft und geringe Erzabbaugeschwindigkeit. +mech.javelin-ship.name = Javelin +mech.javelin-ship.description = Ein Schiff für Überfälle. Anfänglich träge, kann es auf hohe Geschwindigkeiten beschleunigen um an gegnerischen Aussenposten vorbei zu fliegen und dabei mit seinen Blitzwaffen und Raketen große Mengen an Schaden verursachen. +mech.javelin-ship.weapon = Raketensalve +mech.javelin-ship.ability = Statische Entladung +mech.trident-ship.name = Trident +mech.trident-ship.description = Ein schwerer Bomber, solide gepanzert. +mech.trident-ship.weapon = Bombenschacht +mech.glaive-ship.name = Glaive +mech.glaive-ship.description = Ein großes, gut gepanzertes Gunship. Ausgerüstet mit einer Brandwaffe. Gute Beschleunigung und maximale Geschwindigkeit. +mech.glaive-ship.weapon = Flammen-Mehrlader +item.explosiveness = [LIGHT_GRAY]Explosivität: {0} +item.flammability = [LIGHT_GRAY]Entflammbarkeit: {0} +item.radioactivity = [LIGHT_GRAY]Radioaktivität: {0} +unit.health = [LIGHT_GRAY]Lebenskraft: {0} +unit.speed = [LIGHT_GRAY]Geschwindigkeit: {0} +mech.weapon = [LIGHT_GRAY]Waffe: {0} +mech.health = [LIGHT_GRAY]Health: {0} +mech.itemcapacity = [LIGHT_GRAY]Materialkapazität: {0} +mech.minespeed = [LIGHT_GRAY]Erzabbaugeschwindigkeit: {0} +mech.minepower = [LIGHT_GRAY]Erzabbaukraft: {0} +mech.ability = [LIGHT_GRAY]Fähigkeit: {0} +mech.buildspeed = [LIGHT_GRAY]Building Speed: {0}% +liquid.heatcapacity = [LIGHT_GRAY]Wärmekapazität: {0} +liquid.viscosity = [LIGHT_GRAY]Viskosität: {0} +liquid.temperature = [LIGHT_GRAY]Temperatur: {0} +block.grass.name = Gras +block.salt.name = Salz +block.saltrocks.name = Salt Rocks +block.pebbles.name = Pebbles +block.tendrils.name = Tendrils +block.sandrocks.name = Sandstein +block.spore-pine.name = Spore Pine +block.sporerocks.name = Spore Rocks +block.rock.name = Gestein +block.snowrock.name = Snow Rock +block.shale.name = Shale +block.shale-boulder.name = Shale Boulder +block.moss.name = Moos +block.shrubs.name = Shrubs +block.spore-moss.name = Spore Moss +block.shalerocks.name = Shale Rocks +block.scrap-wall.name = Scrap Wall +block.scrap-wall-large.name = Large Scrap Wall +block.scrap-wall-huge.name = Huge Scrap Wall +block.scrap-wall-gigantic.name = Gigantic Scrap Wall +block.thruster.name = Thruster +block.kiln.name = Kiln +block.kiln.description = Schmelzt Sand und Blei zu metaglass. Erfordert kleine Mengen Energie. +block.graphite-press.name = Graphite Press +block.multi-press.name = Multi-Press +block.constructing = {0}\n[LIGHT_GRAY](Constructing) +block.spawn.name = Gegnerischer Startpunkt +block.core-shard.name = Core: Shard +block.core-foundation.name = Core: Foundation +block.core-nucleus.name = Core: Nucleus +block.deepwater.name = Tiefes Wasser block.water.name = Wasser -block.lava.name = Lava -block.oil.name = Öl +block.tainted-water.name = Tainted Water +block.darksand-tainted-water.name = Dark Sand Tainted Water +block.tar.name = Teer block.stone.name = Stein -block.blackstone.name = schwarzer Stein -block.iron.name = Eisen -block.coal.name = Kohle -block.titanium.name = Titan -block.uranium.name = Uran -block.dirt.name = Erde block.sand.name = Sand +block.darksand.name = Dunkler Sand block.ice.name = Eis block.snow.name = Schnee -block.grass.name = Gras -block.sandblock.name = Sandstein -block.snowblock.name = Schneeblock -block.stoneblock.name = Steinblock -block.blackstoneblock.name = Schwarzer Stein -block.grassblock.name = Grasblock -block.mossblock.name = Moosblock -block.shrub.name = Busch -block.rock.name = Felsen -block.icerock.name = Eisfelsen -block.blackrock.name = Schwarzer Felsen -block.dirtblock.name = Erdblock -block.stonewall.name = Steinwand -block.stonewall.fulldescription = Ein billiger Verteidigungsblock. Nützlich zum Schutz des Kerns und der Geschütztürme in den ersten Wellen. -block.ironwall.name = Eisenwand -block.ironwall.fulldescription = Ein grundlegender Verteidigungsblock. Bietet Schutz vor Feinden. -block.steelwall.name = Stahlwand -block.steelwall.fulldescription = Ein Standard-Verteidigungsblock. Bietet angemessen Schutz vor Feinden. -block.titaniumwall.name = Titanwand -block.titaniumwall.fulldescription = Eine starke Abwehrblockade. Bietet Schutz vor Feinden. -block.duriumwall.name = Diriumwand -block.duriumwall.fulldescription = Eine sehr starke Abwehrblockade. Bietet guten Schutz vor Feinden. -block.compositewall.name = Verbundende Wand -block.steelwall-large.name = grosse Stahlwand -block.steelwall-large.fulldescription = Ein Standard-Verteidigungsblock. Mehrere Blöcke gross. -block.titaniumwall-large.name = grosse Titanwand -block.titaniumwall-large.fulldescription = Eine starke Abwehrblockade. Mehrere Blöcke gross. -block.duriumwall-large.name = grosse Diriumwand -block.duriumwall-large.fulldescription = Eine sehr starke Abwehrblockade. Mehrere Blöcke gross. -block.titaniumshieldwall.name = geschützte Wand -block.titaniumshieldwall.fulldescription = Ein starker Abwehrblock mit einem extra eingebauten Schild. Benötigt Energie. Verwendet Energie, um feindliche Kugeln zu absorbieren. Es wird empfohlen, Verstärker zu verwenden, um diesem Block Energie zuzuführen. -block.repairturret.name = Reparaturgeschütz -block.repairturret.fulldescription = Repariert beschädigte Blöcke in der Nähe in einem langsamen Tempo. Nutzt geringe Mengen an Energie. -block.megarepairturret.name = Reparaturgeschütz II -block.megarepairturret.fulldescription = Repariert in der Nähe beschädigte Blöcke in Reichweite zu einem vernünftigen Preis. Verwendet Energie. -block.shieldgenerator.name = Schildgenerator -block.shieldgenerator.fulldescription = Ein fortgeschrittener Verteidigungsblock. Schützt alle Blöcke in einem Radius vor Angriffen. Bei keinem Betrieb langsamer Strom verbrauch, verliert bei Kugelkontakt jedoch schnell Energie. +block.craters.name = Craters +block.sand-water.name = Sandiges Wasser +block.darksand-water.name = Dark Sand Water +block.char.name = Char +block.holostone.name = Holo stone +block.ice-snow.name = Ice Snow +block.rocks.name = Felsen +block.icerocks.name = Ice rocks +block.snowrocks.name = Snow Rocks +block.dunerocks.name = Dune Rocks +block.pine.name = Pine +block.white-tree-dead.name = White Tree Dead +block.white-tree.name = Weißer Baum +block.spore-cluster.name = Spore Cluster +block.metal-floor.name = Metallboden +block.metal-floor-2.name = Metallboden 2 +block.metal-floor-3.name = Metallboden 3 +block.metal-floor-5.name = Metallboden 5 +block.metal-floor-damaged.name = Metallboden Beschädigt +block.dark-panel-1.name = Dark Panel 1 +block.dark-panel-2.name = Dark Panel 2 +block.dark-panel-3.name = Dark Panel 3 +block.dark-panel-4.name = Dark Panel 4 +block.dark-panel-5.name = Dark Panel 5 +block.dark-panel-6.name = Dark Panel 6 +block.dark-metal.name = Dark Metal +block.ignarock.name = Igna Rock +block.hotrock.name = Hot Rock +block.magmarock.name = Magma Rock +block.cliffs.name = Cliffs +block.copper-wall.name = Kupfermauer +block.copper-wall-large.name = Große Kupfermauer +block.titanium-wall.name = Titanmauer +block.titanium-wall-large.name = Große Titanmauer +block.phase-wall.name = Phasenmauer +block.phase-wall-large.name = Große Phasenmauer +block.thorium-wall.name = Thorium-Mauer +block.thorium-wall-large.name = Große Thorium-Mauer block.door.name = Tür -block.door.fulldescription = Ein Block, der durch Antippen geöffnet und geschlossen werden kann. -block.door-large.name = grosse Tür -block.door-large.fulldescription = Ein Block der mehrere Felder gross ist und der durch Antippen geöffnet und geschlossen werden kann. -block.conduit.name = Leitungsrohr -block.conduit.fulldescription = Grundlegender Flüssigkeitstransportblock. Funktioniert wie ein Förderband, aber mit Flüssigkeiten. Am besten mit Pumpen oder anderen Leitungen verwenden. Kann als Brücke für Gegner und Spieler verwendet werden. -block.pulseconduit.name = Pulsleitungsrohr -block.pulseconduit.fulldescription = Fortschrittlicher Flüssigkeitstransportblock. Transportiert Flüssigkeiten schneller und speichert mehr als normale Leitungsrohre. -block.liquidrouter.name = flüssigkeiten verteiler -block.liquidrouter.fulldescription = Funktioniert ähnlich wie ein Router. Akzeptiert Flüssigkeit von einer Seite und gibt sie auf die anderen Seiten aus. Nützlich zum Aufspalten von Flüssigkeit aus eines einzelnen Leitungsrohres in mehrere andere Leitungensrohre. +block.door-large.name = Große Tür +block.duo.name = Duo +block.scorch.name = Scorch +block.scatter.name = Scatter +block.hail.name = Hail +block.lancer.name = Lancer block.conveyor.name = Förderband -block.conveyor.fulldescription = Grundelement Transport Block. Bewegt Gegenstände nach vorne und legt sie automatisch in Türmen oder ähnliches. Drehbar. Kann als Brücke für Gegner und Spieler verwendet werden. -block.steelconveyor.name = Stahlförderband -block.steelconveyor.fulldescription = Erweiterter Transportblock Bewegt Gegenstände schneller als Standardförderer. -block.poweredconveyor.name = Impulsförderband -block.poweredconveyor.fulldescription = Der ultimative Transportblock für Gegenstände. Bewegt Gegenstände noch schneller als Stahlförderer. -block.router.name = Verteiler -block.router.fulldescription = Akzeptiert Objekte aus einer Richtung und gibt sie in 3 andere Richtungen aus. Kann auch eine bestimmte Anzahl von Gegenständen speichern. Geeignet zum Aufteilen der Materialien von einem Bohrer in mehrere Geschütze. +block.titanium-conveyor.name = Titan-Förderband block.junction.name = Kreuzung -block.junction.fulldescription = Fungiert als Brücke für zwei kreuzende Förderbänder. Nützlich in Situationen mit zwei verschiedenen Förderbänder, die unterschiedliche Materialien zu verschiedenen Orten transportieren. -block.conveyortunnel.name = Förderbandtunnel -block.conveyortunnel.fulldescription = Transportiert Artikel unter Blöcken. Verwendung für einen Tunnel, der in den zu untertunnelnden Block führt, und einen auf der anderen Seite. Stellen Sie sicher, dass beide Tunnel in entgegengesetzte Richtungen weisen, das heisst das die Tunnel voneinander weggucken. -block.liquidjunction.name = flüssigkeite Kreuzung -block.liquidjunction.fulldescription = Funktioniert als Brücke für zwei kreuzende Leitungsrohre. Nützlich in Situationen mit zwei verschiedenen Leitungen, die unterschiedliche Flüssigkeiten zu verschiedenen Orten transportieren. -block.liquiditemjunction.name = Flüssigkeit-Gegenstand-Kreuzung -block.liquiditemjunction.fulldescription = Fungiert als Brücke zum Überqueren von Leitungsrohre und Förderbändern. -block.powerbooster.name = Energieverstärker -block.powerbooster.fulldescription = Verteilt die Energie an alle Blöcke innerhalb seines Radius. -block.powerlaser.name = Energielaser -block.powerlaser.fulldescription = Erzeugt einen Laser, der die Kraft auf den Block davor überträgt. Erzeugt selbst keine Energie. Am besten mit Generatoren oder anderen Lasern verwendet. -block.powerlaserrouter.name = Laser Verteiler -block.powerlaserrouter.fulldescription = Laser, der die Kraft in drei Richtungen gleichzeitig verteilt. Nützlich in Situationen, in denen mehrere Blöcke von einem Generator mit Strom versorgt werden müssen. -block.powerlasercorner.name = Laser-Ecke -block.powerlasercorner.fulldescription = Laser, der die Kraft in zwei Richtungen gleichzeitig verteilt. Nützlich in Situationen, in denen mehrere Blöcke von einem Generator mit Strom versorgt werden müssen und ein Router ungenau ist. -block.teleporter.name = Teleporter -block.teleporter.fulldescription = Erweiterter Transportblock Teleporter geben Gegenstände in andere Teleporter derselben Farbe ein. Tut nichts, wenn keine Teleporter derselben Farbe existieren. Wenn mehrere Teleporter mit derselben Farbe existieren, wird eine zufällige ausgewählt. Verwendet Energie. Tippen, um die Farbe zu ändern. +block.router.name = Verteiler +block.distributor.name = Großer Verteiler block.sorter.name = Sortierer -block.sorter.fulldescription = Sortiert den Gegenstand nach Materialart. Das zu akzeptierende Material wird durch die Farbe im Block angezeigt. Alle Artikel, die dem Sortiermaterial entsprechen, werden vorwärts ausgegeben, alles andere wird nach links und rechts ausgegeben. -block.core.name = Kern -block.pump.name = Pumpe -block.pump.fulldescription = Pumpen Flüssigkeiten aus einem Quellblock - meist Wasser, Lava oder Öl. Gibt Flüssigkeit in benachbarte Leitungsrohre aus. -block.fluxpump.name = flux Pumpe -block.fluxpump.fulldescription = Eine erweiterte Version der Pumpe. Speichert mehr Flüssigkeit und pumpt Flüssigkeit schneller. -block.smelter.name = Schmelzer -block.smelter.fulldescription = Der essentielle Bastelblock. Wenn 1x Eisen und 1x Kohle eingegeben wird, wird 1x Stahl ausgegeben. -block.crucible.name = Tiegel -block.crucible.fulldescription = Ein fortgeschrittener Handwerksblock. Braucht Kohle um zu funktionieren. Wenn 1x Titan und 1x Stahl eingegeben wird, wird 1x Dirium ausgegeben. -block.coalpurifier.name = Kohle-Extraktor -block.coalpurifier.fulldescription = Ein einfacher Extraktorblock. Gibt Kohle aus, wenn der Block mit grossen Mengen Wasser und Stein gefüllt wird. -block.titaniumpurifier.name = Titan-Extraktor -block.titaniumpurifier.fulldescription = Ein Standard-Extraktorblock. Gibt Titan aus wenn er mit grossen Mengen Wasser und Eisen gefüllt wird. -block.oilrefinery.name = Ölraffinerie -block.oilrefinery.fulldescription = Veredelt grosse Mengen Öl zu Kohle. Nützlich für die Betankung von Blöcken die Kohle benutzen wie z.b. Waffentürme, wenn Kohleadern knapp sind. -block.stoneformer.name = Steinformer -block.stoneformer.fulldescription = Verfestigt flüssige Lava zu Stein. Nützlich für die Herstellung von grossen Mengen von Stein für Kohle-Extraktor. -block.lavasmelter.name = Lava-Schmelzer -block.lavasmelter.fulldescription = Verwendet Lava, um Eisen zu Stahl schmelzen. Eine Alternative zum Schmelzer. Nützlich in Situationen, in denen Kohle knapp ist. -block.stonedrill.name = Steinbohrer -block.stonedrill.fulldescription = Der wesentliche Bohrer. Wenn er auf Steinplatten gelegt wird, gibt er Steine ​​mit einer langsamen Geschwindigkeit auf endlosen Zeit aus. -block.irondrill.name = Eisenbohrer -block.irondrill.fulldescription = Eine grundlegender Bohrer. Wenn es auf Eisenerz gelegt wird, gibt er Eisen auf endloser Zeit langsam aus. -block.coaldrill.name = Kohlenbohrer -block.coaldrill.fulldescription = Eine grundlegender Bohrer. Wenn es auf Kohleerz platziert wird, gibt er für endloser Zeit langsam Kohle aus. -block.uraniumdrill.name = Uran-Bohrer -block.uraniumdrill.fulldescription = Ein fortgeschrittener Bohrer. Wenn es auf Uranerz platziert wird, gibt er Uran mit einer langsamen Geschwindigkeit auf endloser Zeit ab. -block.titaniumdrill.name = Titan-Bohrer -block.titaniumdrill.fulldescription = Ein fortgeschrittener Bohrer. Wenn es auf Titanerz platziert wird, gibt er Titan langsam aus für undendlich langer Zeit -block.omnidrill.name = Omni-Bohrer -block.omnidrill.fulldescription = Der ultimative Bohrer. Baut in schnellem Tempo jegliches Erz ab. -block.coalgenerator.name = Kohle-Generator -block.coalgenerator.fulldescription = Der wesentliche Generator. Erzeugt Energie aus Kohle. Gibt Energie als Laser an seine 4 Seiten aus. -block.thermalgenerator.name = thermischer Generator -block.thermalgenerator.fulldescription = Erzeugt Energie aus Lava. Gibt Energie als Laser an seine 4 Seiten aus. -block.combustiongenerator.name = Verbrennungsgenerator -block.combustiongenerator.fulldescription = Erzeugt Energie aus Öl. Gibt Energie als Laser an seine 4 Seiten aus. -block.rtgenerator.name = RTG-Generator -block.rtgenerator.fulldescription = Erzeugt geringe Mengen an Energie aus dem radioaktiven Zerfall von Uran. Gibt Energie als Laser an seine 4 Seiten aus. -block.nuclearreactor.name = Kernreaktor -block.nuclearreactor.fulldescription = Eine erweiterte Version des RTG-Generators und der ultimative Energie Generator. Erzeugt Strom aus Uran. Erfordert konstante Wasserkühlung. Sehr heiss; explodiert heftig, wenn zu wenig Kühlmittel zugeführt wird. -block.turret.name = Geschütz -block.turret.fulldescription = Ein einfacher, billiger Turm. Verwendet Stein für Munition. Hat etwas mehr Reichweite als das Doppelgeschütz. -block.doubleturret.name = Doppelgeschütz -block.doubleturret.fulldescription = Eine etwas stärkere Version des Geschützes. Verwendet Stein für Munition. Hat deutlich mehr Schaden, hat aber eine geringere Reichweite. Schiesst zwei Kugeln. -block.machineturret.name = Gatling Geschütz -block.machineturret.fulldescription = Ein Standard-Allround-Turm. Verwendet Eisen für Munition. Hat eine schnelle Feuerrate mit gutem Schaden. -block.shotgunturret.name = Splittergeschütz -block.shotgunturret.fulldescription = Ein Standard-Turm. Verwendet Eisen für Munition. Schiesst 7 Kugeln auf einmal. Geringere Reichweite, aber höhere Schadensleistung als das Gatling Geschütz -block.flameturret.name = Flammenwerfer -block.flameturret.fulldescription = Fortschrittlicher Nahbereichswaffe. Verwendet Kohle für Munition. Hat eine sehr geringe Reichweite, aber sehr hohen Schaden. Gut für Nahkampf. Empfohlen für den Einsatz hinter Mauern. -block.sniperturret.name = Schienenkanone -block.sniperturret.fulldescription = Fortschrittliches Reichweitengeschütz. Verwendet Stahl für Munition. Sehr hoher Schaden, aber niedrige Feuerrate. Teuer zu verwenden, kann aber aufgrund seiner Reichweite weit entfernt von den feindlichen Linien platziert werden. -block.mortarturret.name = Flakgeschütz -block.mortarturret.fulldescription = Fortschrittlicher Flächen-Schaden Turm. Verwendet Kohle für Munition. Sehr langsame Feuerrate und Geschosse, aber sehr hoher Einzelziel- und Flächenschaden. Nützlich gegen grosse Mengen von Feinden. -block.laserturret.name = Laserturm -block.laserturret.fulldescription = Fortschrittlicher Einzelziel-Turm. Verwendet Strom. Guter Allround-Revolver für mittlere Reichweiten. Nur Einzelziel. Verfehlt nie. -block.waveturret.name = Teslakanone -block.waveturret.fulldescription = Erweiterter Mehrfach-Ziele-Turm. Verwendet Strom. Mittlere Reichweite. Verfehlt nie. Im Durchschnitt zu wenig Schaden, aber kann mehrere Feinde gleichzeitig mit Kettenblitz treffen. -block.plasmaturret.name = Plasmageschütz -block.plasmaturret.fulldescription = Hochentwickelte Version des Flammenwerfers. Verwendet Kohle als Munition. Sehr hoher Schaden, niedriger bis mittlerer Reichweite. -block.chainturret.name = Kettengeschütz -block.chainturret.fulldescription = Die ultimative Schnellfeuer Waffe. Verwendet Uran als Munition. Schiesst grosse Kugeln mit hoher Feuerrate. Mittlere Reichweite. Mehrere Felder gross. Hält extrem viel aus. -block.titancannon.name = Titan Kanone -block.titancannon.fulldescription = Die ultimative Langstrecken Kanone. Verwendet Uran als Munition. Schiesst grosse Flächen-Schadenden-Granaten mit einer mittleren Feuerrate ab. Lange Reichweite. Ist mehrere Felder gross. Hält extrem viel aus. -block.playerspawn.name = Spielerspawn -block.enemyspawn.name = Gegnerspawn +block.sorter.description = Sortiert Materialien. Wenn ein Gegenstand der Auswahl entspricht, darf er vorbei. Andernfalls wird er links oder rechts ausgegeben. +block.overflow-gate.name = Überlauftor +block.overflow-gate.description = Ein Verteiler, der nur Materialien nach links oder rechts ausgibt, falls der Weg gerade aus blockiert ist. +block.silicon-smelter.name = Silizium-Schmelzer +block.phase-weaver.name = Phasenweber +block.pulverizer.name = Pulverisierer +block.cryofluidmixer.name = Kryoflüssigkeitsmixer +block.melter.name = Schmelzer +block.incinerator.name = Verbrennungsanlage +block.spore-press.name = Spore Press +block.separator.name = Separierer +block.coal-centrifuge.name = Coal Centrifuge +block.power-node.name = Stromknoten +block.power-node-large.name = Großer Stromknoten +block.surge-tower.name = Surge Tower +block.battery.name = Batterie +block.battery-large.name = Große Batterie +block.combustion-generator.name = Verbrennungsgenerator +block.turbine-generator.name = Turbinengenerator +block.differential-generator.name = Differential Generator +block.impact-reactor.name = Impact Reactor +block.mechanical-drill.name = Mechanischer Bohrer +block.pneumatic-drill.name = Pneumatischer Bohrer +block.laser-drill.name = Laser-Bohrer +block.water-extractor.name = Wasser-Extraktor +block.cultivator.name = Kultivierer +block.dart-mech-pad.name = Dart Mech Pad +block.delta-mech-pad.name = Delta Mech Pad +block.javelin-ship-pad.name = Javelin Ship Pad +block.trident-ship-pad.name = Trident Ship Pad +block.glaive-ship-pad.name = Glaive Ship Pad +block.omega-mech-pad.name = Omega Mech Pad +block.tau-mech-pad.name = Tau Mech Pad +block.conduit.name = Leitungsrohr +block.mechanical-pump.name = Mechanische Pumpe +block.item-source.name = Materialquelle +block.item-void.name = Materialschlucker +block.liquid-source.name = Flüssigkeitsquelle +block.power-void.name = Stromsenke +block.power-source.name = Unendliche Stromquelle +block.unloader.name = Entlader +block.vault.name = Tresor +block.wave.name = Welle +block.swarmer.name = Schwärmer +block.salvo.name = Salve +block.ripple.name = Riffel +block.phase-conveyor.name = Phasen-Transportband +block.bridge-conveyor.name = Brücken-Transportband +block.plastanium-compressor.name = Plastanium-Verdichter +block.pyratite-mixer.name = Pyratit-Mixer +block.blast-mixer.name = Sprengmixer +block.solar-panel.name = Solar Panel +block.solar-panel-large.name = Großes Solar Panel +block.oil-extractor.name = Oil Extraktor +block.spirit-factory.name = Spirit-Drohnenfabrik +block.phantom-factory.name = Phantom-Drohnenfabrik +block.wraith-factory.name = Wraith Fighter-Fabrik +block.ghoul-factory.name = Ghoul Bomber-Fabrik +block.dagger-factory.name = Dagger Mech-Fabrik +block.crawler-factory.name = Crawler Mech Factory +block.titan-factory.name = Titan Mech-Fabrik +block.fortress-factory.name = Fortress Mech-Fabrik +block.revenant-factory.name = Revenant Fighter-Fabrik +block.repair-point.name = Reparaturpunkt +block.pulse-conduit.name = Impulskanal +block.phase-conduit.name = Phasenkanal +block.liquid-router.name = Flüssigkeits-Router +block.liquid-tank.name = Flüssigkeitstank +block.liquid-junction.name = Flüssigkeits-Kreuzung +block.bridge-conduit.name = Kanalbrücke +block.rotary-pump.name = Rotierende Pumpe +block.thorium-reactor.name = Thorium-Reaktor +block.mass-driver.name = Massenbeschleuniger +block.blast-drill.name = Sprengbohrer +block.thermal-pump.name = Thermische Pumpe +block.thermal-generator.name = Thermischer Generator +block.alloy-smelter.name = Legierungsschmeltzer +block.mender.name = Mender +block.mend-projector.name = Reparaturprojektor +block.surge-wall.name = Spannungsstoß-Mauer +block.surge-wall-large.name = Große Spannungsstoß-Mauer +block.cyclone.name = Cyclone +block.fuse.name = Fuse +block.shock-mine.name = Schock-Mine +block.overdrive-projector.name = Beschleunigungs-Projektor +block.force-projector.name = Kraftfeld-Projektor +block.arc.name = Arc +block.rtg-generator.name = RTG Generator +block.spectre.name = Spectre +block.meltdown.name = Meltdown +block.container.name = Container +block.launch-pad.name = Launch Pad +block.launch-pad.description = Launches batches of items without any need for a core launch. Unfinished. +block.launch-pad-large.name = Large Launch Pad +team.blue.name = Blau +team.red.name = Rot +team.orange.name = Orange +team.none.name = Grau +team.green.name = Grün +team.purple.name = Lila +unit.spirit.name = Spirit Drohne +unit.spirit.description = Die anfängliche Drohne. Sie wird gewöhnlich in der Basis Erz ab, sammelt Materialien und repariert Blöcke. +unit.phantom.name = Phantom Drohne +unit.phantom.description = Eine fortgeschrittene Drohne. Baut automatisch Erz ab, sammelt Materialien und repariert Blöcke. Deutlich effizienter als die Drohne. +unit.dagger.name = Dagger +unit.dagger.description = Eine Standard-Bodeneinheit. Nützlich in Schwärmen. +unit.crawler.name = Crawler +unit.titan.name = Titan +unit.titan.description = Eine fortgeschrittene gepanzerte Bodeneinheit. Greift sowohl Boden- als auch Luftziele an. +unit.ghoul.name = Ghoul Bomber +unit.ghoul.description = Ein schwerer Flächenbomber. +unit.wraith.name = Wraith Fighter +unit.wraith.description = Eine schneller Abfangjäger. +unit.fortress.name = Fortress +unit.fortress.description = Eine schwere Artillerie-Bodeneinheit. +unit.revenant.name = Revenant +unit.eruptor.name = Eruptor +unit.chaos-array.name = Chaos Array +unit.eradicator.name = Eradicator +unit.lich.name = Lich +unit.reaper.name = Reaper +tutorial.begin = Deine Mission ist es den [LIGHT_GRAY]Gegner[] auszurotten.\n\n Beginne damit [accent] Kupfer abzubauen[]. Beginne in dem du auf ein Kupfer Vorkommen nahe deines Kerns klickst. +tutorial.drill = Manuelles Abbauen von Ressourcen ist ineffizient.\n[accent]Bohrer[] können automatisch abbauen.\n Platziere einen auf einem Kupfer Vorkommen. +tutorial.conveyor = [accent]Transportbänder[] werden dazu benutzt Materialien zum Kern zu transportieren.\n Erstelle eine Reihe von Transportbändern zum Kern. +tutorial.morecopper = Du brauchst [accent]mehr Kupfer[]!\n\nEntweder du baust es manuell ab, oder du platzierst weitere Bohrer. +tutorial.turret = Verteidigungsgebäude müssen gebaut werden um[LIGHT_GRAY] Gegner[] abzuwehren.\nBaue einen Duo Geschützturm in die Nähe deiner Basis. +tutorial.drillturret = Duo Geschütztürme benötigen[accent] Kupfermunition []um zu schießen.\nPlatziere einen Bohrer neben das Geschütz um es mit Kupfer zu versorgen. +tutorial.waves = Der [LIGHT_GRAY] Gegner[] greift an.\n\nVerteidige deinen Kern 2 Wellen lang. Bau mehr Türme. +tutorial.lead = Mehr Erz ist verfügbar. Finde Blei und bau es ab.\n\n Klicke auf deine Einheit und ziehe die Maus auf den Kern um Ressourcen zu übertragen. +tutorial.smelter = Kupfer und Blei sind schwache Metalle.\n Super [accent]dichte Legierung [] kann in einem Schmeltzer erzeugt werden.\n\n Bau einen. +tutorial.densealloy = Der Schmeltzer wird nun Legierung produzieren.\n Produziere einige.\n Verbessere die Produktion sofern notwendig. +tutorial.siliconsmelter = Der Kern wird nun [accent]spirit drohnen[] erstellen. Diese Bauen Rohstoffe und reparieren Blöcke.\n\nFabriken für andere Einheiten benötigen [accent]Silizium[].\n Baue ein Silizium Schmeltzer. +tutorial.silicondrill = Silizium benötigt [accent]Kohle[] und [accent]Sand[].\n Fange damit an die Bohrer zu platzieren. +tutorial.generator = Diese Technologie benötigt power.\n Erstelle einen Verbrennungs-Generator dafür. +tutorial.generatordrill = Verbrennungs Generatoren benötigen Kraftstoff.\nBenutze Kohle aus einem Bohrer als Kraftstoff. +tutorial.node = Power muss transportiert werden.\nErstelle einen [accent]Stromknoten[] nahe deinem Verbrennungs Generator um seine Power zu transportieren. +tutorial.nodelink = Power kann über verbundene Power Blocks, Generatoren oder Stromknoten transferierd werden.\n\n Verbinde die Power in dem du auf den Knoten klickst und dann den Generator und den Silizium Schmeltzer auswählst. +tutorial.silicon = Silizium wird produziert. Produziere einiges.\n\n Verbesserungen am Produktionssystem werden empfohlen. +tutorial.daggerfactory = Konstruiere eine Dagger Mech Fabrik.\n\n Diese wird verwendet um Angreifende Mechs zu erstellen. +tutorial.router = Fabriken benötigen Ressourcen um zu funktionieren.\n Platziere ein Router um Materialien auf Transportbändern aufzuteilen. +tutorial.dagger = Verbinde die Fabrik mit einem Stromknoten. Wenn alle Voraussetzungen gegeben sind, beginnt die Fabrik Mechs zu konstruieren.\n\n Platziere mehr Bohrer und Transportbänder um die Versorgung der Fabrik zu sichern. +tutorial.battle = Der[LIGHT_GRAY] Gegner[] hat seinen Kern offenbart.\nZerstöre ihn mit deiner Einheit und den Dagger Mechs. +block.copper-wall.description = Ein günstiger Verteidigungsblock.\nNützlich, um die Basis und Türme in den ersten Wellen zu beschützen. +block.copper-wall-large.description = Ein günstiger Verteidigungsblock.\nNützlich, um die Basis und Türme in den ersten Wellen zu beschützen.\nBenötigt mehrere Kacheln. +block.thorium-wall.description = Ein starker Verteidigungsblock.\nGuter Schutz vor Feinden. +block.thorium-wall-large.description = Ein starker Verteidigungsblock.\nGuter Schutz vor Feinden.\nBenötigt mehrere Kacheln. +block.phase-wall.description = Nicht so stark wie eine Thorium-Mauer, aber reflektiert Schüsse bis zu einer gewissen Stärke. +block.phase-wall-large.description = Nicht so stark wie eine Thorium-Mauer, aber reflektiert Schüsse bis zu einer gewissen Stärke.\nBenötigt mehrere Kacheln. +block.surge-wall.description = Der stärkste Verteidigungsblock.\nHat eine kleine Chance, bei einem Schuss einen Lichtbogen in Richtung angreifer auszulösen. +block.surge-wall-large.description = Der stärkste Verteidigungsblock.\nHat eine kleine Chance, bei einem Schuss einen Lichtbogen in Richtung angreifer auszulösen.\nBenötigt mehrere Kacheln. +block.door.description = Eine kleine Tür, die durch darauf tippen geöffnet und geschlossen werden kann.\nGegner können durch geöffnete Türen schießen und laufen. +block.door-large.description = Eine kleine Tür, die durch darauf tippen geöffnet und geschlossen werden kann.\nGegner können durch geöffnete Türen schießen und laufen.\nBenötigt mehrere Kacheln. +block.mend-projector.description = Heilt zyklisch Blöcke in seiner Umgebung. +block.overdrive-projector.description = Erhöht die Geschwindigkeit von nahegelegenen Blöcken wie Bohrer und Förderbänder. +block.force-projector.description = Erzeugt ein sechseckiges Kraftfeld um sich selbst, durch das Blöcke und Einheiten vor Schaden beschützt werden. +block.shock-mine.description = Beschädigt Gegner, die auf die Mine laufen. Für Gegener schwer zu sehen. +block.duo.description = Ein kleiner, günstiger Geschützturm. +block.scatter.description = A medium-sized anti-air turret. Sprays clumps of lead or scrap flak at enemy units. +block.arc.description = Ein kleiner Geschützturm, der Lichtbögen in Richtung des Gegners schießt. +block.hail.description = Ein kleiner Artillerie-Geschützturm. +block.lancer.description = Ein mittelgroßer Geschützturm, der sich auflädt und Elektrizitätsstrahlen verschießt. +block.wave.description = Ein mittelgroßer Geschützturm, der flüssige Kugeln verschießt. +block.salvo.description = Ein mittelgroßer Geschützturm, der Schüsse in Salven abfeuert. +block.swarmer.description = Ein mittelgroßer Geschützturm, der Raketenschwärme abfeuert. +block.ripple.description = Ein großer Artillerie-Geschützturm, der mehrere Schüsse gleichzeitig abfeuert. +block.cyclone.description = Ein großer Schnellfeuer-Geschützturm. +block.fuse.description = Ein großer Geschützturm, der starke Strahlen mit kurzer Reichweite abfeuert. +block.spectre.description = Ein großer Geschützturm, der zwei starke Schüsse gleichzeitig abfeuert. +block.meltdown.description = Ein großer Geschützturm, der starke Strahlen mit großer Reichweite abfeuert. +block.conveyor.description = Basis-Transportblock. Bewegt Materialien vorwärts und lädt sie automatisch in Geschütztürme oder Verarbeitungsanlagen. Rotierbar. +block.titanium-conveyor.description = Verbesserter Transportblock. Bewegt Materialien schneller als Standard-Förderbänder. +block.phase-conveyor.description = Verbesserter Transportblock. Verwendet Strom, um Materialien zu einem verbundenen Phasen-Förderband über mehrere Kacheln zu teleportieren. +block.junction.description = Fungiert als Brücke zwischen zwei kreuzenden Förderbändern. Nützlich, wenn zwei verschiedene Förderbänder sich kreuzen, aber unterschiedliche Materialien verwenden. +block.mass-driver.description = Ultimativer Transportblock. Sammelt mehrere Materialien und schießt sie zu einem verbundenen Massenbeschleuniger über eine große Reichweite. +block.silicon-smelter.description = Reduziert Sand mit hochreinem Kohlenstoff, um Silizium zu produzieren. +block.plastanium-compressor.description = Produziert Plastanium aus Öl und Titan. +block.phase-weaver.description = Produziert Phasengewebe aus radioaktivem Thorium und großen Mengen an Sand. +block.alloy-smelter.description = Verarbeitet Titan, Blei, Silizium und Kupfer zu einer Stromstoßlegierung. +block.pulverizer.description = Zertrümmert Stein zu Sand. Nützlich, wenn kein natürlicher Sand verfügbar ist. +block.pyratite-mixer.description = Vermischt Kohle, Blei und Sand zu hochentzündlichem Pyratit. +block.blast-mixer.description = Verwendet Öl, um Pyratit in eine weniger enzündliche aber explosivee Mischung umzuwandeln. +block.cryofluidmixer.description = Verarbeitet Wasser mit Titan zu einer Kryoflüssigkeit, die viel effizienter kühlt. +block.melter.description = Erhitzt Stein auf extrem hohe Temperaturen, um Lava zu erhalten. +block.incinerator.description = Vernichtet beliebige überschüssige Materialien oder Flüssigkeiten. +block.spore-press.description = Compresses spore pods into oil. +block.separator.description = Setzt Stein Wasserdruck aus, um verschiedene Mineralien im Stein freizulegen. +block.power-node.description = Überträgt Strom zu verbundenen Knoten. Bis zu vier Stromquellen, -verbraucher oder -knoten können verbunden werden. Der Knoten erhält Strom von benachbarten Knoten und gibt Strom benachbarte Blöcke weiter. +block.power-node-large.description = Hat einen größeren Radius als der normale Stromknoten und verbindet bis zu sechs Stromquellen, -verbraucher oder -knoten. +block.battery.description = Speichert Strom, solange ein Überschuss besteht, und gibt ihn bei Knappheit ab, solange Kapazität vorhanden ist. +block.battery-large.description = Speichert sehr viel mehr Strom als eine normale Batterie. +block.combustion-generator.description = Generiert Stromg, indem Öl oder entzündliche Materialien verbrannt werden. +block.turbine-generator.description = Effizienter als ein Verbrennungsgenerator, benötigt jedoch zusätzlich Wasser. +block.thermal-generator.description = Erzeugt große Mengen Strom aus Lava. +block.solar-panel.description = Erzeugt kleine Mengen an Strom aus Sonnenenergie. +block.solar-panel-large.description = Erzeugt viel mehr Strom als ein normales Solar Panel, ist aber auch sehr viel teurer in der Anschaffung. +block.thorium-reactor.description = Erzeugt riesige Mengen Strom aus radioaktivem Thorium. Benötigt konstante Kühlung. Explodiert verheerend, wenn unzureichende Mengen an Kühlung vorhanden sind. +block.rtg-generator.description = Ein Radioisotopengenerator, der keine Kühlung benötigt, aber weniger Strom als ein Thorium-Reaktor liefert. +block.unloader.description = Entlädt Materialien aus einem Container, Tresor oder einer Basis auf ein Förderband oder direkt in einen benachbarten Block. Der Typ des auszuladenden Materials kann durch darauf tippen verändert werden. +block.container.description = Speichert eine kleine Menge an Materialien pro Typ. Benachbarte Container, Tresore und Basen werden zu einem Behälter zusammengefasst. Ein[LIGHT_GRAY] Entlader[] kann verwendet werden, um Materialien auszuladen. +block.vault.description = Speichert eine große Menge an Materialien pro Typ. Benachbarte Container, Tresore und Basen werden zu einem Behälter zusammengefasst. Ein[LIGHT_GRAY] Entlader[] kann verwendet werden, um Materialien auszuladen. +block.mechanical-drill.description = Ein günstiger Bohrer. Wenn er auf passende Kacheln gesetzt wird, baut er unbegrenzt Erze des entsprechenden Typs mit geringer Geschwindigkeit ab. +block.pneumatic-drill.description = Ein verbesserter Bohrer, der schneller ist und in der Lage ist, härtere Erze abzubauen, indem er von Luftdruck gebrauch macht. +block.laser-drill.description = Erlaubt es, durch Lasertechnologie noch schneller zu bohren, benötigt aber Strom. Erlaubt zusätzlich das Abbauen von radioaktivem Thorium. +block.blast-drill.description = Der ultimative Bohrer. Benötigt große Mengen an Strom. +block.water-extractor.description = Extrahiert Wasser aus dem Boden. Verwende ihn, wenn es keinen See in der Nähe gibt. +block.cultivator.description = Kultiviert den Boden mit Wasser, um Biomasse zu erzeugen. +block.oil-extractor.description = Verwendet große Mengen an Strom, um Öl aus Sand zu extrahieren. Verwende ihn, wenn es keine direkte Ölquelle gibt. +block.trident-ship-pad.description = Wechsle in einen massiv gepanzerten schweren Bomber.\nVerwende das Pad, indem du doppelt darauf tippst, während du darauf bist. +block.javelin-ship-pad.description = Wechsle in einen starken und schnellen Abfangjäger mit Blitz-Waffen.\nVerwende das Pad, indem du doppelt darauf tippst, während du darauf bist. +block.glaive-ship-pad.description = Wechsle in ein großes, gut gepanzertes Kampfflugzeug.\nVerwende das Pad, indem du doppelt darauf tippst, während du darauf bist. +block.tau-mech-pad.description = Wechsle in einen Support-Mech, der befreundete Blöcke und Einheiten heilen kann.\nVerwende das Pad, indem du doppelt darauf tippst, während du darauf bist. +block.delta-mech-pad.description = Wechsle in einen schnellen, leicht gepanzerten Mech, der für Überfälle gemacht ist.\nVerwende das Pad, indem du doppelt darauf tippst, während du darauf bist. +block.omega-mech-pad.description = Wechsle in einen klobigen und gut gepanzerten Mech, der für Frontangriffe gemacht ist.\nVerwende das Pad, indem du doppelt darauf tippst, während du darauf bist. +block.spirit-factory.description = Produziert leichte Drohnen, die Erz abbauen und Blöcke reparieren können. +block.phantom-factory.description = Produziert erweiterte Drohnen, die deutlich effizienter sind als Spirit-Drohnen. +block.wraith-factory.description = Produziert schnelle Abfangjäger. +block.ghoul-factory.description = Produziert schwere Flächenbomber. +block.dagger-factory.description = Produziert Standard-Bodeneinheiten. +block.titan-factory.description = Produziert fortgeschrittene, gepanzerte Bodeneinheiten. +block.fortress-factory.description = Produziert schwere Artillerie-Bodeneinheiten. +block.revenant-factory.description = Produziert schwere Laser-Bodeneinheiten. +block.repair-point.description = Heilt durchgehend die nächste befreundete, beschädigte Einheit in der Umgebung. +block.conduit.description = Standard Flüssigkeits-Transportblock. Funktioniert wie ein Förderband, nur für Flüssigkeiten. Wird am Besten mit Extraktoren, Pumpen oder anderen Kanälen benutzt. +block.pulse-conduit.description = Verbesserter Flüssigkeits-Transportblock. Transportiert Flüssigkeiten schneller und speichert mehr als Standard Kanäle. +block.phase-conduit.description = Verbesserter Flüssigkeits-Transportblock. Verwendet Strom, um Flüssigkeiten zu einem verbundenen Phasenkanal zu teleportieren. +block.liquid-router.description = Akzeptiert Flüssigkeiten aus einer Richtung und verteilt sie an bis zu drei andere Richtungen weiter. Nützlich, um Flüssigkeiten aus einer Quelle an mehrere Empfänger zu verteilen. +block.liquid-tank.description = Speichert eine große Menge an Flüssigkeiten. Verwende es als Puffer, wenn Angebot und Nachfrage an einer Flüssigkeit schwanken. +block.liquid-junction.description = Fungiert als Brücke über zwei kreuzende Kanäle. Nützlich in Situationen, in denen sich zwei Kanäle mit verschiedenen Flüssigkeiten kreuzen. +block.bridge-conduit.description = Verbesserter Flüssigkeits-Transportblock. Erlaubt es, Flüssigkeiten über bis zu 3 Kacheln beliebigen Terrains oder Inhalts zu transportieren. +block.mechanical-pump.description = Eine günstige, langsame Punkte, die keine Strom benötigt. +block.rotary-pump.description = Eine fortgeschrittene Pumpe, die mithilfe von Strom doppelt so schnell pumpt. +block.thermal-pump.description = Die ultimative Pumpe, dreimal so schnell wie eine mechanische Pumpe und die einzige Pumpe, die Lava fördern kann. +block.router.description = Akzeptiert Materialien aus einer Richtung und leitet sie gleichmäßig in bis zu drei andere Richtungen weiter. Nützlich, wenn die Materialien aus einer Richtung an mehrere Empfänger verteilt werden sollen. +block.distributor.description = Ein weiterentwickelter Router, der Materialien in bis zu sieben Richtungen gleichmäßig verteilt. +block.bridge-conveyor.description = Verbesserter Transportblock. Erlaubt es, Materialien über bis zu 3 Kacheln beliebigen Terrains oder Inhalts zu transportieren. +block.item-source.description = Produziert unendlich items. Nur im Sandkasten verfügbar. +block.liquid-source.description = Produziert unendlich Flüssigkeiten. Nur im Sandkasten verfügbar. +block.item-void.description = Zerstört Materialien, die hereingegeben werden, ohne Strom zu verbrauchen. Nur im Sandkasten verfügbar. +block.power-source.description = Erzeugt unendlich viel Strom. Nur im Sandkasten verfügbar. +block.power-void.description = Verschlingt den kompletten übrigen Strom. Nur im Sandkasten verfügbar. +liquid.water.description = Wird überlicherweise zum Kühlen von Maschinen und zur Müllverarbeitung verwendet. +liquid.oil.description = Kann verbrannt, zum explodieren gebracht, oder als Kühlung verwendet werden. +liquid.cryofluid.description = Die effizienteste Flüssigkeit, um Dinge herunter zu kühlen. diff --git a/core/assets/bundles/bundle_es.properties b/core/assets/bundles/bundle_es.properties index af95e6b530..fb185a2d6d 100644 --- a/core/assets/bundles/bundle_es.properties +++ b/core/assets/bundles/bundle_es.properties @@ -1,552 +1,947 @@ -text.about = Creado por [ROYAL]Anuken [] - [SKY] anukendev@gmail.com [] Originalmente una entrada en el [naranja] GDL [] Metal Monstrosity Jam. Créditos: - SFX hecho con [AMARILLO] bfxr [] - Música hecha por [VERDE] RoccoW [] / encontrado en [lime] FreeMusicArchive.org [] Agradecimientos especiales a: - [coral] MitchellFJN []: extensa prueba de juego y comentarios - [cielo] Luxray5474 []: trabajo wiki, contribuciones de código - [lime] Epowerj []: sistema de compilación de código, icono - Todos los probadores beta en itch.io y Google Play\n -text.credits = Créditos -text.discord = ¡Únete al Discord de Mindustry! -text.changes = [SCARLET] ¡Atención! [] Algunas mecánicas importantes del juego han sido cambiadas. - [acento] Los teletransportadores [] ahora usan energía. - [acento]Los talleres de fundición[] y [acento]los crisoles [] ahora tienen una capacidad máxima de artículos. - [acento] Los crisoles[] ahora requieren carbón como combustible. -text.link.discord.description = La sala oficial del discord de Mindustry -text.link.github.description = Código fuente del juego -text.link.dev-builds.description = Estados en desarrollo inestables -text.link.trello.description = Tablero trello oficial para las características planificadas -text.link.itch.io.description = itch.io és la página con descargas para PC y la versión web -text.link.google-play.description = Listado en la tienda de Google Play -text.link.wiki.description = Wiki oficial de Mindustry -text.linkfail = ¡Error al abrir el enlace!\nLa URL ha sido copiada a su portapapeles -text.editor.web = ¡La versión web no es compatible con el editor!\nDescargue el juego para usarlo. -text.multiplayer.web = ¡Esta versión del juego no admite multijugador!\nPara jugar al modo multijugador desde su navegador, use el enlace \"versión de varios jugadores\" en la página itch.io. -text.gameover = El núcleo fue destruido. -text.highscore = [YELLOW]¡Nueva mejor puntuación! -text.lasted = Duró hasta la ronda -text.level.highscore = Puntuación màs alta: [accent] -text.level.delete.title = Confirmar Eliminación -text.level.delete = ¿Seguro que quieres eliminar el mapa \"[ORANGE] \" {0}? -text.level.select = Selección de nivel -text.level.mode = Modo de juego: -text.savegame = Guardar Partida -text.loadgame = Cargar Partida -text.joingame = Unirse a una Partida -text.newgame = Nueva Partida -text.quit = Salir -text.about.button = Acerca de -text.name = Nombre -text.public = Público -text.players = {0} Jugadores en línea -text.server.player.host = {0} ANFITRIÓN -text.players.single = {0} jugador en línea -text.server.mismatch = Error de paquete: posible desajuste de la versión cliente / servidor.\n¡Asegúrate de que tú y el anfitrión tengáis la última versión de Mindustry! -text.server.closing = [accent] Cerrando servidor ... -text.server.kicked.kick = ¡Has sido expulsado del servidor! -text.server.kicked.invalidPassword = ¡Contraseña inválida! -text.server.kicked.clientOutdated = Cliente desactualizado ¡Actualiza tu juego! -text.server.kicked.serverOutdated = Servidor desactualizado ¡Pidele actualizar al anfitrión! -text.server.kicked.banned = Tu entrada está prohibida en este servidor. -text.server.kicked.recentKick = Has sido echado recientemente.\nEspera antes de conectarte de nuevo. -text.server.connected = se ha unido. -text.server.disconnected = se ha desconectado -text.nohost = ¡No se puede alojar el servidor en un mapa personalizado! -text.host.info = El botón [acento] host [] aloja un servidor en los puertos [escarlata] 6567 [] y [escarlata] 6568. [] Cualquiera en el mismo [LIGHT_GRAY] wifi o red local [] debería poder ver su servidor en su servidor lista. Si desea que las personas puedan conectarse desde cualquier lugar mediante IP, se requiere [acento] reenvío de puerto []. [LIGHT_GRAY] Nota: Si alguien tiene problemas para conectarse a su juego LAN, asegúrese de haber permitido a Mindustry el acceso a su red local en la configuración de su firewall. -text.join.info = Aquí puede ingresar un servidor [acento] IP [] para conectarse, o descubrir servidores de [acento] red local [] para conectarse. Tanto el modo multijugador LAN como WAN son compatibles. [LIGHT_GRAY] Nota: no hay una lista de servidores global automática; si desea conectarse con alguien por IP, deberá solicitar al host su IP. -text.hostserver = Hostear servidor -text.host = Hostear -text.hosting = [acento] Abriendo servidor ... -text.hosts.refresh = Refrescar -text.hosts.discovering = Descubriendo juegos en LAN -text.server.refreshing = Servidor refrescante -text.hosts.none = [lightgray] ¡No se encontraron juegos LAN! -text.host.invalid = [escarlata] No se puede conectar al host. -text.server.friendlyfire = Fuego amigo -text.trace = Rastro del jugador -text.trace.playername = Nombre del jugador: [acento] {0} -text.trace.ip = IP: [acento] {0} -text.trace.id = ID único: [acento] {0} -text.trace.android = Cliente de Android: [acento] {0} -text.trace.modclient = Cliente personalizado: [acento] {0} -text.trace.totalblocksbroken = Total de bloques rotos: [acento] {0} -text.trace.structureblocksbroken = Bloques de estructura rotos: [acento] {0} -text.trace.lastblockbroken = Último bloque roto: [acento] {0} -text.trace.totalblocksplaced = Total de bloques colocados: [acento] {0} -text.trace.lastblockplaced = Último bloque colocado: [acento] {0} -text.invalidid = ID de cliente no válido Presente un informe del error. -text.server.bans = Baneos -text.server.bans.none = ¡No se encontraron jugadores baneados! -text.server.admins = Admins -text.server.admins.none = ¡No se encontraron administradores! -text.server.add = Agregar servidor -text.server.delete = ¿Seguro que quieres eliminar este servidor? -text.server.hostname = Anfitrión: {0} -text.server.edit = Editar servidor -text.server.outdated = [crimson] ¡Servidor obsoleto! [] -text.server.outdated.client = [carmesí] Cliente desactualizado! [] -text.server.version = [lightgray] Versión: {0} -text.server.custombuild = [amarillo] Creación personalizada -text.confirmban = ¿Estás seguro de que quieres prohibir este jugador? -text.confirmunban = ¿Estás seguro de que quieres desbloquear a este jugador? -text.confirmadmin = ¿Seguro que quieres que este jugador sea un administrador? -text.confirmunadmin = ¿Seguro que quieres eliminar el estado de administrador de este reproductor? -text.joingame.byip = Unirse por IP ... -text.joingame.title = Unirse a una partida -text.joingame.ip = IP: -text.disconnect = Desconectado. -text.disconnect.data = ¡Fallo al cargar datos mundiales! -text.connecting = [accent] Conectando ... -text.connecting.data = [accent] Cargando información del mapa... -text.connectfail = [crimson] Fallo al conectar al servidor: [orange] -text.server.port = Puerto: -text.server.addressinuse = ¡Dirección ya en uso! -text.server.invalidport = ¡Número de puerto inválido! -text.server.error = [crimson] Error en la creación del servidor: [orange] -text.tutorial.back = < Anterior -text.tutorial.next = Siguiente > -text.save.new = Nuevo Guardado -text.save.overwrite = ¿Seguro que quieres sobrescribir este juego guardado? -text.overwrite = Sobreescribir -text.save.none = ¡No hay juegos guardados! -text.saveload = [accent] Guardando... -text.savefail = ¡Error al guardar el juego! -text.save.delete.confirm = ¿Estás seguro de que deseas eliminar este guardado? -text.save.delete = Borrar -text.save.export = Exportar guardado -text.save.import.invalid = [orange] ¡Este guardado es inválido! -text.save.import.fail = [crimson] Fallo al importar guardado: [orange] {0} -text.save.export.fail = [crimson] Fallo al exportar guardado: [orange] {0} -text.save.import = Importar Guardado -text.save.newslot = Nombre del guardado: -text.save.rename = Renombrar -text.save.rename.text = Nuevo nombre -text.selectslot = Seleccionar una guardado -text.slot = [accent] Casilla {0} -text.save.corrupted = [orange] ¡Arhivo de guardado corrupto o inválido! -text.empty = -text.on = Encendido -text.off = Apagado -text.save.autosave = Guardado automático: {0} -text.save.map = Mapa: {0} -text.save.wave = Horda: {0} -text.save.difficulty = Dificultad: {0} -text.save.date = Guardado por última vez: {0} -text.confirm = Confirmar -text.delete = Eliiminar -text.ok = OK -text.open = Abrir -text.cancel = Cancelar -text.openlink = Abrir enlace -text.copylink = Copiar link -text.back = Atrás -text.quit.confirm = ¿Seguro que quieres salir? -text.changelog.title = Changelog -text.changelog.loading = Obteniendo changelog ... -text.changelog.error.android = [naranja] Tenga en cuenta que el registro de cambios no funciona en Android 4.4 y versiones posteriores. Esto se debe a un error interno de Android. -text.changelog.error = [escarlata] ¡Error al obtener el registro de cambios! Comprueba tu conexión a Internet. -text.changelog.current = [amarillo] [[Versión actual] -text.changelog.latest = [naranja] [[Última versión] -text.loading = [accent] Cargando... -text.wave = [orange] Horda {0} -text.wave.waiting = Horda en {0} -text.waiting = Esperando... -text.enemies = {0} Enemigos -text.enemies.single = {0} Enemigo -text.loadimage = Cargar imagen -text.saveimage = Guardar imagen -text.oregen = Generación de mineral {0} -text.editor.badsize = [orange]¡Dimensiones de imagen inválidas![]\nDimensiones de mapa válidas: {0} -text.editor.errorimageload = Error al cargar el archivo de imagen: [orange] {0} -text.editor.errorimagesave = Error al guardar el archivo de imagen: [orange] {0} -text.editor.generate = Generar -text.editor.resize = Cambiar tamaño -text.editor.loadmap = Cargar mapa -text.editor.savemap = Guardar mapa -text.editor.loadimage = Cargar imagen -text.editor.saveimage = Guardar imagen -text.editor.unsaved = [scarlet] ¡Tienes cambios sin guardar! [] ¿Estás seguro de que quieres salir? -text.editor.brushsize = Tamaño del pincel: {0} -text.editor.noplayerspawn = ¡Este mapa no tiene punto de aparición del jugador! -text.editor.manyplayerspawns = ¡Los mapas no pueden tener más de un punto de spawn de jugador! -text.editor.manyenemyspawns = {0 }¡No puede tener más de puntos de aparición enemiga! -text.editor.resizemap = Cambiar el tamaño del mapa -text.editor.resizebig = [escarlata] ¡Advertencia! [] Los mapas de más de 256 unidades pueden ser inestables. -text.editor.mapname = Nombre del mapa -text.editor.overwrite = [acento] ¡Advertencia!\nEsto sobrescribe un mapa existente. -text.editor.failoverwrite = [carmesí] ¡No se puede sobrescribir el mapa por defecto! -text.editor.selectmap = Seleccione un mapa para cargar: -text.width = Ancho: -text.height = Altura: -text.randomize = Aleatorizar -text.apply = Aplicar -text.update = Refrescar -text.menu = Menú -text.play = Jugar -text.load = Cargar -text.save = Salvar -text.language.restart = Por favor, reinicie su juego para que la configuración de idioma surta efecto. -text.settings.language = Idioma -text.settings = Ajustes -text.tutorial = Tutorial -text.editor = Editor -text.mapeditor = Editor de Mapas -text.donate = Donar -text.settings.reset = Restablecer los valores predeterminados -text.settings.controls = Controles -text.settings.game = Juego -text.settings.sound = Sonido -text.settings.graphics = Gráficos -text.upgrades = Mejoras -text.purchased = [LIME] Creado! -text.weapons = Armas -text.paused = Pausado -text.respawn = Reapareciendo en -text.info.title = [acento] Información -text.error.title = [carmesí] Se ha producido un error -text.error.crashmessage = [SCARLET] Se ha producido un error inesperado, que habría causado un bloqueo. [] Informe las circunstancias exactas bajo las cuales se produjo este error al desarrollador: [ORANGE] anukendev@gmail.com [] -text.error.crashtitle = Ha ocurrido un error -text.mode.break = Modo de eliminación: -text.mode.place = Modo de colocación: -placemode.hold.name = Línea -placemode.areadelete.name = Àrea -placemode.touchdelete.name = Toque -placemode.holddelete.name = Mantener -placemode.none.name = Ninguno -placemode.touch.name = Toque -placemode.cursor.name = Cursor -text.blocks.extrainfo = [acento] información adicional del bloque: -text.blocks.blockinfo = Información de bloque -text.blocks.powercapacity = Capacidad de energía -text.blocks.powershot = Energía/disparo -text.blocks.powersecond = energía drenada/segundo -text.blocks.powerdraindamage = Energía drenada/daño -text.blocks.shieldradius = Radio del escudo -text.blocks.itemspeedsecond = Velocidad del objeto/segundo -text.blocks.range = Rango -text.blocks.size = Tamaño -text.blocks.powerliquid = Poder/Líquido -text.blocks.maxliquidsecond = máximo líquido/segundo -text.blocks.liquidcapacity = Capacidad de liquido -text.blocks.liquidsecond = Líquido/segundo -text.blocks.damageshot = Daño/ disparo -text.blocks.ammocapacity = Capacidad de munición -text.blocks.ammo = Munición: -text.blocks.ammoitem = Munición/objeto -text.blocks.maxitemssecond = Objetos máximos/segundo -text.blocks.powerrange = Rango de energía -text.blocks.lasertilerange = Rango de poder en casilllas -text.blocks.capacity = Capacidad -text.blocks.itemcapacity = Capacidad de items -text.blocks.maxpowergenerationsecond = Máxima generación de energía/segundo -text.blocks.powergenerationsecond = Generación de energía/segundo -text.blocks.generationsecondsitem = Segundos de generación/Item -text.blocks.input = Ingreso -text.blocks.inputliquid = Entrada de líquidos -text.blocks.inputitem = Entrada de ítems -text.blocks.output = Salida -text.blocks.secondsitem = Segundos/Ítem -text.blocks.maxpowertransfersecond = Máxima transferencia de poder/segundo -text.blocks.explosive = ¡Altamente explosivo! -text.blocks.repairssecond = Reparado / segundo -text.blocks.health = Vida -text.blocks.inaccuracy = Inexactitud -text.blocks.shots = Disparos -text.blocks.shotssecond = Disparos / segundo -text.blocks.fuel = Combustible -text.blocks.fuelduration = Duración del combustible -text.blocks.maxoutputsecond = Máxima salida/segundo -text.blocks.inputcapacity = Capacidad de entrada -text.blocks.outputcapacity = Capacidad de salida -text.blocks.poweritem = Poder/Ítem -text.placemode = Modo colocar -text.breakmode = Modo romper -text.health = Salud -setting.difficulty.easy = Fácil -setting.difficulty.normal = Mormal -setting.difficulty.hard = Difícil -setting.difficulty.insane = Insano -setting.difficulty.purge = Purga +credits.text = Created by [ROYAL]Anuken[] - [SKY]anukendev@gmail.com[]\n\n[GRAY](In case you can't tell, this text is currently unfinished.\nTranslators, don't edit it yet!) +credits = Créditos +contributors = Translators and Contributors +discord = ¡Únete al Discord de Mindustry! +link.discord.description = La sala oficial del Discord de Mindustry +link.github.description = Código fuente del juego +link.dev-builds.description = Versiones de desarrollo inestable +link.trello.description = Tablero de Trello oficial para las características planificadas +link.itch.io.description = itch.io es la página donde podes descargar las versiones para PC y web +link.google-play.description = Ficha en la Google Play Store +link.wiki.description = Wiki oficial de Mindustry +linkfail = ¡Error al abrir el enlace!\nLa URL ha sido copiada a su portapapeles. +screenshot = Screenshot saved to {0} +screenshot.invalid = Map too large, potentially not enough memory for screenshot. +gameover = Tu núcleo ha sido destruido. +gameover.pvp = ¡El equipo[accent] {0}[] ha ganado! +highscore = [accent]¡Nueva mejor puntuación! +stat.wave = Waves Defeated:[accent] {0} +stat.enemiesDestroyed = Enemies Destroyed:[accent] {0} +stat.built = Buildings Built:[accent] {0} +stat.destroyed = Buildings Destroyed:[accent] {0} +stat.deconstructed = Buildings Deconstructed:[accent] {0} +stat.delivered = Resources Launched: +stat.rank = Final Rank: [accent]{0} +placeline = You have selected a block.\nYou can[accent] place in a line[] by[accent] holding down your finger for a few seconds[] and dragging in a direction.\nTry it. +removearea = You have selected removal mode.\nYou can[accent] remove blocks in a rectangle[] by[accent] holding down your finger for a few seconds[] and dragging.\nTry it. +launcheditems = [accent]Launched Items +map.delete = ¿Estás seguro que quieres borrar el mapa "[accent]{0}[]"? +level.highscore = Puntuación más alta: [accent]{0} +level.select = Selección de nivel +level.mode = Modo de juego: +showagain = No mostrar otra vez en la próxima sesión +coreattack = < ¡El núcleo está bajo ataque! > +nearpoint = [[ [scarlet]LEAVE DROP POINT IMMEDIATELY[] ]\nannihilation imminent +outofbounds = [[ OUT OF BOUNDS ]\n[]self-destruct in {0} +database = Core Database +savegame = Guardar Partida +loadgame = Cargar Partida +joingame = Unirse a la Partida +addplayers = Agregar/Quitar Jugadores +customgame = Partida personalizada +newgame = New Game +none = +minimap = Minimap +close = Cerrar +quit = Salir +maps = Mapas +continue = Continuar +maps.none = [LIGHT_GRAY]¡No se han encontrado mapas! +about.button = Acerca de +name = Nombre: +noname = Pick a[accent] player name[] first. +filename = Nombre del archivo: +unlocked = ¡Nuevo Bloque Desbloqueado! +completed = [accent]Completed +techtree = Tech Tree +research.list = [LIGHT_GRAY]Research: +research = Research +researched = [LIGHT_GRAY]{0} researched. +players = {0} jugadores online +players.single = {0} jugador online +server.closing = [accent]Cerrando servidor... +server.kicked.kick = ¡Has sido expulsado del servidor! +server.kicked.serverClose = El servidor ha cerrado. +server.kicked.clientOutdated = ¡Cliente desactualizado! ¡Actualiza tu juego! +server.kicked.serverOutdated = ¡Servidor desactualizado! ¡Pídele al anfitrión que lo actualice! +server.kicked.banned = Has sido baneado del servidor. +server.kicked.recentKick = Has sido expulsado recientemente.\nEspera para poder conectarte de nuevo. +server.kicked.nameInUse = Ya hay alguien con ese\nnombre en el servidor. +server.kicked.nameEmpty = Tu nombre debe por lo menos contener un carácter o número. +server.kicked.idInUse = ¡Ya estás en el servidor! Conectarse con dos cuentas no está permitido. +server.kicked.customClient = Este servidor no soporta versiones personalizadas. Descarga una versión oficial. +server.kicked.gameover = Game over! +host.info = El botón [accent]host[] hostea un servidor en el puerto [scarlet]6567[]. \nCualquier persona en la misma [LIGHT_GRAY]wifi o red local[] debería poder ver tu servidor en la lista de servidores.\n\nSi quieres que cualquier persona se pueda conectar de cualquier lugar por IP, la [accent]asignación de puertos[] es requerida.\n\n[LIGHT_GRAY]Nota: Si alguien experimenta problemas conectándose a tu partida LAN, asegúrate de permitir a Mindustry acceso a tu red local mediante la configuración de tu firewall. +join.info = Aquí, puedes escribir la [accent]IP de un server[] para conectarte, o descubrir servidores de [accent]red local[] para conectarte.\nLAN y WAN es soportado para jugar en multijugador.\n\n[LIGHT_GRAY]Nota: No hay una lista automática global de servidores; si quieres conectarte por IP, tendrás que preguntarle al anfitrión por la IP. +hostserver = Hostear Servidor +hostserver.mobile = Hostear\nJuego +host = Hostear +hosting = [accent]Abriendo servidor... +hosts.refresh = Actualizar +hosts.discovering = Descubrir partidas LAN +server.refreshing = Actualizando servidor... +hosts.none = [lightgray]¡No se han encontrado partidas LAN! +host.invalid = [scarlet]No se ha podido conectar al anfitrión. +trace = Rastrear Jugador +trace.playername = Nombre de jugador: [accent]{0} +trace.ip = IP: [accent]{0} +trace.id = ID Única: [accent]{0} +trace.mobile = Mobile Client: [accent]{0} +trace.modclient = Cliente Personalizado: [accent]{0} +invalidid = ¡ID de cliente inválida! Envía un informe del error. +server.bans = Baneos +server.bans.none = ¡Ningún usuario ha sido baneado! +server.admins = Administradores +server.admins.none = ¡Ningún administrador ha sido encontrado! +server.add = Agregar Servidor +server.delete = ¿Estás seguro de querer borrar este servidor? +server.hostname = Anfitrión: {0} +server.edit = Editar Servidor +server.outdated = [crimson]¡Servidor desactualizado![] +server.outdated.client = [crimson]¡Cliente desactualizado![] +server.version = [lightgray]Versión: {0} +server.custombuild = [yellow]Versión personalizada +confirmban = ¿Estás seguro de querer banear este jugador? +confirmkick = ¿Estás seguro de querer expulsar este jugador? +confirmunban = ¿Estás seguro de querer desbanear este jugador? +confirmadmin = ¿Estás seguro de querer hacer administrador a este jugador? +confirmunadmin = ¿Estás seguro de querer quitar los permisos de administrador a este jugador? +joingame.title = Unirse a la partida +joingame.ip = IP: +disconnect = Desconectado. +disconnect.data = ¡Se ha fallado la carga de datos del mundo! +connecting = [accent]Conectando... +connecting.data = [accent]Cargando datos del mundo... +server.port = Puerto: +server.addressinuse = ¡La dirección ya está en uso! +server.invalidport = ¡El número de puerto es invalido! +server.error = [crimson]Error hosteando el servidor: error [accent]{0} +save.old = Este punto de guardado es de una versión más antigua de este juego, y ya no puede ser usada.\n\n[LIGHT_GRAY]La retrocmpatibilidad de los puntos de guardado estará completamente implementada en la versión 4.0. +save.new = Nuevo Punto de Guardado +save.overwrite = ¿Estás seguro de querer sobrescribir\neste punto de guardado? +overwrite = Sobrescribir +save.none = ¡No se ha encontrado ningún punto de guardado! +saveload = [accent]Guardando... +savefail = ¡No se ha podido guardar la partida! +save.delete.confirm = ¿Estás seguro de querer borrar este punto de guardado? +save.delete = Borrar +save.export = Exportar Punto de Guardado +save.import.invalid = [accent]¡Este punto de guardado es inválido! +save.import.fail = [crimson]La importación del punto de guardado ha fallado: error [accent]{0} +save.export.fail = [crimson]La exportación del punto de guardado ha fallado: error [accent]{0} +save.import = Importar Punto de Guardado +save.newslot = Nombre del Punto de Guardado: +save.rename = Renombrar +save.rename.text = Nuevo nombre: +selectslot = Selecciona un Punto de Guardado. +slot = [accent]Casilla {0} +save.corrupted = [accent]¡El punto de guardado está corrupto o es inválido!\nSi acabas de actualizar el juego, esto debe ser probablemente un cambio en el formato de guardado y[scarlet] no[] un error. +empty = +on = Encendido +off = Apagado +save.autosave = Autoguardado: {0} +save.map = Mapa: {0} +save.wave = Horda {0} +save.difficulty = Dificultad: {0} +save.date = Última vez guardado: {0} +save.playtime = Tiempo de juego: {0} +warning = Warning. +confirm = Confirmar +delete = Borrar +ok = OK +open = Abrir +customize = Customize +cancel = Cancelar +openlink = Abrir Enlace +copylink = Copiar Enlace +back = Atrás +quit.confirm = ¿Estás seguro de querer salir de la partida? +changelog.title = Registro de Parches +changelog.loading = Consiguiendo el registro de parches... +changelog.error.android = [accent]¡Nota: el registro de parches a veces no funciona en Android 4.4 o inferior!\nEsto es por un error interno de Android. +changelog.error.ios = [accent]El registro de parches no está actualmente soportado para iOS. +changelog.error = [scarlet]¡Error consiguiendo el registro de parches!Comprueba tu conexión a Internet. +changelog.current = [yellow][[Versión actual] +changelog.latest = [accent][[Última version] +loading = [accent]Cargando... +saving = [accent]Guardando... +wave = [accent]Horda {0} +wave.waiting = Horda en {0} +wave.waveInProgress = [LIGHT_GRAY]Wave in progress +waiting = Esperando... +waiting.players = Esperando jugadores... +wave.enemies = [LIGHT_GRAY]{0} Enemigos Restantes +wave.enemy = [LIGHT_GRAY]{0} Enemigo Restante +loadimage = Cargar Imagen +saveimage = Guardar Imagen +unknown = Desconocido +custom = Personalizado +builtin = Incorporado +map.delete.confirm = ¿Estás seguro de querer borrar este mapa? ¡Recuerda que está acción no puede sdeshacerse! +map.random = [accent]Mapa Aleatorio +map.nospawn = ¡Este mapa no tiene ningún núcleo en el cual pueda aparecer el jugador! Agrega un núcleo[ROYAL] blue[] al mapa con el editor. +map.nospawn.pvp = ¡Este mapa no tiene ningún núcleo enemigo para que aparezca el jugador! Añade un núcleo[SCARLET] red[] a este mapa en el editor. +map.nospawn.attack = This map does not have any enemy cores for player to attack! Add[SCARLET] red[] cores to this map in the editor. +map.invalid = Error cargando el mapa: archivo corrupto o inválido. +editor.brush = Pincel +editor.openin = Abrir en el Editor +editor.oregen = Generación de Minerales +editor.oregen.info = Generación de Minerales: +editor.mapinfo = Info del Mapa +editor.author = Autor: +editor.description = Descripción: +editor.waves = Waves: +editor.rules = Rules: +editor.ingame = Edit In-Game +waves.title = Waves +waves.remove = Remove +waves.never = +waves.every = every +waves.waves = wave(s) +waves.perspawn = per spawn +waves.to = to +waves.boss = Boss +waves.preview = Preview +waves.edit = Edit... +waves.copy = Copy to Clipboard +waves.load = Load from Clipboard +waves.invalid = Invalid waves in clipboard. +waves.copied = Waves copied. +editor.default = [LIGHT_GRAY] +edit = Edit... +editor.name = Nombre: +editor.spawn = Spawn Unit +editor.removeunit = Remove Unit +editor.teams = Equipos +editor.elevation = Elevación +editor.errorload = Error loading file:\n[accent]{0} +editor.errorsave = Error saving file:\n[accent]{0} +editor.errorimage = That's an image, not a map. Don't go around changing extensions expecting it to work.\n\nIf you want to import a legacy map, use the 'import legacy map' button in the editor. +editor.errorlegacy = This map is too old, and uses a legacy map format that is no longer supported. +editor.errorheader = This map file is either not valid or corrupt. +editor.errorname = Map has no name defined. +editor.update = Update +editor.randomize = Randomize +editor.apply = Apply +editor.generate = Generar +editor.resize = Cambiar Tamaño +editor.loadmap = Cargar Mapa +editor.savemap = Guardar Mapa +editor.saved = ¡Guardado! +editor.save.noname = ¡Tu mapa no tiene un nombre! Pon uno en el menú 'Info del Mapa'. +editor.save.overwrite = ¡Tu mapa sobrescribe uno ya incorporado! Elige un nombre diferente en el menú 'Info del Mapa'. +editor.import.exists = [scarlet]¡No se ha podido importar:[] un mapa incorporado con el nombre '{0}' ya existe! +editor.import = Importar... +editor.importmap = Importar Mapa +editor.importmap.description = Importar un mapa ya existente +editor.importfile = Importar Archivo +editor.importfile.description = Importar un archivo externo del mapa +editor.importimage = Importar Imagen del Terreno +editor.importimage.description = Importar archivo externo de imagen del mapa +editor.export = Exportar... +editor.exportfile = Exportar Archivo +editor.exportfile.description = Exportar archivo del mapa +editor.exportimage = Exportar Imagen del Terreno +editor.exportimage.description = Exportar archivo de imagen del mapa +editor.loadimage = Importar Terreno +editor.saveimage = Exportar Terreno +editor.unsaved = [scarlet]¡Tienes cambios sin guardar![]\n¿Estás seguro de querer salir? +editor.resizemap = Cambiar Tamaño del Mapa +editor.mapname = Nombre del Mapa: +editor.overwrite = [accent]¡Advertencia!\nEsto sobrescribe un mapa ya existente. +editor.overwrite.confirm = [scarlet]¡Advertencia![] Un mapa con ese nombre ya existe. ¿Estás seguro de querer sobrescribirlo? +editor.selectmap = Selecciona un mapa para cargar: +filters.empty = [LIGHT_GRAY]No filters! Add one with the button below. +filter.distort = Distort +filter.noise = Noise +filter.ore = Ore +filter.rivernoise = River Noise +filter.scatter = Scatter +filter.terrain = Terrain +filter.option.scale = Scale +filter.option.chance = Chance +filter.option.mag = Magnitude +filter.option.threshold = Threshold +filter.option.circle-scale = Circle Scale +filter.option.octaves = Octaves +filter.option.falloff = Falloff +filter.option.block = Block +filter.option.floor = Floor +filter.option.wall = Wall +filter.option.ore = Ore +filter.option.floor2 = Secondary Floor +filter.option.threshold2 = Secondary Threshold +filter.option.radius = Radius +filter.option.percentile = Percentile +width = Ancho: +height = Alto: +menu = Menu +play = Jugar +load = Cargar +save = Guardar +fps = FPS: {0} +tps = TPS: {0} +ping = Ping: {0} ms +language.restart = Por favor reinicie el juego para que los cambios del lenguaje surjan efecto. +settings = Ajustes +tutorial = Tutorial +editor = Editor +mapeditor = Editor de Mapa +donate = Donar +abandon = Abandon +abandon.text = This zone and all its resources will be lost to the enemy. +locked = Locked +complete = [LIGHT_GRAY]Complete: +zone.requirement = Wave {0} in zone {1} +resume = Resume Zone:\n[LIGHT_GRAY]{0} +bestwave = [LIGHT_GRAY]Best: {0} +launch = Launch +launch.title = Launch Successful +launch.next = [LIGHT_GRAY]next opportunity at wave {0} +launch.unable = [scarlet]Unable to LAUNCH.[] Enemies. +launch.confirm = This will launch all resources in your core.\nYou will not be able to return to this base. +uncover = Uncover +configure = Configure Loadout +configure.locked = [LIGHT_GRAY]Reach wave {0}\nto configure loadout. +zone.unlocked = [LIGHT_GRAY]{0} unlocked. +zone.requirement.complete = Wave {0} reached:\n{1} zone requirements met. +zone.config.complete = Wave {0} reached:\nLoadout config unlocked. +zone.resources = Resources Detected: +add = Add... +boss.health = Boss Health +connectfail = [crimson]Ha fallado la conexión con el servidor: [accent]{0} +error.unreachable = Servidor inaccesible. +error.invalidaddress = Dirección inválida. +error.timedout = ¡Se acabó el tiempo!\n¡Asegúrate que el host ha hecho el port forwarding, y que la dirección es correcta! +error.mismatch = Error de paquete:\nposible versión no válida del servidor/cliente.\nAsegúrate de que tú y el host tenéis la última versión de Mindustry. +error.alreadyconnected = Ya estás conectado. +error.mapnotfound = ¡Archivo de mapa no encontrado! +error.io = Network I/O error. +error.any = Error de red desconocido. +zone.groundZero.name = Ground Zero +zone.desertWastes.name = Desert Wastes +zone.craters.name = The Craters +zone.frozenForest.name = Frozen Forest +zone.ruinousShores.name = Ruinous Shores +zone.stainedMountains.name = Stained Mountains +zone.desolateRift.name = Desolate Rift +zone.nuclearComplex.name = Nuclear Production Complex +zone.overgrowth.name = Overgrowth +zone.tarFields.name = Tar Fields +settings.language = Lenguaje +settings.reset = Reiniciar por los de defecto +settings.rebind = Reasignar +settings.controls = Controles +settings.game = Juego +settings.sound = Sonido +settings.graphics = Gráficos +settings.cleardata = Limpiar Datos del Juego... +settings.clear.confirm = ¿Estas seguro de querer limpiar estos datos?\n¡Esta acción no puede deshacerse! +settings.clearall.confirm = [scarlet]ADVERTENCIA![]\nEsto va a eliminar todos tus datos, incluyendo guardados, mapas, desbloqueos y keybinds.\nUna vez presiones 'ok', el juego va a borrrar todos tus datos y saldrá del juego automáticamente. +settings.clearunlocks = Eliminar Desbloqueos +settings.clearall = Eliminar Todo +paused = Pausado +yes = Sí +no = No +info.title = [accent]Información +error.title = [crimson]Un error ha ocurrido. +error.crashtitle = Un error ha ocurrido. +blocks.input = Input +blocks.output = Output +blocks.booster = Booster +block.unknown = [LIGHT_GRAY]??? +blocks.powercapacity = Capacidad de Energía +blocks.powershot = Energía/Disparo +blocks.targetsair = Apunta al Aire +blocks.targetsground = Targets Ground +blocks.itemsmoved = Move Speed +blocks.launchtime = Time Between Launches +blocks.shootrange = Rango +blocks.size = Tamaño +blocks.liquidcapacity = Capacidad de Líquidos +blocks.powerrange = Rango de Energía +blocks.poweruse = Consumo de Energía +blocks.powerdamage = Energía/Daño +blocks.itemcapacity = Capacidad de Objetos +blocks.basepowergeneration = Generación de energía base +blocks.productiontime = Production Time +blocks.repairtime = Block Full Repair Time +blocks.speedincrease = Speed Increase +blocks.range = Range +blocks.drilltier = Taladrables +blocks.drillspeed = Velocidad Base del Taladro +blocks.boosteffect = Boost Effect +blocks.maxunits = Max Active Units +blocks.health = Vida +blocks.buildtime = Build Time +blocks.inaccuracy = Imprecisión +blocks.shots = Disparos +blocks.reload = Recarga +blocks.ammo = Ammo +bar.drillspeed = Drill Speed: {0}/s +bar.efficiency = Efficiency: {0}% +bar.powerbalance = Power: {0} +bar.poweramount = Power: {0} +bar.poweroutput = Power Output: {0} +bar.items = Items: {0} +bar.liquid = Liquid +bar.heat = Heat +bar.power = Power +bar.progress = Build Progress +bar.spawned = Units: {0}/{1} +bullet.damage = [stat]{0}[lightgray] dmg +bullet.splashdamage = [stat]{0}[lightgray] area dmg ~[stat] {1}[lightgray] tiles +bullet.incendiary = [stat]incendiary +bullet.homing = [stat]homing +bullet.shock = [stat]shock +bullet.frag = [stat]frag +bullet.knockback = [stat]{0}[lightgray] knockback +bullet.freezing = [stat]freezing +bullet.tarred = [stat]tarred +bullet.multiplier = [stat]{0}[lightgray]x ammo multiplier +bullet.reload = [stat]{0}[lightgray]x reload +unit.blocks = bloques +unit.powersecond = unidades de energía/segundo +unit.liquidsecond = unidades de líquido/segundo +unit.itemssecond = objetos/segundo +unit.liquidunits = unidades de líquido +unit.powerunits = unidades de energía +unit.degrees = grados +unit.seconds = segundos +unit.persecond = /sec +unit.timesspeed = x speed +unit.percent = % +unit.items = objetos +category.general = General +category.power = Energía +category.liquids = Líquidos +category.items = Objetos +category.crafting = Fabricación +category.shooting = Disparo +category.optional = Mejoras Opcionales +setting.landscape.name = Lock Landscape +setting.shadows.name = Shadows +setting.linear.name = Linear Filtering +setting.animatedwater.name = Animated Water +setting.animatedshields.name = Animated Shields +setting.antialias.name = Antialias[LIGHT_GRAY] (requires restart)[] +setting.indicators.name = Ally Indicators +setting.autotarget.name = Auto apuntado +setting.fpscap.name = Máx FPS +setting.fpscap.none = Nada +setting.fpscap.text = {0} FPS +setting.swapdiagonal.name = Always Diagonal Placement +setting.difficulty.training = entrenamiento +setting.difficulty.easy = fácil +setting.difficulty.normal = normal +setting.difficulty.hard = difícil +setting.difficulty.insane = locura setting.difficulty.name = Dificultad: -setting.screenshake.name = Shake de pantalla -setting.smoothcam.name = Cámara lisa -setting.indicators.name = Indicador del enemigo -setting.effects.name = Mostrar efectos -setting.sensitivity.name = Sensibilidad del controlador -setting.saveinterval.name = Intervalo de autoguardado -setting.seconds = Segundos -setting.fullscreen.name = Pantalla completa -setting.multithread.name = Multithreading -setting.fps.name = Mostrar fps +setting.screenshake.name = Movimiento de la Pantalla +setting.effects.name = Mostrar Efectos +setting.sensitivity.name = Sensibilidad del Control +setting.saveinterval.name = Intervalo del Auto-guardado +setting.seconds = {0} Segundos +setting.fullscreen.name = Pantalla Completa +setting.borderlesswindow.name = Borderless Window[LIGHT_GRAY] (may require restart) +setting.fps.name = Mostrar FPS setting.vsync.name = VSync -setting.lasers.name = Mostrar láseres de poder -setting.previewopacity.name = Colocando Vista Previa Opacidad -setting.healthbars.name = Mostrar barras de vida de enemigos y jugadores -setting.pixelate.name = Pixelear pantalla -setting.musicvol.name = Volumen de la música -setting.mutemusic.name = Apagar música +setting.lasers.name = Mostrar Energía de los Láseres +setting.pixelate.name = Pixelate [LIGHT_GRAY](may decrease performance) +setting.minimap.name = Mostrar Minimapa +setting.musicvol.name = Volumen de la Música +setting.mutemusic.name = Silenciar Musica setting.sfxvol.name = Volumen de los efectos de sonido -setting.mutesound.name = Apagar sonidos -map.maze.name = Laberinto -map.fortress.name = Fortaleza -map.sinkhole.name = Sumidero -map.caves.name = Cuevas -map.volcano.name = Volcán -map.caldera.name = Caldera -map.scorch.name = Desierto volcánico -map.desert.name = Desierto -map.island.name = Isla -map.grassland.name = Pastizal -map.tundra.name = Tundra -map.spiral.name = Espiral -map.tutorial.name = Tutorial -tutorial.intro.text = [amarillo] Bienvenido al tutorial. [] Para comenzar, presione 'siguiente'. -tutorial.moveDesktop.text = Para moverse, use las teclas [naranja] [[WASD] []. Mantenga [naranja] shift [] para impulsar. Mantenga presionada la tecla [naranja] CTRL [] mientras usa la rueda de desplazamiento [naranja] [] para acercar o alejar la imagen. -tutorial.shoot.text = Usa el mouse para apuntar, mantén presionado [naranja] el botón izquierdo del mouse [] para disparar. Intenta practicar en el objetivo [amarillo] []. -tutorial.moveAndroid.text = Para recorrer la vista, arrastre un dedo por la pantalla. Pellizque y arrastre para acercar o alejar. -tutorial.placeSelect.text = Intente seleccionar un transportador [amarillo] [] desde el menú del bloque en la parte inferior derecha. -tutorial.placeConveyorDesktop.text = Utilice [naranja] [[rueda de desplazamiento] [] para girar la cinta transportadora hacia [naranja] hacia adelante [], luego colóquela en la ubicación [amarilla] marcada [] usando [naranja] [[botón izquierdo del mouse] []. -tutorial.placeConveyorAndroid.text = Utilice [naranja] [[girar el botón] [] para girar el transportador hacia [naranja] hacia delante [], arrástrelo con un dedo, luego colóquelo en la ubicación [amarilla] marcada [] usando [naranja] [[marca de verificación][]. -tutorial.placeConveyorAndroidInfo.text = Alternativamente, puede presionar el icono de la cruz en la parte inferior izquierda para cambiar a [naranja] [[modo táctil] [] y colocar bloques tocando en la pantalla. En modo táctil, los bloques se pueden girar con la flecha en la parte inferior izquierda. Presione [amarillo] al lado [] para probarlo. -tutorial.placeDrill.text = Ahora, seleccione y coloque un taladro de piedra [amarillo] [] en la ubicación marcada. -tutorial.blockInfo.text = Si desea obtener más información sobre un bloque, puede tocar el signo de interrogación [naranja] [] en la parte superior derecha para leer su descripción. -tutorial.deselectDesktop.text = Puede deseleccionar un bloque usando el [naranja] [[botón derecho del mouse] []. -tutorial.deselectAndroid.text = Puede deseleccionar un bloque presionando el botón [naranja] X []. -tutorial.drillPlaced.text = El taladro ahora producirá piedra [amarilla], [] la enviará al transportador y luego la moverá al núcleo [amarillo] []. -tutorial.drillInfo.text = Diferentes minerales necesitan diferentes ejercicios. La piedra requiere taladros de piedra, el hierro requiere taladros de hierro, etc. -tutorial.drillPlaced2.text = Mover elementos al núcleo los coloca en su inventario de elementos [amarillo] [], en la esquina superior izquierda. Colocar bloques utiliza elementos de tu inventario. -tutorial.moreDrills.text = Puede vincular muchos taladros y cintas transportadoras juntas, de esa manera. -tutorial.deleteBlock.text = Puede eliminar bloques haciendo clic en el botón [naranja] del botón derecho del mouse [] en el bloque que desea eliminar. Intente eliminar este transportador. -tutorial.deleteBlockAndroid.text = Puede eliminar bloques mediante [naranja] seleccionando la cruz [] en el menú [naranja] del modo de interrupción [] en la parte inferior izquierda y tocando un bloque. Intente eliminar este transportador. -tutorial.placeTurret.text = Ahora, seleccione y coloque una torreta [amarilla] [] en la ubicación marcada [amarilla] []. -tutorial.placedTurretAmmo.text = Esta torre ahora aceptará [amarillo] munición [] del transportador. Puedes ver la cantidad de munición que tiene al pasar el mouse sobre ella y verificar la barra verde [verde] []. -tutorial.turretExplanation.text = Las torretas dispararán automáticamente al enemigo más cercano al alcance, siempre que tengan suficiente munición. -tutorial.waves.text = Cada [amarillo] 60 [] segundos, una ola de [coral] enemigos [] aparecerán en lugares específicos e intentarán destruir el núcleo. -tutorial.coreDestruction.text = Tu objetivo es [amarillo] defender el núcleo []. Si se destruye el núcleo, tú [coral] pierdes el juego []. -tutorial.pausingDesktop.text = Si alguna vez necesita tomar un descanso, presione el botón de pausa [naranja] [] en el espacio superior izquierdo o [naranja] [] para detener el juego. Aún puede seleccionar y colocar bloques mientras está en pausa, pero no puede mover o disparar. -tutorial.pausingAndroid.text = Si alguna vez necesita tomarse un descanso, presione el botón de pausa [naranja] [] en la parte superior izquierda para pausar el juego. Todavía puede romper y colocar bloques mientras está en pausa. -tutorial.purchaseWeapons.text = Puedes comprar nuevas armas [amarillas] [] para tu mech abriendo el menú de actualización en la esquina inferior izquierda. -tutorial.switchWeapons.text = Cambie las armas haciendo clic en su icono en la esquina inferior izquierda o usando números [naranja] [[1-9] []. -tutorial.spawnWave.text = Aquí viene una ola ahora. Destruyelos. -tutorial.pumpDesc.text = En olas posteriores, es posible que deba usar bombas [amarillas] [] para distribuir líquidos para generadores o extractores. -tutorial.pumpPlace.text = Las bombas funcionan de manera similar a los taladros, excepto que producen líquidos en lugar de artículos. Intente colocar una bomba en el aceite designado [amarillo] []. -tutorial.conduitUse.text = Ahora coloque un conducto [naranja] [] que se aleje de la bomba. -tutorial.conduitUse2.text = Y algunos más ... -tutorial.conduitUse3.text = Y algunos más ... -tutorial.generator.text = Ahora, coloque un bloque [naranja] de generador de combustión [] al final del conducto. -tutorial.generatorExplain.text = Este generador ahora creará energía [amarilla] [] del aceite. -tutorial.lasers.text = La potencia se distribuye usando láseres de potencia [amarillos] []. Gira y coloca uno aquí. -tutorial.laserExplain.text = El generador ahora moverá energía al bloque láser. Un haz [amarillo] opaco [] significa que está transmitiendo corriente, y un haz [amarillo] transparente [] significa que no lo está. -tutorial.laserMore.text = Puedes verificar cuánta potencia tiene un bloque al pasar el mouse sobre él y verificar la barra amarilla [amarilla] [] en la parte superior. -tutorial.healingTurret.text = Este láser se puede usar para alimentar una torreta de reparación de [cal] []. Coloca uno aquí. -tutorial.healingTurretExplain.text = Mientras tenga energía, esta torreta [cal] reparará los bloques cercanos. [] ¡Al jugar, asegúrate de obtener uno en tu base lo más rápido posible! -tutorial.smeltery.text = Muchos bloques requieren [naranja] acero [] para fabricar, lo que requiere una fundición [naranja] [] para fabricar. Coloca uno aquí. -tutorial.smelterySetup.text = Esta fundición producirá acero [naranja] [] a partir de la plancha de entrada, utilizando carbón como combustible. -tutorial.tunnelExplain.text = También tenga en cuenta que los artículos pasan por un bloque de túnel [naranja] [] y salen del otro lado, pasando por el bloque de piedra. Tenga en cuenta que los túneles solo pueden atravesar hasta 2 bloques. -tutorial.end.text = ¡Y eso concluye el tutorial! ¡Buena suerte! -text.keybind.title = Vuelva a conectar las llaves -keybind.move_x.name = mover_x -keybind.move_y.name = mover_y -keybind.select.name = Elija -keybind.break.name = Romper -keybind.shoot.name = ¡Dispara! -keybind.zoom_hold.name = Enfoque_Mantener -keybind.zoom.name = Enfoquè -keybind.block_info.name = Bloque_informacion -keybind.menu.name = Menú +setting.mutesound.name = Silenciar Sonido +setting.crashreport.name = Enviar informes de fallos anónimos +setting.chatopacity.name = Chat Opacity +setting.playerchat.name = Display In-Game Chat +keybind.title = Reasignar Teclas +category.general.name = General +category.view.name = Visión +category.multiplayer.name = Multijugador +command.attack = Atacar +command.retreat = Retirarse +command.patrol = Patrullar +keybind.gridMode.name = Block Select +keybind.gridModeShift.name = Category Select +keybind.press = Presiona una tecla... +keybind.press.axis = Pulsa un eje o botón... +keybind.screenshot.name = Map Screenshot +keybind.move_x.name = Mover x +keybind.move_y.name = Mover y +keybind.select.name = Seleccionar +keybind.diagonal_placement.name = Diagonal Placement +keybind.pick.name = Pick Block +keybind.break_block.name = Break Block +keybind.deselect.name = Deseleccionar +keybind.shoot.name = Disparar +keybind.zoom_hold.name = Mantener Zoom +keybind.zoom.name = Zoom +keybind.menu.name = Menu keybind.pause.name = Pausa -keybind.dash.name = Deslizar +keybind.minimap.name = Minimap +keybind.dash.name = Correr keybind.chat.name = Chat -keybind.player_list.name = Jugadores_lista -keybind.console.name = Console -keybind.rotate_alt.name = Rotacion_alt -keybind.rotate.name = Girar -keybind.weapon_1.name = Arma_1 -keybind.weapon_2.name = Arma_2 -keybind.weapon_3.name = Arma_3\n -keybind.weapon_4.name = Arma_4 -keybind.weapon_5.name = Arma_5 -keybind.weapon_6.name = Arma6 -mode.text.help.title = Descripción de modos -mode.waves.name = Hordas -mode.waves.description = El modo normal. Recursos limitados y las hordas vendrán automáticamente -mode.sandbox.name = Sandbox -mode.sandbox.description = Recursos infinitos y sin temporizador para las olas. -mode.freebuild.name = Construcción libre -mode.freebuild.description = Recursos limitados y sin tiempo definido para las hordas -upgrade.standard.name = Estandar -upgrade.standard.description = El mech estándar. -upgrade.blaster.name = Blaster -upgrade.blaster.description = Dispara una bala lenta y débil. -upgrade.triblaster.name = Triblaster -upgrade.triblaster.description = Dispara 3 balas en una extensión. -upgrade.clustergun.name = Cleaster Gun -upgrade.clustergun.description = Dispara una propagación inexacta de granadas explosivas. -upgrade.beam.name = Cañòn Beam -upgrade.beam.description = Dispara un rayo láser perforante de largo alcance. -upgrade.vulcan.name = Volcán -upgrade.vulcan.description = Dispara una ráfaga de balas rapidas -upgrade.shockgun.name = Pistola Shock\n -upgrade.shockgun.description = Dispara una ráfaga devastadora de metralla cargada. -item.stone.name = Piedra -item.iron.name = Hierro +keybind.player_list.name = Lista de jugadores +keybind.console.name = consola +keybind.rotate.name = Rotar +keybind.toggle_menus.name = Alternar menús +keybind.chat_history_prev.name = Historial de chat anterior +keybind.chat_history_next.name = Historial de chat siguiente +keybind.chat_scroll.name = Chat scroll +keybind.drop_unit.name = drop unit +keybind.zoom_minimap.name = Zoom minimapa +mode.help.title = Descripción de modos +mode.survival.name = Survival +mode.survival.description = The normal mode. Limited resources and automatic incoming waves. +mode.sandbox.name = sandbox +mode.sandbox.description = Recursos ilimitados y sin temporizador para las hordas. +mode.pvp.name = PvP +mode.pvp.description = Pelea contra otros jugadores localmente. +mode.attack.name = Attack +mode.attack.description = No waves, with the goal to destroy the enemy base. +mode.custom = Custom Rules +rules.infiniteresources = Infinite Resources +rules.wavetimer = Wave Timer +rules.waves = Waves +rules.enemyCheat = Infinite AI Resources +rules.unitdrops = Unit Drops +rules.unitbuildspeedmultiplier = Unit Creation Speed Multiplier +rules.unithealthmultiplier = Unit Health Multiplier +rules.playerhealthmultiplier = Player Health Multiplier +rules.playerdamagemultiplier = Player Damage Multiplier +rules.unitdamagemultiplier = Unit Damage Multiplier +rules.enemycorebuildradius = Enemy Core No-Build Radius:[LIGHT_GRAY] (tiles) +rules.respawntime = Respawn Time:[LIGHT_GRAY] (sec) +rules.wavespacing = Wave Spacing:[LIGHT_GRAY] (sec) +rules.buildcostmultiplier = Build Cost Multiplier +rules.buildspeedmultiplier = Build Speed Multiplier +rules.waitForWaveToEnd = Waves wait for enemies +rules.dropzoneradius = Drop Zone Radius:[LIGHT_GRAY] (tiles) +rules.respawns = Max respawns per wave +rules.limitedRespawns = Limit Respawns +rules.title.waves = Waves +rules.title.respawns = Respawns +rules.title.resourcesbuilding = Resources & Building +rules.title.player = Players +rules.title.enemy = Enemies +rules.title.unit = Units +content.item.name = Objetos +content.liquid.name = Líquidos +content.unit.name = Unidades +content.block.name = Blocks +content.mech.name = Mecanoides +item.copper.name = Cobre +item.copper.description = Un útil material estructural. Usado extensivamente en todo tipo de bloques. +item.lead.name = Plomo +item.lead.description = Un material básico. Usado extensivamente en electrónicos y bloques de transferencia de líquidos. item.coal.name = Carbón -item.steel.name = Acero +item.coal.description = Un combustible común y preparado para ser quemado. +item.graphite.name = Graphite item.titanium.name = Titanio -item.dirium.name = Dirio -item.uranium.name = Uranio +item.titanium.description = Un metal raro super ligero usado extensivamente en transportación de liquidos, taladros y aeronaves. +item.thorium.name = Torio +item.thorium.description = Un metal radiactivo, muy denso usado en soporte de estructuras y combustible nuclear. +item.silicon.name = Silicona +item.silicon.description = Un semiconductor muy útil, se usa para paneles solares y muchos electrónicos complejos. +item.plastanium.name = Plastanio +item.plastanium.description = Un material dúctil, ligero usado en aeronaves y proyectiles de fragmentación. +item.phase-fabric.name = Tejido de fase +item.phase-fabric.description = Una sustancia casi sin peso usada en electrónica avanzada y en tecnología autoreparadora. +item.surge-alloy.name = Surge Alloy +item.surge-alloy.description = Una aleación avanzada con propiedades eléctricas únicas. +item.spore-pod.name = Spore Pod +item.spore-pod.description = Used for conversion into oil, explosives and fuel. item.sand.name = Arena +item.sand.description = Un material común que es usado extensivamente en la fundición, para alear y como fundente. +item.blast-compound.name = Compuesto Explosivo +item.blast-compound.description = Un compuesto volatil usado en bombas y explosivos. Aunque se puede quemar como combustible, esto no es recomendable. +item.pyratite.name = Pirotita +item.pyratite.description = Una sustancia extremadamente inflamable usada en armas incendiarias. +item.metaglass.name = Metaglass +item.metaglass.description = A super-tough glass compound. Extensively used for liquid distribution and storage. +item.scrap.name = Scrap +item.scrap.description = Leftover remnants of old structures and units. Contains trace amounts of many different metals. liquid.water.name = Agua -liquid.plasma.name = Plasma -liquid.lava.name = Lava -liquid.oil.name = Aceite -block.weaponfactory.name = Fábrica de armas -block.weaponfactory.fulldescription = Se usa para crear armas para el jugador mech. Haga clic para usar. Automáticamente toma recursos del núcleo. -block.air.name = Aire -block.blockpart.name = Bloque +liquid.slag.name = Slag +liquid.oil.name = Petróleo +liquid.cryofluid.name = Criogénico +mech.alpha-mech.name = Alpha +mech.alpha-mech.weapon = Repetidor Pesado +mech.alpha-mech.ability = Enjambre de Drones +mech.alpha-mech.description = El mecanoide estándar. Tiene velocidad y daño decentes, puede crear hasta 3 drones para poder ofensivo incremenado. +mech.delta-mech.name = Delta +mech.delta-mech.weapon = Generador de arco +mech.delta-mech.ability = Descarga +mech.delta-mech.description = Un mecanoide rápido y ligeramente armado para ataques de ataque y retirada. Hace poco daño a estructuras, pero puede eliminar rápidamente a grandes grupos de unidades con sus armas de arco eléctrico. +mech.tau-mech.name = Tau +mech.tau-mech.weapon = Láser de reestructuración +mech.tau-mech.ability = Repair Burst +mech.tau-mech.description = El mecanoide de soporte. Repara bloques aliados disparándolos. Puede extinguir el fuego y reparar aliados en un rango con su habilidad de reparación. +mech.omega-mech.name = Omega +mech.omega-mech.weapon = Enjambre de misiles +mech.omega-mech.ability = Armored Configuration +mech.omega-mech.description = Un mecanoide grande y bien armado, hecho para asaltos en primera línea. Su habilidad de armadura puede bloquear hasta el 90% del daño que recibe. +mech.dart-ship.name = Dardo +mech.dart-ship.weapon = Repetidor +mech.dart-ship.description = La nave normal. Bastante ligera y rápida, pero tiene poca capacidad ofensiva y baja velocidad minado. +mech.javelin-ship.name = Jabalina +mech.javelin-ship.description = Una nave de ataque y retirada. Aunque inicialmente lento, puede acelerar a altas velocidades y volar sobre puestos enemigos, causando gran daño con su habilidad de rayos y misiles. +mech.javelin-ship.weapon = Ráfaga de misiles +mech.javelin-ship.ability = Discharge Booster +mech.trident-ship.name = Tridente +mech.trident-ship.description = Un bombardero pesado. Razonablemente bien equipado. +mech.trident-ship.weapon = Bomb Bay +mech.glaive-ship.name = Glaive +mech.glaive-ship.description = Una nave pistolera grande y bien armada. Equipada con un repetidor incendiario. Buena aceleración y velocidad máxima. +mech.glaive-ship.weapon = Flame Repeater +item.explosiveness = [LIGHT_GRAY]Explosividad: {0} +item.flammability = [LIGHT_GRAY]Inflamabilidad: {0} +item.radioactivity = [LIGHT_GRAY]Radioactividad: {0} +unit.health = [LIGHT_GRAY]Vida: {0} +unit.speed = [LIGHT_GRAY]Velocidad: {0} +mech.weapon = [LIGHT_GRAY]Arma: {0} +mech.health = [LIGHT_GRAY]Health: {0} +mech.itemcapacity = [LIGHT_GRAY]Capacidad de objetos: {0} +mech.minespeed = [LIGHT_GRAY]Velocidad de minado: {0} +mech.minepower = [LIGHT_GRAY]Potencia de minado: {0} +mech.ability = [LIGHT_GRAY]Hablidad: {0} +mech.buildspeed = [LIGHT_GRAY]Building Speed: {0}% +liquid.heatcapacity = [LIGHT_GRAY]Capacidad Térmica: {0} +liquid.viscosity = [LIGHT_GRAY]Viscosidad: {0} +liquid.temperature = [LIGHT_GRAY]Temperatura: {0} +block.grass.name = Grass +block.salt.name = Salt +block.saltrocks.name = Salt Rocks +block.pebbles.name = Pebbles +block.tendrils.name = Tendrils +block.sandrocks.name = Sand Rocks +block.spore-pine.name = Spore Pine +block.sporerocks.name = Spore Rocks +block.rock.name = Rock +block.snowrock.name = Snow Rock +block.shale.name = Shale +block.shale-boulder.name = Shale Boulder +block.moss.name = Moss +block.shrubs.name = Shrubs +block.spore-moss.name = Spore Moss +block.shalerocks.name = Shale Rocks +block.scrap-wall.name = Scrap Wall +block.scrap-wall-large.name = Large Scrap Wall +block.scrap-wall-huge.name = Huge Scrap Wall +block.scrap-wall-gigantic.name = Gigantic Scrap Wall +block.thruster.name = Thruster +block.kiln.name = Kiln +block.kiln.description = Smelts sand and lead into metaglass. Requires small amounts of power. +block.graphite-press.name = Graphite Press +block.multi-press.name = Multi-Press +block.constructing = {0}\n[LIGHT_GRAY](Construyendo) +block.spawn.name = Punto de generación +block.core-shard.name = Core: Shard +block.core-foundation.name = Core: Foundation +block.core-nucleus.name = Core: Nucleus block.deepwater.name = Aguas profundas block.water.name = Agua -block.lava.name = Lava -block.oil.name = Aceite +block.tainted-water.name = Tainted Water +block.darksand-tainted-water.name = Dark Sand Tainted Water +block.tar.name = Alquitrán block.stone.name = Piedra -block.blackstone.name = Piedra Negra -block.iron.name = Hierro -block.coal.name = Carbón -block.titanium.name = Titanio -block.uranium.name = Uranio -block.dirt.name = Sucio block.sand.name = Arena +block.darksand.name = Dark Sand block.ice.name = Hielo block.snow.name = Nieve -block.grass.name = Césped -block.sandblock.name = Bloque de arena -block.snowblock.name = Bloque de nieve -block.stoneblock.name = Bloque de piedra -block.blackstoneblock.name = Bloque de piedra negra -block.grassblock.name = Bloque de césped -block.mossblock.name = Bloque de musgo -block.shrub.name = Arbusto -block.rock.name = Piedra -block.icerock.name = Piedra congelada -block.blackrock.name = Roca Negra -block.dirtblock.name = Bloque de suciedad -block.stonewall.name = Pared de piedra -block.stonewall.fulldescription = Un bloque defensivo barato. Útil para proteger el núcleo y las torrecillas en las primeras olas. -block.ironwall.name = Muro de hierro -block.ironwall.fulldescription = Un bloque defensivo básico. Proporciona protección de los enemigos. -block.steelwall.name = Muro de acero -block.steelwall.fulldescription = Un bloque defensivo estándar. protección adecuada de los enemigos. -block.titaniumwall.name = Muro de titanio -block.titaniumwall.fulldescription = Un fuerte bloqueo defensivo. Proporciona protección de los enemigos. -block.duriumwall.name = Muro de Dirio -block.duriumwall.fulldescription = Un bloque defensivo muy fuerte. Proporciona protección de los enemigos. -block.compositewall.name = Muro compuesto -block.steelwall-large.name = Pared de acero grande -block.steelwall-large.fulldescription = Un bloque defensivo estándar. Se extiende por múltiples mosaicos. -block.titaniumwall-large.name = Gran pared de titanio -block.titaniumwall-large.fulldescription = Un fuerte bloqueo defensivo. Se extiende por múltiples mosaicos. -block.duriumwall-large.name = Gran pared de dirio -block.duriumwall-large.fulldescription = Un bloque defensivo muy fuerte. Se extiende por múltiples mosaicos. -block.titaniumshieldwall.name = Muro blindado -block.titaniumshieldwall.fulldescription = Un fuerte bloque defensivo, con un escudo extra incorporado. Requiere poder Usa energía para absorber las balas enemigas. Se recomienda utilizar potenciadores de potencia para proporcionar energía a este bloque. -block.repairturret.name = Torreta de reparación -block.repairturret.fulldescription = Repara los bloques dañados cercanos en el rango a una velocidad lenta. Utiliza pequeñas cantidades de energía. -block.megarepairturret.name = Torreta de reparación II -block.megarepairturret.fulldescription = Repara los bloques dañados cercanos en el rango a una tasa decente. Utiliza el poder -block.shieldgenerator.name = Generador de escudo -block.shieldgenerator.fulldescription = Un bloque defensivo avanzado. Protege todos los bloques en un radio del ataque. Utiliza potencia a un ritmo lento cuando está inactivo, pero consume energía rápidamente al contacto con balas. +block.craters.name = Craters +block.sand-water.name = Sand water +block.darksand-water.name = Dark Sand Water +block.char.name = Char +block.holostone.name = Holo stone +block.ice-snow.name = Ice Snow +block.rocks.name = Rocks +block.icerocks.name = Ice rocks +block.snowrocks.name = Snow Rocks +block.dunerocks.name = Dune Rocks +block.pine.name = Pine +block.white-tree-dead.name = White Tree Dead +block.white-tree.name = White Tree +block.spore-cluster.name = Spore Cluster +block.metal-floor.name = Metal Floor +block.metal-floor-2.name = Metal Floor 2 +block.metal-floor-3.name = Metal Floor 3 +block.metal-floor-5.name = Metal Floor 5 +block.metal-floor-damaged.name = Metal Floor Damaged +block.dark-panel-1.name = Dark Panel 1 +block.dark-panel-2.name = Dark Panel 2 +block.dark-panel-3.name = Dark Panel 3 +block.dark-panel-4.name = Dark Panel 4 +block.dark-panel-5.name = Dark Panel 5 +block.dark-panel-6.name = Dark Panel 6 +block.dark-metal.name = Dark Metal +block.ignarock.name = Igna Rock +block.hotrock.name = Hot Rock +block.magmarock.name = Magma Rock +block.cliffs.name = Cliffs +block.copper-wall.name = Muro de cobre +block.copper-wall-large.name = Muro de cobre grande +block.titanium-wall.name = Titanium Wall +block.titanium-wall-large.name = Large Titanium Wall +block.phase-wall.name = Muro de Fase grande +block.phase-wall-large.name = Muro de Fase grande +block.thorium-wall.name = Pared de Torio +block.thorium-wall-large.name = Pared de Torio grande block.door.name = Puerta -block.door.fulldescription = Un bloque que se puede abrir y cerrar tocando. -block.door-large.name = Puerta grande -block.door-large.fulldescription = Un bloque que se puede abrir y cerrar tocando. -block.conduit.name = Conducto -block.conduit.fulldescription = Bloque de transporte de líquido básico. Funciona como un transportador, pero con líquidos. Se usa mejor con bombas u otros conductos. Se puede usar como puente sobre líquidos para enemigos y jugadores. -block.pulseconduit.name = Conducto de pulso -block.pulseconduit.fulldescription = Bloque de transporte de líquidos avanzado. Transporta líquidos más rápido y almacena más que los conductos estándar. -block.liquidrouter.name = Enrutador líquido -block.liquidrouter.fulldescription = Funciona de manera similar a un enrutador. Acepta entrada de líquido desde un lado y lo envía a los otros lados. Útil para dividir el líquido de un solo conducto en otros múltiples conductos. -block.conveyor.name = Transportador -block.conveyor.fulldescription = Bloque de transporte de elementos básicos. Mueve los artículos hacia adelante y los deposita automáticamente en torretas o crafters. Giratorio. Se puede usar como puente sobre líquidos para enemigos y jugadores. -block.steelconveyor.name = Transportador de acero -block.steelconveyor.fulldescription = Bloque de transporte de elementos avanzados. Mueve los artículos más rápido que los transportadores estándar. -block.poweredconveyor.name = Transportador de pulso -block.poweredconveyor.fulldescription = El último bloque de transporte de elementos. Mueve los artículos más rápido que los transportadores de acero. +block.door-large.name = Puerta Larga +block.duo.name = Dúo +block.scorch.name = Scorch +block.scatter.name = Scatter +block.hail.name = Granizo +block.lancer.name = Lancero +block.conveyor.name = Cinta Transportadora +block.titanium-conveyor.name = Cinta Transportadora de Titanio +block.junction.name = Cruce block.router.name = Enrutador -block.router.fulldescription = Acepta elementos de una dirección y los envía a otras 3 direcciones. También puede almacenar una cierta cantidad de artículos. Útil para dividir los materiales de un taladro en múltiples torretas. -block.junction.name = Union -block.junction.fulldescription = Actúa como un puente para cruzar dos cintas transportadoras. Útil en situaciones con dos transportadores diferentes que llevan diferentes materiales a diferentes ubicaciones. -block.conveyortunnel.name = Túnel transportador -block.conveyortunnel.fulldescription = Transporta el artículo debajo de bloques. Para usarlo, coloque un túnel que conduce al bloque por el que se colocará el túnel, y otro del otro lado. Asegúrate de que ambos túneles estén orientados en direcciones opuestas, que se dirigen hacia los bloques a los que están ingresando o enviando. -block.liquidjunction.name = Unión líquida -block.liquidjunction.fulldescription = Actúa como un puente para dos conductos que cruzan. Útil en situaciones con dos conductos diferentes que llevan diferentes líquidos a diferentes lugares. -block.liquiditemjunction.name = Unión líquidos-elementos -block.liquiditemjunction.fulldescription = Actúa como un puente para cruzar conductos y transportadores. -block.powerbooster.name = Ampliación de potencia -block.powerbooster.fulldescription = Distribuye energía a todos los bloques dentro de su radio. -block.powerlaser.name = Láser de potencia -block.powerlaser.fulldescription = Crea un láser que transmite potencia al bloque que está delante de él. No genera ningún poder en sí mismo. Se usa mejor con generadores u otros láseres. -block.powerlaserrouter.name = Enrutador láser -block.powerlaserrouter.fulldescription = Láser que distribuye la potencia en tres direcciones a la vez. Útil en situaciones donde se requiere para alimentar múltiples bloques de un generador. -block.powerlasercorner.name = Laser esquinero -block.powerlasercorner.fulldescription = Láser que distribuye la potencia en dos direcciones a la vez. Útil en situaciones donde se requiere alimentar múltiples bloques desde un generador, y un enrutador es impreciso. -block.teleporter.name = Teletransportador -block.teleporter.fulldescription = Bloque de transporte de elementos avanzados. Los teleportadores ingresan elementos a otros teletransportadores del mismo color. No hace nada si no existen teletransportadores del mismo color. Si existen múltiples teleportadores del mismo color, se selecciona uno aleatorio. Utiliza el poder Toca para cambiar el color. +block.distributor.name = Distribuidor block.sorter.name = Clasificador -block.sorter.fulldescription = Ordena el artículo por tipo de material. El material a aceptar se indica por el color en el bloque. Todos los elementos que coinciden con el material de clasificación se envían hacia adelante, todo lo demás se envía a la izquierda y a la derecha. -block.core.name = Nùcleo -block.pump.name = Bomba -block.pump.fulldescription = Bombea líquidos de un bloque fuente, generalmente agua, lava o aceite. Emite líquido en los conductos cercanos. -block.fluxpump.name = Bomba de flujo -block.fluxpump.fulldescription = Una versión avanzada de la bomba. Almacena más líquido y bombea líquido más rápido. -block.smelter.name = horno de fundición -block.smelter.fulldescription = El bloque de elaboración esencial. Cuando se ingresa 1 hierro y 1 carbón como combustible, se emite un acero. Se aconseja ingresar hierro y carbón en diferentes bandas para evitar obstrucciones. -block.crucible.name = Crisol -block.crucible.fulldescription = Un bloque de elaboración avanzada. Cuando se ingresa 1 titanio, 1 acero y 1 carbón como combustible, se emite un dirium. Se aconseja ingresar carbón, acero y titanio en diferentes bandas para evitar obstrucciones. -block.coalpurifier.name = Extractor de carbón -block.coalpurifier.fulldescription = Un bloque extractor básico. Salidas de carbón cuando se suministra con grandes cantidades de agua y piedra. -block.titaniumpurifier.name = extractor de titanio -block.titaniumpurifier.fulldescription = Un bloque extractor estándar. Salidas de titanio cuando se suministra con grandes cantidades de agua y hierro. -block.oilrefinery.name = Refinería de petróleo -block.oilrefinery.fulldescription = Refina grandes cantidades de aceite en artículos de carbón. Útil para alimentar torretas a base de carbón cuando las vetas de carbón son escasas. -block.stoneformer.name = Piedra antigua -block.stoneformer.fulldescription = Se vende lava líquida en piedra. Útil para producir cantidades masivas de piedra para purificadores de carbón. -block.lavasmelter.name = Fundición de lava -block.lavasmelter.fulldescription = Utiliza lava para convertir el hierro en acero. Una alternativa a las fundiciones. Útil en situaciones donde el carbón es escaso. -block.stonedrill.name = Perforadora de piedra -block.stonedrill.fulldescription = El taladro esencial. Cuando se coloca en baldosas de piedra, las impresiones de piedra a un ritmo lento de forma indefinida. -block.irondrill.name = Perforadora de hierro -block.irondrill.fulldescription = Un ejercicio básico. Cuando se coloca sobre baldosas de mineral de hierro, emite hierro a un ritmo lento indefinidamente. -block.coaldrill.name = Perforadora de carbón -block.coaldrill.fulldescription = Un ejercicio básico. Cuando se coloca en las baldosas de mineral de carbón, emite carbón a un ritmo lento indefinidamente. -block.uraniumdrill.name = Taladro de uranio -block.uraniumdrill.fulldescription = Un taladro avanzado. Cuando se coloca en placas de mineral de uranio, emite uranio a un ritmo lento de forma indefinida. -block.titaniumdrill.name = Taladro de titanio -block.titaniumdrill.fulldescription = Un taladro avanzado. Cuando se coloca en baldosas de mineral de titanio, emite titanio a un ritmo lento indefinidamente. -block.omnidrill.name = omnidrill -block.omnidrill.fulldescription = El último ejercicio Va a extraer cualquier mineral que se coloca a un ritmo rápido. -block.coalgenerator.name = Generador de carbón -block.coalgenerator.fulldescription = El generador esencial. Genera energía del carbón Emite energía como láser en sus 4 lados. -block.thermalgenerator.name = Generador térmico -block.thermalgenerator.fulldescription = Genera energía de la lava Emite energía como láser en sus 4 lados. -block.combustiongenerator.name = Generador de combustión -block.combustiongenerator.fulldescription = Genera energía del petróleo. Emite energía como láser en sus 4 lados. -block.rtgenerator.name = Generador de RTG -block.rtgenerator.fulldescription = Genera pequeñas cantidades de energía a partir de la desintegración radiactiva del uranio. Emite energía como láser en sus 4 lados. -block.nuclearreactor.name = Reactor nuclear -block.nuclearreactor.fulldescription = Una versión avanzada del generador RTG y el último generador de energía. Genera energía del uranio. Requiere enfriamiento constante de agua. Altamente volátil; explotará violentamente si se suministran cantidades insuficientes de refrigerante. -block.turret.name = Torre -block.turret.fulldescription = Una torrecilla básica y barata. Utiliza piedra para munición. Tiene un poco más de alcance que la torre doble. -block.doubleturret.name = Doble torreta -block.doubleturret.fulldescription = Una versión un poco más poderosa de la torreta. Utiliza piedra para munición. Hace mucho más daño, pero tiene un rango menor. Dispara dos balas. -block.machineturret.name = Torreta de gatling -block.machineturret.fulldescription = Una torreta versátil estándar. Utiliza hierro para munición. Tiene una velocidad de disparo rápida con un daño decente. -block.shotgunturret.name = Torreta divisoria -block.shotgunturret.fulldescription = Una torreta estándar. Utiliza hierro para munición. Dispara una extensión de 7 balas. Alcance más bajo, pero mayor producción de daños que la torreta de gatling. -block.flameturret.name = Torreta de fuego -block.flameturret.fulldescription = Torreta de corto alcance avanzada. Utiliza carbón para munición. Tiene un rango muy bajo, pero un daño muy alto. Bueno para cuartos cercanos. Recomendado para ser utilizado detrás de las paredes. -block.sniperturret.name = Torreta railgun -block.sniperturret.fulldescription = Torreta de largo alcance avanzada. Utiliza acero para munición. Daño muy alto, pero bajo índice de fuego. Es caro de usar, pero puede colocarse lejos de las líneas enemigas debido a su alcance. -block.mortarturret.name = Torreta antiaérea -block.mortarturret.fulldescription = Torreta avanzada de baja salpicadura de daños por salpicadura. Utiliza carbón para munición. Dispara un aluvión de balas que explotan en metralla. Útil para grandes multitudes de enemigos. -block.laserturret.name = Torreta láser -block.laserturret.fulldescription = Torreta de un solo objetivo avanzado. Utiliza el energia. Buena torre de medio alcance. Objetivo único. Nunca falla -block.waveturret.name = Torreta tesla -block.waveturret.fulldescription = Torreta multi-objetivo avanzada. Utiliza el poder Rango medio. Nunca falla. De Medio a bajo daño, pero puede golpear a varios enemigos simultáneamente con rayos en cadena. -block.plasmaturret.name = Torreta de plasma -block.plasmaturret.fulldescription = Versión altamente avanzada de la torreta de fuego. Utiliza carbón como munición. Daño muy alto, rango bajo a medio. -block.chainturret.name = Torreta de cadena -block.chainturret.fulldescription = La torreta de fuego rápido suprema. Usa uranio como munición. Dispara babosas grandes a una alta cadencia. Rango medio. Se extiende por múltiples bloques. Extremadamente duradero. -block.titancannon.name = Cañón titán -block.titancannon.fulldescription = La torreta de largo alcance suprema. Usa uranio como munición. Dispara grandes proyectiles con daño de área a una cadencia media. De largo alcance. Se extiende por múltiples bloques. Extremadamente duradero. -block.playerspawn.name = Punto de aparición del jugador -block.enemyspawn.name = Generador de enemigos +block.sorter.description = Clasifica objetos. Si un objeto es igual al seleccionado, pasará al frente. Si no, el objeto saldrá por la izquierda y la derecha. +block.overflow-gate.name = Compuerta de Desborde +block.overflow-gate.description = Un enrutador que solo saca por la izquierda y la derecha si la cinta del frente está llena. +block.silicon-smelter.name = Horno para Silicona +block.phase-weaver.name = Tejedor de Fase +block.pulverizer.name = Pulverizador +block.cryofluidmixer.name = Mezclador de Criogénicos +block.melter.name = Fundidor +block.incinerator.name = Incinerador +block.spore-press.name = Spore Press +block.separator.name = Separador +block.coal-centrifuge.name = Coal Centrifuge +block.power-node.name = Nodo de Energía +block.power-node-large.name = Nodo de Energía Grande +block.surge-tower.name = Surge Tower +block.battery.name = Batería +block.battery-large.name = Batería Grande +block.combustion-generator.name = Generador de Combustión +block.turbine-generator.name = Turbina +block.differential-generator.name = Differential Generator +block.impact-reactor.name = Impact Reactor +block.mechanical-drill.name = Taladro mecánico +block.pneumatic-drill.name = Taladro neumático +block.laser-drill.name = Taladro Laser +block.water-extractor.name = Extractor de Agua +block.cultivator.name = Cultivador +block.dart-mech-pad.name = Dart Mech Pad +block.delta-mech-pad.name = Pad de mecanoide Delta +block.javelin-ship-pad.name = Pad de nave Jabalina +block.trident-ship-pad.name = Pad de nave Tridente +block.glaive-ship-pad.name = Pad de nave Glaive +block.omega-mech-pad.name = Pad de mecanoide Omega +block.tau-mech-pad.name = Pad de mecanoide Tau +block.conduit.name = Conducto +block.mechanical-pump.name = Bomba Mecánica +block.item-source.name = Fuente de objetos +block.item-void.name = Vacío de objetos +block.liquid-source.name = Fuente de líquidos +block.power-void.name = Vacío de energía +block.power-source.name = Energía Infinita +block.unloader.name = Descargador +block.vault.name = Bóveda +block.wave.name = Horda +block.swarmer.name = Enjambredor +block.salvo.name = Salva +block.ripple.name = Onda +block.phase-conveyor.name = Cinta Transportadora de Fase +block.bridge-conveyor.name = Cinta Transportadora Puente +block.plastanium-compressor.name = Compresor de Plastanio +block.pyratite-mixer.name = Mezclador de Pirotita +block.blast-mixer.name = Mezclador de Explosivos +block.solar-panel.name = Panel Solar +block.solar-panel-large.name = Panel Solar Grande +block.oil-extractor.name = Extractor de Petróleo +block.spirit-factory.name = Fábrica de Drones Espíritu +block.phantom-factory.name = Fábrica de Drones Fantasmales +block.wraith-factory.name = Fábrica de Wraith Fighter +block.ghoul-factory.name = Fábrica de Ghoul Bomber +block.dagger-factory.name = Fábrica de Dagas +block.crawler-factory.name = Crawler Mech Factory +block.titan-factory.name = Fábrica de Titanes +block.fortress-factory.name = Fábrica de mecanoide Fortress +block.revenant-factory.name = Fábrica de Revenant Fighter +block.repair-point.name = Punto de Reparación +block.pulse-conduit.name = Conducto de Pulso +block.phase-conduit.name = Conducto de Fase +block.liquid-router.name = Enrutador de Líquidos +block.liquid-tank.name = Tanque de Líquidos +block.liquid-junction.name = Cruce de Líquidos +block.bridge-conduit.name = Conducto Puente +block.rotary-pump.name = Bomba Rotatoria +block.thorium-reactor.name = Reactor de Torio +block.mass-driver.name = Teletransportador de Masa +block.blast-drill.name = Taladro de explosión +block.thermal-pump.name = Bomba Térmica +block.thermal-generator.name = Generador Térmico +block.alloy-smelter.name = Alloy Smelter +block.mender.name = Mender +block.mend-projector.name = Proyector de reparación +block.surge-wall.name = Surge Wall +block.surge-wall-large.name = Large Surge Wall +block.cyclone.name = Ciclón +block.fuse.name = Fuse +block.shock-mine.name = Shock Mine +block.overdrive-projector.name = Proyector de sobremarcha +block.force-projector.name = Proyector de fuerza +block.arc.name = Arc +block.rtg-generator.name = Generador RTG +block.spectre.name = Espectro +block.meltdown.name = Meltdown +block.container.name = Contenedor +block.launch-pad.name = Launch Pad +block.launch-pad.description = Launches batches of items without any need for a core launch. Unfinished. +block.launch-pad-large.name = Large Launch Pad +team.blue.name = Azul +team.red.name = Rojo +team.orange.name = Naranja +team.none.name = Gris +team.green.name = Verde +team.purple.name = Púrpura +unit.spirit.name = Dron Espíritu +unit.spirit.description = El dron del comienzo. Aparece en el núcleo por defecto. Mina automáticamente minerales, recoge objetos y repara bloques. +unit.phantom.name = Dron Fantasmal +unit.phantom.description = Un dron avanzado. Mina automáticamente minerales, recoge objetos y repra bloques. Bastante más efectivo que un dron normal. +unit.dagger.name = Daga +unit.dagger.description = Una unidad de terreno. Útil con enjambres. +unit.crawler.name = Crawler +unit.titan.name = Titán +unit.titan.description = Una unidad blindada de terreno, avanzada. Ataca blancos de aire y de terreno. +unit.ghoul.name = Ghoul Bomber +unit.ghoul.description = Una unidad bombardera pesada. Usa compuesto explosivo o pirotita como munición. +unit.wraith.name = Wraith Fighter +unit.wraith.description = Una unidad interceptora rápida. +unit.fortress.name = Fortress +unit.fortress.description = Una unidad terrestre pesada de artillería. +unit.revenant.name = Revenant +unit.eruptor.name = Eruptor +unit.chaos-array.name = Chaos Array +unit.eradicator.name = Eradicator +unit.lich.name = Lich +unit.reaper.name = Reaper +tutorial.begin = Tu objetivo aquí es erradicar el[LIGHT_GRAY] enemy[].\n\nComienza[accent]minando copper[]. Toca una veta de cobre cerca de tu núcleo para hacer esto. +tutorial.drill = Minar manualmente es ineficiente.\nLos [accent]taladros pueden minar automáticamente.\nColoca uno en una veta de cobre. +tutorial.conveyor = Los [accent]Conveyors[] se usan para transportar objetos al núcleo.\nConstruye una línea de transportadores del taladro al núcleo. +tutorial.morecopper = Se requiere más cobre.\n\nMínalo manualmente o coloca más taladros. +tutorial.turret = Se tiene que construir estructuras defensivas para repeler el [LIGHT_GRAY]enemy[].\nConstruye una torreta dúo cerca de tu base. +tutorial.drillturret = Los dúos requieren[accent] copper ammo[]para disparar.\nColoca un taladro junto a la torre para darle cobre. +tutorial.waves = El[LIGHT_GRAY] enemy[] se acerca.\n\nDefiende tu núcleo 2 hordas. Construye más torretas. +tutorial.lead = Hay más minerales disponibles. Explora y mna[accent] lead[].\n\n Desliza de tu unidad al núcleo para transferir recursos. +tutorial.smelter = El cobre y el plomo son metales débiles. Una[accent] Dense Alloy[] superior puede ser creada en una fundición.\n\nConstruye una. +tutorial.densealloy = La fundición ahora producirá la aleación.\nObtén algunas.\nMejora la producción si es necesario. +tutorial.siliconsmelter = El núcleo creará ahora un[accent] spirit drone[] para minar y reparar bloques.\n\nHay fábricas que crean otras unidades con[accent] silicona.\nCrea una fundición de silicona. +tutorial.silicondrill = La silicona requiere[accent] coal[] y[accent]sand[].\nEmpieza haciendo taladros. +tutorial.generator = Esta tecnología requiere energía.\nCrea un[accent] combustion generator[] para generarla. +tutorial.generatordrill = Los generadores de combustión requieren combustible.\nProporciónalo carbón de un taladro. +tutorial.node = La energía requiere ser transportada.\nCrea un[accent] power node[] junto al generador de combustión para transferir su energía. +tutorial.nodelink = La energía puede ser transferida mediante colocando bloques de energía y generadores juntos, o por nodos conectados.\n\nConecta energía tocando el nodo y seleccionando el generador y la fundición de silicona. +tutorial.silicon = La silicona está siendo producida. Obtén algo de silicona.\n\nEs recomendado mejorar la producción. +tutorial.daggerfactory = Construye una[accent] dagger mech factory[].\n\nEsto se usará para crear unidades terrestres de ataque. +tutorial.router = Las fábricas necesitan recursos para funcionar.\nCrea un enrutador para separar recursos del transportador. +tutorial.dagger = Conecta nodos de energía a la fábrica.\nUna vez las necesidades se cumplan, una unidad será creada.\n\nCrea taladros, generadores y transportadores según necesites. +tutorial.battle = El[LIGHT_GRAY] enemy[] ha revelado su núcleo.\nDestrúyelo con tu nave y tus unidades de combate. +block.copper-wall.description = Un bloque defensivo barato.\nÚtil para defneder e núcleo y las torres en las primeras hordas. +block.copper-wall-large.description = Un bloque defensivo barato.\nÚtil para defneder e núcleo y las torres en las primeras hordas.\nOcupa múltiples casillas. +block.thorium-wall.description = Un bloque defensivo fuerte.\nBuena protección contra enemigos. +block.thorium-wall-large.description = Un bloque defensivo fuerte.\nBuena protección contra enemigos.\nOcupa múltiples casillas. +block.phase-wall.description = No es tan fuerte como un muro de torio pero rebota balas al enemigo si no son demasiado fuertes. +block.phase-wall-large.description = No es tan fuerte como un muro de torio pero rebota balas al enemigo si no son demasiado fuertes.\nOcupa múltiples casillas. +block.surge-wall.description = El bloque defensivo más fuerte.\nTiene una pequeña probabilidad de disparar rayos al atacante. +block.surge-wall-large.description = El bloque defensivo más fuerte.\nTiene una pequeña probabilidad de disparar rayos al atacante.\nOcupa múltiplies casillas. +block.door.description = Una puerta pequeña que puede ser abierta y cerrada tocándola.\nSi está abirta, los enemigos pueden moverse y disparar a través de ella.\nOcupa múltiples casillas. +block.door-large.description = Una puerta grande que puede ser abierta y cerrada tocándola.\nSi está abirta, los enemigos pueden moverse y disparar a través de ella.\nOcupa múltiples casillas. +block.mend-projector.description = Regenera edificios cercanos periódcamente. +block.overdrive-projector.description = Aumenta la velocidad de edificios cercanos como taladros y transportadores. +block.force-projector.description = Crea un área de fuerza hexagonal alrededor de él, protegiendo edificios y unidades dentro de él del daño de las balas. +block.shock-mine.description = Daña enemigos que pisan a mina. Casi invisible al enemigo. +block.duo.description = Una torre pequeña y barata. +block.scatter.description = A medium-sized anti-air turret. Sprays clumps of lead or scrap flak at enemy units. +block.arc.description = Una torre pequeña que disapra electricidad en un arco aleatorio al enemigo. +block.hail.description = Una torre de artillería pequeña. +block.lancer.description = Una torre de tamaño mediano que dispara rayos cargados eléctricamente. +block.wave.description = Una torre de tamaño mediano que dispara burbujas de líquido. +block.salvo.description = Una torre de tramaño mediano que dispara balas en salvos. +block.swarmer.description = Una torre de tamaño mediano que dispara misiles en grupo. +block.ripple.description = Una torre de artillería grande que dispara varios disparos simultáneamente. +block.cyclone.description = Una torre de disparo rápido grande. +block.fuse.description = Una torre grande que dispara rayos poderosos de corto alcance. +block.spectre.description = Una torre grande que dispara dos balas poderosas de una vez. +block.meltdown.description = Una torre grande que dispara rayos poderosos de largo alcance. +block.conveyor.description = Bloque de transporte básico. Mueve objetos hacia adelante y los deposita automáticamente en torres o fábricas. Rotable. +block.titanium-conveyor.description = Bloque de transporte avanzado. Mueve objetos más rápido que los transportadores estándar. +block.phase-conveyor.description = Bloque de transporte avanzado. Usa energía para transportar objetos a otro transportador de fase conectado por varias casillas. +block.junction.description = Actúa como puente para dos transportadores que se cruzan. Útil en situaciones con dos diferentes transportadores transportando diferentes materiales a diferentes lugares. +block.mass-driver.description = El mejor bloque de transorte. Recoge varios objetos y los dispara a otro conductor de masa en un largo rango. +block.silicon-smelter.description = Reduce arena con coque de alta pureza para producir silicona. +block.plastanium-compressor.description = Produce plastanio con aceite y titanio. +block.phase-weaver.description = Produces phase fabric from radioactive thorium and high amounts of sand. +block.alloy-smelter.description = Produce "surge alloy" con titanio, plomo, silicona y cobre. +block.pulverizer.description = Despedaza la piedra en arena. Útil cuando no hay arena natural. +block.pyratite-mixer.description = Mezcla carbón, plomo y arena en pirotita altamente inflamable. +block.blast-mixer.description = Usa aceite para transformar pirotita en un objeto menos inflamable pero más explosivo: compuesto explosivo. +block.cryofluidmixer.description = Combina agua y titanio en líquido criogénico que es mucho más eficiente para enfriar. +block.melter.description = Calienta piedra a temperaturas muy altas para obtener lava. +block.incinerator.description = Se deshace de cualquier líquido u objeto excesivo. +block.spore-press.description = Compresses spore pods into oil. +block.separator.description = Expone piedra a la presión del agua para obtener diversos minerales contenidos en la piedra. +block.power-node.description = Transmite energía a nodos conectados, conecta hasta cuatro fuentes de energía, edificios que usan energía o nodos. El nodo obtendrá o transmitirá energía de cualquier bloque adyacente. +block.power-node-large.description = Tiene un radio más amplio que el nodo de energía y conecta hasta seis fuentes de energía, edificios que usan energía o nodos. +block.battery.description = Guarda energía cuando hay abundancia y proporciona energía cuando hay escasez de energía mientras la batería tenga energía. +block.battery-large.description = Almacena mucha más energía que una batería normal. +block.combustion-generator.description = Genera energía quemando aceite o matteriales inflamables. +block.turbine-generator.description = Más eficiente que un generador de combustión, pero requiere agua adicional. +block.thermal-generator.description = Genera una gran cantidad de energía con la lava. +block.solar-panel.description = Proporciona una pequeña cantidad de energía procedente del sol. +block.solar-panel-large.description = Genera un mucho mejor suministro de energía que un panel solar estándar, pero también es mucho más caro de construir. +block.thorium-reactor.description = Genera grandes cantidades de energía del torio altamente radioactivo. Necesita enfriamiento constante. Explotará violentamente si no se le aporta suficiente enfriamiento. +block.rtg-generator.description = Un generador radioisótropo termoeléctrico que no necesita enfriamiento pero proporciona menos energía que un reactor de torio. +block.unloader.description = Descarga objetos de un contenedor, almacén o el núcleo a un transportador o directamente a un bloque adyacente. El tipo de objeto descargado puede ser cambiado tocando el descagador. +block.container.description = Almacena una pequeña cantidad de objetos. Úsalo para crear almacenes cuando no hay una demanda constante de materales. Un [LIGHT_GRAY] unloader[] puede usarse para obtener objetos del contenedor. +block.vault.description = Almacena una gran cantidad de objetos. Úsalo para crear almacenes cuando no hay una demanda constante de materales. Un [LIGHT_GRAY] unloader[] puede usarse para obtener objetos del almacén. +block.mechanical-drill.description = Un taladro barato. Cuando es colocado en casillas apropiadas, extrae objetos lentamente de forma indefinida. +block.pneumatic-drill.description = Un taladro mejorado que es más rápido y puede obtener minerales más duros usando la presión. +block.laser-drill.description = Permite obtener minerales incluso más rápido con la tecnología láser, pero requiere energía. Además, se puede obtener torio radioactivo con este taladro. +block.blast-drill.description = El mejor taladro. Requiere grandes cantidades de energía. +block.water-extractor.description = Extrae agua de la tierra. Úsalo cuando no haya lagos cercanos. +block.cultivator.description = Cultiva la tierra para obtener biomateria. +block.oil-extractor.description = Usa grandes cantidades de energía para extraer aceite de la arena. Úsalo cuando no hay fuentes directas de aceite cerca. +block.trident-ship-pad.description = Deja tu nave actual y transfórmate en una unidad aérea bombardera pesada.\nUsa el pad tocándolo dos veces mientras estás en él. +block.javelin-ship-pad.description = Deja tu nave actual y transfórmate en una unidad aérea fuerte y rápida interceptora con arma eléctrica.\nUsa el pad tocándolo dos veces mientras estás en él. +block.glaive-ship-pad.description = Deja tu nave actual y transfórmate en una unidad aérea grande y bien armada nave pistolera.\nUsa el pad tocándolo dos veces mientras estás en él. +block.tau-mech-pad.description = Deja tu nave actual y transfórmate en un mecanoide de soporte que puede reparar construcciones y tropas aliadas.\nUsa el pad tocándolo dos veces mientras estás en él. +block.delta-mech-pad.description = Deja tu nave actual y transfórmate en un mecanoide rápido y ligero hecho para ataques de emboscada y retirada.\nUsa el pad tocándolo dos veces mientras estás en él. +block.omega-mech-pad.description = Deja tu nave actual y transfórmate en un mecanoide pesado y bien armado, hecho para asaltos en primera línea.\nUsa el pad tocándolo dos veces mientras estás en él. +block.spirit-factory.description = Produce drones ligeros que obtienen minerales y reparan bloques. +block.phantom-factory.description = Produce drones avanzados que son significativamente más eficientes que un dron espíritu. +block.wraith-factory.description = Produce unidades aéreas rápidas e interceptoras. +block.ghoul-factory.description = Produce unidades bombarderas pesadas. +block.dagger-factory.description = Produce unidades terrestres básicas. +block.titan-factory.description = Produce unidades terrestres avanzadas. +block.fortress-factory.description = Produce unidades terrestres de artillería pesada. +block.revenant-factory.description = Produce unidades terrestres láser pesadas. +block.repair-point.description = Repara la unidad dañada más cercana a su alrededor. +block.conduit.description = Bloque de transporte de líquidos básico. Funciona como un transportador, pero con líquidos. Usado con bombas, extractores u otros conductos. +block.pulse-conduit.description = Bloque de transporte de líquidos avanzado. Transporta líquidos más rápidamente y almacena más que los conductos estándar. +block.phase-conduit.description = Bloque de transporte de líquidos avanzado. Usa energía para transportar líquidos a otro conducto de fase conectado por varias casillas. +block.liquid-router.description = Acepta líquidos de una dirección y los deja en hasta 3 direcciones equitativamente. También puede amacenar cierta capacidad de líquido. Útil para dividir los líquidos de una fuente a varios objetivos. +block.liquid-tank.description = Almacena una gran cantidad de líquidos. Úsalo para crear almacenes cuando no hay una demanda constante de materiales o para asegurarse de enfriar bloques vitales. +block.liquid-junction.description = Actúa como un puente para dos condusctos que se cruzan. Útil en situaciones en las que hay dos conductos con líquidos diferentes a diferentes lugares. +block.bridge-conduit.description = Bloque avanzado de transporte de líquidos. Permite transportar líquidos por encima hasta 3 casillas de cualquier terreno o construcción. +block.mechanical-pump.description = Una bomba barata con extracción lenta, pero sin uso de energía. +block.rotary-pump.description = Una bomba avanzada que duplica la velocidad usando energía. +block.thermal-pump.description = La mejor bomba. Tres veces más rápido que la bomba mecánica, y la única bomba que puede extraer lava. +block.router.description = Acepta objetos de una dirección y deja objetos equitativamente en hasta 3 direcciones diferentes. Útil para dividir los materiales de una fuente de recursos a múltiples objetivos. +block.distributor.description = Un enrutador avanzado que distribuye objetos equitativamente en hasta otras 7 direcciones. +block.bridge-conveyor.description = Bloque avanado de transporte. Puede transportar objetos por encima hasta 3 casillas de cualquier terreno o construcción. +block.item-source.description = Da objetos infinitos. Solo en sandbox. +block.liquid-source.description = Da líquido infinito. Solo en sandbox. +block.item-void.description = Destruye cuanquier objeto que va a él sin necesitar energía. Solo en sandbox. +block.power-source.description = Da energía infinita. Solo en sandbox. +block.power-void.description = Elimina toda la energía que se le da. Solo en sandbox. +liquid.water.description = Usado comúnmente para enfriar máquinas y para procesar residuos. +liquid.oil.description = Puede ser quemado, explotado o como un enfriador. +liquid.cryofluid.description = El líquido más eficiente pra enfriar las cosas. diff --git a/core/assets/bundles/bundle_fr.properties b/core/assets/bundles/bundle_fr.properties index 6c8a007520..d62de4ef6e 100644 --- a/core/assets/bundles/bundle_fr.properties +++ b/core/assets/bundles/bundle_fr.properties @@ -1,528 +1,947 @@ -text.about = Créé par [ROYAL]Anuken.[]\nA l'origine une entrée dans le [orange]GDL[] MM Jam.\n\nCrédits: \n- SFX réalisé avec [yellow]bfxr[] \n- Musique faite par [lime]RoccoW[] / trouvé sur [lime]FreeMusicArchive.org[] \n\nRemerciements particuliers à:\n- [coral]MitchellFJN[]: nombreux tests et retours d'expérience \n- [sky]Luxray5474[]: travail wiki, contributions de code \n- [lime]Epowerj[]: système de compilation de code, icône \n- Tous les beta testeurs sont sur itch.io et Google Play\n -text.discord = Rejoignez le discord de Mindustry -text.changes = [SCARLET]Attention![] \nCertains mécanismes importants du jeu ont ete modifies.\n-Les [accent]telporteurs[] utilisent maintenant de l'énergie.\n-Les [accent]fonderies[] et les[accent]crucibles[] ont maintenant une capacité maximale d'articles.\n-Les [accent]crucibles[] exigent maintenant du charbon comme combustible. -text.gameover = Le noyau a été détruit. -text.highscore = [YELLOW]Nouveau meilleur score! -text.lasted = Vous avez duré jusqu'à la vague -text.level.highscore = Meilleur score: [accent]{0} -text.level.delete.title = Confirmer -text.level.delete = Êtes-vous sûr de vouloir supprimer la carte \"[orange] \"? -text.level.select = Sélection de niveau -text.level.mode = Mode de jeu : -text.savegame = Sauvegarder la partie -text.loadgame = Charger la partie -text.joingame = Rejoindre la partie -text.quit = Quitter -text.about.button = À propos -text.name = Nom : -text.public = Publique -text.players = joueurs en ligne -text.server.player.host = Héberger -text.players.single = joueur en ligne -text.server.mismatch = Erreur de paquet: possible incompatibilité de version client/serveur. Assurez-vous que vous et l'hôte avez la dernière version de Mindustry! -text.server.closing = [accent]Fermeture du serveur ... -text.server.kicked.kick = Vous avez été expulsé du serveur! -text.server.kicked.invalidPassword = Mot de passe non valide ! -text.server.kicked.clientOutdated = Client dépassé! Mettez à jour votre jeu! -text.server.kicked.serverOutdated = Serveur dépassé! Demandez à l'hôte de le mettre à jour! -text.server.kicked.banned = Vous êtes banni sur ce serveur. -text.server.connected = a rejoint le serveur -text.server.disconnected = {0} s'est déconnecté. -text.nohost = Impossible d'héberger le serveur sur une carte personnalisée! -text.hostserver = Héberger un serveur -text.host = Héberger -text.hosting = [accent]Ouverture du serveur ... -text.hosts.refresh = Actualiser -text.hosts.discovering = Recherche de parties LAN -text.server.refreshing = Actualisation du serveur -text.hosts.none = [lightgray]Aucun jeu LAN trouvé! -text.host.invalid = [scarlet]Impossible de se connecter à l'hôte. -text.server.friendlyfire = Tir allié -text.trace = suivre le joueur -text.trace.playername = Nom du joueur: [accent] {0} -text.trace.ip = IP: [accent] {0} -text.trace.id = ID unique: [accent] {0} -text.trace.android = Client Android: [accent] {0} -text.trace.modclient = Client personnalisé: [accent] {0} -text.trace.totalblocksbroken = Total des blocs détruits: [accent] {0} -text.trace.structureblocksbroken = Blocs de structure détruits: [accent] {0} -text.trace.lastblockbroken = Dernier bloc détruit: [accent] {0} -text.trace.totalblocksplaced = Nombre total de blocs placés: [accent] {0} -text.trace.lastblockplaced = Dernier bloc placé: [accent] {0} -text.invalidid = ID client invalide! Soumettre un rapport de bug -text.server.bans = Interdictions -text.server.bans.none = Aucun joueur banni trouvé! -text.server.admins = Administrateurs -text.server.admins.none = Aucun administrateur trouvé! -text.server.add = Ajouter un serveur -text.server.delete = Êtes-vous sûr de vouloir supprimer ce serveur? -text.server.hostname = Héberger -text.server.edit = éditer le serveur -text.server.outdated = [crimson]Serveur obsolète![] -text.server.outdated.client = [Crimson]Client obsolète![] -text.server.version = [lightgray]Version: {0} -text.server.custombuild = [jaune]Construction personnalisée -text.confirmban = Êtes-vous sûr de vouloir bannir ce joueur? -text.confirmunban = Êtes-vous sûr de vouloir annuler le ban de ce joueur? -text.confirmadmin = Êtes-vous sûr de vouloir faire de ce joueur un administrateur? -text.confirmunadmin = Êtes-vous sûr de vouloir supprimer le statut d'administrateur de ce joueur? -text.joingame.byip = Rejoindre par IP ... -text.joingame.title = Rejoindre une partie -text.joingame.ip = IP : -text.disconnect = Déconnecté -text.connecting = [accent]Connexion ... -text.connecting.data = [accent] Chargement des données de la partie ... -text.connectfail = [crimson] Échec de la connexion au serveur : [orange] -text.server.port = Port : -text.server.addressinuse = Adresse déjà utilisée! -text.server.invalidport = Numéro de port incorrect. -text.server.error = [crimson]Erreur lors de l'hébergement du serveur: [orange] {0} -text.tutorial.back = < Précédent\n -text.tutorial.next = Suivant> -text.save.new = Nouvelle sauvegarde -text.save.overwrite = Êtes-vous sûr de vouloir remplacer cette sauvegarde? -text.overwrite = Écraser -text.save.none = Aucune sauvegarde trouvée! -text.saveload = [accent]Sauvegarde ... -text.savefail = Échec de la sauvegarde du jeu ! -text.save.delete.confirm = Êtes-vous sûr de vouloir supprimer cette sauvegarde? -text.save.delete = Supprimer -text.save.export = Exporter la sauvegarde -text.save.import.invalid = [orange]Cette sauvegarde est invalide! -text.save.import.fail = [crimson]Echec de l'importation de la sauvegarde: [orange] {0} -text.save.export.fail = [crimson]Échec de l'exportation de la sauvegarde: [orange] {0} -text.save.import = Importer la sauvegarde -text.save.newslot = Enregistrer le nom: -text.save.rename = Renommer -text.save.rename.text = Nouveau nom: -text.selectslot = Sélectionnez une sauvegarde. -text.slot = [accent]Emplacement {0} -text.save.corrupted = [orange]Le fichier enregistrer est corrompu ou invalide! -text.empty = -text.on = Allumer -text.off = Eteint -text.save.autosave = Sauvegarde automatique -text.save.map = Carte -text.save.wave = Vague : -text.save.difficulty = DIFFICULTÉ -text.save.date = Dernière sauvegarde: {0} -text.confirm = Confirmer -text.delete = Supprimer -text.ok = OK -text.open = Ouvrir -text.cancel = Annuler -text.openlink = Lien public -text.back = Retour -text.quit.confirm = Êtes-vous sûr de vouloir quitter? -text.changelog.title = Note de mise à jour -text.changelog.error = [Scarlet] Erreur lors de l'obtention du changement de serveur! Vérifiez votre connexion internet. -text.changelog.current = [jaune] [[Version actuelle] -text.changelog.latest = [orange][[Dernière version] -text.loading = [accent]Chargement ... -text.wave = [orange]Vague {0} -text.wave.waiting = Vague dans {0} -text.waiting = Attente... -text.enemies = ennemis -text.enemies.single = Ennemi -text.loadimage = Charger l'image -text.saveimage = Enregistrer l'image -text.oregen = Génération de minerais -text.editor.badsize = [orange]Dimensions de l'image non valides![] Dimensions de la carte valides: {0} -text.editor.errorimageload = Erreur lors du chargement du fichier image:[orange] {0} -text.editor.errorimagesave = Erreur lors de la sauvegarde du fichier image:[orange] {0} -text.editor.generate = Générer -text.editor.resize = Redimensionner -text.editor.loadmap = Charger la carte -text.editor.savemap = Enregistrer la carte -text.editor.loadimage = Charger l'image -text.editor.saveimage = Enregistrer l'image -text.editor.unsaved = [scarlet] Vous avez des changements non sauvegardés![] Êtes-vous sûr de vouloir quitter? -text.editor.brushsize = Taille de la brosse: -text.editor.noplayerspawn = Cette carte n'a pas de point d'apparition de joueur! -text.editor.manyplayerspawns = Les cartes ne peuvent pas avoir plus d'un point d'apparition de joueur! -text.editor.manyenemyspawns = Il ne peut pas avoir plus de {0} points d'apparition ennemis! -text.editor.resizemap = Redimensionner la carte -text.editor.resizebig = [scarlet]Attention![] Les cartes de plus de 256 unités peuvent laggés et être instables. -text.editor.mapname = Nom de la carte: -text.editor.overwrite = [accent]Attention! Cela écrasera la carte existante. -text.editor.failoverwrite = [Crimson]Impossible d'écraser la carte par défaut! -text.editor.selectmap = Sélectionnez une carte à charger: -text.width = Largeur: -text.height = Hauteur: -text.randomize = Rendre aléatoire -text.apply = Appliquer -text.update = Modifier -text.menu = Menu -text.play = Jouer -text.load = Charger -text.save = Sauvegarder -text.language.restart = Veuillez redémarrer votre jeu pour que les paramètres de langue soient appliqués. -text.settings.language = Langue -text.settings = Réglages -text.tutorial = Tutoriel -text.editor = Éditeur -text.mapeditor = Éditeur de carte -text.donate = Faire un don -text.settings.reset = Valeur par défaut. -text.settings.controls = Contrôles -text.settings.game = Jeu -text.settings.sound = Son -text.settings.graphics = Graphique -text.upgrades = Améliorations -text.purchased = [VERT]Créé! -text.weapons = Armes -text.paused = Pause -text.respawn = Réapparition dans -text.info.title = [accent]Info -text.error.title = [crimson]Une erreur est survenue -text.error.crashmessage = [SCARLET]Une erreur inattendue est survenue, et aurait provoqué un crash.[] Veuillez indiquer les circonstances exactes dans lesquelles cette erreur est survenue au développeur:[ORANGE] anukendev@gmail.com[] -text.error.crashtitle = Une erreur est survenue -text.mode.break = Mode de rupture: {0} -text.mode.place = Placez le mode: {0} -placemode.hold.name = Ligne -placemode.areadelete.name = surface -placemode.touchdelete.name = toucher -placemode.holddelete.name = tenir -placemode.none.name = aucun -placemode.touch.name = toucher -placemode.cursor.name = curseur -text.blocks.extrainfo = [accent] info supplémentaire du bloc: -text.blocks.blockinfo = Bloquer les infos -text.blocks.powercapacity = capacité d'énergie -text.blocks.powershot = Energie/Tir -text.blocks.powersecond = Energie/Seconde -text.blocks.powerdraindamage = Utilisation d'énergie/Dommage -text.blocks.shieldradius = rayon du bouclier -text.blocks.itemspeedsecond = Vitesse d'Article/Seconde -text.blocks.range = Portée -text.blocks.size = Taille -text.blocks.powerliquid = énergie/Liquide -text.blocks.maxliquidsecond = Max Liquide/Seconde -text.blocks.liquidcapacity = Capacité liquide -text.blocks.liquidsecond = Liquide/seconde -text.blocks.damageshot = Dégâts/tir -text.blocks.ammocapacity = Capacité de munitions -text.blocks.ammo = Munition -text.blocks.ammoitem = Munitions/Article -text.blocks.maxitemssecond = Max articles/seconde -text.blocks.powerrange = Gamme de puissance -text.blocks.lasertilerange = portée du laser -text.blocks.capacity = Capacité -text.blocks.itemcapacity = Capacité article -text.blocks.maxpowergenerationsecond = Génération max d'énergie/seconde -text.blocks.powergenerationsecond = Génération d'énergie/seconde -text.blocks.generationsecondsitem = Génération Secondes / item -text.blocks.input = entrée -text.blocks.inputliquid = Entrée de liquide -text.blocks.inputitem = entré d'article -text.blocks.output = Sortie -text.blocks.secondsitem = Secondes/article -text.blocks.maxpowertransfersecond = Transfert max d'énergie/seconde -text.blocks.explosive = Hautement explosif ! -text.blocks.repairssecond = Réparation/seconde -text.blocks.health = Santé -text.blocks.inaccuracy = Inexactitude -text.blocks.shots = tirs -text.blocks.shotssecond = Tirs/seconde -text.blocks.fuel = Carburant -text.blocks.fuelduration = Durée du carburant -text.blocks.maxoutputsecond = Sortie max/seconde -text.blocks.inputcapacity = Capacité d'entrée -text.blocks.outputcapacity = Capacité de sortie -text.blocks.poweritem = Energie/Article -text.placemode = Mode Placement -text.breakmode = Mode destruction -text.health = santé -setting.difficulty.easy = facile -setting.difficulty.normal = normal -setting.difficulty.hard = difficile -setting.difficulty.insane = Extreme -setting.difficulty.purge = Purge +credits.text = Created by [ROYAL]Anuken[] - [SKY]anukendev@gmail.com[]\n\n[GRAY](In case you can't tell, this text is currently unfinished.\nTranslators, don't edit it yet!) +credits = Crédits +contributors = Traducteurs et contributeurs +discord = Rejoignez le discord de Mindustry +link.discord.description = Le discord officiel de Mindustry! +link.github.description = Code source du jeu. +link.dev-builds.description = Versions instables du jeu. +link.trello.description = Trello officiel pour les futurs ajouts . +link.itch.io.description = Page itch.io avec le lien du téléchargement pour PC et la version web . +link.google-play.description = Accès à la version android du Google Play Store. +link.wiki.description = Wiki officiel de mindustry . +linkfail = Erreur lors de l'ouverture du lien !\nL'URL a été copié avec succès. +screenshot = Capture d'écran sauvegardée à {0} +screenshot.invalid = Map too large, potentially not enough memory for screenshot. +gameover = Partie terminée. +gameover.pvp = L'équipe [accent] {0}[] a gagnée ! +highscore = [YELLOW]Nouveau meilleur score! +stat.wave = Vagues vaincues:[accent] {0} +stat.enemiesDestroyed = Ennemies détruits:[accent] {0} +stat.built = Bâtiments construits:[accent] {0} +stat.destroyed = Bâtiments détruits:[accent] {0} +stat.deconstructed = Bâtiments déconstruits:[accent] {0} +stat.delivered = Ressources transférées: +stat.rank = Rang Final: [accent]{0} +placeline = Tu as sélectionné un bloc.\nTu peux les[accent] placer en rangée[] en[accent] maintenant ton doigt sur l'écran pendant quelques secondes[] et en le glissant vers n'importe qu'elle direction.\nEssaye! +removearea = Tu as sélectionné le mode de suppression.\nTu peux[accent] supprimer les blocs en rectangle[] en[accent] maintenant ton doigt sur l'écran pendant quelques secondes[] et en le glissant.\nEssaye! +launcheditems = [accent]ressources transférées +map.delete = Êtes-vous sûr de supprimer cette carte"[accent]{0}[]"? +level.highscore = Meilleur score: [accent]{0} +level.select = Sélection de niveau +level.mode = Mode de jeu : +showagain = Ne pas montrer la prochaine fois +coreattack = [scarlet] +nearpoint = [[ [scarlet]QUITTEZ LE POINT D'APPARITION ENNEMI IMMÉDIATEMENT[] ]\nannihilation imminente +outofbounds = [[ HORS LIMITES ]\n[]auto-destruction dans {0} +database = Base de données +savegame = Sauvegarder la partie +loadgame = Charger la partie +joingame = Rejoindre une partie +addplayers = Ajouter/Enlever des joueurs +customgame = Partie customisée +newgame = Nouvelle partie +none = +minimap = Minimap +close = Fermer +quit = Quitter +maps = Cartes +continue = Continuer +maps.none = [LIGHT_GRAY]Aucune carte trouvée! +about.button = À propos +name = Nom: +noname = Commencer par choisir un[accent] nom de joueur[]. +filename = Nom du fichier: +unlocked = Nouveau bloc débloqué! +completed = [accent]Complété +techtree = Arbre technologique +research.list = [LIGHT_GRAY]Recherche: +research = Rechercher +researched = [LIGHT_GRAY]{0} recherché(e). +players = {0} joueurs en ligne +players.single = {0} joueurs en ligne +server.closing = [accent]Fermeture du serveur... +server.kicked.kick = Vous avez été expulsé du serveur! +server.kicked.serverClose = Serveur fermé. +server.kicked.clientOutdated = Client dépassé! Mettez à jour votre jeu! +server.kicked.serverOutdated = Serveur dépassé! Demandez à l'hôte de le mettre à jour! +server.kicked.banned = Vous avez été banni sur ce serveur. +server.kicked.recentKick = Vous avez été expulsé récemment.\nAttendez avant de vous connecter à nouveau. +server.kicked.nameInUse = Il y a déjà quelqu'un avec ce nom\nsur ce serveur. +server.kicked.nameEmpty = Votre nom doit contenir au moins une lettre ou un chiffre. +server.kicked.idInUse = Vous êtes déjà sur ce serveur ! Se connecter avec deux comptes n'est pas permis ! +server.kicked.customClient = Ce serveur ne supporte pas les versions personnalisées (Custom builds). Téléchargez une version officielle. +server.kicked.gameover = Game over! +host.info = Le bouton [accent]Héberger[] héberge un serveur sur le port [scarlet]6567[]. \nN'importe qui sur le même [LIGHT_GRAY]wifi ou réseau local []devrait voir votre serveur sur leur liste des serveurs.\n\nSi vous voulez que les gens puissent s'y connecter de partout à l'aide de votre IP, [accent]le transfert de port (port forwarding)[] est demandé.\n\n[LIGHT_GRAY]Note: Si quelqu'un a des problèmes de connexion à votre partie LAN, vérifiez que vous avez autorisé l'accès à Mindustry sur votre réseau local dans les paramètres de votre pare-feu. +join.info = Ici vous pouvez entrez [accent]l'IP d'un serveur []pour s'y connecter, ou découvrir un serveur en [accent]réseau local[].\nLe multijoueur en LAN ainsi qu'en WAN est supporté.\n\n[LIGHT_GRAY]Note: Il n'y a pas de liste de serveurs globaux automatiques; Si vous voulez vous connectez à quelqu'un par IP, il faudra d'abord demander à l'hébergeur leur IP. +hostserver = Héberger une partie +hostserver.mobile = Héberger\nune partie +host = Héberger +hosting = [accent]Ouverture du serveur... +hosts.refresh = Actualiser +hosts.discovering = Découverte de jeux en LAN +server.refreshing = Actualisation du serveur +hosts.none = [lightgray]Aucun jeu en LAN trouvé! +host.invalid = [scarlet]Impossible de se connecter à l'hôte. +trace = Suivre le joueur +trace.playername = Nom du joueur : [accent]{0} +trace.ip = IP: [accent]{0} +trace.id = ID Unique : [accent]{0} +trace.mobile = Mobile Client: [accent]{0} +trace.modclient = Client personnalisé: [accent]{0} +invalidid = invalidid=ID du client invalide! Soumettre un rapport d'erreur +server.bans = Bannis +server.bans.none = Aucun joueur banni trouvé! +server.admins = Administrateurs +server.admins.none = Pas d'administrateurs trouvés! +server.add = Ajouter un serveur +server.delete = Êtes-vous sûr de supprimer ce serveur ? +server.hostname = Héberger: {0} +server.edit = Modifier le serveur +server.outdated = [crimson]Serveur obsolète![] +server.outdated.client = [crimson]Client obsolète![] +server.version = [lightgray]Version: {0} {1} +server.custombuild = [yellow]Version personnalisée +confirmban = Êtes-vous sûr de bannir ce joueur? +confirmkick = Êtes-vous sûr d'expulser ce joueur? +confirmunban = Êtes-vous sûr de réintégrer ce joueur ? +confirmadmin = Êtes-vous sûr de faire de ce joueur un administrateur? +confirmunadmin = Êtes-vous sûr d'enlever le statut d'administrateur à ce joueur? +joingame.title = Rejoindre une partie +joingame.ip = IP: +disconnect = Déconnecté. +disconnect.data = Les données du monde n'ont pas pu être chargées ! +connecting = [accent]Connexion... +connecting.data = [accent]Chargement des données du monde... +server.port = Port: +server.addressinuse = Addresse déjà utilisée! +server.invalidport = numéro de port invalide! +server.error = [crimson]Erreur d'hébergement: [accent]{0} +save.old = Cette sayvegarde provient d'une ancienne version du jeu, et ne peut plus être utilisée.\n\n[LIGHT_GRAY]la compabilité des anciennes sauvegardes sera bientôt ajoutée dans la version 4.0 stable. +save.new = Nouvelle sauvegarde +save.overwrite = Êtes-vous sûr d'écraser\ncette sauvegarde ? +overwrite = Écraser +save.none = Aucune sauvegarde trouvée ! +saveload = [accent]Sauvegarde... +savefail = Échec de la sauvegarde! +save.delete.confirm = Êtes-vous sûr de supprimer cette sauvegarde? +save.delete = supprimer +save.export = Exporter une\nSauvegarde +save.import.invalid = [accent]Cette sauvegarde est invalide! +save.import.fail = [crimson]L'importation de la sauvegarde\na échoué: [accent]{0} +save.export.fail = [crimson]L'exportation de la sauvegarde\na échoué [accent]{0} +save.import = Importer une sauvegarde +save.newslot = Nom de la sauvegarde: +save.rename = Renommer +save.rename.text = Nouveau nom: +selectslot = Sélectionner une sauvegarde. +slot = [accent]Emplacement {0} +save.corrupted = [accent]Fichier de sauvegarde corrompu ou invalide!\nSi vous venez de mettre à jour votre jeu, c'est probablement dû à un changement du format de sauvegarde et [scarlet]non[] un bug. +empty = +on = On +off = Off +save.autosave = Sauvegarde automatique: {0} +save.map = Carte: {0} +save.wave = Vague {0} +save.difficulty = Difficulté: {0} +save.date = Dernière sauvegarde: {0} +save.playtime = Temps de jeu: {0} +warning = Avertissement. +confirm = Confirmer +delete = Supprimer +ok = OK +open = Ouverture +customize = Customize +cancel = Annuler +openlink = Ouvrir le lien +copylink = Copier le lien +back = Retour +quit.confirm = Êtes-vous sûr de partir? +changelog.title = Notes de mise à jour +changelog.loading = Récupération des notes de mise à jour... +changelog.error.android = [accent]Remarquez que les notes de mise à jour peuvent ne pas marcher sur Android 4.4 et inférieur!\nC'est dû à un bug interne d'Android . +changelog.error.ios = [accent]Les notes de mise à jour ne sont pas suppporté sur iOS. +changelog.error = [scarlet]Erreur lors de la récupération des notes de mises à jour!\nVérifiez votre connexion internet. +changelog.current = [yellow][[Version actuelle] +changelog.latest = [accent][[Dernière version] +loading = [accent]Chargement... +saving = [accent]Sauvegarde... +wave = [accent]Vague {0} +wave.waiting = [LIGHT_GRAY]Vague dans {0} +wave.waveInProgress = [LIGHT_GRAY]Wave in progress +waiting = [LIGHT_GRAY]En attente... +waiting.players = En attente de joueurs... +wave.enemies = [LIGHT_GRAY]{0} Ennemis restants +wave.enemy = [LIGHT_GRAY]{0} Ennemi restant +loadimage = Charger l'image +saveimage = Sauvegarder l'image +unknown = Inconnu +custom = Personnalisé +builtin = Pré-fait +map.delete.confirm = Êtes-vous sûr de supprimer cette carte? Cette action ne peut pas être défaite! +map.random = [accent]Carte aléatoire +map.nospawn = Cette carte n'a pas de base pour que le joueur y apparaisse! Ajouter une [ROYAL]base bleue[] sur cette carte dans l'éditeur. +map.nospawn.pvp = Cette carte n'a pas de base ennemies pour qu'un joueur ennemi y apparaisse! Ajouter au moins une [SCARLET]Base rouge[] sur cette carte dans l'éditeur. +map.nospawn.attack = This map does not have any enemy cores for player to attack! Add[SCARLET] red[] cores to this map in the editor. +map.invalid = Erreur lors du chargement de la carte: carte corrompue ou invalide. +editor.brush = Pinceau +editor.openin = Ouvrir dans l'éditeur +editor.oregen = Génération de minerais +editor.oregen.info = Génération de minerais: +editor.mapinfo = Infos sur la carte +editor.author = Auteur: +editor.description = Description: +editor.waves = Vagues: +editor.rules = Rules: +editor.ingame = Edit In-Game +waves.title = Vagues +waves.remove = Remove +waves.never = jamais +waves.every = tous les +waves.waves = vague(s) +waves.perspawn = par apparition +waves.to = à +waves.boss = Boss +waves.preview = Prévisualiser +waves.edit = Modifier... +waves.copy = Copier dans le Presse-papiers +waves.load = Coller depuis le Presse-papiers +waves.invalid = Vagues invalides dans le Presse-papiers. +waves.copied = Vagues copiées +editor.default = [LIGHT_GRAY] +edit = Modifier... +editor.name = Nom: +editor.spawn = Spawn Unit +editor.removeunit = Remove Unit +editor.teams = Équipe +editor.elevation = Élevation +editor.errorload = Erreur lors du chargement du fichier:\n[accent]{0} +editor.errorsave = Erreur lors de la sauvegarde du fichier:\n[accent]{0} +editor.errorimage = That's an image, not a map. Don't go around changing extensions expecting it to work.\n\nIf you want to import a legacy map, use the 'import legacy map' button in the editor. +editor.errorlegacy = This map is too old, and uses a legacy map format that is no longer supported. +editor.errorheader = This map file is either not valid or corrupt. +editor.errorname = La carte n'a pas de nom! +editor.update = Mettre à jour +editor.randomize = Randomiser +editor.apply = Appliquer +editor.generate = Générer +editor.resize = Redimensionner +editor.loadmap = Charger une carte +editor.savemap = Sauvegarder une carte +editor.saved = Sauvegardé! +editor.save.noname = Votre carte n'a pas de nom ! Définissez-en un sur le menu 'info de la carte'. +editor.save.overwrite = Votre carte réécrit une carte préfaite! Choisissez un nom différent dans le menu 'info de la carte' . +editor.import.exists = [scarlet]Importation impossible :[] Une carte préfaite nommé '{0}' existe déjà! +editor.import = Importation... +editor.importmap = Importer une carte +editor.importmap.description = Importer une carte existante +editor.importfile = Importer un fichier +editor.importfile.description = Importer un fichier de carte extérieur . +editor.importimage = Importer l'image du terrain +editor.importimage.description = Importer une image de la carte extérieure +editor.export = Exporter... +editor.exportfile = Exporter un fichier +editor.exportfile.description = Exporter une fichier de carte +editor.exportimage = Exporter l'image du terrain +editor.exportimage.description = Exporter une image de la carte +editor.loadimage = Importer le terrain +editor.saveimage = Exportr le terrain +editor.unsaved = [scarlet]Vous avez des changements non sauvegardés ![]\nÊtes-vous sûr de partir +editor.resizemap = Redimensionner\nla carte +editor.mapname = Nom de la carte²: +editor.overwrite = [accent]Attention !\nCeci réécrit une carte existante . +editor.overwrite.confirm = [scarlet]Attention ![] Une carte avec ce nom existe déjà. Êtes-vous sûr de vouloir la réécrire? +editor.selectmap = Séléctionnez une carte: +filters.empty = [LIGHT_GRAY]Aucun filtre! Ajoutez-en un avec les boutons ci-dessous. +filter.distort = Déformation +filter.noise = Bruit +filter.ore = Minerai +filter.rivernoise = Bruit des rivières +filter.scatter = Dispersement +filter.terrain = Terrain +filter.option.scale = Gamme +filter.option.chance = Chance +filter.option.mag = Magnitude +filter.option.threshold = Seuil +filter.option.circle-scale = Gamme du cercle +filter.option.octaves = Octaves +filter.option.falloff = Diminution +filter.option.block = Bloc +filter.option.floor = Sol +filter.option.wall = Mur +filter.option.ore = Minerai +filter.option.floor2 = Sol secondaire +filter.option.threshold2 = Seuil secondaire +filter.option.radius = Radius +filter.option.percentile = Percentile +width = Largeur: +height = Hauteur: +menu = Menu +play = Jouer +load = Charger +save = Sauvegarder +fps = FPS: {0} +tps = TPS: {0} +ping = Ping: {0}ms +language.restart = Veuillez redémarrez votre jeu pour le changement de langage prenne effet. +settings = Paramètres +tutorial = Tutoriel +editor = Éditeur +mapeditor = Éditeur de carte +donate = Faire un\ndon +abandon = Abandon +abandon.text = Cette zone et tous ses ressources vont être perdues. +locked = Verrouillé +complete = [LIGHT_GRAY]Compléter: +zone.requirement = Vague {0} dans la zone {1} +resume = Reprendre la partie en cours:\n[LIGHT_GRAY]{0} +bestwave = [LIGHT_GRAY]Meilleur: {0} +launch = Lancement +launch.title = Lancement réussi +launch.next = [LIGHT_GRAY] Prochaine opportunité à la vague {0} +launch.unable = [scarlet]Impossible d'effectuer le lancement.[] Ennemis. +launch.confirm = Cela va transférer tous tes ressources dans votre coeur.\nTu ne vas pas pouvoir retourner à cette base. +uncover = Découvrir +configure = Configurer le transfert des ressources. +configure.locked = [LIGHT_GRAY]Atteigner la vague {0}\npour configurer le transfert des ressources +zone.unlocked = [LIGHT_GRAY]{0} Débloquée. +zone.requirement.complete = Vague {0} atteinte:\n{1} Exigences de la zone complétées +zone.config.complete = Vague {0} atteinte:\nConfiguration du transfert débloquée. +zone.resources = Ressources détectées: +add = Ajouter... +boss.health = Vie du BOSS +connectfail = [crimson]Échec de la connexion au serveur : [accent]{0} +error.unreachable = Serveur injoignable. +error.invalidaddress = Adresse invalide. +error.timedout = Délai de connexion dépassé!\nAssurez-vous que l'hôte a autorisé l'accès au port, et que l'adresse soit correcte! +error.mismatch = Erreur de paquet:\nPossible différence de verison entre le client et le serveur .\nVérifiez que vous et l'hôte avez la plus récente version de Mindustry ! +error.alreadyconnected = Déjà connecté. +error.mapnotfound = Fichier de la carte introuvable! +error.io = Erreur de Réseau (I/O) +error.any = Erreur réseau inconnue. +zone.groundZero.name = Première Bataille +zone.desertWastes.name = Desert Wastes +zone.craters.name = Les Cratères +zone.frozenForest.name = Forêt Glaciale +zone.ruinousShores.name = Rives en Ruine +zone.stainedMountains.name = Montagnes Tâchetées +zone.desolateRift.name = Fissure abandonnée +zone.nuclearComplex.name = Complexe nucléaire +zone.overgrowth.name = Overgrowth +zone.tarFields.name = Tar Fields +settings.language = Langage +settings.reset = Valeur par défaut. +settings.rebind = Réattribuer +settings.controls = Contrôles +settings.game = Jeu +settings.sound = Son +settings.graphics = Graphismes +settings.cleardata = Effacer les données du jeu... +settings.clear.confirm = Êtes-vous sûr d'effacer ces données ?\nCe qui est fait ne peut être défait ! +settings.clearall.confirm = [scarlet]ATTENTION![]\nCet action effacera toutes les données , incluant les sauvegarges, les cartes, les déblocages et la configuration des touches.\nUne fois que vous aurez pressé 'ok' le jeu effacera toutes les données et se fermera. +settings.clearunlocks = Effacer les déblocages +settings.clearall = Tout effacer +paused = En pause +yes = Oui +no = Non +info.title = Info +error.title = [crimson]Une erreur s'est produite +error.crashtitle = Une erreur s'est produite +blocks.input = Input +blocks.output = Output +blocks.booster = Booster +block.unknown = [LIGHT_GRAY]??? +blocks.powercapacity = Capacité d'énergie +blocks.powershot = Énergie/Tir +blocks.targetsair = Cible les unités aériennes +blocks.targetsground = Cible les unités terrestres +blocks.itemsmoved = Vitesse de Déplacement +blocks.launchtime = Temps entre chaque lancement +blocks.shootrange = Portée +blocks.size = Taille +blocks.liquidcapacity = Capacité en liquide +blocks.powerrange = Distance de transmission +blocks.poweruse = Énergie utilisée +blocks.powerdamage = Énergie/Dégâts +blocks.itemcapacity = Stockage +blocks.basepowergeneration = Génération d'énergie minimale +blocks.productiontime = Temps de production +blocks.repairtime = Temps pour la Réparation Totale du Bloc +blocks.speedincrease = Speed Increase +blocks.range = Portée +blocks.drilltier = Forable +blocks.drillspeed = Vitesse de forage de base +blocks.boosteffect = Boost Effect +blocks.maxunits = Nombre d'unités actives maximal +blocks.health = Santé +blocks.buildtime = Build Time +blocks.inaccuracy = Précision +blocks.shots = Tir +blocks.reload = Tirs/Seconde +blocks.ammo = Munitions +bar.drillspeed = Vitesse de forage: {0}/s +bar.efficiency = Efficacité: {0}% +bar.powerbalance = Énergie: {0} +bar.poweramount = Power: {0} +bar.poweroutput = Énergie en sortie: {0} +bar.items = Objets: {0} +bar.liquid = Liquide +bar.heat = Chaleur +bar.power = Énergie +bar.progress = Progression de la construction +bar.spawned = Unités: {0}/{1} +bullet.damage = [stat]{0}[lightgray] dmg +bullet.splashdamage = [stat]{0}[lightgray] dmg de zone ~[stat] {1}[lightgray] tuiles +bullet.incendiary = [stat]incendiaire +bullet.homing = [stat]autoguidage +bullet.shock = [stat]choc +bullet.frag = [stat]frag +bullet.knockback = [stat]{0}[lightgray] recul +bullet.freezing = [stat]gel +bullet.tarred = [stat]tarred +bullet.multiplier = [stat]{0}[lightgray]x multiplicateur de munitions +bullet.reload = [stat]{0}[lightgray]x rechargement +unit.blocks = blocs +unit.powersecond = Énergie/seconde +unit.liquidsecond = Liquides/seconde +unit.itemssecond = Objets/seconde +unit.liquidunits = Unité de liquide +unit.powerunits = Unité d'énergie +unit.degrees = degrés +unit.seconds = secondes +unit.persecond = /sec +unit.timesspeed = x speed +unit.percent = % +unit.items = Objets +category.general = Général +category.power = Énergie +category.liquids = Liquides +category.items = Objets +category.crafting = Fabrication +category.shooting = Défense +category.optional = Améliorations optionnelles +setting.landscape.name = Verrouiller la rotation en mode paysage +setting.shadows.name = Ombres +setting.linear.name = Linear Filtering +setting.animatedwater.name = Eau animée +setting.animatedshields.name = Animated Shields +setting.antialias.name = Antialias[LIGHT_GRAY] (demande le redémarrage de l'appareil)[] +setting.indicators.name = Indicateurs pour les alliés +setting.autotarget.name = Visée automatique +setting.fpscap.name = Max FPS +setting.fpscap.none = Aucun +setting.fpscap.text = {0} FPS +setting.swapdiagonal.name = Autoriser le placement des blocs en diagonal +setting.difficulty.training = Entraînement +setting.difficulty.easy = Facile +setting.difficulty.normal = Normal +setting.difficulty.hard = Difficile +setting.difficulty.insane = Extrème setting.difficulty.name = Difficulté: -setting.screenshake.name = Tremblement d'écran -setting.smoothcam.name = Caméra lisse -setting.indicators.name = Indicateurs ennemis -setting.effects.name = Effets d'affichage +setting.screenshake.name = Tremblement de l'écran +setting.effects.name = Montrer les effets setting.sensitivity.name = Sensibilité de la manette setting.saveinterval.name = Intervalle des sauvegardes auto setting.seconds = {0} secondes setting.fullscreen.name = Plein écran -setting.multithread.name = Multithreading [scarlet] (instable!) +setting.borderlesswindow.name = Borderless Window[LIGHT_GRAY] (may require restart) setting.fps.name = Afficher FPS setting.vsync.name = VSync setting.lasers.name = Afficher les rayons des lasers -setting.healthbars.name = Afficher les barres de santé des entités -setting.pixelate.name = Pixéliser l'écran -setting.musicvol.name = volume musique -setting.mutemusic.name = Musique muette -setting.sfxvol.name = Volume SFX -setting.mutesound.name = Son muet -map.maze.name = Labyrinthe -map.fortress.name = forteresse -map.sinkhole.name = gouffre -map.caves.name = cavernes -map.volcano.name = volcan -map.caldera.name = chaudron -map.scorch.name = brûlure -map.desert.name = désert -map.island.name = Île -map.grassland.name = prairie -map.tundra.name = toundra -map.spiral.name = spirale -map.tutorial.name = tutoriel -tutorial.intro.text = [yellow]Bienvenue dans le tutoriel[]. Pour commencer, appuyez sur \"suivant\". -tutorial.moveDesktop.text = Pour vous déplacer, utilisez les touches [orange][[WASD][]. Maintenez [orange]shift[] pour accélérer. Maintenez la touche [orange]CTRL[] enfoncée tout en utilisant la [orange]molette[] pour effectuer un zoom avant ou arrière. -tutorial.shoot.text = Utilisez votre souris pour viser, maintenez le [orange]bouton gauche de la souris[] pour tirer. Essayez de pratiquer sur la [yellow]cible[]. -tutorial.moveAndroid.text = Pour déplacer votre vue, faites glisser un doigt sur l'écran. Pincez et faites glisser pour effectuer un zoom avant ou arrière. -tutorial.placeSelect.text = Essayez de sélectionner un [yellow]transporteur[] dans le menu des blocs en bas à droite. -tutorial.placeConveyorDesktop.text = Utilisez la [orange][[molette][] pour faire pivoter le transporteur [orange]vers l'avant[], puis placez-le dans l'emplacement [yellow]marqué[] en utilisant le [orange][[bouton gauche de la souris][]. -tutorial.placeConveyorAndroid.text = Utilisez [orange][[bouton de rotation][] pour faire pivoter le transporteur [orange]vers l'avant[], faites-le glisser avec votre doigt, puis placez-le dans l'emplacement [yellow]marqué[] avec [orange][[coche][]. -tutorial.placeConveyorAndroidInfo.text = Vous pouvez également appuyer sur l'icône du réticule en bas à gauche pour passer en [orange][[mode tactile][] et placer des blocs en appuyant sur l'écran. En mode tactile, les blocs peuvent être pivotés avec la flèche en bas à gauche. Appuyez sur [yellow]suivant[] pour essayer. -tutorial.placeDrill.text = Maintenant, sélectionnez et placez un [yellow]extracteur de pierre[] à l'endroit marqué. -tutorial.blockInfo.text = Si vous voulez en savoir plus sur un bloc, vous pouvez appuyer sur le [orange]point d'interrogation[] en haut à droite pour lire sa description. -tutorial.deselectDesktop.text = Vous pouvez désélectionner un bloc en utilisant le [orange][[bouton droit de la souris][]. -tutorial.deselectAndroid.text = Vous pouvez désélectionner un bloc en appuyant sur le bouton [orange]X[]. -tutorial.drillPlaced.text = L'extracteur produira maintenant de la [yellow]pierre[], qui sera placée sur le transporteur, puis sera emmenée dans le [yellow]noyau[]. -tutorial.drillInfo.text = Différents minerais ont besoin de différents extracteurs. La pierre nécessite des extracteurs de pierre, le fer des extracteurs de fer,...etc... -tutorial.drillPlaced2.text = Le déplacement d'article dans le noyau le place dans votre[yellow]inventaire[], au milieu à droite. Le placement de blocs utilise les éléments de votre inventaire. -tutorial.moreDrills.text = Vous pouvez relier plusieurs extracteurs et transporteurs ensemble, comme ça. -tutorial.deleteBlock.text = Vous pouvez supprimer des blocs en cliquant sur le clique [orange]droit de la souris[] sur le bloc que vous voulez supprimer. Essayez de supprimer ce transporteur. -tutorial.deleteBlockAndroid.text = Vous pouvez supprimer des blocs en [orange]sélectionnant le réticule[] dans [orange] le menu du mode destruction[] en bas à gauche et en appuyant sur un bloc. Essayez de supprimer ce transporteur. -tutorial.placeTurret.text = Maintenant, sélectionnez et placez une [yellow]tourelle[] à l'[yellow]emplacement marqué[]. -tutorial.placedTurretAmmo.text = Cette tourelle accepte maintenant les [yellow]munitions[] du transporteur. Vous pouvez voir combien de munitions elle a en la survolant et en vérifiant sa [green]barre verte[]. -tutorial.turretExplanation.text = Les tourelles tirent automatiquement sur l'ennemi le plus proche, pourvu qu'elles aient suffisamment de munitions. -tutorial.waves.text = Toutes les [yellow]60 secondes[], une [coral]vague d'ennemis[] apparaîtra dans des endroits spécifiques et tentera de détruire votre noyau. -tutorial.coreDestruction.text = Votre objectif est de [yellow]défendre le noyau[]. Si le noyau est détruit, vous [coral]perdez le jeu[]. -tutorial.pausingDesktop.text = Si vous avez besoin de faire une pause, appuyez sur le bouton [orange]pause[] en haut à gauche pour mettre le jeu en pause. Vous pouvez toujours casser et placer des blocs en pause, mais vous ne pouvez pas bouger ou tirer. -tutorial.pausingAndroid.text = Si vous avez besoin de faire une pause, appuyez sur le bouton [orange]pause[] en haut à gauche pour mettre le jeu en pause. Vous pouvez toujours casser et placer des blocs en pause. -tutorial.purchaseWeapons.text = Vous pouvez acheter de nouvelles [yellow]armes[] pour votre robot en ouvrant le menu de mise à niveau en bas à gauche. -tutorial.switchWeapons.text = Changez d'arme en cliquant sur l'icône en bas à droite, ou en utilisant les chiffres [orange][[1-9][]. -tutorial.spawnWave.text = Une vague arrive. Détruisez-les. -tutorial.pumpDesc.text = Dans les vagues plus lointaines, vous devrez peut-être utiliser des [yellow]pompes[] pour distribuer du liquide pour les générateurs ou les extracteurs. -tutorial.pumpPlace.text = Les pompes fonctionnent de la même manière que les extracteurs, sauf qu'elles récoltent du liquides et non du minerai. Essayez de placer une pompe sur [yellow]pétrole[] désigné. -tutorial.conduitUse.text = Maintenant, placez un [orange]conduit[] qui s'éloigne de la pompe. -tutorial.conduitUse2.text = Et un autre ... -tutorial.conduitUse3.text = Et un autre ... -tutorial.generator.text = Maintenant, placez un bloc [orange]de générateur à combustion[] à l'extrémité du conduit. -tutorial.generatorExplain.text = Ce générateur va maintenant créer de l' [yellow]énergie[] à partir de pétrole. -tutorial.lasers.text = L'énergie est distribuée à l'aide de [yellow]lasers d'énergie[]. Tournez et placez-en un ici. -tutorial.laserExplain.text = Le générateur va maintenant déplacer la puissance dans le laser. Un faisceau [yellow]opaque[] signifie qu'il transmet actuellement de la puissance, et un faisceau [yellow]transparent[] signifie que ce n'est pas le cas. -tutorial.laserMore.text = Vous pouvez vérifier la puissance d'un bloc en survolant celui-ci et en vérifiant la [yellow]barre jaune[] en haut. -tutorial.healingTurret.text = Ce laser peut être utilisé pour alimenter une [lime]tourelle de réparation[]. Placez-en une ici. -tutorial.healingTurretExplain.text = Tant qu'elle a de la puissance, cette tourelle [lime]réparera les blocs voisins[].En jouant, assurez-vous d'en avoir une dans votre base le plus rapidement possible! -tutorial.smeltery.text = De nombreux blocs nécessitent de l' [orange]acier[] pour fabriquer, ce qui nécessite une [orange]fonderie[]. Placez-en une ici. -tutorial.smelterySetup.text = Cette fonderie produira maintenant de l' [orange]acier[] à partir du fer, utilisant le charbon comme combustible. -tutorial.tunnelExplain.text = Notez également que les objets traversent un [orange]tunnel[] et émergent de l'autre côté, traversant le bloc de pierre. Gardez à l'esprit que les tunnels ne peuvent traverser que 2 blocs. -tutorial.end.text = Et cela conclut le tutoriel! Bonne chance! -text.keybind.title = Relier le clés +setting.pixelate.name = Pixeliser[LIGHT_GRAY] (disables animations) +setting.minimap.name = Montrer la minimap +setting.musicvol.name = Volume de la musique +setting.mutemusic.name = Couper la musique +setting.sfxvol.name = Volume des SFX +setting.mutesound.name = Couper les SFX +setting.crashreport.name = Envoyer un rapport de crash anonyme +setting.chatopacity.name = Opacité du chat +setting.playerchat.name = Display In-Game Chat +keybind.title = Paramétrer les touches +category.general.name = Général +category.view.name = Voir +category.multiplayer.name = Multijoueur +command.attack = Attaque +command.retreat = Retraite +command.patrol = Patrouille +keybind.gridMode.name = Sélection des blocs +keybind.gridModeShift.name = Sélection des catégories +keybind.press = Appuyer sur une touche... +keybind.press.axis = Appuyer sur un axe ou une touche... +keybind.screenshot.name = Capture d'écran keybind.move_x.name = mouvement x keybind.move_y.name = mouvement y keybind.select.name = sélectionner -keybind.break.name = Pause -keybind.shoot.name = tirer -keybind.zoom_hold.name = tenir le zoom +keybind.diagonal_placement.name = Placement en diagonale +keybind.pick.name = Choisir un bloc +keybind.break_block.name = Suppprimer un bloc +keybind.deselect.name = Désélectionner +keybind.shoot.name = tirer +keybind.zoom_hold.name = maintenir le zoom keybind.zoom.name = zoom -keybind.block_info.name = bloc_info keybind.menu.name = menu keybind.pause.name = Pause -keybind.dash.name = attaque frontal +keybind.minimap.name = Minimap +keybind.dash.name = Courir keybind.chat.name = chat keybind.player_list.name = Liste des joueurs -keybind.console.name = console -keybind.rotate_alt.name = tourner_alt +keybind.console.name = console keybind.rotate.name = Tourner -keybind.weapon_1.name = Arme 1 -keybind.weapon_2.name = Arme 2 -keybind.weapon_3.name = Arme 3 -keybind.weapon_4.name = Arme 4 -keybind.weapon_5.name = Arme 5 -keybind.weapon_6.name = Arme 6 -mode.waves.name = Vagues +keybind.toggle_menus.name = Cacher/afficher les menus +keybind.chat_history_prev.name = remonter l'historique du chat +keybind.chat_history_next.name = descendre l'historique du chat +keybind.chat_scroll.name = Chat scroll +keybind.drop_unit.name = drop unit +keybind.zoom_minimap.name = Zoom minimap +mode.help.title = Description des modes de jeu +mode.survival.name = Survival +mode.survival.description = Le mode normal. Ressources limitées et vagues automatiques. mode.sandbox.name = bac à sable -mode.freebuild.name = construction libre -upgrade.standard.name = La norme -upgrade.standard.description = Le robot standard. -upgrade.blaster.name = blaster -upgrade.blaster.description = Tire faiblement et lentetement. -upgrade.triblaster.name = Tri-blaster -upgrade.triblaster.description = Tire 3 balles se propageant en V. -upgrade.clustergun.name = clustergun -upgrade.clustergun.description = Tire une portée de grenades explosives. -upgrade.beam.name = beam cannon -upgrade.beam.description = Tire un rayon laser de perçage à longue portée. -upgrade.vulcan.name = Vulcain -upgrade.vulcan.description = Tire un barrage de balles rapides. -upgrade.shockgun.name = shockgun -upgrade.shockgun.description = Tire une charge de balles qui explosent en éclats. -item.stone.name = pierre -item.iron.name = fer -item.coal.name = charbon -item.steel.name = Acier +mode.sandbox.description = Ressources infinies et pas de minuterie pour les vagues. +mode.pvp.name = JcJ +mode.pvp.description = Battez-vous contre d'autres joueurs en local. +mode.attack.name = Attaque +mode.attack.description = Pas de vagues, avec le but de détruire la base ennemie. +mode.custom = Règles personnalisées +rules.infiniteresources = Ressources infinies +rules.wavetimer = Minuterie pour les vagues +rules.waves = Vagues +rules.enemyCheat = Infinite AI Resources +rules.unitdrops = Unit Drops +rules.unitbuildspeedmultiplier = Unit Creation Speed Multiplier +rules.unithealthmultiplier = Unit Health Multiplier +rules.playerhealthmultiplier = Player Health Multiplier +rules.playerdamagemultiplier = Player Damage Multiplier +rules.unitdamagemultiplier = Unit Damage Multiplier +rules.enemycorebuildradius = Enemy Core No-Build Radius:[LIGHT_GRAY] (tiles) +rules.respawntime = Respawn Time:[LIGHT_GRAY] (sec) +rules.wavespacing = Wave Spacing:[LIGHT_GRAY] (sec) +rules.buildcostmultiplier = Build Cost Multiplier +rules.buildspeedmultiplier = Build Speed Multiplier +rules.waitForWaveToEnd = Waves wait for enemies +rules.dropzoneradius = Drop Zone Radius:[LIGHT_GRAY] (tiles) +rules.respawns = Max respawns per wave +rules.limitedRespawns = Limite de réapparition +rules.title.waves = Vagues +rules.title.respawns = Réapparitions +rules.title.resourcesbuilding = Resources & Building +rules.title.player = Joueurs +rules.title.enemy = Ennemis +rules.title.unit = Unités +content.item.name = Objets +content.liquid.name = Liquides +content.unit.name = Unités +content.block.name = Blocks +content.mech.name = Méchas +item.copper.name = Cuivre +item.copper.description = Un matériau de construction utile. Utilisé intensivement dans tout les blocs. +item.lead.name = Plomb +item.lead.description = Un matériau de départ. Utilisé intensivement en électronique et pour le transport de blocs. +item.coal.name = Charbon +item.coal.description = Un carburant commun et facile à obtenir. +item.graphite.name = Graphite item.titanium.name = Titane -item.dirium.name = dirium -item.uranium.name = uranium -item.sand.name = sable -liquid.water.name = eau -liquid.plasma.name = plasma -liquid.lava.name = lave -liquid.oil.name = pétrole -block.weaponfactory.name = usine d'armes -block.weaponfactory.fulldescription = Utilisé pour créer des armes pour le joueur robot. Cliquez pour utiliser. Extrait automatiquement les ressources du noyau. -block.air.name = air -block.blockpart.name = partie de block -block.deepwater.name = eaux profondes -block.water.name = eau -block.lava.name = lave -block.oil.name = pétrole -block.stone.name = pierre -block.blackstone.name = pierre noire -block.iron.name = fer -block.coal.name = charbon -block.titanium.name = titane -block.uranium.name = uranium -block.dirt.name = terre -block.sand.name = sable -block.ice.name = glace -block.snow.name = neige -block.grass.name = herbe -block.sandblock.name = bloc de sable -block.snowblock.name = bloc de neige -block.stoneblock.name = bloc de pierre -block.blackstoneblock.name = bloc de roche noire -block.grassblock.name = bloc d'herbe -block.mossblock.name = bloc de mousse -block.shrub.name = arbuste -block.rock.name = roche -block.icerock.name = roche de glace -block.blackrock.name = roche noire -block.dirtblock.name = bloc de terre -block.stonewall.name = mur de pierres -block.stonewall.fulldescription = Un bloc défensif bon marché. Utile pour protéger le noyau et les tourelles durant les premières vagues. -block.ironwall.name = mur de fer -block.ironwall.fulldescription = Un bloc défensif de base. Fournit une protection contre les ennemis. -block.steelwall.name = mur en acier -block.steelwall.fulldescription = Un bloc défensif standard. une protection adéquate contre les ennemis. -block.titaniumwall.name = mur de titane -block.titaniumwall.fulldescription = Un bloc défensif très résistant. Fournit une protection contre les ennemis. -block.duriumwall.name = mur de dirium -block.duriumwall.fulldescription = Un bloc défensif extrêmement résistant. Fournit une protection contre les ennemis. -block.compositewall.name = mur composite -block.steelwall-large.name = grand mur d'acier -block.steelwall-large.fulldescription = Un bloc défensif standard, prend plusieurs blocs de largeurs. -block.titaniumwall-large.name = grand mur de titane -block.titaniumwall-large.fulldescription = Un bloc défensif très résistant, prend plusieurs blocs de largeurs. -block.duriumwall-large.name = grand mur de dirium -block.duriumwall-large.fulldescription = Un bloc défensif extrêmement résistant, prend plusieurs blocs de largeurs. -block.titaniumshieldwall.name = mur blindé -block.titaniumshieldwall.fulldescription = Un bloc défensif solide, avec un bouclier intégré. Nécessite de l'énergie. Utilise l'énergie pour absorber les balles ennemies. Il est recommandé d'utiliser un distributeur d'énergie pour fournir de l'énergie à ce bloc. -block.repairturret.name = tourelle de réparation -block.repairturret.fulldescription = Répare les blocs avoisinants endommagés dans un zone circulaire à un rythme lent. Utilise de l'énergie. -block.megarepairturret.name = tourelle de réparation II -block.megarepairturret.fulldescription = Répare les blocs avoisinants endommagés dans un zone circulaire à un rythme régulier. Utilise de l'énergie. -block.shieldgenerator.name = générateur de bouclier -block.shieldgenerator.fulldescription = Un bloc défensif avancé. Protège tous les blocs avoisinants dans une zone circulaire. Utilise l'énergie à un rythme lent, mais draine beaucoup d'énergie au contact d'un tir ennemi. -block.door.name = porte -block.door.fulldescription = Un bloc qui peut être ouvert et fermé en cliquant dessus. -block.door-large.name = grande porte -block.door-large.fulldescription = Un bloc qui peut être ouvert et fermé en cliquant dessus. -block.conduit.name = conduit -block.conduit.fulldescription = Bloc de transport de liquide basique. Fonctionne comme un transporteur, mais avec des liquides. A placé à coté de pompes. Peut être utilisé comme un pont sur les liquides pour les ennemis et les joueurs. -block.pulseconduit.name = conduit à impulsion -block.pulseconduit.fulldescription = Bloc de transport de liquide avancé. Transporte les liquides plus rapidement et stocke plus que les conduits standards. -block.liquidrouter.name = routeur de liquide -block.liquidrouter.fulldescription = Fonctionne de manière similaire à un routeur. Accepte l'entrée de liquide d'un côté et l'envoie vers 3 autres côtés. Utile pour répartir le liquide d'un conduit vers plusieurs autres conduits. -block.conveyor.name = transporteur -block.conveyor.fulldescription = Bloc de transport standard. Déplace les objets vers l'avant et les dépose automatiquement dans les tourelles ou des blocs d'artisanats. Rotatif. Peut être utilisé comme une plateforme sur les liquides, pour les ennemis et les joueurs. -block.steelconveyor.name = transporteur en acier -block.steelconveyor.fulldescription = transporteur d'articles avancé. Déplace les objets plus rapidement que les transporteurs standards. -block.poweredconveyor.name = transporteur à impulsions -block.poweredconveyor.fulldescription = Le transporteur d'articles ultime. Déplace les articles plus rapidement que les convoyeurs en acier. -block.router.name = Routeur -block.router.fulldescription = Accepte les éléments d'une direction et les distribue dans 3 autres directions. Peut également stocker une certaine quantité d'articles. Utile pour diviser cette quantité afin d'approvisionner plusieurs tourelles ou des blocs d'artisanat. -block.junction.name = jonction -block.junction.fulldescription = Agit comme un pont pour deux transporteurs qui se croisent et qui possèdent différents articles. -block.conveyortunnel.name = tunnel de transport -block.conveyortunnel.fulldescription = Transporte des articles sous les blocs. Pour l'utiliser, placez les entre des blocs, au maximum deux. Assurez-vous que les deux tunnels sont orientés dans des directions opposées. -block.liquidjunction.name = jonction à liquide -block.liquidjunction.fulldescription = Agit comme un pont pour deux conduits. Utile dans la situations ou deux conduits se croisent et transportent différents liquides. -block.liquiditemjunction.name = jonction de liquide-article -block.liquiditemjunction.fulldescription = Agit comme un pont pour croiser les conduits et les convoyeurs. -block.powerbooster.name = distributeur d'énergie -block.powerbooster.fulldescription = Distribue la puissance à tous les blocs avoisinants dans une zone circulaire. -block.powerlaser.name = laser d'énergie -block.powerlaser.fulldescription = Crée un laser qui transmet la puissance au bloc en face de lui. Ne génère pas d'énergie par lui-même. Idéal avec des générateurs ou d'autres lasers. -block.powerlaserrouter.name = routeur laser -block.powerlaserrouter.fulldescription = Laser qui distribue la puissance à trois directions à la fois. Utile pour séparer l'énergie afin d'alimenter plusieurs blocs -block.powerlasercorner.name = laser en angle droit -block.powerlasercorner.fulldescription = Laser qui distribue la puissance à deux directions en angle droit. Utile pour séparer l'énergie afin d'alimenter plusieurs blocs. -block.teleporter.name = téléporteur -block.teleporter.fulldescription = Bloc de transport avancé. Les téléporteurs saisissent des articles aux autres téléporteurs de la même couleur. Ne fait rien si aucun téléporteur de la même couleur n'existe. Si plusieurs téléporteurs existent de la même couleur, un téléporteur aléatoire est sélectionné. Utilise de l'énergie. Cliquez pour changer de couleur. -block.sorter.name = trieur -block.sorter.fulldescription = Trie l'article par type de matériau. Le matériau à accepter est indiqué par la couleur du bloc. Tous les éléments qui correspondent au matériau de tri sont sortis vers l'avant, tout le reste est sorti à gauche et à droite. -block.core.name = noyau -block.pump.name = pompe -block.pump.fulldescription = Pompe les liquides provenant d'un bloc source - généralement de l'eau, de la lave ou de l'huile. Transmet le liquide dans les conduits voisins. -block.fluxpump.name = pompe à flux -block.fluxpump.fulldescription = Une version avancée de la pompe. Stocke plus et pompe le liquide plus rapidement que la pompe ordinaire. -block.smelter.name = fonderie -block.smelter.fulldescription = Le bloc d'artisanat essentiel. Produit de l'acier lorsqu'il est approvisionné en fer et charbon. Il est conseillé d'introduire le fer et le charbon par différents transporteurs pour éviter les situations de débordement. -block.crucible.name = Crucible -block.crucible.fulldescription = Un bloc d'artisanat avancé. Produit du diridium lorsqu'il est approvisionné en fer, en titanium et en charbon . Il est conseillé d'introduire ces minerais par différents transporteurs. -block.coalpurifier.name = raffinerie de charbon -block.coalpurifier.fulldescription = Un bloc d'artisanat de base. Produit du charbon lorsqu'il est fourni avec de grandes quantités d'eau et de pierre. -block.titaniumpurifier.name = raffinerie de titane -block.titaniumpurifier.fulldescription = Un bloc d'artisanat avancé. Produit du titane lorsqu'il est fourni avec de grandes quantités d'eau et de fer. -block.oilrefinery.name = raffinerie de pétrole -block.oilrefinery.fulldescription = Un bloc d'artisanat standard. Affine de grandes quantités de pétrole grâce au charbon. Utile pour alimenter les tourelles à charbon lorsque les filons de charbon sont rares. -block.stoneformer.name = raffinerie de pierre -block.stoneformer.fulldescription = Un bloc d'artisanat standard. Il purifie la lave en pierre. Utile pour produire des quantités massives de pierre. -block.lavasmelter.name = fonderie d'acier -block.lavasmelter.fulldescription = Un bloc d'artisanat de base. Utilise la lave pour convertir le fer en acier. Une alternative aux fonderies. Utile dans les situations où le charbon est rare. -block.stonedrill.name = extracteur de pierre -block.stonedrill.fulldescription = L'extracteur essentiel. Lorsqu'il est placé sur de la pierre, il l'extrait à un rythme rapide. -block.irondrill.name = extracteur de fer -block.irondrill.fulldescription = Un extracteur de base. Lorsqu'ils il est placé sur un minerai de charbon, il l'extrait à un rythme régulier. -block.coaldrill.name = extracteur de charbon -block.coaldrill.fulldescription = Un extracteur de base. Lorsqu'il est placé sur un minerai de charbon, il l'extrait à un rythme régulier. -block.uraniumdrill.name = extracteur d'uranium -block.uraniumdrill.fulldescription = Un extracteur avancé .Lorsqu'il est placé sur un minerai d'uranium, il l'extrait lentement. -block.titaniumdrill.name = extracteur de titane -block.titaniumdrill.fulldescription = Un extracteur avancé. Lorsqu'il est placé sur un minerai de titane, il l'extrait lentement. -block.omnidrill.name = omni-extracteur -block.omnidrill.fulldescription = L'extracteur ultime .Minera n'importe quel minerai sur lequel il est placé à un rythme rapide. -block.coalgenerator.name = générateur à charbon -block.coalgenerator.fulldescription = Le générateur essentiel. Génère de l'énergie à partir du charbon. eparpille l'énergie de ses 4 cotés. -block.thermalgenerator.name = générateur thermique -block.thermalgenerator.fulldescription = Génère de l'énergie à partir de la lave. éparpille l'énergie de ses 4 cotés. -block.combustiongenerator.name = générateur à combustion -block.combustiongenerator.fulldescription = Génère de l'énergie à partir du pétrole. éparpille l'énergie de ses 4 cotés. -block.rtgenerator.name = Générateur RTG -block.rtgenerator.fulldescription = Génère de petites quantités d'énergie à partir d'uranium. éparpille l'énergie de ses 4 cotés -block.nuclearreactor.name = réacteur nucléaire -block.nuclearreactor.fulldescription = Une version avancée du générateur RTG ,et le générateur d'énergie ultime. Génère de l'énergie à partir de l'uranium. Nécessite un refroidissement à eau constant. Hautement inflammable; explosera violemment si des quantités insuffisantes de liquide de refroidissement sont fournies. -block.turret.name = tourelle -block.turret.fulldescription = Une tourelle basique et bon marché. Utilise la pierre pour munition. A légèrement plus de portée que la double-tourelle. -block.doubleturret.name = tourelle double -block.doubleturret.fulldescription = Une version légèrement plus puissante de la tourelle. Utilise la pierre comme munition. Fait beaucoup plus de dégâts, mais a une portée inférieure. -block.machineturret.name = tourelle gattling -block.machineturret.fulldescription = Une tourelle complète standard. Utilise le fer pour munition. A une vitesse de tir rapide avec des dégâts décents. -block.shotgunturret.name = tourelle fusil à pompe. -block.shotgunturret.fulldescription = Une tourelle standard. Utilise le fer pour munition. Tire une portée de 7 balles. Portée basse, mais plus de dégâts que la tourelle gattling. -block.flameturret.name = tourelle incendiaire -block.flameturret.fulldescription = Tourelle avancée à courte portée. Utilise du charbon pour munition. A une portée très faible, mais des dégâts très élevés. Bon pour les places étroites. Recommandé pour tirer à travers les murs. -block.sniperturret.name = Tourelle laser -block.sniperturret.fulldescription = Tourelle avancée à longue portée. Utilise l'acier pour munition. Dommages très élevés, mais faible vitesse de tir. Chère, mais peut être placé loin des lignes ennemies en raison de sa portée. -block.mortarturret.name = Tourelle Antiaérienne -block.mortarturret.fulldescription = Tourelle avancée à fragmentation de faible précision. Utilise du charbon pour munition. Tire un barrage de balles qui explose en éclats. Utile pour les grandes foules d'ennemis. -block.laserturret.name = Tourelle Laser -block.laserturret.fulldescription = Tourelle à cible unique avancée. Utilise de l'énergie. Bonne tourelle polyvalente de moyenne portée. Une seule cible seulement. Ne manque jamais. -block.waveturret.name = Tourelle Tesla -block.waveturret.fulldescription = Tourelle multi-cible avancée. Utilise de l'énergie. Portée moyenne. Ne manque jamais sa cible. Peu de dégâts, mais peut frapper plusieurs ennemis simultanément avec sa répétition d'éclair. -block.plasmaturret.name = Tourelle à plasma -block.plasmaturret.fulldescription = Version très avancée de la tourelle incendiaire. Utilise le charbon comme munition. Dommages très élevés, de faible à moyenne portée. -block.chainturret.name = Tourelle à répétition. -block.chainturret.fulldescription = La tourelle ultime à tir rapide. Utilise l'uranium comme munition. Tire de grosses salves à une vitesse de feu élevée. Portée moyenne. Traverse plusieurs carreaux. Extrêmement résistante. -block.titancannon.name = Cannon Titan -block.titancannon.fulldescription = La tourelle à longue portée ultime. Utilise l'uranium comme munition. Tire de gros obus à dégâts de zone à une cadence de tir moyenne. Longue portée. occupe plusieurs blocks. Extrêmement dur. -block.playerspawn.name = point d'apparition joueur -block.enemyspawn.name = Point d'apparition ennemie +item.titanium.description = Un métal rare super-léger largement utilisé dans le transport de liquides et d'objets ainsi que dans les foreuses de haut-niveau et l'aviation. +item.thorium.name = Thorium +item.thorium.description = Un métal dense et radioactif utilisé comme support structurel et comme carburant nucléaire. +item.silicon.name = Silicone +item.silicon.description = Un matériau semi-conducteur extrêmement utile, avec des utilisations dans les panneaux solaires et dans beaucoup d'autre composants électroniques complexes. +item.plastanium.name = Plastanium +item.plastanium.description = Un matériau léger et docile utilisé dans l'aviation avancée et dans les munitions à fragmentation. +item.phase-fabric.name = Tissu phasé +item.phase-fabric.description = Une substance au poids quasiment inexistant utilisé pour l'électronique avancé et la technologie auto-réparatrice. +item.surge-alloy.name = Alliage superchargé +item.surge-alloy.description = Un alliage avancé avec des propriétés électriques avancées. +item.spore-pod.name = Spore Pod +item.spore-pod.description = Used for conversion into oil, explosives and fuel. +item.sand.name = Sable +item.sand.description = Un matériau commun utilisé largement dans la fonte, à la fois dans l'alliage et comme un flux. +item.blast-compound.name = Mélange explosif +item.blast-compound.description = Un composé volatile utilisé dans les bombes et les explosifs. Bien qu'il puisse être utilisé comme carburant, ce n'est pas conseillé. +item.pyratite.name = Pyratite +item.pyratite.description = Une substance extrêmement inflammable utilisée dans les armes incendiaires. +item.metaglass.name = Métavitre +item.metaglass.description = Un composé de vitre super-résistant. Utilisé largement pour le transport et le stockage de liquides. +item.scrap.name = Scrap +item.scrap.description = Leftover remnants of old structures and units. Contains trace amounts of many different metals. +liquid.water.name = Eau +liquid.slag.name = Slag +liquid.oil.name = Pétrole +liquid.cryofluid.name = Liquide cryogénique +mech.alpha-mech.name = Alpha +mech.alpha-mech.weapon = Fusil automatique +mech.alpha-mech.ability = Essaim de drone +mech.alpha-mech.description = Le mécha standard. À une vitesse et des dégâts décents; Il peut aussi créer jusqu'à 3 drones pour infliger des dégâts supplémentaires. +mech.delta-mech.name = Delta +mech.delta-mech.weapon = Arc électrique +mech.delta-mech.ability = Décharge +mech.delta-mech.description = Un mécha rapide, avec une armure légère, fait pour des tactiques de harcèlements. Il inflige, par contre, peu de dégâts aux structures, néanmoins il peut tuer de grand groupes d'ennemis très rapidement avec ses arcs électriques. +mech.tau-mech.name = Tau +mech.tau-mech.weapon = Laser restructurant +mech.tau-mech.ability = Explosion réparante +mech.tau-mech.description = Un mécha de support. Soigne les blocs alliés en tirant dessus. Il peut aussi éteindre les feux et soigner ses alliés en zone avec sa compétence. +mech.omega-mech.name = Omega +mech.omega-mech.weapon = Essaim de missiles auto-guidés +mech.omega-mech.ability = Armure +mech.omega-mech.description = Un mécha cuirassé et large fait pour les assauts frontaux. Sa compétence "Armure" lui permet de bloquer 90% des dégâts. +mech.dart-ship.name = Dard +mech.dart-ship.weapon = Pistolet automatique +mech.dart-ship.description = Le vaisseau standard. Raisonnablement rapide et léger. Il a néanmoins peu d'attaque et une faible vitesse de minage. +mech.javelin-ship.name = Javelin +mech.javelin-ship.description = Un vaisseau qui, bien que lent au départ, peut accélerer pour atteindre de très grandes vitesses et voler jusqu'aux avant-postes ennemis, faisant d'énormes dégâts avec ses arc électriques obtenus à vitesse maximum et ses missiles. +mech.javelin-ship.weapon = Missiles explosifs autoguidés +mech.javelin-ship.ability = Décharge de propulseur +mech.trident-ship.name = Trident +mech.trident-ship.description = Un bombardier lourd raisonnablement cuirassé. +mech.trident-ship.weapon = Largage de bombes +mech.glaive-ship.name = Glaive +mech.glaive-ship.description = Un grand vaisseau de combat cuirassé. Equipé avec un fusil automatique à munitions incendiaires. Il a aussi une bonne accéleration ainsi qu'une bonne vitesse maximale. +mech.glaive-ship.weapon = Fusil automatique incendiaire +item.explosiveness = [LIGHT_GRAY]Explosivité: {0} +item.flammability = [LIGHT_GRAY]Inflammabilité: {0} +item.radioactivity = [LIGHT_GRAY]Radioactivité: {0} +unit.health = [LIGHT_GRAY]Santé: {0} +unit.speed = [LIGHT_GRAY]Rapidité: {0} +mech.weapon = [LIGHT_GRAY]Arme: {0} +mech.health = [LIGHT_GRAY]Health: {0} +mech.itemcapacity = [LIGHT_GRAY]Capacité de stockage: {0} +mech.minespeed = [LIGHT_GRAY]Vitesse de minage: {0} +mech.minepower = [LIGHT_GRAY]Puissance du minage: {0} +mech.ability = [LIGHT_GRAY]Compétence: {0} +mech.buildspeed = [LIGHT_GRAY]Vitesse de construction: {0}% +liquid.heatcapacity = [LIGHT_GRAY]Capacité Thermique: {0} +liquid.viscosity = [LIGHT_GRAY]Viscosité: {0} +liquid.temperature = [LIGHT_GRAY]Température: {0} +block.grass.name = Herbe +block.salt.name = Sel +block.saltrocks.name = Roches de sel +block.pebbles.name = Cailloux +block.tendrils.name = Tendrils +block.sandrocks.name = Roches de sable +block.spore-pine.name = Spore Pine +block.sporerocks.name = Spore Rocks +block.rock.name = Roche +block.snowrock.name = Roches de neige +block.shale.name = Shale +block.shale-boulder.name = Shale Boulder +block.moss.name = Mousse +block.shrubs.name = Shrubs +block.spore-moss.name = Spore Moss +block.shalerocks.name = Shale Rocks +block.scrap-wall.name = Scrap Wall +block.scrap-wall-large.name = Large Scrap Wall +block.scrap-wall-huge.name = Huge Scrap Wall +block.scrap-wall-gigantic.name = Gigantic Scrap Wall +block.thruster.name = Thruster +block.kiln.name = Kiln +block.kiln.description = Smelts sand and lead into metaglass. Requires small amounts of power. +block.graphite-press.name = Graphite Press +block.multi-press.name = Multi-Press +block.constructing = {0}\n[LIGHT_GRAY](En Construction) +block.spawn.name = Générateur d'ennemis +block.core-shard.name = Core: Shard +block.core-foundation.name = Core: Foundation +block.core-nucleus.name = Core: Nucleus +block.deepwater.name = Eau profonde +block.water.name = Eau +block.tainted-water.name = Tainted Water +block.darksand-tainted-water.name = Dark Sand Tainted Water +block.tar.name = Pétrole +block.stone.name = Roche +block.sand.name = Sable +block.darksand.name = Sable sombre +block.ice.name = Glace +block.snow.name = Neige +block.craters.name = Cratères +block.sand-water.name = Sand water +block.darksand-water.name = Dark Sand Water +block.char.name = Char +block.holostone.name = Holo stone +block.ice-snow.name = Ice Snow +block.rocks.name = Roches +block.icerocks.name = Roches de glace +block.snowrocks.name = Roches de neige +block.dunerocks.name = Roches de dunes +block.pine.name = Pin +block.white-tree-dead.name = Arbre blanc mort +block.white-tree.name = Arbre blanc +block.spore-cluster.name = Spore Cluster +block.metal-floor.name = Plancher de métal +block.metal-floor-2.name = Plancher de métal 2 +block.metal-floor-3.name = Plancher de métal 3 +block.metal-floor-5.name = Plancher de métal 5 +block.metal-floor-damaged.name = Plancher de métal endommagé +block.dark-panel-1.name = Panneau sombre 1 +block.dark-panel-2.name = Panneau sombre 2 +block.dark-panel-3.name = Panneau sombre 3 +block.dark-panel-4.name = Panneau sombre 4 +block.dark-panel-5.name = Panneau sombre 5 +block.dark-panel-6.name = Panneau sombre 6 +block.dark-metal.name = Métal Sombre +block.ignarock.name = Roches ignées +block.hotrock.name = Hot Rock +block.magmarock.name = Magma Rock +block.cliffs.name = Falaises +block.copper-wall.name = Mur de cuivre +block.copper-wall-large.name = Grand mur de cuivre +block.titanium-wall.name = Mur de titane +block.titanium-wall-large.name = Grand mur de titane +block.phase-wall.name = Mur phasé +block.phase-wall-large.name = Grand mur phasé +block.thorium-wall.name = Mur en Thorium +block.thorium-wall-large.name = Mur en Thorium large +block.door.name = Porte +block.door-large.name = Grande porte +block.duo.name = Duo +block.scorch.name = Scorch +block.scatter.name = Scatter +block.hail.name = Hail +block.lancer.name = Lancier +block.conveyor.name = Convoyeur +block.titanium-conveyor.name = Convoyeur en titane +block.junction.name = Jonction +block.router.name = [accent]routeur[] +block.distributor.name = Distributeur +block.sorter.name = Trieur +block.sorter.description = Trie les articles. Si un article rcorrespond à la sélection, il peut passer. Autrement, l'article est distribué vers la gauche ou la droite. +block.overflow-gate.name = Barrière de Débordement +block.overflow-gate.description = C'est la combinaison entre un Routeur et un Diviseur qui peut seulement distribuer à gauche et à droite si le chemin de devant est bloqué. +block.silicon-smelter.name = Fonderie de Silicone +block.phase-weaver.name = Tisseur à Phase +block.pulverizer.name = Pulvérisateur +block.cryofluidmixer.name = Refroidisseur +block.melter.name = Four à Fusion +block.incinerator.name = Incinérateur +block.spore-press.name = Spore Press +block.separator.name = Séparateur +block.coal-centrifuge.name = Centrifuge à charbon +block.power-node.name = Transmetteur énergétique +block.power-node-large.name = Gros transmetteur énergétique +block.surge-tower.name = Surge Tower +block.battery.name = Batterie +block.battery-large.name = Grande batterie +block.combustion-generator.name = Générateur à combustion +block.turbine-generator.name = Générateur à Turbine +block.differential-generator.name = Differential Generator +block.impact-reactor.name = Réacteur à impact +block.mechanical-drill.name = Foreuse mécanique +block.pneumatic-drill.name = Foreuse à vérin +block.laser-drill.name = Foreuse Laser +block.water-extractor.name = Extracteur d'eau +block.cultivator.name = Cultivateur +block.dart-mech-pad.name = Dart Mech Pad +block.delta-mech-pad.name = Reconstructeur de mécha "Delta" +block.javelin-ship-pad.name = Reconstructeur de vaisseau "Javelin" +block.trident-ship-pad.name = Reconstructeur de vaisseau "Trident" +block.glaive-ship-pad.name = Reconstructeur de vaisseau "Glaive +block.omega-mech-pad.name = Reconstructeur de mécha "Oméga" +block.tau-mech-pad.name = Reconstructeur de mécha "Tau" +block.conduit.name = Conduit +block.mechanical-pump.name = Pompe mécanique +block.item-source.name = Source d'objets +block.item-void.name = Destructeur d'objets +block.liquid-source.name = Source de liquide +block.power-void.name = absorbeur énergétique +block.power-source.name = Puissance infinie +block.unloader.name = Déchargeur +block.vault.name = Coffre-Fort +block.wave.name = Vague +block.swarmer.name = Essaim +block.salvo.name = Salve +block.ripple.name = Ripple +block.phase-conveyor.name = Convoyeur phasé +block.bridge-conveyor.name = Pont +block.plastanium-compressor.name = Compresseur de Plastanium +block.pyratite-mixer.name = Mixeur à Pyratite +block.blast-mixer.name = Mixeur à Explosion +block.solar-panel.name = Panneau Solaire +block.solar-panel-large.name = Grand Panneau Solaire +block.oil-extractor.name = Extracteur d'huile +block.spirit-factory.name = Usine de "Drones spirituels" +block.phantom-factory.name = Usine de "Drones fantômes" +block.wraith-factory.name = Usine de "Combattants spectraux" +block.ghoul-factory.name = Usine de "Bombardiers goules" +block.dagger-factory.name = Usine de "Poignards" +block.crawler-factory.name = Crawler Mech Factory +block.titan-factory.name = Usine de "Titans" +block.fortress-factory.name = Usine de "Forteresse" +block.revenant-factory.name = Usine de "Revenants" +block.repair-point.name = Point de Réparation +block.pulse-conduit.name = Conduit à Impulsion +block.phase-conduit.name = Conduit à Phase +block.liquid-router.name = Routeur de Liquide +block.liquid-tank.name = Réservoir à Liquide +block.liquid-junction.name = Jonction à Liquide +block.bridge-conduit.name = Pont à liquide +block.rotary-pump.name = Pompe Rotative +block.thorium-reactor.name = Réacteur à Thorium +block.mass-driver.name = Transporteur de masse +block.blast-drill.name = Foreuse à explosion +block.thermal-pump.name = Pompe thermique +block.thermal-generator.name = Générateur thermique +block.alloy-smelter.name = Fonderie d'alliage superchargé +block.mender.name = Mender +block.mend-projector.name = Projecteur soignant +block.surge-wall.name = Mur superchargé +block.surge-wall-large.name = Grand mur superchargé +block.cyclone.name = Cyclone +block.fuse.name = Fuse +block.shock-mine.name = Mines +block.overdrive-projector.name = Projecteur accélérant +block.force-projector.name = Projecteur de champ de force +block.arc.name = Arc +block.rtg-generator.name = G.T.R. +block.spectre.name = Spectre +block.meltdown.name = Meltdown +block.container.name = Conteneur +block.launch-pad.name = Plateforme de lancement +block.launch-pad.description = Launches batches of items without any need for a core launch. Unfinished. +block.launch-pad-large.name = Grande plateforme de lancement +team.blue.name = Bleu +team.red.name = Rouge +team.orange.name = Orange +team.none.name = Gris +team.green.name = Vert +team.purple.name = Violet +unit.spirit.name = Drone spirituel +unit.spirit.description = L'unité de soutien de départ. Apparaît dans la base par défaut. Mine automatiquement les minerais, récupère les objets au sol et répare les blocs. +unit.phantom.name = Drone Fantôme +unit.phantom.description = Une unité de soutien avancée. Mine automatiquement les minerais, récupère les objets au sol et répare les blocs. Bien plus efficace qu'un drone spirituel. +unit.dagger.name = Poignard +unit.dagger.description = Une unité terrestre basiquee. Utile en armée. +unit.crawler.name = Crawler +unit.titan.name = Titan +unit.titan.description = Une unité terrestre cuirassée avancée. Attaque les unités terrestres comme aériennes. +unit.ghoul.name = Bombardier goule +unit.ghoul.description = Un bombardier lourd . Utilise de la pyratite ou des explosifs comme munitions. +unit.wraith.name = Combattant spectral +unit.wraith.description = Une unité volante rapide harcelant les ennemis .Utilise du plomb comme munitions. +unit.fortress.name = Forteresse +unit.fortress.description = Une unité terrestre d'artillerie lourde . +unit.revenant.name = Revenant +unit.eruptor.name = Eruptor +unit.chaos-array.name = Chaos Array +unit.eradicator.name = Eradicator +unit.lich.name = Lich +unit.reaper.name = Reaper +tutorial.begin = Votre mission, si vous l'acceptez est de détruire [LIGHT_GRAY]l'ennemi[].\n\nCommencez par [accent] miner du cuivre[]. Appuyer sur un filon de cuivre proche de votre base pour faire ceci. +tutorial.drill = Le minage manuel est inefficace.\n[accent]Des foreuses[]peuvent miner automatiquement.\nPlacez-en une sur un filon de cuivre. +tutorial.conveyor = [accent]Les Tapis roulants[] sont utilisés pour transporter des objets jusqu'à la base.\nFaites une ligne de tapis roulants de la foreuse à la base . +tutorial.morecopper = Plus de cuivre est demandé .\n\nRécupérez le soit manuellement soit construisez plus de foreuses. +tutorial.turret = Des constructions défensives doivent être construites pour repousser [LIGHT_GRAY]les ennemis[].Construisez une tourelle "duo" près de votre base. +tutorial.drillturret = Les tourelles "Duo" ont besoin de [accent]munitions en cuivre[] pour tirer.\nPlacez une foreuse à côté de la tourelle pour l'approvisionner avec du cuivre. +tutorial.waves = Les [LIGHT_GRAY]ennemies[] approchent.\n\nDéfendez votre base pour 2 vagues. Construisez plus de tourelles. +tutorial.lead = Plus de minerais sont forables. Explorez et minez du[accent] Plomb[].\n\nRamenez votre unité à la base pour transférer les ressources. +tutorial.smelter = Le cuivre et le plomb sont des métaux fragiles.\nUn alliage de qualité supérieure peut être créé dans une fonderie, l'[accent] alliage lourd [].\n\n Construisez-en un. +tutorial.densealloy = La fonderie va maintenant produire de l'alliage lourd.\nObtenez-en .\nVous pouvez aussi améliorer la production si nécessaire . +tutorial.siliconsmelter = La base va maintenant créer un[accent] drone spirituel[] pour miner et réparer les blocs.\n\nDes usines pour faire d'autres unités peuvent être faites avec du [accent] silicone.\nFaites une fonderie de silicone . +tutorial.silicondrill = Faire du silicone demande [accent] du charbon[] et[accent] du sable [].\nCommencez par construire des foreuses . +tutorial.generator = Cette technologie requiert de l'énergie pour fonctionner.\nFaites un [accent]générateur à combustion[] pour en produire. +tutorial.generatordrill = Les générateurs à combustion ont besoin de carburant.\n Donnez-lui du charbon comme carburant avec une foreuse. +tutorial.node = L'énergie doit être transportée .\nCréez un [accent] Transmetteur energétique[] à côté de votre générateur à combustion pour transférer son énergie. +tutorial.nodelink = L'énergie peut être transféré à l'aide de blocs utilisant de l'énergie ou des générateurs, ou encore par des Transmetteurs énergétiques reliés.\n\nReliez des transmetteurs en appuyant dessus puis en sélectionnant le générateur et la fonderie de silicone. +tutorial.silicon = Du silicone est maintenant produit. Obtenez-en.\n\nAugmenter la production est recommandé. +tutorial.daggerfactory = Construire [accent]une usine de "Poignards" []est recommandé .\n\nElle sera utilisée pour produire des unités d'attaque. +tutorial.router = Les usines ont besoin de ressources pour fonctionner.\nCréez un routeur pour séparer les objets. +tutorial.dagger = Reliez des transmetteurs énergétiques à l'usine.\nUne fois que les conditions seront remplies , un mécha sera créé.\nConstruisez autant de foreuses, de générateurs et de tapis roulants que nécessaire. +tutorial.battle = [LIGHT_GRAY]L'Ennemi[] a révélé sa base .\nDétruisez la avec votre unité et des méchas "Poignard". +block.copper-wall.description = Un bloc défensif à faible coût.\nUtile pour protéger la base et les tourelles dans les premières lors des premières vagues. +block.copper-wall-large.description = Un bloc défensif à faible coût.\nUtile pour protéger la base et les tourelles dans les premières lors des premières vagues.\nFait du 2 sur 2. +block.thorium-wall.description = Un bloc défensif puissant.\nProcure une très bonne protection contre les ennemis. +block.thorium-wall-large.description = Un bloc défensif puissant.\nProcure une très bonne protection contre les ennemis.\nFait du 2 sur 2. +block.phase-wall.description = Moins puissant qu'un mur en Thorium mais déviera les balles sauf si elles sont trop puissantes. +block.phase-wall-large.description = Moins puissant qu'un mur en Thorium mais déviera les balles sauf si elles sont trop puissantes.\nFait du 2 sur 2. +block.surge-wall.description = Le plus puissant bloc défensif .\nA une faible chance de créer des éclairs vers les ennemis . +block.surge-wall-large.description = Le plus puissant bloc défensif .\nA une faible chance de créer des éclairs vers les ennemis .\nFait du 2 sur 2. +block.door.description = Une petite porte pouvant être ouverte et fermée en appuyant dessus.\nSi elle est ouverte les ennemis peuvent tirer et passer à travers. +block.door-large.description = Une large porte pouvant être ouverte et fermée en appuyant dessus.\nSi elle est ouverte les ennemis peuvent tirer et passer à travers.\nFait du 2 sur 2. +block.mend-projector.description = Soigne périodiquement les batiments autour de lui. +block.overdrive-projector.description = Accélère les batiments autour de lui, notamment les foreuses et les convoyeurs. +block.force-projector.description = Crée un champ de force hexagonal autour de lui qui protège les batiments et les unités à l'intérieur de prendre des dégâts à cause des balles. +block.shock-mine.description = Blesse les ennemis qui marchent dessus. Quasiment invisble pour l'ennemi. +block.duo.description = une petite tourelle avec un coût faible . +block.scatter.description = A medium-sized anti-air turret. Sprays clumps of lead or scrap flak at enemy units. +block.arc.description = une petite tourelle tirant des arcs électrques vers les ennemis. +block.hail.description = une petite tourelle d'artillerie. +block.lancer.description = une tourelle de taille moyenne tirant des rayons chargés en électricité. +block.wave.description = une tourelle de taille moyenne tirant rapidement des bulles de liquide . +block.salvo.description = une tourelle de taille moyenne qui tire par salves. +block.swarmer.description = une tourelle de taille moyenne qui tire des missiles qui se dispersent. +block.ripple.description = Une grande tourelle d'artillerie qui tire plusieurs tirs simultanément. +block.cyclone.description = Une grande tourelle tirant rapidement ... très rapidement . +block.fuse.description = Une grande tourelle qui tire de puissants rayons lasers avec une faible portée. +block.spectre.description = Une grande tourelle qui tire deux puissantes balles simultanément. +block.meltdown.description = Une grande tourelle tirant de puissants rayons lasers avec une grande portée. +block.conveyor.description = Convoyeur basique servant à transporter des objets. Les objets déplacés en avant sont automatiquement déposés dans les tourelles ou les batiments. Peut être tourné. +block.titanium-conveyor.description = Convoyeur avancé . Déplace les objets plus rapidement que les convoyeurs standards. +block.phase-conveyor.description = convoyeur très avancé . Utilise de l'énergie pour téléporter des objets à un convoyeur phasé connecté jusqu'à une longue distance . +block.junction.description = Agit comme un pont pour deux ligne de convoyeurs se croisant. Utile lorsque deux différents convoyeurs déplacent différents matériaux à différents endroits. +block.mass-driver.description = Batiment de transport d'objet [accent]ultime[]. Collecte un grand nombre d'objets puis les tire à un autre transporteur de masse sur une très longue distance. +block.silicon-smelter.description = Utilise du sable, du charbon et de l'énergie afin de produire du silicone. +block.plastanium-compressor.description = Produit du plastanium à partir de pétrole et de titane. +block.phase-weaver.description = Produit du tissu phasé à partir de thorium et de grandes quantités de sable. +block.alloy-smelter.description = Produit un alliage superchargé à l'aide de titane de plomb de silicone et de cuivre. +block.pulverizer.description = Écrase la pierre pour en faire du sable. Utile quand il y a un manque de sable naturel. +block.pyratite-mixer.description = Mélange charbon, plomb et sable en l'hautement inflammable pyratite. +block.blast-mixer.description = Utilise du pétrole pour transformer la pyratite en un mélange explosif moins inflammable mais plus explosif que la pyratite. +block.cryofluidmixer.description = Combine de l'eau et du titane en un liquide cryogénique bien plus efficace pour refroidir. +block.melter.description = chauffe de la pierre à de très hautes températures pour obtenir de la lave. +block.incinerator.description = Permet de se débarasser de n'importe quel objet ou liquide en exces . +block.spore-press.description = Compresses spore pods into oil. +block.separator.description = Expose la pierre à de l'eau sous pression afin d'obtenir différents minéraux contenus dansla pierre. +block.power-node.description = Transmet l'énergie aux transmetteurs énergétiques connectés . Jusqu'à quatre sources d'énergie, consommateurs ou transmetteurs peuvent être connectés. Le transmetteur recevra de l'énergie ou le transmettra à n'importe quel batiment adjacent. +block.power-node-large.description = A un plus grand rayon que le transmetteur énergétique standard et jusqu'à six sources d'énergie, consommateurs ou transmetteurs peuvent être connectés. +block.battery.description = Stocke l'énergie quand elle est en abondance et le distribue si il y a trop peu d'énergie tant qu'il lui reste de l'énergie. +block.battery-large.description = Stocke bien plus d'énergie qu'une batterie normale. +block.combustion-generator.description = Génère de l'énergie en brûlant du pétrole ou des matériaux inflammables. +block.turbine-generator.description = Plus efficace qu'un générateur à combustion, mais requiert de l'eau . +block.thermal-generator.description = Génère une grande quantité d'énergie à partir de lave . +block.solar-panel.description = Génère une faible quantité d'énergie . +block.solar-panel-large.description = Génère bien plus d'énergie qu'un panneau solaire standard, Mais est aussi bien plus cher à construire. +block.thorium-reactor.description = Génère énormément d'énergie à l'aide de la radioactivité du thorium. Requiert néanmoins un refroidissement constant. Explosera violemment en cas de surchauffe. +block.rtg-generator.description = Un générateur thermo-électrique à radioisotope qui ne demande pas de refroidissement mais produit moins d'énergie qu'un réacteur à Thorium. +block.unloader.description = Décharge des objets depuis des conteneurs, coffres-forts ou de la base sur un convoyeur ou directement dans un bloc adjacent . Le type d'objet peut être changé en appuyant sur le déchargeur. +block.container.description = Stocke un petit nombre d'objet . Utile pour réguler le flux d'objet quand la demande de matériaux est inconstante.un [LIGHT_GRAY] déchargeur[] peut être utilisé pour récupérer des objets depuis le conteneur. +block.vault.description = Stocke un grand nombre d'objets. Utile pour réguler le flux d'objet quand la demande de matériaux est inconstante.un [LIGHT_GRAY] déchargeur[] peut être utilisé pour récupérer des objets depuis le coffre-fort. +block.mechanical-drill.description = Une foreuse de faible coût. Si elle est placée sur à un endroit approprié, produit des matériaux lentement à l'infini. +block.pneumatic-drill.description = Une foreuse amélioré plus rapide et capable de forer des matériaux plus dur grâce à l'usage de vérins à air comprimé. +block.laser-drill.description = Permet de forer bien plus vite grâce à la technologie laser, cela demande néanmoins de l'énergie . Additionnellement, le thorium, un matériau radioactif, peut-être récupéré avec cette foreuse. +block.blast-drill.description = La Foreuse ultime . Demande une grande quantité d'énergie . +block.water-extractor.description = Extrait l'eau des nappes phréatiques. Utile quand il n'y a pas d'eau à proximité. +block.cultivator.description = Cultive le sol avec de l'eau afin d'obtenir de la biomasse. +block.oil-extractor.description = Utilise une grande quantité d'énergie afin d'extraire du pétrole du sable . Utile quand il n'y a pas de lacs de pétrole à proximité. +block.trident-ship-pad.description = Quitte ton mécha ou ton vaisseau actuel pour un bombardier lourd raisonnablement cuirassé .\nUtilisez le reconstructeur en double cliquant dessus lorsque vous êtes dessus. +block.javelin-ship-pad.description = Quitte ton mécha ou ton vaisseau actuel pour un intercepteur rapide et puissant avec des armes électriques.\nUtilisez le reconstructeur en double cliquant dessus lorsque vous êtes dessus. +block.glaive-ship-pad.description = Quitte ton mécha ou ton vaisseau actuel pour un large vaisseau cuirassé .\nUtilisez le reconstructeur en double cliquant dessus lorsque vous êtes dessus. +block.tau-mech-pad.description = Quitte ton mécha ou ton vaisseau actuel pour un mécha de support qui peut soigner les batiments et unités alliées.\nUtilisez le reconstructeur en double cliquant dessus lorsque vous êtes dessus. +block.delta-mech-pad.description = Quitte ton mécha ou ton vaisseau actuel pour un mécha rapide mais peu résistant fait pour les stratégies de harcèlement.\nUtilisez le reconstructeur en double cliquant dessus lorsque vous êtes dessus. +block.omega-mech-pad.description = Quitte ton mécha ou ton vaisseau actuel pour un mécha cuirassé et large, fait pour les assauts frontaux .\nUtilisez le reconstructeur en double cliquant dessus lorsque vous êtes dessus. +block.spirit-factory.description = Produit des petits drones qui réparent les batiments et minent des matériaux. +block.phantom-factory.description = Produit des drones avancés qui sont bien plus efficaces que les drones spirituels. +block.wraith-factory.description = Produit des intercepteurs rapides qui harcèlent l'ennemi. +block.ghoul-factory.description = Produit des bombardiers lourds. +block.dagger-factory.description = Produit des unités terrestres basiques. +block.titan-factory.description = Produit des unités terrestres avancées et cuirassées. +block.fortress-factory.description = Produit des unités terrestres d'artillerie lourde . +block.revenant-factory.description = Produit des unités terrestres lourdes avec des lasers. +block.repair-point.description = Soigne en continu l'unité blessée la plus proche tant qu'elle est à sa portée. +block.conduit.description = tuyau basique permettant le transport de liquide . Marche comme un convoyeur mais avec les liquides. Utile si utilisé avec des extracteurs, des pompes ou d'autres conduits. +block.pulse-conduit.description = tuyau avancé permettant le transport de liquide . Transporte les liquides plus rapidement et en stocke plus que les tuyaux standards. +block.phase-conduit.description = tuyau très avancé permettant le transport de liquide. Utilise de l'énergie pour téléporter les liquides à un autre tuyau phasé sur une longue distance. +block.liquid-router.description = Accepte les liquide en une direction et les rejete de tout les côtés équitablement. Peut aussi stocker une certaine quantité de liquide. Utile pour envoyer un liquide à plusieurs endroits. +block.liquid-tank.description = Stocke une grande quantité de liquides . Utile pour réguler la sortie quand la demande est inconstante ou comme sécurité pour refroidir des batiments important. +block.liquid-junction.description = Agit comme une intersection pour deux conduits se croisant.Utile si deux conduits amènent différents liquides à différents endroits. +block.bridge-conduit.description = bloc de transport de liquide avancé . Permet le transport de liquides jusqu'à 3 blocs de n'importe quel terrain ou batiment . +block.mechanical-pump.description = Une pompe de faible prix pompant lentement, mais ne consomme pas d'énergie. +block.rotary-pump.description = Une pompe avancée qui double sa vitesse en utilisant de l'énergie. +block.thermal-pump.description = La pompe ultime . Trois fois plus rapide qu'une pompe mécanique et la seule pompe capable de récupérer de la lave. +block.router.description = Accepte les objets depuis une ou plus directions et le renvoie dans n'importe quelle direction. Utile pour séparer une chaîne de convoyeurs en plusieurs.[accent]Le seul et l'Unique[] +block.distributor.description = Un routeur avancé qui sépare les objets jusqu'à 7 autres directions équitablement. +block.bridge-conveyor.description = bloc de transport avancé permettant de traverser jusqu'à 3 blocs de n'importe quel terrain ou batiment. +block.item-source.description = Produit des objets à l'infini. Bac à sable uniquement . +block.liquid-source.description = Source de liquide infinie . Bac à sable uniquement. +block.item-void.description = Désintègre n'importe quel objet qui va à l'intérieur sans utiliser d'énergie. Bac à sable uniquement. +block.power-source.description = Produit de l'énergie à l'infini. Bac à sable uniquement. +block.power-void.description = Supprime toute l'énergie allant à l'intérieur.Bac à sable uniquement +liquid.water.description = Couramment utilisé pour le refroidissement et le traitement des déchets. +liquid.oil.description = Peut être brûlé, utilisé comme explosif ou comme liquide de refroidissement. +liquid.cryofluid.description = Le liquide de refroidissement le plus efficace. diff --git a/core/assets/bundles/bundle_fr_BE.properties b/core/assets/bundles/bundle_fr_BE.properties new file mode 100644 index 0000000000..b700208253 --- /dev/null +++ b/core/assets/bundles/bundle_fr_BE.properties @@ -0,0 +1,947 @@ +credits.text = Créé par [ROYAL]Anuken[] - [SKY]anukendev@gmail.com[] +credits = Crédits +contributors = Traducteurs et contributeurs +discord = Rejoignez le discord de Mindustry ! +link.discord.description = Le discord officiel de Mindustry +link.github.description = Code source du jeu +link.dev-builds.description = Versions instables de développement +link.trello.description = Planning Trello officiel pour les fonctionnalités planifiées. +link.itch.io.description = Page web itch.io avec les versions ordinateurs téléchargeables et la version web +link.google-play.description = Page Google Play du jeu +link.wiki.description = Wiki officiel de Mindustry +linkfail = L'ouverture du lien a échoué!\nL'URL a été copiée dans votre presse-papier. +screenshot = Capture d'écran enregistrée sur {0} +screenshot.invalid = Carte trop grande, potentiellement pas assez de mémoire pour la capture d'écran. +gameover = Le base a été détruite. +gameover.pvp = L'équipe[accent] {0}[] a gagnée ! +highscore = [accent]Nouveau meilleur score ! +stat.wave = Vagues vaincues:[accent] {0} +stat.enemiesDestroyed = Ennemies détruits:[accent] {0} +stat.built = Bâtiments construits:[accent] {0} +stat.destroyed = Bâtiments détruits:[accent] {0} +stat.deconstructed = Bâtiments déconstruits:[accent] {0} +stat.delivered = Ressources transférées: +stat.rank = FRang Final: [accent]{0} +placeline = Tu as sélectionné un bloc.\nTu peux les[accent] placer en rangée[] en[accent] maintenant ton doigt sur l'écran pendant quelque secondes[] et en le glissant vers n'importe qu'elle direction.\nEssaye! +removearea = Tu as sélectionné le mode de suppression.\nTu peux[accent] supprimer les blocs en rectangle[] en[accent] maintenant ton doigt sur l'écran pendant quelques secondes[] et en le glissant.\nEssaye! +launcheditems = [accent]Ressources transférées +map.delete = Êtes-vous sûr de vouloir supprimer cette carte ?"[accent]{0}[]"? +level.highscore = Meilleur score: [accent]{0} +level.select = Sélection de niveau +level.mode = Mode de jeu: +showagain = Ne plus montrer la prochaine fois. +coreattack = +nearpoint = [[ [scarlet]QUITTEZ LE POINT D'APPARITION ENNEMI IMMÉDIATEMENT[] ]\nannihilation imminente +outofbounds = [[ HORS LIMITES ]\n[]auto-destruction dans {0} +database = Base de données +savegame = Sauvegarder la partie +loadgame = Charger la partie +joingame = Rejoindre la partie +addplayers = Ajouter/Enlever des joueurs +customgame = Partie personnalisée +newgame = Nouvelle partie +none = +minimap = Minimap +close = Fermer +quit = Quitter +maps = Cartes +continue = Continue +maps.none = [LIGHT_GRAY]Aucune carte trouvée! +about.button = À propos +name = Nom: +noname = Choisissez d'abord [accent]un pseudo[]. +filename = Nom du fichier: +unlocked = Nouveau bloc debloqué! +completed = [accent]Terminé +techtree = Arbre technologique +research.list = [LIGHT_GRAY]Recherche: +research = Recherche +researched = [LIGHT_GRAY]{0} recherchée. +players = {0} joueurs en ligne +players.single = {0} joueur en ligne +server.closing = [accent]Fermeture du serveur ... +server.kicked.kick = Vous avez été expulsé du serveur ! +server.kicked.serverClose = Serveur fermé. +server.kicked.clientOutdated = Client dépassé! Mettez à jour votre jeu ! +server.kicked.serverOutdated = Serveur dépassé! Demandez à l'hôte de le mettre à jour ! +server.kicked.banned = Vous êtes banni de ce serveur. +server.kicked.recentKick = Vous avez été expulsé récemment.\nAttendez avant de vous connecter à nouveau. +server.kicked.nameInUse = Il y a déjà quelqu'un avec ce nom\nsur ce serveur. +server.kicked.nameEmpty = Votre nom doit contenir au moins une lettre ou un chiffre. +server.kicked.idInUse = Vous êtes déjà sur ce serveur ! Se connecter avec deux comptes n'est pas permis ! +server.kicked.customClient = Ce serveur ne supporte pas les versions personnalisées (Custom builds). Télécharger une version officielle. +server.kicked.gameover = Vous avez perdu ! +host.info = Le bouton [accent]héberger[] héberge un serveur sur les ports [scarlet]6567[] et [scarlet]6568.[]\nN'importe qui sur le même [LIGHT_GRAY]réseau wifi ou local[] devrait pouvoir voir votre serveur dans sa liste de serveurs.\n\nSi vous voulez que les gens puissent se connecter de n'importe où grâce à l'IP, [accent]rediriger les ports[] est requis.\n\n[LIGHT_GRAY]Note:Si quelqu'un éprouve des difficultés à se connecter à votre partie LAN, assurez-vous que vous avez autorisé Mindustry à accéder à votre réseau local dans les paramètres de votre pare-feu. +join.info = Ici, vous pouvez entrer l' [accent]IP d'un serveur[] pour s'y connecter, ou découvrir les serveurs[accent]sur votre réseau local[] pour s'y connecter.\nLes parties multijoueur LAN et WAN sont toutes deux supportées.\n\n[LIGHT_GRAY]Note: Aucune liste globale des serveurs n'est génerée automatiquement: si vous voulez vous connecter à un serveur par IP, vous devrez demander l'IP à l'hébergeur. +hostserver = Héberger un serveur +hostserver.mobile = Héberger\nUne partie +host = Héberger +hosting = [accent]Ouverture du serveur ... +hosts.refresh = Actualiser +hosts.discovering = Recherche de parties en LAN +server.refreshing = Actualisation du serveur +hosts.none = [lightgray]Aucun jeu en LAN trouvé ! +host.invalid = [scarlet]Impossible de se\nconnecter à l'hôte. +trace = Suivre le joueur +trace.playername = Nom du joueur: [accent]{0} +trace.ip = IP: [accent]{0} +trace.id = ID Unique: [accent]{0} +trace.mobile = Mobile Client: [accent]{0} +trace.modclient = Client personnalisé: [accent]{0} +invalidid = ID client invalide ! Soumettre un rapport de bug +server.bans = Bannis +server.bans.none = Aucun joueurs bannis trouvés ! +server.admins = Administrateurs +server.admins.none = Aucun administrateurs trouvé ! +server.add = Ajouter un serveur +server.delete = Êtes-vous sûr de vouloir supprimer ce serveur ? +server.hostname = Hôte: {0} +server.edit = Modifier le serveur +server.outdated = [crimson]Serveur obsolète ![] +server.outdated.client = [crimson]Client obsolète ![] +server.version = [lightgray]Version: {0} {1} +server.custombuild = [yellow]Version personnalisée +confirmban = Êtes-vous sûr de vouloir bannir ce joueur ? +confirmkick = Êtes-vous sûr de vouloir expulser ce joueur? +confirmunban = Êtes-vous sûr de vouloir annuler le ban de ce joueur ? +confirmadmin = Êtes-vous sûr de vouloir faire de ce joueur un administrateur ? +confirmunadmin = Êtes-vous sûr de vouloir supprimer le statut d'administrateur de ce joueur ? +joingame.title = Rejoindre une partie +joingame.ip = IP: +disconnect = Déconnecté. +disconnect.data = Les données du monde n'ont pas pu être chargées ! +connecting = [accent]Connexion... +connecting.data = [accent]Chargement des données du monde... +server.port = Port: +server.addressinuse = Adresse déjà utilisée ! +server.invalidport = Numéro de port incorrect ! +server.error = [crimson]Erreur lors de l'hébergement du serveur: [accent]{0} +save.old = Cette sauvegarde correspond à une ancienne version du jeu et ne peut donc plus être utilisée.\n\n[LIGHT_GRAY]La rétrocompatibilité des sauvegardes va être implémentée dans la version finale de la 4.0. +save.new = Nouvelle sauvegarde +save.overwrite = Êtes-vous sûr de vouloir\nécraser cette sauvegarde ? +overwrite = Écraser +save.none = Aucune sauvegarde trouvée ! +saveload = [accent]Sauvegarde... +savefail = Échec de la sauvegarde ! +save.delete.confirm = Êtes-vous sûr de supprimer cette sauvegarde ? +save.delete = Supprimer +save.export = Exporter une\nSauvegarde +save.import.invalid = [accent]Cette sauvegarde est invalide! +save.import.fail = [crimson]L'importation de la sauvegarde\na échouée: [accent]{0} +save.export.fail = [crimson]L'exportation de la sauvegarde\na échouée: [accent]{0} +save.import = Importer une sauvegarde +save.newslot = Nom de la sauvegarde: +save.rename = Renommer +save.rename.text = Nouveau nom: +selectslot = Sélectionnez une sauvegarde. +slot = [accent]Emplacement {0} +save.corrupted = [accent]Fichier de sauvegarde corrompu ou invalide!\nSi vous venez de mettre à jour votre jeu, c'est probablement dû à un changement du format de sauvegarde et [scarlet]non[] un bug. +empty = +on = Allumer +off = Éteint +save.autosave = Sauvegarde automatique {0} +save.map = Carte: {0} +save.wave = Vague {0} +save.difficulty = Difficulté: {0} +save.date = Dernière sauvegarde: {0} +save.playtime = Temps de jeu: {0} +warning = Avertissement. +confirm = Confirmer +delete = Supprimer +ok = OK +open = Ouvrir +customize = Personnaliser +cancel = Annuler +openlink = Ouvrir le lien +copylink = Copier le lien +back = Retour +quit.confirm = Êtes-vous sûr de vouloir quitter? +changelog.title = Notes de mise à jour +changelog.loading = Récupération des notes de mise à jour... +changelog.error.android = [accent]Notez que les notes de mise à jour ne marchent pas, certaines fois, sur Android 4.4 et versions antérieures!\nCeci est dû à un bug interne à Android. +changelog.error.ios = [accent]Les notes de mise à jour ne sont actuellement pas supportée sur IOS. +changelog.error = [scarlet]Erreur lors de la récupération des notes de mises à jour!\nVérifiez votre connexion internet. +changelog.current = [yellow][[Version actuelle] +changelog.latest = [accent][[Dernière version] +loading = [accent]Chargement... +saving = [accent]Sauvegarde... +wave = [accent]Vague {0} +wave.waiting = [LIGHT_GRAY]Prochaine vague dans {0} +wave.waveInProgress = [LIGHT_GRAY]Vague en cours +waiting = [LIGHT_GRAY]En attente... +waiting.players = En attente de joueurs ... +wave.enemies = [LIGHT_GRAY]{0} Ennemis restants +wave.enemy = [LIGHT_GRAY]{0} Ennemi restant +loadimage = Charger l'image +saveimage = Sauvegarder l'image +unknown = Inconnu +custom = Personnalisé +builtin = Pré-fait +map.delete.confirm = Êtes-vous sûr de vouloir effacer cette carte ? Cette action est irréversible ! +map.random = [accent]Carte aléatoire +map.nospawn = Cette carte ne possède pas de base pour que le joueur puisse apparaître !Ajouter un [ROYAL]base bleue[] sur cette carte dans l'éditeur. +map.nospawn.pvp = Cette carte ne contient aucune base ennemi dans lequel le joueur apparaît!\nAjoutez des bases [SCARLET]rouge[] à cette carte dans l'éditeur. +map.nospawn.attack = Cette carte ne contient aucune base ennemi à attaquer! Ajoutez des bases [SCARLET]rouge[] à cette carte dans l'éditeur. +map.invalid = Erreur lors du chargement de la carte: carte corrompue ou invalide. +editor.brush = Pinceau +editor.openin = Ouvrir dans l'éditeur +editor.oregen = Génération des minerais +editor.oregen.info = Génération de minerais: +editor.mapinfo = Infos sur la carte +editor.author = Auteur: +editor.description = Description: +editor.waves = Vagues: +editor.rules = Rules: +editor.ingame = Edit In-Game +waves.title = Vagues +waves.remove = Retirer +waves.never = +waves.every = tous les +waves.waves = vague(s) +waves.perspawn = par apparition +waves.to = à +waves.boss = Boss +waves.preview = Prévisualiser +waves.edit = Modifier... +waves.copy = Copier dans le Presse-papiers +waves.load = Coller depuis le Presse-papiers +waves.invalid = Vagues invalides dans le Presse-papiers. +waves.copied = Vagues copiées. +editor.default = [LIGHT_GRAY] +edit = Modifier... +editor.name = Nom: +editor.spawn = Spawn Unit +editor.removeunit = Remove Unit +editor.teams = Équipes +editor.elevation = Élévation +editor.errorload = Erreur lors du chargement du fichier:\n[accent]{0} +editor.errorsave = Erreur lors de la sauvegarde du fichier:\n[accent]{0} +editor.errorimage = That's an image, not a map. Don't go around changing extensions expecting it to work.\n\nIf you want to import a legacy map, use the 'import legacy map' button in the editor. +editor.errorlegacy = This map is too old, and uses a legacy map format that is no longer supported. +editor.errorheader = This map file is either not valid or corrupt. +editor.errorname = La carte n'a pas de nom! +editor.update = Mettre à jour +editor.randomize = Randomiser +editor.apply = Appliquer +editor.generate = Générer +editor.resize = Redimensionner +editor.loadmap = Charger une carte +editor.savemap = Sauvegarder une carte +editor.saved = Sauvegardé ! +editor.save.noname = Votre carte ne possède pas de nom ! Ajouter en un dans le menu 'Infos sur la carte'. +editor.save.overwrite = Une carte posséde déjà ce nom ! Choisissez un nom différent dans le menu 'Infos sur la carte'. +editor.import.exists = [scarlet]Importation impossible :[] Une carte nommé '{0}' existe déjà! +editor.import = Importation... +editor.importmap = Importer une carte +editor.importmap.description = Importer une carte déjà existante +editor.importfile = Importer un fichier +editor.importfile.description = Importer une carte à partir d'un fichier externe +editor.importimage = Importer l'image du terrain +editor.importimage.description = Importer une image de terrain à partir d'un fichier externe +editor.export = Exportation en cours... +editor.exportfile = Exporter un fichier +editor.exportfile.description = Exporter une carte +editor.exportimage = Exporter l'image du terrain +editor.exportimage.description = Exporter la carte sous forme d'image +editor.loadimage = Importer le terrain +editor.saveimage = Exportr le terrain +editor.unsaved = [scarlet] Vous avez des changements non sauvegardés ![] Êtes-vous sûr de vouloir quitter ? +editor.resizemap = Redimensionner\nla carte +editor.mapname = Nom de la carte: +editor.overwrite = [accent]Attention!\nCela écrasera une carte existante. +editor.overwrite.confirm = [scarlet]Attention ![] Une carte avec ce nom existe déjà. Êtes-vous sûr de vouloir la réécrire? +editor.selectmap = Sélectionnez une carte à charger: +filters.empty = [LIGHT_GRAY]Aucun filtre! Ajoutez-en un avec les boutons ci-dessous. +filter.distort = Déformation +filter.noise = Bruit +filter.ore = Minerai +filter.rivernoise = Bruit des rivières +filter.scatter = Dispersement +filter.terrain = Terrain +filter.option.scale = Échelle +filter.option.chance = Chance +filter.option.mag = Magnitude +filter.option.threshold = Seuil +filter.option.circle-scale = Échelle du cercle +filter.option.octaves = Octaves +filter.option.falloff = Diminution +filter.option.block = Bloc +filter.option.floor = Sol +filter.option.wall = Mur +filter.option.ore = Minerai +filter.option.floor2 = Sol secondaire +filter.option.threshold2 = Seuil secondaire +filter.option.radius = Radius +filter.option.percentile = Percentile +width = Largeur: +height = Hauteur: +menu = Menu +play = Jouer +load = Charger +save = Sauvegarder +fps = FPS: {0} +tps = TPS: {0} +ping = Ping: {0}ms +language.restart = Veuillez redémarrez votre jeu pour le changement de langage prenne effet. +settings = Paramètres +tutorial = Tutoriel +editor = Éditeur +mapeditor = Éditeur de carte +donate = Faire un\ndon +abandon = Abandonner +abandon.text = Cette zone et toutes ses ressources seront perdues. +locked = Verrouillé +complete = [LIGHT_GRAY]Compléter: +zone.requirement = Vague {0} dans la zone {1} +resume = Reprendre la partie en cours:\n[LIGHT_GRAY]{0} +bestwave = [LIGHT_GRAY]Meilleur: {0} +launch = Lancement +launch.title = Lancement réussi +launch.next = [LIGHT_GRAY]Prochaine opportunité à la vague {0} +launch.unable = [scarlet]Impossible d'effectuer le lancement.[] Ennemis. +launch.confirm = Cela lancera toutes les ressources dans votre noyau.\nVous ne pourrez pas revenir à cette base. +uncover = Découvrir +configure = Configurer le transfert des ressources. +configure.locked = [LIGHT_GRAY]Atteigner la vague {0}\npour configurer le transfert des ressources. +zone.unlocked = [LIGHT_GRAY]{0} Débloquée. +zone.requirement.complete = Vague {0} atteinte:\n{1} Exigences de la zone complétées +zone.config.complete = Vague {0} atteinte:\nConfiguration du transfert débloquée. +zone.resources = Ressources détectées: +add = Ajouter... +boss.health = Vie du BOSS +connectfail = [crimson]Échec de la connexion au serveur: [accent]{0} +error.unreachable = Serveur inaccessible. +error.invalidaddress = Adresse invalide. +error.timedout = Expiration du délai !\nAssurez-vous que la redirection de port est configurée sur l'hôte et que l'adresse est correcte ! +error.mismatch = Erreur de paquet:\nPossible d'incompatibilité de version client/serveur.\nAssurez-vous que l'hôte et vous disposez de la dernière version de Mindustry ! +error.alreadyconnected = Déjà connecté. +error.mapnotfound = Fichier de carte introuvable ! +error.io = Network I/O error. +error.any = Erreur réseau inconnue. +zone.groundZero.name = Première Bataille +zone.desertWastes.name = Déchets du désert +zone.craters.name = Les Cratères +zone.frozenForest.name = Forêt Glaciale +zone.ruinousShores.name = Rives en Ruine +zone.stainedMountains.name = Montagnes Tâchetées +zone.desolateRift.name = Fissure abandonnée +zone.nuclearComplex.name = Complexe nucléaire +zone.overgrowth.name = Surcroissance +zone.tarFields.name = Champs de goudron +settings.language = Langage +settings.reset = Valeur par défaut. +settings.rebind = Réatttribuer +settings.controls = Contrôles +settings.game = Jeu +settings.sound = Son +settings.graphics = Graphiques +settings.cleardata = Effacer les données du jeu... +settings.clear.confirm = Êtes-vous sûr d'effacer ces données ?\n[scarlet]Ceci est irréversible +settings.clearall.confirm = [scarlet]ATTENTION![]\nCet action effacera toutes les données , incluant les sauvegarges, les cartes, les déblocages et la configuration des touches.\nUne fois que vous aurez pressé 'Ok' le jeu effacera toutes les données et se fermera. +settings.clearunlocks = Éffacer les déblocages +settings.clearall = Tout effacer +paused = En pause +yes = Oui +no = Non +info.title = Info +error.title = [crimson]Une erreur s'est produite +error.crashtitle = Une erreur s'est produite +blocks.input = Ressource(s) requise(s) +blocks.output = Ressource(s) produite(s) +blocks.booster = Booster +block.unknown = [LIGHT_GRAY]Inconnu +blocks.powercapacity = Capacité d'énergie +blocks.powershot = Énergie/Tir +blocks.targetsair = Cible les unités aériennes +blocks.targetsground = Cible les unités terrestres +blocks.itemsmoved = Vitesse de déplacement +blocks.launchtime = Temps entre chaque lancement +blocks.shootrange = Portée +blocks.size = Taille +blocks.liquidcapacity = Capacité en liquide +blocks.powerrange = Distance de transmission +blocks.poweruse = Énergie utilisée +blocks.powerdamage = Énergie/Dégâts +blocks.itemcapacity = Stockage +blocks.basepowergeneration = Production d'énergie de base +blocks.productiontime = Temps de production +blocks.repairtime = Temps pour la réparation totale du bloc +blocks.speedincrease = Augmentation de la vitesse +blocks.range = Portée +blocks.drilltier = Forable +blocks.drillspeed = Vitesse de forage de base +blocks.boosteffect = Effet boostant +blocks.maxunits = Maximum d'unitée active +blocks.health = Santé +blocks.buildtime = Build Time +blocks.inaccuracy = Précision +blocks.shots = Tirs +blocks.reload = Tirs/Seconde +blocks.ammo = Munition +bar.drillspeed = Vitesse de forage: {0}/s +bar.efficiency = Efficacité: {0}% +bar.powerbalance = Énergie: {0} +bar.poweramount = Énergie: {0} +bar.poweroutput = Énergie en sortie: {0} +bar.items = Objets: {0} +bar.liquid = Liquide +bar.heat = Chaleur +bar.power = Énergie +bar.progress = Progression de la construction +bar.spawned = Unités: {0}/{1} +bullet.damage = [stat]{0}[lightgray] dégats +bullet.splashdamage = [stat]{0}[lightgray] dgt zone ~[stat] {1}[lightgray] tuiles +bullet.incendiary = [stat]incendiaire +bullet.homing = [stat]autoguidage +bullet.shock = [stat]choc +bullet.frag = [stat]frag +bullet.knockback = [stat]{0}[lightgray]recul +bullet.freezing = [stat]gel +bullet.tarred = [stat]tarred +bullet.multiplier = [stat]{0}[lightgray]x multiplicateur de munitions +bullet.reload = [stat]{0}[lightgray]x vitesse de rechargement +unit.blocks = Blocs +unit.powersecond = Énergie/seconde +unit.liquidsecond = Liquides/seconde +unit.itemssecond = Objets/seconde +unit.liquidunits = Unité de liquide +unit.powerunits = Unité d'énergie +unit.degrees = degrés +unit.seconds = secondes +unit.persecond = /sec +unit.timesspeed = x vitesse +unit.percent = % +unit.items = Objets +category.general = Général +category.power = Énergie +category.liquids = Liquides +category.items = Objets +category.crafting = Fabrication +category.shooting = Défense +category.optional = Améliorations facultatives +setting.landscape.name = Verrouiller la rotation en mode paysage +setting.shadows.name = Ombres +setting.linear.name = Linear Filtering +setting.animatedwater.name = Eau animée +setting.animatedshields.name = Boucliers Animés +setting.antialias.name = Antialias[LIGHT_GRAY] (demande le redémarrage de l'appareil)[] +setting.indicators.name = Indicateurs d'alliés +setting.autotarget.name = Visée automatique +setting.fpscap.name = Max FPS +setting.fpscap.none = Vide +setting.fpscap.text = {0} FPS +setting.swapdiagonal.name = Autoriser le placement des blocs en diagonal +setting.difficulty.training = Entraînement +setting.difficulty.easy = Facile +setting.difficulty.normal = Normal +setting.difficulty.hard = Difficile +setting.difficulty.insane = Êxtreme +setting.difficulty.name = Difficulté: +setting.screenshake.name = Tremblement d'écran +setting.effects.name = Montrer les effets +setting.sensitivity.name = Contôle de la sensibilité +setting.saveinterval.name = Intervalle des sauvegardes auto +setting.seconds = {0} Secondes +setting.fullscreen.name = Plein écran +setting.borderlesswindow.name = Borderless Window[LIGHT_GRAY] (may require restart) +setting.fps.name = Afficher FPS +setting.vsync.name = VSync +setting.lasers.name = Afficher les rayons des lasers +setting.pixelate.name = Pixélisé [LIGHT_GRAY](peut diminuer les performances)[] +setting.minimap.name = Montrer la minimap +setting.musicvol.name = Volume de la musique +setting.mutemusic.name = Couper la musique +setting.sfxvol.name = Volume des SFX +setting.mutesound.name = Couper les SFX +setting.crashreport.name = Envoyer des rapports d'incident anonymement. +setting.chatopacity.name = Opacité du tchat +setting.playerchat.name = Afficher le tchat en jeu +keybind.title = Paramétrer les touches +category.general.name = Général +category.view.name = Voir +category.multiplayer.name = Multijoueur +command.attack = Attaquer +command.retreat = Retraite +command.patrol = Patrouiller +keybind.gridMode.name = Sélectionnez le bloc +keybind.gridModeShift.name = Sélection de la catégorie +keybind.press = Appuyez sur une touche ... +keybind.press.axis = Appuyez sur un axe ou une touche... +keybind.screenshot.name = Map Screenshot +keybind.move_x.name = Mouvement X +keybind.move_y.name = Mouvement Y +keybind.select.name = Sélectionner/Tirer +keybind.diagonal_placement.name = Placement en diagonal +keybind.pick.name = Choisir un bloc +keybind.break_block.name = Supprimer un bloc +keybind.deselect.name = Déselectionner +keybind.shoot.name = Tirer +keybind.zoom_hold.name = Tenir le zoom +keybind.zoom.name = Zoom +keybind.menu.name = Menu +keybind.pause.name = Pause +keybind.minimap.name = Mini-Map +keybind.dash.name = Sprint +keybind.chat.name = Tchat +keybind.player_list.name = Liste des joueurs +keybind.console.name = Console +keybind.rotate.name = Tourner +keybind.toggle_menus.name = Montrer/Cacher les menus +keybind.chat_history_prev.name = Reculer dans l'historique du tchat +keybind.chat_history_next.name = Suite de l'historique du tchat +keybind.chat_scroll.name = Faire défiler le tchat +keybind.drop_unit.name = Larguer une unité +keybind.zoom_minimap.name = Zoomer la minimap +mode.help.title = Description des modes de jeu +mode.survival.name = Survival +mode.survival.description = Le mode normal. Ressources limitées et vagues automatiques. +mode.sandbox.name = Bac à sable +mode.sandbox.description = Ressources infinies et pas de compte à rebours pour les vagues. +mode.pvp.name = PvP +mode.pvp.description = Lutter contre d'autres joueurs pour gagner ! +mode.attack.name = Attaque +mode.attack.description = Pas de vagues, le but est de détruire la base ennemie. +mode.custom = Règles personnalisées +rules.infiniteresources = Ressources infinies +rules.wavetimer = Temps de vague +rules.waves = Vague +rules.enemyCheat = Ressources infinies pour l'IA +rules.unitdrops = Uniter Drops +rules.unitbuildspeedmultiplier = Multiplicateur de vitesse de création d'unités +rules.unithealthmultiplier = Multiplicateur de la santé des unités +rules.playerhealthmultiplier = Multiplicateur de la santé des joueurs +rules.playerdamagemultiplier = Multiplicateur de dégât des joueurs +rules.unitdamagemultiplier = Multiplicateur de dégât des unités +rules.enemycorebuildradius = Rayon de non-construction autour de la base ennemi:[LIGHT_GRAY] (tuiles) +rules.respawntime = Temps de réapparition:[LIGHT_GRAY] (sec) +rules.wavespacing = Espacement des vagues:[LIGHT_GRAY] (sec) +rules.buildcostmultiplier = Multiplicateur de coût de construction +rules.buildspeedmultiplier = Multiplicateur de vitesse de construction +rules.waitForWaveToEnd = Les vagues attendent les ennemis +rules.dropzoneradius = Rayon de la zone de largage:[LIGHT_GRAY] (tuiles) +rules.respawns = Max d'apparition par vague +rules.limitedRespawns = Limite d'apparition +rules.title.waves = Vagues +rules.title.respawns = Apparition +rules.title.resourcesbuilding = Ressources & Bâtiment +rules.title.player = Joueurs +rules.title.enemy = Ennemis +rules.title.unit = Unités +content.item.name = Objets +content.liquid.name = Liquides +content.unit.name = Unités +content.block.name = Blocs +content.mech.name = Mécha +item.copper.name = Cuivre +item.copper.description = Un matériau de construction utile. Utilisé intensivement dans tout les blocs. +item.lead.name = Plomb +item.lead.description = Un matériau de départ. Utilisé intensivement en électronique et pour le transport de blocs. +item.coal.name = Charbon +item.coal.description = Un carburant commun et facile à obtenir. +item.graphite.name = Graphite +item.titanium.name = Titane +item.titanium.description = Un métal rare super-léger largement utilisé dans le transport de liquides et d'objets ainsi que dans les foreuses de haut-niveau et l'aviation +item.thorium.name = Thorium +item.thorium.description = Un métal dense, et radioactif utilisé comme support structurel et comme carburant nucléaire. +item.silicon.name = Silicone +item.silicon.description = Un matériau semi-conducteur extrêmement utile, avec des utilisations dans les panneaux solaires et beaucoup d'autre composants électroniques complexes. +item.plastanium.name = Plastanium +item.plastanium.description = Un matériau léger et docile utilisé dans l'aviation avancée et dans les munitions à fragmentation. +item.phase-fabric.name = Phase Fabric +item.phase-fabric.description = Une substance presque en apesanteur utilisée dans l'électronique de pointe et la technologie autoréparable. +item.surge-alloy.name = Alliage superchargé +item.surge-alloy.description = Un alliage avancé aux propriétés électriques uniques. +item.spore-pod.name = Bulbe sporifère +item.spore-pod.description = Utilisé pour l'obtention d'huile, d'explosifs et de carburants +item.sand.name = Sable +item.sand.description = Un matériau commun utilisé largement dans la fonte, à la fois dans l'alliage et comme un flux. +item.blast-compound.name = Mélange explosif +item.blast-compound.description = Un composé volatile utilisé dans les bombes et les explosifs. Bien qu'il puisse être utilisé comme carburant, ce n'est pas conseillé. +item.pyratite.name = Pyratite +item.pyratite.description = Une substance extrêmement inflammable utilisée dans les armes incendiaires. +item.metaglass.name = Métaverre +item.metaglass.description = Un composé de verre très résistant. Utilisation intensive pour la distribution et le stockage de liquides. +item.scrap.name = Ferraille +item.scrap.description = Restes de vieilles structures et unités. Contient des traces de nombreux métaux différents. +liquid.water.name = Eau +liquid.slag.name = Scorie +liquid.oil.name = Pétrole +liquid.cryofluid.name = Liquide Cryogénique +mech.alpha-mech.name = Alpha +mech.alpha-mech.weapon = Fusil automatique +mech.alpha-mech.ability = Essaim de drone +mech.alpha-mech.description = Le mécha standard. A une vitesse et des dégâts décents; Il peut aussi créer jusqu'à 3 drones pour des faire des dégâts supplémentaires. +mech.delta-mech.name = Delta +mech.delta-mech.weapon = Arc électrique +mech.delta-mech.ability = Décharge +mech.delta-mech.description = Un mécha rapide avec une armure légère fait pour des tactiques de harcèlements. Il fait par contre peu de dégâts au structures, néanmoins il peut tuer de grand groupes d'ennemis très rapidement avec ses arcs électriques. +mech.tau-mech.name = Tau +mech.tau-mech.weapon = Laser restructurant +mech.tau-mech.ability = Explosion réparante +mech.tau-mech.description = Le support technique. Soigne les blocs alliés en leur tirant dessus. Peut soigner les alliés dans un rayon grâce à sa capacité de réparation. +mech.omega-mech.name = Oméga +mech.omega-mech.weapon = Essaim de missiles auto-guidés +mech.omega-mech.ability = Armure +mech.omega-mech.description = Un mécha cuirassé et large fait pour les assauts frontaux. Sa compétence "Armure" lui permet de bloquer 90% des dégâts. +mech.dart-ship.name = Dard +mech.dart-ship.weapon = Pistolet automatique +mech.dart-ship.description = Le vaisseau standard. Raisonnablement rapide et léger, il a néanmoins peu d'attaque et une faible vitesse de minage. +mech.javelin-ship.name = Javelin +mech.javelin-ship.description = Un vaisseau qui bien que lent au départ peut accélerer pour atteindre de très grandes vitesses et voler jusqu'au avant-postes ennemis, faisant d'énormes dégâts avec ses arc électriques obtenus à vitesse maximum et ses missiles. +mech.javelin-ship.weapon = Missiles explosifs autoguidés +mech.javelin-ship.ability = Décharge de propulseur +mech.trident-ship.name = Trident +mech.trident-ship.description = Un bombardier lourd raisonnablement cuirassé +mech.trident-ship.weapon = Largage de bombe +mech.glaive-ship.name = Glaive +mech.glaive-ship.description = Un grand vaisseau de combat cuirassé. Equipé avec un fusil automatique à munitions incendiaires. Il a aussi une bonne accéleration ainsi qu'une bonne vitesse maximale. +mech.glaive-ship.weapon = Fusil automatique incendiaire +item.explosiveness = [LIGHT_GRAY]Explosivité: {0} +item.flammability = [LIGHT_GRAY]Inflammabilité: {0} +item.radioactivity = [LIGHT_GRAY]Radioactivité: {0} +unit.health = [LIGHT_GRAY]Santé: {0} +unit.speed = [LIGHT_GRAY]Vitesse: {0} +mech.weapon = [LIGHT_GRAY]Arme: {0} +mech.health = [LIGHT_GRAY]Santé: {0} +mech.itemcapacity = [LIGHT_GRAY]Capacité de stockage: {0} +mech.minespeed = [LIGHT_GRAY]Vitesse de forage: {0} +mech.minepower = [LIGHT_GRAY]Puissance du forage: {0} +mech.ability = [LIGHT_GRAY]Compétence: {0} +mech.buildspeed = [LIGHT_GRAY]Building Speed: {0}% +liquid.heatcapacity = [LIGHT_GRAY]Capacité Thermique {0} +liquid.viscosity = [LIGHT_GRAY]Viscosité: {0} +liquid.temperature = [LIGHT_GRAY]Température: {0} +block.grass.name = Herbe +block.salt.name = Sel +block.saltrocks.name = Roches de sel +block.pebbles.name = Cailloux +block.tendrils.name = Vrilles +block.sandrocks.name = Roche de sableuse +block.spore-pine.name = Pin sporifère +block.sporerocks.name = Roche sporifère +block.rock.name = Roche +block.snowrock.name = Roche enneigée +block.shale.name = Schiste +block.shale-boulder.name = Rocher de schiste +block.moss.name = Mousse +block.shrubs.name = Arbustes +block.spore-moss.name = Mousse sporifère +block.shalerocks.name = Roche de schiste +block.scrap-wall.name = Mur de ferraille +block.scrap-wall-large.name = Grand mur de ferraille +block.scrap-wall-huge.name = Enorme mur de ferraille +block.scrap-wall-gigantic.name = Gigantesque mur de ferraille +block.thruster.name = Propulseur +block.kiln.name = Four a métaverre +block.kiln.description = Fait fondre le sable et le plomb en métaverre. Nécessite de petites quantités d'énergie. +block.graphite-press.name = Presse à graphite +block.multi-press.name = Multi-Presse +block.constructing = {0}\n[LIGHT_GRAY](En construction) +block.spawn.name = Générateur d'ennemi +block.core-shard.name = Core: Shard +block.core-foundation.name = Core: Foundation +block.core-nucleus.name = Core: Nucleus +block.deepwater.name = Eau profonde +block.water.name = Eau +block.tainted-water.name = Eau contaminée +block.darksand-tainted-water.name = Eau contaminée par le sable noir +block.tar.name = Pétrole +block.stone.name = Pierre +block.sand.name = Sable +block.darksand.name = Sable noire +block.ice.name = Glace +block.snow.name = Neige +block.craters.name = Cratères +block.sand-water.name = Eau (sable) +block.darksand-water.name = Eau (sable noir) +block.char.name = Carboniser +block.holostone.name = Holo stone +block.ice-snow.name = Neige glacée +block.rocks.name = Roche +block.icerocks.name = Roches de glace +block.snowrocks.name = Roches de neige +block.dunerocks.name = Roches de sable +block.pine.name = Pin +block.white-tree-dead.name = Arbre blanc mort +block.white-tree.name = Arbre blanc +block.spore-cluster.name = Grappe de spores +block.metal-floor.name = Sol métallique +block.metal-floor-2.name = Sol métallique 2 +block.metal-floor-3.name = Sol métallique 3 +block.metal-floor-5.name = Sol métallique 5 +block.metal-floor-damaged.name = Sol métallique endommagé +block.dark-panel-1.name = Panneau noir 1 +block.dark-panel-2.name = Panneau noir 2 +block.dark-panel-3.name = Panneau noir 3 +block.dark-panel-4.name = Panneau noir 4 +block.dark-panel-5.name = Panneau noir 5 +block.dark-panel-6.name = Panneau noir 6 +block.dark-metal.name = Métal noir +block.ignarock.name = Roche d'igna +block.hotrock.name = Roche chaude +block.magmarock.name = Roche de magma +block.cliffs.name = Falaises +block.copper-wall.name = Mur de cuivre +block.copper-wall-large.name = Grand mur de cuivre +block.titanium-wall.name = Mur de titane +block.titanium-wall-large.name = Grand mur de titane +block.phase-wall.name = Mur phasé +block.phase-wall-large.name = Grand mur phasé +block.thorium-wall.name = Mur en thorium +block.thorium-wall-large.name = Grand mur en thorium +block.door.name = Porte +block.door-large.name = Grande porte +block.duo.name = Duo +block.scorch.name = Scorch +block.scatter.name = Scatter +block.hail.name = Hail +block.lancer.name = Lancer +block.conveyor.name = Transporteur +block.titanium-conveyor.name = Transporteur en titane +block.junction.name = Junction +block.router.name = Routeur +block.distributor.name = [accent]Distributeur[] +block.sorter.name = Trieur +block.sorter.description = Trie les articles. Si un article correspond à la sélection, il peut passer. Autrement, l'article est distribué vers la gauche ou la droite. +block.overflow-gate.name = Barrière de Débordement +block.overflow-gate.description = C'est la combinaison entre un routeur et un diviseur qui peut seulement distribuer à gauche et à droite si le chemin de devant est bloqué. +block.silicon-smelter.name = Fonderie de silicone +block.phase-weaver.name = Tisseur à phase +block.pulverizer.name = Pulvérisateur +block.cryofluidmixer.name = Refroidisseur +block.melter.name = Four à Fusion +block.incinerator.name = Incinérateur +block.spore-press.name = Spore presse +block.separator.name = Séparateur +block.coal-centrifuge.name = Centrifugeuse à charbon +block.power-node.name = Transmetteur énergétique +block.power-node-large.name = Grand transmetteur énergétique +block.surge-tower.name = Tour de surtension +block.battery.name = Batterie +block.battery-large.name = Batterie large +block.combustion-generator.name = Générateur à combustion +block.turbine-generator.name = Générateur à turbine +block.differential-generator.name = Générateur différentiel +block.impact-reactor.name = Impact Reactor +block.mechanical-drill.name = Foreuse mécanique +block.pneumatic-drill.name = Foreuse à vérin +block.laser-drill.name = Foreuse Laser +block.water-extractor.name = Extracteur d'eau +block.cultivator.name = Cultivateur +block.dart-mech-pad.name = Dart Mech Pad +block.delta-mech-pad.name = Delta Mech Pad +block.javelin-ship-pad.name = Javelin Ship Pad +block.trident-ship-pad.name = Trident Ship Pad +block.glaive-ship-pad.name = Glaive Ship Pad +block.omega-mech-pad.name = Omega Mech Pad +block.tau-mech-pad.name = Tau Mech Pad +block.conduit.name = Conduit +block.mechanical-pump.name = Pompe Méchanique +block.item-source.name = Source d'objets +block.item-void.name = Destructeur d'objets +block.liquid-source.name = Source de liquide +block.power-void.name = Absorbeur énergétique +block.power-source.name = Puissance infinie +block.unloader.name = Déchargeur +block.vault.name = Coffre-Fort +block.wave.name = Vague +block.swarmer.name = Essaim +block.salvo.name = Salve +block.ripple.name = Ripple +block.phase-conveyor.name = Transporteur phasé +block.bridge-conveyor.name = Pont transporteur +block.plastanium-compressor.name = Compresseur de plastanium +block.pyratite-mixer.name = Mixeur à pyratite +block.blast-mixer.name = Mixeur à explosion +block.solar-panel.name = Panneau solaire +block.solar-panel-large.name = Grand panneau solaire +block.oil-extractor.name = Extracteur de pétrol +block.spirit-factory.name = Usine de "Drones spirituels" +block.phantom-factory.name = Usine de "Drones fantômes" +block.wraith-factory.name = Usine de "Combattants spectraux" +block.ghoul-factory.name = Usine de "Bombardiers goules" +block.dagger-factory.name = Usine de "Poignards" +block.crawler-factory.name = Usine de "Chenille" +block.titan-factory.name = Usine de "Titans" +block.fortress-factory.name = Usine de "Forteresse" +block.revenant-factory.name = Usine de "Revenants" +block.repair-point.name = Point de Réparation +block.pulse-conduit.name = Conduit à Impulsion +block.phase-conduit.name = Conduit à Phase +block.liquid-router.name = Routeur de Liquide +block.liquid-tank.name = Réservoir de Liquide +block.liquid-junction.name = Jonction à Liquide +block.bridge-conduit.name = Pont à liquide +block.rotary-pump.name = Pompe Rotative +block.thorium-reactor.name = Réacteur à Thorium +block.mass-driver.name = Transporteur de masse +block.blast-drill.name = Foreuse à explosion +block.thermal-pump.name = Pompe thermique +block.thermal-generator.name = Générateur thermique +block.alloy-smelter.name = Fonderie d'alliage superchargé +block.mender.name = Gardien +block.mend-projector.name = Projecteur soignant +block.surge-wall.name = Mur superchargé +block.surge-wall-large.name = Grand mur superchargé +block.cyclone.name = Cyclone +block.fuse.name = Fuse +block.shock-mine.name = Mines terrestre +block.overdrive-projector.name = Projecteur accélérant +block.force-projector.name = Projecteur de champ de force +block.arc.name = Arc +block.rtg-generator.name = G.T.R. +block.spectre.name = Spectre +block.meltdown.name = Meltdown +block.container.name = Conteneur +block.launch-pad.name = Rampe de lancement +block.launch-pad.description = Lance des lots d'articles sans qu'il soit nécessaire de procéder à un lancement de base. Inachevé. +block.launch-pad-large.name = Grande rampe de lancement +team.blue.name = Bleu +team.red.name = Rouge +team.orange.name = Orange +team.none.name = Gris +team.green.name = Vert +team.purple.name = Violet +unit.spirit.name = Drone spirituel +unit.spirit.description = L'unité de soutien de départ. Apparaît dans la base par défaut. Mine automatiquement les minerais, récupère les objets au sol et répare les blocs. +unit.phantom.name = Drone Fantôme +unit.phantom.description = Une unité de soutien avancée. Mine automatiquement les minerais, récupère les objets au sol et répare les blocs. Bien plus efficace qu'un drone spirituel. +unit.dagger.name = Poignard +unit.dagger.description = Une unité terrestre de base. Utile en essaims. +unit.crawler.name = Chenille +unit.titan.name = Titan +unit.titan.description = Une unité terrestre cuirassée avancée. Utilise de l'alliage lourd pour munition. Attaque les unités aérinnes comme terrestres. +unit.ghoul.name = Bombardier goule +unit.ghoul.description = Un bombardier lourd. Utilise de la pyratite ou des explosifs comme munitions. +unit.wraith.name = Combattant spectral +unit.wraith.description = Une unité volante rapide harcelant les ennemis. Utilise du plomb comme munitions. +unit.fortress.name = Forteresse +unit.fortress.description = Une unité terrestre d'artillerie lourde. +unit.revenant.name = Revenant +unit.eruptor.name = Eruptor +unit.chaos-array.name = Chaos Array +unit.eradicator.name = Eradicator +unit.lich.name = Lich +unit.reaper.name = Reaper +tutorial.begin = Votre mission, si vous l'acceptez est de détruire [LIGHT_GRAY]l'ennemi[].\n\nCommencez par [accent] miner du cuivre[]. Appuyer sur un filon de cuivre proche de votre base pour faire ceci. +tutorial.drill = Le minage manuel est inefficace.\n[accent]Des foreuses[]peuvent miner automatiquement.\nPlacez-en une sur un filon de cuivre. +tutorial.conveyor = [accent]Les Tapis roulants[] sont utilisés pour transporter des objets jusqu'à la base.\nFaites une ligne de tapis roulants de la foreuse à la base . +tutorial.morecopper = Plus de cuivre est demandé .\n\nRécupérez le soit manuellement soit construisez plus de foreuses. +tutorial.turret = Des constructions défensives doivent être construites pour repousser [LIGHT_GRAY]les ennemis[].Construisez une tourelle "duo" près de votre base. +tutorial.drillturret = Les tourelles "Duo" ont besoin de [accent]munitions en cuivre[] pour tirer.\nPlacez une foreuse à côté de la tourelle pour l'approvisionner avec du cuivre. +tutorial.waves = Les [LIGHT_GRAY]ennemies[] approchent.\n\nDéfendez votre base pour 2 vagues. Construisez plus de tourelles. +tutorial.lead = Plus de minerais sont forables. Explorez et minez du[accent] Plomb[].\n\nRamenez votre unité à la base pour transférer les ressources. +tutorial.smelter = Le cuivre et le plomb sont des métaux fragiles.\nUn alliage de qualité supérieure peut être créé dans une fonderie, l'[accent] alliage lourd [].\n\n Construisez-en un. +tutorial.densealloy = La fonderie va maintenant produire de l'alliage lourd.\nObtenez-en .\nVous pouvez aussi améliorer la production si nécessaire . +tutorial.siliconsmelter = La base va maintenant créer un[accent] drone spirituel[] pour miner et réparer les blocs.\n\nDes usines pour faire d'autres unités peuvent être faites avec du [accent] silicone.\nFaites une fonderie de silicone . +tutorial.silicondrill = Faire du silicone demande [accent] du charbon[] et[accent] du sable [].\nCommencez par construire des foreuses . +tutorial.generator = Cette technologie requiert de l'énergie pour fonctionner.\nFaites un [accent]générateur à combustion[] pour en produire. +tutorial.generatordrill = Les générateurs à combustion ont besoin de carburant.\n Donnez-lui du charbon comme carburant avec une foreuse. +tutorial.node = L'énergie doit être transportée .\nCréez un [accent] Transmetteur energétique[] à côté de votre générateur à combustion pour transférer son énergie. +tutorial.nodelink = L'énergie peut être transféré à l'aide de blocs utilisant de l'énergie ou des générateurs, ou encore par des Transmetteurs énergétiques reliés.\n\nReliez des transmetteurs en appuyant dessus puis en sélectionnant le générateur et la fonderie de silicone. +tutorial.silicon = Du silicone est maintenant produit. Obtenez-en.\n\nAugmenter la production est recommandé. +tutorial.daggerfactory = Construire [accent]une usine de "Poignards" []est recommandé .\n\nElle sera utilisée pour produire des unités d'attaque. +tutorial.router = Les usines ont besoin de ressources pour fonctionner.\nCréez un routeur pour séparer les objets. +tutorial.dagger = Reliez des transmetteurs énergétiques à l'usine.\nUne fois que les conditions seront remplies , un mécha sera créé.\nConstruisez autant de foreuses, de générateurs et de tapis roulants que nécessaire. +tutorial.battle = [LIGHT_GRAY]L'Ennemi[] a révélé sa base.\nDétruisez la avec votre unité et des méchas "Poignard". +block.copper-wall.description = Un bloc défensif bon marché.\nUtile pour protéger le noyau et les tourelles lors des premières vagues. +block.copper-wall-large.description = Un bloc défensif bon marché.\nUtile pour protéger le noyau et les tourelles lors des premières vagues.\nS'étend sur plusieurs tuiles. +block.thorium-wall.description = Un puissant bloc défensif.\nBonne protection contre les ennemis. +block.thorium-wall-large.description = Un puissant bloc défensif.\nBonne protection contre les ennemis.\nS'étend sur plusieurs tuiles. +block.phase-wall.description = Pas aussi fort qu'un mur de thorium, mais détournera les balles à moins qu'elles ne soient trop puissantes. +block.phase-wall-large.description = Pas aussi fort qu'un mur de thorium, mais détournera les balles à moins qu'elles ne soient trop puissantes.\nS'étend sur plusieurs tuiles. +block.surge-wall.description = Le bloc défensif le plus puissant.\nPeu de chances de déclencher des éclairs en direction de l'attaquant. +block.surge-wall-large.description = Le bloc défensif le plus puissant.\nPeu de chances de déclencher des éclairs en direction de l'attaquant.\nS'étend sur plusieurs tuiles. +block.door.description = Une petite porte qui peut être ouverte et fermée en tapotant dessus.\nSi elle est ouverte, les ennemis peuvent tirer et se déplacer. +block.door-large.description = Une grande porte qui peut être ouverte et fermée en tapotant dessus.\nSi elle est ouverte, les ennemis peuvent tirer et se déplacer.\nS'étend sur plusieurs tuiles. +block.mend-projector.description = Guérit périodiquement les bâtiments situés à proximité. +block.overdrive-projector.description = Augmente la vitesse des bâtiments à proximité, comme les foreuses et les convoyeurs. +block.force-projector.description = Crée un champ de force hexagonal autour de lui-même, protégeant les bâtiments et les unités internes des dommages causés par les balles. +block.shock-mine.description = Endommage les ennemis qui marchent sur la mine. Presque invisible à l'ennemi. +block.duo.description = Une petite tourelle pas chère. +block.scatter.description = Une tourelle anti-air de taille moyenne. Pulvérise des amas de plomb ou de ferraille sur les unités ennemies. +block.arc.description = Une petite tourelle qui tire de l'électricité dans un arc au hasard vers l'ennemi. +block.hail.description = Une petite tourelle d'artillerie. +block.lancer.description = Une tourelle de taille moyenne qui tire des faisceaux d’électricité chargés. +block.wave.description = Une tourelle de taille moyenne à tir rapide qui tire des bulles de liquide. +block.salvo.description = Une tourelle de taille moyenne qui tire des coups de salves. +block.swarmer.description = Une tourelle de taille moyenne qui tire des missiles éclatés. +block.ripple.description = Une grande tourelle d'artillerie qui tire plusieurs coups simultanément. +block.cyclone.description = Une grande tourelle à tir rapide. +block.fuse.description = Une grande tourelle qui tire de puissants faisceaux à courte portée. +block.spectre.description = Une grande tourelle qui tire deux balles puissantes à la fois. +block.meltdown.description = Une grande tourelle qui tire de puissants faisceaux à longue portée. +block.conveyor.description = Bloc de transport d'articles standard.\nDéplace les objets et les déposes automatiquement dans des tourelles ou des usines. Rotatif. +block.titanium-conveyor.description = Bloc de transport d'articles avancé.\nDéplace les articles plus rapidement que les convoyeurs standard. +block.phase-conveyor.description = Bloc de transport d'articles avancé.\nUtilise le pouvoir de téléporter des articles vers un convoyeur de phase connecté sur plusieurs carreaux. +block.junction.description = Agit comme un pont pour deux bandes transporteuses qui se croisent.\nUtile dans les situations avec deux convoyeurs différents transportant des matériaux différents à des endroits différents. +block.mass-driver.description = Bloc de transport d'articles ultime.\nRecueille plusieurs objets et les envoie ensuite à un autre pilote de masse sur une longue distance. +block.silicon-smelter.description = Réduit le sable avec du coke* très pur afin de produire du silicium. (*Coke produit à partir de charbon:REF) +block.plastanium-compressor.description = Produit du plastanium à partir de pétrole et de titane. +block.phase-weaver.description = Produit un tissu de phase à partir de thorium radioactif et de grandes quantités de sable. +block.alloy-smelter.description = Produit un alliage de surtension à partir de titane, plomb, silicium et cuivre. +block.pulverizer.description = Brise la pierre en sable. Utile en cas de manque de sable naturel. +block.pyratite-mixer.description = Mélange le charbon, le plomb et le sable en pyratite hautement inflammable. +block.blast-mixer.description = Utilise du pétrole pour transformer la pyratite en un composé explosif moins inflammable mais plus explosif. +block.cryofluidmixer.description = L'eau et le titane combinés forment un fluide cryo beaucoup plus efficace pour le refroidissement. +block.melter.description = Chauffe la pierre à des températures très élevées pour obtenir de la lave. +block.incinerator.description = Se débarrasse de tout article ou liquide en excès. +block.spore-press.description = Comprime les gousses de spores en huile. +block.separator.description = Exposer la pierre à la pression de l'eau afin d'obtenir différents minéraux contenus dans la pierre. +block.power-node.description = Transmet la puissance à des noeuds connectés. Il est possible de connecter jusqu'à quatre sources d'alimentation, puits ou nœuds.\nLe nœud recevra de l’alimentation ou fournira l’alimentation à tous les blocs adjacents. +block.power-node-large.description = Son rayon d'action est supérieur à celui du nœud d'alimentation et peut être connecté à six sources d'alimentation, puits ou nœuds au maximum. +block.battery.description = Stocke l’énergie chaque fois qu’il ya abondance et en cas de pénurie, tant qu’il reste de la capacité. +block.battery-large.description = Stocke beaucoup plus d'énergie qu'une batterie ordinaire. +block.combustion-generator.description = Génère de l'énergie en brûlant du pétrole ou des matériaux inflammables. +block.turbine-generator.description = Plus efficace qu'un générateur de combustion, mais nécessite de l'eau supplémentaire. +block.thermal-generator.description = Génère une grande quantité d'énergie grâce à la lave. +block.solar-panel.description = Fournit une petite quantité d'énergie grâce au soleil. +block.solar-panel-large.description = Fournit une bien meilleure alimentation qu'un panneau solaire standard, mais coûte également beaucoup plus cher à construire. +block.thorium-reactor.description = Génère d'énormes quantités d'énergie à partir de thorium hautement radioactif. Nécessite un refroidissement constant.\nExplose violemment si des quantités insuffisantes de liquide de refroidissement ne sont pas fournies. +block.rtg-generator.description = Générateur thermoélectrique à radio-isotopes ne nécessitant pas de refroidissement mais fournissant moins d'énergie qu'un réacteur à thorium. +block.unloader.description = Décharge des articles d'un conteneur, d'une chambre forte ou d'un noyau sur un convoyeur ou directement dans un bloc adjacent.\nLe type d'élément à décharger peut être modifié en tapotant sur le déchargeur. +block.container.description = Stocke une petite quantité d'objets. Utilisez-le pour créer des tampons lorsqu'il existe une demande non constante de matériaux. [LIGHT_GRAY]Un déchargeur[] peut être utilisé pour récupérer des éléments du conteneur. +block.vault.description = Stocke une grande quantité d'objets. Utilisez-le pour créer des tampons lorsqu'il existe une demande non constante de matériaux. [LIGHT_GRAY]Un déchargeur[] peut être utilisé pour récupérer des éléments du coffre-fort. +block.mechanical-drill.description = Un extracteur bon marché. Lorsqu'il est placé sur des carreaux appropriés, les objets sortent à un rythme lent et indéfiniment. +block.pneumatic-drill.description = Un extracteur améliorée, plus rapide et capable de traiter des matériaux plus durs en utilisant la pression atmosphérique. +block.laser-drill.description = Permet de forer encore plus rapidement grâce à la technologie laser, mais nécessite de l'énergie. De plus, le thorium radioactif peut être récupéré avec cet extracteur. +block.blast-drill.description = L'extracteur ultime. Nécessite de grandes quantités d'énergie. +block.water-extractor.description = Extrait l'eau du sol. Utilisez-le quand il n'y a pas de lac à proximité. +block.cultivator.description = Cultiver le sol avec de l'eau afin d'obtenir du biomatter. +block.oil-extractor.description = Utilise de grandes quantités d'énergie pour extraire le pétrole du sable. Utilisez-le lorsqu'il n'y a pas de source directe de pétrole à proximité. +block.trident-ship-pad.description = Quittez votre vaisseau actuel et changez-vous en un bombardier lourd raisonnablement bien blindé.\nUtilisez la plate-forme en tapotant deux fois dessus. +block.javelin-ship-pad.description = Quittez votre vaisseau actuel et changez-vous en un intercepteur puissant et rapide doté d’armes légères.\nUtilisez la plate-forme en tapotant deux fois dessus. +block.glaive-ship-pad.description = Quittez votre vaisseau actuel et changez-vous en un grand vaisseau de combat bien blindé.\nUtilisez la plate-forme en tapotant deux fois dessus. +block.tau-mech-pad.description = Quittez votre vaisseau actuel et changez-vous en un centre de support capable de soigner les bâtiments et unités amis.\nUtilisez la plate-forme en tapotant deux fois dessus. +block.delta-mech-pad.description = Quittez votre vaisseau actuel et changez-vous en un méchant rapide, légèrement blindé, conçu pour les attaques à la volée.\nUtilisez la plate-forme en tapotant deux fois dessus. +block.omega-mech-pad.description = Quittez votre vaisseau actuel et changez-vous en un mech encombrant et bien blindé, conçu pour les assauts de première ligne.\nUtilisez la plate-forme en tapotant deux fois dessus. +block.spirit-factory.description = Produit des drones légers qui extraient du minerai et réparent des blocs. +block.phantom-factory.description = Produit des unités de drones avancées qui sont nettement plus efficaces qu'un drone spirituel. +block.wraith-factory.description = Produit des intercepteurs rapides qui harcèlent l'ennemi. +block.ghoul-factory.description = Produit des tapis de bombardiers lourds. +block.dagger-factory.description = Produit des unités terrestres de base. +block.titan-factory.description = Produit des unités terrestres avancées et blindées. +block.fortress-factory.description = Produit des unités terrestres d'artillerie lourde. +block.revenant-factory.description = Produit des unités terrestres laser lourdes. +block.repair-point.description = Soigne en permanence l'unité endommagée la plus proche à proximité. +block.conduit.description = Bloc de transport liquide de base. Fonctionne comme un convoyeur, mais avec des liquides. Utilisation optimale avec des extracteurs, des pompes ou d’autres conduits. +block.pulse-conduit.description = Bloc de transport de liquide avancé. Transporte les liquides plus rapidement et stocke plus que des conduits standard. +block.phase-conduit.description = Bloc de transport de liquide avancé. Utilise le pouvoir de téléporter des liquides vers un conduit de phase connecté sur plusieurs carreaux. +block.liquid-router.description = Accepte les liquides d'une direction et les envoie dans 3 autres directions de manière égale. Peut également stocker une certaine quantité de liquide. Utile pour séparer les liquides d'une source à plusieurs cibles. +block.liquid-tank.description = Stocke une grande quantité de liquides. Utilisez-le pour créer des tampons en cas de demande non constante de matériaux ou comme protection pour le refroidissement des blocs vitaux. +block.liquid-junction.description = Agit comme un pont pour deux conduits de croisement. Utile dans les situations avec deux conduits différents transportant des liquides différents à des endroits différents. +block.bridge-conduit.description = Bloc de transport de liquide avancé. Permet de transporter des liquides jusqu'à 3 tuiles de n'importe quel terrain ou bâtiment. +block.mechanical-pump.description = Une pompe bon marché avec un débit lent, mais aucune consommation d'énergie. +block.rotary-pump.description = Une pompe avancée qui double la vitesse en utilisant l’énergie. +block.thermal-pump.description = La pompe ultime. Trois fois plus rapide qu'une pompe mécanique et la seule pompe capable de récupérer de la lave. +block.router.description = Accepte les éléments d'une direction et les envoie dans 3 autres directions de manière égale. Utile pour séparer les matériaux d'une source en plusieurs cibles. +block.distributor.description = Un routeur avancé qui divise les articles en 7 autres directions de manière égale. [scarlet]Seule et unique ![] +block.bridge-conveyor.description = Bloc de transport d'articles avancé. Permet de transporter des objets sur plus de 3 tuiles de n'importe quel terrain ou bâtiment. +block.item-source.description = Sort infiniment les articles. Bac à sable seulement. +block.liquid-source.description = Débit infini de liquides. Bac à sable seulement. +block.item-void.description = Détruit tous les objets qui y entrent sans utiliser d'énergie. Bac à sable seulement. +block.power-source.description = Débit infini d'énergie. Bac à sable seulement. +block.power-void.description = Annule toute l'énergie qui y est introduite. Bac à sable seulement. +liquid.water.description = Couramment utilisé pour les machines de refroidissement et le traitement des déchets. +liquid.oil.description = Peut être brûlé, explosé ou utilisé comme liquide de refroidissement. +liquid.cryofluid.description = Le liquide de refroidissement le plus efficace. diff --git a/core/assets/bundles/bundle_in_ID.properties b/core/assets/bundles/bundle_in_ID.properties index 6c96a7468c..62b475ee78 100644 --- a/core/assets/bundles/bundle_in_ID.properties +++ b/core/assets/bundles/bundle_in_ID.properties @@ -1,491 +1,947 @@ -text.about = Dibuat oleh [ROYAL]Anuken.[]\nAwalnya masuk di [orange]GDL[] MM Jam.\n\nKredit:\n- SFX dibuat dengan [YELLOW]bfxr[]\n- Musik dibuat oleh [GREEN]RoccoW[] / ditemukan di [lime]FreeMusicArchive.org[]\n\nTerima kasih khusus kepada:\n- [coral]MitchellFJN[]: playtesting dan umpan balik yang luas\n- [sky]Luxray5474[]: pekerjaan wiki, kontribusi kode\n- Semua penguji beta di itch.io dan Google Play\n -text.discord = Bergabunglah dengan Discord Mindustry! -text.gameover = Intinya hancur. -text.highscore = [YELLOW]Rekor baru! -text.lasted = Anda bertahan sampai gelombang -text.level.highscore = Skor Tinggi: [accent]{0} -text.level.delete.title = Konfirmasi Hapus -text.level.delete = Yakin ingin menghapus peta \"[orange]{0}\"? -text.level.select = Pilih Level -text.level.mode = Modus permainan: -text.savegame = Simpan Permainan -text.loadgame = Lanjutkan -text.joingame = Bermain Bersama -text.quit = Keluar -text.about.button = Tentang -text.name = Nama: -text.public = Publik -text.players = {0} pemain online -text.server.player.host = {0} (host) -text.players.single = {0} pemain online -text.server.mismatch = Kesalahan paket: kemungkinan versi client / server tidak sesuai.\nPastikan Anda dan host memiliki versi terbaru Mindustry! -text.server.closing = [accent]Menutup server... -text.server.kicked.kick = Anda telah dikeluarkan dari server! -text.server.kicked.invalidPassword = Kata sandi salah! -text.server.kicked.clientOutdated = Client versi lama! Update game Anda! -text.server.kicked.serverOutdated = Server versi lama! Tanyakan host untuk mengupdate! -text.server.connected = {0} telah bergabung. -text.server.disconnected = {0} telah terputus. -text.nohost = Tidak dapat meng-host server pada peta khusus! -text.hostserver = Host Server -text.host = Host -text.hosting = [accent]Membuka server... -text.hosts.refresh = Segarkan -text.hosts.discovering = Mencari game LAN -text.server.refreshing = Menyegarkan server -text.hosts.none = [lightgray]Tidak ada game LAN yang ditemukan! -text.host.invalid = [scarlet]Tidak dapat terhubung ke host. -text.server.friendlyfire = Tembak Sesama -text.server.add = Tambahkan Server -text.server.delete = Yakin ingin menghapus server ini? -text.server.hostname = Host: {0} -text.server.edit = Sunting Server -text.joingame.byip = Bergabung dengan IP... -text.joingame.title = Bermain Bersama -text.joingame.ip = IP: -text.disconnect = Sambungan terputus. -text.connecting = [accent]Menghubungkan... -text.connecting.data = [accent]Memuat data level... -text.connectfail = [crimson]Gagal terhubung ke server: [orange]{0} -text.server.port = Port: -text.server.addressinuse = Alamat sudah di pakai! -text.server.invalidport = Nomor port salah! -text.server.error = [crimson]Kesalahan server hosting: [orange]{0} -text.tutorial.back = < Sebelumnya -text.tutorial.next = Berikutnya > -text.save.new = Simpan Baru -text.save.overwrite = Yakin ingin mengganti slot simpan ini? -text.overwrite = Ganti -text.save.none = Tidak ada simpanan ditemukan! -text.saveload = [accent]Menyimpan... -text.savefail = Gagal menyimpan game! -text.save.delete.confirm = Yakin ingin menghapus save ini? -text.save.delete = Hapus -text.save.export = Ekspor Simpanan -text.save.import.invalid = [orange]Simpanan ini tidak valid! -text.save.import.fail = [crimson]Gagal mengimpor: [orange]{0} -text.save.export.fail = [crimson]Gagal mengekspor save: [orange]{0} -text.save.import = Impor Simpanan -text.save.newslot = Nama simpanan: -text.save.rename = Ganti nama -text.save.rename.text = Nama baru: -text.selectslot = Pilih simpanan. -text.slot = [accent]Slot{0} -text.save.corrupted = [orange]Simpanan rusak atau tidak valid! -text.empty = -text.on = Hidup -text.off = Mati -text.save.autosave = Simpan otomatis: {0} -text.save.map = Peta: {0} -text.save.wave = Gelombang {0} -text.save.date = Terakhir Disimpan: {0} -text.confirm = Konfirmasi -text.delete = Hapus -text.ok = OK -text.open = Buka -text.cancel = Batal -text.openlink = Buka tautan -text.back = Kembali -text.quit.confirm = Anda yakin ingin berhenti? -text.loading = [accent]Memuat... -text.wave = [orange]Gelombang {0} -text.wave.waiting = Gelombang dimulai {0} -text.waiting = Menunggu... -text.enemies = {0} musuh -text.enemies.single = {0} Musuh -text.loadimage = Buka Gambar -text.saveimage = Simpan Gambar -text.oregen = Generator Bijih -text.editor.badsize = [orange]Dimensi gambar tidak valid![]\nDimensi peta yang valid: {0} -text.editor.errorimageload = Kesalahan saat memuat file gambar:\n[orange]{0} -text.editor.errorimagesave = Kesalahan saat menyimpan file gambar:\n[orange]{0} -text.editor.generate = Hasilkan -text.editor.resize = Ubah ukuran -text.editor.loadmap = Buka Peta -text.editor.savemap = Simpan Peta -text.editor.loadimage = Buka Gambar -text.editor.saveimage = Simpan Gambar -text.editor.unsaved = [scarlet]Anda memiliki perubahan yang belum disimpan![]\nYakin ingin keluar? -text.editor.brushsize = Ukuran sikat: {0} -text.editor.noplayerspawn = Peta ini tidak memiliki spawnpoint pemain! -text.editor.manyplayerspawns = Peta tidak bisa memiliki lebih dari satu\nspawnpoint pemain! -text.editor.manyenemyspawns = Tidak dapat memiliki lebih dari\n{0} spawnpoint musuh! -text.editor.resizemap = Ubah ukuran peta -text.editor.resizebig = [scarlet]Peringatan!\n[]Peta yang lebih besar dari 256 unit mungkin nge-lag dan tidak stabil. -text.editor.mapname = Nama Peta: -text.editor.overwrite = [accent]Peringatan!\nIni akan mengganti peta yang ada. -text.editor.failoverwrite = [crimson]Tidak dapat mengganti peta default! -text.editor.selectmap = Pilih peta yang akan dimuat: -text.width = Lebar: -text.height = Tinggi: -text.randomize = Acak -text.apply = Terapkan -text.update = Perbarui -text.menu = Menu -text.play = Main -text.load = Buka -text.save = Simpan -text.language.restart = Silakan mulai ulang permainan Anda agar pengaturan bahasa mulai berlaku. -text.settings.language = Bahasa -text.settings = Pengaturan -text.tutorial = Tutorial -text.editor = Pengedit -text.mapeditor = Pengedit Peta -text.donate = Sumbangkan -text.settings.reset = Atur ulang ke Default -text.settings.controls = Kontrol -text.settings.game = Permainan -text.settings.sound = Suara -text.settings.graphics = Grafis -text.upgrades = Perbaruan -text.purchased = [LIME]Dibuat! -text.weapons = Senjata -text.paused = Jeda -text.respawn = Respawning dalam -text.info.title = [accent]Info -text.error.title = [crimson]Telah terjadi kesalahan -text.error.crashmessage = [SCARLET]Kesalahan tak terduga telah terjadi, yang menyebabkan kerusakan.\n[]Tolong laporkan keadaan yang tepat dimana kesalahan ini terjadi pada pengembang:\n[ORANGE] anukendev@gmail.com[] -text.error.crashtitle = Telah terjadi kesalahan -text.mode.break = Mode penghancur: {0} -text.mode.place = Mode penaruh: {0} -placemode.hold.name = garis -placemode.areadelete.name = area -placemode.touchdelete.name = sentuh -placemode.holddelete.name = tahan -placemode.none.name = tidak ada -placemode.touch.name = sentuh -placemode.cursor.name = kursor -text.blocks.extrainfo = [accent]info tambahan blok: -text.blocks.blockinfo = Info Blok -text.blocks.powercapacity = Kapasitas Tenaga -text.blocks.powershot = Tenaga/tembakan -text.blocks.powersecond = Tenaga/detik -text.blocks.powerdraindamage = Tenaga Dipakai/damage -text.blocks.shieldradius = Radius Perisai -text.blocks.itemspeedsecond = Kecepatan Barang/detik -text.blocks.range = Jangkauan -text.blocks.size = Ukuran -text.blocks.powerliquid = Tenaga/Cairan -text.blocks.maxliquidsecond = Batas cairan/detik -text.blocks.liquidcapacity = Kapasitas cairan -text.blocks.liquidsecond = Cairan/detik -text.blocks.damageshot = Damage/tembakan -text.blocks.ammocapacity = Kapasitas Amunisi -text.blocks.ammo = Amunisi -text.blocks.ammoitem = Amunisi/barang -text.blocks.maxitemssecond = Batas barang/detik -text.blocks.powerrange = Jangkauan tenaga -text.blocks.lasertilerange = Kotak jangkauan laser -text.blocks.capacity = Kapasitas -text.blocks.itemcapacity = Kapasitas Barang -text.blocks.maxpowergenerationsecond = Batas Penghasil Tenaga/detik -text.blocks.powergenerationsecond = Penghasil Tenaga/detik -text.blocks.generationsecondsitem = Waktu Penghasil (detik)/barang -text.blocks.input = Masukan -text.blocks.inputliquid = Cairan yang Masuk -text.blocks.inputitem = Barang yang Masuk -text.blocks.output = Keluar -text.blocks.secondsitem = Detik/barang -text.blocks.maxpowertransfersecond = Batas transfer tenaga/detik -text.blocks.explosive = Mudah meledak! -text.blocks.repairssecond = Perbaikan/detik -text.blocks.health = Darah -text.blocks.inaccuracy = Ketidaktelitian -text.blocks.shots = Tembakan -text.blocks.shotssecond = Tembakan/detik -text.blocks.fuel = Bahan Bakar -text.blocks.fuelduration = Durasi Bahan Bakar -text.blocks.maxoutputsecond = Batas keluar/detik -text.blocks.inputcapacity = Kapasitas masuk -text.blocks.outputcapacity = Kapasitas keluar -text.blocks.poweritem = Tenaga/barang -text.placemode = Mode Penempatan -text.breakmode = Mode Penghancur -text.health = darah +credits.text = Created by [ROYAL]Anuken[] - [SKY]anukendev@gmail.com[]\n\n[GRAY](In case you can't tell, this text is currently unfinished.\nTranslators, don't edit it yet!) +credits = Kredit +contributors = Translators and Contributors +discord = Mari bergabung di Discord Mindustry! +link.discord.description = grup Discord Mindustry resmi +link.github.description = Source code permainan +link.dev-builds.description = Bentukan pengembang (kurang stabil) +link.trello.description = Papan trello resmi untuk fitur-fitur terencana +link.itch.io.description = Halaman itch.io dengan unduhan PC dan versi web +link.google-play.description = Google Play store +link.wiki.description = wiki resmi Mindustry +linkfail = Gagal membuka link!\nURL telah disalin ke papan salin. +screenshot = Screenshot saved to {0} +screenshot.invalid = Map too large, potentially not enough memory for screenshot. +gameover = Intinya hancur. +gameover.pvp = Tim [accent] {0}[] menang! +highscore = [YELLOW]Rekor baru! +stat.wave = Waves Defeated:[accent] {0} +stat.enemiesDestroyed = Enemies Destroyed:[accent] {0} +stat.built = Buildings Built:[accent] {0} +stat.destroyed = Buildings Destroyed:[accent] {0} +stat.deconstructed = Buildings Deconstructed:[accent] {0} +stat.delivered = Resources Launched: +stat.rank = Final Rank: [accent]{0} +placeline = You have selected a block.\nYou can[accent] place in a line[] by[accent] holding down your finger for a few seconds[] and dragging in a direction.\nTry it. +removearea = You have selected removal mode.\nYou can[accent] remove blocks in a rectangle[] by[accent] holding down your finger for a few seconds[] and dragging.\nTry it. +launcheditems = [accent]Launched Items +map.delete = Apakah kamu yakin ingin menghapus peta "[orange]{0}[]"? +level.highscore = Skor Tinggi: [accent]{0} +level.select = Pilih Level +level.mode = Modus permainan: +showagain = Jangan tampilkan lagi di sesi berikutnya +coreattack = < Intinya sedang diserang! > +nearpoint = [[ [scarlet]LEAVE DROP POINT IMMEDIATELY[] ]\nannihilation imminent +outofbounds = [[ OUT OF BOUNDS ]\n[]self-destruct in {0} +database = Core Database +savegame = Simpan Permainan +loadgame = Lanjutkan +joingame = Bermain Bersama +addplayers = Tambah/Hapus Pemain +customgame = Game Bebas +newgame = New Game +none = +minimap = Minimap +close = Tutup +quit = Keluar +maps = Peta +continue = Lanjutkan +maps.none = [LIGHT_GRAY]Peta tidak ditemukan! +about.button = Tentang +name = Nama: +noname = Pick a[accent] player name[] first. +filename = Nama file: +unlocked = Blok Baru Terbuka! +completed = [accent]Completed +techtree = Tech Tree +research.list = [LIGHT_GRAY]Research: +research = Research +researched = [LIGHT_GRAY]{0} researched. +players = {0} pemain online +players.single = {0} pemain online +server.closing = [accent]Menutup server... +server.kicked.kick = Anda telah dikeluarkan dari server! +server.kicked.serverClose = Server ditutup. +server.kicked.clientOutdated = Client versi lama! Update game Anda! +server.kicked.serverOutdated = Server versi lama! Tanyakan host untuk memperbaharuinya! +server.kicked.banned = Anda telah di-ban dari server ini. +server.kicked.recentKick = Kamu baru saja dikeluarkan.\nTunggu sebentar sebelum terhubung kembali. +server.kicked.nameInUse = Ada seseorang dengan nama\nitu di server ini. +server.kicked.nameEmpty = Nama Anda harus mengandung setidaknya satu karakter atau angka. +server.kicked.idInUse = Kamu sudah ada di server ini! Menghubungkan dengan dua akun tidak diizinkan. +server.kicked.customClient = Server ini tidak mendukung bentukan khusus. Unduh versi resmi. +server.kicked.gameover = Game over! +host.info = Tombol [accent]host[] akan membuat server dengan port [scarlet]6567[] dan [scarlet]6568.[]\nSiapa saja yang terhubung ke [LIGHT_GRAY]WiFi atau jaringan lokal[] dapat melihat server Anda di daftar server.\n\nJika Anda ingin orang-orang agar dapat terhubung dari mana saja melalui IP, diperlukan [accent]port forwarding[].\n\n[LIGHT_GRAY]Catatan: Jika seseorang mengalami kesulitan untuk bergabung, pastikan bahwa Anda telah mengizinkan akses Mindustry ke jaringan lokal Anda di pengaturan firewall. +join.info = Di sini, Anda dapat memasukan [accent]IP server[] yang akan dihubungkan, atau menemukan server di [accent]jaringan lokal[] untuk dihubungkan.\nKedua jaringan LAN dan WAN didukung.\n\n[LIGHT_GRAY]Catatan: There is no automatic global server list; if you want to connect to someone by IP, you would need to ask the host for their IP. +hostserver = Host Server +hostserver.mobile = Host\nPermainan +host = Host +hosting = [accent]Membuka server... +hosts.refresh = Segarkan +hosts.discovering = Mencari game LAN +server.refreshing = Menyegarkan server +hosts.none = [lightgray]Tidak ada game LAN yang ditemukan! +host.invalid = [scarlet]Tidak dapat terhubung ke host. +trace = Lacak Pemain +trace.playername = Nama pemain: [accent]{0} +trace.ip = IP: [accent]{0} +trace.id = ID Unik: [accent]{0} +trace.mobile = Mobile Client: [accent]{0} +trace.modclient = Klien Khusus: [accent]{0} +invalidid = ID klien tidak valid! Kirim laporan bug. +server.bans = Ban +server.bans.none = Tidak ada pemain di-ban! +server.admins = Admin +server.admins.none = Tidak ada admin yang ditemukan! +server.add = Tambahkan Server +server.delete = Yakin ingin menghapus server ini? +server.hostname = Host: {0} +server.edit = Sunting Server +server.outdated = [crimson]Server kedaluarsa![] +server.outdated.client = [crimson]Klien kedaluarsa![] +server.version = [lightgray]Versi: {0} +server.custombuild = [yellow]Bentukan khusus +confirmban = Apakah Anda yakin ingin melarang pemain ini? +confirmkick = Apakah Anda yakin ingin mengeluarkan pemain ini? +confirmunban = Apakah Anda yakin ingin mengizinkan pemain ini? +confirmadmin = Apakah Anda yakin ingin menjadikan pemain ini sebagai admin? +confirmunadmin = Are you sure you want to remove admin status from this player? +joingame.title = Bermain Bersama +joingame.ip = IP: +disconnect = Sambungan terputus. +disconnect.data = Failed to load world data! +connecting = [accent]Menghubungkan... +connecting.data = [accent]Memuat data level... +server.port = Port: +server.addressinuse = Alamat sudah di pakai! +server.invalidport = Nomor port salah! +server.error = [crimson]Kesalahan server hosting: [accent]{0} +save.old = This save is for an older version of the game, and can no longer be used.\n\n[LIGHT_GRAY]Save backwards compatibility will be implemented in the full 4.0 release. +save.new = Simpan Baru +save.overwrite = Yakin ingin mengganti slot simpan ini? +overwrite = Ganti +save.none = Tidak ada simpanan ditemukan! +saveload = [accent]Menyimpan... +savefail = Gagal menyimpan game! +save.delete.confirm = Yakin ingin menghapus save ini? +save.delete = Hapus +save.export = Ekspor Simpanan +save.import.invalid = [accent]Simpanan ini tidak valid! +save.import.fail = [crimson]Gagal mengimpor: [accent]{0} +save.export.fail = [crimson]Gagal mengekspor save: [accent]{0} +save.import = Impor Simpanan +save.newslot = Nama simpanan: +save.rename = Ganti nama +save.rename.text = Nama baru: +selectslot = Pilih simpanan. +slot = [accent]Slot{0} +save.corrupted = [accent]Simpanan rusak atau tidak valid! +empty = +on = Hidup +off = Mati +save.autosave = Simpan otomatis: {0} +save.map = Peta: {0} +save.wave = Gelombang {0} +save.difficulty = Difficulty: {0} +save.date = Terakhir Disimpan: {0} +save.playtime = Playtime: {0} +warning = Warning. +confirm = Konfirmasi +delete = Hapus +ok = OK +open = Buka +customize = Customize +cancel = Batal +openlink = Buka tautan +copylink = Copy Link +back = Kembali +quit.confirm = Anda yakin ingin berhenti? +changelog.title = Changelog +changelog.loading = Getting changelog... +changelog.error.android = [accent]Note that the changelog sometimes does not work on Android 4.4 and below!\nThis is due to an internal Android bug. +changelog.error.ios = [accent]The changelog is currently not supported in iOS. +changelog.error = [scarlet]Error getting changelog!\nCheck your internet connection. +changelog.current = [yellow][[Current version] +changelog.latest = [accent][[Latest version] +loading = [accent]Memuat... +saving = [accent]Saving... +wave = [accent]Gelombang {0} +wave.waiting = Gelombang dimulai {0} +wave.waveInProgress = [LIGHT_GRAY]Wave in progress +waiting = Menunggu... +waiting.players = Waiting for players... +wave.enemies = [LIGHT_GRAY]{0} Enemies Remaining +wave.enemy = [LIGHT_GRAY]{0} Enemy Remaining +loadimage = Buka Gambar +saveimage = Simpan Gambar +unknown = Unknown +custom = Custom +builtin = Built-In +map.delete.confirm = Are you sure you want to delete this map? This action cannot be undone! +map.random = [accent]Random Map +map.nospawn = This map does not have any cores for the player to spawn in! Add a [ROYAL]blue[] core to this map in the editor. +map.nospawn.pvp = This map does not have any enemy cores for player to spawn into! Add[SCARLET] red[] cores to this map in the editor. +map.nospawn.attack = This map does not have any enemy cores for player to attack! Add[SCARLET] red[] cores to this map in the editor. +map.invalid = Error loading map: corrupted or invalid map file. +editor.brush = Brush +editor.openin = Open In Editor +editor.oregen = Ore Generation +editor.oregen.info = Ore Generation: +editor.mapinfo = Map Info +editor.author = Author: +editor.description = Description: +editor.waves = Waves: +editor.rules = Rules: +editor.ingame = Edit In-Game +waves.title = Waves +waves.remove = Remove +waves.never = +waves.every = every +waves.waves = wave(s) +waves.perspawn = per spawn +waves.to = to +waves.boss = Boss +waves.preview = Preview +waves.edit = Edit... +waves.copy = Copy to Clipboard +waves.load = Load from Clipboard +waves.invalid = Invalid waves in clipboard. +waves.copied = Waves copied. +editor.default = [LIGHT_GRAY] +edit = Edit... +editor.name = Name: +editor.spawn = Spawn Unit +editor.removeunit = Remove Unit +editor.teams = Teams +editor.elevation = Elevation +editor.errorload = Error loading file:\n[accent]{0} +editor.errorsave = Error saving file:\n[accent]{0} +editor.errorimage = That's an image, not a map. Don't go around changing extensions expecting it to work.\n\nIf you want to import a legacy map, use the 'import legacy map' button in the editor. +editor.errorlegacy = This map is too old, and uses a legacy map format that is no longer supported. +editor.errorheader = This map file is either not valid or corrupt. +editor.errorname = Map has no name defined. +editor.update = Update +editor.randomize = Randomize +editor.apply = Apply +editor.generate = Hasilkan +editor.resize = Ubah ukuran +editor.loadmap = Buka Peta +editor.savemap = Simpan Peta +editor.saved = Saved! +editor.save.noname = Your map does not have a name! Set one in the 'map info' menu. +editor.save.overwrite = Your map overwrites a built-in map! Pick a different name in the 'map info' menu. +editor.import.exists = [scarlet]Unable to import:[] a built-in map named '{0}' already exists! +editor.import = Import... +editor.importmap = Import Map +editor.importmap.description = Import an already existing map +editor.importfile = Import File +editor.importfile.description = Import an external map file +editor.importimage = Import Terrain Image +editor.importimage.description = Import an external map image file +editor.export = Export... +editor.exportfile = Export File +editor.exportfile.description = Export a map file +editor.exportimage = Export Terrain Image +editor.exportimage.description = Export a map image file +editor.loadimage = Buka Gambar +editor.saveimage = Simpan Gambar +editor.unsaved = [scarlet]Anda memiliki perubahan yang belum disimpan![]\nYakin ingin keluar? +editor.resizemap = Ubah ukuran peta +editor.mapname = Nama Peta: +editor.overwrite = [accent]Peringatan!\nIni akan mengganti peta yang ada. +editor.overwrite.confirm = [scarlet]Warning![] A map with this name already exists. Are you sure you want to overwrite it? +editor.selectmap = Pilih peta yang akan dimuat: +filters.empty = [LIGHT_GRAY]No filters! Add one with the button below. +filter.distort = Distort +filter.noise = Noise +filter.ore = Ore +filter.rivernoise = River Noise +filter.scatter = Scatter +filter.terrain = Terrain +filter.option.scale = Scale +filter.option.chance = Chance +filter.option.mag = Magnitude +filter.option.threshold = Threshold +filter.option.circle-scale = Circle Scale +filter.option.octaves = Octaves +filter.option.falloff = Falloff +filter.option.block = Block +filter.option.floor = Floor +filter.option.wall = Wall +filter.option.ore = Ore +filter.option.floor2 = Secondary Floor +filter.option.threshold2 = Secondary Threshold +filter.option.radius = Radius +filter.option.percentile = Percentile +width = Lebar: +height = Tinggi: +menu = Menu +play = Main +load = Buka +save = Simpan +fps = FPS: {0} +tps = TPS: {0} +ping = Ping: {0}ms +language.restart = Silakan mulai ulang permainan Anda agar pengaturan bahasa mulai berlaku. +settings = Pengaturan +tutorial = Tutorial +editor = Pengedit +mapeditor = Pengedit Peta +donate = Sumbangkan +abandon = Abandon +abandon.text = This zone and all its resources will be lost to the enemy. +locked = Locked +complete = [LIGHT_GRAY]Complete: +zone.requirement = Wave {0} in zone {1} +resume = Resume Zone:\n[LIGHT_GRAY]{0} +bestwave = [LIGHT_GRAY]Best: {0} +launch = Launch +launch.title = Launch Successful +launch.next = [LIGHT_GRAY]next opportunity at wave {0} +launch.unable = [scarlet]Unable to LAUNCH.[] Enemies. +launch.confirm = This will launch all resources in your core.\nYou will not be able to return to this base. +uncover = Uncover +configure = Configure Loadout +configure.locked = [LIGHT_GRAY]Reach wave {0}\nto configure loadout. +zone.unlocked = [LIGHT_GRAY]{0} unlocked. +zone.requirement.complete = Wave {0} reached:\n{1} zone requirements met. +zone.config.complete = Wave {0} reached:\nLoadout config unlocked. +zone.resources = Resources Detected: +add = Add... +boss.health = Boss Health +connectfail = [crimson]Gagal terhubung ke server: [accent]{0} +error.unreachable = Server unreachable. +error.invalidaddress = Invalid address. +error.timedout = Timed out!\nMake sure the host has port forwarding set up, and that the address is correct! +error.mismatch = Packet error:\npossible client/server version mismatch.\nMake sure you and the host have the latest version of Mindustry! +error.alreadyconnected = Already connected. +error.mapnotfound = Map file not found! +error.io = Network I/O error. +error.any = Unkown network error. +zone.groundZero.name = Ground Zero +zone.desertWastes.name = Desert Wastes +zone.craters.name = The Craters +zone.frozenForest.name = Frozen Forest +zone.ruinousShores.name = Ruinous Shores +zone.stainedMountains.name = Stained Mountains +zone.desolateRift.name = Desolate Rift +zone.nuclearComplex.name = Nuclear Production Complex +zone.overgrowth.name = Overgrowth +zone.tarFields.name = Tar Fields +settings.language = Bahasa +settings.reset = Atur ulang ke Default +settings.rebind = Rebind +settings.controls = Kontrol +settings.game = Permainan +settings.sound = Suara +settings.graphics = Grafis +settings.cleardata = Clear Game Data... +settings.clear.confirm = Are you sure you want to clear this data?\nWhat is done cannot be undone! +settings.clearall.confirm = [scarlet]WARNING![]\nThis will clear all data, including saves, maps, unlocks and keybinds.\nOnce you press 'ok' the game will wipe all data and automatically exit. +settings.clearunlocks = Clear Unlocks +settings.clearall = Clear All +paused = Jeda +yes = Yes +no = No +info.title = [accent]Info +error.title = [crimson]Telah terjadi kesalahan +error.crashtitle = Telah terjadi kesalahan +blocks.input = Input +blocks.output = Output +blocks.booster = Booster +block.unknown = [LIGHT_GRAY]??? +blocks.powercapacity = Kapasitas Tenaga +blocks.powershot = Tenaga/tembakan +blocks.targetsair = Targets Air +blocks.targetsground = Targets Ground +blocks.itemsmoved = Move Speed +blocks.launchtime = Time Between Launches +blocks.shootrange = Range +blocks.size = Ukuran +blocks.liquidcapacity = Kapasitas cairan +blocks.powerrange = Jangkauan tenaga +blocks.poweruse = Power Use +blocks.powerdamage = Power/Damage +blocks.itemcapacity = Kapasitas Barang +blocks.basepowergeneration = Base Power Generation +blocks.productiontime = Production Time +blocks.repairtime = Block Full Repair Time +blocks.speedincrease = Speed Increase +blocks.range = Range +blocks.drilltier = Drillables +blocks.drillspeed = Base Drill Speed +blocks.boosteffect = Boost Effect +blocks.maxunits = Max Active Units +blocks.health = Darah +blocks.buildtime = Build Time +blocks.inaccuracy = Ketidaktelitian +blocks.shots = Tembakan +blocks.reload = Reload +blocks.ammo = Ammo +bar.drillspeed = Drill Speed: {0}/s +bar.efficiency = Efficiency: {0}% +bar.powerbalance = Power: {0} +bar.poweramount = Power: {0} +bar.poweroutput = Power Output: {0} +bar.items = Items: {0} +bar.liquid = Liquid +bar.heat = Heat +bar.power = Power +bar.progress = Build Progress +bar.spawned = Units: {0}/{1} +bullet.damage = [stat]{0}[lightgray] dmg +bullet.splashdamage = [stat]{0}[lightgray] area dmg ~[stat] {1}[lightgray] tiles +bullet.incendiary = [stat]incendiary +bullet.homing = [stat]homing +bullet.shock = [stat]shock +bullet.frag = [stat]frag +bullet.knockback = [stat]{0}[lightgray] knockback +bullet.freezing = [stat]freezing +bullet.tarred = [stat]tarred +bullet.multiplier = [stat]{0}[lightgray]x ammo multiplier +bullet.reload = [stat]{0}[lightgray]x reload +unit.blocks = blocks +unit.powersecond = power units/second +unit.liquidsecond = liquid units/second +unit.itemssecond = items/second +unit.liquidunits = liquid units +unit.powerunits = power units +unit.degrees = degrees +unit.seconds = seconds +unit.persecond = /sec +unit.timesspeed = x speed +unit.percent = % +unit.items = items +category.general = General +category.power = Power +category.liquids = Liquids +category.items = Items +category.crafting = Crafting +category.shooting = Shooting +category.optional = Optional Enhancements +setting.landscape.name = Lock Landscape +setting.shadows.name = Shadows +setting.linear.name = Linear Filtering +setting.animatedwater.name = Animated Water +setting.animatedshields.name = Animated Shields +setting.antialias.name = Antialias[LIGHT_GRAY] (requires restart)[] +setting.indicators.name = Ally Indicators +setting.autotarget.name = Auto-Target +setting.fpscap.name = Max FPS +setting.fpscap.none = None +setting.fpscap.text = {0} FPS +setting.swapdiagonal.name = Always Diagonal Placement +setting.difficulty.training = training setting.difficulty.easy = mudah setting.difficulty.normal = normal setting.difficulty.hard = sulit setting.difficulty.insane = sangat susah -setting.difficulty.purge = paling susah setting.difficulty.name = Kesulitan: setting.screenshake.name = Layar Bergoyang -setting.smoothcam.name = Kamera Halus -setting.indicators.name = Indikator Musuh setting.effects.name = Efek Tampilan setting.sensitivity.name = Sensitivitas Pengendali setting.saveinterval.name = Waktu Simpan Otomatis setting.seconds = {0} Detik setting.fullscreen.name = Layar Penuh +setting.borderlesswindow.name = Borderless Window[LIGHT_GRAY] (may require restart) setting.fps.name = Tunjukkan FPS setting.vsync.name = VSync setting.lasers.name = Tampilkan Laser Tenaga -setting.healthbars.name = Tampilkan Bar Darah Entitas -setting.pixelate.name = Layar Pixel +setting.pixelate.name = Pixelate [LIGHT_GRAY](may decrease performance) +setting.minimap.name = Show Minimap setting.musicvol.name = Volume Musik setting.mutemusic.name = Bisukan Musik setting.sfxvol.name = Volume Suara setting.mutesound.name = Bisukan Suara -map.maze.name = labirin -map.fortress.name = benteng -map.sinkhole.name = lubang pembuangan -map.caves.name = gua -map.volcano.name = gunung berapi -map.caldera.name = kaldera -map.scorch.name = penghangusan -map.desert.name = gurun -map.island.name = pulau -map.grassland.name = padang rumput -map.tundra.name = tundra -map.spiral.name = spiral -map.tutorial.name = tutorial -tutorial.intro.text = [yellow]Selamat datang di tutorial.[] Untuk memulai, tekan 'berikutnya'. -tutorial.moveDesktop.text = Untuk bergerak, gunakan tombol [orange][[WASD][]. Tahan tombol [orange]shift[] untuk mempercepat. Tahan [orange]CTRL[] saat menggunakan [orange]scrollwheel[] untuk memperbesar atau memperkecil tampilan. -tutorial.shoot.text = Gunakan mouse anda untuk mengarahkan, tahan [orange]tombol kiri mouse[] untuk menembak. Cobalah menembaki [yellow]target[]. -tutorial.moveAndroid.text = Untuk menggeser tampilan, seret satu jari ke layar. Jepit dan seret untuk memperbesar atau memperkecil tampilan. -tutorial.placeSelect.text = Coba pilih [yellow]konveyor[] dari menu blok di kanan bawah. -tutorial.placeConveyorDesktop.text = Gunakan [orange][[scrollwheel][] untuk memutar konveyor menghadap [orange]ke depan[], lalu letakkan di [yellow]lokasi yang ditandai[] menggunakan [orange][[tombol kiri mouse]][]. -tutorial.placeConveyorAndroid.text = Gunakan [orange][[tombol putar]][] untuk memutar konveyor menghadap [orange]ke depan[], seret ke posisi dengan satu jari, lalu letakkan di [yellow]lokasi yang ditandai[] dengan menggunakan [orange][[tanda centang][]. -tutorial.placeConveyorAndroidInfo.text = Sebagai alternatif, Anda dapat menekan ikon crosshair di kiri bawah untuk beralih ke [orange][[mode sentuh]][], dan letakkan blok dengan mengetuk layar. Dalam mode sentuh, blok bisa diputar dengan panah di kiri bawah. Tekan [yellow]berikutnya[] untuk mencobanya. -tutorial.placeDrill.text = Sekarang, pilih dan tempatkan [yellow]pertambangan battu[] di lokasi yang ditandai. -tutorial.blockInfo.text = Jika Anda ingin mempelajari lebih lanjut tentang blok, Anda dapat menekan [orange]tanda tanya[] di bagian kanan atas untuk membaca deskripsinya. -tutorial.deselectDesktop.text = Anda bisa membatalkan pemilihan blok menggunakan [orange][[tombol mouse kanan][]. -tutorial.deselectAndroid.text = Anda dapat membatalkan pemilihan blok dengan menekan tombol [orange]X (silang)[]. -tutorial.drillPlaced.text = Pertambangannya sekarang akan menghasilkan [yellow]batu[] yang dikeluarkan ke konveyor, lalu memindahkannya ke [yellow]intinya[]. -tutorial.drillInfo.text = Bijih yang berbeda membutuhkan pertambangan yang berbeda. Batu membutuhkan pertambangan batu, besi membutuhkan pertambangan besi, dll. -tutorial.drillPlaced2.text = Memindahkan barang ke dalam inti menempatkannya di [yellow]inventaris barang[] Anda, di kiri atas. Menempatkan blok menggunakan barang dari inventaris Anda. -tutorial.moreDrills.text = Anda bisa menghubungkan banyak pertambangan dan konveyor bersama-sama, seperti biasa. -tutorial.deleteBlock.text = Anda dapat menghapus blok dengan mengeklik [orange]tombol mouse kanan[] di blok yang ingin Anda hapus. Coba hapus konveyor ini. -tutorial.deleteBlockAndroid.text = Anda dapat menghapus blok dengan [orange]memilih crosshair[] di [orange]menu mode penghancur[] di kiri bawah dan mengetuk bloknya. Coba hapus konveyor ini. -tutorial.placeTurret.text = Sekarang, pilih dan tempatkan [yellow]turret[] di [yellow]lokasi yang ditandai[]. -tutorial.placedTurretAmmo.text = Turret ini sekarang akan menerima [yellow]amunisi[] dari konveyor. Anda dapat melihat berapa banyak amunisi yang dimiliki dengan menggeser kursor di bloknya dan memeriksa di [green]bilah hijau[]. -tutorial.turretExplanation.text = Turret secara otomatis akan menembak musuh terdekat dalam jangkauan, selama mereka memiliki cukup amunisi. -tutorial.waves.text = Setiap [yellow]60[] detik, gelombang [coral]musuh[] akan muncul di lokasi tertentu dan berusaha menghancurkan intinya. -tutorial.coreDestruction.text = Tujuan Anda adalah untuk [yellow]mempertahankan intinya[]. Jika intinya hancur, Anda [coral]kalah dalam permainan[]. -tutorial.pausingDesktop.text = Jika Anda perlu istirahat sebentar, tekan [orange]tombol jeda[] di bagian kiri atas atau [orange]tombol spasi[] untuk menghentikan sementara permainan. Anda masih bisa memilih dan menempatkan blok sambil berhenti, tapi tidak bisa bergerak atau menembak. -tutorial.pausingAndroid.text = Jika Anda perlu istirahat sebentar, tekan [orange]tombol jeda[] di kiri atas untuk menjeda permainan. Anda masih bisa menghapus dan menempatkan blok sambil berhenti sebentar. -tutorial.purchaseWeapons.text = Anda bisa membeli [yellow]senjata baru[] untuk robot Anda dengan membuka menu upgrade di kiri bawah. -tutorial.switchWeapons.text = Untuk mengganti senjata, klik ikonnya di kiri bawah, atau gunakan angka [orange][[1-9][]. -tutorial.spawnWave.text = Gelombang sekarang datang. Hancurkan mereka. -tutorial.pumpDesc.text = Pada gelombang selanjutnya, Anda mungkin perlu menggunakan [yellow]pompa[] untuk mendistribusikan cairan untuk generator atau ekstraktor. -tutorial.pumpPlace.text = Pompa bekerja seperti dengan pertambangan, namun mereka menghasilkan cairan dan bukan barang. Cobalah menempatkan pompa pada [yellow]minyak yang ditunjuk[]. -tutorial.conduitUse.text = Sekarang tempatkan [orange]saluran[] yang mengarah jauh dari pompa. -tutorial.conduitUse2.text = Dan beberapa lagi... -tutorial.conduitUse3.text = Dan beberapa lagi... -tutorial.generator.text = Sekarang, tempatkan [orange]blok generator pembakaran[] di ujung saluran. -tutorial.generatorExplain.text = Generator ini sekarang akan menciptakan [yellow]tenaga[] dari minyak. -tutorial.lasers.text = Tenaga didistribusikan menggunakan [yellow]laser tenaga[]. Putar dan tempatkan di sini. -tutorial.laserExplain.text = Generator sekarang akan memindahkan tenaga ke blok laser. Sinar [yellow]terang[] menandakan bahwa saat ini mentransmisikan tenaga, dan sinar [yellow]transparan[] berarti tidak. -tutorial.laserMore.text = Anda dapat memeriksa berapa banyak tenaga yang dimiliki blok dengan memindahkan kursor/mengetuk di atasnya dan memeriksa [yellow]bar kuning[] di bagian atas. -tutorial.healingTurret.text = Laser ini bisa digunakan untuk menyalakan [lime]turret perbaikan[]. Tempatkan satu di sini. -tutorial.healingTurretExplain.text = Selama memiliki tenaga, turret ini akan [lime]memperbaiki blok terdekat[]. Saat bermain, pastikan Anda memasukkannya ke markas Anda secepat mungkin! -tutorial.smeltery.text = Banyak blok yang membutuhkan [orange]baja[] agar dapat dibangun, yang membutuhkan [orange]peleburan[] untuk dibuat. Tempatkan satu di sini. -tutorial.smelterySetup.text = Peleburan ini sekarang akan menghasilkan [orange]baja[] dari besi yang masuk, dengan batubara sebagai bahan bakarnya. -tutorial.tunnelExplain.text = Perhatikan juga bahwa barang-barang itu masuk melalui [orange]blok terowongan[] dan muncul di sisi lain, melewati blok batu. Perlu diingat bahwa terowongan hanya bisa melalui sampai 2 blok. -tutorial.end.text = Dan itu menyimpulkan tutorialnya! Semoga berhasil! +setting.crashreport.name = Send Anonymous Crash Reports +setting.chatopacity.name = Chat Opacity +setting.playerchat.name = Display In-Game Chat +keybind.title = Rebind Keys +category.general.name = General +category.view.name = View +category.multiplayer.name = Multiplayer +command.attack = Attack +command.retreat = Retreat +command.patrol = Patrol +keybind.gridMode.name = Block Select +keybind.gridModeShift.name = Category Select +keybind.press = Press a key... +keybind.press.axis = Press an axis or key... +keybind.screenshot.name = Map Screenshot keybind.move_x.name = gerak_x keybind.move_y.name = gerak_y keybind.select.name = pilih -keybind.break.name = hapus +keybind.diagonal_placement.name = Diagonal Placement +keybind.pick.name = Pick Block +keybind.break_block.name = Break Block +keybind.deselect.name = Deselect keybind.shoot.name = tembak keybind.zoom_hold.name = perbesar_tahan keybind.zoom.name = perbesar keybind.menu.name = menu keybind.pause.name = jeda +keybind.minimap.name = Minimap keybind.dash.name = berlari -keybind.rotate_alt.name = putar_alt +keybind.chat.name = chat +keybind.player_list.name = player_list +keybind.console.name = console keybind.rotate.name = putar -keybind.weapon_1.name = senjata_1 -keybind.weapon_2.name = senjata_2 -keybind.weapon_3.name = senjata_3 -keybind.weapon_4.name = senjata_4 -keybind.weapon_5.name = senjata_5 -keybind.weapon_6.name = senjata_6 -mode.waves.name = gelombang +keybind.toggle_menus.name = Toggle menus +keybind.chat_history_prev.name = Chat history prev +keybind.chat_history_next.name = Chat history next +keybind.chat_scroll.name = Chat scroll +keybind.drop_unit.name = drop unit +keybind.zoom_minimap.name = Zoom minimap +mode.help.title = Description of modes +mode.survival.name = Survival +mode.survival.description = The normal mode. Limited resources and automatic incoming waves. mode.sandbox.name = sandbox -mode.freebuild.name = freebuild -upgrade.standard.name = standar -upgrade.standard.description = Robot standar. -upgrade.blaster.name = blaster -upgrade.blaster.description = Menembakan sebuah peluru yang lemah dan lambat. -upgrade.triblaster.name = triblaster -upgrade.triblaster.description = Menembakan 3 peluru secara menyebar. -upgrade.clustergun.name = clustergun -upgrade.clustergun.description = Menembakan sebuah granat eksplosif yang tidak akurat. -upgrade.beam.name = meriam sinar -upgrade.beam.description = Menembakan sinar laser jarak jauh. -upgrade.vulcan.name = vulcan -upgrade.vulcan.description = Menembakkan rombongan peluru dengan cepat. -upgrade.shockgun.name = shockgun -upgrade.shockgun.description = Menembakkan ledakan yang menghancurkan dari pecahan peluru yang terisi. -item.stone.name = batu -item.iron.name = besi +mode.sandbox.description = infinite resources and no timer for waves. +mode.pvp.name = PvP +mode.pvp.description = fight against other players locally. +mode.attack.name = Attack +mode.attack.description = No waves, with the goal to destroy the enemy base. +mode.custom = Custom Rules +rules.infiniteresources = Infinite Resources +rules.wavetimer = Wave Timer +rules.waves = Waves +rules.enemyCheat = Infinite AI Resources +rules.unitdrops = Unit Drops +rules.unitbuildspeedmultiplier = Unit Creation Speed Multiplier +rules.unithealthmultiplier = Unit Health Multiplier +rules.playerhealthmultiplier = Player Health Multiplier +rules.playerdamagemultiplier = Player Damage Multiplier +rules.unitdamagemultiplier = Unit Damage Multiplier +rules.enemycorebuildradius = Enemy Core No-Build Radius:[LIGHT_GRAY] (tiles) +rules.respawntime = Respawn Time:[LIGHT_GRAY] (sec) +rules.wavespacing = Wave Spacing:[LIGHT_GRAY] (sec) +rules.buildcostmultiplier = Build Cost Multiplier +rules.buildspeedmultiplier = Build Speed Multiplier +rules.waitForWaveToEnd = Waves wait for enemies +rules.dropzoneradius = Drop Zone Radius:[LIGHT_GRAY] (tiles) +rules.respawns = Max respawns per wave +rules.limitedRespawns = Limit Respawns +rules.title.waves = Waves +rules.title.respawns = Respawns +rules.title.resourcesbuilding = Resources & Building +rules.title.player = Players +rules.title.enemy = Enemies +rules.title.unit = Units +content.item.name = Items +content.liquid.name = Liquids +content.unit.name = Units +content.block.name = Blocks +content.mech.name = Mechs +item.copper.name = Copper +item.copper.description = A useful structure material. Used extensively in all types of blocks. +item.lead.name = Lead +item.lead.description = A basic starter material. Used extensively in electronics and liquid transportation blocks. item.coal.name = batu bara -item.steel.name = baja +item.coal.description = A common and readily available fuel. +item.graphite.name = Graphite item.titanium.name = titanium -item.dirium.name = dirium -item.uranium.name = uranium +item.titanium.description = A rare super-light metal used extensively in liquid transportation, drills and aircraft. +item.thorium.name = thorium +item.thorium.description = A dense, radioactive metal used as structural support and nuclear fuel. +item.silicon.name = Silicon +item.silicon.description = An extremely useful semiconductor, with applications in solar panels and many complex electronics. +item.plastanium.name = Plastanium +item.plastanium.description = A light, ductile material used in advanced aircraft and fragmentation ammunition. +item.phase-fabric.name = Phase Fabric +item.phase-fabric.description = A near-weightless substance used in advanced electronics and self-repairing technology. +item.surge-alloy.name = Surge Alloy +item.surge-alloy.description = An advanced alloy with unique electrical properties. +item.spore-pod.name = Spore Pod +item.spore-pod.description = Used for conversion into oil, explosives and fuel. item.sand.name = pasir +item.sand.description = A common material that is used extensively in smelting, both in alloying and as a flux. +item.blast-compound.name = Blast Compound +item.blast-compound.description = A volatile compound used in bombs and explosives. While it can burned as fuel, this is not advised. +item.pyratite.name = Pyratite +item.pyratite.description = An extremely flammable substance used in incendiary weapons. +item.metaglass.name = Metaglass +item.metaglass.description = A super-tough glass compound. Extensively used for liquid distribution and storage. +item.scrap.name = Scrap +item.scrap.description = Leftover remnants of old structures and units. Contains trace amounts of many different metals. liquid.water.name = air -liquid.plasma.name = plasma -liquid.lava.name = lahar +liquid.slag.name = Slag liquid.oil.name = minyak -block.weaponfactory.name = pabrik senjata -block.weaponfactory.fulldescription = Dipakai untuk membuat senjata bagi robot pemain. Klik untuk memakai. Otomatis mengambil sumber daya dari inti. -block.air.name = udara -block.blockpart.name = bagian blok -block.deepwater.name = air dangkal -block.water.name = air -block.lava.name = lahar -block.oil.name = minyak -block.stone.name = batu -block.blackstone.name = batu hitam -block.iron.name = besi -block.coal.name = batu bara -block.titanium.name = titanium -block.uranium.name = uranium -block.dirt.name = tanah -block.sand.name = pasir -block.ice.name = es -block.snow.name = salju -block.grass.name = rumput -block.sandblock.name = blok pasir -block.snowblock.name = blok salju -block.stoneblock.name = blok batu -block.blackstoneblock.name = blok batu hitam -block.grassblock.name = blok rumput -block.mossblock.name = blok lumut -block.shrub.name = belukar -block.rock.name = batu -block.icerock.name = batu es -block.blackrock.name = batu hitam -block.dirtblock.name = blok tanah -block.stonewall.name = dinding batu -block.stonewall.fulldescription = Sebuah blok defensif yang murah. Berguna untuk melindungi inti dan turret di beberapa gelombang pertama. -block.ironwall.name = dinding besi -block.ironwall.fulldescription = Blok defensif dasar. Menyediakan perlindungan dari musuh. -block.steelwall.name = dinding baja -block.steelwall.fulldescription = Sebuah blok defensif standar. Perlindungan yang memadai dari musuh. -block.titaniumwall.name = dinding titanium -block.titaniumwall.fulldescription = Blok pertahanan yang kuat. Menyediakan perlindungan dari musuh. -block.duriumwall.name = dinding dirium -block.duriumwall.fulldescription = Blok pertahanan yang sangat kuat. Menyediakan perlindungan dari musuh. -block.compositewall.name = dinding komposit -block.steelwall-large.name = dinding baja besar -block.steelwall-large.fulldescription = Sebuah blok defensif standar. Membentang beberapa ubin. -block.titaniumwall-large.name = dinding titanium besar -block.titaniumwall-large.fulldescription = Blok pertahanan yang kuat. Membentang beberapa ubin. -block.duriumwall-large.name = dinding dirium yang besar -block.duriumwall-large.fulldescription = Blok pertahanan yang sangat kuat. Membentang beberapa ubin. -block.titaniumshieldwall.name = dinding perisai -block.titaniumshieldwall.fulldescription = Sebuah blok defensif yang kuat, dengan tambahan perisai. Membutuhkan tenaga. Menggunakan energi untuk menyerap peluru musuh. Dianjurkan untuk menggunakan pemercepat tenaga untuk memberi energi pada blok ini. -block.repairturret.name = turret perbaikan -block.repairturret.fulldescription = Memperbaiki blok terdekat yang rusak dengan lambat. Menggunakan sedikit tenaga. -block.megarepairturret.name = perbaikan turret II -block.megarepairturret.fulldescription = Memperbaiki blok yang rusak dengan normal. Menggunakan tenaga. -block.shieldgenerator.name = pembangkit perisai -block.shieldgenerator.fulldescription = Blok defensif yang maju. Mellindungi semua blok dalam radius dari serangan. Menggunakan tenaga dengan lambat saat menganggur, namun menyalurkan energi dengan cepat pada kontak peluru. +liquid.cryofluid.name = Cryofluid +mech.alpha-mech.name = Alpha +mech.alpha-mech.weapon = Heavy Repeater +mech.alpha-mech.ability = Drone Swarm +mech.alpha-mech.description = The standard mech. Has decent speed and damage output; can create up to 3 drones for increased offensive capability. +mech.delta-mech.name = Delta +mech.delta-mech.weapon = Arc Generator +mech.delta-mech.ability = Discharge +mech.delta-mech.description = A fast, lightly-armored mech made for hit-and-run attacks. Does little damage against structures, but can kill large groups of enemy units very quickly with its arc lightning weapons. +mech.tau-mech.name = Tau +mech.tau-mech.weapon = Restruct Laser +mech.tau-mech.ability = Repair Burst +mech.tau-mech.description = The support mech. Heals allied blocks by shooting at them. Can extinguish fires and heal allies in a radius with its repair ability. +mech.omega-mech.name = Omega +mech.omega-mech.weapon = Swarm Missiles +mech.omega-mech.ability = Armored Configuration +mech.omega-mech.description = A bulky and well-armored mech, made for front-line assaults. Its armor ability can block up to 90% of incoming damage. +mech.dart-ship.name = Dart +mech.dart-ship.weapon = Repeater +mech.dart-ship.description = The standard ship. Reasonably fast and light, but has little offensive capability and low mining speed. +mech.javelin-ship.name = Javelin +mech.javelin-ship.description = A hit-and-run strike ship. While initially slow, it can accelerate to great speeds and fly by enemy outposts, dealing large amounts of damage with its lightning ability and missiles. +mech.javelin-ship.weapon = Burst Missiles +mech.javelin-ship.ability = Discharge Booster +mech.trident-ship.name = Trident +mech.trident-ship.description = A heavy bomber. Reasonably well armored. +mech.trident-ship.weapon = Bomb Bay +mech.glaive-ship.name = Glaive +mech.glaive-ship.description = A large, well-armored gunship. Equipped with an incendiary repeater. Good acceleration and maximum speed. +mech.glaive-ship.weapon = Flame Repeater +item.explosiveness = [LIGHT_GRAY]Explosiveness: {0} +item.flammability = [LIGHT_GRAY]Flammability: {0} +item.radioactivity = [LIGHT_GRAY]Radioactivity: {0} +unit.health = [LIGHT_GRAY]Health: {0} +unit.speed = [LIGHT_GRAY]Speed: {0} +mech.weapon = [LIGHT_GRAY]Weapon: {0} +mech.health = [LIGHT_GRAY]Health: {0} +mech.itemcapacity = [LIGHT_GRAY]Item Capacity: {0} +mech.minespeed = [LIGHT_GRAY]Mining Speed: {0} +mech.minepower = [LIGHT_GRAY]Mining Power: {0} +mech.ability = [LIGHT_GRAY]Ability: {0} +mech.buildspeed = [LIGHT_GRAY]Building Speed: {0}% +liquid.heatcapacity = [LIGHT_GRAY]Heat Capacity: {0} +liquid.viscosity = [LIGHT_GRAY]Viscosity: {0} +liquid.temperature = [LIGHT_GRAY]Temperature: {0} +block.grass.name = Grass +block.salt.name = Salt +block.saltrocks.name = Salt Rocks +block.pebbles.name = Pebbles +block.tendrils.name = Tendrils +block.sandrocks.name = Sand Rocks +block.spore-pine.name = Spore Pine +block.sporerocks.name = Spore Rocks +block.rock.name = Rock +block.snowrock.name = Snow Rock +block.shale.name = Shale +block.shale-boulder.name = Shale Boulder +block.moss.name = Moss +block.shrubs.name = Shrubs +block.spore-moss.name = Spore Moss +block.shalerocks.name = Shale Rocks +block.scrap-wall.name = Scrap Wall +block.scrap-wall-large.name = Large Scrap Wall +block.scrap-wall-huge.name = Huge Scrap Wall +block.scrap-wall-gigantic.name = Gigantic Scrap Wall +block.thruster.name = Thruster +block.kiln.name = Kiln +block.kiln.description = Smelts sand and lead into metaglass. Requires small amounts of power. +block.graphite-press.name = Graphite Press +block.multi-press.name = Multi-Press +block.constructing = {0}\n[LIGHT_GRAY](Constructing) +block.spawn.name = Enemy Spawn +block.core-shard.name = Core: Shard +block.core-foundation.name = Core: Foundation +block.core-nucleus.name = Core: Nucleus +block.deepwater.name = deepwater +block.water.name = water +block.tainted-water.name = Tainted Water +block.darksand-tainted-water.name = Dark Sand Tainted Water +block.tar.name = Tar +block.stone.name = stone +block.sand.name = sand +block.darksand.name = Dark Sand +block.ice.name = ice +block.snow.name = snow +block.craters.name = Craters +block.sand-water.name = Sand water +block.darksand-water.name = Dark Sand Water +block.char.name = Char +block.holostone.name = Holo stone +block.ice-snow.name = Ice Snow +block.rocks.name = Rocks +block.icerocks.name = Ice rocks +block.snowrocks.name = Snow Rocks +block.dunerocks.name = Dune Rocks +block.pine.name = Pine +block.white-tree-dead.name = White Tree Dead +block.white-tree.name = White Tree +block.spore-cluster.name = Spore Cluster +block.metal-floor.name = Metal Floor +block.metal-floor-2.name = Metal Floor 2 +block.metal-floor-3.name = Metal Floor 3 +block.metal-floor-5.name = Metal Floor 5 +block.metal-floor-damaged.name = Metal Floor Damaged +block.dark-panel-1.name = Dark Panel 1 +block.dark-panel-2.name = Dark Panel 2 +block.dark-panel-3.name = Dark Panel 3 +block.dark-panel-4.name = Dark Panel 4 +block.dark-panel-5.name = Dark Panel 5 +block.dark-panel-6.name = Dark Panel 6 +block.dark-metal.name = Dark Metal +block.ignarock.name = Igna Rock +block.hotrock.name = Hot Rock +block.magmarock.name = Magma Rock +block.cliffs.name = Cliffs +block.copper-wall.name = Copper Wall +block.copper-wall-large.name = Large Copper Wall +block.titanium-wall.name = Titanium Wall +block.titanium-wall-large.name = Large Titanium Wall +block.phase-wall.name = Phase Wall +block.phase-wall-large.name = Large Phase Wall +block.thorium-wall.name = Thorium Wall +block.thorium-wall-large.name = Large Thorium Wall block.door.name = pintu -block.door.fulldescription = Blok yang bisa dibuka dan ditutup dengan mengetuknya. block.door-large.name = pintu besar -block.door-large.fulldescription = Blok yang bisa dibuka dan ditutup dengan mengetuknya. -block.conduit.name = saluran -block.conduit.fulldescription = Blok pengangkut cairan dasar. Bekerja seperti konveyor, tapi dengan cairan. Terbaik digunakan dengan pompa atau saluran lainnya. Bisa digunakan sebagai jembatan di atas cairan untuk musuh dan pemain. -block.pulseconduit.name = saluran cepat -block.pulseconduit.fulldescription = Blok pengangkut cairan tingkat lanjut. Mengangkut cairan lebih cepat dan menyimpan lebih banyak dari pada saluran standar. -block.liquidrouter.name = router cairan -block.liquidrouter.fulldescription = Bekerja seperti router. Menerima masukan cairan dari satu sisi dan mengeluarkannya ke sisi yang lain. Berguna untuk pemisahan cairan dari satu saluran ke beberapa saluran lainnya. +block.duo.name = Duo +block.scorch.name = Scorch +block.scatter.name = Scatter +block.hail.name = Hail +block.lancer.name = Lancer block.conveyor.name = konveyor -block.conveyor.fulldescription = Blok dasar pengangkut barang. Memindahkan barang ke depan dan secara otomatis menyimpannya ke turret, ekstraktor, dan pertambangan. Bisa diputar. Bisa digunakan sebagai jembatan di atas cairan untuk musuh dan pemain. -block.steelconveyor.name = konveyor baja -block.steelconveyor.fulldescription = Blok transportasi barang lanjutan. Memindahkan barang lebih cepat dari konveyor standar. -block.poweredconveyor.name = konveyor cepat -block.poweredconveyor.fulldescription = Blok terbaik untuk pengangkutan barang. Memindahkan barang lebih cepat dari konveyor baja. -block.router.name = router -block.router.fulldescription = Menerima item dari satu arah dan mengeluarkannya ke 3 arah. Bisa juga menyimpan sejumlah barang. Berguna untuk membelah bahan dari satu pertambangan ke beberapa turret. +block.titanium-conveyor.name = Titanium Conveyor block.junction.name = persimpangan jalan -block.junction.fulldescription = Bertindak sebagai jembatan untuk dua sabuk persimpangan. Berguna dalam situasi dengan dua konveyor berbeda yang membawa bahan berbeda ke lokasi yang berbeda. -block.conveyortunnel.name = terowongan konveyor -block.conveyortunnel.fulldescription = Memindahkan barang di bawah blok. Untuk menggunakan, tempatkan satu terowongan yang menuju ke terowongan di bawah blok, dan satu di sisi lain. Pastikan kedua terowongan menghadap ke arah yang berlawanan, yaitu menuju blok yang mereka masukkan atau keluarkan. -block.liquidjunction.name = persimpangan cairan -block.liquidjunction.fulldescription = Bertindak sebagai jembatan untuk dua saluran persimpangan. Berguna dalam situasi dengan dua saluran berbeda yang membawa cairan berbeda ke lokasi yang berbeda. -block.liquiditemjunction.name = persimpangan barang-cairan -block.liquiditemjunction.fulldescription = Bertindak sebagai jembatan untuk menyilang saluran dan konveyor. -block.powerbooster.name = pemercepat tenaga -block.powerbooster.fulldescription = Mendistribusikan tenaga ke semua blok dalam radiusnya. -block.powerlaser.name = laser tenaga -block.powerlaser.fulldescription = Membuat laser yang mentransmisikan daya ke blok di depannya. Tidak menghasilkan tenaga itu sendiri. Terbaik digunakan dengan generator atau laser lainnya. -block.powerlaserrouter.name = router laser -block.powerlaserrouter.fulldescription = Laser yang mendistribusikan tenaga ke tiga arah sekaligus. Berguna dalam situasi di mana diperlukan tenaga ke beberapa blok dari satu generator. -block.powerlasercorner.name = sudut laser -block.powerlasercorner.fulldescription = Laser yang mendistribusikan tenaga ke dua arah sekaligus. Berguna dalam situasi di mana diperlukan tenaga ke beberapa blok dari satu generator, dan arah router kurang tepat. -block.teleporter.name = teleporter -block.teleporter.fulldescription = Blok transportasi barang lanjutan. Teleporter memasukkan barang ke teleporter lain dengan warna yang sama. Tidak ada apa-apa jika tidak ada teleporter dengan warna yang sama. Jika beberapa teleporter memiliki warna yang sama, teleporter dipilih secara acak. Menggunakan tenaga. Ketuk/klik untuk mengubah warna. +block.router.name = router +block.distributor.name = Distributor block.sorter.name = penyortir -block.sorter.fulldescription = Menyortir barang menurut jenis bahannya. Bahan yang diterima ditandai dengan warna di blok. Semua item yang sesuai dengan jenis bahan dilepaskan ke depan, segala sesuatu yang lain dikeluarkan ke kiri dan kanan. -block.core.name = inti -block.pump.name = pompa -block.pump.fulldescription = Memompa cairan dari sumber blok- biasanya air, lahar atau minyak. Mengeluarkan cairan ke saluran terdekat. -block.fluxpump.name = pompa flux -block.fluxpump.fulldescription = Sebuah versi lanjutan dari pompa. Menyimpan lebih banyak cairan dan memompa cairan lebih cepat. -block.smelter.name = peleburan -block.smelter.fulldescription = Blok kerajinan esensial. Saat dimasukkan 1 besi dan 1 batu bara sebagai bahan bakar, akan mengeluarkan satu baja. Disarankan untuk memasukkan besi dan batu bara ke sabuk yang berbeda untuk mencegah penyumbatan. -block.crucible.name = peleburan dirium -block.crucible.fulldescription = Sebuah blok kerajinan yang maju. Saat dimasukkan 1 titanium, 1 baja dan 1 batu bara sebagai bahan bakar, mengeluarkan satu dirium. Disarankan untuk memasukkan batubara, baja dan titanium pada sabuk yang berbeda untuk mencegah penyumbatan. -block.coalpurifier.name = ekstraktor batubara -block.coalpurifier.fulldescription = Blok ekstraktor dasar. mengeluarkan batu bara saat dipasok dengan air dan batu dalam skala yang besar. -block.titaniumpurifier.name = ekstraktor titanium -block.titaniumpurifier.fulldescription = Blok ekstraktor standar. mengeluarkan titanium bila dipasok dengan air dan besi dalam skala yang besar. -block.oilrefinery.name = penyulingan minyak -block.oilrefinery.fulldescription = Menyuling sejumlah minyak menjadi batubara. Berguna untuk memasok turret berbasis batubara saat penambangan batubara langka. -block.stoneformer.name = pembentuk batu -block.stoneformer.fulldescription = Mengubah lahar ke dalam batu. Berguna untuk menghasilkan batu dalam jumlah besar untuk pemurni batu bara. -block.lavasmelter.name = peleburan lava -block.lavasmelter.fulldescription = Menggunakan lahar untuk mengubah besi menjadi baja. Sebuah alternatif untuk peleburan batubara. Berguna dalam situasi di mana pertambangan batubara langka. -block.stonedrill.name = pertambangan batu -block.stonedrill.fulldescription = Pertambangan penting. Saat diletakkan di atas ubin batu, akan menghasilkan batu pada kecepatan yang lambat tanpa batas waktu. -block.irondrill.name = pertambangan besi -block.irondrill.fulldescription = Pertambangan dasar. Saat diletakkan di atas ubin bijih besi, akan mengeluarkan besi pada kecepatan yang lambat tanpa batas waktu. -block.coaldrill.name = pertambangan batubara -block.coaldrill.fulldescription = Pertambangan dasar. Saat ditempatkan di ubin bijih batubara, akan mengeluarkan batu bara pada kecepatan yang lambat tanpa batas waktu. -block.uraniumdrill.name = pertambangan uranium -block.uraniumdrill.fulldescription = Sebuah pertambangan yang canggih. Saat ditempatkan di ubin bijih uranium, akan mengeluarkan uranium pada kecepatan lambat tanpa batas waktu. -block.titaniumdrill.name = pertambangan titanium -block.titaniumdrill.fulldescription = Sebuah pertambangan yang canggih. Saat ditempatkan pada ubin bijih titanium, akan mengeluarkan titanium pada kecepatan lambat tanpa batas waktu. -block.omnidrill.name = pertambangan super -block.omnidrill.fulldescription = Pertambangan yang terbaik. Akan saya tambang bijih apapun itu ditempatkan pada kecepatan tinggi. -block.coalgenerator.name = pembangkit tenaga batubara -block.coalgenerator.fulldescription = Generator penting. Menghasilkan tenaga dari batu bara. Keluarkan tenaga sebagai laser ke 4 sisinya. -block.thermalgenerator.name = pembangkit tenaga panas -block.thermalgenerator.fulldescription = Menghasilkan tenaga dari lahar. Mengeluarkan tenaga sebagai laser ke 4 sisi. -block.combustiongenerator.name = pembangkit tenaga minyak -block.combustiongenerator.fulldescription = Menghasilkan tenaga dari minyak. Mengeluarkan tenaga sebagai laser ke 4 sisi. -block.rtgenerator.name = pembangkit tenaga radioaktif -block.rtgenerator.fulldescription = Menghasilkan sedikit tenaga dari peluruhan radioaktif uranium. Mengeluarkan tenaga sebagai laser ke 4 sisi. -block.nuclearreactor.name = reaktor nuklir -block.nuclearreactor.fulldescription = Versi lanjutan Pembangkit Tenaga Radioaktif, dan generator tenaga tertinggi. Menghasilkan tenaga dari uranium. Membutuhkan pendinginan air konstan. Sangat mudah menguap; akan meledak dengan hebat jika tidak cukup jumlah pendingin yang diberikan. -block.turret.name = turret -block.turret.fulldescription = Sebuah menara dasar yang murah. Menggunakan batu untuk amunisi. Memiliki jangkauan yang sedikit lebih banyak daripada turret ganda. -block.doubleturret.name = turret ganda -block.doubleturret.fulldescription = Versi turret standar yang sedikit lebih bertenaga. Menggunakan batu untuk amunisi. Memberikan damage secara signifikan lebih banyak, namun memiliki jangkauan yang lebih rendah. Menembak dua peluru. -block.machineturret.name = turret cepat -block.machineturret.fulldescription = Sebuah menara standar. Menggunakan besi untuk amunisi. Memiliki tembakan yang cepat dengan damage yang layak. -block.shotgunturret.name = turret split -block.shotgunturret.fulldescription = Sebuah turret standar. Menggunakan besi untuk amunisi. Menembakkan 7 peluru. Jaraknya pendek, namun damage-nya lebih tinggi daripada turret cepat. -block.flameturret.name = turret api -block.flameturret.fulldescription = Turret jarak dekat lanjutan. Menggunakan batubara untuk amunisi. Memiliki jangkauan yang pendek, namun sangat tinggi damage-nya. Bagus untuk jarak dekat. Dianjurkan untuk digunakan dibalik dinding. -block.sniperturret.name = turret railgun -block.sniperturret.fulldescription = Turret jarak jauh lanjutan. Menggunakan baja untuk amunisi. Kerusakan yang sangat tinggi, namun menembak dengan lambat. Mahal untuk digunakan, tapi bisa ditempatkan jauh dari garis musuh karena jangkauannya. -block.mortarturret.name = turret flak -block.mortarturret.fulldescription = Turret dengan akurasi pendek dan damage eksplosif. Menggunakan batubara untuk amunisi. Menembakkan peluru yang meledak lalu menjadi pecahan peluru. Berguna untuk kerumunan musuh yang besar. -block.laserturret.name = turret laser -block.laserturret.fulldescription = Turret satu target. Menggunakan tenaga. Memiliki jarak sedang yang bagus. Target tunggal saja. Tidak pernah meleset. -block.waveturret.name = turret tesla -block.waveturret.fulldescription = Turret target banyak. Menggunakan tenaga. Jaraknya sedang. Tidak pernah meleset. Rata-rata damage-nya kecil, namun bisa menembak beberapa musuh bersamaan dengan petir berantai. -block.plasmaturret.name = turret plasma -block.plasmaturret.fulldescription = Versi yang sangat maju dari turret api. Menggunakan batubara sebagai amunisi. Damage yang sangat tinggi, jaraknya pendek sampai sedang. -block.chainturret.name = turret berantai -block.chainturret.fulldescription = Menara api yang menembak dengan cepat. Menggunakan uranium sebagai amunisi. Menembak peluru besar dengan kecepatan tinggi. Jaraknya sedang. Membentang beberapa ubin. Sangat tangguh. -block.titancannon.name = meriam titan -block.titancannon.fulldescription = Turret jarak jauh terakhir. Menggunakan uranium sebagai amunisi. Menembakkan peluru yang meledak dengan cipratan besar dengan kecepatan sedang. Jarak jauh. Membentang beberapa ubin. Sangat tangguh. -block.playerspawn.name = spawn pemain -block.enemyspawn.name = spawn musuh \ No newline at end of file +block.sorter.description = Sorts items. If an item matches the selection, it is allowed to pass. Otherwise, the item is outputted to the left and right. +block.overflow-gate.name = Overflow Gate +block.overflow-gate.description = A combination splitter and router that only outputs to the left and right if the front path is blocked. +block.silicon-smelter.name = Silicon Smelter +block.phase-weaver.name = Phase Weaver +block.pulverizer.name = Pulverizer +block.cryofluidmixer.name = Cryofluid Mixer +block.melter.name = Melter +block.incinerator.name = Incinerator +block.spore-press.name = Spore Press +block.separator.name = Separator +block.coal-centrifuge.name = Coal Centrifuge +block.power-node.name = Power Node +block.power-node-large.name = Large Power Node +block.surge-tower.name = Surge Tower +block.battery.name = Battery +block.battery-large.name = Large Battery +block.combustion-generator.name = Combustion Generator +block.turbine-generator.name = Turbine Generator +block.differential-generator.name = Differential Generator +block.impact-reactor.name = Impact Reactor +block.mechanical-drill.name = Mechanical Drill +block.pneumatic-drill.name = Pneumatic Drill +block.laser-drill.name = Laser Drill +block.water-extractor.name = Water Extractor +block.cultivator.name = Cultivator +block.dart-mech-pad.name = Dart Mech Pad +block.delta-mech-pad.name = Delta Mech Pad +block.javelin-ship-pad.name = Javelin Ship Pad +block.trident-ship-pad.name = Trident Ship Pad +block.glaive-ship-pad.name = Glaive Ship Pad +block.omega-mech-pad.name = Omega Mech Pad +block.tau-mech-pad.name = Tau Mech Pad +block.conduit.name = saluran +block.mechanical-pump.name = Mechanical Pump +block.item-source.name = Item Source +block.item-void.name = Item Void +block.liquid-source.name = Liquid Source +block.power-void.name = Power Void +block.power-source.name = Power Infinite +block.unloader.name = Unloader +block.vault.name = Vault +block.wave.name = Wave +block.swarmer.name = Swarmer +block.salvo.name = Salvo +block.ripple.name = Ripple +block.phase-conveyor.name = Phase Conveyor +block.bridge-conveyor.name = Bridge Conveyor +block.plastanium-compressor.name = Plastanium Compressor +block.pyratite-mixer.name = Pyratite Mixer +block.blast-mixer.name = Blast Mixer +block.solar-panel.name = Solar Panel +block.solar-panel-large.name = Large Solar Panel +block.oil-extractor.name = Oil Extractor +block.spirit-factory.name = Spirit Drone Factory +block.phantom-factory.name = Phantom Drone Factory +block.wraith-factory.name = Wraith Fighter Factory +block.ghoul-factory.name = Ghoul Bomber Factory +block.dagger-factory.name = Dagger Mech Factory +block.crawler-factory.name = Crawler Mech Factory +block.titan-factory.name = Titan Mech Factory +block.fortress-factory.name = Fortress Mech Factory +block.revenant-factory.name = Revenant Fighter Factory +block.repair-point.name = Repair Point +block.pulse-conduit.name = Pulse Conduit +block.phase-conduit.name = Phase Conduit +block.liquid-router.name = Liquid Router +block.liquid-tank.name = Liquid Tank +block.liquid-junction.name = Liquid Junction +block.bridge-conduit.name = Bridge Conduit +block.rotary-pump.name = Rotary Pump +block.thorium-reactor.name = Thorium Reactor +block.mass-driver.name = Mass Driver +block.blast-drill.name = Blast Drill +block.thermal-pump.name = Thermal Pump +block.thermal-generator.name = Thermal Generator +block.alloy-smelter.name = Alloy Smtler +block.mender.name = Mender +block.mend-projector.name = Mend Projector +block.surge-wall.name = Surge Wall +block.surge-wall-large.name = Large Surge Wall +block.cyclone.name = Cyclone +block.fuse.name = Fuse +block.shock-mine.name = Shock Mine +block.overdrive-projector.name = Overdrive Projector +block.force-projector.name = Force Projector +block.arc.name = Arc +block.rtg-generator.name = RTG Generator +block.spectre.name = Spectre +block.meltdown.name = Meltdown +block.container.name = Container +block.launch-pad.name = Launch Pad +block.launch-pad.description = Launches batches of items without any need for a core launch. Unfinished. +block.launch-pad-large.name = Large Launch Pad +team.blue.name = blue +team.red.name = red +team.orange.name = orange +team.none.name = gray +team.green.name = green +team.purple.name = purple +unit.spirit.name = Spirit Drone +unit.spirit.description = The starter drone unit. Spawns in the core by default. Automatically mines ores, collects items and repairs blocks. +unit.phantom.name = Phantom Drone +unit.phantom.description = An advanced drone unit. Automatically mines ores, collects items and repairs blocks. Significantly more effective than a drone. +unit.dagger.name = Dagger +unit.dagger.description = A basic ground unit. Useful in swarms. +unit.crawler.name = Crawler +unit.titan.name = Titan +unit.titan.description = An advanced armored ground unit. Uses carbide as ammo. Attacks both ground and air targets. +unit.ghoul.name = Ghoul Bomber +unit.ghoul.description = A heavy carpet bomber. Uses blast compound or pyratite as ammo. +unit.wraith.name = Wraith Fighter +unit.wraith.description = A fast, hit-and-run interceptor unit. +unit.fortress.name = Fortress +unit.fortress.description = A heavy artillery ground unit. +unit.revenant.name = Revenant +unit.eruptor.name = Eruptor +unit.chaos-array.name = Chaos Array +unit.eradicator.name = Eradicator +unit.lich.name = Lich +unit.reaper.name = Reaper +tutorial.begin = Your mission here is to eradicate the[LIGHT_GRAY] enemy[].\n\nBegin by[accent] mining copper[]. Tap a copper ore vein near your core to do this. +tutorial.drill = Mining manually is inefficient.\n[accent]Drills []can mine automatically.\nPlace one on a copper vein. +tutorial.conveyor = [accent]Conveyors[] are used to transport items to the core.\nMake a line of conveyors from the drill to the core. +tutorial.morecopper = More copper is required.\n\nEither mine it manually, or place more drills. +tutorial.turret = Defensive structures must be built to repel the[LIGHT_GRAY] enemy[].\nBuild a duo turret near your base. +tutorial.drillturret = Duo turrets require[accent] copper ammo []to shoot.\nPlace a drill next to the turret to supply it with mined copper. +tutorial.waves = The[LIGHT_GRAY] enemy[] approaches.\n\nDefend your core for 2 waves. Build more turrets. +tutorial.lead = More ores are available. Explore and mine[accent] lead[].\n\nDrag from your unit to the core to transfer resources. +tutorial.smelter = Copper and lead are weak metals.\nSuperior[accent] Dense Alloy[] can be created in a smelter.\n\nBuild one. +tutorial.densealloy = The smelter will now produce alloy.\nGet some.\nImprove the production if necessary. +tutorial.siliconsmelter = The core will now create a[accent] spirit drone[] for mining and repairing blocks.\n\nFactories for other units can be created with [accent] silicon.\nMake a silicon smelter. +tutorial.silicondrill = Silicon requires[accent] coal[] and[accent] sand[].\nStart by making drills. +tutorial.generator = This technology requires power.\nCreate a[accent] combustion generator[] for it. +tutorial.generatordrill = Combustion generators need fuel.\nFuel it with coal from a drill. +tutorial.node = Power requires transport.\nCreate a[accent] power node[] next to your combustion generator to transfer its power. +tutorial.nodelink = Power can be transferred through contacting power blocks and generators, or by linked power nodes.\n\nLink power by tapping the node and selecting the generator and silicon smelter. +tutorial.silicon = Silicon is being produced. Get some.\n\nImproving the production system is advised. +tutorial.daggerfactory = Construct a[accent] dagger mech factory.[]\n\nThis will be used to create attack mechs. +tutorial.router = Factories need resources to function.\nCreate a router to split conveyor resources. +tutorial.dagger = Link power nodes to the factory.\nOnce requirements are met, a mech will be created.\n\nCreate more drills, generators and conveyors as necessary. +tutorial.battle = The[LIGHT_GRAY] enemy[] has revealed their core.\nDestroy it with your unit and dagger mechs. +block.copper-wall.description = A cheap defensive block.\nUseful for protecting the core and turrets in the first few waves. +block.copper-wall-large.description = A cheap defensive block.\nUseful for protecting the core and turrets in the first few waves.\nSpans multiple tiles. +block.thorium-wall.description = A strong defensive block.\nGood protection from enemies. +block.thorium-wall-large.description = A strong defensive block.\nGood protection from enemies.\nSpans multiple tiles. +block.phase-wall.description = Not as strong as a thorium wall but will deflect bullets unless they are too powerful. +block.phase-wall-large.description = Not as strong as a thorium wall but will deflect bullets unless they are too powerful.\nSpans multiple tiles. +block.surge-wall.description = The strongest defensive block.\nHas a small chance of triggering lightning towards the attacker. +block.surge-wall-large.description = The strongest defensive block.\nHas a small chance of triggering lightning towards the attacker.\nSpans multiple tiles. +block.door.description = A small door that can be opened and closed by tapping on it.\nIf opened, enemies can shoot and move through. +block.door-large.description = A large door that can be opened and closed by tapping on it.\nIf opened, enemies can shoot and move through.\nSpans multiple tiles. +block.mend-projector.description = Periodically heals buildings in its vicinity. +block.overdrive-projector.description = Increases the speed of nearby buildings like drills and conveyors. +block.force-projector.description = Creates a hexagonal force field around itself, protecting buildings and units inside from damage through bullets. +block.shock-mine.description = Damages enemies stepping on the mine. Nearly invisible to the enemy. +block.duo.description = A small, cheap turret. +block.scatter.description = A medium-sized anti-air turret. Sprays clumps of lead or scrap flak at enemy units. +block.arc.description = A small turret which shoots electricity in a random arc towards the enemy. +block.hail.description = A small artillery turret. +block.lancer.description = A medium-sized turret which shoots charged electricity beams. +block.wave.description = A medium-sized rapid-fire turret which shoots liquid bubbles. +block.salvo.description = A medium-sized turret which fires shots in salvos. +block.swarmer.description = A medium-sized turret which shoots burst missiles. +block.ripple.description = A large artillery turret which fires several shots simultaneously. +block.cyclone.description = A large rapid fire turret. +block.fuse.description = A large turret which shoots powerful short-range beams. +block.spectre.description = A large turret which shoots two powerful bullets at once. +block.meltdown.description = A large turret which shoots powerful long-range beams. +block.conveyor.description = Basic item transport block. Moved items forward and automatically deposits them into turrets or crafters. Rotatable. +block.titanium-conveyor.description = Advanced item transport block. Moves items faster than standard conveyors. +block.phase-conveyor.description = Advanced item transport block. Uses power to teleport items to a connected phase conveyor over several tiles. +block.junction.description = Acts as a bridge for two crossing conveyor belts. Useful in situations with two different conveyors carrying different materials to different locations. +block.mass-driver.description = Ultimate item transport block. Collects several items and then shoots them to another mass driver over a long range. +block.silicon-smelter.description = Reduces sand with highly pure coke in order to produce silicon. +block.plastanium-compressor.description = Produces plastanium from oil and titanium. +block.phase-weaver.description = Produces phase fabric from radioactive thorium and high amounts of sand. +block.alloy-smelter.description = Produces surge alloy from titanium, lead, silicon and copper. +block.pulverizer.description = Crushes stone into sand. Useful when there is a lack of natural sand. +block.pyratite-mixer.description = Mixes coal, lead and sand into highly flammable pyratite. +block.blast-mixer.description = Uses oil for transforming pyratite into the less flammable but more explosive blast compound. +block.cryofluidmixer.description = Combines water and titanium into cryofluid which is much more efficient for cooling. +block.melter.description = Heats up stone to very high temperatures to obtain lava. +block.incinerator.description = Gets rid of any excess item or liquid. +block.spore-press.description = Compresses spore pods into oil. +block.separator.description = Exposes stone to water pressure in order to obtain various minerals contained in the stone. +block.power-node.description = Transmits power to connected nodes. Up to four power sources, sinks or nodes can be connected. The node will receive power from or supply power to any adjacent blocks. +block.power-node-large.description = Has a larger radius than the power node and connects to up to six power sources, sinks or nodes. +block.battery.description = Stores power whenever there is an abundance and provides power whenever there is a shortage, as long as there is capacity left. +block.battery-large.description = Stores much more power than a regular battery. +block.combustion-generator.description = Generates power by burning oil or flammable materials. +block.turbine-generator.description = More efficient than a combustion generator, but requires additional water. +block.thermal-generator.description = Generates a large amount of power from lava. +block.solar-panel.description = Provides a small amount of power from the sun. +block.solar-panel-large.description = Provides much better power supply than a standard solar panel, but is also much more expensive to build. +block.thorium-reactor.description = Generates huge amounts of power from highly radioactive thorium. Requires constant cooling. Will explode violently if insufficient amounts of coolant are supplied. +block.rtg-generator.description = A radioisotope thermoelectric generator which does not require cooling but provides less power than a thorium reactor. +block.unloader.description = Unloads items from a container, vault or core onto a conveyor or directly into an adjacent block. The type of item to be unloaded can be changed by tapping on the unloader. +block.container.description = Stores a small amount of items. Use it for creating buffers when there is a non-constant demand of materials. An[LIGHT_GRAY] unloader[] can be used to retrieve items from the container. +block.vault.description = Stores a large amount of items. Use it for creating buffers when there is a non-constant demand of materials. An[LIGHT_GRAY] unloader[] can be used to retrieve items from the vault. +block.mechanical-drill.description = A cheap drill. When placed on appropriate tiles, outputs items at a slow pace indefinitely. +block.pneumatic-drill.description = An improved drill which is faster and able to process harder materials by making use of air pressure. +block.laser-drill.description = Allows drilling even faster through laser technology, but requires power. Additionally, radioactive thorium can be retrieved with this drill. +block.blast-drill.description = The ultimate drill. Requires large amounts of power. +block.water-extractor.description = Extracts water from the ground. Use it when there is no lake nearby. +block.cultivator.description = Cultivates the soil with water in order to obtain biomatter. +block.oil-extractor.description = Uses large amounts of power in order to extract oil from sand. Use it when there is no direct source of oil nearby. +block.trident-ship-pad.description = Leave your current vessel and change into a reasonably well armored heavy bomber.\nUse the pad by double tapping while standing on it. +block.javelin-ship-pad.description = Leave your current vessel and change into a strong and fast interceptor with lightning weapons.\nUse the pad by double tapping while standing on it. +block.glaive-ship-pad.description = Leave your current vessel and change into a large, well-armored gunship.\nUse the pad by double tapping while standing on it. +block.tau-mech-pad.description = Leave your current vessel and change into a support mech which can heal friendly buildings and units.\nUse the pad by double tapping while standing on it. +block.delta-mech-pad.description = Leave your current vessel and change into a fast, lightly-armored mech made for hit-and-run attacks.\nUse the pad by double tapping while standing on it. +block.omega-mech-pad.description = Leave your current vessel and change into a bulky and well-armored mech, made for front-line assaults.\nUse the pad by double tapping while standing on it. +block.spirit-factory.description = Produces light drones which mine ore and repair blocks. +block.phantom-factory.description = Produces advanced drone units which are significantly more effective than a spirit drone. +block.wraith-factory.description = Produces fast, hit-and-run interceptor units. +block.ghoul-factory.description = Produces heavy carpet bombers. +block.dagger-factory.description = Produces basic ground units. +block.titan-factory.description = Produces advanced, armored ground units. +block.fortress-factory.description = Produces heavy artillery ground units. +block.revenant-factory.description = Produces heavy laser ground units. +block.repair-point.description = Continuously heals the closest damaged unit in its vicinity. +block.conduit.description = Basic liquid transport block. Works like a conveyor, but with liquids. Best used with extractors, pumps or other conduits. +block.pulse-conduit.description = Advanced liquid transport block. Transports liquids faster and stores more than standard conduits. +block.phase-conduit.description = Advanced liquid transport block. Uses power to teleport liquids to a connected phase conduit over several tiles. +block.liquid-router.description = Accepts liquids from one direction and outputs them to up to 3 other directions equally. Can also store a certain amount of liquid. Useful for splitting the liquids from one source to multiple targets. +block.liquid-tank.description = Stores a large amount of liquids. Use it for creating buffers when there is a non-constant demand of materials or as a safeguard for cooling vital blocks. +block.liquid-junction.description = Acts as a bridge for two crossing conduits. Useful in situations with two different conduits carrying different liquids to different locations. +block.bridge-conduit.description = Advanced liquid transport block. Allows transporting liquids over up to 3 tiles of any terrain or building. +block.mechanical-pump.description = A cheap pump with slow output, but no power consumption. +block.rotary-pump.description = An advanced pump which doubles up speed by using power. +block.thermal-pump.description = The ultimate pump. Three times as fast as a mechanical pump and the only pump which is able to retrieve lava. +block.router.description = Accepts items from one direction and outputs them to up to 3 other directions equally. Useful for splitting the materials from one source to multiple targets. +block.distributor.description = An advanced router which splits items to up to 7 other directions equally. +block.bridge-conveyor.description = Advanced item transport block. Allows transporting items over up to 3 tiles of any terrain or building. +block.item-source.description = Infinitely outputs items. Sandbox only. +block.liquid-source.description = Infinitely outputs liquids. Sandbox only. +block.item-void.description = Destroys any items which go into it without using power. Sandbox only. +block.power-source.description = Infinitely outputs power. Sandbox only. +block.power-void.description = Voids all power inputted into it. Sandbox only. +liquid.water.description = Commonly used for cooling machines and waste processing. +liquid.oil.description = Can be burnt, exploded or used as a coolant. +liquid.cryofluid.description = The most efficient liquid for cooling things down. diff --git a/core/assets/bundles/bundle_it.properties b/core/assets/bundles/bundle_it.properties new file mode 100644 index 0000000000..92e7fd5fe1 --- /dev/null +++ b/core/assets/bundles/bundle_it.properties @@ -0,0 +1,947 @@ +credits.text = Creato da [ROYAL]Anuken[] - [SKY]anukendev@gmail.com[]\n\n[GRAY](Nel caso non te ne sia accorto, la traduzione del gioco non è completa.\n Chi di dovere sta lavorando più velocemente possibile per completarla! Un aiutino non sarebbe male!) +credits = Crediti +contributors = Translators and Contributors +discord = Unisciti sul server discord di mindustry! +link.discord.description = la chatroom ufficiale del server discord di Mindustry +link.github.description = Codice sorgente del gioco +link.dev-builds.description = Build di sviluppo versioni instabili +link.trello.description = Scheda ufficiale trello per funzionalità pianificate +link.itch.io.description = pagina di itch.io con download per PC e versione web +link.google-play.description = Elenco di Google Play Store +link.wiki.description = wiki ufficiale di Mindustry +linkfail = Impossibile aprire il link! L'URL è stato copiato nella tua bacheca. +screenshot = Screenshot salvato a {0} +screenshot.invalid = Mappa troppo grossa, probabilmente non c'è abbastanza memoria libera. +gameover = Il nucleo è stato distrutto. +gameover.pvp = La squadra [accent] {0}[] ha vinto! +highscore = [YELLOW]Nuovo record! +stat.wave = Ondate sconfitte:[accent] {0} +stat.enemiesDestroyed = Nemici distrutti:[accent] {0} +stat.built = Costruzioni erette:[accent] {0} +stat.destroyed = Costruzioni distrutte:[accent] {0} +stat.deconstructed = Costruzioni smontate:[accent] {0} +stat.delivered = Riorse lanciate: +stat.rank = Livello finale: [accent]{0} +placeline = Hai appena selezionato un blocco.\nOra puoi[accent] piazzarne una linea[] eseguendo[accent] una lunga pressione[] e poi trascinando in ogni direzione.\nProva! +removearea = Hai appena selezionato la modalità distruzione.\nOra puoi[accent] rimuovere blocchi in una certa zona [] eseguendo[accent] una lunga pressione[] e poi trascinando in ogni direzione.\nProva! +launcheditems = [accent]Oggetti lanciati +map.delete = Sei sicuro di voler eliminare questa mappa"[accent]{0}[]"? +level.highscore = Miglior punteggio: [accent]{0} +level.select = Selezione del livello +level.mode = Modalità di gioco: +showagain = non mostrare più +coreattack = < Il nucleo è sotto attacco! > +nearpoint = [[ [scarlet]LACIA LA ZONA NEMICA IMMEDIATAMENTE[] ]\nautodistruzione imminente +outofbounds = [[ SEI FUORI DAL MONDO ]\n[]Auto-distruzione in {0} +database = Database nucleo +savegame = Salva +loadgame = Carica +joingame = Unisciti al gioco +addplayers = Aggiungi/rimuovi giocatori +customgame = Gioco personalizzato +newgame = Nuova partita +none = +minimap = Minimapa +close = Chiuso +quit = Esci +maps = Mappe +continue = Continua +maps.none = [LIGHT_GRAY]Nessuna mappa trovata! +about.button = Info +name = Nome: +noname = Scegli un [accent] nome[] prima di unirti. +filename = Nome file: +unlocked = Nuovo blocco scoperto! +completed = [accent]Completo +techtree = Albero scoperta +research.list = [LIGHT_GRAY]Ricerca: +research = Ricerca +researched = [LIGHT_GRAY]{0} cercati. +players = {0} giocatori online +players.single = {0} giocatori online +server.closing = [accent]Chiusura server ... +server.kicked.kick = Sei stato cacciato dal server! +server.kicked.serverClose = Server chiuso. +server.kicked.clientOutdated = Versione del client obsoleta! Aggiorna il tuo gioco! +server.kicked.serverOutdated = Server obsoleto! Chiedi all'host di aggiornare! +server.kicked.banned = Sei bannato da questo server. +server.kicked.recentKick = Sei stato cacciato di recente.\nAspetta prima di riconnetterti. +server.kicked.nameInUse = C'è già qualcuno con il tuo nome\nsu questo server. +server.kicked.nameEmpty = Il tuo nome deve contenere almeno un carattere. +server.kicked.idInUse = Sei già su questo server! Non è permesso connettersi con due account. +server.kicked.customClient = Questo server non supporta le build personalizzate. Scarica la versione ufficiale dal sito. +server.kicked.gameover = Game over! +host.info = Il pulsante [accent]hos [] ospita un server sulle porte [scarlet]6567[] e [scarlet]656.[] Chiunque sulla stessa [LIGHT_GRAY]connessione wifi o rete locale[] dovrebbe essere in grado di vedere il proprio server nel proprio elenco server.\n\n Se vuoi che le persone siano in grado di connettersi ovunque tramite IP, è richiesto il [accent]port forwarding[]. \n\n[LIGHT_GRAY]Nota: se qualcuno sta riscontrando problemi durante la connessione al gioco LAN, assicurati di aver consentito a Mindustry di accedere alla rete locale nelle impostazioni del firewall. +join.info = Qui è possibile inserire un [accent]IP del server[] a cui connettersi, o scoprire [accento]un server sulla rete locale[] disponibile.\n Sono supportati sia il multiplayer LAN che WAN. \n\n[LIGHT_GRAY]Nota: non esiste un elenco di server globali automatici; se si desidera connettersi a qualcuno tramite IP, è necessario chiedere all'host il proprio IP. +hostserver = Host Server +hostserver.mobile = Host\nServer +host = Host +hosting = [accent] Apertura del server ... +hosts.refresh = Aggiorna +hosts.discovering = Ricerca partite LAN +server.refreshing = Aggiornamento del server +hosts.none = [lightgray]Nessuna partita LAN trovata! +host.invalid = [scarlet]Impossibile connettersi all'host. +trace = Traccia giocatore +trace.playername = Nome del giocatore: [accent]{0} +trace.ip = IP: [accent]{0} +trace.id = ID univoco: [accent]{0} +trace.mobile = Mobile Client: [accent]{0} +trace.modclient = Client personalizzato: [accent]{0} +invalidid = ID client non valido! Invia una segnalazione di bug. +server.bans = Lista Ban +server.bans.none = Nessun giocatore bandito trovato! Finora tutto liscio. +server.admins = Amministratori +server.admins.none = Nessun amministratore trovato! +server.add = Aggiungi server +server.delete = Sei sicuro di voler eliminare questo server? +server.hostname = Host: {0} +server.edit = Modifica server +server.outdated = [crimson]Server obsoleto![] +server.outdated.client = [crimson]Client obsoleto![] +server.version = [lightgray]Versione: {0} +server.custombuild = [yellow] Costruzione personalizzata +confirmban = Sei sicuro di voler bandire questo giocatore? +confirmkick = Are you sure you want to kick this player? +confirmunban = Sei sicuro di voler riammettere questo giocatore? +confirmadmin = Sei sicuro di voler rendere questo giocatore un amministratore? +confirmunadmin = Sei sicuro di voler rimuovere lo stato di amministratore da questo giocatore? +joingame.title = Unisciti alla Partita +joingame.ip = IP: +disconnect = Disconnesso. +disconnect.data = Il mondo non si vuole caricare, mi dispiace! +connecting = [accent]Connessione in corso ... +connecting.data = [accent]Caricamento dei dati del mondo ... +server.port = Porta: +server.addressinuse = Indirizzo già in uso! +server.invalidport = Numero di porta non valido! +server.error = [crimson]Errore nell'hosting del server: [accent] {0} +save.old = Questo salvataggio è per una versione precedente di mindustry e non può attualmente essere utilizzato .\n\n[LIGHT_GRAY]La cvompatibilità con i salvataggi precedenti verrà abilitata nella versione definitiva di mindustry 4.0. +save.new = Nuovo Salvataggio +save.overwrite = Sei sicuro di voler sovrascrivere questo salvataggio? +overwrite = Sovrascrivi +save.none = Nessun salvataggio trovato! +saveload = [Accent]Salvataggio ... +savefail = Salvataggio del gioco non riuscito! +save.delete.confirm = Sei sicuro di voler eliminare questo salvataggio? +save.delete = Elimina +save.export = Esporta Salvataggio +save.import.invalid = [accent]Questo salvataggio non è valido! +save.import.fail = [crimson]Impossibile importare salvataggio: [accent]{0} +save.export.fail = [crimson]Impossibile esportare il salvataggio: [accent]{0} +save.import = Importa Salvataggio +save.newslot = Salva nome: +save.rename = Rinomina +save.rename.text = Nuovo nome: +selectslot = Seleziona un salvataggio. +slot = [accent]Slot {0} +save.corrupted = [orang]Salvataggio corrotto o non valido! +empty = +on = On +off = Off +save.autosave = Salvataggio automatico: {0} +save.map = Mappa: {0} +save.wave = Ondata: {0} +save.difficulty = Difficoltà: {0} +save.date = Ultimo salvataggio: {0} +save.playtime = Tempo di gioco: {0} +warning = Warning. +confirm = Conferma +delete = Elimina +ok = OK +open = Apri +customize = Customize +cancel = Annulla +openlink = Apri Link +copylink = Copia link +back = Indietro +quit.confirm = Sei sicuro di voler uscire? +changelog.title = Registro modifiche +changelog.loading = Ottenendo il registro delle modifiche ... +changelog.error.android = [accent]Nota che il registro delle modifiche non funziona su Android 4.4 e versioni precedenti! Ciò è dovuto a un bug interno di Android. +changelog.error.ios = [accent]Il registro delle modifiche non è ancora supportato su IoS +changelog.error = [scarlet]Errore durante il recupero del registro delle modifiche! Controlla la tua connessione Internet. +changelog.current = [yellow][[Current version] +changelog.latest = [accent][[Latest version] +loading = [accent]Caricamento in corso ... +saving = [accent]Salvando . . . +wave = [accent]Ondata {0} +wave.waiting = Ondata in {0} +wave.waveInProgress = [LIGHT_GRAY]Wave in progress +waiting = In attesa... +waiting.players = Waiting for players... +wave.enemies = [LIGHT_GRAY]{0} Enemies Remaining +wave.enemy = [LIGHT_GRAY]{0} Enemy Remaining +loadimage = Carica immagine +saveimage = Salva Immagine +unknown = Sconosciuto +custom = Personalizzato +builtin = Incluso +map.delete.confirm = Sei sicuro di voler eliminare questa mappa? Non potrai tornare indietro! +map.random = [accent]Mappa casuale +map.nospawn = Questa mappa non possiede un nucleo dove spawnare! Aggiungine uno nell'editor. +map.nospawn.pvp = Questa mappa non ha un nucleo nemico! Aggiungi un [SCARLET]nucleo rosso[] nell'editor per poter giocare. +map.nospawn.attack = Questa mappa non ha un nucleo nemico! Aggiungi un [SCARLET]nucleo rosso[] nell'editor per poter giocare. +map.invalid = Errore nel caricamento della mappa: file mappa corrotto o non valido. +editor.brush = Pennello +editor.openin = Apri nell'editor +editor.oregen = Generazione dei minerali +editor.oregen.info = Generazione dei minerali: +editor.mapinfo = Informazioni mappa +editor.author = Autore: +editor.description = Descrizione: +editor.waves = Ondate: +editor.rules = Rules: +editor.ingame = Edit In-Game +waves.title = Ondate +waves.remove = Rimuovi +waves.never = +waves.every = sempre +waves.waves = ondata/e +waves.perspawn = per spawn +waves.to = a +waves.boss = Boss +waves.preview = Anteprima +waves.edit = Modifica... +waves.copy = Copia negli appunti +waves.load = Caica dagli appunti +waves.invalid = Onde dagli appunti non valide. +waves.copied = Onde copiate. +editor.default = [LIGHT_GRAY] +edit = Modifica... +editor.name = Nome: +editor.spawn = Spawn Unit +editor.removeunit = Remove Unit +editor.teams = Squadre +editor.elevation = Elevazione +editor.errorload = Errore nel caricamento di:\n[accent]{0} +editor.errorsave = Errore nel salvataggio di:\n[accent]{0} +editor.errorimage = That's an image, not a map. Don't go around changing extensions expecting it to work.\n\nIf you want to import a legacy map, use the 'import legacy map' button in the editor. +editor.errorlegacy = This map is too old, and uses a legacy map format that is no longer supported. +editor.errorheader = This map file is either not valid or corrupt. +editor.errorname = Questa mappa è senza nome. +editor.update = Aggiorna +editor.randomize = Casualizza +editor.apply = Applica +editor.generate = Genera +editor.resize = Ridimensiona +editor.loadmap = Carica\nmappa +editor.savemap = Salva\nla mappa +editor.saved = Salvato! +editor.save.noname = La tua mappa non ha un nome! Impostane uno nelle informazioni della mappa. +editor.save.overwrite = La tua mappa sovrascrive quelle incluse! Imposta un nome diverso nelle informazioni della mappa. +editor.import.exists = [scarlet]Impossibile importare:[] esiste già una mappa chiamata '{0}' che non può essere svrascritta! +editor.import = Importando... +editor.importmap = Importa mappa +editor.importmap.description = Importa mappa preesistente +editor.importfile = Importa file +editor.importfile.description = Importa un file mappa esterno +editor.importimage = Importa mappa terreno +editor.importimage.description = Importa immagine esterna terreno +editor.export = Esportazione... +editor.exportfile = Esporta file +editor.exportfile.description = Esporta file mappa +editor.exportimage = Esporta immagine terreno +editor.exportimage.description = Esporta file immagine mappa +editor.loadimage = Carica\nimmagine +editor.saveimage = Salva\nImmagine +editor.unsaved = [scarlet]Hai modifiche non salvate![]\nSei sicuro di voler uscire? +editor.resizemap = Ridimensiona la mappa +editor.mapname = Nome Mappa: +editor.overwrite = [Accent]Attenzione!\nQuesto sovrascrive una mappa esistente. +editor.overwrite.confirm = [scarlet]Attenzione![] Una mappa con questo nome esiste già. Sei sicuro di volerla sovrascrivere? +editor.selectmap = Seleziona una mappa da caricare: +filters.empty = [LIGHT_GRAY]No filters! Add one with the button below. +filter.distort = Modifica +filter.noise = Interferenza +filter.ore = Minerali +filter.rivernoise = Interferenze a fiume +filter.scatter = Dispersione +filter.terrain = Terreno +filter.option.scale = Scala +filter.option.chance = Probabilità +filter.option.mag = Magnitudine +filter.option.threshold = Threshold +filter.option.circle-scale = Circle Scale +filter.option.octaves = Octaves +filter.option.falloff = Falloff +filter.option.block = Block +filter.option.floor = Floor +filter.option.wall = Wall +filter.option.ore = Ore +filter.option.floor2 = Secondary Floor +filter.option.threshold2 = Secondary Threshold +filter.option.radius = Radius +filter.option.percentile = Percentile +width = Larghezza: +height = Altezza: +menu = Menu +play = Gioca +load = Carica +save = Salva +fps = FPS: {0} +tps = TPS: {0} +ping = Ping: {0}ms +language.restart = Riavvia il gioco affinché il cambiamento della lingua abbia effetto. +settings = Impostazioni +tutorial = Tutorial +editor = Editor +mapeditor = Editor Mappe +donate = Dona +abandon = Abandon +abandon.text = This zone and all its resources will be lost to the enemy. +locked = Locked +complete = [LIGHT_GRAY]Complete: +zone.requirement = Wave {0} in zone {1} +resume = Resume Zone:\n[LIGHT_GRAY]{0} +bestwave = [LIGHT_GRAY]Best: {0} +launch = Launch +launch.title = Launch Successful +launch.next = [LIGHT_GRAY]next opportunity at wave {0} +launch.unable = [scarlet]Unable to LAUNCH.[] Enemies. +launch.confirm = This will launch all resources in your core.\nYou will not be able to return to this base. +uncover = Uncover +configure = Configure Loadout +configure.locked = [LIGHT_GRAY]Reach wave {0}\nto configure loadout. +zone.unlocked = [LIGHT_GRAY]{0} unlocked. +zone.requirement.complete = Wave {0} reached:\n{1} zone requirements met. +zone.config.complete = Wave {0} reached:\nLoadout config unlocked. +zone.resources = Resources Detected: +add = Add... +boss.health = Boss Health +connectfail = [crimson] Impossibile connettersi al server: [accent] {0} +error.unreachable = Server unreachable. +error.invalidaddress = Invalid address. +error.timedout = Timed out!\nMake sure the host has port forwarding set up, and that the address is correct! +error.mismatch = Packet error:\npossible client/server version mismatch.\nMake sure you and the host have the latest version of Mindustry! +error.alreadyconnected = Already connected. +error.mapnotfound = Map file not found! +error.io = Network I/O error. +error.any = Unkown network error. +zone.groundZero.name = Ground Zero +zone.desertWastes.name = Desert Wastes +zone.craters.name = The Craters +zone.frozenForest.name = Frozen Forest +zone.ruinousShores.name = Ruinous Shores +zone.stainedMountains.name = Stained Mountains +zone.desolateRift.name = Desolate Rift +zone.nuclearComplex.name = Nuclear Production Complex +zone.overgrowth.name = Overgrowth +zone.tarFields.name = Tar Fields +settings.language = Lingua +settings.reset = Resetta Alle Impostazioni Predefinite +settings.rebind = Reimposta +settings.controls = Controlli +settings.game = Gioco +settings.sound = Suono +settings.graphics = Grafica +settings.cleardata = Clear Game Data... +settings.clear.confirm = Are you sure you want to clear this data?\nWhat is done cannot be undone! +settings.clearall.confirm = [scarlet]WARNING![]\nThis will clear all data, including saves, maps, unlocks and keybinds.\nOnce you press 'ok' the game will wipe all data and automatically exit. +settings.clearunlocks = Clear Unlocks +settings.clearall = Clear All +paused = In pausa +yes = Si +no = No +info.title = [accent] Info +error.title = [crimson]Si è verificato un errore +error.crashtitle = Si è verificato un errore +blocks.input = Input +blocks.output = Output +blocks.booster = Booster +block.unknown = [LIGHT_GRAY]??? +blocks.powercapacity = Capacità Energetica +blocks.powershot = Danno/Colpo +blocks.targetsair = Attacca nemici aerei +blocks.targetsground = Targets Ground +blocks.itemsmoved = Move Speed +blocks.launchtime = Time Between Launches +blocks.shootrange = Raggio +blocks.size = Grandezza +blocks.liquidcapacity = Capacità del liquido +blocks.powerrange = Raggio Energia +blocks.poweruse = Utilizzo energia +blocks.powerdamage = Power/Damage +blocks.itemcapacity = Capacità +blocks.basepowergeneration = Base Power Generation +blocks.productiontime = Production Time +blocks.repairtime = Block Full Repair Time +blocks.speedincrease = Speed Increase +blocks.range = Range +blocks.drilltier = Scavabili +blocks.drillspeed = Velocità scavo stbile +blocks.boosteffect = Boost Effect +blocks.maxunits = Max Active Units +blocks.health = Salute +blocks.buildtime = Build Time +blocks.inaccuracy = Inaccuratezza +blocks.shots = Colpi +blocks.reload = Ricarica +blocks.ammo = Ammo +bar.drillspeed = Drill Speed: {0}/s +bar.efficiency = Efficiency: {0}% +bar.powerbalance = Power: {0} +bar.poweramount = Power: {0} +bar.poweroutput = Power Output: {0} +bar.items = Items: {0} +bar.liquid = Liquid +bar.heat = Heat +bar.power = Power +bar.progress = Build Progress +bar.spawned = Units: {0}/{1} +bullet.damage = [stat]{0}[lightgray] dmg +bullet.splashdamage = [stat]{0}[lightgray] area dmg ~[stat] {1}[lightgray] tiles +bullet.incendiary = [stat]incendiary +bullet.homing = [stat]homing +bullet.shock = [stat]shock +bullet.frag = [stat]frag +bullet.knockback = [stat]{0}[lightgray] knockback +bullet.freezing = [stat]freezing +bullet.tarred = [stat]tarred +bullet.multiplier = [stat]{0}[lightgray]x ammo multiplier +bullet.reload = [stat]{0}[lightgray]x reload +unit.blocks = blocchi +unit.powersecond = unità energia/secondo +unit.liquidsecond = unità liquide/secondo +unit.itemssecond = oggetti/secondo +unit.liquidunits = unità liquidi +unit.powerunits = unità energia +unit.degrees = gradi +unit.seconds = secondi +unit.persecond = /sec +unit.timesspeed = x speed +unit.percent = % +unit.items = oggetti +category.general = Generali +category.power = Energia +category.liquids = Liquidi +category.items = Oggetti +category.crafting = Produzione +category.shooting = Potenza di fuoco +category.optional = Optional Enhancements +setting.landscape.name = Lock Landscape +setting.shadows.name = Shadows +setting.linear.name = Linear Filtering +setting.animatedwater.name = Animated Water +setting.animatedshields.name = Animated Shields +setting.antialias.name = Antialias[LIGHT_GRAY] (requires restart)[] +setting.indicators.name = Ally Indicators +setting.autotarget.name = Auto-Target +setting.fpscap.name = Limite FPS +setting.fpscap.none = Niente +setting.fpscap.text = {0} FPS +setting.swapdiagonal.name = Always Diagonal Placement +setting.difficulty.training = training +setting.difficulty.easy = facile +setting.difficulty.normal = medio +setting.difficulty.hard = difficile +setting.difficulty.insane = folle +setting.difficulty.name = Difficoltà: +setting.screenshake.name = Movimento dello schermo +setting.effects.name = Visualizza effetti +setting.sensitivity.name = Sensibilità del controller +setting.saveinterval.name = Intervallo di salvataggio automatico +setting.seconds = {0} Secondi +setting.fullscreen.name = Schermo Intero +setting.borderlesswindow.name = Borderless Window[LIGHT_GRAY] (may require restart) +setting.fps.name = Mostra FPS +setting.vsync.name = VSync +setting.lasers.name = Mostra Laser Energetici +setting.pixelate.name = Pixelate [LIGHT_GRAY](may decrease performance) +setting.minimap.name = Mostra minimappa +setting.musicvol.name = Volume Musica +setting.mutemusic.name = Silenzia musica +setting.sfxvol.name = Volume SFX +setting.mutesound.name = Togli suoni +setting.crashreport.name = Send Anonymous Crash Reports +setting.chatopacity.name = Chat Opacity +setting.playerchat.name = Display In-Game Chat +keybind.title = Configurazione Tasti +category.general.name = Generale +category.view.name = Visualizzazione +category.multiplayer.name = Multigiocatore +command.attack = Attack +command.retreat = Retreat +command.patrol = Patrol +keybind.gridMode.name = Block Select +keybind.gridModeShift.name = Category Select +keybind.press = Press a key... +keybind.press.axis = Press an axis or key... +keybind.screenshot.name = Map Screenshot +keybind.move_x.name = Sposta_x +keybind.move_y.name = Sposta_y +keybind.select.name = seleziona +keybind.diagonal_placement.name = Diagonal Placement +keybind.pick.name = Pick Block +keybind.break_block.name = Break Block +keybind.deselect.name = Deselect +keybind.shoot.name = spara +keybind.zoom_hold.name = zoom_hold +keybind.zoom.name = zoom +keybind.menu.name = menu +keybind.pause.name = pausa +keybind.minimap.name = Minimap +keybind.dash.name = Scatto +keybind.chat.name = Chat +keybind.player_list.name = lista_giocatori +keybind.console.name = console +keybind.rotate.name = Ruotare +keybind.toggle_menus.name = Abilita menù +keybind.chat_history_prev.name = Scorri chat vero l'alto +keybind.chat_history_next.name = Scorri chatt verso il basso +keybind.chat_scroll.name = Scorri chat +keybind.drop_unit.name = droppa materiali +keybind.zoom_minimap.name = Zomma minimappa +mode.help.title = Descrizione delle modalità +mode.survival.name = Survival +mode.survival.description = The normal mode. Limited resources and automatic incoming waves. +mode.sandbox.name = Sandbox +mode.sandbox.description = risorse infinite e nessun timer per le ondate. +mode.pvp.name = PvP +mode.pvp.description = fight against other players locally. +mode.attack.name = Attack +mode.attack.description = No waves, with the goal to destroy the enemy base. +mode.custom = Custom Rules +rules.infiniteresources = Infinite Resources +rules.wavetimer = Wave Timer +rules.waves = Waves +rules.enemyCheat = Infinite AI Resources +rules.unitdrops = Unit Drops +rules.unitbuildspeedmultiplier = Unit Creation Speed Multiplier +rules.unithealthmultiplier = Unit Health Multiplier +rules.playerhealthmultiplier = Player Health Multiplier +rules.playerdamagemultiplier = Player Damage Multiplier +rules.unitdamagemultiplier = Unit Damage Multiplier +rules.enemycorebuildradius = Enemy Core No-Build Radius:[LIGHT_GRAY] (tiles) +rules.respawntime = Respawn Time:[LIGHT_GRAY] (sec) +rules.wavespacing = Wave Spacing:[LIGHT_GRAY] (sec) +rules.buildcostmultiplier = Build Cost Multiplier +rules.buildspeedmultiplier = Build Speed Multiplier +rules.waitForWaveToEnd = Waves wait for enemies +rules.dropzoneradius = Drop Zone Radius:[LIGHT_GRAY] (tiles) +rules.respawns = Max respawns per wave +rules.limitedRespawns = Limit Respawns +rules.title.waves = Waves +rules.title.respawns = Respawns +rules.title.resourcesbuilding = Resources & Building +rules.title.player = Players +rules.title.enemy = Enemies +rules.title.unit = Units +content.item.name = Oggetti +content.liquid.name = Liquidi +content.unit.name = Units +content.block.name = Blocks +content.mech.name = Mech +item.copper.name = Rame +item.copper.description = Una utile materiale styrutturale. Molto usato in tutti i blocchi. +item.lead.name = Piombo +item.lead.description = Un materiale base, molto usato nei blocchi di trasporto. +item.coal.name = carbone +item.coal.description = Un carburante comune e facilmente ottenibile. +item.graphite.name = Graphite +item.titanium.name = titanio +item.titanium.description = Un raro metallo super leggero usato ampiamente nel trasporto di liquidi, trapani e navi. +item.thorium.name = Torio +item.thorium.description = Un materiale denso e radioattivo, utilizzato nella costruzione di strutture e come carburante del reattore nucleare. +item.silicon.name = Silicio +item.silicon.description = Un semiconduttore molto utile che viene utilizzato nei pannelli solari e nei macchinari elettronici. +item.plastanium.name = Plastaniu +item.plastanium.description = Un materiale leggero e duttile, utilizzato nelle navi avanzete e come munizione. +item.phase-fabric.name = Phase Fabric +item.phase-fabric.description = A near-weightless substance used in advanced electronics and self-repairing technology. +item.surge-alloy.name = Surge Alloy +item.surge-alloy.description = An advanced alloy with unique electrical properties. +item.spore-pod.name = Spore Pod +item.spore-pod.description = Used for conversion into oil, explosives and fuel. +item.sand.name = sabbia +item.sand.description = Un materiale base che viene altamente usato nei processi di fusione, Sia come lega che come lubrificante. +item.blast-compound.name = Polvere esplosiva +item.blast-compound.description = Un composto altamente volatile, utilizzato nella produzione di bombe ed esplosivi. Può essere utilizzato come combustibile anche se non è consigliato. +item.pyratite.name = Pirite +item.pyratite.description = Una sostanza molto infiammabile che viene utilizzata nelle armi a fuoco. +item.metaglass.name = Metaglass +item.metaglass.description = A super-tough glass compound. Extensively used for liquid distribution and storage. +item.scrap.name = Scrap +item.scrap.description = Leftover remnants of old structures and units. Contains trace amounts of many different metals. +liquid.water.name = acqua +liquid.slag.name = Slag +liquid.oil.name = petrolio +liquid.cryofluid.name = criogenium +mech.alpha-mech.name = Alpha +mech.alpha-mech.weapon = Ripetitore pesante +mech.alpha-mech.ability = Orda di droni +mech.alpha-mech.description = Il mech standard. È abbastanza veloce e produce abbastanza danni; può anche generare 3 droni per aumentare il suo danno complessivo. +mech.delta-mech.name = Delta +mech.delta-mech.weapon = Generatore di fulmini +mech.delta-mech.ability = Scarica +mech.delta-mech.description = Un veloce, poco armato mech fatto per giocare a tocca e fuga con il nemico. Fa poco danno alle strutture, ma può uccidere un gran nummero di nemici grazie alle sue armi ad alto voltaggio. +mech.tau-mech.name = Tau +mech.tau-mech.weapon = Laser ricostruttore +mech.tau-mech.ability = Ripara esplosioni +mech.tau-mech.description = Un mach di supporto. Cura i blocchi danneggiati sparandogli contro. Può spegnere fuochi e curare i compagni di squadra. +mech.omega-mech.name = Omega +mech.omega-mech.weapon = SCiame di missili +mech.omega-mech.ability = Configurazione armata +mech.omega-mech.description = Un ingombrante e ben armato mech, fatto per stare in prima linea. La sue difese possono bloccare fino al 90% dei danni. +mech.dart-ship.name = Dart +mech.dart-ship.weapon = Ripetitore +mech.dart-ship.description = Una navicella standard. Molto veloce e leggera, ma può minare pochi blocchi e ha scarse potenzialità nella difesa. +mech.javelin-ship.name = Javelin +mech.javelin-ship.description = Una navetta da tocca e fuga. Anche se inizialmente lenta, può accellerare ad alte velocità e voloare sopra gli avamposti dei nemici, può provocare molti danni ai nemici tramite l'utilizzo di fulmini o missili. +mech.javelin-ship.weapon = Missili esplosivi +mech.javelin-ship.ability = Discharge Booster +mech.trident-ship.name = Tridente +mech.trident-ship.description = Un bombardiere pesante. Giùstamente ben protetto. +mech.trident-ship.weapon = Valle delle bombe +mech.glaive-ship.name = Glaive +mech.glaive-ship.description = Una grande e ben armata macchina da guerra. Equipaggiato con ripetitore di fiamma e con alta accellerazione e velocità massima. +mech.glaive-ship.weapon = Ripetitore di fiamma +item.explosiveness = [LIGHT_GRAY]Esplosività: {0} +item.flammability = [LIGHT_GRAY]Infiammabilità: {0} +item.radioactivity = [LIGHT_GRAY]Radioattività: {0} +unit.health = [LIGHT_GRAY]Vita: {0} +unit.speed = [LIGHT_GRAY]Velocità: {0} +mech.weapon = [LIGHT_GRAY]Armi: {0} +mech.health = [LIGHT_GRAY]Health: {0} +mech.itemcapacity = [LIGHT_GRAY]Capacità oggetti: {0} +mech.minespeed = [LIGHT_GRAY]Velocità di scavo: {0} +mech.minepower = [LIGHT_GRAY]Potenza di scavo: {0} +mech.ability = [LIGHT_GRAY]Abilità: {0} +mech.buildspeed = [LIGHT_GRAY]Building Speed: {0}% +liquid.heatcapacity = [LIGHT_GRAY]Capacità calorifica: {0} +liquid.viscosity = [LIGHT_GRAY]Viscosità: {0} +liquid.temperature = [LIGHT_GRAY]Temperatura: {0} +block.grass.name = Grass +block.salt.name = Salt +block.saltrocks.name = Salt Rocks +block.pebbles.name = Pebbles +block.tendrils.name = Tendrils +block.sandrocks.name = Sand Rocks +block.spore-pine.name = Spore Pine +block.sporerocks.name = Spore Rocks +block.rock.name = Rock +block.snowrock.name = Snow Rock +block.shale.name = Shale +block.shale-boulder.name = Shale Boulder +block.moss.name = Moss +block.shrubs.name = Shrubs +block.spore-moss.name = Spore Moss +block.shalerocks.name = Shale Rocks +block.scrap-wall.name = Scrap Wall +block.scrap-wall-large.name = Large Scrap Wall +block.scrap-wall-huge.name = Huge Scrap Wall +block.scrap-wall-gigantic.name = Gigantic Scrap Wall +block.thruster.name = Thruster +block.kiln.name = Kiln +block.kiln.description = Smelts sand and lead into metaglass. Requires small amounts of power. +block.graphite-press.name = Graphite Press +block.multi-press.name = Multi-Press +block.constructing = {0}\n[LIGHT_GRAY](Constructing) +block.spawn.name = Enemy Spawn +block.core-shard.name = Core: Shard +block.core-foundation.name = Core: Foundation +block.core-nucleus.name = Core: Nucleus +block.deepwater.name = acqua profonda +block.water.name = acqua +block.tainted-water.name = Tainted Water +block.darksand-tainted-water.name = Dark Sand Tainted Water +block.tar.name = Tar +block.stone.name = pietra +block.sand.name = sabbia +block.darksand.name = Dark Sand +block.ice.name = ghiaccio +block.snow.name = neve +block.craters.name = Craters +block.sand-water.name = Sand water +block.darksand-water.name = Dark Sand Water +block.char.name = Char +block.holostone.name = Holo stone +block.ice-snow.name = Ice Snow +block.rocks.name = Rocks +block.icerocks.name = Ice rocks +block.snowrocks.name = Snow Rocks +block.dunerocks.name = Dune Rocks +block.pine.name = Pine +block.white-tree-dead.name = White Tree Dead +block.white-tree.name = White Tree +block.spore-cluster.name = Spore Cluster +block.metal-floor.name = Metal Floor +block.metal-floor-2.name = Metal Floor 2 +block.metal-floor-3.name = Metal Floor 3 +block.metal-floor-5.name = Metal Floor 5 +block.metal-floor-damaged.name = Metal Floor Damaged +block.dark-panel-1.name = Dark Panel 1 +block.dark-panel-2.name = Dark Panel 2 +block.dark-panel-3.name = Dark Panel 3 +block.dark-panel-4.name = Dark Panel 4 +block.dark-panel-5.name = Dark Panel 5 +block.dark-panel-6.name = Dark Panel 6 +block.dark-metal.name = Dark Metal +block.ignarock.name = Igna Rock +block.hotrock.name = Hot Rock +block.magmarock.name = Magma Rock +block.cliffs.name = Cliffs +block.copper-wall.name = Muro di rame +block.copper-wall-large.name = Muro grande di rame +block.titanium-wall.name = Titanium Wall +block.titanium-wall-large.name = Large Titanium Wall +block.phase-wall.name = Muro di fase +block.phase-wall-large.name = Muro grande di fase +block.thorium-wall.name = Muro di torio +block.thorium-wall-large.name = Muro grande di torio +block.door.name = porta +block.door-large.name = Porta grande +block.duo.name = Duo +block.scorch.name = Scorch +block.scatter.name = Scatter +block.hail.name = Bombardiere +block.lancer.name = Idrogetto +block.conveyor.name = trasportatore +block.titanium-conveyor.name = Nastro trasportatore potenziato +block.junction.name = Incrocio +block.router.name = router +block.distributor.name = Mega router +block.sorter.name = Filtro +block.sorter.description = Divide gli oggetti. Se l'oggetto corrisponde a quello selezionato, Può passare. Altrimenti viene espulso sui lati. +block.overflow-gate.name = splitter per eccesso +block.overflow-gate.description = Una combinazione di un divisore e di un router , che distribuisce sui suoi lati se la via centrale è bloccata. +block.silicon-smelter.name = Fonderia per silicio +block.phase-weaver.name = Tessitore di fase +block.pulverizer.name = Polverizzatore +block.cryofluidmixer.name = Mixer liqidi +block.melter.name = Fonditore +block.incinerator.name = Inceneritore +block.spore-press.name = Spore Press +block.separator.name = Separatore +block.coal-centrifuge.name = Coal Centrifuge +block.power-node.name = Snodo energetico +block.power-node-large.name = Snodo energetico grande +block.surge-tower.name = Surge Tower +block.battery.name = Batteria +block.battery-large.name = Batteria grossa +block.combustion-generator.name = generatore a carbone +block.turbine-generator.name = Turbina +block.differential-generator.name = Differential Generator +block.impact-reactor.name = Impact Reactor +block.mechanical-drill.name = Trapano meccanico +block.pneumatic-drill.name = Trapano pneumatico +block.laser-drill.name = Estrattore laser +block.water-extractor.name = Estrattore d'acqua +block.cultivator.name = Coltivatore +block.dart-mech-pad.name = Dart Mech Pad +block.delta-mech-pad.name = Delta Mech Pad +block.javelin-ship-pad.name = Javelin Ship Pad +block.trident-ship-pad.name = Trident Ship Pad +block.glaive-ship-pad.name = Glaive Ship Pad +block.omega-mech-pad.name = Omega Mech Pad +block.tau-mech-pad.name = Tau Mech Pad +block.conduit.name = Condotto +block.mechanical-pump.name = Pompa meccanica +block.item-source.name = Sorgente oggetti +block.item-void.name = Vuoto oggetti +block.liquid-source.name = Sorgente liquida +block.power-void.name = Energia nulla +block.power-source.name = Energia infinita +block.unloader.name = Scaricatore +block.vault.name = Deposito +block.wave.name = Idrogetto +block.swarmer.name = Swarmer +block.salvo.name = Cannoncino +block.ripple.name = Cannone +block.phase-conveyor.name = Nastro trasportatore ad alta velocità +block.bridge-conveyor.name = Nastro trasportatore sopraelevato +block.plastanium-compressor.name = Compressore al plastanio +block.pyratite-mixer.name = Mixer pirite +block.blast-mixer.name = Mixer poleri +block.solar-panel.name = Pannello solare +block.solar-panel-large.name = Pannrllo solare 3x3 +block.oil-extractor.name = Estrattore petrolio +block.spirit-factory.name = Spirit Drone Factory +block.phantom-factory.name = Phantom Drone Factory +block.wraith-factory.name = Wraith Fighter Factory +block.ghoul-factory.name = Ghoul Bomber Factory +block.dagger-factory.name = Dagger Mech Factory +block.crawler-factory.name = Crawler Mech Factory +block.titan-factory.name = Titan Mech Factory +block.fortress-factory.name = Fortress Mech Factory +block.revenant-factory.name = Revenant Fighter Factory +block.repair-point.name = Punto di riparazione +block.pulse-conduit.name = Condotta attiva +block.phase-conduit.name = Condotto ad alta velocità +block.liquid-router.name = Distributore liquidi +block.liquid-tank.name = Tanica d'acqua +block.liquid-junction.name = Giunzione liquida +block.bridge-conduit.name = Condotta sopraelevata +block.rotary-pump.name = Pompa a turbina +block.thorium-reactor.name = Reattore al torio +block.mass-driver.name = Lancia materiali +block.blast-drill.name = Blast Drill +block.thermal-pump.name = Pompa termica +block.thermal-generator.name = Generatore termico +block.alloy-smelter.name = Altoforno +block.mender.name = Mender +block.mend-projector.name = Riparatore +block.surge-wall.name = Surge Wall +block.surge-wall-large.name = Large Surge Wall +block.cyclone.name = Cyclone +block.fuse.name = Fuse +block.shock-mine.name = Shock Mine +block.overdrive-projector.name = Overdrive Projector +block.force-projector.name = Force Projector +block.arc.name = Arc +block.rtg-generator.name = RTG Generator +block.spectre.name = Spectre +block.meltdown.name = Meltdown +block.container.name = Container +block.launch-pad.name = Launch Pad +block.launch-pad.description = Launches batches of items without any need for a core launch. Unfinished. +block.launch-pad-large.name = Large Launch Pad +team.blue.name = blue +team.red.name = red +team.orange.name = orange +team.none.name = gray +team.green.name = green +team.purple.name = purple +unit.spirit.name = Spirit Drone +unit.spirit.description = The starter drone unit. Spawns in the core by default. Automatically mines ores, collects items and repairs blocks. +unit.phantom.name = Phantom Drone +unit.phantom.description = An advanced drone unit. Automatically mines ores, collects items and repairs blocks. Significantly more effective than a drone. +unit.dagger.name = Pericolo +unit.dagger.description = Un unità terrena base, molto più efficiente se in branco. +unit.crawler.name = Crawler +unit.titan.name = Titano +unit.titan.description = Un'unità di terra corazzata avanzata. Utilizza carburo come munizione. Attacca sia bersagli terrestri che aerei. +unit.ghoul.name = Ghoul Bomber +unit.ghoul.description = A heavy carpet bomber. Uses blast compound or pyratite as ammo. +unit.wraith.name = Wraith Fighter +unit.wraith.description = A fast, hit-and-run interceptor unit. +unit.fortress.name = Fortress +unit.fortress.description = A heavy artillery ground unit. +unit.revenant.name = Revenant +unit.eruptor.name = Eruptor +unit.chaos-array.name = Chaos Array +unit.eradicator.name = Eradicator +unit.lich.name = Lich +unit.reaper.name = Reaper +tutorial.begin = Your mission here is to eradicate the[LIGHT_GRAY] enemy[].\n\nBegin by[accent] mining copper[]. Tap a copper ore vein near your core to do this. +tutorial.drill = Mining manually is inefficient.\n[accent]Drills []can mine automatically.\nPlace one on a copper vein. +tutorial.conveyor = [accent]Conveyors[] are used to transport items to the core.\nMake a line of conveyors from the drill to the core. +tutorial.morecopper = More copper is required.\n\nEither mine it manually, or place more drills. +tutorial.turret = Defensive structures must be built to repel the[LIGHT_GRAY] enemy[].\nBuild a duo turret near your base. +tutorial.drillturret = Duo turrets require[accent] copper ammo []to shoot.\nPlace a drill next to the turret to supply it with mined copper. +tutorial.waves = The[LIGHT_GRAY] enemy[] approaches.\n\nDefend your core for 2 waves. Build more turrets. +tutorial.lead = More ores are available. Explore and mine[accent] lead[].\n\nDrag from your unit to the core to transfer resources. +tutorial.smelter = Copper and lead are weak metals.\nSuperior[accent] Dense Alloy[] can be created in a smelter.\n\nBuild one. +tutorial.densealloy = The smelter will now produce alloy.\nGet some.\nImprove the production if necessary. +tutorial.siliconsmelter = The core will now create a[accent] spirit drone[] for mining and repairing blocks.\n\nFactories for other units can be created with [accent] silicon.\nMake a silicon smelter. +tutorial.silicondrill = Silicon requires[accent] coal[] and[accent] sand[].\nStart by making drills. +tutorial.generator = This technology requires power.\nCreate a[accent] combustion generator[] for it. +tutorial.generatordrill = Combustion generators need fuel.\nFuel it with coal from a drill. +tutorial.node = Power requires transport.\nCreate a[accent] power node[] next to your combustion generator to transfer its power. +tutorial.nodelink = Power can be transferred through contacting power blocks and generators, or by linked power nodes.\n\nLink power by tapping the node and selecting the generator and silicon smelter. +tutorial.silicon = Silicon is being produced. Get some.\n\nImproving the production system is advised. +tutorial.daggerfactory = Construct a[accent] dagger mech factory.[]\n\nThis will be used to create attack mechs. +tutorial.router = Factories need resources to function.\nCreate a router to split conveyor resources. +tutorial.dagger = Link power nodes to the factory.\nOnce requirements are met, a mech will be created.\n\nCreate more drills, generators and conveyors as necessary. +tutorial.battle = The[LIGHT_GRAY] enemy[] has revealed their core.\nDestroy it with your unit and dagger mechs. +block.copper-wall.description = A cheap defensive block.\nUseful for protecting the core and turrets in the first few waves. +block.copper-wall-large.description = A cheap defensive block.\nUseful for protecting the core and turrets in the first few waves.\nSpans multiple tiles. +block.thorium-wall.description = A strong defensive block.\nGood protection from enemies. +block.thorium-wall-large.description = A strong defensive block.\nGood protection from enemies.\nSpans multiple tiles. +block.phase-wall.description = Not as strong as a thorium wall but will deflect bullets unless they are too powerful. +block.phase-wall-large.description = Not as strong as a thorium wall but will deflect bullets unless they are too powerful.\nSpans multiple tiles. +block.surge-wall.description = The strongest defensive block.\nHas a small chance of triggering lightning towards the attacker. +block.surge-wall-large.description = The strongest defensive block.\nHas a small chance of triggering lightning towards the attacker.\nSpans multiple tiles. +block.door.description = A small door that can be opened and closed by tapping on it.\nIf opened, enemies can shoot and move through. +block.door-large.description = A large door that can be opened and closed by tapping on it.\nIf opened, enemies can shoot and move through.\nSpans multiple tiles. +block.mend-projector.description = Periodically heals buildings in its vicinity. +block.overdrive-projector.description = Increases the speed of nearby buildings like drills and conveyors. +block.force-projector.description = Creates a hexagonal force field around itself, protecting buildings and units inside from damage through bullets. +block.shock-mine.description = Damages enemies stepping on the mine. Nearly invisible to the enemy. +block.duo.description = A small, cheap turret. +block.scatter.description = A medium-sized anti-air turret. Sprays clumps of lead or scrap flak at enemy units. +block.arc.description = A small turret which shoots electricity in a random arc towards the enemy. +block.hail.description = A small artillery turret. +block.lancer.description = A medium-sized turret which shoots charged electricity beams. +block.wave.description = A medium-sized rapid-fire turret which shoots liquid bubbles. +block.salvo.description = A medium-sized turret which fires shots in salvos. +block.swarmer.description = A medium-sized turret which shoots burst missiles. +block.ripple.description = A large artillery turret which fires several shots simultaneously. +block.cyclone.description = A large rapid fire turret. +block.fuse.description = A large turret which shoots powerful short-range beams. +block.spectre.description = A large turret which shoots two powerful bullets at once. +block.meltdown.description = A large turret which shoots powerful long-range beams. +block.conveyor.description = Basic item transport block. Moved items forward and automatically deposits them into turrets or crafters. Rotatable. +block.titanium-conveyor.description = Advanced item transport block. Moves items faster than standard conveyors. +block.phase-conveyor.description = Advanced item transport block. Uses power to teleport items to a connected phase conveyor over several tiles. +block.junction.description = Acts as a bridge for two crossing conveyor belts. Useful in situations with two different conveyors carrying different materials to different locations. +block.mass-driver.description = Ultimate item transport block. Collects several items and then shoots them to another mass driver over a long range. +block.silicon-smelter.description = Reduces sand with highly pure coke in order to produce silicon. +block.plastanium-compressor.description = Produces plastanium from oil and titanium. +block.phase-weaver.description = Produces phase fabric from radioactive thorium and high amounts of sand. +block.alloy-smelter.description = Produces surge alloy from titanium, lead, silicon and copper. +block.pulverizer.description = Crushes stone into sand. Useful when there is a lack of natural sand. +block.pyratite-mixer.description = Mixes coal, lead and sand into highly flammable pyratite. +block.blast-mixer.description = Uses oil for transforming pyratite into the less flammable but more explosive blast compound. +block.cryofluidmixer.description = Combines water and titanium into cryofluid which is much more efficient for cooling. +block.melter.description = Heats up stone to very high temperatures to obtain lava. +block.incinerator.description = Gets rid of any excess item or liquid. +block.spore-press.description = Compresses spore pods into oil. +block.separator.description = Exposes stone to water pressure in order to obtain various minerals contained in the stone. +block.power-node.description = Transmits power to connected nodes. Up to four power sources, sinks or nodes can be connected. The node will receive power from or supply power to any adjacent blocks. +block.power-node-large.description = Has a larger radius than the power node and connects to up to six power sources, sinks or nodes. +block.battery.description = Stores power whenever there is an abundance and provides power whenever there is a shortage, as long as there is capacity left. +block.battery-large.description = Stores much more power than a regular battery. +block.combustion-generator.description = Generates power by burning oil or flammable materials. +block.turbine-generator.description = More efficient than a combustion generator, but requires additional water. +block.thermal-generator.description = Generates a large amount of power from lava. +block.solar-panel.description = Provides a small amount of power from the sun. +block.solar-panel-large.description = Provides much better power supply than a standard solar panel, but is also much more expensive to build. +block.thorium-reactor.description = Generates huge amounts of power from highly radioactive thorium. Requires constant cooling. Will explode violently if insufficient amounts of coolant are supplied. +block.rtg-generator.description = A radioisotope thermoelectric generator which does not require cooling but provides less power than a thorium reactor. +block.unloader.description = Unloads items from a container, vault or core onto a conveyor or directly into an adjacent block. The type of item to be unloaded can be changed by tapping on the unloader. +block.container.description = Stores a small amount of items. Use it for creating buffers when there is a non-constant demand of materials. An[LIGHT_GRAY] unloader[] can be used to retrieve items from the container. +block.vault.description = Stores a large amount of items. Use it for creating buffers when there is a non-constant demand of materials. An[LIGHT_GRAY] unloader[] can be used to retrieve items from the vault. +block.mechanical-drill.description = A cheap drill. When placed on appropriate tiles, outputs items at a slow pace indefinitely. +block.pneumatic-drill.description = An improved drill which is faster and able to process harder materials by making use of air pressure. +block.laser-drill.description = Allows drilling even faster through laser technology, but requires power. Additionally, radioactive thorium can be retrieved with this drill. +block.blast-drill.description = The ultimate drill. Requires large amounts of power. +block.water-extractor.description = Extracts water from the ground. Use it when there is no lake nearby. +block.cultivator.description = Cultivates the soil with water in order to obtain biomatter. +block.oil-extractor.description = Uses large amounts of power in order to extract oil from sand. Use it when there is no direct source of oil nearby. +block.trident-ship-pad.description = Leave your current vessel and change into a reasonably well armored heavy bomber.\nUse the pad by double tapping while standing on it. +block.javelin-ship-pad.description = Leave your current vessel and change into a strong and fast interceptor with lightning weapons.\nUse the pad by double tapping while standing on it. +block.glaive-ship-pad.description = Leave your current vessel and change into a large, well-armored gunship.\nUse the pad by double tapping while standing on it. +block.tau-mech-pad.description = Leave your current vessel and change into a support mech which can heal friendly buildings and units.\nUse the pad by double tapping while standing on it. +block.delta-mech-pad.description = Leave your current vessel and change into a fast, lightly-armored mech made for hit-and-run attacks.\nUse the pad by double tapping while standing on it. +block.omega-mech-pad.description = Leave your current vessel and change into a bulky and well-armored mech, made for front-line assaults.\nUse the pad by double tapping while standing on it. +block.spirit-factory.description = Produces light drones which mine ore and repair blocks. +block.phantom-factory.description = Produces advanced drone units which are significantly more effective than a spirit drone. +block.wraith-factory.description = Produces fast, hit-and-run interceptor units. +block.ghoul-factory.description = Produces heavy carpet bombers. +block.dagger-factory.description = Produces basic ground units. +block.titan-factory.description = Produces advanced, armored ground units. +block.fortress-factory.description = Produces heavy artillery ground units. +block.revenant-factory.description = Produces heavy laser ground units. +block.repair-point.description = Continuously heals the closest damaged unit in its vicinity. +block.conduit.description = Basic liquid transport block. Works like a conveyor, but with liquids. Best used with extractors, pumps or other conduits. +block.pulse-conduit.description = Advanced liquid transport block. Transports liquids faster and stores more than standard conduits. +block.phase-conduit.description = Advanced liquid transport block. Uses power to teleport liquids to a connected phase conduit over several tiles. +block.liquid-router.description = Accepts liquids from one direction and outputs them to up to 3 other directions equally. Can also store a certain amount of liquid. Useful for splitting the liquids from one source to multiple targets. +block.liquid-tank.description = Stores a large amount of liquids. Use it for creating buffers when there is a non-constant demand of materials or as a safeguard for cooling vital blocks. +block.liquid-junction.description = Acts as a bridge for two crossing conduits. Useful in situations with two different conduits carrying different liquids to different locations. +block.bridge-conduit.description = Advanced liquid transport block. Allows transporting liquids over up to 3 tiles of any terrain or building. +block.mechanical-pump.description = A cheap pump with slow output, but no power consumption. +block.rotary-pump.description = An advanced pump which doubles up speed by using power. +block.thermal-pump.description = The ultimate pump. Three times as fast as a mechanical pump and the only pump which is able to retrieve lava. +block.router.description = Accepts items from one direction and outputs them to up to 3 other directions equally. Useful for splitting the materials from one source to multiple targets. +block.distributor.description = An advanced router which splits items to up to 7 other directions equally. +block.bridge-conveyor.description = Advanced item transport block. Allows transporting items over up to 3 tiles of any terrain or building. +block.item-source.description = Infinitely outputs items. Sandbox only. +block.liquid-source.description = Infinitely outputs liquids. Sandbox only. +block.item-void.description = Destroys any items which go into it without using power. Sandbox only. +block.power-source.description = Infinitely outputs power. Sandbox only. +block.power-void.description = Voids all power inputted into it. Sandbox only. +liquid.water.description = Commonly used for cooling machines and waste processing. +liquid.oil.description = Can be burnt, exploded or used as a coolant. +liquid.cryofluid.description = The most efficient liquid for cooling things down. diff --git a/core/assets/bundles/bundle_ita.properties b/core/assets/bundles/bundle_ita.properties deleted file mode 100644 index 02b83f8158..0000000000 --- a/core/assets/bundles/bundle_ita.properties +++ /dev/null @@ -1,551 +0,0 @@ -text.about = Creato da [ROYAL]Anuken[] - [SKY]anukendev@gmail.com[]\nOriginariamente era una voce nel [orange]GDL[] Metal Monstrosity Jam.\n\n Crediti:\n - SFX realizzato con [YELLOW]bfxr [] \n - Musica creata da [GREEN]RoccoW[] / trovata su [lime]FreeMusicArchive.org[]\n\n Un ringraziamento speciale a:\n - [coral]MitchellFJN []: esteso test del gioco e feedback\n - [sky]Luxray5474 []: lavorazione della wiki, contributi col codice\n - [lime]Epowerj []: sistema di costruzione del codice, icone\n - Tutti i beta tester su itch.io e Google Play\n -text.credits = Crediti -text.discord = Unisciti sul server discord di mindustry! -text.changes = [SCARLET]Attenzione!\n[]Alcune importanti meccaniche di gioco sono state modificate.\n\n - [accent]I teletrasporti[] ora usano la corrente.\n - [accent]Le fornaci[] e [accent]i crogioli[] ora hanno una capacità massima di oggetti. \n- [accent]I crogioli[] ora richiedono il carbone come combustibile. -text.link.discord.description = la chatroom ufficiale del server discord di Mindustry -text.link.github.description = Codice sorgente del gioco -text.link.dev-builds.description = Build di sviluppo versioni instabili -text.link.trello.description = Scheda ufficiale trello per funzionalità pianificate -text.link.itch.io.description = pagina di itch.io con download per PC e versione web -text.link.google-play.description = Elenco di Google Play Store -text.link.wiki.description = wiki ufficiale di Mindustry -text.linkfail = Impossibile aprire il link! L'URL è stato copiato nella tua bacheca. -text.editor.web = La versione web non supporta l'editor! Scarica il gioco per usarlo. -text.multiplayer.web = Questa versione del gioco non supporta il multiplayer! Per giocare in multiplayer dal tuo browser, usa il link \"versione web multiplayer\" nella pagina itch.io. -text.gameover = Il nucleo è stato distrutto. -text.highscore = [YELLOW]Nuovo record! -text.lasted = Sei durato fino all'onda -text.level.highscore = Migliore: [accent]{0} -text.level.delete.title = Conferma Eliminazione -text.level.delete = Sei sicuro di voler eliminare la mappa \"[arancione]{0}\"? -text.level.select = Selezione del livello -text.level.mode = Modalità di gioco: -text.savegame = Salva -text.loadgame = Carica -text.joingame = Gioca MP -text.newgame = Nuovo gioco -text.quit = Esci -text.about.button = Informazioni -text.name = Nome: -text.public = Pubblico -text.players = {0} giocatori online -text.server.player.host = {0} (host) -text.players.single = {0} giocatori online -text.server.mismatch = Errore nel pacchetto: possibile discrepanza nella versione client / server. Assicurati che tu e l'host abbiate l'ultima versione di Mindustry! -text.server.closing = [accent]Chiusura server ... -text.server.kicked.kick = Sei stato cacciato dal server! -text.server.kicked.invalidPassword = 10468 = Password non valida. -text.server.kicked.clientOutdated = Versione del client obsoleta! Aggiorna il tuo gioco! -text.server.kicked.serverOutdated = Server obsoleto! Chiedi all'host di aggiornare! -text.server.kicked.banned = Sei stato bannato su questo server. -text.server.kicked.recentKick = Sei stato cacciato di recente. Attendi prima di connetterti di nuovo. -text.server.connected = {0} si è connesso -text.server.disconnected = {0} si è disconnesso -text.nohost = Impossibile hostare il server con una mappa personalizzata! -text.host.info = Il pulsante [accent]hos [] ospita un server sulle porte [scarlet]6567[] e [scarlet]656.[] Chiunque sulla stessa [LIGHT_GRAY]connessione wifi o rete locale[] dovrebbe essere in grado di vedere il proprio server nel proprio elenco server.\n\n Se vuoi che le persone siano in grado di connettersi ovunque tramite IP, è richiesto il [accent]port forwarding[]. \n\n[LIGHT_GRAY]Nota: se qualcuno sta riscontrando problemi durante la connessione al gioco LAN, assicurati di aver consentito a Mindustry di accedere alla rete locale nelle impostazioni del firewall. -text.join.info = Qui è possibile inserire un [accent]IP del server[] a cui connettersi, o scoprire [accento]un server sulla rete locale[] disponibile.\n Sono supportati sia il multiplayer LAN che WAN. \n\n[LIGHT_GRAY]Nota: non esiste un elenco di server globali automatici; se si desidera connettersi a qualcuno tramite IP, è necessario chiedere all'host il proprio IP. -text.hostserver = Server host -text.host = Host -text.hosting = [accento] Apertura del server ... -text.hosts.refresh = Aggiorna -text.hosts.discovering = Scoperta partite LAN -text.server.refreshing = Aggiornamento server -text.hosts.none = [lightgray]Nessuna partita LAN trovata! -text.host.invalid = [scarlet]Impossibile connettersi all'host. -text.server.friendlyfire = Fuoco amico -text.trace = Trace Player -text.trace.playername = Nome del giocatore: [accent]{0} -text.trace.ip = IP: [accent]{0} -text.trace.id = ID univoco: [accent]{0} -text.trace.android = Client Android: [accent] {0} -text.trace.modclient = Cliente personalizzato: [accent]{0} -text.trace.totalblocksbroken = Totale blocchi interrotti: [accent]{0} -text.trace.structureblocksbroken = Blocchi strutturali distrutti: [accent]{0} -text.trace.lastblockbroken = Ultimo blocco distrutto: [accent]{0} -text.trace.totalblocksplaced = Totale blocchi posizionati: [accent]{0} -text.trace.lastblockplaced = Ultimo blocco inserito: [accent]{0} -text.invalidid = ID cliente non valido! Invia una segnalazione di bug. -text.server.bans = Lista Ban -text.server.bans.none = Nessun giocatore bandito trovato! -text.server.admins = Amministratori -text.server.admins.none = Nessun amministratore trovato! -text.server.add = Aggiungi server -text.server.delete = Sei sicuro di voler eliminare questo server? -text.server.hostname = Host: {0} -text.server.edit = Modifica server -text.server.outdated = [crimson]Server obsoleto![] -text.server.outdated.client = [crimson]Client obsoleto![] -text.server.version = [lightgray]Versione: {0} -text.server.custombuild = [yellow] Costruzione personalizzata -text.confirmban = Sei sicuro di voler bandire questo giocatore? -text.confirmunban = Sei sicuro di voler sbloccare questo giocatore? -text.confirmadmin = Sei sicuro di voler rendere questo giocatore un amministratore? -text.confirmunadmin = Sei sicuro di voler rimuovere lo stato di amministratore da questo player? -text.joingame.byip = Unisciti a IP ... -text.joingame.title = Unisciti alla Partita -text.joingame.ip = IP: -text.disconnect = Disconnesso. -text.disconnect.data = Errore nel caricamento i dati del mondo! -text.connecting = [accent]Connessione in corso ... -text.connecting.data = [accent]Caricamento dei dati del mondo ... -text.connectfail = [crimson] Impossibile connettersi al server: [orange] {0} -text.server.port = Porta: -text.server.addressinuse = Indirizzo già in uso! -text.server.invalidport = Numero di porta non valido! -text.server.error = [crimson]Errore nell'hosting del server: [orange] {0} -text.tutorial.back = < Prec -text.tutorial.next = Succ > -text.save.new = Nuovo Salvataggio -text.save.overwrite = Sei sicuro di voler sovrascrivere questo salvataggio? -text.overwrite = Sostituisci -text.save.none = Nessun salvataggio trovato! -text.saveload = [Accent]Salvataggio ... -text.savefail = Salvataggio del gioco non riuscito! -text.save.delete.confirm = Sei sicuro di voler eliminare questo salvataggio? -text.save.delete = Elimina -text.save.export = Esporta Salva -text.save.import.invalid = [orange]Questo salvataggio non è valido! -text.save.import.fail = [crimson]Impossibile importare salvataggio: [orange]{0} -text.save.export.fail = [crimson]Impossibile esportare il salvataggio: [orange]{0} -text.save.import = Importa Salvataggio -text.save.newslot = Salva nome: -text.save.rename = Rinomina -text.save.rename.text = Nuovo nome: -text.selectslot = Seleziona un salvataggio. -text.slot = [accent]Slot {0} -text.save.corrupted = [orang]File di salvataggio danneggiato o non valido! -text.empty = -text.on = Acceso -text.off = Spento -text.save.autosave = Salvataggio automatico: {0} -text.save.map = mappa -text.save.wave = Ondata: -text.save.difficulty = Difficolta: {0} -text.save.date = Ultimo salvataggio: {0} -text.confirm = Conferma -text.delete = Elimina -text.ok = OK -text.open = Apri -text.cancel = Annulla -text.openlink = Apri Link -text.copylink = Copia link -text.back = Indietro -text.quit.confirm = Sei sicuro di voler uscire? -text.changelog.title = Registro modifiche -text.changelog.loading = Ottenere il registro delle modifiche ... -text.changelog.error.android = [orange]Nota che il log delle modifiche non funziona su Android 4.4 e versioni precedenti! Ciò è dovuto a un bug interno di Android. -text.changelog.error = [scarlet]Errore durante il recupero del changelog! Controlla la tua connessione Internet. -text.changelog.current = [yellow][[Current version] -text.changelog.latest = [orange][[Latest version] -text.loading = [accent]Caricamento in corso ... -text.wave = [orange]Onda {0} -text.wave.waiting = Onda in {0} -text.waiting = In attesa... -text.enemies = {0} Nemici -text.enemies.single = {0} Nemico -text.loadimage = Carica immagine -text.saveimage = Salva Immagine -text.oregen = Generazione dei minerali -text.editor.badsize = [orange]Dimensioni dell'immagine non valide![]\n Dimensioni della mappa valide: {0} -text.editor.errorimageload = Errore durante il caricamento del file immagine:\n [orange]{0} -text.editor.errorimagesave = Errore durante il salvataggio del file immagine:\n [orange]{0} -text.editor.generate = Genera -text.editor.resize = Zomma o \nRiduci -text.editor.loadmap = Carica\nmappa -text.editor.savemap = Salva\nla mappa -text.editor.loadimage = Carica\nimmagine -text.editor.saveimage = Salva\nImmagine -text.editor.unsaved = [scarlet]Hai modifiche non salvate![]\nSei sicuro di voler uscire? -text.editor.brushsize = Dimensione del pennello: {0} -text.editor.noplayerspawn = Questa mappa non ha lo spawnpoint del giocatore! -text.editor.manyplayerspawns = Le mappe non possono avere più di un punto di spawn di un giocatore! -text.editor.manyenemyspawns = Non puoi avere più di {0} spawn nemici! -text.editor.resizemap = Ridimensiona la mappa -text.editor.resizebig = [Scarlet]Attenzione!\n[]Le mappe più grandi di 256 unità potrebbero causare del lag oltre ad essere instabili. -text.editor.mapname = Nome Mappa: -text.editor.overwrite = [Accent]Attenzione!\nQuesto sovrascrive una mappa esistente. -text.editor.failoverwrite = [crimson]Impossibile sovrascrivere la mappa di default! -text.editor.selectmap = Seleziona una mappa da caricare: -text.width = Larghezza: -text.height = Altezza: -text.randomize = Randomizza -text.apply = Applicare -text.update = Aggiorna -text.menu = Menu -text.play = Gioca -text.load = Carica -text.save = Salva -text.language.restart = Riavvia il gioco affinché il cambiamento della lingua abbia effetto. -text.settings.language = Lingua -text.settings = Impostazioni -text.tutorial = Lezioni -text.editor = Editor -text.mapeditor = Editor delle mappe -text.donate = Dona -text.settings.reset = Resetta Alle Impostazioni Predefinite -text.settings.controls = Controlli -text.settings.game = Gioco -text.settings.sound = Suono -text.settings.graphics = Grafica -text.upgrades = Miglioramenti -text.purchased = [LIME]Creato! -text.weapons = Armi -text.paused = In pausa -text.respawn = Rinascita in -text.info.title = [Accent]Informazioni -text.error.title = [crimson]Si è verificato un errore -text.error.crashmessage = [SCARLET]Si è verificato un errore imprevisto che ha causato un arresto anomalo.[] Si prega di segnalare le circostanze esatte in cui questo errore si è verificato allo sviluppatore:\n[ORANGE]anukendev@gmail.com[] -text.error.crashtitle = Si è verificato un errore -text.mode.break = Modalità di interruzione: {0} -text.mode.place = Modalità luogo: {0} -placemode.hold.name = linea -placemode.areadelete.name = area -placemode.touchdelete.name = toccare -placemode.holddelete.name = trattieni -placemode.none.name = nessuno -placemode.touch.name = toccare -placemode.cursor.name = cursore -text.blocks.extrainfo = [accent]informazioni extra sui blocchi: -text.blocks.blockinfo = Informazioni sul blocco -text.blocks.powercapacity = Capacità energetica -text.blocks.powershot = Danno/Colpo -text.blocks.powersecond = Energia/Secondo -text.blocks.powerdraindamage = Consumo/Danno -text.blocks.shieldradius = Raggio dello scudo -text.blocks.itemspeedsecond = Velocita Oggetti/Secondo -text.blocks.range = Gamma -text.blocks.size = Grandezza -text.blocks.powerliquid = Energia/Liquido -text.blocks.maxliquidsecond = Max liquido/Secondo -text.blocks.liquidcapacity = Capacità del liquido -text.blocks.liquidsecond = Liquido/Secondo -text.blocks.damageshot = Danni colpo -text.blocks.ammocapacity = Capacità del caricatore -text.blocks.ammo = Munizioni -text.blocks.ammoitem = Munizioni/Oggetto -text.blocks.maxitemssecond = Oggetti massimi/secondo -text.blocks.powerrange = Raggio Energia -text.blocks.lasertilerange = Raggio piastrelle laser -text.blocks.capacity = Capacità -text.blocks.itemcapacity = Capacità oggetto -text.blocks.maxpowergenerationsecond = Massima Energia Generata/secondo -text.blocks.powergenerationsecond = Energia generata/secondo -text.blocks.generationsecondsitem = Generazione secondi/oggetto -text.blocks.input = Ingresso -text.blocks.inputliquid = Ingresso del liquido -text.blocks.inputitem = Ingresso Oggetto -text.blocks.output = Uscita -text.blocks.secondsitem = Secondi/item -text.blocks.maxpowertransfersecond = Massimo trasferimento di potenza/secondo -text.blocks.explosive = Altamente esplosivo! -text.blocks.repairssecond = Ripara/secondo -text.blocks.health = Salute -text.blocks.inaccuracy = inesattezza -text.blocks.shots = Colpi -text.blocks.shotssecond = Colpi/secondo -text.blocks.fuel = Carburante -text.blocks.fuelduration = Durata del carburante -text.blocks.maxoutputsecond = Uscita max/secondo -text.blocks.inputcapacity = Capacità di ingresso -text.blocks.outputcapacity = Capacità di uscita -text.blocks.poweritem = Energia/Oggetto -text.placemode = Place Mode -text.breakmode = Modalità di interruzione -text.health = Salutee -setting.difficulty.easy = facile -setting.difficulty.normal = medio -setting.difficulty.hard = difficile -setting.difficulty.insane = Folle -setting.difficulty.purge = Epurazione -setting.difficulty.name = Difficoltà: -setting.screenshake.name = Screen Shake -setting.smoothcam.name = Smooth Camera -setting.indicators.name = Indicatori nemici -setting.effects.name = Visualizza effetti -setting.sensitivity.name = Sensibilità del controllore. -setting.saveinterval.name = Intervallo di salvataggio automatico -setting.seconds = {0} Secondi -setting.fullscreen.name = Schermo Intero -setting.multithread.name = multithreading -setting.fps.name = Mostra FPS -setting.vsync.name = Sincronizzazione Verticale -setting.lasers.name = Mostra Energia Dei Laser -setting.healthbars.name = Mostra barra della salute delle entità -setting.pixelate.name = Schermo Pixelate -setting.musicvol.name = Volume Musica -setting.mutemusic.name = Musica muta -setting.sfxvol.name = Volume SFX -setting.mutesound.name = Suono muto -map.maze.name = labirinto -map.fortress.name = fortezza -map.sinkhole.name = dolina -map.caves.name = grotte -map.volcano.name = vulcano -map.caldera.name = caldera -map.scorch.name = bruciatura -map.desert.name = Deserto -map.island.name = Isola -map.grassland.name = Prateria -map.tundra.name = Tundra -map.spiral.name = spirale -map.tutorial.name = Tutorial -tutorial.intro.text = [yellow]Benvenuti nel tutorial.[] Per iniziare, premere 'succ'. -tutorial.moveDesktop.text = Per spostarsi, utilizza i tasti [orange][[WASD][] . Tenere premuto [orange]shift []per correre. Tenere premuto [orange]CTRL[] mentre si utilizza la [orange]rotella del mouse[] per ingrandire o ridurre lo zoom. -tutorial.shoot.text = Usa il mouse per mirare, tieni premuto [orange]tasto sinistro del mouse[] per sparare. Fai uun po' di pratica con quest' [yellow]obiettivo[]. -tutorial.moveAndroid.text = Per spostare la vista, trascina un dito sullo schermo. Pizzica e trascina per ingrandire o ridurre. -tutorial.placeSelect.text = Prova a selezionare un [yellow]nastro trasportatore[] dal menu dei blocchi in basso a destra. -tutorial.placeConveyorDesktop.text = Utilizza la [orange]rotellina di scorrimento[] per ruotare il nastro trasportatore in modo che sia rivolto verso [orange]in avanti[], quindi posizionarlo nella [yellow]posizione contrassegnata[] utilizzando il [orange]tasto sinistro del mouse[]. -tutorial.placeConveyorAndroid.text = Utilizzare il pulsante [orange]tasto di rotazione[] per ruotare il trasportatore in modo che sia rivolto [orange]in avanti[], trascinalo in posizione con un dito, quindi posizionalo nella [yellow]posizione contrassegnata[] utilizzando [orange]segno di spunta[] -tutorial.placeConveyorAndroidInfo.text = In alternativa, puoi premere l'icona mirino in basso a sinistra per passare alla [orange] touch mode[] e posiziona i blocchi toccando sullo schermo. In modalità touch, i blocchi possono essere ruotati con la freccia in basso a sinistra. Premi [yellow]avanti[] per provarlo. -tutorial.placeDrill.text = Ora, seleziona e posiziona un [yellow]trapano per pietra[] nella posizione contrassegnata. -tutorial.blockInfo.text = Se vuoi saperne di più su un blocco, puoi toccare il [orange]punto interrogativo[] in alto a destra per leggere la sua descrizione. -tutorial.deselectDesktop.text = Puoi deselezionare un blocco usando [orange]tasto destro del mouse[]. -tutorial.deselectAndroid.text = È possibile deselezionare un blocco premendo il tasto [orange]X[]. -tutorial.drillPlaced.text = Il trapano ora produrrà [yellow]pietra,[] la manderà sul nastro trasportatore, quindi la sposterà nel [yellow]nucleo[]. -tutorial.drillInfo.text = I minerali differenti hanno bisogno di trapani diversi. La pietra richiede il trapano di pietra, il ferro richiede il trapano di ferro, ecc. -tutorial.drillPlaced2.text = Spostando gli oggetti nel nucleo li metti nell' [yellow]inventario[], in alto a sinistra. Piazzare i blocchi usa gli oggetti dal tuo inventario. -tutorial.moreDrills.text = Puoi collegare molti trapani e trasportatori insieme, in questo modo. -tutorial.deleteBlock.text = È possibile eliminare i blocchi facendo clic sul [orange]pulsante destro del mouse[] sul blocco che si desidera eliminare. Prova a eliminare questo trasportatore. -tutorial.deleteBlockAndroid.text = È possibile eliminare i blocchi [orange]selezionandoli col mirino[] nel menu della [orange]modalità pausa[] in basso a sinistra e toccando un blocco. Prova a eliminare questo trasportatore. -tutorial.placeTurret.text = Ora, seleziona e posiziona una [yellow]torretta[] nella [yellow]posizione contrassegnata[]. -tutorial.placedTurretAmmo.text = Questa torretta ora accetta [yellow]munizioni[] dal trasportatore. Puoi vedere quante munizioni ha al passaggio del mouse [green]barra verde[]. -tutorial.turretExplanation.text = Le torrette spareranno automaticamente al nemico più vicino nel raggio d'azione, a patto che abbiano munizioni sufficienti. -tutorial.waves.text = Ogni [yellow]60[] secondi, un'ondata di [coral]nemici[] si genera in posizioni specifiche e tenta di distruggere il nucleo. -tutorial.coreDestruction.text = Il tuo obiettivo è difendere [yellow]il nucleo[]. Se il nucleo viene distrutto, tu [coral]perdi la partita[]. -tutorial.pausingDesktop.text = Se hai bisogno di fare una pausa, premi il [orange]pulsante di pausa[] in alto a sinistra per mettere in pausa il gioco. Puoi ancora selezionare e posizionare i blocchi mentre sei in pausa, ma non puoi muoverti o sparare -tutorial.pausingAndroid.text = Se hai bisogno di fare una pausa, premi il [orange]pulsante di pausa[] in alto a sinistra per mettere in pausa il gioco. Puoi ancora rompere e posizionare i blocchi mentre sei in pausa. -tutorial.purchaseWeapons.text = Puoi acquistare nuove [yellow]armi[] per il tuo mech aprendo il menu di aggiornamenti in basso a sinistra. -tutorial.switchWeapons.text = Cambia le armi facendo clic sulla sua icona in basso a sinistra o usando i numeri [orange][[1-9][]. -tutorial.spawnWave.text = Ecco un'ondata ora. Distruggili. -tutorial.pumpDesc.text = Nelle onde successive, potrebbe essere necessario utilizzare le [yellow]pompe[] per distribuire i liquidi per i generatori o gli estrattori. -tutorial.pumpPlace.text = Le pompe funzionano in modo simile ai trapani, tranne per il fatto che producono liquidi anziché oggetti. Prova a posizionare una pompa sull' [yellow]petrolio evidenziato[]. -tutorial.conduitUse.text = Ora posiziona una [orange]conduttura[] -tutorial.conduitUse2.text = E alcuni altri ... -tutorial.conduitUse3.text = E alcuni altri ... -tutorial.generator.text = Ora, posizionare un [orange]generatore a combustione[] all'estremità del condotto. -tutorial.generatorExplain.text = Questo generatore ora creerà [yellow]corrente[] dall'petrolio. -tutorial.lasers.text = La potenza è distribuita usando i [yellow]laser energetici[]. Ruota e posizionane uno qui. -tutorial.laserExplain.text = Il generatore ora trasferirà l'energia nel blocco laser. Un raggio [yellow]opaco[] indica che sta trasmettendo corrente e un raggio [yellow]trasparente[] significa che non la sta strasmettendo. -tutorial.laserMore.text = Puoi controllare quanta energia ha un blocco passandoci sopra e controllando la barra [yellow]gialla[] in alto. -tutorial.healingTurret.text = Questo laser può essere utilizzato per alimentare una [lime]torretta di riparazione[]. Mettine una qui. -tutorial.healingTurretExplain.text = Finché ha energia, questa torretta [lime]riparerà i blocchi vicini.[] Durante la riproduzione del gioco, assicurati di averne una nella tua base il più rapidamente possibile! -tutorial.smeltery.text = Molti blocchi richiedono [orange]acciaio[] da produrre, che richiede una [orange] fonderia[] per la produzione. Mettine una qui. -tutorial.smelterySetup.text = Questa fonderia produrrà ora [orange]acciaio[] dal ferro in ingresso, usando il carbone come combustibile. -tutorial.tunnelExplain.text = Si noti inoltre che gli oggetti passano attraverso un[orange]tunnel[] e emergono dall'altra parte, passando attraverso il blocco di pietra. Tieni presente che i tunnel possono attraversare fino a 2 blocchi. -tutorial.end.text = E questo conclude il tutorial! In bocca al lupo! -text.keybind.title = Configurazione Tasti -keybind.move_x.name = move_x -keybind.move_y.name = move_y -keybind.select.name = seleziona -keybind.break.name = rompere -keybind.shoot.name = sparare -keybind.zoom_hold.name = zoom_hold -keybind.zoom.name = zoom -keybind.block_info.name = Informazioni blocco -keybind.menu.name = menu -keybind.pause.name = pausa -keybind.dash.name = corsa -keybind.chat.name = Chat -keybind.player_list.name = lista_giocatori -keybind.console.name = console -keybind.rotate_alt.name = rotate_alt -keybind.rotate.name = Ruotare -keybind.weapon_1.name = arma_1 -keybind.weapon_2.name = arma_2 -keybind.weapon_3.name = arma_3 -keybind.weapon_4.name = arma_4 -keybind.weapon_5.name = arma_5 -keybind.weapon_6.name = arma_6 -mode.text.help.title = Descrizione delle modalità -mode.waves.name = onde -mode.waves.description = modalità normale. risorse limitate e onde in entrata automatiche. -mode.sandbox.name = Sandbox -mode.sandbox.description = risorse infinite e nessun timer per le onde. -mode.freebuild.name = freebuild -mode.freebuild.description = risorse limitate e nessun timer per le onde. -upgrade.standard.name = Standard -upgrade.standard.description = Il mech standard. -upgrade.blaster.name = blaster -upgrade.blaster.description = Spara un proiettile lento, debole. -upgrade.triblaster.name = triblaster -upgrade.triblaster.description = Spara 3 proiettili a diffusione. -upgrade.clustergun.name = clustergun -upgrade.clustergun.description = Spara delle imprecise granate esplosive. -upgrade.beam.name = cannone a raggi -upgrade.beam.description = Spara un raggio laser penetrante a lungo raggio. -upgrade.vulcan.name = Vulcano -upgrade.vulcan.description = Spara una raffica di proiettili veloci. -upgrade.shockgun.name = shockgun -upgrade.shockgun.description = Spara a una devastante esplosione di shrapnel carichi. -item.stone.name = pietra -item.iron.name = ferro -item.coal.name = carbone -item.steel.name = acciaio -item.titanium.name = titanio -item.dirium.name = diridio -item.uranium.name = uranio -item.sand.name = sabbia -liquid.water.name = acqua -liquid.plasma.name = Plasma -liquid.lava.name = lava -liquid.oil.name = petrolio -block.weaponfactory.name = fabbrica d'armi -block.weaponfactory.fulldescription = Utilizzata per creare armi per il giocatore mech. Clicca per usare. Prende automaticamente le risorse dal core. -block.air.name = aria -block.blockpart.name = blockpart -block.deepwater.name = acque profonde -block.water.name = acqua -block.lava.name = lava -block.oil.name = petrolio -block.stone.name = pietra -block.blackstone.name = pietra nera -block.iron.name = ferro -block.coal.name = carbone -block.titanium.name = titanio -block.uranium.name = uranio -block.dirt.name = terra -block.sand.name = sabbia -block.ice.name = ghiaccio -block.snow.name = neve -block.grass.name = Erba -block.sandblock.name = blocco di sabbia -block.snowblock.name = blocco di neve -block.stoneblock.name = blocco di pietra -block.blackstoneblock.name = blocco di pietra nera -block.grassblock.name = blocco d'erba -block.mossblock.name = blocco di muschio -block.shrub.name = arbusto -block.rock.name = roccia -block.icerock.name = giaccio -block.blackrock.name = roccia nera -block.dirtblock.name = blocco di terra -block.stonewall.name = muro di pietra -block.stonewall.fulldescription = Un blocco difensivo poco costoso. Utile per proteggere il nucleo e le torrette nelle prime ondate. -block.ironwall.name = muro di ferro -block.ironwall.fulldescription = Un blocco difensivo di base. Fornisce protezione dai nemici. -block.steelwall.name = muro d'acciaio -block.steelwall.fulldescription = Un blocco difensivo standard. protezione adeguata dai nemici. -block.titaniumwall.name = muro di titanio -block.titaniumwall.fulldescription = Un forte blocco difensivo. Fornisce protezione dai nemici. -block.duriumwall.name = muro di diridio -block.duriumwall.fulldescription = Un blocco difensivo molto forte. Fornisce protezione dai nemici. -block.compositewall.name = muro composito -block.steelwall-large.name = grande muro di acciaio -block.steelwall-large.fulldescription = Un blocco difensivo standard. Si estende su più tessere. -block.titaniumwall-large.name = grande muro di titanio -block.titaniumwall-large.fulldescription = Un forte blocco difensivo. Si estende su più tessere. -block.duriumwall-large.name = grande muro di diridio -block.duriumwall-large.fulldescription = Un blocco difensivo molto forte. Si estende su più tessere. -block.titaniumshieldwall.name = muro schermato -block.titaniumshieldwall.fulldescription = Un forte blocco difensivo, con uno scudo incorporato extra. Richiede energia. Utilizza l'energia per assorbire i proiettili nemici. Si consiglia di utilizzare i booster di energia per fornire energia a questo blocco. -block.repairturret.name = torretta di riparazione -block.repairturret.fulldescription = Ripara i blocchi danneggiati vicini nel raggio di azione a un ritmo lento. Utilizza piccole quantità di energia. -block.megarepairturret.name = torretta di riparazione II -block.megarepairturret.fulldescription = Ripara i blocchi vicini danneggiati nel raggio di portata a ritmo moderato. Usa il potere. -block.shieldgenerator.name = generatore di scudi -block.shieldgenerator.fulldescription = Un blocco difensivo avanzato. Fa da scudo per tutti i blocchi in un raggio dalla posizione. Utilizza l'energia a una velocità ridotta quando è inattivo, ma scarica rapidamente energia sul contatto con i proiettili. -block.door.name = porta -block.door.fulldescription = Un blocco che può essere aperto e chiuso toccandolo. -block.door-large.name = grande porta -block.door-large.fulldescription = Un blocco che può essere aperto e chiuso toccandolo. -block.conduit.name = Condotto -block.conduit.fulldescription = Blocco di trasporto liquido di base. Funziona come un trasportatore, ma con liquidi. Ideale per pompe o altri condotti. Può essere usato come un ponte sui liquidi per nemici e giocatori. -block.pulseconduit.name = condotto di impulso -block.pulseconduit.fulldescription = Blocco di trasporto di liquidi avanzato. Trasporta i liquidi più velocemente e immagazzina più dei condotti standard. -block.liquidrouter.name = router liquido -block.liquidrouter.fulldescription = Funziona in modo simile a un router. Accetta input liquidi da un lato e li invia agli altri lati. Utile per separare il liquido da un singolo condotto in più condotti. -block.conveyor.name = trasportatore -block.conveyor.fulldescription = Blocco di trasporto basico. Sposta gli oggetti in avanti e li deposita automaticamente in torrette o crafters. Ruotabile. Può essere usato come un ponte sui liquidi per nemici e giocatori. -block.steelconveyor.name = trasportatore d'acciaio -block.steelconveyor.fulldescription = Blocco avanzato di trasporto. Sposta gli oggetti più velocemente rispetto ai trasportatori standard. -block.poweredconveyor.name = trasportatore di impulsi -block.poweredconveyor.fulldescription = Il blocco di trasporto di ultima generazione. Sposta gli oggetti più velocemente dei trasportatori in acciaio. -block.router.name = router -block.router.fulldescription = Accetta elementi da una direzione e li invia a 3 altre direzioni. Può anche memorizzare una certa quantità di oggetti. Utile per dividere i materiali da un trapano a più torrette. -block.junction.name = giunzione -block.junction.fulldescription = Funziona come un ponte per due nastri trasportatori che la attraversono. Utile in situazioni con due diversi trasportatori che trasportano materiali diversi in luoghi diversi. -block.conveyortunnel.name = tunnel di trasporto -block.conveyortunnel.fulldescription = Trasporta oggetti sotto blocchi. Per utilizzare, posizionare un tunnel che conduce nel blocco da scavare sotto il tunnel e uno sull'altro lato. Assicurarsi che entrambe le gallerie siano rivolte in direzioni opposte, cioè verso i blocchi in cui vengono immesse o in uscita. -block.liquidjunction.name = giunzione liquida -block.liquidjunction.fulldescription = Funziona come un ponte per due condotti di attraversamento. Utile in situazioni con due condotti diversi che trasportano liquidi diversi in luoghi diversi. -block.liquiditemjunction.name = giunzione di oggetti liquidi -block.liquiditemjunction.fulldescription = Funziona come un ponte per attraversare condutture e trasportatori. -block.powerbooster.name = power booster -block.powerbooster.fulldescription = Distribuisce l'energia a tutti i blocchi entro il suo raggio. -block.powerlaser.name = laser energetico -block.powerlaser.fulldescription = Crea un laser che trasmette energia al blocco di fronte ad esso. Non genera alcuna energia. Ideale per generatori o altri laser. -block.powerlaserrouter.name = router laser -block.powerlaserrouter.fulldescription = Laser che distribuisce la potenza in tre direzioni contemporaneamente. Utile in situazioni in cui è necessario alimentare più blocchi da un generatore. -block.powerlasercorner.name = angolo laser -block.powerlasercorner.fulldescription = Laser che distribuisce la potenza in due direzioni contemporaneamente. Utile in situazioni in cui è necessario alimentare più blocchi da un generatore e un router è impreciso. -block.teleporter.name = teletrasporto -block.teleporter.fulldescription = Blocco avanzato di trasporto dell'elemento. I teletrasportatori immettono gli oggetti ad altri teletrasportatori dello stesso colore. Non fa nulla se non esistono teletrasportatori dello stesso colore. Se esistono più teletrasporti dello stesso colore, ne viene selezionato uno casuale. Usa l'energia. Tocca per cambiare colore. -block.sorter.name = sorter -block.sorter.fulldescription = Ordina l'oggetto per tipo di materiale. Il materiale da accettare è indicato dal colore nel blocco. Tutti gli articoli che corrispondono al materiale di ordinamento vengono emessi in avanti, tutto il resto viene emesso a sinistra e a destra. -block.core.name = Centro -block.pump.name = pompa -block.pump.fulldescription = Pompa di liquidi da un blocco sorgente - di solito acqua, lava o petrolio. Emette liquido nei condotti nelle vicinanze. -block.fluxpump.name = pompaflux -block.fluxpump.fulldescription = Una versione avanzata della pompa. Memorizza più liquido e pompa il liquido più velocemente. -block.smelter.name = fonderia -block.smelter.fulldescription = Il blocco di lavorazione essenziale. Quando immesso 1 ferro e 1 carbone come combustibile, emette un acciaio. Si consiglia di inserire ferro e carbone su diverse cinghie per evitare l'intasamento. -block.crucible.name = crogiuolo -block.crucible.fulldescription = Un blocco di lavorazione avanzato. Immettendo 1 titanio, 1 acciaio e 1 carbone come combustibile, emette un diridio. Si consiglia di inserire carbone, acciaio e titanio su nastri diversi per evitare l'intasamento. -block.coalpurifier.name = estrattore di carbone -block.coalpurifier.fulldescription = Un blocco estrattore di base. Emette carbone quando viene fornito con grandi quantità di acqua e pietra. -block.titaniumpurifier.name = estrattore di titanio -block.titaniumpurifier.fulldescription = Un blocco estrattore standard. Produce il titanio quando viene fornito con grandi quantità di acqua e ferro. -block.oilrefinery.name = raffineria d'petrolio -block.oilrefinery.fulldescription = Affina grandi quantità di petrolio in oggetti di carbone. Utile per alimentare torrette a base di carbone quando le vene del carbone scarseggiano. -block.stoneformer.name = forma pietre -block.stoneformer.fulldescription = Solidifica la lava producendo pietra. Utile per produrre enormi quantità di pietra per depuratori di carbone. -block.lavasmelter.name = fonderia a lava -block.lavasmelter.fulldescription = Usa la lava per convertire il ferro in acciaio. Un'alternativa alle smelterie. Utile in situazioni in cui il carbone è scarso. -block.stonedrill.name = trapano di pietra -block.stonedrill.fulldescription = Il trapano essenziale. Se posizionato su delle piastrelle di pietra, emette una pietra a un ritmo lento indefinitamente. -block.irondrill.name = trapano di ferro -block.irondrill.fulldescription = Un trapano di base. Quando viene posizionato su delle piastrelle con il minerale ferro, emette il ferro a un ritmo lento indefinitamente. -block.coaldrill.name = trivella di carbone -block.coaldrill.fulldescription = Un trapano di base. Se posizionato su delle piastrelle di carbone, produce a tempo indeterminato il carbone a un ritmo lento. -block.uraniumdrill.name = trapano all'uranio -block.uraniumdrill.fulldescription = Un trapano avanzato. Se posizionato su delel piastrelle con dell'uranio, emette l'uranio a un ritmo lento indefinitamente. -block.titaniumdrill.name = trapano in titanio -block.titaniumdrill.fulldescription = Un trapano avanzato. Se posizionato su delle piastrelle di titanio, emette il titanio a un ritmo lento indefinitamente. -block.omnidrill.name = omnidrill -block.omnidrill.fulldescription = L'ultimo trapano. Trapanerà qualsiasi minerale su cui è posizionato ad un ritmo rapido. -block.coalgenerator.name = generatore di carbone -block.coalgenerator.fulldescription = Il generatore essenziale. Genera potenza dal carbone. Emette potenza come laser sui suoi 4 lati. -block.thermalgenerator.name = generatore termico -block.thermalgenerator.fulldescription = Genera energia dalla lava. Emette energia come laser sui suoi 4 lati. -block.combustiongenerator.name = generatore a combustione -block.combustiongenerator.fulldescription = Genera energia dall'petrolio. Emette energia come laser sui suoi 4 lati. -block.rtgenerator.name = Generatore RTG -block.rtgenerator.fulldescription = Genera piccole quantità di energia dal decadimento radioattivo dell'uranio. Emette potenza come laser sui suoi 4 lati. -block.nuclearreactor.name = reattore nucleare -block.nuclearreactor.fulldescription = Una versione avanzata del Generatore RTG e il massimo generatore di energia. Genera potenza dall'uranio. Richiede un raffreddamento costante dell'acqua. Altamente volatile; esploderà violentemente se vengono fornite quantità insufficienti di refrigerante. -block.turret.name = torretta -block.turret.fulldescription = Una torretta semplice ed economica. Usa la pietra per le munizioni. Ha un raggio leggermente superiore rispetto alla doppia torretta. -block.doubleturret.name = doppia torretta -block.doubleturret.fulldescription = Una versione leggermente più potente della torretta. Usa la pietra per le munizioni. Fa molto più danni, ma ha un raggio più basso. Spara due proiettili. -block.machineturret.name = torretta di gattling -block.machineturret.fulldescription = Una torretta standard a tutto tondo. Usa il ferro per le munizioni. Ha una velocità di fuoco veloce con danni decenti. -block.shotgunturret.name = torretta di splitter -block.shotgunturret.fulldescription = Una torretta standard. Usa il ferro per le munizioni. Spara una diffusione di 7 proiettili. Gittata inferiore, ma maggiore danno inflitto rispetto alla torretta gattling. -block.flameturret.name = lanciafiamme -block.flameturret.fulldescription = Torretta avanzata a distanza ravvicinata. Usa carbone per munizioni. Ha una portata molto bassa, ma un danno molto alto. Buono per luoghi chiusi. Consigliato per essere usato dietro i muri. -block.sniperturret.name = torretta ellettromagnetica -block.sniperturret.fulldescription = Torretta avanzata a lungo raggio. Utilizza l'acciaio come munizioni. Danno molto alto, ma bassa velocità di fuoco. Costoso da usare, ma può essere posizionato lontano dalle linee nemiche a causa della sua portata. -block.mortarturret.name = torretta di sfogo -block.mortarturret.fulldescription = Torretta a getto d'acqua avanzato a bassa precisione. Usa carbone per munizioni. Spara una raffica di proiettili che esplodono in shrapnel. Utile per grandi folle di nemici. -block.laserturret.name = torretta laser -block.laserturret.fulldescription = Avanzata torretta a bersaglio singolo. Usa l'energia Buona torretta a medio raggio a tutto tondo. Ingaggio singolo. Non manca mai il bersaglio. -block.waveturret.name = Torretta tesla -block.waveturret.fulldescription = Torretta multi-target avanzata. Usa l'energia. Gamma media Non manca mai. Danno basso, ma può colpire più nemici contemporaneamente con dei fulmini a catena. -block.plasmaturret.name = torretta a plasma -block.plasmaturret.fulldescription = Versione altamente avanzata del lanciafiamme. Usa il carbone come munizione. Danno molto alto, da basso a medio raggio. -block.chainturret.name = torretta a catena -block.chainturret.fulldescription = L'ultima torretta a fuoco rapido. Usa l'uranio come munizione. Spara grossi proiettili ad un alto tasso di fuoco. Gamma media Si estende su più tessere. Estremamente duro. -block.titancannon.name = cannone di titano -block.titancannon.fulldescription = L'ultima torretta a lungo raggio. Usa l'uranio come munizione. Spara grossi proiettili di schizzi a una velocità media di fuoco. Lungo raggio. Si estende su più tessere. Estremamente duro. -block.playerspawn.name = spawngiocatore -block.enemyspawn.name = spawnnemico diff --git a/core/assets/bundles/bundle_ja.properties b/core/assets/bundles/bundle_ja.properties new file mode 100644 index 0000000000..3bdc536779 --- /dev/null +++ b/core/assets/bundles/bundle_ja.properties @@ -0,0 +1,947 @@ +credits.text = Created by [ROYAL]Anuken[] - [SKY]anukendev@gmail.com[]\n\n[GRAY](In case you can't tell, this text is currently unfinished.\nTranslators, don't edit it yet!) +credits = クレジット +contributors = 翻訳や協力してくださった方々 +discord = DiscordのMindustryに参加! +link.discord.description = Mindustryの公式Discordグループ +link.github.description = このゲームのソースコード +link.dev-builds.description = 不安定な開発版 +link.trello.description = 公式 Trelloボード で実装予定の機能をチェック +link.itch.io.description = itch.io でPC版のダウンロードやweb版で遊ぼう +link.google-play.description = Google Playのストア +link.wiki.description = 公式 Mindustry Wiki +linkfail = リンクを開けませんでした!\nURLをクリップボードにコピーしました。 +screenshot = スクリーンショットを {0} に保存しました。 +screenshot.invalid = マップが広すぎます。スクリーンショットに必要なメモリが足りない可能性があります。 +gameover = ゲームオーバー +gameover.pvp = [accent] {0}[] チームの勝利! +highscore = [accent]ハイスコアを記録! +stat.wave = 防衛したウェーブ:[accent] {0} +stat.enemiesDestroyed = 敵による破壊数:[accent] {0} +stat.built = 建設した建造物数:[accent] {0} +stat.destroyed = 破壊した建造物数:[accent] {0} +stat.deconstructed = 解体した建造物数:[accent] {0} +stat.delivered = 獲得した資源: +stat.rank = 最終ランク: [accent]{0} +placeline = ブロックを選択しました。\n[accent]少し長押し[]して、好きな方向にドラッグすると[accent]一直線上にブロックを設置[]することができます。\nやってみよう。 +removearea = 撤去モードが選択されました。\n[accent]少し長押し[]して、ドラッグすると[accent]範囲内のブロックを撤去[]することができます。\nやってみよう。 +launcheditems = [accent]回収したアイテム +map.delete = マップ "[accent]{0}[]" を削除してもよろしいですか? +level.highscore = ハイスコア: [accent]{0} +level.select = レベル選択 +level.mode = ゲームモード: +showagain = 次回以降表示しない +coreattack = < コアが攻撃を受けています! > +nearpoint = [[ [scarlet]直ちに出現ポイントより離脱せよ[] ]\n殲滅されます +outofbounds = [[ 区域外 ]\n[]自爆まであと {0} 秒 +database = コアデーターベース +savegame = 保存 +loadgame = 読み込む +joingame = 参加 +addplayers = プレイヤーを追加/削除 +customgame = カスタムプレイ +newgame = 新しく始める +none = <なし> +minimap = ミニマップ +close = 閉じる +quit = 終了 +maps = マップ +continue = 続ける +maps.none = [LIGHT_GRAY]マップが見つかりませんでした! +about.button = 情報 +name = プレイヤー名: +noname = [accent]プレイヤー名[]を入力してください。 +filename = ファイル名: +unlocked = 新しい要素をアンロック! +completed = [accent]完了 +techtree = テックツリー +research.list = [LIGHT_GRAY]調査: +research = 調査 +researched = [LIGHT_GRAY]{0} の調査が完了しました +players = {0} 人がオンライン +players.single = {0} 人がオンライン +server.closing = [accent]サーバーを閉じています... +server.kicked.kick = サーバからキックされました! +server.kicked.serverClose = サーバーが閉じられました。 +server.kicked.clientOutdated = 古いクライアントです! ゲームをアップデートしてください! +server.kicked.serverOutdated = 古いサーバーです! ホストに更新してもらってください! +server.kicked.banned = サーバーからブロックされています。 +server.kicked.recentKick = 直前にキックされています。\nもう一度接続できるまでお待ちください。 +server.kicked.nameInUse = このサーバーでは、\nすでに同じ名前が使用されています。 +server.kicked.nameEmpty = 無効な名前です。 +server.kicked.idInUse = すでにサーバーに参加しています! 二つのアカウントでの同時接続は許可されていません。 +server.kicked.customClient = このサーバーはカスタムビルドをサポートしていません。公式版をダウンロードしてください。 +server.kicked.gameover = ゲームオーバー! +host.info = [accent]ホスト[]をすると、ポート[scarlet]6567[]でサーバーが開かれまます。\n同じ[LIGHT_GRAY]WiFiやローカル上のネットワークなど[]ではサーバーリストに表示されるようになります。\n\nIPアドレスで他のところからも接続できるようにするには、[accent]ポート開放[]が必要です。\n\n注意: もしLAN上のゲームに参加できない場合、Mindustryがファイアーウォールの設定でローカルネットワークへの接続が許可されているかを確認してください。 +join.info = ここでは、[accent]サーバーのIPアドレス[]から接続したり、[accent]ローカル上[]のサーバーを探したりすることができます。\nLANやWAN上の両方のマルチプレイに対応しています。\n\n[LIGHT_GRAY]注意: 世界中のサーバーの一覧ではありません。他人のサーバーにIPアドレスで接続したい場合は、あらかじめホスト側にIPアドレスをお尋ねください。 +hostserver = ホスト +hostserver.mobile = ホスト +host = ホスト +hosting = [accent]サーバーを開いています... +hosts.refresh = 更新 +hosts.discovering = LAN上のサーバーを探索中 +server.refreshing = サーバーを更新中 +hosts.none = [lightgray]ローカル上のサーバーが見つかりませんでした! +host.invalid = [scarlet]ホストに接続できません。 +trace = プレイヤーの記録 +trace.playername = プレイヤー名: [accent]{0} +trace.ip = IP: [accent]{0} +trace.id = ユニークID: [accent]{0} +trace.mobile = Mobile Client: [accent]{0} +trace.modclient = カスタムクライアント: [accent]{0} +invalidid = 無効なクライアントIDです! バグ報告してください。 +server.bans = ブロック +server.bans.none = ブロックされたプレイヤーは見つかりませんでした! +server.admins = 管理者 +server.admins.none = 管理者はいません! +server.add = サーバーを追加 +server.delete = サーバーを削除してもよろしいですか? +server.hostname = ホスト: {0} +server.edit = サーバーを編集 +server.outdated = [crimson]古いサーバーです![] +server.outdated.client = [crimson]古いクライアントです![] +server.version = [lightgray]バージョン: {0} {1} +server.custombuild = [yellow]カスタムビルド +confirmban = このプレイヤーをブロックしてもよろしいですか? +confirmkick = このプレイヤーをキックしてもよろしいですか? +confirmunban = このプレイヤーのブロックを解除してもよろしいですか? +confirmadmin = このプレイヤーを管理者にしてもよろしいですか? +confirmunadmin = このプレイヤーを管理者から削除してもよろしいですか? +joingame.title = サーバーに参加 +joingame.ip = アドレス: +disconnect = 接続が切断されました。 +disconnect.data = ワールドデータの読み込みに失敗しました! +connecting = [accent]接続中... +connecting.data = [accent]ワールドデータを読み込み中... +server.port = ポート: +server.addressinuse = アドレスがすでに使用されています! +server.invalidport = 無効なポート番号です! +server.error = [crimson]サーバーのホストエラー: [accent]{0} +save.old = この古いバージョンのセーブデータは使用することができません。\n\n[LIGHT_GRAY]セーブデータの下位互換性の実装は正式版4.0で行われます。 +save.new = 新規保存 +save.overwrite = このスロットに上書きしてもよろしいですか? +overwrite = 上書き +save.none = セーブデータがに見つかりませんでした! +saveload = [accent]セーブ中... +savefail = ゲームの保存に失敗しました! +save.delete.confirm = このセーブデータを削除してよろしいですか? +save.delete = 削除 +save.export = エクスポート +save.import.invalid = [accent]無効なセーブデータです! +save.import.fail = [crimson]セーブデータのインポートに失敗しました: [accent]{0} +save.export.fail = [crimson]セーブデータのエクスポートに失敗しました: [accent]{0} +save.import = セーブデータを読み込む +save.newslot = セーブ名: +save.rename = 名前を変更 +save.rename.text = 新しい名前: +selectslot = セーブデータを選択 +slot = [accent]スロット {0} +save.corrupted = [accent]セーブファイルが無効、または破損しました!\nゲームのアップデート直後の場合、恐らくセーブデータのフォーマットの変更によるもので、バグではありません。 +empty = <空> +on = オン +off = オフ +save.autosave = 自動保存: {0} +save.map = マップ: {0} +save.wave = ウェーブ {0} +save.difficulty = 難易度: {0} +save.date = 最終保存日時: {0} +save.playtime = プレイ時間: {0} +warning = 警告 +confirm = 確認 +delete = 削除 +ok = OK +open = 開く +customize = カスタマイズ +cancel = キャンセル +openlink = リンクを開く +copylink = リンクをコピー +back = 戻る +quit.confirm = 終了してもよろしいですか? +changelog.title = 変更履歴 +changelog.loading = 変更履歴を取得中... +changelog.error.android = [accent]変更履歴はAndroid4.4または、それ以下では動作しない場合があります!\nこれはAndroidの内部バグによるものです。 +changelog.error.ios = [accent]変更履歴はiOSに対応していません。 +changelog.error = [scarlet]変更履歴を取得できませんでした!\nインターネット接続を確認してください。 +changelog.current = [yellow][[現在のバージョン] +changelog.latest = [accent][[最新版] +loading = [accent]読み込み中... +saving = [accent]保存中... +wave = [accent]ウェーブ {0} +wave.waiting = [LIGHT_GRAY]次のウェーブまで {0} 秒 +wave.waveInProgress = [LIGHT_GRAY]ウェーブ進行中 +waiting = [LIGHT_GRAY]待機中... +waiting.players = プレイヤーを待っています... +wave.enemies = [LIGHT_GRAY]敵は残り {0} 体 +wave.enemy = [LIGHT_GRAY]敵は残り {0} 体 +loadimage = 画像を読み込む +saveimage = 画像を保存 +unknown = 不明 +custom = カスタム +builtin = 組み込み +map.delete.confirm = マップを削除してもよろしいですか? これは元に戻すことができません! +map.random = [accent]ランダムマップ +map.nospawn = このマップにはプレイヤーの出現するコアがありません! エディターで[ROYAL]青い[]コアをマップに追加してください。 +map.nospawn.pvp = このマップにはプレイヤーの敵が出現するコアがありません! エディターで[SCARLET]青以外[]のコアをマップに追加してください。 +map.nospawn.attack = このマップにはプレイヤーを攻撃する敵のコアがありません! エディターから[SCARLET]赤い[]コアを追加してください。 +map.invalid = マップの読み込みエラー: ファイルが無効、または破損しています。 +editor.brush = ブラシ +editor.openin = エディターで開く +editor.oregen = 鉱石の生成 +editor.oregen.info = 鉱石の生成: +editor.mapinfo = マップ情報 +editor.author = 作者: +editor.description = 説明: +editor.waves = ウェーブ: +editor.rules = Rules: +editor.ingame = Edit In-Game +waves.title = ウェーブ +waves.remove = 削除 +waves.never = <永久> +waves.every = ウェーブ +waves.waves = ごとに出現 +waves.perspawn = 体出現 +waves.to = から +waves.boss = ボス +waves.preview = プレビュー +waves.edit = 編集... +waves.copy = クリップボードにコピー +waves.load = クリップボードから読み込む +waves.invalid = クリップボードのウェーブは無効なウェーブです。 +waves.copied = ウェーブをコピーしました。 +editor.default = [LIGHT_GRAY]<デフォルト> +edit = 編集... +editor.name = 名前: +editor.spawn = Spawn Unit +editor.removeunit = Remove Unit +editor.teams = チーム +editor.elevation = 標高 +editor.errorload = ファイルの読み込みエラー:\n[accent]{0} +editor.errorsave = ファイルの保存エラー:\n[accent]{0} +editor.errorimage = That's an image, not a map. Don't go around changing extensions expecting it to work.\n\nIf you want to import a legacy map, use the 'import legacy map' button in the editor. +editor.errorlegacy = This map is too old, and uses a legacy map format that is no longer supported. +editor.errorheader = This map file is either not valid or corrupt. +editor.errorname = マップに名前が設定されていません。 +editor.update = 更新 +editor.randomize = ランダム +editor.apply = 適用 +editor.generate = 生成 +editor.resize = リサイズ +editor.loadmap = マップを読み込む +editor.savemap = マップを保存 +editor.saved = 保存しました! +editor.save.noname = マップに名前が設定されていません! メニューの 'マップ情報' から設定してください。 +editor.save.overwrite = 組み込みマップを上書きしようとしています! メニューの 'マップ情報' から異なる名前に設定してください。 +editor.import.exists = [scarlet]インポートできませんでした:[] '{0}' はすでに組み込みマップの名前として存在します! +editor.import = インポート... +editor.importmap = マップをインポート +editor.importmap.description = すでに存在しているマップを読み込む +editor.importfile = ファイルをインポート +editor.importfile.description = 外部マップファイルを読み込む +editor.importimage = 古いイメージをインポート +editor.importimage.description = 外部イメージファイルを読み込む +editor.export = エクスポート... +editor.exportfile = ファイルをエクスポート +editor.exportfile.description = マップファイルをエクスポートする +editor.exportimage = 地形イメージをエクスポート +editor.exportimage.description = イメージファイルをエクスポートする +editor.loadimage = 地形をインポート +editor.saveimage = 地形をエクスポート +editor.unsaved = [scarlet]保存されていない変更があります![]\n終了してもよろしいですか? +editor.resizemap = マップをリサイズ +editor.mapname = マップ名: +editor.overwrite = [accent]警告!\nすでに存在するマップを上書きします。 +editor.overwrite.confirm = [scarlet]警告![] すでに同じ名前のマップが存在します。上書きしてもよろしいですか? +editor.selectmap = 読み込むマップを選択: +filters.empty = [LIGHT_GRAY]フィルターが設定されていません! 下のボタンからフィルターを追加してください。 +filter.distort = ゆがみ +filter.noise = ノイズ +filter.ore = 鉱石 +filter.rivernoise = リバーノイズ +filter.scatter = 分散 +filter.terrain = 地形 +filter.option.scale = スケール +filter.option.chance = 確率 +filter.option.mag = マグニチュード +filter.option.threshold = スレッシュホールド +filter.option.circle-scale = サークルスケール +filter.option.octaves = オクターブ +filter.option.falloff = フォールオフ +filter.option.block = ブロック +filter.option.floor = 地面 +filter.option.wall = 壁 +filter.option.ore = 鉱石 +filter.option.floor2 = 2番目の地面 +filter.option.threshold2 = 2番目のスレッシュホールド +filter.option.radius = Radius +filter.option.percentile = Percentile +width = 幅: +height = 高さ: +menu = メニュー +play = プレイ +load = 読み込む +save = 保存 +fps = FPS: {0} +tps = TPS: {0} +ping = Ping: {0}ms +language.restart = ゲームを再起動後、言語設定が有効になります。 +settings = 設定 +tutorial = チュートリアル +editor = エディター +mapeditor = マップエディター +donate = 寄附 +abandon = 撤退 +abandon.text = このゾーンとすべての資源が敵に奪われます。 +locked = ロック +complete = [LIGHT_GRAY]達成済み: +zone.requirement = ゾーン {1} でウェーブ {0} を達成 +resume = 再開ゾーン:\n[LIGHT_GRAY]{0} +bestwave = [LIGHT_GRAY]最高ウェーブ: {0} +launch = 発射 +launch.title = 発射成功 +launch.next = [LIGHT_GRAY]次の発射は ウェーブ {0} +launch.unable = [scarlet]発射できません[] 敵によって妨害されています。 +launch.confirm = すべての資源がコアに搬入されます。\nもうこの基地には戻ってくることはできません。 +uncover = 開放 +configure = 積荷の設定 +configure.locked = [LIGHT_GRAY]ウェーブ {0} を達成すると積荷を設定できるようになります。 +zone.unlocked = [LIGHT_GRAY]{0} がアンロックされました. +zone.requirement.complete = ウェーブ {0} を達成:\n{1} の開放条件を達成しました。 +zone.config.complete = ウェーブ {0} を達成:\n積荷の設定が解除されました。 +zone.resources = 発見した資源: +add = 追加... +boss.health = ボスのHP +connectfail = [crimson]サーバーへの接続できませんでした:\n\n[accent]{0} +error.unreachable = サーバーに到達できません。\nアドレスは正しいですか? +error.invalidaddress = 無効なアドレスです。 +error.timedout = タイムアウトしました!\nホストがポート開放されているかを確認してください。また、このアドレスは無効なアドレスではありません! +error.mismatch = パケットエラー:\n恐らくクライアント/サーバーのバージョンが一致していません。\nゲームとホストが最新版のMindustryかどうかを確認してください! +error.alreadyconnected = すでに接続されています。 +error.mapnotfound = マップファイルが見つかりません! +error.io = ネットワークエラーです。 +error.any = 不明なネットワークエラーです。 +zone.groundZero.name = グラウンド · ゼロ +zone.desertWastes.name = デザート · ウェーツ +zone.craters.name = ザ · クレーター +zone.frozenForest.name = フローズン · フォレスト +zone.ruinousShores.name = ルーイナス · ショアーズ +zone.stainedMountains.name = ステインド · マウンテン +zone.desolateRift.name = ディサレット · リフト +zone.nuclearComplex.name = ニュークリア · プロダクション · コンプレックス +zone.overgrowth.name = オーバーグロウス +zone.tarFields.name = ター · フィールズ +settings.language = 言語 +settings.reset = デフォルトにリセット +settings.rebind = 再設定 +settings.controls = コントロール +settings.game = ゲーム +settings.sound = サウンド +settings.graphics = グラフィック +settings.cleardata = データを削除... +settings.clear.confirm = データを削除してもよろしいですか?\nこれを元に戻すことはできません! +settings.clearall.confirm = [scarlet]警告![]\nこれはすべてのデータが削除されます。これにはセーブデータ、マップ、アンロック、キーバインドが含まれます。\n「ok」 を押すと、すべてのデータが削除され、自動的に終了します。 +settings.clearunlocks = アンロックを削除 +settings.clearall = すべてを削除 +paused = [accent]< ポーズ > +yes = はい +no = いいえ +info.title = 情報 +error.title = [crimson]エラーが発生しました +error.crashtitle = エラーが発生しました +blocks.input = 搬入 +blocks.output = 搬出 +blocks.booster = ブースト +block.unknown = [LIGHT_GRAY]??? +blocks.powercapacity = 電力容量 +blocks.powershot = 電力/ショット +blocks.targetsair = 対空攻撃 +blocks.targetsground = 対地攻撃 +blocks.itemsmoved = 輸送速度 +blocks.launchtime = 発射の待機時間 +blocks.shootrange = 範囲 +blocks.size = 大きさ +blocks.liquidcapacity = 液体容量 +blocks.powerrange = 電力範囲 +blocks.poweruse = 電力使用量 +blocks.powerdamage = 電力/ダメージ +blocks.itemcapacity = アイテム容量 +blocks.basepowergeneration = 基本発電量 +blocks.productiontime = 製造速度 +blocks.repairtime = ブロックの完全修復速度 +blocks.speedincrease = 速度向上 +blocks.range = 範囲 +blocks.drilltier = ドリル +blocks.drillspeed = 基本採掘速度 +blocks.boosteffect = ブースト効果 +blocks.maxunits = 最大ユニット数 +blocks.health = 耐久値 +blocks.buildtime = 建設時間 +blocks.inaccuracy = 精度のずれ +blocks.shots = ショット +blocks.reload = ショット/秒 +blocks.ammo = 弾薬 +bar.drillspeed = 採掘速度: {0}/秒 +bar.efficiency = 効率: {0}% +bar.powerbalance = 電力: {0}/秒 +bar.poweramount = 電力: {0} +bar.poweroutput = 電力発電量: {0} +bar.items = アイテム: {0} +bar.liquid = 液体 +bar.heat = 熱 +bar.power = 電力 +bar.progress = 建設状況 +bar.spawned = ユニット数: {0}/{1} +bullet.damage = [stat]{0}[lightgray] ダメージ +bullet.splashdamage = [stat]{0}[lightgray] 範囲ダメージ 約[stat] {1}[lightgray] タイル +bullet.incendiary = [stat]焼夷弾 +bullet.homing = [stat]ホーミング +bullet.shock = [stat]電撃 +bullet.frag = [stat]爆発弾 +bullet.knockback = [stat]{0}[lightgray] ノックバック +bullet.freezing = [stat]フリーズ +bullet.tarred = [stat]タール弾 +bullet.multiplier = [stat]弾薬 {0}[lightgray]倍 +bullet.reload = [stat]リロード速度 {0}[lightgray]倍 +unit.blocks = ブロック +unit.powersecond = 電力/秒 +unit.liquidsecond = 液体/秒 +unit.itemssecond = アイテム/秒 +unit.liquidunits = 液体 +unit.powerunits = 電力 +unit.degrees = 度 +unit.seconds = 秒 +unit.persecond = /秒 +unit.timesspeed = 倍の速度 +unit.percent = % +unit.items = アイテム +category.general = 一般 +category.power = 電力 +category.liquids = 液体 +category.items = アイテム +category.crafting = 搬入/搬出 +category.shooting = ショット +category.optional = 強化オプション +setting.landscape.name = 横画面で固定 +setting.shadows.name = 影 +setting.linear.name = Linear Filtering +setting.animatedwater.name = 水のアニメーション +setting.animatedshields.name = シールドのアニメーション +setting.antialias.name = アンチエイリアス[LIGHT_GRAY] (再起動が必要)[] +setting.indicators.name = 敵/味方の方角表示 +setting.autotarget.name = オートターゲット +setting.fpscap.name = 最大FPS +setting.fpscap.none = なし +setting.fpscap.text = {0} FPS +setting.swapdiagonal.name = 常に斜め設置 +setting.difficulty.training = トレーニング +setting.difficulty.easy = イージー +setting.difficulty.normal = ノーマル +setting.difficulty.hard = ハード +setting.difficulty.insane = クレイジー +setting.difficulty.name = 難易度: +setting.screenshake.name = 画面の揺れ +setting.effects.name = 画面効果 +setting.sensitivity.name = 操作感度 +setting.saveinterval.name = 自動保存間隔 +setting.seconds = {0} 秒 +setting.fullscreen.name = フルスクリーン +setting.borderlesswindow.name = Borderless Window[LIGHT_GRAY] (may require restart) +setting.fps.name = FPSを表示 +setting.vsync.name = VSync +setting.lasers.name = 電力線を表示 +setting.pixelate.name = ピクセル化 [LIGHT_GRAY](パフォーマンスが低下する可能性があります) +setting.minimap.name = ミニマップを表示 +setting.musicvol.name = 音楽 音量 +setting.mutemusic.name = 音楽をミュート +setting.sfxvol.name = 効果音 音量 +setting.mutesound.name = 効果音をミュート +setting.crashreport.name = 匿名でクラッシュレポートを送信する +setting.chatopacity.name = チャットの透明度 +setting.playerchat.name = ゲーム内にチャットを表示 +keybind.title = キーバインドを再設定 +category.general.name = 一般 +category.view.name = 表示 +category.multiplayer.name = マルチプレイ +command.attack = 攻撃 +command.retreat = 後退 +command.patrol = 巡回 +keybind.gridMode.name = ブロック選択 +keybind.gridModeShift.name = カテゴリー選択 +keybind.press = キーを押してください... +keybind.press.axis = 軸またはキーを押してください... +keybind.screenshot.name = スクリーンショット +keybind.move_x.name = 左右移動 +keybind.move_y.name = 上下移動 +keybind.select.name = 選択/ショット +keybind.diagonal_placement.name = 斜め設置 +keybind.pick.name = ブロックの選択 +keybind.break_block.name = ブロックの破壊 +keybind.deselect.name = 選択解除 +keybind.shoot.name = ショット +keybind.zoom_hold.name = 長押しズーム +keybind.zoom.name = ズーム +keybind.menu.name = メニュー +keybind.pause.name = ポーズ +keybind.minimap.name = ミニマップ +keybind.dash.name = ダッシュ +keybind.chat.name = チャット +keybind.player_list.name = プレイヤーリスト +keybind.console.name = コンソール +keybind.rotate.name = 回転 +keybind.toggle_menus.name = メニュー切り替え +keybind.chat_history_prev.name = 前のチャット履歴 +keybind.chat_history_next.name = 次のチャット履歴 +keybind.chat_scroll.name = チャットスクロール +keybind.drop_unit.name = ドロップユニット +keybind.zoom_minimap.name = ミニマップのズーム +mode.help.title = モード説明 +mode.survival.name = サバイバル +mode.survival.description = 通常のモードです。 資源も限られる中、自動的にウェーブが進行していきます。 +mode.sandbox.name = サンドボックス +mode.sandbox.description = 無限の資源があり、ウェーブを自由に進行できます。 +mode.pvp.name = PvP +mode.pvp.description = エリア内で他のプレイヤーと戦います。 +mode.attack.name = アタック +mode.attack.description = ウェーブがなく、敵の基地を破壊することを目指します。 +mode.custom = カスタムルール +rules.infiniteresources = 資源の無限化 +rules.wavetimer = ウェーブの自動進行 +rules.waves = ウェーブ +rules.enemyCheat = 敵の資源の無限化 +rules.unitdrops = ユニットの戦利品 +rules.unitbuildspeedmultiplier = ユニットの製造速度倍率 +rules.unithealthmultiplier = ユニットの体力倍率 +rules.playerhealthmultiplier = プレイヤーの体力倍率 +rules.playerdamagemultiplier = プレイヤーのダメージ倍率 +rules.unitdamagemultiplier = ユニットのダメージ倍率 +rules.enemycorebuildradius = 敵コア周辺の建設禁止区域の半径: [LIGHT_GRAY] (タイル) +rules.respawntime = 復活までの待機時間:[LIGHT_GRAY] (秒) +rules.wavespacing = ウェーブ間の待機時間:[LIGHT_GRAY] (秒) +rules.buildcostmultiplier = 建設コストの倍率 +rules.buildspeedmultiplier = 建設速度の倍率 +rules.waitForWaveToEnd = 敵が倒されるまでウェーブの進行を中断 +rules.dropzoneradius = 出現範囲の半径:[LIGHT_GRAY] (タイル) +rules.respawns = ウェーブごとの最大復活回数 +rules.limitedRespawns = 復活回数の制限 +rules.title.waves = ウェーブ +rules.title.respawns = 復活 +rules.title.resourcesbuilding = 資源 & 建設 +rules.title.player = プレイヤー +rules.title.enemy = 敵 +rules.title.unit = ユニット +content.item.name = アイテム +content.liquid.name = 液体 +content.unit.name = ユニット +content.block.name = ブロック +content.mech.name = 機体 +item.copper.name = 銅 +item.copper.description = 便利な鉱石です。様々なブロックの材料として幅広く使われています。 +item.lead.name = 鉛 +item.lead.description = 一般的で手軽な鉱石です。機械や液体輸送ブロックなどに使われます。 +item.coal.name = 石炭 +item.coal.description = 一般的で有用な燃料です。 +item.graphite.name = 黒鉛 +item.titanium.name = チタン +item.titanium.description = 希少で非常に軽量な金属です。液体輸送やドリル、航空機などで使われます。 +item.thorium.name = トリウム +item.thorium.description = 放射性を持つ高密度な金属です。建造物の支えや核燃料として使われます。 +item.silicon.name = シリコン +item.silicon.description = 非常に有用な半導体でソーラーパネルや多くの複雑な機械に応用できます。 +item.plastanium.name = プラスタニウム +item.plastanium.description = 軽量で伸縮性のある材料です。高度な航空機や分散型の弾薬として使用されます。 +item.phase-fabric.name = フェーズファイバー +item.phase-fabric.description = 極めて軽量な素材です。高度な機械や自己修復技術に使用されます。 +item.surge-alloy.name = サージ合金 +item.surge-alloy.description = 電気的特性を持った特殊な合金です。 +item.spore-pod.name = 胞子ポッド +item.spore-pod.description = 石油や爆薬、燃料への転換として使用されます。 +item.sand.name = 砂 +item.sand.description = 合金や融剤など広く使用されている一般的な材料です。 +item.blast-compound.name = 爆発性化合物 +item.blast-compound.description = 爆弾や爆発物に使われる揮発性の化合物です。燃料として燃やすこともできますが、お勧めしません。 +item.pyratite.name = ピラタイト +item.pyratite.description = 焼夷兵器などに使われる非常に燃えやすい物質です。 +item.metaglass.name = メタガラス +item.metaglass.description = とても頑丈な強化ガラスです。液体の輸送やタンクとして幅広く使われています。 +item.scrap.name = スクラップ +item.scrap.description = 昔の建造物やユニットの残骸です。様々な種類の金属が微量に含まれています。 +liquid.water.name = 水 +liquid.slag.name = スラグ +liquid.oil.name = 石油 +liquid.cryofluid.name = 冷却水 +mech.alpha-mech.name = アルファ +mech.alpha-mech.weapon = 重機関砲 +mech.alpha-mech.ability = 再生 +mech.alpha-mech.description = 一般的な機体です。十分な速度と攻撃性能です。 +mech.delta-mech.name = デルタ +mech.delta-mech.weapon = 電撃砲 +mech.delta-mech.ability = 放電 +mech.delta-mech.description = 突撃攻撃が可能な素早く軽量化された機体です。建造物にはダメージをほとんど与えられませんが、電撃によって多くの敵に攻撃することができます。 +mech.tau-mech.name = タウ +mech.tau-mech.weapon = 再構築レーザー +mech.tau-mech.ability = リペアバースト +mech.tau-mech.description = 支援型機体です。攻撃を受けた味方のブロックを修復します。修復能力によって周辺の味方を修復します。 +mech.omega-mech.name = オメガ +mech.omega-mech.weapon = ロケット弾 +mech.omega-mech.ability = 特殊装甲 +mech.omega-mech.description = 最前線での攻撃向けに作られた大型機体です。特殊装甲によってダメージの90%を防ぐことができます。 +mech.dart-ship.name = ダーツ +mech.dart-ship.weapon = 機関砲 +mech.dart-ship.description = 一般的な機体です。軽く高速で使いやすいですが、攻撃能力はほとんどなく採掘速度も遅いのが難点です。 +mech.javelin-ship.name = ジャベリン +mech.javelin-ship.description = 突撃攻撃型の機体です。最初の速度は遅いですが、敵基地が近づくと飛行能力が飛躍的に高まり、電撃やミサイルで大きなダメージを与えることができます。 +mech.javelin-ship.weapon = バーストミサイル +mech.javelin-ship.ability = 放電ブースター +mech.trident-ship.name = トライデント +mech.trident-ship.description = 強力な爆撃機です。強固な装甲を有しています。 +mech.trident-ship.weapon = 爆弾 +mech.glaive-ship.name = グライブ +mech.glaive-ship.description = 重武装された大型攻撃機です。焼夷弾が装備され、機体の中でも優れた加速と最高速度を有しています。 +mech.glaive-ship.weapon = 焼夷弾 +item.explosiveness = [LIGHT_GRAY]爆発性: {0}% +item.flammability = [LIGHT_GRAY]可燃性: {0}% +item.radioactivity = [LIGHT_GRAY]放射能: {0}% +unit.health = [LIGHT_GRAY]耐久値: {0} +unit.speed = [LIGHT_GRAY]速度: {0} +mech.weapon = [LIGHT_GRAY]武器: {0} +mech.health = [LIGHT_GRAY]体力: {0} +mech.itemcapacity = [LIGHT_GRAY]アイテム容量: {0} +mech.minespeed = [LIGHT_GRAY]採掘速度: {0} +mech.minepower = [LIGHT_GRAY]採掘性能: {0} +mech.ability = [LIGHT_GRAY]能力: {0} +mech.buildspeed = [LIGHT_GRAY]建設速度: {0}% +liquid.heatcapacity = [LIGHT_GRAY]熱容量: {0} +liquid.viscosity = [LIGHT_GRAY]粘度: {0} +liquid.temperature = [LIGHT_GRAY]温度: {0} +block.grass.name = 草 +block.salt.name = 岩塩氷河 +block.saltrocks.name = 岩塩 +block.pebbles.name = 小石 +block.tendrils.name = つる +block.sandrocks.name = 砂岩 +block.spore-pine.name = 胞子の松の木 +block.sporerocks.name = 胞子の岩 +block.rock.name = 岩 +block.snowrock.name = 雪の積もった岩 +block.shale.name = 泥板岩 +block.shale-boulder.name = 泥板岩の丸石 +block.moss.name = コケ +block.shrubs.name = 低木 +block.spore-moss.name = 胞子のコケ +block.shalerocks.name = 泥板岩の岩 +block.scrap-wall.name = スクラップの壁 +block.scrap-wall-large.name = 大きなスクラップの壁 +block.scrap-wall-huge.name = とても大きなスクラップの壁 +block.scrap-wall-gigantic.name = 巨大なスクラップの壁 +block.thruster.name = スラスター +block.kiln.name = 溶解炉 +block.kiln.description = 砂と鉛を溶かしてメタガラスを生成します。少量の電力が必要です。 +block.graphite-press.name = 黒鉛圧縮機 +block.multi-press.name = マルチ圧縮機 +block.constructing = {0}\n[LIGHT_GRAY](建設中) +block.spawn.name = 敵の出現場所 +block.core-shard.name = コア: シャード +block.core-foundation.name = コア: ファンデーション +block.core-nucleus.name = コア: ニュークリアス +block.deepwater.name = 深層水 +block.water.name = 水 +block.tainted-water.name = 汚れた水 +block.darksand-tainted-water.name = 黒い砂で汚れた水 +block.tar.name = タール +block.stone.name = 石 +block.sand.name = 砂 +block.darksand.name = 黒い砂 +block.ice.name = 氷 +block.snow.name = 雪 +block.craters.name = クレーター +block.sand-water.name = 濁った水 +block.darksand-water.name = 黒い砂で濁った水 +block.char.name = 焦げ跡 +block.holostone.name = ホロストーン +block.ice-snow.name = 雪氷 +block.rocks.name = 岩 +block.icerocks.name = 氷岩 +block.snowrocks.name = 雪の積もった岩 +block.dunerocks.name = 砂丘の岩 +block.pine.name = 松の木 +block.white-tree-dead.name = 白い枯れた樹木 +block.white-tree.name = 白い樹木 +block.spore-cluster.name = 胞子の房 +block.metal-floor.name = 金属製の地面 +block.metal-floor-2.name = 金属製の地面 2 +block.metal-floor-3.name = 金属製の地面 3 +block.metal-floor-5.name = 金属製の地面 5 +block.metal-floor-damaged.name = 破壊された金属製の地面 +block.dark-panel-1.name = ダークパネル 1 +block.dark-panel-2.name = ダークパネル 2 +block.dark-panel-3.name = ダークパネル 3 +block.dark-panel-4.name = ダークパネル 4 +block.dark-panel-5.name = ダークパネル 5 +block.dark-panel-6.name = ダークパネル 6 +block.dark-metal.name = ダークメタル +block.ignarock.name = イグナロック +block.hotrock.name = ホットロック +block.magmarock.name = マグマロック +block.cliffs.name = 崖 +block.copper-wall.name = 銅の壁 +block.copper-wall-large.name = 巨大な銅の壁 +block.titanium-wall.name = チタンの壁 +block.titanium-wall-large.name = 巨大なチタンの壁 +block.phase-wall.name = フェーズファイバーの壁 +block.phase-wall-large.name = 巨大なフェーズファイバーの壁 +block.thorium-wall.name = トリウムの壁 +block.thorium-wall-large.name = 巨大なトリウムの壁 +block.door.name = ドア +block.door-large.name = 大型のドア +block.duo.name = デュオ +block.scorch.name = スコーチ +block.scatter.name = スキャッター +block.hail.name = ヘイル +block.lancer.name = ランサー +block.conveyor.name = コンベアー +block.titanium-conveyor.name = チタンコンベアー +block.junction.name = ジャンクション +block.router.name = ルーター +block.distributor.name = ディストリビューター +block.sorter.name = ソーター +block.sorter.description = アイテムを分別して搬出します。設定したアイテムは通過させます。他のアイテムが搬入されると側面にアイテムを搬出します。 +block.overflow-gate.name = オーバーフローゲート +block.overflow-gate.description = 搬出先にアイテムを搬入する空きがない場合に左右にアイテムを搬出します。 +block.silicon-smelter.name = シリコン溶鉱炉 +block.phase-weaver.name = フェーズ織機 +block.pulverizer.name = 粉砕機 +block.cryofluidmixer.name = 冷却ミキサー +block.melter.name = 融合機 +block.incinerator.name = 焼却炉 +block.spore-press.name = 胞子圧縮機 +block.separator.name = 分離機 +block.coal-centrifuge.name = 石炭遠心分離機 +block.power-node.name = 電源ノード +block.power-node-large.name = 大型電源ノード +block.surge-tower.name = サージタワー +block.battery.name = バッテリー +block.battery-large.name = 大型バッテリー +block.combustion-generator.name = 火力発電機 +block.turbine-generator.name = タービン発電機 +block.differential-generator.name = 差動発電機 +block.impact-reactor.name = インパクトリアクター +block.mechanical-drill.name = 機械ドリル +block.pneumatic-drill.name = 空気圧ドリル +block.laser-drill.name = レーザードリル +block.water-extractor.name = ウォーターポンプ +block.cultivator.name = 培養機 +block.dart-mech-pad.name = アルファ整備台 +block.delta-mech-pad.name = デルタ整備台 +block.javelin-ship-pad.name = ジャベリン整備台 +block.trident-ship-pad.name = トライデント整備台 +block.glaive-ship-pad.name = グライブ整備台 +block.omega-mech-pad.name = オメガ整備台 +block.tau-mech-pad.name = タウ整備台 +block.conduit.name = パイプ +block.mechanical-pump.name = 機械ポンプ +block.item-source.name = アイテムソース +block.item-void.name = アイテムボイド +block.liquid-source.name = 液体ソース +block.power-void.name = 電力ボイド +block.power-source.name = 無限電源 +block.unloader.name = 搬出機 +block.vault.name = ボールト +block.wave.name = ウェーブ +block.swarmer.name = スウォーマー +block.salvo.name = サルボー +block.ripple.name = リップル +block.phase-conveyor.name = フェーズコンベアー +block.bridge-conveyor.name = ブリッジコンベアー +block.plastanium-compressor.name = プラスタニウム圧縮機 +block.pyratite-mixer.name = ピラタイトミキサー +block.blast-mixer.name = 化合物ミキサー +block.solar-panel.name = ソーラーパネル +block.solar-panel-large.name = 大型ソーラーパネル +block.oil-extractor.name = 石油抽出機 +block.spirit-factory.name = スピリットドローン製造機 +block.phantom-factory.name = ファントムドローン製造機 +block.wraith-factory.name = レースファイター製造機 +block.ghoul-factory.name = グールボンバー製造機 +block.dagger-factory.name = ダガーユニット製造機 +block.crawler-factory.name = クローラー製造機 +block.titan-factory.name = タイタンユニット製造機 +block.fortress-factory.name = フォートレスユニット製造機 +block.revenant-factory.name = レベナントファイター製造機 +block.repair-point.name = 修復ポイント +block.pulse-conduit.name = パルスパイプ +block.phase-conduit.name = フェーズパイプ +block.liquid-router.name = 液体ルーター +block.liquid-tank.name = 液体タンク +block.liquid-junction.name = 液体ジャンクション +block.bridge-conduit.name = ブリッジパイプ +block.rotary-pump.name = ロータリーポンプ +block.thorium-reactor.name = トリウムリアクター +block.mass-driver.name = マスドライバー +block.blast-drill.name = エアブラストドリル +block.thermal-pump.name = サーマルポンプ +block.thermal-generator.name = サーマル発電機 +block.alloy-smelter.name = 合金溶鉱炉 +block.mender.name = 修復機 +block.mend-projector.name = 修復プロジェクター +block.surge-wall.name = サージの壁 +block.surge-wall-large.name = 大きなサージの壁 +block.cyclone.name = サイクロン +block.fuse.name = ヒューズ +block.shock-mine.name = 地雷 +block.overdrive-projector.name = 加速プロジェクター +block.force-projector.name = 力場プロジェクター +block.arc.name = アーク +block.rtg-generator.name = RTG発電機 +block.spectre.name = スペクター +block.meltdown.name = メルトダウン +block.container.name = コンテナー +block.launch-pad.name = 発射台 +block.launch-pad.description = コアを発射することなく、沢山のアイテムを発射します。まだ未完成だよ。 +block.launch-pad-large.name = 大型発射台 +team.blue.name = ブルー +team.red.name = レッド +team.orange.name = オレンジ +team.none.name = グレー +team.green.name = グリーン +team.purple.name = パープル +unit.spirit.name = スピリットドローン +unit.spirit.description = 手軽なドローンユニットです。最初からコアに出現します。鉱石の採掘やブロックの修理を自動で行います。 +unit.phantom.name = ファントムドローン +unit.phantom.description = 高度な小型ドローンユニットです。自動で鉱石の採掘やブロックの修理をします。スピリットドローンを遥かに凌ぐ性能を誇ります。 +unit.dagger.name = ダガー +unit.dagger.description = 基本的な地上ユニットです。集団になると便利に使えます。 +unit.crawler.name = クローラー +unit.titan.name = タイタン +unit.titan.description = 高度な武装地上ユニットです。空と地上の両方の敵に攻撃を行います。 +unit.ghoul.name = グール爆撃機 +unit.ghoul.description = 重爆撃機です。 +unit.wraith.name = レースファイター +unit.wraith.description = 高速で突撃攻撃が可能な迎撃ユニットです。 +unit.fortress.name = フォートレス +unit.fortress.description = 砲撃型の地上ユニットです。 +unit.revenant.name = レベナント +unit.eruptor.name = ユーロター +unit.chaos-array.name = ケアスアレー +unit.eradicator.name = エラディケーター +unit.lich.name = リッチ +unit.reaper.name = リーパー +tutorial.begin = ここでのミッションは[LIGHT_GRAY]敵[]を倒すことです。\n\nまずは、[accent]銅の採掘[]から始めましょう。コアの近くの銅鉱石の鉱脈をタップしましょう。 +tutorial.drill = 手動で採掘するのは非効率的です。\n[accent]ドリル[]を使えば自動で採掘できます。\n銅鉱石の鉱脈の上に1つ置いてみましょう。 +tutorial.conveyor = [accent]コンベアー[]はコアにアイテムを輸送することができます。\nドリルからコアまでコンベアー作りましょう。 +tutorial.morecopper = まだまだ銅が必要です。\n\n手動で採掘するか、ドリルをもっと設置しましょう。 +tutorial.turret = [LIGHT_GRAY]敵[]を撃退するために防衛体制を作らなければなりません。\n基地の近くにターレットのデュオを設置しましょう。 +tutorial.drillturret = デュオには弾として[accent]銅の弾薬[]が必要です。\nデュオの横にドリルを置き、採掘した銅を供給しましょう。 +tutorial.waves = [LIGHT_GRAY]敵[]が近づいてきます。\n\n2ウェーブの間、コアを守ってください。ターレットをもっと設置しましょう。 +tutorial.lead = 他の鉱石も使ってみましょう。[accent]鉛[]を見つけて、採掘しましょう。\n\nユニットからコアにドラッグして、資源を転送できます。 +tutorial.smelter = 銅を鉛はやわらかい金属です。\nより硬い[accent]高密度合金[]を溶鉱炉で作りましょう。 +tutorial.densealloy = 溶鉱炉で作った合金を回収しましょう。\n\nまた、必要に応じて効率化しましょう。 +tutorial.siliconsmelter = コアは、採掘やブロックの修復のために[accent]スピリットドローン[]を作成しています。\n\n他のユニットを作るには[accent]シリコン[]が必要です。\nシリコン溶鉱炉を作りましょう。 +tutorial.silicondrill = シリコンには[accent]石炭[]と[accent]砂[]が必要です。\nまず、ドリルを作りましょう。 +tutorial.generator = この技術には電力が必要です。\n[accent]火力発電機[]を作りましょう。 +tutorial.generatordrill = 火力発電機には燃料が必要です。\nドリルから石炭を補給しましょう。 +tutorial.node = 電力を送電する必要があります。\n火力発電機の隣に[accent]電源ノード[]を作って、電力を供給しましょう。 +tutorial.nodelink = 電力は隣接している電力ブロックや発電機、接続された状態の電源ノードを使って送電することができます。\n\nノードをタップしてから、発電機とシリコン溶鉱炉を選択して電力を繋げましょう。 +tutorial.silicon = シリコン溶鉱炉で製造したシリコンを回収しましょう。\n\nまた、効率化をすることをお勧めします。 +tutorial.daggerfactory = [accent]ダガーユニット製造機[]を作りましょう。\n\nこれを使うと攻撃ユニットを作ってくれます。 +tutorial.router = 生産機には電力が必要です。\nコンベアーから資源を分けるためにルーターを作りましょう。 +tutorial.dagger = 電源ノードを生産機に接続しましょう。\n要件が揃うと、ユニットを作り始めます。\n\n必要に応じて、ドリルや発電機、コンベアーを増やしましょう。 +tutorial.battle = [LIGHT_GRAY]敵[]のコアが見つかりました。\nユニットやダガー機で敵の基地を破壊しましょう。 +block.copper-wall.description = 安価な防壁ブロックです。\n最初のウェーブでコアやターレットを保護するのに有用です。 +block.copper-wall-large.description = 安価な大型防壁ブロックです。\n最初のウェーブでコアやターレットを保護するのに有用です。 +block.thorium-wall.description = 強化された防壁ブロックです。\n敵からの保護により強固です。 +block.thorium-wall-large.description = 強化された大型防壁ブロックです。\n敵からの保護により強固です。 +block.phase-wall.description = トリウムの壁ほど強固ではないが、強力な弾でなければ弾き返すことができます。 +block.phase-wall-large.description = トリウムの壁ほど強固ではないが、強力な弾でなければ弾き返すことができます。 +block.surge-wall.description = 最も硬い防壁ブロックです。\nたまに攻撃されると敵に電撃を与えます。 +block.surge-wall-large.description = 最も硬い大型防壁ブロックです。\nたまに攻撃されると敵に電撃を与えます。 +block.door.description = 小さなドアブロックです。タップすることで開閉することができます。\nただし、ドアが開いている場合、弾や敵も通過できます。 +block.door-large.description = 大型のドアブロックです。タップすることで開閉することができます。\nただし、ドアが開いている場合、弾や敵も通過できます。 +block.mend-projector.description = 定期的に周辺のブロックを修復します。 +block.overdrive-projector.description = ドリルやコンベアなど、近くの施設の効率を向上させます。 +block.force-projector.description = 周囲に六角形の力場を作り出し、内部の建造物やユニットなどを守ります。 +block.shock-mine.description = 踏んだ敵にダメージを与えます。敵に見えることはありません。 +block.duo.description = 小さく安価なターレットです。 +block.scatter.description = 中規模の対空型ターレットです。敵に鉛またはスクラップの塊を分散するように発射させます。 +block.arc.description = 小型の電撃型ターレットです。敵に向かってランダムな半円状に電撃を放ちます。 +block.hail.description = 小型の砲撃型ターレットです。 +block.lancer.description = チャージビームを放つ中型のターレットです。 +block.wave.description = バブルの連射攻撃をする中型のターレットです。 +block.salvo.description = 一斉に攻撃を行う中型のターレットです。 +block.swarmer.description = バーストミサイルで攻撃する中型ターレットです。 +block.ripple.description = 同時に複数ショットを発射する大型ターレットです。 +block.cyclone.description = 大型の連射型ターレットです。 +block.fuse.description = 短距離攻撃が得意な大型のターレットです。 +block.spectre.description = 一度に2発の強力な弾を放つ大型のターレットです。 +block.meltdown.description = 強力な長距離攻撃が可能な大型のターレットです。 +block.conveyor.description = 一般的なアイテム輸送ブロックです。アイテムを前方に移動し、自動的にターレットや機械などに搬入します。回転させることができます。 +block.titanium-conveyor.description = 改良されたアイテム輸送ブロックです。通常のコンベアーよりも速くアイテムを輸送します。 +block.phase-conveyor.description = 改良されたアイテム転送ブロックです。電力を使用して、離れた場所にあるフェーズコンベアーにアイテムを転送することができます。 +block.junction.description = 十字に交差したコンベアーをそれぞれ前方に搬出します。コンベアーで複雑な構造を組み立てるときに便利です。 +block.mass-driver.description = 長距離の輸送が可能な上位アイテム輸送ブロックです。離れた別のマスドライバーにアイテムを発射します。 +block.silicon-smelter.description = 石炭と砂からシリコンを製造します。 +block.plastanium-compressor.description = オイルとチタンからプラスタニウムを製造します。 +block.phase-weaver.description = 放射性トリウムと多量の砂からフェーズファイバーを製造します。 +block.alloy-smelter.description = チタンや鉛、シリコン、銅からサージ合金を製造します。 +block.pulverizer.description = 石を砕いて砂にします。自然の砂がない場合に有用です。 +block.pyratite-mixer.description = 石炭、鉛、砂から燃えやすいピラタイトを製造します。 +block.blast-mixer.description = 可燃性のピラタイトを石油を使用してさらに爆発性化合物にします。 +block.cryofluidmixer.description = 水とチタンから冷却に効率的な冷却水を製造します。 +block.melter.description = 石を熱で溶かして溶岩を生成します。 +block.incinerator.description = 不要なアイテムや液体を焼却します。 +block.spore-press.description = 胞子ポッドを石油に圧縮します。 +block.separator.description = 石を水圧で砕き、石に含まれる様々な鉱石を回収します。 +block.power-node.description = 電力ノード間で電力の送電を行います。最大で4つの電力源やノードなどに接続できます。隣接するブロックから電力の送電や供給を行います。 +block.power-node-large.description = 巨大な電力ノードです。最大で6つの電力源やノードに接続できます。 +block.battery.description = 余分な電力の充電して、貯めておくことができます。必要があれば、溜まった電力を供給します。 +block.battery-large.description = 通常のバッテリーよりもたくさんの電力を溜めておくことができます。 +block.combustion-generator.description = 石油や可燃性の物質を燃やして発電します。 +block.turbine-generator.description = 水を使って火力発電機より効率的に発電します。 +block.thermal-generator.description = 溶岩から大量の電力を発電します。 +block.solar-panel.description = 太陽光から少量の電力を発電します。 +block.solar-panel-large.description = 高価な建設費と引き換えに、通常のソーラーパネルより大量の電力を発電することができます。 +block.thorium-reactor.description = 高い放射性を持ったトリウムから大量の電力を発電します。これには一定の冷却が必要です。冷却が不十分な場合、大きな爆発が発生します。 +block.rtg-generator.description = トリウムリアクターよりも発電量は少ないですが、冷却を必要としない放射性同位体熱発電機(RTG)です。 +block.unloader.description = コンテナやボールト、コアからアイテムをコンベアーか隣接するブロックに搬出します。搬出機をタップして搬出するアイテムを変更することができます。 +block.container.description = 各種類のアイテムを少量ずつ保管します。隣接するコンテナーやボール卜、コアは一つのストレージユニットとして扱われます。 [LIGHT_GRAY]搬出機[]を使って、コンテナーからアイテムを搬出できます。 +block.vault.description = 各種類のアイテムを大量に保管します。隣接するコンテナーやボール卜、コアは一つのストレージユニットとして扱われます。[LIGHT_GRAY]搬出機[]を使って、ボールトからアイテムを搬出できます。 +block.mechanical-drill.description = 安価なドリルです。採掘可能な鉱脈に設置すると、アイテムを採掘して搬出します。 +block.pneumatic-drill.description = より速く採掘できるように改良されたドリルです。また、より硬い鉱石も採掘することができます。 +block.laser-drill.description = 電力を使用したレーザー技術でより速く採掘することができます。また、放射性のトリウムを回収することができます。 +block.blast-drill.description = 上位のドリルです。大量の電力が必要になります。 +block.water-extractor.description = 地面から水を汲み上げます。近くに湖がない場合に有用です。 +block.cultivator.description = 胞子の小さな集まりを工業用ポッドに培養します。 +block.oil-extractor.description = 大量の電力を使用して、砂から石油を回収します。近くに油田がない場合に有用です。 +block.trident-ship-pad.description = 機体を重装備の爆撃機に乗り換えます。\n整備台に乗ってダブルタップすることで使用することができます。 +block.javelin-ship-pad.description = 機体を高速で強力な電撃砲を搭載した迎撃機に乗り換えます。\n整備台に乗ってダブルタップすることで使用することができます。 +block.glaive-ship-pad.description = 機体を重装備の大型攻撃機に乗り換えます。\n整備台に乗ってダブルタップすることで使用することができます。 +block.tau-mech-pad.description = 機体を味方の建造物やユニットの修復が可能な支援型機体に乗り換えます。\n整備台に乗ってダブルタップすることで使用することができます。 +block.delta-mech-pad.description = 機体を高速で突撃攻撃に向いた軽装備の機体に乗り換えます。\n整備台に乗ってダブルタップすることで使用することができます。 +block.omega-mech-pad.description = 機体を最前線での戦闘に向いた重装備の機体に乗り換えます。\n整備台に乗ってダブルタップすることで使用することができます。 +block.spirit-factory.description = 鉱石の採掘やブロックの修復を行う小型のドローンユニットのスピリットを製造します。 +block.phantom-factory.description = スピリットドローンの性能を遥かに凌ぐ上位のドローンユニットのファントムドローンを製造します。 +block.wraith-factory.description = 高速で突撃攻撃が可能な迎撃ユニットのレースファイターを製造します。 +block.ghoul-factory.description = 大型爆撃機のグールを製造します。 +block.dagger-factory.description = 基本的な地上ユニットのダガーを製造します。 +block.titan-factory.description = 高度な武装地上ユニットのタイタンを製造します。 +block.fortress-factory.description = 大型の砲撃地上ユニットのフォートレスを製造します。 +block.revenant-factory.description = 大型のレーザー航空ユニットのレベナントを製造します。 +block.repair-point.description = 近くの負傷したユニットを修復します。 +block.conduit.description = 一般的な液体輸送ブロックです。液体版のコンベアーです。ポンプや他のパイプに使うことができます。 +block.pulse-conduit.description = 高度な液体輸送ブロックです。通常のパイプより速く、たくさんのアイテムを輸送することができます。 +block.phase-conduit.description = 高度な液体輸送ブロックです。電力を使用して、液体を他の離れたフェーズパイプに転送することができます。 +block.liquid-router.description = 搬入したアイテムをほかの3方向に均等に搬出します。液体の漏れを防ぐことができます。一つの資源から複数に分ける際などに使われます。 +block.liquid-tank.description = 大量の液体を保管しておくことができます。需要が不安定な製造設備や重要な施設の冷却水の予備などとして使用されます。 +block.liquid-junction.description = パイプを他のパイプと交差できるようにします。それぞれ搬入した液体を前方に搬出します。パイプで複雑な構造を組み立てるときなどに使われます。 +block.bridge-conduit.description = 高度な液体輸送ブロックです。地形や建物を超えて、3ブロック離れた場所に液体を輸送することができます。 +block.mechanical-pump.description = 安価なポンプです。搬出速度は遅いですが、電力を使わず使用できます。 +block.rotary-pump.description = 高度なポンプです。電力を使用して2倍速く搬出することができます。 +block.thermal-pump.description = 最高性能のポンプです。 +block.router.description = 搬入したアイテムをほかの3方向に均等に搬出します。一つの資源から複数に分ける際などに使われます。 +block.distributor.description = 高度なルーターです。搬入したアイテムをほかの7方向に均等に分けて搬出します。 +block.bridge-conveyor.description = 高度な輸送ブロックです。地形や建物を超えて、3ブロック離れた場所にアイテムを輸送することができます。 +block.item-source.description = アイテムを無限に搬出します。サンドボックスのみ。 +block.liquid-source.description = 液体を無限に搬出します。サンドボックスのみ。 +block.item-void.description = 電力を必要とせずにアイテムを廃棄します。サンドボックスのみ。 +block.power-source.description = 無限に電力を出力します。サンドボックスのみ。 +block.power-void.description = 入力されたすべての電力を破棄します。サンドボックスのみ。 +liquid.water.description = 機械の冷却や廃棄物の処理など幅広く使われている液体です。 +liquid.oil.description = 燃焼させたり、爆発させたり、冷却水としても使用される液体です。 +liquid.cryofluid.description = 冷却に特化した液体です。 diff --git a/core/assets/bundles/bundle_ko.properties b/core/assets/bundles/bundle_ko.properties index 5a49f2557a..636749902c 100644 --- a/core/assets/bundles/bundle_ko.properties +++ b/core/assets/bundles/bundle_ko.properties @@ -1,553 +1,947 @@ -text.about = 만든이 : [ROYAL]Anuken[] - [SKY]anukendev@gmail.com[]\n이 게임은 [orange]GDL[] Metal Monstrosity Jam 을 사용했습니다.\n\n크레딧\n- [YELLOW]bfxr[] 가 SFX 를 만듬\n- [GREEN]Roccow[] 가 음악을 만듬\n\n특별히 감사한 분들\n- [coral]MitchellFJN[]: 테스트하고 피드백을 주신 분\n- [sky]Luxray5474[]: wiki 를 만들고 코드에 기여하신 분\n- [lime]Epowerj[]: 코드를 만들고 아이콘을 제작하신 분\n- itch.io 그리고 Google Play 에서의 모든 베타 테스터 분들\n -text.credits = 크레딧 -text.discord = Mindustry 디스코드에 참여하세요! -text.changes = [SCARLET]주의!\n[]몇몇 중요한 게임 메커니즘이 변경되었습니다.\n\n- [accent]텔레포터[]는 이제 전력을 사용합니다.\n- [accent]제련소[]와 [accent]도가니[] 는 이제 최대 자원저장 공간을 가집니다.\n- [accent]도가니[] 는 이제 석탄 연료를 필요로 합니다. -text.link.discord.description = 공식 Mindustry 디스코드 채팅방 -text.link.github.description = 게임 소스코드 -text.link.dev-builds.description = 개발중인 빌드 (불안정) -text.link.trello.description = 공식 trello 보드에서 현재 계획중인 기능을 찾을 수 있습니다. -text.link.itch.io.description = itch.io 사이트에서 PC 버전 다운로드 또는 웹 버전을 플레이 할 수 있습니다. -text.link.google-play.description = Google Play 스토어 목록 -text.link.wiki.description = 공식 Mindustry 위키 -text.linkfail = 링크를 열지 못했습니다!\nURL이 클립보드에 복사되었습니다. -text.editor.web = 웹버전은 맵 편집기를 지원하지 않습니다!\n게임을 다운로드 한 후에 사용하세요. -text.multiplayer.web = 이 버전의 게임은 멀티플레이를 지원하지 않습니다!\n멀티플레이를 브라우저에서 하고 싶다면 이 itch.io 페이지에서 \"Multiplayer web version\" 버튼을 눌러서 플레이 해 주세요. -text.gameover = 코어가 파괴되었습니다. -text.highscore = [YELLOW]최고 점수 달성! -text.lasted = 이번 맵에서 달성한 마지막 웨이브 : -text.level.highscore = 최고 점수: [accent]{0} -text.level.delete.title = 맵 삭제 확인 -text.level.delete = 정말로 이 \"[orange]{0}\" 맵을 삭제하시겠습니까? -text.level.select = 맵 선택 -text.level.mode = 게임 모드: -text.savegame = 저장하기 -text.loadgame = 불러오기 -text.joingame = 멀티플레이 -text.newgame = 새 게임 -text.quit = 나가기 -text.about.button = 소개 -text.name = 이름: -text.public = 공개 -text.players = {0}명 온라인 -text.server.player.host = {0} 이 호스트함. -text.players.single = {0}명 온라인. -text.server.mismatch = 패킷오류 : 현재 게임 버전과 서버 버전이 일치하지 않습니다.\n현재 게임 버전이 최신버전인지 확인 해 주세요! -text.server.closing = [accent]서버 닫는중... -text.server.kicked.kick = 당신은 서버에서 강제 퇴장 되었습니다. -text.server.kicked.invalidPassword = 잘못된 비밀번호 입니다! -text.server.kicked.clientOutdated = 현재 플레이중인 게임 버전이 낮습니다!\n게임을 업데이트 해 주세요. -text.server.kicked.serverOutdated = 이 서버는 현재 클라이언트보다 낮은 버전의 서버입니다!\n서버장에게 업데이트를 요청하세요! -text.server.kicked.banned = 당신은 이 서버에서 차단되었습니다. -text.server.kicked.recentKick = 최근에 강제 퇴장되었습니다.\n잠시 후에 다시 입장 해 주세요. -text.server.connected = {0} 님이 서버에 입장했습니다. -text.server.disconnected = {0} 님이 서버에서 나갔습니다. -text.nohost = 커스텀 맵은 서버호스팅이 불가능합니다! -text.host.info = [accent]호스트[] 버튼은 현재 네트워크의 [scarlet]6567[] 과 [scarlet]6568[] 포트를 사용합니다.\n[LIGHY_GRAY]같은 Wi-Fi 또는 로컬 네트워크[] 에서 서버 목록을 볼 수 있습니다.\n\n만약 플레이어들이 이 IP를 통해 어디에서나 연결할 수 있게 하고 싶다면, 공유기 설정에서 [accent]포트 포워딩[]을 해야 합니다.\n\n[LIGHT_GRAY]참고 : LAN 게임 연결에 문제가 있는 사람이 있다면, 방화벽 설정에서 Mindustry 가 로컬 네트워크에 액세스하도록 허용했는지 확인 해 주세요. -text.join.info = 여기서 [accent]서버 IP[]를 입력하여 다른 서버에 접속할 수 있습니다.\n또는 [accent]로컬 네트워크(LAN)[] 서버를 검색하여 접속할 수 있습니다.\nLAN 및 WAN 멀티 플레이어 모두 지원됩니다.\n\n[LIGHT_GRAY]참고 : 여기에서는 자동으로 글로벌 서버를 추가하지 않습니다. IP로 다른 사람의 서버에 접속할려면 서버장에게 IP를 요청해야 합니다. -text.hostserver = 서버 열기 -text.host = 호스트 -text.hosting = [accent]서버여는중... -text.hosts.refresh = 새로고침 -text.hosts.discovering = LAN 게임 찾기 -text.server.refreshing = 서버 새로고침 -text.hosts.none = [lightgray]LAN 게임이 없습니다! -text.host.invalid = [scarlet]호스트에 연결할 수 없습니다! -text.server.friendlyfire = 팀킬 허용 -text.trace = 플레이어 추적 -text.trace.playername = 플레이어 이름 : [accent]{0} -text.trace.ip = IP : [accent]{0} -text.trace.id = 고유 ID : [accent]{0} -text.trace.android = Android 클라이언트 : [accent]{0} -text.trace.modclient = 수정된 클라이언트 : [accent]{0} -text.trace.totalblocksbroken = 총 파괴한 블록 수 : [accent]{0} -text.trace.structureblocksbroken = 총 구조 블럭 파괴수 : [accent]{0} -text.trace.lastblockbroken = 마지막으로 파괴한 블록 : [accent]{0} -text.trace.totalblocksplaced = 총 설치한 블록 수 : [accent]{0} -text.trace.lastblockplaced = 마지막으로 설치한 블록 : [accent]{0} -text.invalidid = 잘못된 클라이언트 ID 입니다! 공식 Mindustry 으로 버그 보고서를 제출 해 주세요. -text.server.bans = 차단된 유저들 -text.server.bans.none = 차단된 플레이어가 없습니다. -text.server.admins = 관리자 -text.server.admins.none = 관리자가 없습니다. -text.server.add = 서버 추가 -text.server.delete = 이 서버를 삭제 하시겠습니까? -text.server.hostname = 호스트: -text.server.edit = 서버 수정 -text.server.outdated = [crimson]서버 버전이 낮습니다![] -text.server.outdated.client = [Crimson]클라이언트 버전이 낮습니다![] -text.server.version = [lightgray] 버전 : {0} -text.server.custombuild = [노란색]수정된 빌드 -text.confirmban = 이 플레이어를 차단하시겠습니까? -text.confirmunban = 이 플레이어를 차단 해제 하시겠습니까? -text.confirmadmin = 이 플레이어를 관리자로 설정 하시겠습니까? -text.confirmunadmin = 이 플레이어에서 관리자 상태를 삭제 하시겠습니까? -text.joingame.byip = IP로 참가하기... -text.joingame.title = 게임 참가 -text.joingame.ip = IP: -text.disconnect = 서버와의 연결이 해제되었습니다. -text.disconnect.data = 맵 데이터를 불러오지 못했습니다! -text.connecting = [accent]연결중... -text.connecting.data = [accent]맵 데이터 로딩중... -text.connectfail = [crimson][orange]{0}[] 서버에 연결하지 못했습니다.[orange] -text.server.port = 포트: -text.server.addressinuse = 이 주소는 이미 사용중입니다! -text.server.invalidport = 포트 번호가 잘못되었습니다. -text.server.error = [crimson][orange]{0}[] 서버를 호스팅 하는데 오류가 발생했습니다. -text.tutorial.back = < 이전 -text.tutorial.next = 다음 > -text.save.new = 새로 저장 -text.save.overwrite = 이 저장 슬롯을 덮어씌우겠습니까? -text.overwrite = 덮어쓰기 -text.save.none = 저장 파일을 찾지 못했습니다! -text.saveload = [accent]저장중... -text.savefail = 게임을 저장하지 못했습니다! -text.save.delete.confirm = 이 저장파일을 삭제 하시겠습니까? -text.save.delete = 삭제 -text.save.export = 저장파일 내보내기 -text.save.import.invalid = [orange]저장파일이 유효하지 않습니다! -text.save.import.fail = [crimson]저장파일을 불러오지 못함: {0}[orange] -text.save.export.fail = [crimson]저장파일을 내보내지 못함: {0}[orange] -text.save.import = 저장파일 불러오기 -text.save.newslot = 저장 파일이름 : -text.save.rename = 이름 변경 -text.save.rename.text = 새 이름 : -text.selectslot = 저장슬롯을 선택하십시오. -text.slot = [accent]{0}번째 슬롯 -text.save.corrupted = [orange]저장파일이 손상되었습니다! -text.empty = <비어있음> -text.on = 켜기 -text.off = 끄기 -text.save.autosave = 자동저장: {0} -text.save.map = 맵: {0} -text.save.wave = {0} 단계 -text.save.difficulty = 난이도 : {0} -text.save.date = 마지막 저장 날짜 : {0} -text.confirm = 확인 -text.delete = 삭제 -text.ok = 확인 -text.open = 열기 -text.cancel = 취소 -text.openlink = 링크 열기 -text.copylink = 링크 복사 -text.back = 뒤로 -text.quit.confirm = 종료 하시겠습니까? -text.changelog.title = 변경사항 -text.changelog.loading = 업데이트 내역 가져오는중.. -text.changelog.error.android = [orange]업데이트 내역은 가끔 Android 4.4 이하에서 작동하지 않습니다.\n이것은 Android 내부 버그입니다. -text.changelog.error.ios = [orange]현재 업데이트 내역은 iOS 에서 지원하지 않습니다. -text.changelog.error = [searlet]업데이트 내역을 가져오는데 오류가 발생했습니다!\n인터넷 연결을 확인 해 주세요. -text.changelog.current = [yellow][[현재 버전] -text.changelog.latest = [orange][[최신 버전] -text.loading = [accent]로딩중 ... -text.wave = [orange]{0} 단계 -text.wave.waiting = 다음 단계까지 {0} 초 남음 -text.waiting = 대기 중... -text.enemies = 남은 잡몹 수 : {0} -text.enemies.single = {0} 마리 남음 -text.loadimage = 사진 불러오기 -text.saveimage = 사진 저장 -text.oregen = 광물 생성 -text.editor.badsize = [orange]사진 크기가 잘못되었습니다![]\n유효한 맵 크기 : {0} -text.editor.errorimageload = [orange]{0} 사진을 불러오는데 오류가 발생했습니다. -text.editor.errorimagesave = [orange]{0} 사진을 저장하는데 오류가 발생했습니다. -text.editor.generate = 생성 -text.editor.resize = 크기 조정 -text.editor.loadmap = 맵 불러오기 -text.editor.savemap = 맵 저장 -text.editor.loadimage = 사진 불러오기 -text.editor.saveimage = 사진 저장 -text.editor.unsaved = [scarlet]변경사항을 저장하지 않았습니다![]\n종료하시겠습니까? -text.editor.brushsize = 브러쉬 크기 : {0} -text.editor.noplayerspawn = 이맵에는 플레이어의 스폰 지점이 없습니다! -text.editor.manyplayerspawns = 맵에는 플레이어 스폰 지점이 둘 이상 있을 수 없습니다! -text.editor.manyenemyspawns = 잡몹 스폰지점을 {0}개 이상으로 지정할 수 없습니다! -text.editor.resizemap = 맵 크기 조정 -text.editor.resizebig = [scarlet]경고!\n[]맵 크기가 256이상일경우 랙이 걸리거나 게임이 불안정할 수 있습니다. -text.editor.mapname = 맵 이름 : -text.editor.overwrite = [accent]경고!\n이 작업은 기존 맵을 덮어 쓰게 됩니다! -text.editor.failoverwrite = [crimson]기본맵을 덮어 쓸수 없습니다! -text.editor.selectmap = 불러올 맵 선택 : -text.width = 넓이 : -text.height = 높이: -text.randomize = 무작위 -text.apply = 적용 -text.update = 업데이트 -text.menu = 메뉴 -text.play = 플레이 -text.load = 불러오기 -text.save = 저장 -text.language.restart = 언어 설정을 적용하려면 게임을 다시 시작하십시오. -text.settings.language = 언어 -text.settings = 설정 -text.tutorial = 자습서 -text.editor = 에디터 -text.mapeditor = 맵 편집기 -text.donate = 기부하기 -text.settings.reset = 기본값으로 재설정 -text.settings.controls = 컨트롤 -text.settings.game = 게임 설정 -text.settings.sound = 소리 설정 -text.settings.graphics = 그래픽 설정 -text.upgrades = 업그레이드 -text.purchased = [LIME]제작됨! -text.weapons = 무기 -text.paused = 일시중지 -text.respawn = 남은 부활 시간 : -text.info.title = [accent]정보 -text.error.title = [crimson]예기지 않은 오류가 발생했습니다! -text.error.crashmessage = [SCARLET]예기치 못한 오류가 발생하여, 무언가 충돌을 일으켰습니다\n[]이 오류에 대한 정확한 내용을 개발자에게 전달해 주세요!\n[ORANGE]anukendev@gmail.com[] 또는 [orange]Mindustry 디스코드[orange]로 보내주세요! -text.error.crashtitle = 오류 발생! -text.mode.break = 삭제 모드 : {0} -text.mode.place = 설치 모드 : {0} -placemode.hold.name = 라인 -placemode.areadelete.name = 구역 삭제 -placemode.touchdelete.name = 클릭하여 삭제 -placemode.holddelete.name = 길게 눌러 삭제 -placemode.none.name = 없음 -placemode.touch.name = 클릭 -placemode.cursor.name = 커서 -text.blocks.extrainfo = [accent]추가 블록 정보: -text.blocks.blockinfo = 블록 정보 -text.blocks.powercapacity = 전력 저장량 -text.blocks.powershot = 초당 전력량 -text.blocks.powersecond = 초당 발전량 -text.blocks.powerdraindamage = 데미지당 에너지 소모량 -text.blocks.shieldradius = 보호막 범위 -text.blocks.itemspeedsecond = 초당 운반량 -text.blocks.range = 범위 -text.blocks.size = 크기 -text.blocks.powerliquid = 액체량당 전력 -text.blocks.maxliquidsecond = 초당 최대 액체 운반량 -text.blocks.liquidcapacity = 액체 저장량 -text.blocks.liquidsecond = 초당 액체 운반량 -text.blocks.damageshot = 1발당 데미지 -text.blocks.ammocapacity = 최대 탄약 저장량 -text.blocks.ammo = 탄약 -text.blocks.ammoitem = 아이템당 탄약 -text.blocks.maxitemssecond = 초당 최대 아이템수 -text.blocks.powerrange = 전력 출력범위 -text.blocks.lasertilerange = 전력 레이저 범위 -text.blocks.capacity = 용량 -text.blocks.itemcapacity = 아이템 저장용량 -text.blocks.maxpowergenerationsecond = 초당 최대 발전량 -text.blocks.powergenerationsecond = 초당 전력 생산량 -text.blocks.generationsecondsitem = 초당 생산량 / 아이템 -text.blocks.input = 입력 -text.blocks.inputliquid = 가능한 액체 -text.blocks.inputitem = 가능한 아이템 -text.blocks.output = 출력 -text.blocks.secondsitem = 초당 아이템수 -text.blocks.maxpowertransfersecond = 초당 최대 전력량 -text.blocks.explosive = 이게 파괴되면 매우 큰 폭발을 일으켜서 근처 블록에게 피해를 입힙니다! -text.blocks.repairssecond = 초당 수리량 -text.blocks.health = 체력 -text.blocks.inaccuracy = 명중하지 않을 확률 -text.blocks.shots = 발 -text.blocks.shotssecond = 초당 발사횟수 -text.blocks.fuel = 연료 -text.blocks.fuelduration = 연료 지속 시간 -text.blocks.maxoutputsecond = 초당 최대 출력 -text.blocks.inputcapacity = 최대 입력량 -text.blocks.outputcapacity = 최대 출력량 -text.blocks.poweritem = 아이템당 전력량 -text.placemode = 설치 모드 -text.breakmode = 삭제 모드 -text.health = 체력 +credits.text = Created by [ROYAL]Anuken[] - [SKY]anukendev@gmail.com[] +credits = 제작자 +contributors = 번역 및 기여자들 +discord = Mindustry Discord 에 참여 해 보세요! +link.discord.description = 공식 Mindustry Discord 채팅방 +link.github.description = 게임 소스코드 +link.dev-builds.description = 불안정한 개발 빌드들 +link.trello.description = 다음 출시될 기능들을 게시판 공식 Trello 보드 +link.itch.io.description = PC 버전 다운로드와 HTML5 버전이 있는 itch.io 사이트 +link.google-play.description = Google Play 스토어 정보 +link.wiki.description = 공식 Mindustry 위키 +linkfail = 링크를 여는데 실패했습니다!\nURL이 기기의 클립보드에 복사되었습니다. +screenshot = 화면 캡쳐가 {0} 경로에 저장되었습니다. +screenshot.invalid = 맵이 너무 커서 스크린샷을 찍을 메모리가 충분하지 않습니다. +gameover = 게임 오버 +gameover.pvp = [accent]{0}[] 팀이 승리했습니다! +highscore = [accent]최고점수 달성! +stat.wave = 웨이브 성공:[accent]{0} +stat.enemiesDestroyed = 파괴한 적 수:[accent]{0} +stat.built = 건설한 건물 수:[accent]{0} +stat.destroyed = 파괴된 건물 수:[accent]{0} +stat.deconstructed = 파괴한 건물 수:[accent]{0} +stat.delivered = 획득한 자원: +stat.rank = 최종 기록: [accent]{0} +placeline = 블록을 선택하셨습니다.\n][accent]몇초간 설치 시작지점을 누르고[] 원하는 방향을 향해 드래그 하면 [accent]일렬로[] 설치할 수 있습니다.\n한번 해 보세요. +removearea = 블록 제거모드를 선택하셨습니다.\n[accent]몇초간 제거 시작지점을 누르고[] 원하는 구역 끝을 향해 드래그 하면 [accent]직사각형[] 안에 있는 모든 건물을 제거할 수 있습니다.\n한번 해 보세요. +launcheditems = [accent]출격 아이템 +map.delete = 정말로 "[accent]{0}[]" 맵을 삭제하시겠습니까?\n +level.highscore = 최고 점수: [accent]{0} +level.select = 맵 선택 +level.mode = 게임 모드 : +showagain = 다음 세션에서 이 메세지를 표시하지 않습니다 +coreattack = < 코어가 공격받고 있습니다! > +nearpoint = [[ [scarlet]드롭 지점에서 나가세요[] ]\n적 스폰시 건물 및 유닛 파괴 +outofbounds = [[ 출입 금지 구역 ]\n[]{0}초후 유닛이 파괴됩니다. +database = 코어 데이터베이스 +savegame = 게임 저장 +loadgame = 게임 불러오기 +joingame = 멀티플레이 +addplayers = 플레이어 추가/제거 +customgame = 커스텀 게임 +newgame = 새 게임 +none = <없음> +minimap = 미니맵 +close = 닫기 +quit = 나가기 +maps = 맵 +continue = 계속하기 +maps.none = [LIGHT_GRAY]맵을 찾을 수 없습니다! +about.button = 정보 +name = 이름 : +noname = 먼저 [accent] 플레이어 이름[] 을 설정하세요. +filename = 파일 이름 : +unlocked = 새 블록 잠금 해제됨 +completed = [accent]연구됨 +techtree = 기술 트리 +research.list = [LIGHT_GRAY]연구: +research = 연구 +researched = [LIGHT_GRAY]{0}연구됨. +players = 현재 {0}명 접속중 +players.single = 현재 {0}명만 있음. +server.closing = [accent]서버 닫는중... +server.kicked.kick = 서버에서 추방되었습니다! +server.kicked.serverClose = 서버 종료됨. +server.kicked.clientOutdated = 오래된 버전의 클라이언트 입니다! 게임을 업데이트 하세요! +server.kicked.serverOutdated = 오래된 버전의 서버입니다! 서버 호스트 관리자에게 문의하세요! +server.kicked.banned = 서버 규칙 위반으로 인해, 이제 당신은 영원히 이 서버를 플레이 하실 수 없습니다. +server.kicked.recentKick = 방금 추방처리 되었습니다.\n잠시 기다린 후에 접속 해 주세요. +server.kicked.nameInUse = 이 닉네임이 이미 서버에서 사용중입니다. +server.kicked.nameEmpty = 닉네임에는 반드시 영어 또는 숫자가 있어야 합니다. +server.kicked.idInUse = 이미 서버에 접속중입니다! 다중 계정은 허용되지 않습니다. +server.kicked.customClient = 이 서버는 직접 빌드한 버전을 지원하지 않습니다. 공식 버전을 사용하세요. +server.kicked.gameover = 게임 오버! +host.info = [accent]호스트[] 버튼은 현재 네트워크의 [scarlet]6567[] 포트를 사용합니다.\n[LIGHT_GRAY]같은 Wi-Fi 또는 로컬 네트워크[] 에서 서버 목록을 볼 수 있습니다.\n\n만약 플레이어들이 이 IP를 통해 어디에서나 연결할 수 있게 하고 싶다면, 공유기 설정에서 [accent]포트 포워딩[]을 해야 합니다.\n\n[LIGHT_GRAY]참고: LAN 게임 연결에 문제가 있는 사람이 있다면, 방화벽 설정에서 Mindustry 가 로컬 네트워크에 액세스하도록 허용했는지 확인 해 주세요. +join.info = 여기서 [accent]서버 IP[]를 입력하여 다른 서버에 접속할 수 있습니다.\n또는 [accent]로컬 네트워크(LAN)[] 서버를 검색하여 접속할 수 있습니다.\nLAN 및 WAN 멀티 플레이어 모두 지원됩니다.\n\n[LIGHT_GRAY]참고:여기에서는 자동으로 글로벌 서버를 추가하지 않습니다. IP로 다른 사람의 서버에 접속할려면 서버장에게 IP를 요청해야 합니다. +hostserver = 서버 열기 +hostserver.mobile = 서버\n열기 +host = 서버 열기 +hosting = [accent]서버 여는중... +hosts.refresh = 새로고침 +hosts.discovering = LAN 게임 찾기 +server.refreshing = 서버 목록 새로고치는중... +hosts.none = [lightgray]LAN 게임을 찾을 수 없습니다! +host.invalid = [scarlet]서버에 연결할 수 없습니다! +trace = 플레이어 정보 보기 +trace.playername = 이름: [accent]{0} +trace.ip = IP: [accent]{0}{0} +trace.id = 고유 ID: [accent]{0} +trace.mobile = 모바일 클라이언트: [accent]{0} +trace.modclient = 수정된 클라이언트: [accent]{0} +invalidid = 잘못된 클라이언트 ID 입니다! 버그 보고서를 제출 해 주세요. +server.bans = 차단된 유저 +server.bans.none = 차단된 플레이어가 없습니다. +server.admins = 관리자 +server.admins.none = 관리자가 없습니다! +server.add = 서버 추가 +server.delete = 이 서버를 삭제 하시겠습니까? +server.hostname = 호스트: {0} +server.edit = 서버 수정 +server.outdated = [crimson]서버 버전이 낮습니다![] +server.outdated.client = [crimson]클라이언트 버전이 낮습니다![] +server.version = [lightgray]서버 버전: {0} {1} +server.custombuild = [yellow]커스텀 서버 +confirmban = 이 플레이어를 차단하시겠습니까? +confirmkick = 정말로 이 플레이어를 추방시키겠습니까? +confirmunban = 이 플레이어를 차단해제 하시겠습니까? +confirmadmin = 이 플레이어를 관리자로 만들겠습니까? +confirmunadmin = 이 플레이어를 일반 유저로 만들겠습니까? +joingame.title = 게임 참가 +joingame.ip = 주소: +disconnect = 서버와 연결이 해제되었습니다. +disconnect.data = 맵 데이터를 받아오는데 실패했습니다! +connecting = [accent]연결중... +connecting.data = [accent]맵 데이터 다운로드중... +server.port = 포트: +server.addressinuse = 이 주소는 이미 사용중입니다! +server.invalidport = 포트 번호가 잘못되었습니다. +server.error = [accent]{0}[crimson]서버를 여는데 오류가 발생했습니다. +save.old = 이 저장파일은 이전 버전의 게임용이며, 지금은 사용할 수 없습니다. \n\n[LIGHT_GRAY]4.0 정식때 이전 게임버전에서 만든 저장파일과 호환됩니다. +save.new = 새로 저장 +save.overwrite = 이 저장 슬롯을 덮어씌우겠습니까? +overwrite = 덮어쓰기 +save.none = 저장 파일을 찾지 못했습니다! +saveload = [accent]저장중... +savefail = 게임을 저장하지 못했습니다! +save.delete.confirm = 이 저장파일을 삭제 하시겠습니까? +save.delete = 삭제 +save.export = 저장파일 내보내기 +save.import.invalid = [accent]파일이 잘못되었습니다! +save.import.fail = [crimson]저장파일을 불러오지 못함: [accent]{0} +save.export.fail = [crimson]저장파일을 내보내지 못함: [accent]{0} +save.import = 저장파일 불러오기 +save.newslot = 저장 파일이름: +save.rename = 이름 변경 +save.rename.text = 새 이름: +selectslot = 저장슬롯을 선택하십시오. +slot = [accent]{0}번째 슬롯 +save.corrupted = [accent]세이브 파일이 손상되었거나 잘못된 파일입니다! 만약 게임을 업데이트 했다면 이것은 아마 저장 형식 변경일 것이고, 이것은 버그가 [scarlet]아닙니다[]. +empty = <비어있음> +on = 켜기 +off = 끄기 +save.autosave = 자동저장: {0} +save.map = 맵: {0} +save.wave = 웨이브 {0} +save.difficulty = 난이도: {0} +save.date = 마지막 저장날짜: {0} +save.playtime = 플레이시간: {0} +warning = 경고. +confirm = 확인 +delete = 삭제 +ok = OK +open = 열기 +customize = 커스텀마이징 +cancel = 취소 +openlink = 링크 열기 +copylink = 링크 복사 +back = 뒤로가기 +quit.confirm = 정말로 종료하시겠습니까? +changelog.title = 변경사항 +changelog.loading = 변경사항 가져오는중... +changelog.error.android = [accent]게임 변경사항은 가끔 Android 4.4 이하에서 작동하지 않습니다. 이것은 내부 Android 버그 때문입니다. +changelog.error.ios = [accent]현재 iOS에서는 변경 사항을 지원하지 않습니다. +changelog.error = [scarlet]게임 변경사항을 가져오는 중 오류가 발생했습니다!\n인터넷 연결을 확인하십시오. +changelog.current = [yellow][[현재 버전] +changelog.latest = [accent][[최신 버전] +loading = [accent]불러오는중... +saving = [accent]저장중... +wave = [accent]웨이브 {0} +wave.waiting = [green]{0}초[]후 웨이브 시작 +wave.waveInProgress = [LIGHT_GRAY]웨이브 진행중 +waiting = [LIGHT_GRAY]대기중... +waiting.players = 다른 플레이어를 기다리는 중.. +wave.enemies = [LIGHT_GRAY]{0} 마리 남았음 +wave.enemy = [LIGHT_GRAY]{0} 마리 남음 +loadimage = 사진 불러오기 +saveimage = 사진 저장 +unknown = 알 수 없음 +custom = 커스텀 +builtin = 기본맵 +map.delete.confirm = 이 맵을 삭제하시겠습니까? 이 명령은 취소할 수 없습니다! +map.random = [accent]랜덤 맵 +map.nospawn = 이 맵에 플레이어가 스폰 할 코어가 없습니다! 맵 편집기에서 [ROYAL]파란색[]코어를 맵에 추가하세요. +map.nospawn.pvp = 이 맵에는 적팀 코어가 없습니다! 에디터에서 [SCARLET]파란색 팀이 아닌[] 코어를 추가하세요. +map.nospawn.attack = 이 맵에는 플레이어가 공격할 수 있는 적의 코어가 없습니다! 에디터에서 [SCARLET] 빨간팀[] 코어를 맵에 추가하세요. +map.invalid = 파일이 잘못되었거나 손상되어 맵을 열 수 없습니다. +editor.brush = 브러쉬 +editor.openin = 편집기 열기 +editor.oregen = 광물 무작위 생성 +editor.oregen.info = 광물 무작위 생성: +editor.mapinfo = 맵 정보 +editor.author = 만든이: +editor.description = 설명: +editor.waves = 웨이브: +editor.rules = 규칙: +editor.ingame = 인게임 편집 +waves.title = 웨이브 +waves.remove = 삭제 +waves.never = <절대> +waves.every = 매 +waves.waves = 웨이브마다 +waves.perspawn = 스폰. +waves.to = 부터 +waves.boss = 이 몹은 보스임. +waves.preview = 미리보기 +waves.edit = 편집... +waves.copy = 클립보드로 복사 +waves.load = 클립보드에서 불러오기 +waves.invalid = 클립보드의 잘못된 웨이브 데이터 +waves.copied = 웨이브 복사됨 +editor.default = [LIGHT_GRAY]<기본값> +edit = 편집... +editor.name = 이름: +editor.spawn = 유닛 생성 +editor.removeunit = 유닛 삭제 +editor.teams = 팀 +editor.elevation = 지형 높이 +editor.errorload = [accent]{0} 파일을 불러오는데 실패했습니다. +editor.errorsave = [accent]{0} 파일을 저장하는데 실패했습니다. +editor.errorimage = 이것은 맵이 아니라 사진입니다. 확장자를 바꿔서 시도할 생각하지 마세요.\n\n예전 맵을 가져올려면 편집기의 '예전 맵 가져오기' 버튼을 사용하세요. +editor.errorlegacy = 이 맵은 너무 오래되어, 더이상 지원하지 않는 맵 형식을 사용합니다. +editor.errorheader = 이 맵 파일은 유효하지 않거나 손상되었습니다. +editor.errorname = 맵에 이름이 지정되어 있지 않습니다. +editor.update = 업데이트 +editor.randomize = 랜덤 +editor.apply = 적용 +editor.generate = 생성 +editor.resize = 맵 크기조정 +editor.loadmap = 맵 불러오기 +editor.savemap = 맵 저장 +editor.saved = 저장됨! +editor.save.noname = 지도에 이름이 없습니다! 메뉴 -> '맵 정보' 에서 설정하세요. +editor.save.overwrite = 이 맵의 이름은 기존에 있던 맵을 덮어씁니다! '맵 정보' 메뉴에서 다른 이름을 선택하세요. +editor.import.exists = [scarlet]맵을 불러올 수 없음: [] 기존에 있던 '{0}' 맵이 이미 존재합니다! +editor.import = 가져오기 +editor.importmap = 맵 가져오기 +editor.importmap.description = 이미 존재하는 맵 가져오기 +editor.importfile = 파일 가져오기 +editor.importfile.description = 외부 맵 파일 가져오기 +editor.importimage = 지형 사진 가져오기 +editor.importimage.description = 외부 맵 이미지 파일 가져오기 +editor.export = 내보내기 +editor.exportfile = 파일 내보내기 +editor.exportfile.description = 맵 파일 내보내기 +editor.exportimage = 지형 이미지 내보내기 +editor.exportimage.description = 맵 이미지 파일 내보내기 +editor.loadimage = 지형 가져오기 +editor.saveimage = 지형 내보내기 +editor.unsaved = [scarlet]변경사항을 저장하지 않았습니다![]\n정말로 나가시겠습니까? +editor.resizemap = 맵 크기 조정 +editor.mapname = 맵 이름: +editor.overwrite = [accept]경고!이 명령은 기존 맵을 덮어씌우게 됩니다. +editor.overwrite.confirm = [scarlet]경고![] 이 이름을 가진 맵이 이미 있습니다. 덮어 쓰시겠습니까? +editor.selectmap = 불러올 맵 선택: +filters.empty = [LIGHT_GRAY]필터가 없습니다!! 아래 버튼을 눌러 추가하세요. +filter.distort = 왜곡 +filter.noise = 노이즈 +filter.ore = 자원 +filter.rivernoise = 협곡 노이즈 +filter.scatter = 뿌리기 +filter.terrain = 지형 +filter.option.scale = 스케일 +filter.option.chance = 기회 +filter.option.mag = 규모 +filter.option.threshold = 한계점 +filter.option.circle-scale = 둥근 크기 +filter.option.octaves = 옥타보스 +filter.option.falloff = 절벽 +filter.option.block = 블록 +filter.option.floor = 바닥 +filter.option.wall = 벽 +filter.option.ore = 자원 +filter.option.floor2 = 2번째 바닥 +filter.option.threshold2 = 2번째 한계점 +filter.option.radius = 반경 +filter.option.percentile = 백분위수 +width = 넓이: +height = 높이: +menu = 메뉴 +play = 플레이 +load = 불러오기 +save = 저장 +fps = FPS: {0} +tps = TPS: {0} +ping = Ping: {0}ms +language.restart = 언어를 변경하려면 게임을 다시시작 해 주세요. +settings = 설정 +tutorial = 게임 방법 +editor = 편집기 +mapeditor = 맵 편집기 +donate = 기부 +abandon = 포기 +abandon.text = 이 구역과 모든 자원이 적에게 빼앗길 것입니다. +locked = 잠김 +complete = [LIGHT_GRAY]완료: +zone.requirement = 지역 {1} 에서 웨이브 {0} 달성 +resume = 지역 계속 플레이:\n[LIGHT_GRAY]{0} +bestwave = [LIGHT_GRAY]최고 점수: {0} +launch = < 출격 > +launch.title = 출격 성공 +launch.next = [LIGHT_GRAY]다음 출격기회는 {0} 단계에서 나타납니다. +launch.unable = [scarlet]출격할 수 없습니다.[] {0}마리 남음. +launch.confirm = 출격하게 되면 모든 자원이 코어로 들어갑니다.\n또한 성공하기 전까지 기지로 돌아갈 수 없습니다. +uncover = 털어넣기 +configure = 로드아웃 설정 +configure.locked = {0} 단계에서 로드아웃을 설정할 수 있음. +zone.unlocked = [LIGHT_GRAY] 잠금 해제됨. +zone.requirement.complete = 웨이브 {0} 달성:\n{1} 지역 요구사항이 충족됨. +zone.config.complete = 웨이브 {0} 달성:\n로드아웃 설정 잠금 해제됨. +zone.resources = 자원 감지됨: +add = 추가... +boss.health = 보스 체력 +connectfail = [crimson]{0}[accent] 서버에 연결하지 못했습니다.[] +error.unreachable = 서버에 연결하지 못했습니다.\n서버 주소가 정확히 입력되었나요? +error.invalidaddress = 잘못된 주소입니다. +error.timedout = 시간 초과!\n서버에 포트 포워딩이 설정되어 있고 주소가 올바른지 확인하십시오. +error.mismatch = 패킷 오류:\n클라이언트/서버 버전이 일치하지 않습니다.\n접속할려는 서버가 최신 버전의 Mindustry 인지 확인하세요! +error.alreadyconnected = 이미 접속중입니다. +error.mapnotfound = 맵 파일을 찾을 수 없습니다! +error.io = 네트워크 I/O 오류. +error.any = 알 수 없는 네트워크 오류. +zone.groundZero.name = 그라운드 제로 +zone.desertWastes.name = 쓰레기 사막 +zone.craters.name = 분화구 +zone.frozenForest.name = 얼어붙은 숲 +zone.ruinousShores.name = 파멸의 기슭 +zone.stainedMountains.name = 얼룩진 산맥 +zone.desolateRift.name = 황량한 강 +zone.nuclearComplex.name = 핵 생산 단지 +zone.overgrowth.name = 과성장 지역 +zone.tarFields.name = 타르 지역 +settings.language = 언어 +settings.reset = 설정 초기화 +settings.rebind = 키 재설정 +settings.controls = 컨트롤 +settings.game = 게임 +settings.sound = 소리 +settings.graphics = 그래픽 +settings.cleardata = 게임 데이터 초기화... +settings.clear.confirm = 정말로 초기화 하겠습니까?\n이 작업을 되돌릴 수 없습니다! +settings.clearall.confirm = [scarlet]경고![]\n이 작업은 저장된 맵, 맵파일, 잠금 해제된 목록과 키 매핑, 그리고 모든 데이터를 삭제합니다.\n확인 버튼을 다시 눌러 모든 데이터를 삭제하고 게임에서 나갑니다. +settings.clearunlocks = 잠금 해제 초기화 +settings.clearall = 모두 초기화 +paused = 일시 정지 +yes = 예 +no = 아니오 +info.title = [accent]정보 +error.title = [crimson]오류가 발생했습니다. +error.crashtitle = 오류가 발생했습니다. +blocks.input = 입력 +blocks.output = 출력 +blocks.booster = 가속 +block.unknown = [LIGHT_GRAY]??? +blocks.powercapacity = 전력 용량 +blocks.powershot = 1발당 전력 소모량 +blocks.targetsair = 공중공격 가능 +blocks.targetsground = 지상공격 가능 +blocks.itemsmoved = 이동 속도 +blocks.launchtime = 출격 시간 +blocks.shootrange = 사거리 +blocks.size = 크기 +blocks.liquidcapacity = 액체 용량 +blocks.powerrange = 전력 범위 +blocks.poweruse = 전력 사용 +blocks.powerdamage = 전력/데미지 +blocks.itemcapacity = 저장 용량 +blocks.basepowergeneration = 기본 전력 생성량 +blocks.productiontime = 제작 시간 +blocks.repairtime = 전체 블록 수리시간 +blocks.speedincrease = 속도 증가 +blocks.range = 사거리 +blocks.drilltier = 드릴 +blocks.drillspeed = 기본 드릴 속도 +blocks.boosteffect = 가속 효과 +blocks.maxunits = 최대 활성유닛 +blocks.health = 체력 +blocks.buildtime = Build Time +blocks.inaccuracy = 오차각 +blocks.shots = 발포 횟수 +blocks.reload = 재장전 +blocks.ammo = 탄약 +bar.drillspeed = Drill Speed: {0}/s +bar.efficiency = 효율성: {0}% +bar.powerbalance = 전력: {0}/s +bar.poweramount = 전력: {0} +bar.poweroutput = 전력 출력: {0} +bar.items = 아이템: {0} +bar.liquid = 액체 +bar.heat = 발열 +bar.power = 전력 +bar.progress = 건설 진행 +bar.spawned = 유닛: {0}/{1} +bullet.damage = [stat]{0}[lightgray] 데미지 +bullet.splashdamage = [stat]{0}[lightgray] 범위 데미지 ~[stat] {1}[lightgray] 타일 +bullet.incendiary = [stat]방화 +bullet.homing = [stat]유도탄 +bullet.shock = [stat]충격탄 +bullet.frag = [stat]파편 +bullet.knockback = [stat]{0}[lightgray] 넉백 +bullet.freezing = [stat]동결 +bullet.tarred = [stat]타르 +bullet.multiplier = [stat]{0}[lightgray]x 탄약 배율 +bullet.reload = [stat]{0}[lightgray]x 사격 속도 +unit.blocks = 블록 +unit.powersecond = 전력/초 +unit.liquidsecond = 액체/초 +unit.itemssecond = 개/초 +unit.liquidunits = 액체 +unit.powerunits = 전력 +unit.degrees = 도 +unit.seconds = 초 +unit.persecond = /초 +unit.timesspeed = x 배 +unit.percent = % +unit.items = 아이템 +category.general = 일반 +category.power = 전력 +category.liquids = 액체 +category.items = 아이템 +category.crafting = 제작 +category.shooting = 사격 +category.optional = 보조 아이템 +setting.landscape.name = 가로화면으로 고정 +setting.shadows.name = 그림자 +setting.linear.name = 선형 필터링 +setting.animatedwater.name = 움직이는 물 +setting.animatedshields.name = 움직이는 보호막 +setting.antialias.name = 안티 에일리어싱[LIGHT_GRAY] (재시작 필요)[] +setting.indicators.name = 아군/적 인디게이터 표시 +setting.autotarget.name = 자동 조준 +setting.fpscap.name = 최대 FPS +setting.fpscap.none = 없음 +setting.fpscap.text = {0}FPS +setting.swapdiagonal.name = 항상 대각선 설치 +setting.difficulty.training = 훈련 setting.difficulty.easy = 쉬움 setting.difficulty.normal = 보통 setting.difficulty.hard = 어려움 -setting.difficulty.insane = 미쳤음 -setting.difficulty.purge = 청소기 +setting.difficulty.insane = 미침 setting.difficulty.name = 난이도: -setting.screenshake.name = 화면 흔들림 -setting.smoothcam.name = 부드러운 카메라 이동 -setting.indicators.name = 적 위치 표시 화살표 +setting.screenshake.name = 화면 흔들기 setting.effects.name = 화면 효과 setting.sensitivity.name = 컨트롤러 감도 -setting.saveinterval.name = 자동 저장 간격 -setting.seconds = {0}초 +setting.saveinterval.name = 저장 간격 +setting.seconds = 초 setting.fullscreen.name = 전체 화면 -setting.multithread.name = 멀티 스레드 활성화 -setting.fps.name = 초당 프레임 표시 -setting.vsync.name = 수직동기화 -setting.lasers.name = 파워 레이저 표시 -setting.previewopacity.name = 블럭 배치 미리보기 표시 -setting.healthbars.name = 체력 막대바 표시 -setting.pixelate.name = 화면 픽셀화 -setting.musicvol.name = 음악 볼륨 -setting.mutemusic.name = 음악 끄기 -setting.sfxvol.name = 효과음 볼륨 -setting.mutesound.name = 효과음 끄기 -map.maze.name = 미로 -map.fortress.name = 요새 -map.sinkhole.name = 싱크홀 -map.caves.name = 동굴 -map.volcano.name = 화산 -map.caldera.name = 칼데라 -map.scorch.name = 타버린 세계 -map.desert.name = 사막 -map.island.name = 섬 -map.grassland.name = 초원 -map.tundra.name = 툰드라 -map.spiral.name = 나선 -map.tutorial.name = 자습서 -tutorial.intro.text = [yellow] 자습서에 오신 것을 환영합니다.[] 시작하려면 '다음'을 누르세요. -tutorial.moveDesktop.text = 이동하려면 [orange] [[WASD] [] 키를 사용하세요. 또한, [orange]Shift[]를 누르고 있으면 빠르게 이동할 수 있습니다. [orange]CTRL[]을 누른 상태에서 [orange] 마우스 스크롤 휠 []을 사용하여 확대 또는 축소 할 수 있습니다 -tutorial.shoot.text = 마우스를 사용하여 조준하고 [orange]왼쪽 마우스 버튼[]을 눌러 쏘세요. 저기 노란색 [yellow]타겟[]앞에서 연습해보세요. -tutorial.moveAndroid.text = 화면을 이동하기 위해서 한손가락으로 드래그 해 보세요. 확대 및 축소는 두손가락으로 하면 됩니다. -tutorial.placeSelect.text = 오른쪽 하단에 있는 블럭 메뉴에서 [yellow]컨베이어[]를 선택하세요. -tutorial.placeConveyorDesktop.text = [orange][[마우스 스크롤 휠][]을 사용하여 컨베이어를[orange]앞 방향[]으로 돌린다음 [yellow]표시된 위치[]에 [orange][[왼쪽 마우스 버튼][]을 이용해 컨베이어를 설치 합니다. -tutorial.placeConveyorAndroid.text = 컨베이어를 회전 시키기 위해[orange][[회전 버튼][]를 누르고 [orange]앞방향[]을 보게 한후, 한손가락으로 [yellow]표시된 위치[]에 [orange][[확인][] 버튼으로 설치하세요. -tutorial.placeConveyorAndroidInfo.text = 아니면 왼쪽 하단에 [orange][[설치 모드][]를 눌러 클릭모드로 전환하고 클릭만으로 블럭을 배치 할수 있습니다.\n방향을 바꾸려면 왼쪽하단 5번째 칸에 있는 화살표로 바꾸시면 됩니다.\n[yellow]다음[]을 눌러 한번 해 보세요. -tutorial.placeDrill.text = 이제, 표시된 위치에 [yellow]돌 드릴[]을 선택하여 배치하세요. -tutorial.blockInfo.text = 블록에 대해 더 자세히 알고 싶다면, 오른쪽 상단에있는 [orange]물음표[]를 눌러 설명을 읽으세요. -tutorial.deselectDesktop.text = [orange][[마우스 오른쪽 버튼][]을 사용하여 블록을 선택 해제할 수 있습니다. -tutorial.deselectAndroid.text = [orange]X[] 버튼을 눌러 블록을 선택 해제할 수 있습니다. -tutorial.drillPlaced.text = 드릴은 이제 [yellow] 돌[]을 생산할 것이고, 컨베이어로 내보낸 다음 [yellow]코어[]로 옮길 것입니다. -tutorial.drillInfo.text = 각 광석은 그에 맞는 드릴이 필요합니다. 돌은 돌 드릴이 필요하고, 철은 철 드릴이 필요합니다. -tutorial.drillPlaced2.text = 아이템을 코어로 이동하면 왼쪽 상단의 [orange]아이템 인벤토리[]에 표시됩니다.\n인벤토리의 아이템은 블록을 배치할때 사용됩니다. -tutorial.moreDrills.text = 드릴과 컨베이어는 많이 연결할 수 있습니다. 이것처럼요. -tutorial.deleteBlock.text = 블록을 삭제하고 싶으면 [orange]마우스 오른쪽 버튼[]을 클릭하여 블록을 삭제할 수 있습니다.\n이 컨베이어를 삭제 해 보세요. -tutorial.deleteBlockAndroid.text = 왼쪽 하단에 있는 [orange]블록 삭제모드[]안에 [orange]십자선[] 아이콘을 선택한 다음 블록을 눌러 삭제할 수 있습니다. 이 컨베이어를 삭제해 보세요. -tutorial.placeTurret.text = 이제[yellow] 표시된 위치 []에 [yellow]포탑[]을 선택하여 배치하세요. -tutorial.placedTurretAmmo.text = 이 포탑은 컨베이어에서 [yellow]탄약[]을 받습니다.\n포탑에 커서를 가리키면 [green]녹색 막대[]를 통해 얼마나 많은 탄약이 포탑 안에있는지 확인 할수 있습니다. -tutorial.turretExplanation.text = 포탑은 충분한 탄약을 보유하고있는 한, 범위 내의 가장 가까운 적을 향해 자동으로 공격합니다. -tutorial.waves.text = [yellow]60[]초 마다, 웨이브가 시작되고[coral]적[]들이 특정 위치에 스폰되어 코어를 파괴하기위해 올 것 입니다. -tutorial.coreDestruction.text = 당신의 목표는 [yellow]코어를 최대한 오래동안 방어하는 것[]입니다. 코어가 파괴되면 [coral]게임에서 패배합니다[]. -tutorial.pausingDesktop.text = 휴식을 취해야 하는 경우, 왼쪽 상단에 있는 [orange]일시정지 버튼[] 을 누르거나 [orange]스페이스바[] 를 눌러 게임을 일시정지 시킬 수 있습니다.\n일시정지된 상태에서 블록을 선택하고 배치할 수는 있지만, 움직이거나 공격할 수는 없습니다. -tutorial.pausingAndroid.text = 휴식을 취해야 하는 경우, 왼쪽 상단에 있는 [orange]일시정지 버튼[] 을 눌러 게임을 일시정지 시킬 수 있습니다.\n일시정지된 상태에서 블록을 선택하고 배치할 수는 있지만, 움직이거나 공격할 수는 없습니다. -tutorial.purchaseWeapons.text = 왼쪽 하단에 있는 업그레이드 메뉴를 열어 새로운 [yellow]무기[]를 구입할 수 있습니다. -tutorial.switchWeapons.text = 왼쪽 하단의 아이콘을 클릭하거나 숫자 [orange][[1-9][] 버튼을 사용하여 무기를 바꿀 수 있습니다. -tutorial.spawnWave.text = 웨이브가 시작되었습니다. 적들을 파괴하세요! -tutorial.pumpDesc.text = 이후 웨이브에선 발전기 또는 추출기용 액체를 사용하기 위해 [yellow]펌프[] 를 사용해야 할 수도 있습니다 -tutorial.pumpPlace.text = 펌프는 드릴과 유사하게 작동하지만, 아이템 대신 액체를 생산합니다. [yellow]지정된 위치[]에 펌프를 놓으세요. -tutorial.conduitUse.text = 이제 [orange]파이프[] 를 펌프에서 멀리 떨어트려 두세요 -tutorial.conduitUse2.text = 하나 더... -tutorial.conduitUse3.text = 하나 더... -tutorial.generator.text = 이제 파이프 끝 부분에 [orange]석유 발전기[] 블록을 놓으세요. -tutorial.generatorExplain.text = 이제 이 발전기는 석유에서 [yellow]전력[]을 생성합니다. -tutorial.lasers.text = 전력은 [yellow]파워 레이저[]를 통해 전송됩니다. 회전한 후 이곳에 배치하세요. -tutorial.laserExplain.text = 발전기가 이제 레이저 블록으로 전력을 보냅니다.\n[yellow]불투명[] 광선은 현재 전력을 전송 중임을 의미하고 [yellow]투명[] 광선은 전송하지 않음을 의미합니다. -tutorial.laserMore.text = 블록 위로 마우스를 올리고 상단의 [yellow]노란 막대[]를 확인하여 블록의 전력을 볼 수 있습니다 -tutorial.healingTurret.text = 이 레이저는 [lime]수리포탑[] 에 전력을 공급하는데 사용합니다. 여기에 하나 놓으세요 -tutorial.healingTurretExplain.text = 전력이 있는 한 이 포탑은 [lime]주변 블록을 수리[] 합니다.\n시간이 있을 때 가능한 빨리 기지에 하나가 있는지 확인하세요! -tutorial.smeltery.text = 많은 블록들은[orange]강철[]을 필요로 합니다.\n[orange]제련소[]를 선택해 여기에 놓으세요. -tutorial.smelterySetup.text = 이 제련소는 석탄을 연료로 사용하며 철을 넣으면 [orange]강철[] 을 생산할 것입니다. -tutorial.tunnelExplain.text = 또한 아이템이 [orange]터널 블록[]을 통해 지나가고 다른 쪽에서 돌 블록을 통과하여 나옵니다.\n터널은 최대 2블록까지만 통과할 수 있습니다. -tutorial.end.text = 이것으로 자습서를 마칩니다! 행운을 빕니다! -text.keybind.title = 키 지정 -keybind.move_x.name = x축 이동 -keybind.move_y.name = y축 이동 +setting.borderlesswindow.name = 테두리 없는 창모드[LIGHT_GRAY] (재시작이 필요할 수 있습니다) +setting.fps.name = FPS 표시 +setting.vsync.name = VSync 활성화 +setting.lasers.name = 전력 노드 레이저 표시 +setting.pixelate.name = 픽셀화[LIGHT_GRAY] (애니메이션 효과가 꺼집니다) +setting.minimap.name = 미니맵 보기 +setting.musicvol.name = 음악 크기 +setting.mutemusic.name = 음소거 +setting.sfxvol.name = 효과음 크기 +setting.mutesound.name = 소리 끄기 +setting.crashreport.name = 오류 보고서 보내기 +setting.chatopacity.name = 채팅 투명도 +setting.playerchat.name = 인게임 채팅 표시 +keybind.title = 조작키 설정 +category.general.name = 일반 +category.view.name = 보기 +category.multiplayer.name = 멀티플레이 +command.attack = 공격 +command.retreat = 후퇴 +command.patrol = 순찰 +keybind.gridMode.name = 블록 선택 +keybind.gridModeShift.name = 카테고리 선택 +keybind.press = 키를 누르세요... +keybind.press.axis = 축 또는 키를 누르세요... +keybind.screenshot.name = 맵 스크린샷 +keybind.move_x.name = 오른쪽/왼쪽 이동 +keybind.move_y.name = 위 / 아래 중간 keybind.select.name = 선택 -keybind.break.name = 파괴 -keybind.shoot.name = 발사 -keybind.zoom_hold.name = 확대 할때 누를 버튼 +keybind.diagonal_placement.name = 대각선 설치 +keybind.pick.name = 블록 선택 +keybind.break_block.name = 블록 파괴 +keybind.deselect.name = 선택해제 +keybind.shoot.name = 사격 +keybind.zoom_hold.name = 길게 확대 keybind.zoom.name = 확대 -keybind.block_info.name = 블록 정보 keybind.menu.name = 메뉴 -keybind.pause.name = 일시정지 +keybind.pause.name = 일시중지 +keybind.minimap.name = 미니맵 keybind.dash.name = 달리기 -keybind.chat.name = 대화창 +keybind.chat.name = 채팅 keybind.player_list.name = 플레이어 목록 keybind.console.name = 콘솔 -keybind.rotate_alt.name = 반대로 돌기 keybind.rotate.name = 회전 -keybind.weapon_1.name = 무기 단축키_1 -keybind.weapon_2.name = 무기 단축키_2 -keybind.weapon_3.name = 무기 단축키_3 -keybind.weapon_4.name = 무기 단축키_4 -keybind.weapon_5.name = 무기 단축키_5 -keybind.weapon_6.name = 무기 단축키_6 -mode.text.help.title = 모드 설명 -mode.waves.name = 단계 -mode.waves.description = 이것은 일반 모드입니다. 제한된 자원과 자동으로 웨이브가 시작됩니다. +keybind.toggle_menus.name = 메뉴 보이기/숨기기 +keybind.chat_history_prev.name = 이전 채팅기록 +keybind.chat_history_next.name = 다음 채팅기록 +keybind.chat_scroll.name = 채팅 스크롤 +keybind.drop_unit.name = 유닛 드롭 +keybind.zoom_minimap.name = 미니맵 확대 +mode.help.title = 모드 도움말 +mode.survival.name = 생존 +mode.survival.description = 이것은 일반 모드입니다. 제한된 자원을 가지고 자동으로 다음 단계가 시작됩니다. mode.sandbox.name = 샌드박스 -mode.sandbox.description = 무한한 자원과 웨이브를 시작하는 타이머가 없습니다. -mode.freebuild.name = 자유 건설 -mode.freebuild.description = 제한된 자원을 가지고 있으며, 웨이브를 시작하기 위한 타이머가 없습니다. -upgrade.standard.name = 기본 -upgrade.standard.description = 그냥 기본 총. -upgrade.blaster.name = 블래스터 -upgrade.blaster.description = 그냥 느리고 약한 총알. -upgrade.triblaster.name = 3단 블래스터 -upgrade.triblaster.description = 3발을 동시에 쏘는 총. -upgrade.clustergun.name = 유탄발사기 -upgrade.clustergun.description = 적에 맞으면 폭발하거나, 일정시간 후 터집니다. -upgrade.beam.name = 레이저 캐논 -upgrade.beam.description = 적을 통과하는 장거리 레이저 빔을 쏩니다 -upgrade.vulcan.name = 벌칸 -upgrade.vulcan.description = 빠른 속도로 총을 쏩니다 -upgrade.shockgun.name = 샷건 -upgrade.shockgun.description = 충전된 총알을 산탄처럼 쏘는 총 -item.stone.name = 돌 -item.iron.name = 철 +mode.sandbox.description = 무한한 자원을 가지고 자유롭게 다음 단계를 시작할 수 있습니다. +mode.pvp.name = PvP +mode.pvp.description = 실제 플레이어와 PvP를 합니다. +mode.attack.name = 공격 +mode.attack.description = 적 기지를 파괴하세요. 웨이브가 없습니다. +mode.custom = 커스텀 규칙 +rules.infiniteresources = 무한 자원 +rules.wavetimer = 웨이브 타이머 +rules.waves = 웨이브 +rules.enemyCheat = 무한 AI 자원 +rules.unitdrops = 유닛 드롭 +rules.unitbuildspeedmultiplier = 유닛 제조속도 배수 +rules.unithealthmultiplier = 유닛 체력 배수 +rules.playerhealthmultiplier = 플레이어 체력 배수 +rules.playerdamagemultiplier = 플레이어 공격력 배수 +rules.unitdamagemultiplier = 유닛 공격력 배수 +rules.enemycorebuildradius = 적 코어 건설 금지구역:[LIGHT_GRAY] (타일) +rules.respawntime = 리스폰 시간:[LIGHT_GRAY] (초) +rules.wavespacing = 웨이브 간격:[LIGHT_GRAY] (초) +rules.buildcostmultiplier = 건설 소모 배수 +rules.buildspeedmultiplier = 건설 속도 배수 +rules.waitForWaveToEnd = 웨이브가 끝날때까지 기다리는중 +rules.dropzoneradius = 드롭 구역 반경:[LIGHT_GRAY] (타일) +rules.respawns = 웨이브당 최대 리스폰 횟수 +rules.limitedRespawns = 리스폰 제한 +rules.title.waves = 웨이브 +rules.title.respawns = 리스폰 +rules.title.resourcesbuilding = 자원 & 건축 +rules.title.player = 플레이어들 +rules.title.enemy = 적 +rules.title.unit = 유닛 +content.item.name = 아이템 +content.liquid.name = 액체 +content.unit.name = 유닛 +content.block.name = 블록 +content.mech.name = 기체 +item.copper.name = 구리 +item.copper.description = 모든 종류의 블록에서 광범위하게 사용되는 자원입니다. +item.lead.name = 납 +item.lead.description = 쉽게 구할 수 있으며, 전자 및 액체 수송 블록에서 광범위하게 사용되는 자원입니다. item.coal.name = 석탄 -item.steel.name = 강철 +item.coal.description = 흔하고 쉽게 구할 수 있는 연료. +item.graphite.name = 흑연 item.titanium.name = 티타늄 -item.dirium.name = 합금 -item.uranium.name = 우라늄 +item.titanium.description = 파이프 재료나 고급 드릴, 비행기/기체 등에서 재료로 사용되는 자원입니다. +item.thorium.name = 토륨 +item.thorium.description = 건물의 재료, 터렛의 탄약 또는 핵연료로 사용되는 방사성 금속입니다. +item.silicon.name = 실리콘 +item.silicon.description = 매우 유용한 반도체로, 기체를 만들거나 태양 전지판 등 전자 건물에 사용할 수 있습니다. +item.plastanium.name = 플라스터늄 +item.plastanium.description = 고급 항공기 및 분열 탄약에 사용되는 가벼운 연성 재료. +item.phase-fabric.name = 위상 패브릭 +item.phase-fabric.description = 최첨단 전자 제품과 자기수리 기술에 사용되는 거의 무중력에 가까운 물질입니다. +item.surge-alloy.name = 서지 합금 +item.surge-alloy.description = 독특한 전기 특성을 가진 고급 합금입니다. +item.spore-pod.name = 포자 포드 +item.spore-pod.description = 석유, 폭발물 및 연료로 전환하는데 사용됩니다. item.sand.name = 모래 +item.sand.description = 고밀도 합금 제작이나 제련시 이 광물을 사용하여 소모 재료를 줄이는 등 광범위하게 사용되는 일반적인 재료입니다. +item.blast-compound.name = 폭발 화합물 +item.blast-compound.description = 터렛 및 건설의 재료로 사용되는 휘발성 폭발물.\n연료로도 사용할 수 있지만, 별로 추천하지는 않습니다. +item.pyratite.name = 피라타이트 +item.pyratite.description = 폭발성을 가진 재료로, 주로 터렛의 탄약으로 사용됩니다. +item.metaglass.name = 메타유리 +item.metaglass.description = 초강력 유리 화합물. 액체 분배 및 저장에 광범위하게 사용됩니다. +item.scrap.name = 조각 +item.scrap.description = 오래된 건물과 유닛의 남은 잔해. 미량의 많은 금속이 포함되어 있습니다. liquid.water.name = 물 -liquid.plasma.name = 플라즈마 -liquid.lava.name = 용암 +liquid.slag.name = 광재 liquid.oil.name = 석유 -block.weaponfactory.name = 무기 공장 -block.weaponfactory.fulldescription = 플레이어용 무기를 만드는 데 사용됩니다.\n클릭하여 사용하십시오.\n코어에 있는 자원을 사용합니다. -block.air.name = 공기 -block.blockpart.name = 블록파트 -block.deepwater.name = 깊은 물 +liquid.cryofluid.name = 냉각유체 +mech.alpha-mech.name = 알파 +mech.alpha-mech.weapon = 중무장 소총 +mech.alpha-mech.ability = 회복 +mech.alpha-mech.description = 표준 기체.\n적절한 속도와 공격력을 갖추고 있습니다. +mech.delta-mech.name = 델타 +mech.delta-mech.weapon = 전격 생산기 +mech.delta-mech.ability = 충전 +mech.delta-mech.description = 빠르게 이동하는 적을 처치하기 위한 가벼운 기체.\n구조물에는 거의 피해를 주지 않지만, 전격 무기를 사용하여 많은 적군을 매우 빠르게 죽일 수 있습니다. +mech.tau-mech.name = 타우 +mech.tau-mech.weapon = 건물 수리총 +mech.tau-mech.ability = 유닛 치료 +mech.tau-mech.description = 지원형 기체.\n총을 발사하여 건물을 치료하고 회복 능력 사용으로 화재를 진압하거나, 반경 내 아군을 치유시킵니다. +mech.omega-mech.name = 오메가 +mech.omega-mech.weapon = 전방 유도미사일 +mech.omega-mech.ability = 방어모드 +mech.omega-mech.description = 지상 기체 최종판이자 건물 파괴용으로 적합한 부피가 크고 튼튼한 기체.\n방어 모드는 최대 90% 의 피해를 줄일 수 있습니다. +mech.dart-ship.name = 다트 +mech.dart-ship.weapon = 소총 +mech.dart-ship.description = 표준 비행선.\n빠르고 가볍지만 공격력이 거의 없고 채광 속도가 느립니다. +mech.javelin-ship.name = 재블린 +mech.javelin-ship.description = 치고 빠지는 공격을 위한 비행선.\n처음에는 느리지만, 가속도가 붙어 엄청난 속도로 미사일 피해를 입힐 수 있으며, 전격 능력을 사용할 수 있습니다. +mech.javelin-ship.weapon = 유도 미사일 +mech.javelin-ship.ability = 가속 전격 생성기 +mech.trident-ship.name = 삼지창 +mech.trident-ship.description = 대형 공중 폭격기.\n당연하게도 엄청 단단합니다. +mech.trident-ship.weapon = 폭탄 저장고 +mech.glaive-ship.name = 글레브 +mech.glaive-ship.description = 크고 잘 무장된 총을 가진 비행선.\n방화용 리피터가 장착되어 있으며, 가속도와 최대속도가 높습니다. +mech.glaive-ship.weapon = 방화총 +item.explosiveness = [LIGHT_GRAY]폭발성: {0} +item.flammability = [LIGHT_GRAY]인화성: {0} +item.radioactivity = [LIGHT_GRAY]방사능: {0} +unit.health = [LIGHT_GRAY]체력: {0} +unit.speed = [LIGHT_GRAY]속도: {0} +mech.weapon = [LIGHT_GRAY]무기: {0} +mech.health = [LIGHT_GRAY]체력: {0} +mech.itemcapacity = [LIGHT_GRAY]아이템 수용 용량: {0} +mech.minespeed = [LIGHT_GRAY]채광 속도: {0}% +mech.minepower = [LIGHT_GRAY]채광 레벨: {0} +mech.ability = [LIGHT_GRAY]능력: {0} +mech.buildspeed = [LIGHT_GRAY]건설 속도: {0}% +liquid.heatcapacity = [LIGHT_GRAY]발열 용량: {0} +liquid.viscosity = [LIGHT_GRAY]점도: {0} +liquid.temperature = [LIGHT_GRAY]온도: {0} +block.grass.name = 잔디 +block.salt.name = 소금 +block.saltrocks.name = 소금 바위 +block.pebbles.name = 조약돌 +block.tendrils.name = 덩굴 +block.sandrocks.name = 모래 바위 +block.spore-pine.name = 포자 소나무 +block.sporerocks.name = 포자 바위 +block.rock.name = 바위 +block.snowrock.name = 눈바위 +block.shale.name = 이판암 +block.shale-boulder.name = 둥근 이판암 +block.moss.name = 이끼 +block.shrubs.name = 관목 +block.spore-moss.name = 포자 이끼 +block.shalerocks.name = 이판암 바위 +block.scrap-wall.name = 조각벽 +block.scrap-wall-large.name = 큰 조각벽 +block.scrap-wall-huge.name = 거대한 조각 벽 +block.scrap-wall-gigantic.name = 엄청나게 큰 조각 벽 +block.thruster.name = 트릭스터 +block.kiln.name = 가마 +block.kiln.description = 모래를 녹여서 메타유리로 만듭니다. 소량의 전력이 필요합니다. +block.graphite-press.name = 흑연 압축기 +block.multi-press.name = 다중 압축기 +block.constructing = {0} [LIGHT_GRAY](만드는중) +block.spawn.name = 적 스폰지점 +block.core-shard.name = 코어-공유 +block.core-foundation.name = 코어-기초 +block.core-nucleus.name = 코어-핵 +block.deepwater.name = 깊은물 block.water.name = 물 -block.lava.name = 용암 -block.oil.name = 석유 +block.tainted-water.name = 오염된 물 +block.darksand-tainted-water.name = 검은 모래로 오염된 물 +block.tar.name = 타르 block.stone.name = 돌 -block.blackstone.name = 검은 돌 -block.iron.name = 철 -block.coal.name = 석탄 -block.titanium.name = 티타늄 -block.uranium.name = 우라늄 -block.dirt.name = 흙 block.sand.name = 모래 +block.darksand.name = 검은 모래 block.ice.name = 얼음 block.snow.name = 눈 -block.grass.name = 잔디 -block.sandblock.name = 모래 블럭 -block.snowblock.name = 얼음 블럭 -block.stoneblock.name = 돌 블럭 -block.blackstoneblock.name = 검은 돌 블럭 -block.grassblock.name = 잔디 블럭 -block.mossblock.name = 이끼블럭 -block.shrub.name = 덤불 -block.rock.name = 바위 -block.icerock.name = 얼음 덩어리 -block.blackrock.name = 검은 바위 -block.dirtblock.name = 흙 블럭 -block.stonewall.name = 돌 벽 -block.stonewall.fulldescription = 가장 안좋은 그냥 돌벽. 초반에 코어와 포탑을 보호하는데 사용 할 수 있습니다. -block.ironwall.name = 철 벽 -block.ironwall.fulldescription = 기본적인 벽 블럭. 적으로부터 보호해 줍니다. -block.steelwall.name = 강철 벽 -block.steelwall.fulldescription = 나름 좋은 벽 블럭. 적으로부터의 공격을 대부분 보호해 줍니다. -block.titaniumwall.name = 티타늄 벽 -block.titaniumwall.fulldescription = 강력한 벽 블럭. 적으로부터의 공격을 거의 보호해 줍니다 -block.duriumwall.name = 합금 벽 -block.duriumwall.fulldescription = 매우 강력한 벽 블록. 게임내에서 가장 강력한 벽 입니다. -block.compositewall.name = 복합 벽 -block.steelwall-large.name = 대형 강철 벽 -block.steelwall-large.fulldescription = 좋은 벽 블럭. 2x2 크기에 큰 벽입니다. -block.titaniumwall-large.name = 대형 티타늄 벽 -block.titaniumwall-large.fulldescription = 강력한 벽 블럭. 2x2 크기에 큰 벽입니다. -block.duriumwall-large.name = 대형 합금 벽 -block.duriumwall-large.fulldescription = 매우 강력한 벽 블록. 2x2 크기에 큰 벽입니다. -block.titaniumshieldwall.name = 보호막 벽 -block.titaniumshieldwall.fulldescription = 보호막이 내장 된 강력한 벽 블럭입니다.\n적의 총알을 흡수할 때 에너지를 사용하기 때문에 전력이 필요합니다.\n전력을 공급할때 무선 공급기를 사용하면 편리합니다 . -block.repairturret.name = 수리포탑 -block.repairturret.fulldescription = 범위 안에 있는 손상된 블록을 느린 속도로 수리합니다. 소량의 전력을 사용합니다. -block.megarepairturret.name = 수리포탑 II -block.megarepairturret.fulldescription = 범위 안에 있는 손상된 블록을 수리합니다. 전력을 사용합니다. -block.shieldgenerator.name = 보호막 발전기 -block.shieldgenerator.fulldescription = 고급 보호 블록. 반경 내의 모든 블록을 공격으로부터 보호합니다.\n작동 중일 때는 느린 속도로 전력을 사용하지만 공격을 받았을 시 그만큼의 전력을 소모하게 됩니다. +block.craters.name = 크레이터 +block.sand-water.name = 젖은모래 +block.darksand-water.name = 검은모래물 +block.char.name = 숯 +block.holostone.name = 홀로스톤 +block.ice-snow.name = 얼음눈 +block.rocks.name = 돌 +block.icerocks.name = 얼음바위 +block.snowrocks.name = 눈바위 +block.dunerocks.name = 모래돌 +block.pine.name = 소나무 +block.white-tree-dead.name = 죽은 하얀나무 +block.white-tree.name = 하얀 나무 +block.spore-cluster.name = 포자낭 +block.metal-floor.name = 금속제 바닥 +block.metal-floor-2.name = 금속제 바닥 2 +block.metal-floor-3.name = 금속제 바닥 3 +block.metal-floor-5.name = 금속제 바닥 5 +block.metal-floor-damaged.name = 손상된 금속제 바닥 +block.dark-panel-1.name = 어두운 패널 1 +block.dark-panel-2.name = 어두운 패널 2 +block.dark-panel-3.name = 어두운 패널 3 +block.dark-panel-4.name = 어두운 패널 4 +block.dark-panel-5.name = 어두운 패널 5 +block.dark-panel-6.name = 어두운 패널 6 +block.dark-metal.name = 어두운 금속제 +block.ignarock.name = 얼은 바위 +block.hotrock.name = 뜨거운 바위 +block.magmarock.name = 마그마 바위 +block.cliffs.name = 절벽 +block.copper-wall.name = 구리벽 +block.copper-wall-large.name = 큰 구리벽 +block.titanium-wall.name = 티타늄 벽 +block.titanium-wall-large.name = 대형 티타늄 벽 +block.phase-wall.name = 상직물벽 +block.phase-wall-large.name = 큰 상직물벽 +block.thorium-wall.name = 토륨벽 +block.thorium-wall-large.name = 대형 토륨벽 block.door.name = 문 -block.door.fulldescription = 블록을 탭하여 열거나 닫을 수 있습니다. -block.door-large.name = 대형 문 -block.door-large.fulldescription = 블록을 탭하여 열거나 닫을 수 있습니다. 2x2 크기 입니다. -block.conduit.name = 파이프 -block.conduit.fulldescription = 기본 액체 이송 블록. 컨베이어처럼 작동하지만 액체를 운반하는데 쓰입니다.\n펌프 또는 다른 파이프와 함께 사용하는 것이 가장 좋습니다.\n적과 플레이어가 액체를 건널때 쓰일수도 있습니다. -block.pulseconduit.name = 펄스 파이프 -block.pulseconduit.fulldescription = 파이프에 상위호환, 액체를 더 빨리 운반하고 파이프보다 더 많이 저장합니다. -block.liquidrouter.name = 액체 분배기 -block.liquidrouter.fulldescription = 분배기와 유사하게 작동합니다.\n한 방향으로 액체를 입력받아 다른 방향으로 출력합니다. 하나의 파이프에서 다른 파이프들로 액체를 분배할 때 유용합니다. +block.door-large.name = 대형문 +block.duo.name = 듀오 +block.scorch.name = 스코어치 +block.scatter.name = 스캐터 +block.hail.name = 헤일 +block.lancer.name = 랜서 block.conveyor.name = 컨베이어 -block.conveyor.fulldescription = 기본 컨베이어.\n아이템을 앞으로 운반시켜 자동으로 포탑이나 제작기에 넣어 줍니다.\n회전이 가능하고 적과 플레이어의 다리가 될 수도 있습니다. -block.steelconveyor.name = 강철 컨베이어 -block.steelconveyor.fulldescription = 빠른 컨베이어, 표준 컨베이어보다 빠르게 아이템을 운반합니다. -block.poweredconveyor.name = 펄스 컨베이어 -block.poweredconveyor.fulldescription = 가장 빠른 컨베이어. 강철 컨베이어보다 매우 항목을 이동합니다. -block.router.name = 분배기 -block.router.fulldescription = 한 방향으로 아이템을 받아 다른 방향으로 출력합니다.\n하나의 컨베이어에서 다른 컨베이어들로 아이템을 분배할 때 유용합니다 +block.titanium-conveyor.name = 티타늄 컨베이어 block.junction.name = 교차기 -block.junction.fulldescription = 두 개의 컨베이어를 교차시키는 역할을합니다.\n교차된 위치의 다른 재료를 담고있는 2개의 컨베이어가 있는 상황에서 유용합니다. -block.conveyortunnel.name = 컨베이어 터널 -block.conveyortunnel.fulldescription = 한블럭 단위로 아이템을 전송 합니다.\n이 블럭을 사용하려면 두 터널이 한블럭을 사이로 서로 반대 방향을 향하게하세요. -block.liquidjunction.name = 액체 교차기 -block.liquidjunction.fulldescription = 교차기와 유사하며 두 개 파이프를 교차시키는 역할을합니다.\n교차된 위치의 다른 액체를 담고있는 2개의 파이프가 있는 상황에서 유용합니다. -block.liquiditemjunction.name = 액체 아이템 교차기 -block.liquiditemjunction.fulldescription = 파이프와 컨베이어를 교차시키는 역할을합니다. -block.powerbooster.name = 무선 전력 공급기 -block.powerbooster.fulldescription = 반경 내에 무선으로 전력을 공급합니다. -block.powerlaser.name = 파워 레이저 -block.powerlaser.fulldescription = 반경 내에 바라보는 블록에 전력을 전송하는 레이저를 쏩니다.\n전력을 생성하지는 않고 발전기 또는 다른 레이저와 함께 사용하는 것이 가장 좋습니다. -block.powerlaserrouter.name = 레이저 분배기 -block.powerlaserrouter.fulldescription = 한 번에 세 방향으로 전력을 전송하는 레이저를 쏩니다.\n하나의 발전기에서 여러 개의 블록에 전원을 공급해야하는 경우에 유용합니다. -block.powerlasercorner.name = 코너 레이저 -block.powerlasercorner.fulldescription = 한 번에 두 방향으로 전력을 전송하는 레이저를 쏩니다.\n하나의 발전기에서 여러 개의 블록에 전원을 공급해야하는 상황에서 유용합니다 -block.teleporter.name = 텔레포터 -block.teleporter.fulldescription = 고급 아이템 전송 블록.\n텔레포터는 동일한 색깔의 다른 텔레포터에게 아이템을 전송합니다.\n같은 색의 텔레포터가 없다면 아무것도 하지 않습니다.\n동일한 색상의 여러 텔레포터가 있는 경우 임의의 하나에게 전송됩니다.\n전원을 사용하고, 눌러서 색깔을 바꿀수 있습니다. +block.router.name = 분배기 +block.distributor.name = 대형 분배기 block.sorter.name = 필터 -block.sorter.fulldescription = 유형별로 아이템을 정렬합니다.\n통과할 아이템은 블록의 색상으로 표시됩니다.\n통과된 아이템은 앞으로 출력 되고 나머지는 왼쪽과 오른쪽으로 출력됩니다. -block.core.name = 코어 -block.pump.name = 펌프 -block.pump.fulldescription = 물, 용암 또는 기름과 같은 액체를 퍼올립니다.\n붙어있는 파이프에게 액체를 배출합니다. -block.fluxpump.name = 플럭스 펌프 -block.fluxpump.fulldescription = 빠른 펌프. 더 많은 액체를 저장하고 액체를 더 빠르게 퍼올립니다. -block.smelter.name = 제련소 -block.smelter.fulldescription = 필수적인 제작 블록.\n1개의 철과 1개의 석탄을 연료로 넣으면 하나의 강철을 생성합니다.\n막힘을 방지하기 위해 철과 석탄을 다른 컨베이어에 넣는 것이 좋습니다. -block.crucible.name = 도가니 -block.crucible.fulldescription = 고급 제작 블록.\n티타늄 1개, 강철 1개, 석탄 1개를 연료로 넣으면 하나의 합금을 출력합니다.\n막힘을 방지하기 위해 석탄, 강철 및 티타늄을 다른 컨베이어에 입력하는 것이 좋습니다. -block.coalpurifier.name = 석탄 추출기 -block.coalpurifier.fulldescription = 간단한 추출기. 다량의 물과 돌이 공급되면 석탄을 생산합니다. -block.titaniumpurifier.name = 티타늄 추출기 -block.titaniumpurifier.fulldescription = 기본 추출기 블록. 다량의 물과 철이 공급되면 티타늄을 생산합니다. -block.oilrefinery.name = 정유소 -block.oilrefinery.fulldescription = 다량의 기름을 석탄으로 정제합니다.\n석탄이 부족할 때 석탄 기반 포탑에 연료를 공급하는 데 유용합니다. -block.stoneformer.name = 돌 생산기 -block.stoneformer.fulldescription = 용암을 돌로 바꿉니다. 석탄 추출기용 돌을 대량 생산할 때 유용합니다. -block.lavasmelter.name = 용암 제련소 -block.lavasmelter.fulldescription = 용암을 사용하여 철을 강철로 전환합니다. 제련소의 대안으로 쓸수 있습니다.\n석탄이 부족한 상황에서 유용합니다. -block.stonedrill.name = 돌 드릴 -block.stonedrill.fulldescription = 필수 적인 드릴. 돌 타일 위에 놓을 때 느린속도로 채굴합니다. -block.irondrill.name = 철 드릴 -block.irondrill.fulldescription = 기본 드릴. 철 광석 타일에 놓으면 느린속도로 철을 채굴합니다. -block.coaldrill.name = 석탄 드릴 -block.coaldrill.fulldescription = 기본 드릴. 석탄 광석 타일 위에 놓으면 느린속도로 석탄을 채굴 합니다. -block.uraniumdrill.name = 우라늄 드릴 -block.uraniumdrill.fulldescription = 고급 드릴. 우라늄 광석 타일 위에 놓으면, 느린 속도로 우라늄을 채굴합니다. -block.titaniumdrill.name = 티타늄 드릴 -block.titaniumdrill.fulldescription = 고급 드릴. 티타늄 광석 타일 위에 놓으면 느린속도로 티타늄을 채굴합니다. -block.omnidrill.name = 옴니 드릴 -block.omnidrill.fulldescription = 빠른 속도로 모든 광석을 채굴 할 수 있습니다. -block.coalgenerator.name = 석탄 발전기 -block.coalgenerator.fulldescription = 필수적인 발전기. 석탄으로 전력을 생산합니다. 4면에 레이저로 전력을 출력합니다. -block.thermalgenerator.name = 지열 발전기 -block.thermalgenerator.fulldescription = 용암으로부터 전력을 생산합니다. 4면에 레이저로 전력을 출력합니다. -block.combustiongenerator.name = 석유 발전기 -block.combustiongenerator.fulldescription = 기름에서 전력을 생산합니다. 4면에 레이저로 전력을 출력합니다. -block.rtgenerator.name = 우라늄 발전기 -block.rtgenerator.fulldescription = 우라늄의 방사성 붕괴로부터 적은 양의 전력을 생성합니다. 4면에 레이저로 전력을 출력합니다. -block.nuclearreactor.name = 원자로 -block.nuclearreactor.fulldescription = 우라늄 발전기의 고급 버전. 우라늄으로부터 전력을 생산합니다.\n냉각수가 필요하며 높은 휘발성을 가지고 있습니다.\n물 공급량이 부족할 경우 매우 크게 폭발 하여 다른 블록에게 큰 피해를 줍니다. -block.turret.name = 포탑 -block.turret.fulldescription = 간단한 포탑. 탄약으로 돌을 사용합니다. 이중 포탑보다 약간 넓은 범위를가집니다. -block.doubleturret.name = 2단 포탑 -block.doubleturret.fulldescription = 포탑의 상위 버전입니다. 탄약으로 돌을 사용합니다. 더 많은 데미지를 주지만 범위는 낮습니다. 총알 2발을 동시에 발사합니다. -block.machineturret.name = 게틀링 포탑 -block.machineturret.fulldescription = 표준적인 포탑. 탄약으로 철을 사용합니다. 적당한 데미지에 빠른 발사 속도를 가지고 있습니다. -block.shotgunturret.name = 스플리터 포탑 -block.shotgunturret.fulldescription = 표준적인 포탑. 탄약으로 철을 사용합니다. 7발에 총알을 쏩니다. 범위가 낮지만 게틀링 포탑보다 높은 데미지를 가지고 있습니다. -block.flameturret.name = 화염 포탑 -block.flameturret.fulldescription = 고급 근거리 포탑. 탄약으로 석탄을 사용합니다. 범위는 낮지만 높은 데미지를 가지고 있습니다, 벽 뒤에 두는것이 좋습니다. -block.sniperturret.name = 레일건 포탑 -block.sniperturret.fulldescription = 고급 장거리 포탑. 탄약에 강철을 사용합니다. 매우 높은 데미지를 입힐 수 있지만 발사 속도는 낮습니다.\n사용 범위에 따라 적의 공격 범위 밖에서 공격할 스 있습니다. -block.mortarturret.name = 플랭크 포탑 -block.mortarturret.fulldescription = 고급 저 정확도 스플래쉬 데미지 포탑. 탄약에 석탄을 사용합니다. 파편으로 폭발하는 총알을 사용하고. 많은 적군에게 유용합니다. -block.laserturret.name = 레이저 포탑 -block.laserturret.fulldescription = 고급 단일 대상 공격 포탑. 전력을 사용합니다. 중간 크기의 사거리를 가지고 있고 절대 빗나가지 않습니다. -block.waveturret.name = 테슬라 포탑 -block.waveturret.fulldescription = 고급 다중 타겟 포탑. 전력을 사용합니다. 중간크기의 사거리를 가지고 있으며. 절대 빗나가지 않으므로 걱정할 염려가 없습니다.\n데미지는 거의 없지만 연속공격으로 여러 명의 적들을 동시에 공격 할 수 있습니다. -block.plasmaturret.name = 플라즈마 포탑 -block.plasmaturret.fulldescription = 포탑의 최고급 버전. 석탄을 탄약으로 사용합니다.\n엄청나게 높은 데미지와 근거리와 중거리 사이 정도의 사거리를 가지고 있습니다. -block.chainturret.name = 체인 포탑 -block.chainturret.fulldescription = 궁극에 고속 포탑. 우라늄을 탄약으로 사용합니다. 높은 발사속도에 적당한 사정거리를 가지고 있고 대형 알을 발사합니다.\n2x2 크기이고, 매우 아픕니다. -block.titancannon.name = 타이탄 캐논 -block.titancannon.fulldescription = 궁극의 장거리 포탑. 우라늄을 탄약으로 사용합니다. 적당한 발사속도에 긴 사정거리를 가지고 있고 3x3 크기이며, 매우 아픕니다 -block.playerspawn.name = 플레이어 스폰 지점 -block.enemyspawn.name = 적 스폰 지점 +block.sorter.description = 아이템을 넣어서 필터에 설정된 아이템일 경우 바로 앞으로 통과하며, 그렇지 않을 경우 옆으로 통과합니다. +block.overflow-gate.name = 오버플로 게이트 +block.overflow-gate.description = 정면으로 가는 자원이 막히면 옆으로 출력하고, 그렇지 않으면 계속 정면으로 출력합니다. +block.silicon-smelter.name = 실리콘 제련소 +block.phase-weaver.name = 상직물 합성기 +block.pulverizer.name = 분쇄기 +block.cryofluidmixer.name = 냉각수 제조기 +block.melter.name = 융해기 +block.incinerator.name = 소각로 +block.spore-press.name = 포자 압축기 +block.separator.name = 셉터 +block.coal-centrifuge.name = 석탄 원심분리기 +block.power-node.name = 전력 노드 +block.power-node-large.name = 대형 전력 노드 +block.surge-tower.name = 서지 합금 타워 +block.battery.name = 배터리 +block.battery-large.name = 대형 배터리 +block.combustion-generator.name = 화력 발전기 +block.turbine-generator.name = 터빈 발전기 +block.differential-generator.name = 차동 발전기 +block.impact-reactor.name = 핵융합로 +block.mechanical-drill.name = 기계식 드릴 +block.pneumatic-drill.name = 공기 드릴 +block.laser-drill.name = 레이저 드릴 +block.water-extractor.name = 물 추출기 +block.cultivator.name = 온실 +block.dart-mech-pad.name = 알파 기체 패드 +block.delta-mech-pad.name = 델타 기체 패드 +block.javelin-ship-pad.name = 재블린 비행선 패드 +block.trident-ship-pad.name = 삼지창 비행선 패드 +block.glaive-ship-pad.name = 글레브 비행선 패드 +block.omega-mech-pad.name = 오메가 기체 패드 +block.tau-mech-pad.name = 타우 기체 패드 +block.conduit.name = 파이프 +block.mechanical-pump.name = 기계식 펌프 +block.item-source.name = 아이템 소스 +block.item-void.name = 아이템 삭제 장치 +block.liquid-source.name = 무한 액체공급 장치 +block.power-void.name = 방전장치 +block.power-source.name = 무한 전력공급 장치 +block.unloader.name = 언로더 +block.vault.name = 창고 +block.wave.name = 파도 +block.swarmer.name = 스웜 +block.salvo.name = 살보 +block.ripple.name = 립플 +block.phase-conveyor.name = 위상 컨베이어 +block.bridge-conveyor.name = 터널 컨베이어 +block.plastanium-compressor.name = 플라스터늄 압축기 +block.pyratite-mixer.name = 피라타이트 혼합기 +block.blast-mixer.name = 폭발물 혼합기 +block.solar-panel.name = 태양 전지판 +block.solar-panel-large.name = 대형 태양 전지판 +block.oil-extractor.name = 석유 추출기 +block.spirit-factory.name = 스피릿 드론 공장 +block.phantom-factory.name = 팬텀 드론 공장 +block.wraith-factory.name = 유령 전투기 공장 +block.ghoul-factory.name = 구울 폭격기 공장 +block.dagger-factory.name = 디거 기체 공장 +block.crawler-factory.name = 크롤러 기체 공장 +block.titan-factory.name = 타이탄 기체 공장 +block.fortress-factory.name = 포트리스 기체 공장 +block.revenant-factory.name = 레비넌트 전투기 공장 +block.repair-point.name = 수리 지점 +block.pulse-conduit.name = 퓨즈 파이프 +block.phase-conduit.name = 상직물 파이프 +block.liquid-router.name = 액체 분배기 +block.liquid-tank.name = 물탱크 +block.liquid-junction.name = 액체 교차기 +block.bridge-conduit.name = 다리 파이프 +block.rotary-pump.name = 동력 펌프 +block.thorium-reactor.name = 토륨 원자로 +block.mass-driver.name = 매스 드라이버 +block.blast-drill.name = 압축 공기분사 드릴 +block.thermal-pump.name = 화력 펌프 +block.thermal-generator.name = 열발전기 +block.alloy-smelter.name = 서지 합금 제련소 +block.mender.name = 멘더 +block.mend-projector.name = 수리 프로젝터 +block.surge-wall.name = 서지 합금벽 +block.surge-wall-large.name = 큰 서지 합금벽 +block.cyclone.name = 사이클론 +block.fuse.name = 퓨즈 +block.shock-mine.name = 전격 지뢰 +block.overdrive-projector.name = 오버드라이브 프로젝터 +block.force-projector.name = 보호막 프로젝터 +block.arc.name = 아크 +block.rtg-generator.name = RTG 발전기 +block.spectre.name = 스펙터 +block.meltdown.name = 멜트다운 +block.container.name = 컨테이너 +block.launch-pad.name = 발사대 +block.launch-pad.description = 출격할 필요 없이 아이템을 수송시킵시다. +block.launch-pad-large.name = 큰 출격 패드 +team.blue.name = 블루팀 +team.red.name = 레드팀 +team.orange.name = 오렌지팀 +team.none.name = 공기팀 +team.green.name = 그린팀 +team.purple.name = 보라색팀 +unit.spirit.name = 스피릿 드론 +unit.spirit.description = 기본 드론 유닛. 기본적으로 코어에서 1개가 스폰됩니다.\n자동으로 채광하며 아이템을 수집하고, 블록을 수리합니다. +unit.phantom.name = 팬텀 드론 +unit.phantom.description = 첨단 드론 유닛.\n광석을 자동으로 채광하며, 아이템을 수집하고 블록을 수리합니다. 일반 드론보다 훨씬 효과적입니다. +unit.dagger.name = 디거 +unit.dagger.description = 기본 지상 유닛입니다.\n플레이어 기체처럼 드론을 소환하지는 않습니다. +unit.crawler.name = 크롤러 +unit.titan.name = 타이탄 +unit.titan.description = 고급 지상 유닛입니다.\n원거리 총 대신에 근접 화염 방사기를 가지고 있으며, 지상과 공중 둘다 공격할 수 있습니다. +unit.ghoul.name = 구울 폭격기 +unit.ghoul.description = 무겁고 튼튼한 지상 폭격기 입니다.\n주로 적 건물로 이동하여 엄청난 폭격을 가합니다. +unit.wraith.name = 유령 전투기 +unit.wraith.description = 적 핵심 건물 및 유닛을 집중적으로 공격하는 방식을 사용하는 전투기 입니다. +unit.fortress.name = 포트리스 +unit.fortress.description = 중포 지상 유닛.\n높은 공격력을 가진 총과 높은 체력을 가지고 있습니다. +unit.revenant.name = 레비넌트 +unit.eruptor.name = 이럽터 +unit.chaos-array.name = 카오스 배열 +unit.eradicator.name = 짭멸 +unit.lich.name = 리치 +unit.reaper.name = 사신 +tutorial.begin = 플레이어의 주요 목표는 [LIGHT_GRAY]적군[]을 제거하는 것입니다.\n\n이 게임은 [accent]구리를 채광[]하는 것으로 시작합니다.\n이것을 하기 위해 플레이어의 중심부 근처에 있는 구리 광맥을 누르세요. +tutorial.drill = 수동으로 채광하는 것은 효율이 낮습니다.\n[accent]드릴[]은 자동으로 채광 작업을 합니다.\n구리 광맥에 표시된 영역에 드릴을 하나를 놓으세요. +tutorial.conveyor = [accent]컨베이어[]를 사용하여 아이템을 코어로 운반합니다.\n드릴에서 코어까지 컨베이어 라인을 만드세요. +tutorial.morecopper = 더 많은 구리가 필요합니다.\n\n수동으로 채광하거나, 드릴을 더 설치하세요. +tutorial.turret = 방어 구조물은 [LIGHT_GRAY]적[]을 물리치기 위해 반드시 필요합니다.\n기지 근처에 듀오 터렛을 설치하세요. +tutorial.drillturret = 듀오 터렛이 작동하기 위해서는[accent] 구리 탄약 []을 필요로 합니다.\n터렛 옆에 드릴을 설치하여 구리를 공급하세요. +tutorial.waves = [LIGHT_GRAY]적[]이 접근합니다.\n\n2단계 동안 코어를 보호하고 더 많은 터렛을 만드세요. +tutorial.lead = 더 많은 광석을 이용할 수 있습니다. [accent]납[]을 찾아 탐색하세요.\n\n아이템을 코어로 전송할려면 플레이어 기체 또는 비행기에서 코어로 드래그 하세요. +tutorial.smelter = 구리와 납은 약한 금속입니다.\n[accent]고밀도 합금[]은 제련소에서 만들 수 있습니다.\n\n하나 만드세요. +tutorial.densealloy = 이 제련소는 이제 고밀도 합금을 생산할 것입니다.\n몇개 더 생산하세요.\n필요한 경우 더 만드세요. +tutorial.siliconsmelter = 이제 이코어는 채굴과 수리하기 위한[accent] 스피릿 드론[]을 생성 할 것 입니다.\n\n[accent]실리콘[]을 사용해 다른 유닛을 생성하기 위한 공장을 만들 수 있습니다.\n실리콘 제련기를 제작하세요! +tutorial.silicondrill = 실리콘을 제작하려면[accent] 석탄[] 과[accent] 모래[]가 필요합니다.\n드릴을 먼저 건설해보는건 어떤가요? +tutorial.generator = 이 건물은 [LIGHT_YELLOW]전력[]이 필요합니다.\n[accent] 석탄 발전기[]를 건설하세요. +tutorial.generatordrill = [accent] 석탄 발전기[]는 연료가 필요합니다.\n[accent] 석탄[]을 드릴로 채굴해서 연료를 체워주세요. +tutorial.node = 전력은 송신해줄 노드가 필요합니다.\n[accent] 전력 노드[]를 석탄 등등 발전기 옆에 설치해서 생산된 전기를 다른곳으로 송신합시다. +tutorial.nodelink = 전력은 전력 블록과 발전기에 연결하거나, 연결된 전력 노드를 통해 전송이 가능합니다. \n\n전력 노드를 누르고 발전기와 실리콘 제련기를 선택하여 전원을 연결합시다. +tutorial.silicon = 실리콘이 생산되고 있습니다.\n\n생산 시스템의 개선을 권고 드립니다. +tutorial.daggerfactory = 이[accent] 디거 기체 공장[]은\n\n공격하는 기체를 생산하기 위해 사용됩니다. +tutorial.router = 공장을 작동시키기 위해 자원이 필요합니다.\n컨베이어에 운반되고 있는 자원을 분할할 분배기를 만드세요. +tutorial.dagger = 전력 노드를 공장에 연결하세요.\n일단 요구 사항이 충족되면 기체 생산을 시작합니다.\n\n필요에 따라 드릴 및 발전기, 컨베이어를 더 많이 만들 수 있습니다. +tutorial.battle = [LIGHT_GRAY]적[]의 코어가 드러났습니다.\n당신의 부대와 디거를 사용하여 파괴하세요. +block.copper-wall.description = 싸구려 방어블록.\n처음에 몇번 웨이브에서 건물과 터렛을 보호하는 데 유용함. +block.copper-wall-large.description = 싸구려 방어블록.\n처음에 몇번 웨이브에서 건물과 터렛을 보호하는 데 유용함.\n4개를 합친 블록입니다. +block.thorium-wall.description = 강력한 방어블록.\n적 공격으로부터 좋은 보호를 할 수 있습니다. +block.thorium-wall-large.description = 강력한 방어블록.\n적 공격으로부터 좋은 보호를 할 수 있습니다.\n4개를 합친 블록입니다. +block.phase-wall.description = 토륨 벽만큼 강하지 않지만 벽을 향해 날아오는 총알이 너무 강력하지 않으면 총알을 튕겨냅니다. +block.phase-wall-large.description = 토륨 벽만큼 강하지 않지만 벽을 향해 날아오는 총알이 너무 강력하지 않으면 총알을 튕겨냅니다.\n4개를 합친 블록입니다. +block.surge-wall.description = 최강 방어블록.\n공격을 받으면 낮은 확률로 공격자에게 전격 공격을 합니다. +block.surge-wall-large.description = 최강 방어블록.\n공격을 받으면 낮은 확률로 공격자에게 전격 공격을 합니다.\n4개를 합친 블록입니다. +block.door.description = 눌러서 열고 닫을 수 있는 작은 문.\n만약 문이 열리면, 적들은 총을 쏘며 문을 통과할 수 있습니다. +block.door-large.description = 누르는 것으로 여닫을 수 있는 큰 문.\n만약 문이 열리면, 적들은 총을 쏘며 문을 통과할 수 있습니다.\n4개를 합친 블록입니다. +block.mend-projector.description = 주변 건물을 주기적으로 치료합니다. +block.overdrive-projector.description = 드릴과 컨베이어와 같은 인근 건물의 속도를 높여줍니다. +block.force-projector.description = 총알에게서 내부의 건물과 유닛을 보호하면서 그 주위에 육각형 보호막을 만듭니다. +block.shock-mine.description = 지뢰를 밟는 적에게 피해를 줍니다. 적에게는 거의 보이지 않습니다. +block.duo.description = 작고 싼 터렛. +block.scatter.description = 중형 대공 터렛. 납이나 고철 덩어리를 적에게 쏩니다. +block.arc.description = 적을 향해 무작위 각도로 전기를 쏘는 작은 터렛. +block.hail.description = 작은 포병 터렛. +block.lancer.description = 충전된 전기빔을 쏘는 중형 터렛. +block.wave.description = 액체를 뿜는 중간 크기의 속화 터렛. +block.salvo.description = 일제히 사격을 하는 중형 터렛. +block.swarmer.description = 유도 미사일을 발사하는 중형 터렛. +block.ripple.description = 여러 발의 사격을 동시에 하는 대형 포대. +block.cyclone.description = 대형 초고속 사격 터렛. +block.fuse.description = 강력한 단거리 빔을 쏘는 대형 터렛. +block.spectre.description = 한 번에 두 발의 강력한 총알을 쏘는 대형 터렛. +block.meltdown.description = 강력한 장거리 빔을 쏘는 대형 터렛. +block.conveyor.description = 기본 아이템 수송 블록. 아이템을 앞으로 이동시켜 자동으로 터렛이나 건물에 넣어줍니다. 회전식. +block.titanium-conveyor.description = 고급 아이템 운송 블록. 표준 컨베이어보다 아이템을 더 빨리 이동시킨다. +block.phase-conveyor.description = 고급품 수송 블록. 여러 타일을 통해 아이템을 연결된 위상 컨베이어로 텔레포트하기 위해 전력을 사용합니다. +block.junction.description = 2개의 컨베이어 벨트를 교차시키는 다리 역할을 합니다. 서로 다른 재료를 다른 장소로 운반하는 두 개의 다른 컨베이어의 상황에서 유용합니다. +block.mass-driver.description = 최강 아이템 수송 블록. 몇 가지 아이템을 모아 장거리에서 또 다른 매스 드라이버에게 발사합니다. +block.silicon-smelter.description = 고순도 석탄으로 모래를 줄여 실리콘을 생산합니다. +block.plastanium-compressor.description = 석유와 티타늄으로 플라스타늄을 생산합니다. +block.phase-weaver.description = 방사능 토륨과 많은 량의 모래에서 상직물을 생산합니다. +block.alloy-smelter.description = 티타늄, 납, 실리콘, 구리로부터 서지 합금을 생산합니다. +block.pulverizer.description = 모래로 을 부숩니다. 천연 모래가 부족할 때 유용합니다. +block.pyratite-mixer.description = 석탄, 납, 모래를 가연성이 높은 피라타이트로 만듭니다. +block.blast-mixer.description = 기름을 사용하여 피라타이트를 인화성은 떨어지지만 폭발성은 높은 폭발성 화합물로 변환시킵니다. +block.cryofluidmixer.description = 물과 티타늄을 냉각에 훨씬 더 효과적인 냉동액으로 결합시킵니다. +block.melter.description = 고철을 녹여 더 많은 처리나 터렛의 사용을 위해 광재에 녹입니다. +block.incinerator.description = 불필요한 아이템을 소각시켜 줄 수 있는 건물입니다. +block.spore-press.description = 포자 포드를 석유로 바꿔줍니다. +block.separator.description = 돌을 분해하여 각종 자원으로 재활용 할 수 있게 해 주는 건물입니다. +block.power-node.description = 전원을 연결된 노드로 전송합니다. 최대 4개의 동력원, 싱크 또는 노드를 연결할 수 있습니다. 노드는 인접한 블록으로부터 전원을 공급하거나 공급받을 수 있습니다. +block.power-node-large.description = 생성된 전력를 다른 건물로 전달하기 위한 건물이며, 일반 노드보다 더 많은 전력을 이동시킬 수 있습니다. +block.battery.description = 전력 생산량에 여유가 있을경우, 생산된 잉여 전력을 여기에 저장합니다. +block.battery-large.description = 일반 배터리보다 훨씬 많은 량의 전력을 저장합니다. +block.combustion-generator.description = 기름이나 인화성 물질을 태워 전력을 발생시킵니다. +block.turbine-generator.description = 화력 발전기보다 효율적이지만, 추가 액체가 필요합니다. +block.thermal-generator.description = 뜨거운 위치에 건설하면 전력을 생산합니다. +block.solar-panel.description = 태양으로부터 소량의 전력을 공급합니다. +block.solar-panel-large.description = 일반 태양 전지판보다 훨씬 나은 전력을 공급하지만, 건축비도 훨씬 비쌉니다. +block.thorium-reactor.description = 고방사능 토륨으로부터 막대한 양의 전력을 발생시킵니다. 지속적인 냉각이 필요하며 냉각제의 양이 부족하면 크게 폭발합니다. 전력 출력은 최대 용량에서 기본 전력을 발생시키는 완전성에 따라 결정됩니다. +block.rtg-generator.description = 냉각은 필요 없지만 토륨 원자로에 비해 전력을 적게 공급하는 방사성 동위원소 열전 발생기. +block.unloader.description = 컨테이너, 금고 또는 코어에서 인접한 블록으로 아이템을 출하합니다. 출하시킬 아이템의 종류는 언로더를 눌러 지정할 수 있습니다. +block.container.description = 각종 소량의 자원을 저장할 수 있습니다.[LIGHT_GRAY]언로더[]를 사용하여 컨테이너에서 물건을 회수할 수 있습니다. +block.vault.description = 각종 대량의 자원을 저장할 수 있습니다.[LIGHT_GRAY]언로더[]를 사용하여 금고에서 물건을 회수할 수 있습니다. +block.mechanical-drill.description = 싸구려 드릴. 적절한 타일 위에 놓였을때 매우 느린 속도로 계속 출력합니다. +block.pneumatic-drill.description = 기압을 이용하여 보다 빠르고 단단한 물질을 채광할 수 있는 향상된 드릴. +block.laser-drill.description = 토륨을 채광할 수 있는 최고급 드릴입니다. 전력과 물을 공급하여 빠른 속도로 채광할 수 있습니다. +block.blast-drill.description = 최상위 드릴입니다. 많은량의 전력이 필요합니다. +block.water-extractor.description = 땅에서 물을 추출합니다. 근처에 호수가 없을 때 사용하세요. +block.cultivator.description = 소량의 포자를 산업용으로 준비된 포자로 배양하는 건물입니다. +block.oil-extractor.description = 모래에서 기름을 추출하기 위해 대량을 전력을 사용합니다. 근처에 직접적인 석유 공급원이 없을때 사용하세요. +block.trident-ship-pad.description = 지금 타고있는 배를 떠나 잘 무장된 중폭격기로 갈아타세요.\n누르거나 클릭하여 이 기체로 바꿉니다. +block.javelin-ship-pad.description = 지금 타고있는 배를 떠나 전격 무기와 강력하고 빠른 요격체로 변신합니다.\n누르거나 클릭하여 이 기체로 바꿉니다. +block.glaive-ship-pad.description = 지금 타고있는 배를 떠나 크고 잘 무장된 기체로 갈아타세요.\n누르거나 클릭하여 이 기체로 바꿉니다. +block.tau-mech-pad.description = 지금 타고있는 배를 떠나 아군 건물이나 유닛을 치료할 수 있는 지원형 기체로 갈아타세요.\n누르거나 클릭하여 이 기체로 바꿉니다. +block.delta-mech-pad.description = 지금 타고있는 배를 떠나 뺑소니 공격을 위해 만들어진 빠르고 가벼운 기체로 갈아타세요.\n누르거나 클릭하여 이 기체로 바꿉니다. +block.omega-mech-pad.description = 지금 타고있는 배를 떠나 최전방 공격을 위해 만든 부피가 크고 잘 무장된 기체로 갈아타세요.\n누르거나 클릭하여 이 기체로 바꿉니다. +block.spirit-factory.description = 광석을 채굴하고 블록을 수리하는 경량 드론을 생산합니다. +block.phantom-factory.description = 스피릿 드론보다 훨씬 효과적인 첨단 드론 유닛을 생산합니다. +block.wraith-factory.description = 빠른 뺑소니 요격기 유닛을 생산합니다. +block.ghoul-factory.description = 중탄두 폭격기를 생산합니다. +block.dagger-factory.description = 기본 지상 유닛을 생산합니다. +block.titan-factory.description = 첨단 장갑 지상부대를 생산합니다. +block.fortress-factory.description = 중대포 지상부대를 생산합니다. +block.revenant-factory.description = 중량의 레이저 포대를 가진 공중부대를 생산합니다. +block.repair-point.description = 주변에서 가장 가까운 손상된 유닛을 지속적으로 치료합니다. +block.conduit.description = 일반 파이프.\n액체가 지나갈 수 있도록 해 줍니다. +block.pulse-conduit.description = 고급 액체 수송블록. 일반 파이프보다 액체 운송 속도가 빠릅니다. +block.phase-conduit.description = 고급 액체 수송블록. 물을 먼거리로 순간이동 시켜 주는 장치입니다. +block.liquid-router.description = 물펌프를 다른 방향으로 분배할 수 있게 하는 블럭입니다. +block.liquid-tank.description = 액체 종류를 저장할 수 있는 물탱크 입니다. +block.liquid-junction.description = 물펌프와 다른 물펌프를 서로 교차시키게 할 수 있는 블럭입니다. +block.bridge-conduit.description = 다리와 다리 사이를 연결하여 액체가 지나갈 수 있게 해 줍니다.\n주로 다리 사이에 지나갈 수 없는 장애물이 있을 때 사용합니다. +block.mechanical-pump.description = 느린 속도로 물을 퍼올리는 펌프입니다. 대신 전력이 필요없습니다. +block.rotary-pump.description = 일반 물 펌프보다 더 빠른 속도로 물을 끌어올릴 수 있는 펌프입니다. +block.thermal-pump.description = 최고의 펌프. +block.router.description = 한 방향에서 아이템을 널은 후 최대 3개의 다른 방향으로 균등하게 출력합니다. 재료를 한곳에서 여러 개의 목표로 분할하는 데 유용합니다. +block.distributor.description = 아이템을 최대 7개의 다른 방향으로 똑같이 분할하는 고급 분배기. +block.bridge-conveyor.description = 고급 자원 수송 블록.\n지형이나 건물을 넘어 최대 3개 타일을 건너뛰고 자원을 운송할 수 있습니다. +block.item-source.description = 자원을 선택하면 그 자원이 무한하게 생성되는 블록입니다. 샌드박스 전용. +block.liquid-source.description = 무한한 액체를 출력해냅니다. 샌드박스 전용. +block.item-void.description = 아이템을 사라지게 만듭니다. 샌드박스 전용. +block.power-source.description = 무한한 전력을 공급해주는 블록입니다. 샌드박스 전용. +block.power-void.description = 설정된 아이템을 계속해서 출력하는 블록입니다. +liquid.water.description = 일반적으로 냉각기와 폐기물 처리에 사용된다. +liquid.oil.description = 연소, 폭발 또는 냉각제로 사용될 수 있다. +liquid.cryofluid.description = 건물을 냉각시키는데 가장 효과적인 액체. diff --git a/core/assets/bundles/bundle_nl.properties b/core/assets/bundles/bundle_nl.properties new file mode 100644 index 0000000000..0d40e6a7a1 --- /dev/null +++ b/core/assets/bundles/bundle_nl.properties @@ -0,0 +1,947 @@ +credits.text = Gemaakt door [ROYAL]Anuken[] - [SKY]anukendev@gmail.com[] +credits = Credits +contributors = Vertalers en Medewerkers +discord = Word lid van de Mindustry Discord! +link.discord.description = De officiële Mindustry discord chatroom +link.github.description = Game broncode +link.dev-builds.description = Onstabiele ontwikkeling builds +link.trello.description = Officiële trello-bord voor geplande functies +link.itch.io.description = itch.io pagina met pc-downloads en webversie +link.google-play.description = Google Play store vermelding +link.wiki.description = Officiële Mindustry wiki +linkfail = Kan link niet openen!\nDe URL is gekopieerd naar je klembord +screenshot = Schermafbeeling opgeslagen in {0} +screenshot.invalid = Map too large, potentially not enough memory for screenshot. +gameover = Game Over +gameover.pvp = het[accent] {0}[] team heeft gewonnen! +highscore = [accent]Nieuw topscore! +stat.wave = Waves Verslagen:[accent] {0} +stat.enemiesDestroyed = Vijanden Vernietigd:[accent] {0} +stat.built = Gebouwen Gebouwd:[accent] {0} +stat.destroyed = Gebouwen Vernietigd:[accent] {0} +stat.deconstructed = Gebouwen Gesloopt:[accent] {0} +stat.delivered = Middelen Gelanceerd: +stat.rank = Eindrang: [accent]{0} +placeline = You have selected a block.\nYou can[accent] place in a line[] by[accent] holding down your finger for a few seconds[] and dragging in a direction.\nTry it. +removearea = You have selected removal mode.\nYou can[accent] remove blocks in a rectangle[] by[accent] holding down your finger for a few seconds[] and dragging.\nTry it. +launcheditems = [accent]Launched Items +map.delete = Weet je zeker dat je de map wilt verwijderen? "[accent]{0}[]"? +level.highscore = Topscore: [accent]{0} +level.select = Selecteer Level +level.mode = Spelmodus: +showagain = Don't show again next session +coreattack = < Core is under attack! > +nearpoint = [[ [scarlet]LEAVE DROP POINT IMMEDIATELY[] ]\nannihilation imminent +outofbounds = [[ OUT OF BOUNDS ]\n[]self-destruct in {0} +database = Core Database +savegame = Save Game +loadgame = Load Game +joingame = Join Game +addplayers = Add/Remove Players +customgame = Custom Game +newgame = New Game +none = +minimap = Minimap +close = Close +quit = Quit +maps = Maps +continue = Continue +maps.none = [LIGHT_GRAY]No maps found! +about.button = About +name = Name: +noname = Pick a[accent] player name[] first. +filename = File Name: +unlocked = New content unlocked! +completed = [accent]Completed +techtree = Tech Tree +research.list = [LIGHT_GRAY]Research: +research = Research +researched = [LIGHT_GRAY]{0} researched. +players = {0} players online +players.single = {0} player online +server.closing = [accent]Closing server... +server.kicked.kick = You have been kicked from the server! +server.kicked.serverClose = Server closed. +server.kicked.clientOutdated = Outdated client! Update your game! +server.kicked.serverOutdated = Outdated server! Ask the host to update! +server.kicked.banned = You are banned on this server. +server.kicked.recentKick = You have been kicked recently.\nWait before connecting again. +server.kicked.nameInUse = There is someone with that name\nalready on this server. +server.kicked.nameEmpty = Your chosen name is invalid. +server.kicked.idInUse = You are already on this server! Connecting with two accounts is not permitted. +server.kicked.customClient = This server does not support custom builds. Download an official version. +server.kicked.gameover = Game over! +host.info = The [accent]host[] button hosts a server on port [scarlet]6567[]. \nAnybody on the same [LIGHT_GRAY]wifi or local network[] should be able to see your server in their server list.\n\nIf you want people to be able to connect from anywhere by IP, [accent]port forwarding[] is required.\n\n[LIGHT_GRAY]Note: If someone is experiencing trouble connecting to your LAN game, make sure you have allowed Mindustry access to your local network in your firewall settings. +join.info = Here, you can enter a [accent]server IP[] to connect to, or discover [accent]local network[] servers to connect to.\nBoth LAN and WAN multiplayer is supported.\n\n[LIGHT_GRAY]Note: There is no automatic global server list; if you want to connect to someone by IP, you would need to ask the host for their IP. +hostserver = Host Game +hostserver.mobile = Host\nGame +host = Host +hosting = [accent]Opening server... +hosts.refresh = Refresh +hosts.discovering = Discovering LAN games +server.refreshing = Refreshing server +hosts.none = [lightgray]No local games found! +host.invalid = [scarlet]Can't connect to host. +trace = Trace Player +trace.playername = Player name: [accent]{0} +trace.ip = IP: [accent]{0} +trace.id = Unique ID: [accent]{0} +trace.mobile = Mobile Client: [accent]{0} +trace.modclient = Custom Client: [accent]{0} +invalidid = Invalid client ID! Submit a bug report. +server.bans = Bans +server.bans.none = No banned players found! +server.admins = Admins +server.admins.none = No admins found! +server.add = Add Server +server.delete = Are you sure you want to delete this server? +server.hostname = Host: {0} +server.edit = Edit Server +server.outdated = [crimson]Outdated Server![] +server.outdated.client = [crimson]Outdated Client![] +server.version = [lightgray]Version: {0} {1} +server.custombuild = [yellow]Custom Build +confirmban = Are you sure you want to ban this player? +confirmkick = Are you sure you want to kick this player? +confirmunban = Are you sure you want to unban this player? +confirmadmin = Are you sure you want to make this player an admin? +confirmunadmin = Are you sure you want to remove admin status from this player? +joingame.title = Join Game +joingame.ip = Address: +disconnect = Disconnected. +disconnect.data = Failed to load world data! +connecting = [accent]Connecting... +connecting.data = [accent]Loading world data... +server.port = Port: +server.addressinuse = Address already in use! +server.invalidport = Invalid port number! +server.error = [crimson]Error hosting server: [accent]{0} +save.old = This save is for an older version of the game, and can no longer be used.\n\n[LIGHT_GRAY]Save backwards compatibility will be implemented in the full 4.0 release. +save.new = New Save +save.overwrite = Are you sure you want to overwrite\nthis save slot? +overwrite = Overwrite +save.none = No saves found! +saveload = [accent]Saving... +savefail = Failed to save game! +save.delete.confirm = Are you sure you want to delete this save? +save.delete = Delete +save.export = Export Save +save.import.invalid = [accent]This save is invalid! +save.import.fail = [crimson]Failed to import save: [accent]{0} +save.export.fail = [crimson]Failed to export save: [accent]{0} +save.import = Import Save +save.newslot = Save name: +save.rename = Rename +save.rename.text = New name: +selectslot = Select a save. +slot = [accent]Slot {0} +save.corrupted = [accent]Save file corrupted or invalid!\nIf you have just updated your game, this is probably a change in the save format and [scarlet]not[] a bug. +empty = +on = On +off = Off +save.autosave = Autosave: {0} +save.map = Map: {0} +save.wave = Wave {0} +save.difficulty = Difficulty: {0} +save.date = Last Saved: {0} +save.playtime = Playtime: {0} +warning = Warning. +confirm = Confirm +delete = Delete +ok = OK +open = Open +customize = Customize +cancel = Cancel +openlink = Open Link +copylink = Copy Link +back = Back +quit.confirm = Are you sure you want to quit? +changelog.title = Changelog +changelog.loading = Getting changelog... +changelog.error.android = [accent]Note that the changelog sometimes does not work on Android 4.4 and below!\nThis is due to an internal Android bug. +changelog.error.ios = [accent]The changelog is currently not supported in iOS. +changelog.error = [scarlet]Error getting changelog!\nCheck your internet connection. +changelog.current = [yellow][[Current version] +changelog.latest = [accent][[Latest version] +loading = [accent]Loading... +saving = [accent]Saving... +wave = [accent]Wave {0} +wave.waiting = [LIGHT_GRAY]Wave in {0} +wave.waveInProgress = [LIGHT_GRAY]Wave in progress +waiting = [LIGHT_GRAY]Waiting... +waiting.players = Waiting for players... +wave.enemies = [LIGHT_GRAY]{0} Enemies Remaining +wave.enemy = [LIGHT_GRAY]{0} Enemy Remaining +loadimage = Load Image +saveimage = Save Image +unknown = Unknown +custom = Custom +builtin = Built-In +map.delete.confirm = Are you sure you want to delete this map? This action cannot be undone! +map.random = [accent]Random Map +map.nospawn = This map does not have any cores for the player to spawn in! Add a[ROYAL] blue[] core to this map in the editor. +map.nospawn.pvp = This map does not have any enemy cores for player to spawn into! Add[SCARLET] red[] cores to this map in the editor. +map.nospawn.attack = This map does not have any enemy cores for player to attack! Add[SCARLET] red[] cores to this map in the editor. +map.invalid = Error loading map: corrupted or invalid map file. +editor.brush = Brush +editor.openin = Open In Editor +editor.oregen = Ore Generation +editor.oregen.info = Ore Generation: +editor.mapinfo = Map Info +editor.author = Author: +editor.description = Description: +editor.waves = Waves: +editor.rules = Rules: +editor.ingame = Edit In-Game +waves.title = Waves +waves.remove = Remove +waves.never = +waves.every = every +waves.waves = wave(s) +waves.perspawn = per spawn +waves.to = to +waves.boss = Boss +waves.preview = Preview +waves.edit = Edit... +waves.copy = Copy to Clipboard +waves.load = Load from Clipboard +waves.invalid = Invalid waves in clipboard. +waves.copied = Waves copied. +editor.default = [LIGHT_GRAY] +edit = Edit... +editor.name = Name: +editor.spawn = Spawn Unit +editor.removeunit = Remove Unit +editor.teams = Teams +editor.elevation = Elevation +editor.errorload = Error loading file:\n[accent]{0} +editor.errorsave = Error saving file:\n[accent]{0} +editor.errorimage = That's an image, not a map. Don't go around changing extensions expecting it to work.\n\nIf you want to import a legacy map, use the 'import legacy map' button in the editor. +editor.errorlegacy = This map is too old, and uses a legacy map format that is no longer supported. +editor.errorheader = This map file is either not valid or corrupt. +editor.errorname = Map has no name defined. +editor.update = Update +editor.randomize = Randomize +editor.apply = Apply +editor.generate = Generate +editor.resize = Resize +editor.loadmap = Load Map +editor.savemap = Save Map +editor.saved = Saved! +editor.save.noname = Your map does not have a name! Set one in the 'map info' menu. +editor.save.overwrite = Your map overwrites a built-in map! Pick a different name in the 'map info' menu. +editor.import.exists = [scarlet]Unable to import:[] a built-in map named '{0}' already exists! +editor.import = Import... +editor.importmap = Import Map +editor.importmap.description = Import an already existing map +editor.importfile = Import File +editor.importfile.description = Import an external map file +editor.importimage = Import Legacy Image +editor.importimage.description = Import an external map image file +editor.export = Export... +editor.exportfile = Export File +editor.exportfile.description = Export a map file +editor.exportimage = Export Terrain Image +editor.exportimage.description = Export a map image file +editor.loadimage = Import Terrain +editor.saveimage = Export Terrain +editor.unsaved = [scarlet]You have unsaved changes![]\nAre you sure you want to exit? +editor.resizemap = Resize Map +editor.mapname = Map Name: +editor.overwrite = [accent]Warning!\nThis overwrites an existing map. +editor.overwrite.confirm = [scarlet]Warning![] A map with this name already exists. Are you sure you want to overwrite it? +editor.selectmap = Select a map to load: +filters.empty = [LIGHT_GRAY]No filters! Add one with the button below. +filter.distort = Distort +filter.noise = Noise +filter.ore = Ore +filter.rivernoise = River Noise +filter.scatter = Scatter +filter.terrain = Terrain +filter.option.scale = Scale +filter.option.chance = Chance +filter.option.mag = Magnitude +filter.option.threshold = Threshold +filter.option.circle-scale = Circle Scale +filter.option.octaves = Octaves +filter.option.falloff = Falloff +filter.option.block = Block +filter.option.floor = Floor +filter.option.wall = Wall +filter.option.ore = Ore +filter.option.floor2 = Secondary Floor +filter.option.threshold2 = Secondary Threshold +filter.option.radius = Radius +filter.option.percentile = Percentile +width = Width: +height = Height: +menu = Menu +play = Play +load = Load +save = Save +fps = FPS: {0} +tps = TPS: {0} +ping = Ping: {0}ms +language.restart = Please restart your game for the language settings to take effect. +settings = Settings +tutorial = Tutorial +editor = Editor +mapeditor = Map Editor +donate = Donate +abandon = Abandon +abandon.text = This zone and all its resources will be lost to the enemy. +locked = Locked +complete = [LIGHT_GRAY]Complete: +zone.requirement = Wave {0} in zone {1} +resume = Resume Zone:\n[LIGHT_GRAY]{0} +bestwave = [LIGHT_GRAY]Best Wave: {0} +launch = < LAUNCH > +launch.title = Launch Successful +launch.next = [LIGHT_GRAY]next opportunity at wave {0} +launch.unable = [scarlet]Unable to LAUNCH.[] {0} Enemies. +launch.confirm = This will launch all resources in your core.\nYou will not be able to return to this base. +uncover = Uncover +configure = Configure Loadout +configure.locked = [LIGHT_GRAY]Unlock configuring loadout:\nWave {0}. +zone.unlocked = [LIGHT_GRAY]{0} unlocked. +zone.requirement.complete = Wave {0} reached:\n{1} zone requirements met. +zone.config.complete = Wave {0} reached:\nLoadout config unlocked. +zone.resources = Resources Detected: +add = Add... +boss.health = Boss Health +connectfail = [crimson]Failed to connect to server:\n\n[accent]{0} +error.unreachable = Server unreachable.\nIs the address spelled correctly? +error.invalidaddress = Invalid address. +error.timedout = Timed out!\nMake sure the host has port forwarding set up, and that the address is correct! +error.mismatch = Packet error:\npossible client/server version mismatch.\nMake sure you and the host have the latest version of Mindustry! +error.alreadyconnected = Already connected. +error.mapnotfound = Map file not found! +error.io = Network I/O error. +error.any = Unknown network error. +zone.groundZero.name = Ground Zero +zone.desertWastes.name = Desert Wastes +zone.craters.name = The Craters +zone.frozenForest.name = Frozen Forest +zone.ruinousShores.name = Ruinous Shores +zone.stainedMountains.name = Stained Mountains +zone.desolateRift.name = Desolate Rift +zone.nuclearComplex.name = Nuclear Production Complex +zone.overgrowth.name = Overgrowth +zone.tarFields.name = Tar Fields +settings.language = Language +settings.reset = Reset to Defaults +settings.rebind = Rebind +settings.controls = Controls +settings.game = Game +settings.sound = Sound +settings.graphics = Graphics +settings.cleardata = Clear Game Data... +settings.clear.confirm = Are you sure you want to clear this data?\nWhat is done cannot be undone! +settings.clearall.confirm = [scarlet]WARNING![]\nThis will clear all data, including saves, maps, unlocks and keybinds.\nOnce you press 'ok' the game will wipe all data and automatically exit. +settings.clearunlocks = Clear Unlocks +settings.clearall = Clear All +paused = [accent]< Paused > +yes = Yes +no = No +info.title = Info +error.title = [crimson]An error has occured +error.crashtitle = An error has occured +blocks.input = Input +blocks.output = Output +blocks.booster = Booster +block.unknown = [LIGHT_GRAY]??? +blocks.powercapacity = Power Capacity +blocks.powershot = Power/Shot +blocks.targetsair = Targets Air +blocks.targetsground = Targets Ground +blocks.itemsmoved = Move Speed +blocks.launchtime = Time Between Launches +blocks.shootrange = Range +blocks.size = Size +blocks.liquidcapacity = Liquid Capacity +blocks.powerrange = Power Range +blocks.poweruse = Power Use +blocks.powerdamage = Power/Damage +blocks.itemcapacity = Item Capacity +blocks.basepowergeneration = Base Power Generation +blocks.productiontime = Production Time +blocks.repairtime = Block Full Repair Time +blocks.speedincrease = Speed Increase +blocks.range = Range +blocks.drilltier = Drillables +blocks.drillspeed = Base Drill Speed +blocks.boosteffect = Boost Effect +blocks.maxunits = Max Active Units +blocks.health = Health +blocks.buildtime = Build Time +blocks.inaccuracy = Inaccuracy +blocks.shots = Shots +blocks.reload = Shots/Second +blocks.ammo = Ammo +bar.drillspeed = Drill Speed: {0}/s +bar.efficiency = Efficiency: {0}% +bar.powerbalance = Power: {0} +bar.poweramount = Power: {0} +bar.poweroutput = Power Output: {0} +bar.items = Items: {0} +bar.liquid = Liquid +bar.heat = Heat +bar.power = Power +bar.progress = Build Progress +bar.spawned = Units: {0}/{1} +bullet.damage = [stat]{0}[lightgray] dmg +bullet.splashdamage = [stat]{0}[lightgray] area dmg ~[stat] {1}[lightgray] tiles +bullet.incendiary = [stat]incendiary +bullet.homing = [stat]homing +bullet.shock = [stat]shock +bullet.frag = [stat]frag +bullet.knockback = [stat]{0}[lightgray] knockback +bullet.freezing = [stat]freezing +bullet.tarred = [stat]tarred +bullet.multiplier = [stat]{0}[lightgray]x ammo multiplier +bullet.reload = [stat]{0}[lightgray]x reload +unit.blocks = blocks +unit.powersecond = power units/second +unit.liquidsecond = liquid units/second +unit.itemssecond = items/second +unit.liquidunits = liquid units +unit.powerunits = power units +unit.degrees = degrees +unit.seconds = seconds +unit.persecond = /sec +unit.timesspeed = x speed +unit.percent = % +unit.items = items +category.general = General +category.power = Power +category.liquids = Liquids +category.items = Items +category.crafting = Crafting +category.shooting = Shooting +category.optional = Optional Enhancements +setting.landscape.name = Lock Landscape +setting.shadows.name = Shadows +setting.linear.name = Linear Filtering +setting.animatedwater.name = Animated Water +setting.animatedshields.name = Animated Shields +setting.antialias.name = Antialias[LIGHT_GRAY] (requires restart)[] +setting.indicators.name = Ally Indicators +setting.autotarget.name = Auto-Target +setting.fpscap.name = Max FPS +setting.fpscap.none = None +setting.fpscap.text = {0} FPS +setting.swapdiagonal.name = Always Diagonal Placement +setting.difficulty.training = training +setting.difficulty.easy = easy +setting.difficulty.normal = normal +setting.difficulty.hard = hard +setting.difficulty.insane = insane +setting.difficulty.name = Difficulty: +setting.screenshake.name = Screen Shake +setting.effects.name = Display Effects +setting.sensitivity.name = Controller Sensitivity +setting.saveinterval.name = Autosave Interval +setting.seconds = {0} Seconds +setting.fullscreen.name = Fullscreen +setting.borderlesswindow.name = Borderless Window[LIGHT_GRAY] (may require restart) +setting.fps.name = Show FPS +setting.vsync.name = VSync +setting.lasers.name = Show Power Lasers +setting.pixelate.name = Pixelate [LIGHT_GRAY](may decrease performance) +setting.minimap.name = Show Minimap +setting.musicvol.name = Music Volume +setting.mutemusic.name = Mute Music +setting.sfxvol.name = SFX Volume +setting.mutesound.name = Mute Sound +setting.crashreport.name = Send Anonymous Crash Reports +setting.chatopacity.name = Chat Opacity +setting.playerchat.name = Display In-Game Chat +keybind.title = Rebind Keys +category.general.name = General +category.view.name = View +category.multiplayer.name = Multiplayer +command.attack = Attack +command.retreat = Retreat +command.patrol = Patrol +keybind.gridMode.name = Block Select +keybind.gridModeShift.name = Category Select +keybind.press = Press a key... +keybind.press.axis = Press an axis or key... +keybind.screenshot.name = Map Screenshot +keybind.move_x.name = Move x +keybind.move_y.name = Move y +keybind.select.name = Select/Shoot +keybind.diagonal_placement.name = Diagonal Placement +keybind.pick.name = Pick Block +keybind.break_block.name = Break Block +keybind.deselect.name = Deselect +keybind.shoot.name = Shoot +keybind.zoom_hold.name = Zoom Hold +keybind.zoom.name = Zoom +keybind.menu.name = Menu +keybind.pause.name = Pause +keybind.minimap.name = Minimap +keybind.dash.name = Dash +keybind.chat.name = Chat +keybind.player_list.name = Player list +keybind.console.name = Console +keybind.rotate.name = Rotate +keybind.toggle_menus.name = Toggle menus +keybind.chat_history_prev.name = Chat history prev +keybind.chat_history_next.name = Chat history next +keybind.chat_scroll.name = Chat scroll +keybind.drop_unit.name = Drop Unit +keybind.zoom_minimap.name = Zoom minimap +mode.help.title = Description of modes +mode.survival.name = Survival +mode.survival.description = The normal mode. Limited resources and automatic incoming waves. +mode.sandbox.name = Sandbox +mode.sandbox.description = Infinite resources and no timer for waves. +mode.pvp.name = PvP +mode.pvp.description = Fight against other players locally. +mode.attack.name = Attack +mode.attack.description = No waves, with the goal to destroy the enemy base. +mode.custom = Custom Rules +rules.infiniteresources = Infinite Resources +rules.wavetimer = Wave Timer +rules.waves = Waves +rules.enemyCheat = Infinite AI Resources +rules.unitdrops = Unit Drops +rules.unitbuildspeedmultiplier = Unit Creation Speed Multiplier +rules.unithealthmultiplier = Unit Health Multiplier +rules.playerhealthmultiplier = Player Health Multiplier +rules.playerdamagemultiplier = Player Damage Multiplier +rules.unitdamagemultiplier = Unit Damage Multiplier +rules.enemycorebuildradius = Enemy Core No-Build Radius:[LIGHT_GRAY] (tiles) +rules.respawntime = Respawn Time:[LIGHT_GRAY] (sec) +rules.wavespacing = Wave Spacing:[LIGHT_GRAY] (sec) +rules.buildcostmultiplier = Build Cost Multiplier +rules.buildspeedmultiplier = Build Speed Multiplier +rules.waitForWaveToEnd = Waves wait for enemies +rules.dropzoneradius = Drop Zone Radius:[LIGHT_GRAY] (tiles) +rules.respawns = Max respawns per wave +rules.limitedRespawns = Limit Respawns +rules.title.waves = Waves +rules.title.respawns = Respawns +rules.title.resourcesbuilding = Resources & Building +rules.title.player = Players +rules.title.enemy = Enemies +rules.title.unit = Units +content.item.name = Items +content.liquid.name = Liquids +content.unit.name = Units +content.block.name = Blocks +content.mech.name = Mechs +item.copper.name = Copper +item.copper.description = A useful structure material. Used extensively in all types of blocks. +item.lead.name = Lead +item.lead.description = A basic starter material. Used extensively in electronics and liquid transportation blocks. +item.coal.name = Coal +item.coal.description = A common and readily available fuel. +item.graphite.name = Graphite +item.titanium.name = Titanium +item.titanium.description = A rare super-light metal used extensively in liquid transportation, drills and aircraft. +item.thorium.name = Thorium +item.thorium.description = A dense, radioactive metal used as structural support and nuclear fuel. +item.silicon.name = Silicon +item.silicon.description = An extremely useful semiconductor, with applications in solar panels and many complex electronics. +item.plastanium.name = Plastanium +item.plastanium.description = A light, ductile material used in advanced aircraft and fragmentation ammunition. +item.phase-fabric.name = Phase Fabric +item.phase-fabric.description = A near-weightless substance used in advanced electronics and self-repairing technology. +item.surge-alloy.name = Surge Alloy +item.surge-alloy.description = An advanced alloy with unique electrical properties. +item.spore-pod.name = Spore Pod +item.spore-pod.description = Used for conversion into oil, explosives and fuel. +item.sand.name = Sand +item.sand.description = A common material that is used extensively in smelting, both in alloying and as a flux. +item.blast-compound.name = Blast Compound +item.blast-compound.description = A volatile compound used in bombs and explosives. While it can burned as fuel, this is not advised. +item.pyratite.name = Pyratite +item.pyratite.description = An extremely flammable substance used in incendiary weapons. +item.metaglass.name = Metaglass +item.metaglass.description = A super-tough glass compound. Extensively used for liquid distribution and storage. +item.scrap.name = Scrap +item.scrap.description = Leftover remnants of old structures and units. Contains trace amounts of many different metals. +liquid.water.name = Water +liquid.slag.name = Slag +liquid.oil.name = Oil +liquid.cryofluid.name = Cryofluid +mech.alpha-mech.name = Alpha +mech.alpha-mech.weapon = Heavy Repeater +mech.alpha-mech.ability = Drone Swarm +mech.alpha-mech.description = The standard mech. Has decent speed and damage output; can create up to 3 drones for increased offensive capability. +mech.delta-mech.name = Delta +mech.delta-mech.weapon = Arc Generator +mech.delta-mech.ability = Discharge +mech.delta-mech.description = A fast, lightly-armored mech made for hit-and-run attacks. Does little damage against structures, but can kill large groups of enemy units very quickly with its arc lightning weapons. +mech.tau-mech.name = Tau +mech.tau-mech.weapon = Restruct Laser +mech.tau-mech.ability = Repair Burst +mech.tau-mech.description = The support mech. Heals allied blocks by shooting at them. Can heal allies in a radius with its repair ability. +mech.omega-mech.name = Omega +mech.omega-mech.weapon = Swarm Missiles +mech.omega-mech.ability = Armored Configuration +mech.omega-mech.description = A bulky and well-armored mech, made for front-line assaults. Its armor ability can block up to 90% of incoming damage. +mech.dart-ship.name = Dart +mech.dart-ship.weapon = Repeater +mech.dart-ship.description = The standard ship. Reasonably fast and light, but has little offensive capability and low mining speed. +mech.javelin-ship.name = Javelin +mech.javelin-ship.description = A hit-and-run strike ship. While initially slow, it can accelerate to great speeds and fly by enemy outposts, dealing large amounts of damage with its lightning ability and missiles. +mech.javelin-ship.weapon = Burst Missiles +mech.javelin-ship.ability = Discharge Booster +mech.trident-ship.name = Trident +mech.trident-ship.description = A heavy bomber. Reasonably well armored. +mech.trident-ship.weapon = Bomb Bay +mech.glaive-ship.name = Glaive +mech.glaive-ship.description = A large, well-armored gunship. Equipped with an incendiary repeater. Good acceleration and maximum speed. +mech.glaive-ship.weapon = Flame Repeater +item.explosiveness = [LIGHT_GRAY]Explosiveness: {0}% +item.flammability = [LIGHT_GRAY]Flammability: {0}% +item.radioactivity = [LIGHT_GRAY]Radioactivity: {0}% +unit.health = [LIGHT_GRAY]Health: {0} +unit.speed = [LIGHT_GRAY]Speed: {0} +mech.weapon = [LIGHT_GRAY]Weapon: {0} +mech.health = [LIGHT_GRAY]Health: {0} +mech.itemcapacity = [LIGHT_GRAY]Item Capacity: {0} +mech.minespeed = [LIGHT_GRAY]Mining Speed: {0} +mech.minepower = [LIGHT_GRAY]Mining Power: {0} +mech.ability = [LIGHT_GRAY]Ability: {0} +mech.buildspeed = [LIGHT_GRAY]Building Speed: {0}% +liquid.heatcapacity = [LIGHT_GRAY]Heat Capacity: {0} +liquid.viscosity = [LIGHT_GRAY]Viscosity: {0} +liquid.temperature = [LIGHT_GRAY]Temperature: {0} +block.grass.name = Grass +block.salt.name = Salt +block.saltrocks.name = Salt Rocks +block.pebbles.name = Pebbles +block.tendrils.name = Tendrils +block.sandrocks.name = Sand Rocks +block.spore-pine.name = Spore Pine +block.sporerocks.name = Spore Rocks +block.rock.name = Rock +block.snowrock.name = Snow Rock +block.shale.name = Shale +block.shale-boulder.name = Shale Boulder +block.moss.name = Moss +block.shrubs.name = Shrubs +block.spore-moss.name = Spore Moss +block.shalerocks.name = Shale Rocks +block.scrap-wall.name = Scrap Wall +block.scrap-wall-large.name = Large Scrap Wall +block.scrap-wall-huge.name = Huge Scrap Wall +block.scrap-wall-gigantic.name = Gigantic Scrap Wall +block.thruster.name = Thruster +block.kiln.name = Kiln +block.kiln.description = Smelts sand and lead into metaglass. Requires small amounts of power. +block.graphite-press.name = Graphite Press +block.multi-press.name = Multi-Press +block.constructing = {0} [LIGHT_GRAY](Constructing) +block.spawn.name = Enemy Spawn +block.core-shard.name = Core: Shard +block.core-foundation.name = Core: Foundation +block.core-nucleus.name = Core: Nucleus +block.deepwater.name = Deep Water +block.water.name = Water +block.tainted-water.name = Tainted Water +block.darksand-tainted-water.name = Dark Sand Tainted Water +block.tar.name = Tar +block.stone.name = Stone +block.sand.name = Dark Sand +block.darksand.name = Dark Sand +block.ice.name = Ice +block.snow.name = Snow +block.craters.name = Craters +block.sand-water.name = Sand water +block.darksand-water.name = Dark Sand Water +block.char.name = Char +block.holostone.name = Holo stone +block.ice-snow.name = Ice Snow +block.rocks.name = Rocks +block.icerocks.name = Ice rocks +block.snowrocks.name = Snow Rocks +block.dunerocks.name = Dune Rocks +block.pine.name = Pine +block.white-tree-dead.name = White Tree Dead +block.white-tree.name = White Tree +block.spore-cluster.name = Spore Cluster +block.metal-floor.name = Metal Floor +block.metal-floor-2.name = Metal Floor 2 +block.metal-floor-3.name = Metal Floor 3 +block.metal-floor-5.name = Metal Floor 5 +block.metal-floor-damaged.name = Metal Floor Damaged +block.dark-panel-1.name = Dark Panel 1 +block.dark-panel-2.name = Dark Panel 2 +block.dark-panel-3.name = Dark Panel 3 +block.dark-panel-4.name = Dark Panel 4 +block.dark-panel-5.name = Dark Panel 5 +block.dark-panel-6.name = Dark Panel 6 +block.dark-metal.name = Dark Metal +block.ignarock.name = Igna Rock +block.hotrock.name = Hot Rock +block.magmarock.name = Magma Rock +block.cliffs.name = Cliffs +block.copper-wall.name = Copper Wall +block.copper-wall-large.name = Large Copper Wall +block.titanium-wall.name = Titanium Wall +block.titanium-wall-large.name = Large Titanium Wall +block.phase-wall.name = Phase Wall +block.phase-wall-large.name = Large Phase Wall +block.thorium-wall.name = Thorium Wall +block.thorium-wall-large.name = Large Thorium Wall +block.door.name = Door +block.door-large.name = Large Door +block.duo.name = Duo +block.scorch.name = Scorch +block.scatter.name = Scatter +block.hail.name = Hail +block.lancer.name = Lancer +block.conveyor.name = Conveyor +block.titanium-conveyor.name = Titanium Conveyor +block.junction.name = Junction +block.router.name = Router +block.distributor.name = Distributor +block.sorter.name = Sorter +block.sorter.description = Sorts items. If an item matches the selection, it is allowed to pass. Otherwise, the item is outputted to the left and right. +block.overflow-gate.name = Overflow Gate +block.overflow-gate.description = A combination splitter and router that only outputs to the left and right if the front path is blocked. +block.silicon-smelter.name = Silicon Smelter +block.phase-weaver.name = Phase Weaver +block.pulverizer.name = Pulverizer +block.cryofluidmixer.name = Cryofluid Mixer +block.melter.name = Melter +block.incinerator.name = Incinerator +block.spore-press.name = Spore Press +block.separator.name = Separator +block.coal-centrifuge.name = Coal Centrifuge +block.power-node.name = Power Node +block.power-node-large.name = Large Power Node +block.surge-tower.name = Surge Tower +block.battery.name = Battery +block.battery-large.name = Large Battery +block.combustion-generator.name = Combustion Generator +block.turbine-generator.name = Turbine Generator +block.differential-generator.name = Differential Generator +block.impact-reactor.name = Impact Reactor +block.mechanical-drill.name = Mechanical Drill +block.pneumatic-drill.name = Pneumatic Drill +block.laser-drill.name = Laser Drill +block.water-extractor.name = Water Extractor +block.cultivator.name = Cultivator +block.dart-mech-pad.name = Dart Mech Pad +block.delta-mech-pad.name = Delta Mech Pad +block.javelin-ship-pad.name = Javelin Ship Pad +block.trident-ship-pad.name = Trident Ship Pad +block.glaive-ship-pad.name = Glaive Ship Pad +block.omega-mech-pad.name = Omega Mech Pad +block.tau-mech-pad.name = Tau Mech Pad +block.conduit.name = Conduit +block.mechanical-pump.name = Mechanical Pump +block.item-source.name = Item Source +block.item-void.name = Item Void +block.liquid-source.name = Liquid Source +block.power-void.name = Power Void +block.power-source.name = Power Infinite +block.unloader.name = Unloader +block.vault.name = Vault +block.wave.name = Wave +block.swarmer.name = Swarmer +block.salvo.name = Salvo +block.ripple.name = Ripple +block.phase-conveyor.name = Phase Conveyor +block.bridge-conveyor.name = Bridge Conveyor +block.plastanium-compressor.name = Plastanium Compressor +block.pyratite-mixer.name = Pyratite Mixer +block.blast-mixer.name = Blast Mixer +block.solar-panel.name = Solar Panel +block.solar-panel-large.name = Large Solar Panel +block.oil-extractor.name = Oil Extractor +block.spirit-factory.name = Spirit Drone Factory +block.phantom-factory.name = Phantom Drone Factory +block.wraith-factory.name = Wraith Fighter Factory +block.ghoul-factory.name = Ghoul Bomber Factory +block.dagger-factory.name = Dagger Mech Factory +block.crawler-factory.name = Crawler Mech Factory +block.titan-factory.name = Titan Mech Factory +block.fortress-factory.name = Fortress Mech Factory +block.revenant-factory.name = Revenant Fighter Factory +block.repair-point.name = Repair Point +block.pulse-conduit.name = Pulse Conduit +block.phase-conduit.name = Phase Conduit +block.liquid-router.name = Liquid Router +block.liquid-tank.name = Liquid Tank +block.liquid-junction.name = Liquid Junction +block.bridge-conduit.name = Bridge Conduit +block.rotary-pump.name = Rotary Pump +block.thorium-reactor.name = Thorium Reactor +block.mass-driver.name = Mass Driver +block.blast-drill.name = Airblast Drill +block.thermal-pump.name = Thermal Pump +block.thermal-generator.name = Thermal Generator +block.alloy-smelter.name = Alloy Smelter +block.mender.name = Mender +block.mend-projector.name = Mend Projector +block.surge-wall.name = Surge Wall +block.surge-wall-large.name = Large Surge Wall +block.cyclone.name = Cyclone +block.fuse.name = Fuse +block.shock-mine.name = Shock Mine +block.overdrive-projector.name = Overdrive Projector +block.force-projector.name = Force Projector +block.arc.name = Arc +block.rtg-generator.name = RTG Generator +block.spectre.name = Spectre +block.meltdown.name = Meltdown +block.container.name = Container +block.launch-pad.name = Launch Pad +block.launch-pad.description = Launches batches of items without any need for a core launch. Unfinished. +block.launch-pad-large.name = Large Launch Pad +team.blue.name = blue +team.red.name = red +team.orange.name = orange +team.none.name = gray +team.green.name = green +team.purple.name = purple +unit.spirit.name = Spirit Drone +unit.spirit.description = The starter drone unit. Spawns in the core by default. Automatically mines ores and repairs blocks. +unit.phantom.name = Phantom Drone +unit.phantom.description = An advanced drone unit. Automatically mines ores and repairs blocks. Significantly more effective than a spirit drone. +unit.dagger.name = Dagger +unit.dagger.description = A basic ground unit. Useful in swarms. +unit.crawler.name = Crawler +unit.titan.name = Titan +unit.titan.description = An advanced, armored ground unit. Attacks both ground and air targets. +unit.ghoul.name = Ghoul Bomber +unit.ghoul.description = A heavy carpet bomber. +unit.wraith.name = Wraith Fighter +unit.wraith.description = A fast, hit-and-run interceptor unit. +unit.fortress.name = Fortress +unit.fortress.description = A heavy artillery ground unit. +unit.revenant.name = Revenant +unit.eruptor.name = Eruptor +unit.chaos-array.name = Chaos Array +unit.eradicator.name = Eradicator +unit.lich.name = Lich +unit.reaper.name = Reaper +tutorial.begin = Your mission here is to eradicate the[LIGHT_GRAY] enemy[].\n\nBegin by[accent] mining copper[]. Tap a copper ore vein near your core to do this. +tutorial.drill = Mining manually is inefficient.\n[accent]Drills []can mine automatically.\nPlace one on a copper vein. +tutorial.conveyor = [accent]Conveyors[] are used to transport items to the core.\nMake a line of conveyors from the drill to the core. +tutorial.morecopper = More copper is required.\n\nEither mine it manually, or place more drills. +tutorial.turret = Defensive structures must be built to repel the[LIGHT_GRAY] enemy[].\nBuild a duo turret near your base. +tutorial.drillturret = Duo turrets require[accent] copper ammo []to shoot.\nPlace a drill next to the turret to supply it with mined copper. +tutorial.waves = The[LIGHT_GRAY] enemy[] approaches.\n\nDefend your core for 2 waves. Build more turrets. +tutorial.lead = More ores are available. Explore and mine[accent] lead[].\n\nDrag from your unit to the core to transfer resources. +tutorial.smelter = Copper and lead are weak metals.\nSuperior[accent] Dense Alloy[] can be created in a smelter.\n\nBuild one. +tutorial.densealloy = The smelter will now produce alloy.\nGet some.\nImprove the production if necessary. +tutorial.siliconsmelter = The core will now create a[accent] spirit drone[] for mining and repairing blocks.\n\nFactories for other units can be created with [accent] silicon.\nMake a silicon smelter. +tutorial.silicondrill = Silicon requires[accent] coal[] and[accent] sand[].\nStart by making drills. +tutorial.generator = This technology requires power.\nCreate a[accent] combustion generator[] for it. +tutorial.generatordrill = Combustion generators need fuel.\nFuel it with coal from a drill. +tutorial.node = Power requires transport.\nCreate a[accent] power node[] next to your combustion generator to transfer its power. +tutorial.nodelink = Power can be transferred through contacting power blocks and generators, or by linked power nodes.\n\nLink power by tapping the node and selecting the generator and silicon smelter. +tutorial.silicon = Silicon is being produced. Get some.\n\nImproving the production system is advised. +tutorial.daggerfactory = Construct a[accent] dagger mech factory.[]\n\nThis will be used to create attack mechs. +tutorial.router = Factories need resources to function.\nCreate a router to split conveyor resources. +tutorial.dagger = Link power nodes to the factory.\nOnce requirements are met, a mech will be created.\n\nCreate more drills, generators and conveyors as necessary. +tutorial.battle = The[LIGHT_GRAY] enemy[] has revealed their core.\nDestroy it with your unit and dagger mechs. +block.copper-wall.description = A cheap defensive block.\nUseful for protecting the core and turrets in the first few waves. +block.copper-wall-large.description = A cheap defensive block.\nUseful for protecting the core and turrets in the first few waves.\nSpans multiple tiles. +block.thorium-wall.description = A strong defensive block.\nGood protection from enemies. +block.thorium-wall-large.description = A strong defensive block.\nGood protection from enemies.\nSpans multiple tiles. +block.phase-wall.description = Not as strong as a thorium wall but will deflect bullets unless they are too powerful. +block.phase-wall-large.description = Not as strong as a thorium wall but will deflect bullets unless they are too powerful.\nSpans multiple tiles. +block.surge-wall.description = The strongest defensive block.\nHas a small chance of triggering lightning towards the attacker. +block.surge-wall-large.description = The strongest defensive block.\nHas a small chance of triggering lightning towards the attacker.\nSpans multiple tiles. +block.door.description = A small door that can be opened and closed by tapping on it.\nIf opened, enemies can shoot and move through. +block.door-large.description = A large door that can be opened and closed by tapping on it.\nIf opened, enemies can shoot and move through.\nSpans multiple tiles. +block.mend-projector.description = Periodically heals blocks in its vicinity. +block.overdrive-projector.description = Increases the speed of nearby buildings like drills and conveyors. +block.force-projector.description = Creates a hexagonal force field around itself, protecting buildings and units inside from damage through bullets. +block.shock-mine.description = Damages enemies stepping on the mine. Nearly invisible to the enemy. +block.duo.description = A small, cheap turret. +block.scatter.description = A medium-sized anti-air turret. Sprays clumps of lead or scrap flak at enemy units. +block.arc.description = A small turret which shoots electricity in a random arc towards the enemy. +block.hail.description = A small artillery turret. +block.lancer.description = A medium-sized turret which shoots charged electricity beams. +block.wave.description = A medium-sized rapid-fire turret which shoots liquid bubbles. +block.salvo.description = A medium-sized turret which fires shots in salvos. +block.swarmer.description = A medium-sized turret which shoots burst missiles. +block.ripple.description = A large artillery turret which fires several shots simultaneously. +block.cyclone.description = A large rapid fire turret. +block.fuse.description = A large turret which shoots powerful short-range beams. +block.spectre.description = A large turret which shoots two powerful bullets at once. +block.meltdown.description = A large turret which shoots powerful long-range beams. +block.conveyor.description = Basic item transport block. Moves items forward and automatically deposits them into turrets or crafters. Rotatable. +block.titanium-conveyor.description = Advanced item transport block. Moves items faster than standard conveyors. +block.phase-conveyor.description = Advanced item transport block. Uses power to teleport items to a connected phase conveyor over several tiles. +block.junction.description = Acts as a bridge for two crossing conveyor belts. Useful in situations with two different conveyors carrying different materials to different locations. +block.mass-driver.description = Ultimate item transport block. Collects several items and then shoots them to another mass driver over a long range. +block.silicon-smelter.description = Reduces sand with highly pure coal in order to produce silicon. +block.plastanium-compressor.description = Produces plastanium from oil and titanium. +block.phase-weaver.description = Produces phase fabric from radioactive thorium and high amounts of sand. +block.alloy-smelter.description = Produces surge alloy from titanium, lead, silicon and copper. +block.pulverizer.description = Crushes stone into sand. Useful when there is a lack of natural sand. +block.pyratite-mixer.description = Mixes coal, lead and sand into highly flammable pyratite. +block.blast-mixer.description = Uses oil for transforming pyratite into the less flammable but more explosive blast compound. +block.cryofluidmixer.description = Combines water and titanium into cryofluid which is much more efficient for cooling. +block.melter.description = Melts down scrap into slag for further processing or usage in turrets. +block.incinerator.description = Gets rid of any excess item or liquid. +block.spore-press.description = Compresses spore pods into oil. +block.separator.description = Extracts useful minerals from slag. +block.power-node.description = Transmits power to connected nodes. Up to four power sources, sinks or nodes can be connected. The node will receive power from or supply power to any adjacent blocks. +block.power-node-large.description = Has a larger radius than the power node and connects to up to six power sources, sinks or nodes. +block.battery.description = Stores power whenever there is an abundance and provides power whenever there is a shortage, as long as there is capacity left. +block.battery-large.description = Stores much more power than a regular battery. +block.combustion-generator.description = Generates power by burning oil or flammable materials. +block.turbine-generator.description = More efficient than a combustion generator, but requires additional water. +block.thermal-generator.description = Generates power when placed in hot locations. +block.solar-panel.description = Provides a small amount of power from the sun. +block.solar-panel-large.description = Provides much better power supply than a standard solar panel, but is also much more expensive to build. +block.thorium-reactor.description = Generates huge amounts of power from highly radioactive thorium. Requires constant cooling. Will explode violently if insufficient amounts of coolant are supplied. Power output depends on fullness, with base power generated at half capacity. +block.rtg-generator.description = A radioisotope thermoelectric generator which does not require cooling but provides less power than a thorium reactor. +block.unloader.description = Unloads items from a container, vault or core onto a conveyor or directly into an adjacent block. The type of item to be unloaded can be changed by tapping on the unloader. +block.container.description = Stores a small amount of items of each type. Adjacent containers, vaults and cores will be treated as a single storage unit. An[LIGHT_GRAY] unloader[] can be used to retrieve items from the container. +block.vault.description = Stores a large amount of items of each type. Adjacent containers, vaults and cores will be treated as a single storage unit. An[LIGHT_GRAY] unloader[] can be used to retrieve items from the vault. +block.mechanical-drill.description = A cheap drill. When placed on appropriate tiles, outputs items at a slow pace indefinitely. +block.pneumatic-drill.description = An improved drill which is faster and able to process harder materials by making use of air pressure. +block.laser-drill.description = Allows drilling even faster through laser technology, but requires power. Additionally, radioactive thorium can be retrieved with this drill. +block.blast-drill.description = The ultimate drill. Requires large amounts of power. +block.water-extractor.description = Extracts water from the ground. Use it when there is no lake nearby. +block.cultivator.description = Cultivates spores with water in order to obtain biomatter. +block.oil-extractor.description = Uses large amounts of power in order to extract oil from sand. Use it when there is no direct source of oil nearby. +block.trident-ship-pad.description = Leave your current vessel and change into a reasonably well armored heavy bomber.\nUse the pad by double tapping while standing on it. +block.javelin-ship-pad.description = Leave your current vessel and change into a strong and fast interceptor with lightning weapons.\nUse the pad by double tapping while standing on it. +block.glaive-ship-pad.description = Leave your current vessel and change into a large, well-armored gunship.\nUse the pad by double tapping while standing on it. +block.tau-mech-pad.description = Leave your current vessel and change into a support mech which can heal friendly buildings and units.\nUse the pad by double tapping while standing on it. +block.delta-mech-pad.description = Leave your current vessel and change into a fast, lightly-armored mech made for hit-and-run attacks.\nUse the pad by double tapping while standing on it. +block.omega-mech-pad.description = Leave your current vessel and change into a bulky and well-armored mech, made for front-line assaults.\nUse the pad by double tapping while standing on it. +block.spirit-factory.description = Produces light drones which mine ore and repair blocks. +block.phantom-factory.description = Produces advanced drone units which are significantly more effective than a spirit drone. +block.wraith-factory.description = Produces fast, hit-and-run interceptor units. +block.ghoul-factory.description = Produces heavy carpet bombers. +block.dagger-factory.description = Produces basic ground units. +block.titan-factory.description = Produces advanced, armored ground units. +block.fortress-factory.description = Produces heavy artillery ground units. +block.revenant-factory.description = Produces heavy laser air units. +block.repair-point.description = Continuously heals the closest damaged unit in its vicinity. +block.conduit.description = Basic liquid transport block. Works like a conveyor, but with liquids. Best used with extractors, pumps or other conduits. +block.pulse-conduit.description = Advanced liquid transport block. Transports liquids faster and stores more than standard conduits. +block.phase-conduit.description = Advanced liquid transport block. Uses power to teleport liquids to a connected phase conduit over several tiles. +block.liquid-router.description = Accepts liquids from one direction and outputs them to up to 3 other directions equally. Can also store a certain amount of liquid. Useful for splitting the liquids from one source to multiple targets. +block.liquid-tank.description = Stores a large amount of liquids. Use it for creating buffers when there is a non-constant demand of materials or as a safeguard for cooling vital blocks. +block.liquid-junction.description = Acts as a bridge for two crossing conduits. Useful in situations with two different conduits carrying different liquids to different locations. +block.bridge-conduit.description = Advanced liquid transport block. Allows transporting liquids over up to 3 tiles of any terrain or building. +block.mechanical-pump.description = A cheap pump with slow output, but no power consumption. +block.rotary-pump.description = An advanced pump which doubles up speed by using power. +block.thermal-pump.description = The ultimate pump. Three times as fast as a mechanical pump and the only pump which is able to retrieve lava. +block.router.description = Accepts items from one direction and outputs them to up to 3 other directions equally. Useful for splitting the materials from one source to multiple targets. +block.distributor.description = An advanced router which splits items to up to 7 other directions equally. +block.bridge-conveyor.description = Advanced item transport block. Allows transporting items over up to 3 tiles of any terrain or building. +block.item-source.description = Infinitely outputs items. Sandbox only. +block.liquid-source.description = Infinitely outputs liquids. Sandbox only. +block.item-void.description = Destroys any items which go into it without using power. Sandbox only. +block.power-source.description = Infinitely outputs power. Sandbox only. +block.power-void.description = Voids all power inputted into it. Sandbox only. +liquid.water.description = Commonly used for cooling machines and waste processing. +liquid.oil.description = Can be burnt, exploded or used as a coolant. +liquid.cryofluid.description = The most efficient liquid for cooling things down. diff --git a/core/assets/bundles/bundle_nl_BE.properties b/core/assets/bundles/bundle_nl_BE.properties new file mode 100644 index 0000000000..a1b20b8171 --- /dev/null +++ b/core/assets/bundles/bundle_nl_BE.properties @@ -0,0 +1,947 @@ +credits.text = Gemaakt door [ROYAL]Anuken[] - [SKY]anukendev@gmail.com[] +credits = Credits +contributors = Vertalers en medewerkers +discord = Sluit je aan bij de Mindustry discord server! +link.discord.description = De officiële Mindustry discord chatroom +link.github.description = Broncode +link.dev-builds.description = Onstabiele versies +link.trello.description = Officiële Trello voor geplande toevoegingen. +link.itch.io.description = Itch.io pagina met de PC downloads en online versie +link.google-play.description = Mindustry op Google Play +link.wiki.description = Officiël Mindustry wiki +linkfail = Openen van link mislukt!\nDe link is gekopiëerd naar je klembord. +screenshot = Locatie screenshot: {0} +screenshot.invalid = Kaart te groot, mogelijks te weinig geheugen voor een screenshot te kunnen maken. +gameover = Game Over +gameover.pvp = Het[accent] {0}[] team heeft gewonnen! +highscore = [accent]Nieuw record! +stat.wave = Je overleefde tot aanvalsgolf: [accent]{0}[]. +stat.enemiesDestroyed = Vijanden vernietigd:[accent] {0} +stat.built = Gebouwen gebouwd:[accent] {0} +stat.destroyed = Gebouwen vernietigd:[accent] {0} +stat.deconstructed = Gebouwen afgebroken:[accent] {0} +stat.delivered = Gronstoffen meegenomen: +stat.rank = Eindresultaat: [accent]{0} +placeline = You have selected a block.\nYou can[accent] place in a line[] by[accent] holding down your finger for a few seconds[] and dragging in a direction.\n\n[scarlet]DO IT. +removearea = You have selected removal mode.\nYou can[accent] remove blocks in a rectangle[] by[accent] holding down your finger for a few seconds[] and dragging.\n\n[scarlet]DO IT. +launcheditems = [accent]Meegenomen grondstoffen +map.delete = Ben je zeker dat je de kaart "[accent]{0}[]" wilt verwijderen? +level.highscore = Beste score: [accent]{0} +level.select = Selecteer level +level.mode = Speelmode: +showagain = Toon dit volgende keer niet meer. +coreattack = < Kern wordt aangevallen! > +nearpoint = [[ [scarlet]VERLAAT dit gebied onmiddelijk[] ]\nDirecte vernietiging... +outofbounds = [[ BUITEN HET SPEELBAAR GEBIED]\n[]Zelfvernietiging in {0} +database = Kern Database +savegame = opslaan +loadgame = openen +joingame = Multiplayer +addplayers = Voeg toe/verwijder spelers +customgame = Aangepaste versie +newgame = Nieuw spel +none = +minimap = Kaartje +close = Sluit +quit = Verlaat +maps = Kaarten +continue = Ga verder +maps.none = [LIGHT_GRAY]Geen kaarten gevonden! +about.button = Extra info +name = Naam: +noname = Kies eerst[accent] een naam[]. +filename = Bestands naam: +unlocked = Ontgrendeld! +completed = [accent]Voltooid +techtree = Technische vooruitgang +research.list = [LIGHT_GRAY]Onderzoek: +research = Onderzoek +researched = [LIGHT_GRAY]{0} onderzocht. +players = {0} spelers online +players.single = {0} speler online +server.closing = [accent]Server wordt gesloten... +server.kicked.kick = Je bent uit de server gegooid! +server.kicked.serverClose = Server gesloten. +server.kicked.clientOutdated = Verouderde versie! Update Mindustry! +server.kicked.serverOutdated = Verouderde server! Vraag de eigenaar van de server om de server te updaten! +server.kicked.banned = Je bent verbannen van deze server. +server.kicked.recentKick = Je bent daarnet van de server gegooid.\nWacht even voor je weer verbindt +server.kicked.nameInUse = Er is al iemand met dezelfde naam op de server. +server.kicked.nameEmpty = Je gekozen naam is ongeldig. +server.kicked.idInUse = Je bent al verbonden met de server! Verbinden met 2 clients tegelijk is verboden. +server.kicked.customClient = Deze server ondersteunt geen aangepaste versies (mods). Download een officiële versie. +server.kicked.gameover = Game over! +host.info = Ook De [accent]host[] knop hosts een server op poort [scarlet]6567[]. \nIedereen die verbonden is met dezelfde [LIGHT_GRAY]wifi of lokaal netwerk[] zou je server moeten zien in zijn server lijst.\n\nAls je wil dat personen kunnen verbinden met je server van ergens anders via IP. Dan is [accent]port forwarding[] is nodig.\n\n[LIGHT_GRAY]Nota: Als iemand problemen heeft met het verbinden tot je LAN spel, zorg dan dat mindustry toestemming heeft tot je lokale netwerk in de Firewall instellingen. +join.info = Hier kan je een [accent]server IP[] invullen waarmee je wil verbinden. Je kan hier ook verbinden met servers op je [accent]lokale netwerk[]. LAN en WAN multiplayer wordt ondersteund.\n\n[LIGHT_GRAY]Belangrijk: er is geen automatische globale server lijst; als je met iemand wil verbinden via een IP adres moet je zijn/haar IP adres vragen. +hostserver = Host Game +hostserver.mobile = Host\nGame +host = Host +hosting = [accent]De server wordt geopend... +hosts.refresh = Herlaad +hosts.discovering = LAN games worden gezocht +server.refreshing = De server wordt herladen +hosts.none = [lightgray]Geen games op je lokale netwerk gevonden. +host.invalid = [scarlet]Kan niet verbinden met de host (server). +trace = Zoeken speler +trace.playername = Naam speler: [accent]{0} +trace.ip = IP: [accent]{0} +trace.id = Uniek ID: [accent]{0} +trace.mobile = Mobile Client: [accent]{0} +trace.modclient = Aangepaste Client: [accent]{0} +invalidid = Ongeldig client ID! Verstuur een bug report! +server.bans = Verbannen +server.bans.none = Geen verbannen spelers gevonden! +server.admins = Administrators +server.admins.none = Geen Administrators gevonden! +server.add = Voeg server toe +server.delete = Ben je zeker dat je deze sever wil verwijderen? +server.hostname = Host: {0} +server.edit = Bewerk Server +server.outdated = [crimson]Verouderde Server![] +server.outdated.client = [crimson]Verouderde Client![] +server.version = [lightgray]Versie: {0} {1} +server.custombuild = [yellow]Aangepaste versie +confirmban = Ben je zeker dat je deze speler wil verbannen? +confirmkick = Ben je zeker dat je deze speler van de server wil gooien? +confirmunban = Ben je zeker dat je de verbanning ongedaan wil maken? +confirmadmin = Ben je zeker dat je deze speler administrator wil maken? +confirmunadmin = Ben je zeker dat je de Administrator status van deze speler ongedaan wilt maken? +joingame.title = Verbinden met server +joingame.ip = IP adres: +disconnect = Verbinding verbroken. +disconnect.data = Laden map data mislukt! +connecting = [accent]Verbinden... +connecting.data = [accent]Laden map data... +server.port = Poort: +server.addressinuse = Dit adres wordt al gebruikt! +server.invalidport = Ongeldige poort! +server.error = [crimson]Error hosting server: [accent]{0} +save.old = Deze save word niet meer ondersteund\n\n[LIGHT_GRAY]Terugwaardse compatibiliteit zal geïmplementeerd worden in de volledige 4.0 versie +save.new = Nieuwe save +save.overwrite = Ben je zeker dat je deze save\nwil overschrijven? +overwrite = Overschrijf +save.none = Geen saves gevonden! +saveload = [accent]Opslaan... +savefail = Opslaan mislukt! +save.delete.confirm = Ben je zeker dat je deze save wil verwijderen? +save.delete = Verwijder +save.export = Exporteer Save +save.import.invalid = [accent]Deze save is ongeldig! +save.import.fail = [crimson]Save importeren mislukt: [accent]{0} +save.export.fail = [crimson]Save exporteren mislukt: [accent]{0} +save.import = Importeer Save +save.newslot = Save naam: +save.rename = Naam wijzigen +save.rename.text = Nieuwe naam: +selectslot = Selecteer een save. +slot = [accent]Slot {0} +save.corrupted = [accent]Save file corrupted or invalid!\nIf you have just updated your game, this is probably a change in the save format and [scarlet]not[] a bug. +empty = +on = On +off = Off +save.autosave = Autosave: {0} +save.map = Map: {0} +save.wave = Wave {0} +save.difficulty = Difficulty: {0} +save.date = Last Saved: {0} +save.playtime = Playtime: {0} +warning = Warning. +confirm = Confirm +delete = Delete +ok = OK +open = Open +customize = Customize +cancel = Cancel +openlink = Open Link +copylink = Copy Link +back = Back +quit.confirm = Are you sure you want to quit? +changelog.title = Changelog +changelog.loading = Getting changelog... +changelog.error.android = [accent]Note that the changelog sometimes does not work on Android 4.4 and below!\nThis is due to an internal Android bug. +changelog.error.ios = [accent]The changelog is currently not supported in iOS. +changelog.error = [scarlet]Error getting changelog!\nCheck your internet connection. +changelog.current = [yellow][[Current version] +changelog.latest = [accent][[Latest version] +loading = [accent]Loading... +saving = [accent]Saving... +wave = [accent]Wave {0} +wave.waiting = [LIGHT_GRAY]Wave in {0} +wave.waveInProgress = [LIGHT_GRAY]Wave in progress +waiting = [LIGHT_GRAY]Waiting... +waiting.players = Waiting for players... +wave.enemies = [LIGHT_GRAY]{0} Enemies Remaining +wave.enemy = [LIGHT_GRAY]{0} Enemy Remaining +loadimage = Load Image +saveimage = Save Image +unknown = Unknown +custom = Custom +builtin = Built-In +map.delete.confirm = Are you sure you want to delete this map? This action cannot be undone! +map.random = [accent]Random Map +map.nospawn = This map does not have any cores for the player to spawn in! Add a[ROYAL] blue[] core to this map in the editor. +map.nospawn.pvp = This map does not have any enemy cores for player to spawn into! Add[SCARLET] non-blue[] cores to this map in the editor. +map.nospawn.attack = This map does not have any enemy cores for player to attack! Add[SCARLET] red[] cores to this map in the editor. +map.invalid = Error loading map: corrupted or invalid map file. +editor.brush = Brush +editor.openin = Open In Editor +editor.oregen = Ore Generation +editor.oregen.info = Ore Generation: +editor.mapinfo = Map Info +editor.author = Author: +editor.description = Description: +editor.waves = Waves: +editor.rules = Rules: +editor.ingame = Edit In-Game +waves.title = Waves +waves.remove = Remove +waves.never = +waves.every = every +waves.waves = wave(s) +waves.perspawn = per spawn +waves.to = to +waves.boss = Boss +waves.preview = Preview +waves.edit = Edit... +waves.copy = Copy to Clipboard +waves.load = Load from Clipboard +waves.invalid = Invalid waves in clipboard. +waves.copied = Waves copied. +editor.default = [LIGHT_GRAY] +edit = Edit... +editor.name = Name: +editor.spawn = Spawn Unit +editor.removeunit = Remove Unit +editor.teams = Teams +editor.elevation = Elevation +editor.errorload = Error loading file:\n[accent]{0} +editor.errorsave = Error saving file:\n[accent]{0} +editor.errorimage = That's an image, not a map. Don't go around changing extensions expecting it to work.\n\nIf you want to import a legacy map, use the 'import legacy map' button in the editor. +editor.errorlegacy = This map is too old, and uses a legacy map format that is no longer supported. +editor.errorheader = This map file is either not valid or corrupt. +editor.errorname = Map has no name defined. +editor.update = Update +editor.randomize = Randomize +editor.apply = Apply +editor.generate = Generate +editor.resize = Resize +editor.loadmap = Load Map +editor.savemap = Save Map +editor.saved = Saved! +editor.save.noname = Your map does not have a name! Set one in the 'map info' menu. +editor.save.overwrite = Your map overwrites a built-in map! Pick a different name in the 'map info' menu. +editor.import.exists = [scarlet]Unable to import:[] a built-in map named '{0}' already exists! +editor.import = Import... +editor.importmap = Import Map +editor.importmap.description = Import an already existing map +editor.importfile = Import File +editor.importfile.description = Import an external map file +editor.importimage = Import Legacy Image +editor.importimage.description = Import an external map image file +editor.export = Export... +editor.exportfile = Export File +editor.exportfile.description = Export a map file +editor.exportimage = Export Terrain Image +editor.exportimage.description = Export a map image file +editor.loadimage = Import Terrain +editor.saveimage = Export Terrain +editor.unsaved = [scarlet]You have unsaved changes![]\nAre you sure you want to exit? +editor.resizemap = Resize Map +editor.mapname = Map Name: +editor.overwrite = [accent]Warning!\nThis overwrites an existing map. +editor.overwrite.confirm = [scarlet]Warning![] A map with this name already exists. Are you sure you want to overwrite it? +editor.selectmap = Select a map to load: +filters.empty = [LIGHT_GRAY]No filters! Add one with the button below. +filter.distort = Distort +filter.noise = Noise +filter.ore = Ore +filter.rivernoise = River Noise +filter.scatter = Scatter +filter.terrain = Terrain +filter.option.scale = Scale +filter.option.chance = Chance +filter.option.mag = Magnitude +filter.option.threshold = Threshold +filter.option.circle-scale = Circle Scale +filter.option.octaves = Octaves +filter.option.falloff = Falloff +filter.option.block = Block +filter.option.floor = Floor +filter.option.wall = Wall +filter.option.ore = Ore +filter.option.floor2 = Secondary Floor +filter.option.threshold2 = Secondary Threshold +filter.option.radius = Radius +filter.option.percentile = Percentile +width = Width: +height = Height: +menu = Menu +play = Play +load = Load +save = Save +fps = FPS: {0} +tps = TPS: {0} +ping = Ping: {0}ms +language.restart = Please restart your game for the language settings to take effect. +settings = Settings +tutorial = Tutorial +editor = Editor +mapeditor = Map Editor +donate = Donate +abandon = Abandon +abandon.text = This zone and all its resources will be lost to the enemy. +locked = Locked +complete = [LIGHT_GRAY]Reach: +zone.requirement = Wave {0} in zone {1} +resume = Resume Zone:\n[LIGHT_GRAY]{0} +bestwave = [LIGHT_GRAY]Best Wave: {0} +launch = < LAUNCH > +launch.title = Launch Successful +launch.next = [LIGHT_GRAY]next opportunity at wave {0} +launch.unable = [scarlet]Unable to LAUNCH.[] {0} Enemies. +launch.confirm = This will launch all resources in your core.\nYou will not be able to return to this base. +uncover = Uncover +configure = Configure Loadout +configure.locked = [LIGHT_GRAY]Unlock configuring loadout:\nWave {0}. +zone.unlocked = [LIGHT_GRAY]{0} unlocked. +zone.requirement.complete = Wave {0} reached:\n{1} zone requirements met. +zone.config.complete = Wave {0} reached:\nLoadout config unlocked. +zone.resources = Resources Detected: +add = Add... +boss.health = Boss Health +connectfail = [crimson]Failed to connect to server:\n\n[accent]{0} +error.unreachable = Server unreachable.\nIs the address spelled correctly? +error.invalidaddress = Invalid address. +error.timedout = Timed out!\nMake sure the host has port forwarding set up, and that the address is correct! +error.mismatch = Packet error:\npossible client/server version mismatch.\nMake sure you and the host have the latest version of Mindustry! +error.alreadyconnected = Already connected. +error.mapnotfound = Map file not found! +error.io = Network I/O error. +error.any = Unknown network error. +zone.groundZero.name = Ground Zero +zone.desertWastes.name = Desert Wastes +zone.craters.name = The Craters +zone.frozenForest.name = Frozen Forest +zone.ruinousShores.name = Ruinous Shores +zone.stainedMountains.name = Stained Mountains +zone.desolateRift.name = Desolate Rift +zone.nuclearComplex.name = Nuclear Production Complex +zone.overgrowth.name = Overgrowth +zone.tarFields.name = Tar Fields +settings.language = Language +settings.reset = Reset to Defaults +settings.rebind = Rebind +settings.controls = Controls +settings.game = Game +settings.sound = Sound +settings.graphics = Graphics +settings.cleardata = Clear Game Data... +settings.clear.confirm = Are you sure you want to clear this data?\nWhat is done cannot be undone! +settings.clearall.confirm = [scarlet]WARNING![]\nThis will clear all data, including saves, maps, unlocks and keybinds.\nOnce you press 'ok' the game will wipe all data and automatically exit. +settings.clearunlocks = Clear Unlocks +settings.clearall = Clear All +paused = [accent]< Paused > +yes = Yes +no = No +info.title = Info +error.title = [crimson]An error has occured +error.crashtitle = An error has occured +blocks.input = Input +blocks.output = Output +blocks.booster = Booster +block.unknown = [LIGHT_GRAY]??? +blocks.powercapacity = Power Capacity +blocks.powershot = Power/Shot +blocks.targetsair = Targets Air +blocks.targetsground = Targets Ground +blocks.itemsmoved = Move Speed +blocks.launchtime = Time Between Launches +blocks.shootrange = Range +blocks.size = Size +blocks.liquidcapacity = Liquid Capacity +blocks.powerrange = Power Range +blocks.poweruse = Power Use +blocks.powerdamage = Power/Damage +blocks.itemcapacity = Item Capacity +blocks.basepowergeneration = Base Power Generation +blocks.productiontime = Production Time +blocks.repairtime = Block Full Repair Time +blocks.speedincrease = Speed Increase +blocks.range = Range +blocks.drilltier = Drillables +blocks.drillspeed = Base Drill Speed +blocks.boosteffect = Boost Effect +blocks.maxunits = Max Active Units +blocks.health = Health +blocks.buildtime = Build Time +blocks.inaccuracy = Inaccuracy +blocks.shots = Shots +blocks.reload = Shots/Second +blocks.ammo = Ammo +bar.drillspeed = Drill Speed: {0}/s +bar.efficiency = Efficiency: {0}% +bar.powerbalance = Power: {0}/s +bar.poweramount = Power: {0} +bar.poweroutput = Power Output: {0} +bar.items = Items: {0} +bar.liquid = Liquid +bar.heat = Heat +bar.power = Power +bar.progress = Build Progress +bar.spawned = Units: {0}/{1} +bullet.damage = [stat]{0}[lightgray] damage +bullet.splashdamage = [stat]{0}[lightgray] area dmg ~[stat] {1}[lightgray] tiles +bullet.incendiary = [stat]incendiary +bullet.homing = [stat]homing +bullet.shock = [stat]shock +bullet.frag = [stat]frag +bullet.knockback = [stat]{0}[lightgray] knockback +bullet.freezing = [stat]freezing +bullet.tarred = [stat]tarred +bullet.multiplier = [stat]{0}[lightgray]x ammo multiplier +bullet.reload = [stat]{0}[lightgray]x fire rate +unit.blocks = blocks +unit.powersecond = power units/second +unit.liquidsecond = liquid units/second +unit.itemssecond = items/second +unit.liquidunits = liquid units +unit.powerunits = power units +unit.degrees = degrees +unit.seconds = seconds +unit.persecond = /sec +unit.timesspeed = x speed +unit.percent = % +unit.items = items +category.general = General +category.power = Power +category.liquids = Liquids +category.items = Items +category.crafting = Input/Output +category.shooting = Shooting +category.optional = Optional Enhancements +setting.landscape.name = Lock Landscape +setting.shadows.name = Shadows +setting.linear.name = Linear Filtering +setting.animatedwater.name = Animated Water +setting.animatedshields.name = Animated Shields +setting.antialias.name = Antialias[LIGHT_GRAY] (requires restart)[] +setting.indicators.name = Enemy/Ally Indicators +setting.autotarget.name = Auto-Target +setting.fpscap.name = Max FPS +setting.fpscap.none = None +setting.fpscap.text = {0} FPS +setting.swapdiagonal.name = Always Diagonal Placement +setting.difficulty.training = training +setting.difficulty.easy = easy +setting.difficulty.normal = normal +setting.difficulty.hard = hard +setting.difficulty.insane = insane +setting.difficulty.name = Difficulty: +setting.screenshake.name = Screen Shake +setting.effects.name = Display Effects +setting.sensitivity.name = Controller Sensitivity +setting.saveinterval.name = Autosave Interval +setting.seconds = {0} Seconds +setting.fullscreen.name = Fullscreen +setting.borderlesswindow.name = Borderless Window[LIGHT_GRAY] (may require restart) +setting.fps.name = Show FPS +setting.vsync.name = VSync +setting.lasers.name = Show Power Lasers +setting.pixelate.name = Pixelate [LIGHT_GRAY](may decrease performance, disables animations) +setting.minimap.name = Show Minimap +setting.musicvol.name = Music Volume +setting.mutemusic.name = Mute Music +setting.sfxvol.name = SFX Volume +setting.mutesound.name = Mute Sound +setting.crashreport.name = Send Anonymous Crash Reports +setting.chatopacity.name = Chat Opacity +setting.playerchat.name = Display In-Game Chat +keybind.title = Rebind Keys +category.general.name = General +category.view.name = View +category.multiplayer.name = Multiplayer +command.attack = Attack +command.retreat = Retreat +command.patrol = Patrol +keybind.gridMode.name = Block Select +keybind.gridModeShift.name = Category Select +keybind.press = Press a key... +keybind.press.axis = Press an axis or key... +keybind.screenshot.name = Map Screenshot +keybind.move_x.name = Move x +keybind.move_y.name = Move y +keybind.select.name = Select/Shoot +keybind.diagonal_placement.name = Diagonal Placement +keybind.pick.name = Pick Block +keybind.break_block.name = Break Block +keybind.deselect.name = Deselect +keybind.shoot.name = Shoot +keybind.zoom_hold.name = Zoom Hold +keybind.zoom.name = Zoom +keybind.menu.name = Menu +keybind.pause.name = Pause +keybind.minimap.name = Minimap +keybind.dash.name = Dash +keybind.chat.name = Chat +keybind.player_list.name = Player list +keybind.console.name = Console +keybind.rotate.name = Rotate +keybind.toggle_menus.name = Toggle menus +keybind.chat_history_prev.name = Chat history prev +keybind.chat_history_next.name = Chat history next +keybind.chat_scroll.name = Chat scroll +keybind.drop_unit.name = Drop Unit +keybind.zoom_minimap.name = Zoom minimap +mode.help.title = Description of modes +mode.survival.name = Survival +mode.survival.description = The normal mode. Limited resources and automatic incoming waves. +mode.sandbox.name = Sandbox +mode.sandbox.description = Infinite resources and no timer for waves. +mode.pvp.name = PvP +mode.pvp.description = Fight against other players locally. +mode.attack.name = Attack +mode.attack.description = No waves, with the goal to destroy the enemy base. +mode.custom = Custom Rules +rules.infiniteresources = Infinite Resources +rules.wavetimer = Wave Timer +rules.waves = Waves +rules.enemyCheat = Infinite AI (Red Team) Resources +rules.unitdrops = Unit Drops +rules.unitbuildspeedmultiplier = Unit Creation Speed Multiplier +rules.unithealthmultiplier = Unit Health Multiplier +rules.playerhealthmultiplier = Player Health Multiplier +rules.playerdamagemultiplier = Player Damage Multiplier +rules.unitdamagemultiplier = Unit Damage Multiplier +rules.enemycorebuildradius = Enemy Core No-Build Radius:[LIGHT_GRAY] (tiles) +rules.respawntime = Respawn Time:[LIGHT_GRAY] (sec) +rules.wavespacing = Wave Spacing:[LIGHT_GRAY] (sec) +rules.buildcostmultiplier = Build Cost Multiplier +rules.buildspeedmultiplier = Build Speed Multiplier +rules.waitForWaveToEnd = Waves wait for enemies +rules.dropzoneradius = Drop Zone Radius:[LIGHT_GRAY] (tiles) +rules.respawns = Max respawns per wave +rules.limitedRespawns = Limit Respawns +rules.title.waves = Waves +rules.title.respawns = Respawns +rules.title.resourcesbuilding = Resources & Building +rules.title.player = Players +rules.title.enemy = Enemies +rules.title.unit = Units +content.item.name = Items +content.liquid.name = Liquids +content.unit.name = Units +content.block.name = Blocks +content.mech.name = Mechs +item.copper.name = Copper +item.copper.description = A useful structure material. Used extensively in all types of blocks. +item.lead.name = Lead +item.lead.description = A basic starter material. Used extensively in electronics and liquid transportation blocks. +item.coal.name = Coal +item.coal.description = A common and readily available fuel. +item.graphite.name = Graphite +item.titanium.name = Titanium +item.titanium.description = A rare super-light metal used extensively in liquid transportation, drills and aircraft. +item.thorium.name = Thorium +item.thorium.description = A dense, radioactive metal used as structural support and nuclear fuel. +item.silicon.name = Silicon +item.silicon.description = An extremely useful semiconductor, with applications in solar panels and many complex electronics. +item.plastanium.name = Plastanium +item.plastanium.description = A light, ductile material used in advanced aircraft and fragmentation ammunition. +item.phase-fabric.name = Phase Fabric +item.phase-fabric.description = A near-weightless substance used in advanced electronics and self-repairing technology. +item.surge-alloy.name = Surge Alloy +item.surge-alloy.description = An advanced alloy with unique electrical properties. +item.spore-pod.name = Spore Pod +item.spore-pod.description = Used for conversion into oil, explosives and fuel. +item.sand.name = Sand +item.sand.description = A common material that is used extensively in smelting, both in alloying and as a flux. +item.blast-compound.name = Blast Compound +item.blast-compound.description = A volatile compound used in bombs and explosives. While it can burned as fuel, this is not advised. +item.pyratite.name = Pyratite +item.pyratite.description = An extremely flammable substance used in incendiary weapons. +item.metaglass.name = Metaglass +item.metaglass.description = A super-tough glass compound. Extensively used for liquid distribution and storage. +item.scrap.name = Scrap +item.scrap.description = Leftover remnants of old structures and units. Contains trace amounts of many different metals. +liquid.water.name = Water +liquid.slag.name = Slag +liquid.oil.name = Oil +liquid.cryofluid.name = Cryofluid +mech.alpha-mech.name = Alpha +mech.alpha-mech.weapon = Heavy Repeater +mech.alpha-mech.ability = Regeneration +mech.alpha-mech.description = The standard mech. Has decent speed and damage output. +mech.delta-mech.name = Delta +mech.delta-mech.weapon = Arc Generator +mech.delta-mech.ability = Discharge +mech.delta-mech.description = A fast, lightly-armored mech made for hit-and-run attacks. Does little damage against structures, but can kill large groups of enemy units very quickly with its arc lightning weapons. +mech.tau-mech.name = Tau +mech.tau-mech.weapon = Restruct Laser +mech.tau-mech.ability = Repair Burst +mech.tau-mech.description = The support mech. Heals allied blocks by shooting at them. Can heal allies in a radius with its repair ability. +mech.omega-mech.name = Omega +mech.omega-mech.weapon = Swarm Missiles +mech.omega-mech.ability = Armored Configuration +mech.omega-mech.description = A bulky and well-armored mech, made for front-line assaults. Its armor ability can block up to 90% of incoming damage. +mech.dart-ship.name = Dart +mech.dart-ship.weapon = Repeater +mech.dart-ship.description = The standard ship. Reasonably fast and light, but has little offensive capability and low mining speed. +mech.javelin-ship.name = Javelin +mech.javelin-ship.description = A hit-and-run strike ship. While initially slow, it can accelerate to great speeds and fly by enemy outposts, dealing large amounts of damage with its lightning ability and missiles. +mech.javelin-ship.weapon = Burst Missiles +mech.javelin-ship.ability = Discharge Booster +mech.trident-ship.name = Trident +mech.trident-ship.description = A heavy bomber. Reasonably well armored. +mech.trident-ship.weapon = Bomb Bay +mech.glaive-ship.name = Glaive +mech.glaive-ship.description = A large, well-armored gunship. Equipped with an incendiary repeater. Good acceleration and maximum speed. +mech.glaive-ship.weapon = Flame Repeater +item.explosiveness = [LIGHT_GRAY]Explosiveness: {0}% +item.flammability = [LIGHT_GRAY]Flammability: {0}% +item.radioactivity = [LIGHT_GRAY]Radioactivity: {0}% +unit.health = [LIGHT_GRAY]Health: {0} +unit.speed = [LIGHT_GRAY]Speed: {0} +mech.weapon = [LIGHT_GRAY]Weapon: {0} +mech.health = [LIGHT_GRAY]Health: {0} +mech.itemcapacity = [LIGHT_GRAY]Item Capacity: {0} +mech.minespeed = [LIGHT_GRAY]Mining Speed: {0}% +mech.minepower = [LIGHT_GRAY]Mining Power: {0} +mech.ability = [LIGHT_GRAY]Ability: {0} +mech.buildspeed = [LIGHT_GRAY]Building Speed: {0}% +liquid.heatcapacity = [LIGHT_GRAY]Heat Capacity: {0} +liquid.viscosity = [LIGHT_GRAY]Viscosity: {0} +liquid.temperature = [LIGHT_GRAY]Temperature: {0} +block.grass.name = Grass +block.salt.name = Salt +block.saltrocks.name = Salt Rocks +block.pebbles.name = Pebbles +block.tendrils.name = Tendrils +block.sandrocks.name = Sand Rocks +block.spore-pine.name = Spore Pine +block.sporerocks.name = Spore Rocks +block.rock.name = Rock +block.snowrock.name = Snow Rock +block.shale.name = Shale +block.shale-boulder.name = Shale Boulder +block.moss.name = Moss +block.shrubs.name = Shrubs +block.spore-moss.name = Spore Moss +block.shalerocks.name = Shale Rocks +block.scrap-wall.name = Scrap Wall +block.scrap-wall-large.name = Large Scrap Wall +block.scrap-wall-huge.name = Huge Scrap Wall +block.scrap-wall-gigantic.name = Gigantic Scrap Wall +block.thruster.name = Thruster +block.kiln.name = Kiln +block.kiln.description = Smelts sand and lead into metaglass. Requires small amounts of power. +block.graphite-press.name = Graphite Press +block.multi-press.name = Multi-Press +block.constructing = {0} [LIGHT_GRAY](Constructing) +block.spawn.name = Enemy Spawn +block.core-shard.name = Core: Shard +block.core-foundation.name = Core: Foundation +block.core-nucleus.name = Core: Nucleus +block.deepwater.name = Deep Water +block.water.name = Water +block.tainted-water.name = Tainted Water +block.darksand-tainted-water.name = Dark Sand Tainted Water +block.tar.name = Tar +block.stone.name = Stone +block.sand.name = Sand +block.darksand.name = Dark Sand +block.ice.name = Ice +block.snow.name = Snow +block.craters.name = Craters +block.sand-water.name = Sand water +block.darksand-water.name = Dark Sand Water +block.char.name = Char +block.holostone.name = Holo stone +block.ice-snow.name = Ice Snow +block.rocks.name = Rocks +block.icerocks.name = Ice rocks +block.snowrocks.name = Snow Rocks +block.dunerocks.name = Dune Rocks +block.pine.name = Pine +block.white-tree-dead.name = White Tree Dead +block.white-tree.name = White Tree +block.spore-cluster.name = Spore Cluster +block.metal-floor.name = Metal Floor 1 +block.metal-floor-2.name = Metal Floor 2 +block.metal-floor-3.name = Metal Floor 3 +block.metal-floor-5.name = Metal Floor 4 +block.metal-floor-damaged.name = Metal Floor Damaged +block.dark-panel-1.name = Dark Panel 1 +block.dark-panel-2.name = Dark Panel 2 +block.dark-panel-3.name = Dark Panel 3 +block.dark-panel-4.name = Dark Panel 4 +block.dark-panel-5.name = Dark Panel 5 +block.dark-panel-6.name = Dark Panel 6 +block.dark-metal.name = Dark Metal +block.ignarock.name = Igna Rock +block.hotrock.name = Hot Rock +block.magmarock.name = Magma Rock +block.cliffs.name = Cliffs +block.copper-wall.name = Copper Wall +block.copper-wall-large.name = Large Copper Wall +block.titanium-wall.name = Titanium Wall +block.titanium-wall-large.name = Large Titanium Wall +block.phase-wall.name = Phase Wall +block.phase-wall-large.name = Large Phase Wall +block.thorium-wall.name = Thorium Wall +block.thorium-wall-large.name = Large Thorium Wall +block.door.name = Door +block.door-large.name = Large Door +block.duo.name = Duo +block.scorch.name = Scorch +block.scatter.name = Scatter +block.hail.name = Hail +block.lancer.name = Lancer +block.conveyor.name = Conveyor +block.titanium-conveyor.name = Titanium Conveyor +block.junction.name = Junction +block.router.name = Router +block.distributor.name = Distributor +block.sorter.name = Sorter +block.sorter.description = Sorts items. If an item matches the selection, it is allowed to pass. Otherwise, the item is outputted to the left and right. +block.overflow-gate.name = Overflow Gate +block.overflow-gate.description = A combination splitter and router that only outputs to the left and right if the front path is blocked. +block.silicon-smelter.name = Silicon Smelter +block.phase-weaver.name = Phase Weaver +block.pulverizer.name = Pulverizer +block.cryofluidmixer.name = Cryofluid Mixer +block.melter.name = Melter +block.incinerator.name = Incinerator +block.spore-press.name = Spore Press +block.separator.name = Separator +block.coal-centrifuge.name = Coal Centrifuge +block.power-node.name = Power Node +block.power-node-large.name = Large Power Node +block.surge-tower.name = Surge Tower +block.battery.name = Battery +block.battery-large.name = Large Battery +block.combustion-generator.name = Combustion Generator +block.turbine-generator.name = Turbine Generator +block.differential-generator.name = Differential Generator +block.impact-reactor.name = Impact Reactor +block.mechanical-drill.name = Mechanical Drill +block.pneumatic-drill.name = Pneumatic Drill +block.laser-drill.name = Laser Drill +block.water-extractor.name = Water Extractor +block.cultivator.name = Cultivator +block.dart-mech-pad.name = Dart Mech Pad +block.delta-mech-pad.name = Delta Mech Pad +block.javelin-ship-pad.name = Javelin Ship Pad +block.trident-ship-pad.name = Trident Ship Pad +block.glaive-ship-pad.name = Glaive Ship Pad +block.omega-mech-pad.name = Omega Mech Pad +block.tau-mech-pad.name = Tau Mech Pad +block.conduit.name = Conduit +block.mechanical-pump.name = Mechanical Pump +block.item-source.name = Item Source +block.item-void.name = Item Void +block.liquid-source.name = Liquid Source +block.power-void.name = Power Void +block.power-source.name = Power Infinite +block.unloader.name = Unloader +block.vault.name = Vault +block.wave.name = Wave +block.swarmer.name = Swarmer +block.salvo.name = Salvo +block.ripple.name = Ripple +block.phase-conveyor.name = Phase Conveyor +block.bridge-conveyor.name = Bridge Conveyor +block.plastanium-compressor.name = Plastanium Compressor +block.pyratite-mixer.name = Pyratite Mixer +block.blast-mixer.name = Blast Mixer +block.solar-panel.name = Solar Panel +block.solar-panel-large.name = Large Solar Panel +block.oil-extractor.name = Oil Extractor +block.spirit-factory.name = Spirit Drone Factory +block.phantom-factory.name = Phantom Drone Factory +block.wraith-factory.name = Wraith Fighter Factory +block.ghoul-factory.name = Ghoul Bomber Factory +block.dagger-factory.name = Dagger Mech Factory +block.crawler-factory.name = Crawler Mech Factory +block.titan-factory.name = Titan Mech Factory +block.fortress-factory.name = Fortress Mech Factory +block.revenant-factory.name = Revenant Fighter Factory +block.repair-point.name = Repair Point +block.pulse-conduit.name = Pulse Conduit +block.phase-conduit.name = Phase Conduit +block.liquid-router.name = Liquid Router +block.liquid-tank.name = Liquid Tank +block.liquid-junction.name = Liquid Junction +block.bridge-conduit.name = Bridge Conduit +block.rotary-pump.name = Rotary Pump +block.thorium-reactor.name = Thorium Reactor +block.mass-driver.name = Mass Driver +block.blast-drill.name = Airblast Drill +block.thermal-pump.name = Thermal Pump +block.thermal-generator.name = Thermal Generator +block.alloy-smelter.name = Alloy Smelter +block.mender.name = Mender +block.mend-projector.name = Mend Projector +block.surge-wall.name = Surge Wall +block.surge-wall-large.name = Large Surge Wall +block.cyclone.name = Cyclone +block.fuse.name = Fuse +block.shock-mine.name = Shock Mine +block.overdrive-projector.name = Overdrive Projector +block.force-projector.name = Force Projector +block.arc.name = Arc +block.rtg-generator.name = RTG Generator +block.spectre.name = Spectre +block.meltdown.name = Meltdown +block.container.name = Container +block.launch-pad.name = Launch Pad +block.launch-pad.description = Launches batches of items without any need for a core launch. Unfinished. +block.launch-pad-large.name = Large Launch Pad +team.blue.name = blue +team.red.name = red +team.orange.name = orange +team.none.name = gray +team.green.name = green +team.purple.name = purple +unit.spirit.name = Spirit Drone +unit.spirit.description = The starter drone unit. Spawns in the core by default. Automatically mines ores and repairs blocks. +unit.phantom.name = Phantom Drone +unit.phantom.description = An advanced drone unit. Automatically mines ores and repairs blocks. Significantly more effective than a spirit drone. +unit.dagger.name = Dagger +unit.dagger.description = A basic ground unit. Useful in swarms. +unit.crawler.name = Crawler +unit.titan.name = Titan +unit.titan.description = An advanced, armored ground unit. Attacks both ground and air targets. +unit.ghoul.name = Ghoul Bomber +unit.ghoul.description = A heavy carpet bomber. +unit.wraith.name = Wraith Fighter +unit.wraith.description = A fast, hit-and-run interceptor unit. +unit.fortress.name = Fortress +unit.fortress.description = A heavy artillery ground unit. +unit.revenant.name = Revenant +unit.eruptor.name = Eruptor +unit.chaos-array.name = Chaos Array +unit.eradicator.name = Eradicator +unit.lich.name = Lich +unit.reaper.name = Reaper +tutorial.begin = Your mission here is to eradicate the[LIGHT_GRAY] enemy[].\n\nBegin by[accent] mining copper[]. Tap a copper ore vein near your core to do this. +tutorial.drill = Mining manually is inefficient.\n[accent]Drills []can mine automatically.\nPlace one on a copper vein. +tutorial.conveyor = [accent]Conveyors[] are used to transport items to the core.\nMake a line of conveyors from the drill to the core. +tutorial.morecopper = More copper is required.\n\nEither mine it manually, or place more drills. +tutorial.turret = Defensive structures must be built to repel the[LIGHT_GRAY] enemy[].\nBuild a duo turret near your base. +tutorial.drillturret = Duo turrets require[accent] copper ammo []to shoot.\nPlace a drill next to the turret to supply it with mined copper. +tutorial.waves = The[LIGHT_GRAY] enemy[] approaches.\n\nDefend your core for 2 waves. Build more turrets. +tutorial.lead = More ores are available. Explore and mine[accent] lead[].\n\nDrag from your unit to the core to transfer resources. +tutorial.smelter = Copper and lead are weak metals.\nSuperior[accent] Dense Alloy[] can be created in a smelter.\n\nBuild one. +tutorial.densealloy = The smelter will now produce alloy.\nGet some.\nImprove the production if necessary. +tutorial.siliconsmelter = The core will now create a[accent] spirit drone[] for mining and repairing blocks.\n\nFactories for other units can be created with [accent] silicon.\nMake a silicon smelter. +tutorial.silicondrill = Silicon requires[accent] coal[] and[accent] sand[].\nStart by making drills. +tutorial.generator = This technology requires power.\nCreate a[accent] combustion generator[] for it. +tutorial.generatordrill = Combustion generators need fuel.\nFuel it with coal from a drill. +tutorial.node = Power requires transport.\nCreate a[accent] power node[] next to your combustion generator to transfer its power. +tutorial.nodelink = Power can be transferred through contacting power blocks and generators, or by linked power nodes.\n\nLink power by tapping the node and selecting the generator and silicon smelter. +tutorial.silicon = Silicon is being produced. Get some.\n\nImproving the production system is advised. +tutorial.daggerfactory = Construct a[accent] dagger mech factory.[]\n\nThis will be used to create attack mechs. +tutorial.router = Factories need resources to function.\nCreate a router to split conveyor resources. +tutorial.dagger = Link power nodes to the factory.\nOnce requirements are met, a mech will be created.\n\nCreate more drills, generators and conveyors as necessary. +tutorial.battle = The[LIGHT_GRAY] enemy[] has revealed their core.\nDestroy it with your unit and dagger mechs. +block.copper-wall.description = A cheap defensive block.\nUseful for protecting the core and turrets in the first few waves. +block.copper-wall-large.description = A cheap defensive block.\nUseful for protecting the core and turrets in the first few waves.\nSpans multiple tiles. +block.thorium-wall.description = A strong defensive block.\nGood protection from enemies. +block.thorium-wall-large.description = A strong defensive block.\nGood protection from enemies.\nSpans multiple tiles. +block.phase-wall.description = Not as strong as a thorium wall but will deflect bullets unless they are too powerful. +block.phase-wall-large.description = Not as strong as a thorium wall but will deflect bullets unless they are too powerful.\nSpans multiple tiles. +block.surge-wall.description = The strongest defensive block.\nHas a small chance of triggering lightning towards the attacker. +block.surge-wall-large.description = The strongest defensive block.\nHas a small chance of triggering lightning towards the attacker.\nSpans multiple tiles. +block.door.description = A small door that can be opened and closed by tapping on it.\nIf opened, enemies can shoot and move through. +block.door-large.description = A large door that can be opened and closed by tapping on it.\nIf opened, enemies can shoot and move through.\nSpans multiple tiles. +block.mend-projector.description = Periodically heals blocks in its vicinity. +block.overdrive-projector.description = Increases the speed of nearby buildings like drills and conveyors. +block.force-projector.description = Creates a hexagonal force field around itself, protecting buildings and units inside from damage through bullets. +block.shock-mine.description = Damages enemies stepping on the mine. Nearly invisible to the enemy. +block.duo.description = A small, cheap turret. Useful against ground units. +block.scatter.description = A medium-sized anti-air turret. Sprays clumps of lead or scrap flak at enemy units. +block.arc.description = A small close-range turret which shoots electricity in a random arc towards the enemy. +block.hail.description = A small artillery turret. +block.lancer.description = A medium-sized turret which shoots charged electricity beams. +block.wave.description = A medium-sized rapid-fire turret which shoots liquid bubbles. +block.salvo.description = A medium-sized turret which fires shots in salvos. +block.swarmer.description = A medium-sized turret which shoots burst missiles. +block.ripple.description = A large artillery turret which fires several shots simultaneously. +block.cyclone.description = A large rapid fire turret. +block.fuse.description = A large turret which shoots powerful short-range beams. +block.spectre.description = A large turret which shoots two powerful bullets at once. +block.meltdown.description = A large turret which shoots powerful long-range beams. +block.conveyor.description = Basic item transport block. Moves items forward and automatically deposits them into turrets or crafters. Rotatable. +block.titanium-conveyor.description = Advanced item transport block. Moves items faster than standard conveyors. +block.phase-conveyor.description = Advanced item transport block. Uses power to teleport items to a connected phase conveyor over several tiles. +block.junction.description = Acts as a bridge for two crossing conveyor belts. Useful in situations with two different conveyors carrying different materials to different locations. +block.mass-driver.description = Ultimate item transport block. Collects several items and then shoots them to another mass driver over a long range. +block.silicon-smelter.description = Reduces sand with highly pure coal in order to produce silicon. +block.plastanium-compressor.description = Produces plastanium from oil and titanium. +block.phase-weaver.description = Produces phase fabric from radioactive thorium and high amounts of sand. +block.alloy-smelter.description = Produces surge alloy from titanium, lead, silicon and copper. +block.pulverizer.description = Crushes scrap into sand. Useful when there is a lack of natural sand. +block.pyratite-mixer.description = Mixes coal, lead and sand into highly flammable pyratite. +block.blast-mixer.description = Uses oil for transforming pyratite into the less flammable but more explosive blast compound. +block.cryofluidmixer.description = Combines water and titanium into cryofluid which is much more efficient for cooling. +block.melter.description = Melts down scrap into slag for further processing or usage in turrets. +block.incinerator.description = Gets rid of any excess item or liquid. +block.spore-press.description = Compresses spore pods into oil. +block.separator.description = Extracts useful minerals from slag. +block.power-node.description = Transmits power to connected nodes. Up to four power sources, sinks or nodes can be connected. The node will receive power from or supply power to any adjacent blocks. +block.power-node-large.description = Has a larger radius than the power node and connects to up to six power sources, sinks or nodes. +block.battery.description = Stores power whenever there is an abundance and provides power whenever there is a shortage, as long as there is capacity left. +block.battery-large.description = Stores much more power than a regular battery. +block.combustion-generator.description = Generates power by burning oil or flammable materials. +block.turbine-generator.description = More efficient than a combustion generator, but requires additional water. +block.thermal-generator.description = Generates power when placed in hot locations. +block.solar-panel.description = Provides a small amount of power from the sun. +block.solar-panel-large.description = Provides much better power supply than a standard solar panel, but is also much more expensive to build. +block.thorium-reactor.description = Generates huge amounts of power from highly radioactive thorium. Requires constant cooling. Will explode violently if insufficient amounts of coolant are supplied. Power output depends on fullness, with base power generated at full capacity. +block.rtg-generator.description = A radioisotope thermoelectric generator which does not require cooling but provides less power than a thorium reactor. +block.unloader.description = Unloads items from a container, vault or core onto a conveyor or directly into an adjacent block. The type of item to be unloaded can be changed by tapping on the unloader. +block.container.description = Stores a small amount of items of each type. An[LIGHT_GRAY] unloader[] can be used to retrieve items from the container. +block.vault.description = Stores a large amount of items of each type. An[LIGHT_GRAY] unloader[] can be used to retrieve items from the vault. +block.mechanical-drill.description = A cheap drill. When placed on appropriate tiles, outputs items at a slow pace indefinitely. +block.pneumatic-drill.description = An improved drill which is faster and able to process harder materials by making use of air pressure. +block.laser-drill.description = Allows drilling even faster through laser technology, but requires power. Additionally, radioactive thorium can be retrieved with this drill. +block.blast-drill.description = The ultimate drill. Requires large amounts of power. +block.water-extractor.description = Extracts water from the ground. Use it when there is no lake nearby. +block.cultivator.description = Cultivates tiny concentrations of spores into industry-ready pods. +block.oil-extractor.description = Uses large amounts of power in order to extract oil from sand. Use it when there is no direct source of oil nearby. +block.trident-ship-pad.description = Leave your current vessel and change into a reasonably well armored heavy bomber.\nUse the pad by double tapping while standing on it. +block.javelin-ship-pad.description = Leave your current vessel and change into a strong and fast interceptor with lightning weapons.\nUse the pad by double tapping while standing on it. +block.glaive-ship-pad.description = Leave your current vessel and change into a large, well-armored gunship.\nUse the pad by double tapping while standing on it. +block.tau-mech-pad.description = Leave your current vessel and change into a support mech which can heal friendly buildings and units.\nUse the pad by double tapping while standing on it. +block.delta-mech-pad.description = Leave your current vessel and change into a fast, lightly-armored mech made for hit-and-run attacks.\nUse the pad by double tapping while standing on it. +block.omega-mech-pad.description = Leave your current vessel and change into a bulky and well-armored mech, made for front-line assaults.\nUse the pad by double tapping while standing on it. +block.spirit-factory.description = Produces light drones which mine ore and repair blocks. +block.phantom-factory.description = Produces advanced drone units which are significantly more effective than a spirit drone. +block.wraith-factory.description = Produces fast, hit-and-run interceptor units. +block.ghoul-factory.description = Produces heavy carpet bombers. +block.dagger-factory.description = Produces basic ground units. +block.titan-factory.description = Produces advanced, armored ground units. +block.fortress-factory.description = Produces heavy artillery ground units. +block.revenant-factory.description = Produces heavy laser air units. +block.repair-point.description = Continuously heals the closest damaged unit in its vicinity. +block.conduit.description = Basic liquid transport block. Works like a conveyor, but with liquids. Best used with extractors, pumps or other conduits. +block.pulse-conduit.description = Advanced liquid transport block. Transports liquids faster and stores more than standard conduits. +block.phase-conduit.description = Advanced liquid transport block. Uses power to teleport liquids to a connected phase conduit over several tiles. +block.liquid-router.description = Accepts liquids from one direction and outputs them to up to 3 other directions equally. Can also store a certain amount of liquid. Useful for splitting the liquids from one source to multiple targets. +block.liquid-tank.description = Stores a large amount of liquids. Use it for creating buffers when there is a non-constant demand of materials or as a safeguard for cooling vital blocks. +block.liquid-junction.description = Acts as a bridge for two crossing conduits. Useful in situations with two different conduits carrying different liquids to different locations. +block.bridge-conduit.description = Advanced liquid transport block. Allows transporting liquids over up to 3 tiles of any terrain or building. +block.mechanical-pump.description = A cheap pump with slow output, but no power consumption. +block.rotary-pump.description = An advanced pump which doubles up speed by using power. +block.thermal-pump.description = The ultimate pump. +block.router.description = Accepts items from one direction and outputs them to up to 3 other directions equally. Useful for splitting the materials from one source to multiple targets. +block.distributor.description = An advanced router which splits items to up to 7 other directions equally. +block.bridge-conveyor.description = Advanced item transport block. Allows transporting items over up to 3 tiles of any terrain or building. +block.item-source.description = Infinitely outputs items. Sandbox only. +block.liquid-source.description = Infinitely outputs liquids. Sandbox only. +block.item-void.description = Destroys any items which go into it without using power. Sandbox only. +block.power-source.description = Infinitely outputs power. Sandbox only. +block.power-void.description = Voids all power inputted into it. Sandbox only. +liquid.water.description = Commonly used for cooling machines and waste processing. +liquid.oil.description = Can be burnt, exploded or used as a coolant. +liquid.cryofluid.description = The most efficient liquid for cooling things down. diff --git a/core/assets/bundles/bundle_pl.properties b/core/assets/bundles/bundle_pl.properties index 08131a3ddc..2bc43010e6 100644 --- a/core/assets/bundles/bundle_pl.properties +++ b/core/assets/bundles/bundle_pl.properties @@ -1,488 +1,947 @@ -text.about = Stworzony przez [ROYAL] Anuken. []\nPierwotnie wpis w [orange] GDL [] MM Jam.\n\nNapisy:\n- SFX wykonane z pomocą [YELLOW] bfxr []\n- Muzyka wykonana przez [GREEN] RoccoW [] / znaleziona na [lime] FreeMusicArchive.org []\n\nSpecjalne podziękowania dla:\n- [coral] MitchellFJN []: obszerne testowanie i feedback\n- [niebo] Luxray5474 []: prace związane z wiki, pomoc z kodem\n- Wszystkich beta testerów na itch.io i Google Play\n -text.discord = Odwiedź nasz serwer Discord -text.gameover = Rdzeń został zniszczony. -text.highscore = [YELLOW] Nowy rekord! -text.lasted = Wytrwałeś do fali -text.level.highscore = Rekord: [accent]{0} -text.level.delete.title = Potwierdź kasowanie -text.level.delete = Czy na pewno chcesz usunąć mapę \"[orange]{0}\"? -text.level.select = Wybrany poziom -text.level.mode = Tryb gry: -text.savegame = Zapisz Grę -text.loadgame = Wczytaj grę -text.joingame = Gra wieloosobowa -text.quit = Wyjdź -text.about.button = O grze -text.name = Nazwa: -text.public = Publiczny -text.players = {0} graczy online -text.server.player.host = {0} (host) -text.players.single = {0} gracz online -text.server.mismatch = Błąd pakietu: możliwa niezgodność wersji klienta/serwera.\nUpewnij się, że Ty i host macie najnowszą wersję Mindustry! -text.server.closing = [accent] Zamykanie serwera ... -text.server.kicked.kick = Zostałeś wyrzucony z serwera! -text.server.kicked.invalidPassword = Nieprawidłowe hasło! -text.server.kicked.clientOutdated = Nieaktualna gra! Zaktualizują ją! -text.server.kicked.serverOutdated = Nieaktualna gra! Zaktualizują ją! -text.server.connected = {0} dołączył do gry . -text.server.disconnected = {0} został rozłączony. -text.nohost = Nie można hostować serwera na mapie niestandardowej! -text.hostserver = Serwer hosta -text.host = Host -text.hosting = [accent] Otwieranie serwera ... -text.hosts.refresh = Odśwież -text.hosts.discovering = Wyszukiwanie gier w sieci LAN -text.server.refreshing = Odświeżanie serwera -text.hosts.none = [lightgray] Brak serwerów w sieci LAN! -text.host.invalid = [scarlet] Nie można połączyć się z hostem. -text.server.friendlyfire = Bratobójczy ogień -text.server.add = Dodaj serwer -text.server.delete = Czy na pewno chcesz usunąć ten serwer? -text.server.hostname = Host: {0} -text.server.edit = Edytuj serwer -text.joingame.byip = Dołącz przez IP... -text.joingame.title = Dołącz do gry -text.joingame.ip = IP: -text.disconnect = Rozłączony. -text.connecting = [accent]Łączenie ... -text.connecting.data = [accent]Ładowanie danych świata... -text.connectfail = [crimson]Nie można połączyć się z serwerem: [orange] {0} -text.server.port = Port: -text.server.addressinuse = Adres jest już w użyciu! -text.server.invalidport = Nieprawidłowy numer portu. -text.server.error = [crimson] Błąd hostowania serwera: [orange] {0} -text.tutorial.back = < Cofnij -text.tutorial.next = Dalej > -text.save.new = Nowy zapis -text.save.overwrite = Czy na pewno chcesz nadpisać zapis gry? -text.overwrite = Nadpisz -text.save.none = Nie znaleziono zapisów gry! -text.saveload = [akcent]Zapisywanie... -text.savefail = Nie udało się zapisać gry! -text.save.delete.confirm = Czy na pewno chcesz usunąć ten zapis gry? -text.save.delete = Usuń -text.save.export = Eksportuj -text.save.import.invalid = [orange]Zapis gry jest niepoprawny! -text.save.import.fail = [crimson]Nie udało się zaimportować zapisu: [orange] {0} -text.save.export.fail = [crimson]Nie można wyeksportować zapisu: [orange] {0} -text.save.import = Importuj -text.save.newslot = Zapisz nazwę: -text.save.rename = Zmień nazwę -text.save.rename.text = Zmień nazwę -text.selectslot = Wybierz zapis. -text.slot = [accent]Slot {0} -text.save.corrupted = [orange]Zapis gry jest uszkodzony lub nieprawidłowy! -text.empty = -text.on = Włączone -text.off = Wyłączone -text.save.autosave = Zapisywanie automatyczne -text.save.map = Mapa: {0} -text.save.wave = Fala: {0} -text.save.date = Ostatnio zapisano: {0} -text.confirm = Potwierdź -text.delete = Usuń -text.ok = Ok -text.open = Otwórz -text.cancel = Anuluj -text.openlink = Otwórz link -text.back = Wróć -text.quit.confirm = Czy na pewno chcesz wyjść? -text.loading = [accent]Ładowanie ... -text.wave = [orange]Fala {0} -text.wave.waiting = Fala w {0} -text.waiting = Oczekiwanie... -text.enemies = {0} wrogów -text.enemies.single = {0} wróg -text.loadimage = Załaduj obraz -text.saveimage = Zapisz obraz -text.editor.badsize = [orange]Nieprawidłowe wymiary obrazu![]\nWymiary poprawne: {0} -text.editor.errorimageload = Błąd podczas ładowania pliku obrazu: [orange]{0} -text.editor.errorimagesave = Błąd podczas zapisywania pliku obrazu: [orange]{0} -text.editor.generate = Generuj -text.editor.resize = Zmień rozmiar -text.editor.loadmap = Załaduj mapę -text.editor.savemap = Zapisz mapę -text.editor.loadimage = Załaduj obraz -text.editor.saveimage = Zapisz obraz -text.editor.unsaved = [scarlet]Masz niezapisane zmiany![]\nCzy na pewno chcesz wyjść? -text.editor.brushsize = Rozmiar pędzla: {0} -text.editor.noplayerspawn = Ta mapa nie ma ustawionego spawnu gracza! -text.editor.manyplayerspawns = Mapy nie mogą mieć więcej niż jeden punkt spawnu gracza! -text.editor.manyenemyspawns = Nie może mieć więcej niż {0} punktów spawnu wroga! -text.editor.resizemap = Zmień rozmiar mapy -text.editor.resizebig = [scarlet]Uwaga![]\nMapy większe niż 256 jednostek mogą przycinać i być niestabilne. -text.editor.mapname = Nazwa mapy: -text.editor.overwrite = [accent]Uwaga!\nSpowoduje to nadpisanie istniejącej mapy. -text.editor.failoverwrite = [crimson]Nie można nadpisać mapy podstawowej! -text.editor.selectmap = Wybierz mapę do załadowania: -text.width = Szerokość: -text.height = Wysokość: -text.randomize = Wylosuj -text.apply = Zastosuj -text.update = Zaktualizuj -text.menu = Menu -text.play = Graj -text.load = Wczytaj -text.save = Zapisz -text.language.restart = Uruchom grę ponownie aby ustawiony język zaczął funkcjonować. -text.settings.language = Język -text.settings = Ustawienia -text.tutorial = Poradnik -text.editor = Edytor -text.mapeditor = Edytor map -text.donate = Wspomóż nas -text.settings.reset = Przywróć domyślne -text.settings.controls = Sterowanie -text.settings.game = Gra -text.settings.sound = Dźwięk -text.settings.graphics = Grafika -text.upgrades = Ulepszenia -text.purchased = [LIME]Stworzono! -text.weapons = Bronie -text.paused = Wstrzymano -text.respawn = Odrodzenie za -text.info.title = [accent]Informacje -text.error.title = [crimson]Wystąpił błąd -text.error.crashmessage = [SCARLET]Wystąpił nieoczekiwany błąd, który spowodowałby awarię.[]\nProszę, powiadom dewelopera gry o tym błędzie, pisząc jak do niego doszło: [ORANGE]anukendev@gmail.com[] -text.error.crashtitle = Wystąpił błąd -text.mode.break = Tryb przerw: {0} -text.mode.place = Tryb układania: {0} -placemode.hold.name = linia -placemode.areadelete.name = obszar -placemode.touchdelete.name = Dotyk -placemode.holddelete.name = Przytrzymać -placemode.none.name = żaden -placemode.touch.name = Dotyk -placemode.cursor.name = kursor -text.blocks.extrainfo = [accent]Dodatkowe informacje o bloku: -text.blocks.blockinfo = Informacje o bloku -text.blocks.powercapacity = Moc znamionowa -text.blocks.powershot = moc / strzał -text.blocks.powersecond = moc / sekunda -text.blocks.powerdraindamage = siła ataku / obrażenia -text.blocks.shieldradius = Promień osłony -text.blocks.itemspeedsecond = prędkość ​​/ sekundy -text.blocks.range = Zakres -text.blocks.size = Rozmiar -text.blocks.powerliquid = moc / ciecz -text.blocks.maxliquidsecond = maksymalna ilość cieczy / sekunda -text.blocks.liquidcapacity = Pojemność cieczy -text.blocks.liquidsecond = ciecz / sekunda -text.blocks.damageshot = obrażenia / strzał -text.blocks.ammocapacity = Pojemność amunicji -text.blocks.ammo = Amunicja: -text.blocks.ammoitem = amunicja / przedmiot -text.blocks.maxitemssecond = Maksymalna liczba przedmiotów / Sekunda -text.blocks.powerrange = Zakres mocy -text.blocks.lasertilerange = Zasięg lasera -text.blocks.capacity = Wydajność -text.blocks.itemcapacity = Pojemność przedmiotów -text.blocks.maxpowergenerationsecond = maksymalne generowanie energii / sekunda -text.blocks.powergenerationsecond = wytwarzanie energii / sekunda -text.blocks.generationsecondsitem = sekunda / przedmiot -text.blocks.input = Wkład -text.blocks.inputliquid = Potrzebna ciecz -text.blocks.inputitem = Potrzebne przedmioty -text.blocks.output = Wyjście -text.blocks.secondsitem = sekundy / przedmiot -text.blocks.maxpowertransfersecond = maksymalny transfer mocy / sekundę -text.blocks.explosive = Wysoce wybuchowy! -text.blocks.repairssecond = naprawa / sekunda -text.blocks.health = Zdrowie -text.blocks.inaccuracy = Niedokładność -text.blocks.shots = Strzały -text.blocks.shotssecond = Strzały / Sekunda -text.blocks.fuel = Paliwo -text.blocks.fuelduration = Wydajność paliwa -text.blocks.maxoutputsecond = maksymalne wyjście / sekunda -text.blocks.inputcapacity = Pojemność wejściowa -text.blocks.outputcapacity = Wydajność wyjściowa -text.blocks.poweritem = moc / przedmiot -text.placemode = Tryb miejsca -text.breakmode = Tryb przerwania -text.health = Zdrowie: +credits.text = Created by [ROYAL]Anuken[] - [SKY]anukendev@gmail.com[]\n\n[GRAY](In case you can't tell, this text is currently unfinished.\nTranslators, don't edit it yet!) +credits = Zasłużeni +contributors = Tłumacze i pomocnicy +discord = Odwiedź nasz serwer Discord! +link.discord.description = Oficjalny serwer Discord Mindustry +link.github.description = Kod Gry +link.dev-builds.description = Niestabilne wersje gry +link.trello.description = Oficjalna tablica Trello z planowanym funkcjami +link.itch.io.description = Strona itch.io z oficjanymi wersjami do pobrania +link.google-play.description = Strona na sklepie Google Play +link.wiki.description = Oficjana Wiki Mindustry +linkfail = Nie udało się otworzyć linku!\nURL został skopiowany. +screenshot = Zapisano zdjęcie do {0} +screenshot.invalid = Zrzut ekranu jest zbyt duży. Najprawdopodobniej brakuje miejsca w pamięci urządzenia. +gameover = Rdzeń został zniszczony. +gameover.pvp = Zwyciężyła drużyna [accent]{0}[]! +highscore = [YELLOW] Nowy rekord! +stat.wave = Fale powstrzymane:[accent] {0} +stat.enemiesDestroyed = Przeciwnicy zniszczeni:[accent] {0} +stat.built = Budynki zbudowane:[accent] {0} +stat.destroyed = Budynki zniszczone:[accent] {0} +stat.deconstructed = Budynki zrekonstruowane:[accent] {0} +stat.delivered = Surowce wystrzelone: +stat.rank = Final Rank: [accent]{0} +placeline = You have selected a block.\nYou can[accent] place in a line[] by[accent] holding down your finger for a few seconds[] and dragging in a direction.\nTry it. +removearea = You have selected removal mode.\nYou can[accent] remove blocks in a rectangle[] by[accent] holding down your finger for a few seconds[] and dragging.\nTry it. +launcheditems = [accent]Wystrzelone przedmioty +map.delete = Jesteś pewny, że chcesz usunąć "[accent]{0}[]"? +level.highscore = Rekord: [accent]{0} +level.select = Wybrany poziom +level.mode = Tryb gry: +showagain = Nie pokazuj tego więcej +coreattack = < Rdzeń jest atakowany! > +nearpoint = [[ [scarlet]OPUŚĆ PUNKT ZRZUTU NATYCHMIAST[] ]\n unicestwienie nadchodzi +outofbounds = [[ POZA GRANICAMI ]\n[]samozniszcenie za {0} sekund +database = Centralna baza danych +savegame = Zapisz Grę +loadgame = Wczytaj grę +joingame = Gra wieloosobowa +addplayers = Dodaj/Usuń graczy +customgame = Własna Gra +newgame = Nowa Gra +none = +minimap = Minimapa +close = Zamknij +quit = Wyjdź +maps = Mapy +continue = Kontynuuj +maps.none = [LIGHT_GRAY]Nie znaleziono żadnych map! +about.button = O grze +name = Nazwa: +noname = Najpierw wybierz [accent]nazwę gracza[] +filename = Nazwa Pliku: +unlocked = Odblokowano nowy blok! +completed = [accent]Ukończony +techtree = Drzewo Technologiczne +research.list = [LIGHT_GRAY]Badania: +research = Badaj +researched = [LIGHT_GRAY]{0} zbadane. +players = {0} graczy online +players.single = {0} gracz online +server.closing = [accent] Zamykanie serwera... +server.kicked.kick = Zostałeś wyrzucony z serwera! +server.kicked.serverClose = Serwer został zamknięty. +server.kicked.clientOutdated = Nieaktualna gra! Zaktualizują ją! +server.kicked.serverOutdated = Nieaktualny serwer! Poproś hosta o jego aktualizację. +server.kicked.banned = Zostałeś zbanowany na tym serwerze. +server.kicked.recentKick = Zostałeś niedawno wyrzucony.\nPoczekaj chwilę przed ponownym połączniem. +server.kicked.nameInUse = Ta nazwa jest już zajęta na tym serwerze. +server.kicked.nameEmpty = Wybrana przez Ciebie nazwa jest nieprawidłowa. +server.kicked.idInUse = Jesteś już na serwerze! Używanie tego samego konta na 2 urządzeniach jest zabronione. +server.kicked.customClient = Ten serwer nie wspomaga wersji deweloperskich. Pobierz oficjalną wersję. +server.kicked.gameover = Koniec gry! +host.info = Przycisk [accent]host[] hostuje serwer na porcie [scarlet]6567[] i [scarlet]6568.[]\nKtokolwiek z tym samym [LIGHT_GRAY]wifi lub hotspotem[] powinien zobaczyć twój serwer.\n\nJeśli chcesz, aby każdy z twoim IP mógł dołączyć, [accent]przekierowywanie portów[] jest potrzebne.\n\n[LIGHT_GRAY]Notka:Jeśli ktokolwiek ma problem z dołączeniem do gry, upewnij się, że udostępniłeś Mindustry dostęp do sieci. +join.info = Tutaj możesz wpisać [accent]IP serwera[], aby dołączyć lub wyszukaj [accent]serwery w lokalnej sieci[], do których chcesz dołączyć .\nGra wieloosobowa na LAN i WAN jest wspomagana.\n\n[LIGHT_GRAY]Notka: Nie ma automatycznej listy wszystkich serwerów; jeśli chcesz dołączyć przez IP, musisz zapytać się hosta o IP. +hostserver = Stwórz Serwer +hostserver.mobile = Hostuj\ngrę +host = Hostuj +hosting = [accent] Otwieranie serwera... +hosts.refresh = Odśwież +hosts.discovering = Wyszukiwanie gier w sieci LAN +server.refreshing = Odświeżanie serwera +hosts.none = [lightgray] Brak serwerów w sieci LAN! +host.invalid = [scarlet] Nie można połączyć się z hostem. +trace = Zlokalizuj gracza +trace.playername = Nazwa gracza: [accent]{0} +trace.ip = IP: [accent]{0} +trace.id = Wyjątkowe ID: [accent]{0} +trace.mobile = Mobile Client: [accent]{0} +trace.modclient = Zmodowany klient: [accent]{0} +invalidid = Złe ID klienta! Udostępnij raport błędu. +server.bans = Bany +server.bans.none = Nie znaleziono zbanowanych osób! +server.admins = Admini +server.admins.none = Nie znaleziono adminów! +server.add = Dodaj serwer +server.delete = Czy na pewno chcesz usunąć ten serwer? +server.hostname = Host: {0} +server.edit = Edytuj serwer +server.outdated = [crimson]Przestarzały serwer![] +server.outdated.client = [crimson]Przestarzały klient![] +server.version = [lightgray]Wersja: {0} +server.custombuild = [yellow]Zmodowany klient +confirmban = Jesteś pewny, że chcesz zbanować tego gracza? +confirmkick = Jesteś pewny, że chcesz wyrzucić tego gracza? +confirmunban = Jesteś pewny, że chcesz odbanować tego gracza? +confirmadmin = Jesteś pewny, że chcesz dać rangę admina temu graczowi? +confirmunadmin = Jesteś pewny, że chcesz zabrać rangę admina temu graczowi? +joingame.title = Dołącz do gry +joingame.ip = IP: +disconnect = Rozłączono. +disconnect.data = Nie udało się załadować mapy! +connecting = [accent]Łączenie... +connecting.data = [accent]Ładowanie danych świata... +server.port = Port: +server.addressinuse = Adres jest już w użyciu! +server.invalidport = Nieprawidłowy numer portu. +server.error = [crimson]Błąd hostowania serwera: [accent]{0} +save.old = Ten zapis jest ze starej wersji i gra nie może go teraz wczytać.\n\n[LIGHT_GRAY]Wsparcie starych zapisów będzie w pełnej wersji 4.0. +save.new = Nowy zapis +save.overwrite = Czy na pewno chcesz nadpisać zapis gry? +overwrite = Nadpisz +save.none = Nie znaleziono zapisów gry! +saveload = [accent]Zapisywanie... +savefail = Nie udało się zapisać gry! +save.delete.confirm = Czy na pewno chcesz usunąć ten zapis gry? +save.delete = Usuń +save.export = Eksportuj +save.import.invalid = [accent]Zapis gry jest niepoprawny! +save.import.fail = [crimson]Nie udało się zaimportować zapisu: [accent] {0} +save.export.fail = [crimson]Nie można wyeksportować zapisu: [accent] {0} +save.import = Importuj zapis +save.newslot = Zapisz nazwę: +save.rename = Zmień nazwę +save.rename.text = Nowa nazwa: +selectslot = Wybierz zapis. +slot = [accent]Slot {0} +save.corrupted = [accent]Zapis gry jest uszkodzony lub nieprawidłowy! Jeżeli aktualizowałeś grę, najprawdopodobniej zmiana w formacie zapisu i [scarlet]nie jest[] to błąd. +empty = +on = Włączone +off = Wyłączone +save.autosave = Autozapis: {0} +save.map = Mapa: {0} +save.wave = Fala {0} +save.difficulty = Poziom trudności: {0} +save.date = Ostatnio zapisano: {0} +save.playtime = Czas gry: {0} +warning = Uwaga. +confirm = Potwierdź +delete = Usuń +ok = Ok +open = Otwórz +customize = Customize +cancel = Anuluj +openlink = Otwórz link +copylink = Kopiuj link +back = Wróć +quit.confirm = Czy na pewno chcesz wyjść? +changelog.title = Lista Zmian +changelog.loading = Pobieranie listy zmian... +changelog.error.android = [accent]Notka: lista zmian czasami nie działa na Androidzie 4.4 i w dół!\nJest to spowodowane przez błąd Androida. +changelog.error.ios = [accent]Lista zmian nie jest wspierana przez IOS. +changelog.error = [scarlet]Błąd podczas pobierania listy zmian!\nSprawdź połączenie z internetem. +changelog.current = [yellow][[Twoja wersja] +changelog.latest = [accent][[Najnowsza wersja] +loading = [accent]Ładowanie... +saving = [accent]Zapisywanie... +wave = [accent]Fala {0} +wave.waiting = Fala za {0} +wave.waveInProgress = [LIGHT_GRAY]Wave in progress +waiting = [LIGHT_GRAY]Oczekiwanie... +waiting.players = Oczekiwanie na graczy... +wave.enemies = Pozostało [LIGHT_GRAY]{0} wrogów +wave.enemy = Pozostał [LIGHT_GRAY]{0} wróg +loadimage = Załaduj obraz +saveimage = Zapisz obraz +unknown = Nieznane +custom = Własne +builtin = Wbudowane +map.delete.confirm = Jesteś pewny, że chcesz usunąć tę mapę? Nie będzie można jej przywrócić. +map.random = [accent]Losowa mapa +map.nospawn = Ta mapa nie zawiera żadnego rdzenia! Dodaj [ROYAL]niebieski[] rdzeń do tej mapy w edytorze. +map.nospawn.pvp = Ta mapa nie ma żadnego rdzenia przeciwnika, aby mogli się zrespić przeciwnicy! Dodaj[SCARLET] inny niż niebieski[] rdzeń do mapy w edytorze. +map.nospawn.attack = Ta mapa nie ma żadnego rdzenia przeciwnika, aby można było go zaatakować! Dodaj[SCARLET] czerwony[] rdzeń do mapy w edytorze. +map.invalid = Błąd podczas ładowania mapy: uszkodzony lub niepoprawny plik mapy. +editor.brush = Pędzel +editor.openin = Otwórz w edytorze +editor.oregen = Generacja złóż +editor.oregen.info = Generacja złóż: +editor.mapinfo = Informacje o mapie +editor.author = Autor: +editor.description = Opis: +editor.waves = Fale: +editor.rules = Rules: +editor.ingame = Edit In-Game +waves.title = Fale +waves.remove = Usuń +waves.never = +waves.every = co +waves.waves = wave(s) +waves.perspawn = per spawn +waves.to = do +waves.boss = Boss +waves.preview = Preview +waves.edit = Edytuj... +waves.copy = Kopiuj do schowka +waves.load = Załaduj ze schowka +waves.invalid = Invalid waves in clipboard. +waves.copied = Fale zostały skopiowane. +editor.default = [LIGHT_GRAY] +edit = Edytuj... +editor.name = Nazwa: +editor.spawn = Spawn Unit +editor.removeunit = Remove Unit +editor.teams = Drużyny +editor.elevation = Poziom terenu +editor.errorload = Błąd podczas ładowania pliku:\n[accent]{0} +editor.errorsave = Błąd podczas zapisywania pliku:\n[accent]{0} +editor.errorimage = That's an image, not a map. Don't go around changing extensions expecting it to work.\n\nIf you want to import a legacy map, use the 'import legacy map' button in the editor. +editor.errorlegacy = This map is too old, and uses a legacy map format that is no longer supported. +editor.errorheader = This map file is either not valid or corrupt. +editor.errorname = Mapa nie zawiera nazwy. +editor.update = Aktualizuj +editor.randomize = Losuj +editor.apply = Zastosuj +editor.generate = Generuj +editor.resize = Zmień rozmiar +editor.loadmap = Załaduj mapę +editor.savemap = Zapisz mapę +editor.saved = Zapisano! +editor.save.noname = Twoja mapa nie ma nazwy! Ustaw ją w 'Informacjach o mapie'. +editor.save.overwrite = Ta mapa nadpisze wbudowaną mapę! Ustaw inną nazwę w 'Informacjach o mapie'. +editor.import.exists = [scarlet]Nie można zaimportować:[] wbudowana mapa o nazwie '{0}' już istnieje! +editor.import = Importuj... +editor.importmap = Importuj Mapę +editor.importmap.description = Importuj istniejącą mapę +editor.importfile = Importuj Plik +editor.importfile.description = Importuj zewnętrzny plik mapy +editor.importimage = Importuj Obraz Terenu +editor.importimage.description = Importuj zewnętrzny obraz terenu +editor.export = Eksportuj... +editor.exportfile = Eksportuj plik +editor.exportfile.description = Eksportuj plik mapy +editor.exportimage = Eksportuj Obraz Terenu +editor.exportimage.description = Eksportuj plik obrazu terenu +editor.loadimage = Załaduj obraz +editor.saveimage = Zapisz obraz +editor.unsaved = [scarlet]Masz niezapisane zmiany![]\nCzy na pewno chcesz wyjść? +editor.resizemap = Zmień rozmiar mapy +editor.mapname = Nazwa mapy: +editor.overwrite = [accent]Uwaga!\nSpowoduje to nadpisanie istniejącej mapy. +editor.overwrite.confirm = [scarlet]Uwaga![] Mapa pod tą nazwą już istnieje. Jesteś pewny, że chcesz ją nadpisać? +editor.selectmap = Wybierz mapę do załadowania: +filters.empty = [LIGHT_GRAY]Bez filtrów! Dodaj jeden za pomocą przycisku poniżej. +filter.distort = Distort +filter.noise = Szum +filter.ore = Ruda +filter.rivernoise = Szum rzeki +filter.scatter = Zozprosz +filter.terrain = Teren +filter.option.scale = Skala +filter.option.chance = Szansa +filter.option.mag = Magnitude +filter.option.threshold = Próg +filter.option.circle-scale = Skala koła +filter.option.octaves = Oktawy +filter.option.falloff = Falloff +filter.option.block = Blok +filter.option.floor = Podłoga +filter.option.wall = Ściana +filter.option.ore = Ruda +filter.option.floor2 = Druga podłoga +filter.option.threshold2 = Secondary Threshold +filter.option.radius = Radius +filter.option.percentile = Percentile +width = Szerokość: +height = Wysokość: +menu = Menu +play = Graj +load = Wczytaj +save = Zapisz +fps = FPS: {0} +tps = TPS: {0} +ping = Ping: {0}ms +language.restart = Uruchom grę ponownie, aby ustawiony język zaczął funkcjonować. +settings = Ustawienia +tutorial = Poradnik +editor = Edytor +mapeditor = Edytor map +donate = Wspomóż nas +abandon = Opuść +abandon.text = Ta strefa i wszystkie jej surowce będą przejęte przez przeciwników. +locked = Zablokowane +complete = [LIGHT_GRAY]Ukończone: +zone.requirement = Fala {0} w strefie {1} +resume = Kontynuuj Strefę:\n[LIGHT_GRAY]{0} +bestwave = [LIGHT_GRAY]Najwyższa fala: {0} +launch = < WYSTRZEL > +launch.title = Wystrzelenie Udane +launch.next = [LIGHT_GRAY]Następna okazja przy fali {0} +launch.unable = [scarlet]Nie można wystrzelić![] Wykryto {0} przeciwników. +launch.confirm = Spowoduje to wystrzelenie wszystkich surowców w rdzeniu.\nNie będziesz mógł wrócić do tej bazy. +uncover = Odkryj +configure = Skonfiguruj ładunek +configure.locked = [LIGHT_GRAY]Dotrzyj do fali {0}\nAby skonfigurować ładunek. +zone.unlocked = [LIGHT_GRAY]Strefa {0} odblokowana. +zone.requirement.complete = Fala {0} osiągnięta:\n{1} Wymagania strefy zostały spełnione. +zone.config.complete = Fala {0} osiągnięta:\nKonfiguracja ładunku odblokowana. +zone.resources = Wykryte Zasoby: +add = Dodaj... +boss.health = Boss Health +connectfail = [crimson]Nie można połączyć się z serwerem:\n\n[accent]{0} +error.unreachable = Serwer niedostępny.\nCzy adres jest wpisany poprawnie? +error.invalidaddress = Niepoprawny adres. +error.timedout = Przekroczono limit czasu!/nUpewnij się, że host ma ustawione przekierowanie portu oraz, czy adres jest poprawny! +error.mismatch = Błąd pakietu:\nprawdopodobne niedopasowanie klienta/serwera.\nUpewnij się, że ty i host macie najnowszą wersję Mindustry! +error.alreadyconnected = Jesteś już połączony. +error.mapnotfound = Plik mapy nie został znaleziony! +error.io = Błąd siecowy I/O. +error.any = Nieznany błąd sieci. +zone.groundZero.name = Wybuch Lądowy +zone.desertWastes.name = Desert Wastes +zone.craters.name = Kratery +zone.frozenForest.name = Zamrożony Las +zone.ruinousShores.name = Zniszczone Przybrzerza +zone.stainedMountains.name = Zabarwione Góry +zone.desolateRift.name = Ponura Szczelina +zone.nuclearComplex.name = Centrum Wyrobu Jądrowego +zone.overgrowth.name = Overgrowth +zone.tarFields.name = Tar Fields +settings.language = Język +settings.reset = Przywróć domyślne +settings.rebind = Zmień +settings.controls = Sterowanie +settings.game = Gra +settings.sound = Dźwięk +settings.graphics = Grafika +settings.cleardata = Wyczyść dane gry... +settings.clear.confirm = Czy jesteś pewien że chcesz usunąć te dane?\nPo tym nie ma powrotu! +settings.clearall.confirm = [scarlet]UWAGA![]\nTo wykasuje wszystkie dane, włącznie z zapisanymi grami i mapami, ustawienami, i znanymi technologiami.\nKiedy naciśniesz 'ok', gra usunie wszystkie swoje dane i automatycznie wyłączy się. +settings.clearunlocks = Wyczyść listę przedmiotów +settings.clearall = Wyczyść wszystko +paused = [accent]< Wstrzymano > +yes = Jasne! +no = Nie ma mowy! +info.title = Informacje +error.title = [crimson]Wystąpił błąd +error.crashtitle = Wystąpił błąd +blocks.input = Wejście +blocks.output = Wyjście +blocks.booster = Wzmacniacz +block.unknown = [LIGHT_GRAY]??? +blocks.powercapacity = Pojemność mocy +blocks.powershot = moc/strzał +blocks.targetsair = Namierzanie wrogów powietrznych +blocks.targetsground = Namierzanie wrogów lądowych +blocks.itemsmoved = Prędkość poruszania się +blocks.launchtime = Czas pomiędzy wystrzeleniami +blocks.shootrange = Zasięg +blocks.size = Rozmiar +blocks.liquidcapacity = Pojemność cieczy +blocks.powerrange = Zakres mocy +blocks.poweruse = Zużycie prądu +blocks.powerdamage = Moc/Zniszczenia +blocks.itemcapacity = Pojemność przedmiotów +blocks.basepowergeneration = Podstawowa generacja mocy +blocks.productiontime = Czas produkcji +blocks.repairtime = Czas pełnej naprawy bloku +blocks.speedincrease = Speed Increase +blocks.range = Zasięg +blocks.drilltier = Co może wykopać +blocks.drillspeed = Postawowa szybkość kopania +blocks.boosteffect = Efekt wzmocnienia +blocks.maxunits = Maksymalna ilość jednostek +blocks.health = Zdrowie +blocks.buildtime = Build Time +blocks.inaccuracy = Niedokładność +blocks.shots = Strzały +blocks.reload = Strzałów/sekundę +blocks.ammo = Amunicja +bar.drillspeed = Prędkość wiertła: {0}/s +bar.efficiency = Efektywność: {0}% +bar.powerbalance = Moc: {0} +bar.poweramount = Moc: {0} +bar.poweroutput = Wyjście mocy: {0} +bar.items = Przedmiotów: {0} +bar.liquid = Płyn +bar.heat = Ciepło +bar.power = Prąd +bar.progress = Postęp Budowy +bar.spawned = Jednostki: {0}/{1} +bullet.damage = [stat]{0}[lightgray] Obrażenia +bullet.splashdamage = [stat]{0}[lightgray] Obrażenia obszarowe ~[stat] {1}[lightgray] kratki +bullet.incendiary = [stat]zapalający +bullet.homing = [stat]naprowadzający +bullet.shock = [stat]piorunoey +bullet.frag = [stat]fragmentacyjny +bullet.knockback = [stat]{0}[lightgray] odrzut +bullet.freezing = [stat]zamrażający +bullet.tarred = [stat]smolny +bullet.multiplier = [stat]{0}[lightgray]x mnożnik amunicji +bullet.reload = [stat]{0}[lightgray]x fire rate +unit.blocks = Klocki +unit.powersecond = jednostek prądu na sekundę +unit.liquidsecond = jednostek płynów na sekundę +unit.itemssecond = przedmiotów na sekundę +unit.liquidunits = jednostek płynów +unit.powerunits = jednostek prądu +unit.degrees = stopnie +unit.seconds = sekundy +unit.persecond = /sekundę +unit.timesspeed = x prędkość +unit.percent = % +unit.items = przedmioty +category.general = Główne +category.power = Prąd +category.liquids = Płyny +category.items = Przedmioty +category.crafting = Przetwórstwo +category.shooting = Strzelanie +category.optional = Dodatkowe ulepszenia +setting.landscape.name = Zablokuj tryb panoramiczny +setting.shadows.name = Cienie +setting.linear.name = Linear Filtering +setting.animatedwater.name = Animowana woda +setting.animatedshields.name = Animowana Tarcza +setting.antialias.name = Antialias[LIGHT_GRAY] (wymaga restartu)[] +setting.indicators.name = Wskaźniki Przyjaciół +setting.autotarget.name = Automatyczne Celowanie +setting.fpscap.name = Maksymalny FPS +setting.fpscap.none = Nieograniczone +setting.fpscap.text = {0} FPS +setting.swapdiagonal.name = Pozwala na ukośne stawianie +setting.difficulty.training = trening setting.difficulty.easy = łatwy setting.difficulty.normal = normalny setting.difficulty.hard = trudny setting.difficulty.insane = szalony -setting.difficulty.purge = Czystka setting.difficulty.name = Poziom trudności setting.screenshake.name = Trzęsienie się ekranu -setting.smoothcam.name = Płynna kamera -setting.indicators.name = Wskaźniki wroga setting.effects.name = Wyświetlanie efektów setting.sensitivity.name = Czułość kontrolera setting.saveinterval.name = Interwał automatycznego zapisywania setting.seconds = Sekundy -setting.fps.name = Widoczny licznik FPS +setting.fullscreen.name = Pełny ekran +setting.borderlesswindow.name = Borderless Window[LIGHT_GRAY] (may require restart) +setting.fps.name = Pokazuj FPS setting.vsync.name = Synchronizacja pionowa setting.lasers.name = Pokaż lasery zasilające -setting.healthbars.name = Pokaż paski zdrowia jednostki -setting.pixelate.name = Rozpikselizowany obraz +setting.pixelate.name = Pikselacja [LIGHT_GRAY](wyłącza animacje) +setting.minimap.name = Pokaż Minimapę setting.musicvol.name = Głośność muzyki setting.mutemusic.name = Wycisz muzykę setting.sfxvol.name = Głośność dźwięków setting.mutesound.name = Wycisz dźwięki -map.maze.name = labirynt -map.fortress.name = twierdza -map.sinkhole.name = wgłębienie -map.caves.name = jaskinie -map.volcano.name = wulkan -map.caldera.name = kaldera -map.scorch.name = opalacz -map.desert.name = pustynia -map.island.name = wyspa -map.grassland.name = łąka -map.tundra.name = tundra -map.spiral.name = spirala -map.tutorial.name = Poradnik -tutorial.intro.text = [yellow]Witamy w poradniku do gry.[]\nAby rozpocząć, naciśnij \"Dalej\". -tutorial.moveDesktop.text = Aby się poruszać, użyj klawiszy [orange]W A S[] oraz [orange]D[]. Przytrzymaj [orange]shift[], aby przyśpieszyć.\nPrzytrzymując [orange]ctrl[] podczas kręcenia [orange]rolką myszy[] możesz zmieniać poziom przybliżenia mapy. -tutorial.shoot.text = Do celowania używasz kursora myszy.\n[orange]Lewy przycisk myszy[] służy do strzelania. Poćwicz na [yellow]celu[]. -tutorial.moveAndroid.text = Aby przesunąć widok, przeciągnij jednym palcem po ekranie. Ściśnij i przeciągnij, aby powiększyć lub pomniejszyć. -tutorial.placeSelect.text = Wybierz [yellow]przenośnik[] z menu blokowego w prawym dolnym rogu. -tutorial.placeConveyorDesktop.text = Użyj [orange]rolki myszy[] aby obrócić przenośnik [orange]do przodu[], a następnie umieść go w [yellow]oznaczonym miejscu[] za pomocą [orange]lewego przycisku myszy[]. -tutorial.placeConveyorAndroid.text = Użyj [orange]przycisk obracania[], aby obrócić przenośnik [orange]do przodu[]. Jednym palcem przeciągnij go na [yellow]wskazaną pozycję[], a następnie umieść go za pomocą [orange]znacznika wyboru[]. -tutorial.placeConveyorAndroidInfo.text = Możesz też nacisnąć ikonę celownika w lewym dolnym rogu, aby przełączyć na [pomarańczowy] [[tryb dotykowy] [] i umieścić bloki, dotykając ekranu. W trybie dotykowym bloki można obracać strzałką w lewym dolnym rogu. Naciśnij [żółty] następny [], aby go wypróbować. -tutorial.placeDrill.text = Teraz wybierz i umieść [yellow]wiertło do kamienia[] w oznaczonym miejscu. -tutorial.blockInfo.text = Jeśli chcesz się dowiedzieć więcej na temat wybranego bloku, kliknij [orange]znak zapytania[] w prawym górnym rogu, aby przeczytać jego opis. -tutorial.deselectDesktop.text = Możesz anulować wybór bloku za pomocą [orange]prawego przycisku myszy[]. -tutorial.deselectAndroid.text = Możesz odznaczyć blok, naciskając przycisk [orange]X[]. -tutorial.drillPlaced.text = Wiertło będzie teraz produkować [yellow]kamień[] i podawać go na przenośnik, który przeniesie go do [yellow]rdzenia[] -tutorial.drillInfo.text = Różne rudy wymagają różnych wierteł. Kamień wymaga wiertła do kamieniu, żelazo wymaga wiertła do żelaza, itd. -tutorial.drillPlaced2.text = Przeniesienie przedmiotów do rdzenia powoduje umieszczenie ich w [yellow]inwentarzu przedmiotów[] w lewym górnym rogu. Stawianie bloków te przedmioty zużywa. -tutorial.moreDrills.text = Możesz połączyć wiertła i przenośników w przedstawiony sposób: -tutorial.deleteBlock.text = Możesz usuwać bloki, klikając [orange]prawy przycisk myszy[] na bloku, który chcesz usunąć.\nSpróbuj usunąć [yellow]oznaczony[] przenośnik. -tutorial.deleteBlockAndroid.text = Możesz usuwać bloki za pomocą [orange]krzyżyka[] w [orange]menu przerywania[] w lewym dolnym rogu i stukając w blok.\nSpróbuj usunąć [yellow]oznaczony[] przenośnik. -tutorial.placeTurret.text = Teraz wybierz i umieść [yellow]działko[] w [yellow]zaznaczonym miejscu[]. -tutorial.placedTurretAmmo.text = Działko przyjmie teraz [yellow]amunicję[] z przenośnika. Możesz zobaczyć, ile ma amunicji, najeżdżając na działko kursorem i sprawdzając [green]zielony pasek[]. -tutorial.turretExplanation.text = Wieżyczki będą strzelać automatycznie do najbliższego wroga w zasięgu, o ile będą miały wystarczającą ilość amunicji. -tutorial.waves.text = Co każde [yellow]60[] sekund, fala [coral]wrogów[] pojawi się w określonym miejscu na mapie i spróbuje zniszczyć rdzeń. -tutorial.coreDestruction.text = Twoim celem jest [yellow]obrona rdzenia[]. Zniszczenie rdzenia jest równoznaczne z [coral]przegraną[]. -tutorial.pausingDesktop.text = Jeśli chcesz zrobić przerwę, naciśnij [orange]spację[] lub [orange]przycisk pauzy[] w lewym górnym rogu. Nadal możesz wybierać i wstawiać klocki podczas pauzy, ale nie możesz niszczyć i strzelać. -tutorial.pausingAndroid.text = Jeśli chcesz zrobić przerwę, naciśnij [orange]przycisk pauzy[]. Nadal możesz stawiać i niszczyć bloki. -tutorial.purchaseWeapons.text = Możesz kupić nowe [żółte] bronie [] dla swojego mecha, otwierając menu aktualizacji w lewym dolnym rogu. -tutorial.switchWeapons.text = Zmień broń, klikając jej ikonę w lewym dolnym rogu lub używając cyfr [orange][1-9[]. -tutorial.spawnWave.text = Fala wrogów nadchodzi! Zniszcz ich. -tutorial.pumpDesc.text = W późniejszych falach może być konieczne użycie [yellow]pompy[] do rozprowadzania cieczy dla generatorów lub ekstraktorów. -tutorial.pumpPlace.text = Pompy działają podobnie do wierteł, z tym wyjątkiem, że wytwarzają ciecze zamiast ród.\nSpróbuj umieścić pompę na [yellow]oleju[]. -tutorial.conduitUse.text = Teraz umieść [orange]rurę[] wychodzącą od pompy. -tutorial.conduitUse2.text = I tak jeszcze jedną... -tutorial.conduitUse3.text = I jeszcze jedeną... -tutorial.generator.text = Teraz umieść [orange]generator spalinowy[] na końcu kanału. -tutorial.generatorExplain.text = Generator ten będzie teraz wytwarzać [yellow]energię[] z oleju. -tutorial.lasers.text = Energia jest dystrybuowana za pomocą [yellow]przekaźników[]. Umieść takowy przekaźnik na [yellow]wskazanej pozycji[]. -tutorial.laserExplain.text = Generator przeniesie teraz moc do przekaźnika. Wiązka [orange]nieprzeźroczysta[] oznacza, że ​​aktualnie transmituje moc. Wiązka [yellow]przeźroczysta[] wskazuje na brak przekazywanej energii. -tutorial.laserMore.text = Możesz sprawdzić ile energii przechowuje blok najeżdżając na niego kursorem i sprawdzając [yellow]żółty pasek[]. -tutorial.healingTurret.text = Energia może być używany do zasilania [lime]wież naprawczych[]. Umieść takową [yellow]tutaj[]. -tutorial.healingTurretExplain.text = Dopóki dostarczana jest do niej energia będzie [lime]naprawiać pobliskie bloki[]. Podczas gry ustawiłeś ją niedaleko swojej bazy tak szybko, jak to tylko możliwe! -tutorial.smeltery.text = Wiele bloków wymaga do pracy [orange]stali[], którą można wytwarzać w [orange]hucie[]. A skoro tak, to postaw ją teraz. -tutorial.smelterySetup.text = Huta wytworzy teraz z żelaza [orange]stal[], wykorzystując węgiel jako paliwo. -tutorial.tunnelExplain.text = Zwróć też uwagę, że przedmioty przechodzą przez [orange]tunel[] i wynurzają się po drugiej stronie, przechodząc przez kamienny blok. Pamiętaj, że tunele mogą przechodzić tylko do 2 bloków. -tutorial.end.text = I na tym poradnik się kończy!\nPozostaje tylko życzyć Ci [orange]powodzenia[]! +setting.crashreport.name = Wysyłaj anonimowo dane o crashu gry +setting.chatopacity.name = Przezroczystość czatu +setting.playerchat.name = Wyświetlaj czat w grze +keybind.title = Zmień +category.general.name = Ogólne +category.view.name = Wyświetl +category.multiplayer.name = Multiplayer +command.attack = Atakuj +command.retreat = Wycofaj +command.patrol = Patrol +keybind.gridMode.name = Wybieranie Bloku +keybind.gridModeShift.name = Wybieranie Kategorii +keybind.press = Naciśnij wybrany klawisz... +keybind.press.axis = Naciśnij oś lub klawisz... +keybind.screenshot.name = Zrzut ekranu mapy keybind.move_x.name = Poruszanie w poziomie keybind.move_y.name = Poruszanie w pionie -keybind.select.name = Wybieranie -keybind.break.name = Niszczenie +keybind.select.name = Zaznacz +keybind.diagonal_placement.name = Diagonal Placement +keybind.pick.name = Wybierz Blok +keybind.break_block.name = Zniszcz Blok +keybind.deselect.name = Odznacz keybind.shoot.name = Strzelanie keybind.zoom_hold.name = Inicjator przybliżania keybind.zoom.name = Przybliżanie -keybind.menu.name = menu -keybind.pause.name = pauza -keybind.dash.name = przyśpieszenie -keybind.rotate_alt.name = Obracanie (1) -keybind.rotate.name = Obracanie (2) -keybind.weapon_1.name = Broń 1 -keybind.weapon_2.name = Broń 2 -keybind.weapon_3.name = Broń 3 -keybind.weapon_4.name = Broń 4 -keybind.weapon_5.name = Broń 5 -keybind.weapon_6.name = Broń 6 -mode.waves.name = Fale -mode.sandbox.name = sandbox -mode.freebuild.name = budowanie -upgrade.standard.name = Standardowy -upgrade.standard.description = Standardowy mech. -upgrade.blaster.name = blaster -upgrade.blaster.description = Wystrzeliwuje powolną, słabą kulę. -upgrade.triblaster.name = triblaster -upgrade.triblaster.description = Strzela 3 pociskami w rozprzestrzenianiu. -upgrade.clustergun.name = clustergun -upgrade.clustergun.description = Wystrzeliwuje w różnych kierunkach kilka granatów. -upgrade.beam.name = Działko laserowe -upgrade.beam.description = Strzela laserem o dalekim zasięgu. -upgrade.vulcan.name = wulkan -upgrade.vulcan.description = Wystrzeliwuje grad szybkich pocisków. -upgrade.shockgun.name = Działko elektryczne -upgrade.shockgun.description = Wystrzeliwuje niszczycielski podmuch naładowanych odłamków. -item.stone.name = kamień -item.iron.name = żelazo -item.coal.name = węgiel -item.steel.name = stal -item.titanium.name = tytan -item.dirium.name = dirium -item.uranium.name = uran -item.sand.name = piasek -liquid.water.name = woda -liquid.plasma.name = plazma -liquid.lava.name = lawa -liquid.oil.name = olej -block.weaponfactory.name = fabryka broni -block.air.name = Powietrze. -block.blockpart.name = Kawałek bloku -block.deepwater.name = głęboka woda -block.water.name = woda -block.lava.name = lawa -block.oil.name = olej -block.stone.name = kamień -block.blackstone.name = czarny kamień -block.iron.name = żelazo -block.coal.name = węgiel -block.titanium.name = tytan -block.uranium.name = uran -block.dirt.name = ziemia -block.sand.name = piasek -block.ice.name = lód -block.snow.name = śnieg -block.grass.name = trawa -block.sandblock.name = blok z piasku -block.snowblock.name = blok ze śniegu -block.stoneblock.name = blok z kamienia -block.blackstoneblock.name = blok z czarnego kamienia -block.grassblock.name = blok z trawy -block.mossblock.name = porośnięty blok -block.shrub.name = krzew -block.rock.name = kamyk -block.icerock.name = kamyk lodowy -block.blackrock.name = czarny kamyk -block.dirtblock.name = Brudny blok -block.stonewall.name = Kamienna ściana -block.stonewall.fulldescription = Tani blok obronny. Przydatny do ochrony rdzenia i wież w pierwszych kilku falach. -block.ironwall.name = Żelazna ściana -block.ironwall.fulldescription = Podstawowy blok obronny. Zapewnia ochronę przed wrogami. -block.steelwall.name = stalowa ściana -block.steelwall.fulldescription = Standardowy blok obronny. odpowiednia ochrona przed wrogami. -block.titaniumwall.name = tytanowa ściana -block.titaniumwall.fulldescription = Silny blok obronny. Zapewnia ochronę przed wrogami. -block.duriumwall.name = ściana z dirium -block.duriumwall.fulldescription = Bardzo silny blok obronny. Zapewnia ochronę przed wrogami. -block.compositewall.name = ściana kompozytowa -block.steelwall-large.name = duża stalowa ściana -block.steelwall-large.fulldescription = Standardowy blok obronny. Rozpiętość wielu płytek. -block.titaniumwall-large.name = duża tytanowa ściana -block.titaniumwall-large.fulldescription = Silny blok obronny. Rozpiętość wielu płytek. -block.duriumwall-large.name = duża ściana z dirium -block.duriumwall-large.fulldescription = Bardzo silny blok obronny. Rozpiętość wielu płytek. -block.titaniumshieldwall.name = Ściana z polem obronnym -block.titaniumshieldwall.fulldescription = Silny blok obronny z dodatkową wbudowaną tarczą. Wymaga zasilania. Używa energii do pochłaniania pocisków wroga. W celu dostarczenia zasilania zaleca się stosowanie wzmacniaczy energii. -block.repairturret.name = Wieża naprawcza -block.repairturret.fulldescription = Naprawia pobliskie uszkodzone bloki w niedużej prędkości. Wykorzystuje niewielkie ilości energii. -block.megarepairturret.name = Wieża naprawcza II -block.megarepairturret.fulldescription = Naprawia pobliskie uszkodzone bloki z przyzwoitą prędkośą. Do działania wykorzystuje energię. -block.shieldgenerator.name = Generator tarczy -block.shieldgenerator.fulldescription = Zaawansowany blok obronny. Osłania wszystkie bloki w promieniu przed atakiem wroga. Zużywa energię z niewielką prędkością gdy jest w stanie bezczynności, ale z kolei gdy jest w użyciu, energię pobiera bardzo szybko. -block.door.name = drzwi -block.door.fulldescription = Blok, który można otworzyć i zamknąć poprzez dotknięcie -block.door-large.name = duże drzwi -block.door-large.fulldescription = Blok, który można otworzyć i zamknąć poprzez dotknięcie -block.conduit.name = Rura -block.conduit.fulldescription = Podstawowy blok transportu cieczy. Działa jak przenośnik, tylko że z płynami. Najlepiej stosować z pompami bądź razem innymi rurami.\nMoże być używany jako pomost nad płynami dla wrogów i graczy. -block.pulseconduit.name = Rura impulsowa -block.pulseconduit.fulldescription = Zaawansowany blok transportu cieczy. Transportuje ciecze szybciej i przechowuje więcej niż rury standardowe. -block.liquidrouter.name = Rozdzielacz płynów -block.liquidrouter.fulldescription = Działa podobnie do zwykłego rozdzielacza. Akceptuje wejście cieczy z jednej strony i przekazuje ją na pozostałe strony. Przydatny do rozdzielania cieczy z jednej rury do wielu. +keybind.menu.name = Menu +keybind.pause.name = Pauza +keybind.minimap.name = Minimap +keybind.dash.name = Przyspieszenie +keybind.chat.name = Czat +keybind.player_list.name = Lista graczy +keybind.console.name = Konsola +keybind.rotate.name = Obracanie +keybind.toggle_menus.name = Zmiana widoczności menu +keybind.chat_history_prev.name = Przewiń wiadomości w górę +keybind.chat_history_next.name = Przewiń wiadomości w dół +keybind.chat_scroll.name = Przewijaj Wiadomości +keybind.drop_unit.name = Wyrzucanie przedmiot +keybind.zoom_minimap.name = Powiększenie mapy +mode.help.title = Opis trybów +mode.survival.name = Przeżycie +mode.survival.description = Zwykły tryb. Limitowane surowce i fale przeciwników. +mode.sandbox.name = Piaskownica +mode.sandbox.description = Nieskończone surowce i fale bez odliczania. Dla przedszkolaków! +mode.pvp.name = PvP +mode.pvp.description = Walcz przeciwko innym graczom. +mode.attack.name = Atak +mode.attack.description = Brak fal, celem jest zniszczenie bazy przeciwnika. +mode.custom = Własny tryb +rules.infiniteresources = Nieskończone zasoby +rules.wavetimer = Zegar fal +rules.waves = Fale +rules.enemyCheat = Nieskończone zasoby komputera-przeciwnika (czerwonego zespołu) +rules.unitdrops = Unit Drops +rules.unitbuildspeedmultiplier = Unit Creation Speed Multiplier +rules.unithealthmultiplier = Unit Health Multiplier +rules.playerhealthmultiplier = Player Health Multiplier +rules.playerdamagemultiplier = Player Damage Multiplier +rules.unitdamagemultiplier = Unit Damage Multiplier +rules.enemycorebuildradius = Enemy Core No-Build Radius:[LIGHT_GRAY] (tiles) +rules.respawntime = Respawn Time:[LIGHT_GRAY] (sec) +rules.wavespacing = Wave Spacing:[LIGHT_GRAY] (sec) +rules.buildcostmultiplier = Build Cost Multiplier +rules.buildspeedmultiplier = Build Speed Multiplier +rules.waitForWaveToEnd = Waves wait for enemies +rules.dropzoneradius = Drop Zone Radius:[LIGHT_GRAY] (tiles) +rules.respawns = Max respawns per wave +rules.limitedRespawns = Limit Respawns +rules.title.waves = Waves +rules.title.respawns = Respawns +rules.title.resourcesbuilding = Resources & Building +rules.title.player = Players +rules.title.enemy = Enemies +rules.title.unit = Units +content.item.name = Przedmioty +content.liquid.name = Płyny +content.unit.name = Jednostki +content.block.name = Klocki +content.mech.name = Mechs +item.copper.name = Miedź +item.copper.description = Przydatny materiał budowlany. Szeroko używany w prawie każdej konstrukcji. +item.lead.name = Ołów +item.lead.description = Podstawowy matriał. Używany w przesyle przemiotów i płynów. Nie jest on przypadkiem szkodliwy? +item.coal.name = Węgiel +item.coal.description = Zwykły i łatwo dostępny materiał energetyczny. +item.graphite.name = Grafit +item.titanium.name = Tytan +item.titanium.description = Rzadki i bardzo lekki materiał. Używany w bardzo zaawansowanym przewodnictwie, wiertłach i samolotach. Poczuj się jak Tytan! +item.thorium.name = Uran +item.thorium.description = Zwarty i radioaktywny materiał używany w struktucrach i paliwie nuklearnym. Nie trzymaj go w rękach! +item.silicon.name = Krzem +item.silicon.description = Niesamowicie przydatny półprzewodnk uźywany w panelach słonecznych i skomplikowanej elektronice. Nie, w Dolinie Krzemowej już nie ma krzemu. +item.plastanium.name = Plastan +item.plastanium.description = Lekki i plastyczny materiał używany w amunicji odłamkowej i samolotach. Używany też w klockach LEGO (dlatego są niezniszczalne)! +item.phase-fabric.name = Włókno Fazowe +item.phase-fabric.description = Niewiarygodnie lekkie włókno używane w zaawansowanej elektronice i technologii samo-naprawiającej się. +item.surge-alloy.name = Energetyczny Stop +item.surge-alloy.description = Zaawansowany materiał z niesłychanymi wartościami energetycznymi. +item.spore-pod.name = Spore Pod +item.spore-pod.description = Used for conversion into oil, explosives and fuel. +item.sand.name = Piasek +item.sand.description = Zwykły materiał używany pospolicie w przepalaniu, stopach i jako topnik. Dostanie piaskiem po oczach nie jest przyjemne. +item.blast-compound.name = Wybuchowy związek +item.blast-compound.description = Lotny związek używany w pirotechnice. Może być używany jako materiał energetyczny, ale nie polecam. BOOOM! +item.pyratite.name = Piratian +item.pyratite.description = Niesamowicie palny związek używany w zbrojeniu. Nielegalny w 9 państwach. +item.metaglass.name = Metaszkło +item.metaglass.description = Niesamowite silne szkło. Szeroko używane w transporcie i przechowywaniu płynów. +item.scrap.name = Resztki +item.scrap.description = Pozostałości starych budynków i jednostek. Składa się z małej ilości wszystkiego. +liquid.water.name = Woda +liquid.slag.name = Slag +liquid.oil.name = Ropa +liquid.cryofluid.name = Lodociecz +mech.alpha-mech.name = Alpha +mech.alpha-mech.weapon = Ciężki Karabin +mech.alpha-mech.ability = Chmara Dronòw +mech.alpha-mech.description = Standardowy mech. Średnia broń i prędkość, leć potrafi stworzyć trzy małe drony do walki. +mech.delta-mech.name = Delta +mech.delta-mech.weapon = Generator Piorunów +mech.delta-mech.ability = Rozładunek +mech.delta-mech.description = Szybki i wrażliwy mech stworzony do szybkih ataków i ucieczki. Budynką robi prawie nic, leć jest wstanie szybko rozwalić grupę wrogich jednostek piorunami. +mech.tau-mech.name = Tau +mech.tau-mech.weapon = Laser Odbudowy +mech.tau-mech.ability = Wybuch Naprawy +mech.tau-mech.description = Mech pomocny. Naprawia budynki drużyny, strzelając w nie. Potrafi wygasić niedalekie pożary i uleczyć bliskich przyjaciół. +mech.omega-mech.name = Omega +mech.omega-mech.weapon = Rakiety Chmarowe +mech.omega-mech.ability = Układ Obronny +mech.omega-mech.description = Duży i silny mech, zaprojektowany na ataki. Jego zdolność pozwala mu na zablokowanie do 90% zagrożeń. +mech.dart-ship.name = Strzałka +mech.dart-ship.weapon = Karabin +mech.dart-ship.description = Standardowy statek. Lekki i szybki, ale jest kiepski jak chodzi o walkę i kopanie. +mech.javelin-ship.name = Javelin +mech.javelin-ship.description = Statek do ataku i szybkiej ucieczki. Zaczyna powoli, ale przyspiesza do wielkiej prędkości. Przy tej prędkości, może przelecieć koło wrogiej bazy i atakować piorunami czy rakietami. +mech.javelin-ship.weapon = Seria Rakiet +mech.javelin-ship.ability = Dopalacze Prądowe +mech.trident-ship.name = Trójząb +mech.trident-ship.description = Ciężki bombowiec. Dobrze uzbrojony. +mech.trident-ship.weapon = Wnęka bombowa +mech.glaive-ship.name = Glewia +mech.glaive-ship.description = Duży, uzbrojony statek. Dobra prędkość i przyspieszenie. Ma ognisty karabin. +mech.glaive-ship.weapon = Zapalający Karabin +item.explosiveness = [LIGHT_GRAY]Wybuchowość: {0} +item.flammability = [LIGHT_GRAY]Palność: {0} +item.radioactivity = [LIGHT_GRAY]Promieniotwórczość: {0} +unit.health = [LIGHT_GRAY]Zdrowie: {0} +unit.speed = [LIGHT_GRAY]Prędkość: {0} +mech.weapon = [LIGHT_GRAY]Broń: {0} +mech.health = [LIGHT_GRAY]Zdrowie: {0} +mech.itemcapacity = [LIGHT_GRAY]Pojemność przedmiotów: {0} +mech.minespeed = [LIGHT_GRAY]Prędkość kopania: {0} +mech.minepower = [LIGHT_GRAY]Moc kopania: {0} +mech.ability = [LIGHT_GRAY]Umiejętność: {0} +mech.buildspeed = [LIGHT_GRAY]Building Speed: {0}% +liquid.heatcapacity = [LIGHT_GRAY]Wytrzymałość na przegrzewanie: {0} +liquid.viscosity = [LIGHT_GRAY]Lepkość: {0} +liquid.temperature = [LIGHT_GRAY]Temperatura: {0} +block.grass.name = Grass +block.salt.name = Salt +block.saltrocks.name = Salt Rocks +block.pebbles.name = Pebbles +block.tendrils.name = Tendrils +block.sandrocks.name = Sand Rocks +block.spore-pine.name = Spore Pine +block.sporerocks.name = Spore Rocks +block.rock.name = Rock +block.snowrock.name = Snow Rock +block.shale.name = Shale +block.shale-boulder.name = Shale Boulder +block.moss.name = Moss +block.shrubs.name = Shrubs +block.spore-moss.name = Spore Moss +block.shalerocks.name = Shale Rocks +block.scrap-wall.name = Scrap Wall +block.scrap-wall-large.name = Large Scrap Wall +block.scrap-wall-huge.name = Huge Scrap Wall +block.scrap-wall-gigantic.name = Gigantic Scrap Wall +block.thruster.name = Thruster +block.kiln.name = Wypalarka +block.kiln.description = Stapia ołów i piasek na metaszkło. Wymaga małą ilość energii. +block.graphite-press.name = Grafitowa Prasa +block.multi-press.name = Multi-Prasa +block.constructing = {0} [LIGHT_GRAY](Budowa) +block.spawn.name = Spawn wrogów +block.core-shard.name = Rdzeń: Ułamek +block.core-foundation.name = Rdzeń: Podstawa +block.core-nucleus.name = Rdzeń: Jądro +block.deepwater.name = Głęboka Woda +block.water.name = Woda +block.tainted-water.name = Tainted Water +block.darksand-tainted-water.name = Dark Sand Tainted Water +block.tar.name = Smoła +block.stone.name = Kamień +block.sand.name = Piasek +block.darksand.name = Czarny piasek +block.ice.name = Lód +block.snow.name = Śnieg +block.craters.name = Kratery +block.sand-water.name = Sand water +block.darksand-water.name = Dark Sand Water +block.char.name = Char +block.holostone.name = Holo stone +block.ice-snow.name = Ice Snow +block.rocks.name = Rocks +block.icerocks.name = Ice rocks +block.snowrocks.name = Snow Rocks +block.dunerocks.name = Dune Rocks +block.pine.name = Pine +block.white-tree-dead.name = White Tree Dead +block.white-tree.name = White Tree +block.spore-cluster.name = Spore Cluster +block.metal-floor.name = Metal Floor +block.metal-floor-2.name = Metal Floor 2 +block.metal-floor-3.name = Metal Floor 3 +block.metal-floor-5.name = Metal Floor 5 +block.metal-floor-damaged.name = Metal Floor Damaged +block.dark-panel-1.name = Dark Panel 1 +block.dark-panel-2.name = Dark Panel 2 +block.dark-panel-3.name = Dark Panel 3 +block.dark-panel-4.name = Dark Panel 4 +block.dark-panel-5.name = Dark Panel 5 +block.dark-panel-6.name = Dark Panel 6 +block.dark-metal.name = Dark Metal +block.ignarock.name = Igna Rock +block.hotrock.name = Hot Rock +block.magmarock.name = Magma Rock +block.cliffs.name = Cliffs +block.copper-wall.name = Miedziana Ściana +block.copper-wall-large.name = Duża miedziana ściana +block.titanium-wall.name = Tytanowa Ściana +block.titanium-wall-large.name = Duża Tytanowa Ściana +block.phase-wall.name = Fazowa Ściana +block.phase-wall-large.name = Duża Fazowa Ściana +block.thorium-wall.name = Torowa Ściana +block.thorium-wall-large.name = Duża Torowa Ściana +block.door.name = Drzwi +block.door-large.name = Duże drzwi +block.duo.name = Podwójne działko +block.scorch.name = Scorch +block.scatter.name = Scatter +block.hail.name = Hail +block.lancer.name = Lancer block.conveyor.name = Przenośnik -block.conveyor.fulldescription = Podstawowy blok transportu przedmiotów. Przenosi przedmioty do przodu i automatycznie umieszcza je w blokach. Może być używany jako pomost nad płynami dla wrogów i graczy. -block.steelconveyor.name = Przenośnik stalowy -block.steelconveyor.fulldescription = Zaawansowany blok transportu przedmiotów. Przenosi elementy szybciej niż standardowe przenośniki. -block.poweredconveyor.name = przenośnik impulsowy -block.poweredconveyor.fulldescription = Najszybszy blok transportowy przedmiotów. +block.titanium-conveyor.name = Tytanowy przenośnik +block.junction.name = Węzeł block.router.name = Rozdzielacz -block.router.fulldescription = Przyjmuje przedmioty z jednego kierunku i przekazuje je w 3 innych kierunkach. Może również przechowywać pewną liczbę przedmiotów. Przydatny do dzielenia materiałów z jednego wiertła na wiele dział. -block.junction.name = węzeł -block.junction.fulldescription = Działa jako pomost dla dwóch skrzyżowanych przenośników taśmowych. Przydatny w sytuacjach, gdy dwa różne przenośniki przenoszą różne materiały do ​​różnych lokalizacji. -block.conveyortunnel.name = tunel przenośnikowy -block.conveyortunnel.fulldescription = Transportuje przedmiot pod blokami. Aby użyć, umieść jeden tunel prowadzący do bloku, który ma zostać tunelowany, i jeden po drugiej stronie. Upewnij się, że oba tunele są skierowane w przeciwnych kierunkach, czyli w wejście do bloku podającego, a wyjście do bloku odbierającego. -block.liquidjunction.name = Węzeł dla płynów -block.liquidjunction.fulldescription = Skrzyżowanie dla rurociągów. Przydatne w sytuacji, w której transportujemy różne ciecze w rurach, które muszą się przeciąć. -block.liquiditemjunction.name = Węzeł rur i przenośników -block.liquiditemjunction.fulldescription = Skrzyżowanie przenośników taśmowych oraz rur. -block.powerbooster.name = Wzmacniacz energii -block.powerbooster.fulldescription = Dystrybuuje moc do wszystkich bloków w swoim promieniu. -block.powerlaser.name = Przekaźnik -block.powerlaser.fulldescription = Tworzy laser, który przesyła moc do bloku przed nim. Nie generuje energii. Najlepiej stosować z generatorami lub innymi przekaźnikami. -block.powerlaserrouter.name = Duży rozdzielacz energii -block.powerlaserrouter.fulldescription = Przekaźnik, który dystrybuuje energię w trzech kierunkach jednocześnie. Przydatne w sytuacjach, w których wymagane jest zasilanie wielu bloków z jednego generatora. -block.powerlasercorner.name = Rozdzielacz energii -block.powerlasercorner.fulldescription = Przekaźnik, który dystrybuuje energię w dwóch kierunkach jednocześnie. Przydatny w sytuacjach, gdy wymagane jest zasilanie wielu bloków z jednego generatora, a router jest nieprecyzyjny. -block.teleporter.name = teleporter -block.teleporter.fulldescription = Zaawansowany blok transportu przedmiotów. Teleporty przenoszą przedmioty do innych teleportów tego samego koloru. Jeżeli nie istnieją teleporty z tym samym kolorem, to nie niczego nigdzie nie przenosi. Jeśli istnieje wiele teleportów tego samego koloru, wybierany jest losowy. Zużywa energię. Stuknij aby zmienić kolor. +block.distributor.name = Dystrybutor block.sorter.name = Sortownik -block.sorter.fulldescription = Sortuje przedmioty według rodzaju materiału. Materiał do zaakceptowania jest oznaczony w bloku. Wszystkie pasujące przedmioty są wysyłane do przodu, wszystko inne jest wyprowadzane na lewo i na prawo. -block.core.name = Rdzeń -block.pump.name = pompa -block.pump.fulldescription = Pompuje ciecze z bloku źródłowego - zwykle wody, lawy lub oleju. Wyprowadza ciecz do pobliskich rur. -block.fluxpump.name = pompa strumieniowa -block.fluxpump.fulldescription = Zaawansowana wersja pompy. Przechowuje więcej cieczy i szybciej pompuje. -block.smelter.name = huta -block.smelter.fulldescription = Niezbędny blok rzemieślniczy. Po wprowadzeniu 1 żelaza i 1 węgla jako paliwa, wytwarza 1 stal. Zaleca się wprowadzanie żelaza i węgla na różne pasy, aby zapobiec zatkaniu. -block.crucible.name = tygiel -block.crucible.fulldescription = Zaawansowany blok rzemieślniczy. Po wprowadzeniu 1 tytanu, 1 stali i 1 węgla jako paliwa, otrzymuje 1 dirium. Zaleca się wprowadzanie węgla, stali i tytanu z różnych taśm, aby zapobiec zatkaniu. -block.coalpurifier.name = ekstraktor węgla -block.coalpurifier.fulldescription = Wyprowadza węgiel, gdy jest dostarczana duża ilością wody i kamienia. -block.titaniumpurifier.name = ekstraktor tytanu -block.titaniumpurifier.fulldescription = Wyprowadza tytan, gdy jest dostarczana duża ilości wody i żelaza. -block.oilrefinery.name = Rafineria ropy -block.oilrefinery.fulldescription = Rafinuje duże ilości oleju do postaci węgla. Przydatne do napędzania działek napędzanych węglem, gry nie ma w pobliżu wystarcząjacej ilości rud węgla. -block.stoneformer.name = Wytwarzacz kamienia -block.stoneformer.fulldescription = Schładza lawę do postaci kamień. Przydatny do produkcji ogromnych ilości kamienia do oczyszczania węgla. -block.lavasmelter.name = Huta lawowa -block.lavasmelter.fulldescription = Używa lawy, by przekształcić żelazo w stal. Alternatywa dla zwykłych hut. Przydatny w sytuacjach, gdy brakuje węgla. -block.stonedrill.name = wiertło do kamienia -block.stonedrill.fulldescription = Niezbędne wiertło. Po umieszczeniu na kamiennym podłożu, powoli i w nieskończoność wydobywa kamień. -block.irondrill.name = wiertło do żelaza -block.irondrill.fulldescription = Po umieszczeniu na rudzie żelaza, powoli i w nieskończoność wydobywa żelazo. -block.coaldrill.name = wiertło do węgla -block.coaldrill.fulldescription = Po umieszczeniu na rudzie węgla, powoli i w nieskończoność wydobywa węgiel. -block.uraniumdrill.name = wiertło do uranu -block.uraniumdrill.fulldescription = Wiertło zaawansowane. Po umieszczeniu na rudzie uranu, wydobywa uran w wolnym tempie przez czas nieokreślony. -block.titaniumdrill.name = wiertło do tytanu -block.titaniumdrill.fulldescription = Wiertło zaawansowane. Po umieszczeniu na rudzie tytanu, wydobywa tytan w wolnym tempie przez czas nieokreślony. -block.omnidrill.name = omnidril -block.omnidrill.fulldescription = Wiertło wielofunkcyjne. W szybkim tempie wydobywa każdą rudę. -block.coalgenerator.name = generator na węgiel -block.coalgenerator.fulldescription = Niezbędny generator. Generuje energię z węgla na wszystkie strony. -block.thermalgenerator.name = generator termiczny -block.thermalgenerator.fulldescription = Generuje energię z lawy. Generuje energię z węgla na wszystkie strony. -block.combustiongenerator.name = generator spalinowy -block.combustiongenerator.fulldescription = Generuje moc z oleju. Generuje energię z węgla na wszystkie strony. -block.rtgenerator.name = Generator RTG -block.rtgenerator.fulldescription = Generuje niewielkie ilości energii z rozpadu promieniotwórczego uranu. Generuje energię z węgla na wszystkie strony. -block.nuclearreactor.name = reaktor jądrowy -block.nuclearreactor.fulldescription = Zaawansowana wersja generatora RTG i zarazem najlepsze źródło energii. Generuje ją z uranu. Wymaga stałego chłodzenia wodą. Wybucha niemal natychmiast w momencie, gdy dostarczona zostanie niewystarczająca ilość płynu chłodzącego. -block.turret.name = działko -block.turret.fulldescription = Podstawowa, nieduża wieżyczka. Używa kamienia jako amunicji. Ma nieco większy zasięg niż działko podwójne. -block.doubleturret.name = działko podwójne -block.doubleturret.fulldescription = Nieco mocniejsza wersja działka. Używa kamienia jako amunicji. Znacznie więcej obrażeń, ale ma mniejszy zasięg. Wystrzeliwuje dwie kule. -block.machineturret.name = działko szybkostrzelne -block.machineturret.fulldescription = Standardowa, wszechstronna wieża. Używa żelaza jako amunicji. Strzela dość szybko i ma przyzwoite uszkodzenia. -block.shotgunturret.name = działko odłamkowe -block.shotgunturret.fulldescription = Standardowa wieża. Używa żelaza do amunicji. Wystrzeliwuje 7 pocisków. Niższy zasięg, ale większe obrażenia niż działko szybkostrzelne. -block.flameturret.name = miotacz ognia -block.flameturret.fulldescription = Zaawansowana wieżyczka bliskiego zasięgu. Używa węgla jako amunicji. Ma bardzo niski zasięg, ale bardzo duże obrażenia. Dobra na krótkie dystanse. Zalecana do stosowania za ścianami. -block.sniperturret.name = karabin -block.sniperturret.fulldescription = Zaawansowana wieżyczka dalekiego zasięgu. Używa stali jako amunicji. Ma bardzo duże obrażenia, ale niski współczynnik ognia. Kosztowne w użyciu, ale można je umieścić z dala od linii wroga ze względu na jego zasięg. -block.mortarturret.name = Miotacz odłamków -block.mortarturret.fulldescription = Zaawansowana, bardzo dokładne działko rozpryskowe. Używa węgla jako amunicji. Wystrzeliwuje grad eksplodujących pocisków. Przydatny dla dużych hord wrogów. -block.laserturret.name = działo laserowe -block.laserturret.fulldescription = Zaawansowana wieża z jednym celem. Używa energii. Dobra wieża o średnim zasięgu. Atakuje tylko 1 cel i nigdy nie pudłuje. -block.waveturret.name = Działo Tesli -block.waveturret.fulldescription = Zaawansowana wieża celującai. Używa mocy. Średni zasięg. Nigdy nie trafia. Stosuje niskie obrażenia, ale może trafić wielu wrogów jednocześnie z oświetleniem łańcuszkiem. -block.plasmaturret.name = Działo plazmowe -block.plasmaturret.fulldescription = Wysoce zaawansowana wersja miotacza ognia. Używa węgla jako amunicji. Zadaje bardzo duże obrażenia i posiada średni zasięg. -block.chainturret.name = Działo uranowe -block.chainturret.fulldescription = Najlepsze działo szybkiego ognia. Używa uranu jako amunicji. Wystrzeliwuje duże spirale z dużą szybkością. Posiada średni zasięg. -block.titancannon.name = Potężne działo uranowe -block.titancannon.fulldescription = Najlepsze działo dalekiego zasięgu. Używa uranu jako amunicji. Wystrzeliwuje duże pociski z odłamkami. Ma średnią szybkostrzelność i duży promień rażenia. -block.playerspawn.name = Spawn gracza -block.enemyspawn.name = Spawn wroga +block.sorter.description = Sortuje przedmioty. Jeśli przedmiot pasuje to przechodzi dalej, jeśli nie - to przechodzi na boki. +block.overflow-gate.name = Brama Przeciwprzepełnieniowa +block.overflow-gate.description = Rozdzielacz, który przerzuca przedmioty, kiedy główna droga jest przepełniona +block.silicon-smelter.name = Huta Krzemu +block.phase-weaver.name = Fazowa Fabryka +block.pulverizer.name = Rozkruszacz +block.cryofluidmixer.name = Mieszacz Cryofluidu +block.melter.name = Przetapiacz +block.incinerator.name = Spalacz +block.spore-press.name = Spore Press +block.separator.name = Rozdzielacz +block.coal-centrifuge.name = Coal Centrifuge +block.power-node.name = Węzeł Prądu +block.power-node-large.name = Duży Węzeł Prądu +block.surge-tower.name = Wieża Energetyczna +block.battery.name = Bateria +block.battery-large.name = Duża Bateria +block.combustion-generator.name = Generator Spalinowy +block.turbine-generator.name = Generator Turbinowy +block.differential-generator.name = Generator Różnicowy +block.impact-reactor.name = Reaktor Uderzeniowy +block.mechanical-drill.name = Wiertło Mechaniczne +block.pneumatic-drill.name = Wiertło Pneumatyczne +block.laser-drill.name = Wiertło Laserowe +block.water-extractor.name = Ekstraktor Wody +block.cultivator.name = Spluchniacz +block.dart-mech-pad.name = Dart Mech Pad +block.delta-mech-pad.name = Lądowisko Mecha Delta +block.javelin-ship-pad.name = Lądowisko Statku Oszczep +block.trident-ship-pad.name = Lądowisko Statku Trójząb +block.glaive-ship-pad.name = Lądowisko Statku Glewia +block.omega-mech-pad.name = Lądowisko Mecha Omega +block.tau-mech-pad.name = Lądowisko Mecha Tau +block.conduit.name = Rura +block.mechanical-pump.name = Mechaniczna Pompa +block.item-source.name = Źródło przedmiotów +block.item-void.name = Próżnia przedmiotów +block.liquid-source.name = Źródło płynów +block.power-void.name = Próżnia prądu +block.power-source.name = Nieskończony Prąd +block.unloader.name = Wyciągacz +block.vault.name = Magazyn +block.wave.name = Działo Płynowe +block.swarmer.name = Działo Rojowe +block.salvo.name = Działo Salwowe +block.ripple.name = Działo falowe +block.phase-conveyor.name = Fazowy Transporter +block.bridge-conveyor.name = Most Transportowy +block.plastanium-compressor.name = Kompresor Plastanu +block.pyratite-mixer.name = Mieszacz Piratianu +block.blast-mixer.name = Wybuchowy Mieszacz +block.solar-panel.name = Panel Słoneczny +block.solar-panel-large.name = Duży Panel Słoneczny +block.oil-extractor.name = Ekstraktor Ropy +block.spirit-factory.name = Fabryka Dronów Duch +block.phantom-factory.name = Fabryka Dronów Widmo +block.wraith-factory.name = Fabryka Wojowników Zjawa +block.ghoul-factory.name = Fabryka Bombowców Upiór +block.dagger-factory.name = Fabryka Mechów Nóż +block.crawler-factory.name = Crawler Mech Factory +block.titan-factory.name = Fabryka Mechów Tytan +block.fortress-factory.name = Fabryka Mechów Fortreca +block.revenant-factory.name = Fabryka Wojowników Potwór +block.repair-point.name = Punkt Napraw +block.pulse-conduit.name = Rura Pulsacyjna +block.phase-conduit.name = Rura Fazowa +block.liquid-router.name = Rozdzielacz Płynów +block.liquid-tank.name = Zbiornik Płynów +block.liquid-junction.name = Łącznik Płynów +block.bridge-conduit.name = Most Płynów +block.rotary-pump.name = Wirowa Pompa +block.thorium-reactor.name = Reaktor Torowy +block.mass-driver.name = Katapulta Masy +block.blast-drill.name = Wiertło Wybuchowe +block.thermal-pump.name = Pompa Termalna +block.thermal-generator.name = Generator Termalny +block.alloy-smelter.name = Piec Mieszający +block.mender.name = Mender +block.mend-projector.name = Projektor Napraw +block.surge-wall.name = Ściana Stopu Energetycznego +block.surge-wall-large.name = Duża Ściana Stopu Energetycznego +block.cyclone.name = Cyklon +block.fuse.name = Lont +block.shock-mine.name = Mina +block.overdrive-projector.name = Projektor Nad-prędkości +block.force-projector.name = Projektor Pola Siłowego +block.arc.name = Piorun +block.rtg-generator.name = Generator RTG +block.spectre.name = Spectre +block.meltdown.name = Meltdown +block.container.name = Kontener +block.launch-pad.name = Skocznia +block.launch-pad.description = Launches batches of items without any need for a core launch. Unfinished. +block.launch-pad-large.name = Duża skocznia +team.blue.name = niebieski +team.red.name = czerwony +team.orange.name = pomarańczowy +team.none.name = szary +team.green.name = zielony +team.purple.name = fioletowy +unit.spirit.name = Duch +unit.spirit.description = Początkowy dron. Rdzeń zawsze tworzy jeden. Wydobywa surowce, naprawia budynki oraz pomaga przy budowie. +unit.phantom.name = Widmo +unit.phantom.description = Zaawansowany dron. Wydobywa surowce, naprawia budynki oraz pomaga przy budowie szybciej niż dron Duch. +unit.dagger.name = Nóż +unit.dagger.description = Podstawowy mech lądowy. Sam jest słaby, lecz przydatny w dużych ilościach. +unit.crawler.name = Pełzak +unit.titan.name = Tytan +unit.titan.description = Bardziej zaawansowany mech lądowy. Atakuje cele lądowe i niebne. +unit.ghoul.name = Upiór +unit.ghoul.description = Ciężki bombowiec. +unit.wraith.name = Zjawa +unit.wraith.description = Szybka jednostka do ataku i ucieczki. +unit.fortress.name = Fortreca +unit.fortress.description = Wielka jednostka artyleryjna lądowa. +unit.revenant.name = Potwór +unit.eruptor.name = Wysadzać +unit.chaos-array.name = Kolejka Chaosu +unit.eradicator.name = Niszczyciel +unit.lich.name = Obudzony +unit.reaper.name = Żeniec +tutorial.begin = Your mission here is to eradicate the[LIGHT_GRAY] enemy[].\n\nBegin by[accent] mining copper[]. Tap a copper ore vein near your core to do this. +tutorial.drill = Mining manually is inefficient.\n[accent]Drills []can mine automatically.\nPlace one on a copper vein. +tutorial.conveyor = [accent]Conveyors[] are used to transport items to the core.\nMake a line of conveyors from the drill to the core. +tutorial.morecopper = More copper is required.\n\nEither mine it manually, or place more drills. +tutorial.turret = Defensive structures must be built to repel the[LIGHT_GRAY] enemy[].\nBuild a duo turret near your base. +tutorial.drillturret = Duo turrets require[accent] copper ammo []to shoot.\nPlace a drill next to the turret to supply it with mined copper. +tutorial.waves = The[LIGHT_GRAY] enemy[] approaches.\n\nDefend your core for 2 waves. Build more turrets. +tutorial.lead = More ores are available. Explore and mine[accent] lead[].\n\nDrag from your unit to the core to transfer resources. +tutorial.smelter = Copper and lead are weak metals.\nSuperior[accent] Dense Alloy[] can be created in a smelter.\n\nBuild one. +tutorial.densealloy = The smelter will now produce alloy.\nGet some.\nImprove the production if necessary. +tutorial.siliconsmelter = The core will now create a[accent] spirit drone[] for mining and repairing blocks.\n\nFactories for other units can be created with [accent] silicon.\nMake a silicon smelter. +tutorial.silicondrill = Silicon requires[accent] coal[] and[accent] sand[].\nStart by making drills. +tutorial.generator = This technology requires power.\nCreate a[accent] combustion generator[] for it. +tutorial.generatordrill = Combustion generators need fuel.\nFuel it with coal from a drill. +tutorial.node = Power requires transport.\nCreate a[accent] power node[] next to your combustion generator to transfer its power. +tutorial.nodelink = Power can be transferred through contacting power blocks and generators, or by linked power nodes.\n\nLink power by tapping the node and selecting the generator and silicon smelter. +tutorial.silicon = Silicon is being produced. Get some.\n\nImproving the production system is advised. +tutorial.daggerfactory = Construct a[accent] dagger mech factory.[]\n\nThis will be used to create attack mechs. +tutorial.router = Factories need resources to function.\nCreate a router to split conveyor resources. +tutorial.dagger = Link power nodes to the factory.\nOnce requirements are met, a mech will be created.\n\nCreate more drills, generators and conveyors as necessary. +tutorial.battle = The[LIGHT_GRAY] enemy[] has revealed their core.\nDestroy it with your unit and dagger mechs. +block.copper-wall.description = A cheap defensive block.\nUseful for protecting the core and turrets in the first few waves. +block.copper-wall-large.description = A cheap defensive block.\nUseful for protecting the core and turrets in the first few waves.\nSpans multiple tiles. +block.thorium-wall.description = A strong defensive block.\nGood protection from enemies. +block.thorium-wall-large.description = A strong defensive block.\nGood protection from enemies.\nSpans multiple tiles. +block.phase-wall.description = Not as strong as a thorium wall but will deflect bullets unless they are too powerful. +block.phase-wall-large.description = Not as strong as a thorium wall but will deflect bullets unless they are too powerful.\nSpans multiple tiles. +block.surge-wall.description = The strongest defensive block.\nHas a small chance of triggering lightning towards the attacker. +block.surge-wall-large.description = The strongest defensive block.\nHas a small chance of triggering lightning towards the attacker.\nSpans multiple tiles. +block.door.description = A small door that can be opened and closed by tapping on it.\nIf opened, enemies can shoot and move through. +block.door-large.description = A large door that can be opened and closed by tapping on it.\nIf opened, enemies can shoot and move through.\nSpans multiple tiles. +block.mend-projector.description = Periodically heals blocks in its vicinity. +block.overdrive-projector.description = Increases the speed of nearby buildings like drills and conveyors. +block.force-projector.description = Creates a hexagonal force field around itself, protecting buildings and units inside from damage through bullets. +block.shock-mine.description = Damages enemies stepping on the mine. Nearly invisible to the enemy. +block.duo.description = A small, cheap turret. Useful against ground units. +block.scatter.description = A medium-sized anti-air turret. Sprays clumps of lead or scrap flak at enemy units. +block.arc.description = A small close-range turret which shoots electricity in a random arc towards the enemy. +block.hail.description = A small artillery turret. +block.lancer.description = A medium-sized turret which shoots charged electricity beams. +block.wave.description = A medium-sized rapid-fire turret which shoots liquid bubbles. +block.salvo.description = A medium-sized turret which fires shots in salvos. +block.swarmer.description = A medium-sized turret which shoots burst missiles. +block.ripple.description = A large artillery turret which fires several shots simultaneously. +block.cyclone.description = A large rapid fire turret. +block.fuse.description = A large turret which shoots powerful short-range beams. +block.spectre.description = A large turret which shoots two powerful bullets at once. +block.meltdown.description = A large turret which shoots powerful long-range beams. +block.conveyor.description = Basic item transport block. Moves items forward and automatically deposits them into turrets or crafters. Rotatable. +block.titanium-conveyor.description = Advanced item transport block. Moves items faster than standard conveyors. +block.phase-conveyor.description = Advanced item transport block. Uses power to teleport items to a connected phase conveyor over several tiles. +block.junction.description = Acts as a bridge for two crossing conveyor belts. Useful in situations with two different conveyors carrying different materials to different locations. +block.mass-driver.description = Ultimate item transport block. Collects several items and then shoots them to another mass driver over a long range. +block.silicon-smelter.description = Reduces sand with highly pure coal in order to produce silicon. +block.plastanium-compressor.description = Produces plastanium from oil and titanium. +block.phase-weaver.description = Produces phase fabric from radioactive thorium and high amounts of sand. +block.alloy-smelter.description = Produces surge alloy from titanium, lead, silicon and copper. +block.pulverizer.description = Crushes scrap into sand. Useful when there is a lack of natural sand. +block.pyratite-mixer.description = Mixes coal, lead and sand into highly flammable pyratite. +block.blast-mixer.description = Uses oil for transforming pyratite into the less flammable but more explosive blast compound. +block.cryofluidmixer.description = Combines water and titanium into cryofluid which is much more efficient for cooling. +block.melter.description = Melts down scrap into slag for further processing or usage in turrets. +block.incinerator.description = Gets rid of any excess item or liquid. +block.spore-press.description = Compresses spore pods into oil. +block.separator.description = Extracts useful minerals from slag. +block.power-node.description = Transmits power to connected nodes. Up to four power sources, sinks or nodes can be connected. The node will receive power from or supply power to any adjacent blocks. +block.power-node-large.description = Has a larger radius than the power node and connects to up to six power sources, sinks or nodes. +block.battery.description = Stores power whenever there is an abundance and provides power whenever there is a shortage, as long as there is capacity left. +block.battery-large.description = Stores much more power than a regular battery. +block.combustion-generator.description = Generates power by burning oil or flammable materials. +block.turbine-generator.description = More efficient than a combustion generator, but requires additional water. +block.thermal-generator.description = Generates power when placed in hot locations. +block.solar-panel.description = Provides a small amount of power from the sun. +block.solar-panel-large.description = Provides much better power supply than a standard solar panel, but is also much more expensive to build. +block.thorium-reactor.description = Generates huge amounts of power from highly radioactive thorium. Requires constant cooling. Will explode violently if insufficient amounts of coolant are supplied. Power output depends on fullness, with base power generated at full capacity. +block.rtg-generator.description = A radioisotope thermoelectric generator which does not require cooling but provides less power than a thorium reactor. +block.unloader.description = Unloads items from a container, vault or core onto a conveyor or directly into an adjacent block. The type of item to be unloaded can be changed by tapping on the unloader. +block.container.description = Stores a small amount of items of each type. An[LIGHT_GRAY] unloader[] can be used to retrieve items from the container. +block.vault.description = Stores a large amount of items of each type. An[LIGHT_GRAY] unloader[] can be used to retrieve items from the vault. +block.mechanical-drill.description = A cheap drill. When placed on appropriate tiles, outputs items at a slow pace indefinitely. +block.pneumatic-drill.description = An improved drill which is faster and able to process harder materials by making use of air pressure. +block.laser-drill.description = Allows drilling even faster through laser technology, but requires power. Additionally, radioactive thorium can be retrieved with this drill. +block.blast-drill.description = The ultimate drill. Requires large amounts of power. +block.water-extractor.description = Extracts water from the ground. Use it when there is no lake nearby. +block.cultivator.description = Cultivates tiny concentrations of spores into industry-ready pods. +block.oil-extractor.description = Uses large amounts of power in order to extract oil from sand. Use it when there is no direct source of oil nearby. +block.trident-ship-pad.description = Leave your current vessel and change into a reasonably well armored heavy bomber.\nUse the pad by double tapping while standing on it. +block.javelin-ship-pad.description = Leave your current vessel and change into a strong and fast interceptor with lightning weapons.\nUse the pad by double tapping while standing on it. +block.glaive-ship-pad.description = Leave your current vessel and change into a large, well-armored gunship.\nUse the pad by double tapping while standing on it. +block.tau-mech-pad.description = Leave your current vessel and change into a support mech which can heal friendly buildings and units.\nUse the pad by double tapping while standing on it. +block.delta-mech-pad.description = Leave your current vessel and change into a fast, lightly-armored mech made for hit-and-run attacks.\nUse the pad by double tapping while standing on it. +block.omega-mech-pad.description = Leave your current vessel and change into a bulky and well-armored mech, made for front-line assaults.\nUse the pad by double tapping while standing on it. +block.spirit-factory.description = Produces light drones which mine ore and repair blocks. +block.phantom-factory.description = Produces advanced drone units which are significantly more effective than a spirit drone. +block.wraith-factory.description = Produces fast, hit-and-run interceptor units. +block.ghoul-factory.description = Produces heavy carpet bombers. +block.dagger-factory.description = Produces basic ground units. +block.titan-factory.description = Produces advanced, armored ground units. +block.fortress-factory.description = Produces heavy artillery ground units. +block.revenant-factory.description = Produces heavy laser air units. +block.repair-point.description = Continuously heals the closest damaged unit in its vicinity. +block.conduit.description = Basic liquid transport block. Works like a conveyor, but with liquids. Best used with extractors, pumps or other conduits. +block.pulse-conduit.description = Zaawansowany blok do przenoszenia cieczy. Transportuje je szybciej i magazynuje więcej niż standardowe rury. +block.phase-conduit.description = Zaawansowany blok do przenoszenia cieczy. Używa prądu, aby przenieść ciecz do połączonego phase conduit przez kilka bloków. +block.liquid-router.description = Accepts liquids from one direction and outputs them to up to 3 other directions equally. Can also store a certain amount of liquid. Useful for splitting the liquids from one source to multiple targets. +block.liquid-tank.description = Stores a large amount of liquids. Use it for creating buffers when there is a non-constant demand of materials or as a safeguard for cooling vital blocks. +block.liquid-junction.description = Działa jak most dla dwóch krzyżujących się rur. Przydatne w sytuacjach, kiedy dwie rury mają różne ciecze do różnych lokacji. +block.bridge-conduit.description = Zaawansowany blok przenoszący ciecze. Pozwala na przenoszenie cieczy nawet do 3 bloków na każdym terenie, przez każdy budynek. +block.mechanical-pump.description = Tania pompa o niskiej przepustowości. Nie wymaga prądu. +block.rotary-pump.description = Zaawansowana pompa, dwukrotnie większa przepustowość od mechanicznej pompy. Wymaga prądu. +block.thermal-pump.description = The ultimate pump. Three times as fast as a mechanical pump and the only pump which is able to retrieve lava. +block.router.description = Akceptuje przedmioty z jednego miejsca i rozdziela je do trzech innych kierunków. Przydatne w rozdzielaniu materiałów z jednego źródła do wielu celów. +block.distributor.description = Zaawansowany rozdzielacz, rozdzielający przedmioty do 7 innych kierunków. +block.bridge-conveyor.description = Zaawansowany blok transportujący. Pozwala na przenoszenie przedmiotów nawet do 3 bloków na każdym terenie, przez każdy budynek. +block.item-source.description = Wydziela przedmioty w nieskończoność. Dostępny tylko w trybie sandbox. +block.liquid-source.description = Wydziela ciecz w nieskończoność. Dostępny tylko w trybie sandbox. +block.item-void.description = Niszczy wszystkie przedmioty, które idą do tego bloku, który nie wymaga prądu. Dostępny tylko w trybie sandbox. +block.power-source.description = Wydziela prąd w nieskończoność. Dostępny tylko w trybie sandbox. +block.power-void.description = Niszczy całą energię wprowadzoną do tego bloku. Dostępny tylko w trybie sandbox. +liquid.water.description = Powszechnie używana do schładzania budowli i przetwarzania odpadów. +liquid.oil.description = Może się palić, eksplodować lub być używana do schładzania. +liquid.cryofluid.description = Najefektywniejsza ciecz do schładzania budowli. diff --git a/core/assets/bundles/bundle_pt_BR.properties b/core/assets/bundles/bundle_pt_BR.properties index 664b1e25a1..107330d9f0 100644 --- a/core/assets/bundles/bundle_pt_BR.properties +++ b/core/assets/bundles/bundle_pt_BR.properties @@ -1,471 +1,947 @@ -text.about=Criado por [ROYAL]Anuken.[]\nOriginalmente uma entrada para a [orange]GDL[] MM Jam.\n\nCredits:\n- SFX feito com [YELLOW]bfxr[]\n- Música feita por [GREEN]RoccoW[] / encontrada em [lime]FreeMusicArchive.org[]\n\nAgradecimento especial para:\n- [coral]MitchellFJN[]: playtesting extensivo e feedback\n- [sky]Luxray5474[]: wiki work e contribuições com código\n- Todos os beta testers do itch.io e Google Play\n -text.discord=Junte-se ao Discord do Mindustry! (Lá nós falamos em inglês) -text.gameover=O núcleo foi destruído. -text.highscore=[YELLOW]Novo recorde! -text.lasted=Você durou até a horda -text.level.highscore= Melhor\npontuação: [accent] {0} -text.level.delete.title=Confirmar exclusão -text.level.delete=Você tem certeza que quer excluir\no mapa "[orange]{0}"? -text.level.select=Seleção de Fase -text.level.mode=Modo de Jogo: -text.savegame=Salvar Jogo -text.loadgame=Carregar Jogo -text.quit=Sair -text.save.overwrite=Você tem certeza que quer salvar sobre este slot? -text.overwrite=Salvar sobre -text.saveload=[accent]Salvando... -text.savefail=Falha ao salvar jogo! -text.selectslot=Selecione um slot para salvar. -text.slot=[accent]Slot {0} -text.save.corrupted=[orange]Arquivo corrompido ou inválido! -text.empty= -text.save.wave=Horda {0} -text.save.date=Último salvamento: {0} -text.confirm=Confirmar -text.delete=Excluir -text.ok=OK -text.open=Abrir -text.cancel=Cancelar -text.openlink=Abrir Link -text.back=Voltar -text.quit.confirm=Você tem certeza que quer sair? -text.loading=[accent]Carregando... -text.wave=[orange]Horda {0} -text.wave.waiting=Horda em {0} -text.waiting=Aguardando... -text.countdown=Horda em {0} -text.enemies={0} Inimigos restantes -text.enemies.single={0} Inimigo restante -text.loadimage=Carregar\nImagem -text.saveimage=Salvar\nImagem -text.editor.badsize=[orange]Dimensões de imagem inválidas![]\nDimensões de mapa válidas: {0} -text.editor.errorimageload=Erro ao carregar arquivo de imagem:\n[orange]{0} -text.editor.errorimagesave=Erro ao salvar arquivo de imagem:\n[orange]{0} -text.editor.generate=Gerar -text.editor.resize=Redimen\n sionar -text.editor.loadmap=Carregar\n Mapa -text.editor.savemap=Salvar\n Mapa -text.editor.loadimage=Carregar\n Imagem -text.editor.saveimage=Salvar\nImagem -text.editor.unsaved=[scarlet]Você tem alterações não salvas![]\nTem certeza que quer sair? -text.editor.brushsize=Tamanho do pincel: {0} -text.editor.noplayerspawn=Este mapa não tem ponto de spawn para o jogador! -text.editor.manyplayerspawns=Mapas não podem ter mais de um\nponto de spawn para jogador! -text.editor.manyenemyspawns=Não pode haver mais de\n{0} pontos de spawn para inimigos! -text.editor.resizemap=Redimensionar Mapa -text.editor.resizebig=[scarlet]Aviso!\n[]Mapas maiores que 256 unidades podem ser 'lentos' e instáveis -text.editor.mapname=Nome do Mapa: -text.editor.overwrite=[accent]Aviso!\nIsso sobrescreve um mapa existente. -text.editor.failoverwrite=[crimson]Não é possível salvar sobre o mapa padrão! -text.editor.selectmap=Selecione uma mapa para carregar: -text.width=Largura: -text.height=Altura: -text.randomize=Aleatório -text.apply=Aplicar -text.update=Atualizar -text.menu=Menu -text.play=Jogar -text.load=Carregar -text.save=Salvar -text.settings=Configurações -text.tutorial=Tutorial -text.editor=Editor -text.mapeditor=Editor de mapa -text.donate=Doar -text.settings.reset=Restaurar Padrões -text.settings.controls=Controles -text.settings.game=Jogo -text.settings.sound=Som -text.settings.graphics=Gráficos -text.upgrades=Melhorias -text.purchased=[LIME]Comprado! -text.weapons=Arsenal -text.paused=Pausado -text.respawn=Reaparecendo em -text.error.title=[crimson]Um erro ocorreu -text.error.crashmessage=[SCARLET]Um erro inesperado aconteceu, que pode ter causado o jogo a fechar. []Por favor, informe as exatas circunstâncias em que o erro ocorreu ao desenvolvidor:\n[ORANGE]anukendev@gmail.com[] -text.error.crashtitle=Um erro ocorreu. -text.blocks.extrainfo=[accent]Informação extra: -text.blocks.blockinfo=Informação do Bloco -text.blocks.powercapacity=Capacidade de Energia -text.blocks.powershot=Energia/tiro -text.blocks.powersecond=Energia/segundo -text.blocks.powerdraindamage=Energia/dano -text.blocks.shieldradius=Raio do Escudo -text.blocks.itemspeedsecond=Itens/segundo -text.blocks.range=Alcance -text.blocks.size=Tamanho -text.blocks.powerliquid=Energia/Líquido -text.blocks.maxliquidsecond=Entrada Máx. Líquido/segundo -text.blocks.liquidcapacity=Capacidade de Líquido -text.blocks.liquidsecond=Líquido/segundo -text.blocks.damageshot=Dano/tiro -text.blocks.ammocapacity=Munição Máxima -text.blocks.ammo=Munição -text.blocks.ammoitem=Munição/item -text.blocks.maxitemssecond=Máximo de itens/segundo -text.blocks.powerrange=Alcance da Energia -text.blocks.lasertilerange=Alcance do Laser (em células) -text.blocks.capacity=Capacidade -text.blocks.itemcapacity=Capacidade de Itens -text.blocks.powergenerationsecond=Geração de Energia/segundo -text.blocks.generationsecondsitem=Tempo de geração/item -text.blocks.input=Entrada -text.blocks.inputliquid=Líquido de entrada -text.blocks.inputitem=Item de entrada -text.blocks.output=Saída -text.blocks.secondsitem=Segundos/item -text.blocks.maxpowertransfersecond=Transferência máxima de Energia/segundo -text.blocks.explosive=Altamente Explosivo! -text.blocks.repairssecond=Reparo/segundo -text.blocks.health=Saúde -text.blocks.inaccuracy=Imprecisão -text.blocks.shots=Tiros -text.blocks.shotssecond=Taxa de tiro -text.placemode=Modo construção -text.breakmode=Modo remoção -text.health=Saúde -setting.difficulty.easy=Fácil -setting.difficulty.normal=Normal -setting.difficulty.hard=Difícil -setting.difficulty.name=Dificuldade -setting.screenshake.name=Balanço da Tela -#Tremor da tela? -setting.smoothcam.name=Câmera suave -#Suavizar Câmera? -setting.indicators.name=Indicadores de Inimigos -setting.effects.name=Particulas -setting.sensitivity.name=Sensibilidade do Controle -setting.fps.name=Mostrar FPS -setting.vsync.name=VSync -setting.lasers.name=Mostrar lasers -setting.healthbars.name=Mostrar barra de saúde de entidades -setting.pixelate.name=Pixelar Tela -setting.musicvol.name=Volume da Música -setting.mutemusic.name=Desligar Musica -setting.sfxvol.name=Volume de Efeitos -setting.mutesound.name=Desligar Som -map.maze.name=maze -map.fortress.name=fortress -map.sinkhole.name=sinkhole -map.caves.name=caves -map.volcano.name=volcano -map.caldera.name=caldera -map.scorch.name=scorch -map.desert.name=desert -map.island.name=island -map.grassland.name=grassland -map.tundra.name=tundra -map.spiral.name=spiral -map.tutorial.name=tutorial -tutorial.intro.text=[yellow]Bem-vindo ao tutorial.[] Para começar aperte 'próximo'. -tutorial.moveDesktop.text=Para mover, use as teclas [orange][[WASD][]. Segure [orange]shift[] para mover rápido. Segure [orange]CTRL[] enquanto usa a [orange]roda do mouse[] para aumentar ou diminuir o zoom. -tutorial.shootInternal.text=Use o mouse para mirar, segure [orange]botão esquerdo do mouse[] para atirar. Tente praticar no [yellow]alvo[]. -tutorial.moveAndroid.text=Para arrastar a visão, passe um dedo pela tela. Pince com os dedos para aumentar ou diminuir o zoom. -tutorial.placeSelect.text=Tente selecionar uma [yellow]esteira[] do menu de blocos no canto inferior direito. -tutorial.placeConveyorDesktop.text=Use a [orange][[roda do mouse][] para girar a esteira até que aponte [orange]para frente[], então coloque-a no [yellow]local marcado[] usando o [orange][[botão esquerdo do mouse][]. -tutorial.placeConveyorAndroid.text=Use o [orange][[botão de girar][] para girar a esteira para que aponte [orange]para frente[], arraste-a para a posição e então coloque-a na [yellow]posição marcada[] usando o [orange][[botão de confirmação][]. -tutorial.placeConveyorAndroidInfo.text=Você também pode apertar no ícone com uma cruz no canto inferior esquerdo para alterar para o [orange][[modo de toque][], e colocar blocos apertando na tela. No modo de toque, blocos podem ser girados com a seta no canto inferior esquerdo. Aperte [yellow]próximo[] para tentar. -tutorial.placeDrill.text=Agora selecione e coloque uma [yellow]broca de pedra[] no local marcado. -tutorial.blockInfo.text=Se quiser saber mais sobre os blocos, você pode apertar o [orange]símbolo de interrogação[] no canto superior direito para ler mais. -tutorial.deselectDesktop.text=Você pode cancelar a seleção de um bloco usando o [orange][botão direito do mouse[]. -tutorial.deselectAndroid.text=ocê pode cancelar a seleção de um bloco apertando o botão [orange]X[]. -tutorial.drillPlaced.text=A broca produzirá [yellow]pedra[], direcionando o produzido para a esteira a qual moverá a pedra para o [yellow]núcleo[]. -tutorial.drillInfo.text=Minérios diferentes precisam de diferentes brocas. Pedra precisam de brocas de pedra, Ferro de brocas de ferro, etc. -tutorial.drillPlaced2.text=Itens movidos para o núcleo são colocados em seu [yellow]inventário[], no canto superior esquerdo. Colocar blocos gasta os recursos do inventário. -tutorial.moreDrills.text=Você pode conectar várias brocas e esteiras, veja. -tutorial.deleteBlock.text=Você pode excluir blocos clickando com o [orange]botão direito do mouse[] no bloco que quiser destruir. Tente excluir esta esteira. -tutorial.deleteBlockAndroid.text=Você pode excluir blocos [orange]apertando na cruz[] no [orange]menu modo de quebra[] no canto inferior esquerdo e então apertando no bloco desejado. Tente excluir esta esteira. -tutorial.placeTurret.text=Agora, selecione e construa uma [yellow]torre[] no [yellow]local marcado[]. -tutorial.placedTurretAmmo.text=Esta torre aceitará [yellow]munição[] da esteira. Você pode ver quanta munição elas tem passando o mouse sobre elas e verificando a [green]barra verde[]. -tutorial.turretExplanation.text=As torres irão atirar no inimigo mais próximo que estiver ao alcance, contanto que tenham munição suficiente. -tutorial.waves.text=A cada [yellow]60[] segundos, uma horda de [coral]inimigos[] irá aparecer em locais específicos e tentará destruir o núcleo. -tutorial.coreDestruction.text=Seu objetivo é [yellow]defender o núcleo[]. Se o núcleo for destruído, vecê [coral]perde o jogo[]. -tutorial.pausingDesktop.text=Se você precisar parar por alguns instantes, aperte o [orange]botão de pausa[] no canto superior esquerdo ou [orange]barra de espaço[] para pausar o jogo. Você pode colocar blocos enquanto o jogo esta pausado, porém não poderá se mover ou atirar. -tutorial.pausingAndroid.text=Se você precisar parar por alguns instantes, aperte o [orange]botão de pausa[] no canto superior esquerdo ou [orange]barra de espaço[] para pausar o jogo. Você pode colocar blocos enquanto o jogo esta pausado. -tutorial.purchaseWeapons.text=Você pode comprar novas [yellow]armas[] para seu mecha, basta abrir o menu de melhorias no canto inferior esquerdo. -tutorial.switchWeapons.text=Alterne entre suas armas clickando em seu ícone ou usando as teclas numéricas [orange][[1-9][]. -tutorial.spawnWave.text=Uma horda esta vindo. Destrúa-os. -tutorial.pumpDesc.text=Em hordas mais avançadas, você talvez precise de [yellow]bombas[] para distribuir líquidos para geradores ou extratores. -tutorial.pumpPlace.text=Bombas trabalham de forma semelhante às brocas, porém elas produzem líquidos ao envés de minérios. Tente colocar uma bomba na [yellow]célula de petróleo designada[]. -tutorial.conduitUse.text=Agora coloque um [orange]cano[] levando para longe da bomba. -tutorial.conduitUse2.text=E mais alguns... -tutorial.conduitUse3.text=E mais alguns... -tutorial.generator.text=Agora coloque um [orange]gerador a combustão[] no final do cano. -tutorial.generatorExplain.text=Este gerador irá produzir [yellow]energia[] do petróleo. -tutorial.lasers.text=Energia é distribuida usando [yellow]lasers[]. Gire e coloque um aqui. -tutorial.laserExplain.text=O gerador irá mover energia para o bloco do laser. Um feixe [yellow]opaco[] significa que a energia está sendo transmitida, e um feixe [yellow]transparente[] significa que não. -tutorial.laserMore.text=Você pode verificar quanta energia um bloco tem ao passar o mouse sobre eles e verificando a [yellow]barra amarela[] no topo. -tutorial.healingTurret.text=Este laser pode ser usado para energizar uma [lime]torre de reparo[]. Coloque uma aqui. -tutorial.healingTurretExplain.text=Enquanto tiver energia, esta torre irá [lime]reparar blocos próximos.[] Quando jogar, tenha certeza de construir uma dessas próximas do núcleo o mais rápido possível! -tutorial.smeltery.text=Muitos blocos precisam de [orange]aço[] para serem construídos, o que requer uma [orange]fundidora[] para ser feito. Coloque uma aqui. -tutorial.smelterySetup.text=Esta fundidora irá produzir [orange]aço[] quando receber carvão e ferro. -tutorial.end.text=E este é o fim do Tutorial! Boa Sorte! -keybind.move_x.name=move_x -keybind.move_y.name=move_y -keybind.select.name=selecionar -keybind.break.name=quebrar -keybind.shootInternal.name=atirar -keybind.zoom_hold.name=segurar_zoom -keybind.zoom.name=zoom -keybind.menu.name=menu -keybind.pause.name=pausar -keybind.dash.name=Correr -keybind.rotate_alt.name=girar_alt* -keybind.rotate.name=girar -keybind.weapon_1.name=Arma 1 -keybind.weapon_2.name=Arma 2 -keybind.weapon_3.name=Arma 3 -keybind.weapon_4.name=Arma 4 -keybind.weapon_5.name=Arma 5 -keybind.weapon_6.name=Arma 6 -mode.waves.name=hordas -mode.sandbox.name=sandbox -#CAIXINHA DE AREIA -mode.freebuild.name=construção \nlivre -weapon.blaster.name=Blaster -weapon.blaster.description=Atira um projétil lento e fraco. -weapon.triblaster.name=Blaster Triplo -weapon.triblaster.description=Atira 3 balas que se espalham. -weapon.multigun.name=Escopeta -weapon.multigun.description=Atira balas com baixa precisão e uma\n alta taxa de disparo. -weapon.flamer.name=Lança-Chamas -weapon.railgun.name=Rifle Sniper -weapon.flamer.description=É um lança-chamas. O que mais ele faria? -weapon.railgun.description=Atira um projétil de longo alcance. -weapon.mortar.name=Morteiro -weapon.mortar.description=Atira um projétil lento, porém devastador. -item.stone.name=Pedra -item.iron.name=Ferro -item.coal.name=Carvão -item.steel.name=Aço -item.titanium.name=Titânio -item.dirium.name=Dírio -item.uranium.name=Urânio -liquid.water.name=Água -liquid.plasma.name=Plasma -liquid.lava.name=Lava -liquid.oil.name=Petróleo -block.air.name=Ar -block.blockpart.name=blockpart -#que? -block.deepwater.name=Água Profunda -block.water.name=Água -block.lava.name=Lava -block.oil.name=Petróleo -block.stone.name=Pedra -block.blackstone.name=Pedra Escura -block.iron.name=Ferro -block.coal.name=Carvão -block.titanium.name=Titânio -block.uranium.name=Urânio -block.dirt.name=Terra -block.sand.name=Areia -block.ice.name=Gelo -block.snow.name=Neve -block.grass.name=Grama -block.sandblock.name=Bloco de Areia -block.snowblock.name=Bloco de Neve -block.stoneblock.name=Rocha -block.blackstoneblock.name=Rocha Escura -block.grassblock.name=Bloco de Grama -block.mossblock.name=Musgo -block.shrub.name=Arbusto -block.rock.name=Rocha -block.icerock.name=Rocha de Gelo -block.blackrock.name=Rocha Escura -block.dirtblock.name=Bloco de Terra -block.stonewall.name=Parede de Pedra -block.stonewall.fulldescription=Um bloco defensivo barato. Útil para proteger o núcleo e torres nas primeiras hordas. -block.ironwall.name=Parede de Ferro -block.ironwall.fulldescription=Um bloco defensivo básico. Fornece proteção contra inimigos. -block.steelwall.name=Parede de aço -block.steelwall.fulldescription=Um bloco defensivo padrão. Fornece proteção contra inimigos. -block.titaniumwall.name=Parede de Titânio -block.titaniumwall.fulldescription=Um bloco defensivo forte. Fornece proteção contra inimigos. -block.duriumwall.name=Parede de Dírio -block.duriumwall.fulldescription=Um bloco defensivo muito forte. Fornece proteção contra inimigos. -block.compositewall.name=Parede de Composto -block.compositewall.fulldescription= Um bloco defensivo extremamente forte. Fornece a melhor proteção contra inimigos. -block.steelwall-large.name=Parede Grande de Aço -block.steelwall-large.fulldescription=Um bloco defensivo padrão. Ocupa multiplas células. -block.titaniumwall-large.name=Parede Grande de Titânio -block.titaniumwall-large.fulldescription=Um bloco defensivo forte. Ocupa multiplas células. -block.duriumwall-large.name=Parede Grande de Dírio -block.duriumwall-large.fulldescription=Um bloco defensivo muito forte. Ocupa multiplas células. -block.titaniumshieldwall.name=Parede com Escudo -block.titaniumshieldwall.fulldescription=Um bloco defensivo forte, com um escudo de energia imbutido. Usa energia passivamente e para absorver projéteis inimigos. É recomendado usar distribuidores de energia para abastecer este bloco. -#A strong defensive block, with an extra built-in shield. Requires power. Uses energy to absorb enemy bullets. It is recommended to use power boosters to provide energy to this block. -block.repairturret.name=Torre de Reparo -block.repairturret.fulldescription=Lentamente repara blocos danificados dentro do seu alcance. Consome um pouco de energia. -#Repairs nearby damaged blocks in range at a slow rate. Uses small amounts of power. -block.repairturret.description=[powerinfo]Consome Energia.[white]\nRepara blocos próximos. -block.megarepairturret.name=Torre de Reparo II -block.megarepairturret.fulldescription=Repara blocos danificados dentro do seu alcance. Consome um pouco de energia. -block.megarepairturret.description=[powerinfo]Consome Energia.[white]\nRepara blocos próximos. -block.shieldgenerator.name=Gerador de Escudo -block.shieldgenerator.fulldescription= Um bloco defensivo avançado. Protege todos os blocos em um raio. Lentamente usa energia quando parado, mas rapidamente drena em contato com projéteis. -#An advanced defensive block. Shields all the blocks in a radius from attack. Uses power at a slow rate when idle, but drains energy quickly on bullet contact. -block.door.name=Porta -block.door.fulldescription=Um bloco que pode ser aberto e fechado ao tocar nele. -block.door.description=Abre e Fecha.\n[interact]Toque para alternar o estado. -block.door-large.name=Porta Grande -block.door-large.fulldescription=Um bloco que pode ser aberto e fechado ao tocar nele. -block.door-large.description=Abre e Fecha.\n[interact]Toque para alternar o estado. -block.conduit.name=Cano -block.conduit.fulldescription=Bloco de transporte de líquido básico. Funciona como uma esteira, mas com líquidos. Pode ser usado como uma ponte para inimigos e jogadores. -#Basic liquid transport block. Works like a conveyor, but with liquids. Best used with pumps or other conduits. Can be used as a bridge over liquids for enemies and players. -block.pulseconduit.name=Cano de impulso -block.pulseconduit.fulldescription=Bloco de transporte de líquido avançado. Transporta líquidos mais rapidamente e armazena mais que canos normais. -#Advanced liquid transport block. Transports liquids faster and stores more than standard conduits. -block.liquidrouter.name=Roteador de líquido -block.liquidrouter.fulldescription=Aceita líquido de uma direção e o redireciona para as outras 3 direções. Útil para dividir o líquido entre vários canos. -#Works similarly to a router. Accepts liquid input from one side and outputs it to the other sides. Useful for splitting liquid from a single conduit into multiple other conduits. -block.liquidrouter.description=Divide líquidos em 3 direções. -block.conveyor.name=Esteira -block.conveyor.fulldescription=Bloco de transporte básico. Movimenta itens para frente e automaticamente os deposita em torres ou blocos de fabricação. Pode ser girado. Pode ser usado como uma ponte para inimigos e jogadores. -#Basic item transport block. Moves items forward and automatically deposits them into turrets or crafters. Rotatable. Can be used as a bridge over liquids for enemies and players. -block.steelconveyor.name=Esteira de aço -block.steelconveyor.fulldescription=Bloco de transporte avançado. Movimenta itens mais rapidamente que esteiras normais. -#Advanced item transport block. Moves items faster than standard conveyors. -block.poweredconveyor.name=Esteira de Impulso -block.poweredconveyor.fulldescription=O Bloco supremo de transporte. Movimenta itens mais rapidamente que esteiras de aço. -#The ultimate item transport block. Moves items faster than steel conveyors. -block.router.name=Roteador -block.router.fulldescription=Aceita itens de uma direção e os redireciona para as outras 3 direções. Pode guardar uma certa quantidade de itens. Útil para dividir materiais entre várias torres. -block.router.description=Divide materiais em 3 direções. -block.junction.name=Junção -block.junction.fulldescription=Funciona como uma ponte para 2 linhas de esteiras que se cruzam. Útil em situações onde duas esteiras carregam diferentes materiais para diferentes locais. -block.junction.description=Funciona como uma junção para as esteiras. -block.conveyortunnel.name=Túnel de esteira -block.conveyortunnel.fulldescription=Transporta itens por baixo de blocos. Para usar coloque um túnel apontado para o bloco que deseja passar por baixo, e outro apontado para o primeiro túnel. -block.conveyortunnel.description=Transporta intes por baixo de blocos. -block.liquidjunction.name=Junção de líquido -block.liquidjunction.fulldescription=Funciona como uma ponte para 2 canos que se cruzam. Útil em situações onde 2 canos diferentes carregam diferentes líquidos para diferentes locais. -block.liquiditemjunction.name=liquid-item junction -block.liquiditemjunction.fulldescription=Acts as a bridge for crossing conduits and conveyors. -block.liquiditemjunction.description=Serves as a junction for items and liquids. -block.powerbooster.name=Distribuidor de energia -block.powerbooster.fulldescription=Distribui energia para todos os blocos dentro de seu raio. -block.powerbooster.description=Distribui energia em um raio. -block.powerlaser.name=Laser -#Laser de energia? -block.powerlaser.fulldescription=Cria um laser que transmite energia para o bloco à sua frente. Melhor usado com geradores ou outros lasers. Não gera energia. -block.powerlaser.description=Transmite energia. -block.powerlaserrouter.name=laser duplo -block.powerlaserrouter.fulldescription=Divide a entrada de energia em 3 lasers. Útil em situações onde é necessário conectar muitos blocos a partir de um gerador. -block.powerlaserrouter.description=Divide a entrada de energia em 3 lasers. -block.powerlasercorner.name=laser triplo -#*Essa nomeação ficou escrota -block.powerlasercorner.fulldescription=Laser que distribui energia para duas direções ao mesmo tempo. Útil em situações onde é necessário conectar muitos blocos a partir de um gerador. -block.powerlasercorner.description=Divide a entrada de energia em 2 lasers. -block.teleporter.name=Teleportador -block.teleporter.fulldescription=Bloco avançado de transporte de itens. Teleportadores transferem itens para outros teleportadores da mesma cor. Não faz nada se não houverem outros da mesma cor. Se houverem múltiplos da mesma cor, um aleatório será selecionado. Toque nas flechas para mudar de cor. -block.teleporter.description=[interact]Tap block to config[] -block.sorter.name=Ordenador -block.sorter.fulldescription=Separa itens pelo tipo de material. O material a ser aceito é indicado pela cor do bloco. Todos os itens que correspondem ao material a ser separado são direcionados para frente, todo o resto é direcionado para os lados. -block.sorter.description=[interact]Aperte no bloco para configurar[] -block.core.name=núcleo -block.pump.name=bomba -block.pump.fulldescription=Bombeia líquidos de um bloco, geralmente água, lava ou petróleo. Os líquidos são bombeados para canos próximos. -block.pump.description=Bombeia líquidos para canos próximos. -block.fluxpump.name=Bomba de fluxo -block.fluxpump.fulldescription=Uma versão avançada da bomba comum. Guarda mais líquido e bombeia mais rápido. -block.fluxpump.description=Bombeia líquidos para canos próximos. -block.smelter.name=Fornalha -block.smelter.fulldescription=O bloco de produção essencial. Quando recebe 1 carvão e\n1 ferro produz 1 aço -block.smelter.description=Converte carvão + ferro em aço. -block.crucible.name=Usina de fundição -block.crucible.fulldescription=Um bloco de produção avançado. Quando recebe 1 titânio e 1 aço produz 1 dírio. -block.crucible.description=Converte aço + titânio em dírio. -block.coalpurifier.name=Extrator de carvão -block.coalpurifier.fulldescription=Um bloco extrator básico. Produz carvão quando fornecido com grandes quantidades de água e pedra. -block.coalpurifier.description=Converte pedra + água em carvão. -block.titaniumpurifier.name=Extrator de titânio -block.titaniumpurifier.fulldescription=Um bloco extrator padrão. Produz titânio quando fornecido com grandes quantidas de água e ferro. -block.titaniumpurifier.description=Converte água e ferro em titânio. -block.oilrefinery.name=Refinaria de Petróleo -block.oilrefinery.fulldescription=Refina grande quantidades de petróleo para produzir carvão. Útil para abastecer torres que utilizam carvão quando jazidas de carvão são escassas. -block.oilrefinery.description=Converte petróleo em carvão. -block.stoneformer.name=Formador de Pedra -block.stoneformer.fulldescription=Solidifica lava para formar pedra. Útil para produzir grandes quantidades de pedra para extratores de carvão. -block.stoneformer.description=Converte lava em pedra. -block.lavasmelter.name=Fornalha à Lava -block.lavasmelter.fulldescription=Usa lava para converter ferro em aço. Uma alternativa para a fundidora. Útil em situações onde não há carvão por perto. -block.lavasmelter.description=Converte ferro + lava em aço. -block.stonedrill.name=Broca de pedra -block.stonedrill.fulldescription=A broca essencial. Quando colocada em uma jazida de pedra gera pedra indefinidamente. -block.stonedrill.description=Gera 1 pedra a cada 4 segundos. -#Mines 1 stone every 4 seconds. -block.irondrill.name=Broca de Ferro -block.irondrill.fulldescription=Uma broca básica. Quando colocada sobre uma jazida de ferro, lentamente gera ferro. -#A basic drill. When placed on iron ore tiles, outputs iron at a slow pace indefinitely. -block.irondrill.description=Gera 1 ferro a cada 5 segundos. -block.coaldrill.name=Broca de Carvão -block.coaldrill.fulldescription=Uma broca básica. Quando colocada sobre uma jazida de carvão, lentamente gera carvão. -block.coaldrill.description=Gera 1 carvão a cada 5 segundos. -block.uraniumdrill.name=Broca de Urânio -block.uraniumdrill.fulldescription=Uma broca avançada. Quando colocada sobre uma jazida de urânio, lentamente gera urânio. -block.uraniumdrill.description=Gera 1 Urânio a cada 7 segundos. -block.titaniumdrill.name=Broca de Titânio -block.titaniumdrill.fulldescription=Uma broca avançada. Quando colocada sobre uma jazida de titânio, lentamente gera titânio. -block.titaniumdrill.description=Gera 1 Titânio a cada 5 segundos. -block.omnidrill.name=Omnibroca -block.omnidrill.fulldescription=A broca suprema. Rapidamente extrai qualquer minério em que é colocada. -#The ultimate drill. Will mine any ore it is placed on at a rapid pace. -block.omnidrill.description=Gera 1 de qualquer recurso a cada 3 segundos. -block.coalgenerator.name=Gerador à Carvão -#Crase ou não? -block.coalgenerator.fulldescription=O gerador essencial. Gera energia a partir de carvão. Distribui energia em forma de laser para os 4 lados. -block.coalgenerator.description=Gera energia a partir de carvão. -block.thermalgenerator.name=Gerador Térmico -block.thermalgenerator.fulldescription=Gera energia a partir de lava. Distribui energia em forma de laser para os 4 lados. -block.thermalgenerator.description=Gera energia a partir de lava. -block.combustiongenerator.name=Gerador à Combustão -block.combustiongenerator.fulldescription=Gera energia a partir de petróleo. Distribui energia em forma de laser para os 4 lados. -block.combustiongenerator.description=Gera energia a partir de petróleo. -block.rtgenerator.name=Gerador RTG -block.rtgenerator.fulldescription=Gera pouca quantidade de energia a partir do decaimento radioativo do urânio. Distribui energia em forma de laser para os 4 lados. -block.rtgenerator.description=Gera energia a partir de Urânio. -block.nuclearreactor.name=Reator Nuclear -block.nuclearreactor.fulldescription=Uma versão avançada do gerador RTG. Gera energia a partir de Urânio. Requer constante resfriamento à água. Altamente volátil; explodirá violentamente se não for suprido com quantiddades suficientes de água. -block.turret.name=Torre Comum -block.turret.fulldescription=Uma torre básica e barata. Usa pedra como munição. Tem alcance um pouco maior que a torre dupla. -block.turret.description=[turretinfo]Munição: pedra -block.doubleturret.name=Torre Dupla -block.doubleturret.fulldescription=Uma versão um pouco mais poderosa do que a torre comum. Usa pedra como munição. Causa um dano maior, porém tem menor alcance. Atira dois projéteis. -block.doubleturret.description=[turretinfo]Munição: pedra -block.machineturret.name=Torre Automática -block.machineturret.fulldescription=Uma torre padrão completa. Usa ferro como munição. Tem alta taxa de disparo e dano decente. -block.machineturret.description=[turretinfo]Munição: ferro -block.shotgunturret.name=Torre Splitter -#Splitter turret -block.shotgunturret.fulldescription=Uma torre padrão. Usa ferro como munição. Atira 7 balas em forma de cone. Pouco alcance, porém maior dano do que a Torre Dupla. -block.shotgunturret.description=[turretinfo]Munição: ferro -block.flameturret.name=Torre lança-\nchamas -block.flameturret.fulldescription=Torre avançada de baixo alcance. Usa carvão. Pouco alcance mas alto dano. Boa para trechos estreitos. Recomenda-se usá-la atŕas de paredes. -block.flameturret.description=[turretinfo]Munição: carvão -block.sniperturret.name=Torre Sniper -#Torre Railgun? -block.sniperturret.fulldescription=Torre avançada de longo alcance. Usa aço como munição. Dano altíssimo, porém baixa taxa de disparo. Cara para usar, porém pode ser colocada longe das linhas inimigas dado seu alcance. -block.sniperturret.description=[turretinfo]Munição: aço -block.mortarturret.name=Torre Flak -block.mortarturret.fulldescription=Torre avançada de dano em área. Usa carvão. Taxa de disparo e balas lentas, mas alto dano em alvo único ou distribuído. -block.mortarturret.description=[turretinfo]Munição: carvão -block.laserturret.name=Torre laser -block.laserturret.fulldescription=Torre de alvo único avançada. Usa energia. Boa torre de alcance médio e uso geral. Alvo único apenas. Nunca erra. -block.laserturret.description=[turretinfo]Usa Energia -block.waveturret.name=Torre Tesla -block.waveturret.fulldescription=Torre de múltiplos alvos avançada. Usa Energia. Alcance médio. Nunca erra. Dano médio-baixo, porém pode acertar vários inimigos simultaneamente com raios conectados. -block.waveturret.description=[turretinfo]Usa Energia -block.plasmaturret.name=Torre de Plasma -block.plasmaturret.fulldescription=Versão altamente avançada da Torre lança-chamas. Usa carvão. Dano altíssimo e alcance médio-baixo. -block.plasmaturret.description=[turretinfo]Munição: carvão -block.chainturret.name=Canhão automático -block.chainturret.fulldescription=A torre de tiro rápido mais avançada. Usa Urânio como munição. Atira grandes projéteis rapidamente. Alcance médio. Ocupa várias células. Extremamente resistente. -block.chainturret.description=[turretinfo]Munição: Urânio -block.titancannon.name=Canhão Titã -block.titancannon.fulldescription=A torre de longo alcance mais avançada. Usa Urânio como munição. Atira várias balas de dano em área à uma taxa de disparo média. Alto alcance. Ocupa várias células. Extremamente resistente. -block.titancannon.description=[turretinfo]Munição: Urânio -block.playerspawn.name=playerspawn -block.enemyspawn.name=enemyspawn +credits.text = Created by [ROYAL]Anuken[] - [SKY]anukendev@gmail.com[]\n\n[GRAY](In case you can't tell, this text is currently unfinished.\nTranslators, don't edit it yet!) +credits = Creditos +contributors = Tradutores e contribuidores +discord = Junte-se ao Discord do Mindustry! (Lá nós falamos em inglês) +link.discord.description = O discord oficial do Mindustry +link.github.description = Codigo fonte do jogo. +link.dev-builds.description = Desenvolvimentos Instaveis +link.trello.description = Trello Oficial para Updates Planejados +link.itch.io.description = Pagina da Itch.io com os Downloads +link.google-play.description = Listamento do google play store +link.wiki.description = Wiki oficial do Mindustry +linkfail = Falha ao abrir o link\nO Url foi copiado +screenshot = Screenshot salvo para {0} +screenshot.invalid = Map too large, potentially not enough memory for screenshot. +gameover = O núcleo foi destruído. +gameover.pvp = O time[accent] {0}[] É vitorioso! +highscore = [YELLOW]Novo recorde! +stat.wave = Ondas derrotadas:[accent] {0} +stat.enemiesDestroyed = Enimigos Destruídos:[accent] {0} +stat.built = Construções construídas:[accent] {0} +stat.destroyed = Construções destruídas:[accent] {0} +stat.deconstructed = Construções desconstruídas:[accent] {0} +stat.delivered = Recursos lançados: +stat.rank = Rank Final: [accent]{0} +placeline = Você selecionou um bloco.\nVocê pode[accent] colocar uma linha[] por[accent] carregar o seu dedo por alguns segundos[] e arrastar em uma direção.\nTente. +removearea = Você selecionou o modo de remoção.\nVocê pode[accent] remover blocos dentro de um retângulo[] por[accent] carregar o seu dedo por alguns segundos[] e arrastar.\nTente. +launcheditems = [accent]Launched Items +map.delete = Certeza que quer deletar o mapa "[accent]{0}[]"? +level.highscore = Melhor\npontuação: [accent] {0} +level.select = Seleção de Fase +level.mode = Modo de Jogo: +showagain = Não mostrar na proxima sessão +coreattack = < O núcleo está sobre ataque! > +nearpoint = [[ [scarlet]SAIA DO PONTO DE SPAWN IMEDIATAMENTE[] ]\naniquilação iminente +outofbounds = [[ OUT OF BOUNDS ]\n[]auto destruição em {0} +database = banco do núcleo +savegame = Salvar Jogo +loadgame = Carregar Jogo +joingame = Entrar no Jogo +addplayers = Adicionar/Remover Jogador +customgame = Jogo Customizado +newgame = Novo Jogo +none = +minimap = MiniMapa +close = Fechar +quit = Sair +maps = Mapas +continue = Continuar +maps.none = [LIGHT_GRAY]Nenhum Mapa Encontrado! +about.button = Sobre +name = Nome: +noname = Pegue[accent] um nome[] primeiro. +filename = Nome do arquivo: +unlocked = Novo bloco Desbloqueado! +completed = [accent]Completado +techtree = Árvore de tecnologia +research.list = [LIGHT_GRAY]Pesquise: +research = Pesquisa +researched = [LIGHT_GRAY]{0} pesquisado. +players = {0} Jogadores Ativos +players.single = {0} Jogador Ativo +server.closing = [accent]Fechando servidor... +server.kicked.kick = Voce foi expulso do servidor! +server.kicked.serverClose = Servidor Fechado. +server.kicked.clientOutdated = Cliente desatualizado! Atualize seu jogo! +server.kicked.serverOutdated = Servidor desatualiado! Peca ao dono para atualizar! +server.kicked.banned = Voce foi banido do servidor. +server.kicked.recentKick = Voce foi banido recentemente.\nEspere para conectar de novo. +server.kicked.nameInUse = Este nome ja esta sendo usado\nneste servidor. +server.kicked.nameEmpty = Voce deve ter pelo menos uma letra ou numero. +server.kicked.idInUse = Voce ja esta neste servidor! Conectar com duas contas não é permitido. +server.kicked.customClient = Este servidor não suporta construções customizadas. Baixe a versão original. +server.kicked.gameover = Fim de jogo! +host.info = The [accent]Hospedar[]Botão Hopeda um servidor no Host[scarlet]6567[] e [scarlet]6568.[]\nQualquer um no [LIGHT_GRAY]Wi-fi Ou Internet local[] Pode ver este servidor na lista de servidores.\n\nSe voce quer poder entrar em qualquer servidor em seu ip, [accent]port forwarding[] é requerido.\n\n[LIGHT_GRAY]Note: Se alguem esta com problemas em conectar no seu servidor lan, Tenha certeza que deixou mindustry Acessar sua internet local nas configurações de firewall +join.info = Aqui, Você pode entar em um [accent]IP De servidor[] Para conectar, Ou descobrir [accent]Servidores[] Da rede local.\nAmbos os servidores LAN e WAN São suportados.\n\n[LIGHT_GRAY]Note: Não tem uma lista de servidores automaticos; Se você quer conectar ao IP de alguem, Você precisa pedir o IP Ao Rosteador. +hostserver = Hospedar servidor +hostserver.mobile = Hospedar\nJogo +host = Hospedar +hosting = [accent]Abrindo server... +hosts.refresh = atualizar +hosts.discovering = Descobrindo jogos em lan +server.refreshing = Atualizando servidor +hosts.none = [lightgray]Nenhum jogo lan encontrado! +host.invalid = [scarlet]Não foi possivel Hospedar. +trace = Traçar jogador +trace.playername = Nome do jogador: [accent]{0} +trace.ip = IP: [accent]{0} +trace.id = ID unico: [accent]{0} +trace.mobile = Mobile Client: [accent]{0} +trace.modclient = Cliente Customizado: [accent]{0} +invalidid = ID do cliente invalido! Reporte o bug. +server.bans = Banidos +server.bans.none = Nenhum jogador banido encontrado! +server.admins = Administradores +server.admins.none = Nenhum administrador encontrado! +server.add = Adicionar servidor +server.delete = Certeza que quer deletar o servidor? +server.hostname = Hospedar: {0} +server.edit = Editar servidor +server.outdated = [crimson]Servidor desatualizado![] +server.outdated.client = [crimson]Cliente desatualizado![] +server.version = [lightgray]Versão: {0} +server.custombuild = [yellow]Construção customizada +confirmban = Certeza que quer banir este jogador? +confirmkick = Certeza que quer expulsar o jogador? +confirmunban = Certeza que quer desbanir este jogador? +confirmadmin = Certeza que quer fazer este jogador um administrador? +confirmunadmin = Certeza que quer remover o estatus de adminstrador deste jogador? +joingame.title = Entrar no jogo +joingame.ip = IP: +disconnect = Desconectado. +disconnect.data = Falha ao abrir a data do mundo! +connecting = [accent]Conectando... +connecting.data = [accent]Carregando data do mundo... +server.port = Porte: +server.addressinuse = Senha em uso! +server.invalidport = Numero de port invalido! +server.error = [crimson]Erro ao hospedar o servidor: [accent]{0} +save.old = Este save é para uma versão antiga do jogo, E não pode ser usado.\n\n[LIGHT_GRAY]Salvar Versões antigas vai ser Implementado Na versão 4.0 completa +save.new = Novo Save +save.overwrite = Você tem certeza que quer salvar sobre este slot? +overwrite = Salvar sobre +save.none = Nenhum save encontrado! +saveload = [accent]Salvando... +savefail = Falha ao salvar jogo! +save.delete.confirm = Certeza que quer deletar este save? +save.delete = Deletar +save.export = Exportar save +save.import.invalid = [accent]Este save é invalido! +save.import.fail = [crimson]Falha ao importar save: [accent]{0} +save.export.fail = [crimson]Falha ao Exportar save: [accent]{0} +save.import = Importar save +save.newslot = Nome do save: +save.rename = Renomear +save.rename.text = Novo jogo: +selectslot = Selecione um slot para salvar. +slot = [accent]Slot {0} +save.corrupted = [accent]Arquivo corrompido ou inválido! +empty = +on = Ligado +off = Desligado +save.autosave = Autosalvar: {0} +save.map = Mapa: {0} +save.wave = Horda {0} +save.difficulty = Dificuldade: {0} +save.date = Último salvamento: {0} +save.playtime = Tempo De Jogo: {0} +warning = Aviso. +confirm = Confirmar +delete = Excluir +ok = OK +open = Abrir +customize = Customize +cancel = Cancelar +openlink = Abrir Link +copylink = Copiar link +back = Voltar +quit.confirm = Você tem certeza que quer sair? +changelog.title = registro de Mudanças +changelog.loading = Coletando o registro... +changelog.error.android = [accent]Note que o registro as vezes Funciona no android 4.4 e abaixo!\nIsso é por causa de um erro interno no sistema android. +changelog.error.ios = [accent]A registro não é suportada no IOS. +changelog.error = [scarlet]Erro ao coletar o registro!\nCheque a Conexão com a internet. +changelog.current = [yellow][[Primeira versão] +changelog.latest = [accent][[Ultima versão] +loading = [accent]Carregando... +saving = [accent]Salvando... +wave = [accent]Horda {0} +wave.waiting = Horda em {0} +wave.waveInProgress = [LIGHT_GRAY]Horda Em Progresso +waiting = Aguardando... +waiting.players = Esperando por jogadores... +wave.enemies = [LIGHT_GRAY]{0} Inimigos Restantes +wave.enemy = [LIGHT_GRAY]{0} Inimigo Restante +loadimage = Carregar\nImagem +saveimage = Salvar\nImagem +unknown = Desconhecido +custom = Customizado +builtin = Built-In +map.delete.confirm = Certeza que quer deletar este mapa? Isto não pode ser desfeito! +map.random = [accent]Mapa aleatório +map.nospawn = Esse mapa não contém um [yellow]núcleo[] para o jogador Nascer! [ROYAL]blue[] Coloque um [yellow]núcleo[] no editor de mapa. +map.nospawn.pvp = Esse mapa não tem núcleos inimigos para os jogadores nascerem! Adicione[SCARLET] Núcleos vermelhos[] no mapa no editor. +map.nospawn.attack = This map does not have any enemy cores for player to attack! Add[SCARLET] red[] cores to this map in the editor. +map.invalid = Erro ao carregar o mapa: Arquivo de mapa invalido ou corrupto. +editor.brush = Pincel +editor.openin = Abrir no Editor +editor.oregen = Geração de minério +editor.oregen.info = Geração de minério: +editor.mapinfo = Informação do mapa +editor.author = Autor: +editor.description = Descrição: +editor.waves = Ondas: +editor.rules = Rules: +editor.ingame = Edit In-Game +waves.title = Ondas +waves.remove = Remover +waves.never = +waves.every = a casa +waves.waves = ondas(s) +waves.perspawn = por spawn +waves.to = para +waves.boss = Chefe +waves.preview = Prever +waves.edit = Editar... +waves.copy = Copiar para área de transferência +waves.load = carregar da área de transferência +waves.invalid = Ondas inválidas na área de transferência. +waves.copied = Ondas copiadas. +editor.default = [LIGHT_GRAY] +edit = Editar... +editor.name = Nome: +editor.spawn = Spawn Unit +editor.removeunit = Remove Unit +editor.teams = Time +editor.elevation = Elevação +editor.errorload = Erro carregando arquivo:\n[accent]{0} +editor.errorsave = Erro salvando arquivo:\n[accent]{0} +editor.errorimage = That's an image, not a map. Don't go around changing extensions expecting it to work.\n\nIf you want to import a legacy map, use the 'import legacy map' button in the editor. +editor.errorlegacy = This map is too old, and uses a legacy map format that is no longer supported. +editor.errorheader = This map file is either not valid or corrupt. +editor.errorname = Mapa não tem nome definido. +editor.update = atualizar +editor.randomize = Randomizar +editor.apply = Aplicar +editor.generate = Gerar +editor.resize = Redimen\n sionar +editor.loadmap = Carregar\n Mapa +editor.savemap = Salvar\n Mapa +editor.saved = Salvo! +editor.save.noname = Seu mapa não tem um nome! Coloque um no menu de "Informação do mapa" +editor.save.overwrite = O seu mapa Substitui um mapa já construído! Coloque um nome diferente no menu "Informação do mapa" +editor.import.exists = [scarlet]Não foi possivel importar:[] Um mapa Construído chamado '{0}' Já existe! +editor.import = Importando... +editor.importmap = Importar Mapa +editor.importmap.description = Importar um mapa existente +editor.importfile = Importar arquivo +editor.importfile.description = Importar um arquivo externo +editor.importimage = Importar imagem do terreno +editor.importimage.description = Importar uma imagem de terreno externa +editor.export = Exportando... +editor.exportfile = Exportar arquivo +editor.exportfile.description = Exportar um arquivo de mapa +editor.exportimage = Exportar imagem de terreno +editor.exportimage.description = Exportar um arquivo de imagem de mapa +editor.loadimage = Carregar\n Imagem +editor.saveimage = Salvar\nImagem +editor.unsaved = [scarlet]Você tem alterações não salvas![]\nTem certeza que quer sair? +editor.resizemap = Redimensionar Mapa +editor.mapname = Nome do Mapa: +editor.overwrite = [accent]Aviso!\nIsso Subistitui um mapa existente. +editor.overwrite.confirm = [scarlet]Aviso![] Um mapa com esse nome já existe. Tem certeza que deseja substituir? +editor.selectmap = Selecione uma mapa para carregar: +filters.empty = [LIGHT_GRAY]Sem filtro! Adicione um usando o botão abaixo. +filter.distort = Distorcedor +filter.noise = Ruído +filter.ore = Minério +filter.rivernoise = Ruído para rios +filter.scatter = Dispersão +filter.terrain = Terreno +filter.option.scale = Escala +filter.option.chance = Chance +filter.option.mag = Magnitude +filter.option.threshold = Margem +filter.option.circle-scale = Escala de círculo +filter.option.octaves = Oitavas +filter.option.falloff = Falloff +filter.option.block = Bloco +filter.option.floor = Chão +filter.option.wall = Parede +filter.option.ore = Minério +filter.option.floor2 = Chão decundário +filter.option.threshold2 = Margem secundária +filter.option.radius = Radius +filter.option.percentile = Percentile +width = Largura: +height = Altura: +menu = Menu +play = Jogar +load = Carregar +save = Salvar +fps = FPS: {0} +tps = TPS: {0} +ping = Ping: {0}ms +language.restart = Por favor Reinicie seu jogo para a tradução tomar efeito. +settings = Configurações +tutorial = Tutorial +editor = Editor +mapeditor = Editor de mapa +donate = Doar +abandon = Abandonar +abandon.text = Esta zona e todos os seus recursos serão perdidos para o enimigo. +locked = Trancado +complete = [LIGHT_GRAY]Complete: +zone.requirement = Onda {0} Na zona {1} +resume = Resumir Zona:\n[LIGHT_GRAY]{0} +bestwave = [LIGHT_GRAY]Melhor: {0} +launch = Lançar +launch.title = Lançamento feito com sucesso +launch.next = [LIGHT_GRAY]próxima oportunidade na onda {0} +launch.unable = [scarlet]Incapaz de LANÇAR.[] Enimigos. +launch.confirm = Isto vai lançar todos os seus recursos no seu núcleo.\nVoce não será capaz de retornar para esta base. +uncover = Descobrir +configure = Configurar loadout +configure.locked = [LIGHT_GRAY]Alcançe a onda {0}\npara Configurar o Loadout. +zone.unlocked = [LIGHT_GRAY]{0} Desbloqueado. +zone.requirement.complete = Onda {0} alcançada:\n{1} Requerimentos da zona alcançada. +zone.config.complete = Onda {0} Alcançada:\nLoadout config desbloqueado. +zone.resources = Recursos detectados: +add = Adicionar... +boss.health = Saúde do chefe +connectfail = [crimson]Falha ao entrar no servidor: [accent]{0} +error.unreachable = Servidor inalcançavel. +error.invalidaddress = Endereço invalido. +error.timedout = Desconectado!\nTenha certeza que o Rosteador tenha feito Port forwading, E que o indereço esteja correto! +error.mismatch = Erro de pacote:\nPossivel versão do cliente/Servidor incompatibilidade.\nTenha certeza que você e o host tenham a ultima versão! +error.alreadyconnected = Já conectado. +error.mapnotfound = Arquivo de mapa não encontrado! +error.io = Erro I/O de internet. +error.any = Erro de rede desconhecido. +zone.groundZero.name = Marco zero +zone.desertWastes.name = Desert Wastes +zone.craters.name = As crateras +zone.frozenForest.name = Floresta congelada +zone.ruinousShores.name = Costas Ruinosas +zone.stainedMountains.name = Montanhas manchadas +zone.desolateRift.name = Fenda desolada +zone.nuclearComplex.name = Complexo de construção nuclear +zone.overgrowth.name = Overgrowth +zone.tarFields.name = Tar Fields +settings.language = Linguagem +settings.reset = Restaurar Padrões +settings.rebind = Religar +settings.controls = Controles +settings.game = Jogo +settings.sound = Som +settings.graphics = Gráficos +settings.cleardata = Data do jogo limpa... +settings.clear.confirm = Certeza que quer limpar a data?\nOque é feito não pode ser desfeito! +settings.clearall.confirm = [scarlet]Aviso![]\nIsso vai limpar toda a data, Incluindo saves, mapas, Keybinds e desbloqueados.\nQuando apertar 'ok' Vai apagar toda a data e sair automaticamente. +settings.clearunlocks = Limpar liberados +settings.clearall = Limpar tudo +paused = Pausado +yes = Sim +no = Não +info.title = [accent]Informação +error.title = [crimson]Ocorreu um Erro. +error.crashtitle = Ocorreu um Erro +blocks.input = Entrada +blocks.output = Saida +blocks.booster = Booster +block.unknown = [LIGHT_GRAY]??? +blocks.powercapacity = Capacidade de Energia +blocks.powershot = Energia/tiro +blocks.targetsair = Mirar no ar +blocks.targetsground = Mirar no chão +blocks.itemsmoved = Velocidade de movimento +blocks.launchtime = Tempo entre tiros +blocks.shootrange = Alcance +blocks.size = Tamanho +blocks.liquidcapacity = Capacidade de Líquido +blocks.powerrange = Alcance da Energia +blocks.poweruse = Uso de energia +blocks.powerdamage = Dano/Poder +blocks.itemcapacity = Capacidade de Itens +blocks.basepowergeneration = Geração de poder base +blocks.productiontime = Tempo de produção +blocks.repairtime = Tempo de reparo total do bloco +blocks.speedincrease = Aumento de velocidade +blocks.range = Distancia +blocks.drilltier = Furaveis +blocks.drillspeed = Velocidade da furadeira base +blocks.boosteffect = Efeito do Boost +blocks.maxunits = Maximo de unidades ativas +blocks.health = Saúde +blocks.buildtime = Build Time +blocks.inaccuracy = Imprecisão +blocks.shots = Tiros +blocks.reload = Recarregar +blocks.ammo = Munição +bar.drillspeed = Velocidade da furadeira: {0}/s +bar.efficiency = Eficiencia: {0}% +bar.powerbalance = Energia: {0} +bar.poweramount = Energia: {0} +bar.poweroutput = Saida de energia: {0} +bar.items = Itens: {0} +bar.liquid = Liquido +bar.heat = Aquecimento +bar.power = Poder +bar.progress = Progresso da construção +bar.spawned = Unidades: {0}/{1} +bullet.damage = [stat]{0}[lightgray] dano +bullet.splashdamage = [stat]{0}[lightgray] Dano em area ~[stat] {1}[lightgray] Blocos +bullet.incendiary = [stat]incendiario +bullet.homing = [stat]Guiado +bullet.shock = [stat]Choque +bullet.frag = [stat]fraguimento +bullet.knockback = [stat]{0}[lightgray] Impulso +bullet.freezing = [stat]Congelamento +bullet.tarred = [stat]tarred +bullet.multiplier = [stat]{0}[lightgray]x Multiplicador de munição +bullet.reload = [stat]{0}[lightgray]x recarregar +unit.blocks = blocos +unit.powersecond = Unidades de energia/segundo +unit.liquidsecond = Unidades de líquido/segundo +unit.itemssecond = itens/segundo +unit.liquidunits = Unidades de liquido +unit.powerunits = Unidades de energia +unit.degrees = Graus +unit.seconds = segundos +unit.persecond = /sec +unit.timesspeed = x Velocidade +unit.percent = % +unit.items = itens +category.general = Geral +category.power = Poder +category.liquids = Liquidos +category.items = Itens +category.crafting = Construindo +category.shooting = Atirando +category.optional = Melhoras opcionais +setting.landscape.name = Travar panorama +setting.shadows.name = Sombras +setting.linear.name = Linear Filtering +setting.animatedwater.name = Água animada +setting.animatedshields.name = Escudos animados +setting.antialias.name = Antialias[LIGHT_GRAY] (Requer recomeço)[] +setting.indicators.name = Indicador de aliados +setting.autotarget.name = Alvo automatico +setting.fpscap.name = FPS Maximo +setting.fpscap.none = Nenhum +setting.fpscap.text = {0} FPS +setting.swapdiagonal.name = Sempre colocação diagnoal +setting.difficulty.training = treinamento +setting.difficulty.easy = Fácil +setting.difficulty.normal = Normal +setting.difficulty.hard = Difícil +setting.difficulty.insane = insano +setting.difficulty.name = Dificuldade +setting.screenshake.name = Balanço da Tela +setting.effects.name = Efeitos +setting.sensitivity.name = Sensibilidade do Controle +setting.saveinterval.name = Intervalo de autosalvamento +setting.seconds = {0} Segundos +setting.fullscreen.name = Tela Cheia +setting.borderlesswindow.name = Borderless Window[LIGHT_GRAY] (may require restart) +setting.fps.name = Mostrar FPS +setting.vsync.name = VSync +setting.lasers.name = Mostrar lasers +setting.pixelate.name = Pixelizado [LIGHT_GRAY](Pode diminuir a performace) +setting.minimap.name = Mostrar minimapa +setting.musicvol.name = Volume da Música +setting.mutemusic.name = Desligar Música +setting.sfxvol.name = Volume de Efeitos +setting.mutesound.name = Desligar Som +setting.crashreport.name = Enviar denuncias de crash anonimas +setting.chatopacity.name = Chat Opacity +setting.playerchat.name = Display In-Game Chat +keybind.title = Refazer teclas +category.general.name = Geral +category.view.name = Ver +category.multiplayer.name = Multijogador +command.attack = Atacar +command.retreat = Recuar +command.patrol = Patrulha +keybind.gridMode.name = Seleção de blocos +keybind.gridModeShift.name = Seleção de categoria +keybind.press = Pressione uma tecla... +keybind.press.axis = Pressione uma Axis ou tecla... +keybind.screenshot.name = Map Screenshot +keybind.move_x.name = mover_x +keybind.move_y.name = mover_y +keybind.select.name = selecionar +keybind.diagonal_placement.name = Colocação diagonal +keybind.pick.name = Pegar bloco +keybind.break_block.name = Quebrar bloco +keybind.deselect.name = Deselecionar +keybind.shoot.name = Atirar +keybind.zoom_hold.name = segurar_zoom +keybind.zoom.name = Zoom +keybind.menu.name = Menu +keybind.pause.name = Pausar +keybind.minimap.name = Minimap +keybind.dash.name = Correr +keybind.chat.name = Conversa +keybind.player_list.name = Lista_de_jogadores +keybind.console.name = console +keybind.rotate.name = Girar +keybind.toggle_menus.name = Ativar menus +keybind.chat_history_prev.name = Historico do chat anterior +keybind.chat_history_next.name = Historico do proximo chat +keybind.chat_scroll.name = Rolar chat +keybind.drop_unit.name = Soltar unidade +keybind.zoom_minimap.name = Zoom minimap +mode.help.title = Descrição dos modos +mode.survival.name = Sobrevivencia +mode.survival.description = O modo normal. Recursos limitados E Ondas automaticass. +mode.sandbox.name = Caixa de areia +mode.sandbox.description = Recursos infinitos E sem tempo para Ataques. +mode.pvp.name = PvP +mode.pvp.description = Lutar contra outros jogadores locais. +mode.attack.name = Ataque +mode.attack.description = Sem ondas, Com o objetivo de destruir a base inimiga. +mode.custom = Regras personalizadas +rules.infiniteresources = Recursos infinitos +rules.wavetimer = Tempo de onda +rules.waves = Ondas +rules.enemyCheat = Recursos de IA Infinitos +rules.unitdrops = Unidade droppa +rules.unitbuildspeedmultiplier = Multiplicador de velocidade de criação de unidade +rules.unithealthmultiplier = Multiplicador de vida de unidade +rules.playerhealthmultiplier = Player Health Multiplier +rules.playerdamagemultiplier = Player Damage Multiplier +rules.unitdamagemultiplier = Unit Damage Multiplier +rules.enemycorebuildradius = Raio de "Não-criação" de core inimigo:[LIGHT_GRAY] (tiles) +rules.respawntime = Tempo de renascimento:[LIGHT_GRAY] (sec) +rules.wavespacing = Espaço entre waves:[LIGHT_GRAY] (sec) +rules.buildcostmultiplier = Multiplicador de custo de construção +rules.buildspeedmultiplier = Multiplicador de velocidade de construção +rules.waitForWaveToEnd = Waves wait for enemies +rules.dropzoneradius = Drop Zone Radius:[LIGHT_GRAY] (tiles) +rules.respawns = Max respawns per wave +rules.limitedRespawns = Limit Respawns +rules.title.waves = Waves +rules.title.respawns = Respawns +rules.title.resourcesbuilding = Recursos e Construções +rules.title.player = Players +rules.title.enemy = Inimigos +rules.title.unit = Unidades +content.item.name = Itens +content.liquid.name = Liquidos +content.unit.name = Unidades +content.block.name = Blocos +content.mech.name = Mecas +item.copper.name = Cobre +item.copper.description = Um material de estrutura util. Usado extensivamente em Maioria dos blocos. +item.lead.name = Chumbo +item.lead.description = Material de começo basico. usado intensivamente em Blocos de transporte de liquidos e eletronicos. +item.coal.name = Carvão +item.coal.description = Combustivel pronto. +item.graphite.name = Graphite +item.titanium.name = Titânio +item.titanium.description = Um Material raro super leve, metal usado intensivamente na transportação de líquidos, Brocas e Aeronaves. +item.thorium.name = Urânio +item.thorium.description = Um metal denso e radioativo, Usado como suporte material e combustivel nuclear. +item.silicon.name = Sílicio +item.silicon.description = Condutor extremamente importante,Com aplicação em paneis solares e dispositivos complexos. +item.plastanium.name = Plastanio +item.plastanium.description = Leve, Material dutil Usado em aeronaves Avançadas E munição de fragmentação. +item.phase-fabric.name = Fabrica fase +item.phase-fabric.description = Uma substancia quase sem peso Usado em eletronica avançada E tecnologia de auto-reparo. +item.surge-alloy.name = Liga de surto +item.surge-alloy.description = Uma liga com propriedades unicas eletricas. +item.spore-pod.name = Pod de esporos +item.spore-pod.description = Usado em conversão para oleo, Combustivel e explosivos. +item.sand.name = Areia +item.sand.description = Um material comum Que é usado intensivamente em derretimento, Tanto em ligas como fluxo. +item.blast-compound.name = Composto de explosão +item.blast-compound.description = Um composto volatil usado em bombas em bombas em explosivos. Enquanto pode ser queimado como combustivel, Isso não é recomendado. +item.pyratite.name = piratita +item.pyratite.description = Substancia extremamente inflamavel usado em armas incendiarias. +item.metaglass.name = Metavidro +item.metaglass.description = Composto de vidro super-Resistente. Extensivamente usado Para distribuição de líquido e armazem. +item.scrap.name = Sucata +item.scrap.description = Pedaços remanescentes de estruturas e unidades destruidas.. Contem traços de diferentes metais. +liquid.water.name = Água +liquid.slag.name = Slag +liquid.oil.name = Petróleo +liquid.cryofluid.name = Crio Fluido +mech.alpha-mech.name = Alfa +mech.alpha-mech.weapon = Repetidor pesado +mech.alpha-mech.ability = Onda de drones +mech.alpha-mech.description = O meca padrão. Tem uma saida de dano e velocidade decente; Pode criar até 3 drones Para capacidades ofensivas aumentadas. +mech.delta-mech.name = Delta +mech.delta-mech.weapon = Gerador Arc +mech.delta-mech.ability = Descarga +mech.delta-mech.description = Um meca rapido, De baixa armadura Feito for para ataques rapidos. Da pouco dano as estruturas, Mas pode matar grandes grupos de unidades inimigas muito rapidamente Com sua arma ARC. +mech.tau-mech.name = Tau +mech.tau-mech.weapon = Laser restruturador +mech.tau-mech.ability = Tiro reparador +mech.tau-mech.description = O meca de suporte. Conserta blocos aliados Atirando neles. Pode extinguir o fogo e consertar aliados em uma distancia Com sua habilidade de consertar. +mech.omega-mech.name = Omega +mech.omega-mech.weapon = Onda de missies +mech.omega-mech.ability = Configuração Armadurada +mech.omega-mech.description = Um meca volumoso e bem armadurado, Feito para assaltos da primeira linha. Sua habilidade de armadura Pode bloquear 90% de dano. +mech.dart-ship.name = Dardo +mech.dart-ship.weapon = Repetidor +mech.dart-ship.description = Nave padrão. Consideravelmente leve e rapido, Tem pouca capacidade ofensiva E baixa velocidade de mineração. +mech.javelin-ship.name = Javelin +mech.javelin-ship.description = Uma nave de espinhos de atacar e correr. Quando inicialmente lento, pode acelerar a altas velocidades e voar até bases inimigas, Dando altas quantidades de dano Com seus raios e habilidades. +mech.javelin-ship.weapon = Ondas de misseis +mech.javelin-ship.ability = Acelerador de explosão +mech.trident-ship.name = Tridente +mech.trident-ship.description = Um bombardeiro pesado. Consideravelmente bem armadurado. +mech.trident-ship.weapon = Carga de bombas +mech.glaive-ship.name = Glaive +mech.glaive-ship.description = Uma nave armada, bem armadurada. Com um repetidor incendario equipado. Boa aceleração e maxima velocidade. +mech.glaive-ship.weapon = Repetidor de fogo +item.explosiveness = [LIGHT_GRAY]Explosividade: {0} +item.flammability = [LIGHT_GRAY]Inflamabilidade: {0} +item.radioactivity = [LIGHT_GRAY]RadioAtividade: {0} +unit.health = [LIGHT_GRAY]Vida: {0} +unit.speed = [LIGHT_GRAY]Velocidade: {0} +mech.weapon = [LIGHT_GRAY]Arma: {0} +mech.health = [LIGHT_GRAY]Saude: {0} +mech.itemcapacity = [LIGHT_GRAY]Capacidade de itens: {0} +mech.minespeed = [LIGHT_GRAY]Velocidade de mineração: {0} +mech.minepower = [LIGHT_GRAY]Poder de mineração: {0} +mech.ability = [LIGHT_GRAY]Habilidade: {0} +mech.buildspeed = [LIGHT_GRAY]Velocidade de construção: {0}% +liquid.heatcapacity = [LIGHT_GRAY]Capacidade de aquecimento: {0} +liquid.viscosity = [LIGHT_GRAY]Viscosidade: {0} +liquid.temperature = [LIGHT_GRAY]Temperatura: {0} +block.grass.name = Grama +block.salt.name = Sal +block.saltrocks.name = Pedras De Sal +block.pebbles.name = Pebbles +block.tendrils.name = Tendrils +block.sandrocks.name = Pedras de areia +block.spore-pine.name = Pinheiro de esporo +block.sporerocks.name = Pedras de esporo +block.rock.name = Pedra +block.snowrock.name = Pedra de gelo +block.shale.name = Xisto +block.shale-boulder.name = Pedra de xisto +block.moss.name = Musgo +block.shrubs.name = Shrubs +block.spore-moss.name = Musgo de esporos +block.shalerocks.name = Pedas de xisto +block.scrap-wall.name = Parede de sucata +block.scrap-wall-large.name = Parede de sucata grande +block.scrap-wall-huge.name = Parede de sucata Maior +block.scrap-wall-gigantic.name = Muro de sucata gigante +block.thruster.name = Propulsor +block.kiln.name = Kiln +block.kiln.description = Derrete chumbo e areia em Metavidro. Requer pequenas quantidades de energia. +block.graphite-press.name = Prensa de grafite +block.multi-press.name = Multi-Prensa +block.constructing = {0}\n[LIGHT_GRAY](Construindo) +block.spawn.name = Spawn dos inimigos +block.core-shard.name = Core: Fragmento +block.core-foundation.name = Core: Fundação +block.core-nucleus.name = Core: Nucleus +block.deepwater.name = água funda +block.water.name = Água +block.tainted-water.name = Água contaminada +block.darksand-tainted-water.name = Água contaminada de areia escura +block.tar.name = Tar +block.stone.name = Pedra +block.sand.name = Areia +block.darksand.name = Areia escura +block.ice.name = Gelo +block.snow.name = Neve +block.craters.name = Crateras +block.sand-water.name = Água de areia +block.darksand-water.name = Água de areia escura +block.char.name = Char +block.holostone.name = Pedra holo +block.ice-snow.name = Gelo de neve +block.rocks.name = Rochas +block.icerocks.name = Rochas de gelo +block.snowrocks.name = Rochas de neve +block.dunerocks.name = Rochas da duna +block.pine.name = Pinheiro +block.white-tree-dead.name = Arvore branca morta +block.white-tree.name = Arvore branca +block.spore-cluster.name = Grupo de esporos +block.metal-floor.name = Chão de metal +block.metal-floor-2.name = Chão de metal 2 +block.metal-floor-3.name = Chão de metal 3 +block.metal-floor-5.name = Chão de metal 5 +block.metal-floor-damaged.name = Chão de metal danificado +block.dark-panel-1.name = Dark Panel 1 +block.dark-panel-2.name = Dark Panel 2 +block.dark-panel-3.name = Dark Panel 3 +block.dark-panel-4.name = Dark Panel 4 +block.dark-panel-5.name = Dark Panel 5 +block.dark-panel-6.name = Dark Panel 6 +block.dark-metal.name = Dark Metal +block.ignarock.name = Rocha igna +block.hotrock.name = Rocha quente +block.magmarock.name = Rocha de magma +block.cliffs.name = Colinas +block.copper-wall.name = Parede de Cobre +block.copper-wall-large.name = Parede de Cobre Grande +block.titanium-wall.name = Parede de titanio +block.titanium-wall-large.name = Parede de titanio grande +block.phase-wall.name = Parede de fase +block.phase-wall-large.name = Parde de fase grande +block.thorium-wall.name = Parede de tório +block.thorium-wall-large.name = Parede larga de tório +block.door.name = Porta +block.door-large.name = Porta Grande +block.duo.name = Dupla +block.scorch.name = Queimada +block.scatter.name = Dispersão +block.hail.name = Granizo +block.lancer.name = Lancador +block.conveyor.name = Esteira +block.titanium-conveyor.name = Esteira de Titanio +block.junction.name = Junção +block.router.name = Roteador +block.distributor.name = Distribuidor +block.sorter.name = Ordenador +block.sorter.description = [interact]Aperte no bloco para configurar[] +block.overflow-gate.name = Portão Sobrecarregado +block.overflow-gate.description = Uma combinação de roteador e divisor Que apenas manda para a esquerda e Direita se a frente estiver bloqueada. +block.silicon-smelter.name = Fundidora de silicio +block.phase-weaver.name = Palheta de fase +block.pulverizer.name = Pulverizador +block.cryofluidmixer.name = Misturador de Crio Fluido +block.melter.name = Aparelho de fusão +block.incinerator.name = Incinerador +block.spore-press.name = Prensa de Esporo +block.separator.name = Separador +block.coal-centrifuge.name = Coal Centrifuge +block.power-node.name = Célula de energia +block.power-node-large.name = Célula de energia Grande +block.surge-tower.name = Torre de surto +block.battery.name = Bateria +block.battery-large.name = Bateria Grande +block.combustion-generator.name = Gerador de combustão +block.turbine-generator.name = Gerador de Turbina +block.differential-generator.name = Gerador diferencial +block.impact-reactor.name = Reator De Impacto +block.mechanical-drill.name = Furadera Mecânica +block.pneumatic-drill.name = Mineradora Pneumatica +block.laser-drill.name = Broca a Laser +block.water-extractor.name = Extrator de Agua +block.cultivator.name = Cultivador +block.dart-mech-pad.name = Dart Mech Pad +block.delta-mech-pad.name = Controle do mech Delta +block.javelin-ship-pad.name = Controle do mech Javelin +block.trident-ship-pad.name = Controle do mech Tridente +block.glaive-ship-pad.name = Controle do mech Glaive +block.omega-mech-pad.name = Controle do mech Omega +block.tau-mech-pad.name = Controle do mech Tau +block.conduit.name = Cano +block.mechanical-pump.name = Bomba Mecanica +block.item-source.name = Fonte do item +block.item-void.name = Item Vazio +block.liquid-source.name = Liquid Source +block.power-void.name = Poder Vazio +block.power-source.name = Poder Infinito +block.unloader.name = Descarregador +block.vault.name = Cofre +block.wave.name = Onda +block.swarmer.name = Enxame +block.salvo.name = Salvo +block.ripple.name = Ondulação +block.phase-conveyor.name = Esteira de Fases +block.bridge-conveyor.name = Esteira-Ponte +block.plastanium-compressor.name = Compressor de Plastanio +block.pyratite-mixer.name = Misturador de Piratita +block.blast-mixer.name = Misturador de Explosão +block.solar-panel.name = Painel Solar +block.solar-panel-large.name = Painel Solar Grande +block.oil-extractor.name = Extrator de Óleo +block.spirit-factory.name = Fabrica de Drone Spirit +block.phantom-factory.name = Fabrica de Drone Phantom +block.wraith-factory.name = Fabrica de Drone Wraith +block.ghoul-factory.name = Fabrica de Bombardeiro Ghoul +block.dagger-factory.name = Fabrica de mech Dagger +block.crawler-factory.name = Crawler Mech Factory +block.titan-factory.name = Fabrica de mech titan +block.fortress-factory.name = Fabrica de mech Fortress +block.revenant-factory.name = Fabrica de lutadores Revenant +block.repair-point.name = Ponto de Reparo +block.pulse-conduit.name = Conduto de Pulso +block.phase-conduit.name = Conduto de Fase +block.liquid-router.name = Roteador de Líquido +block.liquid-tank.name = Tanque de Líquido +block.liquid-junction.name = Junção de Líquido +block.bridge-conduit.name = Conduto-Ponte +block.rotary-pump.name = Bomba Rotatoria +block.thorium-reactor.name = Reator Torio +block.mass-driver.name = Drive de Massa +block.blast-drill.name = Mineradora de Explosão +block.thermal-pump.name = Cano termico +block.thermal-generator.name = Gerador Térmico +block.alloy-smelter.name = Fundidora de Liga +block.mender.name = Mender +block.mend-projector.name = Projetor Mend +block.surge-wall.name = Parede de Surge +block.surge-wall-large.name = Parede de Surge grande +block.cyclone.name = Ciclone +block.fuse.name = Fundir +block.shock-mine.name = Mina de Choque +block.overdrive-projector.name = Projetor Overdrive +block.force-projector.name = Projetor Force +block.arc.name = Arc +block.rtg-generator.name = Gerador RTG +block.spectre.name = Espectra +block.meltdown.name = Derreter +block.container.name = Container +block.launch-pad.name = Launch Pad +block.launch-pad.description = Launches batches of items without any need for a core launch. Unfinished. +block.launch-pad-large.name = Large Launch Pad +team.blue.name = Azul +team.red.name = Vermelho +team.orange.name = Laranja +team.none.name = Cinza +team.green.name = Verde +team.purple.name = Roxo +unit.spirit.name = Drone Spirit +unit.spirit.description = A unidade de drone inicial. Ele nasce no core por padrão. Minera minérios automaticamente, Coleta itens e repara blocos. +unit.phantom.name = Drone Phantom +unit.phantom.description = Uma unidade de drone avançada. Minera minérios automaticamente, Coleta itens e repara blocos automaticamente. Significantemente mais efetiva. +unit.dagger.name = Dagger +unit.dagger.description = Unidade terrestre basica, Forte em grupos. +unit.crawler.name = Crawler +unit.titan.name = Titan +unit.titan.description = Uma unidade armadurada terreste avancada. Usa carbide como munição. Ataca ambas as unidades de Aereas e terrestres. +unit.ghoul.name = Bombardeiro Ghoul +unit.ghoul.description = Um bombardeiro pesado. Usa composto de explosão Ou piratite como munição. +unit.wraith.name = Lutador Wraith +unit.wraith.description = Uma unidade rapida, Interceptadora de bater e correr. +unit.fortress.name = Fortaleza +unit.fortress.description = Uma unidade pesada de artilharia terrestre. +unit.revenant.name = Revenant +unit.eruptor.name = Eruptor +unit.chaos-array.name = Arraia do caos +unit.eradicator.name = Erradicador +unit.lich.name = Lich +unit.reaper.name = Ceifador +tutorial.begin = Sua missão aqui é de erradicar[LIGHT_GRAY] Inimigo[].\n\nComeçe por[accent] Minerar cobre[]. Clique numa veia de cobre perto de seu core para fazer isso. +tutorial.drill = Minerar manualmente é ineficiente.\n[accent]Mineradoras []podem minerar automaticamente.\nColoque uma numa veia de cobre. +tutorial.conveyor = [accent]Esteiras[] São usadas para transportar itens até o core.\nFaça uma linha de Esteiras da mineradora até o core. +tutorial.morecopper = Mais cobre é preciso.\n\nTanto minere manualmente, Ou coloque mais mineradoras. +tutorial.turret = Estruturas defensivas devem ser construidas para repelir[LIGHT_GRAY] O inimigo[].\nConstrua uma torre dupla perto de sua base. +tutorial.drillturret = Torres duplas precisam de[accent] Cobre como munição []Para atirar.\nColoque uma mineradoura Proxima a torre Para carregar ela com cobre minerado. +tutorial.waves = O[LIGHT_GRAY] Inimigo[] se aproxima.\n\nDefenda seu core por 2 ondas. Construa mais torres. +tutorial.lead = Mais minerios estão disponiveis. Explore e minere[accent] Cobre[].\n\nArraste Da sua unidade até o core para transferir recursos. +tutorial.smelter = Cobre e chumbo são materiais fracos.\nLiga densa[accent] Superior[] Pode ser feito num fundidor.\n\nConstrua um. +tutorial.densealloy = O fundidor agora vai fazer a liga.\nPegue.\nMelhore a produção se necessario. +tutorial.siliconsmelter = O core agora vai criar[accent] O drone Spirit[] Para minerar e consertar blocos.\n\nFabricas para outras unidades podem ser criadas usando [accent] silicio.\nFaça um fundidor de Silicio. +tutorial.silicondrill = Silicio requer[accent] carvão[] e[accent] areia[].\nComeçe a fazer mineradouras. +tutorial.generator = Essa tecnologia requer energia.\nCrie[accent] um gerador a combustão[] para isso. +tutorial.generatordrill = Gerador de combustão requer combustivel.\nCarregue com carvão minerado. +tutorial.node = Energia requer transporte.\nCrie um[accent] Nodo de energia[] Proximo do gerador de combustão. +tutorial.nodelink = Poder pode ser transportado construindo blocos de energia e geradores, Apenas construindo nodos de Energia.\n\nLigue a energia clicando no nodo e selecionando o gerador e o fundidor de silicio. +tutorial.silicon = Silicio esta sendo feito. Pegue.\n\nMelhorar a produção é recomendado. +tutorial.daggerfactory = Construa uma[accent] Fabrica do meca Dagger.[]\n\nIsso vai ser feito para construi mecas de ataque. +tutorial.router = Fabricas precisam de recursos pra construir\nCrie um roteador para espalhadar recursos da esteira. +tutorial.dagger = Ligue os nodos de energia a fabrica.\nQuando os requerimentos forem alcançados, Um meca vai ser criado.\n\nCrie mais mineradoras, geradoras e esteiras se necessario. +tutorial.battle = O[LIGHT_GRAY] Inimigo[] revelou seu core.\nDestrua com sua unidade e Dagger's. +block.copper-wall.description = Um bloco defensivo e barato.\nUtil para proteger o core e torres no começo. +block.copper-wall-large.description = Um bloco defensivo e barato.\nUtil para proteger o core e torres no começo.\nOcupa multiplos espaços. +block.thorium-wall.description = A strong defensive block.\nBoa proteção contra inimigos. +block.thorium-wall-large.description = Um bloco grande e defensivo.\nBoa proteção contra inimigos.\nOcupa multiplos espaços. +block.phase-wall.description = Não tão forte quanto a parede de torio Mas vai defletir balas a menos que seja muito forte. +block.phase-wall-large.description = Não tão forte quanto a parde de torio mas vai defletir balas a menos que seja muito forte.\nOcupa multiplos espaços. +block.surge-wall.description = O bloco defensivo mais forte.\nQue tem uma pequena chance de lancar um raio Contra o atacante. +block.surge-wall-large.description = O bloco defensivo mais forte.\nQue tem uma pequena chance de lancar um raio Contra o atacante.\nOcupa multiplos espaços +block.door.description = Uma pequena porta que pode ser aberta o fechada quando voce clica.\nSe aberta, Os inimigos podem atirar e passar. +block.door-large.description = Uma grande porta que pode ser aberta o fechada quando voce clica.\nSe aberta, Os inimigos podem atirar e passar..\nOcupa multiplos espaços. +block.mend-projector.description = Periodicamente conserta as construções. +block.overdrive-projector.description = Aumenta a velocidade de unidades proximas de geradores e esteiras. +block.force-projector.description = Cria um campo de forca hexagonal em volta de si mesmo, Protegendo construções e unidades dentro de dano por balas. +block.shock-mine.description = Danifica inimigos em cima da mina. Quase invisivel ao inimigo. +block.duo.description = Uma torre pequena e barata. +block.scatter.description = A medium-sized anti-air turret. Sprays clumps of lead or scrap flak at enemy units. +block.arc.description = Uma pequena torre que atira eletricidade em um pequeno arc aleatoriamente no inimigo. +block.hail.description = Uma pequena torre de artilharia. +block.lancer.description = Uma torre de Tamanho-Medio que atira raios de eletricidade. +block.wave.description = Uma torre que Tamanho medio que atira bolhas. +block.salvo.description = Uma torre media que da tiros em salvos. +block.swarmer.description = Uma torre media que atira ondas de misseis. +block.ripple.description = Uma grande torre que atira simultaneamente. +block.cyclone.description = Uma grande torre de tiro rapido. +block.fuse.description = Uma torre grande que atira raios de curta distancia poderosos. +block.spectre.description = Uma grande torre que da dois tiros poderosos ao mesmo tempo. +block.meltdown.description = Uma grande torre que atira dois raios poderosos ao mesmo tempo. +block.conveyor.description = Bloco de transporte de item basico. Move os itens a frente e os deposita automaticamente Em torres ou construtores. Rotacionavel. +block.titanium-conveyor.description = Bloco de transporte de item avancado. Move itens mais rapidos que esteiras padrões. +block.phase-conveyor.description = Bloco de transporte de item avançado. Usa energia para teleportar itens a uma esteira de fase sobre uma severa distancia. +block.junction.description = Funciona como uma ponte Para duas esteiras que estejam se cruzando. Util em situações que tenha duas esteiras diferentes carregando materiais diferentes para lugares diferentes. +block.mass-driver.description = Bloco de transporte de itens supremo. Coleta itens severos e atira eles em outro mass driver de uma longa distancia. +block.silicon-smelter.description = Reduz areia a coque altamente puro Para fazer silicio. +block.plastanium-compressor.description = Produz plastanio para usando oleo e titanio. +block.phase-weaver.description = Produz tecido de fase de torio radioativo e grandes quantidades de areia. +block.alloy-smelter.description = Produz liga de surge de titanio, chumbo, silicio e cobre. +block.pulverizer.description = Esmaga pedra em areia. Util quando esta em falta de areia natural. +block.pyratite-mixer.description = Mistura carvão, Cobre e areia em piratite altamente inflamavel +block.blast-mixer.description = Usa oleo em Transformar piratite em composto de explosão menos inflamavel mas mais explosivo +block.cryofluidmixer.description = Combina agua e titanio em cryo fluido que é mais eficiente em esfriar. +block.melter.description = Aquece pedra em altas temperaturas para fazer lava. +block.incinerator.description = Se livra de itens em excesso ou liquidos. +block.spore-press.description = Compresses spore pods into oil. +block.separator.description = Expos pedra em agua em pressão para ter varios mineiras contendo na pedra. +block.power-node.description = Transmite poder em nodos. Maximo de 4 fontes de energia, sinks ou nodos podem ser conectados. Os nodos vão receber energia de ou dar energia para qualquer bloco adjacente. +block.power-node-large.description = Tem um raio maior que o nodo de energia e pode conectar até 6 fontes de energia, sinks ou nodos. +block.battery.description = Guarda energia sempre que tiver em abundancia e da energia sempre que precisar enquanto tiver capacidade. +block.battery-large.description = Guarda muito mais energia que uma beteria comum +block.combustion-generator.description = Gera poder usando combustivel ou oleo. +block.turbine-generator.description = Mais eficiente que o gerador de Combustão, Mas requer agua adicional. +block.thermal-generator.description = Gera uma quantidade grande de energia usando lava. +block.solar-panel.description = Gera pequenas quantidades de energia do sol. +block.solar-panel-large.description = Da muito mais energia que o painel solar comum, Mas sua produção é mais cara. +block.thorium-reactor.description = Gera altas quantidades de energia do torio radioativo. Requer resfriamento constante. Vai explodir violentamente Se resfriamento insuficiente for fornecido. +block.rtg-generator.description = Um Gerador termoelétrico de radioisótopos Que não precisa de refriamento Mas da muito mais energia que o reator de torio. +block.unloader.description = Descarrega itens de um container, Descarrega em uma esteira ou diretamente em um bloco adjacente. O tipo de item que pode ser descarregado pode ser mudado clicando no descarregador. +block.container.description = Carrega uma baixa quantidade de itens. Usado para criar fontes Quando não tem uma necessidade constante de materiais. Um[LIGHT_GRAY] Descarregador[] pode ser usado para recuperar esses itens do container. +block.vault.description = Carrega uma alta quantidade de itens. Usado para criar fontes Quando não tem uma necessidade constante de materiais. Um[LIGHT_GRAY] Descarregador[] pode ser usado para recuperar esses itens do container. +block.mechanical-drill.description = Uma mineradoura barata. Quando colocado em blocos apropriados, retira itens em um ritmo lento e indefinitavamente. +block.pneumatic-drill.description = Uma mineradora improvisada que é mais rapida e capaz de processar mateirais mais duros usando a pressao do ar +block.laser-drill.description = Possibilita a mineração ainda mais rapida usando tecnologia a laser, Mas requer poder adcionalmente torio radioativo pode ser recuperado com essa mineradora +block.blast-drill.description = A melhor mineradora. Requer muita energia. +block.water-extractor.description = Extrai agua do chão. Use quando não tive nenhum lago proximo +block.cultivator.description = Cultiva o solo com agua para pegar bio materia. +block.oil-extractor.description = Usa altas quantidades de energia Para extrair oleo da areia. Use quando não tiver fontes de oleo por perto +block.trident-ship-pad.description = Deixe sua atual embarcação e mude para um bombardeiro resionavelmente bem armadurado.\nUse o pad clicando duas vezes em cima enquando fica em cima dele. +block.javelin-ship-pad.description = Deixe sua atual embarcação e mude para um interceptador forte e rapido com armas de raio.\nUse o pad clicando duas vezes em cima enquando fica em cima dele. +block.glaive-ship-pad.description = Deixe sua atual embarcação e mude para grande, bem armadurada nave de combate.\nUse o pad clicando duas vezes em cima enquando fica em cima dele. +block.tau-mech-pad.description = Deixe sua atual embarcação e mude para o meca de suporte que pode consertar construções aliadas e unidades.\nUse o pad clicando duas vezes em cima enquando fica em cima dele. +block.delta-mech-pad.description = Deixe sua atual embarcação e mude para o rapido, Levemente armadurado meca feito para ataques rapidos.\nUse o pad clicando duas vezes em cima enquando fica em cima dele. +block.omega-mech-pad.description = Deixe sua atual embarcação e mude para o volumoso e bem armadurado meca feito para ataques da primeira linha.\nUse o pad clicando duas vezes em cima enquando fica em cima dele. +block.spirit-factory.description = Produz drones leves que mineram e reparam blocos. +block.phantom-factory.description = Produz unidades de drone avancadas Que são significativamente mais efetivos que um drone spirit. +block.wraith-factory.description = produz unidades interceptor de ataque rapido. +block.ghoul-factory.description = Produz bombardeiros pesados. +block.dagger-factory.description = Produz unidades terrestres. +block.titan-factory.description = Produz unidades avancadas, armaduradas e terrestres. +block.fortress-factory.description = Produz unidades terrestres pesadas de artilharia. +block.revenant-factory.description = Produz unidades laser, pesadas e terrestres. +block.repair-point.description = Continuamente repara a unidade danificada mais proxima. +block.conduit.description = Bloco de transporte de liquido basico. Funciona como a esteira, Mas com liquidos. Melhor usado com extratores, Bombas ou condutos. +block.pulse-conduit.description = Bloco avancado de transporte de liquido. Transporta liquidos mais rapido E armazena mais que os condutos padrões. +block.phase-conduit.description = Bloco avancado de transporte de liquido. Usa energia para teleportar liquidos conduto de fase sobre uma distancia severa. +block.liquid-router.description = Aceita liquidos de uma direcão e os joga em 3 direções igualmente. Pode armazenar uma certa quantidade de liquido. Util para espalhar liquidosd a fonte para multiplos alvos. +block.liquid-tank.description = Armazena grandes quantidades de liquido. Use quando a demanda de materiais não for constante ou para guardar itens para resfriar blocos vitais. +block.liquid-junction.description = Age como uma ponte para dois canos que se cruzam. Util em situações que tem dois condutos carregando liquidos diferentes até localizações diferentes. +block.bridge-conduit.description = Bloco de transporte de liquidos avancados. Possibilita o transporte de liquido sobre 3 blocos acima de construções ou paredes +block.mechanical-pump.description = Uma bomba barata mais saida de liquidos lenta, Sem consumo de energia. +block.rotary-pump.description = Uma bomba avancada que duplica a velocidade da saida de liquida usando energia. +block.thermal-pump.description = A melhor bomba. Trez vezes mais rapida que a bomba mecanica e a unica bomba capaz de pegar lava. +block.router.description = Aceita itens de uma direção e os divide em 3 direções igualmente. Util para espalhar materiais da fonte para multiplos alvos. +block.distributor.description = Um roteador avancada que espalhas os itens em 7 outras direções igualmente. +block.bridge-conveyor.description = Bloco de transporte de itens avancado. Possibilita o transporte de itens acima de 3 blocos de construção ou paredes. +block.item-source.description = Infinivamente da itens. Apenas caixa de areia. +block.liquid-source.description = Infinitivamente da Liquidos. Apenas caixa de areia. +block.item-void.description = Destroi qualquer item que entre sem requerir energia. Apenas caixa de areia. +block.power-source.description = Infinitivamente da energia. Apenas caixa de areia. +block.power-void.description = Destroi qualquer energia que entre dentro. Apenas caixa de areia. +liquid.water.description = Comumente usado em resfriamento e no processo de perda. +liquid.oil.description = Pode ser queimado, explodido ou usado como resfriador. +liquid.cryofluid.description = A maneira mais eficiente de resfriar qualquer coisa. diff --git a/core/assets/bundles/bundle_ru.properties b/core/assets/bundles/bundle_ru.properties index dda5282ad3..7154d6096d 100644 --- a/core/assets/bundles/bundle_ru.properties +++ b/core/assets/bundles/bundle_ru.properties @@ -1,555 +1,947 @@ -text.about = Создатель [ROYAL] Anuken. [] \nИзначально игра была создана для участия в [orange] GDL [] MM Jam. \n\nАвторы: \n- Звуковые эффекты, сделаны с помощью [YELLOW] bfxr [] \n- Музыка, создана [GREEN] RoccoW [] / найденная на [lime] FreeMusicArchive.org [] \n\nОсобая благодарность: \n- [coral] MitchellFJN []: в тестировании и отзывах \n- [sky] Luxray5474 []: работа в вики, помощь в разработке \n- Все бета-тестеры на itch.io и Google Play\n\nИгра переведена полностью на русский язык [GREEN]krocotavus[] и [GREEN]lexa1549. Дополнил перевод [GREEN]Prosta4ok_ua[]\n -text.credits = Авторы -text.discord = Присоединяйтесь к нашему Discord чату! -text.changes=[SCARLET] Внимание!\n[]Изменена некоторая важная игровая механика.\n\n-[accent]Телепортеры[]теперь используют силу.\n-[accent]Печи[]и[accent]тигли[]теперь имеют максимальная емкость элемента.\n-[accent]Тигли[]теперь требует угля в качестве топлива. -text.link.discord.description = официальный discord-сервер Mindustry -text.link.github.description = Исходный код игры -text.link.dev-builds.description = Нестабильные разработки -text.link.trello.description = Официальная доска trello для запланированных функций -text.link.itch.io.description = itch.io страница с загрузкой ПК и веб-версией -text.link.google-play.description = Google Play список магазинов -text.link.wiki.description = официальная вики Mindustry -text.linkfail = Не удалось открыть ссылку!\nURL-адрес был скопирован в буфер обмена. -text.editor.web = Веб-версия не поддерживает редактор!\nЗагрузите игру, чтобы использовать ее. -text.multiplayer.web = Эта версия игры не поддерживает многопользовательскую игру! \n Чтобы играть в мультиплеер из своего браузера, используйте ссылку «Многопользовательская веб-версия» на странице itch.io. -text.gameover = Ядро было уничтожено. -text.highscore = [YELLOW]Новый рекорд! -text.lasted = Вы продержались до волны -text.level.highscore = Рекорд: [accent]{0} -text.level.delete.title = Подтвердите удаление -text.level.delete = Вы уверены,что хотите удалить эту карту \"[orange]\"{0}? -text.level.select = Выбор уровня -text.level.mode = Режим игры: -text.savegame = Сохранить игру -text.loadgame = Загрузить игру -text.joingame = Присоединиться -text.newgame= Новая игра -text.quit = Выход -text.about.button = Об игре -text.name = Название: -text.public = Общие -text.players = Игроков на сервере: {0} -text.server.player.host={0} (хост) -text.players.single = {0} игрок на сервере -text.server.mismatch = Ошибка пакета: возможное несоответствие версии клиента / сервера. Убедитесь, что у вас и у создателя сервера установлена ​​последняя версия Mindustry! -text.server.closing = [accent]Закрытие сервера... -text.server.kicked.kick = Вас выгнали с сервера! -text.server.kicked.fastShoot = Вы стреляете слишком быстро. -text.server.kicked.invalidPassword = Неверный пароль. -text.server.kicked.clientOutdated = Устаревший клиент! Обновите игру! -text.server.kicked.serverOutdated = Устаревший сервер! Попросите хост обновить! -text.server.kicked.banned = Вы заблокированы на этом сервере. -text.server.kicked.recentKick=Вы недавно были кикнуты.\n Подождите немного перед следующим подключением -text.server.connected = {0} присоединился -text.server.disconnected = {0} отключился. -text.nohost = Не удается запустить сервер на пользовательской карте! -text.host.info=The [accent]host[] button hosts a server on ports [scarlet]6567[] and [scarlet]6568.[]\nAnybody on the same [LIGHT_GRAY]wifi or local network[] should be able to see your server in their server list.\n\nIf you want people to be able to connect from anywhere by IP, [accent]port forwarding[] is required.\n\n[LIGHT_GRAY]Note: If someone is experiencing trouble connecting to your LAN game, make sure you have allowed Mindustry access to your local network in your firewall settings. -text.join.info=Here, you can enter a [accent]server IP[] to connect to, or discover [accent]local network[] servers to connect to.\nBoth LAN and WAN multiplayer is supported.\n\n[LIGHT_GRAY]Note: There is no automatic global server list; if you want to connect to someone by IP, you would need to ask the host for their IP. -text.hostserver = Запустить сервер -text.host = Сервер -text.hosting = [accent]Открытие сервера... -text.hosts.refresh = Обновить -text.hosts.discovering = Поиск локальных игр -text.server.refreshing = Обновление сервера -text.hosts.none = [lightgray]Локальных игр не обнаружено! -text.host.invalid = [scarlet] Не удается подключиться к хосту. -text.server.friendlyfire = Дружественный огонь -text.trace = Слежка за игроком -text.trace.playername = Имя игрока: [accent]{0} -text.trace.ip = IP: [accent]{0} -text.trace.id = Уникальный идентификатор: [accent]{0} -text.trace.android = Клиент Android: [accent]{0} -text.trace.modclient = Пользовательский клиент: [accent]{0} -text.trace.totalblocksbroken = Всего разбитых блоков: [accent]{0} - -e.structureblocksbroken = Структурных блоков сломанных: [accent]{0} -text.trace.lastblockbroken = Последний сломанный блок:[accent]{0} -text.trace.totalblocksplaced = Всего размещено блоков: [accent]{0} -text.trace.lastblockplaced = Последний размещенный блок: [accent]{0} -text.invalidid = Недопустимый идентификатор клиента! Отправьте отчет об ошибке. -text.server.bans = Блокировки -text.server.bans.none = Никаких заблокированных игроков не найдено! -text.server.admins = Администраторы -text.server.admins.none = Администраторов не найдено! -text.server.add = Добавить сервер -text.server.delete = Вы действительно хотите удалить этот сервер? -text.server.hostname = Хост: {0} -text.server.edit = Редактировать сервер -text.server.outdated = [crimson]Устаревший сервер![] -text.server.outdated.client = [crimson]Устаревший клиент![] -text.server.version = [lightgray]Версия: {0} -text.server.custombuild=[yellow]Пользовательская сборка -text.confirmban = Вы действительно хотите заблокировать этого игрока? -text.confirmunban = Вы действительно хотите разблокировать этого игрока? -text.confirmadmin = Вы уверены, что хотите сделать этого игрока администратором? -text.confirmunadmin = Вы действительно хотите удалить статус администратора с этого игрока? -text.joingame.byip = Присоединиться по IP ... -text.joingame.title = Присоединиться к игре -text.joingame.ip = IP: -text.disconnect = Отключён\n -text.disconnect.data = Не удалось загрузить данные мира! -text.connecting = [accent]Подключение... -text.connecting.data = [accent]Загрузка данных мира... -text.connectfail = [crimson]Не удалось подключиться к серверу: [orange] {0} -text.server.port = Порт: -text.server.addressinuse=Адрес уже используется! -text.server.invalidport = Неверный номер порта! -text.server.error = [crimson]Ошибка создания сервера: [orange] {0} -text.tutorial.back = <назад -text.tutorial.next = далее> -text.save.new = Новое сохранение -text.save.overwrite = Вы уверены,что хотите перезаписать этот слот для сохранения? -text.overwrite = Перезаписать -text.save.none = Сохранения не найдены! -text.saveload = [accent]Сохранение... -text.savefail = Не удалось сохранить игру! -text.save.delete.confirm = Вы уверены,что хотите удалить это сохранение? -text.save.delete = Удалить -text.save.export = Отправить сохранение -text.save.import.invalid = [orange]Это сохранение недействительно! -text.save.import.fail = [crimson]Не удалось импортировать сохранение: [orange] {0} -text.save.export.fail = [crimson]Не удалось отправить сохранение: [orange] {0} -text.save.import = Импортировать сохранение -text.save.newslot = Имя сохранения: -text.save.rename = Переименовывать -text.save.rename.text = Новое название: -text.selectslot = Выберите сохранение. -text.slot = [accent]Слот {0} -text.save.corrupted = [orange]Файл сохранения поврежден или имеет недействительный формат! -text.empty = <Пусто> -text.on = Вкл -text.off = Выкл -text.save.autosave = Автосохранение: {0} -text.save.map = Карта: {0} -text.save.wave = Волна: {0} -text.save.difficulty = Сложность: {0} -text.save.date = Последнее сохранение: {0} -text.confirm = Подтвердить -text.delete = Удалить -text.ok = ОК -text.open = Открыть -text.cancel = Отмена -text.openlink = Открыть ссылку -text.copylink = Скопировать ссылку -text.back = Назад -text.quit.confirm = Вы уверены, что хотите выйти? -text.changelog.title = Список изменений -text.changelog.loading = Получение изменений ... -text.changelog.error.android = [orange]Обратите внимание, что журнал изменений иногда не работает на Android 4.4 и ниже!\nЭто связано с внутренней ошибкой Android. -text.changelog.error.ios = [orange]В настоящее время журнал изменений не поддерживается iOS. -text.changelog.error = [scarlet]Ошибка при получении изменений!\nПроверьте подключение к Интернету. -text.changelog.current=[yellow][[Текущая версия] -text.changelog.latest=[orange][[Последняя версия] -text.loading = [accent] Загрузка... -text.wave = [orange]Волна {0} -text.wave.waiting = Волна через {0} -text.waiting = Ожидание... -text.enemies = {0} Противников -text.enemies.single = {0} Противник -text.loadimage = Загрузить изображение -text.saveimage = Сохранить изображение -text.oregen = Генерация руд -text.editor.badsize = [orange]Недопустимый формат изображения! [] \nДопустимый формат карты: {0} -text.editor.errorimageload = Ошибка загрузки изображения: [orange] {0} -text.editor.errorimagesave = Ошибка сохранения изображения: [orange] {0} -text.editor.generate = Создать -text.editor.resize = Изменить \nразмер -text.editor.loadmap = Загрузить\nкарту -text.editor.savemap = Сохранить\nкарту -text.editor.loadimage = Загрузить \nизображение -text.editor.saveimage = Сохранить \nизображение -text.editor.unsaved = [scarlet]У вас есть не сохраненные изменения![] \nВы уверены,что хотите выйти? -text.editor.brushsize = Размер кисти: {0} -text.editor.noplayerspawn = На этой карте нет точки появления игрока! -text.editor.manyplayerspawns = На карте не может быть больше одной точки появления игрока! -text.editor.manyenemyspawns = Не может быть больше {0} вражеских точек появления! -text.editor.resizemap = Изменить размер карты -text.editor.resizebig = [scarlet]Внимание! \n[]Карты размером больше 256 единиц могут быть не стабильны и тормозить. -text.editor.mapname = Название карты: -text.editor.overwrite = [accent]Внимание! \nЭто перезапишет уже существующую карту. -text.editor.failoverwrite = [crimson]Невозможно перезаписать стандартную карту! -text.editor.selectmap = Выберите карту для загрузки: -text.width = Ширина: -text.height = Высота: -text.randomize = Рандомизировать -text.apply = Применить -text.update = Обновить -text.menu = Меню -text.play = Играть -text.load = Загрузить -text.save = Сохранить -text.language.restart = Перезагрузите игру, чтобы настройки языка вступили в силу. -text.settings.language = Язык -text.settings = Настройки -text.tutorial = Обучение -text.editor = Редактор -text.mapeditor = Редактор карт -text.donate = Донат -text.settings.reset = Сбросить по умолчанию -text.settings.controls = Управление -text.settings.game = Игра -text.settings.sound = Звук -text.settings.graphics = Графика -text.upgrades = Улучшения -text.purchased = [LIME]Создан! -text.weapons = Оружие -text.paused = Пауза -text.respawn = Возрождение через -text.info.title = [accent]Информация -text.error.title = [crimson]Произошла ошибка -text.error.crashmessage = [SCARLET]Произошла непредвиденная ошибка,которая могла вызвать сбой.[]Пожалуйста, сообщите точные обстоятельства разработчику,при которых эта ошибка возникла : [ORANGE]anukendev@gmail.com[] -text.error.crashtitle = Произошла ошибка -text.mode.break = Режим сноса: {0} -text.mode.place = Режим размещения: {0} -placemode.hold.name = линия -placemode.areadelete.name = зона -placemode.touchdelete.name = касание -placemode.holddelete.name = удержание -placemode.none.name = ничего -placemode.touch.name = Касание -placemode.cursor.name = курсор -text.blocks.extrainfo = [accent]дополнительная информация о блоке: -text.blocks.blockinfo = Информация о блоке -text.blocks.powercapacity = Вместимость энергии -text.blocks.powershot = Энергия / выстрел -text.blocks.powersecond = Энергия / в секунду -text.blocks.powerdraindamage = Поглощение энергии / урон -text.blocks.shieldradius = Радиус щита -text.blocks.itemspeedsecond = Скорость предметов / в секунду -text.blocks.range = Дальность -text.blocks.size = Размер -text.blocks.powerliquid = Энергия / Жидкость -text.blocks.maxliquidsecond = Макс. Жидкость / в секунду -text.blocks.liquidcapacity = Вместимость жидкости -text.blocks.liquidsecond = Жидкость / в секунду -text.blocks.damageshot = Урон / выстрел -text.blocks.ammocapacity = Вместимость боеприпасов -text.blocks.ammo = боеприпасы -text.blocks.ammoitem = боеприпасы / предмет -text.blocks.maxitemssecond = Макс. Количество предметов / в секунду -text.blocks.powerrange = Диапазон мощности энергии -text.blocks.lasertilerange = Дальность лазера -text.blocks.capacity = Вместимость -text.blocks.itemcapacity = Вместимость предметов -text.blocks.maxpowergenerationsecond = Макс. выработка энергии / в секунду -text.blocks.powergenerationsecond = Выработка энергии / в секунду -text.blocks.generationsecondsitem = Выработка секунд / предмет -text.blocks.input = Прием -text.blocks.inputliquid = Прием жидкости -text.blocks.inputitem = Прием предмета -text.blocks.output = Вывод -text.blocks.secondsitem = Секунды / предмет -text.blocks.maxpowertransfersecond = Максимальная передача энергии / секунда -text.blocks.explosive = Взрывоопасно! -text.blocks.repairssecond = Ремонт / в секунду -text.blocks.health = Здоровье -text.blocks.inaccuracy = Разброс -text.blocks.shots = Выстрелы -text.blocks.shotssecond = Выстрелов / в секунду -text.blocks.fuel = топливо -text.blocks.fuelduration = Продолжительность действия топлива -text.blocks.maxoutputsecond = Макс. Вывод / в секунду -text.blocks.inputcapacity = Вместимость ввода -text.blocks.outputcapacity = Вместимость вывода -text.blocks.poweritem = Энергия / предмет -text.placemode = Режим размещения -text.breakmode = Режим сноса -text.health = здоровье -setting.difficulty.easy = легко -setting.difficulty.normal = нормально -setting.difficulty.hard = тяжело -setting.difficulty.insane = нереально -setting.difficulty.purge = зачистка -setting.difficulty.name = Сложность -setting.screenshake.name = Дрожание экрана -setting.smoothcam.name = Плавная камера -setting.indicators.name = Индикаторы противников -setting.effects.name = Эффекты на экране +credits.text = Создатель [ROYAL] Anuken. - [SKY]anukendev@gmail.com[][]\n\nЕсть недоработки в переводе или хотите найти союзников для совместной игры?\nПишите в офф. discord-сервер Mindustry в канал #русский.\n\nПереводчики на русский язык:\n[YELLOW]Prosta4ok_ua\n[BLACK]XZimur\n[BLUE]Beryllium +credits = Авторы +contributors = Переводчики и Помощники +discord = Присоединяйтесь к нашему Discord! +link.discord.description = Официальный Discord-сервер Mindustry +link.github.description = Исходный код игры +link.dev-builds.description = Нестабильные версии +link.trello.description = Официальная доска Trello для запланированных функций +link.itch.io.description = Itch.io страница с загрузкой ПК версии и веб-версией игры +link.google-play.description = Скачать для Android c Google play +link.wiki.description = Официальная вики Mindustry(англ.) +linkfail = Не удалось открыть ссылку!\nURL-адрес был скопирован в буфер обмена. +screenshot = Cкриншот сохранён в {0} +screenshot.invalid = Карта слишком большая, возможно, не хватает памяти для скриншота. +gameover = Игра окончена +gameover.pvp = [accent] {0}[] команда победила! +highscore = [YELLOW]Новый рекорд! +stat.wave = Волн отражено:[accent] {0} +stat.enemiesDestroyed = Врагов уничтожено:[accent] {0} +stat.built = Строений построено:[accent] {0} +stat.destroyed = Строений уничтожено:[accent] {0} +stat.deconstructed = Строений деконструировано:[accent] {0} +stat.delivered = Ресурсов добыто: +stat.rank = Финальный Счёт: [accent]{0} +placeline = Вы выбрали блок.\nВы можете[accent] строить в линию[], сначала[accent] удерживая палец в течение нескольких секунд[] и потом перетаскивая его.\n\n[scarlet]СДЕЛАЙ ЭТО. +removearea = Вы выбрали режим удаления.\nВы можете[accent] удалять блоки в выбранной области[], [accent]удерживая палец несколько секунд[] и потом перетаскивая его.\n\n[scarlet]СДЕЛАЙ ЭТО +launcheditems = [accent]Запущенные предметы +map.delete = Вы действительно хотите удалить карту "[accent]{0}[]"? +level.highscore = Рекорд: [accent]{0} +level.select = Выбор карты +level.mode = Режим игры: +showagain = Не показывать снова до следующей сессии +coreattack = < Ядро находится под атакой! > +nearpoint = [[ [scarlet]ПОКИНЬТЕ ЗОНУ НЕМЕДЛЕННО[] ]]\nаннигиляция неизбежна. +outofbounds = [[ ЗА ГРАНИЦАМИ ]]\n[]самоуничтожение через{0} +database = База Данных Ядра +savegame = Сохранить игру +loadgame = Загрузить игру +joingame = Сетевая игра +addplayers = Доб/удалить игроков +customgame = Пользовательская игра +newgame = Новая игра +none = <нет> +minimap = Мини-карта +close = Закрыть +quit = Выход +maps = Карты +continue = Продолжить +maps.none = [LIGHT_GRAY]Карты не найдены! +about.button = Об игре +name = Ник: +noname = Для начала, придумайте[accent] себе никнейм[]. +filename = Имя файла: +unlocked = Новый контент разблокирован! +completed = [accent]Завершено +techtree = Технологическое Дерево +research.list = [LIGHT_GRAY]Исследование: +research = Исследование +researched = [LIGHT_GRAY]{0} исследовано. +players = Игроков на сервере: {0} +players.single = {0} игрок на сервере +server.closing = [accent]Закрытие сервера... +server.kicked.kick = Вас выгнали с сервера! +server.kicked.serverClose = Сервер закрыт. +server.kicked.clientOutdated = Устаревший клиент! Обновите игру! +server.kicked.serverOutdated = Устаревший сервер! Попросите хоста обновить сервер/игру! +server.kicked.banned = Вы заблокированы на этом сервере. +server.kicked.recentKick = Вы недавно были кикнуты.\n Подождите немного перед следующим подключением +server.kicked.nameInUse = На этом сервере есть кто-то с этим именем. +server.kicked.nameEmpty = Ваше имя должно содержать хотя бы один символ или цифру. +server.kicked.idInUse = Вы уже на этом сервере! Соединение с двумя учетными записями не разрешено. +server.kicked.customClient = Этот сервер не поддерживает пользовательские сборки. Загрузите официальную версию. +server.kicked.gameover = Игра окончена! +host.info = Кнопка [accent] Сервер [] размещает сервер на порт [accent]6567[].[]\nЛюбой пользователь в той же [LIGHT_GRAY]сети [] получет возможность видеть ваш сервер в своём списке серверов.\n\nЕсли вы хотите, чтобы люди могли подключаться из любого места по IP, то требуется переадресация(проброс) портов.[].\n\n[LIGHT_GRAY] Примечание. Если у кого-то возникают проблемы с подключением к вашей локальной сети, убедитесь, что вы разрешили Mindustry доступ к вашей локальной сети в настройках брандмауэра. +join.info = Здесь вы можете ввести IP-адрес [accent]сервера[] для подключения или открыть [accent]локальную сеть [] для подключения к другим серверам.\nПоддерживается многопользовательский режим LAN и WAN.\n\n[LIGHT_GRAY] Примечание: это не является автоматическим глобальным списком серверов; если вы хотите подключиться к кому-то по IP, вам нужно будет спросить у хоста его IP-адрес. +hostserver = Запустить сервер +hostserver.mobile = Запустить\nсервер +host = Сервер +hosting = [accent]Открытие сервера... +hosts.refresh = Обновить +hosts.discovering = Поиск локальных игр +server.refreshing = Обновление сервера +hosts.none = [lightgray]Локальных игр не обнаружено! +host.invalid = [scarlet] Не удается подключиться к хосту. +trace = Слежка за игроком +trace.playername = Имя игрока: [accent]{0} +trace.ip = IP: [accent]{0} +trace.id = ID: [accent]{0} +trace.mobile = Мобильный клиент: [accent]{0} +trace.modclient = Пользовательский клиент: [accent]{0} +invalidid = Недопустимый ID клиента! Отправьте отчёт об ошибке. +server.bans = Блокировки +server.bans.none = Заблокированных игроков нет! +server.admins = Администраторы +server.admins.none = Администраторов нет! +server.add = Добавить сервер +server.delete = Вы действительно хотите удалить этот сервер? +server.hostname = Хост: {0} +server.edit = Редактировать сервер +server.outdated = [crimson]Устаревший сервер![] +server.outdated.client = [crimson]Устаревший клиент![] +server.version = [lightgray]Версия: {0} {1} +server.custombuild = [yellow]Пользовательская сборка +confirmban = Вы действительно хотите заблокировать этого игрока? +confirmkick = Вы действительно хотите выгнать(кикнуть) этого игрока? +confirmunban = Вы действительно хотите разблокировать этого игрока? +confirmadmin = Вы уверены, что хотите сделать этого игрока администратором? +confirmunadmin = Вы действительно хотите убрать этого игрока из администраторов? +joingame.title = Присоединиться к игре +joingame.ip = IP: +disconnect = Отключён +disconnect.data = Не удалось загрузить данные мира! +connecting = [accent]Подключение... +connecting.data = [accent]Загрузка данных мира... +server.port = Порт: +server.addressinuse = Данный адрес уже используется! +server.invalidport = Неверный номер порта! +server.error = [crimson]Ошибка создания сервера: [accent] {0} +save.old = Это сохранение для более старой версии игры и больше не может использоваться.\n\n[LIGHT_GRAY]Совместимость сохранений будет реализована в полной версии 4.0. +save.new = Новое сохранение +save.overwrite = Вы уверены,что хотите перезаписать этот слот для сохранения? +overwrite = Перезаписать +save.none = Сохранения не найдены! +saveload = [accent]Сохранение... +savefail = Не удалось сохранить игру! +save.delete.confirm = Вы уверены,что хотите удалить это сохранение? +save.delete = Удалить +save.export = Экспортировать сохранение +save.import.invalid = [accent]Это сохранение недействительно! +save.import.fail = [crimson]Не удалось импортировать сохранение: [accent] {0} +save.export.fail = [crimson]Не удалось экспортировать сохранение: [accent] {0} +save.import = Импортировать сохранение +save.newslot = Имя сохранения: +save.rename = Переименовать +save.rename.text = Новое название: +selectslot = Выберите сохранение. +slot = [accent]Слот {0} +save.corrupted = [accent]Сохранённый файл повреждён или имеет недействительный формат!\nЕсли вы только что обновили свою игру, это, вероятно, из-за изменение формата сохранения[scarlet], а не []ошибка. +empty = <Пусто> +on = Вкл +off = Выкл +save.autosave = Автосохранение: {0} +save.map = Карта: {0} +save.wave = Волна: {0} +save.difficulty = Сложность: {0} +save.date = Последнее сохранение: {0} +save.playtime = Время в игре: {0} +warning = Предупреждение +confirm = Подтверждение +delete = Удалить +ok = ОК +open = Открыть +customize = Настроить +cancel = Отмена +openlink = Открыть ссылку +copylink = Скопировать ссылку +back = Назад +quit.confirm = Вы уверены, что хотите выйти? +changelog.title = Список изменений +changelog.loading = Получение изменений ... +changelog.error.android = [accent]Обратите внимание, что журнал изменений иногда не работает на Android 4.4 и ниже!\nЭто связано с внутренней ошибкой Android. +changelog.error.ios = [accent]В настоящее время журнал изменений не поддерживается iOS. +changelog.error = [scarlet]Ошибка при получении изменений!\nПроверьте подключение к Интернету. +changelog.current = [yellow][[Текущая версия] +changelog.latest = [accent][[Последняя версия] +loading = [accent] Загрузка... +saving = [accent]Сохранение.. +wave = [accent]Волна {0} +wave.waiting = [LIGHT_GRAY]Волна через {0} +wave.waveInProgress = [LIGHT_GRAY]Волна продолжается +waiting = Ожидание... +waiting.players = Ожидание игроков ... +wave.enemies = [LIGHT_GRAY]{0} противник. остался +wave.enemy = [LIGHT_GRAY]{0} противник остался +loadimage = Загрузить изображение +saveimage = Сохранить изображение +unknown = Неизвестно +custom = Пользовательская +builtin = Встроенная +map.delete.confirm = Вы действительно хотите удалить эту карту? Это действие не может быть отменено! +map.random = [accent]Случайная карта +map.nospawn = Эта карта не имеет ядер, в которых игрок может появиться! Добавьте[ROYAL] синее[] ядро на эту карту в редакторе карт. +map.nospawn.pvp = У этой карты нет вражеских ядер, в которых игрок может появиться! Добавьте[SCARLET] красные[] ядра к этой карте в редакторе. +map.nospawn.attack = У этой карты нету вражеских ядер! Добавьте [scarlet] красные[] ядра в эту карте в редакторе. +map.invalid = Ошибка загрузки карты: повреждённый или недопустимый файл карты. +editor.brush = Кисть +editor.openin = Открыть в редакторе +editor.oregen = Генерация Руд +editor.oregen.info = Генерация Руд: +editor.mapinfo = Информация о карте +editor.author = Автор: +editor.description = Описание: +editor.waves = Волны: +editor.rules = Правила: +editor.ingame = Редактировать в игре +waves.title = Волны +waves.remove = Удалить +waves.never = <никогда> +waves.every = Каждый +waves.waves = волна(ы) +waves.perspawn = за появление +waves.to = к +waves.boss = Босс +waves.preview = Предварительный просмотр +waves.edit = Редактировать ... +waves.copy = Копировать в буфер обмена +waves.load = Загрузить из буфера обмена +waves.invalid = Неверные волны в буфере обмена. +waves.copied = Волны скопированы. +editor.default = [LIGHT_GRAY]<По умолчанию> +edit = Редактировать... +editor.name = Название: +editor.spawn = Создать боевую единицу +editor.removeunit = Удалить боевую единицу +editor.teams = Команды +editor.elevation = Возвышенность +editor.errorload = Ошибка загрузки изображения: [accent] {0} +editor.errorsave = Ошибка сохранения изображения: [accent] {0} +editor.errorimage = That's an image, not a map. Don't go around changing extensions expecting it to work.\n\nIf you want to import a legacy map, use the 'import legacy map' button in the editor. +editor.errorlegacy = This map is too old, and uses a legacy map format that is no longer supported. +editor.errorheader = This map file is either not valid or corrupt. +editor.errorname = Карта не имеет определенного имени. +editor.update = Обновить +editor.randomize = Случайно +editor.apply = Применить +editor.generate = Сгенерировать +editor.resize = Изменить \nразмер +editor.loadmap = Загрузить\nкарту +editor.savemap = Сохранить\nкарту +editor.saved = Сохранено! +editor.save.noname = У Вашей карты нет названия! Назовите её в меню «Информация о карте». +editor.save.overwrite = Ваша карта не может быть записана поверх встроенной карты! Введите другое название в меню «Информация о карте» +editor.import.exists = [scarlet]Не удалось импортировать: []карта с данным именем уже существует '{0}'! +editor.import = Импорт... +editor.importmap = Импортировать карту +editor.importmap.description = Импортировать уже существующую карту +editor.importfile = Импортировать файл +editor.importfile.description = Импортировать файл карты из вне +editor.importimage = Импортировать устаревшее изображение +editor.importimage.description = Импортировать файл с изображения ландшафта +editor.export = Экспорт... +editor.exportfile = Экспортировать файл +editor.exportfile.description = Экспорт файла карты +editor.exportimage = Экспортировать изображение ландшафта +editor.exportimage.description = Экспортировать файл с изображением карты +editor.loadimage = Загрузить \nизображение +editor.saveimage = Сохранить \nизображение +editor.unsaved = [scarlet]У вас есть несохранённые изменения![]\nВы уверены, что хотите выйти? +editor.resizemap = Изменить размер карты +editor.mapname = Название карты: +editor.overwrite = [accent]Внимание! \nЭто перезапишет уже существующую карту. +editor.overwrite.confirm = [scarlet]Осторожно![] Карта с таким названием уже существует. Вы действительно хотите её перезаписать? +editor.selectmap = Выберите карту для загрузки: +filters.empty = [LIGHT_GRAY]Нет фильтров. Добавьте один при помощи кнопки ниже +filter.distort = Искажение +filter.noise = Шум +filter.ore = Руда +filter.rivernoise = Речной Шум +filter.scatter = Распылитель +filter.terrain = Ландшафт +filter.option.scale = Масштаб +filter.option.chance = Шанс +filter.option.mag = Величина +filter.option.threshold = Спад +filter.option.circle-scale = Круговая шкала +filter.option.octaves = Октавы +filter.option.falloff = Спад +filter.option.block = Блок +filter.option.floor = Поверхность +filter.option.wall = Стена +filter.option.ore = Руда +filter.option.floor2 = Вторая поверхность +filter.option.threshold2 = Вторичный спад +filter.option.radius = Radius +filter.option.percentile = Percentile +width = Ширина: +height = Высота: +menu = Меню +play = Играть +load = Загрузить +save = Сохранить +fps = FPS: {0} +tps = TPS: {0} +ping = Пинг: {0} мс +language.restart = Перезагрузите игру, чтобы языковые настройки вступили в силу.\nPlease restart your game for the language settings to take effect. +settings = Настройки +tutorial = Обучение +editor = Редактор +mapeditor = Редактор карт +donate = Пожертво\nвать +abandon = Покинуть +abandon.text = Эта зона и все ресурсы будут потеряны. +locked = Заблокировано +complete = [LIGHT_GRAY]Достигнута: +zone.requirement = Волна {0} в зоне {1} +resume = Возобновить зону:\n[LIGHT_GRAY]{0} +bestwave = [LIGHT_GRAY]Наилучшая волна: {0} +launch = < ЗАПУСК > +launch.title = Запуск успешен +launch.next = [LIGHT_GRAY]следующая возможность на волне {0} +launch.unable = [scarlet]ЗАПУСК невозможен.[] {0} Враг. +launch.confirm = Это удалит все ресурсы в Вашем ядре.\nВы не сможете вернуться на эту базу. +uncover = Раскрыть +configure = Выгрузить конфигурацию +configure.locked = [LIGHT_GRAY]Разблокировать настройки выгрузки: Волна {0}. +zone.unlocked = [LIGHT_GRAY]{0} разблокировано. +zone.requirement.complete = {0}-я волна достигнута:\nУсловия для зоны "{1}" исполнены. +zone.config.complete = {0} волн достигнуто:\nВыгружаемая конфигурация разблокирована +zone.resources = Обнаруженные ресурсы: +add = Добавить... +boss.health = Здоровье босса +connectfail = [crimson]Не удалось подключиться к серверу: [accent] {0} +error.unreachable = Сервер недоступен. +error.invalidaddress = Некорректный адрес. +error.timedout = Время ожидания истекло!\nУбедитесь, что хост настроен для перенаправления портов и адрес корректный! +error.mismatch = Ошибка пакета:\nвозможное несоответствие версии клиента/сервера. \nУбедитесь, что у Вас и у создателя сервера установлена последняя версия Mindustry! +error.alreadyconnected = Вы уже подключены. +error.mapnotfound = Файл карты не найден! +error.io = Сетевая ошибка ввода-вывода. +error.any = Неизвестная сетевая ошибка. +zone.groundZero.name = Нулевая земля +zone.desertWastes.name = Пустынный мусор +zone.craters.name = Кратеры +zone.frozenForest.name = Ледяной Лес +zone.ruinousShores.name = Разрушенные Берега +zone.stainedMountains.name = Окрашенные горы +zone.desolateRift.name = Пустынный Разлом +zone.nuclearComplex.name = Ядерный Производственный Комплекс +zone.overgrowth.name = Заросли +zone.tarFields.name = Дёгтяные поля +settings.language = Язык +settings.reset = Сбросить по умолчанию +settings.rebind = Смена +settings.controls = Управление +settings.game = Игра +settings.sound = Звук +settings.graphics = Графика +settings.cleardata = Очистить данные ... +settings.clear.confirm = Вы действительно хотите очистить свои данные?\nТо, что сделано, нельзя отменить! +settings.clearall.confirm = [scarlet]ОСТОРОЖНО![]\nЭто уничтожит все данные, включая сохранения, карты, разблокированное и настройки управления.\nПосле того как вы нажмете ОК, игра уничтожит все данные и автоматически закроется. +settings.clearunlocks = Очистить разблокированное +settings.clearall = Очистить всё +paused = Пауза +yes = Да +no = Нет +info.title = Информация +error.title = [crimson]Произошла ошибка +error.crashtitle = Произошла ошибка +blocks.input = Вход +blocks.output = Выход +blocks.booster = Ускоритель +block.unknown = [LIGHT_GRAY]??? +blocks.powercapacity = Вместимость энергии +blocks.powershot = Энергия/выстрел +blocks.targetsair = Воздушные цели +blocks.targetsground = Наземные цели +blocks.itemsmoved = Скорость перемещения +blocks.launchtime = Интервал запусков +blocks.shootrange = Радиус действия +blocks.size = Размер +blocks.liquidcapacity = Вместимость жидкости +blocks.powerrange = Диапазон передачи энергии +blocks.poweruse = Потребляет энергии +blocks.powerdamage = Энергия/урон +blocks.itemcapacity = Вместимость предметов +blocks.basepowergeneration = Базовая генерация энергии +blocks.productiontime = Время производства +blocks.repairtime = Время полной регенерации +blocks.speedincrease = Увеличение скорости +blocks.range = Радиус действия +blocks.drilltier = Добывает +blocks.drillspeed = Базовая скорость бурения +blocks.boosteffect = Ускоряющий эффект +blocks.maxunits = Максимальное количество активных единиц +blocks.health = Здоровье +blocks.buildtime = Время строительства +blocks.inaccuracy = Разброс +blocks.shots = Выстрелы +blocks.reload = Выстрелы/секунду +blocks.ammo = Боеприпасы +bar.drillspeed = Скорость бурения: {0}/s +bar.efficiency = Эффективность: {0}% +bar.powerbalance = Энергия: {0}/с +bar.poweramount = Энергия: {0} +bar.poweroutput = Выходная энергия: {0} +bar.items = Предметы: {0} +bar.liquid = Жидкости +bar.heat = Нагревание +bar.power = Энергия +bar.progress = Прогресс строительства +bar.spawned = Боев. ед. : {0}/{1} +bullet.damage = [stat]{0}[lightgray] урона +bullet.splashdamage = [stat]{0}[lightgray] урона в радиусе ~[stat] {1}[lightgray] БЛОКОВ +bullet.incendiary = [stat]поджигающий +bullet.homing = [stat]самонаводящийся +bullet.shock = [stat]ЭМИ +bullet.frag = [stat]взрывоопасный +bullet.knockback = [stat]{0}[lightgray] отдачи +bullet.freezing = [stat]замораживающий +bullet.tarred = [stat]горючий +bullet.multiplier = [stat]{0}[lightgray]x снарядов +bullet.reload = [stat]{0}[lightgray]x скорость перезарядки +unit.blocks = блоки +unit.powersecond = единиц энергии/секунду +unit.liquidsecond = жидкостных единиц/секунду +unit.itemssecond = предметов/секунду +unit.liquidunits = жидкостных единиц +unit.powerunits = энерг. единиц +unit.degrees = град. +unit.seconds = сек. +unit.persecond = /сек +unit.timesspeed = x скорость +unit.percent = % +unit.items = единиц +category.general = Основные +category.power = Энергия +category.liquids = Жидкости +category.items = Предметы +category.crafting = Ввод/вывод +category.shooting = Cтрельба +category.optional = Дополнительные улучшения +setting.landscape.name = Ландшафтный режим +setting.shadows.name = Тени +setting.linear.name = Линейная фильтрация +setting.animatedwater.name = Анимированная вода +setting.animatedshields.name = Анимированные щиты +setting.antialias.name = Сглаживание[LIGHT_GRAY] (требует перезапуска)[] +setting.indicators.name = Показывать в сторону союзников и врагов +setting.autotarget.name = Авто-стрельба +setting.fpscap.name = Макс. FPS +setting.fpscap.none = Неограниченный +setting.fpscap.text = {0} FPS +setting.swapdiagonal.name = Всегда Диагональное Размещение +setting.difficulty.training = Обучение +setting.difficulty.easy = Легко +setting.difficulty.normal = Нормально +setting.difficulty.hard = Тяжело +setting.difficulty.insane = Безумно +setting.difficulty.name = Сложность: +setting.screenshake.name = Тряска экрана +setting.effects.name = Эффекты setting.sensitivity.name = Чувствительность контроллера -setting.saveinterval.name = Интервал автосохранения -setting.seconds = {0} Секунд -setting.fullscreen.name = Полноэкранный -setting.multithread.name = Многопоточность -setting.fps.name = Показать FPS +setting.saveinterval.name = Интервал сохранения +setting.seconds = {0} Секунд +setting.fullscreen.name = Полноэкранный режим +setting.borderlesswindow.name = Окно без границ [LIGHT_GRAY] (может потребоваться перезапуск) +setting.fps.name = Показывать FPS setting.vsync.name = Верт. синхронизация -setting.lasers.name = Показывать энергетические лазеры -setting.previewopacity.name = Прозрачность объкта при предв. просм. -setting.healthbars.name = Показать полоски здоровья объекта -setting.pixelate.name = Пикселизация экрана +setting.lasers.name = Показывать энергию лазеров +setting.pixelate.name = Пикселизация[LIGHT_GRAY] (отключает анимации) +setting.minimap.name = Показать миникарту setting.musicvol.name = Громкость музыки setting.mutemusic.name = Заглушить музыку setting.sfxvol.name = Громкость звуковых эффектов setting.mutesound.name = Заглушить звук -map.maze.name = лабиринт -map.fortress.name = крепость -map.sinkhole.name = раковина -map.caves.name = пещеры -map.volcano.name = вулкан -map.caldera.name = кальдера -map.scorch.name = горение -map.desert.name = пустыня -map.island.name = остров -map.grassland.name = луг -map.tundra.name = тундра -map.spiral.name = спираль -map.tutorial.name = обучение -tutorial.intro.text = [yellow]Добро пожаловать в обучение.[] Чтобы начать нажмите «далее». -tutorial.moveDesktop.text = Для перемещения используйте [orange][[WASD][] клавиши. Удерживайте [orange]shift[] для ускорения. Удерживайте [orange]CTRL[] при использовании [orange]​​колесика мыши[] для увеличения или уменьшения масштаба. -tutorial.shoot.text = Используйте мышь для прицеливания, удерживайте [orange]левую кнопку мыши[],чтобы выстрелить. Попробуйте на [yellow]цели[]. -tutorial.moveAndroid.text = Чтобы изменить вид, перетащите палец по экрану. Зажмите и разведите двумя пальцами,чтобы увеличить или уменьшить степень приближения. -tutorial.placeSelect.text = Попробуйте выбрать [yellow]конвейер[] из меню блоков внизу справа. -tutorial.placeConveyorDesktop.text = Используйте [orange][[колёсико мыши][],чтобы повернуть конвейер лицом [orange]вперед[], поместите его в [yellow]отмеченное место[],используя [orange][[левую кнопку мыши]]. -tutorial.placeConveyorAndroid.text = Используйте [orange][[кнопку поворота][], чтобы повернуть конвейер лицом [orange]вперед[], перетащите его на место одним пальцем, затем поместите его в [yellow]отмеченное место[],используя [orange][[галочку][]. -tutorial.placeConveyorAndroidInfo.text = Кроме того, вы можете нажать на значок перекрестия в левом нижнем углу, чтобы перейти в [orange][[режим касания][] и поместить блоки, нажав на экран. В режиме касания блоки можно поворачивать стрелкой в ​​левом нижнем углу. Нажмите [yellow]далее[],чтобы попробовать. -tutorial.placeDrill.text = Теперь, выберите и поместите [yellow]каменный бур[] в отмеченное место. -tutorial.blockInfo.text = Если вы хотите узнать больше о блоке, вы можете нажать на [orange]знак вопроса[] в правом верхнем углу, чтобы прочитать его описание. -tutorial.deselectDesktop.text = Вы можете отменить выбор блока, используя [orange][[правую кнопку мыши][]. -tutorial.deselectAndroid.text = Вы можете отменить выбор блока, нажав кнопку [orange]X[]. -tutorial.drillPlaced.text = Бур теперь производит [yellow]камень,[] выведет его на конвейер, а затем переместит его в [yellow]ядро​[] -tutorial.drillInfo.text = Разным рудам нужны разные буры. Камень требует каменный бур, железо требует железный бур и т.д. -tutorial.drillPlaced2.text = Перемещение предметов в ядро ​​помещает их в ваш [yellow]инвентарь для предметов[], в левом верхнем углу. Размещение блоков использует предметы из вашего инвентаря. -tutorial.moreDrills.text = Вы можете соединить много буров и конвейеров вместе, вот так. -tutorial.deleteBlock.text = Вы можете удалить блоки, щелкнув [orange]правой кнопкой мыши[] на блоке, который вы хотите удалить. Попробуйте удалить этот конвейер. -tutorial.deleteBlockAndroid.text = Вы можете удалить блоки [orange]выбрав перекрестие []в меню режима сноса[orange][] в левом нижнем углу и нажать на блок. Попробуйте удалить этот конвейер. -tutorial.placeTurret.text = Теперь выберите и поместите [yellow]турель[] в [yellow]отмеченную позицию[]. -tutorial.placedTurretAmmo.text = Эта турель теперь принимает [yellow]боеприпасы[] от конвейера. Вы можете видеть, сколько патронов у него есть, посмотрев на зеленую полосу над ней [green][]. -tutorial.turretExplanation.text = Турели автоматически стреляют в ближайшего противника в радиусе действия, если у них достаточно боеприпасов. -tutorial.waves.text = Каждые [yellow]60[] секунд, волна [coral]противников[] будет появляться в определенных местах и будет ​​пытаться уничтожить ядро. -tutorial.coreDestruction.text = Ваша цель - [yellow]защитить ядро​​[]. Если ядро будет ​​уничтожено, то вы [cotal]проиграете[] -tutorial.pausingDesktop.text = Если вам нужен будет перерыв, нажмите кнопку [orange]пауза[] в верхнем левом углу или [orange]пробел[],чтобы приостановить игру. Вы можете выбирать и размещать блоки во время паузы, но не можете перемещаться или стрелять. -tutorial.pausingAndroid.text = Если вам нужен будет перерыв, нажмите кнопку [orange]пауза[] в левом верхнем углу, чтобы приостановить игру. Вы можете по-прежнему размещать выбранные блоки во время паузы. -tutorial.purchaseWeapons.text = Вы можете приобрести новое [yellow]оружие[] для своего меха, открыв меню обновлений в левом нижнем углу. -tutorial.switchWeapons.text = Переключаться между оружием можно, щелкнув на его значок в левом нижнем углу или используя цифры [orange][[1-9][]. -tutorial.spawnWave.text = Сейчас прибудет волна. Уничтожьте их. -tutorial.pumpDesc.text = В более поздних волнах, вы должны использовать [yellow]насосы[] для распределения жидкостей для генераторов или экстракторов. -tutorial.pumpPlace.text = Насосы работают аналогично бурам, за исключением того, что они производят жидкости вместо предметов. Попробуйте поместить насос на [yellow]обозначенную нефть[]. -tutorial.conduitUse.text = Теперь поместите [orange]трубопровод[], ведущий от насоса. -tutorial.conduitUse2.text = И еще немного ... -tutorial.conduitUse3.text = И еще немного ... -tutorial.generator.text = Теперь поместите блок[orange]генератор сжигания[] в конец трубопровода. -tutorial.generatorExplain.text = Этот генератор теперь создаст [yellow]энергию[] из нефти. -tutorial.lasers.text = Энергия распределяется с использованием [yellow]электрических лазеров[]. Поверните и поместите его здесь. -tutorial.laserExplain.text = Теперь генератор передаст энергию в лазерный блок. [yellow]Непрозрачный[] луч означает, что он в настоящее время передает электричество, а [yellow]прозрачный[] луч означает, что он не передает электричество. -tutorial.laserMore.text = Вы можете проверить,какой заряд у блока, наблюдая за [yellow]желтой полосой[] над ним. -tutorial.healingTurret.text = Этот лазер можно использовать для питания [lime]ремонтной турели[]. Поместите её сюда. -tutorial.healingTurretExplain.text = Пока она имеет заряд, эта турель будет [lime]ремонтировать соседние блоки.[] Когда вы играете, убедитесь, что вы имеете такую на своей базе как можно быстрее. -tutorial.smeltery.text = Для многих блоков требуется [orange]сталь[], для этого требуется [orange]плавильный завод[]. Поместите его сюда. -tutorial.smelterySetup.text = Этот завод теперь производит [orange]сталь[] из поступающего железа, используя уголь в качестве топлива. -tutorial.tunnelExplain.text = Также обратите внимание, что предметы проходят через [orange] туннельный блок [] и появляются на другой стороне, проходя через каменный блок. Имейте в виду, что туннели могут проходить только до двух блоков. -tutorial.end.text = На этом обучение закончено! Удачи! -text.keybind.title = Переназначить клавиши -keybind.move_x.name = движение_x -keybind.move_y.name = движение_y -keybind.select.name = выбрать -keybind.break.name = Разрушить -keybind.shoot.name = стрельба -keybind.zoom_hold.name = удержание_зума -keybind.zoom.name = Приблизить -keybind.block_info.name = инфо_о_блоке +setting.crashreport.name = Отправлять анонимные отчёты о вылетах +setting.chatopacity.name = Непрозрачность чата +setting.playerchat.name = Отображать чат в игре +keybind.title = Настройка управления +category.general.name = Основное +category.view.name = Просмотр +category.multiplayer.name = Сетевая игра +command.attack = Атаковать +command.retreat = Отступить +command.patrol = Патрулирование +keybind.gridMode.name = Выбрать блок +keybind.gridModeShift.name = Выбрать категорию +keybind.press = Нажмите клавишу... +keybind.press.axis = Нажмите клавишу... +keybind.screenshot.name = Скриншот карты +keybind.move_x.name = Движение по оси x +keybind.move_y.name = Движение по оси y +keybind.select.name = Выбор/Выстрел +keybind.diagonal_placement.name = Диагональное Размещение +keybind.pick.name = Выбрать блок +keybind.break_block.name = Разрушить блок +keybind.deselect.name = Отмена +keybind.shoot.name = Выстрел +keybind.zoom_hold.name = Управление масштабом +keybind.zoom.name = Приблизить/Отдалить keybind.menu.name = Меню keybind.pause.name = Пауза -keybind.dash.name = Рывок -keybind.chat.name = Чат -keybind.player_list.name = список_игроков -keybind.console.name = консоль -keybind.rotate_alt.name = вращать_alt -keybind.rotate.name = вращать -keybind.weapon_1.name = Оружие_1 -keybind.weapon_2.name = Оружие_2 -keybind.weapon_3.name = Оружие_3 -keybind.weapon_4.name = Оружие_4 -keybind.weapon_5.name = Оружие_5 -keybind.weapon_6.name = Оружие_6 -mode.text.help.title=Описание режимов -mode.waves.name = волны -mode.waves.description = в нормальном режиме. ограниченные ресурсы и автоматические наступающие волны. -mode.sandbox.name = песочница -mode.sandbox.description = бесконечные ресурсы и нет таймера для волн. -mode.freebuild.name = свободная\nстройка -mode.freebuild.description=ограниченные ресурсы и нет таймера для волн. -upgrade.standard.name = стандарт -upgrade.standard.description = Стандартный мех. -upgrade.blaster.name = Бластер -upgrade.blaster.description = Стреляет медленной, слабой пулей. -upgrade.triblaster.name = трибластер -upgrade.triblaster.description = Стреляет 3 пулями в разброс. -upgrade.clustergun.name = Кластерная пушка -upgrade.clustergun.description = Стреляет неточным распространением взрывных гранат. -upgrade.beam.name = Лучевая пушка -upgrade.beam.description = Стреляет пробивающим лазерным луч высокой дальности. -upgrade.vulcan.name = вулкан -upgrade.vulcan.description = Стреляет шквалом быстрых пуль. -upgrade.shockgun.name = шоковая пушка -upgrade.shockgun.description = Стреляет взрывным зарядом заряженной шрапнели. -item.stone.name = камень -item.iron.name = железо +keybind.minimap.name = Minimap +keybind.dash.name = Мчаться +keybind.chat.name = Чат +keybind.player_list.name = Список игроков +keybind.console.name = Консоль +keybind.rotate.name = Вращение +keybind.toggle_menus.name = Меню переключения +keybind.chat_history_prev.name = Пред. история чата +keybind.chat_history_next.name = След. история чата +keybind.chat_scroll.name = Прокрутка чата +keybind.drop_unit.name = Сбросить юнита +keybind.zoom_minimap.name = Увеличить миникарту. +mode.help.title = Описание режимов +mode.survival.name = Выживание +mode.survival.description = Обычный режим. В этом режиме надо самим добывать ресурсы и сами волны идут безостановочно. +mode.sandbox.name = Песочница +mode.sandbox.description = Бесконечные ресурсы и нет таймера для волн, но можно самим вызвать волну. +mode.pvp.name = PvP +mode.pvp.description = боритесь против других игроков. +mode.attack.name = Атака +mode.attack.description = Уничтожь вражескую базу. Без волн. +mode.custom = Настройки правил +rules.infiniteresources = Бескон. Ресурсы (Игрок) +rules.wavetimer = Интервал волн +rules.waves = Волны +rules.enemyCheat = Бескон. Ресурсы (ИИ) +rules.unitdrops = Ресурсы Боев. Ед. +rules.unitbuildspeedmultiplier = Множитель Скорости Производства Боев. Ед. +rules.unithealthmultiplier = Множитель Здоровья Боев. Ед. +rules.playerhealthmultiplier = Множитель Здоровья Игрока +rules.playerdamagemultiplier = Множитель Урона Игрока +rules.unitdamagemultiplier = Множитель Урона Боев. Ед. +rules.enemycorebuildradius = Радиус защиты враж. ядер: [LIGHT_GRAY] {0} (блоков) +rules.respawntime = Интервал возрождения: [LIGHT_GRAY] (сек) +rules.wavespacing = Интервал волн: [LIGHT_GRAY] (сек) +rules.buildcostmultiplier = Множитель затрат на строительство +rules.buildspeedmultiplier = Множитель скорости строительства +rules.waitForWaveToEnd = Волны ожидают врагов +rules.dropzoneradius = Радиус зоны высадки врагов:[LIGHT_GRAY] (блоков) +rules.respawns = Макс. кол-во возрождений за волну +rules.limitedRespawns = Ограничение Возрождений +rules.title.waves = Волны +rules.title.respawns = Возрождения +rules.title.resourcesbuilding = Ресурсы & строительство +rules.title.player = Игроки +rules.title.enemy = Враги +rules.title.unit = Бой. ед. +content.item.name = Предметы +content.liquid.name = Жидкости +content.unit.name = Боевые единицы +content.block.name = Блоки +content.mech.name = Мехи +item.copper.name = Медь +item.copper.description = Полезный строительный материал. Широко используется во всех типах блоков. +item.lead.name = Свинец +item.lead.description = Основной начальный материал. Широко используется в электронике и транспортировке жидкости. item.coal.name = Уголь -item.steel.name = сталь -item.titanium.name = титан -item.dirium.name = дириум -item.uranium.name = уран -item.sand.name = песок +item.coal.description = Распространённое и легкодоступное топливо. +item.graphite.name = Графит +item.titanium.name = Титан +item.titanium.description = Редкий сверхлёгкий металл широко используется в производстве: транспорта, буров и самолётов. +item.thorium.name = Торий +item.thorium.description = Плотный радиоактивный металл используется в качестве структурной поддержки и ядерного топлива. +item.silicon.name = Кремний +item.silicon.description = Очень полезный полупроводник с применениями в солнечных батареях и множестве сложной электроники. +item.plastanium.name = Пластиний +item.plastanium.description = Легкий, пластичный материал, используемый в современных самолетах и боеприпасах для фрагментации. +item.phase-fabric.name = Фазовая ткань +item.phase-fabric.description = Невесомое вещество, используемое в современной электронике и технологии самовосстановления. Не для вышивания. +item.surge-alloy.name = Кинетический сплав +item.surge-alloy.description = Передовой сплав с уникальными электрическими свойствами. +item.spore-pod.name = Споровой стручок +item.spore-pod.description = Используется для превращения в нефть, взрывчатые вещества и топливо. +item.sand.name = Песок +item.sand.description = Обычный материал, который широко используется при плавке как в сплаве, так и в виде шлака. +item.blast-compound.name = Взрывная смесь +item.blast-compound.description = Летучее соединение, используемое в бомбах и взрывчатых веществах. Также может гореть в качестве топлива, но не рекомендуется этого делать. +item.pyratite.name = Пиротит +item.pyratite.description = Очень огнеопасное вещество, используемое в зажигательном оружии. +item.metaglass.name = Метастекло +item.metaglass.description = Сверхпрочная смесь стекла и металла. Широко используется для распределения и хранения жидкости. +item.scrap.name = Металлолом +item.scrap.description = Остатки старых сооружений и подразделений. Содержит незначительные количества многих различных металлов. liquid.water.name = Вода -liquid.plasma.name = Плазма -liquid.lava.name = лава +liquid.slag.name = Шлак liquid.oil.name = Нефть -block.weaponfactory.name = оружейный завод -block.weaponfactory.fulldescription=Используется для создания оружия для игрока. Нажмите для использования. Автоматически извлекает ресурсы из ядра. -block.air.name = воздух -block.blockpart.name = часть блока -block.deepwater.name = глубоководье -block.water.name = вода -block.lava.name = лава -block.oil.name = нефть +liquid.cryofluid.name = Криогенная жидкость +mech.alpha-mech.name = Альфа +mech.alpha-mech.weapon = Тяжёлый пулемёт +mech.alpha-mech.ability = Регенерация +mech.alpha-mech.description = Стандартный мех для настольных устройств. Имеет приличную скорость и урон. +mech.delta-mech.name = Дельта +mech.delta-mech.weapon = Дуговой генератор +mech.delta-mech.ability = Разряд +mech.delta-mech.description = Быстрый, легкобронированный мех, сделанный для ударов с отскоком. Наносит малый урон строениям, но очень быстро убивает большие группы вражеских подразделений своим дуговым молниеносным оружием. +mech.tau-mech.name = Тау +mech.tau-mech.weapon = Восстановительный лазер +mech.tau-mech.ability = Регенирирующая вспышка +mech.tau-mech.description = Мех поддержки. Чинит союзные блоки, стреляя в них. Может исцелить союзников радиусом с его способностью восстанавления. +mech.omega-mech.name = Омега +mech.omega-mech.weapon = Ракетомётная пулемёт +mech.omega-mech.ability = Поглощение урона +mech.omega-mech.description = Громоздкий и хорошо бронированный мех, сделанный для фронтовых нападений. Его способность брони может блокировать до 90% входящего урона. +mech.dart-ship.name = Дротик +mech.dart-ship.weapon = Ретранслятор +mech.dart-ship.description = Стандартный корабль для мобильных устройств. Достаточно быстрый и легкий, но имеет небольшие наступательные возможности и низкую скорость добычи. +mech.javelin-ship.name = Джавелин +mech.javelin-ship.description = Ударный корабль, который использует набег с отскоком. Первоначально медленный, но позже он может ускориться до больших скоростей и летать над вражескими заставами, нанося большой урон своей молниеносной способностью и ракетами. +mech.javelin-ship.weapon = Взрывные ракеты +mech.javelin-ship.ability = Дуговой генератор +mech.trident-ship.name = Трезубец +mech.trident-ship.description = Тяжелый бомбардировщик. Довольно хорошо бронирован. +mech.trident-ship.weapon = Бомбы +mech.glaive-ship.name = Копьё +mech.glaive-ship.description = Большой, хорошо бронированный боевой корабль. Оснащён зажигательным ретранслятором. Хорошее ускорение и максимальная скорость. +mech.glaive-ship.weapon = Огненный пулемёт +item.explosiveness = [LIGHT_GRAY]Взрывоопасность: {0}% +item.flammability = [LIGHT_GRAY]Воспламеняемость: {0}% +item.radioactivity = [LIGHT_GRAY]Радиоактивность: {0}% +unit.health = [LIGHT_GRAY]Здоровье: {0} +unit.speed = [LIGHT_GRAY]Скорость: {0} +mech.weapon = [LIGHT_GRAY]Оружие: {0} +mech.health = [LIGHT_GRAY]Здоровье: {0} +mech.itemcapacity = [LIGHT_GRAY]Вместимость предметов: {0} +mech.minespeed = [LIGHT_GRAY]Скорость добычи: {0}% +mech.minepower = [LIGHT_GRAY]Мощность добычи: {0} +mech.ability = [LIGHT_GRAY]Способность: {0} +mech.buildspeed = [LIGHT_GRAY]Скорость строительства: {0}% +liquid.heatcapacity = [LIGHT_GRAY]Теплоёмкость: {0} +liquid.viscosity = [LIGHT_GRAY]Вязкость: {0} +liquid.temperature = [LIGHT_GRAY]Температура: {0} +block.grass.name = Трава +block.salt.name = Соль +block.saltrocks.name = Соляные Камни +block.pebbles.name = Галька +block.tendrils.name = Щупальца +block.sandrocks.name = Песчаные Скалы +block.spore-pine.name = Споровая сосна +block.sporerocks.name = Споровые камни +block.rock.name = Камень +block.snowrock.name = Снежный камень +block.shale.name = Сланец +block.shale-boulder.name = Сланцевый валун +block.moss.name = Мох +block.shrubs.name = Кусты +block.spore-moss.name = Споровый Мох +block.shalerocks.name = Сланцевые Породы +block.scrap-wall.name = Стена из металлолома +block.scrap-wall-large.name = Великая стена из металлолома +block.scrap-wall-huge.name = Огромная стена из металлолома +block.scrap-wall-gigantic.name = Гигантская стена из металлолома +block.thruster.name = Толкатель +block.kiln.name = Печь +block.kiln.description = Выплавляет песок и свинец в метастекло. Требует малого количества энергии. +block.graphite-press.name = Графитный пресс +block.multi-press.name = Мульти-пресс +block.constructing = {0}\n[LIGHT_GRAY](Строится) +block.spawn.name = Точка появления врагов +block.core-shard.name = Ядро: Осколок +block.core-foundation.name = Ядро: Штаб +block.core-nucleus.name = Ядро: Атом +block.deepwater.name = Глубоководье +block.water.name = Вода +block.tainted-water.name = Загрязнённая вода +block.darksand-tainted-water.name = Тёмный песок с загрязнённая вода +block.tar.name = Дёготь block.stone.name = Камень -block.blackstone.name = черный камень -block.iron.name = железо -block.coal.name = уголь -block.titanium.name = титан -block.uranium.name = уран -block.dirt.name = земля -block.sand.name = песок -block.ice.name = лед -block.snow.name = снег -block.grass.name = трава -block.sandblock.name = блок песка -block.snowblock.name = блок снега -block.stoneblock.name = блок камня -block.blackstoneblock.name = блок черного камня -block.grassblock.name = блок травы -block.mossblock.name = блок мха -block.shrub.name = кустарник -block.rock.name = валун -block.icerock.name = замерзший валун -block.blackrock.name = черный валун -block.dirtblock.name = блок земли -block.stonewall.name = каменная стена -block.stonewall.fulldescription = Дешевый оборонительный блок. Полезен для защиты ядра и турелей в первых волнах. -block.ironwall.name = железная стена -block.ironwall.fulldescription = Основной защитный блок. Обеспечивает защиту от противников. -block.steelwall.name = стальная стена -block.steelwall.fulldescription = Стандартный защитный блок. адекватная защита от противников. -block.titaniumwall.name = титановая стена -block.titaniumwall.fulldescription = Сильный защитный блок. Обеспечивает защиту от противников. -block.duriumwall.name = стена из дириума -block.duriumwall.fulldescription = Очень прочный защитный блок. Обеспечивает защиту от противников. -block.compositewall.name = композитная стена -block.steelwall-large.name = большая стальная стена -block.steelwall-large.fulldescription = Стандартный защитный блок. Охватывает несколько клеток. -block.titaniumwall-large.name = большая титановая стена -block.titaniumwall-large.fulldescription = Сильный защитный блок. Охватывает несколько клеток. -block.duriumwall-large.name = большая стена из дириума -block.duriumwall-large.fulldescription = Очень сильный защитный блок. Охватывает несколько клеток. -block.titaniumshieldwall.name = экранированная стена -block.titaniumshieldwall.fulldescription = Прочный защитный блок с дополнительным встроенным щитом. Требует энергию. Использует энергию для поглощения вражеских пуль. Для обеспечения энергией этого блока рекомендуется использовать усилители энергии. -block.repairturret.name = ремонтная турель -block.repairturret.fulldescription = Ремонтирует близлежащие поврежденные блоки в радиусе действия. Использует небольшое количество энергии. -block.megarepairturret.name = ремонтная турель II -block.megarepairturret.fulldescription = Ремонтирует близлежащие поврежденные блоки в радиусе действия с приличной скоростью. Использует энергию. -block.shieldgenerator.name = Генератор щита -block.shieldgenerator.fulldescription = Передовой защитный блок. Защищает все блоки в радиусе от атаки. Использует мало энергии когда бездействует, но быстро разряжается при контакте с пулями. -block.door.name = дверь -block.door.fulldescription = Блок, который можно открыть и закрыть, нажав на него. -block.door-large.name = большая дверь -block.door-large.fulldescription = Блок, который можно открыть и закрыть, нажав на него. -block.conduit.name = трубопровод -block.conduit.fulldescription = Основной блок транспортировки жидкости. Работает как конвейер, но с жидкостями. Лучше всего использовать насосы или другие трубопроводы. Может использоваться как мост через жидкости для противников и игроков. -block.pulseconduit.name = импульсный трубопровод -block.pulseconduit.fulldescription = Передовой блок транспортировки жидкости. Перемещает жидкости быстрее и хранит больше, чем стандартные трубопроводы. -block.liquidrouter.name = Маршрутизатор житкостей -block.liquidrouter.fulldescription = Работает аналогично маршрутизатору. Принимает жидкость с одной стороны и выводит ее на другие стороны. Полезно для разделения жидкости из одного трубопровода на несколько других трубопроводов. -block.conveyor.name = конвейер -block.conveyor.fulldescription = Основной транспортный блок. Перемещает предметы вперед и автоматически перекладывает их в турели или в крафтеры. Могут вращаться . Может использоваться как мост через жидкости для противников и игроков. -block.steelconveyor.name = стальной конвейер -block.steelconveyor.fulldescription = Передовой транспортный блок предметов. Перемещает предметы быстрее, чем стандартные конвейеры. -block.poweredconveyor.name = импульсный конвейер -block.poweredconveyor.fulldescription = Лучший транспортный блок. Перемещает предметы быстрее, чем стальные конвейеры. +block.sand.name = Песок +block.darksand.name = Тёмный песок +block.ice.name = Лёд +block.snow.name = Снег +block.craters.name = Кратеры +block.sand-water.name = Песок с водой +block.darksand-water.name = Тёмный песок с водой +block.char.name = Выжженная Земля +block.holostone.name = Голографический камень +block.ice-snow.name = Ледяной Снег +block.rocks.name = Камни +block.icerocks.name = Ледяные камни +block.snowrocks.name = Снежные камни +block.dunerocks.name = Песчаные Камни +block.pine.name = Сосна +block.white-tree-dead.name = Мёртвое Белое Дерево +block.white-tree.name = Белое Дерево +block.spore-cluster.name = Скопление спор +block.metal-floor.name = Металлический Пол +block.metal-floor-2.name = Металлический Пол 2 +block.metal-floor-3.name = Металлический Пол 3 +block.metal-floor-5.name = Металлический Пол 5 +block.metal-floor-damaged.name = Повреждённый Металлический Пол +block.dark-panel-1.name = Тёмная Панель 1 +block.dark-panel-2.name = Тёмная Панель 2 +block.dark-panel-3.name = Тёмная Панель 3 +block.dark-panel-4.name = Тёмная Панель 4 +block.dark-panel-5.name = Тёмная Панель 5 +block.dark-panel-6.name = Тёмная Панель 6 +block.dark-metal.name = Тёмный металл +block.ignarock.name = Магматические горные породы +block.hotrock.name = Горячий камень +block.magmarock.name = Магма камень +block.cliffs.name = Скалы +block.copper-wall.name = Медная стена +block.copper-wall-large.name = Большая медная стена +block.titanium-wall.name = Титановая стена +block.titanium-wall-large.name = Большая титановая стена +block.phase-wall.name = Фазовая стена +block.phase-wall-large.name = Большая фазовая стена +block.thorium-wall.name = Ториевая стена +block.thorium-wall-large.name = Большая ториевая стена +block.door.name = Дверь +block.door-large.name = Большая дверь +block.duo.name = Двойная турель +block.scorch.name = Обжигатель +block.scatter.name = Рассеиватель +block.hail.name = Град +block.lancer.name = Копейщик +block.conveyor.name = Конвейер +block.titanium-conveyor.name = Титановый конвейер +block.junction.name = Перекрёсток block.router.name = Маршрутизатор -block.router.fulldescription = Принимает предметы с одного направления и выводит их в 3 других направлениях. Может также хранить определенное количество предметов. Используется для разделения материалов с одного бура на несколько турелей -block.junction.name = Перекресток -block.junction.fulldescription = Действует как мост для двух конвейерных лент. Полезно в ситуациях с двумя различными конвейерами, несущими разные материалы в разные места. -block.conveyortunnel.name = конвейерный туннель -block.conveyortunnel.fulldescription = Перемещает предмет под блоками. Чтобы использовать, поместите один туннель, ведущий в блок, который должен быть туннелирован, и один на другой стороне. Убедитесь, что оба туннеля обращены к противоположным направлениям, которые относятся к блокам, которые они принимают или выводят. -block.liquidjunction.name = Перекресток для жидкостей -block.liquidjunction.fulldescription = Действует как мост для двух пересекающихся трубопроводов. Полезно в ситуациях с двумя различными каналами, перемещающими различные жидкости в разные места. -block.liquiditemjunction.name = Распределитель жидкостей и предметов -block.liquiditemjunction.fulldescription = Действует как мост для пересекающихся трубопроводов и конвейеров. -block.powerbooster.name = усилитель энергии -block.powerbooster.fulldescription = Распределяет электричество всем блокам в пределах своего радиуса. -block.powerlaser.name = Энергетический лазер -block.powerlaser.fulldescription = Создает лазер, который передает питание блоку перед ним. Не генерирует никакой энергии. Лучше всего использовать с генераторами или другими лазерами. -block.powerlaserrouter.name = лазерный маршрутизатор -block.powerlaserrouter.fulldescription = Лазер, который одновременно передает электричество в три направления. Полезно в тех ситуациях, когда требуется питание нескольким блокам от одного генератора. -block.powerlasercorner.name = лазерный угол -block.powerlasercorner.fulldescription = Лазер, распределяющий энергию сразу на два направления. Полезно в тех ситуациях, когда требуется питание нескольким блокам от одного генератора, а маршрутизатор не годится. -block.teleporter.name = телепорт -block.teleporter.fulldescription = Улучшенный транспортный блок предметов. Телепортеры передают предметы в другие телепорты одного цвета. Ничего не происходит, если нет телепортеров одного цвета. Если несколько телепортеров имеют один и тот же цвет, выбирается случайный. Использует энергия. Нажмите, чтобы изменить цвет. -block.sorter.name = сортировщик -block.sorter.fulldescription = Сортирует предмет по типу материала. Материал для приема указывается цветом в блоке. Все предметы, соответствующие материалу сортировки, выводятся вперед, все остальное выводится влево и вправо. -block.core.name = Ядро -block.pump.name = Насос -block.pump.fulldescription = Качают жидкости из блока источнка - обычно вода, лава или нефть. Выводит жидкость в соседние трубопроводы. -block.fluxpump.name = Флюсовый насос -block.fluxpump.fulldescription = Передовая версия насоса. Хранит больше жидкости и качает быстрее. -block.smelter.name = Плавильный завод -block.smelter.fulldescription = Основной блок крафтер. При вводе 1 х железа и 1 х угля выдается одна сталь. -block.crucible.name = Тигель -block.crucible.fulldescription = Продвинутый блок крафтер. При вводе 1х титана и 1х стали выдается один дириум. -block.coalpurifier.name = Экстрактор угля -block.coalpurifier.fulldescription = Стандартный экстрактор. Выдает уголь, когда подается большое количество воды и камня. -block.titaniumpurifier.name = Экстрактор титана -block.titaniumpurifier.fulldescription = Стандартный экстрактор. Выдает титан при подаче большого количества воды и железа. -block.oilrefinery.name = Нефтеперерабатывающий Завод -block.oilrefinery.fulldescription = Перерабатывает большое количество нефти в уголь. Полезно для заправки турелей использующих уголь, когда на карте дефицит угля. -block.stoneformer.name = Формировщик камня -block.stoneformer.fulldescription = Охлаждает жидкую лаву, делая из него камень. Полезен для производства большого количества камней для ​угольного очистителя -block.lavasmelter.name = лавовый плавильный завод -block.lavasmelter.fulldescription = Использует лаву для переработки железа в сталь. Альтернатива плавильным заводам. Полезно в ситуациях, когда угля мало. -block.stonedrill.name = каменный бур -block.stonedrill.fulldescription = Важный бур. Когда он помещается на каменную клетку, медленно, бесконечно добывает камень. -block.irondrill.name = Железный бур -block.irondrill.fulldescription = Основной бур. При размещении на клетке с железной рудой, выдает железо медленным темпом на неопределенный срок. -block.coaldrill.name = угольный бур -block.coaldrill.fulldescription = Основной бур. При размещении на клетке с угольной рудой происходит медленный темп добычи угля на неопределенный срок. -block.uraniumdrill.name = урановый бур -block.uraniumdrill.fulldescription = Передовой бур. При размещении на клетке с урановой рудой выдает уран медленным темпом на неопределенный срок. -block.titaniumdrill.name = титановый бур -block.titaniumdrill.fulldescription = Продвинутый бур. При размещении на клетках с титановой рудой выводится титан медленным темпом на неопределенный срок. -block.omnidrill.name = Адаптивный бур -block.omnidrill.fulldescription = Идеальный бур. Будет добывать любую руду на которой стоит с безумным темпом\n -block.coalgenerator.name = угольный генератор -block.coalgenerator.fulldescription = Важный генератор. Генерирует энергию из угля. Выводит энергию в качестве лазеров на 4 стороны. -block.thermalgenerator.name = термальный генератор -block.thermalgenerator.fulldescription = Генерирует энергию из лавы. Выводит электричество в качестве лазеров на 4 стороны. -block.combustiongenerator.name = генератор внутреннего сгорания -block.combustiongenerator.fulldescription = Генерирует энергию из нефти. Выводит энергию в качестве лазеров на 4 стороны. -block.rtgenerator.name = Генератор RTG -block.rtgenerator.fulldescription = Генерирует небольшое количество энергии из распада радиоактивного урана. Выводит энергию в качестве лазеров на 4 стороны. -block.nuclearreactor.name = ядерный реактор -block.nuclearreactor.fulldescription = Передовая версия генератора RTG и идеальный источник энергии. Генерирует энергию из урана. Требуется постоянное водяное охлаждение.Крайне взрывоопасен; сильно взорвётся при подаче недостаточного количества хладагента. -block.turret.name = Турель -block.turret.fulldescription = Базовая, дешевая турель. Использует камень для боеприпасов. Имеет немного больше диапазон, чем двойная турель. -block.doubleturret.name = двойная турель -block.doubleturret.fulldescription = Немного более мощная версия турели. Использует камень для боеприпасов. Значительно больший урон, но имеет более низкий диапазон. Выстреливает двумя пулями. -block.machineturret.name = Турель Гатлинга -block.machineturret.fulldescription = Стандартная универсальная турель. Использует железо для боеприпасов. Обладает быстрой скоростью выстрелов с приличным уроном. -block.shotgunturret.name = разветвленная турель\n -block.shotgunturret.fulldescription = Стандартная турель. Использует железо для боеприпасов. Стреляет в разброс 7 пулями. Маленький диапазон, но более высокий уровень урона по сравнению с турелью Гатлинга. -block.flameturret.name = Огнемётная турель\n -block.flameturret.fulldescription = Продвинутая турель для защиты на близком расстоянии. Использует уголь для боеприпасов. Имеет очень маленький диапазон, но очень высокий урон. Хорошо подходит на близких расстояниях. Рекомендуется использовать со стенами. -block.sniperturret.name = Турель-рельсотрон -block.sniperturret.fulldescription = Продвинутая дальнобойная турель. Использует сталь для боеприпасов. Очень высокий урон, но низкая скорость стрельбы. Дорога в использовании, но может быть помещена далеко от вражеских линий из-за её дальности. -block.mortarturret.name = Зенитная турель -block.mortarturret.fulldescription = Продвинутая турель с уроном по зоне. Использует уголь для боеприпасов. Очень низкая скорость стрельбы и пуль, но очень высокий урон по одной цели и зоне. Полезен для больших толп врагов. -block.laserturret.name = лазерная турель -block.laserturret.fulldescription = Продвинутая турель. Использует энергию. Хорошая , универсальная турель средней дальности. Атакует только одну цель. Никогда не промахивается. -block.waveturret.name = Тесла-турель -block.waveturret.fulldescription = Продвинутая многоцелевая турель. Использует энергию. Средняя дальность. Никогда не промахивается. В среднем, может нанести небольшой урон, но он может поразить нескольких противников одновременно с помощью цепной молнии. -block.plasmaturret.name = плазменная турель -block.plasmaturret.fulldescription = Высокотехнологичная версия огнеметной турели. Использует уголь в качестве боеприпасов. Очень высокий урон, дальность между маленькой и средней. -block.chainturret.name = Пулемётная турель -block.chainturret.fulldescription = Самая лучшая, скорострельная турель. Использует уран для боеприпасов. Стреляет большими снарядами с высокой скорострельностью. Средняя дальность. Охватывает несколько клеток. Чрезвычайно прочная. -block.titancannon.name = Пушка-титан -block.titancannon.fulldescription = Самая лучшая, дальнобойная турель. Использует уран как боеприпасы. Стреляет большими снарядами с уроном по зоне со средней скоростью стрельбы. Большая дальность. Охватывает несколько клеток. Чрезвычайно прочная. -block.playerspawn.name = Точка появления игрока -block.enemyspawn.name = Точка появления врага +block.distributor.name = Разветвитель +block.sorter.name = Сортировщик +block.sorter.description = Сортирует предметы. Если предмет соответствует выбранному, то он проходит насквозь(прямо). В противном случае предмет выводится слева или справа. +block.overflow-gate.name = Избыточный затвор +block.overflow-gate.description = Комбинированный разветвитель и маршрутизатор, который выводит только слева и справа, если передний путь заблокирован. +block.silicon-smelter.name = Кремниевый плавильный завод +block.phase-weaver.name = Фазовый ткач +block.pulverizer.name = Измельчитель +block.cryofluidmixer.name = Мешалка для криогенной жидкости +block.melter.name = Плавильня +block.incinerator.name = Мусоросжигатель +block.spore-press.name = Споровой Пресс +block.separator.name = Отделитель +block.coal-centrifuge.name = Угольная центрифуга +block.power-node.name = Силовой узел +block.power-node-large.name = Большой силовой узел +block.surge-tower.name = Кинетическая башня +block.battery.name = Аккумулятор +block.battery-large.name = Большой аккумулятор +block.combustion-generator.name = Генератор внутреннего сгорания +block.turbine-generator.name = Турбинный генератор +block.differential-generator.name = Дифференциальный генератор +block.impact-reactor.name = Импульсный реактор +block.mechanical-drill.name = Механический бур +block.pneumatic-drill.name = Пневматический бур +block.laser-drill.name = Лазерный бур +block.water-extractor.name = Гидроконденсатор +block.cultivator.name = Культиватор +block.dart-mech-pad.name = Реконструктор "Альфа" +block.delta-mech-pad.name = Реконструктор "Дельта" +block.javelin-ship-pad.name = Реконструктор "Джавелин" +block.trident-ship-pad.name = Реконструктор "Трезубeц" +block.glaive-ship-pad.name = Реконструктор "Копьё" +block.omega-mech-pad.name = Реконструктор "Омега" +block.tau-mech-pad.name = Реконструктор "Тау" +block.conduit.name = Трубопровод +block.mechanical-pump.name = Механическая помпа +block.item-source.name = Источник предметов +block.item-void.name = Предметный вакуум +block.liquid-source.name = Источник жидкостей +block.power-void.name = Энергетический вакуум +block.power-source.name = Источник энергии +block.unloader.name = Разгрузчик +block.vault.name = Хранилище +block.wave.name = Волна +block.swarmer.name = Роевик +block.salvo.name = Залп +block.ripple.name = Рябь +block.phase-conveyor.name = Фазовый конвейер +block.bridge-conveyor.name = Мостовой конвейер +block.plastanium-compressor.name = Пластиниевый компрессор +block.pyratite-mixer.name = Мешалка пиротита +block.blast-mixer.name = Мешалка взрывоопасного соединения +block.solar-panel.name = Солнечная панель +block.solar-panel-large.name = Большая солнечная панель +block.oil-extractor.name = Нефтяная вышка +block.spirit-factory.name = Завод дронов "Призрак" +block.phantom-factory.name = Завод дронов "Фантом" +block.wraith-factory.name = Завод призрачных истребителей +block.ghoul-factory.name = Завод бомбардировщиков "Гуль" +block.dagger-factory.name = Завод мехов "Кинджал" +block.crawler-factory.name = Завод мехов "Камикадзе" +block.titan-factory.name = Завод мехов "Титан" +block.fortress-factory.name = Завод мехов "Крепость" +block.revenant-factory.name = Завод бомбардировщиков "Потусторонний убийца" +block.repair-point.name = Ремонтный пункт +block.pulse-conduit.name = Импульсный трубопровод +block.phase-conduit.name = Фазовый трубопровод +block.liquid-router.name = Жидкостный маршрутизатор +block.liquid-tank.name = Жидкостный резервуар +block.liquid-junction.name = Жидкостный перекрёсток +block.bridge-conduit.name = Мостовой трубопровод +block.rotary-pump.name = Роторный насос +block.thorium-reactor.name = Ториевый реактор +block.mass-driver.name = Электромагнитная катапульта +block.blast-drill.name = Воздушная буровая установка +block.thermal-pump.name = Термальный насос +block.thermal-generator.name = Термальный генератор +block.alloy-smelter.name = Плавильня кинетического сплава +block.mender.name = Регенератор +block.mend-projector.name = Ремонтирующий гранатомёт +block.surge-wall.name = Стена из кинетического сплава +block.surge-wall-large.name = Большая стена из кинетического сплава +block.cyclone.name = Циклон +block.fuse.name = Взрыватель +block.shock-mine.name = Шоковая мина +block.overdrive-projector.name = Сверхприводный проектор +block.force-projector.name = Силовой проектор +block.arc.name = Дуга +block.rtg-generator.name = Радиоизотопный термоэлектрический генератор +block.spectre.name = Призрак +block.meltdown.name = Катастрофа +block.container.name = Склад +block.launch-pad.name = Стартовая площадка +block.launch-pad.description = Запускает партии предметов без необходимости запуска ядра. Незавершённое. +block.launch-pad-large.name = Большая Стартовая площадка +team.blue.name = Синяя +team.red.name = Красная +team.orange.name = Оранжевая +team.none.name = Серая +team.green.name = Зелёная +team.purple.name = Фиолетовая +unit.spirit.name = Дрон-привидение +unit.spirit.description = Начальный дрон. По умолчанию появляется из ядра. Автоматически добывает руды, собирает предметы, ремонтирует блоки. +unit.phantom.name = Фантомный дрон +unit.phantom.description = Продвинутый дрон. Автоматически добывает руды, собирает предметы, ремонтирует блоки. Значительнее эффективней нежели обычный дрон +unit.dagger.name = Кинджал +unit.dagger.description = Основная наземная боевая единица. Может быть полезен в группах. +unit.crawler.name = Камикадзе +unit.titan.name = Титан +unit.titan.description = Улучшенная бронированная наземная боевая единица. Атакует наземные и воздушные цели. +unit.ghoul.name = Бомбардировщик "Гуль" +unit.ghoul.description = Тяжелый ковровый бомбардировщик. +unit.wraith.name = Призрачный истребитель +unit.wraith.description = Быстрый перехватчик, который использует тактику набега с отходом. +unit.fortress.name = Крепость +unit.fortress.description = Боевая единица с тяжёлой артилерийской установкой. +unit.revenant.name = Потусторонний убийца +unit.eruptor.name = Извергатель +unit.chaos-array.name = Массив хаос +unit.eradicator.name = Искоренитель +unit.lich.name = Лич +unit.reaper.name = Жнец +tutorial.begin = Ваша миссия здесь состоит в том, чтобы уничтожить[LIGHT_GRAY] вашего врага[].\n\nНачните с[accent] добычи меди[]. Чтобы добыть её, коснитесь месторождения медной руды рядом с вашим ядром. +tutorial.drill = Ручная работа не очень эффективна.\n[accent]Буры[] могут копать автоматически.\nПоставьте один на медной жиле. +tutorial.conveyor = [accent]Конвейеры[] используются для транспортировки предметов. \nСоздайте конвейеры от бура к ядру. +tutorial.morecopper = Требуется больше меди.\n\nЛибо добудьте её вручную, либо разместите больше буров. +tutorial.turret = Оборонительные сооружения должны быть созданы, чтобы отразить[LIGHT_GRAY] атаку противника[].Постройте двойную турель рядом с вашей базой. +tutorial.drillturret = Двойные турели требуют[accent] патронов из меди[] для стрельбы.\nРазместите бур рядом с турелью, чтобы снабдить её добытой медью. +tutorial.waves = [LIGHT_GRAY]Враг[] приближается.\n\nЗащитите своё ядро от двух волн. Вам может понадобится больше турелей. +tutorial.lead = Осмотритесь! Магическим образом появились новые руды. Добудьте [accent] свинец[].\n\nПеретащите ресурс из своего устройства(юнита) в ядро для переноса ресурсов. +tutorial.smelter = Медь и свинец являются мягкими металлами.\nПревосходный[accent] графит[] может быть создан в плавильном заводе.\n\nПостройте один плавильный завод. +tutorial.densealloy = Теперь плавильный завод производит графит.\nСоздайте немного.\nУлучшите производство, если это необходимо. +tutorial.siliconsmelter = Сейчас ядро создаст[accent] дрона[] для добычи и ремонта блоков. \n\nЗаводы для других единиц могут быть созданы с помощью кремния.\nСоздайте [accent]кремниевый завод[]. +tutorial.silicondrill = Для производства кремния требуется[accent] уголь[] и[accent] песок[].\nНачните их добычу, сделав буры. +tutorial.generator = Эта технология требует энергии.\nПостройте [accent] генератор внутреннего сгорания[] для того, чтобы запитать устройство энергией. +tutorial.generatordrill = Генератор внутреннего сгорания нуждается в топливе.\nЗаправьте его углём. +tutorial.node = Энергия требует транспортировки.\nСоздайте [accent] силовой узел[] рядом с генератором внутреннего сгорания для передачи энергии. +tutorial.nodelink = Энергия может быть передана посредством соприкосновения блоков питания и генераторов или связанных силовых узлов.\n\nСоедините их, нажав на силовой узел и выбрав генератор, а затем кремниевый завод. +tutorial.silicon = Производство кремния началось. Получите немного.\nРекомендуется улучшить эту систему. +tutorial.daggerfactory = Постройте[accent] завод по производству "кинжалов".[]\n\nОн будет производить атакуюших мехов. +tutorial.router = Заводы нуждаются в ресурсах для работы.\nСоздайте маршрутизатор для разделения ресурсов конвейера. +tutorial.dagger = Соедините силовой узел с заводом.\nПосле выполнения требований будет создан мех. \n\nПри необходимости создайте дополнительные буры, генераторы и конвейеры. +tutorial.battle = [LIGHT_GRAY] Враг[] показал своё ядро.\nУничтожьте его своим мехом и вашой новой боевой единицой. +block.copper-wall.description = Дешевый оборонительный блок.\nПолезно для защиты ядра и турелей во время первых волн. +block.copper-wall-large.description = Большая стена самым маленьким запасом прочности.\n Хороша в начале игры. +block.thorium-wall.description = Стена с показателем прочности "выше среднего". +block.thorium-wall-large.description = Большая стена с показателем прочности "выше среднего". +block.phase-wall.description = Стена со средним показателем прочности . +block.phase-wall-large.description = Большая стена с средним показателем прочности. +block.surge-wall.description = Стена с самым большим запасом прочности. +block.surge-wall-large.description = Большая стена с самым большим запасом прочности. +block.door.description = Дверь в прямом смысле этого слова. Просто нажмите на неё. +block.door-large.description = Для больших стен нужны большие двери.\n Для открытия или закрытия просто нажмите на неё. +block.mend-projector.description = Ремонтирует строения в небольшом радиусе. Потребляет энергию. +block.overdrive-projector.description = Ускоряет в небольшом радиусе все ваши действия. +block.force-projector.description = Создаёт в небольшом радиусе силовое поле, которое защищает от атак противника. +block.shock-mine.description = Поставьте её на землю. Она бьётся ЭЛЕКТРИЧЕСТВОМ О_О +block.duo.description = Маленькая и дешёвая турель. Полезно против наземных юнитов. +block.scatter.description = Противовоздушная турель среднего размера. Распыляет глыбы свинца или металлолома на вражеских боевых единиц. +block.arc.description = Маленькая турель ближнего радиуса действия, которая стреляет электричеством по случайной дуге в направлении врага. +block.hail.description = Дальнобойная начальная турель.\nИспользует в качестве снарядов кремний и пиротит.\nДля ускорения стрельбы можно подвести воду и криогенную жидкость. +block.lancer.description = Турель, которая стреляет лазером на среднее расстояние.\nИспользует в качестве снарядов энергию.\nДля ускорения стрельбы можно подвести воду или криогенную жидкость. +block.wave.description = Турель с средним радиусом атаки, которая отталкивает противников в стороны. Использует в качестве снарядов воду, нефть, лаву или криогенную жидкость. +block.salvo.description = Турель с средним радиусом атаки. Использует в качестве снарядов медь, торий, кремний и пиротит.\nТакже нужна какая-то жидкость: вода или криогенная. +block.swarmer.description = Турель с большим радиусом атаки. Использует в качестве снарядов кинетический сплав, пиротит и взрывоопасное соединение.\n Также нужна какая-то жидкость: вода или криогенная. +block.ripple.description = Первая турель 3х3.\nИспользует в качестве снарядов кремний, взрывоопасное соединение и пиротит. \nТакже нужна какая-то жидкость: вода или криогенная. +block.cyclone.description = Турель с большой дальностью атаки. Использует в качестве снарядов пластиний, взрывоопасное соединение и кинетический сплав.\nТакже нужна какая-то жидкость:вода или криогенная. +block.fuse.description = Турель с малой дальностью атаки. Использует в качестве снарядов графит. \nТакже нужна какая-то жидкость: вода или криогенная. +block.spectre.description = Первая турель 4х4 с средним радиусом атаки. Использует в качестве снарядов торий или пиротит. +block.meltdown.description = Турель с средним радиусом атаки. Для патронов требует воду или криогенную жидкость. Также нужна энергия. +block.conveyor.description = Перемещает ресурсы с маленькой скоростью. +block.titanium-conveyor.description = Конвейер второго поколения. Увеличена скорость перемещения предметов и прочность конвейера. +block.phase-conveyor.description = Пока игра находится в 2D, этот конвейер уже в четырёхмерном пространстве(возможно, это ложь).\nТребует энергии и подключается как мостовой конвейер. +block.junction.description = Название говорит само за себя. С помощью него можно сделать две конвейерные ленты, которые проходят через друг друга и не смешиваются. +block.mass-driver.description = При наличии энергии передают ресурсы на расстояние 100 блоков, стреляя в друг-друга. +block.silicon-smelter.description = С помощью песка, угля и энергии производит кремний. +block.plastanium-compressor.description = Создаёт пластиний из титана и нефти. Требует энергии. Для ускорения производства можно добавить в компрессор песок. +block.phase-weaver.description = Производит фазовую материю из тория и песка. Требует большего количества энергии. +block.alloy-smelter.description = Создаёт кинетический сплав из титана, кремния, меди и свинца. Требует энергию. +block.pulverizer.description = Измельчает металлолом в песок. Требует энергию. +block.pyratite-mixer.description = Создаёт пиротит из угля и свинца. Требует энергии. +block.blast-mixer.description = Создаёт взрывоопасное соединение из нефти и пиротита. Для ускорения производства можно добавить в мешалку песок. +block.cryofluidmixer.description = Производит криогенную жидкость из воды и титана. Требует энергии. +block.melter.description = Расплавляет металлолом в шлак для дальнейшей обработки или использования в турелях. +block.incinerator.description = Если есть ненужные ресурсы, можно просто их сжечь.\n Требует энергии. +block.spore-press.description = Сжимает капсулы со спорами в нефть. +block.separator.description = Извлекает полезные минералы из шлака. +block.power-node.description = Максимум допустимо 4 подключения.\n Чтобы соединить с каким-то блоком нужно следующее:\n1. Чтобы он находился в радиусе действия\n2.Нажать на нужный силовой узел, а затем на другой силовой узел или блок. +block.power-node-large.description = Силовой узел второго поколения. Увеличен радиус действия и количество максимально допустимых подключений. +block.battery.description = Позволяет хранить энергию. +block.battery-large.description = Улучшенная версия обычного аккумулятора. +block.combustion-generator.description = Начальный и дешёвый источник энергии. Для производства энергии использует нефть, уголь, пиротит, биоматерию и взрывоопасное соединение. +block.turbine-generator.description = Для производства энергии использует нефть, уголь, пиротит, биоматерию или взрывоопасное соединение.\nТакже обязательно нужна вода. +block.thermal-generator.description = Генерирует энергию, когда помещается в жаркие места. +block.solar-panel.description = Зелёная энергия. Бесконечный источник энергии. +block.solar-panel-large.description = Зелёная энергия. Большой и бесконечный источник энергии. +block.thorium-reactor.description = Вырабатывает огромное количество энергии из высокорадиоактивного тория. Требует постоянного охлаждения. Взорвётся при недостаточном количестве охлаждающей жидкости. Выходная мощность зависит от наполненности, при этом базовая мощность генерируется на полную мощность. +block.rtg-generator.description = Радиоизотопный термоэлектрический генератор, который не требует охлаждения, но обеспечивает меньшую мощность, чем ториевый реактор. +block.unloader.description = Выгружает из ядра или хранилища верхний левый предмет. +block.container.description = Хранит небольшое количество предметов. Используйте его для создания буферов, когда существует непостоянная потребность в материалах. [LIGHT_GRAY] Разгрузчик[] можно использовать для извлечения элементов из хранилища. +block.vault.description = Хранит большое количество предметов. Используйте его для создания буферов, когда существует непостоянная потребность в материалах.[LIGHT_GRAY] Разгрузчик[] можно использовать для извлечения элементов из хранилища. +block.mechanical-drill.description = Самый первый доступный бур. \n\nДобывает медь, свинец, уголь, песок. \n\nМожно подвести к нему [BLUE] воду[] для увеличения скорости бурения. +block.pneumatic-drill.description = Улучшенная версия механического бура.\n\nДобывает тоже самое, что и механический бур. Также может добывать титан и камень.\n\nМожно подвести к нему[BLUE] воду[] для увеличения скорости бурения. +block.laser-drill.description = Улучшенная версия пневматического бура.\n\nДобывает тоже самое, что и пневматический бур. Также может добывать торий.\n\nМожно подвести к нему[BLUE] воду[] для увеличения скорости бурения. +block.blast-drill.description = Самый мощный бур.\n\nДобывает тоже самое, что и лазерный бур. Добывает быстрей всех буров, но требует ещё больше энергии.\n\nМожно подвести к нему [BLUE]воду[] для увеличения скорости бурения. +block.water-extractor.description = Извлекает воду из земли. Используйте его, когда поблизости нет озера. +block.cultivator.description = Выращивает крошечные скопления спор в готовых к промышленности стручках. +block.oil-extractor.description = Использует большое количество энергии для добычи нефти из песка, динозавров(зачёркнуто). Используйте его, когда поблизости нет прямого источника нефти. +block.trident-ship-pad.description = Оставьте свой текущий корабль и перейдите в достаточно хорошо бронированный тяжёлый бомбардировщик.\nИспользуйте двойное нажатие, стоя на реконструкторе, чтобы превратиться в этот мех. +block.javelin-ship-pad.description = Оставьте свой текущий корабль и перейдите в сильный и быстрый перехватчик с молниеносным оружием.\nИспользуйте двойное нажатие, стоя на реконструкторе, чтобы превратиться в этот мех. +block.glaive-ship-pad.description = Превращает вас в Копьё. Реконструктор требует энергию.\nПодробности про Копьё в "разблокированное". +block.tau-mech-pad.description = Покиньте свой текущий корабль и превратитесь в мех поддержки, который может исцелять дружественные здания и юниты.\nИспользуйте двойное нажатие, стоя на реконструкторе, чтобы превратиться в этот мех. +block.delta-mech-pad.description = Оставьте свой текущий корабль и перейдите в большой, хорошо бронированный боевой корабль.\nИспользуйте двойное нажатие, стоя на реконструкторе, чтобы превратиться в этот мех. +block.omega-mech-pad.description = Оставьте свой текущий корабль и превратите его в громоздкий и хорошо бронированный мех, сделанный для фронтовых нападений. \nИспользуйте двойное нажатие, стоя на реконструкторе, чтобы превратиться в этот мех. +block.spirit-factory.description = Производит легкие дроны, которые добывают руду(медную и свинцовую) и ремонтирует блоки. +block.phantom-factory.description = Производит усовершенствованные единицы, которые значительно эффективнее, чем дрон-привидение. +block.wraith-factory.description = Производит быстрые и летающие боевые единицы. +block.ghoul-factory.description = Производит тяжёлых ковровых бомбардировщиков. +block.dagger-factory.description = Производит основных наземных боевых единиц. +block.titan-factory.description = Производит продвинутые защищённые боевые единицы. +block.fortress-factory.description = Производит тяжёлые артиллерийские боевые единицы. +block.revenant-factory.description = Производит тяжёлые летающие боевые единицы. +block.repair-point.description = Может ремонтировать вас и ваши боевые единицы +block.conduit.description = Основной блок транспортировки жидкости. Работает как конвейер, но с жидкостями. +block.pulse-conduit.description = Улучшенный блок транспортировки жидкости. Транспортирует жидкости быстрее и хранит больше, чем стандартные. +block.phase-conduit.description = Улучшенный блок транспортировки жидкости. Использует энергию для передачи жидкостей на подключенный фазовый канал на несколько плиток. +block.liquid-router.description = Принимает жидкости с одного направления и равномерно выводит их до трех других направлений. Может также хранить определенное количество жидкости. Полезно для разделения жидкостей из одного источника на несколько целей. +block.liquid-tank.description = Хранит большое количество жидкостей. Используйте его для создания буферов, когда существует непостоянная потребность в материалах или в качестве защиты для охлаждения жизненно важных блоков. +block.liquid-junction.description = Действует как мост для двух пересекающихся каналов. Полезно в ситуациях с двумя различными каналами, перевозящими различные жидкости в разные места. +block.bridge-conduit.description = Расширенный блок транспортировки жидкости. Позволяет транспортировать жидкости до 3 блоков любого ландшафта или здания. Лучше всего подключать последовательно и в линию. +block.mechanical-pump.description = Дешевый насос с медленным выкачиванием, но без потребления энергии. Качает только воду. +block.rotary-pump.description = Передовой насос, который удваивает скорость, используя энергию. +block.thermal-pump.description = Последний насос. +block.router.description = Принимает предметы из одного направления и равномерно выводит их до трёх других направлений. Полезно для разделения материалов из одного источника на несколько целей. +block.distributor.description = Передовой маршрутизатор, который равномерно разбивает элементы до 7 других направлений. +block.bridge-conveyor.description = Улучшенный транспортный блок элементов. Позволяет транспортировать предметы до 3-х блоков над любым ландшафтом или зданием. +block.item-source.description = Бесконечно выводит предметы. +block.liquid-source.description = Бесконечно выводит жидкости. +block.item-void.description = Уничтожает любые предметы, которые входят в него, не используя енергію. +block.power-source.description = Бесконечно выводит энергию. +block.power-void.description = Аннулирует всю энергию, введённую в него. +liquid.water.description = Эту жидкость можно подвести к бурам для ускорения скорости добычи или к турелям для ускорения стрельбы. +liquid.oil.description = Может гореть, взрывоопасна и используется для охлаждения. +liquid.cryofluid.description = Может быть использована для ускорения стрельбы турелей или для охлаждения чего-то. diff --git a/core/assets/bundles/bundle_tk.properties b/core/assets/bundles/bundle_tk.properties index 814f36c6e0..03a0ec0d8e 100644 --- a/core/assets/bundles/bundle_tk.properties +++ b/core/assets/bundles/bundle_tk.properties @@ -1,551 +1,947 @@ -text.about = [ROYAL] Anuken tarafından oluşturuldu [] - [SKY] anukendev@gmail.com [] Aslen [turuncu] GDL [] Metal Monstrosity Jam. Kredi: - [SARI] ile yapılan SFX bfxr [] - [YEŞİL] RoccoW tarafından yapılan müzik [] / [kireç] bulunan FreeMusicArchive.org [] Özel teşekkürler: - [mercan] MitchellFJN []: Kapsamlı oyun testi ve geri bildirim - [sky] Luxray5474 []: wiki çalışması, kod katkıları - [kireç] Epowerj []: kod sistemi yapılandırması, icon - itch.io ve Google Play'deki tüm beta test kullanıcıları\n -text.credits = Yapımcılar -text.discord = Mindustry Discord'una katılın! -text.changes = [SCARLET] Dikkat! [] Bazı önemli oyun mekanikleri değişti. - [Aksanın] teletaşıyıcı [] şimdi artık gücü kullanıyor. - [accent] dökümcü [] ve [accent] pota [] artık bir maksimum ürün kapasitesine sahip. - [Aksan] pota [] artık yakıt olarak kömür gerektiriyor. -text.link.discord.description = Resmi Mindustry Discord iletişim kanalı -text.link.github.description = Oyunun kaynak kodu -text.link.dev-builds.description = Geliştirme altında olan sürüm -text.link.trello.description = Planlanan özellikler için resmi Trello Bülteni -text.link.itch.io.description = PC yüklemeleri ve web sürümü ile itch.io sayfası -text.link.google-play.description = Google Play mağaza sayfası -text.link.wiki.description = Resmi Mindustry Wikipedi'si -text.linkfail = Bağlantı açılamadı! URL, yazı tahtanıza kopyalandı. -text.editor.web = Web sürümü editörü desteklemiyor! Editörü kullanmak için oyunu indirin. -text.multiplayer.web = Oyunun bu sürümü çok oyunculuyu desteklemiyor! Tarayıcınızdan çok oyunculu oynamak için, itch.io sayfasındaki \"çok oyunculu web sürümü\" bağlantısını kullanın. -text.gameover = Çekirdek yok edildi. -text.highscore = [SARI] Yeni yüksek puan! -text.lasted = Dalgaya kadar sürdün -text.level.highscore = Yüksek Puan: [accent] {0} -text.level.delete.title = Silmeyi onaylayın -text.level.delete = \"[Orange] {0} \" Haritayı silmek istediğinizden emin misiniz? -text.level.select = Seviye Seç -text.level.mode = Oyun Modu -text.savegame = Oyunu Kaydet -text.loadgame = Oyunu yükle -text.joingame = Oyuna katıl -text.newgame = Yeni Oyun -text.quit = Çık -text.about.button = Hakkında -text.name = Adı: -text.public = Herkese açık -text.players = 1090 oyuncu çevrimiçi -text.server.player.host = Sunucu -text.players.single = {0} Oyuncu Çevrimiçi -text.server.mismatch = Paket hatası: olası istemci / sunucu sürümü uyuşmazlığı. Siz ve ev sahibi Mindustry'nin en son sürümüne sahip olduğunuzdan emin olun! -text.server.closing = [accent] Sunucu kapatılıyor ... -text.server.kicked.kick = Sunucudan kovuldun! -text.server.kicked.invalidPassword = Geçersiz şifre! -text.server.kicked.clientOutdated = Oyun sürümünüz geçerli değil. Oyununu güncelleyin! -text.server.kicked.serverOutdated = Eski sunucu! Ev sahibinden güncellemesini isteyin! -text.server.kicked.banned = Bu sunucudan yasaklandınız. -text.server.kicked.recentKick = Son zamanlarda tekmelendin. Tekrar bağlanmadan önce bekleyin. -text.server.connected = {0} katıldı. -text.server.disconnected = {0} bağlantısı kesildi. -text.nohost = Özel bir haritada sunucuyu barındıramıyor! -text.host.info = [Vurgu] ana bilgisayarı [] düğmesi, [657] [65] [65] ve [65] [6568] bağlantı noktalarında bir sunucuyu barındırır. [] Aynı [LIGHT_GRAY] wifi veya yerel ağ [] üzerindeki herkes sunucunuzu sunucularında görebilir. liste. Kişilerin IP tarafından herhangi bir yerden bağlanabilmesini istiyorsanız [vurgu] bağlantı noktası iletme [] gereklidir. [LIGHT_GRAY] Not: Birisi LAN oyununuza bağlanırken sorun yaşıyorsa, güvenlik duvarı ayarlarınızda Mindustry'e yerel ağınıza erişebildiğinizden emin olun. -text.join.info = Burada, bağlanmak için yerel ağ [] sunucularına bağlanmak ya da [aksan] sunucularını bulmak için bir [vurgu] sunucunun IP [] girebilirsiniz. Hem LAN hem de WAN çok oyunculu desteklenir. [LIGHT_GRAY] Not: Otomatik bir global sunucu listesi yoktur; Birisine IP ile bağlanmak isterseniz, ana bilgisayardan kendi IP adreslerini sormanız gerekir. -text.hostserver = Oyunu Sun -text.host = evsahibi -text.hosting = [accent] Sunucu açılıyor ... -text.hosts.refresh = Yenile -text.hosts.discovering = LAN oyunlarını keşfetme -text.server.refreshing = Canlandırıcı sunucu -text.hosts.none = [lightgray] Hayır LAN oyunları bulundu! -text.host.invalid = [scarlet] Ana bilgisayara bağlanılamıyor. -text.server.friendlyfire = Dost ateşi -text.trace = Oyuncuyu Takip Et -text.trace.playername = Oyuncu adı: [accent] {0} -text.trace.ip = IP: [vurgu] {0} -text.trace.id = Benzersiz kimlik: [accent] {0} -text.trace.android = Android : [accent] {0} -text.trace.modclient = Özel Alıcı: [accent] {0} -text.trace.totalblocksbroken = Toplam kırık blok: [accent] {0} -text.trace.structureblocksbroken = Kırılan yapı blokları: [accent] {0} -text.trace.lastblockbroken = Kırılan son blok: [accent] {0} -text.trace.totalblocksplaced = Toplam blok yerleştirildi: [accent] {0} -text.trace.lastblockplaced = Konulan son blok: [accent] {0} -text.invalidid = Geçersiz alıcı kimliği! Bir hata raporu gönderin. -text.server.bans = yasaklar -text.server.bans.none = Yasaklanmış oyuncu bulunamadı! -text.server.admins = Yöneticiler -text.server.admins.none = Yönetici bulunamadı! -text.server.add = Sunucu ekle -text.server.delete = Bu sunucuyu silmek istediğinizden emin misiniz? -text.server.hostname = Sun -text.server.edit = Sunucuyu Düzenle -text.server.outdated = [crimson] Eski Sunucu! -text.server.outdated.client = [crimson] Eski Alıcı! -text.server.version = [lightgray] Sürüm: {0} -text.server.custombuild = [sarı] Özel Yapım -text.confirmban = Bu oyuncuyu yasaklamak istediğinizden emin misiniz? -text.confirmunban = Bu oyuncunun yasağını kaldırmak istediğinden emin misin? -text.confirmadmin = Bu oyuncunun yönetici yapmak istediğinden emin misin? -text.confirmunadmin = Bu oyuncudan yönetici durumunu kaldırmak istediğinizden emin misiniz? -text.joingame.byip = IP ile Katılın ... -text.joingame.title = Oyuna katılmak -text.joingame.ip = IP: -text.disconnect = Bağlantı Kesildi -text.disconnect.data = Dünya verileri yüklenemedi! -text.connecting = [Vurgu] bağlanıyor ... -text.connecting.data = [accent] Dünya verileri yükleniyor ... -text.connectfail = [crimson] Sunucuya bağlanılamadı: [orange] {0} -text.server.port = Liman -text.server.addressinuse = Adres çoktan kullanımda! -text.server.invalidport = Bağlantı noktası numarası geçersiz. -text.server.error = [crimson] Sunucu barındırma hatası: [orange] {0} -text.tutorial.back = <Önceki -text.tutorial.next = İleri > -text.save.new = 6349,Yeni Kayıt -text.save.overwrite = Bu kayıt yuvasının üzerine yazmak istediğinizden emin misiniz? -text.overwrite = Üzerine Yaz -text.save.none = Hiçbir kayıt bulunamadı! -text.saveload = [Vurgu] Kaydediliyor ... -text.savefail = Oyun kaydedilemedi! -text.save.delete.confirm = Bu kaydı silmek istediğinizden emin misiniz? -text.save.delete = Sil -text.save.export = Dışa Aktar -text.save.import.invalid = [turuncu] Bu kayıt geçersiz! -text.save.import.fail = [crimson] Kayıt oyuna aktarılamadı : [orange] {0} -text.save.export.fail = [crimson] Kayıt dışa aktarılamadı: [orange] {0} -text.save.import = İçe Aktar -text.save.newslot = İsmi kaydet: -text.save.rename = Yeniden Adlandır -text.save.rename.text = Yeni İsim: -text.selectslot = Bir kayıt seçin. -text.slot = [accent] Yuva {0} -text.save.corrupted = [orange] Kayıt dosyası bozuk veya geçersiz! -text.empty = -text.on = Açık -text.off = Kapalı -text.save.autosave = Otomatik kaydetme: {0} -text.save.map = harita -text.save.wave = Dalga -text.save.difficulty = zorluk -text.save.date = Son Kaydedilen: {0} -text.confirm = Onayla -text.delete = Sil -text.ok = Tamam -text.open = Açık -text.cancel = İptal -text.openlink = Linki aç -text.copylink = Bağlantıyı kopyala -text.back = Geri -text.quit.confirm = Çıkmak istediğinden emin misin? -text.changelog.title = Değişiklik listesi -text.changelog.loading = Değişiklik listesi yükleniyor -text.changelog.error.android = [turuncu] Android'da olan hata nedeniyle değişiklik listesi görüntülenemiyor. -text.changelog.error = [scarlet] Değişiklik listesi alma hatası! İnternet bağlantınızı kontrol edin. -text.changelog.current = [sarı] [[Güncel versiyon] -text.changelog.latest = [turuncu] [[Son sürüm] -text.loading = [Vurgu] Yükleniyor ... -text.wave = [turuncu] Dalga {0} -text.wave.waiting = {0} içinde dalga -text.waiting = Bekleniyor -text.enemies = {0} Düşmanlar -text.enemies.single = {0} Düşman -text.loadimage = Resmi yükle -text.saveimage = Resmi Kaydet -text.oregen = Maden Üretimi -text.editor.badsize = [orange] Resim boyutları geçersiz! [] Geçerli harita boyutları: {0} -text.editor.errorimageload = Resim dosyası yüklenirken hata oluştu: [orange] {0} -text.editor.errorimagesave = Resim dosyası kaydedilirken hata oluştu: [orange] {0} -text.editor.generate = Üretmek -text.editor.resize = Yeniden Boyutlandırma -text.editor.loadmap = Harita Yükle -text.editor.savemap = Harita Kaydet -text.editor.loadimage = Resmi yükle -text.editor.saveimage = Resmi Kaydet -text.editor.unsaved = [scarlet] Kaydedilmemiş değişiklikleriniz var! [] Çıkmak istediğinizden emin misiniz? -text.editor.brushsize = Fırça boyutu: {0} -text.editor.noplayerspawn = Bu haritanın oyuncu spawnpoint'i yok! -text.editor.manyplayerspawns = Haritalar, birden fazla oyuncu spawnpoint'e sahip olamaz! -text.editor.manyenemyspawns = {0} düşman spawnpoint {0}'den daha fazlası olamaz! -text.editor.resizemap = Haritayı Yeniden Boyutlandır -text.editor.resizebig = [Kızıl] Uyarı! [] 256'dan büyük haritalar yavaş ve dengesiz olabilir. -text.editor.mapname = Harita Adı -text.editor.overwrite = [Vurgu] Uyarı! Bu mevcut bir haritanın üzerine yazar. -text.editor.failoverwrite = [crimson] Varsayılan haritanın üzerine yazılamıyor! -text.editor.selectmap = Yüklenecek bir harita seçin: -text.width = Genişliği: -text.height = Boy: -text.randomize = Rasgele seçmek -text.apply = Uygula -text.update = Güncelle -text.menu = Menü -text.play = Oyna -text.load = Yükle -text.save = Kaydet -text.language.restart = Lütfen dil ayarlarının etkili olması için oyununuzu yeniden başlatın. -text.settings.language = Dil -text.settings = Ayarlar -text.tutorial = Eğitim -text.editor = Editör -text.mapeditor = Harita Editörü -text.donate = Bağışlamak -text.settings.reset = Varsayılanlara Dön -text.settings.controls = kontroller -text.settings.game = Oyun -text.settings.sound = Ses -text.settings.graphics = Grafik -text.upgrades = Geliştirmeler -text.purchased = [KİREÇ] Yap၊ld၊ -text.weapons = Silahlar -text.paused = Duraklatıldı -text.respawn = Saniye içinde yeniden doğacaksınız. -text.info.title = [Vurgu] Bilgi -text.error.title = [crimson] Bir hata oluştu -text.error.crashmessage = [SCARLET] Bir kilitlenme meydana getiren beklenmeyen bir hata oluştu. [] Lütfen geliştiriciye bu hatanın gerçekleştiği koşulları bildirin: [ORANGE] anukendev@gmail.com [] -text.error.crashtitle = Bir hata oluştu -text.mode.break = Ara verme modu: {0} -text.mode.place = Döşeme modu: {0} -placemode.hold.name = hat -placemode.areadelete.name = alan -placemode.touchdelete.name = dokun -placemode.holddelete.name = tut -placemode.none.name = Yok -placemode.touch.name = dokun -placemode.cursor.name = İmleç -text.blocks.extrainfo = [accent] fazladan blok bilgisi: -text.blocks.blockinfo = Blok Bilgisi -text.blocks.powercapacity = Güç kapasitesi -text.blocks.powershot = Güç / atış -text.blocks.powersecond = Güç / saniye -text.blocks.powerdraindamage = Güç tahliye / hasar -text.blocks.shieldradius = Kalkan Yarıçapı -text.blocks.itemspeedsecond = Ürün Hız / saniye -text.blocks.range = Menzil -text.blocks.size = Boyut -text.blocks.powerliquid = Güç / Sıvı -text.blocks.maxliquidsecond = Maksimum sıvı / saniye -text.blocks.liquidcapacity = Sıvı kapasitesi -text.blocks.liquidsecond = Sıvı / saniye -text.blocks.damageshot = Zarar / atış -text.blocks.ammocapacity = Mermi kapasitesi -text.blocks.ammo = Cephane: -text.blocks.ammoitem = Cephane / öğe -text.blocks.maxitemssecond = Maksimum öğe / saniye -text.blocks.powerrange = Güç aralığı -text.blocks.lasertilerange = Lazer karo aralığı -text.blocks.capacity = Kapasite -text.blocks.itemcapacity = Ürün kapasitesi -text.blocks.maxpowergenerationsecond = Maksimum Güç Üretimi / saniye -text.blocks.powergenerationsecond = Güç Üretimi / saniye -text.blocks.generationsecondsitem = Nesil Saniye / öğe -text.blocks.input = giriş -text.blocks.inputliquid = Giriş sıvı -text.blocks.inputitem = Giriş öğesi -text.blocks.output = Çıktı -text.blocks.secondsitem = Saniye / öğe -text.blocks.maxpowertransfersecond = Maksimum güç aktarımı / saniye -text.blocks.explosive = Çok patlayıcı! -text.blocks.repairssecond = Tamir / saniye -text.blocks.health = Can -text.blocks.inaccuracy = yanlışlık -text.blocks.shots = atışlar -text.blocks.shotssecond = Çekim / saniye -text.blocks.fuel = Yakıt -text.blocks.fuelduration = Yakıt Süresi -text.blocks.maxoutputsecond = Maksimum çıkış / saniye -text.blocks.inputcapacity = Giriş kapasitesi -text.blocks.outputcapacity = Çıkış kapasitesi -text.blocks.poweritem = Güç / Ürün -text.placemode = Yer Modu -text.breakmode = Mola modu -text.health = sağlık +credits.text = Created by [ROYAL]Anuken[] - [SKY]anukendev@gmail.com[]\n\n[GRAY](In case you can't tell, this text is currently unfinished.\nTranslators, don't edit it yet!) +credits = Emegi gecenler +contributors = Translators and Contributors +discord = Mindustry'in Discord'una katilin! +link.discord.description = Orjinal Mindustry'in Discord Konusma Odasi +link.github.description = Oyunun Kodu +link.dev-builds.description = Bitirilmemis Yapim Surumu +link.trello.description = Planlanmis Hersey icin Tablo +link.itch.io.description = Bilgisayar ve Site versiyonunun bulundugu Site +link.google-play.description = Google Play magaza sayfasi +link.wiki.description = Orjinal Mindustry Bilgilendirme Sayfasi +linkfail = Link Acilamadi!\nLink sizin icin kopyalandi. +screenshot = Screenshot saved to {0} +screenshot.invalid = Map too large, potentially not enough memory for screenshot. +gameover = Cekirdegin yok edildi. +gameover.pvp = The[accent] {0}[] team is victorious! +highscore = [accent]Yeni Yuksek skor! +stat.wave = Waves Defeated:[accent] {0} +stat.enemiesDestroyed = Enemies Destroyed:[accent] {0} +stat.built = Buildings Built:[accent] {0} +stat.destroyed = Buildings Destroyed:[accent] {0} +stat.deconstructed = Buildings Deconstructed:[accent] {0} +stat.delivered = Resources Launched: +stat.rank = Final Rank: [accent]{0} +placeline = You have selected a block.\nYou can[accent] place in a line[] by[accent] holding down your finger for a few seconds[] and dragging in a direction.\nTry it. +removearea = You have selected removal mode.\nYou can[accent] remove blocks in a rectangle[] by[accent] holding down your finger for a few seconds[] and dragging.\nTry it. +launcheditems = [accent]Launched Items +map.delete = Su haritayi silmek istediginden emin misin? "[accent]{0}[]"? +level.highscore = Yuksek Skor: [accent]{0} +level.select = Seviye secimi +level.mode = Oyun Modu: +showagain = Don't show again next session +coreattack = < Cekirdek Saldiri altinda! > +nearpoint = [[ [scarlet]LEAVE DROP POINT IMMEDIATELY[] ]\nannihilation imminent +outofbounds = [[ OUT OF BOUNDS ]\n[]self-destruct in {0} +database = Core Database +savegame = Oyunu kaydet +loadgame = Devam et +joingame = Oyuna katil +addplayers = Oyuncu ekle/cikar +customgame = Ozel oyun +newgame = New Game +none = +minimap = Minimap +close = Kapat +quit = Cik +maps = Haritalar +continue = Devam et +maps.none = [LIGHT_GRAY]Harita bulunamadi! +about.button = Hakkinda +name = isim: +noname = Pick a[accent] player name[] first. +filename = File Name: +unlocked = Yeni yapi acildi!! +completed = [accent]Completed +techtree = Tech Tree +research.list = [LIGHT_GRAY]Research: +research = Research +researched = [LIGHT_GRAY]{0} researched. +players = {0} oyuncu cevrimici +players.single = {0} oyuncu cevrimici +server.closing = [accent]Oyun kapaniyor. +server.kicked.kick = Oyundan cikarildin +server.kicked.serverClose = Oyun kapandi +server.kicked.clientOutdated = Yeni bir versiyon mevcut! Hemen indir! +server.kicked.serverOutdated = Eski oyun! Yapimciya guncellemesini soyle! +server.kicked.banned = Oyundan kalici olarak cikarildin. +server.kicked.recentKick = Oyundan cikarilmistin.\nBaglanmadn once biraz bekle. +server.kicked.nameInUse = Oyunda bu isimde bir\nkisi zaten var. +server.kicked.nameEmpty = ismin gecerli degil. +server.kicked.idInUse = Zaten oyundasin! iki ayri hesapla oyuna katilamazsin! +server.kicked.customClient = Bu oyun ayarlanmis vesiyonlara izin vermiyor. Orijinal bir versiyon dene! +server.kicked.gameover = Game over! +host.info = [accent]host[] su linkte bir oyun acti! [scarlet]6567[]. \nSeninle [LIGHT_GRAY]ayni internete[] sahip olan kisiler oyunu gorebilir.\n\neger baska yerlerden kisilerind de gelmesini istiyorsan, [accent]oyun acmak[]zorunludur.\n\n[LIGHT_GRAY]Not: eger baglanmakta gucluk cekiliyorsa, antivirusunun internetine baglanmasini izin vermesini sagla. +join.info = Buradan,[accent]Oyunun linkini[] kullanarak katilabilir, yada, [accent]internetinle[] baglanacak oyun bulabilirsin\ninternetli ve Linkli oyunlar desteklenir.\n\n[LIGHT_GRAY]Not: Otomatik bir oyun listesi goruntulenemez. Yapimcidan linkini iste. +hostserver = Oyun ac +hostserver.mobile = Host\nGame +host = Oyun ac +hosting = [accent]Oyun aciliyor +hosts.refresh = Yenile +hosts.discovering = internet oyunu araniyor +server.refreshing = liste yenileniyor +hosts.none = [lightgray]internet oyunu bulunamadi! +host.invalid = [scarlet]Oyuna baglanilamadi. +trace = Oyuncu isaretle +trace.playername = Player name: [accent]{0} +trace.ip = IP: [accent]{0} +trace.id = Unik ID: [accent]{0} +trace.mobile = Mobile Client: [accent]{0} +trace.modclient = Ozel islemci Kullanicisi: [accent]{0} +invalidid = Yanlis islemci Linki! Sorunu bildir +server.bans = Yasaklamalar +server.bans.none = Yasaklananlar bulunamadi! +server.admins = Yetkililer +server.admins.none = Yetkili bulunamadi! +server.add = Oyun ekle +server.delete = Oyunu silmek istedigine emin misin? +server.hostname = Oyun yarat: {0} +server.edit = Oyunu ayarla +server.outdated = [crimson]Eski Oyun![] +server.outdated.client = [crimson]eski islemci![] +server.version = [lightgray]Versiyon: {0} +server.custombuild = [yellow]ozel yapi +confirmban = Bu oyuncuyu kalici olarak atmak istedigine emin misin? +confirmkick = Are you sure you want to kick this player? +confirmunban = Bu oyuncunun yasagini geri almak ister misin? +confirmadmin = Bu oyuncuyu yetkili yapmak istedigine emin misin? +confirmunadmin = Bu oyuncunun yetkisini almak istedigine emin misin? +joingame.title = Oyuna katil +joingame.ip = Link: +disconnect = Cikildi +disconnect.data = Oyunun geri yuklenemedi! +connecting = [accent]Baglaniliyor +connecting.data = [accent]Loading world data... +server.port = Link: +server.addressinuse = Addres zaten kullaniliyor! +server.invalidport = Geçersiz Oyun numarasi! +server.error = [crimson]Oyun acarkes sorun olustu: [accent]{0} +save.old = Bu oyun su anda kullanilamaz.\n\n[LIGHT_GRAY]geri alma oyunun 4.0 surumunde eklenecektir. +save.new = Yeni Kayit Dosyasi +save.overwrite = Bu oyunun uzerinden\ngecmek istedigine emin\nmisin? +overwrite = uzerinden gec +save.none = Kayitli oyun bulunamadi +saveload = [accent]Kaydediliyor... +savefail = Kaydedilemedi! +save.delete.confirm = Bu Kayiti silmek istedigine emin misin? +save.delete = Sil +save.export = Kayiti tasi +save.import.invalid = [accent]Kayit gecersiz! +save.import.fail = [crimson]Failed to import save: [accent]{0} +save.export.fail = [crimson]Failed to export save: [accent]{0} +save.import = Kayit al +save.newslot = Kayit ismi: +save.rename = Yeniden adlandir +save.rename.text = Yeni isim: +selectslot = Kayitli bir dosya sec. +slot = [accent]Slot {0} +save.corrupted = [accent]Kayit gecersiz!\nOyunu guncellediysen, bu buyuk ihtimalle degistirilecek vebu bir [scarlet]sorun degildir.[] +empty = +on = Acik +off = Kapali +save.autosave = Otomatik kayit: {0} +save.map = Harita: {0} +save.wave = Dalga {0} +save.difficulty = Zorluk: {0} +save.date = En sonki kayit: {0} +save.playtime = Oyun Zamani!: {0} +warning = Warning. +confirm = Onayla +delete = Sil +ok = Tamam +open = Ac +customize = Customize +cancel = iptal +openlink = Linki ac +copylink = Linki kopyala +back = Geri don +quit.confirm = Cikmak istedigine emin misin? +changelog.title = Degisimler +changelog.loading = Degisimler yukleniyor... +changelog.error.android = [accent]Not: Degisimler bazen androidde calismaz.\nBu bir degistirilemez sorundan kaynakli. +changelog.error.ios = [accent]Degisimler IOS'da su anda desteklenmiyor. +changelog.error = [scarlet]Degisimler alinamadi.\nInternet baglantini kontrol et +changelog.current = [yellow][[Current version] +changelog.latest = [accent][[Latest version] +loading = [accent]Yukleniyor... +saving = [accent]Kaydediliyor... +wave = [accent]Dalga {0} +wave.waiting = Dalganin baslamasina: {0} +wave.waveInProgress = [LIGHT_GRAY]Wave in progress +waiting = Bekleniyor... +waiting.players = Waiting for players... +wave.enemies = [LIGHT_GRAY]{0} Enemies Remaining +wave.enemy = [LIGHT_GRAY]{0} Enemy Remaining +loadimage = Resimden Yukle +saveimage = Resimi kaydet +unknown = Bilinmeyen +custom = Ozel +builtin = Yapilandirilmis +map.delete.confirm = Haritayi silmek istedigine emin misin? Bu geri alinamaz! +map.random = [accent]Rasgele harita +map.nospawn = Haritada Oyncularin cikmasi icin cekirdek yok! Haritaya[ROYAL]Mavi[] cekirdek ekle. +map.nospawn.pvp = This map does not have any enemy cores for player to spawn into! Add[SCARLET] red[] cores to this map in the editor. +map.nospawn.attack = This map does not have any enemy cores for player to attack! Add[SCARLET] red[] cores to this map in the editor. +map.invalid = Harita yuklenemedi. Gecersiz yada bozuk dosya. +editor.brush = Firca +editor.openin = Editorde ac +editor.oregen = Maden Yaratilma hizi +editor.oregen.info = Maden Yaratilmasi: +editor.mapinfo = Harita bilgisi: +editor.author = Yapimci: +editor.description = Yorum: +editor.waves = Waves: +editor.rules = Rules: +editor.ingame = Edit In-Game +waves.title = Waves +waves.remove = Remove +waves.never = +waves.every = every +waves.waves = wave(s) +waves.perspawn = per spawn +waves.to = to +waves.boss = Boss +waves.preview = Preview +waves.edit = Edit... +waves.copy = Copy to Clipboard +waves.load = Load from Clipboard +waves.invalid = Invalid waves in clipboard. +waves.copied = Waves copied. +editor.default = [LIGHT_GRAY] +edit = Edit... +editor.name = isim: +editor.spawn = Spawn Unit +editor.removeunit = Remove Unit +editor.teams = Takimlar +editor.elevation = Yukseklik +editor.errorload = Error loading file:\n[accent]{0} +editor.errorsave = Error saving file:\n[accent]{0} +editor.errorimage = That's an image, not a map. Don't go around changing extensions expecting it to work.\n\nIf you want to import a legacy map, use the 'import legacy map' button in the editor. +editor.errorlegacy = This map is too old, and uses a legacy map format that is no longer supported. +editor.errorheader = This map file is either not valid or corrupt. +editor.errorname = Map has no name defined. +editor.update = Update +editor.randomize = Randomize +editor.apply = Apply +editor.generate = Yarat +editor.resize = Boyutunu degistir +editor.loadmap = Harita yukle +editor.savemap = Haritayi kaydet +editor.saved = Kaydedildi! +editor.save.noname = Haritanin ismi yok! 'Harita bilgisinden' bi tane ekle +editor.save.overwrite = Haritanin ismi varolan bir haritanin ismi ile ayni! 'Harita bilgisinden' degisik bir isim sec +editor.import.exists = [scarlet]Tasinamadi.:[] ayni isimi tasiyan bir harita '{0}' zaten var!! +editor.import = Bilgisayara kaydet +editor.importmap = Bilgisayardan harita al +editor.importmap.description = Varolan bir harita al +editor.importfile = Dosyayi bilgisayara kaydet +editor.importfile.description = Bilgisayara ozel harita yukle +editor.importimage = Yuzey resmi al +editor.importimage.description = Degisik Yuzey resmi al +editor.export = Disari al +editor.exportfile = Disari cikart +editor.exportfile.description = Haritayi disari cikart +editor.exportimage = Haritanin resmini disari cikart +editor.exportimage.description = Haritanin resmini disari aktar +editor.loadimage = Araziyi yukle +editor.saveimage = Araziyi disari aktar +editor.unsaved = [scarlet]Kaydedilmemis verileriniz var![]\ncikmak istedigine emin misin? +editor.resizemap = Haritanin boyutunu degistir +editor.mapname = Harita ismi: +editor.overwrite = [accent]Dikkat et!\nBu bir haritanin uzerinden cececek. +editor.overwrite.confirm = [scarlet]uyari![] bu isimde bir harita zaten var. Uzerinden gececek misin? +editor.selectmap = Yukleyecek bir harita sec: +filters.empty = [LIGHT_GRAY]No filters! Add one with the button below. +filter.distort = Distort +filter.noise = Noise +filter.ore = Ore +filter.rivernoise = River Noise +filter.scatter = Scatter +filter.terrain = Terrain +filter.option.scale = Scale +filter.option.chance = Chance +filter.option.mag = Magnitude +filter.option.threshold = Threshold +filter.option.circle-scale = Circle Scale +filter.option.octaves = Octaves +filter.option.falloff = Falloff +filter.option.block = Block +filter.option.floor = Floor +filter.option.wall = Wall +filter.option.ore = Ore +filter.option.floor2 = Secondary Floor +filter.option.threshold2 = Secondary Threshold +filter.option.radius = Radius +filter.option.percentile = Percentile +width = Genislik: +height = Yukseklik: +menu = Menu +play = Oyna +load = Yukle +save = Kaydet +fps = FPS: {0} +tps = TPS: {0} +ping = Ping: {0}ms +language.restart = Lutfen dil degisiminin etkin olmasi icin oyunu yeniden baslatin +settings = ayarlar +tutorial = Tutorial +editor = Editor +mapeditor = Harita yaraticisi +donate = Bagis yap +abandon = Abandon +abandon.text = This zone and all its resources will be lost to the enemy. +locked = Locked +complete = [LIGHT_GRAY]Complete: +zone.requirement = Wave {0} in zone {1} +resume = Resume Zone:\n[LIGHT_GRAY]{0} +bestwave = [LIGHT_GRAY]Best: {0} +launch = Launch +launch.title = Launch Successful +launch.next = [LIGHT_GRAY]next opportunity at wave {0} +launch.unable = [scarlet]Unable to LAUNCH.[] Enemies. +launch.confirm = This will launch all resources in your core.\nYou will not be able to return to this base. +uncover = Uncover +configure = Configure Loadout +configure.locked = [LIGHT_GRAY]Reach wave {0}\nto configure loadout. +zone.unlocked = [LIGHT_GRAY]{0} unlocked. +zone.requirement.complete = Wave {0} reached:\n{1} zone requirements met. +zone.config.complete = Wave {0} reached:\nLoadout config unlocked. +zone.resources = Resources Detected: +add = Add... +boss.health = Boss Health +connectfail = [crimson]Su Oyuna baglanilamadi: [accent]{0} +error.unreachable = Server unreachable. +error.invalidaddress = Invalid address. +error.timedout = Timed out!\nMake sure the host has port forwarding set up, and that the address is correct! +error.mismatch = Packet error:\npossible client/server version mismatch.\nMake sure you and the host have the latest version of Mindustry! +error.alreadyconnected = Already connected. +error.mapnotfound = Map file not found! +error.io = Network I/O error. +error.any = Unkown network error. +zone.groundZero.name = Ground Zero +zone.desertWastes.name = Desert Wastes +zone.craters.name = The Craters +zone.frozenForest.name = Frozen Forest +zone.ruinousShores.name = Ruinous Shores +zone.stainedMountains.name = Stained Mountains +zone.desolateRift.name = Desolate Rift +zone.nuclearComplex.name = Nuclear Production Complex +zone.overgrowth.name = Overgrowth +zone.tarFields.name = Tar Fields +settings.language = Dil +settings.reset = ilk ayarlara geri al +settings.rebind = Geri al +settings.controls = Kontroller +settings.game = Oyun +settings.sound = Ses +settings.graphics = Grafikler +settings.cleardata = Clear Game Data... +settings.clear.confirm = Are you sure you want to clear this data?\nWhat is done cannot be undone! +settings.clearall.confirm = [scarlet]WARNING![]\nThis will clear all data, including saves, maps, unlocks and keybinds.\nOnce you press 'ok' the game will wipe all data and automatically exit. +settings.clearunlocks = Clear Unlocks +settings.clearall = Clear All +paused = Duraklatildi +yes = Evet +no = Hayir +info.title = [accent]Bilgi +error.title = [crimson]Bir hata olustu +error.crashtitle = Bir hata olustu +blocks.input = Input +blocks.output = Output +blocks.booster = Booster +block.unknown = [LIGHT_GRAY]??? +blocks.powercapacity = Guc kapasitesi +blocks.powershot = Guc/Saldiri hizi +blocks.targetsair = Havayi hedef alir mi? +blocks.targetsground = Targets Ground +blocks.itemsmoved = Move Speed +blocks.launchtime = Time Between Launches +blocks.shootrange = Menzil +blocks.size = Buyukluk +blocks.liquidcapacity = Sivi kapasitesi +blocks.powerrange = Menzil +blocks.poweruse = Guc kullanimi +blocks.powerdamage = Power/Damage +blocks.itemcapacity = Esya kapasitesi +blocks.basepowergeneration = Base Power Generation +blocks.productiontime = Production Time +blocks.repairtime = Block Full Repair Time +blocks.speedincrease = Speed Increase +blocks.range = Range +blocks.drilltier = Kazilabilirler +blocks.drillspeed = Ana kazma hizi +blocks.boosteffect = Boost Effect +blocks.maxunits = Max Active Units +blocks.health = Can +blocks.buildtime = Build Time +blocks.inaccuracy = sekme +blocks.shots = vuruslar +blocks.reload = Yeniden doldurma +blocks.ammo = Ammo +bar.drillspeed = Drill Speed: {0}/s +bar.efficiency = Efficiency: {0}% +bar.powerbalance = Power: {0} +bar.poweramount = Power: {0} +bar.poweroutput = Power Output: {0} +bar.items = Items: {0} +bar.liquid = Liquid +bar.heat = Heat +bar.power = Power +bar.progress = Build Progress +bar.spawned = Units: {0}/{1} +bullet.damage = [stat]{0}[lightgray] dmg +bullet.splashdamage = [stat]{0}[lightgray] area dmg ~[stat] {1}[lightgray] tiles +bullet.incendiary = [stat]incendiary +bullet.homing = [stat]homing +bullet.shock = [stat]shock +bullet.frag = [stat]frag +bullet.knockback = [stat]{0}[lightgray] knockback +bullet.freezing = [stat]freezing +bullet.tarred = [stat]tarred +bullet.multiplier = [stat]{0}[lightgray]x ammo multiplier +bullet.reload = [stat]{0}[lightgray]x reload +unit.blocks = Yapilar +unit.powersecond = saniyede bir +unit.liquidsecond = Saniyede bir +unit.itemssecond = Saniyede bir +unit.liquidunits = Litre +unit.powerunits = Volt +unit.degrees = derece +unit.seconds = saniye +unit.persecond = /sec +unit.timesspeed = x speed +unit.percent = % +unit.items = esya +category.general = General +category.power = Guc +category.liquids = sivilar +category.items = esyalar +category.crafting = uretim +category.shooting = sikma +category.optional = Optional Enhancements +setting.landscape.name = Lock Landscape +setting.shadows.name = Shadows +setting.linear.name = Linear Filtering +setting.animatedwater.name = Animated Water +setting.animatedshields.name = Animated Shields +setting.antialias.name = Antialias[LIGHT_GRAY] (requires restart)[] +setting.indicators.name = Ally Indicators +setting.autotarget.name = Auto-Target +setting.fpscap.name = Max FPS +setting.fpscap.none = Yok +setting.fpscap.text = {0} FPS +setting.swapdiagonal.name = Always Diagonal Placement +setting.difficulty.training = training setting.difficulty.easy = kolay setting.difficulty.normal = orta setting.difficulty.hard = zor -setting.difficulty.insane = deli -setting.difficulty.purge = tasfiye -setting.difficulty.name = Zorluk: -setting.screenshake.name = Ekran Sallamak -setting.smoothcam.name = Pürüzsüz kamera -setting.indicators.name = Düşman Göstergeleri -setting.effects.name = Görüntü Efektleri -setting.sensitivity.name = Denetleyici hassasiyeti -setting.saveinterval.name = Otomatik Kaydetme Aralığı -setting.seconds = saniye +setting.difficulty.insane = cok zor +setting.difficulty.name = Zorluk derecesi: +setting.screenshake.name = Ekran sallanmasi +setting.effects.name = Efekleri goster +setting.sensitivity.name = Kumanda hassasligi +setting.saveinterval.name = Otomatik kaydetme suresi +setting.seconds = {0} Saniye setting.fullscreen.name = Tam ekran -setting.multithread.name = Çok iş parçacığı -setting.fps.name = Saniyede ... Kare göstermek +setting.borderlesswindow.name = Borderless Window[LIGHT_GRAY] (may require restart) +setting.fps.name = FPS'i goster setting.vsync.name = VSync -setting.lasers.name = Güç Lazerleri Göster -setting.healthbars.name = Varlık Sağlık çubuklarını göster -setting.pixelate.name = Piksel Ekran -setting.musicvol.name = Müzik sesi -setting.mutemusic.name = Müziği Kapat -setting.sfxvol.name = SFX Hacmi +setting.lasers.name = Guc lazerlerini goster +setting.pixelate.name = Pixelate [LIGHT_GRAY](may decrease performance) +setting.minimap.name = Haritayi goster +setting.musicvol.name = Ses yuksekligi +setting.mutemusic.name = Sesi kapat +setting.sfxvol.name = Ses seviyesi setting.mutesound.name = Sesi kapat -map.maze.name = Labirent -map.fortress.name = Kale -map.sinkhole.name = düden -map.caves.name = mağaralar -map.volcano.name = volkan -map.caldera.name = kaldera -map.scorch.name = alazlamak -map.desert.name = çöl -map.island.name = ada -map.grassland.name = Çayır -map.tundra.name = tundra -map.spiral.name = sarmal -map.tutorial.name = Eğitim -tutorial.intro.text = [sarı] Eğiticiye hoşgeldiniz. [] Başlamak için 'ileri' ye basın. -tutorial.moveDesktop.text = Taşımak için [turuncu] [[WASD] [] tuşlarını kullanın. Destek için [turuncu] shift [] tuşunu basılı tutun. Yakınlaştırmak veya uzaklaştırmak için [turuncu] kaydırma tekerini [] kullanırken [turuncu] CTRL [] tuşunu basılı tutun. -tutorial.shoot.text = Hedeflemek için farenizi kullanın, [turuncu] sol fare tuşunu [] vurun. [Sarı] hedef [] üzerinde çalışmayı deneyin. -tutorial.moveAndroid.text = Görünümü kaydırmak için, bir parmağınızı ekran boyunca sürükleyin. Yakınlaştırmak veya uzaklaştırmak için sıkıştırın ve sürükleyin. -tutorial.placeSelect.text = Sağ alttaki blok menüsünden [sarı] bir konveyör [] seçmeyi deneyin. -tutorial.placeConveyorDesktop.text = [Turuncu] [[scrollwheel] [] tuşunu kullanarak konveyörü [turuncu] ileriye [] getirin ve [turuncu] [[sol fare tuşu] [] düğmesini kullanarak [sarı] işaretli konuma [] yerleştirin. -tutorial.placeConveyorAndroid.text = [Turuncu] [[döndürme düğmesi] [] düğmesini kullanarak konveyörü [turuncu] ileriye [] doğru döndürün, bir parmağınızla konumuna sürükleyin, ardından [turuncu] kullanarak [sarı] işaretli konuma [] yerleştirin [[onay işareti][]. -tutorial.placeConveyorAndroidInfo.text = Alternatif olarak, [turuncu] [[dokunma modu] [] moduna geçmek için sol alt taraftaki artı simgesini ve ekrana dokunarak blokları yerleştirebilirsiniz. Dokunmatik modda, bloklar soldaki ok ile döndürülebilir. Denemek için [sarı] sonraki [] tuşuna basın. -tutorial.placeDrill.text = Şimdi, işaretlenmiş konuma bir [sarı] taş matkap [] seçin ve yerleştirin. -tutorial.blockInfo.text = Bir blok hakkında daha fazla bilgi edinmek isterseniz, açıklamayı okumak için sağ üstteki [turuncu] soru işaretine [] dokunabilirsiniz. -tutorial.deselectDesktop.text = [Turuncu] [[sağ fare tuşu] [] kullanarak bir bloğu kaldırabilirsiniz. -tutorial.deselectAndroid.text = [Turuncu] X [] düğmesine basarak bir bloğun seçimini kaldırabilirsiniz. -tutorial.drillPlaced.text = Matkap şimdi [sarı] taş üretecek, [] konveyör üzerine çıkacak, daha sonra [sarı] çekirdeğe [] hareket ettirilecektir. -tutorial.drillInfo.text = Farklı cevherlerin farklı matkaplara ihtiyacı vardır. Taş taş matkaplar gerektirir, demir demir matkaplar gerektirir, vb. -tutorial.drillPlaced2.text = Öğeleri çekirdeğe taşımak, onları sol üstteki [sarı] öğe envanterinize [] yerleştirir. Yerleştirme blokları, envanterinizdeki öğeleri kullanır. -tutorial.moreDrills.text = Birçok matkap ve konveyörü birbirine bağlayabilirsiniz. -tutorial.deleteBlock.text = Silmek istediğiniz blokta [turuncu] sağ fare düğmesine [] tıklayarak blokları silebilirsiniz. Bu konveyörü silmeyi deneyin. -tutorial.deleteBlockAndroid.text = Alt soldaki [turuncu] kesme modu menüsünde [] artı işaretini [] seçerek ve bir bloka dokunarak blokları [turuncu] ile silebilirsiniz. Bu konveyörü silmeyi deneyin. -tutorial.placeTurret.text = Şimdi, [sarı] işaretli konuma [] [sarı] bir taret [] seçin ve yerleştirin. -tutorial.placedTurretAmmo.text = Bu taret artık konveyör [sarı] mermiyi [] kabul edecektir. Üzerinde gezdirerek ne kadar cephane olduğunu ve yeşil renkli [[yeşil] çubuğunu [] kontrol ederek görebilirsiniz. -tutorial.turretExplanation.text = Taretler, yeterli mermiye sahip oldukları sürece otomatik olarak en yakın düşmana ateş ederler. -tutorial.waves.text = Her [sarı] 60 [] saniyede, [mercan] düşmanlardan oluşan bir dalga [] belirli yerlerde doğacak ve çekirdeği yok etmeye çalışacaktır. -tutorial.coreDestruction.text = Hedefiniz [sarı] çekirdeği [] savunmaktır. Çekirdek yok edilirse, [mercan] oyunu kaybedersiniz []. -tutorial.pausingDesktop.text = Bir ara vermeniz gerekiyorsa, oyunu duraklatmak için sol üstteki [turuncu] duraklat [] düğmesine veya [turuncu] boşluk [] tuşuna basın. Duraklatılırken blokları seçebilir ve yerleştirebilirsiniz, ancak hareket edemez veya ateş edemezsiniz. -tutorial.pausingAndroid.text = Bir ara vermeniz gerekirse, oyunu duraklatmak için sol üstteki [turuncu] duraklatma düğmesine [] basın. Duraklatılırken hala blokları kırıp yerleştirebilirsiniz. -tutorial.purchaseWeapons.text = Alt soldaki yükseltme menüsünü açarak, makineniz için yeni [sarı] silahlar [] satın alabilirsiniz. -tutorial.switchWeapons.text = Silahları, sol alt taraftaki simgesini tıklayarak veya sayıları [turuncu] [[1-9] [] kullanarak değiştirebilirsiniz. -tutorial.spawnWave.text = İşte şimdi bir dalga geliyor. Onları yok et. -tutorial.pumpDesc.text = Daha sonraki dalgalarda, jeneratörler veya aspiratörler için sıvı dağıtmak için [sarı] pompaları [] kullanmanız gerekebilir. -tutorial.pumpPlace.text = Pompalar, matkaplar yerine benzer şekilde çalışırlar; [Sarı] belirlenmiş yağa [] bir pompa yerleştirmeyi deneyin. -tutorial.conduitUse.text = Şimdi pompadan önde giden bir [turuncu] kablo kanalı [] yerleştirin. -tutorial.conduitUse2.text = Ve birkaç tane daha ... -tutorial.conduitUse3.text = Ve birkaç tane daha ... -tutorial.generator.text = Şimdi, kanalın ucunda bir [turuncu] yanma jeneratörü [] bloğu yerleştirin. -tutorial.generatorExplain.text = Bu jeneratör şimdi yağdan [sarı] güç [] oluşturacaktır. -tutorial.lasers.text = Güç [sarı] güç lazerleri [] kullanılarak dağıtılır. Döndür ve buraya bir tane yerleştir. -tutorial.laserExplain.text = Jeneratör şimdi gücü lazer bloğuna taşıyacaktır. Bir [sarı] opak [] ışını, şu anda gücü iletmekte olduğu anlamına gelir ve [sarı] saydam [] ışını, bunun olmadığı anlamına gelir. -tutorial.laserMore.text = Bir bloğun üzerine geldiğinde ne kadar gç olduğunu ve üst taraftaki [sarı] sarı çubuğu [] kontrol ederek kontrol edebilirsiniz. -tutorial.healingTurret.text = Bu lazer bir [kireç] onarım tareti [] için kullanılabilir. Bir tane buraya yerleştirin. -tutorial.healingTurretExplain.text = Gücü olduğu sürece, bu taret yakındaki blokları tamir eder. [] en yakın zamanda bu bloku temin edin! -tutorial.smeltery.text = Pek çok blok, [turuncu] yapılabilmesi için çelik gerektirir ve bu da [turuncu] bir dökümcünün [] yapılmasını gerektirir. Bir tane buraya yerleştirin. -tutorial.smelterySetup.text = Bu dökümcü kömürü yakıt olarak kullanarak, demirden [turuncu] çelik [] üretecek. -tutorial.tunnelExplain.text = Ayrıca, eşyaların bir [turuncu] tünel bloğundan [] geçtiğini ve taş bloktan geçerek diğer tarafta ortaya çıktığını unutmayın. Tünellerin yalnızca 2 bloğa kadar gidebileceğini unutmayın. -tutorial.end.text = Ve bu dersi bitirir! İyi şanslar! -text.keybind.title = Tuşları yeniden ayarla -keybind.move_x.name = sağ / sol -keybind.move_y.name = yukarı / aşağı -keybind.select.name = seçmek -keybind.break.name = kırmak -keybind.shoot.name = ateş etme -keybind.zoom_hold.name = tut ve büyüt -keybind.zoom.name = Yakınlaştır -keybind.block_info.name = blok bilgisi -keybind.menu.name = menü -keybind.pause.name = duraklatma -keybind.dash.name = tire -keybind.chat.name = Sohbet -keybind.player_list.name = oyuncu listesi -keybind.console.name = KONTROL MASASI -keybind.rotate_alt.name = rotate_alt -keybind.rotate.name = Döndür -keybind.weapon_1.name = weapon_1 -keybind.weapon_2.name = weapon_2 -keybind.weapon_3.name = weapon_3 -keybind.weapon_4.name = weapon_4 -keybind.weapon_5.name = weapon_5 -keybind.weapon_6.name = weapon_6 -mode.text.help.title = Modların açıklaması -mode.waves.name = dalgalar -mode.waves.description = normal mod. sınırlı kaynaklar ve otomatik gelen dalgalar. -mode.sandbox.name = Limitsiz Oynama -mode.sandbox.description = sonsuz kaynaklar ve dalgalar için zamanlayıcı yok. -mode.freebuild.name = Özgür Oynama -mode.freebuild.description = sınırlı kaynaklar ve dalgalar için zamanlayıcı yok. -upgrade.standard.name = standart -upgrade.standard.description = Standart mech. -upgrade.blaster.name = blaster -upgrade.blaster.description = Yavaş, zayıf bir mermi ateş eder. -upgrade.triblaster.name = triblaster -upgrade.triblaster.description = Bir yayında 3 mermi ateş eder. -upgrade.clustergun.name = clustergun -upgrade.clustergun.description = Yayılan bombalar ateş eder. -upgrade.beam.name = lazer -upgrade.beam.description = Uzun menzilli bir delici lazer ışını atar. -upgrade.vulcan.name = Vulkan -upgrade.vulcan.description = Hızlı mermiler ateş eder. -upgrade.shockgun.name = shockgun -upgrade.shockgun.description = Yıkıcı ve patlayıcı mermiler savurarak ateş eder. -item.stone.name = taş -item.iron.name = Demir -item.coal.name = kömür -item.steel.name = çelik -item.titanium.name = titanyum -item.dirium.name = dirium -item.uranium.name = uranyum -item.sand.name = kum -liquid.water.name = su -liquid.plasma.name = plazma -liquid.lava.name = lav -liquid.oil.name = petrol -block.weaponfactory.name = silah fabrikası -block.weaponfactory.fulldescription = Oyuncu mech için silah oluşturmak için kullanılır. Kullanmak için tıklayın. Kaynaklarını otomatik olarak çekirdekten alır. -block.air.name = hava -block.blockpart.name = blokparçası -block.deepwater.name = derin su +setting.crashreport.name = Send Anonymous Crash Reports +setting.chatopacity.name = Chat Opacity +setting.playerchat.name = Display In-Game Chat +keybind.title = Tuslari ayarla +category.general.name = General +category.view.name = Goster +category.multiplayer.name = Cok oyunculu +command.attack = Attack +command.retreat = Retreat +command.patrol = Patrol +keybind.gridMode.name = Block Select +keybind.gridModeShift.name = Category Select +keybind.press = Bir tusa bas... +keybind.press.axis = Bir yone cevir yada tusa bas... +keybind.screenshot.name = Map Screenshot +keybind.move_x.name = Sol/Sag hareket +keybind.move_y.name = Yukari/asagi hareket +keybind.select.name = Sec/silahi sik +keybind.diagonal_placement.name = Diagonal Placement +keybind.pick.name = Pick Block +keybind.break_block.name = Break Block +keybind.deselect.name = Eldeki yapiyi birak +keybind.shoot.name = Sik +keybind.zoom_hold.name = Yaklasma basili tutmasi +keybind.zoom.name = Yaklas +keybind.menu.name = Menu +keybind.pause.name = Durdur +keybind.minimap.name = Minimap +keybind.dash.name = Kos +keybind.chat.name = konus +keybind.player_list.name = Oyuncu listesi +keybind.console.name = Konsol +keybind.rotate.name = cevir +keybind.toggle_menus.name = Menuleri ac'kapat +keybind.chat_history_prev.name = Konusma gecmisi geri +keybind.chat_history_next.name = Konusma gecmisi ileri +keybind.chat_scroll.name = Konusma kaydir +keybind.drop_unit.name = Unit birak +keybind.zoom_minimap.name = Haritayi yaklastir +mode.help.title = Modlarin aciklamalari +mode.survival.name = Survival +mode.survival.description = The normal mode. Limited resources and automatic incoming waves. +mode.sandbox.name = Serbest +mode.sandbox.description = Sonsuz esyalar ve Dalga suresi yok +mode.pvp.name = PvP +mode.pvp.description = fight against other players locally. +mode.attack.name = Attack +mode.attack.description = No waves, with the goal to destroy the enemy base. +mode.custom = Custom Rules +rules.infiniteresources = Infinite Resources +rules.wavetimer = Wave Timer +rules.waves = Waves +rules.enemyCheat = Infinite AI Resources +rules.unitdrops = Unit Drops +rules.unitbuildspeedmultiplier = Unit Creation Speed Multiplier +rules.unithealthmultiplier = Unit Health Multiplier +rules.playerhealthmultiplier = Player Health Multiplier +rules.playerdamagemultiplier = Player Damage Multiplier +rules.unitdamagemultiplier = Unit Damage Multiplier +rules.enemycorebuildradius = Enemy Core No-Build Radius:[LIGHT_GRAY] (tiles) +rules.respawntime = Respawn Time:[LIGHT_GRAY] (sec) +rules.wavespacing = Wave Spacing:[LIGHT_GRAY] (sec) +rules.buildcostmultiplier = Build Cost Multiplier +rules.buildspeedmultiplier = Build Speed Multiplier +rules.waitForWaveToEnd = Waves wait for enemies +rules.dropzoneradius = Drop Zone Radius:[LIGHT_GRAY] (tiles) +rules.respawns = Max respawns per wave +rules.limitedRespawns = Limit Respawns +rules.title.waves = Waves +rules.title.respawns = Respawns +rules.title.resourcesbuilding = Resources & Building +rules.title.player = Players +rules.title.enemy = Enemies +rules.title.unit = Units +content.item.name = Esyalar +content.liquid.name = Sivilar +content.unit.name = Units +content.block.name = Blocks +content.mech.name = Robotlar +item.copper.name = Bakir +item.copper.description = ise yayar bir materyal. Kazma makineleriyle yada tasimayla alinabilir. +item.lead.name = Kursun +item.lead.description = Basit bir baslangic materyali. sivi tasimada kullanilabilir. +item.coal.name = Komur +item.coal.description = Yaygin bir yakit. +item.graphite.name = Graphite +item.titanium.name = Titanyum +item.titanium.description = Nadir ve hafif bir materyal. Hava araclarinda, Kazma makinelerinde ve sivi tasima tuplerinde kullanilir. +item.thorium.name = Toryum +item.thorium.description = Nukleer yakit olarak kullanilan sert ve nukleer bir materyal. +item.silicon.name = Silikon +item.silicon.description = Gunes panellerinde ve gelismis materallerde kullanilan bir materyal +item.plastanium.name = Plastanyum +item.plastanium.description = hafif bir madde, hava makinelerinde ve silahlara kursun olarak kullanilir. +item.phase-fabric.name = Phase Fabric +item.phase-fabric.description = A near-weightless substance used in advanced electronics and self-repairing technology. +item.surge-alloy.name = Kabarma karisimi +item.surge-alloy.description = An advanced alloy with unique electrical properties. +item.spore-pod.name = Spore Pod +item.spore-pod.description = Used for conversion into oil, explosives and fuel. +item.sand.name = Kum +item.sand.description = karistirma maddesi olark kullanilan yaygin bir madde. +item.blast-compound.name = patlama birlesimi +item.blast-compound.description = Bombalar ve patlayicilarda kullanilabilir. Yakit olarak kullanilmasi tavsiye edilmez. +item.pyratite.name = Pyratite +item.pyratite.description = Yakici silahlar icin yakici bir madde. +item.metaglass.name = Metaglass +item.metaglass.description = A super-tough glass compound. Extensively used for liquid distribution and storage. +item.scrap.name = Scrap +item.scrap.description = Leftover remnants of old structures and units. Contains trace amounts of many different metals. +liquid.water.name = Su +liquid.slag.name = Slag +liquid.oil.name = Benzin +liquid.cryofluid.name = kriyo sivisi +mech.alpha-mech.name = Alpha +mech.alpha-mech.weapon = Agir plazma silahi +mech.alpha-mech.ability = Pervaneli savunma +mech.alpha-mech.description = Standart Robot. Kendisine yardim etmesi icin 3 adet dron cikartabilir +mech.delta-mech.name = Delta +mech.delta-mech.weapon = Arc jenaratoru +mech.delta-mech.ability = Sarz cekici +mech.delta-mech.description = vur kac icin yapilmis olan hizli bir makine. duvarlara az hasar verir ama gruplari temizlemesiyle bilinir. +mech.tau-mech.name = Tau +mech.tau-mech.weapon = Yok edici Lazer +mech.tau-mech.ability = Tamircinin patlamasi +mech.tau-mech.description = Destek bir robot. alaninin icindeki herseyi tamir edebilir +mech.omega-mech.name = Omega +mech.omega-mech.weapon = Solucan roketler +mech.omega-mech.ability = Zirhli Yok edici +mech.omega-mech.description = Tank ve hasar vurucu bir robot. Zirhi ona %90 hasari engellemesini saglayan bir kalkan verir. +mech.dart-ship.name = Dart +mech.dart-ship.weapon = Tarayici +mech.dart-ship.description = Var olan en hizli robot. guclu bir vurkacci +mech.javelin-ship.name = Javelin +mech.javelin-ship.description = vur kac tipindeki bir unit. bir anda buyuk bir hasara sebep olabilir +mech.javelin-ship.weapon = Patlayici roketler +mech.javelin-ship.ability = sarz calici +mech.trident-ship.name = Trident +mech.trident-ship.description = Bir bombaci. Guzel bir zirha sahip +mech.trident-ship.weapon = mini atomlar +mech.glaive-ship.name = Glaive +mech.glaive-ship.description = Guzel, buyuk bir unit. Hiz limiti ve kapesitesi iyidir +mech.glaive-ship.weapon = Orman yakici +item.explosiveness = [LIGHT_GRAY]Patlayicilik: {0} +item.flammability = [LIGHT_GRAY]Yanbilirlik: {0} +item.radioactivity = [LIGHT_GRAY]Radyoaktivite: {0} +unit.health = [LIGHT_GRAY]Can: {0} +unit.speed = [LIGHT_GRAY]hiz: {0} +mech.weapon = [LIGHT_GRAY]silah: {0} +mech.health = [LIGHT_GRAY]Health: {0} +mech.itemcapacity = [LIGHT_GRAY]esya kapasitesi: {0} +mech.minespeed = [LIGHT_GRAY]kazma hizi: {0} +mech.minepower = [LIGHT_GRAY]kazma gucu: {0} +mech.ability = [LIGHT_GRAY]yetenek gucu: {0} +mech.buildspeed = [LIGHT_GRAY]Building Speed: {0}% +liquid.heatcapacity = [LIGHT_GRAY]isinma kapasitesi: {0} +liquid.viscosity = [LIGHT_GRAY]Yari sivilik: {0} +liquid.temperature = [LIGHT_GRAY]isi: {0} +block.grass.name = Grass +block.salt.name = Salt +block.saltrocks.name = Salt Rocks +block.pebbles.name = Pebbles +block.tendrils.name = Tendrils +block.sandrocks.name = Sand Rocks +block.spore-pine.name = Spore Pine +block.sporerocks.name = Spore Rocks +block.rock.name = Rock +block.snowrock.name = Snow Rock +block.shale.name = Shale +block.shale-boulder.name = Shale Boulder +block.moss.name = Moss +block.shrubs.name = Shrubs +block.spore-moss.name = Spore Moss +block.shalerocks.name = Shale Rocks +block.scrap-wall.name = Scrap Wall +block.scrap-wall-large.name = Large Scrap Wall +block.scrap-wall-huge.name = Huge Scrap Wall +block.scrap-wall-gigantic.name = Gigantic Scrap Wall +block.thruster.name = Thruster +block.kiln.name = Kiln +block.kiln.description = Smelts sand and lead into metaglass. Requires small amounts of power. +block.graphite-press.name = Graphite Press +block.multi-press.name = Multi-Press +block.constructing = {0}\n[LIGHT_GRAY](Constructing) +block.spawn.name = Enemy Spawn +block.core-shard.name = Core: Shard +block.core-foundation.name = Core: Foundation +block.core-nucleus.name = Core: Nucleus +block.deepwater.name = su alti block.water.name = su -block.lava.name = lav -block.oil.name = petrol -block.stone.name = taş -block.blackstone.name = siyah taş -block.iron.name = Demir -block.coal.name = kömür -block.titanium.name = titanyum -block.uranium.name = uranyum -block.dirt.name = toprak +block.tainted-water.name = Tainted Water +block.darksand-tainted-water.name = Dark Sand Tainted Water +block.tar.name = Tar +block.stone.name = tas block.sand.name = kum +block.darksand.name = Dark Sand block.ice.name = buz block.snow.name = kar -block.grass.name = Otlar -block.sandblock.name = kumbloku -block.snowblock.name = karbloku -block.stoneblock.name = taşbloku -block.blackstoneblock.name = blackstoneblock -block.grassblock.name = grassblock -block.mossblock.name = mossblock -block.shrub.name = çalı -block.rock.name = Kaya -block.icerock.name = ICEROCK -block.blackrock.name = Siyah Kaya -block.dirtblock.name = dirtblock -block.stonewall.name = taş duvar -block.stonewall.fulldescription = Ucuz bir savunma bloğu. İlk birkaç dalgada çekirdeği ve tareti korumak için kullanışlıdır. -block.ironwall.name = Demir duvar -block.ironwall.fulldescription = Temel bir savunma bloğu. Düşmanlardan korunma sağlar. Taş duvardan daha korunaklıdır. -block.steelwall.name = Çelik duvar -block.steelwall.fulldescription = Standart bir savunma bloğu. düşmanlardan korunma sağlar -block.titaniumwall.name = titanyum duvar -block.titaniumwall.fulldescription = Güçlü bir savunma bloğu. Düşmanlardan korunma sağlar. -block.duriumwall.name = dirium duvar -block.duriumwall.fulldescription = Çok güçlü bir savunma bloğu. Düşmanlardan korunma sağlar. -block.compositewall.name = kompozit duvar -block.steelwall-large.name = büyük çelik duvar -block.steelwall-large.fulldescription = Standart bir savunma bloğu. Birden fazla fayansa yayılır. -block.titaniumwall-large.name = büyük titanyum duvar -block.titaniumwall-large.fulldescription = Güçlü bir savunma bloğu. Birden fazla fayans yayılır. -block.duriumwall-large.name = büyük dirsek duvarı -block.duriumwall-large.fulldescription = Çok güçlü bir savunma bloğu. Birden fazla fayans yayılır. -block.titaniumshieldwall.name = korumalı duvar -block.titaniumshieldwall.fulldescription = Ekstra yerleşik bir kalkan ile güçlü bir savunma bloğu. Düşman mermilerini emmek için enerji kullanır. Bu bloğa enerji sağlamak için güç arttırıcıların kullanılması tavsiye edilir. -block.repairturret.name = onarım tareti -block.repairturret.fulldescription = Yakındaki hasarlı blokları yavaş bir hızda tamir eder. Küçük menzili vardır. Az miktarlarda güç kullanır. -block.megarepairturret.name = onarım tareti II -block.megarepairturret.fulldescription = Yakındaki hasarlı blokları tamir eder. Uygun menzillidir. Gücü kullanır. -block.shieldgenerator.name = kalkan üreteci -block.shieldgenerator.fulldescription = Gelişmiş bir savunma bloğu. Bir yarıçaptaki tüm blokları saldırıya karşı korur. Boştayken gücü yavaş bir hızda kullanır, ancak mermi temasında enerjiyi hızla boşaltır. -block.door.name = kapı -block.door.fulldescription = Dokunarak açılıp kapatılabilen bir blok. -block.door-large.name = büyük kapı -block.door-large.fulldescription = Dokunarak açılıp kapatılabilen bir blok. -block.conduit.name = sıvı borusu -block.conduit.fulldescription = Temel sıvı taşıma bloğu. Bir konveyör gibi çalışır, ancak sıvılar ile. pompa veya diğer borular ile kullanılır. Düşmanlar ve oyuncular için sıvılar üzerinde bir köprü olarak kullanılabilir. -block.pulseconduit.name = hızlı sıvı borusu -block.pulseconduit.fulldescription = Gelişmiş sıvı taşıma bloku. Sıvıları daha hızlı taşır ve standart sıvı taşıma borularından daha fazla sıvı depolar. -block.liquidrouter.name = sıvı yönlendirici -block.liquidrouter.fulldescription = Bir yönlendiriciye benzer şekilde çalışır. Bir taraftan sıvı girişi kabul eder ve diğer tarafa gönderir. Tek bir borudan diğer birçok boruyla sıvı paylaşmak için kullanışlıdır. -block.conveyor.name = konveyör -block.conveyor.fulldescription = En temel madde taşıma bloğu. Öğeleri konulduğu yöne göre maddeleri ileriye taşır ve bunları otomatik olarak taretlere ya da üretici bloklara getirir. konulmadan önce Döndürülebilirler, ancak konulduktan sonra Döndürülemezler. Düşmanlar ve oyuncular için sıvılar üzerinde bir köprü olarak kullanılabilir. -block.steelconveyor.name = çelik konveyör -block.steelconveyor.fulldescription = Gelişmiş madde taşıma bloğu. Öğeleri standart konveyörlerden daha hızlı taşır. -block.poweredconveyor.name = hızlı konveyör -block.poweredconveyor.fulldescription = Nihai ürün taşıma bloğu. Öğeleri çelik konveyörlerden daha hızlı taşır. -block.router.name = yönlendirici -block.router.fulldescription = Öğeleri bir yönden kabul eder ve 3 farklı yöne gönderir. Malzemelerin belirli bir miktarını da depolayabilir. Malzemelerin bir matkaptan çoklu taretlere ayrılması için uygundur. -block.junction.name = Kavşak noktası -block.junction.fulldescription = İki çapraz şekilde geçmeye çalışan konveyör bandı için köprü görevi görür. Farklı yerlere farklı malzemeler taşıyan konveyör olduğu durumlarda kullanışlıdır. -block.conveyortunnel.name = konveyör tüneli -block.conveyortunnel.fulldescription = Maddeleri blokların altından geçirmek için kullanılır. Kullanmak için, altına tünel yapılacak bloğun bir tarafta giriş tüneli ve diğer tarafa çıkış tüneli yerleştirin. Her iki tünelin de giriş veya çıkış yapan bloklara doğru zıt yönlere baktığından emin olun. -block.liquidjunction.name = sıvı bağlantı -block.liquidjunction.fulldescription = İki çaprazdan geçen boru için köprü görevi görür. Farklı yerlere farklı sıvılar taşıyan kanalların olduğu durumlarda kullanışlıdır. -block.liquiditemjunction.name = sıvı madde kavşağı -block.liquiditemjunction.fulldescription = Kanalları ve konveyörleri yan yana geçirmek için bir köprü görevi görür. -block.powerbooster.name = güç yükseltici -block.powerbooster.fulldescription = Gücü kendi yarıçapı içindeki tüm bloklara dağıtır. -block.powerlaser.name = güç lazeri -block.powerlaser.fulldescription = Önündeki bloğa güç ileten bir lazer oluşturur. Herhangi bir güç üretmez. En iyi jeneratörler veya diğer lazerler ile kullanılır. -block.powerlaserrouter.name = lazer yönlendirici -block.powerlaserrouter.fulldescription = Bir kerede gücü üç yöne dağıtan lazer. Bir jeneratörden birçok bloka güç verilmesi gereken durumlarda kullanışlıdır. -block.powerlasercorner.name = lazer köşesi -block.powerlasercorner.fulldescription = Bir kerede gücü iki yöne dağıtan lazer. Bir jeneratörden birçok bloka güç verilmesi gereken durumlarda ve bir yönlendiricinin kesin olmadığı durumlarda kullanışlıdır. -block.teleporter.name = teletaşıyıcı -block.teleporter.fulldescription = Gelişmiş madde taşıma bloğu. tele-taşıyıcı, öğeleri aynı renkte olan bir teletaşıyıcıya yönlendirir. Aynı renkte teletaşıyıcı yoksa, hiçbir şey yapmaz. Aynı renkten birden çok tele-yazıcı varsa, rastgele biri seçilir. Gücü kullanır. Rengi değiştirmek için dokunun. Not: Sadece madde ileten teletaşıyıcılar gücü kullanır. -block.sorter.name = ayrıştırıcı -block.sorter.fulldescription = Malzemeleri türüne göre ayrıştırır. Kabul edilecek malzeme bloktaki renkle gösterilir. Doğru materyal ile eşleşen tüm öğeler ileriye doğru çıkar, diğer her şey sol ve sağ taraflardan çıkar. -block.core.name = çekirdek -block.pump.name = pompa -block.pump.fulldescription = Kaynak bloğundan su, lav veya yağ gibi sıvıları pompalar. Yakındaki kanallara sıvıyı aktarır. -block.fluxpump.name = fluxpump -block.fluxpump.fulldescription = Pompanın gelişmiş bir versiyonu. Sıvıyı daha hızlı pompalar ve daha fazla sıvı depolar. -block.smelter.name = dökümcü -block.smelter.fulldescription = Temel üretim bloğu. 1 demir ve 1 kömür yakıt olarak verildiğinde, demir çıkarır. Tıkanmayı önlemek için farklı konveyörlerden demir ve kömürün kullanılması tavsiye edilir. -block.crucible.name = pota -block.crucible.fulldescription = Gelişmiş bir üretim bloğu. 1 titanyum, 1 çelik ve 1 kömür yakıt olarak girildiğinde, dirium çıkarır. Tıkanmayı önlemek için farklı konveyörlerden kömür, çelik ve titanyum kullanılması tavsiye edilir. -block.coalpurifier.name = kömür çıkarıcı -block.coalpurifier.fulldescription = Temel bir ekstraktör bloğu. Çok miktarda su ve taş ile birlikte tedarik edildiğinde kömür çıkarır. -block.titaniumpurifier.name = titanyum çıkarıcı -block.titaniumpurifier.fulldescription = Standart bir ekstraktör bloğu. Çok miktarda su ve demir ile birlikte verildiğinde titanyum çıkarır. -block.oilrefinery.name = yağ rafinerisi -block.oilrefinery.fulldescription = Büyük miktarda yağı kömür parçalarına ayırır. Kömür damarları kıt olduğunda kömür bazlı taretlerin yakıtı için kullanışlıdır. -block.stoneformer.name = taş biçimlendiricisi -block.stoneformer.fulldescription = Lavı taş haline getirir. Muazzam miktarda taş üretmek için kullanışlıdır. -block.lavasmelter.name = lav dökümcüsü -block.lavasmelter.fulldescription = Demiri çeliğe dönüştürmek için lav kullanır. Dökümcüler için bir alternatif. Kömürün az olduğu durumlarda kullanışlıdır -block.stonedrill.name = taş matkap -block.stonedrill.fulldescription = Temel bir matkap. Taş karolara yerleştirildiğinde, süresiz olarak yavaş bir hızda taş çıkarırç -block.irondrill.name = demir matkap -block.irondrill.fulldescription = Temel bir matkap. Demir cevheri çinileri üzerine yerleştirildiğinde, süresiz olarak yavaş bir şekilde demir çıkarıTemel bir matkap. Demir cevheri çinileri üzerine yerleştirildiğinde, süresiz olarak yavaş bir şekilde demir çıkarır.\n. -block.coaldrill.name = kömür matkap -block.coaldrill.fulldescription = Temel bir matkap. Kömür madeninin üzerine yerleştirildiğinde, süresiz olarak yavaş bir şekilde kömür çıkarır. -block.uraniumdrill.name = uranyum matkap -block.uraniumdrill.fulldescription = Gelişmiş bir matkap. Uranyum cevheri üzerine yerleştirildiğinde, uranyumu süresiz olarak yavaş bir hızda çıkarır. -block.titaniumdrill.name = titanyum matkap -block.titaniumdrill.fulldescription = Gelişmiş bir matkap. Titanyum cevherinin üzerine yerleştirildiğinde, sonsuza yavaş bir tempoda titanyum çıkar. -block.omnidrill.name = omnidrill -block.omnidrill.fulldescription = En büyük matkap. Herhangi bir cevherin uzerine yerlestitldiginde hızlı bir hızda cevher çıkarır -block.coalgenerator.name = kömür jeneratörü -block.coalgenerator.fulldescription = Gerekli jeneratör. Kömürden güç üretir. 4 tarafına lazer olarak güç verir. -block.thermalgenerator.name = termik jeneratör -block.thermalgenerator.fulldescription = Lavdan güç üretir. 4 tarafına lazer olarak güç verir. -block.combustiongenerator.name = yanma jeneratörü -block.combustiongenerator.fulldescription = Yağdan güç üretir. 4 tarafına lazer olarak güç verir. -block.rtgenerator.name = RTG jeneratörü -block.rtgenerator.fulldescription = Uranyumun radyoaktif bozunmasından az miktarda güç üretir. 4 tarafına lazer olarak güç verir. -block.nuclearreactor.name = nükleer reaktör -block.nuclearreactor.fulldescription = RTG Jeneratörünün gelişmiş bir versiyonu ve en iyi güç jeneratörüdür. Uranyumdan güç üretir. Su ile soğutulması gerekir. Son derece tehlikelidir; yetersiz miktarda su ile beslenmediğinde şiddetli patlayabilir. -block.turret.name = taret -block.turret.fulldescription = Basit, ucuz bir kule. Cephane için taş kullanır. Çift taretten biraz daha büyük menzillidir. -block.doubleturret.name = çift ​​taret -block.doubleturret.fulldescription = Taretin biraz daha güçlü bir versiyonu. Cephane için taş kullanır. standart tarete nazaran daha fazla hasar verir, ancak daha düşük bir menzile sahiptir. İki mermi ile ateş eder. -block.machineturret.name = gattling tareti -block.machineturret.fulldescription = Demir atan bir taret. Cephane için demir kullanır. İyi bir hasar ile ateş oranına sahiptir. -block.shotgunturret.name = splitter tareti -block.shotgunturret.fulldescription = Standart bir kule. Cephane için demir kullanır. tek atışta 7 mermi yayılır. Düşük menzillidir, ancak Gatling taretinden daha yüksek hasar verir. -block.flameturret.name = alev tareti -block.flameturret.fulldescription = Gelişmiş yakın menzilli taret. Cephane için kömür kullanır. Çok düşük bir menzile sahiptir, ancak çok yüksek hasar verir. Duvarların arkasında kullanılması tavsiye edilir. -block.sniperturret.name = çelik tareti -block.sniperturret.fulldescription = Gelişmiş uzun menzilli taret. Cephane için çelik kullanır. Yüksek hasar verir, ancak düşük ateş hızı vardır. Kullanımı pahalı, ancak yüksek menzili nedeniyle düşmanı uzak mesafelerden vurabilir. -block.mortarturret.name = flak tareti -block.mortarturret.fulldescription = Gelişmiş sıçrama hasarlı tareti. Cephane için kömür kullanır. Patlayan mermi şarapnel saysinde birden fazla düşman vurabilir. büyük düşman topluluklarını yok etmek için kullanışlıdır. -block.laserturret.name = lazer tareti -block.laserturret.fulldescription = Gelişmiş tek hedefli taret. Gücü kullanır. orta menzilli taret. Sadece tek hedefli. Asla ıskalamaz. -block.waveturret.name = tesla tareti -block.waveturret.fulldescription = Gelişmiş birçok hedefli taret. Gücü kullanır. Orta menzilli. Asla ıskalamaz. Düşük ile orta seviyede hasar verir, ancak aynı anda birden fazla düşmana vurabilir. -block.plasmaturret.name = plazma tareti -block.plasmaturret.fulldescription = Alev taretinin gelişmiş versiyonu. kömürü cephane olarak kullanır. Çok yüksek hasar, düşük ila orta menzil. -block.chainturret.name = zincir tareti -block.chainturret.fulldescription = En iyi hızlı ateş tareti. Uranyumu cephane olarak kullanır. Yüksek ateş hızı vardır. Orta menzillidir. Birden fazla blok boyunca yayılır. Son derece dayanıklıdır. -block.titancannon.name = titan topu -block.titancannon.fulldescription = En iyi uzak menzilli taret. Uranyumu cephane olarak kullanır. Orta ateş hızındadır ve top ateşinin sıçrama hasarı büyüktür. Uzun mesafe. Birden fazla blok boyunca yayılır. Son derece dayanıklıdır. -block.playerspawn.name = oyuncudoğuşu -block.enemyspawn.name = dϋşmandoğuşu +block.craters.name = Craters +block.sand-water.name = Sand water +block.darksand-water.name = Dark Sand Water +block.char.name = Char +block.holostone.name = Holo stone +block.ice-snow.name = Ice Snow +block.rocks.name = Rocks +block.icerocks.name = Ice rocks +block.snowrocks.name = Snow Rocks +block.dunerocks.name = Dune Rocks +block.pine.name = Pine +block.white-tree-dead.name = White Tree Dead +block.white-tree.name = White Tree +block.spore-cluster.name = Spore Cluster +block.metal-floor.name = Metal Floor +block.metal-floor-2.name = Metal Floor 2 +block.metal-floor-3.name = Metal Floor 3 +block.metal-floor-5.name = Metal Floor 5 +block.metal-floor-damaged.name = Metal Floor Damaged +block.dark-panel-1.name = Dark Panel 1 +block.dark-panel-2.name = Dark Panel 2 +block.dark-panel-3.name = Dark Panel 3 +block.dark-panel-4.name = Dark Panel 4 +block.dark-panel-5.name = Dark Panel 5 +block.dark-panel-6.name = Dark Panel 6 +block.dark-metal.name = Dark Metal +block.ignarock.name = Igna Rock +block.hotrock.name = Hot Rock +block.magmarock.name = Magma Rock +block.cliffs.name = Cliffs +block.copper-wall.name = bakir duvar +block.copper-wall-large.name = buyuk bakir duvar +block.titanium-wall.name = Titanium Wall +block.titanium-wall-large.name = Large Titanium Wall +block.phase-wall.name = faz duvar +block.phase-wall-large.name = genis faz duvar +block.thorium-wall.name = Toryum duvari +block.thorium-wall-large.name = genis toryum duvari +block.door.name = kapi +block.door-large.name = genis kapi +block.duo.name = ikili +block.scorch.name = Scorch +block.scatter.name = Scatter +block.hail.name = yagdirici +block.lancer.name = Lazer topu +block.conveyor.name = konvenyor +block.titanium-conveyor.name = Titanyum konvenyor +block.junction.name = ayirici +block.router.name = dagitici +block.distributor.name = yayici +block.sorter.name = secici +block.sorter.description = esyalari secer. rengi ayni olan esya ileriden, digerleri sagdan ve soldan devam eder +block.overflow-gate.name = Kapali dagatici +block.overflow-gate.description = sadece saga ve sola dagatir. onu kapalidir +block.silicon-smelter.name = Silikon eritici +block.phase-weaver.name = Dokumaci +block.pulverizer.name = pulvarizor +block.cryofluidmixer.name = Cryosivisi karistiricisi +block.melter.name = eritici +block.incinerator.name = isi firini +block.spore-press.name = Spore Press +block.separator.name = separator +block.coal-centrifuge.name = Coal Centrifuge +block.power-node.name = Guc Dugumu +block.power-node-large.name = buyuk Guc Dugumu +block.surge-tower.name = Surge Tower +block.battery.name = batarya +block.battery-large.name = buyuk batarya +block.combustion-generator.name = sicaklik jenaratoru +block.turbine-generator.name = termal jenaratoru +block.differential-generator.name = Differential Generator +block.impact-reactor.name = Impact Reactor +block.mechanical-drill.name = Mekanikal Kazici +block.pneumatic-drill.name = Pneumatik Kazici +block.laser-drill.name = Lazer kazici +block.water-extractor.name = su ayiricisi +block.cultivator.name = ekici +block.dart-mech-pad.name = Dart Mech Pad +block.delta-mech-pad.name = Delta Mech Pad +block.javelin-ship-pad.name = Javelin Ship Pad +block.trident-ship-pad.name = Trident Ship Pad +block.glaive-ship-pad.name = Glaive Ship Pad +block.omega-mech-pad.name = Omega Mech Pad +block.tau-mech-pad.name = Tau Mech Pad +block.conduit.name = boru +block.mechanical-pump.name = Mekanikal pompa +block.item-source.name = esya kaynagi +block.item-void.name = esya deligi +block.liquid-source.name = sivi kaynagi +block.power-void.name = guc deligi +block.power-source.name = sonsuz guc +block.unloader.name = bekletici +block.vault.name = kasa +block.wave.name = Dalga +block.swarmer.name = Swarmer +block.salvo.name = Salvo +block.ripple.name = Ripple +block.phase-conveyor.name = Phase konvenyoru +block.bridge-conveyor.name = kopru konvenyoru +block.plastanium-compressor.name = Plastanium Kompresoru +block.pyratite-mixer.name = Pyratite karistirici +block.blast-mixer.name = Patlayici karistiricisi +block.solar-panel.name = gunes paneli +block.solar-panel-large.name = genis gunes paneli +block.oil-extractor.name = benzin ayirici +block.spirit-factory.name = Spirit Drone Factory +block.phantom-factory.name = Phantom Drone Factory +block.wraith-factory.name = Wraith Fighter Factory +block.ghoul-factory.name = Ghoul Bomber Factory +block.dagger-factory.name = Dagger Mech Factory +block.crawler-factory.name = Crawler Mech Factory +block.titan-factory.name = Titan Mech Factory +block.fortress-factory.name = Fortress Mech Factory +block.revenant-factory.name = Revenant Fighter Factory +block.repair-point.name = tamirci +block.pulse-conduit.name = Pulse borusu +block.phase-conduit.name = Phase borusu +block.liquid-router.name = sivi ayirici +block.liquid-tank.name = sivi tanki +block.liquid-junction.name = sivi yonlendirici +block.bridge-conduit.name = kopru borusu +block.rotary-pump.name = donen boru +block.thorium-reactor.name = Thorium Reaktoru +block.mass-driver.name = kütle surucusu +block.blast-drill.name = Patlatici kazici +block.thermal-pump.name = Termal pompa +block.thermal-generator.name = Magma jeneratoru +block.alloy-smelter.name = Alloy eritici +block.mender.name = Mender +block.mend-projector.name = Mend koruyucu +block.surge-wall.name = kabarma duvari +block.surge-wall-large.name = genis kabarma duvari +block.cyclone.name = Siklon +block.fuse.name = fitil +block.shock-mine.name = Shock Mine +block.overdrive-projector.name = Overdrive Projector +block.force-projector.name = Force Projector +block.arc.name = Arc +block.rtg-generator.name = RTG Generator +block.spectre.name = Spectre +block.meltdown.name = Meltdown +block.container.name = Container +block.launch-pad.name = Launch Pad +block.launch-pad.description = Launches batches of items without any need for a core launch. Unfinished. +block.launch-pad-large.name = Large Launch Pad +team.blue.name = blue +team.red.name = red +team.orange.name = orange +team.none.name = gray +team.green.name = green +team.purple.name = purple +unit.spirit.name = Spirit Drone +unit.spirit.description = The starter drone unit. Spawns in the core by default. Automatically mines ores, collects items and repairs blocks. +unit.phantom.name = Phantom Drone +unit.phantom.description = An advanced drone unit. Automatically mines ores, collects items and repairs blocks. Significantly more effective than a drone. +unit.dagger.name = Dagger +unit.dagger.description = basit bir zemin uniti +unit.crawler.name = Crawler +unit.titan.name = Titan +unit.titan.description = havaya sikabilen, gelismis bir unit +unit.ghoul.name = Ghoul Bomber +unit.ghoul.description = A heavy carpet bomber. Uses blast compound or pyratite as ammo. +unit.wraith.name = Wraith Fighter +unit.wraith.description = A fast, hit-and-run interceptor unit. +unit.fortress.name = Fortress +unit.fortress.description = A heavy artillery ground unit. +unit.revenant.name = Revenant +unit.eruptor.name = Eruptor +unit.chaos-array.name = Chaos Array +unit.eradicator.name = Eradicator +unit.lich.name = Lich +unit.reaper.name = Reaper +tutorial.begin = Your mission here is to eradicate the[LIGHT_GRAY] enemy[].\n\nBegin by[accent] mining copper[]. Tap a copper ore vein near your core to do this. +tutorial.drill = Mining manually is inefficient.\n[accent]Drills []can mine automatically.\nPlace one on a copper vein. +tutorial.conveyor = [accent]Conveyors[] are used to transport items to the core.\nMake a line of conveyors from the drill to the core. +tutorial.morecopper = More copper is required.\n\nEither mine it manually, or place more drills. +tutorial.turret = Defensive structures must be built to repel the[LIGHT_GRAY] enemy[].\nBuild a duo turret near your base. +tutorial.drillturret = Duo turrets require[accent] copper ammo []to shoot.\nPlace a drill next to the turret to supply it with mined copper. +tutorial.waves = The[LIGHT_GRAY] enemy[] approaches.\n\nDefend your core for 2 waves. Build more turrets. +tutorial.lead = More ores are available. Explore and mine[accent] lead[].\n\nDrag from your unit to the core to transfer resources. +tutorial.smelter = Copper and lead are weak metals.\nSuperior[accent] Dense Alloy[] can be created in a smelter.\n\nBuild one. +tutorial.densealloy = The smelter will now produce alloy.\nGet some.\nImprove the production if necessary. +tutorial.siliconsmelter = The core will now create a[accent] spirit drone[] for mining and repairing blocks.\n\nFactories for other units can be created with [accent] silicon.\nMake a silicon smelter. +tutorial.silicondrill = Silicon requires[accent] coal[] and[accent] sand[].\nStart by making drills. +tutorial.generator = This technology requires power.\nCreate a[accent] combustion generator[] for it. +tutorial.generatordrill = Combustion generators need fuel.\nFuel it with coal from a drill. +tutorial.node = Power requires transport.\nCreate a[accent] power node[] next to your combustion generator to transfer its power. +tutorial.nodelink = Power can be transferred through contacting power blocks and generators, or by linked power nodes.\n\nLink power by tapping the node and selecting the generator and silicon smelter. +tutorial.silicon = Silicon is being produced. Get some.\n\nImproving the production system is advised. +tutorial.daggerfactory = Construct a[accent] dagger mech factory.[]\n\nThis will be used to create attack mechs. +tutorial.router = Factories need resources to function.\nCreate a router to split conveyor resources. +tutorial.dagger = Link power nodes to the factory.\nOnce requirements are met, a mech will be created.\n\nCreate more drills, generators and conveyors as necessary. +tutorial.battle = The[LIGHT_GRAY] enemy[] has revealed their core.\nDestroy it with your unit and dagger mechs. +block.copper-wall.description = A cheap defensive block.\nUseful for protecting the core and turrets in the first few waves. +block.copper-wall-large.description = A cheap defensive block.\nUseful for protecting the core and turrets in the first few waves.\nSpans multiple tiles. +block.thorium-wall.description = A strong defensive block.\nGood protection from enemies. +block.thorium-wall-large.description = A strong defensive block.\nGood protection from enemies.\nSpans multiple tiles. +block.phase-wall.description = Not as strong as a thorium wall but will deflect bullets unless they are too powerful. +block.phase-wall-large.description = Not as strong as a thorium wall but will deflect bullets unless they are too powerful.\nSpans multiple tiles. +block.surge-wall.description = The strongest defensive block.\nHas a small chance of triggering lightning towards the attacker. +block.surge-wall-large.description = The strongest defensive block.\nHas a small chance of triggering lightning towards the attacker.\nSpans multiple tiles. +block.door.description = A small door that can be opened and closed by tapping on it.\nIf opened, enemies can shoot and move through. +block.door-large.description = A large door that can be opened and closed by tapping on it.\nIf opened, enemies can shoot and move through.\nSpans multiple tiles. +block.mend-projector.description = Periodically heals buildings in its vicinity. +block.overdrive-projector.description = Increases the speed of nearby buildings like drills and conveyors. +block.force-projector.description = Creates a hexagonal force field around itself, protecting buildings and units inside from damage through bullets. +block.shock-mine.description = Damages enemies stepping on the mine. Nearly invisible to the enemy. +block.duo.description = A small, cheap turret. +block.scatter.description = A medium-sized anti-air turret. Sprays clumps of lead or scrap flak at enemy units. +block.arc.description = A small turret which shoots electricity in a random arc towards the enemy. +block.hail.description = A small artillery turret. +block.lancer.description = A medium-sized turret which shoots charged electricity beams. +block.wave.description = A medium-sized rapid-fire turret which shoots liquid bubbles. +block.salvo.description = A medium-sized turret which fires shots in salvos. +block.swarmer.description = A medium-sized turret which shoots burst missiles. +block.ripple.description = A large artillery turret which fires several shots simultaneously. +block.cyclone.description = A large rapid fire turret. +block.fuse.description = A large turret which shoots powerful short-range beams. +block.spectre.description = A large turret which shoots two powerful bullets at once. +block.meltdown.description = A large turret which shoots powerful long-range beams. +block.conveyor.description = Basic item transport block. Moved items forward and automatically deposits them into turrets or crafters. Rotatable. +block.titanium-conveyor.description = Advanced item transport block. Moves items faster than standard conveyors. +block.phase-conveyor.description = Advanced item transport block. Uses power to teleport items to a connected phase conveyor over several tiles. +block.junction.description = Acts as a bridge for two crossing conveyor belts. Useful in situations with two different conveyors carrying different materials to different locations. +block.mass-driver.description = Ultimate item transport block. Collects several items and then shoots them to another mass driver over a long range. +block.silicon-smelter.description = Reduces sand with highly pure coke in order to produce silicon. +block.plastanium-compressor.description = Produces plastanium from oil and titanium. +block.phase-weaver.description = Produces phase fabric from radioactive thorium and high amounts of sand. +block.alloy-smelter.description = Produces surge alloy from titanium, lead, silicon and copper. +block.pulverizer.description = Crushes stone into sand. Useful when there is a lack of natural sand. +block.pyratite-mixer.description = Mixes coal, lead and sand into highly flammable pyratite. +block.blast-mixer.description = Uses oil for transforming pyratite into the less flammable but more explosive blast compound. +block.cryofluidmixer.description = Combines water and titanium into cryofluid which is much more efficient for cooling. +block.melter.description = Heats up stone to very high temperatures to obtain lava. +block.incinerator.description = Gets rid of any excess item or liquid. +block.spore-press.description = Compresses spore pods into oil. +block.separator.description = Exposes stone to water pressure in order to obtain various minerals contained in the stone. +block.power-node.description = Transmits power to connected nodes. Up to four power sources, sinks or nodes can be connected. The node will receive power from or supply power to any adjacent blocks. +block.power-node-large.description = Has a larger radius than the power node and connects to up to six power sources, sinks or nodes. +block.battery.description = Stores power whenever there is an abundance and provides power whenever there is a shortage, as long as there is capacity left. +block.battery-large.description = Stores much more power than a regular battery. +block.combustion-generator.description = Generates power by burning oil or flammable materials. +block.turbine-generator.description = More efficient than a combustion generator, but requires additional water. +block.thermal-generator.description = Generates a large amount of power from lava. +block.solar-panel.description = Provides a small amount of power from the sun. +block.solar-panel-large.description = Provides much better power supply than a standard solar panel, but is also much more expensive to build. +block.thorium-reactor.description = Generates huge amounts of power from highly radioactive thorium. Requires constant cooling. Will explode violently if insufficient amounts of coolant are supplied. +block.rtg-generator.description = A radioisotope thermoelectric generator which does not require cooling but provides less power than a thorium reactor. +block.unloader.description = Unloads items from a container, vault or core onto a conveyor or directly into an adjacent block. The type of item to be unloaded can be changed by tapping on the unloader. +block.container.description = Stores a small amount of items. Use it for creating buffers when there is a non-constant demand of materials. An[LIGHT_GRAY] unloader[] can be used to retrieve items from the container. +block.vault.description = Stores a large amount of items. Use it for creating buffers when there is a non-constant demand of materials. An[LIGHT_GRAY] unloader[] can be used to retrieve items from the vault. +block.mechanical-drill.description = A cheap drill. When placed on appropriate tiles, outputs items at a slow pace indefinitely. +block.pneumatic-drill.description = An improved drill which is faster and able to process harder materials by making use of air pressure. +block.laser-drill.description = Allows drilling even faster through laser technology, but requires power. Additionally, radioactive thorium can be retrieved with this drill. +block.blast-drill.description = The ultimate drill. Requires large amounts of power. +block.water-extractor.description = Extracts water from the ground. Use it when there is no lake nearby. +block.cultivator.description = Cultivates the soil with water in order to obtain biomatter. +block.oil-extractor.description = Uses large amounts of power in order to extract oil from sand. Use it when there is no direct source of oil nearby. +block.trident-ship-pad.description = Leave your current vessel and change into a reasonably well armored heavy bomber.\nUse the pad by double tapping while standing on it. +block.javelin-ship-pad.description = Leave your current vessel and change into a strong and fast interceptor with lightning weapons.\nUse the pad by double tapping while standing on it. +block.glaive-ship-pad.description = Leave your current vessel and change into a large, well-armored gunship.\nUse the pad by double tapping while standing on it. +block.tau-mech-pad.description = Leave your current vessel and change into a support mech which can heal friendly buildings and units.\nUse the pad by double tapping while standing on it. +block.delta-mech-pad.description = Leave your current vessel and change into a fast, lightly-armored mech made for hit-and-run attacks.\nUse the pad by double tapping while standing on it. +block.omega-mech-pad.description = Leave your current vessel and change into a bulky and well-armored mech, made for front-line assaults.\nUse the pad by double tapping while standing on it. +block.spirit-factory.description = Produces light drones which mine ore and repair blocks. +block.phantom-factory.description = Produces advanced drone units which are significantly more effective than a spirit drone. +block.wraith-factory.description = Produces fast, hit-and-run interceptor units. +block.ghoul-factory.description = Produces heavy carpet bombers. +block.dagger-factory.description = Produces basic ground units. +block.titan-factory.description = Produces advanced, armored ground units. +block.fortress-factory.description = Produces heavy artillery ground units. +block.revenant-factory.description = Produces heavy laser ground units. +block.repair-point.description = Continuously heals the closest damaged unit in its vicinity. +block.conduit.description = Basic liquid transport block. Works like a conveyor, but with liquids. Best used with extractors, pumps or other conduits. +block.pulse-conduit.description = Advanced liquid transport block. Transports liquids faster and stores more than standard conduits. +block.phase-conduit.description = Advanced liquid transport block. Uses power to teleport liquids to a connected phase conduit over several tiles. +block.liquid-router.description = Accepts liquids from one direction and outputs them to up to 3 other directions equally. Can also store a certain amount of liquid. Useful for splitting the liquids from one source to multiple targets. +block.liquid-tank.description = Stores a large amount of liquids. Use it for creating buffers when there is a non-constant demand of materials or as a safeguard for cooling vital blocks. +block.liquid-junction.description = Acts as a bridge for two crossing conduits. Useful in situations with two different conduits carrying different liquids to different locations. +block.bridge-conduit.description = Advanced liquid transport block. Allows transporting liquids over up to 3 tiles of any terrain or building. +block.mechanical-pump.description = A cheap pump with slow output, but no power consumption. +block.rotary-pump.description = An advanced pump which doubles up speed by using power. +block.thermal-pump.description = The ultimate pump. Three times as fast as a mechanical pump and the only pump which is able to retrieve lava. +block.router.description = Accepts items from one direction and outputs them to up to 3 other directions equally. Useful for splitting the materials from one source to multiple targets. +block.distributor.description = An advanced router which splits items to up to 7 other directions equally. +block.bridge-conveyor.description = Advanced item transport block. Allows transporting items over up to 3 tiles of any terrain or building. +block.item-source.description = Infinitely outputs items. Sandbox only. +block.liquid-source.description = Infinitely outputs liquids. Sandbox only. +block.item-void.description = Destroys any items which go into it without using power. Sandbox only. +block.power-source.description = Infinitely outputs power. Sandbox only. +block.power-void.description = Voids all power inputted into it. Sandbox only. +liquid.water.description = Commonly used for cooling machines and waste processing. +liquid.oil.description = Can be burnt, exploded or used as a coolant. +liquid.cryofluid.description = The most efficient liquid for cooling things down. diff --git a/core/assets/bundles/bundle_tr.properties b/core/assets/bundles/bundle_tr.properties new file mode 100644 index 0000000000..7aea9e7b65 --- /dev/null +++ b/core/assets/bundles/bundle_tr.properties @@ -0,0 +1,947 @@ +credits.text = Created by [ROYAL]Anuken[] - [SKY]anukendev@gmail.com[]\n\n[GRAY](In case you can't tell, this text is currently unfinished.\nTranslators, don't edit it yet!) +credits = Yapımcılar +contributors = Translators and Contributors +discord = Mindustry Discord'una katılın! +link.discord.description = Resmi Mindustry Discord iletişim kanalı +link.github.description = Oyunun kaynak kodu +link.dev-builds.description = Geliştirme altında olan sürüm +link.trello.description = Planlanan özellikler için resmi Trello Bülteni +link.itch.io.description = PC yüklemeleri ve web sürümü ile itch.io sayfası +link.google-play.description = Google Play mağaza sayfası +link.wiki.description = Resmi Mindustry Wikipedi'si +linkfail = Bağlantı açılamadı! URL, yazı tahtanıza kopyalandı. +screenshot = Screenshot saved to {0} +screenshot.invalid = Map too large, potentially not enough memory for screenshot. +gameover = Çekirdek yok edildi. +gameover.pvp = [accent] {0}[] takimi kazandi ! +highscore = [SARI] Yeni yüksek puan! +stat.wave = Waves Defeated:[accent] {0} +stat.enemiesDestroyed = Enemies Destroyed:[accent] {0} +stat.built = Buildings Built:[accent] {0} +stat.destroyed = Buildings Destroyed:[accent] {0} +stat.deconstructed = Buildings Deconstructed:[accent] {0} +stat.delivered = Resources Launched: +stat.rank = Final Rank: [accent]{0} +placeline = You have selected a block.\nYou can[accent] place in a line[] by[accent] holding down your finger for a few seconds[] and dragging in a direction.\nTry it. +removearea = You have selected removal mode.\nYou can[accent] remove blocks in a rectangle[] by[accent] holding down your finger for a few seconds[] and dragging.\nTry it. +launcheditems = [accent]Launched Items +map.delete = Su haritayi silmek istedigine emin misin? "[orange]{0}[]"? +level.highscore = Yüksek Puan: [accent] {0} +level.select = Seviye Seç +level.mode = Oyun Modu +showagain = Bunu gene gosterme +coreattack = < Cekirdek saldiri altinda! > +nearpoint = [[ [scarlet]LEAVE DROP POINT IMMEDIATELY[] ]\nannihilation imminent +outofbounds = [[ OUT OF BOUNDS ]\n[]self-destruct in {0} +database = Core Database +savegame = Oyunu Kaydet +loadgame = Oyunu yükle +joingame = Oyuna katıl +addplayers = Oyuncu ekle/cikar +customgame = Ozel oyun +newgame = New Game +none = +minimap = Minimap +close = Kapat +quit = Çık +maps = Haritalar +continue = Devam et +maps.none = [LIGHT_GRAY]Harita bulunamadi! +about.button = Hakkında +name = Adı: +noname = Pick a[accent] player name[] first. +filename = File Name: +unlocked = New Block Unlocked! +completed = [accent]Completed +techtree = Tech Tree +research.list = [LIGHT_GRAY]Research: +research = Research +researched = [LIGHT_GRAY]{0} researched. +players = 1090 oyuncu çevrimiçi +players.single = {0} Oyuncu Çevrimiçi +server.closing = [accent] Sunucu kapatılıyor ... +server.kicked.kick = Sunucudan kovuldun! +server.kicked.serverClose = Server closed. +server.kicked.clientOutdated = Oyun sürümünüz geçerli değil. Oyununu güncelleyin! +server.kicked.serverOutdated = Eski sunucu! Ev sahibinden güncellemesini isteyin! +server.kicked.banned = Bu sunucudan yasaklandınız. +server.kicked.recentKick = Son zamanlarda tekmelendin. Tekrar bağlanmadan önce bekleyin. +server.kicked.nameInUse = There is someone with that name\nalready on this server. +server.kicked.nameEmpty = Your name must contain at least one character or number. +server.kicked.idInUse = You are already on this server! Connecting with two accounts is not permitted. +server.kicked.customClient = This server does not support custom builds. Download an official version. +server.kicked.gameover = Game over! +host.info = [Vurgu] ana bilgisayarı [] düğmesi, [657] [65] [65] ve [65] [6568] bağlantı noktalarında bir sunucuyu barındırır. [] Aynı [LIGHT_GRAY] wifi veya yerel ağ [] üzerindeki herkes sunucunuzu sunucularında görebilir. liste. Kişilerin IP tarafından herhangi bir yerden bağlanabilmesini istiyorsanız [vurgu] bağlantı noktası iletme [] gereklidir. [LIGHT_GRAY] Not: Birisi LAN oyununuza bağlanırken sorun yaşıyorsa, güvenlik duvarı ayarlarınızda Mindustry'e yerel ağınıza erişebildiğinizden emin olun. +join.info = Burada, bağlanmak için yerel ağ [] sunucularına bağlanmak ya da [aksan] sunucularını bulmak için bir [vurgu] sunucunun IP [] girebilirsiniz. Hem LAN hem de WAN çok oyunculu desteklenir. [LIGHT_GRAY] Not: Otomatik bir global sunucu listesi yoktur; Birisine IP ile bağlanmak isterseniz, ana bilgisayardan kendi IP adreslerini sormanız gerekir. +hostserver = Oyunu Sun +hostserver.mobile = Host\nGame +host = evsahibi +hosting = [accent] Sunucu açılıyor ... +hosts.refresh = Yenile +hosts.discovering = LAN oyunlarını keşfetme +server.refreshing = Canlandırıcı sunucu +hosts.none = [lightgray] Hayır LAN oyunları bulundu! +host.invalid = [scarlet] Ana bilgisayara bağlanılamıyor. +trace = Oyuncuyu Takip Et +trace.playername = Oyuncu adı: [accent] {0} +trace.ip = IP: [vurgu] {0} +trace.id = Benzersiz kimlik: [accent] {0} +trace.mobile = Mobile Client: [accent]{0} +trace.modclient = Özel Alıcı: [accent] {0} +invalidid = Geçersiz alıcı kimliği! Bir hata raporu gönderin. +server.bans = yasaklar +server.bans.none = Yasaklanmış oyuncu bulunamadı! +server.admins = Yöneticiler +server.admins.none = Yönetici bulunamadı! +server.add = Sunucu ekle +server.delete = Bu sunucuyu silmek istediğinizden emin misiniz? +server.hostname = Sun +server.edit = Sunucuyu Düzenle +server.outdated = [crimson] Eski Sunucu! +server.outdated.client = [crimson] Eski Alıcı! +server.version = [lightgray] Sürüm: {0} +server.custombuild = [sarı] Özel Yapım +confirmban = Bu oyuncuyu yasaklamak istediğinizden emin misiniz? +confirmkick = Are you sure you want to kick this player? +confirmunban = Bu oyuncunun yasağını kaldırmak istediğinden emin misin? +confirmadmin = Bu oyuncunun yönetici yapmak istediğinden emin misin? +confirmunadmin = Bu oyuncudan yönetici durumunu kaldırmak istediğinizden emin misiniz? +joingame.title = Oyuna katılmak +joingame.ip = IP: +disconnect = Bağlantı Kesildi +disconnect.data = Dünya verileri yüklenemedi! +connecting = [Vurgu] bağlanıyor ... +connecting.data = [accent] Dünya verileri yükleniyor ... +server.port = Liman +server.addressinuse = Adres çoktan kullanımda! +server.invalidport = Bağlantı noktası numarası geçersiz. +server.error = [crimson] Sunucu barındırma hatası: [accent] {0} +save.old = This save is for an older version of the game, and can no longer be used.\n\n[LIGHT_GRAY]Save backwards compatibility will be implemented in the full 4.0 release. +save.new = 6349,Yeni Kayıt +save.overwrite = Bu kayıt yuvasının üzerine yazmak istediğinizden emin misiniz? +overwrite = Üzerine Yaz +save.none = Hiçbir kayıt bulunamadı! +saveload = [Vurgu] Kaydediliyor ... +savefail = Oyun kaydedilemedi! +save.delete.confirm = Bu kaydı silmek istediğinizden emin misiniz? +save.delete = Sil +save.export = Dışa Aktar +save.import.invalid = [turuncu] Bu kayıt geçersiz! +save.import.fail = [crimson] Kayıt oyuna aktarılamadı : [accent] {0} +save.export.fail = [crimson] Kayıt dışa aktarılamadı: [accent] {0} +save.import = İçe Aktar +save.newslot = İsmi kaydet: +save.rename = Yeniden Adlandır +save.rename.text = Yeni İsim: +selectslot = Bir kayıt seçin. +slot = [accent] Yuva {0} +save.corrupted = [accent] Kayıt dosyası bozuk veya geçersiz! +empty = +on = Açık +off = Kapalı +save.autosave = Otomatik kaydetme: {0} +save.map = harita +save.wave = Dalga +save.difficulty = zorluk +save.date = Son Kaydedilen: {0} +save.playtime = Playtime: {0} +warning = Warning. +confirm = Onayla +delete = Sil +ok = Tamam +open = Açık +customize = Customize +cancel = İptal +openlink = Linki aç +copylink = Bağlantıyı kopyala +back = Geri +quit.confirm = Çıkmak istediğinden emin misin? +changelog.title = Değişiklik listesi +changelog.loading = Değişiklik listesi yükleniyor +changelog.error.android = [turuncu] Android'da olan hata nedeniyle değişiklik listesi görüntülenemiyor. +changelog.error.ios = [accent]The changelog is currently not supported in iOS. +changelog.error = [scarlet] Değişiklik listesi alma hatası! İnternet bağlantınızı kontrol edin. +changelog.current = [sarı] [[Güncel versiyon] +changelog.latest = [turuncu] [[Son sürüm] +loading = [Vurgu] Yükleniyor ... +saving = [accent]Saving... +wave = [turuncu] Dalga {0} +wave.waiting = {0} içinde dalga +wave.waveInProgress = [LIGHT_GRAY]Wave in progress +waiting = Bekleniyor +waiting.players = Waiting for players... +wave.enemies = [LIGHT_GRAY]{0} Enemies Remaining +wave.enemy = [LIGHT_GRAY]{0} Enemy Remaining +loadimage = Resmi yükle +saveimage = Resmi Kaydet +unknown = Unknown +custom = Custom +builtin = Built-In +map.delete.confirm = Are you sure you want to delete this map? This action cannot be undone! +map.random = [accent]Random Map +map.nospawn = This map does not have any cores for the player to spawn in! Add a [ROYAL]blue[] core to this map in the editor. +map.nospawn.pvp = This map does not have any enemy cores for player to spawn into! Add[SCARLET] red[] cores to this map in the editor. +map.nospawn.attack = This map does not have any enemy cores for player to attack! Add[SCARLET] red[] cores to this map in the editor. +map.invalid = Error loading map: corrupted or invalid map file. +editor.brush = Brush +editor.openin = Open In Editor +editor.oregen = Ore Generation +editor.oregen.info = Ore Generation: +editor.mapinfo = Map Info +editor.author = Author: +editor.description = Description: +editor.waves = Waves: +editor.rules = Rules: +editor.ingame = Edit In-Game +waves.title = Waves +waves.remove = Remove +waves.never = +waves.every = every +waves.waves = wave(s) +waves.perspawn = per spawn +waves.to = to +waves.boss = Boss +waves.preview = Preview +waves.edit = Edit... +waves.copy = Copy to Clipboard +waves.load = Load from Clipboard +waves.invalid = Invalid waves in clipboard. +waves.copied = Waves copied. +editor.default = [LIGHT_GRAY] +edit = Edit... +editor.name = Name: +editor.spawn = Spawn Unit +editor.removeunit = Remove Unit +editor.teams = Teams +editor.elevation = Elevation +editor.errorload = Error loading file:\n[accent]{0} +editor.errorsave = Error saving file:\n[accent]{0} +editor.errorimage = That's an image, not a map. Don't go around changing extensions expecting it to work.\n\nIf you want to import a legacy map, use the 'import legacy map' button in the editor. +editor.errorlegacy = This map is too old, and uses a legacy map format that is no longer supported. +editor.errorheader = This map file is either not valid or corrupt. +editor.errorname = Map has no name defined. +editor.update = Update +editor.randomize = Randomize +editor.apply = Apply +editor.generate = Üretmek +editor.resize = Yeniden Boyutlandırma +editor.loadmap = Harita Yükle +editor.savemap = Harita Kaydet +editor.saved = Saved! +editor.save.noname = Your map does not have a name! Set one in the 'map info' menu. +editor.save.overwrite = Your map overwrites a built-in map! Pick a different name in the 'map info' menu. +editor.import.exists = [scarlet]Unable to import:[] a built-in map named '{0}' already exists! +editor.import = Import... +editor.importmap = Import Map +editor.importmap.description = Import an already existing map +editor.importfile = Import File +editor.importfile.description = Import an external map file +editor.importimage = Import Terrain Image +editor.importimage.description = Import an external map image file +editor.export = Export... +editor.exportfile = Export File +editor.exportfile.description = Export a map file +editor.exportimage = Export Terrain Image +editor.exportimage.description = Export a map image file +editor.loadimage = Resmi yükle +editor.saveimage = Resmi Kaydet +editor.unsaved = [scarlet] Kaydedilmemiş değişiklikleriniz var! [] Çıkmak istediğinizden emin misiniz? +editor.resizemap = Haritayı Yeniden Boyutlandır +editor.mapname = Harita Adı +editor.overwrite = [Vurgu] Uyarı! Bu mevcut bir haritanın üzerine yazar. +editor.overwrite.confirm = [scarlet]Warning![] A map with this name already exists. Are you sure you want to overwrite it? +editor.selectmap = Yüklenecek bir harita seçin: +filters.empty = [LIGHT_GRAY]No filters! Add one with the button below. +filter.distort = Distort +filter.noise = Noise +filter.ore = Ore +filter.rivernoise = River Noise +filter.scatter = Scatter +filter.terrain = Terrain +filter.option.scale = Scale +filter.option.chance = Chance +filter.option.mag = Magnitude +filter.option.threshold = Threshold +filter.option.circle-scale = Circle Scale +filter.option.octaves = Octaves +filter.option.falloff = Falloff +filter.option.block = Block +filter.option.floor = Floor +filter.option.wall = Wall +filter.option.ore = Ore +filter.option.floor2 = Secondary Floor +filter.option.threshold2 = Secondary Threshold +filter.option.radius = Radius +filter.option.percentile = Percentile +width = Genişliği: +height = Boy: +menu = Menü +play = Oyna +load = Yükle +save = Kaydet +fps = FPS: {0} +tps = TPS: {0} +ping = Ping: {0}ms +language.restart = Lütfen dil ayarlarının etkili olması için oyununuzu yeniden başlatın. +settings = Ayarlar +tutorial = Eğitim +editor = Editör +mapeditor = Harita Editörü +donate = Bağışlamak +abandon = Abandon +abandon.text = This zone and all its resources will be lost to the enemy. +locked = Locked +complete = [LIGHT_GRAY]Complete: +zone.requirement = Wave {0} in zone {1} +resume = Resume Zone:\n[LIGHT_GRAY]{0} +bestwave = [LIGHT_GRAY]Best: {0} +launch = Launch +launch.title = Launch Successful +launch.next = [LIGHT_GRAY]next opportunity at wave {0} +launch.unable = [scarlet]Unable to LAUNCH.[] Enemies. +launch.confirm = This will launch all resources in your core.\nYou will not be able to return to this base. +uncover = Uncover +configure = Configure Loadout +configure.locked = [LIGHT_GRAY]Reach wave {0}\nto configure loadout. +zone.unlocked = [LIGHT_GRAY]{0} unlocked. +zone.requirement.complete = Wave {0} reached:\n{1} zone requirements met. +zone.config.complete = Wave {0} reached:\nLoadout config unlocked. +zone.resources = Resources Detected: +add = Add... +boss.health = Boss Health +connectfail = [crimson] Sunucuya bağlanılamadı: [accent] {0} +error.unreachable = Server unreachable. +error.invalidaddress = Invalid address. +error.timedout = Timed out!\nMake sure the host has port forwarding set up, and that the address is correct! +error.mismatch = Packet error:\npossible client/server version mismatch.\nMake sure you and the host have the latest version of Mindustry! +error.alreadyconnected = Already connected. +error.mapnotfound = Map file not found! +error.io = Network I/O error. +error.any = Unkown network error. +zone.groundZero.name = Ground Zero +zone.desertWastes.name = Desert Wastes +zone.craters.name = The Craters +zone.frozenForest.name = Frozen Forest +zone.ruinousShores.name = Ruinous Shores +zone.stainedMountains.name = Stained Mountains +zone.desolateRift.name = Desolate Rift +zone.nuclearComplex.name = Nuclear Production Complex +zone.overgrowth.name = Overgrowth +zone.tarFields.name = Tar Fields +settings.language = Dil +settings.reset = Varsayılanlara Dön +settings.rebind = Rebind +settings.controls = kontroller +settings.game = Oyun +settings.sound = Ses +settings.graphics = Grafik +settings.cleardata = Clear Game Data... +settings.clear.confirm = Are you sure you want to clear this data?\nWhat is done cannot be undone! +settings.clearall.confirm = [scarlet]WARNING![]\nThis will clear all data, including saves, maps, unlocks and keybinds.\nOnce you press 'ok' the game will wipe all data and automatically exit. +settings.clearunlocks = Clear Unlocks +settings.clearall = Clear All +paused = Duraklatıldı +yes = Yes +no = No +info.title = [Vurgu] Bilgi +error.title = [crimson] Bir hata oluştu +error.crashtitle = Bir hata oluştu +blocks.input = Input +blocks.output = Output +blocks.booster = Booster +block.unknown = [LIGHT_GRAY]??? +blocks.powercapacity = Güç kapasitesi +blocks.powershot = Güç / atış +blocks.targetsair = Targets Air +blocks.targetsground = Targets Ground +blocks.itemsmoved = Move Speed +blocks.launchtime = Time Between Launches +blocks.shootrange = Range +blocks.size = Boyut +blocks.liquidcapacity = Sıvı kapasitesi +blocks.powerrange = Güç aralığı +blocks.poweruse = Power Use +blocks.powerdamage = Power/Damage +blocks.itemcapacity = Ürün kapasitesi +blocks.basepowergeneration = Base Power Generation +blocks.productiontime = Production Time +blocks.repairtime = Block Full Repair Time +blocks.speedincrease = Speed Increase +blocks.range = Range +blocks.drilltier = Drillables +blocks.drillspeed = Base Drill Speed +blocks.boosteffect = Boost Effect +blocks.maxunits = Max Active Units +blocks.health = Can +blocks.buildtime = Build Time +blocks.inaccuracy = yanlışlık +blocks.shots = atışlar +blocks.reload = Reload +blocks.ammo = Ammo +bar.drillspeed = Drill Speed: {0}/s +bar.efficiency = Efficiency: {0}% +bar.powerbalance = Power: {0} +bar.poweramount = Power: {0} +bar.poweroutput = Power Output: {0} +bar.items = Items: {0} +bar.liquid = Liquid +bar.heat = Heat +bar.power = Power +bar.progress = Build Progress +bar.spawned = Units: {0}/{1} +bullet.damage = [stat]{0}[lightgray] dmg +bullet.splashdamage = [stat]{0}[lightgray] area dmg ~[stat] {1}[lightgray] tiles +bullet.incendiary = [stat]incendiary +bullet.homing = [stat]homing +bullet.shock = [stat]shock +bullet.frag = [stat]frag +bullet.knockback = [stat]{0}[lightgray] knockback +bullet.freezing = [stat]freezing +bullet.tarred = [stat]tarred +bullet.multiplier = [stat]{0}[lightgray]x ammo multiplier +bullet.reload = [stat]{0}[lightgray]x reload +unit.blocks = blocks +unit.powersecond = power units/second +unit.liquidsecond = liquid units/second +unit.itemssecond = items/second +unit.liquidunits = liquid units +unit.powerunits = power units +unit.degrees = degrees +unit.seconds = seconds +unit.persecond = /sec +unit.timesspeed = x speed +unit.percent = % +unit.items = items +category.general = General +category.power = Power +category.liquids = Liquids +category.items = Items +category.crafting = Crafting +category.shooting = Shooting +category.optional = Optional Enhancements +setting.landscape.name = Lock Landscape +setting.shadows.name = Shadows +setting.linear.name = Linear Filtering +setting.animatedwater.name = Animated Water +setting.animatedshields.name = Animated Shields +setting.antialias.name = Antialias[LIGHT_GRAY] (requires restart)[] +setting.indicators.name = Ally Indicators +setting.autotarget.name = Auto-Target +setting.fpscap.name = Max FPS +setting.fpscap.none = None +setting.fpscap.text = {0} FPS +setting.swapdiagonal.name = Always Diagonal Placement +setting.difficulty.training = training +setting.difficulty.easy = kolay +setting.difficulty.normal = orta +setting.difficulty.hard = zor +setting.difficulty.insane = deli +setting.difficulty.name = Zorluk: +setting.screenshake.name = Ekran Sallamak +setting.effects.name = Görüntü Efektleri +setting.sensitivity.name = Denetleyici hassasiyeti +setting.saveinterval.name = Otomatik Kaydetme Aralığı +setting.seconds = saniye +setting.fullscreen.name = Tam ekran +setting.borderlesswindow.name = Borderless Window[LIGHT_GRAY] (may require restart) +setting.fps.name = Saniyede ... Kare göstermek +setting.vsync.name = VSync +setting.lasers.name = Güç Lazerleri Göster +setting.pixelate.name = Pixelate [LIGHT_GRAY](may decrease performance) +setting.minimap.name = Show Minimap +setting.musicvol.name = Müzik sesi +setting.mutemusic.name = Müziği Kapat +setting.sfxvol.name = SFX Hacmi +setting.mutesound.name = Sesi kapat +setting.crashreport.name = Send Anonymous Crash Reports +setting.chatopacity.name = Chat Opacity +setting.playerchat.name = Display In-Game Chat +keybind.title = Tuşları yeniden ayarla +category.general.name = General +category.view.name = View +category.multiplayer.name = Multiplayer +command.attack = Attack +command.retreat = Retreat +command.patrol = Patrol +keybind.gridMode.name = Block Select +keybind.gridModeShift.name = Category Select +keybind.press = Press a key... +keybind.press.axis = Press an axis or key... +keybind.screenshot.name = Map Screenshot +keybind.move_x.name = sağ / sol +keybind.move_y.name = yukarı / aşağı +keybind.select.name = seçmek +keybind.diagonal_placement.name = Diagonal Placement +keybind.pick.name = Pick Block +keybind.break_block.name = Break Block +keybind.deselect.name = Deselect +keybind.shoot.name = ateş etme +keybind.zoom_hold.name = tut ve büyüt +keybind.zoom.name = Yakınlaştır +keybind.menu.name = menü +keybind.pause.name = duraklatma +keybind.minimap.name = Minimap +keybind.dash.name = tire +keybind.chat.name = Sohbet +keybind.player_list.name = oyuncu listesi +keybind.console.name = KONTROL MASASI +keybind.rotate.name = Döndür +keybind.toggle_menus.name = Toggle menus +keybind.chat_history_prev.name = Chat history prev +keybind.chat_history_next.name = Chat history next +keybind.chat_scroll.name = Chat scroll +keybind.drop_unit.name = drop unit +keybind.zoom_minimap.name = Zoom minimap +mode.help.title = Modların açıklaması +mode.survival.name = Survival +mode.survival.description = The normal mode. Limited resources and automatic incoming waves. +mode.sandbox.name = Limitsiz Oynama +mode.sandbox.description = sonsuz kaynaklar ve dalgalar için zamanlayıcı yok. +mode.pvp.name = PvP +mode.pvp.description = fight against other players locally. +mode.attack.name = Attack +mode.attack.description = No waves, with the goal to destroy the enemy base. +mode.custom = Custom Rules +rules.infiniteresources = Infinite Resources +rules.wavetimer = Wave Timer +rules.waves = Waves +rules.enemyCheat = Infinite AI Resources +rules.unitdrops = Unit Drops +rules.unitbuildspeedmultiplier = Unit Creation Speed Multiplier +rules.unithealthmultiplier = Unit Health Multiplier +rules.playerhealthmultiplier = Player Health Multiplier +rules.playerdamagemultiplier = Player Damage Multiplier +rules.unitdamagemultiplier = Unit Damage Multiplier +rules.enemycorebuildradius = Enemy Core No-Build Radius:[LIGHT_GRAY] (tiles) +rules.respawntime = Respawn Time:[LIGHT_GRAY] (sec) +rules.wavespacing = Wave Spacing:[LIGHT_GRAY] (sec) +rules.buildcostmultiplier = Build Cost Multiplier +rules.buildspeedmultiplier = Build Speed Multiplier +rules.waitForWaveToEnd = Waves wait for enemies +rules.dropzoneradius = Drop Zone Radius:[LIGHT_GRAY] (tiles) +rules.respawns = Max respawns per wave +rules.limitedRespawns = Limit Respawns +rules.title.waves = Waves +rules.title.respawns = Respawns +rules.title.resourcesbuilding = Resources & Building +rules.title.player = Players +rules.title.enemy = Enemies +rules.title.unit = Units +content.item.name = Items +content.liquid.name = Liquids +content.unit.name = Units +content.block.name = Blocks +content.mech.name = Mechs +item.copper.name = Copper +item.copper.description = A useful structure material. Used extensively in all types of blocks. +item.lead.name = Lead +item.lead.description = A basic starter material. Used extensively in electronics and liquid transportation blocks. +item.coal.name = kömür +item.coal.description = A common and readily available fuel. +item.graphite.name = Graphite +item.titanium.name = titanyum +item.titanium.description = A rare super-light metal used extensively in liquid transportation, drills and aircraft. +item.thorium.name = Thorium +item.thorium.description = A dense, radioactive metal used as structural support and nuclear fuel. +item.silicon.name = Silicon +item.silicon.description = An extremely useful semiconductor, with applications in solar panels and many complex electronics. +item.plastanium.name = Plastanium +item.plastanium.description = A light, ductile material used in advanced aircraft and fragmentation ammunition. +item.phase-fabric.name = Phase Fabric +item.phase-fabric.description = A near-weightless substance used in advanced electronics and self-repairing technology. +item.surge-alloy.name = Surge Alloy +item.surge-alloy.description = An advanced alloy with unique electrical properties. +item.spore-pod.name = Spore Pod +item.spore-pod.description = Used for conversion into oil, explosives and fuel. +item.sand.name = kum +item.sand.description = A common material that is used extensively in smelting, both in alloying and as a flux. +item.blast-compound.name = Blast Compound +item.blast-compound.description = A volatile compound used in bombs and explosives. While it can burned as fuel, this is not advised. +item.pyratite.name = Pyratite +item.pyratite.description = An extremely flammable substance used in incendiary weapons. +item.metaglass.name = Metaglass +item.metaglass.description = A super-tough glass compound. Extensively used for liquid distribution and storage. +item.scrap.name = Scrap +item.scrap.description = Leftover remnants of old structures and units. Contains trace amounts of many different metals. +liquid.water.name = su +liquid.slag.name = Slag +liquid.oil.name = petrol +liquid.cryofluid.name = Cryofluid +mech.alpha-mech.name = Alpha +mech.alpha-mech.weapon = Heavy Repeater +mech.alpha-mech.ability = Drone Swarm +mech.alpha-mech.description = The standard mech. Has decent speed and damage output; can create up to 3 drones for increased offensive capability. +mech.delta-mech.name = Delta +mech.delta-mech.weapon = Arc Generator +mech.delta-mech.ability = Discharge +mech.delta-mech.description = A fast, lightly-armored mech made for hit-and-run attacks. Does little damage against structures, but can kill large groups of enemy units very quickly with its arc lightning weapons. +mech.tau-mech.name = Tau +mech.tau-mech.weapon = Restruct Laser +mech.tau-mech.ability = Repair Burst +mech.tau-mech.description = The support mech. Heals allied blocks by shooting at them. Can extinguish fires and heal allies in a radius with its repair ability. +mech.omega-mech.name = Omega +mech.omega-mech.weapon = Swarm Missiles +mech.omega-mech.ability = Armored Configuration +mech.omega-mech.description = A bulky and well-armored mech, made for front-line assaults. Its armor ability can block up to 90% of incoming damage. +mech.dart-ship.name = Dart +mech.dart-ship.weapon = Repeater +mech.dart-ship.description = The standard ship. Reasonably fast and light, but has little offensive capability and low mining speed. +mech.javelin-ship.name = Javelin +mech.javelin-ship.description = A hit-and-run strike ship. While initially slow, it can accelerate to great speeds and fly by enemy outposts, dealing large amounts of damage with its lightning ability and missiles. +mech.javelin-ship.weapon = Burst Missiles +mech.javelin-ship.ability = Discharge Booster +mech.trident-ship.name = Trident +mech.trident-ship.description = A heavy bomber. Reasonably well armored. +mech.trident-ship.weapon = Bomb Bay +mech.glaive-ship.name = Glaive +mech.glaive-ship.description = A large, well-armored gunship. Equipped with an incendiary repeater. Good acceleration and maximum speed. +mech.glaive-ship.weapon = Flame Repeater +item.explosiveness = [LIGHT_GRAY]Explosiveness: {0} +item.flammability = [LIGHT_GRAY]Flammability: {0} +item.radioactivity = [LIGHT_GRAY]Radioactivity: {0} +unit.health = [LIGHT_GRAY]Health: {0} +unit.speed = [LIGHT_GRAY]Speed: {0} +mech.weapon = [LIGHT_GRAY]Weapon: {0} +mech.health = [LIGHT_GRAY]Health: {0} +mech.itemcapacity = [LIGHT_GRAY]Item Capacity: {0} +mech.minespeed = [LIGHT_GRAY]Mining Speed: {0} +mech.minepower = [LIGHT_GRAY]Mining Power: {0} +mech.ability = [LIGHT_GRAY]Ability: {0} +mech.buildspeed = [LIGHT_GRAY]Building Speed: {0}% +liquid.heatcapacity = [LIGHT_GRAY]Heat Capacity: {0} +liquid.viscosity = [LIGHT_GRAY]Viscosity: {0} +liquid.temperature = [LIGHT_GRAY]Temperature: {0} +block.grass.name = Grass +block.salt.name = Salt +block.saltrocks.name = Salt Rocks +block.pebbles.name = Pebbles +block.tendrils.name = Tendrils +block.sandrocks.name = Sand Rocks +block.spore-pine.name = Spore Pine +block.sporerocks.name = Spore Rocks +block.rock.name = Rock +block.snowrock.name = Snow Rock +block.shale.name = Shale +block.shale-boulder.name = Shale Boulder +block.moss.name = Moss +block.shrubs.name = Shrubs +block.spore-moss.name = Spore Moss +block.shalerocks.name = Shale Rocks +block.scrap-wall.name = Scrap Wall +block.scrap-wall-large.name = Large Scrap Wall +block.scrap-wall-huge.name = Huge Scrap Wall +block.scrap-wall-gigantic.name = Gigantic Scrap Wall +block.thruster.name = Thruster +block.kiln.name = Kiln +block.kiln.description = Smelts sand and lead into metaglass. Requires small amounts of power. +block.graphite-press.name = Graphite Press +block.multi-press.name = Multi-Press +block.constructing = {0}\n[LIGHT_GRAY](Constructing) +block.spawn.name = Enemy Spawn +block.core-shard.name = Core: Shard +block.core-foundation.name = Core: Foundation +block.core-nucleus.name = Core: Nucleus +block.deepwater.name = deepwater +block.water.name = water +block.tainted-water.name = Tainted Water +block.darksand-tainted-water.name = Dark Sand Tainted Water +block.tar.name = Tar +block.stone.name = stone +block.sand.name = sand +block.darksand.name = Dark Sand +block.ice.name = ice +block.snow.name = snow +block.craters.name = Craters +block.sand-water.name = Sand water +block.darksand-water.name = Dark Sand Water +block.char.name = Char +block.holostone.name = Holo stone +block.ice-snow.name = Ice Snow +block.rocks.name = Rocks +block.icerocks.name = Ice rocks +block.snowrocks.name = Snow Rocks +block.dunerocks.name = Dune Rocks +block.pine.name = Pine +block.white-tree-dead.name = White Tree Dead +block.white-tree.name = White Tree +block.spore-cluster.name = Spore Cluster +block.metal-floor.name = Metal Floor +block.metal-floor-2.name = Metal Floor 2 +block.metal-floor-3.name = Metal Floor 3 +block.metal-floor-5.name = Metal Floor 5 +block.metal-floor-damaged.name = Metal Floor Damaged +block.dark-panel-1.name = Dark Panel 1 +block.dark-panel-2.name = Dark Panel 2 +block.dark-panel-3.name = Dark Panel 3 +block.dark-panel-4.name = Dark Panel 4 +block.dark-panel-5.name = Dark Panel 5 +block.dark-panel-6.name = Dark Panel 6 +block.dark-metal.name = Dark Metal +block.ignarock.name = Igna Rock +block.hotrock.name = Hot Rock +block.magmarock.name = Magma Rock +block.cliffs.name = Cliffs +block.copper-wall.name = Copper Wall +block.copper-wall-large.name = Large Copper Wall +block.titanium-wall.name = Titanium Wall +block.titanium-wall-large.name = Large Titanium Wall +block.phase-wall.name = Phase Wall +block.phase-wall-large.name = Large Phase Wall +block.thorium-wall.name = Thorium Wall +block.thorium-wall-large.name = Large Thorium Wall +block.door.name = kapı +block.door-large.name = büyük kapı +block.duo.name = Duo +block.scorch.name = Scorch +block.scatter.name = Scatter +block.hail.name = Hail +block.lancer.name = Lancer +block.conveyor.name = konveyör +block.titanium-conveyor.name = Titanium Conveyor +block.junction.name = Kavşak noktası +block.router.name = yönlendirici +block.distributor.name = Distributor +block.sorter.name = ayrıştırıcı +block.sorter.description = Sorts items. If an item matches the selection, it is allowed to pass. Otherwise, the item is outputted to the left and right. +block.overflow-gate.name = Overflow Gate +block.overflow-gate.description = A combination splitter and router that only outputs to the left and right if the front path is blocked. +block.silicon-smelter.name = Silicon Smelter +block.phase-weaver.name = Phase Weaver +block.pulverizer.name = Pulverizer +block.cryofluidmixer.name = Cryofluid Mixer +block.melter.name = Melter +block.incinerator.name = Incinerator +block.spore-press.name = Spore Press +block.separator.name = Separator +block.coal-centrifuge.name = Coal Centrifuge +block.power-node.name = Power Node +block.power-node-large.name = Large Power Node +block.surge-tower.name = Surge Tower +block.battery.name = Battery +block.battery-large.name = Large Battery +block.combustion-generator.name = Combustion Generator +block.turbine-generator.name = Turbine Generator +block.differential-generator.name = Differential Generator +block.impact-reactor.name = Impact Reactor +block.mechanical-drill.name = Mechanical Drill +block.pneumatic-drill.name = Pneumatic Drill +block.laser-drill.name = Laser Drill +block.water-extractor.name = Water Extractor +block.cultivator.name = Cultivator +block.dart-mech-pad.name = Dart Mech Pad +block.delta-mech-pad.name = Delta Mech Pad +block.javelin-ship-pad.name = Javelin Ship Pad +block.trident-ship-pad.name = Trident Ship Pad +block.glaive-ship-pad.name = Glaive Ship Pad +block.omega-mech-pad.name = Omega Mech Pad +block.tau-mech-pad.name = Tau Mech Pad +block.conduit.name = sıvı borusu +block.mechanical-pump.name = Mechanical Pump +block.item-source.name = Item Source +block.item-void.name = Item Void +block.liquid-source.name = Liquid Source +block.power-void.name = Power Void +block.power-source.name = Power Infinite +block.unloader.name = Unloader +block.vault.name = Vault +block.wave.name = Wave +block.swarmer.name = Swarmer +block.salvo.name = Salvo +block.ripple.name = Ripple +block.phase-conveyor.name = Phase Conveyor +block.bridge-conveyor.name = Bridge Conveyor +block.plastanium-compressor.name = Plastanium Compressor +block.pyratite-mixer.name = Pyratite Mixer +block.blast-mixer.name = Blast Mixer +block.solar-panel.name = Solar Panel +block.solar-panel-large.name = Large Solar Panel +block.oil-extractor.name = Oil Extractor +block.spirit-factory.name = Spirit Drone Factory +block.phantom-factory.name = Phantom Drone Factory +block.wraith-factory.name = Wraith Fighter Factory +block.ghoul-factory.name = Ghoul Bomber Factory +block.dagger-factory.name = Dagger Mech Factory +block.crawler-factory.name = Crawler Mech Factory +block.titan-factory.name = Titan Mech Factory +block.fortress-factory.name = Fortress Mech Factory +block.revenant-factory.name = Revenant Fighter Factory +block.repair-point.name = Repair Point +block.pulse-conduit.name = Pulse Conduit +block.phase-conduit.name = Phase Conduit +block.liquid-router.name = Liquid Router +block.liquid-tank.name = Liquid Tank +block.liquid-junction.name = Liquid Junction +block.bridge-conduit.name = Bridge Conduit +block.rotary-pump.name = Rotary Pump +block.thorium-reactor.name = Thorium Reactor +block.mass-driver.name = Mass Driver +block.blast-drill.name = Blast Drill +block.thermal-pump.name = Thermal Pump +block.thermal-generator.name = Thermal Generator +block.alloy-smelter.name = Alloy Smtler +block.mender.name = Mender +block.mend-projector.name = Mend Projector +block.surge-wall.name = Surge Wall +block.surge-wall-large.name = Large Surge Wall +block.cyclone.name = Cyclone +block.fuse.name = Fuse +block.shock-mine.name = Shock Mine +block.overdrive-projector.name = Overdrive Projector +block.force-projector.name = Force Projector +block.arc.name = Arc +block.rtg-generator.name = RTG Generator +block.spectre.name = Spectre +block.meltdown.name = Meltdown +block.container.name = Container +block.launch-pad.name = Launch Pad +block.launch-pad.description = Launches batches of items without any need for a core launch. Unfinished. +block.launch-pad-large.name = Large Launch Pad +team.blue.name = blue +team.red.name = red +team.orange.name = orange +team.none.name = gray +team.green.name = green +team.purple.name = purple +unit.spirit.name = Spirit Drone +unit.spirit.description = The starter drone unit. Spawns in the core by default. Automatically mines ores, collects items and repairs blocks. +unit.phantom.name = Phantom Drone +unit.phantom.description = An advanced drone unit. Automatically mines ores, collects items and repairs blocks. Significantly more effective than a drone. +unit.dagger.name = Dagger +unit.dagger.description = A basic ground unit. Useful in swarms. +unit.crawler.name = Crawler +unit.titan.name = Titan +unit.titan.description = An advanced armored ground unit. Uses carbide as ammo. Attacks both ground and air targets. +unit.ghoul.name = Ghoul Bomber +unit.ghoul.description = A heavy carpet bomber. Uses blast compound or pyratite as ammo. +unit.wraith.name = Wraith Fighter +unit.wraith.description = A fast, hit-and-run interceptor unit. +unit.fortress.name = Fortress +unit.fortress.description = A heavy artillery ground unit. +unit.revenant.name = Revenant +unit.eruptor.name = Eruptor +unit.chaos-array.name = Chaos Array +unit.eradicator.name = Eradicator +unit.lich.name = Lich +unit.reaper.name = Reaper +tutorial.begin = Your mission here is to eradicate the[LIGHT_GRAY] enemy[].\n\nBegin by[accent] mining copper[]. Tap a copper ore vein near your core to do this. +tutorial.drill = Mining manually is inefficient.\n[accent]Drills []can mine automatically.\nPlace one on a copper vein. +tutorial.conveyor = [accent]Conveyors[] are used to transport items to the core.\nMake a line of conveyors from the drill to the core. +tutorial.morecopper = More copper is required.\n\nEither mine it manually, or place more drills. +tutorial.turret = Defensive structures must be built to repel the[LIGHT_GRAY] enemy[].\nBuild a duo turret near your base. +tutorial.drillturret = Duo turrets require[accent] copper ammo []to shoot.\nPlace a drill next to the turret to supply it with mined copper. +tutorial.waves = The[LIGHT_GRAY] enemy[] approaches.\n\nDefend your core for 2 waves. Build more turrets. +tutorial.lead = More ores are available. Explore and mine[accent] lead[].\n\nDrag from your unit to the core to transfer resources. +tutorial.smelter = Copper and lead are weak metals.\nSuperior[accent] Dense Alloy[] can be created in a smelter.\n\nBuild one. +tutorial.densealloy = The smelter will now produce alloy.\nGet some.\nImprove the production if necessary. +tutorial.siliconsmelter = The core will now create a[accent] spirit drone[] for mining and repairing blocks.\n\nFactories for other units can be created with [accent] silicon.\nMake a silicon smelter. +tutorial.silicondrill = Silicon requires[accent] coal[] and[accent] sand[].\nStart by making drills. +tutorial.generator = This technology requires power.\nCreate a[accent] combustion generator[] for it. +tutorial.generatordrill = Combustion generators need fuel.\nFuel it with coal from a drill. +tutorial.node = Power requires transport.\nCreate a[accent] power node[] next to your combustion generator to transfer its power. +tutorial.nodelink = Power can be transferred through contacting power blocks and generators, or by linked power nodes.\n\nLink power by tapping the node and selecting the generator and silicon smelter. +tutorial.silicon = Silicon is being produced. Get some.\n\nImproving the production system is advised. +tutorial.daggerfactory = Construct a[accent] dagger mech factory.[]\n\nThis will be used to create attack mechs. +tutorial.router = Factories need resources to function.\nCreate a router to split conveyor resources. +tutorial.dagger = Link power nodes to the factory.\nOnce requirements are met, a mech will be created.\n\nCreate more drills, generators and conveyors as necessary. +tutorial.battle = The[LIGHT_GRAY] enemy[] has revealed their core.\nDestroy it with your unit and dagger mechs. +block.copper-wall.description = A cheap defensive block.\nUseful for protecting the core and turrets in the first few waves. +block.copper-wall-large.description = A cheap defensive block.\nUseful for protecting the core and turrets in the first few waves.\nSpans multiple tiles. +block.thorium-wall.description = A strong defensive block.\nGood protection from enemies. +block.thorium-wall-large.description = A strong defensive block.\nGood protection from enemies.\nSpans multiple tiles. +block.phase-wall.description = Not as strong as a thorium wall but will deflect bullets unless they are too powerful. +block.phase-wall-large.description = Not as strong as a thorium wall but will deflect bullets unless they are too powerful.\nSpans multiple tiles. +block.surge-wall.description = The strongest defensive block.\nHas a small chance of triggering lightning towards the attacker. +block.surge-wall-large.description = The strongest defensive block.\nHas a small chance of triggering lightning towards the attacker.\nSpans multiple tiles. +block.door.description = A small door that can be opened and closed by tapping on it.\nIf opened, enemies can shoot and move through. +block.door-large.description = A large door that can be opened and closed by tapping on it.\nIf opened, enemies can shoot and move through.\nSpans multiple tiles. +block.mend-projector.description = Periodically heals buildings in its vicinity. +block.overdrive-projector.description = Increases the speed of nearby buildings like drills and conveyors. +block.force-projector.description = Creates a hexagonal force field around itself, protecting buildings and units inside from damage through bullets. +block.shock-mine.description = Damages enemies stepping on the mine. Nearly invisible to the enemy. +block.duo.description = A small, cheap turret. +block.scatter.description = A medium-sized anti-air turret. Sprays clumps of lead or scrap flak at enemy units. +block.arc.description = A small turret which shoots electricity in a random arc towards the enemy. +block.hail.description = A small artillery turret. +block.lancer.description = A medium-sized turret which shoots charged electricity beams. +block.wave.description = A medium-sized rapid-fire turret which shoots liquid bubbles. +block.salvo.description = A medium-sized turret which fires shots in salvos. +block.swarmer.description = A medium-sized turret which shoots burst missiles. +block.ripple.description = A large artillery turret which fires several shots simultaneously. +block.cyclone.description = A large rapid fire turret. +block.fuse.description = A large turret which shoots powerful short-range beams. +block.spectre.description = A large turret which shoots two powerful bullets at once. +block.meltdown.description = A large turret which shoots powerful long-range beams. +block.conveyor.description = Basic item transport block. Moved items forward and automatically deposits them into turrets or crafters. Rotatable. +block.titanium-conveyor.description = Advanced item transport block. Moves items faster than standard conveyors. +block.phase-conveyor.description = Advanced item transport block. Uses power to teleport items to a connected phase conveyor over several tiles. +block.junction.description = Acts as a bridge for two crossing conveyor belts. Useful in situations with two different conveyors carrying different materials to different locations. +block.mass-driver.description = Ultimate item transport block. Collects several items and then shoots them to another mass driver over a long range. +block.silicon-smelter.description = Reduces sand with highly pure coke in order to produce silicon. +block.plastanium-compressor.description = Produces plastanium from oil and titanium. +block.phase-weaver.description = Produces phase fabric from radioactive thorium and high amounts of sand. +block.alloy-smelter.description = Produces surge alloy from titanium, lead, silicon and copper. +block.pulverizer.description = Crushes stone into sand. Useful when there is a lack of natural sand. +block.pyratite-mixer.description = Mixes coal, lead and sand into highly flammable pyratite. +block.blast-mixer.description = Uses oil for transforming pyratite into the less flammable but more explosive blast compound. +block.cryofluidmixer.description = Combines water and titanium into cryofluid which is much more efficient for cooling. +block.melter.description = Heats up stone to very high temperatures to obtain lava. +block.incinerator.description = Gets rid of any excess item or liquid. +block.spore-press.description = Compresses spore pods into oil. +block.separator.description = Exposes stone to water pressure in order to obtain various minerals contained in the stone. +block.power-node.description = Transmits power to connected nodes. Up to four power sources, sinks or nodes can be connected. The node will receive power from or supply power to any adjacent blocks. +block.power-node-large.description = Has a larger radius than the power node and connects to up to six power sources, sinks or nodes. +block.battery.description = Stores power whenever there is an abundance and provides power whenever there is a shortage, as long as there is capacity left. +block.battery-large.description = Stores much more power than a regular battery. +block.combustion-generator.description = Generates power by burning oil or flammable materials. +block.turbine-generator.description = More efficient than a combustion generator, but requires additional water. +block.thermal-generator.description = Generates a large amount of power from lava. +block.solar-panel.description = Provides a small amount of power from the sun. +block.solar-panel-large.description = Provides much better power supply than a standard solar panel, but is also much more expensive to build. +block.thorium-reactor.description = Generates huge amounts of power from highly radioactive thorium. Requires constant cooling. Will explode violently if insufficient amounts of coolant are supplied. +block.rtg-generator.description = A radioisotope thermoelectric generator which does not require cooling but provides less power than a thorium reactor. +block.unloader.description = Unloads items from a container, vault or core onto a conveyor or directly into an adjacent block. The type of item to be unloaded can be changed by tapping on the unloader. +block.container.description = Stores a small amount of items. Use it for creating buffers when there is a non-constant demand of materials. An[LIGHT_GRAY] unloader[] can be used to retrieve items from the container. +block.vault.description = Stores a large amount of items. Use it for creating buffers when there is a non-constant demand of materials. An[LIGHT_GRAY] unloader[] can be used to retrieve items from the vault. +block.mechanical-drill.description = A cheap drill. When placed on appropriate tiles, outputs items at a slow pace indefinitely. +block.pneumatic-drill.description = An improved drill which is faster and able to process harder materials by making use of air pressure. +block.laser-drill.description = Allows drilling even faster through laser technology, but requires power. Additionally, radioactive thorium can be retrieved with this drill. +block.blast-drill.description = The ultimate drill. Requires large amounts of power. +block.water-extractor.description = Extracts water from the ground. Use it when there is no lake nearby. +block.cultivator.description = Cultivates the soil with water in order to obtain biomatter. +block.oil-extractor.description = Uses large amounts of power in order to extract oil from sand. Use it when there is no direct source of oil nearby. +block.trident-ship-pad.description = Leave your current vessel and change into a reasonably well armored heavy bomber.\nUse the pad by double tapping while standing on it. +block.javelin-ship-pad.description = Leave your current vessel and change into a strong and fast interceptor with lightning weapons.\nUse the pad by double tapping while standing on it. +block.glaive-ship-pad.description = Leave your current vessel and change into a large, well-armored gunship.\nUse the pad by double tapping while standing on it. +block.tau-mech-pad.description = Leave your current vessel and change into a support mech which can heal friendly buildings and units.\nUse the pad by double tapping while standing on it. +block.delta-mech-pad.description = Leave your current vessel and change into a fast, lightly-armored mech made for hit-and-run attacks.\nUse the pad by double tapping while standing on it. +block.omega-mech-pad.description = Leave your current vessel and change into a bulky and well-armored mech, made for front-line assaults.\nUse the pad by double tapping while standing on it. +block.spirit-factory.description = Produces light drones which mine ore and repair blocks. +block.phantom-factory.description = Produces advanced drone units which are significantly more effective than a spirit drone. +block.wraith-factory.description = Produces fast, hit-and-run interceptor units. +block.ghoul-factory.description = Produces heavy carpet bombers. +block.dagger-factory.description = Produces basic ground units. +block.titan-factory.description = Produces advanced, armored ground units. +block.fortress-factory.description = Produces heavy artillery ground units. +block.revenant-factory.description = Produces heavy laser ground units. +block.repair-point.description = Continuously heals the closest damaged unit in its vicinity. +block.conduit.description = Basic liquid transport block. Works like a conveyor, but with liquids. Best used with extractors, pumps or other conduits. +block.pulse-conduit.description = Advanced liquid transport block. Transports liquids faster and stores more than standard conduits. +block.phase-conduit.description = Advanced liquid transport block. Uses power to teleport liquids to a connected phase conduit over several tiles. +block.liquid-router.description = Accepts liquids from one direction and outputs them to up to 3 other directions equally. Can also store a certain amount of liquid. Useful for splitting the liquids from one source to multiple targets. +block.liquid-tank.description = Stores a large amount of liquids. Use it for creating buffers when there is a non-constant demand of materials or as a safeguard for cooling vital blocks. +block.liquid-junction.description = Acts as a bridge for two crossing conduits. Useful in situations with two different conduits carrying different liquids to different locations. +block.bridge-conduit.description = Advanced liquid transport block. Allows transporting liquids over up to 3 tiles of any terrain or building. +block.mechanical-pump.description = A cheap pump with slow output, but no power consumption. +block.rotary-pump.description = An advanced pump which doubles up speed by using power. +block.thermal-pump.description = The ultimate pump. Three times as fast as a mechanical pump and the only pump which is able to retrieve lava. +block.router.description = Accepts items from one direction and outputs them to up to 3 other directions equally. Useful for splitting the materials from one source to multiple targets. +block.distributor.description = An advanced router which splits items to up to 7 other directions equally. +block.bridge-conveyor.description = Advanced item transport block. Allows transporting items over up to 3 tiles of any terrain or building. +block.item-source.description = Infinitely outputs items. Sandbox only. +block.liquid-source.description = Infinitely outputs liquids. Sandbox only. +block.item-void.description = Destroys any items which go into it without using power. Sandbox only. +block.power-source.description = Infinitely outputs power. Sandbox only. +block.power-void.description = Voids all power inputted into it. Sandbox only. +liquid.water.description = Commonly used for cooling machines and waste processing. +liquid.oil.description = Can be burnt, exploded or used as a coolant. +liquid.cryofluid.description = The most efficient liquid for cooling things down. diff --git a/core/assets/bundles/bundle_uk_UA.properties b/core/assets/bundles/bundle_uk_UA.properties index 0630b6e4bc..4df890f6ca 100644 --- a/core/assets/bundles/bundle_uk_UA.properties +++ b/core/assets/bundles/bundle_uk_UA.properties @@ -1,500 +1,947 @@ -text.about = Створено [ROYAL] Anuken. []\nСпочатку запис у [orange] GDL [] MM Jam.\nТворці:\n- SFX зроблено з [YELLOW] bfxr []\n- Музика зроблена [GREEN] RoccoW [] / Знайдено на [lime] FreeMusicArchive.org [] \nОсоблива подяка:\n- [coral] MitchellFJN []: екстенсивне тестування та відгуки\n- [sky] Luxray5474 []: робота з вікі, вклади коду\n- Всі бета-тестери на itch.io та Google Play\n -text.discord = Приєднуйтесь до нашого Discord! -text.changes = [SCARLET] Увага! \n[] Деякі важливі механіки гри були змінені.\n- [accent] Телепорти [] тепер використовують електроенергію. \n- [accent] Домінна піч [] та [accent] Тиглі [] тепер мають ліміт. \n- [accent] Тиглі [] зараз вимагають вугілля як паливо. -text.gameover = Ядро було зруйновано. -text.highscore = [YELLOW] Новий рекорд! -text.lasted = Ви тримались до хвилі -text.level.highscore = Рекорд: [accent] {0} -text.level.delete.title = Підтвердьте видалення -text.level.delete = Ви впевнені, що хочете видалити карту \"[orange] {0} \"? -text.level.select = Вибір рівня -text.level.mode = Ігровий режим -text.savegame = Зберегти гру -text.loadgame = Завантажити гру -text.joingame = Приєднатися\nдо гри -text.newgame=Нова гра -text.quit = Вийти -text.about.button = Про -text.name = Назва: -text.public = Публічний -text.players = {0} гравців онлайн -text.server.player.host = {0} (host) -text.players.single = {0} гравців онлайн -text.server.mismatch = Пакетна помилка: невідповідність версії версії клієнта / сервера. Переконайтеся, що ви та хост мають останню версію Mindustry! -text.server.closing = [accent] Закриття сервера ... -text.server.kicked.kick = Ви були вигнані з сервера! -text.server.kicked.invalidPassword = Невірний пароль! -text.server.kicked.clientOutdated = Застарілий клієнт! Оновіть свою гру! -text.server.kicked.serverOutdated = Застарілий сервер! Попросіть хост оновити! -text.server.connected = {0} приєднався. -text.server.disconnected = {0} від'єднано. -text.nohost = Неможливо розмістити сервер на власній карті! -text.hostserver = Хост-сервер -text.host = Хост -text.hosting = [accent] Відкриття сервера ... -text.hosts.refresh = Оновити -text.hosts.discovering = Знайомство з мережевими іграми -text.server.refreshing = Оновити сервери -text.hosts.none = [lightgray] Ніяких ігор у мережі не знайдено! -text.host.invalid = [scarlet] Неможливо підключитися до хосту. -text.server.friendlyfire = Дружній вогонь -text.server.add = Додати сервер -text.server.delete = Ви впевнені, що хочете видалити цей сервер? -text.server.hostname = Хост: {0} -text.server.edit = Редагувати сервер -text.joingame.byip = [] Приєднатися по IP ...[] -text.joingame.title = Приєднатися до гри -text.joingame.ip = IP -text.disconnect = Роз'єднано -text.connecting = [accent] Підключення ... -text.connecting.data = [accent] Завантаження світових даних ... -text.connectfail = [crimson] Не вдалося підключитися до сервера: [orange] {0} -text.server.port = Порт -text.server.addressinuse = Адреса вже використовується! -text.server.invalidport = Недійсний номер порту. -text.server.error = [crimson] Помилка хостингу сервера: [orange] {0} -text.tutorial.back = < Попер. -text.tutorial.next = Далі > -text.save.new = Нове збереження -text.save.overwrite = Ви впевнені, що хочете перезаписати цей слот для збереження? -text.overwrite = Перезаписати -text.save.none = Не знайдено жодних збережень! -text.saveload = [accent] Збереження ... -text.savefail = Не вдалося зберегти гру! -text.save.delete.confirm = Ви впевнені, що хочете видалити це збереження? -text.save.delete = Видалити -text.save.export = Експорт збереження -text.save.import.invalid = [orange] Це збереження недійсне! -text.save.import.fail = [crimson] Не вдалося імпортувати збереження: [orange] {0} -text.save.export.fail = [crimson] Не вдалося експортувати збереження: [orange] {0} -text.save.import = Імпортувати збереження -text.save.newslot = Назва збереження: -text.save.rename = Переіменувати -text.save.rename.text = Нова назва: -text.selectslot = Виберіть збереження. -text.slot = [accent] слот {0} -text.save.corrupted = [orange] Збережений файл пошкоджений або він невірний! -text.empty = <порожньо> -text.on = Увімкнути -text.off = Вимкнути -text.save.autosave = Автозбереження: {0} -text.save.map = Карта -text.save.wave = Хвиля {0} -text.save.difficulty = Складність -text.save.date = Останнє збережено: {0} -text.confirm = Підтвердити -text.delete = Видалити -text.ok = ОК -text.open = Відкрити -text.cancel = Скасувати -text.openlink = Відкрити посилання -text.back = Назад -text.quit.confirm = Ти впевнений що хочеш піти? -text.loading = [accent] Завантаження ... -text.wave = [orange] хвиля {0} -text.wave.waiting = Хвиля через {0} -text.waiting = Очікування… -text.enemies = {0} Вороги -text.enemies.single = Противник -text.loadimage = Завантажити зображення -text.saveimage = Зберегти зображення -text.oregen = Генерація руд -text.editor.badsize = [orange] Недійсні розміри зображення! [] Дійсні розміри карти: {0} -text.editor.errorimageload = Помилка завантаження файлу зображень: [orange] {0} -text.editor.errorimagesave = Помилка збереження файлу зображення: [orange] {0} -text.editor.generate = Генератор -text.editor.resize = Змінити розмір -text.editor.loadmap = // Завантажити карту -text.editor.savemap = Зберегти карту -text.editor.loadimage = Завантажити зображення -text.editor.saveimage = Зберегти зображення -text.editor.unsaved = [scarlet] У вас є незбережені зміни! [] Ви впевнені, що хочете вийти? -text.editor.brushsize = Розмір пензля: {0} -text.editor.noplayerspawn = Ця карта не має ігрового поля для гравця! -text.editor.manyplayerspawns = Карти не можуть мати більше одного ігрового поля для гравців! -text.editor.manyenemyspawns = Не може бути більше ніж {0} ворожих точок! -text.editor.resizemap = Змінити розмір карти -text.editor.resizebig = [scarlet] Попередження! [] Карти, розмір яких перевищує 256 одиниць, можуть виснути і можуть бути нестабільними. -text.editor.mapname = Назва карти: -text.editor.overwrite = [accent] Попередження! Це перезаписує існуючу карту. -text.editor.failoverwrite = [crimson] Неможливо перезаписати карту за замовчуванням! -text.editor.selectmap = Виберіть карту для завантаження: -text.width = Ширина -text.height = Висота -text.randomize = Рандомізувати -text.apply = Застосувати -text.update = Оновити -text.menu = Меню -text.play = Відтворити -text.load = Завантаження -text.save = Зберегти -text.language.restart = Будь ласка, перезапустіть свою гру, щоб налаштування мови набули чинності. -text.settings.language = Мова -text.settings = Налаштування -text.tutorial = Навчальний\nпосібник -text.editor = Редактор -text.mapeditor = Редактор карт -text.donate = Підтримати проект -text.settings.reset = Скинути до стандартних -text.settings.controls = Елементи управління -text.settings.game = Гра -text.settings.sound = Звук -text.settings.graphics = Графіка -text.upgrades = Оновлення -text.purchased = [LIME] Створено! -text.weapons = Зброя -text.paused = Пауза -text.respawn = Відновлення за -text.info.title = [accent] інформація -text.error.title = [crimson] Виникла помилка -text.error.crashmessage = [SCARLET] Виникла несподівана помилка, що призвела до збою. [] Будь ласка, повідомте про конкретні обставини, розробнику: [ORANGE] anukendev@gmail.com [] -text.error.crashtitle = Виникла помилка -text.mode.break = Режим зносу: {0} -text.mode.place = Режим будівництва: {0} -placemode.hold.name = Лінія -placemode.areadelete.name = Площа -placemode.touchdelete.name = Дотик -placemode.holddelete.name = Утримування. -placemode.none.name = (None) -placemode.touch.name = Дотик -placemode.cursor.name = курсор -text.blocks.extrainfo = [accent] додатковий інформаційний блок: -text.blocks.blockinfo = Блокування інформації -text.blocks.powercapacity = Потужність -text.blocks.powershot = Потужність / постріл -text.blocks.powersecond = Потужність / секунда -text.blocks.powerdraindamage = Потужність дренажу / пошкодження -text.blocks.shieldradius = Радіус щита -text.blocks.itemspeedsecond = Швидкість / секунда -text.blocks.range = Радіус -text.blocks.size = Розмір -text.blocks.powerliquid = Потужність / Рідина -text.blocks.maxliquidsecond = Макс. Рідина / секунда -text.blocks.liquidcapacity = Ємкість рідини -text.blocks.liquidsecond = Рідина / секунда -text.blocks.damageshot = Пошкодження / постріл -text.blocks.ammocapacity = Місткість боєприпасів -text.blocks.ammo = Набої -text.blocks.ammoitem = Боєприпаси / предмет -text.blocks.maxitemssecond = Макс. Елементи / секунду -text.blocks.powerrange = Радіус потужності -text.blocks.lasertilerange = Радіус лазерних плиток -text.blocks.capacity = Ємкість -text.blocks.itemcapacity = Ємкість предмету -text.blocks.maxpowergenerationsecond = Максимальна потужність / секунда -text.blocks.powergenerationsecond = Потужність / секунда -text.blocks.generationsecondsitem = Генерація за секунду / предмет -text.blocks.input = Ввід -text.blocks.inputliquid = Ввід речовини -text.blocks.inputitem = Вхідний матеріал -text.blocks.output = Вивід -text.blocks.secondsitem = Секунда / предмет -text.blocks.maxpowertransfersecond = Максимальна передача потужності / секунда -text.blocks.explosive = Вибухонебезпечний! -text.blocks.repairssecond = Ремонт / секунда -text.blocks.health = Здоров'я -text.blocks.inaccuracy = Неточність -text.blocks.shots = Постріли -text.blocks.shotssecond = Постріли / секунду -text.blocks.fuel = Паливо: -text.blocks.fuelduration = Тривалість палива -text.blocks.maxoutputsecond = Макс. Вихід / секунду -text.blocks.inputcapacity = Вхідна ємність -text.blocks.outputcapacity = Випускна ємність -text.blocks.poweritem = Потужність / виріб -text.placemode = Місцевий режим -text.breakmode = Перерваний режим -text.health = Здоров'я -setting.difficulty.easy = Легкий -setting.difficulty.normal = Нормальний -setting.difficulty.hard = Важкий -setting.difficulty.insane = Божевільний -setting.difficulty.purge = Очистити -setting.difficulty.name = Складність -setting.screenshake.name = Тряска екрана -setting.smoothcam.name = Гладка камера -setting.indicators.name = Індикатори ворога -setting.effects.name = Ефекти відображення +credits.text = Створив [ROYAL]Anuken[] - [SKY]anukendev@gmail.com[]\n\nЄ питання по грі або проблеми с перекладом?\nЙди в офіційний сервер discord Mindustry в канал #український.\nПерекладач:Prosta4ok_ua +credits = Автори +contributors = Перекладачі та Помічники +discord = Приєднуйтесь до нашого Discord! +link.discord.description = Офіційний Discord-сервер Mindustry +link.github.description = Код гри +link.dev-builds.description = Нестабільні версії +link.trello.description = Офіційна дошка Trello(англ.) для запланованих функцій +link.itch.io.description = Itch.io сторінка з веб-версією та завантаженням для ПК +link.google-play.description = Скачати з Google Play для Android +link.wiki.description = Офіційна Mindustry вікі (англ.) +linkfail = Не вдалося відкрити посилання!\nURL-адреса скопійовано у ваш буфер обміну. +screenshot = Скріншот збережено в {0} +screenshot.invalid = Мапа занадто велика, тому не вистачає пам’яті для знімку екрана. +gameover = Гру закінчено +gameover.pvp = [accent] {0}[] команда перемогла! +highscore = [YELLOW]Новий рекорд! +stat.wave = Хвиль відражено:[accent] {0} +stat.enemiesDestroyed = Ворогів вбито:[accent] {0} +stat.built = Будівель збудувано:[accent] {0} +stat.destroyed = Будівель знищено:[accent] {0} +stat.deconstructed = Будівель декоструйовано[accent] {0} +stat.delivered = Ресурсів здобуто: +stat.rank = Фінальний рахунок: [accent]{0} +placeline = Ви вибрали блок.\nВи можете[accent] будувати в лінію[] [accent] утримуючи палець протягом декількох секунд[] і потім перетягуючи його.\n\n[scarlet]ЗРОБИ ЦЕ. +removearea = Ви вибрали режим видалення.\nВи можете[accent] видаляти блоки в обраній області[],[accent] утримуючи палець кілька секунд [] і потім перетягуючи його.\n\n[scarlet]ЗРОБИ ЦЕ. +launcheditems = [accent]Запущені предмети +map.delete = Ви впевнені, що хочете видалити мапу "[accent]{0}[]"? +level.highscore = Рекорд: [accent]{0} +level.select = Вибір мапи +level.mode = Режим гри: +showagain = Не показувати знову до наступного сеансу +coreattack = < Ядро під атакою! > +nearpoint = [ [scarlet]ЗАЛИШТЕ ТОЧКУ НЕГАЙНО[] ]\nаннігіляція неминуча. +outofbounds = [ ПОЗА МЕЖАМИ ]\nсаморуйнування через{0} +database = База Даних Ядра +savegame = Зберегти гру +loadgame = Завантажити гру +joingame = Мережева гра +addplayers = Дод/Видалити гравців +customgame = Користувальницька гра +newgame = Нова гра +none = <нічого> +minimap = Міні-мапа +close = Закрити +quit = Вийти +maps = Мапи +continue = Продовжити +maps.none = [LIGHT_GRAY]Мап не знайдено! +about.button = Про гру +name = Нік: +noname = Спочатку придумайте[accent] собі нікнейм[]. +filename = Ім'я файлу: +unlocked = Новий контент розблоковано! +completed = [accent]Завершено +techtree = Технологічне дерево +research.list = [LIGHT_GRAY]Дослідження: +research = Дослідження +researched = [LIGHT_GRAY]{0} досліджено. +players = Гравців на сервері: {0} +players.single = {0} гравець на сервері +server.closing = [accent]Закриття серверу... +server.kicked.kick = Ви були вигнані(кікнуті) з сервера! +server.kicked.serverClose = Сервер закрито. +server.kicked.clientOutdated = Застарілий клієнт! Оновіть свою гру! +server.kicked.serverOutdated = Застарілий сервер! Попросіть адміністратора серверу оновити сервер/гру! +server.kicked.banned = Ви були заблоковані на цьому сервері. +server.kicked.recentKick = Нещодавно Вас вигнали(кікнули). \nПочекайте трохи перед наступним підключенням. +server.kicked.nameInUse = На цьому сервері є хтось \nз таким ніком. +server.kicked.nameEmpty = Ваш нікнейм має містити принаймні один символ або цифру. +server.kicked.idInUse = Ви вже на цьому сервері! Підключення двох облікових записів не допускається. +server.kicked.customClient = Цей сервер не підтримує користувальницькі збірки. Завантажте офіційну версію. +server.kicked.gameover = Гра завершена! +host.info = Кнопка [accent]Сервер[] розміщує сервер на порті [scarlet]6567[]. \nКористувачі, які знаходяться у тій же [LIGHT_GRAY] WiFi або локальній мережі[] повинні бачити ваш сервер у своєму списку серверів.\n\nЯкщо ви хочете, щоб люди могли приєднуватися з будь-якої точки через IP, то [accent] переадресація порту [] обов'язкова.\n\n[LIGHT_GRAY] Примітка. Якщо у вас виникли проблеми з підключенням до вашої локальної гри, переконайтеся, що ви дозволили Mindustry доступ до вашої локальної мережі в налаштуваннях брандмауера. +join.info = Тут ви можете ввести [accent]IP серверу[] для підключення або знайти сервери у [accent]локальній мережі[] для підключення до них.\nПідтримується локальна мережа(LAN) і широкосмугова мережа(WAN).\n\n[LIGHT_GRAY] Примітка. Тут немає автоматичного глобального списку серверів; якщо ви хочете підключитися до когось через IP, вам доведеться попросити створювача серверу дати свій ip. +hostserver = Запустити сервер +hostserver.mobile = Запустити\nсервер +host = Сервер +hosting = [accent]Відкриття серверу... +hosts.refresh = Оновити +hosts.discovering = Пошук локальних ігор +server.refreshing = Оновлення серверу +hosts.none = [lightgray]Локальних ігр не знайдено +host.invalid = [scarlet]Не вдалося підключитися до хосту. +trace = Стежити за гравцем +trace.playername = Ім'я гравця: [accent]{0} +trace.ip = IP: [accent]{0} +trace.id = Унікальний ідентифікатор: [accent]{0} +trace.mobile = Мобільний клієнт: [accent]{0} +trace.modclient = Користувацький клієнт: [accent]{0} +invalidid = Невірний ідентифікатор клієнта! Надішліть звіт про помилку. +server.bans = Блокування +server.bans.none = Заблокованих гравців нема! +server.admins = Адміністратори +server.admins.none = Адміністраторів нема +server.add = Додати сервер +server.delete = Ви впевнені, що хочете видалити цей сервер? +server.hostname = Хост: {0} +server.edit = Редагувати сервер +server.outdated = [crimson]Застарілий сервер![] +server.outdated.client = [crimson]Застарілий клієнт![] +server.version = [lightgray]Версія: {0} +server.custombuild = [yellow]Користувацький збірка +confirmban = Ви впевнені, що хочете заблокувати цього гравця? +confirmkick = Ви впевнені, що хочете викинути(кікнути) цього гравця? +confirmunban = Ви впевнені, що хочете розблокувати цього гравця? +confirmadmin = Ви впевнені, що хочете зробити цього гравця адміністратором? +confirmunadmin = Ви впевнені, що хочете видалити статус адміністратора з цього гравця? +joingame.title = Приєднатися до гри +joingame.ip = IP: +disconnect = Відключено. +disconnect.data = Не вдалося завантажити світові дані! +connecting = [accent]Підключення... +connecting.data = [accent]Завантаження даних світу... +server.port = Порт: +server.addressinuse = Ця адреса вже використовується! +server.invalidport = Недійсний номер порту! +server.error = [crimson]Помилка запуску сервера: [accent]{0} +save.old = Це збереження для старої версії гри, і його більше не можна використовувати.\n\n [LIGHT_GRAY]Зворотна сумісність буде реалізовано у повній версії 4.0. +save.new = Нове збереження +save.overwrite = Ви впевнені, що хочете перезаписати цей слот для збереження? +overwrite = Перезаписати +save.none = Збережень не знайдено! +saveload = [accent]Збереження... +savefail = Не вдалося зберегти гру! +save.delete.confirm = Ви впевнені, що хочете видалити це збереження? +save.delete = Видалити +save.export = Экспортувати збереження +save.import.invalid = [accent]Це збереження недійсне! +save.import.fail = [crimson]Не вдалося імпортувати збереження: [accent]{0} +save.export.fail = [crimson]Не вдалося экспортувати збереження: [accent]{0} +save.import = Імпортувати збереження +save.newslot = Назва збереження: +save.rename = Перейменувати +save.rename.text = Нова назва: +selectslot = Виберіть збереження. +slot = [accent]Слот {0} +save.corrupted = [accent]Збережений файл пошкоджено або э недійсним! \nЯкщо ви щойно оновили свою гру, це, мабуть, є зміною формату збереження та [scarlet] не[] є помилкою. +empty = <порожній> +on = Включено +off = Вимкнено +save.autosave = Автозбереження: {0} +save.map = Мапа: {0} +save.wave = Хвиля {0} +save.difficulty = Складність: {0} +save.date = Останнє збереження +save.playtime = Час гри: {0} +warning = Попередження +confirm = Підтвердження +delete = Видалити +ok = ОК +open = Open +customize = Налаштувати +cancel = Скасувати +openlink = Відкрити посилання +copylink = Скопіювати посилання +back = Назад +quit.confirm = Ви впевнені що хочете вийти? +changelog.title = Журнал змін +changelog.loading = Отримання журналу змін... +changelog.error.android = [accent]Зверніть увагу, що іноді журнал змін не працює на ОС Android 4.4 або на нижчій версії!\nЦе пов'язано з внутрішньою помилкою Android. +changelog.error.ios = [accent]Журнал змін наразі не підтримується iOS. +changelog.error = [scarlet]Помилка отримання журналу змін!\nПеревірте підключення до Інтернету. +changelog.current = [yellow][[Поточна версія] +changelog.latest = [accent][[Остання версія] +loading = [accent]Завантаження... +saving = [accent]Збереження... +wave = [accent]Хвиля {0} +wave.waiting = Хвиля через {0} +wave.waveInProgress = [LIGHT_GRAY]Хвиля триває +waiting = Очікування... +waiting.players = Очікування гравців +wave.enemies = [LIGHT_GRAY]{0} ворог. залишилося +wave.enemy = [LIGHT_GRAY]{0} ворог залишився +loadimage = Завантажити зображення +saveimage = Зберегти зображення +unknown = Невідомо +custom = Користувальницька +builtin = Bбудована +map.delete.confirm = Ви впевнені, що хочете видалити цю мапу? Цю дію не можна скасувати! +map.random = [accent]Випадкова мапа +map.nospawn = Ця мапа не має жодного ядра для спавну гравця! Додайте[ROYAL] сине[] ядро в цю мапу редакторі. +map.nospawn.pvp = У цієї мапи немає ворожих ядер, в яких гравець може з'явитися! Додайте[SCARLET] не сине[] ядро до цієї мапи в редакторі. +map.nospawn.attack = У цієї мапи немає ворожих ядер,які гравець може атакувати! Додайте[SCARLET] червоне[] ядро до цієї мапи в редакторі. +map.invalid = Помилка завантаження мапи: пошкоджений або невірний файл мапи. +editor.brush = Пензлик +editor.openin = Відкрити в редакторі +editor.oregen = Генерація руд +editor.oregen.info = Генерація руд: +editor.mapinfo = Інформація про мапу +editor.author = Автор: +editor.description = Опис: +editor.waves = Хвилі: +editor.rules = Правила: +editor.ingame = Редагувати в грі +waves.title = Хвилі +waves.remove = Видалити +waves.never = <ніколи> +waves.every = Кожен +waves.waves = хвиля(і) +waves.perspawn = за появу +waves.to = до +waves.boss = Босс +waves.preview = Попередній перегляд +waves.edit = Редагувати ... +waves.copy = Копіювати в буфер обміну +waves.load = Завантаження з буфера обміну +waves.invalid = Недійсні хвилі в буфері обміну. +waves.copied = Хвилі скопіювані. +editor.default = [LIGHT_GRAY]<За замовчуванням> +edit = Редагувати... +editor.name = Назва: +editor.spawn = Створити бойову одиницю +editor.removeunit = Видалити бойову одиницю +editor.teams = Команди +editor.elevation = Висота +editor.errorload = Помилка завантаження зображення:[accent] {0} +editor.errorsave = Помилка збереження зображення:\n[accent]{0} +editor.errorimage = That's an image, not a map. Don't go around changing extensions expecting it to work.\n\nIf you want to import a legacy map, use the 'import legacy map' button in the editor. +editor.errorlegacy = This map is too old, and uses a legacy map format that is no longer supported. +editor.errorheader = This map file is either not valid or corrupt. +editor.errorname = Мапа не має визначеного імені. +editor.update = Оновити +editor.randomize = Випадково +editor.apply = Застосувати +editor.generate = Згенерувати +editor.resize = Змінити \nрозмір +editor.loadmap = Завантажити мапу +editor.savemap = Зберегти мапи +editor.saved = Збережено! +editor.save.noname = Ваша мапа не має назви! Встановіть його в меню «Інформація про мапу». +editor.save.overwrite = Ваша мапа перезаписує вбудовану мапу! Виберіть інше ім'я в меню «Інформація про мапу». +editor.import.exists = [scarlet]Неможливо імпортувати: [] вбудована мапа з назвою "{0}" вже існує! +editor.import = Імпорт... +editor.importmap = Імпортувати мапу +editor.importmap.description = Імпортувати вже існуючу мапу +editor.importfile = Імпортувати файл +editor.importfile.description = Імпортувати зовнішній файл мапи +editor.importimage = Імпорт зовнішнього файла зображення мапи +editor.importimage.description = Імпорт зображення місцевості +editor.export = Експорт... +editor.exportfile = Експорт файлу +editor.exportfile.description = Експортувати файл мапи +editor.exportimage = Експорт зображення місцевості +editor.exportimage.description = Експорт файла з зображенням мапи +editor.loadimage = Завантажити\nзображення +editor.saveimage = Зберегти\nзображення +editor.unsaved = [scarlet]У вас є незбережені зміни![]\nВи впевнені, що хочете вийти? +editor.resizemap = Змінити розмір мапи +editor.mapname = Назва мапи: +editor.overwrite = [accent]Попередження!\nЦе перезаписує існуючу мапу. +editor.overwrite.confirm = [scarlet]Попередження![] Мапа з такою назвою вже існує. Ви впевнені, що хочете переписати її? +editor.selectmap = Виберіть мапу для завантаження: +filters.empty = [LIGHT_GRAY]Нема фільтрів! Додайте хоча б один за допомогою кнопки, що знаходиться нижче. +filter.distort = Спотворення +filter.noise = Шум +filter.ore = Руда +filter.rivernoise = Річковий Шум +filter.scatter = Розпилювач +filter.terrain = Ландшафт +filter.option.scale = Масштаб +filter.option.chance = Шанс +filter.option.mag = Величина +filter.option.threshold = Спад +filter.option.circle-scale = Кругова шкала +filter.option.octaves = Октави +filter.option.falloff = Спад +filter.option.block = Блок +filter.option.floor = Поверхня +filter.option.wall = Стіна +filter.option.ore = Руда +filter.option.floor2 = Друга поверхня +filter.option.threshold2 = Вторинний спад +filter.option.radius = Radius +filter.option.percentile = Percentile +width = Ширина: +height = Висота: +menu = Меню +play = Грати +load = Завантажити +save = Зберегти +fps = FPS: {0} +tps = TPS: {0} +ping = Пінг: {0} мс +language.restart = Будь ласка, перезапустіть свою гру, щоб налаштування мови набули чинності.\nPlease restart your game for the language settings to take effect. +settings = Налаштування +tutorial = Навчання +editor = Редактор +mapeditor = Редактор мап +donate = Пожертву\nвання +abandon = Покинути +abandon.text = Ця зона і всі її ресурси будуть втрачені для ворога. +locked = Заблоковано +complete = [LIGHT_GRAY]Досягнута: +zone.requirement = Хвиля {0} у зоні {1} +resume = Відновити зону:\n[LIGHT_GRAY]{0} +bestwave = [LIGHT_GRAY]Найкраща хвиля: {0} +launch = < ЗАПУСК > +launch.title = Запуск вдалий +launch.next = [LIGHT_GRAY]наступна можливість на хвилі{0} +launch.unable = [scarlet]ЗАПУСК неможливий.[] {0} Ворог. +launch.confirm = Це видалить всі ресурси у Вашому ядрі.\nВи не зможете повернутися до цієї бази. +uncover = Розкрити +configure = Вивантаження +configure.locked = [LIGHT_GRAY]Розблокувати можливість вивантаження ресурсів: Хвиля {0}. +zone.unlocked = [LIGHT_GRAY]{0} розблоковано +zone.requirement.complete = {0}-та хвиля досягено:\nвимоги до зони "{1}" виконані. +zone.config.complete = {0} хвиль досягнено. :\nРозблоковано можливість вивантаження. +zone.resources = Виявлені ресурси: +add = Додати... +boss.health = Здоров’я босу +connectfail = [crimson]Не вдалося підключитися до сервера: [accent]{0} +error.unreachable = Сервер не доступний. +error.invalidaddress = Некоректна адреса. +error.timedout = Час очікувування вийшов.\nПереконайтеся, що адреса коректна і що власник сервера налаштував переадресацію порту! +error.mismatch = Помилка пакету:\nможливе невідповідність версії клієнта / сервера.\nПереконайтеся, що у Вас та у володара сервера встановлена остання версія Mindustry! +error.alreadyconnected = Ви вже підключилися. +error.mapnotfound = Файл мапи не знайдено +error.io = Мережева помилка введення-виведення +error.any = Невідома мережева помилка +zone.groundZero.name = Нульова земля +zone.desertWastes.name = Пустеля з відходами +zone.craters.name = Кратери +zone.frozenForest.name = Крижаний Ліс +zone.ruinousShores.name = Зруйновані Берега +zone.stainedMountains.name = Пофарбовані гори +zone.desolateRift.name = Пустельний Розлом +zone.nuclearComplex.name = Ядерний Виробничий Комплекс +zone.overgrowth.name = Зарості +zone.tarFields.name = Дьогтьові поля +settings.language = Мова +settings.reset = Скинути за замовчуванням +settings.rebind = Зміна +settings.controls = Управління +settings.game = Гра +settings.sound = Звук +settings.graphics = Графіка +settings.cleardata = Очистити дані... +settings.clear.confirm = Ви впевнені, що хочете очистити ці дані?\nЦя дія не може бути скасовано! +settings.clearall.confirm = [scarlet]УВАГА![]\nЦе очистить всі дані, включаючи збереження, мапи, розблокуване та призначені клавіші.\nПісля того, як ви натиснете ОК, гра видалить усі дані та автоматично вийде. +settings.clearunlocks = Очистити розблоковане +settings.clearall = Очистити все +paused = Пауза +yes = Так +no = Ні +info.title = Інформація +error.title = [crimson]Виникла помилка +error.crashtitle = Виникла помилка +blocks.input = Вхід +blocks.output = Вихід +blocks.booster = Прискорювач +block.unknown = [LIGHT_GRAY]??? +blocks.powercapacity = Місткість енергії +blocks.powershot = Енергія/постріл +blocks.targetsair = Повітряні мішені +blocks.targetsground = Наземні мішені +blocks.itemsmoved = Швидкість переміщення +blocks.launchtime = Час між запусками +blocks.shootrange = Діапазон дії +blocks.size = Розмір +blocks.liquidcapacity = Місткість рідини +blocks.powerrange = Діапазон передачі енергії +blocks.poweruse = Енергії використовує +blocks.powerdamage = Енергія/урон +blocks.itemcapacity = Місткість предметів +blocks.basepowergeneration = Базова генерація енергії +blocks.productiontime = Час виробництва +blocks.repairtime = Час повного відновлення блоку +blocks.speedincrease = Збільшення швидкості +blocks.range = Радіус дії +blocks.drilltier = Видобуває +blocks.drillspeed = Базова швидкість буріння +blocks.boosteffect = Прискорювальний ефект +blocks.maxunits = Максимальна кількість активних одиниць +blocks.health = Здоров'я +blocks.buildtime = Час будівництва +blocks.inaccuracy = Розкид +blocks.shots = Постріли +blocks.reload = Постріли/секунду +blocks.ammo = Боєприпаси +bar.drillspeed = Швидкість сверрдління: {0}/с +bar.efficiency = Ефективність: {0}% +bar.powerbalance = Енергія: {0}/с +bar.poweramount = Енергія: {0} +bar.poweroutput = Вихідна енергія: {0} +bar.items = Предмети: {0} +bar.liquid = Рідина +bar.heat = Нагрівання +bar.power = Енергія +bar.progress = Хід Будівництва +bar.spawned = Бойов. од. : {0}/{1} +bullet.damage = [stat]{0}[lightgray] шкода +bullet.splashdamage = [stat]{0}[lightgray] шкода по ділянці ~[stat] {1}[lightgray] плитк. +bullet.incendiary = [stat]запальний +bullet.homing = [stat]самонаведення +bullet.shock = [stat]шок +bullet.frag = [stat]осколкова граната +bullet.knockback = [stat]{0}[lightgray] відкидання +bullet.freezing = [stat]заморожування +bullet.tarred = [stat]дьогтьовий +bullet.multiplier = [stat]{0}[lightgray]x патронів +bullet.reload = [stat]{0}[lightgray]x швидкість перезаряджання +unit.blocks = блоки +unit.powersecond = одиниць енергії/секунду +unit.liquidsecond = рідких одиниць/секунду +unit.itemssecond = предметів/секунду +unit.liquidunits = рідинних одиниць +unit.powerunits = енергетичних одиниць +unit.degrees = град. +unit.seconds = сек. +unit.persecond = /сек +unit.timesspeed = x швидкість +unit.percent = % +unit.items = предм. +category.general = Загальні +category.power = Енергетичні +category.liquids = Рідини +category.items = Предмети +category.crafting = Введення/виведення +category.shooting = Стрільба +category.optional = Додаткові поліпшення +setting.landscape.name = Ландшафтний +setting.shadows.name = Тіні +setting.linear.name = Лінійна фільтрація +setting.animatedwater.name = Анімована вода +setting.animatedshields.name = Анімовані щити +setting.antialias.name = Згладжування[LIGHT_GRAY] (потребує перезапуску)[] +setting.indicators.name = Показувати у сторону ворогів та союзників +setting.autotarget.name = Авто-стрільба +setting.fpscap.name = Макс. FPS +setting.fpscap.none = Необмежений +setting.fpscap.text = {0} FPS +setting.swapdiagonal.name = Завжди Діагональне Розміщення +setting.difficulty.training = Навчання +setting.difficulty.easy = Легка +setting.difficulty.normal = Нормальна +setting.difficulty.hard = Важка +setting.difficulty.insane = Божевільна +setting.difficulty.name = Складність: +setting.screenshake.name = Тряска екрану +setting.effects.name = Ефекти setting.sensitivity.name = Чутливість контролера -setting.saveinterval.name = Інтервал автозбереження -setting.seconds = {0} секунд -setting.fullscreen.name = Повноекранний -setting.multithread.name = Багатопотоковий [scarlet] (нестабільний!) -setting.fps.name = Показати FPS -setting.vsync.name = VSunc -setting.lasers.name = Показати енергетичні лазери -setting.healthbars.name = Показати здоров'я -setting.pixelate.name = Пікселяція екрану -setting.musicvol.name = Гучність музики -setting.mutemusic.name = Вимкнути музику -setting.sfxvol.name = Гучність ефектів -setting.mutesound.name = Вимкнути звук -map.maze.name = Лабіринт -map.fortress.name = Фортеця -map.sinkhole.name = Свердловина -map.caves.name = Печери -map.volcano.name = Вулкан -map.caldera.name = Кальдера -map.scorch.name = Мертва земля -map.desert.name = Пустеля -map.island.name = Острів -map.grassland.name = Пасовища -map.tundra.name = Тундра -map.spiral.name = Спіраль -map.tutorial.name = Навчання -tutorial.intro.text = [yellow] Ласкаво просимо до підручника. [] Для початку натисніть \"далі\". -tutorial.moveDesktop.text = Для переміщення використовуйте клавіші [orange] ​​[[WASD] []. Утримуйте [orange] SHIFT[], для прискорення. Утримуйте [orange] CTRL [], використовуючи [orange] колесо прокручування [] для збільшення або зменшення. -tutorial.shoot.text = Використовуйте мишу, щоб націлитись, утримуйте [orange] ліву кнопку миші [], щоб стріляти. Попрактикуйтесь на [yellow] мішені []. -tutorial.moveAndroid.text = Щоб перетягнути панораму, перетягніть один палець по екрану. Використовуйте два пальця, щоб збільшити чи зменшити маштаб. -tutorial.placeSelect.text = Спробуйте вибрати [yellow] конвеєр [] у меню блоку внизу справа. -tutorial.placeConveyorDesktop.text = Використовуйте [orange] [[колесико миші] [], щоб повернути конвеєр [orange] вперед [], а потім помістіть його в [yellow] позначене місце [], використовуючи [orange] [[ліву кнопку миші] []. -tutorial.placeConveyorAndroid.text = Використовуйте [orange] [[кнопку оберту] [], щоб обернути конвеєр [оранжевий] вперед [], перетягуйте його одним пальцем, а потім помістіть його в [yellow] позначене місце [], використовуючи [orange] [[галочка][]. -tutorial.placeConveyorAndroidInfo.text = Крім того, ви можете натиснути піктограму перехрестя внизу ліворуч, щоб переключитися на [orange] [[сенсорний режим]] [], і помістити блоки, натиснувши на екран. У сенсорному режимі блоки можна повертати зі стрілкою внизу ліворуч. Натисніть [yellow] наступний [], щоб спробувати. -tutorial.placeDrill.text = Тепер виберіть та розмістіть [yellow] кам'яне свердло [] у зазначеному місці. -tutorial.blockInfo.text = Якщо ви хочете дізнатись більше про блок, ви можете торкнутися [orange] знак питання [] у верхньому правому куті, щоб прочитати його опис. -tutorial.deselectDesktop.text = Ви можете вимкнути блок, використовуючи [orange] [[клацання правою кнопкою миші] []. -tutorial.deselectAndroid.text = Ви можете скасувати вибір блоку, натиснувши кнопку [orange] X []. -tutorial.drillPlaced.text = Дриль тепер видобуває [yellow] камінь, [] та виведе його на конвеєр, а потім переміщає його в [yellow] ядро []. -tutorial.drillInfo.text = Різні руди потребують різних дрилі. Камінь вимагає кам'яні свердла, залізо вимагає залізні свердла та ін -tutorial.drillPlaced2.text = Переміщення елементів у ядро ​​вказує їх у ваш [yellow] предметний інвентар [] у верхньому лівому куті. Розміщення блоків використовує предмети з вашого інвентарю. -tutorial.moreDrills.text = Ви можете пов'язати багато свердлів і конвеєрів разом в одну гілку конвеєра. -tutorial.deleteBlock.text = Ви можете видалити блоки, натиснувши правою клавішею [orange] правою кнопкою миші [] по блоці, який ви хочете видалити. Спробуйте видалити цей конвеєр. -tutorial.deleteBlockAndroid.text = Ви можете видалити блоки за допомогою [orange], перехрестя [] в меню [mode] зламу [orange] у нижньому лівому куті та натиснувши на блок. Спробуйте видалити цей конвеєр. -tutorial.placeTurret.text = Тепер виділіть та розмістіть [yellow] турель [] у [yellow] позначеному місці []. -tutorial.placedTurretAmmo.text = Ця турель тепер приймає [yellow] боєприпас [] з конвеєра. Ви можете побачити, скільки боєприпасів вона має, натискаючи на неї і перевіряючи [green] зелену полоску []. -tutorial.turretExplanation.text = Турелі будуть автоматично стріляти у найближчого ворога, якщо вони мають достатню кількість боєприпасів. -tutorial.waves.text = Кожні [yellow] 60 [] секунд, хвиля [coral] ворогів [] буде виникати в певних місцях і намагатися знищити ядро. -tutorial.coreDestruction.text = Ваша мета полягає в тому, щоб [yellow] захищати ядро []. Якщо ядро ​​знищено, ви [coral] програєте[]. -tutorial.pausingDesktop.text = Якщо вам коли-небудь потрібно зробити перерву, натисніть кнопку [orange] паузи [] у верхньому лівому куті або на кнопку [orange] пропуск [], щоб призупинити гру. Ви можете вибрати і розмістити блоки під час призупинення, але не можете переміщатися чи стріляти. -tutorial.pausingAndroid.text = Якщо вам коли-небудь потрібно зробити перерву, натисніть кнопку [orange] пауза [] у верхньому лівому куті, щоб призупинити гру. Ти можеш ще знищувати та будувати блоки під час призупинення. -tutorial.purchaseWeapons.text = Ви можете придбати нову [yellow] зброю [] для вашого механізму, відкривши меню оновлення в лівому нижньому кутку. -tutorial.switchWeapons.text = Перемикати зброю будь-яким натисканням його піктограми внизу ліворуч або за допомогою цифр [orange] [[1-9] []. -tutorial.spawnWave.text = Ось хвиля зараз. Знищи їх -tutorial.pumpDesc.text = У пізніших хвилях, можливо, доведеться використовувати [yellow] насоси [] для розподілу рідин для генераторів або екстракторів. -tutorial.pumpPlace.text = Насоси працюють аналогічно свердлам, за винятком того, що вони виробляють рідини замість предметів. Спробуйте встановити насос на [yellow] призначене мастило []. -tutorial.conduitUse.text = Тепер покладіть [orange] трубопровід [], віддаляючись від насоса. -tutorial.conduitUse2.text = І ще кілька ... -tutorial.conduitUse3.text = І ще кілька ... -tutorial.generator.text = Тепер, помістіть блок [orange] ​​базовий генератор енергії [] в кінці каналу. -tutorial.generatorExplain.text = Цей генератор тепер створить [yellow] енергію [] від масла. -tutorial.lasers.text = Потужність розподіляється за допомогою [yellow] лазерів потужності []. Поверніть і помістіть його тут. -tutorial.laserExplain.text = Тепер генератор переведе енергію в лазерний блок. Промінь [yellow] непрозорий [] означає, що в даний час він передає потужність, а промінь [yellow] прозорий [] означає, що це не так. -tutorial.laserMore.text = Ви можете перевірити, скільки енергії в блоку, наведіть курсор миші на нього і перевірте [yellow] жовту стрічку [] у верхній частині екрана. -tutorial.healingTurret.text = Цей лазер може бути використаний для живлення турелі для ремонту [lime] []. Помістіть одну тут. -tutorial.healingTurretExplain.text = Поки вона має енергію, ця турель може [lime] відремонтувати блоки. [] Під час гри постарайтеся збудувати одну таку чим швидше! -tutorial.smeltery.text = Для багатьох блоків потрібна [orange] сталь [], для цього потрібна[orange] доминна піч [] . Місце тут. -tutorial.smelterySetup.text = Ця піч буде тепер виробляти [orange] сталь [] із вхідного заліза, використовуючи вугілля як паливо. -tutorial.tunnelExplain.text = Також зауважте, що елементи проходять через [yellow] тунельний блок [] і з'являються з іншого боку, проходячи через кам'яний блок. Майте на увазі, що тунелі можуть проходити лише до 2 блоків. -tutorial.end.text = Ви завершили підручник! Удачі! -text.keybind.title = Ключ перемотки -keybind.move_x.name = move_x -keybind.move_y.name = move_y -keybind.select.name = Вибрати -keybind.break.name = {0}break{/0}{1}; {/1} +setting.saveinterval.name = Інтервал збереження +setting.seconds = {0} сек. +setting.fullscreen.name = Повноекранний режим +setting.borderlesswindow.name = Вікно без полів[LIGHT_GRAY] (може потребувати перезапуску) +setting.fps.name = Показувати FPS +setting.vsync.name = Вертикальна синхронізація +setting.lasers.name = Показувати енергію лазерів +setting.pixelate.name = Пікселізація[LIGHT_GRAY] (вимикає анімації) +setting.minimap.name = Показати міні-мапу +setting.musicvol.name = Гучність музики +setting.mutemusic.name = Заглушити музику +setting.sfxvol.name = Гучність звукових ефектів +setting.mutesound.name = Заглушити звук +setting.crashreport.name = Надіслати анонімні звіти про аварійне завершення гри +setting.chatopacity.name = Непрозорість чату +setting.playerchat.name = Відображати чат в грі +keybind.title = Налаштування управління +category.general.name = Основне +category.view.name = Перегляд +category.multiplayer.name = Мережева гра +command.attack = Атакувати +command.retreat = Відступити +command.patrol = Патрулювати +keybind.gridMode.name = Вибрати блок +keybind.gridModeShift.name = Вибрати категорію +keybind.press = Натисніть клавішу... +keybind.press.axis = Натисніть клавішу... +keybind.screenshot.name = Скріншот мапи +keybind.move_x.name = Рух по осі x +keybind.move_y.name = Рух по осі x +keybind.select.name = Вибір/Постріл +keybind.diagonal_placement.name = Діагональне Розміщення +keybind.pick.name = Вибрати блок +keybind.break_block.name = Зламати блок +keybind.deselect.name = Скасувати keybind.shoot.name = Постріл -keybind.zoom_hold.name = zoom_hold -keybind.zoom.name = Збільшити -keybind.block_info.name = Інформація про блок +keybind.zoom_hold.name = Удержание зума +keybind.zoom.name = Приблизить keybind.menu.name = Меню keybind.pause.name = Пауза -keybind.dash.name = Тире +keybind.minimap.name = Minimap +keybind.dash.name = Мчати keybind.chat.name = Чат keybind.player_list.name = Список гравців -keybind.console.name = // Консоль 1 -keybind.rotate_alt.name = rotate_alt -keybind.rotate.name = Повернути -keybind.weapon_1.name = Зброя! -keybind.weapon_2.name = Зброя! -keybind.weapon_3.name = Зброя! -keybind.weapon_4.name = Зброя! -keybind.weapon_5.name = Зброя! -keybind.weapon_6.name = Зброя! -mode.waves.name = Хвилі +keybind.console.name = Консоль +keybind.rotate.name = Обертати +keybind.toggle_menus.name = Меню перемикання +keybind.chat_history_prev.name = Попередня історія чату +keybind.chat_history_next.name = Наступна історія чату +keybind.chat_scroll.name = Прокрутка чату +keybind.drop_unit.name = Скинути бой. од. +keybind.zoom_minimap.name = Збільшити мінімапу +mode.help.title = Опис режимів +mode.survival.name = Хвилі +mode.survival.description = Звичайний режим. В цьому режимі треба самим добувати ресурси та хвилі йдуть беззупинно. mode.sandbox.name = Пісочниця -mode.freebuild.name = Вільний режим -upgrade.standard.name = Стандартний -upgrade.standard.description = Стандартний механ. -upgrade.blaster.name = Бластер -upgrade.blaster.description = Стріляє повільно, слабкі кулі. -upgrade.triblaster.name = Трипластер -upgrade.triblaster.description = Вистрілює 3 кулі в розповсюдженні. -upgrade.clustergun.name = Касетна гармата -upgrade.clustergun.description = Вистрілює неточними вибуховими гранатами. -upgrade.beam.name = Пушечна гармата -upgrade.beam.description = Вистрілює далекобійним,пробірний лазерний промінь. -upgrade.vulcan.name = Вулкан -upgrade.vulcan.description = Вистрілює шквал швидких куль. -upgrade.shockgun.name = Шок-пушка -upgrade.shockgun.description = Стріляє руйнівним вибухом заряженої шрапнелі. -item.stone.name = Камінь -item.iron.name = Залізо -item.coal.name = Вугівалля -item.steel.name = Сталь +mode.sandbox.description = В режимі "Пісочниця" незкінченні ресурси(але їх все одно можно добувати) та хвилі йдуть за вашим бажанням. +mode.pvp.name = PVP +mode.pvp.description = боріться проти інших гравців. +mode.attack.name = Атака +mode.attack.description = Зруйнуй базу ворожу базу. Без хвиль. +mode.custom = Користувальницькі правила +rules.infiniteresources = Нескінченні ресурси +rules.wavetimer = Таймер Хвиль +rules.waves = Хвилі +rules.enemyCheat = Нескінченні ресурси для ШІ +rules.unitdrops = Ресурс бойових одиниць +rules.unitbuildspeedmultiplier = Множник Швидкості Виробництва Бойов. Од. +rules.unithealthmultiplier = Множник Здоров'я Створювання Бойов. Од. +rules.playerhealthmultiplier = Множник Здоровя Гравця +rules.playerdamagemultiplier = Множник Шкоди Гравця +rules.unitdamagemultiplier = Множник Шкоди Бойових Одиниць +rules.enemycorebuildradius = Радіус заборони будування для Ворожого Ядра:[LIGHT_GRAY] (плитки) +rules.respawntime = Час відродження:[LIGHT_GRAY] (sec) +rules.wavespacing = Інтервал хвиль:[LIGHT_GRAY] (sec) +rules.buildcostmultiplier = Множник затрат на будівництво +rules.buildspeedmultiplier = Множник швидкості будування +rules.waitForWaveToEnd = Хвилі чекають на ворогів +rules.dropzoneradius = Радіус зони висадки:[LIGHT_GRAY] (блоків) +rules.respawns = Максимальна кількість відроджень за хвилю +rules.limitedRespawns = Обмеження відроджень +rules.title.waves = Хвилі +rules.title.respawns = Відродження +rules.title.resourcesbuilding = Ресурси & будівництво +rules.title.player = Гравці +rules.title.enemy = Вороги +rules.title.unit = Бойов. од. +content.item.name = Предмети +content.liquid.name = Рідини +content.unit.name = Бойові одиниці +content.block.name = Блоки +content.mech.name = Мехи +item.copper.name = Мідь +item.copper.description = Корисний структурний матеріал. Широко використовується у всіх типах блоків. +item.lead.name = Свинець +item.lead.description = Базовий стартовий матеріал. Широко використовується в електроніки та транспорту рідин. +item.coal.name = Вугілля +item.coal.description = Загальне та легкодоступне паливо. +item.graphite.name = Графіт item.titanium.name = Титан -item.dirium.name = Дириум -item.uranium.name = Уран +item.titanium.description = Рідкий суперлегкий метал широко використовується в рідкому транспорті, свердлах та літальних апаратах. +item.thorium.name = Торій +item.thorium.description = Густий, радіоактивний метал, що використовується як структурна підтримка та ядерне паливо. +item.silicon.name = Кремній +item.silicon.description = Надзвичайно корисний напівпровідник з застосуванням в сонячних батареях та складній електроніці. +item.plastanium.name = Пластиній +item.plastanium.description = Легкий, пластичний матеріал, що використовується в сучасних літальних апаратах та у боєприпасах для фрагментації. +item.phase-fabric.name = Фазова тканина +item.phase-fabric.description = Невагома речовина, що використовується в сучасній електроніці і технології самовідновлення. Не для вишивання. +item.surge-alloy.name = Кінетичний сплав +item.surge-alloy.description = Передовий сплав з унікальними електричними властивостями. +item.spore-pod.name = Споровий стручок +item.spore-pod.description = Використовується для перетворення на нафту, вибухові речовини та паливо. item.sand.name = Пісок +item.sand.description = Загальний матеріал, який широко використовується при плавленні, як у процесі плавки, так і у вигляді шлака. +item.blast-compound.name = Вибухова суміш +item.blast-compound.description = Нестійке з'єднання, що використовується в бомбах та вибухових речовинах. Хоча воно може спалюватися як паливо, та це не рекомендується. +item.pyratite.name = Піротит +item.pyratite.description = Вкрай легкозаймиста речовина, що використовується у запальній зброї. +item.metaglass.name = Метаскло +item.metaglass.description = Надміцна суміш скла й метала. Широко використовується для розподілу і зберігання рідини. +item.scrap.name = Металобрухт +item.scrap.description = Залишки старих споруд і бойових одиниць. Містить незначні кількості багатьох різних металів. liquid.water.name = Вода -liquid.plasma.name = Плазма -liquid.lava.name = Лава +liquid.slag.name = Шлак liquid.oil.name = Нафта -block.weaponfactory.name = Фабрика зброї -block.weaponfactory.fulldescription = Використовується для створення зброї для гравця mech. Натисніть, щоб використати. Автоматично приймає ресурси з основного ядра. -block.air.name = Повітря -block.blockpart.name = Блокчастина -block.deepwater.name = Глибока вода +liquid.cryofluid.name = Кріогенна рідина +mech.alpha-mech.name = Альфа +mech.alpha-mech.weapon = Тяжкий кулемет +mech.alpha-mech.ability = Регенерація +mech.alpha-mech.description = Стандартний мех для настільних пристроїв. Має пристойну швидкість і урон. +mech.delta-mech.name = Дельта +mech.delta-mech.weapon = Генератор дуг +mech.delta-mech.ability = Розряд +mech.delta-mech.description = Швидкий, легкоброньований мех, зроблений для ударів з відскоком. Невелика шкода будівлям, але дуже швидко вбиває великі групи ворожих підрозділів своєю дуговою блискавичною зброєю. +mech.tau-mech.name = Тау +mech.tau-mech.weapon = Відновлювальний лазер +mech.tau-mech.ability = Відновлювальний спалах +mech.tau-mech.description = Мех підтримки. Лагодить союзницькі блоки, стріляючи в них. Може зцілити союзників у радіусі зі своєю здатністю для ремонту. +mech.omega-mech.name = Омега +mech.omega-mech.weapon = Ракометний пулемет +mech.omega-mech.ability = Поглинання урона +mech.omega-mech.description = Громіздкий і добре броньований мех, зроблений для фронтових нападів. Його здатність може блокувати до 90% вхідного урона. +mech.dart-ship.name = Дротик +mech.dart-ship.weapon = Ретранслятор +mech.dart-ship.description = Стандартний корабель на мобільних пристріях. Досить швидкий і легкий, але має невеликі наступальні можливості і низьку швидкість видобутку. +mech.javelin-ship.name = Джавелін +mech.javelin-ship.description = Ударний корабель, який використовує набіг з відскоком. Спочатку повільний, але пізніше він може прискоритися до великих швидкостей і літати над ворожими заставами, завдаючи великої шкоди своєю. блискавичною здатністю і ракетами. +mech.javelin-ship.weapon = Вибухові ракети +mech.javelin-ship.ability = Генератор дуг +mech.trident-ship.name = Тризубець +mech.trident-ship.description = Важкий бомбардувальник. Досить добре броньований. +mech.trident-ship.weapon = Бомби +mech.glaive-ship.name = Спис +mech.glaive-ship.description = Великий, добре броньований бойовий корабель. Оснащений запальним ретранслятором. Гарне прискорення і максимальна швидкість. +mech.glaive-ship.weapon = Вогняний кулемет +item.explosiveness = [LIGHT_GRAY]Вибухонебезпечність: {0} +item.flammability = [LIGHT_GRAY]Вогненебезпечність: {0} +item.radioactivity = [LIGHT_GRAY]Радіоактивність: {0} +unit.health = [LIGHT_GRAY]Здоров'я: {0} +unit.speed = [LIGHT_GRAY]Швидкість: {0} +mech.weapon = [LIGHT_GRAY]Зброя: {0} +mech.health = [LIGHT_GRAY]Здоров'я: {0} +mech.itemcapacity = [LIGHT_GRAY] Ємність елементів: {0} +mech.minespeed = [LIGHT_GRAY]Швидкість видобутку: {0}% +mech.minepower = [LIGHT_GRAY]Потужність видобутку: {0} +mech.ability = [LIGHT_GRAY]Здібність: {0} +mech.buildspeed = [LIGHT_GRAY]Швидкість будування: {0}% +liquid.heatcapacity = [LIGHT_GRAY]Теплоємність: {0} +liquid.viscosity = [LIGHT_GRAY]В'язкість: {0} +liquid.temperature = [LIGHT_GRAY]Температура: {0} +block.grass.name = Трава +block.salt.name = Сіль +block.saltrocks.name = Сіляні Камні +block.pebbles.name = Галька +block.tendrils.name = Щупальця +block.sandrocks.name = Піщані Скелі +block.spore-pine.name = Спорова Сосна +block.sporerocks.name = Спорові Камні +block.rock.name = Камінь +block.snowrock.name = Сніжний Камінь +block.shale.name = Сланець +block.shale-boulder.name = Сланцевий Валун +block.moss.name = Мох +block.shrubs.name = Кущі +block.spore-moss.name = Споровий Мох +block.shalerocks.name = Сланцеві Породи +block.scrap-wall.name = Стіна з металобрухту +block.scrap-wall-large.name = Велика стіна з металобрухту +block.scrap-wall-huge.name = Величезна стіна з металобрухту +block.scrap-wall-gigantic.name = Гігантська стіна з металобрухту +block.thruster.name = Штовхач +block.kiln.name = Піч +block.kiln.description = Виплавляє пісок і свинець в метаскло. Потребує малої кількості енергії. +block.graphite-press.name = Графітний прес +block.multi-press.name = Мульти-прес +block.constructing = {0}\n[LIGHT_GRAY](В процесі) +block.spawn.name = Місце появи ворога +block.core-shard.name = Ядро: Осколок +block.core-foundation.name = Ядро: Штаб +block.core-nucleus.name = Ядро: Атом +block.deepwater.name = Глибоководдя block.water.name = Вода -block.lava.name = Лава -block.oil.name = Нафта +block.tainted-water.name = Забруднена вода +block.darksand-tainted-water.name = Темний пісок з забрудненою водою +block.tar.name = Дьоготь block.stone.name = Камінь -block.blackstone.name = Чорний камінь -block.iron.name = Залізо -block.coal.name = Вугілля -block.titanium.name = Титан -block.uranium.name = Уран -block.dirt.name = Бруд block.sand.name = Пісок +block.darksand.name = Темний пісок block.ice.name = Лід block.snow.name = Сніг -block.grass.name = Трава -block.sandblock.name = Блок піску -block.snowblock.name = Блок снігу -block.stoneblock.name = Блок камню -block.blackstoneblock.name = Блок чорного камню -block.grassblock.name = Блок бруду -block.mossblock.name = Моссблок -block.shrub.name = Чагарник -block.rock.name = Камень -block.icerock.name = Ледяний камень -block.blackrock.name = Чорний камінь -block.dirtblock.name = Блок землі -block.stonewall.name = Кам'яна стіна -block.stonewall.fulldescription = Недорогий захисний блок. Корисно для захисту ядра та турелі в перші кілька хвиль. -block.ironwall.name = Залізна стіна -block.ironwall.fulldescription = Основний захисний блок. Забезпечує захист від ворогів. -block.steelwall.name = Сталева стіна -block.steelwall.fulldescription = Стандартний захисний блок. адекватний захист від ворогів. -block.titaniumwall.name = Титанова стіна -block.titaniumwall.fulldescription = Сильний захисний блок. Забезпечує захист від ворогів. -block.duriumwall.name = Діріумова стіна -block.duriumwall.fulldescription = Дуже сильний захисний блок. Забезпечує захист від ворогів. -block.compositewall.name = Композитна стіна -block.steelwall-large.name = Велика сталева стіна -block.steelwall-large.fulldescription = Стандартний захисний блок. Поєднує в собі кілька блоків. -block.titaniumwall-large.name = Велика титанова стіна -block.titaniumwall-large.fulldescription = Сильний захисний блок. Поєднує в собі кілька блоків. -block.duriumwall-large.name = Велика дирмітова стіна -block.duriumwall-large.fulldescription = Дуже сильний захисний блок.Поєднує в собі кілька блоків. -block.titaniumshieldwall.name = Стіна з щитом -block.titaniumshieldwall.fulldescription = Сильний захисний блок з додатковим вбудованим щитом. Потрібна енергія. Використовує енергію для поглинання ворожих куль. Рекомендується використовувати силові пристосування для забезпечення енергії цього блоку. -block.repairturret.name = Ремонтна турель -block.repairturret.fulldescription = Ремонтує недалекі пошкодженні блоки.Повільний темп. Використовує невелику кількість енергії. -block.megarepairturret.name = Ремонтна турель II -block.megarepairturret.fulldescription = Ремонтує недалекі пошкодженні блоки.Збільшений радіус та швидший темп ремонту . Використовує багато енергії. -block.shieldgenerator.name = Генератор щиту -block.shieldgenerator.fulldescription = Передовий захисний блок. Захищає всі блоки в радіусі від нападу. Не вкористовує енергію при бездіяльності, але швидко витрачає енергію на захист від куль. +block.craters.name = Кратери +block.sand-water.name = Пісок з водою +block.darksand-water.name = Темний пісок з водою +block.char.name = Випалена Земля +block.holostone.name = Голографічний камінь +block.ice-snow.name = Крижаний Сніг +block.rocks.name = Камені +block.icerocks.name = Крижані камені +block.snowrocks.name = Снігові камені +block.dunerocks.name = Піщані Камені +block.pine.name = Сосна +block.white-tree-dead.name = Мертве Біле Дерево +block.white-tree.name = Біле Дерево +block.spore-cluster.name = Скупчення спор +block.metal-floor.name = Металевий Пол 1 +block.metal-floor-2.name = Металевий Пол 2 +block.metal-floor-3.name = Металевий Пол 3 +block.metal-floor-5.name = Металевий Пол 4 +block.metal-floor-damaged.name = Пошкоджений Металевий Пол +block.dark-panel-1.name = Темна Панель 1 +block.dark-panel-2.name = Темна Панель 2 +block.dark-panel-3.name = Темна Панель 3 +block.dark-panel-4.name = Темна Панель 4 +block.dark-panel-5.name = Темна Панель 5 +block.dark-panel-6.name = Темна Панель 6 +block.dark-metal.name = Темний метал +block.ignarock.name = Магматичні Гірські Породи +block.hotrock.name = Гарячий Камінь +block.magmarock.name = Магмакамінь +block.cliffs.name = Скелі +block.copper-wall.name = Мідна стіна +block.copper-wall-large.name = Велика мідна стіна +block.titanium-wall.name = Титанова стіна +block.titanium-wall-large.name = Велика титанова стіна +block.phase-wall.name = Фазова стіна +block.phase-wall-large.name = Велика фазова стіна +block.thorium-wall.name = Торієва стіна +block.thorium-wall-large.name = Велика торієва стіна block.door.name = Двері -block.door.fulldescription = Блок, який можна відкрити та закрити, торкнувшись його. block.door-large.name = Великі двері -block.door-large.fulldescription = Блок, який можна відкрити та закрити, торкнувшись його. -block.conduit.name = Трубопровід -block.conduit.fulldescription = Основний транспортний блок. Працює як конвеєр, але з рідинами. Найкраще використовується з насосами або іншими трубопроводами. Може використовуватися як міст через рідини для ворогів та гравців. -block.pulseconduit.name = Імпульсний канал -block.pulseconduit.fulldescription = Покращенний блок перевезення рідин. Транспортує рідини швидше і зберігає більше стандартних каналів. -block.liquidrouter.name = маршрутизатор для рідини -block.liquidrouter.fulldescription = Працює аналогічно маршрутизатору. Приймає рідину ввід з одного боку і виводить його на інші сторони. Корисний для розщеплення рідини з одного каналу на кілька інших трубопроводів. +block.duo.name = Подвійна +block.scorch.name = Випалювач +block.scatter.name = Розсіювач +block.hail.name = Град +block.lancer.name = Списоносець block.conveyor.name = Конвеєр -block.conveyor.fulldescription = Базовий транспортний блок. Переміщує предмети вперед і автоматично вкладає їх у турелі або ремісники. Поворотний Може використовуватися як міст через рідину для ворогів та гравців. -block.steelconveyor.name = Сталевий конвеєр -block.steelconveyor.fulldescription = Розширений блок транспортування предметів. Переміщення елементів швидше, ніж стандартні конвеєри. -block.poweredconveyor.name = Імпульсний конвеєр -block.poweredconveyor.fulldescription = Кінцевий транспортний блок. Переміщення елементів швидше, ніж сталеві конвеєри. +block.titanium-conveyor.name = Титановий конвеєр +block.junction.name = Перехрестя block.router.name = Маршрутизатор -block.router.fulldescription = Приймає елементи з одного напрямку і виводить їх на 3 інших напрямках. Можна також зберігати певну кількість предметів. Використовується для розщеплення матеріалів з одного свердла на декілька башточок. -block.junction.name = Міст -block.junction.fulldescription = Виступає як міст для двох перехресних конвеєрних стрічок. Корисне у ситуаціях з двома різними конвеєрами, що несуть різні матеріали в різних місцях. -block.conveyortunnel.name = Конвеєрний тунель -block.conveyortunnel.fulldescription = Транспортує предмети під блоками. Щоб використати, помістіть один тунель, що веде у блок, щоб бути підсвіченим, а один - з іншого боку. Переконайтеся, що обидва тунелі стикаються з протилежними напрямками, тобто до блоків, які вони вводять або виводять. -block.liquidjunction.name = Міст для рідини -block.liquidjunction.fulldescription = Діє як міст для двох перехресних трубопроводів. Корисно в ситуаціях з двома різними трубами, що несуть різні рідини в різних місцях. -block.liquiditemjunction.name = Перехрестя рідкого пункту -block.liquiditemjunction.fulldescription = Виступає як міст для перетину трубопроводів і конвеєрів. -block.powerbooster.name = Підсилювач потужності -block.powerbooster.fulldescription = Поширює енергію на всі блоки в межах його радіуса. -block.powerlaser.name = Енергетичний лазер -block.powerlaser.fulldescription = Створює лазер, який передає енергію блоку перед ним. Не створює жодної сили сама. Найкраще використовується з генераторами або іншими лазерами. -block.powerlaserrouter.name = Лазерний маршрутизатор -block.powerlaserrouter.fulldescription = Лазер, який розподіляє енергію у три напрямки одночасно. Корисно в ситуаціях, коли потрібно живити кілька блоків від одного генератора. -block.powerlasercorner.name = Лазерний кут -block.powerlasercorner.fulldescription = Лазер, який розподіляє енергію одночасно на два напрямки. Корисно в ситуаціях, коли потрібно живити кілька блоків від одного генератора, а маршрутизатор неточний. -block.teleporter.name = Телепорт -block.teleporter.fulldescription = Продвинутий блок транспортування предметів.Щоб телепортувати предмети з одного місця в інше потрібно збудувати 2 телепорти і назначити на них одинаковий колір. Використовує енергію. Натисніть, щоб змінити колір. +block.distributor.name = Розподілювач block.sorter.name = Сортувальник -block.sorter.fulldescription = Сортує предмети за типом матеріалу. Матеріал для прийняття позначається кольором у блоці. Всі елементи, що відповідають матеріалу сортування, виводяться вперед, а все інше виводить ліворуч і праворуч. -block.core.name = Ядро -block.pump.name = Насос -block.pump.fulldescription = Насоси рідини з вихідного блоку - зазвичай вода, лава чи олія. Виводить рідину в сусідні трубопроводи. -block.fluxpump.name = Флюсовий насос -block.fluxpump.fulldescription = Розширений варіант насоса. Зберігає більше рідини та перекачує швидше. -block.smelter.name = Плавильня -block.smelter.fulldescription = Основний ремісничий блок. Коли вводиться 1 залізо та 1 вугілля в якості палива, виводить одну сталь. Рекомендується вводити залізо та вугілля на різних поясах, щоб запобігти засміченню. -block.crucible.name = Тигель -block.crucible.fulldescription = Розширений блок обробки. При введенні 1 титану, 1 сталі та 1 вугілля в якості пального, виводить один дирний. Рекомендується вводити вугілля, сталь та титан на різних поясах, щоб запобігти засміченню. -block.coalpurifier.name = вугільний екстрактор -block.coalpurifier.fulldescription = Основний екстрактор. Виходить вугілля при постачанні великої кількості води та каменю. -block.titaniumpurifier.name = Титановий екстрактор -block.titaniumpurifier.fulldescription = Стандартний блок екстрактора. Виходить титан при постачанні великої кількості води та заліза. -block.oilrefinery.name = Нафтопереробний завод -block.oilrefinery.fulldescription = Очищує велику кількість нафти і перетворює на вугілля. Корисний для заправки вугільних башточок, коли вугільні родовища є дефіцитними. -block.stoneformer.name = Кам'янний екстрактор -block.stoneformer.fulldescription = Здавлюється рідка лава в камінь. Корисно для виготовлення великої кількості каменю для очищувачів вугілля. -block.lavasmelter.name = Лавовий завод -block.lavasmelter.fulldescription = Використовує лаву для перетворення залізо на сталь. Альтернатива плавильні. Корисно в ситуаціях, коли вугілля є дефіцитним. -block.stonedrill.name = Кам'янна свердловина -block.stonedrill.fulldescription = Основна свердловина.Розміщюється на кам'яній плитці виводить камінь повільними темпами. -block.irondrill.name = Залізна свердловина -block.irondrill.fulldescription = Базова свердловина.Розміщюється на родовищі залізної руди, випускає залізо в повільному темпі. -block.coaldrill.name = Вугільна свердловина -block.coaldrill.fulldescription = Базова свердловина.Розміщюється на родовищі вугільної руди ,видобуває вугілля повільними темпами. -block.uraniumdrill.name = Уранова свердловина -block.uraniumdrill.fulldescription = Продвинута свердловина. Розміщюється на родовищі уранової руди.Видобуток урану відбувається повільними темпами. -block.titaniumdrill.name = Титанова свердловина -block.titaniumdrill.fulldescription = Продвинута свердловина.Розміщюється на родовищі титанової руди, Видобуток титану відбувається повільним темпом. -block.omnidrill.name = Убер свердловина -block.omnidrill.fulldescription = Кінцева свердловина.Дуже швидко видобуває будь-який вид руди. -block.coalgenerator.name = Вугільний генератор -block.coalgenerator.fulldescription = Основний генератор. Генерує енергію з вугілля. Виводиться потужність лазерів на 4 сторони. -block.thermalgenerator.name = Теплогенератор -block.thermalgenerator.fulldescription = Генерує енергію від лави. Виводиться потужність лазерів на 4 сторони. -block.combustiongenerator.name = Генератор горіння -block.combustiongenerator.fulldescription = Генерує енергію з нафти. Виводиться потужність лазерів на 4 сторони. -block.rtgenerator.name = RTG генератор -block.rtgenerator.fulldescription = Генерує невелику кількість енергії з радіоактивного розпаду урану. Виводиться потужність лазерів на 4 сторони. -block.nuclearreactor.name = Ядерний реактор -block.nuclearreactor.fulldescription = Розширений варіант RTG Generator і кінцевий генератор електроенергії. Генерує енергію з урану. Потребує постійного водяного охолодження.Сильно вибухне якщо не буде постачання води у великії кількості -block.turret.name = Турель -block.turret.fulldescription = Базова, дешева турель. Використовує камінь для боєприпасів. Має трохи більше діапазону, ніж подвійна турель. -block.doubleturret.name = Подвійна турель -block.doubleturret.fulldescription = Дещо потужна версія турель. Використовує камінь для боєприпасів. Значно більший урон, але менший діапазон. Вистрілює дві кулі. -block.machineturret.name = Кулеметна турель -block.machineturret.fulldescription = Стандартна всеосяжна турель. Використовує залізо для боєприпасів. Має швидку швидкість пострілу і гідну шкоду. -block.shotgunturret.name = Розріджуюча турель -block.shotgunturret.fulldescription = Стандартна турель. Використовує залізо для боєприпасів. Вистрілює 7 куль навколо себе.Наносить значних ушкоджень,звісно якщо поцілить :) -block.flameturret.name = Вогнемет -block.flameturret.fulldescription = Продвинута турель ближнього діапазону. Використовує вугілля для боєприпасів. Має дуже низький радіус, але дуже високий збиток. Добре для близьких дистанцій. Рекомендується використовувати за стінами. -block.sniperturret.name = Лазерна турель. -block.sniperturret.fulldescription = Продвинута далекобійна турель. Використовує сталь для боєприпасів. Дуже високий збиток, але низький рівень урону. Дорогі для використання, але можуть бути розташовані далеко від ліній ворога через його радіус. -block.mortarturret.name = Флак турель -block.mortarturret.fulldescription = Продвинута,неточна турель. Використовує вугілля для боєприпасів. Стріляє кулями, що вибухають у шрапнеллв. Корисне для великих натовпів ворогів. -block.laserturret.name = Лазерна турель -block.laserturret.fulldescription = Продвинута однопушечна турель. Використовує енергію. Хороша на середніх дистанціях. Ніколи не пропускає. -block.waveturret.name = Тесла -block.waveturret.fulldescription = Передова багатоцільова турель. Використовує енергію. Середній радіус. Ніколи не пропускає. Активно знижує, але може вражати декількох ворогів одночасно з ланцюговим освітленням. -block.plasmaturret.name = Плазмова турель -block.plasmaturret.fulldescription = Дуже продвинута версія Вогнеметної турелі. Використовує вугілля як боєприпаси. Дуже високий урон, від близької до середньої дистанції. -block.chainturret.name = Уранова турель -block.chainturret.fulldescription = Остаточна швидкістна вежа. Використовує уран як боєприпаси. Вистрілює великі кулі при високій швидкості вогню. Середній радіус. Промінь кілька плиток. Надзвичайно жорсткий. -block.titancannon.name = Титанова гармата -block.titancannon.fulldescription = Найбільш далекобійна турель. Використовує уран як боєприпаси. Вистрілює великі снаряди. Далекобійний. Промінь кілька плиток. Надзвичайно жорсткий. -block.playerspawn.name = Спавн Гравця -block.enemyspawn.name = Спавн ворогів +block.sorter.description = Сортує предмети. Якщо товар відповідає виділенню, йому можна пройти. В іншому випадку предмет виводиться ліворуч і праворуч. +block.overflow-gate.name = Надмірний затвор +block.overflow-gate.description = Комбінований розподілювач і маршрутизатор, який виводить тільки вліво та вправо, якщо передній шлях заблоковано. +block.silicon-smelter.name = Кремнієвий завод +block.phase-weaver.name = Фазовий ткач +block.pulverizer.name = Розпилювач +block.cryofluidmixer.name = Змішувач кріогненної рідини +block.melter.name = Плавильня +block.incinerator.name = Сміттєспалювальний завод +block.spore-press.name = Споровий Прес +block.separator.name = Сепаратор +block.coal-centrifuge.name = Вугільна центрифуга +block.power-node.name = Енергійний вузол +block.power-node-large.name = Великий енергетичний вузол +block.surge-tower.name = Кінетична вежа +block.battery.name = Акумулятор +block.battery-large.name = Великий акумулятор +block.combustion-generator.name = Генератор горіння +block.turbine-generator.name = Турбогенератор +block.differential-generator.name = Дифернеціальний генератор +block.impact-reactor.name = Імпульсний реактор +block.mechanical-drill.name = Механічний дриль +block.pneumatic-drill.name = Пневматичний дриль +block.laser-drill.name = Лазерний дриль +block.water-extractor.name = Гідроконденсатор +block.cultivator.name = Культиватор +block.dart-mech-pad.name = Реконструктор "Альфа" +block.delta-mech-pad.name = Реконструктор "Дельта" +block.javelin-ship-pad.name = Реконструктор "Джавелін" +block.trident-ship-pad.name = Реконструктор "Тризуб" +block.glaive-ship-pad.name = Реконструктор "Спис" +block.omega-mech-pad.name = Реконструктор "Омега" +block.tau-mech-pad.name = Реконструктор "Тау" +block.conduit.name = Трубопровід +block.mechanical-pump.name = Механічний насос +block.item-source.name = Джерело предметів +block.item-void.name = Предметний вакуум +block.liquid-source.name = Рідке джерело +block.power-void.name = Енергетичний вакуум +block.power-source.name = Нескінченна енергія +block.unloader.name = Розвантажувач +block.vault.name = Сховище +block.wave.name = Хвиля +block.swarmer.name = Роєвик +block.salvo.name = Залп +block.ripple.name = Рябь +block.phase-conveyor.name = Фазовий конвеєр +block.bridge-conveyor.name = Мостовий конвеєр +block.plastanium-compressor.name = Пластиновий компресор +block.pyratite-mixer.name = Змішувач піротиту +block.blast-mixer.name = Доменний змішувач +block.solar-panel.name = Сонячна панель +block.solar-panel-large.name = Велика сонячна панель +block.oil-extractor.name = Нафтова вежа +block.spirit-factory.name = Завод дронов "Призрак" +block.phantom-factory.name = Завод дронов "Фантом" +block.wraith-factory.name = Завод винищувачів "Примара" +block.ghoul-factory.name = Завод бомбардувальників-винищувачів "Ґуль" +block.dagger-factory.name = Завод мехів "Кинджал" +block.crawler-factory.name = Завод мехів "Камікадзе" +block.titan-factory.name = Завод мехів "Титан" +block.fortress-factory.name = Завод мехів "Крепость" +block.revenant-factory.name = Завод бомбардировщиков "Потусторонний убийца" +block.repair-point.name = Ремонтний пункт +block.pulse-conduit.name = Імпульсний водовід +block.phase-conduit.name = Фазовий водопровід +block.liquid-router.name = Рідкий маршрутизатор +block.liquid-tank.name = Рідкий резервуар +block.liquid-junction.name = Рідкий з'єднання +block.bridge-conduit.name = Мостовий водопровід +block.rotary-pump.name = Роторний насос +block.thorium-reactor.name = Торієвий реактор +block.mass-driver.name = Електромагнітна катапульта +block.blast-drill.name = Бурова установка +block.thermal-pump.name = Тепловий насос +block.thermal-generator.name = Тепловий генератор +block.alloy-smelter.name = Сплавовий завод +block.mender.name = Mender +block.mend-projector.name = Ремонту гранатомет +block.surge-wall.name = Хвиляста стіна +block.surge-wall-large.name = Велика хвиляста стіна +block.cyclone.name = Циклон +block.fuse.name = Підривник +block.shock-mine.name = Міна +block.overdrive-projector.name = Сверхприводний проектор +block.force-projector.name = Силовий проектор +block.arc.name = Дуга +block.rtg-generator.name = Радіоізотопний термоелектричний генератор +block.spectre.name = Мара +block.meltdown.name = Розтоплення +block.container.name = Склад +block.launch-pad.name = Стартовий майданчик +block.launch-pad.description = Запускає партії елементів без необхідності запуску ядра. Незакінчено. +block.launch-pad-large.name = Великий Стартовий майданчик +team.blue.name = Синя +team.red.name = Червона +team.orange.name = Помаренчева +team.none.name = Сіра +team.green.name = Зелена +team.purple.name = Фіолетова +unit.spirit.name = Дрон-привид +unit.spirit.description = Початковий дрон. З'являється в ядрі за замовчуванням. Автоматично добуває руди та ремонтує блоки. +unit.phantom.name = Фантом +unit.phantom.description = Покращений дрон. Автоматично добуває руди та ремонтує блоки. +unit.dagger.name = Кинджал +unit.dagger.description = Базова наземна бойова одиниця. Корисен у купі. +unit.crawler.name = Камікадзе +unit.titan.name = Титан +unit.titan.description = Покращена броньована наземна бойова одиниця. Атакує наземні та повітряні цілі. +unit.ghoul.name = Бомбардувальний "Ґуль" +unit.ghoul.description = Тяжкий ковровий бомбардувальник. +unit.wraith.name = Примарний винищувач +unit.wraith.description = Швидка бойова одиниця, котрий використовує набіг з відскоком. +unit.fortress.name = Фортеця +unit.fortress.description = Тяжка артилерійна наземна бойова одиниця. +unit.revenant.name = Потойбічний вбивця +unit.eruptor.name = Вивиргатель +unit.chaos-array.name = Масив хаосу +unit.eradicator.name = Викорінювач +unit.lich.name = Лич +unit.reaper.name = Жнець +tutorial.begin = Ваша місія тут полягає в ліквідації[LIGHT_GRAY] противника[].\n\nПочнімо з[accent] видобутку міді[]. Щоб зробити це, торкніться мідної рудної жили біля вашого ядра. +tutorial.drill = Ручна робота не ефективна\n[accent]Бури []можуть копати автоматично.\nПоставте один на мідній жилі. +tutorial.conveyor = [accent]Конвейери[] використовуються для транспортування предметів в ядра.\nЗробіть лінію конвейерів від бурів до ядра. +tutorial.morecopper = Треба більше міді\n\nДобудьте вручну або поставте більше бурів +tutorial.turret = Захистні споруди повинні бути побудованими для захисту від [LIGHT_GRAY] ворога[].\nПобудуйте подвійну турель біля бази. +tutorial.drillturret = Подвійні турелі потребують[accent] мідні патрони []для стрільби.\nПоставте бури поруч з турелею, щоб знаюдити її видобутою міддю. +tutorial.waves = [LIGHT_GRAY] Супротивник[] прижується.\n\nЗахистіть своє ядро від двух хвиль противника. Побудуйте більше турелей. +tutorial.lead = Стало доступно більше руд. Знайдіть і добудьте[accent] свинець[].\n\nПеретягніть з вашого меху в ядро для передачі ресурсів. +tutorial.smelter = Свинець і мідь тяжкі метали.\nНайліпший[accent] графіт[] може бути створеним у плавильному заводі.\n\nПобудуйте один завод. +tutorial.densealloy = Плавильний завод тепер буде створювати сплав. +tutorial.siliconsmelter = Ядро зараз створить[accent] дрона привида[] для добування і ремонтування блоків.\n\nЗаводи для других одиниць(юнітов) може бути створено за допомогою [accent] кремнія.\nЗробіть кремнієвий завод. +tutorial.silicondrill = Кремній потребує[accent] вугілляl[] та[accent] пісок[].\nПочніть зі створення бурів. +tutorial.generator = Ця технологія потребує енергії.\nЗробіть[accent] генератор внутрішнього згорання[] для цього. +tutorial.generatordrill = Генератор потребує вугілля.\nПобудуйте бур на вугільній жилі. +tutorial.node = Енергії потребує транспортування\nСоздайте[accent] силовий вузол[] поруч з вашим генератором згорання, щоб передавати його енергію. +tutorial.nodelink = Енергія може бути передана через контактні енергетичні блоки та генератори, або з'єднані силові вузли.\n\nЗ'єднайте їх, торкнувшись вузла та вибравши генератор і кремнієвий завод. +tutorial.silicon = Кремній почався створюватися. Отримайте трохи.\n\nРекомендується вдосконалити виробничу систему. +tutorial.daggerfactory = Побудуйте[accent] завод "Кинджал".[]\n\nЦе буде використано для створення мехів атаки. +tutorial.router = Фабрики потребують ресурсів для функціонування.\nСтворіть маршрутизатор для розподілення ресурсів з конвейера. +tutorial.dagger = Зв'яжіть силовий вузол з заводом.\nЯк тільки вимоги будуть виконані, буде створено мех.\n\nЯкщо необхідно, то створіть ще бурів, генераторів та конвейерів +tutorial.battle = [LIGHT_GRAY] Супротивник[] показав своє ядро.\nЗнищьте його з вашим мехом та бойовою одиницею. +block.copper-wall.description = Дешевий оборонний блок.\nКорисен для захисту ядра і турелей під час перших хвиль. +block.copper-wall-large.description = Велика стіна найменшим запасом міцності.\nКорисна на початку гри. +block.thorium-wall.description = Стіна з показником міцності "вище середнього". +block.thorium-wall-large.description = Велика стіна з показником міцності "вище середнього". +block.phase-wall.description = Стіна з середнім показником міцності. +block.phase-wall-large.description = Велика стіна з середнім показником міцності. +block.surge-wall.description = Стіна з найбільшим показником міцності. +block.surge-wall-large.description = Велика стіна з найбільшим запасом міцності. +block.door.description = Двері в прямому сенсі цього слова. Просто натисніть на неї. +block.door-large.description = Для великих стін потрібні великі двері.\nДля відкриття або закриття просто натисніть на неї. +block.mend-projector.description = Ремонтує будівлі в невеликому радіусі. Споживає енергію. +block.overdrive-projector.description = Прискорює в невеликому радіусі роботу механізмів. +block.force-projector.description = Створює в невеликому радіусі силове поле, яке захищає від атак супротивника. +block.shock-mine.description = Поставте її на землю. Вона б'ється ЕЛЕКТРИКОЮ О_О +block.duo.description = Маленька і дешева турель.Корисно від наземних одиниць.\nДля прискорення стрільби можна підвести воду або кріогенну рідину. +block.scatter.description = Протиповітряна турель середнього розміру. Розпорошує грудки свинцю або металобрухту на ворожих бойових одиниць. +block.arc.description = Невелика турелька з близьким радіусом дії, яка стріляє електрикою у випадковій дузі до ворога.\nДля прискорення стрільби можна підвести воду або кріогенну рідину. +block.hail.description = Далекобійна початкова турель.\nДля прискорення стрільби можна підвести воду або кріогенну рідину. +block.lancer.description = Турель, яка стріляє лазером на середню відстань.\nДля прискорення стрільби можна підвести воду або кріогенну рідину. +block.wave.description = Турель з середнім радіусом атаки, яка відштовхує супротивників в сторони. +block.salvo.description = Турель з середнім радіусом атаки.\nДля прискорення стрільби можна підвести воду або кріогенну рідину. +block.swarmer.description = Середній розмір турелі з великим радіусом атаки, яка стріляє ракетами.\nДля прискорення стрільби можна підвести воду або кріогенну рідину. +block.ripple.description = Велика артилерійська турель, яка одночасно стріляє декільками пострілами. +block.cyclone.description = Велика швидка вогняна турель. \nДля прискорення стрільби можна підвести воду або кріогенну рідину. +block.fuse.description = Велика турель, яка стріляє потужними ближніми променями.\nДля прискорення стрільби можна підвести воду або кріогенну рідину. +block.spectre.description = Велика вежа, яка стріляє зразу двома потужніми кулями.\nДля прискорення стрільби можна підвести воду або кріогенну рідину. +block.meltdown.description = Велика турель, яка стріляє могутніми далекобійними променями. +block.conveyor.description = Переміщує ресурси з малою швидкістю. +block.titanium-conveyor.description = Конвеєр другого покоління. Збільшена швидкість переміщення предметів і міцність конвеєра. +block.phase-conveyor.description = Поки гра знаходиться в 2D, цей конвеєр вже в чотиривимірному просторі (можливо, це брехня).\nПотребує енергії і підключається як мостовий конвеєр. +block.junction.description = Назва говорить сама за себе. За допомогою нього можна зробити дві конвеєрні стрічки, які проходять через один одного і не змішуються. +block.mass-driver.description = При наявності енергії передають ресурси на відстань 100 блоків, стріляючи в один-одного. +block.silicon-smelter.description = За допомогою піску, вугілля і енергії виробляє кремній. +block.plastanium-compressor.description = Створює пластинійи з титану і нафти. Вимагає енергії. Для прискорення виробництва можна додати в компресор пісок. +block.phase-weaver.description = Виробляє фазову тканину торію і піску. Вимагає багато енергії. +block.alloy-smelter.description = Створює кінетичний сплав з титану, кременя, міді і свинця. Вимагає енергію. +block.pulverizer.description = Подрібнює металобрухт в пісок. Вимагає енергії. +block.pyratite-mixer.description = Створює піротит з вугілля та свинцю. Вимагає енергії. +block.blast-mixer.description = Створює вибухонебезпечне з'єднання з нафти і піротіта. Для прискорення виробництва можна додати в мішалку пісок. +block.cryofluidmixer.description = Виробляє криогенну рідину з води і титану. Вимагає енергії. +block.melter.description = Розплавляє металобрухт у шлаки для подальшої переробки або використання у турелях. +block.incinerator.description = Якщо є непотрібні ресурси, можна просто їх спалити.\nВимагає енергії. +block.spore-press.description = Стискає спорові стручки в нафту. +block.separator.description = Витягує корисні мінерали з шлаку. +block.power-node.description = Максимум допустимо 4 підключення.\nЩоб з'єднати з якимось блоком потрібно наступне:\n1. Щоб він знаходився в радіусі дії \n2. Натиснити на потрібний силовий вузол, а потім на інший силовий вузол або блок. +block.power-node-large.description = Силовий вузол другого покоління. Збільшено радіус дії і кількість максимально допустимих підключень. +block.battery.description = Хранит энергию всякий раз, когда есть изобилие, и обеспечивает мощность всякий раз, когда есть недостаток, если есть мощность, але БАТАРЕЙКИ DURACELL ЗБЕРІГАЮТЬ БІЛЬШЕ! (прихована реклама) +block.battery-large.description = Зберігає значно більше енергії, ніж звичайна батарейка... +block.combustion-generator.description = Генерує енергію, спалюючи нафту або легкозаймисті матеріали. +block.turbine-generator.description = Більш ефективний, ніж генератор горіння, але вимагає додаткової води. +block.thermal-generator.description = Генерує енергію при розміщенні в гарячих місцях. +block.solar-panel.description = Забезпечує невелику кількість енергії від сонця. +block.solar-panel-large.description = Забезпечує набагато краще джерело живлення, ніж стандартна панель, але в той же час набагато дорожче її побудувати. +block.thorium-reactor.description = Генерує величезну кількість енергії з високорадіоактивного торію. Потрібно постійне охолодження. Буде сильно вибухнути, якщо буде поставлено недостатню кількість охолоджуючої рідини. Вихідна потужність залежить від повноти, з базовою потужністю, що генерується на повну потужність. +block.rtg-generator.description = Радіоізотопний термоелектричний генератор, який не потребує охолодження, але забезпечує меншу потужність, ніж торієвий реактор. +block.unloader.description = Вивантажує предмети з контейнера, сховища або ядра на конвеєр або безпосередньо в сусідній блок. Тип вивантажуваного елемента можна змінити, торкнувшись розвантажувача. +block.container.description = Зберігає невелику кількість предметів кожного типу. Сусідні контейнери, склепіння та ядра будуть розглядатися як одиничне сховище. [LIGHT_GRAY]Розвантажувач [] можна використовувати для вилучення елементів з контейнера. +block.vault.description = Зберігає велику кількість предметів кожного типу. Сусідні контейнери, сховища та ядра будуть розглядатися як одиничне сховище. [LIGHT_GRAY]Розвантажувач[] можна використовуватися для отримання елементів із сховища. +block.mechanical-drill.description = Найперший доступний бур.\nВидобуває мідь, свинець, вугілля, пісок. \nМожно підвести до нього[BLUE] воду []для збільшення швидкості буріння. +block.pneumatic-drill.description = Покращена версія механічного бура.\nВидобуває теж саме, що і механічний бур. Також може добувати титан і камінь.\nМожно підвести до нього[BLUE] воду []для збільшення швидкості буріння. +block.laser-drill.description = Покращена версія пневматичного бура.\nДобивает теж саме, що і пневматичний бур. Також може добувати торій.\nМожно підвести до нього[BLUE] воду []для збільшення швидкості свердління. +block.blast-drill.description = Найпотужніший бур.\n\nВидобуває теж саме, що і лазерний бур. Свердлить швидше всіх бурів, але вимагає ще більше енергії.\nМожно підвести до нього[BLUE] воду [] для збільшення швидкості буріння. +block.water-extractor.description = Витягує воду з землі. Використовуйте його, коли поблизу немає озера. +block.cultivator.description = Культивує крихітні концентрації спор в готових до промисловості стручки. +block.oil-extractor.description = Використовує велику кількість енергії для видобутку нафти з піску, динозаврів (закреслено). Використовуйте його, коли поблизу немає прямого джерела нафти. +block.trident-ship-pad.description = Залиште свій поточний корабель і перейдіть в досить добре броньований важкий бомбардувальник.\nВикористовуйте подвійне натискання, стоячи на реконструкторів, щоб перетворитися в цей мех. +block.javelin-ship-pad.description = Залиште свій поточний корабель і перейдіть в сильний і швидкий перехоплювач з блискавичним зброєю.\nВикористовуйте подвійне натискання, стоячи на реконструкторів, щоб перетворитися в цей мех. +block.glaive-ship-pad.description = Залиште своє існуюче судно і перетворитесь на великий, добре броньований мех.\nВикористовуйте подвійне натискання, стоячи на реконструкторів, щоб перетворитися в цей мех. +block.tau-mech-pad.description = Покиньте свій поточний корабель і перетворитеся на мех підтримки, який може зцілювати дружні будівлі і юніти.\nВикористовуйте подвійне натискання, стоячи на реконструкторів, щоб перетворитися в цей мех. +block.delta-mech-pad.description = Залиште свій поточний корабель і перейдіть в великий, добре броньований бойовий корабель.\nВикористовуйте подвійне натискання, стоячи на реконструкторів, щоб перетворитися в цей мех. +block.omega-mech-pad.description = Залиште свій поточний корабель і перетворіть його в громіздкий і добре броньований мех, зроблений для фронтових нападів.\nВикористовуйте подвійне натискання, стоячи на реконструкторів, щоб перетворитися в цей мех. +block.spirit-factory.description = Виробляє легкі дрони, які видобувають руду (мідну і свинцеву) і ремонтує блоки. Один за замовчуванням з'являється з ядра. +block.phantom-factory.description = Виробляє вдосконалені одиниці, які значно ефективніше, ніж дрон-привид. +block.wraith-factory.description = Виробляє швидких і літаючих бойових одиниць. +block.ghoul-factory.description = Виробляє важких килимових бомбардувальників. +block.dagger-factory.description = Виробляє основні наземні бойові одиниці. +block.titan-factory.description = Виробляє просунуті захищені бойові одиниці. +block.fortress-factory.description = Виробляє важкі артилерійські бойові одиниці. +block.revenant-factory.description = Виробляє важкі бойові одиниці, котрі літають. +block.repair-point.description = Постійно лікує найближчий пошкоджений апарат в його зоні дії. +block.conduit.description = Основний транспортний блок. Працює як конвеєр, але з рідинами. Найкраще використовується з екстракторами, насосами або іншими трубопроводами. +block.pulse-conduit.description = Розширений блок перевезення рідин. Транспортує рідини швидше і зберігає більше, аніж стандартні. +block.phase-conduit.description = Покращений блок перевезення рідин. Використовує енергію для телепорту рідин до підключеного фазового каналу по декілька блоків. +block.liquid-router.description = Приймає рідини з одного напрямку і виведить їх до 3 інших напрямків однаково. Можна також зберігати певну кількість рідини. Корисно для розщеплення рідин від одного джерела на кілька. +block.liquid-tank.description = Зберігає велику кількість рідини. Використовуйте його для створення буферів, коли існує нестійкий попит на матеріали або як захист для охолодження життєво важливих блоків. +block.liquid-junction.description = Діє як міст для двох перехресних трубопроводів. Корисно в ситуаціях з двома різними трубами, що несуть різні рідини в різні місця. +block.bridge-conduit.description = Покращений блок перевезення рідин. Дозволяє транспортувати рідини понад 3 блоки будь-якої місцевості або будівлі. +block.mechanical-pump.description = Дешевий насос з повільною швидкістю, але без споживання енергії. +block.rotary-pump.description = Розширений насос, який подвоює швидкість, використовуючи енергію. +block.thermal-pump.description = Останній насос. +block.router.description = Приймає елементи з одного напрямку і рівномірно виводить їх до 3 інших напрямків. Корисно для розділення матеріалів від одного джерела на кілька. +block.distributor.description = Розширений маршрутизатор, який рівномірно розбиває елементи на 7 різних напрямків. +block.bridge-conveyor.description = Покращений блок транспортування предметів. Дозволяє транспортувати предмети понад 3 блоки над будь-якої місцевостю або будівлеє. +block.item-source.description = Безліченно виводить предмети. +block.liquid-source.description = Безліченно виводить рідини. +block.item-void.description = Знищує будь-які предмети, які входять, без використання енергії. +block.power-source.description = Нескінченність не межа. Безмежно виводить енергію. +block.power-void.description = Енергія просто йде в порожнечу. +liquid.water.description = Цю рідину можно підвести до бурів для прискорення швидкості видобутку або к турелям для прискорення стрілянини. +liquid.oil.description = Можна спалити, взірвати або використовувати для охолодження. +liquid.cryofluid.description = Рідина з температурою нижче ніж -273 градусів за Цельсієм. Може бути використана для прискорення стрільби турелей або для охолодження чогось. diff --git a/core/assets/bundles/bundle_zh_CN.properties b/core/assets/bundles/bundle_zh_CN.properties new file mode 100644 index 0000000000..bc965c8fb5 --- /dev/null +++ b/core/assets/bundles/bundle_zh_CN.properties @@ -0,0 +1,947 @@ +credits.text = 由[ROYAL]Anuken[]开发 - [SKY]anukendev@gmail.com[]\n\n[GRAY](In case you can't tell, this text is currently unfinished.\nTranslators, don't edit it yet!) +credits = 致谢 +contributors = 译者和贡献者 +discord = 加入 Mindustry 的 Discord! +link.discord.description = 官方 Mindustry Discord 聊天室 +link.github.description = 游戏源码 +link.dev-builds.description = 不稳定开发版 +link.trello.description = Trello board 上的官方计划表 +link.itch.io.description = PC版下载和网页版(itch.io) +link.google-play.description = 从谷歌商店获取安卓版 +link.wiki.description = 官方 Mindustry 维基 +linkfail = 打开链接失败!\nURL 已经复制到剪贴板。 +screenshot = 荧幕截图已放在 {0} +screenshot.invalid = 地图太大,可能没有足够的内存用于截图。 +gameover = 你的核心被摧毁了! +gameover.pvp = [accent] {0}[] 队获胜! +highscore = [accent]新纪录! +stat.wave = 战胜的波数:[accent]{0} +stat.enemiesDestroyed = 消灭的敌人:[accent]{0} +stat.built = 建造的建筑:[accent]{0} +stat.destroyed = 摧毁的建筑:[accent]{0} +stat.deconstructed = 拆除的建筑:[accent]{0} +stat.delivered = 发射的资源: +stat.rank = 最终排名:[accent]{0} +placeline = 你选择了一个方块。\n你能通过[accent]长按几秒钟[]并向一个方向拖动来[accent]直线放置方块[]。\n试试看吧。 +removearea = 你选择了拆除模式。\n你能通过[accent]长按几秒钟[]并拖动来[accent]删除矩形内的方块[]。\n试试看吧。 +launcheditems = [accent]发射的资源 +map.delete = 确定要删除 "[accent]{0}[]" 地图吗? +level.highscore = 最高分:[accent]{0} +level.select = 选择关卡 +level.mode = 游戏模式: +showagain = 下次不再显示 +coreattack = < 核心正在受到攻击!> +nearpoint = [[ [scarlet]立即离开敌人出生点[] ]\n将被全部清除 +outofbounds = [[ 超出边界 ]\n[]{0}秒后自毁 +database = 核心数据库 +savegame = 保存游戏 +loadgame = 载入游戏 +joingame = 加入游戏 +addplayers = 添加/删除玩家 +customgame = 自定义游戏 +newgame = 新游戏 +none = <无> +minimap = 小地图 +close = 关闭 +quit = 退出 +maps = 地图 +continue = 继续 +maps.none = [LIGHT_GRAY]没有找到地图! +about.button = 关于 +name = 名字: +noname = 先取一个[accent]玩家名[]。 +filename = 文件名: +unlocked = 新方块已解锁! +completed = [accent]己研究 +techtree = 科技树 +research.list = [LIGHT_GRAY]研究: +research = 研究 +researched = [LIGHT_GRAY]{0}己研究。 +players = {0} 玩家在线 +players.single = {0}玩家在线 +server.closing = [accent]正在关闭服务器…… +server.kicked.kick = 你被踢出服务器了! +server.kicked.serverClose = 服务器已关闭。 +server.kicked.clientOutdated = 客户端过旧,请更新你的游戏。 +server.kicked.serverOutdated = 服务器过旧,请联系房主升级服务器。 +server.kicked.banned = 你在这个服务器上被拉入黑名单了。 +server.kicked.recentKick = 你刚刚被踢出服务器。\n请稍后重新连接! +server.kicked.nameInUse = 你的名字与服务器中的一个人重复了。 +server.kicked.nameEmpty = 无效的名字! +server.kicked.idInUse = 你已在这个服务器上!不允许用两个账号连接。 +server.kicked.customClient = 这个服务器不支持定制版本。下载官方版本。 +server.kicked.gameover = 游戏结束! +host.info = [accent]创建局域网游戏[]按钮会在[scarlet]6567[]端口运行一个服务器。[]\n任何在同一个[LIGHT_GRAY]wifi或本地网络[]下的人都应该可以在服务器列表中看到你的服务器。\n\n如果你想让别人在任何地方都能通过IP地址连接,你需要设定[accent]端口转发[]。\n\n[LIGHT_GRAY]注意:如果某人无法连接到你的局域网游戏,请确保你在防火墙设置里允许了Mindustry访问本地网络。 +join.info = 此时,可以输入[accent]服务器的IP地址[]来连接,或寻找[accent]本地网络[]中的服务器来连接。\n局域网或广域网多人游戏都被支持。\n\n[LIGHT_GRAY]注意:没有全球服务器列表;如果你想通过IP地址连接某个服务器,你需要向房主询问IP地址。 +hostserver = 创建服务器 +hostserver.mobile = 创建\n服务器 +host = 创建 +hosting = [accent]正在打开服务器…… +hosts.refresh = 刷新 +hosts.discovering = 正在搜索局域网服务器 +server.refreshing = 正在刷新服务器 +hosts.none = [lightgray]未发现局域网游戏! +host.invalid = [scarlet]无法连接服务器。 +trace = 跟踪玩家 +trace.playername = 玩家名称:[accent]{0} +trace.ip = IP地址:[accent]{0} +trace.id = 唯一的ID:[accent]{0} +trace.mobile = Mobile Client: [accent]{0} +trace.modclient = 定制版客户端:[accent]{0} +invalidid = 无效的客户端ID!提交一个错误报告。 +server.bans = 黑名单 +server.bans.none = 没有找到被拉黑的玩家! +server.admins = 管理员 +server.admins.none = 没有找到管理员! +server.add = 添加服务器 +server.delete = 你确定要删除这个服务器吗? +server.hostname = 主机:{0} +server.edit = 编辑服务器 +server.outdated = [crimson]过旧的服务器![] +server.outdated.client = [crimson]过旧的客户端![] +server.version = [lightgray]版本:{0} {1} +server.custombuild = [yellow]定制版 +confirmban = 你确认你想拉黑这名玩家吗? +confirmkick = 你确定你想要踢走这名玩家吗? +confirmunban = 你确定你想要取消拉黑这名玩家吗? +confirmadmin = 你确定你想要使这名玩家成为一个管理员吗? +confirmunadmin = 你确定你想要删除这名玩家的管理员地位吗? +joingame.title = 加入游戏 +joingame.ip = 地址: +disconnect = 已断开 +disconnect.data = 读取世界数据失败! +connecting = [accent]连接中…… +connecting.data = [accent]加载中…… +server.port = 端口: +server.addressinuse = 地址已经在使用中! +server.invalidport = 无效的端口号! +server.error = [crimson]创建服务器错误:[accent]{0} +save.old = 这个存档属于旧版本游戏,不再被使用。\n\n[LIGHT_GRAY]存档向下兼容将在完整的 4.0 版本中被实现。 +save.new = 新存档 +save.overwrite = 你确定你要覆盖这个存档位吗? +overwrite = 覆盖 +save.none = 没有存档被找到! +saveload = [accent]正在保存... +savefail = 保存失败! +save.delete.confirm = 你确定你要删除这个存档吗? +save.delete = 删除 +save.export = 导出存档 +save.import.invalid = [accent]这个存档是无效的! +save.import.fail = [crimson]导入存档失败:[accent]{0} +save.export.fail = [crimson]导出存档失败:[accent]{0} +save.import = 导入存档 +save.newslot = 保存的名称: +save.rename = 重命名 +save.rename.text = 新名称: +selectslot = 选择一个存档。 +slot = [accent]存档位 {0} +save.corrupted = [accent]存档损坏或无效!\n如果你刚刚升级了游戏,那么这可能是因为存档格式改变了,而[scarlet]不是[]bug 。 +empty = <空> +on = 开 +off = 关 +save.autosave = 自动保存:{0} +save.map = 地图:{0} +save.wave = 波次 {0} +save.difficulty = 难度:{0} +save.date = 最后保存过:{0} +save.playtime = 游戏时间:{0} +warning = 警告! +confirm = 确认 +delete = 删除 +ok = 好的 +open = 打开 +customize = 定制 +cancel = 取消 +openlink = 打开链接 +copylink = 复制链接 +back = 返回 +quit.confirm = 你确定你想要退出? +changelog.title = 更新日志 +changelog.loading = 正在获取更新日志…… +changelog.error.android = [accent]注意更新日志有时在安卓 4.4 以下不工作。\n这是安卓系统内部的一个bug。 +changelog.error.ios = [accent]更新日志当前在iOS中不被支持。 +changelog.error = [scarlet]获取更新日志失败!\n请检查你的网络。 +changelog.current = [yellow][[当前版本] +changelog.latest = [accent][[最新版本] +loading = [accent]加载中…… +saving = [accent]保存中…… +wave = [accent]波次 {0} +wave.waiting = [LIGHT_GRAY]下一波将在{0}秒后到来 +wave.waveInProgress = [LIGHT_GRAY]Wave in progress +waiting = [LIGHT_GRAY]等待中…… +waiting.players = 等待玩家中…… +wave.enemies = [LIGHT_GRAY]剩余 {0} 个敌人 +wave.enemy = [LIGHT_GRAY]剩余 {0} 个敌人 +loadimage = 加载图片 +saveimage = 保存图片 +unknown = 未知 +custom = 自定义 +builtin = 内建的 +map.delete.confirm = 你确定你想要删除这张地图吗?这个操作无法取消! +map.random = [accent]随机地图 +map.nospawn = 这个地图没有核心!请在编辑器中添加一个[ROYAL]蓝色[]的核心。 +map.nospawn.pvp = 这个地图没有敌人的核心!请在编辑器中添加一个[ROYAL]红色[]的核心。 +map.nospawn.attack = 这个地图没有敌人的核心!请在编辑中向地图添加一个[SCARLET]红色[]的核心。 +map.invalid = 地图载入错误:地图文件可能已经损坏。 +editor.brush = 笔刷 +editor.openin = 在编辑器中打开 +editor.oregen = 矿石的生成 +editor.oregen.info = 矿石的生成: +editor.mapinfo = 地图信息 +editor.author = 作者: +editor.description = 描述: +editor.waves = 波数: +editor.rules = Rules: +editor.ingame = Edit In-Game +waves.title = 波数 +waves.remove = 移除 +waves.never = <永不> +waves.every = 每 +waves.waves = 波 +waves.perspawn = 每次生成 +waves.to = 至 +waves.boss = BOSS +waves.preview = 预览 +waves.edit = 编辑…… +waves.copy = 复制到剪贴板 +waves.load = 从剪贴板读取 +waves.invalid = 剪贴板中无效的波次信息。 +waves.copied = 波次信息已复制。 +editor.default = [LIGHT_GRAY]<默认> +edit = 编辑…… +editor.name = 名称: +editor.spawn = Spawn Unit +editor.removeunit = Remove Unit +editor.teams = 队伍 +editor.elevation = 高度 +editor.errorload = 读取文件时出现错误:\n[accent]{0} +editor.errorsave = 保存文件时出现错误:\n[accent]{0} +editor.errorimage = That's an image, not a map. Don't go around changing extensions expecting it to work.\n\nIf you want to import a legacy map, use the 'import legacy map' button in the editor. +editor.errorlegacy = This map is too old, and uses a legacy map format that is no longer supported. +editor.errorheader = This map file is either not valid or corrupt. +editor.errorname = 地图没有被定义的名称。 +editor.update = 更新 +editor.randomize = 随机化 +editor.apply = 应用 +editor.generate = 生成 +editor.resize = 调整大小 +editor.loadmap = 载入地图 +editor.savemap = 保存地图 +editor.saved = 已保存! +editor.save.noname = 你的地图没有名字!在“地图信息”菜单里设置一个。 +editor.save.overwrite = 你的地图覆盖了一个内建的地图!在“地图信息”菜单里重新设置一个不同的名称。 +editor.import.exists = [scarlet]无法导入:[]名为‘{0}’的内建地图已存在! +editor.import = 导入... +editor.importmap = 导入地图 +editor.importmap.description = 导入一个已经存在的地图 +editor.importfile = 导入文件 +editor.importfile.description = 导入一个外置的地图文件 +editor.importimage = 导入地形图像 +editor.importimage.description = 导入一个外置的地图图像文件 +editor.export = 导出... +editor.exportfile = 导出文件 +editor.exportfile.description = 导出一个地图文件 +editor.exportimage = 导出一个地形文件 +editor.exportimage.description = 导出一个地图图像文件 +editor.loadimage = 导入地形 +editor.saveimage = 导出地形 +editor.unsaved = [scarlet]你有未保存的更改![]\n你确定你想要要退出? +editor.resizemap = 调整地图大小 +editor.mapname = 地图名称: +editor.overwrite = [accent]警告!\n这将会覆盖一个已经存在的地图。 +editor.overwrite.confirm = [scarlet]警告![]存在同名地图。你确定你想要覆盖? +editor.selectmap = 选择一个地图加载: +filters.empty = [LIGHT_GRAY]没有过滤器(filters)!用下方的按钮添加一个。 +filter.distort = 扭曲程度 +filter.noise = 噪音(Noise) +filter.ore = 矿石数量 +filter.rivernoise = 河流噪音(River Noise) +filter.scatter = 分散程度 +filter.terrain = 地形 +filter.option.scale = 规模大小 +filter.option.chance = 机会(Chance) +filter.option.mag = 大小?(Magnitude) +filter.option.threshold = 门槛?(Threshold) +filter.option.circle-scale = 圈规模?(Circle Scale) +filter.option.octaves = 八度?(Octaves) +filter.option.falloff = 减少?(Falloff) +filter.option.block = 方块 +filter.option.floor = 地面 +filter.option.wall = 墙 +filter.option.ore = 矿石 +filter.option.floor2 = 第二地面?(Secondary Floor) +filter.option.threshold2 = 第二门槛?(Secondary Threshold) +filter.option.radius = Radius +filter.option.percentile = Percentile +width = 宽度: +height = 高度: +menu = 菜单 +play = 开始游戏 +load = 载入游戏 +save = 保存 +fps = FPS: {0} +tps = TPS: {0} +ping = 延迟: {0}ms +language.restart = 为了使语言设置生效请重启游戏。 +settings = 设置 +tutorial = 教程 +editor = 编辑器 +mapeditor = 地图编辑器 +donate = 捐赠 +abandon = 放弃 +abandon.text = 这个区域和它的所有资源会被敌人重置。 +locked = 已被锁定 +complete = [LIGHT_GRAY]完成: +zone.requirement = 在{1}中达到{0}波 +resume = 恢复区:\n[LIGHT_GRAY]{0} +bestwave = [LIGHT_GRAY]最好: {0} +launch = < 发射 > +launch.title = 发射成功 +launch.next = [LIGHT_GRAY]下一个发射机会在第 {0} 波 +launch.unable = [scarlet]发射失败。[]敌人未被全部消灭。 +launch.confirm = 您将发射核心中所有资源。\n此地图将被重置。 +uncover = 解锁 +configure = 设定发射资源数量 +configure.locked = [LIGHT_GRAY]到达第 {0} 波\n才设定发射资源。 +zone.unlocked = [LIGHT_GRAY]{0} 已解锁。 +zone.requirement.complete = 已达到第{0}波。\n达到解锁{1}的需求。 +zone.config.complete = 已达到第{0}波。\n允许携带发射的资源进入此地区。 +zone.resources = 地图中的资源: +add = 添加 +boss.health = BOSS 生命值 +connectfail = [crimson]服务器连接失败:[accent]{0} +error.unreachable = 服务器无法访问。 +error.invalidaddress = 地址无效。 +error.timedout = 连接超时!\n确保服务器设置了端口转发,并且地址正确! +error.mismatch = 不匹配。\n可能是客户端/服务器版本不匹配。\n请确保客户端和服务器都是最新的版本! +error.alreadyconnected = 已连接。 +error.mapnotfound = 找不到地图文件! +error.io = 网络 I/O 错误。 +error.any = 未知网络错误。 +zone.groundZero.name = 零号地区 +zone.desertWastes.name = 沙漠废物 +zone.craters.name = 陨石带 +zone.frozenForest.name = 冰冻森林 +zone.ruinousShores.name = 毁灭海岸 +zone.stainedMountains.name = 绵延群山 +zone.desolateRift.name = 荒芜裂谷 +zone.nuclearComplex.name = 核裂变 +zone.overgrowth.name = 增生区 +zone.tarFields.name = 石油田 +settings.language = 语言 +settings.reset = 恢复默认 +settings.rebind = 重新绑定 +settings.controls = 控制 +settings.game = 游戏 +settings.sound = 声音 +settings.graphics = 图像 +settings.cleardata = 清除游戏数据…… +settings.clear.confirm = 您确定要清除数据吗?\n这个操作无法撤销! +settings.clearall.confirm = [scarlet]警告![]\n这将清除所有数据,包括存档、地图、解锁和键绑定。\n按「是」后,游戏将删除所有数据并自动退出。 +settings.clearunlocks = 清除解锁 +settings.clearall = 清除所有 +paused = 暂停 +yes = 是 +no = 不 +info.title = [accent]详情 +error.title = [crimson]发生了一个错误 +error.crashtitle = 发生了一个错误 +blocks.input = 输入 +blocks.output = 输出 +blocks.booster = 加成物品/液体 +block.unknown = [LIGHT_GRAY]??? +blocks.powercapacity = 能量容量 +blocks.powershot = 能量/发射 +blocks.targetsair = 攻击空中单位 +blocks.targetsground = 攻击地面单位 +blocks.itemsmoved = 移动速度 +blocks.launchtime = 发射间隔时间 +blocks.shootrange = 范围 +blocks.size = 尺寸 +blocks.liquidcapacity = 液体容量 +blocks.powerrange = 能量范围 +blocks.poweruse = 能量使用 +blocks.powerdamage = 功率/损伤 +blocks.itemcapacity = 物品容量 +blocks.basepowergeneration = 基础能源输出 +blocks.productiontime = 生产时间 +blocks.repairtime = 方块完全修复时间 +blocks.speedincrease = 提速 +blocks.range = 范围 +blocks.drilltier = 可钻探矿物 +blocks.drillspeed = 基础钻探速度 +blocks.boosteffect = 加成影响 +blocks.maxunits = 最大单位数量 +blocks.health = 生命值 +blocks.buildtime = Build Time +blocks.inaccuracy = 误差 +blocks.shots = 发射数 +blocks.reload = 重新装弹 +blocks.ammo = 子弹 +bar.drillspeed = 挖掘速度:{0}/s +bar.efficiency = 效率:{0}% +bar.powerbalance = 能量:{0} +bar.poweramount = 能量:{0} +bar.poweroutput = 能量输出:{0} +bar.items = 物体:{0} +bar.liquid = 液体 +bar.heat = 热量 +bar.power = 电力 +bar.progress = 制造进度 +bar.spawned = 单位数量: {0}/{1} +bullet.damage = [stat]{0}[lightgray] 伤害 +bullet.splashdamage = [stat]{0}[lightgray] 范围伤害 ~[stat] {1}[lightgray] 格 +bullet.incendiary = [stat] 燃烧 +bullet.homing = [stat] 追踪 +bullet.shock = [stat] 击晕 +bullet.frag = [stat] 分裂 +bullet.knockback = [stat]{0}[lightgray] 击退 +bullet.freezing = [stat] 冰冻 +bullet.tarred = [stat] 减速 +bullet.multiplier = [stat]{0}[lightgray]x 子弹数量 +bullet.reload = [stat]{0}[lightgray]x 装弹 +unit.blocks = 方块 +unit.powersecond = 能量单位/秒 +unit.liquidsecond = 液体单位/秒 +unit.itemssecond = 物品/秒 +unit.liquidunits = 液体单位 +unit.powerunits = 能量单位 +unit.degrees = 度 +unit.seconds = 秒 +unit.persecond = /秒 +unit.timesspeed = x 速度 +unit.percent = % +unit.items = 物品 +category.general = 普通 +category.power = 能量 +category.liquids = 液体 +category.items = 物品 +category.crafting = 制造 +category.shooting = 发射 +category.optional = 可选的增强物品 +setting.landscape.name = 锁定横屏 +setting.shadows.name = 影子 +setting.linear.name = Linear Filtering +setting.animatedwater.name = 流动的水 +setting.animatedshields.name = 动态画面 +setting.antialias.name = 抗锯齿[LIGHT_GRAY] (需要重新启动)[] +setting.indicators.name = 队友指示器 +setting.autotarget.name = 自动发射 +setting.fpscap.name = FPS限制 +setting.fpscap.none = 无 +setting.fpscap.text = {0} FPS +setting.swapdiagonal.name = 总是自动铺设 +setting.difficulty.training = 训练 +setting.difficulty.easy = 简单 +setting.difficulty.normal = 普通 +setting.difficulty.hard = 困难 +setting.difficulty.insane = 疯狂 +setting.difficulty.name = 难度: +setting.screenshake.name = 屏幕抖动 +setting.effects.name = 显示效果 +setting.sensitivity.name = 控制器灵敏度 +setting.saveinterval.name = 自动保存间隔 +setting.seconds = {0} 秒 +setting.fullscreen.name = 全屏 +setting.borderlesswindow.name = Borderless Window[LIGHT_GRAY] (may require restart) +setting.fps.name = 显示 FPS +setting.vsync.name = 帧同步 +setting.lasers.name = 显示能量射线 +setting.pixelate.name = 像素画面 [LIGHT_GRAY](可能会降低性能) +setting.minimap.name = 显示小地图 +setting.musicvol.name = 音乐音量 +setting.mutemusic.name = 静音 +setting.sfxvol.name = 音效音量 +setting.mutesound.name = 静音 +setting.crashreport.name = 发送匿名崩溃报告 +setting.chatopacity.name = 聊天界面透明度 +setting.playerchat.name = 显示游戏内聊天界面 +keybind.title = 重新绑定按键 +category.general.name = 普通 +category.view.name = 查看 +category.multiplayer.name = 多人 +command.attack = 攻击 +command.retreat = 撤退 +command.patrol = 巡逻 +keybind.gridMode.name = 选择块 +keybind.gridModeShift.name = 选择类别 +keybind.press = 按一下键…… +keybind.press.axis = 按一下轴或键…… +keybind.screenshot.name = 地图截图 +keybind.move_x.name = 水平移动 +keybind.move_y.name = 垂直移动 +keybind.select.name = 选择 +keybind.diagonal_placement.name = 自动铺设 +keybind.pick.name = 选择方块 +keybind.break_block.name = 破坏方块 +keybind.deselect.name = 取消 +keybind.shoot.name = 射击 +keybind.zoom_hold.name = 保持缩放 +keybind.zoom.name = 缩放 +keybind.menu.name = 菜单 +keybind.pause.name = 暂停 +keybind.minimap.name = 小地图 +keybind.dash.name = 冲刺 +keybind.chat.name = 聊天 +keybind.player_list.name = 玩家列表 +keybind.console.name = 控制台 +keybind.rotate.name = 旋转 +keybind.toggle_menus.name = 切换菜单 +keybind.chat_history_prev.name = 前面的聊天记录 +keybind.chat_history_next.name = 后面的聊天记录 +keybind.chat_scroll.name = 聊天记录滚动 +keybind.drop_unit.name = 掉落单位 +keybind.zoom_minimap.name = 小地图缩放 +mode.help.title = 模式说明 +mode.survival.name = 生存 +mode.survival.description = 正常的游戏模式,有限的资源和自动波次。 +mode.sandbox.name = 沙盒 +mode.sandbox.description = 无限的资源,不会自动生成敌人。 +mode.pvp.name = PvP +mode.pvp.description = 和本地玩家对战。 +mode.attack.name = 攻击 +mode.attack.description = 没有波数,但是有摧毁敌人基地的任务。 +mode.custom = 自定义模式 +rules.infiniteresources = 无限资源 +rules.wavetimer = 波次计时器 +rules.waves = 波次 +rules.enemyCheat = 敌人无限资源 +rules.unitdrops = 敌人出生点 +rules.unitbuildspeedmultiplier = 单位生产速度倍数 +rules.unithealthmultiplier = 单位生命倍数 +rules.playerhealthmultiplier = 玩家生命倍数 +rules.playerdamagemultiplier = 玩家伤害倍数 +rules.unitdamagemultiplier = 单位伤害倍数 +rules.enemycorebuildradius = 敌对核心非建设区半径:[LIGHT_GRAY](格) +rules.respawntime = 重生时间:[LIGHT_GRAY](秒) +rules.wavespacing = 波次间隔时间:[LIGHT_GRAY](秒) +rules.buildcostmultiplier = 建设花费倍数 +rules.buildspeedmultiplier = 建设时间倍数 +rules.waitForWaveToEnd = 等待敌人时间 +rules.dropzoneradius = 敌人出生点毁灭大小:[LIGHT_GRAY] (格) +rules.respawns = 每波最大重生次数 +rules.limitedRespawns = 重生限制次数 +rules.title.waves = 波次 +rules.title.respawns = 重生 +rules.title.resourcesbuilding = 资源和建造 +rules.title.player = 玩家 +rules.title.enemy = 敌人 +rules.title.unit = 单位 +content.item.name = 物品 +content.liquid.name = 液体 +content.unit.name = 部队 +content.block.name = 块 +content.mech.name = 机甲 +item.copper.name = 铜 +item.copper.description = 一种有用的结构材料。在各种类型的方块中广泛使用。 +item.lead.name = 铅 +item.lead.description = 一种基本的起始材料。被广泛用于电子设备和液体运输方块。 +item.coal.name = 煤 +item.coal.description = 一种常见并容易获得的燃料。 +item.graphite.name = 石墨 +item.titanium.name = 钛 +item.titanium.description = 一种罕见的超轻金属,被广泛运用于液体运输、钻头和飞机。 +item.thorium.name = 钍 +item.thorium.description = 一种致密的放射性金属,用作结构支撑和核燃料。 +item.silicon.name = 硅 +item.silicon.description = 一种非常有用的半导体,被用于太阳能电池板和很多复杂的电子设备。 +item.plastanium.name = 塑钢 +item.plastanium.description = 一种轻质,可延展的材料,用于高级的飞机和碎片弹药。 +item.phase-fabric.name = 相织物 +item.phase-fabric.description = 一种接近0重量的物质,用于先进的电子技术和自我修复技术。 +item.surge-alloy.name = 巨浪合金 +item.surge-alloy.description = 一种具有独特电气性能的高级合金。 +item.spore-pod.name = 孢子荚 +item.spore-pod.description = 一种用于制造石油,炸药及燃料的生物质。 +item.sand.name = 沙 +item.sand.description = 一种常见的材料,广泛用于冶炼,包括制作合金和助熔剂。 +item.blast-compound.name = 爆炸混合物 +item.blast-compound.description = 一种用于炸弹和炸药的挥发性混合物。虽然它可以作为燃料燃烧,但不建议这样做。 +item.pyratite.name = 硫 +item.pyratite.description = 一种燃烧武器中使用的极易燃物质。 +item.metaglass.name = 钢化玻璃 +item.metaglass.description = 一种超级强硬的复合玻璃。通常用来传送和收藏液体 +item.scrap.name = 废料 +item.scrap.description = 一种废弃的建筑物及废弃单位的残骸,富含多种金属元素。 +liquid.water.name = 水 +liquid.slag.name = 岩浆 +liquid.oil.name = 石油 +liquid.cryofluid.name = 冷冻液 +mech.alpha-mech.name = 阿尔法 +mech.alpha-mech.weapon = 重型机枪 +mech.alpha-mech.ability = 无人机群 +mech.alpha-mech.description = 标准的机甲。具有不错的速度和伤害输出,可以制造多达 3 架无人机以提高进攻能力。 +mech.delta-mech.name = 德尔塔 +mech.delta-mech.weapon = 电弧发电机 +mech.delta-mech.ability = 放电 +mech.delta-mech.description = 一种快速,轻便的机甲,一击即退。对结构造成的伤害很小,但可以用弧形闪电武器很快杀死大量敌方单位。 +mech.tau-mech.name = 医疗机 +mech.tau-mech.weapon = 重构激光 +mech.tau-mech.ability = 修复 +mech.tau-mech.description = 后勤机甲。治疗友军。可以熄灭火焰并治疗一定范围内的友军。 +mech.omega-mech.name = 欧米茄 +mech.omega-mech.weapon = 导弹群 +mech.omega-mech.ability = 配置装甲 +mech.omega-mech.description = 一种装甲厚重的机甲,用于在前线攻击。它的护甲可以阻挡高达90%的伤害。 +mech.dart-ship.name = 飞镖 +mech.dart-ship.weapon = 机枪 +mech.dart-ship.description = 标准飞船。快速轻便,但攻击能力低,采矿速度快。 +mech.javelin-ship.name = 标枪 +mech.javelin-ship.description = 一艘一击即退的攻击船。虽然最初很慢,但它可以加速到很快的速度,并飞过敌人的前哨,利用其闪电能力和导弹造成大量伤害。 +mech.javelin-ship.weapon = 爆裂导弹 +mech.javelin-ship.ability = 放电助推器 +mech.trident-ship.name = 三叉戟 +mech.trident-ship.description = 一种重型轰炸机。有厚装甲。 +mech.trident-ship.weapon = 炸弹 +mech.glaive-ship.name = 阔剑 +mech.glaive-ship.description = 一种大型,装甲厚重的武装直升机。配备燃烧机枪。有优秀的加速能力和最快的速度。 +mech.glaive-ship.weapon = 火焰机枪 +item.explosiveness = [LIGHT_GRAY]爆炸性:{0} +item.flammability = [LIGHT_GRAY]易燃性:{0} +item.radioactivity = [LIGHT_GRAY]放射性:{0} +unit.health = [LIGHT_GRAY]生命值:{0} +unit.speed = [LIGHT_GRAY]速度:{0} +mech.weapon = [LIGHT_GRAY]武器:{0} +mech.health = [LIGHT_GRAY]生命值: {0} +mech.itemcapacity = [LIGHT_GRAY]物品容量:{0} +mech.minespeed = [LIGHT_GRAY]采矿速度:{0} +mech.minepower = [LIGHT_GRAY]采矿力量:{0} +mech.ability = [LIGHT_GRAY]能力:{0} +mech.buildspeed = [LIGHT_GRAY]建造速度:{0}% +liquid.heatcapacity = [LIGHT_GRAY]热容量:{0} +liquid.viscosity = [LIGHT_GRAY]粘度:{0} +liquid.temperature = [LIGHT_GRAY]温度:{0} +block.grass.name = 草地 +block.salt.name = 盐碱地 +block.saltrocks.name = Salt Rocks +block.pebbles.name = Pebbles +block.tendrils.name = Tendrils +block.sandrocks.name = 沙岩块 +block.spore-pine.name = 孢子树 +block.sporerocks.name = 孢子岩石 +block.rock.name = 岩石 +block.snowrock.name = 雪岩石 +block.shale.name = 页岩地 +block.shale-boulder.name = 页岩巨石 +block.moss.name = 苔藓地 +block.shrubs.name = 灌木丛 +block.spore-moss.name = 孢子苔藓地 +block.shalerocks.name = 页岩岩石 +block.scrap-wall.name = 废墙 +block.scrap-wall-large.name = 大型废墙 +block.scrap-wall-huge.name = 巨型废墙 +block.scrap-wall-gigantic.name = 超巨型废墙 +block.thruster.name = 助力器 +block.kiln.name = 熔炉 +block.kiln.description = 将铅和沙子熔炼成钢化玻璃,需要少量电力。 +block.graphite-press.name = 石墨压缩机 +block.multi-press.name = 大型石墨压缩机 +block.constructing = {0}\n[LIGHT_GRAY](建造中) +block.spawn.name = 敌人出生点 +block.core-shard.name = 小型核心 +block.core-foundation.name = 中型核心 +block.core-nucleus.name = 大型核心 +block.deepwater.name = 深水 +block.water.name = 水 +block.tainted-water.name = 污水 +block.darksand-tainted-water.name = 暗沙 污水 +block.tar.name = 石油 +block.stone.name = 石头 +block.sand.name = 沙子 +block.darksand.name = 黑沙 +block.ice.name = 冰 +block.snow.name = 雪 +block.craters.name = 陨石坑 +block.sand-water.name = 沙 水 +block.darksand-water.name = 暗沙 水 +block.char.name = 焦土 +block.holostone.name = 霍洛石头 +block.ice-snow.name = 冰雪地 +block.rocks.name = 岩石 +block.icerocks.name = 冰岩石 +block.snowrocks.name = 雪岩石 +block.dunerocks.name = 沙丘岩石 +block.pine.name = 松树 +block.white-tree-dead.name = 枯萎的白树 +block.white-tree.name = 白树 +block.spore-cluster.name = 孢子簇 +block.metal-floor.name = 金属地板 +block.metal-floor-2.name = 金属地板2 +block.metal-floor-3.name = 金属地板3 +block.metal-floor-5.name = 金属地板5 +block.metal-floor-damaged.name = 损坏的金属地板 +block.dark-panel-1.name = 暗面板1 +block.dark-panel-2.name = 暗面板2 +block.dark-panel-3.name = 暗面板3 +block.dark-panel-4.name = 暗面板4 +block.dark-panel-5.name = 暗面板5 +block.dark-panel-6.name = 暗面板6 +block.dark-metal.name = 暗金属 +block.ignarock.name = 伊格纳石头 +block.hotrock.name = 热石头 +block.magmarock.name = 岩浆石头 +block.cliffs.name = 悬崖 +block.copper-wall.name = 铜墙 +block.copper-wall-large.name = 大型铜墙 +block.titanium-wall.name = 钛墙 +block.titanium-wall-large.name = 大型钛墙 +block.phase-wall.name = 相织布墙 +block.phase-wall-large.name = 大型相织布墙 +block.thorium-wall.name = 钍墙 +block.thorium-wall-large.name = 大型钍墙 +block.door.name = 门 +block.door-large.name = 大门 +block.duo.name = 双管炮 +block.scorch.name = 火焰炮 +block.scatter.name = 分裂炮 +block.hail.name = 冰雹炮 +block.lancer.name = 蓝瑟炮 +block.conveyor.name = 传送带 +block.titanium-conveyor.name = 钛传送带 +block.junction.name = 连接点 +block.router.name = 路由器 +block.distributor.name = 分配器 +block.sorter.name = 分类器 +block.sorter.description = 对物品进行分类。如果物品与所选种类,则允许其通过。否则,物品将从左边和右边输出。 +block.overflow-gate.name = 溢流门 +block.overflow-gate.description = 分离器和路由器的组合,如果前面被挡住,则向从左和右输出。 +block.silicon-smelter.name = 硅冶炼厂 +block.phase-weaver.name = 相织布编织器 +block.pulverizer.name = 粉碎机 +block.cryofluidmixer.name = 冷冻液混合器 +block.melter.name = 熔炉 +block.incinerator.name = 焚化炉 +block.spore-press.name = 孢子压缩机 +block.separator.name = 分离机 +block.coal-centrifuge.name = 煤炭离心机 +block.power-node.name = 能量节点 +block.power-node-large.name = 大型能量节点 +block.surge-tower.name = 远程输电塔 +block.battery.name = 电池 +block.battery-large.name = 大型电池 +block.combustion-generator.name = 燃烧发电机 +block.turbine-generator.name = 涡轮发电机 +block.differential-generator.name = 差动发电机 +block.impact-reactor.name = 冲击反应堆 +block.mechanical-drill.name = 机械钻头 +block.pneumatic-drill.name = 气动钻头 +block.laser-drill.name = 激光钻头 +block.water-extractor.name = 抽水机 +block.cultivator.name = 耕种机 +block.dart-mech-pad.name = 飞镖 机甲平台 +block.delta-mech-pad.name = 德尔塔 机甲平台 +block.javelin-ship-pad.name = 标枪 机甲平台 +block.trident-ship-pad.name = 三叉戟 机甲平台 +block.glaive-ship-pad.name = 阔剑 机甲平台 +block.omega-mech-pad.name = 欧米茄 机甲平台 +block.tau-mech-pad.name = 医疗机 机甲平台 +block.conduit.name = 导管 +block.mechanical-pump.name = 机械泵 +block.item-source.name = 物品源 +block.item-void.name = 物品黑洞 +block.liquid-source.name = 液体源 +block.power-void.name = 能源黑洞 +block.power-source.name = 无限能源 +block.unloader.name = 装卸器 +block.vault.name = 仓库 +block.wave.name = 波浪 +block.swarmer.name = 蜂群 +block.salvo.name = 齐射炮 +block.ripple.name = 浪涌 +block.phase-conveyor.name = 相织布传送带 +block.bridge-conveyor.name = 传送带桥 +block.plastanium-compressor.name = 塑钢压缩机 +block.pyratite-mixer.name = 硫混合器 +block.blast-mixer.name = 爆炸混合器 +block.solar-panel.name = 太阳能电池 +block.solar-panel-large.name = 大型太阳能电池 +block.oil-extractor.name = 石油钻井 +block.spirit-factory.name = 轻型无人机工厂 +block.phantom-factory.name = 鬼怪无人机工厂 +block.wraith-factory.name = 幻影战机工厂 +block.ghoul-factory.name = 食尸鬼轰炸机工厂 +block.dagger-factory.name = 尖刀机甲工厂 +block.crawler-factory.name = 爬行者机甲工厂 +block.titan-factory.name = 泰坦机甲工厂 +block.fortress-factory.name = 堡垒机甲工厂 +block.revenant-factory.name = 亡魂战机工厂 +block.repair-point.name = 维修点 +block.pulse-conduit.name = 脉冲导管 +block.phase-conduit.name = 相织布导管 +block.liquid-router.name = 液体路由器 +block.liquid-tank.name = 储液罐 +block.liquid-junction.name = 液体连接点 +block.bridge-conduit.name = 导管桥 +block.rotary-pump.name = 回旋泵 +block.thorium-reactor.name = 钍反应堆 +block.mass-driver.name = 质量驱动器 +block.blast-drill.name = 爆破钻头 +block.thermal-pump.name = 热能泵 +block.thermal-generator.name = 热能发电机 +block.alloy-smelter.name = 合金冶炼厂 +block.mender.name = Mender +block.mend-projector.name = 修理投影器 +block.surge-wall.name = 波动墙 +block.surge-wall-large.name = 大型波动墙 +block.cyclone.name = 气旋炮 +block.fuse.name = 融合炮 +block.shock-mine.name = 休克地雷 +block.overdrive-projector.name = 超速投影器 +block.force-projector.name = 力墙投影器 +block.arc.name = 电弧 +block.rtg-generator.name = RTG 发电机 +block.spectre.name = 幽灵 +block.meltdown.name = 熔毁 +block.container.name = 容器 +block.launch-pad.name = 发射台 +block.launch-pad.description = 不通过核心发射物体。尚未完成。 +block.launch-pad-large.name = 大型发射台 +team.blue.name = 蓝 +team.red.name = 红 +team.orange.name = 橙 +team.none.name = 灰 +team.green.name = 绿 +team.purple.name = 紫 +unit.spirit.name = 轻型无人机 +unit.spirit.description = 初始无人机。默认情况下在内核中生成。自动开采矿石,收集物品和修理块。 +unit.phantom.name = 鬼怪无人机 +unit.phantom.description = 一种先进的无人机单位。自动开采矿石,收集物品和修理块。比初始无人机有效得多。 +unit.dagger.name = 尖刀 +unit.dagger.description = 基础的地面单位,在蜂群中很有用。 +unit.crawler.name = 爬行者 +unit.titan.name = 泰坦 +unit.titan.description = 高级的有武装地面单位,使用电石作为弹药.攻击地面单位和空中单位. +unit.ghoul.name = 食尸鬼轰炸机 +unit.ghoul.description = 重型地毯轰炸机。用爆炸化合物或黄铁矿作为弹药。 +unit.wraith.name = 幻影战机 +unit.wraith.description = 一种快速,打了就跑的截击机。 +unit.fortress.name = 堡垒 +unit.fortress.description = 一种重炮地面部队。 +unit.revenant.name = 亡魂 +unit.eruptor.name = 暴君 +unit.chaos-array.name = 混沌者 +unit.eradicator.name = 根除者 +unit.lich.name = 尸鬼 +unit.reaper.name = 死神 +tutorial.begin = 你的任务是消灭[LIGHT_GRAY] 敌人 [].\n\n首先开始[accent] 采集铜矿 []。点击核心附近的铜矿开始。 +tutorial.drill = 手动采矿效率低.\n[accent] 钻头 []可以自动采矿.\n放一个在铜矿上吧. +tutorial.conveyor = [accent]传送带[] 可以把物资传送到核心.\n请造一个传送线,从钻头到核心. +tutorial.morecopper = 需要更多的铜.\n\n手动采矿或者放更多的钻头吧. +tutorial.turret = 必须建造防御建筑来击退[LIGHT_GRAY] 敌人[].\n请在你核心附近造一个双人炮塔. +tutorial.drillturret = 双人炮塔需要[accent] 铜质弹药 []来射击.\n可以放一个钻头在炮塔附近供应铜. +tutorial.waves = [LIGHT_GRAY]敌人[] 来了.\n\n保护基地,防御2波攻击. 造更多的炮塔. +tutorial.lead = 有更多的矿可用. 探索和采集[accent] 铅[].\n\n按住你的采矿单位拖放到核心来传送资源. +tutorial.smelter = 铜和铅是弱金属.\n超级[accent] 致密合金[] 可以从冶炼厂生产.\n\n造一个吧. +tutorial.densealloy = 冶炼厂将生产合金.\n生产一些.\n有必要可以改进一下生产. +tutorial.siliconsmelter = 基地现在将制作一个[accent] 无人机[] 来采矿和维修方块.\n\n其他单位的工程可以用[accent] 硅 []来建造.\n造一个硅冶炼厂. +tutorial.silicondrill = 硅需要[accent] 煤[] 和[accent] 沙[].\n开始制作钻头吧. +tutorial.generator = 这项技术需要能源.\n造一个[accent] 燃烧发电机[] 来发电. +tutorial.generatordrill = 燃烧发电机需要燃料.\n用钻头采集煤来供给燃料. +tutorial.node = 能源需要传输.\n造一个[accent] 能量节点[] 靠近火力发电机来传输它的能源. +tutorial.nodelink = 使耗能方块紧靠发电机或者用能源节点连接来传输电力.\n\n点击能源节点并选择发电机和硅冶炼厂来链接能源. +tutorial.silicon = 正在生产硅. 多生产点.\n\n建议优化一下生产系统. +tutorial.daggerfactory = 建造一个[accent] 尖刀机甲工厂.[]\n\n它可以用来生产机甲 +tutorial.router = 工厂需要资源来运作.\n造一个路由器来分发传送资源. +tutorial.dagger = 链接能源节点到工厂.\n一旦需求满足, 将会制作一个机甲.\n\n根据需要制作更多的钻头,发电机,传送带. +tutorial.battle = [LIGHT_GRAY] 敌人[] 的核心已经暴露。\n用你的尖刀机甲摧毁它。 +block.copper-wall.description = 廉价的防守区块。\n用于保护前几波中的核心和炮塔。 +block.copper-wall-large.description = 廉价的防御块。\n用于保护前几个波浪中的核心和炮塔。\n跨越多个块。 +block.thorium-wall.description = 强大的防守区块。\n很好的防御敌人。 +block.thorium-wall-large.description = 强大的防守区块。\n很好地防御敌人。\n跨越多个块。 +block.phase-wall.description = 没有钍墙那样坚固,但是它可以使不太强的子弹发生偏转。 +block.phase-wall-large.description = 没有钍墙那样坚固,但是它可以使不太强的子弹发生偏转。\n跨越多个块。 +block.surge-wall.description = 强大的防守区块。\n有很小的机会向攻击者发射闪电。 +block.surge-wall-large.description = 强大的防御区块。\n有很小的机会向攻击者发射闪电。\n跨越多个区块。 +block.door.description = 一扇小门,可以通过点击打开和关闭。\n如果打开,敌人可以射击并穿过。 +block.door-large.description = 一扇大门,可以通过点击打开和关闭。\n如果打开,敌人可以射击并穿过。\n扫过多个瓷砖。 +block.mend-projector.description = 定期修复附近的建筑物。 +block.overdrive-projector.description = 提高附近建筑物的速度,如钻机和传送带。 +block.force-projector.description = 自身周围创建一个六边形力场,保护建筑物和内部单位免受子弹的伤害。 +block.shock-mine.description = 伤害踩到它的敌人。敌人几乎看不到它。 +block.duo.description = 小而便宜的炮塔。 +block.scatter.description = 中型防空炮塔,向空中单位发射铅或废料。 +block.arc.description = 小型炮塔,发射电弧。 +block.hail.description = 小型炮兵炮台。 +block.lancer.description = 中型炮塔,发射带电的电子束。 +block.wave.description = 中型快速炮塔,射出液体泡泡。 +block.salvo.description = 中型炮塔,齐射射击。 +block.swarmer.description = 发射爆炸导弹的中型炮塔。 +block.ripple.description = 大型炮兵炮塔,可同时向多个目标开火。 +block.cyclone.description = 大型快速炮塔。 +block.fuse.description = 发射强大的短程光束的大型炮塔。 +block.spectre.description = 大型炮塔,一次射出两颗强大的子弹。 +block.meltdown.description = 发射强大的远程光束的大型炮塔。 +block.conveyor.description = 初级传送带。将物品向前移动并自动将它们放入炮塔或工厂中。可旋转方向。 +block.titanium-conveyor.description = 高级传送带。能比初级传送带更快地移动物品。 +block.phase-conveyor.description = 高级传送带。使用电力将物品传送到距离几个块的相位传送带上。 +block.junction.description = 为两条交叉传送带的桥梁。适用于两种不同传送带将不同材料运送到不同位置的情况。 +block.mass-driver.description = 终极传送带。收集几件物品,然后将它们射向长距离外的另一个批量传送带。 +block.silicon-smelter.description = 用高纯度的焦炭来加工沙子以生产硅。 +block.plastanium-compressor.description = 用油和钛生产塑钢。 +block.phase-weaver.description = 用放射性钍和大量沙子生产相织物。 +block.alloy-smelter.description = 用钛,铅,硅和铜生产浪涌合金。 +block.pulverizer.description = 将石头压成沙子。当缺少天然沙子时很有用。 +block.pyratite-mixer.description = 用煤,铅和沙子混合成高度易燃的硫。 +block.blast-mixer.description = 用油将硫转化为不易燃但更具爆炸性的爆炸化合物。 +block.cryofluidmixer.description = 水和钛结合到低温流体中,冷却效率更高。 +block.melter.description = 石头加热到很高的温度以获得熔岩。 +block.incinerator.description = 用于除掉任何多余的物品或液体。 +block.spore-press.description = 压缩孢子荚得到石油。 +block.separator.description = 将石头暴露在水压下,以获得石头中含有的各种矿物质。 +block.power-node.description = 连接节点传输电源。最多可连接四个电源,接收器或节点。节点将从任何相邻块接收电力或向其供电。 +block.power-node-large.description = 传输径大于电源节点,最多可连接六个电源,接收器或节点。 +block.battery.description = 储存电力,当储存有能量时,可在电力短缺时提供电力。 +block.battery-large.description = 比普通电池容量更大。 +block.combustion-generator.description = 通过燃烧油或易燃材料产生电力。 +block.turbine-generator.description = 比燃烧发电机更有效,但需要额外的水。 +block.thermal-generator.description = 从熔岩中产生大量的能量。 +block.solar-panel.description = 标准太阳能面板,提供少量电力。 +block.solar-panel-large.description = 比标准太阳能电池板提供更好的电源,但构建起来要贵得多。 +block.thorium-reactor.description = 高放射性钍产生大量电力。需要持续冷却。如果供应的冷却剂量不足,会剧烈爆炸。 +block.rtg-generator.description = 一种放射性同位素热电发电机,它不需要冷却,但功率低于钍反应堆。 +block.unloader.description = 物品从容器,仓库或核心卸载到传送带上或直接卸载到相邻的块中。可以通过点击卸载器来更改要卸载的项目类型。 +block.container.description = 存储少量物品。当存在非恒定的材料需求时,使用它来创建缓冲区。 [LIGHT_GRAY]卸载器[]可用于从容器中获取物品。 +block.vault.description = 存储大量物品。当存在非恒定的材料需求时,使用它来创建缓冲区。 [LIGHT_GRAY]卸载器[]可用于从仓库中获取物品。 +block.mechanical-drill.description = 便宜的钻头。放置在适当的块上时,无限期地以缓慢的速度输出物品。 +block.pneumatic-drill.description = 一种改进的钻头,它更快,能够利用气压处理更硬的材料。 +block.laser-drill.description = 通过激光技术更快地钻孔,但需要电源。此外,这种钻头可以回收放射性钍。 +block.blast-drill.description = 终极钻头,需要大量电力。 +block.water-extractor.description = 从地下提取水。当附近没有湖泊时使用它。 +block.cultivator.description = 用水培育土壤以获得生物物质。 +block.oil-extractor.description = 使用大量的电力从沙子中提取石油。当附近没有直接的石油来源时使用它。 +block.trident-ship-pad.description = 离开你当前的装置,换成一个装甲合理的重型轰炸机。\n站在上面时双击切换。 +block.javelin-ship-pad.description = 离开你当前的装置,换上一个强大而快速的截击机,用闪电武器。\n站在上面时双击切换。 +block.glaive-ship-pad.description = 离开现有的装置,换成装甲良好的大型武装直升机。\n站在上面时双击切换。 +block.tau-mech-pad.description = 离开你当前的装置并换成一个可以治愈友方建筑物和单位的支撑机械。\n站在上面时双击切换。 +block.delta-mech-pad.description = 离开你当前的装置并换成一个快速,轻装甲的机械装置,用于快速攻击。\n站在上面时双击切换。 +block.omega-mech-pad.description = 离开你当前的装置并换成一个笨重且装甲良好的机甲,用于前线攻击。\n站在上面时双击切换。 +block.spirit-factory.description = 生产轻型无人机,用于开采矿石和修复块。 +block.phantom-factory.description = 生产高级无人机单元,它比轻型无人机更有效。 +block.wraith-factory.description = 生产快速截击机。 +block.ghoul-factory.description = 生产重型地毯轰炸机。 +block.dagger-factory.description = 生产基本地面单位。 +block.titan-factory.description = 生产先进的装甲地面单位。 +block.fortress-factory.description = 生产重型火炮地面部队。 +block.revenant-factory.description = 生产重型激光地面单元。 +block.repair-point.description = 连续治疗附近最近的受损单位。 +block.conduit.description = 基本液体传输块。像输送机一样工作,但用于液体。最适用于提取器,泵或其他导管。 +block.pulse-conduit.description = 高级液体传输块。比标准导管更快地输送液体并储存更多液体。 +block.phase-conduit.description = 高级液体传输块。使用电力将液体传送到多个块上的连接相管道。 +block.liquid-router.description = 接受来自一个方向的液体并将它们平均输出到最多3个其他方向。也可以储存一定量的液体。用于将液体从一个源分成多个目标。 +block.liquid-tank.description = 存储大量液体。当存在对材料的非恒定需求或作为冷却重要块的安全措施时,将其用于创建缓冲区。 +block.liquid-junction.description = 作为两个交叉管道的桥梁。适用于两种不同导管将不同液体输送到不同位置的情况。 +block.bridge-conduit.description = 高级液体传输块。允许在任何地形或建筑物的最多3个块上运输液体。 +block.mechanical-pump.description = 一种输出速度慢但没有功耗的廉价泵。 +block.rotary-pump.description = 一种先进的泵,通过使用动力使速度加倍。 +block.thermal-pump.description = 终级水泵。速度是机械泵的三倍。是唯一能够回收熔岩的泵。 +block.router.description = 从一个方向接受物品,并将它们平均输出到最多3个其他方向。用于将材料从一个源分割为多个目标。 +block.distributor.description = 一个高级路由器,可以将物品分成最多7个方向。 +block.bridge-conveyor.description = 高级项目传输块。允许在跨越任何地形或建筑物上运输物品,最多跨越3个块。 +block.item-source.description = 无限输出物品。仅限沙箱。 +block.liquid-source.description = 无限输出液体。仅限沙箱。 +block.item-void.description = 在不使用电源的情况下销毁任何进入它的物品。仅限沙箱。 +block.power-source.description = 无限输出功率。仅限沙箱。 +block.power-void.description = 消耗输入的所有功率。仅限沙箱。 +liquid.water.description = 通常用于冷却和废物处理。 +liquid.oil.description = 可以燃烧,爆炸或用作冷却液。 +liquid.cryofluid.description = 用于降温的最有效液体。 diff --git a/core/assets/bundles/bundle_zh_TW.properties b/core/assets/bundles/bundle_zh_TW.properties new file mode 100644 index 0000000000..cb93cdf006 --- /dev/null +++ b/core/assets/bundles/bundle_zh_TW.properties @@ -0,0 +1,947 @@ +credits.text = 由[ROYAL]Anuken[]製作 - [SKY]anukendev@gmail.com[] +credits = 致謝名單 +contributors = 翻譯員和貢獻者 +discord = 加入 Mindustry 的 Discord 聊天室! +link.discord.description = 官方 Mindustry Discord 聊天室 +link.github.description = 遊戲原始碼 +link.dev-builds.description = 開發中版本 +link.trello.description = 官方 Trello 功能規劃看板 +link.itch.io.description = itch.io 電腦版下載與網頁版 +link.google-play.description = Google Play 商店頁面 +link.wiki.description = 官方 Mindustry 維基 +linkfail = 無法打開連結!\n我們已將該網址複製到您的剪貼簿。 +screenshot = 截圖保存到{0} +screenshot.invalid = Map too large, potentially not enough memory for screenshot. +gameover = 遊戲結束 +gameover.pvp = [accent]{0}[]隊獲勝! +highscore = [accent]新的高分紀錄! +stat.wave = 打敗的波次:[accent]{0} +stat.enemiesDestroyed = 摧毀的敵人:[accent]{0} +stat.built = 建設的建築:[accent]{0} +stat.destroyed = 摧毀的建築:[accent]{0} +stat.deconstructed = 移除的建築:[accent]{0} +stat.delivered = 發射的資源: +stat.rank = 最終排名:[accent]{0} +placeline = 你選擇了一個方塊。\n[accent]按住你的手指幾秒鐘[]並拖動以[accent]直線放置方塊[]。\n試試吧。 +removearea = 你選擇了移除模式。\n[accent]按住你的手指幾秒鐘[]並拖動以[accent]移除矩形中的方塊[]。\n試試吧。 +launcheditems = [accent]發射了的物品 +map.delete = 確認要刪除「[accent]{0}[]」地圖嗎? +level.highscore = 最高分:[accent]{0} +level.select = 選擇關卡 +level.mode = 遊戲模式: +showagain = 下次不再顯示 +coreattack = 〈核心正在受到攻擊!〉 +nearpoint = 【[scarlet]立即離開下降點[]】\n湮滅即將來臨 +outofbounds = 【超出界限】\n[]於{0}秒後自我毀滅 +database = 核心數據庫 +savegame = 儲存遊戲 +loadgame = 載入遊戲 +joingame = 多人連線 +addplayers = 增加/移除玩家 +customgame = 自訂遊戲 +newgame = 新遊戲 +none = 〈沒有〉 +minimap = Minimap +close = 關閉 +quit = 退出 +maps = 地圖 +continue = 繼續 +maps.none = [LIGHT_GRAY]找不到地圖! +about.button = 關於 +name = 名稱: +noname = 先選擇一個[accent]玩家名稱[]。 +filename = 檔案名稱︰ +unlocked = 新方塊已解鎖! +completed = [accent]完成 +techtree = 科技樹 +research.list = [LIGHT_GRAY]研究︰ +research = 研究 +researched = [LIGHT_GRAY]{0}研究完成。 +players = {0}個線上玩家 +players.single = {0}個線上玩家 +server.closing = [accent]正在關閉伺服器…… +server.kicked.kick = 您已被踢出伺服器! +server.kicked.serverClose = 伺服器已關閉。 +server.kicked.clientOutdated = 客戶端版本過舊!請更新遊戲! +server.kicked.serverOutdated = 伺服器版本過舊!請聯絡伺服主更新伺服器! +server.kicked.banned = 您已經從這個伺服器被封禁。 +server.kicked.recentKick = 您已經從伺服器被踢除。\n請稍後再進行連線。 +server.kicked.nameInUse = 伺服器中已經\n有人有相同的名稱了。 +server.kicked.nameEmpty = 你的名稱必須至少包含一個字母或數字。 +server.kicked.idInUse = 你已經在伺服器中!不允許用兩個賬號。 +server.kicked.customClient = 這個伺服器不支持自訂客戶端,請下載官方版本。 +server.kicked.gameover = 遊戲結束! +host.info = 目前伺服器監聽於連接埠[scarlet]6567[]。\n所有跟您在同一個[LIGHT_GRAY]網路或區域網路[]環境的玩家應該能在他們的伺服器清單中找到您的伺服器。\n\n如果您希望網際網路上的玩家透過IP 位址連線到您的伺服器,您必須設定[accent]連接埠轉發[]。\n\n[LIGHT_GRAY]注意:如果區域網路內有玩家無法連線至您的伺服器,請務必確認您已於防火牆設定中開放Mindustry存取您的區域網路。 +join.info = 您可以在此輸入欲連線的[accent]伺服器的IP位址[],或尋找[accent]區域網路[]內的伺服器。目前支援區域網路與網際網路連線。\n\n[LIGHT_GRAY]注意:這裡沒有網際網路伺服器清單,如果您想透過IP位址連線到某人的伺服器,您必須向他們詢問IP位址。 +hostserver = 建立伺服器 +hostserver.mobile = 建立\n伺服器 +host = 建立 +hosting = [accent]伺服器啟動中…… +hosts.refresh = 刷新 +hosts.discovering = 搜尋區域網路遊戲 +server.refreshing = 刷新伺服器 +hosts.none = [lightgray]找不到區域網路伺服器! +host.invalid = [scarlet]無法連線至伺服器。 +trace = 跟隨玩家 +trace.playername = 玩家名稱:[accent]{0} +trace.ip = IP:[accent]{0} +trace.id = ID:[accent]{0} +trace.mobile = Mobile Client: [accent]{0} +trace.modclient = 自訂客戶端:[accent]{0} +invalidid = 無效的客戶端 ID!請提交錯誤報告。 +server.bans = 封禁 +server.bans.none = 沒有玩家被封禁! +server.admins = 管理員 +server.admins.none = 找不到管理員! +server.add = 新增伺服器 +server.delete = 您確定要刪除這個伺服器嗎? +server.hostname = 主機:{0} +server.edit = 編輯伺服器 +server.outdated = [crimson]伺服器版本過舊![] +server.outdated.client = [crimson]客戶端版本過舊![] +server.version = [lightgray]版本:{0} +server.custombuild = [yellow]自訂組建 +confirmban = 您確定要封禁該玩家嗎? +confirmkick = 您確定要踢出該玩家嗎? +confirmunban = 您確定要解除封禁該玩家嗎? +confirmadmin = 您確定要提升這個玩家為管理員嗎? +confirmunadmin = 您確定要解除這個玩家的管理員嗎? +joingame.title = 加入遊戲 +joingame.ip = IP位址: +disconnect = 已中斷連線。 +disconnect.data = 無法載入地圖資料! +connecting = [accent]連線中…… +connecting.data = [accent]正在載入地圖資料…… +server.port = 連接埠: +server.addressinuse = 該位址已使用中! +server.invalidport = 無效的連接埠! +server.error = [crimson]建立伺服器時發生錯誤:[accent]{0} +save.old = 這個存檔屬於舊版本,無法使用了。\n\n[LIGHT_GRAY]舊存檔兼容將在正式4.0版本中實現。 +save.new = 新存檔 +save.overwrite = 您確定要覆蓋存檔嗎? +overwrite = 覆蓋 +save.none = 找不到存檔! +saveload = [accent]存檔中…… +savefail = 無法存檔! +save.delete.confirm = 您確定要刪除這個存檔嗎? +save.delete = 刪除 +save.export = 匯出存檔 +save.import.invalid = [accent]這是個無效的存檔! +save.import.fail = [crimson]無法匯入存檔:[accent]{0} +save.export.fail = [crimson]無法匯出存檔:[accent]{0} +save.import = 匯入存檔 +save.newslot = 存檔名稱: +save.rename = 重新命名 +save.rename.text = 新名稱: +selectslot = 選取一個存檔。 +slot = [accent]存檔{0} +save.corrupted = [accent]此存檔無效或已損毀!\n如果你剛剛升級了遊戲,那麼這可能是因為存檔格式改變了而[scarlet]不是[]錯誤。 +empty = 〈空白〉 +on = 開啟 +off = 關閉 +save.autosave = 自動存檔:{0} +save.map = 地圖:{0} +save.wave = 波次:{0} +save.difficulty = 難度:{0} +save.date = 最後存檔時間:{0} +save.playtime = 遊玩時間:{0} +warning = 警告。 +confirm = 確認 +delete = 刪除 +ok = 確定 +open = 開啟 +customize = Customize +cancel = 取消 +openlink = 開啟連結 +copylink = 複製連結 +back = 返回 +quit.confirm = 您確定要退出嗎? +changelog.title = 更新日誌 +changelog.loading = 正在取得更新日誌…… +changelog.error.android = [accent]請注意,更新日誌有時無法在Android 4.4或更低版本使用!這是因為 Android 的內部錯誤導致。 +changelog.error.ios = [accent]目前無法在iOS系統中使用更新日誌。 +changelog.error = [scarlet]無法取得更新日誌!請檢查您的網路連線! +changelog.current = [yellow][[Current version] +changelog.latest = [accent][[Latest version] +loading = [accent]載入中…… +saving = [accent]儲存中…… +wave = [accent]第{0}波 +wave.waiting = 將於{0}秒後抵達 +wave.waveInProgress = [LIGHT_GRAY]Wave in progress +waiting = 等待中…… +waiting.players = 等待玩家中…… +wave.enemies = [LIGHT_GRAY]剩下{0}敵人 +wave.enemy = [LIGHT_GRAY]剩下{0}敵人 +loadimage = 載入圖像 +saveimage = 儲存圖像 +unknown = 未知 +custom = 自訂 +builtin = 内建 +map.delete.confirm = 確認要刪除地圖嗎?此操作無法撤回! +map.random = [accent]隨機地圖 +map.nospawn = 這個地圖沒有核心!請在編輯器中添加一個[ROYAL]藍色[]的核心。 +map.nospawn.pvp = 這個地圖沒有核心讓敵人重生!請在編輯器中添加一個[SCARLET]紅色[]的核心。 +map.nospawn.attack = This map does not have any enemy cores for player to attack! Add[SCARLET] red[] cores to this map in the editor. +map.invalid = 地圖載入錯誤:地圖可能已經損壞。 +editor.brush = 粉刷 +editor.openin = 在編輯器中開啟 +editor.oregen = 礦石產生 +editor.oregen.info = 礦石產生: +editor.mapinfo = 地圖資訊 +editor.author = 作者: +editor.description = 描述: +editor.waves = 波次: +editor.rules = Rules: +editor.ingame = Edit In-Game +waves.title = 波次 +waves.remove = 移除 +waves.never = 〈從來沒有〉 +waves.every = 一切 +waves.waves = 波次 +waves.perspawn = 每個重生 +waves.to = 至 +waves.boss = 頭目 +waves.preview = 預覽 +waves.edit = 編輯…… +waves.copy = 複製到剪貼板 +waves.load = 從剪貼板加載 +waves.invalid = 剪貼板中的波次無效。 +waves.copied = 波浪已被複製。 +editor.default = [LIGHT_GRAY]〈默認〉 +edit = 編輯…… +editor.name = 名稱: +editor.spawn = Spawn Unit +editor.removeunit = Remove Unit +editor.teams = 隊伍 +editor.elevation = 高度 +editor.errorload = 加載文件時出錯:\n[accent]{0} +editor.errorsave = 保存文件時出錯:\n[accent]{0} +editor.errorimage = That's an image, not a map. Don't go around changing extensions expecting it to work.\n\nIf you want to import a legacy map, use the 'import legacy map' button in the editor. +editor.errorlegacy = This map is too old, and uses a legacy map format that is no longer supported. +editor.errorheader = This map file is either not valid or corrupt. +editor.errorname = 地圖沒有定義名稱。 +editor.update = 更新 +editor.randomize = 隨機化 +editor.apply = 使用 +editor.generate = 產生 +editor.resize = 調整大小 +editor.loadmap = 載入地圖 +editor.savemap = 儲存地圖 +editor.saved = 已儲存! +editor.save.noname = 您的地圖沒有名稱!在「地圖資訊」畫面設置一個名稱。 +editor.save.overwrite = 您的地圖覆寫一個內建的地圖!在「地圖信息」畫面設置一個不同的名稱。 +editor.import.exists = [scarlet]匯入失敗:[]一個叫「{0}」的內建地圖已存在! +editor.import = 匯入…… +editor.importmap = 匯入地圖 +editor.importmap.description = 匯入一個已存在的地圖 +editor.importfile = 匯入檔案 +editor.importfile.description = 匯入一個外部的地圖檔案 +editor.importimage = 匯入地形圖像檔 +editor.importimage.description = 匯入一個外部的地形圖像檔 +editor.export = 匯出…… +editor.exportfile = 匯出檔案 +editor.exportfile.description = 匯出一個地圖檔案 +editor.exportimage = 匯出地形圖像檔 +editor.exportimage.description = 匯出一個地形圖像檔 +editor.loadimage = 載入圖像 +editor.saveimage = 儲存圖像 +editor.unsaved = [scarlet]尚未儲存變更![]\n您確定要退出嗎? +editor.resizemap = 調整地圖大小 +editor.mapname = 地圖名稱: +editor.overwrite = [accent]警告!這將會覆蓋現有的地圖。 +editor.overwrite.confirm = [scarlet]警告![]同名地圖存在,確定要覆蓋現有地圖? +editor.selectmap = 選取要載入的地圖: +filters.empty = [LIGHT_GRAY]沒有過濾器!使用下面的按鈕添加一個。 +filter.distort = 歪曲 +filter.noise = 噪聲 +filter.ore = 礦石 +filter.rivernoise = 河流噪聲 +filter.scatter = 分散 +filter.terrain = 地形 +filter.option.scale = 比例 +filter.option.chance = 機會 +filter.option.mag = 大小 +filter.option.threshold = 閾 +filter.option.circle-scale = 圓形比例 +filter.option.octaves = 倍頻 +filter.option.falloff = 衰減 +filter.option.block = 方塊 +filter.option.floor = 地板 +filter.option.wall = 牆 +filter.option.ore = 礦石 +filter.option.floor2 = 次要地板 +filter.option.threshold2 = 次要閾 +filter.option.radius = Radius +filter.option.percentile = Percentile +width = 寬度: +height = 長度: +menu = 主選單 +play = 開始 +load = 載入 +save = 儲存 +fps = FPS:{0} +tps = TPS:{0} +ping = 延遲:{0}ms +language.restart = 請重新啟動遊戲以使選取的語言生效。 +settings = 設定 +tutorial = 教學 +editor = 地圖編輯器 +mapeditor = 地圖編輯器 +donate = 贊助 +abandon = 放棄 +abandon.text = 此區域及其所有資源將會丟失給敵人。 +locked = 鎖定 +complete = [LIGHT_GRAY]完成: +zone.requirement = Wave {0} in zone {1} +resume = 繼續區域:\n[LIGHT_GRAY]{0} +bestwave = [LIGHT_GRAY]高分:{0} +launch = 發射 +launch.title = 發射成功 +launch.next = [LIGHT_GRAY]下次的機會於波次{0} +launch.unable = [scarlet]無法發射。[]有敵人。 +launch.confirm = 這將發射核心中的所有資源。\n你將無法返回這個基地。 +uncover = 揭露 +configure = 配置裝載 +configure.locked = [LIGHT_GRAY]到達波次{0}\n以配置裝載。 +zone.unlocked = [LIGHT_GRAY]{0}已解鎖。 +zone.requirement.complete = Wave {0} reached:\n{1} zone requirements met. +zone.config.complete = 到達波次{0}:\n裝載配置已解鎖。 +zone.resources = 檢測到的資源: +add = 新增…… +boss.health = 頭目血量 +connectfail = [crimson]無法連線到伺服器:[accent]{0} +error.unreachable = 無法到達伺服器。 +error.invalidaddress = 無效地址。 +error.timedout = 超時連接!\n確保伺服器設置了連接埠轉發,並且地址正確! +error.mismatch = 包裹錯誤:\n客戶端/伺服器版本可能不匹配。 n確保客戶端和伺服器有最新版本的Mindustry! +error.alreadyconnected = 已連接。 +error.mapnotfound = 找不到地圖! +error.io = 網絡輸入輸出錯誤。 +error.any = 未知網絡錯誤。 +zone.groundZero.name = 歸零地 +zone.desertWastes.name = Desert Wastes +zone.craters.name = 隕石坑 +zone.frozenForest.name = 冰凍森林 +zone.ruinousShores.name = 毀滅性的海岸 +zone.stainedMountains.name = 染山 +zone.desolateRift.name = 荒涼的裂痕 +zone.nuclearComplex.name = 核生產綜合體 +zone.overgrowth.name = Overgrowth +zone.tarFields.name = Tar Fields +settings.language = 語言 +settings.reset = 重設為預設設定 +settings.rebind = 重新綁定 +settings.controls = 操作 +settings.game = 遊戲 +settings.sound = 音效 +settings.graphics = 圖形 +settings.cleardata = 清除遊戲數據…… +settings.clear.confirm = 您確定要清除數據嗎?\n此操作無法撤回! +settings.clearall.confirm = [scarlet]警告![]\n這將清除所有數據,包括存檔、地圖、解鎖和熱鍵綁定。\n按「是」後,遊戲將刪除所有數據並自動退出。 +settings.clearunlocks = 清除已解鎖 +settings.clearall = 清除所有 +paused = [accent]〈已暫停〉 +yes = 是 +no = 否 +info.title = [accent]資訊 +error.title = [crimson]發生錯誤 +error.crashtitle = 發生錯誤 +blocks.input = Input +blocks.output = Output +blocks.booster = Booster +block.unknown = [LIGHT_GRAY]??? +blocks.powercapacity = 蓄電量 +blocks.powershot = 能量/射擊 +blocks.targetsair = 攻擊空中目標 +blocks.targetsground = 攻擊地面 +blocks.itemsmoved = 移動速度 +blocks.launchtime = Time Between Launches +blocks.shootrange = 範圍 +blocks.size = 尺寸 +blocks.liquidcapacity = 液體容量 +blocks.powerrange = 輸出範圍 +blocks.poweruse = 能量使用 +blocks.powerdamage = 能量/傷害 +blocks.itemcapacity = 物品容量 +blocks.basepowergeneration = 基本能量生产 +blocks.productiontime = Production Time +blocks.repairtime = Block Full Repair Time +blocks.speedincrease = Speed Increase +blocks.range = Range +blocks.drilltier = 可鑽取礦物 +blocks.drillspeed = 基本鑽取速度 +blocks.boosteffect = Boost Effect +blocks.maxunits = 最大活躍單位 +blocks.health = 耐久度 +blocks.buildtime = Build Time +blocks.inaccuracy = 誤差 +blocks.shots = 射擊數 +blocks.reload = 重裝彈藥 +blocks.ammo = 彈藥 +bar.drillspeed = 鑽頭速度:{0}/秒 +bar.efficiency = 效率:{0}% +bar.powerbalance = 能量變化:{0} +bar.poweramount = Power: {0} +bar.poweroutput = 能量輸出:{0} +bar.items = 物品:{0} +bar.liquid = 液體 +bar.heat = 熱 +bar.power = 能量 +bar.progress = 建造進度 +bar.spawned = 單位:{0}/{1} +bullet.damage = [stat]{0}[lightgray] dmg +bullet.splashdamage = [stat]{0}[lightgray] area dmg ~[stat] {1}[lightgray] tiles +bullet.incendiary = [stat]incendiary +bullet.homing = [stat]homing +bullet.shock = [stat]shock +bullet.frag = [stat]frag +bullet.knockback = [stat]{0}[lightgray] knockback +bullet.freezing = [stat]freezing +bullet.tarred = [stat]tarred +bullet.multiplier = [stat]{0}[lightgray]x ammo multiplier +bullet.reload = [stat]{0}[lightgray]x reload +unit.blocks = 方塊 +unit.powersecond = 能量單位/秒 +unit.liquidsecond = 液體單位/秒 +unit.itemssecond = 物品/秒 +unit.liquidunits = 液體單位 +unit.powerunits = 能量單位 +unit.degrees = 度 +unit.seconds = 秒 +unit.persecond = /sec +unit.timesspeed = x speed +unit.percent = % +unit.items = 物品 +category.general = 一般 +category.power = 能量 +category.liquids = 液體 +category.items = 物品 +category.crafting = 合成 +category.shooting = 射擊 +category.optional = 可選的強化 +setting.landscape.name = Lock Landscape +setting.shadows.name = Shadows +setting.linear.name = Linear Filtering +setting.animatedwater.name = 動畫水 +setting.animatedshields.name = Animated Shields +setting.antialias.name = 消除鋸齒[LIGHT_GRAY](需要重啟)[] +setting.indicators.name = 盟友指標 +setting.autotarget.name = 自動射擊 +setting.fpscap.name = 最大FPS +setting.fpscap.none = 没有 +setting.fpscap.text = {0}FPS +setting.swapdiagonal.name = 始終對角線放置 +setting.difficulty.training = 訓練 +setting.difficulty.easy = 簡單 +setting.difficulty.normal = 普通 +setting.difficulty.hard = 困難 +setting.difficulty.insane = 瘋狂 +setting.difficulty.name = 難度: +setting.screenshake.name = 畫面抖動 +setting.effects.name = 顯示特效 +setting.sensitivity.name = 控制器靈敏度 +setting.saveinterval.name = 自動存檔間隔 +setting.seconds = {0}秒 +setting.fullscreen.name = 全螢幕 +setting.borderlesswindow.name = Borderless Window[LIGHT_GRAY] (may require restart) +setting.fps.name = 顯示FPS +setting.vsync.name = 垂直同步 +setting.lasers.name = 顯示雷射光束 +setting.pixelate.name = Pixelate [LIGHT_GRAY](may decrease performance) +setting.minimap.name = 顯示小地圖 +setting.musicvol.name = 音樂音量 +setting.mutemusic.name = 靜音 +setting.sfxvol.name = 音效音量 +setting.mutesound.name = 靜音 +setting.crashreport.name = 發送匿名崩潰報告 +setting.chatopacity.name = Chat Opacity +setting.playerchat.name = Display In-Game Chat +keybind.title = 重新綁定按鍵 +category.general.name = 一般 +category.view.name = 查看 +category.multiplayer.name = 多人 +command.attack = 攻擊 +command.retreat = 撤退 +command.patrol = 巡邏 +keybind.gridMode.name = 方塊選取 +keybind.gridModeShift.name = 類別選取 +keybind.press = 按一下鍵…… +keybind.press.axis = 按一下軸心或鍵…… +keybind.screenshot.name = 地圖截圖 +keybind.move_x.name = 水平移動 +keybind.move_y.name = 垂直移動 +keybind.select.name = 選取 +keybind.diagonal_placement.name = 對角線放置 +keybind.pick.name = 選擇方塊 +keybind.break_block.name = 移除方塊 +keybind.deselect.name = 取消選取 +keybind.shoot.name = 射擊 +keybind.zoom_hold.name = 按住縮放 +keybind.zoom.name = 縮放 +keybind.menu.name = 主選單 +keybind.pause.name = 暫停遊戲 +keybind.minimap.name = Minimap +keybind.dash.name = 衝刺 +keybind.chat.name = 聊天 +keybind.player_list.name = 玩家列表 +keybind.console.name = 終端機 +keybind.rotate.name = 旋轉 +keybind.toggle_menus.name = 切換畫面 +keybind.chat_history_prev.name = 之前的聊天記錄 +keybind.chat_history_next.name = 之後的聊天記錄 +keybind.chat_scroll.name = 聊天記錄滾動 +keybind.drop_unit.name = 放下單位 +keybind.zoom_minimap.name = 縮放小地圖 +mode.help.title = 模式說明 +mode.survival.name = 生存 +mode.survival.description = 一般模式。有限的資源與自動來襲的波次。 +mode.sandbox.name = 沙盒 +mode.sandbox.description = 無限的資源,與不倒數計時的波次。 +mode.pvp.name = 對戰 +mode.pvp.description = 和其他玩家鬥爭。 +mode.attack.name = 攻擊 +mode.attack.description = 沒有波次,目標是摧毀敵人的基地。 +mode.custom = Custom Rules +rules.infiniteresources = Infinite Resources +rules.wavetimer = Wave Timer +rules.waves = Waves +rules.enemyCheat = Infinite AI Resources +rules.unitdrops = Unit Drops +rules.unitbuildspeedmultiplier = Unit Creation Speed Multiplier +rules.unithealthmultiplier = Unit Health Multiplier +rules.playerhealthmultiplier = Player Health Multiplier +rules.playerdamagemultiplier = Player Damage Multiplier +rules.unitdamagemultiplier = Unit Damage Multiplier +rules.enemycorebuildradius = Enemy Core No-Build Radius:[LIGHT_GRAY] (tiles) +rules.respawntime = Respawn Time:[LIGHT_GRAY] (sec) +rules.wavespacing = Wave Spacing:[LIGHT_GRAY] (sec) +rules.buildcostmultiplier = Build Cost Multiplier +rules.buildspeedmultiplier = Build Speed Multiplier +rules.waitForWaveToEnd = Waves wait for enemies +rules.dropzoneradius = Drop Zone Radius:[LIGHT_GRAY] (tiles) +rules.respawns = Max respawns per wave +rules.limitedRespawns = Limit Respawns +rules.title.waves = Waves +rules.title.respawns = Respawns +rules.title.resourcesbuilding = Resources & Building +rules.title.player = Players +rules.title.enemy = Enemies +rules.title.unit = Units +content.item.name = 物品 +content.liquid.name = 液體 +content.unit.name = 機組 +content.block.name = 方塊 +content.mech.name = 機甲 +item.copper.name = 銅 +item.copper.description = 一種有用的結構材料。在各種類型的方塊中廣泛使用。 +item.lead.name = 鉛 +item.lead.description = 一種基本的起始材料。被廣泛用於電子設備和運輸液體方塊。 +item.coal.name = 煤 +item.coal.description = 一種常見並容易獲得的燃料。 +item.graphite.name = 石墨 +item.titanium.name = 鈦 +item.titanium.description = 一種罕見的超輕金屬,被廣泛運用於運輸液體、鑽頭和飛機。 +item.thorium.name = 釷 +item.thorium.description = 一種稠密的放射性金屬,用作支撐結構和核燃料。 +item.silicon.name = 矽 +item.silicon.description = 一種非常有用的半導體,被用於太陽能電池板和很多複雜的電子設備。 +item.plastanium.name = 塑料 +item.plastanium.description = 一種輕量、可延展的材料,用於高級的飛機和碎彈藥。 +item.phase-fabric.name = 相織布 +item.phase-fabric.description = 一種近乎無重量的物質,用於先進的電子設備和自修復技術。 +item.surge-alloy.name = 波動合金 +item.surge-alloy.description = 一種具有獨特電子特性的高級合金。 +item.spore-pod.name = 孢子莢 +item.spore-pod.description = 用於轉化為石油、爆炸物和燃料。 +item.sand.name = 沙 +item.sand.description = 一種常見的材料,廣泛用於冶煉,包括製作合金和助熔劑。 +item.blast-compound.name = 爆炸混合物 +item.blast-compound.description = 一種用於炸彈和炸藥的揮發性混合物。雖然它可以作為燃料燃燒,但不建議這樣做。 +item.pyratite.name = 硫 +item.pyratite.description = 一種在燃燒武器中使用的極易燃物質。 +item.metaglass.name = 金屬玻璃 +item.metaglass.description = 一種超級強硬玻璃混合物。廣泛用於液體分配和存儲。 +item.scrap.name = 廢料 +item.scrap.description = 舊結構和單位的遺留剩餘物。含有痕量的許多不同的金屬。 +liquid.water.name = 水 +liquid.slag.name = 礦渣 +liquid.oil.name = 原油 +liquid.cryofluid.name = 冷凍液 +mech.alpha-mech.name = 阿爾法 +mech.alpha-mech.weapon = 重型機關槍 +mech.alpha-mech.ability = 無人機群 +mech.alpha-mech.description = 標準的機甲。具有不錯的速度和傷害輸出;可以製造多達3架無人機以提高進攻能力。 +mech.delta-mech.name = 德爾塔 +mech.delta-mech.weapon = 電弧生成機 +mech.delta-mech.ability = 放電 +mech.delta-mech.description = 一种快速、轻铠的机甲,是用於打了就跑的攻擊。对结构造成的伤害很小,但可以用弧形闪电武器很快杀死大量敌方机组。 +mech.tau-mech.name = Tau機甲 +mech.tau-mech.weapon = 重構激光 +mech.tau-mech.ability = 修复陣 +mech.tau-mech.description = 支援機甲。射擊友好方塊以治療它們。可以使用它的修復能力熄滅火焰並治療一定範圍內的友軍。 +mech.omega-mech.name = 奧米伽 +mech.omega-mech.weapon = 導彈群 +mech.omega-mech.ability = 裝甲配置 +mech.omega-mech.description = 一種笨重、裝甲重的機甲,用於在前線突擊。它的裝甲能力可以阻擋高達90%的傷害。 +mech.dart-ship.name = 鏢船 +mech.dart-ship.weapon = 機關槍 +mech.dart-ship.description = 標準飛船。快速、輕便,但有低的攻擊能力和慢的採礦速度。 +mech.javelin-ship.name = 標槍 +mech.javelin-ship.description = 一種打了就跑的侵襲船。雖然最初很慢,但它可以加速到很快的速度,並飛過敵人的前哨站,利用其閃電能力和導彈造成大量的傷害。 +mech.javelin-ship.weapon = 爆發導彈 +mech.javelin-ship.ability = 放電助推器 +mech.trident-ship.name = 三叉 +mech.trident-ship.description = 一种重型轰炸机。有比較厚的装甲。 +mech.trident-ship.weapon = 炸彈 +mech.glaive-ship.name = 長柄 +mech.glaive-ship.description = 一種大型、裝甲厚的武裝直升機。配備燃燒機關槍。有優秀的加速能力與最快的速度。 +mech.glaive-ship.weapon = 火焰機關槍 +item.explosiveness = [LIGHT_GRAY]爆炸性:{0} +item.flammability = [LIGHT_GRAY]易燃性:{0} +item.radioactivity = [LIGHT_GRAY]放射性:{0} +unit.health = [LIGHT_GRAY]耐久度:{0} +unit.speed = [LIGHT_GRAY]速度:{0} +mech.weapon = [LIGHT_GRAY]武器:{0} +mech.health = [LIGHT_GRAY]血量:{0} +mech.itemcapacity = [LIGHT_GRAY]物品容量:{0} +mech.minespeed = [LIGHT_GRAY]採礦速度:{0} +mech.minepower = [LIGHT_GRAY]採礦力度:{0} +mech.ability = [LIGHT_GRAY]能力:{0} +mech.buildspeed = [LIGHT_GRAY]Building Speed: {0}% +liquid.heatcapacity = [LIGHT_GRAY]熱容量:{0} +liquid.viscosity = [LIGHT_GRAY]粘性:{0} +liquid.temperature = [LIGHT_GRAY]温度:{0} +block.grass.name = 草 +block.salt.name = 鹽 +block.saltrocks.name = Salt Rocks +block.pebbles.name = Pebbles +block.tendrils.name = Tendrils +block.sandrocks.name = 沙岩 +block.spore-pine.name = 孢子鬆 +block.sporerocks.name = 孢子岩 +block.rock.name = 岩石 +block.snowrock.name = 雪巖 +block.shale.name = 頁岩 +block.shale-boulder.name = 頁岩巨石 +block.moss.name = 苔蘚 +block.shrubs.name = Shrubs +block.spore-moss.name = 孢子苔蘚 +block.shalerocks.name = 頁岩岩石 +block.scrap-wall.name = 廢牆 +block.scrap-wall-large.name = 大型廢牆 +block.scrap-wall-huge.name = 巨型廢牆 +block.scrap-wall-gigantic.name = 超巨型廢牆 +block.thruster.name = 推進器 +block.kiln.name = 窯 +block.kiln.description = 將沙子和鉛熔煉成金屬玻璃。需要少量能量。 +block.graphite-press.name = 石墨壓縮機 +block.multi-press.name = 多用途壓縮機 +block.constructing = {0}\n[LIGHT_GRAY](建設中) +block.spawn.name = 敵人生成 +block.core-shard.name = 核心:碎片 +block.core-foundation.name = 核心:基礎 +block.core-nucleus.name = 核心:核子 +block.deepwater.name = 深水 +block.water.name = 水 +block.tainted-water.name = 污水 +block.darksand-tainted-water.name = 黑沙污水 +block.tar.name = 焦油 +block.stone.name = 石頭 +block.sand.name = 沙 +block.darksand.name = 黑沙 +block.ice.name = 冰 +block.snow.name = 雪 +block.craters.name = 隕石坑 +block.sand-water.name = 沙水 +block.darksand-water.name = 黑沙水 +block.char.name = 燒焦 +block.holostone.name = 霍洛石頭 +block.ice-snow.name = 冰雪 +block.rocks.name = 岩石 +block.icerocks.name = 冰岩 +block.snowrocks.name = 雪巖 +block.dunerocks.name = 沙丘岩 +block.pine.name = 松樹 +block.white-tree-dead.name = 白樹死了 +block.white-tree.name = 白樹 +block.spore-cluster.name = 孢子簇 +block.metal-floor.name = 金屬地板 +block.metal-floor-2.name = 金屬地板二 +block.metal-floor-3.name = 金屬地板三 +block.metal-floor-5.name = 金屬地板五 +block.metal-floor-damaged.name = 金屬地板損壞 +block.dark-panel-1.name = Dark Panel 1 +block.dark-panel-2.name = Dark Panel 2 +block.dark-panel-3.name = Dark Panel 3 +block.dark-panel-4.name = Dark Panel 4 +block.dark-panel-5.name = Dark Panel 5 +block.dark-panel-6.name = Dark Panel 6 +block.dark-metal.name = Dark Metal +block.ignarock.name = 火成岩 +block.hotrock.name = 熱岩 +block.magmarock.name = 岩漿岩 +block.cliffs.name = 懸崖 +block.copper-wall.name = 銅牆 +block.copper-wall-large.name = 大型銅牆 +block.titanium-wall.name = 鈦牆 +block.titanium-wall-large.name = 大型鈦牆 +block.phase-wall.name = 相織布牆 +block.phase-wall-large.name = 大型相織布牆 +block.thorium-wall.name = 釷牆 +block.thorium-wall-large.name = 大型釷牆 +block.door.name = 門 +block.door-large.name = 大門 +block.duo.name = 雙炮 +block.scorch.name = 燒焦炮 +block.scatter.name = 分散炮 +block.hail.name = 冰雹炮 +block.lancer.name = 藍瑟炮 +block.conveyor.name = 輸送帶 +block.titanium-conveyor.name = 鈦輸送帶 +block.junction.name = 樞紐 +block.router.name = 分配器 +block.distributor.name = 大型分配器 +block.sorter.name = 分類器 +block.sorter.description = 對物品進行分類。如果物品與所選種類匹配,則允許其通過。否則,物品將從左邊和右邊輸出。 +block.overflow-gate.name = 溢流器 +block.overflow-gate.description = 分離器和分配器的組合。如果前面被擋住,則向從左邊和右邊輸出物品。 +block.silicon-smelter.name = 煉矽廠 +block.phase-weaver.name = 相織布編織器 +block.pulverizer.name = 粉碎機 +block.cryofluidmixer.name = 冷凍液混合器 +block.melter.name = 熔爐 +block.incinerator.name = 焚化爐 +block.spore-press.name = 孢子壓縮機 +block.separator.name = 分離機 +block.coal-centrifuge.name = Coal Centrifuge +block.power-node.name = 能量節點 +block.power-node-large.name = 大型能量節點 +block.surge-tower.name = 波動塔 +block.battery.name = 電池 +block.battery-large.name = 大型電池 +block.combustion-generator.name = 燃燒發電機 +block.turbine-generator.name = 渦輪發電機 +block.differential-generator.name = 差動發電機 +block.impact-reactor.name = 衝擊反應堆 +block.mechanical-drill.name = 機械鑽頭 +block.pneumatic-drill.name = 氣動鑽頭 +block.laser-drill.name = 激光鑽頭 +block.water-extractor.name = 水提取器 +block.cultivator.name = 耕種機 +block.dart-mech-pad.name = Dart Mech Pad +block.delta-mech-pad.name = 德爾塔機甲墊 +block.javelin-ship-pad.name = 標槍機甲墊 +block.trident-ship-pad.name = 三叉船墊 +block.glaive-ship-pad.name = 長柄船墊 +block.omega-mech-pad.name = 奧米伽機甲墊 +block.tau-mech-pad.name = Tau機甲墊 +block.conduit.name = 管線 +block.mechanical-pump.name = 機械泵 +block.item-source.name = 物品源 +block.item-void.name = 物品虚 +block.liquid-source.name = 液體源 +block.power-void.name = 能量空虛 +block.power-source.name = 無限能量 +block.unloader.name = 裝卸器 +block.vault.name = 存儲庫 +block.wave.name = 波浪炮 +block.swarmer.name = 群炮 +block.salvo.name = 齊射炮 +block.ripple.name = 波紋炮 +block.phase-conveyor.name = 相織傳送帶 +block.bridge-conveyor.name = 傳送帶橋 +block.plastanium-compressor.name = 塑料壓縮機 +block.pyratite-mixer.name = 硫混合器 +block.blast-mixer.name = 爆炸混合器 +block.solar-panel.name = 太陽能板 +block.solar-panel-large.name = 大型太陽能板 +block.oil-extractor.name = 石油鑽井 +block.spirit-factory.name = 輕型無人機工廠 +block.phantom-factory.name = 幻影無人機工廠 +block.wraith-factory.name = 怨靈戰鬥機工廠 +block.ghoul-factory.name = 食屍鬼轟炸機工廠 +block.dagger-factory.name = 匕首機甲工廠 +block.crawler-factory.name = 爬行機甲工廠 +block.titan-factory.name = 泰坦機甲工廠 +block.fortress-factory.name = 堡壘機甲工廠 +block.revenant-factory.name = 亡魂戰鬥機工廠 +block.repair-point.name = 維修點 +block.pulse-conduit.name = 脈衝管線 +block.phase-conduit.name = 相織管線 +block.liquid-router.name = 液體分配器 +block.liquid-tank.name = 液體儲罐 +block.liquid-junction.name = 液體連接點 +block.bridge-conduit.name = 管線橋 +block.rotary-pump.name = 迴旋泵 +block.thorium-reactor.name = 釷反應堆 +block.mass-driver.name = 質量驅動器 +block.blast-drill.name = 爆破鑽頭 +block.thermal-pump.name = 熱能泵 +block.thermal-generator.name = 熱能發電機 +block.alloy-smelter.name = 合金冶煉廠 +block.mender.name = Mender +block.mend-projector.name = 修理投影器 +block.surge-wall.name = 波動牆 +block.surge-wall-large.name = 大型波動牆 +block.cyclone.name = 氣旋炮 +block.fuse.name = 融合炮 +block.shock-mine.name = 休克地雷 +block.overdrive-projector.name = 超速投影器 +block.force-projector.name = 力牆投影器 +block.arc.name = 電弧 +block.rtg-generator.name = 放射性同位素熱發電機 +block.spectre.name = 幽靈炮 +block.meltdown.name = 熔毀炮 +block.container.name = 容器 +block.launch-pad.name = 發射台 +block.launch-pad.description = 無需從核心發射即可發射物品。未完成。 +block.launch-pad-large.name = Large Launch Pad +team.blue.name = 藍 +team.red.name = 紅 +team.orange.name = 橙 +team.none.name = 灰 +team.green.name = 綠 +team.purple.name = 紫 +unit.spirit.name = 輕型無人機 +unit.spirit.description = 起始的無人機。默認在核心產生。自動挖掘礦石、收集物品和修理方塊。 +unit.phantom.name = 幻影無人機 +unit.phantom.description = 一種高級的無人機。自動挖掘礦石、收集物品和修理方塊。比輕型無人機明顯更有效。 +unit.dagger.name = 匕首 +unit.dagger.description = 一種基本的地面單位。最好一群地使用。 +unit.crawler.name = 爬行 +unit.titan.name = 泰坦 +unit.titan.description = 一種高級的具有裝甲的地面單位。使用碳化物作為彈藥。攻擊地面單位和空中單位。 +unit.ghoul.name = 食屍鬼轟炸機 +unit.ghoul.description = 一種重型的鋪蓋性的轟炸機。使用爆炸化合物或黃鐵礦作為彈藥。 +unit.wraith.name = 怨靈戰鬥機 +unit.wraith.description = 一種快速、打了就跑的攔截機。 +unit.fortress.name = 堡壘 +unit.fortress.description = 一種具有重型大砲的地面單位。 +unit.revenant.name = 亡魂 +unit.eruptor.name = 爆發者 +unit.chaos-array.name = 混沌陣 +unit.eradicator.name = 消除者 +unit.lich.name = 巫妖 +unit.reaper.name = 收割者 +tutorial.begin = 你的任務是毀滅[LIGHT_GRAY]敵人[]。\n\n首先[accent]挖掘銅礦[]。點擊核心附近的銅脈以開始。 +tutorial.drill = 手動挖掘礦石是低效率的。\n[accent]鑽頭[]能夠自動挖掘礦石。\n在銅脈上放置一個鑽頭。 +tutorial.conveyor = [accent]輸送帶[]能夠將物品運輸到核心。\n製作一條從鑽頭開始到核心的輸送帶。 +tutorial.morecopper = 需要更多銅。\n\n請手動挖掘銅礦或放置更多鑽頭。 +tutorial.turret = 防禦建築是必須的以擊退[LIGHT_GRAY]敵人[]。\n於核心附近建造一個雙炮。 +tutorial.drillturret = 雙炮需要[accent]銅彈[]以射擊。\n在雙炮旁邊放置一個鑽頭以供應銅。 +tutorial.waves = [LIGHT_GRAY]敵人[]來臨。\n\n防衛核心2波。建造更多的砲塔以防衛。 +tutorial.lead = 有更多的礦石可用。探索和挖掘[accent]鉛[]。\n\n點擊您的單位拖動到核心以傳輸資源。 +tutorial.smelter = 銅和鉛是柔軟的金屬。\n優良的[accent]稠密合金[]可以在冶煉廠中生產。\n\n建造一個冶煉廠。 +tutorial.densealloy = 冶煉廠現在將生產稠密合金。\n請獲取更多的稠密合金。\n必要時改善生產系統。 +tutorial.siliconsmelter = 核心現在將製作一個[accent]輕型無人機[]以挖掘礦石和修理方塊。\n\n製作其他單位的工廠可以使用[accent]矽[]以建造。\n建造一個冶矽廠。 +tutorial.silicondrill = 矽需要[accent]煤[]和[accent]沙[]以製作。\n放置鑽頭以開始。 +tutorial.generator = 這項技術需要能量。\n建造一個[accent]燃燒發電機[]。 +tutorial.generatordrill = 燃燒發電機需要燃料。\n用鑽頭挖的煤為它加燃料。 +tutorial.node = 電源需要運輸。\n在燃燒發電機旁邊建造一個[accent]能量節點[]以傳遞其能量。 +tutorial.nodelink = 能量可透過接觸的能量方塊和發電機,或者通過連接的能量節點傳遞。\n\n點擊能量節點並選擇發電機和冶矽廠以連接能量。 +tutorial.silicon = 正在製作矽。請獲取更多的矽。\n\n建議改善生產系統。 +tutorial.daggerfactory = 建造一個[accent]匕首機甲工廠[]。\n\n這將製作攻擊機甲。 +tutorial.router = 工廠需要資源以運作。\n建造一個分配器以均分輸送帶的資源。 +tutorial.dagger = 連接能量節點至工廠。\n一旦要求滿足,將製作一個機甲。\n\n根據需要建造更多鑽頭、發電機和輸送帶。發電機和輸送帶。 +tutorial.battle = [LIGHT_GRAY]敵人[]透露了他們的核心。\n用你的單位和匕首機甲以摧毀它。 +block.copper-wall.description = 一種便宜的防衛方塊。\n用於前幾波防衛核心和砲塔。 +block.copper-wall-large.description = 一種便宜的防衛方塊。\n用於前幾波防衛核心和砲塔\n佔據多個方塊。 +block.thorium-wall.description = 一種堅強的防衛方塊。\n良好地防衛敵人。 +block.thorium-wall-large.description = 一種堅強的防衛方塊。\n良好地防衛敵人。\n佔據多個方塊。 +block.phase-wall.description = 沒有釷牆那麼強但會使不太強的子彈偏離。 +block.phase-wall-large.description = 沒有釷牆那麼強但會使不太強的子彈偏離。\n佔據多個方塊。 +block.surge-wall.description = 最強的防衛方塊。\n有小的機會對攻擊者觸發閃電。 +block.surge-wall-large.description = 最強的防衛方塊。\n有小的機會對攻擊者觸發閃電。\n佔據多個方塊。 +block.door.description = 可以通過點擊打開和關閉的一扇小門。\n如果打開,敵人可以穿過它射擊和移動。 +block.door-large.description = 可以通過點擊打開和關閉的一扇大門。\n如果打開,敵人可以穿過它射擊和移動。\n佔據多個方塊。 +block.mend-projector.description = 定期修復附近的建築物。 +block.overdrive-projector.description = 提高附近建築物的速度,如鑽頭和輸送帶。 +block.force-projector.description = 在自身周圍形成一個六角形力場,保護內部的建築物和單位免受子彈的傷害。 +block.shock-mine.description = 傷害踩到地雷的敵人。敵人幾乎看不見。 +block.duo.description = 一種小而便宜的砲塔。 +block.scatter.description = 一種中型防空砲塔。向敵方單位噴射鉛塊或碎片。 +block.arc.description = 一種向敵人射出隨機電弧的小砲塔。 +block.hail.description = 一種小型火砲。 +block.lancer.description = 一種射出電子束的中型砲塔。 +block.wave.description = 一種可以快速射出液體氣泡的中型砲塔。 +block.salvo.description = 一種齊射的中型砲塔。 +block.swarmer.description = 一種射出爆炸導彈的中型砲塔。 +block.ripple.description = 一種一次射出幾發子彈的大型火砲。 +block.cyclone.description = 一種快速射擊的大型砲塔。 +block.fuse.description = 一種射出強大的短程射線的大型砲塔。 +block.spectre.description = 一種一次射出兩顆強大的子彈的大型砲塔。 +block.meltdown.description = 一種射出強大的遠程光束的大型砲塔。 +block.conveyor.description = 基本物品傳輸方塊。將物品向前移動並自動將它們放入砲塔或機器中。能夠旋轉方向。 +block.titanium-conveyor.description = 高級物品傳輸方塊。比標準輸送帶更快地移動物品。 +block.phase-conveyor.description = 高級物品傳輸方塊。使用能量將物品傳送到幾個方塊外連接的相織輸送帶。 +block.junction.description = 作為兩個交叉輸送帶的橋樑。適用於兩條不同輸送帶將不同物品運送到不同位置的情況。 +block.mass-driver.description = 終極物品運輸方塊。收集幾件物品,然後將它們射向另一個長距離的質量驅動器。 +block.silicon-smelter.description = 使用高純度焦炭還原沙子以生產矽。 +block.plastanium-compressor.description = 使用油和鈦以生產塑料。 +block.phase-weaver.description = 使用放射性的釷和大量的沙子以生產相織布。 +block.alloy-smelter.description = 使用鈦、鉛、矽和銅以生產波動合金。 +block.pulverizer.description = 將石頭壓成沙子。當缺少天然沙子時有用。 +block.pyratite-mixer.description = 混合煤、鉛和沙子成為易燃的硫。 +block.blast-mixer.description = 使用油將硫變成比較不易燃但更具爆炸性的爆炸混合器。 +block.cryofluidmixer.description = 合水和鈦成冷卻效率更高的冷凍液。 +block.melter.description = 將石頭加熱到很高的溫度以獲得熔岩。 +block.incinerator.description = 清除任何多餘的物品或液體。 +block.spore-press.description = 將孢子莢壓縮成油。 +block.separator.description = 將石頭暴露在水壓下以獲得石頭中的各種礦物質。 +block.power-node.description = 將能量傳輸到連接的節點。最多可連接四個能量來源、接收或節點。節點將從任何相鄰方塊接收能量或向其供能量。 +block.power-node-large.description = 範圍大於能量節點,最多可連接六個能量來源、接收或節點。 +block.battery.description = 有能量剩餘時,存儲電力並在能量短缺時提供能量。 +block.battery-large.description = 比普通電池存儲更多的能量。 +block.combustion-generator.description = 透過燃燒油或可燃物品以產生能量。 +block.turbine-generator.description = 比燃燒發電機更有效,但需要水以操作。 +block.thermal-generator.description = 使用熔岩產生大量的能量。 +block.solar-panel.description = 透過太陽產生少量的能量。 +block.solar-panel-large.description = 比標準太陽能板產生更多的能量,但建造起來昂貴得多。 +block.thorium-reactor.description = 從高度放射性釷產生大量能量。需要持續冷卻。如果供應的冷卻劑不足,會劇烈爆炸。 +block.rtg-generator.description = 一種放射性同位素熱發電機,不需要冷卻,但比釷反應堆產生的能量少。 +block.unloader.description = 將物品從容器、存儲庫或核心卸載到傳輸帶上或直接卸載到相鄰的方塊中。透過點擊卸載器來更改要卸載的物品類型。 +block.container.description = 存儲少量物品。當物品需求非恆定時,使用它來創建緩衝。使用[LIGHT_GRAY]裝卸器[]以從容器提取物品。 +block.vault.description = 存儲大量物品。當物品需求非恆定時,使用它來創建緩衝。使用[LIGHT_GRAY]裝卸器[]以從存儲庫提取物品。 +block.mechanical-drill.description = 一種便宜的鑽頭。當放置在適當的方塊上時,以緩慢的速度無限期地輸出物品。 +block.pneumatic-drill.description = 一種改進的鑽頭。它挖掘更快,能夠利用氣壓挖掘更硬的材料。 +block.laser-drill.description = 通過激光技術可以更快地挖掘,但需要能量。此外,這種鑽頭可以挖掘放射性釷。 +block.blast-drill.description = 終極的鑽頭。需要大量能量。 +block.water-extractor.description = 從地下提取水。當附近沒有湖泊時使用它。 +block.cultivator.description = 用水培養土壤以獲得生物物質。 +block.oil-extractor.description = 使用大量的能量從沙子中提取油。當附近沒有直接的石油來源時使用它。 +block.trident-ship-pad.description = 離開現在的船隻,換成具有相當不錯裝甲的重型轟炸機。\n站在上面雙擊墊以使用它。 +block.javelin-ship-pad.description = 離開現在的船隻,換成具有閃電武器、強大而快速的攔截器。\n站在上面雙擊墊以使用它。 +block.glaive-ship-pad.description = 離開現在的船隻,換成具有重裝甲的武裝直升機。\n站在上面雙擊墊以使用它。 +block.tau-mech-pad.description = 離開現有的船隻,換成可以治愈友好的建築物和單位的支援機甲。\n站在上面雙擊墊以使用它。 +block.delta-mech-pad.description = 離開現在的船隻,換成快速、具有輕裝甲的機甲,用於打了就跑的攻擊。\n站在上面雙擊墊以使用它。 +block.omega-mech-pad.description = 離開現在的船隻,換成龐大、具有重裝甲的機甲,用於前線攻擊。\n站在上面雙擊墊以使用它。 +block.spirit-factory.description = 生產輕型無人機,用於開採礦石和修復方塊。 +block.phantom-factory.description = 生產高級的無人機,比輕型無人機明顯更有效。 +block.wraith-factory.description = 生產快速、打了就跑的攔截機單位。 +block.ghoul-factory.description = 生產重型鋪蓋轟炸機。 +block.dagger-factory.description = 產生基本地面單位。 +block.titan-factory.description = 生產具有裝甲的高級地面單位。 +block.fortress-factory.description = 生產重型火砲地面單位。 +block.revenant-factory.description = 生產重型激光地面單位。 +block.repair-point.description = 持續治療附近最近的受損單位。 +block.conduit.description = 基本液體運輸方塊。像輸送帶一樣工作,但是液體用的。最適用於提取器、泵或其他管線。 +block.pulse-conduit.description = 高級的液體運輸方塊。比標準管線更快地輸送並儲存更多液體。 +block.phase-conduit.description = 高級的液體運輸方塊。使用能量將液體傳送到多個方塊外連接的相織管線。 +block.liquid-router.description = 接受來自一個方向的液體並將它們平均輸出到最多3個其他方向。可以儲存一定量的液體。用於將液體從一個來源分成多個目標。 +block.liquid-tank.description = 存儲大量液體。當液體需求非恆定時,使用它來創建緩衝或作為冷卻重要方塊的保障。 +block.liquid-junction.description = 作為兩個交叉管線的橋樑。適用於兩條不同管線將不同液體運送到不同位置的情況。 +block.bridge-conduit.description = 高級的液體運輸方塊。允許跨過最多3個任何地形或建築物的方塊運輸液體。 +block.mechanical-pump.description = 一種便宜的泵,輸出速度慢,但不使用能量。 +block.rotary-pump.description = 高級的泵,透過使用能量使輸出速度加倍。 +block.thermal-pump.description = 終極泵。輸出速度是機械泵的三倍並且是唯一能夠抽熔岩的泵。 +block.router.description = 接受來自一個方向的物品並將它們平均輸出到最多3個其他方向。 用於將物品從一個來源分割為多個目標。 +block.distributor.description = 高級的分配器,可將物品均分到最多7個其他方向。 +block.bridge-conveyor.description = 高級的物品運輸方塊。允許跨過最多3個任何地形或建築物的方塊運輸物品。 +block.item-source.description = 不限地輸出物品。僅限沙盒。 +block.liquid-source.description = 不限地輸出液體。僅限沙盒。 +block.item-void.description = 不使用能量銷毀任何進入它的物品。僅限沙盒。 +block.power-source.description = 不限地輸出能量。僅限沙盒。 +block.power-void.description = 銷毀所有輸入的能量。僅限沙盒。 +liquid.water.description = 常用於冷卻機器和廢物處理。 +liquid.oil.description = 可以燃燒、爆炸或用作冷卻劑。 +liquid.cryofluid.description = 冷卻東西最有效的液體。 diff --git a/core/assets/contributors b/core/assets/contributors new file mode 100644 index 0000000000..7b5fb43f46 --- /dev/null +++ b/core/assets/contributors @@ -0,0 +1,73 @@ +Prosta4okua +Timmeey86 +Epowerj +Baltazár Radics +Dexapnow +Milinai +키에르 +Luxray5474 +Leone25 +Gureumi +VizardAlpha +LQ +Commodore64x +iczero +Krzysztof Skrzętnicki +Baramos666 +theshadowknight +elmenda452 +Predator127 +Sonnicon +CinExPL +toushangyouxiang +xgamezs +Skybbles // L5474 +William So +beito +BeefEX +Lorex +laohuaji233 +CrazyBearTR +Zachary +Fenr1r +Jaiun Lee +Gab_351 +Carter Gale +Jan Polák +JustYanns +BasedUser +BLucky-gh +DinoWattz +Jae +angelickite +ScriptHosT12 +Senventise +SkeptiC +Deyvid67 +Damlon +DaGamerFiles +Trigg +Uriel +VXF +Valen. H +Valentin Sonin +Clarence "Sparr" Risher +bei2 +AceEllysium +Cedric L'homme +indielm +Ameb +player20033 +Ignacy +J-VdS +Kenny +L5474 +Franciszek Zaranowicz +Andreas Heiskanen +Doyoung Gwak +MMG +Math2128 +Michael Plotke +Niko +Paul T +Dominik \ No newline at end of file diff --git a/core/assets/cursors/cursor.png b/core/assets/cursors/cursor.png index ece5696be0..d8487b7fa1 100644 Binary files a/core/assets/cursors/cursor.png and b/core/assets/cursors/cursor.png differ diff --git a/core/assets/cursors/drill.png b/core/assets/cursors/drill.png new file mode 100644 index 0000000000..9672daf2e3 Binary files /dev/null and b/core/assets/cursors/drill.png differ diff --git a/core/assets/cursors/hand.png b/core/assets/cursors/hand.png index 1613b1eac3..ef489d4554 100644 Binary files a/core/assets/cursors/hand.png and b/core/assets/cursors/hand.png differ diff --git a/core/assets/cursors/ibar.png b/core/assets/cursors/ibar.png deleted file mode 100644 index 61ddc4b423..0000000000 Binary files a/core/assets/cursors/ibar.png and /dev/null differ diff --git a/core/assets/cursors/ibeam.png b/core/assets/cursors/ibeam.png new file mode 100644 index 0000000000..7ce778a271 Binary files /dev/null and b/core/assets/cursors/ibeam.png differ diff --git a/core/assets/cursors/unload.png b/core/assets/cursors/unload.png new file mode 100644 index 0000000000..faa0fe9476 Binary files /dev/null and b/core/assets/cursors/unload.png differ diff --git a/core/assets/fonts/font.ttf b/core/assets/fonts/font.ttf new file mode 100644 index 0000000000..3ccb1ada7c Binary files /dev/null and b/core/assets/fonts/font.ttf differ diff --git a/core/assets/maps/arena.png b/core/assets/maps/arena.png deleted file mode 100644 index 9755ab4f8a..0000000000 Binary files a/core/assets/maps/arena.png and /dev/null differ diff --git a/core/assets/maps/blankmap.png b/core/assets/maps/blankmap.png deleted file mode 100644 index 52073f2a27..0000000000 Binary files a/core/assets/maps/blankmap.png and /dev/null differ diff --git a/core/assets/maps/caldera.msav b/core/assets/maps/caldera.msav new file mode 100644 index 0000000000..ecf899bca0 Binary files /dev/null and b/core/assets/maps/caldera.msav differ diff --git a/core/assets/maps/caldera.png b/core/assets/maps/caldera.png deleted file mode 100644 index c10907aadb..0000000000 Binary files a/core/assets/maps/caldera.png and /dev/null differ diff --git a/core/assets/maps/canyon.png b/core/assets/maps/canyon.png deleted file mode 100644 index 7240a5a5f5..0000000000 Binary files a/core/assets/maps/canyon.png and /dev/null differ diff --git a/core/assets/maps/caves.png b/core/assets/maps/caves.png deleted file mode 100644 index 1fc4865796..0000000000 Binary files a/core/assets/maps/caves.png and /dev/null differ diff --git a/core/assets/maps/craters.msav b/core/assets/maps/craters.msav new file mode 100644 index 0000000000..25102a3944 Binary files /dev/null and b/core/assets/maps/craters.msav differ diff --git a/core/assets/maps/delta.png b/core/assets/maps/delta.png deleted file mode 100644 index c04d38eb49..0000000000 Binary files a/core/assets/maps/delta.png and /dev/null differ diff --git a/core/assets/maps/desert.png b/core/assets/maps/desert.png deleted file mode 100644 index 99488439a3..0000000000 Binary files a/core/assets/maps/desert.png and /dev/null differ diff --git a/core/assets/maps/desolateRift.msav b/core/assets/maps/desolateRift.msav new file mode 100644 index 0000000000..e1f7fe3394 Binary files /dev/null and b/core/assets/maps/desolateRift.msav differ diff --git a/core/assets/maps/fortress.msav b/core/assets/maps/fortress.msav new file mode 100644 index 0000000000..fdfa666271 Binary files /dev/null and b/core/assets/maps/fortress.msav differ diff --git a/core/assets/maps/fortress.png b/core/assets/maps/fortress.png deleted file mode 100644 index 6b977c13de..0000000000 Binary files a/core/assets/maps/fortress.png and /dev/null differ diff --git a/core/assets/maps/frozenForest.msav b/core/assets/maps/frozenForest.msav new file mode 100644 index 0000000000..392dea3ee1 Binary files /dev/null and b/core/assets/maps/frozenForest.msav differ diff --git a/core/assets/maps/grassland.png b/core/assets/maps/grassland.png deleted file mode 100644 index 1c7e8060fe..0000000000 Binary files a/core/assets/maps/grassland.png and /dev/null differ diff --git a/core/assets/maps/groundZero.msav b/core/assets/maps/groundZero.msav new file mode 100644 index 0000000000..04841aa794 Binary files /dev/null and b/core/assets/maps/groundZero.msav differ diff --git a/core/assets/maps/impact0078.msav b/core/assets/maps/impact0078.msav new file mode 100644 index 0000000000..92afbebb7b Binary files /dev/null and b/core/assets/maps/impact0078.msav differ diff --git a/core/assets/maps/island.png b/core/assets/maps/island.png deleted file mode 100644 index 1e5d911afc..0000000000 Binary files a/core/assets/maps/island.png and /dev/null differ diff --git a/core/assets/maps/islands.msav b/core/assets/maps/islands.msav new file mode 100644 index 0000000000..ce9a3baedc Binary files /dev/null and b/core/assets/maps/islands.msav differ diff --git a/core/assets/maps/labyrinth.msav b/core/assets/maps/labyrinth.msav new file mode 100644 index 0000000000..e1d51492aa Binary files /dev/null and b/core/assets/maps/labyrinth.msav differ diff --git a/core/assets/maps/maps.json b/core/assets/maps/maps.json deleted file mode 100644 index 6150de5f22..0000000000 --- a/core/assets/maps/maps.json +++ /dev/null @@ -1,95 +0,0 @@ -{ - "maps": [ - { - "id": 0, - "name": "maze", - "visible": true, - "flipBase": false, - "backgroundColor": "646464" - }, - { - "id": 1, - "name": "fortress", - "visible": true, - "flipBase": false, - "backgroundColor": "646464" - }, - { - "id": 2, - "name": "sinkhole", - "visible": true, - "flipBase": false, - "backgroundColor": "646464" - }, - { - "id": 3, - "name": "caves", - "visible": true, - "flipBase": false, - "backgroundColor": "646464" - }, - { - "id": 4, - "name": "volcano", - "visible": true, - "flipBase": true, - "backgroundColor": "646464" - }, - { - "id": 5, - "name": "caldera", - "visible": true, - "flipBase": false, - "backgroundColor": "646464" - }, - { - "id": 6, - "name": "scorch", - "visible": true, - "flipBase": false, - "backgroundColor": "e5d8bb" - }, - { - "id": 7, - "name": "desert", - "visible": true, - "flipBase": false, - "backgroundColor": "646464" - }, - { - "id": 8, - "name": "island", - "visible": true, - "flipBase": false, - "backgroundColor": "e5d8bb" - }, - { - "id": 9, - "name": "grassland", - "visible": true, - "flipBase": false, - "backgroundColor": "5ab464" - }, - { - "id": 10, - "name": "tundra", - "visible": true, - "flipBase": false, - "backgroundColor": "646464" - }, - { - "id": 11, - "name": "spiral", - "visible": true, - "flipBase": false, - "backgroundColor": "f7feff" - }, - { - "id": 12, - "name": "tutorial", - "visible": false, - "flipBase": false, - "backgroundColor": "646464" - } - ] -} diff --git a/core/assets/maps/maze.msav b/core/assets/maps/maze.msav new file mode 100644 index 0000000000..775b9c83cf Binary files /dev/null and b/core/assets/maps/maze.msav differ diff --git a/core/assets/maps/maze.png b/core/assets/maps/maze.png deleted file mode 100644 index 018f94a1e0..0000000000 Binary files a/core/assets/maps/maze.png and /dev/null differ diff --git a/core/assets/maps/nuclearProductionComplex.msav b/core/assets/maps/nuclearProductionComplex.msav new file mode 100644 index 0000000000..5a48206817 Binary files /dev/null and b/core/assets/maps/nuclearProductionComplex.msav differ diff --git a/core/assets/maps/oilrush.png b/core/assets/maps/oilrush.png deleted file mode 100644 index 43d4e2f851..0000000000 Binary files a/core/assets/maps/oilrush.png and /dev/null differ diff --git a/core/assets/maps/overgrowth.msav b/core/assets/maps/overgrowth.msav new file mode 100644 index 0000000000..f45ab6436b Binary files /dev/null and b/core/assets/maps/overgrowth.msav differ diff --git a/core/assets/maps/pit.png b/core/assets/maps/pit.png deleted file mode 100644 index 58cc2fcc29..0000000000 Binary files a/core/assets/maps/pit.png and /dev/null differ diff --git a/core/assets/maps/ruinousShores.msav b/core/assets/maps/ruinousShores.msav new file mode 100644 index 0000000000..36ec4cc868 Binary files /dev/null and b/core/assets/maps/ruinousShores.msav differ diff --git a/core/assets/maps/saltFlats.msav b/core/assets/maps/saltFlats.msav new file mode 100644 index 0000000000..bae88026b9 Binary files /dev/null and b/core/assets/maps/saltFlats.msav differ diff --git a/core/assets/maps/saltcrags.msav b/core/assets/maps/saltcrags.msav new file mode 100644 index 0000000000..406168ec90 Binary files /dev/null and b/core/assets/maps/saltcrags.msav differ diff --git a/core/assets/maps/scorch.png b/core/assets/maps/scorch.png deleted file mode 100644 index 6ad8e95514..0000000000 Binary files a/core/assets/maps/scorch.png and /dev/null differ diff --git a/core/assets/maps/shoreline.msav b/core/assets/maps/shoreline.msav new file mode 100644 index 0000000000..b0320b4350 Binary files /dev/null and b/core/assets/maps/shoreline.msav differ diff --git a/core/assets/maps/sinkhole.png b/core/assets/maps/sinkhole.png deleted file mode 100644 index ebaffda376..0000000000 Binary files a/core/assets/maps/sinkhole.png and /dev/null differ diff --git a/core/assets/maps/spiral.png b/core/assets/maps/spiral.png deleted file mode 100644 index dfe43fcbb4..0000000000 Binary files a/core/assets/maps/spiral.png and /dev/null differ diff --git a/core/assets/maps/stainedMountains.msav b/core/assets/maps/stainedMountains.msav new file mode 100644 index 0000000000..5cd9828d85 Binary files /dev/null and b/core/assets/maps/stainedMountains.msav differ diff --git a/core/assets/maps/tarFields.msav b/core/assets/maps/tarFields.msav new file mode 100644 index 0000000000..8976acc148 Binary files /dev/null and b/core/assets/maps/tarFields.msav differ diff --git a/core/assets/maps/tendrils.msav b/core/assets/maps/tendrils.msav new file mode 100644 index 0000000000..c222bf61e2 Binary files /dev/null and b/core/assets/maps/tendrils.msav differ diff --git a/core/assets/maps/test1.png b/core/assets/maps/test1.png deleted file mode 100644 index 88ab432825..0000000000 Binary files a/core/assets/maps/test1.png and /dev/null differ diff --git a/core/assets/maps/test2.png b/core/assets/maps/test2.png deleted file mode 100644 index 0596dd6a00..0000000000 Binary files a/core/assets/maps/test2.png and /dev/null differ diff --git a/core/assets/maps/test3.png b/core/assets/maps/test3.png deleted file mode 100644 index 8209a06331..0000000000 Binary files a/core/assets/maps/test3.png and /dev/null differ diff --git a/core/assets/maps/tundra.png b/core/assets/maps/tundra.png deleted file mode 100644 index b64cc2b19f..0000000000 Binary files a/core/assets/maps/tundra.png and /dev/null differ diff --git a/core/assets/maps/tutorial.png b/core/assets/maps/tutorial.png deleted file mode 100644 index 88b8a8af04..0000000000 Binary files a/core/assets/maps/tutorial.png and /dev/null differ diff --git a/core/assets/maps/volcano.png b/core/assets/maps/volcano.png deleted file mode 100644 index dc4f6ba866..0000000000 Binary files a/core/assets/maps/volcano.png and /dev/null differ diff --git a/core/assets/music/1.mp3 b/core/assets/music/1.mp3 deleted file mode 100755 index cdbb366b48..0000000000 Binary files a/core/assets/music/1.mp3 and /dev/null differ diff --git a/core/assets/music/2.mp3 b/core/assets/music/2.mp3 deleted file mode 100755 index cab7d699eb..0000000000 Binary files a/core/assets/music/2.mp3 and /dev/null differ diff --git a/core/assets/music/3.mp3 b/core/assets/music/3.mp3 deleted file mode 100755 index f03d72bbcc..0000000000 Binary files a/core/assets/music/3.mp3 and /dev/null differ diff --git a/core/assets/music/4.mp3 b/core/assets/music/4.mp3 deleted file mode 100755 index 48c4ff24fc..0000000000 Binary files a/core/assets/music/4.mp3 and /dev/null differ diff --git a/core/assets/music/5.mp3 b/core/assets/music/5.mp3 deleted file mode 100755 index 65a5034fbf..0000000000 Binary files a/core/assets/music/5.mp3 and /dev/null differ diff --git a/core/assets/music/6.mp3 b/core/assets/music/6.mp3 deleted file mode 100755 index 88f04f33a8..0000000000 Binary files a/core/assets/music/6.mp3 and /dev/null differ diff --git a/core/assets/shaders/blockbuild.fragment.glsl b/core/assets/shaders/blockbuild.fragment.glsl new file mode 100644 index 0000000000..858018291f --- /dev/null +++ b/core/assets/shaders/blockbuild.fragment.glsl @@ -0,0 +1,54 @@ +#ifdef GL_ES +precision mediump float; +precision mediump int; +#endif + +uniform sampler2D u_texture; + +uniform vec4 u_color; +uniform vec2 u_texsize; +uniform vec2 u_uv; +uniform vec2 u_uv2; +uniform float u_progress; +uniform float u_time; + +varying vec4 v_color; +varying vec2 v_texCoord; + + +bool id(vec2 coords, vec4 base){ + vec4 target = texture2D(u_texture, coords); + return target.a < 0.1 || (coords.x < u_uv.x || coords.y < u_uv.y || coords.x > u_uv2.x || coords.y > u_uv2.y); +} + +bool cont(vec2 T, vec2 v){ + const float step = 3.0; + vec4 base = texture2D(u_texture, T); + return base.a > 0.1 && + (id(T + vec2(0, step) * v, base) || id(T + vec2(0, -step) * v, base) || + id(T + vec2(step, 0) * v, base) || id(T + vec2(-step, 0) * v, base)); +} + +void main() { + + vec2 t = v_texCoord.xy; + + vec2 v = vec2(1.0/u_texsize.x, 1.0/u_texsize.y); + vec2 coords = (v_texCoord-u_uv) / v; + float value = coords.x + coords.y; + + vec4 color = texture2D(u_texture, t); + + vec2 center = ((u_uv + u_uv2)/2.0 - u_uv) /v; + float dst = (abs(center.x - coords.x) + abs(center.y - coords.y))/2.0; + + if((mod(u_time / 1.5 + value, 20.0) < 15.0 && cont(t, v))){ + gl_FragColor = u_color; + }else if(dst > (1.0-u_progress) * (center.x)){ + gl_FragColor = color; + }else if((dst + 1.0 > (1.0-u_progress) * (center.x)) && color.a > 0.1){ + gl_FragColor = u_color; + }else{ + gl_FragColor = vec4(0.0); + } +} diff --git a/core/assets/shaders/default.vertex b/core/assets/shaders/default.vertex.glsl similarity index 100% rename from core/assets/shaders/default.vertex rename to core/assets/shaders/default.vertex.glsl diff --git a/core/assets/shaders/fog.fragment.glsl b/core/assets/shaders/fog.fragment.glsl new file mode 100644 index 0000000000..5c07ae1e02 --- /dev/null +++ b/core/assets/shaders/fog.fragment.glsl @@ -0,0 +1,14 @@ +#ifdef GL_ES +precision mediump float; +precision mediump int; +#endif + +uniform sampler2D u_texture; + +varying vec4 v_color; +varying vec2 v_texCoord; + +void main(){ + vec4 color = texture2D(u_texture, v_texCoord.xy); + gl_FragColor = vec4(0.0, 0.0, 0.0, 1.0 - color.r); +} diff --git a/core/assets/shaders/menu.fragment.glsl b/core/assets/shaders/menu.fragment.glsl new file mode 100644 index 0000000000..1d793559ca --- /dev/null +++ b/core/assets/shaders/menu.fragment.glsl @@ -0,0 +1,44 @@ +#ifdef GL_ES +precision mediump float; +precision mediump int; +#endif + +#define p1 vec3(255.0,211.0,127.0)/255.0 +#define p2 vec3(234.0,182.0,120.0)/255.0 +#define p3 vec3(212.0,129.0,107.0)/255.0 +#define p4 vec3(142.0,77.0,72.0)/255.0 +#define roundm 0.2 + +uniform sampler2D u_texture; +uniform vec2 u_resolution; +uniform int u_time; +uniform vec2 u_uv; +uniform vec2 u_uv2; +uniform float u_scl; + +varying vec4 v_color; +varying vec2 v_texCoord; + +void main() { + ivec2 coords = ivec2((gl_FragCoord.xy - u_resolution/2.0)/u_scl); + + //int roundx = 8; + //int roundy = roundx; + + //coords.x = (coords.x / roundx) * roundx; + //coords.y = (coords.y / roundy) * roundy; + + float d = (abs(float(coords.x)) - abs(float(coords.y))); + + float m = abs(sin(-float(u_time)/50.0 + d/120.0)); + + if(m > 0.95) gl_FragColor.rgb = p1; + else if(m > 0.75) gl_FragColor.rgb = p2; + else if(m > 0.55) gl_FragColor.rgb = p3; + else if(m > 0.35) gl_FragColor.rgb = p4; + else gl_FragColor.rgb = vec3(0.0); + + gl_FragColor.rgb *= 0.5; + + gl_FragColor.a = mod(abs(float(coords.x)) + abs(float(coords.y)), 110.0) < 35.0 ? 1.0 : 0.0; +} \ No newline at end of file diff --git a/core/assets/shaders/outline.fragment b/core/assets/shaders/outline.fragment deleted file mode 100644 index 6a43f1d4a6..0000000000 --- a/core/assets/shaders/outline.fragment +++ /dev/null @@ -1,44 +0,0 @@ -#ifdef GL_ES -precision mediump float; -precision mediump int; -#endif - -uniform sampler2D u_texture; - -uniform vec4 u_color; -uniform vec2 u_texsize; -uniform float u_lighten; - -varying vec4 v_color; -varying vec2 v_texCoord; - -bool id(vec4 v){ - return v.a > 0.1 && !(v.r < 0.01 && v.g < 0.01 && v.b < 0.01); -} - -void main() { - - vec2 T = v_texCoord.xy; - - vec2 v = vec2(1.0/u_texsize.x, 1.0/u_texsize.y); - - bool any = false; - - float step = 1.0; - - vec4 c = texture2D(u_texture, T); - - if(texture2D(u_texture, T).a < 0.1 && - (id(texture2D(u_texture, T + vec2(0, step) * v)) || id(texture2D(u_texture, T + vec2(0, -step) * v)) || - id(texture2D(u_texture, T + vec2(step, 0) * v)) || id(texture2D(u_texture, T + vec2(-step, 0) * v)))) - any = true; - - if(any){ - gl_FragColor = u_color; - }else{ - if((c.r < 0.01 && c.g < 0.01 && c.b < 0.01)){ - c = vec4(0.0); - } - gl_FragColor = mix(c, vec4(1.0, 1.0, 1.0, c.a), u_lighten) * v_color; - } -} diff --git a/core/assets/shaders/pattern.fragment b/core/assets/shaders/pattern.fragment deleted file mode 100644 index 4a25d505b3..0000000000 --- a/core/assets/shaders/pattern.fragment +++ /dev/null @@ -1,60 +0,0 @@ -#ifdef GL_ES -precision mediump float; -precision mediump int; -#endif - -uniform sampler2D u_texture; - -uniform vec4 u_color; -uniform vec2 u_texsize; -uniform float u_time; -uniform vec2 u_offset; - -varying vec4 v_color; -varying vec2 v_texCoord; - -void main() { - - vec2 T = v_texCoord.xy; - vec2 coords = (T * u_texsize) + u_offset; - - float si = 1.0 + sin(u_time / 20.0 /*+ (coords.x + coords.y) / 30.0*/) / 8.0; - - vec4 color = texture2D(u_texture, T) * vec4(si, si, si, 1.0); - - vec2 v = vec2(1.0/u_texsize.x, 1.0/u_texsize.y); - - bool any = false; - - float thickness = 1.0; - float step = 1.0; - - if(texture2D(u_texture, T).a < 0.1 && - (texture2D(u_texture, T + vec2(0, step) * v).a > 0.1 || texture2D(u_texture, T + vec2(0, -step) * v).a > 0.1 || - texture2D(u_texture, T + vec2(step, 0) * v).a > 0.1 || texture2D(u_texture, T + vec2(-step, 0) * v).a > 0.1)) - any = true; - - if(any){ - gl_FragColor = u_color * vec4(si, si, si, 1.0); - }else{ - - //coords.x = float(int(coords.x)); - if(color.a > 0.1){ - float x = coords.x; - float y = coords.y; - float time = u_time; - float w = 1.0; - float h = 1.0; - float f1 = sin(2.0*time+(y/4.0*cos(time/3.0)+(x/2.0)-w/4.0)*((y/3.0)-h/4.0)/w); - float f2 = -2.0*cos(11.0*time/9.0-11.0*pow(y, x)/9.0); - - color.r = (f2 + f1) / 4.0*abs(cos(2.0*(x-y)/w + time)); - color.g = (f2 + f1) /(3.0 + color.r); - color.b = (f2 + f1) /(2.5 + color.g); - - - } - - gl_FragColor = color; - } -} diff --git a/core/assets/shaders/shadow.fragment.glsl b/core/assets/shaders/shadow.fragment.glsl new file mode 100644 index 0000000000..7346d78142 --- /dev/null +++ b/core/assets/shaders/shadow.fragment.glsl @@ -0,0 +1,44 @@ +#ifdef GL_ES +precision mediump float; +precision mediump int; +#endif + +#define SPACE 2.0 + +uniform sampler2D u_texture; + +uniform vec4 u_color; +uniform vec2 u_texsize; +uniform float u_scl; + +varying vec4 v_color; +varying vec2 v_texCoord; + +void main() { + vec2 v = vec2(1.0/u_texsize.x, 1.0/u_texsize.y); + + vec4 c = texture2D(u_texture, v_texCoord.xy); + float spacing = SPACE * u_scl; + + gl_FragColor = mix(vec4(0.0, 0.0, 0.0, min(c.a, u_color.a)), u_color, + (1.0-step(0.001, texture2D(u_texture, v_texCoord.xy).a)) * + step(0.001, + //cardinals + texture2D(u_texture, v_texCoord.xy + vec2(0, spacing) * v).a + + texture2D(u_texture, v_texCoord.xy + vec2(0, -spacing) * v).a + + texture2D(u_texture, v_texCoord.xy + vec2(spacing, 0) * v).a + + texture2D(u_texture, v_texCoord.xy + vec2(-spacing, 0) * v).a + + + //cardinal edges + texture2D(u_texture, v_texCoord.xy + vec2(spacing, spacing) * v).a + + texture2D(u_texture, v_texCoord.xy + vec2(spacing, -spacing) * v).a + + texture2D(u_texture, v_texCoord.xy + vec2(-spacing, spacing) * v).a + + texture2D(u_texture, v_texCoord.xy + vec2(-spacing, -spacing) * v).a + + + //cardinals * 2 + texture2D(u_texture, v_texCoord.xy + vec2(0, spacing) * v*2.0).a + + texture2D(u_texture, v_texCoord.xy + vec2(0, -spacing) * v*2.0).a + + texture2D(u_texture, v_texCoord.xy + vec2(spacing, 0) * v*2.0).a + + texture2D(u_texture, v_texCoord.xy + vec2(-spacing, 0) * v*2.0).a + )); +} \ No newline at end of file diff --git a/core/assets/shaders/shield.fragment b/core/assets/shaders/shield.fragment deleted file mode 100644 index ba57b130ef..0000000000 --- a/core/assets/shaders/shield.fragment +++ /dev/null @@ -1,78 +0,0 @@ -#ifdef GL_ES -precision highp float; -precision highp int; -#endif - -#define MAX_HITS 64 -#define HIT_RADIUS 12.0 -#define ALPHA 0.18 - -uniform sampler2D u_texture; - -uniform vec4 u_color; -uniform vec2 u_texsize; -uniform float u_time; -uniform float u_scaling; -uniform float u_dp; -uniform vec2 u_offset; -uniform int u_hitamount; -uniform vec3 u_hits[MAX_HITS]; - -varying vec4 v_color; -varying vec2 v_texCoord; - -float round(float f){ - return float(int(f)); -} - -void main() { - - vec2 T = v_texCoord.xy; - - vec2 coords = (T * u_texsize) + u_offset; - - T += vec2(sin(coords.y / 3.0 + u_time / 20.0) / 240.0, sin(coords.x / 3.0 + u_time / 20.0) / 240.0) * u_scaling; - - float si = 1.0 + sin(u_time / 20.0) / 8.0; - - vec4 color = texture2D(u_texture, T) * vec4(si, si, si, 1.0); - - vec2 v = vec2(1.0/u_texsize.x, 1.0/u_texsize.y); - - bool any = false; - - float thickness = 1.0; - float step = 1.5; - - if(texture2D(u_texture, T).a < 0.1 && - (texture2D(u_texture, T + vec2(0, step) * v).a > 0.1 || texture2D(u_texture, T + vec2(0, -step) * v).a > 0.1 || - texture2D(u_texture, T + vec2(step, 0) * v).a > 0.1 || texture2D(u_texture, T + vec2(-step, 0) * v).a > 0.1)) - any = true; - - if(any){ - gl_FragColor = u_color * vec4(si, si, si, 1.0); - }else{ - - if(color.a > 0.1){ - if(mod(coords.x / u_dp + coords.y / u_dp + sin(round(coords.x / u_dp) / 5.0) * 3.0 + sin(round(coords.y / u_dp) / 5.0) * 3.0 + u_time / 4.0, 10.0) < 2.0){ - color *= 1.65; - } - - color.a = ALPHA; - - for(int i = 0; i < MAX_HITS; i ++){ - if(i >= u_hitamount) break; - vec3 hit = u_hits[i]; - float rad = hit.z * HIT_RADIUS; - float fin = 1.0 - hit.z; - - if(abs(distance(vec2(hit.x, hit.y), coords - u_texsize/2.0) - rad) < 1.0){ - color = mix(color, u_color* vec4(si, si, si, 1.0), (1.0 * fin)); - color.a = ALPHA + 0.82 *fin; - } - } - } - - gl_FragColor = color; - } -} diff --git a/core/assets/shaders/shield.fragment.glsl b/core/assets/shaders/shield.fragment.glsl new file mode 100644 index 0000000000..cf8b6a7132 --- /dev/null +++ b/core/assets/shaders/shield.fragment.glsl @@ -0,0 +1,49 @@ +#ifdef GL_ES +precision highp float; +precision highp int; +#endif + +#define MAX_HITS 64 +#define HIT_RADIUS 12.0 +#define ALPHA 0.18 +#define thickness 1.0 +#define step 2.0 + +uniform sampler2D u_texture; +uniform vec2 u_texsize; +uniform float u_time; +uniform float u_dp; +uniform vec2 u_offset; + +varying vec4 v_color; +varying vec2 v_texCoord; + +void main() { + + vec2 T = v_texCoord.xy; + vec2 coords = (T * u_texsize) + u_offset; + + T += vec2(sin(coords.y / 3.0 + u_time / 20.0), sin(coords.x / 3.0 + u_time / 20.0)) / u_texsize; + + float si = sin(u_time / 20.0) / 8.0; + vec4 color = texture2D(u_texture, T); + vec2 v = vec2(1.0/u_texsize.x, 1.0/u_texsize.y); + + if(texture2D(u_texture, T).a < 0.1 && + (texture2D(u_texture, T + vec2(0, step) * v).a > 0.1 || texture2D(u_texture, T + vec2(0, -step) * v).a > 0.1 || + texture2D(u_texture, T + vec2(step, 0) * v).a > 0.1 || texture2D(u_texture, T + vec2(-step, 0) * v).a > 0.1)){ + + gl_FragColor = mix(v_color, vec4(1.0), si); + }else{ + + if(color.a > 0.1){ + if(mod(coords.x / u_dp + coords.y / u_dp + sin(floor(coords.x / u_dp) / 5.0) * 3.0 + sin(floor(coords.y / u_dp) / 5.0) * 3.0 + u_time / 4.0, 10.0) < 2.0){ + color *= 1.65; + } + + color.a = ALPHA; + } + + gl_FragColor = color; + } +} diff --git a/core/assets/shaders/tar.fragment.glsl b/core/assets/shaders/tar.fragment.glsl new file mode 100644 index 0000000000..bdbd49c4b3 --- /dev/null +++ b/core/assets/shaders/tar.fragment.glsl @@ -0,0 +1,71 @@ +#ifdef GL_ES +precision highp float; +precision mediump int; +#endif + +uniform sampler2D u_texture; + +uniform vec2 camerapos; +uniform vec2 screensize; +uniform float time; + +varying vec4 v_color; +varying vec2 v_texCoord; + +vec3 permute(vec3 x) { return mod(((x*34.0)+1.0)*x, 289.0); } + +float snoise(vec2 v){ + const vec4 C = vec4(0.211324865405187, 0.366025403784439, + -0.577350269189626, 0.024390243902439); + vec2 i = floor(v + dot(v, C.yy) ); + vec2 x0 = v - i + dot(i, C.xx); + vec2 i1; + i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0); + vec4 x12 = x0.xyxy + C.xxzz; + x12.xy -= i1; + i = mod(i, 289.0); + vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 )) + + i.x + vec3(0.0, i1.x, 1.0 )); + vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), + dot(x12.zw,x12.zw)), 0.0); + m = m*m ; + m = m*m ; + vec3 x = 2.0 * fract(p * C.www) - 1.0; + vec3 h = abs(x) - 0.5; + vec3 ox = floor(x + 0.5); + vec3 a0 = x - ox; + m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h ); + vec3 g; + g.x = a0.x * x0.x + h.x * x0.y; + g.yz = a0.yz * x12.xz + h.yz * x12.yw; + return 130.0 * dot(m, g); +} + +void main() { + + vec2 c = v_texCoord.xy; + vec4 color = texture2D(u_texture, c); + + vec2 v = vec2(1.0/screensize.x, 1.0/screensize.y); + vec2 coords = vec2(c.x / v.x + camerapos.x, c.y / v.y + camerapos.y); + + float stime = time / 5.0; + + float mscl = 30.0; + float mth = 5.0; + + if(color.r > 0.01){ + color = texture2D(u_texture, c + vec2(sin(stime/3.0 + coords.y/0.75) * v.x, 0.0)) * vec4(0.9, 0.9, 1, 1.0); + + float n1 = snoise(coords / 22.0 + vec2(-time) / 540.0); + float n2 = snoise((coords + vec2(632.0)) / 8.0 + vec2(0.0, time) / 510.0); + + float r = (n1 + n2) / 2.0; + + if(r < -0.3 && r > -0.6){ + color *= 1.4; + } + } + + gl_FragColor = color; +} \ No newline at end of file diff --git a/core/assets/shaders/unitbuild.fragment.glsl b/core/assets/shaders/unitbuild.fragment.glsl new file mode 100644 index 0000000000..eff888a827 --- /dev/null +++ b/core/assets/shaders/unitbuild.fragment.glsl @@ -0,0 +1,61 @@ +#ifdef GL_ES +precision mediump float; +precision mediump int; +#endif + +#define step 3.0 + +uniform sampler2D u_texture; + +uniform float u_time; +uniform float u_progress; +uniform vec4 u_color; +uniform vec2 u_uv; +uniform vec2 u_uv2; +uniform vec2 u_texsize; + +varying vec4 v_color; +varying vec2 v_texCoord; + +bool id(vec4 v){ + return v.a > 0.1; +} + +bool id(vec2 coords, vec4 base){ + vec4 target = texture2D(u_texture, coords); + return target.a < 0.1 || (coords.x < u_uv.x || coords.y < u_uv.y || coords.x > u_uv2.x || coords.y > u_uv2.y); +} + +bool cont(vec2 T, vec2 v){ + vec4 base = texture2D(u_texture, T); + return base.a > 0.1 && + (id(T + vec2(0, step) * v, base) || id(T + vec2(0, -step) * v, base) || + id(T + vec2(step, 0) * v, base) || id(T + vec2(-step, 0) * v, base) || + id(T + vec2(step, step) * v, base) || id(T + vec2(-step, -step) * v, base) || + id(T + vec2(step, -step) * v, base) || id(T + vec2(-step, step) * v, base)); +} + +void main() { + vec2 coords = (v_texCoord.xy - u_uv) / (u_uv2 - u_uv); + vec2 t = v_texCoord.xy; + vec2 v = vec2(1.0/u_texsize.x, 1.0/u_texsize.y); + + vec4 c = texture2D(u_texture, v_texCoord.xy); + + if(1.0-abs(coords.x - 0.5)*2.0 < 1.0-u_progress){ + // c = vec4(0.0); + } + + c.a *= u_progress; + + if(c.a > 0.01){ + float f = abs(sin(coords.x*2.0 + u_time)); + if(f > 0.9) + f = 1.0; + else + f = 0.0; + c = mix(c, u_color, f * u_color.a); + } + + gl_FragColor = c * v_color; +} diff --git a/core/assets/shaders/water.fragment.glsl b/core/assets/shaders/water.fragment.glsl new file mode 100644 index 0000000000..bd3e2103e6 --- /dev/null +++ b/core/assets/shaders/water.fragment.glsl @@ -0,0 +1,77 @@ +#ifdef GL_ES +precision highp float; +precision mediump int; +#endif + +uniform sampler2D u_texture; + +uniform vec2 camerapos; +uniform vec2 screensize; +uniform float time; + +varying vec4 v_color; +varying vec2 v_texCoord; + +vec3 permute(vec3 x) { return mod(((x*34.0)+1.0)*x, 289.0); } + +float snoise(vec2 v){ + const vec4 C = vec4(0.211324865405187, 0.366025403784439, + -0.577350269189626, 0.024390243902439); + vec2 i = floor(v + dot(v, C.yy) ); + vec2 x0 = v - i + dot(i, C.xx); + vec2 i1; + i1 = (x0.x > x0.y) ? vec2(1.0, 0.0) : vec2(0.0, 1.0); + vec4 x12 = x0.xyxy + C.xxzz; + x12.xy -= i1; + i = mod(i, 289.0); + vec3 p = permute( permute( i.y + vec3(0.0, i1.y, 1.0 )) + + i.x + vec3(0.0, i1.x, 1.0 )); + vec3 m = max(0.5 - vec3(dot(x0,x0), dot(x12.xy,x12.xy), + dot(x12.zw,x12.zw)), 0.0); + m = m*m ; + m = m*m ; + vec3 x = 2.0 * fract(p * C.www) - 1.0; + vec3 h = abs(x) - 0.5; + vec3 ox = floor(x + 0.5); + vec3 a0 = x - ox; + m *= 1.79284291400159 - 0.85373472095314 * ( a0*a0 + h*h ); + vec3 g; + g.x = a0.x * x0.x + h.x * x0.y; + g.yz = a0.yz * x12.xz + h.yz * x12.yw; + return 130.0 * dot(m, g); +} + +void main() { + + vec2 c = v_texCoord.xy; + vec4 color = texture2D(u_texture, c) * v_color; + + vec2 v = vec2(1.0/screensize.x, 1.0/screensize.y); + vec2 coords = vec2(c.x / v.x + camerapos.x, c.y / v.y + camerapos.y); + + float stime = time / 5.0; + + float mscl = 30.0; + float mth = 5.0; + + color = texture2D(u_texture, c + vec2(sin(stime/3.0 + coords.y/0.75) * v.x, 0.0)) * vec4(0.9, 0.9, 1, 1.0); + color.a = 1.0; + + float n1 = snoise(coords / 40.0 + vec2(time) / 200.0); + float n2 = snoise((coords + vec2(632.0)) / 25.0 + vec2(0.0, -time) / 190.0); + + float r = (n1 + n2) * 3.0; + float tester = mod(float(int(coords.x + coords.y*1.1 + sin(stime / 8.0 + coords.x/5.0 - coords.y/100.0)*2.0)) + + sin(stime / 20.0 + coords.y/3.0) * 1.0 + + sin(stime / 10.0 + coords.y/2.0) * 2.0 + + sin(stime / 7.0 + coords.y/1.0) * 0.5 + + sin(coords.x + coords.y) + + sin(stime / 20.0 + coords.x/4.0) * 1.0, mscl) + r; + + if(tester < mth){ + color *= 1.2; + color.a = 1.0; + } + + gl_FragColor = color; +} \ No newline at end of file diff --git a/core/assets/sounds/bang.mp3 b/core/assets/sounds/bang.mp3 deleted file mode 100644 index d859b89aab..0000000000 Binary files a/core/assets/sounds/bang.mp3 and /dev/null differ diff --git a/core/assets/sounds/bang2.mp3 b/core/assets/sounds/bang2.mp3 deleted file mode 100644 index 71e3deffa4..0000000000 Binary files a/core/assets/sounds/bang2.mp3 and /dev/null differ diff --git a/core/assets/sounds/bigshot.mp3 b/core/assets/sounds/bigshot.mp3 deleted file mode 100644 index 2139d15232..0000000000 Binary files a/core/assets/sounds/bigshot.mp3 and /dev/null differ diff --git a/core/assets/sounds/blast.mp3 b/core/assets/sounds/blast.mp3 deleted file mode 100644 index f05f080eae..0000000000 Binary files a/core/assets/sounds/blast.mp3 and /dev/null differ diff --git a/core/assets/sounds/bloop.mp3 b/core/assets/sounds/bloop.mp3 deleted file mode 100644 index c77f53d7fe..0000000000 Binary files a/core/assets/sounds/bloop.mp3 and /dev/null differ diff --git a/core/assets/sounds/break.mp3 b/core/assets/sounds/break.mp3 deleted file mode 100644 index 56487e4af1..0000000000 Binary files a/core/assets/sounds/break.mp3 and /dev/null differ diff --git a/core/assets/sounds/corexplode.mp3 b/core/assets/sounds/corexplode.mp3 deleted file mode 100644 index 2ee50fb405..0000000000 Binary files a/core/assets/sounds/corexplode.mp3 and /dev/null differ diff --git a/core/assets/sounds/die.mp3 b/core/assets/sounds/die.mp3 deleted file mode 100644 index ae87ef7aa9..0000000000 Binary files a/core/assets/sounds/die.mp3 and /dev/null differ diff --git a/core/assets/sounds/enemyshoot.mp3 b/core/assets/sounds/enemyshoot.mp3 deleted file mode 100644 index 7deb792726..0000000000 Binary files a/core/assets/sounds/enemyshoot.mp3 and /dev/null differ diff --git a/core/assets/sounds/explosion.mp3 b/core/assets/sounds/explosion.mp3 deleted file mode 100644 index f5f6e08fa7..0000000000 Binary files a/core/assets/sounds/explosion.mp3 and /dev/null differ diff --git a/core/assets/sounds/flame.mp3 b/core/assets/sounds/flame.mp3 deleted file mode 100644 index a49ae44245..0000000000 Binary files a/core/assets/sounds/flame.mp3 and /dev/null differ diff --git a/core/assets/sounds/flame2.mp3 b/core/assets/sounds/flame2.mp3 deleted file mode 100644 index c0d71a7b1d..0000000000 Binary files a/core/assets/sounds/flame2.mp3 and /dev/null differ diff --git a/core/assets/sounds/laser.mp3 b/core/assets/sounds/laser.mp3 deleted file mode 100644 index f0b2398aef..0000000000 Binary files a/core/assets/sounds/laser.mp3 and /dev/null differ diff --git a/core/assets/sounds/lasershot.mp3 b/core/assets/sounds/lasershot.mp3 deleted file mode 100644 index 1db62aa08a..0000000000 Binary files a/core/assets/sounds/lasershot.mp3 and /dev/null differ diff --git a/core/assets/sounds/missile.mp3 b/core/assets/sounds/missile.mp3 deleted file mode 100644 index 89af9f5157..0000000000 Binary files a/core/assets/sounds/missile.mp3 and /dev/null differ diff --git a/core/assets/sounds/ping.mp3 b/core/assets/sounds/ping.mp3 deleted file mode 100644 index b9692a37fc..0000000000 Binary files a/core/assets/sounds/ping.mp3 and /dev/null differ diff --git a/core/assets/sounds/place.mp3 b/core/assets/sounds/place.mp3 deleted file mode 100644 index 3f5369e4fa..0000000000 Binary files a/core/assets/sounds/place.mp3 and /dev/null differ diff --git a/core/assets/sounds/purchase.mp3 b/core/assets/sounds/purchase.mp3 deleted file mode 100644 index 6edc48eebd..0000000000 Binary files a/core/assets/sounds/purchase.mp3 and /dev/null differ diff --git a/core/assets/sounds/railgun.mp3 b/core/assets/sounds/railgun.mp3 deleted file mode 100644 index a9bb203a7c..0000000000 Binary files a/core/assets/sounds/railgun.mp3 and /dev/null differ diff --git a/core/assets/sounds/resonate.mp3 b/core/assets/sounds/resonate.mp3 deleted file mode 100644 index ea456660de..0000000000 Binary files a/core/assets/sounds/resonate.mp3 and /dev/null differ diff --git a/core/assets/sounds/respawn.mp3 b/core/assets/sounds/respawn.mp3 deleted file mode 100644 index 56e6fff2fc..0000000000 Binary files a/core/assets/sounds/respawn.mp3 and /dev/null differ diff --git a/core/assets/sounds/shoot.mp3 b/core/assets/sounds/shoot.mp3 deleted file mode 100644 index 4a826fdfdd..0000000000 Binary files a/core/assets/sounds/shoot.mp3 and /dev/null differ diff --git a/core/assets/sounds/shoot_old.mp3 b/core/assets/sounds/shoot_old.mp3 deleted file mode 100644 index bfe18e8806..0000000000 Binary files a/core/assets/sounds/shoot_old.mp3 and /dev/null differ diff --git a/core/assets/sounds/spawn.mp3 b/core/assets/sounds/spawn.mp3 deleted file mode 100644 index c0235b640c..0000000000 Binary files a/core/assets/sounds/spawn.mp3 and /dev/null differ diff --git a/core/assets/sounds/tesla.mp3 b/core/assets/sounds/tesla.mp3 deleted file mode 100644 index c8f053fbd3..0000000000 Binary files a/core/assets/sounds/tesla.mp3 and /dev/null differ diff --git a/core/assets/sounds/waveend.mp3 b/core/assets/sounds/waveend.mp3 deleted file mode 100644 index 60a48c0ba4..0000000000 Binary files a/core/assets/sounds/waveend.mp3 and /dev/null differ diff --git a/core/assets/sprites/background.png b/core/assets/sprites/background.png deleted file mode 100644 index ee3aa9ba6f..0000000000 Binary files a/core/assets/sprites/background.png and /dev/null differ diff --git a/core/assets/sprites/block_colors.png b/core/assets/sprites/block_colors.png new file mode 100644 index 0000000000..f70f94a150 Binary files /dev/null and b/core/assets/sprites/block_colors.png differ diff --git a/core/assets/sprites/icon.icns b/core/assets/sprites/icon.icns index a71732bda5..45b4c89b83 100644 Binary files a/core/assets/sprites/icon.icns and b/core/assets/sprites/icon.icns differ diff --git a/core/assets/sprites/icon.png b/core/assets/sprites/icon.png index c2fa937fb5..737f1c4bf7 100644 Binary files a/core/assets/sprites/icon.png and b/core/assets/sprites/icon.png differ diff --git a/core/assets/sprites/icon_64.png b/core/assets/sprites/icon_64.png new file mode 100644 index 0000000000..8e9259dc88 Binary files /dev/null and b/core/assets/sprites/icon_64.png differ diff --git a/core/assets/sprites/sprites.atlas b/core/assets/sprites/sprites.atlas index 1b4eb5e605..3c531f17a3 100644 --- a/core/assets/sprites/sprites.atlas +++ b/core/assets/sprites/sprites.atlas @@ -1,2416 +1,8643 @@ sprites.png -size: 1024,512 +size: 2048,2048 format: RGBA8888 filter: Nearest,Nearest repeat: none -background +force-projector rotate: false - xy: 1, 195 - size: 421, 316 - orig: 421, 316 + xy: 611, 1430 + size: 96, 96 + orig: 96, 96 offset: 0, 0 index: -1 -blank +force-projector-top rotate: false - xy: 791, 491 - size: 1, 1 - orig: 1, 1 + xy: 709, 1430 + size: 96, 96 + orig: 96, 96 offset: 0, 0 index: -1 -blackrock1 +mend-projector rotate: false - xy: 79, 48 - size: 8, 8 - orig: 8, 8 + xy: 687, 772 + size: 64, 64 + orig: 64, 64 offset: 0, 0 index: -1 -blackrockshadow1 +mend-projector-top rotate: false - xy: 1014, 487 - size: 8, 8 - orig: 8, 8 + xy: 555, 574 + size: 64, 64 + orig: 64, 64 offset: 0, 0 index: -1 -blackstone1 +mender rotate: false - xy: 615, 278 - size: 8, 8 - orig: 8, 8 + xy: 1877, 1360 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -blackstone2 +mender-top rotate: false - xy: 615, 268 - size: 8, 8 - orig: 8, 8 + xy: 1911, 1360 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -blackstone3 +overdrive-projector rotate: false - xy: 768, 436 - size: 8, 8 - orig: 8, 8 + xy: 687, 706 + size: 64, 64 + orig: 64, 64 offset: 0, 0 index: -1 -blackstoneblock1 +overdrive-projector-top rotate: false - xy: 780, 449 - size: 8, 8 - orig: 8, 8 + xy: 555, 508 + size: 64, 64 + orig: 64, 64 offset: 0, 0 index: -1 -blackstoneblock2 +shock-mine rotate: false - xy: 772, 426 - size: 8, 8 - orig: 8, 8 + xy: 1761, 450 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -blackstoneblock3 +bridge-arrow rotate: false - xy: 778, 436 - size: 8, 8 - orig: 8, 8 + xy: 817, 6 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -blackstoneedge +bridge-conveyor rotate: false - xy: 551, 273 - size: 12, 12 - orig: 12, 12 + xy: 2010, 1400 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -block +bridge-conveyor-arrow rotate: false - xy: 85, 38 - size: 8, 8 - orig: 8, 8 + xy: 1547, 1245 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -block-2x2 +bridge-conveyor-bridge rotate: false - xy: 473, 227 - size: 16, 16 - orig: 16, 16 + xy: 1581, 1245 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -block-3x3 +bridge-conveyor-end rotate: false - xy: 424, 198 - size: 24, 24 - orig: 24, 24 + xy: 1541, 1211 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -block-middle +center rotate: false - xy: 190, 112 - size: 8, 8 - orig: 8, 8 + xy: 1575, 1211 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -chainturret +conveyor-0-0 rotate: false - xy: 342, 157 - size: 16, 16 - orig: 16, 16 + xy: 1553, 224 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -chainturret-icon +conveyor-0-1 rotate: false - xy: 57, 48 - size: 10, 10 - orig: 10, 10 + xy: 1553, 190 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -chainturret-icon_old +conveyor-0-2 rotate: false - xy: 651, 382 - size: 10, 10 - orig: 10, 10 + xy: 1553, 156 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -chainturret_old +conveyor-0-3 rotate: false - xy: 362, 177 - size: 16, 16 - orig: 16, 16 + xy: 1553, 122 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -coal1 +conveyor-1-0 rotate: false - xy: 153, 70 - size: 8, 8 - orig: 8, 8 + xy: 1553, 88 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -coal2 +conveyor-1-1 rotate: false - xy: 85, 58 - size: 8, 8 - orig: 8, 8 + xy: 1553, 54 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -coal3 +conveyor-1-2 rotate: false - xy: 89, 48 - size: 8, 8 - orig: 8, 8 + xy: 1553, 20 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -coaldrill +conveyor-1-3 rotate: false - xy: 790, 449 - size: 8, 8 - orig: 8, 8 + xy: 1563, 1395 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -coalgenerator +conveyor-2-0 rotate: false - xy: 788, 439 - size: 8, 8 - orig: 8, 8 + xy: 1597, 1402 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -coalgenerator-top +conveyor-2-1 rotate: false - xy: 800, 452 - size: 8, 8 - orig: 8, 8 + xy: 1631, 1402 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -coalpurifier +conveyor-2-2 rotate: false - xy: 810, 453 - size: 8, 8 - orig: 8, 8 + xy: 1665, 1402 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -combustiongenerator +conveyor-2-3 rotate: false - xy: 820, 453 - size: 8, 8 - orig: 8, 8 + xy: 1699, 1402 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -compositewall +conveyor-3-0 rotate: false - xy: 830, 453 - size: 8, 8 - orig: 8, 8 + xy: 1733, 1402 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -conduit +conveyor-3-1 rotate: false - xy: 840, 453 - size: 8, 8 - orig: 8, 8 + xy: 1767, 1402 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -conduitbottom +conveyor-3-2 rotate: false - xy: 850, 453 - size: 8, 8 - orig: 8, 8 + xy: 1801, 1402 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -conduitliquid +conveyor-3-3 rotate: false - xy: 860, 453 - size: 8, 8 - orig: 8, 8 + xy: 2010, 1366 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -conduittop +conveyor-4-0 rotate: false - xy: 95, 58 - size: 8, 8 - orig: 8, 8 + xy: 1643, 1211 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -conveyor +conveyor-4-1 rotate: false - xy: 99, 48 - size: 8, 8 - orig: 8, 8 + xy: 1643, 1177 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -conveyormove +conveyor-4-2 rotate: false - xy: 95, 38 - size: 8, 8 - orig: 8, 8 + xy: 1643, 1143 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -conveyortunnel +conveyor-4-3 rotate: false - xy: 870, 453 - size: 8, 8 - orig: 8, 8 + xy: 1643, 1109 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -core +titanium-conveyor-0-0 rotate: false - xy: 264, 169 - size: 24, 24 - orig: 24, 24 + xy: 1915, 1122 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -cross +titanium-conveyor-0-1 rotate: false - xy: 880, 455 - size: 8, 8 - orig: 8, 8 + xy: 1915, 1088 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -crucible +titanium-conveyor-0-2 rotate: false - xy: 105, 58 - size: 8, 8 - orig: 8, 8 + xy: 1813, 1054 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -deepwater +titanium-conveyor-0-3 rotate: false - xy: 109, 48 - size: 8, 8 - orig: 8, 8 + xy: 1847, 1054 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -dirt1 +titanium-conveyor-1-0 rotate: false - xy: 105, 38 - size: 8, 8 - orig: 8, 8 + xy: 1881, 1054 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -dirt2 +titanium-conveyor-1-1 rotate: false - xy: 115, 58 - size: 8, 8 - orig: 8, 8 + xy: 1915, 1054 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -dirt3 +titanium-conveyor-1-2 rotate: false - xy: 119, 48 - size: 8, 8 - orig: 8, 8 + xy: 1807, 1020 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -dirtedge +titanium-conveyor-1-3 rotate: false - xy: 722, 442 - size: 12, 12 - orig: 12, 12 + xy: 1841, 1020 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -door +titanium-conveyor-2-0 rotate: false - xy: 115, 38 - size: 8, 8 - orig: 8, 8 + xy: 1875, 1020 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -door-large +titanium-conveyor-2-1 rotate: false - xy: 491, 227 - size: 16, 16 - orig: 16, 16 + xy: 1909, 1020 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -door-large-icon +titanium-conveyor-2-2 rotate: false - xy: 125, 58 - size: 8, 8 - orig: 8, 8 + xy: 1803, 986 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -door-large-open +titanium-conveyor-2-3 rotate: false - xy: 380, 177 - size: 16, 16 - orig: 16, 16 + xy: 1803, 952 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -door-open +titanium-conveyor-3-0 rotate: false - xy: 129, 48 - size: 8, 8 - orig: 8, 8 + xy: 1837, 986 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -doubleturret +titanium-conveyor-3-1 rotate: false - xy: 675, 382 - size: 10, 10 - orig: 10, 10 + xy: 1803, 918 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -duriumwall +titanium-conveyor-3-2 rotate: false - xy: 125, 38 - size: 8, 8 - orig: 8, 8 + xy: 1837, 952 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -duriumwall-large +titanium-conveyor-3-3 rotate: false - xy: 828, 495 - size: 16, 16 - orig: 16, 16 + xy: 1871, 986 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -duriumwall-large-icon +titanium-conveyor-4-0 rotate: false - xy: 135, 58 - size: 8, 8 - orig: 8, 8 + xy: 1803, 884 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -enemyspawn +titanium-conveyor-4-1 rotate: false - xy: 145, 58 - size: 8, 8 - orig: 8, 8 + xy: 1837, 918 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -flameturret +titanium-conveyor-4-2 rotate: false - xy: 669, 370 - size: 10, 10 - orig: 10, 10 + xy: 1871, 952 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -fluxpump +titanium-conveyor-4-3 rotate: false - xy: 139, 48 - size: 8, 8 - orig: 8, 8 + xy: 1905, 986 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -grass1 +distributor rotate: false - xy: 135, 38 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -grass2 - rotate: false - xy: 155, 60 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -grass3 - rotate: false - xy: 149, 48 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -grassblock1 - rotate: false - xy: 145, 38 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -grassblock2 - rotate: false - xy: 159, 50 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -grassedge - rotate: false - xy: 194, 124 - size: 12, 12 - orig: 12, 12 - offset: 0, 0 - index: -1 -ice1 - rotate: false - xy: 155, 38 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -ice2 - rotate: false - xy: 432, 172 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -ice3 - rotate: false - xy: 442, 172 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -iceedge - rotate: false - xy: 328, 129 - size: 12, 12 - orig: 12, 12 - offset: 0, 0 - index: -1 -icerock1 - rotate: false - xy: 792, 429 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -icerock2 - rotate: false - xy: 798, 439 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -icerockshadow1 - rotate: false - xy: 802, 429 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -rockshadow1 - rotate: false - xy: 802, 429 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -icerockshadow2 - rotate: false - xy: 450, 214 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -rockshadow2 - rotate: false - xy: 450, 214 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -iron1 - rotate: false - xy: 462, 174 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -iron2 - rotate: false - xy: 792, 419 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -iron3 - rotate: false - xy: 802, 419 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -irondrill - rotate: false - xy: 470, 215 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -ironwall - rotate: false - xy: 470, 205 - size: 8, 8 - orig: 8, 8 + xy: 630, 1168 + size: 64, 64 + orig: 64, 64 offset: 0, 0 index: -1 junction rotate: false - xy: 470, 195 - size: 8, 8 - orig: 8, 8 + xy: 1757, 55 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -laserturret +mass-driver-base rotate: false - xy: 687, 310 - size: 10, 10 - orig: 10, 10 + xy: 293, 517 + size: 96, 96 + orig: 96, 96 offset: 0, 0 index: -1 -lava +overflow-gate rotate: false - xy: 468, 185 - size: 8, 8 - orig: 8, 8 + xy: 1683, 1266 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -lavaedge +phase-conveyor rotate: false - xy: 874, 465 - size: 12, 12 - orig: 12, 12 + xy: 1711, 1232 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -lavasmelter +phase-conveyor-arrow rotate: false - xy: 480, 217 - size: 8, 8 - orig: 8, 8 + xy: 1677, 1164 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -liquiditemjunction +phase-conveyor-bridge rotate: false - xy: 480, 207 - size: 8, 8 - orig: 8, 8 + xy: 1745, 1232 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -liquidjunction +phase-conveyor-end rotate: false - xy: 490, 217 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -liquidrouter - rotate: false - xy: 480, 197 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -machineturret - rotate: false - xy: 687, 298 - size: 10, 10 - orig: 10, 10 - offset: 0, 0 - index: -1 -megarepairturret - rotate: false - xy: 683, 414 - size: 10, 10 - orig: 10, 10 - offset: 0, 0 - index: -1 -mortarturret - rotate: false - xy: 683, 402 - size: 10, 10 - orig: 10, 10 - offset: 0, 0 - index: -1 -mossblock - rotate: false - xy: 490, 207 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -mossstone - rotate: false - xy: 490, 207 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -nuclearreactor - rotate: false - xy: 264, 143 - size: 24, 24 - orig: 24, 24 - offset: 0, 0 - index: -1 -nuclearreactor-center - rotate: false - xy: 290, 169 - size: 24, 24 - orig: 24, 24 - offset: 0, 0 - index: -1 -nuclearreactor-icon - rotate: false - xy: 490, 197 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -nuclearreactor-lights - rotate: false - xy: 290, 143 - size: 24, 24 - orig: 24, 24 - offset: 0, 0 - index: -1 -nuclearreactor-small - rotate: false - xy: 87, 76 - size: 16, 16 - orig: 16, 16 - offset: 0, 0 - index: -1 -oil - rotate: false - xy: 472, 175 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -oiledge - rotate: false - xy: 918, 481 - size: 12, 12 - orig: 12, 12 - offset: 0, 0 - index: -1 -oilrefinery - rotate: false - xy: 478, 185 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -omnidrill - rotate: false - xy: 488, 187 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -plasmaturret - rotate: false - xy: 141, 68 - size: 10, 10 - orig: 10, 10 - offset: 0, 0 - index: -1 -playerspawn - rotate: false - xy: 482, 175 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -powerbooster - rotate: false - xy: 492, 177 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -poweredconveyor - rotate: false - xy: 498, 187 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -poweredconveyormove - rotate: false - xy: 502, 177 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -powerlaser - rotate: false - xy: 358, 147 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -powerlasercorner - rotate: false - xy: 358, 137 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -powerlaserrouter - rotate: false - xy: 358, 127 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -pulseconduit - rotate: false - xy: 688, 438 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -pulseconduitbottom - rotate: false - xy: 688, 428 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -pulseconduittop - rotate: false - xy: 657, 291 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -pump - rotate: false - xy: 567, 262 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -repairturret - rotate: false - xy: 603, 276 - size: 10, 10 - orig: 10, 10 - offset: 0, 0 - index: -1 -rock1 - rotate: false - xy: 577, 262 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -rock2 - rotate: false - xy: 587, 262 - size: 8, 8 - orig: 8, 8 + xy: 1711, 1198 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 router rotate: false - xy: 569, 252 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -rtgenerator - rotate: false - xy: 569, 242 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -rtgenerator-top - rotate: false - xy: 579, 252 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -sand1 - rotate: false - xy: 569, 232 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -sand2 - rotate: false - xy: 579, 242 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -sand3 - rotate: false - xy: 569, 222 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -sandblock1 - rotate: false - xy: 579, 232 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -sandblock2 - rotate: false - xy: 569, 212 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -sandblock3 - rotate: false - xy: 579, 222 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -sandedge - rotate: false - xy: 932, 481 - size: 12, 12 - orig: 12, 12 - offset: 0, 0 - index: -1 -shadow - rotate: false - xy: 625, 273 - size: 10, 10 - orig: 10, 10 - offset: 0, 0 - index: -1 -shieldgenerator - rotate: false - xy: 589, 252 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -shotgunturret - rotate: false - xy: 637, 273 - size: 10, 10 - orig: 10, 10 - offset: 0, 0 - index: -1 -shrub - rotate: false - xy: 597, 262 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -shrubshadow - rotate: false - xy: 599, 252 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -smelter - rotate: false - xy: 599, 242 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -smelter-middle - rotate: false - xy: 599, 232 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -sniperturret - rotate: false - xy: 649, 273 - size: 10, 10 - orig: 10, 10 - offset: 0, 0 - index: -1 -snow1 - rotate: false - xy: 599, 222 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -snow2 - rotate: false - xy: 599, 212 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -snow3 - rotate: false - xy: 569, 202 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -snowblock1 - rotate: false - xy: 579, 202 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -snowblock2 - rotate: false - xy: 589, 202 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -snowblock3 - rotate: false - xy: 599, 202 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -snowedge - rotate: false - xy: 960, 481 - size: 12, 12 - orig: 12, 12 + xy: 1735, 824 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 sorter rotate: false - xy: 65, 16 - size: 8, 8 - orig: 8, 8 + xy: 1813, 1224 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -steelconveyor +blast-drill rotate: false - xy: 75, 16 - size: 8, 8 - orig: 8, 8 + xy: 1360, 1918 + size: 128, 128 + orig: 128, 128 offset: 0, 0 index: -1 -steelconveyormove +blast-drill-rim rotate: false - xy: 69, 6 - size: 8, 8 - orig: 8, 8 + xy: 1620, 1918 + size: 128, 128 + orig: 128, 128 offset: 0, 0 index: -1 -steelwall +blast-drill-rotator rotate: false - xy: 79, 6 - size: 8, 8 - orig: 8, 8 + xy: 1750, 1918 + size: 128, 128 + orig: 128, 128 offset: 0, 0 index: -1 -steelwall-large +blast-drill-top rotate: false - xy: 105, 94 - size: 16, 16 - orig: 16, 16 + xy: 1880, 1918 + size: 128, 128 + orig: 128, 128 offset: 0, 0 index: -1 -steelwall-large-icon +laser-drill rotate: false - xy: 83, 26 - size: 8, 8 - orig: 8, 8 + xy: 293, 1105 + size: 96, 96 + orig: 96, 96 offset: 0, 0 index: -1 -stone1 +laser-drill-rim rotate: false - xy: 85, 16 - size: 8, 8 - orig: 8, 8 + xy: 391, 1105 + size: 96, 96 + orig: 96, 96 offset: 0, 0 index: -1 -stone2 +laser-drill-rotator rotate: false - xy: 93, 28 - size: 8, 8 - orig: 8, 8 + xy: 293, 909 + size: 96, 96 + orig: 96, 96 offset: 0, 0 index: -1 -stone3 +laser-drill-top rotate: false - xy: 103, 28 - size: 8, 8 - orig: 8, 8 + xy: 391, 1007 + size: 96, 96 + orig: 96, 96 offset: 0, 0 index: -1 -stoneblock1 +mechanical-drill rotate: false - xy: 113, 28 - size: 8, 8 - orig: 8, 8 + xy: 621, 772 + size: 64, 64 + orig: 64, 64 offset: 0, 0 index: -1 -stoneblock2 +mechanical-drill-rotator rotate: false - xy: 123, 28 - size: 8, 8 - orig: 8, 8 + xy: 555, 640 + size: 64, 64 + orig: 64, 64 offset: 0, 0 index: -1 -stoneblock3 +mechanical-drill-top rotate: false - xy: 133, 28 - size: 8, 8 - orig: 8, 8 + xy: 621, 706 + size: 64, 64 + orig: 64, 64 offset: 0, 0 index: -1 -stonedrill +oil-extractor rotate: false - xy: 143, 28 - size: 8, 8 - orig: 8, 8 + xy: 391, 517 + size: 96, 96 + orig: 96, 96 offset: 0, 0 index: -1 -stoneedge +oil-extractor-liquid rotate: false - xy: 674, 426 - size: 12, 12 - orig: 12, 12 + xy: 391, 419 + size: 96, 96 + orig: 96, 96 offset: 0, 0 index: -1 -stoneformer +oil-extractor-rotator rotate: false - xy: 153, 28 - size: 8, 8 - orig: 8, 8 + xy: 293, 223 + size: 96, 96 + orig: 96, 96 offset: 0, 0 index: -1 -stonewall +oil-extractor-top rotate: false - xy: 95, 18 - size: 8, 8 - orig: 8, 8 + xy: 391, 321 + size: 96, 96 + orig: 96, 96 offset: 0, 0 index: -1 -teleporter +pneumatic-drill rotate: false - xy: 105, 18 - size: 8, 8 - orig: 8, 8 + xy: 621, 376 + size: 64, 64 + orig: 64, 64 offset: 0, 0 index: -1 -teleporter-top +pneumatic-drill-rotator rotate: false - xy: 115, 18 - size: 8, 8 - orig: 8, 8 + xy: 555, 244 + size: 64, 64 + orig: 64, 64 offset: 0, 0 index: -1 -thermalgenerator +pneumatic-drill-top rotate: false - xy: 125, 18 - size: 8, 8 - orig: 8, 8 + xy: 621, 310 + size: 64, 64 + orig: 64, 64 offset: 0, 0 index: -1 -titancannon +water-extractor rotate: false - xy: 316, 169 - size: 24, 24 - orig: 24, 24 + xy: 1241, 1362 + size: 64, 64 + orig: 64, 64 offset: 0, 0 index: -1 -titancannon-icon +water-extractor-liquid rotate: false - xy: 625, 261 - size: 10, 10 - orig: 10, 10 + xy: 1241, 1296 + size: 64, 64 + orig: 64, 64 offset: 0, 0 index: -1 -titancannon-icon_old +water-extractor-rotator rotate: false - xy: 637, 261 - size: 10, 10 - orig: 10, 10 + xy: 1307, 1362 + size: 64, 64 + orig: 64, 64 offset: 0, 0 index: -1 -titancannon_old +water-extractor-top rotate: false - xy: 316, 143 - size: 24, 24 - orig: 24, 24 + xy: 1373, 1428 + size: 64, 64 + orig: 64, 64 offset: 0, 0 index: -1 -titanium1 +block-border rotate: false - xy: 135, 18 - size: 8, 8 - orig: 8, 8 + xy: 1519, 110 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -titanium2 +block-middle rotate: false - xy: 145, 18 - size: 8, 8 - orig: 8, 8 + xy: 1519, 76 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -titanium3 +block-select rotate: false - xy: 155, 18 - size: 8, 8 - orig: 8, 8 + xy: 1519, 42 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -titaniumdrill +conduit-liquid rotate: false - xy: 89, 6 - size: 8, 8 - orig: 8, 8 + xy: 1557, 530 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -titaniumpurifier +place-arrow rotate: false - xy: 99, 8 - size: 8, 8 - orig: 8, 8 + xy: 391, 223 + size: 96, 96 + orig: 96, 96 offset: 0, 0 index: -1 -titaniumshieldwall +rubble-1-0 rotate: false - xy: 109, 8 - size: 8, 8 - orig: 8, 8 + xy: 555, 112 + size: 64, 64 + orig: 64, 64 offset: 0, 0 index: -1 -titaniumwall +rubble-1-1 rotate: false - xy: 119, 8 - size: 8, 8 - orig: 8, 8 + xy: 621, 178 + size: 64, 64 + orig: 64, 64 offset: 0, 0 index: -1 -titaniumwall-large +rubble-2-0 rotate: false - xy: 141, 112 - size: 16, 16 - orig: 16, 16 + xy: 687, 244 + size: 64, 64 + orig: 64, 64 offset: 0, 0 index: -1 -titaniumwall-large-icon +rubble-2-1 rotate: false - xy: 129, 8 - size: 8, 8 - orig: 8, 8 + xy: 621, 112 + size: 64, 64 + orig: 64, 64 offset: 0, 0 index: -1 -turret +rubble-3-0 rotate: false - xy: 649, 261 - size: 10, 10 - orig: 10, 10 + xy: 513, 1236 + size: 96, 96 + orig: 96, 96 offset: 0, 0 index: -1 -uranium1 +rubble-3-1 rotate: false - xy: 163, 28 - size: 8, 8 - orig: 8, 8 + xy: 513, 1236 + size: 96, 96 + orig: 96, 96 offset: 0, 0 index: -1 -uranium2 +rubble-4-0 rotate: false - xy: 165, 18 - size: 8, 8 - orig: 8, 8 + xy: 1525, 1658 + size: 128, 128 + orig: 128, 128 offset: 0, 0 index: -1 -uranium3 +rubble-4-1 rotate: false - xy: 169, 8 - size: 8, 8 - orig: 8, 8 + xy: 1525, 1658 + size: 128, 128 + orig: 128, 128 offset: 0, 0 index: -1 -uraniumdrill +bridge-conduit rotate: false - xy: 190, 102 - size: 8, 8 - orig: 8, 8 + xy: 1519, 8 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -water +bridge-conduit-arrow rotate: false - xy: 220, 109 - size: 8, 8 - orig: 8, 8 + xy: 2010, 1810 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -wateredge +bridge-conduit-bridge rotate: false - xy: 722, 428 - size: 12, 12 - orig: 12, 12 + xy: 2010, 1468 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -waveturret +bridge-conduit-end rotate: false - xy: 768, 446 - size: 10, 10 - orig: 10, 10 + xy: 2010, 1434 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -weaponfactory +conduit-bottom rotate: false - xy: 123, 76 - size: 16, 16 - orig: 16, 16 + xy: 1609, 1177 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -weaponfactory-icon +conduit-bottom-0 rotate: false - xy: 230, 109 - size: 8, 8 - orig: 8, 8 + xy: 1609, 1143 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-bottom-1 + rotate: false + xy: 1609, 1109 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-bottom-2 + rotate: false + xy: 1609, 1075 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-bottom-3 + rotate: false + xy: 1609, 1041 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-bottom-4 + rotate: false + xy: 1603, 1007 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-bottom-5 + rotate: false + xy: 1557, 632 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-bottom-6 + rotate: false + xy: 1557, 598 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-top-0 + rotate: false + xy: 1557, 496 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-top-1 + rotate: false + xy: 1557, 462 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-top-2 + rotate: false + xy: 1557, 428 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-top-3 + rotate: false + xy: 1557, 394 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-top-4 + rotate: false + xy: 1553, 360 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-top-5 + rotate: false + xy: 1553, 326 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-top-6 + rotate: false + xy: 1553, 292 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +liquid-junction + rotate: false + xy: 1707, 1368 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +liquid-router-bottom + rotate: false + xy: 1741, 1368 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +liquid-router-liquid + rotate: false + xy: 1775, 1368 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +liquid-router-top + rotate: false + xy: 1741, 1334 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +liquid-tank-bottom + rotate: false + xy: 391, 909 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +liquid-tank-liquid + rotate: false + xy: 391, 811 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +liquid-tank-top + rotate: false + xy: 293, 615 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +mechanical-pump + rotate: false + xy: 1733, 1300 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +phase-conduit + rotate: false + xy: 1751, 1266 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +phase-conduit-arrow + rotate: false + xy: 1785, 1266 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +phase-conduit-bridge + rotate: false + xy: 1677, 1232 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +phase-conduit-end + rotate: false + xy: 1677, 1198 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pulse-conduit-top-0 + rotate: false + xy: 1711, 1096 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pulse-conduit-top-1 + rotate: false + xy: 1779, 1130 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pulse-conduit-top-2 + rotate: false + xy: 1745, 1096 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pulse-conduit-top-3 + rotate: false + xy: 1711, 1062 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pulse-conduit-top-4 + rotate: false + xy: 1779, 1096 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pulse-conduit-top-5 + rotate: false + xy: 1745, 1062 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pulse-conduit-top-6 + rotate: false + xy: 1779, 1062 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +rotary-pump + rotate: false + xy: 621, 244 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +thermal-pump + rotate: false + xy: 905, 1434 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +dart-mech-pad + rotate: false + xy: 489, 48 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +delta-mech-pad + rotate: false + xy: 564, 1168 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +glaive-ship-pad + rotate: false + xy: 400, 1203 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +javelin-ship-pad + rotate: false + xy: 621, 904 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +omega-mech-pad + rotate: false + xy: 293, 125 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +tau-mech-pad + rotate: false + xy: 1249, 1494 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +trident-ship-pad + rotate: false + xy: 1645, 1494 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +battery + rotate: false + xy: 1519, 280 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +battery-large + rotate: false + xy: 317, 1497 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +combustion-generator + rotate: false + xy: 1615, 1245 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +combustion-generator-top + rotate: false + xy: 1609, 1211 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +differential-generator + rotate: false + xy: 317, 1399 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +differential-generator-top + rotate: false + xy: 415, 1399 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +impact-reactor + rotate: false + xy: 163, 1063 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +impact-reactor-bottom + rotate: false + xy: 163, 933 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +impact-reactor-light + rotate: false + xy: 163, 673 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +impact-reactor-plasma-0 + rotate: false + xy: 163, 543 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +impact-reactor-plasma-1 + rotate: false + xy: 163, 413 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +impact-reactor-plasma-2 + rotate: false + xy: 163, 283 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +impact-reactor-plasma-3 + rotate: false + xy: 163, 153 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +power-node + rotate: false + xy: 1745, 1164 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +power-node-large + rotate: false + xy: 687, 376 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +power-source + rotate: false + xy: 1677, 1062 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +power-void + rotate: false + xy: 1779, 1164 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +rtg-generator + rotate: false + xy: 687, 310 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +rtg-generator-top + rotate: false + xy: 1735, 790 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +solar-panel + rotate: false + xy: 1887, 1258 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +solar-panel-large + rotate: false + xy: 807, 1336 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +surge-tower + rotate: false + xy: 828, 1172 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +thermal-generator + rotate: false + xy: 1315, 1494 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +thorium-reactor + rotate: false + xy: 807, 1238 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +thorium-reactor-center + rotate: false + xy: 905, 1336 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +thorium-reactor-lights + rotate: false + xy: 1003, 1434 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +turbine-generator + rotate: false + xy: 1711, 1494 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +turbine-generator-top + rotate: false + xy: 1777, 1494 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +alloy-smelter + rotate: false + xy: 219, 1540 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +alloy-smelter-top + rotate: false + xy: 219, 1442 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +blast-mixer + rotate: false + xy: 807, 1560 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +coal-centrifuge + rotate: false + xy: 1175, 1482 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cryofluidmixer-bottom + rotate: false + xy: 489, 708 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cryofluidmixer-liquid + rotate: false + xy: 489, 576 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cryofluidmixer-top + rotate: false + xy: 489, 510 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cultivator + rotate: false + xy: 489, 444 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cultivator-middle + rotate: false + xy: 489, 312 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cultivator-top + rotate: false + xy: 489, 246 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +graphite-press + rotate: false + xy: 555, 838 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +incinerator + rotate: false + xy: 1727, 395 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-source + rotate: false + xy: 1757, 293 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-void + rotate: false + xy: 1757, 123 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +kiln + rotate: false + xy: 687, 970 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +kiln-top + rotate: false + xy: 555, 772 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +silicon-smelter-top + rotate: false + xy: 555, 772 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +liquid-source + rotate: false + xy: 1809, 1368 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +melter + rotate: false + xy: 1801, 1300 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +multi-press + rotate: false + xy: 293, 419 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +phase-weaver + rotate: false + xy: 621, 508 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +phase-weaver-bottom + rotate: false + xy: 687, 574 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +phase-weaver-weave + rotate: false + xy: 621, 442 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +plastanium-compressor + rotate: false + xy: 687, 508 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +plastanium-compressor-top + rotate: false + xy: 555, 310 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +pulverizer + rotate: false + xy: 1705, 1028 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pulverizer-rotator + rotate: false + xy: 1773, 1028 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pump-liquid + rotate: false + xy: 1735, 994 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pyratite-mixer + rotate: false + xy: 555, 178 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +separator + rotate: false + xy: 753, 772 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +separator-liquid + rotate: false + xy: 753, 706 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +silicon-smelter + rotate: false + xy: 753, 640 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +spore-press + rotate: false + xy: 753, 442 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +spore-press-frame0 + rotate: false + xy: 753, 376 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +spore-press-frame1 + rotate: false + xy: 753, 310 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +spore-press-frame2 + rotate: false + xy: 753, 244 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +spore-press-liquid + rotate: false + xy: 753, 112 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +spore-press-top + rotate: false + xy: 753, 46 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +container + rotate: false + xy: 1175, 1416 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +core-foundation + rotate: false + xy: 786, 1792 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +core-nucleus + rotate: false + xy: 1, 1031 + size: 160, 160 + orig: 160, 160 + offset: 0, 0 + index: -1 +core-shard + rotate: false + xy: 1347, 1560 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +launch-pad + rotate: false + xy: 293, 811 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +launch-pad-large + rotate: false + xy: 163, 23 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +unloader + rotate: false + xy: 1837, 850 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +vault + rotate: false + xy: 1003, 1238 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +arc-heat + rotate: false + xy: 1519, 348 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +block-1 + rotate: false + xy: 1519, 144 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +block-2 + rotate: false + xy: 1101, 1244 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +block-3 + rotate: false + xy: 1249, 1560 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +block-4 + rotate: false + xy: 526, 1756 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +hail-heat + rotate: false + xy: 1547, 1279 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +lancer-heat + rotate: false + xy: 687, 904 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +meltdown-heat + rotate: false + xy: 875, 1662 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +ripple-heat + rotate: false + xy: 391, 125 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +salvo-heat + rotate: false + xy: 687, 112 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +salvo-panel-left + rotate: false + xy: 621, 46 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +salvo-panel-right + rotate: false + xy: 687, 46 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +scorch-heat + rotate: false + xy: 1735, 722 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +wave-liquid + rotate: false + xy: 1439, 1428 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +crawler-factory + rotate: false + xy: 489, 906 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +dagger-factory + rotate: false + xy: 489, 906 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +draug-factory + rotate: false + xy: 489, 906 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +phantom-factory + rotate: false + xy: 489, 906 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +spirit-factory + rotate: false + xy: 489, 906 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +wraith-factory + rotate: false + xy: 489, 906 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +crawler-factory-top + rotate: false + xy: 489, 774 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +dagger-factory-top + rotate: false + xy: 489, 114 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +draug-factory-top + rotate: false + xy: 621, 1102 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +fortress-factory + rotate: false + xy: 611, 1332 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +fortress-factory-top + rotate: false + xy: 219, 1344 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +ghoul-factory-top + rotate: false + xy: 219, 1344 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +titan-factory-top + rotate: false + xy: 219, 1344 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +ghoul-factory + rotate: false + xy: 415, 1301 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +phantom-factory-top + rotate: false + xy: 687, 640 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +repair-point-base + rotate: false + xy: 1735, 926 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +revenant-factory + rotate: false + xy: 1135, 1662 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +revenant-factory-top + rotate: false + xy: 1395, 1658 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +spirit-factory-top + rotate: false + xy: 753, 508 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +titan-factory + rotate: false + xy: 905, 1238 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +wraith-factory-top + rotate: false + xy: 1439, 1362 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +copper-wall + rotate: false + xy: 1643, 1075 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +copper-wall-large + rotate: false + xy: 1175, 1350 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +door + rotate: false + xy: 1599, 837 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +door-large + rotate: false + xy: 696, 1168 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +door-large-open + rotate: false + xy: 555, 1102 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +door-open + rotate: false + xy: 1667, 905 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +phase-wall + rotate: false + xy: 1677, 1130 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +phase-wall-large + rotate: false + xy: 555, 442 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +scrap-wall-gigantic + rotate: false + xy: 1655, 1658 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +scrap-wall-huge1 + rotate: false + xy: 611, 1234 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +scrap-wall-huge2 + rotate: false + xy: 709, 1234 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +scrap-wall-huge3 + rotate: false + xy: 807, 1434 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +scrap-wall-large1 + rotate: false + xy: 753, 1036 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +scrap-wall-large2 + rotate: false + xy: 753, 970 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +scrap-wall-large3 + rotate: false + xy: 753, 904 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +scrap-wall-large4 + rotate: false + xy: 753, 838 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +scrap-wall1 + rotate: false + xy: 1735, 688 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scrap-wall2 + rotate: false + xy: 1769, 722 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scrap-wall3 + rotate: false + xy: 1769, 688 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scrap-wall4 + rotate: false + xy: 1761, 654 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scrap-wall5 + rotate: false + xy: 1761, 654 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +surge-wall + rotate: false + xy: 1847, 1156 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +surge-wall-large + rotate: false + xy: 894, 1172 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +thorium-wall + rotate: false + xy: 1847, 1088 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +thorium-wall-large + rotate: false + xy: 1381, 1494 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +thruster + rotate: false + xy: 875, 1532 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +titanium-wall + rotate: false + xy: 1803, 850 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium-wall-large + rotate: false + xy: 1579, 1494 + size: 64, 64 + orig: 64, 64 offset: 0, 0 index: -1 bullet rotate: false - xy: 557, 199 - size: 8, 8 - orig: 8, 8 + xy: 526, 1906 + size: 52, 52 + orig: 52, 52 offset: 0, 0 index: -1 -chainbullet +bullet-back rotate: false - xy: 49, 1 - size: 8, 7 - orig: 8, 7 + xy: 1956, 1790 + size: 52, 52 + orig: 52, 52 offset: 0, 0 index: -1 -circle +casing rotate: false - xy: 791, 494 - size: 17, 17 - orig: 17, 17 + xy: 1667, 1248 + size: 8, 16 + orig: 8, 16 offset: 0, 0 index: -1 -circle2 +circle-shadow rotate: false - xy: 424, 310 + xy: 1, 1193 size: 201, 201 orig: 201, 201 offset: 0, 0 index: -1 -blastenemy-t1 +error rotate: false - xy: 1008, 497 - size: 14, 14 - orig: 14, 14 - offset: 0, 0 - index: -1 -blastenemy-t2 - rotate: false - xy: 141, 96 - size: 14, 14 - orig: 14, 14 - offset: 0, 0 - index: -1 -blastenemy-t3 - rotate: false - xy: 159, 114 - size: 14, 14 - orig: 14, 14 - offset: 0, 0 - index: -1 -empenemy-t1 - rotate: false - xy: 141, 80 - size: 14, 14 - orig: 14, 14 - offset: 0, 0 - index: -1 -empenemy-t2 - rotate: false - xy: 342, 141 - size: 14, 14 - orig: 14, 14 - offset: 0, 0 - index: -1 -empenemy-t3 - rotate: false - xy: 69, 58 - size: 14, 14 - orig: 14, 14 - offset: 0, 0 - index: -1 -fastenemy-t1 - rotate: false - xy: 551, 257 - size: 14, 14 - orig: 14, 14 - offset: 0, 0 - index: -1 -fastenemy-t2 - rotate: false - xy: 509, 229 - size: 14, 14 - orig: 14, 14 - offset: 0, 0 - index: -1 -fastenemy-t3 - rotate: false - xy: 651, 410 - size: 14, 14 - orig: 14, 14 - offset: 0, 0 - index: -1 -flamerenemy-t1 - rotate: false - xy: 651, 394 - size: 14, 14 - orig: 14, 14 - offset: 0, 0 - index: -1 -flamerenemy-t2 - rotate: false - xy: 57, 32 - size: 14, 14 - orig: 14, 14 - offset: 0, 0 - index: -1 -flamerenemy-t3 - rotate: false - xy: 653, 366 - size: 14, 14 - orig: 14, 14 - offset: 0, 0 - index: -1 -fortressenemy-t1 - rotate: false - xy: 769, 491 - size: 20, 20 - orig: 20, 20 - offset: 0, 0 - index: -1 -fortressenemy-t2 - rotate: false - xy: 27, 4 - size: 20, 20 - orig: 20, 20 - offset: 0, 0 - index: -1 -fortressenemy-t3 - rotate: false - xy: 603, 288 - size: 20, 20 - orig: 20, 20 - offset: 0, 0 - index: -1 -healerenemy-t1 - rotate: false - xy: 653, 350 - size: 14, 14 - orig: 14, 14 - offset: 0, 0 - index: -1 -healerenemy-t2 - rotate: false - xy: 653, 334 - size: 14, 14 - orig: 14, 14 - offset: 0, 0 - index: -1 -healerenemy-t3 - rotate: false - xy: 653, 318 - size: 14, 14 - orig: 14, 14 - offset: 0, 0 - index: -1 -mortarenemy-t1 - rotate: false - xy: 810, 479 - size: 14, 14 - orig: 14, 14 - offset: 0, 0 - index: -1 -mortarenemy-t2 - rotate: false - xy: 794, 462 - size: 14, 14 - orig: 14, 14 - offset: 0, 0 - index: -1 -mortarenemy-t3 - rotate: false - xy: 810, 463 - size: 14, 14 - orig: 14, 14 - offset: 0, 0 - index: -1 -rapidenemy-t1 - rotate: false - xy: 826, 479 - size: 14, 14 - orig: 14, 14 - offset: 0, 0 - index: -1 -rapidenemy-t2 - rotate: false - xy: 826, 463 - size: 14, 14 - orig: 14, 14 - offset: 0, 0 - index: -1 -rapidenemy-t3 - rotate: false - xy: 842, 479 - size: 14, 14 - orig: 14, 14 - offset: 0, 0 - index: -1 -standardenemy-t1 - rotate: false - xy: 842, 463 - size: 14, 14 - orig: 14, 14 - offset: 0, 0 - index: -1 -targetenemy-t1 - rotate: false - xy: 842, 463 - size: 14, 14 - orig: 14, 14 - offset: 0, 0 - index: -1 -standardenemy-t2 - rotate: false - xy: 974, 481 - size: 12, 12 - orig: 12, 12 - offset: 0, 0 - index: -1 -standardenemy-t3 - rotate: false - xy: 988, 481 - size: 12, 12 - orig: 12, 12 - offset: 0, 0 - index: -1 -tankenemy-t1 - rotate: false - xy: 858, 479 - size: 14, 14 - orig: 14, 14 - offset: 0, 0 - index: -1 -tankenemy-t2 - rotate: false - xy: 858, 463 - size: 14, 14 - orig: 14, 14 - offset: 0, 0 - index: -1 -tankenemy-t3 - rotate: false - xy: 874, 479 - size: 14, 14 - orig: 14, 14 - offset: 0, 0 - index: -1 -titanenemy-t1 - rotate: false - xy: 123, 112 - size: 16, 16 - orig: 16, 16 - offset: 0, 0 - index: -1 -titanenemy-t2 - rotate: false - xy: 105, 76 - size: 16, 16 - orig: 16, 16 - offset: 0, 0 - index: -1 -titanenemy-t3 - rotate: false - xy: 123, 94 - size: 16, 16 - orig: 16, 16 - offset: 0, 0 - index: -1 -enemyarrow - rotate: false - xy: 59, 1 - size: 8, 7 - orig: 8, 7 - offset: 0, 0 - index: -1 -icon-coal - rotate: false - xy: 450, 204 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -icon-dirium - rotate: false - xy: 460, 215 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -icon-iron - rotate: false - xy: 460, 205 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -icon-sand - rotate: false - xy: 450, 194 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -icon-steel - rotate: false - xy: 460, 195 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -icon-stone - rotate: false - xy: 448, 184 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -icon-titanium - rotate: false - xy: 452, 174 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -icon-uranium - rotate: false - xy: 458, 184 - size: 8, 8 - orig: 8, 8 + xy: 869, 472 + size: 48, 48 + orig: 48, 48 offset: 0, 0 index: -1 laser rotate: false - xy: 175, 116 - size: 1, 12 - orig: 1, 12 + xy: 204, 1346 + size: 4, 48 + orig: 4, 48 offset: 0, 0 index: -1 -laserend +laser-end rotate: false - xy: 453, 225 - size: 18, 18 - orig: 18, 18 + xy: 1101, 1458 + size: 72, 72 + orig: 72, 72 offset: 0, 0 index: -1 -laserfull +minelaser rotate: false - xy: 342, 175 - size: 18, 18 - orig: 18, 18 + xy: 498, 1251 + size: 4, 48 + orig: 4, 48 offset: 0, 0 index: -1 -mech-standard +minelaser-end rotate: false - xy: 890, 481 - size: 12, 12 - orig: 12, 12 + xy: 1101, 1384 + size: 72, 72 + orig: 72, 72 offset: 0, 0 index: -1 -mech-standard-icon +missile rotate: false - xy: 904, 481 - size: 12, 12 - orig: 12, 12 + xy: 1527, 704 + size: 36, 36 + orig: 36, 36 offset: 0, 0 index: -1 -ship-standard +missile-back rotate: false - xy: 946, 481 - size: 12, 12 - orig: 12, 12 + xy: 1527, 666 + size: 36, 36 + orig: 36, 36 + offset: 0, 0 + index: -1 +scale_marker + rotate: false + xy: 317, 1632 + size: 4, 4 + orig: 4, 4 + offset: 0, 0 + index: -1 +scorch1 + rotate: false + xy: 1803, 680 + size: 28, 100 + orig: 28, 100 + offset: 0, 0 + index: -1 +scorch2 + rotate: false + xy: 1795, 578 + size: 28, 100 + orig: 28, 100 + offset: 0, 0 + index: -1 +scorch3 + rotate: false + xy: 1795, 476 + size: 28, 100 + orig: 28, 100 + offset: 0, 0 + index: -1 +scorch4 + rotate: false + xy: 1825, 578 + size: 28, 100 + orig: 28, 100 + offset: 0, 0 + index: -1 +scorch5 + rotate: false + xy: 1825, 476 + size: 28, 100 + orig: 28, 100 offset: 0, 0 index: -1 shell rotate: false - xy: 579, 212 - size: 8, 8 - orig: 8, 8 + xy: 1519, 570 + size: 36, 36 + orig: 36, 36 + offset: 0, 0 + index: -1 +shell-back + rotate: false + xy: 1519, 532 + size: 36, 36 + orig: 36, 36 offset: 0, 0 index: -1 shot rotate: false - xy: 589, 222 - size: 8, 8 - orig: 8, 8 + xy: 1761, 416 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -shot-long +transfer rotate: false - xy: 589, 212 - size: 8, 8 - orig: 8, 8 + xy: 1241, 1498 + size: 4, 48 + orig: 4, 48 offset: 0, 0 index: -1 -titanshell +transfer-arrow rotate: false - xy: 139, 8 - size: 8, 8 - orig: 8, 8 + xy: 1871, 918 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -border +transfer-end rotate: false - xy: 1, 44 - size: 24, 40 - split: 5, 5, 5, 10 - orig: 24, 40 + xy: 1101, 1310 + size: 72, 72 + orig: 72, 72 + offset: 0, 0 + index: -1 +alloy-smelter-icon-large + rotate: false + xy: 819, 748 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +alloy-smelter-icon-medium + rotate: false + xy: 783, 12 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +alloy-smelter-icon-small + rotate: false + xy: 137, 33 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +arc + rotate: false + xy: 1519, 382 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +arc-icon-full + rotate: false + xy: 1519, 314 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +arc-icon-large + rotate: false + xy: 819, 648 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +arc-icon-small + rotate: false + xy: 137, 7 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +battery-icon-large + rotate: false + xy: 819, 540 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +battery-icon-small + rotate: false + xy: 485, 1600 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +battery-large-icon-large + rotate: false + xy: 819, 490 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +battery-large-icon-medium + rotate: false + xy: 1519, 246 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +battery-large-icon-small + rotate: false + xy: 807, 1534 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +blast-drill-icon-full + rotate: false + xy: 1490, 1918 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +blast-drill-icon-large + rotate: false + xy: 819, 440 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +blast-drill-icon-medium + rotate: false + xy: 1519, 212 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +blast-drill-icon-small + rotate: false + xy: 1565, 671 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +blast-mixer-icon-large + rotate: false + xy: 819, 390 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +blast-mixer-icon-medium + rotate: false + xy: 1519, 178 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +blast-mixer-icon-small + rotate: false + xy: 1871, 824 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +bridge-conduit-icon-large + rotate: false + xy: 819, 240 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +bridge-conduit-icon-small + rotate: false + xy: 1905, 858 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +bridge-conveyor-icon-large + rotate: false + xy: 819, 190 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +bridge-conveyor-icon-small + rotate: false + xy: 1979, 1328 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +char-icon-large + rotate: false + xy: 819, 90 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +char-icon-small + rotate: false + xy: 1949, 1169 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +cliffs-icon-large + rotate: false + xy: 87, 9 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +cliffs-icon-small + rotate: false + xy: 833, 1534 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +coal-centrifuge-icon-large + rotate: false + xy: 877, 1122 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +coal-centrifuge-icon-medium + rotate: false + xy: 1569, 1007 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +coal-centrifuge-icon-small + rotate: false + xy: 1949, 1143 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +combustion-generator-icon-large + rotate: false + xy: 877, 1072 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +combustion-generator-icon-small + rotate: false + xy: 1949, 1117 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +conduit-icon-full + rotate: false + xy: 1557, 564 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-icon-large + rotate: false + xy: 927, 1122 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +conduit-icon-small + rotate: false + xy: 1949, 1091 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +container-icon-large + rotate: false + xy: 877, 1022 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +container-icon-medium + rotate: false + xy: 1553, 258 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +container-icon-small + rotate: false + xy: 1949, 1065 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +conveyor-icon-large + rotate: false + xy: 927, 1072 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +conveyor-icon-small + rotate: false + xy: 1795, 412 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +copper-wall-icon-large + rotate: false + xy: 977, 1122 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +copper-wall-icon-small + rotate: false + xy: 1821, 412 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +copper-wall-large-icon-large + rotate: false + xy: 877, 972 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +copper-wall-large-icon-medium + rotate: false + xy: 1643, 1041 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +copper-wall-large-icon-small + rotate: false + xy: 1955, 1266 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +core-foundation-icon-large + rotate: false + xy: 927, 1022 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +core-foundation-icon-medium + rotate: false + xy: 1637, 1007 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +core-foundation-icon-small + rotate: false + xy: 2010, 1340 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +core-nucleus-icon-large + rotate: false + xy: 977, 1072 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +core-nucleus-icon-medium + rotate: false + xy: 1671, 1007 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +core-nucleus-icon-small + rotate: false + xy: 1949, 1039 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +core-shard-icon-large + rotate: false + xy: 1027, 1122 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +core-shard-icon-medium + rotate: false + xy: 1565, 969 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +core-shard-icon-small + rotate: false + xy: 1943, 1013 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +cracks-1-0 + rotate: false + xy: 1565, 935 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +cracks-1-1 + rotate: false + xy: 1565, 901 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +cracks-1-2 + rotate: false + xy: 1565, 867 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +cracks-1-3 + rotate: false + xy: 1565, 833 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +cracks-1-4 + rotate: false + xy: 1565, 799 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +cracks-1-5 + rotate: false + xy: 1565, 765 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +cracks-1-6 + rotate: false + xy: 1565, 731 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +cracks-1-7 + rotate: false + xy: 1565, 697 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +cracks-2-0 + rotate: false + xy: 1175, 1284 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cracks-2-1 + rotate: false + xy: 1167, 1218 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cracks-2-2 + rotate: false + xy: 1101, 1178 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cracks-2-3 + rotate: false + xy: 1167, 1152 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cracks-2-4 + rotate: false + xy: 498, 1170 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cracks-2-5 + rotate: false + xy: 489, 1104 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cracks-2-6 + rotate: false + xy: 489, 1038 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cracks-2-7 + rotate: false + xy: 489, 972 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cracks-3-0 + rotate: false + xy: 1445, 1560 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +cracks-3-1 + rotate: false + xy: 1543, 1560 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +cracks-3-2 + rotate: false + xy: 1641, 1560 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +cracks-3-3 + rotate: false + xy: 1739, 1560 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +cracks-3-4 + rotate: false + xy: 1837, 1560 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +cracks-3-5 + rotate: false + xy: 1935, 1560 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +cracks-3-6 + rotate: false + xy: 415, 1497 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +cracks-3-7 + rotate: false + xy: 513, 1528 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +cracks-4-0 + rotate: false + xy: 916, 1792 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +cracks-4-1 + rotate: false + xy: 1046, 1792 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +cracks-4-2 + rotate: false + xy: 1176, 1792 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +cracks-4-3 + rotate: false + xy: 1306, 1788 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +cracks-4-4 + rotate: false + xy: 1436, 1788 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +cracks-4-5 + rotate: false + xy: 1566, 1788 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +cracks-4-6 + rotate: false + xy: 1696, 1788 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +cracks-4-7 + rotate: false + xy: 1826, 1788 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +cracks-5-0 + rotate: false + xy: 1, 869 + size: 160, 160 + orig: 160, 160 + offset: 0, 0 + index: -1 +cracks-5-1 + rotate: false + xy: 1, 707 + size: 160, 160 + orig: 160, 160 + offset: 0, 0 + index: -1 +cracks-5-2 + rotate: false + xy: 1, 545 + size: 160, 160 + orig: 160, 160 + offset: 0, 0 + index: -1 +cracks-5-3 + rotate: false + xy: 1, 383 + size: 160, 160 + orig: 160, 160 + offset: 0, 0 + index: -1 +cracks-5-4 + rotate: false + xy: 1, 221 + size: 160, 160 + orig: 160, 160 + offset: 0, 0 + index: -1 +cracks-5-5 + rotate: false + xy: 1, 59 + size: 160, 160 + orig: 160, 160 + offset: 0, 0 + index: -1 +cracks-5-6 + rotate: false + xy: 323, 1595 + size: 160, 160 + orig: 160, 160 + offset: 0, 0 + index: -1 +cracks-5-7 + rotate: false + xy: 582, 1886 + size: 160, 160 + orig: 160, 160 + offset: 0, 0 + index: -1 +craters-icon-large + rotate: false + xy: 877, 922 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +craters-icon-small + rotate: false + xy: 1939, 987 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +crawler-factory-icon-full + rotate: false + xy: 489, 840 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +crawler-factory-icon-large + rotate: false + xy: 1027, 1072 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +crawler-factory-icon-medium + rotate: false + xy: 1599, 973 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +crawler-factory-icon-small + rotate: false + xy: 1939, 961 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +cryofluidmixer-icon-full + rotate: false + xy: 489, 642 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cryofluidmixer-icon-large + rotate: false + xy: 927, 922 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +cryofluidmixer-icon-medium + rotate: false + xy: 1599, 939 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +cryofluidmixer-icon-small + rotate: false + xy: 1939, 935 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +cultivator-icon-full + rotate: false + xy: 489, 378 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cultivator-icon-large + rotate: false + xy: 977, 972 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +cultivator-icon-medium + rotate: false + xy: 1633, 973 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +cultivator-icon-small + rotate: false + xy: 1939, 909 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +cyclone + rotate: false + xy: 611, 1528 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +cyclone-icon-full + rotate: false + xy: 709, 1528 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +cyclone-icon-large + rotate: false + xy: 1027, 1022 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +cyclone-icon-medium + rotate: false + xy: 1599, 905 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +cyclone-icon-small + rotate: false + xy: 1939, 883 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +dagger-factory-icon-full + rotate: false + xy: 489, 180 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +dagger-factory-icon-large + rotate: false + xy: 977, 922 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +dagger-factory-icon-medium + rotate: false + xy: 1633, 939 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dagger-factory-icon-small + rotate: false + xy: 1931, 857 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +dark-metal-icon-large + rotate: false + xy: 927, 822 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +dark-metal-icon-small + rotate: false + xy: 1905, 832 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +dark-panel-1-icon-large + rotate: false + xy: 977, 872 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +dark-panel-1-icon-small + rotate: false + xy: 1931, 831 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +dark-panel-2-icon-large + rotate: false + xy: 1027, 922 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +dark-panel-2-icon-small + rotate: false + xy: 1871, 798 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +dark-panel-3-icon-large + rotate: false + xy: 977, 822 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +dark-panel-3-icon-small + rotate: false + xy: 1897, 806 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +dark-panel-4-icon-large + rotate: false + xy: 1027, 872 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +dark-panel-4-icon-small + rotate: false + xy: 1923, 805 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +dark-panel-5-icon-large + rotate: false + xy: 1027, 822 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +dark-panel-5-icon-small + rotate: false + xy: 1897, 780 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +dark-panel-6-icon-large + rotate: false + xy: 819, 40 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +dark-panel-6-icon-small + rotate: false + xy: 1923, 779 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +darksand-icon-large + rotate: false + xy: 877, 772 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +darksand-icon-small + rotate: false + xy: 1971, 1300 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +darksand-tainted-water-icon-large + rotate: false + xy: 927, 772 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +darksand-tainted-water-icon-small + rotate: false + xy: 1957, 857 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +darksand-water-icon-large + rotate: false + xy: 977, 772 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +darksand-water-icon-small + rotate: false + xy: 1957, 831 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +dart-mech-pad-icon-large + rotate: false + xy: 1027, 772 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +dart-mech-pad-icon-medium + rotate: false + xy: 1667, 973 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dart-mech-pad-icon-small + rotate: false + xy: 1949, 805 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +deepwater-icon-large + rotate: false + xy: 869, 672 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +deepwater-icon-small + rotate: false + xy: 1949, 779 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +delta-mech-pad-icon-large + rotate: false + xy: 969, 722 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +delta-mech-pad-icon-medium + rotate: false + xy: 1599, 871 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +delta-mech-pad-icon-small + rotate: false + xy: 1837, 764 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +differential-generator-icon-large + rotate: false + xy: 869, 572 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +differential-generator-icon-medium + rotate: false + xy: 1633, 905 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +differential-generator-icon-small + rotate: false + xy: 1833, 738 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +distributor-icon-large + rotate: false + xy: 919, 622 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +distributor-icon-medium + rotate: false + xy: 1667, 939 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +distributor-icon-small + rotate: false + xy: 1833, 712 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +door-icon-large + rotate: false + xy: 969, 672 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +door-icon-small + rotate: false + xy: 1833, 686 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +door-large-icon-large + rotate: false + xy: 1019, 722 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +door-large-icon-medium + rotate: false + xy: 1633, 871 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +door-large-icon-small + rotate: false + xy: 1863, 772 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +draug-factory-icon-full + rotate: false + xy: 555, 1036 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +draug-factory-icon-large + rotate: false + xy: 919, 572 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +draug-factory-icon-medium + rotate: false + xy: 1599, 803 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +draug-factory-icon-small + rotate: false + xy: 1981, 1274 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +dunerocks-icon-large + rotate: false + xy: 969, 622 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +dunerocks-icon-small + rotate: false + xy: 1975, 805 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +duo + rotate: false + xy: 1633, 837 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +duo-icon-full + rotate: false + xy: 1667, 871 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +duo-icon-large + rotate: false + xy: 1019, 672 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +duo-icon-small + rotate: false + xy: 1975, 779 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +force-projector-icon-large + rotate: false + xy: 869, 422 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +force-projector-icon-medium + rotate: false + xy: 1633, 803 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +force-projector-icon-small + rotate: false + xy: 1863, 746 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +fortress-factory-icon-full + rotate: false + xy: 709, 1332 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +fortress-factory-icon-large + rotate: false + xy: 919, 464 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +fortress-factory-icon-medium + rotate: false + xy: 1667, 837 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +fortress-factory-icon-small + rotate: false + xy: 1859, 720 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +fuse + rotate: false + xy: 204, 1246 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +fuse-icon-full + rotate: false + xy: 317, 1301 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +fuse-icon-large + rotate: false + xy: 969, 522 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +fuse-icon-medium + rotate: false + xy: 1599, 735 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +fuse-icon-small + rotate: false + xy: 1859, 694 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +ghoul-factory-icon-full + rotate: false + xy: 302, 1203 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +ghoul-factory-icon-large + rotate: false + xy: 1019, 564 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +ghoul-factory-icon-medium + rotate: false + xy: 1633, 769 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ghoul-factory-icon-small + rotate: false + xy: 1889, 754 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +glaive-ship-pad-icon-large + rotate: false + xy: 869, 372 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +glaive-ship-pad-icon-medium + rotate: false + xy: 1667, 803 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +glaive-ship-pad-icon-small + rotate: false + xy: 1915, 753 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +graphite-press-icon-large + rotate: false + xy: 919, 414 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +graphite-press-icon-medium + rotate: false + xy: 1599, 701 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +graphite-press-icon-small + rotate: false + xy: 1941, 753 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +grass-icon-large + rotate: false + xy: 969, 472 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +grass-icon-small + rotate: false + xy: 1967, 753 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +hail + rotate: false + xy: 1633, 735 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +hail-icon-full + rotate: false + xy: 1667, 769 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +hail-icon-large + rotate: false + xy: 1019, 514 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +hail-icon-small + rotate: false + xy: 1889, 728 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +holostone-icon-large + rotate: false + xy: 919, 364 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +holostone-icon-small + rotate: false + xy: 1915, 727 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +hotrock-icon-large + rotate: false + xy: 969, 422 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +hotrock-icon-small + rotate: false + xy: 1941, 727 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +ice-icon-large + rotate: false + xy: 1019, 464 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +ice-icon-small + rotate: false + xy: 1967, 727 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +ice-snow-icon-large + rotate: false + xy: 869, 272 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +ice-snow-icon-small + rotate: false + xy: 1885, 702 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +icerocks-icon-large + rotate: false + xy: 919, 314 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icerocks-icon-small + rotate: false + xy: 1911, 701 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +ignarock-icon-large + rotate: false + xy: 1327, 896 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +ignarock-icon-small + rotate: false + xy: 1937, 701 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +impact-reactor-icon-full + rotate: false + xy: 163, 803 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +impact-reactor-icon-large + rotate: false + xy: 1377, 946 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +impact-reactor-icon-medium + rotate: false + xy: 1727, 429 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +impact-reactor-icon-small + rotate: false + xy: 1963, 701 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +incinerator-icon-large + rotate: false + xy: 1327, 846 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +incinerator-icon-small + rotate: false + xy: 1885, 676 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-blast-compound-medium + rotate: false + xy: 1911, 675 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-blast-compound-small + rotate: false + xy: 851, 22 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +item-blast-compound-xlarge + rotate: false + xy: 1219, 4 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +item-blast-compound-xxlarge + rotate: false + xy: 1377, 896 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-coal-medium + rotate: false + xy: 1937, 675 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-coal-small + rotate: false + xy: 1631, 1282 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +item-coal-xlarge + rotate: false + xy: 1261, 4 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +item-coal-xxlarge + rotate: false + xy: 1377, 846 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-copper-medium + rotate: false + xy: 1963, 675 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-copper-small + rotate: false + xy: 2031, 1322 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +item-copper-xlarge + rotate: false + xy: 1303, 4 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +item-copper-xxlarge + rotate: false + xy: 1227, 796 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-graphite-medium + rotate: false + xy: 1885, 650 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-graphite-small + rotate: false + xy: 526, 1888 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +item-graphite-xlarge + rotate: false + xy: 1345, 4 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +item-graphite-xxlarge + rotate: false + xy: 1277, 796 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-lead-medium + rotate: false + xy: 1911, 649 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-lead-small + rotate: false + xy: 2010, 1792 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +item-lead-xlarge + rotate: false + xy: 489, 6 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +item-lead-xxlarge + rotate: false + xy: 1327, 796 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-metaglass-medium + rotate: false + xy: 1937, 649 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-metaglass-small + rotate: false + xy: 1563, 1377 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +item-metaglass-xlarge + rotate: false + xy: 1589, 1279 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +item-metaglass-xxlarge + rotate: false + xy: 1377, 796 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-phase-fabric-medium + rotate: false + xy: 1963, 649 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-phase-fabric-small + rotate: false + xy: 1835, 1418 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +item-phase-fabric-xlarge + rotate: false + xy: 531, 4 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +item-phase-fabric-xxlarge + rotate: false + xy: 1127, 752 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-plastanium-medium + rotate: false + xy: 1855, 626 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-plastanium-small + rotate: false + xy: 1649, 1248 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +item-plastanium-xlarge + rotate: false + xy: 573, 4 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +item-plastanium-xxlarge + rotate: false + xy: 1177, 752 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-pyratite-medium + rotate: false + xy: 1855, 600 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-pyratite-small + rotate: false + xy: 1677, 1044 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +item-pyratite-xlarge + rotate: false + xy: 615, 4 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +item-pyratite-xxlarge + rotate: false + xy: 1227, 746 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-sand-medium + rotate: false + xy: 1855, 574 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-sand-small + rotate: false + xy: 1705, 1010 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +item-sand-xlarge + rotate: false + xy: 657, 4 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +item-sand-xxlarge + rotate: false + xy: 1277, 746 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-scrap-medium + rotate: false + xy: 1855, 548 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-scrap-small + rotate: false + xy: 1735, 670 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +item-scrap-xlarge + rotate: false + xy: 699, 4 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +item-scrap-xxlarge + rotate: false + xy: 1327, 746 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-silicon-medium + rotate: false + xy: 1855, 522 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-silicon-small + rotate: false + xy: 1761, 398 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +item-silicon-xlarge + rotate: false + xy: 741, 4 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +item-silicon-xxlarge + rotate: false + xy: 1377, 746 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-source-icon-large + rotate: false + xy: 1119, 702 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-source-icon-small + rotate: false + xy: 1855, 496 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-spore-pod-medium + rotate: false + xy: 1881, 624 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-spore-pod-small + rotate: false + xy: 544, 1888 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +item-spore-pod-xlarge + rotate: false + xy: 1533, 1129 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +item-spore-pod-xxlarge + rotate: false + xy: 1119, 652 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-surge-alloy-medium + rotate: false + xy: 1881, 598 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-surge-alloy-small + rotate: false + xy: 562, 1888 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +item-surge-alloy-xlarge + rotate: false + xy: 1533, 1087 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +item-surge-alloy-xxlarge + rotate: false + xy: 1169, 702 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-thorium-medium + rotate: false + xy: 1881, 572 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-thorium-small + rotate: false + xy: 1469, 3 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +item-thorium-xlarge + rotate: false + xy: 1533, 1045 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +item-thorium-xxlarge + rotate: false + xy: 1119, 602 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-titanium-medium + rotate: false + xy: 1881, 546 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-titanium-small + rotate: false + xy: 1487, 3 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +item-titanium-xlarge + rotate: false + xy: 1527, 1003 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +item-titanium-xxlarge + rotate: false + xy: 1169, 652 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-void-icon-large + rotate: false + xy: 1119, 552 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-void-icon-small + rotate: false + xy: 1881, 520 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +javelin-ship-pad-icon-large + rotate: false + xy: 1119, 502 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +javelin-ship-pad-icon-medium + rotate: false + xy: 1757, 89 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +javelin-ship-pad-icon-small + rotate: false + xy: 1881, 494 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +junction-icon-large + rotate: false + xy: 1119, 452 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +junction-icon-small + rotate: false + xy: 1907, 623 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +kiln-icon-large + rotate: false + xy: 1169, 502 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +kiln-icon-medium + rotate: false + xy: 1757, 21 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +kiln-icon-small + rotate: false + xy: 1907, 597 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +lancer + rotate: false + xy: 621, 838 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +lancer-icon-full + rotate: false + xy: 555, 706 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +lancer-icon-large + rotate: false + xy: 1119, 402 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +lancer-icon-medium + rotate: false + xy: 1605, 1368 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +lancer-icon-small + rotate: false + xy: 1933, 623 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +laser-drill-icon-full + rotate: false + xy: 293, 1007 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +laser-drill-icon-large + rotate: false + xy: 1169, 452 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +laser-drill-icon-medium + rotate: false + xy: 1639, 1368 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +laser-drill-icon-small + rotate: false + xy: 1907, 571 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +launch-pad-icon-large + rotate: false + xy: 1119, 352 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +launch-pad-icon-medium + rotate: false + xy: 1605, 1334 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +launch-pad-icon-small + rotate: false + xy: 1933, 597 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +launch-pad-large-icon-large + rotate: false + xy: 1169, 402 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +launch-pad-large-icon-medium + rotate: false + xy: 1673, 1368 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +launch-pad-large-icon-small + rotate: false + xy: 1959, 623 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +liquid-junction-icon-large + rotate: false + xy: 1169, 352 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +liquid-junction-icon-small + rotate: false + xy: 1907, 545 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +liquid-router-icon-full + rotate: false + xy: 1707, 1334 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +liquid-router-icon-large + rotate: false + xy: 1119, 252 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +liquid-router-icon-small + rotate: false + xy: 1933, 571 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +liquid-source-icon-large + rotate: false + xy: 1169, 302 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +liquid-source-icon-small + rotate: false + xy: 1959, 597 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +liquid-tank-icon-full + rotate: false + xy: 293, 713 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +liquid-tank-icon-large + rotate: false + xy: 1119, 202 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +liquid-tank-icon-medium + rotate: false + xy: 1809, 1334 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +liquid-tank-icon-small + rotate: false + xy: 1907, 519 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +magmarock-icon-large + rotate: false + xy: 1169, 252 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +magmarock-icon-small + rotate: false + xy: 1933, 545 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +mass-driver + rotate: false + xy: 391, 713 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +mass-driver-icon-full + rotate: false + xy: 391, 615 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +mass-driver-icon-large + rotate: false + xy: 1119, 152 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +mass-driver-icon-medium + rotate: false + xy: 1665, 1300 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +mass-driver-icon-small + rotate: false + xy: 1959, 571 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +mech-icon-alpha-mech + rotate: false + xy: 1169, 202 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +mech-icon-dart-ship + rotate: false + xy: 1119, 102 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +mech-icon-delta-mech + rotate: false + xy: 1169, 152 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +mech-icon-glaive-ship + rotate: false + xy: 1439, 1304 + size: 56, 56 + orig: 56, 56 + offset: 0, 0 + index: -1 +mech-icon-javelin-ship + rotate: false + xy: 1119, 52 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +mech-icon-omega-mech + rotate: false + xy: 1505, 1371 + size: 56, 56 + orig: 56, 56 + offset: 0, 0 + index: -1 +mech-icon-tau-mech + rotate: false + xy: 1570, 1436 + size: 56, 56 + orig: 56, 56 + offset: 0, 0 + index: -1 +mech-icon-trident-ship + rotate: false + xy: 819, 972 + size: 56, 56 + orig: 56, 56 + offset: 0, 0 + index: -1 +mechanical-drill-icon-full + rotate: false + xy: 687, 838 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +mechanical-drill-icon-large + rotate: false + xy: 1169, 102 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +mechanical-drill-icon-medium + rotate: false + xy: 1699, 1300 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +mechanical-drill-icon-small + rotate: false + xy: 1933, 519 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +mechanical-pump-icon-large + rotate: false + xy: 1169, 52 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +mechanical-pump-icon-small + rotate: false + xy: 1959, 545 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +meltdown + rotate: false + xy: 745, 1626 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +meltdown-icon-full + rotate: false + xy: 1005, 1662 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +meltdown-icon-large + rotate: false + xy: 1219, 696 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +meltdown-icon-medium + rotate: false + xy: 1767, 1300 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +meltdown-icon-small + rotate: false + xy: 1959, 519 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +melter-icon-large + rotate: false + xy: 1219, 646 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +melter-icon-small + rotate: false + xy: 1907, 493 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +mend-projector-icon-large + rotate: false + xy: 1269, 696 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +mend-projector-icon-medium + rotate: false + xy: 1843, 1360 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +mend-projector-icon-small + rotate: false + xy: 1933, 493 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +mender-icon-large + rotate: false + xy: 1219, 596 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +mender-icon-small + rotate: false + xy: 1959, 493 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +metal-floor-2-icon-large + rotate: false + xy: 1269, 646 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +metal-floor-2-icon-small + rotate: false + xy: 1855, 470 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +metal-floor-3-icon-large + rotate: false + xy: 1319, 696 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +metal-floor-3-icon-small + rotate: false + xy: 1881, 468 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +metal-floor-5-icon-large + rotate: false + xy: 1219, 546 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +metal-floor-5-icon-small + rotate: false + xy: 1907, 467 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +metal-floor-damaged-icon-large + rotate: false + xy: 1269, 596 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +metal-floor-damaged-icon-small + rotate: false + xy: 1933, 467 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +metal-floor-icon-large + rotate: false + xy: 1319, 646 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +metal-floor-icon-small + rotate: false + xy: 1959, 467 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +moss-icon-large + rotate: false + xy: 1369, 696 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +moss-icon-small + rotate: false + xy: 1853, 444 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +multi-press-icon-large + rotate: false + xy: 1219, 496 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +multi-press-icon-medium + rotate: false + xy: 1945, 1360 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +multi-press-icon-small + rotate: false + xy: 1879, 442 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +oil-extractor-icon-full + rotate: false + xy: 293, 321 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +oil-extractor-icon-large + rotate: false + xy: 1269, 546 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +oil-extractor-icon-medium + rotate: false + xy: 1843, 1326 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +oil-extractor-icon-small + rotate: false + xy: 1905, 441 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +omega-mech-pad-icon-large + rotate: false + xy: 1319, 596 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +omega-mech-pad-icon-medium + rotate: false + xy: 1877, 1326 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +omega-mech-pad-icon-small + rotate: false + xy: 1931, 441 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +ore-coal-icon-full + rotate: false + xy: 1911, 1326 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-coal-icon-medium + rotate: false + xy: 1911, 1326 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-coal-icon-large + rotate: false + xy: 1369, 646 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +ore-coal-icon-small + rotate: false + xy: 1957, 441 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +ore-copper-icon-full + rotate: false + xy: 1945, 1326 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-copper-icon-medium + rotate: false + xy: 1945, 1326 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-copper-icon-large + rotate: false + xy: 1219, 446 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +ore-copper-icon-small + rotate: false + xy: 1981, 1248 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +ore-lead-icon-full + rotate: false + xy: 1835, 1292 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-lead-icon-medium + rotate: false + xy: 1835, 1292 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-lead-icon-large + rotate: false + xy: 1269, 496 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +ore-lead-icon-small + rotate: false + xy: 1978, 1222 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +ore-scrap-icon-full + rotate: false + xy: 1869, 1292 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-scrap-icon-medium + rotate: false + xy: 1869, 1292 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-scrap-icon-large + rotate: false + xy: 1319, 546 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +ore-scrap-icon-small + rotate: false + xy: 1978, 1196 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +ore-thorium-icon-full + rotate: false + xy: 1903, 1292 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-thorium-icon-medium + rotate: false + xy: 1903, 1292 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-thorium-icon-large + rotate: false + xy: 1369, 596 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +ore-thorium-icon-small + rotate: false + xy: 1993, 753 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +ore-titanium-icon-full + rotate: false + xy: 1937, 1292 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-titanium-icon-medium + rotate: false + xy: 1937, 1292 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-titanium-icon-large + rotate: false + xy: 1219, 396 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +ore-titanium-icon-small + rotate: false + xy: 1993, 727 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +overdrive-projector-icon-large + rotate: false + xy: 1269, 446 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +overdrive-projector-icon-medium + rotate: false + xy: 1649, 1266 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +overdrive-projector-icon-small + rotate: false + xy: 1989, 701 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +overflow-gate-icon-large + rotate: false + xy: 1319, 496 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +overflow-gate-icon-small + rotate: false + xy: 1989, 675 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +pebbles-icon-large + rotate: false + xy: 1369, 546 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +pebbles-icon-small + rotate: false + xy: 1989, 649 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +phantom-factory-icon-full + rotate: false + xy: 621, 574 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +phantom-factory-icon-large + rotate: false + xy: 1219, 346 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +phantom-factory-icon-medium + rotate: false + xy: 1717, 1266 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +phantom-factory-icon-small + rotate: false + xy: 1985, 623 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +phase-conduit-icon-large + rotate: false + xy: 1269, 396 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +phase-conduit-icon-small + rotate: false + xy: 1985, 597 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +phase-conveyor-icon-large + rotate: false + xy: 1319, 446 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +phase-conveyor-icon-small + rotate: false + xy: 1985, 571 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +phase-wall-icon-large + rotate: false + xy: 1369, 496 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +phase-wall-icon-small + rotate: false + xy: 1985, 545 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +phase-wall-large-icon-large + rotate: false + xy: 1219, 296 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +phase-wall-large-icon-medium + rotate: false + xy: 1779, 1232 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +phase-wall-large-icon-small + rotate: false + xy: 1985, 519 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +phase-weaver-icon-full + rotate: false + xy: 555, 376 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +phase-weaver-icon-large + rotate: false + xy: 1269, 346 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +phase-weaver-icon-medium + rotate: false + xy: 1745, 1198 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +phase-weaver-icon-small + rotate: false + xy: 1985, 493 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +pine-icon-medium + rotate: false + xy: 1711, 1164 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pine-icon-small + rotate: false + xy: 1985, 467 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +plastanium-compressor-icon-large + rotate: false + xy: 1319, 396 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +plastanium-compressor-icon-medium + rotate: false + xy: 1677, 1096 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +plastanium-compressor-icon-small + rotate: false + xy: 1983, 441 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +pneumatic-drill-icon-full + rotate: false + xy: 687, 442 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +pneumatic-drill-icon-large + rotate: false + xy: 1369, 446 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +pneumatic-drill-icon-medium + rotate: false + xy: 1779, 1198 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pneumatic-drill-icon-small + rotate: false + xy: 1853, 418 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +power-node-icon-large + rotate: false + xy: 1219, 246 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +power-node-icon-small + rotate: false + xy: 1879, 416 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +power-node-large-icon-large + rotate: false + xy: 1269, 296 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +power-node-large-icon-medium + rotate: false + xy: 1711, 1130 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +power-node-large-icon-small + rotate: false + xy: 1905, 415 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +power-source-icon-large + rotate: false + xy: 1319, 346 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +power-source-icon-small + rotate: false + xy: 1931, 415 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +power-void-icon-large + rotate: false + xy: 1369, 396 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +power-void-icon-small + rotate: false + xy: 1957, 415 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +pulse-conduit-icon-full + rotate: false + xy: 1745, 1130 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pulse-conduit-icon-large + rotate: false + xy: 1219, 196 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +pulse-conduit-icon-small + rotate: false + xy: 1983, 415 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +pulverizer-icon-full + rotate: false + xy: 1739, 1028 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pulverizer-icon-large + rotate: false + xy: 1269, 246 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +pulverizer-icon-small + rotate: false + xy: 2005, 1314 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +pyratite-mixer-icon-large + rotate: false + xy: 1319, 296 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +pyratite-mixer-icon-medium + rotate: false + xy: 1735, 960 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pyratite-mixer-icon-small + rotate: false + xy: 2007, 1288 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +repair-point + rotate: false + xy: 1769, 994 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +repair-point-icon-full + rotate: false + xy: 1769, 960 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +repair-point-icon-large + rotate: false + xy: 1219, 146 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +repair-point-icon-small + rotate: false + xy: 2007, 1262 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +revenant-factory-icon-full + rotate: false + xy: 1265, 1658 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +revenant-factory-icon-large + rotate: false + xy: 1269, 196 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +revenant-factory-icon-medium + rotate: false + xy: 1735, 892 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +revenant-factory-icon-small + rotate: false + xy: 2007, 1236 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +ripple + rotate: false + xy: 293, 27 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +ripple-icon-full + rotate: false + xy: 391, 27 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +ripple-icon-large + rotate: false + xy: 1369, 296 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +ripple-icon-medium + rotate: false + xy: 1769, 926 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ripple-icon-small + rotate: false + xy: 2004, 1210 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +rock-icon-medium + rotate: false + xy: 1735, 858 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +rock-icon-small + rotate: false + xy: 2004, 1184 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +rocks-icon-large + rotate: false + xy: 1219, 96 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +rocks-icon-small + rotate: false + xy: 1978, 1170 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +rotary-pump-icon-large + rotate: false + xy: 1269, 146 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +rotary-pump-icon-medium + rotate: false + xy: 1769, 892 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +rotary-pump-icon-small + rotate: false + xy: 1975, 1144 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +router-icon-large + rotate: false + xy: 1319, 196 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +router-icon-small + rotate: false + xy: 1975, 1118 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +rtg-generator-icon-large + rotate: false + xy: 1369, 246 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +rtg-generator-icon-medium + rotate: false + xy: 1769, 858 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +rtg-generator-icon-small + rotate: false + xy: 1975, 1092 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +salt-icon-large + rotate: false + xy: 1269, 96 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +salt-icon-small + rotate: false + xy: 1975, 1066 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +saltrocks-icon-large + rotate: false + xy: 1319, 146 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +saltrocks-icon-small + rotate: false + xy: 1975, 1040 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +salvo + rotate: false + xy: 687, 178 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +salvo-icon-full + rotate: false + xy: 555, 46 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +salvo-icon-large + rotate: false + xy: 1369, 196 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +salvo-icon-medium + rotate: false + xy: 1769, 824 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +salvo-icon-small + rotate: false + xy: 2004, 1158 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +sand-icon-large + rotate: false + xy: 1319, 96 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +sand-icon-small + rotate: false + xy: 2001, 1132 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +sand-water-icon-large + rotate: false + xy: 1369, 146 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +sand-water-icon-small + rotate: false + xy: 2001, 1106 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +sandrocks-icon-large + rotate: false + xy: 1369, 96 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +sandrocks-icon-small + rotate: false + xy: 2001, 1080 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +scatter + rotate: false + xy: 762, 1168 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +scatter-icon-full + rotate: false + xy: 753, 1102 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +scatter-icon-large + rotate: false + xy: 1219, 46 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +scatter-icon-medium + rotate: false + xy: 1735, 756 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scatter-icon-small + rotate: false + xy: 2001, 1054 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +scorch + rotate: false + xy: 1769, 790 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scorch-icon-full + rotate: false + xy: 1769, 756 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scorch-icon-large + rotate: false + xy: 1269, 46 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +scorch-icon-small + rotate: false + xy: 2001, 1028 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +scrap-wall-gigantic-icon-large + rotate: false + xy: 1319, 46 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +scrap-wall-gigantic-icon-medium + rotate: false + xy: 1761, 620 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scrap-wall-gigantic-icon-small + rotate: false + xy: 1975, 1014 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +scrap-wall-huge-icon-large + rotate: false + xy: 1369, 46 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +scrap-wall-huge-icon-medium + rotate: false + xy: 1761, 586 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scrap-wall-huge-icon-small + rotate: false + xy: 2001, 1002 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +scrap-wall-icon-large + rotate: false + xy: 1119, 2 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +scrap-wall-icon-small + rotate: false + xy: 1791, 386 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +scrap-wall-large-icon-large + rotate: false + xy: 1169, 2 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +scrap-wall-large-icon-medium + rotate: false + xy: 1761, 552 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scrap-wall-large-icon-small + rotate: false + xy: 1791, 360 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +separator-icon-large + rotate: false + xy: 1505, 1321 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +separator-icon-medium + rotate: false + xy: 1761, 484 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +separator-icon-small + rotate: false + xy: 1817, 344 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +shale-boulder-icon-large + rotate: false + xy: 1497, 1271 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +shale-boulder-icon-small + rotate: false + xy: 1791, 297 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +shale-icon-large + rotate: false + xy: 1491, 1221 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +shale-icon-small + rotate: false + xy: 1817, 318 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +shalerocks-icon-large + rotate: false + xy: 1441, 1204 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +shalerocks-icon-small + rotate: false + xy: 1791, 271 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +shock-mine-icon-large + rotate: false + xy: 1433, 1154 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +shock-mine-icon-small + rotate: false + xy: 1817, 292 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +shrubs-icon-large + rotate: false + xy: 1483, 1121 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +shrubs-icon-small + rotate: false + xy: 1791, 245 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +silicon-smelter-icon-large + rotate: false + xy: 1433, 1104 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +silicon-smelter-icon-medium + rotate: false + xy: 1819, 1258 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +silicon-smelter-icon-small + rotate: false + xy: 1817, 266 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +snow-icon-large + rotate: false + xy: 1427, 1054 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +snow-icon-small + rotate: false + xy: 1791, 219 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +snowrock-icon-medium + rotate: false + xy: 1853, 1258 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +snowrock-icon-small + rotate: false + xy: 1817, 240 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +snowrocks-icon-large + rotate: false + xy: 1427, 1004 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +snowrocks-icon-small + rotate: false + xy: 1791, 193 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +solar-panel-icon-large + rotate: false + xy: 1427, 954 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +solar-panel-icon-small + rotate: false + xy: 1817, 214 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +solar-panel-large-icon-large + rotate: false + xy: 1427, 904 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +solar-panel-large-icon-medium + rotate: false + xy: 1921, 1258 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +solar-panel-large-icon-small + rotate: false + xy: 1791, 167 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +sorter-icon-large + rotate: false + xy: 1427, 854 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +sorter-icon-small + rotate: false + xy: 1817, 188 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +spawn-icon-large + rotate: false + xy: 1427, 804 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +spawn-icon-small + rotate: false + xy: 1791, 141 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +spectre + rotate: false + xy: 1785, 1658 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +spectre-icon-full + rotate: false + xy: 1915, 1658 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +spectre-icon-large + rotate: false + xy: 1427, 754 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +spectre-icon-medium + rotate: false + xy: 1813, 1190 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +spectre-icon-small + rotate: false + xy: 1817, 162 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +spirit-factory-icon-full + rotate: false + xy: 753, 574 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +spirit-factory-icon-large + rotate: false + xy: 1477, 1021 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +spirit-factory-icon-medium + rotate: false + xy: 1847, 1224 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +spirit-factory-icon-small + rotate: false + xy: 1791, 115 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +spore-cluster-icon-large + rotate: false + xy: 1477, 971 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +spore-cluster-icon-medium + rotate: false + xy: 1813, 1156 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +spore-cluster-icon-small + rotate: false + xy: 1817, 136 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +spore-moss-icon-large + rotate: false + xy: 1477, 921 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +spore-moss-icon-small + rotate: false + xy: 1791, 89 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +spore-pine-icon-medium + rotate: false + xy: 1847, 1190 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +spore-pine-icon-small + rotate: false + xy: 1817, 110 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +spore-press-icon-full + rotate: false + xy: 753, 178 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +spore-press-icon-large + rotate: false + xy: 1477, 871 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +spore-press-icon-medium + rotate: false + xy: 1881, 1224 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +spore-press-icon-small + rotate: false + xy: 1791, 63 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +sporerocks-icon-large + rotate: false + xy: 1477, 821 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +sporerocks-icon-small + rotate: false + xy: 1817, 84 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +stone-icon-large + rotate: false + xy: 1477, 771 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +stone-icon-small + rotate: false + xy: 1791, 37 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +surge-tower-icon-large + rotate: false + xy: 1477, 721 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +surge-tower-icon-medium + rotate: false + xy: 1813, 1122 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +surge-tower-icon-small + rotate: false + xy: 1817, 58 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +surge-wall-icon-large + rotate: false + xy: 1427, 704 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +surge-wall-icon-small + rotate: false + xy: 1817, 32 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +surge-wall-large-icon-large + rotate: false + xy: 1419, 654 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +surge-wall-large-icon-medium + rotate: false + xy: 1881, 1190 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +surge-wall-large-icon-small + rotate: false + xy: 1791, 11 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +swarmer + rotate: false + xy: 960, 1172 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +swarmer-icon-full + rotate: false + xy: 1026, 1172 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +swarmer-icon-large + rotate: false + xy: 1419, 554 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +swarmer-icon-medium + rotate: false + xy: 1915, 1224 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +swarmer-icon-small + rotate: false + xy: 1817, 6 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +tainted-water-icon-large + rotate: false + xy: 1419, 504 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +tainted-water-icon-small + rotate: false + xy: 293, 1 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +tar-icon-large + rotate: false + xy: 1419, 454 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +tar-icon-small + rotate: false + xy: 319, 1 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +tau-mech-pad-icon-large + rotate: false + xy: 1419, 304 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +tau-mech-pad-icon-medium + rotate: false + xy: 1813, 1088 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +tau-mech-pad-icon-small + rotate: false + xy: 345, 1 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +tendrils-icon-large + rotate: false + xy: 1419, 254 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +tendrils-icon-small + rotate: false + xy: 371, 1 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +thermal-generator-icon-large + rotate: false + xy: 1419, 204 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +thermal-generator-icon-medium + rotate: false + xy: 1847, 1122 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +thermal-generator-icon-small + rotate: false + xy: 397, 1 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +thermal-pump-icon-large + rotate: false + xy: 1419, 154 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +thermal-pump-icon-medium + rotate: false + xy: 1881, 1156 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +thermal-pump-icon-small + rotate: false + xy: 423, 1 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +thorium-reactor-icon-large + rotate: false + xy: 1419, 104 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +thorium-reactor-icon-medium + rotate: false + xy: 1915, 1190 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +thorium-reactor-icon-small + rotate: false + xy: 449, 1 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +thorium-wall-icon-large + rotate: false + xy: 1419, 54 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +thorium-wall-icon-small + rotate: false + xy: 2015, 701 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +thorium-wall-large-icon-large + rotate: false + xy: 1477, 671 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +thorium-wall-large-icon-medium + rotate: false + xy: 1881, 1122 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +thorium-wall-large-icon-small + rotate: false + xy: 2015, 675 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +thruster-icon-large + rotate: false + xy: 1469, 621 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +thruster-icon-medium + rotate: false + xy: 1915, 1156 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +thruster-icon-small + rotate: false + xy: 2015, 649 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +titan-factory-icon-full + rotate: false + xy: 1003, 1336 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +titan-factory-icon-large + rotate: false + xy: 1469, 571 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +titan-factory-icon-medium + rotate: false + xy: 1881, 1088 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titan-factory-icon-small + rotate: false + xy: 2011, 623 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +titanium-conveyor-icon-large + rotate: false + xy: 1469, 521 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +titanium-conveyor-icon-small + rotate: false + xy: 2011, 597 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +titanium-wall-icon-large + rotate: false + xy: 1469, 471 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +titanium-wall-icon-small + rotate: false + xy: 2011, 571 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +titanium-wall-large-icon-large + rotate: false + xy: 1469, 421 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +titanium-wall-large-icon-medium + rotate: false + xy: 1837, 884 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium-wall-large-icon-small + rotate: false + xy: 2011, 545 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +trident-ship-pad-icon-large + rotate: false + xy: 1469, 371 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +trident-ship-pad-icon-medium + rotate: false + xy: 1905, 952 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +trident-ship-pad-icon-small + rotate: false + xy: 2011, 519 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +turbine-generator-icon-large + rotate: false + xy: 1469, 321 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +turbine-generator-icon-medium + rotate: false + xy: 1803, 816 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +turbine-generator-icon-small + rotate: false + xy: 2011, 493 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +unit-icon-chaos-array + rotate: false + xy: 1005, 1532 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +unit-icon-crawler + rotate: false + xy: 1469, 271 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +unit-icon-dagger + rotate: false + xy: 1469, 221 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +unit-icon-eradicator + rotate: false + xy: 1206, 1922 + size: 152, 124 + orig: 152, 124 + offset: 0, 0 + index: -1 +unit-icon-eruptor + rotate: false + xy: 1843, 1494 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +unit-icon-fortress + rotate: false + xy: 1909, 1494 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +unit-icon-titan + rotate: false + xy: 1241, 1428 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +unloader-icon-large + rotate: false + xy: 1469, 171 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +unloader-icon-small + rotate: false + xy: 2011, 467 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +vault-icon-large + rotate: false + xy: 1469, 121 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +vault-icon-medium + rotate: false + xy: 1871, 884 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +vault-icon-small + rotate: false + xy: 2009, 441 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +water-extractor-icon-full + rotate: false + xy: 1307, 1428 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +water-extractor-icon-large + rotate: false + xy: 1469, 71 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +water-extractor-icon-medium + rotate: false + xy: 1905, 918 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +water-extractor-icon-small + rotate: false + xy: 2009, 415 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +water-icon-large + rotate: false + xy: 1469, 21 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +water-icon-small + rotate: false + xy: 1965, 987 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +wave + rotate: false + xy: 1307, 1296 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +wave-icon-full + rotate: false + xy: 1373, 1362 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +wave-icon-large + rotate: false + xy: 1419, 4 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +wave-icon-medium + rotate: false + xy: 1803, 782 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +wave-icon-small + rotate: false + xy: 1965, 961 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +white-tree-dead-icon-large + rotate: false + xy: 1860, 1394 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +white-tree-dead-icon-medium + rotate: false + xy: 1837, 816 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +white-tree-dead-icon-small + rotate: false + xy: 1965, 935 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +white-tree-icon-large + rotate: false + xy: 1910, 1394 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +white-tree-icon-medium + rotate: false + xy: 1871, 850 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +white-tree-icon-small + rotate: false + xy: 1965, 909 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +wraith-factory-icon-full + rotate: false + xy: 1373, 1296 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +wraith-factory-icon-large + rotate: false + xy: 1555, 1321 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +wraith-factory-icon-medium + rotate: false + xy: 1905, 884 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +wraith-factory-icon-small + rotate: false + xy: 1965, 883 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-biomatter + rotate: false + xy: 1723, 361 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-blast-compound + rotate: false + xy: 1723, 327 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-coal + rotate: false + xy: 1723, 293 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-copper + rotate: false + xy: 1723, 259 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-graphite + rotate: false + xy: 1723, 225 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-lead + rotate: false + xy: 1723, 191 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-metaglass + rotate: false + xy: 1723, 157 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-phase-fabric + rotate: false + xy: 1723, 123 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-plastanium + rotate: false + xy: 1723, 89 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-pyratite + rotate: false + xy: 1723, 55 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-sand + rotate: false + xy: 1723, 21 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-scrap + rotate: false + xy: 1757, 361 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-silicon + rotate: false + xy: 1757, 327 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-spore-pod + rotate: false + xy: 1757, 259 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-surge-alloy + rotate: false + xy: 1757, 225 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-thorium + rotate: false + xy: 1757, 191 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-titanium + rotate: false + xy: 1757, 157 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +liquid-cryofluid + rotate: false + xy: 1639, 1334 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +liquid-oil + rotate: false + xy: 1673, 1334 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +liquid-slag + rotate: false + xy: 1775, 1334 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +liquid-water + rotate: false + xy: 1631, 1300 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +alpha-mech + rotate: false + xy: 1860, 1444 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +alpha-mech-base + rotate: false + xy: 819, 698 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +alpha-mech-leg + rotate: false + xy: 1910, 1444 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +delta-mech + rotate: false + xy: 919, 722 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +delta-mech-base + rotate: false + xy: 869, 622 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +delta-mech-leg + rotate: false + xy: 919, 672 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +omega-mech + rotate: false + xy: 1628, 1436 + size: 56, 56 + orig: 56, 56 + offset: 0, 0 + index: -1 +omega-mech-armor + rotate: false + xy: 621, 640 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +omega-mech-base + rotate: false + xy: 819, 914 + size: 56, 56 + orig: 56, 56 + offset: 0, 0 + index: -1 +omega-mech-leg + rotate: false + xy: 1686, 1436 + size: 56, 56 + orig: 56, 56 + offset: 0, 0 + index: -1 +tau-mech + rotate: false + xy: 819, 798 + size: 56, 56 + orig: 56, 56 + offset: 0, 0 + index: -1 +tau-mech-base + rotate: false + xy: 1419, 404 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +tau-mech-leg + rotate: false + xy: 1419, 354 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +dart-ship + rotate: false + xy: 869, 722 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +glaive-ship + rotate: false + xy: 1975, 1502 + size: 56, 56 + orig: 56, 56 + offset: 0, 0 + index: -1 +javelin-ship + rotate: false + xy: 1169, 602 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +javelin-ship-shield + rotate: false + xy: 1169, 552 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +trident-ship + rotate: false + xy: 1802, 1436 + size: 56, 56 + orig: 56, 56 + offset: 0, 0 + index: -1 +blank + rotate: false + xy: 1164, 1175 + size: 1, 1 + orig: 1, 1 + offset: 0, 0 + index: -1 +circle + rotate: false + xy: 323, 1757 + size: 201, 201 + orig: 201, 201 + offset: 0, 0 + index: -1 +clear + rotate: false + xy: 2004, 1245 + size: 1, 1 + orig: 1, 1 + offset: 0, 0 + index: -1 +shape-3 + rotate: false + xy: 1505, 1429 + size: 63, 63 + orig: 63, 63 + offset: 0, 0 + index: -1 +bar + rotate: false + xy: 1824, 438 + size: 27, 36 + split: 9, 9, 9, 9 + orig: 27, 36 + offset: 0, 0 + index: -1 +bar-top + rotate: false + xy: 1795, 438 + size: 27, 36 + split: 9, 10, 9, 10 + orig: 27, 36 offset: 0, 0 index: -1 button rotate: false - xy: 680, 448 - size: 24, 40 - split: 10, 10, 6, 10 - orig: 24, 40 + xy: 1527, 858 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 + offset: 0, 0 + index: -1 +button-disabled + rotate: false + xy: 786, 1763 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 offset: 0, 0 index: -1 button-down rotate: false - xy: 43, 86 - size: 24, 40 - split: 10, 10, 6, 10 - orig: 24, 40 + xy: 2010, 2019 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 + offset: 0, 0 + index: -1 +button-edge-1 + rotate: false + xy: 1527, 974 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 + offset: 0, 0 + index: -1 +button-edge-2 + rotate: false + xy: 824, 1763 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 + offset: 0, 0 + index: -1 +button-edge-3 + rotate: false + xy: 2010, 1990 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 + offset: 0, 0 + index: -1 +button-edge-4 + rotate: false + xy: 1527, 945 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 offset: 0, 0 index: -1 button-over rotate: false - xy: 43, 86 - size: 24, 40 - split: 10, 10, 6, 10 - orig: 24, 40 + xy: 2010, 1961 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 offset: 0, 0 index: -1 -button-map +button-right rotate: false - xy: 495, 245 - size: 24, 40 - split: 10, 10, 5, 10 - orig: 24, 40 + xy: 1527, 887 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 offset: 0, 0 index: -1 -button-map-down +button-right-down rotate: false - xy: 126, 130 - size: 24, 40 - split: 10, 10, 5, 10 - orig: 24, 40 + xy: 1527, 916 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 offset: 0, 0 index: -1 -button-map-over +button-right-over rotate: false - xy: 126, 130 - size: 24, 40 - split: 10, 10, 5, 10 - orig: 24, 40 + xy: 2010, 1932 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 offset: 0, 0 index: -1 button-select rotate: false - xy: 627, 317 + xy: 1837, 790 size: 24, 24 split: 4, 4, 4, 4 orig: 24, 24 offset: 0, 0 index: -1 +check-disabled + rotate: false + xy: 1541, 1177 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 check-off rotate: false - xy: 521, 253 - size: 28, 32 - orig: 28, 32 + xy: 1575, 1177 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 check-on rotate: false - xy: 706, 456 - size: 28, 32 - orig: 28, 32 + xy: 1575, 1143 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +check-on-disabled + rotate: false + xy: 1575, 1109 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 check-on-over rotate: false - xy: 27, 26 - size: 28, 32 - orig: 28, 32 + xy: 1575, 1075 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 check-over rotate: false - xy: 178, 138 - size: 28, 32 - orig: 28, 32 + xy: 1575, 1041 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 clear rotate: false - xy: 663, 382 + xy: 290, 1234 size: 10, 10 orig: 10, 10 offset: 0, 0 index: -1 +content-background + rotate: false + xy: 1527, 742 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 + offset: 0, 0 + index: -1 +content-background-locked + rotate: false + xy: 1527, 829 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 + offset: 0, 0 + index: -1 +content-background-noitems + rotate: false + xy: 1527, 800 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 + offset: 0, 0 + index: -1 +content-background-over + rotate: false + xy: 1527, 771 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 + offset: 0, 0 + index: -1 cursor rotate: false - xy: 57, 26 + xy: 1227, 1146 size: 4, 4 orig: 4, 4 offset: 0, 0 index: -1 discord-banner rotate: false - xy: 1, 128 - size: 81, 42 - orig: 81, 42 + xy: 1, 12 + size: 84, 45 + orig: 84, 45 offset: 0, 0 index: -1 -discord-banner-over +empty-sector rotate: false - xy: 1, 128 - size: 81, 42 - orig: 81, 42 - offset: 0, 0 - index: -1 -controller-cursor - rotate: false - xy: 810, 495 - size: 16, 16 - orig: 16, 16 + xy: 1599, 769 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-about rotate: false - xy: 706, 440 - size: 14, 14 - orig: 14, 14 + xy: 969, 372 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-about-small + rotate: false + xy: 1633, 701 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-add rotate: false - xy: 736, 436 - size: 14, 14 - orig: 14, 14 + xy: 1019, 414 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-add-small + rotate: false + xy: 1667, 735 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-admin rotate: false - xy: 752, 436 - size: 14, 14 - orig: 14, 14 + xy: 869, 222 + size: 48, 48 + orig: 48, 48 offset: 0, 0 index: -1 icon-admin-small rotate: false - xy: 256, 127 - size: 6, 6 - orig: 6, 6 + xy: 1667, 701 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -icon-areaDelete +icon-admin-small-small rotate: false - xy: 669, 358 - size: 10, 10 - orig: 10, 10 + xy: 1667, 701 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-arrow rotate: false - xy: 178, 122 - size: 14, 14 - orig: 14, 14 + xy: 919, 264 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-arrow-16 + rotate: false + xy: 919, 264 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-arrow-16-small + rotate: false + xy: 1587, 360 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-arrow-small + rotate: false + xy: 1587, 360 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-arrow-down rotate: false - xy: 669, 346 - size: 10, 10 - orig: 10, 10 + xy: 969, 322 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-arrow-down-small + rotate: false + xy: 1587, 326 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-arrow-left rotate: false - xy: 669, 334 - size: 10, 10 - orig: 10, 10 + xy: 1019, 364 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-arrow-left-small + rotate: false + xy: 1587, 292 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-arrow-right rotate: false - xy: 669, 322 - size: 10, 10 - orig: 10, 10 + xy: 869, 172 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-arrow-right-small + rotate: false + xy: 1587, 258 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-arrow-up rotate: false - xy: 736, 424 - size: 10, 10 - orig: 10, 10 + xy: 919, 214 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-arrow-up-small + rotate: false + xy: 1587, 224 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-back rotate: false - xy: 398, 177 - size: 16, 16 - orig: 16, 16 + xy: 969, 272 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-back-small + rotate: false + xy: 1587, 190 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-ban rotate: false - xy: 208, 119 - size: 14, 14 - orig: 14, 14 + xy: 1019, 314 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-ban-small + rotate: false + xy: 1587, 156 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-break + rotate: false + xy: 869, 122 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-break-small + rotate: false + xy: 1587, 122 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-cancel rotate: false - xy: 224, 119 - size: 14, 14 - orig: 14, 14 + xy: 919, 164 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-cancel-small + rotate: false + xy: 1587, 88 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-chat rotate: false - xy: 748, 424 - size: 10, 10 - orig: 10, 10 + xy: 969, 222 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-chat-small + rotate: false + xy: 1587, 54 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-check rotate: false - xy: 240, 119 - size: 14, 14 - orig: 14, 14 + xy: 1019, 264 + size: 48, 48 + orig: 48, 48 offset: 0, 0 index: -1 -icon-close +icon-check-small rotate: false - xy: 1, 86 - size: 40, 40 - orig: 40, 40 + xy: 1587, 20 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -icon-close-down +icon-copy rotate: false - xy: 84, 130 - size: 40, 40 - orig: 40, 40 + xy: 869, 72 + size: 48, 48 + orig: 48, 48 offset: 0, 0 index: -1 -icon-close-over +icon-copy-small rotate: false - xy: 453, 245 - size: 40, 40 - orig: 40, 40 + xy: 1701, 973 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-crafting rotate: false - xy: 760, 424 - size: 10, 10 - orig: 10, 10 + xy: 919, 114 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-crafting-small + rotate: false + xy: 1701, 939 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-cursor rotate: false - xy: 575, 272 - size: 10, 10 - orig: 10, 10 + xy: 969, 172 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-cursor-small + rotate: false + xy: 1701, 905 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-database + rotate: false + xy: 1019, 214 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-database-small + rotate: false + xy: 1701, 871 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-defense rotate: false - xy: 587, 272 - size: 10, 10 - orig: 10, 10 + xy: 969, 122 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-defense-small + rotate: false + xy: 1701, 837 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-dev-builds rotate: false - xy: 49, 10 - size: 14, 14 - orig: 14, 14 + xy: 1019, 164 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-dev-builds-small + rotate: false + xy: 1701, 803 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-diagonal + rotate: false + xy: 919, 64 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-diagonal-small + rotate: false + xy: 1701, 769 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-discord rotate: false - xy: 416, 179 - size: 14, 14 - orig: 14, 14 + xy: 969, 72 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-discord-small + rotate: false + xy: 1701, 735 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-distribution rotate: false - xy: 73, 36 - size: 10, 10 - orig: 10, 10 + xy: 1019, 114 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-distribution-small + rotate: false + xy: 1701, 701 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-donate rotate: false - xy: 432, 182 - size: 14, 14 - orig: 14, 14 + xy: 1019, 64 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-donate-small + rotate: false + xy: 1599, 667 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-dots rotate: false - xy: 264, 127 - size: 14, 14 - orig: 14, 14 + xy: 869, 22 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-dots-small + rotate: false + xy: 1633, 667 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-editor rotate: false - xy: 280, 127 - size: 14, 14 - orig: 14, 14 + xy: 919, 14 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-editor-small + rotate: false + xy: 1667, 667 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-effect + rotate: false + xy: 969, 22 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-effect-small + rotate: false + xy: 1701, 667 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-elevation + rotate: false + xy: 1019, 14 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-elevation-small + rotate: false + xy: 1591, 633 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-eraser + rotate: false + xy: 1960, 1444 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-eraser-small + rotate: false + xy: 1591, 599 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-exit rotate: false - xy: 296, 127 - size: 14, 14 - orig: 14, 14 + xy: 1077, 1122 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-exit-small + rotate: false + xy: 1625, 633 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-file + rotate: false + xy: 1077, 1072 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-file-image + rotate: false + xy: 1077, 1022 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-file-image-small + rotate: false + xy: 1591, 565 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-file-small + rotate: false + xy: 1625, 599 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-file-text rotate: false - xy: 312, 127 - size: 14, 14 - orig: 14, 14 + xy: 1077, 972 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-file-text-small + rotate: false + xy: 1659, 633 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-fill rotate: false - xy: 846, 495 - size: 16, 16 - orig: 16, 16 + xy: 1077, 922 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-fill-small + rotate: false + xy: 1591, 531 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-floppy rotate: false - xy: 342, 125 - size: 14, 14 - orig: 14, 14 + xy: 1077, 872 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-floppy-16 + rotate: false + xy: 1077, 822 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-floppy-16-small + rotate: false + xy: 1625, 565 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-floppy-small + rotate: false + xy: 1659, 599 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-folder rotate: false - xy: 525, 237 - size: 14, 14 - orig: 14, 14 + xy: 1077, 772 + size: 48, 48 + orig: 48, 48 offset: 0, 0 index: -1 icon-folder-parent rotate: false - xy: 541, 237 - size: 14, 14 - orig: 14, 14 + xy: 1069, 722 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-folder-parent-small + rotate: false + xy: 1693, 633 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-folder-small + rotate: false + xy: 1591, 497 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-github rotate: false - xy: 256, 111 - size: 14, 14 - orig: 14, 14 + xy: 1069, 672 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-github-small + rotate: false + xy: 1625, 531 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-google-play rotate: false - xy: 272, 111 - size: 14, 14 - orig: 14, 14 + xy: 1069, 622 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-google-play-small + rotate: false + xy: 1659, 565 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-grid rotate: false - xy: 864, 495 - size: 16, 16 - orig: 16, 16 + xy: 1069, 572 + size: 48, 48 + orig: 48, 48 offset: 0, 0 index: -1 -icon-hold +icon-grid-small rotate: false - xy: 178, 110 - size: 10, 10 - orig: 10, 10 - offset: 0, 0 - index: -1 -icon-holdDelete - rotate: false - xy: 557, 245 - size: 10, 10 - orig: 10, 10 + xy: 1693, 599 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-home rotate: false - xy: 288, 111 - size: 14, 14 - orig: 14, 14 + xy: 1069, 522 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-home-small + rotate: false + xy: 1591, 463 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-host rotate: false - xy: 304, 111 - size: 14, 14 - orig: 14, 14 + xy: 1069, 472 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-host-small + rotate: false + xy: 1625, 497 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-info rotate: false - xy: 557, 233 - size: 10, 10 - orig: 10, 10 + xy: 1069, 422 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-info-small + rotate: false + xy: 1659, 531 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-itch.io rotate: false - xy: 525, 221 - size: 14, 14 - orig: 14, 14 + xy: 1069, 372 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-itch.io-small + rotate: false + xy: 1693, 565 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-item + rotate: false + xy: 1069, 322 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-item-small + rotate: false + xy: 1591, 429 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-line rotate: false - xy: 882, 495 - size: 16, 16 - orig: 16, 16 + xy: 1069, 272 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-line-small + rotate: false + xy: 1625, 463 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-link rotate: false - xy: 541, 221 - size: 14, 14 - orig: 14, 14 + xy: 1069, 222 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-link-small + rotate: false + xy: 1659, 497 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-liquid + rotate: false + xy: 1069, 172 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-liquid-consume + rotate: false + xy: 1069, 122 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-liquid-consume-small + rotate: false + xy: 1693, 531 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-liquid-small + rotate: false + xy: 1591, 395 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-load rotate: false - xy: 509, 213 - size: 14, 14 - orig: 14, 14 + xy: 1069, 72 + size: 48, 48 + orig: 48, 48 offset: 0, 0 index: -1 icon-load-image rotate: false - xy: 900, 495 - size: 16, 16 - orig: 16, 16 + xy: 1069, 22 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-load-image-small + rotate: false + xy: 1625, 429 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-load-map rotate: false - xy: 918, 495 - size: 16, 16 - orig: 16, 16 + xy: 1241, 1246 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-load-map-small + rotate: false + xy: 1659, 463 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-load-small + rotate: false + xy: 1693, 497 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-loading rotate: false - xy: 936, 495 - size: 16, 16 - orig: 16, 16 + xy: 1291, 1246 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-loading-small + rotate: false + xy: 1625, 395 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-locked + rotate: false + xy: 1341, 1246 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-locked-small + rotate: false + xy: 1659, 429 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-map + rotate: false + xy: 1391, 1246 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-map-small + rotate: false + xy: 1693, 463 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-menu rotate: false - xy: 557, 221 - size: 10, 10 - orig: 10, 10 + xy: 1233, 1196 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-menu-large + rotate: false + xy: 1283, 1196 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-menu-large-small + rotate: false + xy: 1659, 395 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-menu-small + rotate: false + xy: 1693, 429 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-missing + rotate: false + xy: 1333, 1196 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-missing-small + rotate: false + xy: 1693, 395 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-none rotate: false - xy: 557, 209 - size: 10, 10 - orig: 10, 10 + xy: 1383, 1196 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-none-small + rotate: false + xy: 1621, 361 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-paste + rotate: false + xy: 1233, 1146 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-paste-small + rotate: false + xy: 1621, 327 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-pause rotate: false - xy: 352, 113 - size: 10, 10 - orig: 10, 10 + xy: 1283, 1146 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-pause-small + rotate: false + xy: 1655, 361 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-pencil rotate: false - xy: 954, 495 - size: 16, 16 - orig: 16, 16 + xy: 1333, 1146 + size: 48, 48 + orig: 48, 48 offset: 0, 0 index: -1 icon-pencil-small rotate: false - xy: 320, 111 - size: 14, 14 - orig: 14, 14 + xy: 1621, 293 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-pick rotate: false - xy: 972, 495 - size: 16, 16 - orig: 16, 16 + xy: 1383, 1146 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-pick-small + rotate: false + xy: 1655, 327 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-play rotate: false - xy: 1002, 483 - size: 10, 10 - orig: 10, 10 + xy: 1441, 1254 + size: 48, 48 + orig: 48, 48 offset: 0, 0 index: -1 icon-play-2 rotate: false - xy: 525, 205 - size: 14, 14 - orig: 14, 14 + xy: 1127, 1102 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-play-2-small + rotate: false + xy: 1689, 361 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-play-custom + rotate: false + xy: 1127, 1052 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-play-custom-small + rotate: false + xy: 1621, 259 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-play-small + rotate: false + xy: 1655, 293 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-players rotate: false - xy: 681, 370 - size: 10, 10 - orig: 10, 10 + xy: 1177, 1102 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-players-small + rotate: false + xy: 1689, 327 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-power rotate: false - xy: 681, 358 - size: 10, 10 - orig: 10, 10 + xy: 1127, 1002 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-power-small + rotate: false + xy: 1621, 225 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-production rotate: false - xy: 681, 346 - size: 10, 10 - orig: 10, 10 + xy: 1177, 1052 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-production-small + rotate: false + xy: 1655, 259 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-quit rotate: false - xy: 541, 205 - size: 14, 14 - orig: 14, 14 + xy: 1127, 952 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-quit-small + rotate: false + xy: 1689, 293 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-redo rotate: false - xy: 990, 495 - size: 16, 16 - orig: 16, 16 + xy: 1177, 1002 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-redo-small + rotate: false + xy: 1621, 191 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-refresh rotate: false - xy: 336, 109 - size: 14, 14 - orig: 14, 14 + xy: 1127, 902 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-refresh-small + rotate: false + xy: 1655, 225 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-rename rotate: false - xy: 627, 301 - size: 14, 14 - orig: 14, 14 + xy: 1177, 952 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-rename-small + rotate: false + xy: 1689, 259 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-resize rotate: false - xy: 656, 426 - size: 16, 16 - orig: 16, 16 + xy: 1127, 852 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-resize-small + rotate: false + xy: 1621, 157 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-rotate rotate: false - xy: 625, 285 - size: 14, 14 - orig: 14, 14 + xy: 1177, 902 + size: 48, 48 + orig: 48, 48 offset: 0, 0 index: -1 icon-rotate-arrow rotate: false - xy: 643, 301 - size: 14, 14 - orig: 14, 14 + xy: 1127, 802 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-rotate-arrow-small + rotate: false + xy: 1655, 191 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-rotate-left rotate: false - xy: 641, 285 - size: 14, 14 - orig: 14, 14 + xy: 1177, 852 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-rotate-left-small + rotate: false + xy: 1689, 225 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-rotate-right rotate: false - xy: 659, 302 - size: 14, 14 - orig: 14, 14 + xy: 1177, 802 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-rotate-right-small + rotate: false + xy: 1621, 123 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-rotate-small + rotate: false + xy: 1655, 157 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-save rotate: false - xy: 667, 410 - size: 14, 14 - orig: 14, 14 + xy: 1227, 1096 + size: 48, 48 + orig: 48, 48 offset: 0, 0 index: -1 icon-save-image rotate: false - xy: 69, 110 - size: 16, 16 - orig: 16, 16 + xy: 1227, 1046 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-save-image-small + rotate: false + xy: 1689, 191 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-save-map rotate: false - xy: 69, 92 - size: 16, 16 - orig: 16, 16 + xy: 1277, 1096 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-save-map-small + rotate: false + xy: 1621, 89 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-save-small + rotate: false + xy: 1655, 123 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-settings rotate: false - xy: 681, 334 - size: 10, 10 - orig: 10, 10 + xy: 1227, 996 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-settings-small + rotate: false + xy: 1689, 157 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-spray + rotate: false + xy: 1277, 1046 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-spray-small + rotate: false + xy: 1621, 55 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-terrain rotate: false - xy: 69, 74 - size: 16, 16 - orig: 16, 16 + xy: 1327, 1096 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-terrain-small + rotate: false + xy: 1655, 89 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-tools rotate: false - xy: 667, 394 - size: 14, 14 - orig: 14, 14 + xy: 1227, 946 + size: 48, 48 + orig: 48, 48 offset: 0, 0 index: -1 -icon-touch +icon-tools-small rotate: false - xy: 681, 322 - size: 10, 10 - orig: 10, 10 - offset: 0, 0 - index: -1 -icon-touchDelete - rotate: false - xy: 675, 310 - size: 10, 10 - orig: 10, 10 + xy: 1689, 123 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-trash rotate: false - xy: 762, 474 - size: 14, 14 - orig: 14, 14 + xy: 1277, 996 + size: 48, 48 + orig: 48, 48 offset: 0, 0 index: -1 icon-trash-16 rotate: false - xy: 87, 112 - size: 16, 16 - orig: 16, 16 + xy: 1327, 1046 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-trash-16-small + rotate: false + xy: 1621, 21 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-trash-small + rotate: false + xy: 1655, 55 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-tree + rotate: false + xy: 1377, 1096 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-tree-small + rotate: false + xy: 1689, 89 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-trello rotate: false - xy: 762, 458 - size: 14, 14 - orig: 14, 14 + xy: 1227, 896 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-trello-small + rotate: false + xy: 1655, 21 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-turret + rotate: false + xy: 1277, 946 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-turret-small + rotate: false + xy: 1689, 55 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-tutorial rotate: false - xy: 778, 475 - size: 14, 14 - orig: 14, 14 + xy: 1327, 996 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-tutorial-small + rotate: false + xy: 1689, 21 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-undo rotate: false - xy: 87, 94 - size: 16, 16 - orig: 16, 16 + xy: 1377, 1046 + size: 48, 48 + orig: 48, 48 offset: 0, 0 index: -1 -icon-weapon +icon-undo-small rotate: false - xy: 675, 298 - size: 10, 10 - orig: 10, 10 + xy: 1727, 633 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-units + rotate: false + xy: 1227, 846 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-units-small + rotate: false + xy: 1727, 599 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-unlocks + rotate: false + xy: 1277, 896 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-unlocks-small + rotate: false + xy: 1727, 565 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-upgrade + rotate: false + xy: 1327, 946 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-upgrade-small + rotate: false + xy: 1727, 531 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-wiki rotate: false - xy: 778, 459 - size: 14, 14 - orig: 14, 14 + xy: 1377, 996 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-wiki-small + rotate: false + xy: 1727, 497 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 icon-zoom rotate: false - xy: 105, 112 - size: 16, 16 - orig: 16, 16 + xy: 1277, 846 + size: 48, 48 + orig: 48, 48 offset: 0, 0 index: -1 icon-zoom-small rotate: false - xy: 794, 478 - size: 14, 14 - orig: 14, 14 + xy: 1727, 463 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +info-banner + rotate: false + xy: 204, 1199 + size: 84, 45 + orig: 84, 45 + offset: 0, 0 + index: -1 +inventory + rotate: false + xy: 1859, 652 + size: 24, 40 + split: 10, 10, 10, 14 + orig: 24, 40 offset: 0, 0 index: -1 logotext rotate: false - xy: 143, 172 - size: 89, 21 - orig: 89, 21 + xy: 1, 1960 + size: 579, 86 + orig: 579, 86 offset: 0, 0 index: -1 pane rotate: false - xy: 736, 452 - size: 24, 36 - split: 10, 10, 5, 5 - orig: 24, 36 + xy: 1519, 608 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 offset: 0, 0 index: -1 -pane-button +pane-2 rotate: false - xy: 627, 343 - size: 24, 36 - split: 10, 10, 5, 5 - orig: 24, 36 + xy: 1519, 637 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 offset: 0, 0 index: -1 scroll rotate: false - xy: 208, 135 + xy: 1791, 323 size: 24, 35 split: 10, 10, 6, 5 orig: 24, 35 @@ -2418,220 +8645,3752 @@ scroll index: -1 scroll-horizontal rotate: false - xy: 566, 284 + xy: 1127, 1152 size: 35, 24 split: 6, 5, 10, 10 orig: 35, 24 offset: 0, 0 index: -1 -scroll-knob-horizontal +scroll-knob-horizontal-black rotate: false - xy: 27, 60 + xy: 744, 1896 size: 40, 24 - split: 10, 6, 0, 24 - pad: -1, -1, 10, 10 + split: 11, 10, 10, 10 orig: 40, 24 offset: 0, 0 index: -1 -scroll-knob-vertical - rotate: false - xy: 152, 130 - size: 24, 40 - split: 10, 10, 6, 10 - orig: 24, 40 - offset: 0, 0 - index: -1 scroll-knob-vertical-black rotate: false - xy: 1, 2 + xy: 1817, 370 size: 24, 40 split: 10, 10, 6, 10 orig: 24, 40 offset: 0, 0 index: -1 +sector-select + rotate: false + xy: 1761, 518 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 selection rotate: false - xy: 424, 195 + xy: 1975, 1192 size: 1, 1 orig: 1, 1 offset: 0, 0 index: -1 slider rotate: false - xy: 599, 274 + xy: 2001, 1160 size: 1, 8 orig: 1, 8 offset: 0, 0 index: -1 slider-knob rotate: false - xy: 627, 381 - size: 22, 44 - orig: 22, 44 + xy: 1979, 1354 + size: 29, 38 + orig: 29, 38 offset: 0, 0 index: -1 slider-knob-down rotate: false - xy: 656, 444 - size: 22, 44 - orig: 22, 44 + xy: 1387, 6 + size: 29, 38 + orig: 29, 38 offset: 0, 0 index: -1 slider-knob-over rotate: false - xy: 656, 444 - size: 22, 44 - orig: 22, 44 + xy: 1387, 6 + size: 29, 38 + orig: 29, 38 offset: 0, 0 index: -1 slider-vertical rotate: false - xy: 27, 1 + xy: 1631, 1279 size: 8, 1 orig: 8, 1 offset: 0, 0 index: -1 -text-sides +underline rotate: false - xy: 1, 172 - size: 140, 21 - orig: 140, 21 + xy: 1519, 416 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 offset: 0, 0 index: -1 -text-sides-down +underline-2 rotate: false - xy: 424, 287 - size: 140, 21 - orig: 140, 21 + xy: 1519, 503 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 offset: 0, 0 index: -1 -text-sides-over +underline-disabled rotate: false - xy: 627, 490 - size: 140, 21 - orig: 140, 21 + xy: 1519, 474 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 offset: 0, 0 index: -1 -textfield +underline-red rotate: false - xy: 234, 135 - size: 28, 28 - split: 6, 6, 6, 6 - orig: 28, 28 - offset: 0, 0 - index: -1 -textfield-over - rotate: false - xy: 234, 165 - size: 28, 28 - split: 2, 2, 2, 2 - orig: 28, 28 + xy: 1519, 445 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 offset: 0, 0 index: -1 white rotate: false - xy: 69, 1 + xy: 1519, 666 size: 3, 3 orig: 3, 3 offset: 0, 0 index: -1 -window - rotate: false - xy: 627, 427 - size: 27, 61 - split: 8, 8, 44, 11 - orig: 27, 61 - offset: 0, 0 - index: -1 window-empty rotate: false - xy: 424, 224 + xy: 1949, 1195 size: 27, 61 split: 8, 8, 44, 11 orig: 27, 61 offset: 0, 0 index: -1 -beam +chaos-array rotate: false - xy: 69, 48 - size: 8, 8 - orig: 8, 8 + xy: 485, 1626 + size: 128, 128 + orig: 128, 128 offset: 0, 0 index: -1 -beam-equip +chaos-array-base rotate: false - xy: 565, 274 - size: 8, 8 - orig: 8, 8 + xy: 656, 1756 + size: 128, 128 + orig: 128, 128 offset: 0, 0 index: -1 -blaster +chaos-array-leg rotate: false - xy: 782, 426 - size: 8, 8 - orig: 8, 8 + xy: 615, 1626 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +crawler + rotate: false + xy: 927, 972 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +crawler-base + rotate: false + xy: 977, 1022 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +crawler-leg + rotate: false + xy: 877, 872 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +dagger + rotate: false + xy: 877, 822 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +dagger-base + rotate: false + xy: 927, 872 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +dagger-leg + rotate: false + xy: 1027, 972 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +draug + rotate: false + xy: 869, 522 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +eradicator + rotate: false + xy: 744, 1922 + size: 152, 124 + orig: 152, 124 + offset: 0, 0 + index: -1 +eradicator-base + rotate: false + xy: 898, 1922 + size: 152, 124 + orig: 152, 124 + offset: 0, 0 + index: -1 +eradicator-leg + rotate: false + xy: 1052, 1922 + size: 152, 124 + orig: 152, 124 + offset: 0, 0 + index: -1 +eruptor + rotate: false + xy: 555, 970 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +eruptor-base + rotate: false + xy: 621, 1036 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +eruptor-leg + rotate: false + xy: 687, 1102 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +fortress + rotate: false + xy: 555, 904 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +fortress-base + rotate: false + xy: 621, 970 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +titan-base + rotate: false + xy: 621, 970 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +fortress-leg + rotate: false + xy: 687, 1036 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +ghoul + rotate: false + xy: 1956, 1844 + size: 72, 72 + orig: 72, 72 + offset: 0, 0 + index: -1 +lich + rotate: false + xy: 1, 1396 + size: 216, 240 + orig: 216, 240 + offset: 0, 0 + index: -1 +phantom + rotate: false + xy: 819, 856 + size: 56, 56 + orig: 56, 56 + offset: 0, 0 + index: -1 +power-cell + rotate: false + xy: 1744, 1436 + size: 56, 56 + orig: 56, 56 + offset: 0, 0 + index: -1 +reaper + rotate: false + xy: 1, 1638 + size: 320, 320 + orig: 320, 320 + offset: 0, 0 + index: -1 +revenant + rotate: false + xy: 1135, 1548 + size: 112, 112 + orig: 112, 112 + offset: 0, 0 + index: -1 +spirit + rotate: false + xy: 1483, 1071 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +titan + rotate: false + xy: 1447, 1494 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +titan-leg + rotate: false + xy: 1513, 1494 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +wraith + rotate: false + xy: 1960, 1394 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +artillery-equip + rotate: false + xy: 819, 590 + size: 48, 56 + orig: 48, 56 offset: 0, 0 index: -1 blaster-equip rotate: false - xy: 73, 26 - size: 8, 8 - orig: 8, 8 + xy: 819, 340 + size: 48, 48 + orig: 48, 48 offset: 0, 0 index: -1 -clustergun +bomber-equip rotate: false - xy: 352, 103 - size: 8, 8 - orig: 8, 8 + xy: 819, 290 + size: 48, 48 + orig: 48, 48 offset: 0, 0 index: -1 -clustergun-equip +missiles-equip rotate: false - xy: 1014, 477 - size: 8, 8 - orig: 8, 8 + xy: 819, 290 + size: 48, 48 + orig: 48, 48 offset: 0, 0 index: -1 -shockgun +chain-blaster-equip rotate: false - xy: 589, 242 - size: 8, 8 - orig: 8, 8 + xy: 819, 140 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +chaos-equip + rotate: false + xy: 819, 1030 + size: 56, 136 + orig: 56, 136 + offset: 0, 0 + index: -1 +eradication-equip + rotate: false + xy: 513, 1334 + size: 96, 192 + orig: 96, 192 + offset: 0, 0 + index: -1 +eruption-equip + rotate: false + xy: 919, 514 + size: 48, 56 + orig: 48, 56 + offset: 0, 0 + index: -1 +flakgun-equip + rotate: false + xy: 969, 572 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +flamethrower-equip + rotate: false + xy: 1019, 614 + size: 48, 56 + orig: 48, 56 + offset: 0, 0 + index: -1 +heal-blaster-equip + rotate: false + xy: 869, 322 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +lich-missiles-equip + rotate: false + xy: 1119, 302 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +reaper-gun-equip + rotate: false + xy: 1369, 346 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +revenant-missiles-equip + rotate: false + xy: 1319, 246 + size: 48, 48 + orig: 48, 48 offset: 0, 0 index: -1 shockgun-equip rotate: false - xy: 589, 232 - size: 8, 8 - orig: 8, 8 + xy: 1491, 1171 + size: 48, 48 + orig: 48, 48 offset: 0, 0 index: -1 -triblaster +swarmer-equip rotate: false - xy: 149, 8 - size: 8, 8 - orig: 8, 8 + xy: 1419, 604 + size: 48, 48 + orig: 48, 48 offset: 0, 0 index: -1 -triblaster-equip + +sprites2.png +size: 2048,1024 +format: RGBA8888 +filter: Nearest,Nearest +repeat: none +alloy-smelter-icon-editor rotate: false - xy: 159, 8 - size: 8, 8 - orig: 8, 8 + xy: 1, 21 + size: 96, 96 + orig: 96, 96 offset: 0, 0 index: -1 -vulcan +arc-icon-editor rotate: false - xy: 200, 109 - size: 8, 8 - orig: 8, 8 + xy: 261, 143 + size: 32, 32 + orig: 32, 32 offset: 0, 0 index: -1 -vulcan-equip +battery-icon-editor rotate: false - xy: 210, 109 - size: 8, 8 - orig: 8, 8 + xy: 427, 143 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +battery-large-icon-editor + rotate: false + xy: 745, 925 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +blast-drill-icon-editor + rotate: false + xy: 1, 249 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +blast-mixer-icon-editor + rotate: false + xy: 745, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +block-border-editor + rotate: false + xy: 569, 397 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +bridge-conduit-icon-editor + rotate: false + xy: 461, 143 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +bridge-conveyor-icon-editor + rotate: false + xy: 603, 397 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +char-icon-editor + rotate: false + xy: 495, 143 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-char1 + rotate: false + xy: 495, 143 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +clear-editor + rotate: false + xy: 261, 376 + size: 1, 1 + orig: 1, 1 + offset: 0, 0 + index: -1 +cliffs-icon-editor + rotate: false + xy: 637, 397 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +coal-centrifuge-icon-editor + rotate: false + xy: 811, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +combustion-generator-icon-editor + rotate: false + xy: 671, 397 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-icon-editor + rotate: false + xy: 427, 109 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +container-icon-editor + rotate: false + xy: 877, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +conveyor-icon-editor + rotate: false + xy: 461, 109 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +copper-wall-icon-editor + rotate: false + xy: 495, 109 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +copper-wall-large-icon-editor + rotate: false + xy: 943, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +core-foundation-icon-editor + rotate: false + xy: 323, 731 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +core-nucleus-icon-editor + rotate: false + xy: 323, 861 + size: 160, 160 + orig: 160, 160 + offset: 0, 0 + index: -1 +core-shard-icon-editor + rotate: false + xy: 99, 21 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +craters-icon-editor + rotate: false + xy: 529, 143 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-craters1 + rotate: false + xy: 529, 143 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +crawler-factory-icon-editor + rotate: false + xy: 1009, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cryofluidmixer-icon-editor + rotate: false + xy: 1075, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cultivator-icon-editor + rotate: false + xy: 1141, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cyclone-icon-editor + rotate: false + xy: 843, 925 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +dagger-factory-icon-editor + rotate: false + xy: 1207, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +dark-metal-icon-editor + rotate: false + xy: 529, 109 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dark-panel-1-icon-editor + rotate: false + xy: 295, 11 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-dark-panel-1 + rotate: false + xy: 295, 11 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dark-panel-2-icon-editor + rotate: false + xy: 329, 11 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-dark-panel-2 + rotate: false + xy: 329, 11 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dark-panel-3-icon-editor + rotate: false + xy: 569, 363 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-dark-panel-3 + rotate: false + xy: 569, 363 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dark-panel-4-icon-editor + rotate: false + xy: 603, 363 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-dark-panel-4 + rotate: false + xy: 603, 363 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dark-panel-5-icon-editor + rotate: false + xy: 637, 363 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-dark-panel-5 + rotate: false + xy: 637, 363 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dark-panel-6-icon-editor + rotate: false + xy: 671, 363 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-dark-panel-6 + rotate: false + xy: 671, 363 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +darksand-icon-editor + rotate: false + xy: 555, 329 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-darksand1 + rotate: false + xy: 555, 329 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +darksand-tainted-water-icon-editor + rotate: false + xy: 555, 295 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-darksand-tainted-water + rotate: false + xy: 555, 295 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +darksand-water-icon-editor + rotate: false + xy: 589, 329 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-darksand-water + rotate: false + xy: 589, 329 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dart-mech-pad-icon-editor + rotate: false + xy: 1273, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +deepwater-icon-editor + rotate: false + xy: 555, 261 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-deepwater + rotate: false + xy: 555, 261 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +delta-mech-pad-icon-editor + rotate: false + xy: 1339, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +differential-generator-icon-editor + rotate: false + xy: 941, 925 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +distributor-icon-editor + rotate: false + xy: 1405, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +door-icon-editor + rotate: false + xy: 589, 295 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +door-large-icon-editor + rotate: false + xy: 1471, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +draug-factory-icon-editor + rotate: false + xy: 1537, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +dunerocks-icon-editor + rotate: false + xy: 623, 329 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +duo-icon-editor + rotate: false + xy: 555, 227 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-char2 + rotate: false + xy: 589, 261 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-char3 + rotate: false + xy: 623, 295 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-clear + rotate: false + xy: 733, 881 + size: 10, 10 + orig: 10, 10 + offset: 0, 0 + index: -1 +editor-craters2 + rotate: false + xy: 657, 329 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-craters3 + rotate: false + xy: 555, 193 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-darksand2 + rotate: false + xy: 589, 227 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-darksand3 + rotate: false + xy: 623, 261 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-grass1 + rotate: false + xy: 657, 295 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +grass-icon-editor + rotate: false + xy: 657, 295 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-grass2 + rotate: false + xy: 589, 193 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-grass3 + rotate: false + xy: 623, 227 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-holostone1 + rotate: false + xy: 657, 261 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +holostone-icon-editor + rotate: false + xy: 657, 261 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-holostone2 + rotate: false + xy: 623, 193 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-holostone3 + rotate: false + xy: 657, 227 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-hotrock1 + rotate: false + xy: 657, 193 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +hotrock-icon-editor + rotate: false + xy: 657, 193 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-hotrock2 + rotate: false + xy: 563, 159 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-hotrock3 + rotate: false + xy: 563, 125 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ice-snow1 + rotate: false + xy: 631, 125 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ice-snow-icon-editor + rotate: false + xy: 631, 125 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ice-snow2 + rotate: false + xy: 665, 159 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ice-snow3 + rotate: false + xy: 665, 125 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ice1 + rotate: false + xy: 597, 159 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ice-icon-editor + rotate: false + xy: 597, 159 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ice2 + rotate: false + xy: 597, 125 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ice3 + rotate: false + xy: 631, 159 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ignarock1 + rotate: false + xy: 563, 91 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ignarock-icon-editor + rotate: false + xy: 563, 91 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ignarock2 + rotate: false + xy: 597, 91 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ignarock3 + rotate: false + xy: 631, 91 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-magmarock1 + rotate: false + xy: 665, 91 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +magmarock-icon-editor + rotate: false + xy: 665, 91 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-magmarock2 + rotate: false + xy: 691, 329 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-magmarock3 + rotate: false + xy: 691, 295 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-metal-floor + rotate: false + xy: 691, 261 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +metal-floor-icon-editor + rotate: false + xy: 691, 261 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-metal-floor-2 + rotate: false + xy: 691, 227 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +metal-floor-2-icon-editor + rotate: false + xy: 691, 227 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-metal-floor-3 + rotate: false + xy: 691, 193 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +metal-floor-3-icon-editor + rotate: false + xy: 691, 193 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-metal-floor-5 + rotate: false + xy: 699, 159 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +metal-floor-5-icon-editor + rotate: false + xy: 699, 159 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-metal-floor-damaged1 + rotate: false + xy: 699, 125 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +metal-floor-damaged-icon-editor + rotate: false + xy: 699, 125 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-metal-floor-damaged2 + rotate: false + xy: 699, 91 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-metal-floor-damaged3 + rotate: false + xy: 705, 397 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-moss1 + rotate: false + xy: 705, 363 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +moss-icon-editor + rotate: false + xy: 705, 363 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-moss2 + rotate: false + xy: 725, 329 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-moss3 + rotate: false + xy: 725, 295 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-coal1 + rotate: false + xy: 725, 261 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-coal2 + rotate: false + xy: 725, 227 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-coal3 + rotate: false + xy: 725, 193 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-copper1 + rotate: false + xy: 733, 159 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-copper2 + rotate: false + xy: 733, 125 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-copper3 + rotate: false + xy: 733, 91 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-lead1 + rotate: false + xy: 363, 35 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-lead2 + rotate: false + xy: 363, 1 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-lead3 + rotate: false + xy: 403, 75 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-scrap1 + rotate: false + xy: 437, 75 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-scrap2 + rotate: false + xy: 471, 75 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-scrap3 + rotate: false + xy: 505, 75 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-thorium1 + rotate: false + xy: 397, 35 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-thorium2 + rotate: false + xy: 397, 1 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-thorium3 + rotate: false + xy: 431, 41 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-titanium1 + rotate: false + xy: 431, 7 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-titanium2 + rotate: false + xy: 465, 41 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-titanium3 + rotate: false + xy: 465, 7 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-pebbles1 + rotate: false + xy: 499, 41 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-pebbles2 + rotate: false + xy: 499, 7 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-pebbles3 + rotate: false + xy: 533, 41 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-salt + rotate: false + xy: 533, 7 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +salt-icon-editor + rotate: false + xy: 533, 7 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-sand-water + rotate: false + xy: 601, 23 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +sand-water-icon-editor + rotate: false + xy: 601, 23 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-sand1 + rotate: false + xy: 567, 57 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +sand-icon-editor + rotate: false + xy: 567, 57 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-sand2 + rotate: false + xy: 567, 23 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-sand3 + rotate: false + xy: 601, 57 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-shale1 + rotate: false + xy: 635, 57 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +shale-icon-editor + rotate: false + xy: 635, 57 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-shale2 + rotate: false + xy: 635, 23 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-shale3 + rotate: false + xy: 669, 57 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-snow1 + rotate: false + xy: 669, 23 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +snow-icon-editor + rotate: false + xy: 669, 23 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-snow2 + rotate: false + xy: 703, 57 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-snow3 + rotate: false + xy: 703, 23 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-spawn + rotate: false + xy: 737, 57 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-spore-moss1 + rotate: false + xy: 737, 23 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +spore-moss-icon-editor + rotate: false + xy: 737, 23 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-spore-moss2 + rotate: false + xy: 733, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-spore-moss3 + rotate: false + xy: 767, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-stone1 + rotate: false + xy: 801, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +stone-icon-editor + rotate: false + xy: 801, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-stone2 + rotate: false + xy: 835, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-stone3 + rotate: false + xy: 869, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-tainted-water + rotate: false + xy: 903, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +tainted-water-icon-editor + rotate: false + xy: 903, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-tar + rotate: false + xy: 937, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +tar-icon-editor + rotate: false + xy: 937, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-tendrils1 + rotate: false + xy: 971, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-tendrils2 + rotate: false + xy: 1005, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-tendrils3 + rotate: false + xy: 1039, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-water + rotate: false + xy: 1073, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +water-icon-editor + rotate: false + xy: 1073, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +force-projector-icon-editor + rotate: false + xy: 1039, 925 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +fortress-factory-icon-editor + rotate: false + xy: 1137, 925 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +fuse-icon-editor + rotate: false + xy: 1235, 925 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +ghoul-factory-icon-editor + rotate: false + xy: 1333, 925 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +glaive-ship-pad-icon-editor + rotate: false + xy: 1431, 925 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +graphite-press-icon-editor + rotate: false + xy: 1603, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +hail-icon-editor + rotate: false + xy: 1107, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icerocks-icon-editor + rotate: false + xy: 1141, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +impact-reactor-icon-editor + rotate: false + xy: 485, 893 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +incinerator-icon-editor + rotate: false + xy: 1175, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-source-icon-editor + rotate: false + xy: 1209, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-void-icon-editor + rotate: false + xy: 1243, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +javelin-ship-pad-icon-editor + rotate: false + xy: 1669, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +junction-icon-editor + rotate: false + xy: 1277, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +kiln-icon-editor + rotate: false + xy: 1735, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +lancer-icon-editor + rotate: false + xy: 1801, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +laser-drill-icon-editor + rotate: false + xy: 1529, 925 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +launch-pad-icon-editor + rotate: false + xy: 1627, 925 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +launch-pad-large-icon-editor + rotate: false + xy: 1, 119 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +liquid-junction-icon-editor + rotate: false + xy: 1311, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +liquid-router-icon-editor + rotate: false + xy: 1345, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +liquid-source-icon-editor + rotate: false + xy: 1379, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +liquid-tank-icon-editor + rotate: false + xy: 1725, 925 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +mass-driver-icon-editor + rotate: false + xy: 1823, 925 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +mechanical-drill-icon-editor + rotate: false + xy: 1867, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +mechanical-pump-icon-editor + rotate: false + xy: 1413, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +meltdown-icon-editor + rotate: false + xy: 131, 249 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +melter-icon-editor + rotate: false + xy: 1447, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +mend-projector-icon-editor + rotate: false + xy: 1933, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +mender-icon-editor + rotate: false + xy: 1481, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +multi-press-icon-editor + rotate: false + xy: 1921, 925 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +oil-extractor-icon-editor + rotate: false + xy: 323, 373 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +omega-mech-pad-icon-editor + rotate: false + xy: 197, 21 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +overdrive-projector-icon-editor + rotate: false + xy: 485, 827 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +overflow-gate-icon-editor + rotate: false + xy: 1515, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pebbles-icon-editor + rotate: false + xy: 1549, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +phantom-factory-icon-editor + rotate: false + xy: 551, 827 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +phase-conduit-icon-editor + rotate: false + xy: 1583, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +phase-conveyor-icon-editor + rotate: false + xy: 1617, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +phase-wall-icon-editor + rotate: false + xy: 1651, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +phase-wall-large-icon-editor + rotate: false + xy: 617, 827 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +phase-weaver-icon-editor + rotate: false + xy: 453, 761 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +pine-icon-editor + rotate: false + xy: 1999, 875 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +plastanium-compressor-icon-editor + rotate: false + xy: 519, 761 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +pneumatic-drill-icon-editor + rotate: false + xy: 453, 695 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +power-node-icon-editor + rotate: false + xy: 1685, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +power-node-large-icon-editor + rotate: false + xy: 519, 695 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +power-source-icon-editor + rotate: false + xy: 1719, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +power-void-icon-editor + rotate: false + xy: 1753, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pulse-conduit-icon-editor + rotate: false + xy: 1787, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pulverizer-icon-editor + rotate: false + xy: 1821, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pyratite-mixer-icon-editor + rotate: false + xy: 585, 761 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +repair-point-icon-editor + rotate: false + xy: 1855, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +revenant-factory-icon-editor + rotate: false + xy: 323, 601 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +ripple-icon-editor + rotate: false + xy: 261, 275 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +rock-icon-editor + rotate: false + xy: 1999, 825 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +rocks-icon-editor + rotate: false + xy: 1889, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +rotary-pump-icon-editor + rotate: false + xy: 453, 629 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +router-icon-editor + rotate: false + xy: 1923, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +rtg-generator-icon-editor + rotate: false + xy: 519, 629 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +saltrocks-icon-editor + rotate: false + xy: 1957, 825 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +salvo-icon-editor + rotate: false + xy: 585, 695 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +sandrocks-icon-editor + rotate: false + xy: 717, 791 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scatter-icon-editor + rotate: false + xy: 453, 563 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +scorch-icon-editor + rotate: false + xy: 717, 757 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scrap-wall-gigantic-icon-editor + rotate: false + xy: 615, 893 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +scrap-wall-huge-icon-editor + rotate: false + xy: 261, 177 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +scrap-wall-icon-editor + rotate: false + xy: 751, 791 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scrap-wall-large-icon-editor + rotate: false + xy: 519, 563 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +separator-icon-editor + rotate: false + xy: 585, 629 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +shale-boulder-icon-editor + rotate: false + xy: 717, 723 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +shalerocks-icon-editor + rotate: false + xy: 785, 791 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +shock-mine-icon-editor + rotate: false + xy: 751, 757 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +shrubs-icon-editor + rotate: false + xy: 717, 689 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +silicon-smelter-icon-editor + rotate: false + xy: 453, 497 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +snowrock-icon-editor + rotate: false + xy: 519, 381 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +snowrocks-icon-editor + rotate: false + xy: 819, 791 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +solar-panel-icon-editor + rotate: false + xy: 785, 757 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +solar-panel-large-icon-editor + rotate: false + xy: 359, 275 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +sorter-icon-editor + rotate: false + xy: 751, 723 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +spawn-icon-editor + rotate: false + xy: 717, 655 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +spectre-icon-editor + rotate: false + xy: 131, 119 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +spirit-factory-icon-editor + rotate: false + xy: 519, 497 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +spore-cluster-icon-editor + rotate: false + xy: 361, 69 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +spore-pine-icon-editor + rotate: false + xy: 683, 843 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +spore-press-icon-editor + rotate: false + xy: 585, 563 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +sporerocks-icon-editor + rotate: false + xy: 853, 791 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +surge-tower-icon-editor + rotate: false + xy: 585, 497 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +surge-wall-icon-editor + rotate: false + xy: 819, 757 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +surge-wall-large-icon-editor + rotate: false + xy: 519, 431 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +swarmer-icon-editor + rotate: false + xy: 585, 431 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +tau-mech-pad-icon-editor + rotate: false + xy: 651, 761 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +tendrils-icon-editor + rotate: false + xy: 785, 723 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +thermal-generator-icon-editor + rotate: false + xy: 651, 695 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +thermal-pump-icon-editor + rotate: false + xy: 359, 177 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +thorium-reactor-icon-editor + rotate: false + xy: 421, 373 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +thorium-wall-icon-editor + rotate: false + xy: 751, 689 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +thorium-wall-large-icon-editor + rotate: false + xy: 651, 629 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +thruster-icon-editor + rotate: false + xy: 323, 471 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +titan-factory-icon-editor + rotate: false + xy: 457, 275 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +titanium-conveyor-icon-editor + rotate: false + xy: 717, 621 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium-wall-icon-editor + rotate: false + xy: 887, 791 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium-wall-large-icon-editor + rotate: false + xy: 651, 563 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +trident-ship-pad-icon-editor + rotate: false + xy: 651, 497 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +turbine-generator-icon-editor + rotate: false + xy: 651, 431 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +unloader-icon-editor + rotate: false + xy: 853, 757 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +vault-icon-editor + rotate: false + xy: 457, 177 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +water-extractor-icon-editor + rotate: false + xy: 295, 111 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +wave-icon-editor + rotate: false + xy: 295, 45 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +white-tree-dead-icon-editor + rotate: false + xy: 1, 701 + size: 320, 320 + orig: 320, 320 + offset: 0, 0 + index: -1 +white-tree-icon-editor + rotate: false + xy: 1, 379 + size: 320, 320 + orig: 320, 320 + offset: 0, 0 + index: -1 +wraith-factory-icon-editor + rotate: false + xy: 361, 111 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 + +sprites3.png +size: 2048,512 +format: RGBA8888 +filter: Nearest,Nearest +repeat: none +char1 + rotate: false + xy: 1775, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +char2 + rotate: false + xy: 315, 59 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +char3 + rotate: false + xy: 349, 59 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +cliffs1 + rotate: false + xy: 1809, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +coal1 + rotate: false + xy: 383, 59 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +coal2 + rotate: false + xy: 1843, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +coal3 + rotate: false + xy: 417, 59 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +copper1 + rotate: false + xy: 1877, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +copper2 + rotate: false + xy: 451, 59 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +copper3 + rotate: false + xy: 1911, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +craters1 + rotate: false + xy: 485, 59 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +craters2 + rotate: false + xy: 1945, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +craters3 + rotate: false + xy: 519, 59 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +craters4 + rotate: false + xy: 1979, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +craters5 + rotate: false + xy: 553, 59 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +craters6 + rotate: false + xy: 2013, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dark-metal-large + rotate: false + xy: 1, 27 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +dark-metal1 + rotate: false + xy: 1429, 217 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dark-metal2 + rotate: false + xy: 1495, 283 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dark-panel-1 + rotate: false + xy: 1627, 347 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dark-panel-1-edge + rotate: false + xy: 1, 93 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +dark-panel-2 + rotate: false + xy: 1463, 217 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dark-panel-2-edge + rotate: false + xy: 645, 415 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +dark-panel-3 + rotate: false + xy: 645, 185 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dark-panel-3-edge + rotate: false + xy: 99, 93 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +dark-panel-4 + rotate: false + xy: 679, 185 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dark-panel-4-edge + rotate: false + xy: 645, 317 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +dark-panel-5 + rotate: false + xy: 713, 185 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dark-panel-5-edge + rotate: false + xy: 743, 415 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +dark-panel-6 + rotate: false + xy: 747, 185 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dark-panel-6-edge + rotate: false + xy: 197, 93 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +darksand-edge + rotate: false + xy: 645, 219 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +darksand-tainted-water + rotate: false + xy: 883, 185 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +darksand-tainted-water-edge + rotate: false + xy: 743, 317 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +darksand-water + rotate: false + xy: 917, 185 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +darksand-water-edge + rotate: false + xy: 841, 415 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +darksand1 + rotate: false + xy: 781, 185 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +darksand2 + rotate: false + xy: 815, 185 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +darksand3 + rotate: false + xy: 849, 185 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +deepwater + rotate: false + xy: 951, 185 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +deepwater-edge + rotate: false + xy: 295, 93 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +dunerocks-large + rotate: false + xy: 1527, 447 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +dunerocks1 + rotate: false + xy: 985, 185 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dunerocks2 + rotate: false + xy: 1019, 185 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +edge + rotate: false + xy: 1053, 185 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +edge-stencil + rotate: false + xy: 743, 219 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +edgier + rotate: false + xy: 1087, 185 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +grass-edge + rotate: false + xy: 841, 317 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +grass1 + rotate: false + xy: 1121, 185 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +grass2 + rotate: false + xy: 1155, 185 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +grass3 + rotate: false + xy: 1189, 185 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +holostone-edge + rotate: false + xy: 939, 415 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +holostone1 + rotate: false + xy: 1223, 185 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +holostone2 + rotate: false + xy: 1257, 185 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +holostone3 + rotate: false + xy: 1291, 185 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +hotrock1 + rotate: false + xy: 1325, 185 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +hotrock2 + rotate: false + xy: 1359, 185 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +hotrock3 + rotate: false + xy: 1393, 185 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ice-edge + rotate: false + xy: 393, 93 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +ice-snow-edge + rotate: false + xy: 841, 219 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +ice-snow1 + rotate: false + xy: 673, 151 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ice-snow2 + rotate: false + xy: 707, 151 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ice-snow3 + rotate: false + xy: 741, 151 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ice1 + rotate: false + xy: 1427, 183 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ice2 + rotate: false + xy: 1461, 183 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ice3 + rotate: false + xy: 639, 151 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icerocks-large + rotate: false + xy: 1429, 251 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +icerocks1 + rotate: false + xy: 775, 151 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icerocks2 + rotate: false + xy: 809, 151 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ignarock-edge + rotate: false + xy: 939, 317 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +ignarock1 + rotate: false + xy: 843, 151 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ignarock2 + rotate: false + xy: 877, 151 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ignarock3 + rotate: false + xy: 911, 151 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +lead1 + rotate: false + xy: 945, 151 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +lead2 + rotate: false + xy: 979, 151 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +lead3 + rotate: false + xy: 1013, 151 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +magmarock1 + rotate: false + xy: 1047, 151 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +magmarock2 + rotate: false + xy: 1081, 151 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +magmarock3 + rotate: false + xy: 1115, 151 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +metal-floor + rotate: false + xy: 1149, 151 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +metal-floor-2 + rotate: false + xy: 1183, 151 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +metal-floor-2-edge + rotate: false + xy: 1037, 415 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +metal-floor-3 + rotate: false + xy: 1217, 151 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +metal-floor-3-edge + rotate: false + xy: 491, 93 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +metal-floor-5 + rotate: false + xy: 1251, 151 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +metal-floor-5-edge + rotate: false + xy: 939, 219 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +metal-floor-damaged-edge + rotate: false + xy: 1037, 317 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +metal-floor-damaged1 + rotate: false + xy: 1285, 151 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +metal-floor-damaged2 + rotate: false + xy: 1319, 151 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +metal-floor-damaged3 + rotate: false + xy: 1353, 151 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +metal-floor-edge + rotate: false + xy: 1135, 415 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +moss-edge + rotate: false + xy: 1037, 219 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +moss1 + rotate: false + xy: 1387, 151 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +moss2 + rotate: false + xy: 1421, 149 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +moss3 + rotate: false + xy: 1455, 149 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-coal1 + rotate: false + xy: 1529, 297 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-coal2 + rotate: false + xy: 1563, 297 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-coal3 + rotate: false + xy: 1597, 297 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-copper1 + rotate: false + xy: 349, 25 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-copper2 + rotate: false + xy: 383, 25 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-copper3 + rotate: false + xy: 417, 25 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-lead1 + rotate: false + xy: 451, 25 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-lead2 + rotate: false + xy: 485, 25 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-lead3 + rotate: false + xy: 519, 25 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-scrap1 + rotate: false + xy: 553, 25 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-scrap2 + rotate: false + xy: 1661, 363 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-scrap3 + rotate: false + xy: 1709, 413 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-thorium1 + rotate: false + xy: 1775, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-thorium2 + rotate: false + xy: 1809, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-thorium3 + rotate: false + xy: 1843, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-titanium1 + rotate: false + xy: 1877, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-titanium2 + rotate: false + xy: 1911, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-titanium3 + rotate: false + xy: 1945, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pebbles1 + rotate: false + xy: 1979, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pebbles2 + rotate: false + xy: 2013, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pebbles3 + rotate: false + xy: 1661, 329 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pine + rotate: false + xy: 589, 141 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +rock1 + rotate: false + xy: 1527, 331 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +rock2 + rotate: false + xy: 1659, 397 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +rocks-large + rotate: false + xy: 67, 27 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +rocks1 + rotate: false + xy: 1709, 379 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +rocks2 + rotate: false + xy: 1695, 345 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +salt + rotate: false + xy: 1695, 311 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +salt-edge + rotate: false + xy: 1135, 317 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +saltrocks-large + rotate: false + xy: 1527, 381 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +saltrocks1 + rotate: false + xy: 1529, 263 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +saltrocks2 + rotate: false + xy: 1563, 263 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +sand-edge + rotate: false + xy: 1233, 415 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +sand-water + rotate: false + xy: 639, 117 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +sand-water-edge + rotate: false + xy: 1135, 219 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +sand1 + rotate: false + xy: 1597, 263 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +sand2 + rotate: false + xy: 1729, 345 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +sand3 + rotate: false + xy: 1729, 311 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +sandrocks-large + rotate: false + xy: 1593, 447 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +sandrocks1 + rotate: false + xy: 673, 117 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +sandrocks2 + rotate: false + xy: 707, 117 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scrap1 + rotate: false + xy: 741, 117 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scrap2 + rotate: false + xy: 775, 117 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scrap3 + rotate: false + xy: 809, 117 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +shale-boulder1 + rotate: false + xy: 945, 117 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +shale-boulder2 + rotate: false + xy: 979, 117 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +shale-edge + rotate: false + xy: 1233, 317 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +shale1 + rotate: false + xy: 843, 117 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +shale2 + rotate: false + xy: 877, 117 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +shale3 + rotate: false + xy: 911, 117 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +shalerocks-large + rotate: false + xy: 133, 27 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +shalerocks1 + rotate: false + xy: 1013, 117 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +shalerocks2 + rotate: false + xy: 1047, 117 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +shrubs-large + rotate: false + xy: 1593, 381 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +shrubs1 + rotate: false + xy: 1081, 117 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +shrubs2 + rotate: false + xy: 1115, 117 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +snow-edge + rotate: false + xy: 1331, 415 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +snow1 + rotate: false + xy: 1149, 117 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +snow2 + rotate: false + xy: 1183, 117 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +snow3 + rotate: false + xy: 1217, 117 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +snowrock1 + rotate: false + xy: 1725, 463 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +snowrock2 + rotate: false + xy: 265, 43 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +snowrocks-large + rotate: false + xy: 1659, 447 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +snowrocks1 + rotate: false + xy: 1251, 117 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +snowrocks2 + rotate: false + xy: 1285, 117 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +spawn + rotate: false + xy: 1319, 117 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +spore-cluster1 + rotate: false + xy: 265, 1 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +spore-cluster2 + rotate: false + xy: 307, 1 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +spore-cluster3 + rotate: false + xy: 589, 99 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +spore-moss-edge + rotate: false + xy: 1233, 219 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +spore-moss1 + rotate: false + xy: 1353, 117 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +spore-moss2 + rotate: false + xy: 1387, 117 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +spore-moss3 + rotate: false + xy: 1421, 115 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +spore-pine + rotate: false + xy: 1577, 331 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +sporerocks-large + rotate: false + xy: 199, 27 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +sporerocks1 + rotate: false + xy: 1455, 115 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +sporerocks2 + rotate: false + xy: 631, 83 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +stone-edge + rotate: false + xy: 1331, 317 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +stone1 + rotate: false + xy: 665, 83 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +stone2 + rotate: false + xy: 699, 83 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +stone3 + rotate: false + xy: 733, 83 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +tainted-water + rotate: false + xy: 767, 83 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +tainted-water-edge + rotate: false + xy: 1429, 415 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +tar + rotate: false + xy: 801, 83 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +tar-edge + rotate: false + xy: 1331, 219 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +tendrils1 + rotate: false + xy: 835, 83 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +tendrils2 + rotate: false + xy: 869, 83 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +tendrils3 + rotate: false + xy: 903, 83 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +thorium1 + rotate: false + xy: 937, 83 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +thorium2 + rotate: false + xy: 971, 83 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +thorium3 + rotate: false + xy: 1005, 83 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium1 + rotate: false + xy: 1039, 83 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium2 + rotate: false + xy: 1073, 83 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium3 + rotate: false + xy: 1107, 83 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +water + rotate: false + xy: 1141, 83 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +water-edge + rotate: false + xy: 1429, 317 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +white-tree + rotate: false + xy: 1, 191 + size: 320, 320 + orig: 320, 320 + offset: 0, 0 + index: -1 +white-tree-dead + rotate: false + xy: 323, 191 + size: 320, 320 + orig: 320, 320 offset: 0, 0 index: -1 diff --git a/core/assets/sprites/sprites.png b/core/assets/sprites/sprites.png index 077a888d44..e9e47cf11e 100644 Binary files a/core/assets/sprites/sprites.png and b/core/assets/sprites/sprites.png differ diff --git a/core/assets/sprites/sprites2.png b/core/assets/sprites/sprites2.png new file mode 100644 index 0000000000..12e26dfe8f Binary files /dev/null and b/core/assets/sprites/sprites2.png differ diff --git a/core/assets/sprites/sprites3.png b/core/assets/sprites/sprites3.png new file mode 100644 index 0000000000..006c713d6b Binary files /dev/null and b/core/assets/sprites/sprites3.png differ diff --git a/core/assets/sprites/sprites_fallback.atlas b/core/assets/sprites/sprites_fallback.atlas new file mode 100644 index 0000000000..533906dbb6 --- /dev/null +++ b/core/assets/sprites/sprites_fallback.atlas @@ -0,0 +1,12414 @@ + +sprites_fallback.png +size: 1024,1024 +format: RGBA8888 +filter: Nearest,Nearest +repeat: none +blast-drill + rotate: false + xy: 850, 269 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +blast-drill-rim + rotate: false + xy: 842, 9 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +blast-drill-rotator + rotate: false + xy: 219, 29 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +blast-drill-top + rotate: false + xy: 349, 29 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +core-nucleus + rotate: false + xy: 526, 403 + size: 160, 160 + orig: 160, 160 + offset: 0, 0 + index: -1 +core-nucleus-icon-editor + rotate: false + xy: 526, 403 + size: 160, 160 + orig: 160, 160 + offset: 0, 0 + index: -1 +blast-drill-icon-editor + rotate: false + xy: 842, 139 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +blast-drill-icon-full + rotate: false + xy: 842, 139 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +white-tree-dead-icon-editor + rotate: false + xy: 1, 243 + size: 320, 320 + orig: 320, 320 + offset: 0, 0 + index: -1 +white-tree-icon-editor + rotate: false + xy: 323, 565 + size: 320, 320 + orig: 320, 320 + offset: 0, 0 + index: -1 +circle-shadow + rotate: false + xy: 323, 159 + size: 201, 201 + orig: 201, 201 + offset: 0, 0 + index: -1 +cracks-5-0 + rotate: false + xy: 526, 241 + size: 160, 160 + orig: 160, 160 + offset: 0, 0 + index: -1 +cracks-5-1 + rotate: false + xy: 526, 79 + size: 160, 160 + orig: 160, 160 + offset: 0, 0 + index: -1 +cracks-5-2 + rotate: false + xy: 645, 813 + size: 160, 160 + orig: 160, 160 + offset: 0, 0 + index: -1 +cracks-5-3 + rotate: false + xy: 645, 651 + size: 160, 160 + orig: 160, 160 + offset: 0, 0 + index: -1 +cracks-5-4 + rotate: false + xy: 807, 813 + size: 160, 160 + orig: 160, 160 + offset: 0, 0 + index: -1 +cracks-5-5 + rotate: false + xy: 807, 651 + size: 160, 160 + orig: 160, 160 + offset: 0, 0 + index: -1 +cracks-5-6 + rotate: false + xy: 688, 489 + size: 160, 160 + orig: 160, 160 + offset: 0, 0 + index: -1 +cracks-5-7 + rotate: false + xy: 688, 327 + size: 160, 160 + orig: 160, 160 + offset: 0, 0 + index: -1 +unit-icon-eradicator + rotate: false + xy: 850, 399 + size: 152, 124 + orig: 152, 124 + offset: 0, 0 + index: -1 +circle + rotate: false + xy: 323, 362 + size: 201, 201 + orig: 201, 201 + offset: 0, 0 + index: -1 +logotext + rotate: false + xy: 1, 887 + size: 579, 86 + orig: 579, 86 + offset: 0, 0 + index: -1 +eradicator + rotate: false + xy: 688, 201 + size: 152, 124 + orig: 152, 124 + offset: 0, 0 + index: -1 +eradicator-base + rotate: false + xy: 688, 75 + size: 152, 124 + orig: 152, 124 + offset: 0, 0 + index: -1 +eradicator-leg + rotate: false + xy: 850, 525 + size: 152, 124 + orig: 152, 124 + offset: 0, 0 + index: -1 +lich + rotate: false + xy: 1, 1 + size: 216, 240 + orig: 216, 240 + offset: 0, 0 + index: -1 +reaper + rotate: false + xy: 1, 565 + size: 320, 320 + orig: 320, 320 + offset: 0, 0 + index: -1 + +sprites_fallback2.png +size: 1024,1024 +format: RGBA8888 +filter: Nearest,Nearest +repeat: none +force-projector + rotate: false + xy: 619, 535 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +force-projector-icon-editor + rotate: false + xy: 619, 535 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +force-projector-top + rotate: false + xy: 507, 15 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +mass-driver-base + rotate: false + xy: 801, 45 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +laser-drill + rotate: false + xy: 605, 145 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +laser-drill-rim + rotate: false + xy: 605, 47 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +laser-drill-rotator + rotate: false + xy: 703, 145 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +laser-drill-top + rotate: false + xy: 703, 47 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +rubble-4-0 + rotate: false + xy: 261, 115 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +rubble-4-1 + rotate: false + xy: 261, 115 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +liquid-tank-bottom + rotate: false + xy: 881, 339 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +liquid-tank-liquid + rotate: false + xy: 899, 241 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +liquid-tank-top + rotate: false + xy: 801, 143 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +glaive-ship-pad + rotate: false + xy: 685, 341 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +glaive-ship-pad-icon-editor + rotate: false + xy: 685, 341 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +battery-large + rotate: false + xy: 311, 17 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +battery-large-icon-editor + rotate: false + xy: 311, 17 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +differential-generator + rotate: false + xy: 489, 211 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +differential-generator-icon-editor + rotate: false + xy: 489, 211 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +differential-generator-top + rotate: false + xy: 507, 113 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +impact-reactor + rotate: false + xy: 131, 765 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +impact-reactor-bottom + rotate: false + xy: 131, 635 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +impact-reactor-light + rotate: false + xy: 131, 375 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +impact-reactor-plasma-0 + rotate: false + xy: 131, 245 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +impact-reactor-plasma-1 + rotate: false + xy: 131, 115 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +impact-reactor-plasma-2 + rotate: false + xy: 261, 765 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +impact-reactor-plasma-3 + rotate: false + xy: 391, 765 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +alloy-smelter + rotate: false + xy: 115, 17 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +alloy-smelter-icon-editor + rotate: false + xy: 115, 17 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +alloy-smelter-top + rotate: false + xy: 213, 17 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +core-foundation + rotate: false + xy: 1, 375 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +core-foundation-icon-editor + rotate: false + xy: 1, 375 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +core-shard + rotate: false + xy: 911, 829 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +core-shard-icon-editor + rotate: false + xy: 911, 829 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +launch-pad + rotate: false + xy: 783, 339 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +launch-pad-icon-editor + rotate: false + xy: 783, 339 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +launch-pad-large + rotate: false + xy: 521, 765 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +launch-pad-large-icon-editor + rotate: false + xy: 521, 765 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +block-3 + rotate: false + xy: 911, 927 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +block-4 + rotate: false + xy: 1, 895 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +meltdown-heat + rotate: false + xy: 781, 765 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +fortress-factory + rotate: false + xy: 587, 243 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +fortress-factory-top + rotate: false + xy: 815, 535 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +ghoul-factory-top + rotate: false + xy: 815, 535 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +titan-factory-top + rotate: false + xy: 815, 535 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +ghoul-factory + rotate: false + xy: 815, 437 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +revenant-factory + rotate: false + xy: 261, 505 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +revenant-factory-top + rotate: false + xy: 261, 245 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +scrap-wall-gigantic + rotate: false + xy: 391, 635 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +scrap-wall-gigantic-icon-editor + rotate: false + xy: 391, 635 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +thruster + rotate: false + xy: 781, 635 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +thruster-icon-editor + rotate: false + xy: 781, 635 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +cyclone-icon-editor + rotate: false + xy: 489, 309 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +cyclone-icon-full + rotate: false + xy: 489, 309 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +fortress-factory-icon-editor + rotate: false + xy: 717, 537 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +fortress-factory-icon-full + rotate: false + xy: 717, 537 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +fuse-icon-editor + rotate: false + xy: 913, 535 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +fuse-icon-full + rotate: false + xy: 913, 535 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +ghoul-factory-icon-editor + rotate: false + xy: 913, 437 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +ghoul-factory-icon-full + rotate: false + xy: 913, 437 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +impact-reactor-icon-editor + rotate: false + xy: 131, 505 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +impact-reactor-icon-full + rotate: false + xy: 131, 505 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +laser-drill-icon-editor + rotate: false + xy: 685, 243 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +laser-drill-icon-full + rotate: false + xy: 685, 243 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +liquid-tank-icon-editor + rotate: false + xy: 801, 241 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +liquid-tank-icon-full + rotate: false + xy: 801, 241 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +mass-driver-icon-editor + rotate: false + xy: 899, 45 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +mass-driver-icon-full + rotate: false + xy: 899, 45 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +meltdown-icon-editor + rotate: false + xy: 261, 635 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +meltdown-icon-full + rotate: false + xy: 261, 635 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +revenant-factory-icon-editor + rotate: false + xy: 261, 375 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +revenant-factory-icon-full + rotate: false + xy: 261, 375 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +spectre-icon-editor + rotate: false + xy: 651, 635 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +spectre-icon-full + rotate: false + xy: 651, 635 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +cracks-3-0 + rotate: false + xy: 911, 731 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +cracks-3-1 + rotate: false + xy: 911, 633 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +cracks-3-2 + rotate: false + xy: 391, 407 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +cracks-3-3 + rotate: false + xy: 391, 309 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +cracks-3-4 + rotate: false + xy: 391, 211 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +cracks-3-5 + rotate: false + xy: 409, 113 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +cracks-3-6 + rotate: false + xy: 409, 15 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +cracks-3-7 + rotate: false + xy: 489, 407 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +cracks-4-0 + rotate: false + xy: 1, 245 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +cracks-4-1 + rotate: false + xy: 1, 115 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +cracks-4-2 + rotate: false + xy: 131, 895 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +cracks-4-3 + rotate: false + xy: 261, 895 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +cracks-4-4 + rotate: false + xy: 391, 895 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +cracks-4-5 + rotate: false + xy: 521, 895 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +cracks-4-6 + rotate: false + xy: 651, 895 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +cracks-4-7 + rotate: false + xy: 781, 895 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +cyclone + rotate: false + xy: 521, 535 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +fuse + rotate: false + xy: 717, 439 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +mass-driver + rotate: false + xy: 899, 143 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +meltdown + rotate: false + xy: 651, 765 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +spectre + rotate: false + xy: 521, 635 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +unit-icon-chaos-array + rotate: false + xy: 391, 505 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +chaos-array + rotate: false + xy: 1, 765 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +chaos-array-base + rotate: false + xy: 1, 635 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +chaos-array-leg + rotate: false + xy: 1, 505 + size: 128, 128 + orig: 128, 128 + offset: 0, 0 + index: -1 +revenant + rotate: false + xy: 1, 1 + size: 112, 112 + orig: 112, 112 + offset: 0, 0 + index: -1 +eradication-equip + rotate: false + xy: 587, 341 + size: 96, 192 + orig: 96, 192 + offset: 0, 0 + index: -1 + +sprites_fallback3.png +size: 1024,1024 +format: RGBA8888 +filter: Nearest,Nearest +repeat: none +mend-projector + rotate: false + xy: 329, 595 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +mend-projector-icon-editor + rotate: false + xy: 329, 595 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +mend-projector-top + rotate: false + xy: 383, 529 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +overdrive-projector + rotate: false + xy: 437, 397 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +overdrive-projector-icon-editor + rotate: false + xy: 437, 397 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +overdrive-projector-top + rotate: false + xy: 437, 331 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +distributor + rotate: false + xy: 363, 199 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +distributor-icon-editor + rotate: false + xy: 363, 199 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +mechanical-drill + rotate: false + xy: 329, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +mechanical-drill-rotator + rotate: false + xy: 329, 727 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +mechanical-drill-top + rotate: false + xy: 329, 661 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +oil-extractor + rotate: false + xy: 1, 827 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +oil-extractor-liquid + rotate: false + xy: 1, 631 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +oil-extractor-rotator + rotate: false + xy: 1, 533 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +oil-extractor-top + rotate: false + xy: 1, 435 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +pneumatic-drill + rotate: false + xy: 395, 595 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +pneumatic-drill-rotator + rotate: false + xy: 503, 463 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +pneumatic-drill-top + rotate: false + xy: 503, 397 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +water-extractor + rotate: false + xy: 767, 793 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +water-extractor-liquid + rotate: false + xy: 833, 793 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +water-extractor-rotator + rotate: false + xy: 899, 793 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +water-extractor-top + rotate: false + xy: 659, 727 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +place-arrow + rotate: false + xy: 1, 239 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +rubble-1-0 + rotate: false + xy: 561, 67 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +rubble-1-1 + rotate: false + xy: 561, 1 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +rubble-2-0 + rotate: false + xy: 461, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +rubble-2-1 + rotate: false + xy: 461, 793 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +rubble-3-0 + rotate: false + xy: 197, 925 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +rubble-3-1 + rotate: false + xy: 197, 925 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +rotary-pump + rotate: false + xy: 561, 199 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +rotary-pump-icon-editor + rotate: false + xy: 561, 199 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +thermal-pump + rotate: false + xy: 687, 925 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +thermal-pump-icon-editor + rotate: false + xy: 687, 925 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +dart-mech-pad + rotate: false + xy: 305, 331 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +dart-mech-pad-icon-editor + rotate: false + xy: 305, 331 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +delta-mech-pad + rotate: false + xy: 305, 265 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +delta-mech-pad-icon-editor + rotate: false + xy: 305, 265 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +javelin-ship-pad + rotate: false + xy: 371, 331 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +javelin-ship-pad-icon-editor + rotate: false + xy: 371, 331 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +omega-mech-pad + rotate: false + xy: 1, 337 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +omega-mech-pad-icon-editor + rotate: false + xy: 1, 337 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +tau-mech-pad + rotate: false + xy: 825, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +tau-mech-pad-icon-editor + rotate: false + xy: 825, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +trident-ship-pad + rotate: false + xy: 593, 595 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +trident-ship-pad-icon-editor + rotate: false + xy: 593, 595 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +power-node-large + rotate: false + xy: 503, 331 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +power-node-large-icon-editor + rotate: false + xy: 503, 331 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +rtg-generator + rotate: false + xy: 561, 133 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +rtg-generator-icon-editor + rotate: false + xy: 561, 133 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +solar-panel-large + rotate: false + xy: 589, 925 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +solar-panel-large-icon-editor + rotate: false + xy: 589, 925 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +surge-tower + rotate: false + xy: 693, 67 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +surge-tower-icon-editor + rotate: false + xy: 693, 67 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +thermal-generator + rotate: false + xy: 891, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +thermal-generator-icon-editor + rotate: false + xy: 891, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +thorium-reactor + rotate: false + xy: 785, 925 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +thorium-reactor-icon-editor + rotate: false + xy: 785, 925 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +thorium-reactor-center + rotate: false + xy: 883, 925 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +thorium-reactor-lights + rotate: false + xy: 99, 827 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +turbine-generator + rotate: false + xy: 647, 529 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +turbine-generator-icon-editor + rotate: false + xy: 647, 529 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +turbine-generator-top + rotate: false + xy: 701, 463 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +blast-mixer + rotate: false + xy: 99, 77 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +blast-mixer-icon-editor + rotate: false + xy: 99, 77 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +coal-centrifuge + rotate: false + xy: 165, 77 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +coal-centrifuge-icon-editor + rotate: false + xy: 165, 77 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cryofluidmixer-bottom + rotate: false + xy: 297, 71 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cryofluidmixer-liquid + rotate: false + xy: 197, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cryofluidmixer-top + rotate: false + xy: 197, 793 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cultivator + rotate: false + xy: 197, 727 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cultivator-middle + rotate: false + xy: 197, 595 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cultivator-top + rotate: false + xy: 251, 529 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +graphite-press + rotate: false + xy: 371, 397 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +graphite-press-icon-editor + rotate: false + xy: 371, 397 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +kiln + rotate: false + xy: 371, 265 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +kiln-icon-editor + rotate: false + xy: 371, 265 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +kiln-top + rotate: false + xy: 429, 199 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +silicon-smelter-top + rotate: false + xy: 429, 199 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +multi-press + rotate: false + xy: 1, 925 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +multi-press-icon-editor + rotate: false + xy: 1, 925 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +phase-weaver + rotate: false + xy: 495, 67 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +phase-weaver-bottom + rotate: false + xy: 495, 1 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +phase-weaver-weave + rotate: false + xy: 395, 793 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +plastanium-compressor + rotate: false + xy: 395, 727 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +plastanium-compressor-icon-editor + rotate: false + xy: 395, 727 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +plastanium-compressor-top + rotate: false + xy: 395, 661 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +pyratite-mixer + rotate: false + xy: 503, 265 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +pyratite-mixer-icon-editor + rotate: false + xy: 503, 265 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +separator + rotate: false + xy: 527, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +separator-icon-editor + rotate: false + xy: 527, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +separator-liquid + rotate: false + xy: 527, 793 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +silicon-smelter + rotate: false + xy: 527, 727 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +silicon-smelter-icon-editor + rotate: false + xy: 527, 727 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +spore-press + rotate: false + xy: 581, 529 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +spore-press-frame0 + rotate: false + xy: 635, 463 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +spore-press-frame1 + rotate: false + xy: 635, 397 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +spore-press-frame2 + rotate: false + xy: 635, 331 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +spore-press-liquid + rotate: false + xy: 693, 199 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +spore-press-top + rotate: false + xy: 693, 133 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +container + rotate: false + xy: 165, 11 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +container-icon-editor + rotate: false + xy: 165, 11 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +vault + rotate: false + xy: 99, 533 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +vault-icon-editor + rotate: false + xy: 99, 533 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +block-2 + rotate: false + xy: 99, 11 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +lancer-heat + rotate: false + xy: 429, 67 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +ripple-heat + rotate: false + xy: 1, 43 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +salvo-heat + rotate: false + xy: 461, 661 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +salvo-panel-left + rotate: false + xy: 515, 529 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +salvo-panel-right + rotate: false + xy: 569, 463 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +wave-liquid + rotate: false + xy: 713, 529 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +crawler-factory + rotate: false + xy: 239, 269 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +dagger-factory + rotate: false + xy: 239, 269 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +draug-factory + rotate: false + xy: 239, 269 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +phantom-factory + rotate: false + xy: 239, 269 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +spirit-factory + rotate: false + xy: 239, 269 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +wraith-factory + rotate: false + xy: 239, 269 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +crawler-factory-top + rotate: false + xy: 297, 137 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +dagger-factory-top + rotate: false + xy: 305, 397 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +draug-factory-top + rotate: false + xy: 263, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +phantom-factory-top + rotate: false + xy: 495, 199 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +spirit-factory-top + rotate: false + xy: 527, 595 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +titan-factory + rotate: false + xy: 99, 729 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +wraith-factory-top + rotate: false + xy: 725, 661 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +copper-wall-large + rotate: false + xy: 173, 373 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +copper-wall-large-icon-editor + rotate: false + xy: 173, 373 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +door-large + rotate: false + xy: 363, 133 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +door-large-icon-editor + rotate: false + xy: 363, 133 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +door-large-open + rotate: false + xy: 363, 67 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +phase-wall-large + rotate: false + xy: 495, 133 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +phase-wall-large-icon-editor + rotate: false + xy: 495, 133 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +scrap-wall-huge1 + rotate: false + xy: 295, 925 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +scrap-wall-huge-icon-editor + rotate: false + xy: 295, 925 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +scrap-wall-huge2 + rotate: false + xy: 393, 925 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +scrap-wall-huge3 + rotate: false + xy: 491, 925 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +scrap-wall-large1 + rotate: false + xy: 569, 265 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +scrap-wall-large2 + rotate: false + xy: 627, 199 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +scrap-wall-large3 + rotate: false + xy: 627, 133 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +scrap-wall-large4 + rotate: false + xy: 627, 67 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +surge-wall-large + rotate: false + xy: 693, 1 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +surge-wall-large-icon-editor + rotate: false + xy: 693, 1 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +thorium-wall-large + rotate: false + xy: 957, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +thorium-wall-large-icon-editor + rotate: false + xy: 957, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +titanium-wall-large + rotate: false + xy: 593, 661 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +titanium-wall-large-icon-editor + rotate: false + xy: 593, 661 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +crawler-factory-icon-editor + rotate: false + xy: 239, 203 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +crawler-factory-icon-full + rotate: false + xy: 239, 203 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cryofluidmixer-icon-editor + rotate: false + xy: 297, 5 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cryofluidmixer-icon-full + rotate: false + xy: 297, 5 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cultivator-icon-editor + rotate: false + xy: 197, 661 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cultivator-icon-full + rotate: false + xy: 197, 661 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +dagger-factory-icon-editor + rotate: false + xy: 305, 463 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +dagger-factory-icon-full + rotate: false + xy: 305, 463 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +draug-factory-icon-editor + rotate: false + xy: 363, 1 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +draug-factory-icon-full + rotate: false + xy: 363, 1 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +lancer-icon-editor + rotate: false + xy: 429, 1 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +lancer-icon-full + rotate: false + xy: 429, 1 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +mechanical-drill-icon-editor + rotate: false + xy: 329, 793 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +mechanical-drill-icon-full + rotate: false + xy: 329, 793 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +oil-extractor-icon-editor + rotate: false + xy: 1, 729 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +oil-extractor-icon-full + rotate: false + xy: 1, 729 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +phantom-factory-icon-editor + rotate: false + xy: 437, 265 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +phantom-factory-icon-full + rotate: false + xy: 437, 265 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +phase-weaver-icon-editor + rotate: false + xy: 395, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +phase-weaver-icon-full + rotate: false + xy: 395, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +pneumatic-drill-icon-editor + rotate: false + xy: 449, 529 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +pneumatic-drill-icon-full + rotate: false + xy: 449, 529 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +ripple-icon-editor + rotate: false + xy: 99, 925 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +ripple-icon-full + rotate: false + xy: 99, 925 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +salvo-icon-editor + rotate: false + xy: 461, 595 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +salvo-icon-full + rotate: false + xy: 461, 595 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +scatter-icon-editor + rotate: false + xy: 569, 331 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +scatter-icon-full + rotate: false + xy: 569, 331 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +scrap-wall-large-icon-editor + rotate: false + xy: 627, 1 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +spirit-factory-icon-editor + rotate: false + xy: 527, 661 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +spirit-factory-icon-full + rotate: false + xy: 527, 661 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +spore-press-icon-editor + rotate: false + xy: 635, 265 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +spore-press-icon-full + rotate: false + xy: 635, 265 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +swarmer-icon-editor + rotate: false + xy: 647, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +swarmer-icon-full + rotate: false + xy: 647, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +titan-factory-icon-editor + rotate: false + xy: 99, 631 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +titan-factory-icon-full + rotate: false + xy: 99, 631 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +water-extractor-icon-editor + rotate: false + xy: 659, 793 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +water-extractor-icon-full + rotate: false + xy: 659, 793 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +wave-icon-editor + rotate: false + xy: 659, 595 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +wave-icon-full + rotate: false + xy: 659, 595 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +wraith-factory-icon-editor + rotate: false + xy: 725, 727 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +wraith-factory-icon-full + rotate: false + xy: 725, 727 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +bullet + rotate: false + xy: 197, 541 + size: 52, 52 + orig: 52, 52 + offset: 0, 0 + index: -1 +bullet-back + rotate: false + xy: 251, 475 + size: 52, 52 + orig: 52, 52 + offset: 0, 0 + index: -1 +laser-end + rotate: false + xy: 99, 291 + size: 72, 72 + orig: 72, 72 + offset: 0, 0 + index: -1 +minelaser-end + rotate: false + xy: 99, 217 + size: 72, 72 + orig: 72, 72 + offset: 0, 0 + index: -1 +transfer-end + rotate: false + xy: 99, 143 + size: 72, 72 + orig: 72, 72 + offset: 0, 0 + index: -1 +alloy-smelter-icon-large + rotate: false + xy: 593, 875 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +arc-icon-large + rotate: false + xy: 940, 555 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +battery-icon-large + rotate: false + xy: 829, 519 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +battery-large-icon-large + rotate: false + xy: 879, 519 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +blast-drill-icon-large + rotate: false + xy: 929, 505 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +blast-mixer-icon-large + rotate: false + xy: 829, 469 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +bridge-conduit-icon-large + rotate: false + xy: 929, 455 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +bridge-conveyor-icon-large + rotate: false + xy: 817, 419 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +char-icon-large + rotate: false + xy: 867, 419 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +cliffs-icon-large + rotate: false + xy: 917, 405 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +coal-centrifuge-icon-large + rotate: false + xy: 967, 405 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +combustion-generator-icon-large + rotate: false + xy: 817, 369 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +conduit-icon-large + rotate: false + xy: 767, 361 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +container-icon-large + rotate: false + xy: 867, 369 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +conveyor-icon-large + rotate: false + xy: 917, 355 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +copper-wall-icon-large + rotate: false + xy: 967, 355 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +copper-wall-large-icon-large + rotate: false + xy: 817, 319 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +core-foundation-icon-large + rotate: false + xy: 767, 311 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +core-nucleus-icon-large + rotate: false + xy: 867, 319 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +core-shard-icon-large + rotate: false + xy: 917, 305 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +cracks-2-0 + rotate: false + xy: 185, 467 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cracks-2-1 + rotate: false + xy: 173, 307 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cracks-2-2 + rotate: false + xy: 173, 241 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cracks-2-3 + rotate: false + xy: 173, 175 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cracks-2-4 + rotate: false + xy: 231, 109 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cracks-2-5 + rotate: false + xy: 231, 43 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cracks-2-6 + rotate: false + xy: 239, 401 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +cracks-2-7 + rotate: false + xy: 239, 335 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +craters-icon-large + rotate: false + xy: 967, 305 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +crawler-factory-icon-large + rotate: false + xy: 867, 269 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +cryofluidmixer-icon-large + rotate: false + xy: 967, 255 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +cultivator-icon-large + rotate: false + xy: 817, 219 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +cyclone-icon-large + rotate: false + xy: 867, 219 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +dagger-factory-icon-large + rotate: false + xy: 967, 205 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +dark-metal-icon-large + rotate: false + xy: 759, 161 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +dark-panel-1-icon-large + rotate: false + xy: 859, 169 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +dark-panel-2-icon-large + rotate: false + xy: 909, 155 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +dark-panel-3-icon-large + rotate: false + xy: 959, 155 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +dark-panel-4-icon-large + rotate: false + xy: 809, 119 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +dark-panel-5-icon-large + rotate: false + xy: 759, 111 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +dark-panel-6-icon-large + rotate: false + xy: 859, 119 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +darksand-icon-large + rotate: false + xy: 759, 61 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +darksand-tainted-water-icon-large + rotate: false + xy: 759, 11 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +darksand-water-icon-large + rotate: false + xy: 909, 105 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +dart-mech-pad-icon-large + rotate: false + xy: 959, 105 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +deepwater-icon-large + rotate: false + xy: 859, 69 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +delta-mech-pad-icon-large + rotate: false + xy: 909, 5 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +differential-generator-icon-large + rotate: false + xy: 959, 55 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +distributor-icon-large + rotate: false + xy: 959, 5 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +lancer + rotate: false + xy: 429, 133 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +mech-icon-glaive-ship + rotate: false + xy: 791, 735 + size: 56, 56 + orig: 56, 56 + offset: 0, 0 + index: -1 +mech-icon-omega-mech + rotate: false + xy: 849, 735 + size: 56, 56 + orig: 56, 56 + offset: 0, 0 + index: -1 +mech-icon-tau-mech + rotate: false + xy: 907, 735 + size: 56, 56 + orig: 56, 56 + offset: 0, 0 + index: -1 +mech-icon-trident-ship + rotate: false + xy: 791, 677 + size: 56, 56 + orig: 56, 56 + offset: 0, 0 + index: -1 +ripple + rotate: false + xy: 1, 141 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +salvo + rotate: false + xy: 461, 727 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +scatter + rotate: false + xy: 569, 397 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +swarmer + rotate: false + xy: 759, 859 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +unit-icon-eruptor + rotate: false + xy: 701, 397 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +unit-icon-fortress + rotate: false + xy: 701, 331 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +unit-icon-titan + rotate: false + xy: 701, 265 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +wave + rotate: false + xy: 659, 661 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +alpha-mech + rotate: false + xy: 790, 569 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +alpha-mech-base + rotate: false + xy: 840, 569 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +alpha-mech-leg + rotate: false + xy: 890, 569 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +delta-mech + rotate: false + xy: 809, 19 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +delta-mech-base + rotate: false + xy: 859, 19 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +delta-mech-leg + rotate: false + xy: 909, 55 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +omega-mech + rotate: false + xy: 849, 677 + size: 56, 56 + orig: 56, 56 + offset: 0, 0 + index: -1 +omega-mech-armor + rotate: false + xy: 437, 463 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +omega-mech-base + rotate: false + xy: 907, 677 + size: 56, 56 + orig: 56, 56 + offset: 0, 0 + index: -1 +omega-mech-leg + rotate: false + xy: 965, 663 + size: 56, 56 + orig: 56, 56 + offset: 0, 0 + index: -1 +tau-mech + rotate: false + xy: 907, 619 + size: 56, 56 + orig: 56, 56 + offset: 0, 0 + index: -1 +dart-ship + rotate: false + xy: 809, 69 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +glaive-ship + rotate: false + xy: 305, 207 + size: 56, 56 + orig: 56, 56 + offset: 0, 0 + index: -1 +trident-ship + rotate: false + xy: 965, 605 + size: 56, 56 + orig: 56, 56 + offset: 0, 0 + index: -1 +shape-3 + rotate: false + xy: 725, 596 + size: 63, 63 + orig: 63, 63 + offset: 0, 0 + index: -1 +discord-banner + rotate: false + xy: 99, 486 + size: 84, 45 + orig: 84, 45 + offset: 0, 0 + index: -1 +info-banner + rotate: false + xy: 99, 439 + size: 84, 45 + orig: 84, 45 + offset: 0, 0 + index: -1 +crawler + rotate: false + xy: 817, 269 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +crawler-base + rotate: false + xy: 767, 261 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +crawler-leg + rotate: false + xy: 917, 255 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +dagger + rotate: false + xy: 759, 211 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +dagger-base + rotate: false + xy: 917, 205 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +dagger-leg + rotate: false + xy: 809, 169 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +eruptor + rotate: false + xy: 263, 793 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +eruptor-base + rotate: false + xy: 263, 727 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +eruptor-leg + rotate: false + xy: 263, 661 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +fortress + rotate: false + xy: 263, 595 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +fortress-base + rotate: false + xy: 317, 529 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +titan-base + rotate: false + xy: 317, 529 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +fortress-leg + rotate: false + xy: 371, 463 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +ghoul + rotate: false + xy: 99, 365 + size: 72, 72 + orig: 72, 72 + offset: 0, 0 + index: -1 +phantom + rotate: false + xy: 791, 619 + size: 56, 56 + orig: 56, 56 + offset: 0, 0 + index: -1 +power-cell + rotate: false + xy: 849, 619 + size: 56, 56 + orig: 56, 56 + offset: 0, 0 + index: -1 +titan + rotate: false + xy: 593, 793 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +titan-leg + rotate: false + xy: 593, 727 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +artillery-equip + rotate: false + xy: 779, 511 + size: 48, 56 + orig: 48, 56 + offset: 0, 0 + index: -1 +blaster-equip + rotate: false + xy: 879, 469 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +bomber-equip + rotate: false + xy: 767, 461 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +missiles-equip + rotate: false + xy: 767, 461 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +chain-blaster-equip + rotate: false + xy: 767, 411 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +chaos-equip + rotate: false + xy: 965, 721 + size: 56, 136 + orig: 56, 136 + offset: 0, 0 + index: -1 + +sprites_fallback4.png +size: 1024,1024 +format: RGBA8888 +filter: Nearest,Nearest +repeat: none +bridge-arrow + rotate: false + xy: 777, 267 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +bridge-conveyor + rotate: false + xy: 777, 97 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +bridge-conveyor-icon-editor + rotate: false + xy: 777, 97 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +bridge-conveyor-arrow + rotate: false + xy: 777, 63 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +bridge-conveyor-bridge + rotate: false + xy: 777, 29 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +bridge-conveyor-end + rotate: false + xy: 781, 842 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +center + rotate: false + xy: 789, 987 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conveyor-0-0 + rotate: false + xy: 823, 953 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conveyor-icon-editor + rotate: false + xy: 823, 953 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conveyor-0-1 + rotate: false + xy: 823, 919 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conveyor-0-2 + rotate: false + xy: 823, 885 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conveyor-0-3 + rotate: false + xy: 815, 817 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conveyor-1-0 + rotate: false + xy: 815, 783 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conveyor-1-1 + rotate: false + xy: 815, 749 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conveyor-1-2 + rotate: false + xy: 815, 715 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conveyor-1-3 + rotate: false + xy: 815, 681 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conveyor-2-0 + rotate: false + xy: 815, 647 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conveyor-2-1 + rotate: false + xy: 815, 613 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conveyor-2-2 + rotate: false + xy: 815, 579 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conveyor-2-3 + rotate: false + xy: 815, 545 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conveyor-3-0 + rotate: false + xy: 815, 511 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conveyor-3-1 + rotate: false + xy: 815, 477 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conveyor-3-2 + rotate: false + xy: 815, 443 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conveyor-3-3 + rotate: false + xy: 845, 409 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conveyor-4-0 + rotate: false + xy: 845, 375 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conveyor-4-1 + rotate: false + xy: 845, 341 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conveyor-4-2 + rotate: false + xy: 845, 307 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conveyor-4-3 + rotate: false + xy: 845, 273 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +block-border + rotate: false + xy: 777, 369 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +block-border-editor + rotate: false + xy: 777, 369 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +block-middle + rotate: false + xy: 777, 335 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +block-select + rotate: false + xy: 777, 301 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-liquid + rotate: false + xy: 811, 230 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +bridge-conduit + rotate: false + xy: 777, 233 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +bridge-conduit-icon-editor + rotate: false + xy: 777, 233 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +bridge-conduit-arrow + rotate: false + xy: 777, 199 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +bridge-conduit-bridge + rotate: false + xy: 777, 165 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +bridge-conduit-end + rotate: false + xy: 777, 131 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-bottom + rotate: false + xy: 781, 536 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-bottom-0 + rotate: false + xy: 781, 502 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-bottom-1 + rotate: false + xy: 781, 468 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-bottom-2 + rotate: false + xy: 781, 434 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-bottom-3 + rotate: false + xy: 811, 400 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-bottom-4 + rotate: false + xy: 811, 366 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-bottom-5 + rotate: false + xy: 811, 332 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-bottom-6 + rotate: false + xy: 811, 298 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-top-0 + rotate: false + xy: 811, 196 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-top-1 + rotate: false + xy: 811, 162 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-top-2 + rotate: false + xy: 811, 128 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-top-3 + rotate: false + xy: 811, 94 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-top-4 + rotate: false + xy: 811, 60 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-top-5 + rotate: false + xy: 811, 26 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-top-6 + rotate: false + xy: 815, 851 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +battery + rotate: false + xy: 743, 233 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +battery-icon-editor + rotate: false + xy: 743, 233 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +combustion-generator + rotate: false + xy: 781, 604 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +combustion-generator-icon-editor + rotate: false + xy: 781, 604 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +combustion-generator-top + rotate: false + xy: 781, 570 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +arc-heat + rotate: false + xy: 743, 301 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +block-1 + rotate: false + xy: 743, 97 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +hail-heat + rotate: false + xy: 701, 829 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +copper-wall + rotate: false + xy: 845, 239 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +copper-wall-icon-editor + rotate: false + xy: 845, 239 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +door + rotate: false + xy: 879, 103 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +door-icon-editor + rotate: false + xy: 879, 103 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +door-open + rotate: false + xy: 879, 35 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +arc-icon-editor + rotate: false + xy: 743, 267 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +arc-icon-full + rotate: false + xy: 743, 267 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +char-icon-editor + rotate: false + xy: 789, 953 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-char1 + rotate: false + xy: 789, 953 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +cliffs-icon-editor + rotate: false + xy: 781, 672 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-icon-editor + rotate: false + xy: 811, 264 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +conduit-icon-full + rotate: false + xy: 811, 264 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +craters-icon-editor + rotate: false + xy: 849, 817 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-craters1 + rotate: false + xy: 849, 817 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dark-metal-icon-editor + rotate: false + xy: 849, 613 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dark-panel-1-icon-editor + rotate: false + xy: 849, 579 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-dark-panel-1 + rotate: false + xy: 849, 579 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dark-panel-2-icon-editor + rotate: false + xy: 849, 545 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-dark-panel-2 + rotate: false + xy: 849, 545 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dark-panel-3-icon-editor + rotate: false + xy: 849, 511 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-dark-panel-3 + rotate: false + xy: 849, 511 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dark-panel-4-icon-editor + rotate: false + xy: 849, 477 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-dark-panel-4 + rotate: false + xy: 849, 477 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dark-panel-5-icon-editor + rotate: false + xy: 849, 443 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-dark-panel-5 + rotate: false + xy: 849, 443 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dark-panel-6-icon-editor + rotate: false + xy: 879, 409 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-dark-panel-6 + rotate: false + xy: 879, 409 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +darksand-icon-editor + rotate: false + xy: 879, 375 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-darksand1 + rotate: false + xy: 879, 375 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +darksand-tainted-water-icon-editor + rotate: false + xy: 879, 341 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-darksand-tainted-water + rotate: false + xy: 879, 341 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +darksand-water-icon-editor + rotate: false + xy: 879, 307 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-darksand-water + rotate: false + xy: 879, 307 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +deepwater-icon-editor + rotate: false + xy: 879, 239 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-deepwater + rotate: false + xy: 879, 239 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dunerocks-icon-editor + rotate: false + xy: 883, 851 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +duo-icon-editor + rotate: false + xy: 891, 953 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +duo-icon-full + rotate: false + xy: 891, 953 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-char2 + rotate: false + xy: 891, 919 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-char3 + rotate: false + xy: 891, 885 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-craters2 + rotate: false + xy: 883, 817 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-craters3 + rotate: false + xy: 883, 783 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-darksand2 + rotate: false + xy: 883, 749 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-darksand3 + rotate: false + xy: 883, 715 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-grass1 + rotate: false + xy: 883, 681 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +grass-icon-editor + rotate: false + xy: 883, 681 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-grass2 + rotate: false + xy: 883, 647 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-grass3 + rotate: false + xy: 883, 613 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-holostone1 + rotate: false + xy: 883, 579 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +holostone-icon-editor + rotate: false + xy: 883, 579 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-holostone2 + rotate: false + xy: 883, 545 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-holostone3 + rotate: false + xy: 883, 511 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-hotrock1 + rotate: false + xy: 883, 477 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +hotrock-icon-editor + rotate: false + xy: 883, 477 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-hotrock2 + rotate: false + xy: 883, 443 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-hotrock3 + rotate: false + xy: 913, 409 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ice-snow1 + rotate: false + xy: 913, 273 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ice-snow-icon-editor + rotate: false + xy: 913, 273 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ice-snow2 + rotate: false + xy: 913, 239 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ice-snow3 + rotate: false + xy: 913, 205 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ice1 + rotate: false + xy: 913, 375 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ice-icon-editor + rotate: false + xy: 913, 375 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ice2 + rotate: false + xy: 913, 341 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ice3 + rotate: false + xy: 913, 307 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ignarock1 + rotate: false + xy: 913, 171 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ignarock-icon-editor + rotate: false + xy: 913, 171 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ignarock2 + rotate: false + xy: 913, 137 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ignarock3 + rotate: false + xy: 913, 103 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-magmarock1 + rotate: false + xy: 913, 69 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +magmarock-icon-editor + rotate: false + xy: 913, 69 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-magmarock2 + rotate: false + xy: 913, 35 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-magmarock3 + rotate: false + xy: 913, 1 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-metal-floor + rotate: false + xy: 917, 851 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +metal-floor-icon-editor + rotate: false + xy: 917, 851 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-metal-floor-2 + rotate: false + xy: 925, 987 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +metal-floor-2-icon-editor + rotate: false + xy: 925, 987 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-metal-floor-3 + rotate: false + xy: 925, 953 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +metal-floor-3-icon-editor + rotate: false + xy: 925, 953 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-metal-floor-5 + rotate: false + xy: 925, 919 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +metal-floor-5-icon-editor + rotate: false + xy: 925, 919 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-metal-floor-damaged1 + rotate: false + xy: 925, 885 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +metal-floor-damaged-icon-editor + rotate: false + xy: 925, 885 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-metal-floor-damaged2 + rotate: false + xy: 917, 817 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-metal-floor-damaged3 + rotate: false + xy: 917, 783 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-moss1 + rotate: false + xy: 917, 749 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +moss-icon-editor + rotate: false + xy: 917, 749 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-moss2 + rotate: false + xy: 917, 715 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-moss3 + rotate: false + xy: 917, 681 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-coal1 + rotate: false + xy: 917, 647 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-coal2 + rotate: false + xy: 917, 613 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-coal3 + rotate: false + xy: 917, 579 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-coal-icon-full + rotate: false + xy: 917, 579 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-coal-icon-medium + rotate: false + xy: 917, 579 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-copper1 + rotate: false + xy: 917, 545 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-copper2 + rotate: false + xy: 917, 511 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-copper3 + rotate: false + xy: 917, 477 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-copper-icon-full + rotate: false + xy: 917, 477 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-copper-icon-medium + rotate: false + xy: 917, 477 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-lead1 + rotate: false + xy: 917, 443 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-lead2 + rotate: false + xy: 947, 409 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-lead3 + rotate: false + xy: 947, 375 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-lead-icon-full + rotate: false + xy: 947, 375 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-lead-icon-medium + rotate: false + xy: 947, 375 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-scrap1 + rotate: false + xy: 947, 341 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-scrap2 + rotate: false + xy: 947, 307 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-scrap3 + rotate: false + xy: 947, 273 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-scrap-icon-full + rotate: false + xy: 947, 273 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-scrap-icon-medium + rotate: false + xy: 947, 273 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-thorium1 + rotate: false + xy: 947, 239 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-thorium2 + rotate: false + xy: 947, 205 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-thorium3 + rotate: false + xy: 947, 171 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-thorium-icon-full + rotate: false + xy: 947, 171 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-thorium-icon-medium + rotate: false + xy: 947, 171 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-titanium1 + rotate: false + xy: 947, 137 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-titanium2 + rotate: false + xy: 947, 103 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-ore-titanium3 + rotate: false + xy: 947, 69 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-titanium-icon-full + rotate: false + xy: 947, 69 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-titanium-icon-medium + rotate: false + xy: 947, 69 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-pebbles1 + rotate: false + xy: 947, 35 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-pebbles2 + rotate: false + xy: 947, 1 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-pebbles3 + rotate: false + xy: 951, 851 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-salt + rotate: false + xy: 959, 987 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +salt-icon-editor + rotate: false + xy: 959, 987 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-sand-water + rotate: false + xy: 951, 817 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +sand-water-icon-editor + rotate: false + xy: 951, 817 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-sand1 + rotate: false + xy: 959, 953 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +sand-icon-editor + rotate: false + xy: 959, 953 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-sand2 + rotate: false + xy: 959, 919 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-sand3 + rotate: false + xy: 959, 885 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-shale1 + rotate: false + xy: 951, 783 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +shale-icon-editor + rotate: false + xy: 951, 783 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-shale2 + rotate: false + xy: 951, 749 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-shale3 + rotate: false + xy: 951, 715 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-snow1 + rotate: false + xy: 951, 681 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +snow-icon-editor + rotate: false + xy: 951, 681 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-snow2 + rotate: false + xy: 951, 647 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-snow3 + rotate: false + xy: 951, 613 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-spawn + rotate: false + xy: 951, 579 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-spore-moss1 + rotate: false + xy: 951, 545 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +spore-moss-icon-editor + rotate: false + xy: 951, 545 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-spore-moss2 + rotate: false + xy: 951, 511 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-spore-moss3 + rotate: false + xy: 951, 477 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-stone1 + rotate: false + xy: 951, 443 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +stone-icon-editor + rotate: false + xy: 951, 443 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-stone2 + rotate: false + xy: 981, 409 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-stone3 + rotate: false + xy: 981, 375 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-tainted-water + rotate: false + xy: 981, 341 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +tainted-water-icon-editor + rotate: false + xy: 981, 341 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-tar + rotate: false + xy: 981, 307 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +tar-icon-editor + rotate: false + xy: 981, 307 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-tendrils1 + rotate: false + xy: 981, 273 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-tendrils2 + rotate: false + xy: 981, 239 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-tendrils3 + rotate: false + xy: 981, 205 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +editor-water + rotate: false + xy: 981, 171 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +water-icon-editor + rotate: false + xy: 981, 171 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +hail-icon-editor + rotate: false + xy: 985, 749 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +hail-icon-full + rotate: false + xy: 985, 749 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icerocks-icon-editor + rotate: false + xy: 985, 715 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pine-icon-editor + rotate: false + xy: 451, 321 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +rock-icon-editor + rotate: false + xy: 501, 571 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +snowrock-icon-editor + rotate: false + xy: 551, 271 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +spore-cluster-icon-editor + rotate: false + xy: 701, 89 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +spore-pine-icon-editor + rotate: false + xy: 601, 721 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +error + rotate: false + xy: 1, 671 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +missile + rotate: false + xy: 743, 717 + size: 36, 36 + orig: 36, 36 + offset: 0, 0 + index: -1 +missile-back + rotate: false + xy: 743, 679 + size: 36, 36 + orig: 36, 36 + offset: 0, 0 + index: -1 +shell + rotate: false + xy: 743, 583 + size: 36, 36 + orig: 36, 36 + offset: 0, 0 + index: -1 +shell-back + rotate: false + xy: 743, 545 + size: 36, 36 + orig: 36, 36 + offset: 0, 0 + index: -1 +alloy-smelter-icon-medium + rotate: false + xy: 743, 369 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +arc + rotate: false + xy: 743, 335 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +battery-large-icon-medium + rotate: false + xy: 743, 199 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +blast-drill-icon-medium + rotate: false + xy: 743, 165 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +blast-mixer-icon-medium + rotate: false + xy: 743, 131 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +coal-centrifuge-icon-medium + rotate: false + xy: 781, 638 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +container-icon-medium + rotate: false + xy: 823, 987 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +copper-wall-large-icon-medium + rotate: false + xy: 845, 205 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +core-foundation-icon-medium + rotate: false + xy: 845, 171 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +core-nucleus-icon-medium + rotate: false + xy: 845, 137 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +core-shard-icon-medium + rotate: false + xy: 845, 103 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +cracks-1-0 + rotate: false + xy: 845, 69 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +cracks-1-1 + rotate: false + xy: 845, 35 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +cracks-1-2 + rotate: false + xy: 845, 1 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +cracks-1-3 + rotate: false + xy: 849, 851 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +cracks-1-4 + rotate: false + xy: 857, 987 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +cracks-1-5 + rotate: false + xy: 857, 953 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +cracks-1-6 + rotate: false + xy: 857, 919 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +cracks-1-7 + rotate: false + xy: 857, 885 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +crawler-factory-icon-medium + rotate: false + xy: 849, 783 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +cryofluidmixer-icon-medium + rotate: false + xy: 849, 749 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +cultivator-icon-medium + rotate: false + xy: 849, 715 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +cyclone-icon-medium + rotate: false + xy: 849, 681 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dagger-factory-icon-medium + rotate: false + xy: 849, 647 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dart-mech-pad-icon-medium + rotate: false + xy: 879, 273 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +delta-mech-pad-icon-medium + rotate: false + xy: 879, 205 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +differential-generator-icon-medium + rotate: false + xy: 879, 171 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +distributor-icon-medium + rotate: false + xy: 879, 137 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +door-icon-large + rotate: false + xy: 1, 971 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +door-large-icon-large + rotate: false + xy: 1, 921 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +door-large-icon-medium + rotate: false + xy: 879, 69 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +draug-factory-icon-large + rotate: false + xy: 1, 821 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +draug-factory-icon-medium + rotate: false + xy: 879, 1 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dunerocks-icon-large + rotate: false + xy: 1, 771 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +duo + rotate: false + xy: 891, 987 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +duo-icon-large + rotate: false + xy: 1, 721 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +force-projector-icon-large + rotate: false + xy: 51, 805 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +force-projector-icon-medium + rotate: false + xy: 981, 103 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +fortress-factory-icon-large + rotate: false + xy: 51, 755 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +fortress-factory-icon-medium + rotate: false + xy: 981, 69 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +fuse-icon-large + rotate: false + xy: 51, 705 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +fuse-icon-medium + rotate: false + xy: 981, 35 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ghoul-factory-icon-large + rotate: false + xy: 51, 655 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +ghoul-factory-icon-medium + rotate: false + xy: 981, 1 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +glaive-ship-pad-icon-large + rotate: false + xy: 1, 621 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +glaive-ship-pad-icon-medium + rotate: false + xy: 985, 851 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +graphite-press-icon-large + rotate: false + xy: 51, 605 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +graphite-press-icon-medium + rotate: false + xy: 985, 817 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +grass-icon-large + rotate: false + xy: 1, 571 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +hail + rotate: false + xy: 985, 783 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +hail-icon-large + rotate: false + xy: 51, 555 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +holostone-icon-large + rotate: false + xy: 51, 505 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +hotrock-icon-large + rotate: false + xy: 1, 471 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +ice-icon-large + rotate: false + xy: 51, 455 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +ice-snow-icon-large + rotate: false + xy: 1, 421 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icerocks-icon-large + rotate: false + xy: 51, 405 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +ignarock-icon-large + rotate: false + xy: 301, 871 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +impact-reactor-icon-large + rotate: false + xy: 301, 821 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +incinerator-icon-large + rotate: false + xy: 301, 771 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-blast-compound-xlarge + rotate: false + xy: 701, 787 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +item-blast-compound-xxlarge + rotate: false + xy: 301, 721 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-coal-xlarge + rotate: false + xy: 701, 745 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +item-coal-xxlarge + rotate: false + xy: 301, 671 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-copper-xlarge + rotate: false + xy: 701, 703 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +item-copper-xxlarge + rotate: false + xy: 301, 621 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-graphite-xlarge + rotate: false + xy: 701, 661 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +item-graphite-xxlarge + rotate: false + xy: 301, 571 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-lead-xlarge + rotate: false + xy: 701, 619 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +item-lead-xxlarge + rotate: false + xy: 301, 521 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-metaglass-xlarge + rotate: false + xy: 701, 577 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +item-metaglass-xxlarge + rotate: false + xy: 301, 471 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-phase-fabric-xlarge + rotate: false + xy: 701, 535 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +item-phase-fabric-xxlarge + rotate: false + xy: 301, 421 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-plastanium-xlarge + rotate: false + xy: 701, 493 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +item-plastanium-xxlarge + rotate: false + xy: 301, 371 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-pyratite-xlarge + rotate: false + xy: 701, 451 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +item-pyratite-xxlarge + rotate: false + xy: 301, 321 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-sand-xlarge + rotate: false + xy: 701, 409 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +item-sand-xxlarge + rotate: false + xy: 301, 271 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-scrap-xlarge + rotate: false + xy: 701, 367 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +item-scrap-xxlarge + rotate: false + xy: 301, 221 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-silicon-xlarge + rotate: false + xy: 701, 325 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +item-silicon-xxlarge + rotate: false + xy: 301, 171 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-source-icon-large + rotate: false + xy: 301, 121 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-spore-pod-xlarge + rotate: false + xy: 701, 283 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +item-spore-pod-xxlarge + rotate: false + xy: 301, 71 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-surge-alloy-xlarge + rotate: false + xy: 701, 241 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +item-surge-alloy-xxlarge + rotate: false + xy: 301, 21 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-thorium-xlarge + rotate: false + xy: 701, 199 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +item-thorium-xxlarge + rotate: false + xy: 351, 971 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-titanium-xlarge + rotate: false + xy: 701, 157 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +item-titanium-xxlarge + rotate: false + xy: 351, 921 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +item-void-icon-large + rotate: false + xy: 351, 871 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +javelin-ship-pad-icon-large + rotate: false + xy: 351, 771 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +junction-icon-large + rotate: false + xy: 351, 671 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +kiln-icon-large + rotate: false + xy: 351, 621 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +lancer-icon-large + rotate: false + xy: 351, 571 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +laser-drill-icon-large + rotate: false + xy: 351, 521 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +launch-pad-icon-large + rotate: false + xy: 351, 471 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +launch-pad-large-icon-large + rotate: false + xy: 351, 421 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +liquid-junction-icon-large + rotate: false + xy: 351, 321 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +liquid-router-icon-large + rotate: false + xy: 351, 271 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +liquid-source-icon-large + rotate: false + xy: 351, 221 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +liquid-tank-icon-large + rotate: false + xy: 351, 171 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +magmarock-icon-large + rotate: false + xy: 351, 121 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +mass-driver-icon-large + rotate: false + xy: 351, 71 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +mech-icon-alpha-mech + rotate: false + xy: 351, 21 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +mech-icon-dart-ship + rotate: false + xy: 401, 971 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +mech-icon-delta-mech + rotate: false + xy: 401, 921 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +mech-icon-javelin-ship + rotate: false + xy: 401, 871 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +mechanical-drill-icon-large + rotate: false + xy: 401, 821 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +mechanical-pump-icon-large + rotate: false + xy: 401, 771 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +meltdown-icon-large + rotate: false + xy: 401, 721 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +melter-icon-large + rotate: false + xy: 401, 671 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +mend-projector-icon-large + rotate: false + xy: 401, 621 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +mender-icon-large + rotate: false + xy: 401, 571 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +metal-floor-2-icon-large + rotate: false + xy: 401, 521 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +metal-floor-3-icon-large + rotate: false + xy: 401, 471 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +metal-floor-5-icon-large + rotate: false + xy: 401, 421 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +metal-floor-damaged-icon-large + rotate: false + xy: 401, 371 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +metal-floor-icon-large + rotate: false + xy: 401, 321 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +moss-icon-large + rotate: false + xy: 401, 271 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +multi-press-icon-large + rotate: false + xy: 401, 221 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +oil-extractor-icon-large + rotate: false + xy: 401, 171 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +omega-mech-pad-icon-large + rotate: false + xy: 401, 121 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +ore-coal-icon-large + rotate: false + xy: 401, 71 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +ore-copper-icon-large + rotate: false + xy: 401, 21 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +ore-lead-icon-large + rotate: false + xy: 451, 971 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +ore-scrap-icon-large + rotate: false + xy: 451, 921 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +ore-thorium-icon-large + rotate: false + xy: 451, 871 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +ore-titanium-icon-large + rotate: false + xy: 451, 821 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +overdrive-projector-icon-large + rotate: false + xy: 451, 771 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +overflow-gate-icon-large + rotate: false + xy: 451, 721 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +pebbles-icon-large + rotate: false + xy: 451, 671 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +phantom-factory-icon-large + rotate: false + xy: 451, 621 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +phase-conduit-icon-large + rotate: false + xy: 451, 571 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +phase-conveyor-icon-large + rotate: false + xy: 451, 521 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +phase-wall-icon-large + rotate: false + xy: 451, 471 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +phase-wall-large-icon-large + rotate: false + xy: 451, 421 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +phase-weaver-icon-large + rotate: false + xy: 451, 371 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +plastanium-compressor-icon-large + rotate: false + xy: 451, 271 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +pneumatic-drill-icon-large + rotate: false + xy: 451, 221 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +power-node-icon-large + rotate: false + xy: 451, 171 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +power-node-large-icon-large + rotate: false + xy: 451, 121 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +power-source-icon-large + rotate: false + xy: 451, 71 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +power-void-icon-large + rotate: false + xy: 451, 21 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +pulse-conduit-icon-large + rotate: false + xy: 501, 971 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +pulverizer-icon-large + rotate: false + xy: 501, 921 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +pyratite-mixer-icon-large + rotate: false + xy: 501, 871 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +repair-point-icon-large + rotate: false + xy: 501, 771 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +revenant-factory-icon-large + rotate: false + xy: 501, 721 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +ripple-icon-large + rotate: false + xy: 501, 621 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +rocks-icon-large + rotate: false + xy: 501, 521 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +rotary-pump-icon-large + rotate: false + xy: 501, 471 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +router-icon-large + rotate: false + xy: 501, 421 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +rtg-generator-icon-large + rotate: false + xy: 501, 371 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +salt-icon-large + rotate: false + xy: 501, 321 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +saltrocks-icon-large + rotate: false + xy: 501, 271 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +salvo-icon-large + rotate: false + xy: 501, 221 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +sand-icon-large + rotate: false + xy: 501, 171 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +sand-water-icon-large + rotate: false + xy: 501, 121 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +sandrocks-icon-large + rotate: false + xy: 501, 71 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +scatter-icon-large + rotate: false + xy: 501, 21 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +scorch-icon-large + rotate: false + xy: 551, 971 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +scrap-wall-gigantic-icon-large + rotate: false + xy: 551, 921 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +scrap-wall-huge-icon-large + rotate: false + xy: 551, 871 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +scrap-wall-icon-large + rotate: false + xy: 551, 821 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +scrap-wall-large-icon-large + rotate: false + xy: 551, 771 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +separator-icon-large + rotate: false + xy: 551, 721 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +shale-boulder-icon-large + rotate: false + xy: 551, 671 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +shale-icon-large + rotate: false + xy: 551, 621 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +shalerocks-icon-large + rotate: false + xy: 551, 571 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +shock-mine-icon-large + rotate: false + xy: 551, 521 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +shrubs-icon-large + rotate: false + xy: 551, 421 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +silicon-smelter-icon-large + rotate: false + xy: 551, 371 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +snow-icon-large + rotate: false + xy: 551, 321 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +snowrocks-icon-large + rotate: false + xy: 551, 221 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +solar-panel-icon-large + rotate: false + xy: 551, 171 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +solar-panel-large-icon-large + rotate: false + xy: 551, 121 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +sorter-icon-large + rotate: false + xy: 551, 71 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +spawn-icon-large + rotate: false + xy: 551, 21 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +spectre-icon-large + rotate: false + xy: 601, 971 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +spirit-factory-icon-large + rotate: false + xy: 601, 871 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +spore-cluster-icon-large + rotate: false + xy: 601, 821 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +spore-moss-icon-large + rotate: false + xy: 601, 771 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +spore-press-icon-large + rotate: false + xy: 601, 671 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +sporerocks-icon-large + rotate: false + xy: 601, 621 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +stone-icon-large + rotate: false + xy: 601, 571 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +surge-tower-icon-large + rotate: false + xy: 601, 521 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +surge-wall-icon-large + rotate: false + xy: 601, 471 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +surge-wall-large-icon-large + rotate: false + xy: 601, 421 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +swarmer-icon-large + rotate: false + xy: 601, 321 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +tainted-water-icon-large + rotate: false + xy: 601, 271 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +tar-icon-large + rotate: false + xy: 601, 221 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +tau-mech-pad-icon-large + rotate: false + xy: 601, 71 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +tendrils-icon-large + rotate: false + xy: 601, 21 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +thermal-generator-icon-large + rotate: false + xy: 651, 971 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +thermal-pump-icon-large + rotate: false + xy: 651, 921 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +thorium-reactor-icon-large + rotate: false + xy: 651, 871 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +thorium-wall-icon-large + rotate: false + xy: 651, 821 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +thorium-wall-large-icon-large + rotate: false + xy: 651, 771 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +thruster-icon-large + rotate: false + xy: 651, 721 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +titan-factory-icon-large + rotate: false + xy: 651, 671 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +titanium-conveyor-icon-large + rotate: false + xy: 651, 621 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +titanium-wall-icon-large + rotate: false + xy: 651, 571 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +titanium-wall-large-icon-large + rotate: false + xy: 651, 521 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +trident-ship-pad-icon-large + rotate: false + xy: 651, 471 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +turbine-generator-icon-large + rotate: false + xy: 651, 421 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +unit-icon-crawler + rotate: false + xy: 651, 371 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +unit-icon-dagger + rotate: false + xy: 651, 321 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +unloader-icon-large + rotate: false + xy: 651, 271 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +vault-icon-large + rotate: false + xy: 651, 221 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +water-extractor-icon-large + rotate: false + xy: 651, 171 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +water-icon-large + rotate: false + xy: 651, 121 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +wave-icon-large + rotate: false + xy: 651, 71 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +white-tree-dead-icon-large + rotate: false + xy: 651, 21 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +white-tree-icon-large + rotate: false + xy: 701, 971 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +wraith-factory-icon-large + rotate: false + xy: 701, 871 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +tau-mech-base + rotate: false + xy: 601, 171 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +tau-mech-leg + rotate: false + xy: 601, 121 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +javelin-ship + rotate: false + xy: 351, 821 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +javelin-ship-shield + rotate: false + xy: 351, 721 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +button + rotate: false + xy: 751, 905 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 + offset: 0, 0 + index: -1 +button-disabled + rotate: false + xy: 701, 60 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 + offset: 0, 0 + index: -1 +button-down + rotate: false + xy: 739, 60 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 + offset: 0, 0 + index: -1 +button-edge-1 + rotate: false + xy: 701, 31 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 + offset: 0, 0 + index: -1 +button-edge-2 + rotate: false + xy: 739, 31 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 + offset: 0, 0 + index: -1 +button-edge-3 + rotate: false + xy: 701, 2 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 + offset: 0, 0 + index: -1 +button-edge-4 + rotate: false + xy: 739, 2 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 + offset: 0, 0 + index: -1 +button-over + rotate: false + xy: 743, 842 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 + offset: 0, 0 + index: -1 +button-right + rotate: false + xy: 751, 934 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 + offset: 0, 0 + index: -1 +button-right-down + rotate: false + xy: 751, 992 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 + offset: 0, 0 + index: -1 +button-right-over + rotate: false + xy: 751, 963 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 + offset: 0, 0 + index: -1 +check-disabled + rotate: false + xy: 789, 919 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +check-off + rotate: false + xy: 789, 885 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +check-on + rotate: false + xy: 781, 808 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +check-on-disabled + rotate: false + xy: 781, 774 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +check-on-over + rotate: false + xy: 781, 740 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +check-over + rotate: false + xy: 781, 706 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +content-background + rotate: false + xy: 743, 755 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 + offset: 0, 0 + index: -1 +content-background-locked + rotate: false + xy: 751, 876 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 + offset: 0, 0 + index: -1 +content-background-noitems + rotate: false + xy: 743, 813 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 + offset: 0, 0 + index: -1 +content-background-over + rotate: false + xy: 743, 784 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 + offset: 0, 0 + index: -1 +empty-sector + rotate: false + xy: 981, 137 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-about + rotate: false + xy: 1, 371 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-about-small + rotate: false + xy: 985, 681 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-add + rotate: false + xy: 51, 355 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-add-small + rotate: false + xy: 985, 647 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-admin + rotate: false + xy: 1, 321 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-admin-small + rotate: false + xy: 985, 613 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-admin-small-small + rotate: false + xy: 985, 613 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-arrow + rotate: false + xy: 51, 305 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-arrow-16 + rotate: false + xy: 51, 305 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-arrow-16-small + rotate: false + xy: 985, 579 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-arrow-small + rotate: false + xy: 985, 579 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-arrow-down + rotate: false + xy: 1, 271 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-arrow-down-small + rotate: false + xy: 985, 545 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-arrow-left + rotate: false + xy: 51, 255 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-arrow-left-small + rotate: false + xy: 985, 511 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-arrow-right + rotate: false + xy: 1, 221 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-arrow-right-small + rotate: false + xy: 985, 477 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-arrow-up + rotate: false + xy: 51, 205 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-arrow-up-small + rotate: false + xy: 985, 443 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-back + rotate: false + xy: 1, 171 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-ban + rotate: false + xy: 51, 155 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-break + rotate: false + xy: 1, 121 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-cancel + rotate: false + xy: 51, 105 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-chat + rotate: false + xy: 1, 71 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-check + rotate: false + xy: 51, 55 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-copy + rotate: false + xy: 1, 21 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-crafting + rotate: false + xy: 51, 5 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-cursor + rotate: false + xy: 101, 971 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-database + rotate: false + xy: 101, 921 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-defense + rotate: false + xy: 101, 871 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-dev-builds + rotate: false + xy: 101, 821 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-diagonal + rotate: false + xy: 101, 771 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-discord + rotate: false + xy: 101, 721 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-distribution + rotate: false + xy: 101, 671 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-donate + rotate: false + xy: 101, 621 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-dots + rotate: false + xy: 101, 571 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-editor + rotate: false + xy: 101, 521 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-effect + rotate: false + xy: 101, 471 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-elevation + rotate: false + xy: 101, 421 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-eraser + rotate: false + xy: 101, 371 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-exit + rotate: false + xy: 101, 321 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-file + rotate: false + xy: 101, 271 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-file-image + rotate: false + xy: 101, 221 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-file-text + rotate: false + xy: 101, 171 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-fill + rotate: false + xy: 101, 121 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-floppy + rotate: false + xy: 101, 71 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-floppy-16 + rotate: false + xy: 101, 21 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-folder + rotate: false + xy: 151, 971 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-folder-parent + rotate: false + xy: 151, 921 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-github + rotate: false + xy: 151, 871 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-google-play + rotate: false + xy: 151, 821 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-grid + rotate: false + xy: 151, 771 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-home + rotate: false + xy: 151, 721 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-host + rotate: false + xy: 151, 671 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-info + rotate: false + xy: 151, 621 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-itch.io + rotate: false + xy: 151, 571 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-item + rotate: false + xy: 151, 521 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-line + rotate: false + xy: 151, 471 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-link + rotate: false + xy: 151, 421 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-liquid + rotate: false + xy: 151, 371 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-liquid-consume + rotate: false + xy: 151, 321 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-load + rotate: false + xy: 151, 271 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-load-image + rotate: false + xy: 151, 221 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-load-map + rotate: false + xy: 151, 171 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-loading + rotate: false + xy: 151, 121 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-locked + rotate: false + xy: 151, 71 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-map + rotate: false + xy: 151, 21 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-menu + rotate: false + xy: 201, 971 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-menu-large + rotate: false + xy: 201, 921 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-missing + rotate: false + xy: 201, 871 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-none + rotate: false + xy: 201, 821 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-paste + rotate: false + xy: 201, 771 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-pause + rotate: false + xy: 201, 721 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-pencil + rotate: false + xy: 201, 671 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-pick + rotate: false + xy: 201, 621 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-play + rotate: false + xy: 201, 571 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-play-2 + rotate: false + xy: 201, 521 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-play-custom + rotate: false + xy: 201, 471 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-players + rotate: false + xy: 201, 421 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-power + rotate: false + xy: 201, 371 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-production + rotate: false + xy: 201, 321 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-quit + rotate: false + xy: 201, 271 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-redo + rotate: false + xy: 201, 221 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-refresh + rotate: false + xy: 201, 171 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-rename + rotate: false + xy: 201, 121 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-resize + rotate: false + xy: 201, 71 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-rotate + rotate: false + xy: 201, 21 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-rotate-arrow + rotate: false + xy: 251, 971 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-rotate-left + rotate: false + xy: 251, 921 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-rotate-right + rotate: false + xy: 251, 871 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-save + rotate: false + xy: 251, 821 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-save-image + rotate: false + xy: 251, 771 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-save-map + rotate: false + xy: 251, 721 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-settings + rotate: false + xy: 251, 671 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-spray + rotate: false + xy: 251, 621 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-terrain + rotate: false + xy: 251, 571 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-tools + rotate: false + xy: 251, 521 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-trash + rotate: false + xy: 251, 471 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-trash-16 + rotate: false + xy: 251, 421 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-tree + rotate: false + xy: 251, 371 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-trello + rotate: false + xy: 251, 321 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-turret + rotate: false + xy: 251, 271 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-tutorial + rotate: false + xy: 251, 221 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-undo + rotate: false + xy: 251, 171 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-units + rotate: false + xy: 251, 121 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-unlocks + rotate: false + xy: 251, 71 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-upgrade + rotate: false + xy: 251, 21 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-wiki + rotate: false + xy: 301, 971 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +icon-zoom + rotate: false + xy: 301, 921 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +pane + rotate: false + xy: 743, 621 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 + offset: 0, 0 + index: -1 +pane-2 + rotate: false + xy: 743, 650 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 + offset: 0, 0 + index: -1 +scroll-horizontal + rotate: false + xy: 743, 403 + size: 35, 24 + split: 6, 5, 10, 10 + orig: 35, 24 + offset: 0, 0 + index: -1 +scroll-knob-horizontal-black + rotate: false + xy: 701, 131 + size: 40, 24 + split: 11, 10, 10, 10 + orig: 40, 24 + offset: 0, 0 + index: -1 +underline + rotate: false + xy: 743, 429 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 + offset: 0, 0 + index: -1 +underline-2 + rotate: false + xy: 743, 516 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 + offset: 0, 0 + index: -1 +underline-disabled + rotate: false + xy: 743, 487 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 + offset: 0, 0 + index: -1 +underline-red + rotate: false + xy: 743, 458 + size: 36, 27 + split: 12, 12, 12, 12 + orig: 36, 27 + offset: 0, 0 + index: -1 +draug + rotate: false + xy: 1, 871 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +spirit + rotate: false + xy: 601, 921 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +wraith + rotate: false + xy: 701, 921 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +eruption-equip + rotate: false + xy: 51, 963 + size: 48, 56 + orig: 48, 56 + offset: 0, 0 + index: -1 +flakgun-equip + rotate: false + xy: 51, 913 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +flamethrower-equip + rotate: false + xy: 51, 855 + size: 48, 56 + orig: 48, 56 + offset: 0, 0 + index: -1 +heal-blaster-equip + rotate: false + xy: 1, 521 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +lich-missiles-equip + rotate: false + xy: 351, 371 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +reaper-gun-equip + rotate: false + xy: 501, 821 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +revenant-missiles-equip + rotate: false + xy: 501, 671 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +shockgun-equip + rotate: false + xy: 551, 471 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +swarmer-equip + rotate: false + xy: 601, 371 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 + +sprites_fallback5.png +size: 1024,512 +format: RGBA8888 +filter: Nearest,Nearest +repeat: none +mender + rotate: false + xy: 69, 3 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +mender-icon-editor + rotate: false + xy: 69, 3 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +mender-top + rotate: false + xy: 103, 37 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +shock-mine + rotate: false + xy: 545, 343 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium-conveyor-0-0 + rotate: false + xy: 613, 343 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium-conveyor-icon-editor + rotate: false + xy: 613, 343 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium-conveyor-0-1 + rotate: false + xy: 647, 377 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium-conveyor-0-2 + rotate: false + xy: 681, 411 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium-conveyor-0-3 + rotate: false + xy: 715, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium-conveyor-1-0 + rotate: false + xy: 749, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium-conveyor-1-1 + rotate: false + xy: 307, 3 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium-conveyor-1-2 + rotate: false + xy: 341, 37 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium-conveyor-1-3 + rotate: false + xy: 375, 71 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium-conveyor-2-0 + rotate: false + xy: 409, 105 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium-conveyor-2-1 + rotate: false + xy: 443, 139 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium-conveyor-2-2 + rotate: false + xy: 477, 173 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium-conveyor-2-3 + rotate: false + xy: 511, 207 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium-conveyor-3-0 + rotate: false + xy: 545, 241 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium-conveyor-3-1 + rotate: false + xy: 579, 275 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium-conveyor-3-2 + rotate: false + xy: 613, 309 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium-conveyor-3-3 + rotate: false + xy: 647, 343 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium-conveyor-4-0 + rotate: false + xy: 681, 377 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium-conveyor-4-1 + rotate: false + xy: 715, 411 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium-conveyor-4-2 + rotate: false + xy: 749, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium-conveyor-4-3 + rotate: false + xy: 783, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +junction + rotate: false + xy: 239, 241 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +junction-icon-editor + rotate: false + xy: 239, 241 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +overflow-gate + rotate: false + xy: 273, 207 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +overflow-gate-icon-editor + rotate: false + xy: 273, 207 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +phase-conveyor + rotate: false + xy: 511, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +phase-conveyor-icon-editor + rotate: false + xy: 511, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +phase-conveyor-arrow + rotate: false + xy: 545, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +phase-conveyor-bridge + rotate: false + xy: 103, 3 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +phase-conveyor-end + rotate: false + xy: 137, 37 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +router + rotate: false + xy: 307, 139 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +router-icon-editor + rotate: false + xy: 307, 139 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +sorter + rotate: false + xy: 375, 139 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +sorter-icon-editor + rotate: false + xy: 375, 139 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +liquid-junction + rotate: false + xy: 477, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +liquid-junction-icon-editor + rotate: false + xy: 477, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +liquid-router-bottom + rotate: false + xy: 69, 37 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +liquid-router-liquid + rotate: false + xy: 137, 105 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +liquid-router-top + rotate: false + xy: 171, 139 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +mechanical-pump + rotate: false + xy: 409, 377 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +mechanical-pump-icon-editor + rotate: false + xy: 409, 377 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +phase-conduit + rotate: false + xy: 375, 309 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +phase-conduit-icon-editor + rotate: false + xy: 375, 309 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +phase-conduit-arrow + rotate: false + xy: 409, 343 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +phase-conduit-bridge + rotate: false + xy: 443, 377 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +phase-conduit-end + rotate: false + xy: 477, 411 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pulse-conduit-top-0 + rotate: false + xy: 579, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pulse-conduit-top-1 + rotate: false + xy: 137, 3 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pulse-conduit-top-2 + rotate: false + xy: 171, 37 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pulse-conduit-top-3 + rotate: false + xy: 205, 71 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pulse-conduit-top-4 + rotate: false + xy: 239, 105 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pulse-conduit-top-5 + rotate: false + xy: 273, 139 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pulse-conduit-top-6 + rotate: false + xy: 307, 173 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +power-node + rotate: false + xy: 375, 275 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +power-node-icon-editor + rotate: false + xy: 375, 275 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +power-source + rotate: false + xy: 443, 343 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +power-void + rotate: false + xy: 511, 411 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +power-void-icon-editor + rotate: false + xy: 511, 411 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +rtg-generator-top + rotate: false + xy: 375, 207 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +solar-panel + rotate: false + xy: 307, 71 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +solar-panel-icon-editor + rotate: false + xy: 307, 71 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +incinerator + rotate: false + xy: 1, 37 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +incinerator-icon-editor + rotate: false + xy: 1, 37 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-source + rotate: false + xy: 1, 3 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-source-icon-editor + rotate: false + xy: 1, 3 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-void + rotate: false + xy: 171, 173 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-void-icon-editor + rotate: false + xy: 171, 173 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +liquid-source + rotate: false + xy: 239, 207 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +liquid-source-icon-editor + rotate: false + xy: 239, 207 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +melter + rotate: false + xy: 477, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +melter-icon-editor + rotate: false + xy: 477, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pulverizer + rotate: false + xy: 341, 207 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pulverizer-rotator + rotate: false + xy: 409, 275 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pump-liquid + rotate: false + xy: 443, 309 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +unloader + rotate: false + xy: 511, 173 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +unloader-icon-editor + rotate: false + xy: 511, 173 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scorch-heat + rotate: false + xy: 579, 411 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +repair-point-base + rotate: false + xy: 545, 411 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +phase-wall + rotate: false + xy: 171, 71 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +phase-wall-icon-editor + rotate: false + xy: 171, 71 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scrap-wall1 + rotate: false + xy: 647, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scrap-wall-icon-editor + rotate: false + xy: 647, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scrap-wall2 + rotate: false + xy: 205, 3 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scrap-wall3 + rotate: false + xy: 239, 37 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scrap-wall4 + rotate: false + xy: 273, 71 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scrap-wall5 + rotate: false + xy: 273, 71 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +surge-wall + rotate: false + xy: 681, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +surge-wall-icon-editor + rotate: false + xy: 681, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +thorium-wall + rotate: false + xy: 477, 207 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +thorium-wall-icon-editor + rotate: false + xy: 477, 207 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium-wall + rotate: false + xy: 341, 3 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium-wall-icon-editor + rotate: false + xy: 341, 3 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +liquid-router-icon-editor + rotate: false + xy: 103, 71 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +liquid-router-icon-full + rotate: false + xy: 103, 71 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pebbles-icon-editor + rotate: false + xy: 307, 241 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +power-source-icon-editor + rotate: false + xy: 477, 377 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pulse-conduit-icon-editor + rotate: false + xy: 545, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pulse-conduit-icon-full + rotate: false + xy: 545, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pulverizer-icon-editor + rotate: false + xy: 375, 241 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pulverizer-icon-full + rotate: false + xy: 375, 241 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +repair-point-icon-editor + rotate: false + xy: 579, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +repair-point-icon-full + rotate: false + xy: 579, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +rocks-icon-editor + rotate: false + xy: 239, 71 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +saltrocks-icon-editor + rotate: false + xy: 409, 241 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +sandrocks-icon-editor + rotate: false + xy: 477, 309 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scorch-icon-editor + rotate: false + xy: 613, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scorch-icon-full + rotate: false + xy: 613, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +shale-boulder-icon-editor + rotate: false + xy: 477, 275 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +shalerocks-icon-editor + rotate: false + xy: 511, 309 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +shock-mine-icon-editor + rotate: false + xy: 579, 377 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +shrubs-icon-editor + rotate: false + xy: 647, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +snowrocks-icon-editor + rotate: false + xy: 273, 37 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +spawn-icon-editor + rotate: false + xy: 409, 173 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +sporerocks-icon-editor + rotate: false + xy: 613, 377 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +tendrils-icon-editor + rotate: false + xy: 341, 71 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +casing + rotate: false + xy: 463, 1 + size: 8, 16 + orig: 8, 16 + offset: 0, 0 + index: -1 +laser + rotate: false + xy: 1019, 347 + size: 4, 48 + orig: 4, 48 + offset: 0, 0 + index: -1 +minelaser + rotate: false + xy: 1019, 297 + size: 4, 48 + orig: 4, 48 + offset: 0, 0 + index: -1 +scale_marker + rotate: false + xy: 811, 327 + size: 4, 4 + orig: 4, 4 + offset: 0, 0 + index: -1 +scorch1 + rotate: false + xy: 817, 411 + size: 28, 100 + orig: 28, 100 + offset: 0, 0 + index: -1 +scorch2 + rotate: false + xy: 847, 411 + size: 28, 100 + orig: 28, 100 + offset: 0, 0 + index: -1 +scorch3 + rotate: false + xy: 877, 411 + size: 28, 100 + orig: 28, 100 + offset: 0, 0 + index: -1 +scorch4 + rotate: false + xy: 907, 411 + size: 28, 100 + orig: 28, 100 + offset: 0, 0 + index: -1 +scorch5 + rotate: false + xy: 937, 411 + size: 28, 100 + orig: 28, 100 + offset: 0, 0 + index: -1 +shot + rotate: false + xy: 613, 411 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +transfer + rotate: false + xy: 835, 17 + size: 4, 48 + orig: 4, 48 + offset: 0, 0 + index: -1 +transfer-arrow + rotate: false + xy: 409, 71 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +alloy-smelter-icon-small + rotate: false + xy: 375, 11 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +arc-icon-small + rotate: false + xy: 409, 45 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +battery-icon-small + rotate: false + xy: 443, 79 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +battery-large-icon-small + rotate: false + xy: 477, 113 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +blast-drill-icon-small + rotate: false + xy: 511, 147 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +blast-mixer-icon-small + rotate: false + xy: 545, 181 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +bridge-conduit-icon-small + rotate: false + xy: 579, 215 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +bridge-conveyor-icon-small + rotate: false + xy: 613, 249 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +char-icon-small + rotate: false + xy: 681, 317 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +cliffs-icon-small + rotate: false + xy: 715, 351 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +coal-centrifuge-icon-small + rotate: false + xy: 749, 379 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +combustion-generator-icon-small + rotate: false + xy: 996, 449 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +conduit-icon-small + rotate: false + xy: 996, 423 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +container-icon-small + rotate: false + xy: 780, 413 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +conveyor-icon-small + rotate: false + xy: 996, 397 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +copper-wall-icon-small + rotate: false + xy: 401, 11 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +copper-wall-large-icon-small + rotate: false + xy: 435, 45 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +core-foundation-icon-small + rotate: false + xy: 427, 19 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +core-nucleus-icon-small + rotate: false + xy: 469, 79 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +core-shard-icon-small + rotate: false + xy: 461, 53 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +craters-icon-small + rotate: false + xy: 503, 113 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +crawler-factory-icon-small + rotate: false + xy: 495, 87 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +cryofluidmixer-icon-small + rotate: false + xy: 537, 147 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +cultivator-icon-small + rotate: false + xy: 529, 121 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +cyclone-icon-small + rotate: false + xy: 571, 181 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +dagger-factory-icon-small + rotate: false + xy: 563, 155 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +dark-metal-icon-small + rotate: false + xy: 605, 215 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +dark-panel-1-icon-small + rotate: false + xy: 597, 189 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +dark-panel-2-icon-small + rotate: false + xy: 639, 249 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +dark-panel-3-icon-small + rotate: false + xy: 631, 223 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +dark-panel-4-icon-small + rotate: false + xy: 673, 283 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +dark-panel-5-icon-small + rotate: false + xy: 665, 257 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +dark-panel-6-icon-small + rotate: false + xy: 707, 317 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +darksand-icon-small + rotate: false + xy: 699, 291 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +darksand-tainted-water-icon-small + rotate: false + xy: 741, 351 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +darksand-water-icon-small + rotate: false + xy: 733, 325 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +dart-mech-pad-icon-small + rotate: false + xy: 780, 387 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +deepwater-icon-small + rotate: false + xy: 806, 385 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +delta-mech-pad-icon-small + rotate: false + xy: 832, 385 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +differential-generator-icon-small + rotate: false + xy: 858, 385 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +distributor-icon-small + rotate: false + xy: 884, 385 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +door-icon-small + rotate: false + xy: 910, 385 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +door-large-icon-small + rotate: false + xy: 936, 385 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +draug-factory-icon-small + rotate: false + xy: 453, 19 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +dunerocks-icon-small + rotate: false + xy: 487, 53 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +duo-icon-small + rotate: false + xy: 479, 27 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +force-projector-icon-small + rotate: false + xy: 479, 1 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +fortress-factory-icon-small + rotate: false + xy: 521, 87 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +fuse-icon-small + rotate: false + xy: 513, 61 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +ghoul-factory-icon-small + rotate: false + xy: 555, 121 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +glaive-ship-pad-icon-small + rotate: false + xy: 547, 95 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +graphite-press-icon-small + rotate: false + xy: 589, 155 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +grass-icon-small + rotate: false + xy: 581, 129 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +hail-icon-small + rotate: false + xy: 623, 189 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +holostone-icon-small + rotate: false + xy: 615, 163 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +hotrock-icon-small + rotate: false + xy: 657, 223 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +ice-icon-small + rotate: false + xy: 649, 197 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +ice-snow-icon-small + rotate: false + xy: 691, 257 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +icerocks-icon-small + rotate: false + xy: 683, 231 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +ignarock-icon-small + rotate: false + xy: 725, 291 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +impact-reactor-icon-medium + rotate: false + xy: 409, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +impact-reactor-icon-small + rotate: false + xy: 717, 265 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +incinerator-icon-small + rotate: false + xy: 759, 325 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-blast-compound-medium + rotate: false + xy: 743, 257 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-blast-compound-small + rotate: false + xy: 427, 1 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +item-coal-medium + rotate: false + xy: 717, 239 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-coal-small + rotate: false + xy: 445, 1 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +item-copper-medium + rotate: false + xy: 743, 231 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-copper-small + rotate: false + xy: 817, 49 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +item-graphite-medium + rotate: false + xy: 505, 27 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-graphite-small + rotate: false + xy: 817, 31 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +item-lead-medium + rotate: false + xy: 505, 1 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-lead-small + rotate: false + xy: 817, 13 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +item-metaglass-medium + rotate: false + xy: 539, 61 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-metaglass-small + rotate: false + xy: 867, 159 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +item-phase-fabric-medium + rotate: false + xy: 531, 35 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-phase-fabric-small + rotate: false + xy: 885, 159 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +item-plastanium-medium + rotate: false + xy: 531, 9 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-plastanium-small + rotate: false + xy: 903, 159 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +item-pyratite-medium + rotate: false + xy: 573, 95 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-pyratite-small + rotate: false + xy: 921, 159 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +item-sand-medium + rotate: false + xy: 565, 69 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-sand-small + rotate: false + xy: 867, 141 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +item-scrap-medium + rotate: false + xy: 607, 129 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-scrap-small + rotate: false + xy: 885, 141 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +item-silicon-medium + rotate: false + xy: 599, 103 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-silicon-small + rotate: false + xy: 903, 141 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +item-source-icon-small + rotate: false + xy: 641, 163 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-spore-pod-medium + rotate: false + xy: 633, 137 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-spore-pod-small + rotate: false + xy: 921, 141 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +item-surge-alloy-medium + rotate: false + xy: 675, 197 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-surge-alloy-small + rotate: false + xy: 859, 123 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +item-thorium-medium + rotate: false + xy: 667, 171 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-thorium-small + rotate: false + xy: 877, 123 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +item-titanium-medium + rotate: false + xy: 777, 299 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-titanium-small + rotate: false + xy: 895, 123 + size: 16, 16 + orig: 16, 16 + offset: 0, 0 + index: -1 +item-void-icon-small + rotate: false + xy: 777, 273 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +javelin-ship-pad-icon-medium + rotate: false + xy: 205, 207 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +javelin-ship-pad-icon-small + rotate: false + xy: 769, 247 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +junction-icon-small + rotate: false + xy: 769, 221 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +kiln-icon-medium + rotate: false + xy: 273, 275 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +kiln-icon-small + rotate: false + xy: 557, 35 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +lancer-icon-medium + rotate: false + xy: 307, 309 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +lancer-icon-small + rotate: false + xy: 557, 9 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +laser-drill-icon-medium + rotate: false + xy: 341, 343 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +laser-drill-icon-small + rotate: false + xy: 591, 69 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +launch-pad-icon-medium + rotate: false + xy: 375, 377 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +launch-pad-icon-small + rotate: false + xy: 583, 43 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +launch-pad-large-icon-medium + rotate: false + xy: 409, 411 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +launch-pad-large-icon-small + rotate: false + xy: 583, 17 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +liquid-junction-icon-small + rotate: false + xy: 625, 103 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +liquid-router-icon-small + rotate: false + xy: 617, 77 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +liquid-source-icon-small + rotate: false + xy: 659, 137 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +liquid-tank-icon-medium + rotate: false + xy: 273, 241 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +liquid-tank-icon-small + rotate: false + xy: 651, 111 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +magmarock-icon-small + rotate: false + xy: 693, 171 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +mass-driver-icon-medium + rotate: false + xy: 341, 309 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +mass-driver-icon-small + rotate: false + xy: 685, 145 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +mechanical-drill-icon-medium + rotate: false + xy: 375, 343 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +mechanical-drill-icon-small + rotate: false + xy: 795, 247 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +mechanical-pump-icon-small + rotate: false + xy: 795, 221 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +meltdown-icon-medium + rotate: false + xy: 443, 411 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +meltdown-icon-small + rotate: false + xy: 609, 43 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +melter-icon-small + rotate: false + xy: 609, 17 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +mend-projector-icon-medium + rotate: false + xy: 511, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +mend-projector-icon-small + rotate: false + xy: 643, 77 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +mender-icon-small + rotate: false + xy: 635, 51 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +metal-floor-2-icon-small + rotate: false + xy: 635, 25 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +metal-floor-3-icon-small + rotate: false + xy: 677, 111 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +metal-floor-5-icon-small + rotate: false + xy: 669, 85 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +metal-floor-damaged-icon-small + rotate: false + xy: 711, 145 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +metal-floor-icon-small + rotate: false + xy: 703, 119 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +moss-icon-small + rotate: false + xy: 661, 51 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +multi-press-icon-medium + rotate: false + xy: 137, 71 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +multi-press-icon-small + rotate: false + xy: 661, 25 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +oil-extractor-icon-medium + rotate: false + xy: 171, 105 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +oil-extractor-icon-small + rotate: false + xy: 695, 85 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +omega-mech-pad-icon-medium + rotate: false + xy: 205, 139 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +omega-mech-pad-icon-small + rotate: false + xy: 687, 59 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +ore-coal-icon-small + rotate: false + xy: 687, 33 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +ore-copper-icon-small + rotate: false + xy: 729, 119 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +ore-lead-icon-small + rotate: false + xy: 721, 93 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +ore-scrap-icon-small + rotate: false + xy: 713, 59 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +ore-thorium-icon-small + rotate: false + xy: 713, 33 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +ore-titanium-icon-small + rotate: false + xy: 687, 7 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +overdrive-projector-icon-medium + rotate: false + xy: 239, 173 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +overdrive-projector-icon-small + rotate: false + xy: 713, 7 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +overflow-gate-icon-small + rotate: false + xy: 747, 93 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +pebbles-icon-small + rotate: false + xy: 739, 67 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +phantom-factory-icon-medium + rotate: false + xy: 341, 275 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +phantom-factory-icon-small + rotate: false + xy: 739, 41 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +phase-conduit-icon-small + rotate: false + xy: 739, 15 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +phase-conveyor-icon-small + rotate: false + xy: 765, 67 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +phase-wall-icon-small + rotate: false + xy: 765, 41 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +phase-wall-large-icon-medium + rotate: false + xy: 205, 105 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +phase-wall-large-icon-small + rotate: false + xy: 765, 15 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +phase-weaver-icon-medium + rotate: false + xy: 239, 139 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +phase-weaver-icon-small + rotate: false + xy: 767, 353 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +pine-icon-medium + rotate: false + xy: 273, 173 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pine-icon-small + rotate: false + xy: 793, 359 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +plastanium-compressor-icon-medium + rotate: false + xy: 307, 207 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +plastanium-compressor-icon-small + rotate: false + xy: 819, 359 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +pneumatic-drill-icon-medium + rotate: false + xy: 341, 241 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pneumatic-drill-icon-small + rotate: false + xy: 845, 359 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +power-node-icon-small + rotate: false + xy: 871, 359 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +power-node-large-icon-medium + rotate: false + xy: 409, 309 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +power-node-large-icon-small + rotate: false + xy: 897, 359 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +power-source-icon-small + rotate: false + xy: 923, 359 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +power-void-icon-small + rotate: false + xy: 949, 359 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +pulse-conduit-icon-small + rotate: false + xy: 962, 385 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +pulverizer-icon-small + rotate: false + xy: 975, 359 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +pyratite-mixer-icon-medium + rotate: false + xy: 477, 343 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pyratite-mixer-icon-small + rotate: false + xy: 785, 327 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +repair-point + rotate: false + xy: 511, 377 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +repair-point-icon-small + rotate: false + xy: 811, 333 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +revenant-factory-icon-medium + rotate: false + xy: 613, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +revenant-factory-icon-small + rotate: false + xy: 837, 333 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +ripple-icon-medium + rotate: false + xy: 171, 3 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ripple-icon-small + rotate: false + xy: 863, 333 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +rock-icon-medium + rotate: false + xy: 205, 37 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +rock-icon-small + rotate: false + xy: 889, 333 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +rocks-icon-small + rotate: false + xy: 915, 333 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +rotary-pump-icon-medium + rotate: false + xy: 273, 105 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +rotary-pump-icon-small + rotate: false + xy: 941, 333 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +router-icon-small + rotate: false + xy: 967, 333 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +rtg-generator-icon-medium + rotate: false + xy: 341, 173 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +rtg-generator-icon-small + rotate: false + xy: 993, 333 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +salt-icon-small + rotate: false + xy: 803, 301 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +saltrocks-icon-small + rotate: false + xy: 803, 275 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +salvo-icon-medium + rotate: false + xy: 443, 275 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +salvo-icon-small + rotate: false + xy: 829, 307 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +sand-icon-small + rotate: false + xy: 829, 281 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +sand-water-icon-small + rotate: false + xy: 855, 307 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +sandrocks-icon-small + rotate: false + xy: 855, 281 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +scatter-icon-medium + rotate: false + xy: 511, 343 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scatter-icon-small + rotate: false + xy: 881, 307 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +scorch + rotate: false + xy: 545, 377 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scorch-icon-small + rotate: false + xy: 881, 281 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +scrap-wall-gigantic-icon-medium + rotate: false + xy: 307, 105 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scrap-wall-gigantic-icon-small + rotate: false + xy: 907, 307 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +scrap-wall-huge-icon-medium + rotate: false + xy: 341, 139 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scrap-wall-huge-icon-small + rotate: false + xy: 907, 281 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +scrap-wall-icon-small + rotate: false + xy: 933, 307 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +scrap-wall-large-icon-medium + rotate: false + xy: 375, 173 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scrap-wall-large-icon-small + rotate: false + xy: 933, 281 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +separator-icon-medium + rotate: false + xy: 443, 241 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +separator-icon-small + rotate: false + xy: 959, 265 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +shale-boulder-icon-small + rotate: false + xy: 985, 270 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +shale-icon-small + rotate: false + xy: 821, 249 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +shalerocks-icon-small + rotate: false + xy: 821, 223 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +shock-mine-icon-small + rotate: false + xy: 847, 255 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +shrubs-icon-small + rotate: false + xy: 847, 229 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +silicon-smelter-icon-medium + rotate: false + xy: 681, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +silicon-smelter-icon-small + rotate: false + xy: 873, 255 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +snow-icon-small + rotate: false + xy: 873, 229 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +snowrock-icon-medium + rotate: false + xy: 239, 3 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +snowrock-icon-small + rotate: false + xy: 899, 255 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +snowrocks-icon-small + rotate: false + xy: 899, 229 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +solar-panel-icon-small + rotate: false + xy: 925, 255 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +solar-panel-large-icon-medium + rotate: false + xy: 341, 105 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +solar-panel-large-icon-small + rotate: false + xy: 925, 229 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +sorter-icon-small + rotate: false + xy: 951, 239 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +spawn-icon-small + rotate: false + xy: 951, 213 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +spectre-icon-medium + rotate: false + xy: 443, 207 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +spectre-icon-small + rotate: false + xy: 977, 239 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +spirit-factory-icon-medium + rotate: false + xy: 477, 241 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +spirit-factory-icon-small + rotate: false + xy: 977, 213 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +spore-cluster-icon-medium + rotate: false + xy: 511, 275 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +spore-cluster-icon-small + rotate: false + xy: 847, 203 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +spore-moss-icon-small + rotate: false + xy: 873, 203 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +spore-pine-icon-medium + rotate: false + xy: 545, 309 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +spore-pine-icon-small + rotate: false + xy: 899, 203 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +spore-press-icon-medium + rotate: false + xy: 579, 343 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +spore-press-icon-small + rotate: false + xy: 925, 203 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +sporerocks-icon-small + rotate: false + xy: 821, 197 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +stone-icon-small + rotate: false + xy: 719, 171 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +surge-tower-icon-medium + rotate: false + xy: 647, 411 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +surge-tower-icon-small + rotate: false + xy: 737, 145 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +surge-wall-icon-small + rotate: false + xy: 755, 119 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +surge-wall-large-icon-medium + rotate: false + xy: 715, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +surge-wall-large-icon-small + rotate: false + xy: 773, 93 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +swarmer-icon-medium + rotate: false + xy: 273, 3 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +swarmer-icon-small + rotate: false + xy: 745, 171 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +tainted-water-icon-small + rotate: false + xy: 763, 145 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +tar-icon-small + rotate: false + xy: 781, 119 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +tau-mech-pad-icon-medium + rotate: false + xy: 307, 37 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +tau-mech-pad-icon-small + rotate: false + xy: 771, 171 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +tendrils-icon-small + rotate: false + xy: 789, 145 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +thermal-generator-icon-medium + rotate: false + xy: 375, 105 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +thermal-generator-icon-small + rotate: false + xy: 797, 171 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +thermal-pump-icon-medium + rotate: false + xy: 409, 139 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +thermal-pump-icon-small + rotate: false + xy: 823, 171 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +thorium-reactor-icon-medium + rotate: false + xy: 443, 173 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +thorium-reactor-icon-small + rotate: false + xy: 815, 145 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +thorium-wall-icon-small + rotate: false + xy: 807, 119 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +thorium-wall-large-icon-medium + rotate: false + xy: 511, 241 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +thorium-wall-large-icon-small + rotate: false + xy: 799, 93 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +thruster-icon-medium + rotate: false + xy: 545, 275 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +thruster-icon-small + rotate: false + xy: 791, 67 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +titan-factory-icon-medium + rotate: false + xy: 579, 309 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titan-factory-icon-small + rotate: false + xy: 791, 41 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +titanium-conveyor-icon-small + rotate: false + xy: 791, 15 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +titanium-wall-icon-small + rotate: false + xy: 849, 177 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +titanium-wall-large-icon-medium + rotate: false + xy: 375, 37 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium-wall-large-icon-small + rotate: false + xy: 875, 177 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +trident-ship-pad-icon-medium + rotate: false + xy: 443, 105 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +trident-ship-pad-icon-small + rotate: false + xy: 901, 177 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +turbine-generator-icon-medium + rotate: false + xy: 477, 139 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +turbine-generator-icon-small + rotate: false + xy: 927, 177 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +unloader-icon-small + rotate: false + xy: 953, 187 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +vault-icon-medium + rotate: false + xy: 545, 207 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +vault-icon-small + rotate: false + xy: 979, 187 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +water-extractor-icon-medium + rotate: false + xy: 579, 241 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +water-extractor-icon-small + rotate: false + xy: 953, 161 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +water-icon-small + rotate: false + xy: 979, 161 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +wave-icon-medium + rotate: false + xy: 613, 275 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +wave-icon-small + rotate: false + xy: 833, 119 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +white-tree-dead-icon-medium + rotate: false + xy: 647, 309 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +white-tree-dead-icon-small + rotate: false + xy: 825, 93 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +white-tree-icon-medium + rotate: false + xy: 681, 343 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +white-tree-icon-small + rotate: false + xy: 817, 67 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +wraith-factory-icon-medium + rotate: false + xy: 715, 377 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +wraith-factory-icon-small + rotate: false + xy: 841, 145 + size: 24, 24 + orig: 24, 24 + offset: 0, 0 + index: -1 +item-biomatter + rotate: false + xy: 35, 71 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-blast-compound + rotate: false + xy: 69, 105 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-coal + rotate: false + xy: 103, 139 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-copper + rotate: false + xy: 137, 173 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-graphite + rotate: false + xy: 171, 207 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-lead + rotate: false + xy: 205, 241 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-metaglass + rotate: false + xy: 239, 275 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-phase-fabric + rotate: false + xy: 273, 309 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-plastanium + rotate: false + xy: 307, 343 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-pyratite + rotate: false + xy: 341, 377 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-sand + rotate: false + xy: 375, 411 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-scrap + rotate: false + xy: 409, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-silicon + rotate: false + xy: 443, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-spore-pod + rotate: false + xy: 35, 37 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-surge-alloy + rotate: false + xy: 69, 71 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-thorium + rotate: false + xy: 103, 105 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +item-titanium + rotate: false + xy: 137, 139 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +liquid-cryofluid + rotate: false + xy: 443, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +liquid-oil + rotate: false + xy: 35, 3 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +liquid-slag + rotate: false + xy: 205, 173 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +liquid-water + rotate: false + xy: 307, 275 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +blank + rotate: false + xy: 780, 442 + size: 1, 1 + orig: 1, 1 + offset: 0, 0 + index: -1 +clear + rotate: false + xy: 780, 439 + size: 1, 1 + orig: 1, 1 + offset: 0, 0 + index: -1 +clear-editor + rotate: false + xy: 780, 439 + size: 1, 1 + orig: 1, 1 + offset: 0, 0 + index: -1 +bar + rotate: false + xy: 996, 475 + size: 27, 36 + split: 9, 9, 9, 9 + orig: 27, 36 + offset: 0, 0 + index: -1 +bar-top + rotate: false + xy: 967, 475 + size: 27, 36 + split: 9, 10, 9, 10 + orig: 27, 36 + offset: 0, 0 + index: -1 +button-select + rotate: false + xy: 647, 283 + size: 24, 24 + split: 4, 4, 4, 4 + orig: 24, 24 + offset: 0, 0 + index: -1 +clear + rotate: false + xy: 988, 385 + size: 10, 10 + orig: 10, 10 + offset: 0, 0 + index: -1 +editor-clear + rotate: false + xy: 988, 385 + size: 10, 10 + orig: 10, 10 + offset: 0, 0 + index: -1 +cursor + rotate: false + xy: 793, 353 + size: 4, 4 + orig: 4, 4 + offset: 0, 0 + index: -1 +icon-back-small + rotate: false + xy: 1, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-ban-small + rotate: false + xy: 1, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-break-small + rotate: false + xy: 35, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-cancel-small + rotate: false + xy: 1, 411 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-chat-small + rotate: false + xy: 35, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-check-small + rotate: false + xy: 69, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-copy-small + rotate: false + xy: 1, 377 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-crafting-small + rotate: false + xy: 35, 411 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-cursor-small + rotate: false + xy: 69, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-database-small + rotate: false + xy: 103, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-defense-small + rotate: false + xy: 1, 343 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-dev-builds-small + rotate: false + xy: 35, 377 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-diagonal-small + rotate: false + xy: 69, 411 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-discord-small + rotate: false + xy: 103, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-distribution-small + rotate: false + xy: 137, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-donate-small + rotate: false + xy: 1, 309 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-dots-small + rotate: false + xy: 35, 343 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-editor-small + rotate: false + xy: 69, 377 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-effect-small + rotate: false + xy: 103, 411 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-elevation-small + rotate: false + xy: 137, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-eraser-small + rotate: false + xy: 171, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-exit-small + rotate: false + xy: 1, 275 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-file-image-small + rotate: false + xy: 35, 309 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-file-small + rotate: false + xy: 69, 343 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-file-text-small + rotate: false + xy: 103, 377 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-fill-small + rotate: false + xy: 137, 411 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-floppy-16-small + rotate: false + xy: 171, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-floppy-small + rotate: false + xy: 205, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-folder-parent-small + rotate: false + xy: 1, 241 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-folder-small + rotate: false + xy: 35, 275 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-github-small + rotate: false + xy: 69, 309 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-google-play-small + rotate: false + xy: 103, 343 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-grid-small + rotate: false + xy: 137, 377 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-home-small + rotate: false + xy: 171, 411 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-host-small + rotate: false + xy: 205, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-info-small + rotate: false + xy: 239, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-itch.io-small + rotate: false + xy: 1, 207 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-item-small + rotate: false + xy: 35, 241 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-line-small + rotate: false + xy: 69, 275 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-link-small + rotate: false + xy: 103, 309 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-liquid-consume-small + rotate: false + xy: 137, 343 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-liquid-small + rotate: false + xy: 171, 377 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-load-image-small + rotate: false + xy: 205, 411 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-load-map-small + rotate: false + xy: 239, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-load-small + rotate: false + xy: 273, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-loading-small + rotate: false + xy: 1, 173 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-locked-small + rotate: false + xy: 35, 207 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-map-small + rotate: false + xy: 69, 241 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-menu-large-small + rotate: false + xy: 103, 275 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-menu-small + rotate: false + xy: 137, 309 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-missing-small + rotate: false + xy: 171, 343 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-none-small + rotate: false + xy: 205, 377 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-paste-small + rotate: false + xy: 239, 411 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-pause-small + rotate: false + xy: 273, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-pencil-small + rotate: false + xy: 307, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-pick-small + rotate: false + xy: 1, 139 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-play-2-small + rotate: false + xy: 35, 173 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-play-custom-small + rotate: false + xy: 69, 207 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-play-small + rotate: false + xy: 103, 241 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-players-small + rotate: false + xy: 137, 275 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-power-small + rotate: false + xy: 171, 309 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-production-small + rotate: false + xy: 205, 343 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-quit-small + rotate: false + xy: 239, 377 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-redo-small + rotate: false + xy: 273, 411 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-refresh-small + rotate: false + xy: 307, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-rename-small + rotate: false + xy: 341, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-resize-small + rotate: false + xy: 1, 105 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-rotate-arrow-small + rotate: false + xy: 35, 139 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-rotate-left-small + rotate: false + xy: 69, 173 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-rotate-right-small + rotate: false + xy: 103, 207 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-rotate-small + rotate: false + xy: 137, 241 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-save-image-small + rotate: false + xy: 171, 275 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-save-map-small + rotate: false + xy: 205, 309 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-save-small + rotate: false + xy: 239, 343 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-settings-small + rotate: false + xy: 273, 377 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-spray-small + rotate: false + xy: 307, 411 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-terrain-small + rotate: false + xy: 341, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-tools-small + rotate: false + xy: 375, 479 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-trash-16-small + rotate: false + xy: 1, 71 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-trash-small + rotate: false + xy: 35, 105 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-tree-small + rotate: false + xy: 69, 139 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-trello-small + rotate: false + xy: 103, 173 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-turret-small + rotate: false + xy: 137, 207 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-tutorial-small + rotate: false + xy: 171, 241 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-undo-small + rotate: false + xy: 205, 275 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-units-small + rotate: false + xy: 239, 309 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-unlocks-small + rotate: false + xy: 273, 343 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-upgrade-small + rotate: false + xy: 307, 377 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-wiki-small + rotate: false + xy: 341, 411 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icon-zoom-small + rotate: false + xy: 375, 445 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +inventory + rotate: false + xy: 751, 283 + size: 24, 40 + split: 10, 10, 10, 14 + orig: 24, 40 + offset: 0, 0 + index: -1 +scroll + rotate: false + xy: 985, 296 + size: 24, 35 + split: 10, 10, 6, 5 + orig: 24, 35 + offset: 0, 0 + index: -1 +scroll-knob-vertical-black + rotate: false + xy: 959, 291 + size: 24, 40 + split: 10, 10, 6, 10 + orig: 24, 40 + offset: 0, 0 + index: -1 +sector-select + rotate: false + xy: 409, 207 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +selection + rotate: false + xy: 814, 476 + size: 1, 1 + orig: 1, 1 + offset: 0, 0 + index: -1 +slider + rotate: false + xy: 1022, 465 + size: 1, 8 + orig: 1, 8 + offset: 0, 0 + index: -1 +slider-knob + rotate: false + xy: 749, 405 + size: 29, 38 + orig: 29, 38 + offset: 0, 0 + index: -1 +slider-knob-down + rotate: false + xy: 783, 439 + size: 29, 38 + orig: 29, 38 + offset: 0, 0 + index: -1 +slider-knob-over + rotate: false + xy: 783, 439 + size: 29, 38 + orig: 29, 38 + offset: 0, 0 + index: -1 +slider-vertical + rotate: false + xy: 409, 42 + size: 8, 1 + orig: 8, 1 + offset: 0, 0 + index: -1 +white + rotate: false + xy: 775, 400 + size: 3, 3 + orig: 3, 3 + offset: 0, 0 + index: -1 +window-empty + rotate: false + xy: 967, 412 + size: 27, 61 + split: 8, 8, 44, 11 + orig: 27, 61 + offset: 0, 0 + index: -1 + +sprites_fallback6.png +size: 1024,1024 +format: RGBA8888 +filter: Nearest,Nearest +repeat: none +char1 + rotate: false + xy: 781, 789 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +char2 + rotate: false + xy: 879, 887 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +char3 + rotate: false + xy: 585, 495 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +cliffs1 + rotate: false + xy: 781, 755 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +coal1 + rotate: false + xy: 879, 853 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +coal2 + rotate: false + xy: 585, 461 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +coal3 + rotate: false + xy: 265, 47 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +copper1 + rotate: false + xy: 265, 13 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +copper2 + rotate: false + xy: 295, 341 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +copper3 + rotate: false + xy: 295, 307 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +craters1 + rotate: false + xy: 295, 273 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +craters2 + rotate: false + xy: 295, 239 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +craters3 + rotate: false + xy: 295, 205 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +craters4 + rotate: false + xy: 295, 171 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +craters5 + rotate: false + xy: 295, 137 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +craters6 + rotate: false + xy: 295, 103 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dark-metal-large + rotate: false + xy: 1, 15 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +dark-metal1 + rotate: false + xy: 329, 347 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dark-metal2 + rotate: false + xy: 329, 313 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dark-panel-1 + rotate: false + xy: 363, 347 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dark-panel-1-edge + rotate: false + xy: 323, 921 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +dark-panel-2 + rotate: false + xy: 329, 279 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dark-panel-2-edge + rotate: false + xy: 1, 277 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +dark-panel-3 + rotate: false + xy: 363, 313 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dark-panel-3-edge + rotate: false + xy: 323, 823 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +dark-panel-4 + rotate: false + xy: 397, 347 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dark-panel-4-edge + rotate: false + xy: 421, 921 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +dark-panel-5 + rotate: false + xy: 329, 245 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dark-panel-5-edge + rotate: false + xy: 1, 179 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +dark-panel-6 + rotate: false + xy: 363, 279 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dark-panel-6-edge + rotate: false + xy: 99, 277 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +darksand-edge + rotate: false + xy: 323, 725 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +darksand-tainted-water + rotate: false + xy: 363, 245 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +darksand-tainted-water-edge + rotate: false + xy: 421, 823 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +darksand-water + rotate: false + xy: 397, 279 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +darksand-water-edge + rotate: false + xy: 519, 921 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +darksand1 + rotate: false + xy: 397, 313 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +darksand2 + rotate: false + xy: 431, 347 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +darksand3 + rotate: false + xy: 329, 211 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +deepwater + rotate: false + xy: 431, 313 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +deepwater-edge + rotate: false + xy: 1, 81 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +dunerocks-large + rotate: false + xy: 617, 659 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +dunerocks1 + rotate: false + xy: 465, 347 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +dunerocks2 + rotate: false + xy: 329, 177 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +edge + rotate: false + xy: 363, 211 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +edge-stencil + rotate: false + xy: 99, 179 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +edgier + rotate: false + xy: 397, 245 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +grass-edge + rotate: false + xy: 197, 277 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +grass1 + rotate: false + xy: 431, 279 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +grass2 + rotate: false + xy: 465, 313 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +grass3 + rotate: false + xy: 329, 143 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +holostone-edge + rotate: false + xy: 323, 627 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +holostone1 + rotate: false + xy: 363, 177 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +holostone2 + rotate: false + xy: 397, 211 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +holostone3 + rotate: false + xy: 431, 245 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +hotrock1 + rotate: false + xy: 465, 279 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +hotrock2 + rotate: false + xy: 329, 109 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +hotrock3 + rotate: false + xy: 363, 143 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ice-edge + rotate: false + xy: 421, 725 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +ice-snow-edge + rotate: false + xy: 519, 823 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +ice-snow1 + rotate: false + xy: 363, 109 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ice-snow2 + rotate: false + xy: 397, 143 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ice-snow3 + rotate: false + xy: 431, 177 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ice1 + rotate: false + xy: 397, 177 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ice2 + rotate: false + xy: 431, 211 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ice3 + rotate: false + xy: 465, 245 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icerocks-large + rotate: false + xy: 715, 757 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +icerocks1 + rotate: false + xy: 465, 211 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +icerocks2 + rotate: false + xy: 397, 109 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ignarock-edge + rotate: false + xy: 617, 921 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +ignarock1 + rotate: false + xy: 431, 143 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ignarock2 + rotate: false + xy: 465, 177 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ignarock3 + rotate: false + xy: 431, 109 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +lead1 + rotate: false + xy: 465, 143 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +lead2 + rotate: false + xy: 465, 109 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +lead3 + rotate: false + xy: 977, 943 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +magmarock1 + rotate: false + xy: 499, 347 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +magmarock2 + rotate: false + xy: 499, 313 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +magmarock3 + rotate: false + xy: 499, 279 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +metal-floor + rotate: false + xy: 499, 245 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +metal-floor-2 + rotate: false + xy: 499, 211 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +metal-floor-2-edge + rotate: false + xy: 99, 81 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +metal-floor-3 + rotate: false + xy: 499, 177 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +metal-floor-3-edge + rotate: false + xy: 197, 179 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +metal-floor-5 + rotate: false + xy: 499, 143 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +metal-floor-5-edge + rotate: false + xy: 323, 529 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +metal-floor-damaged-edge + rotate: false + xy: 421, 627 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +metal-floor-damaged1 + rotate: false + xy: 499, 109 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +metal-floor-damaged2 + rotate: false + xy: 619, 509 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +metal-floor-damaged3 + rotate: false + xy: 619, 475 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +metal-floor-edge + rotate: false + xy: 519, 725 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +moss-edge + rotate: false + xy: 617, 823 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +moss1 + rotate: false + xy: 667, 557 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +moss2 + rotate: false + xy: 701, 557 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +moss3 + rotate: false + xy: 653, 509 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-coal1 + rotate: false + xy: 653, 475 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-coal2 + rotate: false + xy: 687, 523 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-coal3 + rotate: false + xy: 687, 489 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-copper1 + rotate: false + xy: 619, 441 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-copper2 + rotate: false + xy: 653, 441 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-copper3 + rotate: false + xy: 687, 455 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-lead1 + rotate: false + xy: 721, 523 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-lead2 + rotate: false + xy: 721, 489 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-lead3 + rotate: false + xy: 721, 455 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-scrap1 + rotate: false + xy: 687, 421 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-scrap2 + rotate: false + xy: 721, 421 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-scrap3 + rotate: false + xy: 299, 69 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-thorium1 + rotate: false + xy: 299, 35 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-thorium2 + rotate: false + xy: 333, 75 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-thorium3 + rotate: false + xy: 333, 41 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-titanium1 + rotate: false + xy: 367, 75 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-titanium2 + rotate: false + xy: 367, 41 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +ore-titanium3 + rotate: false + xy: 401, 75 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pebbles1 + rotate: false + xy: 401, 41 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pebbles2 + rotate: false + xy: 435, 75 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pebbles3 + rotate: false + xy: 435, 41 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +pine + rotate: false + xy: 323, 381 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +rock1 + rotate: false + xy: 617, 543 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +rock2 + rotate: false + xy: 373, 381 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +rocks-large + rotate: false + xy: 813, 855 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +rocks1 + rotate: false + xy: 469, 75 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +rocks2 + rotate: false + xy: 469, 41 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +salt + rotate: false + xy: 503, 75 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +salt-edge + rotate: false + xy: 715, 921 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +saltrocks-large + rotate: false + xy: 911, 953 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +saltrocks1 + rotate: false + xy: 503, 41 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +saltrocks2 + rotate: false + xy: 299, 1 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +sand-edge + rotate: false + xy: 197, 81 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +sand-water + rotate: false + xy: 435, 7 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +sand-water-edge + rotate: false + xy: 323, 431 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +sand1 + rotate: false + xy: 333, 7 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +sand2 + rotate: false + xy: 367, 7 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +sand3 + rotate: false + xy: 401, 7 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +sandrocks-large + rotate: false + xy: 519, 463 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +sandrocks1 + rotate: false + xy: 469, 7 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +sandrocks2 + rotate: false + xy: 503, 7 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scrap1 + rotate: false + xy: 725, 641 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scrap2 + rotate: false + xy: 725, 607 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +scrap3 + rotate: false + xy: 735, 573 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +shale-boulder1 + rotate: false + xy: 849, 819 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +shale-boulder2 + rotate: false + xy: 849, 785 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +shale-edge + rotate: false + xy: 421, 529 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +shale1 + rotate: false + xy: 815, 821 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +shale2 + rotate: false + xy: 815, 787 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +shale3 + rotate: false + xy: 815, 753 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +shalerocks-large + rotate: false + xy: 67, 15 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +shalerocks1 + rotate: false + xy: 849, 751 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +shalerocks2 + rotate: false + xy: 883, 819 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +shrubs-large + rotate: false + xy: 617, 593 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +shrubs1 + rotate: false + xy: 883, 785 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +shrubs2 + rotate: false + xy: 883, 751 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +snow-edge + rotate: false + xy: 519, 627 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +snow1 + rotate: false + xy: 523, 429 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +snow2 + rotate: false + xy: 523, 395 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +snow3 + rotate: false + xy: 533, 361 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +snowrock1 + rotate: false + xy: 423, 381 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +snowrock2 + rotate: false + xy: 473, 381 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +snowrocks-large + rotate: false + xy: 133, 15 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +snowrocks1 + rotate: false + xy: 533, 327 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +snowrocks2 + rotate: false + xy: 533, 293 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +spawn + rotate: false + xy: 533, 259 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +spore-cluster1 + rotate: false + xy: 977, 977 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +spore-cluster2 + rotate: false + xy: 683, 633 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +spore-cluster3 + rotate: false + xy: 683, 591 + size: 40, 40 + orig: 40, 40 + offset: 0, 0 + index: -1 +spore-moss-edge + rotate: false + xy: 617, 725 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +spore-moss1 + rotate: false + xy: 533, 225 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +spore-moss2 + rotate: false + xy: 533, 191 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +spore-moss3 + rotate: false + xy: 533, 157 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +spore-pine + rotate: false + xy: 683, 675 + size: 48, 48 + orig: 48, 48 + offset: 0, 0 + index: -1 +sporerocks-large + rotate: false + xy: 199, 15 + size: 64, 64 + orig: 64, 64 + offset: 0, 0 + index: -1 +sporerocks1 + rotate: false + xy: 533, 123 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +sporerocks2 + rotate: false + xy: 537, 89 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +stone-edge + rotate: false + xy: 715, 823 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +stone1 + rotate: false + xy: 537, 55 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +stone2 + rotate: false + xy: 537, 21 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +stone3 + rotate: false + xy: 557, 427 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +tainted-water + rotate: false + xy: 733, 723 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +tainted-water-edge + rotate: false + xy: 813, 921 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +tar + rotate: false + xy: 733, 689 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +tar-edge + rotate: false + xy: 421, 431 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +tendrils1 + rotate: false + xy: 767, 721 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +tendrils2 + rotate: false + xy: 767, 687 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +tendrils3 + rotate: false + xy: 801, 719 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +thorium1 + rotate: false + xy: 801, 685 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +thorium2 + rotate: false + xy: 835, 717 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +thorium3 + rotate: false + xy: 869, 717 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium1 + rotate: false + xy: 835, 683 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium2 + rotate: false + xy: 869, 683 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +titanium3 + rotate: false + xy: 759, 653 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +water + rotate: false + xy: 759, 619 + size: 32, 32 + orig: 32, 32 + offset: 0, 0 + index: -1 +water-edge + rotate: false + xy: 519, 529 + size: 96, 96 + orig: 96, 96 + offset: 0, 0 + index: -1 +white-tree + rotate: false + xy: 1, 697 + size: 320, 320 + orig: 320, 320 + offset: 0, 0 + index: -1 +white-tree-dead + rotate: false + xy: 1, 375 + size: 320, 320 + orig: 320, 320 + offset: 0, 0 + index: -1 diff --git a/core/assets/sprites/sprites_fallback.png b/core/assets/sprites/sprites_fallback.png new file mode 100644 index 0000000000..12ded24dff Binary files /dev/null and b/core/assets/sprites/sprites_fallback.png differ diff --git a/core/assets/sprites/sprites_fallback2.png b/core/assets/sprites/sprites_fallback2.png new file mode 100644 index 0000000000..c3faf25e49 Binary files /dev/null and b/core/assets/sprites/sprites_fallback2.png differ diff --git a/core/assets/sprites/sprites_fallback3.png b/core/assets/sprites/sprites_fallback3.png new file mode 100644 index 0000000000..4c1c075789 Binary files /dev/null and b/core/assets/sprites/sprites_fallback3.png differ diff --git a/core/assets/sprites/sprites_fallback4.png b/core/assets/sprites/sprites_fallback4.png new file mode 100644 index 0000000000..2df555ef00 Binary files /dev/null and b/core/assets/sprites/sprites_fallback4.png differ diff --git a/core/assets/sprites/sprites_fallback5.png b/core/assets/sprites/sprites_fallback5.png new file mode 100644 index 0000000000..02079176b1 Binary files /dev/null and b/core/assets/sprites/sprites_fallback5.png differ diff --git a/core/assets/sprites/sprites_fallback6.png b/core/assets/sprites/sprites_fallback6.png new file mode 100644 index 0000000000..c1e2b62ee5 Binary files /dev/null and b/core/assets/sprites/sprites_fallback6.png differ diff --git a/core/assets/sprites/uiskin.json b/core/assets/sprites/uiskin.json new file mode 100644 index 0000000000..3931878115 --- /dev/null +++ b/core/assets/sprites/uiskin.json @@ -0,0 +1,317 @@ +{ + Color: { + black: { a: 1, b: 0, g: 0, r: 0 }, + white: { a: 1, b: 1, g: 1, r: 1 }, + gray: { a: 1, b: 0.32, g: 0.32, r: 0.32 }, + lightgray: { a: 1, b: 0.65, g: 0.65, r: 0.65 } + orange: { hex: "FFA500" }, + accent: { hex: "ffd37f" } + }, + TintedDrawable: { + dialogDim: { + name: white, + color: { r: 0, g: 0, b: 0, a: 0.9 } + }, + guideDim: { + name: white, + color: { r: 0, g: 0, b: 0, a: 0.3 } + }, + invis: { + name: white, + color: { r: 0, g: 0, b: 0, a: 0 } + } + loadDim: { + name: white, + color: { r: 0, g: 0, b: 0, a: 0.8 } + }, + chatfield: { + name: white, + color: { r: 0, g: 0, b: 0, a: 0.2 } + }, + dark: { + name: white, + color: { hex: "#000000ff" } + }, + none: { + name: white, + color: { r: 0, g: 0, b: 0, a: 0 } + }, + flat: { + name: white, + color: { r: 0.0, g: 0.0, b: 0.0, a: 0.6 } + }, + flat-over: { + name: white, + color: { hex: "#ffffff82" } + }, + flat-down: { + name: white, + color: { hex: "#ffd37fff" } + } + }, + ButtonStyle: { + default: { + down: button-down, + up: button + }, + toggle: { + checked: button-down, + down: button-down, + up: button + } + }, + TextButtonStyle: { + default: { + over: button-over, + disabled: button-disabled, + font: default-font, + fontColor: white, + disabledFontColor: gray, + down: button-down, + up: button + }, + node: { + disabled: content-background-locked, + font: default-font, + fontColor: white, + disabledFontColor: gray, + up: content-background, + over: content-background-over + }, + right: { + over: button-right-over, + font: default-font, + fontColor: white, + disabledFontColor: gray, + down: button-right-down, + up: button-right + }, + wave: { + font: default-font, + fontColor: white, + disabledFontColor: gray, + up: button-edge-4 + }, + clear: { + over: flat-over, + font: default-font, + fontColor: white, + disabledFontColor: gray, + down: flat-over, + up: flat + }, + discord: { + font: default-font, + fontColor: white, + up: discord-banner + }, + info: { + font: default-font, + fontColor: white, + up: info-banner + }, + clear-partial: { + down: white, + up: button-select, + over: flat-down, + font: default-font, + fontColor: white, + disabledFontColor: gray + }, + clear-partial-2: { + down: flat-over, + up: none, + over: flat-over, + font: default-font, + fontColor: white, + disabledFontColor: gray + }, + empty: { + font: default-font + }, + clear-toggle: { + font: default-font, + fontColor: white, + checked: flat-down, + down: flat-down, + up: flat, + over: flat-over, + disabled: flat, + disabledFontColor: gray + } + toggle: { + font: default-font, + fontColor: white, + checked: button-down, + down: button-down, + up: button, + over: button-over, + disabled: button, + disabledFontColor: gray + } + }, + ImageButtonStyle: { + default: { + down: button-down, + up: button, + over: button-over, + imageDisabledColor: gray, + imageUpColor: white + }, + node: { + up: content-background, + over: content-background-over + }, + right: { + over: button-right-over, + down: button-right-down, + up: button-right + }, + empty: { + imageDownColor: accent, + imageUpColor: white + }, + emptytoggle: { + imageCheckedColor: white, + imageDownColor: white, + imageUpColor: gray + }, + static: { + up: button + }, + static-down: { + up: button-down + }, + toggle: { + checked: button-down, + down: button-down, + up: button, + imageDisabledColor: gray, + imageUpColor: white + }, + select: { + checked: button-select, + up: none + }, + clear: { + down: flat-over, + up: flat, + over: flat-over + }, + clear-full: { + down: white, + up: button-select, + over: flat-down + }, + clear-partial: { + down: flat-down, + up: none, + over: flat-over + }, + clear-toggle: { + down: flat-down, + checked: flat-down, + up: flat, + over: flat-over + }, + clear-toggle-partial: { + down: flat-down, + checked: flat-down, + up: none, + over: flat-over + }, + }, + ScrollPaneStyle: { + default: { + vScroll: scroll, + vScrollKnob: scroll-knob-vertical-black + }, + horizontal: { + vScroll: scroll, + vScrollKnob: scroll-knob-vertical-black, + hScroll: scroll-horizontal, + hScrollKnob: scroll-knob-horizontal-black + }, + }, + WindowStyle: { + default: { + titleFont: default-font, + titleFontColor: accent + }, + dialog: { + stageBackground: dialogDim, + titleFont: default-font, + background: window-empty, + titleFontColor: accent + } + }, + KeybindDialogStyle: { + default: { + keyColor: accent, + keyNameColor: white, + controllerColor: lightgray + }, + }, + SliderStyle: { + default-horizontal: { + background: slider, + knob: slider-knob, + knobOver: slider-knob-over, + knobDown: slider-knob-down + }, + default-vertical: { + background: slider-vertical, + knob: slider-knob, + knobOver: slider-knob-over, + knobDown: slider-knob-down + } + }, + LabelStyle: { + default: { + font: default-font, + fontColor: white + }, + small: { + font: default-font, + fontColor: white + } + }, + TextFieldStyle: { + default: { + font: default-font-chat, + fontColor: white, + disabledFontColor: gray, + disabledBackground: underline-disabled, + selection: selection, + background: underline, + invalidBackground: underline-red, + cursor: cursor, + messageFont: default-font, + messageFontColor: gray + } + textarea: { + font: default-font-chat, + fontColor: white, + disabledFontColor: gray, + selection: selection, + background: underline, + cursor: cursor, + messageFont: default-font, + messageFontColor: gray + } + }, + CheckBoxStyle: { + default: { + checkboxOn: check-on, + checkboxOff: check-off, + checkboxOnOver: check-on-over, + checkboxOver: check-over, + checkboxOnDisabled: check-on-disabled, + checkboxOffDisabled: check-disabled, + font: default-font, + fontColor: white, + disabledFontColor: gray, + + } + } +} diff --git a/core/assets/ui/korean.fnt b/core/assets/ui/korean.fnt deleted file mode 100644 index d75e46c2b5..0000000000 --- a/core/assets/ui/korean.fnt +++ /dev/null @@ -1,2543 +0,0 @@ -info face="나눔고딕 ExtraBold" size=32 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=4 padding=0,0,0,0 spacing=1,1 outline=1 -common lineHeight=32 base=26 scaleW=1920 scaleH=1920 pages=1 packed=0 alphaChnl=0 redChnl=4 greenChnl=4 blueChnl=4 -page id=0 file="korean.png" -chars count=2447 -char id=10 x=1914 y=69 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=7 page=0 chnl=15 -char id=13 x=1914 y=65 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=7 page=0 chnl=15 -char id=32 x=1914 y=61 width=3 height=3 xoffset=-1 yoffset=-1 xadvance=7 page=0 chnl=15 -char id=33 x=1904 y=530 width=7 height=24 xoffset=2 yoffset=5 xadvance=11 page=0 chnl=15 -char id=34 x=1907 y=949 width=10 height=9 xoffset=1 yoffset=5 xadvance=12 page=0 chnl=15 -char id=35 x=1422 y=970 width=18 height=23 xoffset=-1 yoffset=5 xadvance=16 page=0 chnl=15 -char id=36 x=1904 y=151 width=15 height=28 xoffset=1 yoffset=2 xadvance=16 page=0 chnl=15 -char id=37 x=1488 y=944 width=27 height=25 xoffset=2 yoffset=3 xadvance=30 page=0 chnl=15 -char id=38 x=28 y=974 width=21 height=24 xoffset=0 yoffset=4 xadvance=20 page=0 chnl=15 -char id=40 x=1900 y=31 width=11 height=29 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 -char id=41 x=1906 y=91 width=11 height=29 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 -char id=42 x=31 y=999 width=14 height=14 xoffset=1 yoffset=9 xadvance=16 page=0 chnl=15 -char id=43 x=1801 y=968 width=18 height=18 xoffset=0 yoffset=7 xadvance=18 page=0 chnl=15 -char id=44 x=80 y=999 width=8 height=9 xoffset=0 yoffset=23 xadvance=8 page=0 chnl=15 -char id=45 x=1904 y=234 width=11 height=6 xoffset=0 yoffset=13 xadvance=10 page=0 chnl=15 -char id=46 x=107 y=999 width=8 height=8 xoffset=0 yoffset=23 xadvance=8 page=0 chnl=15 -char id=47 x=1904 y=180 width=14 height=26 xoffset=-1 yoffset=4 xadvance=10 page=0 chnl=15 -char id=48 x=1898 y=849 width=17 height=25 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 -char id=49 x=1567 y=970 width=11 height=23 xoffset=1 yoffset=5 xadvance=16 page=0 chnl=15 -char id=50 x=105 y=974 width=16 height=24 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 -char id=51 x=1899 y=875 width=16 height=25 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 -char id=52 x=1403 y=970 width=18 height=23 xoffset=-1 yoffset=5 xadvance=16 page=0 chnl=15 -char id=53 x=1904 y=603 width=15 height=23 xoffset=1 yoffset=5 xadvance=16 page=0 chnl=15 -char id=54 x=87 y=974 width=17 height=24 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 -char id=55 x=1533 y=970 width=16 height=23 xoffset=0 yoffset=5 xadvance=16 page=0 chnl=15 -char id=56 x=1898 y=823 width=17 height=25 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 -char id=57 x=69 y=974 width=17 height=24 xoffset=0 yoffset=4 xadvance=16 page=0 chnl=15 -char id=58 x=1677 y=968 width=7 height=20 xoffset=0 yoffset=6 xadvance=8 page=0 chnl=15 -char id=59 x=1590 y=970 width=8 height=23 xoffset=0 yoffset=6 xadvance=8 page=0 chnl=15 -char id=60 x=112 y=0 width=15 height=31 xoffset=0 yoffset=2 xadvance=15 page=0 chnl=15 -char id=61 x=46 y=999 width=18 height=12 xoffset=0 yoffset=10 xadvance=18 page=0 chnl=15 -char id=62 x=128 y=0 width=15 height=31 xoffset=0 yoffset=2 xadvance=15 page=0 chnl=15 -char id=63 x=1906 y=449 width=13 height=25 xoffset=1 yoffset=4 xadvance=15 page=0 chnl=15 -char id=64 x=1516 y=944 width=25 height=25 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=15 -char id=65 x=1273 y=973 width=23 height=23 xoffset=-1 yoffset=5 xadvance=20 page=0 chnl=15 -char id=66 x=1904 y=555 width=15 height=23 xoffset=1 yoffset=5 xadvance=16 page=0 chnl=15 -char id=67 x=1900 y=241 width=18 height=25 xoffset=0 yoffset=4 xadvance=18 page=0 chnl=15 -char id=68 x=1363 y=973 width=19 height=23 xoffset=1 yoffset=5 xadvance=20 page=0 chnl=15 -char id=69 x=1905 y=901 width=14 height=23 xoffset=1 yoffset=5 xadvance=16 page=0 chnl=15 -char id=70 x=1904 y=651 width=14 height=23 xoffset=1 yoffset=5 xadvance=14 page=0 chnl=15 -char id=71 x=1898 y=797 width=20 height=25 xoffset=0 yoffset=4 xadvance=22 page=0 chnl=15 -char id=72 x=1441 y=970 width=18 height=23 xoffset=1 yoffset=5 xadvance=20 page=0 chnl=15 -char id=73 x=1912 y=530 width=6 height=23 xoffset=0 yoffset=5 xadvance=6 page=0 chnl=15 -char id=74 x=1579 y=970 width=10 height=23 xoffset=-1 yoffset=5 xadvance=10 page=0 chnl=15 -char id=75 x=1460 y=970 width=18 height=23 xoffset=0 yoffset=5 xadvance=17 page=0 chnl=15 -char id=76 x=1904 y=627 width=14 height=23 xoffset=1 yoffset=5 xadvance=13 page=0 chnl=15 -char id=77 x=1247 y=973 width=25 height=23 xoffset=1 yoffset=5 xadvance=27 page=0 chnl=15 -char id=78 x=1383 y=970 width=19 height=23 xoffset=1 yoffset=5 xadvance=20 page=0 chnl=15 -char id=79 x=1542 y=944 width=22 height=25 xoffset=0 yoffset=4 xadvance=21 page=0 chnl=15 -char id=80 x=1904 y=579 width=15 height=23 xoffset=1 yoffset=5 xadvance=16 page=0 chnl=15 -char id=81 x=1330 y=944 width=22 height=28 xoffset=0 yoffset=4 xadvance=21 page=0 chnl=15 -char id=82 x=1479 y=970 width=17 height=23 xoffset=1 yoffset=5 xadvance=17 page=0 chnl=15 -char id=83 x=1904 y=504 width=15 height=25 xoffset=0 yoffset=4 xadvance=15 page=0 chnl=15 -char id=84 x=1497 y=970 width=17 height=23 xoffset=-1 yoffset=5 xadvance=15 page=0 chnl=15 -char id=85 x=50 y=974 width=18 height=24 xoffset=1 yoffset=5 xadvance=20 page=0 chnl=15 -char id=86 x=1341 y=973 width=21 height=23 xoffset=-1 yoffset=5 xadvance=18 page=0 chnl=15 -char id=87 x=122 y=974 width=31 height=23 xoffset=-1 yoffset=5 xadvance=28 page=0 chnl=15 -char id=88 x=1297 y=973 width=21 height=23 xoffset=-1 yoffset=5 xadvance=18 page=0 chnl=15 -char id=89 x=1319 y=973 width=21 height=23 xoffset=-1 yoffset=5 xadvance=18 page=0 chnl=15 -char id=90 x=1515 y=970 width=17 height=23 xoffset=-1 yoffset=5 xadvance=15 page=0 chnl=15 -char id=91 x=1910 y=0 width=8 height=30 xoffset=2 yoffset=2 xadvance=10 page=0 chnl=15 -char id=92 x=1599 y=969 width=27 height=22 xoffset=-1 yoffset=5 xadvance=27 page=0 chnl=15 -char id=93 x=1386 y=31 width=8 height=30 xoffset=0 yoffset=2 xadvance=10 page=0 chnl=15 -char id=94 x=12 y=999 width=18 height=16 xoffset=0 yoffset=5 xadvance=18 page=0 chnl=15 -char id=95 x=116 y=999 width=18 height=5 xoffset=-1 yoffset=26 xadvance=16 page=0 chnl=15 -char id=97 x=1886 y=967 width=15 height=18 xoffset=-1 yoffset=9 xadvance=15 page=0 chnl=15 -char id=98 x=1565 y=944 width=16 height=25 xoffset=1 yoffset=3 xadvance=16 page=0 chnl=15 -char id=99 x=1720 y=968 width=14 height=19 xoffset=0 yoffset=10 xadvance=13 page=0 chnl=15 -char id=100 x=1582 y=944 width=16 height=25 xoffset=0 yoffset=3 xadvance=16 page=0 chnl=15 -char id=101 x=1703 y=968 width=16 height=19 xoffset=0 yoffset=9 xadvance=15 page=0 chnl=15 -char id=102 x=1904 y=207 width=12 height=26 xoffset=-1 yoffset=2 xadvance=10 page=0 chnl=15 -char id=103 x=1550 y=970 width=16 height=23 xoffset=0 yoffset=10 xadvance=16 page=0 chnl=15 -char id=104 x=1904 y=478 width=15 height=25 xoffset=1 yoffset=3 xadvance=16 page=0 chnl=15 -char id=105 x=1912 y=331 width=7 height=25 xoffset=0 yoffset=3 xadvance=6 page=0 chnl=15 -char id=106 x=1904 y=121 width=9 height=29 xoffset=-1 yoffset=3 xadvance=7 page=0 chnl=15 -char id=107 x=1904 y=361 width=15 height=25 xoffset=0 yoffset=3 xadvance=14 page=0 chnl=15 -char id=108 x=1912 y=387 width=6 height=25 xoffset=0 yoffset=3 xadvance=6 page=0 chnl=15 -char id=109 x=1776 y=968 width=24 height=18 xoffset=0 yoffset=10 xadvance=25 page=0 chnl=15 -char id=110 x=1870 y=968 width=15 height=18 xoffset=1 yoffset=10 xadvance=16 page=0 chnl=15 -char id=111 x=1685 y=968 width=17 height=19 xoffset=0 yoffset=9 xadvance=16 page=0 chnl=15 -char id=112 x=1627 y=969 width=16 height=22 xoffset=0 yoffset=10 xadvance=16 page=0 chnl=15 -char id=113 x=1644 y=969 width=16 height=22 xoffset=0 yoffset=10 xadvance=16 page=0 chnl=15 -char id=114 x=0 y=999 width=11 height=18 xoffset=1 yoffset=10 xadvance=10 page=0 chnl=15 -char id=115 x=1735 y=968 width=13 height=19 xoffset=-1 yoffset=9 xadvance=11 page=0 chnl=15 -char id=116 x=1907 y=925 width=12 height=23 xoffset=-1 yoffset=6 xadvance=10 page=0 chnl=15 -char id=117 x=1854 y=968 width=15 height=18 xoffset=1 yoffset=10 xadvance=16 page=0 chnl=15 -char id=118 x=1837 y=968 width=16 height=18 xoffset=-1 yoffset=10 xadvance=13 page=0 chnl=15 -char id=119 x=1749 y=968 width=26 height=18 xoffset=-1 yoffset=10 xadvance=23 page=0 chnl=15 -char id=120 x=1820 y=968 width=16 height=18 xoffset=-1 yoffset=10 xadvance=14 page=0 chnl=15 -char id=121 x=1661 y=968 width=15 height=22 xoffset=-1 yoffset=10 xadvance=13 page=0 chnl=15 -char id=122 x=1902 y=967 width=15 height=18 xoffset=-1 yoffset=10 xadvance=13 page=0 chnl=15 -char id=123 x=1899 y=0 width=10 height=30 xoffset=-1 yoffset=2 xadvance=8 page=0 chnl=15 -char id=124 x=1912 y=31 width=6 height=29 xoffset=0 yoffset=2 xadvance=7 page=0 chnl=15 -char id=125 x=1375 y=31 width=10 height=30 xoffset=-1 yoffset=2 xadvance=8 page=0 chnl=15 -char id=126 x=89 y=999 width=17 height=8 xoffset=0 yoffset=13 xadvance=18 page=0 chnl=15 -char id=8216 x=65 y=999 width=7 height=10 xoffset=-1 yoffset=3 xadvance=5 page=0 chnl=15 -char id=8217 x=73 y=999 width=6 height=10 xoffset=-1 yoffset=3 xadvance=5 page=0 chnl=15 -char id=44032 x=464 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44033 x=252 y=510 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44036 x=319 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44039 x=280 y=510 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44040 x=1374 y=92 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44041 x=1402 y=92 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44042 x=1430 y=91 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44048 x=308 y=509 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44049 x=336 y=509 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44050 x=1514 y=91 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44051 x=1542 y=91 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44052 x=1570 y=91 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44053 x=1598 y=91 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44054 x=1626 y=91 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44055 x=1731 y=0 width=27 height=30 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44057 x=364 y=509 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44058 x=392 y=509 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44059 x=1759 y=0 width=27 height=30 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44060 x=459 y=712 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44061 x=84 y=684 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44064 x=513 y=712 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44068 x=1495 y=241 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44076 x=540 y=712 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44077 x=567 y=712 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44079 x=1468 y=241 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44080 x=1218 y=62 width=28 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44081 x=1387 y=242 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44088 x=1337 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44089 x=560 y=509 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44092 x=1018 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44096 x=1794 y=91 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44107 x=1822 y=91 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44109 x=1850 y=91 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44116 x=594 y=712 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44120 x=621 y=712 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44124 x=1090 y=242 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44144 x=286 y=887 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44145 x=624 y=886 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44148 x=338 y=887 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44151 x=725 y=915 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44152 x=1378 y=362 width=25 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44154 x=700 y=392 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44160 x=650 y=915 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44161 x=600 y=915 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44163 x=1323 y=302 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44164 x=84 y=123 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44165 x=1629 y=361 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44166 x=1215 y=302 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44169 x=364 y=886 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44170 x=416 y=886 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44171 x=1228 y=31 width=24 height=30 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44172 x=864 y=712 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44176 x=891 y=712 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44180 x=972 y=302 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44188 x=918 y=712 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44189 x=945 y=712 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44191 x=513 y=302 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44192 x=435 y=62 width=28 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44193 x=486 y=302 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44200 x=494 y=886 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44201 x=575 y=915 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44202 x=974 y=886 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44204 x=546 y=886 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44207 x=400 y=915 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44208 x=768 y=332 width=25 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44216 x=250 y=916 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44217 x=1774 y=885 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44219 x=1863 y=271 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44220 x=280 y=122 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44221 x=550 y=392 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44225 x=598 y=886 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44228 x=1188 y=712 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44232 x=1215 y=712 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44236 x=1809 y=271 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44245 x=1242 y=712 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44247 x=1755 y=271 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44256 x=771 y=973 width=27 height=23 xoffset=-1 yoffset=4 xadvance=26 page=0 chnl=15 -char id=44257 x=1316 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44260 x=1344 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44263 x=1372 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44264 x=1400 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44266 x=448 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44268 x=476 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44271 x=504 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44272 x=1428 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44273 x=1456 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44275 x=532 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44277 x=560 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44278 x=588 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44284 x=1163 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44285 x=1192 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44288 x=1221 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44292 x=1421 y=61 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44294 x=1450 y=61 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44300 x=1250 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44301 x=1279 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44303 x=1537 y=61 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44305 x=1566 y=61 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44312 x=1296 y=712 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44316 x=1512 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44320 x=700 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44329 x=1323 y=712 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44332 x=1711 y=61 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44333 x=1512 y=271 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44340 x=1161 y=770 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44341 x=1187 y=770 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44344 x=1213 y=770 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44348 x=1323 y=272 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44356 x=1239 y=770 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44357 x=1265 y=770 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44359 x=945 y=272 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44361 x=1496 y=331 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44368 x=743 y=973 width=27 height=23 xoffset=-1 yoffset=4 xadvance=26 page=0 chnl=15 -char id=44372 x=1736 y=508 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44376 x=1764 y=508 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44385 x=1792 y=507 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44387 x=728 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44396 x=1820 y=507 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44397 x=1848 y=507 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44400 x=1876 y=507 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44403 x=0 y=539 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44404 x=28 y=539 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44405 x=784 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44406 x=812 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44411 x=840 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44412 x=56 y=539 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44413 x=84 y=539 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44415 x=924 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44417 x=112 y=539 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44418 x=952 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44424 x=1317 y=770 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44425 x=1343 y=770 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44428 x=1369 y=770 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44432 x=0 y=363 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44444 x=140 y=32 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44445 x=26 y=363 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44452 x=1593 y=711 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44471 x=980 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44480 x=1395 y=770 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44481 x=1421 y=770 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44484 x=1674 y=711 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44488 x=156 y=363 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44496 x=1447 y=770 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44497 x=1473 y=770 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44499 x=1092 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44508 x=392 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44512 x=420 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44516 x=448 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44536 x=715 y=973 width=27 height=23 xoffset=-1 yoffset=4 xadvance=26 page=0 chnl=15 -char id=44537 x=504 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44540 x=532 y=538 width=27 height=28 xoffset=-1 yoffset=3 xadvance=26 page=0 chnl=15 -char id=44543 x=560 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44544 x=588 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44545 x=1316 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44552 x=616 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44553 x=644 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44555 x=1400 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44557 x=672 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44564 x=1499 y=770 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44592 x=1599 y=886 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44593 x=1499 y=886 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44596 x=1525 y=770 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44599 x=1474 y=886 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44600 x=725 y=392 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44602 x=625 y=392 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44608 x=1249 y=886 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44609 x=1199 y=886 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44611 x=540 y=272 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44613 x=1404 y=362 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44614 x=324 y=272 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44618 x=1551 y=770 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44620 x=0 y=423 width=29 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44621 x=899 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44622 x=928 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44624 x=957 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44628 x=1363 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44630 x=1653 y=61 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44636 x=986 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44637 x=1015 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44639 x=696 y=92 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44640 x=725 y=92 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44641 x=667 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44645 x=1044 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44648 x=924 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44649 x=952 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44652 x=1131 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44656 x=725 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44664 x=980 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44665 x=1008 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44667 x=841 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44668 x=1546 y=31 width=29 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44669 x=1764 y=121 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44676 x=30 y=423 width=29 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44677 x=1247 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44684 x=1160 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44732 x=81 y=742 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44733 x=1577 y=770 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44734 x=1603 y=769 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44736 x=162 y=742 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44740 x=1333 y=242 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44748 x=1629 y=769 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44749 x=1655 y=769 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44751 x=28 y=153 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44752 x=1798 y=61 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44753 x=1326 y=362 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44760 x=1204 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44761 x=1232 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44764 x=1508 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44776 x=1680 y=566 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44779 x=348 y=92 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44781 x=140 y=153 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44788 x=243 y=742 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44792 x=270 y=742 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44796 x=1242 y=302 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44807 x=196 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44808 x=116 y=63 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44813 x=297 y=742 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44816 x=1344 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44844 x=659 y=973 width=27 height=23 xoffset=-1 yoffset=4 xadvance=26 page=0 chnl=15 -char id=44845 x=1400 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44848 x=1428 y=538 width=27 height=28 xoffset=-1 yoffset=3 xadvance=26 page=0 chnl=15 -char id=44850 x=336 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44852 x=1456 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44860 x=1484 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44861 x=1512 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44863 x=392 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44865 x=420 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44866 x=1647 y=0 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44867 x=1675 y=0 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44872 x=609 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44873 x=870 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44880 x=348 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44892 x=203 y=92 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44893 x=1218 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44900 x=1540 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44901 x=351 y=741 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44921 x=1596 y=537 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44928 x=378 y=741 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44932 x=405 y=741 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44936 x=837 y=302 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44944 x=1681 y=769 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44945 x=1707 y=769 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44949 x=1733 y=769 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44956 x=631 y=973 width=27 height=23 xoffset=-1 yoffset=4 xadvance=26 page=0 chnl=15 -char id=44984 x=1792 y=536 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44985 x=1820 y=536 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44988 x=1848 y=536 width=27 height=28 xoffset=-1 yoffset=3 xadvance=26 page=0 chnl=15 -char id=44992 x=1876 y=536 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=44999 x=560 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45000 x=0 y=568 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45001 x=28 y=568 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45003 x=616 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45005 x=56 y=568 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45006 x=672 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45012 x=1759 y=769 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45020 x=648 y=302 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45032 x=202 y=0 width=28 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45033 x=1236 y=332 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45040 x=112 y=568 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45041 x=567 y=741 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45044 x=168 y=568 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45048 x=756 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45056 x=594 y=741 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45057 x=621 y=741 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45060 x=434 y=0 width=28 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45068 x=1785 y=769 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45072 x=675 y=741 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45076 x=0 y=303 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45084 x=1811 y=769 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45085 x=1837 y=768 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45096 x=364 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45124 x=575 y=973 width=27 height=23 xoffset=-1 yoffset=4 xadvance=26 page=0 chnl=15 -char id=45125 x=420 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45128 x=448 y=567 width=27 height=28 xoffset=-1 yoffset=3 xadvance=26 page=0 chnl=15 -char id=45130 x=952 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45132 x=476 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45134 x=1008 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45139 x=1036 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45140 x=504 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45141 x=532 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45143 x=1092 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45145 x=560 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45149 x=588 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45180 x=783 y=741 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45181 x=1863 y=768 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45184 x=837 y=741 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45188 x=1834 y=331 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45196 x=1889 y=768 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45197 x=260 y=887 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45199 x=1260 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45201 x=52 y=363 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45208 x=918 y=741 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45209 x=945 y=741 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45210 x=972 y=741 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45212 x=999 y=741 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45215 x=1026 y=741 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45216 x=1242 y=272 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45217 x=1377 y=272 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45218 x=1404 y=272 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45224 x=1053 y=741 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45225 x=1080 y=741 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45227 x=783 y=302 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45228 x=1053 y=302 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45229 x=243 y=332 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45230 x=270 y=332 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45231 x=887 y=31 width=26 height=30 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45233 x=1107 y=741 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45235 x=725 y=31 width=26 height=30 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45236 x=0 y=800 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45237 x=949 y=886 width=24 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45240 x=26 y=800 width=25 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45244 x=390 y=362 width=25 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45252 x=824 y=886 width=24 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45253 x=699 y=886 width=24 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45255 x=468 y=362 width=25 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45256 x=1512 y=151 width=27 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45257 x=494 y=362 width=25 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45264 x=1269 y=741 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45265 x=1296 y=741 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45268 x=1323 y=741 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45272 x=756 y=302 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45280 x=1350 y=741 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45285 x=810 y=302 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45320 x=912 y=944 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45321 x=1353 y=944 width=22 height=28 xoffset=2 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45323 x=676 y=362 width=25 height=29 xoffset=2 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45324 x=792 y=944 width=23 height=28 xoffset=2 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45328 x=1303 y=392 width=23 height=29 xoffset=2 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45330 x=1351 y=392 width=23 height=29 xoffset=2 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45331 x=1375 y=392 width=23 height=29 xoffset=2 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45336 x=1031 y=944 width=22 height=28 xoffset=2 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45337 x=1077 y=944 width=22 height=28 xoffset=2 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45339 x=525 y=392 width=24 height=29 xoffset=2 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45340 x=1279 y=242 width=26 height=29 xoffset=2 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45341 x=1399 y=392 width=23 height=29 xoffset=2 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45347 x=1303 y=31 width=23 height=30 xoffset=2 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45348 x=52 y=800 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45349 x=1375 y=915 width=24 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45352 x=78 y=800 width=25 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45356 x=988 y=362 width=25 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45364 x=1350 y=915 width=24 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45365 x=1300 y=915 width=24 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45367 x=1066 y=362 width=25 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45368 x=1820 y=151 width=27 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45369 x=1092 y=362 width=25 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45376 x=456 y=944 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45377 x=1100 y=944 width=22 height=28 xoffset=2 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45380 x=360 y=945 width=23 height=28 xoffset=2 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45384 x=1447 y=392 width=23 height=29 xoffset=2 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45392 x=1123 y=944 width=22 height=28 xoffset=2 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45393 x=1146 y=944 width=22 height=28 xoffset=2 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45396 x=1539 y=271 width=26 height=29 xoffset=2 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45397 x=1471 y=392 width=23 height=29 xoffset=2 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45400 x=1663 y=391 width=22 height=29 xoffset=2 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45404 x=130 y=800 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45408 x=156 y=800 width=25 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45432 x=547 y=973 width=27 height=23 xoffset=-1 yoffset=4 xadvance=26 page=0 chnl=15 -char id=45433 x=1736 y=566 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45436 x=1764 y=566 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45440 x=1792 y=565 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45442 x=252 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45448 x=1820 y=565 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45449 x=1848 y=565 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45451 x=336 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45453 x=261 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45458 x=1876 y=565 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45459 x=1227 y=0 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45460 x=1624 y=450 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45464 x=377 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45468 x=58 y=63 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45480 x=376 y=0 width=28 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45516 x=182 y=800 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45520 x=208 y=800 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45524 x=108 y=333 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45532 x=234 y=800 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45533 x=260 y=800 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45535 x=1117 y=242 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45544 x=939 y=973 width=27 height=23 xoffset=-1 yoffset=4 xadvance=26 page=0 chnl=15 -char id=45545 x=140 y=597 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45548 x=168 y=597 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45552 x=196 y=597 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45561 x=224 y=597 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45563 x=616 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45565 x=644 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45572 x=252 y=597 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45573 x=280 y=597 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45576 x=308 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45579 x=336 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45580 x=364 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45588 x=392 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45589 x=420 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45591 x=784 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45593 x=812 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45600 x=286 y=800 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45620 x=1787 y=0 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45628 x=327 y=683 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45656 x=312 y=800 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45660 x=435 y=683 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45664 x=1184 y=332 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45672 x=338 y=800 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45673 x=364 y=799 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45684 x=616 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45685 x=644 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45692 x=672 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45700 x=700 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45701 x=728 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45705 x=896 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45712 x=463 y=973 width=27 height=23 xoffset=-1 yoffset=4 xadvance=26 page=0 chnl=15 -char id=45713 x=784 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45716 x=812 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45720 x=840 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45721 x=952 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45722 x=980 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45728 x=868 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45729 x=896 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45731 x=1008 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45733 x=1036 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45734 x=1064 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45738 x=924 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45740 x=390 y=799 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45744 x=416 y=799 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45748 x=1566 y=271 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45768 x=168 y=945 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45769 x=1169 y=944 width=22 height=28 xoffset=2 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45772 x=0 y=945 width=23 height=28 xoffset=2 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45776 x=1639 y=391 width=23 height=29 xoffset=2 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45778 x=1686 y=391 width=22 height=29 xoffset=2 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45784 x=1261 y=944 width=22 height=28 xoffset=2 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45785 x=1284 y=944 width=22 height=28 xoffset=2 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45787 x=1779 y=361 width=24 height=29 xoffset=2 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45789 x=1709 y=391 width=22 height=29 xoffset=2 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45794 x=1881 y=913 width=23 height=28 xoffset=2 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45796 x=1542 y=683 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45797 x=1569 y=682 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45798 x=1623 y=682 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45800 x=1650 y=682 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45803 x=1677 y=682 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45804 x=162 y=303 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45805 x=189 y=303 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45806 x=216 y=302 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45807 x=243 y=302 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45811 x=270 y=302 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45812 x=1731 y=682 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45813 x=1785 y=682 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45815 x=378 y=302 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45816 x=405 y=302 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45817 x=432 y=302 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45818 x=459 y=302 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45819 x=671 y=31 width=26 height=30 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45823 x=752 y=31 width=26 height=30 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45824 x=468 y=799 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45825 x=494 y=799 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45828 x=27 y=713 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45832 x=621 y=302 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45840 x=520 y=799 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45841 x=546 y=799 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45843 x=702 y=302 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45844 x=1288 y=182 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45845 x=546 y=362 width=25 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45852 x=189 y=713 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45908 x=1809 y=914 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45909 x=1665 y=914 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45910 x=1641 y=914 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45912 x=1049 y=886 width=24 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45915 x=1473 y=915 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45916 x=967 y=392 width=23 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45918 x=991 y=392 width=23 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45919 x=1087 y=392 width=23 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45924 x=1449 y=915 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45925 x=936 y=944 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45927 x=884 y=362 width=25 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45929 x=1039 y=392 width=23 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45931 x=1046 y=31 width=25 height=30 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45934 x=775 y=915 width=24 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45936 x=598 y=799 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45937 x=624 y=799 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45940 x=1161 y=712 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45944 x=1539 y=301 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45952 x=650 y=799 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45953 x=676 y=799 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45955 x=351 y=332 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45956 x=1708 y=181 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45957 x=1170 y=362 width=25 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45964 x=672 y=944 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45968 x=175 y=916 width=24 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45972 x=895 y=392 width=23 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45984 x=1063 y=242 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45985 x=1543 y=391 width=23 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45992 x=390 y=886 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=45996 x=1026 y=770 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46020 x=491 y=973 width=27 height=23 xoffset=-1 yoffset=4 xadvance=26 page=0 chnl=15 -char id=46021 x=1680 y=653 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46024 x=1624 y=653 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46027 x=1568 y=653 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46028 x=1512 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46030 x=1876 y=181 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46032 x=0 y=213 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46036 x=1484 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46037 x=1316 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46039 x=56 y=213 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46041 x=84 y=213 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46043 x=173 y=0 width=28 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46045 x=1260 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46048 x=1714 y=421 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46052 x=290 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46056 x=1102 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46076 x=891 y=770 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46096 x=231 y=0 width=28 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46104 x=104 y=887 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46108 x=52 y=887 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46112 x=1711 y=241 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46120 x=26 y=887 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46121 x=1846 y=855 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46123 x=1620 y=271 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46132 x=1079 y=973 width=27 height=23 xoffset=-1 yoffset=4 xadvance=26 page=0 chnl=15 -char id=46160 x=924 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46161 x=868 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46164 x=616 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46168 x=532 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46176 x=420 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46177 x=392 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46179 x=196 y=212 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46181 x=224 y=212 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46188 x=1560 y=857 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46208 x=1059 y=0 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46216 x=621 y=770 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46237 x=308 y=212 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46244 x=1508 y=857 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46248 x=594 y=770 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46252 x=1248 y=362 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46261 x=1456 y=857 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46263 x=392 y=212 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46265 x=1274 y=362 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46272 x=280 y=655 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46276 x=196 y=655 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46280 x=168 y=655 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46288 x=112 y=655 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46293 x=448 y=212 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46300 x=911 y=973 width=27 height=23 xoffset=-1 yoffset=4 xadvance=26 page=0 chnl=15 -char id=46301 x=84 y=655 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46304 x=56 y=655 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46307 x=28 y=655 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46308 x=0 y=655 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46310 x=560 y=212 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46316 x=1876 y=623 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46317 x=1848 y=623 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46319 x=644 y=212 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46321 x=672 y=212 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46328 x=884 y=857 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46356 x=648 y=944 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46357 x=1054 y=944 width=22 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46360 x=960 y=944 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46363 x=888 y=944 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46364 x=1063 y=392 width=23 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46372 x=1008 y=944 width=22 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46373 x=1307 y=944 width=22 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46375 x=0 y=393 width=24 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46376 x=1080 y=272 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46377 x=1231 y=392 width=23 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46378 x=1300 y=362 width=25 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46384 x=1400 y=625 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46385 x=1344 y=625 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46388 x=1316 y=625 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46392 x=896 y=212 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46400 x=1288 y=625 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46401 x=1260 y=625 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46403 x=924 y=212 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46404 x=952 y=212 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46405 x=980 y=212 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46411 x=1199 y=0 width=27 height=30 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46412 x=651 y=683 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46413 x=705 y=683 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46416 x=1232 y=625 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46420 x=1092 y=212 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46428 x=1299 y=683 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46429 x=1353 y=683 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46431 x=1176 y=212 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46432 x=319 y=92 width=28 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46433 x=1647 y=271 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46496 x=1824 y=885 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46497 x=1699 y=885 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46500 x=208 y=858 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46504 x=1352 y=362 width=25 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46506 x=300 y=392 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46507 x=325 y=392 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46512 x=1274 y=886 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46513 x=1099 y=886 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46515 x=1890 y=271 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46516 x=1428 y=212 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46517 x=500 y=392 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46523 x=1278 y=31 width=24 height=30 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46524 x=135 y=713 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46525 x=243 y=713 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46528 x=1764 y=595 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46532 x=1596 y=211 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46540 x=675 y=712 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46541 x=999 y=712 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46543 x=1680 y=211 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46544 x=377 y=92 width=28 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46545 x=297 y=302 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46552 x=300 y=916 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46572 x=1764 y=211 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46608 x=967 y=973 width=27 height=23 xoffset=-1 yoffset=4 xadvance=26 page=0 chnl=15 -char id=46609 x=1568 y=595 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46612 x=1540 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46616 x=1512 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46629 x=0 y=243 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46636 x=409 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46644 x=464 y=92 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46664 x=1428 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46692 x=1377 y=741 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46696 x=1404 y=741 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46748 x=1400 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46749 x=1372 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46752 x=1288 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46756 x=1260 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46763 x=280 y=242 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46764 x=1232 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46769 x=1204 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46804 x=1176 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46832 x=1092 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46836 x=1647 y=740 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46840 x=918 y=302 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46848 x=1014 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46849 x=988 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46853 x=999 y=302 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46888 x=211 y=974 width=27 height=23 xoffset=-1 yoffset=4 xadvance=26 page=0 chnl=15 -char id=46889 x=1036 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46892 x=588 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46895 x=560 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46896 x=532 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46904 x=448 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46905 x=84 y=597 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46907 x=672 y=242 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46916 x=754 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46920 x=837 y=712 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46924 x=1296 y=302 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46932 x=650 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46933 x=468 y=857 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46944 x=525 y=915 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46948 x=520 y=857 width=25 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46952 x=650 y=392 width=24 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46960 x=200 y=916 width=24 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46961 x=50 y=916 width=24 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46963 x=1512 y=301 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46965 x=675 y=392 width=24 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46972 x=813 y=683 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46973 x=894 y=683 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46976 x=1002 y=683 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46980 x=1647 y=301 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46988 x=1029 y=683 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46989 x=1056 y=683 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46991 x=1728 y=301 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46992 x=1755 y=301 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46993 x=1782 y=301 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46994 x=1809 y=301 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46998 x=1137 y=683 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=46999 x=779 y=31 width=26 height=30 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47000 x=1014 y=857 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47001 x=1040 y=857 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47004 x=1812 y=681 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47008 x=54 y=333 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47016 x=1742 y=856 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47017 x=1820 y=856 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47019 x=135 y=333 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47020 x=952 y=242 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47021 x=189 y=333 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47028 x=1269 y=712 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47029 x=1755 y=711 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47032 x=513 y=741 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47047 x=297 y=332 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47049 x=324 y=332 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47084 x=625 y=915 width=24 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47085 x=504 y=944 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47088 x=1325 y=915 width=24 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47092 x=50 y=393 width=24 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47100 x=408 y=944 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47101 x=336 y=945 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47103 x=378 y=332 width=25 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47104 x=513 y=272 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47105 x=775 y=392 width=23 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47111 x=1327 y=31 width=23 height=30 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47112 x=442 y=799 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47113 x=104 y=800 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47116 x=1485 y=741 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47120 x=270 y=272 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47128 x=520 y=828 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47129 x=468 y=828 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47131 x=81 y=273 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47133 x=54 y=273 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47140 x=749 y=886 width=24 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47141 x=864 y=944 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47144 x=1125 y=915 width=24 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47148 x=1679 y=361 width=24 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47156 x=576 y=944 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47157 x=720 y=944 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47159 x=404 y=332 width=25 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47160 x=1792 y=241 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47161 x=919 y=392 width=23 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47168 x=286 y=829 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47172 x=1620 y=711 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47185 x=260 y=829 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47187 x=1684 y=241 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47196 x=1191 y=973 width=27 height=23 xoffset=-1 yoffset=3 xadvance=26 page=0 chnl=15 -char id=47197 x=28 y=243 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47200 x=840 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47204 x=140 y=243 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47212 x=784 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47213 x=756 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47215 x=1619 y=0 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47217 x=224 y=242 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47224 x=435 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47228 x=551 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47245 x=783 y=92 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47272 x=579 y=0 width=28 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47280 x=104 y=829 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47284 x=78 y=829 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47288 x=1657 y=241 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47296 x=52 y=829 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47297 x=0 y=829 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47299 x=698 y=31 width=26 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47301 x=430 y=332 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47308 x=1219 y=973 width=27 height=23 xoffset=-1 yoffset=3 xadvance=26 page=0 chnl=15 -char id=47312 x=672 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47316 x=392 y=242 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47325 x=644 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47327 x=84 y=32 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47329 x=532 y=242 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47336 x=616 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47337 x=700 y=242 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47340 x=392 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47344 x=840 y=242 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47352 x=336 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47353 x=280 y=568 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47355 x=364 y=31 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47357 x=870 y=92 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47364 x=1716 y=798 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47384 x=448 y=31 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47392 x=459 y=741 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47420 x=1664 y=798 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47421 x=456 y=332 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47424 x=462 y=683 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47428 x=482 y=332 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47436 x=1534 y=799 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47439 x=695 y=0 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47441 x=508 y=332 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47448 x=982 y=92 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47449 x=1010 y=92 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47452 x=252 y=568 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47456 x=1038 y=92 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47464 x=224 y=568 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47465 x=196 y=568 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47467 x=919 y=0 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47469 x=174 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47476 x=239 y=974 width=27 height=23 xoffset=-1 yoffset=3 xadvance=26 page=0 chnl=15 -char id=47477 x=1094 y=92 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47480 x=140 y=568 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47484 x=1150 y=92 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47492 x=84 y=568 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47493 x=1736 y=537 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47495 x=1367 y=0 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47497 x=1206 y=92 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47498 x=476 y=212 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47501 x=1262 y=92 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47502 x=1708 y=537 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47532 x=312 y=945 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47533 x=1215 y=944 width=22 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47536 x=840 y=944 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47540 x=1111 y=392 width=23 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47548 x=1238 y=944 width=22 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47549 x=1192 y=944 width=22 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47551 x=575 y=392 width=24 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47553 x=1255 y=392 width=23 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47560 x=810 y=770 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47561 x=756 y=770 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47564 x=729 y=770 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47566 x=1252 y=242 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47567 x=702 y=770 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47568 x=1225 y=242 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47569 x=1198 y=242 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47570 x=1144 y=242 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47576 x=567 y=770 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47577 x=513 y=770 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47579 x=162 y=333 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47581 x=81 y=333 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47582 x=1863 y=301 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47585 x=486 y=770 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47587 x=914 y=31 width=26 height=30 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47588 x=1222 y=857 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47589 x=1144 y=857 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47592 x=351 y=770 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47596 x=1026 y=302 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47604 x=988 y=857 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47605 x=858 y=857 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47607 x=594 y=302 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47608 x=168 y=123 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47609 x=534 y=332 width=25 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47610 x=196 y=122 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47616 x=729 y=712 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47617 x=27 y=771 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47624 x=81 y=303 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47637 x=54 y=303 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47672 x=144 y=945 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47673 x=48 y=945 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47676 x=1799 y=885 width=24 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47680 x=150 y=393 width=24 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47682 x=847 y=392 width=23 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47688 x=1545 y=915 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47689 x=1497 y=915 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47691 x=560 y=332 width=25 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47693 x=871 y=392 width=23 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47694 x=586 y=332 width=25 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47699 x=1351 y=31 width=23 height=30 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47700 x=702 y=857 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47701 x=1690 y=827 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47704 x=1809 y=711 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47708 x=1728 y=271 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47716 x=1534 y=828 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47717 x=1508 y=828 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47719 x=1674 y=271 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47720 x=1234 y=92 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47721 x=1080 y=332 width=25 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47728 x=696 y=944 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47729 x=480 y=944 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47732 x=1200 y=915 width=24 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47736 x=175 y=393 width=24 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47747 x=612 y=332 width=25 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47748 x=1161 y=272 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47749 x=1327 y=392 width=23 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47751 x=1020 y=31 width=25 height=30 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47756 x=962 y=828 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47784 x=827 y=973 width=27 height=23 xoffset=-1 yoffset=4 xadvance=26 page=0 chnl=15 -char id=47785 x=252 y=539 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47787 x=751 y=0 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47788 x=224 y=539 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47792 x=196 y=539 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47794 x=756 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47800 x=168 y=539 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47801 x=140 y=539 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47803 x=868 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47805 x=896 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47812 x=1888 y=420 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47816 x=0 y=452 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47832 x=144 y=0 width=28 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47833 x=145 y=452 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47868 x=1248 y=857 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47872 x=1274 y=857 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47876 x=810 y=272 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47885 x=1534 y=857 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47887 x=756 y=272 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47889 x=1586 y=857 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47896 x=379 y=974 width=27 height=23 xoffset=-1 yoffset=4 xadvance=26 page=0 chnl=15 -char id=47900 x=1708 y=508 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47904 x=1568 y=508 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47913 x=1232 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47915 x=1148 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47924 x=1204 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47925 x=1176 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47926 x=1148 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47928 x=1064 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47931 x=1036 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47932 x=896 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47933 x=1344 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47934 x=1372 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47940 x=840 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47941 x=812 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47943 x=1456 y=121 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47945 x=1484 y=121 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47949 x=784 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47951 x=1703 y=0 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47952 x=26 y=858 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47956 x=0 y=858 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47960 x=638 y=332 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47969 x=1716 y=827 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47971 x=378 y=272 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=47980 x=1161 y=741 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48008 x=1326 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48012 x=1539 y=741 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48016 x=664 y=332 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48036 x=728 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48040 x=700 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48044 x=616 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48052 x=504 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48055 x=1652 y=121 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48064 x=1135 y=973 width=27 height=23 xoffset=-1 yoffset=4 xadvance=26 page=0 chnl=15 -char id=48068 x=448 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48072 x=420 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48080 x=224 y=510 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48083 x=1232 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48120 x=528 y=944 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48121 x=600 y=944 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48124 x=1524 y=886 width=24 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48127 x=1713 y=914 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48128 x=1519 y=391 width=23 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48130 x=1183 y=392 width=23 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48136 x=1521 y=915 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48137 x=1593 y=915 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48139 x=690 y=332 width=25 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48140 x=1269 y=302 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48141 x=1591 y=391 width=23 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48143 x=1072 y=31 width=25 height=30 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48145 x=1761 y=914 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48148 x=216 y=713 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48149 x=1404 y=712 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48150 x=1782 y=740 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48151 x=1080 y=302 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48152 x=1377 y=712 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48155 x=354 y=683 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48156 x=945 y=302 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48157 x=675 y=302 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48158 x=540 y=302 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48159 x=1458 y=272 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48164 x=1809 y=740 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48165 x=1839 y=681 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48167 x=702 y=272 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48169 x=1441 y=242 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48173 x=783 y=770 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48176 x=1482 y=828 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48177 x=1430 y=857 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48180 x=1596 y=682 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48184 x=729 y=272 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48192 x=598 y=828 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48193 x=624 y=828 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48195 x=243 y=272 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48196 x=532 y=152 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48197 x=716 y=332 width=25 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48201 x=1431 y=712 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48204 x=810 y=712 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48205 x=405 y=712 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48208 x=270 y=713 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48221 x=1566 y=712 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48260 x=1857 y=913 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48261 x=216 y=945 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48264 x=875 y=915 width=24 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48267 x=1175 y=915 width=24 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48268 x=1829 y=361 width=24 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48270 x=1279 y=392 width=23 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48276 x=984 y=944 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48277 x=552 y=944 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48279 x=742 y=332 width=25 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48281 x=1207 y=392 width=23 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48282 x=794 y=332 width=25 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48288 x=26 y=829 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48289 x=130 y=829 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48292 x=702 y=741 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48295 x=1350 y=712 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48296 x=891 y=272 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48304 x=364 y=828 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48305 x=390 y=828 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48307 x=162 y=273 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48308 x=1176 y=152 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48309 x=820 y=332 width=25 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48316 x=264 y=945 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48317 x=288 y=945 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48320 x=700 y=915 width=24 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48324 x=1804 y=361 width=24 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48333 x=240 y=945 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48335 x=846 y=332 width=25 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48336 x=864 y=272 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48337 x=1567 y=391 width=23 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48341 x=999 y=886 width=24 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48344 x=1118 y=857 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48348 x=786 y=683 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48372 x=1711 y=943 width=27 height=24 xoffset=-1 yoffset=3 xadvance=26 page=0 chnl=15 -char id=48373 x=896 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48374 x=812 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48376 x=784 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48380 x=756 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48388 x=728 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48389 x=700 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48391 x=1484 y=151 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48393 x=1540 y=151 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48400 x=1801 y=420 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48404 x=493 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48420 x=550 y=0 width=28 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48428 x=273 y=684 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48448 x=347 y=0 width=28 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48456 x=676 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48457 x=1170 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48460 x=1196 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48464 x=1134 y=302 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48472 x=1222 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48473 x=1300 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48484 x=1627 y=944 width=27 height=24 xoffset=-1 yoffset=3 xadvance=26 page=0 chnl=15 -char id=48488 x=588 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48512 x=532 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48513 x=504 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48516 x=476 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48519 x=448 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48520 x=420 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48521 x=1736 y=151 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48522 x=1764 y=151 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48528 x=392 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48529 x=336 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48531 x=1848 y=151 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48533 x=1876 y=151 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48537 x=280 y=481 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48538 x=252 y=481 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48540 x=728 y=857 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48548 x=872 y=332 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48560 x=224 y=31 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48568 x=378 y=770 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48596 x=1092 y=857 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48597 x=156 y=887 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48600 x=918 y=770 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48604 x=898 y=332 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48617 x=924 y=332 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48624 x=56 y=481 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48628 x=1850 y=449 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48632 x=1822 y=449 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48640 x=1794 y=449 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48643 x=364 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48645 x=392 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48652 x=1795 y=943 width=27 height=24 xoffset=-1 yoffset=3 xadvance=26 page=0 chnl=15 -char id=48653 x=1710 y=450 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48656 x=1682 y=450 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48660 x=56 y=684 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48668 x=28 y=684 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48669 x=0 y=684 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48671 x=588 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48708 x=624 y=944 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48709 x=192 y=945 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48712 x=950 y=915 width=24 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48716 x=823 y=392 width=23 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48718 x=799 y=392 width=23 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48724 x=816 y=944 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48725 x=1833 y=914 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48727 x=950 y=332 width=25 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48729 x=1615 y=391 width=23 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48730 x=976 y=332 width=25 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48731 x=1098 y=31 width=25 height=30 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48736 x=1076 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48737 x=989 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48740 x=931 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48744 x=1595 y=61 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48746 x=0 y=63 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48752 x=1189 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48753 x=406 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48755 x=1871 y=31 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48756 x=1842 y=31 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48757 x=1813 y=31 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48763 x=289 y=0 width=28 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48764 x=728 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48765 x=588 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48768 x=1453 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48772 x=1885 y=61 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48780 x=560 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48781 x=476 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48783 x=1769 y=61 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48784 x=1740 y=61 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48785 x=1092 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48792 x=1540 y=421 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48793 x=1656 y=421 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48808 x=1685 y=421 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48848 x=1222 y=799 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48849 x=1196 y=799 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48852 x=300 y=684 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48855 x=111 y=684 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48856 x=1836 y=271 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48864 x=1170 y=799 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48867 x=1120 y=182 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48868 x=638 y=62 width=28 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48869 x=1002 y=332 width=25 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48876 x=224 y=655 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48897 x=1148 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48904 x=1144 y=799 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48905 x=1092 y=799 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48920 x=1066 y=799 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48921 x=988 y=799 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48923 x=1204 y=182 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48924 x=377 y=62 width=28 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48925 x=962 y=799 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48960 x=351 y=974 width=27 height=23 xoffset=-1 yoffset=3 xadvance=26 page=0 chnl=15 -char id=48961 x=1568 y=624 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48964 x=1540 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48968 x=1372 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48976 x=1204 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48977 x=1148 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=48981 x=1456 y=181 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49044 x=1863 y=710 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49072 x=407 y=973 width=27 height=23 xoffset=-1 yoffset=3 xadvance=26 page=0 chnl=15 -char id=49093 x=1484 y=181 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49100 x=868 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49101 x=644 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49104 x=616 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49108 x=420 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49116 x=336 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49119 x=1596 y=181 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49121 x=1624 y=181 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49212 x=252 y=626 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49233 x=1652 y=181 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49240 x=435 y=973 width=27 height=23 xoffset=-1 yoffset=4 xadvance=26 page=0 chnl=15 -char id=49244 x=168 y=626 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49248 x=84 y=626 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49256 x=1764 y=653 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49257 x=1736 y=653 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49296 x=0 y=887 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49297 x=1326 y=857 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49300 x=216 y=771 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49304 x=1028 y=332 width=25 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49312 x=312 y=858 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49313 x=286 y=858 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49315 x=1630 y=241 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49317 x=1054 y=332 width=25 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49324 x=728 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49325 x=786 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49327 x=464 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49328 x=815 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49331 x=725 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49332 x=1827 y=61 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49333 x=899 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49334 x=319 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49340 x=1308 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49341 x=1743 y=420 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49343 x=232 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49344 x=406 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49345 x=754 y=92 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49349 x=1421 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49352 x=140 y=510 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49353 x=0 y=742 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49356 x=1232 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49360 x=728 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49368 x=945 y=770 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49369 x=540 y=741 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49371 x=1288 y=212 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49372 x=1426 y=31 width=29 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49373 x=672 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49380 x=638 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49381 x=351 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49384 x=264 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49388 x=609 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49396 x=757 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49397 x=754 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49399 x=1131 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49401 x=290 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49408 x=1260 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49412 x=1624 y=479 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49416 x=168 y=183 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49424 x=405 y=770 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49429 x=84 y=183 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49436 x=208 y=829 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49437 x=900 y=915 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49438 x=150 y=916 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49439 x=1288 y=152 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49440 x=156 y=858 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49443 x=234 y=887 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49444 x=1106 y=332 width=25 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49446 x=750 y=392 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49447 x=1429 y=362 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49452 x=724 y=886 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49453 x=25 y=916 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49455 x=1414 y=242 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49456 x=56 y=153 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49457 x=375 y=392 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49462 x=1893 y=681 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49464 x=1316 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49465 x=759 y=683 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49468 x=1624 y=566 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49472 x=420 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49480 x=432 y=712 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49481 x=1326 y=683 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49483 x=392 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49484 x=1576 y=31 width=29 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49485 x=954 y=92 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49492 x=1404 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49493 x=1124 y=886 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49496 x=1586 y=828 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49500 x=1132 y=332 width=25 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49508 x=1149 y=886 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49509 x=1224 y=886 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49511 x=1846 y=241 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49512 x=728 y=212 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49513 x=125 y=393 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49520 x=1596 y=624 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49524 x=1708 y=624 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49528 x=812 y=212 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49541 x=420 y=212 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49548 x=1739 y=943 width=27 height=24 xoffset=-1 yoffset=3 xadvance=26 page=0 chnl=15 -char id=49549 x=252 y=655 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49550 x=308 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49552 x=840 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49556 x=896 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49558 x=364 y=212 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49564 x=1148 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49565 x=1176 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49567 x=1764 y=181 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49569 x=1736 y=181 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49573 x=1792 y=652 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49576 x=1073 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49577 x=1102 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49580 x=1160 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49584 x=116 y=93 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49597 x=1276 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49604 x=1458 y=712 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49608 x=140 y=626 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49612 x=1680 y=181 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49620 x=1485 y=712 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49623 x=174 y=92 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49624 x=521 y=0 width=28 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49632 x=702 y=799 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49636 x=1690 y=798 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49640 x=1188 y=302 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49648 x=728 y=799 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49649 x=754 y=799 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49651 x=1161 y=302 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49660 x=1823 y=943 width=27 height=24 xoffset=-1 yoffset=3 xadvance=26 page=0 chnl=15 -char id=49661 x=476 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49664 x=504 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49668 x=532 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49676 x=560 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49677 x=588 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49679 x=1568 y=181 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49681 x=1540 y=181 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49688 x=672 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49689 x=700 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49692 x=728 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49695 x=756 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49696 x=784 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49704 x=812 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49705 x=840 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49707 x=1512 y=181 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49709 x=896 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49711 x=392 y=31 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49713 x=952 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49714 x=980 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49716 x=780 y=799 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49736 x=476 y=31 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49744 x=54 y=742 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49745 x=108 y=742 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49748 x=1120 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49752 x=1428 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49760 x=135 y=742 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49765 x=1400 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49772 x=806 y=799 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49773 x=832 y=799 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49776 x=432 y=741 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49780 x=1158 y=332 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49788 x=858 y=799 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49789 x=884 y=799 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49791 x=1372 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49793 x=1210 y=332 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49800 x=1428 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49801 x=1456 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49808 x=1484 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49816 x=1512 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49819 x=1344 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49821 x=1316 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49828 x=1879 y=942 width=27 height=24 xoffset=-1 yoffset=3 xadvance=26 page=0 chnl=15 -char id=49829 x=1624 y=624 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49832 x=1652 y=624 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49836 x=1680 y=624 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49837 x=1260 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49844 x=1736 y=624 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49845 x=1764 y=624 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49847 x=1232 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49849 x=1176 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49884 x=1050 y=915 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49885 x=1025 y=915 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49888 x=1014 y=799 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49891 x=1040 y=799 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49892 x=1262 y=332 width=25 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49899 x=351 y=302 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49900 x=1000 y=915 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49901 x=925 y=915 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49903 x=324 y=302 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49905 x=1854 y=361 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49910 x=1118 y=799 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49912 x=1792 y=391 width=29 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49913 x=873 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49915 x=551 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49916 x=60 y=423 width=29 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49920 x=580 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49928 x=1363 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49929 x=1334 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49932 x=696 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49933 x=870 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49939 x=405 y=0 width=28 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49940 x=841 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49941 x=696 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49944 x=1882 y=391 width=29 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49948 x=1516 y=31 width=29 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49956 x=348 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49957 x=1859 y=420 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49960 x=1606 y=31 width=29 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49961 x=1479 y=61 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=49989 x=1508 y=61 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50024 x=543 y=683 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50025 x=867 y=683 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50028 x=448 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50032 x=1593 y=271 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50034 x=1350 y=272 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50040 x=948 y=683 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50041 x=1434 y=683 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50044 x=1856 y=61 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50045 x=1134 y=272 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50052 x=174 y=452 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50056 x=1366 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50060 x=29 y=93 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50112 x=1772 y=420 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50136 x=1851 y=943 width=27 height=24 xoffset=-1 yoffset=3 xadvance=26 page=0 chnl=15 -char id=50137 x=644 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50140 x=672 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50143 x=700 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50144 x=924 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50146 x=868 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50152 x=756 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50153 x=784 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50157 x=840 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50164 x=1305 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50165 x=1392 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50168 x=902 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50184 x=1247 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50192 x=812 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50212 x=667 y=92 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50220 x=1866 y=681 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50224 x=54 y=713 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50228 x=405 y=272 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50236 x=1248 y=799 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50237 x=1274 y=799 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50248 x=1599 y=944 width=27 height=24 xoffset=-1 yoffset=3 xadvance=26 page=0 chnl=15 -char id=50276 x=980 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50277 x=1008 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50280 x=1036 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50284 x=1064 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50292 x=1092 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50293 x=1120 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50297 x=756 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50304 x=1053 y=712 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50324 x=637 y=0 width=28 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50332 x=1204 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50360 x=1300 y=799 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50364 x=1539 y=712 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50409 x=700 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50416 x=267 y=974 width=27 height=23 xoffset=-1 yoffset=3 xadvance=26 page=0 chnl=15 -char id=50417 x=1344 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50420 x=1372 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50424 x=1400 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50426 x=672 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50431 x=532 y=31 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50432 x=1428 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50433 x=1456 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50444 x=1782 y=711 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50448 x=189 y=742 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50452 x=216 y=742 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50460 x=1352 y=799 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50472 x=486 y=741 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50473 x=648 y=741 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50476 x=1652 y=653 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50480 x=1360 y=242 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50488 x=729 y=741 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50489 x=810 y=741 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50491 x=1392 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50493 x=1306 y=242 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50500 x=1820 y=652 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50501 x=1848 y=652 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50504 x=1876 y=652 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50505 x=560 y=182 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50506 x=532 y=182 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50508 x=504 y=182 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50509 x=476 y=182 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50510 x=448 y=182 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50515 x=420 y=182 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50516 x=1738 y=450 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50517 x=1766 y=449 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50519 x=308 y=182 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50520 x=280 y=182 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50521 x=224 y=182 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50525 x=1878 y=449 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50526 x=0 y=481 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50528 x=1134 y=770 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50529 x=1107 y=770 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50532 x=28 y=481 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50536 x=196 y=182 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50544 x=783 y=712 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50545 x=1053 y=770 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50547 x=140 y=183 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50548 x=1276 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50549 x=108 y=303 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50556 x=112 y=481 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50557 x=140 y=481 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50560 x=168 y=481 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50564 x=112 y=183 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50567 x=56 y=183 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50572 x=196 y=481 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50573 x=224 y=481 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50575 x=28 y=183 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50577 x=0 y=183 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50581 x=308 y=480 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50583 x=504 y=31 width=27 height=30 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50584 x=135 y=771 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50588 x=364 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50592 x=1792 y=151 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50601 x=192 y=684 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50612 x=450 y=915 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50613 x=425 y=915 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50616 x=364 y=857 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50617 x=135 y=273 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50619 x=1749 y=885 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50620 x=225 y=393 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50621 x=250 y=392 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50622 x=275 y=392 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50628 x=1424 y=886 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50629 x=1399 y=886 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50630 x=0 y=273 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50631 x=1549 y=241 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50632 x=1708 y=151 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50633 x=450 y=392 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50634 x=1171 y=242 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50636 x=1074 y=886 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50638 x=1768 y=827 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50640 x=324 y=713 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50641 x=1107 y=712 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50644 x=560 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50648 x=1680 y=151 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50656 x=891 y=741 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50657 x=1242 y=741 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50659 x=1652 y=151 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50661 x=27 y=333 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50668 x=1250 y=915 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50669 x=1225 y=915 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50670 x=1150 y=915 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50672 x=1248 y=828 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50676 x=1504 y=361 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50678 x=1529 y=361 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50679 x=200 y=393 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50684 x=1100 y=915 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50685 x=1075 y=915 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50686 x=1350 y=302 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50687 x=1107 y=302 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50688 x=1624 y=151 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50689 x=1729 y=361 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50693 x=550 y=915 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50694 x=858 y=828 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50695 x=1253 y=31 width=24 height=30 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50696 x=702 y=712 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50700 x=616 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50704 x=1596 y=151 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50712 x=381 y=683 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50713 x=489 y=683 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50715 x=1568 y=151 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50716 x=1305 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50724 x=1767 y=943 width=27 height=24 xoffset=-1 yoffset=3 xadvance=26 page=0 chnl=15 -char id=50725 x=644 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50728 x=672 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50732 x=1456 y=151 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50733 x=1428 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50734 x=1400 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50736 x=1372 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50739 x=588 y=31 width=27 height=30 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=50740 x=840 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50741 x=868 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50743 x=1344 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50745 x=1316 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50747 x=84 y=0 width=27 height=31 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=50752 x=522 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50753 x=812 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50756 x=960 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50760 x=145 y=63 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50768 x=1047 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50769 x=1105 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50771 x=203 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50772 x=608 y=0 width=28 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50773 x=1134 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50780 x=162 y=713 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50781 x=351 y=712 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50784 x=924 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50796 x=756 y=741 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50799 x=812 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50801 x=840 y=683 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50808 x=572 y=799 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50809 x=1326 y=799 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50812 x=1430 y=799 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50816 x=297 y=272 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50824 x=182 y=829 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50825 x=442 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50827 x=1522 y=241 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50829 x=494 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50836 x=1683 y=943 width=27 height=24 xoffset=-1 yoffset=3 xadvance=26 page=0 chnl=15 -char id=50837 x=952 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50840 x=980 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50844 x=232 y=92 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50852 x=1008 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50853 x=1036 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50855 x=1204 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50857 x=1148 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50864 x=1064 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50865 x=1092 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50868 x=1120 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50872 x=1120 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50873 x=1064 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50874 x=980 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50880 x=1148 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50881 x=1176 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50883 x=924 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50885 x=896 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50892 x=780 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50893 x=1638 y=798 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50896 x=1482 y=799 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50900 x=1288 y=332 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50908 x=1456 y=799 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50909 x=312 y=887 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50912 x=1451 y=0 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50913 x=1314 y=332 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50920 x=864 y=770 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50921 x=1215 y=272 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50924 x=1204 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50928 x=868 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50936 x=324 y=771 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50937 x=1461 y=683 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50941 x=840 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50948 x=130 y=858 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50949 x=1742 y=827 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50952 x=756 y=712 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50956 x=1340 y=332 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50964 x=1664 y=827 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50965 x=1274 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50967 x=812 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50969 x=1366 y=332 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50976 x=1288 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50977 x=1316 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50980 x=1344 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50984 x=784 y=152 width=27 height=29 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=50992 x=1372 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50993 x=1400 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50995 x=290 y=92 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50997 x=261 y=92 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=50999 x=1535 y=0 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51004 x=154 y=974 width=28 height=23 xoffset=-1 yoffset=3 xadvance=26 page=0 chnl=15 -char id=51005 x=1428 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51008 x=1456 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51012 x=728 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51018 x=700 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51020 x=1484 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51021 x=1512 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51023 x=644 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51025 x=29 y=63 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51026 x=588 y=152 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51027 x=616 y=31 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51028 x=1540 y=480 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51029 x=1568 y=479 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51030 x=1596 y=479 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51031 x=56 y=0 width=27 height=31 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51032 x=754 y=857 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51036 x=1196 y=857 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51040 x=567 y=302 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51048 x=1404 y=857 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51051 x=729 y=302 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51060 x=1689 y=914 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51061 x=1425 y=915 width=23 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51064 x=649 y=886 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51068 x=1754 y=361 width=24 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51069 x=1135 y=392 width=23 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51070 x=1159 y=392 width=23 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51075 x=1392 y=332 width=25 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51076 x=1617 y=915 width=23 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51077 x=1569 y=915 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51079 x=1418 y=332 width=25 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51080 x=504 y=152 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51081 x=25 y=393 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51082 x=891 y=302 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51086 x=936 y=799 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51088 x=29 y=452 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51089 x=1652 y=479 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51092 x=1680 y=479 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51094 x=476 y=152 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51095 x=1708 y=479 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51096 x=448 y=152 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51098 x=364 y=152 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51104 x=84 y=481 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51105 x=1764 y=479 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51107 x=308 y=152 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51108 x=280 y=152 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51109 x=252 y=152 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51110 x=224 y=152 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51116 x=1792 y=478 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51117 x=1820 y=478 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51120 x=1848 y=478 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51124 x=168 y=153 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51132 x=1876 y=478 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51133 x=1890 y=739 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51135 x=84 y=153 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51136 x=1456 y=31 width=29 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51137 x=0 y=153 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51144 x=1653 y=450 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51145 x=0 y=510 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51148 x=28 y=510 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51150 x=1876 y=121 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51152 x=1848 y=121 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51160 x=56 y=510 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51165 x=1820 y=121 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51172 x=84 y=510 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51176 x=112 y=510 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51180 x=1792 y=121 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51200 x=936 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51201 x=1024 y=886 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51204 x=936 y=857 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51208 x=1444 y=332 width=25 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51210 x=1454 y=362 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51216 x=924 y=886 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51217 x=100 y=916 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51219 x=108 y=273 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51221 x=1470 y=332 width=25 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51222 x=216 y=272 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51228 x=168 y=510 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51229 x=81 y=713 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51232 x=196 y=510 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51236 x=1736 y=121 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51244 x=1647 y=711 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51245 x=1566 y=741 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51247 x=1708 y=121 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51249 x=1680 y=121 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51256 x=1188 y=741 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51260 x=156 y=829 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51264 x=1522 y=331 width=25 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51272 x=1174 y=886 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51273 x=1549 y=886 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51276 x=522 y=62 width=28 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51277 x=1548 y=331 width=25 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51284 x=476 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51312 x=1107 y=973 width=27 height=23 xoffset=-1 yoffset=4 xadvance=26 page=0 chnl=15 -char id=51313 x=1624 y=121 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51316 x=532 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51320 x=588 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51322 x=1596 y=121 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51328 x=644 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51329 x=672 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51331 x=1568 y=121 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51333 x=1540 y=121 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51334 x=1512 y=121 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51335 x=168 y=31 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51339 x=28 y=0 width=27 height=31 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51340 x=1479 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51341 x=235 y=423 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51348 x=783 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51357 x=206 y=423 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51359 x=1015 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51361 x=148 y=423 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51368 x=756 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51388 x=318 y=0 width=28 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51389 x=1704 y=682 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51396 x=182 y=858 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51400 x=338 y=858 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51404 x=621 y=272 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51412 x=962 y=857 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51413 x=1066 y=857 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51415 x=648 y=272 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51417 x=1378 y=857 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51424 x=995 y=973 width=27 height=23 xoffset=-1 yoffset=4 xadvance=26 page=0 chnl=15 -char id=51425 x=1428 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51428 x=868 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51445 x=1288 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51452 x=924 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51453 x=952 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51456 x=980 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51460 x=1008 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51461 x=1260 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51462 x=1232 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51468 x=1092 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51469 x=1120 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51471 x=1204 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51473 x=1176 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51480 x=1508 y=799 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51500 x=1143 y=0 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51508 x=648 y=712 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51536 x=1742 y=798 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51537 x=1768 y=798 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51540 x=0 y=771 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51544 x=1574 y=331 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51552 x=1794 y=798 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51553 x=1846 y=797 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51555 x=1120 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51564 x=1260 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51568 x=1288 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51572 x=1484 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51580 x=1540 y=509 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51592 x=1163 y=973 width=27 height=23 xoffset=-1 yoffset=4 xadvance=26 page=0 chnl=15 -char id=51593 x=1064 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51596 x=1596 y=508 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51600 x=1624 y=508 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51608 x=1652 y=508 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51609 x=1680 y=508 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51611 x=1036 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51613 x=1008 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51648 x=910 y=799 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51649 x=1275 y=915 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51652 x=182 y=887 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51655 x=130 y=887 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51656 x=1600 y=331 width=25 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51658 x=1479 y=362 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51664 x=774 y=886 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51665 x=849 y=886 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51667 x=675 y=272 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51669 x=1554 y=361 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51670 x=783 y=272 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51673 x=1299 y=886 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51674 x=1482 y=857 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51676 x=1852 y=391 width=29 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51677 x=261 y=452 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51680 x=232 y=452 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51682 x=1784 y=31 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51684 x=1755 y=31 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51687 x=1726 y=31 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51692 x=116 y=452 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51693 x=58 y=452 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51695 x=812 y=92 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51696 x=493 y=92 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51697 x=58 y=93 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51704 x=1569 y=421 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51705 x=203 y=452 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51708 x=1732 y=391 width=29 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51712 x=1486 y=31 width=29 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51720 x=1627 y=421 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51721 x=380 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51723 x=1334 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51724 x=1395 y=31 width=30 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51725 x=928 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51732 x=1822 y=391 width=29 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51736 x=1598 y=421 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51753 x=754 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51788 x=165 y=684 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51789 x=1836 y=739 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51792 x=280 y=539 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51796 x=837 y=272 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51804 x=1728 y=740 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51805 x=1701 y=740 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51807 x=0 y=93 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51808 x=1666 y=31 width=29 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51809 x=1485 y=271 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51816 x=119 y=423 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51837 x=1682 y=61 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51844 x=1620 y=740 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51864 x=1636 y=31 width=29 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51900 x=883 y=973 width=27 height=23 xoffset=-1 yoffset=4 xadvance=26 page=0 chnl=15 -char id=51901 x=308 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51904 x=336 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51908 x=364 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51916 x=476 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51917 x=700 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51919 x=644 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51921 x=616 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51923 x=560 y=31 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51928 x=1762 y=391 width=29 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51929 x=1537 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51936 x=1189 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51948 x=1073 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51956 x=728 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51976 x=986 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51984 x=378 y=712 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51988 x=297 y=713 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=51992 x=1782 y=271 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52000 x=1846 y=826 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52001 x=1872 y=826 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52033 x=364 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52040 x=756 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52041 x=784 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52044 x=812 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52048 x=840 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52056 x=868 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52057 x=896 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52061 x=336 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52068 x=1488 y=683 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52088 x=463 y=0 width=28 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52089 x=1626 y=331 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52124 x=1380 y=683 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52152 x=1036 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52180 x=603 y=973 width=27 height=23 xoffset=-1 yoffset=4 xadvance=26 page=0 chnl=15 -char id=52196 x=1064 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52199 x=308 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52201 x=252 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52236 x=1272 y=683 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52237 x=597 y=683 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52240 x=246 y=684 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52244 x=27 y=303 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52252 x=219 y=684 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52253 x=390 y=857 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52257 x=135 y=303 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52258 x=224 y=122 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52263 x=644 y=31 width=26 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52264 x=1566 y=450 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52265 x=1092 y=538 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52268 x=1120 y=538 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52270 x=420 y=31 width=27 height=30 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52272 x=140 y=123 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52280 x=1148 y=538 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52281 x=1176 y=538 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52283 x=112 y=123 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52284 x=56 y=123 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52285 x=28 y=123 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52286 x=0 y=123 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52292 x=1260 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52293 x=540 y=770 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52296 x=1288 y=538 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52300 x=1878 y=91 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52308 x=648 y=770 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52309 x=675 y=770 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52311 x=1766 y=91 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52312 x=493 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52313 x=1738 y=91 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52320 x=1450 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52324 x=1316 y=538 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52326 x=1843 y=0 width=27 height=30 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52328 x=1710 y=91 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52336 x=1372 y=538 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52341 x=1682 y=91 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52376 x=78 y=887 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52377 x=1449 y=886 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52380 x=208 y=887 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52384 x=1652 y=331 width=25 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52392 x=799 y=886 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52393 x=1874 y=884 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52395 x=1576 y=241 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52396 x=1654 y=91 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52397 x=600 y=392 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52404 x=1568 y=537 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52405 x=972 y=770 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52408 x=1624 y=537 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52412 x=1486 y=91 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52420 x=999 y=770 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52421 x=1080 y=770 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52423 x=1458 y=91 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52425 x=1346 y=92 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52432 x=1378 y=799 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52436 x=1404 y=799 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52452 x=1318 y=92 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52460 x=1652 y=537 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52464 x=1680 y=537 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52481 x=1290 y=92 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52488 x=1376 y=944 width=27 height=25 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52489 x=1283 y=0 width=27 height=30 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=52492 x=1764 y=537 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52496 x=1178 y=92 width=27 height=29 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=52504 x=1122 y=92 width=27 height=29 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=52505 x=1066 y=92 width=27 height=29 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=52507 x=891 y=0 width=27 height=30 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=52509 x=835 y=0 width=27 height=30 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=52516 x=783 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52520 x=667 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52524 x=666 y=0 width=28 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52537 x=87 y=63 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52572 x=1560 y=799 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52576 x=1586 y=799 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52580 x=941 y=31 width=26 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52588 x=1612 y=798 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52589 x=1678 y=331 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52591 x=1603 y=241 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52593 x=1704 y=331 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52600 x=1404 y=944 width=27 height=25 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52616 x=926 y=92 width=27 height=29 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=52628 x=898 y=92 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52629 x=336 y=31 width=27 height=30 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=52632 x=308 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52636 x=1008 y=242 width=27 height=29 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=52644 x=756 y=242 width=27 height=29 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=52645 x=588 y=242 width=27 height=29 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=52647 x=56 y=32 width=27 height=30 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=52649 x=0 y=32 width=27 height=30 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=52656 x=1820 y=798 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52676 x=0 y=0 width=27 height=31 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=52684 x=1863 y=739 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52688 x=700 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52712 x=1872 y=797 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52716 x=1512 y=741 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52720 x=1202 y=31 width=25 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52728 x=1730 y=331 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52729 x=1756 y=331 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52731 x=364 y=242 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52733 x=994 y=31 width=25 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52740 x=336 y=242 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52744 x=728 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52748 x=308 y=242 width=27 height=29 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=52756 x=252 y=242 width=27 height=29 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=52761 x=1591 y=0 width=27 height=30 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=52768 x=1655 y=943 width=27 height=24 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52769 x=1563 y=0 width=27 height=30 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=52772 x=812 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52776 x=56 y=243 width=27 height=29 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=52784 x=1792 y=211 width=27 height=29 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=52785 x=1708 y=211 width=27 height=29 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=52787 x=1423 y=0 width=27 height=30 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=52789 x=1395 y=0 width=27 height=30 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=52824 x=312 y=829 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52825 x=874 y=886 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52828 x=338 y=829 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52831 x=1324 y=886 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52832 x=1579 y=361 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52833 x=1604 y=361 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52840 x=325 y=916 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52841 x=350 y=916 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52843 x=1765 y=241 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52845 x=1654 y=361 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52852 x=87 y=452 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52853 x=868 y=567 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52856 x=896 y=567 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52860 x=1484 y=211 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52868 x=924 y=567 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52869 x=952 y=567 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52871 x=1204 y=212 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52873 x=1008 y=212 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52880 x=1674 y=740 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52881 x=1134 y=741 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52884 x=864 y=741 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52888 x=1873 y=241 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52896 x=1728 y=711 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52897 x=1701 y=711 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52899 x=27 y=273 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52900 x=1624 y=61 width=28 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52901 x=189 y=273 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52908 x=1830 y=420 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52909 x=980 y=567 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52929 x=868 y=212 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52964 x=546 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52965 x=1374 y=886 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52968 x=572 y=828 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52971 x=1624 y=885 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52972 x=1782 y=331 width=25 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52980 x=674 y=886 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52981 x=1400 y=915 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52983 x=351 y=272 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52984 x=784 y=212 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52985 x=1704 y=361 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52992 x=1755 y=740 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52993 x=1164 y=683 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=52996 x=1407 y=683 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53000 x=432 y=272 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53008 x=1026 y=712 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53009 x=1512 y=712 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53011 x=486 y=272 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53013 x=567 y=272 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53020 x=416 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53024 x=234 y=829 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53028 x=1808 y=331 width=25 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53036 x=1724 y=885 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53037 x=899 y=886 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53039 x=594 y=272 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53040 x=700 y=212 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53041 x=75 y=393 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53048 x=972 y=712 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53076 x=295 y=974 width=27 height=23 xoffset=-1 yoffset=4 xadvance=26 page=0 chnl=15 -char id=53077 x=1008 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53080 x=1036 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53084 x=1064 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53092 x=1092 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53093 x=1120 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53095 x=588 y=212 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53097 x=504 y=212 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53104 x=1511 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53105 x=1482 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53108 x=1424 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53112 x=145 y=93 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53120 x=1395 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53125 x=841 y=92 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53132 x=324 y=742 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53153 x=980 y=242 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53160 x=1291 y=770 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53168 x=216 y=332 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53188 x=799 y=973 width=27 height=23 xoffset=-1 yoffset=4 xadvance=26 page=0 chnl=15 -char id=53216 x=1148 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53217 x=1176 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53220 x=1204 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53224 x=1736 y=479 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53232 x=1232 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53233 x=1260 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53235 x=924 y=242 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53237 x=1288 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53244 x=1794 y=856 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53248 x=1768 y=856 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53252 x=1860 y=331 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53265 x=1886 y=331 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53272 x=108 y=713 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53293 x=896 y=242 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53300 x=1690 y=856 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53301 x=1664 y=856 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53304 x=0 y=713 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53308 x=78 y=363 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53316 x=1638 y=856 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53317 x=1612 y=856 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53319 x=868 y=242 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53321 x=104 y=363 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53328 x=1344 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53332 x=1372 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53336 x=1400 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53344 x=1428 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53356 x=687 y=973 width=27 height=23 xoffset=-1 yoffset=4 xadvance=26 page=0 chnl=15 -char id=53357 x=1456 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53360 x=1484 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53364 x=1512 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53372 x=1540 y=567 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53373 x=1568 y=566 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53377 x=1596 y=566 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53412 x=1349 y=886 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53413 x=1737 y=914 width=23 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53416 x=1170 y=857 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53420 x=400 y=392 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53428 x=1785 y=914 width=23 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53429 x=24 y=945 width=23 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53431 x=0 y=333 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53433 x=0 y=916 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53440 x=1245 y=683 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53441 x=1218 y=683 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53444 x=1191 y=683 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53448 x=1890 y=301 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53449 x=1836 y=301 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53456 x=1110 y=683 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53457 x=1083 y=683 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53459 x=1701 y=301 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53460 x=1674 y=301 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53461 x=1620 y=301 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53468 x=910 y=857 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53469 x=832 y=857 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53472 x=921 y=683 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53476 x=1593 y=301 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53484 x=806 y=857 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53485 x=780 y=857 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53487 x=1566 y=301 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53488 x=812 y=242 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53489 x=130 y=363 width=25 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53496 x=732 y=683 width=26 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53517 x=1485 y=301 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53552 x=75 y=916 width=24 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53553 x=72 y=945 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53556 x=125 y=916 width=24 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53560 x=1879 y=361 width=24 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53562 x=1495 y=392 width=23 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53568 x=96 y=945 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53569 x=120 y=945 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53571 x=182 y=363 width=25 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53572 x=1458 y=302 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53573 x=1423 y=392 width=23 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53580 x=624 y=857 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53581 x=598 y=857 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53584 x=408 y=683 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53588 x=1431 y=302 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53596 x=572 y=857 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53597 x=546 y=857 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53599 x=1404 y=302 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53601 x=208 y=363 width=25 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53608 x=225 y=916 width=24 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53612 x=275 y=916 width=24 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53628 x=1377 y=302 width=26 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53636 x=494 y=857 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53640 x=138 y=684 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53664 x=1051 y=973 width=27 height=23 xoffset=-1 yoffset=3 xadvance=26 page=0 chnl=15 -char id=53665 x=784 y=242 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53668 x=1652 y=566 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53672 x=112 y=153 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53680 x=1708 y=566 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53681 x=0 y=597 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53683 x=1507 y=0 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53685 x=728 y=242 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53690 x=28 y=597 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53692 x=1595 y=450 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53696 x=844 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53720 x=486 y=712 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53748 x=702 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53752 x=728 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53767 x=860 y=31 width=26 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53769 x=234 y=362 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53776 x=323 y=974 width=27 height=23 xoffset=-1 yoffset=3 xadvance=26 page=0 chnl=15 -char id=53804 x=56 y=597 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53805 x=644 y=242 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53808 x=112 y=597 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53812 x=616 y=242 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53820 x=476 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53821 x=504 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53823 x=1479 y=0 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53825 x=560 y=242 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53832 x=806 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53852 x=1311 y=0 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53860 x=1515 y=683 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53888 x=832 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53889 x=260 y=362 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53892 x=975 y=683 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53896 x=286 y=362 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53904 x=884 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53905 x=910 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53909 x=312 y=362 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53916 x=756 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53920 x=952 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53924 x=980 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53932 x=1008 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53937 x=504 y=242 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53944 x=183 y=974 width=27 height=23 xoffset=-1 yoffset=4 xadvance=26 page=0 chnl=15 -char id=53945 x=476 y=242 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53948 x=1064 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53951 x=1092 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53952 x=448 y=242 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53954 x=420 y=242 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53960 x=1120 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53961 x=1148 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53963 x=1255 y=0 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53972 x=1040 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53976 x=1066 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53980 x=864 y=302 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53988 x=1118 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=53989 x=1144 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54000 x=744 y=944 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54001 x=768 y=944 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54004 x=975 y=915 width=24 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54008 x=1015 y=392 width=23 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54016 x=384 y=944 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54017 x=432 y=944 width=23 height=28 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54019 x=338 y=362 width=25 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54021 x=943 y=392 width=23 height=29 xoffset=1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54028 x=699 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54029 x=670 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54030 x=612 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54032 x=583 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54036 x=638 y=92 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54038 x=609 y=92 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54044 x=554 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54045 x=525 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54047 x=580 y=92 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54048 x=551 y=92 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54049 x=522 y=92 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54053 x=496 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54056 x=1316 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54057 x=1593 y=740 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54060 x=1344 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54064 x=196 y=242 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54072 x=1458 y=741 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54073 x=1431 y=741 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54075 x=168 y=243 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54076 x=1696 y=31 width=29 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54077 x=112 y=243 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54084 x=467 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54085 x=438 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54140 x=1352 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54141 x=1378 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54144 x=1215 y=741 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54148 x=364 y=362 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54156 x=1430 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54157 x=1456 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54159 x=84 y=243 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54160 x=435 y=92 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54161 x=416 y=362 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54168 x=1456 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54169 x=27 y=742 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54172 x=1484 y=596 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54176 x=1876 y=211 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54184 x=1890 y=710 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54185 x=1836 y=710 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54187 x=1848 y=211 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54189 x=1820 y=211 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54196 x=1560 y=828 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54200 x=1134 y=712 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54204 x=442 y=362 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54212 x=1612 y=827 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54213 x=1638 y=827 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54216 x=406 y=92 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54217 x=520 y=362 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54224 x=1596 y=595 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54232 x=1736 y=211 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54241 x=1080 y=712 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54243 x=1652 y=211 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54252 x=855 y=973 width=27 height=23 xoffset=-1 yoffset=4 xadvance=26 page=0 chnl=15 -char id=54253 x=1624 y=595 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54256 x=1652 y=595 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54260 x=1680 y=595 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54268 x=1708 y=595 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54269 x=1736 y=595 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54271 x=1624 y=211 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54273 x=1568 y=211 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54280 x=322 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54301 x=293 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54336 x=1794 y=827 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54340 x=1820 y=827 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54364 x=1023 y=973 width=27 height=23 xoffset=-1 yoffset=4 xadvance=26 page=0 chnl=15 -char id=54368 x=1792 y=594 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54372 x=1820 y=594 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54381 x=1848 y=594 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54383 x=1540 y=211 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54392 x=1876 y=594 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54393 x=0 y=626 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54396 x=28 y=626 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54399 x=56 y=626 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54400 x=112 y=626 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54402 x=1512 y=211 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54408 x=196 y=626 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54409 x=224 y=626 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54411 x=1456 y=211 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54413 x=1400 y=212 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54420 x=52 y=858 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54441 x=572 y=362 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54476 x=78 y=858 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54480 x=1758 y=682 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54484 x=598 y=362 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54492 x=104 y=858 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54495 x=1372 y=212 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54504 x=280 y=626 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54508 x=308 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54512 x=364 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54520 x=392 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54523 x=1344 y=212 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54525 x=1316 y=212 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54532 x=519 y=973 width=27 height=23 xoffset=-1 yoffset=4 xadvance=26 page=0 chnl=15 -char id=54536 x=448 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54540 x=924 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54548 x=1008 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54549 x=1036 y=625 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54551 x=1260 y=212 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54588 x=234 y=858 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54589 x=1574 y=886 width=24 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54592 x=260 y=858 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54596 x=624 y=362 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54604 x=1649 y=885 width=24 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54605 x=1674 y=885 width=24 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54607 x=1701 y=271 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54609 x=650 y=362 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54616 x=641 y=422 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54617 x=1064 y=625 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54620 x=1092 y=625 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54624 x=1232 y=212 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54629 x=1148 y=212 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54632 x=1176 y=625 width=27 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54633 x=1120 y=212 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54635 x=1064 y=212 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54637 x=1036 y=212 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54644 x=678 y=683 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54645 x=1431 y=272 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54648 x=624 y=683 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54652 x=1296 y=272 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54660 x=570 y=683 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54661 x=516 y=683 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54663 x=1269 y=272 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54664 x=492 y=0 width=28 height=30 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54665 x=1188 y=272 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54672 x=177 y=423 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54693 x=840 y=212 width=27 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54728 x=416 y=857 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54729 x=375 y=915 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54732 x=442 y=857 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54736 x=702 y=362 width=25 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54738 x=100 y=393 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54744 x=475 y=915 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54745 x=500 y=915 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54747 x=1107 y=272 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54749 x=728 y=362 width=25 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54756 x=54 y=771 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54757 x=81 y=771 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54760 x=108 y=771 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54764 x=1053 y=272 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54772 x=162 y=771 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54773 x=189 y=771 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54775 x=1026 y=272 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54777 x=999 y=272 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54784 x=650 y=857 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54785 x=750 y=915 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54788 x=676 y=857 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54792 x=754 y=362 width=25 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54800 x=825 y=915 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54801 x=850 y=915 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54803 x=972 y=272 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54804 x=1031 y=0 width=27 height=30 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54805 x=780 y=362 width=25 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54812 x=243 y=771 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54816 x=270 y=771 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54820 x=918 y=272 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54829 x=297 y=771 width=26 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54840 x=1432 y=944 width=27 height=25 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54841 x=756 y=212 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54844 x=1792 y=623 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54848 x=947 y=0 width=27 height=30 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=54853 x=807 y=0 width=27 height=30 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=54856 x=1820 y=623 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54857 x=616 y=212 width=27 height=29 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=54859 x=779 y=0 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54861 x=532 y=212 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54865 x=723 y=0 width=27 height=30 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=54868 x=90 y=423 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54869 x=87 y=93 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54872 x=580 y=451 width=28 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54876 x=957 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54887 x=260 y=0 width=28 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54889 x=1044 y=62 width=28 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54896 x=432 y=770 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54897 x=459 y=770 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54900 x=140 y=655 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54915 x=863 y=0 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54917 x=459 y=272 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54924 x=1300 y=857 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54925 x=806 y=362 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54928 x=1352 y=857 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54932 x=832 y=362 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54941 x=858 y=362 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54943 x=806 y=31 width=26 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54945 x=910 y=362 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54952 x=1460 y=944 width=27 height=25 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54956 x=336 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54960 x=975 y=0 width=27 height=30 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=54969 x=336 y=212 width=27 height=29 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=54971 x=1003 y=0 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54980 x=280 y=212 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54981 x=252 y=212 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54984 x=364 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54988 x=1087 y=0 width=27 height=30 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=54993 x=1115 y=0 width=27 height=30 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=54996 x=504 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=54999 x=1171 y=0 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55001 x=168 y=213 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55008 x=1716 y=856 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55012 x=936 y=362 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55016 x=968 y=31 width=25 height=30 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=55024 x=962 y=362 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55029 x=1124 y=31 width=25 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55036 x=837 y=770 width=26 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55037 x=1819 y=241 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55040 x=952 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55044 x=1339 y=0 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55057 x=833 y=31 width=26 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55064 x=1872 y=855 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55065 x=1014 y=362 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55068 x=1738 y=241 width=26 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55072 x=1176 y=31 width=25 height=30 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=55080 x=1040 y=362 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55081 x=1118 y=362 width=25 height=29 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=55083 x=1815 y=0 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55085 x=1150 y=31 width=25 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55092 x=140 y=213 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55093 x=112 y=213 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55096 x=1232 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55100 x=1871 y=0 width=27 height=30 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=55108 x=1288 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55111 x=28 y=32 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55113 x=28 y=213 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55120 x=0 y=974 width=27 height=24 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55121 x=1848 y=181 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55124 x=1540 y=654 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55126 x=112 y=32 width=27 height=30 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=55127 x=1596 y=653 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55128 x=196 y=31 width=27 height=30 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=55129 x=252 y=31 width=27 height=30 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=55136 x=1708 y=653 width=27 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55137 x=1820 y=181 width=27 height=29 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=55139 x=280 y=31 width=27 height=30 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55141 x=1792 y=181 width=27 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55145 x=308 y=31 width=27 height=30 xoffset=-1 yoffset=1 xadvance=26 page=0 chnl=15 -char id=55148 x=442 y=886 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55152 x=468 y=886 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55156 x=1144 y=362 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55164 x=520 y=886 width=25 height=28 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55165 x=1196 y=362 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55169 x=1222 y=362 width=25 height=29 xoffset=-1 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55176 x=675 y=915 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55177 x=425 y=392 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55180 x=572 y=886 width=25 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55184 x=475 y=392 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55192 x=800 y=915 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55193 x=1849 y=884 width=24 height=28 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55195 x=1036 y=242 width=26 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -char id=55197 x=350 y=392 width=24 height=29 xoffset=0 yoffset=2 xadvance=26 page=0 chnl=15 -kernings count=91 -kerning first=65 second=84 amount=-1 -kerning first=65 second=86 amount=-2 -kerning first=65 second=87 amount=-1 -kerning first=65 second=89 amount=-3 -kerning first=65 second=118 amount=-1 -kerning first=65 second=119 amount=-1 -kerning first=89 second=122 amount=-2 -kerning first=70 second=65 amount=-1 -kerning first=70 second=97 amount=-1 -kerning first=89 second=121 amount=-1 -kerning first=89 second=120 amount=-1 -kerning first=89 second=119 amount=-1 -kerning first=76 second=84 amount=-1 -kerning first=76 second=86 amount=-1 -kerning first=76 second=89 amount=-1 -kerning first=80 second=65 amount=-2 -kerning first=80 second=74 amount=-1 -kerning first=80 second=97 amount=-1 -kerning first=89 second=118 amount=-1 -kerning first=80 second=100 amount=-1 -kerning first=80 second=101 amount=-1 -kerning first=80 second=103 amount=-1 -kerning first=89 second=117 amount=-2 -kerning first=80 second=111 amount=-1 -kerning first=80 second=113 amount=-1 -kerning first=80 second=115 amount=-1 -kerning first=80 second=117 amount=-1 -kerning first=84 second=65 amount=-2 -kerning first=84 second=67 amount=-1 -kerning first=84 second=71 amount=-1 -kerning first=84 second=74 amount=-1 -kerning first=84 second=79 amount=-1 -kerning first=84 second=81 amount=-1 -kerning first=84 second=83 amount=-1 -kerning first=84 second=97 amount=-2 -kerning first=84 second=99 amount=-2 -kerning first=84 second=100 amount=-2 -kerning first=84 second=101 amount=-2 -kerning first=89 second=115 amount=-2 -kerning first=84 second=103 amount=-2 -kerning first=89 second=114 amount=-2 -kerning first=84 second=109 amount=-2 -kerning first=84 second=110 amount=-2 -kerning first=84 second=111 amount=-2 -kerning first=84 second=112 amount=-2 -kerning first=84 second=113 amount=-3 -kerning first=84 second=114 amount=-2 -kerning first=84 second=115 amount=-2 -kerning first=84 second=116 amount=-1 -kerning first=84 second=117 amount=-2 -kerning first=84 second=118 amount=-1 -kerning first=84 second=119 amount=-1 -kerning first=84 second=120 amount=-1 -kerning first=84 second=121 amount=-1 -kerning first=84 second=122 amount=-2 -kerning first=86 second=65 amount=-2 -kerning first=86 second=97 amount=-1 -kerning first=86 second=99 amount=-1 -kerning first=86 second=100 amount=-1 -kerning first=86 second=101 amount=-1 -kerning first=86 second=103 amount=-1 -kerning first=89 second=113 amount=-2 -kerning first=86 second=111 amount=-1 -kerning first=86 second=113 amount=-1 -kerning first=86 second=114 amount=-1 -kerning first=86 second=115 amount=-1 -kerning first=89 second=111 amount=-2 -kerning first=86 second=117 amount=-2 -kerning first=89 second=103 amount=-2 -kerning first=89 second=101 amount=-3 -kerning first=89 second=100 amount=-2 -kerning first=89 second=99 amount=-2 -kerning first=86 second=122 amount=-1 -kerning first=87 second=65 amount=-2 -kerning first=87 second=97 amount=-1 -kerning first=87 second=99 amount=-1 -kerning first=87 second=100 amount=-1 -kerning first=87 second=101 amount=-1 -kerning first=87 second=103 amount=-1 -kerning first=89 second=97 amount=-2 -kerning first=89 second=83 amount=-1 -kerning first=87 second=113 amount=-1 -kerning first=87 second=114 amount=-1 -kerning first=87 second=115 amount=-1 -kerning first=87 second=117 amount=-1 -kerning first=89 second=81 amount=-1 -kerning first=89 second=79 amount=-1 -kerning first=89 second=74 amount=-1 -kerning first=89 second=71 amount=-2 -kerning first=89 second=67 amount=-2 -kerning first=89 second=65 amount=-3 diff --git a/core/assets/ui/korean.png b/core/assets/ui/korean.png deleted file mode 100644 index 61fcba054b..0000000000 Binary files a/core/assets/ui/korean.png and /dev/null differ diff --git a/core/assets/ui/square.fnt b/core/assets/ui/square.fnt deleted file mode 100644 index 3ab33bd043..0000000000 --- a/core/assets/ui/square.fnt +++ /dev/null @@ -1,305 +0,0 @@ -info face="5squared pixel Regular" size=32 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=1,1,1,1 spacing=-2,-2 -common lineHeight=44 base=36 scaleW=512 scaleH=512 pages=1 packed=0 -page id=0 file="square.png" -chars count=297 -char id=0 x=238 y=72 width=22 height=22 xoffset=-1 yoffset=15 xadvance=16 page=0 chnl=0 -char id=32 x=0 y=0 width=0 height=0 xoffset=-1 yoffset=35 xadvance=8 page=0 chnl=0 -char id=33 x=498 y=38 width=6 height=22 xoffset=-1 yoffset=15 xadvance=8 page=0 chnl=0 -char id=34 x=136 y=146 width=14 height=10 xoffset=-1 yoffset=15 xadvance=16 page=0 chnl=0 -char id=35 x=260 y=72 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=36 x=352 y=38 width=22 height=30 xoffset=-1 yoffset=11 xadvance=24 page=0 chnl=0 -char id=37 x=282 y=72 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=38 x=304 y=72 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=39 x=150 y=146 width=6 height=10 xoffset=-1 yoffset=15 xadvance=8 page=0 chnl=0 -char id=40 x=326 y=72 width=10 height=22 xoffset=-1 yoffset=15 xadvance=12 page=0 chnl=0 -char id=41 x=336 y=72 width=10 height=22 xoffset=-1 yoffset=15 xadvance=12 page=0 chnl=0 -char id=42 x=346 y=72 width=22 height=22 xoffset=-1 yoffset=11 xadvance=24 page=0 chnl=0 -char id=43 x=368 y=72 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=44 x=150 y=146 width=6 height=10 xoffset=-1 yoffset=31 xadvance=8 page=0 chnl=0 -char id=45 x=156 y=146 width=22 height=6 xoffset=-1 yoffset=23 xadvance=24 page=0 chnl=0 -char id=46 x=178 y=146 width=6 height=6 xoffset=-1 yoffset=31 xadvance=8 page=0 chnl=0 -char id=47 x=390 y=72 width=14 height=22 xoffset=-1 yoffset=15 xadvance=16 page=0 chnl=0 -char id=48 x=404 y=72 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=49 x=426 y=72 width=14 height=22 xoffset=-1 yoffset=15 xadvance=16 page=0 chnl=0 -char id=50 x=440 y=72 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=51 x=462 y=72 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=52 x=484 y=72 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=53 x=0 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=54 x=22 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=55 x=44 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=56 x=66 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=57 x=88 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=58 x=108 y=146 width=6 height=14 xoffset=-1 yoffset=23 xadvance=8 page=0 chnl=0 -char id=59 x=504 y=38 width=6 height=18 xoffset=-1 yoffset=23 xadvance=8 page=0 chnl=0 -char id=60 x=110 y=102 width=14 height=22 xoffset=-1 yoffset=15 xadvance=16 page=0 chnl=0 -char id=61 x=114 y=146 width=22 height=14 xoffset=-1 yoffset=19 xadvance=24 page=0 chnl=0 -char id=62 x=124 y=102 width=14 height=22 xoffset=-1 yoffset=15 xadvance=16 page=0 chnl=0 -char id=63 x=138 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=64 x=160 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=65 x=182 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=66 x=204 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=67 x=226 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=68 x=248 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=69 x=270 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=70 x=292 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=71 x=314 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=72 x=336 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=73 x=358 y=102 width=14 height=22 xoffset=-1 yoffset=15 xadvance=16 page=0 chnl=0 -char id=74 x=372 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=75 x=394 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=76 x=416 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=77 x=438 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=78 x=460 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=79 x=482 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=80 x=0 y=124 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=81 x=216 y=72 width=22 height=26 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=82 x=22 y=124 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=83 x=44 y=124 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=84 x=66 y=124 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=85 x=88 y=124 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=86 x=110 y=124 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=87 x=132 y=124 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=88 x=154 y=124 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=89 x=176 y=124 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=90 x=198 y=124 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=91 x=220 y=124 width=10 height=22 xoffset=-1 yoffset=15 xadvance=12 page=0 chnl=0 -char id=92 x=230 y=124 width=14 height=22 xoffset=-1 yoffset=15 xadvance=16 page=0 chnl=0 -char id=93 x=244 y=124 width=10 height=22 xoffset=-1 yoffset=15 xadvance=12 page=0 chnl=0 -char id=97 x=182 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=98 x=204 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=99 x=226 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=100 x=248 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=101 x=270 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=1108 x=270 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=102 x=292 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=103 x=314 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=104 x=336 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=105 x=358 y=102 width=14 height=22 xoffset=-1 yoffset=15 xadvance=16 page=0 chnl=0 -char id=1110 x=358 y=102 width=14 height=22 xoffset=-1 yoffset=15 xadvance=16 page=0 chnl=0 -char id=1111 x=358 y=102 width=14 height=22 xoffset=-1 yoffset=15 xadvance=16 page=0 chnl=0 -char id=106 x=372 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=107 x=394 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=108 x=416 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=109 x=438 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=110 x=460 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=111 x=482 y=102 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=112 x=0 y=124 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=113 x=216 y=72 width=22 height=26 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=114 x=22 y=124 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=115 x=44 y=124 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=116 x=66 y=124 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=117 x=88 y=124 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=118 x=110 y=124 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=119 x=132 y=124 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=120 x=154 y=124 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=121 x=176 y=124 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=122 x=198 y=124 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=123 x=254 y=124 width=14 height=22 xoffset=-1 yoffset=15 xadvance=16 page=0 chnl=0 -char id=124 x=504 y=0 width=6 height=30 xoffset=-1 yoffset=11 xadvance=8 page=0 chnl=0 -char id=125 x=268 y=124 width=14 height=22 xoffset=-1 yoffset=15 xadvance=16 page=0 chnl=0 -char id=161 x=504 y=102 width=6 height=22 xoffset=-1 yoffset=15 xadvance=8 page=0 chnl=0 -char id=162 x=374 y=38 width=22 height=30 xoffset=-1 yoffset=11 xadvance=24 page=0 chnl=0 -char id=163 x=282 y=124 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=164 x=304 y=124 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=171 x=326 y=124 width=26 height=22 xoffset=-1 yoffset=15 xadvance=28 page=0 chnl=0 -char id=177 x=352 y=124 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=187 x=374 y=124 width=26 height=22 xoffset=-1 yoffset=15 xadvance=28 page=0 chnl=0 -char id=191 x=400 y=124 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=192 x=44 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=193 x=66 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=194 x=88 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=195 x=110 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=196 x=396 y=38 width=22 height=30 xoffset=-1 yoffset=7 xadvance=24 page=0 chnl=0 -char id=197 x=0 y=0 width=22 height=38 xoffset=-1 yoffset=-1 xadvance=24 page=0 chnl=0 -char id=198 x=422 y=124 width=38 height=22 xoffset=-1 yoffset=15 xadvance=40 page=0 chnl=0 -char id=199 x=418 y=38 width=22 height=30 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=200 x=132 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=201 x=154 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=202 x=176 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=203 x=440 y=38 width=22 height=30 xoffset=-1 yoffset=7 xadvance=24 page=0 chnl=0 -char id=204 x=198 y=0 width=14 height=34 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=0 -char id=205 x=212 y=0 width=14 height=34 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=0 -char id=206 x=226 y=0 width=14 height=34 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=0 -char id=207 x=462 y=38 width=14 height=30 xoffset=-1 yoffset=7 xadvance=16 page=0 chnl=0 -char id=208 x=460 y=124 width=26 height=22 xoffset=-1 yoffset=15 xadvance=28 page=0 chnl=0 -char id=209 x=240 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=210 x=262 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=211 x=284 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=212 x=306 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=213 x=328 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=214 x=476 y=38 width=22 height=30 xoffset=-1 yoffset=7 xadvance=24 page=0 chnl=0 -char id=216 x=404 y=72 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=217 x=350 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=218 x=372 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=219 x=394 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=220 x=0 y=72 width=22 height=30 xoffset=-1 yoffset=7 xadvance=24 page=0 chnl=0 -char id=221 x=416 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=222 x=486 y=124 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=223 x=0 y=146 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=224 x=44 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=225 x=66 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=226 x=88 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=227 x=110 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=228 x=396 y=38 width=22 height=30 xoffset=-1 yoffset=7 xadvance=24 page=0 chnl=0 -char id=229 x=0 y=0 width=22 height=38 xoffset=-1 yoffset=-1 xadvance=24 page=0 chnl=0 -char id=230 x=422 y=124 width=38 height=22 xoffset=-1 yoffset=15 xadvance=40 page=0 chnl=0 -char id=231 x=418 y=38 width=22 height=30 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=232 x=132 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=233 x=154 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=234 x=176 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=235 x=440 y=38 width=22 height=30 xoffset=-1 yoffset=7 xadvance=24 page=0 chnl=0 -char id=236 x=198 y=0 width=14 height=34 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=0 -char id=237 x=212 y=0 width=14 height=34 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=0 -char id=238 x=226 y=0 width=14 height=34 xoffset=-1 yoffset=3 xadvance=16 page=0 chnl=0 -char id=239 x=462 y=38 width=14 height=30 xoffset=-1 yoffset=7 xadvance=16 page=0 chnl=0 -char id=240 x=460 y=124 width=26 height=22 xoffset=-1 yoffset=15 xadvance=28 page=0 chnl=0 -char id=241 x=240 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=242 x=262 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=243 x=284 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=244 x=306 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=245 x=328 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=246 x=476 y=38 width=22 height=30 xoffset=-1 yoffset=7 xadvance=24 page=0 chnl=0 -char id=248 x=404 y=72 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=249 x=350 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=250 x=372 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=251 x=394 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=252 x=0 y=72 width=22 height=30 xoffset=-1 yoffset=7 xadvance=24 page=0 chnl=0 -char id=253 x=416 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=254 x=486 y=124 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=255 x=22 y=72 width=22 height=30 xoffset=-1 yoffset=7 xadvance=24 page=0 chnl=0 -char id=258 x=438 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=259 x=438 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=260 x=44 y=72 width=26 height=30 xoffset=-1 yoffset=15 xadvance=28 page=0 chnl=0 -char id=261 x=44 y=72 width=26 height=30 xoffset=-1 yoffset=15 xadvance=28 page=0 chnl=0 -char id=262 x=460 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=263 x=460 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=266 x=70 y=72 width=22 height=30 xoffset=-1 yoffset=7 xadvance=24 page=0 chnl=0 -char id=267 x=70 y=72 width=22 height=30 xoffset=-1 yoffset=7 xadvance=24 page=0 chnl=0 -char id=268 x=482 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=269 x=482 y=0 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=270 x=0 y=38 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=271 x=0 y=38 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=278 x=92 y=72 width=22 height=30 xoffset=-1 yoffset=7 xadvance=24 page=0 chnl=0 -char id=279 x=92 y=72 width=22 height=30 xoffset=-1 yoffset=7 xadvance=24 page=0 chnl=0 -char id=280 x=114 y=72 width=22 height=30 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=281 x=114 y=72 width=22 height=30 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=282 x=22 y=38 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=283 x=22 y=38 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=288 x=136 y=72 width=22 height=30 xoffset=-1 yoffset=7 xadvance=24 page=0 chnl=0 -char id=289 x=136 y=72 width=22 height=30 xoffset=-1 yoffset=7 xadvance=24 page=0 chnl=0 -char id=302 x=158 y=72 width=14 height=30 xoffset=-1 yoffset=15 xadvance=16 page=0 chnl=0 -char id=303 x=158 y=72 width=14 height=30 xoffset=-1 yoffset=15 xadvance=16 page=0 chnl=0 -char id=321 x=22 y=146 width=26 height=22 xoffset=-1 yoffset=15 xadvance=28 page=0 chnl=0 -char id=322 x=22 y=146 width=26 height=22 xoffset=-1 yoffset=15 xadvance=28 page=0 chnl=0 -char id=323 x=44 y=38 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=324 x=44 y=38 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=327 x=66 y=38 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=328 x=66 y=38 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=336 x=88 y=38 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=337 x=88 y=38 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=338 x=48 y=146 width=38 height=22 xoffset=-1 yoffset=15 xadvance=40 page=0 chnl=0 -char id=339 x=48 y=146 width=38 height=22 xoffset=-1 yoffset=15 xadvance=40 page=0 chnl=0 -char id=340 x=110 y=38 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=341 x=110 y=38 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=344 x=132 y=38 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=345 x=132 y=38 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=346 x=154 y=38 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=347 x=154 y=38 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=348 x=176 y=38 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=349 x=176 y=38 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=352 x=198 y=38 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=353 x=198 y=38 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=354 x=172 y=72 width=22 height=30 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=355 x=172 y=72 width=22 height=30 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=356 x=220 y=38 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=357 x=220 y=38 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=366 x=22 y=0 width=22 height=38 xoffset=-1 yoffset=-1 xadvance=24 page=0 chnl=0 -char id=367 x=22 y=0 width=22 height=38 xoffset=-1 yoffset=-1 xadvance=24 page=0 chnl=0 -char id=368 x=242 y=38 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=369 x=242 y=38 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=372 x=264 y=38 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=373 x=264 y=38 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=374 x=286 y=38 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=375 x=286 y=38 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=376 x=22 y=72 width=22 height=30 xoffset=-1 yoffset=7 xadvance=24 page=0 chnl=0 -char id=377 x=308 y=38 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=378 x=308 y=38 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=379 x=194 y=72 width=22 height=30 xoffset=-1 yoffset=7 xadvance=24 page=0 chnl=0 -char id=380 x=194 y=72 width=22 height=30 xoffset=-1 yoffset=7 xadvance=24 page=0 chnl=0 -char id=381 x=330 y=38 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=382 x=330 y=38 width=22 height=34 xoffset=-1 yoffset=3 xadvance=24 page=0 chnl=0 -char id=8216 x=150 y=146 width=6 height=10 xoffset=-1 yoffset=15 xadvance=8 page=0 chnl=0 -char id=8217 x=150 y=146 width=6 height=10 xoffset=-1 yoffset=15 xadvance=8 page=0 chnl=0 -char id=8220 x=136 y=146 width=14 height=10 xoffset=-1 yoffset=15 xadvance=16 page=0 chnl=0 -char id=8221 x=136 y=146 width=14 height=10 xoffset=-1 yoffset=15 xadvance=16 page=0 chnl=0 -char id=8249 x=110 y=102 width=14 height=22 xoffset=-1 yoffset=15 xadvance=16 page=0 chnl=0 -char id=8250 x=124 y=102 width=14 height=22 xoffset=-1 yoffset=15 xadvance=16 page=0 chnl=0 -char id=8364 x=86 y=146 width=22 height=22 xoffset=-1 yoffset=15 xadvance=24 page=0 chnl=0 -char id=1072 x=6 y=177 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1040 x=6 y=177 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1073 x=34 y=177 width=16 height=20 xoffset=0 yoffset=16 xadvance=20 page=0 chnl=0 -char id=1041 x=34 y=177 width=16 height=20 xoffset=0 yoffset=16 xadvance=20 page=0 chnl=0 -char id=1074 x=62 y=177 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1042 x=62 y=177 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1075 x=90 y=177 width=16 height=20 xoffset=0 yoffset=16 xadvance=20 page=0 chnl=0 -char id=1043 x=90 y=177 width=16 height=20 xoffset=0 yoffset=16 xadvance=20 page=0 chnl=0 -char id=1076 x=118 y=177 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1044 x=118 y=177 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1077 x=146 y=177 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1045 x=146 y=177 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1105 x=174 y=169 width=20 height=28 xoffset=0 yoffset=8 xadvance=24 page=0 chnl=0 -char id=1025 x=174 y=169 width=20 height=28 xoffset=0 yoffset=8 xadvance=24 page=0 chnl=0 -char id=1078 x=202 y=177 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1046 x=202 y=177 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1079 x=230 y=177 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1047 x=230 y=177 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1080 x=258 y=177 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1048 x=258 y=177 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1081 x=286 y=169 width=20 height=28 xoffset=0 yoffset=8 xadvance=24 page=0 chnl=0 -char id=1049 x=286 y=169 width=20 height=28 xoffset=0 yoffset=8 xadvance=24 page=0 chnl=0 -char id=1082 x=314 y=177 width=16 height=20 xoffset=0 yoffset=16 xadvance=20 page=0 chnl=0 -char id=1050 x=314 y=177 width=16 height=20 xoffset=0 yoffset=16 xadvance=20 page=0 chnl=0 -char id=1083 x=342 y=177 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1051 x=342 y=177 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1084 x=6 y=205 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1052 x=6 y=205 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1085 x=34 y=205 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1053 x=34 y=205 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1086 x=62 y=205 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1054 x=62 y=205 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1087 x=90 y=205 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1055 x=90 y=205 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1088 x=118 y=205 width=16 height=20 xoffset=0 yoffset=16 xadvance=20 page=0 chnl=0 -char id=1056 x=118 y=205 width=16 height=20 xoffset=0 yoffset=16 xadvance=20 page=0 chnl=0 -char id=1089 x=146 y=205 width=16 height=20 xoffset=0 yoffset=16 xadvance=20 page=0 chnl=0 -char id=1057 x=146 y=205 width=16 height=20 xoffset=0 yoffset=16 xadvance=20 page=0 chnl=0 -char id=1090 x=174 y=205 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1058 x=174 y=205 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1091 x=202 y=205 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1059 x=202 y=205 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1092 x=230 y=205 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1060 x=230 y=205 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1093 x=258 y=205 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1061 x=258 y=205 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1094 x=286 y=205 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1062 x=286 y=205 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1095 x=314 y=205 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1063 x=314 y=205 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1096 x=342 y=205 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1064 x=342 y=205 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1097 x=6 y=229 width=20 height=24 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1065 x=6 y=229 width=20 height=24 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1098 x=34 y=229 width=20 height=24 xoffset=0 yoffset=12 xadvance=24 page=0 chnl=0 -char id=1066 x=34 y=229 width=20 height=24 xoffset=0 yoffset=12 xadvance=24 page=0 chnl=0 -char id=1099 x=62 y=229 width=20 height=24 xoffset=0 yoffset=12 xadvance=24 page=0 chnl=0 -char id=1067 x=62 y=229 width=20 height=24 xoffset=0 yoffset=12 xadvance=24 page=0 chnl=0 -char id=1100 x=94 y=229 width=16 height=24 xoffset=0 yoffset=12 xadvance=20 page=0 chnl=0 -char id=1068 x=94 y=229 width=16 height=24 xoffset=0 yoffset=12 xadvance=20 page=0 chnl=0 -char id=1101 x=122 y=233 width=16 height=20 xoffset=0 yoffset=16 xadvance=20 page=0 chnl=0 -char id=1069 x=122 y=233 width=16 height=20 xoffset=0 yoffset=16 xadvance=20 page=0 chnl=0 -char id=1102 x=146 y=233 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1070 x=146 y=233 width=20 height=20 xoffset=0 yoffset=16 xadvance=24 page=0 chnl=0 -char id=1103 x=178 y=233 width=16 height=20 xoffset=0 yoffset=16 xadvance=20 page=0 chnl=0 -char id=1071 x=178 y=233 width=16 height=20 xoffset=0 yoffset=16 xadvance=20 page=0 chnl=0 -char id=95 x=343 y=153 width=16 height=4 xoffset=0 yoffset=36 xadvance=20 page=0 chnl=0 -kernings count=0 diff --git a/core/assets/ui/square.png b/core/assets/ui/square.png deleted file mode 100644 index 38c5e6ce88..0000000000 Binary files a/core/assets/ui/square.png and /dev/null differ diff --git a/core/assets/ui/title.fnt b/core/assets/ui/title.fnt deleted file mode 100644 index ef571ca7e7..0000000000 --- a/core/assets/ui/title.fnt +++ /dev/null @@ -1,57 +0,0 @@ -info face="Title" size=16 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=0,0 -common lineHeight=14 base=0 scaleW=512 scaleH=512 pages=1 packed=0 -page id=0 file="title.png" -chars count=53 -char id=32 x=0 y=0 width=0 height=0 xoffset=0 yoffset=0 xadvance=3 page=0 chnl=0 -char id=97 x=1 y=0 width=9 height=12 xoffset=0 yoffset=-12 xadvance=8 page=0 chnl=0 -char id=65 x=1 y=0 width=9 height=12 xoffset=0 yoffset=-12 xadvance=8 page=0 chnl=0 -char id=98 x=14 y=0 width=8 height=12 xoffset=0 yoffset=-12 xadvance=7 page=0 chnl=0 -char id=66 x=14 y=0 width=8 height=12 xoffset=0 yoffset=-12 xadvance=7 page=0 chnl=0 -char id=99 x=26 y=0 width=7 height=12 xoffset=0 yoffset=-12 xadvance=6 page=0 chnl=0 -char id=67 x=26 y=0 width=7 height=12 xoffset=0 yoffset=-12 xadvance=6 page=0 chnl=0 -char id=100 x=39 y=0 width=8 height=12 xoffset=0 yoffset=-12 xadvance=7 page=0 chnl=0 -char id=68 x=39 y=0 width=8 height=12 xoffset=0 yoffset=-12 xadvance=7 page=0 chnl=0 -char id=101 x=50 y=0 width=7 height=12 xoffset=0 yoffset=-12 xadvance=6 page=0 chnl=0 -char id=69 x=50 y=0 width=7 height=12 xoffset=0 yoffset=-12 xadvance=6 page=0 chnl=0 -char id=102 x=63 y=0 width=7 height=12 xoffset=0 yoffset=-12 xadvance=6 page=0 chnl=0 -char id=70 x=63 y=0 width=7 height=12 xoffset=0 yoffset=-12 xadvance=6 page=0 chnl=0 -char id=103 x=74 y=0 width=8 height=12 xoffset=0 yoffset=-12 xadvance=7 page=0 chnl=0 -char id=71 x=74 y=0 width=8 height=12 xoffset=0 yoffset=-12 xadvance=7 page=0 chnl=0 -char id=104 x=86 y=0 width=8 height=12 xoffset=0 yoffset=-12 xadvance=7 page=0 chnl=0 -char id=72 x=86 y=0 width=8 height=12 xoffset=0 yoffset=-12 xadvance=7 page=0 chnl=0 -char id=105 x=100 y=0 width=4 height=12 xoffset=0 yoffset=-12 xadvance=3 page=0 chnl=0 -char id=73 x=100 y=0 width=4 height=12 xoffset=0 yoffset=-12 xadvance=3 page=0 chnl=0 -char id=106 x=2 y=12 width=8 height=12 xoffset=0 yoffset=-12 xadvance=7 page=0 chnl=0 -char id=74 x=2 y=12 width=8 height=12 xoffset=0 yoffset=-12 xadvance=7 page=0 chnl=0 -char id=107 x=14 y=12 width=8 height=12 xoffset=0 yoffset=-12 xadvance=7 page=0 chnl=0 -char id=75 x=14 y=12 width=8 height=12 xoffset=0 yoffset=-12 xadvance=7 page=0 chnl=0 -char id=108 x=28 y=12 width=7 height=12 xoffset=0 yoffset=-12 xadvance=6 page=0 chnl=0 -char id=76 x=28 y=12 width=7 height=12 xoffset=0 yoffset=-12 xadvance=6 page=0 chnl=0 -char id=109 x=38 y=12 width=10 height=12 xoffset=0 yoffset=-12 xadvance=9 page=0 chnl=0 -char id=77 x=38 y=12 width=10 height=12 xoffset=0 yoffset=-12 xadvance=9 page=0 chnl=0 -char id=110 x=50 y=12 width=9 height=12 xoffset=0 yoffset=-12 xadvance=8 page=0 chnl=0 -char id=78 x=50 y=12 width=9 height=12 xoffset=0 yoffset=-12 xadvance=8 page=0 chnl=0 -char id=111 x=63 y=12 width=8 height=12 xoffset=0 yoffset=-12 xadvance=7 page=0 chnl=0 -char id=79 x=63 y=12 width=8 height=12 xoffset=0 yoffset=-12 xadvance=7 page=0 chnl=0 -char id=112 x=75 y=12 width=8 height=12 xoffset=0 yoffset=-12 xadvance=7 page=0 chnl=0 -char id=80 x=75 y=12 width=8 height=12 xoffset=0 yoffset=-12 xadvance=7 page=0 chnl=0 -char id=113 x=87 y=12 width=9 height=12 xoffset=0 yoffset=-12 xadvance=8 page=0 chnl=0 -char id=81 x=87 y=12 width=9 height=12 xoffset=0 yoffset=-12 xadvance=8 page=0 chnl=0 -char id=114 x=99 y=12 width=8 height=12 xoffset=0 yoffset=-12 xadvance=7 page=0 chnl=0 -char id=82 x=99 y=12 width=8 height=12 xoffset=0 yoffset=-12 xadvance=7 page=0 chnl=0 -char id=115 x=2 y=24 width=8 height=12 xoffset=0 yoffset=-12 xadvance=7 page=0 chnl=0 -char id=83 x=2 y=24 width=8 height=12 xoffset=0 yoffset=-12 xadvance=7 page=0 chnl=0 -char id=116 x=14 y=24 width=8 height=12 xoffset=0 yoffset=-12 xadvance=7 page=0 chnl=0 -char id=84 x=14 y=24 width=8 height=12 xoffset=0 yoffset=-12 xadvance=7 page=0 chnl=0 -char id=117 x=26 y=24 width=8 height=12 xoffset=0 yoffset=-12 xadvance=7 page=0 chnl=0 -char id=85 x=26 y=24 width=8 height=12 xoffset=0 yoffset=-12 xadvance=7 page=0 chnl=0 -char id=118 x=37 y=24 width=9 height=12 xoffset=0 yoffset=-12 xadvance=8 page=0 chnl=0 -char id=86 x=37 y=24 width=9 height=12 xoffset=0 yoffset=-12 xadvance=8 page=0 chnl=0 -char id=119 x=50 y=24 width=10 height=12 xoffset=0 yoffset=-12 xadvance=9 page=0 chnl=0 -char id=87 x=50 y=24 width=10 height=12 xoffset=0 yoffset=-12 xadvance=9 page=0 chnl=0 -char id=120 x=62 y=24 width=8 height=12 xoffset=0 yoffset=-12 xadvance=7 page=0 chnl=0 -char id=88 x=62 y=24 width=8 height=12 xoffset=0 yoffset=-12 xadvance=7 page=0 chnl=0 -char id=121 x=75 y=24 width=8 height=12 xoffset=0 yoffset=-12 xadvance=7 page=0 chnl=0 -char id=89 x=75 y=24 width=8 height=12 xoffset=0 yoffset=-12 xadvance=7 page=0 chnl=0 -char id=122 x=86 y=24 width=8 height=12 xoffset=0 yoffset=-12 xadvance=7 page=0 chnl=0 -char id=90 x=86 y=24 width=8 height=12 xoffset=0 yoffset=-12 xadvance=7 page=0 chnl=0 diff --git a/core/assets/ui/title.png b/core/assets/ui/title.png deleted file mode 100644 index 0141415b7c..0000000000 Binary files a/core/assets/ui/title.png and /dev/null differ diff --git a/core/assets/ui/uiskin.atlas b/core/assets/ui/uiskin.atlas deleted file mode 100644 index 9926e77897..0000000000 --- a/core/assets/ui/uiskin.atlas +++ /dev/null @@ -1,770 +0,0 @@ - -uiskin.png -size: 512,128 -format: RGBA8888 -filter: Nearest,Nearest -repeat: none -blank - rotate: false - xy: 202, 14 - size: 8, 8 - orig: 8, 8 - offset: 0, 0 - index: -1 -border - rotate: false - xy: 360, 35 - size: 12, 12 - split: 4, 4, 4, 4 - orig: 12, 12 - offset: 0, 0 - index: -1 -border-circle - rotate: false - xy: 418, 35 - size: 28, 28 - orig: 28, 28 - offset: 0, 0 - index: -1 -border-circle-error - rotate: false - xy: 82, 10 - size: 28, 28 - orig: 28, 28 - offset: 0, 0 - index: -1 -border-dark-blue - rotate: false - xy: 500, 42 - size: 3, 3 - split: 1, 1, 1, 1 - orig: 3, 3 - offset: 0, 0 - index: -1 -border-error - rotate: false - xy: 37, 7 - size: 3, 3 - split: 1, 1, 1, 1 - orig: 3, 3 - offset: 0, 0 - index: -1 -border-white - rotate: false - xy: 58, 2 - size: 12, 12 - split: 4, 4, 4, 4 - orig: 12, 12 - offset: 0, 0 - index: -1 -button - rotate: false - xy: 212, 2 - size: 24, 40 - split: 10, 10, 6, 10 - orig: 24, 40 - offset: 0, 0 - index: -1 -button-blue - rotate: false - xy: 82, 40 - size: 24, 40 - split: 10, 10, 10, 8 - pad: 8, 8, 2, 2 - orig: 24, 40 - offset: 0, 0 - index: -1 -button-blue-down - rotate: false - xy: 448, 40 - size: 24, 40 - split: 10, 10, 10, 8 - pad: 8, 8, 2, 2 - orig: 24, 40 - offset: 0, 0 - index: -1 -button-blue-over - rotate: false - xy: 474, 40 - size: 24, 40 - split: 10, 10, 10, 8 - pad: 8, 8, 2, 2 - orig: 24, 40 - offset: 0, 0 - index: -1 -button-down - rotate: false - xy: 108, 40 - size: 24, 40 - split: 10, 10, 6, 10 - orig: 24, 40 - offset: 0, 0 - index: -1 -button-over - rotate: false - xy: 108, 40 - size: 24, 40 - split: 10, 10, 6, 10 - orig: 24, 40 - offset: 0, 0 - index: -1 -button-gray - rotate: false - xy: 160, 40 - size: 24, 40 - split: 10, 10, 10, 8 - pad: 8, 8, 2, 2 - orig: 24, 40 - offset: 0, 0 - index: -1 -button-gray-over - rotate: false - xy: 134, 40 - size: 24, 40 - split: 10, 10, 10, 8 - pad: 8, 8, 2, 2 - orig: 24, 40 - offset: 0, 0 - index: -1 -button-map - rotate: false - xy: 212, 44 - size: 24, 40 - split: 10, 10, 5, 10 - orig: 24, 40 - offset: 0, 0 - index: -1 -button-map-down - rotate: false - xy: 186, 40 - size: 24, 40 - split: 10, 10, 5, 10 - orig: 24, 40 - offset: 0, 0 - index: -1 -button-map-over - rotate: false - xy: 186, 40 - size: 24, 40 - split: 10, 10, 5, 10 - orig: 24, 40 - offset: 0, 0 - index: -1 -button-red - rotate: false - xy: 238, 44 - size: 24, 40 - split: 10, 10, 10, 8 - pad: 8, 8, 2, 2 - orig: 24, 40 - offset: 0, 0 - index: -1 -button-select - rotate: false - xy: 330, 39 - size: 24, 24 - split: 4, 4, 4, 4 - orig: 24, 24 - offset: 0, 0 - index: -1 -button-window-bg - rotate: false - xy: 264, 44 - size: 24, 40 - split: 10, 10, 10, 8 - pad: 8, 8, 2, 2 - orig: 24, 40 - offset: 0, 0 - index: -1 -button-window-over - rotate: false - xy: 290, 44 - size: 24, 40 - split: 10, 10, 10, 8 - pad: 8, 8, 2, 2 - orig: 24, 40 - offset: 0, 0 - index: -1 -check-off - rotate: false - xy: 474, 6 - size: 28, 32 - orig: 28, 32 - offset: 0, 0 - index: -1 -check-on - rotate: false - xy: 238, 10 - size: 28, 32 - orig: 28, 32 - offset: 0, 0 - index: -1 -check-over - rotate: false - xy: 268, 10 - size: 28, 32 - orig: 28, 32 - offset: 0, 0 - index: -1 -clear - rotate: false - xy: 500, 50 - size: 10, 10 - orig: 10, 10 - offset: 0, 0 - index: -1 -color-picker-bar-selector - rotate: false - xy: 495, 98 - size: 14, 28 - orig: 14, 28 - offset: 0, 0 - index: -1 -color-picker-cross - rotate: false - xy: 360, 23 - size: 10, 10 - orig: 10, 10 - offset: 0, 0 - index: -1 -color-picker-selector-horizontal - rotate: false - xy: 495, 95 - size: 6, 1 - orig: 6, 1 - offset: 0, 0 - index: -1 -color-picker-selector-vertical - rotate: false - xy: 72, 2 - size: 1, 6 - orig: 1, 6 - offset: 0, 0 - index: -1 -cursor - rotate: false - xy: 316, 44 - size: 4, 4 - orig: 4, 4 - offset: 0, 0 - index: -1 -cursor-normal - rotate: false - xy: 374, 37 - size: 10, 10 - orig: 10, 10 - offset: 0, 0 - index: -1 -default-pane - rotate: false - xy: 389, 37 - size: 5, 3 - split: 1, 1, 1, 1 - orig: 5, 3 - offset: 0, 0 - index: -1 -default-pane-no-border - rotate: false - xy: 75, 2 - size: 1, 1 - split: 0, 0, 0, 0 - orig: 1, 1 - offset: 0, 0 - index: -1 -default-select - rotate: false - xy: 2, 12 - size: 54, 48 - split: 8, 32, 0, 48 - orig: 54, 48 - offset: 0, 0 - index: -1 -default-select-selection - rotate: false - xy: 37, 2 - size: 3, 3 - split: 1, 1, 1, 1 - orig: 3, 3 - offset: 0, 0 - index: -1 -grey - rotate: false - xy: 78, 2 - size: 1, 1 - orig: 1, 1 - offset: 0, 0 - index: -1 -menu-bg - rotate: false - xy: 78, 2 - size: 1, 1 - orig: 1, 1 - offset: 0, 0 - index: -1 -icon-cancel - rotate: false - xy: 356, 49 - size: 14, 14 - orig: 14, 14 - offset: 0, 0 - index: -1 -icon-check - rotate: false - xy: 372, 49 - size: 14, 14 - orig: 14, 14 - offset: 0, 0 - index: -1 -icon-close - rotate: false - xy: 205, 86 - size: 40, 40 - orig: 40, 40 - offset: 0, 0 - index: -1 -icon-close-down - rotate: false - xy: 247, 86 - size: 40, 40 - orig: 40, 40 - offset: 0, 0 - index: -1 -icon-close-over - rotate: false - xy: 289, 86 - size: 40, 40 - orig: 40, 40 - offset: 0, 0 - index: -1 -icon-cursor - rotate: false - xy: 332, 11 - size: 10, 10 - orig: 10, 10 - offset: 0, 0 - index: -1 -icon-defense - rotate: false - xy: 344, 11 - size: 10, 10 - orig: 10, 10 - offset: 0, 0 - index: -1 -icon-distribution - rotate: false - xy: 356, 11 - size: 10, 10 - orig: 10, 10 - offset: 0, 0 - index: -1 -icon-menu - rotate: false - xy: 368, 11 - size: 10, 10 - orig: 10, 10 - offset: 0, 0 - index: -1 -icon-pause - rotate: false - xy: 372, 23 - size: 10, 10 - orig: 10, 10 - offset: 0, 0 - index: -1 -icon-play - rotate: false - xy: 380, 11 - size: 10, 10 - orig: 10, 10 - offset: 0, 0 - index: -1 -icon-production - rotate: false - xy: 384, 23 - size: 10, 10 - orig: 10, 10 - offset: 0, 0 - index: -1 -icon-rotate - rotate: false - xy: 328, 23 - size: 14, 14 - orig: 14, 14 - offset: 0, 0 - index: -1 -icon-rotate-arrow - rotate: false - xy: 344, 23 - size: 14, 14 - orig: 14, 14 - offset: 0, 0 - index: -1 -icon-settings - rotate: false - xy: 396, 30 - size: 10, 10 - orig: 10, 10 - offset: 0, 0 - index: -1 -icon-touch - rotate: false - xy: 396, 18 - size: 10, 10 - orig: 10, 10 - offset: 0, 0 - index: -1 -list-selection - rotate: false - xy: 386, 36 - size: 1, 1 - orig: 1, 1 - offset: 0, 0 - index: -1 -logotext - rotate: false - xy: 68, 105 - size: 89, 21 - orig: 89, 21 - offset: 0, 0 - index: -1 -logotext-gray - rotate: false - xy: 68, 82 - size: 89, 21 - orig: 89, 21 - offset: 0, 0 - index: -1 -padded-list-selection - rotate: false - xy: 500, 47 - size: 10, 1 - split: 4, 4, 0, 1 - orig: 10, 1 - offset: 0, 0 - index: -1 -pane - rotate: false - xy: 448, 2 - size: 24, 36 - split: 10, 10, 5, 5 - orig: 24, 36 - offset: 0, 0 - index: -1 -progressbar - rotate: false - xy: 324, 52 - size: 1, 32 - orig: 1, 32 - offset: 0, 0 - index: -1 -progressbar-filled - rotate: false - xy: 327, 52 - size: 1, 32 - orig: 1, 32 - offset: 0, 0 - index: -1 -progressbar-filled-vertical - rotate: false - xy: 474, 3 - size: 32, 1 - orig: 32, 1 - offset: 0, 0 - index: -1 -progressbar-vertical - rotate: false - xy: 298, 11 - size: 32, 1 - orig: 32, 1 - offset: 0, 0 - index: -1 -radio-off - rotate: false - xy: 112, 10 - size: 28, 28 - orig: 28, 28 - offset: 0, 0 - index: -1 -radio-on - rotate: false - xy: 142, 10 - size: 28, 28 - orig: 28, 28 - offset: 0, 0 - index: -1 -scroll - rotate: false - xy: 274, 2 - size: 34, 6 - split: 4, 4, 2, 2 - orig: 34, 6 - offset: 0, 0 - index: -1 -scroll-horizontal - rotate: false - xy: 316, 50 - size: 6, 34 - split: 2, 2, 0, 34 - pad: 0, 5, 5, 4 - orig: 6, 34 - offset: 0, 0 - index: -1 -scroll-knob-horizontal - rotate: false - xy: 504, 62 - size: 6, 34 - split: 2, 2, 0, 34 - pad: 0, 5, 13, 12 - orig: 6, 34 - offset: 0, 0 - index: -1 -scroll-knob-vertical - rotate: false - xy: 238, 2 - size: 34, 6 - split: 12, 12, 2, 2 - orig: 34, 6 - offset: 0, 0 - index: -1 -select-box-list-bg - rotate: false - xy: 205, 83 - size: 1, 1 - orig: 1, 1 - offset: 0, 0 - index: -1 -window-bg - rotate: false - xy: 205, 83 - size: 1, 1 - orig: 1, 1 - offset: 0, 0 - index: -1 -select-down - rotate: false - xy: 2, 2 - size: 14, 8 - orig: 14, 8 - offset: 0, 0 - index: -1 -select-up - rotate: false - xy: 18, 2 - size: 14, 8 - orig: 14, 8 - offset: 0, 0 - index: -1 -selection - rotate: false - xy: 328, 20 - size: 1, 1 - orig: 1, 1 - offset: 0, 0 - index: -1 -separator - rotate: false - xy: 404, 15 - size: 1, 1 - orig: 1, 1 - offset: 0, 0 - index: -1 -tree-over - rotate: false - xy: 404, 15 - size: 1, 1 - orig: 1, 1 - offset: 0, 0 - index: -1 -separator-menu - rotate: false - xy: 392, 20 - size: 1, 1 - orig: 1, 1 - offset: 0, 0 - index: -1 -slider - rotate: false - xy: 386, 39 - size: 1, 8 - orig: 1, 8 - offset: 0, 0 - index: -1 -slider-knob - rotate: false - xy: 447, 82 - size: 22, 44 - orig: 22, 44 - offset: 0, 0 - index: -1 -slider-knob-disabled - rotate: false - xy: 471, 82 - size: 22, 44 - orig: 22, 44 - offset: 0, 0 - index: -1 -slider-knob-down - rotate: false - xy: 58, 16 - size: 22, 44 - orig: 22, 44 - offset: 0, 0 - index: -1 -slider-knob-over - rotate: false - xy: 58, 16 - size: 22, 44 - orig: 22, 44 - offset: 0, 0 - index: -1 -slider-vertical - rotate: false - xy: 72, 13 - size: 8, 1 - orig: 8, 1 - offset: 0, 0 - index: -1 -slot - rotate: false - xy: 2, 62 - size: 64, 64 - split: 4, 8, 8, 4 - orig: 64, 64 - offset: 0, 0 - index: -1 -splitpane - rotate: false - xy: 202, 11 - size: 8, 1 - orig: 8, 1 - offset: 0, 0 - index: -1 -splitpane-over - rotate: false - xy: 72, 10 - size: 8, 1 - orig: 8, 1 - offset: 0, 0 - index: -1 -splitpane-vertical - rotate: false - xy: 34, 2 - size: 1, 8 - orig: 1, 8 - offset: 0, 0 - index: -1 -splitpane-vertical-over - rotate: false - xy: 356, 39 - size: 1, 8 - orig: 1, 8 - offset: 0, 0 - index: -1 -sub-menu - rotate: false - xy: 202, 24 - size: 8, 14 - orig: 8, 14 - offset: 0, 0 - index: -1 -textfield - rotate: false - xy: 298, 14 - size: 28, 28 - split: 6, 6, 6, 6 - orig: 28, 28 - offset: 0, 0 - index: -1 -textfield-over - rotate: false - xy: 172, 10 - size: 28, 28 - split: 2, 2, 2, 2 - orig: 28, 28 - offset: 0, 0 - index: -1 -tooltip-bg - rotate: false - xy: 408, 37 - size: 3, 3 - split: 1, 1, 1, 1 - orig: 3, 3 - offset: 0, 0 - index: -1 -touchpad-knob - rotate: false - xy: 159, 82 - size: 44, 44 - orig: 44, 44 - offset: 0, 0 - index: -1 -tree-minus - rotate: false - xy: 392, 6 - size: 10, 10 - orig: 10, 10 - offset: 0, 0 - index: -1 -tree-plus - rotate: false - xy: 68, 64 - size: 12, 16 - orig: 12, 16 - offset: 0, 0 - index: -1 -tree-selection - rotate: false - xy: 413, 37 - size: 3, 3 - split: 1, 1, 1, 1 - orig: 3, 3 - offset: 0, 0 - index: -1 -white - rotate: false - xy: 75, 5 - size: 3, 3 - orig: 3, 3 - offset: 0, 0 - index: -1 -window - rotate: false - xy: 418, 65 - size: 27, 61 - split: 8, 8, 44, 11 - orig: 27, 61 - offset: 0, 0 - index: -1 -window-border-bg - rotate: false - xy: 495, 90 - size: 3, 3 - split: 1, 1, 1, 1 - orig: 3, 3 - offset: 0, 0 - index: -1 -window-gray - rotate: false - xy: 331, 65 - size: 27, 61 - split: 5, 4, 52, 4 - orig: 27, 61 - offset: 0, 0 - index: -1 -window-noborder - rotate: false - xy: 360, 65 - size: 27, 61 - split: 5, 4, 53, 3 - orig: 27, 61 - offset: 0, 0 - index: -1 -window-resizable - rotate: false - xy: 389, 42 - size: 27, 84 - split: 3, 19, 2, 20 - pad: 5, 5, 50, 7 - orig: 27, 84 - offset: 0, 0 - index: -1 diff --git a/core/assets/ui/uiskin.json b/core/assets/ui/uiskin.json deleted file mode 100644 index 6da4b2bc62..0000000000 --- a/core/assets/ui/uiskin.json +++ /dev/null @@ -1,109 +0,0 @@ -{ -com.badlogic.gdx.graphics.g2d.BitmapFont: { - default-font: { - file: square.fnt, - markupEnabled: true, - scale: 0.5 - }, - default-font-chat: { - file: square.fnt, - markupEnabled: false, - scale: 0.5 - }, - korean: { - file: korean.fnt, - markupEnabled: true, - scale: 0.5 - }, - title: { - file: title.fnt, - markupEnabled: true, - scale: 2 - } -}, -com.badlogic.gdx.graphics.Color: { - black: {a: 1, b: 0, g: 0, r: 0 }, - white: {a: 1, b: 1, g: 1, r: 1 }, - green: {a: 1, b: 0, g: 1, r: 0 }, - red: {a: 1, b: 0, g: 0, r: 1 }, - blue: {a: 1, b: 1, g: 0, r: 0 }, - grey: {a: 1, b: 0.32, g: 0.32, r: 0.32 }, - lightgray: {a: 1, b: 0.3, g: 0.3, r: 0.3 }, - orange: {hex: "#FFA500"}, - accent: {hex: "f4ba6e"}, - vis-blue: {a: 1, b: 0.886, g: 0.631, r: 0.105 }, - vis-red: {a: 1, b: 0.047, g: 0, r: 0.862 }, - menuitem: {a: 1, b: 0.65, g: 0.65, r: 0.65 }, - link-label: {a: 1, b: 0.886, g: 0.631, r: 0.105 } -}, -io.anuke.ucore.scene.Skin$TintedDrawable: { - dialogDim: {name: white, color: {r: 0, g: 0, b: 0, a: 0.9} }, - invis: {name: white, color: {r: 0, g: 0, b: 0, a: 0} } - loadDim: {name: white, color: {r: 0, g: 0, b: 0, a: 0.8} }, - chatfield: {name: white, color: {r: 0, g: 0, b: 0, a: 0.2}}, - clear: {name: white, color: {r: 0.1, g: 0.1, b: 0.1, a: 0.75}}, - clear-over: {name: white, color: {r: 1, g: 1, b: 1, a: 0.2} }, - clear-down: {name: white, color: {r: 1, g: 1, b: 1, a: 0.4} } -}, -io.anuke.ucore.scene.ui.Button$ButtonStyle: { - default: {down: button-down, up: button }, - menu: {up: text-sides, over: text-sides-over, down: text-sides-down}, - toggle: {checked: button-down, down: button-down, up: button } -}, -io.anuke.ucore.scene.ui.TextButton$TextButtonStyle: { - default: {over: button-over, disabled: button, font: default-font, fontColor: white, disabledFontColor: grey, down: button-down, up: button, transition: 0 }, - discord: {over: discord-banner-over, font: default-font, fontColor: white, up: discord-banner}, - clear: {down: clear-down, up: clear, over: clear-over, font: default-font, fontColor: white, disabledFontColor: grey }, - empty: {font: default-font}, - toggle: {font: default-font, fontColor: white, checked: button-down, down: button-down, up: button, over: button-over, disabled: button, disabledFontColor: grey } -}, -io.anuke.ucore.scene.ui.ImageButton$ImageButtonStyle: { - default: {down: button-down, up: button, over: button-over, imageDisabledColor: lightgray, imageUpColor: white }, - empty: { imageDownColor: accent, imageUpColor: white}, - emptytoggle: {imageCheckedColor: white, imageDownColor: white, imageUpColor: lightgray}, - static: {up: button }, - static-down: {up: button-down }, - toggle: {checked: button-down, down: button-down, up: button, imageDisabledColor: lightgray, imageUpColor: white }, - togglemap: {down: button-map-down, up: button-map }, - select: {checked: button-select, up: clear }, - close-window: {up: button, imageUp: icon-close, imageOver: icon-close-over, imageDown: icon-close-down, disabled: button } -}, -io.anuke.ucore.scene.ui.ImageTextButton$ImageTextButtonStyle: { - default: {down: button-down, up: button, over: button-over, disabled: button, font: default-font, fontColor: white, disabledFontColor: grey }, - toggle: {checked: button-down, down: button-down, up: button, font: default-font, fontColor: white, over: button-over, disabled: button, disabledFontColor: grey } -}, -io.anuke.ucore.scene.ui.ScrollPane$ScrollPaneStyle: { - default: {background: border, vScroll: scroll, vScrollKnob: scroll-knob-vertical}, - horizontal: {background: border, vScroll: scroll, vScrollKnob: scroll-knob-vertical, hScroll: scroll-horizontal, hScrollKnob: scroll-knob-horizontal}, - volume: {background: button-map, vScroll: scroll, vScrollKnob: scroll-knob-vertical}, - clear: {vScroll: scroll, vScrollKnob: scroll-knob-vertical}, - clear-black: {vScroll: scroll, vScrollKnob: scroll-knob-vertical-black} -}, -io.anuke.ucore.scene.ui.Window$WindowStyle: { - default: {titleFont: default-font, background: window, titleFontColor: accent }, - dialog: {stageBackground: dialogDim, titleFont: default-font, background: window-empty, titleFontColor: accent } -}, -io.anuke.ucore.scene.ui.KeybindDialog$KeybindDialogStyle: { - default: {keyColor: accent, keyNameColor: white, controllerColor: menuitem, paneStyle: clear}, -}, -io.anuke.ucore.scene.ui.Slider$SliderStyle: { - default-horizontal: {background: slider, knob: slider-knob, knobOver: slider-knob-over, knobDown: slider-knob-down}, - default-vertical: {background: slider-vertical, knob: slider-knob, knobOver: slider-knob-over, knobDown: slider-knob-down} -}, -io.anuke.ucore.scene.ui.Label$LabelStyle: { - default: {font: default-font, fontColor: white }, - title: {font: title, fontColor: white }, - link-label: {fontColor: link-label, font: default-font }, - small: {font: default-font, fontColor: white }, - menuitem-shortcut: {font: default-font, fontColor: menuitem } -}, -io.anuke.ucore.scene.ui.TextField$TextFieldStyle: { - default: {font: default-font-chat, fontColor: white, disabledFontColor: grey, selection: selection, background: button, cursor: cursor, messageFont: default-font, messageFontColor: grey } -} -io.anuke.ucore.scene.ui.CheckBox$CheckBoxStyle: { - default: {checkboxOn: check-on, checkboxOff: check-off, checkboxOnOver: check-on-over, checkboxOver: check-over, font: default-font, fontColor: white, disabledFontColor: grey } -}, -io.anuke.ucore.scene.ui.List$ListStyle: { - default: {fontColorUnselected: white, fontColorSelected: white, font: default-font } -} -} diff --git a/core/build.gradle b/core/build.gradle index bd295c0c39..abcec6d8f6 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -1,27 +1,4 @@ apply plugin: "java" sourceCompatibility = 1.8 [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' -sourceSets.main.java.srcDirs = ["src/"] - -import com.badlogic.gdx.tools.texturepacker.TexturePacker - -task packSprites(){ - doLast { - TexturePacker.process("core/assets-raw/sprites/", "core/assets/sprites/", "sprites.atlas") - } -} - -task packUI(){ - doLast { - TexturePacker.process("core/assets-raw/ui/", "core/assets/ui/", "uiskin.atlas") - } -} - -task pack(){ - dependsOn 'packSprites' - dependsOn 'packUI' -} - -eclipse.project { - name = appName + "-core" -} \ No newline at end of file +sourceSets.main.java.srcDirs = ["src/"] \ No newline at end of file diff --git a/core/src/Mindustry.gwt.xml b/core/src/Mindustry.gwt.xml deleted file mode 100644 index c964b6179d..0000000000 --- a/core/src/Mindustry.gwt.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/core/src/io/anuke/mindustry/Mindustry.java b/core/src/io/anuke/mindustry/Mindustry.java index 22fcea6b65..3b07847122 100644 --- a/core/src/io/anuke/mindustry/Mindustry.java +++ b/core/src/io/anuke/mindustry/Mindustry.java @@ -1,42 +1,69 @@ package io.anuke.mindustry; -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.files.FileHandle; -import com.badlogic.gdx.graphics.Pixmap; -import com.badlogic.gdx.graphics.Pixmap.Filter; -import com.badlogic.gdx.graphics.PixmapIO; +import io.anuke.arc.*; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.util.Log; +import io.anuke.arc.util.Time; import io.anuke.mindustry.core.*; -import io.anuke.mindustry.io.BlockLoader; +import io.anuke.mindustry.game.EventType.GameLoadEvent; import io.anuke.mindustry.io.BundleLoader; -import io.anuke.ucore.modules.ModuleCore; -import io.anuke.ucore.util.Log; import static io.anuke.mindustry.Vars.*; -public class Mindustry extends ModuleCore { +public class Mindustry extends ApplicationCore{ - @Override - public void init(){ - debug = Platform.instance.isDebug(); + @Override + public void setup(){ + Time.setDeltaProvider(() -> { + float result = Core.graphics.getDeltaTime() * 60f; + return (Float.isNaN(result) || Float.isInfinite(result)) ? 1f : Mathf.clamp(result, 0.0001f, 60f / 10f); + }); - Log.setUseColors(false); - BundleLoader.load(); - BlockLoader.load(); + Time.mark(); - module(logic = new Logic()); - module(world = new World()); - module(control = new Control()); - module(renderer = new Renderer()); - module(ui = new UI()); - module(netServer = new NetServer()); - module(netClient = new NetClient()); - module(netCommon = new NetCommon()); - } + Vars.init(); - @Override - public void render(){ - super.render(); - threads.handleRender(); - } + Log.setUseColors(false); + BundleLoader.load(); + content.load(); + content.loadColors(); + + add(logic = new Logic()); + add(world = new World()); + add(control = new Control()); + add(renderer = new Renderer()); + add(ui = new UI()); + add(netServer = new NetServer()); + add(netClient = new NetClient()); + } + + @Override + public void init(){ + super.init(); + + Log.info("Time to load [total]: {0}", Time.elapsed()); + Events.fire(new GameLoadEvent()); + } + + @Override + public void update(){ + long lastFrameTime = Time.nanos(); + + super.update(); + + int fpsCap = Core.settings.getInt("fpscap", 125); + + if(fpsCap <= 120){ + long target = (1000 * 1000000) / fpsCap; //target in nanos + long elapsed = Time.timeSinceNanos(lastFrameTime); + if(elapsed < target){ + try{ + Thread.sleep((target - elapsed) / 1000000, (int)((target - elapsed) % 1000000)); + }catch(InterruptedException e){ + e.printStackTrace(); + } + } + } + } } diff --git a/core/src/io/anuke/mindustry/Vars.java b/core/src/io/anuke/mindustry/Vars.java index 66842fa734..42097aa1e1 100644 --- a/core/src/io/anuke/mindustry/Vars.java +++ b/core/src/io/anuke/mindustry/Vars.java @@ -1,157 +1,222 @@ package io.anuke.mindustry; -import com.badlogic.gdx.Application.ApplicationType; -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.files.FileHandle; -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.utils.Array; -import com.badlogic.gdx.utils.IntMap; +import io.anuke.arc.Application.ApplicationType; +import io.anuke.arc.Core; +import io.anuke.arc.files.FileHandle; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.util.Structs; import io.anuke.mindustry.core.*; -import io.anuke.mindustry.entities.Bullet; -import io.anuke.mindustry.entities.Player; -import io.anuke.mindustry.entities.TileEntity; -import io.anuke.mindustry.entities.effect.Shield; -import io.anuke.mindustry.entities.enemies.Enemy; -import io.anuke.mindustry.core.Platform; -import io.anuke.mindustry.net.EditLog; -import io.anuke.mindustry.net.ClientDebug; -import io.anuke.mindustry.net.ServerDebug; -import io.anuke.ucore.UCore; -import io.anuke.ucore.entities.EffectEntity; -import io.anuke.ucore.entities.Entities; -import io.anuke.ucore.entities.EntityGroup; -import io.anuke.ucore.scene.ui.layout.Unit; -import io.anuke.ucore.util.OS; +import io.anuke.mindustry.entities.*; +import io.anuke.mindustry.entities.bullet.Bullet; +import io.anuke.mindustry.entities.effect.Fire; +import io.anuke.mindustry.entities.effect.Puddle; +import io.anuke.mindustry.entities.impl.EffectEntity; +import io.anuke.mindustry.entities.traits.DrawTrait; +import io.anuke.mindustry.entities.traits.SyncTrait; +import io.anuke.mindustry.entities.type.*; +import io.anuke.mindustry.game.*; +import io.anuke.mindustry.gen.Serialization; +import io.anuke.mindustry.net.Net; +import io.anuke.mindustry.world.blocks.defense.ForceProjector.ShieldEntity; + +import java.nio.charset.Charset; +import java.util.Arrays; import java.util.Locale; +@SuppressWarnings("unchecked") public class Vars{ + /** Whether to load locales.*/ + public static boolean loadLocales = true; + /** IO buffer size. */ + public static final int bufferSize = 8192; + /** global charset */ + public static final Charset charset = Charset.forName("UTF-8"); + /** main application name, capitalized */ + public static final String appName = "Mindustry"; + /** URL for itch.io donations. */ + public static final String donationURL = "https://anuke.itch.io/mindustry/purchase"; + /** URL for discord invite. */ + public static final String discordURL = "https://discord.gg/mindustry"; + /** URL for Github API for releases */ + public static final String releasesURL = "https://api.github.com/repos/Anuken/Mindustry/releases"; + /** URL for sending crash reports to */ + public static final String crashReportURL = "http://mins.us.to/report"; + /** maximum distance between mine and core that supports automatic transferring */ + public static final float mineTransferRange = 220f; + /** team of the player by default */ + public static final Team defaultTeam = Team.blue; + /** team of the enemy in waves/sectors */ + public static final Team waveTeam = Team.red; + /** whether to enable editing of units in the editor */ + public static final boolean enableUnitEditing = false; + /** max chat message length */ + public static final int maxTextLength = 150; + /** max player name length in bytes */ + public static final int maxNameLength = 40; + /** displayed item size when ingame, TODO remove. */ + public static final float itemSize = 5f; + /** extra padding around the world; units outside this bound will begin to self-destruct. */ + public static final float worldBounds = 100f; + /** default size of UI icons.*/ + public static final int iconsize = 48; + /** size of UI icons (small)*/ + public static final int iconsizesmall = 32; + /** units outside of this bound will simply die instantly */ + public static final float finalWorldBounds = worldBounds + 500; + /** ticks spent out of bound until self destruct. */ + public static final float boundsCountdown = 60 * 7; + /** for map generator dialog */ + public static boolean updateEditorOnChange = false; + /** size of tiles in units */ + public static final int tilesize = 8; + /** all choosable player colors in join/host dialog */ + public static final Color[] playerColors = { + Color.valueOf("82759a"), + Color.valueOf("c0c1c5"), + Color.valueOf("fff0e7"), + Color.valueOf("7d2953"), + Color.valueOf("ff074e"), + Color.valueOf("ff072a"), + Color.valueOf("ff76a6"), + Color.valueOf("a95238"), + Color.valueOf("ffa108"), + Color.valueOf("feeb2c"), + Color.valueOf("ffcaa8"), + Color.valueOf("008551"), + Color.valueOf("00e339"), + Color.valueOf("423c7b"), + Color.valueOf("4b5ef1"), + Color.valueOf("2cabfe"), + }; + /** default server port */ + public static final int port = 6567; + /** multicast discovery port.*/ + public static final int multicastPort = 20151; + /** multicast group for discovery.*/ + public static final String multicastGroup = "227.2.7.7"; + /** if true, UI is not drawn */ + public static boolean disableUI; + /** if true, game is set up in mobile mode, even on desktop. used for debugging */ + public static boolean testMobile; + /** whether the game is running on a mobile device */ + public static boolean mobile; + /** whether the game is running on an iOS device */ + public static boolean ios; + /** whether the game is running on an Android device */ + public static boolean android; + /** whether the game is running on a headless server */ + public static boolean headless; + /** application data directory, equivalent to {@link io.anuke.arc.Settings#getDataDirectory()} */ + public static FileHandle dataDirectory; + /** data subdirectory used for screenshots */ + public static FileHandle screenshotDirectory; + /** data subdirectory used for custom mmaps */ + public static FileHandle customMapDirectory; + /** tmp subdirectory for map conversion */ + public static FileHandle tmpDirectory; + /** data subdirectory used for saves */ + public static FileHandle saveDirectory; + /** old map file extension, for conversion */ + public static final String oldMapExtension = "mmap"; + /** map file extension */ + public static final String mapExtension = "msav"; + /** save file extension */ + public static final String saveExtension = "msav"; - public static final boolean testMobile = false; - //shorthand for whether or not this is running on android - public static final boolean mobile = (Gdx.app.getType() == ApplicationType.Android) || - Gdx.app.getType() == ApplicationType.iOS || testMobile; - public static final boolean ios = Gdx.app.getType() == ApplicationType.iOS; - public static final boolean android = Gdx.app.getType() == ApplicationType.Android; - //shorthand for whether or not this is running on GWT - public static final boolean gwt = (Gdx.app.getType() == ApplicationType.WebGL); - //whether to send block state change events to players - public static final boolean syncBlockState = false; - //how far away from the player blocks can be placed - public static final float placerange = 66; - //respawn time in frames - public static final float respawnduration = 60*4; - //time between waves in frames (on normal mode) - public static final float wavespace = 60*60*(mobile ? 1 : 1); - //waves can last no longer than 3 minutes, otherwise the next one spawns - public static final float maxwavespace = 60*60*4f; - //advance time the pathfinding starts at - public static final float aheadPathfinding = 60*15; - //how far away from spawn points the player can't place blocks - public static final float enemyspawnspace = 65; - //discord group URL - public static final String discordURL = "https://discord.gg/BKADYds"; + /** list of all locales that can be switched to */ + public static Locale[] locales; - public static final String releasesURL = "https://api.github.com/repos/Anuken/Mindustry/releases"; - public static final String macAppDir = UCore.getProperty("user.home") + "/Library/Application Support/"; - //directory for user-created map data - public static final FileHandle customMapDirectory = gwt ? null : UCore.isAssets() ? - Gdx.files.local("../../desktop/mindustry-maps") : - OS.isMac ? (Gdx.files.absolute(macAppDir).child("maps/")) : - Gdx.files.local("mindustry-maps/"); - //save file directory - public static final FileHandle saveDirectory = gwt ? null : UCore.isAssets() ? - Gdx.files.local("../../desktop/mindustry-saves") : - OS.isMac ? (Gdx.files.absolute(macAppDir).child("saves/")) : - Gdx.files.local("mindustry-saves/"); - //scale of the font - public static float fontscale = Math.max(Unit.dp.scl(1f)/2f, 0.5f); - //camera zoom displayed on startup - public static final int baseCameraScale = Math.round(Unit.dp.scl(4)); - //how much the zoom changes every zoom button press (unused?) - public static final int zoomScale = Math.round(Unit.dp.scl(1)); - //if true, player speed will be increased, massive amounts of resources will be given on start, and other debug options will be available - public static boolean debug = false; - public static boolean debugNet = true; - public static boolean console = false; - //whether the player can clip through walls - public static boolean noclip = false; - //whether to draw chunk borders - public static boolean debugChunks = false; - //whether turrets have infinite ammo (only with debug) - public static boolean infiniteAmmo = true; - //whether to show paths of enemies - public static boolean showPaths = false; - //if false, player is always hidden - public static boolean showPlayer = true; - //whether to hide ui, only on debug - public static boolean showUI = true; - //whether to show block debug - public static boolean showBlockDebug = false; + public static ContentLoader content; + public static GameState state; + public static GlobalData data; + public static EntityCollisions collisions; + public static DefaultWaves defaultWaves; - public static boolean headless = false; + public static Control control; + public static Logic logic; + public static Renderer renderer; + public static UI ui; + public static World world; + public static NetServer netServer; + public static NetClient netClient; - public static float controllerMin = 0.25f; + public static EntityGroup playerGroup; + public static EntityGroup tileGroup; + public static EntityGroup bulletGroup; + public static EntityGroup effectGroup; + public static EntityGroup groundEffectGroup; + public static EntityGroup shieldGroup; + public static EntityGroup puddleGroup; + public static EntityGroup fireGroup; + public static EntityGroup[] unitGroups; - public static float baseControllerSpeed = 11f; + /** all local players, currently only has one player. may be used for local co-op in the future */ + public static Player player; - public static final int saveSlots = 64; - //amount of drops that are left when breaking a block - public static final float breakDropAmount = 0.5f; - - public static Array currentEditLogs = new Array<>(); - - //only if smoothCamera - public static boolean snapCamera = true; - - public static final int tilesize = 8; + public static void init(){ + Serialization.init(); - public static final Locale[] locales = {new Locale("en"), new Locale("fr"), new Locale("ru"), new Locale("uk", "UA"), new Locale("pl"), - new Locale("de"), new Locale("pt", "BR"), new Locale("ko"), new Locale("in", "ID"), new Locale("ita"), new Locale("es")}; + if(loadLocales){ + //load locales + String[] stra = Core.files.internal("locales").readString().split("\n"); + locales = new Locale[stra.length]; + for(int i = 0; i < locales.length; i++){ + String code = stra[i]; + if(code.contains("_")){ + locales[i] = new Locale(code.split("_")[0], code.split("_")[1]); + }else{ + locales[i] = new Locale(code); + } + } - public static final Color[] playerColors = { - Color.valueOf("82759a"), - Color.valueOf("c0c1c5"), - Color.valueOf("fff0e7"), - Color.valueOf("7d2953"), - Color.valueOf("ff074e"), - Color.valueOf("ff072a"), - Color.valueOf("ff76a6"), - Color.valueOf("a95238"), - Color.valueOf("ffa108"), - Color.valueOf("feeb2c"), - Color.valueOf("ffcaa8"), - Color.valueOf("008551"), - Color.valueOf("00e339"), - Color.valueOf("423c7b"), - Color.valueOf("4b5ef1"), - Color.valueOf("2cabfe"), - }; + Arrays.sort(locales, Structs.comparing(l -> l.getDisplayName(l), String.CASE_INSENSITIVE_ORDER)); + } - //server port - public static final int port = 6567; - public static final int webPort = 6568; + Version.init(); - public static final GameState state = new GameState(); - public static final ThreadHandler threads = new ThreadHandler(Platform.instance.getThreadProvider()); + content = new ContentLoader(); + if(!headless){ + content.setVerbose(); + } - public static final ServerDebug serverDebug = new ServerDebug(); - public static final ClientDebug clientDebug = new ClientDebug(); + defaultWaves = new DefaultWaves(); + collisions = new EntityCollisions(); - public static Control control; - public static Logic logic; - public static Renderer renderer; - public static UI ui; - public static World world; - public static NetCommon netCommon; - public static NetServer netServer; - public static NetClient netClient; - - public static Player player; + playerGroup = Entities.addGroup(Player.class).enableMapping(); + tileGroup = Entities.addGroup(TileEntity.class, false); + bulletGroup = Entities.addGroup(Bullet.class).enableMapping(); + effectGroup = Entities.addGroup(EffectEntity.class, false); + groundEffectGroup = Entities.addGroup(DrawTrait.class, false); + puddleGroup = Entities.addGroup(Puddle.class).enableMapping(); + shieldGroup = Entities.addGroup(ShieldEntity.class, false); + fireGroup = Entities.addGroup(Fire.class).enableMapping(); + unitGroups = new EntityGroup[Team.all.length]; - public static final EntityGroup playerGroup = Entities.addGroup(Player.class).enableMapping(); - public static final EntityGroup enemyGroup = Entities.addGroup(Enemy.class).enableMapping(); - public static final EntityGroup tileGroup = Entities.addGroup(TileEntity.class, false); - public static final EntityGroup bulletGroup = Entities.addGroup(Bullet.class); - public static final EntityGroup shieldGroup = Entities.addGroup(Shield.class, false); - public static final EntityGroup effectGroup = Entities.addGroup(EffectEntity.class, false); + for(Team team : Team.all){ + unitGroups[team.ordinal()] = Entities.addGroup(BaseUnit.class).enableMapping(); + } + + for(EntityGroup group : Entities.getAllGroups()){ + group.setRemoveListener(entity -> { + if(entity instanceof SyncTrait && Net.client()){ + netClient.addRemovedEntity((entity).getID()); + } + }); + } + + state = new GameState(); + data = new GlobalData(); + + mobile = Core.app.getType() == ApplicationType.Android || Core.app.getType() == ApplicationType.iOS || testMobile; + ios = Core.app.getType() == ApplicationType.iOS; + android = Core.app.getType() == ApplicationType.Android; + + Core.settings.setAppName(appName); + + dataDirectory = Core.settings.getDataDirectory(); + screenshotDirectory = dataDirectory.child("screenshots/"); + customMapDirectory = dataDirectory.child("maps/"); + saveDirectory = dataDirectory.child("saves/"); + tmpDirectory = dataDirectory.child("tmp/"); + } } diff --git a/core/src/io/anuke/mindustry/ai/BlockIndexer.java b/core/src/io/anuke/mindustry/ai/BlockIndexer.java new file mode 100644 index 0000000000..61500ae687 --- /dev/null +++ b/core/src/io/anuke/mindustry/ai/BlockIndexer.java @@ -0,0 +1,352 @@ +package io.anuke.mindustry.ai; + +import io.anuke.arc.Events; +import io.anuke.arc.collection.*; +import io.anuke.arc.function.Predicate; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.Geometry; +import io.anuke.mindustry.content.Blocks; +import io.anuke.mindustry.entities.type.TileEntity; +import io.anuke.mindustry.game.EventType.TileChangeEvent; +import io.anuke.mindustry.game.EventType.WorldLoadEvent; +import io.anuke.mindustry.game.Team; +import io.anuke.mindustry.game.Teams.TeamData; +import io.anuke.mindustry.type.Item; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.meta.BlockFlag; + +import static io.anuke.mindustry.Vars.*; + +/** Class used for indexing special target blocks for AI. */ +@SuppressWarnings("unchecked") +public class BlockIndexer{ + /** Size of one ore quadrant. */ + private final static int oreQuadrantSize = 20; + /** Size of one structure quadrant. */ + private final static int structQuadrantSize = 12; + + /** Set of all ores that are being scanned. */ + private final ObjectSet scanOres = ObjectSet.with(Item.getAllOres().toArray(Item.class)); + private final ObjectSet itemSet = new ObjectSet<>(); + /** Stores all ore quadtrants on the map. */ + private ObjectMap> ores; + /** Tags all quadrants. */ + private GridBits[] structQuadrants; + /** Stores all damaged tile entities by team. */ + private ObjectSet[] damagedTiles = new ObjectSet[Team.all.length]; + /**All ores available on this map.*/ + private ObjectSet allOres = new ObjectSet<>(); + + /** Maps teams to a map of flagged tiles by type. */ + private ObjectSet[][] flagMap = new ObjectSet[Team.all.length][BlockFlag.all.length]; + /** Maps tile positions to their last known tile index data. */ + private IntMap typeMap = new IntMap<>(); + /** Empty set used for returning. */ + private ObjectSet emptySet = new ObjectSet<>(); + /** Array used for returning and reusing. */ + private Array returnArray = new Array<>(); + + public BlockIndexer(){ + Events.on(TileChangeEvent.class, event -> { + if(typeMap.get(event.tile.pos()) != null){ + TileIndex index = typeMap.get(event.tile.pos()); + for(BlockFlag flag : index.flags){ + getFlagged(index.team)[flag.ordinal()].remove(event.tile); + } + } + process(event.tile); + updateQuadrant(event.tile); + }); + + Events.on(WorldLoadEvent.class, event -> { + damagedTiles = new ObjectSet[Team.all.length]; + flagMap = new ObjectSet[Team.all.length][BlockFlag.all.length]; + + for(int i = 0; i < flagMap.length; i++){ + for(int j = 0; j < BlockFlag.all.length; j++){ + flagMap[i][j] = new ObjectSet<>(); + } + } + + typeMap.clear(); + allOres.clear(); + ores = null; + + //create bitset for each team type that contains each quadrant + structQuadrants = new GridBits[Team.all.length]; + for(int i = 0; i < Team.all.length; i++){ + structQuadrants[i] = new GridBits(Mathf.ceil(world.width() / (float)structQuadrantSize), Mathf.ceil(world.height() / (float)structQuadrantSize)); + } + + for(int x = 0; x < world.width(); x++){ + for(int y = 0; y < world.height(); y++){ + Tile tile = world.tile(x, y); + + process(tile); + + if(tile.entity != null && tile.entity.damaged()){ + notifyTileDamaged(tile.entity); + } + + if(tile.drop() != null) allOres.add(tile.drop()); + } + } + + for(int x = 0; x < quadWidth(); x++){ + for(int y = 0; y < quadHeight(); y++){ + updateQuadrant(world.tile(x * structQuadrantSize, y * structQuadrantSize)); + } + } + + scanOres(); + }); + } + + private ObjectSet[] getFlagged(Team team){ + return flagMap[team.ordinal()]; + } + + /** @return whether this item is present on this map.*/ + public boolean hasOre(Item item){ + return allOres.contains(item); + } + + /** Returns all damaged tiles by team. */ + public ObjectSet getDamaged(Team team){ + returnArray.clear(); + + if(damagedTiles[team.ordinal()] == null){ + damagedTiles[team.ordinal()] = new ObjectSet<>(); + } + + ObjectSet set = damagedTiles[team.ordinal()]; + for(Tile tile : set){ + if(tile.entity == null || tile.entity.getTeam() != team || !tile.entity.damaged()){ + returnArray.add(tile); + } + } + + for(Tile tile : returnArray){ + set.remove(tile); + } + + return set; + } + + /** Get all allied blocks with a flag. */ + public ObjectSet getAllied(Team team, BlockFlag type){ + return flagMap[team.ordinal()][type.ordinal()]; + } + + /** Get all enemy blocks with a flag. */ + public Array getEnemy(Team team, BlockFlag type){ + returnArray.clear(); + for(Team enemy : state.teams.enemiesOf(team)){ + if(state.teams.isActive(enemy)){ + for(Tile tile : getFlagged(enemy)[type.ordinal()]){ + returnArray.add(tile); + } + } + } + return returnArray; + } + + public void notifyTileDamaged(TileEntity entity){ + if(damagedTiles[entity.getTeam().ordinal()] == null){ + damagedTiles[entity.getTeam().ordinal()] = new ObjectSet<>(); + } + + ObjectSet set = damagedTiles[entity.getTeam().ordinal()]; + set.add(entity.tile); + } + + public TileEntity findTile(Team team, float x, float y, float range, Predicate pred){ + TileEntity closest = null; + float dst = 0; + + for(int rx = Math.max((int)((x - range) / tilesize / structQuadrantSize), 0); rx <= (int)((x + range) / tilesize / structQuadrantSize) && rx < quadWidth(); rx++){ + for(int ry = Math.max((int)((y - range) / tilesize / structQuadrantSize), 0); ry <= (int)((y + range) / tilesize / structQuadrantSize) && ry < quadHeight(); ry++){ + + if(!getQuad(team, rx, ry)) continue; + + for(int tx = rx * structQuadrantSize; tx < (rx + 1) * structQuadrantSize && tx < world.width(); tx++){ + for(int ty = ry * structQuadrantSize; ty < (ry + 1) * structQuadrantSize && ty < world.height(); ty++){ + Tile other = world.ltile(tx, ty); + + if(other == null) continue; + + if(other.entity == null || other.getTeam() != team || !pred.test(other) || !other.block().targetable) + continue; + + TileEntity e = other.entity; + + float ndst = Mathf.dst(x, y, e.x, e.y); + if(ndst < range && (closest == null || ndst < dst)){ + dst = ndst; + closest = e; + } + } + } + } + } + + return closest; + } + + /** + * Returns a set of tiles that have ores of the specified type nearby. + * While each tile in the set is not guaranteed to have an ore directly on it, + * each tile will at least have an ore within {@link #oreQuadrantSize} / 2 blocks of it. + * Only specific ore types are scanned. See {@link #scanOres}. + */ + public ObjectSet getOrePositions(Item item){ + return ores.get(item, emptySet); + } + + /** Find the closest ore block relative to a position. */ + public Tile findClosestOre(float xp, float yp, Item item){ + Tile tile = Geometry.findClosest(xp, yp, world.indexer.getOrePositions(item)); + + if(tile == null) return null; + + for(int x = Math.max(0, tile.x - oreQuadrantSize / 2); x < tile.x + oreQuadrantSize / 2 && x < world.width(); x++){ + for(int y = Math.max(0, tile.y - oreQuadrantSize / 2); y < tile.y + oreQuadrantSize / 2 && y < world.height(); y++){ + Tile res = world.tile(x, y); + if(res.block() == Blocks.air && res.drop() == item){ + return res; + } + } + } + + return null; + } + + private void process(Tile tile){ + if(tile.block().flags.size() > 0 && + tile.getTeam() != Team.none){ + ObjectSet[] map = getFlagged(tile.getTeam()); + + for(BlockFlag flag : tile.block().flags){ + + ObjectSet arr = map[flag.ordinal()]; + + arr.add(tile); + + map[flag.ordinal()] = arr; + } + typeMap.put(tile.pos(), new TileIndex(tile.block().flags, tile.getTeam())); + } + + if(ores == null) return; + + int quadrantX = tile.x / oreQuadrantSize; + int quadrantY = tile.y / oreQuadrantSize; + itemSet.clear(); + + Tile rounded = world.tile(Mathf.clamp(quadrantX * oreQuadrantSize + oreQuadrantSize / 2, 0, world.width() - 1), + Mathf.clamp(quadrantY * oreQuadrantSize + oreQuadrantSize / 2, 0, world.height() - 1)); + + //find all items that this quadrant contains + for(int x = quadrantX * structQuadrantSize; x < world.width() && x < (quadrantX + 1) * structQuadrantSize; x++){ + for(int y = quadrantY * structQuadrantSize; y < world.height() && y < (quadrantY + 1) * structQuadrantSize; y++){ + Tile result = world.tile(x, y); + if(result == null || result.drop() == null || !scanOres.contains(result.drop())) continue; + + itemSet.add(result.drop()); + } + } + + //update quadrant at this position + for(Item item : scanOres){ + ObjectSet set = ores.get(item); + + //update quadrant status depending on whether the item is in it + if(!itemSet.contains(item)){ + set.remove(rounded); + }else{ + set.add(rounded); + } + } + } + + private void updateQuadrant(Tile tile){ + if(structQuadrants == null) return; + + //this quadrant is now 'dirty', re-scan the whole thing + int quadrantX = tile.x / structQuadrantSize; + int quadrantY = tile.y / structQuadrantSize; + int index = quadrantX + quadrantY * quadWidth(); + + for(Team team : Team.all){ + TeamData data = state.teams.get(team); + + //fast-set this quadrant to 'occupied' if the tile just placed is already of this team + if(tile.getTeam() == data.team && tile.entity != null && tile.block().targetable){ + structQuadrants[data.team.ordinal()].set(quadrantX, quadrantY); + continue; //no need to process futher + } + + structQuadrants[data.team.ordinal()].set(quadrantX, quadrantY, false); + + outer: + for(int x = quadrantX * structQuadrantSize; x < world.width() && x < (quadrantX + 1) * structQuadrantSize; x++){ + for(int y = quadrantY * structQuadrantSize; y < world.height() && y < (quadrantY + 1) * structQuadrantSize; y++){ + Tile result = world.ltile(x, y); + //when a targetable block is found, mark this quadrant as occupied and stop searching + if(result.entity != null && result.getTeam() == data.team){ + structQuadrants[data.team.ordinal()].set(quadrantX, quadrantY); + break outer; + } + } + } + } + } + + private boolean getQuad(Team team, int quadrantX, int quadrantY){ + return structQuadrants[team.ordinal()].get(quadrantX, quadrantY); + } + + private int quadWidth(){ + return Mathf.ceil(world.width() / (float)structQuadrantSize); + } + + private int quadHeight(){ + return Mathf.ceil(world.height() / (float)structQuadrantSize); + } + + private void scanOres(){ + ores = new ObjectMap<>(); + + //initialize ore map with empty sets + for(Item item : scanOres){ + ores.put(item, new ObjectSet<>()); + } + + for(int x = 0; x < world.width(); x++){ + for(int y = 0; y < world.height(); y++){ + int qx = (x / oreQuadrantSize); + int qy = (y / oreQuadrantSize); + + Tile tile = world.tile(x, y); + + //add position of quadrant to list when an ore is found + if(tile.drop() != null && scanOres.contains(tile.drop()) && tile.block() == Blocks.air){ + ores.get(tile.drop()).add(world.tile( + //make sure to clamp quadrant middle position, since it might go off bounds + Mathf.clamp(qx * oreQuadrantSize + oreQuadrantSize / 2, 0, world.width() - 1), + Mathf.clamp(qy * oreQuadrantSize + oreQuadrantSize / 2, 0, world.height() - 1))); + } + } + } + } + + private class TileIndex{ + public final EnumSet flags; + public final Team team; + + public TileIndex(EnumSet flags, Team team){ + this.flags = flags; + this.team = team; + } + } +} diff --git a/core/src/io/anuke/mindustry/ai/Heuristics.java b/core/src/io/anuke/mindustry/ai/Heuristics.java deleted file mode 100644 index 3a2e7885cb..0000000000 --- a/core/src/io/anuke/mindustry/ai/Heuristics.java +++ /dev/null @@ -1,69 +0,0 @@ -package io.anuke.mindustry.ai; - -import com.badlogic.gdx.ai.pfa.Heuristic; -import io.anuke.mindustry.world.Block; -import io.anuke.mindustry.world.Tile; -import io.anuke.ucore.function.Predicate; - -import static io.anuke.mindustry.Vars.tilesize; - -public class Heuristics { - /**How many times more it costs to go through a destructible block than an empty block.*/ - static final float solidMultiplier = 5f; - /**How many times more it costs to go through a tile that touches a solid block.*/ - static final float occludedMultiplier = 5f; - - /**Calculates the fastest path. No priorities, just avoids solid blocks.*/ - public static class FastestHeuristic implements Heuristic { - - @Override - public float estimate(Tile node, Tile other){ - //Get Manhattan distance cost - float cost = Math.abs(node.worldx() - other.worldx()) + Math.abs(node.worldy() - other.worldy()); - - //If either one of the tiles is a breakable solid block (that is, it's player-made), - //increase the cost by the tilesize times the solid block multiplier - //Also add the block health, so blocks with more health cost more to traverse - if(node.breakable() && node.block().solid) cost += tilesize* solidMultiplier + node.block().health; - if(other.breakable() && other.block().solid) cost += tilesize* solidMultiplier + other.block().health; - - //if this block has solid blocks near it, increase the cost, as we don't want enemies hugging walls - if(node.occluded) cost += tilesize*occludedMultiplier; - - return cost; - } - } - - /**Calculates the fastest and most destructive path based on a block predicate.*/ - public static class DestrutiveHeuristic implements Heuristic { - /**Should return whether a block if "free", e.g. whether it's an important target*/ - private final Predicate frees; - - public DestrutiveHeuristic(Predicate frees){ - this.frees = frees; - } - - @Override - public float estimate(Tile node, Tile other){ - //Get Manhattan distance cost - float cost = Math.abs(node.worldx() - other.worldx()) + Math.abs(node.worldy() - other.worldy()); - - //If either one of the tiles is a breakable solid block (that is, it's player-made), - //increase the cost by the tilesize times the solid block multiplier - //Also add the block health, so blocks with more health cost more to traverse - if(node.breakable() && node.block().solid) cost += tilesize* solidMultiplier + node.block().health; - if(other.breakable() && other.block().solid) cost += tilesize* solidMultiplier + other.block().health; - - //if this block has solid blocks near it, increase the cost, as we don't want enemies hugging walls - if(node.occluded) cost += tilesize*occludedMultiplier; - - if(other.getLinked() != null) other = other.getLinked(); - if(node.getLinked() != null) node = node.getLinked(); - - //check if it's free - if(frees.test(other.block()) || frees.test(node.block())) cost = 0; - - return cost; - } - } -} diff --git a/core/src/io/anuke/mindustry/ai/OptimizedGraph.java b/core/src/io/anuke/mindustry/ai/OptimizedGraph.java deleted file mode 100644 index 02bd9e5ebd..0000000000 --- a/core/src/io/anuke/mindustry/ai/OptimizedGraph.java +++ /dev/null @@ -1,12 +0,0 @@ -package io.anuke.mindustry.ai; - -/**An interface for an indexed graph that doesn't use allocations for connections.*/ -public interface OptimizedGraph{ - /**This is used in the same way as getConnections(), but does not use Connection objects.*/ - N[] connectionsOf(N node); - - /** Returns the unique index of the given node. - * @param node the node whose index will be returned - * @return the unique index of the given node. */ - int getIndex (N node); -} diff --git a/core/src/io/anuke/mindustry/ai/OptimizedPathFinder.java b/core/src/io/anuke/mindustry/ai/OptimizedPathFinder.java deleted file mode 100644 index 6898ee1215..0000000000 --- a/core/src/io/anuke/mindustry/ai/OptimizedPathFinder.java +++ /dev/null @@ -1,268 +0,0 @@ -package io.anuke.mindustry.ai; - -import com.badlogic.gdx.ai.pfa.*; -import com.badlogic.gdx.utils.BinaryHeap; -import com.badlogic.gdx.utils.IntMap; -import com.badlogic.gdx.utils.TimeUtils; - -/**An IndexedAStarPathfinder that uses an OptimizedGraph, and therefore has less allocations.*/ -public class OptimizedPathFinder implements PathFinder { - OptimizedGraph graph; - IntMap> records = new IntMap<>(); - BinaryHeap> openList; - NodeRecord current; - - /** - * The unique ID for each search run. Used to mark nodes. - */ - private int searchId; - - private static final byte UNVISITED = 0; - private static final byte OPEN = 1; - private static final byte CLOSED = 2; - - @SuppressWarnings("unchecked") - public OptimizedPathFinder(OptimizedGraph graph) { - this.graph = graph; - this.openList = new BinaryHeap<>(); - } - - @Override - public boolean searchConnectionPath(N startNode, N endNode, Heuristic heuristic, GraphPath> outPath) { - return false; - } - - @Override - public boolean searchNodePath(N startNode, N endNode, Heuristic heuristic, GraphPath outPath) { - - // Perform AStar - boolean found = search(startNode, endNode, heuristic); - - if (found) { - // Create a path made of nodes - generateNodePath(startNode, outPath); - } - - return found; - } - - protected boolean search(N startNode, N endNode, Heuristic heuristic) { - - initSearch(startNode, endNode, heuristic); - - // Iterate through processing each node - do { - // Retrieve the node with smallest estimated total cost from the open list - current = openList.pop(); - current.category = CLOSED; - - // Terminate if we reached the goal node - if (current.node == endNode) return true; - - visitChildren(endNode, heuristic); - - } while (openList.size > 0); - - // We've run out of nodes without finding the goal, so there's no solution - return false; - } - - @Override - public boolean search(PathFinderRequest request, long timeToRun) { - - long lastTime = TimeUtils.nanoTime(); - - // We have to initialize the search if the status has just changed - if (request.statusChanged) { - initSearch(request.startNode, request.endNode, request.heuristic); - request.statusChanged = false; - } - - // Iterate through processing each node - do { - - // Check the available time - long currentTime = TimeUtils.nanoTime(); - timeToRun -= currentTime - lastTime; - if (timeToRun <= PathFinderQueue.TIME_TOLERANCE) return false; - - // Retrieve the node with smallest estimated total cost from the open list - current = openList.pop(); - current.category = CLOSED; - - // Terminate if we reached the goal node; we've found a path. - if (current.node == request.endNode) { - request.pathFound = true; - - generateNodePath(request.startNode, request.resultPath); - - return true; - } - - // Visit current node's children - visitChildren(request.endNode, request.heuristic); - - // Store the current time - lastTime = currentTime; - - } while (openList.size > 0); - - // The open list is empty and we've not found a path. - request.pathFound = false; - return true; - } - - protected void initSearch(N startNode, N endNode, Heuristic heuristic) { - - // Increment the search id - if (++searchId < 0) searchId = 1; - - // Initialize the open list - openList.clear(); - - // Initialize the record for the start node and add it to the open list - NodeRecord startRecord = getNodeRecord(startNode); - startRecord.node = startNode; - //startRecord.connection = null; - startRecord.costSoFar = 0; - addToOpenList(startRecord, heuristic.estimate(startNode, endNode)); - - current = null; - } - - protected void visitChildren(N endNode, Heuristic heuristic) { - // Get current node's outgoing connections - //Array> connections = graph.getConnections(current.node); - N[] conn = graph.connectionsOf(current.node); - - // Loop through each connection in turn - for (int i = 0; i < conn.length; i++) { - - //Connection connection = connections.get(i) - - // Get the cost estimate for the node - N node = conn[i]; - - if(node == null) continue; - - float addCost = heuristic.estimate(current.node, node); - - float nodeCost = current.costSoFar + addCost; - - float nodeHeuristic; - NodeRecord nodeRecord = getNodeRecord(node); - if (nodeRecord.category == CLOSED) { // The node is closed - - // If we didn't find a shorter route, skip - if (nodeRecord.costSoFar <= nodeCost) continue; - - // We can use the node's old cost values to calculate its heuristic - // without calling the possibly expensive heuristic function - nodeHeuristic = nodeRecord.getEstimatedTotalCost() - nodeRecord.costSoFar; - } else if (nodeRecord.category == OPEN) { // The node is open - - // If our route is no better, then skip - if (nodeRecord.costSoFar <= nodeCost) continue; - - // Remove it from the open list (it will be re-added with the new cost) - openList.remove(nodeRecord); - - // We can use the node's old cost values to calculate its heuristic - // without calling the possibly expensive heuristic function - nodeHeuristic = nodeRecord.getEstimatedTotalCost() - nodeRecord.costSoFar; - } else { // the node is unvisited - - // We'll need to calculate the heuristic value using the function, - // since we don't have a node record with a previously calculated value - nodeHeuristic = heuristic.estimate(node, endNode); - } - - // Update node record's cost and connection - nodeRecord.costSoFar = nodeCost; - nodeRecord.from = current.node; //TODO ??? - - // Add it to the open list with the estimated total cost - addToOpenList(nodeRecord, nodeCost + nodeHeuristic); - } - - } - - protected void generateNodePath(N startNode, GraphPath outPath) { - - // Work back along the path, accumulating nodes - // outPath.clear(); - while (current.from != null) { - outPath.add(current.node); - current = records.get(graph.getIndex(current.from)); - } - outPath.add(startNode); - - // Reverse the path - outPath.reverse(); - } - - protected void addToOpenList(NodeRecord nodeRecord, float estimatedTotalCost) { - openList.add(nodeRecord, estimatedTotalCost); - nodeRecord.category = OPEN; - } - - protected NodeRecord getNodeRecord(N node) { - if(!records.containsKey(graph.getIndex(node))){ - NodeRecord record = new NodeRecord<>(); - record.node = node; - record.searchId = searchId; - records.put(graph.getIndex(node), record); - return record; - }else{ - return records.get(graph.getIndex(node)); - } - } - - /** - * This nested class is used to keep track of the information we need for each node during the search. - * - * @param Type of node - * @author davebaol - */ - static class NodeRecord extends BinaryHeap.Node { - /** - * The reference to the node. - */ - N node; - N from; - - /** - * The incoming connection to the node - */ - //Connection connection; - - /** - * The actual cost from the start node. - */ - float costSoFar; - - /** - * The node category: {@link #UNVISITED}, {@link #OPEN} or {@link #CLOSED}. - */ - byte category; - - /** - * ID of the current search. - */ - int searchId; - - /** - * Creates a {@code NodeRecord}. - */ - public NodeRecord() { - super(0); - } - - /** - * Returns the estimated total cost. - */ - public float getEstimatedTotalCost() { - return getValue(); - } - } -} diff --git a/core/src/io/anuke/mindustry/ai/Pathfind.java b/core/src/io/anuke/mindustry/ai/Pathfind.java deleted file mode 100644 index 1d65921692..0000000000 --- a/core/src/io/anuke/mindustry/ai/Pathfind.java +++ /dev/null @@ -1,248 +0,0 @@ -package io.anuke.mindustry.ai; - -import com.badlogic.gdx.ai.pfa.PathFinderRequest; -import com.badlogic.gdx.ai.pfa.PathSmoother; -import com.badlogic.gdx.math.MathUtils; -import com.badlogic.gdx.math.Vector2; -import io.anuke.mindustry.entities.enemies.Enemy; -import io.anuke.mindustry.game.SpawnPoint; -import io.anuke.mindustry.world.Tile; -import io.anuke.ucore.core.Timers; -import io.anuke.ucore.util.Angles; -import io.anuke.ucore.util.Log; -import io.anuke.ucore.util.Mathf; - -import static io.anuke.mindustry.Vars.*; - -public class Pathfind{ - /**Maximum time taken per frame on pathfinding for a single path.*/ - private static final long maxTime = 1000000 * 5; - - /**Tile graph, for determining conenctions between two tiles*/ - TileGraph graph = new TileGraph(); - /**Smoother that removes extra nodes from a path.*/ - PathSmoother smoother = new PathSmoother(new Raycaster()); - /**temporary vector2 for calculations*/ - Vector2 vector = new Vector2(); - - Vector2 v1 = new Vector2(); - Vector2 v2 = new Vector2(); - Vector2 v3 = new Vector2(); - - /**Finds the position on the path an enemy should move to. - * If the path is not yet calculated, this returns the enemy's position (i. e. "don't move") - * @param enemy The enemy to find a path for - * @return The position the enemy should move to.*/ - public Vector2 find(Enemy enemy){ - //TODO fix -1/-2 node usage - if(enemy.node == -1 || enemy.node == -2){ - findNode(enemy); - } - - if(enemy.node == -2){ - enemy.node = -1; - } - - if(enemy.node < 0 || world.getSpawns().get(enemy.lane).pathTiles == null){ - return vector.set(enemy.x, enemy.y); - } - - Tile[] path = world.getSpawns().get(enemy.lane).pathTiles; - - if(enemy.node >= path.length){ - enemy.node = -1; - return vector.set(enemy.x, enemy.y); - } - - if(enemy.node <= -1){ - return vector.set(enemy.x, enemy.y); - } - - //TODO documentation on what this does - Tile prev = path[enemy.node - 1]; - - Tile target = path[enemy.node]; - - //a bridge has been broken, re-path - if(!world.passable(target.x, target.y)){ - remakePath(); - return vector.set(enemy.x, enemy.y); - } - - float projectLen = Vector2.dst(prev.worldx(), prev.worldy(), target.worldx(), target.worldy()) / 6f; - - Vector2 projection = projectPoint(prev.worldx(), prev.worldy(), - target.worldx(), target.worldy(), enemy.x, enemy.y); - - boolean canProject = true; - - if(projectLen < 8 || !onLine(projection, prev.worldx(), prev.worldy(), target.worldx(), target.worldy())){ - canProject = false; - }else{ - projection.add(v1.set(projectLen, 0).rotate(Angles.angle(prev.worldx(), prev.worldy(), - target.worldx(), target.worldy()))); - } - - float dst = Vector2.dst(enemy.x, enemy.y, target.worldx(), target.worldy()); - float nlinedist = enemy.node >= path.length - 1 ? 9999 : - pointLineDist(path[enemy.node].worldx(), path[enemy.node].worldy(), - path[enemy.node + 1].worldx(), path[enemy.node + 1].worldy(), enemy.x, enemy.y); - - if(dst < 8 || nlinedist < 8){ - if(enemy.node <= path.length-2) - enemy.node ++; - - target = path[enemy.node]; - } - - if(canProject && projection.dst(enemy.x, enemy.y) < Vector2.dst(target.x, target.y, enemy.x, enemy.y)){ - vector.set(projection); - }else{ - vector.set(target.worldx(), target.worldy()); - } - - //near the core, stop - if(enemy.node == path.length - 1){ - vector.set(target.worldx(), target.worldy()); - } - - return vector; - - } - - /**Re-calculate paths for all enemies. Runs when a path changes while moving.*/ - private void remakePath(){ - for(int i = 0; i < enemyGroup.size(); i ++){ - Enemy enemy = enemyGroup.all().get(i); - enemy.node = -1; - } - - resetPaths(); - } - - /**Update the pathfinders and continue calculating the path if it hasn't been calculated yet. - * This method is run each frame.*/ - public void update(){ - - //go through each spawnpoint, and if it's not found a path yet, update it - for(int i = 0; i < world.getSpawns().size; i ++){ - SpawnPoint point = world.getSpawns().get(i); - if(point.request == null || point.finder == null){ - continue; - } - - if(!point.request.pathFound){ - try{ - if(point.finder.search(point.request, maxTime)){ - smoother.smoothPath(point.path); - point.pathTiles = point.path.nodes.toArray(Tile.class); - point.finder = null; - } - }catch (ArrayIndexOutOfBoundsException e){ - //no path - point.request.pathFound = true; - } - } - } - - } - - //1300-1500ms, usually 1400 unoptimized on Caldera - /**Benchmark pathfinding speed. Debugging stuff.*/ - public void benchmark(){ - SpawnPoint point = world.getSpawns().first(); - int amount = 100; - - //warmup - for(int i = 0; i < 100; i ++){ - point.finder.searchNodePath(point.start, world.getCore(), state.difficulty.heuristic, point.path); - point.path.clear(); - } - - Timers.mark(); - for(int i = 0; i < amount; i ++){ - point.finder.searchNodePath(point.start, world.getCore(), state.difficulty.heuristic, point.path); - point.path.clear(); - } - Log.info("Time elapsed: {0}ms\nAverage MS per path: {1}", Timers.elapsed(), Timers.elapsed()/amount); - } - - /**Reset and clear the paths.*/ - public void resetPaths(){ - for(int i = 0; i < world.getSpawns().size; i ++){ - resetPathFor(world.getSpawns().get(i)); - } - } - - private void resetPathFor(SpawnPoint point){ - point.finder = new OptimizedPathFinder<>(graph); - - point.path.clear(); - - point.pathTiles = null; - - point.request = new PathFinderRequest<>(point.start, world.getCore(), state.difficulty.heuristic, point.path); - point.request.statusChanged = true; //IMPORTANT! - } - - /**For an enemy that was just loaded from a save, find the node in the path it should be following.*/ - void findNode(Enemy enemy){ - if(enemy.lane >= world.getSpawns().size || enemy.lane < 0){ - enemy.lane = 0; - } - - if(world.getSpawns().get(enemy.lane).pathTiles == null){ - return; - } - - Tile[] path = world.getSpawns().get(enemy.lane).pathTiles; - - int closest = findClosest(path, enemy.x, enemy.y); - - closest = Mathf.clamp(closest, 1, path.length-1); - if(closest == -1){ - return; - } - - enemy.node = closest; - } - - /**Finds the closest tile to a position, in an array of tiles.*/ - private int findClosest(Tile[] tiles, float x, float y){ - int cindex = -2; - float dst = Float.MAX_VALUE; - - for(int i = 0; i < tiles.length - 1; i ++){ - Tile tile = tiles[i]; - Tile next = tiles[i + 1]; - float d = pointLineDist(tile.worldx(), tile.worldy(), next.worldx(), next.worldy(), x, y); - if(d < dst){ - dst = d; - cindex = i; - } - } - - return cindex + 1; - } - - /**Returns whether a point is on a line.*/ - private boolean onLine(Vector2 vector, float x1, float y1, float x2, float y2){ - return MathUtils.isEqual(vector.dst(x1, y1) + vector.dst(x2, y2), Vector2.dst(x1, y1, x2, y2), 0.01f); - } - - /**Returns distance from a point to a line segment.*/ - private float pointLineDist(float x, float y, float x2, float y2, float px, float py){ - float l2 = Vector2.dst2(x, y, x2, y2); - float t = Math.max(0, Math.min(1, Vector2.dot(px - x, py - y, x2 - x, y2 - y) / l2)); - Vector2 projection = v1.set(x, y).add(v2.set(x2, y2).sub(x, y).scl(t)); // Projection falls on the segment - return projection.dst(px, py); - } - - //TODO documentation - private Vector2 projectPoint(float x1, float y1, float x2, float y2, float pointx, float pointy){ - float px = x2-x1, py = y2-y1, dAB = px*px + py*py; - float u = ((pointx - x1) * px + (pointy - y1) * py) / dAB; - float x = x1 + u * px, y = y1 + u * py; - return v3.set(x, y); //this is D - } -} diff --git a/core/src/io/anuke/mindustry/ai/Pathfinder.java b/core/src/io/anuke/mindustry/ai/Pathfinder.java new file mode 100644 index 0000000000..8700ec6761 --- /dev/null +++ b/core/src/io/anuke/mindustry/ai/Pathfinder.java @@ -0,0 +1,211 @@ +package io.anuke.mindustry.ai; + +import io.anuke.arc.Events; +import io.anuke.arc.collection.IntArray; +import io.anuke.arc.collection.IntQueue; +import io.anuke.arc.math.geom.Geometry; +import io.anuke.arc.math.geom.Point2; +import io.anuke.arc.util.*; +import io.anuke.mindustry.game.EventType.TileChangeEvent; +import io.anuke.mindustry.game.EventType.WorldLoadEvent; +import io.anuke.mindustry.game.Team; +import io.anuke.mindustry.game.Teams.TeamData; +import io.anuke.mindustry.net.Net; +import io.anuke.mindustry.world.Pos; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.meta.BlockFlag; + +import static io.anuke.mindustry.Vars.state; +import static io.anuke.mindustry.Vars.world; + +public class Pathfinder{ + private static final long maxUpdate = Time.millisToNanos(4); + private PathData[] paths; + private IntArray blocked = new IntArray(); + + public Pathfinder(){ + Events.on(WorldLoadEvent.class, event -> clear()); + Events.on(TileChangeEvent.class, event -> { + if(Net.client()) return; + + for(Team team : Team.all){ + TeamData data = state.teams.get(team); + if(state.teams.isActive(team) && data.team != event.tile.getTeam()){ + update(event.tile, data.team); + } + } + + update(event.tile, event.tile.getTeam()); + }); + } + + public void updateSolid(Tile tile){ + update(tile, tile.getTeam()); + } + + public void update(){ + if(Net.client() || paths == null) return; + + for(Team team : Team.all){ + if(state.teams.isActive(team)){ + updateFrontier(team, maxUpdate); + } + } + } + + public Tile getTargetTile(Team team, Tile tile){ + float[][] values = paths[team.ordinal()].weights; + + if(values == null || tile == null) return tile; + + float value = values[tile.x][tile.y]; + + Tile target = null; + float tl = 0f; + for(Point2 point : Geometry.d8){ + int dx = tile.x + point.x, dy = tile.y + point.y; + + Tile other = world.tile(dx, dy); + if(other == null) continue; + + if(values[dx][dy] < value && (target == null || values[dx][dy] < tl) && + !other.solid() && other.floor().drownTime <= 0 && + !(point.x != 0 && point.y != 0 && (world.solid(tile.x + point.x, tile.y) || world.solid(tile.x, tile.y + point.y)))){ //diagonal corner trap + target = other; + tl = values[dx][dy]; + } + } + + if(target == null || tl == Float.MAX_VALUE) return tile; + + return target; + } + + public float getValueforTeam(Team team, int x, int y){ + return paths == null || paths[team.ordinal()].weights == null || team.ordinal() >= paths.length ? 0 : Structs.inBounds(x, y, paths[team.ordinal()].weights) ? paths[team.ordinal()].weights[x][y] : 0; + } + + private boolean passable(Tile tile, Team team){ + return (!tile.solid()) || (tile.breakable() && (tile.getTeam() != team)); + } + + /** + * Clears the frontier, increments the search and sets up all flow sources. + * This only occurs for active teams. + */ + private void update(Tile tile, Team team){ + //make sure team exists + if(paths != null && paths[team.ordinal()] != null && paths[team.ordinal()].weights != null){ + PathData path = paths[team.ordinal()]; + + if(path.weights[tile.x][tile.y] <= 0.1f){ + //this was a previous target + path.frontier.clear(); + }else if(!path.frontier.isEmpty()){ + return; + } + + //impassable tiles have a weight of float.max + if(!passable(tile, team)){ + path.weights[tile.x][tile.y] = Float.MAX_VALUE; + } + + //increment search, clear frontier + path.search++; + path.frontier.clear(); + path.lastSearchTime = Time.millis(); + + //add all targets to the frontier + for(Tile other : world.indexer.getEnemy(team, BlockFlag.target)){ + path.weights[other.x][other.y] = 0; + path.searches[other.x][other.y] = (short)path.search; + path.frontier.addFirst(other.pos()); + } + } + } + + private void createFor(Team team){ + PathData path = new PathData(); + path.weights = new float[world.width()][world.height()]; + path.searches = new short[world.width()][world.height()]; + path.search++; + path.frontier.ensureCapacity((world.width() + world.height()) * 3); + + paths[team.ordinal()] = path; + + for(int x = 0; x < world.width(); x++){ + for(int y = 0; y < world.height(); y++){ + Tile tile = world.tile(x, y); + + if(state.teams.areEnemies(tile.getTeam(), team) + && tile.block().flags.contains(BlockFlag.target)){ + path.frontier.addFirst(tile.pos()); + path.weights[x][y] = 0; + path.searches[x][y] = (short)path.search; + }else{ + path.weights[x][y] = Float.MAX_VALUE; + } + } + } + + updateFrontier(team, -1); + } + + private void updateFrontier(Team team, long nsToRun){ + PathData path = paths[team.ordinal()]; + + long start = Time.nanos(); + + while(path.frontier.size > 0 && (nsToRun < 0 || Time.timeSinceNanos(start) <= nsToRun)){ + Tile tile = world.tile(path.frontier.removeLast()); + if(tile == null || path.weights == null) return; //something went horribly wrong, bail + float cost = path.weights[tile.x][tile.y]; + + //pathfinding overflowed for some reason, time to bail. the next block update will handle this, hopefully + if(path.frontier.size >= world.width() * world.height()){ + path.frontier.clear(); + return; + } + + if(cost < Float.MAX_VALUE){ + for(Point2 point : Geometry.d4){ + + int dx = tile.x + point.x, dy = tile.y + point.y; + Tile other = world.tile(dx, dy); + + if(other != null && (path.weights[dx][dy] > cost + other.cost || path.searches[dx][dy] < path.search) + && passable(other, team)){ + if(other.cost < 0) throw new IllegalArgumentException("Tile cost cannot be negative! " + other); + path.frontier.addFirst(Pos.get(dx, dy)); + path.weights[dx][dy] = cost + other.cost; + path.searches[dx][dy] = (short)path.search; + } + } + } + } + } + + private void clear(){ + Time.mark(); + + paths = new PathData[Team.all.length]; + blocked.clear(); + + for(Team team : Team.all){ + PathData path = new PathData(); + paths[team.ordinal()] = path; + + if(state.teams.isActive(team)){ + createFor(team); + } + } + } + + class PathData{ + float[][] weights; + short[][] searches; + int search = 0; + long lastSearchTime; + IntQueue frontier = new IntQueue(); + } +} diff --git a/core/src/io/anuke/mindustry/ai/Raycaster.java b/core/src/io/anuke/mindustry/ai/Raycaster.java deleted file mode 100644 index 6ce48805e0..0000000000 --- a/core/src/io/anuke/mindustry/ai/Raycaster.java +++ /dev/null @@ -1,87 +0,0 @@ -package io.anuke.mindustry.ai; - -import com.badlogic.gdx.ai.utils.Collision; -import com.badlogic.gdx.ai.utils.Ray; -import com.badlogic.gdx.ai.utils.RaycastCollisionDetector; -import com.badlogic.gdx.math.Vector2; -import io.anuke.mindustry.world.Tile; -import io.anuke.ucore.util.Geometry; -import io.anuke.ucore.util.Mathf; - -import static io.anuke.mindustry.Vars.tilesize; -import static io.anuke.mindustry.Vars.world; - -public class Raycaster implements RaycastCollisionDetector{ - private boolean found = false; - - @Override - public boolean collides(Ray ray){ - found = false; - - Geometry.iterateLine(0f, ray.start.x, ray.start.y, ray.end.x, ray.end.y, tilesize, (x, y)->{ - if(solid(x, y)){ - found = true; - return; - } - }); - - return found; - } - - @Override - public boolean findCollision(Collision collision, Ray ray){ - Vector2 v = vectorCast(ray.start.x, ray.start.y, ray.end.x, ray.end.y); - if(v == null) return false; - collision.point = v; - collision.normal = v.nor(); - return true; - } - - Vector2 vectorCast(float x0f, float y0f, float x1f, float y1f){ - int x0 = (int)x0f; - int y0 = (int)y0f; - int x1 = (int)x1f; - int y1 = (int)y1f; - int dx = Math.abs(x1 - x0); - int dy = Math.abs(y1 - y0); - - int sx = x0 < x1 ? 1 : -1; - int sy = y0 < y1 ? 1 : -1; - - int err = dx - dy; - int e2; - while(true){ - - if(solid(x0, y0)){ - return new Vector2(x0, y0); - } - if(x0 == x1 && y0 == y1) break; - - e2 = 2 * err; - if(e2 > -dy){ - err = err - dy; - x0 = x0 + sx; - } - - if(e2 < dx){ - err = err + dx; - y0 = y0 + sy; - } - } - return null; - } - - private boolean solid(float x, float y){ - Tile tile = world.tile(Mathf.scl2(x, tilesize), Mathf.scl2(y, tilesize)); - - if(tile == null || tile.solid()) return true; - - for(int i = 0; i < 4; i ++){ - Tile near = tile.getNearby(i); - if(near == null || near.solid()) return true; - } - - return false; - } - -} diff --git a/core/src/io/anuke/mindustry/ai/SmoothGraphPath.java b/core/src/io/anuke/mindustry/ai/SmoothGraphPath.java deleted file mode 100644 index 39b1b75ec2..0000000000 --- a/core/src/io/anuke/mindustry/ai/SmoothGraphPath.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.anuke.mindustry.ai; - -import com.badlogic.gdx.ai.pfa.DefaultGraphPath; -import com.badlogic.gdx.ai.pfa.SmoothableGraphPath; -import com.badlogic.gdx.math.Vector2; -import io.anuke.mindustry.world.Tile; - -public class SmoothGraphPath extends DefaultGraphPath implements SmoothableGraphPath{ - private Vector2 vector = new Vector2(); - - @Override - public Vector2 getNodePosition(int index){ - Tile tile = nodes.get(index); - return vector.set(tile.worldx(), tile.worldy()); - } - - @Override - public void swapNodes(int index1, int index2){ - nodes.swap(index1, index2); - } - - @Override - public void truncatePath(int newLength){ - nodes.truncate(newLength); - } - - @Override - public void add (Tile node) { - nodes.add(node); - } - -} diff --git a/core/src/io/anuke/mindustry/ai/TileGraph.java b/core/src/io/anuke/mindustry/ai/TileGraph.java deleted file mode 100644 index c4026d6ec7..0000000000 --- a/core/src/io/anuke/mindustry/ai/TileGraph.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.anuke.mindustry.ai; - -import io.anuke.mindustry.world.Tile; - -/**Tilegraph that ignores player-made tiles.*/ -public class TileGraph implements OptimizedGraph { - private Tile[] tiles = new Tile[4]; - - /**Used for the OptimizedPathFinder implementation.*/ - @Override - public Tile[] connectionsOf(Tile node){ - Tile[] nodes = node.getNearby(tiles); - for(int i = 0; i < 4; i ++){ - if(nodes[i] != null && !nodes[i].passable()){ - nodes[i] = null; - } - } - return nodes; - } - - @Override - public int getIndex(Tile node){ - return node.packedPosition(); - } -} diff --git a/core/src/io/anuke/mindustry/ai/WaveSpawner.java b/core/src/io/anuke/mindustry/ai/WaveSpawner.java new file mode 100644 index 0000000000..5863594f4f --- /dev/null +++ b/core/src/io/anuke/mindustry/ai/WaveSpawner.java @@ -0,0 +1,160 @@ +package io.anuke.mindustry.ai; + +import io.anuke.arc.Events; +import io.anuke.arc.collection.Array; +import io.anuke.arc.function.PositionConsumer; +import io.anuke.arc.math.Angles; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.util.Time; +import io.anuke.arc.util.Tmp; +import io.anuke.mindustry.content.Blocks; +import io.anuke.mindustry.content.Fx; +import io.anuke.mindustry.entities.Damage; +import io.anuke.mindustry.entities.Effects; +import io.anuke.mindustry.entities.type.BaseUnit; +import io.anuke.mindustry.game.EventType.WorldLoadEvent; +import io.anuke.mindustry.game.SpawnGroup; +import io.anuke.mindustry.net.Net; +import io.anuke.mindustry.world.Tile; + +import static io.anuke.mindustry.Vars.*; + +public class WaveSpawner{ + private static final float margin = 40f, coreMargin = tilesize * 3; //how far away from the edge flying units spawn + + private Array flySpawns = new Array<>(); + private Array groundSpawns = new Array<>(); + private boolean spawning = false; + + public WaveSpawner(){ + Events.on(WorldLoadEvent.class, e -> reset()); + } + + public int countSpawns(){ + return groundSpawns.size; + } + + public Array getGroundSpawns(){ + return groundSpawns; + } + + /** @return true if the player is near a ground spawn point. */ + public boolean playerNear(){ + return groundSpawns.contains(g -> Mathf.dst(g.x * tilesize, g.y * tilesize, player.x, player.y) < state.rules.dropZoneRadius); + } + + public void spawnEnemies(){ + spawning = true; + + for(SpawnGroup group : state.rules.spawns){ + int spawned = group.getUnitsSpawned(state.wave - 1); + + if(group.type.isFlying){ + float spread = margin / 1.5f; + + eachFlyerSpawn((spawnX, spawnY) -> { + for(int i = 0; i < spawned; i++){ + BaseUnit unit = group.createUnit(waveTeam); + unit.set(spawnX + Mathf.range(spread), spawnY + Mathf.range(spread)); + unit.add(); + } + }); + }else{ + float spread = tilesize * 2; + + eachGroundSpawn((spawnX, spawnY, doShockwave) -> { + + for(int i = 0; i < spawned; i++){ + Tmp.v1.rnd(spread); + + BaseUnit unit = group.createUnit(waveTeam); + unit.set(spawnX + Tmp.v1.x, spawnY + Tmp.v1.y); + + Time.run(Math.min(i * 5, 60 * 2), () -> spawnEffect(unit)); + } + }); + } + } + + eachGroundSpawn((spawnX, spawnY, doShockwave) -> { + if(doShockwave){ + Time.run(20f, () -> Effects.effect(Fx.spawnShockwave, spawnX, spawnY, state.rules.dropZoneRadius)); + Time.run(40f, () -> Damage.damage(waveTeam, spawnX, spawnY, state.rules.dropZoneRadius, 99999999f, true)); + } + }); + + Time.runTask(121f, () -> spawning = false); + } + + private void eachGroundSpawn(SpawnConsumer cons){ + for(Tile spawn : groundSpawns){ + cons.accept(spawn.worldx(), spawn.worldy(), true); + } + + if(state.rules.attackMode && state.teams.isActive(waveTeam) && !state.teams.get(defaultTeam).cores.isEmpty()){ + Tile firstCore = state.teams.get(defaultTeam).cores.first(); + for(Tile core : state.teams.get(waveTeam).cores){ + Tmp.v1.set(firstCore).sub(core.worldx(), core.worldy()).limit(coreMargin + core.block().size*tilesize); + cons.accept(core.worldx() + Tmp.v1.x, core.worldy() + Tmp.v1.y, false); + } + } + } + + private void eachFlyerSpawn(PositionConsumer cons){ + for(FlyerSpawn spawn : flySpawns){ + float trns = (world.width() + world.height()) * tilesize; + float spawnX = Mathf.clamp(world.width() * tilesize / 2f + Angles.trnsx(spawn.angle, trns), -margin, world.width() * tilesize + margin); + float spawnY = Mathf.clamp(world.height() * tilesize / 2f + Angles.trnsy(spawn.angle, trns), -margin, world.height() * tilesize + margin); + cons.accept(spawnX, spawnY); + } + + if(state.rules.attackMode && state.teams.isActive(waveTeam)){ + for(Tile core : state.teams.get(waveTeam).cores){ + cons.accept(core.worldx(), core.worldy()); + } + } + } + + public boolean isSpawning(){ + return spawning && !Net.client(); + } + + private void reset(){ + + flySpawns.clear(); + groundSpawns.clear(); + + for(int x = 0; x < world.width(); x++){ + for(int y = 0; y < world.height(); y++){ + + if(world.tile(x, y).overlay() == Blocks.spawn){ + addSpawns(x, y); + } + } + } + } + + private void addSpawns(int x, int y){ + groundSpawns.add(world.tile(x, y)); + + FlyerSpawn fspawn = new FlyerSpawn(); + fspawn.angle = Angles.angle(world.width() / 2f, world.height() / 2f, x, y); + flySpawns.add(fspawn); + } + + private void spawnEffect(BaseUnit unit){ + Effects.effect(Fx.unitSpawn, unit.x, unit.y, 0f, unit); + Time.run(30f, () -> { + unit.add(); + Effects.effect(Fx.spawn, unit); + }); + } + + private interface SpawnConsumer{ + void accept(float x, float y, boolean shockwave); + } + + private class FlyerSpawn{ + float angle; + } +} diff --git a/core/src/io/anuke/mindustry/content/Blocks.java b/core/src/io/anuke/mindustry/content/Blocks.java new file mode 100644 index 0000000000..3b9ea32a3b --- /dev/null +++ b/core/src/io/anuke/mindustry/content/Blocks.java @@ -0,0 +1,1748 @@ +package io.anuke.mindustry.content; + +import io.anuke.arc.Core; +import io.anuke.arc.function.BooleanProvider; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.graphics.g2d.*; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.util.Tmp; +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.entities.Damage; +import io.anuke.mindustry.entities.bullet.Bullet; +import io.anuke.mindustry.entities.bullet.BulletType; +import io.anuke.mindustry.game.ContentList; +import io.anuke.mindustry.graphics.*; +import io.anuke.mindustry.type.*; +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.*; +import io.anuke.mindustry.world.blocks.defense.*; +import io.anuke.mindustry.world.blocks.defense.turrets.*; +import io.anuke.mindustry.world.blocks.distribution.*; +import io.anuke.mindustry.world.blocks.power.*; +import io.anuke.mindustry.world.blocks.production.*; +import io.anuke.mindustry.world.blocks.sandbox.*; +import io.anuke.mindustry.world.blocks.storage.*; +import io.anuke.mindustry.world.blocks.units.*; +import io.anuke.mindustry.world.consumers.ConsumeLiquidFilter; +import io.anuke.mindustry.world.meta.Attribute; +import io.anuke.mindustry.world.modules.LiquidModule; + +import static io.anuke.mindustry.Vars.state; +import static io.anuke.mindustry.Vars.world; + +public class Blocks implements ContentList{ + public static Block + + //environment + air, spawn, deepwater, water, taintedWater, tar, stone, craters, charr, sand, darksand, ice, snow, darksandTaintedWater, + holostone, rocks, sporerocks, icerocks, cliffs, sporePine, pine, shrubs, whiteTree, whiteTreeDead, sporeCluster, + iceSnow, sandWater, darksandWater, duneRocks, sandRocks, moss, sporeMoss, shale, shaleRocks, shaleBoulder, grass, salt, + metalFloor, metalFloorDamaged, metalFloor2, metalFloor3, metalFloor5, ignarock, magmarock, hotrock, snowrocks, rock, snowrock, saltRocks, + darkPanel1, darkPanel2, darkPanel3, darkPanel4, darkPanel5, darkPanel6, darkMetal, + pebbles, tendrils, + + //ores + oreCopper, oreLead, oreScrap, oreCoal, oreTitanium, oreThorium, + + //crafting + siliconSmelter, kiln, graphitePress, plastaniumCompressor, multiPress, phaseWeaver, surgeSmelter, pyratiteMixer, blastMixer, cryofluidMixer, + melter, separator, sporePress, pulverizer, incinerator, coalCentrifuge, + + //sandbox + powerVoid, powerSource, itemSource, liquidSource, itemVoid, + + //defense + scrapWall, scrapWallLarge, scrapWallHuge, scrapWallGigantic, thruster, //ok, these names are getting ridiculous, but at least I don't have humongous walls yet + copperWall, copperWallLarge, titaniumWall, titaniumWallLarge, thoriumWall, thoriumWallLarge, door, doorLarge, + phaseWall, phaseWallLarge, surgeWall, surgeWallLarge, mender, mendProjector, overdriveProjector, forceProjector, shockMine, + + //transport + conveyor, titaniumConveyor, distributor, junction, itemBridge, phaseConveyor, sorter, router, overflowGate, massDriver, + + //liquids + mechanicalPump, rotaryPump, thermalPump, conduit, pulseConduit, liquidRouter, liquidTank, liquidJunction, bridgeConduit, phaseConduit, + + //power + combustionGenerator, thermalGenerator, turbineGenerator, differentialGenerator, rtgGenerator, solarPanel, largeSolarPanel, thoriumReactor, + impactReactor, battery, batteryLarge, powerNode, powerNodeLarge, surgeTower, + + //production + mechanicalDrill, pneumaticDrill, laserDrill, blastDrill, waterExtractor, oilExtractor, cultivator, + + //storage + coreShard, coreFoundation, coreNucleus, vault, container, unloader, launchPad, launchPadLarge, + + //turrets + duo, scatter, scorch, hail, arc, wave, lancer, swarmer, salvo, fuse, ripple, cyclone, spectre, meltdown, + + //units + draugFactory, spiritFactory, phantomFactory, wraithFactory, ghoulFactory, revenantFactory, daggerFactory, crawlerFactory, titanFactory, + fortressFactory, repairPoint, + + //upgrades + dartPad, deltaPad, tauPad, omegaPad, javelinPad, tridentPad, glaivePad; + + @Override + public void load(){ + //region environment + + air = new Floor("air"){ + { + alwaysReplace = true; + hasShadow = false; + } + + public void draw(Tile tile){} + public void load(){} + public void init(){} + public boolean isHidden(){ + return true; + } + + public TextureRegion[] variantRegions(){ + if(variantRegions == null){ + variantRegions = new TextureRegion[]{Core.atlas.find("clear")}; + } + return variantRegions; + } + }; + + //create special blockpart variants + for(int dx = 0; dx < BlockPart.maxSize; dx++){ + for(int dy = 0; dy < BlockPart.maxSize; dy++){ + int fx = dx - BlockPart.maxSize/2, fy = dy - BlockPart.maxSize/2; + if(fx != 0 || fy != 0){ + new BlockPart(fx, fy); + } + } + } + + spawn = new OverlayFloor("spawn"){ + { + variants = 0; + } + public void draw(Tile tile){} + }; + + //Registers build blocks + //no reference is needed here since they can be looked up by name later + for(int i = 1; i <= BuildBlock.maxSize; i++){ + new BuildBlock(i); + } + + deepwater = new Floor("deepwater"){{ + speedMultiplier = 0.2f; + variants = 0; + liquidDrop = Liquids.water; + isLiquid = true; + status = StatusEffects.wet; + statusDuration = 120f; + drownTime = 140f; + cacheLayer = CacheLayer.water; + }}; + + water = new Floor("water"){{ + speedMultiplier = 0.5f; + variants = 0; + status = StatusEffects.wet; + statusDuration = 90f; + liquidDrop = Liquids.water; + isLiquid = true; + cacheLayer = CacheLayer.water; + }}; + + taintedWater = new Floor("tainted-water"){{ + speedMultiplier = 0.17f; + variants = 0; + status = StatusEffects.wet; + statusDuration = 140f; + drownTime = 120f; + liquidDrop = Liquids.water; + isLiquid = true; + cacheLayer = CacheLayer.water; + }}; + + darksandTaintedWater = new Floor("darksand-tainted-water"){{ + speedMultiplier = 0.75f; + variants = 0; + status = StatusEffects.wet; + statusDuration = 60f; + liquidDrop = Liquids.water; + isLiquid = true; + cacheLayer = CacheLayer.water; + }}; + + sandWater = new Floor("sand-water"){{ + speedMultiplier = 0.8f; + variants = 0; + status = StatusEffects.wet; + statusDuration = 50f; + liquidDrop = Liquids.water; + isLiquid = true; + cacheLayer = CacheLayer.water; + }}; + + darksandWater = new Floor("darksand-water"){{ + speedMultiplier = 0.8f; + variants = 0; + status = StatusEffects.wet; + statusDuration = 50f; + liquidDrop = Liquids.water; + isLiquid = true; + cacheLayer = CacheLayer.water; + }}; + + tar = new Floor("tar"){{ + drownTime = 150f; + status = StatusEffects.tarred; + statusDuration = 240f; + speedMultiplier = 0.19f; + variants = 0; + liquidDrop = Liquids.oil; + isLiquid = true; + cacheLayer = CacheLayer.tar; + }}; + + stone = new Floor("stone"){{ + + }}; + + craters = new Floor("craters"){{ + variants = 3; + blendGroup = stone; + }}; + + charr = new Floor("char"){{ + blendGroup = stone; + }}; + + ignarock = new Floor("ignarock"){{ + + }}; + + hotrock = new Floor("hotrock"){{ + attributes.set(Attribute.heat, 0.5f); + blendGroup = ignarock; + }}; + + magmarock = new Floor("magmarock"){{ + attributes.set(Attribute.heat, 0.75f); + updateEffect = Fx.magmasmoke; + blendGroup = ignarock; + }}; + + sand = new Floor("sand"){{ + itemDrop = Items.sand; + playerUnmineable = true; + }}; + + darksand = new Floor("darksand"){{ + itemDrop = Items.sand; + playerUnmineable = true; + }}; + + holostone = new Floor("holostone"){{ + + }}; + + grass = new Floor("grass"){{ + + }}; + + salt = new Floor("salt"){{ + variants = 0; + }}; + + snow = new Floor("snow"){{ + attributes.set(Attribute.water, 0.2f); + }}; + + ice = new Floor("ice"){{ + //TODO fix drag/speed + dragMultiplier = 1f; + speedMultiplier = 1f; + attributes.set(Attribute.water, 0.4f); + }}; + + iceSnow = new Floor("ice-snow"){{ + variants = 3; + attributes.set(Attribute.water, 0.3f); + }}; + + cliffs = new StaticWall("cliffs"){{ + variants = 1; + fillsTile = false; + }}; + + rocks = new StaticWall("rocks"){{ + variants = 2; + }}; + + sporerocks = new StaticWall("sporerocks"){{ + variants = 2; + }}; + + rock = new Rock("rock"){{ + variants = 2; + }}; + + snowrock = new Rock("snowrock"){{ + variants = 2; + }}; + + icerocks = new StaticWall("icerocks"){{ + variants = 2; + }}; + + snowrocks = new StaticWall("snowrocks"){{ + variants = 2; + }}; + + duneRocks = new StaticWall("dunerocks"){{ + variants = 2; + }}; + + sandRocks = new StaticWall("sandrocks"){{ + variants = 2; + }}; + + saltRocks = new StaticWall("saltrocks"){{ + }}; + + sporePine = new StaticWall("spore-pine"){{ + variants = 0; + }}; + + pine = new StaticWall("pine"){{ + variants = 0; + }}; + + shrubs = new StaticWall("shrubs"){{ + + }}; + + whiteTreeDead = new TreeBlock("white-tree-dead"){{ + }}; + + whiteTree = new TreeBlock("white-tree"){{ + }}; + + sporeCluster = new Rock("spore-cluster"){{ + variants = 3; + }}; + + shale = new Floor("shale"){{ + variants = 3; + attributes.set(Attribute.oil, 0.15f); + }}; + + shaleRocks = new StaticWall("shalerocks"){{ + variants = 2; + }}; + + shaleBoulder = new Rock("shale-boulder"){{ + variants = 2; + }}; + + moss = new Floor("moss"){{ + variants = 3; + attributes.set(Attribute.spores, 0.15f); + }}; + + sporeMoss = new Floor("spore-moss"){{ + variants = 3; + attributes.set(Attribute.spores, 0.3f); + }}; + + metalFloor = new Floor("metal-floor"){{ + variants = 0; + }}; + + metalFloorDamaged = new Floor("metal-floor-damaged"){{ + variants = 3; + }}; + + metalFloor2 = new Floor("metal-floor-2"){{ + variants = 0; + }}; + + metalFloor3 = new Floor("metal-floor-3"){{ + variants = 0; + }}; + + metalFloor5 = new Floor("metal-floor-5"){{ + variants = 0; + }}; + + darkPanel1 = new Floor("dark-panel-1"){{ variants = 0; }}; + darkPanel2 = new Floor("dark-panel-2"){{ variants = 0; }}; + darkPanel3 = new Floor("dark-panel-3"){{ variants = 0; }}; + darkPanel4 = new Floor("dark-panel-4"){{ variants = 0; }}; + darkPanel5 = new Floor("dark-panel-5"){{ variants = 0; }}; + darkPanel6 = new Floor("dark-panel-6"){{ variants = 0; }}; + + darkMetal = new StaticWall("dark-metal"); + + pebbles = new DoubleOverlayFloor("pebbles"); + + tendrils = new OverlayFloor("tendrils"); + + //endregion + //region ore + + oreCopper = new OreBlock(Items.copper); + oreLead = new OreBlock(Items.lead); + oreScrap = new OreBlock(Items.scrap); + oreCoal = new OreBlock(Items.coal); + oreTitanium = new OreBlock(Items.titanium); + oreThorium = new OreBlock(Items.thorium); + + //endregion + //region crafting + + graphitePress = new GenericCrafter("graphite-press"){{ + requirements(Category.crafting, ItemStack.with(Items.copper, 150, Items.lead, 60)); + + craftEffect = Fx.pulverizeMedium; + outputItem = new ItemStack(Items.graphite, 1); + craftTime = 90f; + size = 2; + hasItems = true; + + consumes.item(Items.coal, 2); + }}; + + multiPress = new GenericCrafter("multi-press"){{ + requirements(Category.crafting, ItemStack.with(Items.titanium, 200, Items.silicon, 50, Items.lead, 200, Items.graphite, 100)); + + craftEffect = Fx.pulverizeMedium; + outputItem = new ItemStack(Items.graphite, 2); + craftTime = 30f; + size = 3; + hasItems = true; + hasLiquids = true; + hasPower = true; + + consumes.power(1.8f); + consumes.item(Items.coal, 3); + consumes.liquid(Liquids.water, 0.1f); + }}; + + siliconSmelter = new GenericSmelter("silicon-smelter"){{ + requirements(Category.crafting, ItemStack.with(Items.copper, 60, Items.lead, 50)); + craftEffect = Fx.smeltsmoke; + outputItem = new ItemStack(Items.silicon, 1); + craftTime = 40f; + size = 2; + hasPower = true; + hasLiquids = false; + flameColor = Color.valueOf("ffef99"); + + consumes.items(new ItemStack(Items.coal, 1), new ItemStack(Items.sand, 2)); + consumes.power(0.50f); + }}; + + kiln = new GenericSmelter("kiln"){{ + requirements(Category.crafting, ItemStack.with(Items.copper, 120, Items.graphite, 60, Items.lead, 60)); + craftEffect = Fx.smeltsmoke; + outputItem = new ItemStack(Items.metaglass, 1); + craftTime = 30f; + size = 2; + hasPower = hasItems = true; + flameColor = Color.valueOf("ffc099"); + + consumes.items(new ItemStack(Items.lead, 1), new ItemStack(Items.sand, 1)); + consumes.power(0.60f); + }}; + + plastaniumCompressor = new GenericCrafter("plastanium-compressor"){{ + requirements(Category.crafting, ItemStack.with(Items.silicon, 160, Items.lead, 230, Items.graphite, 120, Items.titanium, 160)); + hasItems = true; + liquidCapacity = 60f; + craftTime = 60f; + outputItem = new ItemStack(Items.plastanium, 1); + size = 2; + health = 320; + hasPower = hasLiquids = true; + craftEffect = Fx.formsmoke; + updateEffect = Fx.plasticburn; + + consumes.liquid(Liquids.oil, 0.25f); + consumes.power(3f); + consumes.item(Items.titanium, 2); + + int topRegion = reg("-top"); + + drawer = tile -> { + Draw.rect(region, tile.drawx(), tile.drawy()); + + GenericCrafterEntity entity = tile.entity(); + + Draw.alpha(Mathf.absin(entity.totalProgress, 3f, 0.9f) * entity.warmup); + Draw.rect(reg(topRegion), tile.drawx(), tile.drawy()); + Draw.reset(); + }; + }}; + + phaseWeaver = new GenericCrafter("phase-weaver"){{ + requirements(Category.crafting, ItemStack.with(Items.silicon, 260, Items.lead, 240, Items.thorium, 150)); + craftEffect = Fx.smeltsmoke; + outputItem = new ItemStack(Items.phasefabric, 1); + craftTime = 120f; + size = 2; + hasPower = true; + + consumes.items(new ItemStack(Items.thorium, 4), new ItemStack(Items.sand, 10)); + consumes.power(5f); + + int bottomRegion = reg("-bottom"), weaveRegion = reg("-weave"); + + drawIcons = () -> new TextureRegion[]{Core.atlas.find(name + "-bottom"), Core.atlas.find(name)}; + + drawer = tile -> { + GenericCrafterEntity entity = tile.entity(); + + Draw.rect(reg(bottomRegion), tile.drawx(), tile.drawy()); + Draw.rect(reg(weaveRegion), tile.drawx(), tile.drawy(), entity.totalProgress); + + Draw.color(Pal.accent); + Draw.alpha(entity.warmup); + + Lines.lineAngleCenter( + tile.drawx() + Mathf.sin(entity.totalProgress, 6f, Vars.tilesize / 3f * size), + tile.drawy(), + 90, + size * Vars.tilesize / 2f); + + Draw.reset(); + + Draw.rect(region, tile.drawx(), tile.drawy()); + }; + }}; + + surgeSmelter = new GenericSmelter("alloy-smelter"){{ + requirements(Category.crafting, ItemStack.with(Items.silicon, 160, Items.lead, 160, Items.thorium, 140)); + craftEffect = Fx.smeltsmoke; + outputItem = new ItemStack(Items.surgealloy, 1); + craftTime = 75f; + size = 3; + hasPower = true; + + consumes.power(4f); + consumes.items(new ItemStack(Items.titanium, 2), new ItemStack(Items.lead, 4), new ItemStack(Items.silicon, 3), new ItemStack(Items.copper, 3)); + }}; + + cryofluidMixer = new LiquidConverter("cryofluidmixer"){{ + requirements(Category.crafting, ItemStack.with(Items.lead, 130, Items.silicon, 80, Items.thorium, 90)); + outputLiquid = new LiquidStack(Liquids.cryofluid, 0.1f); + craftTime = 60f; + size = 2; + hasPower = true; + hasItems = true; + hasLiquids = true; + rotate = false; + solid = true; + outputsLiquid = true; + + consumes.power(1f); + consumes.item(Items.titanium); + consumes.liquid(Liquids.water, 0.1f); + + int liquidRegion = reg("-liquid"), topRegion = reg("-top"), bottomRegion = reg("-bottom"); + + drawIcons = () -> new TextureRegion[]{Core.atlas.find(name + "-bottom"), Core.atlas.find(name + "-top")}; + + drawer = tile -> { + LiquidModule mod = tile.entity.liquids; + + int rotation = rotate ? tile.rotation() * 90 : 0; + + Draw.rect(reg(bottomRegion), tile.drawx(), tile.drawy(), rotation); + + if(mod.total() > 0.001f){ + Draw.color(outputLiquid.liquid.color); + Draw.alpha(mod.get(outputLiquid.liquid) / liquidCapacity); + Draw.rect(reg(liquidRegion), tile.drawx(), tile.drawy(), rotation); + Draw.color(); + } + + Draw.rect(reg(topRegion), tile.drawx(), tile.drawy(), rotation); + }; + }}; + + blastMixer = new GenericCrafter("blast-mixer"){{ + requirements(Category.crafting, ItemStack.with(Items.lead, 60, Items.titanium, 40)); + hasItems = true; + hasPower = true; + outputItem = new ItemStack(Items.blastCompound, 1); + size = 2; + + consumes.items(new ItemStack(Items.pyratite, 1), new ItemStack(Items.sporePod, 1)); + consumes.power(0.40f); + }}; + + pyratiteMixer = new GenericSmelter("pyratite-mixer"){{ + requirements(Category.crafting, ItemStack.with(Items.copper, 100, Items.lead, 50)); + flameColor = Color.CLEAR; + hasItems = true; + hasPower = true; + outputItem = new ItemStack(Items.pyratite, 1); + + size = 2; + + consumes.power(0.20f); + consumes.items(new ItemStack(Items.coal, 1), new ItemStack(Items.lead, 2), new ItemStack(Items.sand, 2)); + }}; + + melter = new GenericCrafter("melter"){{ + requirements(Category.crafting, ItemStack.with(Items.copper, 60, Items.lead, 70, Items.graphite, 90)); + health = 200; + outputLiquid = new LiquidStack(Liquids.slag, 2f); + craftTime = 10f; + hasLiquids = hasPower = true; + + consumes.power(1f); + consumes.item(Items.scrap, 1); + }}; + + separator = new Separator("separator"){{ + requirements(Category.crafting, ItemStack.with(Items.copper, 60, Items.titanium, 50)); + results = ItemStack.with( + Items.copper, 5, + Items.lead, 3, + Items.graphite, 2, + Items.titanium, 2 + ); + hasPower = true; + craftTime = 35f; + spinnerLength = 1.5f; + spinnerRadius = 3.5f; + spinnerThickness = 1.5f; + spinnerSpeed = 3f; + size = 2; + + consumes.power(1f); + consumes.liquid(Liquids.slag, 0.07f); + }}; + + sporePress = new GenericCrafter("spore-press"){{ + requirements(Category.crafting, ItemStack.with(Items.lead, 70, Items.silicon, 60)); + liquidCapacity = 60f; + craftTime = 20f; + outputLiquid = new LiquidStack(Liquids.oil, 6f); + size = 2; + health = 320; + hasLiquids = true; + hasPower = true; + craftEffect = Fx.none; + + consumes.item(Items.sporePod, 1); + consumes.power(0.60f); + + int[] frameRegions = new int[3]; + for(int i = 0; i < 3; i++){ + frameRegions[i] = reg("-frame" + i); + } + + int liquidRegion = reg("-liquid"); + int topRegion = reg("-top"); + + drawIcons = () -> new TextureRegion[]{Core.atlas.find(name), Core.atlas.find(name + "-top")}; + + drawer = tile -> { + GenericCrafterEntity entity = tile.entity(); + + Draw.rect(region, tile.drawx(), tile.drawy()); + Draw.rect(reg(frameRegions[(int)Mathf.absin(entity.totalProgress, 5f, 2.999f)]), tile.drawx(), tile.drawy()); + Draw.color(Color.CLEAR, tile.entity.liquids.current().color, tile.entity.liquids.total() / liquidCapacity); + Draw.rect(reg(liquidRegion), tile.drawx(), tile.drawy()); + Draw.color(); + Draw.rect(reg(topRegion), tile.drawx(), tile.drawy()); + }; + }}; + + pulverizer = new GenericCrafter("pulverizer"){{ + requirements(Category.crafting, ItemStack.with(Items.copper, 60, Items.lead, 50)); + outputItem = new ItemStack(Items.sand, 1); + craftEffect = Fx.pulverize; + craftTime = 40f; + updateEffect = Fx.pulverizeSmall; + hasItems = hasPower = true; + + consumes.item(Items.scrap, 1); + consumes.power(0.50f); + + int rotatorRegion = reg("-rotator"); + + drawIcons = () -> new TextureRegion[]{Core.atlas.find(name), Core.atlas.find(name + "-rotator")}; + + drawer = tile -> { + GenericCrafterEntity entity = tile.entity(); + + Draw.rect(region, tile.drawx(), tile.drawy()); + Draw.rect(reg(rotatorRegion), tile.drawx(), tile.drawy(), entity.totalProgress * 2f); + }; + }}; + + coalCentrifuge = new GenericCrafter("coal-centrifuge"){{ + requirements(Category.crafting, ItemStack.with(Items.titanium, 40, Items.graphite, 80, Items.lead, 60)); + craftEffect = Fx.smeltsmoke; + outputItem = new ItemStack(Items.coal, 1); + craftTime = 30f; + size = 2; + hasPower = hasItems = hasLiquids = true; + + consumes.liquid(Liquids.oil, 0.09f); + consumes.power(0.5f); + }}; + + incinerator = new Incinerator("incinerator"){{ + requirements(Category.crafting, ItemStack.with(Items.graphite, 10, Items.lead, 30)); + health = 90; + consumes.power(0.50f); + }}; + + //endregion + //region sandbox + + powerVoid = new PowerVoid("power-void"){{ + requirements(Category.power, () -> state.rules.infiniteResources, ItemStack.with()); + alwaysUnlocked = true; + }}; + powerSource = new PowerSource("power-source"){{ + requirements(Category.power, () -> state.rules.infiniteResources, ItemStack.with()); + alwaysUnlocked = true; + }}; + itemSource = new ItemSource("item-source"){{ + requirements(Category.distribution, () -> state.rules.infiniteResources, ItemStack.with()); + alwaysUnlocked = true; + }}; + itemVoid = new ItemVoid("item-void"){{ + requirements(Category.distribution, () -> state.rules.infiniteResources, ItemStack.with()); + alwaysUnlocked = true; + }}; + liquidSource = new LiquidSource("liquid-source"){{ + requirements(Category.liquid, () -> state.rules.infiniteResources, ItemStack.with()); + alwaysUnlocked = true; + }}; + + //endregion + //region defense + + int wallHealthMultiplier = 4; + + scrapWall = new Wall("scrap-wall"){{ + health = 60 * wallHealthMultiplier; + variants = 5; + }}; + + scrapWallLarge = new Wall("scrap-wall-large"){{ + health = 60 * 4 * wallHealthMultiplier; + size = 2; + variants = 4; + }}; + + scrapWallHuge = new Wall("scrap-wall-huge"){{ + health = 60 * 9 * wallHealthMultiplier; + size = 3; + variants = 3; + }}; + + scrapWallGigantic = new Wall("scrap-wall-gigantic"){{ + health = 60 * 16 * wallHealthMultiplier; + size = 4; + }}; + + thruster = new Wall("thruster"){{ + health = 55 * 16 * wallHealthMultiplier; + size = 4; + }}; + + copperWall = new Wall("copper-wall"){{ + requirements(Category.defense, ItemStack.with(Items.copper, 12)); + health = 80 * wallHealthMultiplier; + }}; + + copperWallLarge = new Wall("copper-wall-large"){{ + requirements(Category.defense, ItemStack.with(Items.copper, 12 * 4)); + health = 80 * 4 * wallHealthMultiplier; + size = 2; + }}; + + titaniumWall = new Wall("titanium-wall"){{ + requirements(Category.defense, ItemStack.with(Items.titanium, 12)); + health = 110 * wallHealthMultiplier; + }}; + + titaniumWallLarge = new Wall("titanium-wall-large"){{ + requirements(Category.defense, ItemStack.with(Items.titanium, 12 * 4)); + health = 110 * wallHealthMultiplier * 4; + size = 2; + }}; + + thoriumWall = new Wall("thorium-wall"){{ + requirements(Category.defense, ItemStack.with(Items.thorium, 12)); + health = 200 * wallHealthMultiplier; + }}; + + thoriumWallLarge = new Wall("thorium-wall-large"){{ + requirements(Category.defense, ItemStack.with(Items.thorium, 12 * 4)); + health = 200 * wallHealthMultiplier * 4; + size = 2; + }}; + + phaseWall = new DeflectorWall("phase-wall"){{ + requirements(Category.defense, ItemStack.with(Items.phasefabric, 12)); + health = 150 * wallHealthMultiplier; + }}; + + phaseWallLarge = new DeflectorWall("phase-wall-large"){{ + requirements(Category.defense, ItemStack.with(Items.phasefabric, 12 * 4)); + health = 150 * 4 * wallHealthMultiplier; + size = 2; + }}; + + surgeWall = new SurgeWall("surge-wall"){{ + requirements(Category.defense, ItemStack.with(Items.surgealloy, 12)); + health = 230 * wallHealthMultiplier; + }}; + + surgeWallLarge = new SurgeWall("surge-wall-large"){{ + requirements(Category.defense, ItemStack.with(Items.surgealloy, 12 * 4)); + health = 230 * 4 * wallHealthMultiplier; + size = 2; + }}; + + door = new Door("door"){{ + requirements(Category.defense, ItemStack.with(Items.titanium, 12, Items.silicon, 8)); + health = 100 * wallHealthMultiplier; + }}; + + doorLarge = new Door("door-large"){{ + requirements(Category.defense, ItemStack.with(Items.titanium, 12 * 4, Items.silicon, 8 * 4)); + openfx = Fx.dooropenlarge; + closefx = Fx.doorcloselarge; + health = 100 * 4 * wallHealthMultiplier; + size = 2; + }}; + + mender = new MendProjector("mender"){{ + requirements(Category.effect, ItemStack.with(Items.lead, 60, Items.copper, 50)); + consumes.power(0.3f); + size = 1; + reload = 200f; + range = 40f; + healPercent = 4f; + phaseBoost = 4f; + phaseRangeBoost = 20f; + health = 80; + consumes.item(Items.silicon).boost(); + }}; + + mendProjector = new MendProjector("mend-projector"){{ + requirements(Category.effect, ItemStack.with(Items.lead, 200, Items.titanium, 50, Items.silicon, 80)); + consumes.power(1.5f); + size = 2; + reload = 250f; + range = 85f; + healPercent = 14f; + health = 80 * size * size; + consumes.item(Items.phasefabric).boost(); + }}; + + overdriveProjector = new OverdriveProjector("overdrive-projector"){{ + requirements(Category.effect, ItemStack.with(Items.lead, 200, Items.titanium, 150, Items.silicon, 150, Items.plastanium, 60)); + consumes.power(3.50f); + size = 2; + consumes.item(Items.phasefabric).boost(); + }}; + + forceProjector = new ForceProjector("force-projector"){{ + requirements(Category.effect, ItemStack.with(Items.lead, 200, Items.titanium, 150, Items.silicon, 250)); + size = 3; + consumes.item(Items.phasefabric).boost(); + consumes.power(3f); + }}; + + shockMine = new ShockMine("shock-mine"){{ + requirements(Category.effect, ItemStack.with(Items.lead, 50, Items.silicon, 25)); + hasShadow = false; + health = 40; + damage = 11; + tileDamage = 7f; + length = 10; + tendrils = 5; + }}; + + //endregion + //region distribution + + conveyor = new Conveyor("conveyor"){{ + requirements(Category.distribution, ItemStack.with(Items.copper, 1), true); + health = 45; + speed = 0.03f; + }}; + + titaniumConveyor = new Conveyor("titanium-conveyor"){{ + requirements(Category.distribution, ItemStack.with(Items.copper, 2, Items.lead, 1, Items.titanium, 1)); + health = 65; + speed = 0.08f; + }}; + + junction = new Junction("junction"){{ + requirements(Category.distribution, ItemStack.with(Items.copper, 3), true); + speed = 26; + capacity = 15; + health = 30; + }}; + + itemBridge = new BufferedItemBridge("bridge-conveyor"){{ + requirements(Category.distribution, ItemStack.with(Items.lead, 8, Items.copper, 8)); + range = 4; + speed = 70f; + bufferCapacity = 15; + }}; + + phaseConveyor = new ItemBridge("phase-conveyor"){{ + requirements(Category.distribution, ItemStack.with(Items.phasefabric, 10, Items.silicon, 15, Items.lead, 20, Items.graphite, 20)); + range = 12; + hasPower = true; + consumes.power(0.30f); + }}; + + sorter = new Sorter("sorter"){{ + requirements(Category.distribution, ItemStack.with(Items.lead, 4, Items.copper, 4)); + + }}; + + router = new Router("router"){{ + requirements(Category.distribution, ItemStack.with(Items.copper, 6)); + + }}; + + distributor = new Router("distributor"){{ + requirements(Category.distribution, ItemStack.with(Items.lead, 8, Items.copper, 8)); + size = 2; + }}; + + overflowGate = new OverflowGate("overflow-gate"){{ + requirements(Category.distribution, ItemStack.with(Items.lead, 4, Items.copper, 8)); + }}; + + massDriver = new MassDriver("mass-driver"){{ + requirements(Category.distribution, ItemStack.with(Items.titanium, 250, Items.silicon, 150, Items.lead, 250, Items.thorium, 100)); + size = 3; + itemCapacity = 120; + reloadTime = 200f; + range = 440f; + consumes.power(1.75f); + }}; + + //endregion + //region liquid + + mechanicalPump = new Pump("mechanical-pump"){{ + requirements(Category.liquid, ItemStack.with(Items.copper, 30, Items.lead, 20)); + pumpAmount = 0.1f; + }}; + + rotaryPump = new Pump("rotary-pump"){{ + requirements(Category.liquid, ItemStack.with(Items.copper, 140, Items.lead, 100, Items.silicon, 40, Items.titanium, 70)); + pumpAmount = 0.8f; + consumes.power(0.15f); + liquidCapacity = 30f; + hasPower = true; + size = 2; + }}; + + thermalPump = new Pump("thermal-pump"){{ + requirements(Category.liquid, ItemStack.with(Items.copper, 160, Items.lead, 130, Items.silicon, 60, Items.titanium, 80, Items.thorium, 70)); + pumpAmount = 1.95f; + consumes.power(0.30f); + liquidCapacity = 40f; + hasPower = true; + size = 3; + }}; + + conduit = new Conduit("conduit"){{ + requirements(Category.liquid, ItemStack.with(Items.metaglass, 1)); + health = 45; + }}; + + pulseConduit = new Conduit("pulse-conduit"){{ + requirements(Category.liquid, ItemStack.with(Items.titanium, 1, Items.metaglass, 2)); + liquidCapacity = 16f; + liquidFlowFactor = 4.9f; + health = 90; + }}; + + liquidRouter = new LiquidRouter("liquid-router"){{ + requirements(Category.liquid, ItemStack.with(Items.titanium, 4, Items.metaglass, 4)); + liquidCapacity = 20f; + }}; + + liquidTank = new LiquidTank("liquid-tank"){{ + requirements(Category.liquid, ItemStack.with(Items.titanium, 50, Items.metaglass, 50)); + size = 3; + liquidCapacity = 1500f; + health = 500; + }}; + + liquidJunction = new LiquidJunction("liquid-junction"){{ + requirements(Category.liquid, ItemStack.with(Items.titanium, 4, Items.metaglass, 4)); + }}; + + bridgeConduit = new LiquidExtendingBridge("bridge-conduit"){{ + requirements(Category.liquid, ItemStack.with(Items.titanium, 8, Items.metaglass, 8)); + range = 4; + hasPower = false; + }}; + + phaseConduit = new LiquidBridge("phase-conduit"){{ + requirements(Category.liquid, ItemStack.with(Items.phasefabric, 10, Items.silicon, 15, Items.metaglass, 40, Items.titanium, 20)); + range = 12; + hasPower = true; + consumes.power(0.30f); + }}; + + //endregion + //region power + + powerNode = new PowerNode("power-node"){{ + requirements(Category.power, ItemStack.with(Items.copper, 2, Items.lead, 6)); + maxNodes = 4; + laserRange = 6; + }}; + + powerNodeLarge = new PowerNode("power-node-large"){{ + requirements(Category.power, ItemStack.with(Items.titanium, 10, Items.lead, 20, Items.silicon, 6)); + size = 2; + maxNodes = 6; + laserRange = 9.5f; + }}; + + surgeTower = new PowerNode("surge-tower"){{ + requirements(Category.power, ItemStack.with(Items.titanium, 15, Items.lead, 20, Items.silicon, 30, Items.surgealloy, 30)); + size = 2; + maxNodes = 2; + laserRange = 30f; + }}; + + battery = new Battery("battery"){{ + requirements(Category.power, ItemStack.with(Items.copper, 8, Items.lead, 40)); + consumes.powerBuffered(4000f); + }}; + + batteryLarge = new Battery("battery-large"){{ + requirements(Category.power, ItemStack.with(Items.titanium, 40, Items.lead, 80, Items.silicon, 40)); + size = 3; + consumes.powerBuffered(50000f); + }}; + + combustionGenerator = new BurnerGenerator("combustion-generator"){{ + requirements(Category.power, ItemStack.with(Items.copper, 50, Items.lead, 30)); + powerProduction = 1f; + itemDuration = 60f; + }}; + + thermalGenerator = new ThermalGenerator("thermal-generator"){{ + requirements(Category.power, ItemStack.with(Items.copper, 80, Items.graphite, 70, Items.lead, 100, Items.silicon, 70, Items.metaglass, 80)); + powerProduction = 1.8f; + generateEffect = Fx.redgeneratespark; + size = 2; + }}; + + turbineGenerator = new BurnerGenerator("turbine-generator"){{ + requirements(Category.power, ItemStack.with(Items.copper, 70, Items.graphite, 50, Items.lead, 80, Items.silicon, 60)); + powerProduction = 6f; + itemDuration = 40f; + consumes.liquid(Liquids.water, 0.05f); + hasLiquids = true; + size = 2; + }}; + + differentialGenerator = new SingleTypeGenerator(true, false, "differential-generator"){{ + requirements(Category.power, ItemStack.with(Items.copper, 140, Items.titanium, 100, Items.lead, 200, Items.silicon, 130, Items.metaglass, 100)); + powerProduction = 16f; + itemDuration = 50f; + hasLiquids = true; + size = 3; + + consumes.item(Items.pyratite).optional(true, false); + consumes.liquid(Liquids.cryofluid, 0.2f); + }}; + + rtgGenerator = new DecayGenerator("rtg-generator"){{ + requirements(Category.power, ItemStack.with(Items.lead, 200, Items.silicon, 150, Items.phasefabric, 50, Items.plastanium, 150, Items.thorium, 100)); + size = 2; + powerProduction = 3f; + itemDuration = 220f; + }}; + + solarPanel = new SolarGenerator("solar-panel"){{ + requirements(Category.power, ItemStack.with(Items.lead, 20, Items.silicon, 30)); + powerProduction = 0.06f; + }}; + + largeSolarPanel = new SolarGenerator("solar-panel-large"){{ + requirements(Category.power, ItemStack.with(Items.lead, 200, Items.silicon, 290, Items.phasefabric, 30)); + size = 3; + powerProduction = 0.9f; + }}; + + thoriumReactor = new NuclearReactor("thorium-reactor"){{ + requirements(Category.power, ItemStack.with(Items.lead, 600, Items.silicon, 400, Items.graphite, 300, Items.thorium, 300, Items.metaglass, 100)); + size = 3; + health = 700; + powerProduction = 14f; + consumes.item(Items.thorium); + heating = 0.02f; + consumes.liquid(Liquids.cryofluid, 0.1f).update(false); + }}; + + impactReactor = new ImpactReactor("impact-reactor"){{ + requirements(Category.power, ItemStack.with(Items.lead, 1000, Items.silicon, 600, Items.graphite, 800, Items.thorium, 200, Items.surgealloy, 500, Items.metaglass, 500)); + size = 4; + health = 900; + powerProduction = 130f; + itemDuration = 90f; + consumes.power(25f); + consumes.item(Items.blastCompound); + consumes.liquid(Liquids.cryofluid, 0.25f); + }}; + + //endregion power + //region production + + mechanicalDrill = new Drill("mechanical-drill"){{ + requirements(Category.production, ItemStack.with(Items.copper, 25), true); + tier = 2; + drillTime = 600; + size = 2; + drawMineItem = true; + consumes.liquid(Liquids.water, 0.05f).boost(); + }}; + + pneumaticDrill = new Drill("pneumatic-drill"){{ + requirements(Category.production, ItemStack.with(Items.copper, 35, Items.graphite, 15)); + tier = 3; + drillTime = 480; + size = 2; + drawMineItem = true; + consumes.liquid(Liquids.water, 0.06f).boost(); + }}; + + laserDrill = new Drill("laser-drill"){{ + requirements(Category.production, ItemStack.with(Items.copper, 70, Items.graphite, 60, Items.silicon, 60, Items.titanium, 40)); + drillTime = 280; + size = 3; + hasPower = true; + tier = 4; + updateEffect = Fx.pulverizeMedium; + drillEffect = Fx.mineBig; + + consumes.power(1.10f); + consumes.liquid(Liquids.water, 0.08f).boost(); + }}; + + blastDrill = new Drill("blast-drill"){{ + requirements(Category.production, ItemStack.with(Items.copper, 130, Items.silicon, 120, Items.titanium, 100, Items.thorium, 150)); + drillTime = 220; + size = 4; + drawRim = true; + hasPower = true; + tier = 5; + updateEffect = Fx.pulverizeRed; + updateEffectChance = 0.03f; + drillEffect = Fx.mineHuge; + rotateSpeed = 6f; + warmupSpeed = 0.01f; + + consumes.power(3f); + consumes.liquid(Liquids.water, 0.1f).boost(); + }}; + + waterExtractor = new SolidPump("water-extractor"){{ + requirements(Category.production, ItemStack.with(Items.copper, 50, Items.graphite, 50, Items.lead, 40)); + result = Liquids.water; + pumpAmount = 0.13f; + size = 2; + liquidCapacity = 30f; + rotateSpeed = 1.4f; + attribute = Attribute.water; + + consumes.power(0.90f); + }}; + + cultivator = new Cultivator("cultivator"){{ + requirements(Category.production, ItemStack.with(Items.copper, 20, Items.lead, 50, Items.silicon, 20)); + outputItem = new ItemStack(Items.sporePod, 1); + craftTime = 160; + size = 2; + hasLiquids = true; + hasPower = true; + hasItems = true; + + consumes.power(0.80f); + consumes.liquid(Liquids.water, 0.15f); + }}; + + oilExtractor = new Fracker("oil-extractor"){{ + requirements(Category.production, ItemStack.with(Items.copper, 300, Items.graphite, 350, Items.lead, 230, Items.thorium, 230, Items.silicon, 150)); + result = Liquids.oil; + updateEffect = Fx.pulverize; + liquidCapacity = 50f; + updateEffectChance = 0.05f; + pumpAmount = 0.25f; + size = 3; + liquidCapacity = 30f; + attribute = Attribute.oil; + + consumes.item(Items.sand); + consumes.power(3f); + consumes.liquid(Liquids.water, 0.15f); + }}; + + //endregion + //region storage + + coreShard = new CoreBlock("core-shard"){{ + requirements(Category.effect, () -> false, ItemStack.with(Items.titanium, 2000)); + alwaysUnlocked = true; + + health = 1100; + itemCapacity = 5000; + size = 3; + }}; + + coreFoundation = new CoreBlock("core-foundation"){{ + requirements(Category.effect, () -> false, ItemStack.with(Items.titanium, 3000, Items.silicon, 2000)); + + health = 2000; + itemCapacity = 9000; + size = 4; + }}; + + coreNucleus = new CoreBlock("core-nucleus"){{ + requirements(Category.effect, () -> false, ItemStack.with(Items.titanium, 8000, Items.silicon, 4000, Items.surgealloy, 2000)); + + health = 4000; + itemCapacity = 13000; + size = 5; + }}; + + vault = new Vault("vault"){{ + requirements(Category.effect, ItemStack.with(Items.titanium, 500, Items.thorium, 250)); + size = 3; + itemCapacity = 1000; + }}; + + container = new Vault("container"){{ + requirements(Category.effect, ItemStack.with(Items.titanium, 200)); + size = 2; + itemCapacity = 300; + }}; + + unloader = new Unloader("unloader"){{ + requirements(Category.effect, ItemStack.with(Items.titanium, 50, Items.silicon, 60)); + speed = 7f; + }}; + + launchPad = new LaunchPad("launch-pad"){{ + requirements(Category.effect, () -> world.isZone(), ItemStack.with(Items.copper, 500, Items.silicon, 150, Items.lead, 200)); + size = 3; + itemCapacity = 100; + launchTime = 60f * 16; + hasPower = true; + consumes.power(1f); + }}; + + launchPadLarge = new LaunchPad("launch-pad-large"){{ + requirements(Category.effect, () -> world.isZone(), ItemStack.with(Items.titanium, 400, Items.silicon, 300, Items.lead, 500, Items.plastanium, 150)); + size = 4; + itemCapacity = 250; + launchTime = 60f * 14; + hasPower = true; + consumes.power(2f); + }}; + + + //endregion + //region turrets + + duo = new DoubleTurret("duo"){{ + requirements(Category.turret, ItemStack.with(Items.copper, 70), true); + ammo( + Items.copper, Bullets.standardCopper, + Items.graphite, Bullets.standardDense, + Items.pyratite, Bullets.standardIncendiary, + Items.silicon, Bullets.standardHoming + ); + reload = 20f; + restitution = 0.03f; + range = 95f; + shootCone = 15f; + ammoUseEffect = Fx.shellEjectSmall; + health = 250; + inaccuracy = 2f; + rotatespeed = 10f; + }}; + + scatter = new BurstTurret("scatter"){{ + requirements(Category.turret, ItemStack.with(Items.copper, 170, Items.lead, 90)); + ammo( + Items.scrap, Bullets.flakScrap, + Items.lead, Bullets.flakLead + ); + reload = 18f; + range = 170f; + size = 2; + burstSpacing = 5f; + shots = 2; + targetGround = false; + + recoil = 2f; + rotatespeed = 15f; + inaccuracy = 17f; + shootCone = 35f; + + health = 260 * size * size; + }}; + + scorch = new ItemTurret("scorch"){{ + requirements(Category.turret, ItemStack.with(Items.copper, 50, Items.graphite, 45)); + ammo( + Items.coal, Bullets.basicFlame, + Items.pyratite, Bullets.pyraFlame + ); + recoil = 0f; + reload = 4f; + range = 60f; + shootCone = 50f; + targetAir = false; + ammoUseEffect = Fx.none; + health = 400; + }}; + + hail = new ArtilleryTurret("hail"){{ + requirements(Category.turret, ItemStack.with(Items.copper, 80, Items.graphite, 35)); + ammo( + Items.graphite, Bullets.artilleryDense, + Items.silicon, Bullets.artilleryHoming, + Items.pyratite, Bullets.artlleryIncendiary + ); + reload = 60f; + recoil = 2f; + range = 230f; + inaccuracy = 1f; + shootCone = 10f; + health = 260; + }}; + + wave = new LiquidTurret("wave"){{ + requirements(Category.turret, ItemStack.with(Items.metaglass, 90, Items.lead, 150)); + ammo( + Liquids.water, Bullets.waterShot, + Liquids.slag, Bullets.slagShot, + Liquids.cryofluid, Bullets.cryoShot, + Liquids.oil, Bullets.oilShot + ); + size = 2; + recoil = 0f; + reload = 2f; + inaccuracy = 5f; + shootCone = 50f; + shootEffect = Fx.shootLiquid; + range = 110f; + health = 250 * size * size; + + drawer = (tile, entity) -> { + Draw.rect(region, tile.drawx() + tr2.x, tile.drawy() + tr2.y, entity.rotation - 90); + + Draw.color(entity.liquids.current().color); + Draw.alpha(entity.liquids.total() / liquidCapacity); + Draw.rect(name + "-liquid", tile.drawx() + tr2.x, tile.drawy() + tr2.y, entity.rotation - 90); + Draw.color(); + }; + }}; + + lancer = new ChargeTurret("lancer"){{ + requirements(Category.turret, ItemStack.with(Items.copper, 50, Items.lead, 100, Items.silicon, 90)); + range = 155f; + chargeTime = 50f; + chargeMaxDelay = 30f; + chargeEffects = 7; + shootType = Bullets.lancerLaser; + recoil = 2f; + reload = 90f; + cooldown = 0.03f; + powerUse = 2.5f; + shootShake = 2f; + shootEffect = Fx.lancerLaserShoot; + smokeEffect = Fx.lancerLaserShootSmoke; + chargeEffect = Fx.lancerLaserCharge; + chargeBeginEffect = Fx.lancerLaserChargeBegin; + heatColor = Color.RED; + size = 2; + health = 280 * size * size; + targetAir = false; + }}; + + arc = new PowerTurret("arc"){{ + requirements(Category.turret, ItemStack.with(Items.copper, 70, Items.lead, 70)); + shootType = Bullets.arc; + reload = 24f; + shootCone = 40f; + rotatespeed = 8f; + powerUse = 0.9f; + targetAir = false; + range = 95f; + shootEffect = Fx.lightningShoot; + heatColor = Color.RED; + recoil = 1f; + size = 1; + health = 260; + }}; + + swarmer = new BurstTurret("swarmer"){{ + requirements(Category.turret, ItemStack.with(Items.graphite, 70, Items.titanium, 70, Items.plastanium, 90, Items.silicon, 60)); + ammo( + Items.blastCompound, Bullets.missileExplosive, + Items.pyratite, Bullets.missileIncendiary, + Items.surgealloy, Bullets.missileSurge + ); + reload = 40f; + shots = 4; + burstSpacing = 5; + inaccuracy = 10f; + range = 185f; + xRand = 6f; + size = 2; + health = 300 * size * size; + }}; + + salvo = new BurstTurret("salvo"){{ + requirements(Category.turret, ItemStack.with(Items.copper, 210, Items.graphite, 190, Items.titanium, 120)); + ammo( + Items.copper, Bullets.standardCopper, + Items.graphite, Bullets.standardDense, + Items.pyratite, Bullets.standardIncendiary, + Items.silicon, Bullets.standardHoming, + Items.thorium, Bullets.standardThorium + ); + + size = 2; + range = 150f; + reload = 30f; + restitution = 0.03f; + ammoEjectBack = 3f; + cooldown = 0.03f; + recoil = 3f; + shootShake = 2f; + burstSpacing = 3f; + shots = 4; + ammoUseEffect = Fx.shellEjectBig; + health = 360; + }}; + + fuse = new ItemTurret("fuse"){{ + requirements(Category.turret, ItemStack.with(Items.copper, 450, Items.graphite, 450, Items.thorium, 200)); + + reload = 35f; + shootShake = 4f; + range = 90f; + recoil = 5f; + shots = 3; + spread = 20f; + restitution = 0.1f; + shootCone = 30; + size = 3; + + health = 220 * size * size; + + ammo(Items.graphite, new BulletType(0.01f, 105){ + int rays = 1; + float rayLength = range + 10f; + + { + hitEffect = Fx.hitLancer; + shootEffect = smokeEffect = Fx.lightningShoot; + lifetime = 10f; + despawnEffect = Fx.none; + pierce = true; + } + + @Override + public void init(Bullet b){ + for(int i = 0; i < rays; i++){ + Damage.collideLine(b, b.getTeam(), hitEffect, b.x, b.y, b.rot(), rayLength - Math.abs(i - (rays / 2)) * 20f); + } + } + + @Override + public void draw(Bullet b){ + super.draw(b); + Draw.color(Color.WHITE, Pal.lancerLaser, b.fin()); + //Draw.alpha(b.fout()); + for(int i = 0; i < 7; i++){ + Tmp.v1.trns(b.rot(), i * 8f); + float sl = Mathf.clamp(b.fout() - 0.5f) * (80f - i * 10); + Shapes.tri(b.x + Tmp.v1.x, b.y + Tmp.v1.y, 4f, sl, b.rot() + 90); + Shapes.tri(b.x + Tmp.v1.x, b.y + Tmp.v1.y, 4f, sl, b.rot() - 90); + } + Shapes.tri(b.x, b.y, 20f * b.fout(), (rayLength + 50), b.rot()); + Shapes.tri(b.x, b.y, 20f * b.fout(), 10f, b.rot() + 180f); + Draw.reset(); + } + }); + }}; + + ripple = new ArtilleryTurret("ripple"){{ + requirements(Category.turret, ItemStack.with(Items.copper, 300, Items.graphite, 270, Items.titanium, 120)); + ammo( + Items.graphite, Bullets.artilleryDense, + Items.silicon, Bullets.artilleryHoming, + Items.pyratite, Bullets.artlleryIncendiary, + Items.blastCompound, Bullets.artilleryExplosive, + Items.plastanium, Bullets.arilleryPlastic + ); + size = 3; + shots = 4; + inaccuracy = 12f; + reload = 60f; + ammoEjectBack = 5f; + ammoUseEffect = Fx.shellEjectBig; + cooldown = 0.03f; + velocityInaccuracy = 0.2f; + restitution = 0.02f; + recoil = 6f; + shootShake = 2f; + range = 290f; + + health = 550; + }}; + + cyclone = new ItemTurret("cyclone"){{ + requirements(Category.turret, ItemStack.with(Items.copper, 400, Items.titanium, 250, Items.plastanium, 160)); + ammo( + Items.blastCompound, Bullets.flakExplosive, + Items.plastanium, Bullets.flakPlastic, + Items.surgealloy, Bullets.flakSurge + ); + xRand = 4f; + reload = 6f; + range = 200f; + size = 3; + recoil = 3f; + rotatespeed = 10f; + inaccuracy = 10f; + shootCone = 30f; + + health = 145 * size * size; + }}; + + spectre = new DoubleTurret("spectre"){{ + requirements(Category.turret, ItemStack.with(Items.copper, 700, Items.graphite, 600, Items.surgealloy, 500, Items.plastanium, 350, Items.thorium, 500)); + ammo( + Items.graphite, Bullets.standardDenseBig, + Items.pyratite, Bullets.standardIncendiaryBig, + Items.thorium, Bullets.standardThoriumBig + ); + reload = 6f; + coolantMultiplier = 0.5f; + restitution = 0.1f; + ammoUseEffect = Fx.shellEjectBig; + range = 200f; + inaccuracy = 3f; + recoil = 3f; + xRand = 3f; + shotWidth = 4f; + shootShake = 2f; + shots = 2; + size = 4; + shootCone = 24f; + + health = 155 * size * size; + consumes.add(new ConsumeLiquidFilter(liquid -> liquid.temperature <= 0.5f && liquid.flammability < 0.1f, 2f)).update(false).optional(true, false); + }}; + + meltdown = new LaserTurret("meltdown"){{ + requirements(Category.turret, ItemStack.with(Items.copper, 500, Items.lead, 700, Items.graphite, 600, Items.surgealloy, 650, Items.silicon, 650)); + shootType = Bullets.meltdownLaser; + shootEffect = Fx.shootBigSmoke2; + shootCone = 40f; + recoil = 4f; + size = 4; + shootShake = 2f; + range = 190f; + reload = 50f; + firingMoveFract = 0.5f; + shootDuration = 220f; + powerUse = 14f; + + health = 200 * size * size; + consumes.add(new ConsumeLiquidFilter(liquid -> liquid.temperature <= 0.5f && liquid.flammability < 0.1f, 0.5f)).update(false); + }}; + + //endregion + //region units + + BooleanProvider padVisible = () -> state.rules.attackMode || state.rules.pvp || state.isEditor(); + + draugFactory = new UnitFactory("draug-factory"){{ + requirements(Category.units, ItemStack.with(Items.copper, 30, Items.lead, 120)); + type = UnitTypes.draug; + produceTime = 5000; + size = 2; + maxSpawn = 2; + consumes.power(0.5f); + consumes.items(); + }}; + + spiritFactory = new UnitFactory("spirit-factory"){{ + requirements(Category.units, ItemStack.with(Items.metaglass, 70, Items.lead, 110, Items.silicon, 90)); + type = UnitTypes.spirit; + produceTime = 5700; + size = 2; + maxSpawn = 2; + consumes.power(0.80f); + consumes.items(new ItemStack(Items.silicon, 30), new ItemStack(Items.lead, 30)); + }}; + + phantomFactory = new UnitFactory("phantom-factory"){{ + requirements(Category.units, ItemStack.with(Items.titanium, 90, Items.thorium, 80, Items.lead, 110, Items.silicon, 210)); + type = UnitTypes.phantom; + produceTime = 7300; + size = 2; + maxSpawn = 2; + consumes.power(2f); + consumes.items(new ItemStack(Items.silicon, 70), new ItemStack(Items.lead, 80), new ItemStack(Items.titanium, 80)); + }}; + + wraithFactory = new UnitFactory("wraith-factory"){{ + requirements(Category.units, padVisible, ItemStack.with(Items.titanium, 60, Items.lead, 80, Items.silicon, 90)); + type = UnitTypes.wraith; + produceTime = 1500; + size = 2; + consumes.power(0.6f); + consumes.items(new ItemStack(Items.silicon, 20), new ItemStack(Items.titanium, 10)); + }}; + + ghoulFactory = new UnitFactory("ghoul-factory"){{ + requirements(Category.units, padVisible, ItemStack.with(Items.titanium, 150, Items.lead, 130, Items.silicon, 220)); + type = UnitTypes.ghoul; + produceTime = 2300; + size = 3; + consumes.power(1.2f); + consumes.items(new ItemStack(Items.silicon, 30), new ItemStack(Items.titanium, 20)); + }}; + + revenantFactory = new UnitFactory("revenant-factory"){{ + requirements(Category.units, padVisible, ItemStack.with(Items.plastanium, 100, Items.titanium, 300, Items.lead, 300, Items.silicon, 400)); + type = UnitTypes.revenant; + produceTime = 4000; + size = 4; + consumes.power(3f); + consumes.items(new ItemStack(Items.silicon, 80), new ItemStack(Items.titanium, 80)); + }}; + + daggerFactory = new UnitFactory("dagger-factory"){{ + requirements(Category.units, padVisible, ItemStack.with(Items.lead, 110, Items.silicon, 70)); + type = UnitTypes.dagger; + produceTime = 1700; + size = 2; + consumes.power(0.5f); + consumes.items(new ItemStack(Items.silicon, 15)); + }}; + + crawlerFactory = new UnitFactory("crawler-factory"){{ + requirements(Category.units, padVisible, ItemStack.with(Items.lead, 50, Items.silicon, 80)); + type = UnitTypes.crawler; + produceTime = 500; + size = 2; + maxSpawn = 5; + consumes.power(0.5f); + consumes.items(new ItemStack(Items.coal, 5), new ItemStack(Items.silicon, 5)); + }}; + + titanFactory = new UnitFactory("titan-factory"){{ + requirements(Category.units, padVisible, ItemStack.with(Items.graphite, 100, Items.lead, 100, Items.silicon, 90)); + type = UnitTypes.titan; + produceTime = 2100; + size = 3; + consumes.power(0.60f); + consumes.items(new ItemStack(Items.silicon, 30)); + }}; + + fortressFactory = new UnitFactory("fortress-factory"){{ + requirements(Category.units, padVisible, ItemStack.with(Items.thorium, 80, Items.lead, 220, Items.silicon, 150)); + type = UnitTypes.fortress; + produceTime = 4000; + size = 3; + maxSpawn = 3; + consumes.power(1.4f); + consumes.items(new ItemStack(Items.silicon, 40), new ItemStack(Items.graphite, 30)); + }}; + + repairPoint = new RepairPoint("repair-point"){{ + requirements(Category.units, ItemStack.with(Items.lead, 30, Items.copper, 30, Items.silicon, 30)); + repairSpeed = 0.3f; + powerUse = 1f; + }}; + + //endregion + //region upgrades + + dartPad = new MechPad("dart-mech-pad"){{ + requirements(Category.upgrade, ItemStack.with(Items.lead, 200, Items.graphite, 100, Items.copper, 150)); + mech = Mechs.alpha; + size = 2; + consumes.power(0.5f); + }}; + + deltaPad = new MechPad("delta-mech-pad"){{ + requirements(Category.upgrade, ItemStack.with(Items.lead, 350, Items.titanium, 350, Items.copper, 400, Items.silicon, 450, Items.thorium, 300)); + mech = Mechs.delta; + size = 2; + consumes.power(0.7f); + }}; + + tauPad = new MechPad("tau-mech-pad"){{ + requirements(Category.upgrade, ItemStack.with(Items.lead, 250, Items.titanium, 250, Items.copper, 250, Items.silicon, 250)); + mech = Mechs.tau; + size = 2; + consumes.power(1f); + }}; + + omegaPad = new MechPad("omega-mech-pad"){{ + requirements(Category.upgrade, ItemStack.with(Items.lead, 450, Items.graphite, 550, Items.silicon, 650, Items.thorium, 600, Items.surgealloy, 240)); + mech = Mechs.omega; + size = 3; + consumes.power(1.2f); + }}; + + javelinPad = new MechPad("javelin-ship-pad"){{ + requirements(Category.upgrade, ItemStack.with(Items.lead, 350, Items.silicon, 450, Items.titanium, 500, Items.plastanium, 400, Items.phasefabric, 200)); + mech = Mechs.javelin; + size = 2; + consumes.power(0.8f); + }}; + + tridentPad = new MechPad("trident-ship-pad"){{ + requirements(Category.upgrade, ItemStack.with(Items.lead, 250, Items.copper, 250, Items.silicon, 250, Items.titanium, 300, Items.plastanium, 200)); + mech = Mechs.trident; + size = 2; + consumes.power(1f); + }}; + + glaivePad = new MechPad("glaive-ship-pad"){{ + requirements(Category.upgrade, ItemStack.with(Items.lead, 450, Items.silicon, 650, Items.titanium, 700, Items.plastanium, 600, Items.surgealloy, 200)); + mech = Mechs.glaive; + size = 3; + consumes.power(1.2f); + }}; + + //endregion + } +} diff --git a/core/src/io/anuke/mindustry/content/Bullets.java b/core/src/io/anuke/mindustry/content/Bullets.java new file mode 100644 index 0000000000..37c84c7898 --- /dev/null +++ b/core/src/io/anuke/mindustry/content/Bullets.java @@ -0,0 +1,711 @@ +package io.anuke.mindustry.content; + +import io.anuke.arc.graphics.Color; +import io.anuke.arc.graphics.g2d.*; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.util.Time; +import io.anuke.arc.util.Tmp; +import io.anuke.mindustry.entities.Damage; +import io.anuke.mindustry.entities.Effects; +import io.anuke.mindustry.entities.bullet.*; +import io.anuke.mindustry.entities.effect.*; +import io.anuke.mindustry.entities.type.Unit; +import io.anuke.mindustry.game.ContentList; +import io.anuke.mindustry.graphics.Pal; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.BuildBlock; + +import static io.anuke.mindustry.Vars.world; + +public class Bullets implements ContentList{ + public static BulletType + + //artillery + artilleryDense, arilleryPlastic, artilleryPlasticFrag, artilleryHoming, artlleryIncendiary, artilleryExplosive, artilleryUnit, + + //flak + flakScrap, flakLead, flakPlastic, flakExplosive, flakSurge, + + //missiles + missileExplosive, missileIncendiary, missileSurge, missileJavelin, missileSwarm, missileRevenant, + + //standard + standardCopper, standardDense, standardThorium, standardHoming, standardIncendiary, standardMechSmall, + standardGlaive, standardDenseBig, standardThoriumBig, standardIncendiaryBig, + + //electric + lancerLaser, meltdownLaser, lightning, arc, damageLightning, + + //liquid + waterShot, cryoShot, slagShot, oilShot, + + //environment, misc. + fireball, basicFlame, pyraFlame, driverBolt, healBullet, frag, eruptorShot, + + //bombs + bombExplosive, bombIncendiary, bombOil, explode; + + @Override + public void load(){ + + artilleryDense = new ArtilleryBulletType(3f, 0, "shell"){{ + hitEffect = Fx.flakExplosion; + knockback = 0.8f; + lifetime = 50f; + bulletWidth = bulletHeight = 11f; + collidesTiles = false; + splashDamageRadius = 25f; + splashDamage = 33f; + }}; + + artilleryPlasticFrag = new BasicBulletType(2.5f, 7, "bullet"){{ + bulletWidth = 10f; + bulletHeight = 12f; + bulletShrink = 1f; + lifetime = 15f; + backColor = Pal.plastaniumBack; + frontColor = Pal.plastaniumFront; + despawnEffect = Fx.none; + }}; + + arilleryPlastic = new ArtilleryBulletType(3.4f, 0, "shell"){{ + hitEffect = Fx.plasticExplosion; + knockback = 1f; + lifetime = 55f; + bulletWidth = bulletHeight = 13f; + collidesTiles = false; + splashDamageRadius = 35f; + splashDamage = 45f; + fragBullet = artilleryPlasticFrag; + fragBullets = 10; + backColor = Pal.plastaniumBack; + frontColor = Pal.plastaniumFront; + }}; + + artilleryHoming = new ArtilleryBulletType(3f, 0, "shell"){{ + hitEffect = Fx.flakExplosion; + knockback = 0.8f; + lifetime = 45f; + bulletWidth = bulletHeight = 11f; + collidesTiles = false; + splashDamageRadius = 25f; + splashDamage = 33f; + homingPower = 2f; + homingRange = 50f; + }}; + + artlleryIncendiary = new ArtilleryBulletType(3f, 0, "shell"){{ + hitEffect = Fx.blastExplosion; + knockback = 0.8f; + lifetime = 60f; + bulletWidth = bulletHeight = 13f; + collidesTiles = false; + splashDamageRadius = 25f; + splashDamage = 30f; + incendAmount = 4; + incendSpread = 11f; + frontColor = Pal.lightishOrange; + backColor = Pal.lightOrange; + trailEffect = Fx.incendTrail; + }}; + + artilleryExplosive = new ArtilleryBulletType(2f, 0, "shell"){{ + hitEffect = Fx.blastExplosion; + knockback = 0.8f; + lifetime = 70f; + bulletWidth = bulletHeight = 14f; + collidesTiles = false; + splashDamageRadius = 45f; + splashDamage = 50f; + backColor = Pal.missileYellowBack; + frontColor = Pal.missileYellow; + }}; + + artilleryUnit = new ArtilleryBulletType(2f, 0, "shell"){{ + hitEffect = Fx.blastExplosion; + knockback = 0.8f; + lifetime = 90f; + bulletWidth = bulletHeight = 14f; + collides = true; + collidesTiles = true; + splashDamageRadius = 20f; + splashDamage = 38f; + backColor = Pal.bulletYellowBack; + frontColor = Pal.bulletYellow; + }}; + + flakLead = new FlakBulletType(4.2f, 3){{ + lifetime = 60f; + ammoMultiplier = 3f; + shootEffect = Fx.shootSmall; + bulletWidth = 6f; + bulletHeight = 8f; + hitEffect = Fx.flakExplosion; + splashDamage = 27f; + splashDamageRadius = 15f; + }}; + + flakScrap = new FlakBulletType(4f, 3){{ + lifetime = 60f; + ammoMultiplier = 3f; + shootEffect = Fx.shootSmall; + reloadMultiplier = 0.5f; + bulletWidth = 6f; + bulletHeight = 8f; + hitEffect = Fx.flakExplosion; + splashDamage = 22f; + splashDamageRadius = 24f; + }}; + + flakPlastic = new FlakBulletType(4f, 6){{ + splashDamageRadius = 50f; + fragBullet = artilleryPlasticFrag; + fragBullets = 6; + hitEffect = Fx.plasticExplosion; + frontColor = Pal.plastaniumFront; + backColor = Pal.plastaniumBack; + shootEffect = Fx.shootBig; + }}; + + flakExplosive = new FlakBulletType(4f, 5){{ + //default bullet type, no changes + shootEffect = Fx.shootBig; + }}; + + flakSurge = new FlakBulletType(4f, 7){{ + splashDamage = 33f; + lightining = 2; + lightningLength = 12; + shootEffect = Fx.shootBig; + }}; + + missileExplosive = new MissileBulletType(2.7f, 10, "missile"){{ + bulletWidth = 8f; + bulletHeight = 8f; + bulletShrink = 0f; + drag = -0.01f; + splashDamageRadius = 30f; + splashDamage = 30f; + lifetime = 150f; + hitEffect = Fx.blastExplosion; + despawnEffect = Fx.blastExplosion; + }}; + + missileIncendiary = new MissileBulletType(2.9f, 12, "missile"){{ + frontColor = Pal.lightishOrange; + backColor = Pal.lightOrange; + bulletWidth = 7f; + bulletHeight = 8f; + bulletShrink = 0f; + drag = -0.01f; + homingPower = 7f; + splashDamageRadius = 10f; + splashDamage = 10f; + lifetime = 160f; + hitEffect = Fx.blastExplosion; + incendSpread = 10f; + incendAmount = 3; + }}; + + missileSurge = new MissileBulletType(4.4f, 15, "bullet"){{ + bulletWidth = 8f; + bulletHeight = 8f; + bulletShrink = 0f; + drag = -0.01f; + splashDamageRadius = 30f; + splashDamage = 22f; + lifetime = 150f; + hitEffect = Fx.blastExplosion; + despawnEffect = Fx.blastExplosion; + lightining = 2; + lightningLength = 14; + }}; + + missileJavelin = new MissileBulletType(5f, 10.5f, "missile"){{ + bulletWidth = 8f; + bulletHeight = 8f; + bulletShrink = 0f; + drag = -0.003f; + keepVelocity = false; + splashDamageRadius = 20f; + splashDamage = 1f; + lifetime = 90f; + trailColor = Color.valueOf("b6c6fd"); + hitEffect = Fx.blastExplosion; + despawnEffect = Fx.blastExplosion; + backColor = Pal.bulletYellowBack; + frontColor = Pal.bulletYellow; + weaveScale = 8f; + weaveMag = 2f; + }}; + + missileSwarm = new MissileBulletType(2.7f, 12, "missile"){{ + bulletWidth = 8f; + bulletHeight = 8f; + bulletShrink = 0f; + drag = -0.003f; + homingRange = 60f; + keepVelocity = false; + splashDamageRadius = 25f; + splashDamage = 10f; + lifetime = 120f; + trailColor = Color.GRAY; + backColor = Pal.bulletYellowBack; + frontColor = Pal.bulletYellow; + hitEffect = Fx.blastExplosion; + despawnEffect = Fx.blastExplosion; + weaveScale = 8f; + weaveMag = 2f; + }}; + + missileRevenant = new MissileBulletType(2.7f, 12, "missile"){{ + bulletWidth = 8f; + bulletHeight = 8f; + bulletShrink = 0f; + drag = -0.003f; + homingRange = 60f; + keepVelocity = false; + splashDamageRadius = 25f; + splashDamage = 10f; + lifetime = 50f; + trailColor = Pal.unitBack; + backColor = Pal.unitBack; + frontColor = Pal.unitFront; + hitEffect = Fx.blastExplosion; + despawnEffect = Fx.blastExplosion; + weaveScale = 6f; + weaveMag = 1f; + }}; + + standardCopper = new BasicBulletType(2.5f, 9, "bullet"){{ + bulletWidth = 7f; + bulletHeight = 9f; + shootEffect = Fx.shootSmall; + smokeEffect = Fx.shootSmallSmoke; + ammoMultiplier = 1; + }}; + + standardDense = new BasicBulletType(3.5f, 18, "bullet"){{ + bulletWidth = 9f; + bulletHeight = 12f; + reloadMultiplier = 0.6f; + ammoMultiplier = 2; + }}; + + standardThorium = new BasicBulletType(4f, 29, "bullet"){{ + bulletWidth = 10f; + bulletHeight = 13f; + shootEffect = Fx.shootBig; + smokeEffect = Fx.shootBigSmoke; + ammoMultiplier = 2; + }}; + + standardHoming = new BasicBulletType(3f, 9, "bullet"){{ + bulletWidth = 7f; + bulletHeight = 9f; + homingPower = 5f; + reloadMultiplier = 1.4f; + ammoMultiplier = 3; + }}; + + standardIncendiary = new BasicBulletType(3.2f, 11, "bullet"){{ + bulletWidth = 10f; + bulletHeight = 12f; + frontColor = Pal.lightishOrange; + backColor = Pal.lightOrange; + incendSpread = 3f; + incendAmount = 1; + incendChance = 0.3f; + inaccuracy = 3f; + }}; + + standardGlaive = new BasicBulletType(4f, 7.5f, "bullet"){{ + bulletWidth = 10f; + bulletHeight = 12f; + frontColor = Color.valueOf("feb380"); + backColor = Color.valueOf("ea8878"); + incendSpread = 3f; + incendAmount = 1; + incendChance = 0.3f; + }}; + + standardMechSmall = new BasicBulletType(4f, 9, "bullet"){{ + bulletWidth = 11f; + bulletHeight = 14f; + lifetime = 40f; + inaccuracy = 5f; + despawnEffect = Fx.hitBulletSmall; + }}; + + standardDenseBig = new BasicBulletType(7f, 42, "bullet"){{ + bulletWidth = 15f; + bulletHeight = 21f; + shootEffect = Fx.shootBig; + }}; + + standardThoriumBig = new BasicBulletType(8f, 65, "bullet"){{ + bulletWidth = 16f; + bulletHeight = 23f; + shootEffect = Fx.shootBig; + }}; + + standardIncendiaryBig = new BasicBulletType(7f, 38, "bullet"){{ + bulletWidth = 16f; + bulletHeight = 21f; + frontColor = Pal.lightishOrange; + backColor = Pal.lightOrange; + incendSpread = 3f; + incendAmount = 2; + incendChance = 0.3f; + shootEffect = Fx.shootBig; + }}; + + damageLightning = new BulletType(0.0001f, 0f){{ + lifetime = Lightning.lifetime; + hitEffect = Fx.hitLancer; + despawnEffect = Fx.none; + status = StatusEffects.shocked; + statusDuration = 10f; + }}; + + healBullet = new BulletType(5.2f, 13){ + float healPercent = 3f; + + { + shootEffect = Fx.shootHeal; + smokeEffect = Fx.hitLaser; + hitEffect = Fx.hitLaser; + despawnEffect = Fx.hitLaser; + collidesTeam = true; + } + + @Override + public boolean collides(Bullet b, Tile tile){ + return tile.getTeam() != b.getTeam() || tile.entity.healthf() < 1f; + } + + @Override + public void draw(Bullet b){ + Draw.color(Pal.heal); + Lines.stroke(2f); + Lines.lineAngleCenter(b.x, b.y, b.rot(), 7f); + Draw.color(Color.WHITE); + Lines.lineAngleCenter(b.x, b.y, b.rot(), 3f); + Draw.reset(); + } + + @Override + public void hitTile(Bullet b, Tile tile){ + super.hit(b); + tile = tile.link(); + + if(tile.getTeam() == b.getTeam() && !(tile.block() instanceof BuildBlock)){ + Effects.effect(Fx.healBlockFull, Pal.heal, tile.drawx(), tile.drawy(), tile.block().size); + tile.entity.healBy(healPercent / 100f * tile.entity.maxHealth()); + } + } + }; + + fireball = new BulletType(1f, 4){ + { + pierce = true; + hitTiles = false; + collides = false; + collidesTiles = false; + drag = 0.03f; + hitEffect = despawnEffect = Fx.none; + } + + @Override + public void init(Bullet b){ + b.velocity().setLength(0.6f + Mathf.random(2f)); + } + + @Override + public void draw(Bullet b){ + Draw.color(Pal.lightFlame, Pal.darkFlame, Color.GRAY, b.fin()); + Fill.circle(b.x, b.y, 3f * b.fout()); + Draw.reset(); + } + + @Override + public void update(Bullet b){ + if(Mathf.chance(0.04 * Time.delta())){ + Tile tile = world.tileWorld(b.x, b.y); + if(tile != null){ + Fire.create(tile); + } + } + + if(Mathf.chance(0.1 * Time.delta())){ + Effects.effect(Fx.fireballsmoke, b.x, b.y); + } + + if(Mathf.chance(0.1 * Time.delta())){ + Effects.effect(Fx.ballfire, b.x, b.y); + } + } + }; + + basicFlame = new BulletType(3f, 6f){ + { + ammoMultiplier = 3f; + hitSize = 7f; + lifetime = 42f; + pierce = true; + drag = 0.05f; + statusDuration = 60f * 4; + shootEffect = Fx.shootSmallFlame; + hitEffect = Fx.hitFlameSmall; + despawnEffect = Fx.none; + status = StatusEffects.burning; + } + + @Override + public void draw(Bullet b){ + } + }; + + pyraFlame = new BulletType(3.3f, 9f){ + { + ammoMultiplier = 4f; + hitSize = 7f; + lifetime = 42f; + pierce = true; + drag = 0.05f; + statusDuration = 60f * 6; + shootEffect = Fx.shootPyraFlame; + hitEffect = Fx.hitFlameSmall; + despawnEffect = Fx.none; + status = StatusEffects.burning; + } + + @Override + public void draw(Bullet b){ + } + }; + + lancerLaser = new BulletType(0.001f, 140){ + Color[] colors = {Pal.lancerLaser.cpy().mul(1f, 1f, 1f, 0.4f), Pal.lancerLaser, Color.WHITE}; + float[] tscales = {1f, 0.7f, 0.5f, 0.2f}; + float[] lenscales = {1f, 1.1f, 1.13f, 1.14f}; + float length = 160f; + + { + hitEffect = Fx.hitLancer; + despawnEffect = Fx.none; + hitSize = 4; + lifetime = 16f; + pierce = true; + } + + @Override + public float range(){ + return length; + } + + @Override + public void init(Bullet b){ + Damage.collideLine(b, b.getTeam(), hitEffect, b.x, b.y, b.rot(), length); + } + + @Override + public void draw(Bullet b){ + float f = Mathf.curve(b.fin(), 0f, 0.2f); + float baseLen = length * f; + + Lines.lineAngle(b.x, b.y, b.rot(), baseLen); + for(int s = 0; s < 3; s++){ + Draw.color(colors[s]); + for(int i = 0; i < tscales.length; i++){ + Lines.stroke(7f * b.fout() * (s == 0 ? 1.5f : s == 1 ? 1f : 0.3f) * tscales[i]); + Lines.lineAngle(b.x, b.y, b.rot(), baseLen * lenscales[i]); + } + } + Draw.reset(); + } + }; + + meltdownLaser = new BulletType(0.001f, 70){ + Color tmpColor = new Color(); + Color[] colors = {Color.valueOf("ec745855"), Color.valueOf("ec7458aa"), Color.valueOf("ff9c5a"), Color.WHITE}; + float[] tscales = {1f, 0.7f, 0.5f, 0.2f}; + float[] strokes = {2f, 1.5f, 1f, 0.3f}; + float[] lenscales = {1f, 1.12f, 1.15f, 1.17f}; + float length = 220f; + + { + hitEffect = Fx.hitMeltdown; + despawnEffect = Fx.none; + hitSize = 4; + drawSize = 420f; + lifetime = 16f; + pierce = true; + } + + @Override + public void update(Bullet b){ + if(b.timer.get(1, 5f)){ + Damage.collideLine(b, b.getTeam(), hitEffect, b.x, b.y, b.rot(), length, true); + } + Effects.shake(1f, 1f, b.x, b.y); + } + + @Override + public void hit(Bullet b, float hitx, float hity){ + Effects.effect(hitEffect, colors[2], hitx, hity); + if(Mathf.chance(0.4)){ + Fire.create(world.tileWorld(hitx + Mathf.range(5f), hity + Mathf.range(5f))); + } + } + + @Override + public void draw(Bullet b){ + float baseLen = (length) * b.fout(); + + Lines.lineAngle(b.x, b.y, b.rot(), baseLen); + for(int s = 0; s < colors.length; s++){ + Draw.color(tmpColor.set(colors[s]).mul(1f + Mathf.absin(Time.time(), 1f, 0.1f))); + for(int i = 0; i < tscales.length; i++){ + Tmp.v1.trns(b.rot() + 180f, (lenscales[i] - 1f) * 35f); + Lines.stroke((9f + Mathf.absin(Time.time(), 0.8f, 1.5f)) * b.fout() * strokes[s] * tscales[i]); + Lines.lineAngle(b.x + Tmp.v1.x, b.y + Tmp.v1.y, b.rot(), baseLen * lenscales[i], CapStyle.none); + } + } + Draw.reset(); + } + }; + + waterShot = new LiquidBulletType(Liquids.water){{ + knockback = 0.7f; + }}; + + cryoShot = new LiquidBulletType(Liquids.cryofluid){{ + + }}; + + slagShot = new LiquidBulletType(Liquids.slag){{ + damage = 4; + drag = 0.03f; + }}; + + eruptorShot = new LiquidBulletType(Liquids.slag){{ + damage = 2; + speed = 2.1f; + drag = 0.02f; + }}; + + oilShot = new LiquidBulletType(Liquids.oil){{ + drag = 0.03f; + }}; + + lightning = new BulletType(0.001f, 12f){ + { + lifetime = 1f; + shootEffect = Fx.hitLancer; + smokeEffect = Fx.none; + despawnEffect = Fx.none; + hitEffect = Fx.hitLancer; + keepVelocity = false; + } + + @Override + public float range(){ + return 70f; + } + + @Override + public void draw(Bullet b){ + } + + @Override + public void init(Bullet b){ + Lightning.create(b.getTeam(), Pal.lancerLaser, damage, b.x, b.y, b.rot(), 30); + } + }; + + arc = new BulletType(0.001f, 25){ + { + lifetime = 1; + despawnEffect = Fx.none; + hitEffect = Fx.hitLancer; + } + + @Override + public void draw(Bullet b){ + } + + @Override + public void init(Bullet b){ + Lightning.create(b.getTeam(), Pal.lancerLaser, damage, b.x, b.y, b.rot(), 25); + } + }; + + driverBolt = new MassDriverBolt(); + + frag = new BasicBulletType(5f, 8, "bullet"){{ + bulletWidth = 8f; + bulletHeight = 9f; + bulletShrink = 0.5f; + lifetime = 50f; + drag = 0.04f; + }}; + + bombExplosive = new BombBulletType(10f, 20f, "shell"){{ + bulletWidth = 9f; + bulletHeight = 13f; + hitEffect = Fx.flakExplosion; + shootEffect = Fx.none; + smokeEffect = Fx.none; + }}; + + bombIncendiary = new BombBulletType(7f, 10f, "shell"){{ + bulletWidth = 8f; + bulletHeight = 12f; + hitEffect = Fx.flakExplosion; + backColor = Pal.lightOrange; + frontColor = Pal.lightishOrange; + incendChance = 1f; + incendAmount = 3; + incendSpread = 10f; + }}; + + bombOil = new BombBulletType(2f, 3f, "shell"){ + { + bulletWidth = 8f; + bulletHeight = 12f; + hitEffect = Fx.pulverize; + backColor = new Color(0x4f4f4fff); + frontColor = Color.GRAY; + } + + @Override + public void hit(Bullet b, float x, float y){ + super.hit(b, x, y); + + for(int i = 0; i < 3; i++){ + Tile tile = world.tileWorld(x + Mathf.range(8f), y + Mathf.range(8f)); + Puddle.deposit(tile, Liquids.oil, 5f); + } + } + }; + + explode = new BombBulletType(2f, 3f, "clear"){ + { + hitEffect = Fx.pulverize; + lifetime = 30f; + speed = 1f; + splashDamageRadius = 50f; + splashDamage = 28f; + } + + @Override + public void init(Bullet b){ + if(b.getOwner() instanceof Unit){ + ((Unit)b.getOwner()).kill(); + } + b.time(b.lifetime()); + } + }; + } +} diff --git a/core/src/io/anuke/mindustry/content/Fx.java b/core/src/io/anuke/mindustry/content/Fx.java new file mode 100644 index 0000000000..681340e4e9 --- /dev/null +++ b/core/src/io/anuke/mindustry/content/Fx.java @@ -0,0 +1,1207 @@ +package io.anuke.mindustry.content; + +import io.anuke.arc.Core; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.graphics.g2d.*; +import io.anuke.arc.math.Angles; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.util.Tmp; +import io.anuke.mindustry.entities.Effects.Effect; +import io.anuke.mindustry.entities.effect.GroundEffectEntity.GroundEffect; +import io.anuke.mindustry.entities.type.BaseUnit; +import io.anuke.mindustry.game.ContentList; +import io.anuke.mindustry.graphics.Pal; +import io.anuke.mindustry.graphics.Shapes; +import io.anuke.mindustry.type.Item; +import io.anuke.mindustry.type.Item.Icon; + +import static io.anuke.mindustry.Vars.tilesize; + +public class Fx implements ContentList{ + public static Effect + + none, placeBlock, breakBlock, smoke, spawn, tapBlock, select, + vtolHover, unitDrop, unitPickup, unitLand, pickup, healWave, heal, landShock, reactorsmoke, nuclearsmoke, nuclearcloud, + redgeneratespark, generatespark, fuelburn, plasticburn, pulverize, pulverizeRed, pulverizeRedder, pulverizeSmall, pulverizeMedium, + producesmoke, smeltsmoke, formsmoke, blastsmoke, lava, doorclose, dooropen, dooropenlarge, doorcloselarge, purify, purifyoil, purifystone, generate, + mine, mineBig, mineHuge, smelt, teleportActivate, teleport, teleportOut, ripple, bubble, launch, + healBlock, healBlockFull, healWaveMend, overdriveWave, overdriveBlockFull, shieldBreak, hitBulletSmall, hitFuse, + hitBulletBig, hitFlameSmall, hitLiquid, hitLaser, hitLancer, hitMeltdown, despawn, flakExplosion, blastExplosion, + plasticExplosion, artilleryTrail, incendTrail, missileTrail, absorb, flakExplosionBig, plasticExplosionFlak, burning, fire, + fireSmoke, steam, fireballsmoke, ballfire, freezing, melting, wet, oily, overdriven, dropItem, shockwave, + bigShockwave, nuclearShockwave, explosion, blockExplosion, blockExplosionSmoke, shootSmall, shootHeal, shootSmallSmoke, shootBig, shootBig2, shootBigSmoke, + shootBigSmoke2, shootSmallFlame, shootPyraFlame, shootLiquid, shellEjectSmall, shellEjectMedium, + shellEjectBig, lancerLaserShoot, lancerLaserShootSmoke, lancerLaserCharge, lancerLaserChargeBegin, lightningCharge, lightningShoot, + unitSpawn, spawnShockwave, magmasmoke, impactShockwave, impactcloud, impactsmoke, dynamicExplosion, padlaunch; + + @Override + public void load(){ + + none = new Effect(0, 0f, e -> { + }); + + unitSpawn = new Effect(30f, e -> { + if(!(e.data instanceof BaseUnit)) return; + + Draw.alpha(e.fin()); + + float scl = 1f + e.fout() * 2f; + + BaseUnit unit = (BaseUnit)e.data; + Draw.rect(unit.getIconRegion(), e.x, e.y, + unit.getIconRegion().getWidth() * Draw.scl * scl, unit.getIconRegion().getWidth() * Draw.scl * scl, 180f); + + Draw.reset(); + }); + + placeBlock = new Effect(16, e -> { + Draw.color(Pal.accent); + Lines.stroke(3f - e.fin() * 2f); + Lines.square(e.x, e.y, tilesize / 2f * e.rotation + e.fin() * 3f); + Draw.reset(); + }); + + tapBlock = new Effect(12, e -> { + Draw.color(Pal.accent); + Lines.stroke(3f - e.fin() * 2f); + Lines.circle(e.x, e.y, 4f + (tilesize / 1.5f * e.rotation) * e.fin()); + Draw.reset(); + }); + + breakBlock = new Effect(12, e -> { + Draw.color(Pal.remove); + Lines.stroke(3f - e.fin() * 2f); + Lines.square(e.x, e.y, tilesize / 2f * e.rotation + e.fin() * 3f); + + Angles.randLenVectors(e.id, 3 + (int)(e.rotation * 3), e.rotation * 2f + (tilesize * e.rotation) * e.finpow(), (x, y) -> { + Fill.square(e.x + x, e.y + y, 1f + e.fout() * (3f + e.rotation)); + }); + Draw.reset(); + }); + + select = new Effect(23, e -> { + Draw.color(Pal.accent); + Lines.stroke(e.fout() * 3f); + Lines.circle(e.x, e.y, 3f + e.fin() * 14f); + Draw.reset(); + }); + + smoke = new Effect(100, e -> { + Draw.color(Color.GRAY, Pal.darkishGray, e.fin()); + float size = 7f - e.fin() * 7f; + Draw.rect("circle", e.x, e.y, size, size); + Draw.reset(); + }); + + magmasmoke = new Effect(110, e -> { + Draw.color(Color.GRAY); + Fill.circle(e.x, e.y, e.fslope() * 6f); + Draw.reset(); + }); + + spawn = new Effect(30, e -> { + Lines.stroke(2f * e.fout()); + Draw.color(Pal.accent); + Lines.poly(e.x, e.y, 4, 5f + e.fin() * 12f); + Draw.reset(); + }); + + padlaunch = new Effect(10, e -> { + Lines.stroke(4f * e.fout()); + Draw.color(Pal.accent); + Lines.poly(e.x, e.y, 4, 5f + e.fin() * 60f); + Draw.reset(); + }); + + vtolHover = new Effect(40f, e -> { + float len = e.finpow() * 10f; + float ang = e.rotation + Mathf.randomSeedRange(e.id, 30f); + Draw.color(Pal.lightFlame, Pal.lightOrange, e.fin()); + Fill.circle(e.x + Angles.trnsx(ang, len), e.y + Angles.trnsy(ang, len), 2f * e.fout()); + Draw.reset(); + }); + + unitDrop = new GroundEffect(30, e -> { + Draw.color(Pal.lightishGray); + Angles.randLenVectors(e.id, 9, 3 + 20f * e.finpow(), (x, y) -> { + Fill.circle(e.x + x, e.y + y, e.fout() * 4f + 0.4f); + }); + Draw.reset(); + }); + + unitLand = new GroundEffect(30, e -> { + Draw.color(Tmp.c1.set(e.color).mul(1.1f)); + Angles.randLenVectors(e.id, 6, 17f * e.finpow(), (x, y) -> { + Fill.circle(e.x + x, e.y + y, e.fout() * 4f + 0.3f); + }); + Draw.reset(); + }); + + unitPickup = new GroundEffect(18, e -> { + Draw.color(Pal.lightishGray); + Lines.stroke(e.fin() * 2f); + Lines.poly(e.x, e.y, 4, 13f * e.fout()); + Draw.reset(); + }); + + landShock = new GroundEffect(12, e -> { + Draw.color(Pal.lancerLaser); + Lines.stroke(e.fout() * 3f); + Lines.poly(e.x, e.y, 12, 20f * e.fout()); + Draw.reset(); + }); + + pickup = new Effect(18, e -> { + Draw.color(Pal.lightishGray); + Lines.stroke(e.fout() * 2f); + Lines.spikes(e.x, e.y, 1f + e.fin() * 6f, e.fout() * 4f, 6); + Draw.reset(); + }); + + healWave = new Effect(22, e -> { + Draw.color(Pal.heal); + Lines.stroke(e.fout() * 2f); + Lines.poly(e.x, e.y, 30, 4f + e.finpow() * 60f); + Draw.color(); + }); + + heal = new Effect(11, e -> { + Draw.color(Pal.heal); + Lines.stroke(e.fout() * 2f); + Lines.poly(e.x, e.y, 24, 2f + e.finpow() * 7f); + Draw.color(); + }); + + + hitBulletSmall = new Effect(14, e -> { + Draw.color(Color.WHITE, Pal.lightOrange, e.fin()); + + e.scaled(7f, s -> { + Lines.stroke(0.5f + s.fout()); + Lines.circle(e.x, e.y, s.fin() * 5f); + }); + + + Lines.stroke(0.5f + e.fout()); + + Angles.randLenVectors(e.id, 5, e.fin() * 15f, (x, y) -> { + float ang = Mathf.angle(x, y); + Lines.lineAngle(e.x + x, e.y + y, ang, e.fout() * 3 + 1f); + }); + + Draw.reset(); + }); + + hitFuse = new Effect(14, e -> { + Draw.color(Color.WHITE, Pal.surge, e.fin()); + + e.scaled(7f, s -> { + Lines.stroke(0.5f + s.fout()); + Lines.circle(e.x, e.y, s.fin() * 7f); + }); + + + Lines.stroke(0.5f + e.fout()); + + Angles.randLenVectors(e.id, 6, e.fin() * 15f, (x, y) -> { + float ang = Mathf.angle(x, y); + Lines.lineAngle(e.x + x, e.y + y, ang, e.fout() * 3 + 1f); + }); + + Draw.reset(); + }); + + hitBulletBig = new Effect(13, e -> { + Draw.color(Color.WHITE, Pal.lightOrange, e.fin()); + Lines.stroke(0.5f + e.fout() * 1.5f); + + Angles.randLenVectors(e.id, 8, e.finpow() * 30f, e.rotation, 50f, (x, y) -> { + float ang = Mathf.angle(x, y); + Lines.lineAngle(e.x + x, e.y + y, ang, e.fout() * 4 + 1.5f); + }); + + Draw.reset(); + }); + + hitFlameSmall = new Effect(14, e -> { + Draw.color(Pal.lightFlame, Pal.darkFlame, e.fin()); + Lines.stroke(0.5f + e.fout()); + + Angles.randLenVectors(e.id, 2, e.fin() * 15f, e.rotation, 50f, (x, y) -> { + float ang = Mathf.angle(x, y); + Lines.lineAngle(e.x + x, e.y + y, ang, e.fout() * 3 + 1f); + }); + + Draw.reset(); + }); + + hitLiquid = new Effect(16, e -> { + Draw.color(e.color); + + Angles.randLenVectors(e.id, 5, e.fin() * 15f, e.rotation + 180f, 60f, (x, y) -> { + Fill.circle(e.x + x, e.y + y, e.fout() * 2f); + }); + + Draw.reset(); + }); + + hitLancer = new Effect(12, e -> { + Draw.color(Color.WHITE); + Lines.stroke(e.fout() * 1.5f); + + Angles.randLenVectors(e.id, 8, e.finpow() * 17f, e.rotation, 360f, (x, y) -> { + float ang = Mathf.angle(x, y); + Lines.lineAngle(e.x + x, e.y + y, ang, e.fout() * 4 + 1f); + }); + + Draw.reset(); + }); + + hitMeltdown = new Effect(12, e -> { + Draw.color(Pal.meltdownHit); + Lines.stroke(e.fout() * 2f); + + Angles.randLenVectors(e.id, 6, e.finpow() * 18f, e.rotation, 360f, (x, y) -> { + float ang = Mathf.angle(x, y); + Lines.lineAngle(e.x + x, e.y + y, ang, e.fout() * 4 + 1f); + }); + + Draw.reset(); + }); + + hitLaser = new Effect(8, e -> { + Draw.color(Color.WHITE, Pal.heal, e.fin()); + Lines.stroke(0.5f + e.fout()); + Lines.circle(e.x, e.y, e.fin() * 5f); + Draw.reset(); + }); + + despawn = new Effect(12, e -> { + Draw.color(Pal.lighterOrange, Color.GRAY, e.fin()); + Lines.stroke(e.fout()); + + Angles.randLenVectors(e.id, 7, e.fin() * 7f, e.rotation, 40f, (x, y) -> { + float ang = Mathf.angle(x, y); + Lines.lineAngle(e.x + x, e.y + y, ang, e.fout() * 2 + 1f); + }); + + Draw.reset(); + }); + + flakExplosion = new Effect(20, e -> { + + Draw.color(Pal.bulletYellow); + e.scaled(6, i -> { + Lines.stroke(3f * i.fout()); + Lines.circle(e.x, e.y, 3f + i.fin() * 10f); + }); + + Draw.color(Color.GRAY); + + Angles.randLenVectors(e.id, 5, 2f + 23f * e.finpow(), (x, y) -> { + Fill.circle(e.x + x, e.y + y, e.fout() * 3f + 0.5f); + }); + + Draw.color(Pal.lighterOrange); + Lines.stroke(1f * e.fout()); + + Angles.randLenVectors(e.id + 1, 4, 1f + 23f * e.finpow(), (x, y) -> { + Lines.lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), 1f + e.fout() * 3f); + }); + + Draw.reset(); + }); + + plasticExplosion = new Effect(24, e -> { + + Draw.color(Pal.plastaniumFront); + e.scaled(7, i -> { + Lines.stroke(3f * i.fout()); + Lines.circle(e.x, e.y, 3f + i.fin() * 24f); + }); + + Draw.color(Color.GRAY); + + Angles.randLenVectors(e.id, 7, 2f + 28f * e.finpow(), (x, y) -> { + Fill.circle(e.x + x, e.y + y, e.fout() * 4f + 0.5f); + }); + + Draw.color(Pal.plastaniumBack); + Lines.stroke(1f * e.fout()); + + Angles.randLenVectors(e.id + 1, 4, 1f + 25f * e.finpow(), (x, y) -> { + Lines.lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), 1f + e.fout() * 3f); + }); + + Draw.reset(); + }); + + plasticExplosionFlak = new Effect(28, e -> { + + Draw.color(Pal.plastaniumFront); + e.scaled(7, i -> { + Lines.stroke(3f * i.fout()); + Lines.circle(e.x, e.y, 3f + i.fin() * 34f); + }); + + Draw.color(Color.GRAY); + + Angles.randLenVectors(e.id, 7, 2f + 30f * e.finpow(), (x, y) -> { + Fill.circle(e.x + x, e.y + y, e.fout() * 4f + 0.5f); + }); + + Draw.color(Pal.plastaniumBack); + Lines.stroke(1f * e.fout()); + + Angles.randLenVectors(e.id + 1, 4, 1f + 30f * e.finpow(), (x, y) -> { + Lines.lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), 1f + e.fout() * 3f); + }); + + Draw.reset(); + }); + + blastExplosion = new Effect(22, e -> { + + Draw.color(Pal.missileYellow); + e.scaled(6, i -> { + Lines.stroke(3f * i.fout()); + Lines.circle(e.x, e.y, 3f + i.fin() * 15f); + }); + + Draw.color(Color.GRAY); + + Angles.randLenVectors(e.id, 5, 2f + 23f * e.finpow(), (x, y) -> { + Fill.circle(e.x + x, e.y + y, e.fout() * 4f + 0.5f); + }); + + Draw.color(Pal.missileYellowBack); + Lines.stroke(1f * e.fout()); + + Angles.randLenVectors(e.id + 1, 4, 1f + 23f * e.finpow(), (x, y) -> { + Lines.lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), 1f + e.fout() * 3f); + }); + + Draw.reset(); + }); + + artilleryTrail = new Effect(50, e -> { + Draw.color(e.color); + Fill.circle(e.x, e.y, e.rotation * e.fout()); + Draw.reset(); + }); + + incendTrail = new Effect(50, e -> { + Draw.color(Pal.lightOrange); + Fill.circle(e.x, e.y, e.rotation * e.fout()); + Draw.reset(); + }); + + missileTrail = new Effect(50, e -> { + Draw.color(e.color); + Fill.circle(e.x, e.y, e.rotation * e.fout()); + Draw.reset(); + }); + + absorb = new Effect(12, e -> { + Draw.color(Pal.accent); + Lines.stroke(2f * e.fout()); + Lines.circle(e.x, e.y, 5f * e.fout()); + Draw.reset(); + }); + + flakExplosionBig = new Effect(30, e -> { + + Draw.color(Pal.bulletYellowBack); + e.scaled(6, i -> { + Lines.stroke(3f * i.fout()); + Lines.circle(e.x, e.y, 3f + i.fin() * 25f); + }); + + Draw.color(Color.GRAY); + + Angles.randLenVectors(e.id, 6, 2f + 23f * e.finpow(), (x, y) -> { + Fill.circle(e.x + x, e.y + y, e.fout() * 4f + 0.5f); + }); + + Draw.color(Pal.bulletYellow); + Lines.stroke(1f * e.fout()); + + Angles.randLenVectors(e.id + 1, 4, 1f + 23f * e.finpow(), (x, y) -> { + Lines.lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), 1f + e.fout() * 3f); + }); + + Draw.reset(); + }); + + + burning = new Effect(35f, e -> { + Draw.color(Pal.lightFlame, Pal.darkFlame, e.fin()); + + Angles.randLenVectors(e.id, 3, 2f + e.fin() * 7f, (x, y) -> { + Fill.circle(e.x + x, e.y + y, 0.1f + e.fout() * 1.4f); + }); + + Draw.color(); + }); + + fire = new Effect(35f, e -> { + Draw.color(Pal.lightFlame, Pal.darkFlame, e.fin()); + + Angles.randLenVectors(e.id, 2, 2f + e.fin() * 7f, (x, y) -> { + Fill.circle(e.x + x, e.y + y, 0.2f + e.fslope() * 1.5f); + }); + + Draw.color(); + }); + + fireSmoke = new Effect(35f, e -> { + Draw.color(Color.GRAY); + + Angles.randLenVectors(e.id, 1, 2f + e.fin() * 7f, (x, y) -> { + Fill.circle(e.x + x, e.y + y, 0.2f + e.fslope() * 1.5f); + }); + + Draw.color(); + }); + + steam = new Effect(35f, e -> { + Draw.color(Color.LIGHT_GRAY); + + Angles.randLenVectors(e.id, 2, 2f + e.fin() * 7f, (x, y) -> { + Fill.circle(e.x + x, e.y + y, 0.2f + e.fslope() * 1.5f); + }); + + Draw.color(); + }); + + fireballsmoke = new Effect(25f, e -> { + Draw.color(Color.GRAY); + + Angles.randLenVectors(e.id, 1, 2f + e.fin() * 7f, (x, y) -> { + Fill.circle(e.x + x, e.y + y, 0.2f + e.fout() * 1.5f); + }); + + Draw.color(); + }); + + ballfire = new Effect(25f, e -> { + Draw.color(Pal.lightFlame, Pal.darkFlame, e.fin()); + + Angles.randLenVectors(e.id, 2, 2f + e.fin() * 7f, (x, y) -> { + Fill.circle(e.x + x, e.y + y, 0.2f + e.fout() * 1.5f); + }); + + Draw.color(); + }); + + freezing = new Effect(40f, e -> { + Draw.color(Liquids.cryofluid.color); + + Angles.randLenVectors(e.id, 2, 1f + e.fin() * 2f, (x, y) -> { + Fill.circle(e.x + x, e.y + y, e.fout() * 1.2f); + }); + + Draw.color(); + }); + + melting = new Effect(40f, e -> { + Draw.color(Liquids.slag.color, Color.WHITE, e.fout() / 5f + Mathf.randomSeedRange(e.id, 0.12f)); + + Angles.randLenVectors(e.id, 2, 1f + e.fin() * 3f, (x, y) -> { + Fill.circle(e.x + x, e.y + y, .2f + e.fout() * 1.2f); + }); + + Draw.color(); + }); + + wet = new Effect(40f, e -> { + Draw.color(Liquids.water.color); + + Angles.randLenVectors(e.id, 2, 1f + e.fin() * 2f, (x, y) -> { + Fill.circle(e.x + x, e.y + y, e.fout() * 1f); + }); + + Draw.color(); + }); + + oily = new Effect(42f, e -> { + Draw.color(Liquids.oil.color); + + Angles.randLenVectors(e.id, 2, 1f + e.fin() * 2f, (x, y) -> { + Fill.circle(e.x + x, e.y + y, e.fout() * 1f); + }); + + Draw.color(); + }); + + overdriven = new Effect(20f, e -> { + Draw.color(Pal.accent); + + Angles.randLenVectors(e.id, 2, 1f + e.fin() * 2f, (x, y) -> { + Fill.square(e.x + x, e.y + y, e.fout() * 2.3f + 0.5f); + }); + + Draw.color(); + }); + + dropItem = new Effect(20f, e -> { + float length = 20f * e.finpow(); + float size = 7f * e.fout(); + + Draw.rect(((Item)e.data).icon(Icon.large), e.x + Angles.trnsx(e.rotation, length), e.y + Angles.trnsy(e.rotation, length), size, size); + }); + + + shockwave = new Effect(10f, 80f, e -> { + Draw.color(Color.WHITE, Color.LIGHT_GRAY, e.fin()); + Lines.stroke(e.fout() * 2f + 0.2f); + Lines.circle(e.x, e.y, e.fin() * 28f); + Draw.reset(); + }); + + bigShockwave = new Effect(10f, 80f, e -> { + Draw.color(Color.WHITE, Color.LIGHT_GRAY, e.fin()); + Lines.stroke(e.fout() * 3f); + Lines.circle(e.x, e.y, e.fin() * 50f); + Draw.reset(); + }); + + nuclearShockwave = new Effect(10f, 200f, e -> { + Draw.color(Color.WHITE, Color.LIGHT_GRAY, e.fin()); + Lines.stroke(e.fout() * 3f + 0.2f); + Lines.poly(e.x, e.y, 40, e.fin() * 140f); + Draw.reset(); + }); + + impactShockwave = new Effect(13f, 300f, e -> { + Draw.color(Pal.lighterOrange, Color.LIGHT_GRAY, e.fin()); + Lines.stroke(e.fout() * 4f + 0.2f); + Lines.poly(e.x, e.y, 60, e.fin() * 200f); + Draw.reset(); + }); + + spawnShockwave = new Effect(20f, 400f, e -> { + Draw.color(Color.WHITE, Color.LIGHT_GRAY, e.fin()); + Lines.stroke(e.fout() * 3f + 0.5f); + Lines.poly(e.x, e.y, 40, e.fin() * (e.rotation + 50f)); + Draw.reset(); + }); + + explosion = new Effect(30, e -> { + e.scaled(7, i -> { + Lines.stroke(3f * i.fout()); + Lines.circle(e.x, e.y, 3f + i.fin() * 10f); + }); + + Draw.color(Color.GRAY); + + Angles.randLenVectors(e.id, 6, 2f + 19f * e.finpow(), (x, y) -> { + Fill.circle(e.x + x, e.y + y, e.fout() * 3f + 0.5f); + Fill.circle(e.x + x / 2f, e.y + y / 2f, e.fout() * 1f); + }); + + Draw.color(Pal.lighterOrange, Pal.lightOrange, Color.GRAY, e.fin()); + Lines.stroke(1.5f * e.fout()); + + Angles.randLenVectors(e.id + 1, 8, 1f + 23f * e.finpow(), (x, y) -> { + Lines.lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), 1f + e.fout() * 3f); + }); + + Draw.reset(); + }); + + dynamicExplosion = new Effect(30, e -> { + float intensity = e.rotation; + + e.scaled(5 + intensity * 2, i -> { + Lines.stroke(3.1f * i.fout()); + Lines.poly(e.x, e.y, (int)(20 * intensity), (3f + i.fin() * 14f) * intensity); + }); + + Draw.color(Color.GRAY); + + Angles.randLenVectors(e.id, e.finpow(), (int)(6 * intensity), 21f * intensity, (x, y, in, out) -> { + Fill.circle(e.x + x, e.y + y, out * (2f + intensity) * 3 + 0.5f); + Fill.circle(e.x + x / 2f, e.y + y / 2f, out * (intensity) * 3); + }); + + Draw.color(Pal.lighterOrange, Pal.lightOrange, Color.GRAY, e.fin()); + Lines.stroke((1.7f * e.fout()) * (1f + (intensity - 1f) / 2f)); + + Angles.randLenVectors(e.id + 1, e.finpow(), (int)(9 * intensity), 40f * intensity, (x, y, in, out) -> { + Lines.lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), 1f + out * 4 * (3f + intensity)); + }); + + Draw.reset(); + }); + + blockExplosion = new Effect(30, e -> { + e.scaled(7, i -> { + Lines.stroke(3.1f * i.fout()); + Lines.circle(e.x, e.y, 3f + i.fin() * 14f); + }); + + Draw.color(Color.GRAY); + + Angles.randLenVectors(e.id, 6, 2f + 19f * e.finpow(), (x, y) -> { + Fill.circle(e.x + x, e.y + y, e.fout() * 3f + 0.5f); + Fill.circle(e.x + x / 2f, e.y + y / 2f, e.fout() * 1f); + }); + + Draw.color(Pal.lighterOrange, Pal.lightOrange, Color.GRAY, e.fin()); + Lines.stroke(1.7f * e.fout()); + + Angles.randLenVectors(e.id + 1, 9, 1f + 23f * e.finpow(), (x, y) -> { + Lines.lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), 1f + e.fout() * 3f); + }); + + Draw.reset(); + }); + + blockExplosionSmoke = new Effect(30, e -> { + Draw.color(Color.GRAY); + + Angles.randLenVectors(e.id, 6, 4f + 30f * e.finpow(), (x, y) -> { + Fill.circle(e.x + x, e.y + y, e.fout() * 3f); + Fill.circle(e.x + x / 2f, e.y + y / 2f, e.fout() * 1f); + }); + + Draw.reset(); + }); + + + shootSmall = new Effect(8, e -> { + Draw.color(Pal.lighterOrange, Pal.lightOrange, e.fin()); + float w = 1f + 5 * e.fout(); + Shapes.tri(e.x, e.y, w, 15f * e.fout(), e.rotation); + Shapes.tri(e.x, e.y, w, 3f * e.fout(), e.rotation + 180f); + Draw.reset(); + }); + + shootHeal = new Effect(8, e -> { + Draw.color(Pal.heal); + float w = 1f + 5 * e.fout(); + Shapes.tri(e.x, e.y, w, 17f * e.fout(), e.rotation); + Shapes.tri(e.x, e.y, w, 4f * e.fout(), e.rotation + 180f); + Draw.reset(); + }); + + shootSmallSmoke = new Effect(20f, e -> { + Draw.color(Pal.lighterOrange, Color.LIGHT_GRAY, Color.GRAY, e.fin()); + + Angles.randLenVectors(e.id, 5, e.finpow() * 6f, e.rotation, 20f, (x, y) -> { + Fill.circle(e.x + x, e.y + y, e.fout() * 1.5f); + }); + + Draw.reset(); + }); + + shootBig = new Effect(9, e -> { + Draw.color(Pal.lighterOrange, Pal.lightOrange, e.fin()); + float w = 1.2f + 7 * e.fout(); + Shapes.tri(e.x, e.y, w, 25f * e.fout(), e.rotation); + Shapes.tri(e.x, e.y, w, 4f * e.fout(), e.rotation + 180f); + Draw.reset(); + }); + + shootBig2 = new Effect(10, e -> { + Draw.color(Pal.lightOrange, Color.GRAY, e.fin()); + float w = 1.2f + 8 * e.fout(); + Shapes.tri(e.x, e.y, w, 29f * e.fout(), e.rotation); + Shapes.tri(e.x, e.y, w, 5f * e.fout(), e.rotation + 180f); + Draw.reset(); + }); + + shootBigSmoke = new Effect(17f, e -> { + Draw.color(Pal.lighterOrange, Color.LIGHT_GRAY, Color.GRAY, e.fin()); + + Angles.randLenVectors(e.id, 8, e.finpow() * 19f, e.rotation, 10f, (x, y) -> { + Fill.circle(e.x + x, e.y + y, e.fout() * 2f + 0.2f); + }); + + Draw.reset(); + }); + + shootBigSmoke2 = new Effect(18f, e -> { + Draw.color(Pal.lightOrange, Color.LIGHT_GRAY, Color.GRAY, e.fin()); + + Angles.randLenVectors(e.id, 9, e.finpow() * 23f, e.rotation, 20f, (x, y) -> { + Fill.circle(e.x + x, e.y + y, e.fout() * 2.4f + 0.2f); + }); + + Draw.reset(); + }); + + shootSmallFlame = new Effect(32f, e -> { + Draw.color(Pal.lightFlame, Pal.darkFlame, Color.GRAY, e.fin()); + + Angles.randLenVectors(e.id, 8, e.finpow() * 44f, e.rotation, 10f, (x, y) -> { + Fill.circle(e.x + x, e.y + y, 0.65f + e.fout() * 1.5f); + }); + + Draw.reset(); + }); + + shootPyraFlame = new Effect(33f, e -> { + Draw.color(Pal.lightPyraFlame, Pal.darkPyraFlame, Color.GRAY, e.fin()); + + Angles.randLenVectors(e.id, 10, e.finpow() * 50f, e.rotation, 10f, (x, y) -> { + Fill.circle(e.x + x, e.y + y, 0.65f + e.fout() * 1.6f); + }); + + Draw.reset(); + }); + + shootLiquid = new Effect(40f, e -> { + Draw.color(e.color, Color.WHITE, e.fout() / 6f + Mathf.randomSeedRange(e.id, 0.1f)); + + Angles.randLenVectors(e.id, 6, e.finpow() * 60f, e.rotation, 11f, (x, y) -> { + Fill.circle(e.x + x, e.y + y, 0.5f + e.fout() * 2.5f); + }); + + Draw.reset(); + }); + + shellEjectSmall = new GroundEffect(30f, 400f, e -> { + Draw.color(Pal.lightOrange, Color.LIGHT_GRAY, Pal.lightishGray, e.fin()); + float rot = Math.abs(e.rotation) + 90f; + + int i = Mathf.sign(e.rotation); + + float len = (2f + e.finpow() * 6f) * i; + float lr = rot + e.fin() * 30f * i; + Fill.rect(e.x + Angles.trnsx(lr, len) + Mathf.randomSeedRange(e.id + i + 7, 3f * e.fin()), + e.y + Angles.trnsy(lr, len) + Mathf.randomSeedRange(e.id + i + 8, 3f * e.fin()), + 1f, 2f, rot + e.fin() * 50f * i); + + Draw.color(); + }); + + shellEjectMedium = new GroundEffect(34f, 400f, e -> { + Draw.color(Pal.lightOrange, Color.LIGHT_GRAY, Pal.lightishGray, e.fin()); + float rot = e.rotation + 90f; + for(int i : Mathf.signs){ + float len = (2f + e.finpow() * 10f) * i; + float lr = rot + e.fin() * 20f * i; + Draw.rect(Core.atlas.find("casing"), + e.x + Angles.trnsx(lr, len) + Mathf.randomSeedRange(e.id + i + 7, 3f * e.fin()), + e.y + Angles.trnsy(lr, len) + Mathf.randomSeedRange(e.id + i + 8, 3f * e.fin()), + 2f, 3f, rot); + } + + Draw.color(Color.LIGHT_GRAY, Color.GRAY, e.fin()); + + for(int i : Mathf.signs){ + float ex = e.x, ey = e.y, fout = e.fout(); + Angles.randLenVectors(e.id, 4, 1f + e.finpow() * 11f, e.rotation + 90f * i, 20f, (x, y) -> { + Fill.circle(ex + x, ey + y, fout * 1.5f); + }); + } + + Draw.color(); + }); + + shellEjectBig = new GroundEffect(22f, 400f, e -> { + Draw.color(Pal.lightOrange, Color.LIGHT_GRAY, Pal.lightishGray, e.fin()); + float rot = e.rotation + 90f; + for(int i : Mathf.signs){ + float len = (4f + e.finpow() * 8f) * i; + float lr = rot + Mathf.randomSeedRange(e.id + i + 6, 20f * e.fin()) * i; + Draw.rect(Core.atlas.find("casing"), + e.x + Angles.trnsx(lr, len) + Mathf.randomSeedRange(e.id + i + 7, 3f * e.fin()), + e.y + Angles.trnsy(lr, len) + Mathf.randomSeedRange(e.id + i + 8, 3f * e.fin()), + 2.5f, 4f, + rot + e.fin() * 30f * i + Mathf.randomSeedRange(e.id + i + 9, 40f * e.fin())); + } + + Draw.color(Color.LIGHT_GRAY); + + for(int i : Mathf.signs){ + float ex = e.x, ey = e.y, fout = e.fout(); + Angles.randLenVectors(e.id, 4, -e.finpow() * 15f, e.rotation + 90f * i, 25f, (x, y) -> { + Fill.circle(ex + x, ey + y, fout * 2f); + }); + } + + Draw.color(); + }); + + lancerLaserShoot = new Effect(21f, e -> { + Draw.color(Pal.lancerLaser); + + for(int i : Mathf.signs){ + Shapes.tri(e.x, e.y, 4f * e.fout(), 29f, e.rotation + 90f * i); + } + + Draw.reset(); + }); + + lancerLaserShootSmoke = new Effect(26f, e -> { + Draw.color(Pal.lancerLaser); + + Angles.randLenVectors(e.id, 7, 80f, e.rotation, 0f, (x, y) -> { + Lines.lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), e.fout() * 9f); + }); + + Draw.reset(); + }); + + lancerLaserCharge = new Effect(38f, e -> { + Draw.color(Pal.lancerLaser); + + Angles.randLenVectors(e.id, 2, 1f + 20f * e.fout(), e.rotation, 120f, (x, y) -> { + Lines.lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), e.fslope() * 3f + 1f); + }); + + Draw.reset(); + }); + + lancerLaserChargeBegin = new Effect(71f, e -> { + Draw.color(Pal.lancerLaser); + Fill.circle(e.x, e.y, e.fin() * 3f); + + Draw.color(); + Fill.circle(e.x, e.y, e.fin() * 2f); + }); + + lightningCharge = new Effect(38f, e -> { + Draw.color(Pal.lancerLaser); + + Angles.randLenVectors(e.id, 2, 1f + 20f * e.fout(), e.rotation, 120f, (x, y) -> { + Shapes.tri(e.x + x, e.y + y, e.fslope() * 3f + 1, e.fslope() * 3f + 1, Mathf.angle(x, y)); + }); + + Draw.reset(); + }); + + lightningShoot = new Effect(12f, e -> { + Draw.color(Color.WHITE, Pal.lancerLaser, e.fin()); + Lines.stroke(e.fout() * 1.2f + 0.5f); + + Angles.randLenVectors(e.id, 7, 25f * e.finpow(), e.rotation, 50f, (x, y) -> { + Lines.lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), e.fin() * 5f + 2f); + }); + + Draw.reset(); + }); + + + reactorsmoke = new Effect(17, e -> { + Angles.randLenVectors(e.id, 4, e.fin() * 8f, (x, y) -> { + float size = 1f + e.fout() * 5f; + Draw.color(Color.LIGHT_GRAY, Color.GRAY, e.fin()); + Draw.rect("circle", e.x + x, e.y + y, size, size); + Draw.reset(); + }); + }); + nuclearsmoke = new Effect(40, e -> { + Angles.randLenVectors(e.id, 4, e.fin() * 13f, (x, y) -> { + float size = e.fslope() * 4f; + Draw.color(Color.LIGHT_GRAY, Color.GRAY, e.fin()); + Draw.rect("circle", e.x + x, e.y + y, size, size); + Draw.reset(); + }); + }); + nuclearcloud = new Effect(90, 200f, e -> { + Angles.randLenVectors(e.id, 10, e.finpow() * 90f, (x, y) -> { + float size = e.fout() * 14f; + Draw.color(Color.LIME, Color.GRAY, e.fin()); + Draw.rect("circle", e.x + x, e.y + y, size, size); + Draw.reset(); + }); + }); + impactsmoke = new Effect(60, e -> { + Angles.randLenVectors(e.id, 7, e.fin() * 20f, (x, y) -> { + float size = e.fslope() * 4f; + Draw.color(Color.LIGHT_GRAY, Color.GRAY, e.fin()); + Draw.rect("circle", e.x + x, e.y + y, size, size); + Draw.reset(); + }); + }); + impactcloud = new Effect(140, 400f, e -> { + Angles.randLenVectors(e.id, 20, e.finpow() * 160f, (x, y) -> { + float size = e.fout() * 15f; + Draw.color(Pal.lighterOrange, Color.LIGHT_GRAY, e.fin()); + Draw.rect("circle", e.x + x, e.y + y, size, size); + Draw.reset(); + }); + }); + redgeneratespark = new Effect(18, e -> { + Angles.randLenVectors(e.id, 5, e.fin() * 8f, (x, y) -> { + float len = e.fout() * 4f; + Draw.color(Pal.redSpark, Color.GRAY, e.fin()); + Draw.rect("circle", e.x + x, e.y + y, len, len); + Draw.reset(); + }); + }); + generatespark = new Effect(18, e -> { + Angles.randLenVectors(e.id, 5, e.fin() * 8f, (x, y) -> { + float len = e.fout() * 4f; + Draw.color(Pal.orangeSpark, Color.GRAY, e.fin()); + Draw.rect("circle", e.x + x, e.y + y, len, len); + Draw.reset(); + }); + }); + fuelburn = new Effect(23, e -> { + Angles.randLenVectors(e.id, 5, e.fin() * 9f, (x, y) -> { + float len = e.fout() * 4f; + Draw.color(Color.LIGHT_GRAY, Color.GRAY, e.fin()); + Draw.rect("circle", e.x + x, e.y + y, len, len); + Draw.reset(); + }); + }); + plasticburn = new Effect(40, e -> { + Angles.randLenVectors(e.id, 5, 3f + e.fin() * 5f, (x, y) -> { + Draw.color(Color.valueOf("e9ead3"), Color.GRAY, e.fin()); + Fill.circle(e.x + x, e.y + y, e.fout() * 1f); + Draw.reset(); + }); + }); + pulverize = new Effect(40, e -> { + Angles.randLenVectors(e.id, 5, 3f + e.fin() * 8f, (x, y) -> { + Draw.color(Pal.stoneGray); + Fill.square(e.x + x, e.y + y, e.fout() * 2f + 0.5f, 45); + Draw.reset(); + }); + }); + pulverizeRed = new Effect(40, e -> { + Angles.randLenVectors(e.id, 5, 3f + e.fin() * 8f, (x, y) -> { + Draw.color(Pal.redDust, Pal.stoneGray, e.fin()); + Fill.square(e.x + x, e.y + y, e.fout() * 2f + 0.5f, 45); + Draw.reset(); + }); + }); + pulverizeRedder = new Effect(40, e -> { + Angles.randLenVectors(e.id, 5, 3f + e.fin() * 9f, (x, y) -> { + Draw.color(Pal.redderDust, Pal.stoneGray, e.fin()); + Fill.square(e.x + x, e.y + y, e.fout() * 2.5f + 0.5f, 45); + Draw.reset(); + }); + }); + pulverizeSmall = new Effect(30, e -> { + Angles.randLenVectors(e.id, 3, e.fin() * 5f, (x, y) -> { + Draw.color(Pal.stoneGray); + Fill.square(e.x + x, e.y + y, e.fout() * 1f + 0.5f, 45); + Draw.reset(); + }); + }); + pulverizeMedium = new Effect(30, e -> { + Angles.randLenVectors(e.id, 5, 3f + e.fin() * 8f, (x, y) -> { + Draw.color(Pal.stoneGray); + Fill.square(e.x + x, e.y + y, e.fout() * 1f + 0.5f, 45); + Draw.reset(); + }); + }); + producesmoke = new Effect(12, e -> { + Angles.randLenVectors(e.id, 8, 4f + e.fin() * 18f, (x, y) -> { + Draw.color(Color.WHITE, Pal.accent, e.fin()); + Fill.square(e.x + x, e.y + y, 1f + e.fout() * 3f, 45); + Draw.reset(); + }); + }); + smeltsmoke = new Effect(15, e -> { + Angles.randLenVectors(e.id, 6, 4f + e.fin() * 5f, (x, y) -> { + Draw.color(Color.WHITE, e.color, e.fin()); + Fill.square(e.x + x, e.y + y, 0.5f + e.fout() * 2f, 45); + Draw.reset(); + }); + }); + formsmoke = new Effect(40, e -> { + Angles.randLenVectors(e.id, 6, 5f + e.fin() * 8f, (x, y) -> { + Draw.color(Pal.plasticSmoke, Color.LIGHT_GRAY, e.fin()); + Fill.square(e.x + x, e.y + y, 0.2f + e.fout() * 2f, 45); + Draw.reset(); + }); + }); + blastsmoke = new Effect(26, e -> { + Angles.randLenVectors(e.id, 12, 1f + e.fin() * 23f, (x, y) -> { + float size = 2f + e.fout() * 6f; + Draw.color(Color.LIGHT_GRAY, Color.DARK_GRAY, e.fin()); + Draw.rect("circle", e.x + x, e.y + y, size, size); + Draw.reset(); + }); + }); + lava = new Effect(18, e -> { + Angles.randLenVectors(e.id, 3, 1f + e.fin() * 10f, (x, y) -> { + float size = e.fslope() * 4f; + Draw.color(Color.ORANGE, Color.GRAY, e.fin()); + Draw.rect("circle", e.x + x, e.y + y, size, size); + Draw.reset(); + }); + }); + dooropen = new Effect(10, e -> { + Lines.stroke(e.fout() * 1.6f); + Lines.square(e.x, e.y, tilesize / 2f + e.fin() * 2f); + Draw.reset(); + }); + doorclose = new Effect(10, e -> { + Lines.stroke(e.fout() * 1.6f); + Lines.square(e.x, e.y, tilesize / 2f + e.fout() * 2f); + Draw.reset(); + }); + dooropenlarge = new Effect(10, e -> { + Lines.stroke(e.fout() * 1.6f); + Lines.square(e.x, e.y, tilesize + e.fin() * 2f); + Draw.reset(); + }); + doorcloselarge = new Effect(10, e -> { + Lines.stroke(e.fout() * 1.6f); + Lines.square(e.x, e.y, tilesize + e.fout() * 2f); + Draw.reset(); + }); + purify = new Effect(10, e -> { + Draw.color(Color.ROYAL, Color.GRAY, e.fin()); + Lines.stroke(2f); + Lines.spikes(e.x, e.y, e.fin() * 4f, 2, 6); + Draw.reset(); + }); + purifyoil = new Effect(10, e -> { + Draw.color(Color.BLACK, Color.GRAY, e.fin()); + Lines.stroke(2f); + Lines.spikes(e.x, e.y, e.fin() * 4f, 2, 6); + Draw.reset(); + }); + purifystone = new Effect(10, e -> { + Draw.color(Color.ORANGE, Color.GRAY, e.fin()); + Lines.stroke(2f); + Lines.spikes(e.x, e.y, e.fin() * 4f, 2, 6); + Draw.reset(); + }); + generate = new Effect(11, e -> { + Draw.color(Color.ORANGE, Color.YELLOW, e.fin()); + Lines.stroke(1f); + Lines.spikes(e.x, e.y, e.fin() * 5f, 2, 8); + Draw.reset(); + }); + mine = new Effect(20, e -> { + Angles.randLenVectors(e.id, 6, 3f + e.fin() * 6f, (x, y) -> { + Draw.color(e.color, Color.LIGHT_GRAY, e.fin()); + Fill.square(e.x + x, e.y + y, e.fout() * 2f, 45); + Draw.reset(); + }); + }); + mineBig = new Effect(30, e -> { + Angles.randLenVectors(e.id, 6, 4f + e.fin() * 8f, (x, y) -> { + Draw.color(e.color, Color.LIGHT_GRAY, e.fin()); + Fill.square(e.x + x, e.y + y, e.fout() * 2f + 0.2f, 45); + Draw.reset(); + }); + }); + mineHuge = new Effect(40, e -> { + Angles.randLenVectors(e.id, 8, 5f + e.fin() * 10f, (x, y) -> { + Draw.color(e.color, Color.LIGHT_GRAY, e.fin()); + Fill.square(e.x + x, e.y + y, e.fout() * 2f + 0.5f, 45); + Draw.reset(); + }); + }); + smelt = new Effect(20, e -> { + Angles.randLenVectors(e.id, 6, 2f + e.fin() * 5f, (x, y) -> { + Draw.color(Color.WHITE, e.color, e.fin()); + Fill.square(e.x + x, e.y + y, 0.5f + e.fout() * 2f, 45); + Draw.reset(); + }); + }); + teleportActivate = new Effect(50, e -> { + Draw.color(e.color); + + e.scaled(8f, e2 -> { + Lines.stroke(e2.fout() * 4f); + Lines.circle(e2.x, e2.y, 4f + e2.fin() * 27f); + }); + + Lines.stroke(e.fout() * 2f); + + Angles.randLenVectors(e.id, 30, 4f + 40f * e.fin(), (x, y) -> { + Lines.lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), e.fin() * 4f + 1f); + }); + + Draw.reset(); + }); + teleport = new Effect(60, e -> { + Draw.color(e.color); + Lines.stroke(e.fin() * 2f); + Lines.circle(e.x, e.y, 7f + e.fout() * 8f); + + Angles.randLenVectors(e.id, 20, 6f + 20f * e.fout(), (x, y) -> { + Lines.lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), e.fin() * 4f + 1f); + }); + + Draw.reset(); + }); + teleportOut = new Effect(20, e -> { + Draw.color(e.color); + Lines.stroke(e.fout() * 2f); + Lines.circle(e.x, e.y, 7f + e.fin() * 8f); + + Angles.randLenVectors(e.id, 20, 4f + 20f * e.fin(), (x, y) -> { + Lines.lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), e.fslope() * 4f + 1f); + }); + + Draw.reset(); + }); + ripple = new GroundEffect(false, 30, e -> { + Draw.color(Tmp.c1.set(e.color).mul(1.2f)); + Lines.stroke(e.fout() + 0.4f); + Lines.circle(e.x, e.y, 2f + e.fin() * 4f); + Draw.reset(); + }); + + bubble = new Effect(20, e -> { + Draw.color(Tmp.c1.set(e.color).shiftValue(0.1f)); + Lines.stroke(e.fout() + 0.2f); + Angles.randLenVectors(e.id, 2, 8f, (x, y) -> { + Lines.circle(e.x + x, e.y + y, 1f + e.fin() * 3f); + }); + Draw.reset(); + }); + + launch = new Effect(28, e -> { + Draw.color(Pal.command); + Lines.stroke(e.fout() * 2f); + Lines.poly(e.x, e.y, 40, 4f + e.finpow() * 120f); + Draw.color(); + }); + + healWaveMend = new Effect(40, e -> { + Draw.color(e.color); + Lines.stroke(e.fout() * 2f); + Lines.poly(e.x, e.y, 30, e.finpow() * e.rotation); + Draw.color(); + }); + + overdriveWave = new Effect(50, e -> { + Draw.color(e.color); + Lines.stroke(e.fout() * 1f); + Lines.poly(e.x, e.y, 30, e.finpow() * e.rotation); + Draw.color(); + }); + + healBlock = new Effect(20, e -> { + Draw.color(Pal.heal); + Lines.stroke(2f * e.fout() + 0.5f); + Lines.square(e.x, e.y, 1f + (e.fin() * e.rotation * tilesize / 2f - 1f)); + Draw.color(); + }); + + healBlockFull = new Effect(20, e -> { + Draw.color(e.color); + Draw.alpha(e.fout()); + Fill.square(e.x, e.y, e.rotation * tilesize / 2f); + Draw.color(); + }); + + overdriveBlockFull = new Effect(60, e -> { + Draw.color(e.color); + Draw.alpha(e.fslope() * 0.4f); + Fill.square(e.x, e.y, e.rotation * tilesize); + Draw.color(); + }); + + shieldBreak = new Effect(40, e -> { + Draw.color(Pal.accent); + Lines.stroke(3f * e.fout()); + Lines.poly(e.x, e.y, 6, e.rotation + e.fin(), 90); + Draw.reset(); + }); + } +} diff --git a/core/src/io/anuke/mindustry/content/Items.java b/core/src/io/anuke/mindustry/content/Items.java new file mode 100644 index 0000000000..6ee41034c2 --- /dev/null +++ b/core/src/io/anuke/mindustry/content/Items.java @@ -0,0 +1,101 @@ +package io.anuke.mindustry.content; + +import io.anuke.arc.graphics.Color; +import io.anuke.mindustry.game.ContentList; +import io.anuke.mindustry.type.Item; +import io.anuke.mindustry.type.ItemType; + +public class Items implements ContentList{ + public static Item scrap, copper, lead, graphite, coal, titanium, thorium, silicon, plastanium, phasefabric, surgealloy, + sporePod, sand, blastCompound, pyratite, metaglass; + + @Override + public void load(){ + copper = new Item("copper", Color.valueOf("d99d73")){{ + type = ItemType.material; + hardness = 1; + cost = 0.5f; + alwaysUnlocked = true; + }}; + + lead = new Item("lead", Color.valueOf("8c7fa9")){{ + type = ItemType.material; + hardness = 1; + cost = 0.7f; + }}; + + metaglass = new Item("metaglass", Color.valueOf("ebeef5")){{ + type = ItemType.material; + cost = 1.5f; + }}; + + graphite = new Item("graphite", Color.valueOf("b2c6d2")){{ + type = ItemType.material; + cost = 1f; + }}; + + sand = new Item("sand", Color.valueOf("f7cba4")){{ + + }}; + + coal = new Item("coal", Color.valueOf("272727")){{ + explosiveness = 0.4f; + flammability = 1f; + hardness = 2; + }}; + + titanium = new Item("titanium", Color.valueOf("8da1e3")){{ + type = ItemType.material; + hardness = 3; + cost = 1f; + }}; + + thorium = new Item("thorium", Color.valueOf("f9a3c7")){{ + type = ItemType.material; + explosiveness = 0.2f; + hardness = 4; + radioactivity = 1f; + cost = 1.1f; + }}; + + scrap = new Item("scrap", Color.valueOf("777777")){{ + + }}; + + silicon = new Item("silicon", Color.valueOf("53565c")){{ + type = ItemType.material; + cost = 0.8f; + }}; + + plastanium = new Item("plastanium", Color.valueOf("cbd97f")){{ + type = ItemType.material; + flammability = 0.1f; + explosiveness = 0.2f; + cost = 1.3f; + }}; + + phasefabric = new Item("phase-fabric", Color.valueOf("f4ba6e")){{ + type = ItemType.material; + cost = 1.3f; + radioactivity = 0.6f; + }}; + + surgealloy = new Item("surge-alloy", Color.valueOf("f3e979")){{ + type = ItemType.material; + }}; + + sporePod = new Item("spore-pod", Color.valueOf("7457ce")){{ + flammability = 1.05f; + }}; + + blastCompound = new Item("blast-compound", Color.valueOf("ff795e")){{ + flammability = 0.4f; + explosiveness = 1.2f; + }}; + + pyratite = new Item("pyratite", Color.valueOf("ffaa5f")){{ + flammability = 1.4f; + explosiveness = 0.4f; + }}; + } +} diff --git a/core/src/io/anuke/mindustry/content/Liquids.java b/core/src/io/anuke/mindustry/content/Liquids.java new file mode 100644 index 0000000000..17cc695c0e --- /dev/null +++ b/core/src/io/anuke/mindustry/content/Liquids.java @@ -0,0 +1,38 @@ +package io.anuke.mindustry.content; + +import io.anuke.arc.graphics.Color; +import io.anuke.mindustry.game.ContentList; +import io.anuke.mindustry.type.Liquid; + +public class Liquids implements ContentList{ + public static Liquid water, slag, oil, cryofluid; + + @Override + public void load(){ + + water = new Liquid("water", Color.valueOf("596ab8")){{ + heatCapacity = 0.4f; + effect = StatusEffects.wet; + }}; + + slag = new Liquid("slag", Color.valueOf("ffa166")){{ + temperature = 1f; + viscosity = 0.8f; + effect = StatusEffects.melting; + }}; + + oil = new Liquid("oil", Color.valueOf("313131")){{ + viscosity = 0.7f; + flammability = 1.2f; + explosiveness = 1.2f; + heatCapacity = 0.7f; + effect = StatusEffects.tarred; + }}; + + cryofluid = new Liquid("cryofluid", Color.valueOf("6ecdec")){{ + heatCapacity = 0.9f; + temperature = 0.25f; + effect = StatusEffects.freezing; + }}; + } +} diff --git a/core/src/io/anuke/mindustry/content/Loadouts.java b/core/src/io/anuke/mindustry/content/Loadouts.java new file mode 100644 index 0000000000..a9c7f5bc3f --- /dev/null +++ b/core/src/io/anuke/mindustry/content/Loadouts.java @@ -0,0 +1,54 @@ +package io.anuke.mindustry.content; + +import io.anuke.mindustry.game.ContentList; +import io.anuke.mindustry.type.Loadout; + +public class Loadouts implements ContentList{ + public static Loadout + basicShard, + advancedShard, + basicFoundation, + basicNucleus; + + @Override + public void load(){ + basicShard = new Loadout( + " ### ", + " #1# ", + " ### ", + " ^ ^ ", + " ## ## ", + " C# C# " + ); + + advancedShard = new Loadout( + " ### ", + " #1# ", + "#######", + "C#^ ^C#", + " ## ## ", + " C# C# " + ); + + basicFoundation = new Loadout( + " #### ", + " #### ", + " #2## ", + " #### ", + " ^^^^ ", + " ###### ", + " C#C#C# " + ); + + basicNucleus = new Loadout( + " ##### ", + " ##### ", + " ##3## ", + " ##### ", + " >#####< ", + " ^ ^ ^ ^ ", + "#### ####", + "C#C# C#C#" + ); + } +} diff --git a/core/src/io/anuke/mindustry/content/Mechs.java b/core/src/io/anuke/mindustry/content/Mechs.java new file mode 100644 index 0000000000..801029bae9 --- /dev/null +++ b/core/src/io/anuke/mindustry/content/Mechs.java @@ -0,0 +1,374 @@ +package io.anuke.mindustry.content; + +import io.anuke.arc.Core; +import io.anuke.arc.graphics.Blending; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.graphics.g2d.Draw; +import io.anuke.arc.graphics.g2d.TextureRegion; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.util.Time; +import io.anuke.mindustry.entities.Effects; +import io.anuke.mindustry.entities.Units; +import io.anuke.mindustry.entities.bullet.BombBulletType; +import io.anuke.mindustry.entities.effect.Lightning; +import io.anuke.mindustry.entities.type.Player; +import io.anuke.mindustry.game.ContentList; +import io.anuke.mindustry.graphics.Pal; +import io.anuke.mindustry.graphics.Shaders; +import io.anuke.mindustry.type.Mech; +import io.anuke.mindustry.type.Weapon; + +public class Mechs implements ContentList{ + public static Mech alpha, delta, tau, omega, dart, javelin, trident, glaive; + + public static Mech starter; + + @Override + public void load(){ + + alpha = new Mech("alpha-mech", false){ + { + drillPower = 1; + mineSpeed = 1.5f; + mass = 1.2f; + speed = 0.5f; + boostSpeed = 0.95f; + buildPower = 1.2f; + engineColor = Color.valueOf("ffd37f"); + health = 250f; + + weapon = new Weapon("blaster"){{ + length = 1.5f; + reload = 14f; + roundrobin = true; + ejectEffect = Fx.shellEjectSmall; + bullet = Bullets.standardMechSmall; + }}; + } + + @Override + public void updateAlt(Player player){ + player.healBy(Time.delta() * 0.09f); + } + + }; + + delta = new Mech("delta-mech", false){ + float cooldown = 120; + + { + drillPower = -1; + speed = 0.75f; + boostSpeed = 0.95f; + itemCapacity = 15; + mass = 0.9f; + health = 150f; + buildPower = 0.9f; + weaponOffsetX = -1; + weaponOffsetY = -1; + engineColor = Color.valueOf("d3ddff"); + + weapon = new Weapon("shockgun"){{ + shake = 2f; + length = 1f; + reload = 55f; + shotDelay = 3f; + roundrobin = true; + shots = 2; + inaccuracy = 0f; + ejectEffect = Fx.none; + bullet = Bullets.lightning; + }}; + } + + @Override + public void onLand(Player player){ + if(player.timer.get(Player.timerAbility, cooldown)){ + Effects.shake(1f, 1f, player); + Effects.effect(Fx.landShock, player); + for(int i = 0; i < 8; i++){ + Time.run(Mathf.random(8f), () -> Lightning.create(player.getTeam(), Pal.lancerLaser, 17f, player.x, player.y, Mathf.random(360f), 14)); + } + } + } + }; + + tau = new Mech("tau-mech", false){ + float healRange = 60f; + float healAmount = 10f; + float healReload = 160f; + boolean wasHealed; + + { + drillPower = 4; + mineSpeed = 3f; + itemCapacity = 70; + weaponOffsetY = -1; + weaponOffsetX = 1; + mass = 1.75f; + speed = 0.44f; + drag = 0.35f; + boostSpeed = 0.8f; + canHeal = true; + health = 200f; + buildPower = 1.6f; + engineColor = Pal.heal; + + weapon = new Weapon("heal-blaster"){{ + length = 1.5f; + reload = 24f; + roundrobin = false; + ejectEffect = Fx.none; + recoil = 2f; + bullet = Bullets.healBullet; + }}; + } + + @Override + public void updateAlt(Player player){ + + if(player.timer.get(Player.timerAbility, healReload)){ + wasHealed = false; + + Units.nearby(player.getTeam(), player.x, player.y, healRange, unit -> { + if(unit.health < unit.maxHealth()){ + Effects.effect(Fx.heal, unit); + wasHealed = true; + } + unit.healBy(healAmount); + }); + + if(wasHealed){ + Effects.effect(Fx.healWave, player); + } + } + } + }; + + omega = new Mech("omega-mech", false){ + protected TextureRegion armorRegion; + + { + drillPower = 2; + mineSpeed = 1.5f; + itemCapacity = 50; + speed = 0.36f; + boostSpeed = 0.6f; + mass = 4f; + shake = 4f; + weaponOffsetX = 1; + weaponOffsetY = 0; + engineColor = Color.valueOf("feb380"); + health = 350f; + buildPower = 1.5f; + weapon = new Weapon("swarmer"){{ + length = 1.5f; + recoil = 4f; + reload = 38f; + shots = 4; + spacing = 8f; + inaccuracy = 8f; + roundrobin = true; + ejectEffect = Fx.none; + shake = 3f; + bullet = Bullets.missileSwarm; + }}; + } + + @Override + public float getRotationAlpha(Player player){ + return 0.6f - player.shootHeat * 0.3f; + } + + @Override + public float spreadX(Player player){ + return player.shootHeat * 2f; + } + + @Override + public void load(){ + super.load(); + armorRegion = Core.atlas.find(name + "-armor"); + } + + @Override + public void updateAlt(Player player){ + float scl = 1f - player.shootHeat / 2f; + player.velocity().scl(scl); + } + + @Override + public float getExtraArmor(Player player){ + return player.shootHeat * 30f; + } + + @Override + public void draw(Player player){ + if(player.shootHeat <= 0.01f) return; + + Shaders.build.progress = player.shootHeat; + Shaders.build.region = armorRegion; + Shaders.build.time = Time.time() / 10f; + Shaders.build.color.set(Pal.accent).a = player.shootHeat; + Draw.shader(Shaders.build); + Draw.rect(armorRegion, player.x, player.y, player.rotation); + Draw.shader(); + } + }; + + dart = new Mech("dart-ship", true){ + { + drillPower = 1; + mineSpeed = 0.9f; + speed = 0.5f; + drag = 0.09f; + health = 200f; + weaponOffsetX = -1; + weaponOffsetY = -1; + engineColor = Pal.lightTrail; + cellTrnsY = 1f; + buildPower = 1.1f; + weapon = new Weapon("blaster"){{ + length = 1.5f; + reload = 15f; + roundrobin = true; + ejectEffect = Fx.shellEjectSmall; + bullet = Bullets.standardCopper; + }}; + } + + @Override + public boolean alwaysUnlocked(){ + return true; + } + }; + + javelin = new Mech("javelin-ship", true){ + float minV = 3.6f; + float maxV = 6f; + TextureRegion shield; + + { + drillPower = -1; + speed = 0.11f; + drag = 0.01f; + mass = 2f; + health = 170f; + engineColor = Color.valueOf("d3ddff"); + cellTrnsY = 1f; + weapon = new Weapon("missiles"){{ + length = 1.5f; + reload = 70f; + shots = 4; + inaccuracy = 2f; + roundrobin = true; + ejectEffect = Fx.none; + velocityRnd = 0.2f; + spacing = 1f; + bullet = Bullets.missileJavelin; + }}; + } + + @Override + public void load(){ + super.load(); + shield = Core.atlas.find(name + "-shield"); + } + + @Override + public float getRotationAlpha(Player player){ + return 0.5f; + } + + @Override + public void updateAlt(Player player){ + float scl = scld(player); + if(Mathf.chance(Time.delta() * (0.15 * scl))){ + Effects.effect(Fx.hitLancer, Pal.lancerLaser, player.x, player.y); + Lightning.create(player.getTeam(), Pal.lancerLaser, 10f, + player.x + player.velocity().x, player.y + player.velocity().y, player.rotation, 14); + } + } + + @Override + public void draw(Player player){ + float scl = scld(player); + if(scl < 0.01f) return; + Draw.color(Pal.lancerLaser); + Draw.alpha(scl / 2f); + Draw.blend(Blending.additive); + Draw.rect(shield, player.x + Mathf.range(scl / 2f), player.y + Mathf.range(scl / 2f), player.rotation - 90); + Draw.blend(); + } + + float scld(Player player){ + return Mathf.clamp((player.velocity().len() - minV) / (maxV - minV)); + } + }; + + trident = new Mech("trident-ship", true){ + { + drillPower = 2; + speed = 0.15f; + drag = 0.034f; + mass = 2.5f; + turnCursor = false; + health = 250f; + itemCapacity = 30; + engineColor = Color.valueOf("84f491"); + cellTrnsY = 1f; + buildPower = 2.5f; + weapon = new Weapon("bomber"){{ + length = 0f; + width = 2f; + reload = 25f; + shots = 2; + shotDelay = 1f; + shots = 8; + roundrobin = true; + ejectEffect = Fx.none; + velocityRnd = 1f; + inaccuracy = 20f; + ignoreRotation = true; + bullet = new BombBulletType(16f, 25f, "shell"){{ + bulletWidth = 10f; + bulletHeight = 14f; + hitEffect = Fx.flakExplosion; + shootEffect = Fx.none; + smokeEffect = Fx.none; + }}; + }}; + } + + @Override + public boolean canShoot(Player player){ + return player.velocity().len() > 1.2f; + } + }; + + glaive = new Mech("glaive-ship", true){ + { + drillPower = 4; + mineSpeed = 1.3f; + speed = 0.32f; + drag = 0.06f; + mass = 3f; + health = 240f; + itemCapacity = 60; + engineColor = Color.valueOf("feb380"); + cellTrnsY = 1f; + buildPower = 1.2f; + + weapon = new Weapon("bomber"){{ + length = 1.5f; + reload = 13f; + roundrobin = true; + ejectEffect = Fx.shellEjectSmall; + bullet = Bullets.standardGlaive; + }}; + } + }; + + starter = dart; + } +} diff --git a/core/src/io/anuke/mindustry/content/StatusEffects.java b/core/src/io/anuke/mindustry/content/StatusEffects.java new file mode 100644 index 0000000000..a869b0cbe9 --- /dev/null +++ b/core/src/io/anuke/mindustry/content/StatusEffects.java @@ -0,0 +1,87 @@ +package io.anuke.mindustry.content; + +import io.anuke.arc.math.Mathf; +import io.anuke.mindustry.entities.Effects; +import io.anuke.mindustry.game.ContentList; +import io.anuke.mindustry.type.StatusEffect; + +public class StatusEffects implements ContentList{ + public static StatusEffect none, burning, freezing, wet, melting, tarred, overdrive, shielded, shocked, corroded, boss; + + @Override + public void load(){ + + none = new StatusEffect(); + + burning = new StatusEffect(){{ + damage = 0.04f; + effect = Fx.burning; + + opposite(() -> wet, () -> freezing); + trans(() -> tarred, ((unit, time, newTime, result) -> { + unit.damage(1f); + Effects.effect(Fx.burning, unit.x + Mathf.range(unit.getSize() / 2f), unit.y + Mathf.range(unit.getSize() / 2f)); + result.set(this, Math.min(time + newTime, 300f)); + })); + }}; + + freezing = new StatusEffect(){{ + speedMultiplier = 0.6f; + armorMultiplier = 0.8f; + effect = Fx.freezing; + + opposite(() -> melting, () -> burning); + }}; + + wet = new StatusEffect(){{ + speedMultiplier = 0.9f; + effect = Fx.wet; + + trans(() -> shocked, ((unit, time, newTime, result) -> unit.damage(15f))); + opposite(() -> burning, () -> shocked); + }}; + + melting = new StatusEffect(){{ + speedMultiplier = 0.8f; + armorMultiplier = 0.8f; + damage = 0.3f; + effect = Fx.melting; + + trans(() -> tarred, ((unit, time, newTime, result) -> result.set(this, Math.min(time + newTime / 2f, 140f)))); + opposite(() -> wet, () -> freezing); + }}; + + tarred = new StatusEffect(){{ + speedMultiplier = 0.6f; + effect = Fx.oily; + + trans(() -> melting, ((unit, time, newTime, result) -> result.set(burning, newTime + time))); + trans(() -> burning, ((unit, time, newTime, result) -> result.set(burning, newTime + time))); + }}; + + overdrive = new StatusEffect(){{ + armorMultiplier = 0.95f; + speedMultiplier = 1.15f; + damageMultiplier = 1.4f; + damage = -0.01f; + effect = Fx.overdriven; + }}; + + shielded = new StatusEffect(){{ + armorMultiplier = 3f; + }}; + + boss = new StatusEffect(){{ + armorMultiplier = 3f; + damageMultiplier = 3f; + speedMultiplier = 1.1f; + }}; + + shocked = new StatusEffect(); + + //no effects, just small amounts of damage. + corroded = new StatusEffect(){{ + damage = 0.1f; + }}; + } +} diff --git a/core/src/io/anuke/mindustry/content/TechTree.java b/core/src/io/anuke/mindustry/content/TechTree.java new file mode 100644 index 0000000000..8228b78eb2 --- /dev/null +++ b/core/src/io/anuke/mindustry/content/TechTree.java @@ -0,0 +1,328 @@ +package io.anuke.mindustry.content; + +import io.anuke.arc.collection.Array; +import io.anuke.mindustry.game.ContentList; +import io.anuke.mindustry.type.ItemStack; +import io.anuke.mindustry.world.Block; + +import static io.anuke.mindustry.content.Blocks.*; + +public class TechTree implements ContentList{ + public static TechNode root; + + @Override + public void load(){ + root = node(coreShard, () -> { + + node(conveyor, () -> { + + node(junction, () -> { + node(itemBridge); + node(router, () -> { + node(launchPad, () -> { + node(launchPadLarge, () -> { + + }); + }); + + node(distributor); + node(sorter, () -> { + node(overflowGate); + }); + node(container, () -> { + node(unloader); + node(vault, () -> { + + }); + }); + + node(titaniumConveyor, () -> { + node(phaseConveyor, () -> { + node(massDriver, () -> { + + }); + }); + }); + }); + }); + }); + + node(duo, () -> { + node(scatter, () -> { + node(hail, () -> { + + node(salvo, () -> { + node(swarmer, () -> { + node(cyclone, () -> { + node(spectre, () -> { + + }); + }); + }); + + node(ripple, () -> { + node(fuse, () -> { + + }); + }); + }); + }); + }); + + node(scorch, () -> { + node(arc, () -> { + node(wave, () -> { + + }); + + node(lancer, () -> { + node(meltdown, () -> { + + }); + + node(shockMine, () -> { + + }); + }); + }); + }); + + + node(copperWall, () -> { + node(copperWallLarge); + node(titaniumWall, () -> { + node(door, () -> { + node(doorLarge); + }); + node(titaniumWallLarge); + node(thoriumWall, () -> { + node(thoriumWallLarge); + node(surgeWall, () -> { + node(surgeWallLarge); + node(phaseWall, () -> { + node(phaseWallLarge); + }); + }); + }); + }); + }); + }); + + node(mechanicalDrill, () -> { + node(graphitePress, () -> { + node(pneumaticDrill, () -> { + node(cultivator, () -> { + + }); + + node(laserDrill, () -> { + node(blastDrill, () -> { + + }); + + node(waterExtractor, () -> { + node(oilExtractor, () -> { + + }); + }); + }); + }); + + node(pyratiteMixer, () -> { + node(blastMixer, () -> { + + }); + }); + + node(siliconSmelter, () -> { + + node(sporePress, () -> { + node(coalCentrifuge, () -> { + + }); + node(multiPress, () -> { + + }); + + node(plastaniumCompressor, () -> { + node(phaseWeaver, () -> { + + }); + }); + }); + + node(kiln, () -> { + node(incinerator, () -> { + node(melter, () -> { + node(surgeSmelter, () -> { + + }); + + node(separator, () -> { + node(pulverizer, () -> { + + }); + }); + + node(cryofluidMixer, () -> { + + }); + }); + }); + }); + }); + }); + + + node(mechanicalPump, () -> { + node(conduit, () -> { + node(liquidJunction, () -> { + node(liquidRouter, () -> { + node(liquidTank); + + node(pulseConduit, () -> { + node(phaseConduit, () -> { + + }); + }); + + node(rotaryPump, () -> { + node(thermalPump, () -> { + + }); + }); + }); + node(bridgeConduit); + }); + }); + }); + + node(combustionGenerator, () -> { + node(powerNode, () -> { + node(powerNodeLarge, () -> { + node(surgeTower, () -> { + + }); + }); + + node(battery, () -> { + node(batteryLarge, () -> { + + }); + }); + + node(mender, () -> { + node(mendProjector, () -> { + node(forceProjector, () -> { + node(overdriveProjector, () -> { + + }); + }); + + node(repairPoint, () -> { + + }); + }); + }); + + node(turbineGenerator, () -> { + node(thermalGenerator, () -> { + node(rtgGenerator, () -> { + node(differentialGenerator, () -> { + node(thoriumReactor, () -> { + node(impactReactor, () -> { + + }); + }); + }); + }); + }); + }); + + node(solarPanel, () -> { + node(largeSolarPanel, () -> { + + }); + }); + }); + + node(draugFactory, () -> { + node(spiritFactory, () -> { + node(phantomFactory); + }); + + node(daggerFactory, () -> { + node(crawlerFactory, () -> { + node(titanFactory, () -> { + node(fortressFactory, () -> { + + }); + }); + }); + + node(wraithFactory, () -> { + node(ghoulFactory, () -> { + node(revenantFactory, () -> { + + }); + }); + }); + }); + }); + + node(dartPad, () -> { + node(deltaPad, () -> { + + node(javelinPad, () -> { + node(tridentPad, () -> { + node(glaivePad); + }); + }); + + node(tauPad, () -> { + node(omegaPad, () -> { + + }); + }); + }); + }); + }); + }); + }); + } + + private TechNode node(Block block, Runnable children){ + ItemStack[] requirements = new ItemStack[block.buildRequirements.length]; + for(int i = 0; i < requirements.length; i++){ + requirements[i] = new ItemStack(block.buildRequirements[i].item, block.buildRequirements[i].amount * 5); + } + + return new TechNode(block, requirements, children); + } + + private TechNode node(Block block){ + return node(block, () -> {}); + } + + public static class TechNode{ + static TechNode context; + + public final Block block; + public final ItemStack[] requirements; + public final Array children = new Array<>(); + + TechNode(Block block, ItemStack[] requirements, Runnable children){ + if(context != null){ + context.children.add(this); + } + + this.block = block; + this.requirements = requirements; + + TechNode last = context; + context = this; + children.run(); + context = last; + } + } +} diff --git a/core/src/io/anuke/mindustry/content/UnitTypes.java b/core/src/io/anuke/mindustry/content/UnitTypes.java new file mode 100644 index 0000000000..44dacd6e08 --- /dev/null +++ b/core/src/io/anuke/mindustry/content/UnitTypes.java @@ -0,0 +1,347 @@ +package io.anuke.mindustry.content; + +import io.anuke.arc.collection.ObjectSet; +import io.anuke.mindustry.entities.type.base.*; +import io.anuke.mindustry.game.ContentList; +import io.anuke.mindustry.type.UnitType; +import io.anuke.mindustry.type.Weapon; + +public class UnitTypes implements ContentList{ + public static UnitType + draug, spirit, phantom, + wraith, ghoul, revenant, lich, reaper, + dagger, crawler, titan, fortress, eruptor, chaosArray, eradicator; + + @Override + public void load(){ + draug = new UnitType("draug", Draug.class, Draug::new){{ + isFlying = true; + drag = 0.01f; + speed = 0.3f; + maxVelocity = 1.2f; + range = 50f; + health = 60; + minePower = 0.5f; + engineSize = 1.8f; + engineOffset = 5.7f; + weapon = new Weapon("you have incurred my wrath. prepare to die."){{ + bullet = Bullets.lancerLaser; + }}; + }}; + + spirit = new UnitType("spirit", Spirit.class, Spirit::new){{ + isFlying = true; + drag = 0.01f; + speed = 0.4f; + maxVelocity = 1.6f; + range = 50f; + health = 60; + engineSize = 1.8f; + engineOffset = 5.7f; + weapon = new Weapon("heal-blaster"){{ + length = 1.5f; + reload = 40f; + width = 0.5f; + roundrobin = true; + ejectEffect = Fx.none; + recoil = 2f; + bullet = Bullets.healBullet; + }}; + }}; + + phantom = new UnitType("phantom", Phantom.class, Phantom::new){{ + isFlying = true; + drag = 0.01f; + mass = 2f; + speed = 0.45f; + maxVelocity = 1.9f; + range = 70f; + itemCapacity = 70; + health = 220; + buildPower = 0.9f; + minePower = 1.1f; + engineOffset = 6.5f; + toMine = ObjectSet.with(Items.lead, Items.copper, Items.titanium); + weapon = new Weapon("heal-blaster"){{ + length = 1.5f; + reload = 20f; + width = 0.5f; + roundrobin = true; + ejectEffect = Fx.none; + recoil = 2f; + bullet = Bullets.healBullet; + }}; + }}; + + dagger = new UnitType("dagger", Dagger.class, Dagger::new){{ + maxVelocity = 1.1f; + speed = 0.2f; + drag = 0.4f; + hitsize = 8f; + mass = 1.75f; + health = 130; + weapon = new Weapon("chain-blaster"){{ + length = 1.5f; + reload = 28f; + roundrobin = true; + ejectEffect = Fx.shellEjectSmall; + bullet = Bullets.standardCopper; + }}; + }}; + + crawler = new UnitType("crawler", Crawler.class, Crawler::new){{ + maxVelocity = 1.2f; + speed = 0.26f; + drag = 0.4f; + hitsize = 8f; + mass = 1.75f; + health = 100; + weapon = new Weapon("bomber"){{ + reload = 12f; + ejectEffect = Fx.none; + bullet = Bullets.explode; + }}; + }}; + + titan = new UnitType("titan", Titan.class, Titan::new){{ + maxVelocity = 0.8f; + speed = 0.18f; + drag = 0.4f; + mass = 3.5f; + hitsize = 9f; + rotatespeed = 0.1f; + health = 440; + immunities.add(StatusEffects.burning); + weapon = new Weapon("flamethrower"){{ + length = 1f; + reload = 14f; + roundrobin = true; + recoil = 1f; + ejectEffect = Fx.none; + bullet = Bullets.basicFlame; + }}; + }}; + + fortress = new UnitType("fortress", Fortress.class, Fortress::new){{ + maxVelocity = 0.78f; + speed = 0.15f; + drag = 0.4f; + mass = 5f; + hitsize = 10f; + rotatespeed = 0.06f; + targetAir = false; + health = 750; + weapon = new Weapon("artillery"){{ + length = 1f; + reload = 60f; + width = 10f; + roundrobin = true; + recoil = 4f; + shake = 2f; + ejectEffect = Fx.shellEjectMedium; + bullet = Bullets.artilleryUnit; + }}; + }}; + + eruptor = new UnitType("eruptor", Eruptor.class, Eruptor::new){{ + maxVelocity = 0.81f; + speed = 0.16f; + drag = 0.4f; + mass = 5f; + hitsize = 9f; + rotatespeed = 0.05f; + targetAir = false; + health = 600; + immunities = ObjectSet.with(StatusEffects.burning, StatusEffects.melting); + weapon = new Weapon("eruption"){{ + length = 3f; + reload = 10f; + roundrobin = true; + ejectEffect = Fx.none; + bullet = Bullets.eruptorShot; + recoil = 1f; + width = 7f; + }}; + }}; + + chaosArray = new UnitType("chaos-array", Dagger.class, Dagger::new){{ + maxVelocity = 0.68f; + speed = 0.12f; + drag = 0.4f; + mass = 5f; + hitsize = 20f; + rotatespeed = 0.06f; + health = 4000; + weapon = new Weapon("chaos"){{ + length = 8f; + reload = 50f; + width = 17f; + roundrobin = true; + recoil = 3f; + shake = 2f; + shots = 4; + spacing = 4f; + shotDelay = 5; + ejectEffect = Fx.shellEjectMedium; + bullet = Bullets.flakSurge; + }}; + }}; + + eradicator = new UnitType("eradicator", Dagger.class, Dagger::new){{ + maxVelocity = 0.68f; + speed = 0.12f; + drag = 0.4f; + mass = 5f; + hitsize = 20f; + rotatespeed = 0.06f; + health = 10000; + weapon = new Weapon("eradication"){{ + length = 13f; + reload = 30f; + width = 22f; + roundrobin = true; + recoil = 3f; + shake = 2f; + inaccuracy = 3f; + shots = 4; + spacing = 0f; + shotDelay = 3; + ejectEffect = Fx.shellEjectMedium; + bullet = Bullets.standardThoriumBig; + }}; + }}; + + wraith = new UnitType("wraith", Wraith.class, Wraith::new){{ + speed = 0.3f; + maxVelocity = 1.9f; + drag = 0.01f; + mass = 1.5f; + isFlying = true; + health = 75; + engineOffset = 5.5f; + range = 140f; + weapon = new Weapon("chain-blaster"){{ + length = 1.5f; + reload = 28f; + roundrobin = true; + ejectEffect = Fx.shellEjectSmall; + bullet = Bullets.standardCopper; + }}; + }}; + + ghoul = new UnitType("ghoul", Ghoul.class, Ghoul::new){{ + health = 220; + speed = 0.2f; + maxVelocity = 1.4f; + mass = 3f; + drag = 0.01f; + isFlying = true; + targetAir = false; + engineOffset = 7.8f; + range = 140f; + weapon = new Weapon("bomber"){{ + length = 0f; + width = 2f; + reload = 12f; + roundrobin = true; + ejectEffect = Fx.none; + velocityRnd = 1f; + inaccuracy = 40f; + ignoreRotation = true; + bullet = Bullets.bombExplosive; + }}; + }}; + + revenant = new UnitType("revenant", Revenant.class, Revenant::new){{ + health = 1000; + mass = 5f; + hitsize = 20f; + speed = 0.1f; + maxVelocity = 1f; + drag = 0.01f; + range = 80f; + shootCone = 40f; + isFlying = true; + rotateWeapon = true; + engineOffset = 12f; + engineSize = 3f; + rotatespeed = 0.01f; + attackLength = 90f; + baseRotateSpeed = 0.06f; + weapon = new Weapon("revenant-missiles"){{ + length = 3f; + reload = 70f; + width = 10f; + shots = 2; + inaccuracy = 2f; + roundrobin = true; + ejectEffect = Fx.none; + velocityRnd = 0.2f; + spacing = 1f; + bullet = Bullets.missileRevenant; + }}; + }}; + + lich = new UnitType("lich", Revenant.class, Revenant::new){{ + health = 7000; + mass = 20f; + hitsize = 40f; + speed = 0.01f; + maxVelocity = 0.6f; + drag = 0.02f; + range = 80f; + shootCone = 20f; + isFlying = true; + rotateWeapon = true; + engineOffset = 21; + engineSize = 5.3f; + rotatespeed = 0.01f; + attackLength = 90f; + baseRotateSpeed = 0.04f; + weapon = new Weapon("lich-missiles"){{ + length = 4f; + reload = 180f; + width = 22f; + shots = 22; + shootCone = 100f; + shotDelay = 2; + inaccuracy = 10f; + roundrobin = true; + ejectEffect = Fx.none; + velocityRnd = 0.2f; + spacing = 1f; + bullet = Bullets.missileRevenant; + }}; + }}; + + reaper = new UnitType("reaper", Revenant.class, Revenant::new){{ + health = 13000; + mass = 30f; + hitsize = 56f; + speed = 0.01f; + maxVelocity = 0.6f; + drag = 0.02f; + range = 80f; + shootCone = 30f; + isFlying = true; + rotateWeapon = true; + engineOffset = 40; + engineSize = 7.3f; + rotatespeed = 0.01f; + baseRotateSpeed = 0.04f; + weapon = new Weapon("reaper-gun"){{ + length = 3f; + reload = 10f; + width = 32f; + shots = 1; + shootCone = 100f; + + shake = 1f; + inaccuracy = 3f; + roundrobin = true; + ejectEffect = Fx.none; + bullet = Bullets.standardDenseBig; + }}; + }}; + } +} diff --git a/core/src/io/anuke/mindustry/content/Zones.java b/core/src/io/anuke/mindustry/content/Zones.java new file mode 100644 index 0000000000..70b7835da7 --- /dev/null +++ b/core/src/io/anuke/mindustry/content/Zones.java @@ -0,0 +1,177 @@ +package io.anuke.mindustry.content; + +import io.anuke.arc.collection.Array; +import io.anuke.mindustry.game.ContentList; +import io.anuke.mindustry.game.SpawnGroup; +import io.anuke.mindustry.maps.generators.MapGenerator; +import io.anuke.mindustry.maps.generators.MapGenerator.Decoration; +import io.anuke.mindustry.maps.zonegen.DesertWastesGenerator; +import io.anuke.mindustry.type.*; +import io.anuke.mindustry.world.Block; + +public class Zones implements ContentList{ + public static Zone + groundZero, desertWastes, + craters, frozenForest, ruinousShores, stainedMountains, tarFields, + saltFlats, overgrowth, infestedIslands, + desolateRift, nuclearComplex; + + @Override + public void load(){ + + groundZero = new Zone("groundZero", new MapGenerator("groundZero", 1)){{ + baseLaunchCost = ItemStack.with(Items.copper, -100); + startingItems = ItemStack.list(Items.copper, 100); + alwaysUnlocked = true; + conditionWave = 5; + launchPeriod = 5; + resources = new Item[]{Items.copper, Items.scrap, Items.lead, Items.sand}; + }}; + + desertWastes = new Zone("desertWastes", new DesertWastesGenerator(260, 260)){{ + startingItems = ItemStack.list(Items.copper, 200); + conditionWave = 20; + launchPeriod = 10; + loadout = Loadouts.advancedShard; + zoneRequirements = ZoneRequirement.with(groundZero, 20); + resources = new Item[]{Items.copper, Items.lead, Items.coal, Items.sand}; + rules = r -> { + r.waves = true; + r.waveTimer = true; + r.launchWaveMultiplier = 3f; + r.waveSpacing = 60 * 50f; + r.spawns = Array.with( + new SpawnGroup(UnitTypes.crawler){{ + unitScaling = 3f; + }}, + new SpawnGroup(UnitTypes.dagger){{ + unitScaling = 4f; + begin = 2; + spacing = 2; + }}, + new SpawnGroup(UnitTypes.wraith){{ + unitScaling = 3f; + begin = 11; + spacing = 3; + }}, + new SpawnGroup(UnitTypes.eruptor){{ + unitScaling = 3f; + begin = 22; + unitAmount = 1; + spacing = 4; + }}, + new SpawnGroup(UnitTypes.titan){{ + unitScaling = 3f; + begin = 37; + unitAmount = 2; + spacing = 4; + }}, + new SpawnGroup(UnitTypes.fortress){{ + unitScaling = 2f; + effect = StatusEffects.boss; + begin = 41; + spacing = 20; + }} + ); + }; + }}; + + saltFlats = new Zone("saltFlats", new MapGenerator("saltFlats")){{ + baseLaunchCost = ItemStack.with(Items.copper, -100); + startingItems = ItemStack.list(Items.copper, 100); + alwaysUnlocked = true; + conditionWave = 5; + launchPeriod = 5; + loadout = Loadouts.basicFoundation; + zoneRequirements = ZoneRequirement.with(desertWastes, 60); + blockRequirements = new Block[]{Blocks.daggerFactory, Blocks.draugFactory}; + resources = new Item[]{Items.copper, Items.scrap, Items.lead, Items.coal, Items.sand}; + }}; + + craters = new Zone("craters", new MapGenerator("craters", 1).dist(0).decor(new Decoration(Blocks.snow, Blocks.sporeCluster, 0.004))){{ + startingItems = ItemStack.list(Items.copper, 200); + conditionWave = 10; + zoneRequirements = ZoneRequirement.with(groundZero, 10); + blockRequirements = new Block[]{Blocks.router}; + resources = new Item[]{Items.copper, Items.lead, Items.coal, Items.sand, Items.scrap}; + }}; + + frozenForest = new Zone("frozenForest", new MapGenerator("frozenForest", 1) + .decor(new Decoration(Blocks.snow, Blocks.sporeCluster, 0.02))){{ + loadout = Loadouts.basicFoundation; + baseLaunchCost = ItemStack.with(); + startingItems = ItemStack.list(Items.copper, 400); + conditionWave = 10; + zoneRequirements = ZoneRequirement.with(craters, 10); + resources = new Item[]{Items.copper, Items.lead, Items.coal}; + }}; + + overgrowth = new Zone("overgrowth", new MapGenerator("overgrowth")){{ + startingItems = ItemStack.list(Items.copper, 3000, Items.lead, 2000, Items.silicon, 1000, Items.metaglass, 500); + conditionWave = 12; + launchPeriod = 4; + loadout = Loadouts.basicNucleus; + zoneRequirements = ZoneRequirement.with(frozenForest, 40); + blockRequirements = new Block[]{Blocks.router}; + resources = new Item[]{Items.copper, Items.lead, Items.coal, Items.titanium, Items.sand, Items.thorium, Items.scrap}; + }}; + + ruinousShores = new Zone("ruinousShores", new MapGenerator("ruinousShores", 1).dist(3f, true)){{ + loadout = Loadouts.basicFoundation; + baseLaunchCost = ItemStack.with(); + startingItems = ItemStack.list(Items.copper, 400); + conditionWave = 20; + launchPeriod = 20; + zoneRequirements = ZoneRequirement.with(desertWastes, 20, craters, 15); + blockRequirements = new Block[]{Blocks.graphitePress, Blocks.combustionGenerator}; + resources = new Item[]{Items.copper, Items.scrap, Items.lead, Items.coal, Items.sand}; + }}; + + stainedMountains = new Zone("stainedMountains", new MapGenerator("stainedMountains", 2) + .dist(0f, false) + .decor(new Decoration(Blocks.shale, Blocks.shaleBoulder, 0.02))){{ + loadout = Loadouts.basicFoundation; + startingItems = ItemStack.list(Items.copper, 400, Items.lead, 100); + conditionWave = 10; + launchPeriod = 10; + zoneRequirements = ZoneRequirement.with(frozenForest, 15); + blockRequirements = new Block[]{Blocks.pneumaticDrill}; + resources = new Item[]{Items.copper, Items.scrap, Items.lead, Items.coal, Items.titanium, Items.sand}; + }}; + + tarFields = new Zone("tarFields", new MapGenerator("tarFields") + .dist(0f, false) + .decor(new Decoration(Blocks.shale, Blocks.shaleBoulder, 0.02))){{ + loadout = Loadouts.basicFoundation; + startingItems = ItemStack.list(Items.copper, 500, Items.lead, 200); + conditionWave = 15; + launchPeriod = 10; + zoneRequirements = ZoneRequirement.with(ruinousShores, 20); + blockRequirements = new Block[]{Blocks.coalCentrifuge}; + resources = new Item[]{Items.copper, Items.scrap, Items.lead, Items.coal, Items.titanium, Items.sand}; + }}; + + desolateRift = new Zone("desolateRift", new MapGenerator("desolateRift").dist(2f)){{ + loadout = Loadouts.basicNucleus; + baseLaunchCost = ItemStack.with(); + startingItems = ItemStack.list(Items.copper, 2000, Items.lead, 2000, Items.graphite, 500, Items.titanium, 500, Items.silicon, 500); + conditionWave = 3; + launchPeriod = 2; + zoneRequirements = ZoneRequirement.with(tarFields, 20); + blockRequirements = new Block[]{Blocks.thermalGenerator}; + resources = new Item[]{Items.copper, Items.scrap, Items.lead, Items.coal, Items.titanium, Items.sand, Items.thorium}; + }}; + + nuclearComplex = new Zone("nuclearComplex", new MapGenerator("nuclearProductionComplex", 1) + .decor(new Decoration(Blocks.snow, Blocks.sporeCluster, 0.01))){{ + loadout = Loadouts.basicNucleus; + baseLaunchCost = ItemStack.with(); + startingItems = ItemStack.list(Items.copper, 2500, Items.lead, 3000, Items.silicon, 800, Items.metaglass, 400); + conditionWave = 30; + launchPeriod = 15; + zoneRequirements = ZoneRequirement.with(stainedMountains, 20); + blockRequirements = new Block[]{Blocks.thermalGenerator}; + resources = new Item[]{Items.copper, Items.scrap, Items.lead, Items.coal, Items.titanium, Items.thorium, Items.sand}; + }}; + } +} diff --git a/core/src/io/anuke/mindustry/core/ContentLoader.java b/core/src/io/anuke/mindustry/core/ContentLoader.java new file mode 100644 index 0000000000..1bf12ba4a3 --- /dev/null +++ b/core/src/io/anuke/mindustry/core/ContentLoader.java @@ -0,0 +1,245 @@ +package io.anuke.mindustry.core; + +import io.anuke.arc.collection.*; +import io.anuke.arc.function.Consumer; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.graphics.Pixmap; +import io.anuke.arc.util.Log; +import io.anuke.mindustry.content.*; +import io.anuke.mindustry.entities.bullet.BulletType; +import io.anuke.mindustry.entities.effect.Fire; +import io.anuke.mindustry.entities.effect.Puddle; +import io.anuke.mindustry.entities.traits.TypeTrait; +import io.anuke.mindustry.entities.type.Player; +import io.anuke.mindustry.game.*; +import io.anuke.mindustry.type.*; +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.LegacyColorMapper; + +import static io.anuke.arc.Core.files; + +/** + * Loads all game content. + * Call load() before doing anything with content. + */ +@SuppressWarnings("unchecked") +public class ContentLoader{ + private boolean loaded = false; + private boolean verbose = false; + + private ObjectMap[] contentNameMap = new ObjectMap[ContentType.values().length]; + private Array[] contentMap = new Array[ContentType.values().length]; + private MappableContent[][] temporaryMapper; + private ObjectSet> initialization = new ObjectSet<>(); + private ContentList[] content = { + new Fx(), + new Items(), + new StatusEffects(), + new Liquids(), + new Bullets(), + new Mechs(), + new UnitTypes(), + new Blocks(), + new Loadouts(), + new TechTree(), + new Zones(), + + //these are not really content classes, but this makes initialization easier + new LegacyColorMapper(), + }; + + public void setVerbose(){ + verbose = true; + } + + /** Creates all content types. */ + public void load(){ + if(loaded){ + Log.info("Content already loaded, skipping."); + return; + } + + registerTypes(); + + for(ContentType type : ContentType.values()){ + contentMap[type.ordinal()] = new Array<>(); + contentNameMap[type.ordinal()] = new ObjectMap<>(); + } + + for(ContentList list : content){ + list.load(); + } + + int total = 0; + + for(ContentType type : ContentType.values()){ + + for(Content c : contentMap[type.ordinal()]){ + if(c instanceof MappableContent){ + String name = ((MappableContent)c).name; + if(contentNameMap[type.ordinal()].containsKey(name)){ + throw new IllegalArgumentException("Two content objects cannot have the same name! (issue: '" + name + "')"); + } + contentNameMap[type.ordinal()].put(name, (MappableContent)c); + } + total++; + } + } + + //set up ID mapping + for(Array arr : contentMap){ + for(int i = 0; i < arr.size; i++){ + int id = arr.get(i).id; + if(id != i){ + throw new IllegalArgumentException("Out-of-order IDs for content '" + arr.get(i) + "' (expected " + i + " but got " + id + ")"); + } + } + } + + if(verbose){ + Log.info("--- CONTENT INFO ---"); + for(int k = 0; k < contentMap.length; k++){ + Log.info("[{0}]: loaded {1}", ContentType.values()[k].name(), contentMap[k].size); + } + Log.info("Total content loaded: {0}", total); + Log.info("-------------------"); + } + + loaded = true; + } + + public void initialize(Consumer callable){ + initialize(callable, false); + } + + /** Initializes all content with the specified function. */ + public void initialize(Consumer callable, boolean override){ + if(initialization.contains(callable) && !override) return; + + for(ContentType type : ContentType.values()){ + for(Content content : contentMap[type.ordinal()]){ + callable.accept(content); + } + } + + initialization.add(callable); + } + + /** Loads block colors. */ + public void loadColors(){ + Pixmap pixmap = new Pixmap(files.internal("sprites/block_colors.png")); + for(int i = 0; i < pixmap.getWidth(); i++){ + if(blocks().size > i){ + int color = pixmap.getPixel(i, 0); + + if(color == 0) continue; + + Block block = block(i); + Color.rgba8888ToColor(block.color, color); + } + } + pixmap.dispose(); + } + + public void verbose(boolean verbose){ + this.verbose = verbose; + } + + public void dispose(){ + //clear all content, currently not needed + } + + public void handleContent(Content content){ + contentMap[content.getContentType().ordinal()].add(content); + } + + public void setTemporaryMapper(MappableContent[][] temporaryMapper){ + this.temporaryMapper = temporaryMapper; + } + + public Array[] getContentMap(){ + return contentMap; + } + + public T getByName(ContentType type, String name){ + if(contentNameMap[type.ordinal()] == null){ + return null; + } + return (T)contentNameMap[type.ordinal()].get(name); + } + + public T getByID(ContentType type, int id){ + + if(temporaryMapper != null && temporaryMapper[type.ordinal()] != null && temporaryMapper[type.ordinal()].length != 0){ + //-1 = invalid content + if(id < 0){ + return null; + } + if(temporaryMapper[type.ordinal()].length <= id || temporaryMapper[type.ordinal()][id] == null){ + return getByID(type, 0); //default value is always ID 0 + } + return (T)temporaryMapper[type.ordinal()][id]; + } + + if(id >= contentMap[type.ordinal()].size || id < 0){ + return null; + } + return (T)contentMap[type.ordinal()].get(id); + } + + public Array getBy(ContentType type){ + return (Array)contentMap[type.ordinal()]; + } + + //utility methods, just makes things a bit shorter + + public Array blocks(){ + return getBy(ContentType.block); + } + + public Block block(int id){ + return (Block)getByID(ContentType.block, id); + } + + public Array items(){ + return getBy(ContentType.item); + } + + public Item item(int id){ + return (Item)getByID(ContentType.item, id); + } + + public Array liquids(){ + return getBy(ContentType.liquid); + } + + public Liquid liquid(int id){ + return (Liquid)getByID(ContentType.liquid, id); + } + + public Array bullets(){ + return getBy(ContentType.bullet); + } + + public BulletType bullet(int id){ + return (BulletType)getByID(ContentType.bullet, id); + } + + public Array zones(){ + return getBy(ContentType.zone); + } + + public Array units(){ + return getBy(ContentType.unit); + } + + /** + * Registers sync IDs for all types of sync entities. + * Do not register units here! + */ + private void registerTypes(){ + TypeTrait.registerType(Player.class, Player::new); + TypeTrait.registerType(Fire.class, Fire::new); + TypeTrait.registerType(Puddle.class, Puddle::new); + } +} diff --git a/core/src/io/anuke/mindustry/core/Control.java b/core/src/io/anuke/mindustry/core/Control.java index c319cc7e0e..3b87d16091 100644 --- a/core/src/io/anuke/mindustry/core/Control.java +++ b/core/src/io/anuke/mindustry/core/Control.java @@ -1,410 +1,329 @@ package io.anuke.mindustry.core; -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.Input.Buttons; -import com.badlogic.gdx.graphics.Color; +import io.anuke.arc.*; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.graphics.GL20; +import io.anuke.arc.graphics.g2d.Draw; +import io.anuke.arc.graphics.g2d.TextureAtlas; +import io.anuke.arc.input.KeyCode; +import io.anuke.arc.scene.ui.Dialog; +import io.anuke.arc.scene.ui.TextField; +import io.anuke.arc.util.*; import io.anuke.mindustry.core.GameState.State; -import io.anuke.mindustry.entities.Player; +import io.anuke.mindustry.entities.Effects; +import io.anuke.mindustry.entities.type.Player; +import io.anuke.mindustry.game.*; import io.anuke.mindustry.game.EventType.*; -import io.anuke.mindustry.game.Tutorial; -import io.anuke.mindustry.game.UpgradeInventory; -import io.anuke.mindustry.graphics.Fx; -import io.anuke.mindustry.input.AndroidInput; -import io.anuke.mindustry.input.DefaultKeybinds; -import io.anuke.mindustry.input.DesktopInput; -import io.anuke.mindustry.input.InputHandler; -import io.anuke.mindustry.io.Saves; +import io.anuke.mindustry.gen.Call; +import io.anuke.mindustry.input.*; +import io.anuke.mindustry.maps.Map; import io.anuke.mindustry.net.Net; -import io.anuke.mindustry.resource.Item; -import io.anuke.mindustry.resource.Weapon; -import io.anuke.mindustry.world.Map; -import io.anuke.ucore.UCore; -import io.anuke.ucore.core.*; -import io.anuke.ucore.core.Inputs.DeviceType; -import io.anuke.ucore.entities.Entities; -import io.anuke.ucore.modules.Module; -import io.anuke.ucore.scene.ui.layout.Unit; -import io.anuke.ucore.util.Atlas; -import io.anuke.ucore.util.InputProxy; -import io.anuke.ucore.util.Mathf; +import io.anuke.mindustry.type.*; +import io.anuke.mindustry.ui.dialogs.FloatingDialog; +import io.anuke.mindustry.world.Tile; +import java.io.IOException; +import java.nio.IntBuffer; + +import static io.anuke.arc.Core.scene; import static io.anuke.mindustry.Vars.*; -/**Control module. +/** + * Control module. * Handles all input, saving, keybinds and keybinds. - * Should not handle any game-critical state. - * This class is not created in the headless server.*/ -public class Control extends Module{ - private UpgradeInventory upgrades = new UpgradeInventory(); - private Tutorial tutorial = new Tutorial(); - private boolean hiscore = false; + * Should not handle any logic-critical state. + * This class is not created in the headless server. + */ +public class Control implements ApplicationListener{ + public final Saves saves; - private boolean wasPaused = false; + private Interval timer = new Interval(2); + private boolean hiscore = false; + private boolean wasPaused = false; + private InputHandler input; - private Saves saves; + public Control(){ + IntBuffer buf = BufferUtils.newIntBuffer(1); + Core.gl.glGetIntegerv(GL20.GL_MAX_TEXTURE_SIZE, buf); + int maxSize = buf.get(0); - private float respawntime; - private InputHandler input; + saves = new Saves(); + data = new GlobalData(); - private InputProxy proxy; - private float controlx, controly; - private boolean controlling; - private Throwable error; + Core.input.setCatch(KeyCode.BACK, true); - public Control(){ - saves = new Saves(); + Effects.setShakeFalloff(10000f); - Inputs.useControllers(!gwt); + content.initialize(Content::init); + Core.atlas = new TextureAtlas(maxSize < 2048 ? "sprites/sprites_fallback.atlas" : "sprites/sprites.atlas"); + Draw.scl = 1f / Core.atlas.find("scale_marker").getWidth(); + content.initialize(Content::load, true); - Gdx.input.setCatchBackKey(true); + data.load(); - if(mobile){ - input = new AndroidInput(); - }else{ - input = new DesktopInput(); - } + Core.settings.setAppName(appName); + Core.settings.defaults( + "ip", "localhost", + "color-0", Color.rgba8888(playerColors[8]), + "color-1", Color.rgba8888(playerColors[11]), + "color-2", Color.rgba8888(playerColors[13]), + "color-3", Color.rgba8888(playerColors[9]), + "name", "", + "lastBuild", 0 + ); - proxy = new InputProxy(Gdx.input){ - @Override - public int getY() { - return controlling ? (int)controly : input.getY(); + createPlayer(); + + saves.load(); + + Events.on(StateChangeEvent.class, event -> { + if((event.from == State.playing && event.to == State.menu) || (event.from == State.menu && event.to != State.menu)){ + Time.runTask(5f, Platform.instance::updateRPC); } + }); - @Override - public int getX() { - return controlling ? (int)controlx : input.getX(); - } + Events.on(PlayEvent.class, event -> { + player.setTeam(defaultTeam); + player.setDead(true); + player.add(); - @Override - public int getY(int pointer) { - return pointer == 0 ? getY() : super.getY(pointer); - } - - @Override - public int getX(int pointer) { - return pointer == 0 ? getX() : super.getX(pointer); - } - }; - - Inputs.addProcessor(input); - - Effects.setShakeFalloff(10000f); - - Core.atlas = new Atlas("sprites.atlas"); - - for(Item item : Item.getAllItems()){ - item.init(); - } - - Sounds.load("shoot.mp3", "place.mp3", "explosion.mp3", "enemyshoot.mp3", - "corexplode.mp3", "break.mp3", "spawn.mp3", "flame.mp3", "die.mp3", - "respawn.mp3", "purchase.mp3", "flame2.mp3", "bigshot.mp3", "laser.mp3", "lasershot.mp3", - "ping.mp3", "tesla.mp3", "waveend.mp3", "railgun.mp3", "blast.mp3", "bang2.mp3"); - - Sounds.setFalloff(9000f); - - Musics.load("1.mp3", "2.mp3", "3.mp3", "4.mp3", "5.mp3", "6.mp3"); - - DefaultKeybinds.load(); - - for(int i = 0; i < saveSlots; i ++){ - Settings.defaults("save-" + i + "-autosave", !gwt); - Settings.defaults("save-" + i + "-name", "untitled"); - Settings.defaults("save-" + i + "-data", "empty"); - } - - Settings.defaultList( - "ip", "localhost", - "port", port+"", - "name", mobile || gwt ? "player" : UCore.getProperty("user.name"), - "servers", "", - "color", Color.rgba8888(playerColors[8]), - "lastVersion", "3.2", - "lastBuild", 0 - ); - - KeyBinds.load(); - - for(Map map : world.maps().list()){ - Settings.defaults("hiscore" + map.name, 0); - } - - player = new Player(); - player.name = Settings.getString("name"); - player.isAndroid = mobile; - player.color.set(Settings.getInt("color")); - player.isLocal = true; - - saves.load(); - - Events.on(StateChangeEvent.class, (from, to) -> { - if((from == State.playing && to == State.menu) || (from == State.menu && to != State.menu)){ - Timers.runTask(5f, Platform.instance::updateRPC); - } - }); - - Events.on(PlayEvent.class, () -> { - renderer.clearTiles(); - - player.set(world.getSpawnX(), world.getSpawnY()); - - Core.camera.position.set(player.x, player.y, 0); - - ui.hudfrag.updateItems(); - - state.set(State.playing); - }); - - Events.on(ResetEvent.class, () -> { - upgrades.reset(); - player.weaponLeft = player.weaponRight = Weapon.blaster; - - player.add(); - player.heal(); - - respawntime = -1; - hiscore = false; - - ui.hudfrag.updateItems(); - ui.hudfrag.updateWeapons(); - ui.hudfrag.fadeRespawn(false); - }); - - Events.on(WaveEvent.class, () -> { - Sounds.play("spawn"); - - int last = Settings.getInt("hiscore" + world.getMap().name, 0); - - if(state.wave > last && !state.mode.infiniteResources && !state.mode.disableWaveTimer){ - Settings.putInt("hiscore" + world.getMap().name, state.wave); - Settings.save(); - hiscore = true; - } - - Platform.instance.updateRPC(); - }); - - Events.on(GameOverEvent.class, () -> { - Effects.shake(5, 6, Core.camera.position.x, Core.camera.position.y); - Sounds.play("corexplode"); - for(int i = 0; i < 16; i ++){ - Timers.run(i*2, ()-> Effects.effect(Fx.explosion, world.getCore().worldx()+Mathf.range(40), world.getCore().worldy()+Mathf.range(40))); - } - Effects.effect(Fx.coreexplosion, world.getCore().worldx(), world.getCore().worldy()); - - ui.restart.show(); - - Timers.runTask(30f, () -> state.set(State.menu)); - }); - } - - //FIXME figure out what's causing this problem in the first place - public void triggerInputUpdate(){ - Gdx.input = proxy; - } - - public void setError(Throwable error){ - this.error = error; - } - - public UpgradeInventory upgrades() { - return upgrades; - } - - public Saves getSaves(){ - return saves; - } - - public boolean showCursor(){ - return controlling; - } - - public InputHandler input(){ - return input; - } - - public void playMap(Map map){ - ui.loadfrag.show(); - saves.resetSave(); - - Timers.runTask(10, () -> { - logic.reset(); - world.loadMap(map); - logic.play(); - }); - - Timers.runTask(18, () -> ui.loadfrag.hide()); - } - - public boolean isHighScore(){ - return hiscore; - } - - public float getRespawnTime(){ - return respawntime; - } - - public void setRespawnTime(float respawntime){ - this.respawntime = respawntime; - } - - public Tutorial tutorial(){ - return tutorial; - } - - private void checkOldUser(){ - boolean hasPlayed = false; - - for(Map map : world.maps().getAllMaps()){ - if(Settings.getInt("hiscore" + map.name) != 0){ - hasPlayed = true; - break; - } - } - - if(hasPlayed && Settings.getString("lastVersion").equals("3.2")){ - Timers.runTask(1f, () -> ui.showInfo("$text.changes")); - Settings.putString("lastVersion", "3.3"); - Settings.save(); - } - } - - @Override - public void dispose(){ - Platform.instance.onGameExit(); - Net.dispose(); - } - - @Override - public void pause(){ - wasPaused = state.is(State.paused); - if(state.is(State.playing)) state.set(State.paused); - } - - @Override - public void resume(){ - if(state.is(State.paused) && !wasPaused){ state.set(State.playing); - } - } + }); - @Override - public void init(){ - Timers.run(1f, Musics::shuffleAll); + Events.on(WorldLoadEvent.class, event -> { + Core.app.post(() -> Core.app.post(() -> { + if(Net.active() && player.getClosestCore() != null){ + //set to closest core since that's where the player will probably respawn; prevents camera jumps + Core.camera.position.set(player.getClosestCore()); + }else{ + //locally, set to player position since respawning occurs immediately + Core.camera.position.set(player); + } + })); + }); - Entities.initPhysics(); - Entities.collisions().setCollider(tilesize, world::solid); + Events.on(ResetEvent.class, event -> { + player.reset(); - Platform.instance.updateRPC(); + hiscore = false; - checkOldUser(); - } + saves.resetSave(); + }); - @Override - public void update(){ - - if(error != null){ - throw new RuntimeException(error); - } - - Gdx.input = proxy; - - if(Inputs.keyTap("console")){ - console = !console; - } - - if(KeyBinds.getSection("default").device.type == DeviceType.controller){ - if(Inputs.keyTap("select")){ - Inputs.getProcessor().touchDown(Gdx.input.getX(), Gdx.input.getY(), 0, Buttons.LEFT); + Events.on(WaveEvent.class, event -> { + if(world.getMap().getHightScore() < state.wave){ + hiscore = true; + world.getMap().setHighScore(state.wave); } + }); - if(Inputs.keyRelease("select")){ - Inputs.getProcessor().touchUp(Gdx.input.getX(), Gdx.input.getY(), 0, Buttons.LEFT); + Events.on(GameOverEvent.class, event -> { + state.stats.wavesLasted = state.wave; + Effects.shake(5, 6, Core.camera.position.x, Core.camera.position.y); + //the restart dialog can show info for any number of scenarios + Call.onGameOver(event.winner); + if(state.rules.zone != null){ + //remove zone save on game over + if(saves.getZoneSlot() != null){ + saves.getZoneSlot().delete(); + } } + }); - float xa = Inputs.getAxis("cursor_x"); - float ya = Inputs.getAxis("cursor_y"); - - if(Math.abs(xa) > controllerMin || Math.abs(ya) > controllerMin) { - float scl = Settings.getInt("sensitivity")/100f * Unit.dp.scl(1f); - controlx += xa*baseControllerSpeed*scl; - controly -= ya*baseControllerSpeed*scl; - controlling = true; - - Gdx.input.setCursorCatched(true); - - Inputs.getProcessor().touchDragged(Gdx.input.getX(), Gdx.input.getY(), 0); + //autohost for pvp maps + Events.on(WorldLoadEvent.class, event -> { + if(state.rules.pvp && !Net.active()){ + try{ + Net.host(port); + player.isAdmin = true; + }catch(IOException e){ + ui.showError(Core.bundle.format("server.error", Strings.parseException(e, true))); + Core.app.post(() -> state.set(State.menu)); + } } + }); - controlx = Mathf.clamp(controlx, 0, Gdx.graphics.getWidth()); - controly = Mathf.clamp(controly, 0, Gdx.graphics.getHeight()); + Events.on(UnlockEvent.class, e -> ui.hudfrag.showUnlock(e.content)); - if(Gdx.input.getDeltaX() > 1 || Gdx.input.getDeltaY() > 1) { - controlling = false; - Gdx.input.setCursorCatched(false); - } + Events.on(BlockBuildEndEvent.class, e -> { + if(e.team == player.getTeam()){ + if(e.breaking){ + state.stats.buildingsDeconstructed++; + }else{ + state.stats.buildingsBuilt++; + } + } + }); + + Events.on(BlockDestroyEvent.class, e -> { + if(e.tile.getTeam() == player.getTeam()){ + state.stats.buildingsDestroyed++; + } + }); + + Events.on(UnitDestroyEvent.class, e -> { + if(e.unit.getTeam() != player.getTeam()){ + state.stats.enemyUnitsDestroyed++; + } + }); + + Events.on(ZoneRequireCompleteEvent.class, e -> { + ui.hudfrag.showToast(Core.bundle.format("zone.requirement.complete", state.wave, e.zone.localizedName)); + }); + + Events.on(ZoneConfigureCompleteEvent.class, e -> { + ui.hudfrag.showToast(Core.bundle.format("zone.config.complete", e.zone.configureWave)); + }); + } + + void createPlayer(){ + player = new Player(); + player.name = Core.settings.getString("name"); + player.color.set(Core.settings.getInt("color-0")); + player.isLocal = true; + player.isMobile = mobile; + + if(mobile){ + input = new MobileInput(); }else{ - controlling = false; - Gdx.input.setCursorCatched(false); + input = new DesktopInput(); } - if(!controlling){ - controlx = Gdx.input.getX(); - controly = Gdx.input.getY(); + if(!state.is(State.menu)){ + player.add(); } + Core.input.addProcessor(input); + } + + public InputHandler input(){ + return input; + } + + public void playMap(Map map, Rules rules){ + ui.loadAnd(() -> { + logic.reset(); + world.loadMap(map); + state.rules = rules; + logic.play(); + }); + } + + public void playZone(Zone zone){ + ui.loadAnd(() -> { + logic.reset(); + world.loadGenerator(zone.generator); + zone.rules.accept(state.rules); + state.rules.zone = zone; + for(Tile core : state.teams.get(defaultTeam).cores){ + for(ItemStack stack : zone.getStartingItems()){ + core.entity.items.add(stack.item, stack.amount); + } + } + state.set(State.playing); + control.saves.zoneSave(); + logic.play(); + }); + } + + public boolean isHighScore(){ + return hiscore; + } + + @Override + public void dispose(){ + content.dispose(); + Net.dispose(); + ui.editor.dispose(); + } + + @Override + public void pause(){ + wasPaused = state.is(State.paused); + if(state.is(State.playing)) state.set(State.paused); + } + + @Override + public void resume(){ + if(state.is(State.paused) && !wasPaused){ + state.set(State.playing); + } + } + + @Override + public void init(){ + Platform.instance.updateRPC(); + + if(!Core.settings.getBool("4.0-warning-2", false)){ + + Time.run(5f, () -> { + FloatingDialog dialog = new FloatingDialog("VERY IMPORTANT"); + dialog.buttons.addButton("$ok", () -> { + dialog.hide(); + Core.settings.put("4.0-warning-2", true); + Core.settings.save(); + }).size(100f, 60f); + dialog.cont.add("Reminder: The alpha version you are about to play is very unstable, and is [accent]not representative of the final v4 release.[]\n\n " + + "\nThere is currently[scarlet] no sound implemented[]; this is intentional.\n" + + "All current art and UI is unfinished, and will be changed before release. " + + "\n\n[accent]Saves may be corrupted without warning between updates.").wrap().width(400f); + dialog.show(); + }); + } + } + + @Override + public void update(){ saves.update(); - if(state.inventory.isUpdated() && (Timers.get("updateItems", 8) || state.is(State.paused))){ - ui.hudfrag.updateItems(); - state.inventory.setUpdated(false); - } + input.updateController(); - if(!state.is(State.menu)){ - input.update(); + //autosave global data if it's modified + data.checkSave(); - if(Inputs.keyTap("pause") && !ui.restart.isShown() && (state.is(State.paused) || state.is(State.playing))){ + if(!state.is(State.menu)){ + input.update(); + + if(world.isZone()){ + for(Tile tile : state.teams.get(player.getTeam()).cores){ + for(Item item : content.items()){ + if(tile.entity.items.has(item)){ + data.unlockContent(item); + } + } + } + } + + //auto-update rpc every 5 seconds + if(timer.get(0, 60 * 5)){ + Platform.instance.updateRPC(); + } + + if(Core.input.keyTap(Binding.pause) && !ui.restart.isShown() && (state.is(State.paused) || state.is(State.playing))){ state.set(state.is(State.playing) ? State.paused : State.playing); - } + } - if(Inputs.keyTap("menu")){ - if(state.is(State.paused)){ - ui.paused.hide(); - state.set(State.playing); - }else if (!ui.restart.isShown()){ - if(ui.chatfrag.chatOpen()) { - ui.chatfrag.hide(); - }else{ - ui.paused.show(); - state.set(State.paused); - } - } - } + if(Core.input.keyTap(Binding.menu) && !ui.restart.isShown()){ + if(ui.chatfrag.chatOpen()){ + ui.chatfrag.hide(); + }else if(!ui.paused.isShown() && !scene.hasDialog()){ + ui.paused.show(); + state.set(State.paused); + } + } - if(!state.is(State.paused) || Net.active()){ - Entities.update(effectGroup); + if(!mobile && Core.input.keyTap(Binding.screenshot) && !(scene.getKeyboardFocus() instanceof TextField) && !ui.chatfrag.chatOpen()){ + renderer.takeMapScreenshot(); + } - if(respawntime > 0){ + }else{ + if(!state.isPaused()){ + Time.update(); + } - respawntime -= Timers.delta(); - - if(respawntime <= 0){ - player.set(world.getSpawnX(), world.getSpawnY()); - player.heal(); - player.add(); - Effects.sound("respawn"); - ui.hudfrag.fadeRespawn(false); - } - } - - if(tutorial.active()){ - tutorial.update(); - } - } - }else{ - if(!state.is(State.paused) || Net.active()){ - Timers.update(); - } - } - } + if(!scene.hasDialog() && !(scene.root.getChildren().peek() instanceof Dialog) && Core.input.keyTap(KeyCode.BACK)){ + Platform.instance.hide(); + } + } + } } diff --git a/core/src/io/anuke/mindustry/core/GameState.java b/core/src/io/anuke/mindustry/core/GameState.java index 20acc304e3..1e755de6c6 100644 --- a/core/src/io/anuke/mindustry/core/GameState.java +++ b/core/src/io/anuke/mindustry/core/GameState.java @@ -1,40 +1,63 @@ package io.anuke.mindustry.core; -import io.anuke.mindustry.game.Difficulty; +import io.anuke.arc.Events; +import io.anuke.mindustry.entities.type.BaseUnit; +import io.anuke.mindustry.entities.type.base.BaseDrone; import io.anuke.mindustry.game.EventType.StateChangeEvent; -import io.anuke.mindustry.game.GameMode; -import io.anuke.mindustry.game.Inventory; -import io.anuke.ucore.core.Events; +import io.anuke.mindustry.game.*; +import io.anuke.mindustry.net.Net; + +import static io.anuke.mindustry.Vars.unitGroups; +import static io.anuke.mindustry.Vars.waveTeam; public class GameState{ - private State state = State.menu; + /** Current wave number, can be anything in non-wave modes. */ + public int wave = 1; + /** Wave countdown in ticks. */ + public float wavetime; + /** Whether the game is in game over state. */ + public boolean gameOver = false, launched = false; + /** The current game rules. */ + public Rules rules = new Rules(); + /** Statistics for this save/game. Displayed after game over. */ + public Stats stats = new Stats(); + /** Team data. Gets reset every new game. */ + public Teams teams = new Teams(); + /** Number of enemies in the game; only used clientside in servers. */ + public int enemies; + /** Current game state. */ + private State state = State.menu; - public final Inventory inventory = new Inventory(); + public int enemies(){ + return Net.client() ? enemies : unitGroups[waveTeam.ordinal()].count(b -> !(b instanceof BaseDrone)); + } - public int wave = 1; - public int lastUpdated = -1; - public float wavetime; - public float extrawavetime; - public int enemies = 0; - public boolean gameOver = false; - public GameMode mode = GameMode.waves; - public Difficulty difficulty = Difficulty.normal; - public boolean friendlyFire; - - public void set(State astate){ - Events.fire(StateChangeEvent.class, state, astate); - state = astate; - } - - public boolean is(State astate){ - return state == astate; - } + public BaseUnit boss(){ + return unitGroups[waveTeam.ordinal()].find(BaseUnit::isBoss); + } - public State getState(){ - return state; - } - - public enum State{ - paused, playing, menu - } + public void set(State astate){ + Events.fire(new StateChangeEvent(state, astate)); + state = astate; + } + + public boolean isEditor(){ + return rules.editor; + } + + public boolean isPaused(){ + return (is(State.paused) && !Net.active()) || (gameOver && !Net.active()); + } + + public boolean is(State astate){ + return state == astate; + } + + public State getState(){ + return state; + } + + public enum State{ + paused, playing, menu + } } diff --git a/core/src/io/anuke/mindustry/core/Logic.java b/core/src/io/anuke/mindustry/core/Logic.java index cb4f484c73..b4a2fd9b76 100644 --- a/core/src/io/anuke/mindustry/core/Logic.java +++ b/core/src/io/anuke/mindustry/core/Logic.java @@ -1,109 +1,167 @@ package io.anuke.mindustry.core; -import com.badlogic.gdx.utils.Array; +import io.anuke.annotations.Annotations.Loc; +import io.anuke.annotations.Annotations.Remote; +import io.anuke.arc.ApplicationListener; +import io.anuke.arc.Events; +import io.anuke.arc.collection.ObjectSet.ObjectSetIterator; +import io.anuke.arc.util.Time; +import io.anuke.mindustry.content.*; import io.anuke.mindustry.core.GameState.State; -import io.anuke.mindustry.entities.enemies.Enemy; -import io.anuke.mindustry.game.EnemySpawn; -import io.anuke.mindustry.game.EventType.GameOverEvent; -import io.anuke.mindustry.game.EventType.PlayEvent; -import io.anuke.mindustry.game.EventType.ResetEvent; -import io.anuke.mindustry.game.EventType.WaveEvent; -import io.anuke.mindustry.game.SpawnPoint; -import io.anuke.mindustry.game.WaveCreator; -import io.anuke.mindustry.graphics.Fx; +import io.anuke.mindustry.entities.*; +import io.anuke.mindustry.entities.type.Player; +import io.anuke.mindustry.entities.type.TileEntity; +import io.anuke.mindustry.game.EventType.*; +import io.anuke.mindustry.game.*; +import io.anuke.mindustry.game.Teams.TeamData; +import io.anuke.mindustry.gen.BrokenBlock; import io.anuke.mindustry.net.Net; -import io.anuke.mindustry.net.NetEvents; +import io.anuke.mindustry.type.Item; +import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.blocks.ProductionBlocks; -import io.anuke.ucore.core.Effects; -import io.anuke.ucore.core.Events; -import io.anuke.ucore.core.Timers; -import io.anuke.ucore.entities.Entities; -import io.anuke.ucore.modules.Module; -import io.anuke.ucore.util.Mathf; +import io.anuke.mindustry.world.blocks.BuildBlock; +import io.anuke.mindustry.world.blocks.BuildBlock.BuildEntity; import static io.anuke.mindustry.Vars.*; -/**Logic module. +/** + * Logic module. * Handles all logic for entities and waves. * Handles game state events. * Does not store any game state itself. - * + *

* This class should not call any outside methods to change state of modules, but instead fire events. */ -public class Logic extends Module { - private final Array spawns = WaveCreator.getSpawns(); +public class Logic implements ApplicationListener{ - @Override - public void init(){ - Entities.initPhysics(); - Entities.collisions().setCollider(tilesize, world::solid); + public Logic(){ + Events.on(WaveEvent.class, event -> { + if(world.isZone()){ + world.getZone().updateWave(state.wave); + } + for (Player p : playerGroup.all()) { + p.respawns = state.rules.respawns; + } + }); + + Events.on(BlockDestroyEvent.class, event -> { + //blocks that get broken are appended to the team's broken block queue + Tile tile = event.tile; + Block block = tile.block(); + if(block instanceof BuildBlock){ + BuildEntity entity = tile.entity(); + + //update block to reflect the fact that something was being constructed + if(entity.cblock != null && entity.cblock.synthetic()){ + block = entity.cblock; + }else{ + //otherwise this was a deconstruction that was interrupted, don't want to rebuild that + return; + } + } + + TeamData data = state.teams.get(tile.getTeam()); + data.brokenBlocks.addFirst(BrokenBlock.get(tile.x, tile.y, tile.rotation(), block.id)); + }); + } + + /** Handles the event of content being used by either the player or some block. */ + public void handleContent(UnlockableContent content){ + if(!headless){ + data.unlockContent(content); + } } public void play(){ - state.wavetime = wavespace * state.difficulty.timeScaling * 2; + state.set(State.playing); + state.wavetime = state.rules.waveSpacing * 2; //grace period of 2x wave time before game starts + Events.fire(new PlayEvent()); - if(state.mode.infiniteResources){ - state.inventory.fill(); + //add starting items + if(!world.isZone()){ + for(Team team : Team.all){ + if(state.teams.isActive(team)){ + for(Tile core : state.teams.get(team).cores){ + core.entity.items.add(Items.copper, 200); + } + } + } } - - Events.fire(PlayEvent.class); } public void reset(){ state.wave = 1; - state.extrawavetime = maxwavespace * state.difficulty.maxTimeScaling; - state.wavetime = wavespace * state.difficulty.timeScaling; - state.enemies = 0; - state.lastUpdated = -1; - state.gameOver = false; - state.inventory.clearItems(); + state.wavetime = state.rules.waveSpacing; + state.gameOver = state.launched = false; + state.teams = new Teams(); + state.rules = new Rules(); + state.stats = new Stats(); - Timers.clear(); + Time.clear(); Entities.clear(); + TileEntity.sleepingEntities = 0; - Events.fire(ResetEvent.class); + Events.fire(new ResetEvent()); } public void runWave(){ + world.spawner.spawnEnemies(); + state.wave++; + state.wavetime = world.isZone() && world.getZone().isBossWave(state.wave) ? state.rules.waveSpacing * state.rules.bossWaveMultiplier : + world.isZone() && world.getZone().isLaunchWave(state.wave) ? state.rules.waveSpacing * state.rules.launchWaveMultiplier : state.rules.waveSpacing; - if(state.lastUpdated < state.wave + 1){ - world.pathfinder().resetPaths(); - state.lastUpdated = state.wave + 1; - } + Events.fire(new WaveEvent()); + } - for(EnemySpawn spawn : spawns){ - Array spawns = world.getSpawns(); + private void checkGameOver(){ + if(!state.rules.attackMode && state.teams.get(defaultTeam).cores.size == 0 && !state.gameOver){ + state.gameOver = true; + Events.fire(new GameOverEvent(waveTeam)); + }else if(state.rules.attackMode){ + Team alive = null; - for(int lane = 0; lane < spawns.size; lane ++){ - int fl = lane; - Tile tile = spawns.get(lane).start; - int spawnamount = spawn.evaluate(state.wave, lane); - - for(int i = 0; i < spawnamount; i ++){ - float range = 12f; - - Timers.runTask(i*5f, () -> { - - Enemy enemy = new Enemy(spawn.type); - enemy.set(tile.worldx() + Mathf.range(range), tile.worldy() + Mathf.range(range)); - enemy.lane = fl; - enemy.tier = spawn.tier(state.wave, fl); - enemy.add(); - - Effects.effect(Fx.spawn, enemy); - - state.enemies ++; - }); + for(Team team : Team.all){ + if(state.teams.get(team).cores.size > 0){ + if(alive != null){ + return; + } + alive = team; } } + + if(alive != null && !state.gameOver){ + state.gameOver = true; + Events.fire(new GameOverEvent(alive)); + } + } + } + + @Remote(called = Loc.both) + public static void launchZone(){ + if(!headless){ + ui.hudfrag.showLaunch(); } - state.wave ++; - state.wavetime = wavespace * state.difficulty.timeScaling; - state.extrawavetime = maxwavespace * state.difficulty.maxTimeScaling; + for(Tile tile : new ObjectSetIterator<>(state.teams.get(defaultTeam).cores)){ + Effects.effect(Fx.launch, tile); + } - Events.fire(WaveEvent.class); + Time.runTask(30f, () -> { + for(Tile tile : new ObjectSetIterator<>(state.teams.get(defaultTeam).cores)){ + for(Item item : content.items()){ + data.addItem(item, tile.entity.items.get(item)); + } + world.removeBlock(tile); + } + state.launched = true; + }); + } + + @Remote(called = Loc.both) + public static void onGameOver(Team winner){ + state.stats.wavesLasted = state.wave; + ui.restart.show(winner); + netClient.setQuiet(); } @Override @@ -111,50 +169,64 @@ public class Logic extends Module { if(!state.is(State.menu)){ - if(control != null) control.triggerInputUpdate(); + if(!state.isPaused()){ + Time.update(); - if(!state.is(State.paused) || Net.active()){ - Timers.update(); - } - - if(!Net.client()) - world.pathfinder().update(); - - if(world.getCore() != null && world.getCore().block() != ProductionBlocks.core && !state.gameOver){ - state.gameOver = true; - if(Net.server()) NetEvents.handleGameOver(); - Events.fire(GameOverEvent.class); - } - - if(!state.is(State.paused) || Net.active()){ - - if(!state.mode.disableWaveTimer){ - - if(state.enemies <= 0){ - if(!world.getMap().name.equals("tutorial")) state.wavetime -= Timers.delta(); - - if(state.lastUpdated < state.wave + 1 && state.wavetime < aheadPathfinding){ //start updating beforehand - world.pathfinder().resetPaths(); - state.lastUpdated = state.wave + 1; - } - }else if(!world.getMap().name.equals("tutorial")){ - state.extrawavetime -= Timers.delta(); + if(state.rules.waves && state.rules.waveTimer && !state.gameOver){ + if(!state.rules.waitForWaveToEnd || unitGroups[waveTeam.ordinal()].size() == 0){ + state.wavetime = Math.max(state.wavetime - Time.delta(), 0); } } - if(!Net.client() && (state.wavetime <= 0 || state.extrawavetime <= 0)){ + if(!Net.client() && state.wavetime <= 0 && state.rules.waves){ runWave(); } - Entities.update(Entities.defaultGroup()); - Entities.update(bulletGroup); - Entities.update(enemyGroup); - Entities.update(tileGroup); - Entities.update(shieldGroup); + if(!headless){ + Entities.update(effectGroup); + Entities.update(groundEffectGroup); + } + + if(!state.isEditor()){ + for(EntityGroup group : unitGroups){ + Entities.update(group); + } + + Entities.update(puddleGroup); + Entities.update(shieldGroup); + Entities.update(bulletGroup); + Entities.update(tileGroup); + Entities.update(fireGroup); + }else{ + for(EntityGroup group : unitGroups){ + group.updateEvents(); + collisions.updatePhysics(group); + } + } + + Entities.update(playerGroup); - Entities.collideGroups(bulletGroup, enemyGroup); - Entities.collideGroups(bulletGroup, playerGroup); + //effect group only contains item transfers in the headless version, update it! + if(headless){ + Entities.update(effectGroup); + } + + if(!state.isEditor()){ + + for(EntityGroup group : unitGroups){ + if(group.isEmpty()) continue; + collisions.collideGroups(bulletGroup, group); + } + + collisions.collideGroups(bulletGroup, playerGroup); + } + + world.pathfinder.update(); + } + + if(!Net.client() && !world.isInvalidMap() && !state.isEditor()){ + checkGameOver(); } } } diff --git a/core/src/io/anuke/mindustry/core/NetClient.java b/core/src/io/anuke/mindustry/core/NetClient.java index 85fc3b3ebc..05ea541609 100644 --- a/core/src/io/anuke/mindustry/core/NetClient.java +++ b/core/src/io/anuke/mindustry/core/NetClient.java @@ -1,82 +1,89 @@ package io.anuke.mindustry.core; -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.utils.IntMap; -import com.badlogic.gdx.utils.IntSet; -import com.badlogic.gdx.utils.TimeUtils; +import io.anuke.annotations.Annotations.*; +import io.anuke.arc.ApplicationListener; +import io.anuke.arc.Core; +import io.anuke.arc.collection.IntSet; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.math.RandomXS128; +import io.anuke.arc.util.*; +import io.anuke.arc.util.io.ReusableByteInStream; +import io.anuke.arc.util.serialization.Base64Coder; import io.anuke.mindustry.Vars; import io.anuke.mindustry.core.GameState.State; -import io.anuke.mindustry.entities.Bullet; -import io.anuke.mindustry.entities.BulletType; -import io.anuke.mindustry.entities.Player; -import io.anuke.mindustry.entities.SyncEntity; -import io.anuke.mindustry.entities.enemies.Enemy; -import io.anuke.mindustry.net.Net; +import io.anuke.mindustry.entities.Entities; +import io.anuke.mindustry.entities.EntityGroup; +import io.anuke.mindustry.entities.traits.BuilderTrait.BuildRequest; +import io.anuke.mindustry.entities.traits.SyncTrait; +import io.anuke.mindustry.entities.traits.TypeTrait; +import io.anuke.mindustry.entities.type.Player; +import io.anuke.mindustry.entities.type.Unit; +import io.anuke.mindustry.game.Version; +import io.anuke.mindustry.gen.Call; +import io.anuke.mindustry.gen.RemoteReadClient; +import io.anuke.mindustry.net.Administration.TraceInfo; +import io.anuke.mindustry.net.*; import io.anuke.mindustry.net.Net.SendMode; -import io.anuke.mindustry.net.NetworkIO; import io.anuke.mindustry.net.Packets.*; -import io.anuke.mindustry.resource.Item; -import io.anuke.mindustry.resource.Upgrade; -import io.anuke.mindustry.resource.UpgradeRecipes; -import io.anuke.mindustry.resource.Weapon; -import io.anuke.mindustry.world.Block; -import io.anuke.mindustry.world.Map; -import io.anuke.mindustry.world.Placement; import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.blocks.ProductionBlocks; -import io.anuke.ucore.core.Effects; -import io.anuke.ucore.core.Timers; -import io.anuke.ucore.entities.BaseBulletType; -import io.anuke.ucore.entities.Entities; -import io.anuke.ucore.entities.Entity; -import io.anuke.ucore.entities.EntityGroup; -import io.anuke.ucore.modules.Module; -import io.anuke.ucore.util.Log; -import io.anuke.ucore.util.Timer; +import io.anuke.mindustry.world.modules.ItemModule; -import java.nio.ByteBuffer; +import java.io.DataInputStream; +import java.io.IOException; +import java.util.zip.InflaterInputStream; import static io.anuke.mindustry.Vars.*; -public class NetClient extends Module { - private final static float dataTimeout = 60*18; //18 seconds timeout +public class NetClient implements ApplicationListener{ + private final static float dataTimeout = 60 * 18; private final static float playerSyncTime = 2; - private final static int maxRequests = 50; + public final static float viewScale = 2f; - private Timer timer = new Timer(5); + private Interval timer = new Interval(5); + /** Whether the client is currently connecting. */ private boolean connecting = false; - private boolean kicked = false; - private IntSet recieved = new IntSet(); - private IntMap recent = new IntMap<>(); - private int requests = 0; - private float timeoutTime = 0f; //data timeout counter + /** If true, no message will be shown on disconnect. */ + private boolean quiet = false; + /** Counter for data timeout. */ + private float timeoutTime = 0f; + /** Last sent client snapshot ID. */ + private int lastSent; + + /** List of entities that were removed, and need not be added while syncing. */ + private IntSet removed = new IntSet(); + /** Byte stream for reading in snapshots. */ + private ReusableByteInStream byteStream = new ReusableByteInStream(); + private DataInputStream dataStream = new DataInputStream(byteStream); public NetClient(){ Net.handleClient(Connect.class, packet -> { + Log.info("Connecting to server: {0}", packet.addressTCP); + player.isAdmin = false; - Net.setClientLoaded(false); - recieved.clear(); - recent.clear(); - timeoutTime = 0f; - connecting = true; - kicked = false; + reset(); - ui.chatfrag.clearMessages(); ui.loadfrag.hide(); - ui.loadfrag.show("$text.connecting.data"); + ui.loadfrag.show("$connecting.data"); - Entities.clear(); + ui.loadfrag.setButton(() -> { + ui.loadfrag.hide(); + connecting = false; + quiet = true; + Net.disconnect(); + }); ConnectPacket c = new ConnectPacket(); c.name = player.name; - c.android = mobile; + c.mobile = mobile; + c.versionType = Version.type; c.color = Color.rgba8888(player.color); + c.usid = getUsid(packet.addressTCP); c.uuid = Platform.instance.getUUID(); if(c.uuid == null){ - ui.showError("$text.invalidid"); + ui.showError("$invalidid"); ui.loadfrag.hide(); disconnectQuietly(); return; @@ -86,238 +93,207 @@ public class NetClient extends Module { }); Net.handleClient(Disconnect.class, packet -> { - if (kicked) return; - - Timers.runTask(3f, ui.loadfrag::hide); - state.set(State.menu); - - ui.showError("$text.disconnect"); connecting = false; - + logic.reset(); Platform.instance.updateRPC(); + + if(quiet) return; + + Time.runTask(3f, ui.loadfrag::hide); + + ui.showError("$disconnect"); }); - Net.handleClient(WorldData.class, data -> { + Net.handleClient(WorldStream.class, data -> { Log.info("Recieved world data: {0} bytes.", data.stream.available()); - NetworkIO.loadWorld(data.stream); - player.set(world.getSpawnX(), world.getSpawnY()); + NetworkIO.loadWorld(new InflaterInputStream(data.stream)); finishConnecting(); }); - Net.handleClient(CustomMapPacket.class, packet -> { - Log.info("Recieved custom map: {0} bytes.", packet.stream.available()); - - //custom map is always sent before world data - Map map = NetworkIO.loadMap(packet.stream); - - world.maps().setNetworkMap(map); - - MapAckPacket ack = new MapAckPacket(); - Net.send(ack, SendMode.tcp); + Net.handleClient(InvokePacket.class, packet -> { + packet.writeBuffer.position(0); + RemoteReadClient.readPacket(packet.writeBuffer, packet.type); }); + } - Net.handleClient(SyncPacket.class, packet -> { - if (connecting) return; - int players = 0; - int enemies = 0; + //called on all clients + @Remote(called = Loc.server, targets = Loc.server, variants = Variant.both) + public static void sendMessage(String message, String sender, Player playersender){ + if(Vars.ui != null){ + Vars.ui.chatfrag.addMessage(message, sender); + } - ByteBuffer data = ByteBuffer.wrap(packet.data); - long time = data.getLong(); + if(playersender != null){ + playersender.lastText = message; + playersender.textFadeTime = 1f; + } + } - byte groupid = data.get(); + //equivalent to above method but there's no sender and no console log + @Remote(called = Loc.server, targets = Loc.server) + public static void sendMessage(String message){ + if(Vars.ui != null){ + Vars.ui.chatfrag.addMessage(message, null); + } + } - EntityGroup group = Entities.getGroup(groupid); + //called when a server recieves a chat message from a player + @Remote(called = Loc.server, targets = Loc.client) + public static void sendChatMessage(Player player, String message){ + if(message.length() > maxTextLength){ + throw new ValidateException(player, "Player has sent a message above the text limit."); + } - while (data.position() < data.capacity()) { - int id = data.getInt(); + //server console logging + Log.info("&y{0}: &lb{1}", player.name, message); - SyncEntity entity = (SyncEntity) group.getByID(id); + //invoke event for all clients but also locally + //this is required so other clients get the correct name even if they don't know who's sending it yet + Call.sendMessage(message, colorizeName(player.id, player.name), player); + } - if(entity instanceof Player) players ++; - if(entity instanceof Enemy) enemies ++; + private static String colorizeName(int id, String name){ + Player player = playerGroup.getByID(id); + if(name == null || player == null) return null; + return "[#" + player.color.toString().toUpperCase() + "]" + name; + } - if (entity == null || id == player.id) { - if (id != player.id && requests < maxRequests) { - EntityRequestPacket req = new EntityRequestPacket(); - req.id = id; - req.group = groupid; - Net.send(req, SendMode.udp); - requests ++; + @Remote(variants = Variant.one) + public static void onTraceInfo(Player player, TraceInfo info){ + if(player != null){ + ui.traces.show(player, info); + } + } + + @Remote(variants = Variant.one, priority = PacketPriority.high) + public static void onKick(KickReason reason){ + netClient.disconnectQuietly(); + state.set(State.menu); + + if(!reason.quiet){ + if(reason.extraText() != null){ + ui.showText(reason.toString(), reason.extraText()); + }else{ + ui.showText("$disconnect", reason.toString()); + } + } + ui.loadfrag.hide(); + } + + @Remote(variants = Variant.both) + public static void onInfoMessage(String message){ + ui.showText("", message); + } + + @Remote(variants = Variant.both) + public static void onWorldDataBegin(){ + Entities.clear(); + netClient.removed.clear(); + logic.reset(); + + ui.chatfrag.clearMessages(); + Net.setClientLoaded(false); + + ui.loadfrag.show("$connecting.data"); + + ui.loadfrag.setButton(() -> { + ui.loadfrag.hide(); + netClient.connecting = false; + netClient.quiet = true; + Net.disconnect(); + }); + } + + @Remote(variants = Variant.one) + public static void onPositionSet(float x, float y){ + player.x = x; + player.y = y; + } + + @Remote + public static void onPlayerDisconnect(int playerid){ + playerGroup.removeByID(playerid); + } + + @Remote(variants = Variant.one, priority = PacketPriority.low, unreliable = true) + public static void onEntitySnapshot(byte groupID, short amount, short dataLen, byte[] data){ + try{ + netClient.byteStream.setBytes(Net.decompressSnapshot(data, dataLen)); + DataInputStream input = netClient.dataStream; + + EntityGroup group = Entities.getGroup(groupID); + + //go through each entity + for(int j = 0; j < amount; j++){ + int id = input.readInt(); + byte typeID = input.readByte(); + + SyncTrait entity = group == null ? null : (SyncTrait)group.getByID(id); + boolean add = false, created = false; + + if(entity == null && id == player.id){ + entity = player; + add = true; + } + + //entity must not be added yet, so create it + if(entity == null){ + entity = (SyncTrait)TypeTrait.getTypeByID(typeID).get(); //create entity from supplier + entity.resetID(id); + if(!netClient.isEntityUsed(entity.getID())){ + add = true; } - data.position(data.position() + SyncEntity.getWriteSize((Class) group.getType())); - } else { - entity.read(data, time); + created = true; + } + + //read the entity + entity.read(input); + + if(created && entity.getInterpolator() != null && entity.getInterpolator().target != null){ + //set initial starting position + entity.setNet(entity.getInterpolator().target.x, entity.getInterpolator().target.y); + if(entity instanceof Unit && entity.getInterpolator().targets.length > 0){ + ((Unit)entity).rotation = entity.getInterpolator().targets[0]; + } + } + + if(add){ + entity.add(); + netClient.addRemovedEntity(entity.getID()); + } + } + }catch(IOException e){ + throw new RuntimeException(e); + } + } + + @Remote(variants = Variant.one, priority = PacketPriority.low, unreliable = true) + public static void onStateSnapshot(float waveTime, int wave, int enemies, short coreDataLen, byte[] coreData){ + try{ + state.wavetime = waveTime; + state.wave = wave; + state.enemies = enemies; + + netClient.byteStream.setBytes(Net.decompressSnapshot(coreData, coreDataLen)); + DataInputStream input = netClient.dataStream; + + byte cores = input.readByte(); + for(int i = 0; i < cores; i++){ + int pos = input.readInt(); + Tile tile = world.tile(pos); + + if(tile != null && tile.entity != null){ + tile.entity.items.read(input); + }else{ + new ItemModule().read(input); } } - if(debugNet){ - clientDebug.setSyncDebug(players, enemies); - } - }); - - Net.handleClient(StateSyncPacket.class, packet -> { - - System.arraycopy(packet.items, 0, state.inventory.getItems(), 0, packet.items.length); - - state.enemies = packet.enemies; - state.wavetime = packet.countdown; - state.wave = packet.wave; - - ui.hudfrag.updateItems(); - }); - - Net.handleClient(BlockLogRequestPacket.class, packet -> { - currentEditLogs = packet.editlogs; - }); - - Net.handleClient(PlacePacket.class, (packet) -> { - Placement.placeBlock(packet.x, packet.y, Block.getByID(packet.block), packet.rotation, true, Timers.get("placeblocksound", 10)); - - if(packet.playerid == player.id){ - Tile tile = world.tile(packet.x, packet.y); - if(tile != null) Block.getByID(packet.block).placed(tile); - } - }); - - Net.handleClient(BreakPacket.class, (packet) -> - Placement.breakBlock(packet.x, packet.y, true, Timers.get("breakblocksound", 10))); - - Net.handleClient(EntitySpawnPacket.class, packet -> { - EntityGroup group = packet.group; - - //duplicates. - if (group.getByID(packet.entity.id) != null || - recieved.contains(packet.entity.id)) return; - - recieved.add(packet.entity.id); - recent.put(packet.entity.id, packet.entity); - - packet.entity.add(); - - Log.info("Recieved entity {0}", packet.entity.id); - }); - - Net.handleClient(EnemyDeathPacket.class, packet -> { - Enemy enemy = enemyGroup.getByID(packet.id); - if (enemy != null){ - enemy.type.onDeath(enemy, true); - }else if(recent.get(packet.id) != null){ - recent.get(packet.id).remove(); - }else{ - Log.err("Got remove for null entity! {0}", packet.id); - } - recieved.add(packet.id); - }); - - Net.handleClient(BulletPacket.class, packet -> { - //TODO shoot effects for enemies, clientside as well as serverside - BulletType type = (BulletType) BaseBulletType.getByID(packet.type); - Entity owner = enemyGroup.getByID(packet.owner); - new Bullet(type, owner, packet.x, packet.y, packet.angle).add(); - }); - - Net.handleClient(BlockDestroyPacket.class, packet -> { - Tile tile = world.tile(packet.position % world.width(), packet.position / world.width()); - if (tile != null && tile.entity != null) { - tile.entity.onDeath(true); - } - }); - - Net.handleClient(BlockUpdatePacket.class, packet -> { - Tile tile = world.tile(packet.position % world.width(), packet.position / world.width()); - if (tile != null && tile.entity != null) { - tile.entity.health = packet.health; - } - }); - - Net.handleClient(DisconnectPacket.class, packet -> { - Player player = playerGroup.getByID(packet.playerid); - - if (player != null) { - player.remove(); - } - - Platform.instance.updateRPC(); - }); - - Net.handleClient(KickPacket.class, packet -> { - kicked = true; - Net.disconnect(); - state.set(State.menu); - if(!packet.reason.quiet) ui.showError("$text.server.kicked." + packet.reason.name()); - ui.loadfrag.hide(); - }); - - Net.handleClient(GameOverPacket.class, packet -> { - if(world.getCore().block() != ProductionBlocks.core && - world.getCore().entity != null){ - world.getCore().entity.onDeath(true); - } - kicked = true; - ui.restart.show(); - }); - - Net.handleClient(FriendlyFireChangePacket.class, packet -> state.friendlyFire = packet.enabled); - - Net.handleClient(ItemTransferPacket.class, packet -> { - Runnable r = () -> { - Tile tile = world.tile(packet.position); - if (tile == null || tile.entity == null) return; - Tile next = tile.getNearby(packet.rotation); - tile.entity.items[packet.itemid] --; - next.block().handleItem(Item.getByID(packet.itemid), next, tile); - }; - - threads.run(r); - }); - - Net.handleClient(ItemSetPacket.class, packet -> { - Runnable r = () -> { - Tile tile = world.tile(packet.position); - if (tile == null || tile.entity == null) return; - tile.entity.items[packet.itemid] = packet.amount; - }; - - threads.run(r); - }); - - Net.handleClient(ItemOffloadPacket.class, packet -> { - Runnable r = () -> { - Tile tile = world.tile(packet.position); - if (tile == null || tile.entity == null) return; - Tile next = tile.getNearby(tile.getRotation()); - next.block().handleItem(Item.getByID(packet.itemid), next, tile); - }; - - threads.run(r); - }); - - Net.handleClient(NetErrorPacket.class, packet -> { - ui.showError(packet.message); - disconnectQuietly(); - }); - - Net.handleClient(PlayerAdminPacket.class, packet -> { - Player player = playerGroup.getByID(packet.id); - player.isAdmin = packet.admin; - ui.listfrag.rebuild(); - }); - - Net.handleClient(TracePacket.class, packet -> { - Player player = playerGroup.getByID(packet.info.playerid); - ui.traces.show(player, packet.info); - }); - - Net.handleClient(UpgradePacket.class, packet -> { - Weapon weapon = (Weapon) Upgrade.getByID(packet.id); - - state.inventory.removeItems(UpgradeRecipes.get(weapon)); - control.upgrades().addWeapon(weapon); - ui.hudfrag.updateWeapons(); - Effects.sound("purchase"); - }); + }catch(IOException e){ + throw new RuntimeException(e); + } } @Override @@ -329,12 +305,12 @@ public class NetClient extends Module { }else if(!connecting){ Net.disconnect(); }else{ //...must be connecting - timeoutTime += Timers.delta(); + timeoutTime += Time.delta(); if(timeoutTime > dataTimeout){ Log.err("Failed to load data!"); ui.loadfrag.hide(); - kicked = true; - ui.showError("$text.disconnect.data"); + quiet = true; + ui.showError("$disconnect.data"); Net.disconnect(); timeoutTime = 0f; } @@ -351,8 +327,20 @@ public class NetClient extends Module { ui.loadfrag.hide(); ui.join.hide(); Net.setClientLoaded(true); - Timers.runTask(1f, () -> Net.send(new ConnectConfirmPacket(), SendMode.tcp)); - Timers.runTask(40f, Platform.instance::updateRPC); + Core.app.post(Call::connectConfirm); + Time.runTask(40f, Platform.instance::updateRPC); + } + + private void reset(){ + Net.setClientLoaded(false); + removed.clear(); + timeoutTime = 0f; + connecting = true; + quiet = false; + lastSent = 0; + + Entities.clear(); + ui.chatfrag.clearMessages(); } public void beginConnecting(){ @@ -360,31 +348,60 @@ public class NetClient extends Module { } public void disconnectQuietly(){ - kicked = true; + quiet = true; Net.disconnect(); } - public void clearRecieved(){ - recieved.clear(); + /** When set, any disconnects will be ignored and no dialogs will be shown. */ + public void setQuiet(){ + quiet = true; + } + + public void addRemovedEntity(int id){ + removed.add(id); + } + + public boolean isEntityUsed(int id){ + return removed.contains(id); } void sync(){ - requests = 0; if(timer.get(0, playerSyncTime)){ + BuildRequest[] requests; + //limit to 10 to prevent buffer overflows + int usedRequests = Math.min(player.buildQueue().size, 10); - byte[] bytes = new byte[player.getWriteSize() + 8]; - ByteBuffer buffer = ByteBuffer.wrap(bytes); - buffer.putLong(TimeUtils.millis()); - player.write(buffer); + requests = new BuildRequest[usedRequests]; + for(int i = 0; i < usedRequests; i++){ + requests[i] = player.buildQueue().get(i); + } - PositionPacket packet = new PositionPacket(); - packet.data = bytes; - Net.send(packet, SendMode.udp); + Call.onClientShapshot(lastSent++, player.x, player.y, + player.pointerX, player.pointerY, player.rotation, player.baseRotation, + player.velocity().x, player.velocity().y, + player.getMineTile(), + player.isBoosting, player.isShooting, ui.chatfrag.chatOpen(), + requests, + Core.camera.position.x, Core.camera.position.y, + Core.camera.width * viewScale, Core.camera.height * viewScale); } if(timer.get(1, 60)){ Net.updatePing(); } } -} + + String getUsid(String ip){ + if(Core.settings.getString("usid-" + ip, null) != null){ + return Core.settings.getString("usid-" + ip, null); + }else{ + byte[] bytes = new byte[8]; + new RandomXS128().nextBytes(bytes); + String result = new String(Base64Coder.encode(bytes)); + Core.settings.put("usid-" + ip, result); + Core.settings.save(); + return result; + } + } +} \ No newline at end of file diff --git a/core/src/io/anuke/mindustry/core/NetCommon.java b/core/src/io/anuke/mindustry/core/NetCommon.java deleted file mode 100644 index 9b8a9435da..0000000000 --- a/core/src/io/anuke/mindustry/core/NetCommon.java +++ /dev/null @@ -1,69 +0,0 @@ -package io.anuke.mindustry.core; - -import io.anuke.mindustry.entities.Player; -import io.anuke.mindustry.net.Net; -import io.anuke.mindustry.net.Net.SendMode; -import io.anuke.mindustry.net.Packets.*; -import io.anuke.mindustry.resource.Upgrade; -import io.anuke.mindustry.resource.Weapon; -import io.anuke.mindustry.world.Tile; -import io.anuke.ucore.modules.Module; - -import static io.anuke.mindustry.Vars.*; - -public class NetCommon extends Module { - - public NetCommon(){ - - Net.handle(ShootPacket.class, (packet) -> { - Player player = playerGroup.getByID(packet.playerid); - - Weapon weapon = (Weapon) Upgrade.getByID(packet.weaponid); - weapon.shoot(player, packet.x, packet.y, packet.rotation); - }); - - Net.handle(ChatPacket.class, (packet) -> { - ui.chatfrag.addMessage(packet.text, colorizeName(packet.id, packet.name)); - }); - - Net.handle(WeaponSwitchPacket.class, (packet) -> { - Player player = playerGroup.getByID(packet.playerid); - - if (player == null) return; - - player.weaponLeft = (Weapon) Upgrade.getByID(packet.left); - player.weaponRight = (Weapon) Upgrade.getByID(packet.right); - }); - - Net.handle(BlockTapPacket.class, (packet) -> { - Tile tile = world.tile(packet.position); - tile.block().tapped(tile); - }); - - Net.handle(BlockConfigPacket.class, (packet) -> { - Tile tile = world.tile(packet.position); - if (tile != null) tile.block().configure(tile, packet.data); - }); - - Net.handle(PlayerDeathPacket.class, (packet) -> { - Player player = playerGroup.getByID(packet.id); - if(player == null) return; - - player.doRespawn(); - }); - } - - public void sendMessage(String message){ - ChatPacket packet = new ChatPacket(); - packet.name = null; - packet.text = message; - Net.send(packet, SendMode.tcp); - if(!headless) ui.chatfrag.addMessage(message, null); - } - - public String colorizeName(int id, String name){ - Player player = playerGroup.getByID(id); - if(name == null || player == null) return null; - return "[#" + player.color.toString().toUpperCase() + "]" + name; - } -} diff --git a/core/src/io/anuke/mindustry/core/NetServer.java b/core/src/io/anuke/mindustry/core/NetServer.java index 0559a441c6..ab4eca7e0d 100644 --- a/core/src/io/anuke/mindustry/core/NetServer.java +++ b/core/src/io/anuke/mindustry/core/NetServer.java @@ -1,52 +1,71 @@ package io.anuke.mindustry.core; -import com.badlogic.gdx.utils.*; +import io.anuke.annotations.Annotations.Loc; +import io.anuke.annotations.Annotations.Remote; +import io.anuke.arc.ApplicationListener; +import io.anuke.arc.Events; +import io.anuke.arc.collection.IntMap; +import io.anuke.arc.collection.ObjectSet; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.graphics.Colors; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.Rectangle; +import io.anuke.arc.math.geom.Vector2; +import io.anuke.arc.util.*; +import io.anuke.arc.util.io.*; +import io.anuke.mindustry.content.Blocks; import io.anuke.mindustry.core.GameState.State; -import io.anuke.mindustry.entities.Player; -import io.anuke.mindustry.entities.SyncEntity; -import io.anuke.mindustry.game.EventType.GameOverEvent; -import io.anuke.mindustry.io.Version; +import io.anuke.mindustry.entities.Entities; +import io.anuke.mindustry.entities.EntityGroup; +import io.anuke.mindustry.entities.traits.BuilderTrait.BuildRequest; +import io.anuke.mindustry.entities.traits.Entity; +import io.anuke.mindustry.entities.traits.SyncTrait; +import io.anuke.mindustry.entities.type.Player; +import io.anuke.mindustry.game.EventType.WorldLoadEvent; +import io.anuke.mindustry.game.Team; +import io.anuke.mindustry.game.Version; +import io.anuke.mindustry.gen.Call; +import io.anuke.mindustry.gen.RemoteReadServer; import io.anuke.mindustry.net.*; import io.anuke.mindustry.net.Administration.PlayerInfo; -import io.anuke.mindustry.net.Net.SendMode; +import io.anuke.mindustry.net.Administration.TraceInfo; import io.anuke.mindustry.net.Packets.*; -import io.anuke.mindustry.resource.*; -import io.anuke.mindustry.world.Block; -import io.anuke.mindustry.world.Placement; import io.anuke.mindustry.world.Tile; -import io.anuke.ucore.core.Events; -import io.anuke.ucore.core.Timers; -import io.anuke.ucore.entities.Entities; -import io.anuke.ucore.entities.EntityGroup; -import io.anuke.ucore.modules.Module; -import io.anuke.ucore.util.Log; -import io.anuke.ucore.util.Timer; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; +import java.io.*; import java.nio.ByteBuffer; +import java.util.zip.DeflaterOutputStream; + import static io.anuke.mindustry.Vars.*; -public class NetServer extends Module{ - private final static float serverSyncTime = 4, itemSyncTime = 10, kickDuration = 30 * 1000; - - private final static int timerEntitySync = 0; - private final static int timerStateSync = 1; +public class NetServer implements ApplicationListener{ + public final static int maxSnapshotSize = 430; + private final static float serverSyncTime = 15, kickDuration = 30 * 1000; + private final static Vector2 vector = new Vector2(); + private final static Rectangle viewport = new Rectangle(); + /** If a player goes away of their server-side coordinates by this distance, they get teleported back. */ + private final static float correctDist = 16f; public final Administration admins = new Administration(); - /**Maps connection IDs to players.*/ + /** Maps connection IDs to players. */ private IntMap connections = new IntMap<>(); - private ObjectMap weapons = new ObjectMap<>(); private boolean closing = false; - private Timer timer = new Timer(5); + + private ByteBuffer writeBuffer = ByteBuffer.allocate(127); + private ByteBufferOutput outputBuffer = new ByteBufferOutput(writeBuffer); + + /** Stream for writing player sync data to. */ + private ReusableByteOutStream syncStream = new ReusableByteOutStream(); + /** Data stream for writing player sync data to. */ + private DataOutputStream dataStream = new DataOutputStream(syncStream); public NetServer(){ - - Events.on(GameOverEvent.class, () -> { - weapons.clear(); - admins.getEditLogs().clear(); - }); + Events.on(WorldLoadEvent.class, event -> { + if(!headless){ + connections.clear(); + } + }); Net.handleServer(Connect.class, (id, connect) -> { if(admins.isIPBanned(connect.addressTCP)){ @@ -54,28 +73,72 @@ public class NetServer extends Module{ } }); + Net.handleServer(Disconnect.class, (id, packet) -> { + Player player = connections.get(id); + if(player != null){ + onDisconnect(player); + } + connections.remove(id); + }); + Net.handleServer(ConnectPacket.class, (id, packet) -> { - String uuid = new String(Base64Coder.encode(packet.uuid)); + String uuid = packet.uuid; - if(Net.getConnection(id) == null || - admins.isIPBanned(Net.getConnection(id).address)) return; + NetConnection connection = Net.getConnection(id); + + if(connection == null || + admins.isIPBanned(connection.address)) return; + + if(connection.hasBegunConnecting){ + kick(id, KickReason.idInUse); + return; + } + + connection.hasBegunConnecting = true; - TraceInfo trace = admins.getTrace(Net.getConnection(id).address); PlayerInfo info = admins.getInfo(uuid); - trace.uuid = uuid; - trace.android = packet.android; + + connection.mobile = packet.mobile; if(admins.isIDBanned(uuid)){ kick(id, KickReason.banned); return; } - if(TimeUtils.millis() - info.lastKicked < kickDuration){ + if(Time.millis() - info.lastKicked < kickDuration){ kick(id, KickReason.recentKick); return; } - Log.info("Recieved connect packet for player '{0}' / UUID {1} / IP {2}", packet.name, uuid, trace.ip); + if(packet.versionType == null || ((packet.version == -1 || !packet.versionType.equals(Version.type)) && Version.build != -1 && !admins.allowsCustomClients())){ + kick(id, KickReason.customClient); + return; + } + + boolean preventDuplicates = headless && netServer.admins.getStrict(); + + if(preventDuplicates){ + for(Player player : playerGroup.all()){ + if(player.name.trim().equalsIgnoreCase(packet.name.trim())){ + kick(id, KickReason.nameInUse); + return; + } + + if(player.uuid.equals(packet.uuid) || player.usid.equals(packet.usid)){ + kick(id, KickReason.idInUse); + return; + } + } + } + + packet.name = fixName(packet.name); + + if(packet.name.trim().length() <= 0){ + kick(id, KickReason.nameEmpty); + return; + } + + Log.debug("Recieved connect packet for player '{0}' / UUID {1} / IP {2}", packet.name, uuid, connection.address); String ip = Net.getConnection(id).address; @@ -87,294 +150,250 @@ public class NetServer extends Module{ } if(packet.version == -1){ - trace.modclient = true; + connection.modclient = true; } Player player = new Player(); - player.isAdmin = admins.isAdmin(uuid, ip); - player.clientid = id; + player.isAdmin = admins.isAdmin(uuid, packet.usid); + player.con = Net.getConnection(id); + player.usid = packet.usid; player.name = packet.name; - player.isAndroid = packet.android; - player.set(world.getSpawnX(), world.getSpawnY()); - player.setNet(player.x, player.y); + player.uuid = uuid; + player.isMobile = packet.mobile; + player.dead = true; player.setNet(player.x, player.y); player.color.set(packet.color); + player.color.a = 1f; + + try{ + writeBuffer.position(0); + player.write(outputBuffer); + }catch(Throwable t){ + t.printStackTrace(); + kick(id, KickReason.nameEmpty); + return; + } + + //playing in pvp mode automatically assigns players to teams + if(state.rules.pvp){ + player.setTeam(assignTeam(playerGroup.all())); + Log.info("Auto-assigned player {0} to team {1}.", player.name, player.getTeam()); + } + connections.put(id, player); - trace.playerid = player.id; - - if(world.getMap().custom){ - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - NetworkIO.writeMap(world.getMap(), stream); - CustomMapPacket data = new CustomMapPacket(); - data.stream = new ByteArrayInputStream(stream.toByteArray()); - Net.sendStream(id, data); - - Log.info("Sending custom map: Packed {0} uncompressed bytes of MAP data.", stream.size()); - }else{ - //hack-- simulate the map ack packet recieved to send the world data to the client. - Net.handleServerReceived(id, new MapAckPacket()); - } + sendWorldData(player, id); Platform.instance.updateRPC(); }); - Net.handleServer(MapAckPacket.class, (id, packet) -> { + Net.handleServer(InvokePacket.class, (id, packet) -> { Player player = connections.get(id); - - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - NetworkIO.writeWorld(player, weapons.get(admins.getTrace(Net.getConnection(id).address).uuid, new ByteArray()), stream); - WorldData data = new WorldData(); - data.stream = new ByteArrayInputStream(stream.toByteArray()); - Net.sendStream(id, data); - - Log.info("Packed {0} uncompressed bytes of WORLD data.", stream.size()); - }); - - Net.handleServer(ConnectConfirmPacket.class, (id, packet) -> { - Player player = connections.get(id); - - if (player == null) return; - - player.add(); - Log.info("&y{0} has connected.", player.name); - netCommon.sendMessage("[accent]" + player.name + " has connected."); - }); - - Net.handleServer(Disconnect.class, (id, packet) -> { - Player player = connections.get(packet.id); - - if (player == null) { - Log.err("Unknown client has disconnected (ID={0})", id); - return; - } - - Log.info("&y{0} has disconnected.", player.name); - netCommon.sendMessage("[accent]" + player.name + " has disconnected."); - player.remove(); - - DisconnectPacket dc = new DisconnectPacket(); - dc.playerid = player.id; - - Net.send(dc, SendMode.tcp); - - Platform.instance.updateRPC(); - admins.save(); - }); - - Net.handleServer(PositionPacket.class, (id, packet) -> { - ByteBuffer buffer = ByteBuffer.wrap(packet.data); - long time = buffer.getLong(); - - Player player = connections.get(id); - player.read(buffer, time); - }); - - Net.handleServer(ShootPacket.class, (id, packet) -> { - TraceInfo info = admins.getTrace(Net.getConnection(id).address); - Weapon weapon = (Weapon)Upgrade.getByID(packet.weaponid); - - float wtrc = 80; - - if(!Timers.get("fastshoot-" + id + "-" + weapon.id, wtrc)){ - info.fastShots.getAndIncrement(weapon.id, 0, 1); - - if(info.fastShots.get(weapon.id, 0) > (int)(wtrc / (weapon.getReload() / 2f)) + 30){ - kick(id, KickReason.fastShoot); - } - }else{ - info.fastShots.put(weapon.id, 0); - } - - packet.playerid = connections.get(id).id; - Net.sendExcept(id, packet, SendMode.udp); - }); - - Net.handleServer(PlacePacket.class, (id, packet) -> { - packet.playerid = connections.get(id).id; - - Block block = Block.getByID(packet.block); - - if(!Placement.validPlace(packet.x, packet.y, block)) return; - - Recipe recipe = Recipes.getByResult(block); - - if(recipe == null) return; - - Tile tile = world.tile(packet.x, packet.y); - if(tile.synthetic() && admins.isValidateReplace() && !admins.validateBreak(admins.getTrace(Net.getConnection(id).address).uuid, Net.getConnection(id).address)){ - if(Timers.get("break-message-" + id, 120)){ - sendMessageTo(id, "[scarlet]Anti-grief: you are replacing blocks too quickly. wait until replacing again."); - } - return; - } - - state.inventory.removeItems(recipe.requirements); - - Placement.placeBlock(packet.x, packet.y, block, packet.rotation, true, false); - - admins.logEdit(packet.x, packet.y, connections.get(id), block, packet.rotation, EditLog.EditAction.PLACE); - admins.getTrace(Net.getConnection(id).address).lastBlockPlaced = block; - admins.getTrace(Net.getConnection(id).address).totalBlocksPlaced ++; - admins.getInfo(admins.getTrace(Net.getConnection(id).address).uuid).totalBlockPlaced ++; - - Net.send(packet, SendMode.tcp); - }); - - Net.handleServer(BreakPacket.class, (id, packet) -> { - packet.playerid = connections.get(id).id; - - if(!Placement.validBreak(packet.x, packet.y)) return; - - Tile tile = world.tile(packet.x, packet.y); - - if(tile.synthetic() && !admins.validateBreak(admins.getTrace(Net.getConnection(id).address).uuid, Net.getConnection(id).address)){ - if(Timers.get("break-message-" + id, 120)){ - sendMessageTo(id, "[scarlet]Anti-grief: you are breaking blocks too quickly. wait until breaking again."); - } - return; - } - - Block block = Placement.breakBlock(packet.x, packet.y, true, false); - - if(block != null) { - admins.logEdit(packet.x, packet.y, connections.get(id), block, tile.getRotation(), EditLog.EditAction.BREAK); - admins.getTrace(Net.getConnection(id).address).lastBlockBroken = block; - admins.getTrace(Net.getConnection(id).address).totalBlocksBroken++; - admins.getInfo(admins.getTrace(Net.getConnection(id).address).uuid).totalBlocksBroken ++; - if (block.update || block.destructible) - admins.getTrace(Net.getConnection(id).address).structureBlocksBroken++; - } - - Net.send(packet, SendMode.tcp); - }); - - Net.handleServer(ChatPacket.class, (id, packet) -> { - if(!Timers.get("chatFlood" + id, 20)){ - ChatPacket warn = new ChatPacket(); - warn.text = "[scarlet]You are sending messages too quickly."; - Net.sendTo(id, warn, SendMode.tcp); - return; - } - Player player = connections.get(id); - packet.name = player.name; - packet.id = player.id; - Net.send(packet, SendMode.tcp); - }); - - Net.handleServer(UpgradePacket.class, (id, packet) -> { - Player player = connections.get(id); - - Weapon weapon = (Weapon) Upgrade.getByID(packet.id); - String uuid = admins.getTrace(Net.getConnection(id).address).uuid; - - if(!state.inventory.hasItems(UpgradeRecipes.get(weapon))){ - return; - } - - if (!weapons.containsKey(uuid)) weapons.put(uuid, new ByteArray()); - - if (!weapons.get(uuid).contains(weapon.id)){ - weapons.get(uuid).add(weapon.id); - }else{ - return; - } - - state.inventory.removeItems(UpgradeRecipes.get(weapon)); - Net.sendTo(id, packet, SendMode.tcp); - }); - - Net.handleServer(WeaponSwitchPacket.class, (id, packet) -> { - TraceInfo info = admins.getTrace(Net.getConnection(id).address); - - packet.playerid = connections.get(id).id; - Net.sendExcept(id, packet, SendMode.tcp); - }); - - Net.handleServer(BlockTapPacket.class, (id, packet) -> { - Net.sendExcept(id, packet, SendMode.tcp); - }); - - Net.handleServer(BlockConfigPacket.class, (id, packet) -> { - Net.sendExcept(id, packet, SendMode.tcp); - }); - - Net.handleServer(EntityRequestPacket.class, (cid, packet) -> { - - int id = packet.id; - int dest = cid; - EntityGroup group = Entities.getGroup(packet.group); - if(group.getByID(id) != null){ - EntitySpawnPacket p = new EntitySpawnPacket(); - p.entity = (SyncEntity)group.getByID(id); - p.group = group; - Net.sendTo(dest, p, SendMode.tcp); - } - }); - - Net.handleServer(PlayerDeathPacket.class, (id, packet) -> { - packet.id = connections.get(id).id; - Net.sendExcept(id, packet, SendMode.tcp); - }); - - Net.handleServer(AdministerRequestPacket.class, (id, packet) -> { - Player player = connections.get(id); - - if(!player.isAdmin){ - Log.err("ACCESS DENIED: Player {0} / {1} attempted to perform admin action without proper security access.", - player.name, Net.getConnection(player.clientid).address); - return; - } - - Player other = playerGroup.getByID(packet.id); - - if(other == null || other.isAdmin){ - Log.err("{0} attempted to perform admin action on nonexistant or admin player.", player.name); - return; - } - - String ip = Net.getConnection(other.clientid).address; - - if(packet.action == AdminAction.ban){ - admins.banPlayerIP(ip); - kick(other.clientid, KickReason.banned); - Log.info("&lc{0} has banned {1}.", player.name, other.name); - }else if(packet.action == AdminAction.kick){ - kick(other.clientid, KickReason.kick); - Log.info("&lc{0} has kicked {1}.", player.name, other.name); - }else if(packet.action == AdminAction.trace){ - TracePacket trace = new TracePacket(); - trace.info = admins.getTrace(ip); - Net.sendTo(id, trace, SendMode.tcp); - Log.info("&lc{0} has requested trace info of {1}.", player.name, other.name); - } - }); - - Net.handleServer(BlockLogRequestPacket.class, (id, packet) -> { - packet.editlogs = admins.getEditLogs().get(packet.x + packet.y * world.width(), new Array<>()); - Net.sendTo(id, packet, SendMode.udp); - }); - - Net.handleServer(RollbackRequestPacket.class, (id, packet) -> { - Player player = connections.get(id); - - if(!player.isAdmin){ - Log.err("ACCESS DENIED: Player {0} / {1} attempted to perform a rollback without proper security access.", - player.name, Net.getConnection(player.clientid).address); - return; - } - - admins.rollbackWorld(packet.rollbackTimes); - Log.info("&lc{0} has rolled back the world {1} times.", player.name, packet.rollbackTimes); + if(player == null) return; + RemoteReadServer.readPacket(packet.writeBuffer, packet.type, player); }); } + public Team assignTeam(Iterable players){ + //find team with minimum amount of players and auto-assign player to that. + return Structs.findMin(Team.all, team -> { + if(state.teams.isActive(team) && !state.teams.get(team).cores.isEmpty()){ + int count = 0; + for(Player other : players){ + if(other.getTeam() == team){ + count++; + } + } + return count; + } + return Integer.MAX_VALUE; + }); + } + + public void sendWorldData(Player player, int clientID){ + ByteArrayOutputStream stream = new ByteArrayOutputStream(); + DeflaterOutputStream def = new FastDeflaterOutputStream(stream); + NetworkIO.writeWorld(player, def); + WorldStream data = new WorldStream(); + data.stream = new ByteArrayInputStream(stream.toByteArray()); + Net.sendStream(clientID, data); + + Log.debug("Packed {0} compressed bytes of world data.", stream.size()); + } + + public static void onDisconnect(Player player){ + //singleplayer multiplayer wierdness + if(player.con == null){ + player.remove(); + return; + } + + if(player.con.hasConnected){ + Call.sendMessage("[accent]" + player.name + "[accent] has disconnected."); + Call.onPlayerDisconnect(player.id); + } + player.remove(); + netServer.connections.remove(player.con.id); + Log.info("&lm[{1}] &lc{0} has disconnected.", player.name, player.uuid); + } + + private static float compound(float speed, float drag){ + float total = 0f; + for(int i = 0; i < 50; i++){ + total *= (1f - drag); + total += speed; + } + return total; + } + + @Remote(targets = Loc.client, unreliable = true) + public static void onClientShapshot( + Player player, + int snapshotID, + float x, float y, + float pointerX, float pointerY, + float rotation, float baseRotation, + float xVelocity, float yVelocity, + Tile mining, + boolean boosting, boolean shooting, boolean chatting, + BuildRequest[] requests, + float viewX, float viewY, float viewWidth, float viewHeight + ){ + NetConnection connection = player.con; + if(connection == null || snapshotID < connection.lastRecievedClientSnapshot) return; + + boolean verifyPosition = !player.isDead() && netServer.admins.getStrict() && headless; + + if(connection.lastRecievedClientTime == 0) connection.lastRecievedClientTime = Time.millis() - 16; + + connection.viewX = viewX; + connection.viewY = viewY; + connection.viewWidth = viewWidth; + connection.viewHeight = viewHeight; + + long elapsed = Time.timeSinceMillis(connection.lastRecievedClientTime); + + float maxSpeed = boosting && !player.mech.flying ? player.mech.boostSpeed : player.mech.speed; + float maxMove = elapsed / 1000f * 60f * Math.min(compound(maxSpeed, player.mech.drag) * 1.25f, player.mech.maxSpeed * 1.1f); + + player.pointerX = pointerX; + player.pointerY = pointerY; + player.setMineTile(mining); + player.isTyping = chatting; + player.isBoosting = boosting; + player.isShooting = shooting; + player.buildQueue().clear(); + for(BuildRequest req : requests){ + Tile tile = world.tile(req.x, req.y); + if(tile == null) continue; + //auto-skip done requests + if(req.breaking && tile.block() == Blocks.air){ + continue; + }else if(!req.breaking && tile.block() == req.block && (!req.block.rotate || tile.rotation() == req.rotation)){ + continue; + } + player.buildQueue().addLast(req); + } + + vector.set(x - player.getInterpolator().target.x, y - player.getInterpolator().target.y); + //vector.limit(maxMove); + + float prevx = player.x, prevy = player.y; + player.set(player.getInterpolator().target.x, player.getInterpolator().target.y); + if(!player.mech.flying && player.boostHeat < 0.01f){ + player.move(vector.x, vector.y); + }else{ + player.x += vector.x; + player.y += vector.y; + } + float newx = player.x, newy = player.y; + + if(!verifyPosition){ + player.x = prevx; + player.y = prevy; + newx = x; + newy = y; + }else if(Mathf.dst(x, y, newx, newy) > correctDist){ + Call.onPositionSet(player.con.id, newx, newy); //teleport and correct position when necessary + } + + //reset player to previous synced position so it gets interpolated + player.x = prevx; + player.y = prevy; + + //set interpolator target to *new* position so it moves toward it + player.getInterpolator().read(player.x, player.y, newx, newy, rotation, baseRotation); + player.velocity().set(xVelocity, yVelocity); //only for visual calculation purposes, doesn't actually update the player + + connection.lastRecievedClientSnapshot = snapshotID; + connection.lastRecievedClientTime = Time.millis(); + } + + @Remote(targets = Loc.client, called = Loc.server) + public static void onAdminRequest(Player player, Player other, AdminAction action){ + + if(!player.isAdmin){ + Log.warn("ACCESS DENIED: Player {0} / {1} attempted to perform admin action without proper security access.", + player.name, player.con.address); + return; + } + + if(other == null || ((other.isAdmin && !player.isLocal) && other != player)){ + Log.warn("{0} attempted to perform admin action on nonexistant or admin player.", player.name); + return; + } + + if(action == AdminAction.wave){ + //no verification is done, so admins can hypothetically spam waves + //not a real issue, because server owners may want to do just that + state.wavetime = 0f; + }else if(action == AdminAction.ban){ + netServer.admins.banPlayerIP(other.con.address); + netServer.kick(other.con.id, KickReason.banned); + Log.info("&lc{0} has banned {1}.", player.name, other.name); + }else if(action == AdminAction.kick){ + netServer.kick(other.con.id, KickReason.kick); + Log.info("&lc{0} has kicked {1}.", player.name, other.name); + }else if(action == AdminAction.trace){ + TraceInfo info = new TraceInfo(other.con.address, other.uuid, other.con.modclient, other.con.mobile); + if(player.con != null){ + Call.onTraceInfo(player.con.id, other, info); + }else{ + NetClient.onTraceInfo(other, info); + } + Log.info("&lc{0} has requested trace info of {1}.", player.name, other.name); + } + } + + @Remote(targets = Loc.client) + public static void connectConfirm(Player player){ + if(player.con == null || player.con.hasConnected) return; + + player.add(); + player.con.hasConnected = true; + Call.sendMessage("[accent]" + player.name + "[accent] has connected."); + Log.info("&lm[{1}] &y{0} has connected. ", player.name, player.uuid); + } + + public boolean isWaitingForPlayers(){ + if(state.rules.pvp){ + int used = 0; + for(Team t : Team.all){ + if(playerGroup.count(p -> p.getTeam() == t) > 0){ + used++; + } + } + return used < 2; + } + return false; + } + public void update(){ + if(!headless && !closing && Net.server() && state.is(State.menu)){ closing = true; - reset(); - ui.loadfrag.show("$text.server.closing"); - Timers.runTask(5f, () -> { + ui.loadfrag.show("$server.closing"); + Time.runTask(5f, () -> { Net.closeServer(); ui.loadfrag.hide(); closing = false; @@ -386,9 +405,10 @@ public class NetServer extends Module{ } } - public void reset(){ - weapons.clear(); - admins.clearTraces(); + public void kickAll(KickReason reason){ + for(NetConnection con : Net.getConnections()){ + kick(con.id, reason); + } } public void kick(int connection, KickReason reason){ @@ -397,107 +417,159 @@ public class NetServer extends Module{ Log.err("Cannot kick unknown player!"); return; }else{ - Log.info("Kicking connection #{0} / IP: {1}. Reason: {2}", connection, con.address, reason); + Log.info("Kicking connection #{0} / IP: {1}. Reason: {2}", connection, con.address, reason.name()); } - if((reason == KickReason.kick || reason == KickReason.banned) && admins.getTrace(con.address).uuid != null){ - PlayerInfo info = admins.getInfo(admins.getTrace(con.address).uuid); - info.timesKicked ++; - info.lastKicked = TimeUtils.millis(); + Player player = connections.get(con.id); + + if(player != null && (reason == KickReason.kick || reason == KickReason.banned) && player.uuid != null){ + PlayerInfo info = admins.getInfo(player.uuid); + info.timesKicked++; + info.lastKicked = Time.millis(); } - KickPacket p = new KickPacket(); - p.reason = reason; + Call.onKick(connection, reason); - con.send(p, SendMode.tcp); - Timers.runTask(2f, con::close); + Time.runTask(2f, con::close); admins.save(); } - void sendMessageTo(int id, String message){ - ChatPacket packet = new ChatPacket(); - packet.text = message; - Net.sendTo(id, packet, SendMode.tcp); + public void writeSnapshot(Player player) throws IOException{ + syncStream.reset(); + ObjectSet cores = state.teams.get(player.getTeam()).cores; + + dataStream.writeByte(cores.size); + + for(Tile tile : cores){ + dataStream.writeInt(tile.pos()); + tile.entity.items.write(dataStream); + } + + dataStream.close(); + byte[] stateBytes = syncStream.toByteArray(); + + //write basic state data. + Call.onStateSnapshot(player.con.id, state.wavetime, state.wave, state.enemies(), (short)stateBytes.length, Net.compressSnapshot(stateBytes)); + + viewport.setSize(player.con.viewWidth, player.con.viewHeight).setCenter(player.con.viewX, player.con.viewY); + + //check for syncable groups + for(EntityGroup group : Entities.getAllGroups()){ + if(group.isEmpty() || !(group.all().get(0) instanceof SyncTrait)) continue; + + //make sure mapping is enabled for this group + if(!group.mappingEnabled()){ + throw new RuntimeException("Entity group '" + group.getType() + "' contains SyncTrait entities, yet mapping is not enabled. In order for syncing to work, you must enable mapping for this group."); + } + + syncStream.reset(); + + int sent = 0; + + for(Entity entity : group.all()){ + SyncTrait sync = (SyncTrait)entity; + if(!sync.isSyncing()) continue; + + //write all entities now + dataStream.writeInt(entity.getID()); //write id + dataStream.writeByte(sync.getTypeID()); //write type ID + sync.write(dataStream); //write entity + + sent++; + + if(syncStream.size() > maxSnapshotSize){ + dataStream.close(); + byte[] syncBytes = syncStream.toByteArray(); + Call.onEntitySnapshot(player.con.id, (byte)group.getID(), (short)sent, (short)syncBytes.length, Net.compressSnapshot(syncBytes)); + sent = 0; + syncStream.reset(); + } + } + + if(sent > 0){ + dataStream.close(); + + byte[] syncBytes = syncStream.toByteArray(); + Call.onEntitySnapshot(player.con.id, (byte)group.getID(), (short)sent, (short)syncBytes.length, Net.compressSnapshot(syncBytes)); + } + } + } + + String fixName(String name){ + name = name.trim(); + if(name.equals("[") || name.equals("]")){ + return ""; + } + + for(int i = 0; i < name.length(); i++){ + if(name.charAt(i) == '[' && i != name.length() - 1 && name.charAt(i + 1) != '[' && (i == 0 || name.charAt(i - 1) != '[')){ + String prev = name.substring(0, i); + String next = name.substring(i); + String result = checkColor(next); + + name = prev + result; + } + } + + StringBuilder result = new StringBuilder(); + int curChar = 0; + while(curChar < name.length() && result.toString().getBytes().length < maxNameLength){ + result.append(name.charAt(curChar++)); + } + return result.toString(); + } + + String checkColor(String str){ + + for(int i = 1; i < str.length(); i++){ + if(str.charAt(i) == ']'){ + String color = str.substring(1, i); + + if(Colors.get(color.toUpperCase()) != null || Colors.get(color.toLowerCase()) != null){ + Color result = (Colors.get(color.toLowerCase()) == null ? Colors.get(color.toUpperCase()) : Colors.get(color.toLowerCase())); + if(result.a <= 0.8f){ + return str.substring(i + 1); + } + }else{ + try{ + Color result = Color.valueOf(color); + if(result.a <= 0.8f){ + return str.substring(i + 1); + } + }catch(Exception e){ + return str; + } + } + } + } + return str; } void sync(){ - if(timer.get(timerEntitySync, serverSyncTime)){ - //scan through all groups with syncable entities - for(EntityGroup group : Entities.getAllGroups()) { - if(group.size() == 0 || !(group.all().iterator().next() instanceof SyncEntity)) continue; + try{ - //get write size for one entity (adding 4, as you need to write the ID as well) - int writesize = SyncEntity.getWriteSize((Class)group.getType()) + 4; - //amount of entities - int amount = group.size(); - //maximum amount of entities per packet - int maxsize = 64; + //iterate through each player + for(int i = 0; i < playerGroup.size(); i++){ + Player player = playerGroup.all().get(i); + if(player.isLocal) continue; - //current buffer you're writing to - ByteBuffer current = null; - //number of entities written to this packet/buffer - int written = 0; + NetConnection connection = player.con; - //for all the entities... - for (int i = 0; i < amount; i++) { - //if the buffer is null, create a new one - if(current == null){ - //calculate amount of entities to go into this packet - int csize = Math.min(amount-i, maxsize); - //create a byte array to write to - byte[] bytes = new byte[csize*writesize + 1 + 8]; - //wrap it for easy writing - current = ByteBuffer.wrap(bytes); - current.putLong(TimeUtils.millis()); - //write the group ID so the client knows which group this is - current.put((byte)group.getID()); - } - - SyncEntity entity = (SyncEntity) group.all().get(i); - - //write ID to the buffer - current.putInt(entity.id); - - int previous = current.position(); - //write extra data to the buffer - entity.write(current); - - written ++; - - //if the packet is too big now... - if(written >= maxsize){ - //send the packet. - SyncPacket packet = new SyncPacket(); - packet.data = current.array(); - Net.send(packet, SendMode.udp); - - //reset data, send the next packet - current = null; - written = 0; - } + if(connection == null || !connection.isConnected() || !connections.containsKey(connection.id)){ + //player disconnected, call d/c event + onDisconnect(player); + return; } - //make sure to send incomplete packets too - if(current != null){ - SyncPacket packet = new SyncPacket(); - packet.data = current.array(); - Net.send(packet, SendMode.udp); - } + if(!player.timer.get(Player.timerSync, serverSyncTime) || !connection.hasConnected) continue; + + writeSnapshot(player); } - } - if(timer.get(timerStateSync, itemSyncTime)){ - StateSyncPacket packet = new StateSyncPacket(); - packet.items = state.inventory.getItems(); - packet.countdown = state.wavetime; - packet.enemies = state.enemies; - packet.wave = state.wave; - packet.time = Timers.time(); - packet.timestamp = TimeUtils.millis(); - - Net.send(packet, SendMode.udp); + }catch(IOException e){ + e.printStackTrace(); } } } diff --git a/core/src/io/anuke/mindustry/core/Platform.java b/core/src/io/anuke/mindustry/core/Platform.java index 1c3ab23003..85026c9335 100644 --- a/core/src/io/anuke/mindustry/core/Platform.java +++ b/core/src/io/anuke/mindustry/core/Platform.java @@ -1,89 +1,91 @@ package io.anuke.mindustry.core; -import com.badlogic.gdx.files.FileHandle; -import com.badlogic.gdx.utils.Base64Coder; -import io.anuke.mindustry.core.ThreadHandler.ThreadProvider; -import io.anuke.ucore.core.Settings; -import io.anuke.ucore.entities.Entity; -import io.anuke.ucore.entities.EntityGroup; -import io.anuke.ucore.function.Consumer; -import io.anuke.ucore.scene.ui.TextField; +import io.anuke.arc.Core; +import io.anuke.arc.Input.TextInput; +import io.anuke.arc.files.FileHandle; +import io.anuke.arc.function.Consumer; +import io.anuke.arc.function.Predicate; +import io.anuke.arc.math.RandomXS128; +import io.anuke.arc.scene.ui.TextField; +import io.anuke.arc.util.serialization.Base64Coder; -import java.util.Date; -import java.util.Locale; -import java.util.Random; +import static io.anuke.mindustry.Vars.mobile; -public abstract class Platform { - /**Each separate game platform should set this instance to their own implementation.*/ - public static Platform instance = new Platform() {}; +public abstract class Platform{ + /** Each separate game platform should set this instance to their own implementation. */ + public static Platform instance = new Platform(){ + }; - /**Format the date using the default date formatter.*/ - public String format(Date date){return "invalid";} - /**Format a number by adding in commas or periods where needed.*/ - public String format(int number){return "invalid";} - /**Show a native error dialog.*/ - public void showError(String text){} - /**Add a text input dialog that should show up after the field is tapped.*/ - public void addDialog(TextField field){ - addDialog(field, 16); - } - /**See addDialog().*/ - public void addDialog(TextField field, int maxLength){} - /**Update discord RPC.*/ - public void updateRPC(){} - /**Called when the game is exited.*/ - public void onGameExit(){} - /**Open donation dialog. Currently android only.*/ - public void openDonations(){} - /**Whether discord RPC is supported.*/ - public boolean hasDiscord(){return true;} - /**Request Android permissions for writing files.*/ - public void requestWritePerms(){} - /**Return the localized name for the locale. This is basically a workaround for GWT not supporting getName().*/ - public String getLocaleName(Locale locale){ - return locale.toString(); - } - /**Whether joining games is supported.*/ - public boolean canJoinGame(){ - return true; - } - /**Whether debug mode is enabled.*/ - public boolean isDebug(){return false;} - /**Must be 8 bytes in length.*/ - public byte[] getUUID(){ - String uuid = Settings.getString("uuid", ""); - if(uuid.isEmpty()){ - byte[] result = new byte[8]; - new Random().nextBytes(result); - uuid = new String(Base64Coder.encode(result)); - Settings.putString("uuid", uuid); - Settings.save(); - return result; - } - return Base64Coder.decode(uuid); - } - /**Only used for iOS or android: open the share menu for a map or save.*/ - public void shareFile(FileHandle file){} + /** Add a text input dialog that should show up after the field is tapped. */ + public void addDialog(TextField field){ + addDialog(field, 16); + } - /**Show a file chooser. Desktop only. - * + /** See addDialog(). */ + public void addDialog(TextField field, int maxLength){ + if(!mobile) return; //this is mobile only, desktop doesn't need dialogs + + field.tapped(() -> { + TextInput input = new TextInput(); + input.text = field.getText(); + input.maxLength = maxLength; + input.accepted = text -> { + field.clearText(); + field.appendText(text); + field.change(); + Core.input.setOnscreenKeyboardVisible(false); + }; + Core.input.getTextInput(input); + }); + } + + /** Update discord RPC. */ + public void updateRPC(){ + } + + /** Whether donating is supported. */ + public boolean canDonate(){ + return false; + } + + /** Must be a base64 string 8 bytes in length. */ + public String getUUID(){ + String uuid = Core.settings.getString("uuid", ""); + if(uuid.isEmpty()){ + byte[] result = new byte[8]; + new RandomXS128().nextBytes(result); + uuid = new String(Base64Coder.encode(result)); + Core.settings.put("uuid", uuid); + Core.settings.save(); + return uuid; + } + return uuid; + } + + /** Only used for iOS or android: open the share menu for a map or save. */ + public void shareFile(FileHandle file){ + } + + /** + * Show a file chooser. * @param text File chooser title text - * @param content Type of files to be loaded + * @param content Description of the type of files to be loaded * @param cons Selection listener - * @param open Whether to open or save files. - * @param filetype File extensions to filter. + * @param open Whether to open or save files + * @param filetype File extension to filter */ - public void showFileChooser(String text, String content, Consumer cons, boolean open, String filetype){} - /**Use the default thread provider from the kryonet module for this.*/ - public ThreadProvider getThreadProvider(){ - return new ThreadProvider() { - @Override public boolean isOnThread() {return true;} - @Override public void sleep(long ms) {} - @Override public void start(Runnable run) {} - @Override public void stop() {} - @Override public void notify(Object object) {} - @Override public void wait(Object object) {} - @Override public void switchContainer(EntityGroup group) {} - }; - } -} + public void showFileChooser(String text, String content, Consumer cons, boolean open, Predicate filetype){ + } + + /** Hide the app. Android only. */ + public void hide(){ + } + + /** Forces the app into landscape mode. Currently Android only. */ + public void beginForceLandscape(){ + } + + /** Stops forcing the app into landscape orientation. Currently Android only. */ + public void endForceLandscape(){ + } +} \ No newline at end of file diff --git a/core/src/io/anuke/mindustry/core/Renderer.java b/core/src/io/anuke/mindustry/core/Renderer.java index 214b5785c0..a6a5aa1363 100644 --- a/core/src/io/anuke/mindustry/core/Renderer.java +++ b/core/src/io/anuke/mindustry/core/Renderer.java @@ -1,593 +1,373 @@ package io.anuke.mindustry.core; -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.graphics.Colors; -import com.badlogic.gdx.graphics.Texture; -import com.badlogic.gdx.graphics.Texture.TextureWrap; -import com.badlogic.gdx.graphics.g2d.GlyphLayout; -import com.badlogic.gdx.math.MathUtils; -import com.badlogic.gdx.math.Rectangle; -import com.badlogic.gdx.math.Vector2; -import com.badlogic.gdx.utils.Array; -import com.badlogic.gdx.utils.FloatArray; -import com.badlogic.gdx.utils.Pools; +import io.anuke.arc.ApplicationListener; +import io.anuke.arc.Core; +import io.anuke.arc.files.FileHandle; +import io.anuke.arc.function.Consumer; +import io.anuke.arc.function.Predicate; +import io.anuke.arc.graphics.*; +import io.anuke.arc.graphics.g2d.*; +import io.anuke.arc.graphics.glutils.FrameBuffer; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.Rectangle; +import io.anuke.arc.math.geom.Vector2; +import io.anuke.arc.util.*; +import io.anuke.arc.util.pooling.Pools; +import io.anuke.mindustry.content.Fx; import io.anuke.mindustry.core.GameState.State; -import io.anuke.mindustry.entities.Player; -import io.anuke.mindustry.entities.SyncEntity; -import io.anuke.mindustry.entities.enemies.Enemy; -import io.anuke.mindustry.game.SpawnPoint; -import io.anuke.mindustry.graphics.BlockRenderer; -import io.anuke.mindustry.graphics.Shaders; -import io.anuke.mindustry.input.InputHandler; -import io.anuke.mindustry.input.PlaceMode; -import io.anuke.mindustry.ui.fragments.ToolFragment; -import io.anuke.mindustry.world.BlockBar; -import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.blocks.Blocks; -import io.anuke.mindustry.world.blocks.ProductionBlocks; -import io.anuke.ucore.core.*; -import io.anuke.ucore.entities.EffectEntity; -import io.anuke.ucore.entities.Entities; -import io.anuke.ucore.function.Callable; -import io.anuke.ucore.graphics.*; -import io.anuke.ucore.modules.RendererModule; -import io.anuke.ucore.scene.ui.layout.Unit; -import io.anuke.ucore.scene.utils.Cursors; -import io.anuke.ucore.util.Angles; -import io.anuke.ucore.util.Mathf; -import io.anuke.ucore.util.Tmp; +import io.anuke.mindustry.entities.*; +import io.anuke.mindustry.entities.effect.GroundEffectEntity; +import io.anuke.mindustry.entities.effect.GroundEffectEntity.GroundEffect; +import io.anuke.mindustry.entities.impl.EffectEntity; +import io.anuke.mindustry.entities.traits.*; +import io.anuke.mindustry.entities.type.*; +import io.anuke.mindustry.game.Team; +import io.anuke.mindustry.graphics.*; +import io.anuke.mindustry.world.blocks.defense.ForceProjector.ShieldEntity; +import static io.anuke.arc.Core.*; import static io.anuke.mindustry.Vars.*; -import static io.anuke.ucore.core.Core.batch; -import static io.anuke.ucore.core.Core.camera; -public class Renderer extends RendererModule{ - private final static float shieldHitDuration = 18f; - - public Surface shadowSurface, shieldSurface, indicatorSurface; - - private int targetscale = baseCameraScale; - private Texture background = new Texture("sprites/background.png"); - private FloatArray shieldHits = new FloatArray(); - private Array shieldDraws = new Array<>(); - private Rectangle rect = new Rectangle(), rect2 = new Rectangle(); - private BlockRenderer blocks = new BlockRenderer(); +public class Renderer implements ApplicationListener{ + public final BlockRenderer blocks = new BlockRenderer(); + public final MinimapRenderer minimap = new MinimapRenderer(); + public final OverlayRenderer overlays = new OverlayRenderer(); + public final Pixelator pixelator = new Pixelator(); - public Renderer() { - Lines.setCircleVertices(14); + public FrameBuffer shieldBuffer = new FrameBuffer(2, 2); + private Color clearColor; + private float targetscale = io.anuke.arc.scene.ui.layout.Unit.dp.scl(4); + private float camerascale = targetscale; + private Rectangle rect = new Rectangle(), rect2 = new Rectangle(); + private float shakeIntensity, shaketime; - Core.cameraScale = baseCameraScale; - Effects.setEffectProvider((name, color, x, y, rotation) -> { - if(Settings.getBool("effects")){ - Rectangle view = rect.setSize(camera.viewportWidth, camera.viewportHeight) - .setCenter(camera.position.x, camera.position.y); - Rectangle pos = rect2.setSize(name.size).setCenter(x, y); - if(view.overlaps(pos)){ - new EffectEntity(name, color, rotation).set(x, y).add(effectGroup); - } - } - }); + public Renderer(){ + batch = new SpriteBatch(4096); + camera = new Camera(); + Lines.setCircleVertices(20); + Shaders.init(); - Cursors.cursorScaling = 3; - Cursors.outlineColor = Color.valueOf("444444"); - Cursors.arrow = Cursors.loadCursor("cursor"); - Cursors.hand = Cursors.loadCursor("hand"); - Cursors.ibeam = Cursors.loadCursor("ibar"); + Effects.setScreenShakeProvider((intensity, duration) -> { + shakeIntensity = Math.max(intensity, shakeIntensity); + shaketime = Math.max(shaketime, duration); + }); - clearColor = Hue.lightness(0.4f); - clearColor.a = 1f; + Effects.setEffectProvider((effect, color, x, y, rotation, data) -> { + if(effect == Fx.none) return; + if(Core.settings.getBool("effects")){ + Rectangle view = camera.bounds(rect); + Rectangle pos = rect2.setSize(effect.size).setCenter(x, y); - background.setWrap(TextureWrap.Repeat, TextureWrap.Repeat); - } + if(view.overlaps(pos)){ - @Override - public void init(){ - pixelate = Settings.getBool("pixelate"); - int scale = Settings.getBool("pixelate") ? Core.cameraScale : 1; - - shadowSurface = Graphics.createSurface(scale); - shieldSurface = Graphics.createSurface(scale); - indicatorSurface = Graphics.createSurface(scale); - pixelSurface = Graphics.createSurface(scale); - } + if(!(effect instanceof GroundEffect)){ + EffectEntity entity = Pools.obtain(EffectEntity.class, EffectEntity::new); + entity.effect = effect; + entity.color.set(color); + entity.rotation = rotation; + entity.data = data; + entity.id++; + entity.set(x, y); + if(data instanceof Entity){ + entity.setParent((Entity)data); + } + effectGroup.add(entity); + }else{ + GroundEffectEntity entity = Pools.obtain(GroundEffectEntity.class, GroundEffectEntity::new); + entity.effect = effect; + entity.color.set(color); + entity.rotation = rotation; + entity.id++; + entity.data = data; + entity.set(x, y); + if(data instanceof Entity){ + entity.setParent((Entity)data); + } + groundEffectGroup.add(entity); + } + } + } + }); - public void setPixelate(boolean pixelate){ - this.pixelate = pixelate; - } - - @Override - public void update(){ - - if(Core.cameraScale != targetscale){ - float targetzoom = (float) Core.cameraScale / targetscale; - camera.zoom = Mathf.lerpDelta(camera.zoom, targetzoom, 0.2f); - - if(Mathf.in(camera.zoom, targetzoom, 0.005f)){ - camera.zoom = 1f; - Graphics.setCameraScale(targetscale); - control.input().resetCursor(); - } - }else{ - camera.zoom = Mathf.lerpDelta(camera.zoom, 1f, 0.2f); - } - - if(state.is(State.menu)){ - clearScreen(); - }else{ - boolean smoothcam = Settings.getBool("smoothcam"); - - if(world.getCore() == null || world.getCore().block() == ProductionBlocks.core){ - if(!smoothcam){ - setCamera(player.x, player.y); - }else{ - smoothCamera(player.x, player.y, mobile ? 0.3f : 0.14f); - } - }else{ - smoothCamera(world.getCore().worldx(), world.getCore().worldy(), 0.4f); - } - - if(Settings.getBool("pixelate")) - limitCamera(4f, player.x, player.y); - - float prex = camera.position.x, prey = camera.position.y; - updateShake(0.75f); - float prevx = camera.position.x, prevy = camera.position.y; - clampCamera(-tilesize / 2f, -tilesize / 2f + 1, world.width() * tilesize - tilesize / 2f, world.height() * tilesize - tilesize / 2f); - - float deltax = camera.position.x - prex, deltay = camera.position.y - prey; - - if(mobile){ - player.x += camera.position.x - prevx; - player.y += camera.position.y - prevy; - } - - float lastx = camera.position.x, lasty = camera.position.y; - - if(snapCamera && smoothcam && Settings.getBool("pixelate")){ - camera.position.set((int) camera.position.x, (int) camera.position.y, 0); - } - - if(Gdx.graphics.getHeight() / Core.cameraScale % 2 == 1){ - camera.position.add(0, -0.5f, 0); - } - - if(Gdx.graphics.getWidth() / Core.cameraScale % 2 == 1){ - camera.position.add(-0.5f, 0, 0); - } - - draw(); - - camera.position.set(lastx - deltax, lasty - deltay, 0); - - if(debug && !ui.chatfrag.chatOpen()) - record(); //this only does something if GdxGifRecorder is on the class path, which it usually isn't - } - } - - @Override - public void draw(){ - camera.update(); - - clearScreen(clearColor); - - batch.setProjectionMatrix(camera.combined); - - if(pixelate) - Graphics.surface(pixelSurface, false); - else - batch.begin(); - - //clears shield surface - Graphics.surface(shieldSurface); - Graphics.surface(); - - drawPadding(); - - blocks.drawFloor(); - blocks.processBlocks(); - blocks.drawBlocks(false); - - Graphics.shader(Shaders.outline, false); - Entities.draw(enemyGroup); - Entities.draw(playerGroup, p -> !p.isAndroid); - Graphics.shader(); - - Entities.draw(Entities.defaultGroup()); - - blocks.drawBlocks(true); - - Graphics.shader(Shaders.outline, false); - Entities.draw(playerGroup, p -> p.isAndroid); - Graphics.shader(); - - Entities.draw(bulletGroup); - Entities.draw(effectGroup); - - drawShield(); - - drawOverlay(); - - if(Settings.getBool("indicators") && showUI){ - drawEnemyMarkers(); - } - - if(pixelate) - Graphics.flushSurface(); - - drawPlayerNames(); - - batch.end(); - } - - @Override - public void resize(int width, int height){ - super.resize(width, height); - control.input().resetCursor(); - camera.position.set(player.x, player.y, 0); - } - - @Override - public void dispose() { - background.dispose(); - } - - public void clearTiles(){ - blocks.clearTiles(); - } - - void drawPadding(){ - float vw = world.width() * tilesize; - float cw = camera.viewportWidth * camera.zoom; - float ch = camera.viewportHeight * camera.zoom; - if(vw < cw){ - batch.draw(background, - camera.position.x + vw/2, - Mathf.round(camera.position.y - ch/2, tilesize), - (cw - vw) /2, - ch + tilesize, - 0, 0, - ((cw - vw) / 2 / tilesize), -ch / tilesize + 1); - - batch.draw(background, - camera.position.x - vw/2, - Mathf.round(camera.position.y - ch/2, tilesize), - -(cw - vw) /2, - ch + tilesize, - 0, 0, - -((cw - vw) / 2 / tilesize), -ch / tilesize + 1); - } - } - - void drawPlayerNames(){ - GlyphLayout layout = Pools.obtain(GlyphLayout.class); - - Draw.tscl(0.25f/2); - for(Player player : playerGroup.all()){ - if(!player.isLocal && !player.isDead()){ - layout.setText(Core.font, player.name); - Draw.color(0f, 0f, 0f, 0.3f); - Draw.rect("blank", player.getDrawPosition().x, player.getDrawPosition().y + 8 - layout.height/2, layout.width + 2, layout.height + 2); - Draw.color(); - Draw.tcolor(player.getColor()); - Draw.text(player.name, player.getDrawPosition().x, player.getDrawPosition().y + 8); - - if(player.isAdmin){ - Draw.color(player.getColor()); - float s = 3f; - Draw.rect("icon-admin-small", player.getDrawPosition().x + layout.width/2f + 2 + 1, player.getDrawPosition().y + 7f, s, s); - } - Draw.reset(); - } - } - Pools.free(layout); - Draw.tscl(fontscale); + clearColor = new Color(0f, 0f, 0f, 1f); } - void drawEnemyMarkers(){ - Graphics.surface(indicatorSurface); - Draw.color(Color.RED); + @Override + public void update(){ + //TODO hack, find source of this bug + Color.WHITE.set(1f, 1f, 1f, 1f); - for(Enemy enemy : enemyGroup.all()) { + camerascale = Mathf.lerpDelta(camerascale, targetscale, 0.1f); + camera.width = graphics.getWidth() / camerascale; + camera.height = graphics.getHeight() / camerascale; - if (rect.setSize(camera.viewportWidth, camera.viewportHeight).setCenter(camera.position.x, camera.position.y) - .overlaps(enemy.hitbox.getRect(enemy.x, enemy.y))) { - continue; - } + if(state.is(State.menu)){ + graphics.clear(Color.BLACK); + }else{ + Vector2 position = Tmp.v3.set(player); - float angle = Angles.angle(camera.position.x, camera.position.y, enemy.x, enemy.y); - float tx = Angles.trnsx(angle, Unit.dp.scl(20f)); - float ty = Angles.trnsy(angle, Unit.dp.scl(20f)); - Draw.rect("enemyarrow", camera.position.x + tx, camera.position.y + ty, angle); - } + if(player.isDead()){ + TileEntity core = player.getClosestCore(); + if(core != null && player.spawner == null){ + camera.position.lerpDelta(core.x, core.y, 0.08f); + }else{ + camera.position.lerpDelta(position, 0.08f); + } + }else if(!mobile){ + camera.position.lerpDelta(position, 0.08f); + } - Draw.color(); - Draw.alpha(0.4f); - Graphics.flushSurface(); - Draw.color(); - } + updateShake(0.75f); + if(pixelator.enabled()){ + pixelator.drawPixelate(); + }else{ + draw(); + } + } + } - void drawShield(){ - if(shieldGroup.size() == 0 && shieldDraws.size == 0) return; - - Graphics.surface(renderer.shieldSurface, false); - Draw.color(Color.ROYAL); - Entities.draw(shieldGroup); - for(Callable c : shieldDraws){ - c.run(); - } - Draw.reset(); - Graphics.surface(); - - for(int i = 0; i < shieldHits.size / 3; i++){ - float time = shieldHits.get(i * 3 + 2); + @Override + public void dispose(){ + minimap.dispose(); + shieldBuffer.dispose(); + blocks.dispose(); + } - time += Timers.delta() / shieldHitDuration; - shieldHits.set(i * 3 + 2, time); + void updateShake(float scale){ + if(shaketime > 0){ + float intensity = shakeIntensity * (settings.getInt("screenshake", 4) / 4f) * scale; + camera.position.add(Mathf.range(intensity), Mathf.range(intensity)); + shakeIntensity -= 0.25f * Time.delta(); + shaketime -= Time.delta(); + shakeIntensity = Mathf.clamp(shakeIntensity, 0f, 100f); + }else{ + shakeIntensity = 0f; + } + } - if(time >= 1f){ - shieldHits.removeRange(i * 3, i * 3 + 2); - i--; - } - } + public void draw(){ + camera.update(); - Texture texture = shieldSurface.texture(); - Shaders.shield.color.set(Color.SKY); + if(Float.isNaN(camera.position.x) || Float.isNaN(camera.position.y)){ + camera.position.x = player.x; + camera.position.y = player.y; + } - Tmp.tr2.setRegion(texture); - Shaders.shield.region = Tmp.tr2; - Shaders.shield.hits = shieldHits; - - if(Shaders.shield.isFallback){ - Draw.color(1f, 1f, 1f, 0.3f); - Shaders.outline.color = Color.SKY; - Shaders.outline.region = Tmp.tr2; - } + graphics.clear(clearColor); - Graphics.end(); - Graphics.shader(Shaders.shield.isFallback ? Shaders.outline : Shaders.shield); - Graphics.setScreen(); + if(!graphics.isHidden() && (Core.settings.getBool("animatedwater") || Core.settings.getBool("animatedshields")) && (shieldBuffer.getWidth() != graphics.getWidth() || shieldBuffer.getHeight() != graphics.getHeight())){ + shieldBuffer.resize(graphics.getWidth(), graphics.getHeight()); + } - Core.batch.draw(texture, 0, Gdx.graphics.getHeight(), Gdx.graphics.getWidth(), -Gdx.graphics.getHeight()); + Draw.proj(camera.projection()); - Graphics.shader(); - Graphics.end(); - Graphics.beginCam(); - - Draw.color(); - shieldDraws.clear(); - } + blocks.floor.drawFloor(); - public BlockRenderer getBlocks() { - return blocks; - } + drawAndInterpolate(groundEffectGroup, e -> e instanceof BelowLiquidTrait); + drawAndInterpolate(puddleGroup); + drawAndInterpolate(groundEffectGroup, e -> !(e instanceof BelowLiquidTrait)); - public void addShieldHit(float x, float y){ - shieldHits.addAll(x, y, 0f); - } + blocks.processBlocks(); - public void addShield(Callable call){ - shieldDraws.add(call); - } + blocks.drawShadows(); + Draw.color(); - void drawOverlay(){ + blocks.floor.beginDraw(); + blocks.floor.drawLayer(CacheLayer.walls); + blocks.floor.endDraw(); - //draw tutorial placement point - if(world.getMap().name.equals("tutorial") && control.tutorial().showBlock()){ - int x = world.getCore().x + control.tutorial().getPlacePoint().x; - int y = world.getCore().y + control.tutorial().getPlacePoint().y; - int rot = control.tutorial().getPlaceRotation(); + blocks.drawBlocks(Layer.block); + blocks.drawFog(); - Lines.stroke(1f); - Draw.color(Color.YELLOW); - Lines.square(x * tilesize, y * tilesize, tilesize / 2f + Mathf.sin(Timers.time(), 4f, 1f)); + Draw.shader(Shaders.blockbuild, true); + blocks.drawBlocks(Layer.placement); + Draw.shader(); - Draw.color(Color.ORANGE); - Lines.stroke(2f); - if(rot != -1){ - Lines.lineAngle(x * tilesize, y * tilesize, rot * 90, 6); - } - Draw.reset(); - } + blocks.drawBlocks(Layer.overlay); - //draw config selected block - if(ui.configfrag.isShown()){ - Tile tile = ui.configfrag.getSelectedTile(); - Draw.color(Colors.get("accent")); - Lines.stroke(1f); - Lines.square(tile.drawx(), tile.drawy(), - tile.block().width * tilesize / 2f + 1f); - Draw.reset(); - } - - int tilex = control.input().getBlockX(); - int tiley = control.input().getBlockY(); - - if(mobile){ - Vector2 vec = Graphics.world(Gdx.input.getX(0), Gdx.input.getY(0)); - tilex = Mathf.scl2(vec.x, tilesize); - tiley = Mathf.scl2(vec.y, tilesize); - } + drawGroundShadows(); - InputHandler input = control.input(); + drawAllTeams(false); - //draw placement box - if((input.recipe != null && state.inventory.hasItems(input.recipe.requirements) && (!ui.hasMouse() || mobile) - && control.input().drawPlace())){ + blocks.skipLayer(Layer.turret); + blocks.drawBlocks(Layer.laser); - input.placeMode.draw(control.input().getBlockX(), control.input().getBlockY(), - control.input().getBlockEndX(), control.input().getBlockEndY()); + drawFlyerShadows(); - Lines.stroke(1f); - Draw.color(Color.SCARLET); - for(SpawnPoint spawn : world.getSpawns()){ - Lines.dashCircle(spawn.start.worldx(), spawn.start.worldy(), enemyspawnspace); - } + drawAllTeams(true); - if(world.getCore() != null) { - Draw.color(Color.LIME); - Lines.poly(world.getSpawnX(), world.getSpawnY(), 4, 6f, Timers.time() * 2f); - } - - if(input.breakMode == PlaceMode.holdDelete) - input.breakMode.draw(tilex, tiley, 0, 0); - - }else if(input.breakMode.delete && control.input().drawPlace() - && (input.recipe == null || !state.inventory.hasItems(input.recipe.requirements)) - && (input.placeMode.delete || input.breakMode.both || !mobile)){ + drawAndInterpolate(bulletGroup); + drawAndInterpolate(effectGroup); - if(input.breakMode == PlaceMode.holdDelete) - input.breakMode.draw(tilex, tiley, 0, 0); - else - input.breakMode.draw(control.input().getBlockX(), control.input().getBlockY(), - control.input().getBlockEndX(), control.input().getBlockEndY()); - } + overlays.drawBottom(); + drawAndInterpolate(playerGroup, p -> true, Player::drawBuildRequests); - if(ui.toolfrag.confirming){ - ToolFragment t = ui.toolfrag; - PlaceMode.areaDelete.draw(t.px, t.py, t.px2, t.py2); - } - - Draw.reset(); + if(Entities.countInBounds(shieldGroup) > 0){ + if(settings.getBool("animatedshields")){ + Draw.flush(); + shieldBuffer.begin(); + graphics.clear(Color.CLEAR); + Entities.draw(shieldGroup); + Entities.draw(shieldGroup, shield -> true, shield -> ((ShieldEntity)shield).drawOver()); + Draw.flush(); + shieldBuffer.end(); + Draw.shader(Shaders.shield); + Draw.color(Pal.accent); + Draw.rect(Draw.wrap(shieldBuffer.getTexture()), camera.position.x, camera.position.y, camera.width, -camera.height); + Draw.color(); + Draw.shader(); + }else{ + Entities.draw(shieldGroup, shield -> true, shield -> ((ShieldEntity)shield).drawSimple()); + } + } - //draw selected block bars and info - if(input.recipe == null && !ui.hasMouse()){ - Tile tile = world.tileWorld(Graphics.mouseWorld().x, Graphics.mouseWorld().y); + overlays.drawTop(); - if(tile != null && tile.block() != Blocks.air){ - Tile target = tile; - if(tile.isLinked()) - target = tile.getLinked(); + drawAndInterpolate(playerGroup, p -> !p.isDead() && !p.isLocal, Player::drawName); - if(showBlockDebug && target.entity != null){ - Draw.color(Color.RED); - Lines.crect(target.drawx(), target.drawy(), target.block().width * tilesize, target.block().height * tilesize); - Vector2 v = new Vector2(); + Draw.color(); + Draw.flush(); + } + private void drawGroundShadows(){ + Draw.color(0, 0, 0, 0.4f); + float rad = 1.6f; + Consumer draw = u -> { + float size = Math.max(u.getIconRegion().getWidth(), u.getIconRegion().getHeight()) * Draw.scl; + Draw.rect("circle-shadow", u.x, u.y, size * rad, size * rad); + }; - Draw.tcolor(Color.YELLOW); - Draw.tscl(0.25f); - Array arr = target.block().getDebugInfo(target); - StringBuilder result = new StringBuilder(); - for(int i = 0; i < arr.size/2; i ++){ - result.append(arr.get(i*2)); - result.append(": "); - result.append(arr.get(i*2 + 1)); - result.append("\n"); - } - Draw.textc(result.toString(), target.drawx(), target.drawy(), v); - Draw.color(0f, 0f, 0f, 0.5f); - Fill.rect(target.drawx(), target.drawy(), v.x, v.y); - Draw.textc(result.toString(), target.drawx(), target.drawy(), v); - Draw.tscl(fontscale); - Draw.reset(); - } + for(EntityGroup group : unitGroups){ + if(!group.isEmpty()){ + drawAndInterpolate(group, unit -> !unit.isDead(), draw::accept); + } + } - if(Inputs.keyDown("block_info") && target.block().fullDescription != null){ - Draw.color(Colors.get("accent")); - Lines.crect(target.drawx(), target.drawy(), target.block().width * tilesize, target.block().height * tilesize); - Draw.color(); - } - - if(Inputs.keyDown("block_logs")){ - Draw.color(Colors.get("accent")); - Lines.crect(target.drawx(), target.drawy(), target.block().width * tilesize, target.block().height * tilesize); - Draw.color(); - } + if(!playerGroup.isEmpty()){ + drawAndInterpolate(playerGroup, unit -> !unit.isDead(), draw::accept); + } - if(target.entity != null) { - int bot = 0, top = 0; - for (BlockBar bar : target.block().bars) { - float offset = Mathf.sign(bar.top) * (target.block().height / 2f * tilesize + 3f + 4f * ((bar.top ? top : bot))) + - (bar.top ? -1f : 0f); + Draw.color(); + } - float value = bar.value.get(target); + private void drawFlyerShadows(){ + float trnsX = -12, trnsY = -13; + Draw.color(0, 0, 0, 0.22f); - if(MathUtils.isEqual(value, -1f)) continue; + for(EntityGroup group : unitGroups){ + if(!group.isEmpty()){ + drawAndInterpolate(group, unit -> unit.isFlying() && !unit.isDead(), baseUnit -> baseUnit.drawShadow(trnsX, trnsY)); + } + } - drawBar(bar.color, target.drawx(), target.drawy() + offset, value); + if(!playerGroup.isEmpty()){ + drawAndInterpolate(playerGroup, unit -> unit.isFlying() && !unit.isDead(), player -> player.drawShadow(trnsX, trnsY)); + } - if (bar.top) - top++; - else - bot++; - } - } + Draw.color(); + } - target.block().drawSelect(target); - } - } - - if((!debug || showUI) && Settings.getBool("healthbars")){ + private void drawAllTeams(boolean flying){ + for(Team team : Team.all){ + EntityGroup group = unitGroups[team.ordinal()]; - //draw entity health bars - for(Enemy entity : enemyGroup.all()){ - drawHealth(entity); - } + if(group.count(p -> p.isFlying() == flying) + + playerGroup.count(p -> p.isFlying() == flying && p.getTeam() == team) == 0 && flying) continue; - for(Player player : playerGroup.all()){ - if(!player.isDead() && !player.isAndroid) drawHealth(player); - } - } - } + drawAndInterpolate(unitGroups[team.ordinal()], u -> u.isFlying() == flying && !u.isDead(), Unit::drawUnder); + drawAndInterpolate(playerGroup, p -> p.isFlying() == flying && p.getTeam() == team && !p.isDead(), Unit::drawUnder); - void drawHealth(SyncEntity dest){ - float x = dest.getDrawPosition().x; - float y = dest.getDrawPosition().y; - if(dest instanceof Player && snapCamera && Settings.getBool("smoothcam") && Settings.getBool("pixelate")){ - drawHealth((int) x, (int) y - 7f, dest.health, dest.maxhealth); - }else{ - drawHealth(x, y - 7f, dest.health, dest.maxhealth); - } - } + drawAndInterpolate(unitGroups[team.ordinal()], u -> u.isFlying() == flying && !u.isDead(), Unit::drawAll); + drawAndInterpolate(playerGroup, p -> p.isFlying() == flying && p.getTeam() == team, Unit::drawAll); + blocks.drawTeamBlocks(Layer.turret, team); - void drawHealth(float x, float y, float health, float maxhealth){ - drawBar(Color.RED, x, y, health / maxhealth); - } - - //TODO optimize! - public void drawBar(Color color, float x, float y, float finion){ - finion = Mathf.clamp(finion); + drawAndInterpolate(unitGroups[team.ordinal()], u -> u.isFlying() == flying && !u.isDead(), Unit::drawOver); + drawAndInterpolate(playerGroup, p -> p.isFlying() == flying && p.getTeam() == team, Unit::drawOver); + } + } - if(finion > 0) finion = Mathf.clamp(finion + 0.2f, 0.24f, 1f); + public void drawAndInterpolate(EntityGroup group){ + drawAndInterpolate(group, t -> true, DrawTrait::draw); + } - float len = 3; + public void drawAndInterpolate(EntityGroup group, Predicate toDraw){ + drawAndInterpolate(group, toDraw, DrawTrait::draw); + } - float w = (int) (len * 2 * finion) + 0.5f; + public void drawAndInterpolate(EntityGroup group, Predicate toDraw, Consumer drawer){ + Entities.draw(group, toDraw, drawer); + } - x -= 0.5f; - y += 0.5f; + public void scaleCamera(float amount){ + targetscale += amount; + clampScale(); + } - Lines.stroke(3f); - Draw.color(Color.SLATE); - Lines.line(x - len + 1, y, x + len + 1.5f, y); - Lines.stroke(1f); - Draw.color(Color.BLACK); - Lines.line(x - len + 1, y, x + len + 0.5f, y); - Draw.color(color); - if(w >= 1) - Lines.line(x - len + 1, y, x - len + w, y); - Draw.reset(); - } + public void clampScale(){ + float s = io.anuke.arc.scene.ui.layout.Unit.dp.scl(1f); + targetscale = Mathf.clamp(targetscale, s * 1.5f, Math.round(s * 6)); + } - public void setCameraScale(int amount){ - targetscale = amount; - clampScale(); - //scale up all surfaces in preparation for the zoom - if(Settings.getBool("pixelate")){ - for(Surface surface : Graphics.getSurfaces()){ - surface.setScale(targetscale); - } - } - } + public float getScale(){ + return targetscale; + } - public void scaleCamera(int amount){ - setCameraScale(targetscale + amount); - } + public void setScale(float scl){ + targetscale = scl; + clampScale(); + } - public void clampScale(){ - targetscale = Mathf.clamp(targetscale, Math.round(Unit.dp.scl(2)), Math.round(Unit.dp.scl((5)))); - } + public void takeMapScreenshot(){ + drawGroundShadows(); + + int w = world.width() * tilesize, h = world.height() * tilesize; + int memory = w * h * 4 / 1024 / 1024; + + if(memory >= 65){ + ui.showInfo("$screenshot.invalid"); + return; + } + + boolean hadShields = Core.settings.getBool("animatedshields"); + boolean hadWater = Core.settings.getBool("animatedwater"); + Core.settings.put("animatedwater", false); + Core.settings.put("animatedshields", false); + + FrameBuffer buffer = new FrameBuffer(w, h); + + float vpW = camera.width, vpH = camera.height, px = camera.position.x, py = camera.position.y; + disableUI = true; + camera.width = w; + camera.height = h; + camera.position.x = w / 2f + tilesize / 2f; + camera.position.y = h / 2f + tilesize / 2f; + Draw.flush(); + buffer.begin(); + draw(); + Draw.flush(); + buffer.end(); + disableUI = false; + camera.width = vpW; + camera.height = vpH; + camera.position.set(px, py); + buffer.begin(); + byte[] lines = ScreenUtils.getFrameBufferPixels(0, 0, w, h, true); + for(int i = 0; i < lines.length; i += 4){ + lines[i + 3] = (byte)255; + } + buffer.end(); + Pixmap fullPixmap = new Pixmap(w, h, Pixmap.Format.RGBA8888); + BufferUtils.copy(lines, 0, fullPixmap.getPixels(), lines.length); + FileHandle file = screenshotDirectory.child("screenshot-" + Time.millis() + ".png"); + PixmapIO.writePNG(file, fullPixmap); + fullPixmap.dispose(); + ui.showInfoFade(Core.bundle.format("screenshot", file.toString())); + + buffer.dispose(); + + Core.settings.put("animatedwater", hadWater); + Core.settings.put("animatedshields", hadShields); + } } diff --git a/core/src/io/anuke/mindustry/core/ThreadHandler.java b/core/src/io/anuke/mindustry/core/ThreadHandler.java deleted file mode 100644 index 683443be57..0000000000 --- a/core/src/io/anuke/mindustry/core/ThreadHandler.java +++ /dev/null @@ -1,145 +0,0 @@ -package io.anuke.mindustry.core; - -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.utils.Array; -import com.badlogic.gdx.utils.TimeUtils; -import io.anuke.ucore.core.Timers; -import io.anuke.ucore.entities.Entities; -import io.anuke.ucore.entities.Entity; -import io.anuke.ucore.entities.EntityGroup; -import io.anuke.ucore.entities.EntityGroup.ArrayContainer; -import io.anuke.ucore.util.Log; - -import static io.anuke.mindustry.Vars.control; -import static io.anuke.mindustry.Vars.logic; - -public class ThreadHandler { - private final Array toRun = new Array<>(); - private final ThreadProvider impl; - private float delta = 1f; - private long frame = 0; - private float framesSinceUpdate; - private boolean enabled; - - private final Object updateLock = new Object(); - private boolean rendered = true; - - public ThreadHandler(ThreadProvider impl){ - this.impl = impl; - - Timers.setDeltaProvider(() -> { - float result = impl.isOnThread() ? delta : Gdx.graphics.getDeltaTime()*60f; - return Math.min(Float.isNaN(result) ? 1f : result, 12f); - }); - } - - public void run(Runnable r){ - if(enabled) { - synchronized (toRun) { - toRun.add(r); - } - }else{ - r.run(); - } - } - - public int getFPS(){ - return (int)(60/delta); - } - - public long getFrameID(){ - return frame; - } - - public float getFramesSinceUpdate(){ - return framesSinceUpdate; - } - - public void handleRender(){ - - if(!enabled) return; - - framesSinceUpdate += Timers.delta(); - - synchronized (updateLock) { - rendered = true; - impl.notify(updateLock); - } - } - - public void setEnabled(boolean enabled){ - if(enabled){ - logic.doUpdate = false; - for(EntityGroup group : Entities.getAllGroups()){ - impl.switchContainer(group); - } - Timers.runTask(2f, () -> { - impl.start(this::runLogic); - this.enabled = true; - }); - }else{ - this.enabled = false; - impl.stop(); - for(EntityGroup group : Entities.getAllGroups()){ - group.setContainer(new ArrayContainer<>()); - } - Timers.runTask(2f, () -> { - logic.doUpdate = true; - }); - } - } - - public boolean isEnabled(){ - return enabled; - } - - private void runLogic(){ - try { - while (true) { - long time = TimeUtils.millis(); - - synchronized (toRun) { - for(Runnable r : toRun){ - r.run(); - } - toRun.clear(); - } - - logic.update(); - - long elapsed = TimeUtils.timeSinceMillis(time); - long target = (long) (1000 / 60f); - - delta = Math.max(elapsed, target) / 1000f * 60f; - - if (elapsed < target) { - impl.sleep(target - elapsed); - } - - synchronized(updateLock) { - while(!rendered) { - impl.wait(updateLock); - } - rendered = false; - } - - frame ++; - framesSinceUpdate = 0; - } - } catch (InterruptedException ex) { - Log.info("Stopping logic thread."); - } catch (Throwable ex) { - control.setError(ex); - } - } - - public interface ThreadProvider { - boolean isOnThread(); - void sleep(long ms) throws InterruptedException; - void start(Runnable run); - void stop(); - void wait(Object object) throws InterruptedException; - void notify(Object object); - void switchContainer(EntityGroup group); - } -} diff --git a/core/src/io/anuke/mindustry/core/UI.java b/core/src/io/anuke/mindustry/core/UI.java index d01439e3b2..7a234521b3 100644 --- a/core/src/io/anuke/mindustry/core/UI.java +++ b/core/src/io/anuke/mindustry/core/UI.java @@ -231,7 +231,6 @@ public class UI implements ApplicationListener{ showTextInput(title, text, textLength < 0 ? 12 : textLength, def, (field, c) -> true, confirmed); } - public void showInfoFade(String info){ Table table = new Table(); table.setFillParent(true); diff --git a/core/src/io/anuke/mindustry/core/World.java b/core/src/io/anuke/mindustry/core/World.java index 18ba128b32..898d5a7040 100644 --- a/core/src/io/anuke/mindustry/core/World.java +++ b/core/src/io/anuke/mindustry/core/World.java @@ -1,359 +1,523 @@ package io.anuke.mindustry.core; -import com.badlogic.gdx.math.GridPoint2; -import com.badlogic.gdx.math.MathUtils; -import com.badlogic.gdx.math.Vector2; -import com.badlogic.gdx.utils.Array; -import io.anuke.mindustry.ai.Pathfind; -import io.anuke.mindustry.entities.TileEntity; -import io.anuke.mindustry.game.SpawnPoint; -import io.anuke.mindustry.io.Maps; +import io.anuke.annotations.Annotations.Nullable; +import io.anuke.arc.*; +import io.anuke.arc.collection.IntArray; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.Geometry; +import io.anuke.arc.math.geom.Point2; +import io.anuke.arc.util.*; +import io.anuke.mindustry.ai.*; +import io.anuke.mindustry.content.Blocks; +import io.anuke.mindustry.core.GameState.State; +import io.anuke.mindustry.entities.Entities; +import io.anuke.mindustry.game.EventType.TileChangeEvent; +import io.anuke.mindustry.game.EventType.WorldLoadEvent; +import io.anuke.mindustry.game.Team; +import io.anuke.mindustry.io.MapIO; +import io.anuke.mindustry.maps.*; +import io.anuke.mindustry.maps.generators.Generator; +import io.anuke.mindustry.type.Zone; import io.anuke.mindustry.world.*; -import io.anuke.mindustry.world.blocks.Blocks; -import io.anuke.mindustry.world.blocks.DistributionBlocks; -import io.anuke.mindustry.world.blocks.ProductionBlocks; -import io.anuke.mindustry.world.blocks.WeaponBlocks; -import io.anuke.ucore.entities.Entities; -import io.anuke.ucore.entities.Entity; -import io.anuke.ucore.modules.Module; -import io.anuke.ucore.util.Mathf; -import io.anuke.ucore.util.Tmp; +import io.anuke.mindustry.world.blocks.BlockPart; -import static io.anuke.mindustry.Vars.control; -import static io.anuke.mindustry.Vars.tilesize; +import static io.anuke.mindustry.Vars.*; -public class World extends Module{ - private int seed; - - private Map currentMap; - private Tile[][] tiles; - private Pathfind pathfind = new Pathfind(); - private Maps maps = new Maps(); - private Tile core; - private Array spawns = new Array<>(); +public class World implements ApplicationListener{ + public final Maps maps = new Maps(); + public final BlockIndexer indexer = new BlockIndexer(); + public final WaveSpawner spawner = new WaveSpawner(); + public final Pathfinder pathfinder = new Pathfinder(); + public final Context context = new Context(); - private Tile[] temptiles = new Tile[4]; - - public World(){ - maps.loadMaps(); - currentMap = maps.getMap(0); - } - - @Override - public void dispose(){ - maps.dispose(); - } + private Map currentMap; + private Tile[][] tiles; - public Array getSpawns(){ - return spawns; - } + private boolean generating, invalidMap; - public Tile getCore(){ - return core; - } - - public Maps maps(){ - return maps; - } - - public Pathfind pathfinder(){ - return pathfind; - } + public World(){ + maps.load(); + } - public float getSpawnX(){ - return core.worldx(); - } + @Override + public void init(){ + maps.loadLegacyMaps(); + } - public float getSpawnY(){ - return core.worldy() - tilesize*2; - } - - public boolean solid(int x, int y){ - Tile tile = tile(x, y); - - return tile == null || tile.solid(); - } - - public boolean passable(int x, int y){ - Tile tile = tile(x, y); - - return tile != null && tile.passable(); - } - - public boolean wallSolid(int x, int y){ - Tile tile = tile(x, y); - return tile == null || tile.block().solid; - } - - public boolean isAccessible(int x, int y){ - return !wallSolid(x, y-1) || !wallSolid(x, y+1) || !wallSolid(x-1, y) ||!wallSolid(x+1, y); - } - - public boolean blends(Block block, int x, int y){ - return !floorBlends(x, y-1, block) || !floorBlends(x, y+1, block) - || !floorBlends(x-1, y, block) ||!floorBlends(x+1, y, block); - } - - public boolean floorBlends(int x, int y, Block block){ - Tile tile = tile(x, y); - return tile == null || tile.floor().id <= block.id; - } - - public Map getMap(){ - return currentMap; - } - - public int width(){ - return currentMap.getWidth(); - } - - public int height(){ - return currentMap.getHeight(); - } + @Override + public void dispose(){ + maps.dispose(); + } - public Tile tile(int packed){ - return tile(packed % width(), packed / width()); - } - - public Tile tile(int x, int y){ - if(tiles == null){ - return null; - } - if(!Mathf.inBounds(x, y, tiles)) return null; - return tiles[x][y]; - } - - public Tile tileWorld(float x, float y){ - return tile(Mathf.scl2(x, tilesize), Mathf.scl2(y, tilesize)); - } + public boolean isInvalidMap(){ + return invalidMap; + } - public int toTile(float coord){ - return Mathf.scl2(coord, tilesize); - } - - public Tile[][] getTiles(){ - return tiles; - } - - private void createTiles(){ - for(int x = 0; x < tiles.length; x ++){ - for(int y = 0; y < tiles[0].length; y ++){ - if(tiles[x][y] == null){ - tiles[x][y] = new Tile(x, y, Blocks.stone); - } - } - } - } - - private void clearTileEntities(){ - for(int x = 0; x < tiles.length; x ++){ - for(int y = 0; y < tiles[0].length; y ++){ - if(tiles[x][y] != null && tiles[x][y].entity != null){ - tiles[x][y].entity.remove(); - } - } - } - } - - public void loadMap(Map map){ - loadMap(map, MathUtils.random(0, 99999)); - } - - public void loadMap(Map map, int seed){ - currentMap = map; - - if(tiles != null){ - clearTileEntities(); - - if(tiles.length != map.getWidth() || tiles[0].length != map.getHeight()){ - tiles = new Tile[map.getWidth()][map.getHeight()]; - } - - createTiles(); - }else{ - tiles = new Tile[map.getWidth()][map.getHeight()]; - - createTiles(); - } - - spawns.clear(); - - Entities.resizeTree(0, 0, map.getWidth() * tilesize, map.getHeight() * tilesize); - - this.seed = seed; - - core = WorldGenerator.generate(map.pixmap, tiles, spawns); + public boolean solid(int x, int y){ + Tile tile = tile(x, y); - Placement.placeBlock(core.x, core.y, ProductionBlocks.core, 0, false, false); - - if(!map.name.equals("tutorial")){ - setDefaultBlocks(); - }else{ - control.tutorial().setDefaultBlocks(core.x, core.y); - } - - pathfind.resetPaths(); - } - - void setDefaultBlocks(){ - int x = core.x, y = core.y; - int flip = Mathf.sign(!currentMap.flipBase); - int fr = currentMap.flipBase ? 2 : 0; - - set(x, y-2*flip, DistributionBlocks.conveyor, 1 + fr); - set(x, y-3*flip, DistributionBlocks.conveyor, 1 + fr); - - for(int i = 0; i < 2; i ++){ - int d = Mathf.sign(i-0.5f); - - set(x+2*d, y-2*flip, ProductionBlocks.stonedrill, d); - set(x+2*d, y-1*flip, DistributionBlocks.conveyor, 1 + fr); - set(x+2*d, y, DistributionBlocks.conveyor, 1 + fr); - set(x+2*d, y+1*flip, WeaponBlocks.doubleturret, 0 + fr); - - set(x+1*d, y-3*flip, DistributionBlocks.conveyor, 2*d); - set(x+2*d, y-3*flip, DistributionBlocks.conveyor, 2*d); - set(x+2*d, y-4*flip, DistributionBlocks.conveyor, 1 + fr); - set(x+2*d, y-5*flip, DistributionBlocks.conveyor, 1 + fr); - - set(x+3*d, y-5*flip, ProductionBlocks.stonedrill, 0 + fr); - set(x+3*d, y-4*flip, ProductionBlocks.stonedrill, 0 + fr); - set(x+3*d, y-3*flip, ProductionBlocks.stonedrill, 0 + fr); - } - } - - void set(int x, int y, Block type, int rot){ - if(!Mathf.inBounds(x, y, tiles)){ - return; - } - if(type == ProductionBlocks.stonedrill){ - tiles[x][y].setFloor(Blocks.stone); - } - tiles[x][y].setBlock(type, rot); - } - - public int getSeed(){ - return seed; - } + return tile == null || tile.solid(); + } - public void removeBlock(Tile tile){ - if(!tile.block().isMultiblock() && !tile.isLinked()){ - tile.setBlock(Blocks.air); - }else{ - Tile target = tile.target(); - Array removals = target.getLinkedTiles(); - for(Tile toremove : removals){ - //note that setting a new block automatically unlinks it - if(toremove != null) toremove.setBlock(Blocks.air); - } - } - } - - public TileEntity findTileTarget(float x, float y, Tile tile, float range, boolean damaged){ - Entity closest = null; - float dst = 0; - - int rad = (int)(range/tilesize)+1; - int tilex = Mathf.scl2(x, tilesize); - int tiley = Mathf.scl2(y, tilesize); - - for(int rx = -rad; rx <= rad; rx ++){ - for(int ry = -rad; ry <= rad; ry ++){ - Tile other = tile(rx+tilex, ry+tiley); - - if(other != null && other.getLinked() != null){ - other = other.getLinked(); - } - - if(other == null || other.entity == null || (tile != null && other.entity == tile.entity)) continue; - - TileEntity e = other.entity; - - if(damaged && e.health >= e.tile.block().health) - continue; - - float ndst = Vector2.dst(x, y, e.x, e.y); - if(ndst < range && (closest == null || ndst < dst)){ - dst = ndst; - closest = e; - } - } - } + public boolean passable(int x, int y){ + Tile tile = tile(x, y); - return (TileEntity) closest; - } + return tile != null && tile.passable(); + } - /**Raycast, but with world coordinates.*/ - public GridPoint2 raycastWorld(float x, float y, float x2, float y2){ - return raycast(Mathf.scl2(x, tilesize), Mathf.scl2(y, tilesize), - Mathf.scl2(x2, tilesize), Mathf.scl2(y2, tilesize)); - } - - /** - * Input is in block coordinates, not world coordinates. - * @return null if no collisions found, block position otherwise.*/ - public GridPoint2 raycast(int x0f, int y0f, int x1, int y1){ - int x0 = x0f; - int y0 = y0f; - int dx = Math.abs(x1 - x0); - int dy = Math.abs(y1 - y0); + public boolean wallSolid(int x, int y){ + Tile tile = tile(x, y); + return tile == null || tile.block().solid; + } - int sx = x0 < x1 ? 1 : -1; - int sy = y0 < y1 ? 1 : -1; + public boolean isAccessible(int x, int y){ + return !wallSolid(x, y - 1) || !wallSolid(x, y + 1) || !wallSolid(x - 1, y) || !wallSolid(x + 1, y); + } - int err = dx - dy; - int e2; - while(true){ + public Map getMap(){ + return currentMap; + } - if(!passable(x0, y0)){ - return Tmp.g1.set(x0, y0); - } - if(x0 == x1 && y0 == y1) break; + public void setMap(Map map){ + this.currentMap = map; + } - e2 = 2 * err; - if(e2 > -dy){ - err = err - dy; - x0 = x0 + sx; - } + public int width(){ + return tiles == null ? 0 : tiles.length; + } - if(e2 < dx){ - err = err + dx; - y0 = y0 + sy; - } - } - return null; - } + public int height(){ + return tiles == null ? 0 : tiles[0].length; + } - public void raycastEach(int x0f, int y0f, int x1, int y1, Raycaster cons){ - int x0 = x0f; - int y0 = y0f; - int dx = Math.abs(x1 - x0); - int dy = Math.abs(y1 - y0); + public int unitWidth(){ + return width()*tilesize; + } - int sx = x0 < x1 ? 1 : -1; - int sy = y0 < y1 ? 1 : -1; + public int unitHeight(){ + return height()*tilesize; + } - int err = dx - dy; - int e2; - while(true){ + public @Nullable Tile tile(int pos){ + return tiles == null ? null : tile(Pos.x(pos), Pos.y(pos)); + } - if(cons.accept(x0, y0)) break; - if(x0 == x1 && y0 == y1) break; + public @Nullable Tile tile(int x, int y){ + if(tiles == null){ + return null; + } + if(!Structs.inBounds(x, y, tiles)) return null; + return tiles[x][y]; + } - e2 = 2 * err; - if(e2 > -dy){ - err = err - dy; - x0 = x0 + sx; - } + public @Nullable Tile ltile(int x, int y){ + Tile tile = tile(x, y); + if(tile == null) return null; + return tile.block().linked(tile); + } - if(e2 < dx){ - err = err + dx; - y0 = y0 + sy; - } - } - } + public Tile rawTile(int x, int y){ + return tiles[x][y]; + } - public interface Raycaster{ - boolean accept(int x, int y); - } + public @Nullable Tile tileWorld(float x, float y){ + return tile(Math.round(x / tilesize), Math.round(y / tilesize)); + } + + public @Nullable Tile ltileWorld(float x, float y){ + return ltile(Math.round(x / tilesize), Math.round(y / tilesize)); + } + + public int toTile(float coord){ + return Math.round(coord / tilesize); + } + + public Tile[][] getTiles(){ + return tiles; + } + + private void clearTileEntities(){ + for(int x = 0; x < tiles.length; x++){ + for(int y = 0; y < tiles[0].length; y++){ + if(tiles[x][y] != null && tiles[x][y].entity != null){ + tiles[x][y].entity.remove(); + } + } + } + } + + /** + * Resizes the tile array to the specified size and returns the resulting tile array. + * Only use for loading saves! + */ + public Tile[][] createTiles(int width, int height){ + if(tiles != null){ + clearTileEntities(); + + if(tiles.length != width || tiles[0].length != height){ + tiles = new Tile[width][height]; + } + }else{ + tiles = new Tile[width][height]; + } + + return tiles; + } + + /** + * Call to signify the beginning of map loading. + * TileChangeEvents will not be fired until endMapLoad(). + */ + public void beginMapLoad(){ + generating = true; + } + + /** Call to signal the beginning of loading the map with a custom set of tiles. */ + public void beginMapLoad(Tile[][] tiles){ + this.tiles = tiles; + generating = true; + } + + /** + * Call to signify the end of map loading. Updates tile occlusions and sets up physics for the world. + * A WorldLoadEvent will be fire. + */ + public void endMapLoad(){ + prepareTiles(tiles); + + for(int x = 0; x < tiles.length; x++){ + for(int y = 0; y < tiles[0].length; y++){ + Tile tile = tiles[x][y]; + tile.updateOcclusion(); + + if(tile.entity != null){ + tile.entity.updateProximity(); + } + } + } + + addDarkness(tiles); + + Entities.getAllGroups().each(group -> group.resize(-finalWorldBounds, -finalWorldBounds, tiles.length * tilesize + finalWorldBounds * 2, tiles[0].length * tilesize + finalWorldBounds * 2)); + + generating = false; + Events.fire(new WorldLoadEvent()); + } + + public boolean isGenerating(){ + return generating; + } + + public boolean isZone(){ + return getZone() != null; + } + + public Zone getZone(){ + return state.rules.zone; + } + + public void loadGenerator(Generator generator){ + beginMapLoad(); + + createTiles(generator.width, generator.height); + generator.generate(tiles); + + endMapLoad(); + } + + public void loadMap(Map map){ + + try{ + MapIO.loadMap(map); + }catch(Exception e){ + Log.err(e); + if(!headless){ + ui.showError("$map.invalid"); + Core.app.post(() -> state.set(State.menu)); + invalidMap = true; + } + generating = false; + return; + } + + this.currentMap = map; + + invalidMap = false; + + if(!headless){ + if(state.teams.get(defaultTeam).cores.size == 0){ + ui.showError("$map.nospawn"); + invalidMap = true; + }else if(state.rules.pvp){ //pvp maps need two cores to be valid + invalidMap = true; + for(Team team : Team.all){ + if(state.teams.get(team).cores.size != 0 && team != defaultTeam){ + invalidMap = false; + } + } + if(invalidMap){ + ui.showError("$map.nospawn.pvp"); + } + }else if(state.rules.attackMode){ //pvp maps need two cores to be valid + invalidMap = state.teams.get(waveTeam).cores.isEmpty(); + if(invalidMap){ + ui.showError("$map.nospawn.attack"); + } + } + }else{ + invalidMap = true; + for(Team team : Team.all){ + if(state.teams.get(team).cores.size != 0){ + invalidMap = false; + } + } + + if(invalidMap){ + throw new MapException(map, "Map has no cores!"); + } + } + + if(invalidMap) Core.app.post(() -> state.set(State.menu)); + } + + public void notifyChanged(Tile tile){ + if(!generating){ + Core.app.post(() -> Events.fire(new TileChangeEvent(tile))); + } + } + + public void removeBlock(Tile tile){ + tile.link().getLinkedTiles(other -> other.setBlock(Blocks.air)); + } + + public void setBlock(Tile tile, Block block, Team team){ + setBlock(tile, block, team, 0); + } + + public void setBlock(Tile tile, Block block, Team team, int rotation){ + tile.setBlock(block, team, rotation); + if(block.isMultiblock()){ + int offsetx = -(block.size - 1) / 2; + int offsety = -(block.size - 1) / 2; + + for(int dx = 0; dx < block.size; dx++){ + for(int dy = 0; dy < block.size; dy++){ + int worldx = dx + offsetx + tile.x; + int worldy = dy + offsety + tile.y; + if(!(worldx == tile.x && worldy == tile.y)){ + Tile toplace = world.tile(worldx, worldy); + if(toplace != null){ + toplace.setBlock(BlockPart.get(dx + offsetx, dy + offsety), team); + } + } + } + } + } + } + + /** + * Raycast, but with world coordinates. + */ + public Point2 raycastWorld(float x, float y, float x2, float y2){ + return raycast(Math.round(x / tilesize), Math.round(y / tilesize), + Math.round(x2 / tilesize), Math.round(y2 / tilesize)); + } + + /** + * Input is in block coordinates, not world coordinates. + * @return null if no collisions found, block position otherwise. + */ + public Point2 raycast(int x0f, int y0f, int x1, int y1){ + int x0 = x0f; + int y0 = y0f; + int dx = Math.abs(x1 - x0); + int dy = Math.abs(y1 - y0); + + int sx = x0 < x1 ? 1 : -1; + int sy = y0 < y1 ? 1 : -1; + + int err = dx - dy; + int e2; + while(true){ + + if(!passable(x0, y0)){ + return Tmp.g1.set(x0, y0); + } + if(x0 == x1 && y0 == y1) break; + + e2 = 2 * err; + if(e2 > -dy){ + err = err - dy; + x0 = x0 + sx; + } + + if(e2 < dx){ + err = err + dx; + y0 = y0 + sy; + } + } + return null; + } + + public void raycastEachWorld(float x0, float y0, float x1, float y1, Raycaster cons){ + raycastEach(toTile(x0), toTile(y0), toTile(x1), toTile(y1), cons); + } + + public void raycastEach(int x0f, int y0f, int x1, int y1, Raycaster cons){ + int x0 = x0f; + int y0 = y0f; + int dx = Math.abs(x1 - x0); + int dy = Math.abs(y1 - y0); + + int sx = x0 < x1 ? 1 : -1; + int sy = y0 < y1 ? 1 : -1; + + int err = dx - dy; + int e2; + while(true){ + + if(cons.accept(x0, y0)) break; + if(x0 == x1 && y0 == y1) break; + + e2 = 2 * err; + if(e2 > -dy){ + err = err - dy; + x0 = x0 + sx; + } + + if(e2 < dx){ + err = err + dx; + y0 = y0 + sy; + } + } + } + + public void addDarkness(Tile[][] tiles){ + byte[][] dark = new byte[tiles.length][tiles[0].length]; + byte[][] writeBuffer = new byte[tiles.length][tiles[0].length]; + + byte darkIterations = 4; + for(int x = 0; x < tiles.length; x++){ + for(int y = 0; y < tiles[0].length; y++){ + Tile tile = tiles[x][y]; + if(tile.block().solid && !tile.block().synthetic() && tile.block().fillsTile){ + dark[x][y] = darkIterations; + } + } + } + + for(int i = 0; i < darkIterations; i++){ + for(int x = 0; x < tiles.length; x++){ + for(int y = 0; y < tiles[0].length; y++){ + boolean min = false; + for(Point2 point : Geometry.d4){ + int newX = x + point.x, newY = y + point.y; + if(Structs.inBounds(newX, newY, tiles) && dark[newX][newY] < dark[x][y]){ + min = true; + break; + } + } + writeBuffer[x][y] = (byte)Math.max(0, dark[x][y] - Mathf.num(min)); + } + } + + for(int x = 0; x < tiles.length; x++){ + System.arraycopy(writeBuffer[x], 0, dark[x], 0, tiles[0].length); + } + } + + for(int x = 0; x < tiles.length; x++){ + for(int y = 0; y < tiles[0].length; y++){ + Tile tile = tiles[x][y]; + if(tile.block().solid && !tile.block().synthetic()){ + tiles[x][y].rotation(dark[x][y]); + } + } + } + } + + /** + * 'Prepares' a tile array by:
+ * - setting up multiblocks
+ * - updating occlusion
+ * Usually used before placing structures on a tile array. + */ + public void prepareTiles(Tile[][] tiles){ + + //find multiblocks + IntArray multiblocks = new IntArray(); + + for(int x = 0; x < tiles.length; x++){ + for(int y = 0; y < tiles[0].length; y++){ + Tile tile = tiles[x][y]; + + if(tile.block().isMultiblock()){ + multiblocks.add(tile.pos()); + } + } + } + + //place multiblocks now + for(int i = 0; i < multiblocks.size; i++){ + int pos = multiblocks.get(i); + + int x = Pos.x(pos); + int y = Pos.y(pos); + + Block result = tiles[x][y].block(); + Team team = tiles[x][y].getTeam(); + + int offsetx = -(result.size - 1) / 2; + int offsety = -(result.size - 1) / 2; + + for(int dx = 0; dx < result.size; dx++){ + for(int dy = 0; dy < result.size; dy++){ + int worldx = dx + offsetx + x; + int worldy = dy + offsety + y; + if(!(worldx == x && worldy == y)){ + Tile toplace = world.tile(worldx, worldy); + if(toplace != null){ + toplace.setBlock(BlockPart.get(dx + offsetx, dy + offsety), team); + } + } + } + } + } + } + + public interface Raycaster{ + boolean accept(int x, int y); + } + + class Context implements WorldContext{ + @Override + public Tile tile(int x, int y){ + return tiles[x][y]; + } + + @Override + public void resize(int width, int height){ + createTiles(width, height); + } + + @Override + public Tile create(int x, int y, int floorID, int overlayID, int wallID){ + return (tiles[x][y] = new Tile(x, y, floorID, overlayID, wallID)); + } + + @Override + public boolean isGenerating(){ + return World.this.isGenerating(); + } + + @Override + public void begin(){ + beginMapLoad(); + } + + @Override + public void end(){ + endMapLoad(); + } + } } diff --git a/core/src/io/anuke/mindustry/editor/DrawOperation.java b/core/src/io/anuke/mindustry/editor/DrawOperation.java new file mode 100755 index 0000000000..b5a81a759f --- /dev/null +++ b/core/src/io/anuke/mindustry/editor/DrawOperation.java @@ -0,0 +1,95 @@ +package io.anuke.mindustry.editor; + +import io.anuke.annotations.Annotations.Struct; +import io.anuke.arc.collection.LongArray; +import io.anuke.mindustry.game.Team; +import io.anuke.mindustry.gen.TileOp; +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.Floor; + +import static io.anuke.mindustry.Vars.content; + +public class DrawOperation{ + private MapEditor editor; + private LongArray array = new LongArray(); + + public DrawOperation(MapEditor editor) { + this.editor = editor; + } + + public boolean isEmpty(){ + return array.isEmpty(); + } + + public void addOperation(long op){ + array.add(op); + } + + public void undo(){ + for(int i = array.size - 1; i >= 0; i--){ + updateTile(i); + } + } + + public void redo(){ + for(int i = 0; i < array.size; i++){ + updateTile(i); + } + } + + private void updateTile(int i) { + long l = array.get(i); + array.set(i, TileOp.get(TileOp.x(l), TileOp.y(l), TileOp.type(l), getTile(editor.tile(TileOp.x(l), TileOp.y(l)), TileOp.type(l)))); + setTile(editor.tile(TileOp.x(l), TileOp.y(l)), TileOp.type(l), TileOp.value(l)); + } + + short getTile(Tile tile, byte type){ + if(type == OpType.floor.ordinal()){ + return tile.floorID(); + }else if(type == OpType.block.ordinal()){ + return tile.blockID(); + }else if(type == OpType.rotation.ordinal()){ + return tile.rotation(); + }else if(type == OpType.team.ordinal()){ + return tile.getTeamID(); + }else if(type == OpType.overlay.ordinal()){ + return tile.overlayID(); + } + throw new IllegalArgumentException("Invalid type."); + } + + void setTile(Tile tile, byte type, short to){ + editor.load(() -> { + if(type == OpType.floor.ordinal()){ + tile.setFloor((Floor)content.block(to)); + }else if(type == OpType.block.ordinal()){ + Block block = content.block(to); + tile.setBlock(block, tile.getTeam(), tile.rotation()); + }else if(type == OpType.rotation.ordinal()){ + tile.rotation(to); + }else if(type == OpType.team.ordinal()){ + tile.setTeam(Team.all[to]); + }else if(type == OpType.overlay.ordinal()){ + tile.setOverlayID(to); + } + }); + editor.renderer().updatePoint(tile.x, tile.y); + } + + @Struct + class TileOpStruct{ + short x; + short y; + byte type; + short value; + } + + public enum OpType{ + floor, + block, + rotation, + team, + overlay + } +} diff --git a/core/src/io/anuke/mindustry/editor/EditorTile.java b/core/src/io/anuke/mindustry/editor/EditorTile.java new file mode 100644 index 0000000000..41234e1578 --- /dev/null +++ b/core/src/io/anuke/mindustry/editor/EditorTile.java @@ -0,0 +1,147 @@ +package io.anuke.mindustry.editor; + +import io.anuke.mindustry.content.Blocks; +import io.anuke.mindustry.core.GameState.State; +import io.anuke.mindustry.editor.DrawOperation.OpType; +import io.anuke.mindustry.game.Team; +import io.anuke.mindustry.gen.TileOp; +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.*; +import io.anuke.mindustry.world.modules.*; + +import static io.anuke.mindustry.Vars.state; +import static io.anuke.mindustry.Vars.ui; + +//TODO somehow remove or replace this class with a more flexible solution +public class EditorTile extends Tile{ + + public EditorTile(int x, int y, int floor, int overlay, int wall){ + super(x, y, floor, overlay, wall); + } + + @Override + public void setFloor(Floor type){ + if(state.is(State.playing)){ + super.setFloor(type); + return; + } + + if(type instanceof OverlayFloor){ + //don't place on liquids + if(!floor.isLiquid){ + setOverlayID(type.id); + } + return; + } + + if(floor == type && overlayID() == 0) return; + if(overlayID() != 0) op(OpType.overlay, overlayID()); + if(floor != type) op(OpType.floor, floor.id); + super.setFloor(type); + } + + @Override + public void setBlock(Block type){ + if(state.is(State.playing)){ + super.setBlock(type); + return; + } + + if(block == type) return; + op(OpType.block, block.id); + if(rotation != 0) op(OpType.rotation, rotation); + if(team != 0) op(OpType.team, team); + super.setBlock(type); + } + + @Override + public void setBlock(Block type, Team team, int rotation){ + if(state.is(State.playing)){ + super.setBlock(type, team, rotation); + return; + } + + setBlock(type); + setTeam(team); + rotation(rotation); + } + + @Override + public void setTeam(Team team){ + if(state.is(State.playing)){ + super.setTeam(team); + return; + } + + if(getTeamID() == team.ordinal()) return; + op(OpType.team, getTeamID()); + super.setTeam(team); + } + + @Override + public void rotation(int rotation){ + if(state.is(State.playing)){ + super.rotation(rotation); + return; + } + + if(rotation == rotation()) return; + op(OpType.rotation, rotation()); + super.rotation(rotation); + } + + @Override + public void setOverlayID(short overlay){ + if(state.is(State.playing)){ + super.setOverlayID(overlay); + return; + } + + if(overlayID() == overlay) return; + op(OpType.overlay, this.overlay); + super.setOverlayID(overlay); + } + + @Override + protected void preChanged(){ + if(state.is(State.playing)){ + super.preChanged(); + return; + } + + super.setTeam(Team.none); + } + + @Override + protected void changed(){ + if(state.is(State.playing)){ + super.changed(); + return; + } + + entity = null; + + if(block == null){ + block = Blocks.air; + } + + if(floor == null){ + floor = (Floor)Blocks.air; + } + + Block block = block(); + + if(block.hasEntity()){ + entity = block.newEntity().init(this, false); + entity.cons = new ConsumeModule(entity); + if(block.hasItems) entity.items = new ItemModule(); + if(block.hasLiquids) entity.liquids = new LiquidModule(); + if(block.hasPower) entity.power = new PowerModule(); + } + } + + private void op(OpType type, short value){ + ui.editor.editor.addTileOp(TileOp.get(x, y, (byte)type.ordinal(), value)); + } +} diff --git a/core/src/io/anuke/mindustry/editor/EditorTool.java b/core/src/io/anuke/mindustry/editor/EditorTool.java new file mode 100644 index 0000000000..97447f43a9 --- /dev/null +++ b/core/src/io/anuke/mindustry/editor/EditorTool.java @@ -0,0 +1,241 @@ +package io.anuke.mindustry.editor; + +import io.anuke.arc.collection.IntArray; +import io.anuke.arc.function.*; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.Bresenham2; +import io.anuke.arc.util.Structs; +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.content.Blocks; +import io.anuke.mindustry.game.Team; +import io.anuke.mindustry.world.*; + +public enum EditorTool{ + zoom, + pick{ + public void touched(MapEditor editor, int x, int y){ + if(!Structs.inBounds(x, y, editor.width(), editor.height())) return; + + Tile tile = editor.tile(x, y).link(); + editor.drawBlock = tile.block() == Blocks.air ? tile.overlay() == Blocks.air ? tile.floor() : tile.overlay() : tile.block(); + } + }, + line("replace", "orthogonal"){ + + @Override + public void touchedLine(MapEditor editor, int x1, int y1, int x2, int y2){ + //straight + if(mode == 1){ + if(Math.abs(x2 - x1) > Math.abs(y2 - y1)){ + y2 = y1; + }else{ + x2 = x1; + } + } + + Bresenham2.line(x1, y1, x2, y2, (x, y) -> { + if(mode == 0){ + //replace + editor.drawBlocksReplace(x, y); + }else{ + //normal + editor.drawBlocks(x, y); + } + }); + } + }, + pencil("replace", "square", "drawteams"){ + { + edit = true; + draggable = true; + } + + @Override + public void touched(MapEditor editor, int x, int y){ + if(mode == -1){ + //normal mode + editor.drawBlocks(x, y); + }else if(mode == 0){ + //replace mode + editor.drawBlocksReplace(x, y); + }else if(mode == 1){ + //square mode + editor.drawBlocks(x, y, true, tile -> true); + }else if(mode == 2){ + //draw teams + editor.drawCircle(x, y, tile -> tile.link().setTeam(editor.drawTeam)); + } + + } + }, + eraser("eraseores"){ + { + edit = true; + draggable = true; + } + + @Override + public void touched(MapEditor editor, int x, int y){ + editor.drawCircle(x, y, tile -> { + if(mode == -1){ + //erase block + Vars.world.removeBlock(tile); + }else if(mode == 0){ + //erase ore + tile.clearOverlay(); + } + }); + } + }, + fill("replaceall", "fillteams"){ + { + edit = true; + } + + IntArray stack = new IntArray(); + + @Override + public void touched(MapEditor editor, int x, int y){ + if(!Structs.inBounds(x, y, editor.width(), editor.height())) return; + Tile tile = editor.tile(x, y); + + if(editor.drawBlock.isMultiblock()){ + //don't fill multiblocks, thanks + pencil.touched(editor, x, y); + return; + } + + //mode 0 or 1, fill everything with the floor/tile or replace it + if(mode == 0 || mode == -1){ + Predicate tester; + Consumer setter; + + if(editor.drawBlock.isOverlay()){ + Block dest = tile.overlay(); + if(dest == editor.drawBlock) return; + tester = t -> t.overlay() == dest; + setter = t -> t.setOverlay(editor.drawBlock); + }else if(editor.drawBlock.isFloor()){ + Block dest = tile.floor(); + if(dest == editor.drawBlock) return; + tester = t -> t.floor() == dest; + setter = t -> t.setFloorUnder(editor.drawBlock.asFloor()); + }else{ + Block dest = tile.block(); + if(dest == editor.drawBlock) return; + tester = t -> t.block() == dest; + setter = t -> t.setBlock(editor.drawBlock); + } + + //replace only when the mode is 0 using the specified functions + fill(editor, x, y, mode == 0, tester, setter); + }else if(mode == 1){ //mode 1 is team fill + + //only fill synthetic blocks, it's meaningless otherwise + if(tile.link().synthetic()){ + Team dest = tile.getTeam(); + if(dest == editor.drawTeam) return; + fill(editor, x, y, false, t -> t.getTeamID() == dest.ordinal() && t.link().synthetic(), t -> t.setTeam(editor.drawTeam)); + } + } + } + + void fill(MapEditor editor, int x, int y, boolean replace, Predicate tester, Consumer filler){ + int width = editor.width(), height = editor.height(); + + if(replace){ + //just do it on everything + for(int cx = 0; cx < width; cx++){ + for(int cy = 0; cy < height; cy++){ + Tile tile = editor.tile(cx, cy); + if(tester.test(tile)){ + filler.accept(tile); + } + } + } + + }else{ + //perform flood fill + int x1; + + stack.clear(); + stack.add(Pos.get(x, y)); + + while(stack.size > 0){ + int popped = stack.pop(); + x = Pos.x(popped); + y = Pos.y(popped); + + x1 = x; + while(x1 >= 0 && tester.test(editor.tile(x1, y))) x1--; + x1++; + boolean spanAbove = false, spanBelow = false; + while(x1 < width && tester.test(editor.tile(x1, y))){ + filler.accept(editor.tile(x1, y)); + + if(!spanAbove && y > 0 && tester.test(editor.tile(x1, y - 1))){ + stack.add(Pos.get(x1, y - 1)); + spanAbove = true; + }else if(spanAbove && !tester.test(editor.tile(x1, y - 1))){ + spanAbove = false; + } + + if(!spanBelow && y < height - 1 && tester.test(editor.tile(x1, y + 1))){ + stack.add(Pos.get(x1, y + 1)); + spanBelow = true; + }else if(spanBelow && y < height - 1 && !tester.test(editor.tile(x1, y + 1))){ + spanBelow = false; + } + x1++; + } + } + } + } + }, + spray("replace"){ + final double chance = 0.012; + + { + edit = true; + draggable = true; + } + + @Override + public void touched(MapEditor editor, int x, int y){ + + //floor spray + if(editor.drawBlock.isFloor()){ + editor.drawCircle(x, y, tile -> { + if(Mathf.chance(chance)){ + tile.setFloor(editor.drawBlock.asFloor()); + } + }); + }else if(mode == 0){ //replace-only mode, doesn't affect air + editor.drawBlocks(x, y, tile -> Mathf.chance(chance) && tile.block() != Blocks.air); + }else{ + editor.drawBlocks(x, y, tile -> Mathf.chance(chance)); + } + } + }; + + /** All the internal alternate placement modes of this tool. */ + public final String[] altModes; + /** The current alternate placement mode. -1 is the standard mode, no changes.*/ + public int mode = -1; + /** Whether this tool causes canvas changes when touched.*/ + public boolean edit; + /** Whether this tool should be dragged across the canvas when the mouse moves.*/ + public boolean draggable; + + EditorTool(){ + this(new String[]{}); + } + + EditorTool(String... altModes){ + this.altModes = altModes; + } + + public void touched(MapEditor editor, int x, int y){} + + public void touchedLine(MapEditor editor, int x1, int y1, int x2, int y2){} +} diff --git a/core/src/io/anuke/mindustry/editor/MapEditor.java b/core/src/io/anuke/mindustry/editor/MapEditor.java new file mode 100644 index 0000000000..dd1cecd4d3 --- /dev/null +++ b/core/src/io/anuke/mindustry/editor/MapEditor.java @@ -0,0 +1,405 @@ +package io.anuke.mindustry.editor; + +import io.anuke.arc.collection.StringMap; +import io.anuke.arc.files.FileHandle; +import io.anuke.arc.function.Consumer; +import io.anuke.arc.function.Predicate; +import io.anuke.arc.graphics.Pixmap; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.util.Structs; +import io.anuke.mindustry.content.Blocks; +import io.anuke.mindustry.game.Team; +import io.anuke.mindustry.gen.TileOp; +import io.anuke.mindustry.io.LegacyMapIO; +import io.anuke.mindustry.io.MapIO; +import io.anuke.mindustry.maps.Map; +import io.anuke.mindustry.world.*; +import io.anuke.mindustry.world.blocks.BlockPart; +import io.anuke.mindustry.world.blocks.Floor; + +import static io.anuke.mindustry.Vars.world; + +public class MapEditor{ + public static final int[] brushSizes = {1, 2, 3, 4, 5, 9, 15, 20}; + + private final Context context = new Context(); + private StringMap tags = new StringMap(); + private MapRenderer renderer = new MapRenderer(this); + + private OperationStack stack = new OperationStack(); + private DrawOperation currentOp; + private boolean loading; + + public int brushSize = 1; + public int rotation; + public Block drawBlock = Blocks.stone; + public Team drawTeam = Team.blue; + + public StringMap getTags(){ + return tags; + } + + public void beginEdit(int width, int height){ + reset(); + + loading = true; + createTiles(width, height); + renderer.resize(width(), height()); + loading = false; + } + + public void beginEdit(Map map){ + reset(); + + loading = true; + tags.putAll(map.tags); + MapIO.loadMap(map, context); + checkLinkedTiles(); + renderer.resize(width(), height()); + loading = false; + } + + public void beginEdit(Pixmap pixmap){ + reset(); + + createTiles(pixmap.getWidth(), pixmap.getHeight()); + load(() -> LegacyMapIO.readPixmap(pixmap, tiles())); + renderer.resize(width(), height()); + } + + //adds missing blockparts + public void checkLinkedTiles(){ + Tile[][] tiles = world.getTiles(); + + //clear block parts first + for(int x = 0; x < width(); x++){ + for(int y = 0; y < height(); y++){ + if(tiles[x][y].block() instanceof BlockPart){ + tiles[x][y].setBlock(Blocks.air); + } + } + } + + //set up missing blockparts + for(int x = 0; x < width(); x++){ + for(int y = 0; y < height(); y++){ + if(tiles[x][y].block().isMultiblock()){ + world.setBlock(tiles[x][y], tiles[x][y].block(), tiles[x][y].getTeam()); + } + } + } + } + + public void load(Runnable r){ + loading = true; + r.run(); + loading = false; + } + + /** Creates a 2-D array of EditorTiles with stone as the floor block. */ + private void createTiles(int width, int height){ + Tile[][] tiles = world.createTiles(width, height); + + for(int x = 0; x < width; x++){ + for(int y = 0; y < height; y++){ + tiles[x][y] = new EditorTile(x, y, Blocks.stone.id, (short)0, (short)0); + } + } + } + + public Map createMap(FileHandle file){ + return new Map(file, width(), height(), new StringMap(tags), true); + } + + private void reset(){ + clearOp(); + brushSize = 1; + drawBlock = Blocks.stone; + tags = new StringMap(); + } + + public Tile[][] tiles(){ + return world.getTiles(); + } + + public Tile tile(int x, int y){ + return world.rawTile(x, y); + } + + public int width(){ + return world.width(); + } + + public int height(){ + return world.height(); + } + + public void drawBlocksReplace(int x, int y){ + drawBlocks(x, y, tile -> tile.block() != Blocks.air || drawBlock.isFloor()); + } + + public void drawBlocks(int x, int y){ + drawBlocks(x, y, false, tile -> true); + } + + public void drawBlocks(int x, int y, Predicate tester){ + drawBlocks(x, y, false, tester); + } + + public void drawBlocks(int x, int y, boolean square, Predicate tester){ + if(drawBlock.isMultiblock()){ + x = Mathf.clamp(x, (drawBlock.size - 1) / 2, width() - drawBlock.size / 2 - 1); + y = Mathf.clamp(y, (drawBlock.size - 1) / 2, height() - drawBlock.size / 2 - 1); + + int offsetx = -(drawBlock.size - 1) / 2; + int offsety = -(drawBlock.size - 1) / 2; + + for(int dx = 0; dx < drawBlock.size; dx++){ + for(int dy = 0; dy < drawBlock.size; dy++){ + int worldx = dx + offsetx + x; + int worldy = dy + offsety + y; + + if(Structs.inBounds(worldx, worldy, width(), height())){ + Tile tile = tile(worldx, worldy); + + Block block = tile.block(); + + //bail out if there's anything blocking the way + if(block.isMultiblock() || block instanceof BlockPart){ + return; + } + + renderer.updatePoint(worldx, worldy); + } + } + } + + world.setBlock(tile(x, y), drawBlock, drawTeam); + }else{ + + boolean isFloor = drawBlock.isFloor() && drawBlock != Blocks.air; + + Consumer drawer = tile -> { + if(!tester.test(tile)) return; + + //remove linked tiles blocking the way + if(!isFloor && (tile.isLinked() || tile.block().isMultiblock())){ + world.removeBlock(tile.link()); + } + + if(isFloor){ + tile.setFloor(drawBlock.asFloor()); + }else{ + tile.setBlock(drawBlock); + if(drawBlock.synthetic()){ + tile.setTeam(drawTeam); + } + if(drawBlock.rotate){ + tile.rotation((byte)rotation); + } + } + }; + + if(square){ + drawSquare(x, y, drawer); + }else{ + drawCircle(x, y, drawer); + } + } + } + + public void drawCircle(int x, int y, Consumer drawer){ + for(int rx = -brushSize; rx <= brushSize; rx++){ + for(int ry = -brushSize; ry <= brushSize; ry++){ + if(Mathf.dst2(rx, ry) <= (brushSize - 0.5f) * (brushSize - 0.5f)){ + int wx = x + rx, wy = y + ry; + + if(wx < 0 || wy < 0 || wx >= width() || wy >= height()){ + continue; + } + + drawer.accept(tile(wx, wy)); + } + } + } + } + + public void drawSquare(int x, int y, Consumer drawer){ + for(int rx = -brushSize; rx <= brushSize; rx++){ + for(int ry = -brushSize; ry <= brushSize; ry++){ + int wx = x + rx, wy = y + ry; + + if(wx < 0 || wy < 0 || wx >= width() || wy >= height()){ + continue; + } + + drawer.accept(tile(wx, wy)); + } + } + } + + public void draw_DEPRECATED(int x, int y, boolean paint, Block drawBlock, double chance){ + boolean isfloor = drawBlock instanceof Floor && drawBlock != Blocks.air; + Tile[][] tiles = world.getTiles(); + + if(drawBlock.isMultiblock()){ + x = Mathf.clamp(x, (drawBlock.size - 1) / 2, width() - drawBlock.size / 2 - 1); + y = Mathf.clamp(y, (drawBlock.size - 1) / 2, height() - drawBlock.size / 2 - 1); + + int offsetx = -(drawBlock.size - 1) / 2; + int offsety = -(drawBlock.size - 1) / 2; + + for(int dx = 0; dx < drawBlock.size; dx++){ + for(int dy = 0; dy < drawBlock.size; dy++){ + int worldx = dx + offsetx + x; + int worldy = dy + offsety + y; + + if(Structs.inBounds(worldx, worldy, width(), height())){ + Tile tile = tiles[worldx][worldy]; + + Block block = tile.block(); + + //bail out if there's anything blocking the way + if(block.isMultiblock() || block instanceof BlockPart){ + return; + } + + renderer.updatePoint(worldx, worldy); + } + } + } + + world.setBlock(tiles[x][y], drawBlock, drawTeam); + }else{ + for(int rx = -brushSize; rx <= brushSize; rx++){ + for(int ry = -brushSize; ry <= brushSize; ry++){ + if(Mathf.dst(rx, ry) <= brushSize - 0.5f && (chance >= 0.999 || Mathf.chance(chance))){ + int wx = x + rx, wy = y + ry; + + if(wx < 0 || wy < 0 || wx >= width() || wy >= height() || (paint && !isfloor && tiles[wx][wy].block() == Blocks.air)){ + continue; + } + + Tile tile = tiles[wx][wy]; + + if(!isfloor && (tile.isLinked() || tile.block().isMultiblock())){ + world.removeBlock(tile.link()); + } + + if(isfloor){ + tile.setFloor((Floor)drawBlock); + }else{ + tile.setBlock(drawBlock); + if(drawBlock.synthetic()){ + tile.setTeam(drawTeam); + } + if(drawBlock.rotate){ + tile.rotation((byte)rotation); + } + } + } + } + } + } + } + + public MapRenderer renderer(){ + return renderer; + } + + public void resize(int width, int height){ + clearOp(); + + Tile[][] previous = world.getTiles(); + int offsetX = -(width - width()) / 2, offsetY = -(height - height()) / 2; + loading = true; + + Tile[][] tiles = world.createTiles(width, height); + for(int x = 0; x < width; x++){ + for(int y = 0; y < height; y++){ + int px = offsetX + x, py = offsetY + y; + if(Structs.inBounds(px, py, previous.length, previous[0].length)){ + tiles[x][y] = previous[px][py]; + tiles[x][y].x = (short)x; + tiles[x][y].y = (short)y; + }else{ + tiles[x][y] = new EditorTile(x, y, Blocks.stone.id, (short)0, (short)0); + } + } + } + + renderer.resize(width, height); + loading = false; + } + + public void clearOp(){ + stack.clear(); + } + + public void undo(){ + if(stack.canUndo()){ + stack.undo(); + } + } + + public void redo(){ + if(stack.canRedo()){ + stack.redo(); + } + } + + public boolean canUndo(){ + return stack.canUndo(); + } + + public boolean canRedo(){ + return stack.canRedo(); + } + + public void flushOp(){ + if(currentOp == null || currentOp.isEmpty()) return; + stack.add(currentOp); + currentOp = null; + } + + public void addTileOp(long data){ + if(loading) return; + + if(currentOp == null) currentOp = new DrawOperation(this); + currentOp.addOperation(data); + + renderer.updatePoint(TileOp.x(data), TileOp.y(data)); + } + + class Context implements WorldContext{ + @Override + public Tile tile(int x, int y){ + return world.tile(x, y); + } + + @Override + public void resize(int width, int height){ + world.createTiles(width, height); + } + + @Override + public Tile create(int x, int y, int floorID, int overlayID, int wallID){ + return (tiles()[x][y] = new EditorTile(x, y, floorID, overlayID, wallID)); + } + + @Override + public boolean isGenerating(){ + return world.isGenerating(); + } + + @Override + public void begin(){ + world.beginMapLoad(); + } + + @Override + public void end(){ + world.endMapLoad(); + } + } +} \ No newline at end of file diff --git a/core/src/io/anuke/mindustry/editor/MapEditorDialog.java b/core/src/io/anuke/mindustry/editor/MapEditorDialog.java new file mode 100644 index 0000000000..798a53c671 --- /dev/null +++ b/core/src/io/anuke/mindustry/editor/MapEditorDialog.java @@ -0,0 +1,658 @@ +package io.anuke.mindustry.editor; + +import io.anuke.arc.Core; +import io.anuke.arc.collection.Array; +import io.anuke.arc.collection.StringMap; +import io.anuke.arc.files.FileHandle; +import io.anuke.arc.function.Consumer; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.graphics.Pixmap; +import io.anuke.arc.graphics.g2d.TextureRegion; +import io.anuke.arc.input.KeyCode; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.Vector2; +import io.anuke.arc.scene.actions.Actions; +import io.anuke.arc.scene.event.Touchable; +import io.anuke.arc.scene.style.TextureRegionDrawable; +import io.anuke.arc.scene.ui.*; +import io.anuke.arc.scene.ui.TextButton.TextButtonStyle; +import io.anuke.arc.scene.ui.layout.Table; +import io.anuke.arc.scene.ui.layout.Unit; +import io.anuke.arc.util.*; +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.core.GameState.State; +import io.anuke.mindustry.core.Platform; +import io.anuke.mindustry.game.*; +import io.anuke.mindustry.graphics.Pal; +import io.anuke.mindustry.io.JsonIO; +import io.anuke.mindustry.io.MapIO; +import io.anuke.mindustry.maps.Map; +import io.anuke.mindustry.ui.dialogs.FileChooser; +import io.anuke.mindustry.ui.dialogs.FloatingDialog; +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.Block.Icon; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.OverlayFloor; +import io.anuke.mindustry.world.blocks.storage.CoreBlock; + +import static io.anuke.mindustry.Vars.*; + +public class MapEditorDialog extends Dialog implements Disposable{ + public final MapEditor editor; + + private MapView view; + private MapInfoDialog infoDialog; + private MapLoadDialog loadDialog; + private MapResizeDialog resizeDialog; + private MapGenerateDialog generateDialog; + private ScrollPane pane; + private FloatingDialog menu; + private Rules lastSavedRules; + private boolean saved = false; + private boolean shownWithMap = false; + private Array blocksOut = new Array<>(); + + public MapEditorDialog(){ + super("", "dialog"); + + background("dark"); + + editor = new MapEditor(); + view = new MapView(editor); + infoDialog = new MapInfoDialog(editor); + generateDialog = new MapGenerateDialog(editor); + + menu = new FloatingDialog("$menu"); + menu.addCloseButton(); + + float isize = iconsize; + float swidth = 180f; + + menu.cont.table(t -> { + t.defaults().size(swidth, 60f).padBottom(5).padRight(5).padLeft(5); + + t.addImageTextButton("$editor.savemap", "icon-floppy-16", isize, this::save); + + t.addImageTextButton("$editor.mapinfo", "icon-pencil", isize, () -> { + infoDialog.show(); + menu.hide(); + }); + + t.row(); + + t.addImageTextButton("$editor.generate", "icon-editor", isize, () -> { + generateDialog.show(); + menu.hide(); + }); + + t.addImageTextButton("$editor.resize", "icon-resize", isize, () -> { + resizeDialog.show(); + menu.hide(); + }); + + t.row(); + + t.addImageTextButton("$editor.import", "icon-load-map", isize, () -> + createDialog("$editor.import", + "$editor.importmap", "$editor.importmap.description", "icon-load-map", (Runnable)loadDialog::show, + "$editor.importfile", "$editor.importfile.description", "icon-file", (Runnable)() -> + Platform.instance.showFileChooser("$editor.loadmap", "Map Files", file -> ui.loadAnd(() -> { + world.maps.tryCatchMapError(() -> { + if(MapIO.isImage(file)){ + ui.showInfo("$editor.errorimage"); + }else if(file.extension().equalsIgnoreCase(oldMapExtension)){ + editor.beginEdit(world.maps.makeLegacyMap(file)); + }else{ + editor.beginEdit(MapIO.createMap(file, true)); + } + }); + }), true, FileChooser.anyMapFiles), + + "$editor.importimage", "$editor.importimage.description", "icon-file-image", (Runnable)() -> + Platform.instance.showFileChooser("$loadimage", "Image Files", file -> + ui.loadAnd(() -> { + try{ + Pixmap pixmap = new Pixmap(file); + editor.beginEdit(pixmap); + pixmap.dispose(); + }catch(Exception e){ + ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, true))); + Log.err(e); + } + }), true, FileChooser.pngFiles)) + ); + + t.addImageTextButton("$editor.export", "icon-save-map", isize, () -> + Platform.instance.showFileChooser("$editor.savemap", "Map Files", file -> { + file = file.parent().child(file.nameWithoutExtension() + "." + mapExtension); + FileHandle result = file; + ui.loadAnd(() -> { + try{ + if(!editor.getTags().containsKey("name")){ + editor.getTags().put("name", result.nameWithoutExtension()); + } + MapIO.writeMap(result, editor.createMap(result)); + }catch(Exception e){ + ui.showError(Core.bundle.format("editor.errorsave", Strings.parseException(e, true))); + Log.err(e); + } + }); + }, false, FileChooser.mapFiles)); + }); + + menu.cont.row(); + + menu.cont.addImageTextButton("$editor.ingame", "icon-arrow", isize, this::playtest).padTop(-5).size(swidth * 2f + 10, 60f); + + menu.cont.row(); + + menu.cont.addImageTextButton("$quit", "icon-back", isize, () -> { + tryExit(); + menu.hide(); + }).size(swidth * 2f + 10, 60f); + + resizeDialog = new MapResizeDialog(editor, (x, y) -> { + if(!(editor.width() == x && editor.height() == y)){ + ui.loadAnd(() -> { + editor.resize(x, y); + }); + } + }); + + loadDialog = new MapLoadDialog(map -> ui.loadAnd(() -> { + try{ + editor.beginEdit(map); + }catch(Exception e){ + ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, true))); + Log.err(e); + } + })); + + setFillParent(true); + + clearChildren(); + margin(0); + shown(this::build); + + update(() -> { + if(Core.scene.getKeyboardFocus() instanceof Dialog && Core.scene.getKeyboardFocus() != this){ + return; + } + + Vector2 v = pane.stageToLocalCoordinates(Core.input.mouse()); + + if(v.x >= 0 && v.y >= 0 && v.x <= pane.getWidth() && v.y <= pane.getHeight()){ + Core.scene.setScrollFocus(pane); + }else{ + Core.scene.setScrollFocus(null); + } + + if(Core.scene != null && Core.scene.getKeyboardFocus() == this){ + doInput(); + } + }); + + shown(() -> { + + saved = true; + if(!Core.settings.getBool("landscape")) Platform.instance.beginForceLandscape(); + editor.clearOp(); + Core.scene.setScrollFocus(view); + if(!shownWithMap){ + //clear units, rules and other unnecessary stuff + logic.reset(); + state.rules = new Rules(); + editor.beginEdit(200, 200); + } + shownWithMap = false; + + Time.runTask(10f, Platform.instance::updateRPC); + }); + + hidden(() -> { + editor.clearOp(); + Platform.instance.updateRPC(); + if(!Core.settings.getBool("landscape")) Platform.instance.endForceLandscape(); + }); + } + + @Override + protected void drawBackground(float x, float y){ + drawDefaultBackground(x, y); + } + + public void resumeEditing(){ + state.set(State.menu); + shownWithMap = true; + show(); + state.rules = (lastSavedRules == null ? new Rules() : lastSavedRules); + lastSavedRules = null; + editor.renderer().updateAll(); + } + + private void playtest(){ + menu.hide(); + ui.loadAnd(() -> { + lastSavedRules = state.rules; + hide(); + //only reset the player; logic.reset() will clear entities, which we do not want + state.teams = new Teams(); + player.reset(); + state.rules = Gamemode.editor.apply(lastSavedRules.copy()); + world.setMap(new Map(StringMap.of( + "name", "Editor Playtesting", + "width", editor.width(), + "height", editor.height() + ))); + world.endMapLoad(); + //add entities so they update. is this really needed? + for(int x = 0; x < world.width(); x++){ + for(int y = 0; y < world.height(); y++){ + Tile tile = world.rawTile(x, y); + if(tile.entity != null){ + tile.entity.add(); + } + } + } + player.set(world.width() * tilesize/2f, world.height() * tilesize/2f); + player.setDead(false); + logic.play(); + }); + } + + private void save(){ + String name = editor.getTags().get("name", "").trim(); + editor.getTags().put("rules", JsonIO.write(state.rules)); + player.dead = true; + + if(name.isEmpty()){ + infoDialog.show(); + Core.app.post(() -> ui.showError("$editor.save.noname")); + }else{ + Map map = world.maps.all().find(m -> m.name().equals(name)); + if(map != null && !map.custom){ + handleSaveBuiltin(map); + }else{ + world.maps.saveMap(editor.getTags()); + ui.showInfoFade("$editor.saved"); + } + } + + menu.hide(); + saved = true; + } + + /** Called when a built-in map save is attempted.*/ + protected void handleSaveBuiltin(Map map){ + ui.showError("$editor.save.overwrite"); + } + + /** + * Argument format: + * 0) button name + * 1) description + * 2) icon name + * 3) listener + */ + private void createDialog(String title, Object... arguments){ + FloatingDialog dialog = new FloatingDialog(title); + + float h = 90f; + + dialog.cont.defaults().size(360f, h).padBottom(5).padRight(5).padLeft(5); + + for(int i = 0; i < arguments.length; i += 4){ + String name = (String)arguments[i]; + String description = (String)arguments[i + 1]; + String iconname = (String)arguments[i + 2]; + Runnable listenable = (Runnable)arguments[i + 3]; + + TextButton button = dialog.cont.addButton(name, () -> { + listenable.run(); + dialog.hide(); + menu.hide(); + }).left().margin(0).get(); + + button.clearChildren(); + button.addImage(iconname).size(iconsize).padLeft(10); + button.table(t -> { + t.add(name).growX().wrap(); + t.row(); + t.add(description).color(Color.GRAY).growX().wrap(); + }).growX().pad(10f).padLeft(5); + + button.row(); + + dialog.cont.row(); + } + + dialog.addCloseButton(); + dialog.show(); + } + + @Override + public Dialog show(){ + return super.show(Core.scene, Actions.sequence(Actions.alpha(0f), Actions.scaleTo(1f, 1f), Actions.fadeIn(0.3f))); + } + + @Override + public void dispose(){ + editor.renderer().dispose(); + } + + public void beginEditMap(FileHandle file){ + ui.loadAnd(() -> { + try{ + shownWithMap = true; + editor.beginEdit(MapIO.createMap(file, true)); + show(); + }catch(Exception e){ + Log.err(e); + ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, true))); + } + }); + } + + public MapView getView(){ + return view; + } + + public void resetSaved(){ + saved = false; + } + + public boolean hasPane(){ + return Core.scene.getScrollFocus() == pane || Core.scene.getKeyboardFocus() != this; + } + + public void build(){ + float amount = 10f, baseSize = 60f; + + float size = mobile ? (int)(Math.min(Core.graphics.getHeight(), Core.graphics.getWidth()) / amount / Unit.dp.scl(1f)) : + Math.min(Core.graphics.getDisplayMode().height / amount, baseSize); + + clearChildren(); + table(cont -> { + cont.left(); + + cont.table(mid -> { + mid.top(); + + Table tools = new Table().top(); + + ButtonGroup group = new ButtonGroup<>(); + + Consumer addTool = tool -> { + Table[] lastTable = {null}; + + ImageButton button = new ImageButton("icon-" + tool.name() + "-small", "clear-toggle"); + button.clicked(() -> { + view.setTool(tool); + if(lastTable[0] != null){ + lastTable[0].remove(); + } + }); + button.resizeImage(iconsizesmall); + button.update(() -> button.setChecked(view.getTool() == tool)); + group.add(button); + + if(tool.altModes.length > 0){ + button.clicked(l -> { + if(!mobile){ + //desktop: rightclick + l.setButton(KeyCode.MOUSE_RIGHT); + } + }, e -> { + //need to double tap + if(mobile && e.getTapCount() < 2){ + return; + } + + if(lastTable[0] != null){ + lastTable[0].remove(); + } + + Table table = new Table("dialogDim"); + table.defaults().size(300f, 70f); + + for(int i = 0; i < tool.altModes.length; i++){ + int mode = i; + String name = tool.altModes[i]; + + table.addButton(b -> { + b.left(); + b.marginLeft(6); + b.setStyle(Core.scene.skin.get("clear-toggle", TextButtonStyle.class)); + b.add(Core.bundle.get("toolmode." + name)).left(); + b.row(); + b.add(Core.bundle.get("toolmode." + name + ".description")).color(Color.LIGHT_GRAY).left(); + }, () -> { + tool.mode = (tool.mode == mode ? -1 : mode); + table.remove(); + }).update(b -> b.setChecked(tool.mode == mode)); + table.row(); + } + + table.update(() -> { + Vector2 v = button.localToStageCoordinates(Tmp.v1.setZero()); + table.setPosition(v.x, v.y, Align.topLeft); + if(!isShown()){ + table.remove(); + lastTable[0] = null; + } + }); + + table.pack(); + table.act(Core.graphics.getDeltaTime()); + + addChild(table); + lastTable[0] = table; + }); + } + + + Label mode = new Label(""); + mode.setColor(Pal.remove); + mode.update(() -> mode.setText(tool.mode == -1 ? "" : "M" + (tool.mode + 1) + " ")); + mode.setAlignment(Align.bottomRight, Align.bottomRight); + mode.touchable(Touchable.disabled); + + tools.stack(button, mode); + }; + + tools.defaults().size(size, size); + + tools.addImageButton("icon-menu-large-small", "clear", iconsizesmall, menu::show); + + ImageButton grid = tools.addImageButton("icon-grid-small", "clear-toggle", iconsizesmall, () -> view.setGrid(!view.isGrid())).get(); + + addTool.accept(EditorTool.zoom); + + tools.row(); + + ImageButton undo = tools.addImageButton("icon-undo-small", "clear", iconsizesmall, editor::undo).get(); + ImageButton redo = tools.addImageButton("icon-redo-small", "clear", iconsizesmall, editor::redo).get(); + + addTool.accept(EditorTool.pick); + + tools.row(); + + undo.setDisabled(() -> !editor.canUndo()); + redo.setDisabled(() -> !editor.canRedo()); + + undo.update(() -> undo.getImage().setColor(undo.isDisabled() ? Color.GRAY : Color.WHITE)); + redo.update(() -> redo.getImage().setColor(redo.isDisabled() ? Color.GRAY : Color.WHITE)); + grid.update(() -> grid.setChecked(view.isGrid())); + + addTool.accept(EditorTool.line); + addTool.accept(EditorTool.pencil); + addTool.accept(EditorTool.eraser); + + tools.row(); + + addTool.accept(EditorTool.fill); + addTool.accept(EditorTool.spray); + + ImageButton rotate = tools.addImageButton("icon-arrow-16-small", "clear", iconsizesmall, () -> editor.rotation = (editor.rotation + 1) % 4).get(); + rotate.getImage().update(() -> { + rotate.getImage().setRotation(editor.rotation * 90); + rotate.getImage().setOrigin(Align.center); + }); + + tools.row(); + + tools.table("underline", t -> t.add("$editor.teams")) + .colspan(3).height(40).width(size * 3f + 3f).padBottom(3); + + tools.row(); + + ButtonGroup teamgroup = new ButtonGroup<>(); + + int i = 0; + + for(Team team : Team.all){ + ImageButton button = new ImageButton("white", "clear-toggle-partial"); + button.margin(4f); + button.getImageCell().grow(); + button.getStyle().imageUpColor = team.color; + button.clicked(() -> editor.drawTeam = team); + button.update(() -> button.setChecked(editor.drawTeam == team)); + teamgroup.add(button); + tools.add(button); + + if(i++ % 3 == 2) tools.row(); + } + + mid.add(tools).top().padBottom(-6); + + mid.row(); + + mid.table("underline", t -> { + Slider slider = new Slider(0, MapEditor.brushSizes.length - 1, 1, false); + slider.moved(f -> editor.brushSize = MapEditor.brushSizes[(int)(float)f]); + + t.top(); + t.add("$editor.brush"); + t.row(); + t.add(slider).width(size * 3f - 20).padTop(4f); + }).padTop(5).growX().top(); + + }).margin(0).left().growY(); + + + cont.table(t -> t.add(view).grow()).grow(); + + cont.table(this::addBlockSelection).right().growY(); + + }).grow(); + } + + private void doInput(){ + + if(Core.input.ctrl()){ + //alt mode select + //TODO these keycode are unusable, tweak later + for(int i = 0; i < view.getTool().altModes.length + 1; i++){ + if(Core.input.keyTap(KeyCode.valueOf("NUM_" + (i + 1)))){ + view.getTool().mode = i - 1; + } + } + }else{ + //tool select + for(int i = 0; i < EditorTool.values().length; i++){ + if(Core.input.keyTap(KeyCode.valueOf("NUM_" + (i + 1)))){ + view.setTool(EditorTool.values()[i]); + break; + } + } + } + + + if(Core.input.keyTap(KeyCode.ESCAPE)){ + if(!menu.isShown()){ + menu.show(); + } + } + + if(Core.input.keyTap(KeyCode.R)){ + editor.rotation = Mathf.mod(editor.rotation + 1, 4); + } + + if(Core.input.keyTap(KeyCode.E)){ + editor.rotation = Mathf.mod(editor.rotation - 1, 4); + } + + //ctrl keys (undo, redo, save) + if(Core.input.ctrl()){ + if(Core.input.keyTap(KeyCode.Z)){ + editor.undo(); + } + + if(Core.input.keyTap(KeyCode.Y)){ + editor.redo(); + } + + if(Core.input.keyTap(KeyCode.S)){ + save(); + } + + if(Core.input.keyTap(KeyCode.G)){ + view.setGrid(!view.isGrid()); + } + } + } + + private void tryExit(){ + if(!saved){ + ui.showConfirm("$confirm", "$editor.unsaved", this::hide); + }else{ + hide(); + } + } + + private void addBlockSelection(Table table){ + Table content = new Table(); + pane = new ScrollPane(content); + pane.setFadeScrollBars(false); + pane.setOverscroll(true, false); + ButtonGroup group = new ButtonGroup<>(); + + int i = 0; + + blocksOut.clear(); + blocksOut.addAll(Vars.content.blocks()); + blocksOut.sort((b1, b2) -> { + int core = -Boolean.compare(b1 instanceof CoreBlock, b2 instanceof CoreBlock); + if(core != 0) return core; + int synth = Boolean.compare(b1.synthetic(), b2.synthetic()); + if(synth != 0) return synth; + int ore = Boolean.compare(b1 instanceof OverlayFloor, b2 instanceof OverlayFloor); + if(ore != 0) return ore; + return Integer.compare(b1.id, b2.id); + }); + + for(Block block : blocksOut){ + TextureRegion region = block.icon(Icon.medium); + + if(!Core.atlas.isFound(region)) continue; + + ImageButton button = new ImageButton("white", "clear-toggle"); + button.getStyle().imageUp = new TextureRegionDrawable(region); + button.clicked(() -> editor.drawBlock = block); + button.resizeImage(8 * 4f); + button.update(() -> button.setChecked(editor.drawBlock == block)); + group.add(button); + content.add(button).size(50f); + + if(++i % 4 == 0){ + content.row(); + } + } + + group.getButtons().get(2).setChecked(true); + + table.table("underline", extra -> extra.labelWrap(() -> editor.drawBlock.localizedName).width(200f).center()).growX(); + table.row(); + table.add(pane).growY().fillX(); + } +} diff --git a/core/src/io/anuke/mindustry/editor/MapGenerateDialog.java b/core/src/io/anuke/mindustry/editor/MapGenerateDialog.java new file mode 100644 index 0000000000..c669cd943c --- /dev/null +++ b/core/src/io/anuke/mindustry/editor/MapGenerateDialog.java @@ -0,0 +1,368 @@ +package io.anuke.mindustry.editor; + +import io.anuke.arc.Core; +import io.anuke.arc.collection.Array; +import io.anuke.arc.function.Supplier; +import io.anuke.arc.graphics.Pixmap; +import io.anuke.arc.graphics.Pixmap.Format; +import io.anuke.arc.graphics.Texture; +import io.anuke.arc.scene.ui.Image; +import io.anuke.arc.scene.ui.layout.Stack; +import io.anuke.arc.scene.ui.layout.Table; +import io.anuke.arc.util.Scaling; +import io.anuke.arc.util.async.AsyncExecutor; +import io.anuke.arc.util.async.AsyncResult; +import io.anuke.mindustry.content.Blocks; +import io.anuke.mindustry.editor.generation.*; +import io.anuke.mindustry.editor.generation.GenerateFilter.GenerateInput; +import io.anuke.mindustry.game.Team; +import io.anuke.mindustry.graphics.Pal; +import io.anuke.mindustry.io.MapIO; +import io.anuke.mindustry.ui.BorderImage; +import io.anuke.mindustry.ui.dialogs.FloatingDialog; +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.Floor; + +import static io.anuke.mindustry.Vars.*; + +@SuppressWarnings("unchecked") +public class MapGenerateDialog extends FloatingDialog{ + private final Supplier[] filterTypes = new Supplier[]{ + NoiseFilter::new, ScatterFilter::new, TerrainFilter::new, DistortFilter::new, + RiverNoiseFilter::new, OreFilter::new, MedianFilter::new, BlendFilter::new + }; + private final MapEditor editor; + + private Pixmap pixmap; + private Texture texture; + private GenerateInput input = new GenerateInput(); + private Array filters = new Array<>(); + private int scaling = mobile ? 3 : 1; + private Table filterTable; + + private AsyncExecutor executor = new AsyncExecutor(1); + private AsyncResult result; + private boolean generating; + private GenTile returnTile = new GenTile(); + + private GenTile[][] buffer1, buffer2; + + public MapGenerateDialog(MapEditor editor){ + super("$editor.generate"); + this.editor = editor; + + shown(this::setup); + addCloseButton(); + buttons.addButton("$editor.apply", () -> { + ui.loadAnd(() -> { + apply(); + hide(); + }); + }).size(160f, 64f); + buttons.addButton("$editor.randomize", () -> { + for(GenerateFilter filter : filters){ + filter.randomize(); + } + update(); + }).size(160f, 64f); + + buttons.addImageTextButton("$add", "icon-add", iconsize, this::showAdd).height(64f).width(140f); + } + + void setup(){ + if(pixmap != null){ + pixmap.dispose(); + texture.dispose(); + pixmap = null; + texture = null; + } + + pixmap = new Pixmap(editor.width() / scaling, editor.height() / scaling, Format.RGBA8888); + texture = new Texture(pixmap); + + cont.clear(); + cont.table("flat", t -> { + t.margin(8f); + t.stack(new BorderImage(texture){{ + setScaling(Scaling.fit); + }}, new Stack(){{ + add(new Image("loadDim")); + add(new Image("icon-refresh"){{ + setScaling(Scaling.none); + }}); + visible(() -> generating && !updateEditorOnChange); + }}).size(mobile ? 300f : 400f).padRight(6); + t.pane(p -> filterTable = p).width(300f).get().setScrollingDisabled(true, false); + }).grow(); + + buffer1 = create(); + buffer2 = create(); + + update(); + rebuildFilters(); + } + + GenTile[][] create(){ + GenTile[][] out = new GenTile[editor.width() / scaling][editor.height() / scaling]; + + for(int x = 0; x < out.length; x++){ + for(int y = 0; y < out[0].length; y++){ + out[x][y] = new GenTile(); + } + } + return out; + } + + void rebuildFilters(){ + filterTable.clearChildren(); + filterTable.top(); + + for(GenerateFilter filter : filters){ + filterTable.table(t -> { + t.add(filter.name()).padTop(5).color(Pal.accent).growX().left(); + + t.row(); + + t.table(b -> { + b.left(); + b.defaults().size(50f); + b.addImageButton("icon-refresh-small", iconsizesmall, () -> { + filter.randomize(); + update(); + }); + + b.addImageButton("icon-arrow-up-small", iconsizesmall, () -> { + int idx = filters.indexOf(filter); + filters.swap(idx, Math.max(0, idx - 1)); + rebuildFilters(); + update(); + }); + b.addImageButton("icon-arrow-down-small", iconsizesmall, () -> { + int idx = filters.indexOf(filter); + filters.swap(idx, Math.min(filters.size - 1, idx + 1)); + rebuildFilters(); + update(); + }); + b.addImageButton("icon-trash-small", iconsizesmall, () -> { + filters.remove(filter); + rebuildFilters(); + update(); + }); + }).growX(); + }).fillX(); + filterTable.row(); + filterTable.table("underline", f -> { + f.left(); + for(FilterOption option : filter.options){ + option.changed = this::update; + + f.table(t -> { + t.left(); + option.build(t); + }).growX().left(); + f.row(); + } + }).pad(3).padTop(0).width(280f); + filterTable.row(); + } + + if(filters.isEmpty()){ + filterTable.add("$filters.empty").wrap().width(200f); + } + } + + void showAdd(){ + FloatingDialog selection = new FloatingDialog("$add"); + selection.setFillParent(false); + selection.cont.defaults().size(210f, 60f); + int i = 0; + for(Supplier gen : filterTypes){ + GenerateFilter filter = gen.get(); + selection.cont.addButton(filter.name(), () -> { + filters.add(filter); + rebuildFilters(); + update(); + selection.hide(); + }); + if(++i % 2 == 0) selection.cont.row(); + } + + selection.cont.addButton("Default Ores", () -> { + int index = 0; + for(Block block : new Block[]{Blocks.oreCopper, Blocks.oreCoal, Blocks.oreLead, Blocks.oreTitanium, Blocks.oreThorium}){ + OreFilter filter = new OreFilter(); + filter.threshold += index ++ * 0.02f; + filter.ore = block; + filters.add(filter); + } + + rebuildFilters(); + update(); + selection.hide(); + }); + + selection.addCloseButton(); + selection.show(); + } + + GenTile dset(Tile tile){ + returnTile.set(tile); + return returnTile; + } + + void apply(){ + if(result != null){ + result.get(); + } + + buffer1 = null; + buffer2 = null; + generating = false; + if(pixmap != null){ + pixmap.dispose(); + texture.dispose(); + pixmap = null; + texture = null; + } + + //writeback buffer + GenTile[][] writeTiles = new GenTile[editor.width()][editor.height()]; + + for(int x = 0; x < editor.width(); x++){ + for(int y = 0; y < editor.height(); y++){ + writeTiles[x][y] = new GenTile(); + } + } + + for(GenerateFilter filter : filters){ + input.setFilter(filter, editor.width(), editor.height(), 1, (x, y) -> dset(editor.tile(x, y))); + //write to buffer + for(int x = 0; x < editor.width(); x++){ + for(int y = 0; y < editor.height(); y++){ + Tile tile = editor.tile(x, y); + input.begin(editor, x, y, tile.floor(), tile.block(), tile.overlay()); + filter.apply(input); + writeTiles[x][y].set(input.floor, input.block, input.ore, tile.getTeam(), tile.rotation()); + } + } + + editor.load(() -> { + //read from buffer back into tiles + for(int x = 0; x < editor.width(); x++){ + for(int y = 0; y < editor.height(); y++){ + Tile tile = editor.tile(x, y); + GenTile write = writeTiles[x][y]; + + tile.rotation(write.rotation); + tile.setFloor((Floor)content.block(write.floor)); + tile.setBlock(content.block(write.block)); + tile.setTeam(Team.all[write.team]); + tile.setOverlay(content.block(write.ore)); + } + } + }); + } + + //reset undo stack as generation... messes things up + editor.load(editor::checkLinkedTiles); + editor.renderer().updateAll(); + editor.clearOp(); + } + + void update(){ + + if(generating){ + return; + } + + Array copy = new Array<>(filters); + + result = executor.submit(() -> { + try{ + generating = true; + + if(!filters.isEmpty()){ + //write to buffer1 for reading + for(int px = 0; px < pixmap.getWidth(); px++){ + for(int py = 0; py < pixmap.getHeight(); py++){ + buffer1[px][py].set(editor.tile(px * scaling, py * scaling)); + } + } + } + + for(GenerateFilter filter : copy){ + input.setFilter(filter, pixmap.getWidth(), pixmap.getHeight(), scaling, (x, y) -> buffer1[x][y]); + //read from buffer1 and write to buffer2 + for(int px = 0; px < pixmap.getWidth(); px++){ + for(int py = 0; py < pixmap.getHeight(); py++){ + int x = px * scaling, y = py * scaling; + GenTile tile = buffer1[px][py]; + input.begin(editor, x, y, content.block(tile.floor), content.block(tile.block), content.block(tile.ore)); + filter.apply(input); + buffer2[px][py].set(input.floor, input.block, input.ore, Team.all[tile.team], tile.rotation); + } + } + for(int px = 0; px < pixmap.getWidth(); px++){ + for(int py = 0; py < pixmap.getHeight(); py++){ + buffer1[px][py].set(buffer2[px][py]); + } + } + } + + for(int px = 0; px < pixmap.getWidth(); px++){ + for(int py = 0; py < pixmap.getHeight(); py++){ + int color; + //get result from buffer1 if there's filters left, otherwise get from editor directly + if(filters.isEmpty()){ + Tile tile = editor.tile(px * scaling, py * scaling); + color = MapIO.colorFor(tile.floor(), tile.block(), tile.overlay(), Team.none); + }else{ + GenTile tile = buffer1[px][py]; + color = MapIO.colorFor(content.block(tile.floor), content.block(tile.block), content.block(tile.ore), Team.none); + } + pixmap.drawPixel(px, pixmap.getHeight() - 1 - py, color); + } + } + + Core.app.post(() -> { + if(pixmap == null || texture == null){ + return; + } + texture.draw(pixmap, 0, 0); + generating = false; + }); + }catch(Exception e){ + generating = false; + e.printStackTrace(); + } + return null; + }); + } + + public static class GenTile{ + public byte team, rotation; + public short block, floor, ore; + + void set(Block floor, Block wall, Block ore, Team team, int rotation){ + this.floor = floor.id; + this.block = wall.id; + this.ore = ore.id; + this.team = (byte)team.ordinal(); + this.rotation = (byte)rotation; + } + + void set(GenTile other){ + this.floor = other.floor; + this.block = other.block; + this.ore = other.ore; + this.team = other.team; + this.rotation = other.rotation; + } + + void set(Tile other){ + set(other.floor(), other.block(), other.overlay(), other.getTeam(), other.rotation()); + } + + } +} diff --git a/core/src/io/anuke/mindustry/editor/MapInfoDialog.java b/core/src/io/anuke/mindustry/editor/MapInfoDialog.java new file mode 100644 index 0000000000..1bfe9fad4c --- /dev/null +++ b/core/src/io/anuke/mindustry/editor/MapInfoDialog.java @@ -0,0 +1,77 @@ +package io.anuke.mindustry.editor; + +import io.anuke.arc.Core; +import io.anuke.arc.collection.ObjectMap; +import io.anuke.arc.scene.ui.TextArea; +import io.anuke.arc.scene.ui.TextField; +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.core.Platform; +import io.anuke.mindustry.game.Rules; +import io.anuke.mindustry.ui.dialogs.CustomRulesDialog; +import io.anuke.mindustry.ui.dialogs.FloatingDialog; + +public class MapInfoDialog extends FloatingDialog{ + private final MapEditor editor; + private final WaveInfoDialog waveInfo; + private final CustomRulesDialog ruleInfo = new CustomRulesDialog(); + + public MapInfoDialog(MapEditor editor){ + super("$editor.mapinfo"); + this.editor = editor; + this.waveInfo = new WaveInfoDialog(editor); + + addCloseButton(); + + shown(this::setup); + } + + private void setup(){ + cont.clear(); + + ObjectMap tags = editor.getTags(); + + cont.pane(t -> { + t.add("$editor.name").padRight(8).left(); + t.defaults().padTop(15); + + TextField name = t.addField(tags.get("name", ""), text -> { + tags.put("name", text); + }).size(400, 55f).get(); + name.setMessageText("$unknown"); + + t.row(); + t.add("$editor.description").padRight(8).left(); + + TextArea description = t.addArea(tags.get("description", ""), "textarea", text -> { + tags.put("description", text); + }).size(400f, 140f).get(); + + t.row(); + t.add("$editor.author").padRight(8).left(); + + TextField author = t.addField(tags.get("author", Core.settings.getString("mapAuthor", "")), text -> { + tags.put("author", text); + Core.settings.put("mapAuthor", text); + Core.settings.save(); + }).size(400, 55f).get(); + author.setMessageText("$unknown"); + + t.row(); + t.add("$editor.rules").padRight(8).left(); + t.addButton("$edit", () -> ruleInfo.show(Vars.state.rules, () -> Vars.state.rules = new Rules())).left().width(200f); + + t.row(); + t.add("$editor.waves").padRight(8).left(); + t.addButton("$edit", waveInfo::show).left().width(200f); + + name.change(); + description.change(); + author.change(); + + Platform.instance.addDialog(name, 50); + Platform.instance.addDialog(author, 50); + Platform.instance.addDialog(description, 1000); + t.margin(16f); + }); + } +} diff --git a/core/src/io/anuke/mindustry/editor/MapLoadDialog.java b/core/src/io/anuke/mindustry/editor/MapLoadDialog.java new file mode 100644 index 0000000000..de02642f9c --- /dev/null +++ b/core/src/io/anuke/mindustry/editor/MapLoadDialog.java @@ -0,0 +1,77 @@ +package io.anuke.mindustry.editor; + +import io.anuke.arc.function.Consumer; +import io.anuke.arc.scene.ui.*; +import io.anuke.arc.scene.ui.layout.Table; +import io.anuke.arc.util.Scaling; +import io.anuke.mindustry.maps.Map; +import io.anuke.mindustry.ui.BorderImage; +import io.anuke.mindustry.ui.dialogs.FloatingDialog; + +import static io.anuke.mindustry.Vars.world; + +public class MapLoadDialog extends FloatingDialog{ + private Map selected = null; + + public MapLoadDialog(Consumer loader){ + super("$editor.loadmap"); + + shown(this::rebuild); + rebuild(); + + TextButton button = new TextButton("$load"); + button.setDisabled(() -> selected == null); + button.clicked(() -> { + if(selected != null){ + loader.accept(selected); + hide(); + } + }); + + buttons.defaults().size(200f, 50f); + buttons.addButton("$cancel", this::hide); + buttons.add(button); + } + + public void rebuild(){ + cont.clear(); + if(world.maps.all().size > 0){ + selected = world.maps.all().first(); + } + + ButtonGroup group = new ButtonGroup<>(); + + int maxcol = 3; + + int i = 0; + + Table table = new Table(); + table.defaults().size(200f, 90f).pad(4f); + table.margin(10f); + + ScrollPane pane = new ScrollPane(table, "horizontal"); + pane.setFadeScrollBars(false); + + for(Map map : world.maps.all()){ + + TextButton button = new TextButton(map.name(), "toggle"); + button.add(new BorderImage(map.texture, 2f).setScaling(Scaling.fit)).size(16 * 4f); + button.getCells().reverse(); + button.clicked(() -> selected = map); + button.getLabelCell().grow().left().padLeft(5f); + group.add(button); + table.add(button); + if(++i % maxcol == 0) table.row(); + } + + if(world.maps.all().size == 0){ + table.add("$maps.none").center(); + }else{ + cont.add("$editor.loadmap"); + } + + cont.row(); + cont.add(pane); + } + +} diff --git a/core/src/io/anuke/mindustry/editor/MapRenderer.java b/core/src/io/anuke/mindustry/editor/MapRenderer.java new file mode 100644 index 0000000000..3bca816d61 --- /dev/null +++ b/core/src/io/anuke/mindustry/editor/MapRenderer.java @@ -0,0 +1,166 @@ +package io.anuke.mindustry.editor; + +import io.anuke.arc.Core; +import io.anuke.arc.collection.IntSet; +import io.anuke.arc.collection.IntSet.IntSetIterator; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.graphics.Texture; +import io.anuke.arc.graphics.g2d.Draw; +import io.anuke.arc.graphics.g2d.TextureRegion; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.util.Disposable; +import io.anuke.mindustry.content.Blocks; +import io.anuke.mindustry.game.Team; +import io.anuke.mindustry.graphics.IndexedRenderer; +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.BlockPart; + +import static io.anuke.mindustry.Vars.tilesize; + +public class MapRenderer implements Disposable{ + private static final int chunkSize = 64; + private IndexedRenderer[][] chunks; + private IntSet updates = new IntSet(); + private IntSet delayedUpdates = new IntSet(); + private MapEditor editor; + private int width, height; + private Texture texture; + + public MapRenderer(MapEditor editor){ + this.editor = editor; + texture = Core.atlas.find("clear-editor").getTexture(); + } + + public void resize(int width, int height){ + if(chunks != null){ + for(int x = 0; x < chunks.length; x++){ + for(int y = 0; y < chunks[0].length; y++){ + chunks[x][y].dispose(); + } + } + } + + chunks = new IndexedRenderer[(int)Math.ceil((float)width / chunkSize)][(int)Math.ceil((float)height / chunkSize)]; + + for(int x = 0; x < chunks.length; x++){ + for(int y = 0; y < chunks[0].length; y++){ + chunks[x][y] = new IndexedRenderer(chunkSize * chunkSize * 2); + } + } + this.width = width; + this.height = height; + updateAll(); + } + + public void draw(float tx, float ty, float tw, float th){ + Draw.flush(); + + IntSetIterator it = updates.iterator(); + while(it.hasNext){ + int i = it.next(); + int x = i % width; + int y = i / width; + render(x, y); + } + updates.clear(); + + updates.addAll(delayedUpdates); + delayedUpdates.clear(); + + for(int x = 0; x < chunks.length; x++){ + for(int y = 0; y < chunks[0].length; y++){ + IndexedRenderer mesh = chunks[x][y]; + + if(mesh == null){ + continue; + } + + mesh.getTransformMatrix().setToTranslation(tx, ty).scale(tw / (width * tilesize), th / (height * tilesize)); + mesh.setProjectionMatrix(Draw.proj()); + + mesh.render(texture); + } + } + } + + public void updatePoint(int x, int y){ + updates.add(x + y * width); + } + + public void updateAll(){ + for(int x = 0; x < width; x++){ + for(int y = 0; y < height; y++){ + render(x, y); + } + } + } + + private void render(int wx, int wy){ + int x = wx / chunkSize, y = wy / chunkSize; + IndexedRenderer mesh = chunks[x][y]; + Tile tile = editor.tiles()[wx][wy]; + + Team team = tile.getTeam(); + Block floor = tile.floor(); + Block wall = tile.block(); + + TextureRegion region; + + int idxWall = (wx % chunkSize) + (wy % chunkSize) * chunkSize; + int idxDecal = (wx % chunkSize) + (wy % chunkSize) * chunkSize + chunkSize * chunkSize; + + if(wall != Blocks.air && (wall.synthetic() || wall instanceof BlockPart)){ + region = !Core.atlas.isFound(wall.editorIcon()) ? Core.atlas.find("clear-editor") : wall.editorIcon(); + + if(wall.rotate){ + mesh.draw(idxWall, region, + wx * tilesize + wall.offset(), wy * tilesize + wall.offset(), + region.getWidth() * Draw.scl, region.getHeight() * Draw.scl, tile.rotation() * 90 - 90); + }else{ + mesh.draw(idxWall, region, + wx * tilesize + wall.offset() + (tilesize - region.getWidth() * Draw.scl) / 2f, + wy * tilesize + wall.offset() + (tilesize - region.getHeight() * Draw.scl) / 2f, + region.getWidth() * Draw.scl, region.getHeight() * Draw.scl); + } + }else{ + region = floor.editorVariantRegions()[Mathf.randomSeed(idxWall, 0, floor.editorVariantRegions().length - 1)]; + + mesh.draw(idxWall, region, wx * tilesize, wy * tilesize, 8, 8); + } + + float offsetX = -(wall.size / 3) * tilesize, offsetY = -(wall.size / 3) * tilesize; + + if(wall.update || wall.destructible){ + mesh.setColor(team.color); + region = Core.atlas.find("block-border-editor"); + }else if(!wall.synthetic() && wall != Blocks.air){ + region = !Core.atlas.isFound(wall.editorIcon()) ? Core.atlas.find("clear-editor") : wall.editorIcon(); + offsetX = tilesize / 2f - region.getWidth() / 2f * Draw.scl; + offsetY = tilesize / 2f - region.getHeight() / 2f * Draw.scl; + }else if(wall == Blocks.air && tile.overlay() != null){ + region = tile.overlay().editorVariantRegions()[Mathf.randomSeed(idxWall, 0, tile.overlay().editorVariantRegions().length - 1)]; + }else{ + region = Core.atlas.find("clear-editor"); + } + + mesh.draw(idxDecal, region, + wx * tilesize + offsetX, wy * tilesize + offsetY, + region.getWidth() * Draw.scl, region.getHeight() * Draw.scl); + mesh.setColor(Color.WHITE); + } + + @Override + public void dispose(){ + if(chunks == null){ + return; + } + for(int x = 0; x < chunks.length; x++){ + for(int y = 0; y < chunks[0].length; y++){ + if(chunks[x][y] != null){ + chunks[x][y].dispose(); + } + } + } + } +} diff --git a/core/src/io/anuke/mindustry/editor/MapResizeDialog.java b/core/src/io/anuke/mindustry/editor/MapResizeDialog.java new file mode 100644 index 0000000000..fb6c4f6adb --- /dev/null +++ b/core/src/io/anuke/mindustry/editor/MapResizeDialog.java @@ -0,0 +1,57 @@ +package io.anuke.mindustry.editor; + +import io.anuke.arc.function.IntPositionConsumer; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.scene.ui.layout.Table; +import io.anuke.mindustry.ui.dialogs.FloatingDialog; + +public class MapResizeDialog extends FloatingDialog{ + private static final int minSize = 50, maxSize = 500, increment = 50; + int width, height; + + public MapResizeDialog(MapEditor editor, IntPositionConsumer cons){ + super("$editor.resizemap"); + shown(() -> { + cont.clear(); + width = editor.width(); + height = editor.height(); + + Table table = new Table(); + + for(boolean w : Mathf.booleans){ + table.add(w ? "$width" : "$height").padRight(8f); + table.defaults().height(60f).padTop(8); + table.addButton("<", () -> { + if(w) + width = move(width, -1); + else + height = move(height, -1); + }).size(60f); + + table.table("button", t -> t.label(() -> (w ? width : height) + "")).width(200); + + table.addButton(">", () -> { + if(w) + width = move(width, 1); + else + height = move(height, 1); + }).size(60f); + table.row(); + } + cont.row(); + cont.add(table); + + }); + + buttons.defaults().size(200f, 50f); + buttons.addButton("$cancel", this::hide); + buttons.addButton("$editor.resize", () -> { + cons.accept(width, height); + hide(); + }); + } + + static int move(int value, int direction){ + return Mathf.clamp((value / increment + direction) * increment, minSize, maxSize); + } +} diff --git a/core/src/io/anuke/mindustry/editor/MapSaveDialog.java b/core/src/io/anuke/mindustry/editor/MapSaveDialog.java new file mode 100644 index 0000000000..57ed966a0a --- /dev/null +++ b/core/src/io/anuke/mindustry/editor/MapSaveDialog.java @@ -0,0 +1,75 @@ +package io.anuke.mindustry.editor; + +import io.anuke.arc.function.Consumer; +import io.anuke.arc.scene.ui.TextButton; +import io.anuke.arc.scene.ui.TextField; +import io.anuke.mindustry.core.Platform; +import io.anuke.mindustry.maps.Map; +import io.anuke.mindustry.ui.dialogs.FloatingDialog; + +import static io.anuke.mindustry.Vars.ui; +import static io.anuke.mindustry.Vars.world; + +public class MapSaveDialog extends FloatingDialog{ + private TextField field; + private Consumer listener; + + public MapSaveDialog(Consumer cons){ + super("$editor.savemap"); + field = new TextField(); + listener = cons; + + Platform.instance.addDialog(field); + + shown(() -> { + cont.clear(); + cont.label(() -> { + Map map = world.maps.byName(field.getText()); + if(map != null){ + if(map.custom){ + return "$editor.overwrite"; + }else{ + return "$editor.failoverwrite"; + } + } + return ""; + }).colspan(2); + cont.row(); + cont.add("$editor.mapname").padRight(14f); + cont.add(field).size(220f, 48f); + }); + + buttons.defaults().size(200f, 50f).pad(2f); + buttons.addButton("$cancel", this::hide); + + TextButton button = new TextButton("$save"); + button.clicked(() -> { + if(!invalid()){ + cons.accept(field.getText()); + hide(); + } + }); + button.setDisabled(this::invalid); + buttons.add(button); + } + + public void save(){ + if(!invalid()){ + listener.accept(field.getText()); + }else{ + ui.showError("$editor.failoverwrite"); + } + } + + public void setFieldText(String text){ + field.setText(text); + } + + private boolean invalid(){ + if(field.getText().isEmpty()){ + return true; + } + Map map = world.maps.byName(field.getText()); + return map != null && !map.custom; + } +} diff --git a/core/src/io/anuke/mindustry/editor/MapView.java b/core/src/io/anuke/mindustry/editor/MapView.java new file mode 100644 index 0000000000..79e141fe34 --- /dev/null +++ b/core/src/io/anuke/mindustry/editor/MapView.java @@ -0,0 +1,344 @@ +package io.anuke.mindustry.editor; + +import io.anuke.arc.Core; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.graphics.g2d.*; +import io.anuke.arc.input.GestureDetector; +import io.anuke.arc.input.GestureDetector.GestureListener; +import io.anuke.arc.input.KeyCode; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.*; +import io.anuke.arc.scene.Element; +import io.anuke.arc.scene.event.*; +import io.anuke.arc.scene.ui.TextField; +import io.anuke.arc.scene.ui.layout.Unit; +import io.anuke.arc.util.Tmp; +import io.anuke.mindustry.graphics.Pal; +import io.anuke.mindustry.input.Binding; +import io.anuke.mindustry.ui.GridImage; + +import static io.anuke.mindustry.Vars.mobile; +import static io.anuke.mindustry.Vars.ui; + +public class MapView extends Element implements GestureListener{ + private MapEditor editor; + private EditorTool tool = EditorTool.pencil; + private float offsetx, offsety; + private float zoom = 1f; + private boolean grid = false; + private GridImage image = new GridImage(0, 0); + private Vector2 vec = new Vector2(); + private Rectangle rect = new Rectangle(); + private Vector2[][] brushPolygons = new Vector2[MapEditor.brushSizes.length][0]; + + private boolean drawing; + private int lastx, lasty; + private int startx, starty; + private float mousex, mousey; + private EditorTool lastTool; + + public MapView(MapEditor editor){ + this.editor = editor; + + for(int i = 0; i < MapEditor.brushSizes.length; i++){ + float size = MapEditor.brushSizes[i]; + brushPolygons[i] = Geometry.pixelCircle(size, (index, x, y) -> Mathf.dst(x, y, index, index) <= index - 0.5f); + } + + Core.input.getInputProcessors().insert(0, new GestureDetector(20, 0.5f, 2, 0.15f, this)); + touchable(Touchable.enabled); + + Point2 firstTouch = new Point2(); + + addListener(new InputListener(){ + + @Override + public boolean mouseMoved(InputEvent event, float x, float y){ + mousex = x; + mousey = y; + + return false; + } + + @Override + public boolean touchDown(InputEvent event, float x, float y, int pointer, KeyCode button){ + if(pointer != 0){ + return false; + } + + if(!mobile && button != KeyCode.MOUSE_LEFT && button != KeyCode.MOUSE_MIDDLE){ + return true; + } + + if(button == KeyCode.MOUSE_MIDDLE){ + lastTool = tool; + tool = EditorTool.zoom; + } + + mousex = x; + mousey = y; + + Point2 p = project(x, y); + lastx = p.x; + lasty = p.y; + startx = p.x; + starty = p.y; + tool.touched(editor, p.x, p.y); + firstTouch.set(p); + + if(tool.edit){ + ui.editor.resetSaved(); + } + + drawing = true; + return true; + } + + @Override + public void touchUp(InputEvent event, float x, float y, int pointer, KeyCode button){ + if(!mobile && button != KeyCode.MOUSE_LEFT && button != KeyCode.MOUSE_MIDDLE){ + return; + } + + drawing = false; + + Point2 p = project(x, y); + + if(tool == EditorTool.line){ + ui.editor.resetSaved(); + tool.touchedLine(editor, startx, starty, p.x, p.y); + } + + editor.flushOp(); + + if(button == KeyCode.MOUSE_MIDDLE && lastTool != null){ + tool = lastTool; + lastTool = null; + } + + } + + @Override + public void touchDragged(InputEvent event, float x, float y, int pointer){ + mousex = x; + mousey = y; + + Point2 p = project(x, y); + + if(drawing && tool.draggable && !(p.x == lastx && p.y == lasty)){ + ui.editor.resetSaved(); + Bresenham2.line(lastx, lasty, p.x, p.y, (cx, cy) -> tool.touched(editor, cx, cy)); + } + + if(tool == EditorTool.line && tool.mode == 1){ + if(Math.abs(p.x - firstTouch.x) > Math.abs(p.y - firstTouch.y)){ + lastx = p.x; + lasty = firstTouch.y; + }else{ + lastx = firstTouch.x; + lasty = p.y; + } + }else{ + lastx = p.x; + lasty = p.y; + } + } + }); + } + + public EditorTool getTool(){ + return tool; + } + + public void setTool(EditorTool tool){ + this.tool = tool; + } + + public boolean isGrid(){ + return grid; + } + + public void setGrid(boolean grid){ + this.grid = grid; + } + + @Override + public void act(float delta){ + super.act(delta); + + if(Core.scene.getKeyboardFocus() == null || !(Core.scene.getKeyboardFocus() instanceof TextField) && !Core.input.keyDown(KeyCode.CONTROL_LEFT)){ + float ax = Core.input.axis(Binding.move_x); + float ay = Core.input.axis(Binding.move_y); + offsetx -= ax * 15f / zoom; + offsety -= ay * 15f / zoom; + } + + if(Core.input.keyTap(KeyCode.SHIFT_LEFT)){ + lastTool = tool; + tool = EditorTool.pick; + } + + if(Core.input.keyRelease(KeyCode.SHIFT_LEFT) && lastTool != null){ + tool = lastTool; + lastTool = null; + } + + if(ui.editor.hasPane()) return; + + zoom += Core.input.axis(KeyCode.SCROLL) / 10f * zoom; + clampZoom(); + } + + private void clampZoom(){ + zoom = Mathf.clamp(zoom, 0.2f, 20f); + } + + private Point2 project(float x, float y){ + float ratio = 1f / ((float)editor.width() / editor.height()); + float size = Math.min(width, height); + float sclwidth = size * zoom; + float sclheight = size * zoom * ratio; + x = (x - getWidth() / 2 + sclwidth / 2 - offsetx * zoom) / sclwidth * editor.width(); + y = (y - getHeight() / 2 + sclheight / 2 - offsety * zoom) / sclheight * editor.height(); + + if(editor.drawBlock.size % 2 == 0 && tool != EditorTool.eraser){ + return Tmp.g1.set((int)(x - 0.5f), (int)(y - 0.5f)); + }else{ + return Tmp.g1.set((int)x, (int)y); + } + } + + private Vector2 unproject(int x, int y){ + float ratio = 1f / ((float)editor.width() / editor.height()); + float size = Math.min(width, height); + float sclwidth = size * zoom; + float sclheight = size * zoom * ratio; + float px = ((float)x / editor.width()) * sclwidth + offsetx * zoom - sclwidth / 2 + getWidth() / 2; + float py = ((float)(y) / editor.height()) * sclheight + + offsety * zoom - sclheight / 2 + getHeight() / 2; + return vec.set(px, py); + } + + @Override + public void draw(){ + float ratio = 1f / ((float)editor.width() / editor.height()); + float size = Math.min(width, height); + float sclwidth = size * zoom; + float sclheight = size * zoom * ratio; + float centerx = x + width / 2 + offsetx * zoom; + float centery = y + height / 2 + offsety * zoom; + + image.setImageSize(editor.width(), editor.height()); + + if(!ScissorStack.pushScissors(rect.set(x, y, width, height))){ + return; + } + + Draw.color(Pal.remove); + Lines.stroke(2f); + Lines.rect(centerx - sclwidth / 2 - 1, centery - sclheight / 2 - 1, sclwidth + 2, sclheight + 2); + if(Core.scene.getKeyboardFocus() != null && isDescendantOf(Core.scene.getKeyboardFocus())){ + editor.renderer().draw(centerx - sclwidth / 2, centery - sclheight / 2, sclwidth, sclheight); + } + Draw.reset(); + + if(!ScissorStack.pushScissors(rect.set(x, y, width, height))){ + return; + } + + if(grid){ + Draw.color(Color.GRAY); + image.setBounds(centerx - sclwidth / 2, centery - sclheight / 2, sclwidth, sclheight); + image.draw(); + Draw.color(); + } + + int index = 0; + for(int i = 0; i < MapEditor.brushSizes.length; i++){ + if(editor.brushSize == MapEditor.brushSizes[i]){ + index = i; + break; + } + } + + float scaling = zoom * Math.min(width, height) / editor.width(); + + Draw.color(Pal.accent); + Lines.stroke(Unit.dp.scl(2f)); + + if((!editor.drawBlock.isMultiblock() || tool == EditorTool.eraser) && tool != EditorTool.fill){ + if(tool == EditorTool.line && drawing){ + Vector2 v1 = unproject(startx, starty).add(x, y); + float sx = v1.x, sy = v1.y; + Vector2 v2 = unproject(lastx, lasty).add(x, y); + + Lines.poly(brushPolygons[index], sx, sy, scaling); + Lines.poly(brushPolygons[index], v2.x, v2.y, scaling); + } + + if((tool.edit || (tool == EditorTool.line && !drawing)) && (!mobile || drawing)){ + Point2 p = project(mousex, mousey); + Vector2 v = unproject(p.x, p.y).add(x, y); + + //pencil square outline + if(tool == EditorTool.pencil && tool.mode == 1){ + Lines.square(v.x + scaling/2f, v.y + scaling/2f, scaling * (editor.brushSize + 0.5f)); + }else{ + Lines.poly(brushPolygons[index], v.x, v.y, scaling); + } + } + }else{ + if((tool.edit || tool == EditorTool.line) && (!mobile || drawing)){ + Point2 p = project(mousex, mousey); + Vector2 v = unproject(p.x, p.y).add(x, y); + float offset = (editor.drawBlock.size % 2 == 0 ? scaling / 2f : 0f); + Lines.square( + v.x + scaling / 2f + offset, + v.y + scaling / 2f + offset, + scaling * editor.drawBlock.size / 2f); + } + } + + Draw.color(Pal.accent); + Lines.stroke(Unit.dp.scl(3f)); + Lines.rect(x, y, width, height); + Draw.reset(); + + ScissorStack.popScissors(); + ScissorStack.popScissors(); + } + + private boolean active(){ + return Core.scene.getKeyboardFocus() != null + && Core.scene.getKeyboardFocus().isDescendantOf(ui.editor) + && ui.editor.isShown() && tool == EditorTool.zoom && + Core.scene.hit(Core.input.mouse().x, Core.input.mouse().y, true) == this; + } + + @Override + public boolean pan(float x, float y, float deltaX, float deltaY){ + if(!active()) return false; + offsetx += deltaX / zoom; + offsety += deltaY / zoom; + return false; + } + + @Override + public boolean zoom(float initialDistance, float distance){ + if(!active()) return false; + float nzoom = distance - initialDistance; + zoom += nzoom / 10000f / Unit.dp.scl(1f) * zoom; + clampZoom(); + return false; + } + + @Override + public boolean pinch(Vector2 initialPointer1, Vector2 initialPointer2, Vector2 pointer1, Vector2 pointer2){ + return false; + } + + @Override + public void pinchStop(){ + + } +} diff --git a/core/src/io/anuke/mindustry/editor/OperationStack.java b/core/src/io/anuke/mindustry/editor/OperationStack.java new file mode 100755 index 0000000000..7927cbb026 --- /dev/null +++ b/core/src/io/anuke/mindustry/editor/OperationStack.java @@ -0,0 +1,51 @@ +package io.anuke.mindustry.editor; + +import io.anuke.arc.collection.Array; + +public class OperationStack{ + private final static int maxSize = 10; + private Array stack = new Array<>(); + private int index = 0; + + public OperationStack(){ + + } + + public void clear(){ + stack.clear(); + index = 0; + } + + public void add(DrawOperation action){ + stack.truncate(stack.size + index); + index = 0; + stack.add(action); + + if(stack.size > maxSize){ + stack.remove(0); + } + } + + public boolean canUndo(){ + return !(stack.size - 1 + index < 0); + } + + public boolean canRedo(){ + return !(index > -1 || stack.size + index < 0); + } + + public void undo(){ + if(!canUndo()) return; + + stack.get(stack.size - 1 + index).undo(); + index--; + } + + public void redo(){ + if(!canRedo()) return; + + index++; + stack.get(stack.size - 1 + index).redo(); + + } +} diff --git a/core/src/io/anuke/mindustry/editor/WaveInfoDialog.java b/core/src/io/anuke/mindustry/editor/WaveInfoDialog.java new file mode 100644 index 0000000000..6b4c2e80d0 --- /dev/null +++ b/core/src/io/anuke/mindustry/editor/WaveInfoDialog.java @@ -0,0 +1,268 @@ +package io.anuke.mindustry.editor; + +import io.anuke.arc.Core; +import io.anuke.arc.collection.Array; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.input.KeyCode; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.scene.event.Touchable; +import io.anuke.arc.scene.ui.Label; +import io.anuke.arc.scene.ui.TextField.TextFieldFilter; +import io.anuke.arc.scene.ui.layout.Table; +import io.anuke.arc.util.*; +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.content.*; +import io.anuke.mindustry.game.*; +import io.anuke.mindustry.graphics.Pal; +import io.anuke.mindustry.io.JsonIO; +import io.anuke.mindustry.type.*; +import io.anuke.mindustry.ui.dialogs.FloatingDialog; + +import static io.anuke.mindustry.Vars.*; +import static io.anuke.mindustry.game.SpawnGroup.never; + +public class WaveInfoDialog extends FloatingDialog{ + private final static int displayed = 20; + private Array groups = new Array<>(); + + private Table table, preview; + private int start = 0; + private UnitType lastType = UnitTypes.dagger; + private float updateTimer, updatePeriod = 1f; + + public WaveInfoDialog(MapEditor editor){ + super("$waves.title"); + + shown(this::setup); + hidden(() -> { + state.rules.spawns = groups; + }); + + keyDown(key -> { + if(key == KeyCode.ESCAPE || key == KeyCode.BACK){ + Core.app.post(this::hide); + } + }); + + addCloseButton(); + buttons.addButton("$waves.edit", () -> { + FloatingDialog dialog = new FloatingDialog("$waves.edit"); + dialog.addCloseButton(); + dialog.setFillParent(false); + dialog.cont.defaults().size(210f, 64f); + dialog.cont.addButton("$waves.copy", () -> { + ui.showInfoFade("$waves.copied"); + Core.app.getClipboard().setContents(world.maps.writeWaves(groups)); + dialog.hide(); + }).disabled(b -> groups == null); + dialog.cont.row(); + dialog.cont.addButton("$waves.load", () -> { + try{ + groups = world.maps.readWaves(Core.app.getClipboard().getContents()); + buildGroups(); + }catch(Exception e){ + ui.showError("$waves.invalid"); + } + dialog.hide(); + }).disabled(b -> Core.app.getClipboard().getContents() == null || Core.app.getClipboard().getContents().isEmpty()); + dialog.cont.row(); + dialog.cont.addButton("$settings.reset", () -> ui.showConfirm("$confirm", "$settings.clear.confirm", () -> { + groups = JsonIO.copy(defaultWaves.get()); + buildGroups(); + dialog.hide(); + })); + dialog.show(); + }).size(270f, 64f); + } + + void setup(){ + groups = JsonIO.copy(state.rules.spawns.isEmpty() ? defaultWaves.get() : state.rules.spawns); + + cont.clear(); + cont.stack(new Table("clear", main -> { + main.pane(t -> table = t).growX().growY().get().setScrollingDisabled(true, false); + main.row(); + main.addButton("$add", () -> { + if(groups == null) groups = new Array<>(); + groups.add(new SpawnGroup(lastType)); + buildGroups(); + }).growX().height(70f); + }), new Label("$waves.none"){{ + visible(groups::isEmpty); + touchable(Touchable.disabled); + setWrap(true); + setAlignment(Align.center, Align.center); + }}).width(390f).growY(); + + cont.table("clear", m -> { + m.add("$waves.preview").color(Color.LIGHT_GRAY).growX().center().get().setAlignment(Align.center, Align.center); + m.row(); + m.addButton("-", () -> { + }).update(t -> { + if(t.getClickListener().isPressed()){ + updateTimer += Time.delta(); + if(updateTimer >= updatePeriod){ + start = Math.max(start - 1, 0); + updateTimer = 0f; + updateWaves(); + } + } + }).growX().height(70f); + m.row(); + m.pane(t -> preview = t).grow().get().setScrollingDisabled(true, false); + m.row(); + m.addButton("+", () -> { + }).update(t -> { + if(t.getClickListener().isPressed()){ + updateTimer += Time.delta(); + if(updateTimer >= updatePeriod){ + start++; + updateTimer = 0f; + updateWaves(); + } + } + }).growX().height(70f); + }).growY().width(180f).growY(); + + buildGroups(); + } + + void buildGroups(){ + table.clear(); + table.top(); + table.margin(10f); + + if(groups != null){ + for(SpawnGroup group : groups){ + table.table("clear", t -> { + t.margin(6f).defaults().pad(2).padLeft(5f).growX().left(); + t.addButton(b -> { + b.left(); + b.addImage(group.type.iconRegion).size(30f).padRight(3); + b.add(group.type.localizedName).color(Pal.accent); + }, () -> showUpdate(group)).pad(-6f).padBottom(0f); + + t.row(); + t.table(spawns -> { + spawns.addField("" + (group.begin + 1), TextFieldFilter.digitsOnly, text -> { + if(Strings.canParsePostiveInt(text)){ + group.begin = Strings.parseInt(text) - 1; + updateWaves(); + } + }).width(100f); + spawns.add("$waves.to").padLeft(4).padRight(4); + spawns.addField(group.end == never ? "" : (group.end + 1) + "", TextFieldFilter.digitsOnly, text -> { + if(Strings.canParsePostiveInt(text)){ + group.end = Strings.parseInt(text) - 1; + updateWaves(); + }else if(text.isEmpty()){ + group.end = never; + updateWaves(); + } + }).width(100f).get().setMessageText(Core.bundle.get("waves.never")); + }); + t.row(); + t.table(p -> { + p.add("$waves.every").padRight(4); + p.addField(group.spacing + "", TextFieldFilter.digitsOnly, text -> { + if(Strings.canParsePostiveInt(text) && Strings.parseInt(text) > 0){ + group.spacing = Strings.parseInt(text); + updateWaves(); + } + }).width(100f); + p.add("$waves.waves").padLeft(4); + }); + + t.row(); + t.table(a -> { + a.addField(group.unitAmount + "", TextFieldFilter.digitsOnly, text -> { + if(Strings.canParsePostiveInt(text)){ + group.unitAmount = Strings.parseInt(text); + updateWaves(); + } + }).width(80f); + + a.add(" + "); + a.addField(Strings.fixed(Math.max((Mathf.isZero(group.unitScaling) ? 0 : 1f / group.unitScaling), 0), 2), TextFieldFilter.floatsOnly, text -> { + if(Strings.canParsePositiveFloat(text)){ + group.unitScaling = 1f / Strings.parseFloat(text); + updateWaves(); + } + }).width(80f); + a.add("$waves.perspawn").padLeft(4); + }); + + t.row(); + t.addCheck("$waves.boss", b -> group.effect = (b ? StatusEffects.boss : null)).padTop(4).update(b -> b.setChecked(group.effect == StatusEffects.boss)); + + t.row(); + t.addButton("$waves.remove", () -> { + groups.remove(group); + table.getCell(t).pad(0f); + t.remove(); + updateWaves(); + }).growX().pad(-6f).padTop(5); + }).width(340f).pad(5); + table.row(); + } + }else{ + table.add("$editor.default"); + } + + updateWaves(); + } + + void showUpdate(SpawnGroup group){ + FloatingDialog dialog = new FloatingDialog(""); + dialog.setFillParent(false); + int i = 0; + for(UnitType type : content.units()){ + dialog.cont.addButton(t -> { + t.left(); + t.addImage(type.iconRegion).size(40f).padRight(2f); + t.add(type.localizedName); + }, () -> { + lastType = type; + group.type = type; + dialog.hide(); + buildGroups(); + }).pad(2).margin(12f).fillX(); + if(++i % 3 == 0) dialog.cont.row(); + } + dialog.show(); + } + + void updateWaves(){ + preview.clear(); + preview.top(); + + for(int i = start; i < displayed + start; i++){ + int wave = i; + preview.table("underline", table -> { + table.add((wave + 1) + "").color(Pal.accent).center().colspan(2).get().setAlignment(Align.center, Align.center); + table.row(); + + int[] spawned = new int[Vars.content.getBy(ContentType.unit).size]; + + for(SpawnGroup spawn : groups){ + spawned[spawn.type.id] += spawn.getUnitsSpawned(wave); + } + + for(int j = 0; j < spawned.length; j++){ + if(spawned[j] > 0){ + UnitType type = content.getByID(ContentType.unit, j); + table.addImage(type.iconRegion).size(30f).padRight(4); + table.add(spawned[j] + "x").color(Color.LIGHT_GRAY).padRight(6); + table.row(); + } + } + + if(table.getChildren().size == 1){ + table.add("$none").color(Pal.remove); + } + }).width(110f).pad(2f); + + preview.row(); + } + } +} diff --git a/core/src/io/anuke/mindustry/editor/generation/BlendFilter.java b/core/src/io/anuke/mindustry/editor/generation/BlendFilter.java new file mode 100644 index 0000000000..c600ad1bef --- /dev/null +++ b/core/src/io/anuke/mindustry/editor/generation/BlendFilter.java @@ -0,0 +1,46 @@ +package io.anuke.mindustry.editor.generation; + +import io.anuke.arc.math.Mathf; +import io.anuke.mindustry.content.Blocks; +import io.anuke.mindustry.editor.generation.FilterOption.BlockOption; +import io.anuke.mindustry.editor.generation.FilterOption.SliderOption; +import io.anuke.mindustry.world.Block; + +import static io.anuke.mindustry.editor.generation.FilterOption.floorsOnly; + +public class BlendFilter extends GenerateFilter{ + float radius = 2f; + Block flooronto = Blocks.stone, floor = Blocks.ice; + + { + options( + new SliderOption("radius", () -> radius, f -> radius = f, 1f, 10f), + new BlockOption("flooronto", () -> flooronto, b -> flooronto = b, floorsOnly), + new BlockOption("floor", () -> floor, b -> floor = b, floorsOnly) + ); + } + + @Override + public void apply(){ + if(in.floor == flooronto) return; + + int rad = (int)radius; + boolean found = false; + + outer: + for(int x = -rad; x <= rad; x++){ + for(int y = -rad; y <= rad; y++){ + if(Mathf.dst2(x, y) > rad*rad) continue; + + if(in.tile(in.x + x, in.y + y).floor == flooronto.id){ + found = true; + break outer; + } + } + } + + if(found){ + in.floor = floor; + } + } +} diff --git a/core/src/io/anuke/mindustry/editor/generation/DistortFilter.java b/core/src/io/anuke/mindustry/editor/generation/DistortFilter.java new file mode 100644 index 0000000000..3fab21d3a5 --- /dev/null +++ b/core/src/io/anuke/mindustry/editor/generation/DistortFilter.java @@ -0,0 +1,27 @@ +package io.anuke.mindustry.editor.generation; + +import io.anuke.mindustry.editor.MapGenerateDialog.GenTile; +import io.anuke.mindustry.editor.generation.FilterOption.SliderOption; +import io.anuke.mindustry.world.blocks.Floor; + +import static io.anuke.mindustry.Vars.content; + +public class DistortFilter extends GenerateFilter{ + float scl = 40, mag = 5; + + { + options( + new SliderOption("scale", () -> scl, f -> scl = f, 1f, 400f), + new SliderOption("mag", () -> mag, f -> mag = f, 0.5f, 100f) + ); + } + + @Override + public void apply(){ + GenTile tile = in.tile(in.x / (in.scaling) + (noise(in.x, in.y, scl, mag) - mag / 2f) / in.scaling, in.y / (in.scaling) + (noise(in.x, in.y + o, scl, mag) - mag / 2f) / in.scaling); + + in.floor = content.block(tile.floor); + if(!content.block(tile.block).synthetic() && !in.block.synthetic()) in.block = content.block(tile.block); + if(!((Floor)in.floor).isLiquid) in.ore = content.block(tile.ore); + } +} diff --git a/core/src/io/anuke/mindustry/editor/generation/FilterOption.java b/core/src/io/anuke/mindustry/editor/generation/FilterOption.java new file mode 100644 index 0000000000..1ef160315e --- /dev/null +++ b/core/src/io/anuke/mindustry/editor/generation/FilterOption.java @@ -0,0 +1,95 @@ +package io.anuke.mindustry.editor.generation; + +import io.anuke.arc.Core; +import io.anuke.arc.function.*; +import io.anuke.arc.scene.style.TextureRegionDrawable; +import io.anuke.arc.scene.ui.Slider; +import io.anuke.arc.scene.ui.layout.Table; +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.content.Blocks; +import io.anuke.mindustry.ui.dialogs.FloatingDialog; +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.Block.Icon; +import io.anuke.mindustry.world.blocks.*; + +import static io.anuke.mindustry.Vars.updateEditorOnChange; + +public abstract class FilterOption{ + public static final Predicate floorsOnly = b -> (b instanceof Floor && !(b instanceof OverlayFloor)) && Core.atlas.isFound(b.icon(Icon.full)); + public static final Predicate wallsOnly = b -> (!b.synthetic() && !(b instanceof Floor)) && Core.atlas.isFound(b.icon(Icon.full)); + public static final Predicate floorsOptional = b -> b == Blocks.air || ((b instanceof Floor && !(b instanceof OverlayFloor)) && Core.atlas.isFound(b.icon(Icon.full))); + public static final Predicate wallsOptional = b -> b == Blocks.air || ((!b.synthetic() && !(b instanceof Floor)) && Core.atlas.isFound(b.icon(Icon.full))); + public static final Predicate wallsOresOptional = b -> b == Blocks.air || (((!b.synthetic() && !(b instanceof Floor)) || (b instanceof OverlayFloor)) && Core.atlas.isFound(b.icon(Icon.full))); + public static final Predicate oresOnly = b -> b instanceof OverlayFloor && Core.atlas.isFound(b.icon(Icon.full)); + + public abstract void build(Table table); + + public Runnable changed = () -> { + }; + + static class SliderOption extends FilterOption{ + final String name; + final FloatProvider getter; + final FloatConsumer setter; + final float min, max; + + SliderOption(String name, FloatProvider getter, FloatConsumer setter, float min, float max){ + this.name = name; + this.getter = getter; + this.setter = setter; + this.min = min; + this.max = max; + } + + @Override + public void build(Table table){ + table.add("$filter.option." + name); + table.row(); + Slider slider = table.addSlider(min, max, (max - min) / 200f, setter).growX().get(); + slider.setValue(getter.get()); + if(updateEditorOnChange){ + slider.changed(changed); + }else{ + slider.released(changed); + } + } + } + + static class BlockOption extends FilterOption{ + final String name; + final Supplier supplier; + final Consumer consumer; + final Predicate filter; + + BlockOption(String name, Supplier supplier, Consumer consumer, Predicate filter){ + this.name = name; + this.supplier = supplier; + this.consumer = consumer; + this.filter = filter; + } + + @Override + public void build(Table table){ + table.addButton(b -> b.addImage(supplier.get().icon(Icon.small)).update(i -> ((TextureRegionDrawable)i.getDrawable()) + .setRegion(supplier.get() == Blocks.air ? Core.atlas.find("icon-none") : supplier.get().icon(Icon.small))).size(8 * 3), () -> { + FloatingDialog dialog = new FloatingDialog(""); + dialog.setFillParent(false); + int i = 0; + for(Block block : Vars.content.blocks()){ + if(!filter.test(block)) continue; + + dialog.cont.addImage(block == Blocks.air ? Core.atlas.find("icon-none-small") : block.icon(Icon.medium)).size(8 * 4).pad(3).get().clicked(() -> { + consumer.accept(block); + dialog.hide(); + changed.run(); + }); + if(++i % 10 == 0) dialog.cont.row(); + } + + dialog.show(); + }).pad(4).margin(12f); + + table.add("$filter.option." + name); + } + } +} diff --git a/core/src/io/anuke/mindustry/editor/generation/GenerateFilter.java b/core/src/io/anuke/mindustry/editor/generation/GenerateFilter.java new file mode 100644 index 0000000000..9105a7b4e8 --- /dev/null +++ b/core/src/io/anuke/mindustry/editor/generation/GenerateFilter.java @@ -0,0 +1,99 @@ +package io.anuke.mindustry.editor.generation; + +import io.anuke.arc.Core; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.util.Pack; +import io.anuke.arc.util.noise.RidgedPerlin; +import io.anuke.arc.util.noise.Simplex; +import io.anuke.mindustry.content.Blocks; +import io.anuke.mindustry.editor.MapEditor; +import io.anuke.mindustry.editor.MapGenerateDialog.GenTile; +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.blocks.Floor; + +public abstract class GenerateFilter{ + protected transient float o = (float)(Math.random() * 10000000.0); + protected transient long seed; + protected transient GenerateInput in; + + public FilterOption[] options; + + protected abstract void apply(); + + protected float noise(float x, float y, float scl, float mag){ + return (float)in.noise.octaveNoise2D(1f, 0f, 1f / scl, x + o, y + o) * mag; + } + + protected float noise(float x, float y, float scl, float mag, float octaves, float persistence){ + return (float)in.noise.octaveNoise2D(octaves, persistence, 1f / scl, x + o, y + o) * mag; + } + + protected float rnoise(float x, float y, float scl, float mag){ + return in.pnoise.getValue((int)(x + o), (int)(y + o), 1f / scl) * mag; + } + + public void randomize(){ + seed = Mathf.random(99999999); + } + + protected float chance(){ + return Mathf.randomSeed(Pack.longInt(in.x, in.y + (int)o)); + } + + public void options(FilterOption... options){ + this.options = options; + } + + public String name(){ + return Core.bundle.get("filter." + getClass().getSimpleName().toLowerCase().replace("filter", ""), getClass().getSimpleName().replace("Filter", "")); + } + + public final void apply(GenerateInput in){ + this.in = in; + apply(); + //remove extra ores on liquids + if(((Floor)in.floor).isLiquid){ + in.ore = Blocks.air; + } + } + + public static class GenerateInput{ + public Floor srcfloor; + public Block srcblock; + public Block srcore; + public int x, y, width, height, scaling; + + public MapEditor editor; + public Block floor, block, ore; + + Simplex noise = new Simplex(); + RidgedPerlin pnoise = new RidgedPerlin(0, 1); + TileProvider buffer; + + public void begin(MapEditor editor, int x, int y, Block floor, Block block, Block ore){ + this.editor = editor; + this.floor = this.srcfloor = (Floor)floor; + this.block = this.srcblock = block; + this.ore = srcore = ore; + this.x = x; + this.y = y; + } + + public void setFilter(GenerateFilter filter, int width, int height, int scaling, TileProvider buffer){ + this.buffer = buffer; + this.width = width; + this.height = height; + this.scaling = scaling; + noise.setSeed(filter.seed); + pnoise.setSeed((int)(filter.seed + 1)); + } + + GenTile tile(float x, float y){ + return buffer.get(Mathf.clamp((int)x, 0, width - 1), Mathf.clamp((int)y, 0, height - 1)); + } + + public interface TileProvider{ + GenTile get(int x, int y); + } + } +} diff --git a/core/src/io/anuke/mindustry/editor/generation/MedianFilter.java b/core/src/io/anuke/mindustry/editor/generation/MedianFilter.java new file mode 100644 index 0000000000..548b861f8c --- /dev/null +++ b/core/src/io/anuke/mindustry/editor/generation/MedianFilter.java @@ -0,0 +1,46 @@ +package io.anuke.mindustry.editor.generation; + +import io.anuke.arc.collection.IntArray; +import io.anuke.arc.math.Mathf; +import io.anuke.mindustry.editor.MapGenerateDialog.GenTile; +import io.anuke.mindustry.editor.generation.FilterOption.SliderOption; + +import static io.anuke.mindustry.Vars.content; + +public class MedianFilter extends GenerateFilter{ + float radius = 2; + float percentile = 0.5f; + IntArray blocks = new IntArray(), floors = new IntArray(); + + { + options( + new SliderOption("radius", () -> radius, f -> radius = f, 1f, 12f), + new SliderOption("percentile", () -> percentile, f -> percentile = f, 0f, 1f) + ); + } + + @Override + public void apply(){ + int rad = (int)radius; + blocks.clear(); + floors.clear(); + for(int x = -rad; x <= rad; x++){ + for(int y = -rad; y <= rad; y++){ + if(Mathf.dst2(x, y) > rad*rad) continue; + + GenTile tile = in.tile(in.x + x, in.y + y); + blocks.add(tile.block); + floors.add(tile.floor); + } + } + + floors.sort(); + blocks.sort(); + + int index = Math.min((int)(floors.size * percentile), floors.size - 1); + int floor = floors.get(index), block = blocks.get(index); + + in.floor = content.block(floor); + if(!content.block(block).synthetic() && !in.block.synthetic()) in.block = content.block(block); + } +} diff --git a/core/src/io/anuke/mindustry/editor/generation/NoiseFilter.java b/core/src/io/anuke/mindustry/editor/generation/NoiseFilter.java new file mode 100644 index 0000000000..6a6871df91 --- /dev/null +++ b/core/src/io/anuke/mindustry/editor/generation/NoiseFilter.java @@ -0,0 +1,35 @@ +package io.anuke.mindustry.editor.generation; + +import io.anuke.mindustry.content.Blocks; +import io.anuke.mindustry.editor.generation.FilterOption.BlockOption; +import io.anuke.mindustry.editor.generation.FilterOption.SliderOption; +import io.anuke.mindustry.world.Block; + +import static io.anuke.mindustry.editor.generation.FilterOption.floorsOnly; +import static io.anuke.mindustry.editor.generation.FilterOption.wallsOnly; + +public class NoiseFilter extends GenerateFilter{ + float scl = 40, threshold = 0.5f, octaves = 3f, falloff = 0.5f; + Block floor = Blocks.stone, block = Blocks.rocks; + + { + options( + new SliderOption("scale", () -> scl, f -> scl = f, 1f, 500f), + new SliderOption("threshold", () -> threshold, f -> threshold = f, 0f, 1f), + new SliderOption("octaves", () -> octaves, f -> octaves = f, 1f, 10f), + new SliderOption("falloff", () -> falloff, f -> falloff = f, 0f, 1f), + new BlockOption("floor", () -> floor, b -> floor = b, floorsOnly), + new BlockOption("wall", () -> block, b -> block = b, wallsOnly) + ); + } + + @Override + public void apply(){ + float noise = noise(in.x, in.y, scl, 1f, octaves, falloff); + + if(noise > threshold){ + in.floor = floor; + if(wallsOnly.test(in.srcblock)) in.block = block; + } + } +} diff --git a/core/src/io/anuke/mindustry/editor/generation/OreFilter.java b/core/src/io/anuke/mindustry/editor/generation/OreFilter.java new file mode 100644 index 0000000000..b6fb04b196 --- /dev/null +++ b/core/src/io/anuke/mindustry/editor/generation/OreFilter.java @@ -0,0 +1,32 @@ +package io.anuke.mindustry.editor.generation; + +import io.anuke.mindustry.content.Blocks; +import io.anuke.mindustry.editor.generation.FilterOption.SliderOption; +import io.anuke.mindustry.world.Block; + +import static io.anuke.mindustry.editor.generation.FilterOption.BlockOption; +import static io.anuke.mindustry.editor.generation.FilterOption.oresOnly; + +public class OreFilter extends GenerateFilter{ + public float scl = 50, threshold = 0.72f, octaves = 3f, falloff = 0.4f; + public Block ore = Blocks.oreCopper; + + { + options( + new SliderOption("scale", () -> scl, f -> scl = f, 1f, 500f), + new SliderOption("threshold", () -> threshold, f -> threshold = f, 0f, 1f), + new SliderOption("octaves", () -> octaves, f -> octaves = f, 1f, 10f), + new SliderOption("falloff", () -> falloff, f -> falloff = f, 0f, 1f), + new BlockOption("ore", () -> ore, b -> ore = b, oresOnly) + ); + } + + @Override + public void apply(){ + float noise = noise(in.x, in.y, scl, 1f, octaves, falloff); + + if(noise > threshold){ + in.ore = ore; + } + } +} diff --git a/core/src/io/anuke/mindustry/editor/generation/RiverNoiseFilter.java b/core/src/io/anuke/mindustry/editor/generation/RiverNoiseFilter.java new file mode 100644 index 0000000000..2cfb256b41 --- /dev/null +++ b/core/src/io/anuke/mindustry/editor/generation/RiverNoiseFilter.java @@ -0,0 +1,42 @@ +package io.anuke.mindustry.editor.generation; + +import io.anuke.mindustry.content.Blocks; +import io.anuke.mindustry.editor.generation.FilterOption.BlockOption; +import io.anuke.mindustry.editor.generation.FilterOption.SliderOption; +import io.anuke.mindustry.world.Block; + +import static io.anuke.mindustry.editor.generation.FilterOption.floorsOnly; +import static io.anuke.mindustry.editor.generation.FilterOption.wallsOnly; + +public class RiverNoiseFilter extends GenerateFilter{ + float scl = 40, threshold = 0f, threshold2 = 0.1f; + Block floor = Blocks.water, floor2 = Blocks.deepwater, block = Blocks.sandRocks; + + { + options( + new SliderOption("scale", () -> scl, f -> scl = f, 1f, 500f), + new SliderOption("threshold", () -> threshold, f -> threshold = f, 0f, 1f), + new SliderOption("threshold2", () -> threshold2, f -> threshold2 = f, 0f, 1f), + new BlockOption("block", () -> block, b -> block = b, wallsOnly), + new BlockOption("floor", () -> floor, b -> floor = b, floorsOnly), + new BlockOption("floor2", () -> floor2, b -> floor2 = b, floorsOnly) + ); + } + + @Override + public void apply(){ + float noise = rnoise(in.x, in.y, scl, 1f); + + if(noise >= threshold){ + in.floor = floor; + + if(in.srcblock.solid){ + in.block = block; + } + + if(noise >= threshold2){ + in.floor = floor2; + } + } + } +} diff --git a/core/src/io/anuke/mindustry/editor/generation/ScatterFilter.java b/core/src/io/anuke/mindustry/editor/generation/ScatterFilter.java new file mode 100644 index 0000000000..ef00049bba --- /dev/null +++ b/core/src/io/anuke/mindustry/editor/generation/ScatterFilter.java @@ -0,0 +1,38 @@ +package io.anuke.mindustry.editor.generation; + +import io.anuke.mindustry.content.Blocks; +import io.anuke.mindustry.editor.generation.FilterOption.BlockOption; +import io.anuke.mindustry.editor.generation.FilterOption.SliderOption; +import io.anuke.mindustry.world.Block; + +import static io.anuke.mindustry.editor.generation.FilterOption.*; + +public class ScatterFilter extends GenerateFilter{ + float chance = 0.1f; + Block flooronto = Blocks.air, floor = Blocks.air, block = Blocks.air; + + { + options( + new SliderOption("chance", () -> chance, f -> chance = f, 0f, 1f), + new BlockOption("flooronto", () -> flooronto, b -> flooronto = b, floorsOptional), + new BlockOption("floor", () -> floor, b -> floor = b, floorsOptional), + new BlockOption("block", () -> block, b -> block = b, wallsOresOptional) + ); + } + + @Override + public void apply(){ + + if(block != Blocks.air && (in.srcfloor == flooronto || flooronto == Blocks.air) && in.srcblock == Blocks.air && chance() <= chance){ + if(!block.isOverlay()){ + in.block = block; + }else{ + in.ore = block; + } + } + + if(floor != Blocks.air && (in.srcfloor == flooronto || flooronto == Blocks.air) && chance() <= chance){ + in.floor = floor; + } + } +} diff --git a/core/src/io/anuke/mindustry/editor/generation/TerrainFilter.java b/core/src/io/anuke/mindustry/editor/generation/TerrainFilter.java new file mode 100644 index 0000000000..ab8e07c3cb --- /dev/null +++ b/core/src/io/anuke/mindustry/editor/generation/TerrainFilter.java @@ -0,0 +1,42 @@ +package io.anuke.mindustry.editor.generation; + +import io.anuke.arc.math.Mathf; +import io.anuke.mindustry.content.Blocks; +import io.anuke.mindustry.editor.generation.FilterOption.BlockOption; +import io.anuke.mindustry.editor.generation.FilterOption.SliderOption; +import io.anuke.mindustry.world.Block; + +import static io.anuke.mindustry.editor.generation.FilterOption.floorsOnly; +import static io.anuke.mindustry.editor.generation.FilterOption.wallsOnly; + +public class TerrainFilter extends GenerateFilter{ + float scl = 40, threshold = 0.9f, octaves = 3f, falloff = 0.5f, magnitude = 1f, circleScl = 2.1f; + Block floor = Blocks.stone, block = Blocks.rocks; + + { + options( + new SliderOption("scale", () -> scl, f -> scl = f, 1f, 500f), + new SliderOption("mag", () -> magnitude, f -> magnitude = f, 0f, 2f), + new SliderOption("threshold", () -> threshold, f -> threshold = f, 0f, 1f), + new SliderOption("circle-scale", () -> circleScl, f -> circleScl = f, 0f, 3f), + new SliderOption("octaves", () -> octaves, f -> octaves = f, 1f, 10f), + new SliderOption("falloff", () -> falloff, f -> falloff = f, 0f, 1f), + new BlockOption("floor", () -> floor, b -> floor = b, floorsOnly), + new BlockOption("wall", () -> block, b -> block = b, wallsOnly) + ); + } + + @Override + public void apply(){ + float noise = noise(in.x, in.y, scl, magnitude, octaves, falloff) + Mathf.dst((float)in.x / in.editor.width(), (float)in.y / in.editor.height(), 0.5f, 0.5f) * circleScl; + + in.floor = floor; + in.ore = Blocks.air; + + if(noise >= threshold){ + in.block = block; + }else{ + in.block = Blocks.air; + } + } +} \ No newline at end of file diff --git a/core/src/io/anuke/mindustry/entities/Bullet.java b/core/src/io/anuke/mindustry/entities/Bullet.java deleted file mode 100644 index 279993b4c4..0000000000 --- a/core/src/io/anuke/mindustry/entities/Bullet.java +++ /dev/null @@ -1,76 +0,0 @@ -package io.anuke.mindustry.entities; - -import io.anuke.mindustry.entities.enemies.Enemy; -import io.anuke.mindustry.world.Tile; -import io.anuke.ucore.entities.BulletEntity; -import io.anuke.ucore.entities.Entity; -import io.anuke.ucore.util.Timer; - -import static io.anuke.mindustry.Vars.*; - -public class Bullet extends BulletEntity{ - public Timer timer = new Timer(3); - - public Bullet(BulletType type, Entity owner, float x, float y, float angle){ - super(type, owner, angle); - set(x, y); - this.type = type; - } - - public void draw(){ - //interpolate position linearly at low tick speeds - if(SyncEntity.isSmoothing()){ - x += threads.getFramesSinceUpdate() * velocity.x; - y += threads.getFramesSinceUpdate() * velocity.y; - - type.draw(this); - - x -= threads.getFramesSinceUpdate() * velocity.x; - y -= threads.getFramesSinceUpdate() * velocity.y; - }else{ - type.draw(this); - } - } - - public float drawSize(){ - return 8; - } - - public boolean collidesTiles(){ - return owner instanceof Enemy; - } - - @Override - public void update(){ - super.update(); - - if (collidesTiles()) { - world.raycastEach(world.toTile(lastX), world.toTile(lastY), world.toTile(x), world.toTile(y), (x, y) -> { - - Tile tile = world.tile(x, y); - if (tile == null) return false; - tile = tile.target(); - - if (tile.entity != null && tile.entity.collide(this) && !tile.entity.dead) { - tile.entity.collision(this); - remove(); - type.hit(this); - - return true; - } - - return false; - }); - } - } - - @Override - public int getDamage(){ - return damage == -1 ? type.damage : damage; - } - - @Override - public Bullet add(){ - return super.add(bulletGroup); - } -} diff --git a/core/src/io/anuke/mindustry/entities/BulletType.java b/core/src/io/anuke/mindustry/entities/BulletType.java deleted file mode 100644 index 592854f9b2..0000000000 --- a/core/src/io/anuke/mindustry/entities/BulletType.java +++ /dev/null @@ -1,485 +0,0 @@ -package io.anuke.mindustry.entities; - -import com.badlogic.gdx.graphics.Color; -import io.anuke.mindustry.entities.effect.DamageArea; -import io.anuke.mindustry.entities.effect.EMP; -import io.anuke.mindustry.entities.enemies.Enemy; -import io.anuke.mindustry.graphics.Fx; -import io.anuke.ucore.graphics.Draw; -import io.anuke.ucore.core.Effects; -import io.anuke.ucore.core.Timers; -import io.anuke.ucore.entities.BaseBulletType; -import io.anuke.ucore.graphics.Lines; -import io.anuke.ucore.util.Angles; -import io.anuke.ucore.util.Mathf; - -import static io.anuke.mindustry.graphics.Fx.*; - -public abstract class BulletType extends BaseBulletType{ - - public static final BulletType - - none = new BulletType(0f, 0){ - public void draw(Bullet b){} - }, - stone = new BulletType(1.5f, 2){ - public void draw(Bullet b){ - Draw.colorl(0.64f); - Draw.rect("blank", b.x, b.y, 2f, 2f); - Draw.reset(); - } - }, - iron = new BulletType(1.7f, 2){ - public void draw(Bullet b){ - Draw.color(Color.GRAY); - Draw.rect("bullet", b.x, b.y, b.angle()); - Draw.reset(); - } - }, - chain = new BulletType(2f, 8){ - public void draw(Bullet b){ - Draw.color(whiteOrange); - Draw.rect("chainbullet", b.x, b.y, b.angle()); - Draw.reset(); - } - }, - sniper = new BulletType(3f, 25){ - public void draw(Bullet b){ - Draw.color(Color.LIGHT_GRAY); - Lines.stroke(1f); - Lines.lineAngleCenter(b.x, b.y, b.angle(), 3f); - Draw.reset(); - } - - public void update(Bullet b){ - if(b.timer.get(0, 4)){ - Effects.effect(Fx.railsmoke, b.x, b.y); - } - } - }, - emp = new BulletType(1.6f, 8){ - { - lifetime = 50f; - hitsize = 6f; - } - - public void draw(Bullet b){ - float rad = 6f + Mathf.sin(Timers.time(), 5f, 2f); - - Draw.color(Color.SKY); - Lines.circle(b.x, b.y, 4f); - Draw.rect("circle", b.x, b.y, rad, rad); - Draw.reset(); - } - - public void update(Bullet b){ - if(b.timer.get(0, 2)){ - Effects.effect(Fx.empspark, b.x + Mathf.range(2), b.y + Mathf.range(2)); - } - } - - public void despawned(Bullet b){ - hit(b); - } - - public void hit(Bullet b, float hitx, float hity){ - Timers.run(5f, ()-> new EMP(b.x, b.y, b.getDamage()).add()); - Effects.effect(Fx.empshockwave, b); - Effects.shake(3f, 3f, b); - } - }, - //TODO better visuals for shell - shell = new BulletType(1.1f, 60){ - { - lifetime = 110f; - hitsize = 11f; - } - - public void draw(Bullet b){ - float rad = 8f; - Draw.color(Color.ORANGE); - Draw.color(Color.GRAY); - Draw.rect("circle", b.x, b.y, rad, rad); - rad += Mathf.sin(Timers.time(), 3f, 1f); - Draw.color(Color.ORANGE); - Draw.rect("circle", b.x, b.y, rad/1.7f, rad/1.7f); - Draw.reset(); - } - - public void update(Bullet b){ - if(b.timer.get(0, 7)){ - Effects.effect(Fx.smoke, b.x + Mathf.range(2), b.y + Mathf.range(2)); - } - } - - public void despawned(Bullet b){ - hit(b); - } - - public void hit(Bullet b, float hitx, float hity){ - Effects.shake(3f, 3f, b); - - Effects.effect(Fx.shellsmoke, b); - Effects.effect(Fx.shellexplosion, b); - - DamageArea.damage(!(b.owner instanceof Enemy), b.x, b.y, 25f, (int)(damage * 2f/3f)); - } - }, - flak = new BulletType(2.9f, 8) { - - public void init(Bullet b) { - b.velocity.scl(Mathf.random(0.6f, 1f)); - } - - public void update(Bullet b){ - if(b.timer.get(0, 7)){ - Effects.effect(Fx.smoke, b.x + Mathf.range(2), b.y + Mathf.range(2)); - } - } - - public void draw(Bullet b) { - Draw.color(Color.GRAY); - Lines.stroke(3f); - Lines.lineAngleCenter(b.x, b.y, b.angle(), 2f); - Lines.stroke(1.5f); - Lines.lineAngleCenter(b.x, b.y, b.angle(), 5f); - Draw.reset(); - } - - public void hit(Bullet b, float hitx, float hity) { - Effects.effect(shellsmoke, b); - for(int i = 0; i < 3; i ++){ - Bullet bullet = new Bullet(flakspark, b.owner, hitx, hity, b.angle() + Mathf.range(120f)); - bullet.add(); - } - } - - public void despawned(Bullet b) { - hit(b, b.x, b.y); - } - }, - flakspark = new BulletType(2f, 2) { - { - drag = 0.05f; - } - - public void init(Bullet b) { - b.velocity.scl(Mathf.random(0.6f, 1f)); - } - - public void draw(Bullet b) { - Draw.color(Color.LIGHT_GRAY, Color.GRAY, b.fin()); - Lines.stroke(2f - b.fin()); - Lines.lineAngleCenter(b.x, b.y, b.angle(), 2f); - Draw.reset(); - } - }, - titanshell = new BulletType(1.8f, 38){ - { - lifetime = 70f; - hitsize = 15f; - } - - public void draw(Bullet b){ - Draw.color(whiteOrange); - Draw.rect("titanshell", b.x, b.y, b.angle()); - Draw.reset(); - } - - public void update(Bullet b){ - if(b.timer.get(0, 4)){ - Effects.effect(Fx.smoke, b.x + Mathf.range(2), b.y + Mathf.range(2)); - } - } - - public void despawned(Bullet b){ - hit(b); - } - - public void hit(Bullet b, float hitx, float hity){ - Effects.shake(3f, 3f, b); - - Effects.effect(Fx.shellsmoke, b); - Effects.effect(Fx.shockwaveSmall, b); - - DamageArea.damage(!(b.owner instanceof Enemy), b.x, b.y, 50f, (int)(damage * 2f/3f)); - } - }, - yellowshell = new BulletType(1.2f, 20){ - { - lifetime = 60f; - hitsize = 11f; - } - - public void draw(Bullet b){ - Draw.color(whiteYellow); - Draw.rect("titanshell", b.x, b.y, b.angle()); - Draw.reset(); - } - - public void update(Bullet b){ - if(b.timer.get(0, 4)){ - Effects.effect(Fx.smoke, b.x + Mathf.range(2), b.y + Mathf.range(2)); - } - } - - public void despawned(Bullet b){ - hit(b); - } - - public void hit(Bullet b, float hitx, float hity){ - Effects.shake(3f, 3f, b); - - Effects.effect(Fx.shellsmoke, b); - Effects.effect(Fx.shockwaveSmall, b); - - DamageArea.damage(!(b.owner instanceof Enemy), b.x, b.y, 25f, (int)(damage * 2f/3f)); - } - }, - blast = new BulletType(1.1f, 90){ - { - lifetime = 0f; - hitsize = 8f; - speed = 0f; - } - - public void despawned(Bullet b){ - hit(b); - } - - public void hit(Bullet b, float hitx, float hity){ - Effects.shake(3f, 3f, b); - - Effects.effect(Fx.blastsmoke, b); - Effects.effect(Fx.blastexplosion, b); - - //TODO remove translation() usage - Angles.circleVectors(30, 6f, (nx, ny) -> { - float ang = Mathf.atan2(nx, ny); - Bullet o = new Bullet(blastshot, b.owner, b.x + nx, b.y + ny, ang).add(); - o.damage = b.damage/9; - }); - } - - public void draw(Bullet b){} - }, - blastshot = new BulletType(1.6f, 6){ - { - lifetime = 7f; - } - public void draw(Bullet b){} - }, - small = new BulletType(1.5f, 2){ - public void draw(Bullet b){ - Draw.color(glowy); - Draw.rect("shot", b.x, b.y, b.angle() - 45); - Draw.reset(); - } - }, - smallSlow = new BulletType(1.2f, 2){ - public void draw(Bullet b){ - Draw.color(Color.ORANGE); - Draw.rect("shot", b.x, b.y, b.angle() - 45); - Draw.reset(); - } - }, - purple = new BulletType(1.6f, 2){ - Color color = new Color(0x8b5ec9ff); - - public void draw(Bullet b){ - Draw.color(color); - Draw.rect("bullet", b.x, b.y, b.angle()); - Draw.reset(); - } - }, - flame = new BulletType(0.7f, 5){ //for turrets - public void draw(Bullet b){ - Draw.color(Color.YELLOW, Color.SCARLET, b.time/lifetime); - float size = 6f-b.time/lifetime*5f; - Draw.rect("circle", b.x, b.y, size, size); - Draw.reset(); - } - }, - plasmaflame = new BulletType(0.8f, 17){ - { - lifetime = 65f; - } - public void draw(Bullet b){ - Draw.color(Color.valueOf("efa66c"), Color.valueOf("72deaf"), b.time/lifetime); - float size = 7f-b.time/lifetime*6f; - Draw.rect("circle", b.x, b.y, size, size); - Draw.reset(); - } - }, - flameshot = new BulletType(0.5f, 3){ //for enemies - public void draw(Bullet b){ - Draw.color(Color.ORANGE, Color.SCARLET, b.time/lifetime); - float size = 6f-b.time/lifetime*5f; - Draw.rect("circle", b.x, b.y, size, size); - Draw.reset(); - } - }, - shot = new BulletType(2.7f, 5){ - { - lifetime = 40; - } - - public void draw(Bullet b){ - Draw.color(Color.WHITE, lightOrange, b.fout()/2f + 0.25f); - Lines.stroke(1.5f); - Lines.lineAngle(b.x, b.y, b.angle(), 3f); - Draw.reset(); - } - }, - spread = new BulletType(2.4f, 9) { - { - lifetime = 70; - } - - public void draw(Bullet b) { - float size = 3f - b.fin()*1f; - - Draw.color(Color.PURPLE, Color.WHITE, 0.8f); - Lines.stroke(1f); - Lines.circle(b.x, b.y, size); - Draw.reset(); - } - }, - cluster = new BulletType(4.5f, 12){ - { - lifetime = 60; - drag = 0.05f; - } - - public void draw(Bullet b){ - Lines.stroke(2f); - Draw.color(lightOrange, Color.WHITE, 0.4f); - Lines.poly(b.x, b.y, 3, 1.6f, b.angle()); - Lines.stroke(1f); - Draw.color(Color.WHITE, lightOrange, b.fin()/2f); - Draw.alpha(b.fin()); - Lines.spikes(b.x, b.y, 1.5f, 2f, 6); - Draw.reset(); - } - - public void despawned(Bullet b){ - hit(b); - } - - public void hit(Bullet b, float hitx, float hity){ - Effects.shake(1.5f, 1.5f, b); - - Effects.effect(Fx.clusterbomb, b); - - DamageArea.damage(!(b.owner instanceof Enemy), b.x, b.y, 35f, damage); - } - }, - vulcan = new BulletType(4.5f, 12) { - { - lifetime = 50; - } - - public void init(Bullet b) { - Timers.reset(b, "smoke", Mathf.random(4f)); - } - - public void draw(Bullet b){ - Draw.color(lightGray); - Lines.stroke(1f); - Lines.lineAngleCenter(b.x, b.y, b.angle(), 2f); - Draw.reset(); - } - - public void update(Bullet b){ - if(b.timer.get(0, 4)){ - Effects.effect(Fx.chainsmoke, b.x, b.y); - } - } - }, - shockshell = new BulletType(5.5f, 11) { - - { - drag = 0.03f; - lifetime = 30f; - } - - public void init(Bullet b) { - b.velocity.scl(Mathf.random(0.5f, 1f)); - } - - public void draw(Bullet b) { - Draw.color(Color.WHITE, Color.ORANGE, b.fin()); - Lines.stroke(2f); - Lines.lineAngleCenter(b.x, b.y, b.angle(), b.fout()*5f); - Draw.reset(); - } - - public void despawned(Bullet b) { - hit(b); - } - - public void hit(Bullet b, float hitx, float hity) { - for(int i = 0; i < 4; i ++){ - Bullet bullet = new Bullet(scrap, b.owner, b.x, b.y, b.angle() + Mathf.range(80f)); - bullet.add(); - } - } - }, - scrap = new BulletType(2f, 3) { - { - drag = 0.06f; - lifetime = 30f; - } - - public void init(Bullet b) { - b.velocity.scl(Mathf.random(0.5f, 1f)); - } - - public void draw(Bullet b) { - Draw.color(Color.WHITE, Color.ORANGE, b.fin()); - Lines.stroke(1f); - Lines.lineAngleCenter(b.x, b.y, b.angle(), b.fout()*4f); - Draw.reset(); - } - }, - beamlaser = new BulletType(0.001f, 38) { - float length = 230f; - { - drawSize = length*2f+20f; - lifetime = 15f; - } - - public void init(Bullet b) { - DamageArea.damageLine(b.owner, Fx.beamhit, b.x, b.y, b.angle(), length, damage); - } - - public void draw(Bullet b) { - float f = b.fout()*1.5f; - - Draw.color(beam); - Draw.rect("circle", b.x, b.y, 6f*f, 6f*f); - Lines.stroke(3f * f); - Lines.lineAngle(b.x, b.y, b.angle(), length); - - Lines.stroke(2f * f); - Lines.lineAngle(b.x, b.y, b.angle(), length + 6f); - Lines.stroke(1f * f); - Lines.lineAngle(b.x, b.y, b.angle(), length + 12f); - - Draw.color(beamLight); - Lines.stroke(1.5f * f); - Draw.rect("circle", b.x, b.y, 3f*f, 3f*f); - Lines.lineAngle(b.x, b.y, b.angle(), length); - } - }; - - private BulletType(float speed, int damage){ - this.speed = speed; - this.damage = damage; - } - - @Override - public void hit(Bullet b, float hitx, float hity){ - Effects.effect(Fx.hit, hitx, hity); - } -} diff --git a/core/src/io/anuke/mindustry/entities/Damage.java b/core/src/io/anuke/mindustry/entities/Damage.java new file mode 100644 index 0000000000..474a9747e4 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/Damage.java @@ -0,0 +1,278 @@ +package io.anuke.mindustry.entities; + +import io.anuke.annotations.Annotations.Struct; +import io.anuke.arc.collection.GridBits; +import io.anuke.arc.collection.IntQueue; +import io.anuke.arc.function.*; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.*; +import io.anuke.arc.util.Time; +import io.anuke.mindustry.content.Bullets; +import io.anuke.mindustry.content.Fx; +import io.anuke.mindustry.entities.Effects.Effect; +import io.anuke.mindustry.entities.bullet.Bullet; +import io.anuke.mindustry.entities.effect.Fire; +import io.anuke.mindustry.entities.effect.Lightning; +import io.anuke.mindustry.entities.type.Unit; +import io.anuke.mindustry.game.Team; +import io.anuke.mindustry.gen.Call; +import io.anuke.mindustry.gen.PropCell; +import io.anuke.mindustry.graphics.Pal; +import io.anuke.mindustry.world.Tile; + +import static io.anuke.mindustry.Vars.*; + +/** Utility class for damaging in an area. */ +public class Damage{ + private static Rectangle rect = new Rectangle(); + private static Rectangle hitrect = new Rectangle(); + private static Vector2 tr = new Vector2(); + private static GridBits bits = new GridBits(30, 30); + private static IntQueue propagation = new IntQueue(); + + /** Creates a dynamic explosion based on specified parameters. */ + public static void dynamicExplosion(float x, float y, float flammability, float explosiveness, float power, float radius, Color color){ + for(int i = 0; i < Mathf.clamp(power / 20, 0, 6); i++){ + int branches = 5 + Mathf.clamp((int)(power / 30), 1, 20); + Time.run(i * 2f + Mathf.random(4f), () -> Lightning.create(Team.none, Pal.power, 3, + x, y, Mathf.random(360f), branches + Mathf.range(2))); + } + + for(int i = 0; i < Mathf.clamp(flammability / 4, 0, 30); i++){ + Time.run(i / 2f, () -> Call.createBullet(Bullets.fireball, x, y, Mathf.random(360f))); + } + + int waves = Mathf.clamp((int)(explosiveness / 4), 0, 30); + + for(int i = 0; i < waves; i++){ + int f = i; + Time.run(i * 2f, () -> { + Damage.damage(x, y, Mathf.clamp(radius + explosiveness, 0, 50f) * ((f + 1f) / waves), explosiveness / 2f); + Effects.effect(Fx.blockExplosionSmoke, x + Mathf.range(radius), y + Mathf.range(radius)); + }); + } + + if(explosiveness > 15f){ + Effects.effect(Fx.shockwave, x, y); + } + + if(explosiveness > 30f){ + Effects.effect(Fx.bigShockwave, x, y); + } + + float shake = Math.min(explosiveness / 4f + 3f, 9f); + Effects.shake(shake, shake, x, y); + Effects.effect(Fx.dynamicExplosion, x, y, radius / 8f); + } + + public static void createIncend(float x, float y, float range, int amount){ + for(int i = 0; i < amount; i++){ + float cx = x + Mathf.range(range); + float cy = y + Mathf.range(range); + Tile tile = world.tileWorld(cx, cy); + if(tile != null){ + Fire.create(tile); + } + } + } + + public static void collideLine(Bullet hitter, Team team, Effect effect, float x, float y, float angle, float length){ + collideLine(hitter, team, effect, x, y, angle, length, false); + } + + /** + * Damages entities in a line. + * Only enemies of the specified team are damaged. + */ + public static void collideLine(Bullet hitter, Team team, Effect effect, float x, float y, float angle, float length, boolean large){ + tr.trns(angle, length); + IntPositionConsumer collider = (cx, cy) -> { + Tile tile = world.ltile(cx, cy); + if(tile != null && tile.entity != null && tile.getTeamID() != team.ordinal() && tile.entity.collide(hitter)){ + tile.entity.collision(hitter); + hitter.getBulletType().hit(hitter, tile.worldx(), tile.worldy()); + } + }; + + world.raycastEachWorld(x, y, x + tr.x, y + tr.y, (cx, cy) -> { + collider.accept(cx, cy); + if(large){ + for(Point2 p : Geometry.d4){ + collider.accept(cx + p.x, cy + p.y); + } + } + return false; + }); + + rect.setPosition(x, y).setSize(tr.x, tr.y); + float x2 = tr.x + x, y2 = tr.y + y; + + if(rect.width < 0){ + rect.x += rect.width; + rect.width *= -1; + } + + if(rect.height < 0){ + rect.y += rect.height; + rect.height *= -1; + } + + float expand = 3f; + + rect.y -= expand; + rect.x -= expand; + rect.width += expand * 2; + rect.height += expand * 2; + + Consumer cons = e -> { + e.hitbox(hitrect); + Rectangle other = hitrect; + other.y -= expand; + other.x -= expand; + other.width += expand * 2; + other.height += expand * 2; + + Vector2 vec = Geometry.raycastRect(x, y, x2, y2, other); + + if(vec != null){ + Effects.effect(effect, vec.x, vec.y); + e.collision(hitter, vec.x, vec.y); + hitter.collision(e, vec.x, vec.y); + } + }; + + Units.nearbyEnemies(team, rect, cons); + } + + /** Damages all entities and blocks in a radius that are enemies of the team. */ + public static void damageUnits(Team team, float x, float y, float size, float damage, Predicate predicate, Consumer acceptor){ + Consumer cons = entity -> { + if(!predicate.test(entity)) return; + + entity.hitbox(hitrect); + if(!hitrect.overlaps(rect)){ + return; + } + entity.damage(damage); + acceptor.accept(entity); + }; + + rect.setSize(size * 2).setCenter(x, y); + if(team != null){ + Units.nearbyEnemies(team, rect, cons); + }else{ + Units.nearby(rect, cons); + } + } + + /** Damages everything in a radius. */ + public static void damage(float x, float y, float radius, float damage){ + damage(null, x, y, radius, damage, false); + } + + /** Damages all entities and blocks in a radius that are enemies of the team. */ + public static void damage(Team team, float x, float y, float radius, float damage){ + damage(team, x, y, radius, damage, false); + } + + /** Damages all entities and blocks in a radius that are enemies of the team. */ + public static void damage(Team team, float x, float y, float radius, float damage, boolean complete){ + Consumer cons = entity -> { + if(entity.getTeam() == team || entity.dst(x, y) > radius){ + return; + } + float amount = calculateDamage(x, y, entity.x, entity.y, radius, damage); + entity.damage(amount); + //TODO better velocity displacement + float dst = tr.set(entity.x - x, entity.y - y).len(); + entity.velocity().add(tr.setLength((1f - dst / radius) * 2f / entity.mass())); + }; + + rect.setSize(radius * 2).setCenter(x, y); + if(team != null){ + Units.nearbyEnemies(team, rect, cons); + }else{ + Units.nearby(rect, cons); + } + + if(!complete){ + int trad = (int)(radius / tilesize); + Tile tile = world.tileWorld(x, y); + if(tile != null){ + tileDamage(team, tile.x, tile.y, trad, damage); + } + }else{ + completeDamage(team, x, y, radius, damage); + } + } + + public static void tileDamage(Team team, int startx, int starty, int radius, float baseDamage){ + bits.clear(); + propagation.clear(); + int bitOffset = bits.width() / 2; + + propagation.addFirst(PropCell.get((byte)0, (byte)0, (short)baseDamage)); + //clamp radius to fit bits + radius = Math.min(radius, bits.width() / 2); + + while(!propagation.isEmpty()){ + int prop = propagation.removeLast(); + int x = PropCell.x(prop); + int y = PropCell.y(prop); + int damage = PropCell.damage(prop); + //manhattan distance used for calculating falloff, results in a diamond pattern + int dst = Math.abs(x) + Math.abs(y); + + int scaledDamage = (int)(damage * (1f - (float)dst / radius)); + + bits.set(bitOffset + x, bitOffset + y); + Tile tile = world.ltile(startx + x, starty + y); + + if(scaledDamage <= 0 || tile == null) continue; + + //apply damage to entity if needed + if(tile.entity != null && tile.getTeam() != team){ + int health = (int)tile.entity.health; + if(tile.entity.health > 0){ + tile.entity.damage(scaledDamage); + scaledDamage -= health; + + if(scaledDamage <= 0) continue; + } + } + + for(Point2 p : Geometry.d4){ + if(!bits.get(bitOffset + x + p.x, bitOffset + y + p.y)){ + propagation.addFirst(PropCell.get((byte)(x + p.x), (byte)(y + p.y), (short)scaledDamage)); + } + } + } + } + + private static void completeDamage(Team team, float x, float y, float radius, float damage){ + int trad = (int)(radius / tilesize); + for(int dx = -trad; dx <= trad; dx++){ + for(int dy = -trad; dy <= trad; dy++){ + Tile tile = world.tile(Math.round(x / tilesize) + dx, Math.round(y / tilesize) + dy); + if(tile != null && tile.entity != null && (team == null || state.teams.areEnemies(team, tile.getTeam())) && Mathf.dst(dx, dy) <= trad){ + tile.entity.damage(damage); + } + } + } + } + + private static float calculateDamage(float x, float y, float tx, float ty, float radius, float damage){ + float dist = Mathf.dst(x, y, tx, ty); + float falloff = 0.4f; + float scaled = Mathf.lerp(1f - dist / radius, 1f, falloff); + return damage * scaled; + } + + @Struct + class PropCellStruct{ + byte x; + byte y; + short damage; + } +} diff --git a/core/src/io/anuke/mindustry/entities/Effects.java b/core/src/io/anuke/mindustry/entities/Effects.java new file mode 100644 index 0000000000..4a2f964b61 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/Effects.java @@ -0,0 +1,166 @@ +package io.anuke.mindustry.entities; + +import io.anuke.arc.Core; +import io.anuke.arc.collection.Array; +import io.anuke.arc.function.Consumer; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.Position; +import io.anuke.arc.util.pooling.Pools; +import io.anuke.mindustry.entities.impl.EffectEntity; +import io.anuke.mindustry.entities.traits.ScaleTrait; + +public class Effects{ + private static final EffectContainer container = new EffectContainer(); + private static Array effects = new Array<>(); + private static ScreenshakeProvider shakeProvider; + private static float shakeFalloff = 1000f; + private static EffectProvider provider = (effect, color, x, y, rotation, data) -> { + EffectEntity entity = Pools.obtain(EffectEntity.class, EffectEntity::new); + entity.effect = effect; + entity.color = color; + entity.rotation = rotation; + entity.data = data; + entity.set(x, y); + entity.add(); + }; + + public static void setEffectProvider(EffectProvider prov){ + provider = prov; + } + + public static void setScreenShakeProvider(ScreenshakeProvider provider){ + shakeProvider = provider; + } + + public static void renderEffect(int id, Effect render, Color color, float life, float rotation, float x, float y, Object data){ + container.set(id, color, life, render.lifetime, rotation, x, y, data); + render.draw.render(container); + } + + public static Effect getEffect(int id){ + if(id >= effects.size || id < 0) + throw new IllegalArgumentException("The effect with ID \"" + id + "\" does not exist!"); + return effects.get(id); + } + + public static Array all(){ + return effects; + } + + public static void effect(Effect effect, float x, float y, float rotation){ + provider.createEffect(effect, Color.WHITE, x, y, rotation, null); + } + + public static void effect(Effect effect, float x, float y){ + effect(effect, x, y, 0); + } + + public static void effect(Effect effect, Color color, float x, float y){ + provider.createEffect(effect, color, x, y, 0f, null); + } + + public static void effect(Effect effect, Position loc){ + provider.createEffect(effect, Color.WHITE, loc.getX(), loc.getY(), 0f, null); + } + + public static void effect(Effect effect, Color color, float x, float y, float rotation){ + provider.createEffect(effect, color, x, y, rotation, null); + } + + public static void effect(Effect effect, Color color, float x, float y, float rotation, Object data){ + provider.createEffect(effect, color, x, y, rotation, data); + } + + public static void effect(Effect effect, float x, float y, float rotation, Object data){ + provider.createEffect(effect, Color.WHITE, x, y, rotation, data); + } + + /** Default value is 1000. Higher numbers mean more powerful shake (less falloff). */ + public static void setShakeFalloff(float falloff){ + shakeFalloff = falloff; + } + + private static void shake(float intensity, float duration){ + if(shakeProvider == null) throw new RuntimeException("Screenshake provider is null! Set it first."); + shakeProvider.accept(intensity, duration); + } + + public static void shake(float intensity, float duration, float x, float y){ + if(Core.camera == null) return; + + float distance = Core.camera.position.dst(x, y); + if(distance < 1) distance = 1; + + shake(Mathf.clamp(1f / (distance * distance / shakeFalloff)) * intensity, duration); + } + + public static void shake(float intensity, float duration, Position loc){ + shake(intensity, duration, loc.getX(), loc.getY()); + } + + public interface ScreenshakeProvider{ + void accept(float intensity, float duration); + } + + public static class Effect{ + private static int lastid = 0; + public final int id; + public final EffectRenderer draw; + public final float lifetime; + /** Clip size. */ + public float size; + + public Effect(float life, float clipsize, EffectRenderer draw){ + this.id = lastid++; + this.lifetime = life; + this.draw = draw; + this.size = clipsize; + effects.add(this); + } + + public Effect(float life, EffectRenderer draw){ + this(life, 28f, draw); + } + } + + public static class EffectContainer implements ScaleTrait{ + public float x, y, time, lifetime, rotation; + public Color color; + public int id; + public Object data; + private EffectContainer innerContainer; + + public void set(int id, Color color, float life, float lifetime, float rotation, float x, float y, Object data){ + this.x = x; + this.y = y; + this.color = color; + this.time = life; + this.lifetime = lifetime; + this.id = id; + this.rotation = rotation; + this.data = data; + } + + public void scaled(float lifetime, Consumer cons){ + if(innerContainer == null) innerContainer = new EffectContainer(); + if(time <= lifetime){ + innerContainer.set(id, color, time, lifetime, rotation, x, y, data); + cons.accept(innerContainer); + } + } + + @Override + public float fin(){ + return time / lifetime; + } + } + + public interface EffectProvider{ + void createEffect(Effect effect, Color color, float x, float y, float rotation, Object data); + } + + public interface EffectRenderer{ + void render(EffectContainer effect); + } +} diff --git a/core/src/io/anuke/mindustry/entities/Entities.java b/core/src/io/anuke/mindustry/entities/Entities.java new file mode 100755 index 0000000000..2a6f388ff5 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/Entities.java @@ -0,0 +1,89 @@ +package io.anuke.mindustry.entities; + +import io.anuke.arc.Core; +import io.anuke.arc.collection.Array; +import io.anuke.arc.collection.IntMap; +import io.anuke.arc.function.Consumer; +import io.anuke.arc.function.Predicate; +import io.anuke.arc.graphics.Camera; +import io.anuke.arc.math.geom.Rectangle; +import io.anuke.mindustry.entities.traits.DrawTrait; +import io.anuke.mindustry.entities.traits.Entity; + +import static io.anuke.mindustry.Vars.collisions; + +public class Entities{ + private static final Array> groupArray = new Array<>(); + private static final IntMap> groups = new IntMap<>(); + private static final Rectangle viewport = new Rectangle(); + private static final boolean clip = true; + private static int count = 0; + + public static void clear(){ + for(EntityGroup group : groupArray){ + group.clear(); + } + } + + public static EntityGroup getGroup(int id){ + return groups.get(id); + } + + public static Array> getAllGroups(){ + return groupArray; + } + + public static EntityGroup addGroup(Class type){ + return addGroup(type, true); + } + + public static EntityGroup addGroup(Class type, boolean useTree){ + EntityGroup group = new EntityGroup<>(type, useTree); + groups.put(group.getID(), group); + groupArray.add(group); + return group; + } + + public static void update(EntityGroup group){ + group.updateEvents(); + + if(group.useTree()){ + collisions.updatePhysics(group); + } + + for(Entity e : group.all()){ + e.update(); + } + } + + public static int countInBounds(EntityGroup group){ + count = 0; + draw(group, e -> true, e -> count++); + return count; + } + + public static void draw(EntityGroup group){ + draw(group, e -> true); + } + + public static void draw(EntityGroup group, Predicate toDraw){ + draw(group, toDraw, DrawTrait::draw); + } + + @SuppressWarnings("unchecked") + public static void draw(EntityGroup group, Predicate toDraw, Consumer cons){ + if(clip){ + Camera cam = Core.camera; + viewport.set(cam.position.x - cam.width / 2, cam.position.y - cam.height / 2, cam.width, cam.height); + } + + for(Entity e : group.all()){ + if(!(e instanceof DrawTrait) || !toDraw.test((T)e) || !e.isAdded()) continue; + DrawTrait draw = (DrawTrait)e; + + if(!clip || viewport.overlaps(draw.getX() - draw.drawSize()/2f, draw.getY() - draw.drawSize()/2f, draw.drawSize(), draw.drawSize())){ + cons.accept((T)e); + } + } + } +} diff --git a/core/src/io/anuke/mindustry/entities/EntityCollisions.java b/core/src/io/anuke/mindustry/entities/EntityCollisions.java new file mode 100644 index 0000000000..808f8e4fc8 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/EntityCollisions.java @@ -0,0 +1,236 @@ +package io.anuke.mindustry.entities; + +import io.anuke.arc.collection.Array; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.*; +import io.anuke.mindustry.entities.traits.Entity; +import io.anuke.mindustry.entities.traits.SolidTrait; +import io.anuke.mindustry.world.Tile; + +import static io.anuke.mindustry.Vars.tilesize; +import static io.anuke.mindustry.Vars.world; + +public class EntityCollisions{ + //range for tile collision scanning + private static final int r = 1; + //move in 1-unit chunks + private static final float seg = 1f; + + //tile collisions + private Rectangle tmp = new Rectangle(); + private Vector2 vector = new Vector2(); + private Vector2 l1 = new Vector2(); + private Rectangle r1 = new Rectangle(); + private Rectangle r2 = new Rectangle(); + + //entity collisions + private Array arrOut = new Array<>(); + + public void move(SolidTrait entity, float deltax, float deltay){ + + boolean movedx = false; + + while(Math.abs(deltax) > 0 || !movedx){ + movedx = true; + moveDelta(entity, Math.min(Math.abs(deltax), seg) * Mathf.sign(deltax), 0, true); + + if(Math.abs(deltax) >= seg){ + deltax -= seg * Mathf.sign(deltax); + }else{ + deltax = 0f; + } + } + + boolean movedy = false; + + while(Math.abs(deltay) > 0 || !movedy){ + movedy = true; + moveDelta(entity, 0, Math.min(Math.abs(deltay), seg) * Mathf.sign(deltay), false); + + if(Math.abs(deltay) >= seg){ + deltay -= seg * Mathf.sign(deltay); + }else{ + deltay = 0f; + } + } + } + + public void moveDelta(SolidTrait entity, float deltax, float deltay, boolean x){ + + Rectangle rect = r1; + entity.hitboxTile(rect); + entity.hitboxTile(r2); + rect.x += deltax; + rect.y += deltay; + + int tilex = Math.round((rect.x + rect.width / 2) / tilesize), tiley = Math.round((rect.y + rect.height / 2) / tilesize); + + for(int dx = -r; dx <= r; dx++){ + for(int dy = -r; dy <= r; dy++){ + int wx = dx + tilex, wy = dy + tiley; + if(solid(wx, wy) && entity.collidesGrid(wx, wy)){ + tmp.setSize(tilesize).setCenter(wx * tilesize, wy * tilesize); + + if(tmp.overlaps(rect)){ + Vector2 v = Geometry.overlap(rect, tmp, x); + rect.x += v.x; + rect.y += v.y; + } + } + } + } + + entity.setX(entity.getX() + rect.x - r2.x); + entity.setY(entity.getY() + rect.y - r2.y); + } + + public boolean overlapsTile(Rectangle rect){ + rect.getCenter(vector); + int r = 1; + + //assumes tiles are centered + int tilex = Math.round(vector.x / tilesize); + int tiley = Math.round(vector.y / tilesize); + + for(int dx = -r; dx <= r; dx++){ + for(int dy = -r; dy <= r; dy++){ + int wx = dx + tilex, wy = dy + tiley; + if(solid(wx, wy)){ + r2.setSize(tilesize).setCenter(wx * tilesize, wy * tilesize); + + if(r2.overlaps(rect)){ + return true; + } + } + } + } + return false; + } + + @SuppressWarnings("unchecked") + public void updatePhysics(EntityGroup group){ + + QuadTree tree = group.tree(); + tree.clear(); + + for(Entity entity : group.all()){ + if(entity instanceof SolidTrait){ + SolidTrait s = (SolidTrait)entity; + s.lastPosition().set(s.getX(), s.getY()); + tree.insert(s); + } + } + } + + private static boolean solid(int x, int y){ + Tile tile = world.tile(x, y); + return tile != null && tile.solid(); + } + + private void checkCollide(Entity entity, Entity other){ + + SolidTrait a = (SolidTrait)entity; + SolidTrait b = (SolidTrait)other; + + a.hitbox(this.r1); + b.hitbox(this.r2); + + r1.x += (a.lastPosition().x - a.getX()); + r1.y += (a.lastPosition().y - a.getY()); + r2.x += (b.lastPosition().x - b.getX()); + r2.y += (b.lastPosition().y - b.getY()); + + float vax = a.getX() - a.lastPosition().x; + float vay = a.getY() - a.lastPosition().y; + float vbx = b.getX() - b.lastPosition().x; + float vby = b.getY() - b.lastPosition().y; + + if(a != b && a.collides(b) && b.collides(a)){ + l1.set(a.getX(), a.getY()); + boolean collide = r1.overlaps(r2) || collide(r1.x, r1.y, r1.width, r1.height, vax, vay, + r2.x, r2.y, r2.width, r2.height, vbx, vby, l1); + if(collide){ + a.collision(b, l1.x, l1.y); + b.collision(a, l1.x, l1.y); + } + } + } + + private boolean collide(float x1, float y1, float w1, float h1, float vx1, float vy1, + float x2, float y2, float w2, float h2, float vx2, float vy2, Vector2 out){ + float px = vx1, py = vy1; + + vx1 -= vx2; + vy1 -= vy2; + + float xInvEntry, yInvEntry; + float xInvExit, yInvExit; + + if(vx1 > 0.0f){ + xInvEntry = x2 - (x1 + w1); + xInvExit = (x2 + w2) - x1; + }else{ + xInvEntry = (x2 + w2) - x1; + xInvExit = x2 - (x1 + w1); + } + + if(vy1 > 0.0f){ + yInvEntry = y2 - (y1 + h1); + yInvExit = (y2 + h2) - y1; + }else{ + yInvEntry = (y2 + h2) - y1; + yInvExit = y2 - (y1 + h1); + } + + float xEntry, yEntry; + float xExit, yExit; + + xEntry = xInvEntry / vx1; + xExit = xInvExit / vx1; + + yEntry = yInvEntry / vy1; + yExit = yInvExit / vy1; + + float entryTime = Math.max(xEntry, yEntry); + float exitTime = Math.min(xExit, yExit); + + if(entryTime > exitTime || xExit < 0.0f || yExit < 0.0f || xEntry > 1.0f || yEntry > 1.0f){ + return false; + }else{ + float dx = x1 + w1 / 2f + px * entryTime; + float dy = y1 + h1 / 2f + py * entryTime; + + out.set(dx, dy); + + return true; + } + } + + @SuppressWarnings("unchecked") + public void collideGroups(EntityGroup groupa, EntityGroup groupb){ + + for(Entity entity : groupa.all()){ + if(!(entity instanceof SolidTrait)) + continue; + + SolidTrait solid = (SolidTrait)entity; + + solid.hitbox(r1); + r1.x += (solid.lastPosition().x - solid.getX()); + r1.y += (solid.lastPosition().y - solid.getY()); + + solid.hitbox(r2); + r2.merge(r1); + + arrOut.clear(); + groupb.tree().getIntersect(arrOut, r2); + + for(SolidTrait sc : arrOut){ + sc.hitbox(r1); + if(r2.overlaps(r1)){ + checkCollide(entity, sc); + } + } + } + } +} diff --git a/core/src/io/anuke/mindustry/entities/EntityGroup.java b/core/src/io/anuke/mindustry/entities/EntityGroup.java new file mode 100644 index 0000000000..d3010c84f3 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/EntityGroup.java @@ -0,0 +1,202 @@ +package io.anuke.mindustry.entities; + +import io.anuke.arc.collection.Array; +import io.anuke.arc.collection.IntMap; +import io.anuke.arc.function.Consumer; +import io.anuke.arc.function.Predicate; +import io.anuke.arc.math.geom.QuadTree; +import io.anuke.arc.math.geom.Rectangle; +import io.anuke.mindustry.entities.traits.Entity; + +public class EntityGroup{ + private static int lastid; + private final boolean useTree; + private final int id; + private final Class type; + private final Array entityArray = new Array<>(false, 16); + private final Array entitiesToRemove = new Array<>(false, 16); + private final Array entitiesToAdd = new Array<>(false, 16); + private IntMap map; + private QuadTree tree; + private Consumer removeListener; + private Consumer addListener; + + public EntityGroup(Class type, boolean useTree){ + this.useTree = useTree; + this.id = lastid++; + this.type = type; + + if(useTree){ + tree = new QuadTree<>(new Rectangle(0, 0, 0, 0)); + } + } + + public boolean useTree(){ + return useTree; + } + + public void setRemoveListener(Consumer removeListener){ + this.removeListener = removeListener; + } + + public void setAddListener(Consumer addListener){ + this.addListener = addListener; + } + + public EntityGroup enableMapping(){ + map = new IntMap<>(); + return this; + } + + public boolean mappingEnabled(){ + return map != null; + } + + public Class getType(){ + return type; + } + + public int getID(){ + return id; + } + + public void updateEvents(){ + + for(T e : entitiesToAdd){ + if(e == null) + continue; + entityArray.add(e); + e.added(); + + if(map != null){ + map.put(e.getID(), e); + } + } + + entitiesToAdd.clear(); + + for(T e : entitiesToRemove){ + entityArray.removeValue(e, true); + if(map != null){ + map.remove(e.getID()); + } + e.removed(); + } + + entitiesToRemove.clear(); + } + + public T getByID(int id){ + if(map == null) throw new RuntimeException("Mapping is not enabled for group " + id + "!"); + return map.get(id); + } + + public void removeByID(int id){ + if(map == null) throw new RuntimeException("Mapping is not enabled for group " + id + "!"); + T t = map.get(id); + if(t != null){ //remove if present in map already + remove(t); + }else{ //maybe it's being queued? + for(T check : entitiesToAdd){ + if(check.getID() == id){ //if it is indeed queued, remove it + entitiesToAdd.removeValue(check, true); + if(removeListener != null){ + removeListener.accept(check); + } + break; + } + } + } + } + + @SuppressWarnings("unchecked") + public void intersect(float x, float y, float width, float height, Consumer out){ + //don't waste time for empty groups + if(isEmpty()) return; + tree().getIntersect(out, x, y, width, height); + } + + public QuadTree tree(){ + if(!useTree) throw new RuntimeException("This group does not support quadtrees! Enable quadtrees when creating it."); + return tree; + } + + /** Resizes the internal quadtree, if it is enabled.*/ + public void resize(float x, float y, float w, float h){ + if(useTree){ + tree = new QuadTree<>(new Rectangle(x, y, w, h)); + } + } + + public boolean isEmpty(){ + return entityArray.size == 0; + } + + public int size(){ + return entityArray.size; + } + + public int count(Predicate pred){ + int count = 0; + for(int i = 0; i < entityArray.size; i++){ + if(pred.test(entityArray.get(i))) count++; + } + return count; + } + + public void add(T type){ + if(type == null) throw new RuntimeException("Cannot add a null entity!"); + if(type.getGroup() != null) return; + type.setGroup(this); + entitiesToAdd.add(type); + + if(mappingEnabled()){ + map.put(type.getID(), type); + } + + if(addListener != null){ + addListener.accept(type); + } + } + + public void remove(T type){ + if(type == null) throw new RuntimeException("Cannot remove a null entity!"); + type.setGroup(null); + entitiesToRemove.add(type); + + if(removeListener != null){ + removeListener.accept(type); + } + } + + public void clear(){ + for(T entity : entityArray) + entity.setGroup(null); + + for(T entity : entitiesToAdd) + entity.setGroup(null); + + for(T entity : entitiesToRemove) + entity.setGroup(null); + + entitiesToAdd.clear(); + entitiesToRemove.clear(); + entityArray.clear(); + if(map != null) + map.clear(); + } + + public T find(Predicate pred){ + + for(int i = 0; i < entityArray.size; i++){ + if(pred.test(entityArray.get(i))) return entityArray.get(i); + } + + return null; + } + + /** Returns the logic-only array for iteration. */ + public Array all(){ + return entityArray; + } +} diff --git a/core/src/io/anuke/mindustry/entities/Player.java b/core/src/io/anuke/mindustry/entities/Player.java deleted file mode 100644 index 63463baa14..0000000000 --- a/core/src/io/anuke/mindustry/entities/Player.java +++ /dev/null @@ -1,309 +0,0 @@ -package io.anuke.mindustry.entities; - -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.math.Vector2; -import io.anuke.mindustry.graphics.Fx; -import io.anuke.mindustry.graphics.Shaders; -import io.anuke.mindustry.net.Net; -import io.anuke.mindustry.net.NetEvents; -import io.anuke.mindustry.resource.Mech; -import io.anuke.mindustry.resource.Upgrade; -import io.anuke.mindustry.resource.Weapon; -import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.blocks.Blocks; -import io.anuke.ucore.core.*; -import io.anuke.ucore.entities.SolidEntity; -import io.anuke.ucore.graphics.Draw; -import io.anuke.ucore.util.Angles; -import io.anuke.ucore.util.Mathf; -import io.anuke.ucore.util.Timer; -import io.anuke.ucore.util.Translator; - -import java.nio.ByteBuffer; - -import static io.anuke.mindustry.Vars.*; - -public class Player extends SyncEntity{ - static final float speed = 1.1f; - static final float dashSpeed = 1.8f; - - static final int timerDash = 0; - static final int timerShootLeft = 1; - static final int timerShootRight = 2; - static final int timerRegen = 3; - - public String name = "name"; - public boolean isAndroid; - public boolean isAdmin; - public Color color = new Color(); - - public Weapon weaponLeft = Weapon.blaster; - public Weapon weaponRight = Weapon.blaster; - public Mech mech = Mech.standard; - - public float targetAngle = 0f; - public boolean dashing = false; - - public int clientid = -1; - public boolean isLocal = false; - public Timer timer = new Timer(4); - - private Vector2 movement = new Vector2(); - private Translator tr = new Translator(); - - public Player(){ - hitbox.setSize(5); - hitboxTile.setSize(4f); - - maxhealth = 200; - heal(); - } - - @Override - public void damage(float amount){ - if(debug || isAndroid) return; - - health -= amount; - if(health <= 0 && !dead && isLocal){ //remote players don't die normally - onDeath(); - dead = true; - } - } - - @Override - public boolean collides(SolidEntity other){ - if(other instanceof Bullet){ - Bullet b = (Bullet)other; - if(!state.friendlyFire && b.owner instanceof Player){ - return false; - } - } - return !isDead() && super.collides(other) && !isAndroid; - } - - @Override - public void onDeath(){ - dead = true; - if(Net.active()){ - NetEvents.handlePlayerDeath(); - } - - Effects.effect(Fx.explosion, this); - Effects.shake(4f, 5f, this); - Effects.sound("die", this); - - control.setRespawnTime(respawnduration); - ui.hudfrag.fadeRespawn(true); - } - - /**called when a remote player death event is recieved*/ - public void doRespawn(){ - dead = true; - Effects.effect(Fx.explosion, this); - Effects.shake(4f, 5f, this); - Effects.sound("die", this); - - Timers.run(respawnduration + 5f, () -> { - heal(); - set(world.getSpawnX(), world.getSpawnY()); - interpolator.target.set(x, y); - }); - } - - @Override - public void drawSmooth(){ - if((debug && (!showPlayer || !showUI)) || (isAndroid && isLocal) || dead) return; - boolean snap = snapCamera && Settings.getBool("smoothcam") && Settings.getBool("pixelate") && isLocal; - - String part = isAndroid ? "ship" : "mech"; - - Shaders.outline.color.set(getColor()); - Shaders.outline.lighten = 0f; - Shaders.outline.region = Draw.region(part + "-" + mech.name); - - Shaders.outline.apply(); - - if(!isAndroid) { - for (int i : Mathf.signs) { - Weapon weapon = i < 0 ? weaponLeft : weaponRight; - tr.trns(angle - 90, 3*i, 2); - float w = i > 0 ? -8 : 8; - if(snap){ - Draw.rect(weapon.name + "-equip", (int)x + tr.x, (int)y + tr.y, w, 8, angle - 90); - }else{ - Draw.rect(weapon.name + "-equip", x + tr.x, y + tr.y, w, 8, angle - 90); - } - } - } - - if(snap){ - Draw.rect(part + "-" + mech.name, (int)x, (int)y, angle-90); - }else{ - Draw.rect(part + "-" + mech.name, x, y, angle-90); - } - - Graphics.flush(); - } - - @Override - public void update(){ - if(!isLocal || isAndroid){ - if(isAndroid && isLocal){ - angle = Mathf.slerpDelta(angle, targetAngle, 0.2f); - } - if(!isLocal) interpolate(); - return; - } - - if(isDead()) return; - - Tile tile = world.tileWorld(x, y); - - //if player is in solid block - if(tile != null && ((tile.floor().liquid && tile.block() == Blocks.air) || tile.solid())) { - damage(health + 1); //die instantly - } - - if(ui.chatfrag.chatOpen()) return; - - dashing = Inputs.keyDown("dash"); - - float speed = dashing ? (debug ? Player.dashSpeed * 5f : Player.dashSpeed) : Player.speed; - - if(health < maxhealth && timer.get(timerRegen, 20)) - health ++; - - health = Mathf.clamp(health, -1, maxhealth); - - movement.set(0, 0); - - float xa = Inputs.getAxis("move_x"); - float ya = Inputs.getAxis("move_y"); - if(Math.abs(xa) < 0.3) xa = 0; - if(Math.abs(ya) < 0.3) ya = 0; - - movement.y += ya*speed; - movement.x += xa*speed; - - boolean shooting = !Inputs.keyDown("dash") && Inputs.keyDown("shoot") && control.input().recipe == null - && !ui.hasMouse() && !control.input().onConfigurable(); - - if(shooting){ - weaponLeft.update(player, true); - weaponRight.update(player, false); - } - - if(dashing && timer.get(timerDash, 3) && movement.len() > 0){ - Effects.effect(Fx.dashsmoke, x + Angles.trnsx(angle + 180f, 3f), y + Angles.trnsy(angle + 180f, 3f)); - } - - movement.limit(speed); - - if(!noclip){ - move(movement.x*Timers.delta(), movement.y*Timers.delta()); - }else{ - x += movement.x*Timers.delta(); - y += movement.y*Timers.delta(); - } - - if(!shooting){ - if(!movement.isZero()) - angle = Mathf.slerpDelta(angle, movement.angle(), 0.13f); - }else{ - float angle = Angles.mouseAngle(x, y); - this.angle = Mathf.slerpDelta(this.angle, angle, 0.1f); - } - - x = Mathf.clamp(x, 0, world.width() * tilesize); - y = Mathf.clamp(y, 0, world.height() * tilesize); - } - - @Override - public Player add(){ - return add(playerGroup); - } - - @Override - public String toString() { - return "Player{" + id + ", android=" + isAndroid + ", local=" + isLocal + ", " + x + ", " + y + "}\n"; - } - - @Override - public void writeSpawn(ByteBuffer buffer) { - buffer.put((byte)name.getBytes().length); - buffer.put(name.getBytes()); - buffer.put(weaponLeft.id); - buffer.put(weaponRight.id); - buffer.put(isAndroid ? 1 : (byte)0); - buffer.put(isAdmin ? 1 : (byte)0); - buffer.putInt(Color.rgba8888(color)); - buffer.putFloat(x); - buffer.putFloat(y); - } - - @Override - public void readSpawn(ByteBuffer buffer) { - byte nlength = buffer.get(); - byte[] n = new byte[nlength]; - buffer.get(n); - name = new String(n); - weaponLeft = (Weapon) Upgrade.getByID(buffer.get()); - weaponRight = (Weapon) Upgrade.getByID(buffer.get()); - isAndroid = buffer.get() == 1; - isAdmin = buffer.get() == 1; - color.set(buffer.getInt()); - x = buffer.getFloat(); - y = buffer.getFloat(); - setNet(x, y); - } - - @Override - public void write(ByteBuffer data) { - if(Net.client() || isLocal) { - data.putFloat(x); - data.putFloat(y); - }else{ - data.putFloat(interpolator.target.x); - data.putFloat(interpolator.target.y); - } - data.putFloat(angle); - data.putShort((short)health); - data.put((byte)(dashing ? 1 : 0)); - } - - @Override - public void read(ByteBuffer data, long time) { - float x = data.getFloat(); - float y = data.getFloat(); - float angle = data.getFloat(); - short health = data.getShort(); - byte dashing = data.get(); - - this.health = health; - this.dashing = dashing == 1; - - interpolator.read(this.x, this.y, x, y, angle, time); - } - - @Override - public void interpolate() { - super.interpolate(); - - Interpolator i = interpolator; - - float tx = x + Angles.trnsx(angle + 180f, 4f); - float ty = y + Angles.trnsy(angle + 180f, 4f); - - if(isAndroid && i.target.dst(i.last) > 2f && timer.get(timerDash, 1)){ - Effects.effect(Fx.dashsmoke, tx, ty); - } - - if(dashing && !dead && timer.get(timerDash, 3) && i.target.dst(i.last) > 1f){ - Effects.effect(Fx.dashsmoke, tx, ty); - } - } - - public Color getColor(){ - return color; - } -} diff --git a/core/src/io/anuke/mindustry/entities/Predict.java b/core/src/io/anuke/mindustry/entities/Predict.java new file mode 100644 index 0000000000..25bb2aea84 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/Predict.java @@ -0,0 +1,81 @@ +package io.anuke.mindustry.entities; + +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.Vector2; +import io.anuke.arc.util.Time; +import io.anuke.mindustry.entities.traits.TargetTrait; + +/** + * Class for predicting shoot angles based on velocities of targets. + */ +public class Predict{ + private static Vector2 vec = new Vector2(); + private static Vector2 vresult = new Vector2(); + + /** + * Calculates of intercept of a stationary and moving target. Do not call from multiple threads! + * @param srcx X of shooter + * @param srcy Y of shooter + * @param dstx X of target + * @param dsty Y of target + * @param dstvx X velocity of target (subtract shooter X velocity if needed) + * @param dstvy Y velocity of target (subtract shooter Y velocity if needed) + * @param v speed of bullet + * @return the intercept location + */ + public static Vector2 intercept(float srcx, float srcy, float dstx, float dsty, float dstvx, float dstvy, float v){ + dstvx /= Time.delta(); + dstvy /= Time.delta(); + float tx = dstx - srcx, + ty = dsty - srcy; + + // Get quadratic equation components + float a = dstvx * dstvx + dstvy * dstvy - v * v; + float b = 2 * (dstvx * tx + dstvy * ty); + float c = tx * tx + ty * ty; + + // Solve quadratic + Vector2 ts = quad(a, b, c); + + // Find smallest positive solution + Vector2 sol = vresult.set(dstx, dsty); + if(ts != null){ + float t0 = ts.x, t1 = ts.y; + float t = Math.min(t0, t1); + if(t < 0) t = Math.max(t0, t1); + if(t > 0){ + sol.set(dstx + dstvx * t, dsty + dstvy * t); + } + } + + return sol; + } + + /** + * See {@link #intercept(float, float, float, float, float, float, float)}. + */ + public static Vector2 intercept(TargetTrait src, TargetTrait dst, float v){ + return intercept(src.getX(), src.getY(), dst.getX(), dst.getY(), + dst.getTargetVelocityX() - src.getTargetVelocityX()/2f, + dst.getTargetVelocityY() - src.getTargetVelocityY()/2f, v); + } + + private static Vector2 quad(float a, float b, float c){ + Vector2 sol = null; + if(Math.abs(a) < 1e-6){ + if(Math.abs(b) < 1e-6){ + sol = Math.abs(c) < 1e-6 ? vec.set(0, 0) : null; + }else{ + vec.set(-c / b, -c / b); + } + }else{ + float disc = b * b - 4 * a * c; + if(disc >= 0){ + disc = Mathf.sqrt(disc); + a = 2 * a; + sol = vec.set((-b - disc) / a, (-b + disc) / a); + } + } + return sol; + } +} diff --git a/core/src/io/anuke/mindustry/entities/StatusEffect.java b/core/src/io/anuke/mindustry/entities/StatusEffect.java deleted file mode 100644 index f84d076f3d..0000000000 --- a/core/src/io/anuke/mindustry/entities/StatusEffect.java +++ /dev/null @@ -1,5 +0,0 @@ -package io.anuke.mindustry.entities; - -public enum StatusEffect{ - none; -} diff --git a/core/src/io/anuke/mindustry/entities/SyncEntity.java b/core/src/io/anuke/mindustry/entities/SyncEntity.java deleted file mode 100644 index f24186e66b..0000000000 --- a/core/src/io/anuke/mindustry/entities/SyncEntity.java +++ /dev/null @@ -1,139 +0,0 @@ -package io.anuke.mindustry.entities; - -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.math.Vector2; -import com.badlogic.gdx.math.Vector3; -import com.badlogic.gdx.utils.ObjectIntMap; -import com.badlogic.gdx.utils.TimeUtils; -import io.anuke.mindustry.entities.enemies.Enemy; -import io.anuke.ucore.core.Timers; -import io.anuke.ucore.entities.DestructibleEntity; -import io.anuke.ucore.util.Mathf; - -import java.nio.ByteBuffer; - -import static io.anuke.mindustry.Vars.threads; - -public abstract class SyncEntity extends DestructibleEntity{ - private static ObjectIntMap> writeSizes = new ObjectIntMap<>(); - - protected transient Interpolator interpolator = new Interpolator(); - - //smoothed position/angle - private Vector3 spos = new Vector3(); - - public float angle; - - static{ - setWriteSize(Enemy.class, 4 + 4 + 2 + 2); - setWriteSize(Player.class, 4 + 4 + 4 + 2 + 1); - } - - public static boolean isSmoothing(){ - return threads.isEnabled() && threads.getFPS() <= Gdx.graphics.getFramesPerSecond() / 2f; - } - - public abstract void writeSpawn(ByteBuffer data); - public abstract void readSpawn(ByteBuffer data); - - public abstract void write(ByteBuffer data); - public abstract void read(ByteBuffer data, long time); - - public void interpolate(){ - interpolator.update(); - - x = interpolator.pos.x; - y = interpolator.pos.y; - angle = interpolator.angle; - } - - @Override - public final void draw(){ - final float x = this.x, y = this.y, angle = this.angle; - - //interpolates data at low tick speeds. - if(isSmoothing()){ - if(Vector2.dst(spos.x, spos.y, x, y) > 128){ - spos.set(x, y, angle); - } - - this.x = spos.x = Mathf.lerpDelta(spos.x, x, 0.2f); - this.y = spos.y = Mathf.lerpDelta(spos.y, y, 0.2f); - this.angle = spos.z = Mathf.slerpDelta(spos.z, angle, 0.3f); - } - - drawSmooth(); - - this.x = x; - this.y = y; - this.angle = angle; - } - - public Vector3 getDrawPosition(){ - return isSmoothing() ? spos : spos.set(x, y, angle); - } - - public void drawSmooth(){} - - public int getWriteSize(){ - return getWriteSize(getClass()); - } - - public static int getWriteSize(Class type){ - int i = writeSizes.get(type, -1); - if(i == -1) throw new RuntimeException("Write size for class \"" + type + "\" is not defined!"); - return i; - } - - protected static void setWriteSize(Class type, int size){ - writeSizes.put(type, size); - } - - public T setNet(float x, float y){ - set(x, y); - interpolator.target.set(x, y); - interpolator.last.set(x, y); - interpolator.spacing = 1f; - interpolator.time = 0f; - return (T)this; - } - - public static class Interpolator { - //used for movement - public Vector2 target = new Vector2(); - public Vector2 last = new Vector2(); - public float targetrot; - public float spacing = 1f; - public float time; - - //current state - public Vector2 pos = new Vector2(); - public float angle; - - public void read(float cx, float cy, float x, float y, float angle, long sent){ - targetrot = angle; - time = 0f; - last.set(cx, cy); - target.set(x, y); - spacing = Math.min(Math.max(((TimeUtils.timeSinceMillis(sent) / 1000f) * 60f), 4f), 10); - } - - public void update(){ - - time += 1f / spacing * Math.min(Timers.delta(), 1f); - - time = Mathf.clamp(time, 0, 2f); - - Mathf.lerp2(pos.set(last), target, time); - - angle = Mathf.slerpDelta(angle, targetrot, 0.6f); - - if(target.dst(pos) > 128){ - pos.set(target); - last.set(target); - time = 0f; - } - - } - } -} diff --git a/core/src/io/anuke/mindustry/entities/TileEntity.java b/core/src/io/anuke/mindustry/entities/TileEntity.java deleted file mode 100644 index adf41edaa2..0000000000 --- a/core/src/io/anuke/mindustry/entities/TileEntity.java +++ /dev/null @@ -1,153 +0,0 @@ -package io.anuke.mindustry.entities; - -import io.anuke.mindustry.graphics.Fx; -import io.anuke.mindustry.net.Net; -import io.anuke.mindustry.net.NetEvents; -import io.anuke.mindustry.resource.Item; -import io.anuke.mindustry.world.Block; -import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.blocks.types.Wall; -import io.anuke.ucore.core.Effects; -import io.anuke.ucore.core.Timers; -import io.anuke.ucore.entities.Entity; -import io.anuke.ucore.util.Mathf; -import io.anuke.ucore.util.Timer; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; - -import static io.anuke.mindustry.Vars.tileGroup; -import static io.anuke.mindustry.Vars.world; - -public class TileEntity extends Entity{ - public Tile tile; - public int[] items = new int[Item.getAllItems().size]; - public Timer timer; - public float health; - public boolean dead = false; - public boolean added; - - /**Sets this tile entity data to this tile, and adds it if necessary.*/ - public TileEntity init(Tile tile, boolean added){ - this.tile = tile; - this.added = added; - x = tile.worldx(); - y = tile.worldy(); - - health = tile.block().health; - - timer = new Timer(tile.block().timers); - - if(added){ - add(); - } - - return this; - } - - public void write(DataOutputStream stream) throws IOException{ - - } - - public void read(DataInputStream stream) throws IOException{ - - } - - public void readNetwork(DataInputStream stream, float elapsed) throws IOException{ - read(stream); - } - - public void onDeath(){ - onDeath(false); - } - - public void onDeath(boolean force){ - if(Net.server()){ - NetEvents.handleBlockDestroyed(this); - } - - if(!Net.active() || Net.server() || force){ - - if(!dead) { - dead = true; - Block block = tile.block(); - - block.onDestroyed(tile); - - world.removeBlock(tile); - remove(); - } - } - } - - public void collision(Bullet other){ - damage(other.getDamage()); - } - - public void damage(int damage){ - if(dead) return; - - int amount = tile.block().handleDamage(tile, damage); - health -= amount; - if(health <= 0) onDeath(); - - if(Net.server()){ - NetEvents.handleBlockDamaged(this); - } - } - - public boolean collide(Bullet other){ - return true; - } - - @Override - public void update(){ - synchronized (Tile.tileSetLock) { - if (health != 0 && health < tile.block().health && !(tile.block() instanceof Wall) && - Mathf.chance(0.009f * Timers.delta() * (1f - health / tile.block().health))) { - - Effects.effect(Fx.smoke, x + Mathf.range(4), y + Mathf.range(4)); - } - - if (health <= 0) { - onDeath(); - } - - tile.block().update(tile); - } - } - - public int totalItems(){ - int sum = 0; - for(int i = 0; i < items.length; i ++){ - sum += items[i]; - } - return sum; - } - - public int getItem(Item item){ - return items[item.id]; - } - - public boolean hasItem(Item item){ - return getItem(item) > 0; - } - - public boolean hasItem(Item item, int amount){ - return getItem(item) >= amount; - } - - public void addItem(Item item, int amount){ - items[item.id] += amount; - } - - public void removeItem(Item item, int amount){ - items[item.id] -= amount; - } - - @Override - public TileEntity add(){ - return add(tileGroup); - } -} diff --git a/core/src/io/anuke/mindustry/entities/Units.java b/core/src/io/anuke/mindustry/entities/Units.java new file mode 100644 index 0000000000..b4b6ea115b --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/Units.java @@ -0,0 +1,221 @@ +package io.anuke.mindustry.entities; + +import io.anuke.arc.collection.EnumSet; +import io.anuke.arc.function.Consumer; +import io.anuke.arc.function.Predicate; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.Geometry; +import io.anuke.arc.math.geom.Rectangle; +import io.anuke.mindustry.entities.traits.TargetTrait; +import io.anuke.mindustry.entities.type.*; +import io.anuke.mindustry.game.Team; +import io.anuke.mindustry.world.Tile; + +import static io.anuke.mindustry.Vars.*; + +/** Utility class for unit and team interactions.*/ +public class Units{ + private static Rectangle hitrect = new Rectangle(); + private static Unit result; + private static float cdist; + private static boolean boolResult; + + /** + * Validates a target. + * @param target The target to validate + * @param team The team of the thing doing tha targeting + * @param x The X position of the thing doign the targeting + * @param y The Y position of the thing doign the targeting + * @param range The maximum distance from the target X/Y the targeter can be for it to be valid + * @return whether the target is invalid + */ + public static boolean invalidateTarget(TargetTrait target, Team team, float x, float y, float range){ + return target == null || (range != Float.MAX_VALUE && !target.withinDst(x, y, range)) || target.getTeam() == team || !target.isValid(); + } + + /** See {@link #invalidateTarget(TargetTrait, Team, float, float, float)} */ + public static boolean invalidateTarget(TargetTrait target, Team team, float x, float y){ + return invalidateTarget(target, team, x, y, Float.MAX_VALUE); + } + + /** See {@link #invalidateTarget(TargetTrait, Team, float, float, float)} */ + public static boolean invalidateTarget(TargetTrait target, Unit targeter){ + return invalidateTarget(target, targeter.getTeam(), targeter.x, targeter.y, targeter.getWeapon().bullet.range()); + } + + /** Returns whether there are any entities on this tile. */ + public static boolean anyEntities(Tile tile){ + float size = tile.block().size * tilesize; + return anyEntities(tile.drawx() - size/2f, tile.drawy() - size/2f, size, size); + } + + public static boolean anyEntities(float x, float y, float width, float height){ + boolResult = false; + + nearby(x, y, width, height, unit -> { + if(boolResult) return; + if(!unit.isFlying()){ + unit.hitbox(hitrect); + + if(hitrect.overlaps(x, y, width, height)){ + boolResult = true; + } + } + }); + + return boolResult; + } + + /** Returns the neareset damaged tile. */ + public static TileEntity findDamagedTile(Team team, float x, float y){ + Tile tile = Geometry.findClosest(x, y, world.indexer.getDamaged(team)); + return tile == null ? null : tile.entity; + } + + /** Returns the neareset ally tile in a range. */ + public static TileEntity findAllyTile(Team team, float x, float y, float range, Predicate pred){ + return world.indexer.findTile(team, x, y, range, pred); + } + + /** Returns the neareset enemy tile in a range. */ + public static TileEntity findEnemyTile(Team team, float x, float y, float range, Predicate pred){ + if(team == Team.none) return null; + + for(Team enemy : state.teams.enemiesOf(team)){ + TileEntity entity = world.indexer.findTile(enemy, x, y, range, pred); + if(entity != null){ + return entity; + } + } + return null; + } + + /** Returns the closest target enemy. First, units are checked, then tile entities. */ + public static TargetTrait closestTarget(Team team, float x, float y, float range){ + return closestTarget(team, x, y, range, Unit::isValid); + } + + /** Returns the closest target enemy. First, units are checked, then tile entities. */ + public static TargetTrait closestTarget(Team team, float x, float y, float range, Predicate unitPred){ + return closestTarget(team, x, y, range, unitPred, t -> true); + } + + /** Returns the closest target enemy. First, units are checked, then tile entities. */ + public static TargetTrait closestTarget(Team team, float x, float y, float range, Predicate unitPred, Predicate tilePred){ + if(team == Team.none) return null; + + Unit unit = closestEnemy(team, x, y, range, unitPred); + if(unit != null){ + return unit; + }else{ + return findEnemyTile(team, x, y, range, tilePred); + } + } + + /** Returns the closest enemy of this team. Filter by predicate. */ + public static Unit closestEnemy(Team team, float x, float y, float range, Predicate predicate){ + if(team == Team.none) return null; + + result = null; + cdist = 0f; + + nearbyEnemies(team, x - range, y - range, range*2f, range*2f, e -> { + if(e.isDead() || !predicate.test(e)) return; + + float dst2 = Mathf.dst2(e.x, e.y, x, y); + if(dst2 < range*range && (result == null || dst2 < cdist)){ + result = e; + cdist = dst2; + } + }); + + return result; + } + + /** Returns the closest ally of this team. Filter by predicate. */ + public static Unit closest(Team team, float x, float y, float range, Predicate predicate){ + result = null; + cdist = 0f; + + nearby(team, x, y, range, e -> { + if(!predicate.test(e)) return; + + float dist = Mathf.dst2(e.x, e.y, x, y); + if(result == null || dist < cdist){ + result = e; + cdist = dist; + } + }); + + return result; + } + + /** Iterates over all units in a rectangle. */ + public static void nearby(Team team, float x, float y, float width, float height, Consumer cons){ + unitGroups[team.ordinal()].intersect(x, y, width, height, cons); + playerGroup.intersect(x, y, width, height, player -> { + if(player.getTeam() == team){ + cons.accept(player); + } + }); + } + + /** Iterates over all units in a circle around this position. */ + public static void nearby(Team team, float x, float y, float radius, Consumer cons){ + unitGroups[team.ordinal()].intersect(x - radius, y - radius, radius*2f, radius*2f, unit -> { + if(unit.withinDst(x, y, radius)){ + cons.accept(unit); + } + }); + + playerGroup.intersect(x - radius, y - radius, radius*2f, radius*2f, unit -> { + if(unit.getTeam() == team && unit.withinDst(x, y, radius)){ + cons.accept(unit); + } + }); + } + + /** Iterates over all units in a rectangle. */ + public static void nearby(float x, float y, float width, float height, Consumer cons){ + for(Team team : Team.all){ + unitGroups[team.ordinal()].intersect(x, y, width, height, cons); + } + + playerGroup.intersect(x, y, width, height, cons); + } + + /** Iterates over all units in a rectangle. */ + public static void nearby(Rectangle rect, Consumer cons){ + nearby(rect.x, rect.y, rect.width, rect.height, cons); + } + + /** Iterates over all units that are enemies of this team. */ + public static void nearbyEnemies(Team team, float x, float y, float width, float height, Consumer cons){ + EnumSet targets = state.teams.enemiesOf(team); + + for(Team other : targets){ + unitGroups[other.ordinal()].intersect(x, y, width, height, cons); + } + + playerGroup.intersect(x, y, width, height, player -> { + if(targets.contains(player.getTeam())){ + cons.accept(player); + } + }); + } + + /** Iterates over all units that are enemies of this team. */ + public static void nearbyEnemies(Team team, Rectangle rect, Consumer cons){ + nearbyEnemies(team, rect.x, rect.y, rect.width, rect.height, cons); + } + + /** Iterates over all units. */ + public static void all(Consumer cons){ + for(Team team : Team.all){ + unitGroups[team.ordinal()].all().each(cons); + } + + playerGroup.all().each(cons); + } + +} diff --git a/core/src/io/anuke/mindustry/entities/bullet/ArtilleryBulletType.java b/core/src/io/anuke/mindustry/entities/bullet/ArtilleryBulletType.java new file mode 100644 index 0000000000..cbe7cae4fd --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/bullet/ArtilleryBulletType.java @@ -0,0 +1,42 @@ +package io.anuke.mindustry.entities.bullet; + +import io.anuke.arc.graphics.g2d.Draw; +import io.anuke.mindustry.content.Fx; +import io.anuke.mindustry.entities.Effects; +import io.anuke.mindustry.entities.Effects.Effect; + +//TODO scale velocity depending on fslope() +public class ArtilleryBulletType extends BasicBulletType{ + protected Effect trailEffect = Fx.artilleryTrail; + + public ArtilleryBulletType(float speed, float damage, String bulletSprite){ + super(speed, damage, bulletSprite); + collidesTiles = false; + collides = false; + collidesAir = false; + hitShake = 1f; + } + + @Override + public void update(Bullet b){ + super.update(b); + + if(b.timer.get(0, 3 + b.fslope() * 2f)){ + Effects.effect(trailEffect, backColor, b.x, b.y, b.fslope() * 4f); + } + } + + @Override + public void draw(Bullet b){ + float baseScale = 0.7f; + float scale = (baseScale + b.fslope() * (1f - baseScale)); + + float height = bulletHeight * ((1f - bulletShrink) + bulletShrink * b.fout()); + + Draw.color(backColor); + Draw.rect(backRegion, b.x, b.y, bulletWidth * scale, height * scale, b.rot() - 90); + Draw.color(frontColor); + Draw.rect(frontRegion, b.x, b.y, bulletWidth * scale, height * scale, b.rot() - 90); + Draw.color(); + } +} diff --git a/core/src/io/anuke/mindustry/entities/bullet/BasicBulletType.java b/core/src/io/anuke/mindustry/entities/bullet/BasicBulletType.java new file mode 100644 index 0000000000..86b96ba7a8 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/bullet/BasicBulletType.java @@ -0,0 +1,40 @@ +package io.anuke.mindustry.entities.bullet; + +import io.anuke.arc.Core; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.graphics.g2d.Draw; +import io.anuke.arc.graphics.g2d.TextureRegion; +import io.anuke.mindustry.graphics.Pal; + +/** An extended BulletType for most ammo-based bullets shot from turrets and units. */ +public class BasicBulletType extends BulletType{ + public Color backColor = Pal.bulletYellowBack, frontColor = Pal.bulletYellow; + public float bulletWidth = 5f, bulletHeight = 7f; + public float bulletShrink = 0.5f; + public String bulletSprite; + + public TextureRegion backRegion; + public TextureRegion frontRegion; + + public BasicBulletType(float speed, float damage, String bulletSprite){ + super(speed, damage); + this.bulletSprite = bulletSprite; + } + + @Override + public void load(){ + backRegion = Core.atlas.find(bulletSprite + "-back"); + frontRegion = Core.atlas.find(bulletSprite); + } + + @Override + public void draw(Bullet b){ + float height = bulletHeight * ((1f - bulletShrink) + bulletShrink * b.fout()); + + Draw.color(backColor); + Draw.rect(backRegion, b.x, b.y, bulletWidth, height, b.rot() - 90); + Draw.color(frontColor); + Draw.rect(frontRegion, b.x, b.y, bulletWidth, height, b.rot() - 90); + Draw.color(); + } +} diff --git a/core/src/io/anuke/mindustry/entities/bullet/BombBulletType.java b/core/src/io/anuke/mindustry/entities/bullet/BombBulletType.java new file mode 100644 index 0000000000..be974f91e4 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/bullet/BombBulletType.java @@ -0,0 +1,17 @@ +package io.anuke.mindustry.entities.bullet; + +public class BombBulletType extends BasicBulletType{ + + public BombBulletType(float damage, float radius, String sprite){ + super(0.7f, 0, sprite); + splashDamageRadius = radius; + splashDamage = damage; + collidesTiles = false; + collides = false; + bulletShrink = 0.7f; + lifetime = 30f; + drag = 0.05f; + keepVelocity = false; + collidesAir = false; + } +} diff --git a/core/src/io/anuke/mindustry/entities/bullet/Bullet.java b/core/src/io/anuke/mindustry/entities/bullet/Bullet.java new file mode 100644 index 0000000000..4060376c14 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/bullet/Bullet.java @@ -0,0 +1,319 @@ +package io.anuke.mindustry.entities.bullet; + +import io.anuke.annotations.Annotations.Loc; +import io.anuke.annotations.Annotations.Remote; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.Rectangle; +import io.anuke.arc.math.geom.Vector2; +import io.anuke.arc.util.*; +import io.anuke.arc.util.pooling.Pool.Poolable; +import io.anuke.arc.util.pooling.Pools; +import io.anuke.mindustry.entities.EntityGroup; +import io.anuke.mindustry.entities.effect.Lightning; +import io.anuke.mindustry.entities.impl.SolidEntity; +import io.anuke.mindustry.entities.traits.*; +import io.anuke.mindustry.entities.type.Unit; +import io.anuke.mindustry.game.Team; +import io.anuke.mindustry.world.Tile; + +import static io.anuke.mindustry.Vars.bulletGroup; +import static io.anuke.mindustry.Vars.world; + +public class Bullet extends SolidEntity implements DamageTrait, ScaleTrait, Poolable, DrawTrait, VelocityTrait, TimeTrait, TeamTrait, AbsorbTrait{ + public Interval timer = new Interval(3); + + private float lifeScl; + private Team team; + private Object data; + private boolean supressCollision, supressOnce, initialized; + + protected BulletType type; + protected Entity owner; + protected float time; + + /** Internal use only! */ + public Bullet(){ + } + + public static Bullet create(BulletType type, TeamTrait owner, float x, float y, float angle){ + return create(type, owner, owner.getTeam(), x, y, angle); + } + + public static Bullet create(BulletType type, Entity owner, Team team, float x, float y, float angle){ + return create(type, owner, team, x, y, angle, 1f); + } + + public static Bullet create(BulletType type, Entity owner, Team team, float x, float y, float angle, float velocityScl){ + return create(type, owner, team, x, y, angle, velocityScl, 1f, null); + } + + public static Bullet create(BulletType type, Entity owner, Team team, float x, float y, float angle, float velocityScl, float lifetimeScl){ + return create(type, owner, team, x, y, angle, velocityScl, lifetimeScl, null); + } + + public static Bullet create(BulletType type, Entity owner, Team team, float x, float y, float angle, float velocityScl, float lifetimeScl, Object data){ + Bullet bullet = Pools.obtain(Bullet.class, Bullet::new); + bullet.type = type; + bullet.owner = owner; + bullet.data = data; + + bullet.velocity.set(0, type.speed).setAngle(angle).scl(velocityScl); + if(type.keepVelocity){ + bullet.velocity.add(owner instanceof VelocityTrait ? ((VelocityTrait)owner).velocity() : Vector2.ZERO); + } + + bullet.team = team; + bullet.type = type; + bullet.lifeScl = lifetimeScl; + + bullet.set(x - bullet.velocity.x * Time.delta(), y - bullet.velocity.y * Time.delta()); + bullet.add(); + + return bullet; + } + + public static Bullet create(BulletType type, Bullet parent, float x, float y, float angle){ + return create(type, parent.owner, parent.team, x, y, angle); + } + + public static Bullet create(BulletType type, Bullet parent, float x, float y, float angle, float velocityScl){ + return create(type, parent.owner, parent.team, x, y, angle, velocityScl); + } + + /** Internal use only. */ + @Remote(called = Loc.server, unreliable = true) + public static void createBullet(BulletType type, float x, float y, float angle){ + create(type, null, Team.none, x, y, angle); + } + + /** ok */ + @Remote(called = Loc.server, unreliable = true) + public static void createBullet(BulletType type, Team team, float x, float y, float angle){ + create(type, null, team, x, y, angle); + } + + public Entity getOwner(){ + return owner; + } + + public boolean collidesTiles(){ + return type.collidesTiles; + } + + public void supress(){ + supressCollision = true; + supressOnce = true; + } + + public BulletType getBulletType(){ + return type; + } + + public void resetOwner(Entity entity, Team team){ + this.owner = entity; + this.team = team; + } + + public void scaleTime(float add){ + time += add; + } + + public Object getData(){ + return data; + } + + public void setData(Object data){ + this.data = data; + } + + public float damageMultiplier(){ + if(owner instanceof Unit){ + return ((Unit)owner).getDamageMultipler(); + } + return 1f; + } + + @Override + public void absorb(){ + supressCollision = true; + remove(); + } + + @Override + public float drawSize(){ + return type.drawSize; + } + + @Override + public float damage(){ + if(owner instanceof Lightning && data instanceof Float){ + return (Float)data; + } + return type.damage * damageMultiplier(); + } + + @Override + public Team getTeam(){ + return team; + } + + @Override + public float getShieldDamage(){ + return Math.max(damage(), type.splashDamage); + } + + @Override + public boolean collides(SolidTrait other){ + return type.collides && (other != owner && !(other instanceof DamageTrait)) && !supressCollision && !(other instanceof Unit && ((Unit)other).isFlying() && !type.collidesAir); + } + + @Override + public void collision(SolidTrait other, float x, float y){ + if(!type.pierce) remove(); + type.hit(this, x, y); + + if(other instanceof Unit){ + Unit unit = (Unit)other; + unit.velocity().add(Tmp.v3.set(other.getX(), other.getY()).sub(x, y).setLength(type.knockback / unit.mass())); + unit.applyEffect(type.status, type.statusDuration); + } + } + + @Override + public void update(){ + type.update(this); + + x += velocity.x * Time.delta(); + y += velocity.y * Time.delta(); + + velocity.scl(Mathf.clamp(1f - type.drag * Time.delta())); + + time += Time.delta() * 1f / (lifeScl); + time = Mathf.clamp(time, 0, type.lifetime); + + if(time >= type.lifetime){ + if(!supressCollision) type.despawned(this); + remove(); + } + + if(type.hitTiles && collidesTiles() && !supressCollision && initialized){ + world.raycastEach(world.toTile(lastPosition().x), world.toTile(lastPosition().y), world.toTile(x), world.toTile(y), (x, y) -> { + + Tile tile = world.ltile(x, y); + if(tile == null) return false; + + if(tile.entity != null && tile.entity.collide(this) && type.collides(this, tile) && !tile.entity.isDead() && (type.collidesTeam || tile.getTeam() != team)){ + if(tile.getTeam() != team){ + tile.entity.collision(this); + } + + if(!supressCollision){ + type.hitTile(this, tile); + remove(); + } + + return true; + } + + return false; + }); + } + + if(supressOnce){ + supressCollision = false; + supressOnce = false; + } + + initialized = true; + } + + @Override + public void reset(){ + type = null; + owner = null; + velocity.setZero(); + time = 0f; + timer.clear(); + lifeScl = 1f; + team = null; + data = null; + supressCollision = false; + supressOnce = false; + initialized = false; + } + + @Override + public void hitbox(Rectangle rectangle){ + rectangle.setSize(type.hitSize).setCenter(x, y); + } + + @Override + public void hitboxTile(Rectangle rectangle){ + rectangle.setSize(type.hitSize).setCenter(x, y); + } + + @Override + public float lifetime(){ + return type.lifetime; + } + + @Override + public void time(float time){ + this.time = time; + } + + @Override + public float time(){ + return time; + } + + @Override + public void removed(){ + Pools.free(this); + } + + @Override + public EntityGroup targetGroup(){ + return bulletGroup; + } + + @Override + public void added(){ + type.init(this); + } + + @Override + public void draw(){ + type.draw(this); + } + + @Override + public float fin(){ + return time / type.lifetime; + } + + @Override + public Vector2 velocity(){ + return velocity; + } + + public void velocity(float speed, float angle){ + velocity.set(0, speed).setAngle(angle); + } + + public void limit(float f){ + velocity.limit(f); + } + + /** Sets the bullet's rotation in degrees. */ + public void rot(float angle){ + velocity.setAngle(angle); + } + + /** @return the bullet's rotation. */ + public float rot(){ + float angle = Mathf.atan2(velocity.x, velocity.y) * Mathf.radiansToDegrees; + if(angle < 0) angle += 360; + return angle; + } +} diff --git a/core/src/io/anuke/mindustry/entities/bullet/BulletType.java b/core/src/io/anuke/mindustry/entities/bullet/BulletType.java new file mode 100644 index 0000000000..b554c2ac68 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/bullet/BulletType.java @@ -0,0 +1,160 @@ +package io.anuke.mindustry.entities.bullet; + +import io.anuke.arc.math.Angles; +import io.anuke.arc.math.Mathf; +import io.anuke.mindustry.content.Fx; +import io.anuke.mindustry.content.StatusEffects; +import io.anuke.mindustry.entities.*; +import io.anuke.mindustry.entities.Effects.Effect; +import io.anuke.mindustry.entities.effect.Lightning; +import io.anuke.mindustry.entities.traits.TargetTrait; +import io.anuke.mindustry.game.Content; +import io.anuke.mindustry.graphics.Pal; +import io.anuke.mindustry.type.ContentType; +import io.anuke.mindustry.type.StatusEffect; +import io.anuke.mindustry.world.Tile; + +public abstract class BulletType extends Content{ + public float lifetime; + public float speed; + public float damage; + public float hitSize = 4; + public float drawSize = 40f; + public float drag = 0f; + public boolean pierce; + public Effect hitEffect, despawnEffect; + + /** Effect created when shooting. */ + public Effect shootEffect = Fx.shootSmall; + /** Extra smoke effect created when shooting. */ + public Effect smokeEffect = Fx.shootSmallSmoke; + /** Extra inaccuracy when firing. */ + public float inaccuracy = 0f; + /** How many bullets get created per ammo item/liquid. */ + public float ammoMultiplier = 1f; + /** Multiplied by turret reload speed to get final shoot speed. */ + public float reloadMultiplier = 1f; + /** Recoil from shooter entities. */ + public float recoil; + + public float splashDamage = 0f; + /** Knockback in velocity. */ + public float knockback; + /** Whether this bullet hits tiles. */ + public boolean hitTiles = true; + /** Status effect applied on hit. */ + public StatusEffect status = StatusEffects.none; + /** Intensity of applied status effect in terms of duration. */ + public float statusDuration = 60 * 1f; + /** Whether this bullet type collides with tiles. */ + public boolean collidesTiles = true; + /** Whether this bullet type collides with tiles that are of the same team. */ + public boolean collidesTeam = false; + /** Whether this bullet type collides with air units. */ + public boolean collidesAir = true; + /** Whether this bullet types collides with anything at all. */ + public boolean collides = true; + /** Whether velocity is inherited from the shooter. */ + public boolean keepVelocity = true; + + //additional effects + + public int fragBullets = 9; + public float fragVelocityMin = 0.2f, fragVelocityMax = 1f; + public BulletType fragBullet = null; + + /** Use a negative value to disable splash damage. */ + public float splashDamageRadius = -1f; + + public int incendAmount = 0; + public float incendSpread = 8f; + public float incendChance = 1f; + + public float homingPower = 0f; + public float homingRange = 50f; + + public int lightining; + public int lightningLength = 5; + + public float hitShake = 0f; + + public BulletType(float speed, float damage){ + this.speed = speed; + this.damage = damage; + lifetime = 40f; + hitEffect = Fx.hitBulletSmall; + despawnEffect = Fx.hitBulletSmall; + } + + /** Returns maximum distance the bullet this bullet type has can travel. */ + public float range(){ + return speed * lifetime * (1f - drag); + } + + public boolean collides(Bullet bullet, Tile tile){ + return true; + } + + public void hitTile(Bullet b, Tile tile){ + hit(b); + } + + public void hit(Bullet b){ + hit(b, b.x, b.y); + } + + public void hit(Bullet b, float x, float y){ + Effects.effect(hitEffect, x, y, b.rot()); + + Effects.shake(hitShake, hitShake, b); + + if(fragBullet != null){ + for(int i = 0; i < fragBullets; i++){ + float len = Mathf.random(1f, 7f); + float a = Mathf.random(360f); + Bullet.create(fragBullet, b, x + Angles.trnsx(a, len), y + Angles.trnsy(a, len), a, Mathf.random(fragVelocityMin, fragVelocityMax)); + } + } + + if(Mathf.chance(incendChance)){ + Damage.createIncend(x, y, incendSpread, incendAmount); + } + + if(splashDamageRadius > 0){ + Damage.damage(b.getTeam(), x, y, splashDamageRadius, splashDamage * b.damageMultiplier()); + } + } + + public void despawned(Bullet b){ + Effects.effect(despawnEffect, b.x, b.y, b.rot()); + + if(fragBullet != null || splashDamageRadius > 0){ + hit(b); + } + + for(int i = 0; i < lightining; i++){ + Lightning.create(b.getTeam(), Pal.surge, damage, b.x, b.y, Mathf.random(360f), lightningLength); + } + } + + public void draw(Bullet b){ + } + + public void init(Bullet b){ + } + + public void update(Bullet b){ + + if(homingPower > 0.0001f){ + TargetTrait target = Units.closestTarget(b.getTeam(), b.x, b.y, homingRange); + if(target != null){ + b.velocity().setAngle(Mathf.slerpDelta(b.velocity().angle(), b.angleTo(target), 0.08f)); + } + } + } + + @Override + public ContentType getContentType(){ + return ContentType.bullet; + } +} diff --git a/core/src/io/anuke/mindustry/entities/bullet/FlakBulletType.java b/core/src/io/anuke/mindustry/entities/bullet/FlakBulletType.java new file mode 100644 index 0000000000..0a435d0bcb --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/bullet/FlakBulletType.java @@ -0,0 +1,41 @@ +package io.anuke.mindustry.entities.bullet; + +import io.anuke.arc.math.geom.Rectangle; +import io.anuke.arc.util.Time; +import io.anuke.mindustry.content.Fx; +import io.anuke.mindustry.entities.Units; + +public abstract class FlakBulletType extends BasicBulletType{ + protected static Rectangle rect = new Rectangle(); + protected float explodeRange = 30f; + + public FlakBulletType(float speed, float damage){ + super(speed, damage, "shell"); + splashDamage = 15f; + splashDamageRadius = 34f; + hitEffect = Fx.flakExplosionBig; + bulletWidth = 8f; + bulletHeight = 10f; + } + + @Override + public void update(Bullet b){ + super.update(b); + if(b.getData() instanceof Integer) return; + + if(b.timer.get(2, 6)){ + Units.nearbyEnemies(b.getTeam(), rect.setSize(explodeRange * 2f).setCenter(b.x, b.y), unit -> { + if(b.getData() instanceof Float) return; + + if(unit.dst(b) < explodeRange){ + b.setData(0); + Time.run(5f, () -> { + if(b.getData() instanceof Integer){ + b.time(b.lifetime()); + } + }); + } + }); + } + } +} diff --git a/core/src/io/anuke/mindustry/entities/bullet/LiquidBulletType.java b/core/src/io/anuke/mindustry/entities/bullet/LiquidBulletType.java new file mode 100644 index 0000000000..cc2b97ca46 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/bullet/LiquidBulletType.java @@ -0,0 +1,76 @@ +package io.anuke.mindustry.entities.bullet; + +import io.anuke.arc.graphics.Color; +import io.anuke.arc.graphics.g2d.Draw; +import io.anuke.arc.graphics.g2d.Fill; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.Geometry; +import io.anuke.arc.math.geom.Point2; +import io.anuke.mindustry.content.Fx; +import io.anuke.mindustry.entities.Effects; +import io.anuke.mindustry.entities.effect.Fire; +import io.anuke.mindustry.entities.effect.Puddle; +import io.anuke.mindustry.type.Liquid; +import io.anuke.mindustry.world.Tile; + +import static io.anuke.mindustry.Vars.tilesize; +import static io.anuke.mindustry.Vars.world; + +public class LiquidBulletType extends BulletType{ + Liquid liquid; + + public LiquidBulletType(Liquid liquid){ + super(3.5f, 0); + this.liquid = liquid; + + lifetime = 74f; + status = liquid.effect; + statusDuration = 90f; + despawnEffect = Fx.none; + hitEffect = Fx.hitLiquid; + smokeEffect = Fx.none; + shootEffect = Fx.none; + drag = 0.009f; + knockback = 0.55f; + } + + @Override + public float range(){ + return speed * lifetime / 2f; + } + + @Override + public void update(Bullet b){ + super.update(b); + + if(liquid.canExtinguish()){ + Tile tile = world.tileWorld(b.x, b.y); + if(tile != null && Fire.has(tile.x, tile.y)){ + Fire.extinguish(tile, 100f); + b.remove(); + hit(b); + } + } + } + + @Override + public void draw(Bullet b){ + Draw.color(liquid.color, Color.WHITE, b.fout() / 100f + Mathf.randomSeedRange(b.id, 0.1f)); + + Fill.circle(b.x, b.y, 0.5f + b.fout() * 2.5f); + } + + @Override + public void hit(Bullet b, float hitx, float hity){ + Effects.effect(hitEffect, liquid.color, hitx, hity); + Puddle.deposit(world.tileWorld(hitx, hity), liquid, 5f); + + if(liquid.temperature <= 0.5f && liquid.flammability < 0.3f){ + float intensity = 400f; + Fire.extinguish(world.tileWorld(hitx, hity), intensity); + for(Point2 p : Geometry.d4){ + Fire.extinguish(world.tileWorld(hitx + p.x * tilesize, hity + p.y * tilesize), intensity); + } + } + } +} diff --git a/core/src/io/anuke/mindustry/entities/bullet/MassDriverBolt.java b/core/src/io/anuke/mindustry/entities/bullet/MassDriverBolt.java new file mode 100644 index 0000000000..a3bd379542 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/bullet/MassDriverBolt.java @@ -0,0 +1,106 @@ +package io.anuke.mindustry.entities.bullet; + +import io.anuke.arc.graphics.Color; +import io.anuke.arc.graphics.g2d.Draw; +import io.anuke.arc.math.Angles; +import io.anuke.arc.math.Mathf; +import io.anuke.mindustry.content.Fx; +import io.anuke.mindustry.entities.Effects; +import io.anuke.mindustry.graphics.Pal; +import io.anuke.mindustry.world.blocks.distribution.MassDriver.DriverBulletData; + +import static io.anuke.mindustry.Vars.content; + +public class MassDriverBolt extends BulletType{ + + public MassDriverBolt(){ + super(5.3f, 50); + collidesTiles = false; + lifetime = 200f; + despawnEffect = Fx.smeltsmoke; + hitEffect = Fx.hitBulletBig; + drag = 0.005f; + } + + @Override + public void draw(Bullet b){ + float w = 11f, h = 13f; + + Draw.color(Pal.bulletYellowBack); + Draw.rect("shell-back", b.x, b.y, w, h, b.rot() + 90); + + Draw.color(Pal.bulletYellow); + Draw.rect("shell", b.x, b.y, w, h, b.rot() + 90); + + Draw.reset(); + } + + @Override + public void update(Bullet b){ + //data MUST be an instance of DriverBulletData + if(!(b.getData() instanceof DriverBulletData)){ + hit(b); + return; + } + + float hitDst = 7f; + + DriverBulletData data = (DriverBulletData)b.getData(); + + //if the target is dead, just keep flying until the bullet explodes + if(data.to.isDead()){ + return; + } + + float baseDst = data.from.dst(data.to); + float dst1 = b.dst(data.from); + float dst2 = b.dst(data.to); + + boolean intersect = false; + + //bullet has gone past the destination point: but did it intersect it? + if(dst1 > baseDst){ + float angleTo = b.angleTo(data.to); + float baseAngle = data.to.angleTo(data.from); + + //if angles are nearby, then yes, it did + if(Angles.near(angleTo, baseAngle, 2f)){ + intersect = true; + //snap bullet position back; this is used for low-FPS situations + b.set(data.to.x + Angles.trnsx(baseAngle, hitDst), data.to.y + Angles.trnsy(baseAngle, hitDst)); + } + } + + //if on course and it's in range of the target + if(Math.abs(dst1 + dst2 - baseDst) < 4f && dst2 <= hitDst){ + intersect = true; + } //else, bullet has gone off course, does not get recieved. + + if(intersect){ + data.to.handlePayload(b, data); + } + } + + @Override + public void despawned(Bullet b){ + super.despawned(b); + + if(!(b.getData() instanceof DriverBulletData)) return; + + DriverBulletData data = (DriverBulletData)b.getData(); + + for(int i = 0; i < data.items.length; i++){ + int amountDropped = Mathf.random(0, data.items[i]); + if(amountDropped > 0){ + float angle = b.rot() + Mathf.range(100f); + Effects.effect(Fx.dropItem, Color.WHITE, b.x, b.y, angle, content.item(i)); + } + } + } + + @Override + public void hit(Bullet b, float hitx, float hity){ + super.hit(b, hitx, hity); + despawned(b); + } +} diff --git a/core/src/io/anuke/mindustry/entities/bullet/MissileBulletType.java b/core/src/io/anuke/mindustry/entities/bullet/MissileBulletType.java new file mode 100644 index 0000000000..56a6b5ae7f --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/bullet/MissileBulletType.java @@ -0,0 +1,35 @@ +package io.anuke.mindustry.entities.bullet; + +import io.anuke.arc.graphics.Color; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.util.Time; +import io.anuke.mindustry.content.Fx; +import io.anuke.mindustry.entities.Effects; +import io.anuke.mindustry.graphics.Pal; + +public class MissileBulletType extends BasicBulletType{ + protected Color trailColor = Pal.missileYellowBack; + + protected float weaveScale = 0f; + protected float weaveMag = -1f; + + public MissileBulletType(float speed, float damage, String bulletSprite){ + super(speed, damage, bulletSprite); + backColor = Pal.missileYellowBack; + frontColor = Pal.missileYellow; + homingPower = 7f; + } + + @Override + public void update(Bullet b){ + super.update(b); + + if(Mathf.chance(Time.delta() * 0.2)){ + Effects.effect(Fx.missileTrail, trailColor, b.x, b.y, 2f); + } + + if(weaveMag > 0){ + b.velocity().rotate(Mathf.sin(Time.time() + b.id * 4422, weaveScale, weaveMag)); + } + } +} diff --git a/core/src/io/anuke/mindustry/entities/effect/DamageArea.java b/core/src/io/anuke/mindustry/entities/effect/DamageArea.java deleted file mode 100644 index c1ce0382f9..0000000000 --- a/core/src/io/anuke/mindustry/entities/effect/DamageArea.java +++ /dev/null @@ -1,111 +0,0 @@ -package io.anuke.mindustry.entities.effect; - -import com.badlogic.gdx.math.Rectangle; -import com.badlogic.gdx.math.Vector2; -import io.anuke.mindustry.entities.Player; -import io.anuke.mindustry.world.Tile; -import io.anuke.ucore.core.Effects; -import io.anuke.ucore.core.Effects.Effect; -import io.anuke.ucore.entities.DestructibleEntity; -import io.anuke.ucore.entities.Entities; -import io.anuke.ucore.entities.Entity; -import io.anuke.ucore.entities.SolidEntity; -import io.anuke.ucore.function.Consumer; -import io.anuke.ucore.util.Mathf; -import io.anuke.ucore.util.Physics; -import io.anuke.ucore.util.Translator; - -import static io.anuke.mindustry.Vars.*; - -public class DamageArea{ - private static Rectangle rect = new Rectangle(); - private static Translator tr = new Translator(); - - //only for entities, not tiles (yet!) - public static void damageLine(Entity owner, Effect effect, float x, float y, float angle, float length, int damage){ - tr.trns(angle, length); - rect.setPosition(x, y).setSize(tr.x, tr.y); - float x2 = tr.x + x, y2 = tr.y + y; - - if(rect.width < 0){ - rect.x += rect.width; - rect.width *= -1; - } - - if(rect.height < 0){ - rect.y += rect.height; - rect.height *= -1; - } - - float expand = 3f; - - rect.y -= expand; - rect.x -= expand; - rect.width += expand*2; - rect.height += expand*2; - - Consumer cons = e -> { - if(e == owner || (e instanceof Player && ((Player)e).isAndroid)) return; - DestructibleEntity enemy = (DestructibleEntity) e; - Rectangle other = enemy.hitbox.getRect(enemy.x, enemy.y); - other.y -= expand; - other.x -= expand; - other.width += expand * 2; - other.height += expand * 2; - - Vector2 vec = Physics.raycastRect(x, y, x2, y2, other); - - if (vec != null) { - Effects.effect(effect, vec.x, vec.y); - enemy.damage(damage); - } - }; - - Entities.getNearby(enemyGroup, rect, cons); - if(state.friendlyFire) Entities.getNearby(playerGroup, rect, cons); - } - - public static void damageEntities(float x, float y, float radius, int damage){ - damage(true, x, y, radius, damage); - - for(Player player : playerGroup.all()){ - if(player.isAndroid) continue; - int amount = calculateDamage(x, y, player.x, player.y, radius, damage); - player.damage(amount); - } - } - - public static void damage(boolean enemies, float x, float y, float radius, int damage){ - Consumer cons = entity -> { - DestructibleEntity enemy = (DestructibleEntity)entity; - if(enemy.distanceTo(x, y) > radius || (entity instanceof Player && ((Player)entity).isAndroid)){ - return; - } - int amount = calculateDamage(x, y, enemy.x, enemy.y, radius, damage); - enemy.damage(amount); - }; - - if(enemies){ - Entities.getNearby(enemyGroup, x, y, radius*2, cons); - }else{ - int trad = (int)(radius / tilesize); - for(int dx = -trad; dx <= trad; dx ++){ - for(int dy= -trad; dy <= trad; dy ++){ - Tile tile = world.tile(Mathf.scl2(x, tilesize) + dx, Mathf.scl2(y, tilesize) + dy); - if(tile != null && tile.entity != null && Vector2.dst(dx, dy, 0, 0) <= trad){ - int amount = calculateDamage(x, y, tile.worldx(), tile.worldy(), radius, damage); - tile.entity.damage(amount); - } - } - } - - Entities.getNearby(playerGroup, x, y, radius*2, cons); - } - } - - static int calculateDamage(float x, float y, float tx, float ty, float radius, int damage){ - float dist = Vector2.dst(x, y, tx, ty); - float scaled = 1f - dist/radius; - return (int)(damage * scaled); - } -} diff --git a/core/src/io/anuke/mindustry/entities/effect/Decal.java b/core/src/io/anuke/mindustry/entities/effect/Decal.java new file mode 100644 index 0000000000..79b2c3276d --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/effect/Decal.java @@ -0,0 +1,36 @@ +package io.anuke.mindustry.entities.effect; + +import io.anuke.arc.graphics.g2d.Draw; +import io.anuke.arc.math.Mathf; +import io.anuke.mindustry.entities.EntityGroup; +import io.anuke.mindustry.entities.impl.TimedEntity; +import io.anuke.mindustry.entities.traits.BelowLiquidTrait; +import io.anuke.mindustry.entities.traits.DrawTrait; +import io.anuke.mindustry.graphics.Pal; + +import static io.anuke.mindustry.Vars.groundEffectGroup; + +/** + * Class for creating block rubble on the ground. + */ +public abstract class Decal extends TimedEntity implements BelowLiquidTrait, DrawTrait{ + + @Override + public float lifetime(){ + return 8200f; + } + + @Override + public void draw(){ + Draw.color(Pal.rubble.r, Pal.rubble.g, Pal.rubble.b, 1f - Mathf.curve(fin(), 0.98f)); + drawDecal(); + Draw.color(); + } + + @Override + public EntityGroup targetGroup(){ + return groundEffectGroup; + } + + abstract void drawDecal(); +} diff --git a/core/src/io/anuke/mindustry/entities/effect/EMP.java b/core/src/io/anuke/mindustry/entities/effect/EMP.java deleted file mode 100644 index e7afc7b366..0000000000 --- a/core/src/io/anuke/mindustry/entities/effect/EMP.java +++ /dev/null @@ -1,120 +0,0 @@ -package io.anuke.mindustry.entities.effect; - -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.math.Vector2; -import com.badlogic.gdx.utils.Array; -import io.anuke.mindustry.graphics.Fx; -import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.blocks.types.PowerAcceptor; -import io.anuke.ucore.core.Effects; -import io.anuke.ucore.entities.TimedEntity; -import io.anuke.ucore.graphics.Draw; -import io.anuke.ucore.graphics.Lines; -import io.anuke.ucore.util.Mathf; -import io.anuke.ucore.util.Translator; - -import static io.anuke.mindustry.Vars.tilesize; -import static io.anuke.mindustry.Vars.world; - -public class EMP extends TimedEntity{ - static final int maxTargets = 8; - static Array array = new Array<>(); - static Translator tr = new Translator(); - - int radius = 4; - int damage = 6; - Array targets = new Array<>(maxTargets); - - public EMP(float x, float y, int damage){ - this.damage = damage; - set(x, y); - - lifetime = 30f; - - int worldx = Mathf.scl2(x, tilesize); - int worldy = Mathf.scl2(y, tilesize); - - array.clear(); - - for(int dx = -radius; dx <= radius; dx ++){ - for(int dy = -radius; dy <= radius; dy ++){ - if(Vector2.dst(dx, dy, 0, 0) < radius){ - Tile tile = world.tile(worldx + dx, worldy + dy); - - if(tile != null && tile.block().destructible){ - array.add(tile); - } - } - } - } - - array.shuffle(); - - for(int i = 0; i < array.size && i < maxTargets; i ++){ - Tile tile = array.get(i); - targets.add(tile); - - if(tile != null && tile.block() instanceof PowerAcceptor){ - PowerAcceptor p = (PowerAcceptor)tile.block(); - p.setPower(tile, 0f); - tile.entity.damage((int)(damage*2f)); //extra damage - } - - if(tile == null) continue; - - //entity may be null here, after the block is dead! - Effects.effect(Fx.empspark, tile.worldx(), tile.worldy()); - if(tile.entity != null) tile.entity.damage(damage); - } - } - - @Override - public void drawOver(){ - Draw.color(Color.SKY); - - for(int i = 0; i < targets.size; i ++){ - Tile target = targets.get(i); - - drawLine(target.worldx(), target.worldy()); - - float rad = 5f*fout(); - Draw.rect("circle", target.worldx(), target.worldy(), rad, rad); - } - - for(int i = 0; i < 14 - targets.size; i ++){ - tr.trns(Mathf.randomSeed(i + id*77)*360f, radius * tilesize); - drawLine(x + tr.x, y + tr.y); - } - - Lines.stroke(fout()*2f); - Lines.poly(x, y, 34, radius * tilesize); - - Draw.reset(); - } - - private void drawLine(float targetx, float targety){ - int joints = 3; - float r = 3f; - float lastx = x, lasty = y; - - for(int seg = 0; seg < joints; seg ++){ - float dx = Mathf.range(r), - dy = Mathf.range(r); - - float frac = (seg+1f)/joints; - - float tx = (targetx - x)*frac + x + dx, - ty = (targety - y)*frac + y + dy; - - drawLaser(lastx, lasty, tx, ty); - - lastx = tx; - lasty = ty; - } - } - - private void drawLaser(float x, float y, float x2, float y2){ - Lines.stroke(fout() * 2f); - Lines.line(x, y, x2, y2); - } -} diff --git a/core/src/io/anuke/mindustry/entities/effect/Fire.java b/core/src/io/anuke/mindustry/entities/effect/Fire.java new file mode 100644 index 0000000000..7a6d02dbef --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/effect/Fire.java @@ -0,0 +1,216 @@ +package io.anuke.mindustry.entities.effect; + +import io.anuke.annotations.Annotations.Remote; +import io.anuke.arc.collection.IntMap; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.Geometry; +import io.anuke.arc.math.geom.Point2; +import io.anuke.arc.util.*; +import io.anuke.mindustry.content.*; +import io.anuke.mindustry.entities.*; +import io.anuke.mindustry.entities.impl.TimedEntity; +import io.anuke.mindustry.entities.traits.SaveTrait; +import io.anuke.mindustry.entities.traits.SyncTrait; +import io.anuke.mindustry.entities.type.TileEntity; +import io.anuke.mindustry.gen.Call; +import io.anuke.mindustry.net.Net; +import io.anuke.mindustry.world.*; + +import java.io.*; + +import static io.anuke.mindustry.Vars.*; + +public class Fire extends TimedEntity implements SaveTrait, SyncTrait{ + private static final IntMap map = new IntMap<>(); + private static final float baseLifetime = 1000f, spreadChance = 0.05f, fireballChance = 0.07f; + + private int loadedPosition = -1; + private Tile tile; + private Block block; + private float baseFlammability = -1, puddleFlammability; + private float lifetime; + + /** Deserialization use only! */ + public Fire(){ + } + + @Remote + public static void onRemoveFire(int fid){ + fireGroup.removeByID(fid); + } + + /** Start a fire on the tile. If there already is a file there, refreshes its lifetime. */ + public static void create(Tile tile){ + if(Net.client() || tile == null) return; //not clientside. + + Fire fire = map.get(tile.pos()); + + if(fire == null){ + fire = new Fire(); + fire.tile = tile; + fire.lifetime = baseLifetime; + fire.set(tile.worldx(), tile.worldy()); + fire.add(); + map.put(tile.pos(), fire); + }else{ + fire.lifetime = baseLifetime; + fire.time = 0f; + } + } + + public static boolean has(int x, int y){ + if(!Structs.inBounds(x, y, world.width(), world.height()) || !map.containsKey(Pos.get(x, y))){ + return false; + } + Fire fire = map.get(Pos.get(x, y)); + return fire.isAdded() && fire.fin() < 1f && fire.tile != null && fire.tile.x == x && fire.tile.y == y; + } + + /** + * Attempts to extinguish a fire by shortening its life. If there is no fire here, does nothing. + */ + public static void extinguish(Tile tile, float intensity){ + if(tile != null && map.containsKey(tile.pos())){ + map.get(tile.pos()).time += intensity * Time.delta(); + } + } + + @Override + public byte version(){ + return 0; + } + + @Override + public float lifetime(){ + return lifetime; + } + + @Override + public void update(){ + if(Mathf.chance(0.1 * Time.delta())){ + Effects.effect(Fx.fire, x + Mathf.range(4f), y + Mathf.range(4f)); + } + + if(Mathf.chance(0.05 * Time.delta())){ + Effects.effect(Fx.fireSmoke, x + Mathf.range(4f), y + Mathf.range(4f)); + } + + time = Mathf.clamp(time + Time.delta(), 0, lifetime()); + map.put(tile.pos(), this); + + if(Net.client()){ + return; + } + + if(time >= lifetime() || tile == null){ + remove(); + return; + } + + TileEntity entity = tile.link().entity; + boolean damage = entity != null; + + float flammability = baseFlammability + puddleFlammability; + + if(!damage && flammability <= 0){ + time += Time.delta() * 8; + } + + if(baseFlammability < 0 || block != tile.block()){ + baseFlammability = tile.block().getFlammability(tile); + block = tile.block(); + } + + if(damage){ + lifetime += Mathf.clamp(flammability / 8f, 0f, 0.6f) * Time.delta(); + } + + if(flammability > 1f && Mathf.chance(spreadChance * Time.delta() * Mathf.clamp(flammability / 5f, 0.3f, 2f))){ + Point2 p = Geometry.d4[Mathf.random(3)]; + Tile other = world.tile(tile.x + p.x, tile.y + p.y); + create(other); + + if(Mathf.chance(fireballChance * Time.delta() * Mathf.clamp(flammability / 10f))){ + Call.createBullet(Bullets.fireball, x, y, Mathf.random(360f)); + } + } + + if(Mathf.chance(0.1 * Time.delta())){ + Puddle p = Puddle.getPuddle(tile); + if(p != null){ + puddleFlammability = p.getFlammability() / 3f; + }else{ + puddleFlammability = 0; + } + + if(damage){ + entity.damage(0.4f); + } + Damage.damageUnits(null, tile.worldx(), tile.worldy(), tilesize, 3f, + unit -> !unit.isFlying() && !unit.isImmune(StatusEffects.burning), + unit -> unit.applyEffect(StatusEffects.burning, 60 * 5)); + } + } + + @Override + public void writeSave(DataOutput stream) throws IOException{ + stream.writeInt(tile.pos()); + stream.writeFloat(lifetime); + stream.writeFloat(time); + } + + @Override + public void readSave(DataInput stream, byte version) throws IOException{ + this.loadedPosition = stream.readInt(); + this.lifetime = stream.readFloat(); + this.time = stream.readFloat(); + add(); + } + + @Override + public void write(DataOutput data) throws IOException{ + data.writeInt(tile.pos()); + data.writeFloat(lifetime); + } + + @Override + public void read(DataInput data) throws IOException{ + int pos = data.readInt(); + this.lifetime = data.readFloat(); + + x = Pos.x(pos) * tilesize; + y = Pos.y(pos) * tilesize; + tile = world.tile(pos); + } + + @Override + public void reset(){ + loadedPosition = -1; + tile = null; + baseFlammability = -1; + puddleFlammability = 0f; + incrementID(); + } + + @Override + public void added(){ + if(loadedPosition != -1){ + map.put(loadedPosition, this); + tile = world.tile(loadedPosition); + set(tile.worldx(), tile.worldy()); + } + } + + @Override + public void removed(){ + if(tile != null){ + Call.onRemoveFire(id); + map.remove(tile.pos()); + } + } + + @Override + public EntityGroup targetGroup(){ + return fireGroup; + } +} diff --git a/core/src/io/anuke/mindustry/entities/effect/GroundEffectEntity.java b/core/src/io/anuke/mindustry/entities/effect/GroundEffectEntity.java new file mode 100644 index 0000000000..54094c4a87 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/effect/GroundEffectEntity.java @@ -0,0 +1,90 @@ +package io.anuke.mindustry.entities.effect; + +import io.anuke.arc.math.Mathf; +import io.anuke.arc.util.Time; +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.entities.Effects; +import io.anuke.mindustry.entities.Effects.Effect; +import io.anuke.mindustry.entities.Effects.EffectRenderer; +import io.anuke.mindustry.entities.impl.EffectEntity; +import io.anuke.mindustry.world.Tile; + +/** + * A ground effect contains an effect that is rendered on the ground layer as opposed to the top layer. + */ +public class GroundEffectEntity extends EffectEntity{ + private boolean once; + + @Override + public void update(){ + GroundEffect effect = (GroundEffect)this.effect; + + if(effect.isStatic){ + time += Time.delta(); + + time = Mathf.clamp(time, 0, effect.staticLife); + + if(!once && time >= lifetime()){ + once = true; + time = 0f; + Tile tile = Vars.world.tileWorld(x, y); + if(tile != null && tile.floor().isLiquid){ + remove(); + } + }else if(once && time >= effect.staticLife){ + remove(); + } + }else{ + super.update(); + } + } + + @Override + public void draw(){ + GroundEffect effect = (GroundEffect)this.effect; + + if(once && effect.isStatic) + Effects.renderEffect(id, effect, color, lifetime(), rotation, x, y, data); + else + Effects.renderEffect(id, effect, color, time, rotation, x, y, data); + } + + @Override + public void reset(){ + super.reset(); + once = false; + } + + /** + * An effect that is rendered on the ground layer as opposed to the top layer. + */ + public static class GroundEffect extends Effect{ + /** + * How long this effect stays on the ground when static. + */ + public final float staticLife; + /** + * If true, this effect will stop and lie on the ground for a specific duration, + * after its initial lifetime is over. + */ + public final boolean isStatic; + + public GroundEffect(float life, float staticLife, EffectRenderer draw){ + super(life, draw); + this.staticLife = staticLife; + this.isStatic = true; + } + + public GroundEffect(boolean isStatic, float life, EffectRenderer draw){ + super(life, draw); + this.staticLife = 0f; + this.isStatic = isStatic; + } + + public GroundEffect(float life, EffectRenderer draw){ + super(life, draw); + this.staticLife = 0f; + this.isStatic = false; + } + } +} diff --git a/core/src/io/anuke/mindustry/entities/effect/ItemTransfer.java b/core/src/io/anuke/mindustry/entities/effect/ItemTransfer.java new file mode 100644 index 0000000000..2cb7f567e1 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/effect/ItemTransfer.java @@ -0,0 +1,120 @@ +package io.anuke.mindustry.entities.effect; + +import io.anuke.annotations.Annotations.Loc; +import io.anuke.annotations.Annotations.Remote; +import io.anuke.arc.graphics.g2d.*; +import io.anuke.arc.math.Interpolation; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.Position; +import io.anuke.arc.math.geom.Vector2; +import io.anuke.arc.util.Time; +import io.anuke.arc.util.pooling.Pools; +import io.anuke.mindustry.entities.EntityGroup; +import io.anuke.mindustry.entities.impl.TimedEntity; +import io.anuke.mindustry.entities.traits.DrawTrait; +import io.anuke.mindustry.entities.type.Unit; +import io.anuke.mindustry.graphics.Pal; +import io.anuke.mindustry.type.Item; +import io.anuke.mindustry.world.Tile; + +import static io.anuke.mindustry.Vars.effectGroup; + +public class ItemTransfer extends TimedEntity implements DrawTrait{ + private Vector2 from = new Vector2(); + private Vector2 current = new Vector2(); + private Vector2 tovec = new Vector2(); + private Item item; + private float seed; + private Position to; + private Runnable done; + + public ItemTransfer(){ + } + + @Remote(called = Loc.server, unreliable = true) + public static void transferItemEffect(Item item, float x, float y, Unit to){ + if(to == null) return; + create(item, x, y, to, () -> { + }); + } + + @Remote(called = Loc.server, unreliable = true) + public static void transferItemToUnit(Item item, float x, float y, Unit to){ + if(to == null) return; + create(item, x, y, to, () -> to.addItem(item)); + } + + @Remote(called = Loc.server) + public static void transferItemTo(Item item, int amount, float x, float y, Tile tile){ + if(tile == null || tile.entity == null || tile.entity.items == null) return; + for(int i = 0; i < Mathf.clamp(amount / 3, 1, 8); i++){ + Time.run(i * 3, () -> create(item, x, y, tile, () -> { + })); + } + tile.entity.items.add(item, amount); + } + + public static void create(Item item, float fromx, float fromy, Position to, Runnable done){ + ItemTransfer tr = Pools.obtain(ItemTransfer.class, ItemTransfer::new); + tr.item = item; + tr.from.set(fromx, fromy); + tr.to = to; + tr.done = done; + tr.seed = Mathf.range(1f); + tr.add(); + } + + @Override + public float lifetime(){ + return 60; + } + + @Override + public void reset(){ + super.reset(); + item = null; + to = null; + done = null; + from.setZero(); + current.setZero(); + tovec.setZero(); + } + + @Override + public void removed(){ + if(done != null){ + done.run(); + } + Pools.free(this); + } + + @Override + public void update(){ + if(to == null){ + remove(); + return; + } + + super.update(); + current.set(from).interpolate(tovec.set(to.getX(), to.getY()), fin(), Interpolation.pow3); + current.add(tovec.set(to.getX(), to.getY()).sub(from).nor().rotate90(1).scl(seed * fslope() * 10f)); + set(current.x, current.y); + } + + @Override + public void draw(){ + Lines.stroke(fslope() * 2f, Pal.accent); + + Lines.circle(x, y, fslope() * 2f); + + Draw.color(item.color); + Fill.circle(x, y, fslope() * 1.5f); + + Draw.reset(); + } + + @Override + public EntityGroup targetGroup(){ + return effectGroup; + } +} diff --git a/core/src/io/anuke/mindustry/entities/effect/Lightning.java b/core/src/io/anuke/mindustry/entities/effect/Lightning.java new file mode 100644 index 0000000000..bc842d7d95 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/effect/Lightning.java @@ -0,0 +1,134 @@ +package io.anuke.mindustry.entities.effect; + +import io.anuke.annotations.Annotations.Loc; +import io.anuke.annotations.Annotations.Remote; +import io.anuke.arc.collection.Array; +import io.anuke.arc.collection.IntSet; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.graphics.g2d.*; +import io.anuke.arc.math.*; +import io.anuke.arc.math.geom.*; +import io.anuke.arc.util.pooling.Pools; +import io.anuke.mindustry.content.Bullets; +import io.anuke.mindustry.entities.EntityGroup; +import io.anuke.mindustry.entities.Units; +import io.anuke.mindustry.entities.bullet.Bullet; +import io.anuke.mindustry.entities.impl.TimedEntity; +import io.anuke.mindustry.entities.traits.DrawTrait; +import io.anuke.mindustry.entities.traits.TimeTrait; +import io.anuke.mindustry.entities.type.Unit; +import io.anuke.mindustry.game.Team; +import io.anuke.mindustry.gen.Call; +import io.anuke.mindustry.graphics.Pal; + +import static io.anuke.mindustry.Vars.bulletGroup; + +public class Lightning extends TimedEntity implements DrawTrait, TimeTrait{ + public static final float lifetime = 10f; + + private static final RandomXS128 random = new RandomXS128(); + private static final Rectangle rect = new Rectangle(); + private static final Array entities = new Array<>(); + private static final IntSet hit = new IntSet(); + private static final int maxChain = 8; + private static final float hitRange = 30f; + private static int lastSeed = 0; + + private Array lines = new Array<>(); + private Color color = Pal.lancerLaser; + + /** For pooling use only. Do not call directly! */ + public Lightning(){ + } + + /** Create a lighting branch at a location. Use Team.none to damage everyone. */ + public static void create(Team team, Color color, float damage, float x, float y, float targetAngle, int length){ + Call.createLighting(lastSeed++, team, color, damage, x, y, targetAngle, length); + } + + /** Do not invoke! */ + @Remote(called = Loc.server) + public static void createLighting(int seed, Team team, Color color, float damage, float x, float y, float rotation, int length){ + + Lightning l = Pools.obtain(Lightning.class, Lightning::new); + Float dmg = damage; + + l.x = x; + l.y = y; + l.color = color; + l.add(); + + random.setSeed(seed); + hit.clear(); + + for(int i = 0; i < length / 2; i++){ + Bullet.create(Bullets.damageLightning, l, team, x, y, 0f, 1f, 1f, dmg); + l.lines.add(new Vector2(x + Mathf.range(3f), y + Mathf.range(3f))); + + rect.setSize(hitRange).setCenter(x, y); + entities.clear(); + if(hit.size < maxChain){ + Units.nearbyEnemies(team, rect, u -> { + if(!hit.contains(u.getID())){ + entities.add(u); + } + }); + } + + Unit furthest = Geometry.findFurthest(x, y, entities); + + if(furthest != null){ + hit.add(furthest.getID()); + x = furthest.x; + y = furthest.y; + }else{ + rotation += random.range(20f); + x += Angles.trnsx(rotation, hitRange / 2f); + y += Angles.trnsy(rotation, hitRange / 2f); + } + } + } + + @Override + public float lifetime(){ + return lifetime; + } + + @Override + public void reset(){ + super.reset(); + color = Pal.lancerLaser; + lines.clear(); + } + + @Override + public void removed(){ + super.removed(); + Pools.free(this); + } + + @Override + public void draw(){ + Lines.stroke(3f * fout()); + Draw.color(color, Color.WHITE, fin()); + Lines.beginLine(); + + Lines.linePoint(x, y); + for(Position p : lines){ + Lines.linePoint(p.getX(), p.getY()); + } + Lines.endLine(); + + int i = 0; + + for(Position p : lines){ + Fill.square(p.getX(), p.getY(), (5f - (float)i++ / lines.size * 2f) * fout(), 45); + } + Draw.reset(); + } + + @Override + public EntityGroup targetGroup(){ + return bulletGroup; + } +} diff --git a/core/src/io/anuke/mindustry/entities/effect/Puddle.java b/core/src/io/anuke/mindustry/entities/effect/Puddle.java new file mode 100644 index 0000000000..4b633ac41d --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/effect/Puddle.java @@ -0,0 +1,312 @@ +package io.anuke.mindustry.entities.effect; + +import io.anuke.annotations.Annotations.Loc; +import io.anuke.annotations.Annotations.Remote; +import io.anuke.arc.collection.IntMap; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.graphics.g2d.Draw; +import io.anuke.arc.graphics.g2d.Fill; +import io.anuke.arc.math.Angles; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.*; +import io.anuke.arc.util.Time; +import io.anuke.arc.util.pooling.Pool.Poolable; +import io.anuke.arc.util.pooling.Pools; +import io.anuke.mindustry.content.*; +import io.anuke.mindustry.entities.*; +import io.anuke.mindustry.entities.impl.SolidEntity; +import io.anuke.mindustry.entities.traits.*; +import io.anuke.mindustry.gen.Call; +import io.anuke.mindustry.net.Net; +import io.anuke.mindustry.type.Liquid; +import io.anuke.mindustry.world.Tile; + +import java.io.*; + +import static io.anuke.mindustry.Vars.*; + +public class Puddle extends SolidEntity implements SaveTrait, Poolable, DrawTrait, SyncTrait{ + private static final IntMap map = new IntMap<>(); + private static final float maxLiquid = 70f; + private static final int maxGeneration = 2; + private static final Color tmp = new Color(); + private static final Rectangle rect = new Rectangle(); + private static final Rectangle rect2 = new Rectangle(); + private static int seeds; + + private int loadedPosition = -1; + + private float updateTime; + private float lastRipple; + private Tile tile; + private Liquid liquid; + private float amount, targetAmount; + private float accepting; + private byte generation; + + /** Deserialization use only! */ + public Puddle(){ + } + + /** Deposists a puddle between tile and source. */ + public static void deposit(Tile tile, Tile source, Liquid liquid, float amount){ + deposit(tile, source, liquid, amount, 0); + } + + /** Deposists a puddle at a tile. */ + public static void deposit(Tile tile, Liquid liquid, float amount){ + deposit(tile, tile, liquid, amount, 0); + } + + /** Returns the puddle on the specified tile. May return null. */ + public static Puddle getPuddle(Tile tile){ + return map.get(tile.pos()); + } + + private static void deposit(Tile tile, Tile source, Liquid liquid, float amount, int generation){ + if(tile == null) return; + + if(tile.floor().isLiquid && !canStayOn(liquid, tile.floor().liquidDrop)){ + reactPuddle(tile.floor().liquidDrop, liquid, amount, tile, + (tile.worldx() + source.worldx()) / 2f, (tile.worldy() + source.worldy()) / 2f); + + Puddle p = map.get(tile.pos()); + + if(generation == 0 && p != null && p.lastRipple <= Time.time() - 40f){ + Effects.effect(Fx.ripple, tile.floor().liquidDrop.color, + (tile.worldx() + source.worldx()) / 2f, (tile.worldy() + source.worldy()) / 2f); + p.lastRipple = Time.time(); + } + return; + } + + Puddle p = map.get(tile.pos()); + if(p == null){ + if(Net.client()) return; //not clientside. + + Puddle puddle = Pools.obtain(Puddle.class, Puddle::new); + puddle.tile = tile; + puddle.liquid = liquid; + puddle.amount = amount; + puddle.generation = (byte)generation; + puddle.set((tile.worldx() + source.worldx()) / 2f, (tile.worldy() + source.worldy()) / 2f); + puddle.add(); + map.put(tile.pos(), puddle); + }else if(p.liquid == liquid){ + p.accepting = Math.max(amount, p.accepting); + + if(generation == 0 && p.lastRipple <= Time.time() - 40f && p.amount >= maxLiquid / 2f){ + Effects.effect(Fx.ripple, p.liquid.color, (tile.worldx() + source.worldx()) / 2f, (tile.worldy() + source.worldy()) / 2f); + p.lastRipple = Time.time(); + } + }else{ + p.amount += reactPuddle(p.liquid, liquid, amount, p.tile, p.x, p.y); + } + } + + /** + * Returns whether the first liquid can 'stay' on the second one. + * Currently, the only place where this can happen is oil on water. + */ + private static boolean canStayOn(Liquid liquid, Liquid other){ + return liquid == Liquids.oil && other == Liquids.water; + } + + /** Reacts two liquids together at a location. */ + private static float reactPuddle(Liquid dest, Liquid liquid, float amount, Tile tile, float x, float y){ + if((dest.flammability > 0.3f && liquid.temperature > 0.7f) || + (liquid.flammability > 0.3f && dest.temperature > 0.7f)){ //flammable liquid + hot liquid + Fire.create(tile); + if(Mathf.chance(0.006 * amount)){ + Call.createBullet(Bullets.fireball, x, y, Mathf.random(360f)); + } + }else if(dest.temperature > 0.7f && liquid.temperature < 0.55f){ //cold liquid poured onto hot puddle + if(Mathf.chance(0.5f * amount)){ + Effects.effect(Fx.steam, x, y); + } + return -0.1f * amount; + }else if(liquid.temperature > 0.7f && dest.temperature < 0.55f){ //hot liquid poured onto cold puddle + if(Mathf.chance(0.8f * amount)){ + Effects.effect(Fx.steam, x, y); + } + return -0.4f * amount; + } + return 0f; + } + + @Remote(called = Loc.server) + public static void onPuddleRemoved(int puddleid){ + puddleGroup.removeByID(puddleid); + } + + public float getFlammability(){ + return liquid.flammability * amount; + } + + @Override + public byte version(){ + return 0; + } + + @Override + public void hitbox(Rectangle rectangle){ + rectangle.setCenter(x, y).setSize(tilesize); + } + + @Override + public void hitboxTile(Rectangle rectangle){ + rectangle.setCenter(x, y).setSize(0f); + } + + @Override + public void update(){ + + //no updating happens clientside + if(Net.client()){ + amount = Mathf.lerpDelta(amount, targetAmount, 0.15f); + }else{ + //update code + float addSpeed = accepting > 0 ? 3f : 0f; + + amount -= Time.delta() * (1f - liquid.viscosity) / (5f + addSpeed); + + amount += accepting; + accepting = 0f; + + if(amount >= maxLiquid / 1.5f && generation < maxGeneration){ + float deposited = Math.min((amount - maxLiquid / 1.5f) / 4f, 0.3f) * Time.delta(); + for(Point2 point : Geometry.d4){ + Tile other = world.tile(tile.x + point.x, tile.y + point.y); + if(other != null && other.block() == Blocks.air){ + deposit(other, tile, liquid, deposited, generation + 1); + amount -= deposited / 2f; //tweak to speed up/slow down puddle propagation + } + } + } + + amount = Mathf.clamp(amount, 0, maxLiquid); + + if(amount <= 0f){ + Call.onPuddleRemoved(getID()); + } + } + + //effects-only code + if(amount >= maxLiquid / 2f && updateTime <= 0f){ + Units.nearby(rect.setSize(Mathf.clamp(amount / (maxLiquid / 1.5f)) * 10f).setCenter(x, y), unit -> { + if(unit.isFlying()) return; + + unit.hitbox(rect2); + if(!rect.overlaps(rect2)) return; + + unit.applyEffect(liquid.effect, 60 * 2); + + if(unit.velocity().len() > 0.1){ + Effects.effect(Fx.ripple, liquid.color, unit.x, unit.y); + } + }); + + if(liquid.temperature > 0.7f && (tile.link().entity != null) && Mathf.chance(0.3 * Time.delta())){ + Fire.create(tile); + } + + updateTime = 20f; + } + + updateTime -= Time.delta(); + } + + @Override + public void draw(){ + seeds = id; + boolean onLiquid = tile.floor().isLiquid; + float f = Mathf.clamp(amount / (maxLiquid / 1.5f)); + float smag = onLiquid ? 0.8f : 0f; + float sscl = 20f; + + Draw.color(tmp.set(liquid.color).shiftValue(-0.05f)); + Fill.circle(x + Mathf.sin(Time.time() + seeds * 532, sscl, smag), y + Mathf.sin(Time.time() + seeds * 53, sscl, smag), f * 8f); + Angles.randLenVectors(id, 3, f * 6f, (ex, ey) -> { + Fill.circle(x + ex + Mathf.sin(Time.time() + seeds * 532, sscl, smag), + y + ey + Mathf.sin(Time.time() + seeds * 53, sscl, smag), f * 5f); + seeds++; + }); + Draw.color(); + } + + @Override + public float drawSize(){ + return 20; + } + + @Override + public void writeSave(DataOutput stream) throws IOException{ + stream.writeInt(tile.pos()); + stream.writeFloat(x); + stream.writeFloat(y); + stream.writeByte(liquid.id); + stream.writeFloat(amount); + stream.writeByte(generation); + } + + @Override + public void readSave(DataInput stream, byte version) throws IOException{ + this.loadedPosition = stream.readInt(); + this.x = stream.readFloat(); + this.y = stream.readFloat(); + this.liquid = content.liquid(stream.readByte()); + this.amount = stream.readFloat(); + this.generation = stream.readByte(); + add(); + } + + @Override + public void reset(){ + loadedPosition = -1; + tile = null; + liquid = null; + amount = 0; + generation = 0; + accepting = 0; + } + + @Override + public void added(){ + if(loadedPosition != -1){ + map.put(loadedPosition, this); + tile = world.tile(loadedPosition); + } + } + + @Override + public void removed(){ + map.remove(tile.pos()); + reset(); + } + + @Override + public void write(DataOutput data) throws IOException{ + data.writeFloat(x); + data.writeFloat(y); + data.writeByte(liquid.id); + data.writeShort((short)(amount * 4)); + data.writeInt(tile.pos()); + } + + @Override + public void read(DataInput data) throws IOException{ + x = data.readFloat(); + y = data.readFloat(); + liquid = content.liquid(data.readByte()); + targetAmount = data.readShort() / 4f; + int pos = data.readInt(); + tile = world.tile(pos); + + map.put(pos, this); + } + + @Override + public EntityGroup targetGroup(){ + return puddleGroup; + } +} diff --git a/core/src/io/anuke/mindustry/entities/effect/RubbleDecal.java b/core/src/io/anuke/mindustry/entities/effect/RubbleDecal.java new file mode 100644 index 0000000000..7944714f6b --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/effect/RubbleDecal.java @@ -0,0 +1,44 @@ +package io.anuke.mindustry.entities.effect; + +import io.anuke.arc.Core; +import io.anuke.arc.graphics.g2d.Draw; +import io.anuke.arc.graphics.g2d.TextureRegion; +import io.anuke.arc.math.Mathf; + +import static io.anuke.mindustry.Vars.headless; + +public class RubbleDecal extends Decal{ + private static final TextureRegion[][] regions = new TextureRegion[16][0]; + private TextureRegion region; + + /** Creates a rubble effect at a position. Provide a block size to use. */ + public static void create(float x, float y, int size){ + if(headless) return; + + if(regions[size].length == 0 || regions[size][0].getTexture().isDisposed()){ + regions[size] = new TextureRegion[2]; + for(int j = 0; j < 2; j++){ + regions[size][j] = Core.atlas.find("rubble-" + size + "-" + j); + } + } + + RubbleDecal decal = new RubbleDecal(); + decal.region = regions[size][Mathf.clamp(Mathf.randomSeed(decal.id, 0, 1), 0, regions[size].length - 1)]; + + if(!Core.atlas.isFound(decal.region)){ + return; + } + + decal.set(x, y); + decal.add(); + } + + @Override + public void drawDecal(){ + if(!Core.atlas.isFound(region)){ + remove(); + return; + } + Draw.rect(region, x, y, Mathf.randomSeed(id, 0, 4) * 90); + } +} diff --git a/core/src/io/anuke/mindustry/entities/effect/ScorchDecal.java b/core/src/io/anuke/mindustry/entities/effect/ScorchDecal.java new file mode 100644 index 0000000000..c5828f3078 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/effect/ScorchDecal.java @@ -0,0 +1,49 @@ +package io.anuke.mindustry.entities.effect; + +import io.anuke.arc.Core; +import io.anuke.arc.graphics.g2d.Draw; +import io.anuke.arc.graphics.g2d.TextureRegion; +import io.anuke.arc.math.Angles; +import io.anuke.arc.math.Mathf; +import io.anuke.mindustry.world.Tile; + +import static io.anuke.mindustry.Vars.headless; +import static io.anuke.mindustry.Vars.world; + +public class ScorchDecal extends Decal{ + private static final int scorches = 5; + private static final TextureRegion[] regions = new TextureRegion[scorches]; + + public static void create(float x, float y){ + if(headless) return; + + if(regions[0] == null || regions[0].getTexture().isDisposed()){ + for(int i = 0; i < regions.length; i++){ + regions[i] = Core.atlas.find("scorch" + (i + 1)); + } + } + + Tile tile = world.tileWorld(x, y); + + if(tile == null || tile.floor().liquidDrop != null) return; + + ScorchDecal decal = new ScorchDecal(); + decal.set(x, y); + decal.add(); + } + + @Override + public void drawDecal(){ + for(int i = 0; i < 5; i++){ + TextureRegion region = regions[Mathf.randomSeed(id - i, 0, scorches - 1)]; + float rotation = Mathf.randomSeed(id + i, 0, 360); + float space = 1.5f + Mathf.randomSeed(id + i + 1, 0, 20) / 10f; + Draw.rect(region, + x + Angles.trnsx(rotation, space), + y + Angles.trnsy(rotation, space) + region.getHeight() / 2f * Draw.scl, + region.getWidth() * Draw.scl, + region.getHeight() * Draw.scl, + region.getWidth() / 2f * Draw.scl, 0, rotation - 90); + } + } +} diff --git a/core/src/io/anuke/mindustry/entities/effect/Shield.java b/core/src/io/anuke/mindustry/entities/effect/Shield.java deleted file mode 100644 index 405389e7e6..0000000000 --- a/core/src/io/anuke/mindustry/entities/effect/Shield.java +++ /dev/null @@ -1,103 +0,0 @@ -package io.anuke.mindustry.entities.effect; - -import com.badlogic.gdx.math.Interpolation; -import io.anuke.mindustry.entities.enemies.Enemy; -import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.blocks.types.defense.ShieldBlock; -import io.anuke.ucore.core.Timers; -import io.anuke.ucore.entities.BulletEntity; -import io.anuke.ucore.entities.Entities; -import io.anuke.ucore.entities.Entity; -import io.anuke.ucore.graphics.Draw; -import io.anuke.ucore.util.Mathf; - -import static io.anuke.mindustry.Vars.bulletGroup; -import static io.anuke.mindustry.Vars.shieldGroup; - -public class Shield extends Entity{ - public boolean active; - public boolean hitPlayers = false; - public float radius = 0f; - - private float uptime = 0f; - private final Tile tile; - - public Shield(Tile tile){ - this.tile = tile; - this.x = tile.worldx(); - this.y = tile.worldy(); - } - - public float drawSize(){ - return 150; - } - - @Override - public void update(){ - float alpha = 0.1f; - Interpolation interp = Interpolation.fade; - - if(active){ - uptime = interp.apply(uptime, 1f, alpha * Timers.delta()); - }else{ - uptime = interp.apply(uptime, 0f, alpha * Timers.delta()); - if(uptime <= 0.05f) - remove(); - } - uptime = Mathf.clamp(uptime); - - if(!(tile.block() instanceof ShieldBlock)){ - remove(); - return; - } - - ShieldBlock block = (ShieldBlock)tile.block(); - - Entities.getNearby(bulletGroup, x, y, block.shieldRadius * 2*uptime + 10, entity->{ - BulletEntity bullet = (BulletEntity)entity; - if((bullet.owner instanceof Enemy || hitPlayers)){ - - float dst = entity.distanceTo(this); - - if(dst < drawRadius()/2f){ - ((ShieldBlock)tile.block()).handleBullet(tile, bullet); - } - } - }); - } - - @Override - public void draw(){ - if(!(tile.block() instanceof ShieldBlock) || radius <= 1f){ - return; - } - - float rad = drawRadius(); - Draw.rect("circle2", x, y, rad, rad); - } - - float drawRadius(){ - return (radius*2 + Mathf.sin(Timers.time(), 25f, 2f)); - } - - public void removeDelay(){ - active = false; - } - - @Override - public Shield add(){ - return super.add(shieldGroup); - } - - @Override - public void added(){ - active = true; - } - - @Override - public void removed(){ - active = false; - uptime = 0f; - } - -} diff --git a/core/src/io/anuke/mindustry/entities/effect/TeslaOrb.java b/core/src/io/anuke/mindustry/entities/effect/TeslaOrb.java deleted file mode 100644 index 40b860a01c..0000000000 --- a/core/src/io/anuke/mindustry/entities/effect/TeslaOrb.java +++ /dev/null @@ -1,135 +0,0 @@ -package io.anuke.mindustry.entities.effect; - -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.math.Vector2; -import com.badlogic.gdx.utils.Array; -import com.badlogic.gdx.utils.ObjectSet; -import io.anuke.mindustry.entities.enemies.Enemy; -import io.anuke.mindustry.graphics.Fx; -import io.anuke.ucore.core.Effects; -import io.anuke.ucore.core.Timers; -import io.anuke.ucore.entities.Entities; -import io.anuke.ucore.entities.Entity; -import io.anuke.ucore.entities.SolidEntity; -import io.anuke.ucore.graphics.Draw; -import io.anuke.ucore.graphics.Lines; -import io.anuke.ucore.util.Mathf; - -import static io.anuke.mindustry.Vars.enemyGroup; - -public class TeslaOrb extends Entity{ - private Array points = new Array<>(); - private ObjectSet hit = new ObjectSet<>(); - private int damage = 0; - private float range = 0; - private float lifetime = 30f; - private float life = 0f; - private Vector2 vector = new Vector2(); - - public TeslaOrb(float x, float y, float range, int damage){ - set(x, y); - this.damage = damage; - this.range = range; - } - - void shock(){ - float stopchance = 0.1f; - float curx = x, cury = y; - float shake = 3f; - - int max = 7; - - while(points.size < max){ - if(Mathf.chance(stopchance)){ - break; - } - - Array enemies = Entities.getNearby(enemyGroup, curx, cury, range*2f); - - synchronized (Entities.entityLock) { - - for (SolidEntity entity : enemies) { - if (entity != null && entity.distanceTo(curx, cury) < range && !hit.contains((Enemy) entity)) { - hit.add((Enemy) entity); - points.add(new Vector2(entity.x + Mathf.range(shake), entity.y + Mathf.range(shake))); - damageEnemy((Enemy) entity); - curx = entity.x; - cury = entity.y; - break; - } - } - } - } - - if(points.size == 0){ - remove(); - } - } - - void damageEnemy(Enemy enemy){ - enemy.damage(damage); - Effects.effect(Fx.laserhit, enemy.x + Mathf.range(2f), enemy.y + Mathf.range(2f)); - } - - @Override - public void update(){ - life += Timers.delta(); - - if(life >= lifetime){ - remove(); - } - } - - @Override - public void drawOver(){ - if(points.size == 0) return; - - float range = 1f; - - Vector2 previous = vector.set(x, y); - - for(Vector2 enemy : points){ - - - float x1 = previous.x + Mathf.range(range), - y1 = previous.y + Mathf.range(range), - x2 = enemy.x + Mathf.range(range), - y2 = enemy.y + Mathf.range(range); - - Draw.color(Color.WHITE); - Draw.alpha(1f-life/lifetime); - - Lines.stroke(3f - life/lifetime*2f); - Lines.line(x1, y1, x2, y2); - - float rad = 7f - life/lifetime*5f; - - Draw.rect("circle", x2, y2, rad, rad); - - if(previous.epsilonEquals(x, y, 0.001f)){ - Draw.rect("circle", x, y, rad, rad); - } - - //Draw.color(Color.WHITE); - - //Draw.stroke(2f - life/lifetime*2f); - //Draw.line(x1, y1, x2, y2); - - Draw.reset(); - - previous = enemy; - } - } - - @Override - public void added(){ - Timers.run(1f, ()->{ - shock(); - }); - } - - @Override - public float drawSize(){ - return 200; - } -} diff --git a/core/src/io/anuke/mindustry/entities/enemies/Enemy.java b/core/src/io/anuke/mindustry/entities/enemies/Enemy.java deleted file mode 100644 index da56d84fde..0000000000 --- a/core/src/io/anuke/mindustry/entities/enemies/Enemy.java +++ /dev/null @@ -1,164 +0,0 @@ -package io.anuke.mindustry.entities.enemies; - -import com.badlogic.gdx.graphics.g2d.TextureRegion; -import com.badlogic.gdx.math.Vector2; -import io.anuke.mindustry.entities.Bullet; -import io.anuke.mindustry.entities.BulletType; -import io.anuke.mindustry.entities.SyncEntity; -import io.anuke.mindustry.net.Net; -import io.anuke.mindustry.net.NetEvents; -import io.anuke.ucore.entities.Entity; -import io.anuke.ucore.entities.SolidEntity; -import io.anuke.ucore.graphics.Draw; -import io.anuke.ucore.util.Mathf; -import io.anuke.ucore.util.Timer; -import io.anuke.ucore.util.Translator; - -import java.nio.ByteBuffer; - -import static io.anuke.mindustry.Vars.enemyGroup; - -public class Enemy extends SyncEntity { - public EnemyType type; - - public Timer timer = new Timer(5); - public float idletime = 0f; - public int lane; - public int node = -1; - - public Enemy spawner; - public int spawned; - - public Vector2 velocity = new Vector2(); - public Vector2 totalMove = new Vector2(); - public Vector2 tpos = new Vector2(-999, -999); - public Entity target; - public float hitTime; - public int tier = 1; - - public TextureRegion region; - public Translator tr = new Translator(); - - public Enemy(EnemyType type){ - this.type = type; - } - - /**internal constructor used for deserialization, DO NOT USE*/ - public Enemy(){} - - @Override - public void update(){ - type.update(this); - } - - @Override - public void drawSmooth(){ - type.draw(this); - } - - @Override - public void drawOver(){ - type.drawOver(this); - } - - @Override - public float drawSize(){ - return 14; - } - - @Override - public boolean collides(SolidEntity other){ - return (other instanceof Bullet) && !(((Bullet) other).owner instanceof Enemy); - } - - @Override - public void damage(float amount){ - super.damage(amount); - hitTime = EnemyType.hitDuration; - } - - @Override - public void onDeath(){ - type.onDeath(this, false); - } - - @Override - public void removed(){ - type.removed(this); - } - - @Override - public void added(){ - hitbox.setSize(type.hitsize); - hitboxTile.setSize(type.hitsizeTile); - maxhealth = type.health * tier; - region = Draw.region(type.name + "-t" + Mathf.clamp(tier, 1, 3)); - - heal(); - } - - @Override - public Enemy add(){ - return add(enemyGroup); - } - - @Override - public void writeSpawn(ByteBuffer buffer) { - buffer.put(type.id); - buffer.put((byte)lane); - buffer.put((byte)tier); - buffer.putFloat(x); - buffer.putFloat(y); - buffer.putShort((short)health); - } - - @Override - public void readSpawn(ByteBuffer buffer) { - type = EnemyType.getByID(buffer.get()); - lane = buffer.get(); - tier = buffer.get(); - x = buffer.getFloat(); - y = buffer.getFloat(); - health = buffer.getShort(); - setNet(x, y); - } - - @Override - public void write(ByteBuffer data) { - data.putFloat(x); - data.putFloat(y); - data.putShort((short)(angle*2)); - data.putShort((short)health); - } - - @Override - public void read(ByteBuffer data, long time) { - - float x = data.getFloat(); - float y = data.getFloat(); - short angle = data.getShort(); - short health = data.getShort(); - - this.health = health; - - interpolator.read(this.x, this.y, x, y, angle/2f, time); - } - - public void shoot(BulletType bullet){ - shoot(bullet, 0); - } - - public void shoot(BulletType bullet, float rotation){ - - if(!(Net.client())) { - tr.trns(angle + rotation, type.length); - Bullet out = new Bullet(bullet, this, x + tr.x, y + tr.y, this.angle + rotation).add(); - out.damage = (int) ((bullet.damage * (1 + (tier - 1) * 1f))); - type.onShoot(this, bullet, rotation); - - if(Net.server()){ - NetEvents.handleBullet(bullet, this, x + tr.x, y + tr.y, this.angle + rotation, (short)out.damage); - } - } - } -} diff --git a/core/src/io/anuke/mindustry/entities/enemies/EnemyType.java b/core/src/io/anuke/mindustry/entities/enemies/EnemyType.java deleted file mode 100644 index b5603fd8a9..0000000000 --- a/core/src/io/anuke/mindustry/entities/enemies/EnemyType.java +++ /dev/null @@ -1,289 +0,0 @@ -package io.anuke.mindustry.entities.enemies; - -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.math.Vector2; -import com.badlogic.gdx.utils.Array; -import io.anuke.mindustry.entities.BulletType; -import io.anuke.mindustry.entities.Player; -import io.anuke.mindustry.entities.TileEntity; -import io.anuke.mindustry.graphics.Fx; -import io.anuke.mindustry.graphics.Shaders; -import io.anuke.mindustry.net.Net; -import io.anuke.mindustry.net.NetEvents; -import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.blocks.Blocks; -import io.anuke.ucore.core.Effects; -import io.anuke.ucore.core.Graphics; -import io.anuke.ucore.core.Timers; -import io.anuke.ucore.entities.Entities; -import io.anuke.ucore.graphics.Draw; -import io.anuke.ucore.graphics.Lines; -import io.anuke.ucore.util.Mathf; -import io.anuke.ucore.util.Strings; - -import static io.anuke.mindustry.Vars.*; - -public class EnemyType { - - //TODO documentation, comments - private static byte lastid = 0; - private static Array types = new Array<>(); - - public final static Color[] tierColors = { - Color.valueOf("ffe451"), Color.valueOf("f48e20"), Color.valueOf("ff6757"), - Color.valueOf("ff2d86"), Color.valueOf("cb2dff"), Color.valueOf("362020") }; - public final static int maxtier = tierColors.length; - public final static float maxIdleLife = 60f*2f; //2 seconds idle = death - public final static float hitDuration = 5f; - - public final String name; - public final byte id; - - protected int timeid; - protected int health = 60; - protected float hitsize = 5f; - protected float hitsizeTile = 4f; - protected float speed = 0.4f; - protected float reload = 32; - protected float range = 60; - protected float length = 4; - protected float rotatespeed = 0.1f; - protected float turretrotatespeed = 0.2f; - protected boolean alwaysRotate = false; - protected BulletType bullet = BulletType.small; - protected String shootsound = "enemyshoot"; - protected boolean targetCore = false; - protected boolean stopNearCore = true; - protected boolean targetClient = false; - protected float mass = 1f; - - protected final int timerTarget = timeid ++; - protected final int timerReload = timeid ++; - protected final int timerReset = timeid ++; - - protected final Vector2 shift = new Vector2(); - protected final Vector2 move = new Vector2(); - protected final Vector2 calc = new Vector2(); - - public EnemyType(String name){ - this.id = lastid++; - this.name = name; - types.add(this); - } - - public void draw(Enemy enemy){ - Shaders.outline.color.set(tierColors[enemy.tier - 1]); - Shaders.outline.lighten = Mathf.clamp(enemy.hitTime/hitDuration); - Shaders.outline.region = enemy.region; - - Shaders.outline.apply(); - - Draw.rect(enemy.region, enemy.x, enemy.y, enemy.angle - 90); - Draw.color(); - - Graphics.flush(); - - if(isCalculating(enemy)){ - Draw.color(Color.SKY); - Lines.polySeg(20, 0, 4, enemy.x, enemy.y, 11f, Timers.time() * 2f + enemy.id*52f); - Lines.polySeg(20, 0, 4, enemy.x, enemy.y, 11f, Timers.time() * 2f + enemy.id*52f + 180f); - Draw.color(); - } - - if(showPaths){ - Draw.tscl(0.25f); - Draw.text((int)enemy.idletime + " " + enemy.node + " " + enemy.id + "\n" + Strings.toFixed(enemy.totalMove.x, 2) + ", " - + Strings.toFixed(enemy.totalMove.x, 2), enemy.x, enemy.y); - Draw.tscl(fontscale); - } - - Shaders.outline.lighten = 0f; - } - - public void drawOver(Enemy enemy){ } - - public void update(Enemy enemy){ - float lastx = enemy.x, lasty = enemy.y; - if(enemy.hitTime > 0){ - enemy.hitTime -= Timers.delta(); - } - - if(enemy.lane >= world.getSpawns().size || enemy.lane < 0) enemy.lane = 0; - - boolean waiting = enemy.lane >= world.getSpawns().size || enemy.lane < 0 - || world.getSpawns().get(enemy.lane).pathTiles == null || enemy.node <= 0; - - move(enemy); - - enemy.velocity.set(enemy.x - lastx, enemy.y - lasty).scl(1f / Timers.delta()); - enemy.totalMove.add(enemy.velocity); - - float minv = 0.07f; - - if(enemy.timer.get(timerReset, 80)){ - enemy.totalMove.setZero(); - } - - if(enemy.velocity.len() < minv && !waiting && enemy.target == null){ - enemy.idletime += Timers.delta(); - }else{ - enemy.idletime = 0; - } - - if(enemy.timer.getTime(timerReset) > 50 && enemy.totalMove.len() < 0.2f && !waiting && enemy.target == null){ - enemy.idletime = 999999f; - } - - Tile tile = world.tileWorld(enemy.x, enemy.y); - if(tile != null && tile.floor().liquid && tile.block() == Blocks.air){ - enemy.damage(enemy.health+1); //drown - } - - if(Float.isNaN(enemy.angle)){ - enemy.angle = 0; - } - - if(enemy.target == null || alwaysRotate){ - enemy.angle = Mathf.slerpDelta(enemy.angle, enemy.velocity.angle(), rotatespeed); - }else{ - enemy.angle = Mathf.slerpDelta(enemy.angle, enemy.angleTo(enemy.target), turretrotatespeed); - } - - enemy.x = Mathf.clamp(enemy.x, 0, world.width() * tilesize); - enemy.y = Mathf.clamp(enemy.y, 0, world.height() * tilesize); - } - - public void move(Enemy enemy){ - if(Net.client()){ - enemy.interpolate(); - if(targetClient) updateTargeting(enemy, false); - return; - } - - float speed = this.speed + 0.04f * enemy.tier; - float range = this.range + enemy.tier * 5; - - Tile core = world.getCore(); - - if(core == null) return; - - if(enemy.idletime > maxIdleLife && enemy.node > 0){ - enemy.onDeath(); - return; - } - - boolean nearCore = enemy.distanceTo(core.worldx(), core.worldy()) <= range - 18f && stopNearCore; - Vector2 vec; - - if(nearCore){ - vec = move.setZero(); - if(targetCore) enemy.target = core.entity; - }else{ - vec = world.pathfinder().find(enemy); - vec.sub(enemy.x, enemy.y).limit(speed); - } - - shift.setZero(); - float shiftRange = enemy.hitbox.width + 2f; - float avoidRange = shiftRange + 4f; - float attractRange = avoidRange + 7f; - float avoidSpeed = this.speed/2.7f; - - Entities.getNearby(enemyGroup, enemy.x, enemy.y, range, en -> { - Enemy other = (Enemy)en; - if(other == enemy) return; - float dst = other.distanceTo(enemy); - - if(dst < shiftRange){ - float scl = Mathf.clamp(1.4f - dst / shiftRange) * mass * 1f/mass; - shift.add((enemy.x - other.x) * scl, (enemy.y - other.y) * scl); - }else if(dst < avoidRange){ - calc.set((enemy.x - other.x), (enemy.y - other.y)).setLength(avoidSpeed); - shift.add(calc.scl(1.1f)); - }else if(dst < attractRange && !nearCore && !isCalculating(enemy)){ - calc.set((enemy.x - other.x), (enemy.y - other.y)).setLength(avoidSpeed); - shift.add(calc.scl(-1)); - } - }); - - shift.limit(1f); - vec.add(shift.scl(0.5f)); - - enemy.move(vec.x * Timers.delta(), vec.y * Timers.delta()); - - updateTargeting(enemy, nearCore); - - behavior(enemy); - } - - public void behavior(Enemy enemy){} - - public void updateTargeting(Enemy enemy, boolean nearCore){ - if(enemy.target != null && enemy.target instanceof TileEntity && ((TileEntity)enemy.target).dead){ - enemy.target = null; - } - - if(enemy.timer.get(timerTarget, 15) && !nearCore){ - enemy.target = world.findTileTarget(enemy.x, enemy.y, null, range, false); - - //no tile found - if(enemy.target == null){ - enemy.target = Entities.getClosest(playerGroup, enemy.x, enemy.y, range, e -> !((Player)e).isAndroid && - !((Player)e).isDead()); - } - }else if(nearCore){ - enemy.target = world.getCore().entity; - } - - if(enemy.target != null && bullet != null){ - updateShooting(enemy); - } - } - - public void updateShooting(Enemy enemy){ - float reload = this.reload / Math.max(enemy.tier / 1.5f, 1f); - - if(enemy.timer.get(timerReload, reload)){ - shoot(enemy); - } - } - - public void shoot(Enemy enemy){ - enemy.shoot(bullet); - if(shootsound != null) Effects.sound(shootsound, enemy); - } - - public void onShoot(Enemy enemy, BulletType type, float rotation){} - - public void onDeath(Enemy enemy, boolean force){ - if(Net.server()){ - NetEvents.handleEnemyDeath(enemy); - } - - if(!Net.client() || force) { - Effects.effect(Fx.explosion, enemy); - Effects.shake(3f, 4f, enemy); - Effects.sound("bang2", enemy); - enemy.remove(); - enemy.dead = true; - } - } - - public void removed(Enemy enemy){ - if(!enemy.dead){ - if(enemy.spawner != null){ - enemy.spawner.spawned --; - }else{ - state.enemies --; - } - } - } - - public boolean isCalculating(Enemy enemy){ - return enemy.node < 0 && !Net.client(); - } - - public static EnemyType getByID(byte id){ - return types.get(id); - } -} diff --git a/core/src/io/anuke/mindustry/entities/enemies/EnemyTypes.java b/core/src/io/anuke/mindustry/entities/enemies/EnemyTypes.java deleted file mode 100644 index 799b8fb1be..0000000000 --- a/core/src/io/anuke/mindustry/entities/enemies/EnemyTypes.java +++ /dev/null @@ -1,32 +0,0 @@ -package io.anuke.mindustry.entities.enemies; - -import io.anuke.mindustry.entities.enemies.types.BlastType; -import io.anuke.mindustry.entities.enemies.types.EmpType; -import io.anuke.mindustry.entities.enemies.types.FastType; -import io.anuke.mindustry.entities.enemies.types.FlamerType; -import io.anuke.mindustry.entities.enemies.types.FortressType; -import io.anuke.mindustry.entities.enemies.types.HealerType; -import io.anuke.mindustry.entities.enemies.types.MortarType; -import io.anuke.mindustry.entities.enemies.types.RapidType; -import io.anuke.mindustry.entities.enemies.types.*; -import io.anuke.mindustry.entities.enemies.types.TankType; -import io.anuke.mindustry.entities.enemies.types.TargetType; -import io.anuke.mindustry.entities.enemies.types.TitanType; - -public class EnemyTypes { - public static final EnemyType - - standard = new StandardType(), - fast = new FastType(), - rapid = new RapidType(), - flamer = new FlamerType(), - tank = new TankType(), - blast = new BlastType(), - mortar = new MortarType(), - healer = new HealerType(), - titan = new TitanType(), - emp = new EmpType(), - fortress = new FortressType(), - target = new TargetType(); - -} diff --git a/core/src/io/anuke/mindustry/entities/enemies/types/BlastType.java b/core/src/io/anuke/mindustry/entities/enemies/types/BlastType.java deleted file mode 100644 index 5ce1e804ed..0000000000 --- a/core/src/io/anuke/mindustry/entities/enemies/types/BlastType.java +++ /dev/null @@ -1,54 +0,0 @@ -package io.anuke.mindustry.entities.enemies.types; - -import io.anuke.mindustry.entities.Bullet; -import io.anuke.mindustry.entities.BulletType; -import io.anuke.mindustry.entities.TileEntity; -import io.anuke.mindustry.entities.enemies.Enemy; -import io.anuke.mindustry.entities.enemies.EnemyType; - -import static io.anuke.mindustry.Vars.tilesize; - -public class BlastType extends EnemyType { - - public BlastType() { - super("blastenemy"); - health = 30; - speed = 0.8f; - bullet = null; - turretrotatespeed = 0f; - mass = 0.8f; - stopNearCore = false; - } - - @Override - public void behavior(Enemy enemy){ - - float range = 10f; - float ox = 0, oy = 0; - - if(enemy.target instanceof TileEntity){ - TileEntity e = (TileEntity)enemy.target; - range = (e.tile.block().width * tilesize) /2f + 8f; - ox = e.tile.block().getPlaceOffset().x; - oy = e.tile.block().getPlaceOffset().y; - } - - if(enemy.target != null && enemy.target.distanceTo(enemy.x - ox, enemy.y - oy) < range){ - explode(enemy); - } - } - - @Override - public void onDeath(Enemy enemy, boolean force){ - if(force) explode(enemy); - super.onDeath(enemy, force); - } - - void explode(Enemy enemy){ - Bullet b = new Bullet(BulletType.blast, enemy, enemy.x, enemy.y, 0).add(); - b.damage = BulletType.blast.damage + (enemy.tier-1) * 40; - enemy.damage(999); - enemy.remove(); - } - -} diff --git a/core/src/io/anuke/mindustry/entities/enemies/types/EmpType.java b/core/src/io/anuke/mindustry/entities/enemies/types/EmpType.java deleted file mode 100644 index cfe1a62da2..0000000000 --- a/core/src/io/anuke/mindustry/entities/enemies/types/EmpType.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.anuke.mindustry.entities.enemies.types; - -import io.anuke.mindustry.entities.BulletType; -import io.anuke.mindustry.entities.enemies.EnemyType; - -public class EmpType extends EnemyType { - - public EmpType() { - super("empenemy"); - - speed = 0.3f; - reload = 70; - health = 210; - range = 80f; - bullet = BulletType.emp; - turretrotatespeed = 0.1f; - } - -} diff --git a/core/src/io/anuke/mindustry/entities/enemies/types/FastType.java b/core/src/io/anuke/mindustry/entities/enemies/types/FastType.java deleted file mode 100644 index 29c7ed5a0c..0000000000 --- a/core/src/io/anuke/mindustry/entities/enemies/types/FastType.java +++ /dev/null @@ -1,17 +0,0 @@ -package io.anuke.mindustry.entities.enemies.types; - -import io.anuke.mindustry.entities.enemies.EnemyType; - -public class FastType extends EnemyType { - - public FastType() { - super("fastenemy"); - - speed = 0.73f; - reload = 20; - mass = 0.2f; - - health = 50; - } - -} diff --git a/core/src/io/anuke/mindustry/entities/enemies/types/FlamerType.java b/core/src/io/anuke/mindustry/entities/enemies/types/FlamerType.java deleted file mode 100644 index 0aad1e7b10..0000000000 --- a/core/src/io/anuke/mindustry/entities/enemies/types/FlamerType.java +++ /dev/null @@ -1,20 +0,0 @@ -package io.anuke.mindustry.entities.enemies.types; - -import io.anuke.mindustry.entities.BulletType; -import io.anuke.mindustry.entities.enemies.EnemyType; - -public class FlamerType extends EnemyType { - - public FlamerType() { - super("flamerenemy"); - - speed = 0.35f; - health = 150; - reload = 6; - bullet = BulletType.flameshot; - shootsound = "flame"; - mass = 1.5f; - range = 40; - } - -} diff --git a/core/src/io/anuke/mindustry/entities/enemies/types/FortressType.java b/core/src/io/anuke/mindustry/entities/enemies/types/FortressType.java deleted file mode 100644 index d02c85fe7c..0000000000 --- a/core/src/io/anuke/mindustry/entities/enemies/types/FortressType.java +++ /dev/null @@ -1,60 +0,0 @@ -package io.anuke.mindustry.entities.enemies.types; - -import io.anuke.mindustry.entities.BulletType; -import io.anuke.mindustry.entities.enemies.Enemy; -import io.anuke.mindustry.entities.enemies.EnemyType; -import io.anuke.mindustry.entities.enemies.EnemyTypes; -import io.anuke.mindustry.graphics.Fx; -import io.anuke.ucore.core.Effects; -import io.anuke.ucore.core.Timers; -import io.anuke.ucore.util.Angles; - -import static io.anuke.mindustry.Vars.world; - -public class FortressType extends EnemyType { - final int maxSpawn = 6; - final float spawnTime = 190; - - public FortressType() { - super("fortressenemy"); - - speed = 0.25f; - reload = 90; - health = 700; - range = 70f; - bullet = BulletType.yellowshell; - hitsize = 10f; - turretrotatespeed = rotatespeed = 0.08f; - length = 7f; - mass = 7f; - } - - @Override - public void behavior(Enemy enemy){ - if(enemy.distanceTo(world.getCore().worldx(), - world.getCore().worldy()) <= 90f){ - - if(Timers.get(this, "spawn", spawnTime) && enemy.spawned < maxSpawn){ - enemy.tr.trns(enemy.angle, 20f); - - Enemy s = new Enemy(EnemyTypes.fast); - s.lane = enemy.lane; - s.tier = enemy.tier; - s.spawner = enemy; - s.set(enemy.x + enemy.tr.x, enemy.y + enemy.tr.y); - s.add(); - - Effects.effect(Fx.spawn, enemy); - enemy.spawned ++; - } - - } - } - - - public void onShoot(Enemy enemy, BulletType type, float rotation){ - Effects.effect(Fx.largeCannonShot, enemy.x + enemy.tr.x, enemy.y + enemy.tr.y, enemy.angle); - Effects.shake(3f, 3f, enemy); - } - -} diff --git a/core/src/io/anuke/mindustry/entities/enemies/types/HealerType.java b/core/src/io/anuke/mindustry/entities/enemies/types/HealerType.java deleted file mode 100644 index 938f33c27c..0000000000 --- a/core/src/io/anuke/mindustry/entities/enemies/types/HealerType.java +++ /dev/null @@ -1,99 +0,0 @@ -package io.anuke.mindustry.entities.enemies.types; - -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.math.MathUtils; -import io.anuke.mindustry.entities.Bullet; -import io.anuke.mindustry.entities.BulletType; -import io.anuke.mindustry.entities.enemies.Enemy; -import io.anuke.mindustry.entities.enemies.EnemyType; -import io.anuke.mindustry.graphics.Fx; -import io.anuke.mindustry.graphics.Shaders; -import io.anuke.ucore.core.Effects; -import io.anuke.ucore.core.Graphics; -import io.anuke.ucore.core.Timers; -import io.anuke.ucore.entities.Entities; -import io.anuke.ucore.graphics.Draw; -import io.anuke.ucore.graphics.Hue; -import io.anuke.ucore.graphics.Shapes; - -import static io.anuke.mindustry.Vars.enemyGroup; - -public class HealerType extends EnemyType { - - public HealerType() { - super("healerenemy"); - speed = 0.25f; - reload = 10; - health = 200; - bullet = BulletType.shot; - range = 40f; - alwaysRotate = false; - targetCore = false; - stopNearCore = true; - targetClient = true; - mass = 1.1f; - } - - - @Override - public void behavior(Enemy enemy){ - - if(enemy.idletime > 60f*3){ //explode after 3 seconds of stillness - explode(enemy); - Effects.effect(Fx.shellexplosion, enemy); - Effects.effect(Fx.shellsmoke, enemy); - } - } - - @Override - public void updateTargeting(Enemy enemy, boolean nearCore){ - if(enemy.timer.get(timerTarget, 15)){ - enemy.target = Entities.getClosest(enemyGroup, - enemy.x, enemy.y, range, e -> e instanceof Enemy && e != enemy && ((Enemy)e).healthfrac() < 1f); - } - - if(enemy.target != null){ - updateShooting(enemy); - } - } - - - @Override - public void updateShooting(Enemy enemy){ - Enemy target = (Enemy)enemy.target; - - if(target.health < target.maxhealth && enemy.timer.get(timerReload, reload)){ - target.health ++; - enemy.idletime = 0; - } - } - - @Override - public void drawOver(Enemy enemy){ - Enemy target = (Enemy)enemy.target; - - if(target == null) return; - - enemy.tr.trns(enemy.angleTo(target), 5f); - - Shaders.outline.color.set(Color.CLEAR); - Shaders.outline.apply(); - - if(target.health < target.maxhealth){ - Draw.color(Hue.rgb(138, 244, 138, (MathUtils.sin(Timers.time()) + 1f) / 13f)); - Draw.alpha(0.9f); - Shapes.laser("laser", "laserend", enemy.x + enemy.tr.x, enemy.y + enemy.tr.y, target.x - enemy.tr.x/1.5f, target.y - enemy.tr.y/1.5f); - Draw.color(); - } - - Graphics.flush(); - } - - void explode(Enemy enemy){ - Bullet b = new Bullet(BulletType.blast, enemy, enemy.x, enemy.y, 0).add(); - b.damage = BulletType.blast.damage + (enemy.tier-1) * 30; - enemy.damage(999); - enemy.remove(); - } - -} diff --git a/core/src/io/anuke/mindustry/entities/enemies/types/MortarType.java b/core/src/io/anuke/mindustry/entities/enemies/types/MortarType.java deleted file mode 100644 index ad1ac1bfbc..0000000000 --- a/core/src/io/anuke/mindustry/entities/enemies/types/MortarType.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.anuke.mindustry.entities.enemies.types; - -import io.anuke.mindustry.entities.BulletType; -import io.anuke.mindustry.entities.enemies.EnemyType; - -public class MortarType extends EnemyType { - - public MortarType() { - super("mortarenemy"); - - health = 200; - speed = 0.25f; - reload = 100f; - bullet = BulletType.shell; - turretrotatespeed = 0.15f; - rotatespeed = 0.05f; - range = 120f; - mass = 1.2f; - } - -} diff --git a/core/src/io/anuke/mindustry/entities/enemies/types/RapidType.java b/core/src/io/anuke/mindustry/entities/enemies/types/RapidType.java deleted file mode 100644 index ed2621d74c..0000000000 --- a/core/src/io/anuke/mindustry/entities/enemies/types/RapidType.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.anuke.mindustry.entities.enemies.types; - -import io.anuke.mindustry.entities.BulletType; -import io.anuke.mindustry.entities.enemies.EnemyType; - -public class RapidType extends EnemyType { - - public RapidType() { - super("rapidenemy"); - - reload = 8; - bullet = BulletType.purple; - rotatespeed = 0.08f; - health = 260; - speed = 0.33f; - hitsize = 8f; - mass = 3f; - range = 70; - } - -} diff --git a/core/src/io/anuke/mindustry/entities/enemies/types/StandardType.java b/core/src/io/anuke/mindustry/entities/enemies/types/StandardType.java deleted file mode 100644 index 112155beeb..0000000000 --- a/core/src/io/anuke/mindustry/entities/enemies/types/StandardType.java +++ /dev/null @@ -1,10 +0,0 @@ -package io.anuke.mindustry.entities.enemies.types; - -import io.anuke.mindustry.entities.enemies.EnemyType; - -public class StandardType extends EnemyType { - - public StandardType(){ - super("standardenemy"); - } -} diff --git a/core/src/io/anuke/mindustry/entities/enemies/types/TankType.java b/core/src/io/anuke/mindustry/entities/enemies/types/TankType.java deleted file mode 100644 index 64a827c1b2..0000000000 --- a/core/src/io/anuke/mindustry/entities/enemies/types/TankType.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.anuke.mindustry.entities.enemies.types; - -import io.anuke.mindustry.entities.BulletType; -import io.anuke.mindustry.entities.enemies.Enemy; -import io.anuke.mindustry.entities.enemies.EnemyType; -import io.anuke.ucore.util.Angles; - -public class TankType extends EnemyType { - - public TankType() { - super("tankenemy"); - - health = 350; - speed = 0.24f; - reload = 90f; - rotatespeed = 0.06f; - bullet = BulletType.small; - length = 3f; - mass = 1.4f; - length = 8f; - } - - @Override - public void shoot(Enemy enemy){ - super.shoot(enemy); - - Angles.shotgun(3, 8f, enemy.angle, f -> enemy.shoot(bullet, f)); - } - -} diff --git a/core/src/io/anuke/mindustry/entities/enemies/types/TargetType.java b/core/src/io/anuke/mindustry/entities/enemies/types/TargetType.java deleted file mode 100644 index 822e9d5823..0000000000 --- a/core/src/io/anuke/mindustry/entities/enemies/types/TargetType.java +++ /dev/null @@ -1,64 +0,0 @@ -package io.anuke.mindustry.entities.enemies.types; - -import com.badlogic.gdx.graphics.Color; -import io.anuke.mindustry.entities.enemies.Enemy; -import io.anuke.mindustry.entities.enemies.EnemyType; -import io.anuke.mindustry.entities.enemies.EnemyTypes; -import io.anuke.ucore.core.Timers; -import io.anuke.ucore.graphics.Draw; -import io.anuke.ucore.graphics.Lines; -import io.anuke.ucore.util.Mathf; - -import static io.anuke.mindustry.Vars.control; - -public class TargetType extends EnemyType { - - public TargetType(){ - super("targetenemy"); - - speed = 0f; - health = 40; - shootsound = null; - } - - @Override - public void move(Enemy enemy){ - - } - - @Override - public void shoot(Enemy enemy){ - //do nothing - } - - @Override - public void removed(Enemy enemy){ - //don't call enemy death since this is only a target - } - - @Override - public void draw(Enemy enemy){ - super.draw(enemy); - - Draw.color(Color.YELLOW); - - if(control.tutorial().showTarget()){ - Lines.spikes(enemy.x, enemy.y, 11f + Mathf.sin(Timers.time(), 7f, 1f), 4f, 8, Timers.time()); - } - - Draw.color(); - } - - @Override - public void onDeath(Enemy enemy, boolean force){ - super.onDeath(enemy, force); - Timers.run(100f, ()->{ - new Enemy(EnemyTypes.target).set(enemy.x, enemy.y).add(); - }); - } - - @Override - public boolean isCalculating(Enemy enemy){ - return false; - } -} diff --git a/core/src/io/anuke/mindustry/entities/enemies/types/TitanType.java b/core/src/io/anuke/mindustry/entities/enemies/types/TitanType.java deleted file mode 100644 index c8e2786b94..0000000000 --- a/core/src/io/anuke/mindustry/entities/enemies/types/TitanType.java +++ /dev/null @@ -1,47 +0,0 @@ -package io.anuke.mindustry.entities.enemies.types; - -import io.anuke.mindustry.entities.BulletType; -import io.anuke.mindustry.entities.enemies.Enemy; -import io.anuke.mindustry.entities.enemies.EnemyType; -import io.anuke.ucore.core.Timers; -import io.anuke.ucore.util.Angles; -import io.anuke.ucore.util.Mathf; - -public class TitanType extends EnemyType { - - public TitanType() { - super("titanenemy"); - - speed = 0.26f; - reload = 30; - health = 430; - range = 60f; - bullet = BulletType.small; - hitsize = 7f; - mass = 4f; - } - - @Override - public void updateShooting(Enemy enemy){ - Timers.get(enemy, "salvo", 240); - - if(Timers.getTime(enemy, "salvo") < 60){ - if(Timers.get(enemy, "salvoShoot", 6)){ - enemy.shoot(BulletType.flameshot, Mathf.range(20f)); - } - } - - if(Timers.get(enemy, "shotgun", 80)){ - Angles.shotgun(5, 10f, 0f, f->{ - enemy.shoot(BulletType.smallSlow, f); - }); - } - - if(Timers.get(enemy, "circle", 200)){ - Angles.circle(8, f->{ - enemy.shoot(BulletType.smallSlow, f); - }); - } - } - -} diff --git a/core/src/io/anuke/mindustry/entities/impl/BaseEntity.java b/core/src/io/anuke/mindustry/entities/impl/BaseEntity.java new file mode 100755 index 0000000000..e2da48908e --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/impl/BaseEntity.java @@ -0,0 +1,66 @@ +package io.anuke.mindustry.entities.impl; + +import io.anuke.mindustry.entities.EntityGroup; +import io.anuke.mindustry.entities.traits.Entity; + +public abstract class BaseEntity implements Entity{ + private static int lastid; + /** Do not modify. Used for network operations and mapping. */ + public int id; + public float x, y; + protected transient EntityGroup group; + + public BaseEntity(){ + id = lastid++; + } + + @Override + public int getID(){ + return id; + } + + @Override + public void resetID(int id){ + this.id = id; + } + + @Override + public EntityGroup getGroup(){ + return group; + } + + @Override + public void setGroup(EntityGroup group){ + this.group = group; + } + + @Override + public float getX(){ + return x; + } + + @Override + public void setX(float x){ + this.x = x; + } + + @Override + public float getY(){ + return y; + } + + @Override + public void setY(float y){ + this.y = y; + } + + @Override + public String toString(){ + return getClass() + " " + id; + } + + /** Increments this entity's ID. Used for pooled entities.*/ + public void incrementID(){ + id = lastid++; + } +} diff --git a/core/src/io/anuke/mindustry/entities/impl/DestructibleEntity.java b/core/src/io/anuke/mindustry/entities/impl/DestructibleEntity.java new file mode 100644 index 0000000000..dce6f3dae5 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/impl/DestructibleEntity.java @@ -0,0 +1,43 @@ +package io.anuke.mindustry.entities.impl; + + +import io.anuke.mindustry.entities.traits.*; + +public abstract class DestructibleEntity extends SolidEntity implements HealthTrait{ + public transient boolean dead; + public float health; + + @Override + public boolean collides(SolidTrait other){ + return other instanceof DamageTrait; + } + + @Override + public void collision(SolidTrait other, float x, float y){ + if(other instanceof DamageTrait){ + onHit(other); + damage(((DamageTrait)other).damage()); + } + } + + @Override + public void health(float health){ + this.health = health; + } + + @Override + public float health(){ + return health; + } + + @Override + public boolean isDead(){ + return dead; + } + + @Override + public void setDead(boolean dead){ + this.dead = dead; + } + +} diff --git a/core/src/io/anuke/mindustry/entities/impl/EffectEntity.java b/core/src/io/anuke/mindustry/entities/impl/EffectEntity.java new file mode 100644 index 0000000000..789ce78dfa --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/impl/EffectEntity.java @@ -0,0 +1,81 @@ +package io.anuke.mindustry.entities.impl; + +import io.anuke.arc.graphics.Color; +import io.anuke.arc.util.pooling.Pool.Poolable; +import io.anuke.arc.util.pooling.Pools; +import io.anuke.mindustry.entities.Effects; +import io.anuke.mindustry.entities.Effects.Effect; +import io.anuke.mindustry.entities.EntityGroup; +import io.anuke.mindustry.entities.traits.DrawTrait; +import io.anuke.mindustry.entities.traits.Entity; + +import static io.anuke.mindustry.Vars.effectGroup; + +public class EffectEntity extends TimedEntity implements Poolable, DrawTrait{ + public Effect effect; + public Color color = new Color(Color.WHITE); + public Object data; + public float rotation = 0f; + + public Entity parent; + public float poffsetx, poffsety; + + /** For pooling use only! */ + public EffectEntity(){ + } + + public void setParent(Entity parent){ + this.parent = parent; + this.poffsetx = x - parent.getX(); + this.poffsety = y - parent.getY(); + } + + @Override + public EntityGroup targetGroup(){ + //this should never actually be called + return effectGroup; + } + + @Override + public float lifetime(){ + return effect.lifetime; + } + + @Override + public float drawSize(){ + return effect.size; + } + + @Override + public void update(){ + if(effect == null){ + remove(); + return; + } + + super.update(); + if(parent != null){ + x = parent.getX() + poffsetx; + y = parent.getY() + poffsety; + } + } + + @Override + public void reset(){ + effect = null; + color.set(Color.WHITE); + rotation = time = poffsetx = poffsety = 0f; + parent = null; + data = null; + } + + @Override + public void draw(){ + Effects.renderEffect(id, effect, color, time, rotation, x, y, data); + } + + @Override + public void removed(){ + Pools.free(this); + } +} diff --git a/core/src/io/anuke/mindustry/entities/impl/SolidEntity.java b/core/src/io/anuke/mindustry/entities/impl/SolidEntity.java new file mode 100644 index 0000000000..544abdbefa --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/impl/SolidEntity.java @@ -0,0 +1,19 @@ +package io.anuke.mindustry.entities.impl; + +import io.anuke.arc.math.geom.Vector2; +import io.anuke.mindustry.entities.traits.SolidTrait; + +public abstract class SolidEntity extends BaseEntity implements SolidTrait{ + protected transient Vector2 velocity = new Vector2(0f, 0.0001f); + private transient Vector2 lastPosition = new Vector2(); + + @Override + public Vector2 lastPosition(){ + return lastPosition; + } + + @Override + public Vector2 velocity(){ + return velocity; + } +} diff --git a/core/src/io/anuke/mindustry/entities/impl/TimedEntity.java b/core/src/io/anuke/mindustry/entities/impl/TimedEntity.java new file mode 100644 index 0000000000..e4007a5c4b --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/impl/TimedEntity.java @@ -0,0 +1,34 @@ +package io.anuke.mindustry.entities.impl; + +import io.anuke.arc.util.pooling.Pool.Poolable; +import io.anuke.mindustry.entities.traits.ScaleTrait; +import io.anuke.mindustry.entities.traits.TimeTrait; + +public abstract class TimedEntity extends BaseEntity implements ScaleTrait, TimeTrait, Poolable{ + public float time; + + @Override + public void time(float time){ + this.time = time; + } + + @Override + public float time(){ + return time; + } + + @Override + public void update(){ + updateTime(); + } + + @Override + public void reset(){ + time = 0f; + } + + @Override + public float fin(){ + return time() / lifetime(); + } +} diff --git a/core/src/io/anuke/mindustry/entities/traits/AbsorbTrait.java b/core/src/io/anuke/mindustry/entities/traits/AbsorbTrait.java new file mode 100644 index 0000000000..d408296c7c --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/traits/AbsorbTrait.java @@ -0,0 +1,13 @@ +package io.anuke.mindustry.entities.traits; + +public interface AbsorbTrait extends Entity, TeamTrait, DamageTrait{ + void absorb(); + + default boolean canBeAbsorbed(){ + return true; + } + + default float getShieldDamage(){ + return damage(); + } +} diff --git a/core/src/io/anuke/mindustry/entities/traits/BelowLiquidTrait.java b/core/src/io/anuke/mindustry/entities/traits/BelowLiquidTrait.java new file mode 100644 index 0000000000..c4abd8abf0 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/traits/BelowLiquidTrait.java @@ -0,0 +1,7 @@ +package io.anuke.mindustry.entities.traits; + +/** + * A flag interface for marking an effect as appearing below liquids. + */ +public interface BelowLiquidTrait{ +} diff --git a/core/src/io/anuke/mindustry/entities/traits/BuilderMinerTrait.java b/core/src/io/anuke/mindustry/entities/traits/BuilderMinerTrait.java new file mode 100644 index 0000000000..5fbde29251 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/traits/BuilderMinerTrait.java @@ -0,0 +1,22 @@ +package io.anuke.mindustry.entities.traits; + +/** A class for gracefully merging mining and building traits.*/ +public interface BuilderMinerTrait extends MinerTrait, BuilderTrait{ + + default void updateMechanics(){ + updateBuilding(); + + //mine only when not building + if(buildRequest() == null){ + updateMining(); + } + } + + default void drawMechanics(){ + if(isBuilding()){ + drawBuilding(); + }else{ + drawMining(); + } + } +} diff --git a/core/src/io/anuke/mindustry/entities/traits/BuilderTrait.java b/core/src/io/anuke/mindustry/entities/traits/BuilderTrait.java new file mode 100644 index 0000000000..fc32a530d4 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/traits/BuilderTrait.java @@ -0,0 +1,299 @@ +package io.anuke.mindustry.entities.traits; + +import io.anuke.arc.Core; +import io.anuke.arc.Events; +import io.anuke.arc.collection.Array; +import io.anuke.arc.collection.Queue; +import io.anuke.arc.graphics.g2d.*; +import io.anuke.arc.math.Angles; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.Vector2; +import io.anuke.arc.util.Time; +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.content.Blocks; +import io.anuke.mindustry.entities.type.TileEntity; +import io.anuke.mindustry.entities.type.Unit; +import io.anuke.mindustry.game.EventType.BuildSelectEvent; +import io.anuke.mindustry.gen.Call; +import io.anuke.mindustry.graphics.Pal; +import io.anuke.mindustry.net.Net; +import io.anuke.mindustry.world.*; +import io.anuke.mindustry.world.blocks.BuildBlock; +import io.anuke.mindustry.world.blocks.BuildBlock.BuildEntity; + +import java.io.*; +import java.util.Arrays; + +import static io.anuke.mindustry.Vars.*; +import static io.anuke.mindustry.entities.traits.BuilderTrait.BuildDataStatic.removal; +import static io.anuke.mindustry.entities.traits.BuilderTrait.BuildDataStatic.tmptr; + +/** Interface for units that build things.*/ +public interface BuilderTrait extends Entity, TeamTrait{ + //these are not instance variables! + float placeDistance = 220f; + float mineDistance = 70f; + + /** Updates building mechanism for this unit.*/ + default void updateBuilding(){ + float finalPlaceDst = state.rules.infiniteResources ? Float.MAX_VALUE : placeDistance; + Unit unit = (Unit)this; + //remove already completed build requests + removal.clear(); + for(BuildRequest req : buildQueue()){ + removal.add(req); + } + + buildQueue().clear(); + + for(BuildRequest request : removal){ + if(!((request.breaking && world.tile(request.x, request.y).block() == Blocks.air) || + (!request.breaking && (world.tile(request.x, request.y).rotation() == request.rotation || !request.block.rotate) + && world.tile(request.x, request.y).block() == request.block))){ + buildQueue().addLast(request); + } + } + + BuildRequest current = buildRequest(); + + if(current == null){ + return; + } + + Tile tile = world.tile(current.x, current.y); + + if(dst(tile) > finalPlaceDst){ + if(buildQueue().size > 1){ + buildQueue().removeFirst(); + buildQueue().addLast(current); + } + return; + } + + if(!(tile.block() instanceof BuildBlock)){ + if(!current.initialized && canCreateBlocks() && !current.breaking && Build.validPlace(getTeam(), current.x, current.y, current.block, current.rotation)){ + Call.beginPlace(getTeam(), current.x, current.y, current.block, current.rotation); + }else if(!current.initialized && canCreateBlocks() && current.breaking && Build.validBreak(getTeam(), current.x, current.y)){ + Call.beginBreak(getTeam(), current.x, current.y); + }else{ + buildQueue().removeFirst(); + return; + } + } + + TileEntity core = unit.getClosestCore(); + + if(tile.entity instanceof BuildEntity && !current.initialized){ + Core.app.post(() -> Events.fire(new BuildSelectEvent(tile, unit.getTeam(), this, current.breaking))); + current.initialized = true; + } + + //if there is no core to build with or no build entity, stop building! + if((core == null && !state.rules.infiniteResources) || !(tile.entity instanceof BuildEntity)){ + return; + } + + //otherwise, update it. + BuildEntity entity = tile.entity(); + + if(entity == null){ + return; + } + + if(unit.dst(tile) <= finalPlaceDst){ + unit.rotation = Mathf.slerpDelta(unit.rotation, unit.angleTo(entity), 0.4f); + } + + //progress is synced, thus not updated clientside + if(!Net.client()){ + //deconstructing is 2x as fast + if(current.breaking){ + entity.deconstruct(unit, core, 2f / entity.buildCost * Time.delta() * getBuildPower(tile) * state.rules.buildSpeedMultiplier); + }else{ + entity.construct(unit, core, 1f / entity.buildCost * Time.delta() * getBuildPower(tile) * state.rules.buildSpeedMultiplier); + } + + current.progress = entity.progress(); + }else{ + entity.progress = current.progress; + } + } + + /** Returns the queue for storing build requests. */ + Queue buildQueue(); + + /** Build power, can be any float. 1 = builds recipes in normal time, 0 = doesn't build at all. */ + float getBuildPower(Tile tile); + + /** Whether this type of builder can begin creating new blocks. */ + default boolean canCreateBlocks(){ + return true; + } + + default void writeBuilding(DataOutput output) throws IOException{ + BuildRequest request = buildRequest(); + + if(request != null){ + output.writeByte(request.breaking ? 1 : 0); + output.writeInt(Pos.get(request.x, request.y)); + output.writeFloat(request.progress); + if(!request.breaking){ + output.writeShort(request.block.id); + output.writeByte(request.rotation); + } + }else{ + output.writeByte(-1); + } + } + + default void readBuilding(DataInput input) throws IOException{ + readBuilding(input, true); + } + + default void readBuilding(DataInput input, boolean applyChanges) throws IOException{ + if(applyChanges) buildQueue().clear(); + + byte type = input.readByte(); + if(type != -1){ + int position = input.readInt(); + float progress = input.readFloat(); + BuildRequest request; + + if(type == 1){ //remove + request = new BuildRequest(Pos.x(position), Pos.y(position)); + }else{ //place + short block = input.readShort(); + byte rotation = input.readByte(); + request = new BuildRequest(Pos.x(position), Pos.y(position), rotation, content.block(block)); + } + + request.progress = progress; + + if(applyChanges){ + buildQueue().addLast(request); + }else if(isBuilding()){ + buildRequest().progress = progress; + } + } + } + + /** Return whether this builder's place queue contains items. */ + default boolean isBuilding(){ + return buildQueue().size != 0; + } + + /** Clears the placement queue. */ + default void clearBuilding(){ + buildQueue().clear(); + } + + /** Add another build requests to the tail of the queue, if it doesn't exist there yet. */ + default void addBuildRequest(BuildRequest place){ + for(BuildRequest request : buildQueue()){ + if(request.x == place.x && request.y == place.y){ + return; + } + } + Tile tile = world.tile(place.x, place.y); + if(tile != null && tile.entity instanceof BuildEntity){ + place.progress = tile.entity().progress; + } + buildQueue().addLast(place); + } + + /** + * Return the build requests currently active, or the one at the top of the queue. + * May return null. + */ + default BuildRequest buildRequest(){ + return buildQueue().size == 0 ? null : buildQueue().first(); + } + + //due to iOS weirdness, this is apparently required + class BuildDataStatic{ + static Array removal = new Array<>(); + static Vector2[] tmptr = new Vector2[]{new Vector2(), new Vector2(), new Vector2(), new Vector2()}; + } + + /** Draw placement effects for an entity. */ + default void drawBuilding(){ + if(!isBuilding()) return; + + Unit unit = (Unit)this; + BuildRequest request = buildRequest(); + Tile tile = world.tile(request.x, request.y); + + if(dst(tile) > placeDistance && !state.isEditor()){ + return; + } + + Lines.stroke(1f, Pal.accent); + float focusLen = 3.8f + Mathf.absin(Time.time(), 1.1f, 0.6f); + float px = unit.x + Angles.trnsx(unit.rotation, focusLen); + float py = unit.y + Angles.trnsy(unit.rotation, focusLen); + + float sz = Vars.tilesize * tile.block().size / 2f; + float ang = unit.angleTo(tile); + + tmptr[0].set(tile.drawx() - sz, tile.drawy() - sz); + tmptr[1].set(tile.drawx() + sz, tile.drawy() - sz); + tmptr[2].set(tile.drawx() - sz, tile.drawy() + sz); + tmptr[3].set(tile.drawx() + sz, tile.drawy() + sz); + + Arrays.sort(tmptr, (a, b) -> -Float.compare(Angles.angleDist(Angles.angle(unit.x, unit.y, a.x, a.y), ang), + Angles.angleDist(Angles.angle(unit.x, unit.y, b.x, b.y), ang))); + + float x1 = tmptr[0].x, y1 = tmptr[0].y, + x3 = tmptr[1].x, y3 = tmptr[1].y; + + Draw.alpha(1f); + + Lines.line(px, py, x1, y1); + Lines.line(px, py, x3, y3); + + Fill.circle(px, py, 1.6f + Mathf.absin(Time.time(), 0.8f, 1.5f)); + + Draw.color(); + } + + /** Class for storing build requests. Can be either a place or remove request. */ + class BuildRequest{ + public final int x, y, rotation; + public final Block block; + public final boolean breaking; + + public float progress; + public boolean initialized; + + /** This creates a build request. */ + public BuildRequest(int x, int y, int rotation, Block block){ + this.x = x; + this.y = y; + this.rotation = rotation; + this.block = block; + this.breaking = false; + } + + /** This creates a remove request. */ + public BuildRequest(int x, int y){ + this.x = x; + this.y = y; + this.rotation = -1; + this.block = world.tile(x, y).block(); + this.breaking = true; + } + + @Override + public String toString(){ + return "BuildRequest{" + + "x=" + x + + ", y=" + y + + ", rotation=" + rotation + + ", recipe=" + block + + ", breaking=" + breaking + + ", progress=" + progress + + ", initialized=" + initialized + + '}'; + } + } +} diff --git a/core/src/io/anuke/mindustry/entities/traits/DamageTrait.java b/core/src/io/anuke/mindustry/entities/traits/DamageTrait.java new file mode 100644 index 0000000000..3f009687d6 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/traits/DamageTrait.java @@ -0,0 +1,5 @@ +package io.anuke.mindustry.entities.traits; + +public interface DamageTrait{ + float damage(); +} diff --git a/core/src/io/anuke/mindustry/entities/traits/DrawTrait.java b/core/src/io/anuke/mindustry/entities/traits/DrawTrait.java new file mode 100644 index 0000000000..648d9797b3 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/traits/DrawTrait.java @@ -0,0 +1,10 @@ +package io.anuke.mindustry.entities.traits; + +public interface DrawTrait extends Entity{ + + default float drawSize(){ + return 20f; + } + + void draw(); +} diff --git a/core/src/io/anuke/mindustry/entities/traits/Entity.java b/core/src/io/anuke/mindustry/entities/traits/Entity.java new file mode 100644 index 0000000000..2de8e0e013 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/traits/Entity.java @@ -0,0 +1,42 @@ +package io.anuke.mindustry.entities.traits; + +import io.anuke.mindustry.entities.EntityGroup; + +public interface Entity extends MoveTrait{ + + int getID(); + + void resetID(int id); + + default void update(){} + + default void removed(){} + + default void added(){} + + EntityGroup targetGroup(); + + @SuppressWarnings("unchecked") + default void add(){ + if(targetGroup() != null){ + targetGroup().add(this); + } + } + + @SuppressWarnings("unchecked") + default void remove(){ + if(getGroup() != null){ + getGroup().remove(this); + } + + setGroup(null); + } + + EntityGroup getGroup(); + + void setGroup(EntityGroup group); + + default boolean isAdded(){ + return getGroup() != null; + } +} diff --git a/core/src/io/anuke/mindustry/entities/traits/HealthTrait.java b/core/src/io/anuke/mindustry/entities/traits/HealthTrait.java new file mode 100644 index 0000000000..536b6caad2 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/traits/HealthTrait.java @@ -0,0 +1,48 @@ +package io.anuke.mindustry.entities.traits; + +import io.anuke.arc.math.Mathf; + +public interface HealthTrait{ + + void health(float health); + + float health(); + + float maxHealth(); + + boolean isDead(); + + void setDead(boolean dead); + + default void onHit(SolidTrait entity){ + } + + default void onDeath(){ + } + + default void damage(float amount){ + health(health() - amount); + if(health() <= 0 && !isDead()){ + onDeath(); + setDead(true); + } + } + + default void clampHealth(){ + health(Mathf.clamp(health(), 0, maxHealth())); + } + + default float healthf(){ + return health() / maxHealth(); + } + + default void healBy(float amount){ + health(health() + amount); + clampHealth(); + } + + default void heal(){ + health(maxHealth()); + setDead(false); + } +} diff --git a/core/src/io/anuke/mindustry/entities/traits/MinerTrait.java b/core/src/io/anuke/mindustry/entities/traits/MinerTrait.java new file mode 100644 index 0000000000..c8275f6463 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/traits/MinerTrait.java @@ -0,0 +1,101 @@ +package io.anuke.mindustry.entities.traits; + +import io.anuke.arc.graphics.Color; +import io.anuke.arc.graphics.g2d.*; +import io.anuke.arc.math.*; +import io.anuke.arc.util.Time; +import io.anuke.mindustry.content.*; +import io.anuke.mindustry.entities.Effects; +import io.anuke.mindustry.entities.type.*; +import io.anuke.mindustry.gen.Call; +import io.anuke.mindustry.graphics.*; +import io.anuke.mindustry.type.Item; +import io.anuke.mindustry.world.Tile; + +import static io.anuke.mindustry.Vars.*; + +public interface MinerTrait extends Entity{ + + /** Returns the range at which this miner can mine blocks.*/ + default float getMiningRange(){ + return 70f; + } + + default boolean isMining(){ + return getMineTile() != null; + } + + /** Returns the tile this builder is currently mining. */ + Tile getMineTile(); + + /** Sets the tile this builder is currently mining. */ + void setMineTile(Tile tile); + + /** Returns the mining speed of this miner. 1 = standard, 0.5 = half speed, 2 = double speed, etc. */ + float getMinePower(); + + /** Returns whether or not this builder can mine a specific item type. */ + boolean canMine(Item item); + + default void updateMining(){ + Unit unit = (Unit)this; + Tile tile = getMineTile(); + TileEntity core = unit.getClosestCore(); + + if(tile == null || core == null || tile.block() != Blocks.air || dst(tile.worldx(), tile.worldy()) > getMiningRange() + || tile.drop() == null || !unit.acceptsItem(tile.drop()) || !canMine(tile.drop())){ + setMineTile(null); + }else{ + Item item = tile.drop(); + unit.rotation = Mathf.slerpDelta(unit.rotation, unit.angleTo(tile.worldx(), tile.worldy()), 0.4f); + + if(Mathf.chance(Time.delta() * (0.06 - item.hardness * 0.01) * getMinePower())){ + + if(unit.dst(core) < mineTransferRange && core.tile.block().acceptStack(item, 1, core.tile, unit) == 1){ + Call.transferItemTo(item, 1, + tile.worldx() + Mathf.range(tilesize / 2f), + tile.worldy() + Mathf.range(tilesize / 2f), core.tile); + }else if(unit.acceptsItem(item)){ + Call.transferItemToUnit(item, + tile.worldx() + Mathf.range(tilesize / 2f), + tile.worldy() + Mathf.range(tilesize / 2f), + unit); + } + } + + if(Mathf.chance(0.06 * Time.delta())){ + Effects.effect(Fx.pulverizeSmall, + tile.worldx() + Mathf.range(tilesize / 2f), + tile.worldy() + Mathf.range(tilesize / 2f), 0f, item.color); + } + } + } + + default void drawMining(){ + Unit unit = (Unit)this; + Tile tile = getMineTile(); + + if(tile == null) return; + + float focusLen = 4f + Mathf.absin(Time.time(), 1.1f, 0.5f); + float swingScl = 12f, swingMag = tilesize / 8f; + float flashScl = 0.3f; + + float px = unit.x + Angles.trnsx(unit.rotation, focusLen); + float py = unit.y + Angles.trnsy(unit.rotation, focusLen); + + float ex = tile.worldx() + Mathf.sin(Time.time() + 48, swingScl, swingMag); + float ey = tile.worldy() + Mathf.sin(Time.time() + 48, swingScl + 2f, swingMag); + + Draw.color(Color.LIGHT_GRAY, Color.WHITE, 1f - flashScl + Mathf.absin(Time.time(), 0.5f, flashScl)); + + Shapes.laser("minelaser", "minelaser-end", px, py, ex, ey, 0.75f); + + if(unit instanceof Player && ((Player)unit).isLocal){ + Lines.stroke(1f, Pal.accent); + Lines.poly(tile.worldx(), tile.worldy(), 4, tilesize / 2f * Mathf.sqrt2, Time.time()); + } + + Draw.color(); + } +} diff --git a/core/src/io/anuke/mindustry/entities/traits/MoveTrait.java b/core/src/io/anuke/mindustry/entities/traits/MoveTrait.java new file mode 100644 index 0000000000..561c7a76bd --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/traits/MoveTrait.java @@ -0,0 +1,20 @@ +package io.anuke.mindustry.entities.traits; + +import io.anuke.arc.math.geom.Position; + +public interface MoveTrait extends Position{ + + void setX(float x); + + void setY(float y); + + default void moveBy(float x, float y){ + setX(getX() + x); + setY(getY() + y); + } + + default void set(float x, float y){ + setX(x); + setY(y); + } +} diff --git a/core/src/io/anuke/mindustry/entities/traits/SaveTrait.java b/core/src/io/anuke/mindustry/entities/traits/SaveTrait.java new file mode 100644 index 0000000000..d992de766a --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/traits/SaveTrait.java @@ -0,0 +1,8 @@ +package io.anuke.mindustry.entities.traits; + +/** + * Marks an entity as serializable. + */ +public interface SaveTrait extends Entity, TypeTrait, Saveable{ + byte version(); +} diff --git a/core/src/io/anuke/mindustry/entities/traits/Saveable.java b/core/src/io/anuke/mindustry/entities/traits/Saveable.java new file mode 100644 index 0000000000..b0d22e6a34 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/traits/Saveable.java @@ -0,0 +1,8 @@ +package io.anuke.mindustry.entities.traits; + +import java.io.*; + +public interface Saveable{ + void writeSave(DataOutput stream) throws IOException; + void readSave(DataInput stream, byte version) throws IOException; +} diff --git a/core/src/io/anuke/mindustry/entities/traits/ScaleTrait.java b/core/src/io/anuke/mindustry/entities/traits/ScaleTrait.java new file mode 100644 index 0000000000..9975bb12a8 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/traits/ScaleTrait.java @@ -0,0 +1,43 @@ +package io.anuke.mindustry.entities.traits; + +import io.anuke.arc.math.Interpolation; + +public interface ScaleTrait{ + /** 0 to 1. */ + float fin(); + + /** 1 to 0 */ + default float fout(){ + return 1f - fin(); + } + + /** 1 to 0 */ + default float fout(Interpolation i){ + return i.apply(fout()); + } + + /** 1 to 0, ending at the specified margin */ + default float fout(float margin){ + float f = fin(); + if(f >= 1f - margin){ + return 1f - (f - (1f - margin)) / margin; + }else{ + return 1f; + } + } + + /** 0 to 1 **/ + default float fin(Interpolation i){ + return i.apply(fin()); + } + + /** 0 to 1 */ + default float finpow(){ + return Interpolation.pow3Out.apply(fin()); + } + + /** 0 to 1 to 0 */ + default float fslope(){ + return (0.5f - Math.abs(fin() - 0.5f)) * 2f; + } +} diff --git a/core/src/io/anuke/mindustry/entities/traits/ShooterTrait.java b/core/src/io/anuke/mindustry/entities/traits/ShooterTrait.java new file mode 100644 index 0000000000..ca3010eec3 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/traits/ShooterTrait.java @@ -0,0 +1,13 @@ +package io.anuke.mindustry.entities.traits; + +import io.anuke.arc.util.Interval; +import io.anuke.mindustry.type.Weapon; + +public interface ShooterTrait extends VelocityTrait, TeamTrait{ + + Interval getTimer(); + + int getShootTimer(boolean left); + + Weapon getWeapon(); +} diff --git a/core/src/io/anuke/mindustry/entities/traits/SolidTrait.java b/core/src/io/anuke/mindustry/entities/traits/SolidTrait.java new file mode 100644 index 0000000000..5ebf8105f3 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/traits/SolidTrait.java @@ -0,0 +1,38 @@ +package io.anuke.mindustry.entities.traits; + + +import io.anuke.arc.math.geom.*; +import io.anuke.arc.math.geom.QuadTree.QuadTreeObject; +import io.anuke.mindustry.Vars; + +public interface SolidTrait extends QuadTreeObject, MoveTrait, VelocityTrait, Entity, Position{ + + void hitbox(Rectangle rectangle); + + void hitboxTile(Rectangle rectangle); + + Vector2 lastPosition(); + + default boolean collidesGrid(int x, int y){ + return true; + } + + default float getDeltaX(){ + return getX() - lastPosition().x; + } + + default float getDeltaY(){ + return getY() - lastPosition().y; + } + + default boolean collides(SolidTrait other){ + return true; + } + + default void collision(SolidTrait other, float x, float y){ + } + + default void move(float x, float y){ + Vars.collisions.move(this, x, y); + } +} diff --git a/core/src/io/anuke/mindustry/entities/traits/SpawnerTrait.java b/core/src/io/anuke/mindustry/entities/traits/SpawnerTrait.java new file mode 100644 index 0000000000..df23a1053d --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/traits/SpawnerTrait.java @@ -0,0 +1,16 @@ +package io.anuke.mindustry.entities.traits; + +import io.anuke.arc.math.geom.Position; +import io.anuke.mindustry.entities.type.Player; +import io.anuke.mindustry.world.Tile; + +public interface SpawnerTrait extends TargetTrait, Position{ + Tile getTile(); + + void updateSpawning(Player unit); + + @Override + default boolean isValid(){ + return getTile().entity instanceof SpawnerTrait; + } +} diff --git a/core/src/io/anuke/mindustry/entities/traits/SyncTrait.java b/core/src/io/anuke/mindustry/entities/traits/SyncTrait.java new file mode 100644 index 0000000000..7a25c9c1ed --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/traits/SyncTrait.java @@ -0,0 +1,48 @@ +package io.anuke.mindustry.entities.traits; + +import io.anuke.mindustry.net.Interpolator; + +import java.io.*; + +public interface SyncTrait extends Entity, TypeTrait{ + + /** Sets the position of this entity and updated the interpolator. */ + default void setNet(float x, float y){ + set(x, y); + + if(getInterpolator() != null){ + getInterpolator().target.set(x, y); + getInterpolator().last.set(x, y); + getInterpolator().pos.set(0, 0); + getInterpolator().updateSpacing = 16; + getInterpolator().lastUpdated = 0; + } + } + + /** Interpolate entity position only. Override if you need to interpolate rotations or other values. */ + default void interpolate(){ + if(getInterpolator() == null){ + throw new RuntimeException("This entity must have an interpolator to interpolate()!"); + } + + getInterpolator().update(); + + setX(getInterpolator().pos.x); + setY(getInterpolator().pos.y); + } + + /** Return the interpolator used for smoothing the position. Optional. */ + default Interpolator getInterpolator(){ + return null; + } + + /** Whether syncing is enabled for this entity; true by default. */ + default boolean isSyncing(){ + return true; + } + + //Read and write sync data, usually position + void write(DataOutput data) throws IOException; + + void read(DataInput data) throws IOException; +} diff --git a/core/src/io/anuke/mindustry/entities/traits/TargetTrait.java b/core/src/io/anuke/mindustry/entities/traits/TargetTrait.java new file mode 100644 index 0000000000..819e9dbcdc --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/traits/TargetTrait.java @@ -0,0 +1,35 @@ +package io.anuke.mindustry.entities.traits; + +import io.anuke.arc.math.geom.Position; +import io.anuke.mindustry.game.Team; + +/** + * Base interface for targetable entities. + */ +public interface TargetTrait extends Position, VelocityTrait{ + + boolean isDead(); + + Team getTeam(); + + default float getTargetVelocityX(){ + if(this instanceof SolidTrait){ + return ((SolidTrait)this).getDeltaX(); + } + return velocity().x; + } + + default float getTargetVelocityY(){ + if(this instanceof SolidTrait){ + return ((SolidTrait)this).getDeltaY(); + } + return velocity().y; + } + + /** + * Whether this entity is a valid target. + */ + default boolean isValid(){ + return !isDead(); + } +} diff --git a/core/src/io/anuke/mindustry/entities/traits/TeamTrait.java b/core/src/io/anuke/mindustry/entities/traits/TeamTrait.java new file mode 100644 index 0000000000..5f800b2098 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/traits/TeamTrait.java @@ -0,0 +1,7 @@ +package io.anuke.mindustry.entities.traits; + +import io.anuke.mindustry.game.Team; + +public interface TeamTrait extends Entity{ + Team getTeam(); +} diff --git a/core/src/io/anuke/mindustry/entities/traits/TimeTrait.java b/core/src/io/anuke/mindustry/entities/traits/TimeTrait.java new file mode 100644 index 0000000000..47dc7dfe48 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/traits/TimeTrait.java @@ -0,0 +1,23 @@ +package io.anuke.mindustry.entities.traits; + +import io.anuke.arc.math.Mathf; +import io.anuke.arc.util.Time; + +public interface TimeTrait extends ScaleTrait, Entity{ + + float lifetime(); + + void time(float time); + + float time(); + + default void updateTime(){ + time(Mathf.clamp(time() + Time.delta(), 0, lifetime())); + + if(time() >= lifetime()){ + remove(); + } + } + + //fin() is not implemented due to compiler issues with iOS/RoboVM +} diff --git a/core/src/io/anuke/mindustry/entities/traits/TypeTrait.java b/core/src/io/anuke/mindustry/entities/traits/TypeTrait.java new file mode 100644 index 0000000000..69d1794e96 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/traits/TypeTrait.java @@ -0,0 +1,44 @@ +package io.anuke.mindustry.entities.traits; + +import io.anuke.arc.collection.Array; +import io.anuke.arc.collection.ObjectIntMap; +import io.anuke.arc.function.Supplier; + +public interface TypeTrait{ + int[] lastRegisteredID = {0}; + Array> registeredTypes = new Array<>(); + ObjectIntMap> typeToID = new ObjectIntMap<>(); + + /** + * Register and return a type ID. The supplier should return a fresh instace of that type. + */ + static void registerType(Class type, Supplier supplier){ + if(typeToID.get(type, -1) != -1){ + return; //already registered + } + + registeredTypes.add(supplier); + int result = lastRegisteredID[0]; + typeToID.put(type, result); + lastRegisteredID[0]++; + } + + /**Gets a syncable type by ID.*/ + static Supplier getTypeByID(int id){ + if(id == -1){ + throw new IllegalArgumentException("Attempt to retrieve invalid entity type ID! Did you forget to set it in ContentLoader.registerTypes()?"); + } + return registeredTypes.get(id); + } + + /** + * Returns the type ID of this entity used for intstantiation. Should be < BYTE_MAX. + * Do not override! + */ + default int getTypeID(){ + int id = typeToID.get(getClass(), -1); + if(id == -1) + throw new RuntimeException("Class of type '" + getClass() + "' is not registered! Did you forget to register it in ContentLoader#registerTypes()?"); + return id; + } +} diff --git a/core/src/io/anuke/mindustry/entities/traits/VelocityTrait.java b/core/src/io/anuke/mindustry/entities/traits/VelocityTrait.java new file mode 100644 index 0000000000..e5bddaed43 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/traits/VelocityTrait.java @@ -0,0 +1,36 @@ +package io.anuke.mindustry.entities.traits; + +import io.anuke.arc.math.geom.Vector2; +import io.anuke.arc.util.Time; + +public interface VelocityTrait extends MoveTrait{ + + Vector2 velocity(); + + default void applyImpulse(float x, float y){ + velocity().x += x / mass(); + velocity().y += y / mass(); + } + + default float maxVelocity(){ + return Float.MAX_VALUE; + } + + default float mass(){ + return 1f; + } + + default float drag(){ + return 0f; + } + + default void updateVelocity(){ + velocity().scl(1f - drag() * Time.delta()); + + if(this instanceof SolidTrait){ + ((SolidTrait)this).move(velocity().x * Time.delta(), velocity().y * Time.delta()); + }else{ + moveBy(velocity().x * Time.delta(), velocity().y * Time.delta()); + } + } +} diff --git a/core/src/io/anuke/mindustry/entities/type/BaseUnit.java b/core/src/io/anuke/mindustry/entities/type/BaseUnit.java new file mode 100644 index 0000000000..88f9090f5e --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/type/BaseUnit.java @@ -0,0 +1,387 @@ +package io.anuke.mindustry.entities.type; + +import io.anuke.annotations.Annotations.Loc; +import io.anuke.annotations.Annotations.Remote; +import io.anuke.arc.Core; +import io.anuke.arc.graphics.g2d.Draw; +import io.anuke.arc.graphics.g2d.TextureRegion; +import io.anuke.arc.math.Angles; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.Geometry; +import io.anuke.arc.math.geom.Rectangle; +import io.anuke.arc.util.Interval; +import io.anuke.arc.util.Time; +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.content.StatusEffects; +import io.anuke.mindustry.entities.EntityGroup; +import io.anuke.mindustry.entities.Units; +import io.anuke.mindustry.entities.traits.ShooterTrait; +import io.anuke.mindustry.entities.traits.TargetTrait; +import io.anuke.mindustry.entities.units.*; +import io.anuke.mindustry.game.Team; +import io.anuke.mindustry.gen.Call; +import io.anuke.mindustry.net.Net; +import io.anuke.mindustry.type.*; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.units.UnitFactory.UnitFactoryEntity; +import io.anuke.mindustry.world.meta.BlockFlag; + +import java.io.*; + +import static io.anuke.mindustry.Vars.*; + +/** Base class for AI units. */ +public abstract class BaseUnit extends Unit implements ShooterTrait{ + + protected static int timerIndex = 0; + + protected static final int timerTarget = timerIndex++; + protected static final int timerTarget2 = timerIndex++; + protected static final int timerShootLeft = timerIndex++; + protected static final int timerShootRight = timerIndex++; + + protected UnitType type; + protected Interval timer = new Interval(5); + protected StateMachine state = new StateMachine(); + protected TargetTrait target; + + protected int spawner = noSpawner; + + /** internal constructor used for deserialization, DO NOT USE */ + public BaseUnit(){ + } + + @Remote(called = Loc.server) + public static void onUnitDeath(BaseUnit unit){ + if(unit == null) return; + + if(Net.server() || !Net.active()){ + UnitDrops.dropItems(unit); + } + + unit.onSuperDeath(); + + //visual only. + if(Net.client()){ + Tile tile = world.tile(unit.spawner); + if(tile != null && !Net.client()){ + tile.block().unitRemoved(tile, unit); + } + + unit.spawner = noSpawner; + } + + //must run afterwards so the unit's group is not null when sending the removal packet + Core.app.post(unit::remove); + } + + @Override + public float drag(){ + return type.drag; + } + + public Tile getSpawner(){ + return world.tile(spawner); + } + + /** Initialize the type and team of this unit. Only call once! */ + public void init(UnitType type, Team team){ + if(this.type != null) throw new RuntimeException("This unit is already initialized!"); + + this.type = type; + this.team = team; + } + + public UnitType getType(){ + return type; + } + + public void setSpawner(Tile tile){ + this.spawner = tile.pos(); + } + + public void rotate(float angle){ + rotation = Mathf.slerpDelta(rotation, angle, type.rotatespeed); + } + + public boolean targetHasFlag(BlockFlag flag){ + return (target instanceof TileEntity && ((TileEntity)target).tile.block().flags.contains(flag)) || + (target instanceof Tile && ((Tile)target).block().flags.contains(flag)); + } + + public void setState(UnitState state){ + this.state.set(state); + } + + public boolean retarget(){ + return timer.get(timerTarget, 20); + } + + /** Only runs when the unit has a target. */ + public void behavior(){ + + } + + public void updateTargeting(){ + if(target == null || (target instanceof Unit && (target.isDead() || target.getTeam() == team)) + || (target instanceof TileEntity && ((TileEntity)target).tile.entity == null)){ + target = null; + } + } + + public void targetClosestAllyFlag(BlockFlag flag){ + Tile target = Geometry.findClosest(x, y, world.indexer.getAllied(team, flag)); + if(target != null) this.target = target.entity; + } + + public void targetClosestEnemyFlag(BlockFlag flag){ + Tile target = Geometry.findClosest(x, y, world.indexer.getEnemy(team, flag)); + if(target != null) this.target = target.entity; + } + + public void targetClosest(){ + TargetTrait newTarget = Units.closestTarget(team, x, y, Math.max(getWeapon().bullet.range(), type.range), u -> type.targetAir || !u.isFlying()); + if(newTarget != null){ + target = newTarget; + } + } + + public TileEntity getClosestEnemyCore(){ + + for(Team enemy : Vars.state.teams.enemiesOf(team)){ + Tile tile = Geometry.findClosest(x, y, Vars.state.teams.get(enemy).cores); + if(tile != null){ + return tile.entity; + } + } + + return null; + } + + public UnitState getStartState(){ + return null; + } + + protected void drawItems(){ + float backTrns = 4f; + if(item.amount > 0){ + int stored = Mathf.clamp(item.amount / 6, 1, 8); + + for(int i = 0; i < stored; i++){ + float angT = i == 0 ? 0 : Mathf.randomSeedRange(i + 2, 60f); + float lenT = i == 0 ? 0 : Mathf.randomSeedRange(i + 3, 1f) - 1f; + Draw.rect(item.item.icon(Item.Icon.large), + x + Angles.trnsx(rotation + 180f + angT, backTrns + lenT), + y + Angles.trnsy(rotation + 180f + angT, backTrns + lenT), + itemSize, itemSize, rotation); + } + } + } + + public boolean isBoss(){ + return hasEffect(StatusEffects.boss); + } + + @Override + public float getDamageMultipler(){ + return status.getDamageMultiplier() * Vars.state.rules.unitDamageMultiplier; + } + + @Override + public boolean isImmune(StatusEffect effect){ + return type.immunities.contains(effect); + } + + @Override + public boolean isValid(){ + return super.isValid() && isAdded(); + } + + @Override + public Interval getTimer(){ + return timer; + } + + @Override + public int getShootTimer(boolean left){ + return left ? timerShootLeft : timerShootRight; + } + + @Override + public Weapon getWeapon(){ + return type.weapon; + } + + @Override + public TextureRegion getIconRegion(){ + return type.iconRegion; + } + + @Override + public int getItemCapacity(){ + return type.itemCapacity; + } + + @Override + public void interpolate(){ + super.interpolate(); + + if(interpolator.values.length > 0){ + rotation = interpolator.values[0]; + } + } + + @Override + public float maxHealth(){ + return type.health * Vars.state.rules.unitHealthMultiplier; + } + + @Override + public float mass(){ + return type.mass; + } + + @Override + public boolean isFlying(){ + return type.isFlying; + } + + @Override + public void update(){ + if(isDead()){ + //dead enemies should get immediately removed + remove(); + return; + } + + hitTime -= Time.delta(); + + if(Net.client()){ + interpolate(); + status.update(this); + return; + } + + if(!isFlying() && (world.tileWorld(x, y) != null && world.tileWorld(x, y).solid())){ + kill(); + } + + avoidOthers(); + + if(spawner != noSpawner && (world.tile(spawner) == null || !(world.tile(spawner).entity instanceof UnitFactoryEntity))){ + kill(); + } + + updateTargeting(); + + state.update(); + updateVelocityStatus(); + + if(target != null) behavior(); + + if(!isFlying()){ + clampPosition(); + } + } + + @Override + public void draw(){ + + } + + @Override + public float maxVelocity(){ + return type.maxVelocity; + } + + @Override + public void removed(){ + super.removed(); + Tile tile = world.tile(spawner); + if(tile != null && !Net.client()){ + tile.block().unitRemoved(tile, this); + } + + spawner = noSpawner; + } + + @Override + public float drawSize(){ + return type.hitsize * 10; + } + + @Override + public void onDeath(){ + Call.onUnitDeath(this); + } + + @Override + public void added(){ + state.set(getStartState()); + + health(maxHealth()); + } + + @Override + public void hitbox(Rectangle rectangle){ + rectangle.setSize(type.hitsize).setCenter(x, y); + } + + @Override + public void hitboxTile(Rectangle rectangle){ + rectangle.setSize(type.hitsizeTile).setCenter(x, y); + } + + @Override + public EntityGroup targetGroup(){ + return unitGroups[team.ordinal()]; + } + + @Override + public byte version(){ + return 0; + } + + @Override + public void writeSave(DataOutput stream) throws IOException{ + super.writeSave(stream); + stream.writeByte(type.id); + stream.writeInt(spawner); + } + + @Override + public void readSave(DataInput stream, byte version) throws IOException{ + super.readSave(stream, version); + byte type = stream.readByte(); + this.spawner = stream.readInt(); + + this.type = content.getByID(ContentType.unit, type); + add(); + } + + @Override + public void write(DataOutput data) throws IOException{ + super.writeSave(data); + data.writeByte(type.id); + data.writeInt(spawner); + } + + @Override + public void read(DataInput data) throws IOException{ + float lastx = x, lasty = y, lastrot = rotation; + + super.readSave(data, version()); + + this.type = content.getByID(ContentType.unit, data.readByte()); + this.spawner = data.readInt(); + + interpolator.read(lastx, lasty, x, y, rotation); + rotation = lastrot; + x = lastx; + y = lasty; + } + + public void onSuperDeath(){ + super.onDeath(); + } +} \ No newline at end of file diff --git a/core/src/io/anuke/mindustry/entities/type/FlyingUnit.java b/core/src/io/anuke/mindustry/entities/type/FlyingUnit.java new file mode 100644 index 0000000000..e0a95b7866 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/type/FlyingUnit.java @@ -0,0 +1,220 @@ +package io.anuke.mindustry.entities.type; + +import io.anuke.arc.graphics.Color; +import io.anuke.arc.graphics.g2d.Draw; +import io.anuke.arc.graphics.g2d.Fill; +import io.anuke.arc.math.Angles; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.Vector2; +import io.anuke.arc.util.Time; +import io.anuke.arc.util.Tmp; +import io.anuke.mindustry.entities.Predict; +import io.anuke.mindustry.entities.Units; +import io.anuke.mindustry.entities.bullet.BulletType; +import io.anuke.mindustry.entities.units.UnitState; +import io.anuke.mindustry.graphics.Pal; +import io.anuke.mindustry.net.Net; +import io.anuke.mindustry.world.meta.BlockFlag; + +public abstract class FlyingUnit extends BaseUnit{ + protected float[] weaponAngles = {0, 0}; + + protected final UnitState + + attack = new UnitState(){ + public void entered(){ + target = null; + } + + public void update(){ + + if(Units.invalidateTarget(target, team, x, y)){ + target = null; + } + + if(retarget()){ + targetClosest(); + + if(target == null) targetClosestEnemyFlag(BlockFlag.producer); + if(target == null) targetClosestEnemyFlag(BlockFlag.turret); + + if(target == null){ + setState(patrol); + } + }; + + if(target != null){ + attack(type.attackLength); + + if((Angles.near(angleTo(target), rotation, type.shootCone) || getWeapon().ignoreRotation) //bombers and such don't care about rotation + && dst(target) < getWeapon().bullet.range()){ + BulletType ammo = getWeapon().bullet; + + if(type.rotateWeapon){ + for(boolean left : Mathf.booleans){ + int wi = Mathf.num(left); + float wx = x + Angles.trnsx(rotation - 90, getWeapon().width * Mathf.sign(left)); + float wy = y + Angles.trnsy(rotation - 90, getWeapon().width * Mathf.sign(left)); + + weaponAngles[wi] = Mathf.slerpDelta(weaponAngles[wi], Angles.angle(wx, wy, target.getX(), target.getY()), 0.1f); + + Tmp.v2.trns(weaponAngles[wi], getWeapon().length); + getWeapon().update(FlyingUnit.this, wx + Tmp.v2.x, wy + Tmp.v2.y, weaponAngles[wi], left); + } + }else{ + Vector2 to = Predict.intercept(FlyingUnit.this, target, ammo.speed); + getWeapon().update(FlyingUnit.this, to.x, to.y); + } + } + } + } + }, + patrol = new UnitState(){ + public void update(){ + if(retarget()){ + targetClosest(); + targetClosestEnemyFlag(BlockFlag.target); + + if(target != null && !Units.invalidateTarget(target, team, x, y)){ + setState(attack); + return; + } + + target = getClosestCore(); + }; + + if(target != null){ + circle(60f + Mathf.absin(Time.time() + Mathf.randomSeed(id) * 1200f, 70f, 1200f)); + } + } + }; + + @Override + public void move(float x, float y){ + moveBy(x, y); + } + + @Override + public void update(){ + super.update(); + + if(!Net.client()){ + updateRotation(); + wobble(); + } + } + + @Override + public void drawUnder(){ + drawEngine(); + } + + @Override + public void draw(){ + Draw.mixcol(Color.WHITE, hitTime / hitDuration); + Draw.rect(type.region, x, y, rotation - 90); + + drawWeapons(); + drawItems(); + + Draw.mixcol(); + } + + public void drawWeapons(){ + + } + + public void drawEngine(){ + Draw.color(Pal.engine); + Fill.circle(x + Angles.trnsx(rotation + 180, type.engineOffset), y + Angles.trnsy(rotation + 180, type.engineOffset), + type.engineSize + Mathf.absin(Time.time(), 2f, type.engineSize / 4f)); + + Draw.color(Color.WHITE); + Fill.circle(x + Angles.trnsx(rotation + 180, type.engineOffset - 1f), y + Angles.trnsy(rotation + 180, type.engineOffset - 1f), + (type.engineSize + Mathf.absin(Time.time(), 2f, type.engineSize / 4f)) / 2f); + Draw.color(); + } + + @Override + public void behavior(){ + + if(Units.invalidateTarget(target, this)){ + for(boolean left : Mathf.booleans){ + int wi = Mathf.num(left); + weaponAngles[wi] = Mathf.slerpDelta(weaponAngles[wi], rotation, 0.1f); + } + } + } + + @Override + public UnitState getStartState(){ + return attack; + } + + protected void wobble(){ + if(Net.client()) return; + + x += Mathf.sin(Time.time() + id * 999, 25f, 0.05f) * Time.delta(); + y += Mathf.cos(Time.time() + id * 999, 25f, 0.05f) * Time.delta(); + + if(velocity.len() <= 0.05f){ + //rotation += Mathf.sin(Time.time() + id * 99, 10f, 2f * type.speed)*Time.delta(); + } + } + + protected void updateRotation(){ + rotation = velocity.angle(); + } + + protected void circle(float circleLength){ + circle(circleLength, type.speed); + } + + protected void circle(float circleLength, float speed){ + if(target == null) return; + + Tmp.v1.set(target.getX() - x, target.getY() - y); + + if(Tmp.v1.len() < circleLength){ + Tmp.v1.rotate((circleLength - Tmp.v1.len()) / circleLength * 180f); + } + + Tmp.v1.setLength(speed * Time.delta()); + + velocity.add(Tmp.v1); + } + + protected void moveTo(float circleLength){ + if(target == null) return; + + Tmp.v1.set(target.getX() - x, target.getY() - y); + + float length = circleLength <= 0.001f ? 1f : Mathf.clamp((dst(target) - circleLength) / 100f, -1f, 1f); + + Tmp.v1.setLength(type.speed * Time.delta() * length); + if(length < -0.5f){ + Tmp.v1.rotate(180f); + }else if(length < 0){ + Tmp.v1.setZero(); + } + + velocity.add(Tmp.v1); + } + + protected void attack(float circleLength){ + Tmp.v1.set(target.getX() - x, target.getY() - y); + + float ang = angleTo(target); + float diff = Angles.angleDist(ang, rotation); + + if(diff > 100f && Tmp.v1.len() < circleLength){ + Tmp.v1.setAngle(velocity.angle()); + }else{ + Tmp.v1.setAngle(Mathf.slerpDelta(velocity.angle(), Tmp.v1.angle(), 0.44f)); + } + + Tmp.v1.setLength(type.speed * Time.delta()); + + velocity.add(Tmp.v1); + } +} diff --git a/core/src/io/anuke/mindustry/entities/type/GroundUnit.java b/core/src/io/anuke/mindustry/entities/type/GroundUnit.java new file mode 100644 index 0000000000..e319063e9d --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/type/GroundUnit.java @@ -0,0 +1,244 @@ +package io.anuke.mindustry.entities.type; + +import io.anuke.arc.graphics.Color; +import io.anuke.arc.graphics.g2d.Draw; +import io.anuke.arc.math.Angles; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.Vector2; +import io.anuke.arc.util.Time; +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.entities.Predict; +import io.anuke.mindustry.entities.Units; +import io.anuke.mindustry.entities.bullet.BulletType; +import io.anuke.mindustry.entities.units.UnitState; +import io.anuke.mindustry.game.Team; +import io.anuke.mindustry.type.Weapon; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.Floor; + +import static io.anuke.mindustry.Vars.world; + +public abstract class GroundUnit extends BaseUnit{ + protected static Vector2 vec = new Vector2(); + + protected float walkTime; + protected float stuckTime; + protected float baseRotation; + + public final UnitState + + attack = new UnitState(){ + public void entered(){ + target = null; + } + + public void update(){ + TileEntity core = getClosestEnemyCore(); + + if(core == null){ + setState(patrol); + return; + } + + float dst = dst(core); + + if(dst < getWeapon().bullet.range() / 1.1f){ + target = core; + } + + if(dst > getWeapon().bullet.range() * 0.5f){ + moveToCore(); + } + } + }, + patrol = new UnitState(){ + public void update(){ + TileEntity target = getClosestCore(); + + if(target != null){ + if(dst(target) > 400f){ + moveAwayFromCore(); + }else if(!(!Units.invalidateTarget(GroundUnit.this.target, GroundUnit.this) && dst(GroundUnit.this.target) < getWeapon().bullet.range())){ + patrol(); + } + } + } + }; + + @Override + public void interpolate(){ + super.interpolate(); + + if(interpolator.values.length > 1){ + baseRotation = interpolator.values[1]; + } + } + + @Override + public void move(float x, float y){ + float dst = Mathf.dst(x, y); + if(dst > 0.01f){ + baseRotation = Mathf.slerp(baseRotation, Mathf.angle(x, y), type.baseRotateSpeed * (dst / type.speed)); + } + super.move(x, y); + } + + @Override + public UnitState getStartState(){ + return attack; + } + + @Override + public void update(){ + super.update(); + + stuckTime = !vec.set(x, y).sub(lastPosition()).isZero(0.0001f) ? 0f : stuckTime + Time.delta(); + + if(!velocity.isZero()){ + baseRotation = Mathf.slerpDelta(baseRotation, velocity.angle(), 0.05f); + } + + if(stuckTime < 1f){ + walkTime += Time.delta(); + } + } + + @Override + public Weapon getWeapon(){ + return type.weapon; + } + + @Override + public void draw(){ + Draw.mixcol(Color.WHITE, hitTime / hitDuration); + + float ft = Mathf.sin(walkTime * type.speed * 5f, 6f, 2f + type.hitsize / 15f); + + Floor floor = getFloorOn(); + + if(floor.isLiquid){ + Draw.color(Color.WHITE, floor.color, 0.5f); + } + + for(int i : Mathf.signs){ + Draw.rect(type.legRegion, + x + Angles.trnsx(baseRotation, ft * i), + y + Angles.trnsy(baseRotation, ft * i), + type.legRegion.getWidth() * i * Draw.scl, type.legRegion.getHeight() * Draw.scl - Mathf.clamp(ft * i, 0, 2), baseRotation - 90); + } + + if(floor.isLiquid){ + Draw.color(Color.WHITE, floor.color, drownTime * 0.4f); + }else{ + Draw.color(Color.WHITE); + } + + Draw.rect(type.baseRegion, x, y, baseRotation - 90); + + Draw.rect(type.region, x, y, rotation - 90); + + for(int i : Mathf.signs){ + float tra = rotation - 90, trY = -type.weapon.getRecoil(this, i > 0) + type.weaponOffsetY; + float w = -i * type.weapon.region.getWidth() * Draw.scl; + Draw.rect(type.weapon.region, + x + Angles.trnsx(tra, getWeapon().width * i, trY), + y + Angles.trnsy(tra, getWeapon().width * i, trY), w, type.weapon.region.getHeight() * Draw.scl, rotation - 90); + } + + drawItems(); + + Draw.mixcol(); + } + + @Override + public void behavior(){ + + if(!Units.invalidateTarget(target, this)){ + if(dst(target) < getWeapon().bullet.range()){ + rotate(angleTo(target)); + + if(Angles.near(angleTo(target), rotation, 13f)){ + BulletType ammo = getWeapon().bullet; + + Vector2 to = Predict.intercept(GroundUnit.this, target, ammo.speed); + + getWeapon().update(GroundUnit.this, to.x, to.y); + } + } + } + } + + @Override + public void updateTargeting(){ + super.updateTargeting(); + + if(Units.invalidateTarget(target, team, x, y, Float.MAX_VALUE)){ + target = null; + } + + if(retarget()){ + targetClosest(); + } + } + + protected void patrol(){ + vec.trns(baseRotation, type.speed * Time.delta()); + velocity.add(vec.x, vec.y); + vec.trns(baseRotation, type.hitsizeTile * 3); + Tile tile = world.tileWorld(x + vec.x, y + vec.y); + if((tile == null || tile.solid() || tile.floor().drownTime > 0) || stuckTime > 10f){ + baseRotation += Mathf.sign(id % 2 - 0.5f) * Time.delta() * 3f; + } + + rotation = Mathf.slerpDelta(rotation, velocity.angle(), type.rotatespeed); + } + + protected void circle(float circleLength){ + if(target == null) return; + + vec.set(target.getX() - x, target.getY() - y); + + if(vec.len() < circleLength){ + vec.rotate((circleLength - vec.len()) / circleLength * 180f); + } + + vec.setLength(type.speed * Time.delta()); + + velocity.add(vec); + } + + protected void moveToCore(){ + Tile tile = world.tileWorld(x, y); + if(tile == null) return; + Tile targetTile = world.pathfinder.getTargetTile(team, tile); + + if(tile == targetTile) return; + + velocity.add(vec.trns(angleTo(targetTile), type.speed * Time.delta())); + if(Units.invalidateTarget(target, this)){ + rotation = Mathf.slerpDelta(rotation, baseRotation, type.rotatespeed); + } + } + + protected void moveAwayFromCore(){ + Team enemy = null; + for(Team team : Vars.state.teams.enemiesOf(team)){ + if(Vars.state.teams.isActive(team)){ + enemy = team; + break; + } + } + + if(enemy == null) return; + + Tile tile = world.tileWorld(x, y); + if(tile == null) return; + Tile targetTile = world.pathfinder.getTargetTile(enemy, tile); + TileEntity core = getClosestCore(); + + if(tile == targetTile || core == null || dst(core) < 90f) return; + + velocity.add(vec.trns(angleTo(targetTile), type.speed * Time.delta())); + rotation = Mathf.slerpDelta(rotation, baseRotation, type.rotatespeed); + } +} diff --git a/core/src/io/anuke/mindustry/entities/type/Player.java b/core/src/io/anuke/mindustry/entities/type/Player.java new file mode 100644 index 0000000000..6681f22b02 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/type/Player.java @@ -0,0 +1,930 @@ +package io.anuke.mindustry.entities.type; + +import io.anuke.annotations.Annotations.Loc; +import io.anuke.annotations.Annotations.Remote; +import io.anuke.arc.Core; +import io.anuke.arc.collection.Queue; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.graphics.g2d.*; +import io.anuke.arc.math.Angles; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.*; +import io.anuke.arc.util.*; +import io.anuke.arc.util.pooling.Pools; +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.content.Fx; +import io.anuke.mindustry.content.Mechs; +import io.anuke.mindustry.entities.*; +import io.anuke.mindustry.entities.traits.*; +import io.anuke.mindustry.game.Team; +import io.anuke.mindustry.gen.Call; +import io.anuke.mindustry.graphics.Pal; +import io.anuke.mindustry.input.Binding; +import io.anuke.mindustry.input.InputHandler.PlaceDraw; +import io.anuke.mindustry.io.TypeIO; +import io.anuke.mindustry.net.Net; +import io.anuke.mindustry.net.NetConnection; +import io.anuke.mindustry.type.*; +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.Floor; + +import java.io.*; + +import static io.anuke.mindustry.Vars.*; + +public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{ + public static final int timerSync = 2; + public static final int timerAbility = 3; + private static final int timerShootLeft = 0; + private static final int timerShootRight = 1; + private static final float liftoffBoost = 0.2f; + + private static final Rectangle rect = new Rectangle(); + + //region instance variables + + public float baseRotation; + public float pointerX, pointerY; + public String name = "name"; + public String uuid, usid; + public boolean isAdmin, isTransferring, isShooting, isBoosting, isMobile, isTyping; + public float boostHeat, shootHeat, destructTime; + public boolean achievedFlight; + public Color color = new Color(); + public Mech mech = Mechs.starter; + public SpawnerTrait spawner, lastSpawner; + public int respawns; + + public NetConnection con; + public boolean isLocal = false; + public Interval timer = new Interval(4); + public TargetTrait target; + public TargetTrait moveTarget; + + public String lastText; + public float textFadeTime; + + private float walktime; + private Queue placeQueue = new Queue<>(); + private Tile mining; + private Vector2 movement = new Vector2(); + private boolean moved; + + //endregion + + //region unit and event overrides, utility methods + + @Remote(targets = Loc.server, called = Loc.server) + public static void onPlayerDeath(Player player){ + if(player == null) return; + + player.dead = true; + player.placeQueue.clear(); + player.onDeath(); + } + + @Override + public float getDamageMultipler(){ + return status.getDamageMultiplier() * state.rules.playerDamageMultiplier; + } + + @Override + public void hitbox(Rectangle rectangle){ + rectangle.setSize(mech.hitsize).setCenter(x, y); + } + + @Override + public void hitboxTile(Rectangle rectangle){ + rectangle.setSize(mech.hitsize * 2f / 3f).setCenter(x, y); + } + + @Override + public void onRespawn(Tile tile){ + velocity.setZero(); + boostHeat = 1f; + achievedFlight = true; + rotation = 90f; + baseRotation = 90f; + dead = false; + spawner = null; + respawns --; + + setNet(tile.drawx(), tile.drawy()); + clearItem(); + heal(); + } + + @Override + public void move(float x, float y){ + if(!mech.flying){ + collisions.move(this, x, y); + }else{ + moveBy(x, y); + } + } + + @Override + public float drag(){ + return mech.drag; + } + + @Override + public Interval getTimer(){ + return timer; + } + + @Override + public int getShootTimer(boolean left){ + return left ? timerShootLeft : timerShootRight; + } + + @Override + public Weapon getWeapon(){ + return mech.weapon; + } + + @Override + public float getMinePower(){ + return mech.mineSpeed; + } + + @Override + public TextureRegion getIconRegion(){ + return mech.iconRegion; + } + + @Override + public int getItemCapacity(){ + return mech.itemCapacity; + } + + @Override + public void interpolate(){ + super.interpolate(); + + if(interpolator.values.length > 1){ + baseRotation = interpolator.values[1]; + } + + if(interpolator.target.dst(interpolator.last) > 1f){ + walktime += Time.delta(); + } + } + + @Override + public float getBuildPower(Tile tile){ + return mech.buildPower; + } + + @Override + public float maxHealth(){ + return mech.health * state.rules.playerHealthMultiplier; + } + + @Override + public Tile getMineTile(){ + return mining; + } + + @Override + public void setMineTile(Tile tile){ + this.mining = tile; + } + + @Override + public boolean canMine(Item item){ + return item.hardness <= mech.drillPower; + } + + @Override + public float calculateDamage(float amount){ + return amount * Mathf.clamp(1f - (status.getArmorMultiplier() + mech.getExtraArmor(this)) / 100f); + } + + @Override + public void added(){ + baseRotation = 90f; + } + + @Override + public float mass(){ + return mech.mass; + } + + @Override + public boolean isFlying(){ + return mech.flying || boostHeat > liftoffBoost; + } + + @Override + public void damage(float amount){ + hitTime = hitDuration; + if(!Net.client()){ + health -= calculateDamage(amount); + } + + if(health <= 0 && !dead){ + Call.onPlayerDeath(this); + } + } + + @Override + public void set(float x, float y){ + this.x = x; + this.y = y; + } + + @Override + public float maxVelocity(){ + return mech.maxSpeed; + } + + @Override + public Queue buildQueue(){ + return placeQueue; + } + + @Override + public String toString(){ + return "Player{" + id + ", mech=" + mech.name + ", local=" + isLocal + ", " + x + ", " + y + "}"; + } + + @Override + public EntityGroup targetGroup(){ + return playerGroup; + } + + public void setTeam(Team team){ + this.team = team; + } + + //endregion + + //region draw methods + + @Override + public float drawSize(){ + return isLocal ? Float.MAX_VALUE : 40 + placeDistance; + } + + @Override + public void drawShadow(float offsetX, float offsetY){ + float scl = mech.flying ? 1f : boostHeat / 2f; + + Draw.rect(mech.iconRegion, x + offsetX * scl, y + offsetY * scl, rotation - 90); + } + + @Override + public void draw(){ + if(dead) return; + + if(!movement.isZero() && moved && !state.isPaused()){ + walktime += movement.len() * getFloorOn().speedMultiplier * 2f; + baseRotation = Mathf.slerpDelta(baseRotation, movement.angle(), 0.13f); + } + + float ft = Mathf.sin(walktime, 6f, 2f) * (1f - boostHeat); + + Floor floor = getFloorOn(); + + Draw.color(); + Draw.mixcol(Color.WHITE, hitTime / hitDuration); + + if(!mech.flying){ + if(floor.isLiquid){ + Draw.color(Color.WHITE, floor.color, 0.5f); + } + + float boostTrnsY = -boostHeat * 3f; + float boostTrnsX = boostHeat * 3f; + float boostAng = boostHeat * 40f; + + for(int i : Mathf.signs){ + Draw.rect(mech.legRegion, + x + Angles.trnsx(baseRotation, ft * i + boostTrnsY, -boostTrnsX * i), + y + Angles.trnsy(baseRotation, ft * i + boostTrnsY, -boostTrnsX * i), + mech.legRegion.getWidth() * i * Draw.scl, + (mech.legRegion.getHeight() - Mathf.clamp(ft * i, 0, 2)) * Draw.scl, + baseRotation - 90 + boostAng * i); + } + + Draw.rect(mech.baseRegion, x, y, baseRotation - 90); + } + + if(floor.isLiquid){ + Draw.color(Color.WHITE, floor.color, drownTime); + }else{ + Draw.color(Color.WHITE); + } + + Draw.rect(mech.region, x, y, rotation - 90); + + mech.draw(this); + + for(int i : Mathf.signs){ + float tra = rotation - 90, trY = -mech.weapon.getRecoil(this, i > 0) + mech.weaponOffsetY; + float w = i > 0 ? -mech.weapon.region.getWidth() : mech.weapon.region.getWidth(); + Draw.rect(mech.weapon.region, + x + Angles.trnsx(tra, (mech.weaponOffsetX + mech.spreadX(this)) * i, trY), + y + Angles.trnsy(tra, (mech.weaponOffsetX + mech.spreadX(this)) * i, trY), + w * Draw.scl, + mech.weapon.region.getHeight() * Draw.scl, + rotation - 90); + } + + float backTrns = 4f; + if(item.amount > 0){ + ItemStack stack = item; + int stored = Mathf.clamp(stack.amount / 6, 1, 8); + + for(int i = 0; i < stored; i++){ + float angT = i == 0 ? 0 : Mathf.randomSeedRange(i + 1, 60f); + float lenT = i == 0 ? 0 : Mathf.randomSeedRange(i + 2, 1f) - 1f; + Draw.rect(stack.item.icon(Item.Icon.large), + x + Angles.trnsx(rotation + 180f + angT, backTrns + lenT), + y + Angles.trnsy(rotation + 180f + angT, backTrns + lenT), + itemSize, itemSize, rotation); + } + } + + Draw.reset(); + } + + @Override + public void drawStats(){ + Draw.color(Color.BLACK, team.color, healthf() + Mathf.absin(Time.time(), healthf() * 5f, 1f - healthf())); + Draw.rect(getPowerCellRegion(), x + Angles.trnsx(rotation, mech.cellTrnsY, 0f), y + Angles.trnsy(rotation, mech.cellTrnsY, 0f), rotation - 90); + Draw.color(); + } + + @Override + public void drawOver(){ + if(dead) return; + + drawMechanics(); + } + + @Override + public void drawUnder(){ + if(dead) return; + + float size = mech.engineSize * (mech.flying ? 1f : boostHeat); + Draw.color(mech.engineColor); + Fill.circle(x + Angles.trnsx(rotation + 180, mech.engineOffset), y + Angles.trnsy(rotation + 180, mech.engineOffset), + size + Mathf.absin(Time.time(), 2f, size / 4f)); + + Draw.color(Color.WHITE); + Fill.circle(x + Angles.trnsx(rotation + 180, mech.engineOffset - 1f), y + Angles.trnsy(rotation + 180, mech.engineOffset - 1f), + (size + Mathf.absin(Time.time(), 2f, size / 4f)) / 2f); + Draw.color(); + } + + public void drawName(){ + BitmapFont font = Core.scene.skin.getFont("default-font"); + GlyphLayout layout = Pools.obtain(GlyphLayout.class, GlyphLayout::new); + final float nameHeight = 11; + final float textHeight = 15; + + boolean ints = font.usesIntegerPositions(); + font.setUseIntegerPositions(false); + font.getData().setScale(0.25f / io.anuke.arc.scene.ui.layout.Unit.dp.scl(1f)); + layout.setText(font, name); + Draw.color(0f, 0f, 0f, 0.3f); + Fill.rect(x, y + nameHeight - layout.height / 2, layout.width + 2, layout.height + 3); + Draw.color(); + font.setColor(color); + + font.draw(name, x, y + nameHeight, 0, Align.center, false); + + if(isAdmin){ + float s = 3f; + Draw.color(color.r * 0.5f, color.g * 0.5f, color.b * 0.5f, 1f); + Draw.rect(Core.atlas.find("icon-admin-small"), x + layout.width / 2f + 2 + 1, y + nameHeight - 1.5f, s, s); + Draw.color(color); + Draw.rect(Core.atlas.find("icon-admin-small"), x + layout.width / 2f + 2 + 1, y + nameHeight - 1f, s, s); + } + + if(Core.settings.getBool("playerchat") && ((textFadeTime > 0 && lastText != null) || isTyping)){ + String text = textFadeTime <= 0 || lastText == null ? "[LIGHT_GRAY]" + Strings.animated(Time.time(), 4, 15f, ".") : lastText; + float width = 100f; + float visualFadeTime = 1f - Mathf.curve(1f - textFadeTime, 0.9f); + font.setColor(1f, 1f, 1f, textFadeTime <= 0 || lastText == null ? 1f : visualFadeTime); + + layout.setText(font, text, Color.WHITE, width, Align.bottom, true); + + Draw.color(0f, 0f, 0f, 0.3f * (textFadeTime <= 0 || lastText == null ? 1f : visualFadeTime)); + Fill.rect(x, y + textHeight + layout.height - layout.height/2f, layout.width + 2, layout.height + 3); + font.draw(text, x - width/2f, y + textHeight + layout.height, width, Align.center, true); + } + + Draw.reset(); + Pools.free(layout); + font.getData().setScale(1f); + font.setColor(Color.WHITE); + font.setUseIntegerPositions(ints); + } + + /** Draw all current build requests. Does not draw the beam effect, only the positions. */ + public void drawBuildRequests(){ + BuildRequest last = null; + for(BuildRequest request : buildQueue()){ + if(request.progress > 0.01f || (buildRequest() == request && (dst(request.x * tilesize, request.y * tilesize) <= placeDistance || state.isEditor()))) continue; + + if(request.breaking){ + Block block = world.ltile(request.x, request.y).block(); + + //draw removal request + Lines.stroke(2f, Pal.removeBack); + + float rad = Mathf.absin(Time.time(), 7f, 1f) + block.size * tilesize / 2f - 1; + Lines.square( + request.x * tilesize + block.offset(), + request.y * tilesize + block.offset() - 1, + rad); + + Draw.color(Pal.remove); + + Lines.square( + request.x * tilesize + block.offset(), + request.y * tilesize + block.offset(), rad); + }else{ + Draw.color(); + PlaceDraw draw = PlaceDraw.instance; + + draw.scalex = 1; + draw.scaley = 1; + draw.rotation = request.rotation; + + if(last == null){ + request.block.getPlaceDraw(draw, request.rotation, request.x, request.y, request.rotation); + }else{ + request.block.getPlaceDraw(draw, request.rotation, last.x - request.x, last.y - request.y, last.rotation); + } + + TextureRegion region = draw.region; + + Draw.rect(region, + request.x * tilesize + request.block.offset(), request.y * tilesize + request.block.offset(), + region.getWidth() * 1f * Draw.scl * draw.scalex, + region.getHeight() * 1f * Draw.scl * draw.scaley, request.block.rotate ? draw.rotation * 90 : 0); + + Draw.color(Pal.accent); + for(int i = 0; i < 4; i++){ + Point2 p = Geometry.d8edge[i]; + float offset = -Math.max(request.block.size - 1, 0) / 2f * tilesize; + if(i % 2 == 0) + Draw.rect("block-select", request.x * tilesize + request.block.offset() + offset * p.x, request.y * tilesize + request.block.offset() + offset * p.y, i * 90); + } + Draw.color(); + + last = request; + } + } + + Draw.reset(); + } + + //endregion + + //region update methods + + @Override + public void update(){ + hitTime -= Time.delta(); + textFadeTime -= Time.delta() / (60 * 5); + + if(Float.isNaN(x) || Float.isNaN(y)){ + velocity.set(0f, 0f); + x = 0; + y = 0; + setDead(true); + } + + if(netServer.isWaitingForPlayers()){ + setDead(true); + } + + if(!isDead() && isOutOfBounds()){ + destructTime += Time.delta(); + + if(destructTime >= boundsCountdown){ + kill(); + } + }else{ + destructTime = 0f; + } + + if(isDead()){ + isBoosting = false; + boostHeat = 0f; + if(respawns > 0 || !state.rules.limitedRespawns){ + updateRespawning(); + } + return; + }else{ + spawner = null; + } + + avoidOthers(); + + Tile tile = world.tileWorld(x, y); + + boostHeat = Mathf.lerpDelta(boostHeat, (tile != null && tile.solid()) || (isBoosting && ((!movement.isZero() && moved) || !isLocal)) ? 1f : 0f, 0.08f); + shootHeat = Mathf.lerpDelta(shootHeat, isShooting() ? 1f : 0f, 0.06f); + mech.updateAlt(this); //updated regardless + + if(boostHeat > liftoffBoost + 0.1f){ + achievedFlight = true; + } + + if(boostHeat <= liftoffBoost + 0.05f && achievedFlight && !mech.flying){ + if(tile != null){ + if(mech.shake > 1f){ + Effects.shake(mech.shake, mech.shake, this); + } + Effects.effect(Fx.unitLand, tile.floor().color, x, y, tile.floor().isLiquid ? 1f : 0.5f); + } + mech.onLand(this); + achievedFlight = false; + } + + if(!isLocal){ + interpolate(); + updateMechanics(); //building happens even with non-locals + status.update(this); //status effect updating also happens with non locals for effect purposes + updateVelocityStatus(); //velocity too, for visual purposes + + if(Net.server()){ + updateShooting(); //server simulates player shooting + } + return; + }else if(world.isZone()){ + //unlock mech when used + data.unlockContent(mech); + } + + if(mobile){ + updateFlying(); + }else{ + updateMech(); + } + + isTyping = ui.chatfrag.chatOpen(); + + updateMechanics(); + + if(!mech.flying){ + clampPosition(); + } + } + + protected void updateMech(){ + Tile tile = world.tileWorld(x, y); + + isBoosting = Core.input.keyDown(Binding.dash) && !mech.flying; + + //if player is in solid block + if(tile != null && tile.solid()){ + isBoosting = true; + } + + float speed = isBoosting && !mech.flying ? mech.boostSpeed : mech.speed; + + if(mech.flying){ + //prevent strafing backwards, have a penalty for doing so + float penalty = 0.2f; //when going 180 degrees backwards, reduce speed to 0.2x + speed *= Mathf.lerp(1f, penalty, Angles.angleDist(rotation, velocity.angle()) / 180f); + } + + movement.setZero(); + + float xa = Core.input.axis(Binding.move_x); + float ya = Core.input.axis(Binding.move_y); + if(!Core.input.keyDown(Binding.gridMode)){ + movement.y += ya * speed; + movement.x += xa * speed; + } + + Vector2 vec = Core.input.mouseWorld(control.input().getMouseX(), control.input().getMouseY()); + pointerX = vec.x; + pointerY = vec.y; + updateShooting(); + + movement.limit(speed).scl(Time.delta()); + + if(!ui.chatfrag.chatOpen()){ + velocity.add(movement.x, movement.y); + }else{ + isShooting = false; + } + float prex = x, prey = y; + updateVelocityStatus(); + moved = dst(prex, prey) > 0.001f; + + if(!ui.chatfrag.chatOpen()){ + float baseLerp = mech.getRotationAlpha(this); + if(!isShooting() || !mech.turnCursor){ + if(!movement.isZero()){ + rotation = Mathf.slerpDelta(rotation, mech.flying ? velocity.angle() : movement.angle(), 0.13f * baseLerp); + } + }else{ + float angle = control.input().mouseAngle(x, y); + this.rotation = Mathf.slerpDelta(this.rotation, angle, 0.1f * baseLerp); + } + } + } + + protected void updateShooting(){ + if(!state.isEditor() && isShooting() && mech.canShoot(this)){ + if(!mech.turnCursor){ + //shoot forward ignoring cursor + mech.weapon.update(this, x + Angles.trnsx(rotation, 1f), y + Angles.trnsy(rotation, 1f)); + }else{ + mech.weapon.update(this, pointerX, pointerY); + } + } + } + + protected void updateFlying(){ + if(Units.invalidateTarget(target, this) && !(target instanceof TileEntity && ((TileEntity)target).damaged() && target.isValid() && target.getTeam() == team && mech.canHeal && dst(target) < getWeapon().bullet.range())){ + target = null; + } + + if(state.isEditor()){ + target = null; + } + + float targetX = Core.camera.position.x, targetY = Core.camera.position.y; + float attractDst = 15f; + + if(moveTarget != null && !moveTarget.isDead()){ + targetX = moveTarget.getX(); + targetY = moveTarget.getY(); + boolean tapping = moveTarget instanceof TileEntity && moveTarget.getTeam() == team; + attractDst = 0f; + + if(tapping){ + velocity.setAngle(angleTo(moveTarget)); + } + + if(dst(moveTarget) <= 2f * Time.delta()){ + if(tapping && !isDead()){ + Tile tile = ((TileEntity)moveTarget).tile; + tile.block().tapped(tile, this); + } + + moveTarget = null; + } + }else{ + moveTarget = null; + } + + movement.set((targetX - x) / Time.delta(), (targetY - y) / Time.delta()).limit(isBoosting && !mech.flying ? mech.boostSpeed : mech.speed); + movement.setAngle(Mathf.slerp(movement.angle(), velocity.angle(), 0.05f)); + + if(dst(targetX, targetY) < attractDst){ + movement.setZero(); + } + + float expansion = 3f; + + hitbox(rect); + rect.x -= expansion; + rect.y -= expansion; + rect.width += expansion * 2f; + rect.height += expansion * 2f; + + isBoosting = collisions.overlapsTile(rect) || dst(targetX, targetY) > 85f; + + velocity.add(movement.scl(Time.delta())); + + if(velocity.len() <= 0.2f && mech.flying){ + rotation += Mathf.sin(Time.time() + id * 99, 10f, 1f); + }else if(target == null){ + rotation = Mathf.slerpDelta(rotation, velocity.angle(), velocity.len() / 10f); + } + + float lx = x, ly = y; + updateVelocityStatus(); + moved = dst(lx, ly) > 0.001f; + + if(mech.flying){ + //hovering effect + x += Mathf.sin(Time.time() + id * 999, 25f, 0.08f); + y += Mathf.cos(Time.time() + id * 999, 25f, 0.08f); + } + + //update shooting if not building, not mining and there's ammo left + if(!isBuilding() && getMineTile() == null){ + + //autofire: mobile only! + if(mobile){ + if(target == null){ + isShooting = false; + if(Core.settings.getBool("autotarget")){ + target = Units.closestTarget(team, x, y, getWeapon().bullet.range(), u -> u.getTeam() != Team.none, u -> u.getTeam() != Team.none); + + if(mech.canHeal && target == null){ + target = Geometry.findClosest(x, y, world.indexer.getDamaged(Team.blue)); + if(target != null && dst(target) > getWeapon().bullet.range()){ + target = null; + }else if(target != null){ + target = ((Tile)target).entity; + } + } + + if(target != null){ + setMineTile(null); + } + } + }else if(target.isValid() || (target instanceof TileEntity && ((TileEntity)target).damaged() && target.getTeam() == team && + mech.canHeal && dst(target) < getWeapon().bullet.range())){ + //rotate toward and shoot the target + if(mech.turnCursor){ + rotation = Mathf.slerpDelta(rotation, angleTo(target), 0.2f); + } + + Vector2 intercept = + Predict.intercept(x, y, target.getX(), target.getY(), target.velocity().x - velocity.x, target.velocity().y - velocity.y, getWeapon().bullet.speed); + + pointerX = intercept.x; + pointerY = intercept.y; + + updateShooting(); + isShooting = true; + } + + }else if(isShooting()){ + Vector2 vec = Core.input.mouseWorld(control.input().getMouseX(), + control.input().getMouseY()); + pointerX = vec.x; + pointerY = vec.y; + + updateShooting(); + } + } + } + + //endregion + + //region utility methods + + /** Resets all values of the player. */ + public void reset(){ + resetNoAdd(); + + add(); + } + + public void resetNoAdd(){ + status.clear(); + team = Team.blue; + item.amount = 0; + placeQueue.clear(); + dead = true; + target = null; + moveTarget = null; + spawner = lastSpawner = null; + health = maxHealth(); + mining = null; + boostHeat = drownTime = hitTime = 0f; + mech = Mechs.starter; + placeQueue.clear(); + respawns = state.rules.respawns; + } + + public boolean isShooting(){ + return isShooting && (!isBoosting || mech.flying) && mining == null; + } + + public void updateRespawning(){ + + if(state.isEditor()){ + //instant respawn at center of map. + set(world.width() * tilesize/2f, world.height() * tilesize/2f); + setDead(false); + }else if(spawner != null && spawner.isValid()){ + spawner.updateSpawning(this); + }else if(!netServer.isWaitingForPlayers()){ + if(!Net.client()){ + if(lastSpawner != null && lastSpawner.isValid()){ + this.spawner = lastSpawner; + }else if(getClosestCore() != null){ + this.spawner = (SpawnerTrait)getClosestCore(); + } + } + }else if(getClosestCore() != null){ + set(getClosestCore().getX(), getClosestCore().getY()); + } + } + + public void beginRespawning(SpawnerTrait spawner){ + this.spawner = spawner; + this.lastSpawner = spawner; + this.dead = true; + setNet(spawner.getX(), spawner.getY()); + spawner.updateSpawning(this); + } + + //endregion + + //region read and write methods + + @Override + public byte version(){ + return 0; + } + + @Override + public void writeSave(DataOutput stream) throws IOException{ + stream.writeBoolean(isLocal); + + if(isLocal){ + stream.writeByte(mech.id); + stream.writeInt(lastSpawner == null ? noSpawner : lastSpawner.getTile().pos()); + super.writeSave(stream, false); + } + } + + @Override + public void readSave(DataInput stream, byte version) throws IOException{ + boolean local = stream.readBoolean(); + + if(local){ + byte mechid = stream.readByte(); + int spawner = stream.readInt(); + Tile stile = world.tile(spawner); + Player player = headless ? this : Vars.player; + player.readSaveSuper(stream, version); + player.mech = content.getByID(ContentType.mech, mechid); + player.dead = false; + if(stile != null && stile.entity instanceof SpawnerTrait){ + player.lastSpawner = (SpawnerTrait)stile.entity; + } + } + } + + private void readSaveSuper(DataInput stream, byte version) throws IOException{ + super.readSave(stream, version); + + add(); + } + + @Override + public void write(DataOutput buffer) throws IOException{ + super.writeSave(buffer, !isLocal); + TypeIO.writeStringData(buffer, name); + buffer.writeByte(Pack.byteValue(isAdmin) | (Pack.byteValue(dead) << 1) | (Pack.byteValue(isBoosting) << 2) | (Pack.byteValue(isTyping) << 3)); + buffer.writeInt(Color.rgba8888(color)); + buffer.writeByte(mech.id); + buffer.writeInt(mining == null ? noSpawner : mining.pos()); + buffer.writeInt(spawner == null ? noSpawner : spawner.getTile().pos()); + buffer.writeShort((short)(baseRotation * 2)); + + writeBuilding(buffer); + } + + @Override + public void read(DataInput buffer) throws IOException{ + float lastx = x, lasty = y, lastrot = rotation, lastvx = velocity.x, lastvy = velocity.y; + + super.readSave(buffer, version()); + + name = TypeIO.readStringData(buffer); + byte bools = buffer.readByte(); + isAdmin = (bools & 1) != 0; + dead = (bools & 2) != 0; + boolean boosting = (bools & 4) != 0; + isTyping = (bools & 8) != 0; + color.set(buffer.readInt()); + mech = content.getByID(ContentType.mech, buffer.readByte()); + int mine = buffer.readInt(); + int spawner = buffer.readInt(); + float baseRotation = buffer.readShort() / 2f; + + readBuilding(buffer, !isLocal); + + interpolator.read(lastx, lasty, x, y, rotation, baseRotation); + rotation = lastrot; + x = lastx; + y = lasty; + + if(isLocal){ + velocity.x = lastvx; + velocity.y = lastvy; + }else{ + mining = world.tile(mine); + isBoosting = boosting; + } + + Tile tile = world.tile(spawner); + if(tile != null && tile.entity instanceof SpawnerTrait){ + this.spawner = (SpawnerTrait)tile.entity; + }else{ + this.spawner = null; + } + } + + //endregion +} diff --git a/core/src/io/anuke/mindustry/entities/type/TileEntity.java b/core/src/io/anuke/mindustry/entities/type/TileEntity.java new file mode 100644 index 0000000000..c7793c5895 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/type/TileEntity.java @@ -0,0 +1,316 @@ +package io.anuke.mindustry.entities.type; + +import io.anuke.annotations.Annotations.*; +import io.anuke.arc.Events; +import io.anuke.arc.collection.Array; +import io.anuke.arc.collection.ObjectSet; +import io.anuke.arc.math.geom.Point2; +import io.anuke.arc.math.geom.Vector2; +import io.anuke.arc.util.*; +import io.anuke.mindustry.entities.EntityGroup; +import io.anuke.mindustry.entities.bullet.Bullet; +import io.anuke.mindustry.entities.impl.BaseEntity; +import io.anuke.mindustry.entities.traits.HealthTrait; +import io.anuke.mindustry.entities.traits.TargetTrait; +import io.anuke.mindustry.game.EventType.BlockDestroyEvent; +import io.anuke.mindustry.game.Team; +import io.anuke.mindustry.gen.Call; +import io.anuke.mindustry.world.*; +import io.anuke.mindustry.world.modules.*; + +import java.io.*; + +import static io.anuke.mindustry.Vars.tileGroup; +import static io.anuke.mindustry.Vars.world; + +public class TileEntity extends BaseEntity implements TargetTrait, HealthTrait{ + public static final float timeToSleep = 60f * 4; //4 seconds to fall asleep + private static final ObjectSet tmpTiles = new ObjectSet<>(); + /** This value is only used for debugging. */ + public static int sleepingEntities = 0; + + public Tile tile; + public Block block; + public Interval timer; + public float health; + public float timeScale = 1f, timeScaleDuration; + + public PowerModule power; + public ItemModule items; + public LiquidModule liquids; + public ConsumeModule cons; + + /** List of (cached) tiles with entities in proximity, used for outputting to */ + private Array proximity = new Array<>(8); + private boolean dead = false; + private boolean sleeping; + private float sleepTime; + + @Remote(called = Loc.server, unreliable = true) + public static void onTileDamage(Tile tile, float health){ + if(tile.entity != null){ + tile.entity.health = health; + + if(tile.entity.damaged()){ + world.indexer.notifyTileDamaged(tile.entity); + } + } + } + + @Remote(called = Loc.server) + public static void onTileDestroyed(Tile tile){ + if(tile.entity == null) return; + tile.entity.onDeath(); + } + + /** Sets this tile entity data to this tile, and adds it if necessary. */ + public TileEntity init(Tile tile, boolean shouldAdd){ + this.tile = tile; + x = tile.drawx(); + y = tile.drawy(); + block = tile.block(); + + health = block.health; + timer = new Interval(block.timers); + + if(shouldAdd){ + add(); + } + + return this; + } + + /** Scaled delta. */ + public float delta(){ + return Time.delta() * timeScale; + } + + /** Call when nothing is happening to the entity. This increments the internal sleep timer. */ + public void sleep(){ + sleepTime += Time.delta(); + if(!sleeping && sleepTime >= timeToSleep){ + remove(); + sleeping = true; + sleepingEntities++; + } + } + + /** Call when this entity is updating. This wakes it up. */ + public void noSleep(){ + sleepTime = 0f; + if(sleeping){ + add(); + sleeping = false; + sleepingEntities--; + } + } + + public boolean isSleeping(){ + return sleeping; + } + + public boolean isDead(){ + return dead || tile.entity != this; + } + + @CallSuper + public void write(DataOutput stream) throws IOException{ + stream.writeShort((short)health); + stream.writeByte(Pack.byteByte(tile.getTeamID(), tile.rotation())); //team + rotation + if(items != null) items.write(stream); + if(power != null) power.write(stream); + if(liquids != null) liquids.write(stream); + if(cons != null) cons.write(stream); + } + + @CallSuper + public void read(DataInput stream, byte revision) throws IOException{ + health = stream.readUnsignedShort(); + byte tr = stream.readByte(); + byte team = Pack.leftByte(tr); + byte rotation = Pack.rightByte(tr); + + tile.setTeam(Team.all[team]); + tile.rotation(rotation); + + if(items != null) items.read(stream); + if(power != null) power.read(stream); + if(liquids != null) liquids.read(stream); + if(cons != null) cons.read(stream); + } + + /** Returns the version of this TileEntity IO code.*/ + public byte version(){ + return 0; + } + + public boolean collide(Bullet other){ + return true; + } + + public void collision(Bullet other){ + block.handleBulletHit(this, other); + } + + public void kill(){ + Call.onTileDestroyed(tile); + } + + public void damage(float damage){ + if(dead) return; + + float preHealth = health; + + Call.onTileDamage(tile, health - block.handleDamage(tile, damage)); + + if(health <= 0){ + Call.onTileDestroyed(tile); + } + + if(preHealth >= maxHealth() - 0.00001f && health < maxHealth() && world != null){ //when just damaged + world.indexer.notifyTileDamaged(this); + } + } + + public boolean damaged(){ + return health < maxHealth() - 0.00001f; + } + + public Tile getTile(){ + return tile; + } + + public void removeFromProximity(){ + block.onProximityRemoved(tile); + + Point2[] nearby = Edges.getEdges(block.size); + for(Point2 point : nearby){ + Tile other = world.ltile(tile.x + point.x, tile.y + point.y); + //remove this tile from all nearby tile's proximities + if(other != null){ + other.block().onProximityUpdate(other); + + if(other.entity != null){ + other.entity.proximity.removeValue(tile, true); + } + } + } + } + + public void updateProximity(){ + tmpTiles.clear(); + proximity.clear(); + + Point2[] nearby = Edges.getEdges(block.size); + for(Point2 point : nearby){ + Tile other = world.ltile(tile.x + point.x, tile.y + point.y); + + if(other == null) continue; + if(other.entity == null || !(other.interactable(tile.getTeam()))) continue; + + other.block().onProximityUpdate(other); + + tmpTiles.add(other); + + //add this tile to proximity of nearby tiles + if(!other.entity.proximity.contains(tile, true)){ + other.entity.proximity.add(tile); + } + } + + //using a set to prevent duplicates + for(Tile tile : tmpTiles){ + proximity.add(tile); + } + + block.onProximityAdded(tile); + block.onProximityUpdate(tile); + } + + public Array proximity(){ + return proximity; + } + + @Override + public void health(float health){ + this.health = health; + } + + @Override + public float health(){ + return health; + } + + @Override + public float maxHealth(){ + return block.health; + } + + @Override + public void setDead(boolean dead){ + this.dead = dead; + } + + @Override + public void onDeath(){ + if(!dead){ + dead = true; + + Events.fire(new BlockDestroyEvent(tile)); + block.onDestroyed(tile); + world.removeBlock(tile); + remove(); + } + } + + @Override + public Team getTeam(){ + return tile.getTeam(); + } + + @Override + public Vector2 velocity(){ + return Vector2.ZERO; + } + + @Override + public void update(){ + timeScaleDuration -= Time.delta(); + if(timeScaleDuration <= 0f || !block.canOverdrive){ + timeScale = 1f; + } + + if(health <= 0){ + onDeath(); + return; //no need to update anymore + } + + Block previous = block; + block.update(tile); + if(block == previous && cons != null){ + cons.update(); + } + + if(block == previous && power != null){ + power.graph.update(); + } + } + + @Override + public boolean isValid(){ + return !isDead() && tile.entity == this; + } + + @Override + public EntityGroup targetGroup(){ + return tileGroup; + } + + @Override + public String toString(){ + return "TileEntity{" + + "tile=" + tile + + ", health=" + health + + '}'; + } +} diff --git a/core/src/io/anuke/mindustry/entities/type/Unit.java b/core/src/io/anuke/mindustry/entities/type/Unit.java new file mode 100644 index 0000000000..b36e8dde83 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/type/Unit.java @@ -0,0 +1,411 @@ +package io.anuke.mindustry.entities.type; + +import io.anuke.annotations.Annotations.Nullable; +import io.anuke.arc.Core; +import io.anuke.arc.Events; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.graphics.g2d.Draw; +import io.anuke.arc.graphics.g2d.TextureRegion; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.Geometry; +import io.anuke.arc.math.geom.Vector2; +import io.anuke.arc.util.*; +import io.anuke.mindustry.content.Blocks; +import io.anuke.mindustry.content.Fx; +import io.anuke.mindustry.entities.*; +import io.anuke.mindustry.entities.effect.ScorchDecal; +import io.anuke.mindustry.entities.impl.DestructibleEntity; +import io.anuke.mindustry.entities.traits.*; +import io.anuke.mindustry.entities.units.Statuses; +import io.anuke.mindustry.game.EventType.UnitDestroyEvent; +import io.anuke.mindustry.game.Team; +import io.anuke.mindustry.game.Teams.TeamData; +import io.anuke.mindustry.graphics.Pal; +import io.anuke.mindustry.net.Interpolator; +import io.anuke.mindustry.net.Net; +import io.anuke.mindustry.type.*; +import io.anuke.mindustry.world.Pos; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.Floor; + +import java.io.*; + +import static io.anuke.mindustry.Vars.*; + +public abstract class Unit extends DestructibleEntity implements SaveTrait, TargetTrait, SyncTrait, DrawTrait, TeamTrait{ + /** Total duration of hit flash effect */ + public static final float hitDuration = 9f; + /** Percision divisor of velocity, used when writing. For example a value of '2' would mean the percision is 1/2 = 0.5-size chunks. */ + public static final float velocityPercision = 8f; + /** Maximum absolute value of a velocity vector component. */ + public static final float maxAbsVelocity = 127f / velocityPercision; + public static final int noSpawner = Pos.get(-1, 1); + + private static final Vector2 moveVector = new Vector2(); + + public float rotation; + + protected final Interpolator interpolator = new Interpolator(); + protected final Statuses status = new Statuses(); + protected final ItemStack item = new ItemStack(content.item(0), 0); + + protected Team team = Team.blue; + protected float drownTime, hitTime; + + @Override + public boolean collidesGrid(int x, int y){ + return !isFlying(); + } + + @Override + public Team getTeam(){ + return team; + } + + @Override + public void interpolate(){ + interpolator.update(); + + x = interpolator.pos.x; + y = interpolator.pos.y; + + if(interpolator.values.length > 0){ + rotation = interpolator.values[0]; + } + } + + @Override + public Interpolator getInterpolator(){ + return interpolator; + } + + @Override + public void damage(float amount){ + if(!Net.client()){ + super.damage(calculateDamage(amount)); + } + hitTime = hitDuration; + } + + @Override + public boolean collides(SolidTrait other){ + if(isDead()) return false; + + if(other instanceof DamageTrait){ + return other instanceof TeamTrait && state.teams.areEnemies((((TeamTrait)other).getTeam()), team); + }else{ + return other instanceof Unit && ((Unit)other).isFlying() == isFlying(); + } + } + + @Override + public void onDeath(){ + float explosiveness = 2f + item.item.explosiveness * item.amount; + float flammability = item.item.flammability * item.amount; + Damage.dynamicExplosion(x, y, flammability, explosiveness, 0f, getSize() / 2f, Pal.darkFlame); + + ScorchDecal.create(x, y); + Effects.effect(Fx.explosion, this); + Effects.shake(2f, 2f, this); + + item.amount = 0; + drownTime = 0f; + status.clear(); + Events.fire(new UnitDestroyEvent(this)); + } + + @Override + public Vector2 velocity(){ + return velocity; + } + + @Override + public void move(float x, float y){ + if(!isFlying()){ + super.move(x, y); + }else{ + moveBy(x, y); + } + } + + @Override + public boolean isValid(){ + return !isDead() && isAdded(); + } + + @Override + public void writeSave(DataOutput stream) throws IOException{ + writeSave(stream, false); + } + + @Override + public void readSave(DataInput stream, byte version) throws IOException{ + byte team = stream.readByte(); + boolean dead = stream.readBoolean(); + float x = stream.readFloat(); + float y = stream.readFloat(); + byte xv = stream.readByte(); + byte yv = stream.readByte(); + float rotation = stream.readShort() / 2f; + int health = stream.readShort(); + byte itemID = stream.readByte(); + short itemAmount = stream.readShort(); + + this.status.readSave(stream, version); + this.item.amount = itemAmount; + this.item.item = content.item(itemID); + this.dead = dead; + this.team = Team.all[team]; + this.health = health; + this.x = x; + this.y = y; + this.velocity.set(xv / velocityPercision, yv / velocityPercision); + this.rotation = rotation; + } + + public void writeSave(DataOutput stream, boolean net) throws IOException{ + stream.writeByte(team.ordinal()); + stream.writeBoolean(isDead()); + stream.writeFloat(net ? interpolator.target.x : x); + stream.writeFloat(net ? interpolator.target.y : y); + stream.writeByte((byte)(Mathf.clamp(velocity.x, -maxAbsVelocity, maxAbsVelocity) * velocityPercision)); + stream.writeByte((byte)(Mathf.clamp(velocity.y, -maxAbsVelocity, maxAbsVelocity) * velocityPercision)); + stream.writeShort((short)(rotation * 2)); + stream.writeShort((short)health); + stream.writeByte(item.item.id); + stream.writeShort((short)item.amount); + status.writeSave(stream); + } + + protected void clampPosition(){ + x = Mathf.clamp(x, 0, world.width() * tilesize - tilesize); + y = Mathf.clamp(y, 0, world.height() * tilesize - tilesize); + } + + public void kill(){ + health = -1; + damage(1); + } + + public boolean isImmune(StatusEffect effect){ + return false; + } + + public boolean isOutOfBounds(){ + return x < -worldBounds || y < -worldBounds || x > world.width() * tilesize + worldBounds || y > world.height() * tilesize + worldBounds; + } + + public float calculateDamage(float amount){ + return amount * Mathf.clamp(1f - status.getArmorMultiplier() / 100f); + } + + public float getDamageMultipler(){ + return status.getDamageMultiplier(); + } + + public boolean hasEffect(StatusEffect effect){ + return status.hasEffect(effect); + } + + public void avoidOthers(){ + float radScl = 1.5f; + float fsize = getSize() / radScl; + moveVector.setZero(); + + Units.nearby(x - fsize/2f, y - fsize/2f, fsize, fsize, en -> { + if(en == this || en.isFlying() != isFlying()) return; + float dst = dst(en); + float scl = Mathf.clamp(1f - dst / (getSize()/(radScl*2f) + en.getSize()/(radScl*2f))); + moveVector.add(Tmp.v1.set((x - en.x) * scl, (y - en.y) * scl).limit(0.4f)); + }); + + velocity.add(moveVector.x / mass() * Time.delta(), moveVector.y / mass() * Time.delta()); + } + + public @Nullable TileEntity getClosestCore(){ + TeamData data = state.teams.get(team); + + Tile tile = Geometry.findClosest(x, y, data.cores); + if(tile == null){ + return null; + }else{ + return tile.entity; + } + } + + public Floor getFloorOn(){ + Tile tile = world.tileWorld(x, y); + return tile == null ? (Floor)Blocks.air : tile.floor(); + } + + public void onRespawn(Tile tile){ + } + + /** Updates velocity and status effects. */ + public void updateVelocityStatus(){ + Floor floor = getFloorOn(); + + Tile tile = world.tileWorld(x, y); + + status.update(this); + + velocity.limit(maxVelocity()).scl(1f + (status.getSpeedMultiplier() - 1f) * Time.delta()); + + if(x < -finalWorldBounds || y < -finalWorldBounds || x >= world.width() * tilesize + finalWorldBounds || y >= world.height() * tilesize + finalWorldBounds){ + kill(); + } + + //apply knockback based on spawns + if(getTeam() != waveTeam){ + float relativeSize = state.rules.dropZoneRadius + getSize()/2f + 1f; + for(Tile spawn : world.spawner.getGroundSpawns()){ + if(withinDst(spawn.worldx(), spawn.worldy(), relativeSize)){ + velocity.add(Tmp.v1.set(this).sub(spawn.worldx(), spawn.worldy()).setLength(0.1f + 1f - dst(spawn) / relativeSize).scl(0.45f * Time.delta())); + } + } + } + + //repel player out of bounds + final float warpDst = 180f; + + if(x < 0) velocity.x += (-x/warpDst); + if(y < 0) velocity.y += (-y/warpDst); + if(x > world.unitWidth()) velocity.x -= (x - world.unitWidth())/warpDst; + if(y > world.unitHeight()) velocity.y -= (y - world.unitHeight())/warpDst; + + + if(isFlying()){ + drownTime = 0f; + move(velocity.x * Time.delta(), velocity.y * Time.delta()); + }else{ + boolean onLiquid = floor.isLiquid; + + if(tile != null){ + tile.block().unitOn(tile, this); + if(tile.block() != Blocks.air){ + onLiquid = false; + } + } + + if(onLiquid && velocity.len() > 0.4f && Mathf.chance((velocity.len() * floor.speedMultiplier) * 0.06f * Time.delta())){ + Effects.effect(floor.walkEffect, floor.color, x, y); + } + + if(onLiquid){ + status.handleApply(this, floor.status, floor.statusDuration); + + if(floor.damageTaken > 0f){ + damagePeriodic(floor.damageTaken); + } + } + + if(onLiquid && floor.drownTime > 0){ + drownTime += Time.delta() * 1f / floor.drownTime; + if(Mathf.chance(Time.delta() * 0.05f)){ + Effects.effect(floor.drownUpdateEffect, floor.color, x, y); + } + }else{ + drownTime = Mathf.lerpDelta(drownTime, 0f, 0.03f); + } + + drownTime = Mathf.clamp(drownTime); + + if(drownTime >= 0.999f && !Net.client()){ + damage(health + 1); + } + + float px = x, py = y; + move(velocity.x * floor.speedMultiplier * Time.delta(), velocity.y * floor.speedMultiplier * Time.delta()); + if(Math.abs(px - x) <= 0.0001f) velocity.x = 0f; + if(Math.abs(py - y) <= 0.0001f) velocity.y = 0f; + } + + velocity.scl(Mathf.clamp(1f - drag() * (isFlying() ? 1f : floor.dragMultiplier) * Time.delta())); + } + + public boolean acceptsItem(Item item){ + return this.item.amount <= 0 || (this.item.item == item && this.item.amount <= getItemCapacity()); + } + + public void addItem(Item item){ + addItem(item, 1); + } + + public void addItem(Item item, int amount){ + this.item.amount = this.item.item == item ? this.item.amount + amount : amount; + this.item.item = item; + } + + public void clearItem(){ + item.amount = 0; + } + + public ItemStack item(){ + return item; + } + + public int maxAccepted(Item item){ + return this.item.item != item && this.item.amount > 0 ? 0 : getItemCapacity() - this.item.amount; + } + + public void applyEffect(StatusEffect effect, float duration){ + if(dead || Net.client()) return; //effects are synced and thus not applied through clients + status.handleApply(this, effect, duration); + } + + public void damagePeriodic(float amount){ + damage(amount * Time.delta(), hitTime <= -20 + hitDuration); + } + + public void damage(float amount, boolean withEffect){ + float pre = hitTime; + + damage(amount); + + if(!withEffect){ + hitTime = pre; + } + } + + public void drawUnder(){ + } + + public void drawOver(){ + } + + public void drawStats(){ + Draw.color(Color.BLACK, team.color, healthf() + Mathf.absin(Time.time(), Math.max(healthf() * 5f, 1f), 1f - healthf())); + Draw.rect(getPowerCellRegion(), x, y, rotation - 90); + Draw.color(); + } + + public TextureRegion getPowerCellRegion(){ + return Core.atlas.find("power-cell"); + } + + public void drawAll(){ + if(!isDead()){ + draw(); + drawStats(); + } + } + + public void drawShadow(float offsetX, float offsetY){ + Draw.rect(getIconRegion(), x + offsetX, y + offsetY, rotation - 90); + } + + public float getSize(){ + hitbox(Tmp.r1); + return Math.max(Tmp.r1.width, Tmp.r1.height) * 2f; + } + + public abstract TextureRegion getIconRegion(); + + public abstract Weapon getWeapon(); + + public abstract int getItemCapacity(); + + public abstract float mass(); + + public abstract boolean isFlying(); +} diff --git a/core/src/io/anuke/mindustry/entities/type/base/BaseDrone.java b/core/src/io/anuke/mindustry/entities/type/base/BaseDrone.java new file mode 100644 index 0000000000..0ce42b0bc1 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/type/base/BaseDrone.java @@ -0,0 +1,59 @@ +package io.anuke.mindustry.entities.type.base; + +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.Geometry; +import io.anuke.mindustry.entities.type.FlyingUnit; +import io.anuke.mindustry.entities.units.UnitState; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.meta.BlockFlag; + +import static io.anuke.mindustry.Vars.world; + +public abstract class BaseDrone extends FlyingUnit{ + public final UnitState retreat = new UnitState(){ + public void entered(){ + target = null; + } + + public void update(){ + if(health >= maxHealth()){ + state.set(getStartState()); + }else if(!targetHasFlag(BlockFlag.repair)){ + if(retarget()){ + Tile repairPoint = Geometry.findClosest(x, y, world.indexer.getAllied(team, BlockFlag.repair)); + if(repairPoint != null){ + target = repairPoint; + }else{ + setState(getStartState()); + } + } + }else{ + circle(40f); + } + } + }; + + @Override + protected void updateRotation(){ + if(target != null && shouldRotate() && target.dst(this) < type.range){ + rotation = Mathf.slerpDelta(rotation, angleTo(target), 0.3f); + }else{ + rotation = Mathf.slerpDelta(rotation, velocity.angle(), 0.3f); + } + } + + @Override + public void behavior(){ + if(health <= maxHealth() * type.retreatPercent && !state.is(retreat) && Geometry.findClosest(x, y, world.indexer.getAllied(team, BlockFlag.repair)) != null){ + setState(retreat); + } + } + + public boolean shouldRotate(){ + return state.is(getStartState()); + } + + @Override + public abstract UnitState getStartState(); + +} diff --git a/core/src/io/anuke/mindustry/entities/type/base/BuilderDrone.java b/core/src/io/anuke/mindustry/entities/type/base/BuilderDrone.java new file mode 100644 index 0000000000..21649dbf07 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/type/base/BuilderDrone.java @@ -0,0 +1,241 @@ +package io.anuke.mindustry.entities.type.base; + +import io.anuke.arc.Core; +import io.anuke.arc.Events; +import io.anuke.arc.collection.IntIntMap; +import io.anuke.arc.collection.Queue; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.util.*; +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.entities.EntityGroup; +import io.anuke.mindustry.entities.traits.BuilderTrait; +import io.anuke.mindustry.entities.traits.TargetTrait; +import io.anuke.mindustry.entities.type.*; +import io.anuke.mindustry.entities.units.UnitState; +import io.anuke.mindustry.game.EventType.BuildSelectEvent; +import io.anuke.mindustry.game.Teams.TeamData; +import io.anuke.mindustry.gen.BrokenBlock; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.BuildBlock; +import io.anuke.mindustry.world.blocks.BuildBlock.BuildEntity; + +import java.io.*; + +import static io.anuke.mindustry.Vars.*; + +public class BuilderDrone extends BaseDrone implements BuilderTrait{ + private static final StaticReset reset = new StaticReset(); + private static final IntIntMap totals = new IntIntMap(); + + protected Queue placeQueue = new Queue<>(); + protected boolean isBreaking; + protected Player playerTarget; + + public final UnitState + + build = new UnitState(){ + + public void entered(){ + if(!(target instanceof BuildEntity)){ + target = null; + } + } + + public void update(){ + BuildEntity entity = (BuildEntity)target; + TileEntity core = getClosestCore(); + + if(isBuilding() && entity == null && isRebuild()){ + target = world.tile(buildRequest().x, buildRequest().y); + circle(placeDistance * 0.7f); + target = null; + + BuildRequest request = buildRequest(); + + if(world.tile(request.x, request.y).entity instanceof BuildEntity){ + target = world.tile(request.x, request.y).entity; + } + }else if(entity != null && core != null && (entity.progress < 1f || entity.progress > 0f) && entity.tile.block() instanceof BuildBlock){ //building is valid + if(!isBuilding() && dst(target) < placeDistance * 0.9f){ //within distance, begin placing + if(isBreaking){ + buildQueue().addLast(new BuildRequest(entity.tile.x, entity.tile.y)); + }else{ + buildQueue().addLast(new BuildRequest(entity.tile.x, entity.tile.y, entity.tile.rotation(), entity.cblock)); + } + } + + circle(placeDistance * 0.7f); + velocity.scl(0.74f); + }else{ //else, building isn't valid, follow a player + target = null; + + if(playerTarget == null || playerTarget.getTeam() != team || !playerTarget.isValid()){ + playerTarget = null; + + if(retarget()){ + float minDst = Float.POSITIVE_INFINITY; + int minDrones = Integer.MAX_VALUE; + + //find player with min amount of drones + for(Player player : playerGroup.all()){ + if(player.getTeam() == team){ + int drones = getDrones(player); + float dst = dst2(player); + + if(playerTarget == null || drones < minDrones || (drones == minDrones && dst < minDst)){ + minDrones = drones; + minDst = dst; + playerTarget = player; + } + } + } + } + + if(getSpawner() != null){ + target = getSpawner(); + circle(40f); + target = null; + } + }else{ + incDrones(playerTarget); + TargetTrait prev = target; + target = playerTarget; + float dst = 90f + (id % 4)*30; + float tdst = dst(target); + float scale = (Mathf.lerp(1f, 0.77f, 1f - Mathf.clamp((tdst - dst) / dst))); + circle(dst); + velocity.scl(scale); + target = prev; + } + } + } + }; + + public BuilderDrone(){ + if(reset.check()){ + Events.on(BuildSelectEvent.class, event -> { + EntityGroup group = unitGroups[event.team.ordinal()]; + + if(!(event.tile.entity instanceof BuildEntity)) return; + + for(BaseUnit unit : group.all()){ + if(unit instanceof BuilderDrone){ + BuilderDrone drone = (BuilderDrone)unit; + if(drone.isBuilding()){ + //stop building if opposite building begins. + BuildRequest req = drone.buildRequest(); + if(req.breaking != event.breaking && req.x == event.tile.x && req.y == event.tile.y){ + drone.clearBuilding(); + drone.target = null; + } + } + } + } + }); + } + } + + int getDrones(Player player){ + return Pack.leftShort(totals.get(player.id, 0)); + } + + void incDrones(Player player){ + int num = totals.get(player.id, 0); + int amount = Pack.leftShort(num), frame = Pack.rightShort(num); + short curFrame = (short)(Core.graphics.getFrameId() % Short.MAX_VALUE); + + if(frame != curFrame){ + totals.put(player.id, Pack.shortInt((short)1, curFrame)); + }else{ + totals.put(player.id, Pack.shortInt((short)(amount + 1), curFrame)); + } + } + + boolean isRebuild(){ + //disabled until further notice, reason being that it's too annoying when playing enemies and too broken for ally use + return false; //Vars.state.rules.enemyCheat && team == waveTeam; + } + + @Override + public float getBuildPower(Tile tile){ + return type.buildPower; + } + + @Override + public Queue buildQueue(){ + return placeQueue; + } + + @Override + public void update(){ + super.update(); + + if(!isBuilding() && timer.get(timerTarget2, 15)){ + for(Player player : playerGroup.all()){ + if(player.getTeam() == team && player.buildRequest() != null){ + BuildRequest req = player.buildRequest(); + Tile tile = world.tile(req.x, req.y); + if(tile != null && tile.entity instanceof BuildEntity){ + BuildEntity b = tile.entity(); + float dist = Math.min(b.dst(x, y) - placeDistance, 0); + if(dist / type.maxVelocity < b.buildCost * 0.9f){ + target = b; + this.isBreaking = req.breaking; + setState(build); + break; + } + } + } + } + + if(isRebuild() && !isBuilding()){ + TeamData data = Vars.state.teams.get(team); + if(!data.brokenBlocks.isEmpty()){ + long block = data.brokenBlocks.removeLast(); + + placeQueue.addFirst(new BuildRequest(BrokenBlock.x(block), BrokenBlock.y(block), BrokenBlock.rotation(block), content.block(BrokenBlock.block(block)))); + setState(build); + } + } + } + + updateBuilding(); + } + + @Override + public boolean shouldRotate(){ + return isBuilding(); + } + + @Override + public UnitState getStartState(){ + return build; + } + + @Override + public void drawOver(){ + drawBuilding(); + } + + @Override + public float drawSize(){ + return isBuilding() ? placeDistance * 2f : 30f; + } + + @Override + public boolean canCreateBlocks(){ + return true; + } + + @Override + public void write(DataOutput data) throws IOException{ + super.write(data); + writeBuilding(data); + } + + @Override + public void read(DataInput data) throws IOException{ + super.read(data); + readBuilding(data); + } +} diff --git a/core/src/io/anuke/mindustry/entities/type/base/Crawler.java b/core/src/io/anuke/mindustry/entities/type/base/Crawler.java new file mode 100644 index 0000000000..f3c12c9604 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/type/base/Crawler.java @@ -0,0 +1,6 @@ +package io.anuke.mindustry.entities.type.base; + +import io.anuke.mindustry.entities.type.GroundUnit; + +public class Crawler extends GroundUnit{ +} diff --git a/core/src/io/anuke/mindustry/entities/type/base/Dagger.java b/core/src/io/anuke/mindustry/entities/type/base/Dagger.java new file mode 100644 index 0000000000..13bf6021c9 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/type/base/Dagger.java @@ -0,0 +1,7 @@ +package io.anuke.mindustry.entities.type.base; + +import io.anuke.mindustry.entities.type.GroundUnit; + +public class Dagger extends GroundUnit{ + +} diff --git a/core/src/io/anuke/mindustry/entities/type/base/Draug.java b/core/src/io/anuke/mindustry/entities/type/base/Draug.java new file mode 100644 index 0000000000..47b7caed25 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/type/base/Draug.java @@ -0,0 +1,4 @@ +package io.anuke.mindustry.entities.type.base; + +public class Draug extends MinerDrone{ +} diff --git a/core/src/io/anuke/mindustry/entities/type/base/Eruptor.java b/core/src/io/anuke/mindustry/entities/type/base/Eruptor.java new file mode 100644 index 0000000000..dfbfe9db39 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/type/base/Eruptor.java @@ -0,0 +1,6 @@ +package io.anuke.mindustry.entities.type.base; + +import io.anuke.mindustry.entities.type.GroundUnit; + +public class Eruptor extends GroundUnit{ +} diff --git a/core/src/io/anuke/mindustry/entities/type/base/Fortress.java b/core/src/io/anuke/mindustry/entities/type/base/Fortress.java new file mode 100644 index 0000000000..992948fa79 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/type/base/Fortress.java @@ -0,0 +1,6 @@ +package io.anuke.mindustry.entities.type.base; + +import io.anuke.mindustry.entities.type.GroundUnit; + +public class Fortress extends GroundUnit{ +} diff --git a/core/src/io/anuke/mindustry/entities/type/base/Ghoul.java b/core/src/io/anuke/mindustry/entities/type/base/Ghoul.java new file mode 100644 index 0000000000..bc1f6a5317 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/type/base/Ghoul.java @@ -0,0 +1,7 @@ +package io.anuke.mindustry.entities.type.base; + +import io.anuke.mindustry.entities.type.FlyingUnit; + +public class Ghoul extends FlyingUnit{ + +} diff --git a/core/src/io/anuke/mindustry/entities/type/base/MinerDrone.java b/core/src/io/anuke/mindustry/entities/type/base/MinerDrone.java new file mode 100644 index 0000000000..8f40c9f092 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/type/base/MinerDrone.java @@ -0,0 +1,178 @@ +package io.anuke.mindustry.entities.type.base; + +import io.anuke.arc.math.Mathf; +import io.anuke.arc.util.Structs; +import io.anuke.mindustry.content.Blocks; +import io.anuke.mindustry.entities.traits.MinerTrait; +import io.anuke.mindustry.entities.type.TileEntity; +import io.anuke.mindustry.entities.units.UnitState; +import io.anuke.mindustry.gen.Call; +import io.anuke.mindustry.type.Item; +import io.anuke.mindustry.type.ItemType; +import io.anuke.mindustry.world.Pos; +import io.anuke.mindustry.world.Tile; + +import java.io.*; + +import static io.anuke.mindustry.Vars.world; + +/** A drone that only mines.*/ +public class MinerDrone extends BaseDrone implements MinerTrait{ + protected Item targetItem; + protected Tile mineTile; + + public final UnitState + + mine = new UnitState(){ + public void entered(){ + target = null; + } + + public void update(){ + TileEntity entity = getClosestCore(); + + if(entity == null) return; + + findItem(); + + //core full of the target item, do nothing + if(targetItem != null && entity.block.acceptStack(targetItem, 1, entity.tile, MinerDrone.this) == 0){ + MinerDrone.this.clearItem(); + return; + } + + //if inventory is full, drop it off. + if(item.amount >= getItemCapacity() || (targetItem != null && !acceptsItem(targetItem))){ + setState(drop); + }else{ + if(retarget() && targetItem != null){ + target = world.indexer.findClosestOre(x, y, targetItem); + } + + if(target instanceof Tile){ + moveTo(type.range / 1.5f); + + if(dst(target) < type.range && mineTile != target){ + setMineTile((Tile)target); + } + + if(((Tile)target).block() != Blocks.air){ + setState(drop); + } + }else{ + //nothing to mine anymore, core full: circle spawnpoint + if(getSpawner() != null){ + target = getSpawner(); + + circle(40f); + } + } + } + } + + public void exited(){ + setMineTile(null); + } + }, + + drop = new UnitState(){ + public void entered(){ + target = null; + } + + public void update(){ + if(item.amount == 0 || item.item.type != ItemType.material){ + setState(mine); + return; + } + + target = getClosestCore(); + + if(target == null) return; + + TileEntity tile = (TileEntity)target; + + if(dst(target) < type.range){ + if(tile.tile.block().acceptStack(item.item, item.amount, tile.tile, MinerDrone.this) == item.amount){ + Call.transferItemTo(item.item, item.amount, x, y, tile.tile); + item.amount = 0; + } + + setState(mine); + } + + circle(type.range / 1.8f); + } + }; + + @Override + public UnitState getStartState(){ + return mine; + } + + @Override + public void update(){ + super.update(); + + updateMining(); + } + + @Override + protected void updateRotation(){ + if(mineTile != null && shouldRotate() && mineTile.dst(this) < type.range){ + rotation = Mathf.slerpDelta(rotation, angleTo(mineTile), 0.3f); + }else{ + rotation = Mathf.slerpDelta(rotation, velocity.angle(), 0.3f); + } + } + + @Override + public boolean shouldRotate(){ + return isMining(); + } + + @Override + public void drawOver(){ + drawMining(); + } + + @Override + public boolean canMine(Item item){ + return type.toMine.contains(item); + } + + @Override + public float getMinePower(){ + return type.minePower; + } + + @Override + public Tile getMineTile(){ + return mineTile; + } + + @Override + public void setMineTile(Tile tile){ + mineTile = tile; + } + + @Override + public void write(DataOutput data) throws IOException{ + super.write(data); + data.writeInt(mineTile == null || !state.is(mine) ? Pos.invalid : mineTile.pos()); + } + + @Override + public void read(DataInput data) throws IOException{ + super.read(data); + mineTile = world.tile(data.readInt()); + } + + protected void findItem(){ + TileEntity entity = getClosestCore(); + if(entity == null){ + return; + } + targetItem = Structs.findMin(type.toMine, world.indexer::hasOre, (a, b) -> -Integer.compare(entity.items.get(a), entity.items.get(b))); + } +} diff --git a/core/src/io/anuke/mindustry/entities/type/base/Phantom.java b/core/src/io/anuke/mindustry/entities/type/base/Phantom.java new file mode 100644 index 0000000000..1a50115647 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/type/base/Phantom.java @@ -0,0 +1,5 @@ +package io.anuke.mindustry.entities.type.base; + +public class Phantom extends BuilderDrone{ + +} diff --git a/core/src/io/anuke/mindustry/entities/type/base/RepairDrone.java b/core/src/io/anuke/mindustry/entities/type/base/RepairDrone.java new file mode 100644 index 0000000000..e5be4d3d07 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/type/base/RepairDrone.java @@ -0,0 +1,68 @@ +package io.anuke.mindustry.entities.type.base; + +import io.anuke.mindustry.entities.Units; +import io.anuke.mindustry.entities.type.TileEntity; +import io.anuke.mindustry.entities.units.UnitState; +import io.anuke.mindustry.world.Pos; +import io.anuke.mindustry.world.Tile; + +import java.io.*; + +import static io.anuke.mindustry.Vars.world; + +public class RepairDrone extends BaseDrone{ + public final UnitState repair = new UnitState(){ + + public void entered(){ + target = null; + } + + public void update(){ + + if(retarget()){ + target = Units.findDamagedTile(team, x, y); + } + + if(target != null){ + if(target.dst(RepairDrone.this) > type.range){ + circle(type.range * 0.9f); + }else{ + getWeapon().update(RepairDrone.this, target.getX(), target.getY()); + } + }else{ + //circle spawner if there's nothing to repair + if(getSpawner() != null){ + target = getSpawner(); + circle(type.range * 1.5f, type.speed/2f); + target = null; + } + } + } + }; + + @Override + public boolean shouldRotate(){ + return target != null; + } + + @Override + public UnitState getStartState(){ + return repair; + } + + @Override + public void write(DataOutput data) throws IOException{ + super.write(data); + data.writeInt(state.is(repair) && target instanceof TileEntity ? ((TileEntity)target).tile.pos() : Pos.invalid); + } + + @Override + public void read(DataInput data) throws IOException{ + super.read(data); + Tile repairing = world.tile(data.readInt()); + + if(repairing != null){ + target = repairing.entity; + } + } +} diff --git a/core/src/io/anuke/mindustry/entities/type/base/Revenant.java b/core/src/io/anuke/mindustry/entities/type/base/Revenant.java new file mode 100644 index 0000000000..03233dbb5d --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/type/base/Revenant.java @@ -0,0 +1,35 @@ +package io.anuke.mindustry.entities.type.base; + +import io.anuke.arc.graphics.g2d.Draw; +import io.anuke.arc.math.Angles; +import io.anuke.arc.math.Mathf; +import io.anuke.mindustry.entities.Units; +import io.anuke.mindustry.entities.type.FlyingUnit; + +public class Revenant extends FlyingUnit{ + + @Override + public void drawWeapons(){ + for(int i : Mathf.signs){ + float tra = rotation - 90, trY = -getWeapon().getRecoil(this, i > 0) + type.weaponOffsetY; + float w = i > 0 ? -12 : 12; + float wx = x + Angles.trnsx(tra, getWeapon().width * i, trY), wy = y + Angles.trnsy(tra, getWeapon().width * i, trY); + int wi = (i + 1) / 2; + Draw.rect(getWeapon().region, wx, wy, w, 12, weaponAngles[wi] - 90); + } + } + + @Override + protected void attack(float circleLength){ + moveTo(circleLength); + } + + @Override + protected void updateRotation(){ + if(!Units.invalidateTarget(target, this)){ + rotation = Mathf.slerpDelta(rotation, angleTo(target), type.rotatespeed); + }else{ + rotation = Mathf.slerpDelta(rotation, velocity.angle(), type.baseRotateSpeed); + } + } +} diff --git a/core/src/io/anuke/mindustry/entities/type/base/Spirit.java b/core/src/io/anuke/mindustry/entities/type/base/Spirit.java new file mode 100644 index 0000000000..d43fc658b0 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/type/base/Spirit.java @@ -0,0 +1,4 @@ +package io.anuke.mindustry.entities.type.base; + +public class Spirit extends RepairDrone{ +} diff --git a/core/src/io/anuke/mindustry/entities/type/base/Titan.java b/core/src/io/anuke/mindustry/entities/type/base/Titan.java new file mode 100644 index 0000000000..1ac30593ac --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/type/base/Titan.java @@ -0,0 +1,7 @@ +package io.anuke.mindustry.entities.type.base; + +import io.anuke.mindustry.entities.type.GroundUnit; + +public class Titan extends GroundUnit{ + +} diff --git a/core/src/io/anuke/mindustry/entities/type/base/Wraith.java b/core/src/io/anuke/mindustry/entities/type/base/Wraith.java new file mode 100644 index 0000000000..9123ffcb8d --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/type/base/Wraith.java @@ -0,0 +1,7 @@ +package io.anuke.mindustry.entities.type.base; + +import io.anuke.mindustry.entities.type.FlyingUnit; + +public class Wraith extends FlyingUnit{ + +} diff --git a/core/src/io/anuke/mindustry/entities/units/StateMachine.java b/core/src/io/anuke/mindustry/entities/units/StateMachine.java new file mode 100644 index 0000000000..7ed555e7b6 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/units/StateMachine.java @@ -0,0 +1,24 @@ +package io.anuke.mindustry.entities.units; + +public class StateMachine{ + private UnitState state; + + public void update(){ + if(state != null) state.update(); + } + + public void set(UnitState next){ + if(next == state) return; + if(state != null) state.exited(); + this.state = next; + if(next != null) next.entered(); + } + + public UnitState current(){ + return state; + } + + public boolean is(UnitState state){ + return this.state == state; + } +} diff --git a/core/src/io/anuke/mindustry/entities/units/Statuses.java b/core/src/io/anuke/mindustry/entities/units/Statuses.java new file mode 100644 index 0000000000..75551f692b --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/units/Statuses.java @@ -0,0 +1,160 @@ +package io.anuke.mindustry.entities.units; + +import io.anuke.arc.collection.Array; +import io.anuke.arc.collection.Bits; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.util.Time; +import io.anuke.arc.util.Tmp; +import io.anuke.arc.util.pooling.Pools; +import io.anuke.mindustry.content.StatusEffects; +import io.anuke.mindustry.entities.traits.Saveable; +import io.anuke.mindustry.entities.type.Unit; +import io.anuke.mindustry.type.ContentType; +import io.anuke.mindustry.type.StatusEffect; + +import java.io.*; + +import static io.anuke.mindustry.Vars.content; + +/** Class for controlling status effects on an entity. */ +public class Statuses implements Saveable{ + private static final StatusEntry globalResult = new StatusEntry(); + private static final Array removals = new Array<>(); + + private Array statuses = new Array<>(); + private Bits applied = new Bits(content.getBy(ContentType.status).size); + + private float speedMultiplier; + private float damageMultiplier; + private float armorMultiplier; + + public void handleApply(io.anuke.mindustry.entities.type.Unit unit, StatusEffect effect, float duration){ + if(effect == StatusEffects.none || unit.isImmune(effect)) return; //don't apply empty or immune effects + + if(statuses.size > 0){ + //check for opposite effects + for(StatusEntry entry : statuses){ + //extend effect + if(entry.effect == effect){ + entry.time = Math.max(entry.time, duration); + return; + }else if(entry.effect.reactsWith(effect)){ //find opposite + entry.effect.getTransition(unit, effect, entry.time, duration, globalResult); + entry.time = globalResult.time; + + if(globalResult.effect != entry.effect){ + entry.effect = globalResult.effect; + } + + //stop looking when one is found + return; + } + } + } + + //otherwise, no opposites found, add direct effect + StatusEntry entry = Pools.obtain(StatusEntry.class, StatusEntry::new); + entry.set(effect, duration); + statuses.add(entry); + } + + public Color getStatusColor(){ + if(statuses.size == 0){ + return Tmp.c1.set(Color.WHITE); + } + + float r = 0f, g = 0f, b = 0f; + for(StatusEntry entry : statuses){ + r += entry.effect.color.r; + g += entry.effect.color.g; + b += entry.effect.color.b; + } + return Tmp.c1.set(r / statuses.size, g / statuses.size, b / statuses.size, 1f); + } + + public void clear(){ + statuses.clear(); + } + + public void update(Unit unit){ + applied.clear(); + speedMultiplier = damageMultiplier = armorMultiplier = 1f; + + if(statuses.size == 0) return; + + removals.clear(); + + for(StatusEntry entry : statuses){ + entry.time = Math.max(entry.time - Time.delta(), 0); + applied.set(entry.effect.id); + + if(entry.time <= 0){ + Pools.free(entry); + removals.add(entry); + }else{ + speedMultiplier *= entry.effect.speedMultiplier; + armorMultiplier *= entry.effect.armorMultiplier; + damageMultiplier *= entry.effect.damageMultiplier; + entry.effect.update(unit, entry.time); + } + } + + if(removals.size > 0){ + statuses.removeAll(removals, true); + } + } + + public float getSpeedMultiplier(){ + return speedMultiplier; + } + + public float getDamageMultiplier(){ + return damageMultiplier; + } + + public float getArmorMultiplier(){ + return armorMultiplier; + } + + public boolean hasEffect(StatusEffect effect){ + return applied.get(effect.id); + } + + @Override + public void writeSave(DataOutput stream) throws IOException{ + stream.writeByte(statuses.size); + for(StatusEntry entry : statuses){ + stream.writeByte(entry.effect.id); + stream.writeFloat(entry.time); + } + } + + @Override + public void readSave(DataInput stream, byte version) throws IOException{ + for(StatusEntry effect : statuses){ + Pools.free(effect); + } + + statuses.clear(); + + byte amount = stream.readByte(); + for(int i = 0; i < amount; i++){ + byte id = stream.readByte(); + float time = stream.readFloat(); + StatusEntry entry = Pools.obtain(StatusEntry.class, StatusEntry::new); + entry.set(content.getByID(ContentType.status, id), time); + statuses.add(entry); + } + } + + public static class StatusEntry{ + public StatusEffect effect; + public float time; + + public StatusEntry set(StatusEffect effect, float time){ + this.effect = effect; + this.time = time; + return this; + } + } +} diff --git a/core/src/io/anuke/mindustry/entities/units/UnitDrops.java b/core/src/io/anuke/mindustry/entities/units/UnitDrops.java new file mode 100644 index 0000000000..142f2f3d65 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/units/UnitDrops.java @@ -0,0 +1,47 @@ +package io.anuke.mindustry.entities.units; + +import io.anuke.arc.math.Mathf; +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.content.Items; +import io.anuke.mindustry.entities.type.BaseUnit; +import io.anuke.mindustry.entities.type.TileEntity; +import io.anuke.mindustry.gen.Call; +import io.anuke.mindustry.type.Item; + +public class UnitDrops{ + private static Item[] dropTable; + + public static void dropItems(BaseUnit unit){ + //items only dropped in waves for enemy team + if(unit.getTeam() != Vars.waveTeam || !Vars.state.rules.unitDrops){ + return; + } + + TileEntity core = unit.getClosestEnemyCore(); + + if(core == null || core.dst(unit) > Vars.mineTransferRange){ + return; + } + + if(dropTable == null){ + dropTable = new Item[]{Items.titanium, Items.silicon, Items.lead, Items.copper}; + } + + for(int i = 0; i < 3; i++){ + for(Item item : dropTable){ + //only drop unlocked items + if(!Vars.headless && !Vars.data.isUnlocked(item)){ + continue; + } + + if(Mathf.chance(0.03)){ + int amount = Mathf.random(20, 40); + amount = core.tile.block().acceptStack(item, amount, core.tile, null); + if(amount > 0){ + Call.transferItemTo(item, amount, unit.x + Mathf.range(2f), unit.y + Mathf.range(2f), core.tile); + } + } + } + } + } +} diff --git a/core/src/io/anuke/mindustry/entities/units/UnitState.java b/core/src/io/anuke/mindustry/entities/units/UnitState.java new file mode 100644 index 0000000000..5c5f9c64a8 --- /dev/null +++ b/core/src/io/anuke/mindustry/entities/units/UnitState.java @@ -0,0 +1,12 @@ +package io.anuke.mindustry.entities.units; + +public interface UnitState{ + default void entered(){ + } + + default void exited(){ + } + + default void update(){ + } +} diff --git a/core/src/io/anuke/mindustry/game/Content.java b/core/src/io/anuke/mindustry/game/Content.java new file mode 100644 index 0000000000..b03177e9c3 --- /dev/null +++ b/core/src/io/anuke/mindustry/game/Content.java @@ -0,0 +1,37 @@ +package io.anuke.mindustry.game; + +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.type.ContentType; + + +/** Base class for a content type that is loaded in {@link io.anuke.mindustry.core.ContentLoader}. */ +public abstract class Content{ + public final short id; + + public Content(){ + this.id = (short)Vars.content.getBy(getContentType()).size; + Vars.content.handleContent(this); + } + + /** + * Returns the type name of this piece of content. + * This should return the same value for all instances of this content type. + */ + public abstract ContentType getContentType(); + + /** Called after all content is created. Do not use to load regions or texture data! */ + public void init(){ + } + + /** + * Called after all content is created, only on non-headless versions. + * Use for loading regions or other image data. + */ + public void load(){ + } + + @Override + public String toString(){ + return getContentType().name() + "#" + id; + } +} diff --git a/core/src/io/anuke/mindustry/game/ContentList.java b/core/src/io/anuke/mindustry/game/ContentList.java new file mode 100644 index 0000000000..82406a9880 --- /dev/null +++ b/core/src/io/anuke/mindustry/game/ContentList.java @@ -0,0 +1,7 @@ +package io.anuke.mindustry.game; + +/** Interface for a list of content to be loaded in {@link io.anuke.mindustry.core.ContentLoader}. */ +public interface ContentList{ + /** This method should create all the content. */ + void load(); +} diff --git a/core/src/io/anuke/mindustry/game/DefaultWaves.java b/core/src/io/anuke/mindustry/game/DefaultWaves.java new file mode 100644 index 0000000000..7bf39a29b3 --- /dev/null +++ b/core/src/io/anuke/mindustry/game/DefaultWaves.java @@ -0,0 +1,191 @@ +package io.anuke.mindustry.game; + +import io.anuke.arc.collection.Array; +import io.anuke.mindustry.content.*; +import io.anuke.mindustry.type.ItemStack; + +public class DefaultWaves{ + private Array spawns; + + public Array get(){ + if(spawns == null && UnitTypes.dagger != null){ + spawns = Array.with( + new SpawnGroup(UnitTypes.dagger){{ + end = 10; + unitScaling = 2f; + }}, + + new SpawnGroup(UnitTypes.crawler){{ + begin = 4; + end = 13; + unitAmount = 2; + unitScaling = 1.5f; + }}, + + new SpawnGroup(UnitTypes.wraith){{ + begin = 12; + end = 16; + unitScaling = 1f; + }}, + + new SpawnGroup(UnitTypes.dagger){{ + begin = 11; + unitScaling = 1.7f; + spacing = 2; + max = 4; + }}, + + new SpawnGroup(UnitTypes.titan){{ + begin = 7; + spacing = 3; + unitScaling = 2; + + end = 30; + }}, + + new SpawnGroup(UnitTypes.dagger){{ + begin = 8; + unitScaling = 1; + unitAmount = 4; + spacing = 2; + }}, + + new SpawnGroup(UnitTypes.titan){{ + begin = 28; + spacing = 3; + unitScaling = 1; + end = 40; + }}, + + new SpawnGroup(UnitTypes.titan){{ + begin = 45; + spacing = 3; + unitScaling = 2; + effect = StatusEffects.overdrive; + }}, + + new SpawnGroup(UnitTypes.titan){{ + begin = 120; + spacing = 2; + unitScaling = 3; + unitAmount = 5; + effect = StatusEffects.overdrive; + }}, + + new SpawnGroup(UnitTypes.wraith){{ + begin = 16; + unitScaling = 1; + spacing = 2; + }}, + + new SpawnGroup(UnitTypes.dagger){{ + begin = 82; + spacing = 3; + unitAmount = 4; + unitScaling = 3; + effect = StatusEffects.overdrive; + }}, + + new SpawnGroup(UnitTypes.dagger){{ + begin = 41; + spacing = 5; + unitAmount = 1; + unitScaling = 3; + effect = StatusEffects.shielded; + }}, + + new SpawnGroup(UnitTypes.fortress){{ + begin = 40; + spacing = 5; + unitAmount = 2; + unitScaling = 2; + max = 20; + }}, + + new SpawnGroup(UnitTypes.dagger){{ + begin = 35; + spacing = 3; + unitAmount = 4; + effect = StatusEffects.overdrive; + items = new ItemStack(Items.blastCompound, 60); + end = 60; + }}, + + new SpawnGroup(UnitTypes.dagger){{ + begin = 42; + spacing = 3; + unitAmount = 4; + effect = StatusEffects.overdrive; + items = new ItemStack(Items.pyratite, 100); + end = 130; + }}, + + new SpawnGroup(UnitTypes.ghoul){{ + begin = 40; + unitAmount = 2; + spacing = 2; + unitScaling = 2; + }}, + + new SpawnGroup(UnitTypes.wraith){{ + begin = 50; + unitAmount = 4; + unitScaling = 3; + spacing = 5; + effect = StatusEffects.overdrive; + }}, + + new SpawnGroup(UnitTypes.revenant){{ + begin = 50; + unitAmount = 2; + unitScaling = 3; + spacing = 5; + max = 16; + }}, + + new SpawnGroup(UnitTypes.ghoul){{ + begin = 53; + unitAmount = 2; + unitScaling = 3; + spacing = 4; + }}, + + new SpawnGroup(UnitTypes.eruptor){{ + begin = 31; + unitAmount = 4; + unitScaling = 1; + spacing = 3; + }}, + + new SpawnGroup(UnitTypes.chaosArray){{ + begin = 41; + unitAmount = 1; + unitScaling = 1; + spacing = 30; + }}, + + new SpawnGroup(UnitTypes.eradicator){{ + begin = 81; + unitAmount = 1; + unitScaling = 1; + spacing = 40; + }}, + + new SpawnGroup(UnitTypes.lich){{ + begin = 131; + unitAmount = 1; + unitScaling = 1; + spacing = 40; + }}, + + new SpawnGroup(UnitTypes.ghoul){{ + begin = 90; + unitAmount = 2; + unitScaling = 3; + spacing = 4; + }} + ); + } + return spawns == null ? new Array<>() : spawns; + } +} diff --git a/core/src/io/anuke/mindustry/game/Difficulty.java b/core/src/io/anuke/mindustry/game/Difficulty.java index 0acfd411b9..3b65017ff3 100644 --- a/core/src/io/anuke/mindustry/game/Difficulty.java +++ b/core/src/io/anuke/mindustry/game/Difficulty.java @@ -1,46 +1,28 @@ package io.anuke.mindustry.game; -import com.badlogic.gdx.ai.pfa.Heuristic; -import io.anuke.mindustry.ai.Heuristics.DestrutiveHeuristic; -import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.blocks.types.LiquidBlock; -import io.anuke.mindustry.world.blocks.types.PowerBlock; -import io.anuke.mindustry.world.blocks.types.defense.Turret; -import io.anuke.mindustry.world.blocks.types.distribution.Conveyor; -import io.anuke.mindustry.world.blocks.types.distribution.Router; -import io.anuke.mindustry.world.blocks.types.production.Drill; -import io.anuke.mindustry.world.blocks.types.production.Generator; -import io.anuke.mindustry.world.blocks.types.production.Smelter; -import io.anuke.ucore.util.Bundles; +import io.anuke.arc.Core; -public enum Difficulty { - easy(4f, 2f, 1f, new DestrutiveHeuristic(b -> b instanceof Generator)), - normal(2f, 1f, 1f, new DestrutiveHeuristic(b -> b instanceof Smelter || b instanceof Generator)), - hard(1.5f, 0.5f, 0.75f, new DestrutiveHeuristic(b -> b instanceof Turret || b instanceof Generator || b instanceof Drill || b instanceof Smelter)), - insane(0.5f, 0.25f, 0.5f, new DestrutiveHeuristic(b -> b instanceof Generator || b instanceof Drill || b instanceof Smelter || b instanceof Router)), - purge(0.25f, 0.01f, 0.25f, new DestrutiveHeuristic(b -> b instanceof Generator || b instanceof Drill || b instanceof Router - || b instanceof Smelter || b instanceof Conveyor || b instanceof LiquidBlock || b instanceof PowerBlock)); +/** Presets for time between waves. Currently unused.*/ +public enum Difficulty{ + easy(1.4f), + normal(1f), + hard(0.5f), + insane(0.25f); - /**The scaling of how many waves it takes for one more enemy of a type to appear. - * For example: with enemeyScaling = 2 and the default scaling being 2, it would take 4 waves for - * an enemy spawn to go from 1->2 enemies.*/ - public final float enemyScaling; - /**Multiplier of the time between waves.*/ - public final float timeScaling; - /**Scaling of max time between waves. Default time is 4 minutes.*/ - public final float maxTimeScaling; - /**Pathfdining heuristic for calculating tile costs.*/ - public final Heuristic heuristic; + /** Multiplier of the time between waves. */ + public final float waveTime; - Difficulty(float enemyScaling, float timeScaling, float maxTimeScaling, Heuristic heuristic){ - this.enemyScaling = enemyScaling; - this.timeScaling = timeScaling; - this.heuristic = heuristic; - this.maxTimeScaling = maxTimeScaling; + private String value; + + Difficulty(float waveTime){ + this.waveTime = waveTime; } @Override - public String toString() { - return Bundles.get("setting.difficulty." + name()); + public String toString(){ + if(value == null){ + value = Core.bundle.get("setting.difficulty." + name()); + } + return value; } } diff --git a/core/src/io/anuke/mindustry/game/EnemySpawn.java b/core/src/io/anuke/mindustry/game/EnemySpawn.java deleted file mode 100644 index eff8e10a44..0000000000 --- a/core/src/io/anuke/mindustry/game/EnemySpawn.java +++ /dev/null @@ -1,46 +0,0 @@ -package io.anuke.mindustry.game; - -import io.anuke.mindustry.entities.enemies.EnemyType; -import io.anuke.ucore.util.Mathf; - -import static io.anuke.mindustry.Vars.state; - -public class EnemySpawn{ - /**The enemy type spawned*/ - public final EnemyType type; - /**When this spawns should end*/ - protected int before = Integer.MAX_VALUE; - /**When this spawns should start*/ - protected int after; - /**The spacing, in waves, of spawns. 2 = spawns every other wave*/ - protected int spacing = 1; - /**How many waves need to pass after the start of this spawn for the tier to increase by one*/ - protected int tierscale = 17; - /**How many more enemies there are, every time the tier increases*/ - protected int tierscaleback = 0; - /**The tier this spawn starts at.*/ - protected int tier = 1; - /**Maximum amount of enemies that spawn*/ - protected int max = 60; - /**How many waves need to pass before the amount of enemies increases by 1*/ - protected float scaling = 9999f; - /**Amount of enemies spawned initially, with no scaling*/ - protected int amount = 1; - - public EnemySpawn(EnemyType type){ - this.type = type; - } - - public int evaluate(int wave, int lane){ - if(wave < after || wave > before || (wave - after) % spacing != 0){ - return 0; - } - float scaling = this.scaling * state.difficulty.enemyScaling; - - return Math.min(amount-1 + Math.max((int)((wave / spacing) / scaling), 1) + (tier(wave, lane)-1) * tierscaleback, max); - } - - public int tier(int wave, int lane){ - return Mathf.clamp(tier + (wave-after)/tierscale, 1, EnemyType.maxtier); - } -} diff --git a/core/src/io/anuke/mindustry/game/EventType.java b/core/src/io/anuke/mindustry/game/EventType.java index c900741b7e..de0a286c66 100644 --- a/core/src/io/anuke/mindustry/game/EventType.java +++ b/core/src/io/anuke/mindustry/game/EventType.java @@ -1,90 +1,154 @@ package io.anuke.mindustry.game; import io.anuke.mindustry.core.GameState.State; -import io.anuke.mindustry.entities.BulletType; -import io.anuke.mindustry.entities.TileEntity; -import io.anuke.mindustry.entities.enemies.Enemy; -import io.anuke.mindustry.resource.Weapon; -import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.entities.traits.BuilderTrait; +import io.anuke.mindustry.entities.type.Unit; +import io.anuke.mindustry.type.Zone; import io.anuke.mindustry.world.Tile; -import io.anuke.ucore.entities.Entity; -import io.anuke.ucore.function.Event; -public class EventType { +public class EventType{ - public interface PlayEvent extends Event{ - void handle(); + /** Called when a zone's requirements are met. */ + public static class ZoneRequireCompleteEvent{ + public final Zone zone, required; + + public ZoneRequireCompleteEvent(Zone zone, Zone required){ + this.zone = zone; + this.required = required; + } } - public interface ResetEvent extends Event{ - void handle(); + /** Called when a zone's requirements are met. */ + public static class ZoneConfigureCompleteEvent{ + public final Zone zone; + + public ZoneConfigureCompleteEvent(Zone zone){ + this.zone = zone; + } } - public interface WaveEvent extends Event{ - void handle(); + /** Called when the game is first loaded. */ + public static class GameLoadEvent{ + } - public interface GameOverEvent extends Event{ - void handle(); + public static class PlayEvent{ + } - public interface StateChangeEvent extends Event{ - void handle(State from, State to); + public static class ResetEvent{ + } - public interface FriendlyFireChange extends Event{ - void handle(boolean on); + public static class WaveEvent{ + } - public interface BulletEvent extends Event{ - void handle(BulletType type, Entity owner, float x, float y, float angle, short damage); + public static class GameOverEvent{ + public final Team winner; + + public GameOverEvent(Team winner){ + this.winner = winner; + } } - public interface EnemyDeathEvent extends Event{ - void handle(Enemy enemy); + /** Called when a game begins and the world is loaded. */ + public static class WorldLoadEvent{ + } - public interface BlockDestroyEvent extends Event{ - void handle(TileEntity entity); + /** Called from the logic thread. Do not access graphics here! */ + public static class TileChangeEvent{ + public final Tile tile; + + public TileChangeEvent(Tile tile){ + this.tile = tile; + } } - public interface BlockDamageEvent extends Event{ - void handle(TileEntity entity); + public static class StateChangeEvent{ + public final State from, to; + + public StateChangeEvent(State from, State to){ + this.from = from; + this.to = to; + } } - public interface PlayerDeathEvent extends Event{ - void handle(); + public static class UnlockEvent{ + public final UnlockableContent content; + + public UnlockEvent(UnlockableContent content){ + this.content = content; + } } - public interface BlockConfigEvent extends Event{ - void handle(Tile tile, byte data); + /** + * Called when block building begins by placing down the BuildBlock. + * The tile's block will nearly always be a BuildBlock. + */ + public static class BlockBuildBeginEvent{ + public final Tile tile; + public final Team team; + public final boolean breaking; + + public BlockBuildBeginEvent(Tile tile, Team team, boolean breaking){ + this.tile = tile; + this.team = team; + this.breaking = breaking; + } } - public interface BlockTapEvent extends Event{ - void handle(Tile tile); + public static class BlockBuildEndEvent{ + public final Tile tile; + public final Team team; + public final boolean breaking; + + public BlockBuildEndEvent(Tile tile, Team team, boolean breaking){ + this.tile = tile; + this.team = team; + this.breaking = breaking; + } } - public interface WeaponSwitchEvent extends Event{ - void handle(); + /** + * Called when a player or drone begins building something. + * This does not necessarily happen when a new BuildBlock is created. + */ + public static class BuildSelectEvent{ + public final Tile tile; + public final Team team; + public final BuilderTrait builder; + public final boolean breaking; + + public BuildSelectEvent(Tile tile, Team team, BuilderTrait builder, boolean breaking){ + this.tile = tile; + this.team = team; + this.builder = builder; + this.breaking = breaking; + } } - public interface UpgradeEvent extends Event{ - void handle(Weapon weapon); + /** Called right before a block is destroyed. + * The tile entity of the tile in this event cannot be null when this happens.*/ + public static class BlockDestroyEvent{ + public final Tile tile; + + public BlockDestroyEvent(Tile tile){ + this.tile = tile; + } } - public interface MessageSendEvent extends Event{ - void handle(String message); + public static class UnitDestroyEvent{ + public final Unit unit; + + public UnitDestroyEvent(Unit unit){ + this.unit = unit; + } } - public interface ShootEvent extends Event{ - void handle(Weapon weapon, float x, float y, float angle); - } + public static class ResizeEvent{ - public interface PlaceEvent extends Event{ - void handle(int x, int y, Block block, int rotation); - } - - public interface BreakEvent extends Event{ - void handle(int x, int y); } } + diff --git a/core/src/io/anuke/mindustry/game/GameMode.java b/core/src/io/anuke/mindustry/game/GameMode.java deleted file mode 100644 index 8481f1a97f..0000000000 --- a/core/src/io/anuke/mindustry/game/GameMode.java +++ /dev/null @@ -1,30 +0,0 @@ -package io.anuke.mindustry.game; - -import io.anuke.ucore.util.Bundles; - -public enum GameMode{ - waves, - sandbox{ - { - infiniteResources = true; - disableWaveTimer = true; - } - }, - freebuild{ - { - disableWaveTimer = true; - } - }; - public boolean infiniteResources; - public boolean disableWaveTimer; - - public String description(){ - return Bundles.get("mode."+name()+".description"); - } - - @Override - public String toString(){ - return Bundles.get("mode."+name()+".name"); - } - -} diff --git a/core/src/io/anuke/mindustry/game/Gamemode.java b/core/src/io/anuke/mindustry/game/Gamemode.java new file mode 100644 index 0000000000..6118b273ef --- /dev/null +++ b/core/src/io/anuke/mindustry/game/Gamemode.java @@ -0,0 +1,72 @@ +package io.anuke.mindustry.game; + +import io.anuke.arc.Core; +import io.anuke.arc.function.Consumer; + +/** Defines preset rule sets.. */ +public enum Gamemode{ + survival(rules -> { + rules.waveTimer = true; + rules.waves = true; + rules.unitDrops = true; + }), + sandbox(rules -> { + rules.infiniteResources = true; + rules.waves = true; + rules.waveTimer = false; + rules.respawnTime = 0f; + }), + attack(rules -> { + rules.enemyCheat = true; + rules.unitDrops = true; + rules.waves = false; + rules.attackMode = true; + }), + pvp(rules -> { + rules.pvp = true; + rules.enemyCoreBuildRadius = 600f; + rules.respawnTime = 60 * 10; + rules.buildCostMultiplier = 0.5f; + rules.buildSpeedMultiplier = 2f; + rules.playerDamageMultiplier = 0.33f; + rules.playerHealthMultiplier = 0.5f; + rules.unitBuildSpeedMultiplier = 3f; + rules.unitHealthMultiplier = 3f; + rules.attackMode = true; + }), + editor(true, rules -> { + rules.infiniteResources = true; + rules.editor = true; + rules.waves = false; + rules.enemyCoreBuildRadius = 0f; + rules.waveTimer = false; + rules.respawnTime = 0f; + }); + + private final Consumer rules; + public final boolean hidden; + + Gamemode(Consumer rules){ + this(false, rules); + } + + Gamemode(boolean hidden, Consumer rules){ + this.rules = rules; + this.hidden = hidden; + } + + /** Applies this preset to this ruleset. */ + public Rules apply(Rules in){ + rules.accept(in); + return in; + } + + public String description(){ + return Core.bundle.get("mode." + name() + ".description"); + } + + @Override + public String toString(){ + return Core.bundle.get("mode." + name() + ".name"); + } +} diff --git a/core/src/io/anuke/mindustry/game/GlobalData.java b/core/src/io/anuke/mindustry/game/GlobalData.java new file mode 100644 index 0000000000..f472325bee --- /dev/null +++ b/core/src/io/anuke/mindustry/game/GlobalData.java @@ -0,0 +1,127 @@ +package io.anuke.mindustry.game; + +import io.anuke.arc.Core; +import io.anuke.arc.Events; +import io.anuke.arc.collection.*; +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.content.Items; +import io.anuke.mindustry.game.EventType.UnlockEvent; +import io.anuke.mindustry.type.*; + +import static io.anuke.mindustry.Vars.content; +import static io.anuke.mindustry.Vars.state; + +/** Stores player unlocks. Clientside only. */ +public class GlobalData{ + private ObjectMap> unlocked = new ObjectMap<>(); + private ObjectIntMap items = new ObjectIntMap<>(); + private boolean modified; + + public GlobalData(){ + Core.settings.setSerializer(ContentType.class, (stream, t) -> stream.writeInt(t.ordinal()), stream -> ContentType.values()[stream.readInt()]); + Core.settings.setSerializer(Item.class, (stream, t) -> stream.writeUTF(t.name), stream -> content.getByName(ContentType.item, stream.readUTF())); + + Core.settings.setSerializer(ItemStack.class, (stream, t) -> { + stream.writeUTF(t.item.name); + stream.writeInt(t.amount); + }, stream -> { + String name = stream.readUTF(); + int amount = stream.readInt(); + return new ItemStack(content.getByName(ContentType.item, name), amount); + }); + } + + public void modified(){ + modified = true; + } + + public int getItem(Item item){ + return items.get(item, 0); + } + + public void addItem(Item item, int amount){ + unlockContent(item); + modified = true; + items.getAndIncrement(item, 0, amount); + state.stats.itemsDelivered.getAndIncrement(item, 0, amount); + } + + public boolean hasItems(ItemStack[] stacks){ + for(ItemStack stack : stacks){ + if(items.get(stack.item, 0) < stack.amount){ + return false; + } + } + return true; + } + + public void removeItems(ItemStack[] stacks){ + for(ItemStack stack : stacks){ + items.getAndIncrement(stack.item, 0, -stack.amount); + } + modified = true; + } + + public boolean has(Item item, int amount){ + return items.get(item, 0) >= amount; + } + + public ObjectIntMap items(){ + return items; + } + + /** Returns whether or not this piece of content is unlocked yet. */ + public boolean isUnlocked(UnlockableContent content){ + return content.alwaysUnlocked() || unlocked.getOr(content.getContentType(), ObjectSet::new).contains(content.name); + } + + /** + * Makes this piece of content 'unlocked', if possible. + * If this piece of content is already unlocked, nothing changes. + * Results are not saved until you call {@link #save()}. + */ + public void unlockContent(UnlockableContent content){ + if(content.alwaysUnlocked()) return; + + //fire unlock event so other classes can use it + if(unlocked.getOr(content.getContentType(), ObjectSet::new).add(content.name)){ + modified = true; + content.onUnlock(); + Events.fire(new UnlockEvent(content)); + } + } + + /** Clears all unlocked content. Automatically saves. */ + public void reset(){ + save(); + } + + public void checkSave(){ + if(modified){ + save(); + modified = false; + } + } + + @SuppressWarnings("unchecked") + public void load(){ + unlocked = Core.settings.getObject("unlocks", ObjectMap.class, ObjectMap::new); + for(Item item : Vars.content.items()){ + items.put(item, Core.settings.getInt("item-" + item.name, 0)); + } + + //set up default values + if(!Core.settings.has("item-" + Items.copper.name)){ + addItem(Items.copper, 50); + } + } + + public void save(){ + Core.settings.putObject("unlocks", unlocked); + for(Item item : Vars.content.items()){ + Core.settings.put("item-" + item.name, items.get(item, 0)); + } + Core.settings.save(); + } + +} diff --git a/core/src/io/anuke/mindustry/game/Inventory.java b/core/src/io/anuke/mindustry/game/Inventory.java deleted file mode 100644 index c3b48b6459..0000000000 --- a/core/src/io/anuke/mindustry/game/Inventory.java +++ /dev/null @@ -1,86 +0,0 @@ -package io.anuke.mindustry.game; - -import io.anuke.mindustry.resource.Item; -import io.anuke.mindustry.resource.ItemStack; - -import java.util.Arrays; - -import static io.anuke.mindustry.Vars.debug; - -public class Inventory { - private final int[] items = new int[Item.getAllItems().size]; - private boolean updated; - - public boolean isUpdated(){ - return updated; - } - - public void setUpdated(boolean updated){ - this.updated = updated; - } - - public void clearItems(){ - updated = true; - Arrays.fill(items, 0); - - addItem(Item.stone, 40); - - if(debug){ - Arrays.fill(items, 99999); - } - } - - public void fill(){ - Arrays.fill(items, 999999999); - } - - public int getAmount(Item item){ - return items[item.id]; - } - - public void addItem(Item item, int amount){ - updated = true; - items[item.id] += amount; - } - - public boolean hasItems(ItemStack[] items){ - for(ItemStack stack : items) - if(!hasItem(stack)) - return false; - return true; - } - - public boolean hasItems(ItemStack[] items, int scaling){ - for(ItemStack stack : items) - if(!hasItem(stack.item, stack.amount * scaling)) - return false; - return true; - } - - public boolean hasItem(ItemStack req){ - updated = true; - return items[req.item.id] >= req.amount; - } - - public boolean hasItem(Item item, int amount){ - updated = true; - return items[item.id] >= amount; - } - - public void removeItem(ItemStack req){ - updated = true; - items[req.item.id] -= req.amount; - if(items[req.item.id] < 0) items[req.item.id] = 0; //prevents negative item glitches in multiplayer - } - - public void removeItems(ItemStack... reqs){ - updated = true; - for(ItemStack req : reqs) - removeItem(req); - } - - public int[] getItems(){ - updated = true; - return items; - } -} diff --git a/core/src/io/anuke/mindustry/game/MappableContent.java b/core/src/io/anuke/mindustry/game/MappableContent.java new file mode 100644 index 0000000000..3253975085 --- /dev/null +++ b/core/src/io/anuke/mindustry/game/MappableContent.java @@ -0,0 +1,14 @@ +package io.anuke.mindustry.game; + +public abstract class MappableContent extends Content{ + public final String name; + + public MappableContent(String name){ + this.name = name; + } + + @Override + public String toString(){ + return name; + } +} diff --git a/core/src/io/anuke/mindustry/game/Rules.java b/core/src/io/anuke/mindustry/game/Rules.java new file mode 100644 index 0000000000..c32a63a659 --- /dev/null +++ b/core/src/io/anuke/mindustry/game/Rules.java @@ -0,0 +1,71 @@ +package io.anuke.mindustry.game; + +import io.anuke.annotations.Annotations.Serialize; +import io.anuke.arc.collection.Array; +import io.anuke.mindustry.io.JsonIO; +import io.anuke.mindustry.type.Zone; + +/** + * Defines current rules on how the game should function. + * Does not store game state, just configuration. + */ +@Serialize +public class Rules{ + /** Whether the player has infinite resources. */ + public boolean infiniteResources; + /** Whether the waves come automatically on a timer. If not, waves come when the play button is pressed. */ + public boolean waveTimer = true; + /** Whether waves are spawnable at all. */ + public boolean waves; + /** Whether the enemy AI has infinite resources in most of their buildings and turrets. */ + public boolean enemyCheat; + /** Whether the game objective is PvP. Note that this enables automatic hosting. */ + public boolean pvp; + /** Whether enemy units drop random items on death. */ + public boolean unitDrops; + /** How fast unit pads build units. */ + public float unitBuildSpeedMultiplier = 1f; + /** How much health units start with. */ + public float unitHealthMultiplier = 1f; + /** How much health players start with. */ + public float playerHealthMultiplier = 1f; + /** How much damage player mechs deal. */ + public float playerDamageMultiplier = 1f; + /** How much damage any other units deal. */ + public float unitDamageMultiplier = 1f; + /** Multiplier for buildings for the player. */ + public float buildCostMultiplier = 1f; + /** Multiplier for building speed. */ + public float buildSpeedMultiplier = 1f; + /** No-build zone around enemy core radius. */ + public float enemyCoreBuildRadius = 400f; + /** Radius around enemy wave drop zones.*/ + public float dropZoneRadius = 380f; + /** Player respawn time in ticks. */ + public float respawnTime = 60 * 4; + /** Time between waves in ticks. */ + public float waveSpacing = 60 * 60 * 2; + /** How many times longer a boss wave takes. */ + public float bossWaveMultiplier = 3f; + /** How many times longer a launch wave takes. */ + public float launchWaveMultiplier = 2f; + /** Zone for saves that have them.*/ + public Zone zone; + /** Spawn layout. */ + public Array spawns = new Array<>(); + /** Determines if there should be limited respawns. */ + public boolean limitedRespawns = false; + /** How many times player can respawn during one wave. */ + public int respawns = 5; + /** Hold wave timer until all enemies are destroyed. */ + public boolean waitForWaveToEnd = false; + /** Determinates if gamemode is attack mode */ + public boolean attackMode = false; + /** Whether this is the editor gamemode. */ + public boolean editor = false; + + /** Copies this ruleset exactly. Not very efficient at all, do not use often. */ + public Rules copy(){ + return JsonIO.copy(this); + } +} diff --git a/core/src/io/anuke/mindustry/game/Saves.java b/core/src/io/anuke/mindustry/game/Saves.java new file mode 100644 index 0000000000..a307a315ec --- /dev/null +++ b/core/src/io/anuke/mindustry/game/Saves.java @@ -0,0 +1,276 @@ +package io.anuke.mindustry.game; + +import io.anuke.arc.Core; +import io.anuke.arc.Events; +import io.anuke.arc.collection.*; +import io.anuke.arc.files.FileHandle; +import io.anuke.arc.util.*; +import io.anuke.mindustry.core.GameState.State; +import io.anuke.mindustry.game.EventType.StateChangeEvent; +import io.anuke.mindustry.io.SaveIO; +import io.anuke.mindustry.io.SaveIO.SaveException; +import io.anuke.mindustry.io.SaveMeta; +import io.anuke.mindustry.maps.Map; +import io.anuke.mindustry.type.Zone; + +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; + +import static io.anuke.mindustry.Vars.saveExtension; +import static io.anuke.mindustry.Vars.state; + +public class Saves{ + private int nextSlot; + private Array saves = new Array<>(); + private IntMap saveMap = new IntMap<>(); + private SaveSlot current; + private boolean saving; + private float time; + + private long totalPlaytime; + private long lastTimestamp; + + public Saves(){ + Events.on(StateChangeEvent.class, event -> { + if(event.to == State.menu){ + totalPlaytime = 0; + lastTimestamp = 0; + current = null; + } + }); + } + + public void load(){ + saves.clear(); + IntArray slots = Core.settings.getObject("save-slots", IntArray.class, IntArray::new); + + for(int i = 0; i < slots.size; i++){ + int index = slots.get(i); + if(SaveIO.isSaveValid(index)){ + SaveSlot slot = new SaveSlot(index); + saves.add(slot); + saveMap.put(slot.index, slot); + slot.meta = SaveIO.getMeta(index); + nextSlot = Math.max(index + 1, nextSlot); + } + } + } + + public SaveSlot getCurrent(){ + return current; + } + + public void update(){ + SaveSlot current = this.current; + + if(current != null && !state.is(State.menu) + && !(state.isPaused() && Core.scene.hasDialog())){ + if(lastTimestamp != 0){ + totalPlaytime += Time.timeSinceMillis(lastTimestamp); + } + lastTimestamp = Time.millis(); + } + + if(!state.is(State.menu) && !state.gameOver && current != null && current.isAutosave()){ + time += Time.delta(); + if(time > Core.settings.getInt("saveinterval") * 60){ + saving = true; + + Time.runTask(2f, () -> { + try{ + current.save(); + }catch(Exception e){ + e.printStackTrace(); + } + saving = false; + }); + + time = 0; + } + }else{ + time = 0; + } + } + + public long getTotalPlaytime(){ + return totalPlaytime; + } + + public void resetSave(){ + current = null; + } + + public boolean isSaving(){ + return saving; + } + + public void zoneSave(){ + SaveSlot slot = new SaveSlot(-1); + slot.setName("zone"); + saves.remove(s -> s.index == -1); + saves.add(slot); + saveMap.put(slot.index, slot); + slot.save(); + saveSlots(); + } + + public SaveSlot addSave(String name){ + SaveSlot slot = new SaveSlot(nextSlot); + nextSlot++; + slot.setName(name); + saves.add(slot); + saveMap.put(slot.index, slot); + slot.save(); + saveSlots(); + return slot; + } + + public SaveSlot importSave(FileHandle file) throws IOException{ + SaveSlot slot = new SaveSlot(nextSlot); + slot.importFile(file); + nextSlot++; + slot.setName(file.nameWithoutExtension()); + saves.add(slot); + saveMap.put(slot.index, slot); + slot.meta = SaveIO.getMeta(slot.index); + current = slot; + saveSlots(); + return slot; + } + + public SaveSlot getZoneSlot(){ + SaveSlot slot = getByID(-1); + return slot == null || slot.getZone() == null ? null : slot; + } + + public SaveSlot getByID(int id){ + return saveMap.get(id); + } + + public Array getSaveSlots(){ + return saves; + } + + private void saveSlots(){ + IntArray result = new IntArray(saves.size); + for(int i = 0; i < saves.size; i++) result.add(saves.get(i).index); + + Core.settings.putObject("save-slots", result); + Core.settings.save(); + } + + public class SaveSlot{ + public final int index; + SaveMeta meta; + + public SaveSlot(int index){ + this.index = index; + } + + public void load() throws SaveException{ + try{ + SaveIO.loadFromSlot(index); + meta = SaveIO.getMeta(index); + current = this; + totalPlaytime = meta.timePlayed; + }catch(Exception e){ + throw new SaveException(e); + } + } + + public void save(){ + long time = totalPlaytime; + long prev = totalPlaytime; + totalPlaytime = time; + + SaveIO.saveToSlot(index); + meta = SaveIO.getMeta(index); + if(!state.is(State.menu)){ + current = this; + } + + totalPlaytime = prev; + } + + public boolean isHidden(){ + return getZone() != null; + } + + public String getPlayTime(){ + return Strings.formatMillis(current == this ? totalPlaytime : meta.timePlayed); + } + + public long getTimestamp(){ + return meta.timestamp; + } + + public String getDate(){ + return SimpleDateFormat.getDateTimeInstance().format(new Date(meta.timestamp)); + } + + public Map getMap(){ + return meta.map; + } + + public String getName(){ + return Core.settings.getString("save-" + index + "-name", "untittled"); + } + + public void setName(String name){ + Core.settings.put("save-" + index + "-name", name); + Core.settings.save(); + } + + public Zone getZone(){ + return meta == null || meta.rules == null ? null : meta.rules.zone; + } + + public int getBuild(){ + return meta.build; + } + + public int getWave(){ + return meta.wave; + } + + public boolean isAutosave(){ + return Core.settings.getBool("save-" + index + "-autosave", true); + } + + public void setAutosave(boolean save){ + Core.settings.put("save-" + index + "-autosave", save); + Core.settings.save(); + } + + public void importFile(FileHandle file) throws IOException{ + try{ + file.copyTo(SaveIO.fileFor(index)); + }catch(Exception e){ + throw new IOException(e); + } + } + + public void exportFile(FileHandle file) throws IOException{ + try{ + if(!file.extension().equals(saveExtension)){ + file = file.parent().child(file.nameWithoutExtension() + "." + saveExtension); + } + SaveIO.fileFor(index).copyTo(file); + }catch(Exception e){ + throw new IOException(e); + } + } + + public void delete(){ + SaveIO.fileFor(index).delete(); + saves.removeValue(this, true); + saveMap.remove(index); + if(this == current){ + current = null; + } + + saveSlots(); + } + } +} diff --git a/core/src/io/anuke/mindustry/game/SpawnGroup.java b/core/src/io/anuke/mindustry/game/SpawnGroup.java new file mode 100644 index 0000000000..4637ba4bfd --- /dev/null +++ b/core/src/io/anuke/mindustry/game/SpawnGroup.java @@ -0,0 +1,110 @@ +package io.anuke.mindustry.game; + +import io.anuke.arc.util.serialization.Json; +import io.anuke.arc.util.serialization.Json.Serializable; +import io.anuke.arc.util.serialization.JsonValue; +import io.anuke.mindustry.entities.type.BaseUnit; +import io.anuke.mindustry.type.*; + +import static io.anuke.mindustry.Vars.content; + +/** + * A spawn group defines spawn information for a specific type of unit, with optional extra information like + * weapon equipped, ammo used, and status effects. + * Each spawn group can have multiple sub-groups spawned in different areas of the map. + */ +public class SpawnGroup implements Serializable{ + public static final int never = Integer.MAX_VALUE; + + /** The unit type spawned */ + public UnitType type; + /** When this spawn should end */ + public int end = never; + /** When this spawn should start */ + public int begin; + /** The spacing, in waves, of spawns. For example, 2 = spawns every other wave */ + public int spacing = 1; + /** Maximum amount of units that spawn */ + public int max = 100; + /** How many waves need to pass before the amount of units spawned increases by 1 */ + public float unitScaling = never; + /** Amount of enemies spawned initially, with no scaling */ + public int unitAmount = 1; + /** Status effect applied to the spawned unit. Null to disable. */ + public StatusEffect effect; + /** Items this unit spawns with. Null to disable. */ + public ItemStack items; + + public SpawnGroup(UnitType type){ + this.type = type; + } + + public SpawnGroup(){ + //serialization use only + } + + /** Returns the amount of units spawned on a specific wave. */ + public int getUnitsSpawned(int wave){ + if(wave < begin || wave > end || (wave - begin) % spacing != 0){ + return 0; + } + return Math.min(unitAmount + (int)(((wave - begin) / spacing) / unitScaling), max); + } + + /** + * Creates a unit, and assigns correct values based on this group's data. + * This method does not add() the unit. + */ + public BaseUnit createUnit(Team team){ + BaseUnit unit = type.create(team); + + if(effect != null){ + unit.applyEffect(effect, 999999f); + } + + if(items != null){ + unit.addItem(items.item, items.amount); + } + + return unit; + } + + @Override + public void write(Json json){ + json.writeValue("type", type.name); + if(begin != 0) json.writeValue("begin", begin); + if(end != never) json.writeValue("end", end); + if(spacing != 1) json.writeValue("spacing", spacing); + //if(max != 40) json.writeValue("max", max); + if(unitScaling != never) json.writeValue("scaling", unitScaling); + if(unitAmount != 1) json.writeValue("amount", unitAmount); + if(effect != null) json.writeValue("effect", effect.id); + } + + @Override + public void read(Json json, JsonValue data){ + type = content.getByName(ContentType.unit, data.getString("type", "dagger")); + begin = data.getInt("begin", 0); + end = data.getInt("end", never); + spacing = data.getInt("spacing", 1); + //max = data.getInt("max", 40); + unitScaling = data.getFloat("scaling", never); + unitAmount = data.getInt("amount", 1); + effect = content.getByID(ContentType.status, data.getInt("effect", -1)); + } + + @Override + public String toString(){ + return "SpawnGroup{" + + "type=" + type + + ", end=" + end + + ", begin=" + begin + + ", spacing=" + spacing + + ", max=" + max + + ", unitScaling=" + unitScaling + + ", unitAmount=" + unitAmount + + ", effect=" + effect + + ", items=" + items + + '}'; + } +} diff --git a/core/src/io/anuke/mindustry/game/SpawnPoint.java b/core/src/io/anuke/mindustry/game/SpawnPoint.java deleted file mode 100644 index 62e41e70bb..0000000000 --- a/core/src/io/anuke/mindustry/game/SpawnPoint.java +++ /dev/null @@ -1,19 +0,0 @@ -package io.anuke.mindustry.game; - -import com.badlogic.gdx.ai.pfa.PathFinder; -import com.badlogic.gdx.ai.pfa.PathFinderRequest; - -import io.anuke.mindustry.ai.SmoothGraphPath; -import io.anuke.mindustry.world.Tile; - -public class SpawnPoint{ - public Tile start; - public Tile[] pathTiles; - public PathFinder finder; - public SmoothGraphPath path = new SmoothGraphPath(); - public PathFinderRequest request; - - public SpawnPoint(Tile start){ - this.start = start; - } -} diff --git a/core/src/io/anuke/mindustry/game/Stats.java b/core/src/io/anuke/mindustry/game/Stats.java new file mode 100644 index 0000000000..ebaa62dfc6 --- /dev/null +++ b/core/src/io/anuke/mindustry/game/Stats.java @@ -0,0 +1,70 @@ +package io.anuke.mindustry.game; + +import io.anuke.annotations.Annotations.Serialize; +import io.anuke.arc.collection.Array; +import io.anuke.arc.collection.ObjectIntMap; +import io.anuke.arc.math.Mathf; +import io.anuke.mindustry.type.*; + +@Serialize +public class Stats{ + /** Items delivered to global resoure counter. Zones only. */ + public transient ObjectIntMap itemsDelivered = new ObjectIntMap<>(); + /** Enemy (red team) units destroyed. */ + public int enemyUnitsDestroyed; + /** Total waves lasted. */ + public int wavesLasted; + /** Total (ms) time lasted in this save/zone. */ + public long timeLasted; + /** Friendly buildings fully built. */ + public int buildingsBuilt; + /** Friendly buildings fully deconstructed. */ + public int buildingsDeconstructed; + /** Friendly buildings destroyed. */ + public int buildingsDestroyed; + + public RankResult calculateRank(Zone zone, boolean launched){ + float score = 0; + + //each new launch period adds onto the rank 'points' + if(wavesLasted >= zone.conditionWave){ + score += (float)((wavesLasted - zone.conditionWave) / zone.launchPeriod + 1) * 1.2f; + } + + int capacity = zone.loadout.core().itemCapacity; + + //weigh used fractions + float frac = 0f; + Array obtainable = Array.with(zone.resources).select(i -> i.type == ItemType.material); + for(Item item : obtainable){ + frac += Mathf.clamp((float)itemsDelivered.get(item, 0) / capacity) / (float)obtainable.size; + } + + score += frac * 1.6f; + + if(!launched){ + score *= 0.5f; + } + + int rankIndex = Mathf.clamp((int)(score), 0, Rank.values().length - 1); + Rank rank = Rank.values()[rankIndex]; + String sign = Math.abs((rankIndex + 0.5f) - score) < 0.2f || rank.name().contains("S") ? "" : (rankIndex + 0.5f) < score ? "-" : "+"; + + return new RankResult(rank, sign); + } + + public static class RankResult{ + public final Rank rank; + /** + or - */ + public final String modifier; + + public RankResult(Rank rank, String modifier){ + this.rank = rank; + this.modifier = modifier; + } + } + + public enum Rank{ + F, D, C, B, A, S, SS + } +} diff --git a/core/src/io/anuke/mindustry/game/Team.java b/core/src/io/anuke/mindustry/game/Team.java new file mode 100644 index 0000000000..a5193e48b2 --- /dev/null +++ b/core/src/io/anuke/mindustry/game/Team.java @@ -0,0 +1,26 @@ +package io.anuke.mindustry.game; + +import io.anuke.arc.Core; +import io.anuke.arc.graphics.Color; + +public enum Team{ + none(Color.valueOf("4d4e58")), + blue(Color.valueOf("4169e1")), + red(Color.valueOf("e84737")), + green(Color.valueOf("1dc645")), + purple(Color.valueOf("ba5bd9")), + orange(Color.valueOf("e8c66a")); + + public final static Team[] all = values(); + public final Color color; + public final int intColor; + + Team(Color color){ + this.color = color; + intColor = Color.rgba8888(color); + } + + public String localized(){ + return Core.bundle.get("team." + name() + ".name"); + } +} diff --git a/core/src/io/anuke/mindustry/game/Teams.java b/core/src/io/anuke/mindustry/game/Teams.java new file mode 100644 index 0000000000..be4d1a7e2c --- /dev/null +++ b/core/src/io/anuke/mindustry/game/Teams.java @@ -0,0 +1,69 @@ +package io.anuke.mindustry.game; + +import io.anuke.annotations.Annotations.Struct; +import io.anuke.arc.collection.*; +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.world.Tile; + +/** Class for various team-based utilities. */ +public class Teams{ + private TeamData[] map = new TeamData[Team.all.length]; + + /** + * Register a team. + * @param team The team type enum. + * @param enemies The array of enemies of this team. Any team not in this array is considered neutral. + */ + public void add(Team team, Team... enemies){ + map[team.ordinal()] = new TeamData(team, EnumSet.of(enemies)); + } + + /** Returns team data by type. */ + public TeamData get(Team team){ + if(map[team.ordinal()] == null){ + add(team, Array.with(Team.all).select(t -> t != team).toArray(Team.class)); + } + return map[team.ordinal()]; + } + + /** Returns whether a team is active, e.g. whether it has any cores remaining. */ + public boolean isActive(Team team){ + //the enemy wave team is always active + return team == Vars.waveTeam || get(team).cores.size > 0; + } + + /** Returns a set of all teams that are enemies of this team. */ + public EnumSet enemiesOf(Team team){ + return get(team).enemies; + } + + /** Returns whether {@param other} is an enemy of {@param #team}. */ + public boolean areEnemies(Team team, Team other){ + return enemiesOf(team).contains(other); + } + + /** Allocates a new array with the active teams. + * Never call in the main game loop.*/ + public Array getActive(){ + return Array.select(map, t -> t != null); + } + + public static class TeamData{ + public final ObjectSet cores = new ObjectSet<>(); + public final EnumSet enemies; + public final Team team; + public LongQueue brokenBlocks = new LongQueue(); + + public TeamData(Team team, EnumSet enemies){ + this.team = team; + this.enemies = enemies; + } + } + + /** Represents a block made by this team that was destroyed somewhere on the map. + * This does not include deconstructed blocks.*/ + @Struct + public class BrokenBlockStruct{ + public short x, y, rotation, block; + } +} diff --git a/core/src/io/anuke/mindustry/game/Tutorial.java b/core/src/io/anuke/mindustry/game/Tutorial.java deleted file mode 100644 index 538aad606e..0000000000 --- a/core/src/io/anuke/mindustry/game/Tutorial.java +++ /dev/null @@ -1,604 +0,0 @@ -package io.anuke.mindustry.game; - -import com.badlogic.gdx.math.GridPoint2; -import io.anuke.mindustry.core.GameState.State; -import io.anuke.mindustry.resource.Item; -import io.anuke.mindustry.world.Block; -import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.blocks.*; -import io.anuke.ucore.core.Timers; -import io.anuke.ucore.scene.builders.button; -import io.anuke.ucore.scene.builders.label; -import io.anuke.ucore.scene.builders.table; -import io.anuke.ucore.scene.ui.ImageButton; -import io.anuke.ucore.scene.ui.Label; -import io.anuke.ucore.scene.ui.TextButton; -import io.anuke.ucore.util.Bundles; -import io.anuke.ucore.util.Mathf; -import io.anuke.ucore.util.Tmp; - -import static io.anuke.mindustry.Vars.*; - -public class Tutorial{ - private Stage stage; - private Label info; - private TextButton next, prev; - - public Tutorial(){ - reset(); - } - - public boolean active(){ - return world.getMap() != null && world.getMap().name.equals("tutorial") && !state.is(State.menu); - } - - public void buildUI(table table){ - - table.atop(); - - new table("pane"){{ - atop(); - margin(12); - - info = new label(()->stage.text).pad(10f).padBottom(5f).width(340f).colspan(2).get(); - info.setWrap(true); - - row(); - - prev = new button("$text.tutorial.back", ()->{ - if(!prev.isDisabled()) - move(false); - }).left().get(); - - next = new button("$text.tutorial.next", ()->{ - if(!next.isDisabled()) - move(true); - }).right().get(); - - - }}.end(); - - prev.margin(16); - next.margin(16); - - prev.setDisabled(()->!canMove(false) || !stage.canBack); - next.setDisabled(()->!stage.canForward); - } - - public void update(){ - stage.update(this); - //info.setText(stage.text); - - if(stage.showBlock){ - Tile tile = world.tile(world.getCore().x + stage.blockPlaceX, world.getCore().y + stage.blockPlaceY); - - if(tile.block() == stage.targetBlock && (tile.getRotation() == stage.blockRotation || stage.blockRotation == -1)){ - move(true); - } - } - } - - public void reset(){ - stage = Stage.values()[0]; - stage.onSwitch(); - } - - public void complete(){ - //new TextDialog("Congratulations!", "You have completed the tutorial!").padText(Unit.dp.inPixels(10f)).show(); - state.set(State.menu); - reset(); - } - - void move(boolean forward){ - - if(forward && !canMove(forward)){ - complete(); - }else{ - int current = stage.ordinal(); - - while(true){ - current += Mathf.sign(forward); - - if(current < 0 || current >= Stage.values().length){ - break; - }else if(mobile == Stage.values()[current].androidOnly || mobile != Stage.values()[current].desktopOnly){ - stage = Stage.values()[current]; - stage.onSwitch(); - break; - } - } - } - } - - boolean canMove(boolean forward){ - int current = stage.ordinal(); - - while(true){ - current += Mathf.sign(forward); - - if(current < 0 || current >= Stage.values().length){ - return false; - }else if(mobile == Stage.values()[current].androidOnly || mobile != Stage.values()[current].desktopOnly){ - return true; - } - } - - } - - public boolean showTarget(){ - return stage == Stage.shoot; - } - - public boolean canPlace(){ - return stage.canPlace; - } - - public boolean showBlock(){ - return stage.showBlock; - } - - public Block getPlaceBlock(){ - return stage.targetBlock; - } - - public GridPoint2 getPlacePoint(){ - return Tmp.g1.set(stage.blockPlaceX, stage.blockPlaceY); - } - - public int getPlaceRotation(){ - return stage.blockRotation; - } - - public void setDefaultBlocks(int corex, int corey){ - world.tile(corex, corey - 2).setBlock(Blocks.air); - world.tile(corex, corey - 3).setBlock(Blocks.air); - world.tile(corex, corey - 3).setFloor(Blocks.stone); - - world.tile(corex + 1, corey - 8).setFloor(Blocks.iron); - world.tile(corex - 1, corey - 8).setFloor(Blocks.coal); - - int r = 10; - - for(int x = -r; x <= r; x ++){ - for(int y = -r; y <= r; y ++){ - if(world.tile(corex + x, corey + y).block() == Blocks.rock){ - world.tile(corex + x, corey + y).setBlock(Blocks.air); - } - } - } - } - - public enum Stage{ - intro{ - { - } - }, - moveDesktop{ - { - desktopOnly = true; - } - }, - shoot{ - { - desktopOnly = true; - } - }, - moveAndroid{ - { - androidOnly = true; - } - }, - placeSelect{ - { - canBack = false; - canPlace = true; - } - - void onSwitch(){ - ui.find("sectionbuttondistribution").fireClick(); - } - }, - placeConveyorDesktop{ - { - desktopOnly = true; - canPlace = true; - showBlock = true; - canForward = false; - blockRotation = 1; - blockPlaceX = 0; - blockPlaceY = -2; - targetBlock = DistributionBlocks.conveyor; - } - }, - placeConveyorAndroid{ - { - androidOnly = true; - canPlace = true; - showBlock = true; - canForward = false; - blockRotation = 1; - blockPlaceX = 0; - blockPlaceY = -2; - targetBlock = DistributionBlocks.conveyor; - } - }, - placeConveyorAndroidInfo{ - { - androidOnly = true; - canBack = false; - } - - void onSwitch(){ - //player.recipe = null; - } - }, - placeDrill{ - { - canPlace = true; - canBack = false; - showBlock = true; - canForward = false; - blockPlaceX = 0; - blockPlaceY = -3; - targetBlock = ProductionBlocks.stonedrill; - } - - void onSwitch(){ - ui.find("sectionbuttonproduction").fireClick(); - } - }, - blockInfo{ - { - canBack = true; - } - }, - deselectDesktop{ - { - desktopOnly = true; - canBack = false; - } - }, - deselectAndroid{ - { - androidOnly = true; - canBack = false; - } - }, - drillPlaced{ - { - canBack = false; - } - - void onSwitch(){ - control.input().recipe = null; - } - }, - drillInfo{ - { - } - }, - drillPlaced2{ - { - } - }, - moreDrills{ - { - canBack = false; - } - - void onSwitch(){ - for(int flip : new int[]{1, -1}){ - world.tile(world.getCore().x + flip, world.getCore().y - 2).setBlock(DistributionBlocks.conveyor, 2 * flip); - world.tile(world.getCore().x + flip*2, world.getCore().y - 2).setBlock(DistributionBlocks.conveyor, 2 * flip); - world.tile(world.getCore().x + flip*2, world.getCore().y - 3).setBlock(DistributionBlocks.conveyor, 2 * flip); - world.tile(world.getCore().x + flip*2, world.getCore().y - 3).setBlock(DistributionBlocks.conveyor, 1); - world.tile(world.getCore().x + flip*2, world.getCore().y - 4).setFloor(Blocks.stone); - world.tile(world.getCore().x + flip*2, world.getCore().y - 4).setBlock(ProductionBlocks.stonedrill); - - } - } - }, - deleteBlock{ - { - canBack = false; - canForward = false; - showBlock = true; - targetBlock = Blocks.air; - blockPlaceX = 2; - blockPlaceY = -2; - desktopOnly = true; - } - }, - deleteBlockAndroid{ - { - canBack = false; - canForward = false; - showBlock = true; - targetBlock = Blocks.air; - blockPlaceX = 2; - blockPlaceY = -2; - androidOnly = true; - } - }, - placeTurret{ - { - canBack = false; - canForward = false; - showBlock = true; - canPlace = true; - targetBlock = WeaponBlocks.turret; - blockPlaceX = 2; - blockPlaceY = 2; - } - - void onSwitch(){ - ui.find("sectionbuttonweapon").fireClick(); - } - }, - placedTurretAmmo{ - { - canBack = false; - } - - void onSwitch(){ - for(int i = 0; i < 4; i ++){ - world.tile(world.getCore().x + 2, world.getCore().y - 2 + i).setBlock(DistributionBlocks.conveyor, 1); - } - - control.input().recipe = null; - } - }, - turretExplanation{ - { - canBack = false; - } - }, - waves{ - { - } - }, - coreDestruction{ - { - } - }, - pausingDesktop{ - { - desktopOnly = true; - } - }, - pausingAndroid{ - { - androidOnly = true; - } - }, - //TODO re-add tutorial on weapons - - spawnWave{ - float warmup = 0f; - { - canBack = false; - canForward = false; - } - - void update(Tutorial t){ - warmup += Timers.delta(); - if(state.enemies == 0 && warmup > 60f){ - t.move(true); - } - } - - void onSwitch(){ - warmup = 0f; - logic.runWave(); - } - }, - pumpDesc{ - { - canBack = false; - } - }, - pumpPlace{ - { - canBack = false; - canForward = false; - showBlock = true; - canPlace = true; - targetBlock = ProductionBlocks.pump; - blockPlaceX = 6; - blockPlaceY = -2; - } - - void onSwitch(){ - ui.find("sectionbuttonproduction").fireClick(); - state.inventory.addItem(Item.steel, 60); - state.inventory.addItem(Item.iron, 60); - } - }, - conduitUse{ - { - canBack = false; - canForward = false; - showBlock = true; - canPlace = true; - targetBlock = DistributionBlocks.conduit; - blockPlaceX = 5; - blockPlaceY = -2; - blockRotation = 2; - } - - void onSwitch(){ - ui.find("sectionbuttondistribution").fireClick(); - world.tile(blockPlaceX + world.getCore().x, blockPlaceY + world.getCore().y).setBlock(Blocks.air); - } - }, - conduitUse2{ - { - canBack = false; - canForward = false; - showBlock = true; - canPlace = true; - targetBlock = DistributionBlocks.conduit; - blockPlaceX = 4; - blockPlaceY = -2; - blockRotation = 1; - } - - void onSwitch(){ - world.tile(blockPlaceX + world.getCore().x, blockPlaceY + world.getCore().y).setBlock(Blocks.air); - } - }, - conduitUse3{ - { - canBack = false; - canForward = false; - showBlock = true; - canPlace = true; - targetBlock = DistributionBlocks.conduit; - blockPlaceX = 4; - blockPlaceY = -1; - blockRotation = 1; - } - - void onSwitch(){ - world.tile(blockPlaceX + world.getCore().x, blockPlaceY + world.getCore().y).setBlock(Blocks.air); - } - }, - generator{ - { - canBack = false; - canForward = false; - showBlock = true; - canPlace = true; - targetBlock = ProductionBlocks.combustiongenerator; - blockPlaceX = 4; - blockPlaceY = 0; - } - - void onSwitch(){ - world.tile(blockPlaceX + world.getCore().x, blockPlaceY + world.getCore().y).setBlock(Blocks.air); - ui.find("sectionbuttonpower").fireClick(); - state.inventory.addItem(Item.steel, 60); - state.inventory.addItem(Item.iron, 60); - } - }, - generatorExplain{ - { - canBack = false; - } - }, - lasers{ - { - canBack = false; - canForward = false; - showBlock = true; - canPlace = true; - blockPlaceX = 4; - blockPlaceY = 4; - blockRotation = 2; - targetBlock = DistributionBlocks.powerlaser; - } - - void onSwitch(){ - ui.find("sectionbuttonpower").fireClick(); - } - }, - laserExplain{ - { - canBack = false; - } - }, - laserMore{ - { - canBack = false; - } - }, - healingTurret{ - { - canBack = false; - canForward = false; - showBlock = true; - canPlace = true; - canBack = false; - blockPlaceX = 1; - blockPlaceY = 4; - targetBlock = DefenseBlocks.repairturret; - } - - void onSwitch(){ - ui.find("sectionbuttonpower").fireClick(); - } - }, - healingTurretExplain{ - { - canBack = false; - } - }, - smeltery{ - { - canBack = false; - canForward = false; - showBlock = true; - canPlace = true; - canBack = false; - blockPlaceX = 0; - blockPlaceY = -7; - targetBlock = ProductionBlocks.smelter; - } - - void onSwitch(){ - state.inventory.addItem(Item.stone, 40); - state.inventory.addItem(Item.iron, 40); - ui.find("sectionbuttoncrafting").fireClick(); - - } - }, - smelterySetup{ - { - canBack = false; - } - - void onSwitch(){ - for(int i = 0; i < 5; i ++){ - world.tile(world.getCore().x, world.getCore().y - 6 + i).setBlock(DistributionBlocks.conveyor, 1); - } - - world.tile(world.getCore().x, world.getCore().y - 6 + 1).setBlock(DistributionBlocks.tunnel, 3); - world.tile(world.getCore().x, world.getCore().y - 6 + 2).setBlock(DefenseBlocks.stonewall, 0); - world.tile(world.getCore().x, world.getCore().y - 6 + 3).setBlock(DistributionBlocks.tunnel, 1); - - world.tile(world.getCore().x+1, world.getCore().y - 8).setBlock(ProductionBlocks.irondrill); - world.tile(world.getCore().x-1, world.getCore().y - 8).setBlock(ProductionBlocks.coaldrill); - - world.tile(world.getCore().x+1, world.getCore().y - 7).setBlock(DistributionBlocks.conveyor, 2); - world.tile(world.getCore().x-1, world.getCore().y - 7).setBlock(DistributionBlocks.conveyor, 0); - } - }, - tunnelExplain{ - { - canBack = false; - } - }, - end{ - { - canBack = false; - } - }; - public final String text = Bundles.getNotNull("tutorial."+name()+".text"); - - boolean androidOnly; - boolean desktopOnly; - - boolean canBack = true; - boolean canForward = true; - boolean canPlace = false; - boolean showBlock = false; - - int blockPlaceX = 0; - int blockPlaceY = 0; - int blockRotation = -1; - Block targetBlock = null; - - void update(Tutorial t){}; - void onSwitch(){} - } -} diff --git a/core/src/io/anuke/mindustry/game/UnlockableContent.java b/core/src/io/anuke/mindustry/game/UnlockableContent.java new file mode 100644 index 0000000000..24edd13949 --- /dev/null +++ b/core/src/io/anuke/mindustry/game/UnlockableContent.java @@ -0,0 +1,51 @@ +package io.anuke.mindustry.game; + +import io.anuke.arc.Core; +import io.anuke.arc.graphics.g2d.TextureRegion; +import io.anuke.arc.scene.ui.layout.Table; +import io.anuke.mindustry.Vars; + +/** Base interface for an unlockable content type. */ +public abstract class UnlockableContent extends MappableContent{ + /** Localized, formal name. Never null. Set to block name if not found in bundle. */ + public String localizedName; + /** Localized description. May be null. */ + public String description; + + public UnlockableContent(String name){ + super(name); + + this.localizedName = Core.bundle.get(getContentType() + "." + name + ".name", name); + this.description = Core.bundle.getOrNull(getContentType() + "." + name + ".description"); + } + + /** Returns the localized name of this content. */ + public abstract String localizedName(); + + public abstract TextureRegion getContentIcon(); + + /** This should show all necessary info about this content in the specified table. */ + public abstract void displayInfo(Table table); + + /** Called when this content is unlocked. Use this to unlock other related content. */ + public void onUnlock(){ + } + + /** Whether this content is always hidden in the content info dialog. */ + public boolean isHidden(){ + return false; + } + + /** Override to make content always unlocked. */ + public boolean alwaysUnlocked(){ + return false; + } + + public final boolean unlocked(){ + return Vars.data.isUnlocked(this); + } + + public final boolean locked(){ + return !unlocked(); + } +} diff --git a/core/src/io/anuke/mindustry/game/UpgradeInventory.java b/core/src/io/anuke/mindustry/game/UpgradeInventory.java deleted file mode 100644 index 24fadf2d12..0000000000 --- a/core/src/io/anuke/mindustry/game/UpgradeInventory.java +++ /dev/null @@ -1,25 +0,0 @@ -package io.anuke.mindustry.game; - -import com.badlogic.gdx.utils.Array; -import io.anuke.mindustry.resource.Weapon; - -public class UpgradeInventory { - private final Array weapons = new Array<>(); - - public boolean hasWeapon(Weapon weapon){ - return weapons.contains(weapon, true); - } - - public void addWeapon(Weapon weapon){ - weapons.add(weapon); - } - - public Array getWeapons(){ - return weapons; - } - - public void reset(){ - weapons.clear(); - weapons.add(Weapon.blaster); - } -} diff --git a/core/src/io/anuke/mindustry/game/Version.java b/core/src/io/anuke/mindustry/game/Version.java new file mode 100644 index 0000000000..77a14dac1a --- /dev/null +++ b/core/src/io/anuke/mindustry/game/Version.java @@ -0,0 +1,53 @@ +package io.anuke.mindustry.game; + +import io.anuke.arc.Core; +import io.anuke.arc.collection.ObjectMap; +import io.anuke.arc.files.FileHandle; +import io.anuke.arc.util.Strings; +import io.anuke.arc.util.io.PropertiesUtils; + +import java.io.IOException; + +public class Version{ + /** Build type. 'official' for official releases; 'custom' or 'bleeding edge' are also used. */ + public static String type; + /** Build modifier, e.g. 'alpha' or 'release' */ + public static String modifier; + /** Number specifying the major version, e.g. '4' */ + public static int number; + /** Build number, e.g. '43'. set to '-1' for custom builds. */ + public static int build = 0; + /** Revision number. Used for hotfixes. Does not affect server compatibility. */ + public static int revision = 0; + /** Whether version loading is enabled. */ + public static boolean enabled = true; + + public static void init(){ + if(!enabled) return; + + try{ + FileHandle file = Core.files.internal("version.properties"); + + ObjectMap map = new ObjectMap<>(); + PropertiesUtils.load(map, file.reader()); + + type = map.get("type"); + number = Integer.parseInt(map.get("number")); + modifier = map.get("modifier"); + if(map.get("build").contains(".")){ + String[] split = map.get("build").split("\\."); + try{ + build = Integer.parseInt(split[0]); + revision = Integer.parseInt(split[1]); + }catch(Throwable e){ + e.printStackTrace(); + build = -1; + } + }else{ + build = Strings.canParseInt(map.get("build")) ? Integer.parseInt(map.get("build")) : -1; + } + }catch(IOException e){ + throw new RuntimeException(e); + } + } +} diff --git a/core/src/io/anuke/mindustry/game/WaveCreator.java b/core/src/io/anuke/mindustry/game/WaveCreator.java deleted file mode 100644 index b44fcbf98e..0000000000 --- a/core/src/io/anuke/mindustry/game/WaveCreator.java +++ /dev/null @@ -1,148 +0,0 @@ -package io.anuke.mindustry.game; - -import com.badlogic.gdx.utils.Array; -import io.anuke.mindustry.entities.enemies.EnemyTypes; - -public class WaveCreator{ - - public static Array getSpawns(){ - - return Array.with( - new EnemySpawn(EnemyTypes.standard){{ - scaling = 1; - before = 3; - }}, - - new EnemySpawn(EnemyTypes.fast){{ - scaling = 1; - after = 3; - spacing = 5; - amount = 3; - tierscaleback = 0; - }}, - - new EnemySpawn(EnemyTypes.blast){{ - after = 4; - amount = 2; - spacing = 5; - scaling = 2; - tierscaleback = 1; - }}, - - new EnemySpawn(EnemyTypes.tank){{ - after = 5; - spacing = 5; - scaling = 2; - amount = 2; - }}, - - new EnemySpawn(EnemyTypes.rapid){{ - after = 7; - spacing = 5; - scaling = 2; - amount = 3; - }}, - - new EnemySpawn(EnemyTypes.healer){{ - after = 5; - spacing = 5; - scaling = 1; - amount = 1; - }}, - - new EnemySpawn(EnemyTypes.standard){{ - scaling = 3; - after = 8; - spacing = 4; - tier = 2; - }}, - - new EnemySpawn(EnemyTypes.titan){{ - after = 6; - amount = 2; - spacing = 5; - scaling = 3; - }}, - - new EnemySpawn(EnemyTypes.flamer){{ - after = 12; - amount = 2; - spacing = 5; - scaling = 3; - }}, - - new EnemySpawn(EnemyTypes.emp){{ - after = 15; - amount = 1; - spacing = 5; - scaling = 2; - }}, - - new EnemySpawn(EnemyTypes.blast){{ - after = 4 + 5 + 5; - amount = 3; - spacing = 5; - scaling = 2; - tierscaleback = 0; - }}, - //boss wave - new EnemySpawn(EnemyTypes.fortress){{ - after = 16; - amount = 1; - spacing = 5; - scaling = 1; - }}, - - new EnemySpawn(EnemyTypes.titan){{ - after = 16; - amount = 1; - spacing = 5; - scaling = 3; - tierscaleback = 0; - }}, - - new EnemySpawn(EnemyTypes.healer){{ - after = 16; - spacing = 5; - scaling = 2; - amount = 2; - }}, - //end boss wave - - //enchanced boss wave - new EnemySpawn(EnemyTypes.mortar){{ - after = 16 + 5; - amount = 1; - spacing = 5; - scaling = 3; - }}, - - new EnemySpawn(EnemyTypes.emp){{ - after = 16 + 5; - amount = 1; - spacing = 5; - scaling = 3; - }} - //end enchanced boss wave - ); - } - - public static void testWaves(int from, int to){ - Array spawns = getSpawns(); - for(int i = from; i <= to; i ++){ - System.out.print(i+": "); - int total = 0; - for(EnemySpawn spawn : spawns){ - int a = spawn.evaluate(i, 0); - int t = spawn.tier(i, 0); - total += a; - - if(a > 0){ - System.out.print(a+"x" + spawn.type.name + "-" + t + " "); - } - } - System.out.print(" (" + total + ")"); - System.out.println(); - } - } -} diff --git a/core/src/io/anuke/mindustry/graphics/BlockRenderer.java b/core/src/io/anuke/mindustry/graphics/BlockRenderer.java index 7d94d46476..50d904682d 100644 --- a/core/src/io/anuke/mindustry/graphics/BlockRenderer.java +++ b/core/src/io/anuke/mindustry/graphics/BlockRenderer.java @@ -1,331 +1,327 @@ package io.anuke.mindustry.graphics; -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.graphics.GL20; -import com.badlogic.gdx.graphics.OrthographicCamera; -import com.badlogic.gdx.utils.Array; -import io.anuke.mindustry.core.GameState.State; -import io.anuke.mindustry.game.SpawnPoint; +import io.anuke.arc.Core; +import io.anuke.arc.Events; +import io.anuke.arc.collection.Array; +import io.anuke.arc.collection.Sort; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.graphics.Texture.TextureFilter; +import io.anuke.arc.graphics.g2d.Draw; +import io.anuke.arc.graphics.g2d.Fill; +import io.anuke.arc.graphics.glutils.FrameBuffer; +import io.anuke.arc.util.Disposable; +import io.anuke.arc.util.Tmp; +import io.anuke.mindustry.content.Blocks; +import io.anuke.mindustry.game.EventType.TileChangeEvent; +import io.anuke.mindustry.game.EventType.WorldLoadEvent; +import io.anuke.mindustry.game.Team; import io.anuke.mindustry.world.Block; -import io.anuke.mindustry.world.Layer; import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.blocks.Blocks; -import io.anuke.mindustry.world.blocks.types.StaticBlock; -import io.anuke.mindustry.world.blocks.types.defense.Turret; -import io.anuke.ucore.core.Core; -import io.anuke.ucore.core.Graphics; -import io.anuke.ucore.core.Settings; -import io.anuke.ucore.graphics.CacheBatch; -import io.anuke.ucore.graphics.Draw; -import io.anuke.ucore.graphics.Lines; -import io.anuke.ucore.util.Mathf; -import java.util.Arrays; +import static io.anuke.arc.Core.camera; import static io.anuke.mindustry.Vars.*; -import static io.anuke.ucore.core.Core.camera; -public class BlockRenderer{ - private final static int chunksize = 32; - private final static int initialRequests = 32*32; - private static float storeX = 0; - private static float storeY = 0; - - private int[][][] cache; - private CacheBatch cbatch; - - private Array requests = new Array(initialRequests); - private int requestidx = 0; - private int iterateidx = 0; - - public BlockRenderer(){ - for(int i = 0; i < requests.size; i ++){ - requests.set(i, new BlockRequest()); - } - } - - private class BlockRequest implements Comparable{ - Tile tile; - Layer layer; - - @Override - public int compareTo(BlockRequest other){ - return layer.compareTo(other.layer); - } - - @Override - public String toString(){ - return tile.block().name + ":" + layer.toString(); - } - } - - /**Process all blocks to draw, simultaneously drawing block shadows and static blocks.*/ - public void processBlocks(){ - requestidx = 0; - - int crangex = (int) (camera.viewportWidth / (chunksize * tilesize)) + 1; - int crangey = (int) (camera.viewportHeight / (chunksize * tilesize)) + 1; - - int rangex = (int) (camera.viewportWidth * camera.zoom / tilesize / 2)+2; - int rangey = (int) (camera.viewportHeight * camera.zoom / tilesize / 2)+2; - - int expandr = 3; - - Graphics.surface(renderer.shadowSurface); - - for(int x = -rangex - expandr; x <= rangex + expandr; x++){ - for(int y = -rangey - expandr; y <= rangey + expandr; y++){ - int worldx = Mathf.scl(camera.position.x, tilesize) + x; - int worldy = Mathf.scl(camera.position.y, tilesize) + y; - boolean expanded = (x < -rangex || x > rangex || y < -rangey || y > rangey); - - Tile tile = world.tile(worldx, worldy); - - if(tile != null){ - Block block = tile.block(); - - if(!expanded && block != Blocks.air && world.isAccessible(worldx, worldy)){ - block.drawShadow(tile); - } - - if(!(block instanceof StaticBlock)){ - if(block == Blocks.air){ - if(!state.is(State.paused)) tile.floor().update(tile); - }else{ - - if(!expanded){ - addRequest(tile, Layer.block); - } - - if(block.expanded || !expanded){ - if(block.layer != null && block.isLayer(tile)){ - addRequest(tile, block.layer); - } - - if(block.layer2 != null && block.isLayer2(tile)){ - addRequest(tile, block.layer2); - } - } - } - } - } - } - } - - Draw.color(0, 0, 0, 0.15f); - Graphics.flushSurface(); - Draw.color(); - - Graphics.end(); - drawCache(1, crangex, crangey); - Graphics.begin(); - - Arrays.sort(requests.items, 0, requestidx); - iterateidx = 0; - } - - public int getRequests(){ - return requestidx; - } - - public void drawBlocks(boolean top){ - Layer stopAt = top ? Layer.laser : Layer.overlay; - - for(; iterateidx < requestidx; iterateidx ++){ - - if(iterateidx < requests.size - 1 && requests.get(iterateidx).layer.ordinal() > stopAt.ordinal()){ - break; - } - - BlockRequest req = requests.get(iterateidx); - Block block = req.tile.block(); - - if(req.layer == Layer.block){ - block.draw(req.tile); - }else if(req.layer == block.layer){ - block.drawLayer(req.tile); - }else if(req.layer == block.layer2){ - block.drawLayer2(req.tile); - } - } - } - - private void addRequest(Tile tile, Layer layer){ - if(requestidx >= requests.size){ - requests.add(new BlockRequest()); - } - BlockRequest r = requests.get(requestidx); - if(r == null){ - requests.set(requestidx, r = new BlockRequest()); - } - r.tile = tile; - r.layer = layer; - requestidx ++; - } - - public void drawFloor(){ - int chunksx = world.width() / chunksize, chunksy = world.height() / chunksize; - - //render the entire map - if(cache == null || cache.length != chunksx || cache[0].length != chunksy){ - cache = new int[chunksx][chunksy][2]; - - for(int x = 0; x < chunksx; x++){ - for(int y = 0; y < chunksy; y++){ - cacheChunk(x, y, true); - cacheChunk(x, y, false); - } - } - } - - OrthographicCamera camera = Core.camera; - - if(Graphics.drawing()) Graphics.end(); - - int crangex = (int)(camera.viewportWidth * camera.zoom / (chunksize * tilesize))+1; - int crangey = (int)(camera.viewportHeight * camera.zoom / (chunksize * tilesize))+1; - - drawCache(0, crangex, crangey); - - Graphics.begin(); - - Draw.reset(); - - if(showPaths && debug){ - drawPaths(); - } - - if(debug && debugChunks){ - Draw.color(Color.YELLOW); - Lines.stroke(1f); - for(int x = -crangex; x <= crangex; x++){ - for(int y = -crangey; y <= crangey; y++){ - int worldx = Mathf.scl(camera.position.x, chunksize * tilesize) + x; - int worldy = Mathf.scl(camera.position.y, chunksize * tilesize) + y; - - if(!Mathf.inBounds(worldx, worldy, cache)) - continue; - Lines.rect(worldx * chunksize * tilesize, worldy * chunksize * tilesize, chunksize * tilesize, chunksize * tilesize); - } - } - Draw.reset(); - } - } - - void drawPaths(){ - Draw.color(Color.RED); - for(SpawnPoint point : world.getSpawns()){ - if(point.pathTiles != null){ - for(int i = 1; i < point.pathTiles.length; i ++){ - Lines.line(point.pathTiles[i-1].worldx(), point.pathTiles[i-1].worldy(), - point.pathTiles[i].worldx(), point.pathTiles[i].worldy()); - Lines.circle(point.pathTiles[i-1].worldx(), point.pathTiles[i-1].worldy(), 6f); - } - } - } - Draw.reset(); - } - - - void drawCache(int layer, int crangex, int crangey){ - Gdx.gl.glEnable(GL20.GL_BLEND); - - cbatch.setProjectionMatrix(Core.camera.combined); - cbatch.beginDraw(); - for(int x = -crangex; x <= crangex; x++){ - for(int y = -crangey; y <= crangey; y++){ - int worldx = Mathf.scl(camera.position.x, chunksize * tilesize) + x; - int worldy = Mathf.scl(camera.position.y, chunksize * tilesize) + y; - - if(!Mathf.inBounds(worldx, worldy, cache)) - continue; - - cbatch.drawCache(cache[worldx][worldy][layer]); - } - } - - cbatch.endDraw(); - } - - void cacheChunk(int cx, int cy, boolean floor){ - if(cbatch == null){ - createBatch(); - } - - cbatch.begin(); - Graphics.useBatch(cbatch); - - for(int tilex = cx * chunksize; tilex < (cx + 1) * chunksize; tilex++){ - for(int tiley = cy * chunksize; tiley < (cy + 1) * chunksize; tiley++){ - Tile tile = world.tile(tilex, tiley); - if(tile == null) continue; - if(floor){ - if(!(tile.block() instanceof StaticBlock)){ - tile.floor().draw(tile); - } - }else if(tile.block() instanceof StaticBlock){ - tile.block().draw(tile); - } - } - } - Graphics.popBatch(); - cbatch.end(); - cache[cx][cy][floor ? 0 : 1] = cbatch.getLastCache(); - } - - public void clearTiles(){ - cache = null; - createBatch(); - } - - private void createBatch(){ - if(cbatch != null) - cbatch.dispose(); - cbatch = new CacheBatch(world.width() * world.height() * 4); - } - - public void drawPreview(Block block, float drawx, float drawy, float rotation, float opacity) { - Draw.alpha(opacity); - Draw.rect(block.name(), drawx, drawy, rotation); - } - - public void handlePreview(Block block, float rotation, float drawx, float drawy, int tilex, int tiley) { - - if(control.input().recipe != null && state.inventory.hasItems(control.input().recipe.requirements) - && control.input().validPlace(tilex, tiley, block) && (mobile || control.input().cursorNear())) { - - if(block.isMultiblock()) { - float halfBlockWidth = (block.width * tilesize) / 2; - float halfBlockHeight = (block.height * tilesize) / 2; - if((storeX == 0 && storeY == 0)) { - storeX = drawx; - storeY = drawy; - } - if((storeX == drawx - halfBlockWidth || storeX == drawx + halfBlockWidth || storeY == drawy - halfBlockHeight || storeY == drawy + halfBlockHeight) && - ((tiley - control.input().getBlockY()) % block.height != 0 || (tilex - control.input().getBlockX()) % block.width != 0)) { - return; - } - else { - storeX = drawx; - storeY = drawy; - } - } - - float opacity = (float) Settings.getInt("previewopacity") / 100f; - Draw.color(Color.WHITE); - Draw.alpha(opacity); - - if(block instanceof Turret) { - if (block.isMultiblock()) { - Draw.rect("block-" + block.width + "x" + block.height, drawx, drawy); - } else { - Draw.rect("block", drawx, drawy); - } - } - - drawPreview(block, drawx, drawy, rotation, opacity); - - Draw.reset(); - } - } -} \ No newline at end of file +public class BlockRenderer implements Disposable{ + private final static int initialRequests = 32 * 32; + private final static int expandr = 9; + private final static Color shadowColor = new Color(0, 0, 0, 0.71f); + + public final FloorRenderer floor = new FloorRenderer(); + + private Array requests = new Array<>(true, initialRequests, BlockRequest.class); + private int lastCamX, lastCamY, lastRangeX, lastRangeY; + private int requestidx = 0; + private int iterateidx = 0; + private FrameBuffer shadows = new FrameBuffer(2, 2); + private FrameBuffer fog = new FrameBuffer(2, 2); + private Array outArray = new Array<>(); + private Array shadowEvents = new Array<>(); + + public BlockRenderer(){ + + for(int i = 0; i < requests.size; i++){ + requests.set(i, new BlockRequest()); + } + + Events.on(WorldLoadEvent.class, event -> { + shadowEvents.clear(); + lastCamY = lastCamX = -99; //invalidate camera position so blocks get updated + + shadows.getTexture().setFilter(TextureFilter.Linear, TextureFilter.Linear); + shadows.resize(world.width(), world.height()); + shadows.begin(); + Core.graphics.clear(Color.WHITE); + Draw.proj().setOrtho(0, 0, shadows.getWidth(), shadows.getHeight()); + + Draw.color(shadowColor); + + for(int x = 0; x < world.width(); x++){ + for(int y = 0; y < world.height(); y++){ + Tile tile = world.rawTile(x, y); + if(tile.block().hasShadow){ + Fill.rect(tile.x + 0.5f, tile.y + 0.5f, 1, 1); + } + } + } + + Draw.flush(); + Draw.color(); + shadows.end(); + + fog.getTexture().setFilter(TextureFilter.Linear, TextureFilter.Linear); + fog.resize(world.width(), world.height()); + fog.begin(); + Core.graphics.clear(Color.WHITE); + Draw.proj().setOrtho(0, 0, fog.getWidth(), fog.getHeight()); + + for(int x = 0; x < world.width(); x++){ + for(int y = 0; y < world.height(); y++){ + Tile tile = world.rawTile(x, y); + int edgeBlend = 2; + float rot = tile.rotation(); + boolean fillable = (tile.block().solid && tile.block().fillsTile && !tile.block().synthetic()); + int edgeDst = Math.min(x, Math.min(y, Math.min(Math.abs(x - (world.width() - 1)), Math.abs(y - (world.height() - 1))))); + if(edgeDst <= edgeBlend){ + rot = Math.max((edgeBlend - edgeDst) * (4f / edgeBlend), fillable ? rot : 0); + } + if(rot > 0 && (fillable || edgeDst <= edgeBlend)){ + Draw.color(0f, 0f, 0f, Math.min((rot + 0.5f) / 4f, 1f)); + Fill.rect(tile.x + 0.5f, tile.y + 0.5f, 1, 1); + } + } + } + + Draw.flush(); + Draw.color(); + fog.end(); + }); + + Events.on(TileChangeEvent.class, event -> { + shadowEvents.add(event.tile); + + int avgx = (int)(camera.position.x / tilesize); + int avgy = (int)(camera.position.y / tilesize); + int rangex = (int)(camera.width / tilesize / 2) + 2; + int rangey = (int)(camera.height / tilesize / 2) + 2; + + if(Math.abs(avgx - event.tile.x) <= rangex && Math.abs(avgy - event.tile.y) <= rangey){ + lastCamY = lastCamX = -99; //invalidate camera position so blocks get updated + } + }); + } + + public void drawFog(){ + float ww = world.width() * tilesize, wh = world.height() * tilesize; + float x = camera.position.x + tilesize / 2f, y = camera.position.y + tilesize / 2f; + float u = (x - camera.width / 2f) / ww, + v = (y - camera.height / 2f) / wh, + u2 = (x + camera.width / 2f) / ww, + v2 = (y + camera.height / 2f) / wh; + + Tmp.tr1.set(fog.getTexture()); + Tmp.tr1.set(u, v2, u2, v); + + Draw.shader(Shaders.fog); + Draw.rect(Tmp.tr1, camera.position.x, camera.position.y, camera.width, camera.height); + Draw.shader(); + } + + public void drawShadows(){ + if(!shadowEvents.isEmpty()){ + Draw.flush(); + + shadows.begin(); + Draw.proj().setOrtho(0, 0, shadows.getWidth(), shadows.getHeight()); + + for(Tile tile : shadowEvents){ + //clear it first + Draw.color(Color.WHITE); + Fill.rect(tile.x + 0.5f, tile.y + 0.5f, 1, 1); + //then draw the shadow + Draw.color(!tile.block().hasShadow ? Color.WHITE : shadowColor); + Fill.rect(tile.x + 0.5f, tile.y + 0.5f, 1, 1); + } + + Draw.flush(); + Draw.color(); + shadows.end(); + shadowEvents.clear(); + + Draw.proj(camera.projection()); + renderer.pixelator.rebind(); + } + + float ww = world.width() * tilesize, wh = world.height() * tilesize; + float x = camera.position.x + tilesize / 2f, y = camera.position.y + tilesize / 2f; + float u = (x - camera.width / 2f) / ww, + v = (y - camera.height / 2f) / wh, + u2 = (x + camera.width / 2f) / ww, + v2 = (y + camera.height / 2f) / wh; + + Tmp.tr1.set(shadows.getTexture()); + Tmp.tr1.set(u, v2, u2, v); + + Draw.shader(Shaders.fog); + Draw.rect(Tmp.tr1, camera.position.x, camera.position.y, camera.width, camera.height); + Draw.shader(); + } + + /** Process all blocks to draw. */ + public void processBlocks(){ + iterateidx = 0; + + int avgx = (int)(camera.position.x / tilesize); + int avgy = (int)(camera.position.y / tilesize); + + int rangex = (int)(camera.width / tilesize / 2) + 3; + int rangey = (int)(camera.height / tilesize / 2) + 3; + + if(avgx == lastCamX && avgy == lastCamY && lastRangeX == rangex && lastRangeY == rangey){ + return; + } + + requestidx = 0; + + int minx = Math.max(avgx - rangex - expandr, 0); + int miny = Math.max(avgy - rangey - expandr, 0); + int maxx = Math.min(world.width() - 1, avgx + rangex + expandr); + int maxy = Math.min(world.height() - 1, avgy + rangey + expandr); + + for(int x = minx; x <= maxx; x++){ + for(int y = miny; y <= maxy; y++){ + boolean expanded = (Math.abs(x - avgx) > rangex || Math.abs(y - avgy) > rangey); + Tile tile = world.rawTile(x, y); + if(tile == null) continue; //how is this possible? + Block block = tile.block(); + + if(block != Blocks.air && block.cacheLayer == CacheLayer.normal){ + if(!expanded){ + addRequest(tile, Layer.block); + } + + if(block.expanded || !expanded){ + + if(block.layer != null){ + addRequest(tile, block.layer); + } + + if(block.layer2 != null){ + addRequest(tile, block.layer2); + } + + if(tile.entity != null && tile.entity.power != null && tile.entity.power.links.size > 0){ + for(Tile other : block.getPowerConnections(tile, outArray)){ + if(other.block().layer == Layer.power){ + addRequest(other, Layer.power); + } + } + } + } + } + } + } + + Sort.instance().sort(requests.items, 0, requestidx); + + lastCamX = avgx; + lastCamY = avgy; + lastRangeX = rangex; + lastRangeY = rangey; + } + + public void drawBlocks(Layer stopAt){ + + for(; iterateidx < requestidx; iterateidx++){ + + if(iterateidx < requests.size && requests.get(iterateidx).layer.ordinal() > stopAt.ordinal()){ + break; + } + + BlockRequest req = requests.get(iterateidx); + Block block = req.tile.block(); + + if(req.layer == Layer.block){ + block.draw(req.tile); + if(req.tile.entity != null && req.tile.entity.damaged()){ + block.drawCracks(req.tile); + } + if(block.synthetic() && req.tile.getTeam() != player.getTeam()){ + block.drawTeam(req.tile); + } + }else if(req.layer == block.layer){ + block.drawLayer(req.tile); + }else if(req.layer == block.layer2){ + block.drawLayer2(req.tile); + } + } + } + + public void drawTeamBlocks(Layer layer, Team team){ + int index = this.iterateidx; + + for(; index < requestidx; index++){ + + if(index < requests.size && requests.get(index).layer.ordinal() > layer.ordinal()){ + break; + } + + BlockRequest req = requests.get(index); + if(req.tile.getTeam() != team) continue; + + Block block = req.tile.block(); + + if(req.layer == Layer.block){ + block.draw(req.tile); + }else if(req.layer == block.layer){ + block.drawLayer(req.tile); + }else if(req.layer == block.layer2){ + block.drawLayer2(req.tile); + } + + } + } + + public void skipLayer(Layer stopAt){ + for(; iterateidx < requestidx; iterateidx++){ + if(iterateidx < requests.size && requests.get(iterateidx).layer.ordinal() > stopAt.ordinal()){ + break; + } + } + } + + private void addRequest(Tile tile, Layer layer){ + if(requestidx >= requests.size){ + requests.add(new BlockRequest()); + } + BlockRequest r = requests.get(requestidx); + if(r == null){ + requests.set(requestidx, r = new BlockRequest()); + } + r.tile = tile; + r.layer = layer; + requestidx++; + } + + @Override + public void dispose(){ + shadows.dispose(); + fog.dispose(); + shadows = fog = null; + floor.dispose(); + } + + private class BlockRequest implements Comparable{ + Tile tile; + Layer layer; + + @Override + public int compareTo(BlockRequest other){ + return layer.compareTo(other.layer); + } + + @Override + public String toString(){ + return tile.block().name + ":" + layer.toString(); + } + } +} diff --git a/core/src/io/anuke/mindustry/graphics/CacheLayer.java b/core/src/io/anuke/mindustry/graphics/CacheLayer.java new file mode 100644 index 0000000000..9c96f4d3c5 --- /dev/null +++ b/core/src/io/anuke/mindustry/graphics/CacheLayer.java @@ -0,0 +1,66 @@ +package io.anuke.mindustry.graphics; + +import io.anuke.arc.Core; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.graphics.g2d.Draw; +import io.anuke.arc.graphics.glutils.Shader; + +import static io.anuke.arc.Core.camera; +import static io.anuke.mindustry.Vars.renderer; + +public enum CacheLayer{ + water{ + @Override + public void begin(){ + beginShader(); + } + + @Override + public void end(){ + endShader(Shaders.water); + } + }, + tar{ + @Override + public void begin(){ + beginShader(); + } + + @Override + public void end(){ + endShader(Shaders.tar); + } + }, + normal, + walls; + + public void begin(){ + + } + + public void end(){ + + } + + void beginShader(){ + if(!Core.settings.getBool("animatedwater")) return; + + renderer.blocks.floor.endc(); + renderer.shieldBuffer.begin(); + Core.graphics.clear(Color.CLEAR); + renderer.blocks.floor.beginc(); + } + + void endShader(Shader shader){ + if(!Core.settings.getBool("animatedwater")) return; + + renderer.blocks.floor.endc(); + renderer.shieldBuffer.end(); + + Draw.shader(shader); + Draw.rect(Draw.wrap(renderer.shieldBuffer.getTexture()), camera.position.x, camera.position.y, camera.width, -camera.height); + Draw.shader(); + + renderer.blocks.floor.beginc(); + } +} diff --git a/core/src/io/anuke/mindustry/graphics/FloorRenderer.java b/core/src/io/anuke/mindustry/graphics/FloorRenderer.java new file mode 100644 index 0000000000..06dba70468 --- /dev/null +++ b/core/src/io/anuke/mindustry/graphics/FloorRenderer.java @@ -0,0 +1,235 @@ +package io.anuke.mindustry.graphics; + +import io.anuke.arc.Core; +import io.anuke.arc.Events; +import io.anuke.arc.collection.*; +import io.anuke.arc.collection.IntSet.IntSetIterator; +import io.anuke.arc.graphics.Camera; +import io.anuke.arc.graphics.GL20; +import io.anuke.arc.graphics.g2d.*; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.util.*; +import io.anuke.mindustry.game.EventType.WorldLoadEvent; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.Floor; + +import java.util.Arrays; + +import static io.anuke.mindustry.Vars.tilesize; +import static io.anuke.mindustry.Vars.world; + +public class FloorRenderer implements Disposable{ + private final static int chunksize = 64; + + private Chunk[][] cache; + private CacheBatch cbatch; + private IntSet drawnLayerSet = new IntSet(); + private IntArray drawnLayers = new IntArray(); + private ObjectSet used = new ObjectSet<>(); + + public FloorRenderer(){ + Events.on(WorldLoadEvent.class, event -> clearTiles()); + } + + public void drawFloor(){ + if(cache == null){ + return; + } + + Camera camera = Core.camera; + + int crangex = (int)(camera.width / (chunksize * tilesize)) + 1; + int crangey = (int)(camera.height / (chunksize * tilesize)) + 1; + + int camx = (int)(camera.position.x / (chunksize * tilesize)); + int camy = (int)(camera.position.y / (chunksize * tilesize)); + + int layers = CacheLayer.values().length; + + drawnLayers.clear(); + drawnLayerSet.clear(); + + //preliminary layer check + for(int x = -crangex; x <= crangex; x++){ + for(int y = -crangey; y <= crangey; y++){ + int worldx = camx + x; + int worldy = camy + y; + + if(!Structs.inBounds(worldx, worldy, cache)) + continue; + + Chunk chunk = cache[worldx][worldy]; + + //loop through all layers, and add layer index if it exists + for(int i = 0; i < layers; i++){ + if(chunk.caches[i] != -1 && i != CacheLayer.walls.ordinal()){ + drawnLayerSet.add(i); + } + } + } + } + + IntSetIterator it = drawnLayerSet.iterator(); + while(it.hasNext){ + drawnLayers.add(it.next()); + } + + drawnLayers.sort(); + + Draw.flush(); + beginDraw(); + + for(int i = 0; i < drawnLayers.size; i++){ + CacheLayer layer = CacheLayer.values()[drawnLayers.get(i)]; + + drawLayer(layer); + } + + endDraw(); + } + + public void beginc(){ + cbatch.beginDraw(); + } + + public void endc(){ + cbatch.endDraw(); + } + + public void beginDraw(){ + if(cache == null){ + return; + } + + cbatch.setProjection(Core.camera.projection()); + cbatch.beginDraw(); + + Core.gl.glEnable(GL20.GL_BLEND); + } + + public void endDraw(){ + if(cache == null){ + return; + } + + cbatch.endDraw(); + } + + public void drawLayer(CacheLayer layer){ + if(cache == null){ + return; + } + + Camera camera = Core.camera; + + int crangex = (int)(camera.width / (chunksize * tilesize)) + 1; + int crangey = (int)(camera.height / (chunksize * tilesize)) + 1; + + layer.begin(); + + for(int x = -crangex; x <= crangex; x++){ + for(int y = -crangey; y <= crangey; y++){ + int worldx = (int)(camera.position.x / (chunksize * tilesize)) + x; + int worldy = (int)(camera.position.y / (chunksize * tilesize)) + y; + + if(!Structs.inBounds(worldx, worldy, cache)){ + continue; + } + + Chunk chunk = cache[worldx][worldy]; + if(chunk.caches[layer.ordinal()] == -1) continue; + cbatch.drawCache(chunk.caches[layer.ordinal()]); + } + } + + layer.end(); + } + + private void cacheChunk(int cx, int cy){ + used.clear(); + Chunk chunk = cache[cx][cy]; + + for(int tilex = cx * chunksize; tilex < (cx + 1) * chunksize; tilex++){ + for(int tiley = cy * chunksize; tiley < (cy + 1) * chunksize; tiley++){ + Tile tile = world.tile(tilex, tiley); + + if(tile != null){ + if(tile.block().cacheLayer != CacheLayer.normal){ + used.add(tile.block().cacheLayer); + }else{ + used.add(tile.floor().cacheLayer); + } + } + } + } + + for(CacheLayer layer : used){ + cacheChunkLayer(cx, cy, chunk, layer); + } + } + + private void cacheChunkLayer(int cx, int cy, Chunk chunk, CacheLayer layer){ + SpriteBatch current = Core.batch; + Core.batch = cbatch; + + cbatch.beginCache(); + + for(int tilex = cx * chunksize; tilex < (cx + 1) * chunksize; tilex++){ + for(int tiley = cy * chunksize; tiley < (cy + 1) * chunksize; tiley++){ + Tile tile = world.tile(tilex, tiley); + Floor floor; + + if(tile == null){ + continue; + }else{ + floor = tile.floor(); + } + + if(tile.block().cacheLayer == layer && layer == CacheLayer.walls){ + tile.block().draw(tile); + }else if(floor.cacheLayer == layer && (world.isAccessible(tile.x, tile.y) || tile.block().cacheLayer != CacheLayer.walls || !tile.block().fillsTile)){ + floor.draw(tile); + }else if(floor.cacheLayer.ordinal() < layer.ordinal() && layer != CacheLayer.walls){ + floor.drawNonLayer(tile); + } + } + } + Core.batch = current; + chunk.caches[layer.ordinal()] = cbatch.endCache(); + } + + public void clearTiles(){ + if(cbatch != null) cbatch.dispose(); + + int chunksx = Mathf.ceil((float)(world.width()) / chunksize), + chunksy = Mathf.ceil((float)(world.height()) / chunksize); + cache = new Chunk[chunksx][chunksy]; + SpriteCache sprites = new SpriteCache(world.width() * world.height() * 6, (world.width() / chunksize) * (world.height() / chunksize) * 2, false); + cbatch = new CacheBatch(sprites); + + Time.mark(); + + for(int x = 0; x < chunksx; x++){ + for(int y = 0; y < chunksy; y++){ + cache[x][y] = new Chunk(); + Arrays.fill(cache[x][y].caches, -1); + + cacheChunk(x, y); + } + } + + Log.info("Time to cache: {0}", Time.elapsed()); + } + + @Override + public void dispose(){ + if(cbatch != null){ + cbatch.dispose(); + cbatch = null; + } + } + + private class Chunk{ + int[] caches = new int[CacheLayer.values().length]; + } +} diff --git a/core/src/io/anuke/mindustry/graphics/Fx.java b/core/src/io/anuke/mindustry/graphics/Fx.java deleted file mode 100644 index f1315889a2..0000000000 --- a/core/src/io/anuke/mindustry/graphics/Fx.java +++ /dev/null @@ -1,520 +0,0 @@ -package io.anuke.mindustry.graphics; - -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.graphics.Colors; -import io.anuke.ucore.core.Effects.Effect; -import io.anuke.ucore.graphics.Draw; -import io.anuke.ucore.graphics.Hue; -import io.anuke.ucore.graphics.Lines; -import io.anuke.ucore.graphics.Shapes; -import io.anuke.ucore.util.Angles; -import io.anuke.ucore.util.Mathf; - -import static io.anuke.mindustry.Vars.respawnduration; -import static io.anuke.mindustry.Vars.tilesize; - -public class Fx{ - public static Color lightRed = Hue.mix(Color.WHITE, Color.FIREBRICK, 0.1f); - public static Color lightOrange = Color.valueOf("f68021"); - public static Color lighterOrange = Color.valueOf("f6e096"); - public static Color whiteOrange = Hue.mix(lightOrange, Color.WHITE, 0.6f); - public static Color whiteYellow = Hue.mix(Color.YELLOW, Color.WHITE, 0.6f); - public static Color lightGray = Color.valueOf("b0b0b0"); - public static Color glowy = Color.valueOf("fdc056"); - public static Color beam = Color.valueOf("9bffbe"); - public static Color beamLight = Color.valueOf("ddffe9"); - - public static final Effect - - generatorexplosion = new Effect(28, 40f, e -> { - Angles.randLenVectors(e.id, 16, 10f + e.fin()*8f, (x, y)->{ - float size = e.fout()*12f + 1f; - Draw.color(Color.WHITE, lightOrange, e.fin()); - Draw.rect("circle", e.x + x, e.y + y, size, size); - Draw.reset(); - }); - }), - - reactorsmoke = new Effect(17, e -> { - Angles.randLenVectors(e.id, 4, e.fin()*8f, (x, y)->{ - float size = 1f+e.fout()*5f; - Draw.color(Color.LIGHT_GRAY, Color.GRAY, e.fin()); - Draw.rect("circle", e.x + x, e.y + y, size, size); - Draw.reset(); - }); - }), - - nuclearsmoke = new Effect(40, e -> { - Angles.randLenVectors(e.id, 4, e.fin()*13f, (x, y)->{ - float size = e.finpow()*4f; - Draw.color(Color.LIGHT_GRAY, Color.GRAY, e.fin()); - Draw.rect("circle", e.x + x, e.y + y, size, size); - Draw.reset(); - }); - }), - - nuclearcloud = new Effect(90, 200f, e -> { - Angles.randLenVectors(e.id, 10, e.finpow()*90f, (x, y)->{ - float size = e.fout()*14f; - Draw.color(Color.LIME, Color.GRAY, e.fin()); - Draw.rect("circle", e.x + x, e.y + y, size, size); - Draw.reset(); - }); - }), - - chainshot = new Effect(9f, e -> { - Draw.color(Color.WHITE, lightOrange, e.fin()); - Lines.stroke(e.fout()*4f); - Lines.lineAngle(e.x, e.y, e.rotation, e.fout()*7f); - Lines.stroke(e.fout()*2f); - Lines.lineAngle(e.x, e.y, e.rotation, e.fout()*10f); - Draw.reset(); - }), - - mortarshot = new Effect(10f, e -> { - Draw.color(Color.WHITE, Color.DARK_GRAY, e.fin()); - Lines.stroke(e.fout()*6f); - Lines.lineAngle(e.x, e.y, e.rotation, e.fout()*10f); - Lines.stroke(e.fout()*5f); - Lines.lineAngle(e.x, e.y, e.rotation, e.fout()*14f); - Lines.stroke(e.fout()*1f); - Lines.lineAngle(e.x, e.y, e.rotation, e.fout()*16f); - Draw.reset(); - }), - - railshot = new Effect(9f, e -> { - Draw.color(Color.WHITE, Color.DARK_GRAY, e.fin()); - Lines.stroke(e.fout()*5f); - Lines.lineAngle(e.x, e.y, e.rotation, e.fout()*8f); - Lines.stroke(e.fout()*4f); - Lines.lineAngle(e.x, e.y, e.rotation, e.fout()*12f); - Lines.stroke(e.fout()*1f); - Lines.lineAngle(e.x, e.y, e.rotation, e.fout()*14f); - Draw.reset(); - }), - - titanshot = new Effect(12f, e -> { - Draw.color(Color.WHITE, lightOrange, e.fin()); - Lines.stroke(e.fout()*7f); - Lines.lineAngle(e.x, e.y, e.rotation, e.fout()*12f); - Lines.stroke(e.fout()*4f); - Lines.lineAngle(e.x, e.y, e.rotation, e.fout()*16f); - Lines.stroke(e.fout()*2f); - Lines.lineAngle(e.x, e.y, e.rotation, e.fout()*18f); - Draw.reset(); - }), - - largeCannonShot = new Effect(11f, e -> { - Draw.color(Color.WHITE, whiteYellow, e.fin()); - Lines.stroke(e.fout()*6f); - Lines.lineAngle(e.x, e.y, e.rotation, e.fout()*12f); - Lines.stroke(e.fout()*3f); - Lines.lineAngle(e.x, e.y, e.rotation, e.fout()*16f); - Lines.stroke(e.fout()*1f); - Lines.lineAngle(e.x, e.y, e.rotation, e.fout()*18f); - Draw.reset(); - }), - - shockwave = new Effect(10f, 80f, e -> { - Draw.color(Color.WHITE, Color.LIGHT_GRAY, e.fin()); - Lines.stroke(e.fout()*2f + 0.2f); - Lines.circle(e.x, e.y, e.fin()*28f); - Draw.reset(); - }), - - nuclearShockwave = new Effect(10f, 200f, e -> { - Draw.color(Color.WHITE, Color.LIGHT_GRAY, e.fin()); - Lines.stroke(e.fout()*3f + 0.2f); - Lines.poly(e.x, e.y, 40, e.fin()*140f); - Draw.reset(); - }), - - shockwaveSmall = new Effect(10f, e -> { - Draw.color(Color.WHITE, Color.LIGHT_GRAY, e.fin()); - Lines.stroke(e.fout()*2f + 0.1f); - Lines.circle(e.x, e.y, e.fin()*15f); - Draw.reset(); - }), - - empshockwave = new Effect(7f, e -> { - Draw.color(Color.WHITE, Color.SKY, e.fin()); - Lines.stroke(e.fout()*2f); - Lines.circle(e.x, e.y, e.fin()*40f); - Draw.reset(); - }), - - empspark = new Effect(13, e -> { - Angles.randLenVectors(e.id, 7, 1f + e.fin()*12f, (x, y)->{ - float len = 1f+e.fout()*6f; - Draw.color(Color.SKY); - Lines.lineAngle(e.x + x, e.y + y, Mathf.atan2(x, y), len); - Draw.reset(); - }); - }), - - redgeneratespark = new Effect(18, e -> { - Angles.randLenVectors(e.id, 5, e.fin()*8f, (x, y)->{ - float len = e.fout()*4f; - Draw.color(Color.valueOf("fbb97f"), Color.GRAY, e.fin()); - //Draw.alpha(e.fout()); - Draw.rect("circle", e.x + x, e.y + y, len, len); - Draw.reset(); - }); - }), - - generatespark = new Effect(18, e -> { - Angles.randLenVectors(e.id, 5, e.fin()*8f, (x, y)->{ - float len = e.fout()*4f; - Draw.color(Color.valueOf("d2b29c"), Color.GRAY, e.fin()); - //Draw.alpha(e.fout()); - Draw.rect("circle", e.x + x, e.y + y, len, len); - Draw.reset(); - }); - }), - - fuelburn = new Effect(23, e -> { - Angles.randLenVectors(e.id, 5, e.fin()*9f, (x, y)->{ - float len = e.fout()*4f; - Draw.color(Color.LIGHT_GRAY, Color.GRAY, e.fin()); - //Draw.alpha(e.fout()); - Draw.rect("circle", e.x + x, e.y + y, len, len); - Draw.reset(); - }); - }), - - laserspark = new Effect(14, e -> { - Angles.randLenVectors(e.id, 8, 1f + e.fin()*11f, (x, y)->{ - float len = 1f+e.fout()*5f; - Draw.color(Color.WHITE, Color.CORAL, e.fin()); - Draw.alpha(e.fin()/1.3f); - Lines.lineAngle(e.x + x, e.y + y, Mathf.atan2(x, y), len); - Draw.reset(); - }); - }), - - shellsmoke = new Effect(20, e -> { - Angles.randLenVectors(e.id, 8, 3f + e.fin()*17f, (x, y)->{ - float size = 2f+e.fout()*5f; - Draw.color(Color.LIGHT_GRAY, Color.DARK_GRAY, e.fin()); - Draw.rect("circle", e.x + x, e.y + y, size, size); - Draw.reset(); - }); - }), - - blastsmoke = new Effect(26, e -> { - Angles.randLenVectors(e.id, 12, 1f + e.fin()*23f, (x, y)->{ - float size = 2f+e.fout()*6f; - Draw.color(Color.LIGHT_GRAY, Color.DARK_GRAY, e.fin()); - Draw.rect("circle", e.x + x, e.y + y, size, size); - Draw.reset(); - }); - }), - - lava = new Effect(18, e -> { - Angles.randLenVectors(e.id, 3, 1f + e.fin()*10f, (x, y)->{ - float size = e.finpow()*4f; - Draw.color(Color.ORANGE, Color.GRAY, e.fin()); - Draw.rect("circle", e.x + x, e.y + y, size, size); - Draw.reset(); - }); - }), - - lavabubble = new Effect(45f, e -> { - Draw.color(Color.ORANGE); - float scl = 0.35f; - Lines.stroke(1f - Mathf.clamp(e.fin() - (1f-scl)) * (1f/scl)); - Lines.circle(e.x, e.y, e.fin()*4f); - Draw.reset(); - }), - - oilbubble = new Effect(64f, e -> { - Draw.color(Color.DARK_GRAY); - float scl = 0.25f; - Lines.stroke(1f - Mathf.clamp(e.fin() - (1f-scl)) * (1f/scl)); - Lines.circle(e.x, e.y, e.fin()*3f); - Draw.reset(); - }), - - shellexplosion = new Effect(9, e -> { - Lines.stroke(2f - e.fin()*1.7f); - Draw.color(Color.WHITE, Color.LIGHT_GRAY, e.fin()); - Lines.circle(e.x, e.y, 3f + e.fin() * 9f); - Draw.reset(); - }), - - blastexplosion = new Effect(14, e -> { - Lines.stroke(1.2f - e.fin()); - Draw.color(Color.WHITE, lightOrange, e.fin()); - Lines.circle(e.x, e.y, 1.5f + e.fin() * 9f); - Draw.reset(); - }), - - place = new Effect(16, e -> { - Lines.stroke(3f - e.fin() * 2f); - Lines.square(e.x, e.y, tilesize / 2f + e.fin() * 3f); - Draw.reset(); - }), - - dooropen = new Effect(10, e -> { - Lines.stroke(e.fout() * 1.6f); - Lines.square(e.x, e.y, tilesize / 2f + e.fin() * 2f); - Draw.reset(); - }), - - doorclose= new Effect(10, e -> { - Lines.stroke(e.fout() * 1.6f); - Lines.square(e.x, e.y, tilesize / 2f + e.fout() * 2f); - Draw.reset(); - }), - - dooropenlarge = new Effect(10, e -> { - Lines.stroke(e.fout() * 1.6f); - Lines.square(e.x, e.y, tilesize + e.fin() * 2f); - Draw.reset(); - }), - - doorcloselarge = new Effect(10, e -> { - Lines.stroke(e.fout() * 1.6f); - Lines.square(e.x, e.y, tilesize + e.fout() * 2f); - Draw.reset(); - }), - - purify = new Effect(10, e -> { - Draw.color(Color.ROYAL, Color.GRAY, e.fin()); - Lines.stroke(2f); - Lines.spikes(e.x, e.y, e.fin() * 4f, 2, 6); - Draw.reset(); - }), - - purifyoil = new Effect(10, e -> { - Draw.color(Color.BLACK, Color.GRAY, e.fin()); - Lines.stroke(2f); - Lines.spikes(e.x, e.y, e.fin() * 4f, 2, 6); - Draw.reset(); - }), - - purifystone = new Effect(10, e -> { - Draw.color(Color.ORANGE, Color.GRAY, e.fin()); - Lines.stroke(2f); - Lines.spikes(e.x, e.y, e.fin() * 4f, 2, 6); - Draw.reset(); - }), - - generate = new Effect(11, e -> { - Draw.color(Color.ORANGE, Color.YELLOW, e.fin()); - Lines.stroke(1f); - Lines.spikes(e.x, e.y, e.fin() * 5f, 2, 8); - Draw.reset(); - }), - - spark = new Effect(10, e -> { - Lines.stroke(1f); - Draw.color(Color.WHITE, Color.GRAY, e.fin()); - Lines.spikes(e.x, e.y, e.fin() * 5f, 2, 8); - Draw.reset(); - }), - - sparkbig = new Effect(11, e -> { - Lines.stroke(1f); - Draw.color(lightRed, Color.GRAY, e.fin()); - Lines.spikes(e.x, e.y, e.fin() * 5f, 2.3f, 8); - Draw.reset(); - }), - - smelt = new Effect(10, e -> { - Lines.stroke(1f); - Draw.color(Color.YELLOW, Color.RED, e.fin()); - Lines.spikes(e.x, e.y, e.fin() * 5f, 1f, 8); - Draw.reset(); - }), - - breakBlock = new Effect(12, e -> { - Lines.stroke(2f); - Draw.color(Color.WHITE, Colors.get("break"), e.fin()); - Lines.spikes(e.x, e.y, e.fin() * 6f, 2, 5, 90); - Draw.reset(); - }), - - hit = new Effect(10, e -> { - Lines.stroke(1f); - Draw.color(Color.WHITE, Color.ORANGE, e.fin()); - Lines.spikes(e.x, e.y, e.fin() * 3f, 2, 8); - Draw.reset(); - }), - - laserhit = new Effect(10, e -> { - Lines.stroke(1f); - Draw.color(Color.WHITE, Color.SKY, e.fin()); - Lines.spikes(e.x, e.y, e.fin() * 2f, 2, 6); - Draw.reset(); - }), - - shieldhit = new Effect(9, e -> { - Lines.stroke(1f); - Draw.color(Color.WHITE, Color.SKY, e.fin()); - Lines.spikes(e.x, e.y, e.fin() * 5f, 2, 6); - Lines.stroke(4f*e.fout()); - Lines.circle(e.x, e.y, e.fin()*14f); - Draw.reset(); - }), - - laserShoot = new Effect(8, e -> { - Draw.color(Color.WHITE, lightOrange, e.fin()); - Shapes.lineShot(e.x, e.y, e.rotation, 3, e.fout(), 6f, 2f, 0.8f); - Draw.reset(); - }), - - spreadShoot = new Effect(12, e -> { - Draw.color(Color.WHITE, Color.PURPLE, e.fin()); - Shapes.lineShot(e.x, e.y, e.rotation, 3, e.fout(), 9f, 3.5f, 0.8f); - Draw.reset(); - }), - - clusterShoot = new Effect(12, e -> { - Draw.color(Color.WHITE, lightOrange, e.fin()); - Shapes.lineShot(e.x, e.y, e.rotation, 3, e.fout(), 10f, 2.5f, 0.7f); - Draw.reset(); - }), - - vulcanShoot = new Effect(8, e -> { - Draw.color(lighterOrange, lightOrange, e.fin()); - Shapes.lineShot(e.x, e.y, e.rotation, 3, e.fout(), 10f, 2f, 0.7f); - Draw.reset(); - }), - - shockShoot = new Effect(8, e -> { - Draw.color(Color.WHITE, Color.ORANGE, e.fin()); - Shapes.lineShot(e.x, e.y, e.rotation, 3, e.fout(), 14f, 4f, 0.8f); - Draw.reset(); - }), - - beamShoot = new Effect(8, e -> { - Draw.color(beamLight, beam, e.fin()); - Shapes.lineShot(e.x, e.y, e.rotation - 70, 3, e.fout(), 12f, 1f, 0.5f); - Shapes.lineShot(e.x, e.y, e.rotation + 70, 3, e.fout(), 12f, 1f, 0.5f); - Draw.reset(); - }), - - beamhit = new Effect(8, e -> { - Draw.color(beamLight, beam, e.fin()); - Lines.stroke(e.fout()*3f+0.5f); - Lines.circle(e.x, e.y, e.fin()*8f); - Lines.spikes(e.x, e.y, e.fin()*6f, 2f, 4, 45); - Draw.reset(); - }), - - titanExplosion = new Effect(11, 48f, e -> { - Lines.stroke(2f*e.fout()+0.5f); - Draw.color(Color.WHITE, Color.DARK_GRAY, e.finpow()); - Lines.circle(e.x, e.y, 5f + e.finpow() * 8f); - - Draw.color(e.fin() < 0.5f ? whiteOrange : Color.DARK_GRAY); - float rad = e.fout()*10f + 5f; - Angles.randLenVectors(e.id, 5, 9f, (x, y)->{ - Draw.rect("circle2", e.x + x, e.y + y, rad, rad); - }); - - Draw.reset(); - }), - - explosion = new Effect(11, e -> { - Lines.stroke(2f*e.fout()+0.5f); - Draw.color(Color.WHITE, Color.DARK_GRAY, e.finpow()); - Lines.circle(e.x, e.y, 5f + e.finpow() * 6f); - - Draw.color(e.fin() < 0.5f ? Color.WHITE : Color.DARK_GRAY); - float rad = e.fout()*10f + 5f; - Angles.randLenVectors(e.id, 5, 8f, (x, y)->{ - Draw.rect("circle2", e.x + x, e.y + y, rad, rad); - }); - - Draw.reset(); - }), - - - blockexplosion = new Effect(13, e -> { - Angles.randLenVectors(e.id+1, 8, 5f + e.fin()*11f, (x, y)->{ - float size = 2f+e.fout()*8f; - Draw.color(Color.LIGHT_GRAY, Color.DARK_GRAY, e.fin()); - Draw.rect("circle", e.x + x, e.y + y, size, size); - Draw.reset(); - }); - - Lines.stroke(2f*e.fout()+0.4f); - Draw.color(Color.WHITE, Color.ORANGE, e.finpow()); - Lines.circle(e.x, e.y, 2f + e.finpow() * 9f); - - Draw.color(e.fin() < 0.5f ? Color.WHITE : Color.DARK_GRAY); - float rad = e.fout()*10f + 2f; - Angles.randLenVectors(e.id, 5, 8f, (x, y)->{ - Draw.rect("circle2", e.x + x, e.y + y, rad, rad); - }); - - Draw.reset(); - }), - - clusterbomb = new Effect(10f, e -> { - Draw.color(Color.WHITE, lightOrange, e.fin()); - Lines.stroke(e.fout()*1.5f); - Lines.poly(e.x, e.y, 4, e.fout()*8f); - Lines.circle(e.x, e.y, e.fin()*14f); - Draw.reset(); - }), - - coreexplosion = new Effect(13, e -> { - Lines.stroke(3f-e.fin()*2f); - Draw.color(Color.ORANGE, Color.WHITE, e.fin()); - Lines.spikes(e.x, e.y, 5f + e.fin() * 40f, 6, 6); - Lines.circle(e.x, e.y, 4f + e.fin() * 40f); - Draw.reset(); - }), - - smoke = new Effect(100, e -> { - Draw.color(Color.GRAY, new Color(0.3f, 0.3f, 0.3f, 1f), e.fin()); - float size = 7f-e.fin()*7f; - Draw.rect("circle", e.x, e.y, size, size); - Draw.reset(); - }), - - railsmoke = new Effect(30, e -> { - Draw.color(Color.LIGHT_GRAY, Color.WHITE, e.fin()); - float size = e.fout()*4f; - Draw.rect("circle", e.x, e.y, size, size); - Draw.reset(); - }), - - chainsmoke = new Effect(30, e -> { - Draw.color(lightGray); - float size = e.fout()*4f; - Draw.rect("circle", e.x, e.y, size, size); - Draw.reset(); - }), - - dashsmoke = new Effect(30, e -> { - Draw.color(Color.CORAL, Color.GRAY, e.fin()); - float size = e.fout()*4f; - Draw.rect("circle", e.x, e.y, size, size); - Draw.reset(); - }), - - spawn = new Effect(23, e -> { - Lines.stroke(2f); - Draw.color(Color.DARK_GRAY, Color.SCARLET, e.fin()); - Lines.circle(e.x, e.y, 7f - e.fin() * 6f); - Draw.reset(); - }), - - respawn = new Effect(respawnduration, e -> { - Draw.tcolor(Color.SCARLET); - Draw.tscl(0.25f); - Draw.text("Respawning in " + (int)((e.lifetime-e.time)/60), e.x, e.y); - Draw.tscl(0.5f); - Draw.reset(); - }), - transfer = new Effect(20, e -> { - Draw.color(Color.SCARLET, Color.CLEAR, e.fout()); - Lines.square(e.x, e.y, 4); - Lines.lineAngle(e.x, e.y, e.rotation, 5f); - Draw.reset(); - }); -} diff --git a/core/src/io/anuke/mindustry/graphics/IndexedRenderer.java b/core/src/io/anuke/mindustry/graphics/IndexedRenderer.java new file mode 100644 index 0000000000..b4165dbe63 --- /dev/null +++ b/core/src/io/anuke/mindustry/graphics/IndexedRenderer.java @@ -0,0 +1,245 @@ +package io.anuke.mindustry.graphics; + +import io.anuke.arc.Core; +import io.anuke.arc.graphics.*; +import io.anuke.arc.graphics.VertexAttributes.Usage; +import io.anuke.arc.graphics.g2d.BatchShader; +import io.anuke.arc.graphics.g2d.TextureRegion; +import io.anuke.arc.graphics.glutils.Shader; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.Matrix3; +import io.anuke.arc.util.Disposable; +import io.anuke.arc.util.Strings; + +//TODO this class is a trainwreck, remove it +public class IndexedRenderer implements Disposable{ + private final static int vsize = 5; + + private Shader program = new Shader( + Strings.join("\n", + "attribute vec4 " + Shader.POSITION_ATTRIBUTE + ";", + "attribute vec4 " + Shader.COLOR_ATTRIBUTE + ";", + "attribute vec2 " + Shader.TEXCOORD_ATTRIBUTE + "0;", + "uniform mat4 u_projTrans;", + "varying vec4 v_color;", + "varying vec2 v_texCoords;", + "", + "void main(){", + " v_color = " + Shader.COLOR_ATTRIBUTE + ";", + " v_color.a = v_color.a * (255.0/254.0);", + " v_texCoords = " + Shader.TEXCOORD_ATTRIBUTE + "0;", + " gl_Position = u_projTrans * " + Shader.POSITION_ATTRIBUTE + ";", + "}"), + Strings.join("\n", + "#ifdef GL_ES", + "#define LOWP lowp", + "precision mediump float;", + "#else", + "#define LOWP ", + "#endif", + "", + "varying LOWP vec4 v_color;", + "varying vec2 v_texCoords;", + "uniform sampler2D u_texture;", + "", + "void main(){", + " gl_FragColor = v_color * texture2D(u_texture, v_texCoords);", + "}" + )); + private Mesh mesh; + private float[] tmpVerts = new float[vsize * 6]; + private float[] vertices; + + private Matrix3 projMatrix = new Matrix3(); + private Matrix3 transMatrix = new Matrix3(); + private Matrix3 combined = new Matrix3(); + private float color = Color.WHITE.toFloatBits(); + + public IndexedRenderer(int sprites){ + resize(sprites); + } + + public void render(Texture texture){ + Core.gl.glEnable(GL20.GL_BLEND); + + updateMatrix(); + + program.begin(); + + texture.bind(); + + program.setUniformMatrix4("u_projTrans", BatchShader.copyTransform(combined)); + program.setUniformi("u_texture", 0); + + mesh.render(program, GL20.GL_TRIANGLES, 0, vertices.length / vsize); + + program.end(); + } + + public void setColor(Color color){ + this.color = color.toFloatBits(); + } + + public void draw(int index, TextureRegion region, float x, float y, float w, float h){ + final float fx2 = x + w; + final float fy2 = y + h; + final float u = region.getU(); + final float v = region.getV2(); + final float u2 = region.getU2(); + final float v2 = region.getV(); + + float[] vertices = tmpVerts; + + int idx = 0; + vertices[idx++] = x; + vertices[idx++] = y; + vertices[idx++] = color; + vertices[idx++] = u; + vertices[idx++] = v; + + vertices[idx++] = x; + vertices[idx++] = fy2; + vertices[idx++] = color; + vertices[idx++] = u; + vertices[idx++] = v2; + + vertices[idx++] = fx2; + vertices[idx++] = fy2; + vertices[idx++] = color; + vertices[idx++] = u2; + vertices[idx++] = v2; + + //tri2 + vertices[idx++] = x; + vertices[idx++] = y; + vertices[idx++] = color; + vertices[idx++] = u; + vertices[idx++] = v; + + vertices[idx++] = fx2; + vertices[idx++] = y; + vertices[idx++] = color; + vertices[idx++] = u2; + vertices[idx++] = v; + + vertices[idx++] = fx2; + vertices[idx++] = fy2; + vertices[idx++] = color; + vertices[idx++] = u2; + vertices[idx++] = v2; + + mesh.updateVertices(index * vsize * 6, vertices); + } + + public void draw(int index, TextureRegion region, float x, float y, float w, float h, float rotation){ + final float u = region.getU(); + final float v = region.getV2(); + final float u2 = region.getU2(); + final float v2 = region.getV(); + + final float originX = w / 2, originY = h / 2; + + final float cos = Mathf.cosDeg(rotation); + final float sin = Mathf.sinDeg(rotation); + + float fx = -originX; + float fy = -originY; + float fx2 = w - originX; + float fy2 = h - originY; + + final float worldOriginX = x + originX; + final float worldOriginY = y + originY; + + float x1 = cos * fx - sin * fy; + float y1 = sin * fx + cos * fy; + + float x2 = cos * fx - sin * fy2; + float y2 = sin * fx + cos * fy2; + + float x3 = cos * fx2 - sin * fy2; + float y3 = sin * fx2 + cos * fy2; + + float x4 = x1 + (x3 - x2); + float y4 = y3 - (y2 - y1); + + x1 += worldOriginX; + y1 += worldOriginY; + x2 += worldOriginX; + y2 += worldOriginY; + x3 += worldOriginX; + y3 += worldOriginY; + x4 += worldOriginX; + y4 += worldOriginY; + + float[] vertices = tmpVerts; + + int idx = 0; + vertices[idx++] = x1; + vertices[idx++] = y1; + vertices[idx++] = color; + vertices[idx++] = u; + vertices[idx++] = v; + + vertices[idx++] = x3; + vertices[idx++] = y3; + vertices[idx++] = color; + vertices[idx++] = u2; + vertices[idx++] = v2; + + vertices[idx++] = x4; + vertices[idx++] = y4; + vertices[idx++] = color; + vertices[idx++] = u; + vertices[idx++] = v2; + + //tri2 + vertices[idx++] = x1; + vertices[idx++] = y1; + vertices[idx++] = color; + vertices[idx++] = u; + vertices[idx++] = v; + + vertices[idx++] = x2; + vertices[idx++] = y2; + vertices[idx++] = color; + vertices[idx++] = u2; + vertices[idx++] = v; + + vertices[idx++] = x3; + vertices[idx++] = y3; + vertices[idx++] = color; + vertices[idx++] = u2; + vertices[idx++] = v2; + + mesh.updateVertices(index * vsize * 6, vertices); + } + + public Matrix3 getTransformMatrix(){ + return transMatrix; + } + + public void setProjectionMatrix(Matrix3 matrix){ + projMatrix = matrix; + } + + public void resize(int sprites){ + if(mesh != null) mesh.dispose(); + + mesh = new Mesh(true, 6 * sprites, 0, + new VertexAttribute(Usage.Position, 2, "a_position"), + new VertexAttribute(Usage.ColorPacked, 4, "a_color"), + new VertexAttribute(Usage.TextureCoordinates, 2, "a_texCoord0")); + vertices = new float[6 * sprites * vsize]; + mesh.setVertices(vertices); + } + + private void updateMatrix(){ + combined.set(projMatrix).mul(transMatrix); + } + + @Override + public void dispose(){ + mesh.dispose(); + program.dispose(); + } +} diff --git a/core/src/io/anuke/mindustry/graphics/Layer.java b/core/src/io/anuke/mindustry/graphics/Layer.java new file mode 100644 index 0000000000..f84f6aa3c2 --- /dev/null +++ b/core/src/io/anuke/mindustry/graphics/Layer.java @@ -0,0 +1,16 @@ +package io.anuke.mindustry.graphics; + +public enum Layer{ + /** Base block layer. */ + block, + /** for placement */ + placement, + /** First overlay. Stuff like conveyor items. */ + overlay, + /** "High" blocks, like turrets. */ + turret, + /** Power lasers. */ + power, + /** Extra lasers, like healing turrets. */ + laser +} diff --git a/core/src/io/anuke/mindustry/graphics/MinimapRenderer.java b/core/src/io/anuke/mindustry/graphics/MinimapRenderer.java new file mode 100644 index 0000000000..9777f4e833 --- /dev/null +++ b/core/src/io/anuke/mindustry/graphics/MinimapRenderer.java @@ -0,0 +1,145 @@ +package io.anuke.mindustry.graphics; + +import io.anuke.arc.Core; +import io.anuke.arc.Events; +import io.anuke.arc.collection.Array; +import io.anuke.arc.graphics.*; +import io.anuke.arc.graphics.Pixmap.Format; +import io.anuke.arc.graphics.g2d.*; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.Rectangle; +import io.anuke.arc.util.Disposable; +import io.anuke.mindustry.entities.Units; +import io.anuke.mindustry.entities.type.Unit; +import io.anuke.mindustry.game.EventType.TileChangeEvent; +import io.anuke.mindustry.game.EventType.WorldLoadEvent; +import io.anuke.mindustry.io.MapIO; +import io.anuke.mindustry.world.Tile; + +import static io.anuke.mindustry.Vars.tilesize; +import static io.anuke.mindustry.Vars.world; + +public class MinimapRenderer implements Disposable{ + private static final float baseSize = 16f; + private final Array units = new Array<>(); + private Pixmap pixmap; + private Texture texture; + private TextureRegion region; + private Rectangle rect = new Rectangle(); + private float zoom = 4; + + public MinimapRenderer(){ + Events.on(WorldLoadEvent.class, event -> { + reset(); + updateAll(); + }); + + //make sure to call on the graphics thread + Events.on(TileChangeEvent.class, event -> Core.app.post(() -> update(event.tile))); + } + + public Texture getTexture(){ + return texture; + } + + public void zoomBy(float amount){ + zoom += amount; + setZoom(zoom); + } + + public void setZoom(float amount){ + zoom = Mathf.clamp(amount, 1f, Math.min(world.width(), world.height()) / baseSize / 2f); + } + + public float getZoom(){ + return zoom; + } + + public void reset(){ + if(pixmap != null){ + pixmap.dispose(); + texture.dispose(); + } + setZoom(4f); + pixmap = new Pixmap(world.width(), world.height(), Format.RGBA8888); + texture = new Texture(pixmap); + region = new TextureRegion(texture); + } + + public void drawEntities(float x, float y, float w, float h){ + updateUnitArray(); + + float sz = baseSize * zoom; + float dx = (Core.camera.position.x / tilesize); + float dy = (Core.camera.position.y / tilesize); + dx = Mathf.clamp(dx, sz, world.width() - sz); + dy = Mathf.clamp(dy, sz, world.height() - sz); + + rect.set((dx - sz) * tilesize, (dy - sz) * tilesize, sz * 2 * tilesize, sz * 2 * tilesize); + + for(Unit unit : units){ + float rx = (unit.x - rect.x) / rect.width * w, ry = (unit.y - rect.y) / rect.width * h; + Draw.color(unit.getTeam().color); + Fill.rect(x + rx, y + ry, io.anuke.arc.scene.ui.layout.Unit.dp.scl(baseSize / 2f), io.anuke.arc.scene.ui.layout.Unit.dp.scl(baseSize / 2f)); + } + + Draw.color(); + } + + public TextureRegion getRegion(){ + if(texture == null) return null; + + float sz = Mathf.clamp(baseSize * zoom, baseSize, Math.min(world.width(), world.height())); + float dx = (Core.camera.position.x / tilesize); + float dy = (Core.camera.position.y / tilesize); + dx = Mathf.clamp(dx, sz, world.width() - sz); + dy = Mathf.clamp(dy, sz, world.height() - sz); + float invTexWidth = 1f / texture.getWidth(); + float invTexHeight = 1f / texture.getHeight(); + float x = dx - sz, y = world.height() - dy - sz, width = sz * 2, height = sz * 2; + region.set(x * invTexWidth, y * invTexHeight, (x + width) * invTexWidth, (y + height) * invTexHeight); + return region; + } + + public void updateAll(){ + for(int x = 0; x < world.width(); x++){ + for(int y = 0; y < world.height(); y++){ + pixmap.drawPixel(x, pixmap.getHeight() - 1 - y, colorFor(world.tile(x, y))); + } + } + texture.draw(pixmap, 0, 0); + } + + public void update(Tile tile){ + int color = colorFor(world.tile(tile.x, tile.y)); + pixmap.drawPixel(tile.x, pixmap.getHeight() - 1 - tile.y, color); + + Pixmaps.drawPixel(texture, tile.x, pixmap.getHeight() - 1 - tile.y, color); + } + + public void updateUnitArray(){ + float sz = baseSize * zoom; + float dx = (Core.camera.position.x / tilesize); + float dy = (Core.camera.position.y / tilesize); + dx = Mathf.clamp(dx, sz, world.width() - sz); + dy = Mathf.clamp(dy, sz, world.height() - sz); + + units.clear(); + Units.nearby((dx - sz) * tilesize, (dy - sz) * tilesize, sz * 2 * tilesize, sz * 2 * tilesize, units::add); + } + + private int colorFor(Tile tile){ + tile = tile.link(); + return MapIO.colorFor(tile.floor(), tile.block(), tile.overlay(), tile.getTeam()); + } + + @Override + public void dispose(){ + if(pixmap != null && texture != null){ + pixmap.dispose(); + texture.dispose(); + texture = null; + pixmap = null; + } + } +} diff --git a/core/src/io/anuke/mindustry/graphics/OverlayRenderer.java b/core/src/io/anuke/mindustry/graphics/OverlayRenderer.java new file mode 100644 index 0000000000..494099befb --- /dev/null +++ b/core/src/io/anuke/mindustry/graphics/OverlayRenderer.java @@ -0,0 +1,124 @@ +package io.anuke.mindustry.graphics; + +import io.anuke.arc.Core; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.graphics.g2d.Draw; +import io.anuke.arc.graphics.g2d.Lines; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.Rectangle; +import io.anuke.arc.math.geom.Vector2; +import io.anuke.arc.util.Time; +import io.anuke.arc.util.Tmp; +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.content.Blocks; +import io.anuke.mindustry.entities.Units; +import io.anuke.mindustry.entities.type.Player; +import io.anuke.mindustry.game.Team; +import io.anuke.mindustry.input.InputHandler; +import io.anuke.mindustry.type.Item; +import io.anuke.mindustry.world.Tile; + +import static io.anuke.mindustry.Vars.*; + +public class OverlayRenderer{ + private static final float indicatorLength = 14f; + private static final Rectangle rect = new Rectangle(); + private float buildFadeTime; + + public void drawBottom(){ + InputHandler input = control.input(); + + if(!input.isDrawing() || player.isDead()) return; + + input.drawOutlined(); + } + + public void drawTop(){ + + if(Core.settings.getBool("indicators")){ + for(Player player : playerGroup.all()){ + if(Vars.player != player && Vars.player.getTeam() == player.getTeam()){ + if(!rect.setSize(Core.camera.width * 0.9f, Core.camera.height * 0.9f) + .setCenter(Core.camera.position.x, Core.camera.position.y).contains(player.x, player.y)){ + + Tmp.v1.set(player.x, player.y).sub(Core.camera.position.x, Core.camera.position.y).setLength(indicatorLength); + + Lines.stroke(2f, player.getTeam().color); + Lines.lineAngle(Core.camera.position.x + Tmp.v1.x, Core.camera.position.y + Tmp.v1.y, Tmp.v1.angle(), 4f); + Draw.reset(); + } + } + } + + Units.all(unit -> { + if(unit != player && unit.getTeam() != player.getTeam() && !rect.setSize(Core.camera.width * 0.9f, Core.camera.height * 0.9f).setCenter(Core.camera.position.x, Core.camera.position.y).contains(unit.x, unit.y)){ + Tmp.v1.set(unit.x, unit.y).sub(Core.camera.position.x, Core.camera.position.y).setLength(indicatorLength); + + Lines.stroke(1f, unit.getTeam().color); + Lines.lineAngle(Core.camera.position.x + Tmp.v1.x, Core.camera.position.y + Tmp.v1.y, Tmp.v1.angle(), 3f); + Draw.reset(); + } + }); + } + + if(player.isDead()) return; //dead players don't draw + + InputHandler input = control.input(); + + //draw config selected block + if(input.frag.config.isShown()){ + Tile tile = input.frag.config.getSelectedTile(); + tile.block().drawConfigure(tile); + } + + input.drawTop(); + + buildFadeTime = Mathf.lerpDelta(buildFadeTime, input.isPlacing() ? 1f : 0f, 0.06f); + + Draw.reset(); + Lines.stroke(buildFadeTime * 2f); + + if(buildFadeTime > 0.005f){ + for(Team enemy : state.teams.enemiesOf(player.getTeam())){ + for(Tile core : state.teams.get(enemy).cores){ + float dst = Mathf.dst(player.x, player.y, core.drawx(), core.drawy()); + if(dst < state.rules.enemyCoreBuildRadius * 1.5f){ + Draw.color(Color.DARK_GRAY); + Lines.poly(core.drawx(), core.drawy() - 2, 200, state.rules.enemyCoreBuildRadius); + Draw.color(Pal.accent, enemy.color, 0.5f + Mathf.absin(Time.time(), 10f, 0.5f)); + Lines.poly(core.drawx(), core.drawy(), 200, state.rules.enemyCoreBuildRadius); + } + } + } + } + + Draw.reset(); + + //draw selected block bars and info + if(input.block == null && !Core.scene.hasMouse()){ + Vector2 vec = Core.input.mouseWorld(input.getMouseX(), input.getMouseY()); + Tile tile = world.ltileWorld(vec.x, vec.y); + + if(tile != null && tile.block() != Blocks.air && tile.getTeam() == player.getTeam()){ + tile.block().drawSelect(tile); + } + } + + if(input.isDroppingItem()){ + Vector2 v = Core.input.mouseWorld(input.getMouseX(), input.getMouseY()); + float size = 8; + Draw.rect(player.item().item.icon(Item.Icon.large), v.x, v.y, size, size); + Draw.color(Pal.accent); + Lines.circle(v.x, v.y, 6 + Mathf.absin(Time.time(), 5f, 1f)); + Draw.reset(); + + Tile tile = world.ltileWorld(v.x, v.y); + if(tile != null && tile.interactable(player.getTeam()) && tile.block().acceptStack(player.item().item, player.item().amount, tile, player) > 0){ + Draw.color(Pal.place); + Lines.square(tile.drawx(), tile.drawy(), tile.block().size * tilesize / 2f + 1 + Mathf.absin(Time.time(), 5f, 1f)); + Draw.color(); + } + } + } + +} diff --git a/core/src/io/anuke/mindustry/graphics/Pal.java b/core/src/io/anuke/mindustry/graphics/Pal.java new file mode 100644 index 0000000000..29614a7cd3 --- /dev/null +++ b/core/src/io/anuke/mindustry/graphics/Pal.java @@ -0,0 +1,82 @@ +package io.anuke.mindustry.graphics; + +import io.anuke.arc.graphics.Color; + +public class Pal{ + public static final Color + + items = Color.valueOf("2ea756"), + command = Color.valueOf("eab678"), + + bulletYellow = Color.valueOf("ffeec9"), + bulletYellowBack = Color.valueOf("f9c87a"), + + darkMetal = Color.valueOf("6e7080"), + + missileYellow = Color.valueOf("ffd2ae"), + missileYellowBack = Color.valueOf("e58956"), + + meltdownHit = Color.valueOf("ffb98b"), + + plastaniumBack = Color.valueOf("d8d97f"), + plastaniumFront = Color.valueOf("fffac6"), + + lightFlame = Color.valueOf("ffdd55"), + darkFlame = Color.valueOf("db401c"), + + lightPyraFlame = Color.valueOf("ffb855"), + darkPyraFlame = Color.valueOf("db661c"), + + turretHeat = Color.valueOf("ab3400"), + + lightOrange = Color.valueOf("f68021"), + lightishOrange = Color.valueOf("f8ad42"), + lighterOrange = Color.valueOf("f6e096"), + + lightishGray = Color.valueOf("a2a2a2"), + darkishGray = new Color(0.3f, 0.3f, 0.3f, 1f), + darkerGray = new Color(0.2f, 0.2f, 0.2f, 1f), + ammo = Color.valueOf("ff8947"), + rubble = Color.valueOf("1c1817"), + + boostTo = Color.valueOf("ffad4d"), + boostFrom = Color.valueOf("ff7f57"), + + lancerLaser = Color.valueOf("a9d8ff"), + + stoneGray = Color.valueOf("8f8f8f"), + engine = Color.valueOf("ffbb64"), + + health = Color.valueOf("ff341c"), + heal = Color.valueOf("98ffa9"), + bar = Color.SLATE, + accent = Color.valueOf("ffd37f"), + stat = Color.valueOf("ffd37f"), + locked = Color.valueOf("989aa4"), + accentBack = Color.valueOf("d4816b"), + place = Color.valueOf("6335f8"), + remove = Color.valueOf("e55454"), + removeBack = Color.valueOf("a73e3e"), + placeRotate = accent, + breakInvalid = Color.valueOf("d44b3d"), + range = Color.valueOf("f4ba6e"), + power = Color.valueOf("fbad67"), + powerBar = Color.valueOf("ec7b4c"), + powerLight = Color.valueOf("fbd367"), + placing = accent, + + unitFront = Color.valueOf("ffa665"), + unitBack = Color.valueOf("d06b53"), + + lightTrail = Color.valueOf("ffe2a9"), + + surge = Color.valueOf("f3e979"), + + redSpark = Color.valueOf("fbb97f"), + orangeSpark = Color.valueOf("d2b29c"), + + redDust = Color.valueOf("ffa480"), + redderDust = Color.valueOf("ff7b69"), + + plasticSmoke = Color.valueOf("f1e479"); +} diff --git a/core/src/io/anuke/mindustry/graphics/Pixelator.java b/core/src/io/anuke/mindustry/graphics/Pixelator.java new file mode 100644 index 0000000000..e8a9e0d2f3 --- /dev/null +++ b/core/src/io/anuke/mindustry/graphics/Pixelator.java @@ -0,0 +1,80 @@ +package io.anuke.mindustry.graphics; + +import io.anuke.arc.Core; +import io.anuke.arc.graphics.Blending; +import io.anuke.arc.graphics.Texture.TextureFilter; +import io.anuke.arc.graphics.g2d.Draw; +import io.anuke.arc.graphics.glutils.FrameBuffer; +import io.anuke.arc.util.Disposable; +import io.anuke.mindustry.entities.type.Player; + +import static io.anuke.arc.Core.camera; +import static io.anuke.arc.Core.graphics; +import static io.anuke.mindustry.Vars.playerGroup; +import static io.anuke.mindustry.Vars.renderer; + +public class Pixelator implements Disposable{ + private FrameBuffer buffer = new FrameBuffer(2, 2); + + { + buffer.getTexture().setFilter(TextureFilter.Nearest, TextureFilter.Nearest); + } + + public void drawPixelate(){ + float pre = renderer.getScale(); + float scale = renderer.getScale(); + scale = (int)scale; + renderer.setScale(scale); + camera.width = (int)camera.width; + camera.height = (int)camera.height; + + boolean hadShields = Core.settings.getBool("animatedshields"); + boolean hadWater = Core.settings.getBool("animatedwater"); + Core.settings.put("animatedwater", false); + Core.settings.put("animatedshields", false); + graphics.clear(0f, 0f, 0f, 1f); + + float px = Core.camera.position.x, py = Core.camera.position.y; + Core.camera.position.set((int)px + ((int)(camera.width) % 2 == 0 ? 0 : 0.5f), (int)py + ((int)(camera.height) % 2 == 0 ? 0 : 0.5f)); + + int w = (int)(Core.camera.width); + int h = (int)(Core.camera.height); + + if(!graphics.isHidden() && (buffer.getWidth() != w || buffer.getHeight() != h)){ + buffer.resize(w, h); + } + + Draw.flush(); + buffer.begin(); + renderer.draw(); + + Draw.flush(); + buffer.end(); + + Draw.blend(Blending.disabled); + Draw.rect(Draw.wrap(buffer.getTexture()), Core.camera.position.x, Core.camera.position.y, Core.camera.width, -Core.camera.height); + Draw.blend(); + + renderer.drawAndInterpolate(playerGroup, p -> !p.isDead() && !p.isLocal, Player::drawName); + + Core.camera.position.set(px, py); + Core.settings.put("animatedwater", hadWater); + Core.settings.put("animatedshields", hadShields); + renderer.setScale(pre); + } + + public void rebind(){ + if(enabled()){ + buffer.begin(); + } + } + + public boolean enabled(){ + return Core.settings.getBool("pixelate"); + } + + @Override + public void dispose(){ + buffer.dispose(); + } +} diff --git a/core/src/io/anuke/mindustry/graphics/Shaders.java b/core/src/io/anuke/mindustry/graphics/Shaders.java index 61e04d8861..e253818f1f 100644 --- a/core/src/io/anuke/mindustry/graphics/Shaders.java +++ b/core/src/io/anuke/mindustry/graphics/Shaders.java @@ -1,62 +1,149 @@ package io.anuke.mindustry.graphics; -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.math.Vector2; -import com.badlogic.gdx.utils.FloatArray; -import io.anuke.ucore.core.Core; -import io.anuke.ucore.core.Settings; -import io.anuke.ucore.core.Timers; -import io.anuke.ucore.graphics.Shader; -import io.anuke.ucore.scene.ui.layout.Unit; +import io.anuke.arc.Core; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.graphics.g2d.TextureRegion; +import io.anuke.arc.graphics.glutils.Shader; +import io.anuke.arc.scene.ui.layout.Unit; +import io.anuke.arc.util.Time; public class Shaders{ - public static final Outline outline = new Outline(); - public static final Shield shield = new Shield(); + public static Shadow shadow; + public static BlockBuild blockbuild; + public static Shield shield; + public static UnitBuild build; + public static FogShader fog; + public static MenuShader menu; + public static SurfaceShader water, tar; - private static final Vector2 vec = new Vector2(); - - public static class Outline extends Shader{ - public Color color = new Color(); - public float lighten = 0f; + public static void init(){ + shadow = new Shadow(); + blockbuild = new BlockBuild(); + shield = new Shield(); + build = new UnitBuild(); + fog = new FogShader(); + menu = new MenuShader(); + water = new SurfaceShader("water"); + tar = new SurfaceShader("tar"); + } - public Outline(){ - super("outline", "default"); - } - - @Override - public void apply(){ - shader.setUniformf("u_color", color); - shader.setUniformf("u_lighten", lighten); - shader.setUniformf("u_texsize", vec.set(region.getTexture().getWidth(), region.getTexture().getHeight())); - } - - } - - public static class Shield extends Shader{ - public static final int MAX_HITS = 3*64; - public Color color = new Color(); - public FloatArray hits; - - public Shield(){ - super("shield", "default"); - } - - @Override - public void apply(){ - float scale = Settings.getBool("pixelate") ? 1 : Core.cameraScale / Core.camera.zoom; - float scaling = Core.cameraScale / 4f / Core.camera.zoom; - if(hits.size > 0){ - shader.setUniform3fv("u_hits[0]", hits.items, 0, Math.min(hits.size, MAX_HITS)); - shader.setUniformi("u_hitamount", Math.min(hits.size, MAX_HITS)/3); - } - shader.setUniformf("u_dp", Unit.dp.scl(1f)); - shader.setUniformf("u_color", color); - shader.setUniformf("u_time", Timers.time() / Unit.dp.scl(1f)); - shader.setUniformf("u_scaling", scaling); - shader.setUniformf("u_offset", vec.set(Core.camera.position.x, Core.camera.position.y)); - shader.setUniformf("u_texsize", vec.set(region.getTexture().getWidth() / scale, - region.getTexture().getHeight() / scale)); - } - - } + public static class MenuShader extends LoadShader{ + float time = 0f; + + public MenuShader(){ + super("menu", "default"); + } + + @Override + public void apply(){ + time = time % 158; + + setUniformf("u_resolution", Core.graphics.getWidth(), Core.graphics.getHeight()); + setUniformi("u_time", (int)(time += Core.graphics.getDeltaTime() * 60f)); + setUniformf("u_uv", Core.atlas.white().getU(), Core.atlas.white().getV()); + setUniformf("u_scl", Unit.dp.scl(1f)); + setUniformf("u_uv2", Core.atlas.white().getU2(), Core.atlas.white().getV2()); + } + } + + public static class FogShader extends LoadShader{ + public FogShader(){ + super("fog", "default"); + } + } + + public static class UnitBuild extends LoadShader{ + public float progress, time; + public Color color = new Color(); + public TextureRegion region; + + public UnitBuild(){ + super("unitbuild", "default"); + } + + @Override + public void apply(){ + setUniformf("u_time", time); + setUniformf("u_color", color); + setUniformf("u_progress", progress); + setUniformf("u_uv", region.getU(), region.getV()); + setUniformf("u_uv2", region.getU2(), region.getV2()); + setUniformf("u_texsize", region.getTexture().getWidth(), region.getTexture().getHeight()); + } + } + + public static class Shadow extends LoadShader{ + public Color color = new Color(); + public TextureRegion region = new TextureRegion(); + public float scl; + + public Shadow(){ + super("shadow", "default"); + } + + @Override + public void apply(){ + setUniformf("u_color", color); + setUniformf("u_scl", scl); + setUniformf("u_texsize", region.getTexture().getWidth(), region.getTexture().getHeight()); + } + } + + public static class BlockBuild extends LoadShader{ + public Color color = new Color(); + public float progress; + public TextureRegion region = new TextureRegion(); + + public BlockBuild(){ + super("blockbuild", "default"); + } + + @Override + public void apply(){ + setUniformf("u_progress", progress); + setUniformf("u_color", color); + setUniformf("u_uv", region.getU(), region.getV()); + setUniformf("u_uv2", region.getU2(), region.getV2()); + setUniformf("u_time", Time.time()); + setUniformf("u_texsize", region.getTexture().getWidth(), region.getTexture().getHeight()); + } + } + + public static class Shield extends LoadShader{ + + public Shield(){ + super("shield", "default"); + } + + @Override + public void apply(){ + setUniformf("u_dp", Unit.dp.scl(1f)); + setUniformf("u_time", Time.time() / Unit.dp.scl(1f)); + setUniformf("u_offset", + Core.camera.position.x - Core.camera.width / 2, + Core.camera.position.y - Core.camera.height / 2); + setUniformf("u_texsize", Core.camera.width, + Core.camera.height); + } + } + + public static class SurfaceShader extends LoadShader{ + + public SurfaceShader(String frag){ + super(frag, "default"); + } + + @Override + public void apply(){ + setUniformf("camerapos", Core.camera.position.x - Core.camera.width / 2, Core.camera.position.y - Core.camera.height / 2); + setUniformf("screensize", Core.camera.width, Core.camera.height); + setUniformf("time", Time.time()); + } + } + + public static class LoadShader extends Shader{ + public LoadShader(String frag, String vert){ + super(Core.files.internal("shaders/" + vert + ".vertex.glsl"), Core.files.internal("shaders/" + frag + ".fragment.glsl")); + } + } } diff --git a/core/src/io/anuke/mindustry/graphics/Shapes.java b/core/src/io/anuke/mindustry/graphics/Shapes.java new file mode 100644 index 0000000000..7e629d347b --- /dev/null +++ b/core/src/io/anuke/mindustry/graphics/Shapes.java @@ -0,0 +1,35 @@ +package io.anuke.mindustry.graphics; + +import io.anuke.arc.Core; +import io.anuke.arc.graphics.g2d.*; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.util.Tmp; + +public class Shapes{ + + public static void laser(String line, String edge, float x, float y, float x2, float y2, float scale){ + laser(line, edge, x, y, x2, y2, Mathf.angle(x2 - x, y2 - y), scale); + } + + public static void laser(String line, String edge, float x, float y, float x2, float y2){ + laser(line, edge, x, y, x2, y2, Mathf.angle(x2 - x, y2 - y), 1f); + } + + public static void laser(String line, String edge, float x, float y, float x2, float y2, float rotation, float scale){ + TextureRegion region = Core.atlas.find(edge); + + Tmp.v1.trns(rotation, 8f * scale * Draw.scl); + + Draw.rect(Core.atlas.find(edge), x, y, region.getWidth() * scale * Draw.scl, region.getHeight() * scale * Draw.scl, rotation + 180); + Draw.rect(Core.atlas.find(edge), x2, y2, region.getWidth() * scale * Draw.scl, region.getHeight() * scale * Draw.scl, rotation); + + Lines.stroke(12f * scale); + Lines.line(Core.atlas.find(line), x + Tmp.v1.x, y + Tmp.v1.y, x2 - Tmp.v1.x, y2 - Tmp.v1.y, CapStyle.none, 0f); + Lines.stroke(1f); + } + + public static void tri(float x, float y, float width, float length, float rotation){ + float oy = 17f / 63f * length; + Draw.rect(Core.atlas.find("shape-3"), x, y - oy + length / 2f, width, length, width / 2f, oy, rotation - 90); + } +} diff --git a/core/src/io/anuke/mindustry/input/AndroidInput.java b/core/src/io/anuke/mindustry/input/AndroidInput.java deleted file mode 100644 index 7a859da9f1..0000000000 --- a/core/src/io/anuke/mindustry/input/AndroidInput.java +++ /dev/null @@ -1,191 +0,0 @@ -package io.anuke.mindustry.input; - -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.input.GestureDetector; -import com.badlogic.gdx.math.Vector2; -import io.anuke.mindustry.core.GameState.State; -import io.anuke.mindustry.net.Net; -import io.anuke.mindustry.net.NetEvents; -import io.anuke.mindustry.resource.ItemStack; -import io.anuke.mindustry.world.Tile; -import io.anuke.ucore.core.Graphics; -import io.anuke.ucore.core.Inputs; -import io.anuke.ucore.core.Timers; -import io.anuke.ucore.scene.ui.layout.Unit; -import io.anuke.ucore.util.Mathf; - -import static io.anuke.mindustry.Vars.*; - -public class AndroidInput extends InputHandler{ - public float lmousex, lmousey; - public float mousex, mousey; - public boolean brokeBlock = false; - public boolean placing = false; - - private boolean enableHold = false; - private float warmup; - private float warmupDelay = 20; - - public AndroidInput(){ - Inputs.addProcessor(new GestureDetector(20, 0.5f, 2, 0.15f, new GestureHandler(this))); - } - - @Override public float getCursorEndX(){ return Gdx.input.getX(0); } - @Override public float getCursorEndY(){ return Gdx.input.getY(0); } - @Override public float getCursorX(){ return mousex; } - @Override public float getCursorY(){ return mousey; } - @Override public boolean drawPlace(){ return (placing && !brokeBlock) || (placeMode.pan && recipe != null); } - - @Override - public boolean touchUp(int screenX, int screenY, int pointer, int button){ - if(brokeBlock){ - brokeBlock = false; - return false; - } - - if(placing && pointer == 0 && !placeMode.pan && !breaking()){ - placeMode.released(getBlockX(), getBlockY(), getBlockEndX(), getBlockEndY()); - }else if(pointer == 0 && !breakMode.pan && breaking() && drawPlace()){ - breakMode.released(getBlockX(), getBlockY(), getBlockEndX(), getBlockEndY()); - } - - placing = false; - return false; - } - - @Override - public boolean touchDown(int screenX, int screenY, int pointer, int button){ - if(ui.hasMouse()){ - brokeBlock = true; - return false; - } - - lmousex = screenX; - lmousey = screenY; - - if((!placeMode.pan || breaking()) && pointer == 0){ - mousex = screenX; - mousey = screenY; - } - - placing = pointer == 0; - - warmup = 0; - - if(!state.is(State.menu)){ - Tile cursor = world.tile(Mathf.scl2(Graphics.mouseWorld().x, tilesize), Mathf.scl2(Graphics.mouseWorld().y, tilesize)); - if(cursor != null && !ui.hasMouse(screenX, screenY)){ - Tile linked = cursor.isLinked() ? cursor.getLinked() : cursor; - if(linked != null && linked.block().isConfigurable(linked)){ - ui.configfrag.showConfig(linked); - }else if(!ui.configfrag.hasConfigMouse()){ - ui.configfrag.hideConfig(); - } - - if(linked != null) { - linked.block().tapped(linked); - if(Net.active()) NetEvents.handleBlockTap(linked); - } - } - } - return false; - } - - @Override - public void resetCursor(){ - mousex = Gdx.graphics.getWidth()/2; - mousey = Gdx.graphics.getHeight()/2; - } - - @Override - public boolean cursorNear(){ - return true; - } - - public Tile selected(){ - Vector2 vec = Graphics.world(mousex, mousey); - return world.tile(Mathf.scl2(vec.x, tilesize), Mathf.scl2(vec.y, tilesize)); - } - - public void breakBlock(){ - Tile tile = selected(); - breaktime += Timers.delta(); - - if(breaktime >= tile.block().breaktime){ - brokeBlock = true; - breakBlock(tile.x, tile.y, true); - breaktime = 0f; - } - } - - @Override - public void update(){ - enableHold = breakMode == PlaceMode.holdDelete; - - float xa = Inputs.getAxis("move_x"); - float ya = Inputs.getAxis("move_y"); - if(Math.abs(xa) < controllerMin) xa = 0; - if(Math.abs(ya) < controllerMin) ya = 0; - - player.x += xa * 4f; - player.y += ya * 4f; - - rotation += Inputs.getAxis("rotate_alt"); - rotation += Inputs.getAxis("rotate"); - rotation = Mathf.mod(rotation, 4); - - if(enableHold && Gdx.input.isTouched(0) && Mathf.near2d(lmousex, lmousey, Gdx.input.getX(0), Gdx.input.getY(0), Unit.dp.scl(50)) - && !ui.hasMouse()){ - warmup += Timers.delta(); - - float lx = mousex, ly = mousey; - - mousex = Gdx.input.getX(0); - mousey = Gdx.input.getY(0); - - Tile sel = selected(); - - if(sel == null) - return; - - if(warmup > warmupDelay && validBreak(sel.x, sel.y)){ - breaktime += Timers.delta(); - - if(breaktime > selected().block().breaktime){ - breakBlock(); - breaktime = 0; - } - } - - mousex = lx; - mousey = ly; - }else{ - warmup = 0; - breaktime = 0; - - mousex = Mathf.clamp(mousex, 0, Gdx.graphics.getWidth()); - mousey = Mathf.clamp(mousey, 0, Gdx.graphics.getHeight()); - } - } - - @Override - public boolean tryPlaceBlock(int x, int y, boolean sound){ - if(recipe != null && - validPlace(x, y, recipe.result) && cursorNear() && - state.inventory.hasItems(recipe.requirements)){ - - placeBlock(x, y, recipe.result, rotation, true, sound); - - for(ItemStack stack : recipe.requirements){ - state.inventory.removeItem(stack); - } - - return true; - } - return false; - } - - public boolean breaking(){ - return recipe == null; - } -} diff --git a/core/src/io/anuke/mindustry/input/Binding.java b/core/src/io/anuke/mindustry/input/Binding.java new file mode 100644 index 0000000000..8b2fd227bf --- /dev/null +++ b/core/src/io/anuke/mindustry/input/Binding.java @@ -0,0 +1,57 @@ +package io.anuke.mindustry.input; + +import io.anuke.arc.Application.ApplicationType; +import io.anuke.arc.Core; +import io.anuke.arc.KeyBinds.*; +import io.anuke.arc.input.InputDevice.DeviceType; +import io.anuke.arc.input.KeyCode; + +public enum Binding implements KeyBind{ + move_x(new Axis(KeyCode.A, KeyCode.D), "general"), + move_y(new Axis(KeyCode.S, KeyCode.W)), + select(KeyCode.MOUSE_LEFT), + deselect(KeyCode.MOUSE_RIGHT), + break_block(KeyCode.MOUSE_RIGHT), + rotate(new Axis(KeyCode.SCROLL)), + diagonal_placement(KeyCode.CONTROL_LEFT), + pick(KeyCode.MOUSE_MIDDLE), + dash(KeyCode.SHIFT_LEFT), + gridMode(KeyCode.GRAVE), + gridModeShift(KeyCode.ALT_LEFT), + zoom_hold(KeyCode.CONTROL_LEFT, "view"), + zoom(new Axis(KeyCode.SCROLL)), + menu(Core.app.getType() == ApplicationType.Android ? KeyCode.BACK : KeyCode.ESCAPE), + pause(KeyCode.SPACE), + minimap(KeyCode.M), + toggle_menus(KeyCode.C), + screenshot(KeyCode.P), + player_list(KeyCode.TAB, "multiplayer"), + chat(KeyCode.ENTER), + chat_history_prev(KeyCode.UP), + chat_history_next(KeyCode.DOWN), + chat_scroll(new Axis(KeyCode.SCROLL)), + + ; + + private final KeybindValue defaultValue; + private final String category; + + Binding(KeybindValue defaultValue, String category){ + this.defaultValue = defaultValue; + this.category = category; + } + + Binding(KeybindValue defaultValue){ + this(defaultValue, null); + } + + @Override + public KeybindValue defaultValue(DeviceType type){ + return defaultValue; + } + + @Override + public String category(){ + return category; + } +} diff --git a/core/src/io/anuke/mindustry/input/DefaultKeybinds.java b/core/src/io/anuke/mindustry/input/DefaultKeybinds.java deleted file mode 100644 index d35b60e229..0000000000 --- a/core/src/io/anuke/mindustry/input/DefaultKeybinds.java +++ /dev/null @@ -1,73 +0,0 @@ -package io.anuke.mindustry.input; - -import com.badlogic.gdx.Application.ApplicationType; -import com.badlogic.gdx.Gdx; -import io.anuke.ucore.core.Inputs.Axis; -import io.anuke.ucore.core.Inputs.DeviceType; -import io.anuke.ucore.core.KeyBinds; -import io.anuke.ucore.util.Input; - -public class DefaultKeybinds { - - public static void load(){ - KeyBinds.defaults( - "move_x", new Axis(Input.A, Input.D), - "move_y", new Axis(Input.S, Input.W), - "select", Input.MOUSE_LEFT, - "break", Input.MOUSE_RIGHT, - "shoot", Input.MOUSE_LEFT, - "zoom_hold", Input.CONTROL_LEFT, - "zoom", new Axis(Input.SCROLL), - "menu", Gdx.app.getType() == ApplicationType.Android ? Input.BACK : Input.ESCAPE, - "pause", Input.SPACE, - "dash", Input.SHIFT_LEFT, - "rotate_alt", new Axis(Input.R, Input.E), - "rotate", new Axis(Input.SCROLL), - "toggle_menus", Input.C, - "block_info", Input.CONTROL_LEFT, - "block_logs", Input.I, - "player_list", Input.TAB, - "chat", Input.ENTER, - "chat_history_prev", Input.UP, - "chat_history_next", Input.DOWN, - "chat_scroll", new Axis(Input.SCROLL), - "console", Input.GRAVE, - "weapon_1", Input.NUM_1, - "weapon_2", Input.NUM_2, - "weapon_3", Input.NUM_3, - "weapon_4", Input.NUM_4, - "weapon_5", Input.NUM_5, - "weapon_6", Input.NUM_6 - ); - - KeyBinds.defaults( - DeviceType.controller, - "move_x", new Axis(Input.CONTROLLER_L_STICK_HORIZONTAL_AXIS), - "move_y", new Axis(Input.CONTROLLER_L_STICK_VERTICAL_AXIS), - "cursor_x", new Axis(Input.CONTROLLER_R_STICK_HORIZONTAL_AXIS), - "cursor_y", new Axis(Input.CONTROLLER_R_STICK_VERTICAL_AXIS), - "select", Input.CONTROLLER_R_BUMPER, - "break", Input.CONTROLLER_L_BUMPER, - "shoot", Input.CONTROLLER_R_TRIGGER, - "zoom_hold", Input.ANY_KEY, - "zoom", new Axis(Input.CONTROLLER_DPAD_DOWN, Input.CONTROLLER_DPAD_UP), - "menu", Input.CONTROLLER_X, - "pause", Input.CONTROLLER_L_TRIGGER, - "dash", Input.CONTROLLER_Y, - "rotate_alt", new Axis(Input.CONTROLLER_DPAD_RIGHT, Input.CONTROLLER_DPAD_LEFT), - "rotate", new Axis(Input.CONTROLLER_A, Input.CONTROLLER_B), - "player_list", Input.CONTROLLER_START, - "chat", Input.ENTER, - "chat_history_prev", Input.UP, - "chat_history_next", Input.DOWN, - "chat_scroll", new Axis(Input.SCROLL), - "console", Input.GRAVE, - "weapon_1", Input.NUM_1, - "weapon_2", Input.NUM_2, - "weapon_3", Input.NUM_3, - "weapon_4", Input.NUM_4, - "weapon_5", Input.NUM_5, - "weapon_6", Input.NUM_6 - ); - } -} diff --git a/core/src/io/anuke/mindustry/input/DesktopInput.java b/core/src/io/anuke/mindustry/input/DesktopInput.java index 7564fae8aa..ebac7e421e 100644 --- a/core/src/io/anuke/mindustry/input/DesktopInput.java +++ b/core/src/io/anuke/mindustry/input/DesktopInput.java @@ -1,190 +1,289 @@ package io.anuke.mindustry.input; -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.math.Vector2; +import io.anuke.arc.Core; +import io.anuke.arc.Graphics.Cursor; +import io.anuke.arc.Graphics.Cursor.SystemCursor; +import io.anuke.arc.graphics.g2d.Draw; +import io.anuke.arc.graphics.g2d.Lines; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.Geometry; +import io.anuke.arc.math.geom.Point2; +import io.anuke.arc.scene.ui.TextField; +import io.anuke.mindustry.content.Blocks; import io.anuke.mindustry.core.GameState.State; +import io.anuke.mindustry.graphics.Pal; +import io.anuke.mindustry.input.PlaceUtils.NormalizeDrawResult; +import io.anuke.mindustry.input.PlaceUtils.NormalizeResult; import io.anuke.mindustry.net.Net; -import io.anuke.mindustry.net.NetEvents; +import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Tile; -import io.anuke.ucore.core.Graphics; -import io.anuke.ucore.core.Inputs; -import io.anuke.ucore.core.Inputs.DeviceType; -import io.anuke.ucore.core.KeyBinds; -import io.anuke.ucore.core.Timers; -import io.anuke.ucore.scene.utils.Cursors; -import io.anuke.ucore.util.Mathf; +import static io.anuke.arc.Core.scene; import static io.anuke.mindustry.Vars.*; +import static io.anuke.mindustry.input.PlaceMode.*; public class DesktopInput extends InputHandler{ - float mousex, mousey; - float endx, endy; - private boolean enableHold = false; - private boolean beganBreak; - private boolean rotated = false, rotatedAlt, zoomed; - - @Override public float getCursorEndX(){ return endx; } - @Override public float getCursorEndY(){ return endy; } - @Override public float getCursorX(){ return Graphics.screen(mousex, mousey).x; } - @Override public float getCursorY(){ return Gdx.graphics.getHeight() - 1 - Graphics.screen(mousex, mousey).y; } - @Override public boolean drawPlace(){ return !beganBreak; } + /** Current cursor type. */ + private Cursor cursorType = SystemCursor.arrow; - @Override - public void update(){ - if(player.isDead()) return; + /** Position where the player started dragging a line. */ + private int selectX, selectY; + /** Whether selecting mode is active. */ + private PlaceMode mode; + /** Animation scale for line. */ + private float selectScale; - if(Inputs.keyRelease("select")){ - placeMode.released(getBlockX(), getBlockY(), getBlockEndX(), getBlockEndY()); - } + private int prevX, prevY, prevRotation; - if(Inputs.keyRelease("break") && !beganBreak){ - breakMode.released(getBlockX(), getBlockY(), getBlockEndX(), getBlockEndY()); - } + /** Draws a placement icon for a specific block. */ + void drawPlace(int x, int y, Block block, int rotation, int prevX, int prevY, int prevRotation){ + if(validPlace(x, y, block, rotation)){ + block.getPlaceDraw(placeDraw, rotation, prevX, prevY, prevRotation); - if((Inputs.keyTap("select") && recipe != null) || Inputs.keyTap("break")){ - Vector2 vec = Graphics.world(Gdx.input.getX(), Gdx.input.getY()); - mousex = vec.x; - mousey = vec.y; - } + Draw.color(); + Draw.rect(placeDraw.region, x * tilesize + block.offset(), y * tilesize + block.offset(), + placeDraw.region.getWidth() * selectScale * Draw.scl * placeDraw.scalex, + placeDraw.region.getHeight() * selectScale * Draw.scl * placeDraw.scaley, + block.rotate ? placeDraw.rotation * 90 : 0); - if(!Inputs.keyDown("select") && !Inputs.keyDown("break")){ - mousex = Graphics.mouseWorld().x; - mousey = Graphics.mouseWorld().y; - } - - endx = Gdx.input.getX(); - endy = Gdx.input.getY(); - - boolean controller = KeyBinds.getSection("default").device.type == DeviceType.controller; - - if(Inputs.getAxisActive("zoom") && (Inputs.keyDown("zoom_hold") || controller) - && !state.is(State.menu) && !ui.hasDialog()){ - if((!zoomed || !controller)) { - renderer.scaleCamera((int) Inputs.getAxis("zoom")); - } - zoomed = true; - }else{ - zoomed = false; - } - - if(!rotated) { - rotation += Inputs.getAxis("rotate_alt"); - rotated = true; - } - if(!Inputs.getAxisActive("rotate_alt")) rotated = false; - - if(!rotatedAlt) { - rotation += Inputs.getAxis("rotate"); - rotatedAlt = true; - } - if(!Inputs.getAxisActive("rotate")) rotatedAlt = false; - - rotation = Mathf.mod(rotation, 4); - - if(Inputs.keyDown("break")){ - breakMode = PlaceMode.areaDelete; - }else{ - breakMode = PlaceMode.hold; - } - - for(int i = 1; i <= 6 && i <= control.upgrades().getWeapons().size; i ++){ - if(Inputs.keyTap("weapon_" + i)){ - player.weaponLeft = player.weaponRight = control.upgrades().getWeapons().get(i - 1); - if(Net.active()) NetEvents.handleWeaponSwitch(); - ui.hudfrag.updateWeapons(); - } - } - - Tile cursor = world.tile(tilex(), tiley()); - Tile target = cursor == null ? null : cursor.isLinked() ? cursor.getLinked() : cursor; - boolean showCursor = false; - - if(recipe == null && target != null && !ui.hasMouse() && Inputs.keyDown("block_info") - && target.block().fullDescription != null){ - showCursor = true; - if(Inputs.keyTap("select")){ - ui.hudfrag.blockfrag.showBlockInfo(target.block()); - Cursors.restoreCursor(); + Draw.color(Pal.accent); + for(int i = 0; i < 4; i++){ + Point2 p = Geometry.d8edge[i]; + float offset = -Math.max(block.size - 1, 0) / 2f * tilesize; + if(i % 2 == 0) + Draw.rect("block-select", x * tilesize + block.offset() + offset * p.x, y * tilesize + block.offset() + offset * p.y, i * 90); } - } - - if(recipe == null && !ui.hasMouse() && Inputs.keyDown("block_logs")) { - showCursor = true; - if(Inputs.keyTap("select")){ - NetEvents.handleBlockLogRequest(getBlockX(), getBlockY()); - Timers.runTask(20f, () -> { - ui.hudfrag.blockfrag.showBlockLogs(getBlockX(), getBlockY()); - Cursors.restoreCursor(); - }); - } - } - - if(target != null && target.block().isConfigurable(target)){ - showCursor = true; + Draw.color(); + }else{ + Draw.color(Pal.removeBack); + Lines.square(x * tilesize + block.offset(), y * tilesize + block.offset() - 1, block.size * tilesize / 2f - 1); + Draw.color(Pal.remove); + Lines.square(x * tilesize + block.offset(), y * tilesize + block.offset(), block.size * tilesize / 2f - 1); } - - if(target != null && Inputs.keyTap("select") && !ui.hasMouse()){ - if(target.block().isConfigurable(target)){ - ui.configfrag.showConfig(target); - }else if(!ui.configfrag.hasConfigMouse()){ - ui.configfrag.hideConfig(); - } + } - target.block().tapped(target); - if(Net.active()) NetEvents.handleBlockTap(target); - } - - if(Inputs.keyTap("break")){ - ui.configfrag.hideConfig(); - } - - if(Inputs.keyRelease("break")){ - beganBreak = false; - } + @Override + public boolean isDrawing(){ + return mode != none || block != null; + } - if(recipe != null && Inputs.keyTap("break")){ - beganBreak = true; - recipe = null; - } + @Override + public void drawOutlined(){ + Lines.stroke(1f); + int cursorX = tileX(Core.input.mouseX()); + int cursorY = tileY(Core.input.mouseY()); - //block breaking - if(enableHold && Inputs.keyDown("break") && cursor != null && validBreak(tilex(), tiley())){ - breaktime += Timers.delta(); - if(breaktime >= cursor.getBreakTime()){ - breakBlock(cursor.x, cursor.y, true); - breaktime = 0f; - } - }else{ - breaktime = 0f; - } + //draw selection(s) + if(mode == placing && block != null){ + prevX = selectX; + prevY = selectY; + prevRotation = rotation; - if(recipe != null){ - showCursor = validPlace(tilex(), tiley(), control.input().recipe.result) && control.input().cursorNear(); - } + iterateLine(selectX, selectY, cursorX, cursorY, l -> { + if(l.last && block.rotate){ + drawArrow(block, l.x, l.y, l.rotation); + } + drawPlace(l.x, l.y, block, l.rotation, prevX - l.x, prevY - l.y, prevRotation); - if(!ui.hasMouse()) { - if (showCursor) - Cursors.setHand(); - else - Cursors.restoreCursor(); - } + prevX = l.x; + prevY = l.y; + prevRotation = l.rotation; + }); - } + }else if(mode == breaking){ + NormalizeDrawResult result = PlaceUtils.normalizeDrawArea(Blocks.air, selectX, selectY, cursorX, cursorY, false, maxLength, 1f); + NormalizeResult dresult = PlaceUtils.normalizeArea(selectX, selectY, cursorX, cursorY, rotation, false, maxLength); - public int tilex(){ - return (recipe != null && recipe.result.isMultiblock() && - recipe.result.width % 2 == 0) ? - Mathf.scl(Graphics.mouseWorld().x, tilesize) : Mathf.scl2(Graphics.mouseWorld().x, tilesize); - } + for(int x = dresult.x; x <= dresult.x2; x++){ + for(int y = dresult.y; y <= dresult.y2; y++){ + Tile tile = world.ltile(x, y); + if(tile == null || !validBreak(tile.x, tile.y)) continue; - public int tiley(){ - return (recipe != null && recipe.result.isMultiblock() && - recipe.result.height % 2 == 0) ? - Mathf.scl(Graphics.mouseWorld().y, tilesize) : Mathf.scl2(Graphics.mouseWorld().y, tilesize); - } + Draw.color(Pal.removeBack); + Lines.square(tile.drawx(), tile.drawy() - 1, tile.block().size * tilesize / 2f - 1); + Draw.color(Pal.remove); + Lines.square(tile.drawx(), tile.drawy(), tile.block().size * tilesize / 2f - 1); + } + } - @Override - public boolean keyDown(int keycode) { - return super.keyDown(keycode); - } + Draw.color(Pal.removeBack); + Lines.rect(result.x, result.y - 1, result.x2 - result.x, result.y2 - result.y); + Draw.color(Pal.remove); + Lines.rect(result.x, result.y, result.x2 - result.x, result.y2 - result.y); + }else if(isPlacing()){ + if(block.rotate){ + drawArrow(block, cursorX, cursorY, rotation); + } + drawPlace(cursorX, cursorY, block, rotation, cursorX, cursorY, rotation); + block.drawPlace(cursorX, cursorY, rotation, validPlace(cursorX, cursorY, block, rotation)); + } + + Draw.reset(); + } + + @Override + public void update(){ + if(Net.active() && Core.input.keyTap(Binding.player_list)){ + ui.listfrag.toggle(); + } + + if(Core.input.keyRelease(Binding.select)){ + player.isShooting = false; + } + + if(!state.is(State.menu) && Core.input.keyTap(Binding.minimap) && !ui.chatfrag.chatOpen() && !(scene.getKeyboardFocus() instanceof TextField)){ + if(!ui.minimap.isShown()){ + ui.minimap.show(); + }else{ + ui.minimap.hide(); + } + } + + if(state.is(State.menu) || Core.scene.hasDialog()) return; + + //zoom and rotate things + if(Math.abs(Core.input.axisTap(Binding.zoom)) > 0 && (Core.input.keyDown(Binding.zoom_hold))){ + renderer.scaleCamera(Core.input.axisTap(Binding.zoom)); + } + + if(player.isDead()){ + cursorType = SystemCursor.arrow; + return; + } + + pollInput(); + + //deselect if not placing + if(!isPlacing() && mode == placing){ + mode = none; + } + + if(player.isShooting && !canShoot()){ + player.isShooting = false; + } + + if(isPlacing()){ + cursorType = SystemCursor.hand; + selectScale = Mathf.lerpDelta(selectScale, 1f, 0.2f); + }else{ + selectScale = 0f; + } + + rotation = Mathf.mod(rotation + (int)Core.input.axisTap(Binding.rotate), 4); + + Tile cursor = tileAt(Core.input.mouseX(), Core.input.mouseY()); + + if(cursor != null){ + cursor = cursor.link(); + + cursorType = cursor.block().getCursor(cursor); + + if(isPlacing()){ + cursorType = SystemCursor.hand; + } + + if(!isPlacing() && canMine(cursor)){ + cursorType = ui.drillCursor; + } + + if(canTapPlayer(Core.input.mouseWorld().x, Core.input.mouseWorld().y)){ + cursorType = ui.unloadCursor; + } + } + + if(!Core.scene.hasMouse()){ + Core.graphics.cursor(cursorType); + } + + cursorType = SystemCursor.arrow; + } + + void pollInput(){ + Tile selected = tileAt(Core.input.mouseX(), Core.input.mouseY()); + int cursorX = tileX(Core.input.mouseX()); + int cursorY = tileY(Core.input.mouseY()); + + if(Core.input.keyTap(Binding.deselect)){ + player.setMineTile(null); + } + + if(Core.input.keyTap(Binding.select) && !Core.scene.hasMouse()){ + if(isPlacing()){ + selectX = cursorX; + selectY = cursorY; + mode = placing; + }else if(selected != null){ + //only begin shooting if there's no cursor event + if(!tileTapped(selected) && !tryTapPlayer(Core.input.mouseWorld().x, Core.input.mouseWorld().y) && player.buildQueue().size == 0 && !droppingItem && + !tryBeginMine(selected) && player.getMineTile() == null && !ui.chatfrag.chatOpen()){ + player.isShooting = true; + } + }else if(!ui.chatfrag.chatOpen()){ //if it's out of bounds, shooting is just fine + player.isShooting = true; + } + }else if(Core.input.keyTap(Binding.deselect) && (block != null || mode != none || player.isBuilding()) && + !(player.buildRequest() != null && player.buildRequest().breaking && Core.keybinds.get(Binding.deselect) == Core.keybinds.get(Binding.break_block))){ + if(block == null){ + player.clearBuilding(); + } + + block = null; + mode = none; + }else if(Core.input.keyTap(Binding.break_block) && !Core.scene.hasMouse()){ + //is recalculated because setting the mode to breaking removes potential multiblock cursor offset + mode = breaking; + selectX = tileX(Core.input.mouseX()); + selectY = tileY(Core.input.mouseY()); + } + + if(Core.input.keyRelease(Binding.break_block) || Core.input.keyRelease(Binding.select)){ + + if(mode == placing && block != null){ //touch up while placing, place everything in selection + iterateLine(selectX, selectY, cursorX, cursorY, l -> { + rotation = l.rotation; + tryPlaceBlock(l.x, l.y); + }); + }else if(mode == breaking){ //touch up while breaking, break everything in selection + NormalizeResult result = PlaceUtils.normalizeArea(selectX, selectY, cursorX, cursorY, rotation, false, maxLength); + for(int x = 0; x <= Math.abs(result.x2 - result.x); x++){ + for(int y = 0; y <= Math.abs(result.y2 - result.y); y++){ + int wx = selectX + x * Mathf.sign(cursorX - selectX); + int wy = selectY + y * Mathf.sign(cursorY - selectY); + + tryBreakBlock(wx, wy); + } + } + } + + if(selected != null){ + tryDropItems(selected.link(), Core.input.mouseWorld().x, Core.input.mouseWorld().y); + } + + mode = none; + } + } + + @Override + public boolean selectedBlock(){ + return isPlacing() && mode != breaking; + } + + @Override + public float getMouseX(){ + return Core.input.mouseX(); + } + + @Override + public float getMouseY(){ + return Core.input.mouseY(); + } + + @Override + public void updateController(){ + if(state.is(State.menu)){ + droppingItem = false; + mode = none; + block = null; + } + } } diff --git a/core/src/io/anuke/mindustry/input/GestureHandler.java b/core/src/io/anuke/mindustry/input/GestureHandler.java deleted file mode 100644 index e7544ed613..0000000000 --- a/core/src/io/anuke/mindustry/input/GestureHandler.java +++ /dev/null @@ -1,116 +0,0 @@ -package io.anuke.mindustry.input; - -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.input.GestureDetector.GestureAdapter; -import com.badlogic.gdx.math.Vector2; -import io.anuke.ucore.core.Core; -import io.anuke.ucore.core.Inputs; -import io.anuke.ucore.scene.ui.layout.Unit; -import io.anuke.ucore.util.Mathf; - -import static io.anuke.mindustry.Vars.*; - -public class GestureHandler extends GestureAdapter{ - AndroidInput input; - Vector2 pinch1 = new Vector2(-1, -1), pinch2 = pinch1.cpy(); - Vector2 vector = new Vector2(); - float initzoom = -1; - boolean zoomed = false; - - GestureHandler(AndroidInput input){ - this.input = input; - } - - @Override - public boolean longPress(float x, float y){ - return false; - } - - @Override - public boolean tap (float x, float y, int count, int button) { - if(ui.hasMouse() || input.brokeBlock) return false; - - if(!control.input().placeMode.pan || control.input().recipe == null){ - input.mousex = x; - input.mousey = y; - - if(control.input().recipe == null) - control.input().breakMode.tapped(input.getBlockX(), input.getBlockY()); - else - control.input().placeMode.tapped(input.getBlockX(), input.getBlockY()); - } - return false; - } - - @Override - public boolean pan(float x, float y, float deltaX, float deltaY){ - if(control.showCursor() && !Inputs.keyDown("select")) return false; - - if((!control.showCursor() && !(control.input().recipe != null - && control.input().placeMode.lockCamera && state.inventory.hasItems(control.input().recipe.requirements)) && - !(control.input().recipe == null && control.input().breakMode.lockCamera)) && !ui.hasMouse(x, y)){ - float dx = deltaX*Core.camera.zoom/Core.cameraScale, dy = deltaY*Core.camera.zoom/Core.cameraScale; - player.x -= dx; - player.y += dy; - player.targetAngle = Mathf.atan2(dx, -dy) + 180f; - }else if(control.input().placeMode.lockCamera && (control.input().placeMode.pan && control.input().recipe != null)){ - input.mousex += deltaX; - input.mousey += deltaY; - } - - return false; - } - - @Override - public boolean pinch (Vector2 initialPointer1, Vector2 initialPointer2, Vector2 pointer1, Vector2 pointer2) { - if(control.input().recipe == null && !control.input().breakMode.lockCamera) - return false; - - if(pinch1.x < 0){ - pinch1.set(initialPointer1); - pinch2.set(initialPointer2); - } - - Vector2 vec = (vector.set(pointer1).add(pointer2).scl(0.5f)).sub(pinch1.add(pinch2).scl(0.5f)); - - player.x -= vec.x*Core.camera.zoom/Core.cameraScale; - player.y += vec.y*Core.camera.zoom/Core.cameraScale; - - pinch1.set(pointer1); - pinch2.set(pointer2); - - return false; - } - - @Override - public boolean zoom(float initialDistance, float distance){ - if(initzoom < 0){ - initzoom = initialDistance; - } - - if(Math.abs(distance - initzoom) > Unit.dp.scl(100f) && !zoomed){ - int amount = (distance > initzoom ? 1 : -1); - renderer.scaleCamera(Math.round(Unit.dp.scl(amount))); - initzoom = distance; - zoomed = true; - return true; - } - - return false; - } - - @Override - public void pinchStop () { - initzoom = -1; - pinch2.set(pinch1.set(-1, -1)); - zoomed = false; - } - - int touches(){ - int sum = 0; - for(int i = 0; i < 10; i ++){ - if(Gdx.input.isTouched(i)) sum++; - } - return sum; - } -} diff --git a/core/src/io/anuke/mindustry/input/InputHandler.java b/core/src/io/anuke/mindustry/input/InputHandler.java index 6e1a32b932..f69c9331a6 100644 --- a/core/src/io/anuke/mindustry/input/InputHandler.java +++ b/core/src/io/anuke/mindustry/input/InputHandler.java @@ -1,141 +1,402 @@ package io.anuke.mindustry.input; -import com.badlogic.gdx.InputAdapter; -import com.badlogic.gdx.math.GridPoint2; -import com.badlogic.gdx.math.Vector2; +import io.anuke.annotations.Annotations.Loc; +import io.anuke.annotations.Annotations.Remote; +import io.anuke.arc.Core; +import io.anuke.arc.collection.Array; +import io.anuke.arc.function.Consumer; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.graphics.g2d.Draw; +import io.anuke.arc.graphics.g2d.TextureRegion; +import io.anuke.arc.input.InputProcessor; +import io.anuke.arc.math.Angles; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.Point2; +import io.anuke.arc.math.geom.Vector2; +import io.anuke.arc.scene.ui.layout.Table; +import io.anuke.arc.util.Time; +import io.anuke.arc.util.Tmp; +import io.anuke.mindustry.content.Blocks; +import io.anuke.mindustry.content.Fx; +import io.anuke.mindustry.entities.Effects; +import io.anuke.mindustry.entities.effect.ItemTransfer; +import io.anuke.mindustry.entities.traits.BuilderTrait.BuildRequest; +import io.anuke.mindustry.entities.type.Player; +import io.anuke.mindustry.gen.Call; +import io.anuke.mindustry.graphics.Pal; import io.anuke.mindustry.net.Net; -import io.anuke.mindustry.net.NetEvents; -import io.anuke.mindustry.resource.ItemStack; -import io.anuke.mindustry.resource.Recipe; -import io.anuke.mindustry.world.Block; -import io.anuke.mindustry.world.Placement; -import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.blocks.Blocks; -import io.anuke.ucore.core.Graphics; -import io.anuke.ucore.util.Mathf; +import io.anuke.mindustry.net.ValidateException; +import io.anuke.mindustry.type.Item; +import io.anuke.mindustry.type.ItemStack; +import io.anuke.mindustry.ui.fragments.OverlayFragment; +import io.anuke.mindustry.world.*; import static io.anuke.mindustry.Vars.*; -public abstract class InputHandler extends InputAdapter{ - public float breaktime = 0; - public Recipe recipe; - public int rotation; - public PlaceMode placeMode = mobile ? PlaceMode.cursor : PlaceMode.hold; - public PlaceMode breakMode = mobile ? PlaceMode.none : PlaceMode.holdDelete; - public PlaceMode lastPlaceMode = placeMode; - public PlaceMode lastBreakMode = breakMode; +public abstract class InputHandler implements InputProcessor{ + /** Used for dropping items. */ + final static float playerSelectRange = mobile ? 17f : 11f; + /** Maximum line length. */ + final static int maxLength = 100; + final static Vector2 stackTrns = new Vector2(); + /** Distance on the back from where items originate. */ + final static float backTrns = 3f; - public abstract void update(); - public abstract float getCursorX(); - public abstract float getCursorY(); - public abstract float getCursorEndX(); - public abstract float getCursorEndY(); - public int getBlockX(){ return Mathf.sclb(Graphics.world(getCursorX(), getCursorY()).x, tilesize, round2()); } - public int getBlockY(){ return Mathf.sclb(Graphics.world(getCursorX(), getCursorY()).y, tilesize, round2()); } - public int getBlockEndX(){ return Mathf.sclb(Graphics.world(getCursorEndX(), getCursorEndY()).x, tilesize, round2()); } - public int getBlockEndY(){ return Mathf.sclb(Graphics.world(getCursorEndX(), getCursorEndY()).y, tilesize, round2()); } - public void resetCursor(){} - public boolean drawPlace(){ return true; } - - public boolean onConfigurable(){ - Tile tile = world.tile(getBlockX(), getBlockY()); - return tile != null && (tile.block().isConfigurable(tile) || (tile.isLinked() && tile.getLinked().block().isConfigurable(tile))); - } - - public boolean cursorNear(){ - return Vector2.dst(player.x, player.y, getBlockX() * tilesize, getBlockY() * tilesize) <= placerange || - state.mode.infiniteResources; - } - - public boolean tryPlaceBlock(int x, int y, boolean sound){ - if(recipe != null && - validPlace(x, y, recipe.result) && !ui.hasMouse() && cursorNear() && - state.inventory.hasItems(recipe.requirements)){ - - placeBlock(x, y, recipe.result, rotation, true, sound); - - for(ItemStack stack : recipe.requirements){ - state.inventory.removeItem(stack); - } + public final OverlayFragment frag = new OverlayFragment(this); - return true; - } - return false; - } - - public boolean tryDeleteBlock(int x, int y, boolean sound){ - if(cursorNear() && validBreak(x, y)){ - breakBlock(x, y, sound); - return true; - } - return false; - } - - public boolean round2(){ - return !(recipe != null && recipe.result.isMultiblock() && recipe.result.height % 2 == 0); - } - - public boolean validPlace(int x, int y, Block type){ - - if(!type.isMultiblock() && control.tutorial().active() && - control.tutorial().showBlock()){ - - GridPoint2 point = control.tutorial().getPlacePoint(); - int rotation = control.tutorial().getPlaceRotation(); - Block block = control.tutorial().getPlaceBlock(); - - if(type != block || point.x != x - world.getCore().x || point.y != y - world.getCore().y - || (rotation != -1 && rotation != this.rotation)){ - return false; - } - }else if(control.tutorial().active()){ - return false; - } + public Block block; + public int rotation; + public boolean droppingItem; - return Placement.validPlace(x, y, type); - } - - public boolean validBreak(int x, int y){ - if(control.tutorial().active()){ - - if(control.tutorial().showBlock()){ - GridPoint2 point = control.tutorial().getPlacePoint(); - int rotation = control.tutorial().getPlaceRotation(); - Block block = control.tutorial().getPlaceBlock(); - - if(block != Blocks.air || point.x != x - world.getCore().x || point.y != y - world.getCore().y - || (rotation != -1 && rotation != this.rotation)){ - return false; - } - }else{ - return false; - } - } - - return Placement.validBreak(x, y); - } - - public void placeBlock(int x, int y, Block result, int rotation, boolean effects, boolean sound){ - if(!Net.client()){ //is server or singleplayer - Placement.placeBlock(x, y, result, rotation, effects, sound); - } + protected PlaceDraw placeDraw = new PlaceDraw(); + private PlaceLine line = new PlaceLine(); - if(Net.active()){ - NetEvents.handlePlace(x, y, result, rotation); - } + //methods to override - if(!Net.client()){ - Tile tile = world.tile(x, y); - if(tile != null) result.placed(tile); - } - } + @Remote(targets = Loc.client, called = Loc.server) + public static void dropItem(Player player, float angle){ + if(Net.server() && player.item().amount <= 0){ + throw new ValidateException(player, "Player cannot drop an item."); + } - public void breakBlock(int x, int y, boolean sound){ - if(!Net.client()) - Placement.breakBlock(x, y, true, sound); + Effects.effect(Fx.dropItem, Color.WHITE, player.x, player.y, angle, player.item().item); + player.clearItem(); + } - if(Net.active()){ - NetEvents.handleBreak(x, y); - } - } + @Remote(targets = Loc.both, forward = true, called = Loc.server) + public static void transferInventory(Player player, Tile tile){ + if(Net.server() && (player.item().amount <= 0 || player.isTransferring)){ + throw new ValidateException(player, "Player cannot transfer an item."); + } + + if(player == null || tile.entity == null) return; + + player.isTransferring = true; + + Item item = player.item().item; + int amount = player.item().amount; + int accepted = tile.block().acceptStack(item, amount, tile, player); + player.item().amount -= accepted; + + int sent = Mathf.clamp(accepted / 4, 1, 8); + int removed = accepted / sent; + int[] remaining = {accepted, accepted}; + Block block = tile.block(); + + for(int i = 0; i < sent; i++){ + boolean end = i == sent - 1; + Time.run(i * 3, () -> { + tile.block().getStackOffset(item, tile, stackTrns); + + ItemTransfer.create(item, + player.x + Angles.trnsx(player.rotation + 180f, backTrns), player.y + Angles.trnsy(player.rotation + 180f, backTrns), + new Vector2(tile.drawx() + stackTrns.x, tile.drawy() + stackTrns.y), () -> { + if(tile.block() != block || tile.entity == null || tile.entity.items == null) return; + + tile.block().handleStack(item, removed, tile, player); + remaining[1] -= removed; + + if(end && remaining[1] > 0){ + tile.block().handleStack(item, remaining[1], tile, player); + } + }); + + remaining[0] -= removed; + + if(end){ + player.isTransferring = false; + } + }); + } + } + + @Remote(targets = Loc.both, called = Loc.server, forward = true) + public static void onTileTapped(Player player, Tile tile){ + if(tile == null || player == null) return; + tile.block().tapped(tile, player); + } + + public OverlayFragment getFrag(){ + return frag; + } + + public void update(){ + + } + + public float getMouseX(){ + return Core.input.mouseX(); + } + + public float getMouseY(){ + return Core.input.mouseY(); + } + + public void buildUI(Table table){ + + } + + public void updateController(){ + + } + + public void drawOutlined(){ + + } + + public void drawTop(){ + + } + + public boolean isDrawing(){ + return false; + } + + /** Handles tile tap events that are not platform specific. */ + boolean tileTapped(Tile tile){ + tile = tile.link(); + + boolean consumed = false, showedInventory = false; + + //check if tapped block is configurable + if(tile.block().configurable && tile.interactable(player.getTeam())){ + consumed = true; + if(((!frag.config.isShown() && tile.block().shouldShowConfigure(tile, player)) //if the config fragment is hidden, show + //alternatively, the current selected block can 'agree' to switch config tiles + || (frag.config.isShown() && frag.config.getSelectedTile().block().onConfigureTileTapped(frag.config.getSelectedTile(), tile)))){ + frag.config.showConfig(tile); + } + //otherwise... + }else if(!frag.config.hasConfigMouse()){ //make sure a configuration fragment isn't on the cursor + //then, if it's shown and the current block 'agrees' to hide, hide it. + if(frag.config.isShown() && frag.config.getSelectedTile().block().onConfigureTileTapped(frag.config.getSelectedTile(), tile)){ + consumed = true; + frag.config.hideConfig(); + } + + if(frag.config.isShown()){ + consumed = true; + } + } + + //call tapped event + if(!consumed && tile.interactable(player.getTeam())){ + Call.onTileTapped(player, tile); + } + + //consume tap event if necessary + if(tile.interactable(player.getTeam()) && tile.block().consumesTap){ + consumed = true; + }else if(tile.interactable(player.getTeam()) && tile.block().synthetic() && !consumed){ + if(tile.block().hasItems && tile.entity.items.total() > 0){ + frag.inv.showFor(tile); + consumed = true; + showedInventory = true; + } + } + + //clear when the player taps on something else + if(!consumed && !mobile && player.isBuilding() && block == null){ + player.clearBuilding(); + block = null; + return true; + } + + if(!showedInventory){ + frag.inv.hide(); + } + + return consumed; + } + + /** Tries to select the player to drop off items, returns true if successful. */ + boolean tryTapPlayer(float x, float y){ + if(canTapPlayer(x, y)){ + droppingItem = true; + return true; + } + return false; + } + + boolean canTapPlayer(float x, float y){ + return Mathf.dst(x, y, player.x, player.y) <= playerSelectRange && player.item().amount > 0; + } + + /** Tries to begin mining a tile, returns true if successful. */ + boolean tryBeginMine(Tile tile){ + if(canMine(tile)){ + //if a block is clicked twice, reset it + player.setMineTile(player.getMineTile() == tile ? null : tile); + return true; + } + return false; + } + + boolean canMine(Tile tile){ + return !Core.scene.hasMouse() + && tile.drop() != null && tile.drop().hardness <= player.mech.drillPower + && !(tile.floor().playerUnmineable && tile.overlay().itemDrop == null) + && player.acceptsItem(tile.drop()) + && tile.block() == Blocks.air && player.dst(tile.worldx(), tile.worldy()) <= Player.mineDistance; + } + + /** Returns the tile at the specified MOUSE coordinates. */ + Tile tileAt(float x, float y){ + return world.tile(tileX(x), tileY(y)); + } + + int tileX(float cursorX){ + Vector2 vec = Core.input.mouseWorld(cursorX, 0); + if(selectedBlock()){ + vec.sub(block.offset(), block.offset()); + } + return world.toTile(vec.x); + } + + int tileY(float cursorY){ + Vector2 vec = Core.input.mouseWorld(0, cursorY); + if(selectedBlock()){ + vec.sub(block.offset(), block.offset()); + } + return world.toTile(vec.y); + } + + public boolean selectedBlock(){ + return isPlacing(); + } + + public boolean isPlacing(){ + return block != null; + } + + public float mouseAngle(float x, float y){ + return Core.input.mouseWorld(getMouseX(), getMouseY()).sub(x, y).angle(); + } + + public void remove(){ + Core.input.removeProcessor(this); + frag.remove(); + } + + public boolean canShoot(){ + return block == null && !Core.scene.hasMouse() && !onConfigurable() && !isDroppingItem(); + } + + public boolean onConfigurable(){ + return false; + } + + public boolean isDroppingItem(){ + return droppingItem; + } + + public void tryDropItems(Tile tile, float x, float y){ + if(!droppingItem || player.item().amount <= 0 || canTapPlayer(x, y) || state.isPaused()){ + droppingItem = false; + return; + } + + droppingItem = false; + + ItemStack stack = player.item(); + + if(tile.block().acceptStack(stack.item, stack.amount, tile, player) > 0 && tile.interactable(player.getTeam()) && tile.block().hasItems){ + Call.transferInventory(player, tile); + }else{ + Call.dropItem(player.angleTo(x, y)); + } + } + + public void tryPlaceBlock(int x, int y){ + if(block != null && validPlace(x, y, block, rotation)){ + placeBlock(x, y, block, rotation); + } + } + + public void tryBreakBlock(int x, int y){ + if(validBreak(x, y)){ + breakBlock(x, y); + } + } + + public boolean validPlace(int x, int y, Block type, int rotation){ + return Build.validPlace(player.getTeam(), x, y, type, rotation); + } + + public boolean validBreak(int x, int y){ + return Build.validBreak(player.getTeam(), x, y) && Mathf.dst(player.x, player.y, x * tilesize, y * tilesize) < Player.placeDistance; + } + + public void placeBlock(int x, int y, Block block, int rotation){ + player.addBuildRequest(new BuildRequest(x, y, rotation, block)); + } + + public void breakBlock(int x, int y){ + Tile tile = world.ltile(x, y); + player.addBuildRequest(new BuildRequest(tile.x, tile.y)); + } + + void drawArrow(Block block, int x, int y, int rotation){ + Draw.color(!validPlace(x, y, block, rotation) ? Pal.removeBack : Pal.accentBack); + Draw.rect(Core.atlas.find("place-arrow"), + x * tilesize + block.offset(), + y * tilesize + block.offset() - 1, + Core.atlas.find("place-arrow").getWidth() * Draw.scl, + Core.atlas.find("place-arrow").getHeight() * Draw.scl, rotation * 90 - 90); + + Draw.color(!validPlace(x, y, block, rotation) ? Pal.remove : Pal.accent); + Draw.rect(Core.atlas.find("place-arrow"), + x * tilesize + block.offset(), + y * tilesize + block.offset(), + Core.atlas.find("place-arrow").getWidth() * Draw.scl, + Core.atlas.find("place-arrow").getHeight() * Draw.scl, rotation * 90 - 90); + } + + void iterateLine(int startX, int startY, int endX, int endY, Consumer cons){ + Array points; + boolean diagonal = Core.input.keyDown(Binding.diagonal_placement); + if(Core.settings.getBool("swapdiagonal")){ + diagonal = !diagonal; + } + + if(diagonal){ + points = PlaceUtils.normalizeDiagonal(startX, startY, endX, endY); + }else{ + points = PlaceUtils.normalizeLine(startX, startY, endX, endY); + } + + float angle = Angles.angle(startX, startY, endX, endY); + int baseRotation = (startX == endX && startY == endY) ? rotation : ((int)((angle + 45) / 90f)) % 4; + + Tmp.r3.set(-1, -1, 0, 0); + + for(int i = 0; i < points.size; i++){ + Point2 point = points.get(i); + + if(block != null && Tmp.r2.setSize(block.size * tilesize).setCenter(point.x * tilesize + block.offset(), point.y * tilesize + block.offset()).overlaps(Tmp.r3)){ + continue; + } + + Point2 next = i == points.size - 1 ? null : points.get(i + 1); + line.x = point.x; + line.y = point.y; + line.rotation = next != null ? Tile.relativeTo(point.x, point.y, next.x, next.y) : baseRotation; + line.last = next == null; + cons.accept(line); + + Tmp.r3.setSize(block.size * tilesize).setCenter(point.x * tilesize + block.offset(), point.y * tilesize + block.offset()); + } + } + + public static class PlaceDraw{ + public int rotation, scalex, scaley; + public TextureRegion region; + + public static final PlaceDraw instance = new PlaceDraw(); + } + + class PlaceLine{ + public int x, y, rotation; + public boolean last; + } } diff --git a/core/src/io/anuke/mindustry/input/MobileInput.java b/core/src/io/anuke/mindustry/input/MobileInput.java new file mode 100644 index 0000000000..90d3deeb08 --- /dev/null +++ b/core/src/io/anuke/mindustry/input/MobileInput.java @@ -0,0 +1,769 @@ +package io.anuke.mindustry.input; + +import io.anuke.arc.Core; +import io.anuke.arc.collection.Array; +import io.anuke.arc.function.BooleanProvider; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.graphics.g2d.*; +import io.anuke.arc.input.GestureDetector; +import io.anuke.arc.input.GestureDetector.GestureListener; +import io.anuke.arc.input.KeyCode; +import io.anuke.arc.math.Interpolation; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.*; +import io.anuke.arc.scene.actions.Actions; +import io.anuke.arc.scene.event.Touchable; +import io.anuke.arc.scene.ui.layout.Table; +import io.anuke.arc.util.*; +import io.anuke.mindustry.content.Blocks; +import io.anuke.mindustry.content.Fx; +import io.anuke.mindustry.core.GameState.State; +import io.anuke.mindustry.entities.Effects; +import io.anuke.mindustry.entities.Units; +import io.anuke.mindustry.entities.traits.TargetTrait; +import io.anuke.mindustry.entities.type.TileEntity; +import io.anuke.mindustry.entities.type.Unit; +import io.anuke.mindustry.graphics.Pal; +import io.anuke.mindustry.input.PlaceUtils.NormalizeDrawResult; +import io.anuke.mindustry.input.PlaceUtils.NormalizeResult; +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.Tile; + +import static io.anuke.mindustry.Vars.*; +import static io.anuke.mindustry.input.PlaceMode.*; + +public class MobileInput extends InputHandler implements GestureListener{ + /** Maximum speed the player can pan. */ + private static final float maxPanSpeed = 1.3f; + private static Rectangle r1 = new Rectangle(), r2 = new Rectangle(); + /** Distance to edge of screen to start panning. */ + private final float edgePan = io.anuke.arc.scene.ui.layout.Unit.dp.scl(60f); + + //gesture data + private Vector2 vector = new Vector2(); + private float lastDistance = -1f; + + /** Position where the player started dragging a line. */ + private int lineStartX, lineStartY; + + /** Animation scale for line. */ + private float lineScale; + /** Animation data for crosshair. */ + private float crosshairScale; + private TargetTrait lastTarget; + /** Used for shifting build requests. */ + private float shiftDeltaX, shiftDeltaY; + + /** List of currently selected tiles to place. */ + private Array selection = new Array<>(); + /** Place requests to be removed. */ + private Array removals = new Array<>(); + /** Whether or not the player is currently shifting all placed tiles. */ + private boolean selecting; + /** Whether the player is currently in line-place mode. */ + private boolean lineMode; + /** Current place mode. */ + private PlaceMode mode = none; + /** Whether no recipe was available when switching to break mode. */ + private Block lastBlock; + /** Last placed request. Used for drawing block overlay. */ + private PlaceRequest lastPlaced; + + private int prevX, prevY, prevRotation; + + public MobileInput(){ + Core.input.addProcessor(new GestureDetector(20, 0.5f, 0.4f, 0.15f, this)); + } + + //region utility methods + + /** Check and assign targets for a specific position. */ + void checkTargets(float x, float y){ + Unit unit = Units.closestEnemy(player.getTeam(), x, y, 20f, u -> !u.isDead()); + + if(unit != null){ + player.setMineTile(null); + player.target = unit; + }else{ + Tile tile = world.ltileWorld(x, y); + + if(tile != null && tile.synthetic() && state.teams.areEnemies(player.getTeam(), tile.getTeam())){ + TileEntity entity = tile.entity; + player.setMineTile(null); + player.target = entity; + }else if(tile != null && player.mech.canHeal && tile.entity != null && tile.getTeam() == player.getTeam() && tile.entity.damaged()){ + player.setMineTile(null); + player.target = tile.entity; + } + } + } + + /** Returns whether this tile is in the list of requests, or at least colliding with one. */ + boolean hasRequest(Tile tile){ + return getRequest(tile) != null; + } + + /** Returns whether this block overlaps any selection requests. */ + boolean checkOverlapPlacement(int x, int y, Block block){ + r2.setSize(block.size * tilesize); + r2.setCenter(x * tilesize + block.offset(), y * tilesize + block.offset()); + + for(PlaceRequest req : selection){ + Tile other = req.tile(); + + if(other == null || req.remove) continue; + + r1.setSize(req.block.size * tilesize); + r1.setCenter(other.worldx() + req.block.offset(), other.worldy() + req.block.offset()); + + if(r2.overlaps(r1)){ + return true; + } + } + return false; + } + + /** Returns the selection request that overlaps this tile, or null. */ + PlaceRequest getRequest(Tile tile){ + r2.setSize(tilesize); + r2.setCenter(tile.worldx(), tile.worldy()); + + for(PlaceRequest req : selection){ + Tile other = req.tile(); + + if(other == null) continue; + + if(!req.remove){ + r1.setSize(req.block.size * tilesize); + r1.setCenter(other.worldx() + req.block.offset(), other.worldy() + req.block.offset()); + + if(r2.overlaps(r1)){ + return req; + } + }else{ + + r1.setSize(other.block().size * tilesize); + r1.setCenter(other.worldx() + other.block().offset(), other.worldy() + other.block().offset()); + + if(r2.overlaps(r1)){ + return req; + } + } + } + return null; + } + + void removeRequest(PlaceRequest request){ + selection.removeValue(request, true); + removals.add(request); + } + + void showGuide(String type, BooleanProvider done){ + if(!Core.settings.getBool(type, false)){ + Core.scene.table("guideDim", t -> { + t.margin(10f); + t.touchable(Touchable.disabled); + t.top().table("button", s -> s.add("$" + type).growX().wrap().labelAlign(Align.center, Align.center)).growX(); + t.update(() -> { + if((done.get() || state.is(State.menu)) && t.getUserObject() == null){ + t.actions(Actions.delay(1f), Actions.fadeOut(1f, Interpolation.fade), Actions.remove()); + t.setUserObject("ha"); + } + }); + }); + Core.settings.put(type, true); + data.modified(); + } + } + + boolean isLinePlacing(){ + return mode == placing && lineMode && Mathf.dst(lineStartX * tilesize, lineStartY * tilesize, Core.input.mouseWorld().x, Core.input.mouseWorld().y) >= 3 * tilesize; + } + + boolean isAreaBreaking(){ + return mode == breaking && lineMode && Mathf.dst(lineStartX * tilesize, lineStartY * tilesize, Core.input.mouseWorld().x, Core.input.mouseWorld().y) >= 2 * tilesize; + } + + //endregion + //region UI and drawing + + void drawRequest(PlaceRequest request, PlaceRequest prev){ + Tile tile = request.tile(); + + if(!request.remove){ + if(prev != null){ + request.block.getPlaceDraw(placeDraw, request.rotation, prev.x - request.x, prev.y - request.y, prev.rotation); + }else{ + request.block.getPlaceDraw(placeDraw, request.rotation, 0, 0, request.rotation); + } + + //draw placing request + float offset = request.block.offset(); + TextureRegion region = placeDraw.region; + + Draw.mixcol(Pal.accent, Mathf.clamp((1f - request.scale) / 0.5f)); + Draw.tint(Color.WHITE, Pal.breakInvalid, request.redness); + + Draw.rect(region, tile.worldx() + offset, tile.worldy() + offset, + region.getWidth() * request.scale * Draw.scl * placeDraw.scalex, + region.getHeight() * request.scale * Draw.scl * placeDraw.scaley, + request.block.rotate ? placeDraw.rotation * 90 : 0); + + Draw.mixcol(Pal.accent, 1f); + for(int i = 0; i < 4; i++){ + Point2 p = Geometry.d8edge[i]; + float poffset = -Math.max(request.block.size - 1, 0) / 2f * tilesize; + TextureRegion find = Core.atlas.find("block-select"); + if(i % 2 == 0) + Draw.rect("block-select", request.tile().x * tilesize + request.block.offset() + poffset * p.x, request.tile().y * tilesize + request.block.offset() + poffset * p.y, + find.getWidth() * Draw.scl * request.scale, find.getHeight() * Draw.scl * request.scale, i * 90); + } + Draw.color(); + }else{ + float rad = Math.max((tile.block().size * tilesize / 2f - 1) * request.scale, 1f); + + if(rad <= 1.01f) return; + Draw.mixcol(); + //draw removing request + Draw.tint(Pal.removeBack); + Lines.square(tile.drawx(), tile.drawy() - 1, rad); + Draw.tint(Pal.remove); + Lines.square(tile.drawx(), tile.drawy(), rad); + } + } + + /** Draws a placement icon for a specific block. */ + void drawPlace(int x, int y, Block block, int rotation, int prevX, int prevY, int prevRotation){ + if(validPlace(x, y, block, rotation)){ + block.getPlaceDraw(placeDraw, rotation, prevX, prevY, prevRotation); + + Draw.color(); + Draw.rect(placeDraw.region, x * tilesize + block.offset(), y * tilesize + block.offset(), + placeDraw.region.getWidth() * Draw.scl * placeDraw.scalex, + placeDraw.region.getHeight() * Draw.scl * placeDraw.scaley, + block.rotate ? placeDraw.rotation * 90 : 0); + + Draw.color(Pal.accent); + for(int i = 0; i < 4; i++){ + Point2 p = Geometry.d8edge[i]; + float offset = -Math.max(block.size - 1, 0) / 2f * tilesize; + if(i % 2 == 0) + Draw.rect("block-select", x * tilesize + block.offset() + offset * p.x, y * tilesize + block.offset() + offset * p.y, i * 90); + } + Draw.color(); + }else{ + Draw.color(Pal.removeBack); + Lines.square(x * tilesize + block.offset(), y * tilesize + block.offset() - 1, block.size * tilesize / 2f - 1); + Draw.color(Pal.remove); + Lines.square(x * tilesize + block.offset(), y * tilesize + block.offset(), block.size * tilesize / 2f - 1); + } + } + + @Override + public void buildUI(Table table){ + table.addImage("blank").color(Pal.accent).height(3f).colspan(4).growX(); + table.row(); + table.left().margin(0f).defaults().size(48f); + + table.addImageButton("icon-break", "clear-toggle-partial", iconsize, () -> { + mode = mode == breaking ? block == null ? none : placing : breaking; + lastBlock = block; + if(mode == breaking){ + showGuide("removearea", this::isAreaBreaking); + } + }).update(l -> l.setChecked(mode == breaking)); + + //diagonal swap button + table.addImageButton("icon-diagonal", "clear-toggle-partial", iconsize, () -> { + Core.settings.put("swapdiagonal", !Core.settings.getBool("swapdiagonal")); + Core.settings.save(); + }).update(l -> l.setChecked(Core.settings.getBool("swapdiagonal"))); + + //rotate button + table.addImageButton("icon-arrow", "clear-partial", iconsize, () -> rotation = Mathf.mod(rotation + 1, 4)) + .update(i -> i.getImage().setRotationOrigin(rotation * 90, Align.center)).visible(() -> block != null && block.rotate); + + //confirm button + table.addImageButton("icon-check", "clear-partial", iconsize, () -> { + for(PlaceRequest request : selection){ + Tile tile = request.tile(); + + //actually place/break all selected blocks + if(tile != null){ + if(!request.remove){ + rotation = request.rotation; + Block before = block; + block = request.block; + tryPlaceBlock(tile.x, tile.y); + block = before; + }else{ + tryBreakBlock(tile.x, tile.y); + } + } + } + + //move all current requests to removal array so they fade out + removals.addAll(selection); + selection.clear(); + selecting = false; + }).visible(() -> !selection.isEmpty()); + + Core.scene.table(t -> { + t.bottom().left().visible(() -> (player.isBuilding() || block != null || mode == breaking) && !state.is(State.menu)); + t.addImageTextButton("$cancel", "icon-cancel", 16*2, () -> { + player.clearBuilding(); + mode = none; + block = null; + }).width(155f); + }); + } + + @Override + public boolean isDrawing(){ + return selection.size > 0 || removals.size > 0 || lineMode || player.target != null || mode != PlaceMode.none; + } + + @Override + public boolean isPlacing(){ + return super.isPlacing() && mode == placing; + } + + @Override + public void drawOutlined(){ + Lines.stroke(1f); + + //draw removals + for(PlaceRequest request : removals){ + Tile tile = request.tile(); + + if(tile == null) continue; + + request.scale = Mathf.lerpDelta(request.scale, 0f, 0.2f); + request.redness = Mathf.lerpDelta(request.redness, 0f, 0.2f); + + drawRequest(request, null); + } + + PlaceRequest last = null; + + //draw list of requests + for(PlaceRequest request : selection){ + Tile tile = request.tile(); + + if(tile == null) continue; + + if((!request.remove && validPlace(tile.x, tile.y, request.block, request.rotation)) + || (request.remove && validBreak(tile.x, tile.y))){ + request.scale = Mathf.lerpDelta(request.scale, 1f, 0.2f); + request.redness = Mathf.lerpDelta(request.redness, 0f, 0.2f); + }else{ + request.scale = Mathf.lerpDelta(request.scale, 0.6f, 0.1f); + request.redness = Mathf.lerpDelta(request.redness, 0.9f, 0.2f); + } + + Tmp.c1.set(Draw.getMixColor()); + + if(!request.remove && request == lastPlaced && request.block != null){ + Draw.mixcol(); + if(request.block.rotate) drawArrow(request.block, tile.x, tile.y, request.rotation); + } + + Draw.mixcol(Tmp.c1, 1f); + drawRequest(request, last); + + //draw last placed request + if(!request.remove && request == lastPlaced && request.block != null){ + Draw.mixcol(); + request.block.drawPlace(tile.x, tile.y, rotation, validPlace(tile.x, tile.y, request.block, rotation)); + } + + last = request; + } + + Draw.mixcol(); + Draw.color(Pal.accent); + + //Draw lines + if(lineMode){ + int tileX = tileX(Core.input.mouseX()); + int tileY = tileY(Core.input.mouseY()); + + if(mode == placing && block != null){ + //draw placing + + prevX = lineStartX; + prevY = lineStartY; + prevRotation = rotation; + + iterateLine(lineStartX, lineStartY, tileX, tileY, l -> { + if(l.last && block.rotate){ + drawArrow(block, l.x, l.y, l.rotation); + } + drawPlace(l.x, l.y, block, l.rotation, prevX - l.x, prevY - l.y, prevRotation); + + rotation = l.rotation; + prevX = l.x; + prevY = l.y; + prevRotation = l.rotation; + }); + }else if(mode == breaking){ + //draw breaking + NormalizeDrawResult result = PlaceUtils.normalizeDrawArea(Blocks.air, lineStartX, lineStartY, tileX, tileY, false, maxLength, 1f); + NormalizeResult dresult = PlaceUtils.normalizeArea(lineStartX, lineStartY, tileX, tileY, rotation, false, maxLength); + + for(int x = dresult.x; x <= dresult.x2; x++){ + for(int y = dresult.y; y <= dresult.y2; y++){ + Tile other = world.ltile(x, y); + if(other == null || !validBreak(other.x, other.y)) continue; + + Draw.color(Pal.removeBack); + Lines.square(other.drawx(), other.drawy() - 1, other.block().size * tilesize / 2f - 1); + Draw.color(Pal.remove); + Lines.square(other.drawx(), other.drawy(), other.block().size * tilesize / 2f - 1); + } + } + + Draw.color(Pal.removeBack); + Lines.rect(result.x, result.y - 1, result.x2 - result.x, result.y2 - result.y); + Draw.color(Pal.remove); + Lines.rect(result.x, result.y, result.x2 - result.x, result.y2 - result.y); + + } + + } + + TargetTrait target = player.target; + + //draw targeting crosshair + if(target != null && !state.isEditor()){ + if(target != lastTarget){ + crosshairScale = 0f; + lastTarget = target; + } + + crosshairScale = Mathf.lerpDelta(crosshairScale, 1f, 0.2f); + + Draw.color(Pal.remove); + Lines.stroke(1f); + + float radius = Interpolation.swingIn.apply(crosshairScale); + + Lines.poly(target.getX(), target.getY(), 4, 7f * radius, Time.time() * 1.5f); + Lines.spikes(target.getX(), target.getY(), 3f * radius, 6f * radius, 4, Time.time() * 1.5f); + } + + Draw.reset(); + } + + //endregion + //region input events + + @Override + public boolean touchDown(int screenX, int screenY, int pointer, KeyCode button){ + if(state.is(State.menu) || player.isDead()) return false; + + //get tile on cursor + Tile cursor = tileAt(screenX, screenY); + + float worldx = Core.input.mouseWorld(screenX, screenY).x, worldy = Core.input.mouseWorld(screenX, screenY).y; + + //ignore off-screen taps + if(cursor == null || Core.scene.hasMouse(screenX, screenY)) return false; + + //only begin selecting if the tapped block is a request + selecting = hasRequest(cursor) && isPlacing() && mode == placing; + + //call tap events + if(pointer == 0 && !selecting && mode == none){ + tryTapPlayer(worldx, worldy); + } + + return false; + } + + @Override + public boolean touchUp(int screenX, int screenY, int pointer, KeyCode button){ + + //place down a line if in line mode + if(lineMode){ + int tileX = tileX(screenX); + int tileY = tileY(screenY); + + if(mode == placing && isPlacing()){ + iterateLine(lineStartX, lineStartY, tileX, tileY, l -> { + Tile tile = world.tile(l.x, l.y); + if(tile != null && hasRequest(tile)){ + return; + } + + PlaceRequest request = new PlaceRequest(l.x, l.y, block, l.rotation); + request.scale = 1f; + selection.add(request); + }); + }else if(mode == breaking){ + //normalize area + NormalizeResult result = PlaceUtils.normalizeArea(lineStartX, lineStartY, tileX, tileY, rotation, false, maxLength); + + //break everything in area + for(int x = 0; x <= Math.abs(result.x2 - result.x); x++){ + for(int y = 0; y <= Math.abs(result.y2 - result.y); y++){ + int wx = lineStartX + x * Mathf.sign(tileX - lineStartX); + int wy = lineStartY + y * Mathf.sign(tileY - lineStartY); + + Tile tar = world.ltile(wx, wy); + + if(tar == null) continue; + + if(!hasRequest(world.tile(tar.x, tar.y)) && validBreak(tar.x, tar.y)){ + PlaceRequest request = new PlaceRequest(tar.x, tar.y); + request.scale = 1f; + selection.add(request); + } + } + } + } + + lineMode = false; + }else{ + Tile tile = tileAt(screenX, screenY); + + if(tile == null) return false; + + tryDropItems(tile.link(), Core.input.mouseWorld(screenX, screenY).x, Core.input.mouseWorld(screenX, screenY).y); + } + return false; + } + + @Override + public boolean longPress(float x, float y){ + if(state.is(State.menu) || mode == none || player.isDead()) return false; + + //get tile on cursor + Tile cursor = tileAt(x, y); + + //ignore off-screen taps + if(cursor == null || Core.scene.hasMouse(x, y)) return false; + + //remove request if it's there + //long pressing enables line mode otherwise + lineStartX = cursor.x; + lineStartY = cursor.y; + lineMode = true; + + if(mode == breaking){ + Effects.effect(Fx.tapBlock, cursor.worldx(), cursor.worldy(), 1f); + }else if(block != null){ + Effects.effect(Fx.tapBlock, cursor.worldx() + block.offset(), cursor.worldy() + block.offset(), block.size); + } + + return false; + } + + @Override + public boolean tap(float x, float y, int count, KeyCode button){ + if(state.is(State.menu) || lineMode) return false; + + float worldx = Core.input.mouseWorld(x, y).x, worldy = Core.input.mouseWorld(x, y).y; + + //get tile on cursor + Tile cursor = tileAt(x, y); + + //ignore off-screen taps + if(cursor == null || Core.scene.hasMouse(x, y)) return false; + + checkTargets(worldx, worldy); + + //remove if request present + if(hasRequest(cursor)){ + removeRequest(getRequest(cursor)); + }else if(mode == placing && isPlacing() && validPlace(cursor.x, cursor.y, block, rotation) && !checkOverlapPlacement(cursor.x, cursor.y, block)){ + //add to selection queue if it's a valid place position + selection.add(lastPlaced = new PlaceRequest(cursor.x, cursor.y, block, rotation)); + }else if(mode == breaking && validBreak(cursor.link().x, cursor.link().y) && !hasRequest(cursor.link())){ + //add to selection queue if it's a valid BREAK position + cursor = cursor.link(); + selection.add(new PlaceRequest(cursor.x, cursor.y)); + }else if(!canTapPlayer(worldx, worldy) && !tileTapped(cursor.link())){ + tryBeginMine(cursor); + } + + return false; + } + + @Override + public void update(){ + if(state.is(State.menu) || player.isDead()){ + selection.clear(); + removals.clear(); + mode = none; + } + + //reset state when not placing + if(mode == none){ + selecting = false; + lineMode = false; + removals.addAll(selection); + selection.clear(); + } + + if(lineMode && mode == placing && block == null){ + lineMode = false; + } + + //if there is no mode and there's a recipe, switch to placing + if(block != null && mode == none){ + mode = placing; + } + + if(block != null){ + showGuide("placeline", this::isLinePlacing); + } + + if(block == null && mode == placing){ + mode = none; + } + + //automatically switch to placing after a new recipe is selected + if(lastBlock != block && mode == breaking && block != null){ + mode = placing; + lastBlock = block; + } + + if(lineMode){ + lineScale = Mathf.lerpDelta(lineScale, 1f, 0.1f); + + //When in line mode, pan when near screen edges automatically + if(Core.input.isTouched(0) && lineMode){ + float screenX = Core.input.mouseX(), screenY = Core.input.mouseY(); + + float panX = 0, panY = 0; + + if(screenX <= edgePan){ + panX = -(edgePan - screenX); + } + + if(screenX >= Core.graphics.getWidth() - edgePan){ + panX = (screenX - Core.graphics.getWidth()) + edgePan; + } + + if(screenY <= edgePan){ + panY = -(edgePan - screenY); + } + + if(screenY >= Core.graphics.getHeight() - edgePan){ + panY = (screenY - Core.graphics.getHeight()) + edgePan; + } + + vector.set(panX, panY).scl((Core.camera.width) / Core.graphics.getWidth()); + vector.limit(maxPanSpeed); + + //pan view + Core.camera.position.x += vector.x; + Core.camera.position.y += vector.y; + } + }else{ + lineScale = 0f; + } + + //remove place requests that have disappeared + for(int i = removals.size - 1; i >= 0; i--){ + PlaceRequest request = removals.get(i); + + if(request.scale <= 0.0001f){ + removals.remove(i); + i--; + } + } + } + + @Override + public boolean pan(float x, float y, float deltaX, float deltaY){ + if(Core.scene.hasDialog()) return false; + + float scale = Core.camera.width / Core.graphics.getWidth(); + deltaX *= scale; + deltaY *= scale; + + //can't pan in line mode with one finger or while dropping items! + if((lineMode && !Core.input.isTouched(1)) || droppingItem){ + return false; + } + + if(selecting){ //pan all requests + shiftDeltaX += deltaX; + shiftDeltaY += deltaY; + + int shiftedX = (int)(shiftDeltaX / tilesize); + int shiftedY = (int)(shiftDeltaY / tilesize); + + if(Math.abs(shiftedX) > 0 || Math.abs(shiftedY) > 0){ + for(PlaceRequest req : selection){ + if(req.remove) continue; //don't shift removal requests + req.x += shiftedX; + req.y += shiftedY; + } + + shiftDeltaX %= tilesize; + shiftDeltaY %= tilesize; + } + }else{ + //pan player + Core.camera.position.x -= deltaX; + Core.camera.position.y -= deltaY; + } + + return false; + } + + @Override + public boolean panStop(float x, float y, int pointer, KeyCode button){ + shiftDeltaX = shiftDeltaY = 0f; + return false; + } + + @Override + public boolean zoom(float initialDistance, float distance){ + if(lastDistance == -1) lastDistance = initialDistance; + + float amount = (Mathf.sign(distance > lastDistance) * 0.04f) * Time.delta(); + renderer.scaleCamera(io.anuke.arc.scene.ui.layout.Unit.dp.scl(amount)); + lastDistance = distance; + return true; + } + + float clerp(float value, float min, float max){ + final float alpha = 0.07f; + return value < min ? Mathf.lerpDelta(value, min, alpha) : value > max ? Mathf.lerpDelta(value, max, alpha) : value; + } + + //endregion + + private class PlaceRequest{ + int x, y; + Block block; + int rotation; + boolean remove; + + //animation variables + float scale; + float redness; + + PlaceRequest(int x, int y, Block block, int rotation){ + this.x = x; + this.y = y; + this.block = block; + this.rotation = rotation; + this.remove = false; + } + + PlaceRequest(int x, int y){ + this.x = x; + this.y = y; + this.remove = true; + } + + Tile tile(){ + return world.tile(x, y); + } + } +} diff --git a/core/src/io/anuke/mindustry/input/PlaceMode.java b/core/src/io/anuke/mindustry/input/PlaceMode.java index 6a6b633332..8eb6bb5105 100644 --- a/core/src/io/anuke/mindustry/input/PlaceMode.java +++ b/core/src/io/anuke/mindustry/input/PlaceMode.java @@ -1,417 +1,5 @@ package io.anuke.mindustry.input; -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.graphics.Colors; -import com.badlogic.gdx.math.MathUtils; -import com.badlogic.gdx.math.Vector2; -import io.anuke.mindustry.ui.fragments.ToolFragment; -import io.anuke.mindustry.world.Block; -import io.anuke.mindustry.world.Tile; -import io.anuke.ucore.core.Timers; -import io.anuke.ucore.graphics.Draw; -import io.anuke.ucore.graphics.Lines; -import io.anuke.ucore.util.Bundles; -import io.anuke.ucore.util.Mathf; -import io.anuke.ucore.util.Translator; - -import static io.anuke.mindustry.Vars.*; - -public enum PlaceMode{ - cursor{ - { - shown = true; - lockCamera = true; - pan = true; - } - - public void draw(int tilex, int tiley, int endx, int endy){ - float x = tilex * tilesize; - float y = tiley * tilesize; - - boolean valid = control.input().validPlace(tilex, tiley, control.input().recipe.result) && (mobile || control.input().cursorNear()); - - Vector2 offset = control.input().recipe.result.getPlaceOffset(); - - float si = MathUtils.sin(Timers.time() / 6f) + 1.5f; - - renderer.getBlocks().handlePreview(control.input().recipe.result, control.input().recipe.result.rotate ? control.input().rotation * 90 : 0f, x + offset.x, y + offset.y, tilex, tiley); - - Draw.color(valid ? Colors.get("place") : Colors.get("placeInvalid")); - Lines.stroke(2f); - Lines.crect(x + offset.x, y + offset.y, tilesize * control.input().recipe.result.width + si, - tilesize * control.input().recipe.result.height + si); - - control.input().recipe.result.drawPlace(tilex, tiley, control.input().rotation, valid); - - if(control.input().recipe.result.rotate){ - - Draw.color(Colors.get("placeRotate")); - tr.trns(control.input().rotation * 90, 7, 0); - Lines.line(x, y, x + tr.x, y + tr.y); - } - } - - public void tapped(int tilex, int tiley){ - control.input().tryPlaceBlock(tilex, tiley, true); - } - }, - touch{ - { - shown = true; - lockCamera = false; - showRotate = true; - showCancel = true; - } - - public void tapped(int x, int y){ - control.input().tryPlaceBlock(x, y, true); - } - }, - none{ - { - delete = true; - shown = true; - both = true; - } - }, - holdDelete{ - { - delete = true; - shown = true; - both = true; - } - - public void draw(int tilex, int tiley, int endx, int endy){ - Tile tile = world.tile(tilex, tiley); - - if(tile != null && control.input().validBreak(tilex, tiley)){ - if(tile.isLinked()) - tile = tile.getLinked(); - float fin = control.input().breaktime / tile.getBreakTime(); - - if(mobile && control.input().breaktime > 0){ - Draw.color(Colors.get("breakStart"), Colors.get("break"), fin); - Lines.poly(tile.drawx(), tile.drawy(), 25, 4 + (1f - fin) * 26); - } - Draw.reset(); - } - } - }, - touchDelete{ - { - shown = true; - lockCamera = false; - showRotate = true; - showCancel = true; - delete = true; - } - - public void tapped(int x, int y){ - control.input().tryDeleteBlock(x, y, true); - } - }, - areaDelete{ - int maxlen = 20; - int tilex; - int tiley; - int endx; - int endy; - - { - shown = true; - lockCamera = true; - delete = true; - } - - public void draw(int tilex, int tiley, int endx, int endy){ - float t = tilesize; - - process(tilex, tiley, endx, endy); - - tilex = this.tilex; tiley = this.tiley; - endx = this.endx; endy = this.endy; - float x = this.tilex * t, y = this.tiley * t, - x2 = this.endx * t, y2 = this.endy * t; - - if(x2 >= x){ - x -= t/2; - x2 += t/2; - } - - if(y2 >= y){ - y -= t/2; - y2 += t/2; - } - - Draw.color(Colors.get("break")); - Lines.stroke(1f); - for(int cx = tilex; cx <= endx; cx ++){ - for(int cy = tiley; cy <= endy; cy ++){ - Tile tile = world.tile(cx, cy); - if(tile != null && tile.getLinked() != null) - tile = tile.getLinked(); - if(tile != null && control.input().validBreak(tile.x, tile.y)){ - Lines.crect(tile.drawx(), tile.drawy(), - tile.block().width * t, tile.block().height * t); - } - } - } - - Lines.stroke(2f); - Draw.color(control.input().cursorNear() ? Colors.get("break") : Colors.get("breakInvalid")); - Lines.rect(x, y, x2 - x, y2 - y); - Draw.alpha(0.3f); - Draw.crect("blank", x, y, x2 - x, y2 - y); - Draw.reset(); - } - - public void released(int tilex, int tiley, int endx, int endy){ - process(tilex, tiley, endx, endy); - tilex = this.tilex; tiley = this.tiley; - endx = this.endx; endy = this.endy; - - if(mobile){ - ToolFragment t = ui.toolfrag; - if(!t.confirming || t.px != tilex || t.py != tiley || t.px2 != endx || t.py2 != endy) { - t.confirming = true; - t.px = tilex; - t.py = tiley; - t.px2 = endx; - t.py2 = endy; - return; - } - } - - boolean first = true; - - for(int cx = tilex; cx <= endx; cx ++){ - for(int cy = tiley; cy <= endy; cy ++){ - if(control.input().tryDeleteBlock(cx, cy, first)){ - first = false; - } - } - } - } - - void process(int tilex, int tiley, int endx, int endy){ - - if(Math.abs(endx - tilex) > maxlen){ - endx = Mathf.sign(endx - tilex) * maxlen + tilex; - } - - if(Math.abs(endy - tiley) > maxlen){ - endy = Mathf.sign(endy - tiley) * maxlen + tiley; - } - - if(endx < tilex){ - int t = endx; - endx = tilex; - tilex = t; - } - if(endy < tiley){ - int t = endy; - endy = tiley; - tiley = t; - } - - this.endx = endx; - this.endy = endy; - this.tilex = tilex; - this.tiley = tiley; - } - }, - hold{ - int maxlen = 20; - int tilex; - int tiley; - int endx; - int endy; - int rotation; - - { - lockCamera = true; - shown = true; - showCancel = true; - showRotate = true; - } - - public void draw(int tilex, int tiley, int endx, int endy){ - if(mobile && !Gdx.input.isTouched(0) && !control.showCursor()){ - return; - } - - float t = tilesize; - Block block = control.input().recipe.result; - Vector2 offset = block.getPlaceOffset(); - - process(tilex, tiley, endx, endy); - int tx = tilex, ty = tiley, ex = endx, ey = endy; - tilex = this.tilex; tiley = this.tiley; - endx = this.endx; endy = this.endy; - float x = this.tilex * t, y = this.tiley * t, - x2 = this.endx * t, y2 = this.endy * t; - - if(x2 >= x){ - x -= block.width * t/2; - x2 += block.width * t/2; - } - - if(y2 >= y){ - y -= block.height * t/2; - y2 += block.height * t/2; - } - - x += offset.x; - y += offset.y; - x2 += offset.x; - y2 += offset.y; - - if(tilex == endx && tiley == endy){ - cursor.draw(tilex, tiley, endx, endy); - }else{ - Lines.stroke(2f); - Draw.color(control.input().cursorNear() ? "place" : "placeInvalid"); - Lines.rect(x, y, x2 - x, y2 - y); - Draw.alpha(0.3f); - Draw.crect("blank", x, y, x2 - x, y2 - y); - - int amount = 1; - boolean isX = Math.abs(endx - tilex) >= Math.abs(endy - tiley); - - for(int cx = 0; cx <= Math.abs(endx - tilex); cx += (isX ? 0 : 1)){ - for(int cy = 0; cy <= Math.abs(endy - tiley); cy += (isX ? 1 : 0)){ - - int px = tx + cx * Mathf.sign(ex - tx), - py = ty + cy * Mathf.sign(ey - ty); - - //step by the block size if it's valid - if(control.input().validPlace(px, py, control.input().recipe.result) && state.inventory.hasItems(control.input().recipe.requirements, amount)){ - - renderer.getBlocks().handlePreview(control.input().recipe.result, block.rotate ? rotation * 90 : 0f, px * t + offset.x, py * t + offset.y, px, py); - - if(isX){ - cx += block.width; - }else{ - cy += block.width; - } - amount ++; - }else{ //otherwise, step by 1 until it is valid - if(control.input().cursorNear()){ - Lines.stroke(2f); - Draw.color("placeInvalid"); - Lines.crect( - px * t + (isX ? 0 : offset.x) + (ex < tx && isX && block.width > 1 ? t : 0) - (block.width == 3 && ex > tx && isX ? t : 0), - py * t + (isX ? offset.y : 0) + (ey < ty && !isX && block.height > 1 ? t : 0) - (block.height == 3 && ey > ty && !isX ? t : 0), - t*(isX ? 1 : block.width), - t*(isX ? block.height : 1)); - Draw.color("place"); - } - - if(isX){ - cx += 1; - }else{ - cy += 1; - } - } - } - } - - if(control.input().recipe.result.rotate){ - float cx = tx * t, cy = ty * t; - Lines.stroke(2f); - Draw.color(Colors.get("placeRotate")); - tr.trns(rotation * 90, 7, 0); - Lines.line(cx, cy, cx + tr.x, cy + tr.y); - } - Draw.reset(); - } - } - - public void released(int tilex, int tiley, int endx, int endy){ - process(tilex, tiley, endx, endy); - - control.input().rotation = this.rotation; - - boolean first = true; - for(int x = 0; x <= Math.abs(this.endx - this.tilex); x ++){ - for(int y = 0; y <= Math.abs(this.endy - this.tiley); y ++){ - if(control.input().tryPlaceBlock( - tilex + x * Mathf.sign(endx - tilex), - tiley + y * Mathf.sign(endy - tiley), first)){ - first = false; - } - - } - } - } - - void process(int tilex, int tiley, int endx, int endy){ - if(Math.abs(tilex - endx) > Math.abs(tiley - endy)){ - endy = tiley; - }else{ - endx = tilex; - } - - if(Math.abs(endx - tilex) > maxlen){ - endx = Mathf.sign(endx - tilex) * maxlen + tilex; - } - - if(Math.abs(endy - tiley) > maxlen){ - endy = Mathf.sign(endy - tiley) * maxlen + tiley; - } - - if(endx > tilex) - rotation = 0; - else if(endx < tilex) - rotation = 2; - else if(endy > tiley) - rotation = 1; - else if(endy < tiley) - rotation = 3; - else - rotation = control.input().rotation; - - if(endx < tilex){ - int t = endx; - endx = tilex; - tilex = t; - } - if(endy < tiley){ - int t = endy; - endy = tiley; - tiley = t; - } - - this.endx = endx; - this.endy = endy; - this.tilex = tilex; - this.tiley = tiley; - } - }; - public boolean lockCamera; - public boolean pan = false; - public boolean shown = false; - public boolean showRotate; - public boolean showCancel; - public boolean delete = false; - public boolean both = false; - - private static final Translator tr = new Translator(); - - public void draw(int tilex, int tiley, int endx, int endy){ - - } - - public void released(int tilex, int tiley, int endx, int endy){ - - } - - public void tapped(int x, int y){ - - } - - @Override - public String toString(){ - return Bundles.get("placemode."+name().toLowerCase()+".name"); - } -} \ No newline at end of file +enum PlaceMode{ + none, breaking, placing +} diff --git a/core/src/io/anuke/mindustry/input/PlaceUtils.java b/core/src/io/anuke/mindustry/input/PlaceUtils.java new file mode 100644 index 0000000000..52e06e518b --- /dev/null +++ b/core/src/io/anuke/mindustry/input/PlaceUtils.java @@ -0,0 +1,176 @@ +package io.anuke.mindustry.input; + +import io.anuke.arc.collection.Array; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.Bresenham2; +import io.anuke.arc.math.geom.Point2; +import io.anuke.arc.util.pooling.Pools; +import io.anuke.mindustry.world.Block; + +import static io.anuke.mindustry.Vars.tilesize; + +public class PlaceUtils{ + private static final NormalizeResult result = new NormalizeResult(); + private static final NormalizeDrawResult drawResult = new NormalizeDrawResult(); + private static Bresenham2 bres = new Bresenham2(); + private static Array points = new Array<>(); + + /** Normalize a diagonal line into points. */ + public static Array normalizeDiagonal(int startX, int startY, int endX, int endY){ + Pools.freeAll(points); + points.clear(); + return bres.lineNoDiagonal(startX, startY, endX, endY, Pools.get(Point2.class, Point2::new), points); + } + + /** Normalize two points into one straight line, no diagonals. */ + public static Array normalizeLine(int startX, int startY, int endX, int endY){ + Pools.freeAll(points); + points.clear(); + if(Math.abs(startX - endX) > Math.abs(startY - endY)){ + //go width + for(int i = 0; i <= Math.abs(startX - endX); i++){ + points.add(Pools.obtain(Point2.class, Point2::new).set(startX + i * Mathf.sign(endX - startX), startY)); + } + }else{ + //go height + for(int i = 0; i <= Math.abs(startY - endY); i++){ + points.add(Pools.obtain(Point2.class, Point2::new).set(startX, startY + i * Mathf.sign(endY - startY))); + } + } + return points; + } + + /** + * Normalizes a placement area and returns the result, ready to be used for drawing a rectangle. + * Returned x2 and y2 will always be greater than x and y. + * @param block block that will be drawn + * @param startx starting X coordinate + * @param starty starting Y coordinate + * @param endx ending X coordinate + * @param endy ending Y coordinate + * @param snap whether to snap to a line + * @param maxLength maximum length of area + */ + public static NormalizeDrawResult normalizeDrawArea(Block block, int startx, int starty, int endx, int endy, boolean snap, int maxLength, float scaling){ + normalizeArea(startx, starty, endx, endy, 0, snap, maxLength); + + float offset = block.offset(); + + drawResult.x = result.x * tilesize; + drawResult.y = result.y * tilesize; + drawResult.x2 = result.x2 * tilesize; + drawResult.y2 = result.y2 * tilesize; + + drawResult.x -= block.size * scaling * tilesize / 2; + drawResult.x2 += block.size * scaling * tilesize / 2; + + + drawResult.y -= block.size * scaling * tilesize / 2; + drawResult.y2 += block.size * scaling * tilesize / 2; + + drawResult.x += offset; + drawResult.y += offset; + drawResult.x2 += offset; + drawResult.y2 += offset; + + return drawResult; + } + + /** + * Normalizes a placement area and returns the result. + * Returned x2 and y2 will always be greater than x and y. + * @param tilex starting X coordinate + * @param tiley starting Y coordinate + * @param endx ending X coordinate + * @param endy ending Y coordinate + * @param snap whether to snap to a line + * @param rotation placement rotation + * @param maxLength maximum length of area + */ + public static NormalizeResult normalizeArea(int tilex, int tiley, int endx, int endy, int rotation, boolean snap, int maxLength){ + + if(snap){ + if(Math.abs(tilex - endx) > Math.abs(tiley - endy)){ + endy = tiley; + }else{ + endx = tilex; + } + + if(Math.abs(endx - tilex) > maxLength){ + endx = Mathf.sign(endx - tilex) * maxLength + tilex; + } + + if(Math.abs(endy - tiley) > maxLength){ + endy = Mathf.sign(endy - tiley) * maxLength + tiley; + } + } + + int dx = endx - tilex, dy = endy - tiley; + + if(Math.abs(dx) > Math.abs(dy)){ + if(dx >= 0){ + rotation = 0; + }else{ + rotation = 2; + } + }else if(Math.abs(dx) < Math.abs(dy)){ + if(dy >= 0){ + rotation = 1; + }else{ + rotation = 3; + } + } + + if(endx < tilex){ + int t = endx; + endx = tilex; + tilex = t; + } + if(endy < tiley){ + int t = endy; + endy = tiley; + tiley = t; + } + + result.x2 = endx; + result.y2 = endy; + result.x = tilex; + result.y = tiley; + result.rotation = rotation; + + return result; + } + + static class NormalizeDrawResult{ + float x, y, x2, y2; + } + + static class NormalizeResult{ + int x, y, x2, y2, rotation; + + boolean isX(){ + return Math.abs(x2 - x) > Math.abs(y2 - y); + } + + /** + * Returns length of greater edge of the selection. + */ + int getLength(){ + return Math.max(x2 - x, y2 - y); + } + + /** + * Returns the X position of a specific index along this area as a line. + */ + int getScaledX(int i){ + return x + (x2 - x > y2 - y ? i : 0); + } + + /** + * Returns the Y position of a specific index along this area as a line. + */ + int getScaledY(int i){ + return y + (x2 - x > y2 - y ? 0 : i); + } + } +} diff --git a/core/src/io/anuke/mindustry/io/BlockLoader.java b/core/src/io/anuke/mindustry/io/BlockLoader.java deleted file mode 100644 index fead464606..0000000000 --- a/core/src/io/anuke/mindustry/io/BlockLoader.java +++ /dev/null @@ -1,144 +0,0 @@ -package io.anuke.mindustry.io; - -import com.badlogic.gdx.utils.IntMap; -import com.badlogic.gdx.utils.ObjectIntMap; -import io.anuke.mindustry.world.Block; -import io.anuke.mindustry.world.blocks.*; -import io.anuke.ucore.util.Log; - -public class BlockLoader { - static final ObjectIntMap defaultMap = map( - "air", 0, - "blockpart", 1, - "deepwater", 2, - "water", 3, - "lava", 4, - "oil", 5, - "stone", 6, - "blackstone", 7, - "iron", 8, - "coal", 9, - "titanium", 10, - "uranium", 11, - "dirt", 12, - "sand", 13, - "ice", 14, - "snow", 15, - "grass", 16, - "sandblock", 17, - "snowblock", 18, - "stoneblock", 19, - "blackstoneblock", 20, - "grassblock", 21, - "mossblock", 22, - "shrub", 23, - "rock", 24, - "icerock", 25, - "blackrock", 26, - "dirtblock", 27, - "stonewall", 28, - "ironwall", 29, - "steelwall", 30, - "titaniumwall", 31, - "duriumwall", 32, - "compositewall", 33, - "steelwall-large", 34, - "titaniumwall-large", 35, - "duriumwall-large", 36, - "titaniumshieldwall", 37, - "repairturret", 38, - "megarepairturret", 39, - "shieldgenerator", 40, - "door", 41, - "door-large", 42, - "conduit", 43, - "pulseconduit", 44, - "liquidrouter", 45, - "conveyor", 46, - "steelconveyor", 47, - "poweredconveyor", 48, - "router", 49, - "junction", 50, - "conveyortunnel", 51, - "liquidjunction", 52, - "liquiditemjunction", 53, - "powerbooster", 54, - "powerlaser", 55, - "powerlaserrouter", 56, - "powerlasercorner", 57, - "teleporter", 58, - "sorter", 59, - "core", 60, - "pump", 61, - "fluxpump", 62, - "smelter", 63, - "crucible", 64, - "coalpurifier", 65, - "titaniumpurifier", 66, - "oilrefinery", 67, - "stoneformer", 68, - "lavasmelter", 69, - "stonedrill", 70, - "irondrill", 71, - "coaldrill", 72, - "uraniumdrill", 73, - "titaniumdrill", 74, - "omnidrill", 75, - "coalgenerator", 76, - "thermalgenerator", 77, - "combustiongenerator", 78, - "rtgenerator", 79, - "nuclearreactor", 80, - "turret", 81, - "doubleturret", 82, - "machineturret", 83, - "shotgunturret", 84, - "flameturret", 85, - "sniperturret", 86, - "mortarturret", 87, - "laserturret", 88, - "waveturret", 89, - "plasmaturret", 90, - "chainturret", 91, - "titancannon", 92, - "playerspawn", 93, - "enemyspawn", 94 - ); - static final IntMap blockmap = new IntMap<>(); - - public static void load(){ - - Block[] blockClasses = { - Blocks.air, - DefenseBlocks.compositewall, - DistributionBlocks.conduit, - ProductionBlocks.coaldrill, - WeaponBlocks.chainturret, - SpecialBlocks.enemySpawn - //add any new block sections here - }; - - for(String string : defaultMap.keys()){ - Block block = Block.getByName(string); - blockmap.put(defaultMap.get(string, -1), block); - } - - for(Block block : Block.getAllBlocks()){ - block.init(); - } - - Log.info("Total blocks loaded: {0}", Block.getAllBlocks().size); - } - - public static Block getByOldID(int id){ - return blockmap.get(id); - } - - private static ObjectIntMap map(Object... objects){ - ObjectIntMap map = new ObjectIntMap<>(); - for(int i = 0; i < objects.length/2; i ++){ - map.put((String)objects[i*2], (int)objects[i*2+1]); - } - return map; - } -} diff --git a/core/src/io/anuke/mindustry/io/BundleLoader.java b/core/src/io/anuke/mindustry/io/BundleLoader.java index a66da1b7b5..74a123b23f 100644 --- a/core/src/io/anuke/mindustry/io/BundleLoader.java +++ b/core/src/io/anuke/mindustry/io/BundleLoader.java @@ -1,36 +1,34 @@ package io.anuke.mindustry.io; -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.files.FileHandle; -import com.badlogic.gdx.utils.I18NBundle; -import io.anuke.mindustry.core.Platform; -import io.anuke.ucore.core.Core; -import io.anuke.ucore.core.Settings; -import io.anuke.ucore.util.Log; +import io.anuke.arc.Core; +import io.anuke.arc.files.FileHandle; +import io.anuke.arc.util.*; +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.input.Binding; import java.util.Locale; import static io.anuke.mindustry.Vars.headless; -public class BundleLoader { - private static final boolean externalBundle = false; +public class BundleLoader{ public static void load(){ - Settings.defaults("locale", "default"); - Settings.load("io.anuke.moment"); + Core.settings.defaults("locale", "default"); + Core.keybinds.setDefaults(Binding.values()); + Core.settings.load(); loadBundle(); } private static Locale getLocale(){ - String loc = Settings.getString("locale"); + String loc = Core.settings.getString("locale"); if(loc.equals("default")){ return Locale.getDefault(); }else{ Locale lastLocale; - if (loc.contains("_")) { + if(loc.contains("_")){ String[] split = loc.split("_"); lastLocale = new Locale(split[0], split[1]); - } else { + }else{ lastLocale = new Locale(loc); } @@ -39,26 +37,26 @@ public class BundleLoader { } private static void loadBundle(){ - I18NBundle.setExceptionOnMissingKey(false); + if(headless) return; - if(externalBundle){ - try { - FileHandle handle = Gdx.files.local("bundle"); + try{ + //try loading external bundle + FileHandle handle = Core.files.local("bundle"); - Locale locale = Locale.ENGLISH; - Core.bundle = I18NBundle.createBundle(handle, locale); - }catch (Exception e){ - Log.err(e); - Platform.instance.showError("Failed to find bundle!\nMake sure you have bundle.properties in the same directory\nas the jar file.\n\nIf the problem persists, try running it through the command prompt:\n" + - "Hold left-shift, then right click and select 'open command prompt here'.\nThen, type in 'java -jar mindustry.jar' without quotes."); - Gdx.app.exit(); + Locale locale = Locale.ENGLISH; + Core.bundle = I18NBundle.createBundle(handle, locale); + + Log.info("NOTE: external translation bundle has been loaded."); + if(!headless){ + Time.run(10f, () -> Vars.ui.showInfo("Note: You have successfully loaded an external translation bundle.")); } - }else{ - FileHandle handle = Gdx.files.internal("bundles/bundle"); + }catch(Throwable e){ + //no external bundle found + + FileHandle handle = Core.files.internal("bundles/bundle"); Locale locale = getLocale(); Locale.setDefault(locale); - if(!headless) Log.info("Got locale: {0}", locale); Core.bundle = I18NBundle.createBundle(handle, locale); } } diff --git a/core/src/io/anuke/mindustry/io/Changelogs.java b/core/src/io/anuke/mindustry/io/Changelogs.java index c799647f1c..10605e7550 100644 --- a/core/src/io/anuke/mindustry/io/Changelogs.java +++ b/core/src/io/anuke/mindustry/io/Changelogs.java @@ -1,50 +1,54 @@ package io.anuke.mindustry.io; -import com.badlogic.gdx.utils.Array; -import com.badlogic.gdx.utils.Json; -import com.badlogic.gdx.utils.JsonValue; -import io.anuke.mindustry.net.Net; -import io.anuke.ucore.function.Consumer; +import io.anuke.arc.Core; +import io.anuke.arc.collection.Array; +import io.anuke.arc.function.Consumer; +import io.anuke.arc.util.serialization.JsonReader; +import io.anuke.arc.util.serialization.JsonValue; import static io.anuke.mindustry.Vars.releasesURL; -public class Changelogs { +public class Changelogs{ public static void getChangelog(Consumer> success, Consumer fail){ - Net.http(releasesURL, "GET", result -> { - Json j = new Json(); - Array list = j.fromJson(null, result); + Core.net.httpGet(releasesURL, result -> { + JsonReader reader = new JsonReader(); + JsonValue value = reader.parse(result.getResultAsString()).child; Array out = new Array<>(); - for(JsonValue value : list){ + + while(value != null){ String name = value.getString("name"); String description = value.getString("body").replace("\r", ""); int id = value.getInt("id"); int build = Integer.parseInt(value.getString("tag_name").substring(1)); - out.add(new VersionInfo(name, description, id, build)); + out.add(new VersionInfo(name, description, id, build, value.getString("published_at"))); + value = value.next; } + success.accept(out); }, fail); } public static class VersionInfo{ - public final String name, description; + public final String name, description, date; public final int id, build; - public VersionInfo(String name, String description, int id, int build) { + public VersionInfo(String name, String description, int id, int build, String date){ this.name = name; this.description = description; this.id = id; this.build = build; + this.date = date; } @Override - public String toString() { + public String toString(){ return "VersionInfo{" + - "name='" + name + '\'' + - ", description='" + description + '\'' + - ", id=" + id + - ", build=" + build + - '}'; + "name='" + name + '\'' + + ", description='" + description + '\'' + + ", id=" + id + + ", build=" + build + + '}'; } } } diff --git a/core/src/io/anuke/mindustry/io/JsonIO.java b/core/src/io/anuke/mindustry/io/JsonIO.java new file mode 100644 index 0000000000..6cee4ca311 --- /dev/null +++ b/core/src/io/anuke/mindustry/io/JsonIO.java @@ -0,0 +1,77 @@ +package io.anuke.mindustry.io; + +import io.anuke.arc.collection.EnumSet; +import io.anuke.arc.collection.LongQueue; +import io.anuke.arc.util.serialization.Json; +import io.anuke.arc.util.serialization.JsonValue; +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.game.*; +import io.anuke.mindustry.game.Teams.TeamData; +import io.anuke.mindustry.type.*; + +@SuppressWarnings("unchecked") +public class JsonIO{ + private static Json json = new Json(){{ + setIgnoreUnknownFields(true); + setElementType(Rules.class, "spawns", SpawnGroup.class); + + setSerializer(Zone.class, new Serializer(){ + @Override + public void write(Json json, Zone object, Class knownType){ + json.writeValue(object.name); + } + + @Override + public Zone read(Json json, JsonValue jsonData, Class type){ + return Vars.content.getByName(ContentType.zone, jsonData.asString()); + } + }); + + setSerializer(Item.class, new Serializer(){ + @Override + public void write(Json json, Item object, Class knownType){ + json.writeValue(object.name); + } + + @Override + public Item read(Json json, JsonValue jsonData, Class type){ + return Vars.content.getByName(ContentType.item, jsonData.asString()); + } + }); + + setSerializer(TeamData.class, new Serializer(){ + @Override + public void write(Json json, TeamData object, Class knownType){ + json.writeObjectStart(); + json.writeValue("brokenBlocks", object.brokenBlocks.toArray()); + json.writeValue("team", object.team.ordinal()); + json.writeObjectEnd(); + } + + @Override + public TeamData read(Json json, JsonValue jsonData, Class type){ + long[] blocks = jsonData.get("brokenBlocks").asLongArray(); + Team team = Team.all[jsonData.getInt("team", 0)]; + TeamData out = new TeamData(team, EnumSet.of(new Team[]{})); + out.brokenBlocks = new LongQueue(blocks); + return out; + } + }); + }}; + + public static String write(Object object){ + return json.toJson(object); + } + + public static T copy(T object){ + return read((Class)object.getClass(), write(object)); + } + + public static T read(Class type, String string){ + return json.fromJson(type, string); + } + + public static String print(String in){ + return json.prettyPrint(in); + } +} diff --git a/core/src/io/anuke/mindustry/io/LegacyMapIO.java b/core/src/io/anuke/mindustry/io/LegacyMapIO.java new file mode 100644 index 0000000000..2c65b1f2e7 --- /dev/null +++ b/core/src/io/anuke/mindustry/io/LegacyMapIO.java @@ -0,0 +1,232 @@ +package io.anuke.mindustry.io; + +import io.anuke.arc.collection.*; +import io.anuke.arc.files.FileHandle; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.graphics.Pixmap; +import io.anuke.arc.util.Pack; +import io.anuke.arc.util.Structs; +import io.anuke.arc.util.serialization.Json; +import io.anuke.mindustry.content.Blocks; +import io.anuke.mindustry.game.SpawnGroup; +import io.anuke.mindustry.game.Team; +import io.anuke.mindustry.io.MapIO.TileProvider; +import io.anuke.mindustry.maps.Map; +import io.anuke.mindustry.type.ContentType; +import io.anuke.mindustry.world.*; +import io.anuke.mindustry.world.LegacyColorMapper.LegacyBlock; +import io.anuke.mindustry.world.blocks.BlockPart; +import io.anuke.mindustry.world.blocks.Floor; + +import java.io.*; +import java.util.zip.InflaterInputStream; + +import static io.anuke.mindustry.Vars.*; + +/** Map IO for the "old" .mmap format. + * Differentiate between legacy maps and new maps by checking the extension (or the header).*/ +public class LegacyMapIO{ + private static final ObjectMap fallback = ObjectMap.of("alpha-dart-mech-pad", "dart-mech-pad"); + private static final Json json = new Json(); + + /* Convert a map from the old format to the new format. */ + public static void convertMap(FileHandle in, FileHandle out) throws IOException{ + Map map = readMap(in, true); + + String waves = map.tags.get("waves", "[]"); + Array groups = new Array<>(json.fromJson(SpawnGroup[].class, waves)); + + Tile[][] tiles = world.createTiles(map.width, map.height); + for(int x = 0; x < map.width; x++){ + for(int y = 0; y < map.height; y++){ + tiles[x][y] = new CachedTile(); + tiles[x][y].x = (short)x; + tiles[x][y].y = (short)y; + } + } + state.rules.spawns = groups; + readTiles(map, tiles); + MapIO.writeMap(out, map); + } + + public static Map readMap(FileHandle file, boolean custom) throws IOException{ + try(DataInputStream stream = new DataInputStream(file.read(1024))){ + StringMap tags = new StringMap(); + + //meta is uncompressed + int version = stream.readInt(); + if(version != 1){ + throw new IOException("Outdated legacy map format"); + } + int build = stream.readInt(); + short width = stream.readShort(), height = stream.readShort(); + byte tagAmount = stream.readByte(); + + for(int i = 0; i < tagAmount; i++){ + String name = stream.readUTF(); + String value = stream.readUTF(); + tags.put(name, value); + } + + return new Map(file, width, height, tags, custom, version, build); + } + } + + public static void readTiles(Map map, Tile[][] tiles) throws IOException{ + readTiles(map, (x, y) -> tiles[x][y]); + } + + public static void readTiles(Map map, TileProvider tiles) throws IOException{ + readTiles(map.file, map.width, map.height, tiles); + } + + private static void readTiles(FileHandle file, int width, int height, Tile[][] tiles) throws IOException{ + readTiles(file, width, height, (x, y) -> tiles[x][y]); + } + + private static void readTiles(FileHandle file, int width, int height, TileProvider tiles) throws IOException{ + try(BufferedInputStream input = file.read(bufferSize)){ + + //read map + { + DataInputStream stream = new DataInputStream(input); + + stream.readInt(); //version + stream.readInt(); //build + stream.readInt(); //width + height + byte tagAmount = stream.readByte(); + + for(int i = 0; i < tagAmount; i++){ + stream.readUTF(); //key + stream.readUTF(); //val + } + } + + try(DataInputStream stream = new DataInputStream(new InflaterInputStream(input))){ + + try{ + byte mapped = stream.readByte(); + IntMap idmap = new IntMap<>(); + IntMap namemap = new IntMap<>(); + + for(int i = 0; i < mapped; i++){ + byte type = stream.readByte(); + short total = stream.readShort(); + + for(int j = 0; j < total; j++){ + String name = stream.readUTF(); + if(type == 1){ + Block res = content.getByName(ContentType.block, fallback.get(name, name)); + idmap.put(j, res == null ? Blocks.air : res); + namemap.put(j, fallback.get(name, name)); + } + } + } + + //read floor and create tiles first + for(int i = 0; i < width * height; i++){ + int x = i % width, y = i / width; + int floorid = stream.readUnsignedByte(); + int oreid = stream.readUnsignedByte(); + int consecutives = stream.readUnsignedByte(); + + Tile tile = tiles.get(x, y); + tile.setFloor((Floor)idmap.get(floorid)); + tile.setOverlay(idmap.get(oreid)); + + for(int j = i + 1; j < i + 1 + consecutives; j++){ + int newx = j % width, newy = j / width; + Tile newTile = tiles.get(newx, newy); + newTile.setFloor((Floor)idmap.get(floorid)); + newTile.setOverlay(idmap.get(oreid)); + } + + i += consecutives; + } + + //read blocks + for(int i = 0; i < width * height; i++){ + int x = i % width, y = i / width; + int id = stream.readUnsignedByte(); + Block block = idmap.get(id); + if(block == null) block = Blocks.air; + + Tile tile = tiles.get(x, y); + //the spawn block is saved in the block tile layer in older maps, shift it to the overlay + if(block != Blocks.spawn){ + tile.setBlock(block); + }else{ + tile.setOverlay(block); + } + + if(namemap.get(id, "").equals("part")){ + stream.readByte(); //link + }else if(tile.entity != null){ + byte tr = stream.readByte(); + stream.readShort(); //read health (which is actually irrelevant) + + byte team = Pack.leftByte(tr); + byte rotation = Pack.rightByte(tr); + + tile.setTeam(Team.all[team]); + tile.entity.health = tile.block().health; + tile.rotation(rotation); + + if(tile.block() == Blocks.liquidSource || tile.block() == Blocks.unloader || tile.block() == Blocks.sorter){ + stream.readByte(); //these blocks have an extra config byte, read it + } + }else{ //no entity/part, read consecutives + int consecutives = stream.readUnsignedByte(); + + for(int j = i + 1; j < i + 1 + consecutives; j++){ + int newx = j % width, newy = j / width; + tiles.get(newx, newy).setBlock(block); + } + + i += consecutives; + } + } + + }finally{ + content.setTemporaryMapper(null); + } + } + } + } + + /** Reads a pixmap in the 3.5 pixmap format. */ + public static void readPixmap(Pixmap pixmap, Tile[][] tiles){ + for(int x = 0; x < pixmap.getWidth(); x++){ + for(int y = 0; y < pixmap.getHeight(); y++){ + int color = pixmap.getPixel(x, pixmap.getHeight() - 1 - y); + LegacyBlock block = LegacyColorMapper.get(color); + Tile tile = tiles[x][y]; + + tile.setFloor(block.floor); + tile.setBlock(block.wall); + if(block.ore != null) tile.setOverlay(block.ore); + + //place core + if(color == Color.rgba8888(Color.GREEN)){ + for(int dx = 0; dx < 3; dx++){ + for(int dy = 0; dy < 3; dy++){ + int worldx = dx - 1 + x; + int worldy = dy - 1 + y; + + //multiblock parts + if(Structs.inBounds(worldx, worldy, pixmap.getWidth(), pixmap.getHeight())){ + Tile write = tiles[worldx][worldy]; + write.setBlock(BlockPart.get(dx - 1, dy - 1)); + write.setTeam(Team.blue); + } + } + } + + //actual core parts + tile.setBlock(Blocks.coreShard); + tile.setTeam(Team.blue); + } + } + } + } +} diff --git a/core/src/io/anuke/mindustry/io/MapIO.java b/core/src/io/anuke/mindustry/io/MapIO.java new file mode 100644 index 0000000000..8daf1a7e2a --- /dev/null +++ b/core/src/io/anuke/mindustry/io/MapIO.java @@ -0,0 +1,163 @@ +package io.anuke.mindustry.io; + +import io.anuke.arc.collection.StringMap; +import io.anuke.arc.files.FileHandle; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.graphics.Pixmap; +import io.anuke.arc.graphics.Pixmap.Format; +import io.anuke.arc.util.io.CounterInputStream; +import io.anuke.mindustry.content.Blocks; +import io.anuke.mindustry.game.Team; +import io.anuke.mindustry.game.Version; +import io.anuke.mindustry.maps.Map; +import io.anuke.mindustry.world.*; +import io.anuke.mindustry.world.blocks.storage.CoreBlock; + +import java.io.*; +import java.util.zip.InflaterInputStream; + +import static io.anuke.mindustry.Vars.*; + +/** Reads and writes map files. */ +//TODO does this class even need to exist??? move to Maps? +public class MapIO{ + private static final int[] pngHeader = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A}; + + public static boolean isImage(FileHandle file){ + try(InputStream stream = file.read(32)){ + for(int i1 : pngHeader){ + if(stream.read() != i1){ + return false; + } + } + return true; + }catch(IOException e){ + return false; + } + } + + public static Map createMap(FileHandle file, boolean custom) throws IOException{ + try(InputStream is = new InflaterInputStream(file.read(bufferSize)); CounterInputStream counter = new CounterInputStream(is); DataInputStream stream = new DataInputStream(counter)){ + SaveIO.readHeader(stream); + int version = stream.readInt(); + SaveVersion ver = SaveIO.getSaveWriter(version); + StringMap tags = new StringMap(); + ver.region("meta", stream, counter, in -> tags.putAll(ver.readStringMap(in))); + return new Map(file, tags.getInt("width"), tags.getInt("height"), tags, custom, version, Version.build); + } + } + + public static void writeMap(FileHandle file, Map map) throws IOException{ + try{ + SaveIO.write(file, map.tags); + }catch(Exception e){ + throw new IOException(e); + } + } + + public static void loadMap(Map map){ + SaveIO.load(map.file); + } + + public static void loadMap(Map map, WorldContext cons){ + SaveIO.load(map.file, cons); + } + + public static Pixmap generatePreview(Map map) throws IOException{ + //by default, it does not have an enemy core or any other cores + map.tags.put("enemycore", "false"); + map.tags.put("othercore", "false"); + + try(InputStream is = new InflaterInputStream(map.file.read(bufferSize)); CounterInputStream counter = new CounterInputStream(is); DataInputStream stream = new DataInputStream(counter)){ + SaveIO.readHeader(stream); + int version = stream.readInt(); + SaveVersion ver = SaveIO.getSaveWriter(version); + ver.region("meta", stream, counter, ver::readStringMap); + + Pixmap floors = new Pixmap(map.width, map.height, Format.RGBA8888); + Pixmap walls = new Pixmap(map.width, map.height, Format.RGBA8888); + int black = Color.rgba8888(Color.BLACK); + int shade = Color.rgba8888(0f, 0f, 0f, 0.5f); + CachedTile tile = new CachedTile(){ + @Override + public void setBlock(Block type){ + super.setBlock(type); + int c = colorFor(Blocks.air, block(), Blocks.air, getTeam()); + if(c != black){ + walls.drawPixel(x, floors.getHeight() - 1 - y, c); + floors.drawPixel(x, floors.getHeight() - 1 - y + 1, shade); + } + } + + @Override + public void setTeam(Team team){ + super.setTeam(team); + if(block instanceof CoreBlock){ + if(team != defaultTeam){ + //map must have other team's cores + map.tags.put("othercore", "true"); + } + + if(team == waveTeam){ + //map must have default enemy team's core + map.tags.put("enemycore", "true"); + } + } + } + }; + + ver.region("content", stream, counter, ver::readContentHeader); + ver.region("preview_map", stream, counter, in -> ver.readMap(in, new WorldContext(){ + @Override public void resize(int width, int height){} + @Override public boolean isGenerating(){return false;} + @Override public void begin(){} + @Override public void end(){} + + @Override + public Tile tile(int x, int y){ + tile.x = (short)x; + tile.y = (short)y; + return tile; + } + + @Override + public Tile create(int x, int y, int floorID, int overlayID, int wallID){ + if(overlayID != 0){ + floors.drawPixel(x, floors.getHeight() - 1 - y, colorFor(Blocks.air, Blocks.air, content.block(overlayID), Team.none)); + }else{ + floors.drawPixel(x, floors.getHeight() - 1 - y, colorFor(content.block(floorID), Blocks.air, Blocks.air, Team.none)); + } + return tile; + } + })); + + floors.drawPixmap(walls, 0, 0); + walls.dispose(); + return floors; + }finally{ + content.setTemporaryMapper(null); + } + } + + public static Pixmap generatePreview(Tile[][] tiles){ + Pixmap pixmap = new Pixmap(tiles.length, tiles[0].length, Format.RGBA8888); + for(int x = 0; x < pixmap.getWidth(); x++){ + for(int y = 0; y < pixmap.getHeight(); y++){ + Tile tile = tiles[x][y]; + pixmap.drawPixel(x, pixmap.getHeight() - 1 - y, colorFor(tile.floor(), tile.block(), tile.overlay(), tile.getTeam())); + } + } + return pixmap; + } + + public static int colorFor(Block floor, Block wall, Block ore, Team team){ + if(wall.synthetic()){ + return team.intColor; + } + return Color.rgba8888(wall.solid ? wall.color : ore == Blocks.air ? floor.color : ore.color); + } + + interface TileProvider{ + Tile get(int x, int y); + } +} \ No newline at end of file diff --git a/core/src/io/anuke/mindustry/io/Maps.java b/core/src/io/anuke/mindustry/io/Maps.java deleted file mode 100644 index ad41c3f431..0000000000 --- a/core/src/io/anuke/mindustry/io/Maps.java +++ /dev/null @@ -1,224 +0,0 @@ -package io.anuke.mindustry.io; - -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.files.FileHandle; -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.graphics.Pixmap; -import com.badlogic.gdx.graphics.Texture; -import com.badlogic.gdx.utils.*; -import com.badlogic.gdx.utils.Json.Serializer; -import com.badlogic.gdx.utils.JsonWriter.OutputType; -import io.anuke.mindustry.world.Map; -import io.anuke.ucore.core.Settings; -import io.anuke.ucore.graphics.Pixmaps; -import io.anuke.ucore.util.Log; - -import static io.anuke.mindustry.Vars.*; - -public class Maps implements Disposable{ - private IntMap maps = new IntMap<>(); - private ObjectMap mapNames = new ObjectMap<>(); - private Array defaultMaps = new Array<>(); - private Map networkMap; - private int lastID; - private Json json = new Json(); - private Array array = new Array<>(); - - public Maps() { - json.setOutputType(OutputType.json); - json.setElementType(ArrayContainer.class, "maps", Map.class); - json.setSerializer(Color.class, new ColorSerializer()); - } - - public Iterable list(){ - return maps.values(); - } - - public Array getDefaultMaps(){ - return defaultMaps; - } - - public Array getCustomMaps(){ - array.clear(); - for(Map map : list()){ - if(map.custom) array.add(map); - } - return array; - } - - public Array getAllMaps(){ - array.clear(); - for(Map map : list()){ - array.add(map); - } - return array; - } - - public void setNetworkMap(Map map){ - if(networkMap != null){ - networkMap.pixmap.dispose(); - networkMap = null; - } - - networkMap = map; - } - - public Map getMap(int id){ - if(id == -1){ - return networkMap; - } - return maps.get(id); - } - - public Map getMap(String name){ - return mapNames.get(name); - } - - public void loadMaps(){ - if(!loadMapFile(Gdx.files.internal("maps/maps.json"), true)){ - throw new RuntimeException("Failed to load maps!"); - } - - if(!gwt) { - if (!loadMapFile(customMapDirectory.child("maps.json"), false)) { - try { - Log.info("Failed to find custom map directory."); - customMapDirectory.child("maps.json").writeString("{}", false); - } catch (Exception e) { - throw new RuntimeException("Failed to create custom map directory!"); - } - } - } - } - - public void removeMap(Map map){ - maps.remove(map.id); - mapNames.remove(map.name); - Array out = new Array<>(); - for(Map m : maps.values()){ - if(m.custom){ - out.add(m); - } - } - saveMaps(out, customMapDirectory.child("maps.json")); - } - - public void saveAndReload(Map map, Pixmap out){ - if(map.pixmap != null && out != map.pixmap && map.texture != null){ - map.texture.dispose(); - map.texture = new Texture(out); - }else if (out == map.pixmap){ - map.texture.draw(out, 0, 0); - } - - map.pixmap = out; - if(map.texture == null) map.texture = new Texture(map.pixmap); - - if(map.id == -1){ - if(mapNames.containsKey(map.name)){ - map.id = mapNames.get(map.name).id; - }else{ - map.id = ++lastID; - } - } - - if(!Settings.has("hiscore" + map.name)){ - Settings.defaults("hiscore" + map.name, 0); - } - - saveCustomMap(map); - ui.levels.reload(); - } - - public void saveMaps(Array array, FileHandle file){ - json.toJson(new ArrayContainer(array), file); - } - - public void saveCustomMap(Map toSave){ - toSave.custom = true; - Array out = new Array<>(); - boolean added = false; - for(Map map : maps.values()){ - if(map.custom){ - if(map.name.equals(toSave.name)){ - out.add(toSave); - toSave.id = map.id; - added = true; - }else{ - out.add(map); - } - } - } - if(!added){ - out.add(toSave); - } - maps.remove(toSave.id); - mapNames.remove(toSave.name); - maps.put(toSave.id, toSave); - mapNames.put(toSave.name, toSave); - Pixmaps.write(toSave.pixmap, customMapDirectory.child(toSave.name + ".png")); - saveMaps(out, customMapDirectory.child("maps.json")); - } - - private boolean loadMapFile(FileHandle file, boolean logException){ - try { - Array arr = json.fromJson(ArrayContainer.class, file).maps; - if (arr != null) { //can be an empty map file - for (Map map : arr) { - map.pixmap = new Pixmap(file.sibling(map.name + ".png")); - if (!headless) map.texture = new Texture(map.pixmap); - maps.put(map.id, map); - mapNames.put(map.name, map); - lastID = Math.max(lastID, map.id); - if (!map.custom) { - defaultMaps.add(map); - } - } - } - return true; - }catch (GdxRuntimeException e){ - Log.err(e); - return true; - }catch(Exception e){ - if(logException) { - Log.err(e); - Log.err("Failed loading map file: {0}", file); - } - return false; - } - } - - @Override - public void dispose(){ - for(Map map : maps.values()){ - if(map.texture != null) map.texture.dispose(); - map.pixmap.dispose(); - } - maps.clear(); - } - - public static class ArrayContainer{ - Array maps; - - public ArrayContainer() { - } - - public ArrayContainer(Array maps) { - this.maps = maps; - } - } - - private class ColorSerializer implements Serializer{ - - @Override - public void write(Json json, Color object, Class knownType){ - json.writeValue(object.toString().substring(0, 6)); - } - - @Override - public Color read(Json json, JsonValue jsonData, Class type){ - return Color.valueOf(jsonData.asString()); - } - - } -} diff --git a/core/src/io/anuke/mindustry/io/SaveFileReader.java b/core/src/io/anuke/mindustry/io/SaveFileReader.java new file mode 100644 index 0000000000..a969776896 --- /dev/null +++ b/core/src/io/anuke/mindustry/io/SaveFileReader.java @@ -0,0 +1,115 @@ +package io.anuke.mindustry.io; + +import io.anuke.arc.collection.ObjectMap; +import io.anuke.arc.collection.ObjectMap.Entry; +import io.anuke.arc.collection.StringMap; +import io.anuke.arc.util.io.CounterInputStream; +import io.anuke.arc.util.io.ReusableByteOutStream; +import io.anuke.mindustry.world.WorldContext; + +import java.io.*; + +public abstract class SaveFileReader{ + protected final ReusableByteOutStream byteOutput = new ReusableByteOutStream(); + protected final DataOutputStream dataBytes = new DataOutputStream(byteOutput); + protected final ReusableByteOutStream byteOutputSmall = new ReusableByteOutStream(); + protected final DataOutputStream dataBytesSmall = new DataOutputStream(byteOutputSmall); + protected final ObjectMap fallback = ObjectMap.of( + + ); + + protected void region(String name, DataInput stream, CounterInputStream counter, IORunner cons) throws IOException{ + counter.resetCount(); + int length; + try{ + length = readChunk(stream, cons); + }catch(Throwable e){ + throw new IOException("Error reading region \"" + name + "\".", e); + } + + if(length != counter.count() - 4){ + throw new IOException("Error reading region \"" + name + "\": read length mismatch. Expected: " + length + "; Actual: " + (counter.count() - 4)); + } + } + + protected void region(String name, DataOutput stream, IORunner cons) throws IOException{ + try{ + writeChunk(stream, cons); + }catch(Throwable e){ + throw new IOException("Error writing region \"" + name + "\".", e); + } + } + + public void writeChunk(DataOutput output, IORunner runner) throws IOException{ + writeChunk(output, false, runner); + } + + /** Write a chunk of input to the stream. An integer of some length is written first, followed by the data. */ + public void writeChunk(DataOutput output, boolean isByte, IORunner runner) throws IOException{ + ReusableByteOutStream dout = isByte ? byteOutputSmall : byteOutput; + //reset output position + dout.reset(); + //write the needed info + runner.accept(isByte ? dataBytesSmall : dataBytes); + int length = dout.size(); + //write length (either int or byte) followed by the output bytes + if(!isByte){ + output.writeInt(length); + }else{ + if(length > Short.MAX_VALUE){ + throw new IOException("Byte write length exceeded: " + length + " > " + Short.MAX_VALUE); + } + output.writeShort(length); + } + output.write(dout.getBytes(), 0, length); + } + + public int readChunk(DataInput input, IORunner runner) throws IOException{ + return readChunk(input, false, runner); + } + + /** Reads a chunk of some length. Use the runner for reading to catch more descriptive errors. */ + public int readChunk(DataInput input, boolean isByte, IORunner runner) throws IOException{ + int length = isByte ? input.readUnsignedShort() : input.readInt(); + runner.accept(input); + return length; + } + + public void skipRegion(DataInput input) throws IOException{ + skipRegion(input, false); + } + + /** Skip a region completely. */ + public void skipRegion(DataInput input, boolean isByte) throws IOException{ + int length = readChunk(input, isByte, t -> {}); + int skipped = input.skipBytes(length); + if(length != skipped){ + throw new IOException("Could not skip bytes. Expected length: " + length + "; Actual length: " + skipped); + } + } + + public void writeStringMap(DataOutput stream, ObjectMap map) throws IOException{ + stream.writeShort(map.size); + for(Entry entry : map.entries()){ + stream.writeUTF(entry.key); + stream.writeUTF(entry.value); + } + } + + public StringMap readStringMap(DataInput stream) throws IOException{ + StringMap map = new StringMap(); + short size = stream.readShort(); + for(int i = 0; i < size; i++){ + map.put(stream.readUTF(), stream.readUTF()); + } + return map; + } + + public abstract void read(DataInputStream stream, CounterInputStream counter, WorldContext context) throws IOException; + + public abstract void write(DataOutputStream stream) throws IOException; + + protected interface IORunner{ + void accept(T stream) throws IOException; + } +} diff --git a/core/src/io/anuke/mindustry/io/SaveFileVersion.java b/core/src/io/anuke/mindustry/io/SaveFileVersion.java deleted file mode 100644 index 36b38f39f9..0000000000 --- a/core/src/io/anuke/mindustry/io/SaveFileVersion.java +++ /dev/null @@ -1,38 +0,0 @@ -package io.anuke.mindustry.io; - -import io.anuke.mindustry.game.Difficulty; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; - -public abstract class SaveFileVersion { - public final int version; - - public SaveFileVersion(int version){ - this.version = version; - } - - public SaveMeta getData(DataInputStream stream) throws IOException{ - long time = stream.readLong(); //read last saved time - byte mode = stream.readByte(); //read the gamemode - byte map = stream.readByte(); //read the map - int wave = stream.readInt(); //read the wave - return new SaveMeta(version, time, mode, map, wave, Difficulty.normal); - } - - public abstract void read(DataInputStream stream) throws IOException; - public abstract void write(DataOutputStream stream) throws IOException; - - public static void writeString(DataOutputStream stream, String string) throws IOException{ - stream.writeByte(string.length()); - stream.writeBytes(string); - } - - public static String readString(DataInputStream stream) throws IOException{ - int length = stream.readByte(); - byte[] result = new byte[length]; - stream.read(result); - return new String(result); - } -} diff --git a/core/src/io/anuke/mindustry/io/SaveIO.java b/core/src/io/anuke/mindustry/io/SaveIO.java index 0a150d675a..916c4ad414 100644 --- a/core/src/io/anuke/mindustry/io/SaveIO.java +++ b/core/src/io/anuke/mindustry/io/SaveIO.java @@ -1,165 +1,191 @@ package io.anuke.mindustry.io; -import com.badlogic.gdx.files.FileHandle; -import com.badlogic.gdx.utils.Array; -import com.badlogic.gdx.utils.Base64Coder; -import com.badlogic.gdx.utils.IntMap; -import io.anuke.mindustry.io.versions.Save12; -import io.anuke.mindustry.io.versions.Save13; -import io.anuke.mindustry.io.versions.Save14; -import io.anuke.mindustry.io.versions.Save15; -import io.anuke.ucore.core.Settings; +import io.anuke.arc.collection.*; +import io.anuke.arc.files.FileHandle; +import io.anuke.arc.util.io.CounterInputStream; +import io.anuke.arc.util.io.FastDeflaterOutputStream; +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.io.versions.Save1; +import io.anuke.mindustry.world.WorldContext; import java.io.*; +import java.util.Arrays; +import java.util.zip.InflaterInputStream; import static io.anuke.mindustry.Vars.*; public class SaveIO{ - public static final IntMap versions = new IntMap<>(); - public static final Array versionArray = Array.with( - new Save12(), - new Save13(), - new Save14(), - new Save15() - ); + /** Format header. This is the string 'MSAV' in ASCII. */ + public static final byte[] header = {77, 83, 65, 86}; + public static final IntMap versions = new IntMap<>(); + public static final Array versionArray = Array.with(new Save1()); - static{ - for(SaveFileVersion version : versionArray){ - versions.put(version.version, version); - } - } + static{ + for(SaveVersion version : versionArray){ + versions.put(version.version, version); + } + } - public static void saveToSlot(int slot){ - if(gwt){ - ByteArrayOutputStream stream = new ByteArrayOutputStream(); - write(stream); - Settings.putString("save-"+slot+"-data", new String(Base64Coder.encode(stream.toByteArray()))); - Settings.save(); - }else{ - FileHandle file = fileFor(slot); - boolean exists = file.exists(); - if(exists) file.moveTo(file.sibling(file.name() + "-backup." + file.extension())); - try { - write(fileFor(slot)); - }catch (Exception e){ - if(exists) file.sibling(file.name() + "-backup." + file.extension()).moveTo(file); - throw new RuntimeException(e); - } - } - } - - public static void loadFromSlot(int slot){ - if(gwt){ - String string = Settings.getString("save-"+slot+"-data"); - ByteArrayInputStream stream = new ByteArrayInputStream(Base64Coder.decode(string)); - load(stream); - }else{ - load(fileFor(slot)); - } - } + public static SaveVersion getSaveWriter(){ + return versionArray.peek(); + } - public static DataInputStream getSlotStream(int slot){ - if(gwt){ - String string = Settings.getString("save-"+slot+"-data"); - byte[] bytes = Base64Coder.decode(string); - return new DataInputStream(new ByteArrayInputStream(bytes)); - }else{ - return new DataInputStream(fileFor(slot).read()); - } - } - - public static boolean isSaveValid(int slot){ - try { - return isSaveValid(getSlotStream(slot)); - }catch (Exception e){ - return false; - } - } + public static SaveVersion getSaveWriter(int version){ + return versions.get(version); + } - public static boolean isSaveValid(FileHandle file){ - return isSaveValid(new DataInputStream(file.read())); - } + public static void saveToSlot(int slot){ + FileHandle file = fileFor(slot); + boolean exists = file.exists(); + if(exists) file.moveTo(backupFileFor(file)); + try{ + write(fileFor(slot)); + }catch(Exception e){ + if(exists) backupFileFor(file).moveTo(file); + throw new RuntimeException(e); + } + } - public static boolean isSaveValid(DataInputStream stream){ + public static void loadFromSlot(int slot) throws SaveException{ + load(fileFor(slot)); + } - try{ - int version = stream.readInt(); - SaveFileVersion ver = versions.get(version); - SaveMeta meta = ver.getData(stream); - return meta.map != null; - }catch (Exception e){ - return false; - } - } + public static DataInputStream getSlotStream(int slot){ + return new DataInputStream(new InflaterInputStream(fileFor(slot).read(bufferSize))); + } - public static SaveMeta getData(int slot){ - return getData(getSlotStream(slot)); - } - - public static SaveMeta getData(DataInputStream stream){ - - try{ - int version = stream.readInt(); - SaveMeta meta = versions.get(version).getData(stream); - stream.close(); - return meta; - }catch (IOException e){ - throw new RuntimeException(e); - } - } - - public static FileHandle fileFor(int slot){ - return saveDirectory.child(slot + ".mins"); - } + public static DataInputStream getBackupSlotStream(int slot){ + return new DataInputStream(new InflaterInputStream(backupFileFor(fileFor(slot)).read(bufferSize))); + } - public static void write(FileHandle file){ - write(file.write(false)); - } + public static boolean isSaveValid(int slot){ + try{ + getMeta(slot); + return true; + }catch(Exception e){ + return false; + } + } - public static void write(OutputStream os){ + public static boolean isSaveValid(FileHandle file){ + try{ + return isSaveValid(new DataInputStream(new InflaterInputStream(file.read(bufferSize)))); + }catch(Exception e){ + return false; + } + } - DataOutputStream stream; - - try{ - stream = new DataOutputStream(os); - getVersion().write(stream); - stream.close(); - }catch (Exception e){ - throw new RuntimeException(e); - } - } + public static boolean isSaveValid(DataInputStream stream){ + try{ + getMeta(stream); + return true; + }catch(Exception e){ + e.printStackTrace(); + return false; + } + } - public static void load(FileHandle file){ - try { - load(file.read()); - }catch (RuntimeException e){ - e.printStackTrace(); - FileHandle backup = file.sibling(file.name() + "-backup." + file.extension()); - if(backup.exists()){ - load(backup.read()); - } - } - } + public static SaveMeta getMeta(int slot){ + try{ + return getMeta(getSlotStream(slot)); + }catch(Exception e){ + return getMeta(getBackupSlotStream(slot)); + } + } - public static void load(InputStream is){ - logic.reset(); + public static SaveMeta getMeta(DataInputStream stream){ - DataInputStream stream; - - try{ - stream = new DataInputStream(is); - int version = stream.readInt(); - SaveFileVersion ver = versions.get(version); + try{ + readHeader(stream); + int version = stream.readInt(); + SaveMeta meta = versions.get(version).getMeta(stream); + stream.close(); + return meta; + }catch(IOException e){ + throw new RuntimeException(e); + } + } - ver.read(stream); + public static FileHandle fileFor(int slot){ + return saveDirectory.child(slot + "." + Vars.saveExtension); + } - stream.close(); - }catch (Exception e){ - throw new RuntimeException(e); - } - } + public static FileHandle backupFileFor(FileHandle file){ + return file.sibling(file.name() + "-backup." + file.extension()); + } - public static SaveFileVersion getVersion(){ - return versionArray.peek(); - } + public static void write(FileHandle file, StringMap tags){ + write(new FastDeflaterOutputStream(file.write(false, bufferSize)), tags); + } + + public static void write(FileHandle file){ + write(file, null); + } + + public static void write(OutputStream os, StringMap tags){ + try(DataOutputStream stream = new DataOutputStream(os)){ + stream.write(header); + stream.writeInt(getVersion().version); + if(tags == null){ + getVersion().write(stream); + }else{ + getVersion().write(stream, tags); + } + }catch(Exception e){ + throw new RuntimeException(e); + } + } + + public static void load(FileHandle file) throws SaveException{ + load(file, world.context); + } + + public static void load(FileHandle file, WorldContext context) throws SaveException{ + try{ + //try and load; if any exception at all occurs + load(new InflaterInputStream(file.read(bufferSize)), context); + }catch(SaveException e){ + e.printStackTrace(); + FileHandle backup = file.sibling(file.name() + "-backup." + file.extension()); + if(backup.exists()){ + load(new InflaterInputStream(backup.read(bufferSize)), context); + }else{ + throw new SaveException(e.getCause()); + } + } + } + + /** Loads from a deflated (!) input stream.*/ + public static void load(InputStream is, WorldContext context) throws SaveException{ + try(CounterInputStream counter = new CounterInputStream(is); DataInputStream stream = new DataInputStream(counter)){ + logic.reset(); + readHeader(stream); + int version = stream.readInt(); + SaveVersion ver = versions.get(version); + + ver.read(stream, counter, context); + }catch(Exception e){ + throw new SaveException(e); + }finally{ + content.setTemporaryMapper(null); + } + } + + public static SaveVersion getVersion(){ + return versionArray.peek(); + } + + public static void readHeader(DataInput input) throws IOException{ + byte[] bytes = new byte[header.length]; + input.readFully(bytes); + if(!Arrays.equals(bytes, header)){ + throw new IOException("Incorrect header! Expecting: " + Arrays.toString(header) + "; Actual: " + Arrays.toString(bytes)); + } + } + + public static class SaveException extends RuntimeException{ + public SaveException(Throwable throwable){ + super(throwable); + } + } } diff --git a/core/src/io/anuke/mindustry/io/SaveMeta.java b/core/src/io/anuke/mindustry/io/SaveMeta.java index d937c90603..f88746605f 100644 --- a/core/src/io/anuke/mindustry/io/SaveMeta.java +++ b/core/src/io/anuke/mindustry/io/SaveMeta.java @@ -1,28 +1,26 @@ package io.anuke.mindustry.io; -import io.anuke.mindustry.core.Platform; -import io.anuke.mindustry.game.Difficulty; -import io.anuke.mindustry.game.GameMode; -import io.anuke.mindustry.world.Map; - -import java.util.Date; +import io.anuke.mindustry.game.Rules; +import io.anuke.mindustry.maps.Map; import static io.anuke.mindustry.Vars.world; -public class SaveMeta { +public class SaveMeta{ public int version; - public String date; - public GameMode mode; + public int build; + public long timestamp; + public long timePlayed; public Map map; public int wave; - public Difficulty difficulty; + public Rules rules; - public SaveMeta(int version, long date, int mode, int map, int wave, Difficulty difficulty){ + public SaveMeta(int version, long timestamp, long timePlayed, int build, String map, int wave, Rules rules){ this.version = version; - this.date = Platform.instance.format(new Date(date)); - this.mode = GameMode.values()[mode]; - this.map = world.maps().getMap(map); + this.build = build; + this.timestamp = timestamp; + this.timePlayed = timePlayed; + this.map = world.maps.all().find(m -> m.name().equals(map)); this.wave = wave; - this.difficulty = difficulty; + this.rules = rules; } } diff --git a/core/src/io/anuke/mindustry/io/SaveVersion.java b/core/src/io/anuke/mindustry/io/SaveVersion.java new file mode 100644 index 0000000000..839bed89fe --- /dev/null +++ b/core/src/io/anuke/mindustry/io/SaveVersion.java @@ -0,0 +1,314 @@ +package io.anuke.mindustry.io; + +import io.anuke.arc.collection.*; +import io.anuke.arc.util.Time; +import io.anuke.arc.util.io.CounterInputStream; +import io.anuke.mindustry.entities.Entities; +import io.anuke.mindustry.entities.EntityGroup; +import io.anuke.mindustry.entities.traits.*; +import io.anuke.mindustry.game.*; +import io.anuke.mindustry.game.Teams.TeamData; +import io.anuke.mindustry.gen.BrokenBlock; +import io.anuke.mindustry.maps.Map; +import io.anuke.mindustry.type.ContentType; +import io.anuke.mindustry.world.*; + +import java.io.*; + +import static io.anuke.mindustry.Vars.*; + +public abstract class SaveVersion extends SaveFileReader{ + public final int version; + + public SaveVersion(int version){ + this.version = version; + } + + public SaveMeta getMeta(DataInput stream) throws IOException{ + stream.readInt(); //length of data, doesn't matter here + StringMap map = readStringMap(stream); + return new SaveMeta(map.getInt("version"), map.getLong("saved"), map.getLong("playtime"), map.getInt("build"), map.get("mapname"), map.getInt("wave"), JsonIO.read(Rules.class, map.get("rules", "{}"))); + } + + @Override + public final void write(DataOutputStream stream) throws IOException{ + write(stream, new StringMap()); + } + + @Override + public final void read(DataInputStream stream, CounterInputStream counter, WorldContext context) throws IOException{ + region("meta", stream, counter, this::readMeta); + region("content", stream, counter, this::readContentHeader); + + try{ + region("map", stream, counter, in -> readMap(in, context)); + region("entities", stream, counter, this::readEntities); + }finally{ + content.setTemporaryMapper(null); + } + } + + public final void write(DataOutputStream stream, StringMap extraTags) throws IOException{ + region("meta", stream, out -> writeMeta(out, extraTags)); + region("content", stream, this::writeContentHeader); + region("map", stream, this::writeMap); + region("entities", stream, this::writeEntities); + } + + public void writeMeta(DataOutput stream, StringMap tags) throws IOException{ + writeStringMap(stream, StringMap.of( + "saved", Time.millis(), + "playtime", headless ? 0 : control.saves.getTotalPlaytime(), + "build", Version.build, + "mapname", world.getMap() == null ? "unknown" : world.getMap().name(), + "wave", state.wave, + "wavetime", state.wavetime, + "stats", JsonIO.write(state.stats), + "rules", JsonIO.write(state.rules), + "teamdata", JsonIO.write(state.teams.getActive().toArray(TeamData.class)), + "width", world.width(), + "height", world.height() + ).merge(tags)); + } + + public void readMeta(DataInput stream) throws IOException{ + StringMap map = readStringMap(stream); + + state.wave = map.getInt("wave"); + state.wavetime = map.getFloat("wavetime", state.rules.waveSpacing); + state.stats = JsonIO.read(Stats.class, map.get("stats", "{}")); + state.rules = JsonIO.read(Rules.class, map.get("rules", "{}")); + if(state.rules.spawns.isEmpty()) state.rules.spawns = defaultWaves.get(); + + //only broken blocks are transferred over right now; nothing else + TeamData[] teams = JsonIO.read(TeamData[].class, map.get("teamdata", "[]")); + for(TeamData data : teams){ + state.teams.get(data.team).brokenBlocks = data.brokenBlocks; + } + + Map worldmap = world.maps.byName(map.get("mapname", "\\\\\\")); + world.setMap(worldmap == null ? new Map(StringMap.of( + "name", map.get("mapname", "Unknown"), + "width", 1, + "height", 1 + )) : worldmap); + } + + public void writeMap(DataOutput stream) throws IOException{ + //write world size + stream.writeShort(world.width()); + stream.writeShort(world.height()); + + //floor + overlay + for(int i = 0; i < world.width() * world.height(); i++){ + Tile tile = world.rawTile(i % world.width(), i / world.width()); + stream.writeShort(tile.floorID()); + stream.writeShort(tile.overlayID()); + int consecutives = 0; + + for(int j = i + 1; j < world.width() * world.height() && consecutives < 255; j++){ + Tile nextTile = world.rawTile(j % world.width(), j / world.width()); + + if(nextTile.floorID() != tile.floorID() || nextTile.overlayID() != tile.overlayID()){ + break; + } + + consecutives++; + } + + stream.writeByte(consecutives); + i += consecutives; + } + + //blocks + for(int i = 0; i < world.width() * world.height(); i++){ + Tile tile = world.rawTile(i % world.width(), i / world.width()); + stream.writeShort(tile.blockID()); + + if(tile.entity != null){ + writeChunk(stream, true, out -> { + out.writeByte(tile.entity.version()); + tile.entity.write(out); + }); + }else{ + //write consecutive non-entity blocks + int consecutives = 0; + + for(int j = i + 1; j < world.width() * world.height() && consecutives < 255; j++){ + Tile nextTile = world.rawTile(j % world.width(), j / world.width()); + + if(nextTile.blockID() != tile.blockID()){ + break; + } + + consecutives++; + } + + stream.writeByte(consecutives); + i += consecutives; + } + } + } + + public void readMap(DataInput stream, WorldContext context) throws IOException{ + int width = stream.readUnsignedShort(); + int height = stream.readUnsignedShort(); + + boolean generating = context.isGenerating(); + + if(!generating) context.begin(); + try{ + + context.resize(width, height); + + //read floor and create tiles first + for(int i = 0; i < width * height; i++){ + int x = i % width, y = i / width; + short floorid = stream.readShort(); + short oreid = stream.readShort(); + int consecutives = stream.readUnsignedByte(); + + context.create(x, y, floorid, oreid, (short)0); + + for(int j = i + 1; j < i + 1 + consecutives; j++){ + int newx = j % width, newy = j / width; + context.create(newx, newy, floorid, oreid, (short)0); + } + + i += consecutives; + } + + //read blocks + for(int i = 0; i < width * height; i++){ + int x = i % width, y = i / width; + Block block = content.block(stream.readShort()); + Tile tile = context.tile(x, y); + tile.setBlock(block); + + if(tile.entity != null){ + try{ + readChunk(stream, true, in -> { + byte version = in.readByte(); + tile.entity.read(in, version); + }); + }catch(Exception e){ + throw new IOException("Failed to read tile entity of block: " + block, e); + } + }else{ + int consecutives = stream.readUnsignedByte(); + + for(int j = i + 1; j < i + 1 + consecutives; j++){ + int newx = j % width, newy = j / width; + context.tile(newx, newy).setBlock(block); + } + + i += consecutives; + } + } + }finally{ + if(!generating) context.end(); + } + } + + public void writeEntities(DataOutput stream) throws IOException{ + //write entity chunk + int groups = 0; + + for(EntityGroup group : Entities.getAllGroups()){ + if(!group.isEmpty() && group.all().get(0) instanceof SaveTrait){ + groups++; + } + } + + stream.writeByte(groups); + + for(EntityGroup group : Entities.getAllGroups()){ + if(!group.isEmpty() && group.all().get(0) instanceof SaveTrait){ + stream.writeInt(group.size()); + for(Entity entity : group.all()){ + SaveTrait save = (SaveTrait)entity; + //each entity is a separate chunk. + writeChunk(stream, true, out -> { + out.writeByte(save.getTypeID()); + out.writeByte(save.version()); + save.writeSave(out); + }); + } + } + } + } + + public void readEntities(DataInput stream) throws IOException{ + byte groups = stream.readByte(); + + for(int i = 0; i < groups; i++){ + int amount = stream.readInt(); + for(int j = 0; j < amount; j++){ + //TODO throw exception on read fail + readChunk(stream, true, in -> { + byte typeid = in.readByte(); + byte version = in.readByte(); + SaveTrait trait = (SaveTrait)TypeTrait.getTypeByID(typeid).get(); + trait.readSave(in, version); + }); + } + } + } + + public void readContentHeader(DataInput stream) throws IOException{ + + byte mapped = stream.readByte(); + + MappableContent[][] map = new MappableContent[ContentType.values().length][0]; + + for(int i = 0; i < mapped; i++){ + ContentType type = ContentType.values()[stream.readByte()]; + short total = stream.readShort(); + map[type.ordinal()] = new MappableContent[total]; + + for(int j = 0; j < total; j++){ + String name = stream.readUTF(); + map[type.ordinal()][j] = content.getByName(type, fallback.get(name, name)); + } + } + + content.setTemporaryMapper(map); + + remapContent(); + } + + public void writeContentHeader(DataOutput stream) throws IOException{ + Array[] map = content.getContentMap(); + + int mappable = 0; + for(Array arr : map){ + if(arr.size > 0 && arr.first() instanceof MappableContent){ + mappable++; + } + } + + stream.writeByte(mappable); + for(Array arr : map){ + if(arr.size > 0 && arr.first() instanceof MappableContent){ + stream.writeByte(arr.first().getContentType().ordinal()); + stream.writeShort(arr.size); + for(Content c : arr){ + stream.writeUTF(((MappableContent)c).name); + } + } + } + } + + /** sometimes it's necessary to remap IDs after the content header is read.*/ + public void remapContent(){ + for(Team team : Team.all){ + if(state.teams.isActive(team)){ + LongQueue queue = state.teams.get(team).brokenBlocks; + for(int i = 0; i < queue.size; i++){ + //remap broken block IDs + queue.set(i, BrokenBlock.block(queue.get(i), content.block(BrokenBlock.block(queue.get(i))).id)); + } + } + } + } +} diff --git a/core/src/io/anuke/mindustry/io/Saves.java b/core/src/io/anuke/mindustry/io/Saves.java deleted file mode 100644 index 397d389a3c..0000000000 --- a/core/src/io/anuke/mindustry/io/Saves.java +++ /dev/null @@ -1,189 +0,0 @@ -package io.anuke.mindustry.io; - -import com.badlogic.gdx.files.FileHandle; -import com.badlogic.gdx.utils.Array; -import com.badlogic.gdx.utils.async.AsyncExecutor; -import io.anuke.mindustry.core.GameState.State; -import io.anuke.mindustry.game.Difficulty; -import io.anuke.mindustry.game.GameMode; -import io.anuke.mindustry.world.Map; -import io.anuke.ucore.core.Settings; -import io.anuke.ucore.core.Timers; - -import java.io.IOException; - -import static io.anuke.mindustry.Vars.saveSlots; -import static io.anuke.mindustry.Vars.state; - -public class Saves { - private int nextSlot; - private Array saves = new Array<>(); - private SaveSlot current; - private boolean saving; - private float time; - - private AsyncExecutor exec = new AsyncExecutor(1); - - public void load(){ - saves.clear(); - for(int i = 0; i < saveSlots; i ++){ - if(SaveIO.isSaveValid(i)){ - SaveSlot slot = new SaveSlot(i); - saves.add(slot); - slot.meta = SaveIO.getData(i); - nextSlot = i + 1; - } - } - } - - public SaveSlot getCurrent() { - return current; - } - - public void update(){ - if(state.is(State.menu)){ - current = null; - } - - if(!state.is(State.menu) && !state.gameOver && current != null && current.isAutosave()){ - time += Timers.delta(); - if(time > Settings.getInt("saveinterval")*60) { - saving = true; - - exec.submit(() -> { - SaveIO.saveToSlot(current.index); - current.meta = SaveIO.getData(current.index); - saving = false; - return true; - }); - - time = 0; - } - }else{ - time = 0; - } - } - - public void resetSave(){ - current = null; - } - - public boolean isSaving(){ - return saving; - } - - public boolean canAddSave(){ - return nextSlot < saveSlots; - } - - public void addSave(String name){ - SaveSlot slot = new SaveSlot(nextSlot); - nextSlot ++; - slot.setName(name); - saves.add(slot); - SaveIO.saveToSlot(slot.index); - slot.meta = SaveIO.getData(slot.index); - current = slot; - } - - public SaveSlot importSave(FileHandle file) throws IOException{ - SaveSlot slot = new SaveSlot(nextSlot); - slot.importFile(file); - nextSlot ++; - slot.setName(file.nameWithoutExtension()); - saves.add(slot); - slot.meta = SaveIO.getData(slot.index); - current = slot; - return slot; - } - - public Array getSaveSlots(){ - return saves; - } - - public class SaveSlot{ - public final int index; - SaveMeta meta; - - public SaveSlot(int index){ - this.index = index; - } - - public void load(){ - current = this; - SaveIO.loadFromSlot(index); - meta = SaveIO.getData(index); - } - - public void save(){ - current = this; - SaveIO.saveToSlot(index); - meta = SaveIO.getData(index); - } - - public String getDate(){ - return meta.date; - } - - public Map getMap(){ - return meta.map; - } - - public String getName(){ - return Settings.getString("save-"+index+"-name"); - } - - public void setName(String name){ - Settings.putString("save-"+index+"-name", name); - Settings.save(); - } - - public int getWave(){ - return meta.wave; - } - - public Difficulty getDifficulty(){ - return meta.difficulty; - } - - public GameMode getMode(){ - return meta.mode; - } - - public boolean isAutosave(){ - return Settings.getBool("save-"+index+"-autosave"); - } - - public void setAutosave(boolean save){ - Settings.putBool("save-"+index + "-autosave", save); - Settings.save(); - } - - public void importFile(FileHandle file) throws IOException{ - try{ - file.copyTo(SaveIO.fileFor(index)); - }catch (Exception e){ - throw new IOException(e); - } - } - - public void exportFile(FileHandle file) throws IOException{ - try{ - if(!file.extension().equals("mins")){ - file = file.parent().child(file.nameWithoutExtension() + ".mins"); - } - SaveIO.fileFor(index).copyTo(file); - }catch (Exception e){ - throw new IOException(e); - } - } - - public void delete(){ - SaveIO.fileFor(index).delete(); - saves.removeValue(this, true); - if(this == current){ - current = null; - } - } - } -} diff --git a/core/src/io/anuke/mindustry/io/TypeIO.java b/core/src/io/anuke/mindustry/io/TypeIO.java new file mode 100644 index 0000000000..d8bdbf9b4c --- /dev/null +++ b/core/src/io/anuke/mindustry/io/TypeIO.java @@ -0,0 +1,328 @@ +package io.anuke.mindustry.io; + +import io.anuke.annotations.Annotations.ReadClass; +import io.anuke.annotations.Annotations.WriteClass; +import io.anuke.arc.graphics.Color; +import io.anuke.mindustry.entities.Effects; +import io.anuke.mindustry.entities.Effects.Effect; +import io.anuke.mindustry.entities.Entities; +import io.anuke.mindustry.entities.bullet.Bullet; +import io.anuke.mindustry.entities.bullet.BulletType; +import io.anuke.mindustry.entities.traits.BuilderTrait.BuildRequest; +import io.anuke.mindustry.entities.traits.ShooterTrait; +import io.anuke.mindustry.entities.type.*; +import io.anuke.mindustry.game.Team; +import io.anuke.mindustry.net.Administration.TraceInfo; +import io.anuke.mindustry.net.Packets.AdminAction; +import io.anuke.mindustry.net.Packets.KickReason; +import io.anuke.mindustry.type.*; +import io.anuke.mindustry.world.*; + +import java.io.*; +import java.nio.ByteBuffer; + +import static io.anuke.mindustry.Vars.*; + +/** Class for specifying read/write methods for code generation. */ +@SuppressWarnings("unused") +public class TypeIO{ + + @WriteClass(Player.class) + public static void writePlayer(ByteBuffer buffer, Player player){ + if(player == null){ + buffer.putInt(-1); + }else{ + buffer.putInt(player.id); + } + } + + @ReadClass(Player.class) + public static Player readPlayer(ByteBuffer buffer){ + int id = buffer.getInt(); + return id == -1 ? null : playerGroup.getByID(id); + } + + @WriteClass(Unit.class) + public static void writeUnit(ByteBuffer buffer, Unit unit){ + if(unit.getGroup() == null){ + buffer.put((byte)-1); + return; + } + buffer.put((byte)unit.getGroup().getID()); + buffer.putInt(unit.getID()); + } + + @ReadClass(Unit.class) + public static Unit readUnit(ByteBuffer buffer){ + byte gid = buffer.get(); + if(gid == -1) return null; + int id = buffer.getInt(); + return (Unit)Entities.getGroup(gid).getByID(id); + } + + @WriteClass(ShooterTrait.class) + public static void writeShooter(ByteBuffer buffer, ShooterTrait trait){ + buffer.put((byte)trait.getGroup().getID()); + buffer.putInt(trait.getID()); + } + + @ReadClass(ShooterTrait.class) + public static ShooterTrait readShooter(ByteBuffer buffer){ + byte gid = buffer.get(); + int id = buffer.getInt(); + return (ShooterTrait)Entities.getGroup(gid).getByID(id); + } + + @WriteClass(Bullet.class) + public static void writeBullet(ByteBuffer buffer, Bullet bullet){ + buffer.putInt(bullet.getID()); + } + + @ReadClass(Bullet.class) + public static Bullet readBullet(ByteBuffer buffer){ + int id = buffer.getInt(); + return bulletGroup.getByID(id); + } + + @WriteClass(BaseUnit.class) + public static void writeBaseUnit(ByteBuffer buffer, BaseUnit unit){ + buffer.put((byte)unit.getTeam().ordinal()); + buffer.putInt(unit.getID()); + } + + @ReadClass(BaseUnit.class) + public static BaseUnit readBaseUnit(ByteBuffer buffer){ + byte tid = buffer.get(); + int id = buffer.getInt(); + return unitGroups[tid].getByID(id); + } + + @WriteClass(Tile.class) + public static void writeTile(ByteBuffer buffer, Tile tile){ + buffer.putInt(tile == null ? Pos.get(-1, -1) : tile.pos()); + } + + @ReadClass(Tile.class) + public static Tile readTile(ByteBuffer buffer){ + return world.tile(buffer.getInt()); + } + + @WriteClass(Block.class) + public static void writeBlock(ByteBuffer buffer, Block block){ + buffer.putShort(block.id); + } + + @ReadClass(Block.class) + public static Block readBlock(ByteBuffer buffer){ + return content.block(buffer.getShort()); + } + + @WriteClass(BuildRequest[].class) + public static void writeRequests(ByteBuffer buffer, BuildRequest[] requests){ + buffer.putShort((short)requests.length); + for(BuildRequest request : requests){ + buffer.put(request.breaking ? (byte)1 : 0); + buffer.putInt(Pos.get(request.x, request.y)); + if(!request.breaking){ + buffer.putShort(request.block.id); + buffer.put((byte)request.rotation); + } + } + } + + @ReadClass(BuildRequest[].class) + public static BuildRequest[] readRequests(ByteBuffer buffer){ + short reqamount = buffer.getShort(); + BuildRequest[] reqs = new BuildRequest[reqamount]; + for(int i = 0; i < reqamount; i++){ + byte type = buffer.get(); + int position = buffer.getInt(); + BuildRequest currentRequest; + + if(type == 1){ //remove + currentRequest = new BuildRequest(Pos.x(position), Pos.y(position)); + }else{ //place + short block = buffer.getShort(); + byte rotation = buffer.get(); + currentRequest = new BuildRequest(Pos.x(position), Pos.y(position), rotation, content.block(block)); + } + + reqs[i] = (currentRequest); + } + + return reqs; + } + + @WriteClass(KickReason.class) + public static void writeKick(ByteBuffer buffer, KickReason reason){ + buffer.put((byte)reason.ordinal()); + } + + @ReadClass(KickReason.class) + public static KickReason readKick(ByteBuffer buffer){ + return KickReason.values()[buffer.get()]; + } + + @WriteClass(Team.class) + public static void writeTeam(ByteBuffer buffer, Team reason){ + buffer.put((byte)reason.ordinal()); + } + + @ReadClass(Team.class) + public static Team readTeam(ByteBuffer buffer){ + return Team.all[buffer.get()]; + } + + @WriteClass(AdminAction.class) + public static void writeAction(ByteBuffer buffer, AdminAction reason){ + buffer.put((byte)reason.ordinal()); + } + + @ReadClass(AdminAction.class) + public static AdminAction readAction(ByteBuffer buffer){ + return AdminAction.values()[buffer.get()]; + } + + @WriteClass(Effect.class) + public static void writeEffect(ByteBuffer buffer, Effect effect){ + buffer.putShort((short)effect.id); + } + + @ReadClass(Effect.class) + public static Effect readEffect(ByteBuffer buffer){ + return Effects.getEffect(buffer.getShort()); + } + + @WriteClass(UnitType.class) + public static void writeUnitType(ByteBuffer buffer, UnitType effect){ + buffer.putShort(effect.id); + } + + @ReadClass(UnitType.class) + public static UnitType readUnitType(ByteBuffer buffer){ + return content.getByID(ContentType.unit, buffer.getShort()); + } + + @WriteClass(Color.class) + public static void writeColor(ByteBuffer buffer, Color color){ + buffer.putInt(Color.rgba8888(color)); + } + + @ReadClass(Color.class) + public static Color readColor(ByteBuffer buffer){ + return new Color(buffer.getInt()); + } + + @WriteClass(Mech.class) + public static void writeMech(ByteBuffer buffer, Mech mech){ + buffer.put((byte)mech.id); + } + + @ReadClass(Mech.class) + public static Mech readMech(ByteBuffer buffer){ + return content.getByID(ContentType.mech, buffer.get()); + } + + @WriteClass(Liquid.class) + public static void writeLiquid(ByteBuffer buffer, Liquid liquid){ + buffer.putShort(liquid == null ? -1 : liquid.id); + } + + @ReadClass(Liquid.class) + public static Liquid readLiquid(ByteBuffer buffer){ + short id = buffer.getShort(); + return id == -1 ? null : content.liquid(id); + } + + @WriteClass(BulletType.class) + public static void writeBulletType(ByteBuffer buffer, BulletType type){ + buffer.putShort(type.id); + } + + @ReadClass(BulletType.class) + public static BulletType readBulletType(ByteBuffer buffer){ + return content.getByID(ContentType.bullet, buffer.getShort()); + } + + @WriteClass(Item.class) + public static void writeItem(ByteBuffer buffer, Item item){ + buffer.putShort(item == null ? -1 : item.id); + } + + @ReadClass(Item.class) + public static Item readItem(ByteBuffer buffer){ + short id = buffer.getShort(); + return id == -1 ? null : content.item(id); + } + + @WriteClass(String.class) + public static void writeString(ByteBuffer buffer, String string){ + if(string != null){ + byte[] bytes = string.getBytes(charset); + buffer.putShort((short)bytes.length); + buffer.put(bytes); + }else{ + buffer.putShort((short)-1); + } + } + + @ReadClass(String.class) + public static String readString(ByteBuffer buffer){ + short slength = buffer.getShort(); + if(slength != -1){ + byte[] bytes = new byte[slength]; + buffer.get(bytes); + return new String(bytes, charset); + }else{ + return null; + } + } + + @WriteClass(byte[].class) + public static void writeBytes(ByteBuffer buffer, byte[] bytes){ + buffer.putShort((short)bytes.length); + buffer.put(bytes); + } + + @ReadClass(byte[].class) + public static byte[] readBytes(ByteBuffer buffer){ + short length = buffer.getShort(); + byte[] bytes = new byte[length]; + buffer.get(bytes); + return bytes; + } + + @WriteClass(TraceInfo.class) + public static void writeTraceInfo(ByteBuffer buffer, TraceInfo trace){ + writeString(buffer, trace.ip); + writeString(buffer, trace.uuid); + buffer.put(trace.modded ? (byte)1 : 0); + buffer.put(trace.mobile ? (byte)1 : 0); + } + + @ReadClass(TraceInfo.class) + public static TraceInfo readTraceInfo(ByteBuffer buffer){ + return new TraceInfo(readString(buffer), readString(buffer), buffer.get() == 1, buffer.get() == 1); + } + + public static void writeStringData(DataOutput buffer, String string) throws IOException{ + if(string != null){ + byte[] bytes = string.getBytes(charset); + buffer.writeShort((short)bytes.length); + buffer.write(bytes); + }else{ + buffer.writeShort((short)-1); + } + } + + public static String readStringData(DataInput buffer) throws IOException{ + short slength = buffer.readShort(); + if(slength != -1){ + byte[] bytes = new byte[slength]; + buffer.readFully(bytes); + return new String(bytes, charset); + }else{ + return null; + } + } +} diff --git a/core/src/io/anuke/mindustry/io/Version.java b/core/src/io/anuke/mindustry/io/Version.java deleted file mode 100644 index 6328d20ba6..0000000000 --- a/core/src/io/anuke/mindustry/io/Version.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.anuke.mindustry.io; - -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.files.FileHandle; -import com.badlogic.gdx.utils.ObjectMap; -import com.badlogic.gdx.utils.PropertiesUtils; -import io.anuke.ucore.util.Strings; - -import java.io.IOException; - -public class Version { - public static final String name; - public static final String type; - public static final String code; - public static final int build; - public static final String buildName; - - static{ - try { - FileHandle file = Gdx.files.internal("version.properties"); - - ObjectMap map = new ObjectMap<>(); - PropertiesUtils.load(map, file.reader()); - - name = map.get("name"); - type = map.get("version"); - code = map.get("code"); - build = Strings.canParseInt(map.get("build")) ? Integer.parseInt(map.get("build")) : -1; - buildName = build == -1 ? map.get("build") : "build " + build; - - }catch (IOException e){ - throw new RuntimeException(e); - } - } -} diff --git a/core/src/io/anuke/mindustry/io/versions/Save1.java b/core/src/io/anuke/mindustry/io/versions/Save1.java new file mode 100644 index 0000000000..f9ea40b8ce --- /dev/null +++ b/core/src/io/anuke/mindustry/io/versions/Save1.java @@ -0,0 +1,10 @@ +package io.anuke.mindustry.io.versions; + +import io.anuke.mindustry.io.SaveVersion; + +public class Save1 extends SaveVersion{ + + public Save1(){ + super(1); + } +} diff --git a/core/src/io/anuke/mindustry/io/versions/Save12.java b/core/src/io/anuke/mindustry/io/versions/Save12.java deleted file mode 100644 index bfa64ab57c..0000000000 --- a/core/src/io/anuke/mindustry/io/versions/Save12.java +++ /dev/null @@ -1,287 +0,0 @@ -package io.anuke.mindustry.io.versions; - -import com.badlogic.gdx.utils.Array; -import com.badlogic.gdx.utils.TimeUtils; -import io.anuke.mindustry.Vars; -import io.anuke.mindustry.entities.enemies.Enemy; -import io.anuke.mindustry.entities.enemies.EnemyType; -import io.anuke.mindustry.game.GameMode; -import io.anuke.mindustry.io.BlockLoader; -import io.anuke.mindustry.io.SaveFileVersion; -import io.anuke.mindustry.resource.Item; -import io.anuke.mindustry.resource.Upgrade; -import io.anuke.mindustry.resource.Weapon; -import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.blocks.Blocks; -import io.anuke.ucore.core.Core; -import io.anuke.ucore.entities.Entities; -import io.anuke.ucore.entities.EntityGroup.EntityContainer; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.util.Arrays; - -import static io.anuke.mindustry.Vars.*; - -public class Save12 extends SaveFileVersion { - - public Save12(){ - super(12); - } - - @Override - public void read(DataInputStream stream) throws IOException { - /*long loadTime = */stream.readLong(); - - //general state - byte mode = stream.readByte(); - byte mapid = stream.readByte(); - - int wave = stream.readInt(); - float wavetime = stream.readFloat(); - - float playerx = stream.readFloat(); - float playery = stream.readFloat(); - - int playerhealth = stream.readInt(); - - Vars.player.x = playerx; - Vars.player.y = playery; - Vars.player.health = playerhealth; - state.mode = GameMode.values()[mode]; - Core.camera.position.set(playerx, playery, 0); - - //weapons - - control.upgrades().getWeapons().clear(); - control.upgrades().getWeapons().add(Weapon.blaster); - Vars.player.weaponLeft = Vars.player.weaponRight = Weapon.blaster; - - int weapons = stream.readByte(); - - for(int i = 0; i < weapons; i ++){ - control.upgrades().addWeapon((Weapon)Upgrade.getByID(stream.readByte())); - } - - ui.hudfrag.updateWeapons(); - - //inventory - - int totalItems = stream.readByte(); - - Arrays.fill(state.inventory.getItems(), 0); - - for(int i = 0; i < totalItems; i ++){ - Item item = Item.getByID(stream.readByte()); - int amount = stream.readInt(); - state.inventory.getItems()[item.id] = amount; - } - - ui.hudfrag.updateItems(); - - //enemies - - Entities.clear(); - - int enemies = stream.readInt(); - - Array enemiesToUpdate = new Array<>(); - - for(int i = 0; i < enemies; i ++){ - byte type = stream.readByte(); - int lane = stream.readByte(); - float x = stream.readFloat(); - float y = stream.readFloat(); - byte tier = stream.readByte(); - int health = stream.readInt(); - - try{ - Enemy enemy = new Enemy(EnemyType.getByID(type)); - enemy.lane = lane; - enemy.health = health; - enemy.x = x; - enemy.y = y; - enemy.tier = tier; - enemy.add(enemyGroup); - enemiesToUpdate.add(enemy); - }catch (Exception e){ - throw new RuntimeException(e); - } - } - - state.enemies = enemies; - state.wave = wave; - state.wavetime = wavetime; - - if(!mobile) - Vars.player.add(); - - //map - - int seed = stream.readInt(); - int tiles = stream.readInt(); - - world.loadMap(world.maps().getMap(mapid), seed); - renderer.clearTiles(); - - for(Enemy enemy : enemiesToUpdate){ - enemy.node = -2; - } - - for(int x = 0; x < world.width(); x ++){ - for(int y = 0; y < world.height(); y ++){ - Tile tile = world.tile(x, y); - - //remove breakables like rocks - if(tile.breakable()){ - world.tile(x, y).setBlock(Blocks.air); - } - } - } - - for(int i = 0; i < tiles; i ++){ - int pos = stream.readInt(); - byte link = stream.readByte(); - boolean hasEntity = stream.readBoolean(); - int blockid = stream.readInt(); - - Tile tile = world.tile(pos % world.width(), pos / world.width()); - tile.setBlock(BlockLoader.getByOldID(blockid)); - tile.link = link; - - if(hasEntity){ - byte rotation = stream.readByte(); - int health = stream.readInt(); - int items = stream.readByte(); - - tile.entity.health = health; - tile.setRotation(rotation); - - for(int j = 0; j < items; j ++){ - int itemid = stream.readByte(); - int itemamount = stream.readInt(); - tile.entity.items[itemid] = itemamount; - } - - tile.entity.read(stream); - } - } - } - - @Override - public void write(DataOutputStream stream) throws IOException { - - //--META-- - stream.writeInt(version); //version id - stream.writeLong(TimeUtils.millis()); //last saved - - //--GENERAL STATE-- - stream.writeByte(state.mode.ordinal()); //gamemode - stream.writeByte(world.getMap().id); //map ID - - stream.writeInt(state.wave); //wave - stream.writeFloat(state.wavetime); //wave countdown - - stream.writeFloat(Vars.player.x); //player x/y - stream.writeFloat(Vars.player.y); - - stream.writeInt((int)Vars.player.health); //player health - - stream.writeByte(control.upgrades().getWeapons().size - 1); //amount of weapons - - //start at 1, because the first weapon is always the starter - ignore that - for(int i = 1; i < control.upgrades().getWeapons().size; i ++){ - stream.writeByte(control.upgrades().getWeapons().get(i).id); //weapon ordinal - } - - //--INVENTORY-- - - int l = state.inventory.getItems().length; - int itemsize = 0; - - for(int i = 0; i < l; i ++){ - if(state.inventory.getItems()[i] > 0){ - itemsize ++; - } - } - - stream.writeByte(itemsize); //amount of items - - for(int i = 0; i < l; i ++){ - if(state.inventory.getItems()[i] > 0){ - stream.writeByte(i); //item ID - stream.writeInt(state.inventory.getItems()[i]); //item amount - } - } - - //--ENEMIES-- - - EntityContainer enemies = enemyGroup.all(); - - stream.writeInt(enemies.size()); //enemy amount - - for(int i = 0; i < enemies.size(); i ++){ - Enemy enemy = enemies.get(i); - stream.writeByte(enemy.type.id); //type - stream.writeByte(enemy.lane); //lane - stream.writeFloat(enemy.x); //x - stream.writeFloat(enemy.y); //y - stream.writeByte(enemy.tier); //tier - stream.writeInt((int)enemy.health); //health - } - - //--MAP DATA-- - - //seed - stream.writeInt(world.getSeed()); - - int totalblocks = 0; - - for(int x = 0; x < world.width(); x ++){ - for(int y = 0; y < world.height(); y ++){ - Tile tile = world.tile(x, y); - - if(tile.breakable()){ - totalblocks ++; - } - } - } - - //tile amount - stream.writeInt(totalblocks); - - for(int x = 0; x < world.width(); x ++){ - for(int y = 0; y < world.height(); y ++){ - Tile tile = world.tile(x, y); - - if(tile.breakable()){ - - stream.writeInt(x + y*world.width()); //tile pos - stream.writeByte(tile.link); - stream.writeBoolean(tile.entity != null); //whether it has a tile entity - stream.writeInt(tile.block().id); //block ID - - if(tile.entity != null){ - stream.writeByte(tile.getRotation()); //rotation - stream.writeInt((int)tile.entity.health); //health - int amount = 0; - for(int i = 0; i < tile.entity.items.length; i ++){ - if(tile.entity.items[i] > 0) amount ++; - } - stream.writeByte(amount); //amount of items - - for(int i = 0; i < tile.entity.items.length; i ++){ - if(tile.entity.items[i] > 0){ - stream.writeByte(i); //item ID - stream.writeInt(tile.entity.items[i]); //item amount - } - } - - tile.entity.write(stream); - } - } - } - } - } -} diff --git a/core/src/io/anuke/mindustry/io/versions/Save13.java b/core/src/io/anuke/mindustry/io/versions/Save13.java deleted file mode 100644 index 2d8d6faf8d..0000000000 --- a/core/src/io/anuke/mindustry/io/versions/Save13.java +++ /dev/null @@ -1,320 +0,0 @@ -package io.anuke.mindustry.io.versions; - -import com.badlogic.gdx.utils.Array; -import com.badlogic.gdx.utils.TimeUtils; -import io.anuke.mindustry.Vars; -import io.anuke.mindustry.entities.enemies.Enemy; -import io.anuke.mindustry.entities.enemies.EnemyType; -import io.anuke.mindustry.game.GameMode; -import io.anuke.mindustry.io.BlockLoader; -import io.anuke.mindustry.io.SaveFileVersion; -import io.anuke.mindustry.resource.Item; -import io.anuke.mindustry.resource.Upgrade; -import io.anuke.mindustry.resource.Weapon; -import io.anuke.mindustry.world.Block; -import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.WorldGenerator; -import io.anuke.mindustry.world.blocks.Blocks; -import io.anuke.mindustry.world.blocks.types.BlockPart; -import io.anuke.mindustry.world.blocks.types.Rock; -import io.anuke.ucore.core.Core; -import io.anuke.ucore.entities.Entities; -import io.anuke.ucore.entities.EntityGroup.EntityContainer; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.util.Arrays; - -import static io.anuke.mindustry.Vars.*; - -public class Save13 extends SaveFileVersion { - - public Save13(){ - super(13); - } - - @Override - public void read(DataInputStream stream) throws IOException { - /*long loadTime = */stream.readLong(); - - //general state - byte mode = stream.readByte(); - byte mapid = stream.readByte(); - - int wave = stream.readInt(); - float wavetime = stream.readFloat(); - - float playerx = stream.readFloat(); - float playery = stream.readFloat(); - - int playerhealth = stream.readInt(); - - Vars.player.x = playerx; - Vars.player.y = playery; - Vars.player.health = playerhealth; - state.mode = GameMode.values()[mode]; - Core.camera.position.set(playerx, playery, 0); - - //weapons - - control.upgrades().getWeapons().clear(); - control.upgrades().getWeapons().add(Weapon.blaster); - Vars.player.weaponLeft = Vars.player.weaponRight = Weapon.blaster; - - int weapons = stream.readByte(); - - for(int i = 0; i < weapons; i ++){ - control.upgrades().addWeapon((Weapon) Upgrade.getByID(stream.readByte())); - } - - ui.hudfrag.updateWeapons(); - - //inventory - - int totalItems = stream.readByte(); - - Arrays.fill(state.inventory.getItems(), 0); - - for(int i = 0; i < totalItems; i ++){ - Item item = Item.getByID(stream.readByte()); - int amount = stream.readInt(); - state.inventory.getItems()[item.id] = amount; - } - - ui.hudfrag.updateItems(); - - //enemies - - Entities.clear(); - - int enemies = stream.readInt(); - - Array enemiesToUpdate = new Array<>(); - - for(int i = 0; i < enemies; i ++){ - byte type = stream.readByte(); - int lane = stream.readByte(); - float x = stream.readFloat(); - float y = stream.readFloat(); - byte tier = stream.readByte(); - int health = stream.readShort(); - - try{ - Enemy enemy = new Enemy(EnemyType.getByID(type)); - enemy.lane = lane; - enemy.health = health; - enemy.x = x; - enemy.y = y; - enemy.tier = tier; - enemy.add(enemyGroup); - enemiesToUpdate.add(enemy); - }catch (Exception e){ - throw new RuntimeException(e); - } - } - - state.enemies = enemies; - state.wave = wave; - state.wavetime = wavetime; - - if(!mobile) - Vars.player.add(); - - //map - - int seed = stream.readInt(); - - world.loadMap(world.maps().getMap(mapid), seed); - renderer.clearTiles(); - - for(Enemy enemy : enemiesToUpdate){ - enemy.node = -2; - } - - int rocks = stream.readInt(); - - for(int x = 0; x < world.width(); x ++){ - for(int y = 0; y < world.height(); y ++){ - Tile tile = world.tile(x, y); - - //remove breakables like rocks - if(tile.breakable()){ - world.tile(x, y).setBlock(Blocks.air); - } - } - } - - for(int i = 0; i < rocks; i ++){ - int pos = stream.readInt(); - Tile tile = world.tile(pos % world.width(), pos / world.width()); - Block result = WorldGenerator.rocks.get(tile.floor()); - if(result != null) tile.setBlock(result); - } - - int tiles = stream.readInt(); - - for(int i = 0; i < tiles; i ++){ - int pos = stream.readInt(); - int blockid = stream.readInt(); - - Tile tile = world.tile(pos % world.width(), pos / world.width()); - tile.setBlock(BlockLoader.getByOldID(blockid)); - - if(blockid == Blocks.blockpart.id){ - tile.link = stream.readByte(); - } - - if(tile.entity != null){ - byte rotation = stream.readByte(); - short health = stream.readShort(); - int items = stream.readByte(); - - tile.entity.health = health; - tile.setRotation(rotation); - - for(int j = 0; j < items; j ++){ - int itemid = stream.readByte(); - int itemamount = stream.readInt(); - tile.entity.items[itemid] = itemamount; - } - - tile.entity.read(stream); - } - } - } - - @Override - public void write(DataOutputStream stream) throws IOException { - //--META-- - stream.writeInt(version); //version id - stream.writeLong(TimeUtils.millis()); //last saved - - //--GENERAL STATE-- - stream.writeByte(state.mode.ordinal()); //gamemode - stream.writeByte(world.getMap().id); //map ID - - stream.writeInt(state.wave); //wave - stream.writeFloat(state.wavetime); //wave countdown - - stream.writeFloat(Vars.player.x); //player x/y - stream.writeFloat(Vars.player.y); - - stream.writeInt((int)Vars.player.health); //player health - - stream.writeByte(control.upgrades().getWeapons().size - 1); //amount of weapons - - //start at 1, because the first weapon is always the starter - ignore that - for(int i = 1; i < control.upgrades().getWeapons().size; i ++){ - stream.writeByte(control.upgrades().getWeapons().get(i).id); //weapon ordinal - } - - //--INVENTORY-- - - int l = state.inventory.getItems().length; - int itemsize = 0; - - for(int i = 0; i < l; i ++){ - if(state.inventory.getItems()[i] > 0){ - itemsize ++; - } - } - - stream.writeByte(itemsize); //amount of items - - for(int i = 0; i < l; i ++){ - if(state.inventory.getItems()[i] > 0){ - stream.writeByte(i); //item ID - stream.writeInt(state.inventory.getItems()[i]); //item amount - } - } - - //--ENEMIES-- - EntityContainer enemies = enemyGroup.all(); - - stream.writeInt(enemies.size()); //enemy amount - - for(int i = 0; i < enemies.size(); i ++){ - Enemy enemy = enemies.get(i); - stream.writeByte(enemy.type.id); //type - stream.writeByte(enemy.lane); //lane - stream.writeFloat(enemy.x); //x - stream.writeFloat(enemy.y); //y - stream.writeByte(enemy.tier); //tier - stream.writeShort((short)enemy.health); //health - } - - //--MAP DATA-- - - //seed - stream.writeInt(world.getSeed()); - - int totalblocks = 0; - int totalrocks = 0; - - for(int x = 0; x < world.width(); x ++){ - for(int y = 0; y < world.height(); y ++){ - Tile tile = world.tile(x, y); - - if(tile.breakable()){ - if(tile.block() instanceof Rock){ - totalrocks ++; - }else{ - totalblocks ++; - } - } - } - } - - //amount of rocks - stream.writeInt(totalrocks); - - //write all rocks - for(int x = 0; x < world.width(); x ++) { - for (int y = 0; y < world.height(); y++) { - Tile tile = world.tile(x, y); - - if (tile.block() instanceof Rock) { - stream.writeInt(tile.packedPosition()); - } - } - } - - //write all blocks - stream.writeInt(totalblocks); - - for(int x = 0; x < world.width(); x ++){ - for(int y = 0; y < world.height(); y ++){ - Tile tile = world.tile(x, y); - - if(tile.breakable() && !(tile.block() instanceof Rock)){ - - stream.writeInt(x + y*world.width()); //tile pos - stream.writeInt(tile.block().id); //block ID - - if(tile.block() instanceof BlockPart) stream.writeByte(tile.link); - - if(tile.entity != null){ - stream.writeByte(tile.getRotation()); //rotation - stream.writeShort((short)tile.entity.health); //health - byte amount = 0; - for(int i = 0; i < tile.entity.items.length; i ++){ - if(tile.entity.items[i] > 0) amount ++; - } - stream.writeByte(amount); //amount of items - - for(int i = 0; i < tile.entity.items.length; i ++){ - if(tile.entity.items[i] > 0){ - stream.writeByte(i); //item ID - stream.writeInt(tile.entity.items[i]); //item amount - } - } - - tile.entity.write(stream); - } - } - } - } - } - -} diff --git a/core/src/io/anuke/mindustry/io/versions/Save14.java b/core/src/io/anuke/mindustry/io/versions/Save14.java deleted file mode 100644 index 789fac77d1..0000000000 --- a/core/src/io/anuke/mindustry/io/versions/Save14.java +++ /dev/null @@ -1,345 +0,0 @@ -package io.anuke.mindustry.io.versions; - -import com.badlogic.gdx.utils.Array; -import com.badlogic.gdx.utils.IntMap; -import com.badlogic.gdx.utils.TimeUtils; -import io.anuke.mindustry.Vars; -import io.anuke.mindustry.entities.enemies.Enemy; -import io.anuke.mindustry.entities.enemies.EnemyType; -import io.anuke.mindustry.game.GameMode; -import io.anuke.mindustry.io.SaveFileVersion; -import io.anuke.mindustry.resource.Item; -import io.anuke.mindustry.resource.Upgrade; -import io.anuke.mindustry.resource.Weapon; -import io.anuke.mindustry.world.Block; -import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.WorldGenerator; -import io.anuke.mindustry.world.blocks.Blocks; -import io.anuke.mindustry.world.blocks.types.BlockPart; -import io.anuke.mindustry.world.blocks.types.Rock; -import io.anuke.ucore.core.Core; -import io.anuke.ucore.entities.Entities; -import io.anuke.ucore.entities.EntityGroup.EntityContainer; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.util.Arrays; - -import static io.anuke.mindustry.Vars.*; - -public class Save14 extends SaveFileVersion{ - - public Save14(){ - super(14); - } - - @Override - public void read(DataInputStream stream) throws IOException { - /*long loadTime = */ - stream.readLong(); - - //general state - byte mode = stream.readByte(); - byte mapid = stream.readByte(); - - int wave = stream.readInt(); - float wavetime = stream.readFloat(); - - //block header - - int blocksize = stream.readInt(); - - IntMap map = new IntMap<>(); - - for(int i = 0; i < blocksize; i ++){ - String name = readString(stream); - int id = stream.readShort(); - - map.put(id, Block.getByName(name)); - } - - float playerx = stream.readFloat(); - float playery = stream.readFloat(); - - int playerhealth = stream.readInt(); - - Vars.player.x = playerx; - Vars.player.y = playery; - Vars.player.health = playerhealth; - state.mode = GameMode.values()[mode]; - Core.camera.position.set(playerx, playery, 0); - - //weapons - - control.upgrades().getWeapons().clear(); - control.upgrades().getWeapons().add(Weapon.blaster); - Vars.player.weaponLeft = Vars.player.weaponRight = Weapon.blaster; - - int weapons = stream.readByte(); - - for(int i = 0; i < weapons; i ++){ - control.upgrades().addWeapon((Weapon) Upgrade.getByID(stream.readByte())); - } - - ui.hudfrag.updateWeapons(); - - //inventory - - int totalItems = stream.readByte(); - - Arrays.fill(state.inventory.getItems(), 0); - - for(int i = 0; i < totalItems; i ++){ - Item item = Item.getByID(stream.readByte()); - int amount = stream.readInt(); - state.inventory.getItems()[item.id] = amount; - } - - ui.hudfrag.updateItems(); - - //enemies - - Entities.clear(); - - int enemies = stream.readInt(); - - Array enemiesToUpdate = new Array<>(); - - for(int i = 0; i < enemies; i ++){ - byte type = stream.readByte(); - int lane = stream.readByte(); - float x = stream.readFloat(); - float y = stream.readFloat(); - byte tier = stream.readByte(); - int health = stream.readShort(); - - try{ - Enemy enemy = new Enemy(EnemyType.getByID(type)); - enemy.lane = lane; - enemy.health = health; - enemy.x = x; - enemy.y = y; - enemy.tier = tier; - enemy.add(enemyGroup); - enemiesToUpdate.add(enemy); - }catch (Exception e){ - throw new RuntimeException(e); - } - } - - state.enemies = enemies; - state.wave = wave; - state.wavetime = wavetime; - - if(!mobile) - Vars.player.add(); - - //map - - int seed = stream.readInt(); - - world.loadMap(world.maps().getMap(mapid), seed); - renderer.clearTiles(); - - for(Enemy enemy : enemiesToUpdate){ - enemy.node = -2; - } - - int rocks = stream.readInt(); - - for(int x = 0; x < world.width(); x ++){ - for(int y = 0; y < world.height(); y ++){ - Tile tile = world.tile(x, y); - - //remove breakables like rocks - if(tile.breakable()){ - world.tile(x, y).setBlock(Blocks.air); - } - } - } - - for(int i = 0; i < rocks; i ++){ - int pos = stream.readInt(); - Tile tile = world.tile(pos % world.width(), pos / world.width()); - if(tile == null) continue; - Block result = WorldGenerator.rocks.get(tile.floor()); - if(result != null) tile.setBlock(result); - } - - int tiles = stream.readInt(); - - for(int i = 0; i < tiles; i ++){ - int pos = stream.readInt(); - int blockid = stream.readInt(); - - Tile tile = world.tile(pos % world.width(), pos / world.width()); - tile.setBlock(map.get(blockid)); - - if(blockid == Blocks.blockpart.id){ - tile.link = stream.readByte(); - } - - if(tile.entity != null){ - byte rotation = stream.readByte(); - short health = stream.readShort(); - int items = stream.readByte(); - - tile.entity.health = health; - tile.setRotation(rotation); - - for(int j = 0; j < items; j ++){ - int itemid = stream.readByte(); - int itemamount = stream.readInt(); - tile.entity.items[itemid] = itemamount; - } - - tile.entity.read(stream); - } - } - } - - @Override - public void write(DataOutputStream stream) throws IOException { - //--META-- - stream.writeInt(version); //version id - stream.writeLong(TimeUtils.millis()); //last saved - - //--GENERAL STATE-- - stream.writeByte(state.mode.ordinal()); //gamemode - stream.writeByte(world.getMap().id); //map ID - - stream.writeInt(state.wave); //wave - stream.writeFloat(state.wavetime); //wave countdown - - //--BLOCK HEADER-- - - stream.writeInt(Block.getAllBlocks().size); - - for(int i = 0; i < Block.getAllBlocks().size; i ++){ - Block block = Block.getAllBlocks().get(i); - writeString(stream, block.name); - stream.writeShort(block.id); - } - - stream.writeFloat(Vars.player.x); //player x/y - stream.writeFloat(Vars.player.y); - - stream.writeInt((int)Vars.player.health); //player health - - stream.writeByte(control.upgrades().getWeapons().size - 1); //amount of weapons - - //start at 1, because the first weapon is always the starter - ignore that - for(int i = 1; i < control.upgrades().getWeapons().size; i ++){ - stream.writeByte(control.upgrades().getWeapons().get(i).id); //weapon ordinal - } - - //--INVENTORY-- - - int l = state.inventory.getItems().length; - int itemsize = 0; - - for(int i = 0; i < l; i ++){ - if(state.inventory.getItems()[i] > 0){ - itemsize ++; - } - } - - stream.writeByte(itemsize); //amount of items - - for(int i = 0; i < l; i ++){ - if(state.inventory.getItems()[i] > 0){ - stream.writeByte(i); //item ID - stream.writeInt(state.inventory.getItems()[i]); //item amount - } - } - - //--ENEMIES-- - - EntityContainer enemies = enemyGroup.all(); - - stream.writeInt(enemies.size()); //enemy amount - - for(int i = 0; i < enemies.size(); i ++){ - Enemy enemy = enemies.get(i); - stream.writeByte(enemy.type.id); //type - stream.writeByte(enemy.lane); //lane - stream.writeFloat(enemy.x); //x - stream.writeFloat(enemy.y); //y - stream.writeByte(enemy.tier); //tier - stream.writeShort((short)enemy.health); //health - } - - //--MAP DATA-- - - //seed - stream.writeInt(world.getSeed()); - - int totalblocks = 0; - int totalrocks = 0; - - for(int x = 0; x < world.width(); x ++){ - for(int y = 0; y < world.height(); y ++){ - Tile tile = world.tile(x, y); - - if(tile.breakable()){ - if(tile.block() instanceof Rock){ - totalrocks ++; - }else{ - totalblocks ++; - } - } - } - } - - //amount of rocks - stream.writeInt(totalrocks); - - //write all rocks - for(int x = 0; x < world.width(); x ++) { - for (int y = 0; y < world.height(); y++) { - Tile tile = world.tile(x, y); - - if (tile.block() instanceof Rock) { - stream.writeInt(tile.packedPosition()); - } - } - } - - //write all blocks - stream.writeInt(totalblocks); - - for(int x = 0; x < world.width(); x ++){ - for(int y = 0; y < world.height(); y ++){ - Tile tile = world.tile(x, y); - - if(tile.breakable() && !(tile.block() instanceof Rock)){ - - stream.writeInt(x + y*world.width()); //tile pos - stream.writeInt(tile.block().id); //block ID - - if(tile.block() instanceof BlockPart) stream.writeByte(tile.link); - - if(tile.entity != null){ - stream.writeByte(tile.getRotation()); //rotation - stream.writeShort((short)tile.entity.health); //health - byte amount = 0; - for(int i = 0; i < tile.entity.items.length; i ++){ - if(tile.entity.items[i] > 0) amount ++; - } - stream.writeByte(amount); //amount of items - - for(int i = 0; i < tile.entity.items.length; i ++){ - if(tile.entity.items[i] > 0){ - stream.writeByte(i); //item ID - stream.writeInt(tile.entity.items[i]); //item amount - } - } - - tile.entity.write(stream); - } - } - } - } - } -} diff --git a/core/src/io/anuke/mindustry/io/versions/Save15.java b/core/src/io/anuke/mindustry/io/versions/Save15.java deleted file mode 100644 index e93d8acf8b..0000000000 --- a/core/src/io/anuke/mindustry/io/versions/Save15.java +++ /dev/null @@ -1,370 +0,0 @@ -package io.anuke.mindustry.io.versions; - -import com.badlogic.gdx.utils.Array; -import com.badlogic.gdx.utils.IntMap; -import com.badlogic.gdx.utils.TimeUtils; -import io.anuke.mindustry.entities.enemies.Enemy; -import io.anuke.mindustry.entities.enemies.EnemyType; -import io.anuke.mindustry.game.Difficulty; -import io.anuke.mindustry.game.GameMode; -import io.anuke.mindustry.io.SaveFileVersion; -import io.anuke.mindustry.io.SaveMeta; -import io.anuke.mindustry.resource.Item; -import io.anuke.mindustry.resource.Upgrade; -import io.anuke.mindustry.resource.Weapon; -import io.anuke.mindustry.world.Block; -import io.anuke.mindustry.world.Tile; -import io.anuke.mindustry.world.WorldGenerator; -import io.anuke.mindustry.world.blocks.Blocks; -import io.anuke.mindustry.world.blocks.types.BlockPart; -import io.anuke.mindustry.world.blocks.types.Rock; -import io.anuke.ucore.core.Core; -import io.anuke.ucore.entities.EntityGroup.EntityContainer; - -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; -import java.util.Arrays; - -import static io.anuke.mindustry.Vars.*; - -public class Save15 extends SaveFileVersion { - - public Save15(){ - super(15); - } - - public SaveMeta getData(DataInputStream stream) throws IOException{ - long time = stream.readLong(); //read last saved time - byte mode = stream.readByte(); //read the gamemode - byte map = stream.readByte(); //read the map - int wave = stream.readInt(); //read the wave - stream.readFloat(); //wave time - byte difficulty = stream.readByte(); - return new SaveMeta(version, time, mode, map, wave, Difficulty.values()[difficulty]); - } - - @Override - public void read(DataInputStream stream) throws IOException { - /*long loadTime = */ - stream.readLong(); - - //general state - byte mode = stream.readByte(); - byte mapid = stream.readByte(); - - int wave = stream.readInt(); - float wavetime = stream.readFloat(); - byte difficulty = stream.readByte(); - - state.difficulty = Difficulty.values()[difficulty]; - - //block header - - int blocksize = stream.readInt(); - - IntMap map = new IntMap<>(); - - for(int i = 0; i < blocksize; i ++){ - String name = readString(stream); - int id = stream.readShort(); - - map.put(id, Block.getByName(name)); - } - - float playerx = stream.readFloat(); - float playery = stream.readFloat(); - - int playerhealth = stream.readInt(); - - if(!headless) { - player.x = playerx; - player.y = playery; - player.health = playerhealth; - state.mode = GameMode.values()[mode]; - Core.camera.position.set(playerx, playery, 0); - - //weapons - - control.upgrades().getWeapons().clear(); - control.upgrades().getWeapons().add(Weapon.blaster); - player.weaponLeft = player.weaponRight = Weapon.blaster; - - int weapons = stream.readByte(); - - for (int i = 0; i < weapons; i++) { - control.upgrades().addWeapon((Weapon) Upgrade.getByID(stream.readByte())); - } - - ui.hudfrag.updateWeapons(); - }else{ - byte b = stream.readByte(); - for(int i = 0; i < b; i ++) stream.readByte(); - } - - //inventory - - int totalItems = stream.readByte(); - - Arrays.fill(state.inventory.getItems(), 0); - - for(int i = 0; i < totalItems; i ++){ - Item item = Item.getByID(stream.readByte()); - int amount = stream.readInt(); - state.inventory.getItems()[item.id] = amount; - } - - if(!headless) ui.hudfrag.updateItems(); - - //enemies - - int enemies = stream.readInt(); - - Array enemiesToUpdate = new Array<>(); - - for(int i = 0; i < enemies; i ++){ - byte type = stream.readByte(); - int lane = stream.readByte(); - float x = stream.readFloat(); - float y = stream.readFloat(); - byte tier = stream.readByte(); - int health = stream.readShort(); - - try{ - Enemy enemy = new Enemy(EnemyType.getByID(type)); - enemy.lane = lane; - enemy.health = health; - enemy.x = x; - enemy.y = y; - enemy.tier = tier; - enemy.add(enemyGroup); - enemiesToUpdate.add(enemy); - }catch (Exception e){ - throw new RuntimeException(e); - } - } - - state.enemies = enemies; - state.wave = wave; - state.wavetime = wavetime; - - if(!mobile && !headless) - player.add(); - - //map - - int seed = stream.readInt(); - - world.loadMap(world.maps().getMap(mapid), seed); - if(!headless) renderer.clearTiles(); - - for(Enemy enemy : enemiesToUpdate){ - enemy.node = -2; - } - - int rocks = stream.readInt(); - - for(int x = 0; x < world.width(); x ++){ - for(int y = 0; y < world.height(); y ++){ - Tile tile = world.tile(x, y); - - //remove breakables like rocks - if(tile.breakable()){ - world.tile(x, y).setBlock(Blocks.air); - } - } - } - - for(int i = 0; i < rocks; i ++){ - int pos = stream.readInt(); - Tile tile = world.tile(pos % world.width(), pos / world.width()); - if(tile == null) continue; - Block result = WorldGenerator.rocks.get(tile.floor()); - if(result != null) tile.setBlock(result); - } - - int tiles = stream.readInt(); - - for(int i = 0; i < tiles; i ++){ - int pos = stream.readInt(); - int blockid = stream.readInt(); - - Tile tile = world.tile(pos % world.width(), pos / world.width()); - - tile.setBlock(map.get(blockid)); - - if(blockid == Blocks.blockpart.id){ - tile.link = stream.readByte(); - } - - if(tile.entity != null){ - byte rotation = stream.readByte(); - short health = stream.readShort(); - int items = stream.readByte(); - - tile.entity.health = health; - tile.setRotation(rotation); - - for(int j = 0; j < items; j ++){ - int itemid = stream.readByte(); - int itemamount = stream.readInt(); - tile.entity.items[itemid] = itemamount; - } - - tile.entity.read(stream); - } - } - } - - @Override - public void write(DataOutputStream stream) throws IOException { - //--META-- - stream.writeInt(version); //version id - stream.writeLong(TimeUtils.millis()); //last saved - - //--GENERAL STATE-- - stream.writeByte(state.mode.ordinal()); //gamemode - stream.writeByte(world.getMap().id); //map ID - - stream.writeInt(state.wave); //wave - stream.writeFloat(state.wavetime); //wave countdown - stream.writeByte(state.difficulty.ordinal()); - - //--BLOCK HEADER-- - - stream.writeInt(Block.getAllBlocks().size); - - for(int i = 0; i < Block.getAllBlocks().size; i ++){ - Block block = Block.getAllBlocks().get(i); - writeString(stream, block.name); - stream.writeShort(block.id); - } - - if(!headless) { - stream.writeFloat(player.x); //player x/y - stream.writeFloat(player.y); - - stream.writeInt((int)player.health); //player health - - stream.writeByte(control.upgrades().getWeapons().size - 1); //amount of weapons - - //start at 1, because the first weapon is always the starter - ignore that - for (int i = 1; i < control.upgrades().getWeapons().size; i++) { - stream.writeByte(control.upgrades().getWeapons().get(i).id); //weapon ordinal - } - }else{ - stream.writeFloat(world.getSpawnX()); - stream.writeFloat(world.getSpawnY()); - stream.writeInt(150); - stream.writeByte(0); - } - - //--INVENTORY-- - - int l = state.inventory.getItems().length; - int itemsize = 0; - - for(int i = 0; i < l; i ++){ - if(state.inventory.getItems()[i] > 0){ - itemsize ++; - } - } - - stream.writeByte(itemsize); //amount of items - - for(int i = 0; i < l; i ++){ - if(state.inventory.getItems()[i] > 0){ - stream.writeByte(i); //item ID - stream.writeInt(state.inventory.getItems()[i]); //item amount - } - } - - //--ENEMIES-- - - EntityContainer enemies = enemyGroup.all(); - - stream.writeInt(enemies.size()); //enemy amount - - for(int i = 0; i < enemies.size(); i ++){ - Enemy enemy = enemies.get(i); - stream.writeByte(enemy.type.id); //type - stream.writeByte(enemy.lane); //lane - stream.writeFloat(enemy.x); //x - stream.writeFloat(enemy.y); //y - stream.writeByte(enemy.tier); //tier - stream.writeShort((short)enemy.health); //health - } - - //--MAP DATA-- - - //seed - stream.writeInt(world.getSeed()); - - int totalblocks = 0; - int totalrocks = 0; - - for(int x = 0; x < world.width(); x ++){ - for(int y = 0; y < world.height(); y ++){ - Tile tile = world.tile(x, y); - - if(tile != null && tile.breakable()){ - if(tile.block() instanceof Rock){ - totalrocks ++; - }else{ - totalblocks ++; - } - } - } - } - - //amount of rocks - stream.writeInt(totalrocks); - - //write all rocks - for(int x = 0; x < world.width(); x ++) { - for (int y = 0; y < world.height(); y++) { - Tile tile = world.tile(x, y); - - if (tile != null && tile.block() instanceof Rock) { - stream.writeInt(tile.packedPosition()); - } - } - } - - //write all blocks - stream.writeInt(totalblocks); - - for(int x = 0; x < world.width(); x ++){ - for(int y = 0; y < world.height(); y ++){ - Tile tile = world.tile(x, y); - - if(tile != null && tile.breakable() && !(tile.block() instanceof Rock)){ - - stream.writeInt(x + y*world.width()); //tile pos - stream.writeInt(tile.block().id); //block ID - - if(tile.block() instanceof BlockPart) stream.writeByte(tile.link); - - if(tile.entity != null){ - stream.writeByte(tile.getRotation()); //rotation - stream.writeShort((short)tile.entity.health); //health - byte amount = 0; - for(int i = 0; i < tile.entity.items.length; i ++){ - if(tile.entity.items[i] > 0) amount ++; - } - stream.writeByte(amount); //amount of items - - for(int i = 0; i < tile.entity.items.length; i ++){ - if(tile.entity.items[i] > 0){ - stream.writeByte(i); //item ID - stream.writeInt(tile.entity.items[i]); //item amount - } - } - - tile.entity.write(stream); - } - } - } - } - } -} diff --git a/core/src/io/anuke/mindustry/mapeditor/DrawOperation.java b/core/src/io/anuke/mindustry/mapeditor/DrawOperation.java deleted file mode 100755 index 5884250cd5..0000000000 --- a/core/src/io/anuke/mindustry/mapeditor/DrawOperation.java +++ /dev/null @@ -1,40 +0,0 @@ -package io.anuke.mindustry.mapeditor; - -import com.badlogic.gdx.graphics.Pixmap; -import com.badlogic.gdx.utils.Disposable; -import io.anuke.ucore.graphics.Pixmaps; - -public class DrawOperation implements Disposable{ - Pixmap from, to, pixmap; - - public DrawOperation(Pixmap pixmap){ - this.pixmap = pixmap; - } - - public void add(Pixmap from, Pixmap to) { - this.from = from; - this.to = to; - } - - public void undo() { - if(from != null) pixmap.drawPixmap(from, 0, 0); - } - - public void redo() { - if(to != null) pixmap.drawPixmap(to, 0, 0); - } - - @Override - public void dispose() { - if(!Pixmaps.isDisposed(from)) - from.dispose(); - - if(!Pixmaps.isDisposed(to)) - to.dispose(); - } - - public void disposeFrom(){ - if(from != null) from.dispose(); - } - -} diff --git a/core/src/io/anuke/mindustry/mapeditor/EditorTool.java b/core/src/io/anuke/mindustry/mapeditor/EditorTool.java deleted file mode 100644 index 561a9abf42..0000000000 --- a/core/src/io/anuke/mindustry/mapeditor/EditorTool.java +++ /dev/null @@ -1,87 +0,0 @@ -package io.anuke.mindustry.mapeditor; - -import com.badlogic.gdx.graphics.Pixmap; -import com.badlogic.gdx.math.GridPoint2; -import com.badlogic.gdx.utils.IntSet; -import static io.anuke.mindustry.Vars.*; -import io.anuke.mindustry.world.Block; -import io.anuke.mindustry.world.ColorMapper; -import io.anuke.mindustry.world.ColorMapper.BlockPair; - -import java.util.Stack; - -public enum EditorTool{ - pick{ - public void touched(MapEditor editor, int x, int y){ - BlockPair pair = ColorMapper.get(editor.pixmap().getPixel(x, y)); - if(pair == null) return; - Block block = pair.dominant(); - editor.setDrawBlock(block); - ui.editor.updateSelectedBlock(); - } - }, - pencil{ - { - edit = true; - } - - public void touched(MapEditor editor, int x, int y){ - editor.draw(x, y); - } - }, - line{ - { - - } - - }, - fill{ - { - edit = true; - } - - public void touched(MapEditor editor, int x, int y){ - Pixmap pix = editor.pixmap(); - - int dest = pix.getPixel(x, y); - - int width = pix.getWidth(); - - IntSet set = new IntSet(); - Stack points = new Stack(); - points.add(new GridPoint2(x, y)); - - while( !points.isEmpty()){ - GridPoint2 pos = points.pop(); - set.add(asInt(pos.x, pos.y, width)); - - int pcolor = pix.getPixel(pos.x, pos.y); - if(colorEquals(pcolor, dest)){ - - pix.drawPixel(pos.x, pos.y); - - if(pos.x > 0 && !set.contains(asInt(pos.x - 1, pos.y, width))) points.add(new GridPoint2(pos).cpy().add( -1, 0)); - if(pos.y > 0 && !set.contains(asInt(pos.x, pos.y - 1, width))) points.add(new GridPoint2(pos).cpy().add(0, -1)); - if(pos.x < pix.getWidth() - 1 && !set.contains(asInt(pos.x + 1, pos.y, width))) points.add(new GridPoint2(pos).cpy().add(1, 0)); - if(pos.y < pix.getHeight() - 1 && !set.contains(asInt(pos.x, pos.y + 1, width))) points.add(new GridPoint2(pos).cpy().add(0, 1)); - } - } - - editor.updateTexture(); - } - - int asInt(int x, int y, int width){ - return x+y*width; - } - - boolean colorEquals(int a, int b){ - return a == b; - } - }, - zoom; - boolean edit; - - public void touched(MapEditor editor, int x, int y){ - - } -} diff --git a/core/src/io/anuke/mindustry/mapeditor/MapEditor.java b/core/src/io/anuke/mindustry/mapeditor/MapEditor.java deleted file mode 100644 index 7ef8f97a47..0000000000 --- a/core/src/io/anuke/mindustry/mapeditor/MapEditor.java +++ /dev/null @@ -1,201 +0,0 @@ -package io.anuke.mindustry.mapeditor; - -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.graphics.GL20; -import com.badlogic.gdx.graphics.Pixmap; -import com.badlogic.gdx.graphics.Pixmap.Format; -import com.badlogic.gdx.graphics.Texture; -import io.anuke.mindustry.world.Block; -import io.anuke.mindustry.world.ColorMapper; -import io.anuke.mindustry.world.Map; -import io.anuke.mindustry.world.blocks.Blocks; -import io.anuke.ucore.graphics.Pixmaps; - -public class MapEditor{ - public static final int[] validMapSizes = {128, 256, 512}; - public static final int[] brushSizes = {1, 2, 3, 4, 5, 9, 15}; - public static final int maxSpawnpoints = 15; - public static final Format format = Format.RGBA8888; - - private Pixmap[] brushPixmaps = new Pixmap[brushSizes.length]; - - private Map map; - - private MapFilter filter = new MapFilter(); - private Pixmap filterPixmap; - private Texture filterTexture; - - private Pixmap pixmap; - private Texture texture; - private int brushSize = 1; - private Block drawBlock = Blocks.stone; - - public MapEditor(){ - for(int i = 0; i < brushSizes.length; i ++){ - int s = brushSizes[i]; - brushPixmaps[i] = new Pixmap(s*2-1, s*2-1, format); - } - } - - public Map getMap(){ - return map; - } - - public void updateTexture(){ - texture.draw(pixmap, 0, 0); - } - - public MapFilter getFilter(){ - return filter; - } - - public void applyFilterPreview(){ - if(filterPixmap != null && (filterPixmap.getWidth() != pixmap.getWidth() - || filterPixmap.getHeight() != pixmap.getHeight())){ - filterPixmap.dispose(); - filterTexture.dispose(); - filterPixmap = null; - filterTexture = null; - } - - if(filterPixmap == null){ - filterPixmap = Pixmaps.copy(pixmap); - - filter.process(filterPixmap); - filterTexture = new Texture(filterPixmap); - }else{ - filterPixmap.drawPixmap(pixmap, 0, 0); - filter.process(filterPixmap); - filterTexture.draw(filterPixmap, 0, 0); - } - - } - - public Texture getFilterTexture(){ - return filterTexture; - } - - public void applyFilter(){ - filter.process(pixmap); - texture.draw(pixmap, 0, 0); - } - - public void beginEdit(Map map){ - drawBlock = Blocks.stone; - this.map = map; - this.brushSize = 1; - if(map.pixmap == null){ - pixmap = new Pixmap(256, 256, format); - pixmap.setColor(ColorMapper.getColor(drawBlock)); - pixmap.fill(); - texture = new Texture(pixmap); - }else{ - pixmap = map.pixmap; - texture = map.texture; - pixmap.setColor(ColorMapper.getColor(drawBlock)); - } - - } - - public Block getDrawBlock(){ - return drawBlock; - } - - public void setDrawBlock(Block block){ - this.drawBlock = block; - pixmap.setColor(ColorMapper.getColor(block)); - } - - public void setBrushSize(int size){ - this.brushSize = size; - } - - public int getBrushSize() { - return brushSize; - } - - public void draw(int dx, int dy){ - if(dx < 0 || dy < 0 || dx >= pixmap.getWidth() || dy >= pixmap.getHeight()){ - return; - } - - Gdx.gl.glBindTexture(GL20.GL_TEXTURE_2D, texture.getTextureObjectHandle()); - - int dstWidth = brushSize*2-1; - int dstHeight = brushSize*2-1; - int width = pixmap.getWidth(), height = pixmap.getHeight(); - - int x = dx - dstWidth/2; - int y = dy - dstHeight/2; - - if (x + dstWidth > width){ - x = width - dstWidth; - }else if (x < 0){ - x = 0; - } - - if (y + dstHeight > height){ - dstHeight = height - y; - }else if (y < 0){ - dstHeight += y; - y = 0; - } - - pixmap.fillCircle(dx, dy, brushSize-1); - - Pixmap dst = brush(brushSize); - dst.drawPixmap(pixmap, x, y, dstWidth, dstHeight, 0, 0, dstWidth, dstHeight); - - Gdx.gl.glTexSubImage2D(GL20.GL_TEXTURE_2D, 0, x, y, dstWidth, dstHeight, - dst.getGLFormat(), dst.getGLType(), dst.getPixels()); - } - - private Pixmap brush(int size){ - for(int i = 0; i < brushSizes.length; i ++){ - if(brushSizes[i] == size){ - return brushPixmaps[i]; - } - } - return null; - } - - public Texture texture(){ - return texture; - } - - public Pixmap pixmap(){ - return pixmap; - } - - public void setPixmap(Pixmap out){ - if(pixmap.getWidth() == out.getWidth() && pixmap.getHeight() == out.getHeight()){ - pixmap.dispose(); - pixmap = out; - texture.draw(out, 0, 0); - }else{ - pixmap.dispose(); - texture.dispose(); - pixmap = out; - texture = new Texture(out); - } - pixmap.setColor(ColorMapper.getColor(drawBlock)); - map.pixmap = pixmap; - map.texture = texture; - } - - public void resize(int width, int height){ - Pixmap out = Pixmaps.resize(pixmap, width, height, ColorMapper.getColor(Blocks.stone)); - pixmap.dispose(); - pixmap = out; - texture.dispose(); - texture = new Texture(out); - - if(filterPixmap != null){ - filterPixmap.dispose(); - filterTexture.dispose(); - filterPixmap = null; - filterTexture = null; - } - pixmap.setColor(ColorMapper.getColor(drawBlock)); - } -} diff --git a/core/src/io/anuke/mindustry/mapeditor/MapEditorDialog.java b/core/src/io/anuke/mindustry/mapeditor/MapEditorDialog.java deleted file mode 100644 index 7a5ed16c6d..0000000000 --- a/core/src/io/anuke/mindustry/mapeditor/MapEditorDialog.java +++ /dev/null @@ -1,451 +0,0 @@ -package io.anuke.mindustry.mapeditor; - -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.files.FileHandle; -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.graphics.Pixmap; -import com.badlogic.gdx.graphics.Texture; -import io.anuke.mindustry.core.Platform; -import io.anuke.mindustry.world.Block; -import io.anuke.mindustry.world.ColorMapper; -import io.anuke.mindustry.world.ColorMapper.BlockPair; -import io.anuke.mindustry.world.Map; -import io.anuke.mindustry.world.blocks.Blocks; -import io.anuke.mindustry.world.blocks.SpecialBlocks; -import io.anuke.ucore.core.Core; -import io.anuke.ucore.core.Graphics; -import io.anuke.ucore.core.Inputs; -import io.anuke.ucore.core.Timers; -import io.anuke.ucore.graphics.Draw; -import io.anuke.ucore.graphics.Pixmaps; -import io.anuke.ucore.scene.Element; -import io.anuke.ucore.scene.builders.build; -import io.anuke.ucore.scene.builders.imagebutton; -import io.anuke.ucore.scene.builders.label; -import io.anuke.ucore.scene.builders.table; -import io.anuke.ucore.scene.ui.*; -import io.anuke.ucore.scene.ui.layout.Table; -import io.anuke.ucore.util.Bundles; -import io.anuke.ucore.util.Input; -import io.anuke.ucore.util.Log; -import io.anuke.ucore.util.Strings; - -import java.util.Arrays; - -import static io.anuke.mindustry.Vars.*; - -public class MapEditorDialog extends Dialog{ - private MapEditor editor; - private MapView view; - private MapGenerateDialog dialog; - private MapLoadDialog loadDialog; - private MapSaveDialog saveDialog; - private MapResizeDialog resizeDialog; - private ScrollPane pane; - private boolean saved = false; - - private ButtonGroup blockgroup; - - public MapEditorDialog(){ - super("$text.mapeditor", "dialog"); - if(gwt) return; - - editor = new MapEditor(); - dialog = new MapGenerateDialog(editor); - view = new MapView(editor); - - loadDialog = new MapLoadDialog(map -> { - saveDialog.setFieldText(map.name); - ui.loadfrag.show(); - - Timers.run(3f, () -> { - Map copy = new Map(); - copy.name = map.name; - copy.id = -1; - copy.pixmap = Pixmaps.copy(map.pixmap); - copy.texture = new Texture(copy.pixmap); - copy.oreGen = map.oreGen; - editor.beginEdit(copy); - ui.loadfrag.hide(); - view.clearStack(); - }); - }); - - resizeDialog = new MapResizeDialog(editor, (x, y) -> { - Pixmap pix = editor.pixmap(); - if(!(pix.getWidth() == x && pix.getHeight() == y)){ - ui.loadfrag.show(); - Timers.run(10f, ()->{ - editor.resize(x, y); - view.clearStack(); - ui.loadfrag.hide(); - }); - } - }); - - saveDialog = new MapSaveDialog(name -> { - ui.loadfrag.show(); - if(verifyMap()){ - saved = true; - String before = editor.getMap().name; - editor.getMap().name = name; - Timers.run(10f, () -> { - world.maps().saveAndReload(editor.getMap(), editor.pixmap()); - loadDialog.rebuild(); - ui.loadfrag.hide(); - view.clearStack(); - - if(!name.equals(before)) { - Map map = new Map(); - map.name = editor.getMap().name; - map.oreGen = editor.getMap().oreGen; - map.pixmap = Pixmaps.copy(editor.getMap().pixmap); - map.texture = new Texture(map.pixmap); - map.custom = true; - editor.beginEdit(map); - } - }); - - }else{ - ui.loadfrag.hide(); - } - }); - - setFillParent(true); - - clearChildren(); - margin(0); - build.begin(this); - build(); - build.end(); - - tapped(() -> { - Element e = Core.scene.hit(Graphics.mouse().x, Graphics.mouse().y, true); - if(e == null || !e.isDescendantOf(pane)) Core.scene.setScrollFocus(null); - }); - - update(() -> { - if(Core.scene != null && Core.scene.getKeyboardFocus() == this){ - doInput(); - } - }); - - shown(() -> { - saved = true; - editor.beginEdit(new Map()); - blockgroup.getButtons().get(2).setChecked(true); - Core.scene.setScrollFocus(view); - view.clearStack(); - - Timers.runTask(10f, Platform.instance::updateRPC); - }); - - hidden(() -> Platform.instance.updateRPC()); - } - - public MapView getView() { - return view; - } - - public void resetSaved(){ - saved = false; - } - - public void updateSelectedBlock(){ - Block block = editor.getDrawBlock(); - int i = 0; - for(BlockPair pair : ColorMapper.getPairs()){ - if(pair.wall == block || (pair.wall == Blocks.air && pair.floor == block)){ - blockgroup.getButtons().get(i).setChecked(true); - break; - } - i++; - } - } - - public boolean hasPane(){ - return Core.scene.getScrollFocus() == pane; - } - - public void build(){ - - new table(){{ - float isize = 16*2f; - aleft(); - - new table(){{ - - defaults().growY().width(130f).padBottom(-6); - - new imagebutton("icon-terrain", isize, () -> - dialog.show() - ).text("$text.editor.generate"); - - row(); - - new imagebutton("icon-resize", isize, () -> - resizeDialog.show() - ).text("$text.editor.resize").padTop(4f); - - row(); - - new imagebutton("icon-load-map", isize, () -> - loadDialog.show() - ).text("$text.editor.loadmap"); - - row(); - - new imagebutton("icon-save-map", isize, ()-> - saveDialog.show() - ).text("$text.editor.savemap"); - - row(); - - //iOS does not support loading raw files. - if(!ios) { - - new imagebutton("icon-load-image", isize, () -> { - Platform.instance.showFileChooser(Bundles.get("text.loadimage"), "Image Files", MapEditorDialog.this::tryLoadMap, true, "png"); - }).text("$text.editor.loadimage"); - - row(); - } - - new imagebutton("icon-save-image", isize, () -> { - //iOS doesn't really support saving raw files. Sharing is used instead. - if(!ios){ - Platform.instance.showFileChooser(Bundles.get("text.saveimage"), "Image Files", file -> { - if(!file.extension().toLowerCase().equals(".png")){ - file = file.parent().child(file.nameWithoutExtension() + ".png"); - } - FileHandle result = file; - ui.loadfrag.show(); - Timers.run(3f, () -> { - try{ - Pixmaps.write(editor.pixmap(), result); - }catch (Exception e){ - ui.showError(Bundles.format("text.editor.errorimagesave", Strings.parseException(e, false))); - if(!mobile) Log.err(e); - } - ui.loadfrag.hide(); - }); - }, false, "png"); - }else{ - try{ - FileHandle file = Gdx.files.local(("map-" + ((editor.getMap().name == null) ? "unknown" : editor.getMap().name) + ".png")); - Pixmaps.write(editor.pixmap(), file); - Platform.instance.shareFile(file); - }catch (Exception e){ - ui.showError(Bundles.format("text.editor.errorimagesave", Strings.parseException(e, false))); - } - } - }).text("$text.editor.saveimage"); - - row(); - - new imagebutton("icon-back", isize, () -> { - if(!saved){ - ui.showConfirm("$text.confirm", "$text.editor.unsaved", - MapEditorDialog.this::hide); - }else{ - hide(); - } - }).padBottom(0).text("$text.back"); - - }}.left().growY().end(); - - new table("button"){{ - add(view).grow(); - }}.grow().end(); - - new table(){{ - Table tools = new Table("button"); - tools.top(); - tools.marginTop(0).marginBottom(6); - - ButtonGroup group = new ButtonGroup<>(); - int i = 1; - - tools.defaults().size(53f, 58f).padBottom(-6); - - ImageButton undo = tools.addImageButton("icon-undo", 16*2f, () -> view.undo()).get(); - ImageButton redo = tools.addImageButton("icon-redo", 16*2f, () -> view.redo()).get(); - ImageButton grid = tools.addImageButton("icon-grid", "toggle", 16*2f, () -> view.setGrid(!view.isGrid())).get(); - - undo.setDisabled(() -> !view.getStack().canUndo()); - redo.setDisabled(() -> !view.getStack().canRedo()); - - undo.update(() -> undo.getImage().setColor(undo.isDisabled() ? Color.GRAY : Color.WHITE)); - redo.update(() -> redo.getImage().setColor(redo.isDisabled() ? Color.GRAY : Color.WHITE)); - grid.update(() -> grid.setChecked(view.isGrid())); - - for(EditorTool tool : EditorTool.values()){ - ImageButton button = new ImageButton("icon-" + tool.name(), "toggle"); - button.clicked(() -> view.setTool(tool)); - button.resizeImage(16*2f); - button.update(() -> button.setChecked(view.getTool() == tool)); - group.add(button); - if (tool == EditorTool.pencil) - button.setChecked(true); - - tools.add(button).padBottom(-6f); - if(i++ % 4 == 1) tools.row(); - } - - add(tools).width(53*4).padBottom(-6); - - row(); - - new table("button"){{ - margin(10f); - Slider slider = new Slider(0, MapEditor.brushSizes.length-1, 1, false); - slider.moved(f -> editor.setBrushSize(MapEditor.brushSizes[(int)(float)f])); - new label(() -> Bundles.format("text.editor.brushsize", MapEditor.brushSizes[(int)slider.getValue()])).left(); - row(); - add(slider).growX().padTop(4f); - }}.growX().padBottom(-6).end(); - - row(); - - new table("button"){{ - get().addCheck("$text.oregen", b -> editor.getMap().oreGen = b) - .update(c -> c.setChecked(editor.getMap().oreGen)).padTop(3).padBottom(3); - }}.growX().padBottom(-6).end(); - - row(); - - addBlockSelection(get()); - - row(); - - }}.right().growY().end(); - }}.grow().end(); - } - - public void tryLoadMap(FileHandle file){ - ui.loadfrag.show(); - Timers.runTask(3f, () -> { - try{ - Pixmap pixmap = new Pixmap(file); - if(verifySize(pixmap)){ - editor.setPixmap(pixmap); - view.clearStack(); - }else{ - ui.showError(Bundles.format("text.editor.badsize", Arrays.toString(MapEditor.validMapSizes))); - } - }catch (Exception e){ - ui.showError(Bundles.format("text.editor.errorimageload", Strings.parseException(e, false))); - Log.err(e); - } - ui.loadfrag.hide(); - }); - } - - private void doInput(){ - //tool select - for(int i = 0; i < EditorTool.values().length; i ++){ - int code = i == 0 ? 5 : i; - if(Inputs.keyTap("weapon_" + code)){ - view.setTool(EditorTool.values()[i]); - break; - } - } - - //ctrl keys (undo, redo, save) - if(Inputs.keyDown(Input.CONTROL_LEFT)){ - if(Inputs.keyTap(Input.Z)){ - view.undo(); - } - - if(Inputs.keyTap(Input.Y)){ - view.redo(); - } - - if(Inputs.keyTap(Input.S)){ - saveDialog.save(); - } - - if(Inputs.keyTap(Input.G)){ - view.setGrid(!view.isGrid()); - } - } - } - - private boolean verifySize(Pixmap pix){ - boolean w = false, h = false; - for(int i : MapEditor.validMapSizes){ - if(pix.getWidth() == i) - w = true; - if(pix.getHeight() == i) - h = true; - } - - return w && h; - } - - private boolean verifyMap(){ - int psc = ColorMapper.getColor(SpecialBlocks.playerSpawn); - int esc = ColorMapper.getColor(SpecialBlocks.enemySpawn); - - int playerSpawns = 0; - int enemySpawns = 0; - Pixmap pix = editor.pixmap(); - - for(int x = 0; x < pix.getWidth(); x ++){ - for(int y = 0; y < pix.getHeight(); y ++){ - int i = pix.getPixel(x, y); - if(i == psc) playerSpawns ++; - if(i == esc) enemySpawns ++; - } - } - - if(playerSpawns == 0){ - ui.showError("$text.editor.noplayerspawn"); - return false; - }else if(playerSpawns > 1){ - ui.showError("$text.editor.manyplayerspawns"); - return false; - } - - if(enemySpawns > MapEditor.maxSpawnpoints){ - ui.showError(Bundles.format("text.editor.manyenemyspawns", MapEditor.maxSpawnpoints)); - return false; - } - - return true; - } - - private void addBlockSelection(Table table){ - Table content = new Table(); - pane = new ScrollPane(content, "volume"); - pane.setScrollingDisabled(true, false); - pane.setFadeScrollBars(false); - pane.setOverscroll(true, false); - ButtonGroup group = new ButtonGroup<>(); - blockgroup = group; - - int i = 0; - - for(BlockPair pair : ColorMapper.getPairs()){ - Block block = pair.wall == Blocks.air ? pair.floor : pair.wall; - - ImageButton button = new ImageButton(Draw.hasRegion(block.name) ? Draw.region(block.name) : Draw.region(block.name + "1"), "toggle"); - button.clicked(() -> editor.setDrawBlock(block)); - button.resizeImage(8*4f); - group.add(button); - content.add(button).pad(4f).size(53f, 58f); - - if(i++ % 2 == 1){ - content.row(); - } - } - - group.getButtons().get(2).setChecked(true); - - Table extra = new Table("button"); - extra.labelWrap(() -> editor.getDrawBlock().formalName).width(180f).center(); - table.add(extra).padBottom(-6).growX(); - table.row(); - table.add(pane).growY().fillX(); - } -} diff --git a/core/src/io/anuke/mindustry/mapeditor/MapFilter.java b/core/src/io/anuke/mindustry/mapeditor/MapFilter.java deleted file mode 100644 index bdc1127a90..0000000000 --- a/core/src/io/anuke/mindustry/mapeditor/MapFilter.java +++ /dev/null @@ -1,244 +0,0 @@ -package io.anuke.mindustry.mapeditor; - -import com.badlogic.gdx.graphics.Pixmap; -import com.badlogic.gdx.math.Vector2; -import com.badlogic.gdx.utils.ObjectMap; -import com.badlogic.gdx.utils.OrderedMap; - -import io.anuke.mindustry.world.Block; -import io.anuke.mindustry.world.ColorMapper; -import io.anuke.mindustry.world.ColorMapper.BlockPair; -import io.anuke.mindustry.world.blocks.Blocks; -import io.anuke.mindustry.world.blocks.types.Floor; -import io.anuke.ucore.graphics.Pixmaps; -import io.anuke.ucore.noise.RidgedPerlin; -import io.anuke.ucore.noise.Simplex; -import io.anuke.ucore.util.Mathf; - -public class MapFilter{ - private ObjectMap prefs = map( - pref("replace", "whether to replace blocks"), - pref("terrain", "generate new terrain"), - pref("circle", "generate terrain in a circle"), - pref("distort", "distort the map image"), - pref("sand", "add patches of sand"), - pref("grass", "add patches of grass"), - pref("stone", "add patches of stone"), - pref("blackstone", "add patches of black stone"), - pref("allgrass", "fill map with grass"), - pref("allsnow", "fill map with snow"), - pref("allsand", "fill map with sand"), - pref("water", "add lakes"), - pref("oil", "add oil lakes"), - pref("lavariver", "add lava rivers"), - pref("slavariver", "ad small lava rivers"), - pref("river", "add rivers"), - pref("iceriver", "add frozen rivers"), - pref("oilriver", "add oil rivers") - ); - - private Simplex sim = new Simplex(); - private RidgedPerlin rid = new RidgedPerlin(1, 10, 20f); - private RidgedPerlin rid2 = new RidgedPerlin(1, 6, 1f); - private RidgedPerlin rid3 = new RidgedPerlin(1, 6, 1f); - - public MapFilter(){ - prefs.get("replace").enabled = true; - prefs.get("terrain").enabled = true; - randomize(); - } - - public void randomize(){ - sim.setSeed(Mathf.random(999999)); - rid.setSeed(Mathf.random(999999)); - rid2.setSeed(Mathf.random(999999)); - rid3.setSeed(Mathf.random(999999)); - } - - public ObjectMap getPrefs(){ - return prefs; - } - - public Pixmap process(Pixmap pixmap){ - if(prefs.get("terrain").enabled){ - for(int x = 0; x < pixmap.getWidth(); x++){ - for(int y = 0; y < pixmap.getHeight(); y++){ - float dist = Vector2.dst((float) x / pixmap.getWidth(), (float) y / pixmap.getHeight(), 0.5f, 0.5f) * 2f; - double noise = sim.octaveNoise2D(6, 0.6, 1 / 180.0, x, y + 9999) / (prefs.get("circle").enabled ? 1.7 : 1f) + dist / 10f; - - if(dist > 0.8){ - noise += 2 * (dist - 0.8); - } - - Block block = noise > 0.6 ? Blocks.stoneblock : Blocks.stone; - - pixmap.drawPixel(x, y, ColorMapper.getColor(block)); - } - } - } - - Pixmap src = Pixmaps.copy(pixmap); - - for(int x = 0; x < pixmap.getWidth(); x++){ - for(int y = 0; y < pixmap.getHeight(); y++){ - int dx = 0, dy = 0; - - if(prefs.get("distort").enabled){ - double intensity = 12; - double scale = 80; - double octaves = 4; - double falloff = 0.6; - double nx = (sim.octaveNoise2D(octaves, falloff, 1 / scale, x, y) - 0.5f) * intensity; - double ny = (sim.octaveNoise2D(octaves, falloff, 1 / scale, x, y + 99999) - 0.5f) * intensity; - dx = (int) nx; - dy = (int) ny; - } - - int pix = src.getPixel(x + dx, y + dy); - - BlockPair pair = ColorMapper.get(pix); - Block block = pair == null ? null : pair.wall == Blocks.air ? pair.floor : pair.wall; - - if(block == null) - continue; - - boolean floor = block instanceof Floor; - - double noise = sim.octaveNoise2D(4, 0.6, 1 / 170.0, x, y) + sim.octaveNoise2D(1, 1.0, 1 / 5.0, x, y) / 18.0; - double nwater = sim.octaveNoise2D(1, 1.0, 1 / 130.0, x, y); - noise += nwater / 5.0; - - double noil = sim.octaveNoise2D(1, 1.0, 1 / 150.0, x + 9999, y) + sim.octaveNoise2D(1, 1.0, 1 / 2.0, x, y) / 290.0; - - if(!floor || prefs.get("replace").enabled){ - - if(prefs.get("allgrass").enabled){ - block = floor ? Blocks.grass : Blocks.grassblock; - }else if(prefs.get("allsnow").enabled){ - block = floor ? Blocks.snow : Blocks.snowblock; - }else if(prefs.get("allsand").enabled){ - block = floor ? Blocks.sand : Blocks.sandblock; - }else if(prefs.get("replace").enabled){ - block = floor ? Blocks.stone : Blocks.stoneblock; - } - - if(noise > 0.7 && prefs.get("grass").enabled){ - block = floor ? Blocks.grass : Blocks.grassblock; - } - if(noise > 0.7 && prefs.get("blackstone").enabled){ - block = floor ? Blocks.blackstone : Blocks.blackstoneblock; - } - if(noise > 0.7 && prefs.get("sand").enabled){ - block = floor ? Blocks.sand : Blocks.sandblock; - } - if(noise > 0.8 && prefs.get("stone").enabled){ - block = floor ? Blocks.stone : Blocks.stoneblock; - } - } - - if(floor){ - if(nwater > 0.93 && prefs.get("water").enabled){ - block = Blocks.water; - if(nwater > 0.943){ - block = Blocks.deepwater; - } - } - - if(noil > 0.95 && prefs.get("oil").enabled){ - block = Blocks.dirt; - if(noil > 0.955){ - block = Blocks.oil; - } - } - } - - if(floor && prefs.get("lavariver").enabled){ - double lava = rid.getValue(x, y, 1 / 100f); - double t = 0.6; - if(lava > t){ - block = Blocks.lava; - }else if(lava > t - 0.2){ - block = Blocks.blackstone; - } - } - - if(floor && prefs.get("slavariver").enabled){ - double lava = rid.getValue(x, y, 1 / 40f); - double t = 0.7; - if(lava > t){ - block = Blocks.lava; - }else if(lava > t - 0.3){ - block = Blocks.blackstone; - } - } - - if(floor && prefs.get("oilriver").enabled){ - double lava = rid3.getValue(x, y, 1 / 100f); - double t = 0.9; - if(lava > t){ - block = Blocks.oil; - }else if(lava > t - 0.2){ - block = Blocks.dirt; - } - } - - if(floor && prefs.get("river").enabled){ - double riv = rid2.getValue(x, y, 1 / 140f); - double t = 0.4; - - if(riv > t + 0.1){ - block = Blocks.deepwater; - }else if(riv > t){ - block = Blocks.water; - }else if(riv > t - 0.2){ - block = Blocks.grass; - } - } - - if(floor && prefs.get("iceriver").enabled){ - double riv = rid2.getValue(x, y, 1 / 140f); - double t = 0.4; - - if(riv > t + 0.1){ - block = Blocks.ice; - }else if(riv > t){ - block = Blocks.ice; - }else if(riv > t - 0.2){ - block = Blocks.snow; - } - } - - pixmap.drawPixel(x, y, ColorMapper.getColor(block)); - } - } - - src.dispose(); - - return pixmap; - } - - private static OrderedMap map(GenPref...objects){ - OrderedMap prefs = new OrderedMap<>(); - - for(int i = 0; i < objects.length; i ++){ - GenPref pref = (GenPref)objects[i]; - prefs.put(pref.name, pref); - } - return prefs; - } - - private GenPref pref(String name, String desc){ - return new GenPref(name, desc); - } - - class GenPref{ - public final String name; - public final String description; - public boolean enabled; - - GenPref(String name, String description){ - this.name = name; - this.description = description; - } - } -} diff --git a/core/src/io/anuke/mindustry/mapeditor/MapGenerateDialog.java b/core/src/io/anuke/mindustry/mapeditor/MapGenerateDialog.java deleted file mode 100644 index ee6535aafe..0000000000 --- a/core/src/io/anuke/mindustry/mapeditor/MapGenerateDialog.java +++ /dev/null @@ -1,105 +0,0 @@ -package io.anuke.mindustry.mapeditor; - -import com.badlogic.gdx.graphics.Pixmap; -import com.badlogic.gdx.graphics.g2d.TextureRegion; -import com.badlogic.gdx.utils.Align; -import com.badlogic.gdx.utils.Scaling; - -import static io.anuke.mindustry.Vars.*; -import io.anuke.mindustry.mapeditor.MapFilter.GenPref; -import io.anuke.mindustry.ui.BorderImage; -import io.anuke.mindustry.ui.dialogs.FloatingDialog; -import io.anuke.ucore.core.Timers; -import io.anuke.ucore.graphics.Pixmaps; -import io.anuke.ucore.scene.style.TextureRegionDrawable; -import io.anuke.ucore.scene.ui.CheckBox; -import io.anuke.ucore.scene.ui.Image; -import io.anuke.ucore.scene.ui.ScrollPane; -import io.anuke.ucore.scene.ui.layout.Stack; -import io.anuke.ucore.scene.ui.layout.Table; - -public class MapGenerateDialog extends FloatingDialog{ - private MapEditor editor; - private Image image; - private boolean loading; - - public MapGenerateDialog(MapEditor editor) { - super("$text.editor.generate"); - this.editor = editor; - - Stack stack = new Stack(); - stack.add(image = new BorderImage()); - - Image loadImage = new Image("icon-loading"); - loadImage.setScaling(Scaling.none); - loadImage.setScale(3f); - loadImage.update(() -> loadImage.setOrigin(Align.center)); - loadImage.setVisible(() -> loading); - Image next = new Image("white"); - next.setScaling(Scaling.fit); - next.setColor(0, 0, 0, 0.6f); - next.setVisible(() -> loading); - - stack.add(next); - stack.add(loadImage); - - content().add(stack).grow(); - image.setScaling(Scaling.fit); - Table preft = new Table(); - preft.left(); - preft.margin(4f).marginRight(25f); - - for(GenPref pref : editor.getFilter().getPrefs().values()){ - CheckBox box = new CheckBox(pref.name); - box.setChecked(pref.enabled); - box.changed(() -> pref.enabled = box.isChecked()); - preft.add(box).pad(4f).left(); - preft.row(); - } - - ScrollPane pane = new ScrollPane(preft, "volume"); - pane.setFadeScrollBars(false); - pane.setScrollingDisabled(true, false); - - content().add(pane).fillY(); - - buttons().defaults().size(170f, 50f).pad(4f); - buttons().addButton("$text.back", this::hide); - buttons().addButton("$text.randomize", () ->{ - editor.getFilter().randomize(); - apply(); - }); - buttons().addButton("$text.update", this::apply); - buttons().addButton("$text.apply", () ->{ - ui.loadfrag.show(); - - Timers.run(3f, () ->{ - Pixmap copy = Pixmaps.copy(editor.pixmap()); - editor.applyFilter(); - ui.editor.getView().push(copy, Pixmaps.copy(editor.pixmap())); - ui.loadfrag.hide(); - ui.editor.resetSaved(); - hide(); - }); - }); - - shown(() ->{ - loading = true; - Timers.run(30f, () -> { - editor.applyFilterPreview(); - image.setDrawable(new TextureRegionDrawable(new TextureRegion(editor.getFilterTexture()))); - loading = false; - }); - }); - } - - private void apply(){ - loading = true; - Timers.run(3f, () -> { - editor.applyFilterPreview(); - loading = false; - }); - - } - -} diff --git a/core/src/io/anuke/mindustry/mapeditor/MapLoadDialog.java b/core/src/io/anuke/mindustry/mapeditor/MapLoadDialog.java deleted file mode 100644 index aa71160cfe..0000000000 --- a/core/src/io/anuke/mindustry/mapeditor/MapLoadDialog.java +++ /dev/null @@ -1,73 +0,0 @@ -package io.anuke.mindustry.mapeditor; - -import io.anuke.mindustry.ui.BorderImage; -import io.anuke.mindustry.ui.dialogs.FloatingDialog; -import io.anuke.mindustry.world.Map; -import io.anuke.ucore.function.Consumer; -import io.anuke.ucore.scene.ui.ButtonGroup; -import io.anuke.ucore.scene.ui.ScrollPane; -import io.anuke.ucore.scene.ui.TextButton; -import io.anuke.ucore.scene.ui.layout.Table; - -import static io.anuke.mindustry.Vars.world; - -public class MapLoadDialog extends FloatingDialog{ - private Map selected = world.maps().getMap(0); - - public MapLoadDialog(Consumer loader) { - super("$text.editor.loadmap"); - - shown(this::rebuild); - rebuild(); - - TextButton button = new TextButton("$text.load"); - button.setDisabled(() -> selected == null); - button.clicked(() -> { - if (selected != null) { - loader.accept(selected); - hide(); - } - }); - - buttons().defaults().size(200f, 50f); - buttons().addButton("$text.cancel", this::hide); - buttons().add(button); - } - - public void rebuild(){ - content().clear(); - - selected = world.maps().getMap(0); - - ButtonGroup group = new ButtonGroup<>(); - - int maxcol = 3; - - int i = 0; - - Table table = new Table(); - table.defaults().size(200f, 90f).pad(4f); - table.margin(10f); - - ScrollPane pane = new ScrollPane(table, "horizontal"); - pane.setFadeScrollBars(false); - - for (Map map : world.maps().list()) { - if (!map.visible) continue; - - TextButton button = new TextButton(map.localized(), "toggle"); - button.add(new BorderImage(map.texture, 2f)).size(16 * 4f); - button.getCells().reverse(); - button.clicked(() -> selected = map); - button.getLabelCell().grow().left().padLeft(5f); - group.add(button); - table.add(button); - if (++i % maxcol == 0) table.row(); - } - - content().add("$text.editor.loadmap"); - content().row(); - content().add(pane); - } - -} diff --git a/core/src/io/anuke/mindustry/mapeditor/MapResizeDialog.java b/core/src/io/anuke/mindustry/mapeditor/MapResizeDialog.java deleted file mode 100644 index 6ec83d6cec..0000000000 --- a/core/src/io/anuke/mindustry/mapeditor/MapResizeDialog.java +++ /dev/null @@ -1,67 +0,0 @@ -package io.anuke.mindustry.mapeditor; - -import com.badlogic.gdx.graphics.Pixmap; -import com.badlogic.gdx.utils.Align; - -import io.anuke.mindustry.ui.dialogs.FloatingDialog; -import io.anuke.ucore.function.BiConsumer; -import io.anuke.ucore.scene.ui.ButtonGroup; -import io.anuke.ucore.scene.ui.TextButton; -import io.anuke.ucore.scene.ui.layout.Table; - -public class MapResizeDialog extends FloatingDialog{ - int width, height; - - public MapResizeDialog(MapEditor editor, BiConsumer cons){ - super("$text.editor.resizemap"); - shown(() -> { - content().clear(); - Pixmap pix = editor.pixmap(); - width = pix.getWidth(); - height = pix.getHeight(); - - Table table = new Table(); - - for(int d = 0; d < 2; d ++){ - boolean w = d == 0; - int curr = d == 0 ? pix.getWidth() : pix.getHeight(); - int idx = 0; - for(int i = 0; i < MapEditor.validMapSizes.length; i ++) - if(MapEditor.validMapSizes[i] == curr) idx = i; - - table.add(d == 0 ? "$text.width": "$text.height").padRight(8f); - ButtonGroup group = new ButtonGroup<>(); - for(int i = 0; i < MapEditor.validMapSizes.length; i ++){ - int size = MapEditor.validMapSizes[i]; - TextButton button = new TextButton(size + "", "toggle"); - button.clicked(() -> { - if(w) - width = size; - else - height = size; - }); - group.add(button); - if(i == idx) button.setChecked(true); - table.add(button).size(100f, 54f).pad(2f); - } - - table.row(); - } - - content().label(() -> - width + height > 512 ? "$text.editor.resizebig" : "" - ).get().setAlignment(Align.center, Align.center); - content().row(); - content().add(table); - - }); - - buttons().defaults().size(200f, 50f); - buttons().addButton("$text.cancel", this::hide); - buttons().addButton("$text.editor.resize", () -> { - cons.accept(width, height); - hide(); - }); - - } -} diff --git a/core/src/io/anuke/mindustry/mapeditor/MapSaveDialog.java b/core/src/io/anuke/mindustry/mapeditor/MapSaveDialog.java deleted file mode 100644 index 40ab7cae61..0000000000 --- a/core/src/io/anuke/mindustry/mapeditor/MapSaveDialog.java +++ /dev/null @@ -1,74 +0,0 @@ -package io.anuke.mindustry.mapeditor; - -import io.anuke.mindustry.core.Platform; -import io.anuke.mindustry.ui.dialogs.FloatingDialog; -import io.anuke.mindustry.world.Map; -import io.anuke.ucore.function.Consumer; -import io.anuke.ucore.scene.ui.TextButton; -import io.anuke.ucore.scene.ui.TextField; - -import static io.anuke.mindustry.Vars.*; - -public class MapSaveDialog extends FloatingDialog{ - private TextField field; - private Consumer listener; - - public MapSaveDialog(Consumer cons){ - super("$text.editor.savemap"); - field = new TextField(); - listener = cons; - - Platform.instance.addDialog(field); - - shown(() -> { - content().clear(); - content().label(() ->{ - Map map = world.maps().getMap(field.getText()); - if(map != null){ - if(map.custom){ - return "$text.editor.overwrite"; - }else{ - return "$text.editor.failoverwrite"; - } - } - return ""; - }).colspan(2); - content().row(); - content().add("$text.editor.mapname").padRight(14f); - content().add(field).size(220f, 48f); - }); - - buttons().defaults().size(200f, 50f).pad(2f); - buttons().addButton("$text.cancel", this::hide); - - TextButton button = new TextButton("$text.save"); - button.clicked(() -> { - if(!invalid()){ - cons.accept(field.getText()); - hide(); - } - }); - button.setDisabled(this::invalid); - buttons().add(button); - } - - public void save(){ - if(!invalid()){ - listener.accept(field.getText()); - }else{ - ui.showError("$text.editor.failoverwrite"); - } - } - - public void setFieldText(String text){ - field.setText(text); - } - - private boolean invalid(){ - if(field.getText().isEmpty()){ - return true; - } - Map map = world.maps().getMap(field.getText()); - return map != null && !map.custom; - } -} diff --git a/core/src/io/anuke/mindustry/mapeditor/MapView.java b/core/src/io/anuke/mindustry/mapeditor/MapView.java deleted file mode 100644 index d60f2b14a4..0000000000 --- a/core/src/io/anuke/mindustry/mapeditor/MapView.java +++ /dev/null @@ -1,328 +0,0 @@ -package io.anuke.mindustry.mapeditor; - -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.graphics.Colors; -import com.badlogic.gdx.graphics.Pixmap; -import com.badlogic.gdx.graphics.g2d.Batch; -import com.badlogic.gdx.input.GestureDetector; -import com.badlogic.gdx.input.GestureDetector.GestureListener; -import com.badlogic.gdx.math.Bresenham2; -import com.badlogic.gdx.math.GridPoint2; -import com.badlogic.gdx.math.Rectangle; -import com.badlogic.gdx.math.Vector2; -import com.badlogic.gdx.scenes.scene2d.utils.ScissorStack; -import com.badlogic.gdx.utils.Array; -import io.anuke.mindustry.ui.GridImage; -import io.anuke.mindustry.world.ColorMapper; -import io.anuke.ucore.core.Core; -import io.anuke.ucore.core.Graphics; -import io.anuke.ucore.core.Inputs; -import io.anuke.ucore.graphics.Draw; -import io.anuke.ucore.graphics.Lines; -import io.anuke.ucore.graphics.Pixmaps; -import io.anuke.ucore.scene.Element; -import io.anuke.ucore.scene.event.InputEvent; -import io.anuke.ucore.scene.event.InputListener; -import io.anuke.ucore.scene.event.Touchable; -import io.anuke.ucore.scene.ui.TextField; -import io.anuke.ucore.scene.ui.layout.Unit; -import io.anuke.ucore.util.Input; -import io.anuke.ucore.util.Mathf; -import io.anuke.ucore.util.Tmp; - -import static io.anuke.mindustry.Vars.ui; - -public class MapView extends Element implements GestureListener{ - private MapEditor editor; - private EditorTool tool = EditorTool.pencil; - private OperationStack stack = new OperationStack(); - private DrawOperation op; - private Pixmap current; - private Bresenham2 br = new Bresenham2(); - private boolean updated = false; - private float offsetx, offsety; - private float zoom = 1f; - private boolean grid = false; - private GridImage image = new GridImage(0, 0); - private Vector2 vec = new Vector2(); - private Rectangle rect = new Rectangle(); - - private boolean drawing; - private int lastx, lasty; - private int startx, starty; - - public void setTool(EditorTool tool){ - this.tool = tool; - } - - public EditorTool getTool() { - return tool; - } - - public void clearStack(){ - stack.clear(); - current = null; - } - - public OperationStack getStack() { - return stack; - } - - public void setGrid(boolean grid) { - this.grid = grid; - } - - public boolean isGrid() { - return grid; - } - - public void push(Pixmap previous, Pixmap add){ - DrawOperation op = new DrawOperation(editor.pixmap()); - op.add(previous, add); - stack.add(op); - this.current = add; - } - - public void undo(){ - if(stack.canUndo()){ - stack.undo(); - editor.updateTexture(); - } - } - - public void redo(){ - if(stack.canRedo()){ - stack.redo(); - editor.updateTexture(); - } - } - - public MapView(MapEditor editor){ - this.editor = editor; - - Inputs.addProcessor(0, new GestureDetector(20, 0.5f, 2, 0.15f, this)); - setTouchable(Touchable.enabled); - - addListener(new InputListener(){ - - @Override - public boolean touchDown (InputEvent event, float x, float y, int pointer, int button) { - if(pointer != 0){ - return false; - } - - if(current == null){ - current = Pixmaps.copy(editor.pixmap()); - } - updated = false; - - GridPoint2 p = project(x, y); - lastx = p.x; - lasty = p.y; - startx = p.x; - starty = p.y; - tool.touched(editor, p.x, p.y); - - if(tool.edit){ - updated = true; - ui.editor.resetSaved(); - } - - op = new DrawOperation(editor.pixmap()); - - drawing = true; - return true; - } - - @Override - public void touchUp (InputEvent event, float x, float y, int pointer, int button) { - drawing = false; - - GridPoint2 p = project(x, y); - - if(tool == EditorTool.line){ - ui.editor.resetSaved(); - Array points = br.line(startx, starty, p.x, p.y); - for(GridPoint2 point : points){ - editor.draw(point.x, point.y); - } - updated = true; - } - - if(updated){ - if(op == null) op = new DrawOperation(editor.pixmap()); - Pixmap next = Pixmaps.copy(editor.pixmap()); - op.add(current, next); - current = null; - stack.add(op); - op = null; - } - } - - @Override - public void touchDragged (InputEvent event, float x, float y, int pointer) { - GridPoint2 p = project(x, y); - - if(drawing && tool == EditorTool.pencil){ - ui.editor.resetSaved(); - Array points = br.line(lastx, lasty, p.x, p.y); - for(GridPoint2 point : points){ - editor.draw(point.x, point.y); - } - updated = true; - } - lastx = p.x; - lasty = p.y; - } - }); - } - - @Override - public void act(float delta){ - super.act(delta); - - if(Core.scene.getKeyboardFocus() == null || !(Core.scene.getKeyboardFocus() instanceof TextField) && - !Inputs.keyDown(Input.CONTROL_LEFT)) { - float ax = Inputs.getAxis("move_x"); - float ay = Inputs.getAxis("move_y"); - offsetx -= ax * 15f / zoom; - offsety -= ay * 15f / zoom; - } - - if(ui.editor.hasPane()) return; - - zoom += Inputs.scroll()/10f * zoom; - clampZoom(); - } - - private void clampZoom(){ - zoom = Mathf.clamp(zoom, 0.2f, 12f); - } - - private GridPoint2 project(float x, float y){ - float ratio = 1f / ((float)editor.pixmap().getWidth() / editor.pixmap().getHeight()); - float size = Math.min(width, height); - float sclwidth = size * zoom; - float sclheight = size * zoom * ratio; - x = (x - getWidth()/2 + sclwidth/2 - offsetx*zoom) / sclwidth * editor.texture().getWidth(); - y = (y - getHeight()/2 + sclheight/2 - offsety*zoom) / sclheight * editor.texture().getHeight(); - return Tmp.g1.set((int)x, editor.texture().getHeight() - 1 - (int)y); - } - - private Vector2 unproject(int x, int y){ - float ratio = 1f / ((float)editor.pixmap().getWidth() / editor.pixmap().getHeight()); - float size = Math.min(width, height); - float sclwidth = size * zoom; - float sclheight = size * zoom * ratio; - float px = ((float)x / editor.texture().getWidth()) * sclwidth + offsetx*zoom - sclwidth/2 + getWidth()/2; - float py = (float)((float)(editor.texture().getHeight() - 1 - y) / editor.texture().getHeight()) * sclheight - + offsety*zoom - sclheight/2 + getHeight()/2; - return vec.set(px, py); - } - - @Override - public void draw(Batch batch, float alpha){ - float ratio = 1f / ((float)editor.pixmap().getWidth() / editor.pixmap().getHeight()); - float size = Math.min(width, height); - float sclwidth = size * zoom; - float sclheight = size * zoom * ratio; - float centerx = x + width/2 + offsetx * zoom; - float centery = y + height/2 + offsety * zoom; - - image.setImageSize(editor.pixmap().getWidth(), editor.pixmap().getHeight()); - - batch.flush(); - boolean pop = ScissorStack.pushScissors(rect.set(x + width/2 - size/2, y + height/2 - size/2, size, size)); - - batch.draw(editor.texture(), centerx - sclwidth/2, centery - sclheight/2, sclwidth, sclheight); - - if(grid){ - Draw.color(Color.GRAY); - image.setBounds(centerx - sclwidth/2, centery - sclheight/2, sclwidth, sclheight); - image.draw(batch, alpha); - Draw.color(); - } - - if(tool == EditorTool.line && drawing){ - Vector2 v1 = unproject(startx, starty).add(x, y); - float sx = v1.x, sy = v1.y; - Vector2 v2 = unproject(lastx, lasty).add(x, y); - - Draw.color(Tmp.c1.set(ColorMapper.getColor(editor.getDrawBlock()))); - Lines.stroke(Unit.dp.scl(3f * zoom)); - Lines.line(sx, sy, v2.x, v2.y); - - Lines.poly(sx, sy, 40, editor.getBrushSize() * zoom * 3); - - Lines.poly(v2.x, v2.y, 40, editor.getBrushSize() * zoom * 3); - } - - batch.flush(); - - if(pop) ScissorStack.popScissors(); - - Draw.color(Colors.get("accent")); - Lines.stroke(Unit.dp.scl(3f)); - Lines.rect(x + width/2 - size/2, y + height/2 - size/2, size, size); - Draw.reset(); - } - - private boolean active(){ - return Core.scene.getKeyboardFocus() != null - && Core.scene.getKeyboardFocus().isDescendantOf(ui.editor) - && ui.editor.isShown() && tool == EditorTool.zoom && - Core.scene.hit(Graphics.mouse().x, Graphics.mouse().y, true) == this; - } - - @Override - public boolean touchDown(float x, float y, int pointer, int button){ - return false; - } - - @Override - public boolean tap(float x, float y, int count, int button){ - return false; - } - - @Override - public boolean longPress(float x, float y){ - return false; - } - - @Override - public boolean fling(float velocityX, float velocityY, int button){ - return false; - } - - @Override - public boolean pan(float x, float y, float deltaX, float deltaY){ - if(!active()) return false; - offsetx += deltaX / zoom; - offsety -= deltaY / zoom; - return false; - } - - @Override - public boolean panStop(float x, float y, int pointer, int button){ - return false; - } - - @Override - public boolean zoom(float initialDistance, float distance){ - if(!active()) return false; - float nzoom = distance - initialDistance; - zoom += nzoom / 10000f / Unit.dp.scl(1f) * zoom; - clampZoom(); - return false; - } - - @Override - public boolean pinch(Vector2 initialPointer1, Vector2 initialPointer2, Vector2 pointer1, Vector2 pointer2){ - return false; - } - - @Override - public void pinchStop(){ - - } -} diff --git a/core/src/io/anuke/mindustry/mapeditor/OperationStack.java b/core/src/io/anuke/mindustry/mapeditor/OperationStack.java deleted file mode 100755 index 7de9a0c98d..0000000000 --- a/core/src/io/anuke/mindustry/mapeditor/OperationStack.java +++ /dev/null @@ -1,55 +0,0 @@ -package io.anuke.mindustry.mapeditor; - -import com.badlogic.gdx.utils.Array; - -public class OperationStack{ - private final static int maxSize = 10; - private Array stack = new Array<>(); - private int index = 0; - - public OperationStack(){ - - } - - public void clear(){ - for(DrawOperation op : stack){ - op.dispose(); - } - stack.clear(); - index = 0; - } - - public void add(DrawOperation action){ - stack.truncate(stack.size + index); - index = 0; - stack.add(action); - - if(stack.size > maxSize){ - stack.get(0).disposeFrom(); - stack.removeIndex(0); - } - } - - public boolean canUndo(){ - return !(stack.size - 1 + index < 0); - } - - public boolean canRedo(){ - return !(index > -1 || stack.size + index < 0); - } - - public void undo(){ - if(!canUndo()) return; - - stack.get(stack.size - 1 + index).undo(); - index --; - } - - public void redo(){ - if(!canRedo()) return; - - index ++; - stack.get(stack.size - 1 + index).redo(); - - } -} diff --git a/core/src/io/anuke/mindustry/maps/Map.java b/core/src/io/anuke/mindustry/maps/Map.java new file mode 100644 index 0000000000..c436b86939 --- /dev/null +++ b/core/src/io/anuke/mindustry/maps/Map.java @@ -0,0 +1,115 @@ +package io.anuke.mindustry.maps; + +import io.anuke.arc.Core; +import io.anuke.arc.collection.StringMap; +import io.anuke.arc.files.FileHandle; +import io.anuke.arc.graphics.Texture; +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.game.Rules; +import io.anuke.mindustry.io.JsonIO; + +public class Map implements Comparable{ + /** Whether this is a custom map. */ + public final boolean custom; + /** Metadata. Author description, display name, etc. */ + public final StringMap tags; + /** Base file of this map. File can be named anything at all. */ + public final FileHandle file; + /** Format version. */ + public final int version; + /** Map width/height, shorts. */ + public int width, height; + /** Preview texture. */ + public Texture texture; + /** Build that this map was created in. -1 = unknown or custom build. */ + public int build; + + public Map(FileHandle file, int width, int height, StringMap tags, boolean custom, int version, int build){ + this.custom = custom; + this.tags = tags; + this.file = file; + this.width = width; + this.height = height; + this.version = version; + this.build = build; + } + + public Map(FileHandle file, int width, int height, StringMap tags, boolean custom, int version){ + this(file, width, height, tags, custom, version, -1); + } + + public Map(FileHandle file, int width, int height, StringMap tags, boolean custom){ + this(file, width, height, tags, custom, -1); + } + + public Map(StringMap tags){ + this(Vars.customMapDirectory.child(tags.get("name", "unknown")), 0, 0, tags, true); + } + + public int getHightScore(){ + return Core.settings.getInt("hiscore" + file.nameWithoutExtension(), 0); + } + + public void setHighScore(int score){ + Core.settings.put("hiscore" + file.nameWithoutExtension(), score); + Vars.data.modified(); + } + + /** This creates a new instance.*/ + public Rules rules(){ + Rules result = JsonIO.read(Rules.class, tags.get("rules", "{}")); + if(result.spawns.isEmpty()) result.spawns = Vars.defaultWaves.get(); + return result; + } + + /** Whether this map has a core of the enemy 'wave' team. Default: true. + * Used for checking Attack mode validity.*/ + public boolean hasEnemyCore(){ + return tags.get("enemycore", "true").equals("true"); + } + + /** Whether this map has a core of any team except the default player team. Default: true. + * Used for checking PvP mode validity.*/ + public boolean hasOtherCores(){ + return tags.get("othercore", "true").equals("true"); + } + + public String author(){ + return tag("author"); + } + + public String description(){ + return tag("description"); + } + + public String name(){ + return tag("name"); + } + + public String tag(String name){ + return tags.containsKey(name) && !tags.get(name).trim().isEmpty() ? tags.get(name) : Core.bundle.get("unknown"); + } + + public boolean hasTag(String name){ + return tags.containsKey(name); + } + + @Override + public int compareTo(Map map){ + int type = -Boolean.compare(custom, map.custom); + if(type != 0){ + return type; + }else{ + return name().compareTo(map.name()); + } + } + + @Override + public String toString(){ + return "Map{" + + "file='" + file + '\'' + + ", custom=" + custom + + ", tags=" + tags + + '}'; + } +} diff --git a/core/src/io/anuke/mindustry/maps/MapException.java b/core/src/io/anuke/mindustry/maps/MapException.java new file mode 100644 index 0000000000..7bc1c76805 --- /dev/null +++ b/core/src/io/anuke/mindustry/maps/MapException.java @@ -0,0 +1,10 @@ +package io.anuke.mindustry.maps; + +public class MapException extends RuntimeException{ + public final Map map; + + public MapException(Map map, String s){ + super(s); + this.map = map; + } +} diff --git a/core/src/io/anuke/mindustry/maps/Maps.java b/core/src/io/anuke/mindustry/maps/Maps.java new file mode 100644 index 0000000000..4a47e20f38 --- /dev/null +++ b/core/src/io/anuke/mindustry/maps/Maps.java @@ -0,0 +1,263 @@ +package io.anuke.mindustry.maps; + +import io.anuke.arc.Core; +import io.anuke.arc.collection.*; +import io.anuke.arc.files.FileHandle; +import io.anuke.arc.function.ExceptionRunnable; +import io.anuke.arc.graphics.Texture; +import io.anuke.arc.util.*; +import io.anuke.arc.util.serialization.Json; +import io.anuke.mindustry.game.SpawnGroup; +import io.anuke.mindustry.io.LegacyMapIO; +import io.anuke.mindustry.io.MapIO; + +import java.io.IOException; +import java.io.StringWriter; + +import static io.anuke.mindustry.Vars.*; + +public class Maps implements Disposable{ + /** List of all built-in maps. Filenames only. */ + private static String[] defaultMapNames = {"fortress", "labyrinth", "islands", "tendrils", "caldera"}; + /** All maps stored in an ordered array. */ + private Array maps = new Array<>(); + /** Serializer for meta. */ + private Json json = new Json(); + + /** Returns a list of all maps, including custom ones. */ + public Array all(){ + return maps; + } + + /** Returns a list of only custom maps. */ + public Array customMaps(){ + return maps.select(m -> m.custom); + } + + /** Returns a list of only default maps. */ + public Array defaultMaps(){ + return maps.select(m -> !m.custom); + } + + public Map byName(String name){ + return maps.find(m -> m.name().equals(name)); + } + + /** + * Loads a map from the map folder and returns it. Should only be used for zone maps. + * Does not add this map to the map list. + */ + public Map loadInternalMap(String name){ + FileHandle file = Core.files.internal("maps/" + name + "." + mapExtension); + + try{ + return MapIO.createMap(file, false); + }catch(IOException e){ + throw new RuntimeException(e); + } + } + + /** Load all maps. Should be called at application start. */ + public void load(){ + try{ + for(String name : defaultMapNames){ + FileHandle file = Core.files.internal("maps/" + name + "." + mapExtension); + loadMap(file, false); + } + }catch(IOException e){ + throw new RuntimeException(e); + } + + loadCustomMaps(); + } + + public void reload(){ + dispose(); + load(); + } + + /** + * Save a custom map to the directory. This updates all values and stored data necessary. + * The tags are copied to prevent mutation later. + */ + public void saveMap(ObjectMap baseTags){ + + try{ + StringMap tags = new StringMap(baseTags); + String name = tags.get("name"); + if(name == null) throw new IllegalArgumentException("Can't save a map with no name. How did this happen?"); + FileHandle file; + + //find map with the same exact display name + Map other = maps.find(m -> m.name().equals(name)); + + if(other != null){ + //dispose of map if it's already there + if(other.texture != null){ + other.texture.dispose(); + other.texture = null; + } + maps.remove(other); + file = other.file; + }else{ + file = findFile(); + } + + //create map, write it, etc etc etc + Map map = new Map(file, world.width(), world.height(), tags, true); + MapIO.writeMap(file, map); + + if(!headless){ + map.texture = new Texture(MapIO.generatePreview(world.getTiles())); + } + maps.add(map); + maps.sort(); + }catch(IOException e){ + throw new RuntimeException(e); + } + } + + /** Creates a legacy map by converting it to a non-legacy map and pasting it in a temp directory. + * Should be followed up by {@link #importMap(FileHandle)} .*/ + public Map makeLegacyMap(FileHandle file) throws IOException{ + FileHandle dst = tmpDirectory.child("conversion_map." + mapExtension); + LegacyMapIO.convertMap(file, dst); + return MapIO.createMap(dst, true); + } + + /** Import a map, then save it. This updates all values and stored data necessary. */ + public void importMap(FileHandle file) throws IOException{ + FileHandle dest = findFile(); + file.copyTo(dest); + + loadMap(dest, true); + } + + /** Attempts to run the following code; + * catches any errors and attempts to display them in a readable way.*/ + public void tryCatchMapError(ExceptionRunnable run){ + try{ + run.run(); + }catch(Exception e){ + Log.err(e); + + if("Outdated legacy map format".equals(e.getMessage())){ + ui.showError("$editor.errorlegacy"); + }else if(e.getMessage() != null && e.getMessage().contains("Incorrect header!")){ + ui.showError("$editor.errorheader"); + }else{ + ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, true))); + } + } + } + + /** Removes a map completely. */ + public void removeMap(Map map){ + if(map.texture != null){ + map.texture.dispose(); + map.texture = null; + } + + maps.remove(map); + map.file.delete(); + } + + public String writeWaves(Array groups){ + if(groups == null){ + return "[]"; + } + + StringWriter buffer = new StringWriter(); + json.setWriter(buffer); + + json.writeArrayStart(); + for(int i = 0; i < groups.size; i++){ + json.writeObjectStart(SpawnGroup.class, SpawnGroup.class); + groups.get(i).write(json); + json.writeObjectEnd(); + } + json.writeArrayEnd(); + return buffer.toString(); + } + + public Array readWaves(String str){ + return str == null ? null : str.equals("[]") ? new Array<>() : Array.with(json.fromJson(SpawnGroup[].class, str)); + } + + public void loadLegacyMaps(){ + boolean convertedAny = false; + for(FileHandle file : customMapDirectory.list()){ + if(file.extension().equalsIgnoreCase(oldMapExtension)){ + try{ + convertedAny = true; + LegacyMapIO.convertMap(file, file.sibling(file.nameWithoutExtension() + "." + mapExtension)); + //delete old, converted file; it is no longer useful + file.delete(); + Log.info("Converted file {0}", file); + }catch(Exception e){ + //rename the file to a 'mmap_conversion_failed' extension to keep it there just in case + //but don't delete it + file.copyTo(file.sibling(file.name() + "_conversion_failed")); + file.delete(); + Log.err(e); + } + } + } + + //free up any potential memory that was used up during conversion + if(convertedAny){ + world.createTiles(1, 1); + //reload maps to load the converted ones + reload(); + } + } + + /** Find a new filename to put a map to. */ + private FileHandle findFile(){ + //find a map name that isn't used. + int i = maps.size; + while(customMapDirectory.child("map_" + i + "." + mapExtension).exists()){ + i++; + } + return customMapDirectory.child("map_" + i + "." + mapExtension); + } + + private void loadMap(FileHandle file, boolean custom) throws IOException{ + Map map = MapIO.createMap(file, custom); + + if(map.name() == null){ + throw new IOException("Map name cannot be empty! File: " + file); + } + + if(!headless){ + map.texture = new Texture(MapIO.generatePreview(map)); + } + + maps.add(map); + maps.sort(); + } + + private void loadCustomMaps(){ + for(FileHandle file : customMapDirectory.list()){ + try{ + if(file.extension().equalsIgnoreCase(mapExtension)){ + loadMap(file, true); + } + }catch(Exception e){ + Log.err("Failed to load custom map file '{0}'!", file); + Log.err(e); + } + } + } + + @Override + public void dispose(){ + for(Map map : maps){ + if(map.texture != null){ + map.texture.dispose(); + map.texture = null; + } + } + maps.clear(); + } +} \ No newline at end of file diff --git a/core/src/io/anuke/mindustry/maps/generators/BasicGenerator.java b/core/src/io/anuke/mindustry/maps/generators/BasicGenerator.java new file mode 100644 index 0000000000..375af9543a --- /dev/null +++ b/core/src/io/anuke/mindustry/maps/generators/BasicGenerator.java @@ -0,0 +1,262 @@ +package io.anuke.mindustry.maps.generators; + +import io.anuke.arc.collection.*; +import io.anuke.arc.function.IntPositionConsumer; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.Geometry; +import io.anuke.arc.math.geom.Point2; +import io.anuke.arc.util.Structs; +import io.anuke.arc.util.noise.Simplex; +import io.anuke.mindustry.content.Blocks; +import io.anuke.mindustry.world.*; +import io.anuke.mindustry.world.blocks.Floor; + +import java.util.PriorityQueue; + +public abstract class BasicGenerator extends RandomGenerator{ + protected static final DistanceHeuristic manhattan = (x1, y1, x2, y2) -> Math.abs(x1 - x2) + Math.abs(y1 - y2); + + protected Array ores; + protected Simplex sim = new Simplex(); + protected Simplex sim2 = new Simplex(); + + public BasicGenerator(int width, int height, Block... ores){ + super(width, height); + this.ores = Array.with(ores); + } + + @Override + public void generate(Tile[][] tiles){ + int seed = Mathf.random(99999999); + sim.setSeed(seed); + sim2.setSeed(seed + 1); + super.generate(tiles); + } + + public void ores(Tile[][] tiles){ + pass(tiles, (x, y) -> { + if(ores != null){ + int offsetX = x - 4, offsetY = y + 23; + for(int i = ores.size - 1; i >= 0; i--){ + Block entry = ores.get(i); + if(Math.abs(0.5f - sim.octaveNoise2D(2, 0.7, 1f / (50 + i * 2), offsetX, offsetY + i*999)) > 0.23f && + Math.abs(0.5f - sim2.octaveNoise2D(1, 1, 1f / (40 + i * 4), offsetX, offsetY - i*999)) > 0.32f){ + ore = entry; + break; + } + } + } + }); + } + + public void terrain(Tile[][] tiles, Block dst, float scl, float mag, float cmag){ + pass(tiles, (x, y) -> { + double rocks = sim.octaveNoise2D(5, 0.5, 1f / scl, x, y) * mag + + Mathf.dst((float)x / width, (float)y / height, 0.5f, 0.5f) * cmag; + + double edgeDist = Math.min(x, Math.min(y, Math.min(Math.abs(x - (width - 1)), Math.abs(y - (height - 1))))); + double transition = 5; + if(edgeDist < transition){ + rocks += (transition - edgeDist) / transition / 1.5; + } + + if(rocks > 0.9){ + block =dst; + } + }); + } + + public void noise(Tile[][] tiles, Block floor, Block block, int octaves, float falloff, float scl, float threshold){ + sim.setSeed(Mathf.random(99999)); + pass(tiles, (x, y) -> { + if(sim.octaveNoise2D(octaves, falloff, 1f / scl, x, y) > threshold){ + Tile tile = tiles[x][y]; + this.floor = floor; + if(tile.block().solid){ + this.block = block; + } + } + }); + } + + public void overlay(Tile[][] tiles, Block floor, Block block, float chance, int octaves, float falloff, float scl, float threshold){ + sim.setSeed(Mathf.random(99999)); + pass(tiles, (x, y) -> { + if(sim.octaveNoise2D(octaves, falloff, 1f / scl, x, y) > threshold && Mathf.chance(chance) && tiles[x][y].floor() == floor){ + ore = block; + } + }); + } + + public void tech(Tile[][] tiles){ + Block[] blocks = {Blocks.darkPanel3}; + int secSize = 20; + pass(tiles, (x, y) -> { + int mx = x % secSize, my = y % secSize; + int sclx = x / secSize, scly = y / secSize; + if(noise(sclx, scly, 10f, 1f) > 0.63f && (mx == 0 || my == 0 || mx == secSize - 1 || my == secSize - 1)){ + if(Mathf.chance(noise(x + 0x231523, y, 40f, 1f))){ + floor = Structs.random(blocks); + if(Mathf.dst(mx, my, secSize/2, secSize/2) > secSize/2f + 2){ + floor = Blocks.darkPanel4; + } + } + + if(block.solid && Mathf.chance(0.7)){ + block = Blocks.darkMetal; + } + } + }); + } + + public void distort(Tile[][] tiles, float scl, float mag){ + Block[][] blocks = new Block[width][height]; + Floor[][] floors = new Floor[width][height]; + + each((x, y) -> { + float cx = x + noise(x, y, scl, mag) - mag / 2f, cy = y + noise(x, y + 1525215f, scl, mag) - mag / 2f; + Tile other = tiles[Mathf.clamp((int)cx, 0, width-1)][Mathf.clamp((int)cy, 0, height-1)]; + blocks[x][y] = other.block(); + floors[x][y] = other.floor(); + }); + + pass(tiles, (x, y) -> { + floor = floors[x][y]; + block = blocks[x][y]; + }); + } + + public void scatter(Tile[][] tiles, Block target, Block dst, float chance){ + pass(tiles, (x, y) -> { + if(!Mathf.chance(chance)) return; + if(floor == target){ + floor = dst; + }else if(block == target){ + block = dst; + } + }); + } + + public void each(IntPositionConsumer r){ + for(int x = 0; x < width; x++){ + for(int y = 0; y < height; y++){ + r.accept(x, y); + } + } + } + + protected float noise(float x, float y, float scl, float mag){ + return (float)sim2.octaveNoise2D(1f, 0f, 1f / scl, x + 0x361266f, y + 0x251259f) * mag; + } + + public void pass(Tile[][] tiles, IntPositionConsumer r){ + for(int x = 0; x < width; x++){ + for(int y = 0; y < height; y++){ + floor = tiles[x][y].floor(); + block = tiles[x][y].block(); + ore = tiles[x][y].overlay(); + r.accept(x, y); + tiles[x][y] = new Tile(x, y, floor.id, ore.id, block.id); + } + } + } + + public void brush(Tile[][] tiles, Array path, int rad){ + path.each(tile -> erase(tiles, tile.x, tile.y, rad)); + } + + public void erase(Tile[][] tiles, int cx, int cy, int rad){ + for(int x = -rad; x <= rad; x++){ + for(int y = -rad; y <= rad; y++){ + int wx = cx + x, wy = cy + y; + if(Structs.inBounds(wx, wy, width, height) && Mathf.dst(x, y, 0, 0) <= rad){ + Tile other = tiles[wx][wy]; + other.setBlock(Blocks.air); + } + } + } + } + + public Array pathfind(Tile[][] tiles, int startX, int startY, int endX, int endY, TileHueristic th, DistanceHeuristic dh){ + Tile start = tiles[startX][startY]; + Tile end = tiles[endX][endY]; + GridBits closed = new GridBits(width, height); + IntFloatMap costs = new IntFloatMap(); + PriorityQueue queue = new PriorityQueue<>(tiles.length * tiles[0].length / 2, (a, b) -> Float.compare(costs.get(a.pos(), 0f) + dh.cost(a.x, a.y, end.x, end.y), costs.get(b.pos(), 0f) + dh.cost(b.x, b.y, end.x, end.y))); + queue.add(start); + boolean found = false; + while(!queue.isEmpty()){ + Tile next = queue.poll(); + float baseCost = costs.get(next.pos(), 0f); + if(next == end){ + found = true; + break; + } + closed.set(next.x, next.y); + for(Point2 point : Geometry.d4){ + int newx = next.x + point.x, newy = next.y + point.y; + if(Structs.inBounds(newx, newy, width, height)){ + Tile child = tiles[newx][newy]; + if(!closed.get(child.x, child.y)){ + closed.set(child.x, child.y); + child.rotation(child.relativeTo(next.x, next.y)); + costs.put(child.pos(), th.cost(child) + baseCost); + queue.add(child); + } + } + } + } + + Array out = new Array<>(); + + if(!found) return out; + + Tile current = end; + while(current != start){ + out.add(current); + Point2 p = Geometry.d4(current.rotation()); + current = tiles[current.x + p.x][current.y + p.y]; + } + + out.reverse(); + + return out; + } + + public void inverseFloodFill(Tile[][] tiles, Tile start, Block block){ + IntArray arr = new IntArray(); + arr.add(start.pos()); + while(!arr.isEmpty()){ + int i = arr.pop(); + int x = Pos.x(i), y = Pos.y(i); + tiles[x][y].cost = 2; + for(Point2 point : Geometry.d4){ + int newx = x + point.x, newy = y + point.y; + if(Structs.inBounds(newx, newy, width, height)){ + Tile child = tiles[newx][newy]; + if(child.block() == Blocks.air && child.cost != 2){ + child.cost = 2; + arr.add(child.pos()); + } + } + } + } + + for(int x = 0; x < width; x ++){ + for(int y = 0; y < height; y++){ + Tile tile = tiles[x][y]; + if(tile.cost != 2 && tile.block() == Blocks.air){ + tile.setBlock(block); + } + } + } + } + + public interface DistanceHeuristic{ + float cost(int x1, int y1, int x2, int y2); + } + + public interface TileHueristic{ + float cost(Tile tile); + } +} diff --git a/core/src/io/anuke/mindustry/maps/generators/Generator.java b/core/src/io/anuke/mindustry/maps/generators/Generator.java new file mode 100644 index 0000000000..965c6cf0e7 --- /dev/null +++ b/core/src/io/anuke/mindustry/maps/generators/Generator.java @@ -0,0 +1,23 @@ +package io.anuke.mindustry.maps.generators; + +import io.anuke.mindustry.type.Loadout; +import io.anuke.mindustry.world.Tile; + +public abstract class Generator{ + public int width, height; + protected Loadout loadout; + + public Generator(int width, int height){ + this.width = width; + this.height = height; + } + + public Generator(){ + } + + public void init(Loadout loadout){ + this.loadout = loadout; + } + + public abstract void generate(Tile[][] tiles); +} diff --git a/core/src/io/anuke/mindustry/maps/generators/MapGenerator.java b/core/src/io/anuke/mindustry/maps/generators/MapGenerator.java new file mode 100644 index 0000000000..a19157dbdb --- /dev/null +++ b/core/src/io/anuke/mindustry/maps/generators/MapGenerator.java @@ -0,0 +1,199 @@ +package io.anuke.mindustry.maps.generators; + +import io.anuke.arc.collection.Array; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.Point2; +import io.anuke.arc.util.Structs; +import io.anuke.arc.util.noise.Simplex; +import io.anuke.mindustry.content.Blocks; +import io.anuke.mindustry.io.MapIO; +import io.anuke.mindustry.maps.Map; +import io.anuke.mindustry.type.Item; +import io.anuke.mindustry.type.Loadout; +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.*; +import io.anuke.mindustry.world.blocks.storage.CoreBlock; +import io.anuke.mindustry.world.blocks.storage.StorageBlock; + +import static io.anuke.mindustry.Vars.defaultTeam; +import static io.anuke.mindustry.Vars.world; + +public class MapGenerator extends Generator{ + private Map map; + private String mapName; + private Array decorations = Array.with(new Decoration(Blocks.stone, Blocks.rock, 0.003f)); + /** How much the landscape is randomly distorted. */ + public float distortion = 3; + /** + * The amount of final enemy spawns used. -1 to use everything in the map. + * This amount of enemy spawns is selected randomly from the map. + */ + public int enemySpawns = -1; + /** Whether floor is distorted along with blocks. */ + public boolean distortFloor = false; + + public MapGenerator(String mapName){ + this.mapName = mapName; + } + + public MapGenerator(String mapName, int enemySpawns){ + this.mapName = mapName; + this.enemySpawns = enemySpawns; + } + + public MapGenerator decor(Decoration... decor){ + this.decorations.addAll(decor); + return this; + } + + public MapGenerator dist(float distortion){ + this.distortion = distortion; + return this; + } + + public MapGenerator dist(float distortion, boolean floor){ + this.distortion = distortion; + this.distortFloor = floor; + return this; + } + + { + decor(new Decoration(Blocks.snow, Blocks.snowrock, 0.01), new Decoration(Blocks.ignarock, Blocks.pebbles, 0.03f)); + } + + @Override + public void init(Loadout loadout){ + this.loadout = loadout; + map = world.maps.loadInternalMap(mapName); + width = map.width; + height = map.height; + } + + @Override + public void generate(Tile[][] tiles){ + for(int x = 0; x < width; x++){ + for(int y = 0; y < height; y++){ + tiles[x][y] = new Tile(x, y); + } + } + + MapIO.loadMap(map); + Array players = new Array<>(); + Array enemies = new Array<>(); + + for(int x = 0; x < width; x++){ + for(int y = 0; y < height; y++){ + if(tiles[x][y].block() instanceof CoreBlock && tiles[x][y].getTeam() == defaultTeam){ + players.add(new Point2(x, y)); + tiles[x][y].setBlock(Blocks.air); + } + + if(tiles[x][y].overlay() == Blocks.spawn && enemySpawns != -1){ + enemies.add(new Point2(x, y)); + tiles[x][y].setOverlay(Blocks.air); + } + + if(tiles[x][y].block() instanceof BlockPart){ + tiles[x][y].setBlock(Blocks.air); + } + } + } + + Simplex simplex = new Simplex(Mathf.random(99999)); + + for(int x = 0; x < width; x++){ + for(int y = 0; y < height; y++){ + final double scl = 10; + Tile tile = tiles[x][y]; + int newX = Mathf.clamp((int)(simplex.octaveNoise2D(1, 1, 1.0 / scl, x, y) * distortion + x), 0, width - 1); + int newY = Mathf.clamp((int)(simplex.octaveNoise2D(1, 1, 1.0 / scl, x + 9999, y + 9999) * distortion + y), 0, height - 1); + + if(((tile.block() instanceof StaticWall + && tiles[newX][newY].block() instanceof StaticWall) + || (tile.block() == Blocks.air && !tiles[newX][newY].block().synthetic()) + || (tiles[newX][newY].block() == Blocks.air && tile.block() instanceof StaticWall))){ + tile.setBlock(tiles[newX][newY].block()); + } + + if(distortFloor){ + tile.setFloor(tiles[newX][newY].floor()); + if(tiles[newX][newY].overlay() != Blocks.spawn && tile.overlay() != Blocks.spawn){ + tile.setOverlay(tiles[newX][newY].overlay()); + } + } + + for(Decoration decor : decorations){ + if(x > 0 && y > 0 && (tiles[x - 1][y].block() == decor.wall || tiles[x][y - 1].block() == decor.wall)){ + continue; + } + + if(tile.block() == Blocks.air && !(decor.wall instanceof Floor) && tile.floor() == decor.floor && Mathf.chance(decor.chance)){ + tile.setBlock(decor.wall); + }else if(tile.floor() == decor.floor && decor.wall.isOverlay() && Mathf.chance(decor.chance)){ + tile.setOverlay(decor.wall); + }else if(tile.floor() == decor.floor && decor.wall.isFloor() && !decor.wall.isOverlay() && Mathf.chance(decor.chance)){ + tile.setFloor((Floor)decor.wall); + } + } + + if(tile.block() instanceof StorageBlock && !(tile.block() instanceof CoreBlock) && world.getZone() != null){ + for(Item item : world.getZone().resources){ + if(Mathf.chance(0.3)){ + tile.entity.items.add(item, Math.min(Mathf.random(500), tile.block().itemCapacity)); + } + } + } + } + } + + if(enemySpawns != -1){ + if(enemySpawns > enemies.size){ + throw new IllegalArgumentException("Enemy spawn pool greater than map spawn number for map: " + mapName); + } + + enemies.shuffle(); + for(int i = 0; i < enemySpawns; i++){ + Point2 point = enemies.get(i); + tiles[point.x][point.y].setOverlay(Blocks.spawn); + + int rad = 10, frad = 12; + + for(int x = -rad; x <= rad; x++){ + for(int y = -rad; y <= rad; y++){ + int wx = x + point.x, wy = y + point.y; + double dst = Mathf.dst(x, y); + if(dst < frad && Structs.inBounds(wx, wy, tiles) && (dst <= rad || Mathf.chance(0.5))){ + Tile tile = tiles[wx][wy]; + if(tile.overlay() != Blocks.spawn){ + tile.clearOverlay(); + } + } + } + } + } + } + + Point2 core = players.random(); + if(core == null){ + throw new IllegalArgumentException("All zone maps must have a core."); + } + + loadout.setup(core.x, core.y); + + world.prepareTiles(tiles); + world.setMap(map); + } + + public static class Decoration{ + public final Block floor; + public final Block wall; + public final double chance; + + public Decoration(Block floor, Block wall, double chance){ + this.floor = floor; + this.wall = wall; + this.chance = chance; + } + } +} diff --git a/core/src/io/anuke/mindustry/maps/generators/RandomGenerator.java b/core/src/io/anuke/mindustry/maps/generators/RandomGenerator.java new file mode 100644 index 0000000000..23591e86df --- /dev/null +++ b/core/src/io/anuke/mindustry/maps/generators/RandomGenerator.java @@ -0,0 +1,44 @@ +package io.anuke.mindustry.maps.generators; + +import io.anuke.arc.collection.StringMap; +import io.anuke.mindustry.content.Blocks; +import io.anuke.mindustry.maps.Map; +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.Tile; + +import static io.anuke.mindustry.Vars.world; + +public abstract class RandomGenerator extends Generator{ + protected Block floor; + protected Block block; + protected Block ore; + + public RandomGenerator(int width, int height){ + super(width, height); + } + + @Override + public void generate(Tile[][] tiles){ + for(int x = 0; x < width; x++){ + for(int y = 0; y < height; y++){ + floor = Blocks.air; + block = Blocks.air; + ore = Blocks.air; + generate(x, y); + tiles[x][y] = new Tile(x, y, floor.id, ore.id, block.id); + } + } + + decorate(tiles); + + world.setMap(new Map(new StringMap())); + } + + public abstract void decorate(Tile[][] tiles); + + /** + * Sets {@link #floor} and {@link #block} to the correct values as output. + * Before this method is called, both are set to {@link Blocks#air} as defaults. + */ + public abstract void generate(int x, int y); +} diff --git a/core/src/io/anuke/mindustry/maps/zonegen/DesertWastesGenerator.java b/core/src/io/anuke/mindustry/maps/zonegen/DesertWastesGenerator.java new file mode 100644 index 0000000000..31ed7981dd --- /dev/null +++ b/core/src/io/anuke/mindustry/maps/zonegen/DesertWastesGenerator.java @@ -0,0 +1,47 @@ +package io.anuke.mindustry.maps.zonegen; + +import io.anuke.arc.math.Mathf; +import io.anuke.mindustry.content.Blocks; +import io.anuke.mindustry.maps.generators.BasicGenerator; +import io.anuke.mindustry.world.Tile; + +public class DesertWastesGenerator extends BasicGenerator{ + + public DesertWastesGenerator(int width, int height){ + super(width, height, Blocks.oreCopper, Blocks.oreLead, Blocks.oreCoal, Blocks.oreCopper); + } + + @Override + public void generate(int x, int y){ + floor = Blocks.sand; + } + + @Override + public void decorate(Tile[][] tiles){ + ores(tiles); + terrain(tiles, Blocks.sandRocks, 60f, 1.5f, 0.9f); + + int rand = 40; + int border = 25; + int spawnX = Mathf.clamp(30 + Mathf.range(rand), border, width - border), spawnY = Mathf.clamp(30 + Mathf.range(rand), border, height - border); + int endX = Mathf.clamp(width - 30 + Mathf.range(rand), border, width - border), endY = Mathf.clamp(height - 30 + Mathf.range(rand), border, height - border); + + brush(tiles, pathfind(tiles, spawnX, spawnY, endX, endY, tile -> tile.solid() ? 5f : 0f, manhattan), 6); + brush(tiles, pathfind(tiles, spawnX, spawnY, endX, endY, tile -> tile.solid() ? 4f : 0f + (float)sim.octaveNoise2D(1, 1, 1f / 40f, tile.x, tile.y) * 20, manhattan), 5); + + erase(tiles, endX, endY, 10); + erase(tiles, spawnX, spawnY, 20); + distort(tiles, 20f, 4f); + inverseFloodFill(tiles, tiles[spawnX][spawnY], Blocks.sandRocks); + + noise(tiles, Blocks.salt, Blocks.saltRocks, 5, 0.6f, 200f, 0.55f); + noise(tiles, Blocks.darksand, Blocks.duneRocks, 5, 0.7f, 120f, 0.5f); + + tech(tiles); + overlay(tiles, Blocks.sand, Blocks.pebbles, 0.15f, 5, 0.8f, 30f, 0.62f); + //scatter(tiles, Blocks.sandRocks, Blocks.creeptree, 1f); + + tiles[endX][endY].setOverlay(Blocks.spawn); + loadout.setup(spawnX, spawnY); + } +} diff --git a/core/src/io/anuke/mindustry/maps/zonegen/OvergrowthGenerator.java b/core/src/io/anuke/mindustry/maps/zonegen/OvergrowthGenerator.java new file mode 100644 index 0000000000..0fec4b4879 --- /dev/null +++ b/core/src/io/anuke/mindustry/maps/zonegen/OvergrowthGenerator.java @@ -0,0 +1,43 @@ +package io.anuke.mindustry.maps.zonegen; + +import io.anuke.arc.math.Mathf; +import io.anuke.mindustry.content.Blocks; +import io.anuke.mindustry.maps.generators.BasicGenerator; +import io.anuke.mindustry.world.Tile; + +public class OvergrowthGenerator extends BasicGenerator{ + + public OvergrowthGenerator(int width, int height){ + super(width, height, Blocks.oreCopper, Blocks.oreLead, Blocks.oreCoal, Blocks.oreCopper); + } + + @Override + public void generate(int x, int y){ + floor = Blocks.moss; + } + + @Override + public void decorate(Tile[][] tiles){ + ores(tiles); + terrain(tiles, Blocks.sporePine, 70f, 1.4f, 1f); + + int rand = 40; + int border = 25; + int spawnX = Mathf.clamp(30 + Mathf.range(rand), border, width - border), spawnY = Mathf.clamp(30 + Mathf.range(rand), border, height - border); + int endX = Mathf.clamp(width - 30 + Mathf.range(rand), border, width - border), endY = Mathf.clamp(height - 30 + Mathf.range(rand), border, height - border); + + brush(tiles, pathfind(tiles, spawnX, spawnY, endX, endY, tile -> (tile.solid() ? 5f : 0f) + (float)sim.octaveNoise2D(1, 1, 1f / 50f, tile.x, tile.y) * 50, manhattan), 6); + brush(tiles, pathfind(tiles, spawnX, spawnY, endX, endY, tile -> (tile.solid() ? 4f : 0f) + (float)sim.octaveNoise2D(1, 1, 1f / 90f, tile.x+999, tile.y) * 70, manhattan), 5); + + erase(tiles, endX, endY, 10); + erase(tiles, spawnX, spawnY, 20); + distort(tiles, 20f, 4f); + inverseFloodFill(tiles, tiles[spawnX][spawnY], Blocks.sporerocks); + + noise(tiles, Blocks.darksandTaintedWater, Blocks.duneRocks, 4, 0.7f, 120f, 0.64f); + //scatter(tiles, Blocks.sporePine, Blocks.whiteTreeDead, 1f); + + tiles[endX][endY].setOverlay(Blocks.spawn); + loadout.setup(spawnX, spawnY); + } +} diff --git a/core/src/io/anuke/mindustry/net/Administration.java b/core/src/io/anuke/mindustry/net/Administration.java index cd412e9c25..c1edec3ecb 100644 --- a/core/src/io/anuke/mindustry/net/Administration.java +++ b/core/src/io/anuke/mindustry/net/Administration.java @@ -1,186 +1,62 @@ package io.anuke.mindustry.net; -import com.badlogic.gdx.utils.Array; -import com.badlogic.gdx.utils.IntMap; -import com.badlogic.gdx.utils.Json; -import com.badlogic.gdx.utils.ObjectMap; -import com.badlogic.gdx.utils.TimeUtils; -import io.anuke.mindustry.entities.Player; -import io.anuke.mindustry.world.Block; -import io.anuke.mindustry.world.Placement; -import io.anuke.mindustry.world.blocks.types.BlockPart; -import io.anuke.mindustry.world.blocks.types.Floor; -import io.anuke.mindustry.world.blocks.types.Rock; -import io.anuke.mindustry.world.blocks.types.StaticBlock; -import io.anuke.ucore.core.Settings; -import static io.anuke.mindustry.Vars.world; +import io.anuke.annotations.Annotations.Serialize; +import io.anuke.arc.Core; +import io.anuke.arc.collection.*; -public class Administration { - public static final int defaultMaxBrokenBlocks = 15; - public static final int defaultBreakCooldown = 1000*15; +import static io.anuke.mindustry.Vars.headless; - private Json json = new Json(); - /**All player info. Maps UUIDs to info. This persists throughout restarts.*/ +public class Administration{ + + /** All player info. Maps UUIDs to info. This persists throughout restarts. */ private ObjectMap playerInfo = new ObjectMap<>(); - /**Maps UUIDs to trace infos. This is wiped when a player logs off.*/ - private ObjectMap traceInfo = new ObjectMap<>(); - /**Maps packed coordinates to logs for that coordinate */ - private IntMap> editLogs = new IntMap<>(); - private Array bannedIPs = new Array<>(); public Administration(){ - Settings.defaultList( - "playerInfo", "{}", - "bannedIPs", "{}", - "antigrief", false, - "antigrief-max", defaultMaxBrokenBlocks, - "antigrief-cooldown", defaultBreakCooldown + Core.settings.defaults( + "strict", true, + "servername", "Server" ); load(); } - public boolean isAntiGrief(){ - return Settings.getBool("antigrief"); + public void setStrict(boolean on){ + Core.settings.put("strict", on); + Core.settings.save(); } - public boolean isValidateReplace(){ - return false; + public boolean getStrict(){ + return Core.settings.getBool("strict"); } - public void setAntiGrief(boolean antiGrief){ - Settings.putBool("antigrief", antiGrief); - Settings.save(); + public boolean allowsCustomClients(){ + return Core.settings.getBool("allow-custom", !headless); } - public void setAntiGriefParams(int maxBreak, int cooldown){ - Settings.putInt("antigrief-max", maxBreak); - Settings.putInt("antigrief-cooldown", cooldown); - Settings.save(); + public void setCustomClients(boolean allowed){ + Core.settings.put("allow-custom", allowed); + Core.settings.save(); } - public IntMap> getEditLogs() { - return editLogs; - } - - public void logEdit(int x, int y, Player player, Block block, int rotation, EditLog.EditAction action) { - if(block instanceof BlockPart || block instanceof Rock || block instanceof Floor || block instanceof StaticBlock) return; - if(editLogs.containsKey(x + y * world.width())) { - editLogs.get(x + y * world.width()).add(new EditLog(player.name, block, rotation, action)); - } - else { - Array logs = new Array<>(); - logs.add(new EditLog(player.name, block, rotation, action)); - editLogs.put(x + y * world.width(), logs); - } - } - - public void rollbackWorld(int rollbackTimes) { - for(IntMap.Entry> editLog : editLogs.entries()) { - int coords = editLog.key; - Array logs = editLog.value; - - for(int i = 0; i < rollbackTimes; i++) { - - EditLog log = logs.get(logs.size - 1); - - int x = coords % world.width(); - int y = coords / world.width(); - Block result = log.block; - int rotation = log.rotation; - - if(log.action == EditLog.EditAction.PLACE) { - Placement.breakBlock(x, y, false, false); - - Packets.BreakPacket packet = new Packets.BreakPacket(); - packet.x = (short) x; - packet.y = (short) y; - packet.playerid = 0; - - Net.send(packet, Net.SendMode.tcp); - } - else if(log.action == EditLog.EditAction.BREAK) { - Placement.placeBlock(x, y, result, rotation, false, false); - - Packets.PlacePacket packet = new Packets.PlacePacket(); - packet.x = (short) x; - packet.y = (short) y; - packet.rotation = (byte) rotation; - packet.playerid = 0; - packet.block = result.id; - - Net.send(packet, Net.SendMode.tcp); - } - - logs.removeIndex(logs.size - 1); - if(logs.size == 0) { - editLogs.remove(coords); - break; - } - } - } - } - - public boolean validateBreak(String id, String ip){ - if(!isAntiGrief() || isAdmin(id, ip)) return true; - - PlayerInfo info = getCreateInfo(id); - - if(info.lastBroken == null || info.lastBroken.length != Settings.getInt("antigrief-max")){ - info.lastBroken = new long[Settings.getInt("antigrief-max")]; - } - - long[] breaks = info.lastBroken; - - int shiftBy = 0; - for(int i = 0; i < breaks.length && breaks[i] != 0; i ++){ - if(TimeUtils.timeSinceMillis(breaks[i]) >= Settings.getInt("antigrief-cooldown")){ - shiftBy = i; - } - } - - for (int i = 0; i < breaks.length; i++) { - breaks[i] = (i + shiftBy >= breaks.length) ? 0 : breaks[i + shiftBy]; - } - - int remaining = 0; - for(int i = 0; i < breaks.length; i ++){ - if(breaks[i] == 0){ - remaining = breaks.length - i; - break; - } - } - - if(remaining == 0) return false; - - breaks[breaks.length - remaining] = TimeUtils.millis(); - return true; - } - - /**Call when a player joins to update their information here.*/ + /** Call when a player joins to update their information here. */ public void updatePlayerJoined(String id, String ip, String name){ PlayerInfo info = getCreateInfo(id); info.lastName = name; info.lastIP = ip; - info.timesJoined ++; + info.timesJoined++; if(!info.names.contains(name, false)) info.names.add(name); if(!info.ips.contains(ip, false)) info.ips.add(ip); } - /**Returns trace info by IP.*/ - public TraceInfo getTrace(String ip){ - if(!traceInfo.containsKey(ip)) traceInfo.put(ip, new TraceInfo(ip)); - - return traceInfo.get(ip); + public boolean banPlayer(String uuid){ + return banPlayerID(uuid) || banPlayerIP(getInfo(uuid).lastIP); } - public void clearTraces(){ - traceInfo.clear(); - } - - /**Bans a player by IP; returns whether this player was already banned. - * If there are players who at any point had this IP, they will be UUID banned as well.*/ + /** + * Bans a player by IP; returns whether this player was already banned. + * If there are players who at any point had this IP, they will be UUID banned as well. + */ public boolean banPlayerIP(String ip){ if(bannedIPs.contains(ip, false)) return false; @@ -197,7 +73,7 @@ public class Administration { return true; } - /**Bans a player by UUID; returns whether this player was already banned.*/ + /** Bans a player by UUID; returns whether this player was already banned. */ public boolean banPlayerID(String id){ if(playerInfo.containsKey(id) && playerInfo.get(id).banned) return false; @@ -209,8 +85,10 @@ public class Administration { return true; } - /**Unbans a player by IP; returns whether this player was banned in the first place. - * This method also unbans any player that was banned and had this IP.*/ + /** + * Unbans a player by IP; returns whether this player was banned in the first place. + * This method also unbans any player that was banned and had this IP. + */ public boolean unbanPlayerIP(String ip){ boolean found = bannedIPs.contains(ip, false); @@ -228,8 +106,10 @@ public class Administration { return found; } - /**Unbans a player by ID; returns whether this player was banned in the first place. - * This also unbans all IPs the player used.*/ + /** + * Unbans a player by ID; returns whether this player was banned in the first place. + * This also unbans all IPs the player used. + */ public boolean unbanPlayerID(String id){ PlayerInfo info = getCreateInfo(id); @@ -243,7 +123,9 @@ public class Administration { return true; } - /**Returns list of all players with admin status*/ + /** + * Returns list of all players with admin status + */ public Array getAdmins(){ Array result = new Array<>(); for(PlayerInfo info : playerInfo.values()){ @@ -254,7 +136,9 @@ public class Administration { return result; } - /**Returns list of all players with admin status*/ + /** + * Returns list of all players with admin status + */ public Array getBanned(){ Array result = new Array<>(); for(PlayerInfo info : playerInfo.values()){ @@ -265,26 +149,32 @@ public class Administration { return result; } - /**Returns all banned IPs. This does not include the IPs of ID-banned players.*/ + /** + * Returns all banned IPs. This does not include the IPs of ID-banned players. + */ public Array getBannedIPs(){ return bannedIPs; } - /**Makes a player an admin. Returns whether this player was already an admin.*/ - public boolean adminPlayer(String id, String ip){ + /** + * Makes a player an admin. Returns whether this player was already an admin. + */ + public boolean adminPlayer(String id, String usid){ PlayerInfo info = getCreateInfo(id); - if(info.admin) + if(info.admin && info.adminUsid != null && info.adminUsid.equals(usid)) return false; - info.validAdminIP = ip; + info.adminUsid = usid; info.admin = true; save(); return true; } - /**Makes a player no longer an admin. Returns whether this player was an admin in the first place.*/ + /** + * Makes a player no longer an admin. Returns whether this player was an admin in the first place. + */ public boolean unAdminPlayer(String id){ PlayerInfo info = getCreateInfo(id); @@ -305,16 +195,18 @@ public class Administration { return getCreateInfo(uuid).banned; } - public boolean isAdmin(String id, String ip){ + public boolean isAdmin(String id, String usid){ PlayerInfo info = getCreateInfo(id); - return info.admin && ip.equals(info.validAdminIP); + return info.admin && usid.equals(info.adminUsid); } - public Array findByName(String name, boolean last){ - Array result = new Array<>(); + /** Finds player info by IP, UUID and name. */ + public ObjectSet findByName(String name){ + ObjectSet result = new ObjectSet<>(); for(PlayerInfo info : playerInfo.values()){ - if(info.lastName.toLowerCase().equals(name.toLowerCase()) || (last && info.names.contains(name, false))){ + if(info.lastName.toLowerCase().equals(name.toLowerCase()) || (info.names.contains(name, false)) + || info.ips.contains(name, false) || info.id.equals(name)){ result.add(info); } } @@ -363,36 +255,47 @@ public class Administration { } public void save(){ - Settings.putString("playerInfo", json.toJson(playerInfo)); - Settings.putString("bannedIPs", json.toJson(bannedIPs)); - Settings.save(); + Core.settings.putObject("player-info", playerInfo); + Core.settings.putObject("banned-ips", bannedIPs); + Core.settings.save(); } + @SuppressWarnings("unchecked") private void load(){ - playerInfo = json.fromJson(ObjectMap.class, Settings.getString("playerInfo")); - bannedIPs = json.fromJson(Array.class, Settings.getString("bannedIPs")); + playerInfo = Core.settings.getObject("player-info", ObjectMap.class, ObjectMap::new); + bannedIPs = Core.settings.getObject("banned-ips", Array.class, Array::new); } + @Serialize public static class PlayerInfo{ public String id; public String lastName = "", lastIP = ""; - public String validAdminIP; public Array ips = new Array<>(); public Array names = new Array<>(); - public int timesKicked; //TODO not implemented! + public String adminUsid; + public int timesKicked; public int timesJoined; - public int totalBlockPlaced; - public int totalBlocksBroken; public boolean banned, admin; public long lastKicked; //last kicked timestamp - public long[] lastBroken; - PlayerInfo(String id){ this.id = id; } - private PlayerInfo(){} + public PlayerInfo(){ + } + } + + public static class TraceInfo{ + public String ip, uuid; + public boolean modded, mobile; + + public TraceInfo(String ip, String uuid, boolean modded, boolean mobile){ + this.ip = ip; + this.uuid = uuid; + this.modded = modded; + this.mobile = mobile; + } } } diff --git a/core/src/io/anuke/mindustry/net/ClientDebug.java b/core/src/io/anuke/mindustry/net/ClientDebug.java deleted file mode 100644 index 60b0eb0bdf..0000000000 --- a/core/src/io/anuke/mindustry/net/ClientDebug.java +++ /dev/null @@ -1,51 +0,0 @@ -package io.anuke.mindustry.net; - -import com.badlogic.gdx.utils.OrderedMap; -import com.badlogic.gdx.utils.TimeUtils; -import com.badlogic.gdx.utils.reflect.ClassReflection; - -public class ClientDebug { - private OrderedMap, Long> last = new OrderedMap<>(); - private int syncPlayers = 0; - private int syncEnemies = 0; - - public void handle(Object packet){ - last.put(packet.getClass(), TimeUtils.millis()); - } - - public void setSyncDebug(int players, int enemies){ - this.syncEnemies = enemies; - this.syncPlayers = players; - } - - public String getOut(){ - StringBuilder build = new StringBuilder(); - for(Class type : last.orderedKeys()){ - build.append(elapsed(type)); - build.append("\n"); - } - build.append("sync.players: "); - build.append(syncPlayers); - build.append("\n"); - build.append("sync.enemies: "); - build.append(syncEnemies); - build.append("\n"); - return build.toString(); - } - - private String elapsed(Class type){ - long t = last.get(type, -1L); - if(t == -1){ - return ClassReflection.getSimpleName(type) + ": "; - }else{ - float el = TimeUtils.timeSinceMillis(t) / 1000f; - String tu; - if(el > 1f){ - tu = (int)el + "s"; - }else{ - tu = (int)(el * 60) + "f"; - } - return ClassReflection.getSimpleName(type) + ": " + tu; - } - } -} diff --git a/core/src/io/anuke/mindustry/net/CrashSender.java b/core/src/io/anuke/mindustry/net/CrashSender.java new file mode 100644 index 0000000000..21d197866c --- /dev/null +++ b/core/src/io/anuke/mindustry/net/CrashSender.java @@ -0,0 +1,145 @@ +package io.anuke.mindustry.net; + +import io.anuke.arc.Core; +import io.anuke.arc.collection.ObjectMap; +import io.anuke.arc.function.Consumer; +import io.anuke.arc.util.Log; +import io.anuke.arc.util.OS; +import io.anuke.arc.util.Strings; +import io.anuke.arc.util.io.PropertiesUtils; +import io.anuke.arc.util.io.Streams; +import io.anuke.arc.util.serialization.JsonValue; +import io.anuke.arc.util.serialization.JsonValue.ValueType; +import io.anuke.arc.util.serialization.JsonWriter.OutputType; +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.game.Version; + +import java.io.*; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +public class CrashSender{ + + public static void send(Throwable exception, Consumer writeListener){ + try{ + exception.printStackTrace(); + + //don't create crash logs for me (anuke) or custom builds, as it's expected + if(System.getProperty("user.name").equals("anuke") || Version.build == -1) return; + + //attempt to load version regardless + if(Version.number == 0){ + try{ + ObjectMap map = new ObjectMap<>(); + PropertiesUtils.load(map, new InputStreamReader(CrashSender.class.getResourceAsStream("version.properties"))); + + Version.type = map.get("type"); + Version.number = Integer.parseInt(map.get("number")); + Version.modifier = map.get("modifier"); + if(map.get("build").contains(".")){ + String[] split = map.get("build").split("\\."); + Version.build = Integer.parseInt(split[0]); + Version.revision = Integer.parseInt(split[1]); + }else{ + Version.build = Strings.canParseInt(map.get("build")) ? Integer.parseInt(map.get("build")) : -1; + } + }catch(Throwable ignored){ + Log.err("Failed to parse version."); + } + } + + try{ + File file = new File(OS.getAppDataDirectoryString(Vars.appName), "crashes/crash-report-" + DateTimeFormatter.ofPattern("MM_dd_yyyy_HH_mm_ss").format(LocalDateTime.now()) + ".txt"); + new File(OS.getAppDataDirectoryString(Vars.appName)).mkdir(); + new BufferedOutputStream(new FileOutputStream(file), Streams.DEFAULT_BUFFER_SIZE).write(parseException(exception).getBytes()); + Files.createDirectories(Paths.get(OS.getAppDataDirectoryString(Vars.appName), "crashes")); + + writeListener.accept(file); + }catch(Throwable ignored){ + Log.err("Failed to save local crash report."); + } + + try{ + //check crash report setting + if(!Core.settings.getBool("crashreport", true)){ + return; + } + }catch(Throwable ignored){ + //if there's no settings init we don't know what the user wants but chances are it's an important crash, so send it anyway + } + + //do not send exceptions that occur for versions that can't be parsed + if(Version.number == 0){ + return; + } + + boolean netActive = false, netServer = false; + + //attempt to close connections, if applicable + try{ + netActive = Net.active(); + netServer = Net.server(); + Net.dispose(); + }catch(Throwable ignored){ + } + + JsonValue value = new JsonValue(ValueType.object); + + boolean fn = netActive, fs = netServer; + + //add all relevant info, ignoring exceptions + ex(() -> value.addChild("versionType", new JsonValue(Version.type))); + ex(() -> value.addChild("versionNumber", new JsonValue(Version.number))); + ex(() -> value.addChild("versionModifier", new JsonValue(Version.modifier))); + ex(() -> value.addChild("build", new JsonValue(Version.build))); + ex(() -> value.addChild("net", new JsonValue(fn))); + ex(() -> value.addChild("server", new JsonValue(fs))); + ex(() -> value.addChild("players", new JsonValue(Vars.playerGroup.size()))); + ex(() -> value.addChild("state", new JsonValue(Vars.state.getState().name()))); + ex(() -> value.addChild("os", new JsonValue(System.getProperty("os.name")))); + ex(() -> value.addChild("trace", new JsonValue(parseException(exception)))); + + boolean[] sent = {false}; + + Log.info("Sending crash report."); + //post to crash report URL + Core.net.httpPost(Vars.crashReportURL, value.toJson(OutputType.json), r -> { + Log.info("Crash sent successfully."); + sent[0] = true; + System.exit(1); + }, t -> { + t.printStackTrace(); + sent[0] = true; + System.exit(1); + }); + + //sleep until report is sent + try{ + while(!sent[0]){ + Thread.sleep(30); + } + }catch(InterruptedException ignored){ + } + }catch(Throwable death){ + death.printStackTrace(); + System.exit(1); + } + } + + private static String parseException(Throwable e){ + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + e.printStackTrace(pw); + return sw.toString(); + } + + private static void ex(Runnable r){ + try{ + r.run(); + }catch(Throwable t){ + t.printStackTrace(); + } + } +} diff --git a/core/src/io/anuke/mindustry/net/EditLog.java b/core/src/io/anuke/mindustry/net/EditLog.java deleted file mode 100644 index e9bbeec043..0000000000 --- a/core/src/io/anuke/mindustry/net/EditLog.java +++ /dev/null @@ -1,26 +0,0 @@ -package io.anuke.mindustry.net; - -import io.anuke.mindustry.entities.Player; -import io.anuke.mindustry.world.Block; - -public class EditLog { - public String playername; - public Block block; - public int rotation; - public EditAction action; - - EditLog(String playername, Block block, int rotation, EditAction action) { - this.playername = playername; - this.block = block; - this.rotation = rotation; - this.action = action; - } - - public String info() { - return String.format("Player: %s, Block: %s, Rotation: %s, Edit Action: %s", playername, block.name(), rotation, action.toString()); - } - - public enum EditAction { - PLACE, BREAK; - } -} diff --git a/core/src/io/anuke/mindustry/net/Host.java b/core/src/io/anuke/mindustry/net/Host.java index 631f74a726..77a281d8ec 100644 --- a/core/src/io/anuke/mindustry/net/Host.java +++ b/core/src/io/anuke/mindustry/net/Host.java @@ -1,19 +1,21 @@ package io.anuke.mindustry.net; -public class Host { +public class Host{ public final String name; public final String address; public final String mapname; public final int wave; public final int players; public final int version; + public final String versionType; - public Host(String name, String address, String mapname, int wave, int players, int version){ + public Host(String name, String address, String mapname, int wave, int players, int version, String versionType){ this.name = name; this.address = address; this.players = players; this.mapname = mapname; this.wave = wave; this.version = version; + this.versionType = versionType; } } diff --git a/core/src/io/anuke/mindustry/net/Interpolator.java b/core/src/io/anuke/mindustry/net/Interpolator.java new file mode 100644 index 0000000000..0201f980ce --- /dev/null +++ b/core/src/io/anuke/mindustry/net/Interpolator.java @@ -0,0 +1,57 @@ +package io.anuke.mindustry.net; + +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.Vector2; +import io.anuke.arc.util.Time; + +public class Interpolator{ + //used for movement + public Vector2 target = new Vector2(); + public Vector2 last = new Vector2(); + public float[] targets = {}; + public long lastUpdated, updateSpacing; + + //current state + public Vector2 pos = new Vector2(); + public float[] values = {}; + + public void read(float cx, float cy, float x, float y, float... target1ds){ + if(lastUpdated != 0) updateSpacing = Time.timeSinceMillis(lastUpdated); + + lastUpdated = Time.millis(); + + targets = target1ds; + last.set(cx, cy); + target.set(x, y); + } + + public void reset(){ + values = new float[0]; + targets = new float[0]; + target.setZero(); + last.setZero(); + lastUpdated = 0; + updateSpacing = 16; //1 frame + pos.setZero(); + } + + public void update(){ + if(lastUpdated != 0 && updateSpacing != 0){ + float timeSinceUpdate = Time.timeSinceMillis(lastUpdated); + float alpha = Math.min(timeSinceUpdate / updateSpacing, 2f); + + pos.set(last).lerpPast(target, alpha); + + if(values.length != targets.length){ + values = new float[targets.length]; + } + + for(int i = 0; i < values.length; i++){ + values[i] = Mathf.slerp(values[i], targets[i], alpha); + } + }else{ + pos.set(target); + } + + } +} \ No newline at end of file diff --git a/core/src/io/anuke/mindustry/net/Net.java b/core/src/io/anuke/mindustry/net/Net.java index 43e65dcd72..c80552996f 100644 --- a/core/src/io/anuke/mindustry/net/Net.java +++ b/core/src/io/anuke/mindustry/net/Net.java @@ -1,311 +1,401 @@ package io.anuke.mindustry.net; -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.Net.HttpRequest; -import com.badlogic.gdx.Net.HttpResponse; -import com.badlogic.gdx.Net.HttpResponseListener; -import com.badlogic.gdx.net.HttpRequestBuilder; -import com.badlogic.gdx.utils.Array; -import com.badlogic.gdx.utils.IntMap; -import com.badlogic.gdx.utils.ObjectMap; -import com.badlogic.gdx.utils.reflect.ClassReflection; +import io.anuke.arc.Core; +import io.anuke.arc.collection.*; +import io.anuke.arc.function.BiConsumer; +import io.anuke.arc.function.Consumer; +import io.anuke.arc.util.*; +import io.anuke.arc.util.pooling.Pools; import io.anuke.mindustry.core.Platform; -import io.anuke.mindustry.net.Packet.ImportantPacket; -import io.anuke.mindustry.net.Packet.UnimportantPacket; -import io.anuke.mindustry.net.Streamable.StreamBegin; +import io.anuke.mindustry.gen.Call; +import io.anuke.mindustry.net.Packets.*; import io.anuke.mindustry.net.Streamable.StreamBuilder; -import io.anuke.mindustry.net.Streamable.StreamChunk; -import io.anuke.ucore.core.Timers; -import io.anuke.ucore.function.BiConsumer; -import io.anuke.ucore.function.Consumer; -import io.anuke.ucore.util.Log; import java.io.IOException; +import java.nio.BufferOverflowException; +import java.nio.BufferUnderflowException; import static io.anuke.mindustry.Vars.*; +@SuppressWarnings("unchecked") public class Net{ - private static boolean server; - private static boolean active; - private static boolean clientLoaded; - private static Array packetQueue = new Array<>(); - private static ObjectMap, Consumer> listeners = new ObjectMap<>(); - private static ObjectMap, Consumer> clientListeners = new ObjectMap<>(); - private static ObjectMap, BiConsumer> serverListeners = new ObjectMap<>(); - private static ClientProvider clientProvider; - private static ServerProvider serverProvider; + private static boolean server; + private static boolean active; + private static boolean clientLoaded; + private static Array packetQueue = new Array<>(); + private static ObjectMap, Consumer> clientListeners = new ObjectMap<>(); + private static ObjectMap, BiConsumer> serverListeners = new ObjectMap<>(); + private static ClientProvider clientProvider; + private static ServerProvider serverProvider; + private static IntMap streams = new IntMap<>(); - private static IntMap streams = new IntMap<>(); + /** Display a network error. Call on the graphics thread. */ + public static void showError(Throwable e){ - /**Display a network error.*/ - public static void showError(String text){ - if(!headless){ - ui.showError(text); - }else{ - Log.err(text); - } - } + if(!headless){ - /**Sets the client loaded status, or whether it will recieve normal packets from the server.*/ - public static void setClientLoaded(boolean loaded){ - clientLoaded = loaded; + Throwable t = e; + while(t.getCause() != null){ + t = t.getCause(); + } - if(loaded){ - //handle all packets that were skipped while loading - for(int i = 0; i < packetQueue.size; i ++){ - Log.info("Processing {0} packet post-load.", ClassReflection.getSimpleName(packetQueue.get(i).getClass())); - handleClientReceived(packetQueue.get(i)); - } - } - //clear inbound packet queue - packetQueue.clear(); - } - - /**Connect to an address.*/ - public static void connect(String ip, int port) throws IOException{ - if(!active) { - clientProvider.connect(ip, port); - active = true; - server = false; - }else{ - throw new IOException("Already connected!"); - } - } + String error = t.getMessage() == null ? "" : t.getMessage().toLowerCase(); + String type = t.getClass().toString().toLowerCase(); + boolean isError = false; - /**Host a server at an address*/ - public static void host(int port) throws IOException{ - serverProvider.host(port); - active = true; - server = true; + if(e instanceof BufferUnderflowException || e instanceof BufferOverflowException){ + error = Core.bundle.get("error.io"); + }else if(error.equals("mismatch")){ + error = Core.bundle.get("error.mismatch"); + }else if(error.contains("port out of range") || error.contains("invalid argument") || (error.contains("invalid") && error.contains("address")) || Strings.parseException(e, true).contains("address associated")){ + error = Core.bundle.get("error.invalidaddress"); + }else if(error.contains("connection refused") || error.contains("route to host") || type.contains("unknownhost")){ + error = Core.bundle.get("error.unreachable"); + }else if(type.contains("timeout")){ + error = Core.bundle.get("error.timedout"); + }else if(error.equals("alreadyconnected")){ + error = Core.bundle.get("error.alreadyconnected"); + }else if(!error.isEmpty()){ + error = Core.bundle.get("error.any") + "\n" + Strings.parseException(e, true); + isError = true; + } - Timers.runTask(60f, Platform.instance::updateRPC); - } + if(isError){ + ui.showError(Core.bundle.format("connectfail", error)); + }else{ + ui.showText("", Core.bundle.format("connectfail", error)); + } + ui.loadfrag.hide(); + + if(Net.client()){ + netClient.disconnectQuietly(); + } + } + + Log.err(e); + } + + /** + * Sets the client loaded status, or whether it will recieve normal packets from the server. + */ + public static void setClientLoaded(boolean loaded){ + clientLoaded = loaded; + + if(loaded){ + //handle all packets that were skipped while loading + for(int i = 0; i < packetQueue.size; i++){ + handleClientReceived(packetQueue.get(i)); + } + } + //clear inbound packet queue + packetQueue.clear(); + } + + /** + * Connect to an address. + */ + public static void connect(String ip, int port, Runnable success){ + try{ + if(!active){ + clientProvider.connect(ip, port, success); + active = true; + server = false; + }else{ + throw new IOException("alreadyconnected"); + } + }catch(IOException e){ + showError(e); + } + } + + /** + * Host a server at an address. + */ + public static void host(int port) throws IOException{ + serverProvider.host(port); + active = true; + server = true; + + Time.runTask(60f, Platform.instance::updateRPC); + } + + /** + * Closes the server. + */ + public static void closeServer(){ + for(NetConnection con : getConnections()){ + Call.onKick(con.id, KickReason.serverClose); + } - /**Closes the server.*/ - public static void closeServer(){ serverProvider.close(); server = false; active = false; } public static void disconnect(){ - clientProvider.disconnect(); - server = false; - active = false; - } + clientProvider.disconnect(); + server = false; + active = false; + } - /**Starts discovering servers on a different thread. Does not work with GWT. - * Callback is run on the main libGDX thread.*/ - public static void discoverServers(Consumer> cons){ - clientProvider.discover(cons); - } + public static byte[] compressSnapshot(byte[] input){ + return serverProvider.compressSnapshot(input); + } - /**Returns a list of all connections IDs.*/ - public static Array getConnections(){ - return (Array)serverProvider.getConnections(); - } + public static byte[] decompressSnapshot(byte[] input, int size){ + return clientProvider.decompressSnapshot(input, size); + } - /**Returns a connection by ID*/ - public static NetConnection getConnection(int id){ - return serverProvider.getByID(id); - } - - /**Send an object to all connected clients, or to the server if this is a client.*/ - public static void send(Object object, SendMode mode){ - if(server){ - if(serverProvider != null) serverProvider.send(object, mode); - }else { - if(clientProvider != null) clientProvider.send(object, mode); - } - } + /** + * Starts discovering servers on a different thread. + * Callback is run on the main libGDX thread. + */ + public static void discoverServers(Consumer cons, Runnable done){ + clientProvider.discover(cons, done); + } - /**Send an object to a certain client. Server-side only*/ - public static void sendTo(int id, Object object, SendMode mode){ - serverProvider.sendTo(id, object, mode); - } + /** + * Returns a list of all connections IDs. + */ + public static Array getConnections(){ + return (Array)serverProvider.getConnections(); + } - /**Send an object to everyone EXCEPT certain client. Server-side only*/ - public static void sendExcept(int id, Object object, SendMode mode){ - serverProvider.sendExcept(id, object, mode); - } + /** + * Returns a connection by ID + */ + public static NetConnection getConnection(int id){ + return serverProvider.getByID(id); + } - /**Send a stream to a specific client. Server-side only.*/ - public static void sendStream(int id, Streamable stream){ - serverProvider.sendStream(id, stream); - } - - /**Sets the net clientProvider, e.g. what handles sending, recieving and connecting to a server.*/ - public static void setClientProvider(ClientProvider provider){ - Net.clientProvider = provider; - } + /** + * Send an object to all connected clients, or to the server if this is a client. + */ + public static void send(Object object, SendMode mode){ + if(server){ + if(serverProvider != null) serverProvider.send(object, mode); + }else{ + if(clientProvider != null) clientProvider.send(object, mode); + } + } - /**Sets the net serverProvider, e.g. what handles hosting a server.*/ - public static void setServerProvider(ServerProvider provider){ - Net.serverProvider = provider; - } + /** + * Send an object to a certain client. Server-side only + */ + public static void sendTo(int id, Object object, SendMode mode){ + serverProvider.sendTo(id, object, mode); + } - /**Registers a common listener for when an object is recieved. Fired on both client and serve.r*/ - public static void handle(Class type, Consumer listener){ - listeners.put(type, listener); - } + /** + * Send an object to everyone EXCEPT certain client. Server-side only + */ + public static void sendExcept(int id, Object object, SendMode mode){ + serverProvider.sendExcept(id, object, mode); + } - /**Registers a client listener for when an object is recieved.*/ - public static void handleClient(Class type, Consumer listener){ - clientListeners.put(type, listener); - } + /** + * Send a stream to a specific client. Server-side only. + */ + public static void sendStream(int id, Streamable stream){ + serverProvider.sendStream(id, stream); + } - /**Registers a server listener for when an object is recieved.*/ - public static void handleServer(Class type, BiConsumer listener){ - serverListeners.put(type, (BiConsumer) listener); - } - - /**Call to handle a packet being recieved for the client.*/ - public static void handleClientReceived(Object object){ - if(debugNet) clientDebug.handle(object); + /** + * Sets the net clientProvider, e.g. what handles sending, recieving and connecting to a server. + */ + public static void setClientProvider(ClientProvider provider){ + Net.clientProvider = provider; + } - if(object instanceof StreamBegin) { - StreamBegin b = (StreamBegin) object; - streams.put(b.id, new StreamBuilder(b)); - }else if(object instanceof StreamChunk) { - StreamChunk c = (StreamChunk)object; - StreamBuilder builder = streams.get(c.id); - if(builder == null){ - throw new RuntimeException("Recieved stream chunk without a StreamBegin beforehand!"); - } - builder.add(c.data); - if(builder.isDone()){ - streams.remove(builder.id); - handleClientReceived(builder.build()); - } - }else if(clientListeners.get(object.getClass()) != null || - listeners.get(object.getClass()) != null){ - if(clientLoaded || object instanceof ImportantPacket){ - if(clientListeners.get(object.getClass()) != null) clientListeners.get(object.getClass()).accept(object); - if(listeners.get(object.getClass()) != null) listeners.get(object.getClass()).accept(object); - }else if(!(object instanceof UnimportantPacket)){ - packetQueue.add(object); - Log.info("Queuing packet {0}.", ClassReflection.getSimpleName(object.getClass())); - } - }else{ - Log.err("Unhandled packet type: '{0}'!", ClassReflection.getSimpleName(object.getClass())); - } - } + /** + * Sets the net serverProvider, e.g. what handles hosting a server. + */ + public static void setServerProvider(ServerProvider provider){ + Net.serverProvider = provider; + } - /**Call to handle a packet being recieved for the server.*/ - public static void handleServerReceived(int connection, Object object){ - if(debugNet) serverDebug.handle(connection, object); + /** + * Registers a client listener for when an object is recieved. + */ + public static void handleClient(Class type, Consumer listener){ + clientListeners.put(type, listener); + } - if(serverListeners.get(object.getClass()) != null || listeners.get(object.getClass()) != null){ - if(serverListeners.get(object.getClass()) != null) serverListeners.get(object.getClass()).accept(connection, object); - if(listeners.get(object.getClass()) != null) listeners.get(object.getClass()).accept(object); - }else{ - Log.err("Unhandled packet type: '{0}'!", ClassReflection.getSimpleName(object.getClass())); - } - } + /** + * Registers a server listener for when an object is recieved. + */ + public static void handleServer(Class type, BiConsumer listener){ + serverListeners.put(type, (BiConsumer)listener); + } - /**Pings a host in an new thread. If an error occured, failed() should be called with the exception. */ - public static void pingHost(String address, int port, Consumer valid, Consumer failed){ - clientProvider.pingHost(address, port, valid, failed); - } + /** + * Call to handle a packet being recieved for the client. + */ + public static void handleClientReceived(Object object){ - /**Update client ping.*/ - public static void updatePing(){ - clientProvider.updatePing(); - } + if(object instanceof StreamBegin){ + StreamBegin b = (StreamBegin)object; + streams.put(b.id, new StreamBuilder(b)); + }else if(object instanceof StreamChunk){ + StreamChunk c = (StreamChunk)object; + StreamBuilder builder = streams.get(c.id); + if(builder == null){ + throw new RuntimeException("Recieved stream chunk without a StreamBegin beforehand!"); + } + builder.add(c.data); + if(builder.isDone()){ + streams.remove(builder.id); + handleClientReceived(builder.build()); + } + }else if(clientListeners.get(object.getClass()) != null){ - /**Get the client ping. Only valid after updatePing().*/ - public static int getPing(){ - return server() ? 0 : clientProvider.getPing(); - } - - /**Whether the net is active, e.g. whether this is a multiplayer game.*/ - public static boolean active(){ - return active; - } - - /**Whether this is a server or not.*/ - public static boolean server(){ - return server && active; - } + if(clientLoaded || ((object instanceof Packet) && ((Packet)object).isImportant())){ + if(clientListeners.get(object.getClass()) != null) + clientListeners.get(object.getClass()).accept(object); + Pools.free(object); + }else if(!((object instanceof Packet) && ((Packet)object).isUnimportant())){ + packetQueue.add(object); + }else{ + Pools.free(object); + } + }else{ + Log.err("Unhandled packet type: '{0}'!", object); + } + } - /**Whether this is a client or not.*/ - public static boolean client(){ - return !server && active; - } + /** + * Call to handle a packet being recieved for the server. + */ + public static void handleServerReceived(int connection, Object object){ - public static void dispose(){ - if(clientProvider != null) clientProvider.dispose(); - if(serverProvider != null) serverProvider.dispose(); - clientProvider = null; - serverProvider = null; - server = false; - active = false; - } + if(serverListeners.get(object.getClass()) != null){ + if(serverListeners.get(object.getClass()) != null) + serverListeners.get(object.getClass()).accept(connection, object); + Pools.free(object); + }else{ + Log.err("Unhandled packet type: '{0}'!", object.getClass()); + } + } - public static void http(String url, String method, Consumer listener, Consumer failure){ - HttpRequest req = new HttpRequestBuilder().newRequest() - .method(method).url(url).build(); + /** + * Pings a host in an new thread. If an error occured, failed() should be called with the exception. + */ + public static void pingHost(String address, int port, Consumer valid, Consumer failed){ + clientProvider.pingHost(address, port, valid, failed); + } - Gdx.net.sendHttpRequest(req, new HttpResponseListener() { - @Override - public void handleHttpResponse(HttpResponse httpResponse) { - listener.accept(httpResponse.getResultAsString()); - } + /** + * Update client ping. + */ + public static void updatePing(){ + clientProvider.updatePing(); + } - @Override - public void failed(Throwable t) { - failure.accept(t); - } + /** + * Get the client ping. Only valid after updatePing(). + */ + public static int getPing(){ + return server() ? 0 : clientProvider.getPing(); + } - @Override - public void cancelled() {} - }); - } + /** + * Whether the net is active, e.g. whether this is a multiplayer game. + */ + public static boolean active(){ + return active; + } - /**Client implementation.*/ - public interface ClientProvider { - /**Connect to a server.*/ - void connect(String ip, int port) throws IOException; - /**Send an object to the server.*/ - void send(Object object, SendMode mode); - /**Update the ping. Should be done every second or so.*/ - void updatePing(); - /**Get ping in milliseconds. Will only be valid after a call to updatePing.*/ - int getPing(); - /**Disconnect from the server.*/ - void disconnect(); - /**Discover servers. This should run the callback regardless of whether any servers are found. Should not block. - * Callback should be run on libGDX main thread.*/ - void discover(Consumer> callback); - /**Ping a host. If an error occured, failed() should be called with the exception. */ + /** + * Whether this is a server or not. + */ + public static boolean server(){ + return server && active; + } + + /** + * Whether this is a client or not. + */ + public static boolean client(){ + return !server && active; + } + + public static void dispose(){ + if(clientProvider != null) clientProvider.dispose(); + if(serverProvider != null) serverProvider.dispose(); + clientProvider = null; + serverProvider = null; + server = false; + active = false; + } + + public enum SendMode{ + tcp, udp + } + + /** Client implementation. */ + public interface ClientProvider{ + /** Connect to a server. */ + void connect(String ip, int port, Runnable success) throws IOException; + + /** Send an object to the server. */ + void send(Object object, SendMode mode); + + /** Update the ping. Should be done every second or so. */ + void updatePing(); + + /** Get ping in milliseconds. Will only be valid after a call to updatePing. */ + int getPing(); + + /** Disconnect from the server. */ + void disconnect(); + + /** Decompress an input snapshot byte array. */ + byte[] decompressSnapshot(byte[] input, int size); + + /** + * Discover servers. This should run the callback regardless of whether any servers are found. Should not block. + * Callback should be run on libGDX main thread. + * @param done is the callback that should run after discovery. + */ + void discover(Consumer callback, Runnable done); + + /** Ping a host. If an error occured, failed() should be called with the exception. */ void pingHost(String address, int port, Consumer valid, Consumer failed); - /**Close all connections.*/ - void dispose(); - } - /**Server implementation.*/ - public interface ServerProvider { - /**Host a server at specified port.*/ - void host(int port) throws IOException; - /**Sends a large stream of data to a specific client.*/ - void sendStream(int id, Streamable stream); - /**Send an object to everyone connected.*/ - void send(Object object, SendMode mode); - /**Send an object to a specific client ID.*/ - void sendTo(int id, Object object, SendMode mode); - /**Send an object to everyone except a client ID.*/ - void sendExcept(int id, Object object, SendMode mode); - /**Close the server connection.*/ - void close(); - /**Return all connected users.*/ - Array getConnections(); - /**Returns a connection by ID.*/ - NetConnection getByID(int id); - /**Close all connections.*/ - void dispose(); - } + /** Close all connections. */ + void dispose(); + } - public enum SendMode{ - tcp, udp - } + /** Server implementation. */ + public interface ServerProvider{ + /** Host a server at specified port. */ + void host(int port) throws IOException; + + /** Sends a large stream of data to a specific client. */ + void sendStream(int id, Streamable stream); + + /** Send an object to everyone connected. */ + void send(Object object, SendMode mode); + + /** Send an object to a specific client ID. */ + void sendTo(int id, Object object, SendMode mode); + + /** Send an object to everyone except a client ID. */ + void sendExcept(int id, Object object, SendMode mode); + + /** Close the server connection. */ + void close(); + + /** Compress an input snapshot byte array. */ + byte[] compressSnapshot(byte[] input); + + /** Return all connected users. */ + Array getConnections(); + + /** Returns a connection by ID. */ + NetConnection getByID(int id); + + /** Close all connections. */ + void dispose(); + } } diff --git a/core/src/io/anuke/mindustry/net/NetConnection.java b/core/src/io/anuke/mindustry/net/NetConnection.java index ba6bfbf80c..2bb53fb9f7 100644 --- a/core/src/io/anuke/mindustry/net/NetConnection.java +++ b/core/src/io/anuke/mindustry/net/NetConnection.java @@ -2,15 +2,32 @@ package io.anuke.mindustry.net; import io.anuke.mindustry.net.Net.SendMode; -public abstract class NetConnection { +public abstract class NetConnection{ public final int id; public final String address; + public boolean modclient; + public boolean mobile; + + /** ID of last recieved client snapshot. */ + public int lastRecievedClientSnapshot = -1; + /** Timestamp of last recieved snapshot. */ + public long lastRecievedClientTime; + + public boolean hasConnected = false; + public boolean hasBegunConnecting = false; + public float viewWidth, viewHeight, viewX, viewY; + public NetConnection(int id, String address){ this.id = id; this.address = address; } + public boolean isConnected(){ + return true; + } + public abstract void send(Object object, SendMode mode); + public abstract void close(); } diff --git a/core/src/io/anuke/mindustry/net/NetEvents.java b/core/src/io/anuke/mindustry/net/NetEvents.java deleted file mode 100644 index c9603e92fc..0000000000 --- a/core/src/io/anuke/mindustry/net/NetEvents.java +++ /dev/null @@ -1,196 +0,0 @@ -package io.anuke.mindustry.net; - -import io.anuke.mindustry.Vars; -import io.anuke.mindustry.entities.BulletType; -import io.anuke.mindustry.entities.Player; -import io.anuke.mindustry.entities.TileEntity; -import io.anuke.mindustry.entities.enemies.Enemy; -import io.anuke.mindustry.net.Net.SendMode; -import io.anuke.mindustry.net.Packets.*; -import io.anuke.mindustry.resource.Item; -import io.anuke.mindustry.resource.Weapon; -import io.anuke.mindustry.world.Block; -import io.anuke.mindustry.world.Tile; -import io.anuke.ucore.entities.Entity; - -import static io.anuke.mindustry.Vars.*; - -public class NetEvents { - - public static void handleFriendlyFireChange(boolean enabled){ - FriendlyFireChangePacket packet = new FriendlyFireChangePacket(); - packet.enabled = enabled; - - netCommon.sendMessage(enabled ? "[accent]Friendly fire enabled." : "[accent]Friendly fire disabled."); - - Net.send(packet, SendMode.tcp); - } - - public static void handleGameOver(){ - Net.send(new GameOverPacket(), SendMode.tcp); - } - - public static void handleBullet(BulletType type, Entity owner, float x, float y, float angle, short damage){ - BulletPacket packet = new BulletPacket(); - packet.x = x; - packet.y = y; - packet.angle = angle; - packet.damage = damage; - packet.owner = owner.id; - packet.type = type.id; - Net.send(packet, SendMode.udp); - } - - public static void handleEnemyDeath(Enemy enemy){ - EnemyDeathPacket packet = new EnemyDeathPacket(); - packet.id = enemy.id; - Net.send(packet, SendMode.tcp); - } - - public static void handleBlockDestroyed(TileEntity entity){ - BlockDestroyPacket packet = new BlockDestroyPacket(); - packet.position = entity.tile.packedPosition(); - Net.send(packet, SendMode.tcp); - } - - public static void handleBlockDamaged(TileEntity entity){ - BlockUpdatePacket packet = new BlockUpdatePacket(); - packet.health = (int)entity.health; - packet.position = entity.tile.packedPosition(); - Net.send(packet, SendMode.udp); - } - - public static void handlePlayerDeath(){ - PlayerDeathPacket packet = new PlayerDeathPacket(); - packet.id = Vars.player.id; - Net.send(packet, SendMode.tcp); - } - - public static void handleBlockConfig(Tile tile, byte data){ - BlockConfigPacket packet = new BlockConfigPacket(); - packet.data = data; - packet.position = tile.packedPosition(); - Net.send(packet, SendMode.tcp); - } - - public static void handleBlockTap(Tile tile){ - BlockTapPacket packet = new BlockTapPacket(); - packet.position = tile.packedPosition(); - Net.send(packet, SendMode.tcp); - } - - public static void handleWeaponSwitch(){ - WeaponSwitchPacket packet = new WeaponSwitchPacket(); - packet.left = Vars.player.weaponLeft.id; - packet.right = Vars.player.weaponRight.id; - packet.playerid = Vars.player.id; - Net.send(packet, SendMode.tcp); - } - - public static void handleUpgrade(Weapon weapon){ - UpgradePacket packet = new UpgradePacket(); - packet.id = weapon.id; - Net.send(packet, SendMode.tcp); - } - - public static void handleSendMessage(String message){ - ChatPacket packet = new ChatPacket(); - packet.text = message; - packet.name = player.name; - packet.id = player.id; - Net.send(packet, SendMode.tcp); - - if(Net.server() && !headless){ - ui.chatfrag.addMessage(message, netCommon.colorizeName(player.id, player.name)); - } - } - - public static void handleShoot(Weapon weapon, float x, float y, float angle){ - ShootPacket packet = new ShootPacket(); - packet.weaponid = weapon.id; - packet.x = x; - packet.y = y; - packet.rotation = angle; - packet.playerid = Vars.player.id; - Net.send(packet, SendMode.udp); - } - - public static void handlePlace(int x, int y, Block block, int rotation){ - PlacePacket packet = new PlacePacket(); - packet.x = (short)x; - packet.y = (short)y; - packet.rotation = (byte)rotation; - packet.playerid = Vars.player.id; - packet.block = block.id; - Net.send(packet, SendMode.tcp); - } - - public static void handleBreak(int x, int y){ - BreakPacket packet = new BreakPacket(); - packet.x = (short)x; - packet.y = (short)y; - Net.send(packet, SendMode.tcp); - } - - public static void handleTransfer(Tile tile, byte rotation, Item item){ - ItemTransferPacket packet = new ItemTransferPacket(); - packet.position = tile.packedPosition(); - packet.rotation = rotation; - packet.itemid = (byte)item.id; - Net.send(packet, SendMode.udp); - } - - public static void handleItemSet(Tile tile, Item item, byte amount){ - ItemSetPacket packet = new ItemSetPacket(); - packet.position = tile.packedPosition(); - packet.itemid = (byte)item.id; - packet.amount = amount; - Net.send(packet, SendMode.udp); - } - - public static void handleOffload(Tile tile, Item item){ - ItemOffloadPacket packet = new ItemOffloadPacket(); - packet.position = tile.packedPosition(); - packet.itemid = (byte)item.id; - Net.send(packet, SendMode.udp); - } - - public static void handleAdminSet(Player player, boolean admin){ - PlayerAdminPacket packet = new PlayerAdminPacket(); - packet.admin = admin; - packet.id = player.id; - player.isAdmin = admin; - Net.send(packet, SendMode.tcp); - } - - public static void handleAdministerRequest(Player target, AdminAction action){ - AdministerRequestPacket packet = new AdministerRequestPacket(); - packet.id = target.id; - packet.action = action; - Net.send(packet, SendMode.tcp); - } - - public static void handleTraceRequest(Player target){ - if(Net.client()) { - handleAdministerRequest(target, AdminAction.trace); - }else{ - ui.traces.show(target, netServer.admins.getTrace(Net.getConnection(target.clientid).address)); - } - } - - public static void handleBlockLogRequest(int x, int y) { - BlockLogRequestPacket packet = new BlockLogRequestPacket(); - packet.x = x; - packet.y = y; - packet.editlogs = Vars.currentEditLogs; - - Net.send(packet, SendMode.udp); - } - - public static void handleRollbackRequest(int rollbackTimes) { - RollbackRequestPacket packet = new RollbackRequestPacket(); - packet.rollbackTimes = rollbackTimes; - - Net.send(packet, SendMode.udp); - } -} diff --git a/core/src/io/anuke/mindustry/net/NetworkIO.java b/core/src/io/anuke/mindustry/net/NetworkIO.java index 912881d26d..1729f3d4e4 100644 --- a/core/src/io/anuke/mindustry/net/NetworkIO.java +++ b/core/src/io/anuke/mindustry/net/NetworkIO.java @@ -1,387 +1,111 @@ package io.anuke.mindustry.net; -import com.badlogic.gdx.graphics.Pixmap; -import com.badlogic.gdx.graphics.Pixmap.Format; -import com.badlogic.gdx.utils.ByteArray; -import com.badlogic.gdx.utils.TimeUtils; -import io.anuke.mindustry.entities.Player; -import io.anuke.mindustry.game.GameMode; -import io.anuke.mindustry.io.Version; -import io.anuke.mindustry.resource.Upgrade; -import io.anuke.mindustry.resource.Weapon; -import io.anuke.mindustry.world.*; -import io.anuke.mindustry.world.blocks.Blocks; -import io.anuke.mindustry.world.blocks.types.BlockPart; -import io.anuke.mindustry.world.blocks.types.Rock; -import io.anuke.ucore.core.Timers; -import io.anuke.ucore.entities.Entities; +import io.anuke.arc.Core; +import io.anuke.arc.util.Time; +import io.anuke.mindustry.entities.Entities; +import io.anuke.mindustry.entities.type.Player; +import io.anuke.mindustry.game.*; +import io.anuke.mindustry.io.JsonIO; +import io.anuke.mindustry.io.SaveIO; +import io.anuke.mindustry.maps.Map; import java.io.*; import java.nio.ByteBuffer; +import java.util.Arrays; import static io.anuke.mindustry.Vars.*; -public class NetworkIO { +public class NetworkIO{ - public static void writeMap(Map map, OutputStream os){ - try(DataOutputStream stream = new DataOutputStream(os)){ - stream.writeUTF(map.name); - stream.writeBoolean(map.oreGen); - - stream.writeShort(map.getWidth()); - stream.writeShort(map.getHeight()); - - int width = map.getWidth(); - int cap = map.getWidth() * map.getHeight(); - int pos = 0; - - while(pos < cap){ - int color = map.pixmap.getPixel(pos % width, pos / width); - byte id = ColorMapper.getColorID(color); - - int length = 1; - while(true){ - if(pos >= cap || length > 254){ - break; - } - - pos ++; - - int next = map.pixmap.getPixel(pos % width, pos / width); - if(next != color){ - pos --; - break; - }else{ - length ++; - } - } - - if(id == -1) id = 2; - stream.writeByte((byte)(length > 127 ? length - 256 : length)); - stream.writeByte(id); - - pos ++; - } - }catch (IOException e){ - throw new RuntimeException(e); - } - } - - public static Map loadMap(InputStream is){ - try(DataInputStream stream = new DataInputStream(is)){ - String name = stream.readUTF(); - boolean ores = stream.readBoolean(); - - short width = stream.readShort(); - short height = stream.readShort(); - Pixmap pixmap = new Pixmap(width, height, Format.RGBA8888); - - int pos = 0; - while(stream.available() > 0){ - int length = stream.readByte(); - byte id = stream.readByte(); - if(length < 0) length += 256; - int color = ColorMapper.getColorByID(id); - - for(int p = 0; p < length; p ++){ - pixmap.drawPixel(pos % width, pos / width,color); - pos ++; - } - } - - Map map = new Map(); - map.name = name; - map.oreGen = ores; - map.custom = true; - map.pixmap = pixmap; - map.visible = false; - map.id = -1; - - return map; - }catch (IOException e){ - throw new RuntimeException(e); - } - } - - public static void writeWorld(Player player, ByteArray upgrades, OutputStream os){ + public static void writeWorld(Player player, OutputStream os){ try(DataOutputStream stream = new DataOutputStream(os)){ + stream.writeUTF(JsonIO.write(state.rules)); + SaveIO.getSaveWriter().writeStringMap(stream, world.getMap().tags); - stream.writeFloat(Timers.time()); //timer time - stream.writeLong(TimeUtils.millis()); //timestamp + stream.writeInt(state.wave); + stream.writeFloat(state.wavetime); - //--GENERAL STATE-- - stream.writeByte(state.mode.ordinal()); //gamemode - stream.writeByte(world.getMap().custom ? -1 : world.getMap().id); //map ID + stream.writeInt(player.id); + player.write(stream); - stream.writeInt(state.wave); //wave - stream.writeFloat(state.wavetime); //wave countdown - stream.writeInt(state.enemies); //enemy amount - - stream.writeBoolean(state.friendlyFire); //friendly fire state - stream.writeInt(player.id); //player remap ID - stream.writeBoolean(player.isAdmin); - - //--INVENTORY-- - - for(int i = 0; i < state.inventory.getItems().length; i ++){ //items - stream.writeInt(state.inventory.getItems()[i]); - } - - stream.writeByte(upgrades.size); //upgrade data - - for(int i = 0; i < upgrades.size; i ++){ - stream.writeByte(upgrades.get(i)); - } - - //--MAP DATA-- - - //seed - stream.writeInt(world.getSeed()); - - int totalblocks = 0; - int totalrocks = 0; - - for(int x = 0; x < world.width(); x ++){ - for(int y = 0; y < world.height(); y ++){ - Tile tile = world.tile(x, y); - - if(tile.breakable()){ - if(tile.block() instanceof Rock){ - totalrocks ++; - }else{ - totalblocks ++; - } - } - } - } - - //amount of rocks - stream.writeInt(totalrocks); - - //write all rocks - for(int x = 0; x < world.width(); x ++) { - for (int y = 0; y < world.height(); y++) { - Tile tile = world.tile(x, y); - - if (tile.block() instanceof Rock) { - stream.writeInt(tile.packedPosition()); - } - } - } - - //tile amount - stream.writeInt(totalblocks); - - for(int x = 0; x < world.width(); x ++){ - for(int y = 0; y < world.height(); y ++){ - Tile tile = world.tile(x, y); - - if(tile.breakable() && !(tile.block() instanceof Rock)){ - - stream.writeInt(x + y*world.width()); //tile pos - //TODO will break if block number gets over BYTE_MAX - stream.writeByte(tile.block().id); //block ID - - if(tile.block() instanceof BlockPart){ - stream.writeByte(tile.link); - } - - if(tile.entity != null){ - stream.writeShort(tile.getPackedData()); - stream.writeShort((short)tile.entity.health); //health - - //items - for(int i = 0; i < tile.entity.items.length; i ++){ - stream.writeInt(tile.entity.items[i]); - } - - //timer data - - //amount of active timers - byte times = 0; - - for(; times < tile.entity.timer.getTimes().length; times ++){ - if(tile.entity.timer.getTimes()[times] <= 1){ - break; - } - } - - stream.writeByte(times); - - for(int i = 0; i < times; i ++){ - stream.writeFloat(tile.entity.timer.getTimes()[i]); - } - - tile.entity.write(stream); - } - } - } - } - - }catch (IOException e){ + SaveIO.getSaveWriter().writeMap(stream); + }catch(IOException e){ throw new RuntimeException(e); } } - /**Return whether a custom map is expected, and thus whether the client should wait for additional data.*/ public static void loadWorld(InputStream is){ try(DataInputStream stream = new DataInputStream(is)){ - float timerTime = stream.readFloat(); - long timestamp = stream.readLong(); + Time.clear(); + state.rules = JsonIO.read(Rules.class, stream.readUTF()); + world.setMap(new Map(SaveIO.getSaveWriter().readStringMap(stream))); - Timers.clear(); - Timers.resetTime(timerTime + (TimeUtils.timeSinceMillis(timestamp) / 1000f) * 60f); - - //general state - byte mode = stream.readByte(); - byte mapid = stream.readByte(); - - int wave = stream.readInt(); - float wavetime = stream.readFloat(); - int enemies = stream.readInt(); - boolean friendlyfire = stream.readBoolean(); - - state.enemies = enemies; - state.wave = wave; - state.wavetime = wavetime; - state.mode = GameMode.values()[mode]; - state.friendlyFire = friendlyfire; - - int pid = stream.readInt(); - boolean admin = stream.readBoolean(); - - //inventory - for(int i = 0; i < state.inventory.getItems().length; i ++){ - state.inventory.getItems()[i] = stream.readInt(); - } - - ui.hudfrag.updateItems(); - - control.upgrades().getWeapons().clear(); - control.upgrades().getWeapons().add(Weapon.blaster); - - byte weapons = stream.readByte(); - - for(int i = 0; i < weapons; i ++){ - control.upgrades().getWeapons().add((Weapon) Upgrade.getByID(stream.readByte())); - } - - player.weaponLeft = player.weaponRight = control.upgrades().getWeapons().peek(); - ui.hudfrag.updateWeapons(); + state.wave = stream.readInt(); + state.wavetime = stream.readFloat(); Entities.clear(); - player.id = pid; - player.isAdmin = admin; + int id = stream.readInt(); + player.resetNoAdd(); + player.read(stream); + player.resetID(id); player.add(); - //map - - int seed = stream.readInt(); - - world.loadMap(world.maps().getMap(mapid), seed); - renderer.clearTiles(); - - player.set(world.getSpawnX(), world.getSpawnY()); - - for(int x = 0; x < world.width(); x ++){ - for(int y = 0; y < world.height(); y ++){ - Tile tile = world.tile(x, y); - - //remove breakables like rocks - if(tile.breakable()){ - world.tile(x, y).setBlock(Blocks.air); - } - } - } - - int rocks = stream.readInt(); - - for(int i = 0; i < rocks; i ++){ - int pos = stream.readInt(); - Tile tile = world.tile(pos % world.width(), pos / world.width()); - Block result = WorldGenerator.rocks.get(tile.floor()); - if(result != null) tile.setBlock(result); - } - - int tiles = stream.readInt(); - - for(int i = 0; i < tiles; i ++){ - int pos = stream.readInt(); - byte blockid = stream.readByte(); - - Tile tile = world.tile(pos % world.width(), pos / world.width()); - tile.setBlock(Block.getByID(blockid)); - - if(tile.block() == Blocks.blockpart){ - tile.link = stream.readByte(); - } - - if(tile.entity != null){ - short data = stream.readShort(); - short health = stream.readShort(); - - tile.entity.health = health; - tile.setPackedData(data); - - for(int j = 0; j < tile.entity.items.length; j ++){ - tile.entity.items[j] = stream.readInt(); - } - - byte timers = stream.readByte(); - for(int time = 0; time < timers; time ++){ - tile.entity.timer.getTimes()[time] = stream.readFloat(); - } - - tile.entity.read(stream); - } - } - - }catch (IOException e){ + SaveIO.getSaveWriter().readMap(stream, world.context); + }catch(IOException e){ throw new RuntimeException(e); } } public static ByteBuffer writeServerData(){ - int maxlen = 32; + String name = (headless ? Core.settings.getString("servername") : player.name); + String map = world.getMap() == null ? "None" : world.getMap().name(); - String host = (headless ? "Server" : player.name); - String map = world.getMap().name; + ByteBuffer buffer = ByteBuffer.allocate(256); - host = host.substring(0, Math.min(host.length(), maxlen)); - map = map.substring(0, Math.min(map.length(), maxlen)); - - ByteBuffer buffer = ByteBuffer.allocate(128); - - buffer.put((byte)host.getBytes().length); - buffer.put(host.getBytes()); - - buffer.put((byte)map.getBytes().length); - buffer.put(map.getBytes()); + writeString(buffer, name, 100); + writeString(buffer, map); buffer.putInt(playerGroup.size()); buffer.putInt(state.wave); buffer.putInt(Version.build); + writeString(buffer, Version.type); + //TODO additional information: + // - gamemode ID/name (just pick the closest one?) return buffer; } public static Host readServerData(String hostAddress, ByteBuffer buffer){ - byte hlength = buffer.get(); - byte[] hb = new byte[hlength]; - buffer.get(hb); - - byte mlength = buffer.get(); - byte[] mb = new byte[mlength]; - buffer.get(mb); - - String host = new String(hb); - String map = new String(mb); - + String host = readString(buffer); + String map = readString(buffer); int players = buffer.getInt(); int wave = buffer.getInt(); int version = buffer.getInt(); + String vertype = readString(buffer); - return new Host(host, hostAddress, map, wave, players, version); + return new Host(host, hostAddress, map, wave, players, version, vertype); + } + + private static void writeString(ByteBuffer buffer, String string, int maxlen){ + byte[] bytes = string.getBytes(charset); + //truncating this way may lead to wierd encoding errors at the ends of strings... + if(bytes.length > maxlen){ + bytes = Arrays.copyOfRange(bytes, 0, maxlen); + } + + buffer.put((byte)bytes.length); + buffer.put(bytes); + } + + private static void writeString(ByteBuffer buffer, String string){ + writeString(buffer, string, 32); + } + + private static String readString(ByteBuffer buffer){ + short length = (short)(buffer.get() & 0xff); + byte[] bytes = new byte[length]; + buffer.get(bytes); + return new String(bytes, charset); } } diff --git a/core/src/io/anuke/mindustry/net/Packet.java b/core/src/io/anuke/mindustry/net/Packet.java index 3c0084ba1e..f5feb59c94 100644 --- a/core/src/io/anuke/mindustry/net/Packet.java +++ b/core/src/io/anuke/mindustry/net/Packet.java @@ -1,11 +1,24 @@ package io.anuke.mindustry.net; +import io.anuke.arc.util.pooling.Pool.Poolable; + import java.nio.ByteBuffer; -public interface Packet { - void read(ByteBuffer buffer); - void write(ByteBuffer buffer); +public interface Packet extends Poolable{ + default void read(ByteBuffer buffer){ + } - interface ImportantPacket{} - interface UnimportantPacket{} + default void write(ByteBuffer buffer){ + } + + default void reset(){ + } + + default boolean isImportant(){ + return false; + } + + default boolean isUnimportant(){ + return false; + } } diff --git a/core/src/io/anuke/mindustry/net/Packets.java b/core/src/io/anuke/mindustry/net/Packets.java index bd0b8b9d39..75d218cc11 100644 --- a/core/src/io/anuke/mindustry/net/Packets.java +++ b/core/src/io/anuke/mindustry/net/Packets.java @@ -1,730 +1,183 @@ package io.anuke.mindustry.net; -import com.badlogic.gdx.utils.Array; -import com.badlogic.gdx.utils.Base64Coder; -import com.badlogic.gdx.utils.IntMap; -import com.badlogic.gdx.utils.reflect.ClassReflection; -import com.badlogic.gdx.utils.reflect.ReflectionException; -import io.anuke.mindustry.Vars; -import io.anuke.mindustry.entities.Player; -import io.anuke.mindustry.entities.SyncEntity; -import io.anuke.mindustry.io.Version; -import io.anuke.mindustry.net.Packet.ImportantPacket; -import io.anuke.mindustry.net.Packet.UnimportantPacket; -import io.anuke.mindustry.resource.Item; -import io.anuke.mindustry.world.Block; -import io.anuke.ucore.entities.Entities; -import io.anuke.ucore.entities.EntityGroup; +import io.anuke.arc.Core; +import io.anuke.arc.util.serialization.Base64Coder; +import io.anuke.mindustry.game.Version; +import io.anuke.mindustry.io.TypeIO; + import java.nio.ByteBuffer; -/**Class for storing all packets.*/ -public class Packets { +/** + * Class for storing all packets. + */ +public class Packets{ - public static class Connect implements ImportantPacket{ - public int id; - public String addressTCP; - } + public enum KickReason{ + kick, clientOutdated, serverOutdated, banned, gameover(true), recentKick, + nameInUse, idInUse, nameEmpty, customClient, serverClose; - public static class Disconnect implements ImportantPacket{ - public int id; - public String addressTCP; - } + public final boolean quiet; - public static class WorldData extends Streamable{ + KickReason(){ + this(false); + } - } - - public static class SyncPacket implements Packet, UnimportantPacket{ - public byte[] data; - - @Override - public void write(ByteBuffer buffer) { - buffer.putShort((short)data.length); - buffer.put(data); + KickReason(boolean quiet){ + this.quiet = quiet; } @Override - public void read(ByteBuffer buffer) { - data = new byte[buffer.getShort()]; - buffer.get(data); + public String toString(){ + return Core.bundle.get("server.kicked." + name()); + } + + public String extraText(){ + return Core.bundle.getOrNull("server.kicked." + name() + ".text"); } } - public static class BlockSyncPacket extends Streamable{ + public enum AdminAction{ + kick, ban, trace, wave + } + + public static class Connect implements Packet{ + public int id; + public String addressTCP; + + @Override + public boolean isImportant(){ + return true; + } + } + + public static class Disconnect implements Packet{ + public int id; + + @Override + public boolean isImportant(){ + return true; + } + } + + public static class WorldStream extends Streamable{ } public static class ConnectPacket implements Packet{ public int version; - public String name; - public boolean android; + public String versionType; + public String name, uuid, usid; + public boolean mobile; public int color; - public byte[] uuid; - - @Override - public void write(ByteBuffer buffer) { - buffer.putInt(Version.build); - buffer.put((byte)name.getBytes().length); - buffer.put(name.getBytes()); - buffer.put(android ? (byte)1 : 0); - buffer.putInt(color); - buffer.put(uuid); - } - - @Override - public void read(ByteBuffer buffer) { - version = buffer.getInt(); - byte length = buffer.get(); - byte[] bytes = new byte[length]; - buffer.get(bytes); - name = new String(bytes); - android = buffer.get() == 1; - color = buffer.getInt(); - uuid = new byte[8]; - buffer.get(uuid); - } - } - - public static class ConnectConfirmPacket implements Packet{ - @Override - public void write(ByteBuffer buffer) { } - - @Override - public void read(ByteBuffer buffer) { } - } - - public static class DisconnectPacket implements Packet{ - public int playerid; - - @Override - public void write(ByteBuffer buffer) { - buffer.putInt(playerid); - } - - @Override - public void read(ByteBuffer buffer) { - playerid = buffer.getInt(); - } - } - - public static class StateSyncPacket implements Packet, UnimportantPacket{ - public int[] items; - public float countdown, time; - public int enemies, wave; - public long timestamp; - - @Override - public void write(ByteBuffer buffer) { - for(int i = 0; i < items.length; i ++){ - buffer.putInt(items[i]); - } - - buffer.putFloat(countdown); - buffer.putFloat(time); - buffer.putShort((short)enemies); - buffer.putShort((short)wave); - buffer.putLong(timestamp); - } - - @Override - public void read(ByteBuffer buffer) { - items = new int[Item.getAllItems().size]; - - for(int i = 0; i < items.length; i ++){ - items[i] = buffer.getInt(); - } - - countdown = buffer.getFloat(); - time = buffer.getFloat(); - enemies = buffer.getShort(); - wave = buffer.getShort(); - timestamp = buffer.getLong(); - } - } - - public static class BlockLogRequestPacket implements Packet { - public int x; - public int y; - public Array editlogs; - - @Override - public void write(ByteBuffer buffer) { - buffer.putShort((short)x); - buffer.putShort((short)y); - buffer.putInt(editlogs.size); - for(EditLog value : editlogs) { - buffer.put((byte)value.playername.getBytes().length); - buffer.put(value.playername.getBytes()); - buffer.putInt(value.block.id); - buffer.put((byte) value.rotation); - buffer.put((byte) value.action.ordinal()); - } - } - - @Override - public void read(ByteBuffer buffer) { - x = buffer.getShort(); - y = buffer.getShort(); - editlogs = new Array<>(); - int arraySize = buffer.getInt(); - for(int a = 0; a < arraySize; a ++) { - byte length = buffer.get(); - byte[] bytes = new byte[length]; - buffer.get(bytes); - String name = new String(bytes); - - int blockid = buffer.getInt(); - int rotation = buffer.get(); - int ordinal = buffer.get(); - - editlogs.add(new EditLog(name, Block.getByID(blockid), rotation, EditLog.EditAction.values()[ordinal])); - } - } - } - - public static class RollbackRequestPacket implements Packet { - public int rollbackTimes; - - @Override - public void write(ByteBuffer buffer) { - buffer.putInt(rollbackTimes); - } - - @Override - public void read(ByteBuffer buffer) { - rollbackTimes = buffer.getInt(); - } - } - - public static class PositionPacket implements Packet{ - public byte[] data; - - @Override - public void write(ByteBuffer buffer) { - buffer.put(data); - } - - @Override - public void read(ByteBuffer buffer) { - data = new byte[SyncEntity.getWriteSize(Player.class) + 8]; - buffer.get(data); - } - } - - //not a real packet. - public static class EffectPacket{ - public int id; - public float x, y, rotation; - public int color; - } - - public static class ShootPacket implements Packet, UnimportantPacket{ - public byte weaponid; - public float x, y, rotation; - public int playerid; - - @Override - public void write(ByteBuffer buffer) { - buffer.put(weaponid); - buffer.putFloat(x); - buffer.putFloat(y); - buffer.putFloat(rotation); - buffer.putInt(playerid); - } - - @Override - public void read(ByteBuffer buffer) { - weaponid = buffer.get(); - x = buffer.getFloat(); - y = buffer.getFloat(); - rotation = buffer.getFloat(); - playerid = buffer.getInt(); - } - } - - public static class BulletPacket implements Packet, UnimportantPacket{ - public int type, owner; - public float x, y, angle; - public short damage; - - @Override - public void write(ByteBuffer buffer) { - buffer.putShort((short)type); - buffer.putInt(owner); - buffer.putFloat(x); - buffer.putFloat(y); - buffer.putFloat(angle); - buffer.putShort(damage); - } - - @Override - public void read(ByteBuffer buffer) { - type = buffer.getShort(); - owner = buffer.getInt(); - x = buffer.getFloat(); - y = buffer.getFloat(); - angle = buffer.getFloat(); - damage = buffer.getShort(); - } - } - - public static class PlacePacket implements Packet{ - public int playerid; - public byte rotation; - public short x, y; - public int block; - - @Override - public void write(ByteBuffer buffer) { - buffer.putInt(playerid); - buffer.put(rotation); - buffer.putShort(x); - buffer.putShort(y); - buffer.putInt(block); - } - - @Override - public void read(ByteBuffer buffer) { - playerid = buffer.getInt(); - rotation = buffer.get(); - x = buffer.getShort(); - y = buffer.getShort(); - block = buffer.getInt(); - } - } - - public static class BreakPacket implements Packet{ - public int playerid; - public short x, y; - - @Override - public void write(ByteBuffer buffer) { - buffer.putInt(playerid); - buffer.putShort(x); - buffer.putShort(y); - } - - @Override - public void read(ByteBuffer buffer) { - playerid = buffer.getInt(); - x = buffer.getShort(); - y = buffer.getShort(); - } - } - - public static class EntitySpawnPacket implements Packet{ - public SyncEntity entity; - public EntityGroup group; @Override public void write(ByteBuffer buffer){ - buffer.put((byte)group.getID()); - buffer.putInt(entity.id); - entity.writeSpawn(buffer); + buffer.putInt(Version.build); + TypeIO.writeString(buffer, versionType); + TypeIO.writeString(buffer, name); + TypeIO.writeString(buffer, usid); + buffer.put(mobile ? (byte)1 : 0); + buffer.putInt(color); + buffer.put(Base64Coder.decode(uuid)); } @Override - public void read(ByteBuffer buffer) { - byte groupid = buffer.get(); - int id = buffer.getInt(); - group = Entities.getGroup(groupid); - try { - entity = (SyncEntity) ClassReflection.newInstance(group.getType()); - entity.id = id; - entity.readSpawn(buffer); - entity.setNet(entity.x, entity.y); - }catch (ReflectionException e){ - throw new RuntimeException(e); - } + public void read(ByteBuffer buffer){ + version = buffer.getInt(); + versionType = TypeIO.readString(buffer); + name = TypeIO.readString(buffer); + usid = TypeIO.readString(buffer); + mobile = buffer.get() == 1; + color = buffer.getInt(); + byte[] idbytes = new byte[8]; + buffer.get(idbytes); + uuid = new String(Base64Coder.encode(idbytes)); } } - public static class EnemyDeathPacket implements Packet{ - public int id; + public static class InvokePacket implements Packet{ + public byte type, priority; + + public ByteBuffer writeBuffer; + public int writeLength; @Override - public void write(ByteBuffer buffer) { + public void read(ByteBuffer buffer){ + type = buffer.get(); + priority = buffer.get(); + writeLength = buffer.getShort(); + byte[] bytes = new byte[writeLength]; + buffer.get(bytes); + writeBuffer = ByteBuffer.wrap(bytes); + } + + @Override + public void write(ByteBuffer buffer){ + buffer.put(type); + buffer.put(priority); + buffer.putShort((short)writeLength); + + writeBuffer.position(0); + for(int i = 0; i < writeLength; i++){ + buffer.put(writeBuffer.get()); + } + } + + @Override + public void reset(){ + priority = 0; + } + + @Override + public boolean isImportant(){ + return priority == 1; + } + + @Override + public boolean isUnimportant(){ + return priority == 2; + } + } + + /** Marks the beginning of a stream. */ + public static class StreamBegin implements Packet{ + private static int lastid; + + public int id = lastid++; + public int total; + public byte type; + + @Override + public void write(ByteBuffer buffer){ buffer.putInt(id); + buffer.putInt(total); + buffer.put(type); } @Override - public void read(ByteBuffer buffer) { + public void read(ByteBuffer buffer){ id = buffer.getInt(); + total = buffer.getInt(); + type = buffer.get(); } } - public static class BlockDestroyPacket implements Packet{ - public int position; - - @Override - public void write(ByteBuffer buffer) { - buffer.putInt(position); - } - - @Override - public void read(ByteBuffer buffer) { - position = buffer.getInt(); - } - } - - public static class BlockUpdatePacket implements Packet{ - public int health, position; - - @Override - public void write(ByteBuffer buffer) { - buffer.putShort((short)health); - buffer.putInt(position); - } - - @Override - public void read(ByteBuffer buffer) { - health = buffer.getShort(); - position = buffer.getInt(); - } - } - - public static class ChatPacket implements Packet{ - public String name; - public String text; + public static class StreamChunk implements Packet{ public int id; + public byte[] data; @Override - public void write(ByteBuffer buffer) { - if(name != null) { - buffer.putShort((short) name.getBytes().length); - buffer.put(name.getBytes()); - }else{ - buffer.putShort((short)-1); - } - buffer.putShort((short)text.getBytes().length); - buffer.put(text.getBytes()); + public void write(ByteBuffer buffer){ buffer.putInt(id); - } - - @Override - public void read(ByteBuffer buffer) { - short nlength = buffer.getShort(); - if(nlength != -1) { - byte[] n = new byte[nlength]; - buffer.get(n); - name = new String(n); - } - short tlength = buffer.getShort(); - byte[] t = new byte[tlength]; - buffer.get(t); - - id = buffer.getInt(); - text = new String(t); - } - } - - public static class KickPacket implements Packet, ImportantPacket{ - public KickReason reason; - - @Override - public void write(ByteBuffer buffer) { - buffer.put((byte)reason.ordinal()); - } - - @Override - public void read(ByteBuffer buffer) { - reason = KickReason.values()[buffer.get()]; - } - } - - public enum KickReason{ - kick, invalidPassword, clientOutdated, serverOutdated, banned, gameover(true), recentKick, fastShoot; - public final boolean quiet; - - KickReason(){ quiet = false; } - - KickReason(boolean quiet){ - this.quiet = quiet; - } - } - - public static class UpgradePacket implements Packet{ - public byte id; //weapon ID only, currently - - @Override - public void write(ByteBuffer buffer) { - buffer.put(id); - } - - @Override - public void read(ByteBuffer buffer) { - id = buffer.get(); - } - } - - public static class WeaponSwitchPacket implements Packet{ - public int playerid; - public byte left, right; - - @Override - public void write(ByteBuffer buffer) { - buffer.putInt(playerid); - buffer.put(left); - buffer.put(right); - } - - @Override - public void read(ByteBuffer buffer) { - playerid = buffer.getInt(); - left = buffer.get(); - right = buffer.get(); - } - } - - public static class BlockTapPacket implements Packet{ - public int position; - - @Override - public void write(ByteBuffer buffer) { - buffer.putInt(position); - } - - @Override - public void read(ByteBuffer buffer) { - position = buffer.getInt(); - } - } - - public static class BlockConfigPacket implements Packet{ - public int position; - public byte data; - - @Override - public void write(ByteBuffer buffer) { - buffer.putInt(position); + buffer.putShort((short)data.length); buffer.put(data); } @Override - public void read(ByteBuffer buffer) { - position = buffer.getInt(); - data = buffer.get(); - } - } - - public static class EntityRequestPacket implements Packet{ - public int id; - public byte group; - - @Override - public void write(ByteBuffer buffer) { - buffer.putInt(id); - buffer.put(group); - } - - @Override - public void read(ByteBuffer buffer) { + public void read(ByteBuffer buffer){ id = buffer.getInt(); - group = buffer.get(); - } - } - - public static class GameOverPacket implements Packet{ - @Override - public void write(ByteBuffer buffer) { } - - @Override - public void read(ByteBuffer buffer) { } - } - - public static class FriendlyFireChangePacket implements Packet{ - public boolean enabled; - - @Override - public void write(ByteBuffer buffer) { - buffer.put(enabled ? 1 : (byte)0); - } - - @Override - public void read(ByteBuffer buffer) { - enabled = buffer.get() == 1; - } - } - - public static class PlayerDeathPacket implements Packet{ - public int id; - - @Override - public void write(ByteBuffer buffer) { - buffer.putInt(id); - } - - @Override - public void read(ByteBuffer buffer) { - id = buffer.getInt(); - } - } - - public static class CustomMapPacket extends Streamable{ - - } - - public static class MapAckPacket implements Packet{ - @Override - public void write(ByteBuffer buffer) { } - - @Override - public void read(ByteBuffer buffer) { } - } - - public static class ItemTransferPacket implements Packet, UnimportantPacket{ - public int position; - public byte rotation; - public byte itemid; - - @Override - public void write(ByteBuffer buffer) { - buffer.putInt((rotation) | (position << 2)); - buffer.put(itemid); - } - - @Override - public void read(ByteBuffer buffer) { - int i = buffer.getInt(); - rotation = (byte)(i & 0x3); - position = i >> 2; - itemid = buffer.get(); - } - } - - public static class ItemSetPacket implements Packet, UnimportantPacket{ - public int position; - public byte itemid, amount; - - @Override - public void write(ByteBuffer buffer) { - buffer.putInt(position); - buffer.put(itemid); - buffer.put(amount); - } - - @Override - public void read(ByteBuffer buffer) { - position = buffer.getInt(); - itemid = buffer.get(); - amount = buffer.get(); - } - } - - public static class ItemOffloadPacket implements Packet{ - public int position; - public byte itemid; - - @Override - public void write(ByteBuffer buffer) { - buffer.putInt(position); - buffer.put(itemid); - } - - @Override - public void read(ByteBuffer buffer) { - position = buffer.getInt(); - itemid = buffer.get(); - } - } - - public static class NetErrorPacket implements Packet{ - public String message; - - @Override - public void write(ByteBuffer buffer) { - buffer.putShort((short)message.getBytes().length); - buffer.put(message.getBytes()); - } - - @Override - public void read(ByteBuffer buffer) { - short length = buffer.getShort(); - byte[] bytes = new byte[length]; - buffer.get(bytes); - message = new String(bytes); - } - } - - public static class PlayerAdminPacket implements Packet{ - public boolean admin; - public int id; - - @Override - public void write(ByteBuffer buffer) { - buffer.put(admin ? (byte)1 : 0); - buffer.putInt(id); - } - - @Override - public void read(ByteBuffer buffer) { - admin = buffer.get() == 1; - id = buffer.getInt(); - } - } - - public static class AdministerRequestPacket implements Packet{ - public AdminAction action; - public int id; - - @Override - public void write(ByteBuffer buffer) { - buffer.put((byte)action.ordinal()); - buffer.putInt(id); - } - - @Override - public void read(ByteBuffer buffer) { - action = AdminAction.values()[buffer.get()]; - id = buffer.getInt(); - } - } - - public enum AdminAction{ - kick, ban, trace - } - - public static class TracePacket implements Packet{ - public TraceInfo info; - - @Override - public void write(ByteBuffer buffer) { - buffer.putInt(info.playerid); - buffer.putShort((short)info.ip.getBytes().length); - buffer.put(info.ip.getBytes()); - buffer.put(info.modclient ? (byte)1 : 0); - buffer.put(info.android ? (byte)1 : 0); - - buffer.putInt(info.totalBlocksBroken); - buffer.putInt(info.structureBlocksBroken); - buffer.putInt(info.lastBlockBroken.id); - - buffer.putInt(info.totalBlocksPlaced); - buffer.putInt(info.lastBlockPlaced.id); - buffer.put(Base64Coder.decode(info.uuid)); - } - - @Override - public void read(ByteBuffer buffer) { - int id = buffer.getInt(); - short iplen = buffer.getShort(); - byte[] ipb = new byte[iplen]; - buffer.get(ipb); - - info = new TraceInfo(new String(ipb)); - - info.playerid = id; - info.modclient = buffer.get() == 1; - info.android = buffer.get() == 1; - info.totalBlocksBroken = buffer.getInt(); - info.structureBlocksBroken = buffer.getInt(); - info.lastBlockBroken = Block.getByID(buffer.getInt()); - info.totalBlocksPlaced = buffer.getInt(); - info.lastBlockPlaced = Block.getByID(buffer.getInt()); - byte[] uuid = new byte[8]; - buffer.get(uuid); - - info.uuid = new String(Base64Coder.encode(uuid)); + data = new byte[buffer.getShort()]; + buffer.get(data); } } } diff --git a/core/src/io/anuke/mindustry/net/Registrator.java b/core/src/io/anuke/mindustry/net/Registrator.java index 92d6eca4b8..cfbcffd163 100644 --- a/core/src/io/anuke/mindustry/net/Registrator.java +++ b/core/src/io/anuke/mindustry/net/Registrator.java @@ -1,65 +1,27 @@ package io.anuke.mindustry.net; -import com.badlogic.gdx.utils.ObjectIntMap; -import com.badlogic.gdx.utils.reflect.ClassReflection; +import io.anuke.arc.collection.ObjectIntMap; +import io.anuke.arc.function.Supplier; import io.anuke.mindustry.net.Packets.*; -import io.anuke.mindustry.net.Streamable.StreamBegin; -import io.anuke.mindustry.net.Streamable.StreamChunk; -public class Registrator { - private static Class[] classes = { - StreamBegin.class, - StreamChunk.class, - WorldData.class, - SyncPacket.class, - PositionPacket.class, - ShootPacket.class, - PlacePacket.class, - BreakPacket.class, - StateSyncPacket.class, - BlockLogRequestPacket.class, - RollbackRequestPacket.class, - BlockSyncPacket.class, - BulletPacket.class, - EnemyDeathPacket.class, - BlockUpdatePacket.class, - BlockDestroyPacket.class, - ConnectPacket.class, - DisconnectPacket.class, - ChatPacket.class, - KickPacket.class, - UpgradePacket.class, - WeaponSwitchPacket.class, - BlockTapPacket.class, - BlockConfigPacket.class, - EntityRequestPacket.class, - ConnectConfirmPacket.class, - GameOverPacket.class, - FriendlyFireChangePacket.class, - PlayerDeathPacket.class, - CustomMapPacket.class, - MapAckPacket.class, - EntitySpawnPacket.class, - ItemTransferPacket.class, - ItemSetPacket.class, - ItemOffloadPacket.class, - NetErrorPacket.class, - PlayerAdminPacket.class, - AdministerRequestPacket.class, - TracePacket.class +public class Registrator{ + private static ClassEntry[] classes = { + new ClassEntry(StreamBegin.class, StreamBegin::new), + new ClassEntry(StreamChunk.class, StreamChunk::new), + new ClassEntry(WorldStream.class, WorldStream::new), + new ClassEntry(ConnectPacket.class, ConnectPacket::new), + new ClassEntry(InvokePacket.class, InvokePacket::new) }; - private static ObjectIntMap> ids = new ObjectIntMap<>(); + private static ObjectIntMap ids = new ObjectIntMap<>(); static{ if(classes.length > 127) throw new RuntimeException("Can't have more than 127 registered classes!"); - for(int i = 0; i < classes.length; i ++){ - if(!ClassReflection.isAssignableFrom(Packet.class, classes[i]) && - !ClassReflection.isAssignableFrom(Streamable.class, classes[i])) throw new RuntimeException("Not a packet: " + classes[i]); - ids.put(classes[i], i); + for(int i = 0; i < classes.length; i++){ + ids.put(classes[i].type, i); } } - public static Class getByID(byte id){ + public static ClassEntry getByID(byte id){ return classes[id]; } @@ -67,7 +29,17 @@ public class Registrator { return (byte)ids.get(type, -1); } - public static Class[] getClasses(){ + public static ClassEntry[] getClasses(){ return classes; } + + public static class ClassEntry{ + public final Class type; + public final Supplier constructor; + + public ClassEntry(Class type, Supplier constructor){ + this.type = type; + this.constructor = constructor; + } + } } diff --git a/core/src/io/anuke/mindustry/net/ServerDebug.java b/core/src/io/anuke/mindustry/net/ServerDebug.java deleted file mode 100644 index a34b05e7c1..0000000000 --- a/core/src/io/anuke/mindustry/net/ServerDebug.java +++ /dev/null @@ -1,65 +0,0 @@ -package io.anuke.mindustry.net; - -import com.badlogic.gdx.utils.IntMap; -import com.badlogic.gdx.utils.OrderedMap; -import com.badlogic.gdx.utils.TimeUtils; -import com.badlogic.gdx.utils.reflect.ClassReflection; -import io.anuke.mindustry.entities.Player; -import io.anuke.mindustry.net.Packets.Disconnect; -import io.anuke.ucore.util.Log; - -import static io.anuke.mindustry.Vars.playerGroup; - -public class ServerDebug { - private IntMap, Long>> last = new IntMap<>(); - - public void handle(int connection, Object packet){ - try { - if (!last.containsKey(connection)) - last.put(connection, new OrderedMap<>()); - if (packet instanceof Disconnect) - last.remove(connection); - else - last.get(connection).put(packet.getClass(), TimeUtils.millis()); - }catch (Exception e){ - Log.err(""); - } - } - - public String getOut(){ - StringBuilder build = new StringBuilder(); - for(Player player : playerGroup.all()){ - OrderedMap, Long> map = last.get(player.clientid, new OrderedMap<>()); - build.append("connection "); - build.append(player.clientid); - build.append(" / player '"); - build.append(player.name); - build.append(" android: "); - build.append(player.isAndroid); - build.append("'\n"); - - for(Class type : map.orderedKeys()){ - build.append(" "); - build.append(elapsed(type, map)); - build.append("\n"); - } - } - return build.toString(); - } - - private String elapsed(Class type, OrderedMap, Long> last) { - long t = last.get(type, -1L); - if (t == -1) { - return ClassReflection.getSimpleName(type) + ": "; - } else { - float el = TimeUtils.timeSinceMillis(t) / 1000f; - String tu; - if (el > 1f) { - tu = (int) el + "s"; - } else { - tu = (int) (el * 60) + "f"; - } - return ClassReflection.getSimpleName(type) + ": " + tu; - } - } -} diff --git a/core/src/io/anuke/mindustry/net/Streamable.java b/core/src/io/anuke/mindustry/net/Streamable.java index 1d700b2087..9f2c3c0fbc 100644 --- a/core/src/io/anuke/mindustry/net/Streamable.java +++ b/core/src/io/anuke/mindustry/net/Streamable.java @@ -1,62 +1,20 @@ package io.anuke.mindustry.net; -import com.badlogic.gdx.utils.reflect.ClassReflection; -import com.badlogic.gdx.utils.reflect.ReflectionException; -import io.anuke.mindustry.net.Packet.ImportantPacket; +import io.anuke.mindustry.net.Packets.StreamBegin; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.nio.ByteBuffer; +import java.io.*; -public class Streamable implements ImportantPacket{ +public class Streamable implements Packet{ public transient ByteArrayInputStream stream; - /**Marks the beginning of a stream.*/ - public static class StreamBegin implements Packet{ - private static int lastid; - - public int id = lastid ++; - public int total; - public Class type; - - @Override - public void write(ByteBuffer buffer) { - buffer.putInt(id); - buffer.putInt(total); - buffer.put(Registrator.getID(type)); - } - - @Override - public void read(ByteBuffer buffer) { - id = buffer.getInt(); - total = buffer.getInt(); - type = (Class)Registrator.getByID(buffer.get()); - } - } - - public static class StreamChunk implements Packet{ - public int id; - public byte[] data; - - @Override - public void write(ByteBuffer buffer) { - buffer.putInt(id); - buffer.putShort((short)data.length); - buffer.put(data); - } - - @Override - public void read(ByteBuffer buffer) { - id = buffer.getInt(); - data = new byte[buffer.getShort()]; - buffer.get(data); - } + @Override + public boolean isImportant(){ + return true; } public static class StreamBuilder{ public final int id; - public final Class type; + public final byte type; public final int total; public final ByteArrayOutputStream stream; @@ -68,21 +26,17 @@ public class Streamable implements ImportantPacket{ } public void add(byte[] bytes){ - try { + try{ stream.write(bytes); - }catch (IOException e){ + }catch(IOException e){ throw new RuntimeException(e); } } public Streamable build(){ - try { - Streamable s = ClassReflection.newInstance(type); - s.stream = new ByteArrayInputStream(stream.toByteArray()); - return s; - }catch(ReflectionException e){ - throw new RuntimeException(e); - } + Streamable s = (Streamable)Registrator.getByID(type).constructor.get(); + s.stream = new ByteArrayInputStream(stream.toByteArray()); + return s; } public boolean isDone(){ diff --git a/core/src/io/anuke/mindustry/net/TraceInfo.java b/core/src/io/anuke/mindustry/net/TraceInfo.java deleted file mode 100644 index 699e113e8e..0000000000 --- a/core/src/io/anuke/mindustry/net/TraceInfo.java +++ /dev/null @@ -1,27 +0,0 @@ -package io.anuke.mindustry.net; - -import com.badlogic.gdx.utils.IntIntMap; -import io.anuke.mindustry.world.Block; -import io.anuke.mindustry.world.blocks.Blocks; - -public class TraceInfo { - public int playerid; - public String ip; - public boolean modclient; - public boolean android; - - public IntIntMap fastShots = new IntIntMap(); - - public int totalBlocksBroken; - public int structureBlocksBroken; - public Block lastBlockBroken = Blocks.air; - - public int totalBlocksPlaced; - public Block lastBlockPlaced = Blocks.air; - - public String uuid; - - public TraceInfo(String ip){ - this.ip = ip; - } -} diff --git a/core/src/io/anuke/mindustry/net/ValidateException.java b/core/src/io/anuke/mindustry/net/ValidateException.java new file mode 100644 index 0000000000..08f3f70483 --- /dev/null +++ b/core/src/io/anuke/mindustry/net/ValidateException.java @@ -0,0 +1,15 @@ +package io.anuke.mindustry.net; + +import io.anuke.mindustry.entities.type.Player; + +/** + * Thrown when a client sends invalid information. + */ +public class ValidateException extends RuntimeException{ + public final Player player; + + public ValidateException(Player player, String s){ + super(s); + this.player = player; + } +} diff --git a/core/src/io/anuke/mindustry/resource/Item.java b/core/src/io/anuke/mindustry/resource/Item.java deleted file mode 100644 index 87959c4b61..0000000000 --- a/core/src/io/anuke/mindustry/resource/Item.java +++ /dev/null @@ -1,56 +0,0 @@ -package io.anuke.mindustry.resource; - -import com.badlogic.gdx.graphics.g2d.TextureRegion; -import com.badlogic.gdx.utils.Array; -import io.anuke.ucore.graphics.Draw; -import io.anuke.ucore.util.Bundles; - -public class Item{ - private static final Array items = new Array<>(); - - public static final Item - stone = new Item("stone"), - iron = new Item("iron"), - coal = new Item("coal"), - steel = new Item("steel"), - titanium = new Item("titanium"), - dirium = new Item("dirium"), - uranium = new Item("uranium"), - sand = new Item("sand"); - /*glass = new Item("glass"), - silicon = new Item("silicon");*/ - - public final int id; - public final String name; - public TextureRegion region; - public float explosiveness = 0f; - public float flammability = 0f; - - public Item(String name) { - this.id = items.size; - this.name = name; - - items.add(this); - } - - public void init(){ - this.region = Draw.region("icon-" + name); - } - - public String localizedName(){ - return Bundles.get("item." + this.name + ".name"); - } - - @Override - public String toString() { - return localizedName(); - } - - public static Array getAllItems() { - return Item.items; - } - - public static Item getByID(int id){ - return items.get(id); - } -} diff --git a/core/src/io/anuke/mindustry/resource/ItemStack.java b/core/src/io/anuke/mindustry/resource/ItemStack.java deleted file mode 100644 index 9d6bceb017..0000000000 --- a/core/src/io/anuke/mindustry/resource/ItemStack.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.anuke.mindustry.resource; - -public class ItemStack{ - public Item item; - public int amount; - public float pos; - - public ItemStack(Item item, int amount){ - this.item = item; - this.amount = amount; - } - - public boolean equals(ItemStack other){ - return other != null && other.item == item && other.amount == amount; - } -} diff --git a/core/src/io/anuke/mindustry/resource/Liquid.java b/core/src/io/anuke/mindustry/resource/Liquid.java deleted file mode 100644 index 5bf97660b3..0000000000 --- a/core/src/io/anuke/mindustry/resource/Liquid.java +++ /dev/null @@ -1,47 +0,0 @@ -package io.anuke.mindustry.resource; - -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.utils.Array; -import io.anuke.ucore.util.Bundles; - -public class Liquid { - - private static final Array liquids = new Array<>(); - - public static final Liquid - water = new Liquid("water", Color.ROYAL), - plasma = new Liquid("plasma", Color.CORAL), - lava = new Liquid("lava", Color.valueOf("ed5334")), - oil = new Liquid("oil", Color.valueOf("292929")), - cryofluid = new Liquid("cryofluid", Color.SKY); - - public final Color color; - public final String name; - public final int id; - - public Liquid(String name, Color color) { - this.name = name; - this.color = new Color(color); - - this.id = liquids.size; - - Liquid.liquids.add(this); - } - - public String localizedName(){ - return Bundles.get("liquid."+ this.name + ".name"); - } - - @Override - public String toString(){ - return localizedName(); - } - - public static Array getAllLiquids() { - return Liquid.liquids; - } - - public static Liquid getByID(int id){ - return liquids.get(id); - } -} diff --git a/core/src/io/anuke/mindustry/resource/Mech.java b/core/src/io/anuke/mindustry/resource/Mech.java deleted file mode 100644 index 57a9bf4800..0000000000 --- a/core/src/io/anuke/mindustry/resource/Mech.java +++ /dev/null @@ -1,11 +0,0 @@ -package io.anuke.mindustry.resource; - -public class Mech extends Upgrade{ - public static final Mech - - standard = new Mech("standard"); - - public Mech(String name){ - super(name); - } -} diff --git a/core/src/io/anuke/mindustry/resource/Recipe.java b/core/src/io/anuke/mindustry/resource/Recipe.java deleted file mode 100644 index e7e1910028..0000000000 --- a/core/src/io/anuke/mindustry/resource/Recipe.java +++ /dev/null @@ -1,21 +0,0 @@ -package io.anuke.mindustry.resource; - -import io.anuke.mindustry.world.Block; - -public class Recipe { - public Block result; - public ItemStack[] requirements; - public Section section; - public boolean desktopOnly = false; - - public Recipe(Section section, Block result, ItemStack... requirements){ - this.result = result; - this.requirements = requirements; - this.section = section; - } - - public Recipe setDesktop(){ - desktopOnly = true; - return this; - } -} diff --git a/core/src/io/anuke/mindustry/resource/Recipes.java b/core/src/io/anuke/mindustry/resource/Recipes.java deleted file mode 100644 index a9b750c734..0000000000 --- a/core/src/io/anuke/mindustry/resource/Recipes.java +++ /dev/null @@ -1,114 +0,0 @@ -package io.anuke.mindustry.resource; - -import com.badlogic.gdx.utils.Array; -import io.anuke.mindustry.Vars; -import io.anuke.mindustry.world.Block; -import io.anuke.mindustry.world.blocks.DefenseBlocks; -import io.anuke.mindustry.world.blocks.DistributionBlocks; -import io.anuke.mindustry.world.blocks.ProductionBlocks; -import io.anuke.mindustry.world.blocks.WeaponBlocks; - -import static io.anuke.mindustry.resource.Section.*; - -public class Recipes { - private static final Array list = Array.with( - new Recipe(defense, DefenseBlocks.stonewall, stack(Item.stone, 12)), - new Recipe(defense, DefenseBlocks.ironwall, stack(Item.iron, 12)), - new Recipe(defense, DefenseBlocks.steelwall, stack(Item.steel, 12)), - new Recipe(defense, DefenseBlocks.titaniumwall, stack(Item.titanium, 12)), - new Recipe(defense, DefenseBlocks.diriumwall, stack(Item.dirium, 12)), - new Recipe(defense, DefenseBlocks.steelwalllarge, stack(Item.steel, 12*4)), - new Recipe(defense, DefenseBlocks.titaniumwalllarge, stack(Item.titanium, 12*4)), - new Recipe(defense, DefenseBlocks.diriumwalllarge, stack(Item.dirium, 12*4)), - new Recipe(defense, DefenseBlocks.door, stack(Item.steel, 3), stack(Item.iron, 3*4)).setDesktop(), - new Recipe(defense, DefenseBlocks.largedoor, stack(Item.steel, 3*4), stack(Item.iron, 3*4*4)).setDesktop(), - new Recipe(defense, DefenseBlocks.titaniumshieldwall, stack(Item.titanium, 16)), - - new Recipe(distribution, DistributionBlocks.conveyor, stack(Item.stone, 1)), - new Recipe(distribution, DistributionBlocks.steelconveyor, stack(Item.steel, 1)), - new Recipe(distribution, DistributionBlocks.pulseconveyor, stack(Item.dirium, 1)), - new Recipe(distribution, DistributionBlocks.router, stack(Item.stone, 2)), - new Recipe(distribution, DistributionBlocks.junction, stack(Item.iron, 2)), - new Recipe(distribution, DistributionBlocks.tunnel, stack(Item.iron, 2)), - new Recipe(distribution, DistributionBlocks.conduit, stack(Item.steel, 1)), - new Recipe(distribution, DistributionBlocks.pulseconduit, stack(Item.titanium, 1), stack(Item.steel, 1)), - new Recipe(distribution, DistributionBlocks.liquidrouter, stack(Item.steel, 2)), - new Recipe(distribution, DistributionBlocks.liquidjunction, stack(Item.steel, 2)), - new Recipe(distribution, DistributionBlocks.sorter, stack(Item.steel, 2)), - - new Recipe(weapon, WeaponBlocks.turret, stack(Item.stone, 4)), - new Recipe(weapon, WeaponBlocks.doubleturret, stack(Item.stone, 7)), - new Recipe(weapon, WeaponBlocks.machineturret, stack(Item.iron, 8), stack(Item.stone, 10)), - new Recipe(weapon, WeaponBlocks.shotgunturret, stack(Item.iron, 10), stack(Item.stone, 10)), - new Recipe(weapon, WeaponBlocks.flameturret, stack(Item.iron, 12), stack(Item.steel, 9)), - new Recipe(weapon, WeaponBlocks.sniperturret, stack(Item.iron, 15), stack(Item.steel, 10)), - new Recipe(weapon, WeaponBlocks.laserturret, stack(Item.steel, 12), stack(Item.titanium, 12)), - new Recipe(weapon, WeaponBlocks.mortarturret, stack(Item.steel, 25), stack(Item.titanium, 15)), - new Recipe(weapon, WeaponBlocks.teslaturret, stack(Item.steel, 20), stack(Item.titanium, 25), stack(Item.dirium, 15)), - new Recipe(weapon, WeaponBlocks.plasmaturret, stack(Item.steel, 10), stack(Item.titanium, 20), stack(Item.dirium, 15)), - new Recipe(weapon, WeaponBlocks.chainturret, stack(Item.steel, 50), stack(Item.titanium, 25), stack(Item.dirium, 40)), - new Recipe(weapon, WeaponBlocks.titanturret, stack(Item.steel, 70), stack(Item.titanium, 50), stack(Item.dirium, 55)), - - new Recipe(crafting, ProductionBlocks.smelter, stack(Item.stone, 40), stack(Item.iron, 40)), - new Recipe(crafting, ProductionBlocks.crucible, stack(Item.titanium, 50), stack(Item.steel, 50)), - new Recipe(crafting, ProductionBlocks.coalpurifier, stack(Item.steel, 10), stack(Item.iron, 10)), - new Recipe(crafting, ProductionBlocks.titaniumpurifier, stack(Item.steel, 30), stack(Item.iron, 30)), - new Recipe(crafting, ProductionBlocks.oilrefinery, stack(Item.steel, 15), stack(Item.iron, 15)), - new Recipe(crafting, ProductionBlocks.stoneformer, stack(Item.steel, 10), stack(Item.iron, 10)), - new Recipe(crafting, ProductionBlocks.lavasmelter, stack(Item.steel, 30), stack(Item.titanium, 15)), - new Recipe(crafting, ProductionBlocks.weaponFactory, stack(Item.steel, 60), stack(Item.iron, 60)).setDesktop(), - - new Recipe(production, ProductionBlocks.stonedrill, stack(Item.stone, 12)), - new Recipe(production, ProductionBlocks.irondrill, stack(Item.stone, 25)), - new Recipe(production, ProductionBlocks.coaldrill, stack(Item.stone, 25), stack(Item.iron, 40)), - new Recipe(production, ProductionBlocks.titaniumdrill, stack(Item.iron, 50), stack(Item.steel, 50)), - new Recipe(production, ProductionBlocks.uraniumdrill, stack(Item.iron, 40), stack(Item.steel, 40)), - new Recipe(production, ProductionBlocks.omnidrill, stack(Item.titanium, 40), stack(Item.dirium, 40)), - - new Recipe(power, ProductionBlocks.coalgenerator, stack(Item.iron, 30), stack(Item.stone, 20)), - new Recipe(power, ProductionBlocks.thermalgenerator, stack(Item.steel, 30), stack(Item.iron, 30)), - new Recipe(power, ProductionBlocks.combustiongenerator, stack(Item.iron, 30), stack(Item.stone, 20)), - new Recipe(power, ProductionBlocks.rtgenerator, stack(Item.titanium, 20), stack(Item.steel, 20)), - new Recipe(power, ProductionBlocks.nuclearReactor, stack(Item.titanium, 40), stack(Item.dirium, 40), stack(Item.steel, 50)), - new Recipe(power, DistributionBlocks.powerbooster, stack(Item.steel, 8), stack(Item.iron, 8)), - new Recipe(power, DistributionBlocks.powerlaser, stack(Item.steel, 3), stack(Item.iron, 3)), - new Recipe(power, DistributionBlocks.powerlasercorner, stack(Item.steel, 4), stack(Item.iron, 4)), - new Recipe(power, DistributionBlocks.powerlaserrouter, stack(Item.steel, 5), stack(Item.iron, 5)), - - new Recipe(power, DefenseBlocks.shieldgenerator, stack(Item.titanium, 30), stack(Item.dirium, 30)), - - new Recipe(distribution, DistributionBlocks.teleporter, stack(Item.steel, 30), stack(Item.dirium, 40)), - - new Recipe(power, DefenseBlocks.repairturret, stack(Item.iron, 30)), - new Recipe(power, DefenseBlocks.megarepairturret, stack(Item.iron, 20), stack(Item.steel, 30)), - - new Recipe(production, ProductionBlocks.pump, stack(Item.steel, 10)), - new Recipe(production, ProductionBlocks.fluxpump, stack(Item.steel, 10), stack(Item.dirium, 5)) - ); - - private static ItemStack stack(Item item, int amount){ - return new ItemStack(item, amount); - } - - public static Array all(){ - return list; - } - - public static Recipe getByResult(Block block){ - for(Recipe recipe : list){ - if(recipe.result == block){ - return recipe; - } - } - return null; - } - - public static Array getBy(Section section, Array r){ - for(Recipe recipe : list){ - if(recipe.section == section && !(Vars.mobile && recipe.desktopOnly)) - r.add(recipe); - } - - return r; - } -} diff --git a/core/src/io/anuke/mindustry/resource/Section.java b/core/src/io/anuke/mindustry/resource/Section.java deleted file mode 100644 index feb383eca8..0000000000 --- a/core/src/io/anuke/mindustry/resource/Section.java +++ /dev/null @@ -1,5 +0,0 @@ -package io.anuke.mindustry.resource; - -public enum Section{ - weapon, production, distribution, power, defense, crafting; -} diff --git a/core/src/io/anuke/mindustry/resource/Upgrade.java b/core/src/io/anuke/mindustry/resource/Upgrade.java deleted file mode 100644 index 423528a483..0000000000 --- a/core/src/io/anuke/mindustry/resource/Upgrade.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.anuke.mindustry.resource; - -import com.badlogic.gdx.utils.Array; -import io.anuke.ucore.util.Bundles; - -public abstract class Upgrade { - private static Array upgrades = new Array<>(); - private static byte lastid; - - public final byte id; - public final String name; - public final String description; - - public Upgrade(String name){ - this.id = lastid ++; - this.name = name; - this.description = Bundles.getNotNull("upgrade."+name+".description"); - - upgrades.add(this); - } - - public String localized(){ - return Bundles.get("upgrade." + name + ".name"); - } - - public static Upgrade getByID(byte id){ - return upgrades.get(id); - } - - public static Array getAllUpgrades() { - return upgrades; - } -} diff --git a/core/src/io/anuke/mindustry/resource/UpgradeRecipes.java b/core/src/io/anuke/mindustry/resource/UpgradeRecipes.java deleted file mode 100644 index 03566c51ee..0000000000 --- a/core/src/io/anuke/mindustry/resource/UpgradeRecipes.java +++ /dev/null @@ -1,33 +0,0 @@ -package io.anuke.mindustry.resource; - -import com.badlogic.gdx.utils.ObjectMap; -import com.badlogic.gdx.utils.ObjectMap.Entries; -import io.anuke.ucore.util.Mathf; - -public class UpgradeRecipes { - private static final ObjectMap recipes = Mathf.map( - Weapon.triblaster, list(stack(Item.iron, 60), stack(Item.steel, 80)), - Weapon.clustergun, list(stack(Item.iron, 300), stack(Item.steel, 80)), - Weapon.vulcan, list(stack(Item.iron, 100), stack(Item.steel, 150), stack(Item.titanium, 80)), - Weapon.beam, list(stack(Item.steel, 260), stack(Item.titanium, 160), stack(Item.dirium, 120)), - Weapon.shockgun, list(stack(Item.steel, 240), stack(Item.titanium, 160), stack(Item.dirium, 160)) - ); - - private static final ItemStack[] empty = {}; - - public static ItemStack[] get(Upgrade upgrade){ - return recipes.get(upgrade, empty); - } - - public static Entries getAllRecipes(){ - return recipes.entries(); - } - - private static ItemStack[] list(ItemStack... stacks){ - return stacks; - } - - private static ItemStack stack(Item item, int amount){ - return new ItemStack(item, amount); - } -} diff --git a/core/src/io/anuke/mindustry/resource/Weapon.java b/core/src/io/anuke/mindustry/resource/Weapon.java deleted file mode 100644 index e6f2c19e2f..0000000000 --- a/core/src/io/anuke/mindustry/resource/Weapon.java +++ /dev/null @@ -1,136 +0,0 @@ -package io.anuke.mindustry.resource; - -import io.anuke.mindustry.Vars; -import io.anuke.mindustry.entities.Bullet; -import io.anuke.mindustry.entities.BulletType; -import io.anuke.mindustry.entities.Player; -import io.anuke.mindustry.graphics.Fx; -import io.anuke.mindustry.net.Net; -import io.anuke.mindustry.net.NetEvents; -import io.anuke.ucore.core.Effects; -import io.anuke.ucore.core.Effects.Effect; -import io.anuke.ucore.entities.Entity; -import io.anuke.ucore.util.Angles; -import io.anuke.ucore.util.Mathf; -import io.anuke.ucore.util.Translator; - -public class Weapon extends Upgrade{ - public static final Weapon - - blaster = new Weapon("blaster", 12, BulletType.shot){ - { - effect = Fx.laserShoot; - length = 2f; - } - }, - triblaster = new Weapon("triblaster", 16, BulletType.spread){ - { - shots = 3; - effect = Fx.spreadShoot; - roundrobin = true; - } - }, - clustergun = new Weapon("clustergun", 26f, BulletType.cluster){ - { - effect = Fx.clusterShoot; - inaccuracy = 17f; - roundrobin = true; - shots = 2; - spacing = 0; - } - }, - beam = new Weapon("beam", 30f, BulletType.beamlaser){ - { - effect = Fx.beamShoot; - inaccuracy = 0; - roundrobin = true; - shake = 2f; - } - }, - vulcan = new Weapon("vulcan", 5, BulletType.vulcan){ - { - effect = Fx.vulcanShoot; - inaccuracy = 5; - roundrobin = true; - shake = 1f; - inaccuracy = 4f; - } - }, - shockgun = new Weapon("shockgun", 36, BulletType.shockshell){ - { - shootsound = "bigshot"; - effect = Fx.shockShoot; - shake = 2f; - roundrobin = true; - shots = 7; - inaccuracy = 15f; - length = 3.5f; - } - }; - /**weapon reload in frames*/ - float reload; - /**type of bullet shot*/ - BulletType type; - /**sound made when shooting*/ - String shootsound = "shoot"; - /**amount of shots per fire*/ - int shots = 1; - /**spacing in degrees between multiple shots, if applicable*/ - float spacing = 12f; - /**inaccuracy of degrees of each shot*/ - float inaccuracy = 0f; - /**intensity and duration of each shot's screen shake*/ - float shake = 0f; - /**effect displayed when shooting*/ - Effect effect; - /**shoot barrel length*/ - float length = 3f; - /**whether to shoot the weapons in different arms one after another, rather an all at once*/ - boolean roundrobin = false; - /**translator for vector calulations*/ - Translator tr = new Translator(); - - private Weapon(String name, float reload, BulletType type){ - super(name); - this.reload = reload; - this.type = type; - } - - public void update(Player p, boolean left){ - int t = left ? 1 : 2; - int t2 = !left ? 1 : 2; - if(p.timer.get(t, reload)){ - if(roundrobin){ - p.timer.reset(t2, reload/2f); - } - float ang = Angles.mouseAngle(p.x, p.y); - tr.trns(ang - 90, 3f * Mathf.sign(left), length); - shoot(p, p.x + tr.x, p.y + tr.y, Angles.mouseAngle(p.x + tr.x, p.y + tr.y)); - } - } - - void shootInternal(Player p, float x, float y, float rotation){ - Angles.shotgun(shots, spacing, rotation, f -> bullet(p, x, y, f + Mathf.range(inaccuracy))); - tr.trns(rotation, 3f); - if(effect != null) Effects.effect(effect, x + tr.x, y + tr.y, rotation); - Effects.shake(shake, shake, x, y); - Effects.sound(shootsound, x, y); - } - - public float getReload(){ - return reload; - } - - public void shoot(Player p, float x, float y, float angle){ - shootInternal(p, x, y, angle); - - if(Net.active() && p == Vars.player){ - NetEvents.handleShoot(this, x, y, angle); - } - } - - void bullet(Entity owner, float x, float y, float angle){ - tr.trns(angle, 3f); - new Bullet(type, owner, x + tr.x, y + tr.y, angle).add(); - } -} diff --git a/core/src/io/anuke/mindustry/type/Category.java b/core/src/io/anuke/mindustry/type/Category.java new file mode 100644 index 0000000000..a90bad9c64 --- /dev/null +++ b/core/src/io/anuke/mindustry/type/Category.java @@ -0,0 +1,24 @@ +package io.anuke.mindustry.type; + +public enum Category{ + /** Offensive turrets. */ + turret, + /** Blocks that produce raw resources, such as drills. */ + production, + /** Blocks that move items around. */ + distribution, + /** Blocks that move liquids around. */ + liquid, + /** Blocks that generate or transport power. */ + power, + /** Walls and other defensive structures. */ + defense, + /** Blocks that craft things. */ + crafting, + /** Blocks that create units. */ + units, + /** Things that upgrade the player such as mech pads. */ + upgrade, + /** Things for storage or passive effects. */ + effect +} diff --git a/core/src/io/anuke/mindustry/type/ContentType.java b/core/src/io/anuke/mindustry/type/ContentType.java new file mode 100644 index 0000000000..125ccc8221 --- /dev/null +++ b/core/src/io/anuke/mindustry/type/ContentType.java @@ -0,0 +1,16 @@ +package io.anuke.mindustry.type; + +/** Do not rearrange, ever! */ +public enum ContentType{ + item, + block, + mech, + bullet, + liquid, + status, + unit, + weather, + effect, + zone, + loadout +} diff --git a/core/src/io/anuke/mindustry/type/Item.java b/core/src/io/anuke/mindustry/type/Item.java new file mode 100644 index 0000000000..032b539f45 --- /dev/null +++ b/core/src/io/anuke/mindustry/type/Item.java @@ -0,0 +1,108 @@ +package io.anuke.mindustry.type; + +import io.anuke.arc.Core; +import io.anuke.arc.collection.Array; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.graphics.g2d.TextureRegion; +import io.anuke.arc.scene.ui.layout.Table; +import io.anuke.mindustry.game.UnlockableContent; +import io.anuke.mindustry.ui.ContentDisplay; +import io.anuke.mindustry.world.blocks.Floor; +import io.anuke.mindustry.world.blocks.OreBlock; + +import static io.anuke.mindustry.Vars.content; + +public class Item extends UnlockableContent implements Comparable{ + public final Color color; + private TextureRegion[] regions; + + /** type of the item; used for tabs and core acceptance. default value is {@link ItemType#resource}. */ + public ItemType type = ItemType.resource; + /** how explosive this item is. */ + public float explosiveness = 0f; + /** flammability above 0.3 makes this eleigible for item burners. */ + public float flammability = 0f; + /** how radioactive this item is. 0=none, 1=chernobyl ground zero */ + public float radioactivity; + /** drill hardness of the item */ + public int hardness = 0; + /** + * base material cost of this item, used for calculating place times + * 1 cost = 1 tick added to build time + */ + public float cost = 1f; + /** If true, item is always unlocked. */ + public boolean alwaysUnlocked = false; + + public Item(String name, Color color){ + super(name); + this.color = color; + this.description = Core.bundle.getOrNull("item." + this.name + ".description"); + } + + public void load(){ + regions = new TextureRegion[Icon.values().length]; + for(int i = 0; i < regions.length; i++){ + Icon icon = Icon.values()[i]; + regions[i] = Core.atlas.find(icon == Icon.large ? "item-" + name : "item-" + name + "-" + icon.name()); + } + } + + public TextureRegion icon(Icon icon){ + return regions[icon.ordinal()]; + } + + @Override + public boolean alwaysUnlocked(){ + return alwaysUnlocked; + } + + @Override + public void displayInfo(Table table){ + ContentDisplay.displayItem(table, this); + } + + @Override + public String localizedName(){ + return Core.bundle.get("item." + this.name + ".name"); + } + + @Override + public TextureRegion getContentIcon(){ + return icon(Icon.xxlarge); + } + + @Override + public String toString(){ + return localizedName(); + } + + @Override + public int compareTo(Item item){ + return Integer.compare(id, item.id); + } + + @Override + public ContentType getContentType(){ + return ContentType.item; + } + + public enum Icon{ + small(8 * 2), + medium(8 * 3), + large(8 * 4), + xlarge(8 * 5), + xxlarge(8 * 6); + + public final int size; + + Icon(int size){ + this.size = size; + } + } + + /** Allocates a new array containing all items that generate ores. */ + public static Array getAllOres(){ + return content.blocks().select(b -> b instanceof OreBlock).map(b -> ((Floor)b).itemDrop); + } +} diff --git a/core/src/io/anuke/mindustry/type/ItemStack.java b/core/src/io/anuke/mindustry/type/ItemStack.java new file mode 100644 index 0000000000..8067c56cd9 --- /dev/null +++ b/core/src/io/anuke/mindustry/type/ItemStack.java @@ -0,0 +1,54 @@ +package io.anuke.mindustry.type; + +import io.anuke.arc.collection.Array; +import io.anuke.mindustry.content.Items; + +public class ItemStack implements Comparable{ + public Item item; + public int amount; + + public ItemStack(Item item, int amount){ + if(item == null) item = Items.copper; + this.item = item; + this.amount = amount; + } + + //serialization only + public ItemStack(){ + //prevent nulls. + item = Items.copper; + } + + public boolean equals(ItemStack other){ + return other != null && other.item == item && other.amount == amount; + } + + public static ItemStack[] with(Object... items){ + ItemStack[] stacks = new ItemStack[items.length / 2]; + for(int i = 0; i < items.length; i += 2){ + stacks[i / 2] = new ItemStack((Item)items[i], (Integer)items[i + 1]); + } + return stacks; + } + + public static Array list(Object... items){ + Array stacks = new Array<>(items.length / 2); + for(int i = 0; i < items.length; i += 2){ + stacks.add(new ItemStack((Item)items[i], (Integer)items[i + 1])); + } + return stacks; + } + + @Override + public int compareTo(ItemStack itemStack){ + return item.compareTo(itemStack.item); + } + + @Override + public String toString(){ + return "ItemStack{" + + "item=" + item + + ", amount=" + amount + + '}'; + } +} diff --git a/core/src/io/anuke/mindustry/type/ItemType.java b/core/src/io/anuke/mindustry/type/ItemType.java new file mode 100644 index 0000000000..40459f070e --- /dev/null +++ b/core/src/io/anuke/mindustry/type/ItemType.java @@ -0,0 +1,8 @@ +package io.anuke.mindustry.type; + +public enum ItemType{ + /** Not used for anything besides crafting inside blocks. */ + resource, + /** Can be used for constructing blocks. Only materials are accepted into the core. */ + material +} diff --git a/core/src/io/anuke/mindustry/type/Liquid.java b/core/src/io/anuke/mindustry/type/Liquid.java new file mode 100644 index 0000000000..bb3a0f363a --- /dev/null +++ b/core/src/io/anuke/mindustry/type/Liquid.java @@ -0,0 +1,70 @@ +package io.anuke.mindustry.type; + +import io.anuke.arc.Core; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.graphics.g2d.TextureRegion; +import io.anuke.arc.scene.ui.layout.Table; +import io.anuke.mindustry.content.StatusEffects; +import io.anuke.mindustry.game.UnlockableContent; +import io.anuke.mindustry.ui.ContentDisplay; + +public class Liquid extends UnlockableContent{ + public final Color color; + + /** 0-1, 0 is completely inflammable, anything above that may catch fire when exposed to heat, 0.5+ is very flammable. */ + public float flammability; + /** temperature: 0.5 is 'room' temperature, 0 is very cold, 1 is molten hot */ + public float temperature = 0.5f; + /** how much heat this liquid can store. 0.4=water (decent), anything lower is probably less dense and bad at cooling. */ + public float heatCapacity = 0.5f; + /** how thick this liquid is. 0.5=water (relatively viscous), 1 would be something like tar (very slow) */ + public float viscosity = 0.5f; + /** how prone to exploding this liquid is, when heated. 0 = nothing, 1 = nuke */ + public float explosiveness; + /** the burning color of this liquid */ + public Color flameColor = Color.valueOf("ffb763"); + /** The associated status effect. */ + public StatusEffect effect = StatusEffects.none; + /** Displayed icon. TODO fix it by removing autogen, draw icons manually */ + public TextureRegion iconRegion; + + public Liquid(String name, Color color){ + super(name); + this.color = new Color(color); + this.description = Core.bundle.getOrNull("liquid." + name + ".description"); + } + + public boolean canExtinguish(){ + return flammability < 0.1f && temperature <= 0.5f; + } + + @Override + public void load(){ + iconRegion = Core.atlas.find("liquid-" + name); + } + + @Override + public void displayInfo(Table table){ + ContentDisplay.displayLiquid(table, this); + } + + @Override + public String localizedName(){ + return Core.bundle.get("liquid." + this.name + ".name"); + } + + @Override + public TextureRegion getContentIcon(){ + return iconRegion; + } + + @Override + public String toString(){ + return localizedName(); + } + + @Override + public ContentType getContentType(){ + return ContentType.liquid; + } +} diff --git a/core/src/io/anuke/mindustry/type/LiquidStack.java b/core/src/io/anuke/mindustry/type/LiquidStack.java new file mode 100644 index 0000000000..c4dcda7568 --- /dev/null +++ b/core/src/io/anuke/mindustry/type/LiquidStack.java @@ -0,0 +1,19 @@ +package io.anuke.mindustry.type; + +public class LiquidStack{ + public Liquid liquid; + public float amount; + + public LiquidStack(Liquid liquid, float amount){ + this.liquid = liquid; + this.amount = amount; + } + + @Override + public String toString(){ + return "LiquidStack{" + + "liquid=" + liquid + + ", amount=" + amount + + '}'; + } +} diff --git a/core/src/io/anuke/mindustry/type/Loadout.java b/core/src/io/anuke/mindustry/type/Loadout.java new file mode 100644 index 0000000000..b6a86bcb88 --- /dev/null +++ b/core/src/io/anuke/mindustry/type/Loadout.java @@ -0,0 +1,112 @@ +package io.anuke.mindustry.type; + +import io.anuke.arc.collection.Array; +import io.anuke.arc.collection.IntMap; +import io.anuke.mindustry.content.Blocks; +import io.anuke.mindustry.game.Content; +import io.anuke.mindustry.world.*; +import io.anuke.mindustry.world.blocks.storage.CoreBlock; + +import static io.anuke.mindustry.Vars.defaultTeam; +import static io.anuke.mindustry.Vars.world; + +public class Loadout extends Content{ + private final Array outArray = new Array<>(); + private final IntMap entries = new IntMap(){{ + put('>', new BlockEntry(Blocks.conveyor, 0)); + put('^', new BlockEntry(Blocks.conveyor, 1)); + put('<', new BlockEntry(Blocks.conveyor, 2)); + put('v', new BlockEntry(Blocks.conveyor, 3)); + + put('1', new BlockEntry(Blocks.coreShard)); + put('2', new BlockEntry(Blocks.coreFoundation)); + put('3', new BlockEntry(Blocks.coreNucleus)); + + put('C', new BlockEntry(Blocks.mechanicalDrill, Blocks.oreCopper)); + }}; + + private final IntMap blocks = new IntMap<>(); + private Block core; + + public Loadout(String... layout){ + int coreX = -1, coreY = -1; + + outer: + for(int y = 0; y < layout.length; y++){ + for(int x = 0; x < layout[0].length(); x++){ + char c = layout[y].charAt(x); + if(entries.get(c) != null && entries.get(c).block instanceof CoreBlock){ + core = entries.get(c).block; + coreX = x; + coreY = y; + break outer; + } + } + } + + if(coreX == -1) throw new IllegalArgumentException("Schematic does not have a core."); + + for(int y = 0; y < layout.length; y++){ + for(int x = 0; x < layout[0].length(); x++){ + char c = layout[y].charAt(x); + if(entries.containsKey(c)){ + BlockEntry entry = entries.get(c); + blocks.put(Pos.get(x - coreX, -(y - coreY)), entry); + } + } + } + } + + public Loadout(){ + + } + + public Block core(){ + return core; + } + + public void setup(int x, int y){ + for(IntMap.Entry entry : blocks.entries()){ + int rx = Pos.x(entry.key); + int ry = Pos.y(entry.key); + Tile tile = world.tile(x + rx, y + ry); + world.setBlock(tile, entry.value.block, defaultTeam); + tile.rotation((byte)entry.value.rotation); + if(entry.value.ore != null){ + for(Tile t : tile.getLinkedTiles(outArray)){ + t.setOverlay(entry.value.ore); + } + } + } + } + + @Override + public ContentType getContentType(){ + return ContentType.loadout; + } + + static class BlockEntry{ + final Block block; + final Block ore; + final int rotation; + + BlockEntry(Block block, Block ore){ + this.block = block; + this.ore = ore; + this.rotation = 0; + } + + BlockEntry(Block block, int rotation){ + this.block = block; + this.ore = null; + this.rotation = rotation; + } + + BlockEntry(Block block){ + this.block = block; + this.ore = null; + this.rotation = 0; + } + } + +} diff --git a/core/src/io/anuke/mindustry/type/Mech.java b/core/src/io/anuke/mindustry/type/Mech.java new file mode 100644 index 0000000000..1d9a7cb520 --- /dev/null +++ b/core/src/io/anuke/mindustry/type/Mech.java @@ -0,0 +1,103 @@ +package io.anuke.mindustry.type; + +import io.anuke.arc.Core; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.graphics.g2d.TextureRegion; +import io.anuke.arc.scene.ui.layout.Table; +import io.anuke.mindustry.entities.type.Player; +import io.anuke.mindustry.game.UnlockableContent; +import io.anuke.mindustry.graphics.Pal; +import io.anuke.mindustry.ui.ContentDisplay; + +public class Mech extends UnlockableContent{ + public boolean flying; + public float speed = 1.1f; + public float maxSpeed = 10f; + public float boostSpeed = 0.75f; + public float drag = 0.4f; + public float mass = 1f; + public float shake = 0f; + public float health = 200f; + + public float hitsize = 6f; + public float cellTrnsY = 0f; + public float mineSpeed = 1f; + public int drillPower = -1; + public float buildPower = 1f; + public Color engineColor = Pal.boostTo; + public int itemCapacity = 30; + public boolean turnCursor = true; + public boolean canHeal = false; + + public float weaponOffsetX, weaponOffsetY, engineOffset = 5f, engineSize = 2.5f; + public Weapon weapon; + + public TextureRegion baseRegion, legRegion, region, iconRegion; + + public Mech(String name, boolean flying){ + super(name); + this.flying = flying; + this.description = Core.bundle.get("mech." + name + ".description"); + } + + public String localizedName(){ + return Core.bundle.get("mech." + name + ".name"); + } + + public void updateAlt(Player player){ + } + + public void draw(Player player){ + } + + public float getExtraArmor(Player player){ + return 0f; + } + + public float spreadX(Player player){ + return 0f; + } + + public float getRotationAlpha(Player player){ + return 1f; + } + + public boolean canShoot(Player player){ + return true; + } + + public void onLand(Player player){ + } + + @Override + public void displayInfo(Table table){ + ContentDisplay.displayMech(table, this); + } + + @Override + public TextureRegion getContentIcon(){ + return iconRegion; + } + + @Override + public ContentType getContentType(){ + return ContentType.mech; + } + + @Override + public void load(){ + weapon.load(); + if(!flying){ + legRegion = Core.atlas.find(name + "-leg"); + baseRegion = Core.atlas.find(name + "-base"); + } + + region = Core.atlas.find(name); + iconRegion = Core.atlas.find("mech-icon-" + name); + } + + @Override + public String toString(){ + return localizedName(); + } +} diff --git a/core/src/io/anuke/mindustry/type/StatusEffect.java b/core/src/io/anuke/mindustry/type/StatusEffect.java new file mode 100644 index 0000000000..785bcab946 --- /dev/null +++ b/core/src/io/anuke/mindustry/type/StatusEffect.java @@ -0,0 +1,104 @@ +package io.anuke.mindustry.type; + +import io.anuke.arc.collection.Array; +import io.anuke.arc.collection.ObjectMap; +import io.anuke.arc.function.Supplier; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.util.Time; +import io.anuke.mindustry.content.Fx; +import io.anuke.mindustry.entities.Effects; +import io.anuke.mindustry.entities.Effects.Effect; +import io.anuke.mindustry.entities.type.Unit; +import io.anuke.mindustry.entities.units.Statuses.StatusEntry; +import io.anuke.mindustry.game.Content; + +public class StatusEffect extends Content{ + public float damageMultiplier = 1f; //damage dealt + public float armorMultiplier = 1f; //armor points + public float speedMultiplier = 1f; //speed + public Color color = Color.WHITE.cpy(); //tint color + + /** Transition handler map. */ + private ObjectMap transitions = new ObjectMap<>(); + /** + * Transition initializer array. Since provided effects are only available after init(), this handles putting things + * in the transitions map. + */ + private Array transInit = new Array<>(); + + /** Damage per frame. */ + protected float damage; + /** Effect that happens randomly on top of the affected unit. */ + protected Effect effect = Fx.none; + + @SuppressWarnings("unchecked") + @Override + public void init(){ + for(Object[] pair : transInit){ + Supplier sup = (Supplier)pair[0]; + TransitionHandler handler = (TransitionHandler)pair[1]; + transitions.put(sup.get(), handler); + } + transInit.clear(); + } + + /** Runs every tick on the affected unit while time is greater than 0. */ + public void update(Unit unit, float time){ + if(damage > 0){ + unit.damagePeriodic(damage); + }else if(damage < 0){ //heal unit + unit.healBy(damage * Time.delta()); + } + + if(effect != Fx.none && Mathf.chance(Time.delta() * 0.15f)){ + Effects.effect(effect, unit.x + Mathf.range(unit.getSize() / 2f), unit.y + Mathf.range(unit.getSize() / 2f)); + } + } + + protected void trans(Supplier effect, TransitionHandler handler){ + transInit.add(new Object[]{effect, handler}); + } + + @SuppressWarnings("unchecked") + protected void opposite(Supplier... effect){ + for(Supplier sup : effect){ + trans(sup, (unit, time, newTime, result) -> { + time -= newTime * 0.5f; + if(time > 0){ + result.set(this, time); + return; + } + result.set(sup.get(), newTime); + }); + } + } + + public boolean reactsWith(StatusEffect effect){ + return transitions.containsKey(effect); + } + + /** + * Called when transitioning between two status effects. + * @param to The state to transition to + * @param time The current status effect time + * @param newTime The time that the new status effect will last + */ + public StatusEntry getTransition(Unit unit, StatusEffect to, float time, float newTime, StatusEntry result){ + if(transitions.containsKey(to)){ + transitions.get(to).handle(unit, time, newTime, result); + return result; + } + + return result.set(to, newTime); + } + + @Override + public ContentType getContentType(){ + return ContentType.status; + } + + public interface TransitionHandler{ + void handle(Unit unit, float time, float newTime, StatusEntry result); + } +} diff --git a/core/src/io/anuke/mindustry/type/UnitType.java b/core/src/io/anuke/mindustry/type/UnitType.java new file mode 100644 index 0000000000..1500fb2738 --- /dev/null +++ b/core/src/io/anuke/mindustry/type/UnitType.java @@ -0,0 +1,86 @@ +package io.anuke.mindustry.type; + +import io.anuke.arc.Core; +import io.anuke.arc.collection.ObjectSet; +import io.anuke.arc.function.Supplier; +import io.anuke.arc.graphics.g2d.TextureRegion; +import io.anuke.arc.scene.ui.layout.Table; +import io.anuke.mindustry.content.Items; +import io.anuke.mindustry.entities.traits.TypeTrait; +import io.anuke.mindustry.entities.type.BaseUnit; +import io.anuke.mindustry.game.Team; +import io.anuke.mindustry.game.UnlockableContent; +import io.anuke.mindustry.ui.ContentDisplay; + +public class UnitType extends UnlockableContent{ + protected final Supplier constructor; + public float health = 60; + public float hitsize = 7f; + public float hitsizeTile = 4f; + public float speed = 0.4f; + public float range = 0, attackLength = 150f; + public float rotatespeed = 0.2f; + public float baseRotateSpeed = 0.1f; + public float shootCone = 15f; + public float mass = 1f; + public boolean isFlying; + public boolean targetAir = true; + public boolean rotateWeapon = false; + public float drag = 0.1f; + public float maxVelocity = 5f; + public float retreatPercent = 0.6f; + public int itemCapacity = 30; + public ObjectSet toMine = ObjectSet.with(Items.lead, Items.copper); + public float buildPower = 0.3f, minePower = 0.7f; + public Weapon weapon; + public float weaponOffsetY, engineOffset = 6f, engineSize = 2f; + public ObjectSet immunities = new ObjectSet<>(); + + public TextureRegion iconRegion, legRegion, baseRegion, region; + + public UnitType(String name, Class type, Supplier mainConstructor){ + super(name); + this.constructor = mainConstructor; + this.description = Core.bundle.getOrNull("unit." + name + ".description"); + + TypeTrait.registerType(type, mainConstructor); + } + + @Override + public void displayInfo(Table table){ + ContentDisplay.displayUnit(table, this); + } + + @Override + public String localizedName(){ + return Core.bundle.get("unit." + name + ".name"); + } + + @Override + public TextureRegion getContentIcon(){ + return iconRegion; + } + + @Override + public void load(){ + weapon.load(); + iconRegion = Core.atlas.find("unit-icon-" + name, Core.atlas.find(name)); + region = Core.atlas.find(name); + + if(!isFlying){ + legRegion = Core.atlas.find(name + "-leg"); + baseRegion = Core.atlas.find(name + "-base"); + } + } + + @Override + public ContentType getContentType(){ + return ContentType.unit; + } + + public BaseUnit create(Team team){ + BaseUnit unit = constructor.get(); + unit.init(this, team); + return unit; + } +} diff --git a/core/src/io/anuke/mindustry/type/Weapon.java b/core/src/io/anuke/mindustry/type/Weapon.java new file mode 100644 index 0000000000..7d2a22c98f --- /dev/null +++ b/core/src/io/anuke/mindustry/type/Weapon.java @@ -0,0 +1,174 @@ +package io.anuke.mindustry.type; + +import io.anuke.annotations.Annotations.Loc; +import io.anuke.annotations.Annotations.Remote; +import io.anuke.arc.Core; +import io.anuke.arc.graphics.g2d.TextureRegion; +import io.anuke.arc.math.Angles; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.util.Time; +import io.anuke.arc.util.Tmp; +import io.anuke.mindustry.Vars; +import io.anuke.mindustry.content.Fx; +import io.anuke.mindustry.entities.Effects; +import io.anuke.mindustry.entities.Effects.Effect; +import io.anuke.mindustry.entities.bullet.Bullet; +import io.anuke.mindustry.entities.bullet.BulletType; +import io.anuke.mindustry.entities.traits.ShooterTrait; +import io.anuke.mindustry.entities.type.Player; +import io.anuke.mindustry.gen.Call; +import io.anuke.mindustry.net.Net; + +public class Weapon{ + public final String name; + + /** minimum cursor distance from player, fixes 'cross-eyed' shooting. */ + protected static float minPlayerDist = 20f; + protected static int sequenceNum = 0; + /** bullet shot */ + public BulletType bullet; + /** shell ejection effect */ + public Effect ejectEffect = Fx.none; + /** weapon reload in frames */ + public float reload; + /** amount of shots per fire */ + public int shots = 1; + /** spacing in degrees between multiple shots, if applicable */ + public float spacing = 12f; + /** inaccuracy of degrees of each shot */ + public float inaccuracy = 0f; + /** intensity and duration of each shot's screen shake */ + public float shake = 0f; + /** visual weapon knockback. */ + public float recoil = 1.5f; + /** shoot barrel y offset */ + public float length = 3f; + /** shoot barrel x offset. */ + public float width = 4f; + /** fraction of velocity that is random */ + public float velocityRnd = 0f; + /** whether to shoot the weapons in different arms one after another, rather than all at once */ + public boolean roundrobin = false; + /** randomization of shot length */ + public float lengthRand = 0f; + /** delay in ticks between shots */ + public float shotDelay = 0; + /** whether shooter rotation is ignored when shooting. */ + public boolean ignoreRotation = false; + + public TextureRegion region; + + protected Weapon(String name){ + this.name = name; + } + + protected Weapon(){ + //no region + this.name = ""; + } + + @Remote(targets = Loc.server, called = Loc.both, unreliable = true) + public static void onPlayerShootWeapon(Player player, float x, float y, float rotation, boolean left){ + if(player == null) return; + //clients do not see their own shoot events: they are simulated completely clientside to prevent laggy visuals + //messing with the firerate or any other stats does not affect the server (take that, script kiddies!) + if(Net.client() && player == Vars.player){ + return; + } + + shootDirect(player, x, y, rotation, left); + } + + @Remote(targets = Loc.server, called = Loc.both, unreliable = true) + public static void onGenericShootWeapon(ShooterTrait shooter, float x, float y, float rotation, boolean left){ + if(shooter == null) return; + shootDirect(shooter, x, y, rotation, left); + } + + public static void shootDirect(ShooterTrait shooter, float offsetX, float offsetY, float rotation, boolean left){ + float x = shooter.getX() + offsetX; + float y = shooter.getY() + offsetY; + float baseX = shooter.getX(), baseY = shooter.getY(); + + Weapon weapon = shooter.getWeapon(); + + sequenceNum = 0; + if(weapon.shotDelay > 0.01f){ + Angles.shotgun(weapon.shots, weapon.spacing, rotation, f -> { + Time.run(sequenceNum * weapon.shotDelay, () -> weapon.bullet(shooter, x + shooter.getX() - baseX, y + shooter.getY() - baseY, f + Mathf.range(weapon.inaccuracy))); + sequenceNum++; + }); + }else{ + Angles.shotgun(weapon.shots, weapon.spacing, rotation, f -> weapon.bullet(shooter, x, y, f + Mathf.range(weapon.inaccuracy))); + } + + BulletType ammo = weapon.bullet; + + Tmp.v1.trns(rotation + 180f, ammo.recoil); + + shooter.velocity().add(Tmp.v1); + + Tmp.v1.trns(rotation, 3f); + + Effects.shake(weapon.shake, weapon.shake, x, y); + Effects.effect(weapon.ejectEffect, x, y, rotation * -Mathf.sign(left)); + Effects.effect(ammo.shootEffect, x + Tmp.v1.x, y + Tmp.v1.y, rotation, shooter); + Effects.effect(ammo.smokeEffect, x + Tmp.v1.x, y + Tmp.v1.y, rotation, shooter); + + //reset timer for remote players + shooter.getTimer().get(shooter.getShootTimer(left), weapon.reload); + } + + public void load(){ + region = Core.atlas.find(name + "-equip", Core.atlas.find("clear")); + } + + public void update(ShooterTrait shooter, float pointerX, float pointerY){ + for(boolean left : Mathf.booleans){ + Tmp.v1.set(pointerX, pointerY).sub(shooter.getX(), shooter.getY()); + if(Tmp.v1.len() < minPlayerDist) Tmp.v1.setLength(minPlayerDist); + + float cx = Tmp.v1.x + shooter.getX(), cy = Tmp.v1.y + shooter.getY(); + + float ang = Tmp.v1.angle(); + Tmp.v1.trns(ang - 90, width * Mathf.sign(left), length + Mathf.range(lengthRand)); + + update(shooter, shooter.getX() + Tmp.v1.x, shooter.getY() + Tmp.v1.y, Angles.angle(shooter.getX() + Tmp.v1.x, shooter.getY() + Tmp.v1.y, cx, cy), left); + } + } + + public void update(ShooterTrait shooter, float mountX, float mountY, float angle, boolean left){ + if(shooter.getTimer().get(shooter.getShootTimer(left), reload)){ + if(roundrobin){ + shooter.getTimer().reset(shooter.getShootTimer(!left), reload / 2f); + } + + shoot(shooter, mountX - shooter.getX(), mountY - shooter.getY(), angle, left); + } + } + + public float getRecoil(ShooterTrait player, boolean left){ + return (1f - Mathf.clamp(player.getTimer().getTime(player.getShootTimer(left)) / reload)) * recoil; + } + + public void shoot(ShooterTrait p, float x, float y, float angle, boolean left){ + if(Net.client()){ + //call it directly, don't invoke on server + shootDirect(p, x, y, angle, left); + }else{ + if(p instanceof Player){ //players need special weapon handling logic + Call.onPlayerShootWeapon((Player)p, x, y, angle, left); + }else{ + Call.onGenericShootWeapon(p, x, y, angle, left); + } + } + } + + void bullet(ShooterTrait owner, float x, float y, float angle){ + if(owner == null) return; + + Tmp.v1.trns(angle, 3f); + Bullet.create(bullet, + owner, owner.getTeam(), x + Tmp.v1.x, y + Tmp.v1.y, angle, (1f - velocityRnd) + Mathf.random(velocityRnd)); + } +} diff --git a/core/src/io/anuke/mindustry/type/WeatherEvent.java b/core/src/io/anuke/mindustry/type/WeatherEvent.java new file mode 100644 index 0000000000..385124a1b9 --- /dev/null +++ b/core/src/io/anuke/mindustry/type/WeatherEvent.java @@ -0,0 +1,17 @@ +package io.anuke.mindustry.type; + +import io.anuke.mindustry.game.Content; + +//currently unimplemented, see trello for implementation plans +public class WeatherEvent extends Content{ + public final String name; + + public WeatherEvent(String name){ + this.name = name; + } + + @Override + public ContentType getContentType(){ + return ContentType.weather; + } +} diff --git a/core/src/io/anuke/mindustry/type/Zone.java b/core/src/io/anuke/mindustry/type/Zone.java new file mode 100644 index 0000000000..f0e936ae72 --- /dev/null +++ b/core/src/io/anuke/mindustry/type/Zone.java @@ -0,0 +1,195 @@ +package io.anuke.mindustry.type; + +import io.anuke.arc.Core; +import io.anuke.arc.Events; +import io.anuke.arc.collection.Array; +import io.anuke.arc.function.Consumer; +import io.anuke.arc.graphics.g2d.TextureRegion; +import io.anuke.arc.scene.ui.layout.Table; +import io.anuke.arc.util.Structs; +import io.anuke.mindustry.content.Loadouts; +import io.anuke.mindustry.game.EventType.ZoneConfigureCompleteEvent; +import io.anuke.mindustry.game.EventType.ZoneRequireCompleteEvent; +import io.anuke.mindustry.game.Rules; +import io.anuke.mindustry.game.UnlockableContent; +import io.anuke.mindustry.maps.generators.Generator; +import io.anuke.mindustry.world.Block; + +import java.util.Arrays; + +import static io.anuke.mindustry.Vars.*; + +public class Zone extends UnlockableContent{ + public final Generator generator; + public Block[] blockRequirements = {}; + public ZoneRequirement[] zoneRequirements = {}; + public Item[] resources = {}; + public Consumer rules = rules -> {}; + public boolean alwaysUnlocked; + public int conditionWave = Integer.MAX_VALUE; + public int configureWave = 15; + public int launchPeriod = 10; + public Loadout loadout = Loadouts.basicShard; + + protected ItemStack[] baseLaunchCost = {}; + protected Array startingItems = new Array<>(); + protected ItemStack[] launchCost = null; + + private Array defaultStartingItems = new Array<>(); + + public Zone(String name, Generator generator){ + super(name); + this.generator = generator; + } + + public boolean isBossWave(int wave){ + return wave % configureWave == 0 && wave > 0; + } + + public boolean isLaunchWave(int wave){ + return metCondition() && wave % launchPeriod == 0; + } + + public ItemStack[] getLaunchCost(){ + if(launchCost == null){ + updateLaunchCost(); + } + return launchCost; + } + + public Array getStartingItems(){ + return startingItems; + } + + public void resetStartingItems(){ + startingItems.clear(); + defaultStartingItems.each(stack -> startingItems.add(new ItemStack(stack.item, stack.amount))); + } + + public void updateWave(int wave){ + int value = Core.settings.getInt(name + "-wave", 0); + if(value < wave){ + Core.settings.put(name + "-wave", wave); + data.modified(); + + for(Zone zone : content.zones()){ + ZoneRequirement req = Structs.find(zone.zoneRequirements, f -> f.zone == this); + if(req != null && wave == req.wave + 1){ + Events.fire(new ZoneRequireCompleteEvent(zone, this)); + } + } + + if(wave == configureWave + 1){ + Events.fire(new ZoneConfigureCompleteEvent(this)); + } + } + } + + public int bestWave(){ + return Core.settings.getInt(name + "-wave", 0); + } + + public boolean isCompleted(){ + return bestWave() >= conditionWave; + } + + public void updateLaunchCost(){ + Array stacks = new Array<>(); + + Consumer adder = stack -> { + for(ItemStack other : stacks){ + if(other.item == stack.item){ + other.amount += stack.amount; + return; + } + } + stacks.add(new ItemStack(stack.item, stack.amount)); + }; + + for(ItemStack stack : baseLaunchCost) adder.accept(stack); + for(ItemStack stack : startingItems) adder.accept(stack); + + for(ItemStack stack : stacks){ + if(stack.amount < 0) stack.amount = 0; + } + + stacks.sort(); + launchCost = stacks.toArray(ItemStack.class); + Core.settings.putObject(name + "-starting-items", startingItems); + data.modified(); + } + + /** Whether this zone has met its condition; if true, the player can leave. */ + public boolean metCondition(){ + return state.wave >= conditionWave; + } + + public boolean canConfigure(){ + return bestWave() >= configureWave; + } + + @Override + public void init(){ + generator.init(loadout); + Arrays.sort(resources); + + for(ItemStack stack : startingItems){ + defaultStartingItems.add(new ItemStack(stack.item, stack.amount)); + } + + @SuppressWarnings("unchecked") + Array arr = Core.settings.getObject(name + "-starting-items", Array.class, () -> null); + if(arr != null){ + startingItems = arr; + } + } + + @Override + public boolean alwaysUnlocked(){ + return alwaysUnlocked; + } + + @Override + public boolean isHidden(){ + return true; + } + + //neither of these are implemented, as zones are not displayed in a normal fashion... yet + @Override + public void displayInfo(Table table){ + } + + @Override + public TextureRegion getContentIcon(){ + return null; + } + + @Override + public String localizedName(){ + return Core.bundle.get("zone." + name + ".name"); + } + + @Override + public ContentType getContentType(){ + return ContentType.zone; + } + + public static class ZoneRequirement{ + public final Zone zone; + public final int wave; + + public ZoneRequirement(Zone zone, int wave){ + this.zone = zone; + this.wave = wave; + } + + public static ZoneRequirement[] with(Object... objects){ + ZoneRequirement[] out = new ZoneRequirement[objects.length / 2]; + for(int i = 0; i < objects.length; i += 2){ + out[i / 2] = new ZoneRequirement((Zone)objects[i], (Integer)objects[i + 1]); + } + return out; + } + } + +} diff --git a/core/src/io/anuke/mindustry/ui/Bar.java b/core/src/io/anuke/mindustry/ui/Bar.java new file mode 100644 index 0000000000..4e9a87659d --- /dev/null +++ b/core/src/io/anuke/mindustry/ui/Bar.java @@ -0,0 +1,83 @@ +package io.anuke.mindustry.ui; + +import io.anuke.arc.Core; +import io.anuke.arc.function.FloatProvider; +import io.anuke.arc.function.Supplier; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.graphics.g2d.*; +import io.anuke.arc.math.Mathf; +import io.anuke.arc.math.geom.Rectangle; +import io.anuke.arc.scene.Element; +import io.anuke.arc.scene.style.Drawable; +import io.anuke.arc.util.pooling.Pools; + +public class Bar extends Element{ + private static Rectangle scissor = new Rectangle(); + + private FloatProvider fraction; + private String name = ""; + private float value, lastValue, blink; + private Color blinkColor = new Color(); + + public Bar(String name, Color color, FloatProvider fraction){ + this.fraction = fraction; + this.name = Core.bundle.get(name); + this.blinkColor.set(color); + lastValue = value = fraction.get(); + setColor(color); + } + + public Bar(Supplier name, Supplier color, FloatProvider fraction){ + this.fraction = fraction; + lastValue = value = Mathf.clamp(fraction.get()); + update(() -> { + this.name = name.get(); + this.blinkColor.set(color.get()); + setColor(color.get()); + }); + } + + public Bar blink(Color color){ + blinkColor.set(color); + return this; + } + + @Override + public void draw(){ + float computed = Mathf.clamp(fraction.get()); + if(!Mathf.isEqual(lastValue, computed)){ + blink = 1f; + lastValue = computed; + } + + blink = Mathf.lerpDelta(blink, 0f, 0.2f); + value = Mathf.lerpDelta(value, computed, 0.15f); + + Draw.colorl(0.1f); + Draw.drawable("bar", x, y, width, height); + Draw.color(color, blinkColor, blink); + + Drawable top = Core.scene.skin.getDrawable("bar-top"); + float topWidth = width * value; + + if(topWidth > Core.atlas.find("bar-top").getWidth()){ + top.draw(x, y, topWidth, height); + }else{ + if(ScissorStack.pushScissors(scissor.set(x, y, topWidth, height))){ + top.draw(x, y, Core.atlas.find("bar-top").getWidth(), height); + ScissorStack.popScissors(); + } + } + + Draw.color(); + + BitmapFont font = Core.scene.skin.getFont("default-font"); + GlyphLayout lay = Pools.obtain(GlyphLayout.class, GlyphLayout::new); + lay.setText(font, name); + + font.setColor(Color.WHITE); + font.draw(name, x + width / 2f - lay.width / 2f, y + height / 2f + lay.height / 2f + 1); + + Pools.free(lay); + } +} diff --git a/core/src/io/anuke/mindustry/ui/BorderImage.java b/core/src/io/anuke/mindustry/ui/BorderImage.java index 4dbfdc4809..6c7a8e54d9 100644 --- a/core/src/io/anuke/mindustry/ui/BorderImage.java +++ b/core/src/io/anuke/mindustry/ui/BorderImage.java @@ -1,44 +1,43 @@ package io.anuke.mindustry.ui; -import com.badlogic.gdx.graphics.Colors; -import com.badlogic.gdx.graphics.Texture; -import com.badlogic.gdx.graphics.g2d.Batch; - -import com.badlogic.gdx.graphics.g2d.TextureRegion; -import io.anuke.ucore.graphics.Draw; -import io.anuke.ucore.graphics.Lines; -import io.anuke.ucore.scene.ui.Image; -import io.anuke.ucore.scene.ui.layout.Unit; +import io.anuke.arc.graphics.Texture; +import io.anuke.arc.graphics.g2d.*; +import io.anuke.arc.scene.ui.Image; +import io.anuke.arc.scene.ui.layout.Unit; +import io.anuke.mindustry.graphics.Pal; public class BorderImage extends Image{ - private float thickness = 3f; - - public BorderImage(){} - - public BorderImage(Texture texture){ - super(texture); - } - - public BorderImage(Texture texture, float thick){ - super(texture); - thickness = thick; - } + private float thickness = 3f; - public BorderImage(TextureRegion region, float thick){ - super(region); - thickness = thick; - } - - @Override - public void draw(Batch batch, float alpha){ - super.draw(batch, alpha); - - float scaleX = getScaleX(); - float scaleY = getScaleY(); - - Draw.color(Colors.get("accent")); - Lines.stroke(Unit.dp.scl(thickness)); - Lines.rect(x + imageX, y + imageY, imageWidth * scaleX, imageHeight * scaleY); - Draw.reset(); - } + public BorderImage(){ + + } + + public BorderImage(Texture texture){ + super(texture); + } + + public BorderImage(Texture texture, float thick){ + super(texture); + thickness = thick; + } + + public BorderImage(TextureRegion region, float thick){ + super(region); + thickness = thick; + } + + @Override + public void draw(){ + super.draw(); + + float scaleX = getScaleX(); + float scaleY = getScaleY(); + + Draw.color(Pal.accent); + Draw.alpha(parentAlpha); + Lines.stroke(Unit.dp.scl(thickness)); + Lines.rect(x + imageX, y + imageY, imageWidth * scaleX, imageHeight * scaleY); + Draw.reset(); + } } diff --git a/core/src/io/anuke/mindustry/ui/ContentDisplay.java b/core/src/io/anuke/mindustry/ui/ContentDisplay.java new file mode 100644 index 0000000000..f00506202d --- /dev/null +++ b/core/src/io/anuke/mindustry/ui/ContentDisplay.java @@ -0,0 +1,209 @@ +package io.anuke.mindustry.ui; + +import io.anuke.arc.Core; +import io.anuke.arc.collection.Array; +import io.anuke.arc.collection.OrderedMap; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.scene.ui.layout.Table; +import io.anuke.arc.util.Strings; +import io.anuke.mindustry.graphics.Pal; +import io.anuke.mindustry.type.*; +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.Block.Icon; +import io.anuke.mindustry.world.meta.*; + +public class ContentDisplay{ + + public static void displayBlock(Table table, Block block){ + + table.table(title -> { + int size = 8 * 6; + + title.addImage(block.icon(Icon.large)).size(size); + title.add("[accent]" + block.localizedName).padLeft(5); + }); + + table.row(); + + table.addImage("white").height(3).color(Color.LIGHT_GRAY).pad(8).padLeft(0).padRight(0).fillX(); + + table.row(); + + if(block.description != null){ + table.add(block.description).padLeft(5).padRight(5).width(400f).wrap().fillX(); + table.row(); + + table.addImage("white").height(3).color(Color.LIGHT_GRAY).pad(8).padLeft(0).padRight(0).fillX(); + table.row(); + } + + BlockStats stats = block.stats; + + for(StatCategory cat : stats.toMap().keys()){ + OrderedMap> map = stats.toMap().get(cat); + + if(map.size == 0) continue; + + table.add("$category." + cat.name()).color(Pal.accent).fillX(); + table.row(); + + for(BlockStat stat : map.keys()){ + table.table(inset -> { + inset.left(); + inset.add("[LIGHT_GRAY]" + stat.localized() + ":[] "); + Array arr = map.get(stat); + for(StatValue value : arr){ + value.display(inset); + inset.add().size(10f); + } + + //map.get(stat).display(inset); + }).fillX().padLeft(10); + table.row(); + } + } + } + + public static void displayItem(Table table, Item item){ + + table.table(title -> { + title.addImage(item.getContentIcon()).size(8 * 6); + title.add("[accent]" + item.localizedName()).padLeft(5); + }); + + table.row(); + + table.addImage("white").height(3).color(Color.LIGHT_GRAY).pad(15).padLeft(0).padRight(0).fillX(); + + table.row(); + + if(item.description != null){ + table.add(item.description).padLeft(5).padRight(5).width(400f).wrap().fillX(); + table.row(); + + table.addImage("white").height(3).color(Color.LIGHT_GRAY).pad(15).padLeft(0).padRight(0).fillX(); + table.row(); + } + + table.left().defaults().fillX(); + + table.add(Core.bundle.format("item.explosiveness", (int)(item.explosiveness * 100))); + table.row(); + table.add(Core.bundle.format("item.flammability", (int)(item.flammability * 100))); + table.row(); + table.add(Core.bundle.format("item.radioactivity", (int)(item.radioactivity * 100))); + table.row(); + } + + public static void displayLiquid(Table table, Liquid liquid){ + + table.table(title -> { + title.addImage(liquid.getContentIcon()).size(8 * 6); + title.add("[accent]" + liquid.localizedName()).padLeft(5); + }); + + table.row(); + + table.addImage("white").height(3).color(Color.LIGHT_GRAY).pad(15).padLeft(0).padRight(0).fillX(); + + table.row(); + + if(liquid.description != null){ + table.add(liquid.description).padLeft(5).padRight(5).width(400f).wrap().fillX(); + table.row(); + + table.addImage("white").height(3).color(Color.LIGHT_GRAY).pad(15).padLeft(0).padRight(0).fillX(); + table.row(); + } + + table.left().defaults().fillX(); + + table.add(Core.bundle.format("item.explosiveness", (int)(liquid.explosiveness * 100))); + table.row(); + table.add(Core.bundle.format("item.flammability", (int)(liquid.flammability * 100))); + table.row(); + table.add(Core.bundle.format("liquid.heatcapacity", (int)(liquid.heatCapacity * 100))); + table.row(); + table.add(Core.bundle.format("liquid.temperature", (int)(liquid.temperature * 100))); + table.row(); + table.add(Core.bundle.format("liquid.viscosity", (int)(liquid.viscosity * 100))); + table.row(); + } + + public static void displayMech(Table table, Mech mech){ + table.table(title -> { + title.addImage(mech.getContentIcon()).size(8 * 6); + title.add("[accent]" + mech.localizedName()).padLeft(5); + }); + table.left().defaults().left(); + + table.row(); + + table.addImage("white").height(3).color(Color.LIGHT_GRAY).pad(15).padLeft(0).padRight(0).fillX(); + + table.row(); + + if(mech.description != null){ + table.add(mech.description).padLeft(5).padRight(5).width(400f).wrap().fillX(); + table.row(); + + table.addImage("white").height(3).color(Color.LIGHT_GRAY).pad(15).padLeft(0).padRight(0).fillX(); + table.row(); + } + + table.left().defaults().fillX(); + + if(Core.bundle.has("mech." + mech.name + ".weapon")){ + table.add(Core.bundle.format("mech.weapon", Core.bundle.get("mech." + mech.name + ".weapon"))); + table.row(); + } + if(Core.bundle.has("mech." + mech.name + ".ability")){ + table.add(Core.bundle.format("mech.ability", Core.bundle.get("mech." + mech.name + ".ability"))); + table.row(); + } + + table.add(Core.bundle.format("mech.buildspeed", (int)(mech.buildPower * 100f))); + table.row(); + + table.add(Core.bundle.format("mech.health", (int)mech.health)); + table.row(); + table.add(Core.bundle.format("mech.itemcapacity", mech.itemCapacity)); + table.row(); + + if(mech.drillPower > 0){ + table.add(Core.bundle.format("mech.minespeed", (int)(mech.mineSpeed * 100f))); + table.row(); + table.add(Core.bundle.format("mech.minepower", mech.drillPower)); + table.row(); + } + } + + public static void displayUnit(Table table, UnitType unit){ + table.table(title -> { + title.addImage(unit.getContentIcon()).size(8 * 6); + title.add("[accent]" + unit.localizedName()).padLeft(5); + }); + + table.row(); + + table.addImage("white").height(3).color(Color.LIGHT_GRAY).pad(15).padLeft(0).padRight(0).fillX(); + + table.row(); + + if(unit.description != null){ + table.add(unit.description).padLeft(5).padRight(5).width(400f).wrap().fillX(); + table.row(); + + table.addImage("white").height(3).color(Color.LIGHT_GRAY).pad(15).padLeft(0).padRight(0).fillX(); + table.row(); + } + + table.left().defaults().fillX(); + + table.add(Core.bundle.format("unit.health", unit.health)); + table.row(); + table.add(Core.bundle.format("unit.speed", Strings.fixed(unit.speed, 1))); + table.row(); + table.row(); + } +} diff --git a/core/src/io/anuke/mindustry/ui/GridImage.java b/core/src/io/anuke/mindustry/ui/GridImage.java index b7c5ce6700..81916cba40 100644 --- a/core/src/io/anuke/mindustry/ui/GridImage.java +++ b/core/src/io/anuke/mindustry/ui/GridImage.java @@ -1,37 +1,38 @@ package io.anuke.mindustry.ui; -import com.badlogic.gdx.graphics.g2d.Batch; -import com.badlogic.gdx.graphics.g2d.TextureRegion; - -import io.anuke.ucore.graphics.Draw; -import io.anuke.ucore.scene.Element; +import io.anuke.arc.graphics.g2d.Fill; +import io.anuke.arc.scene.Element; public class GridImage extends Element{ - private int imageWidth, imageHeight; - - public GridImage(int w, int h){ - this.imageWidth = w; - this.imageHeight = h; - } + private int imageWidth, imageHeight; - public void draw(Batch batch, float alpha){ - TextureRegion blank = Draw.region("white"); - - float xspace = (getWidth() / imageWidth); - float yspace = (getHeight() / imageHeight); - float s = 1f; - - for(int x = 0; x <= imageWidth; x ++){ - batch.draw(blank, (int)(getX() + xspace * x - s), getY() - s, 2, getHeight()+ (x == imageWidth ? 1: 0)); - } - - for(int y = 0; y <= imageHeight; y ++){ - batch.draw(blank, getX() - s, (int)(getY() + y * yspace - s), getWidth(), 2); - } - } - - public void setImageSize(int w, int h){ - this.imageWidth = w; - this.imageHeight = h; - } + public GridImage(int w, int h){ + this.imageWidth = w; + this.imageHeight = h; + } + + @Override + public void draw(){ + float xspace = (getWidth() / imageWidth); + float yspace = (getHeight() / imageHeight); + float s = 1f; + + int minspace = 10; + + int jumpx = (int)(Math.max(minspace, xspace) / xspace); + int jumpy = (int)(Math.max(minspace, yspace) / yspace); + + for(int x = 0; x <= imageWidth; x += jumpx){ + Fill.crect((int)(getX() + xspace * x - s), getY() - s, 2, getHeight() + (x == imageWidth ? 1 : 0)); + } + + for(int y = 0; y <= imageHeight; y += jumpy){ + Fill.crect(getX() - s, (int)(getY() + y * yspace - s), getWidth(), 2); + } + } + + public void setImageSize(int w, int h){ + this.imageWidth = w; + this.imageHeight = h; + } } diff --git a/core/src/io/anuke/mindustry/ui/IntFormat.java b/core/src/io/anuke/mindustry/ui/IntFormat.java new file mode 100644 index 0000000000..22d50ab495 --- /dev/null +++ b/core/src/io/anuke/mindustry/ui/IntFormat.java @@ -0,0 +1,33 @@ +package io.anuke.mindustry.ui; + + +import io.anuke.arc.Core; +import io.anuke.arc.function.Function; + +/** + * A low-garbage way to format bundle strings. + */ +public class IntFormat{ + private final StringBuilder builder = new StringBuilder(); + private final String text; + private int lastValue = Integer.MIN_VALUE; + private Function converter = String::valueOf; + + public IntFormat(String text){ + this.text = text; + } + + public IntFormat(String text, Function converter){ + this.text = text; + this.converter = converter; + } + + public CharSequence get(int value){ + if(lastValue != value){ + builder.setLength(0); + builder.append(Core.bundle.format(text, converter.get(value))); + } + lastValue = value; + return builder; + } +} diff --git a/core/src/io/anuke/mindustry/ui/ItemDisplay.java b/core/src/io/anuke/mindustry/ui/ItemDisplay.java new file mode 100644 index 0000000000..728d99a710 --- /dev/null +++ b/core/src/io/anuke/mindustry/ui/ItemDisplay.java @@ -0,0 +1,23 @@ +package io.anuke.mindustry.ui; + +import io.anuke.arc.scene.ui.layout.Table; +import io.anuke.mindustry.type.Item; +import io.anuke.mindustry.type.ItemStack; + +/** An item image with text. */ +public class ItemDisplay extends Table{ + public final Item item; + public final int amount; + + public ItemDisplay(Item item){ + this(item, 0); + } + + public ItemDisplay(Item item, int amount){ + add(new ItemImage(new ItemStack(item, amount))).size(8 * 4); + add(item.localizedName()).padLeft(4); + + this.item = item; + this.amount = amount; + } +} diff --git a/core/src/io/anuke/mindustry/ui/ItemImage.java b/core/src/io/anuke/mindustry/ui/ItemImage.java new file mode 100644 index 0000000000..e7bed333b1 --- /dev/null +++ b/core/src/io/anuke/mindustry/ui/ItemImage.java @@ -0,0 +1,29 @@ +package io.anuke.mindustry.ui; + +import io.anuke.arc.graphics.g2d.TextureRegion; +import io.anuke.arc.scene.ui.Image; +import io.anuke.arc.scene.ui.layout.Stack; +import io.anuke.arc.scene.ui.layout.Table; +import io.anuke.mindustry.type.Item.Icon; +import io.anuke.mindustry.type.ItemStack; + +public class ItemImage extends Stack{ + + public ItemImage(TextureRegion region, int amount){ + Table t = new Table().left().bottom(); + t.add(amount + "").name("item-label"); + + add(new Image(region)); + add(t); + } + + public ItemImage(ItemStack stack){ + add(new Image(stack.item.icon(Icon.large))); + + if(stack.amount != 0){ + Table t = new Table().left().bottom(); + t.add(stack.amount + "").name("item-label"); + add(t); + } + } +} diff --git a/core/src/io/anuke/mindustry/ui/ItemsDisplay.java b/core/src/io/anuke/mindustry/ui/ItemsDisplay.java new file mode 100644 index 0000000000..684cc07aa1 --- /dev/null +++ b/core/src/io/anuke/mindustry/ui/ItemsDisplay.java @@ -0,0 +1,44 @@ +package io.anuke.mindustry.ui; + +import io.anuke.arc.collection.ObjectIntMap; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.scene.ui.layout.Table; +import io.anuke.mindustry.type.Item; +import io.anuke.mindustry.type.Item.Icon; +import io.anuke.mindustry.type.ItemType; + +import java.text.NumberFormat; +import java.util.Locale; + +import static io.anuke.mindustry.Vars.content; +import static io.anuke.mindustry.Vars.data; + +/** Displays a list of items, e.g. launched items.*/ +public class ItemsDisplay extends Table{ + private static final NumberFormat format = NumberFormat.getNumberInstance(Locale.getDefault()); + + public ItemsDisplay(){ + rebuild(); + } + + public void rebuild(){ + clear(); + top().left(); + margin(0); + + table("flat", t -> { + t.margin(10).marginLeft(15).marginTop(15f); + t.add("$launcheditems").colspan(3).left().padBottom(5); + t.row(); + ObjectIntMap items = data.items(); + for(Item item : content.items()){ + if(item.type == ItemType.material && data.isUnlocked(item)){ + t.label(() -> format.format(items.get(item, 0))).left(); + t.addImage(item.icon(Icon.medium)).size(8 * 3).padLeft(4).padRight(4); + t.add(item.localizedName()).color(Color.LIGHT_GRAY).left(); + t.row(); + } + } + }); + } +} diff --git a/core/src/io/anuke/mindustry/ui/Links.java b/core/src/io/anuke/mindustry/ui/Links.java index facbcf45e7..41a1196d70 100644 --- a/core/src/io/anuke/mindustry/ui/Links.java +++ b/core/src/io/anuke/mindustry/ui/Links.java @@ -1,20 +1,28 @@ package io.anuke.mindustry.ui; -import com.badlogic.gdx.graphics.Color; -import io.anuke.ucore.util.Bundles; +import io.anuke.arc.Core; +import io.anuke.arc.graphics.Color; -public class Links { - private static final LinkEntry[] links = { - new LinkEntry("discord", "https://discord.gg/BKADYds", Color.valueOf("7289da")), +public class Links{ + private static LinkEntry[] links; + + private static void createLinks(){ + links = new LinkEntry[]{ + new LinkEntry("discord", "https://discord.gg/mindustry", Color.valueOf("7289da")), new LinkEntry("trello", "https://trello.com/b/aE2tcUwF", Color.valueOf("026aa7")), - new LinkEntry("wiki", "http://mindustry.wikia.com/wiki/Mindustry_Wiki", Color.valueOf("0f142f")), + new LinkEntry("wiki", "https://mindustrygame.github.io/wiki/", Color.valueOf("0f142f")), new LinkEntry("itch.io", "https://anuke.itch.io/mindustry", Color.valueOf("fa5c5c")), new LinkEntry("google-play", "https://play.google.com/store/apps/details?id=io.anuke.mindustry", Color.valueOf("689f38")), new LinkEntry("github", "https://github.com/Anuken/Mindustry/", Color.valueOf("24292e")), - new LinkEntry("dev-builds", "https://github.com/Anuken/Mindustry/wiki", Color.valueOf("fafbfc")), - }; + new LinkEntry("dev-builds", "https://jenkins.hellomouse.net/job/mindustry/", Color.valueOf("fafbfc")) + }; + } public static LinkEntry[] getLinks(){ + if(links == null){ + createLinks(); + } + return links; } @@ -22,10 +30,10 @@ public class Links { public final String name, description, link; public final Color color; - public LinkEntry(String name, String link, Color color) { + public LinkEntry(String name, String link, Color color){ this.name = name; this.color = color; - this.description = Bundles.getNotNull("text.link." + name +".description"); + this.description = Core.bundle.getNotNull("link." + name + ".description"); this.link = link; } } diff --git a/core/src/io/anuke/mindustry/ui/LiquidDisplay.java b/core/src/io/anuke/mindustry/ui/LiquidDisplay.java new file mode 100644 index 0000000000..86f5323102 --- /dev/null +++ b/core/src/io/anuke/mindustry/ui/LiquidDisplay.java @@ -0,0 +1,38 @@ +package io.anuke.mindustry.ui; + +import io.anuke.arc.graphics.Color; +import io.anuke.arc.scene.ui.Image; +import io.anuke.arc.scene.ui.layout.Stack; +import io.anuke.arc.scene.ui.layout.Table; +import io.anuke.arc.util.Strings; +import io.anuke.mindustry.type.Liquid; +import io.anuke.mindustry.world.meta.StatUnit; + +/** An ItemDisplay, but for liquids. */ +public class LiquidDisplay extends Table{ + public final Liquid liquid; + public final float amount; + public final boolean perSecond; + + public LiquidDisplay(Liquid liquid, float amount, boolean perSecond){ + this.liquid = liquid; + this.amount = amount; + this.perSecond = perSecond; + + add(new Stack(){{ + add(new Image(liquid.getContentIcon())); + + if(amount != 0){ + Table t = new Table().left().bottom(); + t.add(Strings.autoFixed(amount, 1)); + add(t); + } + }}).size(8 * 4).padRight(3); + + if(perSecond){ + add(StatUnit.perSecond.localized()).padLeft(2).padRight(5).color(Color.LIGHT_GRAY); + } + + add(liquid.localizedName()); + } +} diff --git a/core/src/io/anuke/mindustry/ui/MenuButton.java b/core/src/io/anuke/mindustry/ui/MenuButton.java index c489923e72..7651ff7a6c 100644 --- a/core/src/io/anuke/mindustry/ui/MenuButton.java +++ b/core/src/io/anuke/mindustry/ui/MenuButton.java @@ -1,38 +1,33 @@ package io.anuke.mindustry.ui; -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.utils.Align; -import io.anuke.ucore.function.Listenable; -import io.anuke.ucore.scene.ui.TextButton; +import io.anuke.arc.graphics.Color; +import io.anuke.arc.scene.ui.TextButton; +import io.anuke.arc.util.Align; + +import static io.anuke.mindustry.Vars.iconsize; public class MenuButton extends TextButton{ - public MenuButton(String icon, String text, Listenable clicked){ - this(icon, text, null, clicked); - } - - public MenuButton(String icon, String text, String description, Listenable clicked){ - super("default"); - float s = 70f; + public MenuButton(String icon, String text, Runnable clicked){ + this(icon, text, null, clicked); + } - clicked(clicked); + public MenuButton(String icon, String text, String description, Runnable clicked){ + super("default"); - clearChildren(); + clicked(clicked); + clearChildren(); - margin(0); + margin(0); - table(t -> { - t.addImage(icon).size(14*3); - t.update(() -> t.setBackground(getClickListener().isOver() || getClickListener().isVisualPressed() ? "button-over" : "button")); - }).size(s - 5, s); + table(t -> { + t.addImage(icon).size(iconsize).padLeft(6); - - table(t -> { - t.add(text).wrap().growX().get().setAlignment(Align.center, Align.left); - if(description != null){ - t.row(); - t.add(description).color(Color.LIGHT_GRAY); - } - }).padLeft(5).growX(); - } + t.add(text).wrap().growX().get().setAlignment(Align.center, Align.left); + if(description != null){ + t.row(); + t.add(description).color(Color.LIGHT_GRAY); + } + }).padLeft(5).growX(); + } } diff --git a/core/src/io/anuke/mindustry/ui/Minimap.java b/core/src/io/anuke/mindustry/ui/Minimap.java new file mode 100644 index 0000000000..a0e4811660 --- /dev/null +++ b/core/src/io/anuke/mindustry/ui/Minimap.java @@ -0,0 +1,99 @@ +package io.anuke.mindustry.ui; + +import io.anuke.arc.Core; +import io.anuke.arc.graphics.g2d.Draw; +import io.anuke.arc.input.KeyCode; +import io.anuke.arc.scene.Element; +import io.anuke.arc.scene.event.*; +import io.anuke.arc.scene.ui.layout.*; + +import static io.anuke.mindustry.Vars.*; + +public class Minimap extends Table{ + + public Minimap(){ + background("pane"); + float margin = 5f; + touchable(Touchable.enabled); + + add(new Element(){ + { + setSize(Unit.dp.scl(140f)); + } + + @Override + public void act(float delta){ + setPosition(margin, margin); + + super.act(delta); + } + + @Override + public void draw(){ + if(renderer.minimap.getRegion() == null) return; + + Draw.rect(renderer.minimap.getRegion(), x + width / 2f, y + height / 2f, width, height); + + if(renderer.minimap.getTexture() != null){ + renderer.minimap.drawEntities(x, y, width, height); + } + } + }).size(140f); + + margin(margin); + + addListener(new InputListener(){ + @Override + public boolean scrolled(InputEvent event, float x, float y, float amountx, float amounty){ + renderer.minimap.zoomBy(amounty); + return true; + } + }); + + addListener(new ClickListener(){ + { + tapSquareSize = Unit.dp.scl(11f); + } + + @Override + public void touchUp(InputEvent event, float x, float y, int pointer, KeyCode button){ + if(inTapSquare()){ + super.touchUp(event, x, y, pointer, button); + }else{ + pressed = false; + pressedPointer = -1; + pressedButton = null; + cancelled = false; + } + } + + @Override + public void touchDragged(InputEvent event, float x, float y, int pointer){ + if(!inTapSquare(x, y)){ + invalidateTapSquare(); + } + super.touchDragged(event, x, y, pointer); + + if(mobile){ + float max = Math.min(world.width(), world.height()) / 16f / 2f; + renderer.minimap.setZoom(1f + y / height * (max - 1f)); + } + } + + @Override + public void clicked(InputEvent event, float x, float y){ + ui.minimap.show(); + } + }); + + update(() -> { + + Element e = Core.scene.hit(Core.input.mouseX(), Core.input.mouseY(), true); + if(e != null && e.isDescendantOf(this)){ + Core.scene.setScrollFocus(this); + }else if(Core.scene.getScrollFocus() == this){ + Core.scene.setScrollFocus(null); + } + }); + } +} diff --git a/core/src/io/anuke/mindustry/ui/MobileButton.java b/core/src/io/anuke/mindustry/ui/MobileButton.java new file mode 100644 index 0000000000..102357e80c --- /dev/null +++ b/core/src/io/anuke/mindustry/ui/MobileButton.java @@ -0,0 +1,15 @@ +package io.anuke.mindustry.ui; + +import io.anuke.arc.scene.ui.ImageButton; +import io.anuke.arc.util.Align; + +public class MobileButton extends ImageButton{ + + public MobileButton(String icon, float isize, String text, Runnable listener){ + super(icon); + resizeImage(isize); + clicked(listener); + row(); + add(text).growX().wrap().center().get().setAlignment(Align.center, Align.center); + } +} diff --git a/core/src/io/anuke/mindustry/ui/MultiReqImage.java b/core/src/io/anuke/mindustry/ui/MultiReqImage.java new file mode 100644 index 0000000000..41c9ef2e44 --- /dev/null +++ b/core/src/io/anuke/mindustry/ui/MultiReqImage.java @@ -0,0 +1,31 @@ +package io.anuke.mindustry.ui; + +import io.anuke.arc.collection.Array; +import io.anuke.arc.scene.ui.layout.Stack; +import io.anuke.arc.util.Time; + +public class MultiReqImage extends Stack{ + private Array displays = new Array<>(); + private float time; + + public void add(ReqImage display){ + displays.add(display); + super.add(display); + } + + @Override + public void act(float delta){ + super.act(delta); + + time += Time.delta() / 60f; + + displays.each(req -> req.visible(false)); + + ReqImage valid = displays.find(ReqImage::valid); + if(valid != null){ + valid.visible(true); + }else{ + displays.get((int)(time) % displays.size).visible(true); + } + } +} diff --git a/core/src/io/anuke/mindustry/ui/PressGroup.java b/core/src/io/anuke/mindustry/ui/PressGroup.java deleted file mode 100644 index c2e7add8e2..0000000000 --- a/core/src/io/anuke/mindustry/ui/PressGroup.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.anuke.mindustry.ui; - -import com.badlogic.gdx.utils.Array; - -import io.anuke.ucore.scene.ui.Button; - -public class PressGroup{ - private Array