Merging of map saves
This commit is contained in:
@@ -4,12 +4,27 @@ import java.lang.annotation.*;
|
|||||||
|
|
||||||
public class Annotations{
|
public class Annotations{
|
||||||
|
|
||||||
@Target({ElementType.METHOD, ElementType.FIELD})
|
/** Indicates that a method should always call its super version. */
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface CallSuper{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Annotation that allows overriding CallSuper annotation. To be used on method that overrides method with CallSuper annotation from parent class.*/
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
|
public @interface OverrideCallSuper {
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Indicates that a method return or field can be null.*/
|
||||||
|
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE})
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
public @interface Nullable{
|
public @interface Nullable{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Indicates that a method return or field cannot be null.*/
|
||||||
@Target({ElementType.METHOD, ElementType.FIELD})
|
@Target({ElementType.METHOD, ElementType.FIELD})
|
||||||
@Retention(RetentionPolicy.SOURCE)
|
@Retention(RetentionPolicy.SOURCE)
|
||||||
public @interface NonNull{
|
public @interface NonNull{
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
package io.anuke.annotations;
|
||||||
|
|
||||||
|
import com.sun.source.util.TreePath;
|
||||||
|
import com.sun.source.util.Trees;
|
||||||
|
import com.sun.tools.javac.tree.JCTree;
|
||||||
|
import com.sun.tools.javac.tree.JCTree.JCExpressionStatement;
|
||||||
|
import io.anuke.annotations.Annotations.OverrideCallSuper;
|
||||||
|
|
||||||
|
import javax.annotation.processing.*;
|
||||||
|
import javax.lang.model.SourceVersion;
|
||||||
|
import javax.lang.model.element.Element;
|
||||||
|
import javax.lang.model.element.TypeElement;
|
||||||
|
import javax.tools.Diagnostic.Kind;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
@SupportedAnnotationTypes("java.lang.Override")
|
||||||
|
public class CallSuperAnnotationProcessor extends AbstractProcessor{
|
||||||
|
private Trees trees;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init (ProcessingEnvironment pe) {
|
||||||
|
super.init(pe);
|
||||||
|
trees = Trees.instance(pe);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean process (Set<? 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
package io.anuke.annotations;
|
||||||
|
|
||||||
|
import com.sun.source.tree.*;
|
||||||
|
import com.sun.source.util.TreePathScanner;
|
||||||
|
import com.sun.source.util.Trees;
|
||||||
|
import com.sun.tools.javac.code.Scope;
|
||||||
|
import com.sun.tools.javac.code.Symbol;
|
||||||
|
import com.sun.tools.javac.code.Symbol.MethodSymbol;
|
||||||
|
import com.sun.tools.javac.code.Type.ClassType;
|
||||||
|
import com.sun.tools.javac.tree.JCTree.JCIdent;
|
||||||
|
import com.sun.tools.javac.tree.JCTree.JCTypeApply;
|
||||||
|
import io.anuke.annotations.Annotations.CallSuper;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
|
||||||
|
class CodeAnalyzerTreeScanner extends TreePathScanner<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;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean checkScope (Scope members) {
|
||||||
|
for (Symbol s : members.getElements()) {
|
||||||
|
if (s instanceof MethodSymbol) {
|
||||||
|
MethodSymbol ms = (MethodSymbol) s;
|
||||||
|
|
||||||
|
if (ms.getSimpleName().toString().equals(methodName)) {
|
||||||
|
Annotation annotation = ms.getAnnotation(CallSuper.class);
|
||||||
|
if (annotation != null) {
|
||||||
|
callSuperUsed = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitMethod (MethodTree methodTree, Trees trees) {
|
||||||
|
if (methodTree.getName().toString().equals(methodName))
|
||||||
|
method = methodTree;
|
||||||
|
|
||||||
|
return super.visitMethod(methodTree, trees);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMethodName (String methodName) {
|
||||||
|
this.methodName = methodName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMethodName () {
|
||||||
|
return methodName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public MethodTree getMethod () {
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCallSuperUsed () {
|
||||||
|
return callSuperUsed;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,9 +13,7 @@ import java.util.List;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
@SupportedSourceVersion(SourceVersion.RELEASE_8)
|
@SupportedSourceVersion(SourceVersion.RELEASE_8)
|
||||||
@SupportedAnnotationTypes({
|
@SupportedAnnotationTypes("io.anuke.annotations.Annotations.Serialize")
|
||||||
"io.anuke.annotations.Annotations.Serialize"
|
|
||||||
})
|
|
||||||
public class SerializeAnnotationProcessor extends AbstractProcessor{
|
public class SerializeAnnotationProcessor extends AbstractProcessor{
|
||||||
/** Target class name. */
|
/** Target class name. */
|
||||||
private static final String className = "Serialization";
|
private static final String className = "Serialization";
|
||||||
@@ -44,20 +42,10 @@ public class SerializeAnnotationProcessor extends AbstractProcessor{
|
|||||||
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className).addModifiers(Modifier.PUBLIC);
|
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className).addModifiers(Modifier.PUBLIC);
|
||||||
classBuilder.addAnnotation(AnnotationSpec.builder(SuppressWarnings.class).addMember("value", "\"unchecked\"").build());
|
classBuilder.addAnnotation(AnnotationSpec.builder(SuppressWarnings.class).addMember("value", "\"unchecked\"").build());
|
||||||
classBuilder.addJavadoc(RemoteMethodAnnotationProcessor.autogenWarning);
|
classBuilder.addJavadoc(RemoteMethodAnnotationProcessor.autogenWarning);
|
||||||
|
|
||||||
|
|
||||||
MethodSpec.Builder method = MethodSpec.methodBuilder("init").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
MethodSpec.Builder method = MethodSpec.methodBuilder("init").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
||||||
|
|
||||||
TypeName jsonType = ClassName.bestGuess("io.anuke.arc.util.serialization.Json");
|
|
||||||
TypeName jsonValueType = ClassName.bestGuess("io.anuke.arc.util.serialization.JsonValue");
|
|
||||||
TypeName ubJsonWriterType = ClassName.bestGuess("io.anuke.arc.util.serialization.UBJsonWriter");
|
|
||||||
TypeName ubJsonReaderType = ClassName.bestGuess("io.anuke.arc.util.serialization.UBJsonReader");
|
|
||||||
|
|
||||||
classBuilder.addField(jsonType, "bjson", Modifier.STATIC, Modifier.PRIVATE);
|
|
||||||
classBuilder.addField(ubJsonReaderType, "bjsonReader", Modifier.STATIC, Modifier.PRIVATE);
|
|
||||||
classBuilder.addStaticBlock(CodeBlock.builder()
|
|
||||||
.addStatement("bjson = new " + jsonType + "()")
|
|
||||||
.addStatement("bjsonReader = new " + ubJsonReaderType + "()")
|
|
||||||
.build());
|
|
||||||
|
|
||||||
for(TypeElement elem : elements){
|
for(TypeElement elem : elements){
|
||||||
TypeName type = TypeName.get(elem.asType());
|
TypeName type = TypeName.get(elem.asType());
|
||||||
String simpleTypeName = type.toString().substring(type.toString().lastIndexOf('.') + 1);
|
String simpleTypeName = type.toString().substring(type.toString().lastIndexOf('.') + 1);
|
||||||
@@ -79,19 +67,7 @@ public class SerializeAnnotationProcessor extends AbstractProcessor{
|
|||||||
.addException(IOException.class)
|
.addException(IOException.class)
|
||||||
.addModifiers(Modifier.PUBLIC);
|
.addModifiers(Modifier.PUBLIC);
|
||||||
|
|
||||||
MethodSpec.Builder jsonWriteMethod = MethodSpec.methodBuilder("write" + simpleTypeName + "Json")
|
|
||||||
.returns(void.class)
|
|
||||||
.addParameter(jsonType, "json")
|
|
||||||
.addParameter(type, "object")
|
|
||||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
|
||||||
|
|
||||||
MethodSpec.Builder jsonReadMethod = MethodSpec.methodBuilder("read" + simpleTypeName + "Json")
|
|
||||||
.returns(type)
|
|
||||||
.addParameter(jsonValueType, "value")
|
|
||||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC);
|
|
||||||
|
|
||||||
readMethod.addStatement("$L object = new $L()", type, type);
|
readMethod.addStatement("$L object = new $L()", type, type);
|
||||||
jsonReadMethod.addStatement("$L object = new $L()", type, type);
|
|
||||||
|
|
||||||
List<VariableElement> fields = ElementFilter.fieldsIn(Utils.elementUtils.getAllMembers(elem));
|
List<VariableElement> fields = ElementFilter.fieldsIn(Utils.elementUtils.getAllMembers(elem));
|
||||||
for(VariableElement field : fields){
|
for(VariableElement field : fields){
|
||||||
@@ -105,9 +81,6 @@ public class SerializeAnnotationProcessor extends AbstractProcessor{
|
|||||||
if(field.asType().getKind().isPrimitive()){
|
if(field.asType().getKind().isPrimitive()){
|
||||||
writeMethod.addStatement("stream.write" + capName + "(object." + name + ")");
|
writeMethod.addStatement("stream.write" + capName + "(object." + name + ")");
|
||||||
readMethod.addStatement("object." + name + "= stream.read" + capName + "()");
|
readMethod.addStatement("object." + name + "= stream.read" + capName + "()");
|
||||||
|
|
||||||
jsonWriteMethod.addStatement("json.writeValue(\"" + name + "\", object." + name + ")");
|
|
||||||
jsonReadMethod.addStatement("if(value.has(\"" + name + "\")) object." + name + "= value.get" + capName + "(\"" + name + "\")");
|
|
||||||
}else{
|
}else{
|
||||||
writeMethod.addStatement("io.anuke.arc.Core.settings.getSerializer(" + typeName + ".class).write(stream, object." + name + ")");
|
writeMethod.addStatement("io.anuke.arc.Core.settings.getSerializer(" + typeName + ".class).write(stream, object." + name + ")");
|
||||||
readMethod.addStatement("object." + name + " = (" + typeName + ")io.anuke.arc.Core.settings.getSerializer(" + typeName + ".class).read(stream)");
|
readMethod.addStatement("object." + name + " = (" + typeName + ")io.anuke.arc.Core.settings.getSerializer(" + typeName + ".class).read(stream)");
|
||||||
@@ -115,7 +88,6 @@ public class SerializeAnnotationProcessor extends AbstractProcessor{
|
|||||||
}
|
}
|
||||||
|
|
||||||
readMethod.addStatement("return object");
|
readMethod.addStatement("return object");
|
||||||
jsonReadMethod.addStatement("return object");
|
|
||||||
|
|
||||||
serializer.addMethod(writeMethod.build());
|
serializer.addMethod(writeMethod.build());
|
||||||
serializer.addMethod(readMethod.build());
|
serializer.addMethod(readMethod.build());
|
||||||
@@ -130,32 +102,6 @@ public class SerializeAnnotationProcessor extends AbstractProcessor{
|
|||||||
|
|
||||||
classBuilder.addMethod(writeMethod.build());
|
classBuilder.addMethod(writeMethod.build());
|
||||||
classBuilder.addMethod(readMethod.build());
|
classBuilder.addMethod(readMethod.build());
|
||||||
|
|
||||||
classBuilder.addMethod(jsonWriteMethod.build());
|
|
||||||
classBuilder.addMethod(jsonReadMethod.build());
|
|
||||||
|
|
||||||
MethodSpec.Builder binaryJsonWriteMethod = MethodSpec.methodBuilder("write" + simpleTypeName + "StreamJson")
|
|
||||||
.returns(void.class)
|
|
||||||
.addParameter(DataOutput.class, "stream")
|
|
||||||
.addParameter(type, "object")
|
|
||||||
.addException(IOException.class)
|
|
||||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
|
||||||
.addStatement("java.io.StringWriter output = new java.io.StringWriter()")
|
|
||||||
.addStatement("bjson.setWriter(output)")
|
|
||||||
.addStatement("bjson.writeObjectStart(" + type + ".class, " + type + ".class)")
|
|
||||||
.addStatement("write" + simpleTypeName + "Json(bjson, object)")
|
|
||||||
.addStatement("bjson.writeObjectEnd()")
|
|
||||||
.addStatement("stream.writeUTF(output.toString())");
|
|
||||||
|
|
||||||
MethodSpec.Builder binaryJsonReadMethod = MethodSpec.methodBuilder("read" + simpleTypeName + "StreamJson")
|
|
||||||
.returns(type)
|
|
||||||
.addParameter(DataInput.class, "stream")
|
|
||||||
.addException(IOException.class)
|
|
||||||
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
|
|
||||||
.addStatement("return read" + simpleTypeName + "Json(bjson.fromJson(null, stream.readUTF()))");
|
|
||||||
|
|
||||||
classBuilder.addMethod(binaryJsonWriteMethod.build());
|
|
||||||
classBuilder.addMethod(binaryJsonReadMethod.build());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
classBuilder.addMethod(method.build());
|
classBuilder.addMethod(method.build());
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
io.anuke.annotations.RemoteMethodAnnotationProcessor
|
io.anuke.annotations.RemoteMethodAnnotationProcessor
|
||||||
io.anuke.annotations.SerializeAnnotationProcessor
|
io.anuke.annotations.SerializeAnnotationProcessor
|
||||||
io.anuke.annotations.StructAnnotationProcessor
|
io.anuke.annotations.StructAnnotationProcessor
|
||||||
|
io.anuke.annotations.CallSuperAnnotationProcessor
|
||||||
26
build.gradle
26
build.gradle
@@ -177,6 +177,31 @@ project(":core"){
|
|||||||
}
|
}
|
||||||
|
|
||||||
dependencies{
|
dependencies{
|
||||||
|
if(System.properties["user.name"] == "anuke"){
|
||||||
|
task cleanGen{
|
||||||
|
doFirst{
|
||||||
|
delete{
|
||||||
|
delete "../core/src/io/anuke/mindustry/gen/"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
task copyGen{
|
||||||
|
doLast{
|
||||||
|
copy{
|
||||||
|
from("../core/build/generated/sources/annotationProcessor/java/main/io/anuke/mindustry/gen"){
|
||||||
|
include "**/*.java"
|
||||||
|
}
|
||||||
|
|
||||||
|
into "../core/src/io/anuke/mindustry/gen"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileJava.dependsOn(cleanGen)
|
||||||
|
compileJava.finalizedBy(copyGen)
|
||||||
|
}
|
||||||
|
|
||||||
compile arcModule("arc-core")
|
compile arcModule("arc-core")
|
||||||
compile arcModule("extensions:freetype")
|
compile arcModule("extensions:freetype")
|
||||||
compile arcModule("extensions:arcnet")
|
compile arcModule("extensions:arcnet")
|
||||||
@@ -227,6 +252,7 @@ project(":annotations"){
|
|||||||
|
|
||||||
dependencies{
|
dependencies{
|
||||||
compile 'com.squareup:javapoet:1.11.0'
|
compile 'com.squareup:javapoet:1.11.0'
|
||||||
|
compile files("${System.getProperty('java.home')}/../lib/tools.jar")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -194,6 +194,8 @@ editor.mapinfo = Map Info
|
|||||||
editor.author = Author:
|
editor.author = Author:
|
||||||
editor.description = Description:
|
editor.description = Description:
|
||||||
editor.waves = Waves:
|
editor.waves = Waves:
|
||||||
|
editor.rules = Rules:
|
||||||
|
editor.ingame = Edit In-Game
|
||||||
waves.title = Waves
|
waves.title = Waves
|
||||||
waves.remove = Remove
|
waves.remove = Remove
|
||||||
waves.never = <never>
|
waves.never = <never>
|
||||||
@@ -211,6 +213,8 @@ waves.copied = Waves copied.
|
|||||||
editor.default = [LIGHT_GRAY]<Default>
|
editor.default = [LIGHT_GRAY]<Default>
|
||||||
edit = Edit...
|
edit = Edit...
|
||||||
editor.name = Name:
|
editor.name = Name:
|
||||||
|
editor.spawn = Spawn Unit
|
||||||
|
editor.removeunit = Remove Unit
|
||||||
editor.teams = Teams
|
editor.teams = Teams
|
||||||
editor.elevation = Elevation
|
editor.elevation = Elevation
|
||||||
editor.errorload = Error loading file:\n[accent]{0}
|
editor.errorload = Error loading file:\n[accent]{0}
|
||||||
@@ -431,11 +435,11 @@ setting.fpscap.name = Max FPS
|
|||||||
setting.fpscap.none = None
|
setting.fpscap.none = None
|
||||||
setting.fpscap.text = {0} FPS
|
setting.fpscap.text = {0} FPS
|
||||||
setting.swapdiagonal.name = Always Diagonal Placement
|
setting.swapdiagonal.name = Always Diagonal Placement
|
||||||
setting.difficulty.training = training
|
setting.difficulty.training = Training
|
||||||
setting.difficulty.easy = easy
|
setting.difficulty.easy = Easy
|
||||||
setting.difficulty.normal = normal
|
setting.difficulty.normal = Normal
|
||||||
setting.difficulty.hard = hard
|
setting.difficulty.hard = Hard
|
||||||
setting.difficulty.insane = insane
|
setting.difficulty.insane = Insane
|
||||||
setting.difficulty.name = Difficulty:
|
setting.difficulty.name = Difficulty:
|
||||||
setting.screenshake.name = Screen Shake
|
setting.screenshake.name = Screen Shake
|
||||||
setting.effects.name = Display Effects
|
setting.effects.name = Display Effects
|
||||||
|
|||||||
Binary file not shown.
BIN
core/assets/maps/craters.msav
Normal file
BIN
core/assets/maps/craters.msav
Normal file
Binary file not shown.
Binary file not shown.
BIN
core/assets/maps/desolateRift.msav
Normal file
BIN
core/assets/maps/desolateRift.msav
Normal file
Binary file not shown.
Binary file not shown.
BIN
core/assets/maps/fortress.msav
Normal file
BIN
core/assets/maps/fortress.msav
Normal file
Binary file not shown.
Binary file not shown.
BIN
core/assets/maps/frozenForest.msav
Normal file
BIN
core/assets/maps/frozenForest.msav
Normal file
Binary file not shown.
Binary file not shown.
BIN
core/assets/maps/groundZero.msav
Normal file
BIN
core/assets/maps/groundZero.msav
Normal file
Binary file not shown.
Binary file not shown.
BIN
core/assets/maps/map_9.msav
Normal file
BIN
core/assets/maps/map_9.msav
Normal file
Binary file not shown.
Binary file not shown.
BIN
core/assets/maps/nuclearProductionComplex.msav
Normal file
BIN
core/assets/maps/nuclearProductionComplex.msav
Normal file
Binary file not shown.
Binary file not shown.
BIN
core/assets/maps/overgrowth.msav
Normal file
BIN
core/assets/maps/overgrowth.msav
Normal file
Binary file not shown.
Binary file not shown.
BIN
core/assets/maps/ruinousShores.msav
Normal file
BIN
core/assets/maps/ruinousShores.msav
Normal file
Binary file not shown.
Binary file not shown.
BIN
core/assets/maps/stainedMountains.msav
Normal file
BIN
core/assets/maps/stainedMountains.msav
Normal file
Binary file not shown.
Binary file not shown.
BIN
core/assets/maps/tarFields.msav
Normal file
BIN
core/assets/maps/tarFields.msav
Normal file
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 502 B After Width: | Height: | Size: 702 B |
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
Before Width: | Height: | Size: 373 KiB After Width: | Height: | Size: 417 KiB |
@@ -25,6 +25,8 @@ import java.util.Locale;
|
|||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public class Vars{
|
public class Vars{
|
||||||
|
/** Whether to load locales.*/
|
||||||
|
public static boolean loadLocales = true;
|
||||||
/** IO buffer size. */
|
/** IO buffer size. */
|
||||||
public static final int bufferSize = 8192;
|
public static final int bufferSize = 8192;
|
||||||
/** global charset */
|
/** global charset */
|
||||||
@@ -104,8 +106,10 @@ public class Vars{
|
|||||||
public static FileHandle customMapDirectory;
|
public static FileHandle customMapDirectory;
|
||||||
/** data subdirectory used for saves */
|
/** data subdirectory used for saves */
|
||||||
public static FileHandle saveDirectory;
|
public static FileHandle saveDirectory;
|
||||||
|
/** old map file extension, for conversion */
|
||||||
|
public static final String oldMapExtension = "mmap";
|
||||||
/** map file extension */
|
/** map file extension */
|
||||||
public static final String mapExtension = "mmap";
|
public static final String mapExtension = "msav";
|
||||||
/** save file extension */
|
/** save file extension */
|
||||||
public static final String saveExtension = "msav";
|
public static final String saveExtension = "msav";
|
||||||
|
|
||||||
@@ -141,19 +145,22 @@ public class Vars{
|
|||||||
public static void init(){
|
public static void init(){
|
||||||
Serialization.init();
|
Serialization.init();
|
||||||
|
|
||||||
//load locales
|
if(loadLocales){
|
||||||
String[] stra = Core.files.internal("locales").readString().split("\n");
|
//load locales
|
||||||
locales = new Locale[stra.length];
|
String[] stra = Core.files.internal("locales").readString().split("\n");
|
||||||
for(int i = 0; i < locales.length; i++){
|
locales = new Locale[stra.length];
|
||||||
String code = stra[i];
|
for(int i = 0; i < locales.length; i++){
|
||||||
if(code.contains("_")){
|
String code = stra[i];
|
||||||
locales[i] = new Locale(code.split("_")[0], code.split("_")[1]);
|
if(code.contains("_")){
|
||||||
}else{
|
locales[i] = new Locale(code.split("_")[0], code.split("_")[1]);
|
||||||
locales[i] = new Locale(code);
|
}else{
|
||||||
|
locales[i] = new Locale(code);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Arrays.sort(locales, Structs.comparing(l -> l.getDisplayName(l), String.CASE_INSENSITIVE_ORDER));
|
||||||
}
|
}
|
||||||
|
|
||||||
Arrays.sort(locales, Structs.comparing(l -> l.getDisplayName(l), String.CASE_INSENSITIVE_ORDER));
|
|
||||||
Version.init();
|
Version.init();
|
||||||
|
|
||||||
content = new ContentLoader();
|
content = new ContentLoader();
|
||||||
|
|||||||
@@ -171,12 +171,10 @@ public class BlockIndexer{
|
|||||||
|
|
||||||
for(int tx = rx * structQuadrantSize; tx < (rx + 1) * structQuadrantSize && tx < world.width(); tx++){
|
for(int tx = rx * structQuadrantSize; tx < (rx + 1) * structQuadrantSize && tx < world.width(); tx++){
|
||||||
for(int ty = ry * structQuadrantSize; ty < (ry + 1) * structQuadrantSize && ty < world.height(); ty++){
|
for(int ty = ry * structQuadrantSize; ty < (ry + 1) * structQuadrantSize && ty < world.height(); ty++){
|
||||||
Tile other = world.tile(tx, ty);
|
Tile other = world.ltile(tx, ty);
|
||||||
|
|
||||||
if(other == null) continue;
|
if(other == null) continue;
|
||||||
|
|
||||||
other = other.target();
|
|
||||||
|
|
||||||
if(other.entity == null || other.getTeam() != team || !pred.test(other) || !other.block().targetable)
|
if(other.entity == null || other.getTeam() != team || !pred.test(other) || !other.block().targetable)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -293,7 +291,7 @@ public class BlockIndexer{
|
|||||||
outer:
|
outer:
|
||||||
for(int x = quadrantX * structQuadrantSize; x < world.width() && x < (quadrantX + 1) * structQuadrantSize; x++){
|
for(int x = quadrantX * structQuadrantSize; x < world.width() && x < (quadrantX + 1) * structQuadrantSize; x++){
|
||||||
for(int y = quadrantY * structQuadrantSize; y < world.height() && y < (quadrantY + 1) * structQuadrantSize; y++){
|
for(int y = quadrantY * structQuadrantSize; y < world.height() && y < (quadrantY + 1) * structQuadrantSize; y++){
|
||||||
Tile result = world.tile(x, y).target();
|
Tile result = world.ltile(x, y);
|
||||||
//when a targetable block is found, mark this quadrant as occupied and stop searching
|
//when a targetable block is found, mark this quadrant as occupied and stop searching
|
||||||
if(result.entity != null && result.getTeam() == data.team){
|
if(result.entity != null && result.getTeam() == data.team){
|
||||||
structQuadrants[data.team.ordinal()].set(index);
|
structQuadrants[data.team.ordinal()].set(index);
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ public class Pathfinder{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean passable(Tile tile, Team team){
|
private boolean passable(Tile tile, Team team){
|
||||||
return (!tile.solid()) || (tile.breakable() && (tile.target().getTeam() != team));
|
return (!tile.solid()) || (tile.breakable() && (tile.getTeam() != team));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -2,10 +2,10 @@ package io.anuke.mindustry.ai;
|
|||||||
|
|
||||||
import io.anuke.arc.Events;
|
import io.anuke.arc.Events;
|
||||||
import io.anuke.arc.collection.Array;
|
import io.anuke.arc.collection.Array;
|
||||||
import io.anuke.arc.collection.IntArray;
|
|
||||||
import io.anuke.arc.math.Angles;
|
import io.anuke.arc.math.Angles;
|
||||||
import io.anuke.arc.math.Mathf;
|
import io.anuke.arc.math.Mathf;
|
||||||
import io.anuke.arc.util.*;
|
import io.anuke.arc.util.Time;
|
||||||
|
import io.anuke.arc.util.Tmp;
|
||||||
import io.anuke.mindustry.content.Blocks;
|
import io.anuke.mindustry.content.Blocks;
|
||||||
import io.anuke.mindustry.content.Fx;
|
import io.anuke.mindustry.content.Fx;
|
||||||
import io.anuke.mindustry.entities.Damage;
|
import io.anuke.mindustry.entities.Damage;
|
||||||
@@ -14,48 +14,25 @@ import io.anuke.mindustry.entities.type.BaseUnit;
|
|||||||
import io.anuke.mindustry.game.EventType.WorldLoadEvent;
|
import io.anuke.mindustry.game.EventType.WorldLoadEvent;
|
||||||
import io.anuke.mindustry.game.SpawnGroup;
|
import io.anuke.mindustry.game.SpawnGroup;
|
||||||
import io.anuke.mindustry.net.Net;
|
import io.anuke.mindustry.net.Net;
|
||||||
import io.anuke.mindustry.world.Pos;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.*;
|
import static io.anuke.mindustry.Vars.*;
|
||||||
|
|
||||||
public class WaveSpawner{
|
public class WaveSpawner{
|
||||||
private Array<FlyerSpawn> flySpawns = new Array<>();
|
private Array<FlyerSpawn> flySpawns = new Array<>();
|
||||||
private Array<GroundSpawn> groundSpawns = new Array<>();
|
private Array<GroundSpawn> groundSpawns = new Array<>();
|
||||||
private IntArray loadedSpawns = new IntArray();
|
|
||||||
private boolean spawning = false;
|
private boolean spawning = false;
|
||||||
|
|
||||||
public WaveSpawner(){
|
public WaveSpawner(){
|
||||||
Events.on(WorldLoadEvent.class, e -> reset());
|
Events.on(WorldLoadEvent.class, e -> reset());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void write(DataOutput stream) throws IOException{
|
|
||||||
stream.writeInt(groundSpawns.size);
|
|
||||||
for(GroundSpawn spawn : groundSpawns){
|
|
||||||
stream.writeInt(Pos.get(spawn.x, spawn.y));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void read(DataInput stream) throws IOException{
|
|
||||||
flySpawns.clear();
|
|
||||||
groundSpawns.clear();
|
|
||||||
loadedSpawns.clear();
|
|
||||||
|
|
||||||
int amount = stream.readInt();
|
|
||||||
|
|
||||||
for(int i = 0; i < amount; i++){
|
|
||||||
loadedSpawns.add(stream.readInt());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int countSpawns(){
|
public int countSpawns(){
|
||||||
return groundSpawns.size;
|
return groundSpawns.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return true if the player is near a ground spawn point. */
|
/** @return true if the player is near a ground spawn point. */
|
||||||
public boolean playerNear(){
|
public boolean playerNear(){
|
||||||
return groundSpawns.count(g -> Mathf.dst(g.x * tilesize, g.y * tilesize, player.x, player.y) < state.rules.dropZoneRadius) > 0;
|
return groundSpawns.contains(g -> Mathf.dst(g.x * tilesize, g.y * tilesize, player.x, player.y) < state.rules.dropZoneRadius);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void spawnEnemies(){
|
public void spawnEnemies(){
|
||||||
@@ -117,21 +94,11 @@ public class WaveSpawner{
|
|||||||
for(int x = 0; x < world.width(); x++){
|
for(int x = 0; x < world.width(); x++){
|
||||||
for(int y = 0; y < world.height(); y++){
|
for(int y = 0; y < world.height(); y++){
|
||||||
|
|
||||||
if(world.tile(x, y).block() == Blocks.spawn){
|
if(world.tile(x, y).overlay() == Blocks.spawn){
|
||||||
addSpawns(x, y);
|
addSpawns(x, y);
|
||||||
|
|
||||||
//hide spawnpoints, they have served their purpose
|
|
||||||
world.tile(x, y).setBlock(Blocks.air);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for(int i = 0; i < loadedSpawns.size; i++){
|
|
||||||
int pos = loadedSpawns.get(i);
|
|
||||||
addSpawns(Pos.x(pos), Pos.y(pos));
|
|
||||||
}
|
|
||||||
|
|
||||||
loadedSpawns.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addSpawns(int x, int y){
|
private void addSpawns(int x, int y){
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ public class Blocks implements ContentList{
|
|||||||
public static Block
|
public static Block
|
||||||
|
|
||||||
//environment
|
//environment
|
||||||
air, part, spawn, deepwater, water, taintedWater, tar, stone, craters, charr, sand, darksand, ice, snow, darksandTaintedWater,
|
air, spawn, deepwater, water, taintedWater, tar, stone, craters, charr, sand, darksand, ice, snow, darksandTaintedWater,
|
||||||
holostone, rocks, sporerocks, icerocks, cliffs, sporePine, pine, shrubs, whiteTree, whiteTreeDead, sporeCluster,
|
holostone, rocks, sporerocks, icerocks, cliffs, sporePine, pine, shrubs, whiteTree, whiteTreeDead, sporeCluster,
|
||||||
iceSnow, sandWater, darksandWater, duneRocks, sandRocks, moss, sporeMoss, shale, shaleRocks, shaleBoulder, grass, salt,
|
iceSnow, sandWater, darksandWater, duneRocks, sandRocks, moss, sporeMoss, shale, shaleRocks, shaleBoulder, grass, salt,
|
||||||
metalFloor, metalFloorDamaged, metalFloor2, metalFloor3, metalFloor5, ignarock, magmarock, hotrock, snowrocks, rock, snowrock, saltRocks,
|
metalFloor, metalFloorDamaged, metalFloor2, metalFloor3, metalFloor5, ignarock, magmarock, hotrock, snowrocks, rock, snowrock, saltRocks,
|
||||||
@@ -88,15 +88,9 @@ public class Blocks implements ContentList{
|
|||||||
hasShadow = false;
|
hasShadow = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void draw(Tile tile){
|
public void draw(Tile tile){}
|
||||||
}
|
public void load(){}
|
||||||
|
public void init(){}
|
||||||
public void load(){
|
|
||||||
}
|
|
||||||
|
|
||||||
public void init(){
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isHidden(){
|
public boolean isHidden(){
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -109,14 +103,24 @@ public class Blocks implements ContentList{
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
part = new BlockPart();
|
//create special blockpart variants
|
||||||
|
for(int dx = 0; dx < BlockPart.maxSize; dx++){
|
||||||
|
for(int dy = 0; dy < BlockPart.maxSize; dy++){
|
||||||
|
int fx = dx - BlockPart.maxSize/2, fy = dy - BlockPart.maxSize/2;
|
||||||
|
if(fx != 0 || fy != 0){
|
||||||
|
new BlockPart(fx, fy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
spawn = new Block("spawn");
|
spawn = new OverlayFloor("spawn"){
|
||||||
|
public void draw(Tile tile){}
|
||||||
|
};
|
||||||
|
|
||||||
//Registers build blocks from size 1-6
|
//Registers build blocks
|
||||||
//no reference is needed here since they can be looked up by name later
|
//no reference is needed here since they can be looked up by name later
|
||||||
for(int i = 1; i <= 6; i++){
|
for(int i = 1; i <= BuildBlock.maxSize; i++){
|
||||||
new BuildBlock("build" + i);
|
new BuildBlock(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
deepwater = new Floor("deepwater"){{
|
deepwater = new Floor("deepwater"){{
|
||||||
@@ -556,7 +560,7 @@ public class Blocks implements ContentList{
|
|||||||
drawer = tile -> {
|
drawer = tile -> {
|
||||||
LiquidModule mod = tile.entity.liquids;
|
LiquidModule mod = tile.entity.liquids;
|
||||||
|
|
||||||
int rotation = rotate ? tile.getRotation() * 90 : 0;
|
int rotation = rotate ? tile.rotation() * 90 : 0;
|
||||||
|
|
||||||
Draw.rect(reg(bottomRegion), tile.drawx(), tile.drawy(), rotation);
|
Draw.rect(reg(bottomRegion), tile.drawx(), tile.drawy(), rotation);
|
||||||
|
|
||||||
|
|||||||
@@ -398,9 +398,9 @@ public class Bullets implements ContentList{
|
|||||||
@Override
|
@Override
|
||||||
public void hitTile(Bullet b, Tile tile){
|
public void hitTile(Bullet b, Tile tile){
|
||||||
super.hit(b);
|
super.hit(b);
|
||||||
tile = tile.target();
|
tile = tile.link();
|
||||||
|
|
||||||
if(tile != null && tile.getTeam() == b.getTeam() && !(tile.block() instanceof BuildBlock)){
|
if(tile.getTeam() == b.getTeam() && !(tile.block() instanceof BuildBlock)){
|
||||||
Effects.effect(Fx.healBlockFull, Pal.heal, tile.drawx(), tile.drawy(), tile.block().size);
|
Effects.effect(Fx.healBlockFull, Pal.heal, tile.drawx(), tile.drawy(), tile.block().size);
|
||||||
tile.entity.healBy(healPercent / 100f * tile.entity.maxHealth());
|
tile.entity.healBy(healPercent / 100f * tile.entity.maxHealth());
|
||||||
}
|
}
|
||||||
@@ -424,7 +424,6 @@ public class Bullets implements ContentList{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(Bullet b){
|
public void draw(Bullet b){
|
||||||
//TODO add color to the bullet depending on the color of the flame it came from
|
|
||||||
Draw.color(Pal.lightFlame, Pal.darkFlame, Color.GRAY, b.fin());
|
Draw.color(Pal.lightFlame, Pal.darkFlame, Color.GRAY, b.fin());
|
||||||
Fill.circle(b.x, b.y, 3f * b.fout());
|
Fill.circle(b.x, b.y, 3f * b.fout());
|
||||||
Draw.reset();
|
Draw.reset();
|
||||||
|
|||||||
@@ -75,8 +75,6 @@ public class StatusEffects implements ContentList{
|
|||||||
armorMultiplier = 3f;
|
armorMultiplier = 3f;
|
||||||
damageMultiplier = 3f;
|
damageMultiplier = 3f;
|
||||||
speedMultiplier = 1.1f;
|
speedMultiplier = 1.1f;
|
||||||
//TODO custom effect
|
|
||||||
//effect = Fx.overdriven;
|
|
||||||
}};
|
}};
|
||||||
|
|
||||||
shocked = new StatusEffect();
|
shocked = new StatusEffect();
|
||||||
|
|||||||
@@ -154,20 +154,6 @@ public class Zones implements ContentList{
|
|||||||
}};
|
}};
|
||||||
}};
|
}};
|
||||||
|
|
||||||
/*
|
|
||||||
crags = new Zone("crags", new MapGenerator("groundZero", 1)){{ //TODO implement
|
|
||||||
baseLaunchCost = ItemStack.with(Items.copper, 300);
|
|
||||||
startingItems = ItemStack.with(Items.copper, 200);
|
|
||||||
conditionWave = 15;
|
|
||||||
zoneRequirements = new Zone[]{frozenForest};
|
|
||||||
blockRequirements = new Block[]{Blocks.copperWall};
|
|
||||||
rules = () -> new Rules(){{
|
|
||||||
waves = true;]
|
|
||||||
waveTimer = true;
|
|
||||||
waveSpacing = 60 * 80;
|
|
||||||
}};
|
|
||||||
}};*/
|
|
||||||
|
|
||||||
stainedMountains = new Zone("stainedMountains", new MapGenerator("stainedMountains", 2)
|
stainedMountains = new Zone("stainedMountains", new MapGenerator("stainedMountains", 2)
|
||||||
.dist(0f, false)
|
.dist(0f, false)
|
||||||
.decor(new Decoration(Blocks.shale, Blocks.shaleBoulder, 0.02))){{
|
.decor(new Decoration(Blocks.shale, Blocks.shaleBoulder, 0.02))){{
|
||||||
|
|||||||
@@ -90,17 +90,12 @@ public class ContentLoader{
|
|||||||
for(Array<Content> arr : contentMap){
|
for(Array<Content> arr : contentMap){
|
||||||
for(int i = 0; i < arr.size; i++){
|
for(int i = 0; i < arr.size; i++){
|
||||||
int id = arr.get(i).id;
|
int id = arr.get(i).id;
|
||||||
if(id < 0) id += 256;
|
|
||||||
if(id != i){
|
if(id != i){
|
||||||
throw new IllegalArgumentException("Out-of-order IDs for content '" + arr.get(i) + "' (expected " + i + " but got " + id + ")");
|
throw new IllegalArgumentException("Out-of-order IDs for content '" + arr.get(i) + "' (expected " + i + " but got " + id + ")");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(blocks().size >= 256){
|
|
||||||
throw new ImpendingDoomException("THE TIME HAS COME. More than 256 blocks have been created.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if(verbose){
|
if(verbose){
|
||||||
Log.info("--- CONTENT INFO ---");
|
Log.info("--- CONTENT INFO ---");
|
||||||
for(int k = 0; k < contentMap.length; k++){
|
for(int k = 0; k < contentMap.length; k++){
|
||||||
@@ -129,7 +124,7 @@ public class ContentLoader{
|
|||||||
/** Loads block colors. */
|
/** Loads block colors. */
|
||||||
public void loadColors(){
|
public void loadColors(){
|
||||||
Pixmap pixmap = new Pixmap(files.internal("sprites/block_colors.png"));
|
Pixmap pixmap = new Pixmap(files.internal("sprites/block_colors.png"));
|
||||||
for(int i = 0; i < 256; i++){
|
for(int i = 0; i < pixmap.getWidth(); i++){
|
||||||
if(blocks().size > i){
|
if(blocks().size > i){
|
||||||
int color = pixmap.getPixel(i, 0);
|
int color = pixmap.getPixel(i, 0);
|
||||||
|
|
||||||
@@ -170,8 +165,6 @@ public class ContentLoader{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public <T extends Content> T getByID(ContentType type, int id){
|
public <T extends Content> T getByID(ContentType type, int id){
|
||||||
//offset negative values by 256, as they are probably a product of byte overflow
|
|
||||||
if(id < 0) id += 256;
|
|
||||||
|
|
||||||
if(temporaryMapper != null && temporaryMapper[type.ordinal()] != null && temporaryMapper[type.ordinal()].length != 0){
|
if(temporaryMapper != null && temporaryMapper[type.ordinal()] != null && temporaryMapper[type.ordinal()].length != 0){
|
||||||
if(temporaryMapper[type.ordinal()].length <= id || temporaryMapper[type.ordinal()][id] == null){
|
if(temporaryMapper[type.ordinal()].length <= id || temporaryMapper[type.ordinal()][id] == null){
|
||||||
@@ -243,10 +236,4 @@ public class ContentLoader{
|
|||||||
TypeTrait.registerType(Bullet.class, Bullet::new);
|
TypeTrait.registerType(Bullet.class, Bullet::new);
|
||||||
TypeTrait.registerType(Lightning.class, Lightning::new);
|
TypeTrait.registerType(Lightning.class, Lightning::new);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ImpendingDoomException extends RuntimeException{
|
|
||||||
ImpendingDoomException(String s){
|
|
||||||
super(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import io.anuke.mindustry.gen.Call;
|
|||||||
import io.anuke.mindustry.input.*;
|
import io.anuke.mindustry.input.*;
|
||||||
import io.anuke.mindustry.maps.Map;
|
import io.anuke.mindustry.maps.Map;
|
||||||
import io.anuke.mindustry.net.Net;
|
import io.anuke.mindustry.net.Net;
|
||||||
import io.anuke.mindustry.type.Item;
|
import io.anuke.mindustry.type.*;
|
||||||
import io.anuke.mindustry.ui.dialogs.FloatingDialog;
|
import io.anuke.mindustry.ui.dialogs.FloatingDialog;
|
||||||
import io.anuke.mindustry.world.Tile;
|
import io.anuke.mindustry.world.Tile;
|
||||||
|
|
||||||
@@ -120,7 +120,7 @@ public class Control implements ApplicationListener{
|
|||||||
Effects.shake(5, 6, Core.camera.position.x, Core.camera.position.y);
|
Effects.shake(5, 6, Core.camera.position.x, Core.camera.position.y);
|
||||||
//the restart dialog can show info for any number of scenarios
|
//the restart dialog can show info for any number of scenarios
|
||||||
Call.onGameOver(event.winner);
|
Call.onGameOver(event.winner);
|
||||||
if(state.rules.zone != -1){
|
if(state.rules.zone != null){
|
||||||
//remove zone save on game over
|
//remove zone save on game over
|
||||||
if(saves.getZoneSlot() != null){
|
if(saves.getZoneSlot() != null){
|
||||||
saves.getZoneSlot().delete();
|
saves.getZoneSlot().delete();
|
||||||
@@ -207,6 +207,23 @@ public class Control implements ApplicationListener{
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void playZone(Zone zone){
|
||||||
|
ui.loadAnd(() -> {
|
||||||
|
logic.reset();
|
||||||
|
world.loadGenerator(zone.generator);
|
||||||
|
state.rules = zone.rules.get();
|
||||||
|
state.rules.zone = zone;
|
||||||
|
for(Tile core : state.teams.get(defaultTeam).cores){
|
||||||
|
for(ItemStack stack : zone.getStartingItems()){
|
||||||
|
core.entity.items.add(stack.item, stack.amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.set(State.playing);
|
||||||
|
control.saves.zoneSave();
|
||||||
|
logic.play();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isHighScore(){
|
public boolean isHighScore(){
|
||||||
return hiscore;
|
return hiscore;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,6 +40,10 @@ public class GameState{
|
|||||||
state = astate;
|
state = astate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isEditor(){
|
||||||
|
return rules.editor;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isPaused(){
|
public boolean isPaused(){
|
||||||
return (is(State.paused) && !Net.active()) || (gameOver && !Net.active());
|
return (is(State.paused) && !Net.active()) || (gameOver && !Net.active());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,12 +58,6 @@ public class Logic implements ApplicationListener{
|
|||||||
public void play(){
|
public void play(){
|
||||||
state.set(State.playing);
|
state.set(State.playing);
|
||||||
state.wavetime = state.rules.waveSpacing * 2; //grace period of 2x wave time before game starts
|
state.wavetime = state.rules.waveSpacing * 2; //grace period of 2x wave time before game starts
|
||||||
|
|
||||||
//sometimes a map has no waves defined, they're defined in the zone rules
|
|
||||||
if(world.getMap().getWaves() != DefaultWaves.get() || !world.isZone()){
|
|
||||||
state.rules.spawns = world.getMap().getWaves();
|
|
||||||
}
|
|
||||||
|
|
||||||
Events.fire(new PlayEvent());
|
Events.fire(new PlayEvent());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -165,15 +159,17 @@ public class Logic implements ApplicationListener{
|
|||||||
Entities.update(groundEffectGroup);
|
Entities.update(groundEffectGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(EntityGroup group : unitGroups){
|
if(!state.isEditor()){
|
||||||
Entities.update(group);
|
for(EntityGroup group : unitGroups){
|
||||||
}
|
Entities.update(group);
|
||||||
|
}
|
||||||
|
|
||||||
Entities.update(puddleGroup);
|
Entities.update(puddleGroup);
|
||||||
Entities.update(shieldGroup);
|
Entities.update(shieldGroup);
|
||||||
Entities.update(bulletGroup);
|
Entities.update(bulletGroup);
|
||||||
Entities.update(tileGroup);
|
Entities.update(tileGroup);
|
||||||
Entities.update(fireGroup);
|
Entities.update(fireGroup);
|
||||||
|
}
|
||||||
Entities.update(playerGroup);
|
Entities.update(playerGroup);
|
||||||
|
|
||||||
//effect group only contains item transfers in the headless version, update it!
|
//effect group only contains item transfers in the headless version, update it!
|
||||||
@@ -181,19 +177,21 @@ public class Logic implements ApplicationListener{
|
|||||||
Entities.update(effectGroup);
|
Entities.update(effectGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(EntityGroup group : unitGroups){
|
if(!state.isEditor()){
|
||||||
if(group.isEmpty()) continue;
|
|
||||||
|
|
||||||
collisions.collideGroups(bulletGroup, group);
|
for(EntityGroup group : unitGroups){
|
||||||
|
if(group.isEmpty()) continue;
|
||||||
|
collisions.collideGroups(bulletGroup, group);
|
||||||
|
}
|
||||||
|
|
||||||
|
collisions.collideGroups(bulletGroup, playerGroup);
|
||||||
|
collisions.collideGroups(playerGroup, playerGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
collisions.collideGroups(bulletGroup, playerGroup);
|
|
||||||
collisions.collideGroups(playerGroup, playerGroup);
|
|
||||||
|
|
||||||
world.pathfinder.update();
|
world.pathfinder.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!Net.client() && !world.isInvalidMap()){
|
if(!Net.client() && !world.isInvalidMap() && !state.isEditor()){
|
||||||
checkGameOver();
|
checkGameOver();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,7 +118,7 @@ public class NetClient implements ApplicationListener{
|
|||||||
}
|
}
|
||||||
|
|
||||||
//called on all clients
|
//called on all clients
|
||||||
@Remote(called = Loc.server, targets = Loc.server)
|
@Remote(called = Loc.server, targets = Loc.server, variants = Variant.both)
|
||||||
public static void sendMessage(String message, String sender, Player playersender){
|
public static void sendMessage(String message, String sender, Player playersender){
|
||||||
if(Vars.ui != null){
|
if(Vars.ui != null){
|
||||||
Vars.ui.chatfrag.addMessage(message, sender);
|
Vars.ui.chatfrag.addMessage(message, sender);
|
||||||
|
|||||||
@@ -12,8 +12,7 @@ import io.anuke.arc.math.Mathf;
|
|||||||
import io.anuke.arc.math.geom.Rectangle;
|
import io.anuke.arc.math.geom.Rectangle;
|
||||||
import io.anuke.arc.math.geom.Vector2;
|
import io.anuke.arc.math.geom.Vector2;
|
||||||
import io.anuke.arc.util.*;
|
import io.anuke.arc.util.*;
|
||||||
import io.anuke.arc.util.io.ByteBufferOutput;
|
import io.anuke.arc.util.io.*;
|
||||||
import io.anuke.arc.util.io.ReusableByteOutStream;
|
|
||||||
import io.anuke.mindustry.content.Blocks;
|
import io.anuke.mindustry.content.Blocks;
|
||||||
import io.anuke.mindustry.core.GameState.State;
|
import io.anuke.mindustry.core.GameState.State;
|
||||||
import io.anuke.mindustry.entities.Entities;
|
import io.anuke.mindustry.entities.Entities;
|
||||||
@@ -213,7 +212,7 @@ public class NetServer implements ApplicationListener{
|
|||||||
|
|
||||||
public void sendWorldData(Player player, int clientID){
|
public void sendWorldData(Player player, int clientID){
|
||||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||||
DeflaterOutputStream def = new DeflaterOutputStream(stream);
|
DeflaterOutputStream def = new FastDeflaterOutputStream(stream);
|
||||||
NetworkIO.writeWorld(player, def);
|
NetworkIO.writeWorld(player, def);
|
||||||
WorldStream data = new WorldStream();
|
WorldStream data = new WorldStream();
|
||||||
data.stream = new ByteArrayInputStream(stream.toByteArray());
|
data.stream = new ByteArrayInputStream(stream.toByteArray());
|
||||||
@@ -290,7 +289,7 @@ public class NetServer implements ApplicationListener{
|
|||||||
//auto-skip done requests
|
//auto-skip done requests
|
||||||
if(req.breaking && tile.block() == Blocks.air){
|
if(req.breaking && tile.block() == Blocks.air){
|
||||||
continue;
|
continue;
|
||||||
}else if(!req.breaking && tile.block() == req.block && (!req.block.rotate || tile.getRotation() == req.rotation)){
|
}else if(!req.breaking && tile.block() == req.block && (!req.block.rotate || tile.rotation() == req.rotation)){
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
player.getPlaceQueue().addLast(req);
|
player.getPlaceQueue().addLast(req);
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package io.anuke.mindustry.core;
|
|||||||
|
|
||||||
import io.anuke.annotations.Annotations.Nullable;
|
import io.anuke.annotations.Annotations.Nullable;
|
||||||
import io.anuke.arc.*;
|
import io.anuke.arc.*;
|
||||||
import io.anuke.arc.collection.Array;
|
|
||||||
import io.anuke.arc.collection.IntArray;
|
import io.anuke.arc.collection.IntArray;
|
||||||
import io.anuke.arc.math.Mathf;
|
import io.anuke.arc.math.Mathf;
|
||||||
import io.anuke.arc.math.geom.Geometry;
|
import io.anuke.arc.math.geom.Geometry;
|
||||||
@@ -18,8 +17,9 @@ import io.anuke.mindustry.game.Team;
|
|||||||
import io.anuke.mindustry.io.MapIO;
|
import io.anuke.mindustry.io.MapIO;
|
||||||
import io.anuke.mindustry.maps.*;
|
import io.anuke.mindustry.maps.*;
|
||||||
import io.anuke.mindustry.maps.generators.Generator;
|
import io.anuke.mindustry.maps.generators.Generator;
|
||||||
import io.anuke.mindustry.type.*;
|
import io.anuke.mindustry.type.Zone;
|
||||||
import io.anuke.mindustry.world.*;
|
import io.anuke.mindustry.world.*;
|
||||||
|
import io.anuke.mindustry.world.blocks.BlockPart;
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.*;
|
import static io.anuke.mindustry.Vars.*;
|
||||||
|
|
||||||
@@ -28,17 +28,22 @@ public class World implements ApplicationListener{
|
|||||||
public final BlockIndexer indexer = new BlockIndexer();
|
public final BlockIndexer indexer = new BlockIndexer();
|
||||||
public final WaveSpawner spawner = new WaveSpawner();
|
public final WaveSpawner spawner = new WaveSpawner();
|
||||||
public final Pathfinder pathfinder = new Pathfinder();
|
public final Pathfinder pathfinder = new Pathfinder();
|
||||||
|
public final Context context = new Context();
|
||||||
|
|
||||||
private Map currentMap;
|
private Map currentMap;
|
||||||
private Tile[][] tiles;
|
private Tile[][] tiles;
|
||||||
|
|
||||||
private Array<Tile> tempTiles = new Array<>();
|
|
||||||
private boolean generating, invalidMap;
|
private boolean generating, invalidMap;
|
||||||
|
|
||||||
public World(){
|
public World(){
|
||||||
maps.load();
|
maps.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(){
|
||||||
|
maps.loadLegacyMaps();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dispose(){
|
public void dispose(){
|
||||||
maps.dispose();
|
maps.dispose();
|
||||||
@@ -97,6 +102,12 @@ public class World implements ApplicationListener{
|
|||||||
return tiles[x][y];
|
return tiles[x][y];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public @Nullable Tile ltile(int x, int y){
|
||||||
|
Tile tile = tile(x, y);
|
||||||
|
if(tile == null) return null;
|
||||||
|
return tile.block().linked(tile);
|
||||||
|
}
|
||||||
|
|
||||||
public Tile rawTile(int x, int y){
|
public Tile rawTile(int x, int y){
|
||||||
return tiles[x][y];
|
return tiles[x][y];
|
||||||
}
|
}
|
||||||
@@ -105,6 +116,10 @@ public class World implements ApplicationListener{
|
|||||||
return tile(Math.round(x / tilesize), Math.round(y / tilesize));
|
return tile(Math.round(x / tilesize), Math.round(y / tilesize));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public @Nullable Tile ltileWorld(float x, float y){
|
||||||
|
return ltile(Math.round(x / tilesize), Math.round(y / tilesize));
|
||||||
|
}
|
||||||
|
|
||||||
public int toTile(float coord){
|
public int toTile(float coord){
|
||||||
return Math.round(coord / tilesize);
|
return Math.round(coord / tilesize);
|
||||||
}
|
}
|
||||||
@@ -160,6 +175,8 @@ public class World implements ApplicationListener{
|
|||||||
* A WorldLoadEvent will be fire.
|
* A WorldLoadEvent will be fire.
|
||||||
*/
|
*/
|
||||||
public void endMapLoad(){
|
public void endMapLoad(){
|
||||||
|
prepareTiles(tiles);
|
||||||
|
|
||||||
for(int x = 0; x < tiles.length; x++){
|
for(int x = 0; x < tiles.length; x++){
|
||||||
for(int y = 0; y < tiles[0].length; y++){
|
for(int y = 0; y < tiles[0].length; y++){
|
||||||
Tile tile = tiles[x][y];
|
Tile tile = tiles[x][y];
|
||||||
@@ -188,24 +205,7 @@ public class World implements ApplicationListener{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Zone getZone(){
|
public Zone getZone(){
|
||||||
return content.getByID(ContentType.zone, state.rules.zone);
|
return state.rules.zone;
|
||||||
}
|
|
||||||
|
|
||||||
public void playZone(Zone zone){
|
|
||||||
ui.loadAnd(() -> {
|
|
||||||
logic.reset();
|
|
||||||
state.rules = zone.rules.get();
|
|
||||||
state.rules.zone = zone.id;
|
|
||||||
loadGenerator(zone.generator);
|
|
||||||
for(Tile core : state.teams.get(defaultTeam).cores){
|
|
||||||
for(ItemStack stack : zone.getStartingItems()){
|
|
||||||
core.entity.items.add(stack.item, stack.amount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state.set(State.playing);
|
|
||||||
control.saves.zoneSave();
|
|
||||||
logic.play();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadGenerator(Generator generator){
|
public void loadGenerator(Generator generator){
|
||||||
@@ -213,24 +213,14 @@ public class World implements ApplicationListener{
|
|||||||
|
|
||||||
createTiles(generator.width, generator.height);
|
createTiles(generator.width, generator.height);
|
||||||
generator.generate(tiles);
|
generator.generate(tiles);
|
||||||
prepareTiles(tiles);
|
|
||||||
|
|
||||||
endMapLoad();
|
endMapLoad();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void loadMap(Map map){
|
public void loadMap(Map map){
|
||||||
beginMapLoad();
|
|
||||||
this.currentMap = map;
|
|
||||||
|
|
||||||
try{
|
try{
|
||||||
createTiles(map.width, map.height);
|
MapIO.loadMap(map);
|
||||||
for(int x = 0; x < map.width; x++){
|
|
||||||
for(int y = 0; y < map.height; y++){
|
|
||||||
tiles[x][y] = new Tile(x, y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
MapIO.readTiles(map, tiles);
|
|
||||||
prepareTiles(tiles);
|
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
Log.err(e);
|
Log.err(e);
|
||||||
if(!headless){
|
if(!headless){
|
||||||
@@ -242,7 +232,7 @@ public class World implements ApplicationListener{
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
endMapLoad();
|
this.currentMap = map;
|
||||||
|
|
||||||
invalidMap = false;
|
invalidMap = false;
|
||||||
|
|
||||||
@@ -289,16 +279,7 @@ public class World implements ApplicationListener{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void removeBlock(Tile tile){
|
public void removeBlock(Tile tile){
|
||||||
if(!tile.block().isMultiblock() && !tile.isLinked()){
|
tile.link().getLinkedTiles(other -> other.setBlock(Blocks.air));
|
||||||
tile.setBlock(Blocks.air);
|
|
||||||
}else{
|
|
||||||
Tile target = tile.target();
|
|
||||||
Array<Tile> removals = target.getLinkedTiles(tempTiles);
|
|
||||||
for(Tile toremove : removals){
|
|
||||||
//note that setting a new block automatically unlinks it
|
|
||||||
if(toremove != null) toremove.setBlock(Blocks.air);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBlock(Tile tile, Block block, Team team){
|
public void setBlock(Tile tile, Block block, Team team){
|
||||||
@@ -318,8 +299,7 @@ public class World implements ApplicationListener{
|
|||||||
if(!(worldx == tile.x && worldy == tile.y)){
|
if(!(worldx == tile.x && worldy == tile.y)){
|
||||||
Tile toplace = world.tile(worldx, worldy);
|
Tile toplace = world.tile(worldx, worldy);
|
||||||
if(toplace != null){
|
if(toplace != null){
|
||||||
toplace.setLinked((byte)(dx + offsetx), (byte)(dy + offsety));
|
toplace.setBlock(BlockPart.get(dx + offsetx, dy + offsety), team);
|
||||||
toplace.setTeam(team);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -327,15 +307,6 @@ public class World implements ApplicationListener{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int transform(int packed, int oldWidth, int oldHeight, int newWidth, int shiftX, int shiftY){
|
|
||||||
int x = packed % oldWidth;
|
|
||||||
int y = packed / oldWidth;
|
|
||||||
if(!Structs.inBounds(x, y, oldWidth, oldHeight)) return -1;
|
|
||||||
x += shiftX;
|
|
||||||
y += shiftY;
|
|
||||||
return y * newWidth + x;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Raycast, but with world coordinates.
|
* Raycast, but with world coordinates.
|
||||||
*/
|
*/
|
||||||
@@ -413,11 +384,6 @@ public class World implements ApplicationListener{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Loads raw map tile data into a Tile[][] array, setting up multiblocks, cliffs and ores. */
|
|
||||||
void loadTileData(Tile[][] tiles){
|
|
||||||
prepareTiles(tiles);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addDarkness(Tile[][] tiles){
|
public void addDarkness(Tile[][] tiles){
|
||||||
byte[][] dark = new byte[tiles.length][tiles[0].length];
|
byte[][] dark = new byte[tiles.length][tiles[0].length];
|
||||||
byte[][] writeBuffer = new byte[tiles.length][tiles[0].length];
|
byte[][] writeBuffer = new byte[tiles.length][tiles[0].length];
|
||||||
@@ -456,7 +422,7 @@ public class World implements ApplicationListener{
|
|||||||
for(int y = 0; y < tiles[0].length; y++){
|
for(int y = 0; y < tiles[0].length; y++){
|
||||||
Tile tile = tiles[x][y];
|
Tile tile = tiles[x][y];
|
||||||
if(tile.block().solid && !tile.block().synthetic()){
|
if(tile.block().solid && !tile.block().synthetic()){
|
||||||
tiles[x][y].setRotation(dark[x][y]);
|
tiles[x][y].rotation(dark[x][y]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -503,25 +469,47 @@ public class World implements ApplicationListener{
|
|||||||
if(!(worldx == x && worldy == y)){
|
if(!(worldx == x && worldy == y)){
|
||||||
Tile toplace = world.tile(worldx, worldy);
|
Tile toplace = world.tile(worldx, worldy);
|
||||||
if(toplace != null){
|
if(toplace != null){
|
||||||
toplace.setLinked((byte)(dx + offsetx), (byte)(dy + offsety));
|
toplace.setBlock(BlockPart.get(dx + offsetx, dy + offsety), team);
|
||||||
toplace.setTeam(team);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//update cliffs, occlusion data
|
|
||||||
for(int x = 0; x < tiles.length; x++){
|
|
||||||
for(int y = 0; y < tiles[0].length; y++){
|
|
||||||
Tile tile = tiles[x][y];
|
|
||||||
|
|
||||||
tile.updateOcclusion();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface Raycaster{
|
public interface Raycaster{
|
||||||
boolean accept(int x, int y);
|
boolean accept(int x, int y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Context implements WorldContext{
|
||||||
|
@Override
|
||||||
|
public Tile tile(int x, int y){
|
||||||
|
return tiles[x][y];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resize(int width, int height){
|
||||||
|
createTiles(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Tile create(int x, int y, int floorID, int overlayID, int wallID){
|
||||||
|
return (tiles[x][y] = new Tile(x, y, floorID, overlayID, wallID));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isGenerating(){
|
||||||
|
return World.this.isGenerating();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void begin(){
|
||||||
|
beginMapLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void end(){
|
||||||
|
endMapLoad();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import io.anuke.mindustry.world.Tile;
|
|||||||
import io.anuke.mindustry.world.blocks.Floor;
|
import io.anuke.mindustry.world.blocks.Floor;
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.content;
|
import static io.anuke.mindustry.Vars.content;
|
||||||
|
import static io.anuke.mindustry.Vars.world;
|
||||||
|
|
||||||
public class DrawOperation{
|
public class DrawOperation{
|
||||||
private LongArray array = new LongArray();
|
private LongArray array = new LongArray();
|
||||||
@@ -24,32 +25,46 @@ public class DrawOperation{
|
|||||||
public void undo(MapEditor editor){
|
public void undo(MapEditor editor){
|
||||||
for(int i = array.size - 1; i >= 0; i--){
|
for(int i = array.size - 1; i >= 0; i--){
|
||||||
long l = array.get(i);
|
long l = array.get(i);
|
||||||
set(editor, editor.tile(TileOp.x(l), TileOp.y(l)), TileOp.type(l), TileOp.from(l));
|
array.set(i, TileOp.get(TileOp.x(l), TileOp.y(l), TileOp.type(l), get(editor.tile(TileOp.x(l), TileOp.y(l)), TileOp.type(l))));
|
||||||
|
set(editor, editor.tile(TileOp.x(l), TileOp.y(l)), TileOp.type(l), TileOp.value(l));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void redo(MapEditor editor){
|
public void redo(MapEditor editor){
|
||||||
for(int i = 0; i < array.size; i++){
|
for(int i = 0; i < array.size; i++){
|
||||||
long l = array.get(i);
|
long l = array.get(i);
|
||||||
set(editor, editor.tile(TileOp.x(l), TileOp.y(l)), TileOp.type(l), TileOp.to(l));
|
array.set(i, TileOp.get(TileOp.x(l), TileOp.y(l), TileOp.type(l), get(editor.tile(TileOp.x(l), TileOp.y(l)), TileOp.type(l))));
|
||||||
|
set(editor, editor.tile(TileOp.x(l), TileOp.y(l)), TileOp.type(l), TileOp.value(l));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void set(MapEditor editor, Tile tile, byte type, byte to){
|
short get(Tile tile, byte type){
|
||||||
|
if(type == OpType.floor.ordinal()){
|
||||||
|
return tile.floorID();
|
||||||
|
}else if(type == OpType.block.ordinal()){
|
||||||
|
return tile.blockID();
|
||||||
|
}else if(type == OpType.rotation.ordinal()){
|
||||||
|
return tile.rotation();
|
||||||
|
}else if(type == OpType.team.ordinal()){
|
||||||
|
return tile.getTeamID();
|
||||||
|
}else if(type == OpType.overlay.ordinal()){
|
||||||
|
return tile.overlayID();
|
||||||
|
}
|
||||||
|
throw new IllegalArgumentException("Invalid type.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(MapEditor editor, Tile tile, byte type, short to){
|
||||||
editor.load(() -> {
|
editor.load(() -> {
|
||||||
if(type == OpType.floor.ordinal()){
|
if(type == OpType.floor.ordinal()){
|
||||||
tile.setFloor((Floor)content.block(to));
|
tile.setFloor((Floor)content.block(to));
|
||||||
}else if(type == OpType.block.ordinal()){
|
}else if(type == OpType.block.ordinal()){
|
||||||
Block block = content.block(to);
|
Block block = content.block(to);
|
||||||
tile.setBlock(block);
|
world.setBlock(tile, block, tile.getTeam(), tile.rotation());
|
||||||
if(block.isMultiblock()){
|
|
||||||
editor.updateLinks(block, tile.x, tile.y);
|
|
||||||
}
|
|
||||||
}else if(type == OpType.rotation.ordinal()){
|
}else if(type == OpType.rotation.ordinal()){
|
||||||
tile.setRotation(to);
|
tile.rotation(to);
|
||||||
}else if(type == OpType.team.ordinal()){
|
}else if(type == OpType.team.ordinal()){
|
||||||
tile.setTeam(Team.all[to]);
|
tile.setTeam(Team.all[to]);
|
||||||
}else if(type == OpType.ore.ordinal()){
|
}else if(type == OpType.overlay.ordinal()){
|
||||||
tile.setOverlayID(to);
|
tile.setOverlayID(to);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -61,8 +76,7 @@ public class DrawOperation{
|
|||||||
short x;
|
short x;
|
||||||
short y;
|
short y;
|
||||||
byte type;
|
byte type;
|
||||||
byte from;
|
short value;
|
||||||
byte to;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum OpType{
|
public enum OpType{
|
||||||
@@ -70,6 +84,6 @@ public class DrawOperation{
|
|||||||
block,
|
block,
|
||||||
rotation,
|
rotation,
|
||||||
team,
|
team,
|
||||||
ore
|
overlay
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package io.anuke.mindustry.editor;
|
package io.anuke.mindustry.editor;
|
||||||
|
|
||||||
import io.anuke.mindustry.content.Blocks;
|
import io.anuke.mindustry.content.Blocks;
|
||||||
|
import io.anuke.mindustry.core.GameState.State;
|
||||||
import io.anuke.mindustry.editor.DrawOperation.OpType;
|
import io.anuke.mindustry.editor.DrawOperation.OpType;
|
||||||
import io.anuke.mindustry.game.Team;
|
import io.anuke.mindustry.game.Team;
|
||||||
import io.anuke.mindustry.gen.TileOp;
|
import io.anuke.mindustry.gen.TileOp;
|
||||||
@@ -9,21 +10,23 @@ import io.anuke.mindustry.world.Tile;
|
|||||||
import io.anuke.mindustry.world.blocks.*;
|
import io.anuke.mindustry.world.blocks.*;
|
||||||
import io.anuke.mindustry.world.modules.*;
|
import io.anuke.mindustry.world.modules.*;
|
||||||
|
|
||||||
|
import static io.anuke.mindustry.Vars.state;
|
||||||
import static io.anuke.mindustry.Vars.ui;
|
import static io.anuke.mindustry.Vars.ui;
|
||||||
|
|
||||||
|
//TODO somehow remove or replace this class with a more flexible solution
|
||||||
public class EditorTile extends Tile{
|
public class EditorTile extends Tile{
|
||||||
|
|
||||||
public EditorTile(int x, int y, byte floor, byte wall){
|
public EditorTile(int x, int y, int floor, int overlay, int wall){
|
||||||
super(x, y, floor, wall);
|
super(x, y, floor, overlay, wall);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Team getTeam(){
|
|
||||||
return Team.all[getTeamID()];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setFloor(Floor type){
|
public void setFloor(Floor type){
|
||||||
|
if(state.is(State.playing)){
|
||||||
|
super.setFloor(type);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if(type instanceof OverlayFloor){
|
if(type instanceof OverlayFloor){
|
||||||
//don't place on liquids
|
//don't place on liquids
|
||||||
if(!floor.isLiquid){
|
if(!floor.isLiquid){
|
||||||
@@ -32,63 +35,91 @@ public class EditorTile extends Tile{
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Block previous = floor();
|
if(floor == type && overlayID() == 0) return;
|
||||||
Block ore = overlay();
|
if(overlayID() != 0) op(OpType.overlay, overlayID());
|
||||||
if(previous == type && ore == Blocks.air) return;
|
if(floor != type) op(OpType.floor, floor.id);
|
||||||
super.setFloor(type);
|
super.setFloor(type);
|
||||||
//ore may get nullified so make sure to save edits
|
|
||||||
if(overlay() != ore){
|
|
||||||
op(TileOp.get(x, y, (byte)OpType.ore.ordinal(), ore.id, overlay().id));
|
|
||||||
}
|
|
||||||
if(previous != type){
|
|
||||||
op(TileOp.get(x, y, (byte)OpType.floor.ordinal(), previous.id, type.id));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setBlock(Block type){
|
public void setBlock(Block type){
|
||||||
Block previous = block;
|
if(state.is(State.playing)){
|
||||||
byte pteam = getTeamID();
|
super.setBlock(type);
|
||||||
if(previous == type) return;
|
return;
|
||||||
super.setBlock(type);
|
|
||||||
if(pteam != getTeamID()){
|
|
||||||
op(TileOp.get(x, y, (byte)OpType.team.ordinal(), pteam, getTeamID()));
|
|
||||||
}
|
}
|
||||||
op(TileOp.get(x, y, (byte)OpType.block.ordinal(), previous.id, type.id));
|
|
||||||
|
|
||||||
|
if(block == type) return;
|
||||||
|
op(OpType.block, block.id);
|
||||||
|
if(rotation != 0) op(OpType.rotation, rotation);
|
||||||
|
if(team != 0) op(OpType.team, team);
|
||||||
|
super.setBlock(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setBlock(Block type, Team team, int rotation){
|
||||||
|
if(state.is(State.playing)){
|
||||||
|
super.setBlock(type, team, rotation);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setBlock(type);
|
||||||
|
setTeam(team);
|
||||||
|
rotation(rotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setTeam(Team team){
|
public void setTeam(Team team){
|
||||||
byte previous = getTeamID();
|
if(state.is(State.playing)){
|
||||||
if(previous == team.ordinal()) return;
|
super.setTeam(team);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(getTeamID() == team.ordinal()) return;
|
||||||
|
op(OpType.team, getTeamID());
|
||||||
super.setTeam(team);
|
super.setTeam(team);
|
||||||
op(TileOp.get(x, y, (byte)OpType.team.ordinal(), previous, (byte)team.ordinal()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setRotation(byte rotation){
|
public void rotation(int rotation){
|
||||||
byte previous = getRotation();
|
if(state.is(State.playing)){
|
||||||
if(previous == rotation) return;
|
super.rotation(rotation);
|
||||||
super.setRotation(rotation);
|
return;
|
||||||
op(TileOp.get(x, y, (byte)OpType.rotation.ordinal(), previous, rotation));
|
}
|
||||||
|
|
||||||
|
if(rotation == rotation()) return;
|
||||||
|
op(OpType.rotation, rotation());
|
||||||
|
super.rotation(rotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setOverlayID(byte ore){
|
public void setOverlayID(short overlay){
|
||||||
byte previous = getOverlayID();
|
if(state.is(State.playing)){
|
||||||
if(previous == ore) return;
|
super.setOverlayID(overlay);
|
||||||
super.setOverlayID(ore);
|
return;
|
||||||
op(TileOp.get(x, y, (byte)OpType.ore.ordinal(), previous, ore));
|
}
|
||||||
|
|
||||||
|
if(overlayID() == overlay) return;
|
||||||
|
op(OpType.overlay, overlay);
|
||||||
|
super.setOverlayID(overlay);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void preChanged(){
|
protected void preChanged(){
|
||||||
|
if(state.is(State.playing)){
|
||||||
|
super.preChanged();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
super.setTeam(Team.none);
|
super.setTeam(Team.none);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void changed(){
|
protected void changed(){
|
||||||
|
if(state.is(State.playing)){
|
||||||
|
super.changed();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
entity = null;
|
entity = null;
|
||||||
|
|
||||||
if(block == null){
|
if(block == null){
|
||||||
@@ -102,8 +133,7 @@ public class EditorTile extends Tile{
|
|||||||
Block block = block();
|
Block block = block();
|
||||||
|
|
||||||
if(block.hasEntity()){
|
if(block.hasEntity()){
|
||||||
entity = block.newEntity();
|
entity = block.newEntity().init(this, false);
|
||||||
entity.health = block.health;
|
|
||||||
entity.cons = new ConsumeModule(entity);
|
entity.cons = new ConsumeModule(entity);
|
||||||
if(block.hasItems) entity.items = new ItemModule();
|
if(block.hasItems) entity.items = new ItemModule();
|
||||||
if(block.hasLiquids) entity.liquids = new LiquidModule();
|
if(block.hasLiquids) entity.liquids = new LiquidModule();
|
||||||
@@ -111,7 +141,7 @@ public class EditorTile extends Tile{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void op(long op){
|
private void op(OpType type, short value){
|
||||||
ui.editor.editor.addTileOp(op);
|
ui.editor.editor.addTileOp(TileOp.get(x, y, (byte)type.ordinal(), value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,21 +15,7 @@ public enum EditorTool{
|
|||||||
public void touched(MapEditor editor, int x, int y){
|
public void touched(MapEditor editor, int x, int y){
|
||||||
if(!Structs.inBounds(x, y, editor.width(), editor.height())) return;
|
if(!Structs.inBounds(x, y, editor.width(), editor.height())) return;
|
||||||
|
|
||||||
Tile tile = editor.tile(x, y);
|
Tile tile = editor.tile(x, y).link();
|
||||||
|
|
||||||
byte link = tile.getLinkByte();
|
|
||||||
|
|
||||||
if(tile.isLinked()){
|
|
||||||
x -= (Pack.leftByte(link) - 8);
|
|
||||||
y -= (Pack.rightByte(link) - 8);
|
|
||||||
|
|
||||||
tile = editor.tile(x, y);
|
|
||||||
}
|
|
||||||
|
|
||||||
//do not.
|
|
||||||
if(tile.isLinked()){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
editor.drawBlock = tile.block() == Blocks.air ? tile.overlay() == Blocks.air ? tile.floor() : tile.overlay() : tile.block();
|
editor.drawBlock = tile.block() == Blocks.air ? tile.overlay() == Blocks.air ? tile.floor() : tile.overlay() : tile.block();
|
||||||
}
|
}
|
||||||
@@ -87,7 +73,7 @@ public enum EditorTool{
|
|||||||
Block draw = editor.drawBlock;
|
Block draw = editor.drawBlock;
|
||||||
dest = draw instanceof OverlayFloor ? tile.overlay() : isfloor ? floor : block;
|
dest = draw instanceof OverlayFloor ? tile.overlay() : isfloor ? floor : block;
|
||||||
|
|
||||||
if(dest == draw || block == Blocks.part || block.isMultiblock()){
|
if(dest == draw || block instanceof BlockPart || block.isMultiblock()){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,7 +102,7 @@ public enum EditorTool{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(draw.rotate){
|
if(draw.rotate){
|
||||||
write.setRotation((byte)editor.rotation);
|
write.rotation((byte)editor.rotation);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +1,28 @@
|
|||||||
package io.anuke.mindustry.editor;
|
package io.anuke.mindustry.editor;
|
||||||
|
|
||||||
import io.anuke.arc.collection.ObjectMap;
|
import io.anuke.arc.collection.StringMap;
|
||||||
import io.anuke.arc.files.FileHandle;
|
import io.anuke.arc.files.FileHandle;
|
||||||
|
import io.anuke.arc.graphics.Pixmap;
|
||||||
import io.anuke.arc.math.Mathf;
|
import io.anuke.arc.math.Mathf;
|
||||||
import io.anuke.arc.util.Pack;
|
|
||||||
import io.anuke.arc.util.Structs;
|
import io.anuke.arc.util.Structs;
|
||||||
import io.anuke.mindustry.content.Blocks;
|
import io.anuke.mindustry.content.Blocks;
|
||||||
import io.anuke.mindustry.game.Team;
|
import io.anuke.mindustry.game.Team;
|
||||||
import io.anuke.mindustry.gen.TileOp;
|
import io.anuke.mindustry.gen.TileOp;
|
||||||
|
import io.anuke.mindustry.io.LegacyMapIO;
|
||||||
import io.anuke.mindustry.io.MapIO;
|
import io.anuke.mindustry.io.MapIO;
|
||||||
import io.anuke.mindustry.maps.Map;
|
import io.anuke.mindustry.maps.Map;
|
||||||
import io.anuke.mindustry.world.Block;
|
import io.anuke.mindustry.world.*;
|
||||||
import io.anuke.mindustry.world.Tile;
|
import io.anuke.mindustry.world.blocks.BlockPart;
|
||||||
import io.anuke.mindustry.world.blocks.Floor;
|
import io.anuke.mindustry.world.blocks.Floor;
|
||||||
|
|
||||||
import java.io.IOException;
|
import static io.anuke.mindustry.Vars.world;
|
||||||
|
|
||||||
public class MapEditor{
|
public class MapEditor{
|
||||||
public static final int[] brushSizes = {1, 2, 3, 4, 5, 9, 15, 20};
|
public static final int[] brushSizes = {1, 2, 3, 4, 5, 9, 15, 20};
|
||||||
|
|
||||||
private ObjectMap<String, String> tags = new ObjectMap<>();
|
private final Context context = new Context();
|
||||||
|
private StringMap tags = new StringMap();
|
||||||
private MapRenderer renderer = new MapRenderer(this);
|
private MapRenderer renderer = new MapRenderer(this);
|
||||||
private Tile[][] tiles;
|
|
||||||
|
|
||||||
private OperationStack stack = new OperationStack();
|
private OperationStack stack = new OperationStack();
|
||||||
private DrawOperation currentOp;
|
private DrawOperation currentOp;
|
||||||
@@ -32,7 +33,7 @@ public class MapEditor{
|
|||||||
public Block drawBlock = Blocks.stone;
|
public Block drawBlock = Blocks.stone;
|
||||||
public Team drawTeam = Team.blue;
|
public Team drawTeam = Team.blue;
|
||||||
|
|
||||||
public ObjectMap<String, String> getTags(){
|
public StringMap getTags(){
|
||||||
return tags;
|
return tags;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -40,39 +41,39 @@ public class MapEditor{
|
|||||||
reset();
|
reset();
|
||||||
|
|
||||||
loading = true;
|
loading = true;
|
||||||
tiles = createTiles(width, height);
|
createTiles(width, height);
|
||||||
renderer.resize(width(), height());
|
renderer.resize(width(), height());
|
||||||
loading = false;
|
loading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void beginEdit(Map map) throws IOException{
|
public void beginEdit(Map map){
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
loading = true;
|
loading = true;
|
||||||
tiles = createTiles(map.width, map.height);
|
|
||||||
tags.putAll(map.tags);
|
tags.putAll(map.tags);
|
||||||
MapIO.readTiles(map, tiles);
|
MapIO.loadMap(map, context);
|
||||||
checkLinkedTiles();
|
checkLinkedTiles();
|
||||||
renderer.resize(width(), height());
|
renderer.resize(width(), height());
|
||||||
loading = false;
|
loading = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void beginEdit(Tile[][] tiles){
|
public void beginEdit(Pixmap pixmap){
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
this.tiles = tiles;
|
createTiles(pixmap.getWidth(), pixmap.getHeight());
|
||||||
checkLinkedTiles();
|
load(() -> LegacyMapIO.readPixmap(pixmap, tiles()));
|
||||||
renderer.resize(width(), height());
|
renderer.resize(width(), height());
|
||||||
}
|
}
|
||||||
|
|
||||||
//adds missing blockparts
|
//adds missing blockparts
|
||||||
public void checkLinkedTiles(){
|
public void checkLinkedTiles(){
|
||||||
|
Tile[][] tiles = world.getTiles();
|
||||||
|
|
||||||
//clear block parts first
|
//clear block parts first
|
||||||
for(int x = 0; x < width(); x++){
|
for(int x = 0; x < width(); x++){
|
||||||
for(int y = 0; y < height(); y++){
|
for(int y = 0; y < height(); y++){
|
||||||
if(tiles[x][y].block() == Blocks.part){
|
if(tiles[x][y].block() instanceof BlockPart){
|
||||||
tiles[x][y].setBlock(Blocks.air);
|
tiles[x][y].setBlock(Blocks.air);
|
||||||
tiles[x][y].setLinkByte((byte)0);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,22 +81,8 @@ public class MapEditor{
|
|||||||
//set up missing blockparts
|
//set up missing blockparts
|
||||||
for(int x = 0; x < width(); x++){
|
for(int x = 0; x < width(); x++){
|
||||||
for(int y = 0; y < height(); y++){
|
for(int y = 0; y < height(); y++){
|
||||||
Block drawBlock = tiles[x][y].block();
|
if(tiles[x][y].block().isMultiblock()){
|
||||||
if(drawBlock.isMultiblock()){
|
world.setBlock(tiles[x][y], tiles[x][y].block(), tiles[x][y].getTeam());
|
||||||
int offsetx = -(drawBlock.size - 1) / 2;
|
|
||||||
int offsety = -(drawBlock.size - 1) / 2;
|
|
||||||
for(int dx = 0; dx < drawBlock.size; dx++){
|
|
||||||
for(int dy = 0; dy < drawBlock.size; dy++){
|
|
||||||
int worldx = dx + offsetx + x;
|
|
||||||
int worldy = dy + offsety + y;
|
|
||||||
|
|
||||||
if(Structs.inBounds(worldx, worldy, width(), height()) && !(dx + offsetx == 0 && dy + offsety == 0)){
|
|
||||||
Tile tile = tiles[worldx][worldy];
|
|
||||||
tile.setBlock(Blocks.part);
|
|
||||||
tile.setLinkByte(Pack.byteByte((byte)(dx + offsetx + 8), (byte)(dy + offsety + 8)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -108,63 +95,41 @@ public class MapEditor{
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a 2-D array of EditorTiles with stone as the floor block. */
|
/** Creates a 2-D array of EditorTiles with stone as the floor block. */
|
||||||
public Tile[][] createTiles(int width, int height){
|
private void createTiles(int width, int height){
|
||||||
tiles = new Tile[width][height];
|
Tile[][] tiles = world.createTiles(width, height);
|
||||||
|
|
||||||
for(int x = 0; x < width; x++){
|
for(int x = 0; x < width; x++){
|
||||||
for(int y = 0; y < height; y++){
|
for(int y = 0; y < height; y++){
|
||||||
tiles[x][y] = new EditorTile(x, y, Blocks.stone.id, (byte)0);
|
tiles[x][y] = new EditorTile(x, y, Blocks.stone.id, (short)0, (short)0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return tiles;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map createMap(FileHandle file){
|
public Map createMap(FileHandle file){
|
||||||
return new Map(file, width(), height(), new ObjectMap<>(tags), true);
|
return new Map(file, width(), height(), new StringMap(tags), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reset(){
|
private void reset(){
|
||||||
clearOp();
|
clearOp();
|
||||||
brushSize = 1;
|
brushSize = 1;
|
||||||
drawBlock = Blocks.stone;
|
drawBlock = Blocks.stone;
|
||||||
tags = new ObjectMap<>();
|
tags = new StringMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Tile[][] tiles(){
|
public Tile[][] tiles(){
|
||||||
return tiles;
|
return world.getTiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Tile tile(int x, int y){
|
public Tile tile(int x, int y){
|
||||||
return tiles[x][y];
|
return world.rawTile(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int width(){
|
public int width(){
|
||||||
return tiles.length;
|
return world.width();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int height(){
|
public int height(){
|
||||||
return tiles[0].length;
|
return world.height();
|
||||||
}
|
|
||||||
|
|
||||||
public void updateLinks(Block block, int x, int y){
|
|
||||||
int offsetx = -(block.size - 1) / 2;
|
|
||||||
int offsety = -(block.size - 1) / 2;
|
|
||||||
|
|
||||||
for(int dx = 0; dx < block.size; dx++){
|
|
||||||
for(int dy = 0; dy < block.size; dy++){
|
|
||||||
int worldx = dx + offsetx + x;
|
|
||||||
int worldy = dy + offsety + y;
|
|
||||||
|
|
||||||
if(Structs.inBounds(worldx, worldy, width(), height())){
|
|
||||||
Tile tile = tiles[worldx][worldy];
|
|
||||||
|
|
||||||
if(!(worldx == x && worldy == y)){
|
|
||||||
tile.setBlock(Blocks.part);
|
|
||||||
tile.setLinkByte(Pack.byteByte((byte)(dx + offsetx + 8), (byte)(dy + offsety + 8)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void draw(int x, int y, boolean paint){
|
public void draw(int x, int y, boolean paint){
|
||||||
@@ -177,45 +142,36 @@ public class MapEditor{
|
|||||||
|
|
||||||
public void draw(int x, int y, boolean paint, Block drawBlock, double chance){
|
public void draw(int x, int y, boolean paint, Block drawBlock, double chance){
|
||||||
boolean isfloor = drawBlock instanceof Floor && drawBlock != Blocks.air;
|
boolean isfloor = drawBlock instanceof Floor && drawBlock != Blocks.air;
|
||||||
|
Tile[][] tiles = world.getTiles();
|
||||||
|
|
||||||
if(drawBlock.isMultiblock()){
|
if(drawBlock.isMultiblock()){
|
||||||
|
|
||||||
x = Mathf.clamp(x, (drawBlock.size - 1) / 2, width() - drawBlock.size / 2 - 1);
|
x = Mathf.clamp(x, (drawBlock.size - 1) / 2, width() - drawBlock.size / 2 - 1);
|
||||||
y = Mathf.clamp(y, (drawBlock.size - 1) / 2, height() - drawBlock.size / 2 - 1);
|
y = Mathf.clamp(y, (drawBlock.size - 1) / 2, height() - drawBlock.size / 2 - 1);
|
||||||
|
|
||||||
int offsetx = -(drawBlock.size - 1) / 2;
|
int offsetx = -(drawBlock.size - 1) / 2;
|
||||||
int offsety = -(drawBlock.size - 1) / 2;
|
int offsety = -(drawBlock.size - 1) / 2;
|
||||||
|
|
||||||
for(int i = 0; i < 2; i++){
|
for(int dx = 0; dx < drawBlock.size; dx++){
|
||||||
for(int dx = 0; dx < drawBlock.size; dx++){
|
for(int dy = 0; dy < drawBlock.size; dy++){
|
||||||
for(int dy = 0; dy < drawBlock.size; dy++){
|
int worldx = dx + offsetx + x;
|
||||||
int worldx = dx + offsetx + x;
|
int worldy = dy + offsety + y;
|
||||||
int worldy = dy + offsety + y;
|
|
||||||
|
|
||||||
if(Structs.inBounds(worldx, worldy, width(), height())){
|
if(Structs.inBounds(worldx, worldy, width(), height())){
|
||||||
Tile tile = tiles[worldx][worldy];
|
Tile tile = tiles[worldx][worldy];
|
||||||
|
|
||||||
if(i == 1){
|
Block block = tile.block();
|
||||||
tile.setBlock(Blocks.part);
|
|
||||||
tile.setLinkByte(Pack.byteByte((byte)(dx + offsetx + 8), (byte)(dy + offsety + 8)));
|
|
||||||
}else{
|
|
||||||
byte link = tile.getLinkByte();
|
|
||||||
Block block = tile.block();
|
|
||||||
|
|
||||||
if(link != 0){
|
//bail out if there's anything blocking the way
|
||||||
removeLinked(worldx - (Pack.leftByte(link) - 8), worldy - (Pack.rightByte(link) - 8));
|
if(block.isMultiblock() || block instanceof BlockPart){
|
||||||
}else if(block.isMultiblock()){
|
return;
|
||||||
removeLinked(worldx, worldy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderer.updatePoint(worldx, worldy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Tile tile = tiles[x][y];
|
world.setBlock(tiles[x][y], drawBlock, drawTeam);
|
||||||
tile.setBlock(drawBlock);
|
|
||||||
tile.setTeam(drawTeam);
|
|
||||||
}else{
|
}else{
|
||||||
for(int rx = -brushSize; rx <= brushSize; rx++){
|
for(int rx = -brushSize; rx <= brushSize; rx++){
|
||||||
for(int ry = -brushSize; ry <= brushSize; ry++){
|
for(int ry = -brushSize; ry <= brushSize; ry++){
|
||||||
@@ -228,14 +184,8 @@ public class MapEditor{
|
|||||||
|
|
||||||
Tile tile = tiles[wx][wy];
|
Tile tile = tiles[wx][wy];
|
||||||
|
|
||||||
if(!isfloor){
|
if(!isfloor && (tile.isLinked() || tile.block().isMultiblock())){
|
||||||
byte link = tile.getLinkByte();
|
world.removeBlock(tile.link());
|
||||||
|
|
||||||
if(tile.block().isMultiblock()){
|
|
||||||
removeLinked(wx, wy);
|
|
||||||
}else if(link != 0 && tiles[wx][wy].block() == Blocks.part){
|
|
||||||
removeLinked(wx - (Pack.leftByte(link) - 8), wy - (Pack.rightByte(link) - 8));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(isfloor){
|
if(isfloor){
|
||||||
@@ -246,7 +196,7 @@ public class MapEditor{
|
|||||||
tile.setTeam(drawTeam);
|
tile.setTeam(drawTeam);
|
||||||
}
|
}
|
||||||
if(drawBlock.rotate){
|
if(drawBlock.rotate){
|
||||||
tile.setRotation((byte)rotation);
|
tile.rotation((byte)rotation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -255,22 +205,6 @@ public class MapEditor{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeLinked(int x, int y){
|
|
||||||
Block block = tiles[x][y].block();
|
|
||||||
|
|
||||||
int offsetx = -(block.size - 1) / 2;
|
|
||||||
int offsety = -(block.size - 1) / 2;
|
|
||||||
for(int dx = 0; dx < block.size; dx++){
|
|
||||||
for(int dy = 0; dy < block.size; dy++){
|
|
||||||
int worldx = x + dx + offsetx, worldy = y + dy + offsety;
|
|
||||||
if(Structs.inBounds(worldx, worldy, width(), height())){
|
|
||||||
tiles[worldx][worldy].setTeam(Team.none);
|
|
||||||
tiles[worldx][worldy].setBlock(Blocks.air);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public MapRenderer renderer(){
|
public MapRenderer renderer(){
|
||||||
return renderer;
|
return renderer;
|
||||||
}
|
}
|
||||||
@@ -278,11 +212,11 @@ public class MapEditor{
|
|||||||
public void resize(int width, int height){
|
public void resize(int width, int height){
|
||||||
clearOp();
|
clearOp();
|
||||||
|
|
||||||
Tile[][] previous = tiles;
|
Tile[][] previous = world.getTiles();
|
||||||
int offsetX = -(width - width()) / 2, offsetY = -(height - height()) / 2;
|
int offsetX = -(width - width()) / 2, offsetY = -(height - height()) / 2;
|
||||||
loading = true;
|
loading = true;
|
||||||
|
|
||||||
tiles = new Tile[width][height];
|
Tile[][] tiles = world.createTiles(width, height);
|
||||||
for(int x = 0; x < width; x++){
|
for(int x = 0; x < width; x++){
|
||||||
for(int y = 0; y < height; y++){
|
for(int y = 0; y < height; y++){
|
||||||
int px = offsetX + x, py = offsetY + y;
|
int px = offsetX + x, py = offsetY + y;
|
||||||
@@ -291,7 +225,7 @@ public class MapEditor{
|
|||||||
tiles[x][y].x = (short)x;
|
tiles[x][y].x = (short)x;
|
||||||
tiles[x][y].y = (short)y;
|
tiles[x][y].y = (short)y;
|
||||||
}else{
|
}else{
|
||||||
tiles[x][y] = new EditorTile(x, y, Blocks.stone.id, (byte)0);
|
tiles[x][y] = new EditorTile(x, y, Blocks.stone.id, (short)0, (short)0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -338,4 +272,36 @@ public class MapEditor{
|
|||||||
|
|
||||||
renderer.updatePoint(TileOp.x(data), TileOp.y(data));
|
renderer.updatePoint(TileOp.x(data), TileOp.y(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class Context implements WorldContext{
|
||||||
|
@Override
|
||||||
|
public Tile tile(int x, int y){
|
||||||
|
return world.tile(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void resize(int width, int height){
|
||||||
|
world.createTiles(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Tile create(int x, int y, int floorID, int overlayID, int wallID){
|
||||||
|
return (tiles()[x][y] = new EditorTile(x, y, floorID, overlayID, wallID));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isGenerating(){
|
||||||
|
return world.isGenerating();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void begin(){
|
||||||
|
world.beginMapLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void end(){
|
||||||
|
world.endMapLoad();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ package io.anuke.mindustry.editor;
|
|||||||
|
|
||||||
import io.anuke.arc.Core;
|
import io.anuke.arc.Core;
|
||||||
import io.anuke.arc.collection.Array;
|
import io.anuke.arc.collection.Array;
|
||||||
|
import io.anuke.arc.collection.StringMap;
|
||||||
import io.anuke.arc.files.FileHandle;
|
import io.anuke.arc.files.FileHandle;
|
||||||
import io.anuke.arc.function.Consumer;
|
import io.anuke.arc.function.Consumer;
|
||||||
import io.anuke.arc.graphics.Color;
|
import io.anuke.arc.graphics.Color;
|
||||||
@@ -17,8 +18,10 @@ import io.anuke.arc.scene.ui.layout.Table;
|
|||||||
import io.anuke.arc.scene.ui.layout.Unit;
|
import io.anuke.arc.scene.ui.layout.Unit;
|
||||||
import io.anuke.arc.util.*;
|
import io.anuke.arc.util.*;
|
||||||
import io.anuke.mindustry.Vars;
|
import io.anuke.mindustry.Vars;
|
||||||
|
import io.anuke.mindustry.core.GameState.State;
|
||||||
import io.anuke.mindustry.core.Platform;
|
import io.anuke.mindustry.core.Platform;
|
||||||
import io.anuke.mindustry.game.Team;
|
import io.anuke.mindustry.game.*;
|
||||||
|
import io.anuke.mindustry.io.JsonIO;
|
||||||
import io.anuke.mindustry.io.MapIO;
|
import io.anuke.mindustry.io.MapIO;
|
||||||
import io.anuke.mindustry.maps.Map;
|
import io.anuke.mindustry.maps.Map;
|
||||||
import io.anuke.mindustry.ui.dialogs.FloatingDialog;
|
import io.anuke.mindustry.ui.dialogs.FloatingDialog;
|
||||||
@@ -40,6 +43,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
|||||||
private MapGenerateDialog generateDialog;
|
private MapGenerateDialog generateDialog;
|
||||||
private ScrollPane pane;
|
private ScrollPane pane;
|
||||||
private FloatingDialog menu;
|
private FloatingDialog menu;
|
||||||
|
private Rules lastSavedRules;
|
||||||
private boolean saved = false;
|
private boolean saved = false;
|
||||||
private boolean shownWithMap = false;
|
private boolean shownWithMap = false;
|
||||||
private Array<Block> blocksOut = new Array<>();
|
private Array<Block> blocksOut = new Array<>();
|
||||||
@@ -91,7 +95,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
|||||||
Platform.instance.showFileChooser("$editor.loadmap", "Map Files", file -> ui.loadAnd(() -> {
|
Platform.instance.showFileChooser("$editor.loadmap", "Map Files", file -> ui.loadAnd(() -> {
|
||||||
try{
|
try{
|
||||||
//TODO what if it's an image? users should be warned for their stupidity
|
//TODO what if it's an image? users should be warned for their stupidity
|
||||||
editor.beginEdit(MapIO.readMap(file, true));
|
editor.beginEdit(MapIO.createMap(file, true));
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, false)));
|
ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, false)));
|
||||||
Log.err(e);
|
Log.err(e);
|
||||||
@@ -103,9 +107,8 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
|||||||
ui.loadAnd(() -> {
|
ui.loadAnd(() -> {
|
||||||
try{
|
try{
|
||||||
Pixmap pixmap = new Pixmap(file);
|
Pixmap pixmap = new Pixmap(file);
|
||||||
Tile[][] tiles = editor.createTiles(pixmap.getWidth(), pixmap.getHeight());
|
editor.beginEdit(pixmap);
|
||||||
editor.load(() -> MapIO.readLegacyPixmap(pixmap, tiles));
|
pixmap.dispose();
|
||||||
editor.beginEdit(tiles);
|
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, false)));
|
ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, false)));
|
||||||
Log.err(e);
|
Log.err(e);
|
||||||
@@ -122,7 +125,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
|||||||
if(!editor.getTags().containsKey("name")){
|
if(!editor.getTags().containsKey("name")){
|
||||||
editor.getTags().put("name", result.nameWithoutExtension());
|
editor.getTags().put("name", result.nameWithoutExtension());
|
||||||
}
|
}
|
||||||
MapIO.writeMap(result, editor.createMap(result), editor.tiles());
|
MapIO.writeMap(result, editor.createMap(result));
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
ui.showError(Core.bundle.format("editor.errorsave", Strings.parseException(e, false)));
|
ui.showError(Core.bundle.format("editor.errorsave", Strings.parseException(e, false)));
|
||||||
Log.err(e);
|
Log.err(e);
|
||||||
@@ -133,10 +136,14 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
|||||||
|
|
||||||
menu.cont.row();
|
menu.cont.row();
|
||||||
|
|
||||||
|
menu.cont.addImageTextButton("$editor.ingame", "icon-arrow", isize, this::playtest).padTop(-5).size(swidth * 2f + 10, 60f);
|
||||||
|
|
||||||
|
menu.cont.row();
|
||||||
|
|
||||||
menu.cont.addImageTextButton("$quit", "icon-back", isize, () -> {
|
menu.cont.addImageTextButton("$quit", "icon-back", isize, () -> {
|
||||||
tryExit();
|
tryExit();
|
||||||
menu.hide();
|
menu.hide();
|
||||||
}).padTop(-5).size(swidth * 2f + 10, 60f);
|
}).size(swidth * 2f + 10, 60f);
|
||||||
|
|
||||||
resizeDialog = new MapResizeDialog(editor, (x, y) -> {
|
resizeDialog = new MapResizeDialog(editor, (x, y) -> {
|
||||||
if(!(editor.width() == x && editor.height() == y)){
|
if(!(editor.width() == x && editor.height() == y)){
|
||||||
@@ -181,11 +188,14 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
|||||||
});
|
});
|
||||||
|
|
||||||
shown(() -> {
|
shown(() -> {
|
||||||
|
//clear units, rules and other unnecessary stuff
|
||||||
|
logic.reset();
|
||||||
saved = true;
|
saved = true;
|
||||||
if(!Core.settings.getBool("landscape")) Platform.instance.beginForceLandscape();
|
if(!Core.settings.getBool("landscape")) Platform.instance.beginForceLandscape();
|
||||||
editor.clearOp();
|
editor.clearOp();
|
||||||
Core.scene.setScrollFocus(view);
|
Core.scene.setScrollFocus(view);
|
||||||
if(!shownWithMap){
|
if(!shownWithMap){
|
||||||
|
state.rules = new Rules();
|
||||||
editor.beginEdit(200, 200);
|
editor.beginEdit(200, 200);
|
||||||
}
|
}
|
||||||
shownWithMap = false;
|
shownWithMap = false;
|
||||||
@@ -205,8 +215,47 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
|||||||
drawDefaultBackground(x, y);
|
drawDefaultBackground(x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void resumeEditing(){
|
||||||
|
state.set(State.menu);
|
||||||
|
shownWithMap = true;
|
||||||
|
show();
|
||||||
|
state.rules = (lastSavedRules == null ? new Rules() : lastSavedRules);
|
||||||
|
lastSavedRules = null;
|
||||||
|
editor.renderer().updateAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void playtest(){
|
||||||
|
menu.hide();
|
||||||
|
ui.loadAnd(() -> {
|
||||||
|
lastSavedRules = state.rules;
|
||||||
|
hide();
|
||||||
|
//only reset the player; logic.reset() will clear entities, which we do not want
|
||||||
|
player.reset();
|
||||||
|
state.rules = Gamemode.editor.apply(new Rules());
|
||||||
|
world.setMap(new Map(StringMap.of(
|
||||||
|
"name", "Editor Playtesting",
|
||||||
|
"width", editor.width(),
|
||||||
|
"height", editor.height()
|
||||||
|
)));
|
||||||
|
world.endMapLoad();
|
||||||
|
//add entities so they update. is this really needed?
|
||||||
|
for(int x = 0; x < world.width(); x++){
|
||||||
|
for(int y = 0; y < world.height(); y++){
|
||||||
|
Tile tile = world.rawTile(x, y);
|
||||||
|
if(tile.entity != null){
|
||||||
|
tile.entity.add();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
player.set(world.width() * tilesize/2f, world.height() * tilesize/2f);
|
||||||
|
player.setDead(false);
|
||||||
|
logic.play();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private void save(){
|
private void save(){
|
||||||
String name = editor.getTags().get("name", "").trim();
|
String name = editor.getTags().get("name", "").trim();
|
||||||
|
editor.getTags().put("rules", JsonIO.write(state.rules));
|
||||||
|
|
||||||
if(name.isEmpty()){
|
if(name.isEmpty()){
|
||||||
infoDialog.show();
|
infoDialog.show();
|
||||||
@@ -216,7 +265,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
|||||||
if(map != null && !map.custom){
|
if(map != null && !map.custom){
|
||||||
ui.showError("$editor.save.overwrite");
|
ui.showError("$editor.save.overwrite");
|
||||||
}else{
|
}else{
|
||||||
world.maps.saveMap(editor.getTags(), editor.tiles());
|
world.maps.saveMap(editor.getTags());
|
||||||
ui.showInfoFade("$editor.saved");
|
ui.showInfoFade("$editor.saved");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -281,9 +330,8 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
|||||||
public void beginEditMap(FileHandle file){
|
public void beginEditMap(FileHandle file){
|
||||||
ui.loadAnd(() -> {
|
ui.loadAnd(() -> {
|
||||||
try{
|
try{
|
||||||
Map map = MapIO.readMap(file, true);
|
|
||||||
shownWithMap = true;
|
shownWithMap = true;
|
||||||
editor.beginEdit(map);
|
editor.beginEdit(MapIO.createMap(file, true));
|
||||||
show();
|
show();
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
Log.err(e);
|
Log.err(e);
|
||||||
|
|||||||
@@ -224,7 +224,7 @@ public class MapGenerateDialog extends FloatingDialog{
|
|||||||
Tile tile = editor.tile(x, y);
|
Tile tile = editor.tile(x, y);
|
||||||
input.begin(editor, x, y, tile.floor(), tile.block(), tile.overlay());
|
input.begin(editor, x, y, tile.floor(), tile.block(), tile.overlay());
|
||||||
filter.apply(input);
|
filter.apply(input);
|
||||||
writeTiles[x][y].set(input.floor, input.block, input.ore, tile.getTeam(), tile.getRotation());
|
writeTiles[x][y].set(input.floor, input.block, input.ore, tile.getTeam(), tile.rotation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,7 +235,7 @@ public class MapGenerateDialog extends FloatingDialog{
|
|||||||
Tile tile = editor.tile(x, y);
|
Tile tile = editor.tile(x, y);
|
||||||
DummyTile write = writeTiles[x][y];
|
DummyTile write = writeTiles[x][y];
|
||||||
|
|
||||||
tile.setRotation(write.rotation);
|
tile.rotation(write.rotation);
|
||||||
tile.setFloor((Floor)content.block(write.floor));
|
tile.setFloor((Floor)content.block(write.floor));
|
||||||
tile.setBlock(content.block(write.block));
|
tile.setBlock(content.block(write.block));
|
||||||
tile.setTeam(Team.all[write.team]);
|
tile.setTeam(Team.all[write.team]);
|
||||||
@@ -322,7 +322,8 @@ public class MapGenerateDialog extends FloatingDialog{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class DummyTile{
|
public static class DummyTile{
|
||||||
public byte block, floor, ore, team, rotation;
|
public byte team, rotation;
|
||||||
|
public short block, floor, ore;
|
||||||
|
|
||||||
void set(Block floor, Block wall, Block ore, Team team, int rotation){
|
void set(Block floor, Block wall, Block ore, Team team, int rotation){
|
||||||
this.floor = floor.id;
|
this.floor = floor.id;
|
||||||
@@ -341,7 +342,7 @@ public class MapGenerateDialog extends FloatingDialog{
|
|||||||
}
|
}
|
||||||
|
|
||||||
void set(Tile other){
|
void set(Tile other){
|
||||||
set(other.floor(), other.block(), other.overlay(), other.getTeam(), other.getRotation());
|
set(other.floor(), other.block(), other.overlay(), other.getTeam(), other.rotation());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,12 +4,16 @@ import io.anuke.arc.Core;
|
|||||||
import io.anuke.arc.collection.ObjectMap;
|
import io.anuke.arc.collection.ObjectMap;
|
||||||
import io.anuke.arc.scene.ui.TextArea;
|
import io.anuke.arc.scene.ui.TextArea;
|
||||||
import io.anuke.arc.scene.ui.TextField;
|
import io.anuke.arc.scene.ui.TextField;
|
||||||
|
import io.anuke.mindustry.Vars;
|
||||||
import io.anuke.mindustry.core.Platform;
|
import io.anuke.mindustry.core.Platform;
|
||||||
|
import io.anuke.mindustry.game.Rules;
|
||||||
|
import io.anuke.mindustry.ui.dialogs.CustomRulesDialog;
|
||||||
import io.anuke.mindustry.ui.dialogs.FloatingDialog;
|
import io.anuke.mindustry.ui.dialogs.FloatingDialog;
|
||||||
|
|
||||||
public class MapInfoDialog extends FloatingDialog{
|
public class MapInfoDialog extends FloatingDialog{
|
||||||
private final MapEditor editor;
|
private final MapEditor editor;
|
||||||
private final WaveInfoDialog waveinfo;
|
private final WaveInfoDialog waveinfo;
|
||||||
|
private final CustomRulesDialog ruleinfo = new CustomRulesDialog();
|
||||||
|
|
||||||
public MapInfoDialog(MapEditor editor){
|
public MapInfoDialog(MapEditor editor){
|
||||||
super("$editor.mapinfo");
|
super("$editor.mapinfo");
|
||||||
@@ -36,7 +40,6 @@ public class MapInfoDialog extends FloatingDialog{
|
|||||||
name.setMessageText("$unknown");
|
name.setMessageText("$unknown");
|
||||||
|
|
||||||
cont.row();
|
cont.row();
|
||||||
|
|
||||||
cont.add("$editor.description").padRight(8).left();
|
cont.add("$editor.description").padRight(8).left();
|
||||||
|
|
||||||
TextArea description = cont.addArea(tags.get("description", ""), "textarea", text -> {
|
TextArea description = cont.addArea(tags.get("description", ""), "textarea", text -> {
|
||||||
@@ -44,7 +47,6 @@ public class MapInfoDialog extends FloatingDialog{
|
|||||||
}).size(400f, 140f).get();
|
}).size(400f, 140f).get();
|
||||||
|
|
||||||
cont.row();
|
cont.row();
|
||||||
|
|
||||||
cont.add("$editor.author").padRight(8).left();
|
cont.add("$editor.author").padRight(8).left();
|
||||||
|
|
||||||
TextField author = cont.addField(tags.get("author", Core.settings.getString("mapAuthor", "")), text -> {
|
TextField author = cont.addField(tags.get("author", Core.settings.getString("mapAuthor", "")), text -> {
|
||||||
@@ -54,14 +56,13 @@ public class MapInfoDialog extends FloatingDialog{
|
|||||||
}).size(400, 55f).get();
|
}).size(400, 55f).get();
|
||||||
author.setMessageText("$unknown");
|
author.setMessageText("$unknown");
|
||||||
|
|
||||||
|
cont.row();
|
||||||
|
cont.add("$editor.rules").padRight(8).left();
|
||||||
|
cont.addButton("$edit", () -> ruleinfo.show(Vars.state.rules, () -> Vars.state.rules = new Rules())).left().width(200f);;
|
||||||
|
|
||||||
cont.row();
|
cont.row();
|
||||||
cont.add("$editor.waves").padRight(8).left();
|
cont.add("$editor.waves").padRight(8).left();
|
||||||
cont.table(t -> {
|
cont.addButton("$edit", waveinfo::show).left().width(200f);
|
||||||
t.add().growX();
|
|
||||||
t.label(() -> tags.containsKey("waves") ? "" : Core.bundle.get("editor.default")).left();
|
|
||||||
t.add().growX();
|
|
||||||
t.addButton("$edit", waveinfo::show).growY().width(200f);
|
|
||||||
}).size(400, 55f);
|
|
||||||
|
|
||||||
name.change();
|
name.change();
|
||||||
description.change();
|
description.change();
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import io.anuke.mindustry.game.Team;
|
|||||||
import io.anuke.mindustry.graphics.IndexedRenderer;
|
import io.anuke.mindustry.graphics.IndexedRenderer;
|
||||||
import io.anuke.mindustry.world.Block;
|
import io.anuke.mindustry.world.Block;
|
||||||
import io.anuke.mindustry.world.Tile;
|
import io.anuke.mindustry.world.Tile;
|
||||||
|
import io.anuke.mindustry.world.blocks.BlockPart;
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.tilesize;
|
import static io.anuke.mindustry.Vars.tilesize;
|
||||||
|
|
||||||
@@ -84,7 +85,6 @@ public class MapRenderer implements Disposable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void updatePoint(int x, int y){
|
public void updatePoint(int x, int y){
|
||||||
//TODO spread out over multiple frames?
|
|
||||||
updates.add(x + y * width);
|
updates.add(x + y * width);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -110,13 +110,13 @@ public class MapRenderer implements Disposable{
|
|||||||
int idxWall = (wx % chunksize) + (wy % chunksize) * chunksize;
|
int idxWall = (wx % chunksize) + (wy % chunksize) * chunksize;
|
||||||
int idxDecal = (wx % chunksize) + (wy % chunksize) * chunksize + chunksize * chunksize;
|
int idxDecal = (wx % chunksize) + (wy % chunksize) * chunksize + chunksize * chunksize;
|
||||||
|
|
||||||
if(wall != Blocks.air && (wall.synthetic() || wall == Blocks.part)){
|
if(wall != Blocks.air && (wall.synthetic() || wall instanceof BlockPart)){
|
||||||
region = !Core.atlas.isFound(wall.editorIcon()) ? Core.atlas.find("clear-editor") : wall.editorIcon();
|
region = !Core.atlas.isFound(wall.editorIcon()) ? Core.atlas.find("clear-editor") : wall.editorIcon();
|
||||||
|
|
||||||
if(wall.rotate){
|
if(wall.rotate){
|
||||||
mesh.draw(idxWall, region,
|
mesh.draw(idxWall, region,
|
||||||
wx * tilesize + wall.offset(), wy * tilesize + wall.offset(),
|
wx * tilesize + wall.offset(), wy * tilesize + wall.offset(),
|
||||||
region.getWidth() * Draw.scl, region.getHeight() * Draw.scl, tile.getRotation() * 90 - 90);
|
region.getWidth() * Draw.scl, region.getHeight() * Draw.scl, tile.rotation() * 90 - 90);
|
||||||
}else{
|
}else{
|
||||||
mesh.draw(idxWall, region,
|
mesh.draw(idxWall, region,
|
||||||
wx * tilesize + wall.offset() + (tilesize - region.getWidth() * Draw.scl) / 2f,
|
wx * tilesize + wall.offset() + (tilesize - region.getWidth() * Draw.scl) / 2f,
|
||||||
|
|||||||
@@ -85,9 +85,8 @@ public class Damage{
|
|||||||
public static void collideLine(Bullet hitter, Team team, Effect effect, float x, float y, float angle, float length){
|
public static void collideLine(Bullet hitter, Team team, Effect effect, float x, float y, float angle, float length){
|
||||||
tr.trns(angle, length);
|
tr.trns(angle, length);
|
||||||
world.raycastEachWorld(x, y, x + tr.x, y + tr.y, (cx, cy) -> {
|
world.raycastEachWorld(x, y, x + tr.x, y + tr.y, (cx, cy) -> {
|
||||||
Tile tile = world.tile(cx, cy);
|
Tile tile = world.ltile(cx, cy);
|
||||||
if(tile != null) tile = tile.target();
|
if(tile != null && tile.entity != null && tile.getTeamID() != team.ordinal() && tile.entity.collide(hitter)){
|
||||||
if(tile != null && tile.entity != null && tile.target().getTeamID() != team.ordinal() && tile.entity.collide(hitter)){
|
|
||||||
tile.entity.collision(hitter);
|
tile.entity.collision(hitter);
|
||||||
hitter.getBulletType().hit(hitter, tile.worldx(), tile.worldy());
|
hitter.getBulletType().hit(hitter, tile.worldx(), tile.worldy());
|
||||||
}
|
}
|
||||||
@@ -216,12 +215,10 @@ public class Damage{
|
|||||||
int scaledDamage = (int)(damage * (1f - (float)dst / radius));
|
int scaledDamage = (int)(damage * (1f - (float)dst / radius));
|
||||||
|
|
||||||
bits.set(bitOffset + x, bitOffset + y);
|
bits.set(bitOffset + x, bitOffset + y);
|
||||||
Tile tile = world.tile(startx + x, starty + y);
|
Tile tile = world.ltile(startx + x, starty + y);
|
||||||
|
|
||||||
if(scaledDamage <= 0 || tile == null) continue;
|
if(scaledDamage <= 0 || tile == null) continue;
|
||||||
|
|
||||||
tile = tile.target();
|
|
||||||
|
|
||||||
//apply damage to entity if needed
|
//apply damage to entity if needed
|
||||||
if(tile.entity != null && tile.getTeam() != team){
|
if(tile.entity != null && tile.getTeam() != team){
|
||||||
int health = (int)tile.entity.health;
|
int health = (int)tile.entity.health;
|
||||||
|
|||||||
@@ -7,10 +7,11 @@ import io.anuke.arc.function.Consumer;
|
|||||||
import io.anuke.arc.function.Predicate;
|
import io.anuke.arc.function.Predicate;
|
||||||
import io.anuke.arc.graphics.Camera;
|
import io.anuke.arc.graphics.Camera;
|
||||||
import io.anuke.arc.math.geom.Rectangle;
|
import io.anuke.arc.math.geom.Rectangle;
|
||||||
import io.anuke.mindustry.Vars;
|
|
||||||
import io.anuke.mindustry.entities.traits.DrawTrait;
|
import io.anuke.mindustry.entities.traits.DrawTrait;
|
||||||
import io.anuke.mindustry.entities.traits.Entity;
|
import io.anuke.mindustry.entities.traits.Entity;
|
||||||
|
|
||||||
|
import static io.anuke.mindustry.Vars.collisions;
|
||||||
|
|
||||||
public class Entities{
|
public class Entities{
|
||||||
public static final int maxLeafObjects = 4;
|
public static final int maxLeafObjects = 4;
|
||||||
private static final Array<EntityGroup<?>> groupArray = new Array<>();
|
private static final Array<EntityGroup<?>> groupArray = new Array<>();
|
||||||
@@ -48,7 +49,7 @@ public class Entities{
|
|||||||
group.updateEvents();
|
group.updateEvents();
|
||||||
|
|
||||||
if(group.useTree()){
|
if(group.useTree()){
|
||||||
Vars.collisions.updatePhysics(group);
|
collisions.updatePhysics(group);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(Entity e : group.all()){
|
for(Entity e : group.all()){
|
||||||
|
|||||||
@@ -225,9 +225,8 @@ public class Bullet extends SolidEntity implements DamageTrait, ScaleTrait, Pool
|
|||||||
if(type.hitTiles && collidesTiles() && !supressCollision && initialized){
|
if(type.hitTiles && collidesTiles() && !supressCollision && initialized){
|
||||||
world.raycastEach(world.toTile(lastPosition().x), world.toTile(lastPosition().y), world.toTile(x), world.toTile(y), (x, y) -> {
|
world.raycastEach(world.toTile(lastPosition().x), world.toTile(lastPosition().y), world.toTile(x), world.toTile(y), (x, y) -> {
|
||||||
|
|
||||||
Tile tile = world.tile(x, y);
|
Tile tile = world.ltile(x, y);
|
||||||
if(tile == null) return false;
|
if(tile == null) return false;
|
||||||
tile = tile.target();
|
|
||||||
|
|
||||||
if(tile.entity != null && tile.entity.collide(this) && type.collides(this, tile) && !tile.entity.isDead() && (type.collidesTeam || tile.getTeam() != team)){
|
if(tile.entity != null && tile.entity.collide(this) && type.collides(this, tile) && !tile.entity.isDead() && (type.collidesTeam || tile.getTeam() != team)){
|
||||||
if(tile.getTeam() != team){
|
if(tile.getTeam() != team){
|
||||||
|
|||||||
@@ -72,6 +72,11 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte version(){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float lifetime(){
|
public float lifetime(){
|
||||||
return lifetime;
|
return lifetime;
|
||||||
@@ -98,7 +103,7 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable{
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
TileEntity entity = tile.target().entity;
|
TileEntity entity = tile.link().entity;
|
||||||
boolean damage = entity != null;
|
boolean damage = entity != null;
|
||||||
|
|
||||||
float flammability = baseFlammability + puddleFlammability;
|
float flammability = baseFlammability + puddleFlammability;
|
||||||
@@ -151,7 +156,7 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readSave(DataInput stream) throws IOException{
|
public void readSave(DataInput stream, byte version) throws IOException{
|
||||||
this.loadedPosition = stream.readInt();
|
this.loadedPosition = stream.readInt();
|
||||||
this.lifetime = stream.readFloat();
|
this.lifetime = stream.readFloat();
|
||||||
this.time = stream.readFloat();
|
this.time = stream.readFloat();
|
||||||
|
|||||||
@@ -143,6 +143,11 @@ public class Puddle extends SolidEntity implements SaveTrait, Poolable, DrawTrai
|
|||||||
return liquid.flammability * amount;
|
return liquid.flammability * amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte version(){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void hitbox(Rectangle rectangle){
|
public void hitbox(Rectangle rectangle){
|
||||||
rectangle.setCenter(x, y).setSize(tilesize);
|
rectangle.setCenter(x, y).setSize(tilesize);
|
||||||
@@ -201,7 +206,7 @@ public class Puddle extends SolidEntity implements SaveTrait, Poolable, DrawTrai
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if(liquid.temperature > 0.7f && (tile.target().entity != null) && Mathf.chance(0.3 * Time.delta())){
|
if(liquid.temperature > 0.7f && (tile.link().entity != null) && Mathf.chance(0.3 * Time.delta())){
|
||||||
Fire.create(tile);
|
Fire.create(tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,7 +250,7 @@ public class Puddle extends SolidEntity implements SaveTrait, Poolable, DrawTrai
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readSave(DataInput stream) throws IOException{
|
public void readSave(DataInput stream, byte version) throws IOException{
|
||||||
this.loadedPosition = stream.readInt();
|
this.loadedPosition = stream.readInt();
|
||||||
this.x = stream.readFloat();
|
this.x = stream.readFloat();
|
||||||
this.y = stream.readFloat();
|
this.y = stream.readFloat();
|
||||||
|
|||||||
@@ -40,10 +40,98 @@ public interface BuilderTrait extends Entity, TeamTrait{
|
|||||||
float placeDistance = 220f;
|
float placeDistance = 220f;
|
||||||
float mineDistance = 70f;
|
float mineDistance = 70f;
|
||||||
|
|
||||||
//due to iOS wierdness
|
/**
|
||||||
class BuildDataStatic{
|
* Update building mechanism for this unit.
|
||||||
static Array<BuildRequest> removal = new Array<>();
|
* This includes mining.
|
||||||
static Vector2[] tmptr = new Vector2[]{new Vector2(), new Vector2(), new Vector2(), new Vector2()};
|
*/
|
||||||
|
default void updateBuilding(){
|
||||||
|
float finalPlaceDst = state.rules.infiniteResources ? Float.MAX_VALUE : placeDistance;
|
||||||
|
Unit unit = (Unit)this;
|
||||||
|
//remove already completed build requests
|
||||||
|
removal.clear();
|
||||||
|
for(BuildRequest req : getPlaceQueue()){
|
||||||
|
removal.add(req);
|
||||||
|
}
|
||||||
|
|
||||||
|
getPlaceQueue().clear();
|
||||||
|
|
||||||
|
for(BuildRequest request : removal){
|
||||||
|
if(!((request.breaking && world.tile(request.x, request.y).block() == Blocks.air) ||
|
||||||
|
(!request.breaking && (world.tile(request.x, request.y).rotation() == request.rotation || !request.block.rotate)
|
||||||
|
&& world.tile(request.x, request.y).block() == request.block))){
|
||||||
|
getPlaceQueue().addLast(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
BuildRequest current = getCurrentRequest();
|
||||||
|
|
||||||
|
//update mining here
|
||||||
|
if(current == null){
|
||||||
|
if(getMineTile() != null){
|
||||||
|
updateMining();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}else{
|
||||||
|
setMineTile(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
Tile tile = world.tile(current.x, current.y);
|
||||||
|
|
||||||
|
if(dst(tile) > finalPlaceDst){
|
||||||
|
if(getPlaceQueue().size > 1){
|
||||||
|
getPlaceQueue().removeFirst();
|
||||||
|
getPlaceQueue().addLast(current);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!(tile.block() instanceof BuildBlock)){
|
||||||
|
if(canCreateBlocks() && !current.breaking && Build.validPlace(getTeam(), current.x, current.y, current.block, current.rotation)){
|
||||||
|
Call.beginPlace(getTeam(), current.x, current.y, current.block, current.rotation);
|
||||||
|
}else if(canCreateBlocks() && current.breaking && Build.validBreak(getTeam(), current.x, current.y)){
|
||||||
|
Call.beginBreak(getTeam(), current.x, current.y);
|
||||||
|
}else{
|
||||||
|
getPlaceQueue().removeFirst();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TileEntity core = unit.getClosestCore();
|
||||||
|
|
||||||
|
//if there is no core to build with or no build entity, stop building!
|
||||||
|
if((core == null && !state.rules.infiniteResources) || !(tile.entity instanceof BuildEntity)){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//otherwise, update it.
|
||||||
|
BuildEntity entity = tile.entity();
|
||||||
|
|
||||||
|
if(entity == null){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(unit.dst(tile) <= finalPlaceDst){
|
||||||
|
unit.rotation = Mathf.slerpDelta(unit.rotation, unit.angleTo(entity), 0.4f);
|
||||||
|
}
|
||||||
|
|
||||||
|
//progress is synced, thus not updated clientside
|
||||||
|
if(!Net.client()){
|
||||||
|
//deconstructing is 2x as fast
|
||||||
|
if(current.breaking){
|
||||||
|
entity.deconstruct(unit, core, 2f / entity.buildCost * Time.delta() * getBuildPower(tile) * state.rules.buildSpeedMultiplier);
|
||||||
|
}else{
|
||||||
|
entity.construct(unit, core, 1f / entity.buildCost * Time.delta() * getBuildPower(tile) * state.rules.buildSpeedMultiplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
current.progress = entity.progress();
|
||||||
|
}else{
|
||||||
|
entity.progress = current.progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!current.initialized){
|
||||||
|
Core.app.post(() -> Events.fire(new BuildSelectEvent(tile, unit.getTeam(), this, current.breaking)));
|
||||||
|
current.initialized = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Returns the queue for storing build requests. */
|
/** Returns the queue for storing build requests. */
|
||||||
@@ -148,97 +236,10 @@ public interface BuilderTrait extends Entity, TeamTrait{
|
|||||||
return getPlaceQueue().size == 0 ? null : getPlaceQueue().first();
|
return getPlaceQueue().size == 0 ? null : getPlaceQueue().first();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
//due to iOS wierdness, this is apparently required
|
||||||
* Update building mechanism for this unit.
|
class BuildDataStatic{
|
||||||
* This includes mining.
|
static Array<BuildRequest> removal = new Array<>();
|
||||||
*/
|
static Vector2[] tmptr = new Vector2[]{new Vector2(), new Vector2(), new Vector2(), new Vector2()};
|
||||||
default void updateBuilding(){
|
|
||||||
Unit unit = (Unit)this;
|
|
||||||
//remove already completed build requests
|
|
||||||
removal.clear();
|
|
||||||
for(BuildRequest req : getPlaceQueue()){
|
|
||||||
removal.add(req);
|
|
||||||
}
|
|
||||||
|
|
||||||
getPlaceQueue().clear();
|
|
||||||
|
|
||||||
for(BuildRequest request : removal){
|
|
||||||
if(!((request.breaking && world.tile(request.x, request.y).block() == Blocks.air) ||
|
|
||||||
(!request.breaking && (world.tile(request.x, request.y).getRotation() == request.rotation || !request.block.rotate)
|
|
||||||
&& world.tile(request.x, request.y).block() == request.block))){
|
|
||||||
getPlaceQueue().addLast(request);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BuildRequest current = getCurrentRequest();
|
|
||||||
|
|
||||||
//update mining here
|
|
||||||
if(current == null){
|
|
||||||
if(getMineTile() != null){
|
|
||||||
updateMining();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}else{
|
|
||||||
setMineTile(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
Tile tile = world.tile(current.x, current.y);
|
|
||||||
|
|
||||||
if(dst(tile) > placeDistance){
|
|
||||||
if(getPlaceQueue().size > 1){
|
|
||||||
getPlaceQueue().removeFirst();
|
|
||||||
getPlaceQueue().addLast(current);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!(tile.block() instanceof BuildBlock)){
|
|
||||||
if(canCreateBlocks() && !current.breaking && Build.validPlace(getTeam(), current.x, current.y, current.block, current.rotation)){
|
|
||||||
Call.beginPlace(getTeam(), current.x, current.y, current.block, current.rotation);
|
|
||||||
}else if(canCreateBlocks() && current.breaking && Build.validBreak(getTeam(), current.x, current.y)){
|
|
||||||
Call.beginBreak(getTeam(), current.x, current.y);
|
|
||||||
}else{
|
|
||||||
getPlaceQueue().removeFirst();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TileEntity core = unit.getClosestCore();
|
|
||||||
|
|
||||||
//if there is no core to build with or no build entity, stop building!
|
|
||||||
if(core == null || !(tile.entity instanceof BuildEntity)){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//otherwise, update it.
|
|
||||||
BuildEntity entity = tile.entity();
|
|
||||||
|
|
||||||
if(entity == null){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(unit.dst(tile) <= placeDistance){
|
|
||||||
unit.rotation = Mathf.slerpDelta(unit.rotation, unit.angleTo(entity), 0.4f);
|
|
||||||
}
|
|
||||||
|
|
||||||
//progress is synced, thus not updated clientside
|
|
||||||
if(!Net.client()){
|
|
||||||
//deconstructing is 2x as fast
|
|
||||||
if(current.breaking){
|
|
||||||
entity.deconstruct(unit, core, 2f / entity.buildCost * Time.delta() * getBuildPower(tile) * state.rules.buildSpeedMultiplier);
|
|
||||||
}else{
|
|
||||||
entity.construct(unit, core, 1f / entity.buildCost * Time.delta() * getBuildPower(tile) * state.rules.buildSpeedMultiplier);
|
|
||||||
}
|
|
||||||
|
|
||||||
current.progress = entity.progress();
|
|
||||||
}else{
|
|
||||||
entity.progress = current.progress;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!current.initialized){
|
|
||||||
Core.app.post(() -> Events.fire(new BuildSelectEvent(tile, unit.getTeam(), this, current.breaking)));
|
|
||||||
current.initialized = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Do not call directly. */
|
/** Do not call directly. */
|
||||||
@@ -291,7 +292,7 @@ public interface BuilderTrait extends Entity, TeamTrait{
|
|||||||
|
|
||||||
Tile tile = world.tile(request.x, request.y);
|
Tile tile = world.tile(request.x, request.y);
|
||||||
|
|
||||||
if(dst(tile) > placeDistance){
|
if(dst(tile) > placeDistance && !state.isEditor()){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,4 +4,5 @@ package io.anuke.mindustry.entities.traits;
|
|||||||
* Marks an entity as serializable.
|
* Marks an entity as serializable.
|
||||||
*/
|
*/
|
||||||
public interface SaveTrait extends Entity, TypeTrait, Saveable{
|
public interface SaveTrait extends Entity, TypeTrait, Saveable{
|
||||||
|
byte version();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,5 @@ import java.io.*;
|
|||||||
|
|
||||||
public interface Saveable{
|
public interface Saveable{
|
||||||
void writeSave(DataOutput stream) throws IOException;
|
void writeSave(DataOutput stream) throws IOException;
|
||||||
|
void readSave(DataInput stream, byte version) throws IOException;
|
||||||
void readSave(DataInput stream) throws IOException;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,15 +41,6 @@ public interface SyncTrait extends Entity, TypeTrait{
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Whether this entity is clipped and not synced when out of viewport. */
|
|
||||||
default boolean isClipped(){
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
default float clipSize(){
|
|
||||||
return (this instanceof DrawTrait ? ((DrawTrait)this).drawSize() : 8f);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Read and write sync data, usually position
|
//Read and write sync data, usually position
|
||||||
void write(DataOutput data) throws IOException;
|
void write(DataOutput data) throws IOException;
|
||||||
|
|
||||||
|
|||||||
@@ -23,9 +23,7 @@ public interface TypeTrait{
|
|||||||
lastRegisteredID[0]++;
|
lastRegisteredID[0]++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**Gets a syncable type by ID.*/
|
||||||
* Registers a syncable type by ID.
|
|
||||||
*/
|
|
||||||
static Supplier<? extends TypeTrait> getTypeByID(int id){
|
static Supplier<? extends TypeTrait> getTypeByID(int id){
|
||||||
if(id == -1){
|
if(id == -1){
|
||||||
throw new IllegalArgumentException("Attempt to retrieve invalid entity type ID! Did you forget to set it in ContentLoader.registerTypes()?");
|
throw new IllegalArgumentException("Attempt to retrieve invalid entity type ID! Did you forget to set it in ContentLoader.registerTypes()?");
|
||||||
|
|||||||
@@ -306,11 +306,6 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
|
|||||||
return type.hitsize * 10;
|
return type.hitsize * 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public float clipSize(){
|
|
||||||
return isBoss() ? 10000000000f : super.clipSize();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDeath(){
|
public void onDeath(){
|
||||||
Call.onUnitDeath(this);
|
Call.onUnitDeath(this);
|
||||||
@@ -338,6 +333,11 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
|
|||||||
return unitGroups[team.ordinal()];
|
return unitGroups[team.ordinal()];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte version(){
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeSave(DataOutput stream) throws IOException{
|
public void writeSave(DataOutput stream) throws IOException{
|
||||||
super.writeSave(stream);
|
super.writeSave(stream);
|
||||||
@@ -346,8 +346,8 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readSave(DataInput stream) throws IOException{
|
public void readSave(DataInput stream, byte version) throws IOException{
|
||||||
super.readSave(stream);
|
super.readSave(stream, version);
|
||||||
byte type = stream.readByte();
|
byte type = stream.readByte();
|
||||||
this.spawner = stream.readInt();
|
this.spawner = stream.readInt();
|
||||||
|
|
||||||
@@ -365,7 +365,9 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
|
|||||||
@Override
|
@Override
|
||||||
public void read(DataInput data) throws IOException{
|
public void read(DataInput data) throws IOException{
|
||||||
float lastx = x, lasty = y, lastrot = rotation;
|
float lastx = x, lasty = y, lastrot = rotation;
|
||||||
super.readSave(data);
|
|
||||||
|
super.readSave(data, version());
|
||||||
|
|
||||||
this.type = content.getByID(ContentType.unit, data.readByte());
|
this.type = content.getByID(ContentType.unit, data.readByte());
|
||||||
this.spawner = data.readInt();
|
this.spawner = data.readInt();
|
||||||
|
|
||||||
|
|||||||
@@ -432,7 +432,7 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
|
|||||||
if(getCurrentRequest() == request && request.progress > 0.001f) continue;
|
if(getCurrentRequest() == request && request.progress > 0.001f) continue;
|
||||||
|
|
||||||
if(request.breaking){
|
if(request.breaking){
|
||||||
Block block = world.tile(request.x, request.y).target().block();
|
Block block = world.ltile(request.x, request.y).block();
|
||||||
|
|
||||||
//draw removal request
|
//draw removal request
|
||||||
Lines.stroke(2f, Pal.removeBack);
|
Lines.stroke(2f, Pal.removeBack);
|
||||||
@@ -636,13 +636,17 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void updateShooting(){
|
protected void updateShooting(){
|
||||||
if(isShooting() && mech.canShoot(this)){
|
if(!state.isEditor() && isShooting() && mech.canShoot(this)){
|
||||||
mech.weapon.update(this, pointerX, pointerY);
|
mech.weapon.update(this, pointerX, pointerY);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void updateFlying(){
|
protected void updateFlying(){
|
||||||
if(Units.invalidateTarget(target, this) && !(target instanceof TileEntity && ((TileEntity)target).damaged() && target.isValid() && ((TileEntity)target).isAdded() && target.getTeam() == team && mech.canHeal && dst(target) < getWeapon().bullet.range())){
|
if(Units.invalidateTarget(target, this) && !(target instanceof TileEntity && ((TileEntity)target).damaged() && target.isValid() && target.getTeam() == team && mech.canHeal && dst(target) < getWeapon().bullet.range())){
|
||||||
|
target = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(state.isEditor()){
|
||||||
target = null;
|
target = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -790,7 +794,11 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
|
|||||||
|
|
||||||
public void updateRespawning(){
|
public void updateRespawning(){
|
||||||
|
|
||||||
if(spawner != null && spawner.isValid()){
|
if(state.isEditor()){
|
||||||
|
//instant respawn at center of map.
|
||||||
|
set(world.width() * tilesize/2f, world.height() * tilesize/2f);
|
||||||
|
setDead(false);
|
||||||
|
}else if(spawner != null && spawner.isValid()){
|
||||||
spawner.updateSpawning(this);
|
spawner.updateSpawning(this);
|
||||||
}else if(!netServer.isWaitingForPlayers()){
|
}else if(!netServer.isWaitingForPlayers()){
|
||||||
if(!Net.client()){
|
if(!Net.client()){
|
||||||
@@ -817,8 +825,8 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
|
|||||||
//region read and write methods
|
//region read and write methods
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isClipped(){
|
public byte version(){
|
||||||
return false;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -833,7 +841,7 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readSave(DataInput stream) throws IOException{
|
public void readSave(DataInput stream, byte version) throws IOException{
|
||||||
boolean local = stream.readBoolean();
|
boolean local = stream.readBoolean();
|
||||||
|
|
||||||
if(local){
|
if(local){
|
||||||
@@ -844,14 +852,14 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
|
|||||||
lastSpawner = (SpawnerTrait)stile.entity;
|
lastSpawner = (SpawnerTrait)stile.entity;
|
||||||
}
|
}
|
||||||
Player player = headless ? this : Vars.player;
|
Player player = headless ? this : Vars.player;
|
||||||
player.readSaveSuper(stream);
|
player.readSaveSuper(stream, version);
|
||||||
player.mech = content.getByID(ContentType.mech, mechid);
|
player.mech = content.getByID(ContentType.mech, mechid);
|
||||||
player.dead = false;
|
player.dead = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readSaveSuper(DataInput stream) throws IOException{
|
private void readSaveSuper(DataInput stream, byte version) throws IOException{
|
||||||
super.readSave(stream);
|
super.readSave(stream, version);
|
||||||
|
|
||||||
add();
|
add();
|
||||||
}
|
}
|
||||||
@@ -859,7 +867,7 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
|
|||||||
@Override
|
@Override
|
||||||
public void write(DataOutput buffer) throws IOException{
|
public void write(DataOutput buffer) throws IOException{
|
||||||
super.writeSave(buffer, !isLocal);
|
super.writeSave(buffer, !isLocal);
|
||||||
TypeIO.writeStringData(buffer, name); //TODO writing strings is very inefficient
|
TypeIO.writeStringData(buffer, name);
|
||||||
buffer.writeByte(Pack.byteValue(isAdmin) | (Pack.byteValue(dead) << 1) | (Pack.byteValue(isBoosting) << 2) | (Pack.byteValue(isTyping) << 3));
|
buffer.writeByte(Pack.byteValue(isAdmin) | (Pack.byteValue(dead) << 1) | (Pack.byteValue(isBoosting) << 2) | (Pack.byteValue(isTyping) << 3));
|
||||||
buffer.writeInt(Color.rgba8888(color));
|
buffer.writeInt(Color.rgba8888(color));
|
||||||
buffer.writeByte(mech.id);
|
buffer.writeByte(mech.id);
|
||||||
@@ -873,7 +881,9 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
|
|||||||
@Override
|
@Override
|
||||||
public void read(DataInput buffer) throws IOException{
|
public void read(DataInput buffer) throws IOException{
|
||||||
float lastx = x, lasty = y, lastrot = rotation, lastvx = velocity.x, lastvy = velocity.y;
|
float lastx = x, lasty = y, lastrot = rotation, lastvx = velocity.x, lastvy = velocity.y;
|
||||||
super.readSave(buffer);
|
|
||||||
|
super.readSave(buffer, version());
|
||||||
|
|
||||||
name = TypeIO.readStringData(buffer);
|
name = TypeIO.readStringData(buffer);
|
||||||
byte bools = buffer.readByte();
|
byte bools = buffer.readByte();
|
||||||
isAdmin = (bools & 1) != 0;
|
isAdmin = (bools & 1) != 0;
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
package io.anuke.mindustry.entities.type;
|
package io.anuke.mindustry.entities.type;
|
||||||
|
|
||||||
import io.anuke.annotations.Annotations.Loc;
|
import io.anuke.annotations.Annotations.*;
|
||||||
import io.anuke.annotations.Annotations.Remote;
|
|
||||||
import io.anuke.arc.Events;
|
import io.anuke.arc.Events;
|
||||||
import io.anuke.arc.collection.Array;
|
import io.anuke.arc.collection.Array;
|
||||||
import io.anuke.arc.collection.ObjectSet;
|
import io.anuke.arc.collection.ObjectSet;
|
||||||
import io.anuke.arc.math.geom.Point2;
|
import io.anuke.arc.math.geom.Point2;
|
||||||
import io.anuke.arc.math.geom.Vector2;
|
import io.anuke.arc.math.geom.Vector2;
|
||||||
import io.anuke.arc.util.Interval;
|
import io.anuke.arc.util.*;
|
||||||
import io.anuke.arc.util.Time;
|
|
||||||
import io.anuke.mindustry.entities.EntityGroup;
|
import io.anuke.mindustry.entities.EntityGroup;
|
||||||
import io.anuke.mindustry.entities.bullet.Bullet;
|
import io.anuke.mindustry.entities.bullet.Bullet;
|
||||||
import io.anuke.mindustry.entities.impl.BaseEntity;
|
import io.anuke.mindustry.entities.impl.BaseEntity;
|
||||||
@@ -115,16 +113,35 @@ public class TileEntity extends BaseEntity implements TargetTrait, HealthTrait{
|
|||||||
return dead || tile.entity != this;
|
return dead || tile.entity != this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@CallSuper
|
||||||
public void write(DataOutput stream) throws IOException{
|
public void write(DataOutput stream) throws IOException{
|
||||||
|
stream.writeShort((short)health);
|
||||||
|
stream.writeByte(Pack.byteByte(tile.getTeamID(), tile.rotation())); //team + rotation
|
||||||
|
if(items != null) items.write(stream);
|
||||||
|
if(power != null) power.write(stream);
|
||||||
|
if(liquids != null) liquids.write(stream);
|
||||||
|
if(cons != null) cons.write(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeConfig(DataOutput stream) throws IOException{
|
@CallSuper
|
||||||
|
public void read(DataInput stream, byte revision) throws IOException{
|
||||||
|
health = stream.readUnsignedShort();
|
||||||
|
byte tr = stream.readByte();
|
||||||
|
byte team = Pack.leftByte(tr);
|
||||||
|
byte rotation = Pack.rightByte(tr);
|
||||||
|
|
||||||
|
tile.setTeam(Team.all[team]);
|
||||||
|
tile.rotation(rotation);
|
||||||
|
|
||||||
|
if(items != null) items.read(stream);
|
||||||
|
if(power != null) power.read(stream);
|
||||||
|
if(liquids != null) liquids.read(stream);
|
||||||
|
if(cons != null) cons.read(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void read(DataInput stream) throws IOException{
|
/** Returns the version of this TileEntity IO code.*/
|
||||||
}
|
public byte version(){
|
||||||
|
return 0;
|
||||||
public void readConfig(DataInput stream) throws IOException{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean collide(Bullet other){
|
public boolean collide(Bullet other){
|
||||||
@@ -168,14 +185,14 @@ public class TileEntity extends BaseEntity implements TargetTrait, HealthTrait{
|
|||||||
|
|
||||||
Point2[] nearby = Edges.getEdges(block.size);
|
Point2[] nearby = Edges.getEdges(block.size);
|
||||||
for(Point2 point : nearby){
|
for(Point2 point : nearby){
|
||||||
Tile other = world.tile(tile.x + point.x, tile.y + point.y);
|
Tile other = world.ltile(tile.x + point.x, tile.y + point.y);
|
||||||
//remove this tile from all nearby tile's proximities
|
//remove this tile from all nearby tile's proximities
|
||||||
if(other != null){
|
if(other != null){
|
||||||
other = other.target();
|
|
||||||
other.block().onProximityUpdate(other);
|
other.block().onProximityUpdate(other);
|
||||||
}
|
|
||||||
if(other != null && other.entity != null){
|
if(other.entity != null){
|
||||||
other.entity.proximity.removeValue(tile, true);
|
other.entity.proximity.removeValue(tile, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -186,10 +203,9 @@ public class TileEntity extends BaseEntity implements TargetTrait, HealthTrait{
|
|||||||
|
|
||||||
Point2[] nearby = Edges.getEdges(block.size);
|
Point2[] nearby = Edges.getEdges(block.size);
|
||||||
for(Point2 point : nearby){
|
for(Point2 point : nearby){
|
||||||
Tile other = world.tile(tile.x + point.x, tile.y + point.y);
|
Tile other = world.ltile(tile.x + point.x, tile.y + point.y);
|
||||||
|
|
||||||
if(other == null) continue;
|
if(other == null) continue;
|
||||||
other = other.target();
|
|
||||||
if(other.entity == null || !(other.interactable(tile.getTeam()))) continue;
|
if(other.entity == null || !(other.interactable(tile.getTeam()))) continue;
|
||||||
|
|
||||||
other.block().onProximityUpdate(other);
|
other.block().onProximityUpdate(other);
|
||||||
@@ -280,6 +296,11 @@ public class TileEntity extends BaseEntity implements TargetTrait, HealthTrait{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isValid(){
|
||||||
|
return !isDead() && tile.entity == this;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public EntityGroup targetGroup(){
|
public EntityGroup targetGroup(){
|
||||||
return tileGroup;
|
return tileGroup;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package io.anuke.mindustry.entities.type;
|
package io.anuke.mindustry.entities.type;
|
||||||
|
|
||||||
|
import io.anuke.annotations.Annotations.Nullable;
|
||||||
import io.anuke.arc.Core;
|
import io.anuke.arc.Core;
|
||||||
import io.anuke.arc.Events;
|
import io.anuke.arc.Events;
|
||||||
import io.anuke.arc.graphics.Color;
|
import io.anuke.arc.graphics.Color;
|
||||||
@@ -8,7 +9,8 @@ import io.anuke.arc.graphics.g2d.TextureRegion;
|
|||||||
import io.anuke.arc.math.Mathf;
|
import io.anuke.arc.math.Mathf;
|
||||||
import io.anuke.arc.math.geom.Geometry;
|
import io.anuke.arc.math.geom.Geometry;
|
||||||
import io.anuke.arc.math.geom.Vector2;
|
import io.anuke.arc.math.geom.Vector2;
|
||||||
import io.anuke.arc.util.*;
|
import io.anuke.arc.util.Time;
|
||||||
|
import io.anuke.arc.util.Tmp;
|
||||||
import io.anuke.mindustry.content.Blocks;
|
import io.anuke.mindustry.content.Blocks;
|
||||||
import io.anuke.mindustry.content.Fx;
|
import io.anuke.mindustry.content.Fx;
|
||||||
import io.anuke.mindustry.entities.*;
|
import io.anuke.mindustry.entities.*;
|
||||||
@@ -138,7 +140,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readSave(DataInput stream) throws IOException{
|
public void readSave(DataInput stream, byte version) throws IOException{
|
||||||
byte team = stream.readByte();
|
byte team = stream.readByte();
|
||||||
boolean dead = stream.readBoolean();
|
boolean dead = stream.readBoolean();
|
||||||
float x = stream.readFloat();
|
float x = stream.readFloat();
|
||||||
@@ -150,7 +152,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
|||||||
byte itemID = stream.readByte();
|
byte itemID = stream.readByte();
|
||||||
short itemAmount = stream.readShort();
|
short itemAmount = stream.readShort();
|
||||||
|
|
||||||
this.status.readSave(stream);
|
this.status.readSave(stream, version);
|
||||||
this.item.amount = itemAmount;
|
this.item.amount = itemAmount;
|
||||||
this.item.item = content.item(itemID);
|
this.item.item = content.item(itemID);
|
||||||
this.dead = dead;
|
this.dead = dead;
|
||||||
@@ -221,7 +223,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
|
|||||||
velocity.add(moveVector.x / mass() * Time.delta(), moveVector.y / mass() * Time.delta());
|
velocity.add(moveVector.x / mass() * Time.delta(), moveVector.y / mass() * Time.delta());
|
||||||
}
|
}
|
||||||
|
|
||||||
public TileEntity getClosestCore(){
|
public @Nullable TileEntity getClosestCore(){
|
||||||
TeamData data = state.teams.get(team);
|
TeamData data = state.teams.get(team);
|
||||||
|
|
||||||
Tile tile = Geometry.findClosest(x, y, data.cores);
|
Tile tile = Geometry.findClosest(x, y, data.cores);
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ public class Drone extends FlyingUnit implements BuilderTrait{
|
|||||||
if(isBreaking){
|
if(isBreaking){
|
||||||
getPlaceQueue().addLast(new BuildRequest(entity.tile.x, entity.tile.y));
|
getPlaceQueue().addLast(new BuildRequest(entity.tile.x, entity.tile.y));
|
||||||
}else{
|
}else{
|
||||||
getPlaceQueue().addLast(new BuildRequest(entity.tile.x, entity.tile.y, entity.tile.getRotation(), entity.cblock));
|
getPlaceQueue().addLast(new BuildRequest(entity.tile.x, entity.tile.y, entity.tile.rotation(), entity.cblock));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ public class Statuses implements Saveable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void readSave(DataInput stream) throws IOException{
|
public void readSave(DataInput stream, byte version) throws IOException{
|
||||||
for(StatusEntry effect : statuses){
|
for(StatusEntry effect : statuses){
|
||||||
Pools.free(effect);
|
Pools.free(effect);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,10 +6,10 @@ import io.anuke.mindustry.type.ContentType;
|
|||||||
|
|
||||||
/** Base class for a content type that is loaded in {@link io.anuke.mindustry.core.ContentLoader}. */
|
/** Base class for a content type that is loaded in {@link io.anuke.mindustry.core.ContentLoader}. */
|
||||||
public abstract class Content{
|
public abstract class Content{
|
||||||
public final byte id;
|
public final short id;
|
||||||
|
|
||||||
public Content(){
|
public Content(){
|
||||||
this.id = (byte)Vars.content.getBy(getContentType()).size;
|
this.id = (short)Vars.content.getBy(getContentType()).size;
|
||||||
Vars.content.handleContent(this);
|
Vars.content.handleContent(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,49 +1,64 @@
|
|||||||
package io.anuke.mindustry.game;
|
package io.anuke.mindustry.game;
|
||||||
|
|
||||||
import io.anuke.arc.Core;
|
import io.anuke.arc.Core;
|
||||||
import io.anuke.arc.function.Supplier;
|
import io.anuke.arc.function.Consumer;
|
||||||
|
|
||||||
/** Defines preset rule sets.. */
|
/** Defines preset rule sets.. */
|
||||||
public enum Gamemode{
|
public enum Gamemode{
|
||||||
survival(() -> new Rules(){{
|
survival(rules -> {
|
||||||
waveTimer = true;
|
rules.waveTimer = true;
|
||||||
waves = true;
|
rules.waves = true;
|
||||||
unitDrops = true;
|
rules.unitDrops = true;
|
||||||
spawns = DefaultWaves.get();
|
}),
|
||||||
}}),
|
sandbox(rules -> {
|
||||||
sandbox(() -> new Rules(){{
|
rules.infiniteResources = true;
|
||||||
infiniteResources = true;
|
rules.waves = true;
|
||||||
waves = true;
|
rules.waveTimer = false;
|
||||||
waveTimer = false;
|
rules.respawnTime = 0f;
|
||||||
respawnTime = 0f;
|
}),
|
||||||
}}),
|
attack(rules -> {
|
||||||
attack(() -> new Rules(){{
|
rules.enemyCheat = true;
|
||||||
enemyCheat = true;
|
rules.unitDrops = true;
|
||||||
unitDrops = true;
|
rules.waves = false;
|
||||||
waves = false;
|
rules.attackMode = true;
|
||||||
attackMode = true;
|
}),
|
||||||
}}),
|
pvp(rules -> {
|
||||||
pvp(() -> new Rules(){{
|
rules.pvp = true;
|
||||||
pvp = true;
|
rules.enemyCoreBuildRadius = 600f;
|
||||||
enemyCoreBuildRadius = 600f;
|
rules.respawnTime = 60 * 10;
|
||||||
respawnTime = 60 * 10;
|
rules.buildCostMultiplier = 0.5f;
|
||||||
buildCostMultiplier = 0.5f;
|
rules.buildSpeedMultiplier = 2f;
|
||||||
buildSpeedMultiplier = 2f;
|
rules.playerDamageMultiplier = 0.45f;
|
||||||
playerDamageMultiplier = 0.45f;
|
rules.playerHealthMultiplier = 0.8f;
|
||||||
playerHealthMultiplier = 0.8f;
|
rules.unitBuildSpeedMultiplier = 3f;
|
||||||
unitBuildSpeedMultiplier = 3f;
|
rules.unitHealthMultiplier = 2f;
|
||||||
unitHealthMultiplier = 2f;
|
rules.attackMode = true;
|
||||||
attackMode = true;
|
}),
|
||||||
}});
|
editor(true, rules -> {
|
||||||
|
rules.infiniteResources = true;
|
||||||
|
rules.editor = true;
|
||||||
|
rules.waves = false;
|
||||||
|
rules.enemyCoreBuildRadius = 0f;
|
||||||
|
rules.waveTimer = false;
|
||||||
|
rules.respawnTime = 0f;
|
||||||
|
});
|
||||||
|
|
||||||
private final Supplier<Rules> rules;
|
private final Consumer<Rules> rules;
|
||||||
|
public final boolean hidden;
|
||||||
|
|
||||||
Gamemode(Supplier<Rules> rules){
|
Gamemode(Consumer<Rules> rules){
|
||||||
this.rules = rules;
|
this(false, rules);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Rules get(){
|
Gamemode(boolean hidden, Consumer<Rules> rules){
|
||||||
return rules.get();
|
this.rules = rules;
|
||||||
|
this.hidden = hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Applies this preset to this ruleset. */
|
||||||
|
public Rules apply(Rules in){
|
||||||
|
rules.accept(in);
|
||||||
|
return in;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String description(){
|
public String description(){
|
||||||
|
|||||||
@@ -2,6 +2,8 @@ package io.anuke.mindustry.game;
|
|||||||
|
|
||||||
import io.anuke.annotations.Annotations.Serialize;
|
import io.anuke.annotations.Annotations.Serialize;
|
||||||
import io.anuke.arc.collection.Array;
|
import io.anuke.arc.collection.Array;
|
||||||
|
import io.anuke.mindustry.io.JsonIO;
|
||||||
|
import io.anuke.mindustry.type.Zone;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines current rules on how the game should function.
|
* Defines current rules on how the game should function.
|
||||||
@@ -47,10 +49,10 @@ public class Rules{
|
|||||||
public float bossWaveMultiplier = 3f;
|
public float bossWaveMultiplier = 3f;
|
||||||
/** How many times longer a launch wave takes. */
|
/** How many times longer a launch wave takes. */
|
||||||
public float launchWaveMultiplier = 2f;
|
public float launchWaveMultiplier = 2f;
|
||||||
/** Zone ID, -1 for invalid zone. */
|
/** Zone for saves that have them.*/
|
||||||
public byte zone = -1;
|
public Zone zone;
|
||||||
/** Spawn layout. Should be assigned on save load based on map or zone. */
|
/** Spawn layout. Should be assigned on save load based on map or zone. */
|
||||||
public transient Array<SpawnGroup> spawns = DefaultWaves.get();
|
public Array<SpawnGroup> spawns = DefaultWaves.get();
|
||||||
/** Determines if there should be limited respawns. */
|
/** Determines if there should be limited respawns. */
|
||||||
public boolean limitedRespawns = false;
|
public boolean limitedRespawns = false;
|
||||||
/** How many times player can respawn during one wave. */
|
/** How many times player can respawn during one wave. */
|
||||||
@@ -59,4 +61,11 @@ public class Rules{
|
|||||||
public boolean waitForWaveToEnd = false;
|
public boolean waitForWaveToEnd = false;
|
||||||
/** Determinates if gamemode is attack mode */
|
/** Determinates if gamemode is attack mode */
|
||||||
public boolean attackMode = false;
|
public boolean attackMode = false;
|
||||||
|
/** Whether this is the editor gamemode. */
|
||||||
|
public boolean editor = false;
|
||||||
|
|
||||||
|
/** Copies this ruleset exactly. Not very efficient at all, do not use often. */
|
||||||
|
public Rules copy(){
|
||||||
|
return JsonIO.read(Rules.class, JsonIO.write(this));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,22 +4,21 @@ import io.anuke.arc.Core;
|
|||||||
import io.anuke.arc.Events;
|
import io.anuke.arc.Events;
|
||||||
import io.anuke.arc.collection.*;
|
import io.anuke.arc.collection.*;
|
||||||
import io.anuke.arc.files.FileHandle;
|
import io.anuke.arc.files.FileHandle;
|
||||||
import io.anuke.arc.util.Strings;
|
import io.anuke.arc.util.*;
|
||||||
import io.anuke.arc.util.Time;
|
|
||||||
import io.anuke.mindustry.core.GameState.State;
|
import io.anuke.mindustry.core.GameState.State;
|
||||||
import io.anuke.mindustry.game.EventType.StateChangeEvent;
|
import io.anuke.mindustry.game.EventType.StateChangeEvent;
|
||||||
import io.anuke.mindustry.io.SaveIO;
|
import io.anuke.mindustry.io.SaveIO;
|
||||||
import io.anuke.mindustry.io.SaveIO.SaveException;
|
import io.anuke.mindustry.io.SaveIO.SaveException;
|
||||||
import io.anuke.mindustry.io.SaveMeta;
|
import io.anuke.mindustry.io.SaveMeta;
|
||||||
import io.anuke.mindustry.maps.Map;
|
import io.anuke.mindustry.maps.Map;
|
||||||
import io.anuke.mindustry.type.ContentType;
|
|
||||||
import io.anuke.mindustry.type.Zone;
|
import io.anuke.mindustry.type.Zone;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.*;
|
import static io.anuke.mindustry.Vars.saveExtension;
|
||||||
|
import static io.anuke.mindustry.Vars.state;
|
||||||
|
|
||||||
public class Saves{
|
public class Saves{
|
||||||
private int nextSlot;
|
private int nextSlot;
|
||||||
@@ -52,7 +51,7 @@ public class Saves{
|
|||||||
SaveSlot slot = new SaveSlot(index);
|
SaveSlot slot = new SaveSlot(index);
|
||||||
saves.add(slot);
|
saves.add(slot);
|
||||||
saveMap.put(slot.index, slot);
|
saveMap.put(slot.index, slot);
|
||||||
slot.meta = SaveIO.getData(index);
|
slot.meta = SaveIO.getMeta(index);
|
||||||
nextSlot = Math.max(index + 1, nextSlot);
|
nextSlot = Math.max(index + 1, nextSlot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,7 +133,7 @@ public class Saves{
|
|||||||
slot.setName(file.nameWithoutExtension());
|
slot.setName(file.nameWithoutExtension());
|
||||||
saves.add(slot);
|
saves.add(slot);
|
||||||
saveMap.put(slot.index, slot);
|
saveMap.put(slot.index, slot);
|
||||||
slot.meta = SaveIO.getData(slot.index);
|
slot.meta = SaveIO.getMeta(slot.index);
|
||||||
current = slot;
|
current = slot;
|
||||||
saveSlots();
|
saveSlots();
|
||||||
return slot;
|
return slot;
|
||||||
@@ -172,7 +171,7 @@ public class Saves{
|
|||||||
public void load() throws SaveException{
|
public void load() throws SaveException{
|
||||||
try{
|
try{
|
||||||
SaveIO.loadFromSlot(index);
|
SaveIO.loadFromSlot(index);
|
||||||
meta = SaveIO.getData(index);
|
meta = SaveIO.getMeta(index);
|
||||||
current = this;
|
current = this;
|
||||||
totalPlaytime = meta.timePlayed;
|
totalPlaytime = meta.timePlayed;
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
@@ -186,7 +185,7 @@ public class Saves{
|
|||||||
totalPlaytime = time;
|
totalPlaytime = time;
|
||||||
|
|
||||||
SaveIO.saveToSlot(index);
|
SaveIO.saveToSlot(index);
|
||||||
meta = SaveIO.getData(index);
|
meta = SaveIO.getMeta(index);
|
||||||
if(!state.is(State.menu)){
|
if(!state.is(State.menu)){
|
||||||
current = this;
|
current = this;
|
||||||
}
|
}
|
||||||
@@ -224,7 +223,7 @@ public class Saves{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Zone getZone(){
|
public Zone getZone(){
|
||||||
return meta == null || meta.rules == null ? null : content.getByID(ContentType.zone, meta.rules.zone);
|
return meta == null || meta.rules == null ? null : meta.rules.zone;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getBuild(){
|
public int getBuild(){
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import io.anuke.mindustry.type.*;
|
|||||||
@Serialize
|
@Serialize
|
||||||
public class Stats{
|
public class Stats{
|
||||||
/** Items delivered to global resoure counter. Zones only. */
|
/** Items delivered to global resoure counter. Zones only. */
|
||||||
public ObjectIntMap<Item> itemsDelivered = new ObjectIntMap<>();
|
public transient ObjectIntMap<Item> itemsDelivered = new ObjectIntMap<>();
|
||||||
/** Enemy (red team) units destroyed. */
|
/** Enemy (red team) units destroyed. */
|
||||||
public int enemyUnitsDestroyed;
|
public int enemyUnitsDestroyed;
|
||||||
/** Total waves lasted. */
|
/** Total waves lasted. */
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ public class BlockRenderer implements Disposable{
|
|||||||
for(int y = 0; y < world.height(); y++){
|
for(int y = 0; y < world.height(); y++){
|
||||||
Tile tile = world.rawTile(x, y);
|
Tile tile = world.rawTile(x, y);
|
||||||
int edgeBlend = 2;
|
int edgeBlend = 2;
|
||||||
float rot = tile.getRotation();
|
float rot = tile.rotation();
|
||||||
boolean fillable = (tile.block().solid && tile.block().fillsTile && !tile.block().synthetic());
|
boolean fillable = (tile.block().solid && tile.block().fillsTile && !tile.block().synthetic());
|
||||||
int edgeDst = Math.min(x, Math.min(y, Math.min(Math.abs(x - (world.width() - 1)), Math.abs(y - (world.height() - 1)))));
|
int edgeDst = Math.min(x, Math.min(y, Math.min(Math.abs(x - (world.width() - 1)), Math.abs(y - (world.height() - 1)))));
|
||||||
if(edgeDst <= edgeBlend){
|
if(edgeDst <= edgeBlend){
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ public class FloorRenderer implements Disposable{
|
|||||||
|
|
||||||
//loop through all layers, and add layer index if it exists
|
//loop through all layers, and add layer index if it exists
|
||||||
for(int i = 0; i < layers; i++){
|
for(int i = 0; i < layers; i++){
|
||||||
if(chunk.caches[i] != -1){
|
if(chunk.caches[i] != -1 && i != CacheLayer.walls.ordinal()){
|
||||||
drawnLayerSet.add(i);
|
drawnLayerSet.add(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ public class MinimapRenderer implements Disposable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private int colorFor(Tile tile){
|
private int colorFor(Tile tile){
|
||||||
tile = tile.target();
|
tile = tile.link();
|
||||||
return MapIO.colorFor(tile.floor(), tile.block(), tile.overlay(), tile.getTeam());
|
return MapIO.colorFor(tile.floor(), tile.block(), tile.overlay(), tile.getTeam());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -97,11 +97,10 @@ public class OverlayRenderer{
|
|||||||
//draw selected block bars and info
|
//draw selected block bars and info
|
||||||
if(input.block == null && !Core.scene.hasMouse()){
|
if(input.block == null && !Core.scene.hasMouse()){
|
||||||
Vector2 vec = Core.input.mouseWorld(input.getMouseX(), input.getMouseY());
|
Vector2 vec = Core.input.mouseWorld(input.getMouseX(), input.getMouseY());
|
||||||
Tile tile = world.tileWorld(vec.x, vec.y);
|
Tile tile = world.ltileWorld(vec.x, vec.y);
|
||||||
|
|
||||||
if(tile != null && tile.block() != Blocks.air && tile.target().getTeam() == player.getTeam()){
|
if(tile != null && tile.block() != Blocks.air && tile.getTeam() == player.getTeam()){
|
||||||
Tile target = tile.target();
|
tile.block().drawSelect(tile);
|
||||||
target.block().drawSelect(target);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,8 +112,7 @@ public class OverlayRenderer{
|
|||||||
Lines.circle(v.x, v.y, 6 + Mathf.absin(Time.time(), 5f, 1f));
|
Lines.circle(v.x, v.y, 6 + Mathf.absin(Time.time(), 5f, 1f));
|
||||||
Draw.reset();
|
Draw.reset();
|
||||||
|
|
||||||
Tile tile = world.tileWorld(v.x, v.y);
|
Tile tile = world.ltileWorld(v.x, v.y);
|
||||||
if(tile != null) tile = tile.target();
|
|
||||||
if(tile != null && tile.interactable(player.getTeam()) && tile.block().acceptStack(player.item().item, player.item().amount, tile, player) > 0){
|
if(tile != null && tile.interactable(player.getTeam()) && tile.block().acceptStack(player.item().item, player.item().amount, tile, player) > 0){
|
||||||
Draw.color(Pal.place);
|
Draw.color(Pal.place);
|
||||||
Lines.square(tile.drawx(), tile.drawy(), tile.block().size * tilesize / 2f + 1 + Mathf.absin(Time.time(), 5f, 1f));
|
Lines.square(tile.drawx(), tile.drawy(), tile.block().size * tilesize / 2f + 1 + Mathf.absin(Time.time(), 5f, 1f));
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import io.anuke.arc.graphics.g2d.*;
|
|||||||
import io.anuke.arc.math.Mathf;
|
import io.anuke.arc.math.Mathf;
|
||||||
import io.anuke.arc.util.Tmp;
|
import io.anuke.arc.util.Tmp;
|
||||||
|
|
||||||
//TODO remove
|
|
||||||
public class Shapes{
|
public class Shapes{
|
||||||
|
|
||||||
public static void laser(String line, String edge, float x, float y, float x2, float y2, float scale){
|
public static void laser(String line, String edge, float x, float y, float x2, float y2, float scale){
|
||||||
|
|||||||
@@ -96,9 +96,8 @@ public class DesktopInput extends InputHandler{
|
|||||||
|
|
||||||
for(int x = dresult.x; x <= dresult.x2; x++){
|
for(int x = dresult.x; x <= dresult.x2; x++){
|
||||||
for(int y = dresult.y; y <= dresult.y2; y++){
|
for(int y = dresult.y; y <= dresult.y2; y++){
|
||||||
Tile tile = world.tile(x, y);
|
Tile tile = world.ltile(x, y);
|
||||||
if(tile == null || !validBreak(tile.x, tile.y)) continue;
|
if(tile == null || !validBreak(tile.x, tile.y)) continue;
|
||||||
tile = tile.target();
|
|
||||||
|
|
||||||
Draw.color(Pal.removeBack);
|
Draw.color(Pal.removeBack);
|
||||||
Lines.square(tile.drawx(), tile.drawy() - 1, tile.block().size * tilesize / 2f - 1);
|
Lines.square(tile.drawx(), tile.drawy() - 1, tile.block().size * tilesize / 2f - 1);
|
||||||
@@ -175,7 +174,7 @@ public class DesktopInput extends InputHandler{
|
|||||||
Tile cursor = tileAt(Core.input.mouseX(), Core.input.mouseY());
|
Tile cursor = tileAt(Core.input.mouseX(), Core.input.mouseY());
|
||||||
|
|
||||||
if(cursor != null){
|
if(cursor != null){
|
||||||
cursor = cursor.target();
|
cursor = cursor.link();
|
||||||
|
|
||||||
cursorType = cursor.block().getCursor(cursor);
|
cursorType = cursor.block().getCursor(cursor);
|
||||||
|
|
||||||
@@ -257,7 +256,7 @@ public class DesktopInput extends InputHandler{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(selected != null){
|
if(selected != null){
|
||||||
tryDropItems(selected.target(), Core.input.mouseWorld().x, Core.input.mouseWorld().y);
|
tryDropItems(selected.link(), Core.input.mouseWorld().x, Core.input.mouseWorld().y);
|
||||||
}
|
}
|
||||||
|
|
||||||
mode = none;
|
mode = none;
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ public abstract class InputHandler implements InputProcessor{
|
|||||||
|
|
||||||
/** Handles tile tap events that are not platform specific. */
|
/** Handles tile tap events that are not platform specific. */
|
||||||
boolean tileTapped(Tile tile){
|
boolean tileTapped(Tile tile){
|
||||||
tile = tile.target();
|
tile = tile.link();
|
||||||
|
|
||||||
boolean consumed = false, showedInventory = false;
|
boolean consumed = false, showedInventory = false;
|
||||||
|
|
||||||
@@ -331,7 +331,7 @@ public abstract class InputHandler implements InputProcessor{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void breakBlock(int x, int y){
|
public void breakBlock(int x, int y){
|
||||||
Tile tile = world.tile(x, y).target();
|
Tile tile = world.ltile(x, y);
|
||||||
player.addBuildRequest(new BuildRequest(tile.x, tile.y));
|
player.addBuildRequest(new BuildRequest(tile.x, tile.y));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -87,8 +87,7 @@ public class MobileInput extends InputHandler implements GestureListener{
|
|||||||
player.setMineTile(null);
|
player.setMineTile(null);
|
||||||
player.target = unit;
|
player.target = unit;
|
||||||
}else{
|
}else{
|
||||||
Tile tile = world.tileWorld(x, y);
|
Tile tile = world.ltileWorld(x, y);
|
||||||
if(tile != null) tile = tile.target();
|
|
||||||
|
|
||||||
if(tile != null && tile.synthetic() && state.teams.areEnemies(player.getTeam(), tile.getTeam())){
|
if(tile != null && tile.synthetic() && state.teams.areEnemies(player.getTeam(), tile.getTeam())){
|
||||||
TileEntity entity = tile.entity;
|
TileEntity entity = tile.entity;
|
||||||
@@ -416,9 +415,8 @@ public class MobileInput extends InputHandler implements GestureListener{
|
|||||||
|
|
||||||
for(int x = dresult.x; x <= dresult.x2; x++){
|
for(int x = dresult.x; x <= dresult.x2; x++){
|
||||||
for(int y = dresult.y; y <= dresult.y2; y++){
|
for(int y = dresult.y; y <= dresult.y2; y++){
|
||||||
Tile other = world.tile(x, y);
|
Tile other = world.ltile(x, y);
|
||||||
if(other == null || !validBreak(other.x, other.y)) continue;
|
if(other == null || !validBreak(other.x, other.y)) continue;
|
||||||
other = other.target();
|
|
||||||
|
|
||||||
Draw.color(Pal.removeBack);
|
Draw.color(Pal.removeBack);
|
||||||
Lines.square(other.drawx(), other.drawy() - 1, other.block().size * tilesize / 2f - 1);
|
Lines.square(other.drawx(), other.drawy() - 1, other.block().size * tilesize / 2f - 1);
|
||||||
@@ -514,12 +512,10 @@ public class MobileInput extends InputHandler implements GestureListener{
|
|||||||
int wx = lineStartX + x * Mathf.sign(tileX - lineStartX);
|
int wx = lineStartX + x * Mathf.sign(tileX - lineStartX);
|
||||||
int wy = lineStartY + y * Mathf.sign(tileY - lineStartY);
|
int wy = lineStartY + y * Mathf.sign(tileY - lineStartY);
|
||||||
|
|
||||||
Tile tar = world.tile(wx, wy);
|
Tile tar = world.ltile(wx, wy);
|
||||||
|
|
||||||
if(tar == null) continue;
|
if(tar == null) continue;
|
||||||
|
|
||||||
tar = tar.target();
|
|
||||||
|
|
||||||
if(!hasRequest(world.tile(tar.x, tar.y)) && validBreak(tar.x, tar.y)){
|
if(!hasRequest(world.tile(tar.x, tar.y)) && validBreak(tar.x, tar.y)){
|
||||||
PlaceRequest request = new PlaceRequest(tar.x, tar.y);
|
PlaceRequest request = new PlaceRequest(tar.x, tar.y);
|
||||||
request.scale = 1f;
|
request.scale = 1f;
|
||||||
@@ -535,7 +531,7 @@ public class MobileInput extends InputHandler implements GestureListener{
|
|||||||
|
|
||||||
if(tile == null) return false;
|
if(tile == null) return false;
|
||||||
|
|
||||||
tryDropItems(tile.target(), Core.input.mouseWorld(screenX, screenY).x, Core.input.mouseWorld(screenX, screenY).y);
|
tryDropItems(tile.link(), Core.input.mouseWorld(screenX, screenY).x, Core.input.mouseWorld(screenX, screenY).y);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -585,11 +581,11 @@ public class MobileInput extends InputHandler implements GestureListener{
|
|||||||
}else if(mode == placing && isPlacing() && validPlace(cursor.x, cursor.y, block, rotation) && !checkOverlapPlacement(cursor.x, cursor.y, block)){
|
}else if(mode == placing && isPlacing() && validPlace(cursor.x, cursor.y, block, rotation) && !checkOverlapPlacement(cursor.x, cursor.y, block)){
|
||||||
//add to selection queue if it's a valid place position
|
//add to selection queue if it's a valid place position
|
||||||
selection.add(lastPlaced = new PlaceRequest(cursor.x, cursor.y, block, rotation));
|
selection.add(lastPlaced = new PlaceRequest(cursor.x, cursor.y, block, rotation));
|
||||||
}else if(mode == breaking && validBreak(cursor.target().x, cursor.target().y) && !hasRequest(cursor.target())){
|
}else if(mode == breaking && validBreak(cursor.link().x, cursor.link().y) && !hasRequest(cursor.link())){
|
||||||
//add to selection queue if it's a valid BREAK position
|
//add to selection queue if it's a valid BREAK position
|
||||||
cursor = cursor.target();
|
cursor = cursor.link();
|
||||||
selection.add(new PlaceRequest(cursor.x, cursor.y));
|
selection.add(new PlaceRequest(cursor.x, cursor.y));
|
||||||
}else if(!canTapPlayer(worldx, worldy) && !tileTapped(cursor.target())){
|
}else if(!canTapPlayer(worldx, worldy) && !tileTapped(cursor.link())){
|
||||||
tryBeginMine(cursor);
|
tryBeginMine(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
40
core/src/io/anuke/mindustry/io/JsonIO.java
Normal file
40
core/src/io/anuke/mindustry/io/JsonIO.java
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package io.anuke.mindustry.io;
|
||||||
|
|
||||||
|
import io.anuke.arc.util.serialization.Json;
|
||||||
|
import io.anuke.arc.util.serialization.JsonValue;
|
||||||
|
import io.anuke.mindustry.Vars;
|
||||||
|
import io.anuke.mindustry.game.Rules;
|
||||||
|
import io.anuke.mindustry.game.SpawnGroup;
|
||||||
|
import io.anuke.mindustry.type.ContentType;
|
||||||
|
import io.anuke.mindustry.type.Zone;
|
||||||
|
|
||||||
|
public class JsonIO{
|
||||||
|
private static Json json = new Json(){{
|
||||||
|
setIgnoreUnknownFields(true);
|
||||||
|
setElementType(Rules.class, "spawns", SpawnGroup.class);
|
||||||
|
|
||||||
|
setSerializer(Zone.class, new Serializer<Zone>(){
|
||||||
|
@Override
|
||||||
|
public void write(Json json, Zone object, Class knownType){
|
||||||
|
json.writeValue(object.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Zone read(Json json, JsonValue jsonData, Class type){
|
||||||
|
return Vars.content.getByName(ContentType.zone, jsonData.asString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}};
|
||||||
|
|
||||||
|
public static String write(Object object){
|
||||||
|
return json.toJson(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> T read(Class<T> type, String string){
|
||||||
|
return json.fromJson(type, string);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String print(String in){
|
||||||
|
return json.prettyPrint(in);
|
||||||
|
}
|
||||||
|
}
|
||||||
226
core/src/io/anuke/mindustry/io/LegacyMapIO.java
Normal file
226
core/src/io/anuke/mindustry/io/LegacyMapIO.java
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
package io.anuke.mindustry.io;
|
||||||
|
|
||||||
|
import io.anuke.arc.collection.*;
|
||||||
|
import io.anuke.arc.files.FileHandle;
|
||||||
|
import io.anuke.arc.graphics.Color;
|
||||||
|
import io.anuke.arc.graphics.Pixmap;
|
||||||
|
import io.anuke.arc.util.Pack;
|
||||||
|
import io.anuke.arc.util.Structs;
|
||||||
|
import io.anuke.arc.util.serialization.Json;
|
||||||
|
import io.anuke.mindustry.content.Blocks;
|
||||||
|
import io.anuke.mindustry.game.SpawnGroup;
|
||||||
|
import io.anuke.mindustry.game.Team;
|
||||||
|
import io.anuke.mindustry.io.MapIO.TileProvider;
|
||||||
|
import io.anuke.mindustry.maps.Map;
|
||||||
|
import io.anuke.mindustry.type.ContentType;
|
||||||
|
import io.anuke.mindustry.world.*;
|
||||||
|
import io.anuke.mindustry.world.LegacyColorMapper.LegacyBlock;
|
||||||
|
import io.anuke.mindustry.world.blocks.BlockPart;
|
||||||
|
import io.anuke.mindustry.world.blocks.Floor;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.util.zip.InflaterInputStream;
|
||||||
|
|
||||||
|
import static io.anuke.mindustry.Vars.*;
|
||||||
|
|
||||||
|
/** Map IO for the "old" .mmap format.
|
||||||
|
* Differentiate between legacy maps and new maps by checking the extension (or the header).*/
|
||||||
|
public class LegacyMapIO{
|
||||||
|
private static final ObjectMap<String, String> fallback = ObjectMap.of("alpha-dart-mech-pad", "dart-mech-pad");
|
||||||
|
private static final Json json = new Json();
|
||||||
|
|
||||||
|
/* Convert a map from the old format to the new format. */
|
||||||
|
public static void convertMap(FileHandle in, FileHandle out) throws IOException{
|
||||||
|
Map map = readMap(in, true);
|
||||||
|
|
||||||
|
String waves = map.tags.get("waves", "[]");
|
||||||
|
Array<SpawnGroup> groups = new Array<>(json.fromJson(SpawnGroup[].class, waves));
|
||||||
|
|
||||||
|
Tile[][] tiles = world.createTiles(map.width, map.height);
|
||||||
|
for(int x = 0; x < map.width; x++){
|
||||||
|
for(int y = 0; y < map.height; y++){
|
||||||
|
tiles[x][y] = new CachedTile();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
state.rules.spawns = groups;
|
||||||
|
readTiles(map, tiles);
|
||||||
|
MapIO.writeMap(out, map);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map readMap(FileHandle file, boolean custom) throws IOException{
|
||||||
|
try(DataInputStream stream = new DataInputStream(file.read(1024))){
|
||||||
|
StringMap tags = new StringMap();
|
||||||
|
|
||||||
|
//meta is uncompressed
|
||||||
|
int version = stream.readInt();
|
||||||
|
int build = stream.readInt();
|
||||||
|
short width = stream.readShort(), height = stream.readShort();
|
||||||
|
byte tagAmount = stream.readByte();
|
||||||
|
|
||||||
|
for(int i = 0; i < tagAmount; i++){
|
||||||
|
String name = stream.readUTF();
|
||||||
|
String value = stream.readUTF();
|
||||||
|
tags.put(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Map(file, width, height, tags, custom, version, build);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void readTiles(Map map, Tile[][] tiles) throws IOException{
|
||||||
|
readTiles(map, (x, y) -> tiles[x][y]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void readTiles(Map map, TileProvider tiles) throws IOException{
|
||||||
|
readTiles(map.file, map.width, map.height, tiles);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void readTiles(FileHandle file, int width, int height, Tile[][] tiles) throws IOException{
|
||||||
|
readTiles(file, width, height, (x, y) -> tiles[x][y]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void readTiles(FileHandle file, int width, int height, TileProvider tiles) throws IOException{
|
||||||
|
try(BufferedInputStream input = file.read(bufferSize)){
|
||||||
|
|
||||||
|
//read map
|
||||||
|
{
|
||||||
|
DataInputStream stream = new DataInputStream(input);
|
||||||
|
|
||||||
|
stream.readInt(); //version
|
||||||
|
stream.readInt(); //build
|
||||||
|
stream.readInt(); //width + height
|
||||||
|
byte tagAmount = stream.readByte();
|
||||||
|
|
||||||
|
for(int i = 0; i < tagAmount; i++){
|
||||||
|
stream.readUTF(); //key
|
||||||
|
stream.readUTF(); //val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try(DataInputStream stream = new DataInputStream(new InflaterInputStream(input))){
|
||||||
|
|
||||||
|
try{
|
||||||
|
byte mapped = stream.readByte();
|
||||||
|
IntMap<Block> idmap = new IntMap<>();
|
||||||
|
IntMap<String> namemap = new IntMap<>();
|
||||||
|
|
||||||
|
for(int i = 0; i < mapped; i++){
|
||||||
|
byte type = stream.readByte();
|
||||||
|
short total = stream.readShort();
|
||||||
|
|
||||||
|
for(int j = 0; j < total; j++){
|
||||||
|
String name = stream.readUTF();
|
||||||
|
if(type == 1){
|
||||||
|
Block res = content.getByName(ContentType.block, fallback.get(name, name));
|
||||||
|
idmap.put(j, res == null ? Blocks.air : res);
|
||||||
|
namemap.put(j, fallback.get(name, name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//read floor and create tiles first
|
||||||
|
for(int i = 0; i < width * height; i++){
|
||||||
|
int x = i % width, y = i / width;
|
||||||
|
int floorid = stream.readUnsignedByte();
|
||||||
|
int oreid = stream.readUnsignedByte();
|
||||||
|
int consecutives = stream.readUnsignedByte();
|
||||||
|
|
||||||
|
Tile tile = tiles.get(x, y);
|
||||||
|
tile.setFloor((Floor)idmap.get(floorid));
|
||||||
|
tile.setOverlay(idmap.get(oreid));
|
||||||
|
|
||||||
|
for(int j = i + 1; j < i + 1 + consecutives; j++){
|
||||||
|
int newx = j % width, newy = j / width;
|
||||||
|
Tile newTile = tiles.get(newx, newy);
|
||||||
|
newTile.setFloor((Floor)idmap.get(floorid));
|
||||||
|
newTile.setOverlay(idmap.get(oreid));
|
||||||
|
}
|
||||||
|
|
||||||
|
i += consecutives;
|
||||||
|
}
|
||||||
|
|
||||||
|
//read blocks
|
||||||
|
for(int i = 0; i < width * height; i++){
|
||||||
|
int x = i % width, y = i / width;
|
||||||
|
int id = stream.readUnsignedByte();
|
||||||
|
Block block = idmap.get(id);
|
||||||
|
|
||||||
|
Tile tile = tiles.get(x, y);
|
||||||
|
//the spawn block is saved in the block tile layer in older maps, shift it to the overlay
|
||||||
|
if(block != Blocks.spawn){
|
||||||
|
tile.setBlock(block);
|
||||||
|
}else{
|
||||||
|
tile.setOverlay(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(namemap.get(id).equals("part")){
|
||||||
|
stream.readByte(); //link
|
||||||
|
}else if(tile.entity != null){
|
||||||
|
byte tr = stream.readByte();
|
||||||
|
stream.readShort(); //read health (which is actually irrelevant)
|
||||||
|
|
||||||
|
byte team = Pack.leftByte(tr);
|
||||||
|
byte rotation = Pack.rightByte(tr);
|
||||||
|
|
||||||
|
tile.setTeam(Team.all[team]);
|
||||||
|
tile.entity.health = tile.block().health;
|
||||||
|
tile.rotation(rotation);
|
||||||
|
|
||||||
|
if(tile.block() == Blocks.liquidSource || tile.block() == Blocks.unloader || tile.block() == Blocks.sorter){
|
||||||
|
stream.readByte(); //these blocks have an extra config byte, read it
|
||||||
|
}
|
||||||
|
}else{ //no entity/part, read consecutives
|
||||||
|
int consecutives = stream.readUnsignedByte();
|
||||||
|
|
||||||
|
for(int j = i + 1; j < i + 1 + consecutives; j++){
|
||||||
|
int newx = j % width, newy = j / width;
|
||||||
|
tiles.get(newx, newy).setBlock(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
i += consecutives;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}finally{
|
||||||
|
content.setTemporaryMapper(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Reads a pixmap in the 3.5 pixmap format. */
|
||||||
|
public static void readPixmap(Pixmap pixmap, Tile[][] tiles){
|
||||||
|
for(int x = 0; x < pixmap.getWidth(); x++){
|
||||||
|
for(int y = 0; y < pixmap.getHeight(); y++){
|
||||||
|
int color = pixmap.getPixel(x, pixmap.getHeight() - 1 - y);
|
||||||
|
LegacyBlock block = LegacyColorMapper.get(color);
|
||||||
|
Tile tile = tiles[x][y];
|
||||||
|
|
||||||
|
tile.setFloor(block.floor);
|
||||||
|
tile.setBlock(block.wall);
|
||||||
|
if(block.ore != null) tile.setOverlay(block.ore);
|
||||||
|
|
||||||
|
//place core
|
||||||
|
if(color == Color.rgba8888(Color.GREEN)){
|
||||||
|
for(int dx = 0; dx < 3; dx++){
|
||||||
|
for(int dy = 0; dy < 3; dy++){
|
||||||
|
int worldx = dx - 1 + x;
|
||||||
|
int worldy = dy - 1 + y;
|
||||||
|
|
||||||
|
//multiblock parts
|
||||||
|
if(Structs.inBounds(worldx, worldy, pixmap.getWidth(), pixmap.getHeight())){
|
||||||
|
Tile write = tiles[worldx][worldy];
|
||||||
|
write.setBlock(BlockPart.get(dx - 1, dy - 1));
|
||||||
|
write.setTeam(Team.blue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//actual core parts
|
||||||
|
tile.setBlock(Blocks.coreShard);
|
||||||
|
tile.setTeam(Team.blue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,53 +1,27 @@
|
|||||||
package io.anuke.mindustry.io;
|
package io.anuke.mindustry.io;
|
||||||
|
|
||||||
import io.anuke.arc.collection.IntIntMap;
|
import io.anuke.arc.collection.StringMap;
|
||||||
import io.anuke.arc.collection.ObjectMap;
|
|
||||||
import io.anuke.arc.collection.ObjectMap.Entry;
|
|
||||||
import io.anuke.arc.files.FileHandle;
|
import io.anuke.arc.files.FileHandle;
|
||||||
import io.anuke.arc.graphics.Color;
|
import io.anuke.arc.graphics.Color;
|
||||||
import io.anuke.arc.graphics.Pixmap;
|
import io.anuke.arc.graphics.Pixmap;
|
||||||
import io.anuke.arc.graphics.Pixmap.Format;
|
import io.anuke.arc.graphics.Pixmap.Format;
|
||||||
import io.anuke.arc.math.Mathf;
|
import io.anuke.arc.util.io.CounterInputStream;
|
||||||
import io.anuke.arc.util.*;
|
|
||||||
import io.anuke.mindustry.content.Blocks;
|
import io.anuke.mindustry.content.Blocks;
|
||||||
import io.anuke.mindustry.game.*;
|
import io.anuke.mindustry.game.Team;
|
||||||
|
import io.anuke.mindustry.game.Version;
|
||||||
import io.anuke.mindustry.maps.Map;
|
import io.anuke.mindustry.maps.Map;
|
||||||
import io.anuke.mindustry.type.ContentType;
|
|
||||||
import io.anuke.mindustry.type.Item;
|
|
||||||
import io.anuke.mindustry.world.*;
|
import io.anuke.mindustry.world.*;
|
||||||
import io.anuke.mindustry.world.LegacyColorMapper.LegacyBlock;
|
import io.anuke.mindustry.world.blocks.storage.CoreBlock;
|
||||||
import io.anuke.mindustry.world.blocks.BlockPart;
|
|
||||||
import io.anuke.mindustry.world.blocks.Floor;
|
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.zip.DeflaterOutputStream;
|
|
||||||
import java.util.zip.InflaterInputStream;
|
import java.util.zip.InflaterInputStream;
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.bufferSize;
|
import static io.anuke.mindustry.Vars.*;
|
||||||
import static io.anuke.mindustry.Vars.content;
|
|
||||||
|
|
||||||
/** Reads and writes map files. */
|
/** Reads and writes map files. */
|
||||||
|
//TODO does this class even need to exist??? move to Maps?
|
||||||
public class MapIO{
|
public class MapIO{
|
||||||
public static final int version = 1;
|
|
||||||
|
|
||||||
private static final int[] pngHeader = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
|
private static final int[] pngHeader = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
|
||||||
private static ObjectMap<String, Block> missingBlocks;
|
|
||||||
|
|
||||||
private static void initBlocks(){
|
|
||||||
if(missingBlocks != null) return;
|
|
||||||
|
|
||||||
//only for legacy maps
|
|
||||||
missingBlocks = ObjectMap.of(
|
|
||||||
"stained-stone", Blocks.shale,
|
|
||||||
"stained-stone-red", Blocks.shale,
|
|
||||||
"stained-stone-yellow", Blocks.shale,
|
|
||||||
"stained-rocks", Blocks.shaleRocks,
|
|
||||||
"stained-boulder", Blocks.shaleBoulder,
|
|
||||||
"stained-rocks-red", Blocks.shaleRocks,
|
|
||||||
"stained-rocks-yellow", Blocks.shaleRocks
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean isImage(FileHandle file){
|
public static boolean isImage(FileHandle file){
|
||||||
try(InputStream stream = file.read(32)){
|
try(InputStream stream = file.read(32)){
|
||||||
@@ -62,42 +36,107 @@ public class MapIO{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Map createMap(FileHandle file, boolean custom) throws IOException{
|
||||||
|
try(InputStream is = new InflaterInputStream(file.read(bufferSize)); CounterInputStream counter = new CounterInputStream(is); DataInputStream stream = new DataInputStream(counter)){
|
||||||
|
SaveIO.readHeader(stream);
|
||||||
|
int version = stream.readInt();
|
||||||
|
SaveVersion ver = SaveIO.getSaveWriter(version);
|
||||||
|
StringMap tags = new StringMap();
|
||||||
|
ver.region("meta", stream, counter, in -> tags.putAll(ver.readStringMap(in)));
|
||||||
|
return new Map(file, tags.getInt("width"), tags.getInt("height"), tags, custom, version, Version.build);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void writeMap(FileHandle file, Map map) throws IOException{
|
||||||
|
try{
|
||||||
|
SaveIO.write(file, map.tags);
|
||||||
|
}catch(Exception e){
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void loadMap(Map map){
|
||||||
|
SaveIO.load(map.file);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void loadMap(Map map, WorldContext cons){
|
||||||
|
SaveIO.load(map.file, cons);
|
||||||
|
}
|
||||||
|
|
||||||
public static Pixmap generatePreview(Map map) throws IOException{
|
public static Pixmap generatePreview(Map map) throws IOException{
|
||||||
Time.mark();
|
//by default, it does not have an enemy core or any other cores
|
||||||
Pixmap floors = new Pixmap(map.width, map.height, Format.RGBA8888);
|
map.tags.put("enemycore", "false");
|
||||||
Pixmap walls = new Pixmap(map.width, map.height, Format.RGBA8888);
|
map.tags.put("othercore", "false");
|
||||||
int black = Color.rgba8888(Color.BLACK);
|
|
||||||
int shade = Color.rgba8888(0f, 0f, 0f, 0.5f);
|
|
||||||
CachedTile tile = new CachedTile(){
|
|
||||||
@Override
|
|
||||||
public void setFloor(Floor type){
|
|
||||||
floors.drawPixel(x, floors.getHeight() - 1 - y, colorFor(type, Blocks.air, Blocks.air, getTeam()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
try(InputStream is = new InflaterInputStream(map.file.read(bufferSize)); CounterInputStream counter = new CounterInputStream(is); DataInputStream stream = new DataInputStream(counter)){
|
||||||
public void setOverlayID(byte b){
|
SaveIO.readHeader(stream);
|
||||||
if(b != 0)
|
int version = stream.readInt();
|
||||||
floors.drawPixel(x, floors.getHeight() - 1 - y, colorFor(floor(), Blocks.air, content.block(b), getTeam()));
|
SaveVersion ver = SaveIO.getSaveWriter(version);
|
||||||
}
|
ver.region("meta", stream, counter, ver::readStringMap);
|
||||||
|
|
||||||
@Override
|
Pixmap floors = new Pixmap(map.width, map.height, Format.RGBA8888);
|
||||||
protected void changed(){
|
Pixmap walls = new Pixmap(map.width, map.height, Format.RGBA8888);
|
||||||
super.changed();
|
int black = Color.rgba8888(Color.BLACK);
|
||||||
int c = colorFor(Blocks.air, block(), Blocks.air, getTeam());
|
int shade = Color.rgba8888(0f, 0f, 0f, 0.5f);
|
||||||
if(c != black){
|
CachedTile tile = new CachedTile(){
|
||||||
walls.drawPixel(x, floors.getHeight() - 1 - y, c);
|
@Override
|
||||||
floors.drawPixel(x, floors.getHeight() - 1 - y + 1, shade);
|
public void setBlock(Block type){
|
||||||
|
super.setBlock(type);
|
||||||
|
int c = colorFor(Blocks.air, block(), Blocks.air, getTeam());
|
||||||
|
if(c != black){
|
||||||
|
walls.drawPixel(x, floors.getHeight() - 1 - y, c);
|
||||||
|
floors.drawPixel(x, floors.getHeight() - 1 - y + 1, shade);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
@Override
|
||||||
readTiles(map, (x, y) -> {
|
public void setTeam(Team team){
|
||||||
tile.x = (short)x;
|
super.setTeam(team);
|
||||||
tile.y = (short)y;
|
if(block instanceof CoreBlock){
|
||||||
return tile;
|
if(team != defaultTeam){
|
||||||
});
|
//map must have other team's cores
|
||||||
floors.drawPixmap(walls, 0, 0);
|
map.tags.put("othercore", "true");
|
||||||
walls.dispose();
|
}
|
||||||
return floors;
|
|
||||||
|
if(team == waveTeam){
|
||||||
|
//map must have default enemy team's core
|
||||||
|
map.tags.put("enemycore", "true");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ver.region("content", stream, counter, ver::readContentHeader);
|
||||||
|
ver.region("preview_map", stream, counter, in -> ver.readMap(in, new WorldContext(){
|
||||||
|
@Override public void resize(int width, int height){}
|
||||||
|
@Override public boolean isGenerating(){return false;}
|
||||||
|
@Override public void begin(){}
|
||||||
|
@Override public void end(){}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Tile tile(int x, int y){
|
||||||
|
tile.x = (short)x;
|
||||||
|
tile.y = (short)y;
|
||||||
|
return tile;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Tile create(int x, int y, int floorID, int overlayID, int wallID){
|
||||||
|
if(overlayID != 0){
|
||||||
|
floors.drawPixel(x, floors.getHeight() - 1 - y, colorFor(Blocks.air, Blocks.air, content.block(overlayID), Team.none));
|
||||||
|
}else{
|
||||||
|
floors.drawPixel(x, floors.getHeight() - 1 - y, colorFor(content.block(floorID), Blocks.air, Blocks.air, Team.none));
|
||||||
|
}
|
||||||
|
return tile;
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
floors.drawPixmap(walls, 0, 0);
|
||||||
|
walls.dispose();
|
||||||
|
return floors;
|
||||||
|
}finally{
|
||||||
|
content.setTemporaryMapper(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Pixmap generatePreview(Tile[][] tiles){
|
public static Pixmap generatePreview(Tile[][] tiles){
|
||||||
@@ -118,360 +157,6 @@ public class MapIO{
|
|||||||
return Color.rgba8888(wall.solid ? wall.color : ore == Blocks.air ? floor.color : ore.color);
|
return Color.rgba8888(wall.solid ? wall.color : ore == Blocks.air ? floor.color : ore.color);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void writeMap(FileHandle file, Map map, Tile[][] tiles) throws IOException{
|
|
||||||
OutputStream output = file.write(false, bufferSize);
|
|
||||||
|
|
||||||
{
|
|
||||||
DataOutputStream stream = new DataOutputStream(output);
|
|
||||||
stream.writeInt(version);
|
|
||||||
stream.writeInt(Version.build);
|
|
||||||
stream.writeShort(tiles.length);
|
|
||||||
stream.writeShort(tiles[0].length);
|
|
||||||
stream.writeByte((byte)map.tags.size);
|
|
||||||
|
|
||||||
for(Entry<String, String> entry : map.tags.entries()){
|
|
||||||
stream.writeUTF(entry.key);
|
|
||||||
stream.writeUTF(entry.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try(DataOutputStream stream = new DataOutputStream(new DeflaterOutputStream(output))){
|
|
||||||
int width = map.width, height = map.height;
|
|
||||||
|
|
||||||
SaveIO.getSaveWriter().writeContentHeader(stream);
|
|
||||||
|
|
||||||
//floor first
|
|
||||||
for(int i = 0; i < tiles.length * tiles[0].length; i++){
|
|
||||||
Tile tile = tiles[i % width][i / width];
|
|
||||||
stream.writeByte(tile.getFloorID());
|
|
||||||
stream.writeByte(tile.getOverlayID());
|
|
||||||
int consecutives = 0;
|
|
||||||
|
|
||||||
for(int j = i + 1; j < width * height && consecutives < 255; j++){
|
|
||||||
Tile nextTile = tiles[j % width][j / width];
|
|
||||||
|
|
||||||
if(nextTile.getFloorID() != tile.getFloorID() || nextTile.block() != Blocks.air || nextTile.getOverlayID() != tile.getOverlayID()){
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
consecutives++;
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.writeByte(consecutives);
|
|
||||||
i += consecutives;
|
|
||||||
}
|
|
||||||
|
|
||||||
//then blocks
|
|
||||||
for(int i = 0; i < tiles.length * tiles[0].length; i++){
|
|
||||||
Tile tile = tiles[i % width][i / width];
|
|
||||||
stream.writeByte(tile.getBlockID());
|
|
||||||
|
|
||||||
if(tile.block() instanceof BlockPart){
|
|
||||||
stream.writeByte(tile.getLinkByte());
|
|
||||||
}else if(tile.entity != null){
|
|
||||||
stream.writeByte(Pack.byteByte(tile.getTeamID(), tile.getRotation())); //team + rotation
|
|
||||||
stream.writeShort(/*(short)tile.entity.health*/tile.block().health); //health
|
|
||||||
tile.entity.writeConfig(stream);
|
|
||||||
}else{
|
|
||||||
//write consecutive non-entity blocks
|
|
||||||
int consecutives = 0;
|
|
||||||
|
|
||||||
for(int j = i + 1; j < width * height && consecutives < 255; j++){
|
|
||||||
Tile nextTile = tiles[j % width][j / width];
|
|
||||||
|
|
||||||
if(nextTile.block() != tile.block()){
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
consecutives++;
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.writeByte(consecutives);
|
|
||||||
i += consecutives;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Map readMap(FileHandle file, boolean custom) throws IOException{
|
|
||||||
try(DataInputStream stream = new DataInputStream(file.read(1024))){
|
|
||||||
ObjectMap<String, String> tags = new ObjectMap<>();
|
|
||||||
|
|
||||||
//meta is uncompressed
|
|
||||||
int version = stream.readInt();
|
|
||||||
if(version == 0){
|
|
||||||
return readLegacyMap(file, custom);
|
|
||||||
}
|
|
||||||
int build = stream.readInt();
|
|
||||||
short width = stream.readShort(), height = stream.readShort();
|
|
||||||
byte tagAmount = stream.readByte();
|
|
||||||
|
|
||||||
for(int i = 0; i < tagAmount; i++){
|
|
||||||
String name = stream.readUTF();
|
|
||||||
String value = stream.readUTF();
|
|
||||||
tags.put(name, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Map(file, width, height, tags, custom, version, build);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Reads tiles from a map, version-agnostic. */
|
|
||||||
public static void readTiles(Map map, Tile[][] tiles) throws IOException{
|
|
||||||
readTiles(map, (x, y) -> tiles[x][y]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Reads tiles from a map, version-agnostic. */
|
|
||||||
public static void readTiles(Map map, TileProvider tiles) throws IOException{
|
|
||||||
if(map.version == 0){
|
|
||||||
readLegacyMmapTiles(map.file, tiles);
|
|
||||||
}else if(map.version == version){
|
|
||||||
readTiles(map.file, map.width, map.height, tiles);
|
|
||||||
}else{
|
|
||||||
throw new IOException("Unknown map version. What?");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Reads tiles from a map in the new build-65 format. */
|
|
||||||
private static void readTiles(FileHandle file, int width, int height, Tile[][] tiles) throws IOException{
|
|
||||||
readTiles(file, width, height, (x, y) -> tiles[x][y]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Reads tiles from a map in the new build-65 format. */
|
|
||||||
private static void readTiles(FileHandle file, int width, int height, TileProvider tiles) throws IOException{
|
|
||||||
try(BufferedInputStream input = file.read(bufferSize)){
|
|
||||||
|
|
||||||
//read map
|
|
||||||
{
|
|
||||||
DataInputStream stream = new DataInputStream(input);
|
|
||||||
|
|
||||||
stream.readInt(); //version
|
|
||||||
stream.readInt(); //build
|
|
||||||
stream.readInt(); //width + height
|
|
||||||
byte tagAmount = stream.readByte();
|
|
||||||
|
|
||||||
for(int i = 0; i < tagAmount; i++){
|
|
||||||
stream.readUTF(); //key
|
|
||||||
stream.readUTF(); //val
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try(DataInputStream stream = new DataInputStream(new InflaterInputStream(input))){
|
|
||||||
|
|
||||||
MappableContent[][] c = SaveIO.getSaveWriter().readContentHeader(stream);
|
|
||||||
|
|
||||||
try{
|
|
||||||
content.setTemporaryMapper(c);
|
|
||||||
|
|
||||||
//read floor and create tiles first
|
|
||||||
for(int i = 0; i < width * height; i++){
|
|
||||||
int x = i % width, y = i / width;
|
|
||||||
byte floorid = stream.readByte();
|
|
||||||
byte oreid = stream.readByte();
|
|
||||||
int consecutives = stream.readUnsignedByte();
|
|
||||||
|
|
||||||
Tile tile = tiles.get(x, y);
|
|
||||||
tile.setFloor((Floor)content.block(floorid));
|
|
||||||
tile.setOverlay(content.block(oreid));
|
|
||||||
|
|
||||||
for(int j = i + 1; j < i + 1 + consecutives; j++){
|
|
||||||
int newx = j % width, newy = j / width;
|
|
||||||
Tile newTile = tiles.get(newx, newy);
|
|
||||||
newTile.setFloor((Floor)content.block(floorid));
|
|
||||||
newTile.setOverlay(content.block(oreid));
|
|
||||||
}
|
|
||||||
|
|
||||||
i += consecutives;
|
|
||||||
}
|
|
||||||
|
|
||||||
//read blocks
|
|
||||||
for(int i = 0; i < width * height; i++){
|
|
||||||
int x = i % width, y = i / width;
|
|
||||||
Block block = content.block(stream.readByte());
|
|
||||||
|
|
||||||
Tile tile = tiles.get(x, y);
|
|
||||||
tile.setBlock(block);
|
|
||||||
|
|
||||||
if(block == Blocks.part){
|
|
||||||
tile.setLinkByte(stream.readByte());
|
|
||||||
}else if(tile.entity != null){
|
|
||||||
byte tr = stream.readByte();
|
|
||||||
short health = stream.readShort();
|
|
||||||
|
|
||||||
byte team = Pack.leftByte(tr);
|
|
||||||
byte rotation = Pack.rightByte(tr);
|
|
||||||
|
|
||||||
tile.setTeam(Team.all[team]);
|
|
||||||
tile.entity.health = /*health*/tile.block().health;
|
|
||||||
tile.setRotation(rotation);
|
|
||||||
|
|
||||||
tile.entity.readConfig(stream);
|
|
||||||
}else{ //no entity/part, read consecutives
|
|
||||||
int consecutives = stream.readUnsignedByte();
|
|
||||||
|
|
||||||
for(int j = i + 1; j < i + 1 + consecutives; j++){
|
|
||||||
int newx = j % width, newy = j / width;
|
|
||||||
tiles.get(newx, newy).setBlock(block);
|
|
||||||
}
|
|
||||||
|
|
||||||
i += consecutives;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}finally{
|
|
||||||
content.setTemporaryMapper(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//region legacy IO
|
|
||||||
|
|
||||||
/** Reads a pixmap in the 3.5 pixmap format. */
|
|
||||||
public static void readLegacyPixmap(Pixmap pixmap, Tile[][] tiles){
|
|
||||||
for(int x = 0; x < pixmap.getWidth(); x++){
|
|
||||||
for(int y = 0; y < pixmap.getHeight(); y++){
|
|
||||||
int color = pixmap.getPixel(x, pixmap.getHeight() - 1 - y);
|
|
||||||
LegacyBlock block = LegacyColorMapper.get(color);
|
|
||||||
Tile tile = tiles[x][y];
|
|
||||||
|
|
||||||
tile.setFloor(block.floor);
|
|
||||||
tile.setBlock(block.wall);
|
|
||||||
if(block.ore != null) tile.setOverlay(block.ore);
|
|
||||||
|
|
||||||
//place core
|
|
||||||
if(color == Color.rgba8888(Color.GREEN)){
|
|
||||||
for(int dx = 0; dx < 3; dx++){
|
|
||||||
for(int dy = 0; dy < 3; dy++){
|
|
||||||
int worldx = dx - 1 + x;
|
|
||||||
int worldy = dy - 1 + y;
|
|
||||||
|
|
||||||
//multiblock parts
|
|
||||||
if(Structs.inBounds(worldx, worldy, pixmap.getWidth(), pixmap.getHeight())){
|
|
||||||
Tile write = tiles[worldx][worldy];
|
|
||||||
write.setBlock(Blocks.part);
|
|
||||||
write.setTeam(Team.blue);
|
|
||||||
write.setLinkByte(Pack.byteByte((byte)(dx - 1 + 8), (byte)(dy - 1 + 8)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//actual core parts
|
|
||||||
tile.setBlock(Blocks.coreShard);
|
|
||||||
tile.setTeam(Team.blue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Reads a pixmap in the old 4.0 .mmap format. */
|
|
||||||
private static void readLegacyMmapTiles(FileHandle file, Tile[][] tiles) throws IOException{
|
|
||||||
readLegacyMmapTiles(file, (x, y) -> tiles[x][y]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Reads a mmap in the old 4.0 .mmap format. */
|
|
||||||
private static void readLegacyMmapTiles(FileHandle file, TileProvider tiles) throws IOException{
|
|
||||||
try(DataInputStream stream = new DataInputStream(file.read(bufferSize))){
|
|
||||||
stream.readInt(); //version
|
|
||||||
byte tagAmount = stream.readByte();
|
|
||||||
|
|
||||||
for(int i = 0; i < tagAmount; i++){
|
|
||||||
stream.readUTF(); //key
|
|
||||||
stream.readUTF(); //val
|
|
||||||
}
|
|
||||||
|
|
||||||
initBlocks();
|
|
||||||
|
|
||||||
//block id -> real id map
|
|
||||||
IntIntMap map = new IntIntMap();
|
|
||||||
IntIntMap oreMap = new IntIntMap();
|
|
||||||
|
|
||||||
short blocks = stream.readShort();
|
|
||||||
for(int i = 0; i < blocks; i++){
|
|
||||||
short id = stream.readShort();
|
|
||||||
String name = stream.readUTF();
|
|
||||||
Block block = content.getByName(ContentType.block, name);
|
|
||||||
if(block == null){
|
|
||||||
//substitute for replacement in missingBlocks if possible
|
|
||||||
if(missingBlocks.containsKey(name)){
|
|
||||||
block = missingBlocks.get(name);
|
|
||||||
}else if(name.startsWith("ore-")){ //an ore floor combination
|
|
||||||
String[] split = name.split("-");
|
|
||||||
String itemName = split[1], floorName = Strings.join("-", Arrays.copyOfRange(split, 2, split.length));
|
|
||||||
Item item = content.getByName(ContentType.item, itemName);
|
|
||||||
Block oreBlock = item == null ? null : content.getByName(ContentType.block, "ore-" + item.name);
|
|
||||||
Block floor = missingBlocks.get(floorName, content.getByName(ContentType.block, floorName));
|
|
||||||
if(oreBlock != null && floor != null){
|
|
||||||
oreMap.put(id, oreBlock.id);
|
|
||||||
block = floor;
|
|
||||||
}else{
|
|
||||||
block = Blocks.air;
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
block = Blocks.air;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
map.put(id, block.id);
|
|
||||||
}
|
|
||||||
short width = stream.readShort(), height = stream.readShort();
|
|
||||||
|
|
||||||
for(int y = 0; y < height; y++){
|
|
||||||
for(int x = 0; x < width; x++){
|
|
||||||
Tile tile = tiles.get(x, y);
|
|
||||||
byte floorb = stream.readByte();
|
|
||||||
byte blockb = stream.readByte();
|
|
||||||
byte link = stream.readByte();
|
|
||||||
byte rotTeamb = stream.readByte();
|
|
||||||
stream.readByte();//unused stuff
|
|
||||||
|
|
||||||
tile.setFloor((Floor)content.block(map.get(floorb, 0)));
|
|
||||||
tile.setBlock(content.block(map.get(blockb, 0)));
|
|
||||||
tile.setRotation(Pack.leftByte(rotTeamb));
|
|
||||||
if(tile.block().synthetic()){
|
|
||||||
tile.setTeam(Team.all[Mathf.clamp(Pack.rightByte(rotTeamb), 0, Team.all.length)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(tile.block() == Blocks.part){
|
|
||||||
tile.setLinkByte(link);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(oreMap.containsKey(floorb)){
|
|
||||||
tile.setOverlay(content.block(oreMap.get(floorb, 0)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Map readLegacyMap(FileHandle file, boolean custom) throws IOException{
|
|
||||||
try(DataInputStream stream = new DataInputStream(file.read(bufferSize))){
|
|
||||||
ObjectMap<String, String> tags = new ObjectMap<>();
|
|
||||||
|
|
||||||
int version = stream.readInt();
|
|
||||||
if(version != 0) throw new IOException("Attempted to read non-legacy map in legacy method!");
|
|
||||||
byte tagAmount = stream.readByte();
|
|
||||||
|
|
||||||
for(int i = 0; i < tagAmount; i++){
|
|
||||||
String name = stream.readUTF();
|
|
||||||
String value = stream.readUTF();
|
|
||||||
tags.put(name, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
short blocks = stream.readShort();
|
|
||||||
for(int i = 0; i < blocks; i++){
|
|
||||||
stream.readShort();
|
|
||||||
stream.readUTF();
|
|
||||||
}
|
|
||||||
short width = stream.readShort(), height = stream.readShort();
|
|
||||||
|
|
||||||
//note that build 64 is the default build of all maps <65; while this can be inaccurate it's better than nothing
|
|
||||||
return new Map(file, width, height, tags, custom, 0, 64);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
interface TileProvider{
|
interface TileProvider{
|
||||||
Tile get(int x, int y);
|
Tile get(int x, int y);
|
||||||
}
|
}
|
||||||
|
|||||||
113
core/src/io/anuke/mindustry/io/SaveFileReader.java
Normal file
113
core/src/io/anuke/mindustry/io/SaveFileReader.java
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
package io.anuke.mindustry.io;
|
||||||
|
|
||||||
|
import io.anuke.arc.collection.ObjectMap;
|
||||||
|
import io.anuke.arc.collection.ObjectMap.Entry;
|
||||||
|
import io.anuke.arc.collection.StringMap;
|
||||||
|
import io.anuke.arc.util.io.CounterInputStream;
|
||||||
|
import io.anuke.arc.util.io.ReusableByteOutStream;
|
||||||
|
import io.anuke.mindustry.world.WorldContext;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
public abstract class SaveFileReader{
|
||||||
|
protected final ReusableByteOutStream byteOutput = new ReusableByteOutStream();
|
||||||
|
protected final DataOutputStream dataBytes = new DataOutputStream(byteOutput);
|
||||||
|
protected final ReusableByteOutStream byteOutputSmall = new ReusableByteOutStream();
|
||||||
|
protected final DataOutputStream dataBytesSmall = new DataOutputStream(byteOutputSmall);
|
||||||
|
protected final ObjectMap<String, String> fallback = ObjectMap.of();
|
||||||
|
|
||||||
|
protected void region(String name, DataInput stream, CounterInputStream counter, IORunner<DataInput> cons) throws IOException{
|
||||||
|
counter.resetCount();
|
||||||
|
int length;
|
||||||
|
try{
|
||||||
|
length = readChunk(stream, cons);
|
||||||
|
}catch(Throwable e){
|
||||||
|
throw new IOException("Error reading region \"" + name + "\".", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(length != counter.count() - 4){
|
||||||
|
throw new IOException("Error reading region \"" + name + "\": read length mismatch. Expected: " + length + "; Actual: " + (counter.count() - 4));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void region(String name, DataOutput stream, IORunner<DataOutput> cons) throws IOException{
|
||||||
|
try{
|
||||||
|
writeChunk(stream, cons);
|
||||||
|
}catch(Throwable e){
|
||||||
|
throw new IOException("Error writing region \"" + name + "\".", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeChunk(DataOutput output, IORunner<DataOutput> runner) throws IOException{
|
||||||
|
writeChunk(output, false, runner);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Write a chunk of input to the stream. An integer of some length is written first, followed by the data. */
|
||||||
|
public void writeChunk(DataOutput output, boolean isByte, IORunner<DataOutput> runner) throws IOException{
|
||||||
|
ReusableByteOutStream dout = isByte ? byteOutputSmall : byteOutput;
|
||||||
|
//reset output position
|
||||||
|
dout.reset();
|
||||||
|
//write the needed info
|
||||||
|
runner.accept(isByte ? dataBytesSmall : dataBytes);
|
||||||
|
int length = dout.size();
|
||||||
|
//write length (either int or byte) followed by the output bytes
|
||||||
|
if(!isByte){
|
||||||
|
output.writeInt(length);
|
||||||
|
}else{
|
||||||
|
if(length > Short.MAX_VALUE){
|
||||||
|
throw new IOException("Byte write length exceeded: " + length + " > " + Short.MAX_VALUE);
|
||||||
|
}
|
||||||
|
output.writeShort(length);
|
||||||
|
}
|
||||||
|
output.write(dout.getBytes(), 0, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int readChunk(DataInput input, IORunner<DataInput> runner) throws IOException{
|
||||||
|
return readChunk(input, false, runner);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Reads a chunk of some length. Use the runner for reading to catch more descriptive errors. */
|
||||||
|
public int readChunk(DataInput input, boolean isByte, IORunner<DataInput> runner) throws IOException{
|
||||||
|
int length = isByte ? input.readUnsignedShort() : input.readInt();
|
||||||
|
runner.accept(input);
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void skipRegion(DataInput input) throws IOException{
|
||||||
|
skipRegion(input, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Skip a region completely. */
|
||||||
|
public void skipRegion(DataInput input, boolean isByte) throws IOException{
|
||||||
|
int length = readChunk(input, isByte, t -> {});
|
||||||
|
int skipped = input.skipBytes(length);
|
||||||
|
if(length != skipped){
|
||||||
|
throw new IOException("Could not skip bytes. Expected length: " + length + "; Actual length: " + skipped);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeStringMap(DataOutput stream, ObjectMap<String, String> map) throws IOException{
|
||||||
|
stream.writeShort(map.size);
|
||||||
|
for(Entry<String, String> entry : map.entries()){
|
||||||
|
stream.writeUTF(entry.key);
|
||||||
|
stream.writeUTF(entry.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public StringMap readStringMap(DataInput stream) throws IOException{
|
||||||
|
StringMap map = new StringMap();
|
||||||
|
short size = stream.readShort();
|
||||||
|
for(int i = 0; i < size; i++){
|
||||||
|
map.put(stream.readUTF(), stream.readUTF());
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void read(DataInputStream stream, CounterInputStream counter, WorldContext context) throws IOException;
|
||||||
|
|
||||||
|
public abstract void write(DataOutputStream stream) throws IOException;
|
||||||
|
|
||||||
|
protected interface IORunner<T>{
|
||||||
|
void accept(T stream) throws IOException;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,258 +0,0 @@
|
|||||||
package io.anuke.mindustry.io;
|
|
||||||
|
|
||||||
import io.anuke.arc.collection.Array;
|
|
||||||
import io.anuke.arc.collection.ObjectMap;
|
|
||||||
import io.anuke.arc.util.Pack;
|
|
||||||
import io.anuke.mindustry.content.Blocks;
|
|
||||||
import io.anuke.mindustry.entities.Entities;
|
|
||||||
import io.anuke.mindustry.entities.EntityGroup;
|
|
||||||
import io.anuke.mindustry.entities.traits.*;
|
|
||||||
import io.anuke.mindustry.game.*;
|
|
||||||
import io.anuke.mindustry.gen.Serialization;
|
|
||||||
import io.anuke.mindustry.type.ContentType;
|
|
||||||
import io.anuke.mindustry.world.Block;
|
|
||||||
import io.anuke.mindustry.world.Tile;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.content;
|
|
||||||
import static io.anuke.mindustry.Vars.world;
|
|
||||||
|
|
||||||
public abstract class SaveFileVersion{
|
|
||||||
public final int version;
|
|
||||||
private final ObjectMap<String, String> fallback = ObjectMap.of(
|
|
||||||
"alpha-dart-mech-pad", "dart-mech-pad"
|
|
||||||
);
|
|
||||||
|
|
||||||
public SaveFileVersion(int version){
|
|
||||||
this.version = version;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SaveMeta getData(DataInputStream stream) throws IOException{
|
|
||||||
long time = stream.readLong();
|
|
||||||
long playtime = stream.readLong();
|
|
||||||
int build = stream.readInt();
|
|
||||||
|
|
||||||
Rules rules = Serialization.readRulesStreamJson(stream);
|
|
||||||
String map = stream.readUTF();
|
|
||||||
int wave = stream.readInt();
|
|
||||||
return new SaveMeta(version, time, playtime, build, map, wave, rules);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeMap(DataOutputStream stream) throws IOException{
|
|
||||||
//write world size
|
|
||||||
stream.writeShort(world.width());
|
|
||||||
stream.writeShort(world.height());
|
|
||||||
|
|
||||||
//floor first
|
|
||||||
for(int i = 0; i < world.width() * world.height(); i++){
|
|
||||||
Tile tile = world.tile(i % world.width(), i / world.width());
|
|
||||||
stream.writeByte(tile.getFloorID());
|
|
||||||
stream.writeByte(tile.getOverlayID());
|
|
||||||
int consecutives = 0;
|
|
||||||
|
|
||||||
for(int j = i + 1; j < world.width() * world.height() && consecutives < 255; j++){
|
|
||||||
Tile nextTile = world.tile(j % world.width(), j / world.width());
|
|
||||||
|
|
||||||
if(nextTile.getFloorID() != tile.getFloorID() || nextTile.getOverlayID() != tile.getOverlayID()){
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
consecutives++;
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.writeByte(consecutives);
|
|
||||||
i += consecutives;
|
|
||||||
}
|
|
||||||
|
|
||||||
//blocks
|
|
||||||
for(int i = 0; i < world.width() * world.height(); i++){
|
|
||||||
Tile tile = world.tile(i % world.width(), i / world.width());
|
|
||||||
stream.writeByte(tile.getBlockID());
|
|
||||||
|
|
||||||
if(tile.block() == Blocks.part){
|
|
||||||
stream.writeByte(tile.getLinkByte());
|
|
||||||
}else if(tile.entity != null){
|
|
||||||
stream.writeByte(Pack.byteByte(tile.getTeamID(), tile.getRotation())); //team + rotation
|
|
||||||
stream.writeShort((short)tile.entity.health); //health
|
|
||||||
|
|
||||||
if(tile.entity.items != null) tile.entity.items.write(stream);
|
|
||||||
if(tile.entity.power != null) tile.entity.power.write(stream);
|
|
||||||
if(tile.entity.liquids != null) tile.entity.liquids.write(stream);
|
|
||||||
if(tile.entity.cons != null) tile.entity.cons.write(stream);
|
|
||||||
|
|
||||||
tile.entity.writeConfig(stream);
|
|
||||||
tile.entity.write(stream);
|
|
||||||
}else{
|
|
||||||
//write consecutive non-entity blocks
|
|
||||||
int consecutives = 0;
|
|
||||||
|
|
||||||
for(int j = i + 1; j < world.width() * world.height() && consecutives < 255; j++){
|
|
||||||
Tile nextTile = world.tile(j % world.width(), j / world.width());
|
|
||||||
|
|
||||||
if(nextTile.block() != tile.block()){
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
consecutives++;
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.writeByte(consecutives);
|
|
||||||
i += consecutives;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void readMap(DataInputStream stream) throws IOException{
|
|
||||||
short width = stream.readShort();
|
|
||||||
short height = stream.readShort();
|
|
||||||
|
|
||||||
world.beginMapLoad();
|
|
||||||
|
|
||||||
Tile[][] tiles = world.createTiles(width, height);
|
|
||||||
|
|
||||||
//read floor and create tiles first
|
|
||||||
for(int i = 0; i < width * height; i++){
|
|
||||||
int x = i % width, y = i / width;
|
|
||||||
byte floorid = stream.readByte();
|
|
||||||
byte oreid = stream.readByte();
|
|
||||||
int consecutives = stream.readUnsignedByte();
|
|
||||||
Block ore = content.block(oreid);
|
|
||||||
|
|
||||||
tiles[x][y] = new Tile(x, y, floorid, (byte)0);
|
|
||||||
tiles[x][y].setOverlay(ore);
|
|
||||||
|
|
||||||
for(int j = i + 1; j < i + 1 + consecutives; j++){
|
|
||||||
int newx = j % width, newy = j / width;
|
|
||||||
Tile newTile = new Tile(newx, newy, floorid, (byte)0);
|
|
||||||
newTile.setOverlay(ore);
|
|
||||||
tiles[newx][newy] = newTile;
|
|
||||||
}
|
|
||||||
|
|
||||||
i += consecutives;
|
|
||||||
}
|
|
||||||
|
|
||||||
//read blocks
|
|
||||||
for(int i = 0; i < width * height; i++){
|
|
||||||
int x = i % width, y = i / width;
|
|
||||||
Block block = content.block(stream.readByte());
|
|
||||||
Tile tile = tiles[x][y];
|
|
||||||
tile.setBlock(block);
|
|
||||||
|
|
||||||
if(block == Blocks.part){
|
|
||||||
tile.setLinkByte(stream.readByte());
|
|
||||||
}else if(tile.entity != null){
|
|
||||||
byte tr = stream.readByte();
|
|
||||||
short health = stream.readShort();
|
|
||||||
|
|
||||||
byte team = Pack.leftByte(tr);
|
|
||||||
byte rotation = Pack.rightByte(tr);
|
|
||||||
|
|
||||||
tile.setTeam(Team.all[team]);
|
|
||||||
tile.entity.health = health;
|
|
||||||
tile.setRotation(rotation);
|
|
||||||
|
|
||||||
if(tile.entity.items != null) tile.entity.items.read(stream);
|
|
||||||
if(tile.entity.power != null) tile.entity.power.read(stream);
|
|
||||||
if(tile.entity.liquids != null) tile.entity.liquids.read(stream);
|
|
||||||
if(tile.entity.cons != null) tile.entity.cons.read(stream);
|
|
||||||
|
|
||||||
tile.entity.readConfig(stream);
|
|
||||||
tile.entity.read(stream);
|
|
||||||
}else{
|
|
||||||
int consecutives = stream.readUnsignedByte();
|
|
||||||
|
|
||||||
for(int j = i + 1; j < i + 1 + consecutives; j++){
|
|
||||||
int newx = j % width, newy = j / width;
|
|
||||||
tiles[newx][newy].setBlock(block);
|
|
||||||
}
|
|
||||||
|
|
||||||
i += consecutives;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
content.setTemporaryMapper(null);
|
|
||||||
world.endMapLoad();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeEntities(DataOutputStream stream) throws IOException{
|
|
||||||
int groups = 0;
|
|
||||||
|
|
||||||
for(EntityGroup<?> group : Entities.getAllGroups()){
|
|
||||||
if(!group.isEmpty() && group.all().get(0) instanceof SaveTrait){
|
|
||||||
groups++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.writeByte(groups);
|
|
||||||
|
|
||||||
for(EntityGroup<?> group : Entities.getAllGroups()){
|
|
||||||
if(!group.isEmpty() && group.all().get(0) instanceof SaveTrait){
|
|
||||||
stream.writeInt(group.size());
|
|
||||||
for(Entity entity : group.all()){
|
|
||||||
stream.writeByte(((SaveTrait)entity).getTypeID());
|
|
||||||
((SaveTrait)entity).writeSave(stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void readEntities(DataInputStream stream) throws IOException{
|
|
||||||
byte groups = stream.readByte();
|
|
||||||
|
|
||||||
for(int i = 0; i < groups; i++){
|
|
||||||
int amount = stream.readInt();
|
|
||||||
for(int j = 0; j < amount; j++){
|
|
||||||
byte typeid = stream.readByte();
|
|
||||||
SaveTrait trait = (SaveTrait)TypeTrait.getTypeByID(typeid).get();
|
|
||||||
trait.readSave(stream);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public MappableContent[][] readContentHeader(DataInputStream stream) throws IOException{
|
|
||||||
|
|
||||||
byte mapped = stream.readByte();
|
|
||||||
|
|
||||||
MappableContent[][] map = new MappableContent[ContentType.values().length][0];
|
|
||||||
|
|
||||||
for(int i = 0; i < mapped; i++){
|
|
||||||
ContentType type = ContentType.values()[stream.readByte()];
|
|
||||||
short total = stream.readShort();
|
|
||||||
map[type.ordinal()] = new MappableContent[total];
|
|
||||||
|
|
||||||
for(int j = 0; j < total; j++){
|
|
||||||
String name = stream.readUTF();
|
|
||||||
map[type.ordinal()][j] = content.getByName(type, fallback.get(name, name));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void writeContentHeader(DataOutputStream stream) throws IOException{
|
|
||||||
Array<Content>[] map = content.getContentMap();
|
|
||||||
|
|
||||||
int mappable = 0;
|
|
||||||
for(Array<Content> arr : map){
|
|
||||||
if(arr.size > 0 && arr.first() instanceof MappableContent){
|
|
||||||
mappable++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.writeByte(mappable);
|
|
||||||
for(Array<Content> arr : map){
|
|
||||||
if(arr.size > 0 && arr.first() instanceof MappableContent){
|
|
||||||
stream.writeByte(arr.first().getContentType().ordinal());
|
|
||||||
stream.writeShort(arr.size);
|
|
||||||
for(Content c : arr){
|
|
||||||
stream.writeUTF(((MappableContent)c).name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void read(DataInputStream stream) throws IOException;
|
|
||||||
|
|
||||||
public abstract void write(DataOutputStream stream) throws IOException;
|
|
||||||
}
|
|
||||||
@@ -2,39 +2,46 @@ package io.anuke.mindustry.io;
|
|||||||
|
|
||||||
import io.anuke.arc.collection.*;
|
import io.anuke.arc.collection.*;
|
||||||
import io.anuke.arc.files.FileHandle;
|
import io.anuke.arc.files.FileHandle;
|
||||||
|
import io.anuke.arc.util.io.CounterInputStream;
|
||||||
|
import io.anuke.arc.util.io.FastDeflaterOutputStream;
|
||||||
import io.anuke.mindustry.Vars;
|
import io.anuke.mindustry.Vars;
|
||||||
import io.anuke.mindustry.io.versions.Save1;
|
import io.anuke.mindustry.io.versions.Save1;
|
||||||
|
import io.anuke.mindustry.world.WorldContext;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.zip.DeflaterOutputStream;
|
import java.util.Arrays;
|
||||||
import java.util.zip.InflaterInputStream;
|
import java.util.zip.InflaterInputStream;
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.*;
|
import static io.anuke.mindustry.Vars.*;
|
||||||
|
|
||||||
//TODO load backup meta if possible
|
|
||||||
public class SaveIO{
|
public class SaveIO{
|
||||||
public static final IntArray breakingVersions = IntArray.with(47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 63);
|
/** Format header. This is the string 'MSAV' in ASCII. */
|
||||||
public static final IntMap<SaveFileVersion> versions = new IntMap<>();
|
public static final byte[] header = {77, 83, 65, 86};
|
||||||
public static final Array<SaveFileVersion> versionArray = Array.with(new Save1());
|
public static final IntMap<SaveVersion> versions = new IntMap<>();
|
||||||
|
public static final Array<SaveVersion> versionArray = Array.with(new Save1());
|
||||||
|
|
||||||
static{
|
static{
|
||||||
for(SaveFileVersion version : versionArray){
|
for(SaveVersion version : versionArray){
|
||||||
versions.put(version.version, version);
|
versions.put(version.version, version);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SaveFileVersion getSaveWriter(){
|
public static SaveVersion getSaveWriter(){
|
||||||
return versionArray.peek();
|
return versionArray.peek();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static SaveVersion getSaveWriter(int version){
|
||||||
|
return versions.get(version);
|
||||||
|
}
|
||||||
|
|
||||||
public static void saveToSlot(int slot){
|
public static void saveToSlot(int slot){
|
||||||
FileHandle file = fileFor(slot);
|
FileHandle file = fileFor(slot);
|
||||||
boolean exists = file.exists();
|
boolean exists = file.exists();
|
||||||
if(exists) file.moveTo(file.sibling(file.name() + "-backup." + file.extension()));
|
if(exists) file.moveTo(backupFileFor(file));
|
||||||
try{
|
try{
|
||||||
write(fileFor(slot));
|
write(fileFor(slot));
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
if(exists) file.sibling(file.name() + "-backup." + file.extension()).moveTo(file);
|
if(exists) backupFileFor(file).moveTo(file);
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,22 +54,30 @@ public class SaveIO{
|
|||||||
return new DataInputStream(new InflaterInputStream(fileFor(slot).read(bufferSize)));
|
return new DataInputStream(new InflaterInputStream(fileFor(slot).read(bufferSize)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static DataInputStream getBackupSlotStream(int slot){
|
||||||
|
return new DataInputStream(new InflaterInputStream(backupFileFor(fileFor(slot)).read(bufferSize)));
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isSaveValid(int slot){
|
public static boolean isSaveValid(int slot){
|
||||||
try{
|
try{
|
||||||
return isSaveValid(getSlotStream(slot));
|
getMeta(slot);
|
||||||
|
return true;
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isSaveValid(FileHandle file){
|
public static boolean isSaveValid(FileHandle file){
|
||||||
return isSaveValid(new DataInputStream(new InflaterInputStream(file.read(bufferSize))));
|
try{
|
||||||
|
return isSaveValid(new DataInputStream(new InflaterInputStream(file.read(bufferSize))));
|
||||||
|
}catch(Exception e){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isSaveValid(DataInputStream stream){
|
public static boolean isSaveValid(DataInputStream stream){
|
||||||
|
|
||||||
try{
|
try{
|
||||||
getData(stream);
|
getMeta(stream);
|
||||||
return true;
|
return true;
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
@@ -70,15 +85,20 @@ public class SaveIO{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SaveMeta getData(int slot){
|
public static SaveMeta getMeta(int slot){
|
||||||
return getData(getSlotStream(slot));
|
try{
|
||||||
|
return getMeta(getSlotStream(slot));
|
||||||
|
}catch(Exception e){
|
||||||
|
return getMeta(getBackupSlotStream(slot));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SaveMeta getData(DataInputStream stream){
|
public static SaveMeta getMeta(DataInputStream stream){
|
||||||
|
|
||||||
try{
|
try{
|
||||||
|
readHeader(stream);
|
||||||
int version = stream.readInt();
|
int version = stream.readInt();
|
||||||
SaveMeta meta = versions.get(version).getData(stream);
|
SaveMeta meta = versions.get(version).getMeta(stream);
|
||||||
stream.close();
|
stream.close();
|
||||||
return meta;
|
return meta;
|
||||||
}catch(IOException e){
|
}catch(IOException e){
|
||||||
@@ -90,61 +110,79 @@ public class SaveIO{
|
|||||||
return saveDirectory.child(slot + "." + Vars.saveExtension);
|
return saveDirectory.child(slot + "." + Vars.saveExtension);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void write(FileHandle file){
|
public static FileHandle backupFileFor(FileHandle file){
|
||||||
write(new DeflaterOutputStream(file.write(false, bufferSize)){
|
return file.sibling(file.name() + "-backup." + file.extension());
|
||||||
byte[] tmp = {0};
|
|
||||||
|
|
||||||
public void write(int var1) throws IOException{
|
|
||||||
tmp[0] = (byte)(var1 & 255);
|
|
||||||
this.write(tmp, 0, 1);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void write(OutputStream os){
|
public static void write(FileHandle file, StringMap tags){
|
||||||
DataOutputStream stream;
|
write(new FastDeflaterOutputStream(file.write(false, bufferSize)), tags);
|
||||||
|
}
|
||||||
|
|
||||||
try{
|
public static void write(FileHandle file){
|
||||||
stream = new DataOutputStream(os);
|
write(file, null);
|
||||||
getVersion().write(stream);
|
}
|
||||||
stream.close();
|
|
||||||
|
public static void write(OutputStream os, StringMap tags){
|
||||||
|
try(DataOutputStream stream = new DataOutputStream(os)){
|
||||||
|
stream.write(header);
|
||||||
|
stream.writeInt(getVersion().version);
|
||||||
|
if(tags == null){
|
||||||
|
getVersion().write(stream);
|
||||||
|
}else{
|
||||||
|
getVersion().write(stream, tags);
|
||||||
|
}
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void load(FileHandle file) throws SaveException{
|
public static void load(FileHandle file) throws SaveException{
|
||||||
|
load(file, world.context);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void load(FileHandle file, WorldContext context) throws SaveException{
|
||||||
try{
|
try{
|
||||||
//try and load; if any exception at all occurs
|
//try and load; if any exception at all occurs
|
||||||
load(new InflaterInputStream(file.read(bufferSize)));
|
load(new InflaterInputStream(file.read(bufferSize)), context);
|
||||||
}catch(SaveException e){
|
}catch(SaveException e){
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
FileHandle backup = file.sibling(file.name() + "-backup." + file.extension());
|
FileHandle backup = file.sibling(file.name() + "-backup." + file.extension());
|
||||||
if(backup.exists()){
|
if(backup.exists()){
|
||||||
load(new InflaterInputStream(backup.read(bufferSize)));
|
load(new InflaterInputStream(backup.read(bufferSize)), context);
|
||||||
}else{
|
}else{
|
||||||
throw new SaveException(e.getCause());
|
throw new SaveException(e.getCause());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void load(InputStream is) throws SaveException{
|
/** Loads from a deflated (!) input stream.*/
|
||||||
try(DataInputStream stream = new DataInputStream(is)){
|
public static void load(InputStream is, WorldContext context) throws SaveException{
|
||||||
|
try(CounterInputStream counter = new CounterInputStream(is); DataInputStream stream = new DataInputStream(counter)){
|
||||||
logic.reset();
|
logic.reset();
|
||||||
|
readHeader(stream);
|
||||||
int version = stream.readInt();
|
int version = stream.readInt();
|
||||||
SaveFileVersion ver = versions.get(version);
|
SaveVersion ver = versions.get(version);
|
||||||
|
|
||||||
ver.read(stream);
|
ver.read(stream, counter, context);
|
||||||
}catch(Exception e){
|
}catch(Exception e){
|
||||||
content.setTemporaryMapper(null);
|
|
||||||
throw new SaveException(e);
|
throw new SaveException(e);
|
||||||
|
}finally{
|
||||||
|
content.setTemporaryMapper(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static SaveFileVersion getVersion(){
|
public static SaveVersion getVersion(){
|
||||||
return versionArray.peek();
|
return versionArray.peek();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void readHeader(DataInput input) throws IOException{
|
||||||
|
byte[] bytes = new byte[header.length];
|
||||||
|
input.readFully(bytes);
|
||||||
|
if(!Arrays.equals(bytes, header)){
|
||||||
|
throw new IOException("Incorrect header! Expecting: " + Arrays.toString(header) + "; Actual: " + Arrays.toString(bytes));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static class SaveException extends RuntimeException{
|
public static class SaveException extends RuntimeException{
|
||||||
public SaveException(Throwable throwable){
|
public SaveException(Throwable throwable){
|
||||||
super(throwable);
|
super(throwable);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import io.anuke.mindustry.maps.Map;
|
|||||||
|
|
||||||
import static io.anuke.mindustry.Vars.world;
|
import static io.anuke.mindustry.Vars.world;
|
||||||
|
|
||||||
|
//TODO consider removing and replacing with a raw StringMap
|
||||||
public class SaveMeta{
|
public class SaveMeta{
|
||||||
public int version;
|
public int version;
|
||||||
public int build;
|
public int build;
|
||||||
|
|||||||
279
core/src/io/anuke/mindustry/io/SaveVersion.java
Normal file
279
core/src/io/anuke/mindustry/io/SaveVersion.java
Normal file
@@ -0,0 +1,279 @@
|
|||||||
|
package io.anuke.mindustry.io;
|
||||||
|
|
||||||
|
import io.anuke.arc.collection.Array;
|
||||||
|
import io.anuke.arc.collection.StringMap;
|
||||||
|
import io.anuke.arc.util.Time;
|
||||||
|
import io.anuke.arc.util.io.CounterInputStream;
|
||||||
|
import io.anuke.mindustry.entities.Entities;
|
||||||
|
import io.anuke.mindustry.entities.EntityGroup;
|
||||||
|
import io.anuke.mindustry.entities.traits.*;
|
||||||
|
import io.anuke.mindustry.game.*;
|
||||||
|
import io.anuke.mindustry.maps.Map;
|
||||||
|
import io.anuke.mindustry.type.ContentType;
|
||||||
|
import io.anuke.mindustry.world.*;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
import static io.anuke.mindustry.Vars.*;
|
||||||
|
|
||||||
|
public abstract class SaveVersion extends SaveFileReader{
|
||||||
|
public final int version;
|
||||||
|
|
||||||
|
public SaveVersion(int version){
|
||||||
|
this.version = version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SaveMeta getMeta(DataInput stream) throws IOException{
|
||||||
|
stream.readInt(); //length of data, doesn't matter here
|
||||||
|
StringMap map = readStringMap(stream);
|
||||||
|
return new SaveMeta(map.getInt("version"), map.getLong("saved"), map.getLong("playtime"), map.getInt("build"), map.get("mapname"), map.getInt("wave"), JsonIO.read(Rules.class, map.get("rules", "{}")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void write(DataOutputStream stream) throws IOException{
|
||||||
|
write(stream, new StringMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void read(DataInputStream stream, CounterInputStream counter, WorldContext context) throws IOException{
|
||||||
|
region("meta", stream, counter, this::readMeta);
|
||||||
|
region("content", stream, counter, this::readContentHeader);
|
||||||
|
region("map", stream, counter, in -> readMap(in, context));
|
||||||
|
region("entities", stream, counter, this::readEntities);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void write(DataOutputStream stream, StringMap extraTags) throws IOException{
|
||||||
|
region("meta", stream, out -> writeMeta(out, extraTags));
|
||||||
|
region("content", stream, this::writeContentHeader);
|
||||||
|
region("map", stream, this::writeMap);
|
||||||
|
region("entities", stream, this::writeEntities);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeMeta(DataOutput stream, StringMap tags) throws IOException{
|
||||||
|
writeStringMap(stream, StringMap.of(
|
||||||
|
"saved", Time.millis(),
|
||||||
|
"playtime", headless ? 0 : control.saves.getTotalPlaytime(),
|
||||||
|
"build", Version.build,
|
||||||
|
"mapname", world.getMap() == null ? "unknown" : world.getMap().name(),
|
||||||
|
"wave", state.wave,
|
||||||
|
"wavetime", state.wavetime,
|
||||||
|
"stats", JsonIO.write(state.stats),
|
||||||
|
"rules", JsonIO.write(state.rules),
|
||||||
|
"width", world.width(),
|
||||||
|
"height", world.height()
|
||||||
|
).merge(tags));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readMeta(DataInput stream) throws IOException{
|
||||||
|
StringMap map = readStringMap(stream);
|
||||||
|
|
||||||
|
state.wave = map.getInt("wave");
|
||||||
|
state.wavetime = map.getFloat("wavetime", state.rules.waveSpacing);
|
||||||
|
state.stats = JsonIO.read(Stats.class, map.get("stats", "{}"));
|
||||||
|
state.rules = JsonIO.read(Rules.class, map.get("rules", "{}"));
|
||||||
|
Map worldmap = world.maps.byName(map.get("mapname", "\\\\\\"));
|
||||||
|
world.setMap(worldmap == null ? new Map(StringMap.of(
|
||||||
|
"name", map.get("mapname", "Unknown"),
|
||||||
|
"width", 1,
|
||||||
|
"height", 1
|
||||||
|
)) : worldmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeMap(DataOutput stream) throws IOException{
|
||||||
|
//write world size
|
||||||
|
stream.writeShort(world.width());
|
||||||
|
stream.writeShort(world.height());
|
||||||
|
|
||||||
|
//floor + overlay
|
||||||
|
for(int i = 0; i < world.width() * world.height(); i++){
|
||||||
|
Tile tile = world.tile(i % world.width(), i / world.width());
|
||||||
|
stream.writeShort(tile.floorID());
|
||||||
|
stream.writeShort(tile.overlayID());
|
||||||
|
int consecutives = 0;
|
||||||
|
|
||||||
|
for(int j = i + 1; j < world.width() * world.height() && consecutives < 255; j++){
|
||||||
|
Tile nextTile = world.tile(j % world.width(), j / world.width());
|
||||||
|
|
||||||
|
if(nextTile.floorID() != tile.floorID() || nextTile.overlayID() != tile.overlayID()){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
consecutives++;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.writeByte(consecutives);
|
||||||
|
i += consecutives;
|
||||||
|
}
|
||||||
|
|
||||||
|
//blocks
|
||||||
|
for(int i = 0; i < world.width() * world.height(); i++){
|
||||||
|
Tile tile = world.tile(i % world.width(), i / world.width());
|
||||||
|
stream.writeShort(tile.blockID());
|
||||||
|
|
||||||
|
if(tile.entity != null){
|
||||||
|
writeChunk(stream, true, out -> {
|
||||||
|
out.writeByte(tile.entity.version());
|
||||||
|
tile.entity.write(out);
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
//write consecutive non-entity blocks
|
||||||
|
int consecutives = 0;
|
||||||
|
|
||||||
|
for(int j = i + 1; j < world.width() * world.height() && consecutives < 255; j++){
|
||||||
|
Tile nextTile = world.tile(j % world.width(), j / world.width());
|
||||||
|
|
||||||
|
if(nextTile.blockID() != tile.blockID()){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
consecutives++;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.writeByte(consecutives);
|
||||||
|
i += consecutives;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readMap(DataInput stream, WorldContext context) throws IOException{
|
||||||
|
int width = stream.readUnsignedShort();
|
||||||
|
int height = stream.readUnsignedShort();
|
||||||
|
|
||||||
|
boolean generating = context.isGenerating();
|
||||||
|
|
||||||
|
if(!generating) context.begin();
|
||||||
|
|
||||||
|
context.resize(width, height);
|
||||||
|
|
||||||
|
//read floor and create tiles first
|
||||||
|
for(int i = 0; i < width * height; i++){
|
||||||
|
int x = i % width, y = i / width;
|
||||||
|
short floorid = stream.readShort();
|
||||||
|
short oreid = stream.readShort();
|
||||||
|
int consecutives = stream.readUnsignedByte();
|
||||||
|
|
||||||
|
context.create(x, y, floorid, oreid, (short)0);
|
||||||
|
|
||||||
|
for(int j = i + 1; j < i + 1 + consecutives; j++){
|
||||||
|
int newx = j % width, newy = j / width;
|
||||||
|
context.create(newx, newy, floorid, oreid, (short)0);
|
||||||
|
}
|
||||||
|
|
||||||
|
i += consecutives;
|
||||||
|
}
|
||||||
|
|
||||||
|
//read blocks
|
||||||
|
for(int i = 0; i < width * height; i++){
|
||||||
|
int x = i % width, y = i / width;
|
||||||
|
Block block = content.block(stream.readShort());
|
||||||
|
Tile tile = context.tile(x, y);
|
||||||
|
tile.setBlock(block);
|
||||||
|
|
||||||
|
if(tile.entity != null){
|
||||||
|
readChunk(stream, true, in -> {
|
||||||
|
byte version = in.readByte();
|
||||||
|
tile.entity.read(in, version);
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
int consecutives = stream.readUnsignedByte();
|
||||||
|
|
||||||
|
for(int j = i + 1; j < i + 1 + consecutives; j++){
|
||||||
|
int newx = j % width, newy = j / width;
|
||||||
|
context.tile(newx, newy).setBlock(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
i += consecutives;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
content.setTemporaryMapper(null);
|
||||||
|
if(!generating) context.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeEntities(DataOutput stream) throws IOException{
|
||||||
|
//write entity chunk
|
||||||
|
int groups = 0;
|
||||||
|
|
||||||
|
for(EntityGroup<?> group : Entities.getAllGroups()){
|
||||||
|
if(!group.isEmpty() && group.all().get(0) instanceof SaveTrait){
|
||||||
|
groups++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.writeByte(groups);
|
||||||
|
|
||||||
|
for(EntityGroup<?> group : Entities.getAllGroups()){
|
||||||
|
if(!group.isEmpty() && group.all().get(0) instanceof SaveTrait){
|
||||||
|
stream.writeInt(group.size());
|
||||||
|
for(Entity entity : group.all()){
|
||||||
|
SaveTrait save = (SaveTrait)entity;
|
||||||
|
//each entity is a separate chunk.
|
||||||
|
writeChunk(stream, true, out -> {
|
||||||
|
out.writeByte(save.getTypeID());
|
||||||
|
out.writeByte(save.version());
|
||||||
|
save.writeSave(out);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readEntities(DataInput stream) throws IOException{
|
||||||
|
byte groups = stream.readByte();
|
||||||
|
|
||||||
|
for(int i = 0; i < groups; i++){
|
||||||
|
int amount = stream.readInt();
|
||||||
|
for(int j = 0; j < amount; j++){
|
||||||
|
//TODO throw exception on read fail
|
||||||
|
readChunk(stream, true, in -> {
|
||||||
|
byte typeid = in.readByte();
|
||||||
|
byte version = in.readByte();
|
||||||
|
SaveTrait trait = (SaveTrait)TypeTrait.getTypeByID(typeid).get();
|
||||||
|
trait.readSave(in, version);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void readContentHeader(DataInput stream) throws IOException{
|
||||||
|
|
||||||
|
byte mapped = stream.readByte();
|
||||||
|
|
||||||
|
MappableContent[][] map = new MappableContent[ContentType.values().length][0];
|
||||||
|
|
||||||
|
for(int i = 0; i < mapped; i++){
|
||||||
|
ContentType type = ContentType.values()[stream.readByte()];
|
||||||
|
short total = stream.readShort();
|
||||||
|
map[type.ordinal()] = new MappableContent[total];
|
||||||
|
|
||||||
|
for(int j = 0; j < total; j++){
|
||||||
|
String name = stream.readUTF();
|
||||||
|
map[type.ordinal()][j] = content.getByName(type, fallback.get(name, name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
content.setTemporaryMapper(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void writeContentHeader(DataOutput stream) throws IOException{
|
||||||
|
Array<Content>[] map = content.getContentMap();
|
||||||
|
|
||||||
|
int mappable = 0;
|
||||||
|
for(Array<Content> arr : map){
|
||||||
|
if(arr.size > 0 && arr.first() instanceof MappableContent){
|
||||||
|
mappable++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.writeByte(mappable);
|
||||||
|
for(Array<Content> arr : map){
|
||||||
|
if(arr.size > 0 && arr.first() instanceof MappableContent){
|
||||||
|
stream.writeByte(arr.first().getContentType().ordinal());
|
||||||
|
stream.writeShort(arr.size);
|
||||||
|
for(Content c : arr){
|
||||||
|
stream.writeUTF(((MappableContent)c).name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -109,12 +109,12 @@ public class TypeIO{
|
|||||||
|
|
||||||
@WriteClass(Block.class)
|
@WriteClass(Block.class)
|
||||||
public static void writeBlock(ByteBuffer buffer, Block block){
|
public static void writeBlock(ByteBuffer buffer, Block block){
|
||||||
buffer.put(block.id);
|
buffer.putShort(block.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ReadClass(Block.class)
|
@ReadClass(Block.class)
|
||||||
public static Block readBlock(ByteBuffer buffer){
|
public static Block readBlock(ByteBuffer buffer){
|
||||||
return content.block(buffer.get());
|
return content.block(buffer.getShort());
|
||||||
}
|
}
|
||||||
|
|
||||||
@WriteClass(BuildRequest[].class)
|
@WriteClass(BuildRequest[].class)
|
||||||
@@ -124,7 +124,7 @@ public class TypeIO{
|
|||||||
buffer.put(request.breaking ? (byte)1 : 0);
|
buffer.put(request.breaking ? (byte)1 : 0);
|
||||||
buffer.putInt(Pos.get(request.x, request.y));
|
buffer.putInt(Pos.get(request.x, request.y));
|
||||||
if(!request.breaking){
|
if(!request.breaking){
|
||||||
buffer.put(request.block.id);
|
buffer.putShort(request.block.id);
|
||||||
buffer.put((byte)request.rotation);
|
buffer.put((byte)request.rotation);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -142,7 +142,7 @@ public class TypeIO{
|
|||||||
if(type == 1){ //remove
|
if(type == 1){ //remove
|
||||||
currentRequest = new BuildRequest(Pos.x(position), Pos.y(position));
|
currentRequest = new BuildRequest(Pos.x(position), Pos.y(position));
|
||||||
}else{ //place
|
}else{ //place
|
||||||
byte block = buffer.get();
|
short block = buffer.getShort();
|
||||||
byte rotation = buffer.get();
|
byte rotation = buffer.get();
|
||||||
currentRequest = new BuildRequest(Pos.x(position), Pos.y(position), rotation, content.block(block));
|
currentRequest = new BuildRequest(Pos.x(position), Pos.y(position), rotation, content.block(block));
|
||||||
}
|
}
|
||||||
@@ -205,7 +205,7 @@ public class TypeIO{
|
|||||||
|
|
||||||
@WriteClass(Mech.class)
|
@WriteClass(Mech.class)
|
||||||
public static void writeMech(ByteBuffer buffer, Mech mech){
|
public static void writeMech(ByteBuffer buffer, Mech mech){
|
||||||
buffer.put(mech.id);
|
buffer.put((byte)mech.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ReadClass(Mech.class)
|
@ReadClass(Mech.class)
|
||||||
@@ -215,33 +215,33 @@ public class TypeIO{
|
|||||||
|
|
||||||
@WriteClass(Liquid.class)
|
@WriteClass(Liquid.class)
|
||||||
public static void writeLiquid(ByteBuffer buffer, Liquid liquid){
|
public static void writeLiquid(ByteBuffer buffer, Liquid liquid){
|
||||||
buffer.put(liquid == null ? -1 : liquid.id);
|
buffer.putShort(liquid == null ? -1 : liquid.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ReadClass(Liquid.class)
|
@ReadClass(Liquid.class)
|
||||||
public static Liquid readLiquid(ByteBuffer buffer){
|
public static Liquid readLiquid(ByteBuffer buffer){
|
||||||
byte id = buffer.get();
|
short id = buffer.getShort();
|
||||||
return id == -1 ? null : content.liquid(buffer.get());
|
return id == -1 ? null : content.liquid(buffer.getShort());
|
||||||
}
|
}
|
||||||
|
|
||||||
@WriteClass(BulletType.class)
|
@WriteClass(BulletType.class)
|
||||||
public static void writeBulletType(ByteBuffer buffer, BulletType type){
|
public static void writeBulletType(ByteBuffer buffer, BulletType type){
|
||||||
buffer.put(type.id);
|
buffer.putShort(type.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ReadClass(BulletType.class)
|
@ReadClass(BulletType.class)
|
||||||
public static BulletType readBulletType(ByteBuffer buffer){
|
public static BulletType readBulletType(ByteBuffer buffer){
|
||||||
return content.getByID(ContentType.bullet, buffer.get());
|
return content.getByID(ContentType.bullet, buffer.getShort());
|
||||||
}
|
}
|
||||||
|
|
||||||
@WriteClass(Item.class)
|
@WriteClass(Item.class)
|
||||||
public static void writeItem(ByteBuffer buffer, Item item){
|
public static void writeItem(ByteBuffer buffer, Item item){
|
||||||
buffer.put(item == null ? -1 : item.id);
|
buffer.putShort(item == null ? -1 : item.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ReadClass(Item.class)
|
@ReadClass(Item.class)
|
||||||
public static Item readItem(ByteBuffer buffer){
|
public static Item readItem(ByteBuffer buffer){
|
||||||
byte id = buffer.get();
|
short id = buffer.getShort();
|
||||||
return id == -1 ? null : content.item(id);
|
return id == -1 ? null : content.item(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,82 +1,10 @@
|
|||||||
package io.anuke.mindustry.io.versions;
|
package io.anuke.mindustry.io.versions;
|
||||||
|
|
||||||
import io.anuke.arc.collection.ObjectMap;
|
import io.anuke.mindustry.io.SaveVersion;
|
||||||
import io.anuke.arc.util.Time;
|
|
||||||
import io.anuke.mindustry.game.*;
|
|
||||||
import io.anuke.mindustry.gen.Serialization;
|
|
||||||
import io.anuke.mindustry.io.SaveFileVersion;
|
|
||||||
import io.anuke.mindustry.maps.Map;
|
|
||||||
import io.anuke.mindustry.type.ContentType;
|
|
||||||
import io.anuke.mindustry.type.Zone;
|
|
||||||
|
|
||||||
import java.io.*;
|
public class Save1 extends SaveVersion{
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.*;
|
|
||||||
|
|
||||||
public class Save1 extends SaveFileVersion{
|
|
||||||
|
|
||||||
public Save1(){
|
public Save1(){
|
||||||
super(1);
|
super(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void read(DataInputStream stream) throws IOException{
|
|
||||||
stream.readLong(); //time
|
|
||||||
stream.readLong(); //total playtime
|
|
||||||
stream.readInt(); //build
|
|
||||||
|
|
||||||
//general state
|
|
||||||
state.rules = Serialization.readRulesStreamJson(stream);
|
|
||||||
String mapname = stream.readUTF();
|
|
||||||
Map map = world.maps.all().find(m -> m.name().equals(mapname));
|
|
||||||
if(map == null) map = new Map(customMapDirectory.child(mapname), 1, 1, new ObjectMap<>(), true);
|
|
||||||
world.setMap(map);
|
|
||||||
state.rules.spawns = map.getWaves();
|
|
||||||
if(content.getByID(ContentType.zone, state.rules.zone) != null){
|
|
||||||
Rules rules = content.<Zone>getByID(ContentType.zone, state.rules.zone).rules.get();
|
|
||||||
if(rules.spawns != DefaultWaves.get()){
|
|
||||||
state.rules.spawns = rules.spawns;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int wave = stream.readInt();
|
|
||||||
float wavetime = stream.readFloat();
|
|
||||||
|
|
||||||
state.wave = wave;
|
|
||||||
state.wavetime = wavetime;
|
|
||||||
state.stats = Serialization.readStats(stream);
|
|
||||||
world.spawner.read(stream);
|
|
||||||
|
|
||||||
content.setTemporaryMapper(readContentHeader(stream));
|
|
||||||
|
|
||||||
readEntities(stream);
|
|
||||||
readMap(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void write(DataOutputStream stream) throws IOException{
|
|
||||||
//--META--
|
|
||||||
stream.writeInt(version); //version id
|
|
||||||
stream.writeLong(Time.millis()); //last saved
|
|
||||||
stream.writeLong(headless ? 0 : control.saves.getTotalPlaytime()); //playtime
|
|
||||||
stream.writeInt(Version.build); //build
|
|
||||||
|
|
||||||
//--GENERAL STATE--
|
|
||||||
Serialization.writeRulesStreamJson(stream, state.rules);
|
|
||||||
stream.writeUTF(world.getMap().name()); //map name
|
|
||||||
|
|
||||||
stream.writeInt(state.wave); //wave
|
|
||||||
stream.writeFloat(state.wavetime); //wave countdown
|
|
||||||
|
|
||||||
Serialization.writeStats(stream, state.stats);
|
|
||||||
world.spawner.write(stream);
|
|
||||||
|
|
||||||
writeContentHeader(stream);
|
|
||||||
|
|
||||||
//--ENTITIES--
|
|
||||||
|
|
||||||
writeEntities(stream);
|
|
||||||
|
|
||||||
writeMap(stream);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +1,18 @@
|
|||||||
package io.anuke.mindustry.maps;
|
package io.anuke.mindustry.maps;
|
||||||
|
|
||||||
import io.anuke.arc.Core;
|
import io.anuke.arc.Core;
|
||||||
import io.anuke.arc.collection.Array;
|
import io.anuke.arc.collection.StringMap;
|
||||||
import io.anuke.arc.collection.ObjectMap;
|
|
||||||
import io.anuke.arc.files.FileHandle;
|
import io.anuke.arc.files.FileHandle;
|
||||||
import io.anuke.arc.graphics.Texture;
|
import io.anuke.arc.graphics.Texture;
|
||||||
import io.anuke.arc.util.Log;
|
|
||||||
import io.anuke.mindustry.Vars;
|
import io.anuke.mindustry.Vars;
|
||||||
import io.anuke.mindustry.game.DefaultWaves;
|
import io.anuke.mindustry.game.Rules;
|
||||||
import io.anuke.mindustry.game.SpawnGroup;
|
import io.anuke.mindustry.io.JsonIO;
|
||||||
import io.anuke.mindustry.io.MapIO;
|
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.world;
|
|
||||||
|
|
||||||
public class Map implements Comparable<Map>{
|
public class Map implements Comparable<Map>{
|
||||||
/** Whether this is a custom map. */
|
/** Whether this is a custom map. */
|
||||||
public final boolean custom;
|
public final boolean custom;
|
||||||
/** Metadata. Author description, display name, etc. */
|
/** Metadata. Author description, display name, etc. */
|
||||||
public final ObjectMap<String, String> tags;
|
public final StringMap tags;
|
||||||
/** Base file of this map. File can be named anything at all. */
|
/** Base file of this map. File can be named anything at all. */
|
||||||
public final FileHandle file;
|
public final FileHandle file;
|
||||||
/** Format version. */
|
/** Format version. */
|
||||||
@@ -29,7 +24,7 @@ public class Map implements Comparable<Map>{
|
|||||||
/** Build that this map was created in. -1 = unknown or custom build. */
|
/** Build that this map was created in. -1 = unknown or custom build. */
|
||||||
public int build;
|
public int build;
|
||||||
|
|
||||||
public Map(FileHandle file, int width, int height, ObjectMap<String, String> tags, boolean custom, int version, int build){
|
public Map(FileHandle file, int width, int height, StringMap tags, boolean custom, int version, int build){
|
||||||
this.custom = custom;
|
this.custom = custom;
|
||||||
this.tags = tags;
|
this.tags = tags;
|
||||||
this.file = file;
|
this.file = file;
|
||||||
@@ -39,26 +34,16 @@ public class Map implements Comparable<Map>{
|
|||||||
this.build = build;
|
this.build = build;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map(FileHandle file, int width, int height, ObjectMap<String, String> tags, boolean custom, int version){
|
public Map(FileHandle file, int width, int height, StringMap tags, boolean custom, int version){
|
||||||
this(file, width, height, tags, custom, version, -1);
|
this(file, width, height, tags, custom, version, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map(FileHandle file, int width, int height, ObjectMap<String, String> tags, boolean custom){
|
public Map(FileHandle file, int width, int height, StringMap tags, boolean custom){
|
||||||
this(file, width, height, tags, custom, MapIO.version);
|
this(file, width, height, tags, custom, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Array<SpawnGroup> getWaves(){
|
public Map(StringMap tags){
|
||||||
if(tags.containsKey("waves")){
|
this(Vars.customMapDirectory.child(tags.get("name", "unknown")), 0, 0, tags, true);
|
||||||
try{
|
|
||||||
return world.maps.readWaves(tags.get("waves"));
|
|
||||||
}catch(Exception e){
|
|
||||||
Log.err("Malformed waves: {0}", tags.get("waves"));
|
|
||||||
e.printStackTrace();
|
|
||||||
return DefaultWaves.get();
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
return DefaultWaves.get();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getHightScore(){
|
public int getHightScore(){
|
||||||
@@ -70,6 +55,23 @@ public class Map implements Comparable<Map>{
|
|||||||
Vars.data.modified();
|
Vars.data.modified();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** This creates a new instance.*/
|
||||||
|
public Rules rules(){
|
||||||
|
return JsonIO.read(Rules.class, tags.get("rules", "{}"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Whether this map has a core of the enemy 'wave' team. Default: true.
|
||||||
|
* Used for checking Attack mode validity.*/
|
||||||
|
public boolean hasEnemyCore(){
|
||||||
|
return tags.get("enemycore", "true").equals("true");
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Whether this map has a core of any team except the default player team. Default: true.
|
||||||
|
* Used for checking PvP mode validity.*/
|
||||||
|
public boolean hasOtherCores(){
|
||||||
|
return tags.get("othercore", "true").equals("true");
|
||||||
|
}
|
||||||
|
|
||||||
public String author(){
|
public String author(){
|
||||||
return tag("author");
|
return tag("author");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,14 @@
|
|||||||
package io.anuke.mindustry.maps;
|
package io.anuke.mindustry.maps;
|
||||||
|
|
||||||
import io.anuke.arc.Core;
|
import io.anuke.arc.Core;
|
||||||
import io.anuke.arc.collection.Array;
|
import io.anuke.arc.collection.*;
|
||||||
import io.anuke.arc.collection.ObjectMap;
|
|
||||||
import io.anuke.arc.files.FileHandle;
|
import io.anuke.arc.files.FileHandle;
|
||||||
import io.anuke.arc.graphics.Texture;
|
import io.anuke.arc.graphics.Texture;
|
||||||
import io.anuke.arc.util.Disposable;
|
import io.anuke.arc.util.Disposable;
|
||||||
import io.anuke.arc.util.Log;
|
import io.anuke.arc.util.Log;
|
||||||
import io.anuke.arc.util.serialization.Json;
|
import io.anuke.arc.util.serialization.Json;
|
||||||
import io.anuke.mindustry.game.SpawnGroup;
|
import io.anuke.mindustry.game.SpawnGroup;
|
||||||
import io.anuke.mindustry.io.MapIO;
|
import io.anuke.mindustry.io.*;
|
||||||
import io.anuke.mindustry.world.Tile;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.StringWriter;
|
import java.io.StringWriter;
|
||||||
@@ -52,7 +50,7 @@ public class Maps implements Disposable{
|
|||||||
FileHandle file = Core.files.internal("maps/" + name + "." + mapExtension);
|
FileHandle file = Core.files.internal("maps/" + name + "." + mapExtension);
|
||||||
|
|
||||||
try{
|
try{
|
||||||
return MapIO.readMap(file, false);
|
return MapIO.createMap(file, false);
|
||||||
}catch(IOException e){
|
}catch(IOException e){
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
@@ -81,13 +79,12 @@ public class Maps implements Disposable{
|
|||||||
* Save a custom map to the directory. This updates all values and stored data necessary.
|
* Save a custom map to the directory. This updates all values and stored data necessary.
|
||||||
* The tags are copied to prevent mutation later.
|
* The tags are copied to prevent mutation later.
|
||||||
*/
|
*/
|
||||||
public void saveMap(ObjectMap<String, String> baseTags, Tile[][] data){
|
public void saveMap(ObjectMap<String, String> baseTags){
|
||||||
|
|
||||||
try{
|
try{
|
||||||
ObjectMap<String, String> tags = new ObjectMap<>(baseTags);
|
StringMap tags = new StringMap(baseTags);
|
||||||
String name = tags.get("name");
|
String name = tags.get("name");
|
||||||
if(name == null) throw new IllegalArgumentException("Can't save a map with no name. How did this happen?");
|
if(name == null) throw new IllegalArgumentException("Can't save a map with no name. How did this happen?");
|
||||||
//FileHandle file = customMapDirectory.child(name + "." + mapExtension);
|
|
||||||
FileHandle file;
|
FileHandle file;
|
||||||
|
|
||||||
//find map with the same exact display name
|
//find map with the same exact display name
|
||||||
@@ -106,11 +103,11 @@ public class Maps implements Disposable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
//create map, write it, etc etc etc
|
//create map, write it, etc etc etc
|
||||||
Map map = new Map(file, data.length, data[0].length, tags, true);
|
Map map = new Map(file, world.width(), world.height(), tags, true);
|
||||||
MapIO.writeMap(file, map, data);
|
MapIO.writeMap(file, map);
|
||||||
|
|
||||||
if(!headless){
|
if(!headless){
|
||||||
map.texture = new Texture(MapIO.generatePreview(data));
|
map.texture = new Texture(MapIO.generatePreview(world.getTiles()));
|
||||||
}
|
}
|
||||||
maps.add(map);
|
maps.add(map);
|
||||||
maps.sort();
|
maps.sort();
|
||||||
@@ -160,6 +157,34 @@ public class Maps implements Disposable{
|
|||||||
return str == null ? null : str.equals("[]") ? new Array<>() : Array.with(json.fromJson(SpawnGroup[].class, str));
|
return str == null ? null : str.equals("[]") ? new Array<>() : Array.with(json.fromJson(SpawnGroup[].class, str));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void loadLegacyMaps(){
|
||||||
|
boolean convertedAny = false;
|
||||||
|
for(FileHandle file : customMapDirectory.list()){
|
||||||
|
if(file.extension().equalsIgnoreCase(oldMapExtension)){
|
||||||
|
try{
|
||||||
|
LegacyMapIO.convertMap(file, file.sibling(file.nameWithoutExtension() + "." + mapExtension));
|
||||||
|
//delete old, converted file; it is no longer useful
|
||||||
|
file.delete();
|
||||||
|
convertedAny = true;
|
||||||
|
Log.info("Converted file {0}", file);
|
||||||
|
}catch(IOException e){
|
||||||
|
//rename the file to a 'mmap_conversion_failed' extension to keep it there just in case
|
||||||
|
//but don't delete it
|
||||||
|
file.copyTo(file.sibling(file.name() + "_conversion_failed"));
|
||||||
|
file.delete();
|
||||||
|
Log.err(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//free up any potential memory that was used up during conversion
|
||||||
|
if(convertedAny){
|
||||||
|
world.createTiles(1, 1);
|
||||||
|
//reload maps to load the converted ones
|
||||||
|
reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Find a new filename to put a map to. */
|
/** Find a new filename to put a map to. */
|
||||||
private FileHandle findFile(){
|
private FileHandle findFile(){
|
||||||
//find a map name that isn't used.
|
//find a map name that isn't used.
|
||||||
@@ -171,7 +196,7 @@ public class Maps implements Disposable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void loadMap(FileHandle file, boolean custom) throws IOException{
|
private void loadMap(FileHandle file, boolean custom) throws IOException{
|
||||||
Map map = MapIO.readMap(file, custom);
|
Map map = MapIO.createMap(file, custom);
|
||||||
|
|
||||||
if(map.name() == null){
|
if(map.name() == null){
|
||||||
throw new IOException("Map name cannot be empty! File: " + file);
|
throw new IOException("Map name cannot be empty! File: " + file);
|
||||||
|
|||||||
@@ -156,8 +156,7 @@ public abstract class BasicGenerator extends RandomGenerator{
|
|||||||
block = tiles[x][y].block();
|
block = tiles[x][y].block();
|
||||||
ore = tiles[x][y].overlay();
|
ore = tiles[x][y].overlay();
|
||||||
r.accept(x, y);
|
r.accept(x, y);
|
||||||
tiles[x][y] = new Tile(x, y, floor.id, block.id);
|
tiles[x][y] = new Tile(x, y, floor.id, ore.id, block.id);
|
||||||
tiles[x][y].setOverlay(ore);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -200,7 +199,7 @@ public abstract class BasicGenerator extends RandomGenerator{
|
|||||||
Tile child = tiles[newx][newy];
|
Tile child = tiles[newx][newy];
|
||||||
if(!closed.get(child.x, child.y)){
|
if(!closed.get(child.x, child.y)){
|
||||||
closed.set(child.x, child.y);
|
closed.set(child.x, child.y);
|
||||||
child.setRotation(child.relativeTo(next.x, next.y));
|
child.rotation(child.relativeTo(next.x, next.y));
|
||||||
costs.put(child.pos(), th.cost(child) + baseCost);
|
costs.put(child.pos(), th.cost(child) + baseCost);
|
||||||
queue.add(child);
|
queue.add(child);
|
||||||
}
|
}
|
||||||
@@ -215,7 +214,7 @@ public abstract class BasicGenerator extends RandomGenerator{
|
|||||||
Tile current = end;
|
Tile current = end;
|
||||||
while(current != start){
|
while(current != start){
|
||||||
out.add(current);
|
out.add(current);
|
||||||
Point2 p = Geometry.d4(current.getRotation());
|
Point2 p = Geometry.d4(current.rotation());
|
||||||
current = tiles[current.x + p.x][current.y + p.y];
|
current = tiles[current.x + p.x][current.y + p.y];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,13 +12,10 @@ import io.anuke.mindustry.type.Item;
|
|||||||
import io.anuke.mindustry.type.Loadout;
|
import io.anuke.mindustry.type.Loadout;
|
||||||
import io.anuke.mindustry.world.Block;
|
import io.anuke.mindustry.world.Block;
|
||||||
import io.anuke.mindustry.world.Tile;
|
import io.anuke.mindustry.world.Tile;
|
||||||
import io.anuke.mindustry.world.blocks.Floor;
|
import io.anuke.mindustry.world.blocks.*;
|
||||||
import io.anuke.mindustry.world.blocks.StaticWall;
|
|
||||||
import io.anuke.mindustry.world.blocks.storage.CoreBlock;
|
import io.anuke.mindustry.world.blocks.storage.CoreBlock;
|
||||||
import io.anuke.mindustry.world.blocks.storage.StorageBlock;
|
import io.anuke.mindustry.world.blocks.storage.StorageBlock;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import static io.anuke.mindustry.Vars.world;
|
import static io.anuke.mindustry.Vars.world;
|
||||||
|
|
||||||
public class MapGenerator extends Generator{
|
public class MapGenerator extends Generator{
|
||||||
@@ -70,115 +67,116 @@ public class MapGenerator extends Generator{
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void generate(Tile[][] tiles){
|
public void generate(Tile[][] tiles){
|
||||||
try{
|
for(int x = 0; x < width; x++){
|
||||||
for(int x = 0; x < width; x++){
|
for(int y = 0; y < height; y++){
|
||||||
for(int y = 0; y < height; y++){
|
tiles[x][y] = new Tile(x, y);
|
||||||
tiles[x][y] = new Tile(x, y);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO this will probably not get the desired effect
|
||||||
|
MapIO.loadMap(map);
|
||||||
|
Array<Point2> players = new Array<>();
|
||||||
|
Array<Point2> enemies = new Array<>();
|
||||||
|
|
||||||
|
for(int x = 0; x < width; x++){
|
||||||
|
for(int y = 0; y < height; y++){
|
||||||
|
if(tiles[x][y].block() instanceof CoreBlock){
|
||||||
|
players.add(new Point2(x, y));
|
||||||
|
tiles[x][y].setBlock(Blocks.air);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(tiles[x][y].overlay() == Blocks.spawn && enemySpawns != -1){
|
||||||
|
enemies.add(new Point2(x, y));
|
||||||
|
tiles[x][y].setOverlay(Blocks.air);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(tiles[x][y].block() instanceof BlockPart){
|
||||||
|
tiles[x][y].setBlock(Blocks.air);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MapIO.readTiles(map, tiles);
|
Simplex simplex = new Simplex(Mathf.random(99999));
|
||||||
Array<Point2> players = new Array<>();
|
|
||||||
Array<Point2> enemies = new Array<>();
|
|
||||||
|
|
||||||
for(int x = 0; x < width; x++){
|
for(int x = 0; x < width; x++){
|
||||||
for(int y = 0; y < height; y++){
|
for(int y = 0; y < height; y++){
|
||||||
if(tiles[x][y].block() instanceof CoreBlock){
|
final double scl = 10;
|
||||||
players.add(new Point2(x, y));
|
Tile tile = tiles[x][y];
|
||||||
tiles[x][y].setBlock(Blocks.air);
|
int newX = Mathf.clamp((int)(simplex.octaveNoise2D(1, 1, 1.0 / scl, x, y) * distortion + x), 0, width - 1);
|
||||||
}
|
int newY = Mathf.clamp((int)(simplex.octaveNoise2D(1, 1, 1.0 / scl, x + 9999, y + 9999) * distortion + y), 0, height - 1);
|
||||||
|
|
||||||
if(tiles[x][y].block() == Blocks.spawn && enemySpawns != -1){
|
if(((tile.block() instanceof StaticWall
|
||||||
enemies.add(new Point2(x, y));
|
&& tiles[newX][newY].block() instanceof StaticWall)
|
||||||
tiles[x][y].setBlock(Blocks.air);
|
|| (tile.block() == Blocks.air && !tiles[newX][newY].block().synthetic())
|
||||||
}
|
|| (tiles[newX][newY].block() == Blocks.air && tile.block() instanceof StaticWall))){
|
||||||
|
tile.setBlock(tiles[newX][newY].block());
|
||||||
if(tiles[x][y].block() == Blocks.part){
|
|
||||||
tiles[x][y].setBlock(Blocks.air);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Simplex simplex = new Simplex(Mathf.random(99999));
|
if(distortFloor){
|
||||||
|
tile.setFloor(tiles[newX][newY].floor());
|
||||||
for(int x = 0; x < width; x++){
|
if(tiles[newX][newY].overlay() != Blocks.spawn && tile.overlay() != Blocks.spawn){
|
||||||
for(int y = 0; y < height; y++){
|
|
||||||
final double scl = 10;
|
|
||||||
Tile tile = tiles[x][y];
|
|
||||||
int newX = Mathf.clamp((int)(simplex.octaveNoise2D(1, 1, 1.0 / scl, x, y) * distortion + x), 0, width - 1);
|
|
||||||
int newY = Mathf.clamp((int)(simplex.octaveNoise2D(1, 1, 1.0 / scl, x + 9999, y + 9999) * distortion + y), 0, height - 1);
|
|
||||||
|
|
||||||
if(((tile.block() instanceof StaticWall
|
|
||||||
&& tiles[newX][newY].block() instanceof StaticWall)
|
|
||||||
|| (tile.block() == Blocks.air && !tiles[newX][newY].block().synthetic())
|
|
||||||
|| (tiles[newX][newY].block() == Blocks.air && tile.block() instanceof StaticWall)) && tiles[newX][newY].block() != Blocks.spawn && tile.block() != Blocks.spawn){
|
|
||||||
tile.setBlock(tiles[newX][newY].block());
|
|
||||||
}
|
|
||||||
|
|
||||||
if(distortFloor){
|
|
||||||
tile.setFloor(tiles[newX][newY].floor());
|
|
||||||
tile.setOverlay(tiles[newX][newY].overlay());
|
tile.setOverlay(tiles[newX][newY].overlay());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for(Decoration decor : decorations){
|
for(Decoration decor : decorations){
|
||||||
if(x > 0 && y > 0 && (tiles[x - 1][y].block() == decor.wall || tiles[x][y - 1].block() == decor.wall)){
|
if(x > 0 && y > 0 && (tiles[x - 1][y].block() == decor.wall || tiles[x][y - 1].block() == decor.wall)){
|
||||||
continue;
|
continue;
|
||||||
}
|
|
||||||
|
|
||||||
if(tile.block() == Blocks.air && !(decor.wall instanceof Floor) && tile.floor() == decor.floor && Mathf.chance(decor.chance)){
|
|
||||||
tile.setBlock(decor.wall);
|
|
||||||
}else if(tile.floor() == decor.floor && decor.wall instanceof Floor && Mathf.chance(decor.chance)){
|
|
||||||
tile.setFloor((Floor)decor.wall);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(tile.block() instanceof StorageBlock && !(tile.block() instanceof CoreBlock) && world.getZone() != null){
|
if(tile.block() == Blocks.air && !(decor.wall instanceof Floor) && tile.floor() == decor.floor && Mathf.chance(decor.chance)){
|
||||||
for(Item item : world.getZone().resources){
|
tile.setBlock(decor.wall);
|
||||||
if(Mathf.chance(0.3)){
|
}else if(tile.floor() == decor.floor && decor.wall instanceof Floor && Mathf.chance(decor.chance)){
|
||||||
tile.entity.items.add(item, Math.min(Mathf.random(500), tile.block().itemCapacity));
|
tile.setFloor((Floor)decor.wall);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(tile.block() instanceof StorageBlock && !(tile.block() instanceof CoreBlock) && world.getZone() != null){
|
||||||
|
for(Item item : world.getZone().resources){
|
||||||
|
if(Mathf.chance(0.3)){
|
||||||
|
tile.entity.items.add(item, Math.min(Mathf.random(500), tile.block().itemCapacity));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(enemySpawns != -1){
|
if(enemySpawns != -1){
|
||||||
if(enemySpawns > enemies.size){
|
if(enemySpawns > enemies.size){
|
||||||
throw new IllegalArgumentException("Enemy spawn pool greater than map spawn number for map: " + mapName);
|
throw new IllegalArgumentException("Enemy spawn pool greater than map spawn number for map: " + mapName);
|
||||||
}
|
}
|
||||||
|
|
||||||
enemies.shuffle();
|
enemies.shuffle();
|
||||||
for(int i = 0; i < enemySpawns; i++){
|
for(int i = 0; i < enemySpawns; i++){
|
||||||
Point2 point = enemies.get(i);
|
Point2 point = enemies.get(i);
|
||||||
tiles[point.x][point.y].setBlock(Blocks.spawn);
|
tiles[point.x][point.y].setOverlay(Blocks.spawn);
|
||||||
|
|
||||||
int rad = 10, frad = 12;
|
int rad = 10, frad = 12;
|
||||||
|
|
||||||
for(int x = -rad; x <= rad; x++){
|
for(int x = -rad; x <= rad; x++){
|
||||||
for(int y = -rad; y <= rad; y++){
|
for(int y = -rad; y <= rad; y++){
|
||||||
int wx = x + point.x, wy = y + point.y;
|
int wx = x + point.x, wy = y + point.y;
|
||||||
double dst = Mathf.dst(x, y);
|
double dst = Mathf.dst(x, y);
|
||||||
if(dst < frad && Structs.inBounds(wx, wy, tiles) && (dst <= rad || Mathf.chance(0.5))){
|
if(dst < frad && Structs.inBounds(wx, wy, tiles) && (dst <= rad || Mathf.chance(0.5))){
|
||||||
Tile tile = tiles[wx][wy];
|
Tile tile = tiles[wx][wy];
|
||||||
|
if(tile.overlay() != Blocks.spawn){
|
||||||
tile.clearOverlay();
|
tile.clearOverlay();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Point2 core = players.random();
|
|
||||||
if(core == null){
|
|
||||||
throw new IllegalArgumentException("All zone maps must have a core.");
|
|
||||||
}
|
|
||||||
|
|
||||||
loadout.setup(core.x, core.y);
|
|
||||||
|
|
||||||
world.prepareTiles(tiles);
|
|
||||||
world.setMap(map);
|
|
||||||
}catch(IOException e){
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Point2 core = players.random();
|
||||||
|
if(core == null){
|
||||||
|
throw new IllegalArgumentException("All zone maps must have a core.");
|
||||||
|
}
|
||||||
|
|
||||||
|
loadout.setup(core.x, core.y);
|
||||||
|
|
||||||
|
world.prepareTiles(tiles);
|
||||||
|
world.setMap(map);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Decoration{
|
public static class Decoration{
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user