Skip to content
Snippets Groups Projects
Commit f801fe03 authored by AnimeGitB's avatar AnimeGitB
Browse files

Update GM Handbook with Spincrystal names and Monster strings

parent 4da4c03e
Branches
Tags
No related merge requests found
......@@ -11,7 +11,11 @@ import java.nio.file.Path;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
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.IntStream;
import emu.grasscutter.GameConstants;
import emu.grasscutter.Grasscutter;
......@@ -21,37 +25,53 @@ import emu.grasscutter.data.GameData;
import emu.grasscutter.data.ResourceLoader;
import emu.grasscutter.data.excels.AvatarData;
import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.data.excels.QuestData;
import emu.grasscutter.utils.Language;
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.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.utils.FileUtils.getResourcePath;
public final class Tools {
public static void createGmHandbooks() throws Exception {
final List<Language> languages = Language.TextStrings.getLanguages();
final Int2ObjectMap<TextStrings> textMaps = Language.getTextMapStrings();
val languages = Language.TextStrings.getLanguages();
val textMaps = Language.getTextMapStrings();
ResourceLoader.loadAll();
final Int2IntSortedMap avatarNames = new Int2IntRBTreeMap(GameData.getAvatarDataMap().int2ObjectEntrySet().stream().collect(Collectors.toMap(e -> (int) e.getIntKey(), e -> (int) e.getValue().getNameTextMapHash())));
final Int2IntSortedMap itemNames = new Int2IntRBTreeMap(GameData.getItemDataMap().int2ObjectEntrySet().stream().collect(Collectors.toMap(e -> (int) e.getIntKey(), e -> (int) e.getValue().getNameTextMapHash())));
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())));
// Int2IntSortedMap questDescs = new Int2IntRBTreeMap(GameData.getQuestDataMap().int2ObjectEntrySet().stream().collect(Collectors.toMap(e -> (int) e.getIntKey(), e -> (int) e.getValue().getDescTextMapHash())));
val mainQuestTitles = new Int2IntRBTreeMap(GameData.getMainQuestDataMap().int2ObjectEntrySet().stream().collect(Collectors.toMap(e -> (int) e.getIntKey(), e -> (int) e.getValue().getTitleTextMapHash())));
// val questDescs = new Int2IntRBTreeMap(GameData.getQuestDataMap().int2ObjectEntrySet().stream().collect(Collectors.toMap(e -> (int) e.getIntKey(), e -> (int) e.getValue().getDescTextMapHash())));
val avatarDataMap = new Int2ObjectRBTreeMap<>(GameData.getAvatarDataMap());
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
final List<StringBuilder> handbookBuilders = new ArrayList<>(TextStrings.NUM_LANGUAGES);
final String now = 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"));
newLine.accept("// Grasscutter " + GameConstants.VERSION + " GM Handbook");
newLine.accept("// Created " + DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss").format(LocalDateTime.now()));
// Commands
newSection.accept("Commands");
final List<CommandHandler> cmdList = CommandMap.getInstance().getHandlersAsList();
final String padCmdLabel = "%" + cmdList.stream().map(CommandHandler::getLabel).map(String::length).max(Integer::compare).get().toString() + "s : ";
for (CommandHandler cmd : cmdList) {
......@@ -62,42 +82,52 @@ public final class Tools {
handbookBuilders.get(i).append(label + desc + "\n");
}
}
// Avatars, Items, Monsters
final String[] handbookSections = {"Avatars", "Items", "Monsters"};
final Int2IntSortedMap[] handbookNames = {avatarNames, itemNames, monsterNames};
for (int section = 0; section < handbookSections.length; section++) {
final var h = handbookNames[section];
final String s = "\n\n// " + handbookSections[section] + "\n";
handbookBuilders.forEach(b -> b.append(s));
final String padId = "%" + Integer.toString(h.keySet().lastInt()).length() + "s : ";
h.forEach((id, hash) -> {
final String sId = padId.formatted(id);
final TextStrings t = textMaps.get((int) hash);
for (int i = 0; i < TextStrings.NUM_LANGUAGES; i++)
handbookBuilders.get(i).append(sId + t.strings[i] + "\n");
});
}
// 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"));
// Avatars
newSection.accept("Avatars");
val avatarPre = getPad.apply(avatarDataMap);
avatarDataMap.forEach((id, data) -> newTranslatedLine.accept(avatarPre.formatted(id) + "{0}", IntList.of((int) data.getNameTextMapHash())));
// Items
newSection.accept("Items");
val itemPre = getPad.apply(itemDataMap);
itemDataMap.forEach((id, data) -> {
val nameHash = (int) data.getNameTextMapHash();
switch (data.getMaterialType()) {
case MATERIAL_BGM:
val bgmNameHash = Optional.ofNullable(data.getItemUse())
.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;
}
});
// 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
handbookBuilders.forEach(b -> b.append("\n\n// Quests\n"));
final var questDataMap = GameData.getQuestDataMap();
final String padQuestId = "%" + Integer.toString(questDataMap.keySet().intStream().max().getAsInt()).length() + "d : ";
questDataMap.keySet().intStream().sorted().forEach(id -> {
final String sId = padQuestId.formatted(id);
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");
});
newSection.accept("Quests");
val padQuestId = getPad.apply(questDataMap);
questDataMap.forEach((id, data) -> newTranslatedLine.accept(
padQuestId.formatted(id) + "{0} - {1}",
IntList.of((int) mainQuestTitles.get(data.getMainId()), (int) data.getDescTextMapHash())));
// Write txt files
for (int i = 0; i < TextStrings.NUM_LANGUAGES; i++) {
......
......@@ -20,7 +20,6 @@ import static emu.grasscutter.utils.FileUtils.getResourcePath;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
......@@ -46,6 +45,7 @@ public final class Language {
private final String languageCode;
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.
......@@ -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 {
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
......@@ -385,9 +385,10 @@ public final class Language {
}
public static TextStrings getTextMapKey(long hash) {
if (textMapStrings == null)
int key = (int) hash;
if ((textMapStrings == null) || (!scannedTextmaps && !textMapStrings.containsKey(key)))
loadTextMaps();
return textMapStrings.get((int) hash);
return textMapStrings.get(key);
}
public static void loadTextMaps() {
......@@ -429,6 +430,7 @@ public final class Language {
usedHashes.add((int) v.getDescTextMapHash());
});
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.getMainQuestDataMap().forEach((k, v) -> usedHashes.add((int) v.getTitleTextMapHash()));
GameData.getQuestDataMap().forEach((k, v) -> usedHashes.add((int) v.getDescTextMapHash()));
......@@ -441,6 +443,7 @@ public final class Language {
usedHashes.add((int) 2864268523L); // Weapon Event Wish
textMapStrings = loadTextMapFiles(usedHashes);
scannedTextmaps = true;
try {
saveTextMapsCache(textMapStrings);
} catch (IOException e) {
......
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