sourceSets.main.java.srcDirs = ["src/"] import arc.files.* import arc.func.* import arc.graphics.* import arc.packer.* import arc.struct.* import arc.util.* import arc.util.io.* import groovy.transform.* import java.util.concurrent.* def genFolder = "../core/assets-raw/sprites_out/generated/" def enableAA = true @CompileStatic static void antialias(File file){ Pixmap result = new Pixmap(new Fi(file)) Pixmaps.antialias(result) new Fi(file).writePng(result) result.dispose() } tasks.register('antialiasImages'){ doLast{ for(def img : project.getProperty("images").split(",")){ println(project.getProperty("startdir") + "/" + img) antialias(new File(project.getProperty("startdir") + "/" + img)) } } } tasks.register('tileImages'){ doLast{ for(def img : project.getProperty("images").split(",")){ println(project.getProperty("startdir") + "/" + img) tileImage(new File(project.getProperty("startdir") + "/" + img)) } } } tasks.register('pack'){ dependsOn = [classes, configurations.runtimeClasspath] doLast{ //cleanup old sprites delete{ delete "../core/assets-raw/sprites_out/" } //copy in new sprites copy{ from "../core/assets-raw/sprites/" into "../core/assets-raw/sprites_out/" } new File(rootDir, "core/build/last_pack_version").text = versionNumber //run generation task; generate all needed sprites file(genFolder).mkdirs() project.services.get(ExecOperations).javaexec{ mainClass = "mindustry.tools.ImagePacker" classpath = sourceSets.main.runtimeClasspath workingDir = genFolder } copy{ from "../core/assets-raw/sprites_out/ui/icons" into "../core/assets-raw/sprites_out/ui/" } delete{ delete "../core/assets-raw/sprites_out/ui/icons" } if(enableAA){ ExecutorService executor = Executors.newFixedThreadPool(OS.cores) long ms = System.currentTimeMillis() //antialias everything except UI elements fileTree(dir: new File(rootDir, 'core/assets-raw/sprites_out/').absolutePath, include: "**/*.png").visit{ file -> if(file.isDirectory() || (file.toString().replace("\\", "/").contains("/ui/") && file.toString().startsWith("icon-")) || file.toString().contains(".9.png") || file.toString().contains("aaaa")) return executor.submit{ antialias(file.file) } } Threads.await(executor) println "Time taken for AA: ${(System.currentTimeMillis() - ms) / 1000f} seconds" } println("\n\nPacking normal 4096 sprites...\n\n") //pack normal sprites TexturePacker.process(new File(rootDir, "core/assets-raw/sprites_out/").absolutePath, new File(rootDir, "core/assets/sprites/").absolutePath, "sprites.aatls") println("\n\nPacking fallback 2048 sprites...\n\n") //replace config file contents fileTree(dir: '../core/assets-raw/sprites_out/', include: "**/*.json").visit{ file -> if(!file.isDirectory()) file.file.text = file.file.text.replace("4096", "2048") } //pack fallback 2048x2048 sprites - disabled when debugging if(!project.hasProperty("args")){ TexturePacker.process(new File(rootDir, "core/assets-raw/sprites_out/").absolutePath, new File(rootDir, "core/assets/sprites/fallback/").absolutePath, "sprites.aatls") } } } tasks.register('genSprites', JavaExec){ dependsOn classes finalizedBy 'antialiasGen' mainClass = "mindustry.tools.ImagePacker" classpath = sourceSets.main.runtimeClasspath standardInput = System.in workingDir = genFolder } tasks.register('fontgen', JavaExec){ dependsOn classes /* icon font pipeline: 1. take set of pre-defined icons and SVGs 2. use Fontello API to get a font with these 3. combine fontello font and standard font, get output font 4. use json to generate a file with constants for every icon size+type (during annotation processing) */ doLast{ Fi folder = Fi.get("core/assets-raw/fontgen/out/") folder.mkdirs() Log.info("Session...") OS.exec("curl", "--fail", "--output", "core/assets-raw/fontgen/out/session", "--form", "config=@core/assets-raw/fontgen/config.json", "https://fontello.com") Log.info("Zip...") String session = folder.child("session").readString() Http.get("https://fontello.com/" + session + "/get").block(result -> { Streams.copy(result.getResultAsStream(), folder.child("font.zip").write()) }) Log.info("Icon font...") ZipFi zip = new ZipFi(folder.child("font.zip")) Fi dest = folder.child("font.woff") zip.list()[0].child("font").child("fontello.ttf").copyTo(dest) dest.copyTo(Fi.get("core/assets/fonts/icon.ttf")) Log.info("Merge...") //TODO this is broken Log.info(OS.exec("fontforge", "-script", Fi.get("core/assets-raw/fontgen/merge.pe").absolutePath(), Fi.get("core/assets/fonts/font.woff").absolutePath(), Fi.get("core/assets-raw/fontgen/out/font.woff").absolutePath()) ) Log.info("Done.") } } tasks.register('icongen', JavaExec){ dependsOn classes mainClass = "mindustry.tools.IconConverter" classpath = sourceSets.main.runtimeClasspath standardInput = System.in workingDir = "../core/assets-raw" } tasks.register('updateScripts', JavaExec){ dependsOn classes mainClass = "mindustry.tools.ScriptMainGenerator" classpath = sourceSets.main.runtimeClasspath standardInput = System.in workingDir = "../core/assets" } tasks.register('fixMaps', JavaExec){ dependsOn classes mainClass = "mindustry.tools.MapFixer" classpath = sourceSets.main.runtimeClasspath standardInput = System.in workingDir = "../core/assets" } tasks.register('updateBundles'){ doLast{ def uniEscape = { String string -> StringBuilder outBuffer = new StringBuilder() int len = string.length() for(int i = 0; i < len; i++){ char ch = string.charAt(i) if((ch > 61) && (ch < 127)){ outBuffer.append(ch == (char) '\\' ? "\\\\" : ch) continue } if(ch >= 0xE000 && ch <= 0xF8FF){ String hex = Integer.toHexString((int) ch) outBuffer.append("\\u") for(int j = 0; j < 4 - hex.length(); j++){ outBuffer.append('0') } outBuffer.append(hex) }else{ outBuffer.append(ch) } } return outBuffer.toString() } OrderedMap base = new OrderedMap<>() PropertiesUtils.load(base, Fi.get("core/assets/bundles/bundle.properties").reader()) //map of line number to comment (or just empty string for blank line) content IntMap commentAndBlankLines = new IntMap<>() var lines = Fi.get("core/assets/bundles/bundle.properties").reader().readLines() int offset = 0 for(int i = 0; i < lines.size(); i++){ var line = lines.get(i) if(i > 0 && lines.get(i - 1).endsWith("\\")) offset++ //multiline value escaped with \\ at end of line else if(line.isEmpty() || line.startsWith("#")) commentAndBlankLines.put(i - offset, line) } Log.info("Updating bundles...") Fi.get("core/assets/bundles").walk(child -> { if(child.name() == "bundle.properties" || child.name() == "global.properties" || child.toString().contains("output")) return if(project.hasProperty("bundle") && child.name() != project.property("bundle")) return Log.info("| @", child.nameWithoutExtension()) OrderedMap other = new OrderedMap<>() ObjectMap extras = new OrderedMap<>() Seq removals = new Seq<>() PropertiesUtils.load(other, child.reader()) for(String key : other.orderedKeys()){ if(!base.containsKey(key) && key.contains(".details") && false){ extras.put(key, other.get(key)) }else if(!base.containsKey(key)){ removals.add(key) Log.info("&lr- Removing unused key '@'...", key) } } if(removals.size > 0) Log.info("&lr@ keys removed.", removals.size) for(String s : removals){ other.remove(s) } int added = 0 for(String key : base.orderedKeys()){ if(other.get(key) == null || other.get(key).trim().isEmpty()){ other.put(key, base.get(key)) added++ Log.info("&lc- Adding missing key '@'...", key) } } Func2 processor = (key, value) -> (key + " =" + (value.trim().isEmpty() ? "" : " ") + uniEscape(value)).replace("\n", "\\n") + "\n" Fi output = child.sibling("output/" + child.name()) if(added > 0) Log.info("&lc@ keys added.", added) if(removals.size + added > 0) Log.info("Writing bundle to @", output) StringBuilder result = new StringBuilder() int i = 0 //add everything ordered for(String key : base.orderedKeys().copy().add(extras.keys().toSeq())){ //append any comments or blank lines as needed to match the english bundle while(commentAndBlankLines.containsKey(i++)) result.append(commentAndBlankLines.get(i - 1) + "\n") if(other.get(key) == null) continue result.append(processor.get(key, other.get(key))) other.remove(key) } child.writeString(result.toString()) }) } }