mirror of
https://git.revela.dev/itsRevela/LCE-Revelations.git
synced 2026-06-17 10:22:07 +00:00
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.
This commit is contained in:
140
tools/DumpSetHealthBC.java
Normal file
140
tools/DumpSetHealthBC.java
Normal file
@@ -0,0 +1,140 @@
|
||||
import com.jpexs.decompiler.flash.SWF;
|
||||
import com.jpexs.decompiler.flash.abc.*;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.AVM2ConstantPool;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.AVM2Code;
|
||||
import com.jpexs.decompiler.flash.abc.avm2.instructions.*;
|
||||
import com.jpexs.decompiler.flash.abc.types.*;
|
||||
import com.jpexs.decompiler.flash.abc.types.traits.*;
|
||||
import com.jpexs.decompiler.flash.tags.*;
|
||||
import com.jpexs.decompiler.flash.configuration.Configuration;
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Dumps the AVM2 bytecode of the SetHealth method from a HUD SWF.
|
||||
*/
|
||||
public class DumpSetHealthBC {
|
||||
public static void main(String[] args) throws Exception {
|
||||
Configuration.autoDeobfuscate.set(false);
|
||||
SWF swf = new SWF(new FileInputStream(args[0]), false);
|
||||
|
||||
for (Tag tag : swf.getTags()) {
|
||||
if (!(tag instanceof DoABC2Tag)) continue;
|
||||
DoABC2Tag abcTag = (DoABC2Tag) tag;
|
||||
ABC abc = abcTag.getABC();
|
||||
AVM2ConstantPool cp = abc.constants;
|
||||
|
||||
// Find Hud class
|
||||
for (int c = 0; c < abc.instance_info.size(); c++) {
|
||||
Multiname mn = cp.getMultiname(abc.instance_info.get(c).name_index);
|
||||
String name = mn.name_index > 0 ? cp.getString(mn.name_index) : "";
|
||||
if (!"Hud".equals(name)) continue;
|
||||
|
||||
InstanceInfo ii = abc.instance_info.get(c);
|
||||
|
||||
// Find SetHealth method
|
||||
for (Trait t : ii.instance_traits.traits) {
|
||||
if (!(t instanceof TraitMethodGetterSetter)) continue;
|
||||
Multiname tMn = cp.getMultiname(t.name_index);
|
||||
String tName = tMn.name_index > 0 ? cp.getString(tMn.name_index) : "";
|
||||
if (!"SetHealth".equals(tName)) continue;
|
||||
|
||||
TraitMethodGetterSetter tm = (TraitMethodGetterSetter) t;
|
||||
MethodBody body = abc.findBody(tm.method_info);
|
||||
if (body == null) continue;
|
||||
|
||||
AVM2Code code = body.getCode();
|
||||
System.out.println("=== SetHealth bytecode ===");
|
||||
System.out.println("max_stack=" + body.max_stack + " max_regs=" + body.max_regs);
|
||||
System.out.println();
|
||||
|
||||
for (int i = 0; i < code.code.size(); i++) {
|
||||
AVM2Instruction inst = code.code.get(i);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(String.format("[%3d] offset=%d opcode=0x%02X", i, inst.getAddress(), inst.definition.instructionCode));
|
||||
|
||||
// Show operands
|
||||
if (inst.operands != null) {
|
||||
for (int op : inst.operands) {
|
||||
sb.append(" ").append(op);
|
||||
}
|
||||
}
|
||||
|
||||
// Try to resolve multiname operands for property instructions
|
||||
if (inst.operands != null && inst.operands.length > 0) {
|
||||
int mnIdx = inst.operands[0];
|
||||
int opcode = inst.definition.instructionCode;
|
||||
// Common property opcodes that use multiname as first operand
|
||||
if (opcode == 0x66 || opcode == 0x61 || opcode == 0x5D || opcode == 0x60 ||
|
||||
opcode == 0x46 || opcode == 0x4F || opcode == 0x68) {
|
||||
// getproperty, setproperty, findpropstrict, getlex, callproperty, callpropvoid, initproperty
|
||||
try {
|
||||
Multiname propMn = cp.getMultiname(mnIdx);
|
||||
String propName = propMn.name_index > 0 ? cp.getString(propMn.name_index) : "?";
|
||||
sb.append(" -> ").append(propName);
|
||||
} catch (Exception e) {}
|
||||
}
|
||||
}
|
||||
|
||||
// Name the opcode
|
||||
String opName = getOpName(inst.definition.instructionCode);
|
||||
sb.append(" (").append(opName).append(")");
|
||||
|
||||
System.out.println(sb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static String getOpName(int opcode) {
|
||||
switch (opcode) {
|
||||
case 0xD0: return "getlocal0";
|
||||
case 0xD1: return "getlocal1";
|
||||
case 0xD2: return "getlocal2";
|
||||
case 0xD3: return "getlocal3";
|
||||
case 0xD4: return "setlocal0";
|
||||
case 0xD5: return "setlocal1";
|
||||
case 0xD6: return "setlocal2";
|
||||
case 0xD7: return "setlocal3";
|
||||
case 0x62: return "getlocal";
|
||||
case 0x63: return "setlocal";
|
||||
case 0x30: return "pushscope";
|
||||
case 0x47: return "returnvoid";
|
||||
case 0x48: return "returnvalue";
|
||||
case 0x66: return "getproperty";
|
||||
case 0x61: return "setproperty";
|
||||
case 0x5D: return "findpropstrict";
|
||||
case 0x60: return "getlex";
|
||||
case 0x24: return "pushbyte";
|
||||
case 0x25: return "pushint";
|
||||
case 0x26: return "pushtrue";
|
||||
case 0x27: return "pushfalse";
|
||||
case 0x20: return "pushnull";
|
||||
case 0xA0: return "add";
|
||||
case 0x73: return "convert_i";
|
||||
case 0x10: return "jump";
|
||||
case 0x12: return "iffalse";
|
||||
case 0x11: return "iftrue";
|
||||
case 0x15: return "iflt";
|
||||
case 0x16: return "ifle";
|
||||
case 0x13: return "ifgt";
|
||||
case 0x0C: return "ifnlt";
|
||||
case 0x46: return "callproperty";
|
||||
case 0x4F: return "callpropvoid";
|
||||
case 0x68: return "initproperty";
|
||||
case 0x4A: return "constructprop";
|
||||
case 0x75: return "convert_d";
|
||||
case 0x29: return "pop";
|
||||
case 0x2A: return "dup";
|
||||
case 0xAB: return "equals";
|
||||
case 0xAD: return "lessthan";
|
||||
case 0xAE: return "lessequals";
|
||||
case 0xAF: return "greaterthan";
|
||||
case 0xB0: return "greaterequals";
|
||||
case 0x96: return "not";
|
||||
case 0xA8: return "coerce_a";
|
||||
default: return String.format("0x%02X", opcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user