mirror of
https://github.com/jie65535/GrasscutterCommandGenerator.git
synced 2025-06-08 23:19:14 +08:00
Update 3.3 Resources (Items/Avatars/Weapons)
This commit is contained in:
parent
7dc2dcc9db
commit
72351ba60f
@ -31,7 +31,7 @@ namespace GrasscutterTools.Game
|
|||||||
ArtifactSubAttribution = new ItemMap(Resources.ArtifactSubAttribution);
|
ArtifactSubAttribution = new ItemMap(Resources.ArtifactSubAttribution);
|
||||||
Avatars = new ItemMap(Resources.Avatar);
|
Avatars = new ItemMap(Resources.Avatar);
|
||||||
AvatarColors = new ItemMap(Resources.AvatarColor);
|
AvatarColors = new ItemMap(Resources.AvatarColor);
|
||||||
Items = new ItemMap(Resources.Item);
|
Items = new ItemMapGroup(Resources.Item);
|
||||||
Monsters = new ItemMapGroup(Resources.Monsters);
|
Monsters = new ItemMapGroup(Resources.Monsters);
|
||||||
Gadgets = new ItemMapGroup(Resources.Gadget);
|
Gadgets = new ItemMapGroup(Resources.Gadget);
|
||||||
Scenes = new ItemMap(Resources.Scene);
|
Scenes = new ItemMap(Resources.Scene);
|
||||||
@ -56,7 +56,7 @@ namespace GrasscutterTools.Game
|
|||||||
|
|
||||||
public static ItemMap AvatarColors { get; private set; }
|
public static ItemMap AvatarColors { get; private set; }
|
||||||
|
|
||||||
public static ItemMap Items { get; private set; }
|
public static ItemMapGroup Items { get; private set; }
|
||||||
|
|
||||||
public static ItemMapGroup Monsters { get; private set; }
|
public static ItemMapGroup Monsters { get; private set; }
|
||||||
|
|
||||||
|
@ -63,5 +63,21 @@ namespace GrasscutterTools.Game
|
|||||||
/// 获取所有行
|
/// 获取所有行
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IEnumerable<string> AllLines => Values.SelectMany(it => it.Lines);
|
public IEnumerable<string> AllLines => Values.SelectMany(it => it.Lines);
|
||||||
|
|
||||||
|
private string[] lines;
|
||||||
|
public string[] Lines => lines ?? (lines = AllLines.ToArray());
|
||||||
|
|
||||||
|
public string this[int id]
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
foreach (var map in Values) {
|
||||||
|
var n = map[id];
|
||||||
|
if (n != ItemMap.EmptyName)
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
return ItemMap.EmptyName;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -567,6 +567,7 @@
|
|||||||
<Content Include="Resources\en-us\ShopType.txt" />
|
<Content Include="Resources\en-us\ShopType.txt" />
|
||||||
<Content Include="Resources\en-us\Weapon.txt" />
|
<Content Include="Resources\en-us\Weapon.txt" />
|
||||||
<None Include="Resources\Newtonsoft.Json.dll" />
|
<None Include="Resources\Newtonsoft.Json.dll" />
|
||||||
|
<Content Include="Resources\MyTools.java" />
|
||||||
<Content Include="Resources\ru-ru\Artifact.txt" />
|
<Content Include="Resources\ru-ru\Artifact.txt" />
|
||||||
<Content Include="Resources\ru-ru\ArtifactCat.txt" />
|
<Content Include="Resources\ru-ru\ArtifactCat.txt" />
|
||||||
<Content Include="Resources\ru-ru\ArtifactMainAttribution.txt" />
|
<Content Include="Resources\ru-ru\ArtifactMainAttribution.txt" />
|
||||||
|
@ -181,9 +181,9 @@ namespace GrasscutterTools.Pages
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void LoadMailSelectableItems()
|
private void LoadMailSelectableItems()
|
||||||
{
|
{
|
||||||
MailSelectableItems = new string[GameData.Items.Count + GameData.Weapons.Count + GameData.Artifacts.Count];
|
MailSelectableItems = new string[GameData.Items.Lines.Length + GameData.Weapons.Count + GameData.Artifacts.Count];
|
||||||
int i = 0;
|
int i = 0;
|
||||||
GameData.Items.Lines.CopyTo(MailSelectableItems, i); i += GameData.Items.Count;
|
GameData.Items.Lines.CopyTo(MailSelectableItems, i); i += GameData.Items.Lines.Length;
|
||||||
GameData.Weapons.Lines.CopyTo(MailSelectableItems, i); i += GameData.Weapons.Count;
|
GameData.Weapons.Lines.CopyTo(MailSelectableItems, i); i += GameData.Weapons.Count;
|
||||||
GameData.Artifacts.Lines.CopyTo(MailSelectableItems, i); i += GameData.Artifacts.Count;
|
GameData.Artifacts.Lines.CopyTo(MailSelectableItems, i); i += GameData.Artifacts.Count;
|
||||||
|
|
||||||
|
394
Source/GrasscutterTools/Resources/MyTools.java
Normal file
394
Source/GrasscutterTools/Resources/MyTools.java
Normal file
@ -0,0 +1,394 @@
|
|||||||
|
package emu.grasscutter.tools;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.io.PrintWriter;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.time.format.DateTimeFormatter;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
import java.util.stream.LongStream;
|
||||||
|
|
||||||
|
import emu.grasscutter.GameConstants;
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import emu.grasscutter.command.CommandHandler;
|
||||||
|
import emu.grasscutter.command.CommandMap;
|
||||||
|
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.game.inventory.ItemType;
|
||||||
|
import emu.grasscutter.game.inventory.MaterialType;
|
||||||
|
import emu.grasscutter.game.props.MonsterType;
|
||||||
|
import emu.grasscutter.utils.Language;
|
||||||
|
import emu.grasscutter.utils.Language.TextStrings;
|
||||||
|
import emu.grasscutter.utils.SparseSet;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2IntRBTreeMap;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap;
|
||||||
|
import lombok.val;
|
||||||
|
|
||||||
|
import static emu.grasscutter.config.Configuration.*;
|
||||||
|
import static emu.grasscutter.utils.FileUtils.getResourcePath;
|
||||||
|
import static emu.grasscutter.utils.Language.getTextMapKey;
|
||||||
|
|
||||||
|
public final class Tools {
|
||||||
|
public static void createGmHandbooks() throws Exception {
|
||||||
|
val languages = Language.TextStrings.getLanguages();
|
||||||
|
|
||||||
|
ResourceLoader.loadAll();
|
||||||
|
|
||||||
|
SparseSet illegalWeaponIds = new SparseSet("""
|
||||||
|
10000-10008, 11411, 11506-11508, 12505, 12506, 12508, 12509,
|
||||||
|
13503, 13506, 14411, 14503, 14505, 14508, 15504-15506
|
||||||
|
""");
|
||||||
|
|
||||||
|
SparseSet illegalRelicIds = new SparseSet("""
|
||||||
|
20001, 23300-23340, 23383-23385, 78310-78554, 99310-99554
|
||||||
|
""");
|
||||||
|
|
||||||
|
SparseSet illegalItemIds = new SparseSet("""
|
||||||
|
3004-3022, 100086, 100087, 100100-101000, 101106-101110, 101306, 101500-104000,
|
||||||
|
105001, 105004, 106000-107000, 107011, 108000, 109000-110000,
|
||||||
|
115000-130000, 200200-200899, 220050, 220054
|
||||||
|
""");
|
||||||
|
|
||||||
|
|
||||||
|
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());
|
||||||
|
|
||||||
|
// Create builders and helper functions
|
||||||
|
val handbookBuilders = IntStream.range(0, TextStrings.NUM_LANGUAGES).mapToObj(i -> new StringBuilder()).toList();
|
||||||
|
var h = new Object() {
|
||||||
|
void newLine(String line) {
|
||||||
|
handbookBuilders.forEach(b -> b.append(line + "\n"));
|
||||||
|
}
|
||||||
|
void newSection(String title) {
|
||||||
|
newLine("\n\n// " + title);
|
||||||
|
}
|
||||||
|
void newTranslatedLine(String template, TextStrings... textstrings) {
|
||||||
|
for (int i = 0; i < TextStrings.NUM_LANGUAGES; i++) {
|
||||||
|
String s = template;
|
||||||
|
for (int j = 0; j < textstrings.length; j++)
|
||||||
|
s = s.replace("{"+j+"}", textstrings[j].strings[i]);
|
||||||
|
handbookBuilders.get(i).append(s + "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void newTranslatedLine(String template, long... hashes) {
|
||||||
|
newTranslatedLine(template, LongStream.of(hashes).mapToObj(hash -> getTextMapKey(hash)).toArray(TextStrings[]::new));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Preamble
|
||||||
|
h.newLine("// Grasscutter " + GameConstants.VERSION + " GM Handbook");
|
||||||
|
h.newLine("// Created " + DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss").format(LocalDateTime.now()));
|
||||||
|
|
||||||
|
// Commands
|
||||||
|
h.newSection("Commands");
|
||||||
|
final List<CommandHandler> cmdList = CommandMap.getInstance().getHandlersAsList();
|
||||||
|
final String padCmdLabel = "%s:";
|
||||||
|
for (CommandHandler cmd : cmdList) {
|
||||||
|
final String label = padCmdLabel.formatted(cmd.getLabel());
|
||||||
|
final String descKey = cmd.getDescriptionKey();
|
||||||
|
for (int i = 0; i < TextStrings.NUM_LANGUAGES; i++) {
|
||||||
|
String desc = languages.get(i).get(descKey).replace("\n", "\n\t");
|
||||||
|
handbookBuilders.get(i).append(label + desc + "\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Avatars
|
||||||
|
h.newSection("Avatars");
|
||||||
|
for (val data : avatarDataMap.values()) {
|
||||||
|
if (data.getId() >= 10000002 && data.getId() < 11000000)
|
||||||
|
h.newTranslatedLine("%d:{0}".formatted(data.getId()), data.getNameTextMapHash());
|
||||||
|
}
|
||||||
|
// avatarDataMap.forEach((id, data) -> h.newTranslatedLine("%d:".formatted(id) + "{0}", data.getNameTextMapHash()));
|
||||||
|
// Items
|
||||||
|
h.newSection("Items");
|
||||||
|
Map<ItemType, List<ItemData>> items = new HashMap<>();
|
||||||
|
for (val type : ItemType.values()) {
|
||||||
|
items.put(type, new ArrayList<>());
|
||||||
|
}
|
||||||
|
for (val item : itemDataMap.values()) {
|
||||||
|
if (!illegalItemIds.contains(item.getId()))
|
||||||
|
items.get(item.getItemType()).add(item);
|
||||||
|
}
|
||||||
|
items.get(ItemType.ITEM_WEAPON).removeIf(data -> illegalWeaponIds.contains(data.getId()));
|
||||||
|
items.get(ItemType.ITEM_RELIQUARY).removeIf(data -> illegalRelicIds.contains(data.getId()));
|
||||||
|
for (val kv : items.entrySet()
|
||||||
|
.stream()
|
||||||
|
.filter(e -> e.getValue().size() > 0)
|
||||||
|
.sorted(Comparator.comparing(e -> e.getValue().get(0).getId()))
|
||||||
|
.toList()) {
|
||||||
|
|
||||||
|
if (kv.getValue().size() == 0)
|
||||||
|
continue;
|
||||||
|
h.newSection(kv.getKey().name());
|
||||||
|
if (kv.getKey() == ItemType.ITEM_MATERIAL) {
|
||||||
|
Map<MaterialType, List<ItemData>> materials = new HashMap<>();
|
||||||
|
for (val type : MaterialType.values()) {
|
||||||
|
materials.put(type, new ArrayList<>());
|
||||||
|
}
|
||||||
|
for (val item : kv.getValue()) {
|
||||||
|
materials.get(item.getMaterialType()).add(item);
|
||||||
|
}
|
||||||
|
for (val mkv : materials.entrySet()
|
||||||
|
.stream()
|
||||||
|
.filter(e -> e.getValue().size() > 0)
|
||||||
|
.sorted(Comparator.comparing(e -> e.getValue()
|
||||||
|
.stream()
|
||||||
|
.mapToInt(ItemData::getId)
|
||||||
|
.average()
|
||||||
|
.orElse(0)))
|
||||||
|
.toList()) {
|
||||||
|
h.newSection(mkv.getKey().name());
|
||||||
|
if (mkv.getKey() == MaterialType.MATERIAL_BGM) {
|
||||||
|
for (val item : mkv.getValue()) {
|
||||||
|
val bgmName = Optional.ofNullable(item.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())
|
||||||
|
.map(hash -> getTextMapKey(hash));
|
||||||
|
if (bgmName.isPresent()) {
|
||||||
|
h.newTranslatedLine("%d:{0} - {1}".formatted(item.getId()), getTextMapKey(item.getNameTextMapHash()), bgmName.get());
|
||||||
|
return;
|
||||||
|
} // Fall-through
|
||||||
|
h.newTranslatedLine("%d:{0}".formatted(item.getId()), getTextMapKey(item.getNameTextMapHash()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (val item : mkv.getValue()) {
|
||||||
|
h.newTranslatedLine("%d:{0}".formatted(item.getId()), getTextMapKey(item.getNameTextMapHash()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (val item : kv.getValue()) {
|
||||||
|
h.newTranslatedLine("%d:{0}".formatted(item.getId()), getTextMapKey(item.getNameTextMapHash()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// itemDataMap.forEach((id, data) -> {
|
||||||
|
// val name = getTextMapKey(data.getNameTextMapHash());
|
||||||
|
// switch (data.getMaterialType()) {
|
||||||
|
// case MATERIAL_BGM:
|
||||||
|
// val bgmName = 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())
|
||||||
|
// .map(hash -> getTextMapKey(hash));
|
||||||
|
// if (bgmName.isPresent()) {
|
||||||
|
// h.newTranslatedLine("%d:".formatted(id) + "{0} - {1}", name, bgmName.get());
|
||||||
|
// return;
|
||||||
|
// } // Fall-through
|
||||||
|
// default:
|
||||||
|
// h.newTranslatedLine("%d:".formatted(id) + "{0}", name);
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
// Monsters
|
||||||
|
h.newSection("Monsters");
|
||||||
|
val monsterTypes = MonsterType.values();
|
||||||
|
for (val type : monsterTypes) {
|
||||||
|
h.newSection(type.name());
|
||||||
|
for (val kv : monsterDataMap.int2ObjectEntrySet()) {
|
||||||
|
if (kv.getValue().getType() == type)
|
||||||
|
h.newTranslatedLine(
|
||||||
|
"%d:".formatted(kv.getIntKey()) + kv.getValue().getMonsterName() + " - {0}",
|
||||||
|
kv.getValue().getNameTextMapHash());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scenes - no translations
|
||||||
|
h.newSection("Scenes");
|
||||||
|
sceneDataMap.forEach((id, data) -> h.newLine("%d:".formatted(id) + data.getScriptData()));
|
||||||
|
// Quests
|
||||||
|
h.newSection("Quests");
|
||||||
|
questDataMap.forEach((id, data) -> h.newTranslatedLine(
|
||||||
|
"%d:".formatted(id) + "{0} - {1}",
|
||||||
|
mainQuestTitles.get(data.getMainId()),
|
||||||
|
data.getDescTextMapHash()));
|
||||||
|
|
||||||
|
// Write txt files
|
||||||
|
for (int i = 0; i < TextStrings.NUM_LANGUAGES; i++) {
|
||||||
|
File GMHandbookOutputpath=new File("./GM Handbook");
|
||||||
|
GMHandbookOutputpath.mkdir();
|
||||||
|
final String fileName = "./GM Handbook/GM Handbook - %s.txt".formatted(TextStrings.ARR_LANGUAGES[i]);
|
||||||
|
try (PrintWriter writer = new PrintWriter(new OutputStreamWriter(new FileOutputStream(fileName), StandardCharsets.UTF_8), false)) {
|
||||||
|
writer.write(handbookBuilders.get(i).toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Grasscutter.getLogger().info("GM Handbooks generated!");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> createGachaMappingJsons() {
|
||||||
|
final int NUM_LANGUAGES = Language.TextStrings.NUM_LANGUAGES;
|
||||||
|
final Language.TextStrings CHARACTER = Language.getTextMapKey(4233146695L); // "Character" in EN
|
||||||
|
final Language.TextStrings WEAPON = Language.getTextMapKey(4231343903L); // "Weapon" in EN
|
||||||
|
final Language.TextStrings STANDARD_WISH = Language.getTextMapKey(332935371L); // "Standard Wish" in EN
|
||||||
|
final Language.TextStrings CHARACTER_EVENT_WISH = Language.getTextMapKey(2272170627L); // "Character Event Wish" in EN
|
||||||
|
final Language.TextStrings CHARACTER_EVENT_WISH_2 = Language.getTextMapKey(3352513147L); // "Character Event Wish-2" in EN
|
||||||
|
final Language.TextStrings WEAPON_EVENT_WISH = Language.getTextMapKey(2864268523L); // "Weapon Event Wish" in EN
|
||||||
|
final List<StringBuilder> sbs = new ArrayList<>(NUM_LANGUAGES);
|
||||||
|
for (int langIdx = 0; langIdx < NUM_LANGUAGES; langIdx++)
|
||||||
|
sbs.add(new StringBuilder("{\n")); // Web requests should never need Windows line endings
|
||||||
|
|
||||||
|
// Avatars
|
||||||
|
GameData.getAvatarDataMap().keySet().intStream().sorted().forEach(id -> {
|
||||||
|
AvatarData data = GameData.getAvatarDataMap().get(id);
|
||||||
|
int avatarID = data.getId();
|
||||||
|
if (avatarID >= 11000000) { // skip test avatar
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String color = switch (data.getQualityType()) {
|
||||||
|
case "QUALITY_PURPLE" -> "purple";
|
||||||
|
case "QUALITY_ORANGE" -> "yellow";
|
||||||
|
case "QUALITY_BLUE" -> "blue";
|
||||||
|
default -> "";
|
||||||
|
};
|
||||||
|
Language.TextStrings avatarName = Language.getTextMapKey(data.getNameTextMapHash());
|
||||||
|
for (int langIdx = 0; langIdx < NUM_LANGUAGES; langIdx++) {
|
||||||
|
sbs.get(langIdx)
|
||||||
|
.append("\t\"")
|
||||||
|
.append(avatarID % 1000 + 1000)
|
||||||
|
.append("\": [\"")
|
||||||
|
.append(avatarName.get(langIdx))
|
||||||
|
.append(" (")
|
||||||
|
.append(CHARACTER.get(langIdx))
|
||||||
|
.append(")\", \"")
|
||||||
|
.append(color)
|
||||||
|
.append("\"],\n");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Weapons
|
||||||
|
GameData.getItemDataMap().keySet().intStream().sorted().forEach(id -> {
|
||||||
|
ItemData data = GameData.getItemDataMap().get(id);
|
||||||
|
if (data.getId() <= 11101 || data.getId() >= 20000) {
|
||||||
|
return; //skip non weapon items
|
||||||
|
}
|
||||||
|
String color = switch (data.getRankLevel()) {
|
||||||
|
case 3 -> "blue";
|
||||||
|
case 4 -> "purple";
|
||||||
|
case 5 -> "yellow";
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
if (color == null) return; // skip unnecessary entries
|
||||||
|
Language.TextStrings weaponName = Language.getTextMapKey(data.getNameTextMapHash());
|
||||||
|
for (int langIdx = 0; langIdx < NUM_LANGUAGES; langIdx++) {
|
||||||
|
sbs.get(langIdx)
|
||||||
|
.append("\t\"")
|
||||||
|
.append(data.getId())
|
||||||
|
.append("\": [\"")
|
||||||
|
.append(weaponName.get(langIdx).replaceAll("\"", "\\\\\""))
|
||||||
|
.append(" (")
|
||||||
|
.append(WEAPON.get(langIdx))
|
||||||
|
.append(")\", \"")
|
||||||
|
.append(color)
|
||||||
|
.append("\"],\n");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
for (int langIdx = 0; langIdx < NUM_LANGUAGES; langIdx++) {
|
||||||
|
sbs.get(langIdx)
|
||||||
|
.append("\t\"200\": \"")
|
||||||
|
.append(STANDARD_WISH.get(langIdx))
|
||||||
|
.append("\",\n\t\"301\": \"")
|
||||||
|
.append(CHARACTER_EVENT_WISH.get(langIdx))
|
||||||
|
.append("\",\n\t\"400\": \"")
|
||||||
|
.append(CHARACTER_EVENT_WISH_2.get(langIdx))
|
||||||
|
.append("\",\n\t\"302\": \"")
|
||||||
|
.append(WEAPON_EVENT_WISH.get(langIdx))
|
||||||
|
.append("\"\n}");
|
||||||
|
}
|
||||||
|
return sbs.stream().map(StringBuilder::toString).toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void createGachaMappings(Path location) throws IOException {
|
||||||
|
ResourceLoader.loadResources();
|
||||||
|
List<String> jsons = createGachaMappingJsons();
|
||||||
|
var usedLocales = new HashSet<String>();
|
||||||
|
StringBuilder sb = new StringBuilder("mappings = {\n");
|
||||||
|
for (int i = 0; i < Language.TextStrings.NUM_LANGUAGES; i++) {
|
||||||
|
String locale = Language.TextStrings.ARR_GC_LANGUAGES[i].toLowerCase(); // TODO: change the templates to not use lowercased locale codes
|
||||||
|
if (usedLocales.add(locale)) { // Some locales fallback to en-us, we don't want to redefine en-us with vietnamese strings
|
||||||
|
sb.append("\t\"%s\": ".formatted(locale));
|
||||||
|
sb.append(jsons.get(i).replace("\n", "\n\t") + ",\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.setLength(sb.length() - 2); // Delete trailing ",\n"
|
||||||
|
sb.append("\n}");
|
||||||
|
|
||||||
|
Files.createDirectories(location.getParent());
|
||||||
|
Files.writeString(location, sb);
|
||||||
|
Grasscutter.getLogger().info("Mappings generated to " + location);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> getAvailableLanguage() {
|
||||||
|
List<String> availableLangList = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
Files.newDirectoryStream(getResourcePath("TextMap"), "TextMap*.json").forEach(path -> {
|
||||||
|
availableLangList.add(path.getFileName().toString().replace("TextMap", "").replace(".json", "").toLowerCase());
|
||||||
|
});
|
||||||
|
} catch (IOException e) {
|
||||||
|
Grasscutter.getLogger().error("Failed to get available languages:", e);
|
||||||
|
}
|
||||||
|
return availableLangList;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated(forRemoval = true, since = "1.2.3")
|
||||||
|
public static String getLanguageOption() {
|
||||||
|
List<String> availableLangList = getAvailableLanguage();
|
||||||
|
|
||||||
|
// Use system out for better format
|
||||||
|
if (availableLangList.size() == 1) {
|
||||||
|
return availableLangList.get(0).toUpperCase();
|
||||||
|
}
|
||||||
|
StringBuilder stagedMessage = new StringBuilder();
|
||||||
|
stagedMessage.append("The following languages mappings are available, please select one: [default: EN] \n");
|
||||||
|
|
||||||
|
StringBuilder groupedLangList = new StringBuilder(">\t"); String input;
|
||||||
|
int groupedLangCount = 0;
|
||||||
|
|
||||||
|
for (String availableLanguage: availableLangList) {
|
||||||
|
groupedLangCount++;
|
||||||
|
groupedLangList.append(availableLanguage).append("\t");
|
||||||
|
|
||||||
|
if (groupedLangCount == 6) {
|
||||||
|
stagedMessage.append(groupedLangList).append("\n");
|
||||||
|
groupedLangCount = 0;
|
||||||
|
groupedLangList = new StringBuilder(">\t");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (groupedLangCount > 0) {
|
||||||
|
stagedMessage.append(groupedLangList).append("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
stagedMessage.append("\nYour choice: [EN] ");
|
||||||
|
|
||||||
|
input = Grasscutter.getConsole().readLine(stagedMessage.toString());
|
||||||
|
if (availableLangList.contains(input.toLowerCase())) {
|
||||||
|
return input.toUpperCase();
|
||||||
|
}
|
||||||
|
|
||||||
|
Grasscutter.getLogger().info("Invalid option. Will use EN (English) as fallback."); return "EN";
|
||||||
|
}
|
||||||
|
}
|
@ -57,4 +57,6 @@
|
|||||||
1071:Cyno
|
1071:Cyno
|
||||||
1072:Candace
|
1072:Candace
|
||||||
1073:Nahida
|
1073:Nahida
|
||||||
1074:Layla
|
1074:Layla
|
||||||
|
1075:Wanderer
|
||||||
|
1076:Faruzan
|
File diff suppressed because it is too large
Load Diff
@ -22,17 +22,16 @@
|
|||||||
11415:Cinnabar Spindle
|
11415:Cinnabar Spindle
|
||||||
11416:Kagotsurube Isshin
|
11416:Kagotsurube Isshin
|
||||||
11417:Sapwood Blade
|
11417:Sapwood Blade
|
||||||
11418:Xiphos’ Moonlight
|
11418:Xiphos' Moonlight
|
||||||
11419:Prized Isshin Blade
|
11419:Prized Isshin Blade
|
||||||
11420:Prized Isshin Blade
|
11420:Prized Isshin Blade
|
||||||
11421:Prized Isshin Blade
|
11421:Prized Isshin Blade
|
||||||
|
11422:Toukabou Shigure
|
||||||
11501:Aquila Favonia
|
11501:Aquila Favonia
|
||||||
11502:Skyward Blade
|
11502:Skyward Blade
|
||||||
11503:Freedom-Sworn
|
11503:Freedom-Sworn
|
||||||
11504:Summit Shaper
|
11504:Summit Shaper
|
||||||
11505:Primordial Jade Cutter
|
11505:Primordial Jade Cutter
|
||||||
11506:Primordial Jade Cutter
|
|
||||||
11507:One Side
|
|
||||||
11509:Mistsplitter Reforged
|
11509:Mistsplitter Reforged
|
||||||
11510:Haran Geppaku Futsu
|
11510:Haran Geppaku Futsu
|
||||||
11511:Key of Khaj-Nisut
|
11511:Key of Khaj-Nisut
|
||||||
@ -64,8 +63,6 @@
|
|||||||
12502:Wolf's Gravestone
|
12502:Wolf's Gravestone
|
||||||
12503:Song of Broken Pines
|
12503:Song of Broken Pines
|
||||||
12504:The Unforged
|
12504:The Unforged
|
||||||
12505:Primordial Jade Greatsword
|
|
||||||
12506:The Other Side
|
|
||||||
12510:Redhorn Stonethresher
|
12510:Redhorn Stonethresher
|
||||||
13101:Beginner's Protector
|
13101:Beginner's Protector
|
||||||
13201:Iron Point
|
13201:Iron Point
|
||||||
@ -91,7 +88,6 @@
|
|||||||
13502:Skyward Spine
|
13502:Skyward Spine
|
||||||
13504:Vortex Vanquisher
|
13504:Vortex Vanquisher
|
||||||
13505:Primordial Jade Winged-Spear
|
13505:Primordial Jade Winged-Spear
|
||||||
13506:Deicide
|
|
||||||
13507:Calamity Queller
|
13507:Calamity Queller
|
||||||
13509:Engulfing Lightning
|
13509:Engulfing Lightning
|
||||||
13511:Staff of the Scarlet Sands
|
13511:Staff of the Scarlet Sands
|
||||||
@ -121,12 +117,11 @@
|
|||||||
14417:Fruit of Fulfillment
|
14417:Fruit of Fulfillment
|
||||||
14501:Skyward Atlas
|
14501:Skyward Atlas
|
||||||
14502:Lost Prayer to the Sacred Winds
|
14502:Lost Prayer to the Sacred Winds
|
||||||
14503:Lost Ballade
|
|
||||||
14504:Memory of Dust
|
14504:Memory of Dust
|
||||||
14505:Primordial Jade Regalia
|
|
||||||
14506:Everlasting Moonglow
|
14506:Everlasting Moonglow
|
||||||
14509:Kagura's Verity
|
14509:Kagura's Verity
|
||||||
14511:A Thousand Floating Dreams
|
14511:A Thousand Floating Dreams
|
||||||
|
14512:Tulaytullah's Remembrance
|
||||||
15101:Hunter's Bow
|
15101:Hunter's Bow
|
||||||
15201:Seasoned Hunter's Bow
|
15201:Seasoned Hunter's Bow
|
||||||
15301:Raven Bow
|
15301:Raven Bow
|
||||||
@ -156,9 +151,6 @@
|
|||||||
15501:Skyward Harp
|
15501:Skyward Harp
|
||||||
15502:Amos' Bow
|
15502:Amos' Bow
|
||||||
15503:Elegy for the End
|
15503:Elegy for the End
|
||||||
15504:Kunwu's Wyrmbane
|
|
||||||
15505:Primordial Jade Vista
|
|
||||||
15506:Mirror Breaker
|
|
||||||
15507:Polar Star
|
15507:Polar Star
|
||||||
15508:Aqua Simulacra
|
15508:Aqua Simulacra
|
||||||
15509:Thundering Pulse
|
15509:Thundering Pulse
|
||||||
|
@ -57,4 +57,6 @@
|
|||||||
1071:Сайно
|
1071:Сайно
|
||||||
1072:Кандакия
|
1072:Кандакия
|
||||||
1073:Нахида
|
1073:Нахида
|
||||||
1074:Лайла
|
1074:Лайла
|
||||||
|
1075:Странник
|
||||||
|
1076:Фарузан
|
File diff suppressed because it is too large
Load Diff
@ -11,7 +11,7 @@
|
|||||||
11403:Церемониальный меч
|
11403:Церемониальный меч
|
||||||
11404:Меч аристократов
|
11404:Меч аристократов
|
||||||
11405:Драконий рык
|
11405:Драконий рык
|
||||||
11406:Прототип:Злоба
|
11406:Прототип: Злоба
|
||||||
11407:Стальное жало
|
11407:Стальное жало
|
||||||
11408:Черногорский длинный меч
|
11408:Черногорский длинный меч
|
||||||
11409:Чёрный меч
|
11409:Чёрный меч
|
||||||
@ -26,13 +26,12 @@
|
|||||||
11419:Легендарный клинок Иссин
|
11419:Легендарный клинок Иссин
|
||||||
11420:Легендарный клинок Иссин
|
11420:Легендарный клинок Иссин
|
||||||
11421:Легендарный клинок Иссин
|
11421:Легендарный клинок Иссин
|
||||||
|
11422:Токабо сигурэ
|
||||||
11501:Меч Сокола
|
11501:Меч Сокола
|
||||||
11502:Небесный меч
|
11502:Небесный меч
|
||||||
11503:Клятва свободы
|
11503:Клятва свободы
|
||||||
11504:Кромсатель пиков
|
11504:Кромсатель пиков
|
||||||
11505:Драгоценный омут
|
11505:Драгоценный омут
|
||||||
11506:Древний Нефритовый Резак
|
|
||||||
11507:Одна Сторона
|
|
||||||
11509:Рассекающий туман
|
11509:Рассекающий туман
|
||||||
11510:Харан гэппаку фуцу
|
11510:Харан гэппаку фуцу
|
||||||
11511:Ключ Хадж-нисут
|
11511:Ключ Хадж-нисут
|
||||||
@ -49,7 +48,7 @@
|
|||||||
12403:Церемониальный двуручный меч
|
12403:Церемониальный двуручный меч
|
||||||
12404:Королевский двуручный меч
|
12404:Королевский двуручный меч
|
||||||
12405:Дождерез
|
12405:Дождерез
|
||||||
12406:Прототип:Архаичный
|
12406:Прототип: Архаичный
|
||||||
12407:Белая тень
|
12407:Белая тень
|
||||||
12408:Черногорская бритва
|
12408:Черногорская бритва
|
||||||
12409:Меч драконьей кости
|
12409:Меч драконьей кости
|
||||||
@ -64,8 +63,6 @@
|
|||||||
12502:Волчья погибель
|
12502:Волчья погибель
|
||||||
12503:Песнь разбитых сосен
|
12503:Песнь разбитых сосен
|
||||||
12504:Некованый
|
12504:Некованый
|
||||||
12505:Древний Нефритовый Двуручный Меч
|
|
||||||
12506:Другая Сторона
|
|
||||||
12510:Краснорогий камнеруб
|
12510:Краснорогий камнеруб
|
||||||
13101:Копьё новичка
|
13101:Копьё новичка
|
||||||
13201:Железный наконечник
|
13201:Железный наконечник
|
||||||
@ -74,7 +71,7 @@
|
|||||||
13303:Чёрная кисть
|
13303:Чёрная кисть
|
||||||
13304:Флагшток
|
13304:Флагшток
|
||||||
13401:Гроза драконов
|
13401:Гроза драконов
|
||||||
13402:Прототип:Звёздный блеск
|
13402:Прототип: Звёздный блеск
|
||||||
13403:Пика полумесяца
|
13403:Пика полумесяца
|
||||||
13404:Черногорская пика
|
13404:Черногорская пика
|
||||||
13405:Смертельный бой
|
13405:Смертельный бой
|
||||||
@ -91,7 +88,6 @@
|
|||||||
13502:Небесная ось
|
13502:Небесная ось
|
||||||
13504:Покоритель вихря
|
13504:Покоритель вихря
|
||||||
13505:Нефритовый коршун
|
13505:Нефритовый коршун
|
||||||
13506:Богоубийство
|
|
||||||
13507:Усмиритель бед
|
13507:Усмиритель бед
|
||||||
13509:Сияющая жатва
|
13509:Сияющая жатва
|
||||||
13511:Посох алых песков
|
13511:Посох алых песков
|
||||||
@ -108,7 +104,7 @@
|
|||||||
14403:Церемониальные мемуары
|
14403:Церемониальные мемуары
|
||||||
14404:Королевский гримуар
|
14404:Королевский гримуар
|
||||||
14405:Солнечная жемчужина
|
14405:Солнечная жемчужина
|
||||||
14406:Прототип:Янтарь
|
14406:Прототип: Янтарь
|
||||||
14407:Морской атлас
|
14407:Морской атлас
|
||||||
14408:Черногорский агат
|
14408:Черногорский агат
|
||||||
14409:Око сознания
|
14409:Око сознания
|
||||||
@ -121,12 +117,11 @@
|
|||||||
14417:Плод восполнения
|
14417:Плод восполнения
|
||||||
14501:Небесный атлас
|
14501:Небесный атлас
|
||||||
14502:Молитва святым ветрам
|
14502:Молитва святым ветрам
|
||||||
14503:Утерянная Баллада
|
|
||||||
14504:Память о пыли
|
14504:Память о пыли
|
||||||
14505:Древняя Нефритовая Регалия
|
|
||||||
14506:Вечное лунное сияние
|
14506:Вечное лунное сияние
|
||||||
14509:Истина кагура
|
14509:Истина кагура
|
||||||
14511:Сновидения тысячи ночей
|
14511:Сновидения тысячи ночей
|
||||||
|
14512:Воспоминания Тулайтуллы
|
||||||
15101:Лук охотника
|
15101:Лук охотника
|
||||||
15201:Лук опытного охотника
|
15201:Лук опытного охотника
|
||||||
15301:Лук ворона
|
15301:Лук ворона
|
||||||
@ -140,7 +135,7 @@
|
|||||||
15403:Церемониальный лук
|
15403:Церемониальный лук
|
||||||
15404:Королевский лук
|
15404:Королевский лук
|
||||||
15405:Ржавый лук
|
15405:Ржавый лук
|
||||||
15406:Прототип:Полумесяц
|
15406:Прототип: Полумесяц
|
||||||
15407:Составной лук
|
15407:Составной лук
|
||||||
15408:Черногорский боевой лук
|
15408:Черногорский боевой лук
|
||||||
15409:Зелёный лук
|
15409:Зелёный лук
|
||||||
@ -156,9 +151,6 @@
|
|||||||
15501:Небесное крыло
|
15501:Небесное крыло
|
||||||
15502:Лук Амоса
|
15502:Лук Амоса
|
||||||
15503:Элегия погибели
|
15503:Элегия погибели
|
||||||
15504:Убийца Драконов Кунву
|
|
||||||
15505:Древняя Нефритовая Виста
|
|
||||||
15506:Разбитое зеркало
|
|
||||||
15507:Полярная звезда
|
15507:Полярная звезда
|
||||||
15508:Аква симулякрум
|
15508:Аква симулякрум
|
||||||
15509:Громовой пульс
|
15509:Громовой пульс
|
||||||
|
@ -57,4 +57,6 @@
|
|||||||
1071:赛诺
|
1071:赛诺
|
||||||
1072:坎蒂丝
|
1072:坎蒂丝
|
||||||
1073:纳西妲
|
1073:纳西妲
|
||||||
1074:莱依拉
|
1074:莱依拉
|
||||||
|
1075:流浪者
|
||||||
|
1076:珐露珊
|
File diff suppressed because it is too large
Load Diff
@ -26,13 +26,12 @@
|
|||||||
11419:「一心传」名刀
|
11419:「一心传」名刀
|
||||||
11420:「一心传」名刀
|
11420:「一心传」名刀
|
||||||
11421:「一心传」名刀
|
11421:「一心传」名刀
|
||||||
|
11422:东花坊时雨
|
||||||
11501:风鹰剑
|
11501:风鹰剑
|
||||||
11502:天空之刃
|
11502:天空之刃
|
||||||
11503:苍古自由之誓
|
11503:苍古自由之誓
|
||||||
11504:斫峰之刃
|
11504:斫峰之刃
|
||||||
11505:磐岩结绿
|
11505:磐岩结绿
|
||||||
11506:磐岩结绿
|
|
||||||
11507:凭虚
|
|
||||||
11509:雾切之回光
|
11509:雾切之回光
|
||||||
11510:波乱月白经津
|
11510:波乱月白经津
|
||||||
11511:圣显之钥
|
11511:圣显之钥
|
||||||
@ -64,8 +63,6 @@
|
|||||||
12502:狼的末路
|
12502:狼的末路
|
||||||
12503:松籁响起之时
|
12503:松籁响起之时
|
||||||
12504:无工之剑
|
12504:无工之剑
|
||||||
12505:砥厄鱼
|
|
||||||
12506:异史
|
|
||||||
12510:赤角石溃杵
|
12510:赤角石溃杵
|
||||||
13101:新手长枪
|
13101:新手长枪
|
||||||
13201:铁尖枪
|
13201:铁尖枪
|
||||||
@ -91,7 +88,6 @@
|
|||||||
13502:天空之脊
|
13502:天空之脊
|
||||||
13504:贯虹之槊
|
13504:贯虹之槊
|
||||||
13505:和璞鸢
|
13505:和璞鸢
|
||||||
13506:弑主
|
|
||||||
13507:息灾
|
13507:息灾
|
||||||
13509:薙草之稻光
|
13509:薙草之稻光
|
||||||
13511:赤沙之杖
|
13511:赤沙之杖
|
||||||
@ -121,12 +117,11 @@
|
|||||||
14417:盈满之实
|
14417:盈满之实
|
||||||
14501:天空之卷
|
14501:天空之卷
|
||||||
14502:四风原典
|
14502:四风原典
|
||||||
14503:散佚的叙事诗
|
|
||||||
14504:尘世之锁
|
14504:尘世之锁
|
||||||
14505:定土玉圭
|
|
||||||
14506:不灭月华
|
14506:不灭月华
|
||||||
14509:神乐之真意
|
14509:神乐之真意
|
||||||
14511:千夜浮梦
|
14511:千夜浮梦
|
||||||
|
14512:图莱杜拉的回忆
|
||||||
15101:猎弓
|
15101:猎弓
|
||||||
15201:历练的猎弓
|
15201:历练的猎弓
|
||||||
15301:鸦羽弓
|
15301:鸦羽弓
|
||||||
@ -156,9 +151,6 @@
|
|||||||
15501:天空之翼
|
15501:天空之翼
|
||||||
15502:阿莫斯之弓
|
15502:阿莫斯之弓
|
||||||
15503:终末嗟叹之诗
|
15503:终末嗟叹之诗
|
||||||
15504:陨龙之梦
|
|
||||||
15505:悬黎千钧
|
|
||||||
15506:破镜
|
|
||||||
15507:冬极白星
|
15507:冬极白星
|
||||||
15508:若水
|
15508:若水
|
||||||
15509:飞雷之弦振
|
15509:飞雷之弦振
|
||||||
|
@ -57,4 +57,6 @@
|
|||||||
1071:賽諾
|
1071:賽諾
|
||||||
1072:坎蒂絲
|
1072:坎蒂絲
|
||||||
1073:納西妲
|
1073:納西妲
|
||||||
1074:萊依拉
|
1074:萊依拉
|
||||||
|
1075:流浪者
|
||||||
|
1076:琺露珊
|
File diff suppressed because it is too large
Load Diff
@ -26,13 +26,12 @@
|
|||||||
11419:「一心傳」名刀
|
11419:「一心傳」名刀
|
||||||
11420:「一心傳」名刀
|
11420:「一心傳」名刀
|
||||||
11421:「一心傳」名刀
|
11421:「一心傳」名刀
|
||||||
|
11422:東花坊時雨
|
||||||
11501:風鷹劍
|
11501:風鷹劍
|
||||||
11502:天空之刃
|
11502:天空之刃
|
||||||
11503:蒼古自由之誓
|
11503:蒼古自由之誓
|
||||||
11504:斫峰之刃
|
11504:斫峰之刃
|
||||||
11505:磐岩結綠
|
11505:磐岩結綠
|
||||||
11506:磐岩結綠
|
|
||||||
11507:憑虛
|
|
||||||
11509:霧切之回光
|
11509:霧切之回光
|
||||||
11510:波亂月白經津
|
11510:波亂月白經津
|
||||||
11511:聖顯之鑰
|
11511:聖顯之鑰
|
||||||
@ -64,8 +63,6 @@
|
|||||||
12502:狼的末路
|
12502:狼的末路
|
||||||
12503:松籟響起之時
|
12503:松籟響起之時
|
||||||
12504:無工之劍
|
12504:無工之劍
|
||||||
12505:砥厄魚
|
|
||||||
12506:異史
|
|
||||||
12510:赤角石潰杵
|
12510:赤角石潰杵
|
||||||
13101:新手長槍
|
13101:新手長槍
|
||||||
13201:鐵尖槍
|
13201:鐵尖槍
|
||||||
@ -91,7 +88,6 @@
|
|||||||
13502:天空之脊
|
13502:天空之脊
|
||||||
13504:貫虹之槊
|
13504:貫虹之槊
|
||||||
13505:和璞鳶
|
13505:和璞鳶
|
||||||
13506:弑主
|
|
||||||
13507:息災
|
13507:息災
|
||||||
13509:薙草之稻光
|
13509:薙草之稻光
|
||||||
13511:赤沙之杖
|
13511:赤沙之杖
|
||||||
@ -121,12 +117,11 @@
|
|||||||
14417:盈滿之實
|
14417:盈滿之實
|
||||||
14501:天空之卷
|
14501:天空之卷
|
||||||
14502:四風原典
|
14502:四風原典
|
||||||
14503:散佚的敘事詩
|
|
||||||
14504:塵世之鎖
|
14504:塵世之鎖
|
||||||
14505:定土玉圭
|
|
||||||
14506:不滅月華
|
14506:不滅月華
|
||||||
14509:神樂之真意
|
14509:神樂之真意
|
||||||
14511:千夜浮夢
|
14511:千夜浮夢
|
||||||
|
14512:圖萊杜拉的回憶
|
||||||
15101:獵弓
|
15101:獵弓
|
||||||
15201:歷練的獵弓
|
15201:歷練的獵弓
|
||||||
15301:鴉羽弓
|
15301:鴉羽弓
|
||||||
@ -156,9 +151,6 @@
|
|||||||
15501:天空之翼
|
15501:天空之翼
|
||||||
15502:阿莫斯之弓
|
15502:阿莫斯之弓
|
||||||
15503:終末嗟嘆之詩
|
15503:終末嗟嘆之詩
|
||||||
15504:昆吾墜龍
|
|
||||||
15505:懸黎千鈞
|
|
||||||
15506:破鏡
|
|
||||||
15507:冬極白星
|
15507:冬極白星
|
||||||
15508:若水
|
15508:若水
|
||||||
15509:飛雷之弦振
|
15509:飛雷之弦振
|
||||||
|
Loading…
Reference in New Issue
Block a user