Generate icons for content in data patch editor

This commit is contained in:
Anuken
2026-06-15 01:50:48 -04:00
parent 4d3cb54f7e
commit 6a9d23a2ef
18 changed files with 274 additions and 56 deletions
@@ -173,7 +173,7 @@ public abstract class UnlockableContent extends MappableContent{
if(!makeNew || !packer.has(name + "-outline")){
String regName = name + (makeNew ? "-outline" : "");
if(packer.registerOutlined(regName)){
PixmapRegion base = Core.atlas.getPixmap(region);
PixmapRegion base = packer.get(region);
var result = Pixmaps.outline(base, outlineColor, outlineRadius);
Drawf.checkBleed(result);
packer.add(page, regName, result);
@@ -185,7 +185,7 @@ public abstract class UnlockableContent extends MappableContent{
protected void makeOutline(MultiPacker packer, TextureRegion region, String name, Color outlineColor, int outlineRadius){
if(region.found() && packer.registerOutlined(name)){
PixmapRegion base = Core.atlas.getPixmap(region);
PixmapRegion base = packer.get(region);
var result = Pixmaps.outline(base, outlineColor, outlineRadius);
Drawf.checkBleed(result);
packer.add(PageType.main, name, result);
@@ -116,6 +116,7 @@ public class MapAssetsDialog extends BaseDialog{
//make sure new assets appear correctly when switching to the content view
if(type == DataAssetType.content){
state.data.reloadContent(false);
state.data.regenerateContentSprites(false);
}
rebuild();
@@ -153,6 +154,9 @@ public class MapAssetsDialog extends BaseDialog{
Seq<DataAsset> results = new Seq<>();
Fi zipped = new ZipFi(targetFile);
Seq<String> errors = new Seq<>();
//force regen
state.data.clearGeneratedImages();
for(var type : DataAssetType.all){
Fi folder = zipped.child(type.folder);
@@ -208,6 +212,10 @@ public class MapAssetsDialog extends BaseDialog{
prev.addAll(results);
prev.sort();
state.data.load(prev);
if(state.data.getContent().size > 0){
//TODO: packs twice (bad)
state.data.regenerateContentSprites(false);
}
rebuild();
}
@@ -219,7 +227,7 @@ public class MapAssetsDialog extends BaseDialog{
ep.add(Seq.with(errors).toString("\n", s -> "[gray]- []" + s)).labelAlign(Align.left, Align.left).grow();
});
}else if(results.size == 0){
idiag.cont.add("@asset.image.none");
idiag.cont.add("@asset.import.none");
}
idiag.buttons.button("@ok", Icon.ok, idiag::hide).size(200f, 64f);
idiag.show();
@@ -102,8 +102,11 @@ public class MapContentView implements AssetView{
list.button(Icon.refresh, Styles.graySquarei, Vars.iconMed, () -> {
Cons<String> handler = str -> {
content.data = str;
state.data.reloadContent(false);
diag.rebuild();
ui.loadAnd(() -> {
state.data.reloadContent(false);
state.data.regenerateContentSprites(false);
diag.rebuild();
});
};
BaseDialog dialog = new BaseDialog("@editor.import");
@@ -176,10 +179,14 @@ public class MapContentView implements AssetView{
buttons.button("@add", Icon.add, () -> showImport(diag)).size(190f, 64f);
}
void addContent(ContentType type, String path, String data){
ContentAsset asset = new ContentAsset(path, type, data);
state.data.getContent().add(asset);
state.data.reloadContent(false);
void addContent(MapAssetsDialog diag, ContentType type, String path, String data){
ui.loadAnd(() -> {
ContentAsset asset = new ContentAsset(path, type, data);
state.data.getContent().add(asset);
state.data.reloadContent(false);
state.data.regenerateContentSprites(false);
diag.rebuild();
});
}
void showImport(MapAssetsDialog diag){
@@ -220,7 +227,7 @@ public class MapContentView implements AssetView{
choose.buttons.button("@asset.content.import.file", Icon.fileText, () -> FileChooser.open("json", "json5", "hjson").submit(file -> {
choose.hide();
addContent(selected[0], nameField.getText() + ".json", file.readString());
addContent(diag, selected[0], nameField.getText() + ".json", file.readString());
diag.rebuild();
})).size(200f, 64f).disabled(b -> nameField.getText().isEmpty() || invalid[0]);
@@ -229,8 +236,7 @@ public class MapContentView implements AssetView{
if(text == null) return;
choose.hide();
addContent(selected[0], nameField.getText() + ".json", text);
diag.rebuild();
addContent(diag, selected[0], nameField.getText() + ".json", text);
}).size(200f, 64f).disabled(b -> nameField.getText().isEmpty() || invalid[0] || Core.app.getClipboardText() == null);
choose.show();
}
@@ -32,6 +32,7 @@ public class MapImagesView implements AssetView{
buttons.button("@add", Icon.add, () -> {
FileChooser.open("png", "jpeg", "jpg").submitMulti(results -> {
var images = getImages();
state.data.clearGeneratedImages();
for(var result : results){
try{
@@ -49,14 +50,13 @@ public class MapImagesView implements AssetView{
String name = result.nameWithoutExtension();
String path = result.name();
var other = images.find(p -> (p.path.equalsIgnoreCase(path) || p.name.equalsIgnoreCase(name)));
var other = images.find(p -> p.path.equalsIgnoreCase(path) || p.name.equalsIgnoreCase(name));
if(other != null){
ui.showErrorMessage(Core.bundle.format("asset.image.exists", other.name + " (" + other.path + ")"));
return;
}
byte[] hash = assetCache.add(bytes);
images.add(new ImageAsset(path, hash));
}catch(Exception e){
ui.showException(e);
@@ -64,7 +64,7 @@ public class MapImagesView implements AssetView{
}
images.sort();
state.data.reloadImages(images);
state.data.regenerateContentSprites(true);
diag.rebuild();
});
}).size(190f, 64f);
@@ -132,6 +132,8 @@ public class MapImagesView implements AssetView{
zipped.delete(); //closes the zip file
int imported = 0;
state.data.clearGeneratedImages();
for(var future : images){
var image = (Result)future.get();
if(image != null){
@@ -141,11 +143,12 @@ public class MapImagesView implements AssetView{
}
String path = image.path.pathWithoutExtension(), name = image.path.nameWithoutExtension();
var other = getImages().find(op -> (op.path.equalsIgnoreCase(path) || op.name.equalsIgnoreCase(name)));
var other = getImages().find(op -> !op.isGenerated() && (op.path.equalsIgnoreCase(path) || op.name.equalsIgnoreCase(name)));
if(other != null){
errors.add("[accent]" + image.path + "[white]: " + Core.bundle.format("asset.image.exists", other.name + " (" + other.path + ")").replace("\n", " "));
continue;
}
getImages().removeAll(i -> i.isGenerated() && i.name.equals(name));
getImages().add(new ImageAsset(path, image.hash));
imported ++;
}
@@ -154,6 +157,7 @@ public class MapImagesView implements AssetView{
getImages().sort();
state.data.reloadImages(getImages());
state.data.regenerateContentSprites(true);
diag.rebuild();
var idiag = new BaseDialog("@asset.image.imports");
@@ -211,7 +215,10 @@ public class MapImagesView implements AssetView{
int cols = (int)Math.max(1, Core.graphics.getWidth() / Scl.scl(size + 12f));
int i = 0;
for(var image : getImages()){
//showing generated images is confusing, so don't.
if(image.isGenerated()) continue;
if(diag.searchString != null && !image.path.toLowerCase().contains(diag.searchString)) continue;
TextureRegion region = Core.atlas.find(regionPrefix + image.name, "nomap");
boolean found = Core.atlas.has(regionPrefix + image.name);
@Nullable Fi cacheFile = image.getCacheFile();
@@ -254,10 +261,12 @@ public class MapImagesView implements AssetView{
var istyle = Styles.emptyi;
b.button(Icon.pencil, istyle, () -> {
ui.showTextInput("@save.rename", "@patch.path", image.path, res -> {
if(!res.endsWith(".png")) res = res + ".png";
Fi fi = new Fi(res);
String name = fi.nameWithoutExtension();
String path = fi.pathWithoutExtension();
var other = getImages().find(p -> p != image && (p.path.equalsIgnoreCase(path) || p.name.equalsIgnoreCase(name)));
var other = getImages().find(p -> p != image && !p.isGenerated() && (p.path.equalsIgnoreCase(path) || p.name.equalsIgnoreCase(name)));
if(other != null){
ui.showErrorMessage(Core.bundle.format("asset.image.exists", other.name + " (" + other.path + ")"));
}else{
@@ -269,7 +278,7 @@ public class MapImagesView implements AssetView{
at.name = regionPrefix + name;
}
image.setPath(path);
image.setPath(fi.path());
image.name = name;
diag.rebuild();
}
+21 -9
View File
@@ -1,5 +1,6 @@
package mindustry.graphics;
import arc.*;
import arc.graphics.*;
import arc.graphics.Texture.*;
import arc.graphics.g2d.*;
@@ -9,17 +10,31 @@ import arc.util.Log.*;
import mindustry.*;
public class MultiPacker implements Disposable{
private PixmapPacker[] packers = new PixmapPacker[PageType.all.length];
private PixmapPacker[] packers;
private ObjectSet<String> outlined = new ObjectSet<>();
public MultiPacker(){
for(int i = 0; i < packers.length; i++){
packers[i] = new PixmapPacker(Math.min(Vars.maxTextureSize, PageType.all[i].width), Math.min(Vars.maxTextureSize, PageType.all[i].height), 2, true);
this(true);
}
public MultiPacker(boolean initialize){
if(initialize){
packers = new PixmapPacker[PageType.all.length];
for(int i = 0; i < packers.length; i++){
packers[i] = new PixmapPacker(Math.min(Vars.maxTextureSize, PageType.all[i].width), Math.min(Vars.maxTextureSize, PageType.all[i].height), 2, true);
}
}
}
@Nullable
public PixmapRegion get(String name){
public PixmapRegion get(TextureRegion region){
return Core.atlas.getPixmap(region);
}
public PixmapRegion get(String region){
return Core.atlas.getPixmap(region);
}
public @Nullable PixmapRegion getPacked(String name){
for(var packer : packers){
var region = packer.getRegion(name);
if(region != null){
@@ -59,10 +74,6 @@ public class MultiPacker implements Disposable{
return outlined.contains(name);
}
public PixmapPacker getPacker(PageType type){
return packers[type.ordinal()];
}
public boolean has(String name){
for(var page : PageType.all){
if(packers[page.ordinal()].getRect(name) != null){
@@ -97,6 +108,7 @@ public class MultiPacker implements Disposable{
@Override
public void dispose(){
if(packers == null) return;
for(int i = 0; i < PageType.all.length; i ++){
var packer = packers[i];
//the UI packer's image is later used when merging with the font, don't dispose it
+21 -2
View File
@@ -70,8 +70,27 @@ public class DataImagePacker{
TextureRegion envReserveRegion = Core.atlas.find("data-patch-reserved-env");
PixmapPacker envPacker = anyEnv ? new PixmapPacker(envReserveRegion.width, envReserveRegion.height, 2, true) : null;
var tasks = new Seq<Future<?>>();
Seq<ImageAsset> toPack = new Seq<>(), pending = new Seq<>();
ObjectSet<String> generatedNames = new ObjectSet<>();
//this makes sure that generated images are prioritized
for(var image : images){
if(image.isGenerated()){
generatedNames.add(image.name);
toPack.add(image);
}else{
pending.add(image);
}
}
for(var image : pending){
if(!generatedNames.contains(image.name)){
toPack.add(image);
}
}
var tasks = new Seq<Future<?>>();
for(var image : toPack){
tasks.add(Vars.mainExecutor.submit(() -> {
Fi cacheFile = image.getCacheFile();
//logged elsewhere
@@ -131,7 +150,7 @@ public class DataImagePacker{
printStats(packer, envPacker);
packer.dispose();
packer.forceDispose();
//textures are never updated, so force disposal
if(envPacker != null) envPacker.forceDispose();
+152 -4
View File
@@ -1,7 +1,14 @@
package mindustry.mod;
import arc.files.*;
import arc.graphics.*;
import arc.graphics.g2d.*;
import arc.graphics.g2d.TextureAtlas.*;
import arc.struct.*;
import arc.util.*;
import mindustry.*;
import mindustry.ctype.*;
import mindustry.graphics.*;
import mindustry.mod.data.*;
public class DataManager{
@@ -22,6 +29,139 @@ public class DataManager{
rebuildOrderedAssets();
}
public void regenerateContentSprites(boolean forcePack){
var allContent = patcher.getAddedContent();
//TODO: what to do when headless (server?)
if(allContent.isEmpty()){
if(forcePack) reloadImages();
return;
}
Seq<UnlockableContent> contentToPack = allContent.select(c -> c instanceof UnlockableContent && c.minfo.asset != null).as();
ObjectSet<String> existingContentHashes = new ObjectSet<>();
ObjectSet<String> imagesWithHashes = new ObjectSet<>();
ObjectMap<UnlockableContent, String> hashes = new ObjectMap<>();
//store hashes based on JSON contents
for(var content : contentToPack){
String hash = content.minfo.asset.hashData();
hashes.put(content, hash);
existingContentHashes.add(hash);
}
var images = getImages();
images.removeAll(i -> {
if(i.isGenerated()){
Fi file = new Fi(i.path);
String imageToContentHash = file.parent().name();
imagesWithHashes.add(imageToContentHash);
//don't remove images that correspond to content that exists with that hash
return !existingContentHashes.contains(imageToContentHash);
}
return false;
});
//remove content that doesn't need to be packed
contentToPack.removeAll(u -> imagesWithHashes.contains(hashes.get(u)));
ObjectMap<String, ImageAsset> imageMap = new ObjectMap<>();
for(var image : images){
imageMap.put(DataImagePacker.regionPrefix + image.name, image);
}
int[] packed = {0};
ObjectMap<String, PixmapRegion> imagePixmaps = new ObjectMap<>();
PixmapRegion error = new PixmapRegion(Pixmaps.blankPixmap());
UnlockableContent[] currentContent = {null};
String[] currentHash = {null};
MultiPacker saver = new MultiPacker(false){
@Override
public void add(PageType type, String name, PixmapRegion region, int[] splits, int[] pads){
try{
if(region.pixmap.width > 2000) throw new IllegalArgumentException("Max image size exceeded");
//strip duplicate prefix
if(name.startsWith(DataImagePacker.regionPrefix)) name = name.substring(DataImagePacker.regionPrefix.length());
String path = "generated/" + currentHash[0] + "/" + name + ".png";
ImageAsset newImage = new ImageAsset();
newImage.setPath(path);
//it would be nice to do this async, but the pixmap typically gets disposed right after add() exist
byte[] bytes = PixmapIO.writePngBytes(region.pixmap);
newImage.updateData(bytes);
images.add(newImage);
packed[0] ++;
}catch(Exception e){
Log.err("Failed to pack image " + name, e);
}
}
@Override
public boolean has(String name){
return imageMap.containsKey(name);
}
@Override
public boolean has(PageType type, String name){
return has(name);
}
@Override
public @Nullable PixmapRegion get(TextureRegion region){
return get(((AtlasRegion)region).name);
}
@Override
public PixmapRegion get(String name){
var pix = imagePixmaps.get(name);
if(pix != null) return pix;
var image = imageMap.get(name);
if(image == null) return error;
try{
var result = new PixmapRegion(new Pixmap(image.getCacheFileNoNull()));
imagePixmaps.put(name, result);
return result;
}catch(Exception e){
Log.err("Failed loading image: " + image.path, e);
imagePixmaps.put(name, error);
return error;
}
}
};
for(var content : contentToPack){
currentContent[0] = content;
currentHash[0] = hashes.get(content);
try{
content.createIcons(saver);
}catch(Throwable e){
Log.err(e);
}
}
imagePixmaps.each((key, val) -> val.pixmap.dispose());
error.pixmap.dispose();
if(packed[0] > 0 || forcePack){
reloadImages();
if(!Vars.headless){
for(var cont : contentToPack){
try{
cont.loadIcon();
cont.load();
}catch(Exception e){
Log.err("Failed to load icons for " + cont, e);
}
}
}
}
}
public void reloadPatches(Seq<PatchAsset> patches){
if(patches != getPatches()) getPatches().set(patches);
@@ -31,13 +171,17 @@ public class DataManager{
rebuildOrderedAssets();
}
public void reloadImages(){
packer.unload();
packer.pack(getImages());
rebuildOrderedAssets();
}
public void reloadImages(Seq<ImageAsset> images){
if(images != getImages()) getImages().set(images);
packer.unload();
packer.pack(images);
rebuildOrderedAssets();
reloadImages();
}
public void reloadAudio(){
@@ -134,6 +278,10 @@ public class DataManager{
return assets.get(type, Seq::new).as();
}
public void clearGeneratedImages(){
getImages().removeAll(ImageAsset::isGenerated);
}
public Seq<ImageAsset> getImages(){
return getAssets(DataAssetType.image);
}
+9 -1
View File
@@ -48,6 +48,7 @@ public class DataPatcher{
private Seq<Runnable> resetters = new Seq<>();
private Seq<Runnable> afterCallbacks = new Seq<>();
private Seq<Object> visitStack = new Seq<>();
private Seq<Content> addedContent = new Seq<>();
private @Nullable PatchAsset currentlyApplyingPatch;
private @Nullable ContentAsset currentlyApplyingContent;
private Seq<LVar> addedVars = new Seq<>();
@@ -203,7 +204,9 @@ public class DataPatcher{
Vars.content.remove(errored);
}
Seq<Content> all = new Seq<>();
addedContent.clear();
Seq<Content> all = addedContent;
for(var arr : Vars.content.getContentMap()){
all.addAll(arr.select(c -> c.minfo.mod == dpMod));
}
@@ -287,10 +290,15 @@ public class DataPatcher{
afterCallbacks.each(Runnable::run);
afterCallbacks.clear();
usedpatches.clear();
addedContent.clear();
if(reloadContentWorld) fixContentArrays();
}
public Seq<Content> getAddedContent(){
return addedContent;
}
void callContentRemove(){
for(var arr : Vars.content.getContentMap()){
for(var value : arr){
+4 -4
View File
@@ -263,7 +263,7 @@ public class Mods implements Loadable{
@Override
public AtlasRegion find(String name){
var base = packer.get(name);
var base = packer.getPacked(name);
if(base != null){
var reg = new AtlasRegion(shadow.find(name).texture, base.x, base.y, base.width, base.height);
@@ -287,15 +287,15 @@ public class Mods implements Loadable{
@Override
public boolean has(String s){
return shadow.has(s) || packer.get(s) != null;
return shadow.has(s) || packer.getPacked(s) != null;
}
//return the *actual* pixmap regions, not the disposed ones.
@Override
public PixmapRegion getPixmap(AtlasRegion region){
PixmapRegion out = packer.get(region.name);
PixmapRegion out = packer.getPacked(region.name);
//this should not happen in normal situations
if(out == null) return packer.get("error");
if(out == null) return packer.getPacked("error");
return out;
}
};
@@ -4,6 +4,7 @@ import arc.files.*;
import arc.struct.*;
import arc.util.*;
import mindustry.ctype.*;
import mindustry.mod.*;
import java.io.*;
@@ -31,6 +32,10 @@ public class ContentAsset extends DataAsset{
public ContentAsset(){
}
public String hashData(){
return type.name() + "_" + DataAssetCache.encodeHash(Strings.sha256(data));
}
public void readOverride(String path, Fi file, ContentType type) throws IOException{
this.type = type;
setPath(path);
@@ -9,6 +9,10 @@ public class ImageAsset extends DataAsset{
setHash(hash);
}
public boolean isGenerated(){
return path.startsWith("generated/");
}
@Override
public DataAssetType getType(){
return DataAssetType.image;
+1 -1
View File
@@ -129,7 +129,7 @@ public class Item extends UnlockableContent implements Senseable{
var pixmaps = new PixmapRegion[frames];
for(int i = 0; i < frames; i++){
pixmaps[i] = Core.atlas.getPixmap(name + (i + 1));
pixmaps[i] = packer.get(name + (i + 1));
}
for(int i = 0; i < frames; i++){
+2 -2
View File
@@ -1232,7 +1232,7 @@ public class UnitType extends UnlockableContent implements Senseable{
for(var region : toOutline){
if(region instanceof AtlasRegion atlas && !Core.atlas.has(atlas.name + "-outline")){
String regionName = atlas.name;
Pixmap outlined = Pixmaps.outline(Core.atlas.getPixmap(region), outlineColor, outlineRadius);
Pixmap outlined = Pixmaps.outline(packer.get(region), outlineColor, outlineRadius);
Drawf.checkBleed(outlined);
@@ -1268,7 +1268,7 @@ public class UnitType extends UnlockableContent implements Senseable{
}
if(sample instanceof Tankc){
PixmapRegion pix = Core.atlas.getPixmap(treadRegion);
PixmapRegion pix = packer.get(treadRegion);
for(int r = 0; r < treadRects.length; r++){
Rect treadRect = treadRects[r];
+7 -7
View File
@@ -1538,7 +1538,7 @@ public class Block extends UnlockableContent implements Senseable{
super.createIcons(packer);
if(!synthetic()){
PixmapRegion image = Core.atlas.getPixmap(fullIcon);
PixmapRegion image = packer.get(fullIcon);
mapColor.set(image.get(image.width/2, image.height/2));
}
@@ -1549,7 +1549,7 @@ public class Block extends UnlockableContent implements Senseable{
for(Team team : Team.all){
//if there's an override, don't generate anything
if(team.hasPalette && !Core.atlas.has(name + "-team-" + team.name)){
var base = Core.atlas.getPixmap(teamRegion);
var base = packer.get(teamRegion);
Pixmap out = new Pixmap(base.width, base.height);
for(int x = 0; x < base.width; x++){
@@ -1584,7 +1584,7 @@ public class Block extends UnlockableContent implements Senseable{
if(outlineIcon){
AtlasRegion atlasRegion = (AtlasRegion)gen[outlinedIcon >= 0 ? Math.min(outlinedIcon, gen.length - 1) : gen.length -1];
PixmapRegion region = Core.atlas.getPixmap(atlasRegion);
PixmapRegion region = packer.get(atlasRegion);
Pixmap out = last = Pixmaps.outline(region, outlineColor, outlineRadius);
Drawf.checkBleed(out);
packer.add(PageType.main, atlasRegion.name, out);
@@ -1597,7 +1597,7 @@ public class Block extends UnlockableContent implements Senseable{
for(var region : toOutline){
if(region instanceof AtlasRegion atlas){
String regionName = atlas.name;
Pixmap outlined = Pixmaps.outline(Core.atlas.getPixmap(region), outlineColor, outlineRadius);
Pixmap outlined = Pixmaps.outline(packer.get(region), outlineColor, outlineRadius);
Drawf.checkBleed(outlined);
@@ -1607,19 +1607,19 @@ public class Block extends UnlockableContent implements Senseable{
}
if(gen.length > 1){
Pixmap base = Core.atlas.getPixmap(gen[0]).crop();
Pixmap base = packer.get(gen[0]).crop();
for(int i = 1; i < gen.length; i++){
if(i == gen.length - 1 && last != null){
base.draw(last, 0, 0, true);
}else{
base.draw(Core.atlas.getPixmap(gen[i]), true);
base.draw(packer.get(gen[i]), true);
}
}
packer.add(PageType.main, "block-" + name + "-full", base);
toDispose.add(base);
}else{
if(gen[0] != null) packer.add(PageType.main, "block-" + name + "-full", Core.atlas.getPixmap(gen[0]));
if(gen[0] != null) packer.add(PageType.main, "block-" + name + "-full", packer.get(gen[0]));
}
toDispose.each(Pixmap::dispose);
@@ -220,8 +220,8 @@ public class Floor extends Block{
if(Core.atlas.has(name + "-edge")) return;
var image = Core.atlas.getPixmap(icons()[0]);
var edge = Core.atlas.getPixmap(Core.atlas.find(name + "-edge-stencil", "edge-stencil"));
var image = packer.get(icons()[0]);
var edge = packer.get(Core.atlas.find(name + "-edge-stencil", "edge-stencil"));
Pixmap result = new Pixmap(edge.width, edge.height);
for(int x = 0; x < edge.width; x++){
@@ -46,8 +46,8 @@ public class OreBlock extends OverlayFloor{
for(int i = 0; i < variants; i++){
//use name (e.g. "ore-copper1"), fallback to "copper1" as per the old naming system
PixmapRegion shadow = Core.atlas.has(name + (i + 1)) ?
Core.atlas.getPixmap(name + (i + 1)) :
Core.atlas.getPixmap(itemDrop.name + (i + 1));
packer.get(name + (i + 1)) :
packer.get(itemDrop.name + (i + 1));
Pixmap image = shadow.crop();
@@ -1,6 +1,5 @@
package mindustry.world.blocks.environment;
import arc.*;
import arc.graphics.*;
import arc.graphics.g2d.*;
import arc.util.*;
@@ -37,10 +36,10 @@ public class ShallowLiquid extends Floor{
//super.createIcons(packer);
if(liquidBase != null && floorBase != null){
var overlay = Core.atlas.getPixmap(liquidBase.region);
var overlay = packer.get(liquidBase.region);
int index = 0;
for(TextureRegion region : floorBase.variantRegions()){
var res = Core.atlas.getPixmap(region).crop();
var res = packer.get(region).crop();
for(int x = 0; x < res.width; x++){
for(int y = 0; y < res.height; y++){
res.setRaw(x, y, Pixmap.blend((overlay.getRaw(x, y) & 0xffffff00) | (int)(liquidOpacity * 255), res.getRaw(x, y)));
+1 -1
View File
@@ -26,4 +26,4 @@ org.gradle.caching=true
org.gradle.internal.http.socketTimeout=100000
org.gradle.internal.http.connectionTimeout=100000
android.enableR8.fullMode=false
archash=6e3c0bbc30
archash=2b0a95e325