package emu.nebula.command.commands; import java.util.ArrayList; import java.util.Collections; import emu.nebula.GameConstants; import emu.nebula.command.Command; import emu.nebula.command.CommandArgs; import emu.nebula.command.CommandHandler; import emu.nebula.command.commands.BuildCommand.StarTowerBuildData; import emu.nebula.data.GameData; import emu.nebula.game.character.GameCharacter; 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( label = "autobuild", aliases = {"ab"}, permission = "player.build", requireTarget = true, desc = "!autobuild [character/disc/potential/melody ids...] lv[target record level] s[target record score] = Generates a record for the player with the target score/record level" ) public class AutoBuildCommand implements CommandHandler { private static final int[] COMMON_SUB_NOTE_SKILLS = new int[] { 90011, 90012, 90013, 90014, 90015, 90016, 90017 }; @Override public String execute(CommandArgs args) { // Create record var target = args.getTarget(); var builder = new StarTowerBuildData(target); // Parse items for (String arg : args.getList()) { int id = Utils.parseSafeInt(arg); int count = 1; builder.parseItem(id, count); } if (args.getMap() != null) { for (var entry : args.getMap().int2IntEntrySet()) { int id = entry.getIntKey(); int count = entry.getIntValue(); builder.parseItem(id, count); } } // Remove extra characters/discs while (builder.getCharacters().size() > 3) { builder.getCharacters().removeLast(); } while (builder.getDiscs().size() > 6) { builder.getDiscs().removeLast(); } // Add random characters/discs if (builder.getCharacters().size() < 3) { int count = 3 - builder.getCharacters().size(); for (int i = 0; i < count; i++) { this.pickRandomCharacter(builder); } if (builder.getCharacters().size() < 3) { return "Error: Not enough trekkers in the record"; } } if (builder.getDiscs().size() < 6) { int count = 6 - builder.getDiscs().size(); for (int i = 0; i < count; i++) { this.pickRandomDisc(builder); } } // Get target level/score int targetLevel = 0; int targetScore = 0; if (args.getSkill() < 0) { // Default target level is 40 targetLevel = 40; } else { targetScore = args.getSkill(); } if (args.getLevel() > 0) { // Target level overrides target score. targetLevel = args.getLevel(); } // Get target score from record level if (targetLevel > 0) { var data = GameData.getStarTowerBuildRankDataTable().get(targetLevel); if (data != null) { targetScore = data.getMinGrade(); } } // Pick random potentials and sub notes this.generate(builder, targetScore); // Create record var build = builder.toBuild(); // Add to star tower manager target.getStarTowerManager().addBuild(build); // Send package to player target.addNextPackage(NetMsgId.st_import_build_notify, build.toProto()); // Send result to player String result = "Generated record for " + target.getName(); if (args.getSender() == null) { result += " (This command may take time to update on the client)"; } return result; } private void pickRandomCharacter(StarTowerBuildData builder) { // Random list var list = new ArrayList(); // Create list of possible characters to add var characters = builder.getPlayer().getCharacters().getCharacterCollection() .stream() .filter(c -> !builder.getCharacters().contains(c)) .toList(); // Check if record is empty if (builder.getCharacters().isEmpty()) { // Get random vanguard trekker for (var character : characters) { if (character.getData().getDes().getTag().contains(GameConstants.CHARACTER_TAG_VANGUARD)) { list.add(character); } } // Add any trekker if we dont have any vanguard trekkers if (list.isEmpty()) { list.addAll(characters); } } else { // Get element of main trekker var main = builder.getCharacters().get(0); var element = main.getElementType(); // Get trekkers of the same element for (var character : characters) { if (character.getData().getElementType() == element) { list.add(character); } } // Add any trekker if we dont have any trekkers of the same element if (list.isEmpty()) { list.addAll(characters); } // Shuffle list to make it random Collections.shuffle(list); // Add first support trekker we find for (var character : list) { if (character.getData().getDes().getTag().contains(GameConstants.CHARACTER_TAG_SUPPORT)) { builder.getCharacters().add(character); return; } } // If we have no support trekkers, then we look for versatile trekkers for (var character : list) { if (character.getData().getDes().getTag().contains(GameConstants.CHARACTER_TAG_VERSATILE)) { builder.getCharacters().add(character); return; } } } // If we have no versatile or support trekkers, add a random trekker from the remaining list if (list.size() > 0) { builder.getCharacters().add(Utils.randomElement(list)); } } private void pickRandomDisc(StarTowerBuildData builder) { // Get element of main trekker var main = builder.getCharacters().get(0); var element = main.getElementType(); // Random list var list = new ArrayList(); // Create list of possible discs to add var discs = builder.getPlayer().getCharacters().getDiscCollection() .stream() .filter(d -> !builder.getDiscs().contains(d)) .toList(); // Get discs of the same element for (var disc : discs) { if (disc.getData().getElementType() == element) { list.add(disc); } } if (list.isEmpty()) { list.addAll(discs); } // End early if list is still empty if (list.isEmpty()) { return; } // Shuffle list to make it random Collections.shuffle(list); // Find random disc for (var disc : list) { var item = GameData.getItemDataTable().get(disc.getDiscId()); if (item.getRarity() == 1) { builder.getDiscs().add(disc); return; } } for (var disc : list) { var item = GameData.getItemDataTable().get(disc.getDiscId()); if (item.getRarity() == 2) { builder.getDiscs().add(disc); return; } } // Just add the first disc that we can find builder.getDiscs().add(list.get(0)); } private void generate(StarTowerBuildData builder, int targetScore) { // Get possible sub notes int subNoteScore = (int) (targetScore * .4D); // Get possible drops var drops = new IntArrayList(); for (var character : builder.getCharacters()) { var element = character.getData().getElementType(); if (element.getSubNoteSkillItemId() == 0) { continue; } if (!drops.contains(element.getSubNoteSkillItemId())) { drops.add(element.getSubNoteSkillItemId()); } } for (var disc : builder.getDiscs()) { var element = disc.getData().getElementType(); if (element.getSubNoteSkillItemId() == 0) { continue; } if (!drops.contains(element.getSubNoteSkillItemId())) { drops.add(element.getSubNoteSkillItemId()); } } for (int id : COMMON_SUB_NOTE_SKILLS) { drops.add(id); } // Randomize sub note ids Collections.shuffle(drops); // Distribute sub notes randomly int amount = (int) Math.ceil(subNoteScore / 15D); int totalSubNotes = amount; // Allocate budget for each sub note var budget = new ItemParamMap(); double totalValue = 0; for (int subNote : drops) { int value = Utils.randomRange(1, 10); budget.add(subNote, value); totalValue += value; } // Add random sub notes for (int subNote : drops) { // Get budgeted value int value = budget.get(subNote); int count = (int) Math.ceil((value / totalValue) * totalSubNotes); // Get current sub notes int cur = builder.getBuild().getSubNoteSkills().get(subNote); int max = Math.max(99 - cur, 0); // Clamp count = Math.min(Math.min(count, amount), max); amount -= count; // Add sub notes builder.getBuild().getSubNoteSkills().add(subNote, count); } // Add leftover sub notes if (amount > 0) { // Randomize again Collections.shuffle(drops); // Add to first sub note that has less than 99 for (int subNote : drops) { // End if we have no more sub notes to give if (amount <= 0) { break; } // Get current sub notes int cur = builder.getBuild().getSubNoteSkills().get(subNote); if (cur >= 99) { continue; } // Add int count = Math.min(99 - cur, amount); amount -= count; builder.getBuild().getSubNoteSkills().add(subNote, count); } } // Calcluate score builder.toBuild().calculateScore(); // Get target potential score int potentialScore = Math.max(targetScore - builder.getBuild().getScore(), 0); // Init weighted list of characters var characters = new ArrayList(); characters.add(builder.getCharacters().get(0)); characters.add(builder.getCharacters().get(0)); // Main character gets an extra chance to get more potentials characters.add(builder.getCharacters().get(1)); characters.add(builder.getCharacters().get(2)); Collections.shuffle(characters); // Get current amount of special potentials var specialCounter = new ItemParamMap(); for (var entry : builder.getBuild().getPotentials()) { var potential = GameData.getPotentialDataTable().get(entry.getIntKey()); if (potential == null) continue; if (potential.isSpecial()) { specialCounter.add(potential.getCharId(), 1); } } // Cache main trekker var main = builder.getCharacters().get(0); // Get random potentials while (potentialScore > 0 && !characters.isEmpty()) { // Get random character var character = Utils.randomElement(characters); // Get character potential data var data = GameData.getCharPotentialDataTable().get(character.getCharId()); if (data == null) { break; } // Check if we should give a special potential int sp = specialCounter.get(character.getCharId()); boolean special = false; if (sp < 2 && potentialScore >= 180) { special = Utils.randomChance(.25); } if (special) { specialCounter.add(character.getCharId(), 1); } // Get possible potential list var list = data.getPotentialList(main == character, special); // Remove potentials we already have maxed out var potentials = new IntArrayList(); for (int id : list) { // Get potential data var potential = GameData.getPotentialDataTable().get(id); if (potential == null) continue; // Filter out max level ones int curLevel = builder.getBuild().getPotentials().get(id); int maxLevel = potential.getMaxLevel(); if (curLevel >= maxLevel) { continue; } // Add potentials.add(id); } // Remove character if we dont have any possible potentials for it if (potentials.isEmpty()) { characters.removeIf(c -> c == character); continue; } // Get random potential int id = Utils.randomElement(potentials); var potential = GameData.getPotentialDataTable().get(id); // Add builder.getBuild().getPotentials().add(id, 1); // Decrement score potentialScore -= potential.getBuildScore(1); } } }