4 Commits

Author SHA1 Message Date
Melledy
33b1cf55d4 Update data version to 60 2025-12-01 21:43:06 -08:00
Melledy
aecea6ab03 Implement !build command for creating records 2025-12-01 13:33:34 -08:00
Melledy
e8e7df7d50 Fix incorrect element type for wind/aqua 2025-12-01 13:21:42 -08:00
Melledy
9188d3b53a Improve command arg handling 2025-12-01 13:16:24 -08:00
7 changed files with 263 additions and 23 deletions

View File

@@ -6,7 +6,7 @@ import emu.nebula.game.inventory.ItemParam;
import emu.nebula.util.WeightedList; import emu.nebula.util.WeightedList;
public class GameConstants { public class GameConstants {
private static final int DATA_VERSION = 54; private static final int DATA_VERSION = 60;
private static final String VERSION = "1.2.0"; private static final String VERSION = "1.2.0";
public static final ZoneId UTC_ZONE = ZoneId.of("UTC"); public static final ZoneId UTC_ZONE = ZoneId.of("UTC");

View File

@@ -7,8 +7,8 @@ import emu.nebula.game.character.GameCharacter;
import emu.nebula.game.character.GameDisc; import emu.nebula.game.character.GameDisc;
import emu.nebula.game.player.Player; import emu.nebula.game.player.Player;
import emu.nebula.util.Utils; 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.Int2IntMap;
import it.unimi.dsi.fastutil.ints.Int2IntOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import it.unimi.dsi.fastutil.objects.ObjectSet; import it.unimi.dsi.fastutil.objects.ObjectSet;
import lombok.Getter; import lombok.Getter;
@@ -53,6 +53,9 @@ public class CommandArgs {
} else if (arg.startsWith("lv")) { // Level } else if (arg.startsWith("lv")) { // Level
this.level = Utils.parseSafeInt(arg.substring(2)); this.level = Utils.parseSafeInt(arg.substring(2));
it.remove(); it.remove();
} else if (arg.startsWith("lvl")) { // Level
this.level = Utils.parseSafeInt(arg.substring(3));
it.remove();
} else if (arg.startsWith("a")) { // Advance } else if (arg.startsWith("a")) { // Advance
this.advance = Utils.parseSafeInt(arg.substring(1)); this.advance = Utils.parseSafeInt(arg.substring(1));
it.remove(); it.remove();
@@ -76,7 +79,7 @@ public class CommandArgs {
int key = Integer.parseInt(split[0]); int key = Integer.parseInt(split[0]);
int value = Integer.parseInt(split[1]); int value = Integer.parseInt(split[1]);
if (this.map == null) this.map = new Int2IntOpenHashMap(); if (this.map == null) this.map = new Int2IntLinkedOpenHashMap();
this.map.put(key, value); this.map.put(key, value);
it.remove(); it.remove();

View File

@@ -0,0 +1,179 @@
package emu.nebula.command.commands;
import java.util.ArrayList;
import java.util.List;
import emu.nebula.command.Command;
import emu.nebula.command.CommandArgs;
import emu.nebula.command.CommandHandler;
import emu.nebula.data.GameData;
import emu.nebula.game.character.GameCharacter;
import emu.nebula.game.character.GameDisc;
import emu.nebula.game.player.Player;
import emu.nebula.game.tower.StarTowerBuild;
import emu.nebula.net.NetMsgId;
import emu.nebula.util.Utils;
import lombok.Getter;
@Command(
label = "build",
aliases = {"b", "record", "r"},
permission = "player.build",
requireTarget = true,
desc = "!build [char ids...] [disc ids...] [potential ids...] [melody ids...]"
)
public class BuildCommand implements CommandHandler {
@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;
this.parseItem(builder, id, count);
}
if (args.getMap() != null) {
for (var entry : args.getMap().int2IntEntrySet()) {
int id = entry.getIntKey();
int count = entry.getIntValue();
this.parseItem(builder, id, count);
}
}
// Check if build is valid
if (builder.getCharacters().size() != 3) {
return "Record must have 3 different characters";
}
if (builder.getDiscs().size() < 3 || builder.getDiscs().size() > 6) {
return "Record must have 3-6 different discs";
}
// Create record
var build = builder.toBuild();
// Add to star tower manager
target.getStarTowerManager().getBuilds().put(build.getUid(), build);
// Send package to player
target.addNextPackage(NetMsgId.st_import_build_notify, build.toProto());
// Send result to player
return "Created record for " + target.getName() + " (This command make take time to update on the client)";
}
private void parseItem(StarTowerBuildData builder, int id, int count) {
// Get item data
var itemData = GameData.getItemDataTable().get(id);
if (itemData == null) {
return;
}
// Clamp
count = Math.max(count, 1);
// Parse by item id
switch (itemData.getItemSubType()) {
case Char -> {
var character = builder.getPlayer().getCharacters().getCharacterById(id);
if (character == null || !character.getData().isAvailable()) {
break;
}
builder.addCharacter(character);
}
case Disc -> {
var disc = builder.getPlayer().getCharacters().getDiscById(id);
if (disc == null || !disc.getData().isAvailable()) {
break;
}
builder.addDisc(disc);
}
case Potential, SpecificPotential -> {
var potentialData = GameData.getPotentialDataTable().get(id);
if (potentialData == null) break;
int level = Math.min(count, potentialData.getMaxLevel());
builder.getBuild().getPotentials().add(id, level);
}
case SubNoteSkill -> {
builder.getBuild().getSubNoteSkills().add(id, count);
}
default -> {
// Ignored
}
}
}
@Getter
private static class StarTowerBuildData {
private Player player;
private StarTowerBuild build;
private List<GameCharacter> characters;
private List<GameDisc> discs;
public StarTowerBuildData(Player player) {
this.player = player;
this.build = new StarTowerBuild(player);
this.characters = new ArrayList<>();
this.discs = new ArrayList<>();
}
public void addCharacter(GameCharacter character) {
if (this.characters.contains(character)) {
return;
}
this.characters.add(character);
}
public void addDisc(GameDisc disc) {
if (this.discs.contains(disc)) {
return;
}
this.discs.add(disc);
}
public StarTowerBuild toBuild() {
// Set characters and discs
build.setChars(this.getCharacters());
build.setDiscs(this.getDiscs());
// Clear character potential cache
build.getCharPots().clear();
for (int charId : build.getCharIds()) {
build.getCharPots().put(charId, 0);
}
// Add potentials to character potential cache
var it = build.getPotentials().iterator();
while (it.hasNext()) {
var potential = it.next();
var data = GameData.getPotentialDataTable().get(potential.getIntKey());
if (data == null || !build.getCharPots().containsKey(data.getCharId())) {
it.remove();
continue;
}
build.getCharPots().add(data.getCharId(), 1);
}
// Calculate score
build.calculateScore();
// Return build
return build;
}
}
}

View File

@@ -13,6 +13,8 @@ public class PotentialDef extends BaseDef {
private int MaxLevel; private int MaxLevel;
private int[] BuildScore; private int[] BuildScore;
private String BriefDesc;
@Override @Override
public int getId() { public int getId() {
return Id; return Id;

View File

@@ -7,10 +7,10 @@ import lombok.Getter;
@Getter @Getter
public enum ElementType { public enum ElementType {
INHERIT (0), INHERIT (0),
WIND (1, 90020), AQUA (1, 90018),
FIRE (2, 90019), FIRE (2, 90019),
EARTH (3, 90021), EARTH (3, 90021),
AQUA (4, 90018), WIND (4, 90020),
LIGHT (5, 90022), LIGHT (5, 90022),
DARK (6, 90023), DARK (6, 90023),
NONE (7); NONE (7);

View File

@@ -1,12 +1,17 @@
package emu.nebula.game.tower; package emu.nebula.game.tower;
import java.util.List;
import dev.morphia.annotations.Entity; import dev.morphia.annotations.Entity;
import dev.morphia.annotations.Id; import dev.morphia.annotations.Id;
import dev.morphia.annotations.Indexed; import dev.morphia.annotations.Indexed;
import emu.nebula.Nebula; import emu.nebula.Nebula;
import emu.nebula.data.GameData; import emu.nebula.data.GameData;
import emu.nebula.database.GameDatabaseObject; import emu.nebula.database.GameDatabaseObject;
import emu.nebula.game.character.GameCharacter;
import emu.nebula.game.character.GameDisc;
import emu.nebula.game.inventory.ItemParamMap; import emu.nebula.game.inventory.ItemParamMap;
import emu.nebula.game.player.Player;
import emu.nebula.proto.Public.ItemTpl; import emu.nebula.proto.Public.ItemTpl;
import emu.nebula.proto.PublicStarTower.BuildPotential; import emu.nebula.proto.PublicStarTower.BuildPotential;
import emu.nebula.proto.PublicStarTower.StarTowerBuildBrief; import emu.nebula.proto.PublicStarTower.StarTowerBuildBrief;
@@ -42,18 +47,23 @@ public class StarTowerBuild implements GameDatabaseObject {
} }
public StarTowerBuild(StarTowerGame game) { public StarTowerBuild(Player player) {
this.uid = Snowflake.newUid(); this.uid = Snowflake.newUid();
this.playerUid = game.getPlayer().getUid(); this.playerUid = player.getUid();
this.name = ""; this.name = "";
this.charPots = new ItemParamMap(); this.charPots = new ItemParamMap();
this.potentials = new ItemParamMap(); this.potentials = new ItemParamMap();
this.subNoteSkills = new ItemParamMap(); this.subNoteSkills = new ItemParamMap();
}
public StarTowerBuild(StarTowerGame game) {
// Initialize basic variables
this(game.getPlayer());
// Characters // Characters
this.charIds = game.getChars().stream() this.charIds = game.getChars().stream()
.filter(d -> d.getId() > 0) .filter(c -> c.getId() > 0)
.mapToInt(d -> d.getId()) .mapToInt(c -> c.getId())
.toArray(); .toArray();
// Discs // Discs
@@ -85,7 +95,19 @@ public class StarTowerBuild implements GameDatabaseObject {
} }
// Caclulate record score and cache it // Caclulate record score and cache it
this.score = this.calculateScore(); this.calculateScore();
}
public void setChars(List<GameCharacter> characters) {
this.charIds = characters.stream()
.mapToInt(c -> c.getCharId())
.toArray();
}
public void setDiscs(List<GameDisc> discs) {
this.discIds = discs.stream()
.mapToInt(d -> d.getDiscId())
.toArray();
} }
public void setName(String newName) { public void setName(String newName) {
@@ -109,9 +131,9 @@ public class StarTowerBuild implements GameDatabaseObject {
// Score // Score
private int calculateScore() { public int calculateScore() {
// Init score // Clear score
int score = 0; this.score = 0;
// Potentials // Potentials
for (var potential : this.getPotentials().int2IntEntrySet()) { for (var potential : this.getPotentials().int2IntEntrySet()) {
@@ -119,16 +141,16 @@ public class StarTowerBuild implements GameDatabaseObject {
if (data == null) continue; if (data == null) continue;
int index = potential.getIntValue() - 1; int index = potential.getIntValue() - 1;
score += data.getBuildScore()[index]; this.score += data.getBuildScore()[index];
} }
// Sub note skills // Sub note skills
for (var item : this.getSubNoteSkills()) { for (var item : this.getSubNoteSkills()) {
score += item.getIntValue() * 15; this.score += item.getIntValue() * 15;
} }
// Complete // Complete
return score; return this.score;
} }
// Proto // Proto

View File

@@ -17,12 +17,13 @@ import emu.nebula.data.GameData;
import emu.nebula.data.ResourceType; import emu.nebula.data.ResourceType;
import emu.nebula.data.resources.CharacterDef; import emu.nebula.data.resources.CharacterDef;
import emu.nebula.data.resources.ItemDef; import emu.nebula.data.resources.ItemDef;
import emu.nebula.data.resources.PotentialDef;
import emu.nebula.game.inventory.ItemType;
public class Handbook { public class Handbook {
public static void generate() { public static void generate() {
// Temp vars // Temp vars
Map<String, String> languageKey = null;
List<Integer> list = null; List<Integer> list = null;
// Save to file // Save to file
@@ -41,31 +42,64 @@ public class Handbook {
writer.println(System.lineSeparator()); writer.println(System.lineSeparator());
writer.println("# Characters"); writer.println("# Characters");
list = GameData.getCharacterDataTable().keySet().intStream().sorted().boxed().toList(); list = GameData.getCharacterDataTable().keySet().intStream().sorted().boxed().toList();
languageKey = loadLanguageKey(CharacterDef.class); var characterLanguageKey = loadLanguageKey(CharacterDef.class);
for (int id : list) { for (int id : list) {
CharacterDef data = GameData.getCharacterDataTable().get(id); CharacterDef data = GameData.getCharacterDataTable().get(id);
writer.print(data.getId()); writer.print(data.getId());
writer.print(" : "); writer.print(" : ");
writer.print(languageKey.getOrDefault(data.getName(), data.getName())); writer.print(characterLanguageKey.getOrDefault(data.getName(), data.getName()));
writer.print(" ("); writer.print(" (");
writer.print(data.getElementType().toString()); writer.print(data.getElementType().toString());
writer.println(")"); writer.println(")");
} }
// Dump characters // Dump items
writer.println(System.lineSeparator()); writer.println(System.lineSeparator());
writer.println("# Items"); writer.println("# Items");
list = GameData.getItemDataTable().keySet().intStream().sorted().boxed().toList(); list = GameData.getItemDataTable().keySet().intStream().sorted().boxed().toList();
languageKey = loadLanguageKey(ItemDef.class); var itemLanguageKey = loadLanguageKey(ItemDef.class);
for (int id : list) { for (int id : list) {
ItemDef data = GameData.getItemDataTable().get(id); ItemDef data = GameData.getItemDataTable().get(id);
writer.print(data.getId()); writer.print(data.getId());
writer.print(" : "); writer.print(" : ");
writer.print(languageKey.getOrDefault(data.getTitle(), data.getTitle())); writer.print(itemLanguageKey.getOrDefault(data.getTitle(), data.getTitle()));
writer.print(" ["); writer.print(" [");
writer.print(data.getItemType()); writer.print(data.getItemType());
writer.println("]"); writer.print("]");
if (data.getItemType() == ItemType.Disc) {
var discData = GameData.getDiscDataTable().get(id);
if (discData != null) {
writer.print(" (");
writer.print(discData.getElementType().toString());
writer.print(")");
}
}
writer.println("");
}
// Dump potentials
writer.println(System.lineSeparator());
writer.println("# Potentials");
list = GameData.getPotentialDataTable().keySet().intStream().sorted().boxed().toList();
var potentialLanguageKey = loadLanguageKey(PotentialDef.class);
for (int id : list) {
PotentialDef data = GameData.getPotentialDataTable().get(id);
writer.print(data.getId());
writer.print(" : ");
CharacterDef charData = GameData.getCharacterDataTable().get(data.getCharId());
writer.print("[");
writer.print(characterLanguageKey.getOrDefault(charData.getName(), charData.getName()));
writer.print("] ");
ItemDef itemData = GameData.getItemDataTable().get(id);
writer.print(itemLanguageKey.getOrDefault(itemData.getTitle(), itemData.getTitle()));
writer.print(" - ");
writer.print(potentialLanguageKey.getOrDefault(data.getBriefDesc(), data.getBriefDesc()));
writer.println("");
} }
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();