this repo has no description
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

introduce AssetRegistry for extensible asset type resolution

Also renamed AssetHandle to Asset, and converted the subtypes to Kotlin.

+567 -528
+3
src/main/java/app/Environment.java
··· 234 234 getUserConfigDir().mkdirs(); 235 235 getUserStateDir().mkdirs(); 236 236 237 + // Initialize asset registry 238 + assets.AssetRegistry.init(); 239 + 237 240 try { 238 241 unpackDatabase(); 239 242 readMainConfig();
+3 -3
src/main/java/app/StarRodMain.java
··· 33 33 import app.input.InvalidInputException; 34 34 import app.pane.Dock; 35 35 import app.pane.Pane; 36 - import assets.AssetHandle; 36 + import assets.Asset; 37 37 import assets.AssetManager; 38 38 import assets.ExpectedAsset; 39 39 import common.BaseEditor; ··· 462 462 case "-COMPILEMAP": 463 463 if (args.length > i + 1) { 464 464 String mapName = args[i + 1]; 465 - AssetHandle mapAsset = AssetManager.getMap(mapName); 465 + Asset mapAsset = AssetManager.getMap(mapName); 466 466 467 467 if (mapAsset == null) { 468 468 Logger.logfError("Cannot find map '%s'!", mapName); ··· 501 501 case "-COMPILEMAPS": 502 502 try { 503 503 File buildDir = AssetManager.getMapBuildDir(); 504 - for (AssetHandle ah : AssetManager.getMapSources()) { 504 + for (Asset ah : AssetManager.getMapSources()) { 505 505 // get existing compiled binaries 506 506 String mapName = Map.deriveName(ah.getFile()); 507 507 File binShape = new File(buildDir, mapName + "_shape.bin");
+4 -4
src/main/java/app/pane/explorer/AssetItem.java
··· 6 6 import javax.swing.ImageIcon; 7 7 import javax.swing.SwingWorker; 8 8 9 - import assets.AssetHandle; 9 + import assets.Asset; 10 10 import util.ui.ThemedIcon; 11 11 12 12 class AssetItem extends Item 13 13 { 14 - final AssetHandle asset; 14 + final Asset asset; 15 15 16 - AssetItem(Tab explorer, AssetHandle asset) 16 + AssetItem(Tab explorer, Asset asset) 17 17 { 18 18 super(explorer, asset.getName(), ThemedIcon.PACKAGE_24, false); 19 19 this.asset = asset; ··· 53 53 } 54 54 55 55 @Override 56 - AssetHandle getAsset() 56 + Asset getAsset() 57 57 { 58 58 return asset; 59 59 }
+3 -3
src/main/java/app/pane/explorer/DirectoryItem.java
··· 10 10 import java.awt.dnd.DropTargetEvent; 11 11 12 12 import app.SwingUtils; 13 - import assets.AssetHandle; 13 + import assets.Asset; 14 14 import assets.AssetManager; 15 15 import util.Logger; 16 16 import util.ui.ThemedIcon; ··· 28 28 @Override 29 29 public void dragEnter(DropTargetDragEvent dtde) 30 30 { 31 - if (dtde.isDataFlavorSupported(AssetHandle.FLAVOUR)) { 31 + if (dtde.isDataFlavorSupported(Asset.FLAVOUR)) { 32 32 dtde.acceptDrag(DnDConstants.ACTION_MOVE); 33 33 dropTarget = true; 34 34 repaint(); ··· 52 52 repaint(); 53 53 try { 54 54 dtde.acceptDrop(DnDConstants.ACTION_MOVE); 55 - var asset = (AssetHandle) dtde.getTransferable().getTransferData(AssetHandle.FLAVOUR); 55 + var asset = (Asset) dtde.getTransferable().getTransferData(Asset.FLAVOUR); 56 56 57 57 File targetDir = new File(AssetManager.getTopLevelAssetDir(), targetPath); 58 58 targetDir.mkdirs();
+6 -6
src/main/java/app/pane/explorer/Item.java
··· 40 40 import javax.swing.UIManager; 41 41 42 42 import app.SwingUtils; 43 - import assets.AssetHandle; 43 + import assets.Asset; 44 44 45 45 abstract class Item extends JPanel 46 46 { 47 47 static final int PADDING = 3; 48 - static final int SIZE = AssetHandle.THUMBNAIL_WIDTH + PADDING * 2; 48 + static final int SIZE = Asset.THUMBNAIL_WIDTH + PADDING * 2; 49 49 50 50 final Tab explorer; 51 51 final String name; ··· 128 128 } 129 129 130 130 /** Override to return the asset for context menu operations. */ 131 - AssetHandle getAsset() 131 + Asset getAsset() 132 132 { 133 133 return null; 134 134 } 135 135 136 136 private void showContextMenu(MouseEvent e) 137 137 { 138 - AssetHandle asset = getAsset(); 138 + Asset asset = getAsset(); 139 139 if (asset == null) 140 140 return; 141 141 ··· 178 178 179 179 private void onRename() 180 180 { 181 - AssetHandle asset = getAsset(); 181 + Asset asset = getAsset(); 182 182 if (asset == null || renameField != null) 183 183 return; 184 184 ··· 265 265 266 266 private void onDelete() 267 267 { 268 - AssetHandle asset = getAsset(); 268 + Asset asset = getAsset(); 269 269 if (asset == null) 270 270 return; 271 271
+9 -9
src/main/java/app/pane/explorer/Tab.java
··· 35 35 import app.Environment; 36 36 import app.SwingUtils; 37 37 import app.pane.DockTab; 38 - import assets.AssetHandle; 38 + import assets.Asset; 39 39 import assets.AssetManager; 40 40 import assets.AssetManager.DirectoryListing; 41 41 import net.miginfocom.swing.MigLayout; ··· 137 137 rebuildBreadcrumb(); 138 138 } 139 139 140 - void openAsset(AssetHandle asset) 140 + void openAsset(Asset asset) 141 141 { 142 142 // TODO 143 143 } ··· 194 194 @Override 195 195 public void dragEnter(DropTargetDragEvent dtde) 196 196 { 197 - if (dtde.isDataFlavorSupported(AssetHandle.FLAVOUR)) { 197 + if (dtde.isDataFlavorSupported(Asset.FLAVOUR)) { 198 198 dtde.acceptDrag(DnDConstants.ACTION_MOVE); 199 199 label.putClientProperty("FlatLaf.style", "font: semibold"); 200 200 } ··· 215 215 label.setFont(normalFont); 216 216 try { 217 217 dtde.acceptDrop(DnDConstants.ACTION_MOVE); 218 - var asset = (AssetHandle) dtde.getTransferable().getTransferData(AssetHandle.FLAVOUR); 218 + var asset = (Asset) dtde.getTransferable().getTransferData(Asset.FLAVOUR); 219 219 220 220 if (asset.getRelativePath().toString().startsWith(targetPath) && !asset.getRelativePath().toString().substring(targetPath.length()).contains("/")) { 221 221 dtde.dropComplete(false); ··· 262 262 for (String subdirName : listing.subdirectories()) 263 263 resultsPanel.add(new DirectoryItem(this, subdirName, currentPath + subdirName + "/")); 264 264 265 - for (AssetHandle asset : listing.files()) 265 + for (Asset asset : listing.files()) 266 266 resultsPanel.add(new AssetItem(this, asset)); 267 267 268 268 resultsPanel.revalidate(); ··· 359 359 360 360 static class AssetTransferable implements Transferable 361 361 { 362 - private final AssetHandle asset; 362 + private final Asset asset; 363 363 364 - AssetTransferable(AssetHandle asset) 364 + AssetTransferable(Asset asset) 365 365 { 366 366 this.asset = asset; 367 367 } ··· 369 369 @Override 370 370 public DataFlavor[] getTransferDataFlavors() 371 371 { 372 - return new DataFlavor[] { AssetHandle.FLAVOUR }; 372 + return new DataFlavor[] { Asset.FLAVOUR }; 373 373 } 374 374 375 375 @Override 376 376 public boolean isDataFlavorSupported(DataFlavor flavor) 377 377 { 378 - return AssetHandle.FLAVOUR.equals(flavor); 378 + return Asset.FLAVOUR.equals(flavor); 379 379 } 380 380 381 381 @Override
+9 -30
src/main/java/assets/AssetHandle.kt src/main/java/assets/Asset.kt
··· 1 1 package assets 2 2 3 - import assets.ui.BackgroundAsset 4 - import assets.ui.MapAsset 5 - import assets.ui.TexturesAsset 6 3 import org.apache.commons.io.FileUtils 7 4 import java.awt.Image 8 5 import java.awt.RenderingHints ··· 14 11 import kotlin.io.path.* 15 12 16 13 /** An asset on disk, but not yet loaded because it may be expensive to load. */ 17 - open class AssetHandle( 14 + open class Asset( 18 15 val root: Path, 19 16 var relativePath: Path, 20 17 ) { ··· 26 23 27 24 constructor(root: Path, relativePath: String) : this(root, Path(relativePath)) 28 25 constructor(root: File, relativePath: String) : this(root.toPath(), relativePath) 29 - constructor(other: AssetHandle) : this(other.root, other.relativePath) 26 + constructor(other: Asset) : this(other.root, other.relativePath) 30 27 31 28 /** Full path on disk. May be a directory. */ 32 29 var path: Path ··· 37 34 } 38 35 39 36 val name: String get() = relativePath.nameWithoutExtension 37 + val extension: String get() = relativePath.extension 38 + val isDirectory: Boolean get() = path.isDirectory() 40 39 41 40 private var cachedThumbnail: Image? = null 42 41 private var thumbnailLoaded = false ··· 53 52 open fun delete(): Boolean = FileUtils.deleteQuietly(path.toFile()) 54 53 55 54 /** Renames this asset within its current directory. */ 56 - open fun rename(newAssetName: String): Boolean { 55 + open fun rename(name: String): Boolean { 57 56 // Preserve file extension 58 - var finalName = newAssetName 59 - val oldName = path.name 60 - val dot = oldName.lastIndexOf('.') 61 - if (dot > 0) 62 - finalName += oldName.substring(dot) 63 - 64 - val newPath = path.parent?.resolve(finalName) ?: return false 57 + val newPath = path.parent?.resolve(name + extension) ?: return false 65 58 if (newPath.exists()) 66 59 return false 67 60 ··· 71 64 }.getOrDefault(false) 72 65 } 73 66 67 + // TODO: take a Path 74 68 /** Moves this asset to a different directory. */ 75 69 open fun move(targetDir: File): Boolean { 76 70 val targetPath = targetDir.toPath().resolve(path.name) ··· 100 94 return cachedThumbnail 101 95 } 102 96 103 - override fun toString(): String = "AssetHandle($root / $relativePath})" 97 + override fun toString(): String = "Asset($relativePath)" 104 98 105 99 companion object { 106 100 const val THUMBNAIL_WIDTH = 74 ··· 108 102 109 103 @JvmField 110 104 val FLAVOUR: DataFlavor = DataFlavor( 111 - DataFlavor.javaJVMLocalObjectMimeType + ";class=\"${AssetHandle::class.java.name}\"" 105 + DataFlavor.javaJVMLocalObjectMimeType + ";class=\"${Asset::class.java.name}\"" 112 106 ) 113 107 114 108 private fun createMultiResThumbnail(src: Image): Image { ··· 144 138 assert(bi.width <= maxW && bi.height <= maxH) 145 139 return bi 146 140 } 147 - 148 - /** Upgrades a plain AssetHandle to a typed subclass based on file extension. */ 149 - @JvmStatic 150 - fun upgrade(handle: AssetHandle): AssetHandle? { 151 - val name = handle.path.name 152 - return when { 153 - name.endsWith(".xml") -> MapAsset(handle) 154 - name.endsWith(".png") -> BackgroundAsset(handle) 155 - name.endsWith(".json") -> TexturesAsset(handle) 156 - else -> null 157 - } 158 - } 159 141 } 160 142 } 161 - 162 - /** Exception thrown when an AssetHandle is created for a non-existent path. */ 163 - class AssetMissingException(message: String) : IllegalArgumentException(message)
+45 -45
src/main/java/assets/AssetManager.java
··· 36 36 return Environment.assetDirectories.get(numDirs - 1); 37 37 } 38 38 39 - public static AssetHandle get(AssetSubdir subdir, String path) 39 + public static Asset get(AssetSubdir subdir, String path) 40 40 { 41 41 for (File assetDir : Environment.assetDirectories) { 42 - AssetHandle ah = new AssetHandle(assetDir, subdir + path); 42 + Asset ah = new Asset(assetDir, subdir + path); 43 43 44 44 if (ah.exists()) 45 45 return ah; 46 46 } 47 - return new AssetHandle(AssetManager.getTopLevelAssetDir(), subdir + path); 47 + return new Asset(AssetManager.getTopLevelAssetDir(), subdir + path); 48 48 } 49 49 50 - public static AssetHandle getTopLevel(AssetHandle source) 50 + public static Asset getTopLevel(Asset source) 51 51 { 52 - return new AssetHandle(getTopLevelAssetDir(), source.getRelativePath().toString()); 52 + return new Asset(getTopLevelAssetDir(), source.getRelativePath().toString()); 53 53 } 54 54 55 - public static AssetHandle getBase(AssetSubdir subdir, String path) 55 + public static Asset getBase(AssetSubdir subdir, String path) 56 56 { 57 - return new AssetHandle(getBaseAssetDir(), subdir + path); 57 + return new Asset(getBaseAssetDir(), subdir + path); 58 58 } 59 59 60 60 /** 61 61 * Delete an asset at all levels of the asset stack 62 62 * @param asset 63 63 */ 64 - public static void deleteAll(AssetHandle asset) 64 + public static void deleteAll(Asset asset) 65 65 { 66 66 for (File assetDir : Environment.assetDirectories) { 67 67 File f = new File(assetDir, asset.getRelativePath().toString()); ··· 71 71 } 72 72 73 73 74 - public static AssetHandle getTextureArchive(String texName) 74 + public static Asset getTextureArchive(String texName) 75 75 { 76 76 return get(AssetSubdir.MAP_TEX, texName + EXT_NEW_TEX); 77 77 } ··· 81 81 return AssetSubdir.MAP_TEX.getModDir(); 82 82 } 83 83 84 - public static AssetHandle getMap(String mapName) 84 + public static Asset getMap(String mapName) 85 85 { 86 86 return get(AssetSubdir.MAP_GEOM, mapName + EXT_MAP); 87 87 } ··· 96 96 return AssetSubdir.MAP_GEOM.getModDir(); 97 97 } 98 98 99 - public static AssetHandle getBackground(String bgName) 99 + public static Asset getBackground(String bgName) 100 100 { 101 101 return get(AssetSubdir.MAP_BG, bgName + EXT_PNG); 102 102 } ··· 106 106 return AssetSubdir.MAP_BG.getModDir(); 107 107 } 108 108 109 - public static AssetHandle getNpcSprite(String spriteName) 109 + public static Asset getNpcSprite(String spriteName) 110 110 { 111 111 return get(AssetSubdir.NPC_SPRITE, spriteName + "/" + FN_SPRITESHEET); 112 112 } 113 113 114 - public static Map<String, AssetHandle> getNpcSpriteRasters(String spriteName) throws IOException 114 + public static Map<String, Asset> getNpcSpriteRasters(String spriteName) throws IOException 115 115 { 116 116 return getAssetMap(AssetSubdir.NPC_SPRITE, spriteName + "/rasters/", EXT_PNG); 117 117 } 118 118 119 - public static Map<String, AssetHandle> getNpcSpritePalettes(String spriteName) throws IOException 119 + public static Map<String, Asset> getNpcSpritePalettes(String spriteName) throws IOException 120 120 { 121 121 return getAssetMap(AssetSubdir.NPC_SPRITE, spriteName + "/palettes/", EXT_PNG); 122 122 } 123 123 124 - public static AssetHandle getPlayerSprite(String spriteName) 124 + public static Asset getPlayerSprite(String spriteName) 125 125 { 126 126 return get(AssetSubdir.PLR_SPRITE, spriteName + EXT_SPRITE); 127 127 } 128 128 129 - public static Map<String, AssetHandle> getPlayerSpriteRasters() throws IOException 129 + public static Map<String, Asset> getPlayerSpriteRasters() throws IOException 130 130 { 131 131 return getAssetMap(AssetSubdir.PLR_SPRITE_IMG, EXT_PNG); 132 132 } 133 133 134 - public static Map<String, AssetHandle> getPlayerSpritePalettes() throws IOException 134 + public static Map<String, Asset> getPlayerSpritePalettes() throws IOException 135 135 { 136 136 return getAssetMap(AssetSubdir.PLR_SPRITE_PAL, EXT_PNG); 137 137 } 138 138 139 - public static Collection<AssetHandle> getMapSources() throws IOException 139 + public static Collection<Asset> getMapSources() throws IOException 140 140 { 141 141 return getAssets(AssetSubdir.MAP_GEOM, EXT_MAP, (p) -> { 142 142 // skip crash and backup files ··· 145 145 }); 146 146 } 147 147 148 - public static Collection<AssetHandle> getBackgrounds() throws IOException 148 + public static Collection<Asset> getBackgrounds() throws IOException 149 149 { 150 150 return getAssets(AssetSubdir.MAP_BG, EXT_PNG); 151 151 } 152 152 153 - public static Collection<AssetHandle> getLegacyTextureArchives() throws IOException 153 + public static Collection<Asset> getLegacyTextureArchives() throws IOException 154 154 { 155 155 return getAssets(AssetSubdir.MAP_TEX, EXT_OLD_TEX); 156 156 } 157 157 158 - public static Collection<AssetHandle> getTextureArchives() throws IOException 158 + public static Collection<Asset> getTextureArchives() throws IOException 159 159 { 160 160 return getAssets(AssetSubdir.MAP_TEX, EXT_NEW_TEX); 161 161 } 162 162 163 - public static Collection<AssetHandle> getMessages() throws IOException 163 + public static Collection<Asset> getMessages() throws IOException 164 164 { 165 165 return getAssets(AssetSubdir.MSG, EXT_MSG); 166 166 } 167 167 168 - private static Collection<AssetHandle> getAssets(AssetSubdir dir, String ext) 168 + private static Collection<Asset> getAssets(AssetSubdir dir, String ext) 169 169 { 170 170 return getAssets(dir, "", ext, null); 171 171 } 172 172 173 - private static Collection<AssetHandle> getAssets(AssetSubdir dir, String subdir, String ext) 173 + private static Collection<Asset> getAssets(AssetSubdir dir, String subdir, String ext) 174 174 { 175 175 return getAssets(dir, subdir, ext, null); 176 176 } 177 177 178 - private static Collection<AssetHandle> getAssets(AssetSubdir dir, String ext, Predicate<Path> shouldAccept) 178 + private static Collection<Asset> getAssets(AssetSubdir dir, String ext, Predicate<Path> shouldAccept) 179 179 { 180 180 return getAssets(dir, "", ext, shouldAccept); 181 181 } 182 182 183 - private static Collection<AssetHandle> getAssets(AssetSubdir dir, String subdir, String ext, Predicate<Path> shouldAccept) 183 + private static Collection<Asset> getAssets(AssetSubdir dir, String subdir, String ext, Predicate<Path> shouldAccept) 184 184 { 185 - Map<String, AssetHandle> assetMap = getAssetMap(dir, subdir, ext, shouldAccept); 185 + Map<String, Asset> assetMap = getAssetMap(dir, subdir, ext, shouldAccept); 186 186 187 187 // return sorted by filename 188 188 return assetMap.entrySet().stream() ··· 191 191 .collect(Collectors.toList()); 192 192 } 193 193 194 - private static Map<String, AssetHandle> getAssetMap(AssetSubdir dir, String ext) 194 + private static Map<String, Asset> getAssetMap(AssetSubdir dir, String ext) 195 195 { 196 196 return getAssetMap(dir, "", ext, null); 197 197 } 198 198 199 - private static Map<String, AssetHandle> getAssetMap(AssetSubdir dir, String subdir, String ext) 199 + private static Map<String, Asset> getAssetMap(AssetSubdir dir, String subdir, String ext) 200 200 { 201 201 return getAssetMap(dir, subdir, ext, null); 202 202 } 203 203 204 - private static Map<String, AssetHandle> getAssetMap(AssetSubdir dir, String ext, Predicate<Path> shouldAccept) 204 + private static Map<String, Asset> getAssetMap(AssetSubdir dir, String ext, Predicate<Path> shouldAccept) 205 205 { 206 206 return getAssetMap(dir, "", ext, shouldAccept); 207 207 } 208 208 209 - private static Map<String, AssetHandle> getAssetMap(AssetSubdir dir, String subdir, String ext, Predicate<Path> shouldAccept) 209 + private static Map<String, Asset> getAssetMap(AssetSubdir dir, String subdir, String ext, Predicate<Path> shouldAccept) 210 210 { 211 - Map<String, AssetHandle> assetMap = new HashMap<>(); 211 + Map<String, Asset> assetMap = new HashMap<>(); 212 212 213 213 for (File stackDir : Environment.assetDirectories) { 214 214 Path assetDir = dir.get(stackDir).toPath(); ··· 229 229 continue; 230 230 231 231 String relPath = dir + subdir + filename; 232 - AssetHandle ah = new AssetHandle(stackDir, relPath); 232 + Asset ah = new Asset(stackDir, relPath); 233 233 234 234 // only add first occurance down the asset stack traversal 235 235 assetMap.putIfAbsent(filename, ah); ··· 245 245 246 246 // --- Generic directory listing --- 247 247 248 - public record DirectoryListing(List<AssetHandle> files, List<String> subdirectories) {} 248 + public record DirectoryListing(List<Asset> files, List<String> subdirectories) {} 249 249 250 250 /** 251 251 * Lists all files and subdirectories at a relative path across the asset stack. 252 - * Files are returned as AssetHandles (first occurrence in the stack wins). 252 + * Files are returned as Assets (first occurrence in the stack wins). 253 253 * Subdirectories are returned as names (union across all stack levels). 254 254 * @param relativePath Relative path from asset root, e.g. "" for root, "mapfs/", "mapfs/geom/" 255 255 */ 256 256 public static DirectoryListing listDirectory(String relativePath) 257 257 { 258 - Map<String, AssetHandle> fileMap = new HashMap<>(); 258 + Map<String, Asset> fileMap = new HashMap<>(); 259 259 TreeSet<String> subdirSet = new TreeSet<>(); 260 260 261 261 TreeSet<String> ignoredPaths = new TreeSet<>(); ··· 277 277 if (ignoredPaths.contains(relPath)) 278 278 continue; 279 279 280 - AssetHandle ah = new AssetHandle(stackDir, relPath); 281 - AssetHandle upgraded = AssetHandle.upgrade(ah); 282 - if (upgraded != null) 283 - fileMap.putIfAbsent(name, upgraded); 284 - else if (Files.isDirectory(entry)) 280 + if (Files.isDirectory(entry)) { 285 281 subdirSet.add(name); 282 + } else { 283 + Asset asset = AssetRegistry.getInstance().create(stackDir.toPath(), java.nio.file.Path.of(relPath)); 284 + fileMap.putIfAbsent(name, asset); 285 + } 286 286 } 287 287 } 288 288 catch (IOException e) { ··· 290 290 } 291 291 } 292 292 293 - List<AssetHandle> files = fileMap.entrySet().stream() 293 + List<Asset> files = fileMap.entrySet().stream() 294 294 .sorted(Map.Entry.comparingByKey()) 295 295 .map(Map.Entry::getValue) 296 296 .collect(Collectors.toList()); ··· 313 313 return dirs; 314 314 } 315 315 316 - public static Collection<AssetHandle> getIcons() throws IOException 316 + public static Collection<Asset> getIcons() throws IOException 317 317 { 318 318 // use TreeMap to keep assets sorted 319 - TreeMap<String, AssetHandle> assetMap = new TreeMap<>(); 319 + TreeMap<String, Asset> assetMap = new TreeMap<>(); 320 320 321 321 for (File assetDir : Environment.assetDirectories) { 322 322 File iconDir = AssetSubdir.ICON.get(assetDir); ··· 334 334 continue; 335 335 } 336 336 337 - AssetHandle ah = new AssetHandle(assetDir, AssetSubdir.ICON + relativeString); 337 + Asset ah = new Asset(assetDir, AssetSubdir.ICON + relativeString); 338 338 if (!assetMap.containsKey(relativeString)) { 339 339 assetMap.put(relativeString, ah); 340 340 }
+87
src/main/java/assets/AssetRegistry.kt
··· 1 + package assets 2 + 3 + import assets.ui.BackgroundAsset 4 + import assets.ui.MapAsset 5 + import assets.ui.TexturesAsset 6 + import java.io.IOException 7 + import java.nio.file.DirectoryStream 8 + import java.nio.file.Files 9 + import java.nio.file.Path 10 + import kotlin.io.path.* 11 + 12 + /** 13 + * Registry for asset types. Maps file extensions to factory functions that create typed Asset instances. 14 + * 15 + * Built-in types are registered at startup via init(). 16 + * Addon types can be registered dynamically via register() with addon = true. 17 + * 18 + * When an unregistered extension is encountered, create() returns a plain Asset instance. 19 + */ 20 + class AssetRegistry { 21 + internal data class Registration( 22 + val extension: String, 23 + val factory: (Path, Path) -> Asset, 24 + val addon: Boolean, 25 + ) 26 + 27 + internal val byExtension = mutableMapOf<String, Registration>() 28 + 29 + /** 30 + * Registers an asset type for a file extension. 31 + * @param extension File extension without leading dot (e.g., "xml", "png") 32 + * @param factory Constructor reference that takes (root: Path, relativePath: Path) -> Asset 33 + * @param addon Whether this is an addon type (used for hot reload) 34 + */ 35 + fun register(extension: String, factory: (Path, Path) -> Asset, addon: Boolean = false) { 36 + byExtension[extension] = Registration(extension, factory, addon) 37 + } 38 + 39 + /** 40 + * Unloads all addon-registered types. 41 + * After unloading, those extensions will create plain Asset instances. 42 + */ 43 + fun unloadAddon() { 44 + byExtension.values.removeAll { it.addon } 45 + } 46 + 47 + /** 48 + * Creates an Asset for the given path. 49 + * Returns a typed subclass if the extension is registered, otherwise a plain Asset. 50 + */ 51 + fun create(root: Path, relativePath: Path): Asset { 52 + val ext = relativePath.extension 53 + val reg = byExtension[ext] 54 + return reg?.factory?.invoke(root, relativePath) ?: Asset(root, relativePath) 55 + } 56 + 57 + /** 58 + * Returns all assets of a specific type by scanning asset directories. 59 + * @param T The asset type to filter by 60 + * @param root Root directory to scan 61 + * @return List of assets matching the type 62 + */ 63 + inline fun <reified T : Asset> getAllOfType(root: Path): List<T> { 64 + // TODO: Implement 65 + return emptyList() 66 + } 67 + 68 + companion object { 69 + /** 70 + * Global registry instance. 71 + * Call init() at startup to register built-in types. 72 + */ 73 + @JvmStatic 74 + val instance = AssetRegistry() 75 + 76 + /** 77 + * Registers built-in asset types. 78 + * Should be called once at application startup. 79 + */ 80 + @JvmStatic 81 + fun init() { 82 + instance.register("xml", ::MapAsset) 83 + instance.register("png", ::BackgroundAsset) 84 + instance.register("json", ::TexturesAsset) 85 + } 86 + } 87 + }
+16
src/main/java/assets/LiveAsset.kt
··· 1 + package assets 2 + 3 + /** 4 + * Base class for loaded, in-memory asset data. 5 + * Holds a reference back to the asset it was loaded from. 6 + * 7 + * The relationship: 8 + * - Asset (file on disk, cheap) 9 + * ↓ load 10 + * - LiveAsset (parsed data in memory, editable) 11 + * ↓ save 12 + * - Asset (written back to disk) 13 + */ 14 + abstract class LiveAsset( 15 + val asset: Asset, 16 + )
+4 -4
src/main/java/assets/ui/AssetCellRenderer.java
··· 11 11 import org.apache.commons.io.FilenameUtils; 12 12 13 13 import app.SwingUtils; 14 - import assets.AssetHandle; 14 + import assets.Asset; 15 15 import net.miginfocom.swing.MigLayout; 16 16 17 - public class AssetCellRenderer extends JPanel implements ListCellRenderer<AssetHandle> 17 + public class AssetCellRenderer extends JPanel implements ListCellRenderer<Asset> 18 18 { 19 19 private final JLabel nameLabel; 20 20 ··· 31 31 32 32 @Override 33 33 public Component getListCellRendererComponent( 34 - JList<? extends AssetHandle> list, 35 - AssetHandle asset, 34 + JList<? extends Asset> list, 35 + Asset asset, 36 36 int index, 37 37 boolean isSelected, 38 38 boolean cellHasFocus)
-32
src/main/java/assets/ui/BackgroundAsset.java
··· 1 - package assets.ui; 2 - 3 - import java.awt.Image; 4 - import java.awt.image.BufferedImage; 5 - import java.io.IOException; 6 - 7 - import javax.imageio.ImageIO; 8 - 9 - import assets.AssetHandle; 10 - 11 - public class BackgroundAsset extends AssetHandle 12 - { 13 - public BufferedImage bimg; 14 - 15 - public BackgroundAsset(AssetHandle asset) 16 - { 17 - super(asset); 18 - 19 - try { 20 - bimg = ImageIO.read(asset.getFile()); 21 - } 22 - catch (IOException e) { 23 - bimg = null; 24 - } 25 - } 26 - 27 - @Override 28 - protected Image loadThumbnail() 29 - { 30 - return bimg; 31 - } 32 - }
+26
src/main/java/assets/ui/BackgroundAsset.kt
··· 1 + package assets.ui 2 + 3 + import assets.Asset 4 + import java.awt.Image 5 + import java.awt.image.BufferedImage 6 + import java.io.IOException 7 + import java.nio.file.Path 8 + import javax.imageio.ImageIO 9 + 10 + class BackgroundAsset(root: Path, relativePath: Path) : Asset(root, relativePath) { 11 + @JvmField 12 + val bimg: BufferedImage? 13 + 14 + /** Convenience constructor for Java interop. */ 15 + constructor(asset: Asset) : this(asset.root, asset.relativePath) 16 + 17 + init { 18 + bimg = try { 19 + ImageIO.read(getFile()) 20 + } catch (e: IOException) { 21 + null 22 + } 23 + } 24 + 25 + override fun loadThumbnail(): Image? = bimg 26 + }
-154
src/main/java/assets/ui/MapAsset.java
··· 1 - package assets.ui; 2 - 3 - import static app.Directories.PROJ_THUMBNAIL; 4 - 5 - import java.awt.Image; 6 - import java.io.BufferedReader; 7 - import java.io.File; 8 - import java.io.FileReader; 9 - import java.io.IOException; 10 - import java.nio.file.Files; 11 - import java.util.regex.Matcher; 12 - import java.util.regex.Pattern; 13 - 14 - import javax.imageio.ImageIO; 15 - 16 - import app.Environment; 17 - import assets.AssetHandle; 18 - import assets.AssetManager; 19 - import game.map.editor.MapEditor; 20 - import util.Logger; 21 - import util.Priority; 22 - 23 - public class MapAsset extends AssetHandle 24 - { 25 - public static void main(String[] args) throws IOException 26 - { 27 - Environment.initialize(); 28 - 29 - long t0 = System.nanoTime(); 30 - 31 - AssetManager.getMapSources(); 32 - 33 - long t1 = System.nanoTime(); 34 - double sec = (t1 - t0) / 1e9; 35 - Logger.logf("Loaded map info %.02f ms", sec * 1e3); 36 - 37 - Environment.exit(); 38 - } 39 - 40 - private static final Matcher MapTagMatcher = Pattern.compile("\\s*<Map .+>\\s*").matcher(""); 41 - private static final Matcher MapDescMatcher = Pattern.compile(".+desc=\"([^\"]+)\".+").matcher(""); 42 - 43 - public String desc = ""; 44 - 45 - public MapAsset(AssetHandle asset) 46 - { 47 - super(asset); 48 - 49 - // need to read Map tag quickly, do not parse whole XML file 50 - try (BufferedReader in = new BufferedReader(new FileReader(asset.getFile()))) { 51 - String line; 52 - 53 - while (true) { 54 - line = in.readLine(); 55 - if (line == null) { 56 - return; // encountered final line without finding Map tag 57 - } 58 - 59 - MapTagMatcher.reset(line); 60 - 61 - if (MapTagMatcher.matches()) 62 - break; 63 - } 64 - 65 - MapDescMatcher.reset(line); 66 - if (MapDescMatcher.matches()) { 67 - desc = MapDescMatcher.group(1); 68 - } 69 - } 70 - catch (IOException e) { 71 - Logger.logError(e.getMessage()); 72 - desc = "READ ERROR"; 73 - } 74 - } 75 - 76 - @Override 77 - public String getAssetDescription() { 78 - return desc; 79 - } 80 - 81 - @Override 82 - public boolean delete() 83 - { 84 - File thumbFile = new File(PROJ_THUMBNAIL + getRelativePath().toString() + ".png"); 85 - if (thumbFile.exists()) 86 - thumbFile.delete(); 87 - return super.delete(); 88 - } 89 - 90 - @Override 91 - public boolean rename(String newFileName) 92 - { 93 - File oldThumb = new File(PROJ_THUMBNAIL + getRelativePath().toString() + ".png"); 94 - if (oldThumb.exists()) { 95 - try { 96 - String newAssetPath = getRelativePath().toString().substring(0, getRelativePath().toString().lastIndexOf('/') + 1) + newFileName; 97 - File newThumb = new File(PROJ_THUMBNAIL + newAssetPath + ".png"); 98 - newThumb.getParentFile().mkdirs(); 99 - Files.move(oldThumb.toPath(), newThumb.toPath()); 100 - } 101 - catch (IOException e) { 102 - return false; 103 - } 104 - } 105 - return super.rename(newFileName); 106 - } 107 - 108 - @Override 109 - public boolean thumbnailHasCheckerboard() { 110 - return false; 111 - } 112 - 113 - @Override 114 - protected Image loadThumbnail() 115 - { 116 - File thumbFile = new File(PROJ_THUMBNAIL + getRelativePath().toString() + ".png"); 117 - if (thumbFile.exists()) { 118 - try { 119 - return ImageIO.read(thumbFile); 120 - } 121 - catch (IOException e) {} 122 - } 123 - 124 - return null; 125 - } 126 - 127 - /** 128 - * Generates thumbnails for all maps that don't already have one. 129 - * Creates a MapEditor instance, so must not be called while one is open. 130 - */ 131 - public static void generateMissingThumbnails() 132 - { 133 - MapEditor editor = null; 134 - 135 - try { 136 - for (AssetHandle asset : AssetManager.getMapSources()) { 137 - File thumbFile = new File(PROJ_THUMBNAIL + asset.getRelativePath().toString() + ".png"); 138 - if (thumbFile.exists()) 139 - continue; 140 - Logger.log("Capturing thumbnail for " + asset.getRelativePath().toString() + "...", Priority.MILESTONE); 141 - if (editor == null) 142 - editor = new MapEditor(false); 143 - editor.generateThumbnail(asset.getFile(), thumbFile, AssetHandle.THUMBNAIL_WIDTH * 2, AssetHandle.THUMBNAIL_HEIGHT * 2); 144 - } 145 - } 146 - catch (Exception e) { 147 - Logger.printStackTrace(e); 148 - } 149 - finally { 150 - if (editor != null) 151 - editor.shutdownThumbnail(); 152 - } 153 - } 154 - }
+140
src/main/java/assets/ui/MapAsset.kt
··· 1 + package assets.ui 2 + 3 + import app.Directories.PROJ_THUMBNAIL 4 + import app.Environment 5 + import assets.Asset 6 + import assets.AssetManager 7 + import game.map.editor.MapEditor 8 + import util.Logger 9 + import util.Priority 10 + import java.awt.Image 11 + import java.io.BufferedReader 12 + import java.io.File 13 + import java.io.FileReader 14 + import java.io.IOException 15 + import java.nio.file.Files 16 + import java.nio.file.Path 17 + import java.util.regex.Pattern 18 + import javax.imageio.ImageIO 19 + 20 + class MapAsset(root: Path, relativePath: Path) : Asset(root, relativePath) { 21 + @JvmField 22 + var desc: String = "" 23 + 24 + /** Convenience constructor for Java interop. */ 25 + constructor(asset: Asset) : this(asset.root, asset.relativePath) 26 + 27 + init { 28 + // Read Map tag quickly without parsing whole XML file 29 + try { 30 + BufferedReader(FileReader(getFile())).use { reader -> 31 + var line: String? 32 + while (true) { 33 + line = reader.readLine() ?: break // Encountered final line without finding Map tag 34 + 35 + val mapTagMatcher = MAP_TAG_MATCHER.reset(line) 36 + if (mapTagMatcher.matches()) { 37 + val descMatcher = MAP_DESC_MATCHER.reset(line) 38 + if (descMatcher.matches()) { 39 + desc = descMatcher.group(1) 40 + } 41 + break 42 + } 43 + } 44 + } 45 + } catch (e: IOException) { 46 + Logger.logError(e.message) 47 + desc = "READ ERROR" 48 + } 49 + } 50 + 51 + override fun getAssetDescription(): String = desc 52 + 53 + override fun delete(): Boolean { 54 + val thumbFile = File("$PROJ_THUMBNAIL${relativePath}.png") 55 + if (thumbFile.exists()) 56 + thumbFile.delete() 57 + return super.delete() 58 + } 59 + 60 + override fun rename(name: String): Boolean { 61 + val oldThumb = File("$PROJ_THUMBNAIL$relativePath.png") 62 + if (oldThumb.exists()) { 63 + try { 64 + val lastSlash = relativePath.toString().lastIndexOf('/') + 1 65 + val newAssetPath = relativePath.toString().substring(0, lastSlash) + name 66 + val newThumb = File("$PROJ_THUMBNAIL$newAssetPath.png") 67 + newThumb.parentFile?.mkdirs() 68 + Files.move(oldThumb.toPath(), newThumb.toPath()) 69 + } catch (e: IOException) { 70 + return false 71 + } 72 + } 73 + return super.rename(name) 74 + } 75 + 76 + override fun thumbnailHasCheckerboard(): Boolean = false 77 + 78 + override fun loadThumbnail(): Image? { 79 + val thumbFile = File("$PROJ_THUMBNAIL$relativePath.png") 80 + return if (thumbFile.exists()) { 81 + try { 82 + ImageIO.read(thumbFile) 83 + } catch (e: IOException) { 84 + null 85 + } 86 + } else { 87 + null 88 + } 89 + } 90 + 91 + companion object { 92 + private val MAP_TAG_MATCHER = Pattern.compile("\\s*<Map .+>\\s*").matcher("") 93 + private val MAP_DESC_MATCHER = Pattern.compile(".+desc=\"([^\"]+)\".+").matcher("") 94 + 95 + /** 96 + * Generates thumbnails for all maps that don't already have one. 97 + * Creates a MapEditor instance, so must not be called while one is open. 98 + */ 99 + @JvmStatic 100 + fun generateMissingThumbnails() { 101 + var editor: MapEditor? = null 102 + 103 + try { 104 + for (asset in AssetManager.getMapSources()) { 105 + val thumbFile = File("$PROJ_THUMBNAIL${asset.relativePath}.png") 106 + if (thumbFile.exists()) 107 + continue 108 + Logger.log("Capturing thumbnail for $asset...", Priority.MILESTONE) 109 + if (editor == null) 110 + editor = MapEditor(false) 111 + editor.generateThumbnail( 112 + asset.getFile(), 113 + thumbFile, 114 + Asset.THUMBNAIL_WIDTH * 2, 115 + Asset.THUMBNAIL_HEIGHT * 2 116 + ) 117 + } 118 + } catch (e: Exception) { 119 + Logger.printStackTrace(e) 120 + } finally { 121 + editor?.shutdownThumbnail() 122 + } 123 + } 124 + 125 + @JvmStatic 126 + fun main(args: Array<String>) { 127 + Environment.initialize() 128 + 129 + val t0 = System.nanoTime() 130 + 131 + AssetManager.getMapSources() 132 + 133 + val t1 = System.nanoTime() 134 + val sec = (t1 - t0) / 1e9 135 + Logger.logf("Loaded map info %.02f ms", sec * 1e3) 136 + 137 + Environment.exit() 138 + } 139 + } 140 + }
+3 -3
src/main/java/assets/ui/SelectBackgroundDialog.java
··· 25 25 26 26 import app.Environment; 27 27 import app.SwingUtils; 28 - import assets.AssetHandle; 28 + import assets.Asset; 29 29 import assets.AssetManager; 30 30 import net.miginfocom.swing.MigLayout; 31 31 import util.Logger; ··· 62 62 private DialogResult result = DialogResult.NONE; 63 63 private BackgroundAsset selectedObject; 64 64 65 - private SelectBackgroundDialog(Collection<AssetHandle> assets, String initialSelection) 65 + private SelectBackgroundDialog(Collection<Asset> assets, String initialSelection) 66 66 { 67 67 super(null, java.awt.Dialog.ModalityType.TOOLKIT_MODAL); 68 68 setDefaultCloseOperation(DISPOSE_ON_CLOSE); 69 69 70 70 DefaultListModel<BackgroundAsset> listModel = new DefaultListModel<>(); 71 71 listModel.addElement(null); 72 - for (AssetHandle ah : assets) { 72 + for (Asset ah : assets) { 73 73 // ignore .alt background 74 74 if (ah.getRelativePath().toString().endsWith("_bg.png")) { 75 75 listModel.addElement(new BackgroundAsset(ah));
+3 -3
src/main/java/assets/ui/SelectMapDialog.java
··· 24 24 import app.Directories; 25 25 import app.Environment; 26 26 import app.SwingUtils; 27 - import assets.AssetHandle; 27 + import assets.Asset; 28 28 import assets.AssetManager; 29 29 import game.map.Map; 30 30 import net.miginfocom.swing.MigLayout; ··· 204 204 205 205 private SelectMapResult result = SelectMapResult.CANCEL; 206 206 207 - private SelectMapDialog(Collection<AssetHandle> assets) 207 + private SelectMapDialog(Collection<Asset> assets) 208 208 { 209 209 super(null, java.awt.Dialog.ModalityType.TOOLKIT_MODAL); 210 210 setDefaultCloseOperation(DISPOSE_ON_CLOSE); 211 211 212 212 DefaultListModel<MapAsset> listModel = new DefaultListModel<>(); 213 - for (AssetHandle ah : assets) { 213 + for (Asset ah : assets) { 214 214 listModel.addElement(new MapAsset(ah)); 215 215 } 216 216
+2 -2
src/main/java/assets/ui/SelectTexDialog.java
··· 28 28 29 29 import app.Environment; 30 30 import app.SwingUtils; 31 - import assets.AssetHandle; 31 + import assets.Asset; 32 32 import assets.AssetManager; 33 33 import net.miginfocom.swing.MigLayout; 34 34 import util.Logger; ··· 70 70 private DialogResult result = DialogResult.NONE; 71 71 private TexturesAsset selectedObject; 72 72 73 - private SelectTexDialog(Collection<AssetHandle> assets, String initialSelection) 73 + private SelectTexDialog(Collection<Asset> assets, String initialSelection) 74 74 { 75 75 super(null, java.awt.Dialog.ModalityType.TOOLKIT_MODAL); 76 76 setDefaultCloseOperation(DISPOSE_ON_CLOSE);
-165
src/main/java/assets/ui/TexturesAsset.java
··· 1 - package assets.ui; 2 - 3 - import java.awt.Graphics2D; 4 - import java.awt.Image; 5 - import java.awt.RenderingHints; 6 - import java.awt.image.BufferedImage; 7 - import java.io.File; 8 - import java.io.IOException; 9 - import java.nio.file.DirectoryStream; 10 - import java.nio.file.Files; 11 - import java.nio.file.Path; 12 - import java.util.ArrayList; 13 - import java.util.Collections; 14 - import java.util.List; 15 - 16 - import javax.imageio.ImageIO; 17 - 18 - import org.apache.commons.io.FileUtils; 19 - import org.apache.commons.io.FilenameUtils; 20 - 21 - import assets.AssetHandle; 22 - import assets.AssetSubdir; 23 - import util.Logger; 24 - 25 - public class TexturesAsset extends AssetHandle 26 - { 27 - private static final int THUMB_W = THUMBNAIL_WIDTH; 28 - private static final int THUMB_H = THUMBNAIL_HEIGHT; 29 - private static final int GAP = 2; 30 - private static final int ROW_HEIGHT = (THUMB_H - GAP) / 2; // two rows 31 - 32 - private final List<BufferedImage> textures = new ArrayList<>(); 33 - 34 - public BufferedImage getPreview(int index) 35 - { 36 - return index < textures.size() ? textures.get(index) : null; 37 - } 38 - 39 - public TexturesAsset(AssetHandle asset) 40 - { 41 - super(asset); 42 - 43 - String dirName = FilenameUtils.getBaseName(asset.getName()) + "/"; 44 - File dir = new File(asset.getRoot().toFile(), AssetSubdir.MAP_TEX + dirName); 45 - 46 - try { 47 - List<File> images = new ArrayList<>(); 48 - 49 - try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir.toPath(), "*.png")) { 50 - for (Path file : stream) { 51 - if (Files.isRegularFile(file)) 52 - images.add(file.toFile()); 53 - } 54 - } 55 - 56 - Collections.shuffle(images); 57 - 58 - for (int i = 0; i < images.size(); i++) { 59 - BufferedImage img = readImage(images.get(i)); 60 - if (img != null) 61 - textures.add(img); 62 - } 63 - } 64 - catch (IOException e) { 65 - Logger.logError("IOException while gathering previews from " + dirName); 66 - } 67 - } 68 - 69 - private File getCompanionDir() 70 - { 71 - String dirName = FilenameUtils.getBaseName(getName()) + "/"; 72 - File dir = new File(getRoot().toFile(), AssetSubdir.MAP_TEX + dirName); 73 - return dir.isDirectory() ? dir : null; 74 - } 75 - 76 - @Override 77 - public boolean delete() 78 - { 79 - File dir = getCompanionDir(); 80 - if (dir != null) 81 - FileUtils.deleteQuietly(dir); 82 - return super.delete(); 83 - } 84 - 85 - @Override 86 - public boolean rename(String newFileName) 87 - { 88 - File dir = getCompanionDir(); 89 - if (dir != null) { 90 - try { 91 - String newDirName = FilenameUtils.getBaseName(newFileName); 92 - File newDir = new File(dir.getParentFile(), newDirName); 93 - Files.move(dir.toPath(), newDir.toPath()); 94 - } 95 - catch (IOException e) { 96 - return false; 97 - } 98 - } 99 - return super.rename(newFileName); 100 - } 101 - 102 - @Override 103 - public boolean move(File targetDir) 104 - { 105 - File dir = getCompanionDir(); 106 - if (dir != null) { 107 - try { 108 - FileUtils.moveDirectory(dir, new File(targetDir, dir.getName())); 109 - } 110 - catch (IOException e) { 111 - return false; 112 - } 113 - } 114 - return super.move(targetDir); 115 - } 116 - 117 - @Override 118 - public boolean thumbnailHasCheckerboard() 119 - { 120 - return false; 121 - } 122 - 123 - @Override 124 - protected Image loadThumbnail() 125 - { 126 - if (textures.isEmpty()) 127 - return null; 128 - 129 - var composite = new BufferedImage(THUMB_W, THUMB_H, BufferedImage.TYPE_INT_ARGB); 130 - Graphics2D g = composite.createGraphics(); 131 - g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR); 132 - 133 - int x = 0; 134 - int y = 0; 135 - for (BufferedImage tex : textures) { 136 - float scale = (float) ROW_HEIGHT / tex.getHeight(); 137 - int w = Math.max(1, Math.round(tex.getWidth() * scale)); 138 - int h = ROW_HEIGHT; 139 - 140 - if (x + w > THUMB_W) { 141 - // Wrap to next row 142 - x = 0; 143 - y += ROW_HEIGHT + GAP; 144 - if (y + ROW_HEIGHT > THUMB_H) 145 - break; 146 - } 147 - 148 - g.drawImage(tex, x, y, w, h, null); 149 - x += w + GAP; 150 - } 151 - 152 - g.dispose(); 153 - return composite; 154 - } 155 - 156 - private static BufferedImage readImage(File imgFile) 157 - { 158 - try { 159 - return ImageIO.read(imgFile); 160 - } 161 - catch (IOException e) { 162 - return null; 163 - } 164 - } 165 - }
+138
src/main/java/assets/ui/TexturesAsset.kt
··· 1 + package assets.ui 2 + 3 + import assets.Asset 4 + import assets.AssetSubdir 5 + import org.apache.commons.io.FileUtils 6 + import org.apache.commons.io.FilenameUtils 7 + import util.Logger 8 + import java.awt.Image 9 + import java.awt.RenderingHints 10 + import java.awt.image.BufferedImage 11 + import java.io.File 12 + import java.io.IOException 13 + import java.nio.file.Files 14 + import java.nio.file.Path 15 + import javax.imageio.ImageIO 16 + 17 + class TexturesAsset(root: Path, relativePath: Path) : Asset(root, relativePath) { 18 + private val textures = mutableListOf<BufferedImage>() 19 + 20 + /** Convenience constructor for Java interop. */ 21 + constructor(asset: Asset) : this(asset.root, asset.relativePath) 22 + 23 + init { 24 + val dirName = "${FilenameUtils.getBaseName(name)}/" 25 + val dir = File(root.toFile(), "${AssetSubdir.MAP_TEX}$dirName") 26 + 27 + try { 28 + val images = mutableListOf<File>() 29 + 30 + Files.newDirectoryStream(dir.toPath(), "*.png").use { stream -> 31 + for (file in stream) { 32 + if (Files.isRegularFile(file)) 33 + images.add(file.toFile()) 34 + } 35 + } 36 + 37 + images.shuffle() 38 + 39 + for (image in images) { 40 + val img = readImage(image) 41 + if (img != null) 42 + textures.add(img) 43 + } 44 + } catch (e: IOException) { 45 + Logger.logError("IOException while gathering previews from $dirName") 46 + } 47 + } 48 + 49 + fun getPreview(index: Int): BufferedImage? = 50 + if (index < textures.size) textures[index] else null 51 + 52 + private fun getCompanionDir(): File? { 53 + val dirName = "${FilenameUtils.getBaseName(name)}/" 54 + val dir = File(root.toFile(), "${AssetSubdir.MAP_TEX}$dirName") 55 + return if (dir.isDirectory) dir else null 56 + } 57 + 58 + override fun delete(): Boolean { 59 + val dir = getCompanionDir() 60 + if (dir != null) 61 + FileUtils.deleteQuietly(dir) 62 + return super.delete() 63 + } 64 + 65 + override fun rename(name: String): Boolean { 66 + val dir = getCompanionDir() 67 + if (dir != null) { 68 + try { 69 + val newDirName = FilenameUtils.getBaseName(name) 70 + val newDir = File(dir.parentFile, newDirName) 71 + Files.move(dir.toPath(), newDir.toPath()) 72 + } catch (e: IOException) { 73 + return false 74 + } 75 + } 76 + return super.rename(name) 77 + } 78 + 79 + override fun move(targetDir: File): Boolean { 80 + val dir = getCompanionDir() 81 + if (dir != null) { 82 + try { 83 + FileUtils.moveDirectory(dir, File(targetDir, dir.name)) 84 + } catch (e: IOException) { 85 + return false 86 + } 87 + } 88 + return super.move(targetDir) 89 + } 90 + 91 + override fun thumbnailHasCheckerboard(): Boolean = false 92 + 93 + override fun loadThumbnail(): Image? { 94 + if (textures.isEmpty()) 95 + return null 96 + 97 + val composite = BufferedImage(THUMB_W, THUMB_H, BufferedImage.TYPE_INT_ARGB) 98 + val g = composite.createGraphics() 99 + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR) 100 + 101 + var x = 0 102 + var y = 0 103 + for (tex in textures) { 104 + val scale = ROW_HEIGHT.toFloat() / tex.height 105 + val w = maxOf(1, (tex.width * scale).toInt()) 106 + val h = ROW_HEIGHT 107 + 108 + if (x + w > THUMB_W) { 109 + // Wrap to next row 110 + x = 0 111 + y += ROW_HEIGHT + GAP 112 + if (y + ROW_HEIGHT > THUMB_H) 113 + break 114 + } 115 + 116 + g.drawImage(tex, x, y, w, h, null) 117 + x += w + GAP 118 + } 119 + 120 + g.dispose() 121 + return composite 122 + } 123 + 124 + companion object { 125 + private const val THUMB_W = THUMBNAIL_WIDTH 126 + private const val THUMB_H = THUMBNAIL_HEIGHT 127 + private const val GAP = 2 128 + private const val ROW_HEIGHT = (THUMB_H - GAP) / 2 // two rows 129 + 130 + private fun readImage(imgFile: File): BufferedImage? { 131 + return try { 132 + ImageIO.read(imgFile) 133 + } catch (e: IOException) { 134 + null 135 + } 136 + } 137 + } 138 + }
+2 -2
src/main/java/game/SimpleItem.java
··· 10 10 11 11 import app.Directories; 12 12 import app.Environment; 13 - import assets.AssetHandle; 13 + import assets.Asset; 14 14 import assets.AssetManager; 15 15 import assets.AssetSubdir; 16 16 import game.texture.Tile; ··· 34 34 35 35 this.enumName = "ITEM_" + name.replaceAll("((?<=[a-z0-9])[A-Z]|(?!^)(?<!_)[A-Z](?=[a-z]))", "_$1").toUpperCase(); 36 36 37 - AssetHandle ah = AssetManager.get(AssetSubdir.ICON, iconName + ".png"); 37 + Asset ah = AssetManager.get(AssetSubdir.ICON, iconName + ".png"); 38 38 Tile tile = null; 39 39 40 40 try {
+3 -3
src/main/java/game/globals/IconRecord.java
··· 20 20 import org.w3c.dom.Element; 21 21 22 22 import app.IconResource; 23 - import assets.AssetHandle; 23 + import assets.Asset; 24 24 import assets.AssetManager; 25 25 import assets.AssetSubdir; 26 26 import game.globals.editor.GlobalsRecord; ··· 35 35 public Icon largeIcon; 36 36 public Icon smallIcon; 37 37 38 - public IconRecord(AssetHandle source) throws IOException 38 + public IconRecord(Asset source) throws IOException 39 39 { 40 40 String base = FilenameUtils.removeExtension(source.getRelativePath().toString()); 41 41 name = base.substring("/icon/".length()); ··· 112 112 for (Element iconElem : iconElems) { 113 113 String path = xmr.getAttribute(iconElem, ATTR_NAME); 114 114 115 - AssetHandle ah = AssetManager.get(AssetSubdir.ICON, path + EXT_PNG); 115 + Asset ah = AssetManager.get(AssetSubdir.ICON, path + EXT_PNG); 116 116 icons.add(new IconRecord(ah)); 117 117 } 118 118
+1 -1
src/main/java/game/globals/editor/GlobalsData.java
··· 69 69 File xmlFile = AssetManager.get(AssetSubdir.ICON, "Icons.xml").getFile(); 70 70 icons.addAll(IconRecord.readXML(xmlFile)); 71 71 /* 72 - for(AssetHandle ah : AssetManager.getIcons()) { 72 + for(Asset ah : AssetManager.getIcons()) { 73 73 icons.addElement(new IconRecord(ah)); 74 74 } 75 75 */
+2 -2
src/main/java/game/globals/editor/GlobalsEditor.java
··· 29 29 import app.StarRodFrame; 30 30 import app.SwingUtils; 31 31 import app.config.Options; 32 - import assets.AssetHandle; 32 + import assets.Asset; 33 33 import assets.AssetManager; 34 34 import assets.ExpectedAsset; 35 35 import game.ProjectDatabase; ··· 370 370 messageNameMap.clear(); 371 371 372 372 try { 373 - for (AssetHandle ah : AssetManager.getMessages()) { 373 + for (Asset ah : AssetManager.getMessages()) { 374 374 Logger.log("Reading messages from: " + ah.getName()); 375 375 MessageAsset group = new MessageAsset(ah); 376 376 for (Message msg : group.messages) {
+1
src/main/java/game/map/Map.java
··· 19 19 import java.util.regex.Pattern; 20 20 import java.util.stream.Collectors; 21 21 22 + import assets.LiveAsset; 22 23 import org.apache.commons.io.FileUtils; 23 24 import org.apache.commons.io.FilenameUtils; 24 25 import org.w3c.dom.Element;
+5 -5
src/main/java/game/map/editor/MapEditor.java
··· 50 50 import app.config.Config; 51 51 import app.config.Options; 52 52 import app.config.Options.Scope; 53 - import assets.AssetHandle; 53 + import assets.Asset; 54 54 import assets.AssetManager; 55 55 import assets.ui.SelectMapDialog; 56 56 import assets.ui.SelectTexDialog; ··· 859 859 continue; 860 860 } 861 861 862 - AssetHandle ah = AssetManager.getMap(mapName); 862 + Asset ah = AssetManager.getMap(mapName); 863 863 if (ah.exists()) { 864 864 recentMaps.add(mapName); 865 865 } ··· 1000 1000 if (editorConfig != null) { 1001 1001 String lastMapName = editorConfig.getString(Options.RecentMap0); 1002 1002 if (lastMapName != null && !lastMapName.isBlank()) { 1003 - AssetHandle ah = AssetManager.getMap(lastMapName); 1003 + Asset ah = AssetManager.getMap(lastMapName); 1004 1004 if (ah.exists()) { 1005 1005 options = new String[] { "Browse Maps", "Reopen " + lastMapName }; 1006 1006 lastMap = ah.getFile(); ··· 3993 3993 Logger.logError("Override name is missing or empty."); 3994 3994 return; 3995 3995 } 3996 - AssetHandle ah = AssetManager.getMap(overrideName); 3996 + Asset ah = AssetManager.getMap(overrideName); 3997 3997 if (!ah.exists()) { 3998 3998 Logger.logError("Couldn't find map: " + overrideName + Directories.EXT_MAP); 3999 3999 return; ··· 4014 4014 Logger.logError("Override name is missing or empty."); 4015 4015 return; 4016 4016 } 4017 - AssetHandle ah = AssetManager.getMap(overrideName); 4017 + Asset ah = AssetManager.getMap(overrideName); 4018 4018 if (!ah.exists()) { 4019 4019 Logger.logError("Couldn't find map: " + overrideName + Directories.EXT_MAP); 4020 4020 return;
+2 -2
src/main/java/game/map/editor/render/TextureManager.java
··· 21 21 import app.Resource; 22 22 import app.Resource.ResourceType; 23 23 import app.StarRodMain; 24 - import assets.AssetHandle; 24 + import assets.Asset; 25 25 import assets.AssetManager; 26 26 import game.map.Map; 27 27 import game.map.editor.ui.SwingGUI; ··· 137 137 TextureArchive ta; 138 138 139 139 try { 140 - AssetHandle ah = AssetManager.getTextureArchive(texArchiveName); 140 + Asset ah = AssetManager.getTextureArchive(texArchiveName); 141 141 if (!ah.exists()) 142 142 return false; 143 143 if (FilenameUtils.getExtension(ah.getName()).equals(Directories.EXT_OLD_TEX)) {
+2 -2
src/main/java/game/map/editor/ui/SwingGUI.java
··· 53 53 import app.StarRodMain; 54 54 import app.SwingUtils; 55 55 import app.SwingUtils.OpenDialogCounter; 56 - import assets.AssetHandle; 56 + import assets.Asset; 57 57 import assets.AssetManager; 58 58 import assets.ui.SelectBackgroundDialog; 59 59 import assets.ui.SelectMapDialog; ··· 1303 1303 private void prompt_OpenMap(String mapName) 1304 1304 { 1305 1305 if (!editor.map.modified || promptForSave()) { 1306 - AssetHandle ah = AssetManager.getMap(mapName); 1306 + Asset ah = AssetManager.getMap(mapName); 1307 1307 if (ah.exists()) { 1308 1308 editor.doNextFrame(() -> { 1309 1309 editor.action_OpenMap(ah.getFile());
+3 -3
src/main/java/game/map/shading/SpriteShadingEditor.java
··· 13 13 import com.google.gson.stream.JsonReader; 14 14 15 15 import app.StarRodException; 16 - import assets.AssetHandle; 16 + import assets.Asset; 17 17 import assets.AssetManager; 18 18 import assets.AssetSubdir; 19 19 import util.Logger; ··· 24 24 { 25 25 public static void saveShadingProfiles(SpriteShadingData data) throws IOException 26 26 { 27 - AssetHandle ah = AssetManager.get(AssetSubdir.SPRITE, FN_SPRITE_SHADING); 27 + Asset ah = AssetManager.get(AssetSubdir.SPRITE, FN_SPRITE_SHADING); 28 28 saveShadingProfiles(ah.getFile(), data); 29 29 } 30 30 ··· 57 57 58 58 public static SpriteShadingData loadData() 59 59 { 60 - AssetHandle ah = AssetManager.get(AssetSubdir.SPRITE, FN_SPRITE_SHADING); 60 + Asset ah = AssetManager.get(AssetSubdir.SPRITE, FN_SPRITE_SHADING); 61 61 if (!ah.exists()) 62 62 throw new StarRodException("Could not find sprite shading definitions!"); 63 63
+2 -2
src/main/java/game/message/editor/AssetListTab.java
··· 14 14 import org.apache.commons.io.FilenameUtils; 15 15 16 16 import app.SwingUtils; 17 - import assets.AssetHandle; 17 + import assets.Asset; 18 18 import assets.AssetManager; 19 19 import game.message.Message; 20 20 import net.miginfocom.swing.MigLayout; ··· 90 90 assets.clear(); 91 91 92 92 try { 93 - for (AssetHandle ah : AssetManager.getMessages()) { 93 + for (Asset ah : AssetManager.getMessages()) { 94 94 Logger.log("Reading messages from: " + ah.getName()); 95 95 assets.add(new MessageAsset(ah)); 96 96 }
+2 -2
src/main/java/game/message/editor/AssetTreeTab.java
··· 14 14 import org.apache.commons.io.FilenameUtils; 15 15 16 16 import app.SwingUtils; 17 - import assets.AssetHandle; 17 + import assets.Asset; 18 18 import assets.AssetManager; 19 19 import game.message.Message; 20 20 import net.miginfocom.swing.MigLayout; ··· 91 91 assets.clear(); 92 92 93 93 try { 94 - for (AssetHandle ah : AssetManager.getMessages()) { 94 + for (Asset ah : AssetManager.getMessages()) { 95 95 Logger.log("Reading messages from: " + ah.getName()); 96 96 assets.add(new MessageAsset(ah)); 97 97 }
+4 -4
src/main/java/game/message/editor/MessageAsset.java
··· 10 10 import app.input.IOUtils; 11 11 import app.input.InputFileException; 12 12 import app.input.Line; 13 - import assets.AssetHandle; 13 + import assets.Asset; 14 14 import assets.AssetManager; 15 15 import game.message.Message; 16 16 import game.message.StringEncoder; ··· 18 18 19 19 public class MessageAsset 20 20 { 21 - public AssetHandle asset; 21 + public Asset asset; 22 22 public List<Message> messages = null; 23 23 24 24 public boolean hasModified; 25 25 public boolean hasError; 26 26 27 - public MessageAsset(AssetHandle asset) 27 + public MessageAsset(Asset asset) 28 28 { 29 29 this.asset = asset; 30 30 reload(); ··· 99 99 linesOut.add(""); 100 100 } 101 101 102 - AssetHandle saveAsset = AssetManager.getTopLevel(asset); 102 + Asset saveAsset = AssetManager.getTopLevel(asset); 103 103 try { 104 104 FileUtils.touch(saveAsset.getFile()); 105 105
+2 -2
src/main/java/game/message/editor/MessageEditor.java
··· 73 73 import app.config.Config; 74 74 import app.config.Options; 75 75 import app.config.Options.Scope; 76 - import assets.AssetHandle; 76 + import assets.Asset; 77 77 import assets.AssetManager; 78 78 import assets.AssetSubdir; 79 79 import common.BaseEditor; ··· 474 474 int i = 1; 475 475 for (SpecialCharacter sc : StringConstants.SpecialCharacter.values()) { 476 476 String imgName = String.format("%02X", sc.code) + ".png"; 477 - AssetHandle ah = AssetManager.get(AssetSubdir.STANDARD_CHARS, imgName); 477 + Asset ah = AssetManager.get(AssetSubdir.STANDARD_CHARS, imgName); 478 478 479 479 JButton button = new JButton(); 480 480 try {
+4 -4
src/main/java/game/sprite/ImgAsset.java
··· 7 7 8 8 import org.apache.commons.io.FilenameUtils; 9 9 10 - import assets.AssetHandle; 10 + import assets.Asset; 11 11 import game.sprite.editor.ImgPreview; 12 12 import game.texture.Palette; 13 13 import game.texture.Tile; ··· 15 15 public class ImgAsset implements GLResource 16 16 { 17 17 // source is not final because it updates upon saving 18 - private AssetHandle source; 18 + private Asset source; 19 19 20 20 public final Tile img; 21 21 public final ImgPreview preview; ··· 24 24 public transient int atlasRow, atlasX, atlasY; 25 25 public transient boolean inUse; 26 26 27 - public ImgAsset(AssetHandle ah) throws IOException 27 + public ImgAsset(Asset ah) throws IOException 28 28 { 29 29 source = ah; 30 30 img = Tile.load(ah.getFile(), CI_4); 31 31 preview = new ImgPreview(); 32 32 } 33 33 34 - public AssetHandle getSource() 34 + public Asset getSource() 35 35 { 36 36 return source; 37 37 }
+4 -4
src/main/java/game/sprite/PalAsset.java
··· 8 8 9 9 import org.apache.commons.io.FilenameUtils; 10 10 11 - import assets.AssetHandle; 11 + import assets.Asset; 12 12 import assets.AssetManager; 13 13 import game.sprite.editor.Editable; 14 14 import game.texture.Palette; ··· 16 16 17 17 public class PalAsset implements GLResource, Editable 18 18 { 19 - private AssetHandle source; 19 + private Asset source; 20 20 private final Tile sourceImg; 21 21 22 22 public final Palette pal; ··· 30 30 // used for accounting during cleanup actions 31 31 public transient boolean inUse; 32 32 33 - public PalAsset(AssetHandle ah) throws IOException 33 + public PalAsset(Asset ah) throws IOException 34 34 { 35 35 source = ah; 36 36 sourceImg = Tile.load(source.getFile(), CI_4); ··· 49 49 sourceImg.savePNG(source.getFile().getAbsolutePath()); 50 50 } 51 51 52 - public AssetHandle getSource() 52 + public Asset getSource() 53 53 { 54 54 return source; 55 55 }
+8 -8
src/main/java/game/sprite/Sprite.java
··· 15 15 import org.w3c.dom.Element; 16 16 17 17 import app.input.InputFileException; 18 - import assets.AssetHandle; 18 + import assets.Asset; 19 19 import assets.AssetManager; 20 20 import assets.AssetSubdir; 21 21 import common.Vector3f; ··· 169 169 return name.isEmpty() ? "Unnamed" : name; 170 170 } 171 171 172 - public AssetHandle getAsset() 172 + public Asset getAsset() 173 173 { 174 174 if (metadata.isPlayer) 175 175 return AssetManager.getPlayerSprite(name); ··· 177 177 return AssetManager.getNpcSprite(name); 178 178 } 179 179 180 - public AssetHandle getRoot(boolean modDir) 180 + public Asset getRoot(boolean modDir) 181 181 { 182 - AssetHandle ah; 182 + Asset ah; 183 183 184 184 if (metadata.isPlayer) 185 185 ah = AssetManager.getBase(AssetSubdir.PLR_SPRITE, ""); ··· 192 192 return ah; 193 193 } 194 194 195 - public AssetHandle getRastersDir(boolean modDir) 195 + public Asset getRastersDir(boolean modDir) 196 196 { 197 - AssetHandle ah; 197 + Asset ah; 198 198 199 199 if (metadata.isPlayer) 200 200 ah = AssetManager.get(AssetSubdir.PLR_SPRITE_IMG, ""); ··· 207 207 return ah; 208 208 } 209 209 210 - public AssetHandle getPalettesDir(boolean modDir) 210 + public Asset getPalettesDir(boolean modDir) 211 211 { 212 - AssetHandle ah; 212 + Asset ah; 213 213 214 214 if (metadata.isPlayer) 215 215 ah = AssetManager.get(AssetSubdir.PLR_SPRITE_PAL, "");
+11 -11
src/main/java/game/sprite/SpriteLoader.java
··· 20 20 import org.w3c.dom.Element; 21 21 22 22 import app.Environment; 23 - import assets.AssetHandle; 23 + import assets.Asset; 24 24 import assets.AssetManager; 25 25 import assets.AssetSubdir; 26 26 import game.sprite.Sprite.SpriteSummary; ··· 241 241 } 242 242 243 243 // load all raster assets in parallel 244 - public static LinkedHashMap<String, ImgAsset> loadSpriteImages(Map<String, AssetHandle> assets) 244 + public static LinkedHashMap<String, ImgAsset> loadSpriteImages(Map<String, Asset> assets) 245 245 { 246 246 ConcurrentHashMap<String, ImgAsset> imgAssets = new ConcurrentHashMap<>(); 247 247 List<CompletableFuture<Void>> futures = new ArrayList<>(); 248 248 249 - for (Entry<String, AssetHandle> entry : assets.entrySet()) { 249 + for (Entry<String, Asset> entry : assets.entrySet()) { 250 250 String name = entry.getKey(); 251 - AssetHandle ah = entry.getValue(); 251 + Asset ah = entry.getValue(); 252 252 253 253 CompletableFuture<Void> future = CompletableFuture.runAsync(() -> { 254 254 try { ··· 281 281 } 282 282 283 283 // load all palette assets in parallel 284 - public static LinkedHashMap<String, PalAsset> loadSpritePalettes(Map<String, AssetHandle> assets) 284 + public static LinkedHashMap<String, PalAsset> loadSpritePalettes(Map<String, Asset> assets) 285 285 { 286 286 ConcurrentHashMap<String, PalAsset> palAssets = new ConcurrentHashMap<>(); 287 287 List<CompletableFuture<Void>> futures = new ArrayList<>(); 288 288 289 - for (Entry<String, AssetHandle> entry : assets.entrySet()) { 289 + for (Entry<String, Asset> entry : assets.entrySet()) { 290 290 String name = entry.getKey(); 291 - AssetHandle ah = entry.getValue(); 291 + Asset ah = entry.getValue(); 292 292 293 293 CompletableFuture<Void> future = CompletableFuture.runAsync(() -> { 294 294 try { ··· 322 322 playerSpriteData = new TreeMap<>(); 323 323 324 324 try { 325 - AssetHandle xmlHandle = AssetManager.get(AssetSubdir.SPRITE, "npc.xml"); 325 + Asset xmlHandle = AssetManager.get(AssetSubdir.SPRITE, "npc.xml"); 326 326 if (!xmlHandle.exists()) { 327 327 throw new IOException(xmlHandle + " does not exist!"); 328 328 } ··· 337 337 xmr.requiresAttribute(npcElem, ATTR_NAME); 338 338 339 339 String name = xmr.getAttribute(npcElem, ATTR_NAME); 340 - AssetHandle ah = AssetManager.getNpcSprite(name); 340 + Asset ah = AssetManager.getNpcSprite(name); 341 341 if (!ah.exists()) { 342 342 Logger.logWarning("Cannot find npc sprite '" + name + "'!"); 343 343 continue; ··· 353 353 } 354 354 355 355 try { 356 - AssetHandle xmlHandle = AssetManager.get(AssetSubdir.SPRITE, "player.xml"); 356 + Asset xmlHandle = AssetManager.get(AssetSubdir.SPRITE, "player.xml"); 357 357 if (!xmlHandle.exists()) { 358 358 throw new IOException(xmlHandle + " does not exist!"); 359 359 } ··· 368 368 xmr.requiresAttribute(playerElem, ATTR_NAME); 369 369 370 370 String name = xmr.getAttribute(playerElem, ATTR_NAME); 371 - AssetHandle ah = AssetManager.getPlayerSprite(name); 371 + Asset ah = AssetManager.getPlayerSprite(name); 372 372 if (!ah.exists()) { 373 373 Logger.logWarning("Cannot find player sprite '" + name + "'!"); 374 374 continue;
+4 -4
src/main/java/game/sprite/editor/SpriteEditor.java
··· 51 51 import app.config.Config; 52 52 import app.config.Options; 53 53 import app.config.Options.Scope; 54 - import assets.AssetHandle; 54 + import assets.Asset; 55 55 import assets.AssetManager; 56 56 import common.BaseEditor; 57 57 import common.BaseEditorSettings; ··· 1396 1396 item.addActionListener((evt) -> { 1397 1397 if (sprite != null) { 1398 1398 try { 1399 - AssetHandle ah = sprite.getRoot(true); 1399 + Asset ah = sprite.getRoot(true); 1400 1400 ah.getFile().mkdirs(); 1401 1401 Desktop.getDesktop().open(ah.getFile()); 1402 1402 } ··· 1411 1411 item.addActionListener((evt) -> { 1412 1412 if (sprite != null) { 1413 1413 try { 1414 - AssetHandle ah = sprite.getRoot(false); 1414 + Asset ah = sprite.getRoot(false); 1415 1415 Desktop.getDesktop().open(ah.getFile()); 1416 1416 } 1417 1417 catch (IOException e) { ··· 1975 1975 1976 1976 private void saveSprite(Sprite spr) 1977 1977 { 1978 - AssetHandle ah = spr.getAsset(); 1978 + Asset ah = spr.getAsset(); 1979 1979 ah = AssetManager.getTopLevel(ah); 1980 1980 1981 1981 sprite.reindex();
+4 -4
src/main/java/game/texture/Texture.java
··· 10 10 import org.apache.commons.io.FilenameUtils; 11 11 12 12 import app.input.InputFileException; 13 - import assets.AssetHandle; 13 + import assets.Asset; 14 14 import assets.AssetManager; 15 15 import assets.AssetSubdir; 16 16 import game.texture.TextureArchive.JsonTexture; ··· 309 309 throw new InputFileException(source, "(%s) Texture cannot have both mipmaps and aux.", json.name); 310 310 } 311 311 312 - AssetHandle mainAsset = AssetManager.get(AssetSubdir.MAP_TEX, texName + "/" + tx.name + ".png"); 312 + Asset mainAsset = AssetManager.get(AssetSubdir.MAP_TEX, texName + "/" + tx.name + ".png"); 313 313 314 314 tx.main = Tile.load(mainAsset.getFile(), imgFormat); 315 315 316 316 if (tx.hasAux) { 317 - AssetHandle auxAsset = AssetManager.get(AssetSubdir.MAP_TEX, texName + "/" + tx.name + "_AUX.png"); 317 + Asset auxAsset = AssetManager.get(AssetSubdir.MAP_TEX, texName + "/" + tx.name + "_AUX.png"); 318 318 tx.aux = Tile.load(auxAsset.getFile(), auxFormat); 319 319 } 320 320 ··· 329 329 int mmWidth = tx.main.width / divisor; 330 330 331 331 String mmName = tx.name + "_MM" + (tx.mipmapList.size() + 1); 332 - AssetHandle mmAsset = AssetManager.get(AssetSubdir.MAP_TEX, texName + "/" + mmName + ".png"); 332 + Asset mmAsset = AssetManager.get(AssetSubdir.MAP_TEX, texName + "/" + mmName + ".png"); 333 333 Tile mipmap = Tile.load(mmAsset.getFile(), imgFormat); 334 334 335 335 if (mipmap.height != mmHeight)