mirror of
https://github.com/Grasscutters/Grasscutter.git
synced 2025-07-04 14:03:33 +00:00
Compare commits
19 Commits
Author | SHA1 | Date | |
---|---|---|---|
8b83dbf58c | |||
d6fef3252f | |||
e73984bd41 | |||
553e22ead8 | |||
5878cb6f8d | |||
100d08ec5d | |||
83b84408a1 | |||
da3981089d | |||
ad502a8568 | |||
f6c84fdfbf | |||
1c4d263dd2 | |||
35962542af | |||
0b5329514b | |||
46b0c7cf93 | |||
1e932ce144 | |||
b1a9ed0226 | |||
676ed32a12 | |||
05fe62b49a | |||
b781e560e4 |
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div>
|
<div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div>
|
||||||
|
|
||||||
[EN](README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md)
|
[EN](README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md)
|
||||||
|
|
||||||
**Attention:** We always welcome contributors to the project. Before adding your contribution, please carefully read our [Code of Conduct](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md).
|
**Attention:** We always welcome contributors to the project. Before adding your contribution, please carefully read our [Code of Conduct](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md).
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div>
|
<div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div>
|
||||||
|
|
||||||
[EN](README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md)
|
[EN](README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md)
|
||||||
|
|
||||||
**תשומת לב בבקשה:** אנחנו מקבלים עזרה בפיתוח התוכנה. לפני שאתם תורמים לפרויקט בבקשה תקראו את [תנאי השימוש](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md).
|
**תשומת לב בבקשה:** אנחנו מקבלים עזרה בפיתוח התוכנה. לפני שאתם תורמים לפרויקט בבקשה תקראו את [תנאי השימוש](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md).
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div>
|
<div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div>
|
||||||
|
|
||||||
[EN](README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md)
|
[EN](README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md)
|
||||||
|
|
||||||
**Aantekening:** We verwelkomen altijd bijdragers aan het project. Lees onze [Gedragscode](https://github.com/Grasscutters/Grasscutter/blob/development/README_NL.md#bijdragen-aan-het-project) zorgvuldig door voordat u uw bijdrage toevoegt.
|
**Aantekening:** We verwelkomen altijd bijdragers aan het project. Lees onze [Gedragscode](https://github.com/Grasscutters/Grasscutter/blob/development/README_NL.md#bijdragen-aan-het-project) zorgvuldig door voordat u uw bijdrage toevoegt.
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div>
|
<div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div>
|
||||||
|
|
||||||
[EN](README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md)
|
[EN](README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md)
|
||||||
|
|
||||||
**Atención:** Siempre damos la bienvenida a contribuidores del proyecto. Antes de añadir tu contribución, por favor lee cuidadosamente nuestro [Código de conducta](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md).
|
**Atención:** Siempre damos la bienvenida a contribuidores del proyecto. Antes de añadir tu contribución, por favor lee cuidadosamente nuestro [Código de conducta](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md).
|
||||||
|
|
||||||
@ -67,7 +67,7 @@
|
|||||||
|
|
||||||
2. Establece el proxy de red a `127.0.0.1:8080` o el puerto de proxy que pusiste.
|
2. Establece el proxy de red a `127.0.0.1:8080` o el puerto de proxy que pusiste.
|
||||||
|
|
||||||
**también puedes usar `start.cmd` para iniciar el servidor y el servicio de proxy automáticamente, pero tienes que configurar el entorno JAVA_HOME**
|
**También puedes usar `start.cmd` para iniciar el servidor y el servicio de proxy automáticamente, pero tienes que configurar el entorno JAVA_HOME**
|
||||||
|
|
||||||
### Construcción
|
### Construcción
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div>
|
<div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div>
|
||||||
|
|
||||||
[EN](README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md)
|
[EN](README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md)
|
||||||
|
|
||||||
**Atensyon:** Ang mga kontributor ay laging welcome sa proyektong ito. Bago mag-bigay ng kontribusyon, basahin muna ng mabuti ang [Code of Conduct](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md).
|
**Atensyon:** Ang mga kontributor ay laging welcome sa proyektong ito. Bago mag-bigay ng kontribusyon, basahin muna ng mabuti ang [Code of Conduct](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md).
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div>
|
<div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div>
|
||||||
|
|
||||||
[EN](README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md)
|
[EN](README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md)
|
||||||
|
|
||||||
**Attention:** De nouveaux contributeurs sont toujours les bienvenus. Avant d'ajouter votre contribution, veuillez lire le [code de conduite](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md).
|
**Attention:** De nouveaux contributeurs sont toujours les bienvenus. Avant d'ajouter votre contribution, veuillez lire le [code de conduite](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md).
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div>
|
<div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div>
|
||||||
|
|
||||||
[EN](README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md)
|
[EN](README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md)
|
||||||
|
|
||||||
**Perhatian:** Kami selalu menyambut kontributor untuk proyek ini. Sebelum menambahkan kontribusi Anda, harap baca [Kode Etik](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md) kami.
|
**Perhatian:** Kami selalu menyambut kontributor untuk proyek ini. Sebelum menambahkan kontribusi Anda, harap baca [Kode Etik](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md) kami.
|
||||||
|
|
||||||
@ -14,7 +14,7 @@
|
|||||||
* Daftar teman
|
* Daftar teman
|
||||||
* Teleportasi
|
* Teleportasi
|
||||||
* Sistem gacha
|
* Sistem gacha
|
||||||
* Co-op *sebahagian* berfungsi
|
* Co-op *sebagian* berfungsi
|
||||||
* Memunculkan monster melalui konsol
|
* Memunculkan monster melalui konsol
|
||||||
* Fitur inventaris (menerima item/karakter, meng-upgrade item/karakter, dll)
|
* Fitur inventaris (menerima item/karakter, meng-upgrade item/karakter, dll)
|
||||||
|
|
||||||
@ -53,9 +53,9 @@
|
|||||||
|
|
||||||
**Catatan:** Sertifikat CA biasanya disimpan di `%USERPROFILE%\ .mitmproxy`, atau anda dapat download dari `http://mitm.it`
|
**Catatan:** Sertifikat CA biasanya disimpan di `%USERPROFILE%\ .mitmproxy`, atau anda dapat download dari `http://mitm.it`
|
||||||
|
|
||||||
klik dua kali untuk [menginstall](https://docs.microsoft.com/en-us/skype-sdk/sdn/articles/installing-the-trusted-root-certificate#installing-a-trusted-root-certificate) or ...
|
klik dua kali untuk [menginstall](https://docs.microsoft.com/en-us/skype-sdk/sdn/articles/installing-the-trusted-root-certificate#installing-a-trusted-root-certificate) ataupun juga
|
||||||
|
|
||||||
- Via command line
|
- melalui command line
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
certutil -addstore root %USERPROFILE%\.mitmproxy\mitmproxy-ca-cert.cer
|
certutil -addstore root %USERPROFILE%\.mitmproxy\mitmproxy-ca-cert.cer
|
||||||
@ -103,6 +103,6 @@ Anda bisa menemukan output jar di root folder proyek.
|
|||||||
# Quick Troubleshooting
|
# Quick Troubleshooting
|
||||||
|
|
||||||
* Jika kompilasi tidak berhasil, periksa instalasi JDK Anda (JDK 17 dan validasi variabel bin PATH JDK)
|
* Jika kompilasi tidak berhasil, periksa instalasi JDK Anda (JDK 17 dan validasi variabel bin PATH JDK)
|
||||||
* Klien saya tidak terhubung, tidak login, 4206, dll... - Sebagian besar pengaturan daemon proxy Anda adalah *masalahnya*, jika menggunakan
|
* Klien saya tidak terhubung, tidak login, 4206, dan lain-lain - Sebagian besar pengaturan daemon proxy Anda adalah *masalahnya*, jika menggunakan
|
||||||
Fiddler pastikan berjalan pada port lain kecuali 8888
|
Fiddler pastikan berjalan pada port lain kecuali 8888
|
||||||
* Urutan startup: MongoDB > Grasscutter > Proxy daemon (mitmdump, fiddler, etc.) > Game
|
* Urutan startup: MongoDB > Grasscutter > Proxy daemon (mitmdump, fiddler, etc.) > Game
|
||||||
|
108
README_it-IT.md
Normal file
108
README_it-IT.md
Normal file
@ -0,0 +1,108 @@
|
|||||||
|

|
||||||
|
<div align="center"><img alt="Documentation" src="https://img.shields.io/badge/Wiki-Grasscutter-blue?style=for-the-badge&link=https://github.com/Grasscutters/Grasscutter/wiki&link=https://github.com/Grasscutters/Grasscutter/wiki"> <img alt="GitHub release (latest by date)" src="https://img.shields.io/github/v/release/Grasscutters/Grasscutter?logo=java&style=for-the-badge"> <img alt="GitHub" src="https://img.shields.io/github/license/Grasscutters/Grasscutter?style=for-the-badge"> <img alt="GitHub last commit" src="https://img.shields.io/github/last-commit/Grasscutters/Grasscutter?style=for-the-badge"> <img alt="GitHub Workflow Status" src="https://img.shields.io/github/workflow/status/Grasscutters/Grasscutter/Build?logo=github&style=for-the-badge"></div>
|
||||||
|
|
||||||
|
<div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div>
|
||||||
|
|
||||||
|
[EN](README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md)
|
||||||
|
|
||||||
|
**Attenzione:** Diamo sempre il benvenuto ai contributori del progetto. Prima di aggiungere il tuo contributo, leggi attentamente il nostro [Codice di condotta](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md).
|
||||||
|
|
||||||
|
## Funzionalità attuali
|
||||||
|
|
||||||
|
* Login
|
||||||
|
* Combattimento
|
||||||
|
* Lista di amici
|
||||||
|
* Teletrasporto
|
||||||
|
* Sistema Gacha
|
||||||
|
* Cooperativa *parzialmente* funzionale
|
||||||
|
* Evoca mostri dalla console
|
||||||
|
* Funzionalità dell'inventario (ricevi oggetti/personaggi, aggiorna oggetti/personaggi, ecc.)
|
||||||
|
|
||||||
|
## Guida rapida all'installazione
|
||||||
|
|
||||||
|
**Nota:** Per il supporto, unisciti al nostro [Discord](https://discord.gg/T5vZU6UyeG).
|
||||||
|
|
||||||
|
### Requisiti
|
||||||
|
|
||||||
|
* Java SE - 17 ([link](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html))
|
||||||
|
|
||||||
|
**Nota:** se vuoi solo **eseguirlo**, **jre** è sufficiente.
|
||||||
|
|
||||||
|
* [MongoDB](https://www.mongodb.com/try/download/community) (consigliato 4.0+)
|
||||||
|
|
||||||
|
* Servizio proxy: mitmproxy (mitmdump, consigliato), Fiddler Classic, ecc.
|
||||||
|
|
||||||
|
### Esecuzione
|
||||||
|
|
||||||
|
**Nota:** Se hai eseguito l'aggiornamento da una versione precedente, rimuovi `config.json` in modo che venga generato di nuovo.
|
||||||
|
|
||||||
|
1. Ottieni "grasscutter.jar".
|
||||||
|
- Scarica da [azioni](https://github.com/Grasscutters/Grasscutter/suites/6895963598/artifacts/267483297)
|
||||||
|
- [Compilalo tu stesso](#Compilazione)
|
||||||
|
2. Crea una cartella `resources` nella directory in cui si trova grasscutter.jar e sposta lì le cartelle `BinOutput` ed `ExcelBinOutput` *(Vedi il [wiki](https://github.com/Grasscutters/Grasscutter/wiki ) per maggiori dettagli su come ottenerli.)*
|
||||||
|
3. Eseguire Grasscutter con `java -jar grasscutter.jar`. **Assicurati che il servizio mongodb sia attivo.**
|
||||||
|
|
||||||
|
### Connessione client
|
||||||
|
|
||||||
|
½. Crea un account usando [il comando corrispondente nella console del server](https://github.com/Grasscutters/Grasscutter/wiki/Commands#targeting).
|
||||||
|
|
||||||
|
1. Reindirizza il traffico: (scegli uno)
|
||||||
|
- mitmdump: `mitmdump -s proxy.py -k`
|
||||||
|
|
||||||
|
Autorizza il certificato CA:
|
||||||
|
|
||||||
|
**Nota:**Il certificato CA si trova solitamente in `%USERPROFILE%\ .mitmproxy`, oppure puoi scaricarlo da `http://mitm.it`
|
||||||
|
|
||||||
|
Fare doppio clic su [installa](https://docs.microsoft.com/en-us/skype-sdk/sdn/articles/installing-the-trusted-root-certificate#installing-a-trusted-root-certificate) o ...
|
||||||
|
|
||||||
|
- Con riga di comando
|
||||||
|
|
||||||
|
```shell
|
||||||
|
certutil -addstore root %USERPROFILE%\.mitmproxy\mitmproxy-ca-cert.cer
|
||||||
|
```
|
||||||
|
|
||||||
|
- Fiddler Classic: esegui Fiddler Classic, abilita `Decrypt https traffic` nelle opzioni e cambia la porta predefinita in (Strumenti -> Opzioni -> Connessioni) in qualcosa di diverso da `8888`, e carica [questo script](https :/ /github.lunatic.moe/fiddlerscript).
|
||||||
|
|
||||||
|
- [File host](https://github.com/Grasscutters/Grasscutter/wiki/Running#traffic-route-map)
|
||||||
|
|
||||||
|
2. Impostare il proxy di rete su `127.0.0.1:8080` o la porta proxy impostata.
|
||||||
|
|
||||||
|
**Puoi anche usare `start.cmd` per avviare automaticamente il server e il servizio proxy, ma devi impostare l'ambiente JAVA_HOME**
|
||||||
|
|
||||||
|
### Compilazione
|
||||||
|
|
||||||
|
Grasscutter usa Gradle per gestire le dipendenze e le build.
|
||||||
|
|
||||||
|
**Requisiti:**
|
||||||
|
|
||||||
|
- [Kit di sviluppo Java SE - 17](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html)
|
||||||
|
- [Git](https://git-scm.com/downloads)
|
||||||
|
|
||||||
|
##### Windows
|
||||||
|
|
||||||
|
```shell
|
||||||
|
git clone https://github.com/Grasscutters/Grasscutter.git
|
||||||
|
cd grasscutter
|
||||||
|
.\gradlew.bat # Impostazioni dell'ambiente
|
||||||
|
.\gradlew jar # Compila
|
||||||
|
```
|
||||||
|
|
||||||
|
##### Linux
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/Grasscutters/Grasscutter.git
|
||||||
|
cd grasscutter
|
||||||
|
chmod +x gradlew
|
||||||
|
./gradlew jar # Compila
|
||||||
|
```
|
||||||
|
|
||||||
|
Puoi trovare il jar generato nella cartella principale del progetto.
|
||||||
|
|
||||||
|
### I comandi sono stati spostati nel [wiki](https://github.com/Grasscutters/Grasscutter/wiki/Commands)!
|
||||||
|
|
||||||
|
# Soluzioni agli errori comuni
|
||||||
|
|
||||||
|
* Se la compilazione non riesce, controlla l'installazione di JDK (JDK 17 e convalida la variabile JDK bin PATH)
|
||||||
|
* Il mio client non si connette, non accede, 4206, ecc... - Probabilmente le tue impostazioni proxy sono *il problema*, se usi
|
||||||
|
Fiddler assicurati di utilizzare una porta diversa da 8888
|
||||||
|
* Sequenza di avvio: MongoDB > Grasscutter > Servizio proxy (mitmdump, fiddler, ecc.) > Gioco
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div>
|
<div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div>
|
||||||
|
|
||||||
[EN](README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md)
|
[EN](README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md)
|
||||||
|
|
||||||
|
|
||||||
***:** 私たちはプロジェクトへの貢献者をいつでも歓迎します。貢献を追加する前に、我々の [行動規範](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md)をよくお読みください。
|
***:** 私たちはプロジェクトへの貢献者をいつでも歓迎します。貢献を追加する前に、我々の [行動規範](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md)をよくお読みください。
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div>
|
<div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div>
|
||||||
|
|
||||||
[EN](README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md)
|
[EN](README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md)
|
||||||
|
|
||||||
**주의 :** 우리는 항상 프로젝트에 기여하는 사람들을 환영합니다. 기여를 하기 전, [행동 지침](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md)을 주의 깊게 읽어주세요.
|
**주의 :** 우리는 항상 프로젝트에 기여하는 사람들을 환영합니다. 기여를 하기 전, [행동 지침](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md)을 주의 깊게 읽어주세요.
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div>
|
<div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div>
|
||||||
|
|
||||||
[EN](README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md)
|
[EN](README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md)
|
||||||
|
|
||||||
**Uwaga:** Zawsze jesteśmy otwarci na wasz wkład w projekt. Przed zaproponowaniem zmian przeczytaj [zasady postępowania (ENG)](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md).
|
**Uwaga:** Zawsze jesteśmy otwarci na wasz wkład w projekt. Przed zaproponowaniem zmian przeczytaj [zasady postępowania (ENG)](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md).
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div>
|
<div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div>
|
||||||
|
|
||||||
[EN](README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md)
|
[EN](README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md)
|
||||||
|
|
||||||
**Внимание:** Мы всегда рады новому вкладу в проект. Однако, перед тем, как сделать свой вклад, пожалуйста, прочтите наш [кодекс делового поведения](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md).
|
**Внимание:** Мы всегда рады новому вкладу в проект. Однако, перед тем, как сделать свой вклад, пожалуйста, прочтите наш [кодекс делового поведения](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md).
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div>
|
<div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div>
|
||||||
|
|
||||||
[EN](README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md)
|
[EN](README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md)
|
||||||
|
|
||||||
**请注意:** 欢迎成为本项目的贡献者。但在提交 PR 之前, 请仔细阅读 [代码规范](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md)。
|
**请注意:** 欢迎成为本项目的贡献者。但在提交 PR 之前, 请仔细阅读 [代码规范](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md)。
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
|
|
||||||
<div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div>
|
<div align="center"><a href="https://discord.gg/T5vZU6UyeG"><img alt="Discord - Grasscutter" src="https://img.shields.io/discord/965284035985305680?label=Discord&logo=discord&style=for-the-badge"></a></div>
|
||||||
|
|
||||||
[EN](README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md)
|
[EN](README.md) | [简中](README_zh-CN.md) | [繁中](README_zh-TW.md) | [FR](README_fr-FR.md) | [ES](README_es-ES.md) | [HE](README_HE.md) | [RU](README_ru-RU.md) | [PL](README_pl-PL.md) | [ID](README_id-ID.md) | [KR](README_ko-KR.md) | [FIL/PH](README_fil-PH.md) | [NL](README_NL.md) | [JP](README_ja-JP.md) | [IT](README_it-IT.md)
|
||||||
|
|
||||||
**請注意:** 歡迎成為本專案的貢獻者。在提交 PR 之前, 請仔細閱讀[程式碼規範](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md)。
|
**請注意:** 歡迎成為本專案的貢獻者。在提交 PR 之前, 請仔細閱讀[程式碼規範](https://github.com/Grasscutters/Grasscutter/blob/stable/CONTRIBUTING.md)。
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ sourceCompatibility = JavaVersion.VERSION_17
|
|||||||
targetCompatibility = JavaVersion.VERSION_17
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
|
|
||||||
group = 'xyz.grasscutters'
|
group = 'xyz.grasscutters'
|
||||||
version = '1.4.3'
|
version = '1.4.4-dev'
|
||||||
|
|
||||||
sourceCompatibility = 17
|
sourceCompatibility = 17
|
||||||
targetCompatibility = 17
|
targetCompatibility = 17
|
||||||
|
@ -5,6 +5,8 @@ import emu.grasscutter.server.http.handlers.GachaHandler;
|
|||||||
import emu.grasscutter.tools.Tools;
|
import emu.grasscutter.tools.Tools;
|
||||||
import emu.grasscutter.utils.FileUtils;
|
import emu.grasscutter.utils.FileUtils;
|
||||||
import emu.grasscutter.utils.JsonUtils;
|
import emu.grasscutter.utils.JsonUtils;
|
||||||
|
import emu.grasscutter.utils.TsvUtils;
|
||||||
|
import lombok.val;
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -88,6 +90,17 @@ public class DataLoader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T> List<T> loadTableToList(String resourcePath, Class<T> classType) throws IOException {
|
||||||
|
val path = FileUtils.getDataPathTsjJsonTsv(resourcePath);
|
||||||
|
Grasscutter.getLogger().info("Loading data table from: "+path);
|
||||||
|
return switch (FileUtils.getFileExtension(path)) {
|
||||||
|
case "json" -> JsonUtils.loadToList(path, classType);
|
||||||
|
case "tsj" -> TsvUtils.loadTsjToListSetField(path, classType);
|
||||||
|
case "tsv" -> TsvUtils.loadTsvToListSetField(path, classType);
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
public static void checkAllFiles() {
|
public static void checkAllFiles() {
|
||||||
try {
|
try {
|
||||||
List<Path> filenames = FileUtils.getPathsFromResource("/defaults/data/");
|
List<Path> filenames = FileUtils.getPathsFromResource("/defaults/data/");
|
||||||
|
@ -11,7 +11,10 @@ import emu.grasscutter.game.world.SpawnDataEntry;
|
|||||||
import emu.grasscutter.game.world.SpawnDataEntry.GridBlockId;
|
import emu.grasscutter.game.world.SpawnDataEntry.GridBlockId;
|
||||||
import emu.grasscutter.game.world.SpawnDataEntry.SpawnGroupEntry;
|
import emu.grasscutter.game.world.SpawnDataEntry.SpawnGroupEntry;
|
||||||
import emu.grasscutter.scripts.SceneIndexManager;
|
import emu.grasscutter.scripts.SceneIndexManager;
|
||||||
|
import emu.grasscutter.utils.FileUtils;
|
||||||
import emu.grasscutter.utils.JsonUtils;
|
import emu.grasscutter.utils.JsonUtils;
|
||||||
|
import emu.grasscutter.utils.TsvUtils;
|
||||||
|
import it.unimi.dsi.fastutil.Pair;
|
||||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
import it.unimi.dsi.fastutil.ints.IntArraySet;
|
import it.unimi.dsi.fastutil.ints.IntArraySet;
|
||||||
@ -23,6 +26,8 @@ import java.io.*;
|
|||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
import java.util.concurrent.CopyOnWriteArraySet;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@ -32,8 +37,9 @@ import static emu.grasscutter.utils.Language.translate;
|
|||||||
|
|
||||||
public class ResourceLoader {
|
public class ResourceLoader {
|
||||||
|
|
||||||
private static final List<String> loadedResources = new ArrayList<>();
|
private static final Set<String> loadedResources = new CopyOnWriteArraySet<>();
|
||||||
|
|
||||||
|
// Get a list of all resource classes, sorted by loadPriority
|
||||||
public static List<Class<?>> getResourceDefClasses() {
|
public static List<Class<?>> getResourceDefClasses() {
|
||||||
Reflections reflections = new Reflections(ResourceLoader.class.getPackage().getName());
|
Reflections reflections = new Reflections(ResourceLoader.class.getPackage().getName());
|
||||||
Set<?> classes = reflections.getSubTypesOf(GameResource.class);
|
Set<?> classes = reflections.getSubTypesOf(GameResource.class);
|
||||||
@ -51,6 +57,25 @@ public class ResourceLoader {
|
|||||||
return classList;
|
return classList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get a list containing sets of all resource classes, sorted by loadPriority
|
||||||
|
protected static List<Set<Class<?>>> getResourceDefClassesPrioritySets() {
|
||||||
|
val reflections = new Reflections(ResourceLoader.class.getPackage().getName());
|
||||||
|
val classes = reflections.getSubTypesOf(GameResource.class);
|
||||||
|
val priorities = ResourceType.LoadPriority.getInOrder();
|
||||||
|
Grasscutter.getLogger().debug("Priorities are "+priorities);
|
||||||
|
val map = new LinkedHashMap<ResourceType.LoadPriority, Set<Class<?>>>(priorities.size());
|
||||||
|
priorities.forEach(p -> map.put(p, new HashSet<>()));
|
||||||
|
|
||||||
|
classes.forEach(c -> {
|
||||||
|
// val c = (Class<?>) o;
|
||||||
|
val annotation = c.getAnnotation(ResourceType.class);
|
||||||
|
if (annotation != null) {
|
||||||
|
map.get(annotation.loadPriority()).add(c);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return List.copyOf(map.values());
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean loadedAll = false;
|
private static boolean loadedAll = false;
|
||||||
public static void loadAll() {
|
public static void loadAll() {
|
||||||
if (loadedAll) return;
|
if (loadedAll) return;
|
||||||
@ -86,48 +111,66 @@ public class ResourceLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void loadResources(boolean doReload) {
|
public static void loadResources(boolean doReload) {
|
||||||
for (Class<?> resourceDefinition : getResourceDefClasses()) {
|
long startTime = System.nanoTime();
|
||||||
ResourceType type = resourceDefinition.getAnnotation(ResourceType.class);
|
val errors = new ConcurrentLinkedQueue<Pair<String, Exception>>(); // Logger in a parallel stream will deadlock
|
||||||
|
|
||||||
if (type == null) {
|
getResourceDefClassesPrioritySets().forEach(classes -> {
|
||||||
continue;
|
classes.stream()
|
||||||
}
|
.parallel().unordered()
|
||||||
|
.forEach(c -> {
|
||||||
|
val type = c.getAnnotation(ResourceType.class);
|
||||||
|
if (type == null) return;
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
val map = GameData.getMapByResourceDef(c);
|
||||||
Int2ObjectMap map = GameData.getMapByResourceDef(resourceDefinition);
|
if (map == null) return;
|
||||||
|
|
||||||
if (map == null) {
|
try {
|
||||||
continue;
|
loadFromResource(c, type, map, doReload);
|
||||||
}
|
} catch (Exception e) {
|
||||||
|
errors.add(Pair.of(Arrays.toString(type.name()), e));
|
||||||
try {
|
}
|
||||||
loadFromResource(resourceDefinition, type, map, doReload);
|
});
|
||||||
} catch (Exception e) {
|
});
|
||||||
Grasscutter.getLogger().error("Error loading resource file: " + Arrays.toString(type.name()), e.getLocalizedMessage());
|
errors.forEach(pair -> Grasscutter.getLogger().error("Error loading resource file: " + pair.left(), pair.right()));
|
||||||
}
|
long endTime = System.nanoTime();
|
||||||
}
|
long ns = (endTime - startTime); //divide by 1000000 to get milliseconds.
|
||||||
|
Grasscutter.getLogger().debug("Loading resources took "+ns+"ns == "+ns/1000000+"ms");
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
protected static void loadFromResource(Class<?> c, ResourceType type, Int2ObjectMap map, boolean doReload) throws Exception {
|
protected static void loadFromResource(Class<?> c, ResourceType type, Int2ObjectMap map, boolean doReload) throws Exception {
|
||||||
if (!loadedResources.contains(c.getSimpleName()) || doReload) {
|
val simpleName = c.getSimpleName();
|
||||||
|
if (doReload || !loadedResources.contains(simpleName)) {
|
||||||
for (String name : type.name()) {
|
for (String name : type.name()) {
|
||||||
loadFromResource(c, name, map);
|
loadFromResource(c, FileUtils.getExcelPath(name), map);
|
||||||
}
|
}
|
||||||
loadedResources.add(c.getSimpleName());
|
loadedResources.add(simpleName);
|
||||||
Grasscutter.getLogger().debug("Loaded " + map.size() + " " + c.getSimpleName() + "s.");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
protected static <T> void loadFromResource(Class<T> c, String fileName, Int2ObjectMap map) throws Exception {
|
protected static <T> void loadFromResource(Class<T> c, Path filename, Int2ObjectMap map) throws Exception {
|
||||||
List<T> list = JsonUtils.loadToList(getResourcePath("ExcelBinOutput/" + fileName), c);
|
val results = switch (FileUtils.getFileExtension(filename)) {
|
||||||
|
case "json" -> JsonUtils.loadToList(filename, c);
|
||||||
for (T o : list) {
|
case "tsj" -> TsvUtils.loadTsjToListSetField(filename, c);
|
||||||
|
case "tsv" -> TsvUtils.loadTsvToListSetField(filename, c);
|
||||||
|
default -> null;
|
||||||
|
};
|
||||||
|
if (results == null) return;
|
||||||
|
results.forEach(o -> {
|
||||||
GameResource res = (GameResource) o;
|
GameResource res = (GameResource) o;
|
||||||
res.onLoad();
|
res.onLoad();
|
||||||
map.put(res.getId(), res);
|
map.put(res.getId(), res);
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||||
|
protected static <T> void loadFromResource(Class<T> c, String fileName, Int2ObjectMap map) throws Exception {
|
||||||
|
JsonUtils.loadToList(getResourcePath("ExcelBinOutput/" + fileName), c).forEach(o -> {
|
||||||
|
GameResource res = (GameResource) o;
|
||||||
|
res.onLoad();
|
||||||
|
map.put(res.getId(), res);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ScenePointConfig { // Sadly this doesn't work as a local class in loadScenePoints()
|
public class ScenePointConfig { // Sadly this doesn't work as a local class in loadScenePoints()
|
||||||
|
@ -2,6 +2,8 @@ package emu.grasscutter.data;
|
|||||||
|
|
||||||
import java.lang.annotation.Retention;
|
import java.lang.annotation.Retention;
|
||||||
import java.lang.annotation.RetentionPolicy;
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@Retention(RetentionPolicy.RUNTIME)
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
public @interface ResourceType {
|
public @interface ResourceType {
|
||||||
@ -28,5 +30,9 @@ public @interface ResourceType {
|
|||||||
public int value() {
|
public int value() {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<LoadPriority> getInOrder() {
|
||||||
|
return Stream.of(LoadPriority.values()).sorted((x, y) -> y.value() - x.value()).toList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,7 +21,7 @@ public class ActivityWatcherData extends GameResource {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoad() {
|
public void onLoad() {
|
||||||
triggerConfig.paramList = triggerConfig.paramList.stream().filter(x -> !x.isBlank()).toList();
|
triggerConfig.paramList = triggerConfig.paramList.stream().filter(x -> (x != null) && !x.isBlank()).toList();
|
||||||
triggerConfig.watcherTriggerType = WatcherTriggerType.getTypeByName(triggerConfig.triggerType);
|
triggerConfig.watcherTriggerType = WatcherTriggerType.getTypeByName(triggerConfig.triggerType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,8 +40,11 @@ public class BattlePassMissionData extends GameResource {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoad() {
|
public void onLoad() {
|
||||||
if (this.getTriggerConfig() != null && getTriggerConfig().getParamList()[0].length() > 0) {
|
if (this.getTriggerConfig() != null) {
|
||||||
this.mainParams = Arrays.stream(getTriggerConfig().getParamList()[0].split("[:;,]")).map(Integer::parseInt).collect(Collectors.toSet());
|
var params = getTriggerConfig().getParamList()[0];
|
||||||
|
if ((params != null) && !params.isEmpty()) {
|
||||||
|
this.mainParams = Arrays.stream(params.split("[:;,]")).map(Integer::parseInt).collect(Collectors.toSet());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,35 +11,50 @@ import emu.grasscutter.utils.Utils;
|
|||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
|
||||||
public class GachaBanner {
|
public class GachaBanner {
|
||||||
@Getter private int gachaType;
|
@Getter private int gachaType = -1;
|
||||||
@Getter private int scheduleId;
|
@Getter int scheduleId = -1;
|
||||||
|
@Getter int sortId = -1;
|
||||||
@Getter private String prefabPath;
|
@Getter private String prefabPath;
|
||||||
private String previewPrefabPath;
|
@Getter private String previewPrefabPath;
|
||||||
@Getter private String titlePath;
|
@Getter private String titlePath;
|
||||||
private int costItemId = 0;
|
private int costItemId = 0;
|
||||||
private int costItemAmount = 1;
|
private int costItemAmount = 1;
|
||||||
private int costItemId10 = 0;
|
private int costItemId10 = 0;
|
||||||
private int costItemAmount10 = 10;
|
private int costItemAmount10 = 10;
|
||||||
@Getter private int beginTime;
|
@Getter private int beginTime = 0;
|
||||||
@Getter private int endTime;
|
@Getter private int endTime = 1924992000;
|
||||||
@Getter private int sortId;
|
|
||||||
@Getter private int gachaTimesLimit = Integer.MAX_VALUE;
|
@Getter private int gachaTimesLimit = Integer.MAX_VALUE;
|
||||||
private int[] rateUpItems4 = {};
|
@Getter private int[] rateUpItems4 = {};
|
||||||
private int[] rateUpItems5 = {};
|
@Getter private int[] rateUpItems5 = {};
|
||||||
@Getter private int[] fallbackItems3 = {11301, 11302, 11306, 12301, 12302, 12305, 13303, 14301, 14302, 14304, 15301, 15302, 15304};
|
// This now handles default values for the fields below
|
||||||
@Getter private int[] fallbackItems4Pool1 = {1014, 1020, 1023, 1024, 1025, 1027, 1031, 1032, 1034, 1036, 1039, 1043, 1044, 1045, 1048, 1053, 1055, 1056, 1064};
|
|
||||||
@Getter private int[] fallbackItems4Pool2 = {11401, 11402, 11403, 11405, 12401, 12402, 12403, 12405, 13401, 13407, 14401, 14402, 14403, 14409, 15401, 15402, 15403, 15405};
|
|
||||||
@Getter private int[] fallbackItems5Pool1 = {1003, 1016, 1042, 1035, 1041};
|
|
||||||
@Getter private int[] fallbackItems5Pool2 = {11501, 11502, 12501, 12502, 13502, 13505, 14501, 14502, 15501, 15502};
|
|
||||||
@Getter private boolean removeC6FromPool = false;
|
|
||||||
@Getter private boolean autoStripRateUpFromFallback = true;
|
|
||||||
private int[][] weights4 = {{1,510}, {8,510}, {10,10000}};
|
|
||||||
private int[][] weights5 = {{1,75}, {73,150}, {90,10000}};
|
|
||||||
private int[][] poolBalanceWeights4 = {{1,255}, {17,255}, {21,10455}};
|
|
||||||
private int[][] poolBalanceWeights5 = {{1,30}, {147,150}, {181,10230}};
|
|
||||||
private int eventChance4 = 50; // Chance to win a featured event item
|
|
||||||
private int eventChance5 = 50; // Chance to win a featured event item
|
|
||||||
@Getter private BannerType bannerType = BannerType.STANDARD;
|
@Getter private BannerType bannerType = BannerType.STANDARD;
|
||||||
|
// Constants used by the BannerType enum
|
||||||
|
static final int[][] DEFAULT_WEIGHTS_4 = {{1,510}, {8,510}, {10,10000}};
|
||||||
|
static final int[][] DEFAULT_WEIGHTS_4_WEAPON = {{1, 600}, {7, 600}, {8, 6600}, {10, 12600}};
|
||||||
|
static final int[][] DEFAULT_WEIGHTS_5 = {{1,75}, {73,150}, {90,10000}};
|
||||||
|
static final int[][] DEFAULT_WEIGHTS_5_CHARACTER = {{1,80}, {73,80}, {90,10000}};
|
||||||
|
static final int[][] DEFAULT_WEIGHTS_5_WEAPON = {{1,100}, {62,100}, {73,7800}, {80,10000}};
|
||||||
|
static final int[] DEFAULT_FALLBACK_ITEMS_4_POOL_1 = {1014, 1020, 1023, 1024, 1025, 1027, 1031, 1032, 1034, 1036, 1039, 1043, 1044, 1045, 1048, 1053, 1055, 1056, 1059, 1064, 1065, 1067, 1068, 1072}; // Default avatars
|
||||||
|
static final int[] DEFAULT_FALLBACK_ITEMS_4_POOL_2 = {11401, 11402, 11403, 11405, 12401, 12402, 12403, 12405, 13401, 13407, 14401, 14402, 14403, 14409, 15401, 15402, 15403, 15405}; // Default weapons
|
||||||
|
static final int[] DEFAULT_FALLBACK_ITEMS_5_POOL_1 = {1003, 1016, 1042, 1035, 1041, 1069}; // Default avatars
|
||||||
|
static final int[] DEFAULT_FALLBACK_ITEMS_5_POOL_2 = {11501, 11502, 12501, 12502, 13502, 13505, 14501, 14502, 15501, 15502}; // Default weapons
|
||||||
|
static final int[] EMPTY_POOL = {}; // Used to remove a type of fallback
|
||||||
|
// These don't change between banner types (apart from Standard having three extra 4star avatars)
|
||||||
|
@Getter private int[] fallbackItems3 = {11301, 11302, 11306, 12301, 12302, 12305, 13303, 14301, 14302, 14304, 15301, 15302, 15304};
|
||||||
|
@Getter private int[] fallbackItems4Pool1 = DEFAULT_FALLBACK_ITEMS_4_POOL_1;
|
||||||
|
@Getter private int[] fallbackItems4Pool2 = DEFAULT_FALLBACK_ITEMS_4_POOL_2;
|
||||||
|
// Different banner types have different defaults, see above for default values and the enum for which are used where.
|
||||||
|
@Getter private int[] fallbackItems5Pool1;
|
||||||
|
@Getter private int[] fallbackItems5Pool2;
|
||||||
|
private int[][] weights4;
|
||||||
|
private int[][] weights5;
|
||||||
|
private int eventChance4 = -1; // Chance to win a featured event item
|
||||||
|
private int eventChance5 = -1; // Chance to win a featured event item
|
||||||
|
//
|
||||||
|
@Getter private boolean removeC6FromPool = false;
|
||||||
|
@Getter private boolean autoStripRateUpFromFallback = true; // Ensures that featured items won't "double dip" into the losing pool
|
||||||
|
private int[][] poolBalanceWeights4 = {{1,255}, {17,255}, {21,10455}}; // Used to ensure that players won't go too many rolls without getting something from pool 1 (avatar) or pool 2 (weapon)
|
||||||
|
private int[][] poolBalanceWeights5 = {{1,30}, {147,150}, {181,10230}};
|
||||||
@Getter private int wishMaxProgress = 2;
|
@Getter private int wishMaxProgress = 2;
|
||||||
|
|
||||||
// Deprecated fields that were tolerated in early May 2022 but have apparently still being circulating in new custom configs
|
// Deprecated fields that were tolerated in early May 2022 but have apparently still being circulating in new custom configs
|
||||||
@ -52,11 +67,15 @@ public class GachaBanner {
|
|||||||
@Deprecated private int hardPity = -1;
|
@Deprecated private int hardPity = -1;
|
||||||
@Deprecated private int minItemType = -1;
|
@Deprecated private int minItemType = -1;
|
||||||
@Deprecated private int maxItemType = -1;
|
@Deprecated private int maxItemType = -1;
|
||||||
|
@Getter private boolean deprecated = false;
|
||||||
|
@Getter private boolean disabled = false;
|
||||||
|
|
||||||
private static void warnDeprecated(String name, String replacement) {
|
private void warnDeprecated(String name, String replacement) {
|
||||||
Grasscutter.getLogger().error("Deprecated field found in Banners.json: "+name+" was replaced back in early May 2022, use "+replacement+" instead. If you do not remove this key from your config, it will refuse to load in a future Grasscutter version.");
|
Grasscutter.getLogger().error("Deprecated field found in Banners config: "+name+" was replaced back in early May 2022, use "+replacement+" instead. You MUST remove this field from your config.");
|
||||||
|
this.deprecated = true;
|
||||||
}
|
}
|
||||||
public void onLoad() {
|
public void onLoad() {
|
||||||
|
// Handle deprecated configs
|
||||||
if (eventChance != -1)
|
if (eventChance != -1)
|
||||||
warnDeprecated("eventChance", "eventChance4 & eventChance5");
|
warnDeprecated("eventChance", "eventChance4 & eventChance5");
|
||||||
if (costItem != 0)
|
if (costItem != 0)
|
||||||
@ -73,30 +92,42 @@ public class GachaBanner {
|
|||||||
warnDeprecated("rateUpItems1", "rateUpItems5");
|
warnDeprecated("rateUpItems1", "rateUpItems5");
|
||||||
if (rateUpItems2.length > 0)
|
if (rateUpItems2.length > 0)
|
||||||
warnDeprecated("rateUpItems2", "rateUpItems4");
|
warnDeprecated("rateUpItems2", "rateUpItems4");
|
||||||
}
|
|
||||||
|
|
||||||
public String getPreviewPrefabPath() {
|
// Handle default values
|
||||||
if (this.previewPrefabPath != null && !this.previewPrefabPath.isEmpty())
|
if (this.previewPrefabPath != null && this.previewPrefabPath.equals("UI_Tab_" + this.prefabPath))
|
||||||
return this.previewPrefabPath;
|
Grasscutter.getLogger().error("Redundant field found in Banners config: previewPrefabPath does not need to be specified if it is identical to prefabPath prefixed with \"UI_Tab_\".");
|
||||||
return "UI_Tab_" + this.prefabPath;
|
if (this.previewPrefabPath == null || this.previewPrefabPath.isEmpty())
|
||||||
|
this.previewPrefabPath = "UI_Tab_" + this.prefabPath;
|
||||||
|
if (this.gachaType < 0)
|
||||||
|
this.gachaType = this.bannerType.gachaType;
|
||||||
|
if (this.costItemId == 0)
|
||||||
|
this.costItemId = this.bannerType.costItemId;
|
||||||
|
if (this.costItemId10 == 0)
|
||||||
|
this.costItemId10 = this.costItemId;
|
||||||
|
if (this.weights4 == null)
|
||||||
|
this.weights4 = this.bannerType.weights4;
|
||||||
|
if (this.weights5 == null)
|
||||||
|
this.weights5 = this.bannerType.weights5;
|
||||||
|
if (this.eventChance4 < 0)
|
||||||
|
this.eventChance4 = this.bannerType.eventChance4;
|
||||||
|
if (this.eventChance5 < 0)
|
||||||
|
this.eventChance5 = this.bannerType.eventChance5;
|
||||||
|
if (this.fallbackItems5Pool1 == null)
|
||||||
|
this.fallbackItems5Pool1 = this.bannerType.fallbackItems5Pool1;
|
||||||
|
if (this.fallbackItems5Pool2 == null)
|
||||||
|
this.fallbackItems5Pool2 = this.bannerType.fallbackItems5Pool2;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ItemParamData getCost(int numRolls) {
|
public ItemParamData getCost(int numRolls) {
|
||||||
return switch (numRolls) {
|
return switch (numRolls) {
|
||||||
case 10 -> new ItemParamData((costItemId10 > 0) ? costItemId10 : getCostItem(), costItemAmount10);
|
case 10 -> new ItemParamData(costItemId10, costItemAmount10);
|
||||||
default -> new ItemParamData(getCostItem(), costItemAmount * numRolls);
|
default -> new ItemParamData(costItemId, costItemAmount * numRolls);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
public int getCostItem() {
|
public int getCostItem() {
|
||||||
return (costItem > 0) ? costItem : costItemId;
|
return costItemId;
|
||||||
}
|
|
||||||
|
|
||||||
public int[] getRateUpItems4() {
|
|
||||||
return (rateUpItems2.length > 0) ? rateUpItems2 : rateUpItems4;
|
|
||||||
}
|
|
||||||
public int[] getRateUpItems5() {
|
|
||||||
return (rateUpItems1.length > 0) ? rateUpItems1 : rateUpItems5;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasEpitomized() {
|
public boolean hasEpitomized() {
|
||||||
@ -120,7 +151,7 @@ public class GachaBanner {
|
|||||||
public int getEventChance(int rarity) {
|
public int getEventChance(int rarity) {
|
||||||
return switch (rarity) {
|
return switch (rarity) {
|
||||||
case 4 -> eventChance4;
|
case 4 -> eventChance4;
|
||||||
default -> (eventChance > -1) ? eventChance : eventChance5;
|
default -> eventChance5;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -138,8 +169,6 @@ public class GachaBanner {
|
|||||||
+ "/gacha/details?s=" + sessionKey + "&scheduleId=" + scheduleId;
|
+ "/gacha/details?s=" + sessionKey + "&scheduleId=" + scheduleId;
|
||||||
|
|
||||||
// Grasscutter.getLogger().info("record = " + record);
|
// Grasscutter.getLogger().info("record = " + record);
|
||||||
ItemParamData costItem1 = this.getCost(1);
|
|
||||||
ItemParamData costItem10 = this.getCost(10);
|
|
||||||
PlayerGachaBannerInfo gachaInfo = player.getGachaInfo().getBannerInfo(this);
|
PlayerGachaBannerInfo gachaInfo = player.getGachaInfo().getBannerInfo(this);
|
||||||
int leftGachaTimes = switch (gachaTimesLimit) {
|
int leftGachaTimes = switch (gachaTimesLimit) {
|
||||||
case Integer.MAX_VALUE -> Integer.MAX_VALUE;
|
case Integer.MAX_VALUE -> Integer.MAX_VALUE;
|
||||||
@ -150,10 +179,10 @@ public class GachaBanner {
|
|||||||
.setScheduleId(this.getScheduleId())
|
.setScheduleId(this.getScheduleId())
|
||||||
.setBeginTime(this.getBeginTime())
|
.setBeginTime(this.getBeginTime())
|
||||||
.setEndTime(this.getEndTime())
|
.setEndTime(this.getEndTime())
|
||||||
.setCostItemId(costItem1.getId())
|
.setCostItemId(this.costItemId)
|
||||||
.setCostItemNum(costItem1.getCount())
|
.setCostItemNum(this.costItemAmount)
|
||||||
.setTenCostItemId(costItem10.getId())
|
.setTenCostItemId(this.costItemId10)
|
||||||
.setTenCostItemNum(costItem10.getCount())
|
.setTenCostItemNum(this.costItemAmount10)
|
||||||
.setGachaPrefabPath(this.getPrefabPath())
|
.setGachaPrefabPath(this.getPrefabPath())
|
||||||
.setGachaPreviewPrefabPath(this.getPreviewPrefabPath())
|
.setGachaPreviewPrefabPath(this.getPreviewPrefabPath())
|
||||||
.setGachaProbUrl(details)
|
.setGachaProbUrl(details)
|
||||||
@ -202,6 +231,31 @@ public class GachaBanner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public enum BannerType {
|
public enum BannerType {
|
||||||
STANDARD, EVENT, WEAPON;
|
STANDARD(200, 224, DEFAULT_WEIGHTS_4, DEFAULT_WEIGHTS_5, 50, 50, DEFAULT_FALLBACK_ITEMS_5_POOL_1, DEFAULT_FALLBACK_ITEMS_5_POOL_2),
|
||||||
|
BEGINNER(100, 224, DEFAULT_WEIGHTS_4, DEFAULT_WEIGHTS_5, 50, 50, DEFAULT_FALLBACK_ITEMS_5_POOL_1, DEFAULT_FALLBACK_ITEMS_5_POOL_2),
|
||||||
|
EVENT(301, 223, DEFAULT_WEIGHTS_4, DEFAULT_WEIGHTS_5_CHARACTER, 50, 50, DEFAULT_FALLBACK_ITEMS_5_POOL_1, DEFAULT_FALLBACK_ITEMS_5_POOL_2), // Legacy value for CHARACTER
|
||||||
|
CHARACTER(301, 223, DEFAULT_WEIGHTS_4, DEFAULT_WEIGHTS_5_CHARACTER, 50, 50, DEFAULT_FALLBACK_ITEMS_5_POOL_1, EMPTY_POOL),
|
||||||
|
CHARACTER2(400, 223, DEFAULT_WEIGHTS_4, DEFAULT_WEIGHTS_5_CHARACTER, 50, 50, DEFAULT_FALLBACK_ITEMS_5_POOL_1, EMPTY_POOL),
|
||||||
|
WEAPON(302, 223, DEFAULT_WEIGHTS_4_WEAPON, DEFAULT_WEIGHTS_5_WEAPON, 75, 75, EMPTY_POOL, DEFAULT_FALLBACK_ITEMS_5_POOL_2);
|
||||||
|
|
||||||
|
public final int gachaType;
|
||||||
|
public final int costItemId;
|
||||||
|
public final int[][] weights4;
|
||||||
|
public final int[][] weights5;
|
||||||
|
public final int eventChance4;
|
||||||
|
public final int eventChance5;
|
||||||
|
public final int[] fallbackItems5Pool1;
|
||||||
|
public final int[] fallbackItems5Pool2;
|
||||||
|
|
||||||
|
BannerType(int gachaType, int costItemId, int[][] weights4, int[][] weights5, int eventChance4, int eventChance5, int[] fallbackItems5Pool1, int[] fallbackItems5Pool2) {
|
||||||
|
this.gachaType = gachaType;
|
||||||
|
this.costItemId = costItemId;
|
||||||
|
this.weights4 = weights4;
|
||||||
|
this.weights5 = weights5;
|
||||||
|
this.eventChance4 = eventChance4;
|
||||||
|
this.eventChance5 = eventChance5;
|
||||||
|
this.fallbackItems5Pool1 = fallbackItems5Pool1;
|
||||||
|
this.fallbackItems5Pool2 = fallbackItems5Pool2;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,8 +45,6 @@ public class GachaSystem extends BaseGameSystem {
|
|||||||
|
|
||||||
private static final int starglitterId = 221;
|
private static final int starglitterId = 221;
|
||||||
private static final int stardustId = 222;
|
private static final int stardustId = 222;
|
||||||
private int[] fallbackItems4Pool2Default = {11401, 11402, 11403, 11405, 12401, 12402, 12403, 12405, 13401, 13407, 14401, 14402, 14403, 14409, 15401, 15402, 15403, 15405};
|
|
||||||
private int[] fallbackItems5Pool2Default = {11501, 11502, 12501, 12502, 13502, 13505, 14501, 14502, 15501, 15502};
|
|
||||||
|
|
||||||
public GachaSystem(GameServer server) {
|
public GachaSystem(GameServer server) {
|
||||||
super(server);
|
super(server);
|
||||||
@ -69,12 +67,24 @@ public class GachaSystem extends BaseGameSystem {
|
|||||||
|
|
||||||
public synchronized void load() {
|
public synchronized void load() {
|
||||||
getGachaBanners().clear();
|
getGachaBanners().clear();
|
||||||
|
int autoScheduleId = 1000;
|
||||||
|
int autoSortId = 9000;
|
||||||
try {
|
try {
|
||||||
List<GachaBanner> banners = DataLoader.loadList("Banners.json", GachaBanner.class);
|
List<GachaBanner> banners = DataLoader.loadTableToList("Banners", GachaBanner.class);
|
||||||
if (banners.size() > 0) {
|
if (banners.size() > 0) {
|
||||||
for (GachaBanner banner : banners) {
|
for (GachaBanner banner : banners) {
|
||||||
banner.onLoad();
|
banner.onLoad();
|
||||||
getGachaBanners().put(banner.getScheduleId(), banner);
|
if (banner.isDeprecated()) {
|
||||||
|
Grasscutter.getLogger().error("A Banner has not been loaded because it contains one or more deprecated fields. Remove the fields mentioned above and reload.");
|
||||||
|
} else if (banner.isDisabled()) {
|
||||||
|
Grasscutter.getLogger().debug("A Banner has not been loaded because it is disabled.");
|
||||||
|
} else {
|
||||||
|
if (banner.scheduleId < 0)
|
||||||
|
banner.scheduleId = autoScheduleId++;
|
||||||
|
if (banner.sortId < 0)
|
||||||
|
banner.sortId = autoSortId--;
|
||||||
|
getGachaBanners().put(banner.scheduleId, banner);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Grasscutter.getLogger().debug("Banners successfully loaded.");
|
Grasscutter.getLogger().debug("Banners successfully loaded.");
|
||||||
} else {
|
} else {
|
||||||
@ -156,7 +166,7 @@ public class GachaSystem extends BaseGameSystem {
|
|||||||
private synchronized int doFallbackRarePull(int[] fallback1, int[] fallback2, int rarity, GachaBanner banner, PlayerGachaBannerInfo gachaInfo) {
|
private synchronized int doFallbackRarePull(int[] fallback1, int[] fallback2, int rarity, GachaBanner banner, PlayerGachaBannerInfo gachaInfo) {
|
||||||
if (fallback1.length < 1) {
|
if (fallback1.length < 1) {
|
||||||
if (fallback2.length < 1) {
|
if (fallback2.length < 1) {
|
||||||
return getRandom((rarity==5)? fallbackItems5Pool2Default : fallbackItems4Pool2Default);
|
return getRandom((rarity==5)? GachaBanner.DEFAULT_FALLBACK_ITEMS_5_POOL_2 : GachaBanner.DEFAULT_FALLBACK_ITEMS_4_POOL_2);
|
||||||
} else {
|
} else {
|
||||||
return getRandom(fallback2);
|
return getRandom(fallback2);
|
||||||
}
|
}
|
||||||
|
@ -4,37 +4,43 @@ import dev.morphia.annotations.Entity;
|
|||||||
|
|
||||||
@Entity
|
@Entity
|
||||||
public class PlayerGachaInfo {
|
public class PlayerGachaInfo {
|
||||||
private PlayerGachaBannerInfo standardBanner;
|
private PlayerGachaBannerInfo standardBanner;
|
||||||
private PlayerGachaBannerInfo eventCharacterBanner;
|
private PlayerGachaBannerInfo beginnerBanner;
|
||||||
private PlayerGachaBannerInfo eventWeaponBanner;
|
private PlayerGachaBannerInfo eventCharacterBanner;
|
||||||
|
private PlayerGachaBannerInfo eventWeaponBanner;
|
||||||
public PlayerGachaInfo() {
|
|
||||||
this.standardBanner = new PlayerGachaBannerInfo();
|
|
||||||
this.eventCharacterBanner = new PlayerGachaBannerInfo();
|
|
||||||
this.eventWeaponBanner = new PlayerGachaBannerInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
public PlayerGachaBannerInfo getStandardBanner() {
|
|
||||||
return standardBanner;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PlayerGachaBannerInfo getEventCharacterBanner() {
|
public PlayerGachaInfo() {
|
||||||
return eventCharacterBanner;
|
this.standardBanner = new PlayerGachaBannerInfo();
|
||||||
}
|
this.eventCharacterBanner = new PlayerGachaBannerInfo();
|
||||||
|
this.eventWeaponBanner = new PlayerGachaBannerInfo();
|
||||||
public PlayerGachaBannerInfo getEventWeaponBanner() {
|
}
|
||||||
return eventWeaponBanner;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PlayerGachaBannerInfo getBannerInfo(GachaBanner banner) {
|
public PlayerGachaBannerInfo getStandardBanner() {
|
||||||
switch (banner.getBannerType()) {
|
if (this.standardBanner == null) this.standardBanner = new PlayerGachaBannerInfo();
|
||||||
case EVENT:
|
return this.standardBanner;
|
||||||
return this.eventCharacterBanner;
|
}
|
||||||
case WEAPON:
|
|
||||||
return this.eventWeaponBanner;
|
public PlayerGachaBannerInfo getBeginnerBanner() {
|
||||||
case STANDARD:
|
if (this.beginnerBanner == null) this.beginnerBanner = new PlayerGachaBannerInfo();
|
||||||
default:
|
return this.beginnerBanner;
|
||||||
return this.standardBanner;
|
}
|
||||||
}
|
|
||||||
}
|
public PlayerGachaBannerInfo getEventCharacterBanner() {
|
||||||
|
if (this.eventCharacterBanner == null) this.eventCharacterBanner = new PlayerGachaBannerInfo();
|
||||||
|
return this.eventCharacterBanner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlayerGachaBannerInfo getEventWeaponBanner() {
|
||||||
|
if (this.eventWeaponBanner == null) this.eventWeaponBanner = new PlayerGachaBannerInfo();
|
||||||
|
return this.eventWeaponBanner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlayerGachaBannerInfo getBannerInfo(GachaBanner banner) {
|
||||||
|
return switch (banner.getBannerType()) {
|
||||||
|
case STANDARD -> this.getStandardBanner();
|
||||||
|
case BEGINNER -> this.getBeginnerBanner();
|
||||||
|
case EVENT, CHARACTER, CHARACTER2 -> this.getEventCharacterBanner();
|
||||||
|
case WEAPON -> this.getEventWeaponBanner();
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,11 @@ public class DeforestationManager extends BasePlayerManager {
|
|||||||
ColliderTypeToWoodItemID.put(10,101310);
|
ColliderTypeToWoodItemID.put(10,101310);
|
||||||
ColliderTypeToWoodItemID.put(11,101311);
|
ColliderTypeToWoodItemID.put(11,101311);
|
||||||
ColliderTypeToWoodItemID.put(12,101312);
|
ColliderTypeToWoodItemID.put(12,101312);
|
||||||
|
ColliderTypeToWoodItemID.put(13,101313);
|
||||||
|
ColliderTypeToWoodItemID.put(14,101314);
|
||||||
|
ColliderTypeToWoodItemID.put(15,101315);
|
||||||
|
ColliderTypeToWoodItemID.put(16,101316);
|
||||||
|
ColliderTypeToWoodItemID.put(17,101317);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DeforestationManager(Player player) {
|
public DeforestationManager(Player player) {
|
||||||
|
@ -1,19 +1,18 @@
|
|||||||
package emu.grasscutter.game.props.ItemUseAction;
|
package emu.grasscutter.game.props.ItemUseAction;
|
||||||
|
|
||||||
import emu.grasscutter.game.props.ItemUseOp;
|
import emu.grasscutter.game.props.ItemUseOp;
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
public class ItemUseAddExp extends ItemUseAction {
|
|
||||||
@Getter private int exp = 0;
|
|
||||||
|
|
||||||
|
public class ItemUseAddExp extends ItemUseInt {
|
||||||
@Override
|
@Override
|
||||||
public ItemUseOp getItemUseOp() {
|
public ItemUseOp getItemUseOp() {
|
||||||
return ItemUseOp.ITEM_USE_ADD_EXP;
|
return ItemUseOp.ITEM_USE_ADD_EXP;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ItemUseAddExp(String[] useParam) {
|
public ItemUseAddExp(String[] useParam) {
|
||||||
try {
|
super(useParam);
|
||||||
this.exp = Integer.parseInt(useParam[0]);
|
}
|
||||||
} catch (NumberFormatException ignored) {}
|
|
||||||
|
public int getExp() {
|
||||||
|
return this.i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ public class ItemUseAddItem extends ItemUseInt {
|
|||||||
super(useParam);
|
super(useParam);
|
||||||
try {
|
try {
|
||||||
this.count = Integer.parseInt(useParam[1]);
|
this.count = Integer.parseInt(useParam[1]);
|
||||||
} catch (NumberFormatException ignored) {}
|
} catch (NumberFormatException | ArrayIndexOutOfBoundsException ignored) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,19 +1,14 @@
|
|||||||
package emu.grasscutter.game.props.ItemUseAction;
|
package emu.grasscutter.game.props.ItemUseAction;
|
||||||
|
|
||||||
import emu.grasscutter.game.props.ItemUseOp;
|
import emu.grasscutter.game.props.ItemUseOp;
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
public class ItemUseAddReliquaryExp extends ItemUseAction {
|
|
||||||
@Getter private int exp = 0;
|
|
||||||
|
|
||||||
|
public class ItemUseAddReliquaryExp extends ItemUseAddExp {
|
||||||
@Override
|
@Override
|
||||||
public ItemUseOp getItemUseOp() {
|
public ItemUseOp getItemUseOp() {
|
||||||
return ItemUseOp.ITEM_USE_ADD_RELIQUARY_EXP;
|
return ItemUseOp.ITEM_USE_ADD_RELIQUARY_EXP;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ItemUseAddReliquaryExp(String[] useParam) {
|
public ItemUseAddReliquaryExp(String[] useParam) {
|
||||||
try {
|
super(useParam);
|
||||||
this.exp = Integer.parseInt(useParam[0]);
|
|
||||||
} catch (NumberFormatException ignored) {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ public class ItemUseAddServerBuff extends ItemUseInt {
|
|||||||
super(useParam);
|
super(useParam);
|
||||||
try {
|
try {
|
||||||
this.duration = Integer.parseInt(useParam[1]);
|
this.duration = Integer.parseInt(useParam[1]);
|
||||||
} catch (NumberFormatException ignored) {}
|
} catch (NumberFormatException | ArrayIndexOutOfBoundsException ignored) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,19 +1,14 @@
|
|||||||
package emu.grasscutter.game.props.ItemUseAction;
|
package emu.grasscutter.game.props.ItemUseAction;
|
||||||
|
|
||||||
import emu.grasscutter.game.props.ItemUseOp;
|
import emu.grasscutter.game.props.ItemUseOp;
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
public class ItemUseAddWeaponExp extends ItemUseAction {
|
|
||||||
@Getter private int exp = 0;
|
|
||||||
|
|
||||||
|
public class ItemUseAddWeaponExp extends ItemUseAddExp {
|
||||||
@Override
|
@Override
|
||||||
public ItemUseOp getItemUseOp() {
|
public ItemUseOp getItemUseOp() {
|
||||||
return ItemUseOp.ITEM_USE_ADD_WEAPON_EXP;
|
return ItemUseOp.ITEM_USE_ADD_WEAPON_EXP;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ItemUseAddWeaponExp(String[] useParam) {
|
public ItemUseAddWeaponExp(String[] useParam) {
|
||||||
try {
|
super(useParam);
|
||||||
this.exp = Integer.parseInt(useParam[0]);
|
|
||||||
} catch (NumberFormatException ignored) {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,10 +15,10 @@ public class ItemUseCombineItem extends ItemUseInt {
|
|||||||
super(useParam);
|
super(useParam);
|
||||||
try {
|
try {
|
||||||
this.resultId = Integer.parseInt(useParam[1]);
|
this.resultId = Integer.parseInt(useParam[1]);
|
||||||
} catch (NumberFormatException ignored) {}
|
} catch (NumberFormatException | ArrayIndexOutOfBoundsException ignored) {}
|
||||||
try {
|
try {
|
||||||
this.resultCount = Integer.parseInt(useParam[2]);
|
this.resultCount = Integer.parseInt(useParam[2]);
|
||||||
} catch (NumberFormatException ignored) {}
|
} catch (NumberFormatException | ArrayIndexOutOfBoundsException ignored) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -19,10 +19,10 @@ public class ItemUseGainAvatar extends ItemUseInt {
|
|||||||
super(useParam);
|
super(useParam);
|
||||||
try {
|
try {
|
||||||
this.level = Integer.parseInt(useParam[1]);
|
this.level = Integer.parseInt(useParam[1]);
|
||||||
} catch (NumberFormatException ignored) {}
|
} catch (NumberFormatException | ArrayIndexOutOfBoundsException ignored) {}
|
||||||
try {
|
try {
|
||||||
this.constellation = Integer.parseInt(useParam[2]);
|
this.constellation = Integer.parseInt(useParam[2]);
|
||||||
} catch (NumberFormatException ignored) {}
|
} catch (NumberFormatException | ArrayIndexOutOfBoundsException ignored) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -8,6 +8,6 @@ public abstract class ItemUseInt extends ItemUseAction {
|
|||||||
public ItemUseInt(String[] useParam) {
|
public ItemUseInt(String[] useParam) {
|
||||||
try {
|
try {
|
||||||
this.i = Integer.parseInt(useParam[0]);
|
this.i = Integer.parseInt(useParam[0]);
|
||||||
} catch (NumberFormatException ignored) {}
|
} catch (NumberFormatException | ArrayIndexOutOfBoundsException ignored) {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package emu.grasscutter.utils;
|
package emu.grasscutter.utils;
|
||||||
|
|
||||||
import emu.grasscutter.Grasscutter;
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import lombok.val;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -87,6 +88,22 @@ public final class FileUtils {
|
|||||||
: Path.of(scripts);
|
: Path.of(scripts);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static final String[] TSJ_JSON_TSV = {"tsj", "json", "tsv"};
|
||||||
|
private static final Path[] DATA_PATHS = {DATA_USER_PATH, DATA_DEFAULT_PATH};
|
||||||
|
public static Path getDataPathTsjJsonTsv(String filename) {
|
||||||
|
return getDataPathTsjJsonTsv(filename, true);
|
||||||
|
}
|
||||||
|
public static Path getDataPathTsjJsonTsv(String filename, boolean fallback) {
|
||||||
|
val name = getFilenameWithoutExtension(filename);
|
||||||
|
for (val data_path : DATA_PATHS) {
|
||||||
|
for (val ext : TSJ_JSON_TSV) {
|
||||||
|
val path = data_path.resolve(name + "." + ext);
|
||||||
|
if (Files.exists(path)) return path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fallback ? DATA_USER_PATH.resolve(name + ".tsj") : null; // Maybe they want to write to a new file
|
||||||
|
}
|
||||||
|
|
||||||
public static Path getDataPath(String path) {
|
public static Path getDataPath(String path) {
|
||||||
Path userPath = DATA_USER_PATH.resolve(path);
|
Path userPath = DATA_USER_PATH.resolve(path);
|
||||||
if (Files.exists(userPath)) return userPath;
|
if (Files.exists(userPath)) return userPath;
|
||||||
@ -111,6 +128,22 @@ public final class FileUtils {
|
|||||||
return RESOURCES_PATH.resolve(path);
|
return RESOURCES_PATH.resolve(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Path getExcelPath(String filename) {
|
||||||
|
return getTsjJsonTsv(RESOURCES_PATH.resolve("ExcelBinOutput"), filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gets path of a resource.
|
||||||
|
// If multiple formats of it exist, priority is TSJ > JSON > TSV
|
||||||
|
// If none exist, return the TSJ path, in case it wants to create a file
|
||||||
|
public static Path getTsjJsonTsv(Path root, String filename) {
|
||||||
|
val name = getFilenameWithoutExtension(filename);
|
||||||
|
for (val ext : TSJ_JSON_TSV) {
|
||||||
|
val path = root.resolve(name + "." + ext);
|
||||||
|
if (Files.exists(path)) return path;
|
||||||
|
}
|
||||||
|
return root.resolve(name + ".tsj");
|
||||||
|
}
|
||||||
|
|
||||||
public static Path getScriptPath(String path) {
|
public static Path getScriptPath(String path) {
|
||||||
return SCRIPTS_PATH.resolve(path);
|
return SCRIPTS_PATH.resolve(path);
|
||||||
}
|
}
|
||||||
@ -167,14 +200,19 @@ public final class FileUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated // No current uses of this anyway
|
@Deprecated // Misnamed legacy function
|
||||||
public static String getFilenameWithoutPath(String fileName) {
|
public static String getFilenameWithoutPath(String filename) {
|
||||||
int i = fileName.lastIndexOf(".");
|
return getFilenameWithoutExtension(filename);
|
||||||
if (i > 0) {
|
}
|
||||||
return fileName.substring(0, i);
|
public static String getFilenameWithoutExtension(String filename) {
|
||||||
} else {
|
int i = filename.lastIndexOf(".");
|
||||||
return fileName;
|
return (i < 0) ? filename : filename.substring(0, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getFileExtension(Path path) {
|
||||||
|
val filename = path.toString();
|
||||||
|
int i = filename.lastIndexOf(".");
|
||||||
|
return (i < 0) ? "" : filename.substring(i+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Path> getPathsFromResource(String folder) throws URISyntaxException {
|
public static List<Path> getPathsFromResource(String folder) throws URISyntaxException {
|
||||||
|
@ -10,9 +10,11 @@ import com.google.gson.TypeAdapter;
|
|||||||
import com.google.gson.TypeAdapterFactory;
|
import com.google.gson.TypeAdapterFactory;
|
||||||
import com.google.gson.reflect.TypeToken;
|
import com.google.gson.reflect.TypeToken;
|
||||||
import com.google.gson.stream.JsonReader;
|
import com.google.gson.stream.JsonReader;
|
||||||
|
import com.google.gson.stream.JsonToken;
|
||||||
import com.google.gson.stream.JsonWriter;
|
import com.google.gson.stream.JsonWriter;
|
||||||
|
|
||||||
import emu.grasscutter.data.common.DynamicFloat;
|
import emu.grasscutter.data.common.DynamicFloat;
|
||||||
|
import it.unimi.dsi.fastutil.floats.FloatArrayList;
|
||||||
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
import it.unimi.dsi.fastutil.ints.IntArrayList;
|
||||||
import it.unimi.dsi.fastutil.ints.IntList;
|
import it.unimi.dsi.fastutil.ints.IntList;
|
||||||
import lombok.val;
|
import lombok.val;
|
||||||
@ -65,43 +67,85 @@ public class JsonAdapters {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(JsonWriter writer, IntList i) {};
|
public void write(JsonWriter writer, IntList l) throws IOException {
|
||||||
|
writer.beginArray();
|
||||||
|
for (val i : l) // .forEach() doesn't appreciate exceptions
|
||||||
|
writer.value(i);
|
||||||
|
writer.endArray();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static class PositionAdapter extends TypeAdapter<Position> {
|
||||||
|
@Override
|
||||||
|
public Position read(JsonReader reader) throws IOException {
|
||||||
|
switch (reader.peek()) {
|
||||||
|
case BEGIN_ARRAY: // "pos": [x,y,z]
|
||||||
|
reader.beginArray();
|
||||||
|
val array = new FloatArrayList(3);
|
||||||
|
while (reader.hasNext())
|
||||||
|
array.add(reader.nextInt());
|
||||||
|
reader.endArray();
|
||||||
|
return new Position(array);
|
||||||
|
case BEGIN_OBJECT: // "pos": {"x": x, "y": y, "z": z}
|
||||||
|
float x = 0f;
|
||||||
|
float y = 0f;
|
||||||
|
float z = 0f;
|
||||||
|
reader.beginObject();
|
||||||
|
for (var next = reader.peek(); next != JsonToken.END_OBJECT; next = reader.peek()) {
|
||||||
|
val name = reader.nextName();
|
||||||
|
switch (name) {
|
||||||
|
case "x", "X", "_x" -> x = (float) reader.nextDouble();
|
||||||
|
case "y", "Y", "_y" -> y = (float) reader.nextDouble();
|
||||||
|
case "z", "Z", "_z" -> z = (float) reader.nextDouble();
|
||||||
|
default -> throw new IOException("Invalid field in Position definition - " + name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
reader.endObject();
|
||||||
|
return new Position(x, y, z);
|
||||||
|
default:
|
||||||
|
throw new IOException("Invalid Position definition - " + reader.peek().name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(JsonWriter writer, Position i) throws IOException {
|
||||||
|
writer.beginArray();
|
||||||
|
writer.value(i.getX());
|
||||||
|
writer.value(i.getY());
|
||||||
|
writer.value(i.getZ());
|
||||||
|
writer.endArray();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static class EnumTypeAdapterFactory implements TypeAdapterFactory {
|
static class EnumTypeAdapterFactory implements TypeAdapterFactory {
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
|
public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
|
||||||
Class<T> rawType = (Class<T>) type.getRawType();
|
Class<T> enumClass = (Class<T>) type.getRawType();
|
||||||
if (!rawType.isEnum()) return null;
|
if (!enumClass.isEnum()) return null;
|
||||||
|
|
||||||
Field id = null;
|
// Make mappings of (string) names to enum constants
|
||||||
// System.out.println("Looking for enum value field");
|
|
||||||
for (Field f : rawType.getDeclaredFields()) {
|
|
||||||
id = switch (f.getName()) {
|
|
||||||
case "value", "id" -> f;
|
|
||||||
default -> null;
|
|
||||||
};
|
|
||||||
if (id != null) break;
|
|
||||||
}
|
|
||||||
if (id == null) {
|
|
||||||
// System.out.println("Not found");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
// System.out.println("Enum value field found - " + id.getName());
|
|
||||||
|
|
||||||
val map = new HashMap<String, T>();
|
val map = new HashMap<String, T>();
|
||||||
boolean acc = id.isAccessible();
|
val enumConstants = enumClass.getEnumConstants();
|
||||||
id.setAccessible(true);
|
for (val constant : enumConstants)
|
||||||
try {
|
map.put(constant.toString(), constant);
|
||||||
for (T constant : rawType.getEnumConstants()) {
|
|
||||||
map.put(constant.toString(), constant);
|
// If the enum also has a numeric value, map those to the constants too
|
||||||
map.put(String.valueOf(id.getInt(constant)), constant);
|
// System.out.println("Looking for enum value field");
|
||||||
|
for (Field f : enumClass.getDeclaredFields()) {
|
||||||
|
if (switch (f.getName()) {case "value", "id" -> true; default -> false;}) {
|
||||||
|
// System.out.println("Enum value field found - " + f.getName());
|
||||||
|
boolean acc = f.isAccessible();
|
||||||
|
f.setAccessible(true);
|
||||||
|
try {
|
||||||
|
for (val constant : enumConstants)
|
||||||
|
map.put(String.valueOf(f.getInt(constant)), constant);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
// System.out.println("Failed to access enum id field.");
|
||||||
|
}
|
||||||
|
f.setAccessible(acc);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
// System.out.println("Failed to access enum id field.");
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
id.setAccessible(acc);
|
|
||||||
|
|
||||||
return new TypeAdapter<T>() {
|
return new TypeAdapter<T>() {
|
||||||
public T read(JsonReader reader) throws IOException {
|
public T read(JsonReader reader) throws IOException {
|
||||||
@ -114,7 +158,9 @@ public class JsonAdapters {
|
|||||||
throw new IOException("Invalid Enum definition - " + reader.peek().name());
|
throw new IOException("Invalid Enum definition - " + reader.peek().name());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void write(JsonWriter writer, T value) {}
|
public void write(JsonWriter writer, T value) throws IOException {
|
||||||
|
writer.value(value.toString());
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import java.io.FileInputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@ -18,6 +19,7 @@ import com.google.gson.reflect.TypeToken;
|
|||||||
|
|
||||||
import emu.grasscutter.data.common.DynamicFloat;
|
import emu.grasscutter.data.common.DynamicFloat;
|
||||||
import emu.grasscutter.utils.JsonAdapters.*;
|
import emu.grasscutter.utils.JsonAdapters.*;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.ints.IntList;
|
import it.unimi.dsi.fastutil.ints.IntList;
|
||||||
|
|
||||||
public final class JsonUtils {
|
public final class JsonUtils {
|
||||||
@ -25,6 +27,7 @@ public final class JsonUtils {
|
|||||||
.setPrettyPrinting()
|
.setPrettyPrinting()
|
||||||
.registerTypeAdapter(DynamicFloat.class, new DynamicFloatAdapter())
|
.registerTypeAdapter(DynamicFloat.class, new DynamicFloatAdapter())
|
||||||
.registerTypeAdapter(IntList.class, new IntListAdapter())
|
.registerTypeAdapter(IntList.class, new IntListAdapter())
|
||||||
|
.registerTypeAdapter(Position.class, new PositionAdapter())
|
||||||
.registerTypeAdapterFactory(new EnumTypeAdapterFactory())
|
.registerTypeAdapterFactory(new EnumTypeAdapterFactory())
|
||||||
.create();
|
.create();
|
||||||
|
|
||||||
@ -102,4 +105,12 @@ public final class JsonUtils {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static <T> T decode(String jsonData, Type type) {
|
||||||
|
try {
|
||||||
|
return gson.fromJson(jsonData, type);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package emu.grasscutter.utils;
|
package emu.grasscutter.utils;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import com.google.gson.annotations.SerializedName;
|
import com.google.gson.annotations.SerializedName;
|
||||||
import com.github.davidmoten.rtreemulti.geometry.Point;
|
import com.github.davidmoten.rtreemulti.geometry.Point;
|
||||||
@ -22,9 +23,7 @@ public class Position implements Serializable {
|
|||||||
@SerializedName(value="z", alternate={"_z", "Z"})
|
@SerializedName(value="z", alternate={"_z", "Z"})
|
||||||
@Getter @Setter private float z;
|
@Getter @Setter private float z;
|
||||||
|
|
||||||
public Position() {
|
public Position() {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public Position(float x, float y) {
|
public Position(float x, float y) {
|
||||||
set(x, y);
|
set(x, y);
|
||||||
@ -34,6 +33,20 @@ public class Position implements Serializable {
|
|||||||
set(x, y, z);
|
set(x, y, z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Position(List<Float> xyz) {
|
||||||
|
switch (xyz.size()) {
|
||||||
|
default: // Might want to error on excess elements, but maybe we want to extend to 3+3 representation later.
|
||||||
|
case 3:
|
||||||
|
this.z = xyz.get(2); // Fall-through
|
||||||
|
case 2:
|
||||||
|
this.y = xyz.get(1); // Fall-through
|
||||||
|
case 1:
|
||||||
|
this.y = xyz.get(0); // pointless fall-through
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Position(String p) {
|
public Position(String p) {
|
||||||
String[] split = p.split(",");
|
String[] split = p.split(",");
|
||||||
if (split.length >= 2) {
|
if (split.length >= 2) {
|
||||||
|
600
src/main/java/emu/grasscutter/utils/TsvUtils.java
Normal file
600
src/main/java/emu/grasscutter/utils/TsvUtils.java
Normal file
@ -0,0 +1,600 @@
|
|||||||
|
package emu.grasscutter.utils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.lang.reflect.ParameterizedType;
|
||||||
|
import java.lang.reflect.Type;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.NoSuchFileException;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.SortedMap;
|
||||||
|
import java.util.TreeMap;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import com.google.gson.JsonArray;
|
||||||
|
import com.google.gson.JsonElement;
|
||||||
|
import com.google.gson.JsonNull;
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import com.google.gson.JsonPrimitive;
|
||||||
|
import com.google.gson.annotations.SerializedName;
|
||||||
|
|
||||||
|
import emu.grasscutter.Grasscutter;
|
||||||
|
import it.unimi.dsi.fastutil.Pair;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectRBTreeMap;
|
||||||
|
import it.unimi.dsi.fastutil.ints.Int2ObjectSortedMap;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2IntArrayMap;
|
||||||
|
import lombok.val;
|
||||||
|
|
||||||
|
import static emu.grasscutter.utils.Utils.nonRegexSplit;
|
||||||
|
|
||||||
|
// Throughout this file, commented System.out.println debug log calls are left in.
|
||||||
|
// This is because the default logger will deadlock when operating on parallel streams.
|
||||||
|
public class TsvUtils {
|
||||||
|
private static final Map<Type, Object> defaultValues = Map.ofEntries(
|
||||||
|
// Map.entry(String.class, null), // builder hates null values
|
||||||
|
Map.entry(Integer.class, 0),
|
||||||
|
Map.entry(int.class, 0),
|
||||||
|
Map.entry(Long.class, 0L),
|
||||||
|
Map.entry(long.class, 0L),
|
||||||
|
Map.entry(Float.class, 0f),
|
||||||
|
Map.entry(float.class, 0f),
|
||||||
|
Map.entry(Double.class, 0d),
|
||||||
|
Map.entry(double.class, 0d),
|
||||||
|
Map.entry(Boolean.class, false),
|
||||||
|
Map.entry(boolean.class, false)
|
||||||
|
);
|
||||||
|
private static final Set<Type> primitiveTypes = Set.of(String.class, Integer.class, int.class, Long.class, long.class, Float.class, float.class, Double.class, double.class, Boolean.class, boolean.class);
|
||||||
|
|
||||||
|
private static final Function<String, Object> parseString = value -> value;
|
||||||
|
private static final Function<String, Object> parseInt = value -> (int) Double.parseDouble(value); //Integer::parseInt;
|
||||||
|
private static final Function<String, Object> parseLong = value -> (long) Double.parseDouble(value); //Long::parseLong;
|
||||||
|
private static Map<Type, Function<String, Object>> primitiveTypeParsers = Map.ofEntries(
|
||||||
|
Map.entry(String.class, parseString),
|
||||||
|
Map.entry(Integer.class, parseInt),
|
||||||
|
Map.entry(int.class, parseInt),
|
||||||
|
Map.entry(Long.class, parseLong),
|
||||||
|
Map.entry(long.class, parseLong),
|
||||||
|
Map.entry(Float.class, Float::parseFloat),
|
||||||
|
Map.entry(float.class, Float::parseFloat),
|
||||||
|
Map.entry(Double.class, Double::parseDouble),
|
||||||
|
Map.entry(double.class, Double::parseDouble),
|
||||||
|
Map.entry(Boolean.class, Boolean::parseBoolean),
|
||||||
|
Map.entry(boolean.class, Boolean::parseBoolean)
|
||||||
|
);
|
||||||
|
private static final Map<Type, Function<String, Object>> typeParsers = new HashMap<>(primitiveTypeParsers);
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static <T> T parsePrimitive(Class<T> type, String string) {
|
||||||
|
if (string == null || string.isEmpty()) return (T) defaultValues.get(type);
|
||||||
|
return (T) primitiveTypeParsers.get(type).apply(string);
|
||||||
|
}
|
||||||
|
// This is more expensive than parsing as the correct types, but it is more tolerant of mismatched data like ints with .0
|
||||||
|
private static double parseNumber(String string) {
|
||||||
|
if (string == null || string.isEmpty()) return 0d;
|
||||||
|
return Double.parseDouble(string);
|
||||||
|
}
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static <T> T parseEnum(Class<T> enumType, String string) {
|
||||||
|
if (string == null || string.isEmpty()) return null;
|
||||||
|
return (T) getEnumTypeParser(enumType).apply(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is idiotic. I hate it. I'll have to look into how Gson beats the JVM into submission over classes where reflection magically fails to find the NoArgsConstructor later.
|
||||||
|
public static <T> T newObj(Class<T> objClass) {
|
||||||
|
try {
|
||||||
|
return objClass.getDeclaredConstructor().newInstance();
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
return JsonUtils.decode("{}", objClass);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Map<Class<?>, Function<String, Object>> enumTypeParsers = new HashMap<>();
|
||||||
|
@SuppressWarnings("deprecated") // Field::isAccessible is deprecated because it doesn't do what people think it does. It does what we want it to, however.
|
||||||
|
private static Function<String, Object> makeEnumTypeParser(Class<?> enumClass) {
|
||||||
|
if (!enumClass.isEnum()) {
|
||||||
|
// System.out.println("Called makeEnumTypeParser with non-enum enumClass "+enumClass);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make mappings of (string) names to enum constants
|
||||||
|
val map = new HashMap<String, Object>();
|
||||||
|
val enumConstants = enumClass.getEnumConstants();
|
||||||
|
for (val constant : enumConstants)
|
||||||
|
map.put(constant.toString(), constant);
|
||||||
|
|
||||||
|
// If the enum also has a numeric value, map those to the constants too
|
||||||
|
// System.out.println("Looking for enum value field");
|
||||||
|
for (Field f : enumClass.getDeclaredFields()) {
|
||||||
|
if (switch (f.getName()) {case "value", "id" -> true; default -> false;}) {
|
||||||
|
// System.out.println("Enum value field found - " + f.getName());
|
||||||
|
boolean acc = f.isAccessible();
|
||||||
|
f.setAccessible(true);
|
||||||
|
try {
|
||||||
|
for (val constant : enumConstants)
|
||||||
|
map.put(String.valueOf(f.getInt(constant)), constant);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
// System.out.println("Failed to access enum id field.");
|
||||||
|
}
|
||||||
|
f.setAccessible(acc);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map::get;
|
||||||
|
}
|
||||||
|
private static synchronized Function<String, Object> getEnumTypeParser(Class<?> enumType) {
|
||||||
|
if (enumType == null) {
|
||||||
|
// System.out.println("Called getEnumTypeParser with null enumType");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return enumTypeParsers.computeIfAbsent(enumType, TsvUtils::makeEnumTypeParser);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static synchronized Function<String, Object> getTypeParser(Type type) {
|
||||||
|
if (type == null) return parseString;
|
||||||
|
return typeParsers.computeIfAbsent(type, t -> value -> JsonUtils.decode(value, t));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Type class2Type(Class<?> classType) {
|
||||||
|
return (Type) classType.getGenericSuperclass();
|
||||||
|
}
|
||||||
|
private static Class<?> type2Class(Type type) {
|
||||||
|
if (type instanceof Class) {
|
||||||
|
return (Class<?>) type;
|
||||||
|
} else if (type instanceof ParameterizedType) {
|
||||||
|
return (Class<?>) ((ParameterizedType) type).getRawType();
|
||||||
|
} else {
|
||||||
|
return type.getClass(); // Probably incorrect
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A helper object that contains a Field and the function to parse a String to create the value for the Field.
|
||||||
|
private static class FieldParser {
|
||||||
|
public final Field field;
|
||||||
|
public final Type type;
|
||||||
|
public final Class<?> classType;
|
||||||
|
public final Function<String, Object> parser;
|
||||||
|
|
||||||
|
FieldParser(Field field) {
|
||||||
|
this.field = field;
|
||||||
|
this.type = field.getGenericType(); // returns specialized type info e.g. java.util.List<java.lang.Integer>
|
||||||
|
this.classType = field.getType();
|
||||||
|
this.parser = getTypeParser(this.type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object parse(String token) {
|
||||||
|
return this.parser.apply(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void parse(Object obj, String token) throws IllegalAccessException {
|
||||||
|
this.field.set(obj, this.parser.apply(token));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<String, FieldParser> makeClassFieldMap(Class<?> classType) {
|
||||||
|
val fieldMap = new HashMap<String, FieldParser>();
|
||||||
|
for (Field field : classType.getDeclaredFields()) {
|
||||||
|
field.setAccessible(true); // Yes, we don't bother setting this back. No, it doesn't matter for this project.
|
||||||
|
val fieldParser = new FieldParser(field);
|
||||||
|
|
||||||
|
val a = field.getDeclaredAnnotation(SerializedName.class);
|
||||||
|
if (a == null) { // No annotation, use raw field name
|
||||||
|
fieldMap.put(field.getName(), fieldParser);
|
||||||
|
} else { // Handle SerializedNames and alternatives
|
||||||
|
fieldMap.put(a.value(), fieldParser);
|
||||||
|
for (val alt : a.alternate()) {
|
||||||
|
fieldMap.put(alt, fieldParser);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fieldMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Map<Class<?>, Map<String, FieldParser>> cachedClassFieldMaps = new HashMap<>();
|
||||||
|
private static synchronized Map<String, FieldParser> getClassFieldMap(Class<?> classType) {
|
||||||
|
return cachedClassFieldMaps.computeIfAbsent(classType, TsvUtils::makeClassFieldMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class StringTree {
|
||||||
|
public final Map<String, StringTree> children = new TreeMap<>();
|
||||||
|
|
||||||
|
public void addPath(String path) {
|
||||||
|
if (path.isEmpty()) return;
|
||||||
|
|
||||||
|
val firstDot = path.indexOf('.');
|
||||||
|
val fieldPath = (firstDot < 0) ? path : path.substring(0, firstDot);
|
||||||
|
val remainder = (firstDot < 0) ? "" : path.substring(firstDot+1);
|
||||||
|
this.children.computeIfAbsent(fieldPath, k -> new StringTree()).addPath(remainder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static class StringValueTree {
|
||||||
|
public final SortedMap<String, StringValueTree> children = new TreeMap<>();
|
||||||
|
public final Int2ObjectSortedMap<StringValueTree> arrayChildren = new Int2ObjectRBTreeMap<>();
|
||||||
|
public String value;
|
||||||
|
|
||||||
|
public StringValueTree(StringTree from) {
|
||||||
|
from.children.forEach((k,v) -> {
|
||||||
|
try {
|
||||||
|
this.arrayChildren.put(Integer.parseInt(k), new StringValueTree(v));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
this.children.put(k, new StringValueTree(v));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(String path, String value) {
|
||||||
|
if (path.isEmpty()) {
|
||||||
|
this.value = value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
val firstDot = path.indexOf('.');
|
||||||
|
val fieldPath = (firstDot < 0) ? path : path.substring(0, firstDot);
|
||||||
|
val remainder = (firstDot < 0) ? "" : path.substring(firstDot+1);
|
||||||
|
try {
|
||||||
|
this.arrayChildren.get(Integer.parseInt(fieldPath)).setValue(remainder, value);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
this.children.get(fieldPath).setValue(remainder, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public JsonElement toJson() {
|
||||||
|
// Determine if this is an object, an array, or a value
|
||||||
|
if (this.value != null) { //
|
||||||
|
return new JsonPrimitive(this.value);
|
||||||
|
}
|
||||||
|
if (!this.arrayChildren.isEmpty()) {
|
||||||
|
val arr = new JsonArray(this.arrayChildren.lastIntKey()+1);
|
||||||
|
arrayChildren.forEach((k,v) -> arr.set(k, v.toJson()));
|
||||||
|
return arr;
|
||||||
|
} else if (this.children.isEmpty()) {
|
||||||
|
return JsonNull.INSTANCE;
|
||||||
|
} else {
|
||||||
|
val obj = new JsonObject();
|
||||||
|
children.forEach((k,v) -> {
|
||||||
|
val j = v.toJson();
|
||||||
|
if (j != JsonNull.INSTANCE)
|
||||||
|
obj.add(k, v.toJson());
|
||||||
|
});
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T toClass(Class<T> classType, Type type) {
|
||||||
|
// System.out.println("toClass called with Class: "+classType+" \tType: "+type);
|
||||||
|
if (type == null)
|
||||||
|
type = class2Type(classType);
|
||||||
|
|
||||||
|
if (primitiveTypeParsers.containsKey(classType)) {
|
||||||
|
return parsePrimitive(classType, this.value);
|
||||||
|
} else if (classType.isEnum()) {
|
||||||
|
return parseEnum(classType, this.value);
|
||||||
|
} else if (classType.isArray()) {
|
||||||
|
return this.toArray(classType);
|
||||||
|
} else if (List.class.isAssignableFrom(classType)) {
|
||||||
|
// if (type instanceof ParameterizedType)
|
||||||
|
val elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
|
||||||
|
return (T) this.toList(type2Class(elementType), elementType);
|
||||||
|
} else if (Map.class.isAssignableFrom(classType)) {
|
||||||
|
// System.out.println("Class: "+classType+" \tClassTypeParams: "+Arrays.toString(classType.getTypeParameters())+" \tType: "+type+" \tTypeArguments: "+Arrays.toString(((ParameterizedType) type).getActualTypeArguments()));
|
||||||
|
// if (type instanceof ParameterizedType)
|
||||||
|
val keyType = ((ParameterizedType) type).getActualTypeArguments()[0];
|
||||||
|
val valueType = ((ParameterizedType) type).getActualTypeArguments()[1];
|
||||||
|
return (T) this.toMap(type2Class(keyType), type2Class(valueType), valueType);
|
||||||
|
} else {
|
||||||
|
return this.toObj(classType, type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T> T toObj(Class<T> objClass, Type objType) {
|
||||||
|
try {
|
||||||
|
// val obj = objClass.getDeclaredConstructor().newInstance();
|
||||||
|
val obj = newObj(objClass);
|
||||||
|
val fieldMap = getClassFieldMap(objClass);
|
||||||
|
this.children.forEach((name, tree) -> {
|
||||||
|
val field = fieldMap.get(name);
|
||||||
|
if (field == null) return;
|
||||||
|
try {
|
||||||
|
if (primitiveTypes.contains(field.type)) {
|
||||||
|
if ((tree.value != null) && !tree.value.isEmpty())
|
||||||
|
field.parse(obj, tree.value);
|
||||||
|
} else {
|
||||||
|
val value = tree.toClass(field.classType, field.type);
|
||||||
|
// System.out.println("Setting field "+name+" to "+value);
|
||||||
|
field.field.set(obj, value);
|
||||||
|
// field.field.set(obj, tree.toClass(field.classType, field.type));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// System.out.println("Exception while setting field "+name+" for class "+objClass+" - "+e);
|
||||||
|
Grasscutter.getLogger().error("Exception while setting field "+name+" ("+field.classType+")"+" for class "+objClass+" - ",e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return obj;
|
||||||
|
} catch (Exception e) {
|
||||||
|
// System.out.println("Exception while creating object of class "+objClass+" - "+e);
|
||||||
|
Grasscutter.getLogger().error("Exception while creating object of class "+objClass+" - ",e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> T toArray(Class<T> classType) {
|
||||||
|
// Primitives don't play so nice with generics, so we handle all of them individually.
|
||||||
|
val containedClass = classType.getComponentType();
|
||||||
|
// val arraySize = this.arrayChildren.size(); // Assume dense 0-indexed
|
||||||
|
val arraySize = this.arrayChildren.lastIntKey()+1; // Could be sparse!
|
||||||
|
// System.out.println("toArray called with Class: "+classType+" \tContains: "+containedClass+" \tof size: "+arraySize);
|
||||||
|
if (containedClass == int.class) {
|
||||||
|
val output = new int[arraySize];
|
||||||
|
this.arrayChildren.forEach((idx, tree) -> output[idx] = (int) parseNumber(tree.value));
|
||||||
|
return (T) output;
|
||||||
|
} else if (containedClass == long.class) {
|
||||||
|
val output = new long[arraySize];
|
||||||
|
this.arrayChildren.forEach((idx, tree) -> output[idx] = (long) parseNumber(tree.value));
|
||||||
|
return (T) output;
|
||||||
|
} else if (containedClass == float.class) {
|
||||||
|
val output = new float[arraySize];
|
||||||
|
this.arrayChildren.forEach((idx, tree) -> output[idx] = (float) parseNumber(tree.value));
|
||||||
|
return (T) output;
|
||||||
|
} else if (containedClass == double.class) {
|
||||||
|
val output = new double[arraySize];
|
||||||
|
this.arrayChildren.forEach((idx, tree) -> output[idx] = (double) parseNumber(tree.value));
|
||||||
|
return (T) output;
|
||||||
|
} else if (containedClass == byte.class) {
|
||||||
|
val output = new byte[arraySize];
|
||||||
|
this.arrayChildren.forEach((idx, tree) -> output[idx] = (byte) parseNumber(tree.value));
|
||||||
|
return (T) output;
|
||||||
|
} else if (containedClass == char.class) {
|
||||||
|
val output = new char[arraySize];
|
||||||
|
this.arrayChildren.forEach((idx, tree) -> output[idx] = (char) parseNumber(tree.value));
|
||||||
|
return (T) output;
|
||||||
|
} else if (containedClass == short.class) {
|
||||||
|
val output = new short[arraySize];
|
||||||
|
this.arrayChildren.forEach((idx, tree) -> output[idx] = (short) parseNumber(tree.value));
|
||||||
|
return (T) output;
|
||||||
|
} else if (containedClass == boolean.class) {
|
||||||
|
val output = new boolean[arraySize];
|
||||||
|
this.arrayChildren.forEach((idx, tree) -> {
|
||||||
|
val value = ((tree.value == null) || tree.value.isEmpty()) ? false : Boolean.parseBoolean(tree.value);
|
||||||
|
output[idx] = value;
|
||||||
|
});
|
||||||
|
return (T) output;
|
||||||
|
} else {
|
||||||
|
val output = Array.newInstance(containedClass, arraySize);
|
||||||
|
this.arrayChildren.forEach((idx, tree) -> ((Object[]) output)[idx] = tree.toClass(containedClass, null));
|
||||||
|
return (T) output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private <E> List<E> toList(Class<E> valueClass, Type valueType) {
|
||||||
|
val arraySize = this.arrayChildren.lastIntKey()+1; // Could be sparse!
|
||||||
|
// System.out.println("toList called with valueClass: "+valueClass+" \tvalueType: "+valueType+" \tof size: "+arraySize);
|
||||||
|
val list = new ArrayList<E>(arraySize);
|
||||||
|
// Safe sparse version
|
||||||
|
for (int i = 0; i < arraySize; i++)
|
||||||
|
list.add(null);
|
||||||
|
this.arrayChildren.forEach((idx, tree) -> list.set(idx, tree.toClass(valueClass, valueType)));
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
private <K,V> Map<K,V> toMap(Class<K> keyClass, Class<V> valueClass, Type valueType) {
|
||||||
|
val map = new HashMap<K,V>();
|
||||||
|
val keyParser = getTypeParser(keyClass);
|
||||||
|
this.children.forEach((key, tree) -> {
|
||||||
|
if ((key != null) && !key.isEmpty())
|
||||||
|
map.put((K) keyParser.apply(key), tree.toClass(valueClass, valueType));
|
||||||
|
});
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flat tab-separated value tables.
|
||||||
|
// Arrays are represented as arrayName.0, arrayName.1, etc. columns.
|
||||||
|
// Maps/POJOs are represented as objName.fieldOneName, objName.fieldTwoName, etc. columns.
|
||||||
|
// This is currently about 25x as slow as TSJ and Gson parsers, likely due to the tree spam.
|
||||||
|
public static <T> List<T> loadTsvToListSetField(Path filename, Class<T> classType) {
|
||||||
|
try (val fileReader = Files.newBufferedReader(filename, StandardCharsets.UTF_8)) {
|
||||||
|
// val fieldMap = getClassFieldMap(classType);
|
||||||
|
// val constructor = classType.getDeclaredConstructor();
|
||||||
|
|
||||||
|
val headerNames = nonRegexSplit(fileReader.readLine(), '\t');
|
||||||
|
val columns = headerNames.size();
|
||||||
|
// If we just crawled through all fields to expand potential subobjects, we might hit recursive data structure explosions (e.g. if something has a Player object)
|
||||||
|
// So we'll only crawl through objects referenced by the header columns
|
||||||
|
val stringTree = new StringTree();
|
||||||
|
headerNames.forEach(stringTree::addPath);
|
||||||
|
|
||||||
|
return fileReader.lines().parallel().map(line -> {
|
||||||
|
// return fileReader.lines().map(line -> {
|
||||||
|
// System.out.println("Processing line of "+filename+" - "+line);
|
||||||
|
val tokens = nonRegexSplit(line, '\t');
|
||||||
|
val m = Math.min(tokens.size(), columns);
|
||||||
|
int t = 0;
|
||||||
|
StringValueTree tree = new StringValueTree(stringTree);
|
||||||
|
try {
|
||||||
|
for (t = 0; t < m; t++) {
|
||||||
|
String token = tokens.get(t);
|
||||||
|
if (!token.isEmpty()) {
|
||||||
|
tree.setValue(headerNames.get(t), token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// return JsonUtils.decode(tree.toJson(), classType);
|
||||||
|
return tree.toClass(classType, null);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Grasscutter.getLogger().warn("Error deserializing an instance of class "+classType.getCanonicalName());
|
||||||
|
Grasscutter.getLogger().warn("At token #"+t+" of #"+m);
|
||||||
|
Grasscutter.getLogger().warn("Header names are: "+headerNames.toString());
|
||||||
|
Grasscutter.getLogger().warn("Tokens are: "+tokens.toString());
|
||||||
|
Grasscutter.getLogger().warn("Stacktrace is: ", e);
|
||||||
|
// System.out.println("Error deserializing an instance of class "+classType.getCanonicalName());
|
||||||
|
// System.out.println("At token #"+t+" of #"+m);
|
||||||
|
// System.out.println("Header names are: "+headerNames.toString());
|
||||||
|
// System.out.println("Tokens are: "+tokens.toString());
|
||||||
|
// System.out.println("Json is: "+tree.toJson().toString());
|
||||||
|
// System.out.println("Stacktrace is: "+ e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}).toList();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Grasscutter.getLogger().error("Error loading file '"+filename+"' - Stacktrace is: ", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This uses a hybrid format where columns can hold JSON-encoded values.
|
||||||
|
// I'll term it TSJ (tab-separated JSON) for now, it has convenient properties.
|
||||||
|
public static <T> List<T> loadTsjToListSetField(Path filename, Class<T> classType) {
|
||||||
|
try (val fileReader = Files.newBufferedReader(filename, StandardCharsets.UTF_8)) {
|
||||||
|
val fieldMap = getClassFieldMap(classType);
|
||||||
|
val constructor = classType.getDeclaredConstructor();
|
||||||
|
|
||||||
|
val headerNames = nonRegexSplit(fileReader.readLine(), '\t');
|
||||||
|
val columns = headerNames.size();
|
||||||
|
val fieldParsers = headerNames.stream().map(fieldMap::get).toList();
|
||||||
|
|
||||||
|
return fileReader.lines().parallel().map(line -> {
|
||||||
|
val tokens = nonRegexSplit(line, '\t');
|
||||||
|
val m = Math.min(tokens.size(), columns);
|
||||||
|
int t = 0;
|
||||||
|
try {
|
||||||
|
T obj = constructor.newInstance();
|
||||||
|
for (t = 0; t < m; t++) {
|
||||||
|
val fieldParser = fieldParsers.get(t);
|
||||||
|
if (fieldParser == null) continue;
|
||||||
|
|
||||||
|
String token = tokens.get(t);
|
||||||
|
if (!token.isEmpty()) {
|
||||||
|
fieldParser.parse(obj, token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
} catch (Exception e) {
|
||||||
|
Grasscutter.getLogger().warn("Error deserializing an instance of class "+classType.getCanonicalName());
|
||||||
|
Grasscutter.getLogger().warn("At token #"+t+" of #"+m);
|
||||||
|
Grasscutter.getLogger().warn("Header names are: "+headerNames.toString());
|
||||||
|
Grasscutter.getLogger().warn("Tokens are: "+tokens.toString());
|
||||||
|
Grasscutter.getLogger().warn("Stacktrace is: ", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}).toList();
|
||||||
|
} catch (NoSuchFileException e) {
|
||||||
|
Grasscutter.getLogger().error("Error loading file '"+filename+"' - File does not exist. You are missing resources. Note that this file may exist in JSON, TSV, or TSJ format, any of which are suitable.");
|
||||||
|
return null;
|
||||||
|
} catch (IOException e) {
|
||||||
|
Grasscutter.getLogger().error("Error loading file '"+filename+"' - Stacktrace is: ", e);
|
||||||
|
return null;
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
Grasscutter.getLogger().error("Error loading file '"+filename+"' - Class is missing NoArgsConstructor");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------
|
||||||
|
// Everything below here is for the AllArgsConstructor TSJ parser
|
||||||
|
// -----------------------------------------------------------------
|
||||||
|
// Sadly, this is a little bit slower than the SetField version.
|
||||||
|
// I've left it in as an example of an optimization attempt that didn't work out, since the naive reflection version will tempt people to try things like this.
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static <T> Pair<Constructor<T>, String[]> getAllArgsConstructor(Class<T> classType) {
|
||||||
|
for (var c : classType.getDeclaredConstructors()) {
|
||||||
|
val consParameters = (java.beans.ConstructorProperties) c.getAnnotation(java.beans.ConstructorProperties.class);
|
||||||
|
if (consParameters != null) {
|
||||||
|
return Pair.of((Constructor<T>) c, consParameters.value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static <T> List<List<T>> loadTsjsToListsConstructor(Class<T> classType, Path... filenames) throws Exception {
|
||||||
|
val pair = getAllArgsConstructor(classType);
|
||||||
|
if (pair == null) {
|
||||||
|
Grasscutter.getLogger().error("No AllArgsContructor found for class: "+classType);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
val constructor = pair.left();
|
||||||
|
val conArgNames = pair.right();
|
||||||
|
val numArgs = constructor.getParameterCount();
|
||||||
|
|
||||||
|
val argMap = new Object2IntArrayMap<String>();
|
||||||
|
for (int i = 0; i < conArgNames.length; i++) {
|
||||||
|
argMap.put(conArgNames[i], i);
|
||||||
|
}
|
||||||
|
|
||||||
|
val argTypes = new Type[numArgs]; // constructor.getParameterTypes() returns base types like java.util.List instead of java.util.List<java.lang.Integer>
|
||||||
|
for (Field field : classType.getDeclaredFields()) {
|
||||||
|
int index = argMap.getOrDefault(field.getName(), -1);
|
||||||
|
if (index < 0) continue;
|
||||||
|
|
||||||
|
argTypes[index] = field.getGenericType(); // returns specialized type info e.g. java.util.List<java.lang.Integer>
|
||||||
|
|
||||||
|
val a = field.getDeclaredAnnotation(SerializedName.class);
|
||||||
|
if (a != null) { // Handle SerializedNames and alternatives
|
||||||
|
argMap.put(a.value(), index);
|
||||||
|
for (val alt : a.alternate()) {
|
||||||
|
argMap.put(alt, index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
val argParsers = Stream.of(argTypes).map(TsvUtils::getTypeParser).toList();
|
||||||
|
|
||||||
|
val defaultArgs = new Object[numArgs];
|
||||||
|
for (int i = 0; i < numArgs; i++) {
|
||||||
|
defaultArgs[i] = defaultValues.get(argTypes[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Stream.of(filenames).parallel().map(filename -> {
|
||||||
|
try (val fileReader = Files.newBufferedReader(filename, StandardCharsets.UTF_8)) {
|
||||||
|
val headerNames = nonRegexSplit(fileReader.readLine(), '\t');
|
||||||
|
val columns = headerNames.size();
|
||||||
|
val argPositions = headerNames.stream().mapToInt(name -> argMap.getOrDefault(name, -1)).toArray();
|
||||||
|
|
||||||
|
return fileReader.lines().parallel().map(line -> {
|
||||||
|
val tokens = nonRegexSplit(line, '\t');
|
||||||
|
val args = defaultArgs.clone();
|
||||||
|
val m = Math.min(tokens.size(), columns);
|
||||||
|
int t = 0;
|
||||||
|
try {
|
||||||
|
for (t = 0; t < m; t++) {
|
||||||
|
val argIndex = argPositions[t];
|
||||||
|
if (argIndex < 0) continue;
|
||||||
|
|
||||||
|
String token = tokens.get(t);
|
||||||
|
if (!token.isEmpty()) {
|
||||||
|
args[argIndex] = argParsers.get(argIndex).apply(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (T) constructor.newInstance(args);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Grasscutter.getLogger().warn("Error deserializing an instance of class "+classType.getCanonicalName()+" : "+constructor.getName());
|
||||||
|
Grasscutter.getLogger().warn("At token #"+t+" of #"+m);
|
||||||
|
Grasscutter.getLogger().warn("Arg names are: "+Arrays.toString(conArgNames));
|
||||||
|
Grasscutter.getLogger().warn("Arg types are: "+Arrays.toString(argTypes));
|
||||||
|
Grasscutter.getLogger().warn("Default Args are: "+Arrays.toString(defaultArgs));
|
||||||
|
Grasscutter.getLogger().warn("Args are: "+Arrays.toString(args));
|
||||||
|
Grasscutter.getLogger().warn("Header names are: "+headerNames.toString());
|
||||||
|
Grasscutter.getLogger().warn("Header types are: "+IntStream.of(argPositions).mapToObj(i -> (i >= 0) ? argTypes[i] : null).toList());
|
||||||
|
Grasscutter.getLogger().warn("Tokens are: "+tokens.toString());
|
||||||
|
Grasscutter.getLogger().warn("Stacktrace is: ", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}).toList();
|
||||||
|
} catch (IOException e) {
|
||||||
|
Grasscutter.getLogger().error("Error loading file '"+filename+"' - Stacktrace is: ", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
}
|
@ -395,4 +395,22 @@ public final class Utils {
|
|||||||
public static <T> T drawRandomListElement(List<T> list) {
|
public static <T> T drawRandomListElement(List<T> list) {
|
||||||
return drawRandomListElement(list, null);
|
return drawRandomListElement(list, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Splits a string by a character, into a list
|
||||||
|
* @param input The string to split
|
||||||
|
* @param separator The character to use as the split points
|
||||||
|
* @return A list of all the substrings
|
||||||
|
*/
|
||||||
|
public static List<String> nonRegexSplit(String input, int separator) {
|
||||||
|
var output = new ArrayList<String>();
|
||||||
|
int start = 0;
|
||||||
|
for (int next = input.indexOf(separator); next > 0; next = input.indexOf(separator, start)) {
|
||||||
|
output.add(input.substring(start, next));
|
||||||
|
start = next + 1;
|
||||||
|
}
|
||||||
|
if (start < input.length())
|
||||||
|
output.add(input.substring(start));
|
||||||
|
return output;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,83 +0,0 @@
|
|||||||
[
|
|
||||||
{
|
|
||||||
"comment": "Beginner's Banner. Do not change for no reason.",
|
|
||||||
"gachaType": 100,
|
|
||||||
"scheduleId": 803,
|
|
||||||
"bannerType": "EVENT",
|
|
||||||
"prefabPath": "GachaShowPanel_A016",
|
|
||||||
"titlePath": "UI_GACHA_SHOW_PANEL_A016_TITLE",
|
|
||||||
"costItemId": 224,
|
|
||||||
"costItemAmount10": 8,
|
|
||||||
"gachaTimesLimit": 20,
|
|
||||||
"beginTime": 0,
|
|
||||||
"endTime": 1924992000,
|
|
||||||
"sortId": 9999,
|
|
||||||
"rateUpItems5": [],
|
|
||||||
"rateUpItems4": [1034]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"comment": "Standard",
|
|
||||||
"gachaType": 200,
|
|
||||||
"scheduleId": 893,
|
|
||||||
"bannerType": "STANDARD",
|
|
||||||
"prefabPath": "GachaShowPanel_A022",
|
|
||||||
"titlePath": "UI_GACHA_SHOW_PANEL_A022_TITLE",
|
|
||||||
"costItemId": 224,
|
|
||||||
"beginTime": 0,
|
|
||||||
"endTime": 1924992000,
|
|
||||||
"sortId": 1000,
|
|
||||||
"fallbackItems4Pool1": [1006, 1014, 1015, 1020, 1021, 1023, 1024, 1025, 1027, 1031, 1032, 1034, 1036, 1039, 1043, 1044, 1045, 1048, 1053, 1055, 1056, 1064],
|
|
||||||
"weights5": [[1,75], [73,150], [90,10000]]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"comment": "Character Event Banner 1",
|
|
||||||
"gachaType": 301,
|
|
||||||
"scheduleId": 903,
|
|
||||||
"bannerType": "EVENT",
|
|
||||||
"prefabPath": "GachaShowPanel_A103",
|
|
||||||
"titlePath": "UI_GACHA_SHOW_PANEL_A081_TITLE",
|
|
||||||
"costItemId": 223,
|
|
||||||
"beginTime": 0,
|
|
||||||
"endTime": 1924992000,
|
|
||||||
"sortId": 9998,
|
|
||||||
"rateUpItems4": [1032, 1020, 1034],
|
|
||||||
"rateUpItems5": [1073],
|
|
||||||
"fallbackItems5Pool2": [],
|
|
||||||
"weights5": [[1,80], [73,80], [90,10000]]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"comment": "Character Event Banner 2",
|
|
||||||
"gachaType": 400,
|
|
||||||
"scheduleId": 923,
|
|
||||||
"bannerType": "EVENT",
|
|
||||||
"prefabPath": "GachaShowPanel_A104",
|
|
||||||
"titlePath": "UI_GACHA_SHOW_PANEL_A049_TITLE",
|
|
||||||
"costItemId": 223,
|
|
||||||
"beginTime": 0,
|
|
||||||
"endTime": 1924992000,
|
|
||||||
"sortId": 9998,
|
|
||||||
"rateUpItems4": [1032, 1020, 1034],
|
|
||||||
"rateUpItems5": [1049],
|
|
||||||
"fallbackItems5Pool2": [],
|
|
||||||
"weights5": [[1,80], [73,80], [90,10000]]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"comment": "Weapon Event Banner",
|
|
||||||
"gachaType": 302,
|
|
||||||
"scheduleId": 913,
|
|
||||||
"bannerType": "WEAPON",
|
|
||||||
"prefabPath": "GachaShowPanel_A105",
|
|
||||||
"titlePath": "UI_GACHA_SHOW_PANEL_A021_TITLE",
|
|
||||||
"costItemId": 223,
|
|
||||||
"beginTime": 0,
|
|
||||||
"endTime": 1924992000,
|
|
||||||
"sortId": 9997,
|
|
||||||
"rateUpItems4":[15405, 11402, 13407, 14402, 12403],
|
|
||||||
"rateUpItems5": [14511, 15509],
|
|
||||||
"fallbackItems5Pool1": [],
|
|
||||||
"weights4": [[1,600], [7,600], [8,6600], [10,12600]],
|
|
||||||
"weights5": [[1,100], [62,100], [73,7800], [80,10000]],
|
|
||||||
"eventChance4": 75,
|
|
||||||
"eventChance5": 75
|
|
||||||
}
|
|
||||||
]
|
|
94
src/main/resources/defaults/data/Banners.tsj
Normal file
94
src/main/resources/defaults/data/Banners.tsj
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
comment bannerType prefabPath titlePath rateUpItems5 rateUpItems4 disabled costItemId scheduleId sortId beginTime endTime weights5 weights4 fallbackItems5Pool2 fallbackItems5Pool1 fallbackItems4Pool1 eventChance4 eventChance5 costItemId10 costItemAmount10 gachaTimesLimit removeC6FromPool
|
||||||
|
Beginner's Banner. Do not change for no reason. BEGINNER GachaShowPanel_A016 UI_GACHA_SHOW_PANEL_A016_TITLE [1034] 224 803 9999 1924992000 8 20
|
||||||
|
Standard STANDARD GachaShowPanel_A022 UI_GACHA_SHOW_PANEL_A022_TITLE 224 893 1000 1924992000 [[1, 75], [73, 150], [90, 10000]] [1006, 1014, 1015, 1020, 1021, 1023, 1024, 1025, 1027, 1031, 1032, 1034, 1036, 1039, 1043, 1044, 1045, 1048, 1053, 1055, 1056, 1059, 1064, 1065, 1067, 1068, 1072]
|
||||||
|
Old Standard image STANDARD GachaShowPanel_A017 UI_GACHA_SHOW_PANEL_A017_TITLE true [1006, 1014, 1015, 1020, 1021, 1023, 1024, 1025, 1027, 1031, 1032, 1034, 1036, 1039, 1043, 1044, 1045, 1048, 1053, 1055, 1056, 1059, 1064, 1065, 1067, 1068, 1072]
|
||||||
|
1.0a character CHARACTER GachaShowPanel_A019 UI_GACHA_SHOW_PANEL_A019_TITLE [1022] [1014, 1031, 1023] true 223 1924992000 [[1, 80], [73, 80], [90, 10000]] []
|
||||||
|
1.0a weapon WEAPON GachaShowPanel_A020 UI_GACHA_SHOW_PANEL_A020_TITLE [11501, 15502] [11402, 12402, 14402, 15402, 13407] true 223 1924992000 [[1, 100], [62, 100], [73, 7800], [80, 10000]] [[1, 600], [7, 600], [8, 6600], [10, 12600]] [] 75 75
|
||||||
|
1.0b character CHARACTER GachaShowPanel_A018 UI_GACHA_SHOW_PANEL_A018_TITLE [1029] [1025, 1034, 1043] true
|
||||||
|
1.0b weapon WEAPON GachaShowPanel_A021 UI_GACHA_SHOW_PANEL_A021_TITLE [14502, 12502] [11403, 15403, 12403, 14403, 13401] true
|
||||||
|
1.1a character CHARACTER GachaShowPanel_A023 UI_GACHA_SHOW_PANEL_A023_TITLE [1033] [1039, 1024, 1027] true
|
||||||
|
1.1a weapon WEAPON GachaShowPanel_A025 UI_GACHA_SHOW_PANEL_A025_TITLE [14504, 15501] [11402, 12405, 14409, 15405, 13407] true
|
||||||
|
1.1b character CHARACTER GachaShowPanel_A024 UI_GACHA_SHOW_PANEL_A024_TITLE [1030] [1044, 1020, 1036] true
|
||||||
|
1.1b weapon WEAPON GachaShowPanel_A026 UI_GACHA_SHOW_PANEL_A026_TITLE [13504, 12504] [11405, 12402, 14401, 15401, 13401] true
|
||||||
|
1.2a character CHARACTER GachaShowPanel_A027 UI_GACHA_SHOW_PANEL_A027_TITLE [1038] [1031, 1043, 1032] true
|
||||||
|
1.2a weapon WEAPON GachaShowPanel_A029 UI_GACHA_SHOW_PANEL_A029_TITLE [11504, 14501] [11401, 12401, 13407, 14403, 15402] true
|
||||||
|
1.2b character CHARACTER GachaShowPanel_A028 UI_GACHA_SHOW_PANEL_A028_TITLE [1037] [1023, 1025, 1034] true
|
||||||
|
1.2b weapon WEAPON GachaShowPanel_A030 UI_GACHA_SHOW_PANEL_A030_TITLE [12501, 15502] [11403, 12402, 13401, 14409, 15401] true
|
||||||
|
1.3a character CHARACTER GachaShowPanel_A031 UI_GACHA_SHOW_PANEL_A031_TITLE [1026] [1039, 1024, 1044] true
|
||||||
|
1.3a weapon WEAPON GachaShowPanel_A034 UI_GACHA_SHOW_PANEL_A034_TITLE [11505, 13505] [11402, 12403, 15405, 14409, 13407] true
|
||||||
|
1.3b character CHARACTER GachaShowPanel_A032 UI_GACHA_SHOW_PANEL_A032_TITLE [1042] [1027, 1032, 1014] true
|
||||||
|
1.3b weapon WEAPON GachaShowPanel_A035 UI_GACHA_SHOW_PANEL_A035_TITLE [13501, 12502] [12410, 13406, 11405, 15403, 14402] true
|
||||||
|
1.3c character CHARACTER GachaShowPanel_A033 UI_GACHA_SHOW_PANEL_A033_TITLE [1046] [1025, 1023, 1036] true
|
||||||
|
1.4a character CHARACTER GachaShowPanel_A036 UI_GACHA_SHOW_PANEL_A036_TITLE [1022] [1043, 1020, 1034] true
|
||||||
|
1.4a weapon WEAPON GachaShowPanel_A038 UI_GACHA_SHOW_PANEL_A038_TITLE [15503, 11502] [11410, 14410, 12401, 15401, 13401] true
|
||||||
|
1.4b character CHARACTER GachaShowPanel_A037 UI_GACHA_SHOW_PANEL_A037_TITLE [1033] [1045, 1014, 1031] true
|
||||||
|
1.4b weapon WEAPON GachaShowPanel_A039 UI_GACHA_SHOW_PANEL_A039_TITLE [15501, 14502] [15410, 11401, 12403, 14401, 13407] true
|
||||||
|
1.5a character CHARACTER GachaShowPanel_A040 UI_GACHA_SHOW_PANEL_A040_TITLE [1030] [1048, 1034, 1039] true
|
||||||
|
1.5a weapon WEAPON GachaShowPanel_A042 UI_GACHA_SHOW_PANEL_A042_TITLE [11504, 14504] [12410, 13406, 11402, 14409, 15403] true
|
||||||
|
1.5b character CHARACTER GachaShowPanel_A041 UI_GACHA_SHOW_PANEL_A041_TITLE [1051] [1044, 1025, 1024] true
|
||||||
|
1.5b weapon WEAPON GachaShowPanel_A043 UI_GACHA_SHOW_PANEL_A043_TITLE [12503, 11501] [11403, 12405, 13401, 14403, 15405] true
|
||||||
|
1.6a character CHARACTER GachaShowPanel_A044 UI_GACHA_SHOW_PANEL_A018_TITLE [1029] [1014, 1043, 1031] true
|
||||||
|
1.6a weapon WEAPON GachaShowPanel_A046 UI_GACHA_SHOW_PANEL_A021_TITLE [12501, 14502] [15412, 11405, 12402, 13407, 14402] true
|
||||||
|
1.6b character CHARACTER GachaShowPanel_A045 UI_GACHA_SHOW_PANEL_A045_TITLE [1047] [1045, 1032, 1020] true
|
||||||
|
1.6b weapon WEAPON GachaShowPanel_A047 UI_GACHA_SHOW_PANEL_A021_TITLE [11503, 14501] [11410, 14410, 15410, 13401, 12401] true
|
||||||
|
2.0a character CHARACTER GachaShowPanel_A048 UI_GACHA_SHOW_PANEL_A048_TITLE [1002] [1027, 1036, 1048] true
|
||||||
|
2.0a weapon WEAPON GachaShowPanel_A050 UI_GACHA_SHOW_PANEL_A021_TITLE [11509, 13502] [12403, 13407, 14401, 11401, 15402] true
|
||||||
|
2.0b character CHARACTER GachaShowPanel_A049 UI_GACHA_SHOW_PANEL_A049_TITLE [1049] [1053, 1039, 1044] true
|
||||||
|
2.0b weapon WEAPON GachaShowPanel_A051 UI_GACHA_SHOW_PANEL_A021_TITLE [15509, 11502] [11403, 12405, 13401, 14403, 15401] true
|
||||||
|
2.1a character CHARACTER GachaShowPanel_A052 UI_GACHA_SHOW_PANEL_A052_TITLE [1052] [1056, 1023, 1043] true
|
||||||
|
2.1a weapon WEAPON GachaShowPanel_A054 UI_GACHA_SHOW_PANEL_A021_TITLE [13509, 12504] [11405, 12402, 13407, 14402, 15403] true
|
||||||
|
2.1b character CHARACTER GachaShowPanel_A053 UI_GACHA_SHOW_PANEL_A053_TITLE [1054] [1045, 1024, 1025] true
|
||||||
|
2.1b weapon WEAPON GachaShowPanel_A055 UI_GACHA_SHOW_PANEL_A021_TITLE [14506, 11505] [11402, 12401, 13401, 14401, 15402] true
|
||||||
|
2.2a character CHARACTER GachaShowPanel_A056 UI_GACHA_SHOW_PANEL_A037_TITLE [1033] [1027, 1036, 1048] true
|
||||||
|
2.2a weapon WEAPON GachaShowPanel_A058 UI_GACHA_SHOW_PANEL_A021_TITLE [15507, 14504] [12416, 11401, 13407, 14409, 15405] true
|
||||||
|
2.2b character CHARACTER GachaShowPanel_A057 UI_GACHA_SHOW_PANEL_A033_TITLE [1046] [1050, 1039, 1053] true
|
||||||
|
2.2b weapon WEAPON GachaShowPanel_A059 UI_GACHA_SHOW_PANEL_A021_TITLE [13501, 15503] [13416, 15416, 11403, 12405, 14402] true
|
||||||
|
2.3a character CHARACTER GachaShowPanel_A060 UI_GACHA_SHOW_PANEL_A027_TITLE [1038] [1032, 1034, 1045] true
|
||||||
|
2.3a character2 CHARACTER2 GachaShowPanel_A062 UI_GACHA_SHOW_PANEL_A041_TITLE [1051] [1032, 1034, 1045] true
|
||||||
|
2.3a weapon WEAPON GachaShowPanel_A063 UI_GACHA_SHOW_PANEL_A021_TITLE [11503, 12503] [14410, 15410, 11405, 12403, 13401] true
|
||||||
|
2.3b character CHARACTER GachaShowPanel_A061 UI_GACHA_SHOW_PANEL_A061_TITLE [1057] [1055, 1014, 1023] true
|
||||||
|
2.3b weapon WEAPON GachaShowPanel_A064 UI_GACHA_SHOW_PANEL_A021_TITLE [12510, 15501] [11410, 15412, 12402, 13407, 14403] true
|
||||||
|
2.4a character CHARACTER GachaShowPanel_A065 UI_GACHA_SHOW_PANEL_A065_TITLE [1063] [1064, 1027, 1036] true
|
||||||
|
2.4a character2 CHARACTER2 GachaShowPanel_A066 UI_GACHA_SHOW_PANEL_A031_TITLE [1026] [1064, 1027, 1036] true
|
||||||
|
2.4a weapon WEAPON GachaShowPanel_A067 UI_GACHA_SHOW_PANEL_A021_TITLE [13507, 13505] [13406, 11402, 12401, 14402, 15401] true
|
||||||
|
2.4b character CHARACTER GachaShowPanel_A068 UI_GACHA_SHOW_PANEL_A040_TITLE [1030] [1025, 1024, 1048] true
|
||||||
|
2.4b character2 CHARACTER2 GachaShowPanel_A069 UI_GACHA_SHOW_PANEL_A028_TITLE [1037] [1025, 1024, 1048] true
|
||||||
|
2.4b weapon WEAPON GachaShowPanel_A070 UI_GACHA_SHOW_PANEL_A021_TITLE [13504, 15502] [12410, 11401, 13401, 14401, 15403] true
|
||||||
|
2.5a character CHARACTER GachaShowPanel_A071 UI_GACHA_SHOW_PANEL_A071_TITLE [1058] [1031, 1039, 1050] true
|
||||||
|
2.5a weapon WEAPON GachaShowPanel_A072 UI_GACHA_SHOW_PANEL_A021_TITLE [14509, 11505] [13416, 11403, 12405, 14409, 15402] true
|
||||||
|
2.5b character CHARACTER GachaShowPanel_A073 UI_GACHA_SHOW_PANEL_A052_TITLE [1052] [1032, 1044, 1056] true
|
||||||
|
2.5b character2 CHARACTER2 GachaShowPanel_A074 UI_GACHA_SHOW_PANEL_A053_TITLE [1054] [1032, 1044, 1056] true
|
||||||
|
2.5b weapon WEAPON GachaShowPanel_A075 UI_GACHA_SHOW_PANEL_A021_TITLE [13509, 14506] [12416, 15416, 11405, 13407, 14403] true
|
||||||
|
2.6a character CHARACTER GachaShowPanel_A076 UI_GACHA_SHOW_PANEL_A076_TITLE [1066] [1043, 1023, 1064] true
|
||||||
|
2.6a character2 CHARACTER2 GachaShowPanel_A077 UI_GACHA_SHOW_PANEL_A036_TITLE [1022] [1043, 1023, 1064] true
|
||||||
|
2.6a weapon WEAPON GachaShowPanel_A078 UI_GACHA_SHOW_PANEL_A021_TITLE [11510, 15503] [11402, 12403, 13401, 14402, 15405] true
|
||||||
|
2.6b character CHARACTER GachaShowPanel_A079 UI_GACHA_SHOW_PANEL_A048_TITLE [1002] [1020, 1045, 1053] true
|
||||||
|
2.6b weapon WEAPON GachaShowPanel_A080 UI_GACHA_SHOW_PANEL_A021_TITLE [11509, 12504] [11401, 12402, 13407, 14401, 15401] true
|
||||||
|
2.7a character CHARACTER GachaShowPanel_A081 UI_GACHA_SHOW_PANEL_A081_TITLE [1060] [1014, 1048, 1034] true
|
||||||
|
2.7a character2 CHARACTER2 GachaShowPanel_A082 UI_GACHA_SHOW_PANEL_A031_TITLE [1026] [1014, 1048, 1034] true
|
||||||
|
2.7a weapon WEAPON GachaShowPanel_A083 UI_GACHA_SHOW_PANEL_A021_TITLE [15508, 13505] [13406, 14409, 12401, 15403, 11403] true
|
||||||
|
2.7b character CHARACTER GachaShowPanel_A084 UI_GACHA_SHOW_PANEL_A061_TITLE [1057] [1065, 1036, 1055] true
|
||||||
|
2.7b weapon WEAPON GachaShowPanel_A085 UI_GACHA_SHOW_PANEL_A021_TITLE [12510, 14504] [12410, 11405, 13401, 14403, 15402] true
|
||||||
|
2.8a character CHARACTER GachaShowPanel_A086 UI_GACHA_SHOW_PANEL_A045_TITLE [1047] [1059, 1027, 1050] true
|
||||||
|
2.8a character2 CHARACTER2 GachaShowPanel_A087 UI_GACHA_SHOW_PANEL_A018_TITLE [1029] [1059, 1027, 1050] true
|
||||||
|
2.8a weapon WEAPON GachaShowPanel_A088 UI_GACHA_SHOW_PANEL_A021_TITLE [11503, 14502] [11410, 15412, 12405, 13407, 14402] true
|
||||||
|
2.8b character CHARACTER GachaShowPanel_A089 UI_GACHA_SHOW_PANEL_A049_TITLE [1049] [1032, 1044, 1064] true
|
||||||
|
2.8b weapon WEAPON GachaShowPanel_A090 UI_GACHA_SHOW_PANEL_A021_TITLE [15509, 11504] [14410, 15410, 11402, 12403, 13401] true
|
||||||
|
3.0a character CHARACTER GachaShowPanel_A091 UI_GACHA_SHOW_PANEL_A091_TITLE [1069] [1067, 1039, 1031] true
|
||||||
|
3.0a character2 CHARACTER2 GachaShowPanel_A092 UI_GACHA_SHOW_PANEL_A040_TITLE [1030] [1067, 1039, 1031] true
|
||||||
|
3.0a weapon WEAPON GachaShowPanel_A093 UI_GACHA_SHOW_PANEL_A021_TITLE [15511, 13504] [11401, 12402, 13407, 14401, 15402] true
|
||||||
|
3.0b character CHARACTER GachaShowPanel_A094 UI_GACHA_SHOW_PANEL_A028_TITLE [1037] [1068, 1043, 1025] true
|
||||||
|
3.0b character2 CHARACTER2 GachaShowPanel_A095 UI_GACHA_SHOW_PANEL_A053_TITLE [1054] [1068, 1043, 1025] true
|
||||||
|
3.0b weapon WEAPON GachaShowPanel_A096 UI_GACHA_SHOW_PANEL_A021_TITLE [14506, 15502] [11403, 12401, 13401, 14409, 15405] true
|
||||||
|
3.1a character CHARACTER GachaShowPanel_A097 UI_GACHA_SHOW_PANEL_A097_TITLE [1071] [1072, 1065, 1053] true
|
||||||
|
3.1a character2 CHARACTER2 GachaShowPanel_A098 UI_GACHA_SHOW_PANEL_A036_TITLE [1022] [1072, 1065, 1053] true
|
||||||
|
3.1a weapon WEAPON GachaShowPanel_A099 UI_GACHA_SHOW_PANEL_A021_TITLE [13511, 15503] [12415, 11405, 13407, 14403, 15401] true
|
||||||
|
3.1b character CHARACTER GachaShowPanel_A100 UI_GACHA_SHOW_PANEL_A0100_TITLE [1070] [1014, 1024, 1023] true
|
||||||
|
3.1b character2 CHARACTER2 GachaShowPanel_A101 UI_GACHA_SHOW_PANEL_A027_TITLE [1038] [1014, 1024, 1023] true
|
||||||
|
3.1b weapon WEAPON GachaShowPanel_A102 UI_GACHA_SHOW_PANEL_A021_TITLE [11511, 11505] [11418, 14416, 12405, 13401, 15403] true
|
||||||
|
3.2a character CHARACTER GachaShowPanel_A103 UI_GACHA_SHOW_PANEL_A0103_TITLE [1073] [1020, 1034, 1032] true
|
||||||
|
3.2a character2 CHARACTER2 GachaShowPanel_A104 UI_GACHA_SHOW_PANEL_A049_TITLE [1049] [1020, 1034, 1032] true
|
||||||
|
3.2a weapon WEAPON GachaShowPanel_A105 UI_GACHA_SHOW_PANEL_A021_TITLE [14511, 15509] [11402, 12403, 13407, 14402, 15405] true
|
||||||
|
3.2b character CHARACTER GachaShowPanel_A106 UI_GACHA_SHOW_PANEL_A071_TITLE [1058] [1074, 1050, 1059]
|
||||||
|
3.2b character2 CHARACTER2 GachaShowPanel_A107 UI_GACHA_SHOW_PANEL_A037_TITLE [1033] [1074, 1050, 1059]
|
||||||
|
3.2b weapon WEAPON GachaShowPanel_A108 UI_GACHA_SHOW_PANEL_A021_TITLE [14509, 15507] [11401, 12402, 13401, 14401, 15402]
|
@ -6,7 +6,7 @@
|
|||||||
"connect": "Cliente conectado desde %s",
|
"connect": "Cliente conectado desde %s",
|
||||||
"disconnect": "Cliente desconectado desde %s",
|
"disconnect": "Cliente desconectado desde %s",
|
||||||
"game_update_error": "Ha ocurrido un error durante la actualización del juego.",
|
"game_update_error": "Ha ocurrido un error durante la actualización del juego.",
|
||||||
"command_error": "Comando de error:"
|
"command_error": "Error de comando:"
|
||||||
},
|
},
|
||||||
"dispatch": {
|
"dispatch": {
|
||||||
"address_bind": "[Dispatch] Servidor de envio iniciado en \u001b[1m\u001b[33m%s:%s\u001b[0m",
|
"address_bind": "[Dispatch] Servidor de envio iniciado en \u001b[1m\u001b[33m%s:%s\u001b[0m",
|
||||||
@ -19,10 +19,10 @@
|
|||||||
"default_password": "[Dispatch] La contraseña por defecto del keystore se cargó correctamente. Por favor, considera establecer la contraseña a 123456 en config.json."
|
"default_password": "[Dispatch] La contraseña por defecto del keystore se cargó correctamente. Por favor, considera establecer la contraseña a 123456 en config.json."
|
||||||
},
|
},
|
||||||
"authentication": {
|
"authentication": {
|
||||||
"default_unable_to_verify": "[Authentication] Algo invocó el método verifyUser que no está disponible en el manejador de autentificación por defecto."
|
"default_unable_to_verify": "[Authentication] Algo llamado método de verifyUser que no está disponible en el controlador de autenticación por defecto."
|
||||||
},
|
},
|
||||||
"no_commands_error": "No se soporta el uso de comandos en modo de solo envío.",
|
"no_commands_error": "No se soporta el uso de comandos en dispatch solo envío.",
|
||||||
"unhandled_request_error": "[Dispatch] Petición potencialmente no manejada %s : %s.",
|
"unhandled_request_error": "[Dispatch] Posible solicitud %s no gestionada: %s.",
|
||||||
"account": {
|
"account": {
|
||||||
"login_attempt": "[Dispatch] El cliente %s está intentando iniciar sesión.",
|
"login_attempt": "[Dispatch] El cliente %s está intentando iniciar sesión.",
|
||||||
"login_success": "[Dispatch] El cliente %s inició sesión como %s.",
|
"login_success": "[Dispatch] El cliente %s inició sesión como %s.",
|
||||||
@ -74,7 +74,7 @@
|
|||||||
"permission_error": "No tienes permiso para ejecutar este comando.",
|
"permission_error": "No tienes permiso para ejecutar este comando.",
|
||||||
"console_execute_error": "Este comando solo puede ser ejecutado desde la consola.",
|
"console_execute_error": "Este comando solo puede ser ejecutado desde la consola.",
|
||||||
"player_execute_error": "Ejecuta este comando desde el juego.",
|
"player_execute_error": "Ejecuta este comando desde el juego.",
|
||||||
"command_exist_error": "Ningún comando encontrado.",
|
"command_exist_error": "Comando no encontrado.",
|
||||||
"no_usage_specified": "No se ha especificado el uso",
|
"no_usage_specified": "No se ha especificado el uso",
|
||||||
"no_description_specified": "No se ha especificado la descripción",
|
"no_description_specified": "No se ha especificado la descripción",
|
||||||
"set_to": "Se ha establecido %s a %s.",
|
"set_to": "Se ha establecido %s a %s.",
|
||||||
@ -96,7 +96,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"execution": {
|
"execution": {
|
||||||
"usage_prefix": "Uso: ",
|
"usage_prefix": "Usa: ",
|
||||||
"player_exist_error": "Jugador no encontrado.",
|
"player_exist_error": "Jugador no encontrado.",
|
||||||
"player_offline_error": "El jugador no está activo.",
|
"player_offline_error": "El jugador no está activo.",
|
||||||
"item_player_exist_error": "Objeto o UID inválido.",
|
"item_player_exist_error": "Objeto o UID inválido.",
|
||||||
@ -106,9 +106,9 @@
|
|||||||
"set_target": "Los siguientes comandos irán solo a @%s por defecto.",
|
"set_target": "Los siguientes comandos irán solo a @%s por defecto.",
|
||||||
"set_target_online": "@%s está activo. Algunos comandos pueden requerir un usuario inactivo.",
|
"set_target_online": "@%s está activo. Algunos comandos pueden requerir un usuario inactivo.",
|
||||||
"set_target_offline": "@%s está inactivo. Algunos comandos pueden requerir un usuario activo.",
|
"set_target_offline": "@%s está inactivo. Algunos comandos pueden requerir un usuario activo.",
|
||||||
"need_target": "Este comando requiere un UID objetivo. Añade un argumento <@UID> o fija un objetivo persistente con /target @UID.",
|
"need_target": "Este comando requiere un UID fijado. Añade un argumento <@UID> o fija un objetivo persistente con /target @UID.",
|
||||||
"need_target_online": "Este comando requiere un UID objetivo activo, pero el objetivo actual está inactivo. Añade un argumento <@UID> diferente o fija un objetivo persistente con /target @UID.",
|
"need_target_online": "Este comando requiere un UID fijado activo, pero el objetivo actual está inactivo. Añade un argumento <@UID> diferente o fija un objetivo persistente con /target @UID.",
|
||||||
"need_target_offline": "Este comando requiere un UID objetivo inactivo, pero el objetivo actual está activo. Añade un argumento <@UID> diferente o fija un objetivo persistente con /target @UID."
|
"need_target_offline": "Este comando requiere un UID fijado inactivo, pero el objetivo actual está activo. Añade un argumento <@UID> diferente o fija un objetivo persistente con /target @UID."
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"enabled": "Activado",
|
"enabled": "Activado",
|
||||||
@ -122,11 +122,11 @@
|
|||||||
"create": "Cuenta creada con UID %s.",
|
"create": "Cuenta creada con UID %s.",
|
||||||
"delete": "Cuenta borrada.",
|
"delete": "Cuenta borrada.",
|
||||||
"no_account": "Cuenta no encontrada.",
|
"no_account": "Cuenta no encontrada.",
|
||||||
"description": "Modifica las cuentas de usuario"
|
"description": "Modifica las cuentas de usuarios"
|
||||||
},
|
},
|
||||||
"announce": {
|
"announce": {
|
||||||
"send_success": "Se envió el anuncio correctamente, puedes revocarlo con /a revoke %s.",
|
"send_success": "Se envió el anuncio correctamente, puedes revocarlo con /a revoke %s.",
|
||||||
"refresh_success": "Se refrescó el archivo de configuración de anuncio correctamente. [Total %s]",
|
"refresh_success": "Se actualizó el archivo de configuración de anuncio correctamente. [Total %s]",
|
||||||
"revoke_done": "Intentando revocar el anuncio %s.",
|
"revoke_done": "Intentando revocar el anuncio %s.",
|
||||||
"not_found": "No se pudo encontrar el anuncio %s.",
|
"not_found": "No se pudo encontrar el anuncio %s.",
|
||||||
"description": "Envía un anuncio a todos los jugadores activos, o gestiona anuncios del server"
|
"description": "Envía un anuncio a todos los jugadores activos, o gestiona anuncios del server"
|
||||||
@ -142,8 +142,8 @@
|
|||||||
"description": "Elimina objetos desbloqueados no equipados, incluyendo objetos de rareza dorada, de tu inventario"
|
"description": "Elimina objetos desbloqueados no equipados, incluyendo objetos de rareza dorada, de tu inventario"
|
||||||
},
|
},
|
||||||
"coop": {
|
"coop": {
|
||||||
"success": "Invocado %s al mundo de %s.",
|
"success": "%s se ha unido al mundo de %s.",
|
||||||
"description": "Fuerza a alguien a ser invocado al mundo de otro. Si no se establece un objetivo, te envía a ti al modo cooperativo."
|
"description": "Fuerza a alguien a unirse al mundo de otro. Si no se establece un objetivo, te envía a ti al modo cooperativo."
|
||||||
},
|
},
|
||||||
"enter_dungeon": {
|
"enter_dungeon": {
|
||||||
"changed": "Cambiado a la mazmorra %s.",
|
"changed": "Cambiado a la mazmorra %s.",
|
||||||
@ -152,7 +152,7 @@
|
|||||||
"description": "Te introduce en una mazmorra"
|
"description": "Te introduce en una mazmorra"
|
||||||
},
|
},
|
||||||
"give": {
|
"give": {
|
||||||
"usage_relic": "Uso: give <artifactID> [mainPropID] [<appendPropID>[,<veces>]]... [lv<nivel 0-20>]",
|
"usage_relic": "Usa: give <artifactID> [mainPropID] [<appendPropID>[,<veces>]]... [lv<nivel 0-20>]",
|
||||||
"illegal_relic": "Este artifactID pertenece a un rango de la lista negra, puede que no sea el que buscas.",
|
"illegal_relic": "Este artifactID pertenece a un rango de la lista negra, puede que no sea el que buscas.",
|
||||||
"given": "Dado %s de %s a %s.",
|
"given": "Dado %s de %s a %s.",
|
||||||
"given_with_level_and_refinement": "Dado %s con nivel %s, refinamiento %s %s veces a %s.",
|
"given_with_level_and_refinement": "Dado %s con nivel %s, refinamiento %s %s veces a %s.",
|
||||||
@ -177,7 +177,7 @@
|
|||||||
"kick": {
|
"kick": {
|
||||||
"player_kick_player": "El jugador [%s:%s] ha echado al jugador [%s:%s]",
|
"player_kick_player": "El jugador [%s:%s] ha echado al jugador [%s:%s]",
|
||||||
"server_kick_player": "Echando al jugador [%s:%s]...",
|
"server_kick_player": "Echando al jugador [%s:%s]...",
|
||||||
"description": "Echa al jugador específico del servidor (WIP)"
|
"description": "Echa a un jugador en específico del servidor (WIP)"
|
||||||
},
|
},
|
||||||
"killall": {
|
"killall": {
|
||||||
"scene_not_found_in_player_world": "Escenario no encontrado en el mundo del jugador.",
|
"scene_not_found_in_player_world": "Escenario no encontrado en el mundo del jugador.",
|
||||||
@ -229,10 +229,10 @@
|
|||||||
},
|
},
|
||||||
"resetShopLimit": {
|
"resetShopLimit": {
|
||||||
"success": "Reinicio completado.",
|
"success": "Reinicio completado.",
|
||||||
"description": "Reinicia el tiempo de refrescado de la tienda del jugador objetivo"
|
"description": "Reinicia el tiempo de la tienda del jugador fijado"
|
||||||
},
|
},
|
||||||
"sendMail": {
|
"sendMail": {
|
||||||
"give_usage": "Uso: give <player> <itemID|itemName> [cantidad] [nivel] [refinamiento]",
|
"give_usage": "Usa: give <player> <itemID|itemName> [cantidad] [nivel] [refinamiento]",
|
||||||
"user_not_exist": "El usuario con ID '%s' No existe.",
|
"user_not_exist": "El usuario con ID '%s' No existe.",
|
||||||
"start_composition": "Empezando la construcción del correo.\nPor favor usa '/sendmail <título>' para continuar.\nPuedes usar '/sendmail stop' en cualquier momento.",
|
"start_composition": "Empezando la construcción del correo.\nPor favor usa '/sendmail <título>' para continuar.\nPuedes usar '/sendmail stop' en cualquier momento.",
|
||||||
"templates": "Las plantillas de correos se implementarán pronto...",
|
"templates": "Las plantillas de correos se implementarán pronto...",
|
||||||
@ -264,17 +264,17 @@
|
|||||||
"fail": "Error al establecer la constelación.",
|
"fail": "Error al establecer la constelación.",
|
||||||
"failed_success": "Las constelaciones de %s han sido establecidas a %s. Por favor reinicia el escenario para ver los cambios.",
|
"failed_success": "Las constelaciones de %s han sido establecidas a %s. Por favor reinicia el escenario para ver los cambios.",
|
||||||
"success": "Las constelaciones de %s han sido establecidas a %s.",
|
"success": "Las constelaciones de %s han sido establecidas a %s.",
|
||||||
"successall": "🇺🇸Constellations for all characters have been set to %s.",
|
"successall": "Las constelaciones de todos los personajes han sido establecidas a %s.",
|
||||||
"description": "Establece el nivel de constelación para tu personaje actual"
|
"description": "Establece el nivel de constelación para tu personaje actual."
|
||||||
},
|
},
|
||||||
"setFetterLevel": {
|
"setFetterLevel": {
|
||||||
"range_error": "El nivel de amistad debe estar entre 0 y 10.",
|
"range_error": "El nivel de amistad debe estar entre 0 y 10.",
|
||||||
"success": "Nivel de amistad establecido a %s.",
|
"success": "Nivel de amistad establecido a %s.",
|
||||||
"level_error": "Nivel de amistad inválido.",
|
"level_error": "Nivel de amistad inválido.",
|
||||||
"description": "Establece tu nivel de amistad para tu personaje actual"
|
"description": "Establece tu nivel de amistad para tu personaje actual."
|
||||||
},
|
},
|
||||||
"setProp": {
|
"setProp": {
|
||||||
"description": "Establece propiedades de la cuenta. Cosas como el modo Dios pueden ser establecidos con este comando, además de cambiar cosas como desbloquear pisos del abusmo o progreso del pase de batalla.\n\tValores para <prop>: godmode | nostamina | unlimitedenergy | abyss | worldlevel | bplevel\n\t(cont.) Observa PlayerProperty enum para ver otros posibles valores, de la forma PROP_MAX_SPRING_VOLUME -> max_spring_volume"
|
"description": "Establece propiedades de la cuenta. Cosas como el modo Dios pueden ser establecidos con este comando, además de cambiar cosas como desbloquear pisos del abismo o progreso del pase de batalla.\n\tValores para <prop>: godmode | nostamina | unlimitedenergy | abyss | worldlevel | bplevel\n\t(cont.) Observa PlayerProperty enum para ver otros posibles valores, de la forma PROP_MAX_SPRING_VOLUME -> max_spring_volume"
|
||||||
},
|
},
|
||||||
"setStats": {
|
"setStats": {
|
||||||
"description": "Establece propiedades de combate para tu personaje actual\n\tValores para <estado>: hp | maxhp | def | atk | em | er | crate | cdmg | cdr | heal | heali | shield | defi\n\t(cont.) Bonus de daño elemental: epyro | ecryo | ehydro | egeo | edendro | eelectro | ephys\n\t(cont.) Resistencia elemental: respyro | rescryo | reshydro | resgeo | resdendro | reselectro | resphys",
|
"description": "Establece propiedades de combate para tu personaje actual\n\tValores para <estado>: hp | maxhp | def | atk | em | er | crate | cdmg | cdr | heal | heali | shield | defi\n\t(cont.) Bonus de daño elemental: epyro | ecryo | ehydro | egeo | edendro | eelectro | ephys\n\t(cont.) Resistencia elemental: respyro | rescryo | reshydro | resgeo | resdendro | reselectro | resphys",
|
||||||
@ -315,10 +315,10 @@
|
|||||||
"failed_parse_avatar_id": "Error al usar el ID del avatar: %s",
|
"failed_parse_avatar_id": "Error al usar el ID del avatar: %s",
|
||||||
"avatar_already_in_team": "El avatar ya está en el equipo.",
|
"avatar_already_in_team": "El avatar ya está en el equipo.",
|
||||||
"avatar_not_found": "Avatar %d no encontrado.",
|
"avatar_not_found": "Avatar %d no encontrado.",
|
||||||
"description": "Modifica tu equipo a mano."
|
"description": "Modifica tu equipo manualmente."
|
||||||
},
|
},
|
||||||
"teleportAll": {
|
"teleportAll": {
|
||||||
"success": "Invocados todos los jugadores a tu localización.",
|
"success": "Invoca a todos los jugadores a tu localización.",
|
||||||
"error": "Solo puedes usar este comando en modo MP.",
|
"error": "Solo puedes usar este comando en modo MP.",
|
||||||
"description": "Teletransporta a todos los jugadores en tu mundo a tu localización"
|
"description": "Teletransporta a todos los jugadores en tu mundo a tu localización"
|
||||||
},
|
},
|
||||||
@ -337,11 +337,11 @@
|
|||||||
"success": "Exitoso.",
|
"success": "Exitoso.",
|
||||||
"failure": "Error, jugador no encontrado.",
|
"failure": "Error, jugador no encontrado.",
|
||||||
"invalid_time": "No se puede establecer la marca de tiempo.",
|
"invalid_time": "No se puede establecer la marca de tiempo.",
|
||||||
"description": "Beta a un jugador"
|
"description": "Bannea a un jugador"
|
||||||
},
|
},
|
||||||
"unlockall": {
|
"unlockall": {
|
||||||
"success": "Desfijados todos los estados abiertos para %s.",
|
"success": "Desbloqueada todas las estadísticas y estados para %s.",
|
||||||
"description": "Desfija todos los estados abiertos para un jugador."
|
"description": "Desbloquea todas las estadísticas y estados para un jugador."
|
||||||
},
|
},
|
||||||
"unban": {
|
"unban": {
|
||||||
"success": "Exitoso.",
|
"success": "Exitoso.",
|
||||||
@ -364,23 +364,23 @@
|
|||||||
},
|
},
|
||||||
"documentation": {
|
"documentation": {
|
||||||
"handbook": {
|
"handbook": {
|
||||||
"title": "🇺🇸GM Handbook",
|
"title": "GM Handbook",
|
||||||
"title_commands": "Comandos",
|
"title_commands": "Comandos",
|
||||||
"title_avatars": "Avatares",
|
"title_avatars": "Avatares",
|
||||||
"title_items": "Objetos",
|
"title_items": "Objetos",
|
||||||
"title_scenes": "Escenario",
|
"title_scenes": "Escenario",
|
||||||
"title_monsters": "Monstruos",
|
"title_monsters": "Monstruos",
|
||||||
"header_id": "🇺🇸Id",
|
"header_id": "Id",
|
||||||
"header_command": "Comando",
|
"header_command": "Comando",
|
||||||
"header_description": "Descripción",
|
"header_description": "Descripción",
|
||||||
"header_avatar": "🇺🇸Avatar",
|
"header_avatar": "Avatar",
|
||||||
"header_item": "Objeto",
|
"header_item": "Objeto",
|
||||||
"header_scene": "Escenario",
|
"header_scene": "Escenario",
|
||||||
"header_monster": "Monstruo"
|
"header_monster": "Monstruo"
|
||||||
},
|
},
|
||||||
"index": {
|
"index": {
|
||||||
"title": "Documentación",
|
"title": "Documentación",
|
||||||
"handbook": "🇺🇸GM Handbook",
|
"handbook": "GM Handbook",
|
||||||
"gacha_mapping": "JSON de mapeo del Gacha"
|
"gacha_mapping": "JSON de mapeo del Gacha"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"messages": {
|
"messages": {
|
||||||
"game": {
|
"game": {
|
||||||
"address_bind": "🇺🇸Game Server started at \u001b[1m\u001b[33m%s:%s\u001b[0m",
|
"address_bind": "Serveur de jeu démarré a l'adresse \u001b[1m\u001b[33m%s:%s\u001b[0m",
|
||||||
"port_bind": "Serveur de jeu démarré sur le port %s",
|
"port_bind": "Serveur de jeu démarré sur le port %s",
|
||||||
"connect": "Client connecté depuis %s",
|
"connect": "Client connecté depuis %s",
|
||||||
"disconnect": "Client déconnecté depuis %s",
|
"disconnect": "Client déconnecté depuis %s",
|
||||||
@ -9,7 +9,7 @@
|
|||||||
"command_error": "Erreur de commande:"
|
"command_error": "Erreur de commande:"
|
||||||
},
|
},
|
||||||
"dispatch": {
|
"dispatch": {
|
||||||
"address_bind": "🇺🇸[Dispatch] Dispatch server started at \u001b[1m\u001b[33m%s:%s\u001b[0m",
|
"address_bind": "[Dispatch] Serveur de répartition \u001b[1m\u001b[33m%s:%s\u001b[0m",
|
||||||
"port_bind": "[Dispatch] Serveur de répartition démarré sur le port %s",
|
"port_bind": "[Dispatch] Serveur de répartition démarré sur le port %s",
|
||||||
"request": "[Dispatch] Le client %s %s demande : %s",
|
"request": "[Dispatch] Le client %s %s demande : %s",
|
||||||
"keystore": {
|
"keystore": {
|
||||||
@ -248,7 +248,7 @@
|
|||||||
"send": "%s %s (niveau %s) ont été ajouté au message.\nContinuez d'ajouter plus d'objets ou utilisez '/sendmail finish' pour envoyer le message.",
|
"send": "%s %s (niveau %s) ont été ajouté au message.\nContinuez d'ajouter plus d'objets ou utilisez '/sendmail finish' pour envoyer le message.",
|
||||||
"invalid_arguments_please_use": "Arguments invalides.\n Veuillez utiliser '/sendmail %s'",
|
"invalid_arguments_please_use": "Arguments invalides.\n Veuillez utiliser '/sendmail %s'",
|
||||||
"title": "<titre>",
|
"title": "<titre>",
|
||||||
"message": "🇺🇸<message>",
|
"message": "< message >",
|
||||||
"sender": "<expéditeur>",
|
"sender": "<expéditeur>",
|
||||||
"arguments": "<itemID|itemName|finish> [quantité] [niveau]",
|
"arguments": "<itemID|itemName|finish> [quantité] [niveau]",
|
||||||
"error": "ERREUR: Stade de construction invalide : %s. Vérifiez la console pour la pile d'appels.",
|
"error": "ERREUR: Stade de construction invalide : %s. Vérifiez la console pour la pile d'appels.",
|
||||||
@ -259,13 +259,13 @@
|
|||||||
"description": "Envoie un message au joueur spécifié en tant que Serveur"
|
"description": "Envoie un message au joueur spécifié en tant que Serveur"
|
||||||
},
|
},
|
||||||
"setConst": {
|
"setConst": {
|
||||||
"range_error": "🇺🇸Constellation level must be between 0 and 6.",
|
"range_error": "Le niveau de constellation doit être compris entre 1 et 6.",
|
||||||
"level_error": "🇺🇸Invalid constellation level.",
|
"level_error": "Niveau de constellation invalide",
|
||||||
"fail": "🇺🇸Failed to set constellation.",
|
"fail": "Impossible de définir le niveau de constellation",
|
||||||
"failed_success": "🇺🇸Constellations for %s have been set to %s. Please reload scene to see changes.",
|
"failed_success": "Les constellations de %s ont été défini à %s. Veuillez recharger la scène pour voir les changements",
|
||||||
"success": "🇺🇸Constellations for %s have been set to %s.",
|
"success": "Les constellations de %s ont été défini à %s.",
|
||||||
"successall": "🇺🇸Constellations for all characters have been set to %s.",
|
"successall": "Les constellations de tous vos personnages ont été défini à %s.",
|
||||||
"description": "🇺🇸Sets constellation level for your current active character"
|
"description": "Définit le niveau de constellation du personnage actif"
|
||||||
},
|
},
|
||||||
"setFetterLevel": {
|
"setFetterLevel": {
|
||||||
"range_error": "Le niveau d'affinité doit être compris entre 0 et 10.",
|
"range_error": "Le niveau d'affinité doit être compris entre 0 et 10.",
|
||||||
@ -278,10 +278,10 @@
|
|||||||
},
|
},
|
||||||
"setStats": {
|
"setStats": {
|
||||||
"description": "Définit les propriétés de combat de votre personnage actif\n\tValeurs pour <stat>: hp | maxhp | def | atk | em | er | crate | cdmg | cdr | heal | heali | shield | defi\n\t(cont.) Bonus de dégât élémentaire: epyro | ecryo | ehydro | egeo | edendro | eelectro | ephys\n\t(cont.) Résistance élémentaire: respyro | rescryo | reshydro | resgeo | resdendro | reselectro | resphys",
|
"description": "Définit les propriétés de combat de votre personnage actif\n\tValeurs pour <stat>: hp | maxhp | def | atk | em | er | crate | cdmg | cdr | heal | heali | shield | defi\n\t(cont.) Bonus de dégât élémentaire: epyro | ecryo | ehydro | egeo | edendro | eelectro | ephys\n\t(cont.) Résistance élémentaire: respyro | rescryo | reshydro | resgeo | resdendro | reselectro | resphys",
|
||||||
"locked_to": "🇺🇸%s locked to %s.",
|
"locked_to": "%s verrouillé à %s.",
|
||||||
"locked_for_to": "🇺🇸%s for %s locked to %s.",
|
"locked_for_to": "%s de %s verrouillé à %s.",
|
||||||
"unlocked": "🇺🇸%s unlocked.",
|
"unlocked": "%s déverrouillé.",
|
||||||
"unlocked_for": "🇺🇸%s for %s unlocked."
|
"unlocked_for": "%s de %s déverrouillé."
|
||||||
},
|
},
|
||||||
"spawn": {
|
"spawn": {
|
||||||
"success": " %s %s sont apparu.",
|
"success": " %s %s sont apparu.",
|
||||||
@ -293,9 +293,9 @@
|
|||||||
"description": "Arrête le serveur"
|
"description": "Arrête le serveur"
|
||||||
},
|
},
|
||||||
"talent": {
|
"talent": {
|
||||||
"out_of_range": "🇺🇸Invalid talent level. Level should be in range of 1-15.",
|
"out_of_range": "Niveau de talent invalide. Le niveau doit être compris entre 1-15.",
|
||||||
"set_id": "🇺🇸Set talent %s - \"%s\" to %s.",
|
"set_id": "Talent %s - \"%s\" défini à %s.",
|
||||||
"id_desc": "🇺🇸Talent %s - \"%s\" - \"%s\"",
|
"id_desc": "Talent %s : \"%s\" - \"%s\"",
|
||||||
"invalid_skill_id": "ID de talent invalide.",
|
"invalid_skill_id": "ID de talent invalide.",
|
||||||
"invalid_level": "Niveau de talent invalide.",
|
"invalid_level": "Niveau de talent invalide.",
|
||||||
"normal_attack_id": "ID de l'attaque normale ID %s.",
|
"normal_attack_id": "ID de l'attaque normale ID %s.",
|
||||||
@ -340,8 +340,8 @@
|
|||||||
"description": "Bannis un joueur"
|
"description": "Bannis un joueur"
|
||||||
},
|
},
|
||||||
"unlockall": {
|
"unlockall": {
|
||||||
"success": "🇺🇸Unlocked all open states for %s.",
|
"success": "Toutes les open states ont été débloqués pour %s.",
|
||||||
"description": "🇺🇸Unlocks all open states for a player."
|
"description": "Débloque toutes les open states d'un joueur"
|
||||||
},
|
},
|
||||||
"unban": {
|
"unban": {
|
||||||
"success": "Succès.",
|
"success": "Succès.",
|
||||||
@ -358,7 +358,7 @@
|
|||||||
},
|
},
|
||||||
"records": {
|
"records": {
|
||||||
"title": "Historique de voeux",
|
"title": "Historique de voeux",
|
||||||
"date": "🇺🇸Date",
|
"date": "Date.",
|
||||||
"item": "Objet"
|
"item": "Objet"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -366,38 +366,38 @@
|
|||||||
"handbook": {
|
"handbook": {
|
||||||
"title": "Manuel GM",
|
"title": "Manuel GM",
|
||||||
"title_commands": "Commandes",
|
"title_commands": "Commandes",
|
||||||
"title_avatars": "🇺🇸Avatars",
|
"title_avatars": "Avatars.",
|
||||||
"title_items": "Objets",
|
"title_items": "Objets",
|
||||||
"title_scenes": "Scènes",
|
"title_scenes": "Scènes",
|
||||||
"title_monsters": "Monstres",
|
"title_monsters": "Monstres",
|
||||||
"header_id": "🇺🇸Id",
|
"header_id": "Identifiant",
|
||||||
"header_command": "Commande",
|
"header_command": "Commande",
|
||||||
"header_description": "🇺🇸Description",
|
"header_description": "Description.",
|
||||||
"header_avatar": "🇺🇸Avatar",
|
"header_avatar": "Avatar.",
|
||||||
"header_item": "Objet",
|
"header_item": "Objet",
|
||||||
"header_scene": "Scène",
|
"header_scene": "Scène",
|
||||||
"header_monster": "Monstre"
|
"header_monster": "Monstre"
|
||||||
},
|
},
|
||||||
"index": {
|
"index": {
|
||||||
"title": "🇺🇸Documentation",
|
"title": "Documentation",
|
||||||
"handbook": "Manuel GM",
|
"handbook": "Manuel GM",
|
||||||
"gacha_mapping": "🇺🇸Gacha mapping JSON"
|
"gacha_mapping": "Gacha mapping JSON"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"plugin": {
|
"plugin": {
|
||||||
"directory_failed": "🇺🇸Failed to create plugins directory: ",
|
"directory_failed": "Impossible de créer le dossier plugins: ",
|
||||||
"unable_to_load": "🇺🇸Unable to load plugin.",
|
"unable_to_load": "Impossible de charger les plugins.",
|
||||||
"invalid_config": "🇺🇸Plugin %s has an invalid config file.",
|
"invalid_config": "Le plugin %s a un fichier de configuration invalide.",
|
||||||
"invalid_main_class": "🇺🇸Plugin %s has an invalid main class.",
|
"invalid_main_class": "Le plugin %s a une classe principale invalide.",
|
||||||
"missing_config": "🇺🇸Plugin %s lacks a valid config file.",
|
"missing_config": "Le plugin %s manque d'une configuration valide.",
|
||||||
"failed_to_load_plugin": "🇺🇸Failed to load plugin: %s",
|
"failed_to_load_plugin": "Impossible de charger le plugin %s",
|
||||||
"failed_to_load": "🇺🇸Failed to load a plugin.",
|
"failed_to_load": "Impossible de charger un plugin.",
|
||||||
"failed_to_load_dependencies": "🇺🇸Failed to load plugins with dependencies.",
|
"failed_to_load_dependencies": "Impossible de charger les plugins avec une ou des dépendances.",
|
||||||
"loading_plugin": "🇺🇸Loading plugin: %s",
|
"loading_plugin": "Chargement du plugin %s",
|
||||||
"failed_add_id": "🇺🇸Failed to add plugin identifier: %s",
|
"failed_add_id": "Impossible d'ajouter l'identifiant du plugin %s",
|
||||||
"enabling_plugin": "🇺🇸Enabling plugin: %s",
|
"enabling_plugin": "Activation du plugin %s",
|
||||||
"enabling_failed": "🇺🇸Failed to enable plugin: %s",
|
"enabling_failed": "Impossible d'activer le plugin %s",
|
||||||
"disabling_plugin": "🇺🇸Disabling plugin: %s",
|
"disabling_plugin": "Désactivation du plugin %s",
|
||||||
"disabling_failed": "🇺🇸Failed to disable plugin: %s"
|
"disabling_failed": "Impossible de désactiver le plugin %s"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
402
src/main/resources/languages/it-IT.json
Normal file
402
src/main/resources/languages/it-IT.json
Normal file
@ -0,0 +1,402 @@
|
|||||||
|
{
|
||||||
|
"messages": {
|
||||||
|
"game": {
|
||||||
|
"address_bind": "Server di gioco avviato su \u001B[1m\u001B[33m%s:%s\u001B[0m",
|
||||||
|
"port_bind": "Server di gioco avviato sulla porta %s",
|
||||||
|
"connect": "Client connesso da %s",
|
||||||
|
"disconnect": "Client disconnesso da %s",
|
||||||
|
"game_update_error": "Si è verificato un errore durante l'aggiornamento del gioco.",
|
||||||
|
"command_error": "Errore comando:"
|
||||||
|
},
|
||||||
|
"dispatch": {
|
||||||
|
"address_bind": "[Dispatch] Dispatch server avviato su \u001B[1m\u001B[33m%s:%s\u001B[0m",
|
||||||
|
"port_bind": "[Dispatch] Dispatch server avviato sulla porta %s",
|
||||||
|
"request": "[Dispatch] Client %s %s richiesta: %s",
|
||||||
|
"keystore": {
|
||||||
|
"general_error": "[Dispatch] Errore nel caricamento di keystore!",
|
||||||
|
"password_error": "[Dispatch] Impossibile caricare il keystore. Provando passwrd di default keystore...",
|
||||||
|
"no_keystore_error": "[Dispatch] Nessun cert SSL trovato! Ritorno ad un server HTTP.",
|
||||||
|
"default_password": "[Dispatch] La password di default del keystore è stata caricata con successo. Considera di impostare la password a 123456 in config.json."
|
||||||
|
},
|
||||||
|
"authentication": {
|
||||||
|
"default_unable_to_verify": "[Authentication] [Autenticazione] Qualcosa ha chiamato metodo VerifyUser che non è disponibile nel gestore di autenticazione predefinito."
|
||||||
|
},
|
||||||
|
"no_commands_error": "I comandi non sono supportati in modalità solo dispatch.",
|
||||||
|
"unhandled_request_error": "[Dispatch] Potenziali %s richieste non gestite: %s.",
|
||||||
|
"account": {
|
||||||
|
"login_attempt": "[Dispatch] Il client %s sta provando a fare il login.",
|
||||||
|
"login_success": "[Dispatch] Client %s loggato come %s.",
|
||||||
|
"login_max_player_limit": "[Dispatch] Client %s non è riuscito ad accedere: Il numero di giocatori online ha raggiunto il limite",
|
||||||
|
"login_token_attempt": "[Dispatch] Il client %s sta tentando di accedere tramite token.",
|
||||||
|
"login_token_error": "[Dispatch] Client %s non è riuscito ad accedere tramite token.",
|
||||||
|
"login_token_success": "[Dispatch] Client %s ha effettuato l'accesso tramite token come %s.",
|
||||||
|
"login_password_error": "[Dispatch] Client %s non è riuscito ad accedere tramite password.",
|
||||||
|
"login_password_storage_error": "[Dispatch] Client %s non è riuscito ad accedere tramite password perché non c'è password nel database.",
|
||||||
|
"combo_token_success": "[Dispatch] Il client %s è riuscito a scambiare il token combinato.",
|
||||||
|
"combo_token_error": "Il client [Dispatch] %s non è riuscito a scambiare il token combinato.",
|
||||||
|
"account_login_create_success": "[Dispatch] Client %s non è riuscito ad accedere: Account %s creato.",
|
||||||
|
"account_login_create_error": "[Dispatch] Client %s non è riuscito ad accedere: Creazione account non riuscita.",
|
||||||
|
"account_login_exist_error": "[Dispatch] Client %s non è riuscito ad accedere: Account non trovato.",
|
||||||
|
"account_cache_error": "Errore di informazioni sulla cache dell'account di gioco.",
|
||||||
|
"session_key_error": "Chiave di sessione errata.",
|
||||||
|
"username_error": "Nome utente non trovato.",
|
||||||
|
"username_create_error": "Nome utente non trovato, creazione non riuscita.",
|
||||||
|
"password_error": "Password non valida",
|
||||||
|
"password_length_error": "La lunghezza della password deve essere maggiore o uguale a 8",
|
||||||
|
"password_storage_error": "Non hai una password per il tuo account. Contatta un amministratore.",
|
||||||
|
"server_max_player_limit": "Il numero di giocatori online ha raggiunto il limite"
|
||||||
|
},
|
||||||
|
"router_error": "[Dispatch] Impossibile collegare il router."
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"free_software": "Grasscutter è un software GRATUITO. Se hai pagato per questo, potresti essere stato truffato. Homepage: https://github.com/Grasscutters/Grasscutter",
|
||||||
|
"starting": "Avvio di Grasscutter...",
|
||||||
|
"shutdown": "Chiusura in corso...",
|
||||||
|
"done": "Fatto! Per aiuto, digita \"help\"",
|
||||||
|
"error": "Si è verificato un errore.",
|
||||||
|
"welcome": "Benvenuto in Grasscutter!",
|
||||||
|
"run_mode_error": "Modalità di esecuzione del server non valida: %s.",
|
||||||
|
"run_mode_help": "La modalità di esecuzione del server deve essere 'HYBRID', 'DISPATCH_ONLY' o 'GAME_ONLY'. Impossibile avviare Grasscutter...",
|
||||||
|
"create_resources": "Creazione cartella risorse...",
|
||||||
|
"resources_error": "Inserisci una copia di 'BinOutput' e 'ExcelBinOutput' nella cartella delle risorse.",
|
||||||
|
"version": "Versione Grassscutter: %s-%s",
|
||||||
|
"game_version": "Versione del gioco: %s",
|
||||||
|
"resources": {
|
||||||
|
"loading": "Caricamento risorse...",
|
||||||
|
"finish": "Terminato il caricamento delle risorse."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"commands": {
|
||||||
|
"generic": {
|
||||||
|
"not_specified": "Nessun comando specificato.",
|
||||||
|
"unknown_command": "Comando sconosciuto: %s",
|
||||||
|
"permission_error": "Non hai il permesso per eseguire questo comando.",
|
||||||
|
"console_execute_error": "Questo comando può essere eseguito solo dalla console.",
|
||||||
|
"player_execute_error": "Esegui questo comando nel gioco.",
|
||||||
|
"command_exist_error": "Nessun comando trovato.",
|
||||||
|
"no_usage_specified": "Nessun utilizzo specificato",
|
||||||
|
"no_description_specified": "Nessuna descrizione specificata",
|
||||||
|
"set_to": "%s impostato su %s.",
|
||||||
|
"set_for_to": "%s per %s impostato su %s.",
|
||||||
|
"invalid": {
|
||||||
|
"amount": "Importo non valido.",
|
||||||
|
"artifactId": "ID artefatto non valido.",
|
||||||
|
"avatarId": "ID avatar non valido.",
|
||||||
|
"avatarLevel": "Livello avatar non valido.",
|
||||||
|
"entityId": "ID entità non valido.",
|
||||||
|
"itemId": "ID articolo non valido.",
|
||||||
|
"itemLevel": "ItemLevel non valido.",
|
||||||
|
"itemRefinement": "Raffinamento articolo non valido.",
|
||||||
|
"statValue": "Valore statistica non valido.",
|
||||||
|
"value_between": "Valore non valido: %s deve essere compreso tra %s e %s.",
|
||||||
|
"playerId": "ID giocatore non valido.",
|
||||||
|
"uid": "UID non valido.",
|
||||||
|
"id": "ID non valido."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"execution": {
|
||||||
|
"usage_prefix": "Utilizzo: ",
|
||||||
|
"player_exist_error": "Giocatore non trovato.",
|
||||||
|
"player_offline_error": "Il giocatore non è online.",
|
||||||
|
"item_player_exist_error": "Elemento o UID non valido.",
|
||||||
|
"player_exist_offline_error": "Il giocatore non è stato trovato o non è online.",
|
||||||
|
"argument_error": "Argomenti non validi.",
|
||||||
|
"clear_target": "Obiettivo cancellato.",
|
||||||
|
"set_target": "I comandi successivi avranno come bersaglio @%s per impostazione predefinita.",
|
||||||
|
"set_target_online": "@%s è online. Alcuni comandi potrebbero richiedere un bersaglio offline.",
|
||||||
|
"set_target_offline": "@%s è offline. Alcuni comandi potrebbero richiedere un bersaglio online.",
|
||||||
|
"need_target": "Questo comando richiede un UID bersaglio. Aggiungi un argomento <@UID> o imposta un bersaglio persistente con /target @UID.",
|
||||||
|
"need_target_online": "Questo comando richiede un UID bersaglio online, ma il bersaglio corrente è offline. Aggiungi un argomento <@UID> diverso o imposta un target persistente con /target @UID.",
|
||||||
|
"need_target_offline": "Questo comando richiede un UID bersaglio offline, ma il bersaglio corrente è online. Aggiungi un argomento <@UID> diverso o imposta un target persistente con /target @UID."
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"enabled": "Abilitato",
|
||||||
|
"disabled": "Disabilitato",
|
||||||
|
"help": "Aiuto",
|
||||||
|
"success": "Successo"
|
||||||
|
},
|
||||||
|
"account": {
|
||||||
|
"invalid": "UID non valido.",
|
||||||
|
"exists": "Esiste già un account con questo nome utente e/o UID.",
|
||||||
|
"create": "Account creato con UID %s.",
|
||||||
|
"delete": "Account cancellato.",
|
||||||
|
"no_account": "Account non trovato.",
|
||||||
|
"description": "Modifica account utente"
|
||||||
|
},
|
||||||
|
"announce": {
|
||||||
|
"send_success": "Invia un annuncio con successo, puoi revocarlo /a revoca %s.",
|
||||||
|
"refresh_success": "Aggiorna il file di configurazione dell'annuncio con successo. [Totale %s]",
|
||||||
|
"revoke_done": "Prova a revocare l'annuncio %s.",
|
||||||
|
"not_found": "Impossibile trovare l'annuncio %s.",
|
||||||
|
"description": "Invia annuncio a tutti i giocatori online o gestisci l'annuncio del server"
|
||||||
|
},
|
||||||
|
"clear": {
|
||||||
|
"weapons": "Armi cancellate per %s.",
|
||||||
|
"artifacts": "Ripuliti artefatti per %s.",
|
||||||
|
"materials": "Materiali cancellati per %s.",
|
||||||
|
"furniture": "Mobili cancellati per %s.",
|
||||||
|
"displays": "Display cancellati per %s.",
|
||||||
|
"virtuals": "Virtuali cancellate per %s.",
|
||||||
|
"everything": "Cancellato tutto per %s.",
|
||||||
|
"description": "Elimina gli oggetti sbloccati non equipaggiati dal tuo inventario. Il valore predefinito è 4* livello 1 raffinamento 1 o inferiore, ma può essere impostato su un livello superiore." },
|
||||||
|
"coop": {
|
||||||
|
"success": "Convocato %s nel mondo di %s.",
|
||||||
|
"description": "Forza qualcuno a unirsi al mondo degli altri. Se nessuno viene preso di mira, ti manda comunque in modalità cooperativa."
|
||||||
|
},
|
||||||
|
"enter_dungeon": {
|
||||||
|
"changed": "Cambiato nel dungeon %s.",
|
||||||
|
"not_found_error": "Il dungeon non esiste.",
|
||||||
|
"in_dungeon_error": "Sei già in quel dungeon.",
|
||||||
|
"description": "Entra in un dungeon"
|
||||||
|
},
|
||||||
|
"give": {
|
||||||
|
"usage_relic": "Utilizzo: fornire <artifactID> [mainPropID] [<appendPropID>[,<times>]]... [lv<livello 0-20>]",
|
||||||
|
"illegal_relic": "Questo ID artefatto appartiene a un intervallo nella blacklist, potrebbe non essere quello che volevi.",
|
||||||
|
"given": "Dato %s di %s a %s.",
|
||||||
|
"given_with_level_and_refinement": "Dato %s con livello %s, perfezionamento %s %s volte a %s.",
|
||||||
|
"given_level": "Dato %s con livello %s %s volte a %s.",
|
||||||
|
"given_avatar": "Dato %s con livello da %s a %s.",
|
||||||
|
"giveall_success": "Ha dato tutti gli oggetti con successo.",
|
||||||
|
"description": "Dà un oggetto a te o al giocatore specificato. Può anche dare tutte le armi, avatar e/o materiali, e può costruire artefatti personalizzati."
|
||||||
|
},
|
||||||
|
"heal": {
|
||||||
|
"success": "Tutti i personaggi sono stati curati.",
|
||||||
|
"description": "Guarisci tutti i personaggi della tua squadra attuale."
|
||||||
|
},
|
||||||
|
"help": {
|
||||||
|
"aliases": "Alias: ",
|
||||||
|
"available_commands": "Comandi disponibili: ",
|
||||||
|
"tip_need_permission": "Autorizzazione: ",
|
||||||
|
"tip_need_no_permission": "Nessun Elemento",
|
||||||
|
"tip_permission_targeted": " (L'autorizzazione %s è richiesta anche per l'uso su altri giocatori)",
|
||||||
|
"warn_player_has_no_permission": "Avviso: non hai il permesso per eseguire questo comando.",
|
||||||
|
"description": "Invia il messaggio di aiuto o mostra le informazioni su un comando specificato"
|
||||||
|
},
|
||||||
|
"kick": {
|
||||||
|
"player_kick_player": "Il giocatore [%s:%s] ha espulso un giocatore [%s:%s]",
|
||||||
|
"server_kick_player": "Ha preso a calci il giocatore [%s:%s]...",
|
||||||
|
"description": "Espelle il giocatore specificato dal server (WIP)"
|
||||||
|
},
|
||||||
|
"killall": {
|
||||||
|
"scene_not_found_in_player_world": "Scena non trovata nel mondo del giocatore.",
|
||||||
|
"kill_monsters_in_scene": "Uccidere %s mostri nella scena %s.",
|
||||||
|
"description": "Uccidi tutte le entità"
|
||||||
|
},
|
||||||
|
"killCharacter": {
|
||||||
|
"success": "Hai ucciso il personaggio attuale di %s.",
|
||||||
|
"description": "Uccide il personaggio attuale del giocatore"
|
||||||
|
},
|
||||||
|
"language": {
|
||||||
|
"current_language": "La lingua attuale è %s.",
|
||||||
|
"language_changed": "Lingua modificata in %s.",
|
||||||
|
"language_not_found": "Attualmente, il server non ha quella lingua.",
|
||||||
|
"description": "Mostra o cambia la lingua corrente"
|
||||||
|
},
|
||||||
|
"list": {
|
||||||
|
"success": "Ci sono %s player(s) online:",
|
||||||
|
"description": "Lista player online"
|
||||||
|
},
|
||||||
|
"permission": {
|
||||||
|
"add": "Autorizzazione aggiunta.",
|
||||||
|
"has_error": "Hanno già questa autorizzazione!",
|
||||||
|
"remove": "Autorizzazione rimossa.",
|
||||||
|
"not_have_error": "Non hanno questa autorizzazione!",
|
||||||
|
"account_error": "Impossibile trovare l'account.",
|
||||||
|
"description": "Concede o rimuove un'autorizzazione per un utente"
|
||||||
|
},
|
||||||
|
"position": {
|
||||||
|
"success": "Coordinate: %s, %s, %s\nRotazione:%s, %s, %s\nID scena: %a",
|
||||||
|
"description": "Ottiene informazioni sulla posizione e sulla rotazione"
|
||||||
|
},
|
||||||
|
"quest": {
|
||||||
|
"added": "Missione %s aggiunta.",
|
||||||
|
"finished": "Missione completata %s.",
|
||||||
|
"not_found": "Missione non trovata.",
|
||||||
|
"invalid_id": "ID missione non valido.",
|
||||||
|
"description": "Aggiungi o completa missioni"
|
||||||
|
},
|
||||||
|
"reload": {
|
||||||
|
"reload_start": "Ricarica configurazione.",
|
||||||
|
"reload_done": "Ricarica completa.",
|
||||||
|
"description": "Ricarica configurazione server"
|
||||||
|
},
|
||||||
|
"resetConst": {
|
||||||
|
"reset_all": "Ripristina le costellazioni di tutti gli avatar.",
|
||||||
|
"success": "Le costellazioni per %s sono state reimpostate. Effettua nuovamente il login per vedere le modifiche.",
|
||||||
|
"description": "Reimposta il livello della costellazione sul tuo personaggio attivo corrente, dovrai reloggare dopo aver usato il comando per vedere eventuali modifiche"
|
||||||
|
},
|
||||||
|
"resetShopLimit": {
|
||||||
|
"success": "Reset completato.",
|
||||||
|
"description": "Reimposta il tempo di aggiornamento del negozio del giocatore bersaglio"
|
||||||
|
},
|
||||||
|
"sendMail": {
|
||||||
|
"give_usage": "Utilizzo: give <player> <itemID|itemName> [importo] [livello] [raffinamento]",
|
||||||
|
"user_not_exist": "L'utente con ID '%s' non esiste.",
|
||||||
|
"start_composition": "Composizione iniziale del messaggio.\nPer favore usa '/sendmail <titolo>' per continuare.\nPuoi usare '/sendmail stop' in qualsiasi momento.",
|
||||||
|
"templates": "I modelli di posta saranno presto implementati...",
|
||||||
|
"invalid_arguments": "Argomenti non validi.",
|
||||||
|
"send_cancel": "Invio messaggio annullato.",
|
||||||
|
"send_done": "Messaggio inviato all'utente %s!",
|
||||||
|
"send_all_done": "Messaggio inviato a tutti gli utenti!",
|
||||||
|
"not_composition_end": "Composizione del messaggio non nella fase finale.\nPer favore usa '/sendmail %s' o '/sendmail stop' per annullare",
|
||||||
|
"please_use": "Per favore usa '/sendmail %s'",
|
||||||
|
"set_title": "Titolo del messaggio impostato come '%s'.\nUsa '/sendmail <content>' per continuare.",
|
||||||
|
"set_contents": "Contenuto del messaggio impostato come '%s'.\nUsa '/sendmail <sender>' per continuare.",
|
||||||
|
"set_message_sender": "Mittente del messaggio impostato come '%s'.\nUsa '/sendmail <itemID|itemName|finish> [amount] [level]' per continuare.",
|
||||||
|
"send": "Allegati %s di %s (livello %s) al messaggio.\nContinua ad aggiungere altri elementi o usa '/sendmail finish' per inviare il messaggio.",
|
||||||
|
"invalid_arguments_please_use": "Argomenti non validi.\n Utilizza '/sendmail %s'",
|
||||||
|
"title": "<titolo>",
|
||||||
|
"message": "<messaggio>",
|
||||||
|
"sender": "<mittente>",
|
||||||
|
"arguments": "<itemID|itemName|finitura> [importo] [livello]",
|
||||||
|
"error": "ERRORE: fase di costruzione %s non valida. Controlla lo stacktrace della console.",
|
||||||
|
"description": "Invia posta all'utente specificato. L'utilizzo di questo comando cambia in base al suo stato di composizione"
|
||||||
|
},
|
||||||
|
"sendMessage": {
|
||||||
|
"success": "Messaggio inviato.",
|
||||||
|
"description": "Invia un messaggio a un giocatore come server. Se usato senza target, invia a tutti i giocatori sul server."
|
||||||
|
},
|
||||||
|
"setConst": {
|
||||||
|
"range_error": "Il livello della costellazione deve essere compreso tra 0 e 6.",
|
||||||
|
"level_error": "Livello costellazione non valido.",
|
||||||
|
"fail": "Impossibile impostare la costellazione.",
|
||||||
|
"failed_success": "Le costellazioni per %s sono state impostate su %s. Ricarica la scena per vedere le modifiche.",
|
||||||
|
"success": "Le costellazioni per %s sono state impostate su %s.",
|
||||||
|
"successall": "Le costellazioni per tutti i personaggi sono state impostate su %s.",
|
||||||
|
"description": "Imposta il livello di costellazione per il tuo attuale personaggio attivo"
|
||||||
|
},
|
||||||
|
"setFetterLevel": {
|
||||||
|
"range_error": "Il livello di restrizione deve essere compreso tra 0 e 10.",
|
||||||
|
"success": "Livello di restrizione impostato su %s.",
|
||||||
|
"level_error": "Livello restrizione non valido.",
|
||||||
|
"description": "Imposta il tuo livello di restrizione per il tuo attuale personaggio attivo"
|
||||||
|
},
|
||||||
|
"setProp": {
|
||||||
|
"description": "Imposta le proprietà dell'intero account. Cose come godmode possono essere abilitate in questo modo, oltre a cambiare cose come il pavimento dell'abisso sbloccato e il progresso del pass battaglia.\n\tValori per <prop> (senza distinzione tra maiuscole e minuscole): GodMode | UnlimitedStamina | UnlimitedEnergy | TowerLevel | WorldLevel | BPLevel | SetOpenState | UnsetOpenState | UnlockMap\n\t(cont.) vedi PlayerProperty enum per altri possibili valori, nella forma PROP_MAX_SPRING_VOLUME -> max_spring_volume"
|
||||||
|
},
|
||||||
|
"setStats": {
|
||||||
|
"description": "Imposta la proprietà di combattimento per il tuo personaggio attivo corrente\n\tValori per <stat>: hp | maxhp | def | atk | em | er | crate | cdmg | cdr | heal | heali | shield | defi\n\t(cont.) Elemental DMG Bonus: epyro | ecryo | ehydro | egeo | edendro | eelectro | ephys\n\t(cont.) Elemental RES: respyro | rescryo | reshydro | resgeo | resdendro | reselectro | resphys",
|
||||||
|
"locked_to": "%s bloccato su %s.",
|
||||||
|
"locked_for_to": "%s per %s bloccato su %s.",
|
||||||
|
"unlocked": "%s sbloccato.",
|
||||||
|
"unlocked_for": "%s per %s sbloccato."
|
||||||
|
},
|
||||||
|
"spawn": {
|
||||||
|
"success": "Evocati %s di %s.",
|
||||||
|
"limit_reached": "Limite di evocazione della scena raggiunto. Generazione invece di %s entità.",
|
||||||
|
"description": "Evoca una entità vicino a te"
|
||||||
|
},
|
||||||
|
"stop": {
|
||||||
|
"success": "Server in chiusura...",
|
||||||
|
"description": "Arresta il server"
|
||||||
|
},
|
||||||
|
"talent": {
|
||||||
|
"out_of_range": "Livello talento non valido. Il livello dovrebbe essere compreso tra 1 e 15.",
|
||||||
|
"set_id": "Imposta talento %s - \"%s\" su %s.",
|
||||||
|
"id_desc": "Talenti %s - \"%s\" - \"%s\"",
|
||||||
|
"invalid_skill_id": "ID talento non valido.",
|
||||||
|
"invalid_level": "Livello talento non valido.",
|
||||||
|
"normal_attack_id": "ID attacco normale %s.",
|
||||||
|
"e_skill_id": "ID talento E %s.",
|
||||||
|
"q_skill_id": "ID talento Q %s.",
|
||||||
|
"description": "Imposta il livello di talento per il tuo attuale personaggio attivo"
|
||||||
|
},
|
||||||
|
"team": {
|
||||||
|
"invalid_usage": "Utilizzo non valido.",
|
||||||
|
"invalid_index": "L'indice non è valido.",
|
||||||
|
"add_too_much": "Il server ti permette di avere al massimo %s avatar nella tua squadra.",
|
||||||
|
"failed_to_add_avatar": "Impossibile aggiungere l'ID avatar %s.",
|
||||||
|
"failed_to_parse_index": "Impossibile analizzare l'indice: %s",
|
||||||
|
"remove_too_much": "Non puoi rimuovere tutti i tuoi avatar.",
|
||||||
|
"ignore_index": "Indici ignorati: %s",
|
||||||
|
"index_out_of_range": "L'indice che hai specificato non è compreso nell'intervallo.",
|
||||||
|
"failed_parse_avatar_id": "Impossibile analizzare l'ID avatar: %s",
|
||||||
|
"avatar_already_in_team": "Avatar è già nel team.",
|
||||||
|
"avatar_not_found": "Avatar %s non trovato.",
|
||||||
|
"description": "Modifica manualmente la tua squadra."
|
||||||
|
},
|
||||||
|
"teleportAll": {
|
||||||
|
"success": "Evoca tutti i giocatori nella tua posizione.",
|
||||||
|
"error": "Puoi usare questo comando solo in modalità MP.",
|
||||||
|
"description": "Teletrasporta tutti i giocatori del tuo mondo nella tua posizione"
|
||||||
|
},
|
||||||
|
"teleport": {
|
||||||
|
"invalid_position": "Posizione non valida.",
|
||||||
|
"exists_error": "La scena specificata non esiste.",
|
||||||
|
"success": "Teletrasportato %s in %s, %s, %s nella scena %s.",
|
||||||
|
"description": "Cambia la posizione del giocatore"
|
||||||
|
},
|
||||||
|
"weather": {
|
||||||
|
"success": "Imposta l'ID meteo su %s con il tipo di clima %s.",
|
||||||
|
"status": "L'ID meteo attuale è %s con il tipo di clima %s.",
|
||||||
|
"description": "Cambia l'ID meteo e il tipo di clima. Gli ID meteo possono essere trovati in ./Resources/ExcelBinOutput/WeatherExcelConfigData.json.\nTipi di clima: soleggiato, nuvoloso, pioggia, temporale, neve, nebbia"
|
||||||
|
},
|
||||||
|
"ban": {
|
||||||
|
"success": "Soccesso.",
|
||||||
|
"failure": "Fallito, giocatore non trovato.",
|
||||||
|
"invalid_time": "Impossibile analizzare il timestamp.",
|
||||||
|
"description": "Banna un giocatore"
|
||||||
|
},
|
||||||
|
"unlockall": {
|
||||||
|
"success": "Sbloccato tutti gli stati aperti per %s.",
|
||||||
|
"description": "Sblocca tutti gli stati aperti per un giocatore."
|
||||||
|
},
|
||||||
|
"unban": {
|
||||||
|
"success": "Successo.",
|
||||||
|
"failure": "Fallito, giocatore non trovato.",
|
||||||
|
"description": "Sbanna un giocatore"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gacha": {
|
||||||
|
"details": {
|
||||||
|
"title": "Dettagli banner",
|
||||||
|
"available_five_stars": "Articoli disponibili a 5 stelle",
|
||||||
|
"available_four_stars": "Articoli disponibili a 4 stelle",
|
||||||
|
"available_three_stars": "Articoli a 3 stelle disponibili"
|
||||||
|
},
|
||||||
|
"records": {
|
||||||
|
"title": "Gacha Records",
|
||||||
|
"date": "Data",
|
||||||
|
"item": "oggetto"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"documentation": {
|
||||||
|
"handbook": {
|
||||||
|
"title": "Manuale GM",
|
||||||
|
"title_commands": "Comandi",
|
||||||
|
"title_avatars": "Avatar",
|
||||||
|
"title_items": "Articoli",
|
||||||
|
"title_scenes": "Scene",
|
||||||
|
"title_monsters": "Mostri",
|
||||||
|
"header_id": "ID",
|
||||||
|
"header_command": "Comando",
|
||||||
|
"header_description": "Descrizione",
|
||||||
|
"header_avatar": "Avatar",
|
||||||
|
"header_item": "Articolo",
|
||||||
|
"header_scene": "Scena",
|
||||||
|
"header_monster": "Mostro"
|
||||||
|
},
|
||||||
|
"index": {
|
||||||
|
"title": "Documentazione",
|
||||||
|
"handbook": "Manuale GM",
|
||||||
|
"gacha_mapping": "Mappatura Gacha JSON"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"plugin": {
|
||||||
|
"directory_failed": "Impossibile creare la directory dei plugin: ",
|
||||||
|
"unable_to_load": "Impossibile caricare il plug-in.",
|
||||||
|
"invalid_config": "Il plug-in %s ha un file di configurazione non valido.",
|
||||||
|
"invalid_main_class": "Il plug-in %s ha una classe principale non valida.",
|
||||||
|
"missing_config": "Il plug-in %s non ha un file di configurazione valido.",
|
||||||
|
"failed_to_load_plugin": "Impossibile caricare il plug-in: %s",
|
||||||
|
"failed_to_load": "Impossibile caricare un plug-in.",
|
||||||
|
"failed_to_load_dependencies": "Impossibile caricare i plugin con le dipendenze.",
|
||||||
|
"loading_plugin": "Caricamento plug-in: %s",
|
||||||
|
"failed_add_id": "Impossibile aggiungere l'identificatore del plug-in: %s",
|
||||||
|
"enabling_plugin": "Abilitazione plug-in: %s",
|
||||||
|
"enabling_failed": "Impossibile abilitare il plug-in: %s",
|
||||||
|
"disabling_plugin": "Disabilitazione plug-in: %s",
|
||||||
|
"disabling_failed": "Impossibile disabilitare il plug-in: %s"
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"messages": {
|
"messages": {
|
||||||
"game": {
|
"game": {
|
||||||
"address_bind": "🇺🇸Game Server started at \u001b[1m\u001b[33m%s:%s\u001b[0m",
|
"address_bind": "게임 서버가 \u001b[1m\u001b[33m%s:%s\u001b[0m에서 열렸습니다.",
|
||||||
"port_bind": "게임 서버가 포트 %s에서 열렸습니다.",
|
"port_bind": "게임 서버가 포트 %s에서 열렸습니다.",
|
||||||
"connect": "클라이언트가 %s에서 연결됐습니다.",
|
"connect": "클라이언트가 %s에서 연결됐습니다.",
|
||||||
"disconnect": "클라이언트가 %s에서 연결이 끊겼습니다.",
|
"disconnect": "클라이언트가 %s에서 연결이 끊겼습니다.",
|
||||||
@ -9,9 +9,9 @@
|
|||||||
"command_error": "명령어 오류:"
|
"command_error": "명령어 오류:"
|
||||||
},
|
},
|
||||||
"dispatch": {
|
"dispatch": {
|
||||||
"address_bind": "🇺🇸[Dispatch] Dispatch server started at \u001b[1m\u001b[33m%s:%s\u001b[0m",
|
"address_bind": "[Dispatch] 디스패치 서버가 \u001b[1m\u001b[33m%s:%s\u001b[0m에서 열렸습니다.",
|
||||||
"port_bind": "[Dispatch] 디스패치 서버가 포트 %s에서 열렸습니다.",
|
"port_bind": "[Dispatch] 디스패치 서버가 포트 %s에서 열렸습니다.",
|
||||||
"request": "🇺🇸[Dispatch] Client %s %s request: %s",
|
"request": "[Dispatch] 클라이언트 %s %s 응답: %s",
|
||||||
"keystore": {
|
"keystore": {
|
||||||
"general_error": "[Dispatch] 키스토어 로딩중 오류가 발생했습니다!",
|
"general_error": "[Dispatch] 키스토어 로딩중 오류가 발생했습니다!",
|
||||||
"password_error": "[Dispatch] 키스토어를 로딩할 수 없습니다. 기본 키스토어 암호로 시도 중...",
|
"password_error": "[Dispatch] 키스토어를 로딩할 수 없습니다. 기본 키스토어 암호로 시도 중...",
|
||||||
@ -19,10 +19,10 @@
|
|||||||
"default_password": "[Dispatch] 기본 키스토어 암호가 성공적으로 로딩됐습니다. config.json에서 암호를 123456으로 설정해 보십시오."
|
"default_password": "[Dispatch] 기본 키스토어 암호가 성공적으로 로딩됐습니다. config.json에서 암호를 123456으로 설정해 보십시오."
|
||||||
},
|
},
|
||||||
"authentication": {
|
"authentication": {
|
||||||
"default_unable_to_verify": "🇺🇸[Authentication] Something called the verifyUser method which is unavailable in the default authentication handler."
|
"default_unable_to_verify": "[Authentication] 검증된 유저. 기본 인증 처리기에서 사용할 수 없는 매서드입니다."
|
||||||
},
|
},
|
||||||
"no_commands_error": "디스패치 전용 모드에서는 명령어가 지원되지 않습니다.",
|
"no_commands_error": "디스패치 전용 모드에서는 명령어가 지원되지 않습니다.",
|
||||||
"unhandled_request_error": "🇺🇸[Dispatch] Potential unhandled %s request: %s.",
|
"unhandled_request_error": "[Dispatch] 처리되지 않은 가능성 %s 응답: %s.",
|
||||||
"account": {
|
"account": {
|
||||||
"login_attempt": "[Dispatch] %s 클라이언트에서 로그인을 시도하고 있습니다.",
|
"login_attempt": "[Dispatch] %s 클라이언트에서 로그인을 시도하고 있습니다.",
|
||||||
"login_success": "[Dispatch] %s 클라이언트가 %s로 로그인 했습니다.",
|
"login_success": "[Dispatch] %s 클라이언트가 %s로 로그인 했습니다.",
|
||||||
@ -136,8 +136,8 @@
|
|||||||
"artifacts": "%s의 성유물을 초기화했습니다.",
|
"artifacts": "%s의 성유물을 초기화했습니다.",
|
||||||
"materials": "%s의 재료를 초기화했습니다.",
|
"materials": "%s의 재료를 초기화했습니다.",
|
||||||
"furniture": "%s의 가구를 초기화했습니다.",
|
"furniture": "%s의 가구를 초기화했습니다.",
|
||||||
"displays": "🇺🇸Cleared displays for %s.",
|
"displays": "%s의 Display를 초기화했습니다.",
|
||||||
"virtuals": "🇺🇸Cleared virtuals for %s.",
|
"virtuals": "%s의 Virtuals를 초기화했습니다.",
|
||||||
"everything": "%s의 모든 아이템을 초기화했습니다.",
|
"everything": "%s의 모든 아이템을 초기화했습니다.",
|
||||||
"description": "인벤토레에서 잠금된 물건을 제외한 항목을 삭제합니다. 기본적으로 4성, 1레벨, 1재련보다 낮은것만을 기준으로 하지만, 더 높게 설정할 수 있습니다."
|
"description": "인벤토레에서 잠금된 물건을 제외한 항목을 삭제합니다. 기본적으로 4성, 1레벨, 1재련보다 낮은것만을 기준으로 하지만, 더 높게 설정할 수 있습니다."
|
||||||
},
|
},
|
||||||
@ -155,9 +155,9 @@
|
|||||||
"usage_relic": "사용법: give <성유물ID> [mainPropID] [<appendPropID>[,<times>]]... [lv<레벨 0-20>]",
|
"usage_relic": "사용법: give <성유물ID> [mainPropID] [<appendPropID>[,<times>]]... [lv<레벨 0-20>]",
|
||||||
"illegal_relic": "이 성유물ID는 블랙리스트 범위에 있습니다. 원하는 ID가 아닐 수 있습니다.",
|
"illegal_relic": "이 성유물ID는 블랙리스트 범위에 있습니다. 원하는 ID가 아닐 수 있습니다.",
|
||||||
"given": "%s개의 %s를 %s에게 지급했습니다.",
|
"given": "%s개의 %s를 %s에게 지급했습니다.",
|
||||||
"given_with_level_and_refinement": "Give %s with level %s, refinement %s %s times to %s.",
|
"given_with_level_and_refinement": "%s을 %s레벨, %s재련, %s번 %s에게 지급했습니다.",
|
||||||
"given_level": "🇺🇸Given %s with level %s %s times to %s.",
|
"given_level": "%s을 레벨 %s로 %s번 %s에게 지급했습니다.",
|
||||||
"given_avatar": "🇺🇸Given %s with level %s to %s.",
|
"given_avatar": "%s을 레벨 %s로 %s에게 지급했습니다.",
|
||||||
"giveall_success": "모든 아이템이 성공적으로 지급되었습니다.",
|
"giveall_success": "모든 아이템이 성공적으로 지급되었습니다.",
|
||||||
"description": "지정된 플레이어가 아이템을 획득합니다. 모든 무기, 캐릭터, 재료를 제공할 수 있으며, 맞춤 성유물을 제공할 수도 있습니다."
|
"description": "지정된 플레이어가 아이템을 획득합니다. 모든 무기, 캐릭터, 재료를 제공할 수 있으며, 맞춤 성유물을 제공할 수도 있습니다."
|
||||||
},
|
},
|
||||||
@ -232,10 +232,10 @@
|
|||||||
"description": "대상 플레이어의 상점의 새로고침 시간을 초기화합니다"
|
"description": "대상 플레이어의 상점의 새로고침 시간을 초기화합니다"
|
||||||
},
|
},
|
||||||
"sendMail": {
|
"sendMail": {
|
||||||
"give_usage": "🇺🇸Usage: give <player> <itemID|itemName> [amount] [level] [refinement]",
|
"give_usage": "사용법: give <player> <itemID|itemName> [amount] [level] [refinement]",
|
||||||
"user_not_exist": "아이디가 '%s'인 사용자가 없습니다.",
|
"user_not_exist": "아이디가 '%s'인 사용자가 없습니다.",
|
||||||
"start_composition": "메세지 작성 시작중.\n계속하려면 '/sendmail <title>'을 입력하십시오.\n'/sendmail stop'도 언제든지 사용할 수 있습니다.",
|
"start_composition": "메세지 작성 시작중.\n계속하려면 '/sendmail <title>'을 입력하십시오.\n'/sendmail stop'도 언제든지 사용할 수 있습니다.",
|
||||||
"templates": "🇺🇸Mail templates coming soon implemented...",
|
"templates": "곧 구현될 메일 템플릿...",
|
||||||
"invalid_arguments": "잘못된 인수입니다.",
|
"invalid_arguments": "잘못된 인수입니다.",
|
||||||
"send_cancel": "메세지 전송이 취소되었습니다",
|
"send_cancel": "메세지 전송이 취소되었습니다",
|
||||||
"send_done": "%s에게 메세지가 전송되었습니다!",
|
"send_done": "%s에게 메세지가 전송되었습니다!",
|
||||||
@ -247,11 +247,11 @@
|
|||||||
"set_message_sender": "메세지 발송자가 '%s'으로 설정되었습니다.\n계속하려면 '/sendmail <itemID|itemName|finish> [amount] [level]'을 사용하십시오.",
|
"set_message_sender": "메세지 발송자가 '%s'으로 설정되었습니다.\n계속하려면 '/sendmail <itemID|itemName|finish> [amount] [level]'을 사용하십시오.",
|
||||||
"send": "%s 의 %s을 (레벨 %s)을 메세지에 첨부했습니다.\n계속 항목을 추가하거나, '/sendmail finish'을 사용해 메세지를 보낼 수 있습니다..",
|
"send": "%s 의 %s을 (레벨 %s)을 메세지에 첨부했습니다.\n계속 항목을 추가하거나, '/sendmail finish'을 사용해 메세지를 보낼 수 있습니다..",
|
||||||
"invalid_arguments_please_use": "잘못된 인수입니다.\n '/sendmail %s'을 사용하십시오",
|
"invalid_arguments_please_use": "잘못된 인수입니다.\n '/sendmail %s'을 사용하십시오",
|
||||||
"title": "🇺🇸<title>",
|
"title": "<title>",
|
||||||
"message": "🇺🇸<message>",
|
"message": "<message>",
|
||||||
"sender": "🇺🇸<sender>",
|
"sender": "<sender>",
|
||||||
"arguments": "🇺🇸<itemID|itemName|finish> [amount] [level]",
|
"arguments": "<itemID|itemName|finish> [amount] [level]",
|
||||||
"error": "오류: Invalid construction stage %s. Check console for stacktrace.",
|
"error": "오류: 잘못된 시공 단계 %s. Console에서 스택을 확인하십시오.",
|
||||||
"description": "지정된 사용자에게 메세지를 보냅니다. 이 명령어의 사용법은 세부 내용에 따라 달라집니다."
|
"description": "지정된 사용자에게 메세지를 보냅니다. 이 명령어의 사용법은 세부 내용에 따라 달라집니다."
|
||||||
},
|
},
|
||||||
"sendMessage": {
|
"sendMessage": {
|
||||||
@ -259,13 +259,13 @@
|
|||||||
"description": "서버가 플레이어에게 메세지를 전송합니다. 목표를 지정하지 않고 명령어를 사용하는 경우, 서버의 모든 인원에게 메세지가 발송됩니다."
|
"description": "서버가 플레이어에게 메세지를 전송합니다. 목표를 지정하지 않고 명령어를 사용하는 경우, 서버의 모든 인원에게 메세지가 발송됩니다."
|
||||||
},
|
},
|
||||||
"setConst": {
|
"setConst": {
|
||||||
"range_error": "🇺🇸Constellation level must be between 0 and 6.",
|
"range_error": "별자리 레벨은 0과 6사이여야 합니다.",
|
||||||
"level_error": "🇺🇸Invalid constellation level.",
|
"level_error": "잘못된 별자리 레벨.",
|
||||||
"fail": "🇺🇸Failed to set constellation.",
|
"fail": "별자리 설정에 실패했습니다..",
|
||||||
"failed_success": "🇺🇸Constellations for %s have been set to %s. Please reload scene to see changes.",
|
"failed_success": "%s에 대한 별자리가 %s로 설정되었습니다. 변경 사항을 보려면 Scene를 다시 로드하세요.",
|
||||||
"success": "🇺🇸Constellations for %s have been set to %s.",
|
"success": "%s의 별자리가 %s로 설정되었습니다.",
|
||||||
"successall": "🇺🇸Constellations for all characters have been set to %s.",
|
"successall": "모든 캐릭터의 별자리가 %s로 설정되었습니다.",
|
||||||
"description": "🇺🇸Sets constellation level for your current active character"
|
"description": "현재 캐릭터의 별자리가 정상적으로 설정되었습니다"
|
||||||
},
|
},
|
||||||
"setFetterLevel": {
|
"setFetterLevel": {
|
||||||
"range_error": "호감도 지수는 0 과 10 사이에 위치해야합니다.",
|
"range_error": "호감도 지수는 0 과 10 사이에 위치해야합니다.",
|
||||||
@ -278,10 +278,10 @@
|
|||||||
},
|
},
|
||||||
"setStats": {
|
"setStats": {
|
||||||
"description": "당신의 현재 캐릭터의 스텟들을 조절합니다.",
|
"description": "당신의 현재 캐릭터의 스텟들을 조절합니다.",
|
||||||
"locked_to": "🇺🇸%s locked to %s.",
|
"locked_to": "%s가 %s로 잠겨있습니다.",
|
||||||
"locked_for_to": "🇺🇸%s for %s locked to %s.",
|
"locked_for_to": "%s의 %s가 %s로 잠겨있습니다.",
|
||||||
"unlocked": "🇺🇸%s unlocked.",
|
"unlocked": "%s 잠금해제됨.",
|
||||||
"unlocked_for": "🇺🇸%s for %s unlocked."
|
"unlocked_for": "%s의 %s 잠금이 해제됩니다."
|
||||||
},
|
},
|
||||||
"spawn": {
|
"spawn": {
|
||||||
"success": " %s 개의 %s 를 소환하는데 성공했습니다.",
|
"success": " %s 개의 %s 를 소환하는데 성공했습니다.",
|
||||||
@ -293,9 +293,9 @@
|
|||||||
"description": "서버를 중지합니다"
|
"description": "서버를 중지합니다"
|
||||||
},
|
},
|
||||||
"talent": {
|
"talent": {
|
||||||
"out_of_range": "🇺🇸Invalid talent level. Level should be in range of 1-15.",
|
"out_of_range": "불분명한 특성 레벨. 특성의 레벨은 16 미만이여야합니다.",
|
||||||
"set_id": "🇺🇸Set talent %s - \"%s\" to %s.",
|
"set_id": "특성 설정 %s - \"%s\" to %s.",
|
||||||
"id_desc": "🇺🇸Talent %s - \"%s\" - \"%s\"",
|
"id_desc": "특성 %s - \"%s\" - \"%s\"",
|
||||||
"invalid_skill_id": "잘못된 스킬ID.",
|
"invalid_skill_id": "잘못된 스킬ID.",
|
||||||
"invalid_level": "불분명한 스킬 레벨.",
|
"invalid_level": "불분명한 스킬 레벨.",
|
||||||
"normal_attack_id": "기본공격 ID %s.",
|
"normal_attack_id": "기본공격 ID %s.",
|
||||||
@ -310,7 +310,7 @@
|
|||||||
"failed_to_add_avatar": "추가하는데 실패했습니다 캐릭터 ID %s.",
|
"failed_to_add_avatar": "추가하는데 실패했습니다 캐릭터 ID %s.",
|
||||||
"failed_to_parse_index": "분석에 실패함 index: %s",
|
"failed_to_parse_index": "분석에 실패함 index: %s",
|
||||||
"remove_too_much": "팀에는 최소 1명의 캐릭터가 편성 되어야 합니다.",
|
"remove_too_much": "팀에는 최소 1명의 캐릭터가 편성 되어야 합니다.",
|
||||||
"ignore_index": "🇺🇸Ignored index(es): %s",
|
"ignore_index": "무시된 인덱스(들): %s",
|
||||||
"index_out_of_range": "지정된 목차는 범위 밖에 있습니다.",
|
"index_out_of_range": "지정된 목차는 범위 밖에 있습니다.",
|
||||||
"failed_parse_avatar_id": "분석에 실패한 캐릭터 ID: %s",
|
"failed_parse_avatar_id": "분석에 실패한 캐릭터 ID: %s",
|
||||||
"avatar_already_in_team": "해당 캐릭터는 이미 팀에 포함되어 있습니다.",
|
"avatar_already_in_team": "해당 캐릭터는 이미 팀에 포함되어 있습니다.",
|
||||||
@ -379,25 +379,25 @@
|
|||||||
"header_monster": "몬스터"
|
"header_monster": "몬스터"
|
||||||
},
|
},
|
||||||
"index": {
|
"index": {
|
||||||
"title": "🇺🇸Documentation",
|
"title": "문서",
|
||||||
"handbook": "🇺🇸GM Handbook",
|
"handbook": "GM Handbook",
|
||||||
"gacha_mapping": "🇺🇸Gacha mapping JSON"
|
"gacha_mapping": "Gacha mapping JSON"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"plugin": {
|
"plugin": {
|
||||||
"directory_failed": "🇺🇸Failed to create plugins directory: ",
|
"directory_failed": "플러그인 디렉토리를 생성하지 못했습니다: ",
|
||||||
"unable_to_load": "🇺🇸Unable to load plugin.",
|
"unable_to_load": "플러그인을 로드할 수 없습니다.",
|
||||||
"invalid_config": "🇺🇸Plugin %s has an invalid config file.",
|
"invalid_config": "%s플러그인에 잘못된 구성 파일이 있습니다.",
|
||||||
"invalid_main_class": "🇺🇸Plugin %s has an invalid main class.",
|
"invalid_main_class": "%s플러그인에 잘못된 메인 클래스가 있습니다.",
|
||||||
"missing_config": "🇺🇸Plugin %s lacks a valid config file.",
|
"missing_config": "%s플러그인에 올바른 구성 파일이 없습니다.",
|
||||||
"failed_to_load_plugin": "🇺🇸Failed to load plugin: %s",
|
"failed_to_load_plugin": "플러그인을 로드하는데 실패했습니다: %s",
|
||||||
"failed_to_load": "🇺🇸Failed to load a plugin.",
|
"failed_to_load": "플러그인을 로드하는데 실패했습니다.",
|
||||||
"failed_to_load_dependencies": "🇺🇸Failed to load plugins with dependencies.",
|
"failed_to_load_dependencies": "종속성 플러그인을 로드하지 못했습니다.",
|
||||||
"loading_plugin": "🇺🇸Loading plugin: %s",
|
"loading_plugin": "플러그인을 로드하는중: %s",
|
||||||
"failed_add_id": "🇺🇸Failed to add plugin identifier: %s",
|
"failed_add_id": "플러그인의 식별자를 추가하지 못했습니다: %s",
|
||||||
"enabling_plugin": "🇺🇸Enabling plugin: %s",
|
"enabling_plugin": "플러그인을 활성화했습니다: %s",
|
||||||
"enabling_failed": "🇺🇸Failed to enable plugin: %s",
|
"enabling_failed": "플러그인을 활성화하는데 실패했습니다: %s",
|
||||||
"disabling_plugin": "🇺🇸Disabling plugin: %s",
|
"disabling_plugin": "플러그인을 비활성화했습니다: %s",
|
||||||
"disabling_failed": "🇺🇸Failed to disable plugin: %s"
|
"disabling_failed": "플러그인을 비활성화하는데 실패했습니다: %s"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -264,7 +264,7 @@
|
|||||||
"fail": "命之座等级设置失败。",
|
"fail": "命之座等级设置失败。",
|
||||||
"failed_success": "命之座 %s 已设置为 %s。",
|
"failed_success": "命之座 %s 已设置为 %s。",
|
||||||
"success": "命之座 %s 已设置为 %s。",
|
"success": "命之座 %s 已设置为 %s。",
|
||||||
"successall": "🇺🇸Constellations for all characters have been set to %s.",
|
"successall": "所有角色的命之座已设置为 %s。",
|
||||||
"description": "为当前活跃角色设置命座等级"
|
"description": "为当前活跃角色设置命座等级"
|
||||||
},
|
},
|
||||||
"setFetterLevel": {
|
"setFetterLevel": {
|
||||||
|
Reference in New Issue
Block a user