Commit f801fe03 authored by AnimeGitB's avatar AnimeGitB
Browse files

Update GM Handbook with Spincrystal names and Monster strings

parent 4da4c03e
...@@ -11,7 +11,11 @@ import java.nio.file.Path; ...@@ -11,7 +11,11 @@ import java.nio.file.Path;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.*; import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.IntStream;
import emu.grasscutter.GameConstants; import emu.grasscutter.GameConstants;
import emu.grasscutter.Grasscutter; import emu.grasscutter.Grasscutter;
...@@ -21,37 +25,53 @@ import emu.grasscutter.data.GameData; ...@@ -21,37 +25,53 @@ import emu.grasscutter.data.GameData;
import emu.grasscutter.data.ResourceLoader; import emu.grasscutter.data.ResourceLoader;
import emu.grasscutter.data.excels.AvatarData; import emu.grasscutter.data.excels.AvatarData;
import emu.grasscutter.data.excels.ItemData; import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.data.excels.QuestData;
import emu.grasscutter.utils.Language; import emu.grasscutter.utils.Language;
import emu.grasscutter.utils.Language.TextStrings; import emu.grasscutter.utils.Language.TextStrings;
import it.unimi.dsi.fastutil.ints.Int2IntSortedMap;
import it.unimi.dsi.fastutil.ints.Int2IntRBTreeMap; import it.unimi.dsi.fastutil.ints.Int2IntRBTreeMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap;
import it.unimi.dsi.fastutil.ints.IntList;
import lombok.val;
import static emu.grasscutter.config.Configuration.*; import static emu.grasscutter.config.Configuration.*;
import static emu.grasscutter.utils.FileUtils.getResourcePath; import static emu.grasscutter.utils.FileUtils.getResourcePath;
public final class Tools { public final class Tools {
public static void createGmHandbooks() throws Exception { public static void createGmHandbooks() throws Exception {
final List<Language> languages = Language.TextStrings.getLanguages(); val languages = Language.TextStrings.getLanguages();
final Int2ObjectMap<TextStrings> textMaps = Language.getTextMapStrings(); val textMaps = Language.getTextMapStrings();
ResourceLoader.loadAll(); ResourceLoader.loadAll();
final Int2IntSortedMap avatarNames = new Int2IntRBTreeMap(GameData.getAvatarDataMap().int2ObjectEntrySet().stream().collect(Collectors.toMap(e -> (int) e.getIntKey(), e -> (int) e.getValue().getNameTextMapHash()))); val mainQuestTitles = new Int2IntRBTreeMap(GameData.getMainQuestDataMap().int2ObjectEntrySet().stream().collect(Collectors.toMap(e -> (int) e.getIntKey(), e -> (int) e.getValue().getTitleTextMapHash())));
final Int2IntSortedMap itemNames = new Int2IntRBTreeMap(GameData.getItemDataMap().int2ObjectEntrySet().stream().collect(Collectors.toMap(e -> (int) e.getIntKey(), e -> (int) e.getValue().getNameTextMapHash()))); // val questDescs = new Int2IntRBTreeMap(GameData.getQuestDataMap().int2ObjectEntrySet().stream().collect(Collectors.toMap(e -> (int) e.getIntKey(), e -> (int) e.getValue().getDescTextMapHash())));
final Int2IntSortedMap monsterNames = new Int2IntRBTreeMap(GameData.getMonsterDataMap().int2ObjectEntrySet().stream().collect(Collectors.toMap(e -> (int) e.getIntKey(), e -> (int) e.getValue().getNameTextMapHash())));
final Int2IntSortedMap mainQuestTitles = new Int2IntRBTreeMap(GameData.getMainQuestDataMap().int2ObjectEntrySet().stream().collect(Collectors.toMap(e -> (int) e.getIntKey(), e -> (int) e.getValue().getTitleTextMapHash()))); val avatarDataMap = new Int2ObjectRBTreeMap<>(GameData.getAvatarDataMap());
// Int2IntSortedMap questDescs = new Int2IntRBTreeMap(GameData.getQuestDataMap().int2ObjectEntrySet().stream().collect(Collectors.toMap(e -> (int) e.getIntKey(), e -> (int) e.getValue().getDescTextMapHash()))); val itemDataMap = new Int2ObjectRBTreeMap<>(GameData.getItemDataMap());
val monsterDataMap = new Int2ObjectRBTreeMap<>(GameData.getMonsterDataMap());
val sceneDataMap = new Int2ObjectRBTreeMap<>(GameData.getSceneDataMap());
val questDataMap = new Int2ObjectRBTreeMap<>(GameData.getQuestDataMap());
Function<SortedMap, String> getPad = m -> "%" + m.lastKey().toString().length() + "s : ";
// Create builders and helper functions
val handbookBuilders = IntStream.range(0, TextStrings.NUM_LANGUAGES).mapToObj(i -> new StringBuilder()).toList();
Consumer<String> newSection = title -> handbookBuilders.forEach(b -> b.append("\n\n// " + title + "\n"));
Consumer<String> newLine = line -> handbookBuilders.forEach(b -> b.append(line + "\n"));
BiConsumer<String, IntList> newTranslatedLine = (template, textmapHashes) -> {
val textstrings = textmapHashes.intStream().mapToObj(textMaps::get).toList();
for (int i = 0; i < TextStrings.NUM_LANGUAGES; i++) {
String s = template;
for (int j = 0; j < textstrings.size(); j++)
s = s.replace("{"+j+"}", textstrings.get(j).strings[i]);
handbookBuilders.get(i).append(s + "\n");
}
};
// Preamble // Preamble
final List<StringBuilder> handbookBuilders = new ArrayList<>(TextStrings.NUM_LANGUAGES); newLine.accept("// Grasscutter " + GameConstants.VERSION + " GM Handbook");
final String now = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss").format(LocalDateTime.now()); newLine.accept("// Created " + DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss").format(LocalDateTime.now()));
for (int i = 0; i < TextStrings.NUM_LANGUAGES; i++)
handbookBuilders.add(new StringBuilder()
.append("// Grasscutter " + GameConstants.VERSION + " GM Handbook\n")
.append("// Created " + now + "\n\n")
.append("// Commands\n"));
// Commands // Commands
newSection.accept("Commands");
final List<CommandHandler> cmdList = CommandMap.getInstance().getHandlersAsList(); final List<CommandHandler> cmdList = CommandMap.getInstance().getHandlersAsList();
final String padCmdLabel = "%" + cmdList.stream().map(CommandHandler::getLabel).map(String::length).max(Integer::compare).get().toString() + "s : "; final String padCmdLabel = "%" + cmdList.stream().map(CommandHandler::getLabel).map(String::length).max(Integer::compare).get().toString() + "s : ";
for (CommandHandler cmd : cmdList) { for (CommandHandler cmd : cmdList) {
...@@ -62,42 +82,52 @@ public final class Tools { ...@@ -62,42 +82,52 @@ public final class Tools {
handbookBuilders.get(i).append(label + desc + "\n"); handbookBuilders.get(i).append(label + desc + "\n");
} }
} }
// Avatars, Items, Monsters // Avatars
final String[] handbookSections = {"Avatars", "Items", "Monsters"}; newSection.accept("Avatars");
final Int2IntSortedMap[] handbookNames = {avatarNames, itemNames, monsterNames}; val avatarPre = getPad.apply(avatarDataMap);
for (int section = 0; section < handbookSections.length; section++) { avatarDataMap.forEach((id, data) -> newTranslatedLine.accept(avatarPre.formatted(id) + "{0}", IntList.of((int) data.getNameTextMapHash())));
final var h = handbookNames[section]; // Items
final String s = "\n\n// " + handbookSections[section] + "\n"; newSection.accept("Items");
handbookBuilders.forEach(b -> b.append(s)); val itemPre = getPad.apply(itemDataMap);
final String padId = "%" + Integer.toString(h.keySet().lastInt()).length() + "s : "; itemDataMap.forEach((id, data) -> {
h.forEach((id, hash) -> { val nameHash = (int) data.getNameTextMapHash();
final String sId = padId.formatted(id); switch (data.getMaterialType()) {
final TextStrings t = textMaps.get((int) hash); case MATERIAL_BGM:
for (int i = 0; i < TextStrings.NUM_LANGUAGES; i++) val bgmNameHash = Optional.ofNullable(data.getItemUse())
handbookBuilders.get(i).append(sId + t.strings[i] + "\n"); .map(u -> u.get(0))
}); .map(u -> u.getUseParam())
.filter(u -> u.length > 0)
.map(u -> Integer.parseInt(u[0]))
.map(bgmId -> GameData.getHomeWorldBgmDataMap().get(bgmId))
.map(bgm -> bgm.getBgmNameTextMapHash());
if (bgmNameHash.isPresent()) {
int trackNameHash = (int) ((long) bgmNameHash.get()); // Textmap hashes are u32, we index as i32 but store most of them as Long :')
if (textMaps.containsKey(trackNameHash)) {
newTranslatedLine.accept(itemPre.formatted(id) + "{0} - {1}", IntList.of(nameHash, trackNameHash));
return;
}
} // Fall-through
default:
newTranslatedLine.accept(itemPre.formatted(id) + "{0}", IntList.of(nameHash));
return;
} }
// Scenes - no translations
handbookBuilders.forEach(b -> b.append("\n\n// Scenes\n"));
final var sceneDataMap = GameData.getSceneDataMap();
final String padSceneId = "%" + Integer.toString(sceneDataMap.keySet().intStream().max().getAsInt()).length() + "d : ";
sceneDataMap.keySet().intStream().sorted().forEach(id -> {
final String sId = padSceneId.formatted(id);
final String data = sceneDataMap.get(id).getScriptData();
handbookBuilders.forEach(b -> b.append(sId + data + "\n"));
}); });
// Monsters
newSection.accept("Monsters");
val monsterPre = getPad.apply(monsterDataMap);
monsterDataMap.forEach((id, data) -> newTranslatedLine.accept(
monsterPre.formatted(id) + data.getMonsterName() + " - {0}",
IntList.of((int) data.getNameTextMapHash())));
// Scenes - no translations
newSection.accept("Scenes");
val padSceneId = getPad.apply(sceneDataMap);
sceneDataMap.forEach((id, data) -> newLine.accept(padSceneId.formatted(id) + data.getScriptData()));
// Quests // Quests
handbookBuilders.forEach(b -> b.append("\n\n// Quests\n")); newSection.accept("Quests");
final var questDataMap = GameData.getQuestDataMap(); val padQuestId = getPad.apply(questDataMap);
final String padQuestId = "%" + Integer.toString(questDataMap.keySet().intStream().max().getAsInt()).length() + "d : "; questDataMap.forEach((id, data) -> newTranslatedLine.accept(
questDataMap.keySet().intStream().sorted().forEach(id -> { padQuestId.formatted(id) + "{0} - {1}",
final String sId = padQuestId.formatted(id); IntList.of((int) mainQuestTitles.get(data.getMainId()), (int) data.getDescTextMapHash())));
final QuestData data = questDataMap.get(id);
final TextStrings title = textMaps.get((int) mainQuestTitles.get(data.getMainId()));
final TextStrings desc = textMaps.get((int) data.getDescTextMapHash());
for (int i = 0; i < TextStrings.NUM_LANGUAGES; i++)
handbookBuilders.get(i).append(sId + title.strings[i] + " - " + desc.strings[i] + "\n");
});
// Write txt files // Write txt files
for (int i = 0; i < TextStrings.NUM_LANGUAGES; i++) { for (int i = 0; i < TextStrings.NUM_LANGUAGES; i++) {
......
...@@ -20,7 +20,6 @@ import static emu.grasscutter.utils.FileUtils.getResourcePath; ...@@ -20,7 +20,6 @@ import static emu.grasscutter.utils.FileUtils.getResourcePath;
import java.io.BufferedInputStream; import java.io.BufferedInputStream;
import java.io.BufferedOutputStream; import java.io.BufferedOutputStream;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
...@@ -46,6 +45,7 @@ public final class Language { ...@@ -46,6 +45,7 @@ public final class Language {
private final String languageCode; private final String languageCode;
private final Map<String, String> translations = new ConcurrentHashMap<>(); private final Map<String, String> translations = new ConcurrentHashMap<>();
private static boolean scannedTextmaps = false; // Ensure that we don't infinitely rescan on cache misses that don't exist
/** /**
* Creates a language instance from a code. * Creates a language instance from a code.
...@@ -242,7 +242,7 @@ public final class Language { ...@@ -242,7 +242,7 @@ public final class Language {
} }
} }
private static final int TEXTMAP_CACHE_VERSION = 0x9CCACE03; private static final int TEXTMAP_CACHE_VERSION = 0x9CCACE04;
@EqualsAndHashCode public static class TextStrings implements Serializable { @EqualsAndHashCode public static class TextStrings implements Serializable {
public static final String[] ARR_LANGUAGES = {"EN", "CHS", "CHT", "JP", "KR", "DE", "ES", "FR", "ID", "PT", "RU", "TH", "VI"}; public static final String[] ARR_LANGUAGES = {"EN", "CHS", "CHT", "JP", "KR", "DE", "ES", "FR", "ID", "PT", "RU", "TH", "VI"};
public static final String[] ARR_GC_LANGUAGES = {"en-US", "zh-CN", "zh-TW", "ja-JP", "ko-KR", "en-US", "es-ES", "fr-FR", "en-US", "en-US", "ru-RU", "en-US", "en-US"}; // TODO: Update the placeholder en-US entries if we ever add GC translations for the missing client languages public static final String[] ARR_GC_LANGUAGES = {"en-US", "zh-CN", "zh-TW", "ja-JP", "ko-KR", "en-US", "es-ES", "fr-FR", "en-US", "en-US", "ru-RU", "en-US", "en-US"}; // TODO: Update the placeholder en-US entries if we ever add GC translations for the missing client languages
...@@ -385,9 +385,10 @@ public final class Language { ...@@ -385,9 +385,10 @@ public final class Language {
} }
public static TextStrings getTextMapKey(long hash) { public static TextStrings getTextMapKey(long hash) {
if (textMapStrings == null) int key = (int) hash;
if ((textMapStrings == null) || (!scannedTextmaps && !textMapStrings.containsKey(key)))
loadTextMaps(); loadTextMaps();
return textMapStrings.get((int) hash); return textMapStrings.get(key);
} }
public static void loadTextMaps() { public static void loadTextMaps() {
...@@ -429,6 +430,7 @@ public final class Language { ...@@ -429,6 +430,7 @@ public final class Language {
usedHashes.add((int) v.getDescTextMapHash()); usedHashes.add((int) v.getDescTextMapHash());
}); });
GameData.getItemDataMap().forEach((k, v) -> usedHashes.add((int) v.getNameTextMapHash())); GameData.getItemDataMap().forEach((k, v) -> usedHashes.add((int) v.getNameTextMapHash()));
GameData.getHomeWorldBgmDataMap().forEach((k, v) -> usedHashes.add((int) v.getBgmNameTextMapHash()));
GameData.getMonsterDataMap().forEach((k, v) -> usedHashes.add((int) v.getNameTextMapHash())); GameData.getMonsterDataMap().forEach((k, v) -> usedHashes.add((int) v.getNameTextMapHash()));
GameData.getMainQuestDataMap().forEach((k, v) -> usedHashes.add((int) v.getTitleTextMapHash())); GameData.getMainQuestDataMap().forEach((k, v) -> usedHashes.add((int) v.getTitleTextMapHash()));
GameData.getQuestDataMap().forEach((k, v) -> usedHashes.add((int) v.getDescTextMapHash())); GameData.getQuestDataMap().forEach((k, v) -> usedHashes.add((int) v.getDescTextMapHash()));
...@@ -441,6 +443,7 @@ public final class Language { ...@@ -441,6 +443,7 @@ public final class Language {
usedHashes.add((int) 2864268523L); // Weapon Event Wish usedHashes.add((int) 2864268523L); // Weapon Event Wish
textMapStrings = loadTextMapFiles(usedHashes); textMapStrings = loadTextMapFiles(usedHashes);
scannedTextmaps = true;
try { try {
saveTextMapsCache(textMapStrings); saveTextMapsCache(textMapStrings);
} catch (IOException e) { } catch (IOException e) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment