This commit is contained in:
Anuken
2019-05-21 13:19:27 -04:00
193 changed files with 3330 additions and 2917 deletions

View File

@@ -4,12 +4,27 @@ import java.lang.annotation.*;
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)
public @interface Nullable{
}
/** Indicates that a method return or field cannot be null.*/
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.SOURCE)
public @interface NonNull{

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -13,9 +13,7 @@ import java.util.List;
import java.util.Set;
@SupportedSourceVersion(SourceVersion.RELEASE_8)
@SupportedAnnotationTypes({
"io.anuke.annotations.Annotations.Serialize"
})
@SupportedAnnotationTypes("io.anuke.annotations.Annotations.Serialize")
public class SerializeAnnotationProcessor extends AbstractProcessor{
/** Target class name. */
private static final String className = "Serialization";
@@ -44,20 +42,10 @@ public class SerializeAnnotationProcessor extends AbstractProcessor{
TypeSpec.Builder classBuilder = TypeSpec.classBuilder(className).addModifiers(Modifier.PUBLIC);
classBuilder.addAnnotation(AnnotationSpec.builder(SuppressWarnings.class).addMember("value", "\"unchecked\"").build());
classBuilder.addJavadoc(RemoteMethodAnnotationProcessor.autogenWarning);
MethodSpec.Builder method = MethodSpec.methodBuilder("init").addModifiers(Modifier.PUBLIC, Modifier.STATIC);
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){
TypeName type = TypeName.get(elem.asType());
String simpleTypeName = type.toString().substring(type.toString().lastIndexOf('.') + 1);
@@ -79,19 +67,7 @@ public class SerializeAnnotationProcessor extends AbstractProcessor{
.addException(IOException.class)
.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);
jsonReadMethod.addStatement("$L object = new $L()", type, type);
List<VariableElement> fields = ElementFilter.fieldsIn(Utils.elementUtils.getAllMembers(elem));
for(VariableElement field : fields){
@@ -105,9 +81,6 @@ public class SerializeAnnotationProcessor extends AbstractProcessor{
if(field.asType().getKind().isPrimitive()){
writeMethod.addStatement("stream.write" + capName + "(object." + name + ")");
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{
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)");
@@ -115,7 +88,6 @@ public class SerializeAnnotationProcessor extends AbstractProcessor{
}
readMethod.addStatement("return object");
jsonReadMethod.addStatement("return object");
serializer.addMethod(writeMethod.build());
serializer.addMethod(readMethod.build());
@@ -130,32 +102,6 @@ public class SerializeAnnotationProcessor extends AbstractProcessor{
classBuilder.addMethod(writeMethod.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());

View File

@@ -1,3 +1,4 @@
io.anuke.annotations.RemoteMethodAnnotationProcessor
io.anuke.annotations.SerializeAnnotationProcessor
io.anuke.annotations.StructAnnotationProcessor
io.anuke.annotations.CallSuperAnnotationProcessor

View File

@@ -29,7 +29,7 @@ allprojects{
arcHash = null
debugged = {
return new File(projectDir.parent, '../debug').exists() && !project.hasProperty("release")
return new File(projectDir.parent, '../debug').exists() && !project.hasProperty("release") && project.hasProperty("args")
}
localArc = {
@@ -177,6 +177,31 @@ project(":core"){
}
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("extensions:freetype")
compile arcModule("extensions:arcnet")
@@ -227,6 +252,7 @@ project(":annotations"){
dependencies{
compile 'com.squareup:javapoet:1.11.0'
compile files("${System.getProperty('java.home')}/../lib/tools.jar")
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@@ -89,13 +89,8 @@ trace = Trace Player
trace.playername = Player name: [accent]{0}
trace.ip = IP: [accent]{0}
trace.id = Unique ID: [accent]{0}
trace.android = Android Client: [accent]{0}
trace.mobile = Mobile Client: [accent]{0}
trace.modclient = Custom Client: [accent]{0}
trace.totalblocksbroken = Total blocks broken: [accent]{0}
trace.structureblocksbroken = Structure blocks broken: [accent]{0}
trace.lastblockbroken = Last block broken: [accent]{0}
trace.totalblocksplaced = Total blocks placed: [accent]{0}
trace.lastblockplaced = Last block placed: [accent]{0}
invalidid = Invalid client ID! Submit a bug report.
server.bans = Bans
server.bans.none = No banned players found!
@@ -199,6 +194,8 @@ editor.mapinfo = Map Info
editor.author = Author:
editor.description = Description:
editor.waves = Waves:
editor.rules = Rules:
editor.ingame = Edit In-Game
waves.title = Waves
waves.remove = Remove
waves.never = <never>
@@ -216,6 +213,8 @@ waves.copied = Waves copied.
editor.default = [LIGHT_GRAY]<Default>
edit = Edit...
editor.name = Name:
editor.spawn = Spawn Unit
editor.removeunit = Remove Unit
editor.teams = Teams
editor.elevation = Elevation
editor.errorload = Error loading file:\n[accent]{0}
@@ -436,11 +435,11 @@ setting.fpscap.name = Max FPS
setting.fpscap.none = None
setting.fpscap.text = {0} FPS
setting.swapdiagonal.name = Always Diagonal Placement
setting.difficulty.training = training
setting.difficulty.easy = easy
setting.difficulty.normal = normal
setting.difficulty.hard = hard
setting.difficulty.insane = insane
setting.difficulty.training = Training
setting.difficulty.easy = Easy
setting.difficulty.normal = Normal
setting.difficulty.hard = Hard
setting.difficulty.insane = Insane
setting.difficulty.name = Difficulty:
setting.screenshake.name = Screen Shake
setting.effects.name = Display Effects

View File

@@ -1,4 +1,4 @@
credits.text = Creato da [ROYAL]Anuken[] - [SKY]anukendev@gmail.com[]\n\n[GRAY](Nel caso non te ne sia accorto, la traduzione del gioco non è completa.\n Chi di dovere sta lavorando più velocemente possibile per completarla!)
credits.text = Creato da [ROYAL]Anuken[] - [SKY]anukendev@gmail.com[]\n\n[GRAY](Nel caso non te ne sia accorto, la traduzione del gioco non è completa.\n Chi di dovere sta lavorando più velocemente possibile per completarla! Un aiutino non sarebbe male!)
credits = Crediti
contributors = Translators and Contributors
discord = Unisciti sul server discord di mindustry!
@@ -10,38 +10,38 @@ link.itch.io.description = pagina di itch.io con download per PC e versione web
link.google-play.description = Elenco di Google Play Store
link.wiki.description = wiki ufficiale di Mindustry
linkfail = Impossibile aprire il link! L'URL è stato copiato nella tua bacheca.
screenshot = Screenshot saved to {0}
screenshot.invalid = Map too large, potentially not enough memory for screenshot.
screenshot = Screenshot salvato a {0}
screenshot.invalid = Mappa troppo grossa, probabilmente non c'è abbastanza memoria libera.
gameover = Il nucleo è stato distrutto.
gameover.pvp = The[accent] {0}[] team is victorious!
gameover.pvp = La squadra [accent] {0}[] ha vinto!
highscore = [YELLOW]Nuovo record!
stat.wave = Waves Defeated:[accent] {0}
stat.enemiesDestroyed = Enemies Destroyed:[accent] {0}
stat.built = Buildings Built:[accent] {0}
stat.destroyed = Buildings Destroyed:[accent] {0}
stat.deconstructed = Buildings Deconstructed:[accent] {0}
stat.delivered = Resources Launched:
stat.rank = Final Rank: [accent]{0}
placeline = You have selected a block.\nYou can[accent] place in a line[] by[accent] holding down your finger for a few seconds[] and dragging in a direction.\nTry it.
removearea = You have selected removal mode.\nYou can[accent] remove blocks in a rectangle[] by[accent] holding down your finger for a few seconds[] and dragging.\nTry it.
launcheditems = [accent]Launched Items
stat.wave = Ondate sconfitte:[accent] {0}
stat.enemiesDestroyed = Nemici distrutti:[accent] {0}
stat.built = Costruzioni erette:[accent] {0}
stat.destroyed = Costruzioni distrutte:[accent] {0}
stat.deconstructed = Costruzioni smontate:[accent] {0}
stat.delivered = Riorse lanciate:
stat.rank = Livello finale: [accent]{0}
placeline = Hai appena selezionato un blocco.\nOra puoi[accent] piazzarne una linea[] eseguendo[accent] una lunga pressione[] e poi trascinando in ogni direzione.\nProva!
removearea = Hai appena selezionato la modalità distruzione.\nOra puoi[accent] rimuovere blocchi in una certa zona [] eseguendo[accent] una lunga pressione[] e poi trascinando in ogni direzione.\nProva!
launcheditems = [accent]Oggetti lanciati
map.delete = Sei sicuro di voler eliminare questa mappa"[accent]{0}[]"?
level.highscore = Miglior punteggio: [accent]{0}
level.select = Selezione del livello
level.mode = Modalità di gioco:
showagain = non mostrare più
coreattack = < Il nucleo è sotto attacco! >
nearpoint = [[ [scarlet]LEAVE DROP POINT IMMEDIATELY[] ]\nannihilation imminent
outofbounds = [[ OUT OF BOUNDS ]\n[]self-destruct in {0}
database = Core Database
nearpoint = [[ [scarlet]LACIA LA ZONA NEMICA IMMEDIATAMENTE[] ]\nautodistruzione imminente
outofbounds = [[ SEI FUORI DAL MONDO ]\n[]Auto-distruzione in {0}
database = Database nucleo
savegame = Salva
loadgame = Carica
joingame = Unisciti al gioco
addplayers = Aggiungi/rimuovi giocatori
customgame = Gioco personalizzato
newgame = New Game
newgame = Nuova partita
none = <Niente . . . >
minimap = Minimap
minimap = Minimapa
close = Chiuso
quit = Esci
maps = Mappe
@@ -49,14 +49,14 @@ continue = Continua
maps.none = [LIGHT_GRAY]Nessuna mappa trovata!
about.button = Info
name = Nome:
noname = Pick a[accent] player name[] first.
filename = File Name:
noname = Scegli un [accent] nome[] prima di unirti.
filename = Nome file:
unlocked = Nuovo blocco scoperto!
completed = [accent]Completed
techtree = Tech Tree
research.list = [LIGHT_GRAY]Research:
research = Research
researched = [LIGHT_GRAY]{0} researched.
completed = [accent]Completo
techtree = Albero scoperta
research.list = [LIGHT_GRAY]Ricerca:
research = Ricerca
researched = [LIGHT_GRAY]{0} cercati.
players = {0} giocatori online
players.single = {0} giocatori online
server.closing = [accent]Chiusura server ...
@@ -74,7 +74,7 @@ server.kicked.gameover = Game over!
host.info = Il pulsante [accent]hos [] ospita un server sulle porte [scarlet]6567[] e [scarlet]656.[] Chiunque sulla stessa [LIGHT_GRAY]connessione wifi o rete locale[] dovrebbe essere in grado di vedere il proprio server nel proprio elenco server.\n\n Se vuoi che le persone siano in grado di connettersi ovunque tramite IP, è richiesto il [accent]port forwarding[]. \n\n[LIGHT_GRAY]Nota: se qualcuno sta riscontrando problemi durante la connessione al gioco LAN, assicurati di aver consentito a Mindustry di accedere alla rete locale nelle impostazioni del firewall.
join.info = Qui è possibile inserire un [accent]IP del server[] a cui connettersi, o scoprire [accento]un server sulla rete locale[] disponibile.\n Sono supportati sia il multiplayer LAN che WAN. \n\n[LIGHT_GRAY]Nota: non esiste un elenco di server globali automatici; se si desidera connettersi a qualcuno tramite IP, è necessario chiedere all'host il proprio IP.
hostserver = Host Server
hostserver.mobile = Host\nGame
hostserver.mobile = Host\nServer
host = Host
hosting = [accent] Apertura del server ...
hosts.refresh = Aggiorna
@@ -82,7 +82,7 @@ hosts.discovering = Ricerca partite LAN
server.refreshing = Aggiornamento del server
hosts.none = [lightgray]Nessuna partita LAN trovata!
host.invalid = [scarlet]Impossibile connettersi all'host.
trace = Trace Player
trace = Traccia giocatore
trace.playername = Nome del giocatore: [accent]{0}
trace.ip = IP: [accent]{0}
trace.id = ID univoco: [accent]{0}
@@ -185,8 +185,8 @@ builtin = Incluso
map.delete.confirm = Sei sicuro di voler eliminare questa mappa? Non potrai tornare indietro!
map.random = [accent]Mappa casuale
map.nospawn = Questa mappa non possiede un nucleo dove spawnare! Aggiungine uno nell'editor.
map.nospawn.pvp = This map does not have any enemy cores for player to spawn into! Add[SCARLET] red[] cores to this map in the editor.
map.nospawn.attack = This map does not have any enemy cores for player to attack! Add[SCARLET] red[] cores to this map in the editor.
map.nospawn.pvp = Questa mappa non ha un nucleo nemico! Aggiungi un [SCARLET]nucleo rosso[] nell'editor per poter giocare.
map.nospawn.attack = Questa mappa non ha un nucleo nemico! Aggiungi un [SCARLET]nucleo rosso[] nell'editor per poter giocare.
map.invalid = Errore nel caricamento della mappa: file mappa corrotto o non valido.
editor.brush = Pennello
editor.openin = Apri nell'editor
@@ -195,32 +195,32 @@ editor.oregen.info = Generazione dei minerali:
editor.mapinfo = Informazioni mappa
editor.author = Autore:
editor.description = Descrizione:
editor.waves = Waves:
waves.title = Waves
waves.remove = Remove
waves.never = <never>
waves.every = every
waves.waves = wave(s)
editor.waves = Ondate:
waves.title = Ondate
waves.remove = Rimuovi
waves.never = <mai>
waves.every = sempre
waves.waves = ondata/e
waves.perspawn = per spawn
waves.to = to
waves.to = a
waves.boss = Boss
waves.preview = Preview
waves.edit = Edit...
waves.copy = Copy to Clipboard
waves.load = Load from Clipboard
waves.invalid = Invalid waves in clipboard.
waves.copied = Waves copied.
editor.default = [LIGHT_GRAY]<Default>
edit = Edit...
waves.preview = Anteprima
waves.edit = Modifica...
waves.copy = Copia negli appunti
waves.load = Caica dagli appunti
waves.invalid = Onde dagli appunti non valide.
waves.copied = Onde copiate.
editor.default = [LIGHT_GRAY]<Predefinito>
edit = Modifica...
editor.name = Nome:
editor.teams = Squadre
editor.elevation = Elevazione
editor.errorload = Error loading file:\n[accent]{0}
editor.errorsave = Error saving file:\n[accent]{0}
editor.errorname = Map has no name defined.
editor.update = Update
editor.randomize = Randomize
editor.apply = Apply
editor.errorload = Errore nel caricamento di:\n[accent]{0}
editor.errorsave = Errore nel salvataggio di:\n[accent]{0}
editor.errorname = Questa mappa è senza nome.
editor.update = Aggiorna
editor.randomize = Casualizza
editor.apply = Applica
editor.generate = Genera
editor.resize = Ridimensiona
editor.loadmap = Carica\nmappa
@@ -250,15 +250,15 @@ editor.overwrite = [Accent]Attenzione!\nQuesto sovrascrive una mappa esistente.
editor.overwrite.confirm = [scarlet]Attenzione![] Una mappa con questo nome esiste già. Sei sicuro di volerla sovrascrivere?
editor.selectmap = Seleziona una mappa da caricare:
filters.empty = [LIGHT_GRAY]No filters! Add one with the button below.
filter.distort = Distort
filter.noise = Noise
filter.ore = Ore
filter.rivernoise = River Noise
filter.scatter = Scatter
filter.terrain = Terrain
filter.option.scale = Scale
filter.option.chance = Chance
filter.option.mag = Magnitude
filter.distort = Modifica
filter.noise = Interferenza
filter.ore = Minerali
filter.rivernoise = Interferenze a fiume
filter.scatter = Dispersione
filter.terrain = Terreno
filter.option.scale = Scala
filter.option.chance = Probabilità
filter.option.mag = Magnitudine
filter.option.threshold = Threshold
filter.option.circle-scale = Circle Scale
filter.option.octaves = Octaves

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
core/assets/maps/map_9.msav Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

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

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: 404 KiB

After

Width:  |  Height:  |  Size: 405 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 306 KiB

After

Width:  |  Height:  |  Size: 307 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 373 KiB

After

Width:  |  Height:  |  Size: 417 KiB

View File

@@ -25,6 +25,8 @@ import java.util.Locale;
@SuppressWarnings("unchecked")
public class Vars{
/** Whether to load locales.*/
public static boolean loadLocales = true;
/** IO buffer size. */
public static final int bufferSize = 8192;
/** global charset */
@@ -38,9 +40,10 @@ public class Vars{
/** URL for Github API for releases */
public static final String releasesURL = "https://api.github.com/repos/Anuken/Mindustry/releases";
/** URL for Github API for contributors */
//TODO remove and replace with a manually updated list
public static final String contributorsURL = "https://api.github.com/repos/Anuken/Mindustry/contributors";
/** URL for sending crash reports to */
public static final String crashReportURL = "http://mindustry.us.to/report";
public static final String crashReportURL = "http://mins.us.to/report";
/** maximum distance between mine and core that supports automatic transferring */
public static final float mineTransferRange = 220f;
/** team of the player by default */
@@ -104,8 +107,10 @@ public class Vars{
public static FileHandle customMapDirectory;
/** data subdirectory used for saves */
public static FileHandle saveDirectory;
/** old map file extension, for conversion */
public static final String oldMapExtension = "mmap";
/** map file extension */
public static final String mapExtension = "mmap";
public static final String mapExtension = "msav";
/** save file extension */
public static final String saveExtension = "msav";
@@ -141,19 +146,22 @@ public class Vars{
public static void init(){
Serialization.init();
//load locales
String[] stra = Core.files.internal("locales").readString().split("\n");
locales = new Locale[stra.length];
for(int i = 0; i < locales.length; i++){
String code = stra[i];
if(code.contains("_")){
locales[i] = new Locale(code.split("_")[0], code.split("_")[1]);
}else{
locales[i] = new Locale(code);
if(loadLocales){
//load locales
String[] stra = Core.files.internal("locales").readString().split("\n");
locales = new Locale[stra.length];
for(int i = 0; i < locales.length; i++){
String code = stra[i];
if(code.contains("_")){
locales[i] = new Locale(code.split("_")[0], code.split("_")[1]);
}else{
locales[i] = new Locale(code);
}
}
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();
content = new ContentLoader();

View File

@@ -171,12 +171,10 @@ public class BlockIndexer{
for(int tx = rx * structQuadrantSize; tx < (rx + 1) * structQuadrantSize && tx < world.width(); tx++){
for(int ty = ry * structQuadrantSize; ty < (ry + 1) * structQuadrantSize && ty < world.height(); ty++){
Tile other = world.tile(tx, ty);
Tile other = world.ltile(tx, ty);
if(other == null) continue;
other = other.target();
if(other.entity == null || other.getTeam() != team || !pred.test(other) || !other.block().targetable)
continue;
@@ -293,7 +291,7 @@ public class BlockIndexer{
outer:
for(int x = quadrantX * structQuadrantSize; x < world.width() && x < (quadrantX + 1) * structQuadrantSize; x++){
for(int y = quadrantY * structQuadrantSize; y < world.height() && y < (quadrantY + 1) * structQuadrantSize; y++){
Tile result = world.tile(x, y).target();
Tile result = world.ltile(x, y);
//when a targetable block is found, mark this quadrant as occupied and stop searching
if(result.entity != null && result.getTeam() == data.team){
structQuadrants[data.team.ordinal()].set(index);

View File

@@ -83,7 +83,7 @@ public class Pathfinder{
}
private boolean passable(Tile tile, Team team){
return (!tile.solid()) || (tile.breakable() && (tile.target().getTeam() != team));
return (!tile.solid()) || (tile.breakable() && (tile.getTeam() != team));
}
/**

View File

@@ -2,10 +2,10 @@ package io.anuke.mindustry.ai;
import io.anuke.arc.Events;
import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.IntArray;
import io.anuke.arc.math.Angles;
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.Fx;
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.SpawnGroup;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.world.Pos;
import java.io.*;
import static io.anuke.mindustry.Vars.*;
public class WaveSpawner{
private Array<FlyerSpawn> flySpawns = new Array<>();
private Array<GroundSpawn> groundSpawns = new Array<>();
private IntArray loadedSpawns = new IntArray();
private boolean spawning = false;
public WaveSpawner(){
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(){
return groundSpawns.size;
}
/** @return true if the player is near a ground spawn point. */
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(){
@@ -117,21 +94,11 @@ public class WaveSpawner{
for(int x = 0; x < world.width(); x++){
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);
//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){

View File

@@ -30,7 +30,7 @@ public class Blocks implements ContentList{
public static Block
//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,
iceSnow, sandWater, darksandWater, duneRocks, sandRocks, moss, sporeMoss, shale, shaleRocks, shaleBoulder, grass, salt,
metalFloor, metalFloorDamaged, metalFloor2, metalFloor3, metalFloor5, ignarock, magmarock, hotrock, snowrocks, rock, snowrock, saltRocks,
@@ -88,15 +88,9 @@ public class Blocks implements ContentList{
hasShadow = false;
}
public void draw(Tile tile){
}
public void load(){
}
public void init(){
}
public void draw(Tile tile){}
public void load(){}
public void init(){}
public boolean isHidden(){
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
for(int i = 1; i <= 6; i++){
new BuildBlock("build" + i);
for(int i = 1; i <= BuildBlock.maxSize; i++){
new BuildBlock(i);
}
deepwater = new Floor("deepwater"){{
@@ -556,7 +560,7 @@ public class Blocks implements ContentList{
drawer = tile -> {
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);
@@ -1105,11 +1109,11 @@ public class Blocks implements ContentList{
requirements(Category.power, ItemStack.with(Items.lead, 1000, Items.silicon, 600, Items.graphite, 800, Items.thorium, 200, Items.surgealloy, 500, Items.metaglass, 500));
size = 4;
health = 900;
powerProduction = 110f;
itemDuration = 60f;
powerProduction = 130f;
itemDuration = 90f;
consumes.power(25f);
consumes.item(Items.blastCompound);
consumes.liquid(Liquids.cryofluid, 0.26f);
consumes.liquid(Liquids.cryofluid, 0.25f);
}};
//endregion power
@@ -1147,7 +1151,7 @@ public class Blocks implements ContentList{
}};
blastDrill = new Drill("blast-drill"){{
requirements(Category.production, ItemStack.with(Items.copper, 130, Items.silicon, 120, Items.titanium, 100, Items.thorium, 60));
requirements(Category.production, ItemStack.with(Items.copper, 130, Items.silicon, 120, Items.titanium, 100, Items.thorium, 100));
drillTime = 200;
size = 4;
drawRim = true;

View File

@@ -398,9 +398,9 @@ public class Bullets implements ContentList{
@Override
public void hitTile(Bullet b, Tile tile){
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);
tile.entity.healBy(healPercent / 100f * tile.entity.maxHealth());
}
@@ -424,7 +424,6 @@ public class Bullets implements ContentList{
@Override
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());
Fill.circle(b.x, b.y, 3f * b.fout());
Draw.reset();

View File

@@ -9,6 +9,7 @@ import io.anuke.arc.math.Mathf;
import io.anuke.arc.util.Time;
import io.anuke.mindustry.entities.Effects;
import io.anuke.mindustry.entities.Units;
import io.anuke.mindustry.entities.bullet.BombBulletType;
import io.anuke.mindustry.entities.effect.Lightning;
import io.anuke.mindustry.entities.type.Player;
import io.anuke.mindustry.game.ContentList;
@@ -307,11 +308,11 @@ public class Mechs implements ContentList{
trident = new Mech("trident-ship", true){
{
drillPower = 2;
speed = 0.14f;
speed = 0.15f;
drag = 0.034f;
mass = 2.5f;
turnCursor = false;
health = 220f;
health = 250f;
itemCapacity = 30;
engineColor = Color.valueOf("84f491");
cellTrnsY = 1f;
@@ -319,14 +320,22 @@ public class Mechs implements ContentList{
weapon = new Weapon("bomber"){{
length = 0f;
width = 2f;
reload = 8f;
reload = 25f;
shots = 2;
shotDelay = 1f;
shots = 8;
roundrobin = true;
ejectEffect = Fx.none;
velocityRnd = 1f;
inaccuracy = 40f;
inaccuracy = 20f;
ignoreRotation = true;
bullet = Bullets.bombExplosive;
bullet = new BombBulletType(14f, 25f, "shell"){{
bulletWidth = 10f;
bulletHeight = 14f;
hitEffect = Fx.flakExplosion;
shootEffect = Fx.none;
smokeEffect = Fx.none;
}};
}};
}

View File

@@ -75,8 +75,6 @@ public class StatusEffects implements ContentList{
armorMultiplier = 3f;
damageMultiplier = 3f;
speedMultiplier = 1.1f;
//TODO custom effect
//effect = Fx.overdriven;
}};
shocked = new StatusEffect();

View File

@@ -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)
.dist(0f, false)
.decor(new Decoration(Blocks.shale, Blocks.shaleBoulder, 0.02))){{

View File

@@ -90,17 +90,12 @@ public class ContentLoader{
for(Array<Content> arr : contentMap){
for(int i = 0; i < arr.size; i++){
int id = arr.get(i).id;
if(id < 0) id += 256;
if(id != i){
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){
Log.info("--- CONTENT INFO ---");
for(int k = 0; k < contentMap.length; k++){
@@ -129,7 +124,7 @@ public class ContentLoader{
/** Loads block colors. */
public void loadColors(){
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){
int color = pixmap.getPixel(i, 0);
@@ -170,10 +165,12 @@ public class ContentLoader{
}
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){
//-1 = invalid content
if(id < 0){
return null;
}
if(temporaryMapper[type.ordinal()].length <= id || temporaryMapper[type.ordinal()][id] == null){
return getByID(type, 0); //default value is always ID 0
}
@@ -243,10 +240,4 @@ public class ContentLoader{
TypeTrait.registerType(Bullet.class, Bullet::new);
TypeTrait.registerType(Lightning.class, Lightning::new);
}
private class ImpendingDoomException extends RuntimeException{
ImpendingDoomException(String s){
super(s);
}
}
}

View File

@@ -18,7 +18,7 @@ import io.anuke.mindustry.gen.Call;
import io.anuke.mindustry.input.*;
import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.*;
import io.anuke.mindustry.ui.dialogs.FloatingDialog;
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);
//the restart dialog can show info for any number of scenarios
Call.onGameOver(event.winner);
if(state.rules.zone != -1){
if(state.rules.zone != null){
//remove zone save on game over
if(saves.getZoneSlot() != null){
saves.getZoneSlot().delete();
@@ -201,8 +201,25 @@ public class Control implements ApplicationListener{
public void playMap(Map map, Rules rules){
ui.loadAnd(() -> {
logic.reset();
state.rules = rules;
world.loadMap(map);
state.rules = rules;
logic.play();
});
}
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();
});
}

View File

@@ -40,6 +40,10 @@ public class GameState{
state = astate;
}
public boolean isEditor(){
return rules.editor;
}
public boolean isPaused(){
return (is(State.paused) && !Net.active()) || (gameOver && !Net.active());
}

View File

@@ -58,12 +58,6 @@ public class Logic implements ApplicationListener{
public void play(){
state.set(State.playing);
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());
}
@@ -92,7 +86,7 @@ public class Logic implements ApplicationListener{
}
private void checkGameOver(){
if(state.rules.waves && state.teams.get(defaultTeam).cores.size == 0 && !state.gameOver){
if(!state.rules.attackMode && state.teams.get(defaultTeam).cores.size == 0 && !state.gameOver){
state.gameOver = true;
Events.fire(new GameOverEvent(waveTeam));
}else if(state.rules.attackMode){
@@ -165,15 +159,17 @@ public class Logic implements ApplicationListener{
Entities.update(groundEffectGroup);
}
for(EntityGroup group : unitGroups){
Entities.update(group);
}
if(!state.isEditor()){
for(EntityGroup group : unitGroups){
Entities.update(group);
}
Entities.update(puddleGroup);
Entities.update(shieldGroup);
Entities.update(bulletGroup);
Entities.update(tileGroup);
Entities.update(fireGroup);
Entities.update(puddleGroup);
Entities.update(shieldGroup);
Entities.update(bulletGroup);
Entities.update(tileGroup);
Entities.update(fireGroup);
}
Entities.update(playerGroup);
//effect group only contains item transfers in the headless version, update it!
@@ -181,19 +177,21 @@ public class Logic implements ApplicationListener{
Entities.update(effectGroup);
}
for(EntityGroup group : unitGroups){
if(group.isEmpty()) continue;
if(!state.isEditor()){
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();
}
if(!Net.client() && !world.isInvalidMap()){
if(!Net.client() && !world.isInvalidMap() && !state.isEditor()){
checkGameOver();
}
}

View File

@@ -20,6 +20,7 @@ import io.anuke.mindustry.entities.type.Player;
import io.anuke.mindustry.game.Version;
import io.anuke.mindustry.gen.Call;
import io.anuke.mindustry.gen.RemoteReadClient;
import io.anuke.mindustry.net.Administration.TraceInfo;
import io.anuke.mindustry.net.*;
import io.anuke.mindustry.net.Net.SendMode;
import io.anuke.mindustry.net.Packets.*;
@@ -117,7 +118,7 @@ public class NetClient implements ApplicationListener{
}
//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){
if(Vars.ui != null){
Vars.ui.chatfrag.addMessage(message, sender);
@@ -158,6 +159,13 @@ public class NetClient implements ApplicationListener{
return "[#" + player.color.toString().toUpperCase() + "]" + name;
}
@Remote(variants = Variant.one)
public static void onTraceInfo(Player player, TraceInfo info){
if(player != null){
ui.traces.show(player, info);
}
}
@Remote(variants = Variant.one, priority = PacketPriority.high)
public static void onKick(KickReason reason){
netClient.disconnectQuietly();

View File

@@ -12,8 +12,7 @@ import io.anuke.arc.math.Mathf;
import io.anuke.arc.math.geom.Rectangle;
import io.anuke.arc.math.geom.Vector2;
import io.anuke.arc.util.*;
import io.anuke.arc.util.io.ByteBufferOutput;
import io.anuke.arc.util.io.ReusableByteOutStream;
import io.anuke.arc.util.io.*;
import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.entities.Entities;
@@ -29,6 +28,7 @@ import io.anuke.mindustry.gen.Call;
import io.anuke.mindustry.gen.RemoteReadServer;
import io.anuke.mindustry.net.*;
import io.anuke.mindustry.net.Administration.PlayerInfo;
import io.anuke.mindustry.net.Administration.TraceInfo;
import io.anuke.mindustry.net.Packets.*;
import io.anuke.mindustry.world.Tile;
@@ -212,7 +212,7 @@ public class NetServer implements ApplicationListener{
public void sendWorldData(Player player, int clientID){
ByteArrayOutputStream stream = new ByteArrayOutputStream();
DeflaterOutputStream def = new DeflaterOutputStream(stream);
DeflaterOutputStream def = new FastDeflaterOutputStream(stream);
NetworkIO.writeWorld(player, def);
WorldStream data = new WorldStream();
data.stream = new ByteArrayInputStream(stream.toByteArray());
@@ -289,7 +289,7 @@ public class NetServer implements ApplicationListener{
//auto-skip done requests
if(req.breaking && tile.block() == Blocks.air){
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;
}
player.getPlaceQueue().addLast(req);
@@ -355,11 +355,11 @@ public class NetServer implements ApplicationListener{
netServer.kick(other.con.id, KickReason.kick);
Log.info("&lc{0} has kicked {1}.", player.name, other.name);
}else if(action == AdminAction.trace){
//TODO implement
TraceInfo info = new TraceInfo(other.con.address, other.uuid, other.con.modclient, other.con.mobile);
if(player.con != null){
//Call.onTraceInfo(player.con.id, other.con.trace);
Call.onTraceInfo(player.con.id, other, info);
}else{
//NetClient.onTraceInfo(other.con.trace);
NetClient.onTraceInfo(other, info);
}
Log.info("&lc{0} has requested trace info of {1}.", player.name, other.name);
}
@@ -478,7 +478,7 @@ public class NetServer implements ApplicationListener{
sent++;
if(syncStream.position() > maxSnapshotSize){
if(syncStream.size() > maxSnapshotSize){
dataStream.close();
byte[] syncBytes = syncStream.toByteArray();
Call.onEntitySnapshot(player.con.id, (byte)group.getID(), (short)sent, (short)syncBytes.length, Net.compressSnapshot(syncBytes));

View File

@@ -2,7 +2,6 @@ package io.anuke.mindustry.core;
import io.anuke.annotations.Annotations.Nullable;
import io.anuke.arc.*;
import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.IntArray;
import io.anuke.arc.math.Mathf;
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.maps.*;
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.blocks.BlockPart;
import static io.anuke.mindustry.Vars.*;
@@ -28,17 +28,22 @@ public class World implements ApplicationListener{
public final BlockIndexer indexer = new BlockIndexer();
public final WaveSpawner spawner = new WaveSpawner();
public final Pathfinder pathfinder = new Pathfinder();
public final Context context = new Context();
private Map currentMap;
private Tile[][] tiles;
private Array<Tile> tempTiles = new Array<>();
private boolean generating, invalidMap;
public World(){
maps.load();
}
@Override
public void init(){
maps.loadLegacyMaps();
}
@Override
public void dispose(){
maps.dispose();
@@ -97,6 +102,12 @@ public class World implements ApplicationListener{
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){
return tiles[x][y];
}
@@ -105,6 +116,10 @@ public class World implements ApplicationListener{
return tile(Math.round(x / tilesize), Math.round(y / tilesize));
}
public @Nullable Tile ltileWorld(float x, float y){
return ltile(Math.round(x / tilesize), Math.round(y / tilesize));
}
public int toTile(float coord){
return Math.round(coord / tilesize);
}
@@ -160,6 +175,8 @@ public class World implements ApplicationListener{
* A WorldLoadEvent will be fire.
*/
public void endMapLoad(){
prepareTiles(tiles);
for(int x = 0; x < tiles.length; x++){
for(int y = 0; y < tiles[0].length; y++){
Tile tile = tiles[x][y];
@@ -188,24 +205,7 @@ public class World implements ApplicationListener{
}
public Zone getZone(){
return content.getByID(ContentType.zone, 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();
});
return state.rules.zone;
}
public void loadGenerator(Generator generator){
@@ -213,24 +213,14 @@ public class World implements ApplicationListener{
createTiles(generator.width, generator.height);
generator.generate(tiles);
prepareTiles(tiles);
endMapLoad();
}
public void loadMap(Map map){
beginMapLoad();
this.currentMap = map;
try{
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 Tile(x, y);
}
}
MapIO.readTiles(map, tiles);
prepareTiles(tiles);
MapIO.loadMap(map);
}catch(Exception e){
Log.err(e);
if(!headless){
@@ -242,7 +232,7 @@ public class World implements ApplicationListener{
return;
}
endMapLoad();
this.currentMap = map;
invalidMap = false;
@@ -289,16 +279,7 @@ public class World implements ApplicationListener{
}
public void removeBlock(Tile tile){
if(!tile.block().isMultiblock() && !tile.isLinked()){
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);
}
}
tile.link().getLinkedTiles(other -> other.setBlock(Blocks.air));
}
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)){
Tile toplace = world.tile(worldx, worldy);
if(toplace != null){
toplace.setLinked((byte)(dx + offsetx), (byte)(dy + offsety));
toplace.setTeam(team);
toplace.setBlock(BlockPart.get(dx + offsetx, dy + offsety), 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.
*/
@@ -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){
byte[][] dark = 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++){
Tile tile = tiles[x][y];
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)){
Tile toplace = world.tile(worldx, worldy);
if(toplace != null){
toplace.setLinked((byte)(dx + offsetx), (byte)(dy + offsety));
toplace.setTeam(team);
toplace.setBlock(BlockPart.get(dx + offsetx, dy + offsety), 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{
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();
}
}
}

View File

@@ -9,6 +9,7 @@ import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.Floor;
import static io.anuke.mindustry.Vars.content;
import static io.anuke.mindustry.Vars.world;
public class DrawOperation{
private LongArray array = new LongArray();
@@ -24,32 +25,46 @@ public class DrawOperation{
public void undo(MapEditor editor){
for(int i = array.size - 1; i >= 0; 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){
for(int i = 0; i < array.size; 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(() -> {
if(type == OpType.floor.ordinal()){
tile.setFloor((Floor)content.block(to));
}else if(type == OpType.block.ordinal()){
Block block = content.block(to);
tile.setBlock(block);
if(block.isMultiblock()){
editor.updateLinks(block, tile.x, tile.y);
}
world.setBlock(tile, block, tile.getTeam(), tile.rotation());
}else if(type == OpType.rotation.ordinal()){
tile.setRotation(to);
tile.rotation(to);
}else if(type == OpType.team.ordinal()){
tile.setTeam(Team.all[to]);
}else if(type == OpType.ore.ordinal()){
}else if(type == OpType.overlay.ordinal()){
tile.setOverlayID(to);
}
});
@@ -61,8 +76,7 @@ public class DrawOperation{
short x;
short y;
byte type;
byte from;
byte to;
short value;
}
public enum OpType{
@@ -70,6 +84,6 @@ public class DrawOperation{
block,
rotation,
team,
ore
overlay
}
}

View File

@@ -1,6 +1,7 @@
package io.anuke.mindustry.editor;
import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.editor.DrawOperation.OpType;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.gen.TileOp;
@@ -9,21 +10,23 @@ import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.*;
import io.anuke.mindustry.world.modules.*;
import static io.anuke.mindustry.Vars.state;
import static io.anuke.mindustry.Vars.ui;
//TODO somehow remove or replace this class with a more flexible solution
public class EditorTile extends Tile{
public EditorTile(int x, int y, byte floor, byte wall){
super(x, y, floor, wall);
}
@Override
public Team getTeam(){
return Team.all[getTeamID()];
public EditorTile(int x, int y, int floor, int overlay, int wall){
super(x, y, floor, overlay, wall);
}
@Override
public void setFloor(Floor type){
if(state.is(State.playing)){
super.setFloor(type);
return;
}
if(type instanceof OverlayFloor){
//don't place on liquids
if(!floor.isLiquid){
@@ -32,63 +35,91 @@ public class EditorTile extends Tile{
return;
}
Block previous = floor();
Block ore = overlay();
if(previous == type && ore == Blocks.air) return;
if(floor == type && overlayID() == 0) return;
if(overlayID() != 0) op(OpType.overlay, overlayID());
if(floor != type) op(OpType.floor, floor.id);
super.setFloor(type);
//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
public void setBlock(Block type){
Block previous = block;
byte pteam = getTeamID();
if(previous == type) return;
super.setBlock(type);
if(pteam != getTeamID()){
op(TileOp.get(x, y, (byte)OpType.team.ordinal(), pteam, getTeamID()));
if(state.is(State.playing)){
super.setBlock(type);
return;
}
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
public void setTeam(Team team){
byte previous = getTeamID();
if(previous == team.ordinal()) return;
if(state.is(State.playing)){
super.setTeam(team);
return;
}
if(getTeamID() == team.ordinal()) return;
op(OpType.team, getTeamID());
super.setTeam(team);
op(TileOp.get(x, y, (byte)OpType.team.ordinal(), previous, (byte)team.ordinal()));
}
@Override
public void setRotation(byte rotation){
byte previous = getRotation();
if(previous == rotation) return;
super.setRotation(rotation);
op(TileOp.get(x, y, (byte)OpType.rotation.ordinal(), previous, rotation));
public void rotation(int rotation){
if(state.is(State.playing)){
super.rotation(rotation);
return;
}
if(rotation == rotation()) return;
op(OpType.rotation, rotation());
super.rotation(rotation);
}
@Override
public void setOverlayID(byte ore){
byte previous = getOverlayID();
if(previous == ore) return;
super.setOverlayID(ore);
op(TileOp.get(x, y, (byte)OpType.ore.ordinal(), previous, ore));
public void setOverlayID(short overlay){
if(state.is(State.playing)){
super.setOverlayID(overlay);
return;
}
if(overlayID() == overlay) return;
op(OpType.overlay, overlay);
super.setOverlayID(overlay);
}
@Override
protected void preChanged(){
if(state.is(State.playing)){
super.preChanged();
return;
}
super.setTeam(Team.none);
}
@Override
protected void changed(){
if(state.is(State.playing)){
super.changed();
return;
}
entity = null;
if(block == null){
@@ -102,8 +133,7 @@ public class EditorTile extends Tile{
Block block = block();
if(block.hasEntity()){
entity = block.newEntity();
entity.health = block.health;
entity = block.newEntity().init(this, false);
entity.cons = new ConsumeModule(entity);
if(block.hasItems) entity.items = new ItemModule();
if(block.hasLiquids) entity.liquids = new LiquidModule();
@@ -111,7 +141,7 @@ public class EditorTile extends Tile{
}
}
private static void op(long op){
ui.editor.editor.addTileOp(op);
private void op(OpType type, short value){
ui.editor.editor.addTileOp(TileOp.get(x, y, (byte)type.ordinal(), value));
}
}

View File

@@ -15,21 +15,7 @@ public enum EditorTool{
public void touched(MapEditor editor, int x, int y){
if(!Structs.inBounds(x, y, editor.width(), editor.height())) return;
Tile tile = editor.tile(x, y);
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;
}
Tile tile = editor.tile(x, y).link();
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;
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;
}
@@ -116,7 +102,7 @@ public enum EditorTool{
}
if(draw.rotate){
write.setRotation((byte)editor.rotation);
write.rotation((byte)editor.rotation);
}
};

View File

@@ -1,27 +1,28 @@
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.graphics.Pixmap;
import io.anuke.arc.math.Mathf;
import io.anuke.arc.util.Pack;
import io.anuke.arc.util.Structs;
import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.gen.TileOp;
import io.anuke.mindustry.io.LegacyMapIO;
import io.anuke.mindustry.io.MapIO;
import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.*;
import io.anuke.mindustry.world.blocks.BlockPart;
import io.anuke.mindustry.world.blocks.Floor;
import java.io.IOException;
import static io.anuke.mindustry.Vars.world;
public class MapEditor{
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 Tile[][] tiles;
private OperationStack stack = new OperationStack();
private DrawOperation currentOp;
@@ -32,7 +33,7 @@ public class MapEditor{
public Block drawBlock = Blocks.stone;
public Team drawTeam = Team.blue;
public ObjectMap<String, String> getTags(){
public StringMap getTags(){
return tags;
}
@@ -40,39 +41,39 @@ public class MapEditor{
reset();
loading = true;
tiles = createTiles(width, height);
createTiles(width, height);
renderer.resize(width(), height());
loading = false;
}
public void beginEdit(Map map) throws IOException{
public void beginEdit(Map map){
reset();
loading = true;
tiles = createTiles(map.width, map.height);
tags.putAll(map.tags);
MapIO.readTiles(map, tiles);
MapIO.loadMap(map, context);
checkLinkedTiles();
renderer.resize(width(), height());
loading = false;
}
public void beginEdit(Tile[][] tiles){
public void beginEdit(Pixmap pixmap){
reset();
this.tiles = tiles;
checkLinkedTiles();
createTiles(pixmap.getWidth(), pixmap.getHeight());
load(() -> LegacyMapIO.readPixmap(pixmap, tiles()));
renderer.resize(width(), height());
}
//adds missing blockparts
public void checkLinkedTiles(){
Tile[][] tiles = world.getTiles();
//clear block parts first
for(int x = 0; x < width(); x++){
for(int y = 0; y < height(); y++){
if(tiles[x][y].block() == Blocks.part){
if(tiles[x][y].block() instanceof BlockPart){
tiles[x][y].setBlock(Blocks.air);
tiles[x][y].setLinkByte((byte)0);
}
}
}
@@ -80,22 +81,8 @@ public class MapEditor{
//set up missing blockparts
for(int x = 0; x < width(); x++){
for(int y = 0; y < height(); y++){
Block drawBlock = tiles[x][y].block();
if(drawBlock.isMultiblock()){
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)));
}
}
}
if(tiles[x][y].block().isMultiblock()){
world.setBlock(tiles[x][y], tiles[x][y].block(), tiles[x][y].getTeam());
}
}
}
@@ -108,63 +95,41 @@ public class MapEditor{
}
/** Creates a 2-D array of EditorTiles with stone as the floor block. */
public Tile[][] createTiles(int width, int height){
tiles = new Tile[width][height];
private void createTiles(int width, int height){
Tile[][] tiles = world.createTiles(width, height);
for(int x = 0; x < width; x++){
for(int y = 0; y < height; y++){
tiles[x][y] = new EditorTile(x, y, Blocks.stone.id, (byte)0);
tiles[x][y] = new EditorTile(x, y, Blocks.stone.id, (short)0, (short)0);
}
}
return tiles;
}
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(){
clearOp();
brushSize = 1;
drawBlock = Blocks.stone;
tags = new ObjectMap<>();
tags = new StringMap();
}
public Tile[][] tiles(){
return tiles;
return world.getTiles();
}
public Tile tile(int x, int y){
return tiles[x][y];
return world.rawTile(x, y);
}
public int width(){
return tiles.length;
return world.width();
}
public int height(){
return tiles[0].length;
}
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)));
}
}
}
}
return world.height();
}
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){
boolean isfloor = drawBlock instanceof Floor && drawBlock != Blocks.air;
Tile[][] tiles = world.getTiles();
if(drawBlock.isMultiblock()){
x = Mathf.clamp(x, (drawBlock.size - 1) / 2, width() - drawBlock.size / 2 - 1);
y = Mathf.clamp(y, (drawBlock.size - 1) / 2, height() - drawBlock.size / 2 - 1);
int offsetx = -(drawBlock.size - 1) / 2;
int offsety = -(drawBlock.size - 1) / 2;
for(int i = 0; i < 2; i++){
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;
for(int dx = 0; dx < drawBlock.size; dx++){
for(int dy = 0; dy < drawBlock.size; dy++){
int worldx = dx + offsetx + x;
int worldy = dy + offsety + y;
if(Structs.inBounds(worldx, worldy, width(), height())){
Tile tile = tiles[worldx][worldy];
if(Structs.inBounds(worldx, worldy, width(), height())){
Tile tile = tiles[worldx][worldy];
if(i == 1){
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();
Block block = tile.block();
if(link != 0){
removeLinked(worldx - (Pack.leftByte(link) - 8), worldy - (Pack.rightByte(link) - 8));
}else if(block.isMultiblock()){
removeLinked(worldx, worldy);
}
}
//bail out if there's anything blocking the way
if(block.isMultiblock() || block instanceof BlockPart){
return;
}
renderer.updatePoint(worldx, worldy);
}
}
}
Tile tile = tiles[x][y];
tile.setBlock(drawBlock);
tile.setTeam(drawTeam);
world.setBlock(tiles[x][y], drawBlock, drawTeam);
}else{
for(int rx = -brushSize; rx <= brushSize; rx++){
for(int ry = -brushSize; ry <= brushSize; ry++){
@@ -228,14 +184,8 @@ public class MapEditor{
Tile tile = tiles[wx][wy];
if(!isfloor){
byte link = tile.getLinkByte();
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 && (tile.isLinked() || tile.block().isMultiblock())){
world.removeBlock(tile.link());
}
if(isfloor){
@@ -246,7 +196,7 @@ public class MapEditor{
tile.setTeam(drawTeam);
}
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(){
return renderer;
}
@@ -278,11 +212,11 @@ public class MapEditor{
public void resize(int width, int height){
clearOp();
Tile[][] previous = tiles;
Tile[][] previous = world.getTiles();
int offsetX = -(width - width()) / 2, offsetY = -(height - height()) / 2;
loading = true;
tiles = new Tile[width][height];
Tile[][] tiles = world.createTiles(width, height);
for(int x = 0; x < width; x++){
for(int y = 0; y < height; y++){
int px = offsetX + x, py = offsetY + y;
@@ -291,7 +225,7 @@ public class MapEditor{
tiles[x][y].x = (short)x;
tiles[x][y].y = (short)y;
}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));
}
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();
}
}
}

View File

@@ -2,6 +2,7 @@ package io.anuke.mindustry.editor;
import io.anuke.arc.Core;
import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.StringMap;
import io.anuke.arc.files.FileHandle;
import io.anuke.arc.function.Consumer;
import io.anuke.arc.graphics.Color;
@@ -17,8 +18,10 @@ import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.scene.ui.layout.Unit;
import io.anuke.arc.util.*;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.game.*;
import io.anuke.mindustry.io.JsonIO;
import io.anuke.mindustry.io.MapIO;
import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.ui.dialogs.FloatingDialog;
@@ -40,6 +43,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
private MapGenerateDialog generateDialog;
private ScrollPane pane;
private FloatingDialog menu;
private Rules lastSavedRules;
private boolean saved = false;
private boolean shownWithMap = false;
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(() -> {
try{
//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){
ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, false)));
Log.err(e);
@@ -103,9 +107,8 @@ public class MapEditorDialog extends Dialog implements Disposable{
ui.loadAnd(() -> {
try{
Pixmap pixmap = new Pixmap(file);
Tile[][] tiles = editor.createTiles(pixmap.getWidth(), pixmap.getHeight());
editor.load(() -> MapIO.readLegacyPixmap(pixmap, tiles));
editor.beginEdit(tiles);
editor.beginEdit(pixmap);
pixmap.dispose();
}catch(Exception e){
ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, false)));
Log.err(e);
@@ -122,7 +125,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
if(!editor.getTags().containsKey("name")){
editor.getTags().put("name", result.nameWithoutExtension());
}
MapIO.writeMap(result, editor.createMap(result), editor.tiles());
MapIO.writeMap(result, editor.createMap(result));
}catch(Exception e){
ui.showError(Core.bundle.format("editor.errorsave", Strings.parseException(e, false)));
Log.err(e);
@@ -133,10 +136,14 @@ public class MapEditorDialog extends Dialog implements Disposable{
menu.cont.row();
menu.cont.addImageTextButton("$editor.ingame", "icon-arrow", isize, this::playtest).padTop(-5).size(swidth * 2f + 10, 60f);
menu.cont.row();
menu.cont.addImageTextButton("$quit", "icon-back", isize, () -> {
tryExit();
menu.hide();
}).padTop(-5).size(swidth * 2f + 10, 60f);
}).size(swidth * 2f + 10, 60f);
resizeDialog = new MapResizeDialog(editor, (x, y) -> {
if(!(editor.width() == x && editor.height() == y)){
@@ -181,11 +188,14 @@ public class MapEditorDialog extends Dialog implements Disposable{
});
shown(() -> {
//clear units, rules and other unnecessary stuff
logic.reset();
saved = true;
if(!Core.settings.getBool("landscape")) Platform.instance.beginForceLandscape();
editor.clearOp();
Core.scene.setScrollFocus(view);
if(!shownWithMap){
state.rules = new Rules();
editor.beginEdit(200, 200);
}
shownWithMap = false;
@@ -205,8 +215,47 @@ public class MapEditorDialog extends Dialog implements Disposable{
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(){
String name = editor.getTags().get("name", "").trim();
editor.getTags().put("rules", JsonIO.write(state.rules));
if(name.isEmpty()){
infoDialog.show();
@@ -216,7 +265,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
if(map != null && !map.custom){
ui.showError("$editor.save.overwrite");
}else{
world.maps.saveMap(editor.getTags(), editor.tiles());
world.maps.saveMap(editor.getTags());
ui.showInfoFade("$editor.saved");
}
}
@@ -281,9 +330,8 @@ public class MapEditorDialog extends Dialog implements Disposable{
public void beginEditMap(FileHandle file){
ui.loadAnd(() -> {
try{
Map map = MapIO.readMap(file, true);
shownWithMap = true;
editor.beginEdit(map);
editor.beginEdit(MapIO.createMap(file, true));
show();
}catch(Exception e){
Log.err(e);

View File

@@ -224,7 +224,7 @@ public class MapGenerateDialog extends FloatingDialog{
Tile tile = editor.tile(x, y);
input.begin(editor, x, y, tile.floor(), tile.block(), tile.overlay());
filter.apply(input);
writeTiles[x][y].set(input.floor, input.block, input.ore, tile.getTeam(), tile.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);
DummyTile write = writeTiles[x][y];
tile.setRotation(write.rotation);
tile.rotation(write.rotation);
tile.setFloor((Floor)content.block(write.floor));
tile.setBlock(content.block(write.block));
tile.setTeam(Team.all[write.team]);
@@ -322,7 +322,8 @@ public class MapGenerateDialog extends FloatingDialog{
}
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){
this.floor = floor.id;
@@ -341,7 +342,7 @@ public class MapGenerateDialog extends FloatingDialog{
}
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());
}
}

View File

@@ -4,12 +4,16 @@ import io.anuke.arc.Core;
import io.anuke.arc.collection.ObjectMap;
import io.anuke.arc.scene.ui.TextArea;
import io.anuke.arc.scene.ui.TextField;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.core.Platform;
import io.anuke.mindustry.game.Rules;
import io.anuke.mindustry.ui.dialogs.CustomRulesDialog;
import io.anuke.mindustry.ui.dialogs.FloatingDialog;
public class MapInfoDialog extends FloatingDialog{
private final MapEditor editor;
private final WaveInfoDialog waveinfo;
private final CustomRulesDialog ruleinfo = new CustomRulesDialog();
public MapInfoDialog(MapEditor editor){
super("$editor.mapinfo");
@@ -36,7 +40,6 @@ public class MapInfoDialog extends FloatingDialog{
name.setMessageText("$unknown");
cont.row();
cont.add("$editor.description").padRight(8).left();
TextArea description = cont.addArea(tags.get("description", ""), "textarea", text -> {
@@ -44,7 +47,6 @@ public class MapInfoDialog extends FloatingDialog{
}).size(400f, 140f).get();
cont.row();
cont.add("$editor.author").padRight(8).left();
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();
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.add("$editor.waves").padRight(8).left();
cont.table(t -> {
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);
cont.addButton("$edit", waveinfo::show).left().width(200f);
name.change();
description.change();

View File

@@ -14,6 +14,7 @@ import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.graphics.IndexedRenderer;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.BlockPart;
import static io.anuke.mindustry.Vars.tilesize;
@@ -84,7 +85,6 @@ public class MapRenderer implements Disposable{
}
public void updatePoint(int x, int y){
//TODO spread out over multiple frames?
updates.add(x + y * width);
}
@@ -110,13 +110,13 @@ public class MapRenderer implements Disposable{
int idxWall = (wx % chunksize) + (wy % 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();
if(wall.rotate){
mesh.draw(idxWall, region,
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{
mesh.draw(idxWall, region,
wx * tilesize + wall.offset() + (tilesize - region.getWidth() * Draw.scl) / 2f,

View File

@@ -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){
tr.trns(angle, length);
world.raycastEachWorld(x, y, x + tr.x, y + tr.y, (cx, cy) -> {
Tile tile = world.tile(cx, cy);
if(tile != null) tile = tile.target();
if(tile != null && tile.entity != null && tile.target().getTeamID() != team.ordinal() && tile.entity.collide(hitter)){
Tile tile = world.ltile(cx, cy);
if(tile != null && tile.entity != null && tile.getTeamID() != team.ordinal() && tile.entity.collide(hitter)){
tile.entity.collision(hitter);
hitter.getBulletType().hit(hitter, tile.worldx(), tile.worldy());
}
@@ -216,12 +215,10 @@ public class Damage{
int scaledDamage = (int)(damage * (1f - (float)dst / radius));
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;
tile = tile.target();
//apply damage to entity if needed
if(tile.entity != null && tile.getTeam() != team){
int health = (int)tile.entity.health;

View File

@@ -7,10 +7,11 @@ import io.anuke.arc.function.Consumer;
import io.anuke.arc.function.Predicate;
import io.anuke.arc.graphics.Camera;
import io.anuke.arc.math.geom.Rectangle;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.entities.traits.DrawTrait;
import io.anuke.mindustry.entities.traits.Entity;
import static io.anuke.mindustry.Vars.collisions;
public class Entities{
public static final int maxLeafObjects = 4;
private static final Array<EntityGroup<?>> groupArray = new Array<>();
@@ -48,7 +49,7 @@ public class Entities{
group.updateEvents();
if(group.useTree()){
Vars.collisions.updatePhysics(group);
collisions.updatePhysics(group);
}
for(Entity e : group.all()){

View File

@@ -225,9 +225,8 @@ public class Bullet extends SolidEntity implements DamageTrait, ScaleTrait, Pool
if(type.hitTiles && collidesTiles() && !supressCollision && initialized){
world.raycastEach(world.toTile(lastPosition().x), world.toTile(lastPosition().y), world.toTile(x), world.toTile(y), (x, y) -> {
Tile tile = world.tile(x, y);
Tile tile = world.ltile(x, y);
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.getTeam() != team){

View File

@@ -72,6 +72,11 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable{
}
}
@Override
public byte version(){
return 0;
}
@Override
public float lifetime(){
return lifetime;
@@ -98,7 +103,7 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable{
return;
}
TileEntity entity = tile.target().entity;
TileEntity entity = tile.link().entity;
boolean damage = entity != null;
float flammability = baseFlammability + puddleFlammability;
@@ -151,7 +156,7 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable{
}
@Override
public void readSave(DataInput stream) throws IOException{
public void readSave(DataInput stream, byte version) throws IOException{
this.loadedPosition = stream.readInt();
this.lifetime = stream.readFloat();
this.time = stream.readFloat();

View File

@@ -143,6 +143,11 @@ public class Puddle extends SolidEntity implements SaveTrait, Poolable, DrawTrai
return liquid.flammability * amount;
}
@Override
public byte version(){
return 0;
}
@Override
public void hitbox(Rectangle rectangle){
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);
}
@@ -245,7 +250,7 @@ public class Puddle extends SolidEntity implements SaveTrait, Poolable, DrawTrai
}
@Override
public void readSave(DataInput stream) throws IOException{
public void readSave(DataInput stream, byte version) throws IOException{
this.loadedPosition = stream.readInt();
this.x = stream.readFloat();
this.y = stream.readFloat();

View File

@@ -40,10 +40,98 @@ public interface BuilderTrait extends Entity, TeamTrait{
float placeDistance = 220f;
float mineDistance = 70f;
//due to iOS wierdness
class BuildDataStatic{
static Array<BuildRequest> removal = new Array<>();
static Vector2[] tmptr = new Vector2[]{new Vector2(), new Vector2(), new Vector2(), new Vector2()};
/**
* Update building mechanism for this unit.
* This includes mining.
*/
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. */
@@ -148,97 +236,10 @@ public interface BuilderTrait extends Entity, TeamTrait{
return getPlaceQueue().size == 0 ? null : getPlaceQueue().first();
}
/**
* Update building mechanism for this unit.
* This includes mining.
*/
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;
}
//due to iOS wierdness, this is apparently required
class BuildDataStatic{
static Array<BuildRequest> removal = new Array<>();
static Vector2[] tmptr = new Vector2[]{new Vector2(), new Vector2(), new Vector2(), new Vector2()};
}
/** Do not call directly. */
@@ -291,7 +292,7 @@ public interface BuilderTrait extends Entity, TeamTrait{
Tile tile = world.tile(request.x, request.y);
if(dst(tile) > placeDistance){
if(dst(tile) > placeDistance && !state.isEditor()){
return;
}

View File

@@ -4,4 +4,5 @@ package io.anuke.mindustry.entities.traits;
* Marks an entity as serializable.
*/
public interface SaveTrait extends Entity, TypeTrait, Saveable{
byte version();
}

View File

@@ -4,6 +4,5 @@ import java.io.*;
public interface Saveable{
void writeSave(DataOutput stream) throws IOException;
void readSave(DataInput stream) throws IOException;
void readSave(DataInput stream, byte version) throws IOException;
}

View File

@@ -41,15 +41,6 @@ public interface SyncTrait extends Entity, TypeTrait{
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
void write(DataOutput data) throws IOException;

View File

@@ -23,9 +23,7 @@ public interface TypeTrait{
lastRegisteredID[0]++;
}
/**
* Registers a syncable type by ID.
*/
/**Gets a syncable type by ID.*/
static Supplier<? extends TypeTrait> getTypeByID(int id){
if(id == -1){
throw new IllegalArgumentException("Attempt to retrieve invalid entity type ID! Did you forget to set it in ContentLoader.registerTypes()?");

View File

@@ -244,12 +244,14 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
@Override
public void update(){
hitTime -= Time.delta();
if(isDead()){
//dead enemies should get immediately removed
remove();
return;
}
hitTime -= Time.delta();
if(Net.client()){
interpolate();
status.update(this);
@@ -304,11 +306,6 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
return type.hitsize * 10;
}
@Override
public float clipSize(){
return isBoss() ? 10000000000f : super.clipSize();
}
@Override
public void onDeath(){
Call.onUnitDeath(this);
@@ -336,6 +333,11 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
return unitGroups[team.ordinal()];
}
@Override
public byte version(){
return 0;
}
@Override
public void writeSave(DataOutput stream) throws IOException{
super.writeSave(stream);
@@ -344,8 +346,8 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
}
@Override
public void readSave(DataInput stream) throws IOException{
super.readSave(stream);
public void readSave(DataInput stream, byte version) throws IOException{
super.readSave(stream, version);
byte type = stream.readByte();
this.spawner = stream.readInt();
@@ -363,7 +365,9 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
@Override
public void read(DataInput data) throws IOException{
float lastx = x, lasty = y, lastrot = rotation;
super.readSave(data);
super.readSave(data, version());
this.type = content.getByID(ContentType.unit, data.readByte());
this.spawner = data.readInt();

View File

@@ -432,7 +432,7 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
if(getCurrentRequest() == request && request.progress > 0.001f) continue;
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
Lines.stroke(2f, Pal.removeBack);
@@ -636,13 +636,22 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
}
protected void updateShooting(){
if(isShooting() && mech.canShoot(this)){
mech.weapon.update(this, pointerX, pointerY);
if(!state.isEditor() && isShooting() && mech.canShoot(this)){
if(!mech.turnCursor){
//shoot forward ignoring cursor
mech.weapon.update(this, x + Angles.trnsx(rotation, 1f), y + Angles.trnsy(rotation, 1f));
}else{
mech.weapon.update(this, pointerX, pointerY);
}
}
}
protected void updateFlying(){
if(Units.invalidateTarget(target, this) && !(target instanceof TileEntity && ((TileEntity)target).damaged() && target.isValid() && ((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;
}
@@ -778,6 +787,7 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
moveTarget = null;
spawner = lastSpawner = null;
health = maxHealth();
mining = null;
boostHeat = drownTime = hitTime = 0f;
mech = Mechs.starter;
placeQueue.clear();
@@ -790,7 +800,11 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
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);
}else if(!netServer.isWaitingForPlayers()){
if(!Net.client()){
@@ -810,6 +824,7 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
this.lastSpawner = spawner;
this.dead = true;
setNet(spawner.getX(), spawner.getY());
spawner.updateSpawning(this);
}
//endregion
@@ -817,8 +832,8 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
//region read and write methods
@Override
public boolean isClipped(){
return false;
public byte version(){
return 0;
}
@Override
@@ -833,7 +848,7 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
}
@Override
public void readSave(DataInput stream) throws IOException{
public void readSave(DataInput stream, byte version) throws IOException{
boolean local = stream.readBoolean();
if(local){
@@ -844,14 +859,14 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
lastSpawner = (SpawnerTrait)stile.entity;
}
Player player = headless ? this : Vars.player;
player.readSaveSuper(stream);
player.readSaveSuper(stream, version);
player.mech = content.getByID(ContentType.mech, mechid);
player.dead = false;
}
}
private void readSaveSuper(DataInput stream) throws IOException{
super.readSave(stream);
private void readSaveSuper(DataInput stream, byte version) throws IOException{
super.readSave(stream, version);
add();
}
@@ -859,7 +874,7 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
@Override
public void write(DataOutput buffer) throws IOException{
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.writeInt(Color.rgba8888(color));
buffer.writeByte(mech.id);
@@ -873,7 +888,9 @@ public class Player extends Unit implements BuilderTrait, ShooterTrait{
@Override
public void read(DataInput buffer) throws IOException{
float lastx = x, lasty = y, lastrot = rotation, lastvx = velocity.x, lastvy = velocity.y;
super.readSave(buffer);
super.readSave(buffer, version());
name = TypeIO.readStringData(buffer);
byte bools = buffer.readByte();
isAdmin = (bools & 1) != 0;

View File

@@ -1,14 +1,12 @@
package io.anuke.mindustry.entities.type;
import io.anuke.annotations.Annotations.Loc;
import io.anuke.annotations.Annotations.Remote;
import io.anuke.annotations.Annotations.*;
import io.anuke.arc.Events;
import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.ObjectSet;
import io.anuke.arc.math.geom.Point2;
import io.anuke.arc.math.geom.Vector2;
import io.anuke.arc.util.Interval;
import io.anuke.arc.util.Time;
import io.anuke.arc.util.*;
import io.anuke.mindustry.entities.EntityGroup;
import io.anuke.mindustry.entities.bullet.Bullet;
import io.anuke.mindustry.entities.impl.BaseEntity;
@@ -115,16 +113,35 @@ public class TileEntity extends BaseEntity implements TargetTrait, HealthTrait{
return dead || tile.entity != this;
}
@CallSuper
public void write(DataOutput stream) throws IOException{
stream.writeShort((short)health);
stream.writeByte(Pack.byteByte(tile.getTeamID(), tile.rotation())); //team + rotation
if(items != null) items.write(stream);
if(power != null) power.write(stream);
if(liquids != null) liquids.write(stream);
if(cons != null) cons.write(stream);
}
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{
}
public void readConfig(DataInput stream) throws IOException{
/** Returns the version of this TileEntity IO code.*/
public byte version(){
return 0;
}
public boolean collide(Bullet other){
@@ -168,14 +185,14 @@ public class TileEntity extends BaseEntity implements TargetTrait, HealthTrait{
Point2[] nearby = Edges.getEdges(block.size);
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
if(other != null){
other = other.target();
other.block().onProximityUpdate(other);
}
if(other != null && other.entity != null){
other.entity.proximity.removeValue(tile, true);
if(other.entity != null){
other.entity.proximity.removeValue(tile, true);
}
}
}
}
@@ -186,10 +203,9 @@ public class TileEntity extends BaseEntity implements TargetTrait, HealthTrait{
Point2[] nearby = Edges.getEdges(block.size);
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;
other = other.target();
if(other.entity == null || !(other.interactable(tile.getTeam()))) continue;
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
public EntityGroup targetGroup(){
return tileGroup;

View File

@@ -1,5 +1,6 @@
package io.anuke.mindustry.entities.type;
import io.anuke.annotations.Annotations.Nullable;
import io.anuke.arc.Core;
import io.anuke.arc.Events;
import io.anuke.arc.graphics.Color;
@@ -8,7 +9,8 @@ import io.anuke.arc.graphics.g2d.TextureRegion;
import io.anuke.arc.math.Mathf;
import io.anuke.arc.math.geom.Geometry;
import io.anuke.arc.math.geom.Vector2;
import io.anuke.arc.util.*;
import io.anuke.arc.util.Time;
import io.anuke.arc.util.Tmp;
import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.content.Fx;
import io.anuke.mindustry.entities.*;
@@ -138,7 +140,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
}
@Override
public void readSave(DataInput stream) throws IOException{
public void readSave(DataInput stream, byte version) throws IOException{
byte team = stream.readByte();
boolean dead = stream.readBoolean();
float x = stream.readFloat();
@@ -150,7 +152,7 @@ public abstract class Unit extends DestructibleEntity implements SaveTrait, Targ
byte itemID = stream.readByte();
short itemAmount = stream.readShort();
this.status.readSave(stream);
this.status.readSave(stream, version);
this.item.amount = itemAmount;
this.item.item = content.item(itemID);
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());
}
public TileEntity getClosestCore(){
public @Nullable TileEntity getClosestCore(){
TeamData data = state.teams.get(team);
Tile tile = Geometry.findClosest(x, y, data.cores);

View File

@@ -58,7 +58,7 @@ public class Drone extends FlyingUnit implements BuilderTrait{
if(isBreaking){
getPlaceQueue().addLast(new BuildRequest(entity.tile.x, entity.tile.y));
}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));
}
}
@@ -211,7 +211,7 @@ public class Drone extends FlyingUnit implements BuilderTrait{
Events.on(BuildSelectEvent.class, event -> {
EntityGroup<BaseUnit> group = unitGroups[event.team.ordinal()];
if(!(event.builder instanceof Player) || !(event.tile.entity instanceof BuildEntity)) return;
if(!(event.tile.entity instanceof BuildEntity)) return;
for(BaseUnit unit : group.all()){
if(unit instanceof Drone){

View File

@@ -130,7 +130,7 @@ public class Statuses implements Saveable{
}
@Override
public void readSave(DataInput stream) throws IOException{
public void readSave(DataInput stream, byte version) throws IOException{
for(StatusEntry effect : statuses){
Pools.free(effect);
}

View File

@@ -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}. */
public abstract class Content{
public final byte id;
public final short id;
public Content(){
this.id = (byte)Vars.content.getBy(getContentType()).size;
this.id = (short)Vars.content.getBy(getContentType()).size;
Vars.content.handleContent(this);
}

View File

@@ -1,49 +1,64 @@
package io.anuke.mindustry.game;
import io.anuke.arc.Core;
import io.anuke.arc.function.Supplier;
import io.anuke.arc.function.Consumer;
/** Defines preset rule sets.. */
public enum Gamemode{
survival(() -> new Rules(){{
waveTimer = true;
waves = true;
unitDrops = true;
spawns = DefaultWaves.get();
}}),
sandbox(() -> new Rules(){{
infiniteResources = true;
waves = true;
waveTimer = false;
respawnTime = 0f;
}}),
attack(() -> new Rules(){{
enemyCheat = true;
unitDrops = true;
waves = false;
attackMode = true;
}}),
pvp(() -> new Rules(){{
pvp = true;
enemyCoreBuildRadius = 600f;
respawnTime = 60 * 10;
buildCostMultiplier = 0.5f;
buildSpeedMultiplier = 2f;
playerDamageMultiplier = 0.45f;
playerHealthMultiplier = 0.8f;
unitBuildSpeedMultiplier = 3f;
unitHealthMultiplier = 2f;
attackMode = true;
}});
survival(rules -> {
rules.waveTimer = true;
rules.waves = true;
rules.unitDrops = true;
}),
sandbox(rules -> {
rules.infiniteResources = true;
rules.waves = true;
rules.waveTimer = false;
rules.respawnTime = 0f;
}),
attack(rules -> {
rules.enemyCheat = true;
rules.unitDrops = true;
rules.waves = false;
rules.attackMode = true;
}),
pvp(rules -> {
rules.pvp = true;
rules.enemyCoreBuildRadius = 600f;
rules.respawnTime = 60 * 10;
rules.buildCostMultiplier = 0.5f;
rules.buildSpeedMultiplier = 2f;
rules.playerDamageMultiplier = 0.45f;
rules.playerHealthMultiplier = 0.8f;
rules.unitBuildSpeedMultiplier = 3f;
rules.unitHealthMultiplier = 2f;
rules.attackMode = true;
}),
editor(true, rules -> {
rules.infiniteResources = true;
rules.editor = true;
rules.waves = false;
rules.enemyCoreBuildRadius = 0f;
rules.waveTimer = false;
rules.respawnTime = 0f;
});
private final Supplier<Rules> rules;
private final Consumer<Rules> rules;
public final boolean hidden;
Gamemode(Supplier<Rules> rules){
this.rules = rules;
Gamemode(Consumer<Rules> rules){
this(false, rules);
}
public Rules get(){
return rules.get();
Gamemode(boolean hidden, Consumer<Rules> rules){
this.rules = rules;
this.hidden = hidden;
}
/** Applies this preset to this ruleset. */
public Rules apply(Rules in){
rules.accept(in);
return in;
}
public String description(){

View File

@@ -2,6 +2,8 @@ package io.anuke.mindustry.game;
import io.anuke.annotations.Annotations.Serialize;
import io.anuke.arc.collection.Array;
import io.anuke.mindustry.io.JsonIO;
import io.anuke.mindustry.type.Zone;
/**
* Defines current rules on how the game should function.
@@ -47,10 +49,10 @@ public class Rules{
public float bossWaveMultiplier = 3f;
/** How many times longer a launch wave takes. */
public float launchWaveMultiplier = 2f;
/** Zone ID, -1 for invalid zone. */
public byte zone = -1;
/** Zone for saves that have them.*/
public Zone 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. */
public boolean limitedRespawns = false;
/** How many times player can respawn during one wave. */
@@ -59,4 +61,11 @@ public class Rules{
public boolean waitForWaveToEnd = false;
/** Determinates if gamemode is attack mode */
public boolean attackMode = false;
/** Whether this is the editor gamemode. */
public boolean editor = false;
/** Copies this ruleset exactly. Not very efficient at all, do not use often. */
public Rules copy(){
return JsonIO.read(Rules.class, JsonIO.write(this));
}
}

View File

@@ -4,22 +4,21 @@ import io.anuke.arc.Core;
import io.anuke.arc.Events;
import io.anuke.arc.collection.*;
import io.anuke.arc.files.FileHandle;
import io.anuke.arc.util.Strings;
import io.anuke.arc.util.Time;
import io.anuke.arc.util.*;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.game.EventType.StateChangeEvent;
import io.anuke.mindustry.io.SaveIO;
import io.anuke.mindustry.io.SaveIO.SaveException;
import io.anuke.mindustry.io.SaveMeta;
import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.type.ContentType;
import io.anuke.mindustry.type.Zone;
import java.io.IOException;
import java.text.SimpleDateFormat;
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{
private int nextSlot;
@@ -52,7 +51,7 @@ public class Saves{
SaveSlot slot = new SaveSlot(index);
saves.add(slot);
saveMap.put(slot.index, slot);
slot.meta = SaveIO.getData(index);
slot.meta = SaveIO.getMeta(index);
nextSlot = Math.max(index + 1, nextSlot);
}
}
@@ -134,7 +133,7 @@ public class Saves{
slot.setName(file.nameWithoutExtension());
saves.add(slot);
saveMap.put(slot.index, slot);
slot.meta = SaveIO.getData(slot.index);
slot.meta = SaveIO.getMeta(slot.index);
current = slot;
saveSlots();
return slot;
@@ -172,7 +171,7 @@ public class Saves{
public void load() throws SaveException{
try{
SaveIO.loadFromSlot(index);
meta = SaveIO.getData(index);
meta = SaveIO.getMeta(index);
current = this;
totalPlaytime = meta.timePlayed;
}catch(Exception e){
@@ -186,7 +185,7 @@ public class Saves{
totalPlaytime = time;
SaveIO.saveToSlot(index);
meta = SaveIO.getData(index);
meta = SaveIO.getMeta(index);
if(!state.is(State.menu)){
current = this;
}
@@ -224,7 +223,7 @@ public class Saves{
}
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(){

View File

@@ -9,7 +9,7 @@ import io.anuke.mindustry.type.*;
@Serialize
public class Stats{
/** 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. */
public int enemyUnitsDestroyed;
/** Total waves lasted. */

View File

@@ -78,7 +78,7 @@ public class BlockRenderer implements Disposable{
for(int y = 0; y < world.height(); y++){
Tile tile = world.rawTile(x, y);
int edgeBlend = 2;
float rot = tile.getRotation();
float rot = tile.rotation();
boolean fillable = (tile.block().solid && tile.block().fillsTile && !tile.block().synthetic());
int edgeDst = Math.min(x, Math.min(y, Math.min(Math.abs(x - (world.width() - 1)), Math.abs(y - (world.height() - 1)))));
if(edgeDst <= edgeBlend){

View File

@@ -62,7 +62,7 @@ public class FloorRenderer implements Disposable{
//loop through all layers, and add layer index if it exists
for(int i = 0; i < layers; i++){
if(chunk.caches[i] != -1){
if(chunk.caches[i] != -1 && i != CacheLayer.walls.ordinal()){
drawnLayerSet.add(i);
}
}

View File

@@ -80,7 +80,7 @@ public class MinimapRenderer implements Disposable{
for(Unit unit : units){
float rx = (unit.x - rect.x) / rect.width * w, ry = (unit.y - rect.y) / rect.width * h;
Draw.color(unit.getTeam().color);
Fill.rect(x + rx, y + ry, baseSize / 2f, baseSize / 2f);
Fill.rect(x + rx, y + ry, io.anuke.arc.scene.ui.layout.Unit.dp.scl(baseSize / 2f), io.anuke.arc.scene.ui.layout.Unit.dp.scl(baseSize / 2f));
}
Draw.color();
@@ -129,7 +129,7 @@ public class MinimapRenderer implements Disposable{
}
private int colorFor(Tile tile){
tile = tile.target();
tile = tile.link();
return MapIO.colorFor(tile.floor(), tile.block(), tile.overlay(), tile.getTeam());
}

View File

@@ -97,11 +97,10 @@ public class OverlayRenderer{
//draw selected block bars and info
if(input.block == null && !Core.scene.hasMouse()){
Vector2 vec = Core.input.mouseWorld(input.getMouseX(), input.getMouseY());
Tile tile = world.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()){
Tile target = tile.target();
target.block().drawSelect(target);
if(tile != null && tile.block() != Blocks.air && tile.getTeam() == player.getTeam()){
tile.block().drawSelect(tile);
}
}
@@ -113,8 +112,7 @@ public class OverlayRenderer{
Lines.circle(v.x, v.y, 6 + Mathf.absin(Time.time(), 5f, 1f));
Draw.reset();
Tile tile = world.tileWorld(v.x, v.y);
if(tile != null) tile = tile.target();
Tile tile = world.ltileWorld(v.x, v.y);
if(tile != null && tile.interactable(player.getTeam()) && tile.block().acceptStack(player.item().item, player.item().amount, tile, player) > 0){
Draw.color(Pal.place);
Lines.square(tile.drawx(), tile.drawy(), tile.block().size * tilesize / 2f + 1 + Mathf.absin(Time.time(), 5f, 1f));

View File

@@ -5,7 +5,6 @@ import io.anuke.arc.graphics.g2d.*;
import io.anuke.arc.math.Mathf;
import io.anuke.arc.util.Tmp;
//TODO remove
public class Shapes{
public static void laser(String line, String edge, float x, float y, float x2, float y2, float scale){

View File

@@ -96,9 +96,8 @@ public class DesktopInput extends InputHandler{
for(int x = dresult.x; x <= dresult.x2; x++){
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;
tile = tile.target();
Draw.color(Pal.removeBack);
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());
if(cursor != null){
cursor = cursor.target();
cursor = cursor.link();
cursorType = cursor.block().getCursor(cursor);
@@ -257,7 +256,7 @@ public class DesktopInput extends InputHandler{
}
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;

View File

@@ -154,7 +154,7 @@ public abstract class InputHandler implements InputProcessor{
/** Handles tile tap events that are not platform specific. */
boolean tileTapped(Tile tile){
tile = tile.target();
tile = tile.link();
boolean consumed = false, showedInventory = false;
@@ -195,16 +195,17 @@ public abstract class InputHandler implements InputProcessor{
}
}
if(!showedInventory){
frag.inv.hide();
}
if(!consumed && player.isBuilding()){
//clear when the player taps on something else
if(!consumed && !mobile && player.isBuilding() && block == null){
player.clearBuilding();
block = null;
return true;
}
if(!showedInventory){
frag.inv.hide();
}
return consumed;
}
@@ -331,7 +332,7 @@ public abstract class InputHandler implements InputProcessor{
}
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));
}

View File

@@ -87,8 +87,7 @@ public class MobileInput extends InputHandler implements GestureListener{
player.setMineTile(null);
player.target = unit;
}else{
Tile tile = world.tileWorld(x, y);
if(tile != null) tile = tile.target();
Tile tile = world.ltileWorld(x, y);
if(tile != null && tile.synthetic() && state.teams.areEnemies(player.getTeam(), tile.getTeam())){
TileEntity entity = tile.entity;
@@ -276,12 +275,11 @@ public class MobileInput extends InputHandler implements GestureListener{
}
}).update(l -> l.setChecked(mode == breaking));
//cancel button
table.addImageButton("icon-cancel", "clear-partial", 16 * 2f, () -> {
player.clearBuilding();
mode = none;
block = null;
}).visible(() -> player.isBuilding() || block != null || mode == breaking);
//diagonal swap button
table.addImageButton("icon-diagonal", "clear-toggle-partial", 16 * 2f, () -> {
Core.settings.put("swapdiagonal", !Core.settings.getBool("swapdiagonal"));
Core.settings.save();
}).update(l -> l.setChecked(Core.settings.getBool("swapdiagonal")));
//rotate button
table.addImageButton("icon-arrow", "clear-partial", 16 * 2f, () -> rotation = Mathf.mod(rotation + 1, 4))
@@ -311,6 +309,15 @@ public class MobileInput extends InputHandler implements GestureListener{
selection.clear();
selecting = false;
}).visible(() -> !selection.isEmpty());
Core.scene.table(t -> {
t.bottom().left().visible(() -> player.isBuilding() || block != null || mode == breaking);
t.addImageTextButton("$cancel", "icon-cancel", 16*2, () -> {
player.clearBuilding();
mode = none;
block = null;
}).width(155f);
});
}
@Override
@@ -408,9 +415,8 @@ public class MobileInput extends InputHandler implements GestureListener{
for(int x = dresult.x; x <= dresult.x2; x++){
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;
other = other.target();
Draw.color(Pal.removeBack);
Lines.square(other.drawx(), other.drawy() - 1, other.block().size * tilesize / 2f - 1);
@@ -506,12 +512,10 @@ public class MobileInput extends InputHandler implements GestureListener{
int wx = lineStartX + x * Mathf.sign(tileX - lineStartX);
int wy = lineStartY + y * Mathf.sign(tileY - lineStartY);
Tile tar = world.tile(wx, wy);
Tile tar = world.ltile(wx, wy);
if(tar == null) continue;
tar = tar.target();
if(!hasRequest(world.tile(tar.x, tar.y)) && validBreak(tar.x, tar.y)){
PlaceRequest request = new PlaceRequest(tar.x, tar.y);
request.scale = 1f;
@@ -527,7 +531,7 @@ public class MobileInput extends InputHandler implements GestureListener{
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;
}
@@ -577,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)){
//add to selection queue if it's a valid place position
selection.add(lastPlaced = new PlaceRequest(cursor.x, cursor.y, block, rotation));
}else if(mode == breaking && validBreak(cursor.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
cursor = cursor.target();
cursor = cursor.link();
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);
}

View 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);
}
}

View File

@@ -0,0 +1,227 @@
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);
if(block == null) block = Blocks.air;
Tile tile = tiles.get(x, y);
//the spawn block is saved in the block tile layer in older maps, shift it to the overlay
if(block != Blocks.spawn){
tile.setBlock(block);
}else{
tile.setOverlay(block);
}
if(namemap.get(id, "").equals("part")){
stream.readByte(); //link
}else if(tile.entity != null){
byte tr = stream.readByte();
stream.readShort(); //read health (which is actually irrelevant)
byte team = Pack.leftByte(tr);
byte rotation = Pack.rightByte(tr);
tile.setTeam(Team.all[team]);
tile.entity.health = tile.block().health;
tile.rotation(rotation);
if(tile.block() == Blocks.liquidSource || tile.block() == Blocks.unloader || tile.block() == Blocks.sorter){
stream.readByte(); //these blocks have an extra config byte, read it
}
}else{ //no entity/part, read consecutives
int consecutives = stream.readUnsignedByte();
for(int j = i + 1; j < i + 1 + consecutives; j++){
int newx = j % width, newy = j / width;
tiles.get(newx, newy).setBlock(block);
}
i += consecutives;
}
}
}finally{
content.setTemporaryMapper(null);
}
}
}
}
/** Reads a pixmap in the 3.5 pixmap format. */
public static void readPixmap(Pixmap pixmap, Tile[][] tiles){
for(int x = 0; x < pixmap.getWidth(); x++){
for(int y = 0; y < pixmap.getHeight(); y++){
int color = pixmap.getPixel(x, pixmap.getHeight() - 1 - y);
LegacyBlock block = LegacyColorMapper.get(color);
Tile tile = tiles[x][y];
tile.setFloor(block.floor);
tile.setBlock(block.wall);
if(block.ore != null) tile.setOverlay(block.ore);
//place core
if(color == Color.rgba8888(Color.GREEN)){
for(int dx = 0; dx < 3; dx++){
for(int dy = 0; dy < 3; dy++){
int worldx = dx - 1 + x;
int worldy = dy - 1 + y;
//multiblock parts
if(Structs.inBounds(worldx, worldy, pixmap.getWidth(), pixmap.getHeight())){
Tile write = tiles[worldx][worldy];
write.setBlock(BlockPart.get(dx - 1, dy - 1));
write.setTeam(Team.blue);
}
}
}
//actual core parts
tile.setBlock(Blocks.coreShard);
tile.setTeam(Team.blue);
}
}
}
}
}

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