diff --git a/src/main/java/emu/nebula/command/CommandArgs.java b/src/main/java/emu/nebula/command/CommandArgs.java index bc56b9c..e81d6e9 100644 --- a/src/main/java/emu/nebula/command/CommandArgs.java +++ b/src/main/java/emu/nebula/command/CommandArgs.java @@ -1,8 +1,11 @@ package emu.nebula.command; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; import java.util.List; import emu.nebula.Nebula; +import emu.nebula.data.GameData; import emu.nebula.data.resources.AffinityLevelDef; import emu.nebula.game.character.GameCharacter; import emu.nebula.game.character.GameDisc; @@ -10,6 +13,7 @@ import emu.nebula.game.player.Player; import emu.nebula.util.Utils; import it.unimi.dsi.fastutil.ints.Int2IntLinkedOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2IntMap; +import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import it.unimi.dsi.fastutil.objects.ObjectSet; import lombok.Getter; @@ -235,4 +239,120 @@ public class CommandArgs { return hasChanged; } + + /** + * Converts a preset code back into a map of ids + * @param code + * @return + */ + public Int2IntMap parsePresetCode(String code) { + // Init map + var items = new Int2IntOpenHashMap(); + + // Decode preset data from base64 + byte[] data = Utils.base64Decode(code); + + var preset = ByteBuffer.wrap(data); + preset.order(ByteOrder.BIG_ENDIAN); + + // Parse character ids + int[] charIds = new int[3]; + try { + for (int i = 0; i < 3; i++) { + int id = preset.getInt(); + + charIds[i] = id; + items.put(id, 1); + } + } catch (Exception e) { + // Ignored + return null; + } + + // Parse potentials + try { + for (int i = 0; i < 3; i++) { + int charId = charIds[i]; + + // Get potential data + var potentials = GameData.getCharPotentialDataTable().get(charId); + if (potentials == null) { + return null; + } + + // Setup potentials + int[] specificPotentialIds; + int[] normalPotentialIds; + int[] commonPotentialIds = potentials.getCommonPotentialIds(); + + // Check if main trekker or not + if (i == 1) { + specificPotentialIds = potentials.getMasterSpecificPotentialIds(); + normalPotentialIds = potentials.getMasterNormalPotentialIds(); + } else { + specificPotentialIds = potentials.getAssistSpecificPotentialIds(); + normalPotentialIds = potentials.getAssistNormalPotentialIds(); + } + + // Parser + int size = (int) Math.ceil((specificPotentialIds.length + (normalPotentialIds.length * 3) + (commonPotentialIds.length * 3)) / 8D); + byte[] potentialBytes = new byte[size]; + preset.get(potentialBytes); + var potentialData = new BitReader(potentialBytes); + + // Calculate levels of potentials + for (int id : specificPotentialIds) { + int level = potentialData.readBits(1); + if (level > 0) { + items.put(id, level); + } + } + + for (int id : normalPotentialIds) { + int level = potentialData.readBits(3); + if (level > 0) { + items.put(id, level); + } + } + + for (int id : commonPotentialIds) { + int level = potentialData.readBits(3); + if (level > 0) { + items.put(id, level); + } + } + } + } catch (Exception e) { + // Ignored + return null; + } + + // Finished + return items; + } + + @Getter + private static class BitReader { + private byte[] bits; + private int arrayIndex; + private int bitIndex; + + public BitReader(byte[] bits) { + this.bits = bits; + } + + public int readBits(int count) { + int result = 0; + for (int i = 0; i < count; i++) { + result <<= 1; + result |= (this.bits[arrayIndex] >> (7 - bitIndex)) & 1; + bitIndex++; + if (bitIndex >= 8) { + bitIndex = 0; + arrayIndex++; + } + } + return result; + } + } } diff --git a/src/main/java/emu/nebula/command/commands/AutoBuildCommand.java b/src/main/java/emu/nebula/command/commands/AutoBuildCommand.java index b2fe81e..64d33de 100644 --- a/src/main/java/emu/nebula/command/commands/AutoBuildCommand.java +++ b/src/main/java/emu/nebula/command/commands/AutoBuildCommand.java @@ -14,7 +14,6 @@ import emu.nebula.game.character.GameDisc; import emu.nebula.game.inventory.ItemParamMap; import emu.nebula.net.NetMsgId; import emu.nebula.util.Utils; - import it.unimi.dsi.fastutil.ints.IntArrayList; @Command( @@ -40,16 +39,19 @@ public class AutoBuildCommand implements CommandHandler { int id = Utils.parseSafeInt(arg); int count = 1; - builder.parseItem(id, count); + if (id != 0) { + builder.parseItem(id, count); + } else if (!Utils.isNumeric(arg)) { + // Might be a preset code + var items = args.parsePresetCode(arg); + if (items != null) { + builder.parseItems(items); + } + } } if (args.getMap() != null) { - for (var entry : args.getMap().int2IntEntrySet()) { - int id = entry.getIntKey(); - int count = entry.getIntValue(); - - builder.parseItem(id, count); - } + builder.parseItems(args.getMap()); } // Remove extra characters/discs @@ -104,8 +106,27 @@ public class AutoBuildCommand implements CommandHandler { } } + // Calcluate score + builder.toBuild(); + // Pick random potentials and sub notes - this.generate(builder, targetScore); + boolean shouldGenerateNotes = !args.hasFlag("-nn") && !args.hasFlag("-nonotes"); + boolean shouldGeneratePotentials = !args.hasFlag("-np") && !args.hasFlag("-nopotentials") ; + + if (shouldGenerateNotes) { + int score = Math.max(targetScore - builder.getBuild().getScore(), 0); + + if (shouldGeneratePotentials) { + score = (int) (score * .4D); + } + + this.generateSubNotes(builder, score); + } + + if (shouldGeneratePotentials) { + int score = Math.max(targetScore - builder.getBuild().getScore(), 0); + this.generatePotentials(builder, score); + } // Create record var build = builder.toBuild(); @@ -246,10 +267,7 @@ public class AutoBuildCommand implements CommandHandler { builder.getDiscs().add(list.get(0)); } - private void generate(StarTowerBuildData builder, int targetScore) { - // Get possible sub notes - int subNoteScore = (int) (targetScore * .4D); - + private void generateSubNotes(StarTowerBuildData builder, int subNoteScore) { // Get possible drops var drops = new IntArrayList(); @@ -343,11 +361,10 @@ public class AutoBuildCommand implements CommandHandler { } // Calcluate score - builder.toBuild().calculateScore(); - - // Get target potential score - int potentialScore = Math.max(targetScore - builder.getBuild().getScore(), 0); - + builder.toBuild(); + } + + private void generatePotentials(StarTowerBuildData builder, int potentialScore) { // Init weighted list of characters var characters = new ArrayList(); diff --git a/src/main/java/emu/nebula/command/commands/BuildCommand.java b/src/main/java/emu/nebula/command/commands/BuildCommand.java index 428bc1a..c6de893 100644 --- a/src/main/java/emu/nebula/command/commands/BuildCommand.java +++ b/src/main/java/emu/nebula/command/commands/BuildCommand.java @@ -13,6 +13,7 @@ import emu.nebula.game.player.Player; import emu.nebula.game.tower.StarTowerBuild; import emu.nebula.net.NetMsgId; import emu.nebula.util.Utils; +import it.unimi.dsi.fastutil.ints.Int2IntMap; import lombok.Getter; @Command( @@ -35,16 +36,19 @@ public class BuildCommand implements CommandHandler { int id = Utils.parseSafeInt(arg); int count = 1; - builder.parseItem(id, count); + if (id != 0) { + builder.parseItem(id, count); + } else if (!Utils.isNumeric(arg)) { + // Might be a preset code + var items = args.parsePresetCode(arg); + if (items != null) { + builder.parseItems(items); + } + } } if (args.getMap() != null) { - for (var entry : args.getMap().int2IntEntrySet()) { - int id = entry.getIntKey(); - int count = entry.getIntValue(); - - builder.parseItem(id, count); - } + builder.parseItems(args.getMap()); } // Check if build is valid @@ -133,6 +137,15 @@ public class BuildCommand implements CommandHandler { } } + public void parseItems(Int2IntMap items) { + for (var entry : items.int2IntEntrySet()) { + int id = entry.getIntKey(); + int count = entry.getIntValue(); + + this.parseItem(id, count); + } + } + public void addCharacter(GameCharacter character) { if (this.characters.contains(character)) { return; @@ -175,6 +188,9 @@ public class BuildCommand implements CommandHandler { build.getCharPots().add(data.getCharId(), 1); } + // Reset secondary skills + build.refreshSecondarySkills(); + // Calculate score build.calculateScore(); diff --git a/src/main/java/emu/nebula/game/tower/StarTowerBuild.java b/src/main/java/emu/nebula/game/tower/StarTowerBuild.java index df7245c..de7f6c6 100644 --- a/src/main/java/emu/nebula/game/tower/StarTowerBuild.java +++ b/src/main/java/emu/nebula/game/tower/StarTowerBuild.java @@ -134,6 +134,10 @@ public class StarTowerBuild implements GameDatabaseObject { this.preference = state; Nebula.getGameDatabase().update(this, this.getUid(), "preference", this.isPreference()); } + + public void refreshSecondarySkills() { + this.secondarySkills = SecondarySkillDef.calculateSecondarySkills(this.getDiscIds(), this.getSubNoteSkills()); + } // Score @@ -161,7 +165,7 @@ public class StarTowerBuild implements GameDatabaseObject { // Check secondary skills if (this.getSecondarySkills() == null) { - this.secondarySkills = SecondarySkillDef.calculateSecondarySkills(this.getDiscIds(), this.getSubNoteSkills()); + this.refreshSecondarySkills(); } // Add score from secondary skills diff --git a/src/main/java/emu/nebula/util/Utils.java b/src/main/java/emu/nebula/util/Utils.java index 8c5382f..f5548d9 100644 --- a/src/main/java/emu/nebula/util/Utils.java +++ b/src/main/java/emu/nebula/util/Utils.java @@ -169,6 +169,10 @@ public class Utils { return (int) sum; } + public static boolean isNumeric(String str) { + return str.matches("-?\\d+(\\.\\d+)?"); + } + public static double generateRandomDouble() { return ThreadLocalRandom.current().nextDouble(); }