Merge
@@ -30,6 +30,7 @@ steam_appid.txt
|
|||||||
/android/assets/mindustry-maps/
|
/android/assets/mindustry-maps/
|
||||||
/android/assets/mindustry-saves/
|
/android/assets/mindustry-saves/
|
||||||
/core/assets/gifexport/
|
/core/assets/gifexport/
|
||||||
|
/annotations/src/main/resources/META-INF/services
|
||||||
/core/assets/version.properties
|
/core/assets/version.properties
|
||||||
/core/assets/locales
|
/core/assets/locales
|
||||||
/ios/src/mindustry/gen/
|
/ios/src/mindustry/gen/
|
||||||
|
|||||||
@@ -22,12 +22,14 @@ First, make sure you have [JDK 8](https://adoptopenjdk.net/) installed. Open a t
|
|||||||
#### Windows
|
#### Windows
|
||||||
|
|
||||||
_Running:_ `gradlew desktop:run`
|
_Running:_ `gradlew desktop:run`
|
||||||
_Building:_ `gradlew desktop:dist`
|
_Building:_ `gradlew desktop:dist`
|
||||||
|
_Sprite Packing:_ `gradlew tools:pack`
|
||||||
|
|
||||||
#### Linux/Mac OS
|
#### Linux/Mac OS
|
||||||
|
|
||||||
_Running:_ `./gradlew desktop:run`
|
_Running:_ `./gradlew desktop:run`
|
||||||
_Building:_ `./gradlew desktop:dist`
|
_Building:_ `./gradlew desktop:dist`
|
||||||
|
_Sprite Packing:_ `./gradlew tools:pack`
|
||||||
|
|
||||||
#### Server
|
#### Server
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
### Adding a server to the list
|
||||||
|
|
||||||
|
Mindustry now has a public list of servers that everyone can see and connect to.
|
||||||
|
This is done by letting clients `GET` a [JSON list of servers](https://github.com/Anuken/Mindustry/blob/master/servers.json) in this repository.
|
||||||
|
|
||||||
|
You may want to add your server to this list. The steps for getting this done are as follows:
|
||||||
|
|
||||||
|
1. **Ensure your server is properly moderated.** For the most part, this applies to survival servers, but PvP servers can be affected as well.
|
||||||
|
You'll need to either hire some moderators, or make use of (currently non-existent) anti-grief and anti-curse plugins.
|
||||||
|
*Consider enabling a rate limit:* `config messageRateLimit 2` will make it so that players can only send messages every 2 seconds, for example.
|
||||||
|
2. **Set an approppriate MOTD, name and description.** This is set with `config <name/desc/motd> <value>`. "Approppriate" means that:
|
||||||
|
- Your name or description must reflect the type of server you're hosting.
|
||||||
|
Since new players may be exposed to the server list early on, put in a phrase like "Co-op survival" or "PvP" so players know what they're getting into. Yes, this is also displayed in the server mode info text, but having extra info in the name doesn't hurt.
|
||||||
|
- Make sure players know where to refer to for server support. It should be fairly clear that the server owner is not me, but you.
|
||||||
|
- Try to be professional in your text; use common sense.
|
||||||
|
3. **Get some good maps.** *(optional, but highly recommended)*. Add some maps to your server and set the map rotation to custom-only. You can get maps from the Steam workshop by subscribing and exporting them; using the `#maps` channel on Discord is also an option.
|
||||||
|
4. **Check your server configuration.** *(optional)* I would recommend adding a message rate limit of 1 second (`config messageRateLimit 1`), and disabling connect/disconnect messages to reduce spam (`config showConnectMessages false`).
|
||||||
|
5. Finally, **submit a pull request** to add your server's IP to the list.
|
||||||
|
This should be fairly straightforward: Press the edit button on the [server file](https://github.com/Anuken/Mindustry/blob/master/servers.json), then add a JSON object with a single key, indicating your server address.
|
||||||
|
For example, if your server address is `google.com`, you would add a comma after the last entry and insert:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"address": "google.com"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Then, press the *'submit pull request'* button and I'll take a look at your server. If I have any issues with it, I'll let you know in the PR comments.
|
||||||
@@ -149,10 +149,22 @@ public class AndroidLauncher extends AndroidApplication{
|
|||||||
}});
|
}});
|
||||||
checkFiles(getIntent());
|
checkFiles(getIntent());
|
||||||
|
|
||||||
|
|
||||||
//new external folder
|
//new external folder
|
||||||
Fi data = Core.files.absolute(getContext().getExternalFilesDir(null).getAbsolutePath());
|
Fi data = Core.files.absolute(getContext().getExternalFilesDir(null).getAbsolutePath());
|
||||||
|
Core.settings.setDataDirectory(data);
|
||||||
|
|
||||||
//moved to internal storage if there's no file indicating that it moved
|
//delete old external files due to screwup
|
||||||
|
if(Core.files.local("files_moved").exists() && !Core.files.local("files_moved_103").exists()){
|
||||||
|
for(Fi fi : data.list()){
|
||||||
|
fi.deleteDirectory();
|
||||||
|
}
|
||||||
|
|
||||||
|
Core.files.local("files_moved").delete();
|
||||||
|
Core.files.local("files_moved_103").writeString("files moved again");
|
||||||
|
}
|
||||||
|
|
||||||
|
//move to internal storage if there's no file indicating that it moved
|
||||||
if(!Core.files.local("files_moved").exists()){
|
if(!Core.files.local("files_moved").exists()){
|
||||||
Log.info("Moving files to external storage...");
|
Log.info("Moving files to external storage...");
|
||||||
|
|
||||||
@@ -160,17 +172,16 @@ public class AndroidLauncher extends AndroidApplication{
|
|||||||
//current local storage folder
|
//current local storage folder
|
||||||
Fi src = Core.files.absolute(Core.files.getLocalStoragePath());
|
Fi src = Core.files.absolute(Core.files.getLocalStoragePath());
|
||||||
for(Fi fi : src.list()){
|
for(Fi fi : src.list()){
|
||||||
fi.copyTo(data.child(fi.name()));
|
fi.copyTo(data);
|
||||||
}
|
}
|
||||||
//create marker
|
//create marker
|
||||||
Core.files.local("files_moved").writeString("files moved to " + data);
|
Core.files.local("files_moved").writeString("files moved to " + data);
|
||||||
|
Core.files.local("files_moved_103").writeString("files moved again");
|
||||||
Log.info("Files moved.");
|
Log.info("Files moved.");
|
||||||
}catch(Throwable t){
|
}catch(Throwable t){
|
||||||
Log.err("Failed to move files!");
|
Log.err("Failed to move files!");
|
||||||
t.printStackTrace();
|
t.printStackTrace();
|
||||||
}
|
}
|
||||||
}else{
|
|
||||||
Core.settings.setDataDirectory(data);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,5 +2,4 @@ apply plugin: "java"
|
|||||||
|
|
||||||
sourceCompatibility = 1.8
|
sourceCompatibility = 1.8
|
||||||
sourceSets.main.java.srcDirs = ["src/main/java/"]
|
sourceSets.main.java.srcDirs = ["src/main/java/"]
|
||||||
sourceSets.main.resources.srcDirs = ["src/main/resources/"]
|
sourceSets.main.resources.srcDirs = ["src/main/resources/"]
|
||||||
|
|
||||||
@@ -3,10 +3,87 @@ package mindustry.annotations;
|
|||||||
import java.lang.annotation.*;
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
public class Annotations{
|
public class Annotations{
|
||||||
|
//region entity interfaces
|
||||||
|
|
||||||
|
public enum DrawLayer{
|
||||||
|
floor,
|
||||||
|
floorOver,
|
||||||
|
groundShadows,
|
||||||
|
groundUnder,
|
||||||
|
ground,
|
||||||
|
flyingShadows,
|
||||||
|
flying,
|
||||||
|
bullets,
|
||||||
|
effects,
|
||||||
|
names,
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Indicates that a method overrides other methods. */
|
||||||
|
@Target({ElementType.METHOD})
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface Replace{
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Indicates that a component field is read-only. */
|
||||||
|
@Target({ElementType.FIELD, ElementType.METHOD})
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface ReadOnly{
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Indicates multiple inheritance on a component type. */
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface Component{
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Indicates that a method is implemented by the annotation processor. */
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface InternalImpl{
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Indicates priority of a method in an entity. Methods with higher priority are done last. */
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface MethodPriority{
|
||||||
|
float value();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Indicates that a component def is present on all entities. */
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface BaseComponent{
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Creates a group that only examines entities that have all the components listed. */
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface GroupDef{
|
||||||
|
Class[] value();
|
||||||
|
boolean spatial() default false;
|
||||||
|
boolean mapping() default false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Indicates an entity definition. */
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface EntityDef{
|
||||||
|
Class[] value();
|
||||||
|
boolean isFinal() default true;
|
||||||
|
boolean pooled() default false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Indicates an internal interface for entity components. */
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface EntityInterface{
|
||||||
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
//region misc. utility
|
||||||
|
|
||||||
@Target(ElementType.TYPE)
|
@Target(ElementType.TYPE)
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
public @interface StyleDefaults {
|
public @interface StyleDefaults{
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Indicates that a method should always call its super version. */
|
/** Indicates that a method should always call its super version. */
|
||||||
@@ -16,10 +93,10 @@ public class Annotations{
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Annotation that allows overriding CallSuper annotation. To be used on method that overrides method with CallSuper annotation from parent class.*/
|
/** Annotation that allows overriding CallSuper annotation. To be used on method that overrides method with CallSuper annotation from parent class. */
|
||||||
@Target(ElementType.METHOD)
|
@Target(ElementType.METHOD)
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
public @interface OverrideCallSuper {
|
public @interface OverrideCallSuper{
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Marks a class as serializable. */
|
/** Marks a class as serializable. */
|
||||||
@@ -29,6 +106,9 @@ public class Annotations{
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
//region struct
|
||||||
|
|
||||||
/** Marks a class as a special value type struct. Class name must end in 'Struct'. */
|
/** Marks a class as a special value type struct. Class name must end in 'Struct'. */
|
||||||
@Target(ElementType.TYPE)
|
@Target(ElementType.TYPE)
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
@@ -44,6 +124,9 @@ public class Annotations{
|
|||||||
int value();
|
int value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
|
//region remote
|
||||||
|
|
||||||
public enum PacketPriority{
|
public enum PacketPriority{
|
||||||
/** Gets put in a queue and processed if not connected. */
|
/** Gets put in a queue and processed if not connected. */
|
||||||
normal,
|
normal,
|
||||||
@@ -138,4 +221,6 @@ public class Annotations{
|
|||||||
public @interface ReadClass{
|
public @interface ReadClass{
|
||||||
Class<?> value();
|
Class<?> value();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//endregion
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,203 @@
|
|||||||
|
package mindustry.annotations;
|
||||||
|
|
||||||
|
import arc.files.*;
|
||||||
|
import arc.struct.Array;
|
||||||
|
import arc.util.*;
|
||||||
|
import arc.util.Log.*;
|
||||||
|
import com.squareup.javapoet.*;
|
||||||
|
import com.sun.source.util.*;
|
||||||
|
import mindustry.annotations.util.*;
|
||||||
|
|
||||||
|
import javax.annotation.processing.*;
|
||||||
|
import javax.lang.model.*;
|
||||||
|
import javax.lang.model.element.*;
|
||||||
|
import javax.lang.model.type.*;
|
||||||
|
import javax.lang.model.util.*;
|
||||||
|
import javax.tools.Diagnostic.*;
|
||||||
|
import javax.tools.*;
|
||||||
|
import java.io.*;
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
import java.lang.reflect.*;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
@SupportedSourceVersion(SourceVersion.RELEASE_8)
|
||||||
|
public abstract class BaseProcessor extends AbstractProcessor{
|
||||||
|
/** Name of the base package to put all the generated classes. */
|
||||||
|
public static final String packageName = "mindustry.gen";
|
||||||
|
|
||||||
|
public static Types typeu;
|
||||||
|
public static Elements elementu;
|
||||||
|
public static Filer filer;
|
||||||
|
public static Messager messager;
|
||||||
|
public static Trees trees;
|
||||||
|
|
||||||
|
protected int round;
|
||||||
|
protected int rounds = 1;
|
||||||
|
protected RoundEnvironment env;
|
||||||
|
protected Fi rootDirectory;
|
||||||
|
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getDefault(String value){
|
||||||
|
switch(value){
|
||||||
|
case "float":
|
||||||
|
case "double":
|
||||||
|
case "int":
|
||||||
|
case "long":
|
||||||
|
case "short":
|
||||||
|
case "char":
|
||||||
|
case "byte":
|
||||||
|
return "0";
|
||||||
|
case "boolean":
|
||||||
|
return "false";
|
||||||
|
default:
|
||||||
|
return "null";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String simpleName(String str){
|
||||||
|
return str.contains(".") ? str.substring(str.lastIndexOf('.') + 1) : str;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TypeName tname(String name) throws Exception{
|
||||||
|
Constructor<TypeName> cons = TypeName.class.getDeclaredConstructor(String.class);
|
||||||
|
cons.setAccessible(true);
|
||||||
|
return cons.newInstance(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TypeName tname(Class<?> c){
|
||||||
|
return ClassName.get(c).box();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TypeVariableName getTVN(TypeParameterElement element) {
|
||||||
|
String name = element.getSimpleName().toString();
|
||||||
|
List<? extends TypeMirror> boundsMirrors = element.getBounds();
|
||||||
|
|
||||||
|
List<TypeName> boundsTypeNames = new ArrayList<>();
|
||||||
|
for (TypeMirror typeMirror : boundsMirrors) {
|
||||||
|
boundsTypeNames.add(TypeName.get(typeMirror));
|
||||||
|
}
|
||||||
|
|
||||||
|
return TypeVariableName.get(name, boundsTypeNames.toArray(new TypeName[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void write(TypeSpec.Builder builder) throws Exception{
|
||||||
|
write(builder, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void write(TypeSpec.Builder builder, Array<String> imports) throws Exception{
|
||||||
|
JavaFile file = JavaFile.builder(packageName, builder.build()).skipJavaLangImports(true).build();
|
||||||
|
|
||||||
|
if(imports != null){
|
||||||
|
String rawSource = file.toString();
|
||||||
|
Array<String> result = new Array<>();
|
||||||
|
for (String s : rawSource.split("\n", -1)) {
|
||||||
|
result.add(s);
|
||||||
|
if (s.startsWith("package ")) {
|
||||||
|
result.add("");
|
||||||
|
for (String i : imports) {
|
||||||
|
result.add(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String out = result.toString("\n");
|
||||||
|
JavaFileObject object = filer.createSourceFile(file.packageName + "." + file.typeSpec.name, file.typeSpec.originatingElements.toArray(new Element[0]));
|
||||||
|
OutputStream stream = object.openOutputStream();
|
||||||
|
stream.write(out.getBytes());
|
||||||
|
stream.close();
|
||||||
|
}else{
|
||||||
|
file.writeTo(filer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Array<Selement> elements(Class<? extends Annotation> type){
|
||||||
|
return Array.with(env.getElementsAnnotatedWith(type)).map(Selement::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Array<Stype> types(Class<? extends Annotation> type){
|
||||||
|
return Array.with(env.getElementsAnnotatedWith(type)).select(e -> e instanceof TypeElement)
|
||||||
|
.map(e -> new Stype((TypeElement)e));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Array<Svar> fields(Class<? extends Annotation> type){
|
||||||
|
return Array.with(env.getElementsAnnotatedWith(type)).select(e -> e instanceof VariableElement)
|
||||||
|
.map(e -> new Svar((VariableElement)e));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Array<Smethod> methods(Class<? extends Annotation> type){
|
||||||
|
return Array.with(env.getElementsAnnotatedWith(type)).select(e -> e instanceof ExecutableElement)
|
||||||
|
.map(e -> new Smethod((ExecutableElement)e));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void err(String message){
|
||||||
|
messager.printMessage(Kind.ERROR, message);
|
||||||
|
Log.err("[CODEGEN ERROR] " +message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void err(String message, Element elem){
|
||||||
|
messager.printMessage(Kind.ERROR, message, elem);
|
||||||
|
Log.err("[CODEGEN ERROR] " + message + ": " + elem);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void err(String message, Selement elem){
|
||||||
|
err(message, elem.e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized void init(ProcessingEnvironment env){
|
||||||
|
super.init(env);
|
||||||
|
|
||||||
|
trees = Trees.instance(env);
|
||||||
|
typeu = env.getTypeUtils();
|
||||||
|
elementu = env.getElementUtils();
|
||||||
|
filer = env.getFiler();
|
||||||
|
messager = env.getMessager();
|
||||||
|
|
||||||
|
Log.setLogLevel(LogLevel.info);
|
||||||
|
|
||||||
|
if(System.getProperty("debug") != null){
|
||||||
|
Log.setLogLevel(LogLevel.debug);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv){
|
||||||
|
if(round++ >= rounds) return false; //only process 1 round
|
||||||
|
if(rootDirectory == null){
|
||||||
|
try{
|
||||||
|
String path = Fi.get(filer.getResource(StandardLocation.CLASS_OUTPUT, "no", "no")
|
||||||
|
.toUri().toURL().toString().substring(OS.isWindows ? 6 : "file:".length()))
|
||||||
|
.parent().parent().parent().parent().parent().parent().parent().toString().replace("%20", " ");
|
||||||
|
rootDirectory = Fi.get(path);
|
||||||
|
}catch(IOException e){
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.env = roundEnv;
|
||||||
|
try{
|
||||||
|
process(roundEnv);
|
||||||
|
}catch(Throwable e){
|
||||||
|
e.printStackTrace();
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SourceVersion getSupportedSourceVersion(){
|
||||||
|
return SourceVersion.RELEASE_8;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void process(RoundEnvironment env) throws Exception{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
package mindustry.annotations;
|
|
||||||
|
|
||||||
import com.sun.source.util.*;
|
|
||||||
import com.sun.tools.javac.tree.*;
|
|
||||||
import com.sun.tools.javac.tree.JCTree.*;
|
|
||||||
import mindustry.annotations.Annotations.*;
|
|
||||||
|
|
||||||
import javax.annotation.processing.*;
|
|
||||||
import javax.lang.model.*;
|
|
||||||
import javax.lang.model.element.*;
|
|
||||||
import javax.tools.Diagnostic.*;
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
@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<? extends TypeElement> 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
package mindustry.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 mindustry.annotations.Annotations.CallSuper;
|
|
||||||
|
|
||||||
import java.lang.annotation.Annotation;
|
|
||||||
|
|
||||||
class CodeAnalyzerTreeScanner extends TreePathScanner<Object, Trees> {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public boolean checkScope (Scope members) {
|
|
||||||
Iterable<Symbol> it;
|
|
||||||
try{
|
|
||||||
it = (Iterable<Symbol>)members.getClass().getMethod("getElements").invoke(members);
|
|
||||||
}catch(Throwable t){
|
|
||||||
try{
|
|
||||||
it = (Iterable<Symbol>)members.getClass().getMethod("getSymbols").invoke(members);
|
|
||||||
}catch(Exception e){
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Symbol s : it) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
package mindustry.annotations;
|
|
||||||
|
|
||||||
import com.squareup.javapoet.*;
|
|
||||||
import mindustry.annotations.Annotations.Loc;
|
|
||||||
import mindustry.annotations.Annotations.Remote;
|
|
||||||
import mindustry.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({
|
|
||||||
"mindustry.annotations.Annotations.Remote",
|
|
||||||
"mindustry.annotations.Annotations.WriteClass",
|
|
||||||
"mindustry.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 = "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<String, ClassSerializer> serializers;
|
|
||||||
//all elements with the Remote annotation
|
|
||||||
private Set<? extends Element> elements;
|
|
||||||
//map of all classes to generate by name
|
|
||||||
private HashMap<String, ClassEntry> classMap;
|
|
||||||
//list of all method entries
|
|
||||||
private ArrayList<MethodEntry> methods;
|
|
||||||
//list of all method entries
|
|
||||||
private ArrayList<ClassEntry> 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<? extends TypeElement> 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<Element> 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,131 +0,0 @@
|
|||||||
package mindustry.annotations;
|
|
||||||
|
|
||||||
import com.squareup.javapoet.*;
|
|
||||||
import mindustry.annotations.Annotations.*;
|
|
||||||
|
|
||||||
import javax.annotation.processing.*;
|
|
||||||
import javax.lang.model.*;
|
|
||||||
import javax.lang.model.element.Modifier;
|
|
||||||
import javax.lang.model.element.*;
|
|
||||||
import javax.lang.model.util.*;
|
|
||||||
import javax.tools.Diagnostic.*;
|
|
||||||
import java.io.*;
|
|
||||||
import java.lang.reflect.*;
|
|
||||||
import java.util.*;
|
|
||||||
import java.util.zip.*;
|
|
||||||
|
|
||||||
@SupportedSourceVersion(SourceVersion.RELEASE_8)
|
|
||||||
@SupportedAnnotationTypes("mindustry.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 = "mindustry.gen";
|
|
||||||
private static final String data = "eJztV0tvGzcQvvfQ3zDRIeDCKhsbQVDUsgP5UViH2IHl9BIEBsUdSYxX3C3Jlawm+XH9Z53hUg/bkuOmOfRQwfDuDme++ebBWe6PfwU3/wTwUU2VLJQdSYfDAnWQvxkschjCAUyMzWtPetJikF2nzzG8deXU5OjkW6VvMPTRGVWYP0mgC+W9HGE4Qbp1mEcg0Zo5E9C1sn2AofQYulqj92ZQoAiuxqVc2Loo2iCU03JYWy2PS+v3OndJNF7bDW1rSnk0D3hUD4foDjNRtWGQwU+HQIGZoajAWB+U1VgOYROOZx+Wgm4eMzJ7ghpoyo14Cl5FsQ2I4PsPcE2/XXpssk7kOMw6mEJe9KXxXZu70uTM4Jjz2Hl9CJ79xCc5LN25mqBoqUZPVosy9DEEY0eebnTtMKZ5iaDddgRd2oA2MGO+XqIvi2mq0xJAqQ0ARHzA8dncywWar91QaZwanMkUS7eqCqNVMKW9x+qRuO6wug3R8GGLvsEwLnMYMZBS6z3XrIgWidYhLgYfyQ50IyKrkZbGTssbjHU4Lh1KVVWbvaUNEf8fUFXYX+rt7vnJ5UXv5Lp3Et30g6NagDK55RZpHrNoyUaxwx+PyA+XLtZCaYBabSpoOzlptttX0uM8oen7aJsqnhLkkixmyPlFjlLe1kL0a/ER6YVis4UXKO2YCbYyNkCBnBQv6ToKY5Gt9kauAveZxVkjYc2fYe8DT4bSCTY2tP5iny4dxuGbnQPY4+3Cxu9N1GdODJAJcTxWTmmaOzI3IxOEl5ok3SBM1obdVxl0OvAyA9iB7Zq0uNtoM9cvy9gpvLoIiXAjW+1mnwZi7Ht5pDy+enlc8k5Fq+kqmG8EpBnQIEn8o1aFp25a/C66B60sgzB25Sx6uaxtMBM8vdVYMbBoHakc3r3rnchYvjhdiBGDM1csPD4cMr3Sc8ZSGHVtuJ+X/e8Xk2TZcKLFOtR2rVYizM8EdDqpwlxkDJZKeCcnUfYLl4+f2MFEhbG8pE0uMhqXt4Gntk/hM3Ti8k0JTSgM8zCWqg7LKPiyWcurKYr1PDaYi0x+Wi08gVaOkdYT85paa+Enbubo4NTWE3QRvtO87eg1Qy/gWeluerQd47w9BCRSsHWdfd6XebGcGptMoKw58Dhe4IwrXJYFKkspEKnYfImdRB0R7+GAasezjRIXamdhSP2M+1/rjv7cB5xI5Zya67KaN2BteNFOFvE2CtPUYObJxbN/1Sxb9hw8f/7dgbsMnKoMcAbjlIezWAcecJRxkmHcGacFTmg48xrLuYBnyuUzerl185y8UPkW6YbPn+HZWFJhtmlmMSKUY+XfUC8m8NgBG52uDeXrVFnYhv3Py3u9sb7X9wu8eMUE9x1GArUoAW0rNyVw42r3WwfwanDQHx1+9FhcMYii4y6E/6fvf3T6UiaZLA3BtXO9Zvvf0Xn2MahNEfmv1unr42peYe9Cxk+chD6gU5qcNla8/GQbSwfhJyvXvslmpC2oxOXAUIe9TgegXfgVXizXOSxN4RSlW9nEnK4eGzsGolO9pw+6xXC6d/pa0yDBzs7db6ZHGEczPgSbO+88qBpVMYjSbH/Trgn0vUM8+oE+O67otMbt8uWHvwGqGwCj";
|
|
||||||
|
|
||||||
private int round;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv){
|
|
||||||
if(round++ != 0) return false; //only process 1 round
|
|
||||||
|
|
||||||
try{
|
|
||||||
Set<TypeElement> elements = ElementFilter.typesIn(roundEnv.getElementsAnnotatedWith(Serialize.class));
|
|
||||||
|
|
||||||
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className).addModifiers(Modifier.PUBLIC);
|
|
||||||
classBuilder.addStaticBlock(CodeBlock.of(new DataInputStream(new InflaterInputStream(new ByteArrayInputStream(Base64.getDecoder().decode(data)))).readUTF()));
|
|
||||||
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("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<VariableElement> 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("arc.Core.settings.getSerializer(" + typeName + ".class).write(stream, object." + name + ")");
|
|
||||||
readMethod.addStatement("object." + name + " = (" + typeName + ")arc.Core.settings.getSerializer(" + typeName + ".class).read(stream)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
readMethod.addStatement("return object");
|
|
||||||
|
|
||||||
serializer.addMethod(writeMethod.build());
|
|
||||||
serializer.addMethod(readMethod.build());
|
|
||||||
|
|
||||||
method.addStatement("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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@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();
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,226 +0,0 @@
|
|||||||
package mindustry.annotations;
|
|
||||||
|
|
||||||
import com.squareup.javapoet.*;
|
|
||||||
import mindustry.annotations.Annotations.Struct;
|
|
||||||
import mindustry.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({
|
|
||||||
"mindustry.annotations.Annotations.Struct"
|
|
||||||
})
|
|
||||||
public class StructAnnotationProcessor extends AbstractProcessor{
|
|
||||||
/** Name of the base package to put all the generated classes. */
|
|
||||||
private static final String packageName = "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<? extends TypeElement> annotations, RoundEnvironment roundEnv){
|
|
||||||
if(round++ != 0) return false; //only process 1 round
|
|
||||||
|
|
||||||
try{
|
|
||||||
Set<TypeElement> 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<VariableElement> 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("<br> ").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.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
package mindustry.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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package mindustry.annotations;
|
package mindustry.annotations.impl;
|
||||||
|
|
||||||
import arc.files.*;
|
import arc.files.*;
|
||||||
import arc.scene.style.*;
|
import arc.scene.style.*;
|
||||||
@@ -6,51 +6,21 @@ import arc.struct.*;
|
|||||||
import arc.util.serialization.*;
|
import arc.util.serialization.*;
|
||||||
import com.squareup.javapoet.*;
|
import com.squareup.javapoet.*;
|
||||||
import mindustry.annotations.Annotations.*;
|
import mindustry.annotations.Annotations.*;
|
||||||
|
import mindustry.annotations.*;
|
||||||
|
|
||||||
import javax.annotation.processing.*;
|
import javax.annotation.processing.*;
|
||||||
import javax.lang.model.*;
|
import javax.lang.model.*;
|
||||||
import javax.lang.model.element.*;
|
import javax.lang.model.element.*;
|
||||||
import javax.tools.Diagnostic.*;
|
|
||||||
import javax.tools.*;
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
@SupportedSourceVersion(SourceVersion.RELEASE_8)
|
|
||||||
@SupportedAnnotationTypes("mindustry.annotations.Annotations.StyleDefaults")
|
@SupportedAnnotationTypes("mindustry.annotations.Annotations.StyleDefaults")
|
||||||
public class AssetsAnnotationProcessor extends AbstractProcessor{
|
public class AssetsProcess extends BaseProcessor{
|
||||||
/** Name of the base package to put all the generated classes. */
|
|
||||||
private static final String packageName = "mindustry.gen";
|
|
||||||
private String path;
|
|
||||||
private int round;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public synchronized void init(ProcessingEnvironment processingEnv){
|
public void process(RoundEnvironment env) throws Exception{
|
||||||
super.init(processingEnv);
|
processSounds("Sounds", rootDirectory + "/core/assets/sounds", "arc.audio.Sound");
|
||||||
//put all relevant utils into utils class
|
processSounds("Musics", rootDirectory + "/core/assets/music", "arc.audio.Music");
|
||||||
Utils.typeUtils = processingEnv.getTypeUtils();
|
processUI(env.getElementsAnnotatedWith(StyleDefaults.class));
|
||||||
Utils.elementUtils = processingEnv.getElementUtils();
|
|
||||||
Utils.filer = processingEnv.getFiler();
|
|
||||||
Utils.messager = processingEnv.getMessager();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv){
|
|
||||||
if(round++ != 0) return false; //only process 1 round
|
|
||||||
|
|
||||||
try{
|
|
||||||
path = Fi.get(Utils.filer.createResource(StandardLocation.CLASS_OUTPUT, "no", "no")
|
|
||||||
.toUri().toURL().toString().substring(System.getProperty("os.name").contains("Windows") ? 6 : "file:".length()))
|
|
||||||
.parent().parent().parent().parent().parent().parent().toString();
|
|
||||||
path = path.replace("%20", " ");
|
|
||||||
|
|
||||||
processSounds("Sounds", path + "/assets/sounds", "arc.audio.Sound");
|
|
||||||
processSounds("Musics", path + "/assets/music", "arc.audio.Music");
|
|
||||||
processUI(roundEnv.getElementsAnnotatedWith(StyleDefaults.class));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}catch(Exception e){
|
|
||||||
e.printStackTrace();
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void processUI(Set<? extends Element> elements) throws Exception{
|
void processUI(Set<? extends Element> elements) throws Exception{
|
||||||
@@ -60,8 +30,8 @@ public class AssetsAnnotationProcessor extends AbstractProcessor{
|
|||||||
MethodSpec.Builder load = MethodSpec.methodBuilder("load").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
MethodSpec.Builder load = MethodSpec.methodBuilder("load").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
||||||
MethodSpec.Builder loadStyles = MethodSpec.methodBuilder("loadStyles").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
MethodSpec.Builder loadStyles = MethodSpec.methodBuilder("loadStyles").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
||||||
MethodSpec.Builder icload = MethodSpec.methodBuilder("load").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
MethodSpec.Builder icload = MethodSpec.methodBuilder("load").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
||||||
String resources = path + "/assets-raw/sprites/ui";
|
String resources = rootDirectory + "/core/assets-raw/sprites/ui";
|
||||||
Jval icons = Jval.read(Fi.get(path + "/assets-raw/fontgen/config.json").readString());
|
Jval icons = Jval.read(Fi.get(rootDirectory + "/core/assets-raw/fontgen/config.json").readString());
|
||||||
|
|
||||||
ictype.addField(FieldSpec.builder(ParameterizedTypeName.get(ObjectMap.class, String.class, TextureRegionDrawable.class),
|
ictype.addField(FieldSpec.builder(ParameterizedTypeName.get(ObjectMap.class, String.class, TextureRegionDrawable.class),
|
||||||
"icons", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).initializer("new ObjectMap<>()").build());
|
"icons", Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).initializer("new ObjectMap<>()").build());
|
||||||
@@ -71,17 +41,18 @@ public class AssetsAnnotationProcessor extends AbstractProcessor{
|
|||||||
int code = val.getInt("code", 0);
|
int code = val.getInt("code", 0);
|
||||||
ichtype.addField(FieldSpec.builder(char.class, name, Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).initializer("(char)" + code).build());
|
ichtype.addField(FieldSpec.builder(char.class, name, Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL).initializer("(char)" + code).build());
|
||||||
|
|
||||||
ictype.addField(TextureRegionDrawable.class, name + "Sm", Modifier.PUBLIC, Modifier.STATIC);
|
ictype.addField(TextureRegionDrawable.class, name + "Small", Modifier.PUBLIC, Modifier.STATIC);
|
||||||
icload.addStatement(name + "Sm = mindustry.ui.Fonts.getGlyph(mindustry.ui.Fonts.def, (char)" + code + ")");
|
icload.addStatement(name + "Small = mindustry.ui.Fonts.getGlyph(mindustry.ui.Fonts.def, (char)" + code + ")");
|
||||||
|
|
||||||
ictype.addField(TextureRegionDrawable.class, name, Modifier.PUBLIC, Modifier.STATIC);
|
ictype.addField(TextureRegionDrawable.class, name, Modifier.PUBLIC, Modifier.STATIC);
|
||||||
icload.addStatement(name + " = mindustry.ui.Fonts.getGlyph(mindustry.ui.Fonts.icon, (char)" + code + ")");
|
icload.addStatement(name + " = mindustry.ui.Fonts.getGlyph(mindustry.ui.Fonts.icon, (char)" + code + ")");
|
||||||
|
|
||||||
icload.addStatement("icons.put($S, " + name + ")", name);
|
icload.addStatement("icons.put($S, " + name + ")", name);
|
||||||
|
icload.addStatement("icons.put($S, " + name + "Small)", name + "Small");
|
||||||
}
|
}
|
||||||
|
|
||||||
Fi.get(resources).walk(p -> {
|
Fi.get(resources).walk(p -> {
|
||||||
if(p.nameWithoutExtension().equals(".DS_Store")) return;
|
if(!p.extEquals("png")) return;
|
||||||
|
|
||||||
String filename = p.name();
|
String filename = p.name();
|
||||||
filename = filename.substring(0, filename.indexOf("."));
|
filename = filename.substring(0, filename.indexOf("."));
|
||||||
@@ -107,12 +78,12 @@ public class AssetsAnnotationProcessor extends AbstractProcessor{
|
|||||||
}
|
}
|
||||||
|
|
||||||
ictype.addMethod(icload.build());
|
ictype.addMethod(icload.build());
|
||||||
JavaFile.builder(packageName, ichtype.build()).build().writeTo(Utils.filer);
|
JavaFile.builder(packageName, ichtype.build()).build().writeTo(BaseProcessor.filer);
|
||||||
JavaFile.builder(packageName, ictype.build()).build().writeTo(Utils.filer);
|
JavaFile.builder(packageName, ictype.build()).build().writeTo(BaseProcessor.filer);
|
||||||
|
|
||||||
type.addMethod(load.build());
|
type.addMethod(load.build());
|
||||||
type.addMethod(loadStyles.build());
|
type.addMethod(loadStyles.build());
|
||||||
JavaFile.builder(packageName, type.build()).build().writeTo(Utils.filer);
|
JavaFile.builder(packageName, type.build()).build().writeTo(BaseProcessor.filer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void processSounds(String classname, String path, String rtype) throws Exception{
|
void processSounds(String classname, String path, String rtype) throws Exception{
|
||||||
@@ -126,7 +97,7 @@ public class AssetsAnnotationProcessor extends AbstractProcessor{
|
|||||||
String name = p.nameWithoutExtension();
|
String name = p.nameWithoutExtension();
|
||||||
|
|
||||||
if(names.contains(name)){
|
if(names.contains(name)){
|
||||||
Utils.messager.printMessage(Kind.ERROR, "Duplicate file name: " + p.toString() + "!");
|
BaseProcessor.err("Duplicate file name: " + p.toString() + "!");
|
||||||
}else{
|
}else{
|
||||||
names.add(name);
|
names.add(name);
|
||||||
}
|
}
|
||||||
@@ -152,7 +123,7 @@ public class AssetsAnnotationProcessor extends AbstractProcessor{
|
|||||||
|
|
||||||
type.addMethod(loadBegin.build());
|
type.addMethod(loadBegin.build());
|
||||||
type.addMethod(dispose.build());
|
type.addMethod(dispose.build());
|
||||||
JavaFile.builder(packageName, type.build()).build().writeTo(Utils.filer);
|
JavaFile.builder(packageName, type.build()).build().writeTo(BaseProcessor.filer);
|
||||||
}
|
}
|
||||||
|
|
||||||
static String capitalize(String s){
|
static String capitalize(String s){
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
package mindustry.annotations.impl;
|
||||||
|
|
||||||
|
import com.sun.source.tree.*;
|
||||||
|
import com.sun.source.util.*;
|
||||||
|
import com.sun.tools.javac.code.Scope;
|
||||||
|
import com.sun.tools.javac.code.*;
|
||||||
|
import com.sun.tools.javac.code.Symbol.*;
|
||||||
|
import com.sun.tools.javac.code.Type.*;
|
||||||
|
import com.sun.tools.javac.tree.*;
|
||||||
|
import com.sun.tools.javac.tree.JCTree.*;
|
||||||
|
import mindustry.annotations.Annotations.*;
|
||||||
|
|
||||||
|
import javax.annotation.processing.*;
|
||||||
|
import javax.lang.model.*;
|
||||||
|
import javax.lang.model.element.*;
|
||||||
|
import javax.tools.Diagnostic.*;
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
@SupportedAnnotationTypes({"java.lang.Override"})
|
||||||
|
public class CallSuperProcess extends AbstractProcessor{
|
||||||
|
private Trees trees;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(ProcessingEnvironment pe){
|
||||||
|
super.init(pe);
|
||||||
|
trees = Trees.instance(pe);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv){
|
||||||
|
for(Element e : roundEnv.getElementsAnnotatedWith(Override.class)){
|
||||||
|
if(e.getAnnotation(OverrideCallSuper.class) != null) return false;
|
||||||
|
|
||||||
|
CodeAnalyzerTreeScanner codeScanner = new CodeAnalyzerTreeScanner();
|
||||||
|
codeScanner.methodName = e.getSimpleName().toString();
|
||||||
|
|
||||||
|
TreePath tp = trees.getPath(e.getEnclosingElement());
|
||||||
|
codeScanner.scan(tp, trees);
|
||||||
|
|
||||||
|
if(codeScanner.callSuperUsed){
|
||||||
|
List list = codeScanner.method.getBody().getStatements();
|
||||||
|
|
||||||
|
if(!doesCallSuper(list, codeScanner.methodName)){
|
||||||
|
processingEnv.getMessager().printMessage(Kind.ERROR, "Overriding method '" + codeScanner.methodName + "' 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class CodeAnalyzerTreeScanner extends TreePathScanner<Object, Trees>{
|
||||||
|
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;
|
||||||
|
com.sun.tools.javac.code.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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public boolean checkScope(Scope members){
|
||||||
|
Iterable<Symbol> it;
|
||||||
|
try{
|
||||||
|
it = (Iterable<Symbol>)members.getClass().getMethod("getElements").invoke(members);
|
||||||
|
}catch(Throwable t){
|
||||||
|
try{
|
||||||
|
it = (Iterable<Symbol>)members.getClass().getMethod("getSymbols").invoke(members);
|
||||||
|
}catch(Exception e){
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(Symbol s : it){
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,713 @@
|
|||||||
|
package mindustry.annotations.impl;
|
||||||
|
|
||||||
|
import arc.files.*;
|
||||||
|
import arc.func.*;
|
||||||
|
import arc.struct.*;
|
||||||
|
import arc.util.*;
|
||||||
|
import arc.util.io.*;
|
||||||
|
import arc.util.pooling.*;
|
||||||
|
import arc.util.pooling.Pool.*;
|
||||||
|
import com.squareup.javapoet.*;
|
||||||
|
import com.squareup.javapoet.TypeSpec.*;
|
||||||
|
import com.sun.source.tree.*;
|
||||||
|
import mindustry.annotations.Annotations.*;
|
||||||
|
import mindustry.annotations.*;
|
||||||
|
import mindustry.annotations.util.*;
|
||||||
|
|
||||||
|
import javax.annotation.processing.*;
|
||||||
|
import javax.lang.model.element.*;
|
||||||
|
import javax.lang.model.type.*;
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
|
||||||
|
@SupportedAnnotationTypes({
|
||||||
|
"mindustry.annotations.Annotations.EntityDef",
|
||||||
|
"mindustry.annotations.Annotations.GroupDef",
|
||||||
|
"mindustry.annotations.Annotations.EntityInterface",
|
||||||
|
"mindustry.annotations.Annotations.BaseComponent"
|
||||||
|
})
|
||||||
|
public class EntityProcess extends BaseProcessor{
|
||||||
|
Array<EntityDefinition> definitions = new Array<>();
|
||||||
|
Array<GroupDefinition> groupDefs = new Array<>();
|
||||||
|
Array<Stype> baseComponents;
|
||||||
|
ObjectMap<String, Stype> componentNames = new ObjectMap<>();
|
||||||
|
ObjectMap<Stype, Array<Stype>> componentDependencies = new ObjectMap<>();
|
||||||
|
ObjectMap<Selement, Array<Stype>> defComponents = new ObjectMap<>();
|
||||||
|
ObjectMap<Svar, String> varInitializers = new ObjectMap<>();
|
||||||
|
ObjectMap<Smethod, String> methodBlocks = new ObjectMap<>();
|
||||||
|
ObjectSet<String> imports = new ObjectSet<>();
|
||||||
|
Array<Smethod> allGroups = new Array<>();
|
||||||
|
Array<Selement> allDefs = new Array<>();
|
||||||
|
Array<Stype> allInterfaces = new Array<>();
|
||||||
|
|
||||||
|
{
|
||||||
|
rounds = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process(RoundEnvironment env) throws Exception{
|
||||||
|
allGroups.addAll(methods(GroupDef.class));
|
||||||
|
allDefs.addAll(elements(EntityDef.class));
|
||||||
|
allInterfaces.addAll(types(EntityInterface.class));
|
||||||
|
|
||||||
|
//round 1: generate component interfaces
|
||||||
|
if(round == 1){
|
||||||
|
baseComponents = types(BaseComponent.class);
|
||||||
|
Array<Stype> allComponents = types(Component.class);
|
||||||
|
|
||||||
|
//store code
|
||||||
|
for(Stype component : allComponents){
|
||||||
|
for(Svar f : component.fields()){
|
||||||
|
VariableTree tree = f.tree();
|
||||||
|
|
||||||
|
//add initializer if it exists
|
||||||
|
if(tree.getInitializer() != null){
|
||||||
|
String init = tree.getInitializer().toString();
|
||||||
|
varInitializers.put(f, init);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(Smethod elem : component.methods()){
|
||||||
|
if(elem.is(Modifier.ABSTRACT) || elem.is(Modifier.NATIVE)) continue;
|
||||||
|
//get all statements in the method, store them
|
||||||
|
methodBlocks.put(elem, elem.tree().getBody().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//store components
|
||||||
|
for(Stype type : allComponents){
|
||||||
|
componentNames.put(type.name(), type);
|
||||||
|
}
|
||||||
|
|
||||||
|
//add component imports
|
||||||
|
for(Stype comp : allComponents){
|
||||||
|
imports.addAll(getImports(comp.e));
|
||||||
|
}
|
||||||
|
|
||||||
|
//create component interfaces
|
||||||
|
for(Stype component : allComponents){
|
||||||
|
TypeSpec.Builder inter = TypeSpec.interfaceBuilder(interfaceName(component))
|
||||||
|
.addModifiers(Modifier.PUBLIC).addAnnotation(EntityInterface.class);
|
||||||
|
|
||||||
|
//implement extra interfaces these components may have, e.g. position
|
||||||
|
for(Stype extraInterface : component.interfaces().select(i -> !isCompInterface(i))){
|
||||||
|
//javapoet completely chokes on this if I add `addSuperInterface` or create the type name with TypeName.get
|
||||||
|
inter.superinterfaces.add(tname(extraInterface.fullName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
//implement super interfaces
|
||||||
|
Array<Stype> depends = getDependencies(component);
|
||||||
|
for(Stype type : depends){
|
||||||
|
inter.addSuperinterface(ClassName.get(packageName, interfaceName(type)));
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectSet<String> signatures = new ObjectSet<>();
|
||||||
|
|
||||||
|
//add utility methods to interface
|
||||||
|
for(Smethod method : component.methods()){
|
||||||
|
//skip private methods, those are for internal use.
|
||||||
|
if(method.is(Modifier.PRIVATE)) continue;
|
||||||
|
|
||||||
|
//keep track of signatures used to prevent dupes
|
||||||
|
signatures.add(method.e.toString());
|
||||||
|
|
||||||
|
inter.addMethod(MethodSpec.methodBuilder(method.name())
|
||||||
|
.addExceptions(method.thrownt())
|
||||||
|
.addTypeVariables(method.typeVariables().map(TypeVariableName::get))
|
||||||
|
.returns(method.ret().toString().equals("void") ? TypeName.VOID : method.retn())
|
||||||
|
.addParameters(method.params().map(v -> ParameterSpec.builder(v.tname(), v.name())
|
||||||
|
.build())).addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
for(Svar field : component.fields().select(e -> !e.is(Modifier.STATIC) && !e.is(Modifier.PRIVATE) && !e.is(Modifier.TRANSIENT))){
|
||||||
|
String cname = field.name();
|
||||||
|
|
||||||
|
//getter
|
||||||
|
if(!signatures.contains(cname + "()")){
|
||||||
|
inter.addMethod(MethodSpec.methodBuilder(cname).addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC)
|
||||||
|
.addAnnotations(Array.with(field.annotations()).select(a -> a.toString().contains("Null")).map(AnnotationSpec::get))
|
||||||
|
.returns(field.tname()).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
//setter
|
||||||
|
if(!field.is(Modifier.FINAL) && !signatures.contains(cname + "(" + field.mirror().toString() + ")") &&
|
||||||
|
!field.annotations().contains(f -> f.toString().equals("@mindustry.annotations.Annotations.ReadOnly"))){
|
||||||
|
inter.addMethod(MethodSpec.methodBuilder(cname).addModifiers(Modifier.ABSTRACT, Modifier.PUBLIC)
|
||||||
|
.addParameter(ParameterSpec.builder(field.tname(), field.name())
|
||||||
|
.addAnnotations(Array.with(field.annotations())
|
||||||
|
.select(a -> a.toString().contains("Null")).map(AnnotationSpec::get)).build()).build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
write(inter);
|
||||||
|
|
||||||
|
//LOGGING
|
||||||
|
|
||||||
|
Log.debug("&gGenerating interface for " + component.name());
|
||||||
|
|
||||||
|
for(TypeName tn : inter.superinterfaces){
|
||||||
|
Log.debug("&g> &lbextends {0}", simpleName(tn.toString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
//log methods generated
|
||||||
|
for(MethodSpec spec : inter.methodSpecs){
|
||||||
|
Log.debug("&g> > &c{0} {1}({2})", simpleName(spec.returnType.toString()), spec.name, Array.with(spec.parameters).toString(", ", p -> simpleName(p.type.toString()) + " " + p.name));
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.debug("");
|
||||||
|
}
|
||||||
|
|
||||||
|
//generate special render layer interfaces
|
||||||
|
for(DrawLayer layer : DrawLayer.values()){
|
||||||
|
//create the DrawLayer interface that entities need to implement
|
||||||
|
String name = "DrawLayer" + Strings.capitalize(layer.name()) + "c";
|
||||||
|
TypeSpec.Builder inter = TypeSpec.interfaceBuilder(name)
|
||||||
|
.addSuperinterface(tname(packageName + ".Entityc"))
|
||||||
|
.addModifiers(Modifier.PUBLIC).addAnnotation(EntityInterface.class);
|
||||||
|
inter.addMethod(MethodSpec.methodBuilder("draw" + Strings.capitalize(layer.name())).addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT).build());
|
||||||
|
write(inter);
|
||||||
|
}
|
||||||
|
}else if(round == 2){ //round 2: get component classes and generate interfaces for them
|
||||||
|
|
||||||
|
//parse groups
|
||||||
|
for(Smethod group : allGroups){
|
||||||
|
GroupDef an = group.annotation(GroupDef.class);
|
||||||
|
Array<Stype> types = types(an, GroupDef::value).map(this::interfaceToComp);;
|
||||||
|
groupDefs.add(new GroupDefinition(group.name(), ClassName.bestGuess(packageName + "." + interfaceName(types.first())), types, an.spatial(), an.mapping()));
|
||||||
|
}
|
||||||
|
|
||||||
|
//add special generated groups
|
||||||
|
for(DrawLayer layer : DrawLayer.values()){
|
||||||
|
String name = "DrawLayer" + Strings.capitalize(layer.name()) + "c";
|
||||||
|
//create group definition with no components directly
|
||||||
|
GroupDefinition def = new GroupDefinition(layer.name(), ClassName.bestGuess(packageName + "." + name), Array.with(), false, false);
|
||||||
|
//add manual inclusions of entities to be added to this group
|
||||||
|
def.manualInclusions.addAll(allDefs.select(s -> allComponents(s).contains(comp -> comp.interfaces().contains(in -> in.name().equals(name)))));
|
||||||
|
groupDefs.add(def);
|
||||||
|
}
|
||||||
|
|
||||||
|
ObjectMap<String, Selement> usedNames = new ObjectMap<>();
|
||||||
|
ObjectMap<Selement, ObjectSet<String>> extraNames = new ObjectMap<>();
|
||||||
|
|
||||||
|
//look at each definition
|
||||||
|
for(Selement<?> type : allDefs){
|
||||||
|
EntityDef ann = type.annotation(EntityDef.class);
|
||||||
|
boolean isFinal = ann.isFinal();
|
||||||
|
if(type.isType() && !type.name().endsWith("Def")){
|
||||||
|
err("All entity def names must end with 'Def'", type.e);
|
||||||
|
}
|
||||||
|
String name = type.isType() ?
|
||||||
|
type.name().replace("Def", "Entity") :
|
||||||
|
createName(type);
|
||||||
|
|
||||||
|
//skip double classes
|
||||||
|
if(usedNames.containsKey(name)){
|
||||||
|
extraNames.getOr(usedNames.get(name), ObjectSet::new).add(type.name());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
usedNames.put(name, type);
|
||||||
|
extraNames.getOr(type, ObjectSet::new).add(name);
|
||||||
|
if(!type.isType()){
|
||||||
|
extraNames.getOr(type, ObjectSet::new).add(type.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeSpec.Builder builder = TypeSpec.classBuilder(name).addModifiers(Modifier.PUBLIC);
|
||||||
|
if(isFinal) builder.addModifiers(Modifier.FINAL);
|
||||||
|
|
||||||
|
Array<Stype> components = allComponents(type);
|
||||||
|
Array<GroupDefinition> groups = groupDefs.select(g -> (!g.components.isEmpty() && !g.components.contains(s -> !components.contains(s))) || g.manualInclusions.contains(type));
|
||||||
|
ObjectMap<String, Array<Smethod>> methods = new ObjectMap<>();
|
||||||
|
ObjectMap<FieldSpec, Svar> specVariables = new ObjectMap<>();
|
||||||
|
|
||||||
|
//add all components
|
||||||
|
for(Stype comp : components){
|
||||||
|
|
||||||
|
//write fields to the class; ignoring transient ones
|
||||||
|
Array<Svar> fields = comp.fields().select(f -> !f.is(Modifier.TRANSIENT));
|
||||||
|
for(Svar f : fields){
|
||||||
|
FieldSpec.Builder fbuilder = FieldSpec.builder(f.tname(), f.name());
|
||||||
|
//keep statics/finals
|
||||||
|
if(f.is(Modifier.STATIC)){
|
||||||
|
fbuilder.addModifiers(Modifier.STATIC);
|
||||||
|
if(f.is(Modifier.FINAL)) fbuilder.addModifiers(Modifier.FINAL);
|
||||||
|
}
|
||||||
|
//add initializer if it exists
|
||||||
|
if(varInitializers.containsKey(f)){
|
||||||
|
fbuilder.initializer(varInitializers.get(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!isFinal) fbuilder.addModifiers(Modifier.PROTECTED);
|
||||||
|
fbuilder.addAnnotations(f.annotations().map(AnnotationSpec::get));
|
||||||
|
builder.addField(fbuilder.build());
|
||||||
|
specVariables.put(builder.fieldSpecs.get(builder.fieldSpecs.size() - 1), f);
|
||||||
|
}
|
||||||
|
|
||||||
|
//get all utility methods from components
|
||||||
|
for(Smethod elem : comp.methods()){
|
||||||
|
methods.getOr(elem.toString(), Array::new).add(elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//override toString method
|
||||||
|
builder.addMethod(MethodSpec.methodBuilder("toString")
|
||||||
|
.addAnnotation(Override.class)
|
||||||
|
.returns(String.class)
|
||||||
|
.addModifiers(Modifier.PUBLIC)
|
||||||
|
.addStatement("return $S + $L", name + "#", "id").build());
|
||||||
|
|
||||||
|
//add all methods from components
|
||||||
|
for(ObjectMap.Entry<String, Array<Smethod>> entry : methods){
|
||||||
|
if(entry.value.contains(m -> m.has(Replace.class))){
|
||||||
|
//check replacements
|
||||||
|
if(entry.value.count(m -> m.has(Replace.class)) > 1){
|
||||||
|
err("Type " + type + " has multiple components replacing method " + entry.key + ".");
|
||||||
|
}
|
||||||
|
Smethod base = entry.value.find(m -> m.has(Replace.class));
|
||||||
|
entry.value.clear();
|
||||||
|
entry.value.add(base);
|
||||||
|
}
|
||||||
|
|
||||||
|
//check multi return
|
||||||
|
if(entry.value.count(m -> !m.isAny(Modifier.NATIVE, Modifier.ABSTRACT) && !m.isVoid()) > 1){
|
||||||
|
err("Type " + type + " has multiple components implementing non-void method " + entry.key + ".");
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.value.sort(Structs.comps(Structs.comparingFloat(m -> m.has(MethodPriority.class) ? m.annotation(MethodPriority.class).value() : 0), Structs.comparing(Selement::name)));
|
||||||
|
|
||||||
|
//representative method
|
||||||
|
Smethod first = entry.value.first();
|
||||||
|
|
||||||
|
//skip internal impl
|
||||||
|
if(first.has(InternalImpl.class)){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//build method using same params/returns
|
||||||
|
MethodSpec.Builder mbuilder = MethodSpec.methodBuilder(first.name()).addModifiers(first.is(Modifier.PRIVATE) ? Modifier.PRIVATE : Modifier.PUBLIC);
|
||||||
|
if(isFinal) mbuilder.addModifiers(Modifier.FINAL);
|
||||||
|
mbuilder.addTypeVariables(first.typeVariables().map(TypeVariableName::get));
|
||||||
|
mbuilder.returns(first.retn());
|
||||||
|
mbuilder.addExceptions(first.thrownt());
|
||||||
|
|
||||||
|
for(Svar var : first.params()){
|
||||||
|
mbuilder.addParameter(var.tname(), var.name());
|
||||||
|
}
|
||||||
|
|
||||||
|
//only write the block if it's a void method with several entries
|
||||||
|
boolean writeBlock = first.ret().toString().equals("void") && entry.value.size > 1;
|
||||||
|
|
||||||
|
if((entry.value.first().is(Modifier.ABSTRACT) || entry.value.first().is(Modifier.NATIVE)) && entry.value.size == 1 && !entry.value.first().has(InternalImpl.class)){
|
||||||
|
err(entry.value.first().up().getSimpleName() + "#" + entry.value.first() + " is an abstract method and must be implemented in some component", type);
|
||||||
|
}
|
||||||
|
|
||||||
|
//SPECIAL CASE: inject group add/remove code
|
||||||
|
if(first.name().equals("add") || first.name().equals("remove")){
|
||||||
|
for(GroupDefinition def : groups){
|
||||||
|
//remove/add from each group, assume imported
|
||||||
|
mbuilder.addStatement("Groups.$L.$L(this)", def.name, first.name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for(Smethod elem : entry.value){
|
||||||
|
if(elem.is(Modifier.ABSTRACT) || elem.is(Modifier.NATIVE) || !methodBlocks.containsKey(elem)) continue;
|
||||||
|
|
||||||
|
//get all statements in the method, copy them over
|
||||||
|
String str = methodBlocks.get(elem);
|
||||||
|
//name for code blocks in the methods
|
||||||
|
String blockName = elem.up().getSimpleName().toString().toLowerCase().replace("comp", "");
|
||||||
|
|
||||||
|
//skip empty blocks
|
||||||
|
if(str.replace("{", "").replace("\n", "").replace("}", "").replace("\t", "").replace(" ", "").isEmpty()){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//wrap scope to prevent variable leakage
|
||||||
|
if(writeBlock){
|
||||||
|
//replace return; with block break
|
||||||
|
str = str.replace("return;", "break " + blockName + ";");
|
||||||
|
mbuilder.addCode(blockName + ": {\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
//trim block
|
||||||
|
str = str.substring(2, str.length() - 1);
|
||||||
|
|
||||||
|
//make sure to remove braces here
|
||||||
|
mbuilder.addCode(str);
|
||||||
|
|
||||||
|
//end scope
|
||||||
|
if(writeBlock) mbuilder.addCode("}\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
//add free code to remove methods
|
||||||
|
if(first.name().equals("remove") && ann.pooled()){
|
||||||
|
mbuilder.addStatement("$T.free(this)", Pools.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.addMethod(mbuilder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
//add pool reset method and implment Poolable
|
||||||
|
if(ann.pooled()){
|
||||||
|
builder.addSuperinterface(Poolable.class);
|
||||||
|
//implement reset()
|
||||||
|
MethodSpec.Builder resetBuilder = MethodSpec.methodBuilder("reset").addModifiers(Modifier.PUBLIC);
|
||||||
|
for(FieldSpec spec : builder.fieldSpecs){
|
||||||
|
Svar variable = specVariables.get(spec);
|
||||||
|
if(spec.type.isPrimitive()){
|
||||||
|
//set to primitive default
|
||||||
|
resetBuilder.addStatement("$L = $L", spec.name, varInitializers.containsKey(variable) ? varInitializers.get(variable) : getDefault(spec.type.toString()));
|
||||||
|
}else{
|
||||||
|
//set to default null
|
||||||
|
if(!varInitializers.containsKey(variable)){
|
||||||
|
resetBuilder.addStatement("$L = null", spec.name);
|
||||||
|
} //else... TODO reset if poolable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.addMethod(resetBuilder.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
//make constructor private
|
||||||
|
builder.addMethod(MethodSpec.constructorBuilder().addModifiers(Modifier.PROTECTED).build());
|
||||||
|
|
||||||
|
//add create() method
|
||||||
|
builder.addMethod(MethodSpec.methodBuilder("create").addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
||||||
|
.returns(tname(packageName + "." + name))
|
||||||
|
.addStatement(ann.pooled() ? "return Pools.obtain($L.class, " +name +"::new)" : "return new $L()", name).build());
|
||||||
|
|
||||||
|
definitions.add(new EntityDefinition(packageName + "." + name, builder, type, components, groups));
|
||||||
|
}
|
||||||
|
|
||||||
|
//generate groups
|
||||||
|
TypeSpec.Builder groupsBuilder = TypeSpec.classBuilder("Groups").addModifiers(Modifier.PUBLIC);
|
||||||
|
MethodSpec.Builder groupInit = MethodSpec.methodBuilder("init").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
||||||
|
for(GroupDefinition group : groupDefs){
|
||||||
|
//Stype ctype = group.components.first();
|
||||||
|
//class names for interface/group
|
||||||
|
ClassName itype = group.baseType;
|
||||||
|
ClassName groupc = ClassName.bestGuess("mindustry.entities.EntityGroup");
|
||||||
|
|
||||||
|
//add field...
|
||||||
|
groupsBuilder.addField(ParameterizedTypeName.get(
|
||||||
|
ClassName.bestGuess("mindustry.entities.EntityGroup"), itype), group.name, Modifier.PUBLIC, Modifier.STATIC);
|
||||||
|
|
||||||
|
groupInit.addStatement("$L = new $T<>($L.class, $L, $L)", group.name, groupc, itype, group.spatial, group.mapping);
|
||||||
|
}
|
||||||
|
|
||||||
|
//write the groups
|
||||||
|
groupsBuilder.addMethod(groupInit.build());
|
||||||
|
|
||||||
|
//add method for resizing all necessary groups
|
||||||
|
MethodSpec.Builder groupResize = MethodSpec.methodBuilder("resize")
|
||||||
|
.addParameter(TypeName.FLOAT, "x").addParameter(TypeName.FLOAT, "y").addParameter(TypeName.FLOAT, "w").addParameter(TypeName.FLOAT, "h")
|
||||||
|
.addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
||||||
|
|
||||||
|
MethodSpec.Builder groupUpdate = MethodSpec.methodBuilder("update")
|
||||||
|
.addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
||||||
|
|
||||||
|
//method resize
|
||||||
|
for(GroupDefinition group : groupDefs){
|
||||||
|
if(group.spatial){
|
||||||
|
groupResize.addStatement("$L.resize(x, y, w, h)", group.name);
|
||||||
|
groupUpdate.addStatement("$L.updatePhysics()", group.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
groupUpdate.addStatement("all.update()");
|
||||||
|
|
||||||
|
for(DrawLayer layer : DrawLayer.values()){
|
||||||
|
MethodSpec.Builder groupDraw = MethodSpec.methodBuilder("draw" + Strings.capitalize(layer.name()))
|
||||||
|
.addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
||||||
|
groupDraw.addStatement("$L.draw($L::$L)", layer.name(), "DrawLayer" + Strings.capitalize(layer.name()) + "c", "draw" + Strings.capitalize(layer.name()));
|
||||||
|
groupsBuilder.addMethod(groupDraw.build());
|
||||||
|
}
|
||||||
|
|
||||||
|
groupsBuilder.addMethod(groupResize.build());
|
||||||
|
groupsBuilder.addMethod(groupUpdate.build());
|
||||||
|
|
||||||
|
write(groupsBuilder);
|
||||||
|
|
||||||
|
//load map of sync IDs
|
||||||
|
StringMap map = new StringMap();
|
||||||
|
Fi idProps = rootDirectory.child("annotations/src/main/resources/classids.properties");
|
||||||
|
if(!idProps.exists()) idProps.writeString("");
|
||||||
|
PropertiesUtils.load(map, idProps.reader());
|
||||||
|
//next ID to be used in generation
|
||||||
|
Integer max = map.values().toArray().map(Integer::parseInt).max(i -> i);
|
||||||
|
int maxID = max == null ? 0 : max + 1;
|
||||||
|
|
||||||
|
//assign IDs
|
||||||
|
definitions.sort(Structs.comparing(t -> t.base.toString()));
|
||||||
|
for(EntityDefinition def : definitions){
|
||||||
|
String name = def.base.fullName();
|
||||||
|
if(map.containsKey(name)){
|
||||||
|
def.classID = map.getInt(name);
|
||||||
|
}else{
|
||||||
|
def.classID = maxID++;
|
||||||
|
map.put(name, def.classID + "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//write assigned IDs
|
||||||
|
PropertiesUtils.store(map, idProps.writer(false), "Maps entity names to IDs. Autogenerated.");
|
||||||
|
|
||||||
|
//build mapping class for sync IDs
|
||||||
|
TypeSpec.Builder idBuilder = TypeSpec.classBuilder("EntityMapping").addModifiers(Modifier.PUBLIC)
|
||||||
|
.addField(FieldSpec.builder(TypeName.get(Prov[].class), "idMap", Modifier.PRIVATE, Modifier.STATIC).initializer("new Prov[256]").build())
|
||||||
|
.addField(FieldSpec.builder(ParameterizedTypeName.get(ClassName.get(ObjectMap.class),
|
||||||
|
tname(String.class), tname(Prov.class)),
|
||||||
|
"nameMap", Modifier.PRIVATE, Modifier.STATIC).initializer("new ObjectMap<>()").build())
|
||||||
|
.addMethod(MethodSpec.methodBuilder("map").addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
||||||
|
.returns(TypeName.get(Prov.class)).addParameter(int.class, "id").addStatement("return idMap[id]").build())
|
||||||
|
.addMethod(MethodSpec.methodBuilder("map").addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
||||||
|
.returns(TypeName.get(Prov.class)).addParameter(String.class, "name").addStatement("return nameMap.get(name)").build());
|
||||||
|
|
||||||
|
CodeBlock.Builder idStore = CodeBlock.builder();
|
||||||
|
|
||||||
|
//store the mappings
|
||||||
|
for(EntityDefinition def : definitions){
|
||||||
|
//store mapping
|
||||||
|
idStore.addStatement("idMap[$L] = $L::new", def.classID, def.name);
|
||||||
|
extraNames.get(def.base).each(extra -> {
|
||||||
|
idStore.addStatement("nameMap.put($S, $L::new)", extra, def.name);
|
||||||
|
});
|
||||||
|
|
||||||
|
//return mapping
|
||||||
|
def.builder.addMethod(MethodSpec.methodBuilder("classId").addAnnotation(Override.class)
|
||||||
|
.returns(int.class).addModifiers(Modifier.PUBLIC).addStatement("return " + def.classID).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
idBuilder.addStaticBlock(idStore.build());
|
||||||
|
|
||||||
|
write(idBuilder);
|
||||||
|
}else{
|
||||||
|
//round 3: generate actual classes and implement interfaces
|
||||||
|
|
||||||
|
//implement each definition
|
||||||
|
for(EntityDefinition def : definitions){
|
||||||
|
|
||||||
|
//get interface for each component
|
||||||
|
for(Stype comp : def.components){
|
||||||
|
|
||||||
|
//implement the interface
|
||||||
|
Stype inter = allInterfaces.find(i -> i.name().equals(interfaceName(comp)));
|
||||||
|
if(inter == null){
|
||||||
|
err("Failed to generate interface for", comp);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
def.builder.addSuperinterface(inter.tname());
|
||||||
|
|
||||||
|
//generate getter/setter for each method
|
||||||
|
for(Smethod method : inter.methods()){
|
||||||
|
String var = method.name();
|
||||||
|
FieldSpec field = Array.with(def.builder.fieldSpecs).find(f -> f.name.equals(var));
|
||||||
|
//make sure it's a real variable AND that the component doesn't already implement it with custom logic
|
||||||
|
if(field == null || comp.methods().contains(m -> m.simpleString().equals(method.simpleString()))) continue;
|
||||||
|
|
||||||
|
//getter
|
||||||
|
if(!method.isVoid()){
|
||||||
|
def.builder.addMethod(MethodSpec.overriding(method.e).addStatement("return " + var).addModifiers(Modifier.FINAL).build());
|
||||||
|
}
|
||||||
|
|
||||||
|
//setter
|
||||||
|
if(method.isVoid() && !Array.with(field.annotations).contains(f -> f.type.toString().equals("@mindustry.annotations.Annotations.ReadOnly"))){
|
||||||
|
def.builder.addMethod(MethodSpec.overriding(method.e).addModifiers(Modifier.FINAL).addStatement("this." + var + " = " + var).build());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
write(def.builder, imports.asArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
//store nulls
|
||||||
|
TypeSpec.Builder nullsBuilder = TypeSpec.classBuilder("Nulls").addModifiers(Modifier.PUBLIC).addModifiers(Modifier.FINAL);
|
||||||
|
|
||||||
|
//create mock types of all components
|
||||||
|
for(Stype interf : allInterfaces){
|
||||||
|
//indirect interfaces to implement methods for
|
||||||
|
Array<Stype> dependencies = interf.allInterfaces().and(interf);
|
||||||
|
Array<Smethod> methods = dependencies.flatMap(Stype::methods);
|
||||||
|
methods.sortComparing(Object::toString);
|
||||||
|
|
||||||
|
//used method signatures
|
||||||
|
ObjectSet<String> signatures = new ObjectSet<>();
|
||||||
|
|
||||||
|
//create null builder
|
||||||
|
String baseName = interf.name().substring(0, interf.name().length() - 1);
|
||||||
|
String className = "Null" + baseName;
|
||||||
|
TypeSpec.Builder nullBuilder = TypeSpec.classBuilder(className)
|
||||||
|
.addModifiers(Modifier.FINAL);
|
||||||
|
|
||||||
|
nullBuilder.addSuperinterface(interf.tname());
|
||||||
|
|
||||||
|
for(Smethod method : methods){
|
||||||
|
String signature = method.toString();
|
||||||
|
if(signatures.contains(signature)) continue;
|
||||||
|
|
||||||
|
Stype compType = interfaceToComp(method.type());
|
||||||
|
MethodSpec.Builder builder = MethodSpec.overriding(method.e).addModifiers(Modifier.PUBLIC, Modifier.FINAL);
|
||||||
|
|
||||||
|
if(!method.isVoid()){
|
||||||
|
if(method.name().equals("isNull")){
|
||||||
|
builder.addStatement("return true");
|
||||||
|
}else if(method.name().equals("id")){
|
||||||
|
builder.addStatement("return -1");
|
||||||
|
}else{
|
||||||
|
Svar variable = compType == null || method.params().size > 0 ? null : compType.fields().find(v -> v.name().equals(method.name()));
|
||||||
|
if(variable == null || !varInitializers.containsKey(variable)){
|
||||||
|
builder.addStatement("return " + getDefault(method.ret().toString()));
|
||||||
|
}else{
|
||||||
|
String init = varInitializers.get(variable);
|
||||||
|
builder.addStatement("return " + (init.equals("{}") ? "new " + variable.mirror().toString() : "") + init);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nullBuilder.addMethod(builder.build());
|
||||||
|
|
||||||
|
signatures.add(signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
nullsBuilder.addField(FieldSpec.builder(interf.cname(), Strings.camelize(baseName)).initializer("new " + className + "()").addModifiers(Modifier.FINAL, Modifier.STATIC, Modifier.PUBLIC).build());
|
||||||
|
|
||||||
|
write(nullBuilder);
|
||||||
|
}
|
||||||
|
|
||||||
|
write(nullsBuilder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<String> getImports(Element elem){
|
||||||
|
return Array.with(trees.getPath(elem).getCompilationUnit().getImports()).map(Object::toString);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return interface for a component type */
|
||||||
|
String interfaceName(Stype comp){
|
||||||
|
String suffix = "Comp";
|
||||||
|
if(!comp.name().endsWith(suffix)){
|
||||||
|
err("All components must have names that end with 'Comp'", comp.e);
|
||||||
|
}
|
||||||
|
return comp.name().substring(0, comp.name().length() - suffix.length()) + "c";
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return all components that a entity def has */
|
||||||
|
Array<Stype> allComponents(Selement<?> type){
|
||||||
|
if(!defComponents.containsKey(type)){
|
||||||
|
//get base defs
|
||||||
|
Array<Stype> components = types(type.annotation(EntityDef.class), EntityDef::value).map(this::interfaceToComp);
|
||||||
|
ObjectSet<Stype> out = new ObjectSet<>();
|
||||||
|
for(Stype comp : components){
|
||||||
|
//get dependencies for each def, add them
|
||||||
|
out.add(comp);
|
||||||
|
out.addAll(getDependencies(comp));
|
||||||
|
}
|
||||||
|
|
||||||
|
defComponents.put(type, out.asArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
return defComponents.get(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
Array<Stype> getDependencies(Stype component){
|
||||||
|
if(!componentDependencies.containsKey(component)){
|
||||||
|
ObjectSet<Stype> out = new ObjectSet<>();
|
||||||
|
//add base component interfaces
|
||||||
|
out.addAll(component.interfaces().select(this::isCompInterface).map(this::interfaceToComp));
|
||||||
|
//remove self interface
|
||||||
|
out.remove(component);
|
||||||
|
|
||||||
|
//out now contains the base dependencies; finish constructing the tree
|
||||||
|
ObjectSet<Stype> result = new ObjectSet<>();
|
||||||
|
for(Stype type : out){
|
||||||
|
result.add(type);
|
||||||
|
result.addAll(getDependencies(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(component.annotation(BaseComponent.class) == null){
|
||||||
|
result.addAll(baseComponents);
|
||||||
|
}
|
||||||
|
|
||||||
|
//remove it again just in case
|
||||||
|
out.remove(component);
|
||||||
|
componentDependencies.put(component, result.asArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
return componentDependencies.get(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isCompInterface(Stype type){
|
||||||
|
return interfaceToComp(type) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
Stype interfaceToComp(Stype type){
|
||||||
|
String name = type.name().substring(0, type.name().length() - 1) + "Comp";
|
||||||
|
return componentNames.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
String createName(Selement<?> elem){
|
||||||
|
Array<Stype> comps = types(elem.annotation(EntityDef.class), EntityDef::value).map(this::interfaceToComp);;
|
||||||
|
comps.sortComparing(Selement::name);
|
||||||
|
return comps.toString("", s -> s.name().replace("Comp", "")) + "Entity";
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isComponent(Stype type){
|
||||||
|
return type.annotation(Component.class) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
<T extends Annotation> Array<Stype> types(T t, Cons<T> consumer){
|
||||||
|
try{
|
||||||
|
consumer.get(t);
|
||||||
|
}catch(MirroredTypesException e){
|
||||||
|
return Array.with(e.getTypeMirrors()).map(Stype::of);
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Missing types.");
|
||||||
|
}
|
||||||
|
|
||||||
|
class GroupDefinition{
|
||||||
|
final String name;
|
||||||
|
final ClassName baseType;
|
||||||
|
final Array<Stype> components;
|
||||||
|
final boolean spatial, mapping;
|
||||||
|
final ObjectSet<Selement> manualInclusions = new ObjectSet<>();
|
||||||
|
|
||||||
|
public GroupDefinition(String name, ClassName bestType, Array<Stype> components, boolean spatial, boolean mapping){
|
||||||
|
this.baseType = bestType;
|
||||||
|
this.components = components;
|
||||||
|
this.name = name;
|
||||||
|
this.spatial = spatial;
|
||||||
|
this.mapping = mapping;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(){
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class EntityDefinition{
|
||||||
|
final Array<GroupDefinition> groups;
|
||||||
|
final Array<Stype> components;
|
||||||
|
final TypeSpec.Builder builder;
|
||||||
|
final Selement base;
|
||||||
|
final String name;
|
||||||
|
int classID;
|
||||||
|
|
||||||
|
public EntityDefinition(String name, Builder builder, Selement base, Array<Stype> components, Array<GroupDefinition> groups){
|
||||||
|
this.builder = builder;
|
||||||
|
this.name = name;
|
||||||
|
this.base = base;
|
||||||
|
this.groups = groups;
|
||||||
|
this.components = components;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(){
|
||||||
|
return "Definition{" +
|
||||||
|
"groups=" + groups +
|
||||||
|
"components=" + components +
|
||||||
|
", base=" + base +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
package mindustry.annotations.impl;
|
||||||
|
|
||||||
|
import com.squareup.javapoet.*;
|
||||||
|
import mindustry.annotations.*;
|
||||||
|
import mindustry.annotations.Annotations.*;
|
||||||
|
import mindustry.annotations.remote.*;
|
||||||
|
|
||||||
|
import javax.annotation.processing.*;
|
||||||
|
import javax.lang.model.element.Modifier;
|
||||||
|
import javax.lang.model.element.*;
|
||||||
|
import javax.lang.model.util.*;
|
||||||
|
import java.io.*;
|
||||||
|
import java.lang.reflect.*;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.zip.*;
|
||||||
|
|
||||||
|
@SupportedAnnotationTypes("mindustry.annotations.Annotations.Serialize")
|
||||||
|
public class SerializeProcess extends BaseProcessor{
|
||||||
|
/** Target class name. */
|
||||||
|
private static final String className = "Serialization";
|
||||||
|
/** Name of the base package to put all the generated classes. */
|
||||||
|
private static final String data = "eJztV0tvGzcQvvfQ3zDRIeDCKhsbQVDUsgP5UViH2IHl9BIEBsUdSYxX3C3Jlawm+XH9Z53hUg/bkuOmOfRQwfDuDme++ebBWe6PfwU3/wTwUU2VLJQdSYfDAnWQvxkschjCAUyMzWtPetJikF2nzzG8deXU5OjkW6VvMPTRGVWYP0mgC+W9HGE4Qbp1mEcg0Zo5E9C1sn2AofQYulqj92ZQoAiuxqVc2Loo2iCU03JYWy2PS+v3OndJNF7bDW1rSnk0D3hUD4foDjNRtWGQwU+HQIGZoajAWB+U1VgOYROOZx+Wgm4eMzJ7ghpoyo14Cl5FsQ2I4PsPcE2/XXpssk7kOMw6mEJe9KXxXZu70uTM4Jjz2Hl9CJ79xCc5LN25mqBoqUZPVosy9DEEY0eebnTtMKZ5iaDddgRd2oA2MGO+XqIvi2mq0xJAqQ0ARHzA8dncywWar91QaZwanMkUS7eqCqNVMKW9x+qRuO6wug3R8GGLvsEwLnMYMZBS6z3XrIgWidYhLgYfyQ50IyKrkZbGTssbjHU4Lh1KVVWbvaUNEf8fUFXYX+rt7vnJ5UXv5Lp3Et30g6NagDK55RZpHrNoyUaxwx+PyA+XLtZCaYBabSpoOzlptttX0uM8oen7aJsqnhLkkixmyPlFjlLe1kL0a/ER6YVis4UXKO2YCbYyNkCBnBQv6ToKY5Gt9kauAveZxVkjYc2fYe8DT4bSCTY2tP5iny4dxuGbnQPY4+3Cxu9N1GdODJAJcTxWTmmaOzI3IxOEl5ok3SBM1obdVxl0OvAyA9iB7Zq0uNtoM9cvy9gpvLoIiXAjW+1mnwZi7Ht5pDy+enlc8k5Fq+kqmG8EpBnQIEn8o1aFp25a/C66B60sgzB25Sx6uaxtMBM8vdVYMbBoHakc3r3rnchYvjhdiBGDM1csPD4cMr3Sc8ZSGHVtuJ+X/e8Xk2TZcKLFOtR2rVYizM8EdDqpwlxkDJZKeCcnUfYLl4+f2MFEhbG8pE0uMhqXt4Gntk/hM3Ti8k0JTSgM8zCWqg7LKPiyWcurKYr1PDaYi0x+Wi08gVaOkdYT85paa+Enbubo4NTWE3QRvtO87eg1Qy/gWeluerQd47w9BCRSsHWdfd6XebGcGptMoKw58Dhe4IwrXJYFKkspEKnYfImdRB0R7+GAasezjRIXamdhSP2M+1/rjv7cB5xI5Zya67KaN2BteNFOFvE2CtPUYObJxbN/1Sxb9hw8f/7dgbsMnKoMcAbjlIezWAcecJRxkmHcGacFTmg48xrLuYBnyuUzerl185y8UPkW6YbPn+HZWFJhtmlmMSKUY+XfUC8m8NgBG52uDeXrVFnYhv3Py3u9sb7X9wu8eMUE9x1GArUoAW0rNyVw42r3WwfwanDQHx1+9FhcMYii4y6E/6fvf3T6UiaZLA3BtXO9Zvvf0Xn2MahNEfmv1unr42peYe9Cxk+chD6gU5qcNla8/GQbSwfhJyvXvslmpC2oxOXAUIe9TgegXfgVXizXOSxN4RSlW9nEnK4eGzsGolO9pw+6xXC6d/pa0yDBzs7db6ZHGEczPgSbO+88qBpVMYjSbH/Trgn0vUM8+oE+O67otMbt8uWHvwGqGwCj";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process(RoundEnvironment env) throws Exception{
|
||||||
|
Set<TypeElement> elements = ElementFilter.typesIn(env.getElementsAnnotatedWith(Serialize.class));
|
||||||
|
|
||||||
|
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className).addModifiers(Modifier.PUBLIC);
|
||||||
|
classBuilder.addStaticBlock(CodeBlock.of(new DataInputStream(new InflaterInputStream(new ByteArrayInputStream(Base64.getDecoder().decode(data)))).readUTF()));
|
||||||
|
classBuilder.addAnnotation(AnnotationSpec.builder(SuppressWarnings.class).addMember("value", "\"unchecked\"").build());
|
||||||
|
classBuilder.addJavadoc(RemoteProcess.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("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<VariableElement> fields = ElementFilter.fieldsIn(BaseProcessor.elementu.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 = BaseProcessor.typeu.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("arc.Core.settings.getSerializer(" + typeName + ".class).write(stream, object." + name + ")");
|
||||||
|
readMethod.addStatement("object." + name + " = (" + typeName + ")arc.Core.settings.getSerializer(" + typeName + ".class).read(stream)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
readMethod.addStatement("return object");
|
||||||
|
|
||||||
|
serializer.addMethod(writeMethod.build());
|
||||||
|
serializer.addMethod(readMethod.build());
|
||||||
|
|
||||||
|
method.addStatement("arc.Core.settings.setSerializer($N, $L)", BaseProcessor.elementu.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(BaseProcessor.filer);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
package mindustry.annotations.impl;
|
||||||
|
|
||||||
|
import com.squareup.javapoet.*;
|
||||||
|
import mindustry.annotations.*;
|
||||||
|
import mindustry.annotations.Annotations.Struct;
|
||||||
|
import mindustry.annotations.Annotations.StructField;
|
||||||
|
|
||||||
|
import javax.annotation.processing.*;
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
@SupportedAnnotationTypes({
|
||||||
|
"mindustry.annotations.Annotations.Struct"
|
||||||
|
})
|
||||||
|
public class StructProcess extends BaseProcessor{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process(RoundEnvironment env) throws Exception{
|
||||||
|
Set<TypeElement> elements = ElementFilter.typesIn(env.getElementsAnnotatedWith(Struct.class));
|
||||||
|
|
||||||
|
for(TypeElement elem : elements){
|
||||||
|
|
||||||
|
if(!elem.getSimpleName().toString().endsWith("Struct")){
|
||||||
|
BaseProcessor.err("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<VariableElement> variables = ElementFilter.fieldsIn(elem.getEnclosedElements());
|
||||||
|
int structSize = variables.stream().mapToInt(StructProcess::varSize).sum();
|
||||||
|
int structTotalSize = (structSize <= 8 ? 8 : structSize <= 16 ? 16 : structSize <= 32 ? 32 : 64);
|
||||||
|
|
||||||
|
if(variables.size() == 0){
|
||||||
|
BaseProcessor.err("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("<br> ").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(BaseProcessor.filer);
|
||||||
|
}catch(IllegalArgumentException e){
|
||||||
|
e.printStackTrace();
|
||||||
|
BaseProcessor.err(e.getMessage(), elem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package mindustry.annotations;
|
package mindustry.annotations.remote;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
@@ -1,18 +1,16 @@
|
|||||||
package mindustry.annotations;
|
package mindustry.annotations.remote;
|
||||||
|
|
||||||
import mindustry.annotations.Annotations.ReadClass;
|
import mindustry.annotations.Annotations.*;
|
||||||
import mindustry.annotations.Annotations.WriteClass;
|
import mindustry.annotations.*;
|
||||||
|
|
||||||
import javax.annotation.processing.RoundEnvironment;
|
import javax.annotation.processing.*;
|
||||||
import javax.lang.model.element.Element;
|
import javax.lang.model.element.*;
|
||||||
import javax.lang.model.type.MirroredTypeException;
|
import javax.lang.model.type.*;
|
||||||
import javax.tools.Diagnostic.Kind;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class finds reader and writer methods annotated by the {@link Annotations.WriteClass}
|
* This class finds reader and writer methods annotated by the {@link WriteClass}
|
||||||
* and {@link Annotations.ReadClass} annotations.
|
* and {@link ReadClass} annotations.
|
||||||
*/
|
*/
|
||||||
public class IOFinder{
|
public class IOFinder{
|
||||||
|
|
||||||
@@ -34,21 +32,21 @@ public class IOFinder{
|
|||||||
|
|
||||||
//make sure there's only one read method
|
//make sure there's only one read method
|
||||||
if(readers.stream().filter(elem -> getValue(elem.getAnnotation(ReadClass.class)).equals(typeName)).count() > 1){
|
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);
|
BaseProcessor.err("Multiple writer methods for type '" + typeName + "'", writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
//make sure there's only one write method
|
//make sure there's only one write method
|
||||||
long count = readers.stream().filter(elem -> getValue(elem.getAnnotation(ReadClass.class)).equals(typeName)).count();
|
long count = readers.stream().filter(elem -> getValue(elem.getAnnotation(ReadClass.class)).equals(typeName)).count();
|
||||||
if(count == 0){
|
if(count == 0){
|
||||||
Utils.messager.printMessage(Kind.ERROR, "Writer method does not have an accompanying reader: ", writer);
|
BaseProcessor.err("Writer method does not have an accompanying reader: ", writer);
|
||||||
}else if(count > 1){
|
}else if(count > 1){
|
||||||
Utils.messager.printMessage(Kind.ERROR, "Writer method has multiple reader for type: ", writer);
|
BaseProcessor.err("Writer method has multiple reader for type: ", writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
Element reader = readers.stream().filter(elem -> getValue(elem.getAnnotation(ReadClass.class)).equals(typeName)).findFirst().get();
|
Element reader = readers.stream().filter(elem -> getValue(elem.getAnnotation(ReadClass.class)).equals(typeName)).findFirst().get();
|
||||||
|
|
||||||
//add to result list
|
//add to result list
|
||||||
result.put(typeName, new ClassSerializer(Utils.getMethodName(reader), Utils.getMethodName(writer), typeName));
|
result.put(typeName, new ClassSerializer(BaseProcessor.getMethodName(reader), BaseProcessor.getMethodName(writer), typeName));
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package mindustry.annotations;
|
package mindustry.annotations.remote;
|
||||||
|
|
||||||
import mindustry.annotations.Annotations.*;
|
import mindustry.annotations.Annotations.*;
|
||||||
|
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
package mindustry.annotations.remote;
|
||||||
|
|
||||||
|
import com.squareup.javapoet.*;
|
||||||
|
import mindustry.annotations.*;
|
||||||
|
import mindustry.annotations.Annotations.*;
|
||||||
|
import mindustry.annotations.remote.IOFinder.*;
|
||||||
|
|
||||||
|
import javax.annotation.processing.*;
|
||||||
|
import javax.lang.model.element.*;
|
||||||
|
import javax.tools.Diagnostic.*;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.*;
|
||||||
|
|
||||||
|
|
||||||
|
/** The annotation processor for generating remote method call code. */
|
||||||
|
@SupportedAnnotationTypes({
|
||||||
|
"mindustry.annotations.Annotations.Remote",
|
||||||
|
"mindustry.annotations.Annotations.WriteClass",
|
||||||
|
"mindustry.annotations.Annotations.ReadClass",
|
||||||
|
})
|
||||||
|
public class RemoteProcess extends BaseProcessor{
|
||||||
|
/** 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 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";
|
||||||
|
|
||||||
|
//class serializers
|
||||||
|
private HashMap<String, ClassSerializer> serializers;
|
||||||
|
//all elements with the Remote annotation
|
||||||
|
private Set<? extends Element> elements;
|
||||||
|
//map of all classes to generate by name
|
||||||
|
private HashMap<String, ClassEntry> classMap;
|
||||||
|
//list of all method entries
|
||||||
|
private ArrayList<MethodEntry> methods;
|
||||||
|
//list of all method entries
|
||||||
|
private ArrayList<ClassEntry> classes;
|
||||||
|
|
||||||
|
{
|
||||||
|
rounds = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process(RoundEnvironment roundEnv) throws Exception{
|
||||||
|
//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<Element> 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)){
|
||||||
|
err("All @Remote methods must be public and static: ", element);
|
||||||
|
}
|
||||||
|
|
||||||
|
//can't generate none methods
|
||||||
|
if(annotation.targets() == Loc.none){
|
||||||
|
err("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, BaseProcessor.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);
|
||||||
|
}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(BaseProcessor.filer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,14 @@
|
|||||||
package mindustry.annotations;
|
package mindustry.annotations.remote;
|
||||||
|
|
||||||
import com.squareup.javapoet.*;
|
import com.squareup.javapoet.*;
|
||||||
import mindustry.annotations.IOFinder.ClassSerializer;
|
import mindustry.annotations.*;
|
||||||
|
import mindustry.annotations.remote.IOFinder.*;
|
||||||
|
|
||||||
|
import javax.lang.model.element.Modifier;
|
||||||
import javax.lang.model.element.*;
|
import javax.lang.model.element.*;
|
||||||
import javax.tools.Diagnostic.Kind;
|
import java.lang.reflect.*;
|
||||||
import java.io.IOException;
|
import java.nio.*;
|
||||||
import java.lang.reflect.Constructor;
|
import java.util.*;
|
||||||
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. */
|
/** Generates code for reading remote invoke packets on the client and server. */
|
||||||
public class RemoteReadGenerator{
|
public class RemoteReadGenerator{
|
||||||
@@ -28,11 +26,10 @@ public class RemoteReadGenerator{
|
|||||||
* @param packageName Full target package name.
|
* @param packageName Full target package name.
|
||||||
* @param needsPlayer Whether this read method requires a reference to the player sender.
|
* @param needsPlayer Whether this read method requires a reference to the player sender.
|
||||||
*/
|
*/
|
||||||
public void generateFor(List<MethodEntry> entries, String className, String packageName, boolean needsPlayer)
|
public void generateFor(List<MethodEntry> entries, String className, String packageName, boolean needsPlayer) throws Exception{
|
||||||
throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException, IOException{
|
|
||||||
|
|
||||||
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className).addModifiers(Modifier.PUBLIC);
|
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className).addModifiers(Modifier.PUBLIC);
|
||||||
classBuilder.addJavadoc(RemoteMethodAnnotationProcessor.autogenWarning);
|
classBuilder.addJavadoc(RemoteProcess.autogenWarning);
|
||||||
|
|
||||||
//create main method builder
|
//create main method builder
|
||||||
MethodSpec.Builder readMethod = MethodSpec.methodBuilder("readPacket")
|
MethodSpec.Builder readMethod = MethodSpec.methodBuilder("readPacket")
|
||||||
@@ -47,7 +44,7 @@ public class RemoteReadGenerator{
|
|||||||
Constructor<TypeName> cons = TypeName.class.getDeclaredConstructor(String.class);
|
Constructor<TypeName> cons = TypeName.class.getDeclaredConstructor(String.class);
|
||||||
cons.setAccessible(true);
|
cons.setAccessible(true);
|
||||||
|
|
||||||
TypeName playerType = cons.newInstance("mindustry.entities.type.Player");
|
TypeName playerType = cons.newInstance("mindustry.gen.Playerc");
|
||||||
//add player parameter
|
//add player parameter
|
||||||
readMethod.addParameter(playerType, "player");
|
readMethod.addParameter(playerType, "player");
|
||||||
}
|
}
|
||||||
@@ -82,7 +79,7 @@ public class RemoteReadGenerator{
|
|||||||
String capName = typeName.equals("byte") ? "" : Character.toUpperCase(typeName.charAt(0)) + typeName.substring(1);
|
String capName = typeName.equals("byte") ? "" : Character.toUpperCase(typeName.charAt(0)) + typeName.substring(1);
|
||||||
|
|
||||||
//write primitives automatically
|
//write primitives automatically
|
||||||
if(Utils.isPrimitive(typeName)){
|
if(BaseProcessor.isPrimitive(typeName)){
|
||||||
if(typeName.equals("boolean")){
|
if(typeName.equals("boolean")){
|
||||||
readBlock.addStatement("boolean " + varName + " = buffer.get() == 1");
|
readBlock.addStatement("boolean " + varName + " = buffer.get() == 1");
|
||||||
}else{
|
}else{
|
||||||
@@ -90,10 +87,10 @@ public class RemoteReadGenerator{
|
|||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
//else, try and find a serializer
|
//else, try and find a serializer
|
||||||
ClassSerializer ser = serializers.get(typeName);
|
ClassSerializer ser = serializers.getOrDefault(typeName, SerializerResolver.locate(entry.element, var.asType()));
|
||||||
|
|
||||||
if(ser == null){ //make sure a serializer exists!
|
if(ser == null){ //make sure a serializer exists!
|
||||||
Utils.messager.printMessage(Kind.ERROR, "No @ReadClass method to read class type: '" + typeName + "'", var);
|
BaseProcessor.err("No @ReadClass method to read class type '" + typeName + "' in method " + entry.targetMethod, var);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,7 +115,7 @@ public class RemoteReadGenerator{
|
|||||||
if(entry.forward && entry.where.isServer && needsPlayer){
|
if(entry.forward && entry.where.isServer && needsPlayer){
|
||||||
//call forwarded method
|
//call forwarded method
|
||||||
readBlock.addStatement(packageName + "." + entry.className + "." + entry.element.getSimpleName() +
|
readBlock.addStatement(packageName + "." + entry.className + "." + entry.element.getSimpleName() +
|
||||||
"__forward(player.con" + (varResult.length() == 0 ? "" : ", ") + varResult.toString() + ")");
|
"__forward(player.con()" + (varResult.length() == 0 ? "" : ", ") + varResult.toString() + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
readBlock.nextControlFlow("catch (java.lang.Exception e)");
|
readBlock.nextControlFlow("catch (java.lang.Exception e)");
|
||||||
@@ -139,6 +136,6 @@ public class RemoteReadGenerator{
|
|||||||
|
|
||||||
//build and write resulting class
|
//build and write resulting class
|
||||||
TypeSpec spec = classBuilder.build();
|
TypeSpec spec = classBuilder.build();
|
||||||
JavaFile.builder(packageName, spec).build().writeTo(Utils.filer);
|
JavaFile.builder(packageName, spec).build().writeTo(BaseProcessor.filer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
package mindustry.annotations;
|
package mindustry.annotations.remote;
|
||||||
|
|
||||||
|
import arc.struct.*;
|
||||||
import com.squareup.javapoet.*;
|
import com.squareup.javapoet.*;
|
||||||
import mindustry.annotations.Annotations.Loc;
|
import mindustry.annotations.Annotations.*;
|
||||||
import mindustry.annotations.IOFinder.ClassSerializer;
|
import mindustry.annotations.*;
|
||||||
|
import mindustry.annotations.remote.IOFinder.*;
|
||||||
|
|
||||||
import javax.lang.model.element.*;
|
import javax.lang.model.element.*;
|
||||||
import javax.tools.Diagnostic.Kind;
|
import java.io.*;
|
||||||
import java.io.IOException;
|
import java.nio.*;
|
||||||
import java.nio.ByteBuffer;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/** Generates code for writing remote invoke packets on the client and server. */
|
/** Generates code for writing remote invoke packets on the client and server. */
|
||||||
public class RemoteWriteGenerator{
|
public class RemoteWriteGenerator{
|
||||||
@@ -26,11 +26,11 @@ public class RemoteWriteGenerator{
|
|||||||
for(ClassEntry entry : entries){
|
for(ClassEntry entry : entries){
|
||||||
//create builder
|
//create builder
|
||||||
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(entry.name).addModifiers(Modifier.PUBLIC);
|
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(entry.name).addModifiers(Modifier.PUBLIC);
|
||||||
classBuilder.addJavadoc(RemoteMethodAnnotationProcessor.autogenWarning);
|
classBuilder.addJavadoc(RemoteProcess.autogenWarning);
|
||||||
|
|
||||||
//add temporary write buffer
|
//add temporary write buffer
|
||||||
classBuilder.addField(FieldSpec.builder(ByteBuffer.class, "TEMP_BUFFER", Modifier.STATIC, Modifier.PRIVATE, Modifier.FINAL)
|
classBuilder.addField(FieldSpec.builder(ByteBuffer.class, "TEMP_BUFFER", Modifier.STATIC, Modifier.PRIVATE, Modifier.FINAL)
|
||||||
.initializer("ByteBuffer.allocate($1L)", RemoteMethodAnnotationProcessor.maxPacketSize).build());
|
.initializer("ByteBuffer.allocate($1L)", RemoteProcess.maxPacketSize).build());
|
||||||
|
|
||||||
//go through each method entry in this class
|
//go through each method entry in this class
|
||||||
for(MethodEntry methodEntry : entry.methods){
|
for(MethodEntry methodEntry : entry.methods){
|
||||||
@@ -52,7 +52,7 @@ public class RemoteWriteGenerator{
|
|||||||
|
|
||||||
//build and write resulting class
|
//build and write resulting class
|
||||||
TypeSpec spec = classBuilder.build();
|
TypeSpec spec = classBuilder.build();
|
||||||
JavaFile.builder(packageName, spec).build().writeTo(Utils.filer);
|
JavaFile.builder(packageName, spec).build().writeTo(BaseProcessor.filer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,12 +73,12 @@ public class RemoteWriteGenerator{
|
|||||||
//validate client methods to make sure
|
//validate client methods to make sure
|
||||||
if(methodEntry.where.isClient){
|
if(methodEntry.where.isClient){
|
||||||
if(elem.getParameters().isEmpty()){
|
if(elem.getParameters().isEmpty()){
|
||||||
Utils.messager.printMessage(Kind.ERROR, "Client invoke methods must have a first parameter of type Player.", elem);
|
BaseProcessor.err("Client invoke methods must have a first parameter of type Player", elem);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!elem.getParameters().get(0).asType().toString().equals("mindustry.entities.type.Player")){
|
if(!elem.getParameters().get(0).asType().toString().equals("Playerc")){
|
||||||
Utils.messager.printMessage(Kind.ERROR, "Client invoke methods should have a first parameter of type Player.", elem);
|
BaseProcessor.err("Client invoke methods should have a first parameter of type Playerc", elem);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,6 +137,8 @@ public class RemoteWriteGenerator{
|
|||||||
//rewind buffer
|
//rewind buffer
|
||||||
method.addStatement("TEMP_BUFFER.position(0)");
|
method.addStatement("TEMP_BUFFER.position(0)");
|
||||||
|
|
||||||
|
method.addTypeVariables(Array.with(elem.getTypeParameters()).map(BaseProcessor::getTVN));
|
||||||
|
|
||||||
for(int i = 0; i < elem.getParameters().size(); i++){
|
for(int i = 0; i < elem.getParameters().size(); i++){
|
||||||
//first argument is skipped as it is always the player caller
|
//first argument is skipped as it is always the player caller
|
||||||
if((!methodEntry.where.isServer/* || methodEntry.mode == Loc.both*/) && i == 0){
|
if((!methodEntry.where.isServer/* || methodEntry.mode == Loc.both*/) && i == 0){
|
||||||
@@ -145,8 +147,12 @@ public class RemoteWriteGenerator{
|
|||||||
|
|
||||||
VariableElement var = elem.getParameters().get(i);
|
VariableElement var = elem.getParameters().get(i);
|
||||||
|
|
||||||
//add parameter to method
|
try{
|
||||||
method.addParameter(TypeName.get(var.asType()), var.getSimpleName().toString());
|
//add parameter to method
|
||||||
|
method.addParameter(TypeName.get(var.asType()), var.getSimpleName().toString());
|
||||||
|
}catch(Throwable t){
|
||||||
|
throw new RuntimeException("Error parsing method " + methodEntry.targetMethod);
|
||||||
|
}
|
||||||
|
|
||||||
//name of parameter
|
//name of parameter
|
||||||
String varName = var.getSimpleName().toString();
|
String varName = var.getSimpleName().toString();
|
||||||
@@ -162,7 +168,7 @@ public class RemoteWriteGenerator{
|
|||||||
method.beginControlFlow("if(mindustry.Vars.net.server())");
|
method.beginControlFlow("if(mindustry.Vars.net.server())");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(Utils.isPrimitive(typeName)){ //check if it's a primitive, and if so write it
|
if(BaseProcessor.isPrimitive(typeName)){ //check if it's a primitive, and if so write it
|
||||||
if(typeName.equals("boolean")){ //booleans are special
|
if(typeName.equals("boolean")){ //booleans are special
|
||||||
method.addStatement("TEMP_BUFFER.put(" + varName + " ? (byte)1 : 0)");
|
method.addStatement("TEMP_BUFFER.put(" + varName + " ? (byte)1 : 0)");
|
||||||
}else{
|
}else{
|
||||||
@@ -171,10 +177,10 @@ public class RemoteWriteGenerator{
|
|||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
//else, try and find a serializer
|
//else, try and find a serializer
|
||||||
ClassSerializer ser = serializers.get(typeName);
|
ClassSerializer ser = serializers.getOrDefault(typeName, SerializerResolver.locate(elem, var.asType()));
|
||||||
|
|
||||||
if(ser == null){ //make sure a serializer exists!
|
if(ser == null){ //make sure a serializer exists!
|
||||||
Utils.messager.printMessage(Kind.ERROR, "No @WriteClass method to write class type: '" + typeName + "'", var);
|
BaseProcessor.err("No @WriteClass method to write class type: '" + typeName + "'", var);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package mindustry.annotations.remote;
|
||||||
|
|
||||||
|
import arc.struct.*;
|
||||||
|
import mindustry.annotations.remote.IOFinder.*;
|
||||||
|
|
||||||
|
import javax.lang.model.element.*;
|
||||||
|
import javax.lang.model.type.*;
|
||||||
|
|
||||||
|
public class SerializerResolver{
|
||||||
|
private static final ClassSerializer entitySerializer = new ClassSerializer("mindustry.io.TypeIO.readEntity", "mindustry.io.TypeIO.writeEntity", "Entityc");
|
||||||
|
|
||||||
|
public static ClassSerializer locate(ExecutableElement elem, TypeMirror mirror){
|
||||||
|
//generic type
|
||||||
|
if(mirror.toString().equals("T")){
|
||||||
|
TypeParameterElement param = elem.getTypeParameters().get(0);
|
||||||
|
if(Array.with(param.getBounds()).contains(SerializerResolver::isEntity)){
|
||||||
|
return entitySerializer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(isEntity(mirror)){
|
||||||
|
return entitySerializer;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isEntity(TypeMirror mirror){
|
||||||
|
return !mirror.toString().contains(".") && mirror.toString().endsWith("c");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,287 @@
|
|||||||
|
package mindustry.annotations.util;
|
||||||
|
|
||||||
|
import com.sun.tools.javac.code.*;
|
||||||
|
import com.sun.tools.javac.code.Attribute.Array;
|
||||||
|
import com.sun.tools.javac.code.Attribute.Enum;
|
||||||
|
import com.sun.tools.javac.code.Attribute.Error;
|
||||||
|
import com.sun.tools.javac.code.Attribute.Visitor;
|
||||||
|
import com.sun.tools.javac.code.Attribute.*;
|
||||||
|
import com.sun.tools.javac.code.Type;
|
||||||
|
import com.sun.tools.javac.code.Symbol.*;
|
||||||
|
import com.sun.tools.javac.code.Type.ArrayType;
|
||||||
|
import com.sun.tools.javac.util.List;
|
||||||
|
import com.sun.tools.javac.util.*;
|
||||||
|
import sun.reflect.annotation.*;
|
||||||
|
|
||||||
|
import javax.lang.model.type.*;
|
||||||
|
import java.io.*;
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
import java.lang.reflect.*;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.Map.*;
|
||||||
|
import java.lang.Class;
|
||||||
|
|
||||||
|
//replaces the standard Java AnnotationProxyMaker with one that doesn't crash
|
||||||
|
//thanks, oracle.
|
||||||
|
@SuppressWarnings({"sunapi", "unchecked"})
|
||||||
|
public class AnnotationProxyMaker{
|
||||||
|
private final Compound anno;
|
||||||
|
private final Class<? extends Annotation> annoType;
|
||||||
|
|
||||||
|
private AnnotationProxyMaker(Compound var1, Class<? extends Annotation> var2){
|
||||||
|
this.anno = var1;
|
||||||
|
this.annoType = var2;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <A extends Annotation> A generateAnnotation(Compound var0, Class<A> var1){
|
||||||
|
AnnotationProxyMaker var2 = new AnnotationProxyMaker(var0, var1);
|
||||||
|
return (A)var1.cast(var2.generateAnnotation());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Annotation generateAnnotation(){
|
||||||
|
return AnnotationParser.annotationForMap(this.annoType, this.getAllReflectedValues());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> getAllReflectedValues(){
|
||||||
|
LinkedHashMap var1 = new LinkedHashMap();
|
||||||
|
Iterator var2 = this.getAllValues().entrySet().iterator();
|
||||||
|
|
||||||
|
while(var2.hasNext()){
|
||||||
|
Entry var3 = (Entry)var2.next();
|
||||||
|
MethodSymbol var4 = (MethodSymbol)var3.getKey();
|
||||||
|
Object var5 = this.generateValue(var4, (Attribute)var3.getValue());
|
||||||
|
if(var5 != null){
|
||||||
|
var1.put(var4.name.toString(), var5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return var1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<MethodSymbol, Attribute> getAllValues(){
|
||||||
|
LinkedHashMap var1 = new LinkedHashMap();
|
||||||
|
ClassSymbol var2 = (ClassSymbol)this.anno.type.tsym;
|
||||||
|
|
||||||
|
for(com.sun.tools.javac.code.Scope.Entry var3 = var2.members().elems; var3 != null; var3 = var3.sibling){
|
||||||
|
if(var3.sym.kind == 16){
|
||||||
|
MethodSymbol var4 = (MethodSymbol)var3.sym;
|
||||||
|
Attribute var5 = var4.getDefaultValue();
|
||||||
|
if(var5 != null){
|
||||||
|
var1.put(var4, var5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Iterator var6 = this.anno.values.iterator();
|
||||||
|
|
||||||
|
while(var6.hasNext()){
|
||||||
|
Pair var7 = (Pair)var6.next();
|
||||||
|
var1.put(var7.fst, var7.snd);
|
||||||
|
}
|
||||||
|
|
||||||
|
return var1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object generateValue(MethodSymbol var1, Attribute var2){
|
||||||
|
AnnotationProxyMaker.ValueVisitor var3 = new AnnotationProxyMaker.ValueVisitor(var1);
|
||||||
|
return var3.getValue(var2);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class MirroredTypesExceptionProxy extends ExceptionProxy{
|
||||||
|
static final long serialVersionUID = 269L;
|
||||||
|
private transient List<TypeMirror> types;
|
||||||
|
private final String typeStrings;
|
||||||
|
|
||||||
|
MirroredTypesExceptionProxy(List<TypeMirror> var1){
|
||||||
|
this.types = var1;
|
||||||
|
this.typeStrings = var1.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString(){
|
||||||
|
return this.typeStrings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int hashCode(){
|
||||||
|
return (this.types != null ? this.types : this.typeStrings).hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(Object var1){
|
||||||
|
return this.types != null && var1 instanceof AnnotationProxyMaker.MirroredTypesExceptionProxy && this.types.equals(((AnnotationProxyMaker.MirroredTypesExceptionProxy)var1).types);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected RuntimeException generateException(){
|
||||||
|
return new MirroredTypesException(this.types);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException{
|
||||||
|
var1.defaultReadObject();
|
||||||
|
this.types = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class MirroredTypeExceptionProxy extends ExceptionProxy{
|
||||||
|
static final long serialVersionUID = 269L;
|
||||||
|
private transient TypeMirror type;
|
||||||
|
private final String typeString;
|
||||||
|
|
||||||
|
MirroredTypeExceptionProxy(TypeMirror var1){
|
||||||
|
this.type = var1;
|
||||||
|
this.typeString = var1.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString(){
|
||||||
|
return this.typeString;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int hashCode(){
|
||||||
|
return (this.type != null ? this.type : this.typeString).hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(Object var1){
|
||||||
|
return this.type != null && var1 instanceof AnnotationProxyMaker.MirroredTypeExceptionProxy && this.type.equals(((AnnotationProxyMaker.MirroredTypeExceptionProxy)var1).type);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected RuntimeException generateException(){
|
||||||
|
return new MirroredTypeException(this.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException{
|
||||||
|
var1.defaultReadObject();
|
||||||
|
this.type = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ValueVisitor implements Visitor{
|
||||||
|
private MethodSymbol meth;
|
||||||
|
private Class<?> returnClass;
|
||||||
|
private Object value;
|
||||||
|
|
||||||
|
ValueVisitor(MethodSymbol var2){
|
||||||
|
this.meth = var2;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object getValue(Attribute var1){
|
||||||
|
Method var2;
|
||||||
|
try{
|
||||||
|
var2 = AnnotationProxyMaker.this.annoType.getMethod(this.meth.name.toString());
|
||||||
|
}catch(NoSuchMethodException var4){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.returnClass = var2.getReturnType();
|
||||||
|
var1.accept(this);
|
||||||
|
if(!(this.value instanceof ExceptionProxy) && !AnnotationType.invocationHandlerReturnType(this.returnClass).isInstance(this.value)){
|
||||||
|
this.typeMismatch(var2, var1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void visitConstant(Constant var1){
|
||||||
|
this.value = var1.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void visitClass(com.sun.tools.javac.code.Attribute.Class var1){
|
||||||
|
this.value = new AnnotationProxyMaker.MirroredTypeExceptionProxy(var1.classType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void visitArray(Array var1){
|
||||||
|
Name var2 = ((ArrayType)var1.type).elemtype.tsym.getQualifiedName();
|
||||||
|
int var6;
|
||||||
|
if(var2.equals(var2.table.names.java_lang_Class)){
|
||||||
|
ListBuffer var14 = new ListBuffer();
|
||||||
|
Attribute[] var15 = var1.values;
|
||||||
|
int var16 = var15.length;
|
||||||
|
|
||||||
|
for(var6 = 0; var6 < var16; ++var6){
|
||||||
|
Attribute var7 = var15[var6];
|
||||||
|
Type var8 = var7 instanceof UnresolvedClass ? ((UnresolvedClass)var7).classType : ((com.sun.tools.javac.code.Attribute.Class)var7).classType;
|
||||||
|
var14.append(var8);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.value = new AnnotationProxyMaker.MirroredTypesExceptionProxy(var14.toList());
|
||||||
|
}else{
|
||||||
|
int var3 = var1.values.length;
|
||||||
|
Class var4 = this.returnClass;
|
||||||
|
this.returnClass = this.returnClass.getComponentType();
|
||||||
|
|
||||||
|
try{
|
||||||
|
Object var5 = java.lang.reflect.Array.newInstance(this.returnClass, var3);
|
||||||
|
|
||||||
|
for(var6 = 0; var6 < var3; ++var6){
|
||||||
|
var1.values[var6].accept(this);
|
||||||
|
if(this.value == null || this.value instanceof ExceptionProxy){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try{
|
||||||
|
java.lang.reflect.Array.set(var5, var6, this.value);
|
||||||
|
}catch(IllegalArgumentException var12){
|
||||||
|
this.value = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.value = var5;
|
||||||
|
}finally{
|
||||||
|
this.returnClass = var4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void visitEnum(Enum var1){
|
||||||
|
if(this.returnClass.isEnum()){
|
||||||
|
String var2 = var1.value.toString();
|
||||||
|
|
||||||
|
try{
|
||||||
|
this.value = java.lang.Enum.valueOf((Class)this.returnClass, var2);
|
||||||
|
}catch(IllegalArgumentException var4){
|
||||||
|
this.value = new EnumConstantNotPresentExceptionProxy((Class)this.returnClass, var2);
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
this.value = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void visitCompound(Compound var1){
|
||||||
|
try{
|
||||||
|
Class var2 = this.returnClass.asSubclass(Annotation.class);
|
||||||
|
this.value = AnnotationProxyMaker.generateAnnotation(var1, var2);
|
||||||
|
}catch(ClassCastException var3){
|
||||||
|
this.value = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void visitError(Error var1){
|
||||||
|
if(var1 instanceof UnresolvedClass){
|
||||||
|
this.value = new AnnotationProxyMaker.MirroredTypeExceptionProxy(((UnresolvedClass)var1).classType);
|
||||||
|
}else{
|
||||||
|
this.value = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void typeMismatch(Method var1, final Attribute var2){
|
||||||
|
class AnnotationTypeMismatchExceptionProxy extends ExceptionProxy{
|
||||||
|
static final long serialVersionUID = 269L;
|
||||||
|
final transient Method method;
|
||||||
|
|
||||||
|
AnnotationTypeMismatchExceptionProxy(Method var2x){
|
||||||
|
this.method = var2x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString(){
|
||||||
|
return "<error>";
|
||||||
|
}
|
||||||
|
|
||||||
|
protected RuntimeException generateException(){
|
||||||
|
return new AnnotationTypeMismatchException(this.method, var2.type.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.value = new AnnotationTypeMismatchExceptionProxy(var1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
package mindustry.annotations.util;
|
||||||
|
|
||||||
|
import arc.struct.Array;
|
||||||
|
import com.squareup.javapoet.*;
|
||||||
|
import com.sun.tools.javac.code.Attribute.*;
|
||||||
|
import mindustry.annotations.*;
|
||||||
|
|
||||||
|
import javax.lang.model.element.*;
|
||||||
|
import javax.lang.model.type.*;
|
||||||
|
import java.lang.Class;
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
import java.lang.reflect.*;
|
||||||
|
|
||||||
|
public class Selement<T extends Element>{
|
||||||
|
public final T e;
|
||||||
|
|
||||||
|
public Selement(T e){
|
||||||
|
this.e = e;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Array<Selement<?>> enclosed(){
|
||||||
|
return Array.with(e.getEnclosedElements()).map(Selement::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String fullName(){
|
||||||
|
return e.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stype asType(){
|
||||||
|
return new Stype((TypeElement)e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Svar asVar(){
|
||||||
|
return new Svar((VariableElement)e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Smethod asMethod(){
|
||||||
|
return new Smethod((ExecutableElement)e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isVar(){
|
||||||
|
return e instanceof VariableElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isType(){
|
||||||
|
return e instanceof TypeElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isMethod(){
|
||||||
|
return e instanceof ExecutableElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Array<? extends AnnotationMirror> annotations(){
|
||||||
|
return Array.with(e.getAnnotationMirrors());
|
||||||
|
}
|
||||||
|
|
||||||
|
public <A extends Annotation> A annotation(Class<A> annotation){
|
||||||
|
try{
|
||||||
|
Method m = com.sun.tools.javac.code.AnnoConstruct.class.getDeclaredMethod("getAttribute", Class.class);
|
||||||
|
m.setAccessible(true);
|
||||||
|
Compound compound = (Compound)m.invoke(e, annotation);
|
||||||
|
return compound == null ? null : AnnotationProxyMaker.generateAnnotation(compound, annotation);
|
||||||
|
}catch(Exception e){
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public <A extends Annotation> boolean has(Class<A> annotation){
|
||||||
|
return annotation(annotation) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Element up(){
|
||||||
|
return e.getEnclosingElement();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TypeMirror mirror(){
|
||||||
|
return e.asType();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TypeName tname(){
|
||||||
|
return TypeName.get(mirror());
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClassName cname(){
|
||||||
|
return ClassName.get((TypeElement)BaseProcessor.typeu.asElement(mirror()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String name(){
|
||||||
|
return e.getSimpleName().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString(){
|
||||||
|
return e.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode(){
|
||||||
|
return e.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o){
|
||||||
|
return o != null && o.getClass() == getClass() && e == ((Selement)o).e;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package mindustry.annotations.util;
|
||||||
|
|
||||||
|
import arc.struct.*;
|
||||||
|
import com.squareup.javapoet.*;
|
||||||
|
import com.sun.source.tree.*;
|
||||||
|
import mindustry.annotations.*;
|
||||||
|
|
||||||
|
import javax.lang.model.element.*;
|
||||||
|
import javax.lang.model.type.*;
|
||||||
|
|
||||||
|
public class Smethod extends Selement<ExecutableElement>{
|
||||||
|
|
||||||
|
public Smethod(ExecutableElement executableElement){
|
||||||
|
super(executableElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAny(Modifier... mod){
|
||||||
|
for(Modifier m : mod){
|
||||||
|
if(is(m)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean is(Modifier mod){
|
||||||
|
return e.getModifiers().contains(mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stype type(){
|
||||||
|
return new Stype((TypeElement)up());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Array<TypeMirror> thrown(){
|
||||||
|
return Array.with(e.getThrownTypes()).as(TypeMirror.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Array<TypeName> thrownt(){
|
||||||
|
return Array.with(e.getThrownTypes()).map(TypeName::get);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Array<TypeParameterElement> typeVariables(){
|
||||||
|
return Array.with(e.getTypeParameters()).as(TypeParameterElement.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Array<Svar> params(){
|
||||||
|
return Array.with(e.getParameters()).map(Svar::new);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isVoid(){
|
||||||
|
return ret().toString().equals("void");
|
||||||
|
}
|
||||||
|
|
||||||
|
public TypeMirror ret(){
|
||||||
|
return e.getReturnType();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TypeName retn(){
|
||||||
|
return TypeName.get(ret());
|
||||||
|
}
|
||||||
|
|
||||||
|
public MethodTree tree(){
|
||||||
|
return BaseProcessor.trees.getTree(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String simpleString(){
|
||||||
|
return name() + "(" + params().toString(", ", p -> BaseProcessor.simpleName(p.mirror().toString())) + ")";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package mindustry.annotations.util;
|
||||||
|
|
||||||
|
import arc.struct.*;
|
||||||
|
import mindustry.annotations.*;
|
||||||
|
|
||||||
|
import javax.lang.model.element.*;
|
||||||
|
import javax.lang.model.type.*;
|
||||||
|
|
||||||
|
public class Stype extends Selement<TypeElement>{
|
||||||
|
|
||||||
|
public Stype(TypeElement typeElement){
|
||||||
|
super(typeElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Stype of(TypeMirror mirror){
|
||||||
|
return new Stype((TypeElement)BaseProcessor.typeu.asElement(mirror));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String fullName(){
|
||||||
|
return mirror().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Array<Stype> interfaces(){
|
||||||
|
return Array.with(e.getInterfaces()).map(Stype::of);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Array<Stype> allInterfaces(){
|
||||||
|
return interfaces().flatMap(s -> s.allInterfaces().and(s)).distinct();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Array<Stype> superclasses(){
|
||||||
|
return Array.with(BaseProcessor.typeu.directSupertypes(mirror())).map(Stype::of);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Array<Stype> allSuperclasses(){
|
||||||
|
return superclasses().flatMap(s -> s.allSuperclasses().and(s)).distinct();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Stype superclass(){
|
||||||
|
return new Stype((TypeElement)BaseProcessor.typeu.asElement(BaseProcessor.typeu.directSupertypes(mirror()).get(0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Array<Svar> fields(){
|
||||||
|
return Array.with(e.getEnclosedElements()).select(e -> e instanceof VariableElement).map(e -> new Svar((VariableElement)e));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Array<Smethod> methods(){
|
||||||
|
return Array.with(e.getEnclosedElements()).select(e -> e instanceof ExecutableElement
|
||||||
|
&& !e.getSimpleName().toString().contains("<")).map(e -> new Smethod((ExecutableElement)e));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Array<Smethod> constructors(){
|
||||||
|
return Array.with(e.getEnclosedElements()).select(e -> e instanceof ExecutableElement
|
||||||
|
&& e.getSimpleName().toString().contains("<")).map(e -> new Smethod((ExecutableElement)e));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TypeMirror mirror(){
|
||||||
|
return e.asType();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package mindustry.annotations.util;
|
||||||
|
|
||||||
|
import com.sun.source.tree.*;
|
||||||
|
import mindustry.annotations.*;
|
||||||
|
|
||||||
|
import javax.lang.model.element.*;
|
||||||
|
|
||||||
|
public class Svar extends Selement<VariableElement>{
|
||||||
|
|
||||||
|
public Svar(VariableElement e){
|
||||||
|
super(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean is(Modifier mod){
|
||||||
|
return e.getModifiers().contains(mod);
|
||||||
|
}
|
||||||
|
|
||||||
|
public VariableTree tree(){
|
||||||
|
return (VariableTree)BaseProcessor.trees.getTree(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
mindustry.annotations.RemoteMethodAnnotationProcessor
|
|
||||||
mindustry.annotations.SerializeAnnotationProcessor
|
|
||||||
mindustry.annotations.StructAnnotationProcessor
|
|
||||||
mindustry.annotations.CallSuperAnnotationProcessor
|
|
||||||
mindustry.annotations.AssetsAnnotationProcessor
|
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#Maps entity names to IDs. Autogenerated.
|
||||||
|
|
||||||
|
dagger=7
|
||||||
|
mindustry.entities.AllEntities.GenericBuilderDef=6
|
||||||
|
mindustry.entities.AllEntities.BulletDef=0
|
||||||
|
mindustry.entities.AllEntities.PlayerDef=4
|
||||||
|
mindustry.entities.AllEntities.GroundEffectDef=9
|
||||||
|
mindustry.entities.AllEntities.FireDef=11
|
||||||
|
mindustry.entities.AllEntities.EffectDef=2
|
||||||
|
mindustry.entities.AllEntities.GenericUnitDef=5
|
||||||
|
mindustry.entities.AllEntities.TileDef=3
|
||||||
|
vanguard=10
|
||||||
|
dagger2=8
|
||||||
|
mindustry.entities.AllEntities.DecalDef=1
|
||||||
@@ -1,4 +1,12 @@
|
|||||||
buildscript{
|
buildscript{
|
||||||
|
ext{
|
||||||
|
getArcHash = {
|
||||||
|
return new Properties().with{ p -> p.load(file('gradle.properties').newReader()); return p }["archash"]
|
||||||
|
}
|
||||||
|
|
||||||
|
arcHash = getArcHash()
|
||||||
|
}
|
||||||
|
|
||||||
repositories{
|
repositories{
|
||||||
mavenLocal()
|
mavenLocal()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
@@ -10,8 +18,9 @@ buildscript{
|
|||||||
|
|
||||||
dependencies{
|
dependencies{
|
||||||
classpath 'com.mobidevelop.robovm:robovm-gradle-plugin:2.3.8'
|
classpath 'com.mobidevelop.robovm:robovm-gradle-plugin:2.3.8'
|
||||||
classpath "com.badlogicgames.gdx:gdx-tools:1.9.10"
|
|
||||||
classpath "com.github.anuken:packr:-SNAPSHOT"
|
classpath "com.github.anuken:packr:-SNAPSHOT"
|
||||||
|
classpath "com.github.Anuken.Arc:packer:$arcHash"
|
||||||
|
classpath "com.github.Anuken.Arc:arc-core:$arcHash"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,7 +37,6 @@ allprojects{
|
|||||||
gdxVersion = '1.9.10'
|
gdxVersion = '1.9.10'
|
||||||
roboVMVersion = '2.3.8'
|
roboVMVersion = '2.3.8'
|
||||||
steamworksVersion = '891ed912791e01fe9ee6237a6497e5212b85c256'
|
steamworksVersion = '891ed912791e01fe9ee6237a6497e5212b85c256'
|
||||||
arcHash = null
|
|
||||||
|
|
||||||
loadVersionProps = {
|
loadVersionProps = {
|
||||||
return new Properties().with{p -> p.load(file('../core/assets/version.properties').newReader()); return p }
|
return new Properties().with{p -> p.load(file('../core/assets/version.properties').newReader()); return p }
|
||||||
@@ -42,10 +50,6 @@ allprojects{
|
|||||||
return !project.hasProperty("release") && new File(projectDir.parent, '../Arc').exists()
|
return !project.hasProperty("release") && new File(projectDir.parent, '../Arc').exists()
|
||||||
}
|
}
|
||||||
|
|
||||||
getArcHash = {
|
|
||||||
return new Properties().with{ p -> p.load(file('gradle.properties').newReader()); return p }["archash"]
|
|
||||||
}
|
|
||||||
|
|
||||||
arcModule = { String name ->
|
arcModule = { String name ->
|
||||||
if(localArc()){
|
if(localArc()){
|
||||||
return project(":Arc:$name")
|
return project(":Arc:$name")
|
||||||
@@ -128,6 +132,20 @@ allprojects{
|
|||||||
props.store(pfile.newWriter(), "Autogenerated file. Do not modify.")
|
props.store(pfile.newWriter(), "Autogenerated file. Do not modify.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
writeProcessors = {
|
||||||
|
new File(rootDir, "annotations/src/main/resources/META-INF/services/").mkdirs()
|
||||||
|
def processorFile = new File(rootDir, "annotations/src/main/resources/META-INF/services/javax.annotation.processing.Processor")
|
||||||
|
def text = new StringBuilder()
|
||||||
|
def files = new File(rootDir, "annotations/src/main/java")
|
||||||
|
files.eachFileRecurse(groovy.io.FileType.FILES){ file ->
|
||||||
|
if(file.name.endsWith(".java") && (file.text.contains(" extends BaseProcessor") || (file.text.contains(" extends AbstractProcessor") && !file.text.contains("abstract class")))){
|
||||||
|
text.append(file.path.substring(files.path.length() + 1)).append("\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
processorFile.text = text.toString().replace(".java", "").replace("/", ".").replace("\\", ".")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
repositories{
|
repositories{
|
||||||
@@ -145,6 +163,9 @@ allprojects{
|
|||||||
project(":desktop"){
|
project(":desktop"){
|
||||||
apply plugin: "java"
|
apply plugin: "java"
|
||||||
|
|
||||||
|
compileJava.options.fork = true
|
||||||
|
compileJava.options.compilerArgs += ["-XDignore.symbol.file"]
|
||||||
|
|
||||||
dependencies{
|
dependencies{
|
||||||
compile project(":core")
|
compile project(":core")
|
||||||
|
|
||||||
@@ -201,6 +222,7 @@ project(":core"){
|
|||||||
outputs.upToDateWhen{ false }
|
outputs.upToDateWhen{ false }
|
||||||
generateLocales()
|
generateLocales()
|
||||||
writeVersion()
|
writeVersion()
|
||||||
|
writeProcessors()
|
||||||
}
|
}
|
||||||
|
|
||||||
task copyChangelog{
|
task copyChangelog{
|
||||||
@@ -225,31 +247,6 @@ project(":core"){
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies{
|
dependencies{
|
||||||
if(System.properties["user.name"] == "anuke"){
|
|
||||||
task cleanGen{
|
|
||||||
doFirst{
|
|
||||||
delete{
|
|
||||||
delete "../core/src/mindustry/gen/"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
task copyGen{
|
|
||||||
doLast{
|
|
||||||
copy{
|
|
||||||
from("../core/build/generated/sources/annotationProcessor/java/main/mindustry/gen"){
|
|
||||||
include "**/*.java"
|
|
||||||
}
|
|
||||||
|
|
||||||
into "../core/src/mindustry/gen"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
compileJava.dependsOn(cleanGen)
|
|
||||||
compileJava.finalizedBy(copyGen)
|
|
||||||
}
|
|
||||||
|
|
||||||
compileJava.dependsOn(preGen)
|
compileJava.dependsOn(preGen)
|
||||||
|
|
||||||
compile "org.lz4:lz4-java:1.4.1"
|
compile "org.lz4:lz4-java:1.4.1"
|
||||||
@@ -299,7 +296,7 @@ project(":tools"){
|
|||||||
|
|
||||||
compile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"
|
compile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop"
|
||||||
compile "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-desktop"
|
compile "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-desktop"
|
||||||
compile "org.reflections:reflections:0.9.11"
|
compile "org.reflections:reflections:0.9.12"
|
||||||
|
|
||||||
compile arcModule("backends:backend-sdl")
|
compile arcModule("backends:backend-sdl")
|
||||||
}
|
}
|
||||||
@@ -309,8 +306,8 @@ project(":annotations"){
|
|||||||
apply plugin: "java"
|
apply plugin: "java"
|
||||||
|
|
||||||
dependencies{
|
dependencies{
|
||||||
compile 'com.squareup:javapoet:1.11.0'
|
compile 'com.squareup:javapoet:1.12.1'
|
||||||
compile "com.github.Anuken.Arc:arc-core:b77767039334b2658f44d69e72d1ef6bc84f95b0"
|
compile "com.github.Anuken.Arc:arc-core:$arcHash"
|
||||||
compile files("${System.getProperty('java.home')}/../lib/tools.jar")
|
compile files("${System.getProperty('java.home')}/../lib/tools.jar")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -164,12 +164,6 @@
|
|||||||
"code": 59414,
|
"code": 59414,
|
||||||
"src": "typicons"
|
"src": "typicons"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"uid": "b90868gfogj970a1g0dnot6hm5r4uj55",
|
|
||||||
"css": "chat",
|
|
||||||
"code": 59415,
|
|
||||||
"src": "typicons"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"uid": "890649841b2c37d56ff90065872fecf3",
|
"uid": "890649841b2c37d56ff90065872fecf3",
|
||||||
"css": "chart-bar",
|
"css": "chart-bar",
|
||||||
@@ -182,12 +176,6 @@
|
|||||||
"code": 59418,
|
"code": 59418,
|
||||||
"src": "typicons"
|
"src": "typicons"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"uid": "bczb7qup4axmc490xmuuv8qdhcnbgeyf",
|
|
||||||
"css": "user",
|
|
||||||
"code": 59420,
|
|
||||||
"src": "typicons"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"uid": "4109c474ff99cad28fd5a2c38af2ec6f",
|
"uid": "4109c474ff99cad28fd5a2c38af2ec6f",
|
||||||
"css": "filter",
|
"css": "filter",
|
||||||
@@ -296,12 +284,6 @@
|
|||||||
"code": 59439,
|
"code": 59439,
|
||||||
"src": "typicons"
|
"src": "typicons"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"uid": "e45e9f27ce40ba9837cc984076d98067",
|
|
||||||
"css": "zoom",
|
|
||||||
"code": 59441,
|
|
||||||
"src": "elusive"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"uid": "0e26e70b4aa537cc206f41b21dcf2fcc",
|
"uid": "0e26e70b4aa537cc206f41b21dcf2fcc",
|
||||||
"css": "lock",
|
"css": "lock",
|
||||||
@@ -819,6 +801,124 @@
|
|||||||
"search": [
|
"search": [
|
||||||
"pencil"
|
"pencil"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "346f9aef245e0c2ded44e31ad7c66acb",
|
||||||
|
"css": "mode-pvp",
|
||||||
|
"code": 59477,
|
||||||
|
"src": "custom_icons",
|
||||||
|
"selected": true,
|
||||||
|
"svg": {
|
||||||
|
"path": "M305.8 0C296.1 0 286.4 1.6 276.7 4.9 257.3 11.3 244.3 24.3 237.9 43.7 231.4 63.1 231.4 82.5 237.9 101.9 244.3 121.4 254 137.5 267 150.5L305.8 189.3 344.7 228.2 383.5 267C396.4 279.9 409.4 286.4 422.3 286.4 435.3 286.4 448.2 279.9 461.2 267L500 228.2C512.9 215.2 519.4 202.3 519.4 189.3 519.4 176.4 512.9 163.4 500 150.5L461.2 111.7 422.3 72.8 383.5 34C370.6 21 354.4 11.3 335 4.9 325.2 1.6 315.5 0 305.8 0ZM927.2 0C917.5 0 907.8 1.6 898.1 4.9 878.6 11.3 862.5 21 849.5 34L810.7 72.8 771.8 111.7 733 150.5 694.2 189.3 655.3 228.2 616.5 267 577.7 305.8 538.8 344.7 500 383.5 461.2 422.3 422.3 461.2 383.5 500C370.6 512.9 354.4 522.7 335 529.1 315.5 535.6 296.1 535.6 276.7 529.1 257.3 522.7 241.1 512.9 228.2 500 215.2 487.1 199 477.3 179.6 470.9 160.2 464.4 140.8 464.4 121.4 470.9 101.9 477.3 89 490.3 82.5 509.7 76.1 529.1 76.1 548.5 82.5 568 89 587.4 98.7 603.6 111.7 616.5 124.6 629.5 134.3 645.6 140.8 665 147.2 684.5 147.2 703.9 140.8 723.3 134.3 742.7 124.6 758.9 111.7 771.8L72.8 810.7 34 849.5C21 862.5 11.3 878.6 4.9 898.1-1.6 917.5-1.6 936.9 4.9 956.3 11.3 975.7 24.3 988.7 43.7 995.1 63.1 1001.6 82.5 1001.6 101.9 995.1 121.4 988.7 137.5 979 150.5 966L189.3 927.2 228.2 888.3C241.1 875.4 257.3 865.7 276.7 859.2 296.1 852.8 315.5 852.8 335 859.2 354.4 865.7 370.6 875.4 383.5 888.3 396.4 901.3 412.6 911 432 917.5 451.5 923.9 470.9 923.9 490.3 917.5 509.7 911 522.7 898.1 529.1 878.6 535.6 859.2 535.6 839.8 529.1 820.4 522.7 801 512.9 784.8 500 771.8 487.1 758.9 477.3 742.7 470.9 723.3 464.4 703.9 464.4 684.5 470.9 665 477.3 645.6 487.1 629.5 500 616.5L538.8 577.7 577.7 538.8 616.5 500 655.3 461.2 694.2 422.3 733 383.5 771.8 344.7 810.7 305.8 849.5 267 888.3 228.2 927.2 189.3 966 150.5C979 137.5 988.7 121.4 995.1 101.9 1001.6 82.5 1001.6 63.1 995.1 43.7 988.7 24.3 975.7 11.3 956.3 4.9 946.6 1.6 936.9 0 927.2 0ZM1082.5 466C1072.8 466 1063.1 467.6 1053.4 470.9 1034 477.3 1017.8 487.1 1004.9 500 991.9 512.9 975.7 522.7 956.3 529.1 936.9 535.6 917.5 535.6 898.1 529.1 878.6 522.7 862.5 512.9 849.5 500 836.6 487.1 823.6 480.6 810.7 480.6 797.7 480.6 784.8 487.1 771.8 500L733 538.8C720.1 551.8 713.6 564.7 713.6 577.7 713.6 590.6 720.1 603.6 733 616.5 746 629.5 755.7 645.6 762.1 665 768.6 684.5 768.6 703.9 762.1 723.3 755.7 742.7 746 758.9 733 771.8 720.1 784.8 710.4 801 703.9 820.4 697.4 839.8 697.4 859.2 703.9 878.6 710.4 898.1 723.3 911 742.7 917.5 762.1 923.9 781.6 923.9 801 917.5 820.4 911 836.6 901.3 849.5 888.3 862.5 875.4 878.6 865.7 898.1 859.2 917.5 852.8 936.9 852.8 956.3 859.2 975.7 865.7 991.9 875.4 1004.9 888.3L1043.7 927.2 1082.5 966C1095.5 979 1111.7 988.7 1131.1 995.1 1150.5 1001.6 1169.9 1001.6 1189.3 995.1 1208.7 988.7 1221.7 975.7 1228.2 956.3 1234.6 936.9 1234.6 917.5 1228.2 898.1 1221.7 878.6 1212 862.5 1199 849.5L1160.2 810.7 1121.4 771.8C1108.4 758.9 1098.7 742.7 1092.2 723.3 1085.8 703.9 1085.8 684.5 1092.2 665 1098.7 645.6 1108.4 629.5 1121.4 616.5 1134.3 603.6 1144 587.4 1150.5 568 1157 548.5 1157 529.1 1150.5 509.7 1144 490.3 1131.1 477.3 1111.7 470.9 1101.9 467.6 1092.2 466 1082.5 466Z",
|
||||||
|
"width": 1233
|
||||||
|
},
|
||||||
|
"search": [
|
||||||
|
"mode-pvp"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "8d74cd519427de451b48df6554aaf593",
|
||||||
|
"css": "mode-attack",
|
||||||
|
"code": 59478,
|
||||||
|
"src": "custom_icons",
|
||||||
|
"selected": true,
|
||||||
|
"svg": {
|
||||||
|
"path": "M849.5 34Q868.9 14.6 898.1 4.9 927.2-4.9 956.3 4.9 985.4 14.6 995.1 43.7 1004.9 72.8 995.1 101.9 985.4 131.1 966 150.5 946.6 169.9 927.2 189.3 907.8 208.7 888.3 228.2 868.9 247.6 849.5 267 830.1 286.4 810.7 305.8 791.3 325.2 771.8 344.7 752.4 364.1 733 383.5 713.6 402.9 694.2 422.3 674.8 441.7 655.3 461.2 635.9 480.6 616.5 500 597.1 519.4 577.7 538.8 558.3 558.3 538.8 577.7 519.4 597.1 500 616.5 480.6 635.9 470.9 665 461.2 694.2 470.9 723.3 480.6 752.4 500 771.8 519.4 791.3 529.1 820.4 538.8 849.5 529.1 878.6 519.4 907.8 490.3 917.5 461.2 927.2 432 917.5 402.9 907.8 383.5 888.3 364.1 868.9 335 859.2 305.8 849.5 276.7 859.2 247.6 868.9 228.2 888.3 208.7 907.8 189.3 927.2 169.9 946.6 150.5 966 131.1 985.4 101.9 995.1 72.8 1004.9 43.7 995.1 14.6 985.4 4.9 956.3-4.9 927.2 4.9 898.1 14.6 868.9 34 849.5 53.4 830.1 72.8 810.7 92.2 791.3 111.7 771.8 131.1 752.4 140.8 723.3 150.5 694.2 140.8 665 131.1 635.9 111.7 616.5 92.2 597.1 82.5 568 72.8 538.8 82.5 509.7 92.2 480.6 121.4 470.9 150.5 461.2 179.6 470.9 208.7 480.6 228.2 500 247.6 519.4 276.7 529.1 305.8 538.8 335 529.1 364.1 519.4 383.5 500 402.9 480.6 422.3 461.2 441.7 441.7 461.2 422.3 480.6 402.9 500 383.5 519.4 364.1 538.8 344.7 558.3 325.2 577.7 305.8 597.1 286.4 616.5 267 635.9 247.6 655.3 228.2 674.8 208.7 694.2 189.3 713.6 169.9 733 150.5 752.4 131.1 771.8 111.7 791.3 92.2 810.7 72.8 830.1 53.4 849.5 34",
|
||||||
|
"width": 1000
|
||||||
|
},
|
||||||
|
"search": [
|
||||||
|
"mode-attack"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "09e5948ca30589e5baa8d27e1c509588",
|
||||||
|
"css": "mode-survival",
|
||||||
|
"code": 59479,
|
||||||
|
"src": "custom_icons",
|
||||||
|
"selected": true,
|
||||||
|
"svg": {
|
||||||
|
"path": "M9.7 43.5Q19.3 14.5 48.3 4.8 77.3-4.8 106.3 4.8 135.3 14.5 154.6 33.8 173.9 53.1 193.2 72.5 212.6 91.8 231.9 111.1 251.2 130.4 280.2 140.1 309.2 149.8 347.8 149.8 386.5 149.8 425.1 149.8 463.8 149.8 502.4 149.8 541.1 149.8 579.7 149.8 618.4 149.8 647.3 140.1 676.3 130.4 695.7 111.1 715 91.8 734.3 72.5 753.6 53.1 772.9 33.8 792.3 14.5 821.3 4.8 850.2-4.8 879.2 4.8 908.2 14.5 917.9 43.5 927.5 72.5 927.5 111.1 927.5 149.8 927.5 188.4 927.5 227.1 927.5 265.7 927.5 304.3 927.5 343 927.5 381.6 927.5 420.3 927.5 458.9 927.5 497.6 927.5 536.2 927.5 574.9 927.5 613.5 917.9 642.5 908.2 671.5 888.9 690.8 869.6 710.1 850.2 729.5 830.9 748.8 811.6 768.1 792.3 787.4 772.9 806.8 753.6 826.1 734.3 845.4 715 864.7 695.7 884.1 676.3 903.4 657 922.7 637.7 942 618.4 961.4 599 980.7 570 990.3 541.1 1000 502.4 1000 463.8 1000 425.1 1000 386.5 1000 357.5 990.3 328.5 980.7 309.2 961.4 289.9 942 270.5 922.7 251.2 903.4 231.9 884.1 212.6 864.7 193.2 845.4 173.9 826.1 154.6 806.8 135.3 787.4 115.9 768.1 96.6 748.8 77.3 729.5 58 710.1 38.6 690.8 19.3 671.5 9.7 642.5 0 613.5 0 574.9 0 536.2 0 497.6 0 458.9 0 420.3 0 381.6 0 343 0 304.3 0 265.7 0 227.1 0 188.4 0 149.8 0 111.1 0 72.5 9.7 43.5",
|
||||||
|
"width": 928
|
||||||
|
},
|
||||||
|
"search": [
|
||||||
|
"mode-survival"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "3a617b3ed2fe766baec5b723b1d9502f",
|
||||||
|
"css": "command-rally",
|
||||||
|
"code": 59480,
|
||||||
|
"src": "custom_icons",
|
||||||
|
"selected": true,
|
||||||
|
"svg": {
|
||||||
|
"path": "M166.7 83.3Q208.3 41.7 270.8 20.8 333.3 0 416.7 0 500 0 583.3 0 666.7 0 729.2 20.8 791.7 41.7 833.3 83.3 875 125 916.7 166.7 958.3 208.3 979.2 270.8 1000 333.3 1000 416.7 1000 500 1000 583.3 1000 666.7 979.2 729.2 958.3 791.7 916.7 833.3 875 875 833.3 916.7 791.7 958.3 729.2 979.2 666.7 1000 583.3 1000 500 1000 416.7 1000 333.3 1000 270.8 979.2 208.3 958.3 166.7 916.7 125 875 83.3 833.3 41.7 791.7 20.8 729.2 0 666.7 0 583.3 0 500 0 416.7 0 333.3 20.8 270.8 41.7 208.3 83.3 166.7 125 125 166.7 83.3M437.5 812.5Q500 833.3 562.5 812.5 625 791.7 666.7 750 708.3 708.3 750 666.7 791.7 625 812.5 562.5 833.3 500 812.5 437.5 791.7 375 750 333.3 708.3 291.7 666.7 250 625 208.3 562.5 187.5 500 166.7 437.5 187.5 375 208.3 333.3 250 291.7 291.7 250 333.3 208.3 375 187.5 437.5 166.7 500 187.5 562.5 208.3 625 250 666.7 291.7 708.3 333.3 750 375 791.7 437.5 812.5",
|
||||||
|
"width": 1000
|
||||||
|
},
|
||||||
|
"search": [
|
||||||
|
"command-rally"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "90fb5a431ca95c46a446c8f4a481d5ce",
|
||||||
|
"css": "command-attack",
|
||||||
|
"code": 59481,
|
||||||
|
"src": "custom_icons",
|
||||||
|
"selected": true,
|
||||||
|
"svg": {
|
||||||
|
"path": "M670.2 74.5Q712.8 31.9 776.6 10.6 840.4-10.6 904.3 10.6 968.1 31.9 989.4 95.7 1010.6 159.6 989.4 223.4 968.1 287.2 925.5 329.8 883 372.3 840.4 414.9 797.9 457.4 755.3 500 712.8 542.6 670.2 585.1 627.7 627.7 585.1 670.2 542.6 712.8 542.6 755.3 542.6 797.9 585.1 840.4 627.7 883 627.7 925.5 627.7 968.1 585.1 968.1 542.6 968.1 500 925.5 457.4 883 414.9 883 372.3 883 329.8 925.5 287.2 968.1 223.4 989.4 159.6 1010.6 95.7 989.4 31.9 968.1 10.6 904.3-10.6 840.4 10.6 776.6 31.9 712.8 74.5 670.2 117 627.7 117 585.1 117 542.6 74.5 500 31.9 457.4 31.9 414.9 31.9 372.3 74.5 372.3 117 372.3 159.6 414.9 202.1 457.4 244.7 457.4 287.2 457.4 329.8 414.9 372.3 372.3 414.9 329.8 457.4 287.2 500 244.7 542.6 202.1 585.1 159.6 627.7 117 670.2 74.5",
|
||||||
|
"width": 1000
|
||||||
|
},
|
||||||
|
"search": [
|
||||||
|
"command-attack"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "17ef812a059c83b5ea3612f860f9569a",
|
||||||
|
"css": "command-retreat",
|
||||||
|
"code": 59482,
|
||||||
|
"src": "custom_icons",
|
||||||
|
"selected": true,
|
||||||
|
"svg": {
|
||||||
|
"path": "M352.3 45.5Q397.7 0 443.2 0 488.6 0 511.4 68.2 534.1 136.4 556.8 204.5 579.5 272.7 647.7 295.5 715.9 318.2 806.8 318.2 897.7 318.2 965.9 340.9 1034.1 363.6 1056.8 431.8 1079.5 500 1056.8 568.2 1034.1 636.4 965.9 659.1 897.7 681.8 806.8 681.8 715.9 681.8 647.7 704.5 579.5 727.3 556.8 795.5 534.1 863.6 511.4 931.8 488.6 1000 443.2 1000 397.7 1000 352.3 954.5 306.8 909.1 261.4 863.6 215.9 818.2 170.5 772.7 125 727.3 79.5 681.8 34.1 636.4 11.4 568.2-11.4 500 11.4 431.8 34.1 363.6 79.5 318.2 125 272.7 170.5 227.3 215.9 181.8 261.4 136.4 306.8 90.9 352.3 45.5",
|
||||||
|
"width": 1068
|
||||||
|
},
|
||||||
|
"search": [
|
||||||
|
"command-retreat"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "1bc31b80669cb5edc2ee5d1370554bc9",
|
||||||
|
"css": "players",
|
||||||
|
"code": 59483,
|
||||||
|
"src": "custom_icons",
|
||||||
|
"selected": true,
|
||||||
|
"svg": {
|
||||||
|
"path": "M370.1 55.1Q401.6 23.6 448.8 7.9 496.1-7.9 543.3 7.9 590.6 23.6 622 55.1 653.5 86.6 685 118.1 716.5 149.6 732.3 196.9 748 244.1 732.3 291.3 716.5 338.6 685 370.1 653.5 401.6 653.5 433.1 653.5 464.6 685 496.1 716.5 527.6 748 559.1 779.5 590.6 811 622 842.5 653.5 874 685 905.5 716.5 937 748 968.5 779.5 984.3 826.8 1000 874 984.3 921.3 968.5 968.5 921.3 984.3 874 1000 811 1000 748 1000 685 1000 622 1000 559.1 1000 496.1 1000 433.1 1000 370.1 1000 307.1 1000 244.1 1000 181.1 1000 118.1 1000 70.9 984.3 23.6 968.5 7.9 921.3-7.9 874 7.9 826.8 23.6 779.5 55.1 748 86.6 716.5 118.1 685 149.6 653.5 181.1 622 212.6 590.6 244.1 559.1 275.6 527.6 307.1 496.1 338.6 464.6 338.6 433.1 338.6 401.6 307.1 370.1 275.6 338.6 259.8 291.3 244.1 244.1 259.8 196.9 275.6 149.6 307.1 118.1 338.6 86.6 370.1 55.1",
|
||||||
|
"width": 992
|
||||||
|
},
|
||||||
|
"search": [
|
||||||
|
"players"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "2073dbd997e5d8e1ffc1322d13ba5585",
|
||||||
|
"css": "chat",
|
||||||
|
"code": 59484,
|
||||||
|
"src": "custom_icons",
|
||||||
|
"selected": true,
|
||||||
|
"svg": {
|
||||||
|
"path": "M129 64.5Q161.3 32.3 209.7 16.1 258.1 0 322.6 0 387.1 0 451.6 0 516.1 0 580.6 0 645.2 0 709.7 0 774.2 0 822.6 16.1 871 32.3 903.2 64.5 935.5 96.8 967.7 129 1000 161.3 1016.1 209.7 1032.3 258.1 1032.3 322.6 1032.3 387.1 1032.3 451.6 1032.3 516.1 1016.1 564.5 1000 612.9 967.7 645.2 935.5 677.4 903.2 709.7 871 741.9 822.6 758.1 774.2 774.2 709.7 774.2 645.2 774.2 580.6 774.2 516.1 774.2 451.6 774.2 387.1 774.2 338.7 790.3 290.3 806.5 274.2 854.8 258.1 903.2 241.9 951.6 225.8 1000 193.5 1000 161.3 1000 129 967.7 96.8 935.5 64.5 903.2 32.3 871 16.1 822.6 0 774.2 0 709.7 0 645.2 0 580.6 0 516.1 0 451.6 0 387.1 0 322.6 0 258.1 16.1 209.7 32.3 161.3 64.5 129 96.8 96.8 129 64.5",
|
||||||
|
"width": 1032
|
||||||
|
},
|
||||||
|
"search": [
|
||||||
|
"chat"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"uid": "9dd9e835aebe1060ba7190ad2b2ed951",
|
||||||
|
"css": "zoom",
|
||||||
|
"code": 59415,
|
||||||
|
"src": "fontawesome"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
version="1.1"
|
||||||
|
id="svg2"
|
||||||
|
width="8"
|
||||||
|
height="7.75"
|
||||||
|
viewBox="0 0 8 7.75"
|
||||||
|
sodipodi:docname="chat.svg"
|
||||||
|
inkscape:version="0.92.4 (f8dce91, 2019-08-02)">
|
||||||
|
<metadata
|
||||||
|
id="metadata8">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs6" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#000000"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1318"
|
||||||
|
id="namedview4"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="23.6"
|
||||||
|
inkscape:cx="-8.033898"
|
||||||
|
inkscape:cy="4.0042373"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="g16" />
|
||||||
|
<g
|
||||||
|
id="g16"
|
||||||
|
transform="translate(-1,-1)">
|
||||||
|
<path
|
||||||
|
style="fill:#ffffff;fill-opacity:1"
|
||||||
|
d="M 2,1.5 Q 2.25,1.25 2.625,1.125 3,1 3.5,1 4,1 4.5,1 5,1 5.5,1 6,1 6.5,1 7,1 7.375,1.125 7.75,1.25 8,1.5 8.25,1.75 8.5,2 8.75,2.25 8.875,2.625 9,3 9,3.5 9,4 9,4.5 9,5 8.875,5.375 8.75,5.75 8.5,6 8.25,6.25 8,6.5 7.75,6.75 7.375,6.875 7,7 6.5,7 6,7 5.5,7 5,7 4.5,7 4,7 3.625,7.125 3.25,7.25 3.125,7.625 3,8 2.875,8.375 2.75,8.75 2.5,8.75 2.25,8.75 2,8.5 1.75,8.25 1.5,8 1.25,7.75 1.125,7.375 1,7 1,6.5 1,6 1,5.5 1,5 1,4.5 1,4 1,3.5 1,3 1.125,2.625 1.25,2.25 1.5,2 1.75,1.75 2,1.5"
|
||||||
|
id="path14"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,62 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
version="1.1"
|
||||||
|
id="svg12"
|
||||||
|
width="5.875"
|
||||||
|
height="5.875"
|
||||||
|
viewBox="0 0 5.875 5.875"
|
||||||
|
sodipodi:docname="command-attack.svg"
|
||||||
|
inkscape:version="0.92.4 (f8dce91, 2019-08-02)">
|
||||||
|
<metadata
|
||||||
|
id="metadata18">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs16" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#000000"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1318"
|
||||||
|
id="namedview14"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="29.5"
|
||||||
|
inkscape:cx="-7.706568"
|
||||||
|
inkscape:cy="1.5137712"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="g61" />
|
||||||
|
<g
|
||||||
|
id="g61"
|
||||||
|
transform="translate(-1.0625,-1.0625)">
|
||||||
|
<path
|
||||||
|
style="fill:#ffffff;fill-opacity:1"
|
||||||
|
d="M 5,1.5 Q 5.25,1.25 5.625,1.125 6,1 6.375,1.125 6.75,1.25 6.875,1.625 7,2 6.875,2.375 6.75,2.75 6.5,3 6.25,3.25 6,3.5 5.75,3.75 5.5,4 5.25,4.25 5,4.5 4.75,4.75 4.5,5 4.25,5.25 4.25,5.5 4.25,5.75 4.5,6 4.75,6.25 4.75,6.5 4.75,6.75 4.5,6.75 4.25,6.75 4,6.5 3.75,6.25 3.5,6.25 3.25,6.25 3,6.5 2.75,6.75 2.375,6.875 2,7 1.625,6.875 1.25,6.75 1.125,6.375 1,6 1.125,5.625 1.25,5.25 1.5,5 1.75,4.75 1.75,4.5 1.75,4.25 1.5,4 1.25,3.75 1.25,3.5 1.25,3.25 1.5,3.25 1.75,3.25 2,3.5 2.25,3.75 2.5,3.75 2.75,3.75 3,3.5 3.25,3.25 3.5,3 3.75,2.75 4,2.5 4.25,2.25 4.5,2 4.75,1.75 5,1.5"
|
||||||
|
id="path59"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,62 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
version="1.1"
|
||||||
|
id="svg2"
|
||||||
|
width="6"
|
||||||
|
height="6"
|
||||||
|
viewBox="0 0 6 6"
|
||||||
|
sodipodi:docname="command-rally.svg"
|
||||||
|
inkscape:version="0.92.4 (f8dce91, 2019-08-02)">
|
||||||
|
<metadata
|
||||||
|
id="metadata8">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs6" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#000000"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1318"
|
||||||
|
id="namedview4"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="29.5"
|
||||||
|
inkscape:cx="-12.118644"
|
||||||
|
inkscape:cy="2.7966102"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="g88" />
|
||||||
|
<g
|
||||||
|
id="g88"
|
||||||
|
transform="translate(-1,-1)">
|
||||||
|
<path
|
||||||
|
style="fill:#ffffff;fill-opacity:1"
|
||||||
|
d="M 2,1.5 Q 2.25,1.25 2.625,1.125 3,1 3.5,1 4,1 4.5,1 5,1 5.375,1.125 5.75,1.25 6,1.5 6.25,1.75 6.5,2 6.75,2.25 6.875,2.625 7,3 7,3.5 7,4 7,4.5 7,5 6.875,5.375 6.75,5.75 6.5,6 6.25,6.25 6,6.5 5.75,6.75 5.375,6.875 5,7 4.5,7 4,7 3.5,7 3,7 2.625,6.875 2.25,6.75 2,6.5 1.75,6.25 1.5,6 1.25,5.75 1.125,5.375 1,5 1,4.5 1,4 1,3.5 1,3 1.125,2.625 1.25,2.25 1.5,2 1.75,1.75 2,1.5 M 3.625,5.875 Q 4,6 4.375,5.875 4.75,5.75 5,5.5 5.25,5.25 5.5,5 5.75,4.75 5.875,4.375 6,4 5.875,3.625 5.75,3.25 5.5,3 5.25,2.75 5,2.5 4.75,2.25 4.375,2.125 4,2 3.625,2.125 3.25,2.25 3,2.5 2.75,2.75 2.5,3 2.25,3.25 2.125,3.625 2,4 2.125,4.375 2.25,4.75 2.5,5 2.75,5.25 3,5.5 3.25,5.75 3.625,5.875"
|
||||||
|
id="path84"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1,62 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
version="1.1"
|
||||||
|
id="svg22"
|
||||||
|
width="5.875"
|
||||||
|
height="5.5"
|
||||||
|
viewBox="0 0 5.875 5.5"
|
||||||
|
sodipodi:docname="command-retreat.svg"
|
||||||
|
inkscape:version="0.92.4 (f8dce91, 2019-08-02)">
|
||||||
|
<metadata
|
||||||
|
id="metadata28">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs26" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#000000"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1318"
|
||||||
|
id="namedview24"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="29.5"
|
||||||
|
inkscape:cx="-14.520127"
|
||||||
|
inkscape:cy="1.4618644"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="g36" />
|
||||||
|
<g
|
||||||
|
id="g36"
|
||||||
|
transform="translate(-1.0625,-1.25)">
|
||||||
|
<path
|
||||||
|
style="fill:#ffffff;fill-opacity:1"
|
||||||
|
d="M 3,1.5 Q 3.25,1.25 3.5,1.25 3.75,1.25 3.875,1.625 4,2 4.125,2.375 4.25,2.75 4.625,2.875 5,3 5.5,3 6,3 6.375,3.125 6.75,3.25 6.875,3.625 7,4 6.875,4.375 6.75,4.75 6.375,4.875 6,5 5.5,5 5,5 4.625,5.125 4.25,5.25 4.125,5.625 4,6 3.875,6.375 3.75,6.75 3.5,6.75 3.25,6.75 3,6.5 2.75,6.25 2.5,6 2.25,5.75 2,5.5 1.75,5.25 1.5,5 1.25,4.75 1.125,4.375 1,4 1.125,3.625 1.25,3.25 1.5,3 1.75,2.75 2,2.5 2.25,2.25 2.5,2 2.75,1.75 3,1.5"
|
||||||
|
id="path34"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.1 KiB |
@@ -0,0 +1,62 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
version="1.1"
|
||||||
|
id="svg12"
|
||||||
|
width="12.875"
|
||||||
|
height="12.875"
|
||||||
|
viewBox="0 0 12.875 12.875"
|
||||||
|
sodipodi:docname="mode-attack.svg"
|
||||||
|
inkscape:version="0.92.4 (f8dce91, 2019-08-02)">
|
||||||
|
<metadata
|
||||||
|
id="metadata18">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs16" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#000000"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1318"
|
||||||
|
id="namedview14"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="46.093751"
|
||||||
|
inkscape:cx="1.4974999"
|
||||||
|
inkscape:cy="12.3775"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="g869" />
|
||||||
|
<g
|
||||||
|
id="g869"
|
||||||
|
transform="translate(-1.0625,-2.0625)">
|
||||||
|
<path
|
||||||
|
style="fill:#ffffff;fill-opacity:1"
|
||||||
|
d="M 12,2.5 Q 12.25,2.25 12.625,2.125 13,2 13.375,2.125 13.75,2.25 13.875,2.625 14,3 13.875,3.375 13.75,3.75 13.5,4 13.25,4.25 13,4.5 12.75,4.75 12.5,5 12.25,5.25 12,5.5 11.75,5.75 11.5,6 11.25,6.25 11,6.5 10.75,6.75 10.5,7 10.25,7.25 10,7.5 9.75,7.75 9.5,8 9.25,8.25 9,8.5 8.75,8.75 8.5,9 8.25,9.25 8,9.5 7.75,9.75 7.5,10 7.25,10.25 7.125,10.625 7,11 7.125,11.375 7.25,11.75 7.5,12 7.75,12.25 7.875,12.625 8,13 7.875,13.375 7.75,13.75 7.375,13.875 7,14 6.625,13.875 6.25,13.75 6,13.5 5.75,13.25 5.375,13.125 5,13 4.625,13.125 4.25,13.25 4,13.5 3.75,13.75 3.5,14 3.25,14.25 3,14.5 2.75,14.75 2.375,14.875 2,15 1.625,14.875 1.25,14.75 1.125,14.375 1,14 1.125,13.625 1.25,13.25 1.5,13 1.75,12.75 2,12.5 2.25,12.25 2.5,12 2.75,11.75 2.875,11.375 3,11 2.875,10.625 2.75,10.25 2.5,10 2.25,9.75 2.125,9.375 2,9 2.125,8.625 2.25,8.25 2.625,8.125 3,8 3.375,8.125 3.75,8.25 4,8.5 4.25,8.75 4.625,8.875 5,9 5.375,8.875 5.75,8.75 6,8.5 6.25,8.25 6.5,8 6.75,7.75 7,7.5 7.25,7.25 7.5,7 7.75,6.75 8,6.5 8.25,6.25 8.5,6 8.75,5.75 9,5.5 9.25,5.25 9.5,5 9.75,4.75 10,4.5 10.25,4.25 10.5,4 10.75,3.75 11,3.5 11.25,3.25 11.5,3 11.75,2.75 12,2.5"
|
||||||
|
id="path867"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.8 KiB |
@@ -0,0 +1,62 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
version="1.1"
|
||||||
|
id="svg2"
|
||||||
|
width="15.874997"
|
||||||
|
height="12.874997"
|
||||||
|
viewBox="0 0 15.874997 12.874997"
|
||||||
|
sodipodi:docname="mode-pvp.svg"
|
||||||
|
inkscape:version="0.92.4 (f8dce91, 2019-08-02)">
|
||||||
|
<metadata
|
||||||
|
id="metadata8">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs6" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#000000"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1318"
|
||||||
|
id="namedview4"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="40.972221"
|
||||||
|
inkscape:cx="-1.9533477"
|
||||||
|
inkscape:cy="12.95987"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="g20" />
|
||||||
|
<g
|
||||||
|
id="g20"
|
||||||
|
transform="translate(-1.0625002,-3.0625)">
|
||||||
|
<path
|
||||||
|
style="fill:#ffffff;fill-opacity:1"
|
||||||
|
d="m 5,3.0625 c -0.125,0 -0.25,0.020833 -0.375,0.0625 -0.25,0.083333 -0.4166667,0.25 -0.5,0.5 -0.083333,0.25 -0.083333,0.5 0,0.75 C 4.2083333,4.625 4.3333333,4.8333333 4.5,5 L 5,5.5 5.5,6 6,6.5 C 6.1666667,6.6666667 6.3333333,6.75 6.5,6.75 6.6666667,6.75 6.8333333,6.6666667 7,6.5 L 7.5,6 C 7.6666667,5.8333333 7.75,5.6666667 7.75,5.5 7.75,5.3333333 7.6666667,5.1666667 7.5,5 L 7,4.5 6.5,4 6,3.5 C 5.8333333,3.3333333 5.625,3.2083333 5.375,3.125 5.25,3.0833333 5.125,3.0625 5,3.0625 Z m 8,0 c -0.125,0 -0.25,0.020833 -0.375,0.0625 C 12.375,3.2083333 12.166667,3.3333333 12,3.5 L 11.5,4 11,4.5 10.5,5 10,5.5 9.5,6 9,6.5 8.5,7 8,7.5 7.5,8 7,8.5 6.5,9 6,9.5 C 5.8333333,9.6666667 5.625,9.7916667 5.375,9.875 c -0.25,0.083333 -0.5,0.083333 -0.75,0 C 4.375,9.7916667 4.1666667,9.6666667 4,9.5 3.8333333,9.3333333 3.625,9.2083333 3.375,9.125 c -0.25,-0.083333 -0.5,-0.083333 -0.75,0 -0.25,0.083333 -0.4166667,0.25 -0.5,0.5 -0.083333,0.25 -0.083333,0.5 0,0.75 0.083333,0.25 0.2083333,0.458333 0.375,0.625 0.1666667,0.166667 0.2916667,0.375 0.375,0.625 0.083333,0.25 0.083333,0.5 0,0.75 C 2.7916667,12.625 2.6666667,12.833333 2.5,13 L 2,13.5 1.5,14 c -0.1666667,0.166667 -0.2916667,0.375 -0.375,0.625 -0.083333,0.25 -0.083333,0.5 0,0.75 0.083333,0.25 0.25,0.416667 0.5,0.5 0.25,0.08333 0.5,0.08333 0.75,0 C 2.625,15.791667 2.8333333,15.666667 3,15.5 L 3.5,15 4,14.5 c 0.1666667,-0.166667 0.375,-0.291667 0.625,-0.375 0.25,-0.08333 0.5,-0.08333 0.75,0 0.25,0.08333 0.4583333,0.208333 0.625,0.375 0.1666667,0.166667 0.375,0.291667 0.625,0.375 0.25,0.08333 0.5,0.08333 0.75,0 0.25,-0.08333 0.4166667,-0.25 0.5,-0.5 0.083333,-0.25 0.083333,-0.5 0,-0.75 C 7.7916667,13.375 7.6666667,13.166667 7.5,13 7.3333333,12.833333 7.2083333,12.625 7.125,12.375 c -0.083333,-0.25 -0.083333,-0.5 0,-0.75 C 7.2083333,11.375 7.3333333,11.166667 7.5,11 L 8,10.5 8.5,10 9,9.5 9.5,9 10,8.5 10.5,8 11,7.5 11.5,7 12,6.5 12.5,6 13,5.5 13.5,5 c 0.166667,-0.1666667 0.291667,-0.375 0.375,-0.625 0.08333,-0.25 0.08333,-0.5 0,-0.75 -0.08333,-0.25 -0.25,-0.4166667 -0.5,-0.5 C 13.25,3.0833333 13.125,3.0625 13,3.0625 Z m 2,6 c -0.125,0 -0.25,0.020833 -0.375,0.0625 C 14.375,9.208333 14.166667,9.3333333 14,9.5 c -0.166667,0.1666667 -0.375,0.2916667 -0.625,0.375 -0.25,0.083333 -0.5,0.083333 -0.75,0 C 12.375,9.7916667 12.166667,9.6666667 12,9.5 11.833333,9.3333333 11.666667,9.25 11.5,9.25 11.333333,9.25 11.166667,9.3333333 11,9.5 L 10.5,10 c -0.166667,0.166667 -0.25,0.333333 -0.25,0.5 0,0.166667 0.08333,0.333333 0.25,0.5 0.166667,0.166667 0.291667,0.375 0.375,0.625 0.08333,0.25 0.08333,0.5 0,0.75 -0.08333,0.25 -0.208333,0.458333 -0.375,0.625 -0.166667,0.166667 -0.291667,0.375 -0.375,0.625 -0.08333,0.25 -0.08333,0.5 0,0.75 0.08333,0.25 0.25,0.416667 0.5,0.5 0.25,0.08333 0.5,0.08333 0.75,0 0.25,-0.08333 0.458333,-0.208333 0.625,-0.375 0.166667,-0.166667 0.375,-0.291667 0.625,-0.375 0.25,-0.08333 0.5,-0.08333 0.75,0 0.25,0.08333 0.458333,0.208333 0.625,0.375 l 0.5,0.5 0.5,0.5 c 0.166667,0.166667 0.375,0.291667 0.625,0.375 0.25,0.08333 0.5,0.08333 0.75,0 0.25,-0.08333 0.416667,-0.25 0.5,-0.5 0.08333,-0.25 0.08333,-0.5 0,-0.75 C 16.791667,14.375 16.666667,14.166667 16.5,14 L 16,13.5 15.5,13 c -0.166667,-0.166667 -0.291667,-0.375 -0.375,-0.625 -0.08333,-0.25 -0.08333,-0.5 0,-0.75 0.08333,-0.25 0.208333,-0.458333 0.375,-0.625 0.166667,-0.166667 0.291667,-0.375 0.375,-0.625 0.08333,-0.25 0.08333,-0.5 0,-0.75 -0.08333,-0.25 -0.25,-0.4166667 -0.5,-0.5 C 15.25,9.0833333 15.125,9.0625 15,9.0625 Z"
|
||||||
|
id="path14"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 5.1 KiB |
@@ -0,0 +1,63 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
version="1.1"
|
||||||
|
id="svg22"
|
||||||
|
width="12"
|
||||||
|
height="12.9375"
|
||||||
|
viewBox="0 0 12 12.9375"
|
||||||
|
sodipodi:docname="mode-survival.svg"
|
||||||
|
inkscape:version="0.92.4 (f8dce91, 2019-08-02)">
|
||||||
|
<metadata
|
||||||
|
id="metadata28">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs26" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#000000"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1318"
|
||||||
|
id="namedview24"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="46.093751"
|
||||||
|
inkscape:cx="0.55999994"
|
||||||
|
inkscape:cy="12.44"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="g844" />
|
||||||
|
<g
|
||||||
|
id="g844"
|
||||||
|
inkscape:label="g844"
|
||||||
|
transform="translate(-2,-2.0625)">
|
||||||
|
<path
|
||||||
|
style="fill:#ffffff;fill-opacity:1"
|
||||||
|
d="M 2.125,2.625 Q 2.25,2.25 2.625,2.125 3,2 3.375,2.125 3.75,2.25 4,2.5 4.25,2.75 4.5,3 4.75,3.25 5,3.5 5.25,3.75 5.625,3.875 6,4 6.5,4 7,4 7.5,4 8,4 8.5,4 9,4 9.5,4 10,4 10.375,3.875 10.75,3.75 11,3.5 11.25,3.25 11.5,3 11.75,2.75 12,2.5 12.25,2.25 12.625,2.125 13,2 13.375,2.125 13.75,2.25 13.875,2.625 14,3 14,3.5 q 0,0.5 0,1 0,0.5 0,1 0,0.5 0,1 0,0.5 0,1 0,0.5 0,1 0,0.5 0,1 0,0.5 -0.125,0.875 Q 13.75,10.75 13.5,11 13.25,11.25 13,11.5 12.75,11.75 12.5,12 12.25,12.25 12,12.5 11.75,12.75 11.5,13 11.25,13.25 11,13.5 10.75,13.75 10.5,14 10.25,14.25 10,14.5 9.75,14.75 9.375,14.875 9,15 8.5,15 8,15 7.5,15 7,15 6.625,14.875 6.25,14.75 6,14.5 5.75,14.25 5.5,14 5.25,13.75 5,13.5 4.75,13.25 4.5,13 4.25,12.75 4,12.5 3.75,12.25 3.5,12 3.25,11.75 3,11.5 2.75,11.25 2.5,11 2.25,10.75 2.125,10.375 2,10 2,9.5 2,9 2,8.5 2,8 2,7.5 2,7 2,6.5 2,6 2,5.5 2,5 2,4.5 2,4 2,3.5 2,3 2.125,2.625"
|
||||||
|
id="path842"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.6 KiB |
@@ -0,0 +1,62 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
version="1.1"
|
||||||
|
id="svg2"
|
||||||
|
width="7.875"
|
||||||
|
height="7.9375"
|
||||||
|
viewBox="0 0 7.875 7.9375"
|
||||||
|
sodipodi:docname="players.svg"
|
||||||
|
inkscape:version="0.92.4 (f8dce91, 2019-08-02)">
|
||||||
|
<metadata
|
||||||
|
id="metadata8">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs6" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#000000"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="2560"
|
||||||
|
inkscape:window-height="1318"
|
||||||
|
id="namedview4"
|
||||||
|
showgrid="false"
|
||||||
|
inkscape:zoom="23.6"
|
||||||
|
inkscape:cx="-14.960805"
|
||||||
|
inkscape:cy="3.4067797"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="27"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="g16" />
|
||||||
|
<g
|
||||||
|
id="g16"
|
||||||
|
transform="translate(-1.0625,-1.0625)">
|
||||||
|
<path
|
||||||
|
style="fill:#ffffff;fill-opacity:1"
|
||||||
|
d="M 4,1.5 Q 4.25,1.25 4.625,1.125 5,1 5.375,1.125 5.75,1.25 6,1.5 6.25,1.75 6.5,2 6.75,2.25 6.875,2.625 7,3 6.875,3.375 6.75,3.75 6.5,4 6.25,4.25 6.25,4.5 6.25,4.75 6.5,5 6.75,5.25 7,5.5 7.25,5.75 7.5,6 7.75,6.25 8,6.5 8.25,6.75 8.5,7 8.75,7.25 8.875,7.625 9,8 8.875,8.375 8.75,8.75 8.375,8.875 8,9 7.5,9 7,9 6.5,9 6,9 5.5,9 5,9 4.5,9 4,9 3.5,9 3,9 2.5,9 2,9 1.625,8.875 1.25,8.75 1.125,8.375 1,8 1.125,7.625 1.25,7.25 1.5,7 1.75,6.75 2,6.5 2.25,6.25 2.5,6 2.75,5.75 3,5.5 3.25,5.25 3.5,5 3.75,4.75 3.75,4.5 3.75,4.25 3.5,4 3.25,3.75 3.125,3.375 3,3 3.125,2.625 3.25,2.25 3.5,2 3.75,1.75 4,1.5"
|
||||||
|
id="path14"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 677 B |
|
Before Width: | Height: | Size: 520 B |
|
Before Width: | Height: | Size: 870 B |
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 207 B |
|
Before Width: | Height: | Size: 247 B |
|
Before Width: | Height: | Size: 266 B |
|
Before Width: | Height: | Size: 265 B |
|
Before Width: | Height: | Size: 266 B |
|
Before Width: | Height: | Size: 180 B |
|
Before Width: | Height: | Size: 164 B |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 5.5 KiB |
@@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
duplicatePadding: true,
|
||||||
|
combineSubdirectories: true,
|
||||||
|
flattenPaths: true,
|
||||||
|
maxWidth: 2048,
|
||||||
|
maxHeight: 2048,
|
||||||
|
fast: true
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 237 B After Width: | Height: | Size: 237 B |
|
Before Width: | Height: | Size: 249 B After Width: | Height: | Size: 249 B |
|
Before Width: | Height: | Size: 320 B After Width: | Height: | Size: 320 B |
|
Before Width: | Height: | Size: 349 B After Width: | Height: | Size: 349 B |
|
Before Width: | Height: | Size: 520 B After Width: | Height: | Size: 520 B |
|
Before Width: | Height: | Size: 870 B After Width: | Height: | Size: 870 B |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 176 B After Width: | Height: | Size: 176 B |
|
Before Width: | Height: | Size: 170 B After Width: | Height: | Size: 170 B |
|
Before Width: | Height: | Size: 5.8 KiB After Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 7.3 KiB |
|
Before Width: | Height: | Size: 270 B After Width: | Height: | Size: 270 B |
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 361 B After Width: | Height: | Size: 361 B |
|
Before Width: | Height: | Size: 181 B After Width: | Height: | Size: 181 B |
|
Before Width: | Height: | Size: 200 B After Width: | Height: | Size: 200 B |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 7.6 KiB |
|
Before Width: | Height: | Size: 218 B After Width: | Height: | Size: 218 B |
|
Before Width: | Height: | Size: 200 B After Width: | Height: | Size: 200 B |
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 5.6 KiB After Width: | Height: | Size: 5.6 KiB |
|
After Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 6.8 KiB |
|
After Width: | Height: | Size: 5.7 KiB |
@@ -59,6 +59,7 @@ stat.built = Buildings Built:[accent] {0}
|
|||||||
stat.destroyed = Buildings Destroyed:[accent] {0}
|
stat.destroyed = Buildings Destroyed:[accent] {0}
|
||||||
stat.deconstructed = Buildings Deconstructed:[accent] {0}
|
stat.deconstructed = Buildings Deconstructed:[accent] {0}
|
||||||
stat.delivered = Resources Launched:
|
stat.delivered = Resources Launched:
|
||||||
|
stat.playtime = Time Played:[accent] {0}
|
||||||
stat.rank = Final Rank: [accent]{0}
|
stat.rank = Final Rank: [accent]{0}
|
||||||
|
|
||||||
launcheditems = [accent]Launched Items
|
launcheditems = [accent]Launched Items
|
||||||
@@ -104,6 +105,7 @@ mods.none = [LIGHT_GRAY]No mods found!
|
|||||||
mods.guide = Modding Guide
|
mods.guide = Modding Guide
|
||||||
mods.report = Report Bug
|
mods.report = Report Bug
|
||||||
mods.openfolder = Open Mod Folder
|
mods.openfolder = Open Mod Folder
|
||||||
|
mod.display = [gray]Mod:[orange] {0}
|
||||||
mod.enabled = [lightgray]Enabled
|
mod.enabled = [lightgray]Enabled
|
||||||
mod.disabled = [scarlet]Disabled
|
mod.disabled = [scarlet]Disabled
|
||||||
mod.disable = Disable
|
mod.disable = Disable
|
||||||
@@ -532,9 +534,12 @@ no = No
|
|||||||
info.title = Info
|
info.title = Info
|
||||||
error.title = [crimson]An error has occured
|
error.title = [crimson]An error has occured
|
||||||
error.crashtitle = An error has occured
|
error.crashtitle = An error has occured
|
||||||
|
unit.nobuild = [scarlet]Unit can't build
|
||||||
blocks.input = Input
|
blocks.input = Input
|
||||||
blocks.output = Output
|
blocks.output = Output
|
||||||
blocks.booster = Booster
|
blocks.booster = Booster
|
||||||
|
blocks.tiles = Required Tiles
|
||||||
|
blocks.affinities = Affinities
|
||||||
block.unknown = [lightgray]???
|
block.unknown = [lightgray]???
|
||||||
blocks.powercapacity = Power Capacity
|
blocks.powercapacity = Power Capacity
|
||||||
blocks.powershot = Power/Shot
|
blocks.powershot = Power/Shot
|
||||||
@@ -669,6 +674,7 @@ setting.mutesound.name = Mute Sound
|
|||||||
setting.crashreport.name = Send Anonymous Crash Reports
|
setting.crashreport.name = Send Anonymous Crash Reports
|
||||||
setting.savecreate.name = Auto-Create Saves
|
setting.savecreate.name = Auto-Create Saves
|
||||||
setting.publichost.name = Public Game Visibility
|
setting.publichost.name = Public Game Visibility
|
||||||
|
setting.playerlimit.name = Player Limit
|
||||||
setting.chatopacity.name = Chat Opacity
|
setting.chatopacity.name = Chat Opacity
|
||||||
setting.lasersopacity.name = Power Laser Opacity
|
setting.lasersopacity.name = Power Laser Opacity
|
||||||
setting.bridgeopacity.name = Bridge Opacity
|
setting.bridgeopacity.name = Bridge Opacity
|
||||||
@@ -948,6 +954,7 @@ block.message.name = Message
|
|||||||
block.illuminator.name = Illuminator
|
block.illuminator.name = Illuminator
|
||||||
block.illuminator.description = A small, compact, configurable light source. Requires power to function.
|
block.illuminator.description = A small, compact, configurable light source. Requires power to function.
|
||||||
block.overflow-gate.name = Overflow Gate
|
block.overflow-gate.name = Overflow Gate
|
||||||
|
block.underflow-gate.name = Underflow Gate
|
||||||
block.silicon-smelter.name = Silicon Smelter
|
block.silicon-smelter.name = Silicon Smelter
|
||||||
block.phase-weaver.name = Phase Weaver
|
block.phase-weaver.name = Phase Weaver
|
||||||
block.pulverizer.name = Pulverizer
|
block.pulverizer.name = Pulverizer
|
||||||
@@ -972,7 +979,8 @@ block.pneumatic-drill.name = Pneumatic Drill
|
|||||||
block.laser-drill.name = Laser Drill
|
block.laser-drill.name = Laser Drill
|
||||||
block.water-extractor.name = Water Extractor
|
block.water-extractor.name = Water Extractor
|
||||||
block.cultivator.name = Cultivator
|
block.cultivator.name = Cultivator
|
||||||
block.dart-mech-pad.name = Alpha Mech Pad
|
block.dart-ship-pad.name = Dart Ship Pad
|
||||||
|
block.alpha-mech-pad.name = Alpha Mech Pad
|
||||||
block.delta-mech-pad.name = Delta Mech Pad
|
block.delta-mech-pad.name = Delta Mech Pad
|
||||||
block.javelin-ship-pad.name = Javelin Ship Pad
|
block.javelin-ship-pad.name = Javelin Ship Pad
|
||||||
block.trident-ship-pad.name = Trident Ship Pad
|
block.trident-ship-pad.name = Trident Ship Pad
|
||||||
@@ -1178,6 +1186,7 @@ block.inverted-sorter.description = Processes items like a standard sorter, but
|
|||||||
block.router.description = Accepts items, then outputs them to up to 3 other directions equally. Useful for splitting the materials from one source to multiple targets.\n\n[scarlet]Never use next to production inputs, as they will get clogged by output.[]
|
block.router.description = Accepts items, then outputs them to up to 3 other directions equally. Useful for splitting the materials from one source to multiple targets.\n\n[scarlet]Never use next to production inputs, as they will get clogged by output.[]
|
||||||
block.distributor.description = An advanced router. Splits items to up to 7 other directions equally.
|
block.distributor.description = An advanced router. Splits items to up to 7 other directions equally.
|
||||||
block.overflow-gate.description = Only outputs to the left and right if the front path is blocked.
|
block.overflow-gate.description = Only outputs to the left and right if the front path is blocked.
|
||||||
|
block.underflow-gate.description = The opposite of an overflow gate. Outputs to the front if the left and right paths are blocked.
|
||||||
block.mass-driver.description = The ultimate item transport block. Collects several items and then shoots them to another mass driver over a long range. Requires power to operate.
|
block.mass-driver.description = The ultimate item transport block. Collects several items and then shoots them to another mass driver over a long range. Requires power to operate.
|
||||||
block.mechanical-pump.description = A cheap pump with slow output, but no power consumption.
|
block.mechanical-pump.description = A cheap pump with slow output, but no power consumption.
|
||||||
block.rotary-pump.description = An advanced pump. Pumps more liquid, but requires power.
|
block.rotary-pump.description = An advanced pump. Pumps more liquid, but requires power.
|
||||||
@@ -1246,7 +1255,6 @@ block.crawler-factory.description = Produces fast self-destructing swarm units.
|
|||||||
block.titan-factory.description = Produces advanced, armored ground units.
|
block.titan-factory.description = Produces advanced, armored ground units.
|
||||||
block.fortress-factory.description = Produces heavy artillery ground units.
|
block.fortress-factory.description = Produces heavy artillery ground units.
|
||||||
block.repair-point.description = Continuously heals the closest damaged unit in its vicinity.
|
block.repair-point.description = Continuously heals the closest damaged unit in its vicinity.
|
||||||
block.dart-mech-pad.description = Provides transformation into a basic attack mech.\nUse by tapping while standing on it.
|
|
||||||
block.delta-mech-pad.description = Provides transformation into a lightly armored hit-and-run attack mech.\nUse by tapping while standing on it.
|
block.delta-mech-pad.description = Provides transformation into a lightly armored hit-and-run attack mech.\nUse by tapping while standing on it.
|
||||||
block.tau-mech-pad.description = Provides transformation into an advanced support mech.\nUse by tapping while standing on it.
|
block.tau-mech-pad.description = Provides transformation into an advanced support mech.\nUse by tapping while standing on it.
|
||||||
block.omega-mech-pad.description = Provides transformation into a heavily-armored missile mech.\nUse by tapping while standing on it.
|
block.omega-mech-pad.description = Provides transformation into a heavily-armored missile mech.\nUse by tapping while standing on it.
|
||||||
|
|||||||
@@ -43,10 +43,10 @@ schematic.replace = Šablona tohoto jména již existuje. Chceš ji nahradit?
|
|||||||
schematic.import = Importuji šablonu...
|
schematic.import = Importuji šablonu...
|
||||||
schematic.exportfile = Exportovat soubor
|
schematic.exportfile = Exportovat soubor
|
||||||
schematic.importfile = Importovat soubor
|
schematic.importfile = Importovat soubor
|
||||||
schematic.browseworkshop = Procházet dílnu
|
schematic.browseworkshop = Procházet Workshop na Steamu
|
||||||
schematic.copy = Zkopírovat do schránky
|
schematic.copy = Zkopírovat do schránky
|
||||||
schematic.copy.import = Importovat ze schránky
|
schematic.copy.import = Importovat ze schránky
|
||||||
schematic.shareworkshop = Sdílet skrze Steam Workshop
|
schematic.shareworkshop = Sdílet skrze Workshop na Steamu
|
||||||
schematic.flip = [accent][[{0}][]/[accent][[{1}][]: Převrátit šablonu
|
schematic.flip = [accent][[{0}][]/[accent][[{1}][]: Převrátit šablonu
|
||||||
schematic.saved = Šablona byla uložena.
|
schematic.saved = Šablona byla uložena.
|
||||||
schematic.delete.confirm = Šablona bude kompletně vyhlazena.
|
schematic.delete.confirm = Šablona bude kompletně vyhlazena.
|
||||||
@@ -123,8 +123,8 @@ mod.item.remove = Tato položka je součástí [accent]'{0}'[] modifikace. Pokud
|
|||||||
mod.remove.confirm = Tato modifikace bude odstraněna.
|
mod.remove.confirm = Tato modifikace bude odstraněna.
|
||||||
mod.author = [lightgray]Autor:[] {0}
|
mod.author = [lightgray]Autor:[] {0}
|
||||||
mod.missing = Toto uložení hra obsahuje modifikace, které byly nedávno aktualizovány, nebo již nejsou nainstalovány. Použití tohoto uložení může vést k chybám. Jsi si jist, že chceš nahrát toto uložení hry?\n[lightgray]Modifikace:\n{0}
|
mod.missing = Toto uložení hra obsahuje modifikace, které byly nedávno aktualizovány, nebo již nejsou nainstalovány. Použití tohoto uložení může vést k chybám. Jsi si jist, že chceš nahrát toto uložení hry?\n[lightgray]Modifikace:\n{0}
|
||||||
mod.preview.missing = Než vystavíš svou modifikaci v dílně, musíš přidat obrázek pro náhled.\nUmísti obrázek pojmenovaný [accent]preview.png[] do složky modifikace a zkus to znovu.
|
mod.preview.missing = Než vystavíš svou modifikaci ve Workshopu na Steamu, musíš přidat obrázek pro náhled.\nUmísti obrázek pojmenovaný [accent]preview.png[] do složky modifikace a zkus to znovu.
|
||||||
mod.folder.missing = V dílně mohou být vystaveny pouze modifikace ve formě složky.\nAbys převedl modifikaci na formu složky, jednoduše rozbal zip soubor do složky a smaž starý zip soubor. Potom znovu spusť hru nebo znovu načti modifikace.
|
mod.folder.missing = Ve Workshopu na Steamu mohou být vystaveny pouze modifikace ve formě složky.\nAbys převedl modifikaci na formu složky, jednoduše rozbal zip soubor do složky a smaž starý zip soubor. Potom znovu spusť hru nebo znovu načti modifikace.
|
||||||
mod.scripts.unsupported = Tvoje zařízení nepodporuje skripty. Některé modifikace nemusí správně fungovat.
|
mod.scripts.unsupported = Tvoje zařízení nepodporuje skripty. Některé modifikace nemusí správně fungovat.
|
||||||
|
|
||||||
about.button = O hře
|
about.button = O hře
|
||||||
@@ -240,8 +240,8 @@ save.playtime = Herní čas: {0}
|
|||||||
warning = Varování.
|
warning = Varování.
|
||||||
confirm = Potvrdit
|
confirm = Potvrdit
|
||||||
delete = Smazat
|
delete = Smazat
|
||||||
view.workshop = Prohlédnout v dílně
|
view.workshop = Prohlédnout ve Workshopu na Steamu
|
||||||
workshop.listing = Upravit popis v dílně
|
workshop.listing = Upravit popis ve Workshopu na Steamu
|
||||||
ok = OK
|
ok = OK
|
||||||
open = Otevřít
|
open = Otevřít
|
||||||
customize = Přizpůsobit pravidla
|
customize = Přizpůsobit pravidla
|
||||||
@@ -285,15 +285,15 @@ map.nospawn.pvp = Tato mapa nemá nepřátelská jádra, u kterých by se mohli
|
|||||||
map.nospawn.attack = Tato mapa nemá nepřátelská jádra, která by mohla být zničena. Přidej v editoru do této mapy aspoň jedno [SCARLET]červené[] jádro.
|
map.nospawn.attack = Tato mapa nemá nepřátelská jádra, která by mohla být zničena. Přidej v editoru do této mapy aspoň jedno [SCARLET]červené[] jádro.
|
||||||
map.invalid = Chyba v načítání mapy: poškozený nebo neplatný soubor mapy.
|
map.invalid = Chyba v načítání mapy: poškozený nebo neplatný soubor mapy.
|
||||||
workshop.update = Aktualizovat položku
|
workshop.update = Aktualizovat položku
|
||||||
workshop.error = Chyba při načítání podrobností z dílny: {0}
|
workshop.error = Chyba při načítání podrobností z Workshopu na Steamu: {0}
|
||||||
map.publish.confirm = Jsi si jistý, že chceš vystavit tuto mapu?\n\n[lightgray]Ujisti se nejprve, že souhlasíš se smluvními podmínkami dílny (EULA), jinak se Tvoje mapa nezobrazí.[]
|
map.publish.confirm = Jsi si jistý, že chceš vystavit tuto mapu?\n\n[lightgray]Ujisti se nejprve, že souhlasíš se smluvními podmínkami Workshopu na Steamu (EULA), jinak se Tvoje mapa nezobrazí.[]
|
||||||
workshop.menu = Vyber si, co bys chtěl dělat s touto položkou.
|
workshop.menu = Vyber si, co bys chtěl dělat s touto položkou.
|
||||||
workshop.info = Informace o položce
|
workshop.info = Informace o položce
|
||||||
changelog = Seznam změn (volitelně):
|
changelog = Seznam změn (volitelně):
|
||||||
eula = Smluvní podmínky platformy Steam
|
eula = Smluvní podmínky platformy Steam
|
||||||
missing = Tato položka byla smazána nebo přesunuta.\n[lightgray]Položka bude automaticky odebrána ze seznamu dílny.
|
missing = Tato položka byla smazána nebo přesunuta.\n[lightgray]Položka bude automaticky odebrána ze seznamu Workshopu na Steamu.
|
||||||
publishing = [accent]Publikuji...
|
publishing = [accent]Publikuji...
|
||||||
publish.confirm = Opravdu chceš toto vystavit?\n\n[lightgray]Ujisti se nejprve, že souhlasíš se smluvními podmínkami dílny (EULA), jinak se Tvoje položky nezobrazí.[]
|
publish.confirm = Opravdu chceš toto vystavit?\n\n[lightgray]Ujisti se nejprve, že souhlasíš se smluvními podmínkami Workshopu na Steamu (EULA), jinak se Tvoje položky nezobrazí.[]
|
||||||
publish.error = Chyba při vystavování položky: {0}
|
publish.error = Chyba při vystavování položky: {0}
|
||||||
steam.error = Nepodařilo se inicializovat služby platformy Steam.\Chyba: {0}
|
steam.error = Nepodařilo se inicializovat služby platformy Steam.\Chyba: {0}
|
||||||
|
|
||||||
@@ -309,9 +309,9 @@ editor.waves = Vln:
|
|||||||
editor.rules = Pravidla:
|
editor.rules = Pravidla:
|
||||||
editor.generation = Generace:
|
editor.generation = Generace:
|
||||||
editor.ingame = Upravit ve hře
|
editor.ingame = Upravit ve hře
|
||||||
editor.publish.workshop = Vystavit v dílně
|
editor.publish.workshop = Vystavit ve Workshopu na Steamu
|
||||||
editor.newmap = Nová mapa
|
editor.newmap = Nová mapa
|
||||||
workshop = Dílna
|
workshop = Workshop na Steamu
|
||||||
waves.title = Vlny
|
waves.title = Vlny
|
||||||
waves.remove = Odebrat
|
waves.remove = Odebrat
|
||||||
waves.never = <Nikdy>
|
waves.never = <Nikdy>
|
||||||
@@ -532,6 +532,8 @@ error.crashtitle = Objevila se chyba
|
|||||||
blocks.input = Vstup
|
blocks.input = Vstup
|
||||||
blocks.output = Výstup
|
blocks.output = Výstup
|
||||||
blocks.booster = Posilovač
|
blocks.booster = Posilovač
|
||||||
|
blocks.tiles = Vyžadované dlaždice
|
||||||
|
blocks.affinities = Synergie
|
||||||
block.unknown = [lightgray]???[]
|
block.unknown = [lightgray]???[]
|
||||||
blocks.powercapacity = Kapacita energie
|
blocks.powercapacity = Kapacita energie
|
||||||
blocks.powershot = Energie na 1 výstřel
|
blocks.powershot = Energie na 1 výstřel
|
||||||
@@ -666,6 +668,7 @@ setting.mutesound.name = Ztišit zvuk
|
|||||||
setting.crashreport.name = Poslat anonymní hlášení o spadnutí Mindustry
|
setting.crashreport.name = Poslat anonymní hlášení o spadnutí Mindustry
|
||||||
setting.savecreate.name = Automaticky ukládat hru
|
setting.savecreate.name = Automaticky ukládat hru
|
||||||
setting.publichost.name = Veřejná viditelnost hry
|
setting.publichost.name = Veřejná viditelnost hry
|
||||||
|
setting.playerlimit.name = Nejvyšší počet hráčů
|
||||||
setting.chatopacity.name = Průsvitnost kanálu zpráv
|
setting.chatopacity.name = Průsvitnost kanálu zpráv
|
||||||
setting.lasersopacity.name = Průsvitnost energetického laseru
|
setting.lasersopacity.name = Průsvitnost energetického laseru
|
||||||
setting.bridgeopacity.name = Průsvitnost přemostění
|
setting.bridgeopacity.name = Průsvitnost přemostění
|
||||||
@@ -761,13 +764,13 @@ rules.blockhealthmultiplier = Násobek zdraví bloků
|
|||||||
rules.playerhealthmultiplier = Násobek zdraví hráče
|
rules.playerhealthmultiplier = Násobek zdraví hráče
|
||||||
rules.playerdamagemultiplier = Násobek útoku hráče
|
rules.playerdamagemultiplier = Násobek útoku hráče
|
||||||
rules.unitdamagemultiplier = Násobek poškození jednotkami
|
rules.unitdamagemultiplier = Násobek poškození jednotkami
|
||||||
rules.enemycorebuildradius = Poloměr, ve kterém se okolo nepřítelského jádra nesmí stavět: [lightgray](dlaždic)[]
|
rules.enemycorebuildradius = Poloměr, ve kterém se okolo nepřátelského jádra nesmí stavět: [lightgray](dlaždic)[]
|
||||||
rules.respawntime = Čas znovuzrození: [lightgray](vteřin)[]
|
rules.respawntime = Čas znovuzrození: [lightgray](vteřin)[]
|
||||||
rules.wavespacing = Čas rozestupu mezi vlnami: [lightgray](vteřin)[]
|
rules.wavespacing = Čas rozestupu mezi vlnami: [lightgray](vteřin)[]
|
||||||
rules.buildcostmultiplier = Násobek ceny stavění
|
rules.buildcostmultiplier = Násobek ceny stavění
|
||||||
rules.buildspeedmultiplier = Násobek rychlosti stavění
|
rules.buildspeedmultiplier = Násobek rychlosti stavění
|
||||||
rules.waitForWaveToEnd = Vlny čekají na nepřátele
|
rules.waitForWaveToEnd = Vlny čekají na nepřátele
|
||||||
rules.dropzoneradius = Poloměr oblasti pro sestoupení: [lightgray](dlaždic)[]
|
rules.dropzoneradius = Poloměr oblasti pro vylíhnutí: [lightgray](dlaždic)[]
|
||||||
rules.respawns = Maximální počet znovuvylíhnutí za vlnu
|
rules.respawns = Maximální počet znovuvylíhnutí za vlnu
|
||||||
rules.limitedRespawns = Maximální počet znovuzrození
|
rules.limitedRespawns = Maximální počet znovuzrození
|
||||||
rules.title.waves = Vlny
|
rules.title.waves = Vlny
|
||||||
@@ -943,7 +946,8 @@ block.inverted-sorter.name = Obrácená třídička
|
|||||||
block.message.name = Zpráva
|
block.message.name = Zpráva
|
||||||
block.illuminator.name = Osvětlovač
|
block.illuminator.name = Osvětlovač
|
||||||
block.illuminator.description = Malý, kompaktní, konfigurovatelný zdroj světla. Vyžaduje pro svoje fungování energii.
|
block.illuminator.description = Malý, kompaktní, konfigurovatelný zdroj světla. Vyžaduje pro svoje fungování energii.
|
||||||
block.overflow-gate.name = Přepadová brána
|
block.overflow-gate.name = Brána s přepadem
|
||||||
|
block.underflow-gate.name = Brána s podtokem
|
||||||
block.silicon-smelter.name = Křemíková huť
|
block.silicon-smelter.name = Křemíková huť
|
||||||
block.phase-weaver.name = Tkalcovna pro fázovou tkaninu
|
block.phase-weaver.name = Tkalcovna pro fázovou tkaninu
|
||||||
block.pulverizer.name = Rozmělňovač
|
block.pulverizer.name = Rozmělňovač
|
||||||
@@ -1034,8 +1038,8 @@ block.overdrive-projector.name = Urychlující projektor
|
|||||||
block.force-projector.name = Silový projektor
|
block.force-projector.name = Silový projektor
|
||||||
block.arc.name = Oblouk
|
block.arc.name = Oblouk
|
||||||
block.rtg-generator.name = RTG
|
block.rtg-generator.name = RTG
|
||||||
block.spectre.name = Duch
|
block.spectre.name = Spectre
|
||||||
block.meltdown.name = Tavička
|
block.meltdown.name = Meltdown
|
||||||
block.container.name = Kontejnér
|
block.container.name = Kontejnér
|
||||||
block.launch-pad.name = Vysílací plošina
|
block.launch-pad.name = Vysílací plošina
|
||||||
block.launch-pad-large.name = Velká vysílací plošina
|
block.launch-pad-large.name = Velká vysílací plošina
|
||||||
@@ -1174,6 +1178,7 @@ block.inverted-sorter.description = Třídí předměty. Pokud je předmět shod
|
|||||||
block.router.description = Přijímá předměty z jednoho směru a posílá je rovnoměrně do zbylých tří směrů. Užitečný pro rozdělení předmětů z jednoho zdroje do různých cílů, jako odbočení z dopravníků a podobně.\n\n[scarlet]Pozor, nepoužívejte pro vstup do produkční budovy, jinak se bude ucpávat výstupními předměty[].
|
block.router.description = Přijímá předměty z jednoho směru a posílá je rovnoměrně do zbylých tří směrů. Užitečný pro rozdělení předmětů z jednoho zdroje do různých cílů, jako odbočení z dopravníků a podobně.\n\n[scarlet]Pozor, nepoužívejte pro vstup do produkční budovy, jinak se bude ucpávat výstupními předměty[].
|
||||||
block.distributor.description = Pokročilý směrovač. Rozděluje předměty rovnoměrně až do 7 dalších směrů.
|
block.distributor.description = Pokročilý směrovač. Rozděluje předměty rovnoměrně až do 7 dalších směrů.
|
||||||
block.overflow-gate.description = Předměty jsou poslány do strany, pokud je směr vpřed zablokován. Užitečné například pro zpracování přebytečného materiálu, pokud je primární příjemce saturován.
|
block.overflow-gate.description = Předměty jsou poslány do strany, pokud je směr vpřed zablokován. Užitečné například pro zpracování přebytečného materiálu, pokud je primární příjemce saturován.
|
||||||
|
block.underflow-gate.description = Předměty jsou poslány vpřed, pokud je směr do strany zablokován. Užitečné například pro zpracování přebytečného materiálu, pokud je primární příjemce saturován.
|
||||||
block.mass-driver.description = Ultimátní blok pro přepravu předmětů. Sebere několik předmětů a vystřelí je k dalšímu hromadnému distributoru přes několik dlaždic. Vyžaduje ke své funkci energii.
|
block.mass-driver.description = Ultimátní blok pro přepravu předmětů. Sebere několik předmětů a vystřelí je k dalšímu hromadnému distributoru přes několik dlaždic. Vyžaduje ke své funkci energii.
|
||||||
block.mechanical-pump.description = Levné čerpadlo s pomalým průtokem, nevyžaduje však energii k provozu.
|
block.mechanical-pump.description = Levné čerpadlo s pomalým průtokem, nevyžaduje však energii k provozu.
|
||||||
block.rotary-pump.description = Pokročilé čerpadlo, které za pomoci energie vyčerpá větší množství kapalin, než standardní.
|
block.rotary-pump.description = Pokročilé čerpadlo, které za pomoci energie vyčerpá větší množství kapalin, než standardní.
|
||||||
@@ -1228,9 +1233,9 @@ block.salvo.description = Větší, pokročilejší verze střílny Duo. Pálí
|
|||||||
block.fuse.description = Velká střílna s krátkým dosahem. Pálí trojice energetických paprsků blízké nepřátele.
|
block.fuse.description = Velká střílna s krátkým dosahem. Pálí trojice energetických paprsků blízké nepřátele.
|
||||||
block.ripple.description = Extrémně silná dělostřelecká střílna. Pálí na dálku shluky střel na nepřátelské jednotky.
|
block.ripple.description = Extrémně silná dělostřelecká střílna. Pálí na dálku shluky střel na nepřátelské jednotky.
|
||||||
block.cyclone.description = Velká protiletecká a protipozemní střílna. Pálí explodující dávky na nepřítele v okolí.
|
block.cyclone.description = Velká protiletecká a protipozemní střílna. Pálí explodující dávky na nepřítele v okolí.
|
||||||
block.spectre.description = Velká střílna s kanónem s dvěma hlavněmi. Střílí velké náboje, které pronikají brněním jak pozemních, tak vzdušných nepřítelských cílů.
|
block.spectre.description = Velká střílna s kanónem s dvěma hlavněmi. Střílí velké náboje, které pronikají brněním jak pozemních, tak vzdušných nepřátelských cílů.
|
||||||
block.meltdown.description = Masivní laserový kanón. Nabije se a pak pálí nepřetržitý laserový paprsek na nepřátele v okolí. Vyžaduje ke své funkci chlazení.
|
block.meltdown.description = Masivní laserový kanón. Nabije se a pak pálí nepřetržitý laserový paprsek na nepřátele v okolí. Vyžaduje ke své funkci chlazení.
|
||||||
block.command-center.description = Vydává příkazy spojeneckým jednotkám na mapě.\nInstruuje jednotky k útoku na nepřítelské jádro, návratu do jádra nebo továrny a ke shromáždění se. Pokud se na mapě nepřátelské jádro nenachází, jednotky budou v útočném režimu držet hlídku.
|
block.command-center.description = Vydává příkazy spojeneckým jednotkám na mapě.\nInstruuje jednotky k útoku na nepřátelské jádro, návratu do jádra nebo továrny a ke shromáždění se. Pokud se na mapě nepřátelské jádro nenachází, jednotky budou v útočném režimu držet hlídku.
|
||||||
block.draug-factory.description = Vyrábí těžící drony Dragoun.
|
block.draug-factory.description = Vyrábí těžící drony Dragoun.
|
||||||
block.spirit-factory.description = Vyrábí drony, které opravují budovy.
|
block.spirit-factory.description = Vyrábí drony, které opravují budovy.
|
||||||
block.phantom-factory.description = Vyrábí drony s pokročilou konstrukcí.
|
block.phantom-factory.description = Vyrábí drony s pokročilou konstrukcí.
|
||||||
@@ -1249,3 +1254,4 @@ block.omega-mech-pad.description = Umožňuje přeměnu Tvého vozidla na těžc
|
|||||||
block.javelin-ship-pad.description = Umožňuje přeměnu Tvého vozidla na rychlou, lehce obrněnou stíhačku.\nAktivuj kliknutím nebo ťupnutím, když se nacházíš nad plošinou.
|
block.javelin-ship-pad.description = Umožňuje přeměnu Tvého vozidla na rychlou, lehce obrněnou stíhačku.\nAktivuj kliknutím nebo ťupnutím, když se nacházíš nad plošinou.
|
||||||
block.trident-ship-pad.description = Umožňuje přeměnu Tvého vozidla na těžkého podpůrného bombardéra.\nAktivuj kliknutím nebo ťupnutím, když se nacházíš nad plošinou.
|
block.trident-ship-pad.description = Umožňuje přeměnu Tvého vozidla na těžkého podpůrného bombardéra.\nAktivuj kliknutím nebo ťupnutím, když se nacházíš nad plošinou.
|
||||||
block.glaive-ship-pad.description = Umožňuje přeměnu Tvého vozidla na velkou, dobře obrněnou střeleckou loď.\nAktivuj kliknutím nebo ťupnutím, když se nacházíš nad plošinou.
|
block.glaive-ship-pad.description = Umožňuje přeměnu Tvého vozidla na velkou, dobře obrněnou střeleckou loď.\nAktivuj kliknutím nebo ťupnutím, když se nacházíš nad plošinou.
|
||||||
|
|
||||||
@@ -141,14 +141,14 @@ players = {0} Spieler online
|
|||||||
players.single = {0} Spieler online
|
players.single = {0} Spieler online
|
||||||
server.closing = [accent]Schließe den Server ...
|
server.closing = [accent]Schließe den Server ...
|
||||||
server.kicked.kick = Du wurdest vom Server gekickt!
|
server.kicked.kick = Du wurdest vom Server gekickt!
|
||||||
server.kicked.whitelist = Du bist nicht auf der Whitelist.
|
server.kicked.whitelist = Du bist hier nicht auf der Whitelist.
|
||||||
server.kicked.serverClose = Server geschlossen.
|
server.kicked.serverClose = Server geschlossen.
|
||||||
server.kicked.vote = Es wurde abgestimmt, dich zu kicken. Tschüss.
|
server.kicked.vote = Es wurde abgestimmt, dich zu kicken. Tschüss.
|
||||||
server.kicked.clientOutdated = Veralteter Client! Aktualisiere dein Spiel!
|
server.kicked.clientOutdated = Veralteter Client! Aktualisiere dein Spiel!
|
||||||
server.kicked.serverOutdated = Veralteter Server! Bitte den Host um ein Update!
|
server.kicked.serverOutdated = Veralteter Server! Bitte den Host um ein Update!
|
||||||
server.kicked.banned = Du wurdest vom Server verbannt.
|
server.kicked.banned = Du wurdest vom Server verbannt.
|
||||||
server.kicked.typeMismatch = Der Server ist nicht mit deinem Versionstyp kompatibel.
|
server.kicked.typeMismatch = Der Server ist nicht mit deinem Versionstyp kompatibel.
|
||||||
server.kicked.playerLimit = Der Server ist voll.\nWarte für einen freien Platz.
|
server.kicked.playerLimit = Dieser Server ist voll. Warte auf einen freien Platz.
|
||||||
server.kicked.recentKick = Du wurdest gerade gekickt.\nWarte bevor du dich wieder verbindest.
|
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.nameInUse = Es ist bereits ein Spieler \nmit diesem Namen auf dem Server.
|
||||||
server.kicked.nameEmpty = Dein Name muss mindestens einen Buchstaben oder eine Zahl enthalten.
|
server.kicked.nameEmpty = Dein Name muss mindestens einen Buchstaben oder eine Zahl enthalten.
|
||||||
@@ -190,7 +190,7 @@ server.version = [lightgray]Version: {0}
|
|||||||
server.custombuild = [yellow]Benutzerdefinierter Build
|
server.custombuild = [yellow]Benutzerdefinierter Build
|
||||||
confirmban = Bist du sicher, dass du diesen Spieler verbannen möchtest?
|
confirmban = Bist du sicher, dass du diesen Spieler verbannen möchtest?
|
||||||
confirmkick = Bist du sicher, dass du diesen Spieler kicken willst?
|
confirmkick = Bist du sicher, dass du diesen Spieler kicken willst?
|
||||||
confirmvotekick = Willst du wirklich eine Abstimmung zum kicken des spielers machen?
|
confirmvotekick = Bist du sicher diesen Spieler mit einer Abstimmung rauszuwerfen?
|
||||||
confirmunban = Bist du sicher, dass du die Verbannung des Spielers rückgängig machen 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?
|
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?
|
confirmunadmin = Bis du sicher, dass dieser Spieler kein Admin mehr sein soll?
|
||||||
@@ -199,9 +199,9 @@ joingame.ip = IP:
|
|||||||
disconnect = Verbindung unterbrochen.
|
disconnect = Verbindung unterbrochen.
|
||||||
disconnect.error = Verbindungsfehler.
|
disconnect.error = Verbindungsfehler.
|
||||||
disconnect.closed = Verbindung geschlossen.
|
disconnect.closed = Verbindung geschlossen.
|
||||||
disconnect.timeout = Zu langer Verbindungsversuch.
|
disconnect.timeout = Zeit Überschreitung.
|
||||||
disconnect.data = Fehler beim Laden der Welt!
|
disconnect.data = Fehler beim Laden der Welt!
|
||||||
cantconnect = Unfähig, dem Spiel beizutreten ([accent]{0}[]).
|
cantconnect = Nicht möglich beizutreten ([accent]{0}[]).
|
||||||
connecting = [accent] Verbinde...
|
connecting = [accent] Verbinde...
|
||||||
connecting.data = [accent] Welt wird geladen...
|
connecting.data = [accent] Welt wird geladen...
|
||||||
server.port = Port:
|
server.port = Port:
|
||||||
@@ -226,7 +226,7 @@ save.rename = Umbenennen
|
|||||||
save.rename.text = Neuer Name
|
save.rename.text = Neuer Name
|
||||||
selectslot = Wähle einen Spielstand
|
selectslot = Wähle einen Spielstand
|
||||||
slot = [accent] Platz {0}
|
slot = [accent] Platz {0}
|
||||||
editmessage = Edit Message
|
editmessage = Nachricht bearbeiten
|
||||||
save.corrupted = [accent] Datei beschädigt oder ungültig!
|
save.corrupted = [accent] Datei beschädigt oder ungültig!
|
||||||
empty = <leer>
|
empty = <leer>
|
||||||
on = An
|
on = An
|
||||||
@@ -392,7 +392,7 @@ filters.empty = [LIGHT_GRAY]Keine Filter! Füge einen mit dem unteren Knopf hinz
|
|||||||
filter.distort = Verzerren
|
filter.distort = Verzerren
|
||||||
filter.noise = Rauschen
|
filter.noise = Rauschen
|
||||||
filter.median = Mittelwert
|
filter.median = Mittelwert
|
||||||
filter.oremedian = Ore Median
|
filter.oremedian =Erz Median
|
||||||
filter.blend = Mischen
|
filter.blend = Mischen
|
||||||
filter.defaultores = Standard Erze
|
filter.defaultores = Standard Erze
|
||||||
filter.ore = Erz
|
filter.ore = Erz
|
||||||
@@ -569,11 +569,11 @@ bar.drillspeed = Bohrgeschwindigkeit: {0}/s
|
|||||||
bar.pumpspeed = Pump Speed: {0}/s
|
bar.pumpspeed = Pump Speed: {0}/s
|
||||||
bar.efficiency = Effizienz: {0}%
|
bar.efficiency = Effizienz: {0}%
|
||||||
bar.powerbalance = Strom: {0}
|
bar.powerbalance = Strom: {0}
|
||||||
bar.powerstored = Stored: {0}/{1}
|
bar.powerstored = Gespeichert: {0}/{1}
|
||||||
bar.poweramount = Strom: {0}
|
bar.poweramount = Strom: {0}
|
||||||
bar.poweroutput = Stromgeneration: {0}
|
bar.poweroutput = Stromgeneration: {0}
|
||||||
bar.items = Items: {0}
|
bar.items = Items: {0}
|
||||||
bar.capacity = Capacity: {0}
|
bar.capacity = Kapazität: {0}
|
||||||
bar.liquid = Flüssigkeit
|
bar.liquid = Flüssigkeit
|
||||||
bar.heat = Hitze
|
bar.heat = Hitze
|
||||||
bar.power = Strom
|
bar.power = Strom
|
||||||
@@ -800,7 +800,7 @@ item.pyratite.name = Pyratit
|
|||||||
item.metaglass.name = Metaglass
|
item.metaglass.name = Metaglass
|
||||||
item.scrap.name = Schrott
|
item.scrap.name = Schrott
|
||||||
liquid.water.name = Wasser
|
liquid.water.name = Wasser
|
||||||
liquid.slag.name = Schlacke
|
liquid.slag.name = Lava
|
||||||
liquid.oil.name = Öl
|
liquid.oil.name = Öl
|
||||||
liquid.cryofluid.name = Kryoflüssigkeit
|
liquid.cryofluid.name = Kryoflüssigkeit
|
||||||
mech.alpha-mech.name = Alpha
|
mech.alpha-mech.name = Alpha
|
||||||
@@ -820,7 +820,7 @@ mech.dart-ship.weapon = Mehrlader
|
|||||||
mech.javelin-ship.name = Javelin
|
mech.javelin-ship.name = Javelin
|
||||||
mech.javelin-ship.weapon = Raketensalve
|
mech.javelin-ship.weapon = Raketensalve
|
||||||
mech.javelin-ship.ability = Statische Entladung
|
mech.javelin-ship.ability = Statische Entladung
|
||||||
mech.trident-ship.name = Trident
|
mech.trident-ship.name = Dreizack
|
||||||
mech.trident-ship.weapon = Bombenschacht
|
mech.trident-ship.weapon = Bombenschacht
|
||||||
mech.glaive-ship.name = Glaive
|
mech.glaive-ship.name = Glaive
|
||||||
mech.glaive-ship.weapon = Flammen-Mehrlader
|
mech.glaive-ship.weapon = Flammen-Mehrlader
|
||||||
@@ -924,9 +924,9 @@ block.thorium-wall.name = Thorium-Mauer
|
|||||||
block.thorium-wall-large.name = Große Thorium-Mauer
|
block.thorium-wall-large.name = Große Thorium-Mauer
|
||||||
block.door.name = Tor
|
block.door.name = Tor
|
||||||
block.door-large.name = Großes Tor
|
block.door-large.name = Großes Tor
|
||||||
block.duo.name = Duo
|
block.duo.name = Doppelgeschütz
|
||||||
block.scorch.name = Flammenwerfer
|
block.scorch.name = Scatter
|
||||||
block.scatter.name = Scatter
|
block.scatter.name = Luftgeschütz
|
||||||
block.hail.name = Streuer
|
block.hail.name = Streuer
|
||||||
block.lancer.name = Lanzer
|
block.lancer.name = Lanzer
|
||||||
block.conveyor.name = Förderband
|
block.conveyor.name = Förderband
|
||||||
@@ -938,7 +938,7 @@ block.router.name = Verteiler
|
|||||||
block.distributor.name = Großer Verteiler
|
block.distributor.name = Großer Verteiler
|
||||||
block.sorter.name = Sortierer
|
block.sorter.name = Sortierer
|
||||||
block.inverted-sorter.name = Inverted Sorter
|
block.inverted-sorter.name = Inverted Sorter
|
||||||
block.message.name = Message
|
block.message.name = Nachricht
|
||||||
block.illuminator.name = Illuminator
|
block.illuminator.name = Illuminator
|
||||||
block.illuminator.description = A small, compact, configurable light source. Requires power to function.
|
block.illuminator.description = A small, compact, configurable light source. Requires power to function.
|
||||||
block.overflow-gate.name = Überlauftor
|
block.overflow-gate.name = Überlauftor
|
||||||
@@ -969,7 +969,7 @@ block.cultivator.name = Kultivierer
|
|||||||
block.dart-mech-pad.name = Dart Mech-Pad
|
block.dart-mech-pad.name = Dart Mech-Pad
|
||||||
block.delta-mech-pad.name = Delta Mech-Pad
|
block.delta-mech-pad.name = Delta Mech-Pad
|
||||||
block.javelin-ship-pad.name = Javelin Luftschiff-Pad
|
block.javelin-ship-pad.name = Javelin Luftschiff-Pad
|
||||||
block.trident-ship-pad.name = Trident Luftschiff-Pad
|
block.trident-ship-pad.name = Dreizack Luftschiff-Pad
|
||||||
block.glaive-ship-pad.name = Glaive Luftschiff-Pad
|
block.glaive-ship-pad.name = Glaive Luftschiff-Pad
|
||||||
block.omega-mech-pad.name = Omega Mech-Pad
|
block.omega-mech-pad.name = Omega Mech-Pad
|
||||||
block.tau-mech-pad.name = Tau Mech-Pad
|
block.tau-mech-pad.name = Tau Mech-Pad
|
||||||
@@ -986,7 +986,7 @@ block.vault.name = Tresor
|
|||||||
block.wave.name = Welle
|
block.wave.name = Welle
|
||||||
block.swarmer.name = Schwärmer
|
block.swarmer.name = Schwärmer
|
||||||
block.salvo.name = Salve
|
block.salvo.name = Salve
|
||||||
block.ripple.name = Riffel
|
block.ripple.name = Zerstörer
|
||||||
block.phase-conveyor.name = Phasen-Transportband
|
block.phase-conveyor.name = Phasen-Transportband
|
||||||
block.bridge-conveyor.name = Brücken-Transportband
|
block.bridge-conveyor.name = Brücken-Transportband
|
||||||
block.plastanium-compressor.name = Plastanium-Verdichter
|
block.plastanium-compressor.name = Plastanium-Verdichter
|
||||||
@@ -995,7 +995,7 @@ block.blast-mixer.name = Sprengmixer
|
|||||||
block.solar-panel.name = Solarpanel
|
block.solar-panel.name = Solarpanel
|
||||||
block.solar-panel-large.name = Großes Solarpanel
|
block.solar-panel-large.name = Großes Solarpanel
|
||||||
block.oil-extractor.name = Öl-Extraktor
|
block.oil-extractor.name = Öl-Extraktor
|
||||||
block.command-center.name = Command Center
|
block.command-center.name = Kommandozentrum
|
||||||
block.draug-factory.name = Draug Miner-Drohnenfactory
|
block.draug-factory.name = Draug Miner-Drohnenfactory
|
||||||
block.spirit-factory.name = Spirit-Drohnenfabrik
|
block.spirit-factory.name = Spirit-Drohnenfabrik
|
||||||
block.phantom-factory.name = Phantom-Drohnenfabrik
|
block.phantom-factory.name = Phantom-Drohnenfabrik
|
||||||
@@ -1010,7 +1010,7 @@ block.repair-point.name = Reparaturpunkt
|
|||||||
block.pulse-conduit.name = Impulskanal
|
block.pulse-conduit.name = Impulskanal
|
||||||
block.plated-conduit.name = Plated Conduit
|
block.plated-conduit.name = Plated Conduit
|
||||||
block.phase-conduit.name = Phasenkanal
|
block.phase-conduit.name = Phasenkanal
|
||||||
block.liquid-router.name = Flüssigkeits-Router
|
block.liquid-router.name = Flüssigkeits-Verteiler
|
||||||
block.liquid-tank.name = Flüssigkeitstank
|
block.liquid-tank.name = Flüssigkeitstank
|
||||||
block.liquid-junction.name = Flüssigkeits-Kreuzung
|
block.liquid-junction.name = Flüssigkeits-Kreuzung
|
||||||
block.bridge-conduit.name = Kanalbrücke
|
block.bridge-conduit.name = Kanalbrücke
|
||||||
@@ -1120,7 +1120,7 @@ unit.eruptor.description = Ein schwerer Mech, der Strukturen abbaut. Feuert eine
|
|||||||
unit.wraith.description = Eine schneller Abfangjäger.
|
unit.wraith.description = Eine schneller Abfangjäger.
|
||||||
unit.ghoul.description = Ein schwerer Flächenbomber.
|
unit.ghoul.description = Ein schwerer Flächenbomber.
|
||||||
unit.revenant.description = Eine schwere, schwebende Raketengruppe.
|
unit.revenant.description = Eine schwere, schwebende Raketengruppe.
|
||||||
block.message.description = Stores a message. Used for communication between allies.
|
block.message.description = Benutzt um Nachrichten mit Verbündeten auszutauschen.
|
||||||
block.graphite-press.description = Komprimiert Kohlestücke zu reinen Graphitplatten.
|
block.graphite-press.description = Komprimiert Kohlestücke zu reinen Graphitplatten.
|
||||||
block.multi-press.description = Eine aktualisierte Version der Graphitpresse. Setzt Wasser und Strom ein, um Kohle schnell und effizient zu verarbeiten.
|
block.multi-press.description = Eine aktualisierte Version der Graphitpresse. Setzt Wasser und Strom ein, um Kohle schnell und effizient zu verarbeiten.
|
||||||
block.silicon-smelter.description = Reduziert Sand mit hochreinem Kohlenstoff, um Silizium zu produzieren.
|
block.silicon-smelter.description = Reduziert Sand mit hochreinem Kohlenstoff, um Silizium zu produzieren.
|
||||||
@@ -1236,7 +1236,7 @@ block.wraith-factory.description = Produziert schnelle Abfangjäger.
|
|||||||
block.ghoul-factory.description = Produziert schwere Flächenbomber.
|
block.ghoul-factory.description = Produziert schwere Flächenbomber.
|
||||||
block.revenant-factory.description = Produziert schwere Raketen basierte Flugeinheiten.
|
block.revenant-factory.description = Produziert schwere Raketen basierte Flugeinheiten.
|
||||||
block.dagger-factory.description = Produziert Standard-Bodeneinheiten.
|
block.dagger-factory.description = Produziert Standard-Bodeneinheiten.
|
||||||
block.crawler-factory.description = Produziert schnelle selbstzerstörende Schwarmeinheiten.
|
block.crawler-factory.description = Produziert schnelle, selbstzerstörende Schwarmeinheiten.
|
||||||
block.titan-factory.description = Produziert fortgeschrittene, gepanzerte Bodeneinheiten.
|
block.titan-factory.description = Produziert fortgeschrittene, gepanzerte Bodeneinheiten.
|
||||||
block.fortress-factory.description = Produziert schwere Artillerie-Bodeneinheiten.
|
block.fortress-factory.description = Produziert schwere Artillerie-Bodeneinheiten.
|
||||||
block.repair-point.description = Heilt durchgehend die nächste befreundete, beschädigte Einheit in der Umgebung.
|
block.repair-point.description = Heilt durchgehend die nächste befreundete, beschädigte Einheit in der Umgebung.
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ credits = Tekijät
|
|||||||
contributors = Kääntäjät ja avustajat
|
contributors = Kääntäjät ja avustajat
|
||||||
discord = Liity Mindustryn Discordiin!
|
discord = Liity Mindustryn Discordiin!
|
||||||
link.discord.description = Mindustryn virallinen Discord-keskusteluhuone
|
link.discord.description = Mindustryn virallinen Discord-keskusteluhuone
|
||||||
link.reddit.description = The Mindustry subreddit
|
link.reddit.description = Mindustry subreddit
|
||||||
link.github.description = Pelin lähdekoodi
|
link.github.description = Pelin lähdekoodi
|
||||||
link.changelog.description = Lista päivityksien muutoksista
|
link.changelog.description = Lista päivityksien muutoksista
|
||||||
link.dev-builds.description = Epävakaat kehitysversiot
|
link.dev-builds.description = Epävakaat kehitysversiot
|
||||||
@@ -19,47 +19,47 @@ screenshot.invalid = Kartta liian laaja, kuvankaappaukselle ei mahdollisesti ole
|
|||||||
gameover = Peli ohi
|
gameover = Peli ohi
|
||||||
gameover.pvp = [accent] {0}[] joukkue voittaa!
|
gameover.pvp = [accent] {0}[] joukkue voittaa!
|
||||||
highscore = [accent]Uusi ennätys!
|
highscore = [accent]Uusi ennätys!
|
||||||
copied = Copied.
|
copied = Kopioitu.
|
||||||
|
|
||||||
load.sound = Sounds
|
load.sound = Ääni
|
||||||
load.map = Maps
|
load.map = Kartat
|
||||||
load.image = Images
|
load.image = Kuvat
|
||||||
load.content = Content
|
load.content = Sisältö
|
||||||
load.system = System
|
load.system = Systeemi
|
||||||
load.mod = Mods
|
load.mod = Modit
|
||||||
load.scripts = Scripts
|
load.scripts = Skriptit
|
||||||
|
|
||||||
be.update = A new Bleeding Edge build is available:
|
be.update = A new Bleeding Edge build is available:
|
||||||
be.update.confirm = Download it and restart now?
|
be.update.confirm = Lataa se ja käynnistä peli uudelleen?
|
||||||
be.updating = Updating...
|
be.updating = Päivitetään...
|
||||||
be.ignore = Ignore
|
be.ignore = Sivuuta
|
||||||
be.noupdates = No updates found.
|
be.noupdates = Ei päivityksiä saatavilla.
|
||||||
be.check = Check for updates
|
be.check = Tarkista päivityksiä
|
||||||
|
|
||||||
schematic = Schematic
|
schematic = Kaavio
|
||||||
schematic.add = Save Schematic...
|
schematic.add = Tallenna Kaavio...
|
||||||
schematics = Schematics
|
schematics = Kaaviot
|
||||||
schematic.replace = A schematic by that name already exists. Replace it?
|
schematic.replace = Kaavio tällä nimellä on jo olemassa. Haluatko korvata sen?
|
||||||
schematic.import = Import Schematic...
|
schematic.import = Tuo Kaavio...
|
||||||
schematic.exportfile = Export File
|
schematic.exportfile = Luo Tiedosto
|
||||||
schematic.importfile = Import File
|
schematic.importfile = Tuo Tiedosto
|
||||||
schematic.browseworkshop = Browse Workshop
|
schematic.browseworkshop = Selaa Työpajaa
|
||||||
schematic.copy = Copy to Clipboard
|
schematic.copy = Kopioi Leikepöydälle
|
||||||
schematic.copy.import = Import from Clipboard
|
schematic.copy.import = Tou Leikepöydälle
|
||||||
schematic.shareworkshop = Share on Workshop
|
schematic.shareworkshop = Jaa työpajaan
|
||||||
schematic.flip = [accent][[{0}][]/[accent][[{1}][]: Flip Schematic
|
schematic.flip = [accent][[{0}][]/[accent][[{1}][]: Käännä Kaavio
|
||||||
schematic.saved = Schematic saved.
|
schematic.saved = Kaavio tallennettu.
|
||||||
schematic.delete.confirm = This schematic will be utterly eradicated.
|
schematic.delete.confirm = Tämä kaavio poistetaan.
|
||||||
schematic.rename = Rename Schematic
|
schematic.rename = Uudelleennimeä Kaavio
|
||||||
schematic.info = {0}x{1}, {2} blocks
|
schematic.info = {0}x{1}, {2} palikkaa
|
||||||
|
|
||||||
stat.wave = Aaltoja voitettu:[accent] {0}
|
stat.wave = Tasoja voitettu:[accent] {0}
|
||||||
stat.enemiesDestroyed = Vihollisia tuhottu:[accent] {0}
|
stat.enemiesDestroyed = Vihollisia tuhottu:[accent] {0}
|
||||||
stat.built = Rakennuksia rakennettu:[accent] {0}
|
stat.built = Rakennuksia rakennettu:[accent] {0}
|
||||||
stat.destroyed = Rakennuksia tuhottu:[accent] {0}
|
stat.destroyed = Rakennuksia tuhottu:[accent] {0}
|
||||||
stat.deconstructed = Rakennuksia purettu:[accent] {0}
|
stat.deconstructed = Rakennuksia purettu:[accent] {0}
|
||||||
stat.delivered = Resursseja laukaistu:
|
stat.delivered = Resursseja laukaistu:
|
||||||
stat.rank = Lopullinen arvo: [accent]{0}
|
stat.rank = Lopullinen arvosana: [accent]{0}
|
||||||
|
|
||||||
launcheditems = [accent]Laukaistut tavarat
|
launcheditems = [accent]Laukaistut tavarat
|
||||||
launchinfo = [unlaunched][[LAUNCH] your core to obtain the items indicated in blue.
|
launchinfo = [unlaunched][[LAUNCH] your core to obtain the items indicated in blue.
|
||||||
@@ -78,49 +78,49 @@ customgame = Mukautettu peli
|
|||||||
newgame = Uusi peli
|
newgame = Uusi peli
|
||||||
none = <ei mitään>
|
none = <ei mitään>
|
||||||
minimap = Pienoiskartta
|
minimap = Pienoiskartta
|
||||||
position = Position
|
position = Sijainti
|
||||||
close = Sulje
|
close = Sulje
|
||||||
website = Verkkosivu
|
website = Verkkosivu
|
||||||
quit = Poistu
|
quit = Poistu
|
||||||
save.quit = Save & Quit
|
save.quit = Tallenna ja Poistu
|
||||||
maps = Kartat
|
maps = Kartat
|
||||||
maps.browse = Browse Maps
|
maps.browse = Selaa Karttoja
|
||||||
continue = Jatka
|
continue = Jatka
|
||||||
maps.none = [lightgray]Karttoja ei löytynyt!
|
maps.none = [lightgray]Karttoja ei löytynyt!
|
||||||
invalid = Invalid
|
invalid = Invalidi
|
||||||
pickcolor = Pick Color
|
pickcolor = Valitse väri
|
||||||
preparingconfig = Preparing Config
|
preparingconfig = Preparing Config
|
||||||
preparingcontent = Preparing Content
|
preparingcontent = Preparing Content
|
||||||
uploadingcontent = Uploading Content
|
uploadingcontent = Uploading Content
|
||||||
uploadingpreviewfile = Uploading Preview File
|
uploadingpreviewfile = Uploading Preview File
|
||||||
committingchanges = Comitting Changes
|
committingchanges = Comitting Changes
|
||||||
done = Done
|
done = Valmis
|
||||||
feature.unsupported = Your device does not support this feature.
|
feature.unsupported = Sinun laitteesi ei tue tätä toimintoa.
|
||||||
|
|
||||||
mods.alphainfo = Keep in mind that mods are in alpha, and[scarlet] may be very buggy[].\nReport any issues you find to the Mindustry GitHub or Discord.
|
mods.alphainfo = Pidä mielessä että modit ovat alpha-tilassa, ja[scarlet] ne voivat olla virheellisiä[].\nRaportoi kaikki virheet Mindustry GitHub-sivuille tai Discordiin.
|
||||||
mods.alpha = [accent](Alpha)
|
mods.alpha = [accent](Alpha)
|
||||||
mods = Mods
|
mods = Modit
|
||||||
mods.none = [LIGHT_GRAY]No mods found!
|
mods.none = [LIGHT_GRAY]Modeja ei löytynyt!
|
||||||
mods.guide = Modding Guide
|
mods.guide = Modaamisen opas
|
||||||
mods.report = Report Bug
|
mods.report = Raportoi ohjelmistovirhe
|
||||||
mods.openfolder = Open Mod Folder
|
mods.openfolder = Avaa Modikansio
|
||||||
mod.enabled = [lightgray]Enabled
|
mod.enabled = [lightgray]Käytössä
|
||||||
mod.disabled = [scarlet]Disabled
|
mod.disabled = [scarlet]Epäkäytössä
|
||||||
mod.disable = Disable
|
mod.disable = Laita pois päältä
|
||||||
mod.delete.error = Unable to delete mod. File may be in use.
|
mod.delete.error = Modia ei pystynyt poistamaan. Tiedosto voi olla käytössä.
|
||||||
mod.requiresversion = [scarlet]Requires min game version: [accent]{0}
|
mod.requiresversion = [scarlet]Tarvitsee vähintää pelin version: [accent]{0}
|
||||||
mod.missingdependencies = [scarlet]Missing dependencies: {0}
|
mod.missingdependencies = [scarlet]Tarvitsee nämä modit: {0}
|
||||||
mod.erroredcontent = [scarlet]Content Errors
|
mod.erroredcontent = [scarlet]Sisältö Virheet
|
||||||
mod.errors = Errors have occurred loading content.
|
mod.errors = Virheitä on tapahtunut pelin ladatessa.
|
||||||
mod.noerrorplay = [scarlet]You have mods with errors.[] Either disable the affected mods or fix the errors before playing.
|
mod.noerrorplay = [scarlet]Sinulla on virheellisiä modeja.[] Joko poista ne käytöstä tai korjaa virheet.
|
||||||
mod.nowdisabled = [scarlet]Mod '{0}' is missing dependencies:[accent] {1}\n[lightgray]These mods need to be downloaded first.\nThis mod will be automatically disabled.
|
mod.nowdisabled = [scarlet]Mod '{0}' is missing dependencies:[accent] {1}\n[lightgray]These mods need to be downloaded first.\nThis mod will be automatically disabled.
|
||||||
mod.enable = Enable
|
mod.enable = Käytä
|
||||||
mod.requiresrestart = The game will now close to apply the mod changes.
|
mod.requiresrestart = Peli suljetaan jotta muutokset voisivat toteutua.
|
||||||
mod.reloadrequired = [scarlet]Reload Required
|
mod.reloadrequired = [scarlet]Vaatii Uudelleenkäynnistystä
|
||||||
mod.import = Import Mod
|
mod.import = Tuo Modi
|
||||||
mod.import.github = Import GitHub Mod
|
mod.import.github = Import GitHub Mod
|
||||||
mod.item.remove = This item is part of the[accent] '{0}'[] mod. To remove it, uninstall that mod.
|
mod.item.remove = This item is part of the[accent] '{0}'[] mod. To remove it, uninstall that mod.
|
||||||
mod.remove.confirm = This mod will be deleted.
|
mod.remove.confirm = Tämä modi poistetaan.
|
||||||
mod.author = [LIGHT_GRAY]Author:[] {0}
|
mod.author = [LIGHT_GRAY]Author:[] {0}
|
||||||
mod.missing = This save contains mods that you have recently updated or no longer have installed. Save corruption may occur. Are you sure you want to load it?\n[lightgray]Mods:\n{0}
|
mod.missing = This save contains mods that you have recently updated or no longer have installed. Save corruption may occur. Are you sure you want to load it?\n[lightgray]Mods:\n{0}
|
||||||
mod.preview.missing = Before publishing this mod in the workshop, you must add an image preview.\nPlace an image named[accent] preview.png[] into the mod's folder and try again.
|
mod.preview.missing = Before publishing this mod in the workshop, you must add an image preview.\nPlace an image named[accent] preview.png[] into the mod's folder and try again.
|
||||||
@@ -143,34 +143,34 @@ server.closing = [accent]Suljetaan palvelinta...
|
|||||||
server.kicked.kick = Sinut on potkittu palvelimelta!
|
server.kicked.kick = Sinut on potkittu palvelimelta!
|
||||||
server.kicked.whitelist = You are not whitelisted here.
|
server.kicked.whitelist = You are not whitelisted here.
|
||||||
server.kicked.serverClose = Palvelin suljettu.
|
server.kicked.serverClose = Palvelin suljettu.
|
||||||
server.kicked.vote = You have been vote-kicked. Goodbye.
|
server.kicked.vote = Sinut on äänetetty pois. Hyvästi.
|
||||||
server.kicked.clientOutdated = Pelisi on vanhentunut! Päivitä se!
|
server.kicked.clientOutdated = Pelisi on vanhentunut! Päivitä se!
|
||||||
server.kicked.serverOutdated = Outdated server! Ask the host to update!
|
server.kicked.serverOutdated = Vanhentunut palvelin! Pyydä isäntää päivittämään!
|
||||||
server.kicked.banned = Sinulla on portikielto tälle palvelimelle.
|
server.kicked.banned = Sinulla on portikielto tälle palvelimelle.
|
||||||
server.kicked.typeMismatch = This server is not compatible with your build type.
|
server.kicked.typeMismatch = This server is not compatible with your build type.
|
||||||
server.kicked.playerLimit = This server is full. Wait for an empty slot.
|
server.kicked.playerLimit = Tämä palvelin on täynnä. Odota vapaata tilaa.
|
||||||
server.kicked.recentKick = Sinut on potkittu äskettäin.\nOdota ennen kuin yhdistät uudestaan.
|
server.kicked.recentKick = Sinut on potkittu äskettäin.\nOdota ennen kuin yhdistät uudestaan.
|
||||||
server.kicked.nameInUse = Joku tuon niminen\non jo tällä palvelimella.
|
server.kicked.nameInUse = Joku tuon niminen\non jo tällä palvelimella.
|
||||||
server.kicked.nameEmpty = Valitsemasi nimi on virheellinen.
|
server.kicked.nameEmpty = Valitsemasi nimi on virheellinen.
|
||||||
server.kicked.idInUse = Olet jo tällä palvelimella! Kahdella käyttäjällä yhdistäminen ei ole sallittua.
|
server.kicked.idInUse = Olet jo tällä palvelimella! Kahdella käyttäjällä yhdistäminen ei ole sallittua.
|
||||||
server.kicked.customClient = Tämä palvelin ei tue muokattuja versioita. Lataa virallinen versio.
|
server.kicked.customClient = Tämä palvelin ei tue muokattuja versioita. Lataa virallinen versio.
|
||||||
server.kicked.gameover = Peli ohi!
|
server.kicked.gameover = Peli ohi!
|
||||||
server.kicked.serverRestarting = The server is restarting.
|
server.kicked.serverRestarting = Tämä palvelin on uudelleenkäynnistymässä.
|
||||||
server.versions = Versiosi:[accent] {0}[]\nPalvelimen versio:[accent] {1}[]
|
server.versions = Versiosi:[accent] {0}[]\nPalvelimen versio:[accent] {1}[]
|
||||||
host.info = The [accent]host[] button hosts a server on port [scarlet]6567[]. \nAnybody on the same [lightgray]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[lightgray]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. Note that public networks sometimes do not allow server discovery.
|
host.info = The [accent]host[] button hosts a server on port [scarlet]6567[]. \nAnybody on the same [lightgray]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[lightgray]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. Note that public networks sometimes do not allow server discovery.
|
||||||
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[lightgray]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.
|
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[lightgray]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 Multiplayer Game
|
hostserver = Pidä yllä monipelaaja peliä
|
||||||
invitefriends = Invite Friends
|
invitefriends = Pyydä Ystäviä
|
||||||
hostserver.mobile = Host\nGame
|
hostserver.mobile = Host\nGame
|
||||||
host = Host
|
host = Host
|
||||||
hosting = [accent]Avataan palvelinta...
|
hosting = [accent]Avataan palvelinta...
|
||||||
hosts.refresh = Päivitä
|
hosts.refresh = Päivitä
|
||||||
hosts.discovering = Discovering LAN games
|
hosts.discovering = Etsitään LAN pelejä
|
||||||
hosts.discovering.any = Discovering games
|
hosts.discovering.any = Etsitään Pelejä
|
||||||
server.refreshing = Päivitetään palvelimen tietoja
|
server.refreshing = Päivitetään palvelimen tietoja
|
||||||
hosts.none = [lightgray]No local games found!
|
hosts.none = [lightgray]Paikallisia pelejä ei löytynyt!
|
||||||
host.invalid = [scarlet]Can't connect to host.
|
host.invalid = [scarlet]Isäntään ei voitu yhdistää.
|
||||||
trace = Trace Player
|
trace = Seuraa Pelaajaa
|
||||||
trace.playername = Pelaajanimi: [accent]{0}
|
trace.playername = Pelaajanimi: [accent]{0}
|
||||||
trace.ip = IP-osoite: [accent]{0}
|
trace.ip = IP-osoite: [accent]{0}
|
||||||
trace.id = Uniikki tunniste: [accent]{0}
|
trace.id = Uniikki tunniste: [accent]{0}
|
||||||
@@ -188,17 +188,17 @@ server.outdated = [crimson]Vanhentunut palvelin![]
|
|||||||
server.outdated.client = [crimson]Vanhentunut asiakasohjelma![]
|
server.outdated.client = [crimson]Vanhentunut asiakasohjelma![]
|
||||||
server.version = [gray]v{0} {1}
|
server.version = [gray]v{0} {1}
|
||||||
server.custombuild = [yellow]Custom Build
|
server.custombuild = [yellow]Custom Build
|
||||||
confirmban = Are you sure you want to ban this player?
|
confirmban = Oletko varma että haluat potkia tämän pelaajan?
|
||||||
confirmkick = Are you sure you want to kick this player?
|
confirmkick = Oletko varma että haluat poistaa tämän pelaajan?
|
||||||
confirmvotekick = Are you sure you want to vote-kick this player?
|
confirmvotekick = Oletko varma että haluat äänestää tämän pelaajan potkituksi?
|
||||||
confirmunban = Are you sure you want to unban this player?
|
confirmunban = Oletko varma että haluat päästää tämän pelaajan takaisin?
|
||||||
confirmadmin = Are you sure you want to make this player an admin?
|
confirmadmin = Oletko varma että haluat antaa pelaajalle hallinto-oikeuksia?
|
||||||
confirmunadmin = Are you sure you want to remove admin status from this player?
|
confirmunadmin = Are you sure you want to remove admin status from this player?
|
||||||
joingame.title = Liity peliin
|
joingame.title = Liity peliin
|
||||||
joingame.ip = Osoite:
|
joingame.ip = Osoite:
|
||||||
disconnect = Disconnected.
|
disconnect = Yhteys katkaistu.
|
||||||
disconnect.error = Connection error.
|
disconnect.error = Yhteysvirhe.
|
||||||
disconnect.closed = Connection closed.
|
disconnect.closed = Yhteys poistettu.
|
||||||
disconnect.timeout = Timed out.
|
disconnect.timeout = Timed out.
|
||||||
disconnect.data = Failed to load world data!
|
disconnect.data = Failed to load world data!
|
||||||
cantconnect = Unable to join game ([accent]{0}[]).
|
cantconnect = Unable to join game ([accent]{0}[]).
|
||||||
@@ -212,7 +212,7 @@ save.new = New Save
|
|||||||
save.overwrite = Are you sure you want to overwrite\nthis save slot?
|
save.overwrite = Are you sure you want to overwrite\nthis save slot?
|
||||||
overwrite = Overwrite
|
overwrite = Overwrite
|
||||||
save.none = No saves found!
|
save.none = No saves found!
|
||||||
saveload = Saving...
|
saveload = Tallennetaan...
|
||||||
savefail = Failed to save game!
|
savefail = Failed to save game!
|
||||||
save.delete.confirm = Are you sure you want to delete this save?
|
save.delete.confirm = Are you sure you want to delete this save?
|
||||||
save.delete = Delete
|
save.delete = Delete
|
||||||
@@ -220,7 +220,7 @@ save.export = Export Save
|
|||||||
save.import.invalid = [accent]This save is invalid!
|
save.import.invalid = [accent]This save is invalid!
|
||||||
save.import.fail = [crimson]Failed to import save: [accent]{0}
|
save.import.fail = [crimson]Failed to import save: [accent]{0}
|
||||||
save.export.fail = [crimson]Failed to export save: [accent]{0}
|
save.export.fail = [crimson]Failed to export save: [accent]{0}
|
||||||
save.import = Import Save
|
save.import = Tuo Tallennus
|
||||||
save.newslot = Tallennuksen nimi:
|
save.newslot = Tallennuksen nimi:
|
||||||
save.rename = Nimeä uudelleen
|
save.rename = Nimeä uudelleen
|
||||||
save.rename.text = Uusi nimi:
|
save.rename.text = Uusi nimi:
|
||||||
@@ -265,8 +265,8 @@ cancelbuilding = [accent][[{0}][] to clear plan
|
|||||||
selectschematic = [accent][[{0}][] to select+copy
|
selectschematic = [accent][[{0}][] to select+copy
|
||||||
pausebuilding = [accent][[{0}][] to pause building
|
pausebuilding = [accent][[{0}][] to pause building
|
||||||
resumebuilding = [scarlet][[{0}][] to resume building
|
resumebuilding = [scarlet][[{0}][] to resume building
|
||||||
wave = [accent]Aalto {0}
|
wave = [accent]Taso {0}
|
||||||
wave.waiting = [lightgray]Wave in {0}
|
wave.waiting = [lightgray]Seuraava taso {0}
|
||||||
wave.waveInProgress = [lightgray]Wave in progress
|
wave.waveInProgress = [lightgray]Wave in progress
|
||||||
waiting = [lightgray]Odotetaan...
|
waiting = [lightgray]Odotetaan...
|
||||||
waiting.players = Odotetaan pelaajia...
|
waiting.players = Odotetaan pelaajia...
|
||||||
@@ -275,7 +275,7 @@ wave.enemy = [lightgray]{0} vihollinen jäljellä
|
|||||||
loadimage = Lataa kuva
|
loadimage = Lataa kuva
|
||||||
saveimage = Tallenna kuva
|
saveimage = Tallenna kuva
|
||||||
unknown = Tuntematon
|
unknown = Tuntematon
|
||||||
custom = Custom
|
custom = Mukautettu
|
||||||
builtin = Sisäänrakennettu
|
builtin = Sisäänrakennettu
|
||||||
map.delete.confirm = Oletko varma että haluat poistaa tämän kartan? Poistoa ei voi peruuttaa!
|
map.delete.confirm = Oletko varma että haluat poistaa tämän kartan? Poistoa ei voi peruuttaa!
|
||||||
map.random = [accent]Satunnainen kartta
|
map.random = [accent]Satunnainen kartta
|
||||||
@@ -296,30 +296,30 @@ publish.confirm = Are you sure you want to publish this?\n\n[lightgray]Make sure
|
|||||||
publish.error = Error publishing item: {0}
|
publish.error = Error publishing item: {0}
|
||||||
steam.error = Failed to initialize Steam services.\nError: {0}
|
steam.error = Failed to initialize Steam services.\nError: {0}
|
||||||
|
|
||||||
editor.brush = Brush
|
editor.brush = Sivellin
|
||||||
editor.openin = Avaa editorissa
|
editor.openin = Avaa editorissa
|
||||||
editor.oregen = Ore Generation
|
editor.oregen = Ore Generation
|
||||||
editor.oregen.info = Ore Generation:
|
editor.oregen.info = Ore Generation:
|
||||||
editor.mapinfo = Kartan tiedot
|
editor.mapinfo = Kartan tiedot
|
||||||
editor.author = Author:
|
editor.author = Tekijä:
|
||||||
editor.description = Kuvaus:
|
editor.description = Kuvaus:
|
||||||
editor.nodescription = A map must have a description of at least 4 characters before being published.
|
editor.nodescription = Kartan kuvaksessa täytyy olla vähintään neljä kirjainta ennen julkaisua.
|
||||||
editor.waves = Aallot:
|
editor.waves = Tasot:
|
||||||
editor.rules = Säännöt:
|
editor.rules = Säännöt:
|
||||||
editor.generation = Generation:
|
editor.generation = Generaatio:
|
||||||
editor.ingame = Edit In-Game
|
editor.ingame = Muokka pelin sisällä
|
||||||
editor.publish.workshop = Publish On Workshop
|
editor.publish.workshop = Julkaise työpajaan
|
||||||
editor.newmap = Uusi kartta
|
editor.newmap = Uusi kartta
|
||||||
workshop = Workshop
|
workshop = Työpaja
|
||||||
waves.title = Aallot
|
waves.title = Tasot
|
||||||
waves.remove = Remove
|
waves.remove = Poista
|
||||||
waves.never = <ei koskaan>
|
waves.never = <ei koskaan>
|
||||||
waves.every = every
|
waves.every = every
|
||||||
waves.waves = wave(s)
|
waves.waves = wave(s)
|
||||||
waves.perspawn = per spawn
|
waves.perspawn = per spawni
|
||||||
waves.to = to
|
waves.to = to
|
||||||
waves.boss = Boss
|
waves.boss = Pomo
|
||||||
waves.preview = Preview
|
waves.preview = Esikatselu
|
||||||
waves.edit = Muokkaa...
|
waves.edit = Muokkaa...
|
||||||
waves.copy = Kopioi leikepöydälle
|
waves.copy = Kopioi leikepöydälle
|
||||||
waves.load = Lataa leikepöydältä
|
waves.load = Lataa leikepöydältä
|
||||||
@@ -327,11 +327,11 @@ waves.invalid = Invalid waves in clipboard.
|
|||||||
waves.copied = Aallot kopioitu.
|
waves.copied = Aallot kopioitu.
|
||||||
waves.none = No enemies defined.\nNote that empty wave layouts will automatically be replaced with the default layout.
|
waves.none = No enemies defined.\nNote that empty wave layouts will automatically be replaced with the default layout.
|
||||||
editor.default = [lightgray]<Default>
|
editor.default = [lightgray]<Default>
|
||||||
details = Details...
|
details = Yksityiskohdat...
|
||||||
edit = Muokkaa...
|
edit = Muokkaa...
|
||||||
editor.name = Nimi:
|
editor.name = Nimi:
|
||||||
editor.spawn = Spawn Unit
|
editor.spawn = Spawni yksikkö
|
||||||
editor.removeunit = Remove Unit
|
editor.removeunit = Poista yksikkö
|
||||||
editor.teams = Joukkueet
|
editor.teams = Joukkueet
|
||||||
editor.errorload = Virhe ladattaessa tiedostoa:\n[accent]{0}
|
editor.errorload = Virhe ladattaessa tiedostoa:\n[accent]{0}
|
||||||
editor.errorsave = Virhe tallennettaessa tiedostoa:\n[accent]{0}
|
editor.errorsave = Virhe tallennettaessa tiedostoa:\n[accent]{0}
|
||||||
@@ -341,10 +341,10 @@ editor.errornot = This is not a map file.
|
|||||||
editor.errorheader = This map file is either not valid or corrupt.
|
editor.errorheader = This map file is either not valid or corrupt.
|
||||||
editor.errorname = Map has no name defined. Are you trying to load a save file?
|
editor.errorname = Map has no name defined. Are you trying to load a save file?
|
||||||
editor.update = Päivitä
|
editor.update = Päivitä
|
||||||
editor.randomize = Randomize
|
editor.randomize = Satunnaista
|
||||||
editor.apply = Apply
|
editor.apply = Käytä
|
||||||
editor.generate = Generate
|
editor.generate = Generoi
|
||||||
editor.resize = Resize
|
editor.resize = Säädä kokoa
|
||||||
editor.loadmap = Lataa kartta
|
editor.loadmap = Lataa kartta
|
||||||
editor.savemap = Tallenna kartta
|
editor.savemap = Tallenna kartta
|
||||||
editor.saved = Tallennettu!
|
editor.saved = Tallennettu!
|
||||||
@@ -370,12 +370,12 @@ editor.resizemap = Resize Map
|
|||||||
editor.mapname = Kartan nimi:
|
editor.mapname = Kartan nimi:
|
||||||
editor.overwrite = [accent]Warning!\nThis overwrites an existing map.
|
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.overwrite.confirm = [scarlet]Warning![] A map with this name already exists. Are you sure you want to overwrite it?
|
||||||
editor.exists = A map with this name already exists.
|
editor.exists = Kartta tällä nimellä on jo olemassa.
|
||||||
editor.selectmap = Select a map to load:
|
editor.selectmap = Valitse kartta ladattavaksi:
|
||||||
|
|
||||||
toolmode.replace = Replace
|
toolmode.replace = Korvaa
|
||||||
toolmode.replace.description = Draws only on solid blocks.
|
toolmode.replace.description = Draws only on solid blocks.
|
||||||
toolmode.replaceall = Replace All
|
toolmode.replaceall = Korvaa kaikki
|
||||||
toolmode.replaceall.description = Replace all blocks in map.
|
toolmode.replaceall.description = Replace all blocks in map.
|
||||||
toolmode.orthogonal = Orthogonal
|
toolmode.orthogonal = Orthogonal
|
||||||
toolmode.orthogonal.description = Draws only orthogonal lines.
|
toolmode.orthogonal.description = Draws only orthogonal lines.
|
||||||
@@ -424,48 +424,48 @@ width = Leveys:
|
|||||||
height = Korkeus:
|
height = Korkeus:
|
||||||
menu = Valikko
|
menu = Valikko
|
||||||
play = Pelaa
|
play = Pelaa
|
||||||
campaign = Campaign
|
campaign = Polku
|
||||||
load = Lataa
|
load = Lataa
|
||||||
save = Tallenna
|
save = Tallenna
|
||||||
fps = FPS: {0}
|
fps = FPS: {0}
|
||||||
ping = Ping: {0}ms
|
ping = Ping: {0}ms
|
||||||
language.restart = Please restart your game for the language settings to take effect.
|
language.restart = Please restart your game for the language settings to take effect.
|
||||||
settings = Asetukset
|
settings = Asetukset
|
||||||
tutorial = Perehdytys
|
tutorial = Tutoriaali
|
||||||
tutorial.retake = Re-Take Tutorial
|
tutorial.retake = Uusita Tutoriaali
|
||||||
editor = Editor
|
editor = Editori
|
||||||
mapeditor = Map Editor
|
mapeditor = Kartan Editori
|
||||||
|
|
||||||
abandon = Hylkää
|
abandon = Hylkää
|
||||||
abandon.text = This zone and all its resources will be lost to the enemy.
|
abandon.text = This zone and all its resources will be lost to the enemy.
|
||||||
locked = Lukittu
|
locked = Lukittu
|
||||||
complete = [lightgray]Reach:
|
complete = [lightgray]Reach:
|
||||||
requirement.wave = Reach Wave {0} in {1}
|
requirement.wave = Pääse Tasolle {0} kartassa {1}
|
||||||
requirement.core = Destroy Enemy Core in {0}
|
requirement.core = Tuhoa vihollise ydin kartassa {0}
|
||||||
requirement.unlock = Unlock {0}
|
requirement.unlock = Avaa {0}
|
||||||
resume = Resume Zone:\n[lightgray]{0}
|
resume = Resume Zone:\n[lightgray]{0}
|
||||||
bestwave = [lightgray]Best Wave: {0}
|
bestwave = [lightgray]Paras taso: {0}
|
||||||
launch = < LAUNCH >
|
launch = < LAUNCH >
|
||||||
launch.title = Launch Successful
|
launch.title = Onnistunut laukaisu
|
||||||
launch.next = [lightgray]next opportunity at wave {0}
|
launch.next = [lightgray]next opportunity at wave {0}
|
||||||
launch.unable2 = [scarlet]Unable to LAUNCH.[]
|
launch.unable2 = [scarlet]Unable to LAUNCH.[]
|
||||||
launch.confirm = This will launch all resources in your core.\nYou will not be able to return to this base.
|
launch.confirm = Tämä laukaisee kaikki resourssit ytimestäsi.\nEt voi enää palata takaisin.
|
||||||
launch.skip.confirm = If you skip now, you will not be able to launch until later waves.
|
launch.skip.confirm = Jos ohitat nyt, voit laukaista vasta myöhemmissä tasoissa.
|
||||||
uncover = Uncover
|
uncover = Paljasta
|
||||||
configure = Configure Loadout
|
configure = Configure Loadout
|
||||||
bannedblocks = Banned Blocks
|
bannedblocks = Kielletyt Palikat
|
||||||
addall = Add All
|
addall = Lisää kaikki
|
||||||
configure.locked = [lightgray]Unlock configuring loadout: Wave {0}.
|
configure.locked = [lightgray]Unlock configuring loadout: Wave {0}.
|
||||||
configure.invalid = Amount must be a number between 0 and {0}.
|
configure.invalid = Amount must be a number between 0 and {0}.
|
||||||
zone.unlocked = [lightgray]{0} unlocked.
|
zone.unlocked = [lightgray]{0} unlocked.
|
||||||
zone.requirement.complete = Wave {0} reached:\n{1} zone requirements met.
|
zone.requirement.complete = Wave {0} reached:\n{1} zone requirements met.
|
||||||
zone.config.unlocked = Loadout unlocked:[lightgray]\n{0}
|
zone.config.unlocked = Loadout unlocked:[lightgray]\n{0}
|
||||||
zone.resources = [lightgray]Resources Detected:
|
zone.resources = [lightgray]Resoursseja havaittu:
|
||||||
zone.objective = [lightgray]Objective: [accent]{0}
|
zone.objective = [lightgray]Objectiivi: [accent]{0}
|
||||||
zone.objective.survival = Survive
|
zone.objective.survival = Selviydy
|
||||||
zone.objective.attack = Destroy Enemy Core
|
zone.objective.attack = Destroy Enemy Core
|
||||||
add = Add...
|
add = Lisää...
|
||||||
boss.health = Boss Health
|
boss.health = Pomon elinpisteet
|
||||||
|
|
||||||
connectfail = [crimson]Connection error:\n\n[accent]{0}
|
connectfail = [crimson]Connection error:\n\n[accent]{0}
|
||||||
error.unreachable = Server unreachable.\nIs the address spelled correctly?
|
error.unreachable = Server unreachable.\nIs the address spelled correctly?
|
||||||
@@ -478,209 +478,209 @@ error.io = Network I/O error.
|
|||||||
error.any = Unknown network error.
|
error.any = Unknown network error.
|
||||||
error.bloom = Failed to initialize bloom.\nYour device may not support it.
|
error.bloom = Failed to initialize bloom.\nYour device may not support it.
|
||||||
|
|
||||||
zone.groundZero.name = Ground Zero
|
zone.groundZero.name = Vaara Alue
|
||||||
zone.desertWastes.name = Desert Wastes
|
zone.desertWastes.name = Aavikon Jätteet
|
||||||
zone.craters.name = The Craters
|
zone.craters.name = Kraatterit
|
||||||
zone.frozenForest.name = Frozen Forest
|
zone.frozenForest.name = Jäätynyt Metsä
|
||||||
zone.ruinousShores.name = Ruinous Shores
|
zone.ruinousShores.name = Tuhoisa Ranta
|
||||||
zone.stainedMountains.name = Stained Mountains
|
zone.stainedMountains.name = Tahravuoret
|
||||||
zone.desolateRift.name = Desolate Rift
|
zone.desolateRift.name = Autio Syvänne
|
||||||
zone.nuclearComplex.name = Nuclear Production Complex
|
zone.nuclearComplex.name = Ydinvoima Kompleksi
|
||||||
zone.overgrowth.name = Overgrowth
|
zone.overgrowth.name = Liikakasvu
|
||||||
zone.tarFields.name = Tar Fields
|
zone.tarFields.name = Terva Pellot
|
||||||
zone.saltFlats.name = Salt Flats
|
zone.saltFlats.name = Suolainen Tasanne
|
||||||
zone.impact0078.name = Impact 0078
|
zone.impact0078.name = Isku 0078
|
||||||
zone.crags.name = Crags
|
zone.crags.name = Kalliot
|
||||||
zone.fungalPass.name = Fungal Pass
|
zone.fungalPass.name = Sienilaakso
|
||||||
|
|
||||||
zone.groundZero.description = The optimal location to begin once more. Low enemy threat. Few resources.\nGather as much lead and copper as possible.\nMove on.
|
zone.groundZero.description = Suotuisa alue aloittaa peli. Matala vaarataso. Vähän resoursseja.\nKerää mahdollisimman paljon kuparia ja lyijyä.\nJatka eteenpäin.
|
||||||
zone.frozenForest.description = Even here, closer to mountains, the spores have spread. The frigid temperatures cannot contain them forever.\n\nBegin the venture into power. Build combustion generators. Learn to use menders.
|
zone.frozenForest.description = Jopa täällä, lähellä vuoria, asustaa itiöitä. Mutta ne eivöt voi elää ikuisesti jäätävässä ilmastossa.\n\nAloita matkasi energiaan. Rakenna polttogeneraattoreita. Opettele korjaajien käyttö.
|
||||||
zone.desertWastes.description = These wastes are vast, unpredictable, and criss-crossed with derelict sector structures.\nCoal is present in the region. Burn it for power, or synthesize graphite.\n\n[lightgray]This landing location cannot be guaranteed.
|
zone.desertWastes.description = Valtavia määriä jätteitä ränsityneiden rakennuksien seassa.\nVoit löytää hiiltä. Polta se energiaksi, tai tiivistä siitä grafiittia.\n\n[lightgray]Laskeutumiskohta on epävarma.
|
||||||
zone.saltFlats.description = On the outskirts of the desert lie the Salt Flats. Few resources can be found in this location.\n\nThe enemy has erected a resource storage complex here. Eradicate their core. Leave nothing standing.
|
zone.saltFlats.description = Aavikoiden reunoilla on suolainen tasanne. Vain vähän resoursse on saatavilla.\n\nVihollinen on rakentanut resurssi hankinnan. Hävitä vihollisen ydin. Älä jätä mitään jäljelle.
|
||||||
zone.craters.description = Water has accumulated in this crater, relic of the old wars. Reclaim the area. Collect sand. Smelt metaglass. Pump water to cool turrets and drills.
|
zone.craters.description = Vettä on kertynyt tähän kraatteriin vanhojen sotien jäänteinä. Ota alue itsellesi. Kerää hiekkaa. Sulata metallilasia. Pumppaa vettä jäähdyttääksesi kaivausporia ja tykkejä.
|
||||||
zone.ruinousShores.description = Past the wastes, is the shoreline. Once, this location housed a coastal defense array. Not much of it remains. Only the most basic defense structures have remained unscathed, everything else reduced to scrap.\nContinue the expansion outwards. Rediscover the technology.
|
zone.ruinousShores.description = Jätteiden takaa, löydät rantaviivan. Kerran täma paikka asutti sotilasjoukkoja. Paljoa jäljella ei ole. Vain perus puolustus rekenteet ovat vahingoittumattomia, kaakki muu on romua.\nJatka laajentamista eteenpäin. Tutki teknologiaa.
|
||||||
zone.stainedMountains.description = Further inland lie the mountains, yet untainted by spores.\nExtract the abundant titanium in this area. Learn how to use it.\n\nThe enemy presence is greater here. Do not give them time to send their strongest units.
|
zone.stainedMountains.description = Alankoa vuroien sisämaassa, löydät paljon itiöelämää.\nOta talteen runsas titaani jota täältä löytyy. Opi käyttämään sitä.\n\nVihollinen on läasnäoleva. Älä anna heille liikaa aikaa lähettää heidän vahvimpia aseitaan.
|
||||||
zone.overgrowth.description = This area is overgrown, closer to the source of the spores.\nThe enemy has established an outpost here. Build Titan units. Destroy it. Reclaim that which was lost.
|
zone.overgrowth.description = Tämä alue on ylikasvanut, lähempänä itiöiden pesäkettä.\nVihollinen on muodostanut etuvartion. Rakenna titaani yksikköjä. Tuhoa vihollinen. Hanki se alue joka joskus on menetetty.
|
||||||
zone.tarFields.description = The outskirts of an oil production zone, between the mountains and desert. One of the few areas with usable tar reserves.\nAlthough abandoned, this area has some dangerous enemy forces nearby. Do not underestimate them.\n\n[lightgray]Research oil processing technology if possible.
|
zone.tarFields.description = Öljytuotannon laitamia, vuorien ja aavikkojen välissä. Yksi ainoista paikosta joissa on tervalähde.\nLöydät vaarallisia vihollis joukkoja täältä hylätystä paikasta. Älä aliarvio niitä.\n\n[lightgray]Tutki öljyprosessointi mekanismeja jos mahdollista.
|
||||||
zone.desolateRift.description = An extremely dangerous zone. Plentiful resources, but little space. High risk of destruction. Leave as soon as possible. Do not be fooled by the long spacing between enemy attacks.
|
zone.desolateRift.description = Extremaalisen vaarallinen alue. Runsaasti resursseja, mutta vain vähän tilaa. Korke riski eliminaatiolle. Lähde mahdollisimman nopeasti. Älä tule huijatuksi pitkien taukojen vihollisten hyökkäyksien vällillä.
|
||||||
zone.nuclearComplex.description = A former facility for the production and processing of thorium, reduced to ruins.\n[lightgray]Research the thorium and its many uses.\n\nThe enemy is present here in great numbers, constantly scouting for attackers.
|
zone.nuclearComplex.description = Entinen laitos tehty toriumin tuotantoa ja prosessointia varten, nykyään vain rauniota.\n[lightgray]Tutki toriumin monia hyödyllisiä käyttötarkoituksia.\n\nVihollinen on läsnä isoin joukkoin kokoajan partioimassa tunkeilijoilta.
|
||||||
zone.fungalPass.description = A transition area between high mountains and lower, spore-ridden lands. A small enemy reconnaissance base is located here.\nDestroy it.\nUse Dagger and Crawler units. Take out the two cores.
|
zone.fungalPass.description = Siirtymis alue korkeiden ja matalien vuorien välillä, itiöiden peitossa. Pieni vihollis tiedustelu tukikohta on täällä. Tuhoa se.\nKäytä Dagger- ja Crawler yksikköjä. Tuhoa kaksi vihollisydintä.
|
||||||
zone.impact0078.description = <insert description here>
|
zone.impact0078.description = <insert description here>
|
||||||
zone.crags.description = <insert description here>
|
zone.crags.description = <insert description here>
|
||||||
|
|
||||||
settings.language = Language
|
settings.language = Kieli
|
||||||
settings.data = Game Data
|
settings.data = Peli Data
|
||||||
settings.reset = Reset to Defaults
|
settings.reset = Nollaa Asetukset
|
||||||
settings.rebind = Rebind
|
settings.rebind = Uudelleenaseta
|
||||||
settings.resetKey = Reset
|
settings.resetKey = Aseta Uudelleen
|
||||||
settings.controls = Controls
|
settings.controls = Kontrollit
|
||||||
settings.game = Game
|
settings.game = Peli
|
||||||
settings.sound = Sound
|
settings.sound = Ääni
|
||||||
settings.graphics = Graphics
|
settings.graphics = Grafiikat
|
||||||
settings.cleardata = Clear Game Data...
|
settings.cleardata = Tyhjennä Pelin Data...
|
||||||
settings.clear.confirm = Are you sure you want to clear this data?\nWhat is done cannot be undone!
|
settings.clear.confirm = Oletko varma että haluat tyhjentää pelin datan?\nMitä on tehty ei voi peruuttaa!
|
||||||
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.clearall.confirm = [scarlet]WARNING![]\nTämä poistaa kaiken datan, mukaanlukien kesken olevat pelit, kartat, avatut asiat ja kontrolliasetukset.\nKun painat 'ok' kaikki datasi poistetaan ja peli suljetaan.
|
||||||
paused = [accent]< Paused >
|
paused = [accent]< Pysäytetty >
|
||||||
clear = Clear
|
clear = Tyhjä
|
||||||
banned = [scarlet]Banned
|
banned = [scarlet]Kielletty
|
||||||
yes = Yes
|
yes = Kyllä
|
||||||
no = No
|
no = Ei
|
||||||
info.title = Info
|
info.title = Informaatio
|
||||||
error.title = [crimson]An error has occured
|
error.title = [crimson]An error has occured
|
||||||
error.crashtitle = An error has occured
|
error.crashtitle = An error has occured
|
||||||
blocks.input = Input
|
blocks.input = Sisääntulo
|
||||||
blocks.output = Output
|
blocks.output = Ulostulo
|
||||||
blocks.booster = Booster
|
blocks.booster = Boostaa
|
||||||
block.unknown = [lightgray]???
|
block.unknown = [lightgray]???
|
||||||
blocks.powercapacity = Power Capacity
|
blocks.powercapacity = Energiakapasiteetti
|
||||||
blocks.powershot = Power/Shot
|
blocks.powershot = Energiaa/Ammus
|
||||||
blocks.damage = Damage
|
blocks.damage = Vahinko
|
||||||
blocks.targetsair = Targets Air
|
blocks.targetsair = Hyökkää Ilmaan
|
||||||
blocks.targetsground = Targets Ground
|
blocks.targetsground = Hyökkää Maahan
|
||||||
blocks.itemsmoved = Move Speed
|
blocks.itemsmoved = Liikkumisnopeus
|
||||||
blocks.launchtime = Time Between Launches
|
blocks.launchtime = Aika Laukaisujen Välillä
|
||||||
blocks.shootrange = Range
|
blocks.shootrange = Kantama
|
||||||
blocks.size = Size
|
blocks.size = Koko
|
||||||
blocks.liquidcapacity = Liquid Capacity
|
blocks.liquidcapacity = Neste Kapasiteetti
|
||||||
blocks.powerrange = Power Range
|
blocks.powerrange = Energia Kantama
|
||||||
blocks.powerconnections = Max Connections
|
blocks.powerconnections = Maksimi konnektio määrä
|
||||||
blocks.poweruse = Power Use
|
blocks.poweruse = Energian Käyttö
|
||||||
blocks.powerdamage = Power/Damage
|
blocks.powerdamage = Energia/Vahinko
|
||||||
blocks.itemcapacity = Item Capacity
|
blocks.itemcapacity = Tavara Kapasiteetti
|
||||||
blocks.basepowergeneration = Base Power Generation
|
blocks.basepowergeneration = Kanta Enegian Generointi
|
||||||
blocks.productiontime = Production Time
|
blocks.productiontime = Produktion Aika
|
||||||
blocks.repairtime = Block Full Repair Time
|
blocks.repairtime = Kokonaisen Palikan Korjaus Aika
|
||||||
blocks.speedincrease = Speed Increase
|
blocks.speedincrease = Nopeuden Kasvu
|
||||||
blocks.range = Range
|
blocks.range = Etäisyys
|
||||||
blocks.drilltier = Drillables
|
blocks.drilltier = Porattavat
|
||||||
blocks.drillspeed = Base Drill Speed
|
blocks.drillspeed = Kanta Poran Nopeus
|
||||||
blocks.boosteffect = Boost Effect
|
blocks.boosteffect = Boostaamisen Vaikutus
|
||||||
blocks.maxunits = Max Active Units
|
blocks.maxunits = Maksimi Määrä Yksikköjä
|
||||||
blocks.health = Health
|
blocks.health = Elämäpisteet
|
||||||
blocks.buildtime = Build Time
|
blocks.buildtime = Rakentamisen Aika
|
||||||
blocks.buildcost = Build Cost
|
blocks.buildcost = Rakentamisen Hinta
|
||||||
blocks.inaccuracy = Inaccuracy
|
blocks.inaccuracy = Epätarkkuus
|
||||||
blocks.shots = Shots
|
blocks.shots = Ammusta
|
||||||
blocks.reload = Shots/Second
|
blocks.reload = Ammusta/Sekunnissa
|
||||||
blocks.ammo = Ammo
|
blocks.ammo = Ammus
|
||||||
|
|
||||||
bar.drilltierreq = Better Drill Required
|
bar.drilltierreq = Parempi Pora Vaadittu
|
||||||
bar.drillspeed = Drill Speed: {0}/s
|
bar.drillspeed = Poran Nopeus: {0}/s
|
||||||
bar.pumpspeed = Pump Speed: {0}/s
|
bar.pumpspeed = Pumpun Nopeus: {0}/s
|
||||||
bar.efficiency = Efficiency: {0}%
|
bar.efficiency = Tehokkuus: {0}%
|
||||||
bar.powerbalance = Power: {0}/s
|
bar.powerbalance = Energia: {0}/s
|
||||||
bar.powerstored = Stored: {0}/{1}
|
bar.powerstored = Säilöttynä: {0}/{1}
|
||||||
bar.poweramount = Power: {0}
|
bar.poweramount = Energia: {0}
|
||||||
bar.poweroutput = Power Output: {0}
|
bar.poweroutput = Energian Ulostulo: {0}
|
||||||
bar.items = Items: {0}
|
bar.items = Tavaroita: {0}
|
||||||
bar.capacity = Capacity: {0}
|
bar.capacity = Kapasiteetti: {0}
|
||||||
bar.liquid = Liquid
|
bar.liquid = Neste
|
||||||
bar.heat = Heat
|
bar.heat = Lämpö
|
||||||
bar.power = Power
|
bar.power = Energia
|
||||||
bar.progress = Build Progress
|
bar.progress = Rakennuksen Edistys
|
||||||
bar.spawned = Units: {0}/{1}
|
bar.spawned = Yksikköjä: {0}/{1}
|
||||||
bar.input = Input
|
bar.input = Sisääntulo
|
||||||
bar.output = Output
|
bar.output = Ulostulo
|
||||||
|
|
||||||
bullet.damage = [stat]{0}[lightgray] damage
|
bullet.damage = [stat]{0}[lightgray] Vahinko
|
||||||
bullet.splashdamage = [stat]{0}[lightgray] area dmg ~[stat] {1}[lightgray] tiles
|
bullet.splashdamage = [stat]{0}[lightgray] Alue vahinko ~[stat] {1}[lightgray] palikkaa
|
||||||
bullet.incendiary = [stat]incendiary
|
bullet.incendiary = [stat]sytyttävä
|
||||||
bullet.homing = [stat]homing
|
bullet.homing = [stat]itseohjautuva
|
||||||
bullet.shock = [stat]shock
|
bullet.shock = [stat]shokki
|
||||||
bullet.frag = [stat]frag
|
bullet.frag = [stat]sirpaloituva
|
||||||
bullet.knockback = [stat]{0}[lightgray] knockback
|
bullet.knockback = [stat]{0}[lightgray] knockback
|
||||||
bullet.freezing = [stat]freezing
|
bullet.freezing = [stat]jäädyttävä
|
||||||
bullet.tarred = [stat]tarred
|
bullet.tarred = [stat]tervattu
|
||||||
bullet.multiplier = [stat]{0}[lightgray]x ammo multiplier
|
bullet.multiplier = [stat]{0}[lightgray]x ammusten multiplikaatio
|
||||||
bullet.reload = [stat]{0}[lightgray]x fire rate
|
bullet.reload = [stat]{0}[lightgray]x ammunta nopeus
|
||||||
|
|
||||||
unit.blocks = blocks
|
unit.blocks = palikat
|
||||||
unit.powersecond = power units/second
|
unit.powersecond = energia yksikköä/sekunti
|
||||||
unit.liquidsecond = liquid units/second
|
unit.liquidsecond = neste yksikköä/sekunti
|
||||||
unit.itemssecond = items/second
|
unit.itemssecond = esinettä/sekunti
|
||||||
unit.liquidunits = liquid units
|
unit.liquidunits = neste yksikköä
|
||||||
unit.powerunits = power units
|
unit.powerunits = energia yksikköä
|
||||||
unit.degrees = degrees
|
unit.degrees = astetta
|
||||||
unit.seconds = seconds
|
unit.seconds = sekunttia
|
||||||
unit.persecond = /sec
|
unit.persecond = /s
|
||||||
unit.timesspeed = x speed
|
unit.timesspeed = x nopeus
|
||||||
unit.percent = %
|
unit.percent = %
|
||||||
unit.items = items
|
unit.items = esinettä
|
||||||
unit.thousands = k
|
unit.thousands = t
|
||||||
unit.millions = mil
|
unit.millions = mil
|
||||||
category.general = General
|
category.general = Yleinen
|
||||||
category.power = Power
|
category.power = Energia
|
||||||
category.liquids = Liquids
|
category.liquids = Neste
|
||||||
category.items = Items
|
category.items = Tavarat
|
||||||
category.crafting = Input/Output
|
category.crafting = Ulos/Sisääntulo
|
||||||
category.shooting = Shooting
|
category.shooting = Ammunta
|
||||||
category.optional = Optional Enhancements
|
category.optional = Mahdolliset Lumoukset
|
||||||
setting.landscape.name = Lock Landscape
|
setting.landscape.name = Lukitse tasavaakaan
|
||||||
setting.shadows.name = Shadows
|
setting.shadows.name = Varjot
|
||||||
setting.blockreplace.name = Automatic Block Suggestions
|
setting.blockreplace.name = Automaattisia Palikka Suosituksia
|
||||||
setting.linear.name = Linear Filtering
|
setting.linear.name = Lineararien Filteeraus
|
||||||
setting.hints.name = Hints
|
setting.hints.name = Vihjeet
|
||||||
setting.buildautopause.name = Auto-Pause Building
|
setting.buildautopause.name = Automaattisest Pysäytä Rakentaessa
|
||||||
setting.animatedwater.name = Animated Water
|
setting.animatedwater.name = Animoitu Vesi
|
||||||
setting.animatedshields.name = Animated Shields
|
setting.animatedshields.name = Animoitu Kilpi
|
||||||
setting.antialias.name = Antialias[lightgray] (requires restart)[]
|
setting.antialias.name = Antialiaasi[lightgray] (vaatii uudelleenkäynnistyksen)[]
|
||||||
setting.indicators.name = Enemy/Ally Indicators
|
setting.indicators.name = Vihollis/Puolulais Indikaattorit
|
||||||
setting.autotarget.name = Auto-Target
|
setting.autotarget.name = Automaatinen Tähtäys
|
||||||
setting.keyboard.name = Mouse+Keyboard Controls
|
setting.keyboard.name = Hiiri+Näppäimistö Kontrollit
|
||||||
setting.touchscreen.name = Touchscreen Controls
|
setting.touchscreen.name = Kosketusnäyttö kontrollit
|
||||||
setting.fpscap.name = Max FPS
|
setting.fpscap.name = Maksimi FPS
|
||||||
setting.fpscap.none = None
|
setting.fpscap.none = Ei Mitään
|
||||||
setting.fpscap.text = {0} FPS
|
setting.fpscap.text = {0} FPS
|
||||||
setting.uiscale.name = UI Scaling[lightgray] (require restart)[]
|
setting.uiscale.name = UI Koko[lightgray] (vaatii uudelleenkäynnistyksen)[]
|
||||||
setting.swapdiagonal.name = Always Diagonal Placement
|
setting.swapdiagonal.name = Aina Vino Korvaus
|
||||||
setting.difficulty.training = Training
|
setting.difficulty.training = Treeni
|
||||||
setting.difficulty.easy = Easy
|
setting.difficulty.easy = Helppo
|
||||||
setting.difficulty.normal = Normal
|
setting.difficulty.normal = Keskivaikea
|
||||||
setting.difficulty.hard = Hard
|
setting.difficulty.hard = Haastava
|
||||||
setting.difficulty.insane = Insane
|
setting.difficulty.insane = Järjetön
|
||||||
setting.difficulty.name = Difficulty:
|
setting.difficulty.name = Vaikeustaso:
|
||||||
setting.screenshake.name = Screen Shake
|
setting.screenshake.name = Näytön keikkuminen
|
||||||
setting.effects.name = Display Effects
|
setting.effects.name = Naytön Efektit
|
||||||
setting.destroyedblocks.name = Display Destroyed Blocks
|
setting.destroyedblocks.name = Näytä Tuhoutuneet Palikat
|
||||||
setting.conveyorpathfinding.name = Conveyor Placement Pathfinding
|
setting.conveyorpathfinding.name = Conveyor Placement Pathfinding
|
||||||
setting.coreselect.name = Allow Schematic Cores
|
setting.coreselect.name = Allow Schematic Cores
|
||||||
setting.sensitivity.name = Controller Sensitivity
|
setting.sensitivity.name = Kontrollin Herkkyys
|
||||||
setting.saveinterval.name = Save Interval
|
setting.saveinterval.name = Tallennuksen Aikaväli
|
||||||
setting.seconds = {0} Seconds
|
setting.seconds = {0} Sekunttia
|
||||||
setting.blockselecttimeout.name = Block Select Timeout
|
setting.blockselecttimeout.name = Block Select Timeout
|
||||||
setting.milliseconds = {0} milliseconds
|
setting.milliseconds = {0} millisekunttia
|
||||||
setting.fullscreen.name = Fullscreen
|
setting.fullscreen.name = Fullscreen
|
||||||
setting.borderlesswindow.name = Borderless Window[lightgray] (may require restart)
|
setting.borderlesswindow.name = Borderless Window[lightgray] (vaatii uudelleenkäynnistyksen)
|
||||||
setting.fps.name = Show FPS
|
setting.fps.name = Näytä FPS
|
||||||
setting.blockselectkeys.name = Show Block Select Keys
|
setting.blockselectkeys.name = Bäytä Palikan Selektio Kontrollit
|
||||||
setting.vsync.name = VSync
|
setting.vsync.name = VSync
|
||||||
setting.pixelate.name = Pixelate[lightgray] (disables animations)
|
setting.pixelate.name = Pixeloi[lightgray] (poistaa animaation käytöstä)
|
||||||
setting.minimap.name = Show Minimap
|
setting.minimap.name = Näytä Minimappi
|
||||||
setting.position.name = Show Player Position
|
setting.position.name = Näytä pelaajan sijainti
|
||||||
setting.musicvol.name = Music Volume
|
setting.musicvol.name = Musiikin Äänenvoimakkuus
|
||||||
setting.ambientvol.name = Ambient Volume
|
setting.ambientvol.name = Tausta Äänet
|
||||||
setting.mutemusic.name = Mute Music
|
setting.mutemusic.name = Sulje Musiikki
|
||||||
setting.sfxvol.name = SFX Volume
|
setting.sfxvol.name = SFX Volyymi
|
||||||
setting.mutesound.name = Mute Sound
|
setting.mutesound.name = Sulje Äänet
|
||||||
setting.crashreport.name = Send Anonymous Crash Reports
|
setting.crashreport.name = Send Anonymous Crash Reports
|
||||||
setting.savecreate.name = Auto-Create Saves
|
setting.savecreate.name = Luo Automaattisesti Tallennukset
|
||||||
setting.publichost.name = Public Game Visibility
|
setting.publichost.name = Public Game Visibility
|
||||||
setting.chatopacity.name = Chat Opacity
|
setting.chatopacity.name = Chatin Läpinäkymättömyys
|
||||||
setting.lasersopacity.name = Power Laser Opacity
|
setting.lasersopacity.name = Energia Laaserin Läpinäkymattämyys
|
||||||
setting.playerchat.name = Display In-Game Chat
|
setting.playerchat.name = Näytä Pelinsisäinen Keskustelu
|
||||||
public.confirm = Do you want to make your game public?\n[accent]Anyone will be able to join your games.\n[lightgray]This can be changed later in Settings->Game->Public Game Visibility.
|
public.confirm = Do you want to make your game public?\n[accent]Anyone will be able to join your games.\n[lightgray]This can be changed later in Settings->Game->Public Game Visibility.
|
||||||
public.beta = Note that beta versions of the game cannot make public lobbies.
|
public.beta = Note that beta versions of the game cannot make public lobbies.
|
||||||
uiscale.reset = UI scale has been changed.\nPress "OK" to confirm this scale.\n[scarlet]Reverting and exiting in[accent] {0}[] seconds...
|
uiscale.reset = UI scale has been changed.\nPress "OK" to confirm this scale.\n[scarlet]Reverting and exiting in[accent] {0}[] seconds...
|
||||||
uiscale.cancel = Cancel & Exit
|
uiscale.cancel = Peruuta ja Poistu
|
||||||
setting.bloom.name = Bloom
|
setting.bloom.name = Bloom
|
||||||
keybind.title = Rebind Keys
|
keybind.title = Rebind Keys
|
||||||
keybinds.mobile = [scarlet]Most keybinds here are not functional on mobile. Only basic movement is supported.
|
keybinds.mobile = [scarlet]Most keybinds here are not functional on mobile. Only basic movement is supported.
|
||||||
category.general.name = General
|
category.general.name = General
|
||||||
category.view.name = View
|
category.view.name = View
|
||||||
category.multiplayer.name = Multiplayer
|
category.multiplayer.name = Multiplayer
|
||||||
command.attack = Attack
|
command.attack = Hyökkää
|
||||||
command.rally = Rally
|
command.rally = Kutsu Koolle
|
||||||
command.retreat = Retreat
|
command.retreat = Palaa
|
||||||
placement.blockselectkeys = \n[lightgray]Key: [{0},
|
placement.blockselectkeys = \n[lightgray]Key: [{0},
|
||||||
keybind.clear_building.name = Clear Building
|
keybind.clear_building.name = Clear Building
|
||||||
keybind.press = Press a key...
|
keybind.press = Press a key...
|
||||||
@@ -692,7 +692,7 @@ keybind.move_y.name = Move y
|
|||||||
keybind.mouse_move.name = Follow Mouse
|
keybind.mouse_move.name = Follow Mouse
|
||||||
keybind.dash.name = Dash
|
keybind.dash.name = Dash
|
||||||
keybind.schematic_select.name = Select Region
|
keybind.schematic_select.name = Select Region
|
||||||
keybind.schematic_menu.name = Schematic Menu
|
keybind.schematic_menu.name = Kaavio Valikko
|
||||||
keybind.schematic_flip_x.name = Flip Schematic X
|
keybind.schematic_flip_x.name = Flip Schematic X
|
||||||
keybind.schematic_flip_y.name = Flip Schematic Y
|
keybind.schematic_flip_y.name = Flip Schematic Y
|
||||||
keybind.category_prev.name = Previous Category
|
keybind.category_prev.name = Previous Category
|
||||||
@@ -711,7 +711,7 @@ keybind.block_select_07.name = Category/Block Select 7
|
|||||||
keybind.block_select_08.name = Category/Block Select 8
|
keybind.block_select_08.name = Category/Block Select 8
|
||||||
keybind.block_select_09.name = Category/Block Select 9
|
keybind.block_select_09.name = Category/Block Select 9
|
||||||
keybind.block_select_10.name = Category/Block Select 10
|
keybind.block_select_10.name = Category/Block Select 10
|
||||||
keybind.fullscreen.name = Toggle Fullscreen
|
keybind.fullscreen.name = Vaihda Fullscreen
|
||||||
keybind.select.name = Select/Shoot
|
keybind.select.name = Select/Shoot
|
||||||
keybind.diagonal_placement.name = Diagonal Placement
|
keybind.diagonal_placement.name = Diagonal Placement
|
||||||
keybind.pick.name = Pick Block
|
keybind.pick.name = Pick Block
|
||||||
@@ -722,8 +722,8 @@ keybind.zoom.name = Zoom
|
|||||||
keybind.menu.name = Menu
|
keybind.menu.name = Menu
|
||||||
keybind.pause.name = Pause
|
keybind.pause.name = Pause
|
||||||
keybind.pause_building.name = Pause/Resume Building
|
keybind.pause_building.name = Pause/Resume Building
|
||||||
keybind.minimap.name = Minimap
|
keybind.minimap.name = Minimappi
|
||||||
keybind.chat.name = Chat
|
keybind.chat.name = Chatti
|
||||||
keybind.player_list.name = Player list
|
keybind.player_list.name = Player list
|
||||||
keybind.console.name = Console
|
keybind.console.name = Console
|
||||||
keybind.rotate.name = Rotate
|
keybind.rotate.name = Rotate
|
||||||
@@ -736,20 +736,20 @@ keybind.drop_unit.name = Drop Unit
|
|||||||
keybind.zoom_minimap.name = Zoom minimap
|
keybind.zoom_minimap.name = Zoom minimap
|
||||||
mode.help.title = Description of modes
|
mode.help.title = Description of modes
|
||||||
mode.survival.name = Survival
|
mode.survival.name = Survival
|
||||||
mode.survival.description = The normal mode. Limited resources and automatic incoming waves.\n[gray]Requires enemy spawns in the map to play.
|
mode.survival.description = Normaali moodi. Rajoitettu määrä resursseja ja tasoilla on aika.\n[gray]Vaatii vihollis spawneja kartassa.
|
||||||
mode.sandbox.name = Sandbox
|
mode.sandbox.name = Hiekkalaatikko
|
||||||
mode.sandbox.description = Infinite resources and no timer for waves.
|
mode.sandbox.description = Ikuisesti resursseja ja tasoilla ei ole aikaa.
|
||||||
mode.editor.name = Editor
|
mode.editor.name = Editori
|
||||||
mode.pvp.name = PvP
|
mode.pvp.name = PvP
|
||||||
mode.pvp.description = Fight against other players locally.\n[gray]Requires at least 2 differently-colored cores in the map to play.
|
mode.pvp.description = Fight against other players locally.\n[gray]Requires at least 2 differently-colored cores in the map to play.
|
||||||
mode.attack.name = Attack
|
mode.attack.name = Attack
|
||||||
mode.attack.description = Destroy the enemy's base. No waves.\n[gray]Requires a red core in the map to play.
|
mode.attack.description = Destroy the enemy's base. No waves.\n[gray]Requires a red core in the map to play.
|
||||||
mode.custom = Custom Rules
|
mode.custom = Custom Rules
|
||||||
|
|
||||||
rules.infiniteresources = Infinite Resources
|
rules.infiniteresources = Ikuisesti Resursseja
|
||||||
rules.reactorexplosions = Reactor Explosions
|
rules.reactorexplosions = Reaktori Räjähdykset
|
||||||
rules.wavetimer = Wave Timer
|
rules.wavetimer = Tasojen Aikaraja
|
||||||
rules.waves = Waves
|
rules.waves = Tasot
|
||||||
rules.attack = Attack Mode
|
rules.attack = Attack Mode
|
||||||
rules.enemyCheat = Infinite AI (Red Team) Resources
|
rules.enemyCheat = Infinite AI (Red Team) Resources
|
||||||
rules.unitdrops = Unit Drops
|
rules.unitdrops = Unit Drops
|
||||||
@@ -778,31 +778,31 @@ rules.title.experimental = Experimental
|
|||||||
rules.lighting = Lighting
|
rules.lighting = Lighting
|
||||||
rules.ambientlight = Ambient Light
|
rules.ambientlight = Ambient Light
|
||||||
|
|
||||||
content.item.name = Items
|
content.item.name = Tavarat
|
||||||
content.liquid.name = Liquids
|
content.liquid.name = Nesteet
|
||||||
content.unit.name = Units
|
content.unit.name = Yksiköt
|
||||||
content.block.name = Blocks
|
content.block.name = Palikat
|
||||||
content.mech.name = Mechs
|
content.mech.name = Yksikkö
|
||||||
item.copper.name = Copper
|
item.copper.name = Kupari
|
||||||
item.lead.name = Lead
|
item.lead.name = Lyijy
|
||||||
item.coal.name = Coal
|
item.coal.name = Hiili
|
||||||
item.graphite.name = Graphite
|
item.graphite.name = Grafiitti
|
||||||
item.titanium.name = Titanium
|
item.titanium.name = Titaani
|
||||||
item.thorium.name = Thorium
|
item.thorium.name = Torium
|
||||||
item.silicon.name = Silicon
|
item.silicon.name = Pii
|
||||||
item.plastanium.name = Plastanium
|
item.plastanium.name = Plastaniumi
|
||||||
item.phase-fabric.name = Phase Fabric
|
item.phase-fabric.name = Kiihde Kuitu
|
||||||
item.surge-alloy.name = Surge Alloy
|
item.surge-alloy.name = Taite Seos
|
||||||
item.spore-pod.name = Spore Pod
|
item.spore-pod.name = Itiö Palko
|
||||||
item.sand.name = Hiekka
|
item.sand.name = Hiekka
|
||||||
item.blast-compound.name = Blast Compound
|
item.blast-compound.name = Räjähde Yhdiste
|
||||||
item.pyratite.name = Pyratite
|
item.pyratite.name = Pyratiitti
|
||||||
item.metaglass.name = Metaglass
|
item.metaglass.name = Metallilasi
|
||||||
item.scrap.name = Scrap
|
item.scrap.name = Romu
|
||||||
liquid.water.name = Vesi
|
liquid.water.name = Vesi
|
||||||
liquid.slag.name = Slag
|
liquid.slag.name = Kuona
|
||||||
liquid.oil.name = Oil
|
liquid.oil.name = Öljy
|
||||||
liquid.cryofluid.name = Cryofluid
|
liquid.cryofluid.name = Kryoneste
|
||||||
mech.alpha-mech.name = Alpha
|
mech.alpha-mech.name = Alpha
|
||||||
mech.alpha-mech.weapon = Heavy Repeater
|
mech.alpha-mech.weapon = Heavy Repeater
|
||||||
mech.alpha-mech.ability = Regeneration
|
mech.alpha-mech.ability = Regeneration
|
||||||
@@ -824,22 +824,22 @@ mech.trident-ship.name = Trident
|
|||||||
mech.trident-ship.weapon = Bomb Bay
|
mech.trident-ship.weapon = Bomb Bay
|
||||||
mech.glaive-ship.name = Glaive
|
mech.glaive-ship.name = Glaive
|
||||||
mech.glaive-ship.weapon = Flame Repeater
|
mech.glaive-ship.weapon = Flame Repeater
|
||||||
item.corestorable = [lightgray]Storable in Core: {0}
|
item.corestorable = [lightgray]Säilöttävissä Ytimeen: {0}
|
||||||
item.explosiveness = [lightgray]Explosiveness: {0}%
|
item.explosiveness = [lightgray]Räjädysmäisyys: {0}%
|
||||||
item.flammability = [lightgray]Flammability: {0}%
|
item.flammability = [lightgray]Flammability: {0}%
|
||||||
item.radioactivity = [lightgray]Radioactivity: {0}%
|
item.radioactivity = [lightgray]Radioactivity: {0}%
|
||||||
unit.health = [lightgray]Health: {0}
|
unit.health = [lightgray]Elämäpisteet: {0}
|
||||||
unit.speed = [lightgray]Speed: {0}
|
unit.speed = [lightgray]Nopeus: {0}
|
||||||
mech.weapon = [lightgray]Weapon: {0}
|
mech.weapon = [lightgray]Ase: {0}
|
||||||
mech.health = [lightgray]Health: {0}
|
mech.health = [lightgray]Elämäpisteet: {0}
|
||||||
mech.itemcapacity = [lightgray]Item Capacity: {0}
|
mech.itemcapacity = [lightgray]Tavara Kapasiteetti: {0}
|
||||||
mech.minespeed = [lightgray]Mining Speed: {0}%
|
mech.minespeed = [lightgray]Louhimis Nopeus: {0}%
|
||||||
mech.minepower = [lightgray]Mining Power: {0}
|
mech.minepower = [lightgray]Louhimis Voima: {0}
|
||||||
mech.ability = [lightgray]Ability: {0}
|
mech.ability = [lightgray]Spesiaalikyky: {0}
|
||||||
mech.buildspeed = [lightgray]Building Speed: {0}%
|
mech.buildspeed = [lightgray]Rakennus Nopeus: {0}%
|
||||||
liquid.heatcapacity = [lightgray]Heat Capacity: {0}
|
liquid.heatcapacity = [lightgray]Lämpö Kapasiteetti: {0}
|
||||||
liquid.viscosity = [lightgray]Viscosity: {0}
|
liquid.viscosity = [lightgray]Tahmeus: {0}
|
||||||
liquid.temperature = [lightgray]Temperature: {0}
|
liquid.temperature = [lightgray]Lämpö: {0}
|
||||||
|
|
||||||
block.sand-boulder.name = Sand Boulder
|
block.sand-boulder.name = Sand Boulder
|
||||||
block.grass.name = Grass
|
block.grass.name = Grass
|
||||||
@@ -865,24 +865,24 @@ block.scrap-wall-huge.name = Huge Scrap Wall
|
|||||||
block.scrap-wall-gigantic.name = Gigantic Scrap Wall
|
block.scrap-wall-gigantic.name = Gigantic Scrap Wall
|
||||||
block.thruster.name = Thruster
|
block.thruster.name = Thruster
|
||||||
block.kiln.name = Kiln
|
block.kiln.name = Kiln
|
||||||
block.graphite-press.name = Graphite Press
|
block.graphite-press.name = Grafiitti Puristin
|
||||||
block.multi-press.name = Multi-Press
|
block.multi-press.name = Multi-Puristin
|
||||||
block.constructing = {0} [lightgray](Constructing)
|
block.constructing = {0} [lightgray](Rakentamassa)
|
||||||
block.spawn.name = Enemy Spawn
|
block.spawn.name = Vihollis Spawni
|
||||||
block.core-shard.name = Core: Shard
|
block.core-shard.name = Ydin: Siru
|
||||||
block.core-foundation.name = Core: Foundation
|
block.core-foundation.name = Ydin: Pohjaus
|
||||||
block.core-nucleus.name = Core: Nucleus
|
block.core-nucleus.name = Ydin: Tuma
|
||||||
block.deepwater.name = Deep Water
|
block.deepwater.name = Syvä Vesi
|
||||||
block.water.name = Water
|
block.water.name = Vesi
|
||||||
block.tainted-water.name = Tainted Water
|
block.tainted-water.name = Pilattu Vesi
|
||||||
block.darksand-tainted-water.name = Dark Sand Tainted Water
|
block.darksand-tainted-water.name = Dark Sand Tainted Water
|
||||||
block.tar.name = Tar
|
block.tar.name = Terva
|
||||||
block.stone.name = Stone
|
block.stone.name = Kivi
|
||||||
block.sand.name = Sand
|
block.sand.name = Hiekka
|
||||||
block.darksand.name = Dark Sand
|
block.darksand.name = Tumma Hiekka
|
||||||
block.ice.name = Ice
|
block.ice.name = Jää
|
||||||
block.snow.name = Snow
|
block.snow.name = Lumi
|
||||||
block.craters.name = Craters
|
block.craters.name = Kraatterit
|
||||||
block.sand-water.name = Sand water
|
block.sand-water.name = Sand water
|
||||||
block.darksand-water.name = Dark Sand Water
|
block.darksand-water.name = Dark Sand Water
|
||||||
block.char.name = Char
|
block.char.name = Char
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ credits = Crediti
|
|||||||
contributors = Traduttori e Contributori
|
contributors = Traduttori e Contributori
|
||||||
discord = Entra nel server Discord di Mindustry!
|
discord = Entra nel server Discord di Mindustry!
|
||||||
link.discord.description = La chatroom ufficiale del server Discord di Mindustry
|
link.discord.description = La chatroom ufficiale del server Discord di Mindustry
|
||||||
link.reddit.description = The Mindustry subreddit
|
link.reddit.description = Il subreddit di Mindustry!
|
||||||
link.github.description = Codice sorgente del gioco
|
link.github.description = Codice sorgente del gioco
|
||||||
link.changelog.description = Elenco delle modifiche del gioco
|
link.changelog.description = Elenco delle modifiche del gioco
|
||||||
link.dev-builds.description = Build di sviluppo versioni instabili
|
link.dev-builds.description = Build di sviluppo versioni instabili
|
||||||
@@ -40,7 +40,7 @@ schematic = Schematica
|
|||||||
schematic.add = Salva Schematica...
|
schematic.add = Salva Schematica...
|
||||||
schematics = Schematiche
|
schematics = Schematiche
|
||||||
schematic.replace = Una schematica con questo nome esiste già. Sostituirla?
|
schematic.replace = Una schematica con questo nome esiste già. Sostituirla?
|
||||||
schematic.import = Importa schematica...
|
schematic.import = Importa schematica
|
||||||
schematic.exportfile = Esporta File
|
schematic.exportfile = Esporta File
|
||||||
schematic.importfile = Importa File
|
schematic.importfile = Importa File
|
||||||
schematic.browseworkshop = Naviga nel Workshop
|
schematic.browseworkshop = Naviga nel Workshop
|
||||||
@@ -59,6 +59,7 @@ stat.built = Costruzioni Erette:[accent] {0}
|
|||||||
stat.destroyed = Costruzioni Distrutte:[accent] {0}
|
stat.destroyed = Costruzioni Distrutte:[accent] {0}
|
||||||
stat.deconstructed = Costruzioni Smantellate:[accent] {0}
|
stat.deconstructed = Costruzioni Smantellate:[accent] {0}
|
||||||
stat.delivered = Risorse Lanciate:
|
stat.delivered = Risorse Lanciate:
|
||||||
|
stat.playtime = Tempo Di Gioco:[accent] {0}
|
||||||
stat.rank = Livello Finale: [accent]{0}
|
stat.rank = Livello Finale: [accent]{0}
|
||||||
|
|
||||||
launcheditems = [accent]Oggetti Lanciati
|
launcheditems = [accent]Oggetti Lanciati
|
||||||
@@ -79,7 +80,7 @@ newgame = Nuova partita
|
|||||||
none = <niente>
|
none = <niente>
|
||||||
minimap = Minimappa
|
minimap = Minimappa
|
||||||
position = Posizione
|
position = Posizione
|
||||||
close = Chiuso
|
close = Chiudi
|
||||||
website = Sito Web
|
website = Sito Web
|
||||||
quit = Esci
|
quit = Esci
|
||||||
save.quit = Salva ed Esci
|
save.quit = Salva ed Esci
|
||||||
@@ -104,6 +105,7 @@ mods.none = [lightgray]Nessuna mod trovata!
|
|||||||
mods.guide = Guida per il modding
|
mods.guide = Guida per il modding
|
||||||
mods.report = Segnala un Bug
|
mods.report = Segnala un Bug
|
||||||
mods.openfolder = Apri Cartella Mods
|
mods.openfolder = Apri Cartella Mods
|
||||||
|
mod.display = [gray]Mod:[orange] {0}
|
||||||
mod.enabled = [lightgray]Abilitato
|
mod.enabled = [lightgray]Abilitato
|
||||||
mod.disabled = [scarlet]Disabilitato
|
mod.disabled = [scarlet]Disabilitato
|
||||||
mod.disable = Disabilita
|
mod.disable = Disabilita
|
||||||
@@ -138,12 +140,12 @@ research.list = [lightgray]Ricerca:
|
|||||||
research = Ricerca
|
research = Ricerca
|
||||||
researched = [lightgray]{0} cercati.
|
researched = [lightgray]{0} cercati.
|
||||||
players = {0} giocatori online
|
players = {0} giocatori online
|
||||||
players.single = {0} giocatori online
|
players.single = {0} giocatore online
|
||||||
server.closing = [accent]Chiusura server...
|
server.closing = [accent]Chiusura server...
|
||||||
server.kicked.kick = Sei stato cacciato dal server!
|
server.kicked.kick = Sei stato espulso dal server!
|
||||||
server.kicked.whitelist = Non sei presente nella whitelist.
|
server.kicked.whitelist = Non sei presente nella whitelist.
|
||||||
server.kicked.serverClose = Server chiuso.
|
server.kicked.serverClose = Server chiuso.
|
||||||
server.kicked.vote = Sei stato cacciato su richiesta dei giocatori. Tanti saluti.
|
server.kicked.vote = Sei stato espulso su richiesta dei giocatori. Tanti saluti.
|
||||||
server.kicked.clientOutdated = Versione del client obsoleta! Aggiorna il gioco!
|
server.kicked.clientOutdated = Versione del client obsoleta! Aggiorna il gioco!
|
||||||
server.kicked.serverOutdated = Server obsoleto! Chiedi all'host di aggiornare la versione del server!
|
server.kicked.serverOutdated = Server obsoleto! Chiedi all'host di aggiornare la versione del server!
|
||||||
server.kicked.banned = Sei stato bandito da questo server.
|
server.kicked.banned = Sei stato bandito da questo server.
|
||||||
@@ -157,7 +159,7 @@ server.kicked.customClient = Questo server non supporta i client personalizzati.
|
|||||||
server.kicked.gameover = Game over!
|
server.kicked.gameover = Game over!
|
||||||
server.kicked.serverRestarting = Il server si sta riavviando.
|
server.kicked.serverRestarting = Il server si sta riavviando.
|
||||||
server.versions = Versione client:[accent] {0}[]\nVersione server:[accent] {1}[]
|
server.versions = Versione client:[accent] {0}[]\nVersione server:[accent] {1}[]
|
||||||
host.info = Il pulsante [accent]Ospita[] ospita un server sulla porta [scarlet]6567[].[] Chiunque sulla stessa [lightgray]rete Wi-Fi o locale[] dovrebbe essere in grado di vedere il server nell'elenco server.\nSe vuoi che le persone siano in grado di connettersi ovunque tramite il tuo IP, è richiesto il [accent]port forwarding[].\n\n[lightgray]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.
|
host.info = Il pulsante [accent]Ospita[] ospita un server sulla porta [scarlet]6567[].[] Chiunque sulla stessa [lightgray]rete Wi-Fi o locale[] dovrebbe essere in grado di vedere il server nell'elenco server.\nSe vuoi che le persone siano in grado di connettersi ovunque tramite il tuo IP, è necessario eseguire il [accent]port forwarding[].\n\n[lightgray]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 l'[accent]IP del server[] a cui connettersi, o scoprire [accent]un server sulla rete locale[] disponibile.\nSono supportati sia il multiplayer LAN che WAN.\n\n[lightgray]Nota: non esiste un elenco automatico dei server globali; se desideri connetterti a qualcuno tramite il suo IP, è necessario chiedere all'host il proprio IP.
|
join.info = Qui è possibile inserire l'[accent]IP del server[] a cui connettersi, o scoprire [accent]un server sulla rete locale[] disponibile.\nSono supportati sia il multiplayer LAN che WAN.\n\n[lightgray]Nota: non esiste un elenco automatico dei server globali; se desideri connetterti a qualcuno tramite il suo IP, è necessario chiedere all'host il proprio IP.
|
||||||
hostserver = Ospita Server
|
hostserver = Ospita Server
|
||||||
invitefriends = Invita Amici
|
invitefriends = Invita Amici
|
||||||
@@ -199,7 +201,7 @@ joingame.ip = Indirizzo:
|
|||||||
disconnect = Disconnesso.
|
disconnect = Disconnesso.
|
||||||
disconnect.error = Errore di connessione.
|
disconnect.error = Errore di connessione.
|
||||||
disconnect.closed = Connessione chiusa.
|
disconnect.closed = Connessione chiusa.
|
||||||
disconnect.timeout = Timed out.
|
disconnect.timeout = Connessione scaduta.
|
||||||
disconnect.data = Errore durante il caricamento del mondo!
|
disconnect.data = Errore durante il caricamento del mondo!
|
||||||
cantconnect = Impossibile unirsi alla partita ([accent]{0}[]).
|
cantconnect = Impossibile unirsi alla partita ([accent]{0}[]).
|
||||||
connecting = [accent]Connessione in corso...
|
connecting = [accent]Connessione in corso...
|
||||||
@@ -251,6 +253,7 @@ copylink = Copia link
|
|||||||
back = Indietro
|
back = Indietro
|
||||||
data.export = Esporta Salvataggio
|
data.export = Esporta Salvataggio
|
||||||
data.import = Importa Salvataggio
|
data.import = Importa Salvataggio
|
||||||
|
data.openfolder = Apri Cartella\nSalvataggi
|
||||||
data.exported = Dati esportati.
|
data.exported = Dati esportati.
|
||||||
data.invalid = Questi non sono dati di gioco validi.
|
data.invalid = Questi non sono dati di gioco validi.
|
||||||
data.import.confirm = Importare dati di gioco esterni sovrascriverà[scarlet] tutti[] i tuoi progressi attuali.\n[accent]L'operazione è irreversibile![]\n\nUna volta importati i dati, il gioco si chiuderà immediatamente.
|
data.import.confirm = Importare dati di gioco esterni sovrascriverà[scarlet] tutti[] i tuoi progressi attuali.\n[accent]L'operazione è irreversibile![]\n\nUna volta importati i dati, il gioco si chiuderà immediatamente.
|
||||||
@@ -263,7 +266,7 @@ reloading = [accent]Ricaricamento delle mods...
|
|||||||
saving = [accent]Salvataggio in corso...
|
saving = [accent]Salvataggio in corso...
|
||||||
cancelbuilding = [accent][[{0}][] per pulire la selezione
|
cancelbuilding = [accent][[{0}][] per pulire la selezione
|
||||||
selectschematic = [accent][[{0}][] per selezionare+copiare
|
selectschematic = [accent][[{0}][] per selezionare+copiare
|
||||||
pausebuilding = [accent][[{0}][] to pause building
|
pausebuilding = [accent][[{0}][] per smettere di costruire
|
||||||
resumebuilding = [scarlet][[{0}][] per riprendere a costruire
|
resumebuilding = [scarlet][[{0}][] per riprendere a costruire
|
||||||
wave = [accent]Ondata {0}
|
wave = [accent]Ondata {0}
|
||||||
wave.waiting = [lightgray]Ondata tra {0}
|
wave.waiting = [lightgray]Ondata tra {0}
|
||||||
@@ -279,7 +282,7 @@ custom = Personalizzato
|
|||||||
builtin = Incluso
|
builtin = Incluso
|
||||||
map.delete.confirm = Sei sicuro di voler eliminare questa mappa? L'operazione è irreversibile!
|
map.delete.confirm = Sei sicuro di voler eliminare questa mappa? L'operazione è irreversibile!
|
||||||
map.random = [accent]Mappa casuale
|
map.random = [accent]Mappa casuale
|
||||||
map.nospawn = Questa mappa non possiede un Nucleo in cui spawnare! Aggiungine uno nell'editor.
|
map.nospawn = Questa mappa non possiede un Nucleo in cui generare! Aggiungine uno nell'editor.
|
||||||
map.nospawn.pvp = Questa mappa non ha un Nucleo Nemico! Aggiungi dei Nuclei nell'editor per poter giocare.
|
map.nospawn.pvp = Questa mappa non ha un Nucleo Nemico! Aggiungi dei Nuclei nell'editor per poter giocare.
|
||||||
map.nospawn.attack = Questa mappa non ha nessun Nucleo Nemico da poter attaccare! Aggiungi dei Nuclei Nemici nell'editor per poter giocare.
|
map.nospawn.attack = Questa mappa non ha nessun Nucleo Nemico da poter attaccare! Aggiungi dei Nuclei Nemici nell'editor per poter giocare.
|
||||||
map.invalid = Errore nel caricamento della mappa: file mappa corrotto o non valido.
|
map.invalid = Errore nel caricamento della mappa: file mappa corrotto o non valido.
|
||||||
@@ -464,7 +467,7 @@ zone.resources = [lightgray]Risorse Trovate:
|
|||||||
zone.objective = [lightgray]Obiettivo: [accent]{0}
|
zone.objective = [lightgray]Obiettivo: [accent]{0}
|
||||||
zone.objective.survival = Sopravvivere
|
zone.objective.survival = Sopravvivere
|
||||||
zone.objective.attack = Distruggere il Nucleo Nemico
|
zone.objective.attack = Distruggere il Nucleo Nemico
|
||||||
add = Aggiungi...
|
add = Aggiungi
|
||||||
boss.health = Vita del Boss
|
boss.health = Vita del Boss
|
||||||
|
|
||||||
connectfail = [crimson]Impossibile connettersi al server:\n\n[accent] {0}
|
connectfail = [crimson]Impossibile connettersi al server:\n\n[accent] {0}
|
||||||
@@ -531,6 +534,8 @@ error.crashtitle = Si è verificato un errore
|
|||||||
blocks.input = Ingresso
|
blocks.input = Ingresso
|
||||||
blocks.output = Uscita
|
blocks.output = Uscita
|
||||||
blocks.booster = Potenziamenti
|
blocks.booster = Potenziamenti
|
||||||
|
blocks.tiles = Blocchi Richiesti
|
||||||
|
blocks.affinities = Affinità
|
||||||
block.unknown = [lightgray]???
|
block.unknown = [lightgray]???
|
||||||
blocks.powercapacity = Capacità Energetica
|
blocks.powercapacity = Capacità Energetica
|
||||||
blocks.powershot = Danno/Colpo
|
blocks.powershot = Danno/Colpo
|
||||||
@@ -642,7 +647,7 @@ setting.difficulty.name = Difficoltà:
|
|||||||
setting.screenshake.name = Movimento dello Schermo
|
setting.screenshake.name = Movimento dello Schermo
|
||||||
setting.effects.name = Visualizza Effetti
|
setting.effects.name = Visualizza Effetti
|
||||||
setting.destroyedblocks.name = Mostra Blocchi Distrutti
|
setting.destroyedblocks.name = Mostra Blocchi Distrutti
|
||||||
setting.conveyorpathfinding.name = Conveyor Placement Pathfinding
|
setting.conveyorpathfinding.name = Posizionamento Nastri Trasportatori Intelligente
|
||||||
setting.coreselect.name = Consenti Schematiche dei Nuclei
|
setting.coreselect.name = Consenti Schematiche dei Nuclei
|
||||||
setting.sensitivity.name = Sensibilità del Controller
|
setting.sensitivity.name = Sensibilità del Controller
|
||||||
setting.saveinterval.name = Intervallo di Salvataggio Automatico
|
setting.saveinterval.name = Intervallo di Salvataggio Automatico
|
||||||
@@ -665,10 +670,11 @@ setting.mutesound.name = Silenzia Suoni
|
|||||||
setting.crashreport.name = Invia rapporti anonimi sugli arresti anomali
|
setting.crashreport.name = Invia rapporti anonimi sugli arresti anomali
|
||||||
setting.savecreate.name = Salvataggio Automatico
|
setting.savecreate.name = Salvataggio Automatico
|
||||||
setting.publichost.name = Gioco Visibile Pubblicamente
|
setting.publichost.name = Gioco Visibile Pubblicamente
|
||||||
|
setting.playerlimit.name = Limite Giocatori
|
||||||
setting.chatopacity.name = Opacità Chat
|
setting.chatopacity.name = Opacità Chat
|
||||||
setting.lasersopacity.name = Opacità Raggi Energetici
|
setting.lasersopacity.name = Opacità Raggi Energetici
|
||||||
setting.bridgeopacity.name = Opacità Nastri e Condotti Sopraelevati
|
setting.bridgeopacity.name = Opacità Nastri e Condotti Sopraelevati
|
||||||
setting.playerchat.name = Mostra Chat in-game
|
setting.playerchat.name = Mostra Chat
|
||||||
public.confirm = Vuoi rendere la tua partita pubblica?\n[accent]Chiunque sarà in grado di accedere alle tue partite.\n[lightgray]Questo può essere modificato più tardi in Impostazioni->Gioco->Partite Pubbliche.
|
public.confirm = Vuoi rendere la tua partita pubblica?\n[accent]Chiunque sarà in grado di accedere alle tue partite.\n[lightgray]Questo può essere modificato più tardi in Impostazioni->Gioco->Partite Pubbliche.
|
||||||
public.beta = Nota che le versioni beta del gioco non possono creare lobby pubbliche.
|
public.beta = Nota che le versioni beta del gioco non possono creare lobby pubbliche.
|
||||||
uiscale.reset = La scala dell'interfaccia utente è stata modificata.\nPremere 'OK' per confermare questa scala.\n[scarlet]Ripristina ed esci in [accent] {0}[] secondi...
|
uiscale.reset = La scala dell'interfaccia utente è stata modificata.\nPremere 'OK' per confermare questa scala.\n[scarlet]Ripristina ed esci in [accent] {0}[] secondi...
|
||||||
@@ -933,7 +939,6 @@ block.lancer.name = Lanciere
|
|||||||
block.conveyor.name = Nastro Trasportatore
|
block.conveyor.name = Nastro Trasportatore
|
||||||
block.titanium-conveyor.name = Nastro Avanzato
|
block.titanium-conveyor.name = Nastro Avanzato
|
||||||
block.armored-conveyor.name = Nastro Corazzato
|
block.armored-conveyor.name = Nastro Corazzato
|
||||||
block.armored-conveyor.description = Trasporta gli oggetti alla stessa velocità del nastro avanzato, ma è più resistente. Accetta input dai lati solo da altri nastri.
|
|
||||||
block.junction.name = Incrocio
|
block.junction.name = Incrocio
|
||||||
block.router.name = Distributore
|
block.router.name = Distributore
|
||||||
block.distributor.name = Distributore Grande
|
block.distributor.name = Distributore Grande
|
||||||
@@ -943,6 +948,7 @@ block.message.name = Messaggio
|
|||||||
block.illuminator.name = Lanterna
|
block.illuminator.name = Lanterna
|
||||||
block.illuminator.description = Una piccola, compatta sorgente di luce. Richiede energia per funzionare.
|
block.illuminator.description = Una piccola, compatta sorgente di luce. Richiede energia per funzionare.
|
||||||
block.overflow-gate.name = Separatore per Eccesso
|
block.overflow-gate.name = Separatore per Eccesso
|
||||||
|
block.underflow-gate.name = Separatore per Eccesso Inverso
|
||||||
block.silicon-smelter.name = Fonderia
|
block.silicon-smelter.name = Fonderia
|
||||||
block.phase-weaver.name = Tessitore di Fase
|
block.phase-weaver.name = Tessitore di Fase
|
||||||
block.pulverizer.name = Polverizzatore
|
block.pulverizer.name = Polverizzatore
|
||||||
@@ -1138,12 +1144,12 @@ block.spore-press.description = Comprime le spore in petrolio.
|
|||||||
block.pulverizer.description = Polverizza la pietra.\nUtile quando manca la sabbia naturale.
|
block.pulverizer.description = Polverizza la pietra.\nUtile quando manca la sabbia naturale.
|
||||||
block.coal-centrifuge.description = Solidifica il petrolio in pezzi di carbone.
|
block.coal-centrifuge.description = Solidifica il petrolio in pezzi di carbone.
|
||||||
block.incinerator.description = Elimina qualsiasi oggetto o liquido in eccesso.
|
block.incinerator.description = Elimina qualsiasi oggetto o liquido in eccesso.
|
||||||
block.power-void.description = Elimina tutta l'energia che riceve, esiste solo nella modalità creativa.
|
block.power-void.description = Elimina tutta l'energia che riceve. Esiste solo nella modalità creativa.
|
||||||
block.power-source.description = Produce energia infinita, esiste solo nella modalità creativa.
|
block.power-source.description = Produce energia infinita. Esiste solo nella modalità creativa.
|
||||||
block.item-source.description = Produce oggetti infiniti, esiste solo nella modalità creativa.
|
block.item-source.description = Produce oggetti infiniti. Esiste solo nella modalità creativa.
|
||||||
block.item-void.description = Elimina gli oggetti che vi entrano senza bisogno di energia, esiste solo nella modalità creativa.
|
block.item-void.description = Elimina gli oggetti che vi entrano senza bisogno di energia. Esiste solo nella modalità creativa.
|
||||||
block.liquid-source.description = Emette continuamente liquidi. Esiste solo nella modalità creativa.
|
block.liquid-source.description = Emette continuamente liquidi. Esiste solo nella modalità creativa.
|
||||||
block.liquid-void.description = Elimina i liquidi in entrata, esiste solo nella modalità creativa.
|
block.liquid-void.description = Elimina i liquidi in entrata. Esiste solo nella modalità creativa.
|
||||||
block.copper-wall.description = Un blocco difensivo economico.\nUtile per proteggere il Nucleo e le torrette nelle prime ondate.
|
block.copper-wall.description = Un blocco difensivo economico.\nUtile per proteggere il Nucleo e le torrette nelle prime ondate.
|
||||||
block.copper-wall-large.description = Un blocco difensivo economico.\nUtile per proteggere il Nucleo e le torrette nelle prime ondate.\nOccupa più blocchi.
|
block.copper-wall-large.description = Un blocco difensivo economico.\nUtile per proteggere il Nucleo e le torrette nelle prime ondate.\nOccupa più blocchi.
|
||||||
block.titanium-wall.description = Un blocco difensivo moderatamente forte.\nFornisce una protezione moderata dai nemici.
|
block.titanium-wall.description = Un blocco difensivo moderatamente forte.\nFornisce una protezione moderata dai nemici.
|
||||||
@@ -1163,17 +1169,19 @@ block.mend-projector.description = Ripara periodicamente blocchi nelle vicinanze
|
|||||||
block.overdrive-projector.description = Aumenta la velocità di edifici vicini come trivelle e nastri trasportatori.
|
block.overdrive-projector.description = Aumenta la velocità di edifici vicini come trivelle e nastri trasportatori.
|
||||||
block.force-projector.description = Crea un campo di forza esagonale attorno a sé, proteggendo gli edifici e le unità all'interno da danni causati da proiettili.
|
block.force-projector.description = Crea un campo di forza esagonale attorno a sé, proteggendo gli edifici e le unità all'interno da danni causati da proiettili.
|
||||||
block.shock-mine.description = Danneggia i nemici che la calpestano. Quasi invisibile al nemico.
|
block.shock-mine.description = Danneggia i nemici che la calpestano. Quasi invisibile al nemico.
|
||||||
block.conveyor.description = Nastro di base. Sposta gli oggetti in avanti e li deposita automaticamente in altri blocchi. Ruotabile.
|
block.conveyor.description = Nastro di base. Sposta gli oggetti in avanti e li deposita automaticamente in altri blocchi. È rotabile.
|
||||||
block.titanium-conveyor.description = Nastro avanzato. Sposta gli oggetti più velocemente dei nastri standard.
|
block.titanium-conveyor.description = Nastro avanzato. Sposta gli oggetti più velocemente dei nastri standard.
|
||||||
|
block.armored-conveyor.description = Trasporta gli oggetti alla stessa velocità del nastro avanzato, ma è più resistente. Accetta input dai lati solo da altri nastri.
|
||||||
block.junction.description = Permette di incrociare nastri che trasportano materiali diversi in posizioni diverse.
|
block.junction.description = Permette di incrociare nastri che trasportano materiali diversi in posizioni diverse.
|
||||||
block.bridge-conveyor.description = Consente il trasporto di oggetti fino a 3 blocchi ad un altro nastro sopraelevato.\nPuò passare sopra ad altri blocchi od edifici.
|
block.bridge-conveyor.description = Consente il trasporto di oggetti fino a 3 blocchi ad un altro nastro sopraelevato.\nPuò passare sopra ad altri blocchi od edifici.
|
||||||
block.phase-conveyor.description = Nastro avanzato. Consuma energia per teletrasportare gli oggetti su un altro nastro di fase collegato.
|
block.phase-conveyor.description = Nastro avanzato. Consuma energia per teletrasportare gli oggetti su un altro nastro di fase collegato.
|
||||||
block.sorter.description = Divide gli oggetti. Se l'oggetto corrisponde a quello selezionato, Può passare. Altrimenti viene espulso sui lati.
|
block.sorter.description = Divide gli oggetti. Se l'oggetto corrisponde a quello selezionato, Può passare. Altrimenti viene espulso sui lati.
|
||||||
block.inverted-sorter.description = Elabora gli oggetti come uno smistatore standard, ma in uscita dà gli elementi selezionati ai lati.
|
block.inverted-sorter.description = Elabora gli oggetti come uno filtro standard, ma in uscita dà gli elementi selezionati ai lati.
|
||||||
block.router.description = Accetta gli elementi da una direzione e li emette fino a 3 altre direzioni allo stesso modo. Utile per suddividere i materiali da una fonte a più destinazioni.
|
block.router.description = Accetta gli elementi da una direzione e li emette fino a 3 altre direzioni allo stesso modo. Utile per suddividere i materiali da una fonte a più destinazioni.
|
||||||
block.distributor.description = Un distributore avanzato che divide gli oggetti in altre 7 direzioni allo stesso modo.
|
block.distributor.description = Un distributore avanzato che divide gli oggetti in altre 7 direzioni allo stesso modo.
|
||||||
block.overflow-gate.description = Una combinazione di un incrocio e di un distributore, che distribuisce sui suoi lati se in nastro difronte si satura.
|
block.overflow-gate.description = Distribuisce gli oggetti ai lati se il nastro davanti a sé è saturo.
|
||||||
block.mass-driver.description = Ultimo blocco di trasporto di oggetti. Raccoglie diversi oggetti e poi li spara su un'altra Lìlancia materiali a lungo raggio.
|
block.underflow-gate.description = L'opposto di un separatore per eccesso. Distribuisce gli oggetti nel nastro davanti a sé se i nastri a destra e a sinistra sono saturi.
|
||||||
|
block.mass-driver.description = Ultimo blocco di trasporto di oggetti. Raccoglie diversi oggetti e poi li spara su un'altra lancia materiali a lungo raggio.
|
||||||
block.mechanical-pump.description = Una pompa economica a bassa efficienza, ma nessun consumo di energia.
|
block.mechanical-pump.description = Una pompa economica a bassa efficienza, ma nessun consumo di energia.
|
||||||
block.rotary-pump.description = Una pompa avanzata che raddoppia la velocità consumando energia.
|
block.rotary-pump.description = Una pompa avanzata che raddoppia la velocità consumando energia.
|
||||||
block.thermal-pump.description = La pompa migliore. Tre volte più veloce di una pompa meccanica e l'unica pompa in grado di recuperare la lava.
|
block.thermal-pump.description = La pompa migliore. Tre volte più veloce di una pompa meccanica e l'unica pompa in grado di recuperare la lava.
|
||||||
|
|||||||
@@ -29,12 +29,12 @@ load.system = 시스템
|
|||||||
load.mod = 모드
|
load.mod = 모드
|
||||||
load.scripts = 스크립트
|
load.scripts = 스크립트
|
||||||
|
|
||||||
be.update = A new Bleeding Edge build is available:
|
be.update = 새로운 블리딩 엣지 버전이 출시되었습니다.
|
||||||
be.update.confirm = Download it and restart now?
|
be.update.confirm = 다운로드 후 게임을 재시작하시겠습니까?
|
||||||
be.updating = Updating...
|
be.updating = 업데이트 중...
|
||||||
be.ignore = Ignore
|
be.ignore = 무시
|
||||||
be.noupdates = No updates found.
|
be.noupdates = 새로운 업데이트가 발견되지 않았습니다.
|
||||||
be.check = Check for updates
|
be.check = 업데이트 확인
|
||||||
|
|
||||||
schematic = 설계도
|
schematic = 설계도
|
||||||
schematic.add = 설계도 저장하기
|
schematic.add = 설계도 저장하기
|
||||||
@@ -98,15 +98,15 @@ done = 완료
|
|||||||
feature.unsupported = 당신의 기기는 이 기능을 지원하지 않습니다.
|
feature.unsupported = 당신의 기기는 이 기능을 지원하지 않습니다.
|
||||||
|
|
||||||
mods.alphainfo = 현재의 모드는 첫 번째 시도이며, 그리고[scarlet] 버그가 매우 많음을 명심하십시오[].\n만약 버그를 발견할경우 Mindustry 깃허브 또는 디스코드로 제보해주세요.
|
mods.alphainfo = 현재의 모드는 첫 번째 시도이며, 그리고[scarlet] 버그가 매우 많음을 명심하십시오[].\n만약 버그를 발견할경우 Mindustry 깃허브 또는 디스코드로 제보해주세요.
|
||||||
mods.alpha = [scarlet](Alpha)
|
mods.alpha=[accent](시험적 기능)
|
||||||
mods = 모드
|
mods = 모드
|
||||||
mods.none = [LIGHT_GRAY]추가한 모드가 없습니다!
|
mods.none = [LIGHT_GRAY]추가한 모드가 없습니다!
|
||||||
mods.guide = 모드 가이드
|
mods.guide = 모드 가이드
|
||||||
mods.report = 문제 신고
|
mods.report = 문제 신고
|
||||||
mods.openfolder = 모드 폴더 열기
|
mods.openfolder = 모드 폴더 열기
|
||||||
mod.enabled = [lightgray]활성화
|
mod.enabled=[blue]활성화
|
||||||
mod.disabled = [scarlet]비활성화
|
mod.disabled=[scarlet]적용 안됨
|
||||||
mod.disable = 비활성화
|
mod.disable=[lightgray]비활성화
|
||||||
mod.delete.error = 모드를 삭제할 수 없습니다. 아마도 해당 모드가 사용중인 것 같습니다.
|
mod.delete.error = 모드를 삭제할 수 없습니다. 아마도 해당 모드가 사용중인 것 같습니다.
|
||||||
mod.requiresversion = [scarlet]게임의 버전이 낮아 모드를 활성화할 수 없습니다!\n[scarlet]요구되는 게임 버전 : [accent]{0}
|
mod.requiresversion = [scarlet]게임의 버전이 낮아 모드를 활성화할 수 없습니다!\n[scarlet]요구되는 게임 버전 : [accent]{0}
|
||||||
mod.missingdependencies = [scarlet]의존되는 모드: {0}
|
mod.missingdependencies = [scarlet]의존되는 모드: {0}
|
||||||
@@ -114,7 +114,7 @@ mod.erroredcontent = [scarlet]컨텐츠 오류
|
|||||||
mod.errors = 컨텐츠를 불러오는 중 오류가 발생하였습니다.
|
mod.errors = 컨텐츠를 불러오는 중 오류가 발생하였습니다.
|
||||||
mod.noerrorplay = [scarlet]모드에 오류가 존재합니다.[] 해당 오류가 발생하는 모드를 비활성화하거나 모드의 오류를 고친 후 플레이가 가능합니다.
|
mod.noerrorplay = [scarlet]모드에 오류가 존재합니다.[] 해당 오류가 발생하는 모드를 비활성화하거나 모드의 오류를 고친 후 플레이가 가능합니다.
|
||||||
mod.nowdisabled = [scarlet]모드 '{0}'는 다음의 모드에 의존합니다 : [accent] {1}\n[lightgray]이 모드를 먼저 다운로드해야합니다.\n이 모드는 자동으로 비활성화됩니다.
|
mod.nowdisabled = [scarlet]모드 '{0}'는 다음의 모드에 의존합니다 : [accent] {1}\n[lightgray]이 모드를 먼저 다운로드해야합니다.\n이 모드는 자동으로 비활성화됩니다.
|
||||||
mod.enable = 활성화
|
mod.enable=활성화
|
||||||
mod.requiresrestart = 모드 변경사항을 적용하기 위해 게임을 종료합니다.
|
mod.requiresrestart = 모드 변경사항을 적용하기 위해 게임을 종료합니다.
|
||||||
mod.reloadrequired = [scarlet]새로고침 예정됨
|
mod.reloadrequired = [scarlet]새로고침 예정됨
|
||||||
mod.import = 모드 추가
|
mod.import = 모드 추가
|
||||||
@@ -123,8 +123,8 @@ mod.item.remove = 이것은 모드[accent] '{0}'[]의 자원입니다. 이 자
|
|||||||
mod.remove.confirm = 이 모드를 삭제하시겠습니까?
|
mod.remove.confirm = 이 모드를 삭제하시겠습니까?
|
||||||
mod.author = [LIGHT_GRAY]제작자 : [] {0}
|
mod.author = [LIGHT_GRAY]제작자 : [] {0}
|
||||||
mod.missing = 이 세이브파일에는 설치하지 않은 모드 혹은 현재 버전에 속해있지 않은 데이터가 포함되어 있습니다. 이 파일을 불러올 경우 세이브파일의 데이터가 손상될 수 있습니다. 정말로 이 파일을 불러오시겠습니까?\n[lightgray]모드 :\n{0}
|
mod.missing = 이 세이브파일에는 설치하지 않은 모드 혹은 현재 버전에 속해있지 않은 데이터가 포함되어 있습니다. 이 파일을 불러올 경우 세이브파일의 데이터가 손상될 수 있습니다. 정말로 이 파일을 불러오시겠습니까?\n[lightgray]모드 :\n{0}
|
||||||
mod.preview.missing = 워크샵에 당신의 모드를 업로드하기 전에 미리보기 이미지를 먼저 추가해야합니다.\n[accent] preview.png[]라는 이름으로 미리보기 이미지를 당신의 모드 폴더안에 준비한 후 다시 시도해주세요.
|
mod.preview.missing=Workshop에 당신의 모드를 업로드하기 전에 미리보기 이미지를 먼저 추가해야합니다.\n[accent] preview.png[]라는 이름으로 미리보기 이미지를 당신의 모드 폴더안에 준비한 후 다시 시도해주세요.
|
||||||
mod.folder.missing = 워크샵에는 폴더 형태의 모드만 게시할 수 있습니다.\n모드를 폴더 형태로 바꾸려면 파일을 폴더에 압축 해제하고 이전 압축파일을 제거한 후, 게임을 재시작하거나 모드를 다시 로드하십시오.
|
mod.folder.missing=Workshop에는 폴더 형태의 모드만 게시할 수 있습니다.\n모드를 폴더 형태로 바꾸려면 파일을 폴더에 압축 해제하고 이전 압축파일을 제거한 후, 게임을 재시작하거나 모드를 다시 로드하십시오.
|
||||||
mod.scripts.unsupported = 당신의 기기는 모드스크립트를 지원하지 않습니다. 모드의 일부 기능이 작동하지 않을 수 있습니다.
|
mod.scripts.unsupported = 당신의 기기는 모드스크립트를 지원하지 않습니다. 모드의 일부 기능이 작동하지 않을 수 있습니다.
|
||||||
|
|
||||||
about.button = 정보
|
about.button = 정보
|
||||||
@@ -155,10 +155,10 @@ server.kicked.nameEmpty = 당신의 닉네임이 비어있습니다.
|
|||||||
server.kicked.idInUse = 이미 서버에 접속중입니다! 다중 계정은 허용되지 않습니다.
|
server.kicked.idInUse = 이미 서버에 접속중입니다! 다중 계정은 허용되지 않습니다.
|
||||||
server.kicked.customClient = 이 서버는 직접 빌드한 버전을 지원하지 않습니다. 공식 버전을 사용하세요.
|
server.kicked.customClient = 이 서버는 직접 빌드한 버전을 지원하지 않습니다. 공식 버전을 사용하세요.
|
||||||
server.kicked.gameover = 코어가 파괴되었습니다...
|
server.kicked.gameover = 코어가 파괴되었습니다...
|
||||||
server.kicked.serverRestarting = The server is restarting.
|
server.kicked.serverRestarting = 서버가 재시작합니다.
|
||||||
server.versions = 클라이언트 버전 : [accent] {0}[]\n서버 버전 : [accent] {1}[]
|
server.versions = 클라이언트 버전 : [accent] {0}[]\n서버 버전 : [accent] {1}[]
|
||||||
host.info = [accent]호스트[] 버튼은 현재 네트워크의 [scarlet]6567[] 포트를 사용합니다.\n[LIGHT_GRAY]같은 Wi-Fi 또는 로컬 네트워크[] 에서 서버 목록을 볼 수 있습니다.\n\n만약 플레이어들이 이 IP를 통해 어디에서나 연결할 수 있게 하고 싶다면, 공유기 설정에서 [accent]포트 포워딩[]을 하시거나 VPN을 사용하셔야 합니다.\n\n[LIGHT_GRAY]참고: LAN 게임 연결에 문제가 있는 사람이 있다면, 방화벽 설정에서 Mindustry 가 로컬 네트워크에 액세스하도록 허용했는지 확인해주세요.
|
host.info = [accent]호스트[] 버튼은 현재 네트워크의 [scarlet]6567[] 포트를 사용합니다.\n[LIGHT_GRAY]같은 Wi-Fi 또는 로컬 네트워크[] 에서 서버 목록을 볼 수 있습니다.\n\n만약 플레이어들이 이 IP를 통해 어디에서나 연결할 수 있게 하고 싶다면, 공유기 설정에서 [accent]포트 포워딩[]을 하시거나 VPN을 사용하셔야 합니다.\n\n[LIGHT_GRAY]참고: LAN 게임 연결에 문제가 있는 사람이 있다면, 방화벽 설정에서 Mindustry 가 로컬 네트워크에 액세스하도록 허용했는지 확인해주세요.
|
||||||
join.info = 여기서 서버 추가를 누르신 후, [accent]서버 IP[]를 입력하여 다른 서버에 접속할 수 있습니다.\n또는 [accent]로컬 네트워크(LAN)[] 서버를 검색하여 접속할 수 있습니다.\nLAN 및 WAN 멀티 플레이어 모두 지원합니다.\n\n[LIGHT_GRAY]참고:여기에서는 자동으로 글로벌 서버를 추가하지 않습니다. IP로 다른 사람의 서버에 접속하려면 직접 서버 주소를 찾아서 적으셔야합니다.[]\n\n[ROYAL]한국의 서버로는 [accent]mindustry.kr[]의 6567, 6568포트와 [accent]server1.mindustry.r-e.kr[]의 8000, 8002 포트가 있습니다.\n서버 주소 입력방법은 < 주소:포트 >의 형식입니다.\n[royal]포트가 없을 시에는 그냥 주소만 입력하시면 됩니다.\n\n[royal]예시) mindustry.kr의 6567포트\nmindustry.kr:6567\n포트가 6567일 경우에는 :6567을 생략할 수 있습니다.
|
join.info = 여기서 서버 추가를 누르신 후, [accent]서버 IP[]를 입력하여 다른 서버에 접속할 수 있습니다.\n또는 [accent]로컬 네트워크(LAN)[] 서버를 검색하여 접속할 수 있습니다.\nLAN 및 WAN 멀티 플레이어 모두 지원합니다.\n\n[LIGHT_GRAY]참고:여기에서는 자동으로 글로벌 서버를 추가하지 않습니다. IP로 다른 사람의 서버에 접속하려면 직접 서버 주소를 찾아서 적으셔야합니다.[]\n\n[ROYAL]한국의 서버로는 [accent]mindustry.kr[]가 있습니다.\n서버 주소 입력방법은 < 주소:포트 >의 형식입니다.\n[royal]포트가 없을 시에는 그냥 주소만 입력하시면 됩니다.\n\n[royal]예시) mindustry.kr의 6567포트\nmindustry.kr:6567\n포트가 6567일 경우에는 :6567을 생략할 수 있습니다.
|
||||||
hostserver = 서버 열기
|
hostserver = 서버 열기
|
||||||
invitefriends = 친구 초대
|
invitefriends = 친구 초대
|
||||||
hostserver.mobile = 서버\n열기
|
hostserver.mobile = 서버\n열기
|
||||||
@@ -240,8 +240,8 @@ save.playtime = 플레이타임 : {0}
|
|||||||
warning = 경고.
|
warning = 경고.
|
||||||
confirm = 확인
|
confirm = 확인
|
||||||
delete = 삭제
|
delete = 삭제
|
||||||
view.workshop = 워크샵에서 보기
|
view.workshop=Workshop에서 보기
|
||||||
workshop.listing = 워크샵 목록 편집하기
|
workshop.listing=Workshop 목록 편집하기
|
||||||
ok = 확인
|
ok = 확인
|
||||||
open = 열기
|
open = 열기
|
||||||
customize = 맞춤설정
|
customize = 맞춤설정
|
||||||
@@ -310,7 +310,7 @@ editor.generation = 맵 생성 설정 :
|
|||||||
editor.ingame = 인게임 편집
|
editor.ingame = 인게임 편집
|
||||||
editor.publish.workshop = 워크샵 업로드
|
editor.publish.workshop = 워크샵 업로드
|
||||||
editor.newmap = 신규 맵
|
editor.newmap = 신규 맵
|
||||||
workshop = 워크샵
|
workshop=Workshop
|
||||||
waves.title = 단계
|
waves.title = 단계
|
||||||
waves.remove = 삭제
|
waves.remove = 삭제
|
||||||
waves.never = 여기까지 유닛생성
|
waves.never = 여기까지 유닛생성
|
||||||
@@ -377,15 +377,15 @@ toolmode.replace = 재배치
|
|||||||
toolmode.replace.description = 블록을 배치합니다.
|
toolmode.replace.description = 블록을 배치합니다.
|
||||||
toolmode.replaceall = 모두 재배치
|
toolmode.replaceall = 모두 재배치
|
||||||
toolmode.replaceall.description = 맵에 있는 모든 블록을 재배치합니다.
|
toolmode.replaceall.description = 맵에 있는 모든 블록을 재배치합니다.
|
||||||
toolmode.orthogonal = 직교
|
toolmode.orthogonal=직각
|
||||||
toolmode.orthogonal.description = 직교로 블록을 배치합니다.
|
toolmode.orthogonal.description=직각으로 블록을 배치합니다.
|
||||||
toolmode.square = 정사각형
|
toolmode.square = 정사각형
|
||||||
toolmode.square.description = 정사각형 형태의 브러시.
|
toolmode.square.description = 정사각형 형태의 브러시.
|
||||||
toolmode.eraseores = 자원 초기화
|
toolmode.eraseores = 자원 초기화
|
||||||
toolmode.eraseores.description = 자원만 초기화합니다.
|
toolmode.eraseores.description = 자원만 초기화합니다.
|
||||||
toolmode.fillteams = 팀 채우기
|
toolmode.fillteams = 팀 채우기
|
||||||
toolmode.fillteams.description = 블록 대신 팀 건물로 채웁니다.
|
toolmode.fillteams.description = 블록 대신 팀 건물로 채웁니다.
|
||||||
toolmode.drawteams = 팀 그리기
|
toolmode.drawteams=팀 색상으로 그리기
|
||||||
toolmode.drawteams.description = 블록 대신 팀 건물을 배치합니다.
|
toolmode.drawteams.description = 블록 대신 팀 건물을 배치합니다.
|
||||||
|
|
||||||
filters.empty = [LIGHT_GRAY]필터가 없습니다!! 아래 버튼을 눌러 추가하세요.
|
filters.empty = [LIGHT_GRAY]필터가 없습니다!! 아래 버튼을 눌러 추가하세요.
|
||||||
@@ -580,7 +580,7 @@ bar.power = 전력
|
|||||||
bar.progress = 생산 진행도
|
bar.progress = 생산 진행도
|
||||||
bar.spawned = 최대 {1}기 중 {0}기 생산됨
|
bar.spawned = 최대 {1}기 중 {0}기 생산됨
|
||||||
bar.input = 입력
|
bar.input = 입력
|
||||||
bar.output = Output
|
bar.output =
|
||||||
|
|
||||||
bullet.damage = [lightgray]피해량 : [stat]{0}[]
|
bullet.damage = [lightgray]피해량 : [stat]{0}[]
|
||||||
bullet.splashdamage = [lightgray]범위 피해량 : [stat]{0}[] / [lightgray]피해 범위 : [stat]{1}[lightgray] 타일
|
bullet.splashdamage = [lightgray]범위 피해량 : [stat]{0}[] / [lightgray]피해 범위 : [stat]{1}[lightgray] 타일
|
||||||
@@ -643,7 +643,7 @@ setting.screenshake.name = 화면 흔들기
|
|||||||
setting.effects.name = 화면 효과
|
setting.effects.name = 화면 효과
|
||||||
setting.destroyedblocks.name = 부서진 블럭 표시
|
setting.destroyedblocks.name = 부서진 블럭 표시
|
||||||
setting.conveyorpathfinding.name = 교차기 자동 설치
|
setting.conveyorpathfinding.name = 교차기 자동 설치
|
||||||
setting.coreselect.name = Allow Schematic Cores
|
setting.coreselect.name = Schematic Cores 켜기
|
||||||
setting.sensitivity.name = 컨트롤러 감도
|
setting.sensitivity.name = 컨트롤러 감도
|
||||||
setting.saveinterval.name = 저장 간격
|
setting.saveinterval.name = 저장 간격
|
||||||
setting.seconds = {0} 초
|
setting.seconds = {0} 초
|
||||||
@@ -690,7 +690,7 @@ keybind.toggle_power_lines.name = 전력 라인 허용
|
|||||||
keybind.move_x.name = 오른쪽 / 왼쪽 이동
|
keybind.move_x.name = 오른쪽 / 왼쪽 이동
|
||||||
keybind.move_y.name = 위 / 아래 이동
|
keybind.move_y.name = 위 / 아래 이동
|
||||||
keybind.mouse_move.name = 커서를 따라서 이동
|
keybind.mouse_move.name = 커서를 따라서 이동
|
||||||
keybind.dash.name = 달리기
|
keybind.dash.name = 부스터
|
||||||
keybind.schematic_select.name = 영역 설정
|
keybind.schematic_select.name = 영역 설정
|
||||||
keybind.schematic_menu.name = 설계도 메뉴
|
keybind.schematic_menu.name = 설계도 메뉴
|
||||||
keybind.schematic_flip_x.name = 설계도 X축 뒤집기
|
keybind.schematic_flip_x.name = 설계도 X축 뒤집기
|
||||||
@@ -755,7 +755,7 @@ rules.enemyCheat = 무한한 적 자원
|
|||||||
rules.unitdrops = 유닛 처치시 자원 약탈
|
rules.unitdrops = 유닛 처치시 자원 약탈
|
||||||
rules.unitbuildspeedmultiplier = 유닛 제조속도 배수
|
rules.unitbuildspeedmultiplier = 유닛 제조속도 배수
|
||||||
rules.unithealthmultiplier = 유닛 체력 배수
|
rules.unithealthmultiplier = 유닛 체력 배수
|
||||||
rules.blockhealthmultiplier = Block Health Multiplier
|
rules.blockhealthmultiplier = 건물 체력 배수
|
||||||
rules.playerhealthmultiplier = 플레이어 체력 배수
|
rules.playerhealthmultiplier = 플레이어 체력 배수
|
||||||
rules.playerdamagemultiplier = 플레이어 공격력 배수
|
rules.playerdamagemultiplier = 플레이어 공격력 배수
|
||||||
rules.unitdamagemultiplier = 유닛 공격력 배수
|
rules.unitdamagemultiplier = 유닛 공격력 배수
|
||||||
@@ -978,7 +978,6 @@ block.mechanical-pump.name = 기계식 펌프
|
|||||||
block.item-source.name = 아이템 소스
|
block.item-source.name = 아이템 소스
|
||||||
block.item-void.name = 아이템 삭제 장치
|
block.item-void.name = 아이템 삭제 장치
|
||||||
block.liquid-source.name = 무한 액체공급 장치
|
block.liquid-source.name = 무한 액체공급 장치
|
||||||
block.liquid-void.name = Liquid Void
|
|
||||||
block.power-void.name = 방전장치
|
block.power-void.name = 방전장치
|
||||||
block.power-source.name = 무한 전력공급 장치
|
block.power-source.name = 무한 전력공급 장치
|
||||||
block.unloader.name = 언로더
|
block.unloader.name = 언로더
|
||||||
@@ -1142,7 +1141,6 @@ block.power-source.description = 무한한 전력을 공급해주는 블록입
|
|||||||
block.item-source.description = 자원을 선택하면 그 자원이 무한하게 생성되는 블록입니다.\n샌드박스에서만 건설가능.
|
block.item-source.description = 자원을 선택하면 그 자원이 무한하게 생성되는 블록입니다.\n샌드박스에서만 건설가능.
|
||||||
block.item-void.description = 자원을 사라지게 만듭니다.\n샌드박스에서만 건설가능.
|
block.item-void.description = 자원을 사라지게 만듭니다.\n샌드박스에서만 건설가능.
|
||||||
block.liquid-source.description = 무한한 액체를 출력합니다.\n샌드박스에서만 건설가능.
|
block.liquid-source.description = 무한한 액체를 출력합니다.\n샌드박스에서만 건설가능.
|
||||||
block.liquid-void.description = Removes any liquids. Sandbox only.
|
|
||||||
block.copper-wall.description = 게임 시작 초기에 방어용으로 적합합니다.
|
block.copper-wall.description = 게임 시작 초기에 방어용으로 적합합니다.
|
||||||
block.copper-wall-large.description = 구리 벽 4개를 뭉친 블럭입니다.
|
block.copper-wall-large.description = 구리 벽 4개를 뭉친 블럭입니다.
|
||||||
block.titanium-wall.description = 흑연이 생산될 즈음에 사용하기 적합합니다.
|
block.titanium-wall.description = 흑연이 생산될 즈음에 사용하기 적합합니다.
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ schematic = Blauwdruk
|
|||||||
schematic.add = Bewaar blauwdruk...
|
schematic.add = Bewaar blauwdruk...
|
||||||
schematics = Blauwdrukken
|
schematics = Blauwdrukken
|
||||||
schematic.replace = Er bestaat al een blauwdruk met die naam. Overschrijven?
|
schematic.replace = Er bestaat al een blauwdruk met die naam. Overschrijven?
|
||||||
schematic.import = Importeer blauwdrul...
|
schematic.import = Importeer blauwdruk...
|
||||||
schematic.exportfile = Exporteer bestand
|
schematic.exportfile = Exporteer bestand
|
||||||
schematic.importfile = Importeer bestand
|
schematic.importfile = Importeer bestand
|
||||||
schematic.browseworkshop = Blader Werkplaats
|
schematic.browseworkshop = Blader Werkplaats
|
||||||
@@ -245,13 +245,13 @@ workshop.listing = Bewerk Workshop vermelding
|
|||||||
ok = Oke
|
ok = Oke
|
||||||
open = Open
|
open = Open
|
||||||
customize = Aanpassen
|
customize = Aanpassen
|
||||||
cancel = Anuleer
|
cancel = Annuleer
|
||||||
openlink = Open Link
|
openlink = Open Link
|
||||||
copylink = Customize Link
|
copylink = Customize Link
|
||||||
back = Teru
|
back = Terug
|
||||||
data.export = Exporteer Data
|
data.export = Exporteer Data
|
||||||
data.import = Importeer Data
|
data.import = Importeer Data
|
||||||
data.exported = Data Geexporteerd.
|
data.exported = Data Geëxporteerd.
|
||||||
data.invalid = Dit is geen geldige game data.
|
data.invalid = Dit is geen geldige game data.
|
||||||
data.import.confirm = Importeren van data verwijderd[scarlet] alle[] huidige data.\n[accent]Dit kan niet ongedaan worden gemaakt![]\n\nWanneer de data is geimport herstart deze game automatisch.
|
data.import.confirm = Importeren van data verwijderd[scarlet] alle[] huidige data.\n[accent]Dit kan niet ongedaan worden gemaakt![]\n\nWanneer de data is geimport herstart deze game automatisch.
|
||||||
classic.export = Exporteer klassieke data
|
classic.export = Exporteer klassieke data
|
||||||
@@ -450,7 +450,7 @@ launch.title = Lancering Sucessvol
|
|||||||
launch.next = [LIGHT_GRAY]volgende lanceerkans in ronde {0}
|
launch.next = [LIGHT_GRAY]volgende lanceerkans in ronde {0}
|
||||||
launch.unable2 = [scarlet]Lanceren niet mogelijk.[]
|
launch.unable2 = [scarlet]Lanceren niet mogelijk.[]
|
||||||
launch.confirm = Dit lanceert alle items in je core.\nJe zal niet meer terug kunnen keren naar deze basis.
|
launch.confirm = Dit lanceert alle items in je core.\nJe zal niet meer terug kunnen keren naar deze basis.
|
||||||
launch.skip.confirm = Als je nu niet lanceert, zul je moeten wachten tot het wel weer kan.
|
launch.skip.confirm = Als je nu niet lanceert zul je moeten wachten tot de volgende mogelijkheid.
|
||||||
uncover = Ontdek
|
uncover = Ontdek
|
||||||
configure = Configureer startinventaris
|
configure = Configureer startinventaris
|
||||||
bannedblocks = Verboden Blokken
|
bannedblocks = Verboden Blokken
|
||||||
@@ -532,21 +532,21 @@ blocks.input = Input
|
|||||||
blocks.output = Output
|
blocks.output = Output
|
||||||
blocks.booster = Booster
|
blocks.booster = Booster
|
||||||
block.unknown = [LIGHT_GRAY]???
|
block.unknown = [LIGHT_GRAY]???
|
||||||
blocks.powercapacity = Stroom Capaciteit
|
blocks.powercapacity = Stroomcapaciteit
|
||||||
blocks.powershot = Stroom/Shot
|
blocks.powershot = Stroom/Schot
|
||||||
blocks.damage = Damage
|
blocks.damage = Schade
|
||||||
blocks.targetsair = Luchtdoelwitten
|
blocks.targetsair = Luchtdoelwitten
|
||||||
blocks.targetsground = Gronddoelwitten
|
blocks.targetsground = Gronddoelwitten
|
||||||
blocks.itemsmoved = Beweegsnelheid
|
blocks.itemsmoved = Beweegsnelheid
|
||||||
blocks.launchtime = Tijd tussen lanceringen
|
blocks.launchtime = Tijd tussen lanceringen
|
||||||
blocks.shootrange = Bereik
|
blocks.shootrange = Bereik
|
||||||
blocks.size = Formaat
|
blocks.size = Formaat
|
||||||
blocks.liquidcapacity = Vloeistof Capaciteit
|
blocks.liquidcapacity = Vloeistofcapaciteit
|
||||||
blocks.powerrange = Stroom Bereik
|
blocks.powerrange = Stroombereik
|
||||||
blocks.powerconnections = Maximale Hoeveelheid Dradem
|
blocks.powerconnections = Maximale Hoeveelheid Connecties
|
||||||
blocks.poweruse = Stroom verbruik
|
blocks.poweruse = Stroomverbruik
|
||||||
blocks.powerdamage = Stroom/Damage
|
blocks.powerdamage = Stroom/Schade
|
||||||
blocks.itemcapacity = Materiaal Capaciteit
|
blocks.itemcapacity = Materiaalcapaciteit
|
||||||
blocks.basepowergeneration = Standaard Stroom Generatie
|
blocks.basepowergeneration = Standaard Stroom Generatie
|
||||||
blocks.productiontime = Productie Tijd
|
blocks.productiontime = Productie Tijd
|
||||||
blocks.repairtime = Volledige Blok Repareertijd
|
blocks.repairtime = Volledige Blok Repareertijd
|
||||||
@@ -556,17 +556,17 @@ blocks.drilltier = Valt te delven
|
|||||||
blocks.drillspeed = Standaard mine snelheid
|
blocks.drillspeed = Standaard mine snelheid
|
||||||
blocks.boosteffect = Boost Effect
|
blocks.boosteffect = Boost Effect
|
||||||
blocks.maxunits = Maximaal Actieve Units
|
blocks.maxunits = Maximaal Actieve Units
|
||||||
blocks.health = Health
|
blocks.health = Levenspunten
|
||||||
blocks.buildtime = Bouw tijd
|
blocks.buildtime = Bouwtijd
|
||||||
blocks.buildcost = Bouw kosten
|
blocks.buildcost = Bouwkosten
|
||||||
blocks.inaccuracy = Onnauwkeurigheid
|
blocks.inaccuracy = Onnauwkeurigheid
|
||||||
blocks.shots = Shoten
|
blocks.shots = Shoten
|
||||||
blocks.reload = Schoten/Seconde
|
blocks.reload = Schoten/Seconde
|
||||||
blocks.ammo = Ammonutie
|
blocks.ammo = Ammunitie
|
||||||
|
|
||||||
bar.drilltierreq = Betere miner nodig
|
bar.drilltierreq = Betere miner nodig
|
||||||
bar.drillspeed = Mining Snelheid: {0}/s
|
bar.drillspeed = Mining Snelheid: {0}/s
|
||||||
bar.pumpspeed = Pomp Snelheid: {0}/s
|
bar.pumpspeed = Pompsnelheid: {0}/s
|
||||||
bar.efficiency = Rendement: {0}%
|
bar.efficiency = Rendement: {0}%
|
||||||
bar.powerbalance = Stroom: {0}
|
bar.powerbalance = Stroom: {0}
|
||||||
bar.powerstored = Opgeslagen: {0}/{1}
|
bar.powerstored = Opgeslagen: {0}/{1}
|
||||||
@@ -591,15 +591,15 @@ bullet.frag = [stat]clusterbom
|
|||||||
bullet.knockback = [stat]{0}[lightgray] terugslag
|
bullet.knockback = [stat]{0}[lightgray] terugslag
|
||||||
bullet.freezing = [stat]bevriezend
|
bullet.freezing = [stat]bevriezend
|
||||||
bullet.tarred = [stat]pek
|
bullet.tarred = [stat]pek
|
||||||
bullet.multiplier = [stat]{0}[lightgray]x ammonutie verdubbelaar
|
bullet.multiplier = [stat]{0}[lightgray]x ammunitieverdubbelaar
|
||||||
bullet.reload = [stat]{0}[lightgray]x herlaad
|
bullet.reload = [stat]{0}[lightgray]x herlaad
|
||||||
|
|
||||||
unit.blocks = blokken
|
unit.blocks = blokken
|
||||||
unit.powersecond = stroom eenheid/seconde
|
unit.powersecond = stroomeenheid/seconde
|
||||||
unit.liquidsecond = vloeistof eenheid/seconde
|
unit.liquidsecond = vloeistofeenheid/seconde
|
||||||
unit.itemssecond = items/seconde
|
unit.itemssecond = items/seconde
|
||||||
unit.liquidunits = vloeistof eenheid
|
unit.liquidunits = vloeistofeenheid
|
||||||
unit.powerunits = stroom eenheid
|
unit.powerunits = stroomeenheid
|
||||||
unit.degrees = graden
|
unit.degrees = graden
|
||||||
unit.seconds = secondes
|
unit.seconds = secondes
|
||||||
unit.persecond = /sec
|
unit.persecond = /sec
|
||||||
@@ -615,8 +615,8 @@ category.items = Items
|
|||||||
category.crafting = Productie
|
category.crafting = Productie
|
||||||
category.shooting = Wapens
|
category.shooting = Wapens
|
||||||
category.optional = Optionele Verbeteringen
|
category.optional = Optionele Verbeteringen
|
||||||
setting.landscape.name = Vergrendel Landscape
|
setting.landscape.name = Vergrendel Landschap
|
||||||
setting.shadows.name = Schaduws
|
setting.shadows.name = Schaduwen
|
||||||
setting.blockreplace.name = Automatische Blok Suggesties
|
setting.blockreplace.name = Automatische Blok Suggesties
|
||||||
setting.linear.name = Linear Filtering
|
setting.linear.name = Linear Filtering
|
||||||
setting.hints.name = Hints
|
setting.hints.name = Hints
|
||||||
@@ -629,14 +629,14 @@ setting.autotarget.name = Auto-Target
|
|||||||
setting.keyboard.name = Muis+Toetsenbord Controls
|
setting.keyboard.name = Muis+Toetsenbord Controls
|
||||||
setting.touchscreen.name = Touchscreen Controls
|
setting.touchscreen.name = Touchscreen Controls
|
||||||
setting.fpscap.name = Max FPS
|
setting.fpscap.name = Max FPS
|
||||||
setting.fpscap.none = None
|
setting.fpscap.none = Geen
|
||||||
setting.fpscap.text = {0} FPS
|
setting.fpscap.text = {0} FPS
|
||||||
setting.uiscale.name = UI Schaal[lightgray] (herstart vereist)[]
|
setting.uiscale.name = UI Schaal[lightgray] (herstart vereist)[]
|
||||||
setting.swapdiagonal.name = Altijd Diagonaal Plaatsen
|
setting.swapdiagonal.name = Altijd Diagonaal Plaatsen
|
||||||
setting.difficulty.training = kalm
|
setting.difficulty.training = oefening
|
||||||
setting.difficulty.easy = makkelijk
|
setting.difficulty.easy = makkelijk
|
||||||
setting.difficulty.normal = normaal
|
setting.difficulty.normal = normaal
|
||||||
setting.difficulty.hard = hard
|
setting.difficulty.hard = moeilijk
|
||||||
setting.difficulty.insane = krankzinnig
|
setting.difficulty.insane = krankzinnig
|
||||||
setting.difficulty.name = Moeilijkheidsgraad:
|
setting.difficulty.name = Moeilijkheidsgraad:
|
||||||
setting.screenshake.name = Schuddend Scherm
|
setting.screenshake.name = Schuddend Scherm
|
||||||
@@ -932,7 +932,7 @@ block.lancer.name = Lancer
|
|||||||
block.conveyor.name = Lopende Band
|
block.conveyor.name = Lopende Band
|
||||||
block.titanium-conveyor.name = Titanium Lopende Band
|
block.titanium-conveyor.name = Titanium Lopende Band
|
||||||
block.armored-conveyor.name = Gepantserde Lopende Band
|
block.armored-conveyor.name = Gepantserde Lopende Band
|
||||||
block.armored-conveyor.description = Verplaatst items met dezelfde snelheid als een van titanium, maar heeft meer levenspunten. accepteert alleen items van de zijkanten als het ook lopende banden zijn.
|
block.armored-conveyor.description = Verplaatst items met dezelfde snelheid als een van titanium, maar heeft meer levenspunten. Accepteert alleen items van de zijkanten als het ook lopende banden zijn.
|
||||||
block.junction.name = Kruising
|
block.junction.name = Kruising
|
||||||
block.router.name = Router
|
block.router.name = Router
|
||||||
block.distributor.name = Distributor
|
block.distributor.name = Distributor
|
||||||
@@ -1060,7 +1060,7 @@ unit.eradicator.name = Eradicator
|
|||||||
unit.lich.name = Lich
|
unit.lich.name = Lich
|
||||||
unit.reaper.name = Reaper
|
unit.reaper.name = Reaper
|
||||||
tutorial.next = [lightgray]<Klik om verder te gaan>
|
tutorial.next = [lightgray]<Klik om verder te gaan>
|
||||||
tutorial.intro = Welkom bij de[scarlet] Mindustry Tutorial.[]\nBegin met het[accent] delven van koper[]. Klik op een vakje die het heeft om het te delven.\n\n[accent]{0}/{1} koper
|
tutorial.intro = Welkom bij de[scarlet] Mindustry Tutorial.[]\nBegin met het[accent] delven van koper[]. Klik op een vakje met koper om het te delven.\n\n[accent]{0}/{1} koper
|
||||||
tutorial.intro.mobile = Welkom bij de[scarlet] Mindustry Tutorial.[]\nVeeg over het scherm om te bewegen.\n[accent]Knijp met 2 vingers [] om in en uit te zoomen.\nBegin met het[accent] delven van koper[]. Beweeg dichterbij, en klik er dan op.\n\n[accent]{0}/{1} koper
|
tutorial.intro.mobile = Welkom bij de[scarlet] Mindustry Tutorial.[]\nVeeg over het scherm om te bewegen.\n[accent]Knijp met 2 vingers [] om in en uit te zoomen.\nBegin met het[accent] delven van koper[]. Beweeg dichterbij, en klik er dan op.\n\n[accent]{0}/{1} koper
|
||||||
tutorial.drill = Met de hand delven is inefficient.\n[accent]Drills []kunnen automatisch voor je delven.\nPlaats er een op de koper.
|
tutorial.drill = Met de hand delven is inefficient.\n[accent]Drills []kunnen automatisch voor je delven.\nPlaats er een op de koper.
|
||||||
tutorial.drill.mobile = Met de hand delven is inefficient.\n[accent]Drills []kunnen automatisch voor je delven.\nZoek de drill rechts onderin.\nSelecter de[accent] mechanische drill[].\nPlaats het op de koper door erop te klikken, druk dan op het[accent] vinkje[] om het bouwen te bevestigen.\nKlik op de[accent] X knop[] om het te anuleren.
|
tutorial.drill.mobile = Met de hand delven is inefficient.\n[accent]Drills []kunnen automatisch voor je delven.\nZoek de drill rechts onderin.\nSelecter de[accent] mechanische drill[].\nPlaats het op de koper door erop te klikken, druk dan op het[accent] vinkje[] om het bouwen te bevestigen.\nKlik op de[accent] X knop[] om het te anuleren.
|
||||||
@@ -1068,8 +1068,8 @@ tutorial.blockinfo = Elk blok heeft andere statistieken. Elke drill kan enkel be
|
|||||||
tutorial.conveyor = [accent]Lopende Banden[] worden gebruikt om je items naar je core te krijgen.\nLeg een line aan van je drills tot aan je core.
|
tutorial.conveyor = [accent]Lopende Banden[] worden gebruikt om je items naar je core te krijgen.\nLeg een line aan van je drills tot aan je core.
|
||||||
tutorial.conveyor.mobile = [accent]Lopende Banden[] worden gebruikt om je items naar je core te krijgen.\nLeg een line aan van je drills tot aan je core.\n[accent] Doe dit door je vinger een paar seconden stil te houden[] en dan in een richting te slepen.\n\n[accent]{0}/{1} lopende banden in 1x geplaatst\n[accent]0/1 items afgeleverd
|
tutorial.conveyor.mobile = [accent]Lopende Banden[] worden gebruikt om je items naar je core te krijgen.\nLeg een line aan van je drills tot aan je core.\n[accent] Doe dit door je vinger een paar seconden stil te houden[] en dan in een richting te slepen.\n\n[accent]{0}/{1} lopende banden in 1x geplaatst\n[accent]0/1 items afgeleverd
|
||||||
tutorial.turret = Defensieve gebouwen moeten worden gebouwd tegen de[LIGHT_GRAY] vijand[].\nBouw een duo kannon bij je basis.
|
tutorial.turret = Defensieve gebouwen moeten worden gebouwd tegen de[LIGHT_GRAY] vijand[].\nBouw een duo kannon bij je basis.
|
||||||
tutorial.drillturret = Duo's hebben[accent] koperen ammonutie []nodig om te schieten.\nPlaatst een drill ernaast om het van koper te voorzien.
|
tutorial.drillturret = Duo's hebben[accent] koperen ammunitie []nodig om te schieten.\nPlaats een drill ernaast om het van koper te voorzien.
|
||||||
tutorial.pause = Tijdens een gevecht is het mogelijk[accent] het spel te pauzeren.[]\nJe kan nog wel je gebouwen plannen dan.\n\n[accent]Pauzeer het spel (spatie) nu.
|
tutorial.pause = Tijdens een gevecht is het mogelijk[accent] het spel te pauzeren.[]\nJe kan nog wel je gebouwen plannen.\n\n[accent]Pauzeer het spel (spatie) nu.
|
||||||
tutorial.pause.mobile = During battle, you are able to[accent] pause the game.[]\nYou may queue buildings while paused.\n\n[accent]Press this button in the top left to pause.
|
tutorial.pause.mobile = During battle, you are able to[accent] pause the game.[]\nYou may queue buildings while paused.\n\n[accent]Press this button in the top left to pause.
|
||||||
tutorial.unpause = Doe het opnieuw om weer verder te gaan.
|
tutorial.unpause = Doe het opnieuw om weer verder te gaan.
|
||||||
tutorial.unpause.mobile = Doe het opnieuw om weer verder te gaan.
|
tutorial.unpause.mobile = Doe het opnieuw om weer verder te gaan.
|
||||||
@@ -1079,10 +1079,10 @@ tutorial.withdraw = In sommige situaties, is het nodig om items uit een blok te
|
|||||||
tutorial.deposit = Je kan de items weer terugstoppen door van je schip het terug te slepen naar waar je het wilt.\n\n[accent]Stop het nu weer terug in de core.[]
|
tutorial.deposit = Je kan de items weer terugstoppen door van je schip het terug te slepen naar waar je het wilt.\n\n[accent]Stop het nu weer terug in de core.[]
|
||||||
tutorial.waves = De[LIGHT_GRAY] vijand[] naderd.\n\nVerdedig je core voor 2 rondes. Bouw meer verdedigingen.
|
tutorial.waves = De[LIGHT_GRAY] vijand[] naderd.\n\nVerdedig je core voor 2 rondes. Bouw meer verdedigingen.
|
||||||
tutorial.waves.mobile = De[LIGHT_GRAY] vijand[] naderd.\n\nVerdedig je core voor 2 rondes. Je schip schiet automatisch op vijanden.\nBouw meer verdedigingen, en mine meer koper.
|
tutorial.waves.mobile = De[LIGHT_GRAY] vijand[] naderd.\n\nVerdedig je core voor 2 rondes. Je schip schiet automatisch op vijanden.\nBouw meer verdedigingen, en mine meer koper.
|
||||||
tutorial.launch = Tijdens sommige waves, kan je je core[accent] lanceren[], hiermee verlaat je de basis permanent[accent] maar je neemt wel alles dat in de core zit met je mee.[]\nVervolgens valt ermee te onderzoeken.\n\n[accent]Druk op de lanceer knop.
|
tutorial.launch = Tijdens sommige waves, kan je je core[accent] lanceren[], hiermee verlaat je de basis permanent[accent] maar je neemt wel alles dat in de core zit met je mee.[]\nMet deze grondstoffen kan je nieuwe technologieën onderzoeken.\n\n[accent]Druk op de lanceerknop.
|
||||||
|
|
||||||
item.copper.description = A useful structure material. Used extensively in all types of blocks.
|
item.copper.description = Een nuttig materiaal voor gebouwen. Wordt erg vaak in blokken gebruikt.
|
||||||
item.lead.description = A basic starter material. Used extensively in electronics and liquid transportation blocks.
|
item.lead.description = Een basismateriaal. Wordt vaak gebruikt in elektronica en vloeistoftransport.
|
||||||
item.metaglass.description = A super-tough glass compound. Extensively used for liquid distribution and storage.
|
item.metaglass.description = A super-tough glass compound. Extensively used for liquid distribution and storage.
|
||||||
item.graphite.description = Mineralized carbon, used for ammunition and electrical insulation.
|
item.graphite.description = Mineralized carbon, used for ammunition and electrical insulation.
|
||||||
item.sand.description = A common material that is used extensively in smelting, both in alloying and as a flux.
|
item.sand.description = A common material that is used extensively in smelting, both in alloying and as a flux.
|
||||||
|
|||||||