Files
LCE-Revelations/tools/AddHardcoreBitmaps.java
itsRevela 3aa2d23fa9 feat: implement hardcore hearts with game mode lock
Display hardcore heart textures when a world is in hardcore mode,
matching Java Edition behavior. Hearts switch between normal/hardcore
across all states (poison, wither, flash) and all HUD resolutions.

C++ changes:
- IUIScene_HUD: check isHardcore() and call SetHardcoreMode() each tick
- UIScene_HUD: send hardcore boolean to Flash via Iggy, invalidate
  SetHealth dirty check on state change to force heart redraw
- CreateWorldMenu/LoadMenu: lock game mode to Survival when hardcore
- MinecraftServer: gate server.properties hardcore override behind
  MINECRAFT_SERVER_BUILD so offline worlds preserve their saved flag

SWF changes (via new Java tools):
- AddHardcoreBitmaps: adds 10 hardcore heart bitmaps to graphics SWFs
- AddHardcoreHearts: adds 10 new frames (15-24) to health sprite
- PatchHudABC: patches HUD ActionScript bytecode with SetHardcore
  method and frame offset logic (+14 normal/poison, +6 wither)

Also updates README changelog styling with consistent ### headings.
2026-03-30 13:50:29 -05:00

116 lines
3.9 KiB
Java

import com.jpexs.decompiler.flash.SWF;
import com.jpexs.decompiler.flash.tags.*;
import com.jpexs.decompiler.flash.tags.base.*;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.*;
import java.util.*;
/**
* Adds hardcore heart bitmap assets to a skinGraphicsHud SWF.
* These bitmaps are referenced by class name from PlaceObject3 tags in
* the health sprite frames defined in the parent skinHud/skinHDHud SWF.
*
* Usage: AddHardcoreBitmaps <skinGraphicsHud.swf> <textures-dir> <output.swf>
*/
public class AddHardcoreBitmaps {
static final String[] TEXTURE_NAMES = {
"Health_Background_Hardcore",
"Health_Background_Hardcore_Flash",
"Health_Full_Hardcore",
"Health_Half_Hardcore",
"Health_Full_Flash_Hardcore",
"Health_Half_Flash_Hardcore",
"Health_Full_Poison_Hardcore",
"Health_Half_Poison_Hardcore",
"Health_Full_Poison_Flash_Hardcore",
"Health_Half_Poison_Flash_Hardcore"
};
public static void main(String[] args) throws Exception {
if (args.length < 3) {
System.out.println("Usage: AddHardcoreBitmaps <skinGraphicsHud.swf> <textures-dir> <output.swf>");
System.exit(1);
}
String swfPath = args[0];
String texturesDir = args[1];
String outputPath = args[2];
System.out.println("Loading SWF: " + swfPath);
SWF swf = new SWF(new FileInputStream(swfPath), false);
// Find max character ID
int maxCharId = 0;
for (Tag tag : swf.getTags()) {
if (tag instanceof CharacterTag) {
int id = ((CharacterTag) tag).getCharacterId();
if (id > maxCharId) maxCharId = id;
}
}
System.out.println("Max existing character ID: " + maxCharId);
// Find SymbolClass tag
SymbolClassTag symbolClass = null;
int symbolClassIdx = -1;
for (int i = 0; i < swf.getTags().size(); i++) {
if (swf.getTags().get(i) instanceof SymbolClassTag) {
symbolClass = (SymbolClassTag) swf.getTags().get(i);
symbolClassIdx = i;
break;
}
}
if (symbolClass == null) {
System.err.println("ERROR: No SymbolClass tag found");
System.exit(1);
}
int nextCharId = maxCharId + 1;
int added = 0;
for (String texName : TEXTURE_NAMES) {
String pngFile = texturesDir + "/" + texName + ".png";
File f = new File(pngFile);
if (!f.exists()) {
System.err.println("ERROR: Missing texture: " + pngFile);
System.exit(1);
}
BufferedImage img = ImageIO.read(f);
System.out.println(" Adding " + texName + " (id=" + nextCharId + ", " + img.getWidth() + "x" + img.getHeight() + ")");
// Create DefineBitsLossless2 tag
DefineBitsLossless2Tag bmp = new DefineBitsLossless2Tag(swf);
bmp.characterID = nextCharId;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ImageIO.write(img, "PNG", baos);
bmp.setImage(baos.toByteArray());
// Insert before SymbolClass
swf.addTag(symbolClassIdx, bmp);
symbolClassIdx++; // SymbolClass shifted by 1
// Add to SymbolClass
symbolClass.tags.add(nextCharId);
symbolClass.names.add(texName);
nextCharId++;
added++;
}
System.out.println("Added " + added + " hardcore heart bitmaps");
// Mark SymbolClass as modified so new entries get serialized
symbolClass.setModified(true);
// Save
System.out.println("Saving to: " + outputPath);
try (FileOutputStream fos = new FileOutputStream(outputPath)) {
swf.saveTo(fos);
}
System.out.println("Done!");
}
}