// • ▌ ▄ ·. ▄▄▄· ▄▄ • ▪ ▄▄· ▄▄▄▄· ▄▄▄· ▐▄▄▄ ▄▄▄ . // ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌· // ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀ // ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌ // ▀▀ █▪▀▀▀ ▀ ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀ ▀ ▀ ▀▀ █▪ ▀▀▀ // Magicbane Emulator Project © 2013 - 2024 // www.magicbane.com package engine.wpak; import engine.gameManager.ConfigManager; import engine.mbEnums; import engine.wpak.data.EffectDescription; import engine.wpak.data.PowerActionEntry; import engine.wpak.data.StatTransfer; import org.pmw.tinylog.Logger; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.regex.Matcher; import java.util.regex.Pattern; public class PowerActionParser { private static final Pattern STRSPLIT_REGEX = Pattern.compile("([^\"]\\S*|\"[^\"]*\")\\s*"); private static final Pattern POWER_ACTION_REGEX = Pattern.compile("(?<=POWERACTIONBEGIN)(.+?)(?=POWERACTIONEND)", Pattern.DOTALL); private static final String powerActionPath = ConfigManager.DEFAULT_DATA_DIR + "wpak/PowerActions.cfg"; public static void parseWpakFile() { // Read .wpak file from disk byte[] fileData; try { fileData = Files.readAllBytes(Paths.get(powerActionPath)); } catch (IOException e) { throw new RuntimeException(e); } String fileContents = new String(fileData); // Iterate over power entries from .wpak data Matcher matcher = POWER_ACTION_REGEX.matcher(fileContents); while (matcher.find()) { PowerActionEntry powerActionEntry = parsePowerActionEntry(matcher.group().trim()); } } private static PowerActionEntry parsePowerActionEntry(String powerActionData) { PowerActionEntry powerActionEntry = new PowerActionEntry(); EffectDescription effectDescription; StatTransfer statTransfer; try { // Remove all lines that contain a # and leading/trailing blank lines powerActionData = powerActionData.replaceAll("(?m)^(\\s*#.*|\\s*)\r?\n?", "").trim(); String[] lineData = powerActionData.split("\n"); // Parse effect entry header Iterator entryIterator = Arrays.stream(lineData).iterator(); String headerLine = entryIterator.next(); ArrayList headerData = new ArrayList<>(); Matcher matcher = STRSPLIT_REGEX.matcher(headerLine.trim()); while (matcher.find()) headerData.add(matcher.group().trim()); Iterator headerIterator = headerData.iterator(); powerActionEntry.action_id = headerIterator.next(); powerActionEntry.action_type = headerIterator.next(); switch (powerActionEntry.action_type) { case "RemoveEffect": effectDescription = new EffectDescription(); effectDescription.effect_id = headerIterator.next(); powerActionEntry.effects.add(effectDescription); break; case "CreateMob": powerActionEntry.petLevel = Integer.parseInt(headerIterator.next()); powerActionEntry.petRace = Integer.parseInt(headerIterator.next()); break; case "DamageOverTime": effectDescription = new EffectDescription(); effectDescription.effect_id = headerIterator.next(); effectDescription.cycleDuration = Integer.parseInt(headerIterator.next()); effectDescription.cycleDelay = Integer.parseInt(headerIterator.next()); powerActionEntry.effects.add(effectDescription); break; case "Transform": case "Invis": case "ApplyEffect": case "ApplyEffects": case "DeferredPower": case "DirectDamage": while (headerIterator.hasNext()) { effectDescription = new EffectDescription(); effectDescription.effect_id = headerIterator.next(); effectDescription.level = Integer.parseInt(headerIterator.next()); powerActionEntry.effects.add(effectDescription); } break; case "TransferStat": statTransfer = new StatTransfer(); statTransfer.fromStat = mbEnums.CostType.valueOf(headerIterator.next()); statTransfer.toStat = mbEnums.CostType.valueOf(headerIterator.next()); statTransfer.fromStatValue = Float.parseFloat(headerIterator.next()); statTransfer.fromCurve = mbEnums.CompoundCurveType.valueOf(headerIterator.next()); statTransfer.toStatValue = Float.parseFloat(headerIterator.next()); statTransfer.toCurve = mbEnums.CompoundCurveType.valueOf(headerIterator.next()); statTransfer.fromStatBool = Boolean.parseBoolean(headerIterator.next()); statTransfer.toStatBool = Boolean.parseBoolean(headerIterator.next()); break; case "TransferStatOT": statTransfer = new StatTransfer(); statTransfer.fromStat = mbEnums.CostType.valueOf(headerIterator.next()); statTransfer.toStat = mbEnums.CostType.valueOf(headerIterator.next()); statTransfer.fromStatValue = Float.parseFloat(headerIterator.next()); statTransfer.fromCurve = mbEnums.CompoundCurveType.valueOf(headerIterator.next()); statTransfer.toStatValue = Float.parseFloat(headerIterator.next()); statTransfer.toCurve = mbEnums.CompoundCurveType.valueOf(headerIterator.next()); statTransfer.fromStatBool = Boolean.parseBoolean(headerIterator.next()); statTransfer.toStatBool = Boolean.parseBoolean(headerIterator.next()); statTransfer.transfer_action = headerIterator.next(); statTransfer.tranfer_ticks = Integer.parseInt(headerIterator.next()); break; case "Teleport":// No arguments for these tags case "TreeChoke": break; default: Logger.error("Unhandled type " + powerActionEntry.action_type + " for Pow4erAction: " + powerActionEntry.action_id); break; } // Process key value pairs after header while (entryIterator.hasNext()) { String lineValue = entryIterator.next(); String[] lineValues = lineValue.split("="); String key = lineValues[0].trim(); String[] arguments; switch (key) { case "BODYPARTS": arguments = lineValues[1].trim().split("\\s+"); for (String bodyPart : arguments) powerActionEntry.bodyparts.add(Integer.parseInt(bodyPart)); break; case "FEMALEBODYPARTS": arguments = lineValues[1].trim().split("\\s+"); for (String bodyPart : arguments) powerActionEntry.femaleBodyParts.add(Integer.parseInt(bodyPart)); break; case "SCALEFACTOR": arguments = lineValues[1].trim().split("\\s+"); for (String bodyPart : arguments) powerActionEntry.scaleFactor.add(Float.parseFloat(bodyPart)); break; case "ISRESISTABLE": powerActionEntry.isResistible = Boolean.parseBoolean(lineValues[1].trim()); break; case "ISAGGRESSIVE": powerActionEntry.isAggressive = Boolean.parseBoolean(lineValues[1].trim()); break; case "BLADETRAILS": powerActionEntry.bladeTrails = Boolean.parseBoolean(lineValues[1].trim()); break; case "SHOULDSHOWWEAPONS": powerActionEntry.shouldShowWeapons = Boolean.parseBoolean(lineValues[1].trim()); break; case "SHOULDSHOWARMOR": powerActionEntry.shouldShowArmor = Boolean.parseBoolean(lineValues[1].trim()); break; case "APPLYEFFECTBLANK": powerActionEntry.applyEffectBlank = Boolean.parseBoolean(lineValues[1].trim()); break; case "WEAROFFEFFECTBLANK": powerActionEntry.wearOffEffectBlank = Boolean.parseBoolean(lineValues[1].trim()); break; case "ATTACKANIMS": arguments = lineValues[1].trim().split("\\s+"); for (String bodyPart : arguments) powerActionEntry.attackAnimations.add(Integer.parseInt(bodyPart)); break; case "REMOVEALL": powerActionEntry.removeAll = Boolean.parseBoolean(lineValues[1].trim()); break; case "WEAROFFEFFECTOTHER": // Keys not parsed go here. case "WEAROFFEFFECTSELF": break; default: Logger.error("Unhandled variable type:" + key + " for powerAction: " + powerActionEntry.action_id); } } } catch (Exception e) { Logger.error(powerActionEntry.action_id + " " + e); } return powerActionEntry; } }